// ═══════════════════════════════════════════════════════
    //  GLOBALES
    // ═══════════════════════════════════════════════════════
    const API_URL = `${window.location.origin}/api`;
    let authToken = null;
    let appData = {
        employees:[], marcaciones:[], incidentes:[],
        companies:[], jornadas:[], turnos:[],
        regiones:[], cargos:[], locales:[],
        festivos:[]   // fechas YYYY-MM-DD de festivos cargados desde la BD
    };
    let ui      = {};
    let filters = {};

    // ═══════════════════════════════════════════════════════
    //  HELPER FETCH
    // ═══════════════════════════════════════════════════════
    async function apiFetch(url, options = {}) {
        const headers = { 'Content-Type': 'application/json', ...options.headers };
        if (authToken) headers['Authorization'] = `Bearer ${authToken}`;
        const res = await fetch(url, { ...options, headers });
        if (res.status === 401) { authToken = null; showLogin(); return null; }
        return res;
    }

    // ═══════════════════════════════════════════════════════
    //  SHOW LOGIN — cero llamadas a API
    // ═══════════════════════════════════════════════════════
    function showLogin() {
        document.getElementById('loading-screen').classList.add('hidden');
        document.getElementById('login-container').classList.remove('hidden');
        document.getElementById('company-selection-container').classList.add('hidden');
        document.getElementById('app-container').classList.add('hidden');
    }

    // ═══════════════════════════════════════════════════════
    //  LOAD APP DATA — solo después del login
    // ═══════════════════════════════════════════════════════
    async function loadAppData() {
        try {
            document.getElementById('loading-screen').classList.remove('hidden');
            document.getElementById('login-container').classList.add('hidden');

            const anioActual   = new Date().getFullYear();
            const anioAnterior = anioActual - 1;

            const [filtersData, companies, marcaciones, incidentes, festivosArr] = await Promise.all([
                apiFetch(`${API_URL}/filters`).then(r => r.json()),
                apiFetch(`${API_URL}/companies`).then(r => r.json()),
                apiFetch(`${API_URL}/marcaciones`).then(r => r.json()),
                apiFetch(`${API_URL}/incidentes`).then(r => r.json()),
                Promise.all([
                    apiFetch(`${API_URL}/festivos?anio=${anioAnterior}`).then(r => r.json()),
                    apiFetch(`${API_URL}/festivos?anio=${anioActual}`).then(r => r.json()),
                    apiFetch(`${API_URL}/festivos?anio=${anioActual + 1}`).then(r => r.json()),
                ]).then(arrays => arrays.flat()),
            ]);

            appData = {
                employees:[], marcaciones, incidentes, companies,
                jornadas: filtersData.jornadas, turnos:   filtersData.turnos,
                regiones: filtersData.regiones, cargos:   filtersData.cargos,
                locales:  filtersData.locales,
                festivos: festivosArr,
            };

            document.getElementById('loading-screen').classList.add('hidden');
            document.getElementById('company-selection-container').classList.remove('hidden');

            populateFilters();
            handlePeriodChange();
            renderEmployeeTable([], true);
            renderIncidentReport();
            renderCompanyList();
            // Inicializar la pestaña activa (Búsqueda Directa por defecto)
            // Tab 2 llena la lista cuando el usuario la activa
            switchCompanyTab('search');
            lucide.createIcons();
            if (ui.generateReportBtn) ui.generateReportBtn.disabled = false;

        } catch (err) {
            console.error('Error cargando datos:', err);
            showLogin();
        }
    }

    // ═══════════════════════════════════════════════════════
    //  HANDLE LOGIN
    // ═══════════════════════════════════════════════════════
    async function handleLogin() {
        const email   = document.getElementById('email').value.trim();
        const password = document.getElementById('password').value;
        const errorEl  = document.getElementById('login-error');
        errorEl.classList.add('hidden');

        if (!email || !password) {
            errorEl.textContent = 'Ingrese email y contraseña.';
            errorEl.classList.remove('hidden');
            return;
        }
        try {
            const res  = await fetch(`${API_URL}/auth/login`, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ email, password })
            });
            const data = await res.json();
            if (data.ok) {
                authToken = data.token;
                document.getElementById('user-name').textContent = data.user.name;
                document.getElementById('user-role').textContent = data.user.role;
                await loadAppData();
            } else {
                errorEl.textContent = data.message || 'Credenciales incorrectas.';
                errorEl.classList.remove('hidden');
            }
        } catch (err) {
            errorEl.textContent = 'No se pudo conectar con el servidor.';
            errorEl.classList.remove('hidden');
        }
    }

    // ═══════════════════════════════════════════════════════
    //  LOGOUT
    // ═══════════════════════════════════════════════════════
    function logout() {
        authToken = null;
        appData = { employees:[], marcaciones:[], incidentes:[],
                    companies:[], jornadas:[], turnos:[], regiones:[], cargos:[], locales:[] };
        document.getElementById('email').value    = '';
        document.getElementById('password').value = '';
        showLogin();
    }

    // ═══════════════════════════════════════════════════════
    //  EVENT LISTENERS
    // ═══════════════════════════════════════════════════════
    function setupEventListeners() {
        if (ui.loginBtn)            ui.loginBtn.addEventListener('click', handleLogin);
        if (ui.forgotPasswordLink)  ui.forgotPasswordLink.addEventListener('click', showForgotPasswordModal);
        if (ui.requestPasswordLink) ui.requestPasswordLink.addEventListener('click', showRequestPasswordModal);
        if (ui.closeModalBtn)       ui.closeModalBtn.addEventListener('click', () => ui.modalContainer.classList.add('hidden'));
        if (ui.menuButton)          ui.menuButton.addEventListener('click', () => ui.sidebar.classList.toggle('-translate-x-full'));
        if (ui.searchBtn)           ui.searchBtn.addEventListener('click', applyFilters);
        if (ui.clearFiltersBtn)     ui.clearFiltersBtn.addEventListener('click', clearAllFilters);
        if (ui.generateReportBtn)   ui.generateReportBtn.addEventListener('click', generateReport);
        if (ui.validateHashBtn)     ui.validateHashBtn.addEventListener('click', () => validateHash(ui.hashInput, ui.hashResultsContainer));
        if (ui.validateHashReportBtn) ui.validateHashReportBtn.addEventListener('click', () => validateHash(filters.hashSearch, ui.hashSearchResults));
        if (filters.hashSearch) filters.hashSearch.addEventListener('keydown', (e) => {
            if (e.key === 'Enter') validateHash(filters.hashSearch, ui.hashSearchResults);
        });
        if (filters.predeterminedPeriod) filters.predeterminedPeriod.addEventListener('change', handlePeriodChange);
        if (filters.individualSearch)    filters.individualSearch.addEventListener('input', handleIndividualSearch);
        if (ui.employeeTableContainer) ui.employeeTableContainer.addEventListener('change', (e) => {
            if (e.target.classList.contains('employee-checkbox') || e.target.id === 'select-all-employees')
                updateGenerateReportButtonState();
        });
        document.addEventListener('click', (e) => {
            if (ui.individualSearchContainer && !ui.individualSearchContainer.contains(e.target)) hideAutocomplete();
        });
        if (ui.printBtn)  ui.printBtn.addEventListener('click',  () => printReport());
        if (ui.pdfBtn)    ui.pdfBtn.addEventListener('click',    () => exportToPDF());
        if (ui.excelBtn)  ui.excelBtn.addEventListener('click',  () => exportToExcel());
        if (ui.wordBtn)   ui.wordBtn.addEventListener('click',   () => exportToWord());
        if (ui.searchIncidentsBtn) ui.searchIncidentsBtn.addEventListener('click', renderIncidentReport);
        if (ui.incidentPrintBtn)   ui.incidentPrintBtn.addEventListener('click',  () => printReport('incident-table-container'));
        if (ui.incidentPdfBtn)     ui.incidentPdfBtn.addEventListener('click',    () => exportToPDF('incident-table-container', 'Reporte de Incidentes'));
        if (ui.incidentExcelBtn)   ui.incidentExcelBtn.addEventListener('click',  () => exportToExcel('incident-table-container', 'Reporte de Incidentes'));
        if (ui.incidentWordBtn)    ui.incidentWordBtn.addEventListener('click',   () => exportToWord('incident-table-container', 'Reporte de Incidentes'));
        if (ui.companyStatusFilter) ui.companyStatusFilter.addEventListener('change', renderCompanyList);
        if (ui.companyPrintBtn)    ui.companyPrintBtn.addEventListener('click',   () => printReport('company-table-container'));
        if (ui.companyPdfBtn)      ui.companyPdfBtn.addEventListener('click',     () => exportToPDF('company-table-container', 'Listado de Empresas'));
        if (ui.companyExcelBtn)    ui.companyExcelBtn.addEventListener('click',   () => exportToExcel('company-table-container', 'Listado de Empresas'));
        if (ui.companyWordBtn)     ui.companyWordBtn.addEventListener('click',    () => exportToWord('company-table-container', 'Listado de Empresas'));
        if (ui.companySearch) ui.companySearch.addEventListener('input', (e) => {
            const term = e.target.value.trim().toLowerCase();
            const filtered = term
                ? appData.companies.filter(c =>
                    c.name.toLowerCase().includes(term) || c.rut.toLowerCase().includes(term))
                : appData.companies;
            renderCompanySelectionList(filtered);
        });
        if (ui.logoutBtn) ui.logoutBtn.addEventListener('click', logout);

        // ── Tabs de selección de empresa ─────────────────────
        if (ui.tabSearchBtn) ui.tabSearchBtn.addEventListener('click', () => switchCompanyTab('search'));
        if (ui.tabListBtn)   ui.tabListBtn.addEventListener('click',   () => switchCompanyTab('list'));

        // Tab 1 — Búsqueda Directa: autocompletado al escribir
        if (ui.companyDirectSearch) {
            ui.companyDirectSearch.addEventListener('input', handleDirectCompanySearch);
            ui.companyDirectSearch.addEventListener('keydown', (e) => {
                if (e.key === 'Enter') accessDirectCompany();
            });
        }
        if (ui.companyDirectBtn) ui.companyDirectBtn.addEventListener('click', accessDirectCompany);

        // (resultados de búsqueda directa son inline — no requiere cerrar al hacer clic fuera)
        setupMainMenuNavigation();
        setupReportSubMenuNavigation();
    }

    // ═══════════════════════════════════════════════════════
    //  MODALS
    // ═══════════════════════════════════════════════════════
    function showForgotPasswordModal(e) {
        e.preventDefault();
        ui.modalTitle.textContent = 'Recuperar Contraseña';
        ui.modalContent.innerHTML = `
            <p class="text-sm text-gray-600 mb-4">Ingrese su correo para recibir un enlace de recuperación.</p>
            <label class="block text-sm font-medium text-gray-700">Correo Electrónico</label>
            <input type="email" id="recovery-email" class="w-full mt-1 p-2 border border-gray-300 rounded-md shadow-sm" placeholder="su.correo@empresa.com">
            <button id="send-recovery-btn" class="mt-4 w-full bg-indigo-600 text-white font-bold py-2 px-4 rounded-lg hover:bg-indigo-700">Enviar Enlace</button>`;
        ui.modalContainer.classList.remove('hidden');
        document.getElementById('send-recovery-btn').addEventListener('click', async () => {
            const email = document.getElementById('recovery-email').value.trim();
            if (!email) return;
            try {
                await fetch(`${API_URL}/auth/forgot-password`, {
                    method: 'POST', headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({ email })
                });
                ui.modalContent.innerHTML = `<p class="text-green-600 font-medium">Si el correo existe, recibirás un enlace en breve.</p>`;
            } catch { ui.modalContent.innerHTML = `<p class="text-red-600">Error al enviar. Intente nuevamente.</p>`; }
        });
    }

    function showRequestPasswordModal(e) {
        e.preventDefault();
        ui.modalTitle.textContent = 'Solicitar Contraseña';
        ui.modalContent.innerHTML = `
            <div id="request-step-1">
                <p class="text-sm text-gray-600 mb-4">Paso 1 de 3: Ingrese su correo.</p>
                <label class="block text-sm font-medium text-gray-700">Correo Electrónico</label>
                <input type="email" id="request-email" class="w-full mt-1 p-2 border border-gray-300 rounded-md shadow-sm" placeholder="su.correo@empresa.com">
                <button id="send-code-btn" class="mt-4 w-full bg-indigo-600 text-white font-bold py-2 px-4 rounded-lg hover:bg-indigo-700">Enviar Código</button>
            </div>`;
        ui.modalContainer.classList.remove('hidden');
        document.getElementById('send-code-btn').addEventListener('click', showVerificationStep);
    }

    function showVerificationStep() {
        ui.modalContent.innerHTML = `
            <div id="request-step-2">
                <p class="text-sm text-gray-600 mb-4">Paso 2 de 3: Ingrese el código de 6 dígitos.</p>
                <label class="block text-sm font-medium text-gray-700">Código de Verificación</label>
                <input type="text" id="verification-code" class="w-full mt-1 p-2 border border-gray-300 rounded-md shadow-sm" placeholder="123456">
                <button id="verify-code-btn" class="mt-4 w-full bg-indigo-600 text-white font-bold py-2 px-4 rounded-lg hover:bg-indigo-700">Verificar Código</button>
            </div>`;
        document.getElementById('verify-code-btn').addEventListener('click', showNewPasswordStep);
    }

    function showNewPasswordStep() {
        ui.modalContent.innerHTML = `
            <div id="request-step-3">
                <p class="text-sm text-gray-600 mb-4">Paso 3 de 3: Ingrese su nueva contraseña.</p>
                <label class="block text-sm font-medium text-gray-700">Nueva Contraseña</label>
                <input type="password" id="new-password" class="w-full mt-1 p-2 border border-gray-300 rounded-md shadow-sm">
                <label class="block text-sm font-medium text-gray-700 mt-4">Confirmar Contraseña</label>
                <input type="password" id="confirm-password" class="w-full mt-1 p-2 border border-gray-300 rounded-md shadow-sm">
                <button id="update-password-btn" class="mt-4 w-full bg-indigo-600 text-white font-bold py-2 px-4 rounded-lg hover:bg-indigo-700">Actualizar Contraseña</button>
            </div>`;
        document.getElementById('update-password-btn').addEventListener('click', () => {
            alert('Contraseña actualizada con éxito.');
            ui.modalContainer.classList.add('hidden');
        });
    }

    // ═══════════════════════════════════════════════════════
    //  HASH VALIDATOR
    // ═══════════════════════════════════════════════════════
    async function validateHash(inputElement, resultContainer) {
        const hashToFind = inputElement ? inputElement.value.trim() : '';
        const container  = resultContainer || ui.hashResultsContainer;
        if (!container) return;

        if (!hashToFind) {
            container.innerHTML = `
                <div class="p-4 text-center text-yellow-800 bg-yellow-50 border border-yellow-200 rounded-md">
                    Por favor, ingrese un código Hash.
                </div>`;
            container.classList.remove('hidden');
            return;
        }

        // Loading state
        container.innerHTML = `
            <div class="flex items-center space-x-3 p-4 text-gray-500">
                <i data-lucide="loader-2" class="w-5 h-5 animate-spin"></i>
                <span>Validando hash…</span>
            </div>`;
        container.classList.remove('hidden');
        lucide.createIcons();

        try {
            // 1. Validate hash via API (returns employee + employer + local regardless of active company)
            const res  = await apiFetch(`${API_URL}/marcaciones/validate/${encodeURIComponent(hashToFind)}`);
            if (!res) return;

            if (!res.ok) {
                const err = await res.json();
                container.innerHTML = `
                    <div class="p-4 text-center text-red-800 bg-red-50 border border-red-200 rounded-md">
                        <p class="font-semibold">No se encontraron resultados</p>
                        <p class="text-sm mt-1">${err.message || 'Hash no encontrado en el sistema.'}</p>
                    </div>`;
                return;
            }

            const data       = await res.json();
            const marcacion  = data.marcacion;
            const employee   = data.employee  || {};
            const employer   = data.employer  || {};
            const localData  = data.local     || {};

            // 2. Reverse geocoding con Nominatim (dirección real del punto de marcación)
            let direccionGeo = 'Obteniendo dirección…';
            try {
                const geoRes = await fetch(
                    `https://nominatim.openstreetmap.org/reverse?format=json&lat=${marcacion.geolocation.lat}&lon=${marcacion.geolocation.lng}&accept-language=es`,
                    { headers: { 'User-Agent': 'ControlAsistencia/1.0' } }
                );
                const geoData   = await geoRes.json();
                const addr      = geoData.address || {};
                // Construir dirección legible
                const partes = [
                    addr.road,
                    addr.house_number,
                    addr.suburb || addr.neighbourhood,
                    addr.city   || addr.town || addr.village || addr.county,
                    addr.state
                ].filter(Boolean);
                direccionGeo = partes.length > 0 ? partes.join(', ') : (geoData.display_name || 'Dirección no disponible');
            } catch {
                direccionGeo = 'No se pudo obtener la dirección';
            }

            const formattedDate = new Date(marcacion.timestamp).toLocaleString('es-CL', {
                weekday: 'long', year: 'numeric', month: 'long', day: 'numeric',
                hour: '2-digit', minute: '2-digit', second: '2-digit'
            });
            const mapUrl    = `https://www.google.com/maps?q=${marcacion.geolocation.lat},${marcacion.geolocation.lng}`;
            const typeColor = marcacion.type === 'Entrada' ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800';
            const statusColor = employer.status === 'active' ? 'bg-green-100 text-green-700' : 'bg-gray-100 text-gray-600';
            const statusLabel = employer.status === 'active' ? 'Activa' : 'Inactiva';

            container.innerHTML = `
                <div class="border border-gray-200 rounded-lg overflow-hidden">

                    <!-- Header banda de tipo de marcación -->
                    <div class="flex items-center justify-between px-4 py-3 bg-gray-50 border-b border-gray-200">
                        <div class="flex items-center space-x-2">
                            <i data-lucide="check-circle-2" class="w-5 h-5 text-green-600"></i>
                            <span class="font-semibold text-gray-800 text-sm">Marcación Verificada</span>
                        </div>
                        <span class="px-3 py-1 rounded-full text-xs font-bold ${typeColor}">
                            ${marcacion.type.toUpperCase()}
                        </span>
                    </div>

                    <div class="p-4 space-y-4">

                        <!-- Fecha y Hora -->
                        <div class="flex items-start space-x-3">
                            <i data-lucide="clock" class="w-4 h-4 text-indigo-500 mt-0.5 flex-shrink-0"></i>
                            <div>
                                <p class="text-xs font-medium text-gray-500 uppercase tracking-wide">Fecha y Hora</p>
                                <p class="text-sm text-gray-800 font-medium capitalize">${formattedDate}</p>
                            </div>
                        </div>

                        <hr class="border-gray-100">

                        <!-- Datos del Trabajador -->
                        <div>
                            <p class="text-xs font-medium text-gray-500 uppercase tracking-wide mb-2 flex items-center space-x-1">
                                <i data-lucide="user" class="w-3 h-3"></i><span>Trabajador</span>
                            </p>
                            <div class="grid grid-cols-2 gap-x-4 gap-y-1 text-sm">
                                <div><span class="text-gray-500">Nombre:</span> <span class="font-medium text-gray-800">${employee.name || '—'}</span></div>
                                <div><span class="text-gray-500">RUT:</span> <span class="font-medium text-gray-800">${employee.rut || '—'}</span></div>
                                <div><span class="text-gray-500">Cargo:</span> <span class="text-gray-700">${employee.cargo || '—'}</span></div>
                                <div><span class="text-gray-500">Jornada:</span> <span class="text-gray-700">${employee.jornada || '—'}</span></div>
                                ${employee.est ? `<div class="col-span-2"><span class="text-gray-500">EST:</span> <span class="text-gray-700">${employee.est}</span></div>` : ''}
                            </div>
                        </div>

                        <hr class="border-gray-100">

                        <!-- Datos del Empleador -->
                        <div>
                            <p class="text-xs font-medium text-gray-500 uppercase tracking-wide mb-2 flex items-center space-x-1">
                                <i data-lucide="building-2" class="w-3 h-3"></i><span>Empleador</span>
                            </p>
                            <div class="grid grid-cols-2 gap-x-4 gap-y-1 text-sm">
                                <div class="col-span-2"><span class="text-gray-500">Razón Social:</span> <span class="font-medium text-gray-800">${employer.name || '—'}</span></div>
                                <div><span class="text-gray-500">RUT Empresa:</span> <span class="font-medium text-gray-800">${employer.rut || '—'}</span></div>
                                <div><span class="text-gray-500">Estado:</span>
                                    <span class="px-1.5 py-0.5 rounded text-xs font-medium ${statusColor}">${statusLabel}</span>
                                </div>
                            </div>
                        </div>

                        <hr class="border-gray-100">

                        <!-- Local / Faena asignado -->
                        ${localData.name ? `
                        <div>
                            <p class="text-xs font-medium text-gray-500 uppercase tracking-wide mb-2 flex items-center space-x-1">
                                <i data-lucide="map" class="w-3 h-3"></i><span>Local / Faena Asignado</span>
                            </p>
                            <div class="text-sm space-y-1">
                                <div><span class="text-gray-500">Nombre:</span> <span class="font-medium text-gray-800">${localData.name}</span></div>
                                <div><span class="text-gray-500">Dirección:</span> <span class="text-gray-700">${localData.direccion || '—'}</span></div>
                                <div><span class="text-gray-500">Región:</span> <span class="text-gray-700">${localData.region || '—'}</span></div>
                            </div>
                        </div>
                        <hr class="border-gray-100">` : ''}

                        <!-- Geolocalización real de la marcación -->
                        <div>
                            <p class="text-xs font-medium text-gray-500 uppercase tracking-wide mb-2 flex items-center space-x-1">
                                <i data-lucide="map-pin" class="w-3 h-3"></i>
                                <span>Ubicación Real de la Marcación</span>
                            </p>
                            <div class="text-sm space-y-1.5">
                                <div class="flex items-start space-x-2">
                                    <i data-lucide="navigation" class="w-3.5 h-3.5 text-gray-400 mt-0.5 flex-shrink-0"></i>
                                    <span class="text-gray-700">${direccionGeo}</span>
                                </div>
                                <div class="text-xs text-gray-400 font-mono">
                                    Lat: ${marcacion.geolocation.lat.toFixed(6)} &nbsp; Lng: ${marcacion.geolocation.lng.toFixed(6)}
                                </div>
                                <a href="${mapUrl}" target="_blank"
                                    class="inline-flex items-center space-x-1.5 text-indigo-600 hover:text-indigo-800 text-sm font-medium mt-1">
                                    <i data-lucide="external-link" class="w-3.5 h-3.5"></i>
                                    <span>Ver en Google Maps</span>
                                </a>
                            </div>
                        </div>

                        <!-- Hash completo -->
                        <div class="mt-2 p-2.5 bg-gray-50 rounded border border-gray-100">
                            <p class="text-[10px] text-gray-400 font-medium uppercase tracking-wide mb-1">Hash Completo</p>
                            <p class="text-[10px] text-gray-500 font-mono break-all">${marcacion.hash}</p>
                        </div>

                    </div>
                </div>`;

            lucide.createIcons();

        } catch (err) {
            console.error('validateHash error:', err);
            container.innerHTML = `
                <div class="p-4 text-center text-red-800 bg-red-50 border border-red-200 rounded-md">
                    <p class="font-semibold">Error al validar</p>
                    <p class="text-sm mt-1">No se pudo conectar con el servidor.</p>
                </div>`;
        }
    }


    // ═══════════════════════════════════════════════════════
    //  REPORT DISPATCHER
    // ═══════════════════════════════════════════════════════
    function generateReport() {
        const activeReportType  = ui.reportNavContainer.querySelector('.bg-indigo-100').dataset.report;
        const reportContainer   = document.getElementById(`report-${activeReportType}`);
        const selectedEmployees = getSelectedEmployees();
        const reportTitle       = ui.reportTitleEl.textContent;
        const company           = getSelectedCompanyData();
        const now               = new Date().toLocaleString('es-CL');

        // Sin empleados seleccionados — genera reporte vacío igualmente
        if (selectedEmployees.length === 0) {
            reportContainer.innerHTML = `
                <div class="printable-block" id="empty-report-block">
                    <div class="border border-gray-300 rounded-md p-6 bg-white">
                        <!-- Cabecera del reporte vacío -->
                        <div class="mb-4 pb-4 border-b border-gray-200 text-sm space-y-1">
                            <p><strong>Empresa:</strong> ${company.name} &nbsp;|&nbsp; <strong>RUT:</strong> ${company.rut}</p>
                            <p><strong>Tipo de Reporte:</strong> ${reportTitle}</p>
                            <p><strong>Período:</strong> ${filters.startDate.value || '—'} al ${filters.endDate.value || '—'}</p>
                            <p><strong>Generado:</strong> ${now}</p>
                        </div>
                        <!-- Mensaje sin resultados -->
                        <div class="flex flex-col items-center justify-center py-12 text-gray-400">
                            <svg xmlns="http://www.w3.org/2000/svg" class="w-16 h-16 mb-4 text-gray-300" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"
                                    d="M9.172 16.172a4 4 0 015.656 0M9 10h.01M15 10h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
                            </svg>
                            <p class="text-lg font-semibold text-gray-500">No hay trabajadores que coincidan con la selección</p>
                            <p class="text-sm text-gray-400 mt-1">Ajuste los filtros e intente nuevamente, o seleccione trabajadores manualmente.</p>
                        </div>
                        <!-- Tabla vacía con encabezados del reporte correspondiente -->
                        <div class="mt-4 overflow-x-auto">
                            ${getEmptyReportTable(activeReportType)}
                        </div>
                        <div class="text-[9px] mt-4 text-gray-400 text-right">
                            Documento generado por Sistema de Control de Asistencia Certificado - Res. 38 DT. — ${now}
                        </div>
                    </div>
                </div>`;
            ui.reportActions.classList.remove('hidden');
            return;
        }

        const sDate = new Date(filters.startDate.value + 'T00:00:00');
        const eDate = new Date(filters.endDate.value   + 'T23:59:59');

        let finalContent = '';
        selectedEmployees.forEach(employee => {
            const headerHTML = generateLegalHeaderRes38(employee, activeReportType);

            // Verificar si el trabajador tiene marcaciones en el período
            const tieneMarcaciones = appData.marcaciones.some(m =>
                m.employeeId === employee.id &&
                new Date(m.timestamp) >= sDate &&
                new Date(m.timestamp) <= eDate
            );

            let bodyHTML = '';
            if (!tieneMarcaciones) {
                // Sin marcaciones — mensaje informativo con tabla vacía
                bodyHTML = buildNoMarcacionesBlock(activeReportType, employee);
            } else {
                if (activeReportType === 'asistencia')     bodyHTML = generateAttendanceReport([employee]);
                if (activeReportType === 'jornada')        bodyHTML = generateDailyJourneyReport([employee]);
                if (activeReportType === 'domingos')       bodyHTML = generateSundaysAndHolidaysReport([employee]);
                if (activeReportType === 'modificaciones') bodyHTML = generateShiftModificationReport([employee]);
            }

            finalContent += `
                <div class="printable-block mb-12" style="page-break-after:always;">
                    ${headerHTML}${bodyHTML}
                    <div class="text-[9px] mt-2 text-gray-500 text-right">Documento generado por Sistema de Control de Asistencia Certificado - Res. 38 DT.</div>
                </div>`;
        });
        reportContainer.innerHTML = finalContent;
        ui.reportActions.classList.remove('hidden');
    }

    // Bloque "sin marcaciones" para un trabajador individual
    function buildNoMarcacionesBlock(reportType, employee) {
        const cols = {
            asistencia:     ['Fecha', 'Asistencia', 'Ausencia', 'Observaciones'],
            jornada:        ['Fecha', 'Jornada Pactada', 'Entrada / Salida', 'Colación Pactada', 'Colación Real', 'Horas Atraso', 'Horas Extras', 'Otras Marcaciones (b.9)', 'Observaciones (b.10)'],
            domingos:       ['Benef. Desc. Adicional (c.2)', 'Fecha dd/mm/aa (c.3)', 'Asistencia (c.4)', 'Ausencia (c.5)', 'Observaciones (c.6)'],
            modificaciones: ['Fecha asignación turno', 'Turno asignado', 'Extensión del turno', 'Fecha asignación nuevo turno', 'Inicio de turno', 'Nuevo turno asignado', 'Extensión nuevo turno', 'Solicitante', 'Observaciones'],
        }[reportType] || ['Fecha', 'Asistencia', 'Ausencia', 'Observaciones'];

        const ths = cols.map(h => `<th>${h}</th>`).join('');
        return `
            <div class="flex items-center space-x-3 my-4 p-4 bg-yellow-50 border border-yellow-200 rounded-md text-yellow-800">
                <svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 flex-shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
                        d="M12 9v2m0 4h.01M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z"/>
                </svg>
                <p class="text-sm font-medium">
                    El trabajador <strong>${employee.name}</strong> no presenta marcaciones en el período seleccionado.
                </p>
            </div>
            <div class="overflow-x-auto">
                <table class="report-table">
                    <thead><tr>${ths}</tr></thead>
                    <tbody>
                        <tr>
                            <td colspan="${cols.length}" class="text-center py-6 text-gray-400 italic">
                                Sin marcaciones registradas para este período.
                            </td>
                        </tr>
                    </tbody>
                </table>
            </div>`;
    }

    // Devuelve una tabla vacía con los encabezados del tipo de reporte activo
    function getEmptyReportTable(reportType) {
        const headers = {
            asistencia:     ['Fecha', 'Asistencia', 'Ausencia', 'Observaciones'],
            jornada:        ['Fecha', 'Jornada Pactada', 'Entrada / Salida', 'Colación Pactada', 'Colación Real', 'Horas Atraso', 'Horas Extras', 'Otras Marcaciones (b.9)', 'Observaciones (b.10)'],
            domingos:       ['Benef. Desc. Adicional (c.2)', 'Fecha dd/mm/aa (c.3)', 'Asistencia (c.4)', 'Ausencia (c.5)', 'Observaciones (c.6)'],
            modificaciones: ['Fecha asignación turno', 'Turno asignado', 'Extensión del turno', 'Fecha asignación nuevo turno', 'Inicio de turno', 'Nuevo turno asignado', 'Extensión nuevo turno', 'Solicitante', 'Observaciones'],
        };
        const cols = headers[reportType] || headers.asistencia;
        const ths  = cols.map(h => `<th>${h}</th>`).join('');
        return `
            <table class="report-table">
                <thead><tr>${ths}</tr></thead>
                <tbody>
                    <tr>
                        <td colspan="${cols.length}" class="text-center py-6 text-gray-400 italic">
                            Sin registros para el período y filtros seleccionados.
                        </td>
                    </tr>
                </tbody>
            </table>`;
    }

    function getSelectedEmployees() {
        const single = getSingleEmployeeFromSearch();
        if (single) return [single];
        const ids = Array.from(document.querySelectorAll('.employee-checkbox:checked')).map(cb => parseInt(cb.dataset.id));
        return appData.employees.filter(e => ids.includes(e.id));
    }

    function getSelectedCompanyData() {
        const info = document.getElementById('company-header-info').textContent;
        if (!info) return { name: 'Empresa No Definida', rut: '00.000.000-0' };
        const parts = info.split(' | ');
        return { name: parts[0], rut: parts[1] };
    }

    function generateLegalHeaderRes38(employee, reportType) {
        const company        = getSelectedCompanyData();
        const localObj       = appData.locales.find(l => l.id === employee.local);
        const nombreLocal    = localObj ? localObj.name      : employee.local;
        const direccionLocal = localObj ? localObj.direccion : 'Dirección no registrada';
        let h = `
            <p><strong>Razón Social del Empleador:</strong> ${company.name}</p>
            <p><strong>RUT del Empleador:</strong> ${company.rut}</p>
            <p><strong>Nombre Completo del Trabajador:</strong> ${employee.name}</p>
            <p><strong>Cédula Nacional de Identidad:</strong> ${employee.rut}</p>
            <p><strong>Lugar de Prestación de los Servicios:</strong> ${nombreLocal} - ${direccionLocal}</p>`;
        if (reportType === 'jornada')  h += `<p><strong>Banda horaria:</strong> No aplica</p>`;
        if (reportType === 'domingos') h += `<p><strong>Cargo o función:</strong> ${employee.cargo}</p>`;
        return `<div class="report-header text-sm p-4 mb-4 border border-gray-300 bg-white" style="page-break-inside:avoid;">${h}</div>`;
    }

    // ═══════════════════════════════════════════════════════
    //  SPECIFIC REPORT GENERATORS
    // ═══════════════════════════════════════════════════════
    function generateAttendanceReport(emps) {
        let html = '';
        const sDate = new Date(filters.startDate.value + 'T00:00:00');
        const eDate = new Date(filters.endDate.value   + 'T00:00:00');
        emps.forEach(() => {
            html += `<table class="report-table"><thead><tr><th>Fecha</th><th>Día</th><th>Asistencia</th><th>Ausencia</th><th>Observaciones</th></tr></thead><tbody>`;
            for (let d = new Date(sDate); d <= eDate; d.setDate(d.getDate() + 1)) {
                const esDomingo  = d.getDay() === 0;
                const nombreFest = getNombreFestivo(d);
                const esFestivo  = nombreFest !== null;
                const diasSemana = ['Dom', 'Lun', 'Mar', 'Mié', 'Jue', 'Vie', 'Sáb'];
                const diaNombre  = diasSemana[d.getDay()];

                // Excluir domingos y festivos del reporte de asistencia
                if (esDomingo || esFestivo) {
                    const etiqueta = esDomingo && esFestivo
                        ? `Domingo / ${nombreFest}`
                        : esDomingo ? 'Domingo' : nombreFest;
                    html += `<tr class="bg-gray-50 text-gray-400 italic">
                        <td>${d.toLocaleDateString('es-CL')}</td>
                        <td>${diaNombre}</td>
                        <td>—</td>
                        <td>—</td>
                        <td class="text-indigo-400 font-medium not-italic">${etiqueta}</td>
                    </tr>`;
                    continue;
                }

                const dd = generateMockAttendanceData(d);
                html += `<tr>
                    <td>${d.toLocaleDateString('es-CL')}</td>
                    <td>${diaNombre}</td>
                    <td class="${dd.asistenciaClass}">${dd.asistencia}</td>
                    <td class="${dd.ausenciaClass}">${dd.ausencia}</td>
                    <td>${dd.obs}</td>
                </tr>`;
            }
            html += `</tbody></table>`;
        });
        return html;
    }

    function generateDailyJourneyReport(emps) {
        let html = '';
        const sDate = new Date(filters.startDate.value + 'T00:00:00');
        const eDate = new Date(filters.endDate.value   + 'T00:00:00');

        emps.forEach(employee => {
            // b.11 — columna Ciclo solo si jornada es Excepcional / Bisemanal / Distribuida
            const tieneJornadaExcepcional = ['Excepcional','Bisemanal','Distribuida en ciclos']
                                            .includes(employee?.jornada);
            const numCols = tieneJornadaExcepcional ? 10 : 9;

            const thCiclo = tieneJornadaExcepcional
                ? `<th title="Art. 27 b.11 — Ciclo: ${employee.turno}">Ciclo / Descanso</th>`
                : '';

            html += `<table class="report-table"><thead>
                <tr>
                    <th>Fecha</th>
                    <th>Jornada Pactada</th>
                    <th>Entrada / Salida</th>
                    <th>Colación Pactada</th>
                    <th>Colación Real</th>
                    <th>Horas Atraso</th>
                    <th>Horas Extras</th>
                    <th title="Art. 27 b.9">Otras Marcaciones</th>
                    <th title="Art. 27 b.10">Observaciones</th>
                    ${thCiclo}
                </tr>
            </thead><tbody>`;

            // Totales semanales por columna (b.12)
            let wk = { jornada:0, colPactada:0, colReal:0, presencia:0, atraso:0, extra:0 };

            for (let d = new Date(sDate); d <= eDate; d.setDate(d.getDate() + 1)) {
                const esDomingo  = d.getDay() === 0;
                const nombreFest = getNombreFestivo(d);
                const esFestivo  = nombreFest !== null;

                // ── Domingo: se omite completamente (fila y totales) ────
                if (esDomingo) continue;

                // ── Festivo: se muestra como fila informativa pero SIN totales ──
                if (esFestivo) {
                    html += `<tr class="bg-gray-50 text-gray-400 italic">
                        <td>${d.toLocaleDateString('es-CL')}</td>
                        <td colspan="${numCols - 2}" class="text-center text-indigo-400 font-medium not-italic">${nombreFest}</td>
                        <td>—</td>
                    </tr>`;
                    continue;
                }

                // ── Día hábil ────────────────────────────────────
                const dd = generateMockJourneyData(d);
                wk.jornada    += (dd.jornadaPactadaRaw  || 0);
                wk.colPactada += (dd.colacionPactadaRaw || 0);
                wk.colReal    += (dd.colacionRealRaw    || 0);
                wk.presencia  += (dd.presenciaRaw       || 0);
                wk.atraso     += (dd.rawMissing         || 0);
                wk.extra      += (dd.rawExtra           || 0);

                const cicloCell = tieneJornadaExcepcional
                    ? `<td class="text-xs text-gray-500">${employee.turno || '—'}</td>`
                    : '';

                html += `<tr>
                    <td>${d.toLocaleDateString('es-CL')}</td>
                    <td>${dd.jornada}</td>
                    <td>${dd.marcaciones}</td>
                    <td>${dd.colacion}</td>
                    <td>${dd.marcacionColacion}</td>
                    <td class="text-red-600">${dd.tiempoFaltante}</td>
                    <td class="text-green-600">${dd.tiempoExtra}</td>
                    <td class="text-xs text-gray-600">${dd.otras}</td>
                    <td class="text-xs italic">${dd.obs}</td>
                    ${cicloCell}
                </tr>`;

                // Cerrar semana: sábado O último día del período
                // (domingo ya se omite por completo con continue arriba)
                if (d.getDay() === 6 || d.getTime() === eDate.getTime()) {
                    html += buildWeeklyTotal(wk, numCols, tieneJornadaExcepcional, employee);
                    wk = { jornada:0, colPactada:0, colReal:0, presencia:0, atraso:0, extra:0 };
                }
            }
            html += `</tbody></table>`;
        });
        return html;
    }
    /**
     * b.12 — Fila de totales semanales con total por columna
     * Fórmula jornada neta = Σ(jornadaPactada) − Σ(colacionPactada)
     */
    function buildWeeklyTotal(wk, numCols, tieneJornadaExcepcional, employee) {
        const netoJornada = wk.jornada - wk.colPactada;   // b.12 fórmula
        const comp        = formatCompensacion(wk.extra, wk.atraso);
        const cicloBlank  = tieneJornadaExcepcional ? '<td class="text-gray-400">—</td>' : '';
        const netoClass   = wk.extra > wk.atraso ? 'text-green-700'
                          : wk.extra < wk.atraso ? 'text-red-700' : 'text-gray-600';
        return `<tr class="total-row bg-indigo-50 font-bold text-xs border-t-2 border-indigo-300">
            <td class="text-right text-gray-600 text-[10px] italic">TOTALES (b.12)</td>
            <td class="${netoClass}" title="Jornada neta = ${formatSecondsToTime(wk.jornada)} − ${formatSecondsToTime(wk.colPactada)} col.">${formatSecondsToTime(netoJornada)}</td>
            <td class="text-gray-600" title="Total neto en recinto = presencia bruta − colación real">${formatSecondsToTime(Math.max(0, wk.presencia - wk.colReal))}</td>
            <td class="text-gray-600">${formatSecondsToTime(wk.colPactada)}</td>
            <td class="text-gray-600">${formatSecondsToTime(wk.colReal)}</td>
            <td class="text-red-600">${wk.atraso > 0 ? formatSecondsToTime(wk.atraso, null, '-') : '00:00:00'}</td>
            <td class="text-green-600">${wk.extra  > 0 ? formatSecondsToTime(wk.extra,  null, '+') : '00:00:00'}</td>
            <td class="text-gray-400">—</td>
            <td class="${comp.clase} font-bold">Comp: ${comp.texto}</td>
            ${cicloBlank}
        </tr>`;
    }
    /**
     * Reporte Domingos y Festivos — Resolución 38, Art. c.2 al c.8
     *
     * Columnas:
     *   c.2  Benef. Descanso Adicional (trabajadores del comercio)
     *   c.3  Fecha (dd/mm/aa)
     *   c.4  Asistencia (sí / no)
     *   c.5  Ausencia   (justificada / injustificada — solo si no asistió)
     *   c.6  Observaciones
     *
     * Totales:
     *   c.7  Sumatoria mensual por cada mes del período
     *   c.8  Sumatoria total del período completo
     */
    /**
     * Reporte Domingos y Festivos — Resolución 38, Art. c.2 al c.8
     * Una tabla completa por mes (encabezado azul + cabecera de columnas + filas + subtotal c.7)
     * Fila final de total del período completo (c.8)
     */
    function generateSundaysAndHolidaysReport(emps) {
        const MESES = ['Enero','Febrero','Marzo','Abril','Mayo','Junio',
                       'Julio','Agosto','Septiembre','Octubre','Noviembre','Diciembre'];

        const sDate = new Date(filters.startDate.value + 'T00:00:00');
        const eDate = new Date(filters.endDate.value   + 'T00:00:00');

        // Cargos del comercio con derecho a descanso dominical adicional (Art. 36 CT)
        const CARGOS_COMERCIO = ['vendedor','vendedora','cajero','cajera','despachador',
                                  'despachadora','promotor','promotora','repositor','reposición'];

        // Cabecera de columnas (se repite en cada tabla mensual)
        const THEAD = `
            <thead>
                <tr>
                    <th title="c.2">Benef. Desc. Adicional</th>
                    <th title="c.3">Fecha (dd/mm/aa)</th>
                    <th title="c.4">Asistencia</th>
                    <th title="c.5">Ausencia</th>
                    <th title="c.6">Observaciones</th>
                </tr>
            </thead>`;

        let html = '';

        emps.forEach(employee => {
            // c.2 — trabajador del comercio con descanso adicional
            const esBenef  = employee?.cargo
                ? CARGOS_COMERCIO.some(c => employee.cargo.toLowerCase().includes(c))
                : false;
            const benefLabel = esBenef ? 'Sí' : 'No';

            // ── Agrupar días domingo/festivo por mes ──────────────────────
            const meses = {};   // { 'YYYY-MM': { label, filas:[], laborados:0 } }

            for (let d = new Date(sDate); d <= eDate; d.setDate(d.getDate() + 1)) {
                const esDomingo  = d.getDay() === 0;
                const nombreFest = getNombreFestivo(d);
                const esFestivo  = nombreFest !== null;
                if (!esDomingo && !esFestivo) continue;

                const key   = `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}`;
                const label = `${MESES[d.getMonth()]} ${d.getFullYear()}`;
                if (!meses[key]) meses[key] = { label, filas: [], laborados: 0 };

                // Fecha en formato dd/mm/aa (c.3)
                const yy  = String(d.getFullYear()).slice(-2);
                const mm  = String(d.getMonth() + 1).padStart(2, '0');
                const dia = String(d.getDate()).padStart(2, '0');

                // Observación base según tipo de día
                let obsBase = '';
                if      (esDomingo && esFestivo) obsBase = `Domingo / ${nombreFest}`;
                else if (esFestivo)               obsBase = nombreFest;

                const dd = generateMockAttendanceData(new Date(d), true);
                if (dd.asistencia === 'Sí') meses[key].laborados++;

                meses[key].filas.push({
                    fecha:         `${dia}/${mm}/${yy}`,
                    asistencia:    dd.asistencia,
                    ausenciaClass: dd.ausenciaClass,
                    ausencia:      dd.ausencia,
                    obs:           dd.obs || obsBase,
                });
            }

            const claves = Object.keys(meses).sort();
            if (claves.length === 0) {
                html += `<p class="text-gray-400 italic text-sm p-4">No hay domingos ni festivos en el período seleccionado.</p>`;
                return;
            }

            const totalGlobal = claves.reduce((s, k) => s + meses[k].laborados, 0);

            // ── Una tabla por mes (c.7 al final de cada una) ──────────────
            claves.forEach(key => {
                const mes = meses[key];

                const filas = mes.filas.map(f => `
                    <tr>
                        <td class="text-center">${benefLabel}</td>
                        <td>${f.fecha}</td>
                        <td class="${f.asistencia === 'Sí' ? 'text-green-600' : 'text-red-600'} font-medium">
                            ${f.asistencia}
                        </td>
                        <td class="${f.ausenciaClass}">${f.ausencia}</td>
                        <td class="text-xs">${f.obs}</td>
                    </tr>`).join('');

                    
                html += `
                <table class="report-table mb-1">
                    <thead>
                        <!-- Encabezado azul del mes -->
                        <tr class="bg-indigo-600">
                            <td colspan="5"
                                class="text-white font-bold text-center py-2 text-sm uppercase tracking-wide">
                                ${mes.label}
                            </td>
                        </tr>
                        <!-- Cabecera de columnas -->
                        <tr>
                            <th title="c.2">Benef. Desc. Adicional</th>
                            <th title="c.3">Fecha (dd/mm/aa)</th>
                            <th title="c.4">Asistencia</th>
                            <th title="c.5">Ausencia</th>
                            <th title="c.6">Observaciones</th>
                        </tr>
                    </thead>
                    <tbody>
                        ${filas || `<tr><td colspan="5" class="text-center text-gray-400 py-2 italic text-xs">Sin domingos ni festivos en ${mes.label}.</td></tr>`}
                        <!-- c.7 — Sumatoria mensual -->
                        <tr class="total-row bg-gray-100 font-bold text-xs">
                            <td colspan="4" class="text-right pr-3">
                                Sumatoria ${mes.label} (c.7)
                            </td>
                            <td class="text-center font-bold text-sm">${mes.laborados}</td>
                        </tr>
                    </tbody>
                </table>`;
            });

            // c.8 — Sumatoria total del período completo
            const primerMes = meses[claves[0]].label;
            const ultimoMes = meses[claves[claves.length - 1]].label;
            const periodoLabel = claves.length > 1
                ? `${primerMes} – ${ultimoMes}`
                : primerMes;

            html += `
                <table class="report-table mt-1">
                    <tbody>
                        <tr class="bg-indigo-100 font-bold">
                            <td colspan="4"
                                class="text-right pr-3 text-indigo-800 text-xs">
                                SUMATORIA TOTAL DEL PERÍODO — ${periodoLabel} (c.8)
                            </td>
                            <td class="text-center font-bold text-sm text-indigo-800">
                                ${totalGlobal}
                            </td>
                        </tr>
                    </tbody>
                </table>`;
        });

        return html;
    }
    function generateShiftModificationReport(emps) {
        let html = '';
        emps.forEach(() => {
            const changes = generateMockShiftChanges();
            const tbody = changes.length > 0
                ? changes.map(c => `<tr><td class="text-center">${c.fechaAsignacion}</td><td class="text-center">${c.turno}</td><td class="text-center">${c.extension}</td><td class="text-center">${c.fechaNuevo}</td><td class="text-center">${c.inicioNuevo}</td><td class="text-center">${c.nuevoTurno}</td><td class="text-center">${c.nuevaExtension}</td><td class="text-center">${c.solicitante}</td><td class="text-left text-xs">${c.obs}</td></tr>`).join('')
                : `<tr><td colspan="9" class="text-center p-4 text-gray-500">No se registran modificaciones en el periodo.</td></tr>`;
            html += `<table class="report-table w-full text-xs"><thead><tr class="bg-gray-100"><th>Fecha asignación turno</th><th>Turno asignado</th><th>Extensión del turno</th><th>Fecha asignación nuevo turno</th><th>Inicio de turno</th><th>Nuevo turno asignado</th><th>Extensión nuevo turno</th><th>Solicitante</th><th>Observaciones</th></tr></thead><tbody>${tbody}</tbody></table>`;
        });
        return html;
    }

    // ═══════════════════════════════════════════════════════
    //  HELPER FESTIVOS
    // ═══════════════════════════════════════════════════════

    /** Devuelve true si la fecha es domingo o festivo */
    function isDomingoOFestivo(date) {
        if (date.getDay() === 0) return true;  // domingo
        const iso = toISODateString(date);
        return appData.festivos.some(f => f.fecha === iso);
    }

    /** Si la fecha es festivo, devuelve su descripción; si no, null */
    function getNombreFestivo(date) {
        const iso = toISODateString(date);
        const f   = appData.festivos.find(f => f.fecha === iso);
        return f ? f.descripcion : null;
    }

        // ═══════════════════════════════════════════════════════
    //  MOCK DATA GENERATORS
    // ═══════════════════════════════════════════════════════
    function generateMockAttendanceData(date) {
        // Solo se llama para días hábiles (domingos y festivos ya excluidos en el report generator)
        const rand = Math.random();
        if (date.getDay() === 6 || rand < 0.1)
            return { asistencia:'No', asistenciaClass:'text-red-600', ausencia:'Justificada',   ausenciaClass:'text-blue-600', obs:'VAC' };
        if (rand < 0.15)
            return { asistencia:'No', asistenciaClass:'text-red-600', ausencia:'Injustificada', ausenciaClass:'text-red-600',  obs:'A.I.' };
        if (rand < 0.2)
            return { asistencia:'No', asistenciaClass:'text-red-600', ausencia:'Justificada',   ausenciaClass:'text-blue-600', obs:'L.M.' };
        return { asistencia:'Sí', asistenciaClass:'text-green-600', ausencia:'', ausenciaClass:'', obs:'' };
    }

    function generateMockJourneyData(date) {
        if (date.getDay() === 0 || date.getDay() === 6)
            return { jornadaPactadaRaw:0, colacionPactadaRaw:0, colacionRealRaw:0,
                     presenciaRaw:0, rawMissing:0, rawExtra:0,
                     jornada:'N/A', marcaciones:'N/A',
                     colacion:'N/A', marcacionColacion:'N/A', otras:'', obs:'Descanso',
                     tiempoFaltante:'-00:00:00', tiempoExtra:'+00:00:00' };

        // Jornada pactada: 09:00-18:00 = 9h brutas / colación pactada: 1h / neta: 8h
        const jornadaInicioRaw   = 9  * 3600;
        const jornadaFinRaw      = 18 * 3600;
        const jornadaPactadaRaw  = jornadaFinRaw - jornadaInicioRaw; // 9h
        const colacionPactadaRaw = 3600;                              // 1h

        const rand = Math.random();

        // Entrada real
        let actualStart = jornadaInicioRaw;
        if (rand < 0.2) actualStart += Math.floor(Math.random() * 30 * 60);

        // Salida real
        let actualEnd = jornadaFinRaw;
        if (rand > 0.8) actualEnd += Math.floor(Math.random() * 3600);

        // Colación real
        let colacionRealRaw   = 0;
        let marcacionColacion = 'No aplica';
        let otras = '';

        if (rand > 0.3) {
            const cs      = (13 * 3600) + Math.floor(Math.random() * 5) * 60;
            const ce      = (14 * 3600) - Math.floor(Math.random() * 5) * 60;
            colacionRealRaw   = ce - cs;
            marcacionColacion = `${formatSecondsToTime(cs)} - ${formatSecondsToTime(ce)}`;
        }

        // Otras marcaciones (b.9)
        if (rand > 0.75 && rand <= 0.78) {
            const ps = (10 * 3600) + Math.floor(Math.random() * 10) * 60;
            const pe = ps + 10 * 60;
            otras = `Pausa activa: ${formatSecondsToTime(ps)}-${formatSecondsToTime(pe)}`;
        } else if (rand >= 0.6 && rand < 0.63) {
            otras = 'Inicio Jornada Pasiva: 16:00:00';
        }

        // ── Cálculo separado de Atraso y Extra ────────────────────────────
        // Se calculan de forma INDEPENDIENTE — no se netan entre sí.
        // Atraso: solo por llegada tarde al inicio de la jornada
        const rawMissing = Math.max(0, actualStart - jornadaInicioRaw);

        // Extra: salida tarde + tiempo de colación no tomada (o tomada más corta)
        const extraSalida  = Math.max(0, actualEnd - jornadaFinRaw);
        const colSaved     = Math.max(0, colacionPactadaRaw - colacionRealRaw);
        const rawExtra     = extraSalida + colSaved;

        const presenciaRaw = actualEnd - actualStart; // tiempo en recinto (bruto)

        return {
            jornadaPactadaRaw,
            colacionPactadaRaw,
            colacionRealRaw,
            presenciaRaw,
            rawMissing,
            rawExtra,
            jornada:          `${formatSecondsToTime(jornadaInicioRaw)} - ${formatSecondsToTime(jornadaFinRaw)}`,
            marcaciones:      `${formatSecondsToTime(actualStart)} - ${formatSecondsToTime(actualEnd)}`,
            colacion:         `${formatSecondsToTime(13*3600)} - ${formatSecondsToTime(14*3600)}`,
            marcacionColacion,
            otras,
            obs:              '',
            tiempoFaltante:   formatSecondsToTime(rawMissing, null, '-'),
            tiempoExtra:      formatSecondsToTime(rawExtra,   null, '+'),
        };
    }
    function generateMockShiftChanges() {
        if (Math.random() > 0.3) return [
            { fechaAsignacion:'15/01/24', turno:'08:00 a 17:00', extension:'Semanal',   fechaNuevo:'10/06/24', inicioNuevo:'17/06/24', nuevoTurno:'14:00 a 22:00', nuevaExtension:'Semanal',   solicitante:'Empleador', obs:'Reestructuración operacional por alta demanda.' },
            { fechaAsignacion:'17/06/24', turno:'14:00 a 22:00', extension:'Semanal',   fechaNuevo:'01/09/24', inicioNuevo:'05/09/24', nuevoTurno:'09:00 a 18:00', nuevaExtension:'Bisemanal', solicitante:'Trabajador', obs:'Acuerdo mutuo por motivos familiares.' },
        ];
        return [];
    }

    // ═══════════════════════════════════════════════════════
    //  AUTOCOMPLETE & FILTERING
    // ═══════════════════════════════════════════════════════
    function handleIndividualSearch() {
        const term = filters.individualSearch.value.trim().toLowerCase();
        applyFilters();
        if (term.length > 0) renderAutocomplete(appData.employees.filter(e => e.name.toLowerCase().includes(term) || e.rut.includes(term)));
        else hideAutocomplete();
    }

    function renderAutocomplete(suggestions) {
        if (suggestions.length === 0) { hideAutocomplete(); return; }
        ui.autocompleteResults.innerHTML = `<ul class="divide-y divide-gray-100">${suggestions.map(s =>
            `<li class="p-2 hover:bg-indigo-100 cursor-pointer autocomplete-item" data-name="${s.name}">${s.name} <span class="text-gray-500 text-xs">(${s.rut})</span></li>`
        ).join('')}</ul>`;
        ui.autocompleteResults.classList.remove('hidden');
        document.querySelectorAll('.autocomplete-item').forEach(item => {
            item.addEventListener('click', (e) => {
                filters.individualSearch.value = e.currentTarget.dataset.name;
                hideAutocomplete(); applyFilters(); updateGenerateReportButtonState();
            });
        });
    }

    function hideAutocomplete() {
        ui.autocompleteResults.innerHTML = '';
        ui.autocompleteResults.classList.add('hidden');
    }

    function applyFilters() {
        const cid = parseInt(document.getElementById('company-header-info').dataset.companyId);
        let emps = appData.employees.filter(e => e.companyId === cid);
        const term = filters.individualSearch.value.toLowerCase().trim();
        if (term)                           emps = emps.filter(e => e.name.toLowerCase().includes(term) || e.rut.includes(term));
        if (filters.jornada.value !== 'Todas') emps = emps.filter(e => e.jornada === filters.jornada.value);
        if (filters.turnos.value  !== 'Todos') emps = emps.filter(e => e.turno   === filters.turnos.value);
        if (filters.region.value  !== 'Todas') emps = emps.filter(e => e.region  === filters.region.value);
        if (filters.local.value   !== 'Todos') emps = emps.filter(e => e.local   === filters.local.value);
        if (filters.cargo.value   !== 'Todos') emps = emps.filter(e => e.cargo   === filters.cargo.value);
        if (filters.est.value     !== 'todos') emps = emps.filter(e => e.est     === filters.est.value);
        renderEmployeeTable(emps);
        updateGenerateReportButtonState();
    }

    function renderEmployeeTable(employees, isInitialState = false) {
        if (isInitialState) {
            ui.employeeTableContainer.innerHTML = `<div class="text-center py-8 text-gray-500"><i data-lucide="search-check" class="mx-auto h-12 w-12 text-gray-400"></i><h3 class="mt-2 text-sm font-medium text-gray-900">Listo para buscar</h3><p class="mt-1 text-sm text-gray-500">Use los filtros y presione Aplicar Filtros Grupales para comenzar.</p></div>`;
            lucide.createIcons(); return;
        }
        if (employees.length === 0) {
            ui.employeeTableContainer.innerHTML = `<div class="text-center py-8 text-gray-500"><i data-lucide="user-x" class="mx-auto h-12 w-12 text-gray-400"></i><h3 class="mt-2 text-sm font-medium text-gray-900">No hay trabajadores que coincidan con la selección</h3><p class="mt-1 text-sm text-gray-500">Intente ajustar los filtros.</p></div>`;
            lucide.createIcons();
        } else {
            ui.employeeTableContainer.innerHTML = `<table class="report-table text-left"><thead><tr>
                <th class="w-10"><input type="checkbox" id="select-all-employees" class="rounded border-gray-300"></th>
                <th>RUT</th><th>Nombre</th><th>Cargo</th><th>Local / Faena</th>
            </tr></thead><tbody>${employees.map(e =>
                `<tr><td><input type="checkbox" class="employee-checkbox rounded border-gray-300" data-id="${e.id}"></td>
                 <td>${e.rut}</td><td>${e.name}</td><td>${e.cargo}</td><td>${getLocalName(e.local)}</td></tr>`
            ).join('')}</tbody></table>`;
            document.getElementById('select-all-employees').addEventListener('change', ev => {
                document.querySelectorAll('.employee-checkbox').forEach(cb => cb.checked = ev.target.checked);
                updateGenerateReportButtonState();
            });
        }
    }

    function getLocalName(localId) {
        const o = appData.locales.find(l => l.id === localId);
        return o ? o.name : localId;
    }

    function getSingleEmployeeFromSearch() {
        const term = filters.individualSearch.value.trim();
        if (!term) return null;
        const found = appData.employees.filter(e => e.name.toLowerCase() === term.toLowerCase() || e.rut === term);
        return found.length === 1 ? found[0] : null;
    }

    function clearAllFilters() {
        Object.values(filters).forEach(el => {
            if (el.tagName === 'SELECT') el.selectedIndex = 0;
            else if (el.type !== 'date') el.value = '';
        });
        handlePeriodChange();
        renderEmployeeTable([], true);
        ui.reportViews.forEach(v => v.innerHTML = '<p class="p-4 text-center text-gray-500">Seleccione empleados y genere un reporte para ver los resultados.</p>');
        ui.reportActions.classList.add('hidden');
        hideAutocomplete();
        // Limpiar resultado de búsqueda por hash de sección 3.3
        if (ui.hashSearchResults) {
            ui.hashSearchResults.innerHTML = '';
            ui.hashSearchResults.classList.add('hidden');
        }
    }

    function updateGenerateReportButtonState() {
        // El botón siempre habilitado — si no hay empleados se genera reporte vacío
        ui.generateReportBtn.disabled = false;
    }

    // ═══════════════════════════════════════════════════════
    //  POPULATE FILTERS
    // ═══════════════════════════════════════════════════════
    function populateFilters() {
        populateSelect(filters.jornada, appData.jornadas);
        populateSelect(filters.turnos,  appData.turnos);
        populateSelect(filters.cargo,   appData.cargos);
        populateSelect(filters.region,  appData.regiones);
        filters.region.addEventListener('change', populateLocales);
        populateLocales();
    }

    function populateLocales() {
        const selectedRegion  = filters.region.value;
        const cid = parseInt(document.getElementById('company-header-info').dataset.companyId);
        filters.local.innerHTML = '';
        appData.locales
            .filter(l => l.id === 'Todos' || (l.companyId === cid && (selectedRegion === 'Todas' || l.region === selectedRegion)))
            .forEach(l => {
                const o = document.createElement('option');
                o.value = l.id; o.textContent = l.name;
                filters.local.appendChild(o);
            });
    }

    function populateSelect(select, options) {
        select.innerHTML = '';
        (options || []).forEach(val => {
            const o = document.createElement('option');
            o.value = val; o.textContent = val;
            select.appendChild(o);
        });
    }

    // ═══════════════════════════════════════════════════════
    //  HELPERS
    // ═══════════════════════════════════════════════════════
    /**
     * Calcula y formatea la compensación semanal.
     * extra - atraso:
     *   > 0 → trabajador tiene horas a favor  (+HH:MM:SS en verde)
     *   < 0 → trabajador tiene horas en contra (-HH:MM:SS en rojo)
     *   = 0 → equilibrado                      (00:00:00 en gris)
     */
    function formatCompensacion(extraSec, atrasoSec) {
        const diff = extraSec - atrasoSec;
        if (diff > 0) return { texto: formatSecondsToTime(diff, null, '+'), clase: 'text-green-700' };
        if (diff < 0) return { texto: formatSecondsToTime(Math.abs(diff), null, '-'), clase: 'text-red-700' };
        return { texto: '00:00:00', clase: 'text-gray-400' };
    }

    function formatSecondsToTime(seconds, _u, sign = '') {
        if (seconds < 0) seconds = 0;
        const h = Math.floor(seconds / 3600);
        const m = Math.floor((seconds % 3600) / 60);
        const s = Math.floor(seconds % 60);
        return sign + `${String(h).padStart(2,'0')}:${String(m).padStart(2,'0')}:${String(s).padStart(2,'0')}`;
    }

    function toISODateString(date) { return date.toISOString().split('T')[0]; }

    function handlePeriodChange() {
        const period = filters.predeterminedPeriod.value;
        const today  = new Date();
        let   start  = new Date();
        const isCustom = period === 'custom';
        filters.startDate.disabled = !isCustom;
        filters.endDate.disabled   = !isCustom;
        if (isCustom) return;
        switch (period) {
            case 'last_week':      start.setDate(today.getDate() - 7);          break;
            case 'last_fortnight': start.setDate(today.getDate() - 15);         break;
            case 'last_month':     start.setMonth(today.getMonth() - 1);        break;
            case 'last_3_months':  start.setMonth(today.getMonth() - 3);        break;
            case 'last_6_months':  start.setMonth(today.getMonth() - 6);        break;
            case 'last_12_months': start.setFullYear(today.getFullYear() - 1);  break;
        }
        filters.startDate.value = toISODateString(start);
        filters.endDate.value   = toISODateString(today);
    }

    // ═══════════════════════════════════════════════════════
    //  INCIDENT REPORT
    // ═══════════════════════════════════════════════════════
    function renderIncidentReport() {
        const sDate = ui.incidentStartDate && ui.incidentStartDate.value ? new Date(ui.incidentStartDate.value + 'T00:00:00') : null;
        const eDate = ui.incidentEndDate   && ui.incidentEndDate.value   ? new Date(ui.incidentEndDate.value   + 'T23:59:59') : null;
        let incidents = appData.incidentes || [];
        if (sDate && eDate) incidents = incidents.filter(inc => { const d = new Date(inc.start); return d >= sDate && d <= eDate; });
        if (incidents.length === 0) {
            ui.incidentTableContainer.innerHTML = `<p class="p-4 text-center text-gray-500">No se encontraron incidentes para el rango seleccionado.</p>`;
            return;
        }
        ui.incidentTableContainer.innerHTML = `
            <table id="incident-table" class="report-table">
                <thead><tr><th>Fecha/Hora Inicio</th><th>Fecha/Hora Término</th><th>Descripción del Incidente</th></tr></thead>
                <tbody>${incidents.map(inc => `
                    <tr>
                        <td>${new Date(inc.start).toLocaleString('es-CL')}</td>
                        <td>${new Date(inc.end).toLocaleString('es-CL')}</td>
                        <td style="white-space:normal;">${inc.description}</td>
                    </tr>`).join('')}
                </tbody>
            </table>`;
    }

    // ═══════════════════════════════════════════════════════
    //  COMPANY LIST & SELECTION
    // ═══════════════════════════════════════════════════════
    function renderCompanyList() {
        const statusFilter = ui.companyStatusFilter.value;
        let companies = appData.companies || [];
        if (statusFilter !== 'all') companies = companies.filter(c => c.status === statusFilter);
        if (companies.length === 0) {
            ui.companyTableContainer.innerHTML = `<p class="p-4 text-center text-gray-500">No se encontraron empresas.</p>`;
            return;
        }
        ui.companyTableContainer.innerHTML = `
            <table id="company-table" class="report-table">
                <thead><tr><th>RUT</th><th>Nombre de la Empresa</th><th>Estado</th><th>Fecha de Registro</th></tr></thead>
                <tbody>${companies.map(c => `
                    <tr>
                        <td>${c.rut}</td>
                        <td style="white-space:normal;">${c.name}</td>
                        <td><span class="px-2 py-1 text-xs font-semibold rounded-full ${c.status === 'active' ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'}">${c.status === 'active' ? 'Activa' : 'Inactiva'}</span></td>
                        <td>${new Date(c.registered + 'T00:00:00').toLocaleDateString('es-CL')}</td>
                    </tr>`).join('')}
                </tbody>
            </table>`;
    }

    // ── Tab switching ────────────────────────────────────────
    function switchCompanyTab(tab) {
        const isSearch = tab === 'search';

        // Active tab button styles
        const activeClass   = ['border-indigo-600', 'text-indigo-600', 'bg-indigo-50', 'font-semibold'];
        const inactiveClass = ['border-transparent', 'text-gray-500', 'font-medium'];

        if (ui.tabSearchBtn && ui.tabListBtn) {
            // Reset both
            ui.tabSearchBtn.classList.remove(...activeClass, ...inactiveClass);
            ui.tabListBtn.classList.remove(...activeClass, ...inactiveClass);
            // Apply correct state
            ui.tabSearchBtn.classList.add(...(isSearch ? activeClass : inactiveClass));
            ui.tabListBtn.classList.add(...(!isSearch ? activeClass : inactiveClass));
        }

        // Show/hide panels
        if (ui.tabSearchPanel) ui.tabSearchPanel.classList.toggle('hidden', !isSearch);
        if (ui.tabListPanel)   ui.tabListPanel.classList.toggle('hidden', isSearch);

        // Clear state when switching
        if (isSearch) {
            if (ui.companyDirectSearch) ui.companyDirectSearch.value = '';
            if (ui.companyDirectResults) ui.companyDirectResults.classList.add('hidden');
            hideDirectCompanyError();
        } else {
            if (ui.companySearch) ui.companySearch.value = '';
            renderCompanySelectionList(appData.companies);
        }
    }

    // ── Tab 1: Autocompletado sin lista ──────────────────────
    function handleDirectCompanySearch() {
        const term = ui.companyDirectSearch.value.trim().toLowerCase();
        hideDirectCompanyError();
        ui.companyDirectResults.innerHTML = '';
        ui.companyDirectResults.classList.add('hidden');

        if (term.length < 2) return;

        const matches = appData.companies.filter(c =>
            c.name.toLowerCase().includes(term) || c.rut.toLowerCase().includes(term)
        );

        if (matches.length === 0) {
            // Mostrar mensaje "sin resultados" inline
            ui.companyDirectResults.innerHTML = `
                <div class="flex items-center space-x-3 px-4 py-4 text-gray-400 text-sm">
                    <i data-lucide="search-x" class="w-5 h-5 flex-shrink-0"></i>
                    <span>No se encontraron empresas con "<strong>${ui.companyDirectSearch.value}</strong>"</span>
                </div>`;
            ui.companyDirectResults.classList.remove('hidden');
            lucide.createIcons();
            return;
        }

        // Resultados inline — el modal crece
        ui.companyDirectResults.innerHTML = `
            <div class="px-4 py-2 bg-gray-50 border-b border-gray-200 text-xs text-gray-500 font-medium uppercase tracking-wide">
                ${matches.length} empresa${matches.length !== 1 ? 's' : ''} encontrada${matches.length !== 1 ? 's' : ''}
            </div>
            <ul class="divide-y divide-gray-100">
                ${matches.map(c => `
                    <li class="px-4 py-3 hover:bg-indigo-50 cursor-pointer direct-company-item transition-colors"
                        data-company-id="${c.id}">
                        <div class="flex items-center justify-between">
                            <div>
                                <p class="font-semibold text-sm text-gray-900">${c.name}</p>
                                <p class="text-xs text-gray-500 mt-0.5">${c.rut}</p>
                            </div>
                            <span class="ml-3 px-2 py-0.5 rounded-full text-xs font-medium flex-shrink-0
                                ${c.status === 'active' ? 'bg-green-100 text-green-700' : 'bg-red-100 text-red-700'}">
                                ${c.status === 'active' ? 'Activa' : 'Inactiva'}
                            </span>
                        </div>
                    </li>`).join('')}
            </ul>`;
        ui.companyDirectResults.classList.remove('hidden');

        ui.companyDirectResults.querySelectorAll('.direct-company-item').forEach(item => {
            item.addEventListener('click', async () => {
                const company = appData.companies.find(c => c.id === parseInt(item.dataset.companyId));
                ui.companyDirectSearch.value = '';
                ui.companyDirectResults.innerHTML = '';
                ui.companyDirectResults.classList.add('hidden');
                await selectCompany(company);
            });
        });
    }

    // ── Tab 1: Botón Acceder ─────────────────────────────────
    async function accessDirectCompany() {
        const term = ui.companyDirectSearch.value.trim().toLowerCase();
        hideDirectCompanyError();

        if (!term) {
            showDirectCompanyError('Ingrese el RUT o nombre de la empresa.');
            return;
        }

        const matches = appData.companies.filter(c =>
            c.name.toLowerCase().includes(term) || c.rut.toLowerCase().includes(term)
        );

        if (matches.length === 0) {
            showDirectCompanyError('No se encontró ninguna empresa con ese RUT o nombre.');
            return;
        }
        if (matches.length > 1) {
            showDirectCompanyError(
                `Se encontraron ${matches.length} empresas. Refine su búsqueda o use la pestaña "Explorar Empresas".`
            );
            return;
        }
        // Exactamente 1 coincidencia
        ui.companyDirectResults.classList.add('hidden');
        await selectCompany(matches[0]);
    }

    function showDirectCompanyError(msg) {
        if (ui.companyDirectErrorMsg) ui.companyDirectErrorMsg.textContent = msg;
        if (ui.companyDirectError)    ui.companyDirectError.classList.remove('hidden');
    }

    function hideDirectCompanyError() {
        if (ui.companyDirectError) ui.companyDirectError.classList.add('hidden');
    }

    function renderCompanySelectionList(companies) {
        if (!companies || companies.length === 0) {
            ui.companyList.innerHTML = `<p class="p-4 text-center text-gray-500">No se encontraron empresas.</p>`;
            return;
        }
        ui.companyList.innerHTML = `<ul class="divide-y divide-gray-200">${companies.map(c =>
            `<li class="p-4 hover:bg-gray-50 cursor-pointer company-select-item" data-company-id="${c.id}">
                <p class="font-semibold text-gray-900">${c.name}</p>
                <p class="text-sm text-gray-500">${c.rut}</p>
            </li>`).join('')}</ul>`;
        document.querySelectorAll('.company-select-item').forEach(item => {
            item.addEventListener('click', async (e) => {
                const cid = parseInt(e.currentTarget.dataset.companyId);
                await selectCompany(appData.companies.find(c => c.id === cid));
            });
        });
    }

    async function selectCompany(company) {
        try {
            const employees = await apiFetch(`${API_URL}/employees?companyId=${company.id}`).then(r => r.json());
            appData.employees = employees;
        } catch (err) { console.warn('No se pudo cargar empleados:', err); }
        ui.companyHeaderInfo.textContent       = `${company.name} | ${company.rut}`;
        ui.companyHeaderInfo.dataset.companyId = company.id;
        ui.companySelectionContainer.classList.add('hidden');
        ui.appContainer.classList.remove('hidden');
        filters.region.value = 'Todas';
        populateLocales();
        navigateToPage('reportes-dt');
    }

    // ═══════════════════════════════════════════════════════
    //  NAVIGATION
    // ═══════════════════════════════════════════════════════
    function navigateToPage(targetId) {
        ui.navItems.forEach(i => i.classList.remove('bg-indigo-100', 'text-indigo-700', 'font-semibold'));
        const nav = document.querySelector(`.nav-item[data-target="${targetId}"]`);
        if (nav) nav.classList.add('bg-indigo-100', 'text-indigo-700', 'font-semibold');
        ui.pages.forEach(p => p.id === `page-${targetId}` ? p.classList.remove('hidden') : p.classList.add('hidden'));
        if (nav) ui.pageTitle.textContent = nav.querySelector('span').textContent;
    }

    function setupMainMenuNavigation() {
        ui.navItems.forEach(item => {
            item.addEventListener('click', (e) => {
                e.preventDefault();
                ui.navItems.forEach(i => i.classList.remove('bg-indigo-100', 'text-indigo-700', 'font-semibold'));
                item.classList.add('bg-indigo-100', 'text-indigo-700', 'font-semibold');
                const tid = item.dataset.target;
                ui.pages.forEach(p => p.id === `page-${tid}` ? p.classList.remove('hidden') : p.classList.add('hidden'));
                ui.pageTitle.textContent = item.querySelector('span').textContent;
                if (window.innerWidth < 768) ui.sidebar.classList.add('-translate-x-full');
            });
        });
    }

    function setupReportSubMenuNavigation() {
        ui.reportNavItems.forEach(item => {
            item.addEventListener('click', () => {
                ui.reportNavItems.forEach(i => {
                    i.classList.remove('bg-indigo-100', 'text-indigo-700', 'font-semibold');
                    i.classList.add('text-gray-600', 'hover:bg-gray-200', 'font-medium');
                });
                item.classList.add('bg-indigo-100', 'text-indigo-700', 'font-semibold');
                item.classList.remove('text-gray-600', 'hover:bg-gray-200', 'font-medium');
                const rid = item.dataset.report;
                ui.reportViews.forEach(v => v.id === `report-${rid}` ? v.classList.remove('hidden') : v.classList.add('hidden'));
                ui.reportTitleEl.textContent = item.textContent;
            });
        });
    }

    // ═══════════════════════════════════════════════════════
    //  EXPORT FUNCTIONS
    // ═══════════════════════════════════════════════════════
    function getActiveReportContent(containerId = null) {
        if (containerId) return document.getElementById(containerId);
        return document.getElementById(`report-${ui.reportNavContainer.querySelector('.bg-indigo-100').dataset.report}`);
    }

    function printReport(containerId = null) {
        const c = getActiveReportContent(containerId);
        if (!c) return;
        document.getElementById('print-area').innerHTML = c.innerHTML;
        window.print();
        document.getElementById('print-area').innerHTML = '';
    }

    function exportToPDF(containerId = null, title = null) {
        const c = getActiveReportContent(containerId);
        if (!c) return;
        const { jsPDF } = window.jspdf;
        const doc = new jsPDF({ orientation: 'l', unit: 'px', format: 'a4' });
        const t = title || ui.reportTitleEl.textContent;
        doc.setFontSize(18); doc.text(t, 20, 30);
        let y = 50;
        c.querySelectorAll('.report-table').forEach(table => {
            doc.autoTable({ html: table, startY: y, theme: 'grid', styles: { fontSize: 6, cellPadding: 2 }, headStyles: { fillColor: [243,244,246], textColor:[0,0,0], fontStyle:'bold' } });
            y = doc.lastAutoTable.finalY + 20;
        });
        doc.save(`${t}.pdf`);
    }

    function exportToExcel(containerId = null, title = null) {
        const c = getActiveReportContent(containerId);
        if (!c) return;
        const t  = title || ui.reportTitleEl.textContent;
        const wb = XLSX.utils.book_new();
        let data = [];
        c.querySelectorAll('.report-table').forEach(table => {
            data.push(Array.from(table.querySelectorAll('thead th')).map(th => th.innerText));
            Array.from(table.querySelectorAll('tbody tr')).forEach(tr => data.push(Array.from(tr.querySelectorAll('td')).map(td => td.innerText)));
            data.push([]);
        });
        XLSX.utils.book_append_sheet(wb, XLSX.utils.aoa_to_sheet(data), t.substring(0, 31));
        XLSX.writeFile(wb, `${t}.xlsx`);
    }

    function exportToWord(containerId = null, title = null) {
        const c = getActiveReportContent(containerId);
        if (!c) return;
        const t = title || ui.reportTitleEl.textContent;
        const blob = new Blob([`\ufeff<html xmlns:o='urn:schemas-microsoft-com:office:office' xmlns:w='urn:schemas-microsoft-com:office:word'><head><meta charset='utf-8'></head><body><h1>${t}</h1>${c.innerHTML}</body></html>`], { type: 'application/msword' });
        const a = Object.assign(document.createElement('a'), { href: URL.createObjectURL(blob), download: `${t}.doc` });
        document.body.appendChild(a); a.click(); document.body.removeChild(a);
    }

    // ═══════════════════════════════════════════════════════
    //  DOM CONTENT LOADED — ÚNICA entrada, CERO APIs
    // ═══════════════════════════════════════════════════════
    document.addEventListener('DOMContentLoaded', function () {
        ui = {
            loginContainer:            document.getElementById('login-container'),
            appContainer:              document.getElementById('app-container'),
            loginBtn:                  document.getElementById('login-btn'),
            loginError:                document.getElementById('login-error'),
            forgotPasswordLink:        document.getElementById('forgot-password-link'),
            requestPasswordLink:       document.getElementById('request-password-link'),
            modalContainer:            document.getElementById('modal-container'),
            modalTitle:                document.getElementById('modal-title'),
            modalContent:              document.getElementById('modal-content'),
            closeModalBtn:             document.getElementById('close-modal-btn'),
            menuButton:                document.getElementById('menu-button'),
            sidebar:                   document.getElementById('sidebar'),
            pageTitle:                 document.getElementById('page-title'),
            navItems:                  document.querySelectorAll('.nav-item'),
            pages:                     document.querySelectorAll('.page'),
            reportNavContainer:        document.getElementById('report-nav-container'),
            reportNavItems:            document.querySelectorAll('.report-nav-item'),
            reportViews:               document.querySelectorAll('.report-view'),
            reportTitleEl:             document.getElementById('report-title'),
            searchBtn:                 document.getElementById('search-btn'),
            clearFiltersBtn:           document.getElementById('clear-filters-btn'),
            employeeTableContainer:    document.getElementById('employee-table-container'),
            generateReportBtn:         document.getElementById('generate-report-btn'),
            reportActions:             document.getElementById('report-actions'),
            individualSearchContainer: document.getElementById('individual-search-container'),
            autocompleteResults:       document.getElementById('autocomplete-results'),
            validateHashBtn:           document.getElementById('validate-hash-btn'),
            hashInput:                 document.getElementById('hash-input'),
            hashResultsContainer:      document.getElementById('hash-results-container'),
            validateHashReportBtn:     document.getElementById('validate-hash-report-btn'),
            hashSearchResults:         document.getElementById('hash-search-results'),
            printBtn:                  document.getElementById('print-btn'),
            pdfBtn:                    document.getElementById('pdf-btn'),
            excelBtn:                  document.getElementById('excel-btn'),
            wordBtn:                   document.getElementById('word-btn'),
            printableReportArea:       document.getElementById('printable-report-area'),
            incidentStartDate:         document.getElementById('incident-start-date'),
            incidentEndDate:           document.getElementById('incident-end-date'),
            searchIncidentsBtn:        document.getElementById('search-incidents-btn'),
            incidentTableContainer:    document.getElementById('incident-table-container'),
            incidentPrintBtn:          document.getElementById('incident-print-btn'),
            incidentPdfBtn:            document.getElementById('incident-pdf-btn'),
            incidentExcelBtn:          document.getElementById('incident-excel-btn'),
            incidentWordBtn:           document.getElementById('incident-word-btn'),
            companyStatusFilter:       document.getElementById('company-status-filter'),
            companyTableContainer:     document.getElementById('company-table-container'),
            companyPrintBtn:           document.getElementById('company-print-btn'),
            companyPdfBtn:             document.getElementById('company-pdf-btn'),
            companyExcelBtn:           document.getElementById('company-excel-btn'),
            companyWordBtn:            document.getElementById('company-word-btn'),
            companySelectionContainer: document.getElementById('company-selection-container'),
            companySearch:             document.getElementById('company-search'),
            companyList:               document.getElementById('company-list'),
            companyHeaderInfo:         document.getElementById('company-header-info'),
            logoutBtn:                 document.getElementById('logout-btn'),
            // Tabs de selección de empresa
            tabSearchBtn:              document.getElementById('tab-search-btn'),
            tabListBtn:                document.getElementById('tab-list-btn'),
            tabSearchPanel:            document.getElementById('tab-search-panel'),
            tabListPanel:              document.getElementById('tab-list-panel'),
            companyDirectSearch:       document.getElementById('company-direct-search'),
            companyDirectBtn:          document.getElementById('company-direct-btn'),
            companyDirectResults:      document.getElementById('company-direct-results'),
            companyDirectError:        document.getElementById('company-direct-error'),
            companyDirectErrorMsg:     document.getElementById('company-direct-error-msg'),
        };
        filters = {
            startDate:           document.getElementById('start-date'),
            endDate:             document.getElementById('end-date'),
            predeterminedPeriod: document.getElementById('predetermined-period'),
            individualSearch:    document.getElementById('individual-search'),
            hashSearch:          document.getElementById('hash-search'),
            jornada:             document.getElementById('jornada'),
            turnos:              document.getElementById('turnos'),
            region:              document.getElementById('region'),
            local:               document.getElementById('local'),
            cargo:               document.getElementById('cargo'),
            est:                 document.getElementById('est'),
        };
        // Estas 3 lineas son TODO lo que hace DOMContentLoaded
        showLogin();
        lucide.createIcons();
        setupEventListeners();
    }); // <- unico cierre de DOMContentLoaded

    /**
     * b.12 — Fila de totales semanales con total por columna
     * Columnas: Fecha | Jornada Pactada | Entrada/Salida | Col.Pactada | Col.Real |
     *           Atraso | Extras | Otras | Observaciones | [Ciclo]
     *
     * Fórmula jornada neta = Σ(jornadaPactada) - Σ(colacionPactada)
     * (Art. 27 b.12: "restará del total de Jornada ordinaria pactada el tiempo de colación pactada")
     */
    function buildWeeklyTotal(wk, numCols, tieneJornadaExcepcional, employee) {
        const netoJornada   = wk.jornada - wk.colPactada;   // b.12 fórmula
        const comp          = formatCompensacion(wk.extra, wk.atraso);
        const cicloBlank    = tieneJornadaExcepcional ? '<td class="text-gray-400">—</td>' : '';

        // Color de neto: rojo si hay más atraso que extra, verde si hay exceso
        const netoClass = wk.extra > wk.atraso ? 'text-green-700'
                        : wk.extra < wk.atraso ? 'text-red-700'
                        : 'text-gray-600';

        return `<tr class="total-row bg-indigo-50 font-bold text-xs border-t-2 border-indigo-300">
            <td class="text-right text-gray-600 text-[10px] italic">TOTALES (b.12)</td>
            <td class="${netoClass}" title="Jornada pactada neta = ${formatSecondsToTime(wk.jornada)} − ${formatSecondsToTime(wk.colPactada)} col. pactada">
                ${formatSecondsToTime(netoJornada)}
            </td>
            <td class="text-gray-600" title="Total neto en recinto = presencia bruta − colación real">${formatSecondsToTime(Math.max(0, wk.presencia - wk.colReal))}</td>
            <td class="text-gray-600">${formatSecondsToTime(wk.colPactada)}</td>
            <td class="text-gray-600">${formatSecondsToTime(wk.colReal)}</td>
            <td class="text-red-600">${wk.atraso > 0 ? formatSecondsToTime(wk.atraso, null, '-') : '00:00:00'}</td>
            <td class="text-green-600">${wk.extra > 0 ? formatSecondsToTime(wk.extra, null, '+') : '00:00:00'}</td>
            <td class="text-gray-400">—</td>
            <td class="${comp.clase} font-bold">Comp: ${comp.texto}</td>
            ${cicloBlank}
        </tr>`;
    }


    /** Construye una tabla de domingos/festivos para un bloque de días */


    function generateShiftModificationReport(emps) {
        let html = '';
        emps.forEach(() => {
            const changes = generateMockShiftChanges();
            const tbody = changes.length > 0
                ? changes.map(c => `<tr><td class="text-center">${c.fechaAsignacion}</td><td class="text-center">${c.turno}</td><td class="text-center">${c.extension}</td><td class="text-center">${c.fechaNuevo}</td><td class="text-center">${c.inicioNuevo}</td><td class="text-center">${c.nuevoTurno}</td><td class="text-center">${c.nuevaExtension}</td><td class="text-center">${c.solicitante}</td><td class="text-left text-xs">${c.obs}</td></tr>`).join('')
                : `<tr><td colspan="9" class="text-center p-4 text-gray-500">No se registran modificaciones en el periodo.</td></tr>`;
            html += `<table class="report-table w-full text-xs"><thead><tr class="bg-gray-100"><th>Fecha asignación turno</th><th>Turno asignado</th><th>Extensión del turno</th><th>Fecha asignación nuevo turno</th><th>Inicio de turno</th><th>Nuevo turno asignado</th><th>Extensión nuevo turno</th><th>Solicitante</th><th>Observaciones</th></tr></thead><tbody>${tbody}</tbody></table>`;
        });
        return html;
    }

    // ═══════════════════════════════════════════════════════
    //  HELPER FESTIVOS
    // ═══════════════════════════════════════════════════════

    /** Devuelve true si la fecha es domingo o festivo */
    function isDomingoOFestivo(date) {
        if (date.getDay() === 0) return true;  // domingo
        const iso = toISODateString(date);
        return appData.festivos.some(f => f.fecha === iso);
    }

    /** Si la fecha es festivo, devuelve su descripción; si no, null */
    function getNombreFestivo(date) {
        const iso = toISODateString(date);
        const f   = appData.festivos.find(f => f.fecha === iso);
        return f ? f.descripcion : null;
    }

        // ═══════════════════════════════════════════════════════
    //  MOCK DATA GENERATORS
    // ═══════════════════════════════════════════════════════
    function generateMockAttendanceData(date) {
        // Solo se llama para días hábiles (domingos y festivos ya excluidos en el report generator)
        const rand = Math.random();
        if (date.getDay() === 6 || rand < 0.1)
            return { asistencia:'No', asistenciaClass:'text-red-600', ausencia:'Justificada',   ausenciaClass:'text-blue-600', obs:'VAC' };
        if (rand < 0.15)
            return { asistencia:'No', asistenciaClass:'text-red-600', ausencia:'Injustificada', ausenciaClass:'text-red-600',  obs:'A.I.' };
        if (rand < 0.2)
            return { asistencia:'No', asistenciaClass:'text-red-600', ausencia:'Justificada',   ausenciaClass:'text-blue-600', obs:'L.M.' };
        return { asistencia:'Sí', asistenciaClass:'text-green-600', ausencia:'', ausenciaClass:'', obs:'' };
    }


    function generateMockShiftChanges() {
        if (Math.random() > 0.3) return [
            { fechaAsignacion:'15/01/24', turno:'08:00 a 17:00', extension:'Semanal',   fechaNuevo:'10/06/24', inicioNuevo:'17/06/24', nuevoTurno:'14:00 a 22:00', nuevaExtension:'Semanal',   solicitante:'Empleador', obs:'Reestructuración operacional por alta demanda.' },
            { fechaAsignacion:'17/06/24', turno:'14:00 a 22:00', extension:'Semanal',   fechaNuevo:'01/09/24', inicioNuevo:'05/09/24', nuevoTurno:'09:00 a 18:00', nuevaExtension:'Bisemanal', solicitante:'Trabajador', obs:'Acuerdo mutuo por motivos familiares.' },
        ];
        return [];
    }

    // ═══════════════════════════════════════════════════════
    //  AUTOCOMPLETE & FILTERING
    // ═══════════════════════════════════════════════════════
    function handleIndividualSearch() {
        const term = filters.individualSearch.value.trim().toLowerCase();
        applyFilters();
        if (term.length > 0) renderAutocomplete(appData.employees.filter(e => e.name.toLowerCase().includes(term) || e.rut.includes(term)));
        else hideAutocomplete();
    }

    function renderAutocomplete(suggestions) {
        if (suggestions.length === 0) { hideAutocomplete(); return; }
        ui.autocompleteResults.innerHTML = `<ul class="divide-y divide-gray-100">${suggestions.map(s =>
            `<li class="p-2 hover:bg-indigo-100 cursor-pointer autocomplete-item" data-name="${s.name}">${s.name} <span class="text-gray-500 text-xs">(${s.rut})</span></li>`
        ).join('')}</ul>`;
        ui.autocompleteResults.classList.remove('hidden');
        document.querySelectorAll('.autocomplete-item').forEach(item => {
            item.addEventListener('click', (e) => {
                filters.individualSearch.value = e.currentTarget.dataset.name;
                hideAutocomplete(); applyFilters(); updateGenerateReportButtonState();
            });
        });
    }

    function hideAutocomplete() {
        ui.autocompleteResults.innerHTML = '';
        ui.autocompleteResults.classList.add('hidden');
    }

    function applyFilters() {
        const cid = parseInt(document.getElementById('company-header-info').dataset.companyId);
        let emps = appData.employees.filter(e => e.companyId === cid);
        const term = filters.individualSearch.value.toLowerCase().trim();
        if (term)                           emps = emps.filter(e => e.name.toLowerCase().includes(term) || e.rut.includes(term));
        if (filters.jornada.value !== 'Todas') emps = emps.filter(e => e.jornada === filters.jornada.value);
        if (filters.turnos.value  !== 'Todos') emps = emps.filter(e => e.turno   === filters.turnos.value);
        if (filters.region.value  !== 'Todas') emps = emps.filter(e => e.region  === filters.region.value);
        if (filters.local.value   !== 'Todos') emps = emps.filter(e => e.local   === filters.local.value);
        if (filters.cargo.value   !== 'Todos') emps = emps.filter(e => e.cargo   === filters.cargo.value);
        if (filters.est.value     !== 'todos') emps = emps.filter(e => e.est     === filters.est.value);
        renderEmployeeTable(emps);
        updateGenerateReportButtonState();
    }

    function renderEmployeeTable(employees, isInitialState = false) {
        if (isInitialState) {
            ui.employeeTableContainer.innerHTML = `<div class="text-center py-8 text-gray-500"><i data-lucide="search-check" class="mx-auto h-12 w-12 text-gray-400"></i><h3 class="mt-2 text-sm font-medium text-gray-900">Listo para buscar</h3><p class="mt-1 text-sm text-gray-500">Use los filtros y presione Aplicar Filtros Grupales para comenzar.</p></div>`;
            lucide.createIcons(); return;
        }
        if (employees.length === 0) {
            ui.employeeTableContainer.innerHTML = `<div class="text-center py-8 text-gray-500"><i data-lucide="user-x" class="mx-auto h-12 w-12 text-gray-400"></i><h3 class="mt-2 text-sm font-medium text-gray-900">No hay trabajadores que coincidan</h3><p class="mt-1 text-sm text-gray-500">Intente ajustar los filtros.</p></div>`;
            lucide.createIcons();
        } else {
            ui.employeeTableContainer.innerHTML = `<table class="report-table text-left"><thead><tr>
                <th class="w-10"><input type="checkbox" id="select-all-employees" class="rounded border-gray-300"></th>
                <th>RUT</th><th>Nombre</th><th>Cargo</th><th>Local / Faena</th>
            </tr></thead><tbody>${employees.map(e =>
                `<tr><td><input type="checkbox" class="employee-checkbox rounded border-gray-300" data-id="${e.id}"></td>
                 <td>${e.rut}</td><td>${e.name}</td><td>${e.cargo}</td><td>${getLocalName(e.local)}</td></tr>`
            ).join('')}</tbody></table>`;
            document.getElementById('select-all-employees').addEventListener('change', ev => {
                document.querySelectorAll('.employee-checkbox').forEach(cb => cb.checked = ev.target.checked);
                updateGenerateReportButtonState();
            });
        }
    }

    function getLocalName(localId) {
        const o = appData.locales.find(l => l.id === localId);
        return o ? o.name : localId;
    }

    function getSingleEmployeeFromSearch() {
        const term = filters.individualSearch.value.trim();
        if (!term) return null;
        const found = appData.employees.filter(e => e.name.toLowerCase() === term.toLowerCase() || e.rut === term);
        return found.length === 1 ? found[0] : null;
    }

    function clearAllFilters() {
        Object.values(filters).forEach(el => {
            if (el.tagName === 'SELECT') el.selectedIndex = 0;
            else if (el.type !== 'date') el.value = '';
        });
        handlePeriodChange();
        renderEmployeeTable([], true);
        ui.reportViews.forEach(v => v.innerHTML = '<p class="p-4 text-center text-gray-500">Seleccione empleados y genere un reporte para ver los resultados.</p>');
        ui.reportActions.classList.add('hidden');
        hideAutocomplete();
        // Limpiar resultado de búsqueda por hash de sección 3.3
        if (ui.hashSearchResults) {
            ui.hashSearchResults.innerHTML = '';
            ui.hashSearchResults.classList.add('hidden');
        }
    }

    function updateGenerateReportButtonState() {
        // El botón siempre habilitado — si no hay empleados se genera reporte vacío
        ui.generateReportBtn.disabled = false;
    }

    // ═══════════════════════════════════════════════════════
    //  POPULATE FILTERS
    // ═══════════════════════════════════════════════════════
    function populateFilters() {
        populateSelect(filters.jornada, appData.jornadas);
        populateSelect(filters.turnos,  appData.turnos);
        populateSelect(filters.cargo,   appData.cargos);
        populateSelect(filters.region,  appData.regiones);
        filters.region.addEventListener('change', populateLocales);
        populateLocales();
    }

    function populateLocales() {
        const selectedRegion  = filters.region.value;
        const cid = parseInt(document.getElementById('company-header-info').dataset.companyId);
        filters.local.innerHTML = '';
        appData.locales
            .filter(l => l.id === 'Todos' || (l.companyId === cid && (selectedRegion === 'Todas' || l.region === selectedRegion)))
            .forEach(l => {
                const o = document.createElement('option');
                o.value = l.id; o.textContent = l.name;
                filters.local.appendChild(o);
            });
    }

    function populateSelect(select, options) {
        select.innerHTML = '';
        (options || []).forEach(val => {
            const o = document.createElement('option');
            o.value = val; o.textContent = val;
            select.appendChild(o);
        });
    }

    // ═══════════════════════════════════════════════════════
    //  HELPERS
    // ═══════════════════════════════════════════════════════
    /**
     * Calcula y formatea la compensación semanal.
     * extra - atraso:
     *   > 0 → trabajador tiene horas a favor  (+HH:MM:SS en verde)
     *   < 0 → trabajador tiene horas en contra (-HH:MM:SS en rojo)
     *   = 0 → equilibrado                      (00:00:00 en gris)
     */
    function formatCompensacion(extraSec, atrasoSec) {
        const diff = extraSec - atrasoSec;
        if (diff > 0) return { texto: formatSecondsToTime(diff, null, '+'), clase: 'text-green-700' };
        if (diff < 0) return { texto: formatSecondsToTime(Math.abs(diff), null, '-'), clase: 'text-red-700' };
        return { texto: '00:00:00', clase: 'text-gray-400' };
    }

    function formatSecondsToTime(seconds, _u, sign = '') {
        if (seconds < 0) seconds = 0;
        const h = Math.floor(seconds / 3600);
        const m = Math.floor((seconds % 3600) / 60);
        const s = Math.floor(seconds % 60);
        return sign + `${String(h).padStart(2,'0')}:${String(m).padStart(2,'0')}:${String(s).padStart(2,'0')}`;
    }

    function toISODateString(date) { return date.toISOString().split('T')[0]; }

    function handlePeriodChange() {
        const period = filters.predeterminedPeriod.value;
        const today  = new Date();
        let   start  = new Date();
        const isCustom = period === 'custom';
        filters.startDate.disabled = !isCustom;
        filters.endDate.disabled   = !isCustom;
        if (isCustom) return;
        switch (period) {
            case 'last_week':      start.setDate(today.getDate() - 7);          break;
            case 'last_fortnight': start.setDate(today.getDate() - 15);         break;
            case 'last_month':     start.setMonth(today.getMonth() - 1);        break;
            case 'last_3_months':  start.setMonth(today.getMonth() - 3);        break;
            case 'last_6_months':  start.setMonth(today.getMonth() - 6);        break;
            case 'last_12_months': start.setFullYear(today.getFullYear() - 1);  break;
        }
        filters.startDate.value = toISODateString(start);
        filters.endDate.value   = toISODateString(today);
    }

    // ═══════════════════════════════════════════════════════
    //  INCIDENT REPORT
    // ═══════════════════════════════════════════════════════
    function renderIncidentReport() {
        const sDate = ui.incidentStartDate && ui.incidentStartDate.value ? new Date(ui.incidentStartDate.value + 'T00:00:00') : null;
        const eDate = ui.incidentEndDate   && ui.incidentEndDate.value   ? new Date(ui.incidentEndDate.value   + 'T23:59:59') : null;
        let incidents = appData.incidentes || [];
        if (sDate && eDate) incidents = incidents.filter(inc => { const d = new Date(inc.start); return d >= sDate && d <= eDate; });
        if (incidents.length === 0) {
            ui.incidentTableContainer.innerHTML = `<p class="p-4 text-center text-gray-500">No se encontraron incidentes para el rango seleccionado.</p>`;
            return;
        }
        ui.incidentTableContainer.innerHTML = `
            <table id="incident-table" class="report-table">
                <thead><tr><th>Fecha/Hora Inicio</th><th>Fecha/Hora Término</th><th>Descripción del Incidente</th></tr></thead>
                <tbody>${incidents.map(inc => `
                    <tr>
                        <td>${new Date(inc.start).toLocaleString('es-CL')}</td>
                        <td>${new Date(inc.end).toLocaleString('es-CL')}</td>
                        <td style="white-space:normal;">${inc.description}</td>
                    </tr>`).join('')}
                </tbody>
            </table>`;
    }

    // ═══════════════════════════════════════════════════════
    //  COMPANY LIST & SELECTION
    // ═══════════════════════════════════════════════════════
    function renderCompanyList() {
        const statusFilter = ui.companyStatusFilter.value;
        let companies = appData.companies || [];
        if (statusFilter !== 'all') companies = companies.filter(c => c.status === statusFilter);
        if (companies.length === 0) {
            ui.companyTableContainer.innerHTML = `<p class="p-4 text-center text-gray-500">No se encontraron empresas.</p>`;
            return;
        }
        ui.companyTableContainer.innerHTML = `
            <table id="company-table" class="report-table">
                <thead><tr><th>RUT</th><th>Nombre de la Empresa</th><th>Estado</th><th>Fecha de Registro</th></tr></thead>
                <tbody>${companies.map(c => `
                    <tr>
                        <td>${c.rut}</td>
                        <td style="white-space:normal;">${c.name}</td>
                        <td><span class="px-2 py-1 text-xs font-semibold rounded-full ${c.status === 'active' ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'}">${c.status === 'active' ? 'Activa' : 'Inactiva'}</span></td>
                        <td>${new Date(c.registered + 'T00:00:00').toLocaleDateString('es-CL')}</td>
                    </tr>`).join('')}
                </tbody>
            </table>`;
    }

    // ── Tab switching ────────────────────────────────────────
    function switchCompanyTab(tab) {
        const isSearch = tab === 'search';

        // Active tab button styles
        const activeClass   = ['border-indigo-600', 'text-indigo-600', 'bg-indigo-50', 'font-semibold'];
        const inactiveClass = ['border-transparent', 'text-gray-500', 'font-medium'];

        if (ui.tabSearchBtn && ui.tabListBtn) {
            // Reset both
            ui.tabSearchBtn.classList.remove(...activeClass, ...inactiveClass);
            ui.tabListBtn.classList.remove(...activeClass, ...inactiveClass);
            // Apply correct state
            ui.tabSearchBtn.classList.add(...(isSearch ? activeClass : inactiveClass));
            ui.tabListBtn.classList.add(...(!isSearch ? activeClass : inactiveClass));
        }

        // Show/hide panels
        if (ui.tabSearchPanel) ui.tabSearchPanel.classList.toggle('hidden', !isSearch);
        if (ui.tabListPanel)   ui.tabListPanel.classList.toggle('hidden', isSearch);

        // Clear state when switching
        if (isSearch) {
            if (ui.companyDirectSearch) ui.companyDirectSearch.value = '';
            if (ui.companyDirectResults) ui.companyDirectResults.classList.add('hidden');
            hideDirectCompanyError();
        } else {
            if (ui.companySearch) ui.companySearch.value = '';
            renderCompanySelectionList(appData.companies);
        }
    }

    // ── Tab 1: Autocompletado sin lista ──────────────────────
    function handleDirectCompanySearch() {
        const term = ui.companyDirectSearch.value.trim().toLowerCase();
        hideDirectCompanyError();
        ui.companyDirectResults.innerHTML = '';
        ui.companyDirectResults.classList.add('hidden');

        if (term.length < 2) return;

        const matches = appData.companies.filter(c =>
            c.name.toLowerCase().includes(term) || c.rut.toLowerCase().includes(term)
        );

        if (matches.length === 0) {
            // Mostrar mensaje "sin resultados" inline
            ui.companyDirectResults.innerHTML = `
                <div class="flex items-center space-x-3 px-4 py-4 text-gray-400 text-sm">
                    <i data-lucide="search-x" class="w-5 h-5 flex-shrink-0"></i>
                    <span>No se encontraron empresas con "<strong>${ui.companyDirectSearch.value}</strong>"</span>
                </div>`;
            ui.companyDirectResults.classList.remove('hidden');
            lucide.createIcons();
            return;
        }

        // Resultados inline — el modal crece
        ui.companyDirectResults.innerHTML = `
            <div class="px-4 py-2 bg-gray-50 border-b border-gray-200 text-xs text-gray-500 font-medium uppercase tracking-wide">
                ${matches.length} empresa${matches.length !== 1 ? 's' : ''} encontrada${matches.length !== 1 ? 's' : ''}
            </div>
            <ul class="divide-y divide-gray-100">
                ${matches.map(c => `
                    <li class="px-4 py-3 hover:bg-indigo-50 cursor-pointer direct-company-item transition-colors"
                        data-company-id="${c.id}">
                        <div class="flex items-center justify-between">
                            <div>
                                <p class="font-semibold text-sm text-gray-900">${c.name}</p>
                                <p class="text-xs text-gray-500 mt-0.5">${c.rut}</p>
                            </div>
                            <span class="ml-3 px-2 py-0.5 rounded-full text-xs font-medium flex-shrink-0
                                ${c.status === 'active' ? 'bg-green-100 text-green-700' : 'bg-red-100 text-red-700'}">
                                ${c.status === 'active' ? 'Activa' : 'Inactiva'}
                            </span>
                        </div>
                    </li>`).join('')}
            </ul>`;
        ui.companyDirectResults.classList.remove('hidden');

        ui.companyDirectResults.querySelectorAll('.direct-company-item').forEach(item => {
            item.addEventListener('click', async () => {
                const company = appData.companies.find(c => c.id === parseInt(item.dataset.companyId));
                ui.companyDirectSearch.value = '';
                ui.companyDirectResults.innerHTML = '';
                ui.companyDirectResults.classList.add('hidden');
                await selectCompany(company);
            });
        });
    }

    // ── Tab 1: Botón Acceder ─────────────────────────────────
    async function accessDirectCompany() {
        const term = ui.companyDirectSearch.value.trim().toLowerCase();
        hideDirectCompanyError();

        if (!term) {
            showDirectCompanyError('Ingrese el RUT o nombre de la empresa.');
            return;
        }

        const matches = appData.companies.filter(c =>
            c.name.toLowerCase().includes(term) || c.rut.toLowerCase().includes(term)
        );

        if (matches.length === 0) {
            showDirectCompanyError('No se encontró ninguna empresa con ese RUT o nombre.');
            return;
        }
        if (matches.length > 1) {
            showDirectCompanyError(
                `Se encontraron ${matches.length} empresas. Refine su búsqueda o use la pestaña "Explorar Empresas".`
            );
            return;
        }
        // Exactamente 1 coincidencia
        ui.companyDirectResults.classList.add('hidden');
        await selectCompany(matches[0]);
    }

    function showDirectCompanyError(msg) {
        if (ui.companyDirectErrorMsg) ui.companyDirectErrorMsg.textContent = msg;
        if (ui.companyDirectError)    ui.companyDirectError.classList.remove('hidden');
    }

    function hideDirectCompanyError() {
        if (ui.companyDirectError) ui.companyDirectError.classList.add('hidden');
    }

    function renderCompanySelectionList(companies) {
        if (!companies || companies.length === 0) {
            ui.companyList.innerHTML = `<p class="p-4 text-center text-gray-500">No se encontraron empresas.</p>`;
            return;
        }
        ui.companyList.innerHTML = `<ul class="divide-y divide-gray-200">${companies.map(c =>
            `<li class="p-4 hover:bg-gray-50 cursor-pointer company-select-item" data-company-id="${c.id}">
                <p class="font-semibold text-gray-900">${c.name}</p>
                <p class="text-sm text-gray-500">${c.rut}</p>
            </li>`).join('')}</ul>`;
        document.querySelectorAll('.company-select-item').forEach(item => {
            item.addEventListener('click', async (e) => {
                const cid = parseInt(e.currentTarget.dataset.companyId);
                await selectCompany(appData.companies.find(c => c.id === cid));
            });
        });
    }

    async function selectCompany(company) {
        try {
            const employees = await apiFetch(`${API_URL}/employees?companyId=${company.id}`).then(r => r.json());
            appData.employees = employees;
        } catch (err) { console.warn('No se pudo cargar empleados:', err); }
        ui.companyHeaderInfo.textContent       = `${company.name} | ${company.rut}`;
        ui.companyHeaderInfo.dataset.companyId = company.id;
        ui.companySelectionContainer.classList.add('hidden');
        ui.appContainer.classList.remove('hidden');
        filters.region.value = 'Todas';
        populateLocales();
        navigateToPage('reportes-dt');
    }

    // ═══════════════════════════════════════════════════════
    //  NAVIGATION
    // ═══════════════════════════════════════════════════════
    function navigateToPage(targetId) {
        ui.navItems.forEach(i => i.classList.remove('bg-indigo-100', 'text-indigo-700', 'font-semibold'));
        const nav = document.querySelector(`.nav-item[data-target="${targetId}"]`);
        if (nav) nav.classList.add('bg-indigo-100', 'text-indigo-700', 'font-semibold');
        ui.pages.forEach(p => p.id === `page-${targetId}` ? p.classList.remove('hidden') : p.classList.add('hidden'));
        if (nav) ui.pageTitle.textContent = nav.querySelector('span').textContent;
    }

    function setupMainMenuNavigation() {
        ui.navItems.forEach(item => {
            item.addEventListener('click', (e) => {
                e.preventDefault();
                ui.navItems.forEach(i => i.classList.remove('bg-indigo-100', 'text-indigo-700', 'font-semibold'));
                item.classList.add('bg-indigo-100', 'text-indigo-700', 'font-semibold');
                const tid = item.dataset.target;
                ui.pages.forEach(p => p.id === `page-${tid}` ? p.classList.remove('hidden') : p.classList.add('hidden'));
                ui.pageTitle.textContent = item.querySelector('span').textContent;
                if (window.innerWidth < 768) ui.sidebar.classList.add('-translate-x-full');
            });
        });
    }

    function setupReportSubMenuNavigation() {
        ui.reportNavItems.forEach(item => {
            item.addEventListener('click', () => {
                ui.reportNavItems.forEach(i => {
                    i.classList.remove('bg-indigo-100', 'text-indigo-700', 'font-semibold');
                    i.classList.add('text-gray-600', 'hover:bg-gray-200', 'font-medium');
                });
                item.classList.add('bg-indigo-100', 'text-indigo-700', 'font-semibold');
                item.classList.remove('text-gray-600', 'hover:bg-gray-200', 'font-medium');
                const rid = item.dataset.report;
                ui.reportViews.forEach(v => v.id === `report-${rid}` ? v.classList.remove('hidden') : v.classList.add('hidden'));
                ui.reportTitleEl.textContent = item.textContent;
            });
        });
    }

    // ═══════════════════════════════════════════════════════
    //  EXPORT FUNCTIONS
    // ═══════════════════════════════════════════════════════
    function getActiveReportContent(containerId = null) {
        if (containerId) return document.getElementById(containerId);
        return document.getElementById(`report-${ui.reportNavContainer.querySelector('.bg-indigo-100').dataset.report}`);
    }

    function printReport(containerId = null) {
        const c = getActiveReportContent(containerId);
        if (!c) return;
        document.getElementById('print-area').innerHTML = c.innerHTML;
        window.print();
        document.getElementById('print-area').innerHTML = '';
    }

    function exportToPDF(containerId = null, title = null) {
        const c = getActiveReportContent(containerId);
        if (!c) return;
        const { jsPDF } = window.jspdf;
        const doc = new jsPDF({ orientation: 'l', unit: 'px', format: 'a4' });
        const t = title || ui.reportTitleEl.textContent;
        doc.setFontSize(18); doc.text(t, 20, 30);
        let y = 50;
        c.querySelectorAll('.report-table').forEach(table => {
            doc.autoTable({ html: table, startY: y, theme: 'grid', styles: { fontSize: 6, cellPadding: 2 }, headStyles: { fillColor: [243,244,246], textColor:[0,0,0], fontStyle:'bold' } });
            y = doc.lastAutoTable.finalY + 20;
        });
        doc.save(`${t}.pdf`);
    }

    function exportToExcel(containerId = null, title = null) {
        const c = getActiveReportContent(containerId);
        if (!c) return;
        const t  = title || ui.reportTitleEl.textContent;
        const wb = XLSX.utils.book_new();
        let data = [];
        c.querySelectorAll('.report-table').forEach(table => {
            data.push(Array.from(table.querySelectorAll('thead th')).map(th => th.innerText));
            Array.from(table.querySelectorAll('tbody tr')).forEach(tr => data.push(Array.from(tr.querySelectorAll('td')).map(td => td.innerText)));
            data.push([]);
        });
        XLSX.utils.book_append_sheet(wb, XLSX.utils.aoa_to_sheet(data), t.substring(0, 31));
        XLSX.writeFile(wb, `${t}.xlsx`);
    }

    function exportToWord(containerId = null, title = null) {
        const c = getActiveReportContent(containerId);
        if (!c) return;
        const t = title || ui.reportTitleEl.textContent;
        const blob = new Blob([`\ufeff<html xmlns:o='urn:schemas-microsoft-com:office:office' xmlns:w='urn:schemas-microsoft-com:office:word'><head><meta charset='utf-8'></head><body><h1>${t}</h1>${c.innerHTML}</body></html>`], { type: 'application/msword' });
        const a = Object.assign(document.createElement('a'), { href: URL.createObjectURL(blob), download: `${t}.doc` });
        document.body.appendChild(a); a.click(); document.body.removeChild(a);
    }

    // ═══════════════════════════════════════════════════════
    //  DOM CONTENT LOADED — ÚNICA entrada, CERO APIs
    // ═══════════════════════════════════════════════════════
    document.addEventListener('DOMContentLoaded', function () {
        ui = {
            loginContainer:            document.getElementById('login-container'),
            appContainer:              document.getElementById('app-container'),
            loginBtn:                  document.getElementById('login-btn'),
            loginError:                document.getElementById('login-error'),
            forgotPasswordLink:        document.getElementById('forgot-password-link'),
            requestPasswordLink:       document.getElementById('request-password-link'),
            modalContainer:            document.getElementById('modal-container'),
            modalTitle:                document.getElementById('modal-title'),
            modalContent:              document.getElementById('modal-content'),
            closeModalBtn:             document.getElementById('close-modal-btn'),
            menuButton:                document.getElementById('menu-button'),
            sidebar:                   document.getElementById('sidebar'),
            pageTitle:                 document.getElementById('page-title'),
            navItems:                  document.querySelectorAll('.nav-item'),
            pages:                     document.querySelectorAll('.page'),
            reportNavContainer:        document.getElementById('report-nav-container'),
            reportNavItems:            document.querySelectorAll('.report-nav-item'),
            reportViews:               document.querySelectorAll('.report-view'),
            reportTitleEl:             document.getElementById('report-title'),
            searchBtn:                 document.getElementById('search-btn'),
            clearFiltersBtn:           document.getElementById('clear-filters-btn'),
            employeeTableContainer:    document.getElementById('employee-table-container'),
            generateReportBtn:         document.getElementById('generate-report-btn'),
            reportActions:             document.getElementById('report-actions'),
            individualSearchContainer: document.getElementById('individual-search-container'),
            autocompleteResults:       document.getElementById('autocomplete-results'),
            validateHashBtn:           document.getElementById('validate-hash-btn'),
            hashInput:                 document.getElementById('hash-input'),
            hashResultsContainer:      document.getElementById('hash-results-container'),
            validateHashReportBtn:     document.getElementById('validate-hash-report-btn'),
            hashSearchResults:         document.getElementById('hash-search-results'),
            printBtn:                  document.getElementById('print-btn'),
            pdfBtn:                    document.getElementById('pdf-btn'),
            excelBtn:                  document.getElementById('excel-btn'),
            wordBtn:                   document.getElementById('word-btn'),
            printableReportArea:       document.getElementById('printable-report-area'),
            incidentStartDate:         document.getElementById('incident-start-date'),
            incidentEndDate:           document.getElementById('incident-end-date'),
            searchIncidentsBtn:        document.getElementById('search-incidents-btn'),
            incidentTableContainer:    document.getElementById('incident-table-container'),
            incidentPrintBtn:          document.getElementById('incident-print-btn'),
            incidentPdfBtn:            document.getElementById('incident-pdf-btn'),
            incidentExcelBtn:          document.getElementById('incident-excel-btn'),
            incidentWordBtn:           document.getElementById('incident-word-btn'),
            companyStatusFilter:       document.getElementById('company-status-filter'),
            companyTableContainer:     document.getElementById('company-table-container'),
            companyPrintBtn:           document.getElementById('company-print-btn'),
            companyPdfBtn:             document.getElementById('company-pdf-btn'),
            companyExcelBtn:           document.getElementById('company-excel-btn'),
            companyWordBtn:            document.getElementById('company-word-btn'),
            companySelectionContainer: document.getElementById('company-selection-container'),
            companySearch:             document.getElementById('company-search'),
            companyList:               document.getElementById('company-list'),
            companyHeaderInfo:         document.getElementById('company-header-info'),
            logoutBtn:                 document.getElementById('logout-btn'),
            // Tabs de selección de empresa
            tabSearchBtn:              document.getElementById('tab-search-btn'),
            tabListBtn:                document.getElementById('tab-list-btn'),
            tabSearchPanel:            document.getElementById('tab-search-panel'),
            tabListPanel:              document.getElementById('tab-list-panel'),
            companyDirectSearch:       document.getElementById('company-direct-search'),
            companyDirectBtn:          document.getElementById('company-direct-btn'),
            companyDirectResults:      document.getElementById('company-direct-results'),
            companyDirectError:        document.getElementById('company-direct-error'),
            companyDirectErrorMsg:     document.getElementById('company-direct-error-msg'),
        };
        filters = {
            startDate:           document.getElementById('start-date'),
            endDate:             document.getElementById('end-date'),
            predeterminedPeriod: document.getElementById('predetermined-period'),
            individualSearch:    document.getElementById('individual-search'),
            hashSearch:          document.getElementById('hash-search'),
            jornada:             document.getElementById('jornada'),
            turnos:              document.getElementById('turnos'),
            region:              document.getElementById('region'),
            local:               document.getElementById('local'),
            cargo:               document.getElementById('cargo'),
            est:                 document.getElementById('est'),
        };
        // Estas 3 lineas son TODO lo que hace DOMContentLoaded
        showLogin();
        lucide.createIcons();
        setupEventListeners();
    }); // <- unico cierre de DOMContentLoaded
