<?php
/**
 * ver_formulario_enviado.php — Ver respuesta de un formulario (imprimible / PDF)
 * Acceso admin/enfermero:   ver_formulario_enviado.php?id=ENVIO_ID  (requiere sesión)
 * Acceso público cliente:   ver_formulario_enviado.php?t=TOKEN      (sin sesión, solo firmado/completado)
 * Acceso turnero (consent): ver_formulario_enviado.php?token=UUID   (firma de consentimiento turnero)
 */
require_once 'config/config.php';

// ── TURNERO: Consentimiento por token UUID (detección previa al flujo normal) ──
$tokenTurnero = trim($_GET['token'] ?? '');
$modoTurnero  = ($tokenTurnero !== '');

if ($modoTurnero && !preg_match('/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i', $tokenTurnero)) {
    http_response_code(400); die('Token de consentimiento inválido');
}

$tokenPublico = trim($_GET['t'] ?? '');
$modoPublico  = ($modoTurnero || $tokenPublico !== '');

if (!$modoPublico && !isUserLoggedIn()) {
    header('Location: login.php'); exit;
}

// ── Cargar datos ──────────────────────────────────────────────────────
$db = Database::getInstance();

if ($modoTurnero) {
    // ── Turnero: cargar desde turnero_consentimientos ──────────────────
    $tcRow = $db->fetch(
        "SELECT tc.id, tc.turno_id, tc.formulario_id, tc.token, tc.estado,
                tc.enviado_at, tc.firmado_at, tc.ip_firma, tc.ua_firma, tc.firma_svg,
                tc.firma_profesional_svg, tc.firmado_profesional_at, tc.datos_respuestas,
                f.nombre AS form_nombre, f.categoria, f.descripcion AS form_descripcion,
                f.esquema, f.doc_encabezado, f.doc_subtitulo, f.doc_logo_base64, f.doc_color, f.doc_pie_pagina,
                p.nombre_completo AS paciente_nombre,
                p.numero_documento, p.tipo_documento,
                p.fecha_nacimiento, p.telefono AS paciente_telefono, p.eps,
                t.sesion_id,
                ts.numero_orden
         FROM   turnero_consentimientos tc
         JOIN   lab_formularios f  ON f.id  = tc.formulario_id
         JOIN   turnero_turnos   t ON t.id  = tc.turno_id
         LEFT   JOIN turnero_solicitudes ts ON ts.turno_id = tc.turno_id
         LEFT   JOIN lab_pacientes p ON p.id = COALESCE(ts.paciente_id, t.paciente_id)
         WHERE  tc.token = ?",
        [$tokenTurnero]
    );
    if (!$tcRow) { http_response_code(404); die('Consentimiento no encontrado'); }

    // ── POST: guardar firma y marcar como firmado ──────────────────────
    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
        header('Content-Type: application/json; charset=utf-8');
        if ($tcRow['estado'] === 'firmado') {
            echo json_encode(['ok' => false, 'error' => 'Este consentimiento ya fue firmado']); exit;
        }
        $input    = json_decode(file_get_contents('php://input'), true) ?? [];
        // Borrador: guardar campos sin firma (auto-save desde el formulario)
        // Muestras Prolongadas: marcar completo sin SVG global
        if (isset($input['mp_completar'])) {
            $dr       = is_array($input['datos_respuestas'] ?? null) ? $input['datos_respuestas'] : [];
            $existing = json_decode($tcRow['datos_respuestas'] ?? '{}', true) ?: [];
            $db->getConnection()
               ->prepare("UPDATE turnero_consentimientos SET estado='firmado', firmado_at=NOW(), datos_respuestas=? WHERE token=?")
               ->execute([json_encode(array_merge($existing, $dr), JSON_UNESCAPED_UNICODE), $tokenTurnero]);
            require_once __DIR__ . '/modules/turnero/api/_helpers.php';
            notificarSSE((int)$tcRow['sesion_id']);
            echo json_encode(['ok' => true, 'mp_completado' => true]); exit;
        }
        if (!array_key_exists('firma_svg', $input) && isset($input['datos_respuestas']) && is_array($input['datos_respuestas'])) {
            $existing = json_decode($tcRow['datos_respuestas'] ?? '{}', true) ?: [];
            $db->getConnection()
               ->prepare("UPDATE turnero_consentimientos SET datos_respuestas=? WHERE token=? AND estado='pendiente'")
               ->execute([json_encode(array_merge($existing, $input['datos_respuestas']), JSON_UNESCAPED_UNICODE), $tokenTurnero]);
            echo json_encode(['ok' => true, 'draft' => true]); exit;
        }
        $firmaSvg = trim($input['firma_svg'] ?? '');
        if (strlen($firmaSvg) < 100) {
            echo json_encode(['ok' => false, 'error' => 'Firma requerida']); exit;
        }
        $datosRespuestas = isset($input['datos_respuestas']) && is_array($input['datos_respuestas'])
            ? json_encode($input['datos_respuestas'], JSON_UNESCAPED_UNICODE)
            : null;
        $ip = filter_var(
            $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['REMOTE_ADDR'] ?? '',
            FILTER_VALIDATE_IP
        ) ?: ($_SERVER['REMOTE_ADDR'] ?? '');
        $ua   = substr($_SERVER['HTTP_USER_AGENT'] ?? '', 0, 500);
        $pdo  = $db->getConnection();
        $stmt = $pdo->prepare(
            "UPDATE turnero_consentimientos
             SET    estado='firmado', firmado_at=NOW(), firma_svg=?,
                    ip_firma=?, ua_firma=?, version_formulario=?, datos_respuestas=?
             WHERE  token=?"
        );
        $stmt->execute([$firmaSvg, $ip, $ua, (int)$tcRow['formulario_id'], $datosRespuestas, $tokenTurnero]);
        require_once __DIR__ . '/modules/turnero/api/_helpers.php';
        notificarSSE((int)$tcRow['sesion_id']);
        echo json_encode(['ok' => true, 'message' => 'Consentimiento firmado correctamente']); exit;
    }

    // ── GET: construir $envio compatible con la plantilla ─────────────
    $envio = [
        'id'                => (int)$tcRow['id'],
        'estado'            => $tcRow['estado'],
        'form_nombre'       => $tcRow['form_nombre'],
        'form_descripcion'  => $tcRow['form_descripcion'],
        'categoria'         => $tcRow['categoria'],
        'esquema'           => $tcRow['esquema'],
        'firma_svg'         => $tcRow['firma_svg'],
        'doc_color'         => $tcRow['doc_color'],
        'doc_encabezado'    => $tcRow['doc_encabezado'],
        'doc_subtitulo'     => $tcRow['doc_subtitulo'],
        'doc_logo_base64'   => $tcRow['doc_logo_base64'],
        'doc_pie_pagina'    => $tcRow['doc_pie_pagina'],
        'paciente_nombre'   => $tcRow['paciente_nombre'],
        'numero_documento'  => $tcRow['numero_documento'],
        'tipo_documento'    => $tcRow['tipo_documento'],
        'fecha_nacimiento'  => $tcRow['fecha_nacimiento'],
        'paciente_telefono' => $tcRow['paciente_telefono'],
        'eps'               => $tcRow['eps'],
        'enviado_por_nombre'=> $_SESSION['admin_user']['full_name'] ?? $_SESSION['admin_user']['username'] ?? 'Turnero',
        'enviado_por_email' => $_SESSION['admin_user']['email'] ?? null,
        'firma_profesional_svg' => $tcRow['firma_profesional_svg'] ?? null,
        'datos_cliente'     => $tcRow['datos_respuestas'] ?: '{}',
        'datos_prefilled'   => json_encode(['__paciente' => [
            'nombre_completo'  => $tcRow['paciente_nombre']   ?? '',
            'numero_documento' => $tcRow['numero_documento']  ?? '',
            'tipo_documento'   => $tcRow['tipo_documento']    ?? '',
            'telefono'         => $tcRow['paciente_telefono'] ?? '',
            'eps'              => $tcRow['eps']               ?? '',
        ]]),
        'created_at'        => $tcRow['enviado_at'] ?? date('Y-m-d H:i:s'),
        'completado_en'     => $tcRow['firmado_at'],
        'ip_cliente'        => $tcRow['ip_firma'],
        'hash_verificacion' => null,
        'permite_firma'     => 1,
        'requiere_firma'    => 1,
    ];
} elseif ($modoPublico) {
    // Acceso por token: solo si el formulario ya fue firmado/completado
    if (!preg_match('/^[a-f0-9]{64}$/i', $tokenPublico)) {
        http_response_code(400); die('Token inválido');
    }
    $envio = $db->fetch(
        "SELECT e.*, f.nombre AS form_nombre, f.categoria, f.descripcion AS form_descripcion,
                f.esquema, f.permite_firma, f.requiere_firma,
                f.doc_encabezado, f.doc_subtitulo, f.doc_logo_base64, f.doc_color, f.doc_pie_pagina,
                p.nombre_completo AS paciente_nombre, p.numero_documento, p.tipo_documento,
                p.fecha_nacimiento, p.telefono AS paciente_telefono, p.eps,
                u.full_name AS enviado_por_nombre, u.email AS enviado_por_email,
                ld.numero_orden
         FROM   lab_form_envios e
         JOIN   lab_formularios f ON f.id = e.formulario_id
         LEFT   JOIN lab_pacientes p ON p.id  = e.paciente_id
         LEFT   JOIN admin_users   u ON u.id  = e.enviado_por
         LEFT   JOIN lab_domicilios ld ON ld.id = e.domicilio_id
         WHERE  e.token = ?",
        [$tokenPublico]
    );
    if (!$envio) { http_response_code(404); die('Formulario no encontrado'); }
    if (!in_array($envio['estado'], ['firmado', 'completado'])) {
        http_response_code(403); die('El formulario aún no ha sido completado.');
    }
} else {
    $id = (int)($_GET['id'] ?? 0);
    if (!$id) { http_response_code(400); die('ID requerido'); }

    $envio = $db->fetch(
        "SELECT e.*, f.nombre AS form_nombre, f.categoria, f.descripcion AS form_descripcion,
                f.esquema, f.permite_firma, f.requiere_firma,
                f.doc_encabezado, f.doc_subtitulo, f.doc_logo_base64, f.doc_color, f.doc_pie_pagina,
                p.nombre_completo AS paciente_nombre, p.numero_documento, p.tipo_documento,
                p.fecha_nacimiento, p.telefono AS paciente_telefono, p.eps,
                u.full_name AS enviado_por_nombre, u.email AS enviado_por_email,
                ld.numero_orden
         FROM   lab_form_envios e
         JOIN   lab_formularios f ON f.id = e.formulario_id
         LEFT   JOIN lab_pacientes p ON p.id  = e.paciente_id
         LEFT   JOIN admin_users   u ON u.id  = e.enviado_por
         LEFT   JOIN lab_domicilios ld ON ld.id = e.domicilio_id
         WHERE  e.id = ?",
        [$id]
    );
    if (!$envio) { http_response_code(404); die('Formulario no encontrado'); }

    // Enfermero solo puede ver sus propios envíos
    if (isEnfermero()) {
        $uid = (int)($_SESSION['admin_user']['id'] ?? 0);
        if ($envio['enviado_por'] != $uid) { http_response_code(403); die('Sin acceso'); }
    }
}

