<?php
/**
 * form_cliente.php — Página pública para que el cliente llene / firme el formulario
 * NO REQUIERE AUTENTICACIÓN. Acceso via token: ?t=TOKEN
 */
$token = trim($_GET['t'] ?? '');
// Si el token es un UUID (viene del turnero vía plantilla WA), redirigir al flujo de consentimiento
if (preg_match('/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i', $token)) {
    header('Location: ver_formulario_enviado.php?token=' . urlencode($token));
    exit;
}
?>
<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <title>Formulario</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
    <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
    <style>
        /* ── Base ──────────────────────────────────────── */
        body { background:#f0f4ff; min-height:100vh; font-size:16px; }
        .form-wrap { max-width:540px; margin:0 auto; padding:1rem; }

        /* ── Header ────────────────────────────────────── */
        .form-header { background:linear-gradient(135deg,#1565c0,#0288d1);
                       border-radius:12px 12px 0 0; padding:24px 20px; }

        /* ── Card ───────────────────────────────────────── */
        .form-card { background:#fff; border-radius:0 0 12px 12px;
                     padding:20px; box-shadow:0 4px 20px rgba(0,0,0,.12); }

        /* ── Linked field (read-only) ───────────────────── */
        .field-linked { background:#eef3ff; border-color:#93b4f7; }
        /* ── Inline linked field dentro de párrafo ──────── */
        .inline-linked {
            display:inline-block;
            border:none;
            border-bottom:1.5px solid #555;
            background:transparent;
            min-width:80px;
            font-size:inherit;
            line-height:inherit;
            color:#1a56db;
            font-weight:600;
            padding:0 3px;
            vertical-align:baseline;
            outline:none;
        }
        .inline-linked:empty { min-width:80px; }
        .inline-linked[data-editable] { border-bottom-color:#1a56db; cursor:text; }
        /* ── Firma canvas ───────────────────────────────── */
        #firma-canvas { border:2px solid #1565c0; border-radius:8px;
                        cursor:crosshair; touch-action:none;
                        background:#fff; display:block; width:100%; height:150px; }
        #firma-canvas.empty { border-style:dashed; border-color:#adb5bd; }
        /* ── Firma inline por campo ──────────────────────────────── */
        .fw-canvas { cursor:crosshair; touch-action:none;
                     background:#fff; display:block; width:100%; height:140px;
                     border-radius:8px; }
        .fw-canvas.empty { border-style:dashed !important; }
        /* ── Firma modos ─────────────────────────────── */
        .firma-modo-btns { display:flex; gap:8px; margin-bottom:12px; }
        .firma-modo-btn  { flex:1; padding:7px 0; border:2px solid #dee2e6; border-radius:8px;
                           background:#f8f9fa; cursor:pointer; font-size:.85rem; font-weight:600;
                           color:#6c757d; transition:all .2s; text-align:center; }
        .firma-modo-btn.activo { border-color:#1565c0; background:#e8f0fe; color:#1565c0; }
        .firma-foto-drop { border:2px dashed #adb5bd; border-radius:8px; padding:28px 16px;
                           text-align:center; color:#6c757d; cursor:pointer;
                           transition:border-color .2s; background:#fff; display:block; }
        .firma-foto-drop:hover, .firma-foto-drop.dragover { border-color:#1565c0; color:#1565c0; }
        #firma-foto-preview { max-width:100%; max-height:160px; border-radius:8px;
                              border:2px solid #1565c0; display:none; margin-top:8px; object-fit:contain; }

        /* ── Estado chips ────────────────────────────────── */
        .chip-estado { display:inline-flex; align-items:center; gap:6px;
                       padding:6px 14px; border-radius:100px; font-size:.85rem;
                       font-weight:600; }

        /* ── Success overlay ─────────────────────────────── */
        .check-circle { width:80px; height:80px; border-radius:50%;
                        background:#e8f5e9; display:flex; align-items:center;
                        justify-content:center; margin:0 auto; }
        @keyframes pop { 0%{transform:scale(0)} 80%{transform:scale(1.15)} 100%{transform:scale(1)} }
        .check-circle svg { animation:pop .4s ease both; }

        /* ── Error screen ─────────────────────────────────── */

        /* ── Loading screen ──────────────────────────────── */
        #loading-screen { min-height:50vh; display:flex; flex-direction:column;
                          align-items:center; justify-content:center; gap:12px; }

        /* ── Topaz SigWeb overlay ─────────────────────────── */
        #topaz-overlay { display:none; position:fixed; inset:0; z-index:9999;
                         background:rgba(0,0,0,.55); align-items:center;
                         justify-content:center; padding:16px; }
        .topaz-modal { background:#fff; border-radius:16px; width:100%;
                       max-width:400px; box-shadow:0 12px 40px rgba(0,0,0,.3); overflow:hidden; }
        .topaz-modal-hdr { background:linear-gradient(135deg,#1565c0,#0288d1);
                           color:#fff; padding:14px 18px; font-weight:700; font-size:.95rem;
                           display:flex; align-items:center; gap:8px; }
        .topaz-modal-body { padding:20px 18px; }
        .topaz-pad-area { border:2px dashed #adb5bd; border-radius:10px; padding:24px 16px;
                          text-align:center; background:#f8fafc; min-height:100px;
                          display:flex; flex-direction:column; align-items:center;
                          justify-content:center; gap:6px; transition:border-color .2s; }
        .topaz-pad-area.has-sig { border-color:#198754; border-style:solid; background:#f0fff4; }
        .topaz-pts-badge { font-size:.8rem; color:#64748b; }
        .topaz-modal-footer { display:flex; gap:8px; justify-content:flex-end;
                              padding:12px 18px; border-top:1px solid #f1f5f9; flex-wrap:wrap; }
        .btn-topaz { font-size:.83rem; padding:6px 13px; border-radius:8px; border:1.5px solid #0288d1;
                     background:#fff; color:#0288d1; cursor:pointer; font-weight:600;
                     display:inline-flex; align-items:center; gap:5px; transition:all .15s; }
        .btn-topaz:hover { background:#e0f2fe; }
    </style>
</head>
<body>
<div class="form-wrap">

    <!-- ── LOADING ──────────────────────────────────────── -->
    <div id="loading-screen">
        <div class="spinner-border text-primary" style="width:3rem;height:3rem;"></div>
        <p class="text-muted">Cargando formulario…</p>
    </div>

    <!-- ── ERROR ─────────────────────────────────────────── -->
    <div id="error-screen" style="display:none">
        <div class="text-center py-5">
            <div style="font-size:4rem">😔</div>
            <h5 id="err-title" class="mt-3 fw-bold text-danger">Formulario no disponible</h5>
            <p id="err-msg" class="text-muted"></p>
        </div>
    </div>

    <!-- ── YA FIRMADO (bloqueado) ───────────────────── -->
    <div id="ya-firmado-screen" style="display:none">
        <div class="form-header text-white" style="border-radius:12px 12px 0 0">
            <!-- Encabezado empresa -->
            <div id="yf-empresa-header" style="display:none;border-bottom:1px solid rgba(255,255,255,.3);margin-bottom:10px;padding-bottom:10px" class="d-flex align-items-center gap-2">
                <img id="yf-empresa-logo" src="" alt="" style="max-height:40px;border-radius:5px;background:rgba(255,255,255,.2);padding:3px;display:none">
                <div>
                    <div id="yf-empresa-nombre" class="fw-bold" style="font-size:.9rem"></div>
                    <div id="yf-empresa-subtitulo" class="opacity-75" style="font-size:.75rem"></div>
                </div>
            </div>
            <h5 class="fw-bold mb-0 text-center">
                <i class="fas fa-lock me-2"></i>Formulario ya completado
            </h5>
        </div>
        <div class="form-card text-center">
            <div class="signed-icon mx-auto mb-3">
                <i class="fas fa-file-signature" style="font-size:2.5rem;color:#1565c0"></i>
            </div>
            <h5 class="fw-bold mb-1" id="yf-titulo">Formulario</h5>
            <p class="text-muted small mb-3" id="yf-paciente"></p>
            <span id="yf-estado-badge" class="badge mb-3" style="font-size:.85rem"></span>

            <div id="yf-firma-thumb" style="display:none" class="mb-3">
                <p class="small text-muted fw-semibold mb-1">Firma registrada:</p>
                <img id="yf-firma-img" src="" alt="Firma"
                     class="border rounded p-2" style="max-height:120px;max-width:100%">
            </div>

            <a id="yf-pdf-btn" href="#" target="_blank"
               class="btn btn-danger w-100 mb-2 fw-semibold">
                <i class="fas fa-file-pdf me-2"></i>Ver documento firmado (PDF)
            </a>

            <div id="yf-hash-box" style="display:none" class="mt-3 text-start">
                <p class="small text-muted mb-1"><i class="fas fa-shield-alt me-1"></i>Sello de integridad SHA-256:</p>
                <code id="yf-hash" class="d-block" style="font-size:10px;word-break:break-all;
                     background:#f0f5ff;border:1px solid #c3d3f7;border-radius:4px;padding:6px 8px;
                     color:#1e3a6e">—</code>
            </div>

            <div class="alert alert-warning border mt-3 text-start small">
                <i class="fas fa-lock me-1"></i>
                Este formulario ya fue firmado y <strong>no puede editarse</strong>.
                Una copia del documento fue registrada con sello digital.
            </div>
        </div>
    </div>

    <!-- ── FORMULARIO ────────────────────────────────────── -->
    <div id="form-screen" style="display:none">
        <div class="form-header text-white mb-0" id="form-main-header">
            <!-- Encabezado empresa (se muestra con config) -->
            <div id="f-empresa-header" style="display:none;border-bottom:1px solid rgba(255,255,255,.3);margin-bottom:10px;padding-bottom:10px" class="d-flex align-items-center gap-2">
                <img id="f-empresa-logo" src="" alt="" style="max-height:48px;border-radius:5px;background:rgba(255,255,255,.2);padding:3px;display:none">
                <div>
                    <div id="f-empresa-nombre" class="fw-bold" style="font-size:.95rem"></div>
                    <div id="f-empresa-subtitulo" class="opacity-75" style="font-size:.78rem"></div>
                </div>
            </div>
            <div class="d-flex align-items-start justify-content-between">
                <div>
                    <h5 class="fw-bold mb-1" id="f-titulo">Formulario</h5>
                    <p class="mb-0 opacity-75 small" id="f-desc"></p>
                </div>
                <div id="f-badge"></div>
            </div>
            <div class="mt-3 pt-2 border-top border-white border-opacity-25 small opacity-75" id="f-paciente-info"></div>
        </div>

        <div class="form-card">
            <!-- Nota para el paciente -->
            <div class="alert alert-info py-2 small mb-3" id="instruccion-nota">
                <i class="fas fa-info-circle me-1"></i>
                Por favor, completa los campos y firma al final.
            </div>

            <!-- Campos del formulario -->
            <form id="form-main" novalidate>
                <div id="campos-container"></div>

                <!-- ── Firma ─────────────────────────────── -->
                <div id="firma-container" style="display:none" class="mt-4">
                    <hr>
                    <label class="form-label fw-semibold">
                        <i class="fas fa-signature me-1 text-primary"></i>
                        Firma digital
                        <span id="firma-req-badge" class="badge bg-danger ms-1 small" style="display:none">Requerida</span>
                    </label>

                    <!-- área canvas (dibujo) -->
                    <div id="firma-canvas-area">
                        <p class="text-muted small mb-2">✍️ Dibuja tu firma con el dedo o el mouse.</p>
                        <canvas id="firma-canvas" class="empty"></canvas>
                        <div class="d-flex gap-2 mt-2 flex-wrap">
                            <button type="button" class="btn btn-outline-secondary btn-sm"
                                    onclick="firma.limpiarCanvas()">
                                <i class="fas fa-eraser me-1"></i>Limpiar firma
                            </button>
                            <button type="button" class="btn-topaz" onclick="topaz.activar('__global')"
                                    title="Usar pad biométrico Topaz">
                                <i class="fas fa-tablet-alt"></i>Tableta
                            </button>
                            <span id="firma-status" class="small text-muted align-self-center">Sin firma</span>
                        </div>
                    </div>

                    <!-- área foto -->
                    <div id="firma-foto-area" class="mt-3">
                        <p class="text-muted small mb-2">📷 Adjunta una foto de tu firma o documento.</p>
                        <label class="firma-foto-drop" id="firma-foto-drop" for="firma-foto-input">
                            <i class="fas fa-camera fa-2x d-block mb-2"></i>
                            Toca para abrir cámara o galería
                        </label>
                        <input type="file" id="firma-foto-input" accept="image/*" capture="environment"
                               class="d-none" onchange="firma._onFotoChange(this)">
                        <img id="firma-foto-preview" src="" alt="Vista previa de firma">
                        <div class="d-flex gap-2 mt-2">
                            <button type="button" class="btn btn-outline-secondary btn-sm"
                                    onclick="firma.limpiarFoto()">
                                <i class="fas fa-eraser me-1"></i>Quitar foto
                            </button>
                            <span id="firma-foto-status" class="small text-muted align-self-center">Sin foto</span>
                        </div>
                    </div>
                </div>

                <!-- ── Submit ─────────────────────────────── -->
                <div class="d-grid mt-4">
                    <button type="button" class="btn btn-primary btn-lg fw-bold"
                            id="btn-enviar" onclick="formCliente.enviar()">
                        <i class="fas fa-check-circle me-2"></i>Enviar formulario
                    </button>
                </div>
                <p class="text-muted text-center small mt-2">
                    <i class="fas fa-lock me-1"></i>Tus datos están protegidos
                </p>
            </form>
        </div>
    </div>

    <!-- ── ÉXITO ─────────────────────────────────────────── -->
    <div id="success-screen" style="display:none">
        <div class="form-header text-white" style="border-radius:12px 12px 0 0">
            <h5 class="fw-bold mb-0 text-center"><i class="fas fa-check-circle me-2"></i>¡Formulario enviado!</h5>
        </div>
        <div class="form-card text-center">
            <div class="check-circle mt-2 mb-3">
                <svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 52 52">
                    <circle cx="26" cy="26" r="25" fill="none" stroke="#4caf50" stroke-width="2"/>
                    <path fill="none" stroke="#4caf50" stroke-width="4" stroke-linecap="round"
                          stroke-linejoin="round" d="M14 27 l7 7 l17-17"/>
                </svg>
            </div>
            <h5 class="fw-bold">¡Gracias! 🎉</h5>
            <p class="text-muted" id="success-msg">Tu respuesta ha sido registrada correctamente.</p>
            <div id="success-firma-thumb" class="mt-3" style="display:none">
                <p class="small text-muted fw-semibold">Firma registrada:</p>
                <img id="success-firma-img" src="" alt="Firma"
                     class="border rounded p-2" style="max-height:120px;max-width:100%">
            </div>

            <!-- Link PDF -->
            <div id="success-pdf-row" style="display:none" class="mt-3">
                <a id="success-pdf-btn" href="#" target="_blank"
                   class="btn btn-danger w-100 fw-semibold">
                    <i class="fas fa-file-pdf me-2"></i>Descargar documento firmado (PDF)
                </a>
            </div>

            <!-- Hash SHA-256 -->
            <div id="success-hash-box" style="display:none" class="mt-3 text-start">
                <p class="small text-muted mb-1 fw-semibold">
                    <i class="fas fa-shield-alt me-1 text-success"></i>Sello de integridad del documento:
                </p>
                <code id="success-hash" class="d-block" style="font-size:10px;word-break:break-all;
                     background:#f0f5ff;border:1px solid #c3d3f7;border-radius:4px;padding:6px 8px;
                     color:#1e3a6e"></code>
            </div>

            <div class="alert alert-light border mt-4 text-start small">
                <i class="fas fa-clock me-1 text-muted"></i>
                Tu información ha sido recibida y está en manos del equipo médico.
                Puedes cerrar esta página.
            </div>
        </div>
    </div>

<!-- ── Topaz SigWeb overlay ───────────────────────────────────────── -->
<div id="topaz-overlay">
    <div class="topaz-modal">
        <div class="topaz-modal-hdr">
            <i class="fas fa-tablet-alt"></i> Pad biométrico Topaz
        </div>
        <div class="topaz-modal-body">
            <div class="topaz-pad-area" id="topaz-pad-area">
                <canvas id="topaz-canvas" width="500" height="150"
                    style="border:1px solid #e2e8f0;border-radius:6px;background:#fff;max-width:100%;display:block;margin:0 auto"></canvas>
                <div id="topaz-status-msg" style="font-size:.9rem;color:#64748b;font-weight:600;margin-top:8px">
                    Firme en el pad biométrico
                </div>
                <div class="topaz-pts-badge">Trazos: <span id="topaz-pts">0</span></div>
            </div>
        </div>
        <div class="topaz-modal-footer">
            <button class="btn btn-outline-secondary btn-sm" onclick="topaz.cancelar()">
                <i class="fas fa-times me-1"></i>Cancelar
            </button>
            <button class="btn btn-outline-secondary btn-sm" onclick="topaz.limpiarPad()">
                <i class="fas fa-eraser me-1"></i>Limpiar
            </button>
            <button class="btn btn-success btn-sm fw-semibold" id="topaz-btn-aceptar"
                    onclick="topaz.aceptar()" disabled>
                <i class="fas fa-check me-1"></i>Aceptar firma
            </button>
        </div>
    </div>
</div>

</div>

<script>
// ══════════════════════════════════════════════════════════════════════
// CONFIG
// ══════════════════════════════════════════════════════════════════════
const TOKEN = <?= json_encode($token) ?>;
const API   = 'api/lab/submit_formulario.php';

let _data       = null;  // respuesta de verificar token
let _cfg        = {};    // config de diseño del laboratorio
let _firmaDibujada = false;
let _firmaModosGlobal  = null;  // modos del topbar (columna firma_modos en DB)
let _hasInlineFirma    = false; // true si el esquema tiene campos tipo firma/firma_profesional

// ══════════════════════════════════════════════════════════════════════
// HELPERS
// ══════════════════════════════════════════════════════════════════════
const $ = id => document.getElementById(id);
function esc(s) {
    return String(s||'').replace(/[<>&"']/g, c=>
        ({'<':'&lt;','>':'&gt;','&':'&amp;','"':'&quot;',"'":'&#39;'}[c])
    );
}
function show(id) { $(id).style.display = ''; }
function hide(id) { $(id).style.display = 'none'; }
function showBlock(id) { $(id).style.display = 'block'; }

// ══════════════════════════════════════════════════════════════════════
// FIRMA DIGITAL
// ══════════════════════════════════════════════════════════════════════
const firma = (() => {
    let canvas, ctx, drawing = false, lastX = 0, lastY = 0;

    function init() {
        canvas = $('firma-canvas');
        ctx    = canvas.getContext('2d');
        // Escalar para retina
        const ratio = window.devicePixelRatio || 1;
        canvas.width  = canvas.offsetWidth  * ratio;
        canvas.height = canvas.offsetHeight * ratio;
        ctx.scale(ratio, ratio);
        ctx.strokeStyle = '#1a1a2e';
        ctx.lineWidth   = 2.5;
        ctx.lineCap     = 'round';
        ctx.lineJoin    = 'round';

        const getPos = e => {
            const rect = canvas.getBoundingClientRect();
            if (e.touches) {
                const scX = canvas.offsetWidth  / rect.width;
                const scY = canvas.offsetHeight / rect.height;
                return {
                    x: (e.touches[0].clientX - rect.left) * scX,
                    y: (e.touches[0].clientY - rect.top)  * scY,
                };
            }
            return { x: e.offsetX, y: e.offsetY };
        };

        // Mouse
        canvas.addEventListener('mousedown',  e => { drawing = true; const p=getPos(e); lastX=p.x; lastY=p.y; });
        canvas.addEventListener('mousemove',  e => { if (!drawing) return; trazo(getPos(e)); });
        canvas.addEventListener('mouseup',    ()=> { drawing = false; ctx.beginPath(); });
        canvas.addEventListener('mouseleave', ()=> { drawing = false; ctx.beginPath(); });

        // Touch
        canvas.addEventListener('touchstart', e => { e.preventDefault(); drawing=true; const p=getPos(e); lastX=p.x; lastY=p.y; }, {passive:false});
        canvas.addEventListener('touchmove',  e => { e.preventDefault(); if (!drawing) return; trazo(getPos(e)); }, {passive:false});
        canvas.addEventListener('touchend',   ()=> { drawing=false; ctx.beginPath(); });

        // Drag & drop en área de foto global
        const fotoDrop = $('firma-foto-drop');
        if (fotoDrop) {
            fotoDrop.addEventListener('dragover',  e => { e.preventDefault(); fotoDrop.classList.add('dragover'); });
            fotoDrop.addEventListener('dragleave', ()  => fotoDrop.classList.remove('dragover'));
            fotoDrop.addEventListener('drop',      e => {
                e.preventDefault();
                fotoDrop.classList.remove('dragover');
                const file = e.dataTransfer.files?.[0];
                if (!file || !file.type.startsWith('image/')) return;
                _onFotoChange({ files: [file] });
            });
        }
    }

    function trazo(p) {
        ctx.beginPath();
        ctx.moveTo(lastX, lastY);
        ctx.lineTo(p.x, p.y);
        ctx.stroke();
        lastX = p.x; lastY = p.y;
        _firmaDibujada = true;
        canvas.classList.remove('empty');
        $('firma-status').textContent = '✅ Firma lista';
        $('firma-status').className = 'small text-success align-self-center fw-semibold';
    }

    let _fotoDato = null;

    // setModos: controla qué secciones se muestran
    // modos puede ser un array ['canvas','foto'] o null
    function setModos(modos) {
        const m = Array.isArray(modos) && modos.length ? modos : ['canvas'];
        $('firma-canvas-area').style.display = m.includes('canvas') ? '' : 'none';
        $('firma-foto-area').style.display   = m.includes('foto')   ? '' : 'none';
    }

    function _onFotoChange(input) {
        const file = input.files && input.files[0];
        if (!file) return;
        const reader = new FileReader();
        reader.onload = e => {
            _fotoDato = e.target.result;
            const preview = $('firma-foto-preview');
            preview.src = _fotoDato;
            preview.style.display = 'block';
            $('firma-foto-drop').style.display = 'none';
            $('firma-foto-status').textContent = '✅ Foto lista';
            $('firma-foto-status').className   = 'small text-success align-self-center fw-semibold';
        };
        reader.readAsDataURL(file);
    }

    function limpiarCanvas() {
        ctx.clearRect(0, 0, canvas.offsetWidth, canvas.offsetHeight);
        _firmaDibujada = false;
        canvas.classList.add('empty');
        $('firma-status').textContent = 'Sin firma';
        $('firma-status').className = 'small text-muted align-self-center';
    }

    function limpiarFoto() {
        _fotoDato = null;
        $('firma-foto-preview').src = '';
        $('firma-foto-preview').style.display = 'none';
        $('firma-foto-drop').style.display = 'block';
        $('firma-foto-status').textContent = 'Sin foto';
        $('firma-foto-status').className   = 'small text-muted align-self-center';
        $('firma-foto-input').value = '';
    }

    function obtenerSVG()  { if (!_firmaDibujada) return null; return canvas.toDataURL('image/png'); }
    function obtenerFoto() { return _fotoDato || null; }

    return { init, limpiarCanvas, limpiarFoto, obtenerSVG, obtenerFoto, setModos, _onFotoChange };
})();

// ══════════════════════════════════════════════════════════════════════
// RENDER CAMPOS
// ══════════════════════════════════════════════════════════════════════
function renderCampos(esquema, prefilled) {
    const cont = $('campos-container');
    let html = '';
    const pad = prefilled || {};

    esquema.forEach(c => {
        if (c.tipo === 'separador') {
            html += `<div class="mt-4 mb-2" data-campo-id="${esc(c.id)}">
                <p class="fw-bold text-secondary small text-uppercase mb-0">${esc(c.label)}</p>
                <hr class="mt-1 mb-3">
            </div>`;
            return;
        }
        // Wrapper con data-campo-id para lógica de condiciones
        const wrapOpen  = `<div data-campo-id="${esc(c.id)}">`;
        const wrapClose = `</div>`;

        if (c.tipo === 'firma' || c.tipo === 'firma_profesional') {
            const modos = (c.tipo === 'firma' && _firmaModosGlobal)
                ? _firmaModosGlobal : (c.modos || ['canvas']);
            html += wrapOpen + renderFirmaInline(c.id, modos, c.label, c.required, c.tipo === 'firma_profesional') + wrapClose;
            return;
        }
        if (c.tipo === 'parrafo') {
            const ws = c.flujoLibre ? 'normal' : 'pre-wrap';
            html += wrapOpen + `<div class="mb-3" style="font-size:.88rem;line-height:1.75;color:#222;text-align:justify;white-space:${ws}">${esc(c.contenido||'')}</div>` + wrapClose;
            return;
        }
        if (c.tipo === 'parrafo_inline') {
            const renderLine = line => line.split(/(\{[a-z_]+\})/g).map((p, i) => {
                if (i % 2 === 1) {
                    const key = p.slice(1,-1);
                    const val = pad['__paciente']?.[key] || pad[key] || '';
                    const hasVal = val.trim() !== '';
                    const w = Math.max(80, val.length * 9 + 24);
                    if (hasVal) {
                        return `<input type="text" class="inline-linked" readonly name="${c.id}_${key}" value="${esc(val)}" style="min-width:${w}px">`;
                    } else {
                        return `<input type="text" class="inline-linked" data-editable name="${c.id}_${key}" placeholder="${esc(key.replace(/_/g,' '))}" style="min-width:${w}px">`;
                    }
                }
                return esc(p);
            }).join('');
            const inlineHtml = (c.contenido||'').split('\n').map(renderLine).join('<br>');
            html += wrapOpen + `<div class="mb-3" style="font-size:.88rem;line-height:2.4;color:#222;text-align:justify">${inlineHtml}</div>` + wrapClose;
            return;
        }
        if (c.tipo === 'lista_marcable') {
            const star = c.required ? '<span class="text-danger ms-1">*</span>' : '';
            const lbl  = `<label class="form-label small fw-semibold mb-1">${esc(c.label)}${star}</label>`;
            const opts = (c.items||[]).map((it, i) => `
                <div class="form-check">
                    <input class="form-check-input" type="checkbox" name="${c.id}" value="${esc(it)}">
                    <label class="form-check-label">${i+1}. ${esc(it)}</label>
                </div>`).join('');
            html += wrapOpen + `<div class="mb-3">${lbl}${opts}</div>` + wrapClose;
            return;
        }

        const req  = c.required ? 'required' : '';
        const star = c.required ? '<span class="text-danger ms-1">*</span>' : '';

        let val = '';
        if (c.tipo === 'linked') {
            val = pad['__paciente']?.[c.linked_key] || pad[c.linked_key] || '';
        } else {
            val = pad[c.id] || '';
        }

        const isLinked   = c.tipo === 'linked';
        const isReadOnly = isLinked && !!val;
        const linkedNote = isLinked && val
            ? '<small class="text-info d-block mt-1"><i class="fas fa-link me-1"></i>Campo autocompletado</small>'
            : (isLinked ? '<small class="text-warning d-block mt-1"><i class="fas fa-edit me-1"></i>Por favor completa este campo</small>' : '');

        const lbl = `<label class="form-label small fw-semibold mb-1">${esc(c.label)}${star}</label>`;

        if (c.tipo === 'textarea') {
            html += wrapOpen + `<div class="mb-3">${lbl}
                <textarea class="form-control ${isReadOnly?'field-linked':''}"
                          name="${c.id}" rows="3" ${req} ${isReadOnly?'readonly':''}
                          placeholder="${esc(c.placeholder||'')}">${esc(val)}</textarea>${linkedNote}
            </div>` + wrapClose;
        } else if (c.tipo === 'select') {
            const opts = (c.options||[]).map(o =>
                `<option value="${esc(o)}" ${o===val?'selected':''}>${esc(o)}</option>`
            ).join('');
            html += wrapOpen + `<div class="mb-3">${lbl}
                <select class="form-select ${isReadOnly?'field-linked':''}"
                        name="${c.id}" ${req} ${isReadOnly?'disabled':''}>
                    <option value="">— seleccionar —</option>${opts}
                </select>${linkedNote}
            </div>` + wrapClose;
        } else if (c.tipo === 'radio') {
            const opts = (c.options||[]).map(o => `
                <div class="form-check form-check-inline me-3">
                    <input class="form-check-input" type="radio" name="${c.id}" value="${esc(o)}"
                           ${o===val?'checked':''} ${isReadOnly?'disabled':''}>
                    <label class="form-check-label">${esc(o)}</label>
                </div>`).join('');
            html += wrapOpen + `<div class="mb-3">${lbl}<div class="d-flex flex-wrap gap-1">${opts}</div>${linkedNote}</div>` + wrapClose;
        } else if (c.tipo === 'checkbox') {
            const vals = Array.isArray(val) ? val : [];
            const opts = (c.options||[]).map(o => `
                <div class="form-check">
                    <input class="form-check-input" type="checkbox" name="${c.id}" value="${esc(o)}"
                           ${vals.includes(o)?'checked':''} ${isReadOnly?'disabled':''}>
                    <label class="form-check-label">${esc(o)}</label>
                </div>`).join('');
            html += wrapOpen + `<div class="mb-3">${lbl}${opts}${linkedNote}</div>` + wrapClose;
        } else if (c.tipo === 'fecha_hoy') {
            let calcVal = pad[c.id] || '';
            if (!calcVal) {
                const now = new Date();
                calcVal = now.getFullYear() + '-'
                    + String(now.getMonth()+1).padStart(2,'0') + '-'
                    + String(now.getDate()).padStart(2,'0');
            }
            html += wrapOpen + `<div class="mb-3">${lbl}
                <input type="date" class="form-control" style="background:#fff9e6;border-color:#ffc107"
                       name="${c.id}" value="${esc(calcVal)}" ${req}>
            </div>` + wrapClose;
        } else if (c.tipo === 'edad') {
            let calcVal = pad[c.id] || '';
            if (!calcVal) {
                const fnac = pad['__paciente']?.fecha_nacimiento || pad.fecha_nacimiento || '';
                if (fnac) {
                    const hoy  = new Date();
                    const bday = new Date(fnac);
                    let edad = hoy.getFullYear() - bday.getFullYear();
                    const m = hoy.getMonth() - bday.getMonth();
                    if (m < 0 || (m === 0 && hoy.getDate() < bday.getDate())) edad--;
                    calcVal = edad >= 0 ? String(edad) : '';
                }
            }
            html += wrapOpen + `<div class="mb-3">${lbl}
                <input type="number" min="0" max="120" class="form-control" style="background:#fff9e6;border-color:#ffc107"
                       name="${c.id}" value="${esc(calcVal)}" ${req} placeholder="Edad en años">
            </div>` + wrapClose;
        } else {
            const t = c.tipo === 'numero' ? 'number'
                    : (c.tipo === 'fecha' || c.linked_key === 'fecha_nacimiento') ? 'date'
                    : c.tipo === 'hora'   ? 'time' : 'text';
            html += wrapOpen + `<div class="mb-3">${lbl}
                <input type="${t}" class="form-control ${isReadOnly?'field-linked':''}"
                       name="${c.id}" value="${esc(val)}" ${req} ${isReadOnly?'readonly':''}
                       placeholder="${esc(c.placeholder||'')}">
                ${linkedNote}
            </div>` + wrapClose;
        }
    });

    cont.innerHTML = html;
}

// ══════════════════════════════════════════════════════════════════════
// FIRMAS INLINE (por campo)
// ══════════════════════════════════════════════════════════════════════
const _fw = {};  // { fieldId: { canvas, ctx, hasFirma, foto, drawing, lastX, lastY } }

function renderFirmaInline(fid, modos, label, required, isPro) {
    const hasCanvas = (modos||[]).includes('canvas');
    const hasFoto   = (modos||[]).includes('foto');
    const hasBoth   = hasCanvas && hasFoto;
    const borderC   = isPro ? '#198754' : '#1565c0';
    const bgC       = isPro ? '#f0fff4' : '#fff';
    const iconC     = isPro ? 'fa-user-md' : 'fa-signature';
    const badge     = isPro
        ? `<span class="badge ms-1" style="background:#198754;font-size:.65rem">Profesional</span>`
        : `<span class="badge ms-1" style="background:#1565c0;font-size:.65rem">Paciente</span>`;
    const star = required ? '<span class="text-danger ms-1">*</span>' : '';
    const lbl  = `<label class="form-label fw-semibold">
        <i class="fas ${iconC} me-1" style="color:${borderC}"></i>${esc(label||'')}${star}${badge}
    </label>`;

    // Firma profesional: el paciente NO puede firmar — se muestra solo como aviso
    if (isPro) {
        return `<div class="mb-4" id="fw-${fid}"><hr class="mb-3">${lbl}
            <div class="alert py-2 px-3" style="background:#f0fff4;border:2px dashed #198754;border-radius:8px;color:#155724;font-size:.85rem">
                <i class="fas fa-lock me-2"></i>
                <strong>Uso exclusivo del profesional.</strong>
                Esta firma será completada por el profesional de salud encargado.
            </div>
        </div>`;
    }

    let html = `<div class="mb-4" id="fw-${fid}"><hr class="mb-3">${lbl}`;

    // Tabs cuando ambos modos están activos
    if (hasBoth) {
        html += `<div class="firma-modo-btns mb-2">
            <button type="button" class="firma-modo-btn activo" id="fw-${fid}-tab-canvas"
                    onclick="firmaWidgetTab('${fid}','canvas')">✍️ Dibujar</button>
            <button type="button" class="firma-modo-btn" id="fw-${fid}-tab-foto"
                    onclick="firmaWidgetTab('${fid}','foto')">📷 Foto</button>
        </div>`;
    }

    if (hasCanvas) {
        html += `<div id="fw-${fid}-canvas-area">
            <p class="text-muted small mb-2">✍️ Dibuja la firma con el dedo o el mouse.</p>
            <canvas id="fw-${fid}-canvas" class="fw-canvas empty"
                    style="border:2px dashed ${borderC};background:${bgC};"></canvas>
            <div class="d-flex gap-2 mt-2 flex-wrap">
                <button type="button" class="btn btn-outline-secondary btn-sm"
                        onclick="firmaWidgetLimpiar('${fid}')">
                    <i class="fas fa-eraser me-1"></i>Limpiar
                </button>
                <button type="button" class="btn-topaz" onclick="topaz.activar('${fid}')"
                        title="Usar pad biométrico Topaz">
                    <i class="fas fa-tablet-alt"></i>Tableta
                </button>
                <span id="fw-${fid}-status" class="small text-muted align-self-center">Sin firma</span>
            </div>
        </div>`;
    }

    if (hasFoto) {
        html += `<div id="fw-${fid}-foto-area"${hasBoth ? ' style="display:none"' : (hasCanvas ? ' class="mt-3"' : '')}>
            <p class="text-muted small mb-2">📷 Adjunta una foto de la firma o arrastra la imagen aquí.</p>
            <label class="firma-foto-drop" id="fw-${fid}-foto-drop" for="fw-${fid}-foto-input">
                <i class="fas fa-camera fa-2x d-block mb-2"></i>
                Toca para abrir cámara / galería, o arrastra aquí
            </label>
            <input type="file" id="fw-${fid}-foto-input" accept="image/*" capture="environment"
                   class="d-none" onchange="firmaWidgetFoto('${fid}', this)">
            <img id="fw-${fid}-foto-preview" src="" alt="Firma"
                 style="max-width:100%;max-height:160px;border-radius:8px;border:2px solid ${borderC};display:none;margin-top:8px;object-fit:contain;">
            <div class="d-flex gap-2 mt-2">
                <button type="button" class="btn btn-outline-secondary btn-sm"
                        onclick="firmaWidgetLimpiarFoto('${fid}')">
                    <i class="fas fa-eraser me-1"></i>Quitar foto
                </button>
                <span id="fw-${fid}-foto-status" class="small text-muted align-self-center">Sin foto</span>
            </div>
        </div>`;
    }

    html += `</div>`;
    return html;
}

function firmaWidgetInit(fid) {
    const canvas = document.getElementById('fw-' + fid + '-canvas');
    if (!canvas) return;
    const ratio = window.devicePixelRatio || 1;
    canvas.width  = canvas.offsetWidth  * ratio;
    canvas.height = canvas.offsetHeight * ratio;
    const ctx = canvas.getContext('2d');
    ctx.scale(ratio, ratio);
    ctx.strokeStyle = '#1a1a2e';
    ctx.lineWidth   = 2.5;
    ctx.lineCap     = 'round';
    _fw[fid] = { canvas, ctx, drawing: false, hasFirma: false, foto: null, lastX: 0, lastY: 0 };

    function pos(e) {
        const r = canvas.getBoundingClientRect();
        const src = e.touches?.[0] ?? e;
        return { x: src.clientX - r.left, y: src.clientY - r.top };
    }
    function start(e) {
        e.preventDefault();
        const s = _fw[fid]; s.drawing = true;
        const p = pos(e); s.lastX = p.x; s.lastY = p.y;
    }
    function move(e) {
        e.preventDefault();
        const s = _fw[fid];
        if (!s.drawing) return;
        const p = pos(e);
        s.ctx.beginPath(); s.ctx.moveTo(s.lastX, s.lastY);
        s.ctx.lineTo(p.x, p.y); s.ctx.stroke();
        s.lastX = p.x; s.lastY = p.y;
        if (!s.hasFirma) {
            s.hasFirma = true;
            canvas.classList.remove('empty');
            canvas.style.borderStyle = 'solid';
            const st = document.getElementById('fw-' + fid + '-status');
            if (st) { st.textContent = '✅ Firmado'; st.className = 'small text-success align-self-center fw-semibold'; }
        }
    }
    function end() { if (_fw[fid]) _fw[fid].drawing = false; }
    canvas.addEventListener('mousedown',  start);
    canvas.addEventListener('mousemove',  move);
    canvas.addEventListener('mouseup',    end);
    canvas.addEventListener('touchstart', start, { passive: false });
    canvas.addEventListener('touchmove',  move,  { passive: false });
    canvas.addEventListener('touchend',   end);

    // Drag & drop en área de foto del widget
    const fotoDrop = document.getElementById('fw-' + fid + '-foto-drop');
    if (fotoDrop) {
        fotoDrop.addEventListener('dragover',  e => { e.preventDefault(); fotoDrop.classList.add('dragover'); });
        fotoDrop.addEventListener('dragleave', ()  => fotoDrop.classList.remove('dragover'));
        fotoDrop.addEventListener('drop',      e => {
            e.preventDefault();
            fotoDrop.classList.remove('dragover');
            const file = e.dataTransfer.files?.[0];
            if (!file || !file.type.startsWith('image/')) return;
            firmaWidgetFoto(fid, { files: [file] });
        });
    }
}

function firmaWidgetTab(fid, tab) {
    const ca = document.getElementById('fw-' + fid + '-canvas-area');
    const fa = document.getElementById('fw-' + fid + '-foto-area');
    const bt = document.getElementById('fw-' + fid + '-tab-canvas');
    const bf = document.getElementById('fw-' + fid + '-tab-foto');
    if (ca) ca.style.display = tab === 'canvas' ? '' : 'none';
    if (fa) fa.style.display = tab === 'foto'   ? '' : 'none';
    if (bt) bt.classList.toggle('activo', tab === 'canvas');
    if (bf) bf.classList.toggle('activo', tab === 'foto');
}

function firmaWidgetLimpiar(fid) {
    const s = _fw[fid];
    if (!s?.canvas) return;
    const r = window.devicePixelRatio || 1;
    s.ctx.clearRect(0, 0, s.canvas.width / r, s.canvas.height / r);
    s.hasFirma = false;
    s.canvas.classList.add('empty');
    s.canvas.style.borderStyle = 'dashed';
    const st = document.getElementById('fw-' + fid + '-status');
    if (st) { st.textContent = 'Sin firma'; st.className = 'small text-muted align-self-center'; }
}

function firmaWidgetFoto(fid, input) {
    const file = input.files?.[0];
    if (!file) return;
    const reader = new FileReader();
    reader.onload = e => {
        if (!_fw[fid]) _fw[fid] = {};
        _fw[fid].foto = e.target.result;
        const p = document.getElementById('fw-' + fid + '-foto-preview');
        const d = document.getElementById('fw-' + fid + '-foto-drop');
        const s = document.getElementById('fw-' + fid + '-foto-status');
        if (p) { p.src = e.target.result; p.style.display = 'block'; }
        if (d) d.style.display = 'none';
        if (s) { s.textContent = '✅ Foto lista'; s.className = 'small text-success align-self-center fw-semibold'; }
    };
    reader.readAsDataURL(file);
}

function firmaWidgetLimpiarFoto(fid) {
    if (_fw[fid]) _fw[fid].foto = null;
    const p = document.getElementById('fw-' + fid + '-foto-preview');
    const d = document.getElementById('fw-' + fid + '-foto-drop');
    const s = document.getElementById('fw-' + fid + '-foto-status');
    const i = document.getElementById('fw-' + fid + '-foto-input');
    if (p) { p.src = ''; p.style.display = 'none'; }
    if (d) d.style.display = '';
    if (s) { s.textContent = 'Sin foto'; s.className = 'small text-muted align-self-center'; }
    if (i) i.value = '';
}

function firmaWidgetObtenerSVG(fid)  { return _fw[fid]?.hasFirma ? _fw[fid].canvas.toDataURL('image/png') : null; }
function firmaWidgetObtenerFoto(fid) { return _fw[fid]?.foto || null; }

// ══════════════════════════════════════════════════════════════════════
// formCliente
// ══════════════════════════════════════════════════════════════════════
const formCliente = {

    async cargar() {
        if (!TOKEN) {
            $('err-title').textContent = 'Token inválido';
            $('err-msg').textContent   = 'El enlace no es válido o ha expirado.';
            hide('loading-screen'); show('error-screen'); return;
        }
        try {
            const r = await fetch(`${API}?t=${encodeURIComponent(TOKEN)}`);
            const d = await r.json();
            if (!d.success) {
                $('err-title').textContent = d.error || 'No disponible';
                $('err-msg').textContent   = d.detalle || 'El formulario no está disponible.';
                hide('loading-screen'); show('error-screen'); return;
            }
            _data = d;
            _cfg  = d.config || {};

            // Si ya fue firmado/completado: mostrar pantalla bloqueada
            if (['firmado','completado'].includes(d.envio.estado)) {
                this._mostrarYaFirmado(d);
                return;
            }

            this._renderForm(d);
        } catch(e) {
            $('err-title').textContent = 'Error de conexión';
            $('err-msg').textContent   = 'No pudimos cargar el formulario. Intenta de nuevo.';
            hide('loading-screen'); show('error-screen');
        }
    },

    _aplicarDiseño(prefixId) {
        const cfg = _cfg;
        // Color del header
        if (cfg.doc_color) {
            document.querySelectorAll('.form-header').forEach(h => h.style.background = cfg.doc_color);
            const canvas = document.getElementById('firma-canvas');
            if (canvas) canvas.style.borderColor = cfg.doc_color;
        }
        // Nombr empresa, logo
        const hdr = $(prefixId + '-empresa-header');
        if (hdr && cfg.doc_encabezado) {
            $(prefixId + '-empresa-nombre').textContent    = cfg.doc_encabezado;
            $(prefixId + '-empresa-subtitulo').textContent = cfg.doc_subtitulo || '';
            if (cfg.doc_logo_base64) {
                const img = $(prefixId + '-empresa-logo');
                img.src = cfg.doc_logo_base64;
                img.style.display = '';
            }
            hdr.style.display = '';
        }
    },

    _mostrarYaFirmado(d) {
        this._aplicarDiseño('yf');
        const env = d.envio;
        const form = d.formulario;
        $('yf-titulo').textContent  = form.nombre || 'Formulario';

        const pac = (d.prefilled || {})['__paciente'];
        if (pac?.nombre_completo) {
            $('yf-paciente').textContent = pac.nombre_completo +
                (pac.numero_documento ? ' · ' + pac.numero_documento : '');
        }

        const badge = $('yf-estado-badge');
        if (env.estado === 'firmado') {
            badge.textContent = '✍️ Firmado digitalmente';
            badge.style.background = '#198754';
            badge.style.color = '#fff';
        } else {
            badge.textContent = '✅ Completado';
            badge.style.background = '#0d6efd';
            badge.style.color = '#fff';
        }

        if (env.firma_svg) {
            $('yf-firma-img').src = env.firma_svg;
            show('yf-firma-thumb');
        }

        // Link al PDF (acceso público vía token, no requiere sesión)
        $('yf-pdf-btn').href = 'ver_formulario_enviado.php?t=' + encodeURIComponent(TOKEN);

        if (env.hash_verificacion) {
            $('yf-hash').textContent = env.hash_verificacion;
            show('yf-hash-box');
        }

        hide('loading-screen');
        show('ya-firmado-screen');
    },

    _renderForm(d) {
        const form = d.formulario;
        const env  = d.envio;
        const prefilled = d.prefilled || {};

        // Diseño doc (color, logo, empresa)
        this._aplicarDiseño('f');

        // Header
        $('f-titulo').textContent = form.nombre;
        $('f-desc').textContent   = form.descripcion || '';

        // Info paciente
        const pac = prefilled['__paciente'];
        if (pac?.nombre_completo) {
            $('f-paciente-info').innerHTML =
                `<i class="fas fa-user me-1"></i>${esc(pac.nombre_completo)}` +
                (pac.numero_documento ? ` &nbsp;·&nbsp; ${esc(pac.numero_documento)}` : '');
        } else {
            hide('f-paciente-info');
        }

        // Si ya está completado/firmado
        if (['completado','firmado'].includes(env.estado)) {
            $('instruccion-nota').innerHTML =
                `<i class="fas fa-check-circle text-success me-1"></i>
                 Este formulario ya fue <strong>completado</strong>. Puedes ver tus respuestas abajo.`;
            $('btn-enviar').disabled = true;
            $('btn-enviar').textContent = 'Ya enviado';

            // Mostrar firma almacenada si existe
            if (env.firma_svg) {
                show('firma-container');
                const canvas = $('firma-canvas');
                if (canvas) canvas.style.display = 'none';
                // Insertar imagen de la firma almacenada
                const existingImg = document.getElementById('_firma-guardada');
                if (!existingImg) {
                    const img = document.createElement('img');
                    img.id = '_firma-guardada';
                    img.src = env.firma_svg;
                    img.alt = 'Firma';
                    img.className = 'border rounded p-2 mt-2';
                    img.style.cssText = 'max-height:140px;max-width:100%;display:block;margin:0 auto';
                    canvas.parentNode.insertBefore(img, canvas.nextSibling);
                }
            }
        }

        // Campos
        // firma_modos global: autoridad para campos tipo 'firma' (topbar del builder)
        const fModosStr = form.firma_modos || '';
        _firmaModosGlobal = fModosStr
            ? fModosStr.split(',').map(m => m.trim()).filter(Boolean)
            : null;

        const hasInlineFirma = form.esquema_decoded.some(c => c.tipo === 'firma' || c.tipo === 'firma_profesional');
        _hasInlineFirma = hasInlineFirma;
        renderCampos(form.esquema_decoded, prefilled);
        iniciarCondiciones(form.esquema_decoded);

        // Firma global (solo si NO hay firmas inline y permite_firma está activo)
        if (form.permite_firma && !hasInlineFirma) {
            show('firma-container');
            if (form.requiere_firma) show('firma-req-badge');
            firma.setModos(_firmaModosGlobal);
        }

        hide('loading-screen');
        show('form-screen');

        if (form.permite_firma && !hasInlineFirma) {
            requestAnimationFrame(() => requestAnimationFrame(() => {
                firma.init();
            }));
        }
        if (hasInlineFirma) {
            requestAnimationFrame(() => requestAnimationFrame(() => {
                // Solo inicializar el canvas para firma del paciente
                // firma_profesional se muestra bloqueada, sin canvas
                form.esquema_decoded.forEach(c => {
                    if (c.tipo === 'firma') firmaWidgetInit(c.id);
                });
            }));
        }
    },

    _recopilar() {
        const datos = {};
        const form  = document.getElementById('form-main');
        const campos = _data.formulario.esquema_decoded;

        campos.forEach(c => {
            if (c.tipo === 'separador' || c.tipo === 'firma' || c.tipo === 'firma_profesional' || c.tipo === 'parrafo') return;
            // linked: si tenía valor pre-llenado se ignora; si estaba vacío y el paciente lo llenó, recogerlo
            if (c.tipo === 'linked') {
                const inp = form.querySelector(`[name="${c.id}"]`);
                const v   = inp ? inp.value.trim() : '';
                if (v) datos[c.id] = v;
                return;
            }

            // parrafo_inline: recolectar solo los inputs editables (los que el usuario llenó)
            if (c.tipo === 'parrafo_inline') {
                form.querySelectorAll(`input[data-editable][name^="${c.id}_"]`).forEach(inp => {
                    const key = inp.name.slice(c.id.length + 1);
                    if (inp.value.trim()) datos[key] = inp.value.trim();
                });
                return;
            }

            if (c.tipo === 'checkbox' || c.tipo === 'lista_marcable') {
                const checks = [...form.querySelectorAll(`input[name="${c.id}"]:checked`)];
                datos[c.id] = checks.map(el => el.value);
            } else if (c.tipo === 'radio') {
                const r = form.querySelector(`input[name="${c.id}"]:checked`);
                datos[c.id] = r ? r.value : '';
            } else {
                const el = form.querySelector(`[name="${c.id}"]`);
                datos[c.id] = el ? el.value : '';
            }
        });
        // Recopilar firmas inline (firma y firma_profesional por campo)
        campos.forEach(c => {
            if (c.tipo !== 'firma' && c.tipo !== 'firma_profesional') return;
            const svg  = firmaWidgetObtenerSVG(c.id);
            const foto = firmaWidgetObtenerFoto(c.id);
            if (svg)  datos[c.id + '_svg']  = svg;
            if (foto) datos[c.id + '_foto'] = foto;
        });
        return datos;
    },

    _validar(datos) {
        const campos = _data.formulario.esquema_decoded;
        for (const c of campos) {
            if (!c.required) continue;
            if (c.tipo === 'separador' || c.tipo === 'linked' || c.tipo === 'firma' || c.tipo === 'firma_profesional' || c.tipo === 'parrafo' || c.tipo === 'parrafo_inline') continue;
            const v = datos[c.id];
            const vacio = Array.isArray(v) ? v.length === 0 : !v || !String(v).trim();
            if (vacio) {
                const el = document.querySelector(`[name="${c.id}"]`);
                if (el) { el.focus(); el.scrollIntoView({block:'center', behavior:'smooth'}); }
                return `El campo "${c.label}" es obligatorio.`;
            }
        }
        if (_data.formulario.requiere_firma && !_hasInlineFirma && !firma.obtenerSVG() && !firma.obtenerFoto()) {
            $('firma-container').scrollIntoView({block:'center', behavior:'smooth'});
            return 'La firma es obligatoria en este formulario.';
        }
        // Validar firmas inline requeridas (solo tipo 'firma' del paciente)
        for (const c of campos) {
            if (!c.required || c.tipo !== 'firma') continue;
            if (!firmaWidgetObtenerSVG(c.id) && !firmaWidgetObtenerFoto(c.id)) {
                const el = document.getElementById('fw-' + c.id);
                if (el) el.scrollIntoView({ block: 'center', behavior: 'smooth' });
                return `El campo "${c.label}" requiere firma.`;
            }
        }
        return null;
    },

    async enviar() {
        const datos = this._recopilar();
        const err   = this._validar(datos);
        if (err) {
            alert(err); return;
        }

        const payload = {
            token: TOKEN,
            datos_cliente: datos,
            firma_svg:  firma.obtenerSVG()  || null,
            firma_foto: firma.obtenerFoto() || null,
        };

        const btn = $('btn-enviar');
        btn.disabled = true;
        btn.innerHTML = '<span class="spinner-border spinner-border-sm me-2"></span>Enviando…';

        try {
            const r = await fetch(API, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify(payload),
            });
            const d = await r.json();
            if (d.success) {
                this._mostrarExito(d, payload.firma_svg);
            } else {
                alert(d.error || 'Error al enviar. Intenta de nuevo.');
                btn.disabled = false;
                btn.innerHTML = '<i class="fas fa-check-circle me-2"></i>Enviar formulario';
            }
        } catch(e) {
            alert('Error de conexión. Revisa tu internet e intenta de nuevo.');
            btn.disabled = false;
            btn.innerHTML = '<i class="fas fa-check-circle me-2"></i>Enviar formulario';
        }
    },

    _mostrarExito(d, firmaSvg) {
        hide('form-screen');
        const msg = d.firmado
            ? '¡Tu formulario fue firmado y enviado exitosamente!'
            : 'Tus respuestas fueron registradas correctamente.';
        $('success-msg').textContent = msg;
        if (firmaSvg) {
            $('success-firma-img').src = firmaSvg;
            $('success-firma-thumb').style.display = '';
        }
        // Hash de verificación
        if (d.hash) {
            $('success-hash').textContent = d.hash;
            show('success-hash-box');
        }
        // Link al PDF (acceso público vía token, no requiere sesión)
        if (d.envio_id) {
            const pdfBtn = $('success-pdf-btn');
            pdfBtn.href = 'ver_formulario_enviado.php?t=' + encodeURIComponent(TOKEN);
            show('success-pdf-row');
        }
        show('success-screen');
        window.scrollTo({top: 0, behavior: 'smooth'});
        // Notificar al frame padre si estamos en modo presencial del turnero
        try { window.parent.postMessage({ type: 'turneroFirmado' }, '*'); } catch(_) {}
    },
};

// ══════════════════════════════════════════════════════════════════════
// LÓGICA DE CONDICIONES — secciones visibles según valor de otro campo
// ══════════════════════════════════════════════════════════════════════
function iniciarCondiciones(esquema) {
    // Agrupar campos en secciones delimitadas por separadores
    // Una sección = { separadorId, condicion, elementIds[] }
    const secciones = [];
    let secActual = null;

    esquema.forEach(c => {
        if (c.tipo === 'separador') {
            secActual = { sepId: c.id, condicion: c.condicion || null, elemIds: [] };
            secciones.push(secActual);
        } else if (secActual) {
            secActual.elemIds.push(c.id);
        }
    });

    const condicionales = secciones.filter(s => s.condicion);
    if (!condicionales.length) return;

    // Marcar cada elemento de sección condicional con data-seccion
    condicionales.forEach(sec => {
        const sepEl = document.querySelector(`[data-campo-id="${sec.sepId}"]`);
        if (sepEl) sepEl.dataset.seccionCond = sec.sepId;
        sec.elemIds.forEach(id => {
            const el = document.querySelector(`[data-campo-id="${id}"]`);
            if (el) el.dataset.seccionCond = sec.sepId;
        });
    });

    function evaluar() {
        condicionales.forEach(sec => {
            const { campo_id, valor, valores } = sec.condicion;
            const valoresCond = valores ? valores : (valor ? [valor] : []);
            const ctrl = document.querySelector(`[data-campo-id="${campo_id}"]`);
            if (!ctrl) return;

            let activo = false;
            const checks = ctrl.querySelectorAll('input[type="checkbox"]');
            if (checks.length) {
                checks.forEach(cb => { if (valoresCond.includes(cb.value) && cb.checked) activo = true; });
            } else {
                const radio = ctrl.querySelector(`input[type="radio"]:checked`);
                const sel   = ctrl.querySelector('select');
                const inp   = ctrl.querySelector('input[type="text"],input[type="hidden"]');
                const v = radio ? radio.value : (sel ? sel.value : (inp ? inp.value : ''));
                activo = valoresCond.includes(v);
            }

            // Mostrar u ocultar toda la sección
            document.querySelectorAll(`[data-seccion-cond="${sec.sepId}"]`).forEach(el => {
                el.style.transition = 'opacity .2s, max-height .3s';
                if (activo) {
                    el.style.maxHeight = '2000px';
                    el.style.opacity   = '1';
                    el.style.overflow  = '';
                    el.style.pointerEvents = '';
                } else {
                    el.style.maxHeight = '0';
                    el.style.opacity   = '0';
                    el.style.overflow  = 'hidden';
                    el.style.pointerEvents = 'none';
                }
            });
        });
    }

    // Escuchar cambios en los campos controladores
    const camposCtrl = [...new Set(condicionales.map(s => s.condicion.campo_id))];
    camposCtrl.forEach(cid => {
        const el = document.querySelector(`[data-campo-id="${cid}"]`);
        if (el) el.addEventListener('change', evaluar);
    });

    // Evaluar estado inicial
    evaluar();
}

// ══════════════════════════════════════════════════════════════════════
// TOPAZ SIGWEB
// ══════════════════════════════════════════════════════════════════════
const topaz = (() => {
    const SIGWEB_URL = '/assets/js/SigWebTablet.js';
    let _loaded = false, _fid = null, _poll = null, _tmr = null;

    function _loadScript() {
        if (_loaded) return Promise.resolve(true);
        return new Promise(res => {
            const s = document.createElement('script');
            s.src = SIGWEB_URL;
            s.onload  = () => { _loaded = true; res(true); };
            s.onerror = () => res(false);
            document.head.appendChild(s);
        });
    }

    function _setStatus(msg, ok) {
        const el = $('topaz-status-msg');
        if (!el) return;
        el.textContent = msg;
        el.style.color = ok ? '#198754' : '#64748b';
        const area = $('topaz-pad-area');
        if (area) area.classList.toggle('has-sig', !!ok);
        const icon = $('topaz-pad-icon');
        if (icon) icon.style.color = ok ? '#198754' : '#adb5bd';
    }

    async function activar(fid) {
        const ok = await _loadScript();
        if (!ok) {
            alert('No se detectó SigWeb.\nInstala el servicio Topaz SigWeb y vuelve a intentarlo.');
            return;
        }
        _fid = fid;
        try {
            const canvas = document.getElementById('topaz-canvas');
            const canvasCtx = canvas.getContext('2d');
            canvasCtx.clearRect(0, 0, canvas.width, canvas.height);
            SetImageXSize(500);
            SetImageYSize(150);
            SetImagePenWidth(3);
            ClearTablet();
            _tmr = SetTabletState(1, canvasCtx, 50);
        } catch(e) {
            alert('Error al activar el pad: ' + e.message);
            return;
        }
        $('topaz-overlay').style.display = 'flex';
        $('topaz-btn-aceptar').disabled = true;
        $('topaz-pts').textContent = '0';
        _setStatus('Firme en el pad biométrico', false);

        let _lastPts = -1;
        _poll = setInterval(() => {
            try {
                const pts = NumberOfTabletPoints();
                $('topaz-pts').textContent = pts;
                const hasSig = pts > 0;
                $('topaz-btn-aceptar').disabled = !hasSig;
                if (hasSig) {
                    _setStatus('✅ Firma detectada — presione Aceptar', true);
                    if (pts !== _lastPts) {
                        _lastPts = pts;
                        GetSigImageB64(function(b64) {
                            if (!b64) return;
                            const img = new Image();
                            img.onload = () => {
                                const cv = document.getElementById('topaz-canvas');
                                const cx = cv.getContext('2d');
                                cx.clearRect(0, 0, cv.width, cv.height);
                                cx.drawImage(img, 0, 0, cv.width, cv.height);
                            };
                            img.src = 'data:image/png;base64,' + b64;
                        });
                    }
                }
            } catch(e) { _stopPoll(); }
        }, 400);
    }

    function _stopPoll() { if (_poll) { clearInterval(_poll); _poll = null; } }

    function _cerrarOverlay() {
        _stopPoll();
        try { SetTabletState(0, _tmr); } catch(e) {}
        _tmr = null; _fid = null;
        $('topaz-overlay').style.display = 'none';
    }

    function cancelar() { _cerrarOverlay(); }

    function limpiarPad() {
        try { _call('ClearTablet'); } catch(e) {}
        $('topaz-pts').textContent = '0';
        $('topaz-btn-aceptar').disabled = true;
        _setStatus('Firme en el pad biométrico', false);
    }

    function aceptar() {
        const fid = _fid;
        _cerrarOverlay();
        try {
          GetSigImageB64(function(b64) {
            if (!b64) { alert('No se capturó ninguna firma.'); return; }
            const img = new Image();
            img.onload = () => {
            if (fid === '__global') {
                const c  = $('firma-canvas');
                const cx = c.getContext('2d');
                const r  = window.devicePixelRatio || 1;
                cx.clearRect(0, 0, c.width / r, c.height / r);
                cx.drawImage(img, 0, 0, c.offsetWidth, c.offsetHeight);
                c.classList.remove('empty');
                _firmaDibujada = true;
                const st = $('firma-status');
                if (st) { st.textContent = '✅ Firma lista (tableta)'; st.className = 'small text-success align-self-center fw-semibold'; }
            } else {
                const s = _fw[fid];
                if (!s?.canvas) return;
                const r = window.devicePixelRatio || 1;
                s.ctx.clearRect(0, 0, s.canvas.width / r, s.canvas.height / r);
                s.ctx.drawImage(img, 0, 0, s.canvas.offsetWidth, s.canvas.offsetHeight);
                s.hasFirma = true;
                s.canvas.classList.remove('empty');
                s.canvas.style.borderStyle = 'solid';
                const st = document.getElementById('fw-' + fid + '-status');
                if (st) { st.textContent = '✅ Firmado (tableta)'; st.className = 'small text-success align-self-center fw-semibold'; }
            }
            };
            img.src = 'data:image/png;base64,' + b64;
          });
        } catch(e) { alert('Error al capturar la firma: ' + e.message); }
    }

    return { activar, cancelar, limpiarPad, aceptar };
})();

// ══════════════════════════════════════════════════════════════════════
// INIT
// ══════════════════════════════════════════════════════════════════════
document.addEventListener('DOMContentLoaded', () => formCliente.cargar());
</script>
</body>
</html>