// ── Consecutivo de orden (D-... domicilio | F-... turnero) ────────────
$numeroOrden = $modoTurnero
    ? ($tcRow['numero_orden'] ?? null)
    : ($envio['numero_orden'] ?? null);

// ── Config global del lab ─────────────────────────────────────────────
$cfgRows = $db->fetchAll('SELECT clave, valor FROM lab_config WHERE valor != ""');
$cfg = [];
foreach ($cfgRows as $r) { $cfg[$r['clave']] = $r['valor']; }

// Merge: per-form override > global config
$docColor      = $envio['doc_color']       ?: ($cfg['doc_color']       ?? '#1565c0');
$docEncabezado = $envio['doc_encabezado']  ?: ($cfg['empresa_nombre']  ?? '');
$docSubtitulo  = $envio['doc_subtitulo']   ?: ($cfg['empresa_subtitulo'] ?? '');
$docLogo       = $envio['doc_logo_base64'] ?: ($cfg['doc_logo_base64']  ?? '');
$docPiePagina  = $envio['doc_pie_pagina']  ?: ($cfg['doc_pie_pagina']   ?? '');
$docDireccion  = $cfg['empresa_direccion'] ?? '';
$docTelefono   = $cfg['empresa_telefono']  ?? '';
$docEmail      = $cfg['empresa_email']     ?? '';
$docCiudad     = $cfg['empresa_ciudad']    ?? '';

$esquema        = json_decode($envio['esquema'],         true) ?? [];
$datosCliente   = json_decode($envio['datos_cliente']   ?? '{}', true) ?? [];
$datosPrefilled = json_decode($envio['datos_prefilled'] ?? '{}', true) ?? [];
$todos          = array_merge($datosPrefilled, $datosCliente);
$modoEditar     = $modoTurnero && $envio['estado'] !== 'firmado';
$embebido       = isset($_GET['embed']) && $_GET['embed'] === '1';
$compact        = $embebido && isset($_GET['compact']);

// Firma pre-guardada del profesional logueado (para botón de 1 clic)
$firmaProfPreguardada = null;
if ($modoTurnero && $embebido && isUserLoggedIn()) {
    $uid = (int)($_SESSION['admin_user']['id'] ?? 0);
    if ($uid) {
        try {
            $stmt = $db->getConnection()->prepare("SELECT firma_svg FROM admin_users WHERE id = ? LIMIT 1");
            $stmt->execute([$uid]);
            $firmaProfPreguardada = $stmt->fetchColumn() ?: null;
        } catch (\Throwable $_) {}
    }
}
// Pre-scan: formulario que solo requiere firma del profesional (sin firma paciente)
$_soloFirmaPro  = !empty(array_filter($esquema, fn($c) => ($c['tipo'] ?? '') === 'firma_profesional'))
               && empty(array_filter($esquema, fn($c) => ($c['tipo'] ?? '') === 'firma'));

// Muestras Prolongadas: detectar secciones hora+firma_profesional con intervalos de minutos
// Agrupa por prefijo de sección (ej. "Taukit", "Heliprobe") para calcular esperas correctas
$_mpMap = [];
if ($modoTurnero && $embebido && $_soloFirmaPro) {
    $_mpSecs = []; $_mpLabel = null; $_mpMin = null; $_mpHora = null;
    foreach ($esquema as $_c) {
        $_t = $_c['tipo'] ?? '';
        if ($_t === 'separador') {
            $_mpLabel = $_c['label'] ?? ''; $_mpMin = null; $_mpHora = null;
            if (preg_match('/[Mm]inuto\s+(\d+)/u', $_mpLabel, $_mx)) $_mpMin = (int)$_mx[1];
        } elseif ($_t === 'hora' && $_mpMin !== null) {
            $_mpHora = $_c['id'] ?? null;
        } elseif ($_t === 'firma_profesional' && $_mpHora !== null && $_mpMin !== null) {
            $_prefix = trim(explode('·', $_mpLabel)[0] ?? '');
            $_mpSecs[] = ['label' => $_mpLabel, 'min' => $_mpMin, 'hora' => $_mpHora, 'firma' => $_c['id'], 'tipo' => $_prefix];
            $_mpHora = null;
        }
    }
    $_mpGroups = [];
    foreach ($_mpSecs as $_s) { $_mpGroups[$_s['tipo']][] = $_s; }
    foreach ($_mpGroups as $_tipo => $_secs) {
        for ($i = 0, $n = count($_secs); $i < $n; $i++) {
            $_s = $_secs[$i]; $_nx = $_secs[$i + 1] ?? null;
            $_mpMap[$_s['firma']] = [
                'hora_campo'    => $_s['hora'],
                'esperar_min'   => $_nx ? ($_nx['min'] - $_s['min']) : 0,
                'next_label'    => $_nx ? $_nx['label'] : null,
                'next_firma_id' => $_nx ? $_nx['firma'] : null,
                'is_last'       => !$_nx,
                'exam_type'     => $_tipo,
            ];
        }
    }
}

// Mapa id → label
$labelMap = [];
foreach ($esquema as $c) {
    if (!empty($c['id'])) $labelMap[$c['id']] = $c['label'] ?? $c['id'];
}

$completado = ($envio['completado_en'] ?? '') ? date('d/m/Y H:i', strtotime($envio['completado_en'])) : '—';
$creado     = ($envio['created_at']    ?? '') ? date('d/m/Y H:i', strtotime($envio['created_at']))    : '—';

function esc2(mixed $v): string {
    if (is_array($v)) return htmlspecialchars(implode(', ', $v), ENT_QUOTES);
    return htmlspecialchars((string)($v ?? ''), ENT_QUOTES);
}
?>
<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title><?= esc2($envio['form_nombre']) ?> — Respuesta</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>
        body { font-size: 14px; background: #f4f7fc; }

        /* ── Barra de acciones (se oculta al imprimir) ── */
        .action-bar {
            position: sticky; top: 0; z-index: 100;
            background: <?= htmlspecialchars($docColor) ?>; color: #fff;
            padding: 10px 20px; display: flex; align-items: center; gap: 12px;
        }
        .action-bar a { color: rgba(255,255,255,.8); text-decoration: none; font-size: 13px; }
        .action-bar a:hover { color: #fff; }

        /* ── Documento ──────────────────────────────────── */
        .doc-wrap    { max-width: 780px; margin: 24px auto; background: #fff;
                       border-radius: 10px; box-shadow: 0 2px 20px rgba(0,0,0,.12);
                       overflow: hidden; }
        .doc-header  { background: <?= htmlspecialchars($docColor) ?>;
                       color: #fff; padding: 24px 32px 20px; }
        .doc-header-inner { display: flex; align-items: flex-start; gap: 16px; }
        .doc-header-logo { flex-shrink: 0; }
        .doc-header-logo img { max-height: 64px; max-width: 100px; border-radius: 6px;
                               background: rgba(255,255,255,.15); padding: 4px; }
        .doc-header-text { flex: 1; }
        .doc-header-text h4 { margin: 0 0 2px; font-size: 15px; font-weight: 700;
                              opacity: .9; letter-spacing: .01em; }
        .doc-header-text .doc-subtitulo { font-size: 12px; opacity: .75; margin-bottom: 4px; }
        .doc-header-text .doc-contacto { font-size: 11px; opacity: .7; }
        .doc-header-text h3 { margin: 8px 0 0; font-size: 18px; font-weight: 700;
                              border-top: 1px solid rgba(255,255,255,.3); padding-top: 8px; }
        .doc-header-badge { flex-shrink: 0; }

        .doc-body { padding: 28px 32px; }

        /* ── Sección ────────────────────────────────────── */
        .section-title { font-size: 11px; font-weight: 700; text-transform: uppercase;
                         letter-spacing: .05em; color: #6c757d; border-bottom: 1px solid #dee2e6;
                         padding-bottom: 6px; margin: 24px 0 14px; }
        .section-title:first-child { margin-top: 0; }

        /* ── Fila campo ─────────────────────────────────── */
        .campo-row { display: flex; gap: 16px; padding: 6px 0;
                     border-bottom: 1px solid #f0f0f0; }
        .campo-row:last-child { border-bottom: none; }
        .campo-label { flex: 0 0 38%; font-size: 12px; color: #6c757d; padding-top: 1px; }
        .campo-valor { flex: 1; font-size: 13px; font-weight: 600; color: #212529;
                       word-break: break-word; }

        /* ── Separador del esquema ──────────────────────── */
        .esquema-sep { font-size: 11px; font-weight: 700; text-transform: uppercase;
                       letter-spacing: .05em; color: <?= htmlspecialchars($docColor) ?>; margin: 20px 0 8px;
                       border-bottom: 2px solid <?= htmlspecialchars($docColor) ?>; padding-bottom: 4px; }

        /* ── Estado badge ───────────────────────────────── */
        .estado-badge { display: inline-block; padding: 3px 10px; border-radius: 20px;
                        font-size: 11px; font-weight: 700; text-transform: uppercase; }
        .estado-firmado    { background: #d1fae5; color: #065f46; }
        .estado-completado { background: #dbeafe; color: #1e40af; }
        .estado-pendiente  { background: #fef3c7; color: #92400e; }

        /* ── Firma ──────────────────────────────────────── */
        .firma-box { background: #f8faff; border: 1px solid #c9d8ff;
                     border-radius: 8px; padding: 14px; display: inline-block; margin-top: 8px; }
        .firma-box img { max-height: 140px; max-width: 340px; display: block; }

        /* ── Footer del doc ─────────────────────────────── */
        .doc-footer { background: #f8faff; border-top: 1px solid #e9ecef;
                      padding: 14px 32px; font-size: 11px; color: #6c757d;
                      display: flex; justify-content: space-between; flex-wrap: wrap; gap: 6px; }
        .hash-short { font-family: monospace; opacity: .7; }

        /* ── Sello SHA-256 ──────────────────────────────── */
        .hash-seal { border: 1px solid #c3d3f7; border-radius: 8px; overflow: hidden; }
        .hash-seal-header { background: <?= htmlspecialchars($docColor) ?>; color: #fff; padding: 8px 14px;
                            font-size: 11px; font-weight: 700; text-transform: uppercase;
                            letter-spacing: .05em; }
        .hash-seal-body { background: #f0f5ff; padding: 12px 14px; }
        .hash-label { font-size: 11px; color: #6c757d; margin-bottom: 4px; }
        .hash-value { display: block; font-family: monospace; font-size: 11px;
                      color: #1e3a6e; word-break: break-all; background: #fff;
                      border: 1px solid #d0dcf7; border-radius: 4px; padding: 6px 8px; }
        .hash-hint { font-size: 11px; color: #6c757d; }
        .hash-hint a { color: #1565c0; word-break: break-all; }
        @media print {
            .hash-seal { border-color: #000; }
            .hash-seal-header { background: #000 !important; -webkit-print-color-adjust:exact; print-color-adjust:exact; }
        }

        <?php if ($embebido): ?>
        /* ── Modo embebido (iframe) ── */
        body { background: #fff; font-size: 12px; }
        .action-bar { display: none !important; }
        .doc-wrap { margin: 0; border-radius: 0; box-shadow: none; max-width: 100%; }
        .doc-header { padding: 10px 14px 8px; }
        .doc-header-text h3 { font-size: 14px; }
        .doc-header-text h4 { font-size: 12px; }
        .doc-body { padding: 12px 14px; }
        .doc-footer { display: none; }
        .section-title { font-size: 10px; margin: 14px 0 8px; }
        .campo-label { font-size: 11px; }
        .campo-valor { font-size: 12px; }
        .campo-edit { margin-bottom: 8px; }
        .campo-edit label { font-size: 11px; margin-bottom: 2px; }
        .campo-edit .form-control,
        .campo-edit .form-select { font-size: 12px; padding: 3px 7px; }
        .campo-edit .form-check-label { font-size: 12px; }
        .campo-row { padding: 3px 0; }
        .turnero-cv { height: 110px !important; }
        .btn { font-size: 12px; padding: 3px 10px; }
        .hash-seal { display: none; }
        .alert { font-size: 12px; padding: 6px 10px; }
        <?php endif; ?>

        <?php if ($compact): ?>
        /* ── Modo compact (tablet, 2 columnas) ── */
        @media (min-width: 480px) {
            .campos-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 4px 14px; align-items: start; }
            .campos-grid .campo-full { grid-column: span 2; }
        }
        <?php endif; ?>

        /* ── Canvas firma profesional ───────────────────── */
        .firma-pro-widget { max-width: 520px; margin-top: 8px; }
        .fpw-canvas { border: 2px solid #198754; border-radius: 8px; background: #f8fff9;
                      cursor: crosshair; display: block; max-width: 100%; touch-action: none; }

        /* ── Campos interactivos (modo turnero editar) ──── */
        .campo-edit { margin-bottom: 14px; }
        .campo-edit label { display: block; font-size: 12px; color: #6c757d;
                            font-weight: 600; margin-bottom: 4px; }
        .campo-edit .form-control,
        .campo-edit .form-select { font-size: 13px; border-color: #c3d3f7;
                                    background: #f8faff; }
        .campo-edit .form-control:focus,
        .campo-edit .form-select:focus { border-color: #1565c0;
                                          box-shadow: 0 0 0 3px rgba(21,101,192,.12); }
        .campo-edit .form-check-label { font-size: 13px; }
        .campo-linked { display: flex; gap: 16px; padding: 6px 0;
                        border-bottom: 1px solid #f0f0f0; }
        .campo-linked-label { flex: 0 0 38%; font-size: 12px; color: #6c757d; }
        .campo-linked-valor { flex: 1; font-size: 13px; font-weight: 600;
                              color: #1565c0; }

        /* ═══════ ESTILOS DE IMPRESIÓN ═══════════════════ */
        @media print {
            body { background: #fff !important; font-size: 12px; }
            .action-bar { display: none !important; }
            .doc-wrap { margin: 0; border-radius: 0; box-shadow: none; max-width: 100%; }
            .doc-header { -webkit-print-color-adjust: exact; print-color-adjust: exact; padding: 18px 22px; }
            .doc-body { padding: 18px 22px; }
            .doc-footer { padding: 10px 22px; }
            @page { margin: 1cm; }
        }
        /* ── 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; }
        @keyframes mp-pulse { 0%,100%{opacity:1} 50%{opacity:.65} }
    </style>
</head>
<body>

<!-- ── Barra de acciones ──────────────────────────────────────────── -->
<div class="action-bar no-print">
    <?php if ($modoTurnero): ?>
    <span style="font-size:13px;opacity:.85">
        <i class="fas fa-file-signature me-1"></i>
        <?= $envio['estado'] === 'firmado' ? 'Consentimiento firmado ✓' : 'Lea el documento y firme al final' ?>
    </span>
    <div style="flex:1"></div>
    <?php if ($envio['estado'] === 'firmado'): ?>
    <button onclick="window.print()" class="btn btn-warning btn-sm fw-semibold">
        <i class="fas fa-file-pdf me-2"></i>Imprimir
    </button>
    <?php endif; ?>
    <?php elseif (!$modoPublico): ?>
    <a href="lab_formularios.php#envios"><i class="fas fa-arrow-left me-1"></i>Volver</a>
    <div style="flex:1"></div>
    <button onclick="window.print()" class="btn btn-warning btn-sm fw-semibold">
        <i class="fas fa-file-pdf me-2"></i>Descargar / Imprimir PDF
    </button>
    <?php else: ?>
    <span style="font-size:13px;opacity:.85"><i class="fas fa-file-alt me-1"></i>Documento firmado</span>
    <div style="flex:1"></div>
    <button onclick="window.print()" class="btn btn-warning btn-sm fw-semibold">
        <i class="fas fa-file-pdf me-2"></i>Descargar / Imprimir PDF
    </button>
    <?php endif; ?>
</div>

<!-- ── Documento ─────────────────────────────────────────────────── -->
<div class="doc-wrap">

    <!-- Header -->
    <div class="doc-header">
        <div class="doc-header-inner">
            <?php if ($docLogo): ?>
            <div class="doc-header-logo">
                <img src="<?= htmlspecialchars($docLogo) ?>" alt="Logo">
            </div>
            <?php endif; ?>
            <div class="doc-header-text">
                <?php if ($docEncabezado): ?>
                <h4><?= esc2($docEncabezado) ?></h4>
                <?php endif; ?>
                <?php if ($docSubtitulo): ?>
                <div class="doc-subtitulo"><?= esc2($docSubtitulo) ?></div>
                <?php endif; ?>
                <?php
                $contacto = array_filter([$docDireccion, $docCiudad, $docTelefono, $docEmail]);
                if ($contacto): ?>
                <div class="doc-contacto"><?= esc2(implode(' · ', $contacto)) ?></div>
                <?php endif; ?>
                <h3><?= esc2($envio['form_nombre']) ?></h3>
                <?php if ($envio['form_descripcion']): ?>
                <div style="font-size:12px;opacity:.8;margin-top:4px"><?= esc2($envio['form_descripcion']) ?></div>
                <?php endif; ?>
            </div>
            <div class="doc-header-badge" style="text-align:right">
                <?php if ($numeroOrden): ?>
                <?php
                $esDomicilio = str_starts_with($numeroOrden, 'D-');
                $ordenColor  = $esDomicilio ? '#bbf7d0' : '#bfdbfe';
                $ordenTxt    = $esDomicilio ? '#14532d' : '#1e3a8a';
                $ordenLabel  = $esDomicilio ? 'Domicilio' : 'Turnero';
                ?>
                <div style="background:<?= $ordenColor ?>;color:<?= $ordenTxt ?>;border-radius:8px;
                            padding:4px 10px;font-size:12px;font-weight:700;letter-spacing:.03em;
                            margin-bottom:6px;font-family:monospace">
                    <?= esc2($numeroOrden) ?>
                </div>
                <div style="font-size:10px;opacity:.75;margin-bottom:4px"><?= $ordenLabel ?></div>
                <?php endif; ?>
                <?php
                $eBadge = match($envio['estado']) {
                    'firmado'    => 'estado-firmado',
                    'completado' => 'estado-completado',
                    default      => 'estado-pendiente',
                };
                ?>
                <span class="estado-badge <?= $eBadge ?>"><?= esc2($envio['estado']) ?></span>
                <div style="font-size:10px;opacity:.7;margin-top:4px"><?= esc2($envio['categoria'] ?? '') ?></div>
            </div>
        </div>
    </div>

    <div class="doc-body">

        <!-- Info del paciente: omitir si el esquema ya tiene campos linked que la muestran -->
        <?php
        $_tieneLinked = !empty(array_filter($esquema, fn($c) => ($c['tipo'] ?? '') === 'linked'));
        ?>
        <?php if ($envio['paciente_nombre'] && !$_tieneLinked): ?>
        <div class="section-title"><i class="fas fa-user me-1"></i>Datos del paciente</div>
        <div class="campo-row">
            <div class="campo-label">Nombre completo</div>
            <div class="campo-valor"><?= esc2($envio['paciente_nombre']) ?></div>
        </div>
        <?php if ($envio['numero_documento']): ?>
        <div class="campo-row">
            <div class="campo-label"><?= esc2($envio['tipo_documento'] ?? 'Documento') ?></div>
            <div class="campo-valor"><?= esc2($envio['numero_documento']) ?></div>
        </div>
        <?php endif; ?>
        <?php if ($envio['fecha_nacimiento']): ?>
        <div class="campo-row">
            <div class="campo-label">Fecha de nacimiento</div>
            <div class="campo-valor"><?= esc2(date('d/m/Y', strtotime($envio['fecha_nacimiento']))) ?></div>
        </div>
        <?php endif; ?>
        <?php if ($envio['paciente_telefono']): ?>
        <div class="campo-row">
            <div class="campo-label">Teléfono</div>
            <div class="campo-valor"><?= esc2($envio['paciente_telefono']) ?></div>
        </div>
        <?php endif; ?>
        <?php if ($envio['eps']): ?>
        <div class="campo-row">
            <div class="campo-label">EPS / Aseguradora</div>
            <div class="campo-valor"><?= esc2($envio['eps']) ?></div>
        </div>
        <?php endif; ?>
        <?php endif; ?>

        <!-- Respuestas del formulario (en orden del esquema) -->
        <div class="section-title">
            <i class="fas fa-wpforms me-1"></i>
            <?= $modoEditar ? 'Formulario de consentimiento' : 'Respuestas del formulario' ?>
        </div>
        <?php
        $paciente = $datosPrefilled['__paciente'] ?? [];

        // Firma global del profesional (un solo valor para todos los campos firma_profesional)
        $firmaSharedProfesional = $envio['firma_profesional_svg'] ?? null;
        // Firma global del paciente: fallback para cuando no hay datos_cliente por campo
        $firmaSharedPaciente    = $envio['firma_svg'] ?? null;

        // Rastrear si ya se renderizó la firma inline (para no duplicarla al pie)
        $_renderedFirmaProfesional  = false;
        $_renderedFirmaPaciente     = false;
        $_esquemaTieneFirmaPaciente = false;
        $_esquemaTieneFirmaAlguna   = false; // true si hay cualquier campo firma/firma_profesional
        $_firmaGlobalPacienteUsada  = false; // la firma global solo va al primer campo firma

        if ($compact): ?><div class="campos-grid"><?php endif;
        foreach ($esquema as $campo):
            $tipo = $campo['tipo'] ?? '';
            if ($tipo === 'separador'): ?>
            <div class="esquema-sep<?= $compact ? ' campo-full' : '' ?>"><?= esc2($campo['label'] ?? '') ?></div>
        <?php   continue; endif;

            // ── Firmas inline: cada campo muestra su propia firma ──
            if ($tipo === 'firma' || $tipo === 'firma_profesional'):
                $cid   = $campo['id'] ?? null;
                if (!$cid) continue;
                $isPro = ($tipo === 'firma_profesional');

                // Marcar presencia de campos firma en el esquema
                $_esquemaTieneFirmaAlguna = true;
                if (!$isPro) {
                    $_esquemaTieneFirmaPaciente = true;
                }

                // La firma profesional global solo se muestra en el PRIMER campo firma_profesional.
                // Campos posteriores (ej. desistimiento) solo muestran su propia firma por campo.
                $fSvg  = $isPro
                    ? (empty($_mpMap) && !$_renderedFirmaProfesional
                        ? ($firmaSharedProfesional ?? $datosCliente[$cid . '_svg'] ?? null)
                        : ($datosCliente[$cid . '_svg'] ?? null))
                    : ($datosCliente[$cid . '_svg'] ?? (!$_firmaGlobalPacienteUsada ? $firmaSharedPaciente : null) ?? null);
                $fFoto = $datosCliente[$cid . '_foto'] ?? null;
                $fIcon    = $isPro ? 'fa-user-md' : 'fa-signature';
                $fColor   = $isPro ? '#198754' : '#1565c0';
                $fLabel   = htmlspecialchars($campo['label'] ?? ($isPro ? 'Firma profesional' : 'Firma paciente'));

                // Campo paciente sin firma:
                // - En turnero pendiente: todos los campos firma muestran canvas.
                // - En vista normal: omitir campos sin firma.
                if (!$fSvg && !$fFoto && !$isPro) {
                    if (!($modoTurnero && $modoEditar)) {
                        continue; // no-turnero: omitir campos sin firma
                    }
                }

                $yaHayCanvasPro = $isPro && $_renderedFirmaProfesional && empty($_mpMap);
                if ($isPro) $_renderedFirmaProfesional = true;
                if (!$isPro && $fSvg) {
                    $_renderedFirmaPaciente    = true;
                    $_firmaGlobalPacienteUsada = true;
                }
        ?>
        <?php if ($compact): ?><div class="campo-full"><?php endif; ?>
        <div class="section-title mt-3" style="color:<?= $fColor ?>">
            <i class="fas <?= $fIcon ?> me-1"></i><?= $fLabel ?>
        </div>
        <?php if ($fSvg): ?>
        <div class="firma-box" style="border-color:<?= $fColor ?>">
            <img src="<?= htmlspecialchars($fSvg) ?>" alt="<?= $fLabel ?>">
        </div>
        <?php elseif ($fFoto): ?>
        <div class="firma-box mt-2" style="border-color:<?= $fColor ?>">
            <img src="<?= htmlspecialchars($fFoto) ?>" alt="Foto - <?= $fLabel ?>">
        </div>
        <?php elseif (!$isPro && $modoTurnero && $modoEditar): ?>
        <div class="turnero-firma-item no-print" data-cid="<?= esc2($cid) ?>">
        <p class="text-muted small mb-2"><i class="fas fa-pen me-1"></i>He leído el documento. Dibuje su firma:</p>
        <canvas class="turnero-cv" width="500" height="160"
                style="border:2px solid #1565c0;border-radius:8px;background:#f0f4ff;
                       cursor:crosshair;display:block;max-width:100%;touch-action:none"></canvas>
        <div class="mt-2 d-flex gap-2 flex-wrap">
            <button class="btn btn-outline-secondary btn-sm turnero-limpiar">
                <i class="fas fa-eraser me-1"></i>Limpiar
            </button>
            <button class="btn-topaz turnero-topaz" title="Usar pad biométrico Topaz">
                <i class="fas fa-tablet-alt"></i>Tableta
            </button>
            <button class="btn btn-success fw-semibold turnero-firmar">
                <i class="fas fa-check-circle me-1"></i>Confirmar: <?= $fLabel ?>
            </button>
        </div>
        <div class="turnero-msg mt-2 small"></div>
        </div>
        <?php elseif ($isPro): ?>
        <?php
        // Mostrar canvas pro si: admin normal, O turnero+embebido+sesión activa+solo firma pro
        $_mostrarCanvasPro = !$yaHayCanvasPro && (
            (!$modoTurnero && !$modoPublico) ||
            ($modoTurnero && $embebido && isUserLoggedIn() && $_soloFirmaPro && $modoEditar)
        );
        if ($_mostrarCanvasPro): ?>
        <?php if ($firmaProfPreguardada): ?>
        <script>window._fpwPreFirma = <?= json_encode($firmaProfPreguardada) ?>;</script>
        <?php endif; ?>
        <!-- Canvas del profesional -->
        <div class="firma-pro-widget no-print" id="fpw-<?= htmlspecialchars($cid) ?>"
             data-envio="<?= (int)$envio['id'] ?>" data-campo="<?= htmlspecialchars($cid) ?>"
             data-solo-pro="<?= ($modoTurnero && $_soloFirmaPro) ? '1' : '0' ?>"
             data-turno="<?= isset($tcRow) ? (int)$tcRow['turno_id'] : '' ?>"
             data-formulario="<?= isset($tcRow) ? (int)$tcRow['formulario_id'] : '' ?>">
            <?php if ($firmaProfPreguardada): ?>
            <div class="fpw-oneclic">
                <button class="btn btn-success fpw-oneclic-btn w-100" style="font-size:1rem;padding:.55rem 1rem">
                    <i class="fas fa-check-circle me-2"></i>Firmar consentimiento
                </button>
                <div class="text-center mt-1">
                    <a href="#" class="small text-muted fpw-usar-otra">✎ Usar otra firma</a>
                </div>
            </div>
            <div class="fpw-canvas-wrap" style="display:none">
            <?php endif; ?>
            <p class="text-muted small mb-2"><i class="fas fa-pen me-1"></i>Dibuje su firma en el recuadro:</p>
            <canvas class="fpw-canvas" width="500" height="150"></canvas>
            <div class="mt-2 d-flex gap-2 flex-wrap">
                <button class="btn btn-outline-secondary btn-sm fpw-clear"><i class="fas fa-eraser me-1"></i>Limpiar</button>
                <button class="btn-topaz fpw-topaz" title="Usar pad biométrico Topaz">
                    <i class="fas fa-tablet-alt"></i>Tableta
                </button>
                <button class="btn btn-success btn-sm fpw-save"><i class="fas fa-check me-1"></i>Guardar firma</button>
            </div>
            <?php if ($firmaProfPreguardada): ?></div><?php endif; ?>
            <div class="fpw-msg mt-2 small"></div>
        </div>
        <?php elseif ($modoPublico): ?>
        <div class="alert py-2 px-3 no-print" style="background:#fff8e1;border:1.5px dashed #f59e0b;border-radius:8px;color:#92400e;font-size:.85rem">
            <i class="fas fa-hourglass-half me-2"></i>
            <strong>Pendiente de firma del profesional.</strong>
        </div>
        <?php endif; ?>
        <?php endif; ?>
        <?php if ($compact): ?></div><?php endif; ?>
        <?php   continue; endif;

            // ── Parrafo estático ──────────────────────────────────
            if ($tipo === 'parrafo'):
                $ws = !empty($campo['flujoLibre']) ? 'normal' : 'pre-wrap';
        ?>
            <div class="mb-3<?= $compact ? ' campo-full' : '' ?>" style="font-size:.88rem;line-height:1.75;color:#444;text-align:justify;white-space:<?= $ws ?>"><?= htmlspecialchars($campo['contenido'] ?? '', ENT_QUOTES) ?></div>
        <?php   continue; endif;

            // ── Parrafo inline (texto con marcadores {key}) ───────
            if ($tipo === 'parrafo_inline'):
                $contenido = $campo['contenido'] ?? '';
                // Sustituir {key} con valor del paciente o de $todos
                $rendered = preg_replace_callback('/\{([a-z_]+)\}/', function($m) use ($paciente, $todos) {
                    return $paciente[$m[1]] ?? $todos[$m[1]] ?? $m[0];
                }, $contenido);
                // Convertir saltos de línea a <br>
                $rendered = nl2br(htmlspecialchars($rendered, ENT_QUOTES));
        ?>
            <div class="mb-3<?= $compact ? ' campo-full' : '' ?>" style="font-size:.88rem;line-height:2;color:#444;text-align:justify"><?= $rendered ?></div>
        <?php   continue; endif;

            $cid = $campo['id'] ?? null;
            if (!$cid) continue;

            // ── Modo editar (turnero o envío normal): campos interactivos ──
            if ($modoEditar):
                $prefill = $todos[$cid] ?? '';
                // Auto-llenar campos del profesional desde la sesión
                if ($prefill === '' && !empty($campo['auto_profesional']) && isUserLoggedIn()) {
                    $prefill = match($campo['auto_profesional']) {
                        'nombre' => $_SESSION['admin_user']['full_name'] ?? '',
                        'cargo'  => $_SESSION['admin_user']['cargo']     ?? '',
                        default  => ''
                    };
                }
                $label   = esc2($campo['label'] ?? $cid);
                $req     = !empty($campo['required']) ? ' required' : '';
                if ($tipo === 'linked'):
                    $lk   = $campo['linked_key'] ?? '';
                    $lval = $paciente[$lk] ?? $todos[$cid] ?? '';
                    if ($lval !== ''):
        ?>
                <div class="campo-linked">
                    <div class="campo-linked-label"><?= $label ?></div>
                    <div class="campo-linked-valor"><?= esc2($lval) ?></div>
                </div>
        <?php       endif; continue; endif; // linked
                if ($tipo === 'textarea'):
        ?>
                <div class="campo-edit<?= $compact ? ' campo-full' : '' ?>">
                    <label><?= $label ?></label>
                    <textarea class="form-control form-control-sm" name="<?= esc2($cid) ?>"
                              rows="3"<?= $req ?>><?= esc2($prefill) ?></textarea>
                </div>
        <?php       continue; endif;
                if ($tipo === 'radio'):
                    $opts = $campo['opciones'] ?? $campo['options'] ?? [];
        ?>
                <div class="campo-edit<?= ($compact && count($opts) > 3) ? ' campo-full' : '' ?>">
                    <label><?= $label ?></label>
                    <?php foreach ($opts as $opt): ?>
                    <div class="form-check">
                        <input class="form-check-input" type="radio"
                               name="<?= esc2($cid) ?>" value="<?= esc2($opt) ?>"
                               <?= ($prefill === $opt ? 'checked' : '') . $req ?>>
                        <label class="form-check-label"><?= esc2($opt) ?></label>
                    </div>
                    <?php endforeach; ?>
                </div>
        <?php       continue; endif;
                if ($tipo === 'checkbox' || $tipo === 'lista_marcable'):
                    $opts       = $campo['opciones'] ?? $campo['options'] ?? $campo['items'] ?? [];
                    $checkedArr = is_array($prefill) ? $prefill
                                : (is_string($prefill) && $prefill !== '' ? (json_decode($prefill, true) ?: [$prefill]) : []);
        ?>
                <div class="campo-edit<?= $compact ? ' campo-full' : '' ?>">
                    <label><?= $label ?></label>
                    <div class="d-flex flex-wrap gap-2">
                    <?php foreach ($opts as $opt): ?>
                    <div class="form-check form-check-inline m-0">
                        <input class="form-check-input" type="checkbox"
                               name="<?= esc2($cid) ?>[]" value="<?= esc2($opt) ?>"
                               <?= in_array($opt, $checkedArr, true) ? 'checked' : '' ?>>
                        <label class="form-check-label"><?= esc2($opt) ?></label>
                    </div>
                    <?php endforeach; ?>
                    </div>
                </div>
        <?php       continue; endif;
                if ($tipo === 'select'):
                    $opts = $campo['opciones'] ?? $campo['options'] ?? [];
        ?>
                <div class="campo-edit">
                    <label><?= $label ?></label>
                    <select class="form-select form-select-sm" name="<?= esc2($cid) ?>"<?= $req ?>>
                        <option value="">— Seleccione —</option>
                        <?php foreach ($opts as $opt): ?>
                        <option value="<?= esc2($opt) ?>"<?= $prefill === $opt ? ' selected' : '' ?>><?= esc2($opt) ?></option>
                        <?php endforeach; ?>
                    </select>
                </div>
        <?php       continue; endif;
                // Calculados: pre-llenar si no tienen valor aún
                if ($tipo === 'fecha_hoy' && $prefill === '') {
                    $prefill = date('Y-m-d');
                }
                if ($tipo === 'edad' && $prefill === '') {
                    $fnac = $paciente['fecha_nacimiento'] ?? '';
                    if ($fnac) {
                        $hoy  = new DateTime();
                        $bday = new DateTime($fnac);
                        $prefill = (string)$hoy->diff($bday)->y;
                    }
                }
                // texto, numero, fecha, hora, y calculados → input
                $inputType = match($tipo) {
                    'numero', 'edad' => 'number',
                    'fecha', 'fecha_hoy' => 'date',
                    'hora' => 'time',
                    default => 'text'
                };
        ?>
                <div class="campo-edit">
                    <label><?= $label ?></label>
                    <input type="<?= $inputType ?>" class="form-control form-control-sm"
                           name="<?= esc2($cid) ?>" value="<?= esc2($prefill) ?>"<?= $req ?>
                           <?= in_array($tipo, ['edad']) ? 'min="0" max="120"' : '' ?>>
                </div>
        <?php       continue;
            endif; // modoEditar

            // ── Modo vista: mostrar valores existentes ─────────────
            if ($tipo === 'linked') {
                $lk    = $campo['linked_key'] ?? '';
                $valor = $paciente[$lk] ?? $todos[$cid] ?? null;
            } elseif ($tipo === 'fecha_hoy') {
                $valor = $todos[$cid] ?? date('Y-m-d');
            } elseif ($tipo === 'edad') {
                $valor = $todos[$cid] ?? '';
                if ($valor === '') {
                    $fnac = $paciente['fecha_nacimiento'] ?? '';
                    if ($fnac) {
                        $valor = (string)(new DateTime())->diff(new DateTime($fnac))->y;
                    }
                }
            } else {
                $valor = $todos[$cid] ?? null;
            }

            if ($valor === null || $valor === '') continue;
            $display = is_array($valor) ? implode(', ', $valor) : (string)$valor;
        ?>
            <div class="campo-row">
                <div class="campo-label"><?= esc2($campo['label'] ?? $cid) ?></div>
                <div class="campo-valor"><?= esc2($display) ?></div>
            </div>
        <?php endforeach; ?>
        <?php if ($compact): ?></div><?php endif; ?>

        <!-- Firma digital global (solo si el esquema no tiene campo firma propio) -->
        <?php if ($envio['firma_svg'] && !$_renderedFirmaPaciente): ?>
        <div class="section-title mt-4"><i class="fas fa-signature me-1"></i>Firma digital (dibujo)</div>
        <div class="firma-box">
            <img src="<?= htmlspecialchars($envio['firma_svg']) ?>" alt="Firma digital">
        </div>
        <?php endif; ?>

        <!-- Firma foto global -->
        <?php if (!empty($datosCliente['__firma_foto'])): ?>
        <div class="section-title mt-4"><i class="fas fa-camera me-1"></i>Foto de firma / documento</div>
        <div class="firma-box">
            <img src="<?= htmlspecialchars($datosCliente['__firma_foto']) ?>" alt="Foto de firma">
        </div>
        <?php endif; ?>

        <!-- Info del envío -->
        <div class="section-title mt-4"><i class="fas fa-clock me-1"></i>Información del envío</div>
        <div class="campo-row">
            <div class="campo-label">Enviado por</div>
            <div class="campo-valor"><?= esc2($envio['enviado_por_nombre'] ?? '—') ?></div>
        </div>
        <div class="campo-row">
            <div class="campo-label">Fecha de envío</div>
            <div class="campo-valor"><?= esc2($creado) ?></div>
        </div>
        <?php if ($envio['completado_en']): ?>
        <div class="campo-row">
            <div class="campo-label">Completado el</div>
            <div class="campo-valor"><?= esc2($completado) ?></div>
        </div>
        <?php endif; ?>
        <?php if ($envio['ip_cliente']): ?>
        <div class="campo-row">
            <div class="campo-label">IP del cliente</div>
            <div class="campo-valor text-muted small fw-normal"><?= esc2($envio['ip_cliente']) ?></div>
        </div>
        <?php endif; ?>

        <?php if ($envio['hash_verificacion']): ?>
        <!-- Sello de integridad SHA-256 -->
        <div class="hash-seal mt-4">
            <div class="hash-seal-header">
                <i class="fas fa-shield-alt me-1"></i>
                Sello de integridad del documento
            </div>
            <div class="hash-seal-body">
                <div class="hash-label">Hash SHA-256 de verificación:</div>
                <code class="hash-value"><?= esc2($envio['hash_verificacion']) ?></code>
                <div class="hash-hint mt-2">
                    Verifique la autenticidad en:
                    <a href="verificar_formulario.php?h=<?= urlencode($envio['hash_verificacion']) ?>" target="_blank">
                        <?php
                        $protocol2 = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS']!=='off') ? 'https' : 'http';
                        echo $protocol2.'://'.$_SERVER['HTTP_HOST'].'/verificar_formulario.php?h='.urlencode($envio['hash_verificacion']);
                        ?>
                    </a>
                </div>
            </div>
        </div>
        <?php endif; ?>

        <!-- ── Widget firma turnero (fallback solo si el esquema no tiene NINGÚN campo firma) ── -->
        <?php if ($modoTurnero && $envio['estado'] !== 'firmado' && !$_esquemaTieneFirmaAlguna): ?>
        <div class="mt-4 no-print turnero-firma-item" data-cid="__firma_global">
            <div class="section-title" style="color:#1565c0">
                <i class="fas fa-pen me-1"></i>Firma del paciente / responsable
            </div>
            <p class="text-muted small mb-2">
                He leído y comprendido el contenido de este documento.
                Por favor dibuje su firma en el recuadro:
            </p>
            <canvas class="turnero-cv" width="500" height="160"
                    style="border:2px solid #1565c0;border-radius:8px;background:#f0f4ff;
                           cursor:crosshair;display:block;max-width:100%;touch-action:none">
            </canvas>
            <div class="mt-2 d-flex gap-2 flex-wrap">
                <button class="btn btn-outline-secondary btn-sm turnero-limpiar">
                    <i class="fas fa-eraser me-1"></i>Limpiar
                </button>
                <button class="btn btn-success fw-semibold turnero-firmar">
                    <i class="fas fa-check-circle me-1"></i>Confirmar y firmar
                </button>
            </div>
            <div class="turnero-msg mt-2 small"></div>
        </div>
        <?php elseif ($modoTurnero && $envio['estado'] === 'firmado'): ?>
        <div class="alert alert-success mt-4 d-flex align-items-start gap-3 no-print">
            <i class="fas fa-check-circle fs-4 flex-shrink-0 mt-1"></i>
            <div>
                <strong>Consentimiento firmado</strong><br>
                <span class="text-success-emphasis">El personal de salud ha sido notificado.</span>
                <div class="mt-2 d-flex gap-2 flex-wrap">
                    <button onclick="window.print()" class="btn btn-warning btn-sm fw-semibold">
                        <i class="fas fa-file-pdf me-1"></i>Descargar PDF
                    </button>
                    <button onclick="window.close()" class="btn btn-outline-secondary btn-sm fw-semibold">
                        <i class="fas fa-times me-1"></i>Cerrar ventana
                    </button>
                </div>
            </div>
        </div>
        <?php endif; ?>

    </div><!-- /doc-body -->

    <div class="doc-footer">
        <span>
            <?php if ($docPiePagina): ?>
            <?= esc2($docPiePagina) ?> &nbsp;&bull;&nbsp;
            <?php endif; ?>
            <i class="fas fa-shield-alt me-1"></i>Generado el <?= date('d/m/Y H:i') ?> &nbsp;&bull;&nbsp; ID #<?= $envio['id'] ?>
            <?php if ($numeroOrden): ?>
            &nbsp;&bull;&nbsp; Orden: <strong><?= esc2($numeroOrden) ?></strong>
            <?php endif; ?>
        </span>
        <?php if ($envio['hash_verificacion']): ?>
        <span class="hash-short" title="Hash SHA-256"><?= substr($envio['hash_verificacion'],0,16) ?>...</span>
        <?php endif; ?>
    </div>

</div><!-- /doc-wrap -->

<script>
<?php if (!empty($_mpMap)): ?>
window._muestrasMap = <?= json_encode($_mpMap, JSON_UNESCAPED_UNICODE) ?>;
window._mpSavedFirmas = {};
<?php endif; ?>
/* ── Firma del profesional: función compartida canvas/1-clic ────── */
function _guardarFirmaPro(widget, svg, msgEl) {
    const turnoId      = widget.dataset.turno;
    const formularioId = widget.dataset.formulario;
    const envioId      = parseInt(widget.dataset.envio, 10);
    const campoId      = widget.dataset.campo;
    const _mpEntry = window._muestrasMap && window._muestrasMap[campoId];
    if (_mpEntry) { _guardarFirmaMP(widget, svg, msgEl, _mpEntry, campoId); return; }
    const soloPro      = widget.dataset.soloPro === '1';
    const isTurnero    = !!(turnoId && formularioId);
    const saveUrl      = isTurnero
        ? 'modules/turnero/api/firmar_profesional_consentimiento.php'
        : 'api/lab/firmar_profesional.php';

    var datosRespuestas = {};
    if (soloPro) {
        document.querySelectorAll('[name]').forEach(function(el) {
            var raw = el.name, isArr = raw.slice(-2) === '[]', name = isArr ? raw.slice(0,-2) : raw;
            if (el.type==='checkbox') { if(el.checked){ if(!Array.isArray(datosRespuestas[name])) datosRespuestas[name]=[]; datosRespuestas[name].push(el.value); } }
            else if (el.type==='radio') { if(el.checked) datosRespuestas[name]=el.value; }
            else if (el.value!=='') datosRespuestas[name]=el.value;
        });
    }

    const payload = isTurnero
        ? { turno_id: parseInt(turnoId), formulario_id: parseInt(formularioId), svg,
            ...(soloPro ? { solo_profesional: true, datos_respuestas: datosRespuestas } : {}) }
        : { envio_id: envioId, campo_id: campoId, svg };

    fetch(saveUrl, { method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify(payload) })
    .then(r => r.json())
    .then(function(data) {
        if (data.ok) {
            if (soloPro) {
                document.querySelectorAll('.firma-pro-widget, .campo-edit, .section-title').forEach(el => el.style.display='none');
                var ok = document.createElement('div');
                ok.className = 'alert alert-success mt-4 d-flex align-items-center gap-2 no-print';
                ok.innerHTML = '<i class="fas fa-check-circle fs-4"></i><div><strong>Firmado correctamente.</strong><br>Puede cerrar esta ventana.</div>';
                widget.parentNode.insertBefore(ok, widget.nextSibling);
                widget.style.display = 'none';
                try { window.parent.postMessage({ type: 'turneroFirmado' }, '*'); } catch(e) {}
            } else {
                var img = document.createElement('img');
                img.src = svg; img.alt = 'Firma profesional';
                img.style.cssText = 'max-height:140px;max-width:340px;display:block';
                var box = document.createElement('div');
                box.className = 'firma-box'; box.style.borderColor = '#198754';
                box.appendChild(img);
                widget.replaceWith(box);
            }
        } else {
            if (msgEl) msgEl.innerHTML = '<span class="text-danger"><i class="fas fa-times me-1"></i>' + (data.error||'Error al guardar') + '</span>';
            widget.querySelectorAll('button').forEach(b => { b.disabled=false; });
            var btnOC = widget.querySelector('.fpw-oneclic-btn');
            if (btnOC) btnOC.innerHTML = '<i class="fas fa-check-circle me-2"></i>Firmar consentimiento';
        }
    })
    .catch(function() {
        if (msgEl) msgEl.innerHTML = '<span class="text-danger"><i class="fas fa-times me-1"></i>Error de conexión.</span>';
        widget.querySelectorAll('button').forEach(b => { b.disabled=false; });
    });
}

/* ── Muestras Prolongadas: guardar firma por campo + countdown ───── */
function _guardarFirmaMP(widget, svg, msgEl, entry, campoId) {
    var dr = {};
    document.querySelectorAll('[name]').forEach(function(el) {
        var n = el.name.endsWith('[]') ? el.name.slice(0,-2) : el.name;
        if (el.type==='checkbox'){ if(el.checked){ if(!Array.isArray(dr[n]))dr[n]=[]; dr[n].push(el.value); } }
        else if (el.type==='radio'){ if(el.checked) dr[n]=el.value; }
        else if (el.value!=='') dr[n]=el.value;
    });
    Object.assign(dr, window._mpSavedFirmas || {});
    dr[campoId + '_svg'] = svg;
    (window._mpSavedFirmas = window._mpSavedFirmas||{})[campoId + '_svg'] = svg;

    fetch(window.location.href, {
        method:'POST', headers:{'Content-Type':'application/json'},
        body: JSON.stringify({ datos_respuestas: dr })
    })
    .then(function(r){ return r.json(); })
    .then(function(data) {
        if (!data.ok) {
            if (msgEl) msgEl.innerHTML = '<span class="text-danger">Error al guardar</span>';
            widget.querySelectorAll('button').forEach(function(b){ b.disabled=false; });
            var bc = widget.querySelector('.fpw-oneclic-btn');
            if (bc) bc.innerHTML = '<i class="fas fa-check-circle me-2"></i>Firmar consentimiento';
            return;
        }
        // Mostrar firma guardada en lugar del widget
        var img = document.createElement('img');
        img.src = svg; img.style.cssText = 'max-height:80px;max-width:240px;display:block';
        var box = document.createElement('div');
        box.className = 'firma-box'; box.style.borderColor = '#198754';
        box.appendChild(img);
        widget.replaceWith(box);

        if (!entry.is_last && entry.esperar_min > 0) {
            _mpIniciarCountdown(entry.esperar_min, entry.next_label, entry.next_firma_id);
        } else if (!entry.is_last) {
            var nw = entry.next_firma_id ? document.getElementById('fpw-' + entry.next_firma_id) : document.querySelector('.firma-pro-widget');
            if (nw) nw.scrollIntoView({ behavior:'smooth', block:'center' });
        } else {
            _mpMostrarFinalizar(entry.exam_type);
        }
    })
    .catch(function() {
        if (msgEl) msgEl.innerHTML = '<span class="text-danger">Error de conexión.</span>';
    });
}

function _mpIniciarCountdown(minutos, nextLabel, nextFirmaId) {
    var targetMs = Date.now() + minutos * 60000;
    var box = document.getElementById('mp-countdown');
    if (!box) {
        box = document.createElement('div');
        box.id = 'mp-countdown';
        box.style.cssText = 'position:sticky;bottom:12px;background:#1565c0;color:#fff;padding:10px 16px;border-radius:8px;margin:12px 0;text-align:center;font-size:.9rem;z-index:50';
        document.querySelector('.doc-body').appendChild(box);
    }
    if (box._mpTick) clearInterval(box._mpTick);

    box._mpTick = setInterval(function() {
        var left = targetMs - Date.now();
        if (left <= 0) {
            clearInterval(box._mpTick);
            box.style.background = '#dc3545';
            box.style.animation = 'mp-pulse 1s ease-in-out infinite';
            box.innerHTML = '<strong><i class="fas fa-bell me-2"></i>¡Hora de la siguiente muestra!</strong>'
                + (nextLabel ? '<br><small>' + nextLabel + '</small>' : '');
            if (navigator.vibrate) navigator.vibrate([400,200,400,200,400]);
            var nw = nextFirmaId ? document.getElementById('fpw-' + nextFirmaId) : document.querySelector('.firma-pro-widget');
            if (nw) {
                nw.scrollIntoView({ behavior:'smooth', block:'center' });
                nw.style.outline = '3px solid #dc3545';
                nw.style.borderRadius = '6px';
            }
            return;
        }
        var m = Math.floor(left / 60000);
        var s = Math.floor((left % 60000) / 1000);
        box.innerHTML = '⏱ Próxima muestra'
            + (nextLabel ? ' <small>(' + nextLabel + ')</small>' : '')
            + ' en <strong>' + String(m).padStart(2,'0') + ':' + String(s).padStart(2,'0') + '</strong>';
    }, 1000);
}

function _mpMostrarFinalizar(examType) {
    if (document.getElementById('mp-finalizar')) return;
    var cd = document.getElementById('mp-countdown');
    if (cd) { clearInterval(cd._mpTick); cd.remove(); }
    var wrap = document.createElement('div');
    wrap.id = 'mp-finalizar';
    wrap.style.cssText = 'position:sticky;bottom:12px;margin:12px 0;text-align:center;z-index:50';
    var btn = document.createElement('button');
    btn.className = 'btn btn-success fw-bold px-4 py-2';
    btn.style.fontSize = '1rem';
    btn.innerHTML = '<i class="fas fa-check-double me-2"></i>Finalizar toma · ' + (examType || 'Muestras');
    btn.addEventListener('click', function() {
        btn.disabled = true;
        btn.innerHTML = '<i class="fas fa-spinner fa-spin me-2"></i>Guardando...';
        var dr = {};
        document.querySelectorAll('[name]').forEach(function(el) {
            var n = el.name.endsWith('[]') ? el.name.slice(0,-2) : el.name;
            if (el.type==='checkbox'){ if(el.checked){ if(!Array.isArray(dr[n]))dr[n]=[]; dr[n].push(el.value); } }
            else if (el.type==='radio'){ if(el.checked) dr[n]=el.value; }
            else if (el.value!=='') dr[n]=el.value;
        });
        Object.assign(dr, window._mpSavedFirmas || {});
        fetch(window.location.href, {
            method:'POST', headers:{'Content-Type':'application/json'},
            body: JSON.stringify({ mp_completar: true, datos_respuestas: dr })
        })
        .then(function(r){ return r.json(); })
        .then(function(data) {
            if (data.ok) {
                wrap.innerHTML = '<div class="alert alert-success py-2 mb-0"><i class="fas fa-check-circle me-2"></i>Toma completada correctamente.</div>';
                try { window.parent.postMessage({ type:'turneroFirmado' }, '*'); } catch(e) {}
            }
        })
        .catch(function(){});
    });
    wrap.appendChild(btn);
    document.querySelector('.doc-body').appendChild(wrap);
}

/* ── Canvas firma del profesional ───────────────────────────────── */
(function () {
    document.querySelectorAll('.firma-pro-widget').forEach(function (widget) {
        const canvas  = widget.querySelector('.fpw-canvas');
        const ctx     = canvas.getContext('2d');
        const btnClear = widget.querySelector('.fpw-clear');
        const btnSave  = widget.querySelector('.fpw-save');
        const msg      = widget.querySelector('.fpw-msg');
        const envioId  = parseInt(widget.dataset.envio, 10);
        const campoId  = widget.dataset.campo;
        let drawing = false;

        // Ajustar resolución para HiDPI
        (function scaleCanvas() {
            const ratio = window.devicePixelRatio || 1;
            const w = canvas.offsetWidth  || canvas.width;
            const h = canvas.offsetHeight || canvas.height;
            canvas.width  = w * ratio;
            canvas.height = h * ratio;
            canvas.style.width  = w + 'px';
            canvas.style.height = h + 'px';
            ctx.scale(ratio, ratio);
            ctx.strokeStyle = '#000';
            ctx.lineWidth   = 2;
            ctx.lineCap     = 'round';
            ctx.lineJoin    = 'round';
        })();

        function getPos(e) {
            const r = canvas.getBoundingClientRect();
            const src = e.touches ? e.touches[0] : e;
            return { x: src.clientX - r.left, y: src.clientY - r.top };
        }

        canvas.addEventListener('mousedown',  function(e){ drawing=true; ctx.beginPath(); const p=getPos(e); ctx.moveTo(p.x,p.y); });
        canvas.addEventListener('mousemove',  function(e){ if(!drawing) return; const p=getPos(e); ctx.lineTo(p.x,p.y); ctx.stroke(); });
        canvas.addEventListener('mouseup',    function(){ drawing=false; });
        canvas.addEventListener('mouseleave', function(){ drawing=false; });
        canvas.addEventListener('touchstart', function(e){ e.preventDefault(); drawing=true; ctx.beginPath(); const p=getPos(e); ctx.moveTo(p.x,p.y); }, {passive:false});
        canvas.addEventListener('touchmove',  function(e){ e.preventDefault(); if(!drawing) return; const p=getPos(e); ctx.lineTo(p.x,p.y); ctx.stroke(); }, {passive:false});
        canvas.addEventListener('touchend',   function(){ drawing=false; });

        btnClear.addEventListener('click', function() {
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            msg.textContent = '';
        });

        var btnTopazPro = widget.querySelector('.fpw-topaz');
        if (btnTopazPro) btnTopazPro.addEventListener('click', function() {
            topaz.activar({ canvas: canvas, onAccept: function() {} });
        });

        // ── Botón 1 clic: firma pre-guardada ──────────────────────
        const btnOneClic  = widget.querySelector('.fpw-oneclic-btn');
        const btnUsarOtra = widget.querySelector('.fpw-usar-otra');
        if (btnOneClic) {
            btnOneClic.addEventListener('click', function() {
                const preFirma = window._fpwPreFirma;
                if (!preFirma) return;
                btnOneClic.disabled = true;
                btnOneClic.innerHTML = '<i class="fas fa-spinner fa-spin me-2"></i>Firmando...';
                _guardarFirmaPro(widget, preFirma, msg);
            });
        }
        if (btnUsarOtra) {
            btnUsarOtra.addEventListener('click', function(e) {
                e.preventDefault();
                widget.querySelector('.fpw-oneclic').style.display = 'none';
                widget.querySelector('.fpw-canvas-wrap').style.display = '';
            });
        }

        btnSave.addEventListener('click', function() {
            const svg = canvas.toDataURL('image/png');
            if (svg.length < 1000) {
                msg.innerHTML = '<span class="text-danger"><i class="fas fa-exclamation-triangle me-1"></i>Por favor dibuje su firma antes de guardar.</span>';
                return;
            }
            btnSave.disabled = true;
            btnSave.innerHTML = '<i class="fas fa-spinner fa-spin me-1"></i>Guardando...';
            msg.textContent = '';
            _guardarFirmaPro(widget, svg, msg);
        });
    });
})();

/* ── Firma de consentimiento turnero (soporta múltiples campos firma) ── */
document.querySelectorAll('.turnero-firma-item').forEach(function(widget) {
    var canvas  = widget.querySelector('.turnero-cv');
    if (!canvas) return;
    var cid     = widget.dataset.cid || '__firma';
    var ctx     = canvas.getContext('2d');
    var btnLimp = widget.querySelector('.turnero-limpiar');
    var btnFirm = widget.querySelector('.turnero-firmar');
    var msgEl   = widget.querySelector('.turnero-msg');
    var drawing = false;
    var btnLabel = btnFirm ? btnFirm.innerHTML : '';

    (function scaleCanvas() {
        var ratio = window.devicePixelRatio || 1;
        var w = canvas.offsetWidth  || canvas.width;
        var h = canvas.offsetHeight || canvas.height;
        canvas.width  = w * ratio;
        canvas.height = h * ratio;
        canvas.style.width  = w + 'px';
        canvas.style.height = h + 'px';
        ctx.scale(ratio, ratio);
        ctx.strokeStyle = '#1565c0';
        ctx.lineWidth   = 2.5;
        ctx.lineCap     = 'round';
        ctx.lineJoin    = 'round';
    })();

    function getPos(e) {
        var r   = canvas.getBoundingClientRect();
        var src = e.touches ? e.touches[0] : e;
        return { x: src.clientX - r.left, y: src.clientY - r.top };
    }

    canvas.addEventListener('mousedown',  function(e){ drawing=true; ctx.beginPath(); var p=getPos(e); ctx.moveTo(p.x,p.y); });
    canvas.addEventListener('mousemove',  function(e){ if(!drawing) return; var p=getPos(e); ctx.lineTo(p.x,p.y); ctx.stroke(); });
    canvas.addEventListener('mouseup',    function(){ drawing=false; });
    canvas.addEventListener('mouseleave', function(){ drawing=false; });
    canvas.addEventListener('touchstart', function(e){ e.preventDefault(); drawing=true; ctx.beginPath(); var p=getPos(e); ctx.moveTo(p.x,p.y); }, {passive:false});
    canvas.addEventListener('touchmove',  function(e){ e.preventDefault(); if(!drawing) return; var p=getPos(e); ctx.lineTo(p.x,p.y); ctx.stroke(); }, {passive:false});
    canvas.addEventListener('touchend',   function(){ drawing=false; });

    if (btnLimp) btnLimp.addEventListener('click', function() {
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        msgEl.textContent = '';
    });

    var btnTopazPac = widget.querySelector('.turnero-topaz');
    if (btnTopazPac) btnTopazPac.addEventListener('click', function() {
        topaz.activar({ canvas: canvas, onAccept: function() {} });
    });

    if (btnFirm) btnFirm.addEventListener('click', function() {
        var png = canvas.toDataURL('image/png');
        if (png.length < 1500) {
            msgEl.innerHTML = '<span class="text-danger"><i class="fas fa-exclamation-triangle me-1"></i>Por favor dibuje su firma antes de confirmar.</span>';
            return;
        }
        btnFirm.disabled = true;
        btnFirm.innerHTML = '<i class="fas fa-spinner fa-spin me-1"></i>Guardando...';
        msgEl.textContent = '';

        var campos = {};
        document.querySelectorAll('[name]').forEach(function(el) {
            var rawName = el.name;
            var isArr   = rawName.slice(-2) === '[]';
            var name    = isArr ? rawName.slice(0, -2) : rawName;
            if (el.type === 'checkbox') {
                if (el.checked) { if (!Array.isArray(campos[name])) campos[name] = []; campos[name].push(el.value); }
            } else if (el.type === 'radio') {
                if (el.checked) campos[name] = el.value;
            } else if (el.value !== '') {
                campos[name] = el.value;
            }
        });
        campos[cid + '_svg'] = png; // identificar qué campo firmó

        fetch(window.location.href, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ firma_svg: png, datos_respuestas: campos })
        })
        .then(function(r) { return r.json(); })
        .then(function(data) {
            if (data.ok) {
                // Ocultar todos los demás widgets de firma (ya no se puede firmar dos veces)
                document.querySelectorAll('.turnero-firma-item').forEach(function(w) {
                    w.style.display = 'none';
                });
                var ok = document.createElement('div');
                ok.className = 'alert alert-success mt-4 d-flex align-items-start gap-3 no-print';
                var _enIframe = (function(){ try { return window.self !== window.top; } catch(e){ return true; } })();
                ok.innerHTML = '<i class="fas fa-check-circle fs-4 flex-shrink-0 mt-1"></i>'
                    + '<div><strong>Firmado correctamente.</strong><br>'
                    + '<span>El personal de salud ha sido notificado.</span>'
                    + '<div class="mt-2 d-flex gap-2 flex-wrap">'
                    + '<button onclick="window.print()" class="btn btn-warning btn-sm fw-semibold">'
                    + '<i class=\"fas fa-file-pdf me-1\"></i>Descargar PDF</button>'
                    + (!_enIframe ? '<button onclick="window.close()" class="btn btn-outline-secondary btn-sm fw-semibold">'
                    + '<i class=\"fas fa-times me-1\"></i>Cerrar</button>' : '')
                    + '</div></div>';
                widget.parentNode.insertBefore(ok, widget.nextSibling);
                try { window.parent.postMessage({ type: 'turneroFirmado' }, '*'); } catch(e) {}
            } else {
                msgEl.innerHTML = '<span class="text-danger"><i class="fas fa-times me-1"></i>' + (data.error || 'Error al guardar') + '</span>';
                btnFirm.disabled = false;
                btnFirm.innerHTML = btnLabel;
            }
        })
        .catch(function() {
            msgEl.innerHTML = '<span class="text-danger"><i class="fas fa-times me-1"></i>Error de conexión.</span>';
            btnFirm.disabled = false;
            btnFirm.innerHTML = btnLabel;
        });
    });
});
</script>

<!-- ── Topaz SigWeb overlay ───────────────────────────────────────── -->
<div id="topaz-overlay" style="display:none">
    <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">
                <i class="fas fa-signature fa-2x" style="color:#adb5bd" id="topaz-pad-icon"></i>
                <div id="topaz-status-msg" style="font-size:.9rem;color:#64748b;font-weight:600">
                    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>

<script>
// ── Topaz SigWeb ────────────────────────────────────────────────────
const topaz = (() => {
    const SIGWEB_URL = 'http://localhost:47289/SigWeb/SigWebTablet.js';
    let _loaded = false, _ctx = null, _target = null, _poll = null;
    // _target: { type: 'turnero'|'pro', canvas: CanvasElement, onAccept: fn }

    function _call(fn, ...args) {
        if (_ctx && typeof _ctx[fn] === 'function') return _ctx[fn](...args);
        const g = window[fn];
        if (typeof g === 'function') return _ctx ? g(...args, _ctx) : g(...args);
        throw new Error('SigWeb: ' + fn + ' no encontrado');
    }

    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 = document.getElementById('topaz-status-msg');
        if (el) { el.textContent = msg; el.style.color = ok ? '#198754' : '#64748b'; }
        const area = document.getElementById('topaz-pad-area');
        if (area) area.classList.toggle('has-sig', !!ok);
        const icon = document.getElementById('topaz-pad-icon');
        if (icon) icon.style.color = ok ? '#198754' : '#adb5bd';
    }

    async function activar(target) {
        const ok = await _loadScript();
        if (!ok) {
            alert('No se detectó SigWeb.\nInstala el servicio Topaz SigWeb y vuelve a intentarlo.');
            return;
        }
        _target = target;
        try {
            _ctx = typeof SigWebTablet !== 'undefined' ? new SigWebTablet() : null;
            _call('SetImageXSize', 500);
            _call('SetImageYSize', 150);
            _call('SetImagePenWidth', 3);
            _call('SetTabletState', 1);
            _call('ClearTablet');
        } catch(e) {
            alert('Error al activar el pad: ' + e.message); return;
        }
        document.getElementById('topaz-overlay').style.display = 'flex';
        document.getElementById('topaz-btn-aceptar').disabled = true;
        document.getElementById('topaz-pts').textContent = '0';
        _setStatus('Firme en el pad biométrico', false);

        _poll = setInterval(() => {
            try {
                const pts = _call('GetSigTotalPoints');
                document.getElementById('topaz-pts').textContent = pts;
                document.getElementById('topaz-btn-aceptar').disabled = pts === 0;
                if (pts > 0) _setStatus('✅ Firma detectada — presione Aceptar', true);
            } catch(e) { _stopPoll(); }
        }, 400);
    }

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

    function _cerrar() {
        _stopPoll();
        try { if (_ctx) _call('SetTabletState', 0); } catch(e) {}
        _ctx = null; _target = null;
        document.getElementById('topaz-overlay').style.display = 'none';
    }

    function cancelar() { _cerrar(); }

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

    function aceptar() {
        let b64;
        try { b64 = _call('GetSigImageB64'); } catch(e) {
            alert('Error al capturar la firma: ' + e.message); _cerrar(); return;
        }
        if (!b64) { alert('No se capturó ninguna firma.'); return; }
        const target = _target;
        _cerrar();
        const img = new Image();
        img.onload = () => {
            const c   = target.canvas;
            const ctx = c.getContext('2d');
            ctx.clearRect(0, 0, c.width, c.height);
            ctx.drawImage(img, 0, 0, c.width, c.height);
            c.classList.add('has-sig');
            if (typeof target.onAccept === 'function') target.onAccept();
        };
        img.src = 'data:image/png;base64,' + b64;
    }

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

<?php if ($modoTurnero && $modoEditar): ?>
// ── Auto-save borrador (campo a campo, sin firma) ─────────────
(function() {
    var _t;
    function leerCampos() {
        var c = {};
        document.querySelectorAll('[name]').forEach(function(el) {
            var n = el.name.endsWith('[]') ? el.name.slice(0, -2) : el.name;
            if (el.type === 'checkbox') {
                if (el.checked) { if (!Array.isArray(c[n])) c[n] = []; c[n].push(el.value); }
            } else if (el.type === 'radio') {
                if (el.checked) c[n] = el.value;
            } else if (el.value !== '') {
                c[n] = el.value;
            }
        });
        return c;
    }
    function guardar() {
        fetch(window.location.href, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ datos_respuestas: leerCampos() })
        }).catch(function() {});
    }
    document.addEventListener('input',  function() { clearTimeout(_t); _t = setTimeout(guardar, 1200); });
    document.addEventListener('change', function() { clearTimeout(_t); _t = setTimeout(guardar,  400); });
})();
<?php endif; ?>
</script>
</body>
</html>
