<?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,
                tc.siguiente_toma_at, tc.toma_inicio_at,
                f.nombre AS form_nombre, f.categoria, f.descripcion AS form_descripcion,
                f.esquema, f.es_toma_progresiva, f.solo_profesional, f.doc_encabezado, f.doc_subtitulo, f.doc_logo_base64, f.doc_color, f.doc_pie_pagina,
                p.id AS paciente_id,
                p.nombre_completo AS paciente_nombre,
                p.numero_documento, p.tipo_documento,
                p.fecha_nacimiento, p.telefono AS paciente_telefono, p.eps, p.direccion, p.genero,
                t.sesion_id,
                ts.numero_orden,
                med.nombres AS medico_nombres, med.apellidos AS medico_apellidos,
                med.cod_especialidad AS medico_especialidad,
                med.codigo AS medico_codigo, med.docidmedico AS medico_docid,
                cu.full_name AS creador_nombre
         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)
         LEFT   JOIN medicos med ON med.id = ts.medico_id
         LEFT   JOIN admin_users cu ON cu.id = tc.creado_por
         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 (in_array($tcRow['estado'], ['firmado', 'cerrado_anticipado'])) {
            echo json_encode(['ok' => false, 'error' => 'Este consentimiento ya fue finalizado']); exit;
        }
        $input    = json_decode(file_get_contents('php://input'), true) ?? [];
        // Cierre anticipado de toma progresiva con justificación
        if (isset($input['mp_cerrar_anticipado'])) {
            $motivo = trim($input['motivo'] ?? '');
            if (!$motivo) { echo json_encode(['ok' => false, 'error' => 'Motivo requerido']); exit; }
            $existing = json_decode($tcRow['datos_respuestas'] ?? '{}', true) ?: [];
            $existing['_cierre_anticipado'] = ['motivo' => $motivo, 'cerrado_at' => date('Y-m-d H:i:s')];
            $db->getConnection()
               ->prepare("UPDATE turnero_consentimientos SET estado='cerrado_anticipado', firmado_at=NOW(), datos_respuestas=? WHERE token=?")
               ->execute([json_encode($existing, JSON_UNESCAPED_UNICODE), $tokenTurnero]);
            require_once __DIR__ . '/modules/turnero/api/_helpers.php';
            notificarSSE((int)$tcRow['sesion_id']);
            echo json_encode(['ok' => true, 'mp_cerrado' => true]); exit;
        }
        // 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) ?: [];
            $allowSave  = $tcRow['estado'] === 'pendiente'
                       || ($modoTurnero && in_array($tcRow['estado'], ['en_progreso', 'enviado']));
            if ($allowSave) {
                $db->getConnection()
                   ->prepare("UPDATE turnero_consentimientos SET datos_respuestas=? WHERE token=?")
                   ->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'=> $tcRow['creador_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']    ?? '',
            'fecha_nacimiento'   => $tcRow['fecha_nacimiento']  ?? '',
            'telefono'           => $tcRow['paciente_telefono'] ?? '',
            'eps'                => $tcRow['eps']               ?? '',
            'direccion'          => $tcRow['direccion']         ?? '',
            'genero'             => $tcRow['genero']            ?? '',
            'medico_nombre'      => trim(($tcRow['medico_nombres'] ?? '') . ' ' . ($tcRow['medico_apellidos'] ?? '')),
            'medico_especialidad'=> $tcRow['medico_especialidad'] ?? '',
            'medico_codigo'      => $tcRow['medico_codigo']       ?? '',
            'medico_docid'       => $tcRow['medico_docid']        ?? '',
        ]]),
        '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, p.direccion, p.genero,
                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, p.direccion, p.genero,
                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 && !in_array($envio['estado'], ['firmado', 'cerrado_anticipado']);
$embebido       = isset($_GET['embed']) && $_GET['embed'] === '1';
$compact        = $embebido && isset($_GET['compact']);
$autoprint      = isset($_GET['autoprint']) && $_GET['autoprint'] === '1';

// Firma pre-guardada del profesional logueado (para botón de 1 clic)
// Aplica tanto al turnero embebido como a la firma directa fuera del turnero
// (staff logueado viendo/firmando un formulario por ?id=), para que el
// nombre y cédula del firmante salgan en cualquier formato.
$firmaProfPreguardada = null;
$profNombre    = null;
$profDocumento = null;
if ((($modoTurnero && $embebido) || (!$modoTurnero && !$modoPublico)) && isUserLoggedIn()) {
    $uid = (int)($_SESSION['admin_user']['id'] ?? 0);
    if ($uid) {
        try {
            $stmt = $db->getConnection()->prepare(
                "SELECT au.firma_svg, au.full_name, au.cedula AS au_cedula,
                        le.nombre_completo AS pro_nombre, le.numero_documento AS pro_documento
                   FROM admin_users au
                   LEFT JOIN lab_enfermeras le ON le.id = au.enfermera_id
                  WHERE au.id = ? LIMIT 1"
            );
            $stmt->execute([$uid]);
            $pRow = $stmt->fetch(PDO::FETCH_ASSOC) ?: [];
            $firmaProfPreguardada = $pRow['firma_svg'] ?: null;
            $profNombre    = $pRow['pro_nombre']    ?: ($pRow['full_name'] ?: null);
            $profDocumento = $pRow['au_cedula']     ?: ($pRow['pro_documento'] ?: null);
        } catch (\Throwable $_) {}
    }
}
// Formulario marcado explícitamente como solo-profesional (sin firma de ningún tipo)
$_formSoloPro   = $modoTurnero && $embebido && !empty($tcRow['solo_profesional']);
// Pre-scan: formulario que solo requiere firma del profesional (sin firma paciente)
$_soloFirmaPro  = ($modoTurnero && !empty($tcRow['es_toma_progresiva']))
               || (!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 condición del separador para no mezclar Glicemia y Cortisol (ambas usan prefijo "Toma").
$_mpMap          = [];
$_mpGroupsFull   = []; // grupos sin filtrar (para la pantalla de configuración)
$_mpSkippedSepIds = []; // separadores de tomas no seleccionadas (se ocultarán en render)
$_tomasConfig    = null; // configuración guardada por paciente ({grupo:[firmaId,...]})

if ($modoTurnero && $embebido && $_soloFirmaPro) {
    $_mpSecs = []; $_mpLabel = null; $_mpMin = null; $_mpHoraFija = null; $_mpHora = null;
    $_mpCondKey = null; $_mpCondDisplay = null; $_mpSepId = null;
    foreach ($esquema as $_c) {
        $_t = $_c['tipo'] ?? '';
        if ($_t === 'separador') {
            $_mpLabel = $_c['label'] ?? ''; $_mpMin = null; $_mpHoraFija = null; $_mpHora = null;
            $_mpSepId = $_c['id'] ?? null;
            $_mpCond  = $_c['condicion'] ?? null;
            // Clave de agrupación: usa valores de condición para distinguir Glicemia vs Cortisol
            if ($_mpCond) {
                $_cv = $_mpCond['valores'] ?? ($_mpCond['valor'] ? [$_mpCond['valor']] : []);
                $_mpCondKey     = implode('|', $_cv);
                $_mpCondDisplay = trim(explode('·', $_mpLabel)[0] ?? '');
            } else {
                $_mpCondKey = $_mpCondDisplay = trim(explode('·', $_mpLabel)[0] ?? '');
            }
            if (preg_match('/[Mm]inuto\s+(\d+)/u', $_mpLabel, $_mx)) {
                $_mpMin = (int)$_mx[1];
            } elseif (preg_match('/(\d{1,2}):(\d{2})\s*(a\.?m\.?|p\.?m\.?)/i', $_mpLabel, $_mx)) {
                $_h = (int)$_mx[1]; $_m2 = (int)$_mx[2];
                $_pm = strtolower(preg_replace('/[^apm]/i', '', $_mx[3])) === 'pm';
                if ($_pm && $_h < 12) $_h += 12;
                $_mpHoraFija = sprintf('%02d:%02d', $_h, $_m2);
                $_mpMin = PHP_INT_MAX; // sentinel: va después de todos los Minuto X
            } elseif (stripos($_mpLabel, 'Otro') !== false) {
                // Si el usuario ingresó la hora de "Otro", usarla para ordenar cronológicamente
                $_otroValor = trim((string)($datosCliente['_otro_hora'] ?? ''));
                $_mpMin = is_numeric($_otroValor) ? (int)$_otroValor : PHP_INT_MAX;
            }
        } elseif ($_t === 'hora' && ($_mpMin !== null || $_mpHoraFija !== null)) {
            $_mpHora = $_c['id'] ?? null;
        } elseif ($_t === 'firma_profesional' && $_mpHora !== null && ($_mpMin !== null || $_mpHoraFija !== null)) {
            $_mpSecs[] = [
                'label'     => $_mpLabel,
                'sep_id'    => $_mpSepId,
                'min'       => $_mpMin ?? PHP_INT_MAX,
                'hora_fija' => $_mpHoraFija,
                'hora'      => $_mpHora,
                'firma'     => $_c['id'],
                'tipo'      => $_mpCondKey,
                'display'   => $_mpCondDisplay,
            ];
            $_mpHora = null;
        }
    }
    // Ordenar por tiempo antes de agrupar para garantizar orden cronológico
    usort($_mpSecs, fn($a, $b) => $a['min'] <=> $b['min']);
    // Mapa sep_id → minuto para re-ordenar secciones en el render
    $_mpSepOrder = [];
    foreach ($_mpSecs as $_s) { $_mpSepOrder[$_s['sep_id']] = $_s['min']; }
    $_mpGroups = [];
    foreach ($_mpSecs as $_s) { $_mpGroups[$_s['tipo']][] = $_s; }
    $_mpGroupsFull = $_mpGroups;

    // Aplicar filtro de ciclos si el profesional ya configuró las tomas para este paciente
    $__rawCfg = $datosCliente['_tomas_config'] ?? null;
    if (is_array($__rawCfg)) {
        $_tomasConfig = $__rawCfg; // exported to JS as-is (nested format)
        // Flatten nested { examName: { groupKey: [fids] } } → { groupKey: [fids] } for rendering
        $__first = reset($__rawCfg);
        $__isNested = is_array($__first) && !empty($__first) && is_array(reset($__first));
        $__flatCfg = [];
        if ($__isNested) {
            foreach ($__rawCfg as $__examCfg) {
                if (!is_array($__examCfg)) continue;
                foreach ($__examCfg as $__gk => $__fids) {
                    foreach ($__fids as $__fid) $__flatCfg[$__gk][] = $__fid;
                }
            }
            foreach ($__flatCfg as &$__v) $__v = array_values(array_unique($__v));
            unset($__v);
        } else {
            $__flatCfg = $__rawCfg;
        }
        foreach ($_mpGroups as $__gk => &$__gsecs) {
            if (!isset($__flatCfg[$__gk])) {
                foreach ($__gsecs as $__s) { $_mpSkippedSepIds[$__s['sep_id']] = true; }
                $__gsecs = [];
                continue;
            }
            $__allowed = array_flip($__flatCfg[$__gk]);
            foreach ($__gsecs as $__s) {
                if (!isset($__allowed[$__s['firma']])) {
                    $_mpSkippedSepIds[$__s['sep_id']] = true;
                }
            }
            $__gsecs = array_values(array_filter($__gsecs, fn($s) => isset($__allowed[$s['firma']])));
        }
        unset($__gsecs);
    }

    // Construir $_mpMap desde grupos (posiblemente filtrados)
    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']] = [
                'label'         => $_s['label'],
                'hora_campo'    => $_s['hora'],
                'next_label'    => $_nx ? $_nx['label'] : null,
                'next_firma_id' => $_nx ? $_nx['firma'] : null,
                'is_last'       => !$_nx,
                'exam_type'     => $_s['display'],
            ];
        }
    }
}

// Primera firma pendiente en toma progresiva (las demás se ocultan hasta su turno)
$_mpPrimeraPendiente = null;
if (!empty($_mpMap)) {
    foreach ($_mpMap as $_fid => $_) {
        if (empty($datosCliente[$_fid]) && empty($datosCliente[$_fid . '_svg'])) {
            $_mpPrimeraPendiente = $_fid;
            break;
        }
    }
}

// Mapa firma_id → obs_campo_id (campo texto/textarea que acompaña cada firma)
$_mpFirmaObs = [];
if (!empty($esquema) && !empty($_mpMap)) {
    $_prevFirma = null; $_pendingObs = null;
    foreach ($esquema as $_oc) {
        $_ot = $_oc['tipo'] ?? '';
        if ($_ot === 'separador') {
            $_prevFirma = null; $_pendingObs = null;
        } elseif (($_ot === 'texto' || $_ot === 'textarea') && isset($_oc['id'])) {
            if ($_prevFirma !== null) {
                $_mpFirmaObs[$_prevFirma] = $_oc['id'];
                $_prevFirma = null;
            } else {
                $_pendingObs = $_oc['id'];
            }
        } elseif ($_ot === 'firma_profesional' && isset($_oc['id']) && isset($_mpMap[$_oc['id']])) {
            if ($_pendingObs !== null) { $_mpFirmaObs[$_oc['id']] = $_pendingObs; }
            $_prevFirma = $_oc['id']; $_pendingObs = null;
        } elseif (!in_array($_ot, ['hora'])) {
            $_prevFirma = null;
        }
    }
    foreach ($_mpFirmaObs as $_fid => $_obsId) {
        if (isset($_mpMap[$_fid])) $_mpMap[$_fid]['obs_campo'] = $_obsId;
    }
}

// Horas de tomas ya firmadas → read-only en edición (no se pueden corregir si ya se firmó esa toma)
$_signedHoraCampos = [];
foreach ($_mpMap as $_fid => $_mpe) {
    if ((!empty($datosCliente[$_fid]) && strlen($datosCliente[$_fid]) > 10)
     || (!empty($datosCliente[$_fid . '_svg']) && strlen($datosCliente[$_fid . '_svg']) > 10)) {
        if (!empty($_mpe['hora_campo'])) $_signedHoraCampos[$_mpe['hora_campo']] = true;
    }
}
$_hcToFirmaIds = [];
foreach ($_mpMap as $_fid => $_mpe) {
    if (!empty($_mpe['hora_campo'])) $_hcToFirmaIds[$_mpe['hora_campo']][] = $_fid;
}

// siguiente_toma_at para recuperar countdown si el modal fue cerrado y reabierto
$_mpSiguienteTomAt = null;
if ($modoTurnero && $embebido && !empty($_mpMap) && $_mpPrimeraPendiente !== null) {
    $raw = $tcRow['siguiente_toma_at'] ?? null;
    if ($raw && $raw > date('Y-m-d H:i:s')) {
        $_mpSiguienteTomAt = $raw;
    }
}

// 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);
}

function _addExamWizardHtml(string $cid): string {
    return '
    <div id="mp-exam-badges" class="mp-exam-badges"></div>
    <div class="mt-2" id="aew-wrap-' . $cid . '">
        <button type="button" class="btn btn-link btn-sm p-0 text-muted" onclick="aewOpen(\'' . $cid . '\')">
            <i class="fas fa-plus-circle me-1"></i>Añadir tipo de examen…
        </button>
        <div id="aew-box-' . $cid . '" class="d-none mt-2 border rounded p-3 bg-light" style="max-width:480px">
            <!-- Paso 1: nombre -->
            <div id="aew-s1-' . $cid . '">
                <div class="fw-semibold small mb-2"><i class="fas fa-vials me-1 text-primary"></i>Nuevo tipo de examen</div>
                <div class="mb-2">
                    <label class="form-label small mb-1">Nombre del examen</label>
                    <input type="text" id="aew-name-' . $cid . '" class="form-control form-control-sm"
                           placeholder="Ej: Curva de Insulina" maxlength="80">
                </div>
                <div class="mb-2">
                    <label class="form-label small mb-1">¿Cuántas tomas?</label>
                    <div class="d-flex align-items-center gap-2">
                        <button type="button" class="btn btn-outline-secondary btn-sm" onclick="aewAddToma(\'' . $cid . '\',-1)"><i class="fas fa-minus"></i></button>
                        <span id="aew-count-' . $cid . '" class="fw-bold" style="min-width:20px;text-align:center">1</span>
                        <button type="button" class="btn btn-outline-secondary btn-sm" onclick="aewAddToma(\'' . $cid . '\',1)"><i class="fas fa-plus"></i></button>
                    </div>
                </div>
                <div id="aew-tomas-' . $cid . '" class="mb-2"></div>
                <div class="d-flex gap-2">
                    <button type="button" class="btn btn-success btn-sm" id="aew-save-' . $cid . '"
                            onclick="aewSave(\'' . $cid . '\')">
                        <i class="fas fa-check me-1"></i>Crear examen
                    </button>
                    <button type="button" class="btn btn-outline-secondary btn-sm" onclick="aewClose(\'' . $cid . '\')">
                        Cancelar
                    </button>
                </div>
                <div id="aew-msg-' . $cid . '" class="small mt-2"></div>
            </div>
        </div>
    </div>';
}
?>
<!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 (3 columnas para inputs cortos) ── */
        @media (min-width: 480px) {
            .campos-grid { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 3px 10px; align-items: start; }
            .campos-grid .campo-full { grid-column: span 3; }
            /* ponytail: :has() override — 4-col only when campo-cuarto present */
            .campos-grid:has(.campo-cuarto) { grid-template-columns: 1fr 1fr 1fr 1fr; }
            .campos-grid:has(.campo-cuarto) .campo-full { grid-column: span 4; }
        }
        .campo-edit          { margin-bottom: 3px !important; }
        .esquema-sep         { margin: 8px 0 4px !important; }
        .section-title       { margin: 6px 0 4px !important; }
        .section-title:first-child { margin-top: 0 !important; }
        /* ── Checkboxes y radios con área táctil mayor ── */
        .campo-edit .form-check {
            padding-left: 2.2em;
            min-height: 2.2rem;
            display: flex;
            align-items: center;
            gap: 0;
        }
        /* radios en fila: override display:flex → inline-flex */
        .campo-edit .d-flex .form-check {
            display: inline-flex;
            width: auto;
        }
        .campo-edit .form-check-input {
            width: 1.35em;
            height: 1.35em;
            margin-top: 0;
            margin-left: -2.2em;
            cursor: pointer;
            flex-shrink: 0;
        }
        .campo-edit .form-check-label {
            font-size: 15px;
            line-height: 1.4;
            padding-left: 0.5em;
            cursor: pointer;
        }
        <?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; }
        /* Antes era solo un lápiz gris de 11px: estaba, pero nadie lo encontraba.
           Lleva texto y contorno para que se lea como lo que es, un botón. */
        .lnk-corregir { background:none; border:none; border-radius:4px;
                        color:#1565c0; cursor:pointer; padding:1px 5px; font-size:10px;
                        font-weight:600; white-space:nowrap; flex-shrink:0;
                        display:inline-flex; align-items:center; gap:3px;
                        text-decoration:underline; }
        .lnk-corregir:hover { background:#1565c0; border-color:#1565c0; color:#fff; }
        .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} }

        /* ── Panel configuración de ciclos ─────────────────── */
        .mp-cfg-overlay { position:fixed;inset:0;z-index:9999;background:rgba(0,0,0,.45);
                          display:flex;align-items:flex-start;justify-content:center;
                          padding:1rem;overflow-y:auto; }
        .mp-cfg-card    { background:#fff;border-radius:14px;max-width:500px;width:100%;
                          box-shadow:0 12px 48px rgba(0,0,0,.3);margin-top:.5rem; }
        .mp-cfg-head    { background:<?= htmlspecialchars($docColor) ?>;color:#fff;
                          padding:1rem 1.25rem;border-radius:14px 14px 0 0;
                          font-size:1rem;font-weight:700; }
        .mp-cfg-body    { padding:1.25rem; }
        .mp-cfg-foot    { padding:0 1.25rem 1.25rem; }
        .mp-chips { display:flex;flex-wrap:wrap;gap:.4rem;padding:.25rem 0; }
        .mp-chip-grp { display:inline-flex;align-items:center; }
        .mp-chip  { padding:.3rem .75rem;border:1.5px solid #cbd5e1;border-radius:2rem;
                    background:#f8fafc;color:#475569;font-size:.82rem;cursor:pointer;transition:all .15s;line-height:1.4; }
        .mp-chip.active { background:#1e3a5f;border-color:#1e3a5f;color:#fff;font-weight:600; }
        .mp-chip.signed { background:#f0fdf4;border-color:#86efac;color:#166534;cursor:default; }
        .mp-chip:not(.signed):not(:disabled):hover { border-color:#1e3a5f; }
        .mp-chip-otro-inp { display:none;width:72px;vertical-align:middle;margin-left:.3rem; }
        .mp-chip-otro-unit { vertical-align:middle;margin-left:.2rem;font-size:.8rem;color:#64748b; }
        .mp-cfg-gtitle  { font-weight:700;font-size:.88rem;color:#1e3a5f;margin-bottom:.5rem;
                          border-left:3px solid <?= htmlspecialchars($docColor) ?>;padding-left:.5rem; }
        .mp-cfg-item    { border:1.5px solid #e5e7eb;border-radius:8px;padding:.35rem .6rem;
                          transition:all .15s;cursor:pointer;user-select:none; }
        .mp-cfg-item:has(.form-check-input:checked) { background:#f0fdf4;border-color:#86efac; }
        .mp-cfg-item label { cursor:pointer; }
        /* ── Badges de examen seleccionado ─────────────────── */
        .mp-exam-badges { margin-top:.6rem; display:flex; flex-direction:column; gap:.4rem; }
        .mp-exam-badge  { display:flex;align-items:center;gap:.5rem;flex-wrap:wrap;
                          background:#f0f9ff;border:1.5px solid #bae6fd;border-radius:8px;
                          padding:.35rem .7rem;font-size:.82rem; }
        .mp-exam-badge.configured { background:#f0fdf4;border-color:#86efac; }
        .mp-exam-badge-name { font-weight:700;color:#1e3a5f; }
        .mp-exam-badge-info { color:#64748b;font-size:.78rem; }
        .mp-exam-badge-btn  { margin-left:auto;background:<?= htmlspecialchars($docColor) ?>;color:#fff;
                              border:none;border-radius:6px;padding:2px 10px;font-size:.78rem;
                              cursor:pointer;white-space:nowrap; }
        .mp-exam-badge-btn:hover { opacity:.85; }

        /* ── Tarjetas por toma ──────────────────────────────── */
        :root { --mp-color: <?= htmlspecialchars($docColor) ?>; }
        .toma-card { border-radius:10px;border:2px solid #e2e8f0;margin-bottom:.6rem;overflow:hidden;transition:border-color .2s,box-shadow .2s; }
        .toma-card--signed { border-color:#86efac; }
        .toma-card--active { border-color:var(--mp-color);box-shadow:0 0 0 3px color-mix(in srgb,var(--mp-color) 20%,transparent);animation:tc-pulse 2.5s ease-in-out infinite; }
        .toma-card--locked { border-color:#e2e8f0;opacity:.6; }
        @keyframes tc-pulse { 0%,100%{box-shadow:0 0 0 3px color-mix(in srgb,var(--mp-color) 15%,transparent)} 50%{box-shadow:0 0 0 6px color-mix(in srgb,var(--mp-color) 25%,transparent)} }
        .toma-card-hdr { display:flex;align-items:center;gap:.45rem;padding:.5rem .85rem;font-size:.82rem;flex-wrap:wrap; }
        .toma-card--signed .toma-card-hdr { background:#f0fdf4;cursor:pointer; }
        .toma-card--active .toma-card-hdr { background:var(--mp-color);color:#fff; }
        .toma-card--locked .toma-card-hdr { background:#f8fafc;color:#94a3b8; }
        .toma-card-num { width:1.4rem;height:1.4rem;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:.7rem;font-weight:800;flex-shrink:0;background:rgba(0,0,0,.1); }
        .toma-card--active .toma-card-num { background:rgba(255,255,255,.2);color:#fff; }
        .toma-card--signed .toma-card-num { background:#bbf7d0;color:#166534; }
        .toma-card--locked .toma-card-num { background:#e2e8f0;color:#94a3b8; }
        .toma-card-lbl  { font-weight:700;flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap; }
        .toma-card-hora { font-size:.75rem;opacity:.75;white-space:nowrap; }
        .toma-card-obs  { font-size:.72rem;opacity:.65;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:120px; }
        .toma-card-badge { font-size:.7rem;padding:2px 8px;border-radius:20px;white-space:nowrap;flex-shrink:0; }
        .toma-badge-done   { background:#dcfce7;color:#166534; }
        .toma-badge-active { background:rgba(255,255,255,.2);color:#fff;border:1px solid rgba(255,255,255,.35); }
        .toma-badge-wait   { background:#f1f5f9;color:#64748b; }
        .toma-card-cd  { font-size:.72rem;opacity:.9;margin-left:auto;white-space:nowrap; }
        .toma-card-body { padding:.7rem .85rem;display:flex;flex-direction:column;gap:.5rem; }
        .toma-card--signed   .toma-card-body { display:none; }
        .toma-card--signed.tc-expanded .toma-card-body { display:flex; }
        .toma-card-body .esquema-sep { display:none !important; }
        .toma-card-body .campo-edit,.toma-card-body .campo-full { display:block !important; }

        /* ── Barra de progreso ──────────────────────────────── */
        .mp-progress-wrap { margin-bottom:.75rem; }
        .mp-prog-hdr  { display:flex;align-items:center;justify-content:space-between;margin-bottom:.35rem; }
        .mp-prog-text { font-size:.78rem;color:#475569; }
        .mp-steps     { display:flex;gap:.3rem;align-items:center; }
        .mp-step      { width:9px;height:9px;border-radius:50%;transition:all .3s; }
        .mp-step--done { background:#22c55e; }
        .mp-step--act  { background:var(--mp-color);box-shadow:0 0 0 2px color-mix(in srgb,var(--mp-color) 30%,transparent);width:11px;height:11px; }
        .mp-step--wait { background:#e2e8f0; }
        .mp-prog-bar  { height:5px;background:#e2e8f0;border-radius:3px;overflow:hidden; }
        .mp-prog-fill { height:100%;background:linear-gradient(90deg,var(--mp-color),#22c55e);border-radius:3px;transition:width .5s ease; }
    </style>
</head>
<?php $zoom = isset($_GET['zoom']) ? (float)$_GET['zoom'] : 1.0; ?>
<body<?= $zoom !== 1.0 ? ' style="zoom:' . $zoom . '"' : '' ?>>


<!-- ── 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>
    <a href="<?= htmlspecialchars('https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']) ?>"
       target="_blank" rel="noopener"
       style="font-size:12px;color:rgba(255,255,255,.85);text-decoration:none;border:1px solid rgba(255,255,255,.4);padding:3px 8px;border-radius:5px;white-space:nowrap"
       title="Abrir en el navegador externo si la página no carga bien">
        <i class="fas fa-external-link-alt me-1"></i>Abrir en navegador
    </a>
    <?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>

    <?php
    // Datos para el panel de ciclos dinámico (renderizado por JS al seleccionar examen)
    $_examOptions = [];
    foreach ($esquema as $_oc) {
        if (($_oc['id'] ?? '') === '_c8j2g16') { $_examOptions = $_oc['options'] ?? []; break; }
    }
    ?>
    <!-- Panel de ciclos — renderizado dinámicamente por JS al seleccionar tipo de examen -->
    <div class="mp-cfg-overlay d-none" id="mp-cfg-overlay">
        <div class="mp-cfg-card">
            <div class="mp-cfg-head d-flex align-items-center justify-content-between gap-2">
                <span id="mp-cfg-title"><i class="fas fa-sliders-h me-2"></i>Configurar protocolo de tomas</span>
                <div class="d-flex align-items-center gap-2">
                    <button id="mp-cfg-todas" type="button"
                            title="Usar todas las tomas sin configurar"
                            style="background:rgba(255,255,255,.2);border:none;color:#fff;border-radius:6px;
                                   padding:3px 10px;font-size:.8rem;cursor:pointer;white-space:nowrap">
                        <i class="fas fa-forward me-1"></i>Usar todas
                    </button>
                    <button id="mp-cfg-cerrar" type="button"
                            title="Cancelar"
                            style="background:rgba(255,255,255,.15);border:none;color:#fff;border-radius:6px;
                                   padding:3px 8px;font-size:1rem;cursor:pointer;line-height:1">&times;</button>
                </div>
            </div>
            <div class="mp-cfg-body">
                <p class="text-muted small mb-3">Marque las tomas que va a realizar.</p>
                <div id="mp-cfg-grupos"></div>
            </div>
            <div class="mp-cfg-foot">
                <button class="btn btn-success btn-lg w-100" id="mp-cfg-iniciar">
                    <i class="fas fa-play-circle me-2"></i>Iniciar protocolo
                </button>
                <div id="mp-cfg-msg" class="text-center mt-2 small"></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 if ($modoTurnero && $modoEditar && !empty($tcRow['paciente_id'])): ?>
        <button type="button" id="btnCargarAnterior" class="btn btn-outline-primary btn-sm no-print mb-3"
                data-paciente="<?= (int)$tcRow['paciente_id'] ?>"
                data-formulario="<?= (int)$tcRow['formulario_id'] ?>"
                data-turno="<?= (int)$tcRow['turno_id'] ?>">
            <i class="fas fa-history me-1"></i>Cargar datos de la visita anterior
        </button>
        <div id="cargarAnteriorMsg" class="small text-muted mb-2" style="display:none"></div>
        <?php endif; ?>
        <?php
        // Los campos vinculados leen de aquí. En los envíos de formulario,
        // datos_prefilled es una copia tomada al enviarlo, que puede no incluir
        // claves agregadas después; se completa con los datos vivos del paciente
        // para que un campo automático no quede vacío por eso.
        $paciente = ($datosPrefilled['__paciente'] ?? []) + array_filter([
            'nombre_completo'  => $envio['paciente_nombre']    ?? null,
            'numero_documento' => $envio['numero_documento']   ?? null,
            'tipo_documento'   => $envio['tipo_documento']     ?? null,
            'fecha_nacimiento' => $envio['fecha_nacimiento']   ?? null,
            'telefono'         => $envio['paciente_telefono']  ?? null,
            'eps'              => $envio['eps']                ?? null,
            'direccion'        => $envio['direccion']          ?? null,
            'genero'           => $envio['genero']            ?? null,
        ], fn($v) => $v !== null && $v !== '');

        // Firma global del profesional (un solo valor para todos los campos firma_profesional)
        $firmaSharedProfesional = $envio['firma_profesional_svg'] ?? null;
        // Firma global del paciente: fallback solo para registros sin datos por-campo (_svg).
        $firmaSharedPaciente    = $envio['firma_svg'] ?? null;
        // Si datos_respuestas ya tiene algún campo _svg, no usar firma_svg global como fallback
        // (evita mostrar la firma del disentimiento en el campo de consentimiento y viceversa).
        $_hayDatosPerCampoSvg = !empty(array_filter(array_keys($datosCliente ?? []), fn($k) => str_ends_with($k, '_svg')));

        // Rastrear si ya se renderizó la firma inline (para no duplicarla al pie)
        $_lnkBtnPuesto              = false; // el lápiz de corregir va una sola vez
        $_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;
        // Re-ordenar $esquema para que las secciones de toma aparezcan en orden cronológico
        if (!empty($_mpSepOrder)) {
            $__secs = []; $__cur = ['is_toma' => false, 'min' => null, 'campos' => []];
            foreach ($esquema as $__c) {
                if (($__c['tipo'] ?? '') === 'separador') {
                    if (!empty($__cur['campos'])) $__secs[] = $__cur;
                    $__it = isset($_mpSepOrder[$__c['id'] ?? '']);
                    $__cur = ['is_toma' => $__it, 'min' => $__it ? $_mpSepOrder[$__c['id']] : null, 'campos' => [$__c]];
                } else {
                    $__cur['campos'][] = $__c;
                }
            }
            if (!empty($__cur['campos'])) $__secs[] = $__cur;
            $__tomaIdxs = []; $__tomaList = [];
            foreach ($__secs as $i => $s) { if ($s['is_toma']) { $__tomaIdxs[] = $i; $__tomaList[] = $s; } }
            usort($__tomaList, fn($a, $b) => $a['min'] <=> $b['min']);
            foreach ($__tomaIdxs as $j => $i) { $__secs[$i] = $__tomaList[$j]; }
            $esquema = array_merge(...array_column($__secs, 'campos'));
        }

        $_saltarSeccion = false;
        $_mpSkipSec     = false; // toma excluida por _tomas_config; siempre ocultar sin importar modoEditar
        // Atributos heredados por los campos de la sección actual. Sin esto, una
        // sección cuya condición no se cumple ocultaba solo su encabezado y dejaba
        // sus campos (hora, observaciones, firma) sueltos y visibles.
        $_secAttr = '';
        foreach ($esquema as $campo):
            $tipo = $campo['tipo'] ?? '';
            if ($tipo === 'separador'):
                $_mpSkipSec = false;
                $cond = $campo['condicion'] ?? null;
                if ($cond) {
                    $vCtrl = $todos[$cond['campo_id']] ?? null;
                    // La condición puede depender de un campo vinculado (p. ej. el
                    // sexo del paciente, que gobierna la sección obstétrica). Esos
                    // no están en las respuestas: su valor sale de la ficha.
                    if ($vCtrl === null) {
                        foreach ($esquema as $__c) {
                            if (($__c['id'] ?? '') === $cond['campo_id']
                                && ($__c['tipo'] ?? '') === 'linked') {
                                $vCtrl = $paciente[$__c['linked_key'] ?? ''] ?? null;
                                break;
                            }
                        }
                    }
                    $vCond = $cond['valores'] ?? ($cond['valor'] ? [$cond['valor']] : []);
                    $_saltarSeccion = is_array($vCtrl)
                        ? empty(array_intersect($vCond, $vCtrl))
                        : !in_array($vCtrl, $vCond, true);
                } else {
                    $_saltarSeccion = false;
                }
                // Ocultar secciones de tomas excluidas por la configuración de ciclos del paciente
                if (!$_saltarSeccion && isset($_mpSkippedSepIds[$campo['id'] ?? ''])) {
                    $_saltarSeccion = true;
                    $_mpSkipSec     = true;
                }
                // La sección queda en el DOM (el JS puede mostrarla si cambian de examen)
                // pero oculta, y sus campos heredan el mismo estado.
                $_secAttr = ' data-sep-id="' . esc2($campo['id'] ?? '') . '"'
                          . (($_saltarSeccion && $modoEditar) ? ' style="display:none"' : '');
                if (($_saltarSeccion && !$modoEditar) || $_mpSkipSec) continue; ?>
            <div class="esquema-sep<?= $compact ? ' campo-full' : '' ?>" data-campo-id="<?= esc2($campo['id'] ?? '') ?>"
                 <?= ($_saltarSeccion && $modoEditar) ? 'style="max-height:0;opacity:0;overflow:hidden;pointer-events:none;margin:0!important;padding:0!important"' : '' ?>><?= esc2($campo['label'] ?? '') ?></div>
        <?php   continue; endif;
            if (($_saltarSeccion && !$modoEditar) || $_mpSkipSec) continue;

            // ── 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] ?? $datosCliente[$cid . '_svg'] ?? null)
                        : ($datosCliente[$cid] ?? $datosCliente[$cid . '_svg'] ?? null))
                    : ($datosCliente[$cid . '_svg'] ?? (!$_firmaGlobalPacienteUsada && !$_hayDatosPerCampoSvg ? $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"<?= $_secAttr ?>><?php endif; ?>
        <div class="section-title mt-3" style="color:<?= $fColor ?>"<?= $compact ? '' : $_secAttr ?>>
            <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 if ($isPro):
            // Sin respaldo al usuario de sesión actual: si no quedó guardado el
            // firmante real de esta toma, no se adivina (evita atribuir la firma
            // a quien simplemente está viendo el documento ahora).
            $_pn = $datosCliente[$cid . '_pro_nombre'] ?? $datosCliente['_pro_nombre'] ?? null;
            $_pd = $datosCliente[$cid . '_pro_cedula'] ?? $datosCliente['_pro_cedula'] ?? null;
            if ($_pn || $_pd): ?>
        <div class="firma-pro-ident" style="font-size:13px;line-height:1.5;margin-top:4px">
            <?php if ($_pn): ?><strong><?= esc2($_pn) ?></strong><br><?php endif; ?>
            <?php if ($_pd): ?><span style="color:#6c757d;font-size:12px">C.C. <?= esc2($_pd) ?></span><?php endif; ?>
        </div>
        <?php endif; endif; ?>
        <?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 (sin importar si también hay firma paciente)
        // Pro canvas: mostrar si el profesional aún no firmó (incluso si el paciente ya firmó via WA y estado='firmado')
        $_mostrarCanvasPro = !$yaHayCanvasPro && (
            (!$modoTurnero && !$modoPublico) ||
            ($modoTurnero && $embebido && isUserLoggedIn())
        );
        if ($_mostrarCanvasPro): ?>
        <!-- Canvas del profesional -->
        <?php
        $_mpBloqueado = !empty($_mpMap)
                     && $_mpPrimeraPendiente !== null
                     && $cid !== $_mpPrimeraPendiente
                     && empty($datosCliente[$cid])
                     && empty($datosCliente[$cid . '_svg']);
        ?>
        <div class="firma-pro-widget no-print" id="fpw-<?= htmlspecialchars($cid) ?>"
             data-envio="<?= (int)$envio['id'] ?>" data-campo="<?= htmlspecialchars($cid) ?>"
             data-solo-pro="<?= $modoTurnero ? '1' : '0' ?>"
             data-turno="<?= isset($tcRow) ? (int)$tcRow['turno_id'] : '' ?>"
             data-formulario="<?= isset($tcRow) ? (int)$tcRow['formulario_id'] : '' ?>"
             <?= $_mpBloqueado ? 'style="display:none"' : '' ?>>
            <?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 ?>"<?= $_secAttr ?>><?= 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"<?= $_secAttr ?>><?= $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 ($lk === 'fecha_nacimiento' && $lval && preg_match('/^\d{4}-\d{2}-\d{2}/', $lval)) {
                        $lval = date('d/m/Y', strtotime($lval));
                    }
                    if ($lval !== ''):
        ?>
                <div class="campo-linked" data-campo-id="<?= esc2($cid) ?>">
                    <div class="campo-linked-label"><?= $label ?></div>
                    <div class="campo-linked-valor"><?= esc2($lval) ?></div>
                    <?php if ($modoTurnero && $embebido && $modoEditar && !$_lnkBtnPuesto): $_lnkBtnPuesto = true; ?>
                    <button type="button" class="lnk-corregir no-print" onclick="pedirCorregirPaciente()"
                            title="Abrir la ficha del paciente para corregir sus datos">
                        <i class="fas fa-pen" style="font-size:9px"></i>Corregir
                    </button>
                    <?php endif; ?>
                </div>
        <?php       endif; continue; endif; // linked
                if ($tipo === 'textarea'):
        ?>
                <div class="campo-edit campo-full" data-campo-id="<?= esc2($cid) ?>"<?= $_secAttr ?>>
                    <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) > 2) ? ' campo-full' : '' ?>" data-campo-id="<?= esc2($cid) ?>"<?= $_secAttr ?>>
                    <label><?= $label ?></label>
                    <div class="d-flex flex-wrap gap-3">
                    <?php foreach ($opts as $opt): ?>
                    <div class="form-check form-check-inline m-0">
                        <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 if ($cid === '_c8j2g16'): ?>
                    <?= _addExamWizardHtml($cid) ?>
                    <?php endif; ?>
                </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' : '' ?>" data-campo-id="<?= esc2($cid) ?>"<?= $_secAttr ?>>
                    <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>
                    <?php if ($cid === '_c8j2g16'): ?>
                    <?= _addExamWizardHtml($cid) ?>
                    <?php endif; ?>
                </div>
        <?php       continue; endif;
                if ($tipo === 'select'):
                    $opts = $campo['opciones'] ?? $campo['options'] ?? [];
        ?>
                <div class="campo-edit" data-campo-id="<?= esc2($cid) ?>"<?= $_secAttr ?>>
                    <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'
                };
                // texto libre ocupa fila completa; tipos cortos caben 3 por fila
                $inputFullClass = ($compact && $tipo === 'texto') ? ' campo-full' : '';
                $extraClase = htmlspecialchars($campo['clase'] ?? '', ENT_QUOTES);
        ?>
                <?php if ($inputType === 'time' && isset($_signedHoraCampos[$cid])): ?>
                <div class="campo-edit<?= $inputFullClass ?><?= $extraClase ? ' '.$extraClase : '' ?>" data-campo-id="<?= esc2($cid) ?>"<?= $_secAttr ?>>
                    <label><?= $label ?></label>
                    <div class="d-flex gap-1 align-items-center">
                        <input type="time" class="form-control form-control-sm" readonly
                               value="<?= esc2($todos[$cid] ?? '') ?>" style="background:#f1f5f9;color:#64748b;cursor:not-allowed">
                        <?php if ($modoTurnero && isset($_hcToFirmaIds[$cid])): ?>
                        <button type="button" class="btn btn-outline-warning btn-sm flex-shrink-0 btn-olvidar-toma"
                                data-hc="<?= esc2($cid) ?>"
                                data-fids="<?= esc2(json_encode($_hcToFirmaIds[$cid])) ?>"
                                title="Olvidar firma — permite editar este tiempo">
                            <i class="fas fa-undo"></i>
                        </button>
                        <?php endif; ?>
                    </div>
                </div>
                <?php else: ?>
                <div class="campo-edit<?= $inputFullClass ?><?= $extraClase ? ' '.$extraClase : '' ?>" data-campo-id="<?= esc2($cid) ?>"<?= $_secAttr ?>>
                    <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 endif; ?>
        <?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 ($_formSoloPro && $envio['estado'] !== 'firmado'): ?>
        <div class="mt-4 no-print" id="btn-completar-pro-wrap">
            <button class="btn btn-success fw-semibold" id="btn-completar-pro" style="font-size:1rem;padding:.6rem 1.4rem">
                <i class="fas fa-check-circle me-2"></i>Guardar y completar
            </button>
            <div id="btn-completar-pro-msg" class="mt-2 small"></div>
        </div>
        <?php elseif ($modoTurnero && $envio['estado'] !== 'firmado' && !$_esquemaTieneFirmaAlguna && !$_soloFirmaPro): ?>
        <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'] === 'cerrado_anticipado'): ?>
        <?php $_ca = $datosCliente['_cierre_anticipado'] ?? null; ?>
        <div class="alert alert-warning mt-4 d-flex align-items-start gap-3 no-print" style="border-left:4px solid #f59e0b">
            <i class="fas fa-exclamation-triangle fs-4 flex-shrink-0 mt-1 text-warning"></i>
            <div>
                <strong>Toma cerrada anticipadamente</strong><br>
                <?php if (!empty($_ca['motivo'])): ?>
                <div class="mt-1 text-secondary" style="font-size:.9rem">
                    <i class="fas fa-comment-alt me-1"></i><?= esc2($_ca['motivo']) ?>
                </div>
                <?php endif; ?>
                <button onclick="window.close()" class="btn btn-outline-secondary btn-sm fw-semibold mt-2">
                    <i class="fas fa-times me-1"></i>Cerrar ventana
                </button>
            </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; ?>

        <?php if ($modoTurnero && $embebido && !empty($_mpMap) && $envio['estado'] === 'en_progreso'): ?>
        <div class="mt-2 no-print text-end">
            <button class="btn btn-outline-primary btn-sm" id="btn-guardar-tiempos">
                <i class="fas fa-save me-1"></i>Guardar tiempos
            </button>
        </div>
        <?php endif; ?>
        <?php if ($modoTurnero && $embebido && !empty($_mpMap) && !in_array($envio['estado'], ['firmado','cerrado_anticipado'])): ?>
        <div class="mt-3 no-print text-end">
            <button class="btn btn-outline-danger btn-sm" id="btn-cerrar-anticip">
                <i class="fas fa-times-circle me-1"></i>Cerrar anticipadamente
            </button>
        </div>
        <!-- Modal cierre anticipado -->
        <div id="modal-cerrar-anticip" style="position:fixed;inset:0;z-index:9999;background:rgba(0,0,0,.5);display:none;align-items:center;justify-content:center">
            <div style="background:#fff;border-radius:12px;padding:1.5rem;max-width:380px;width:94%;box-shadow:0 8px 32px rgba(0,0,0,.25)">
                <h6 class="fw-bold mb-2"><i class="fas fa-exclamation-triangle text-warning me-2"></i>Cerrar anticipadamente</h6>
                <p class="text-muted small mb-3">Explique por qué no se completaron todas las tomas. Este mensaje quedará visible en toma de muestras y recepción.</p>
                <textarea id="mca-motivo" rows="3" class="form-control mb-2" placeholder="Ej: El paciente no toleró más extracciones…"></textarea>
                <div id="mca-error" class="text-danger small mb-2 d-none"></div>
                <div class="d-flex gap-2 justify-content-end">
                    <button class="btn btn-outline-secondary btn-sm" id="mca-cancelar">Cancelar</button>
                    <button class="btn btn-danger btn-sm fw-semibold" id="mca-confirmar">
                        <i class="fas fa-times-circle me-1"></i>Confirmar cierre
                    </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)): ?>
/* ── Panel de ciclos dinámico ── */
var _mpAllGroups   = <?= json_encode($_mpGroupsFull,  JSON_UNESCAPED_UNICODE) ?>;
var _mpExamOptions = <?= json_encode($_examOptions,   JSON_UNESCAPED_UNICODE) ?>;
var _mpTomasConfig = <?= json_encode($_tomasConfig,   JSON_UNESCAPED_UNICODE) ?>;
var _mpCfgToken    = <?= json_encode($tokenTurnero,   JSON_UNESCAPED_UNICODE) ?>;
var _mpModoTurnero = <?= json_encode($modoTurnero && $embebido) ?>;
var _mpOtroHora    = <?= json_encode($datosCliente['_otro_hora'] ?? null, JSON_UNESCAPED_UNICODE) ?>;

function _mpGuardarConfig(config, examType, onOk, onErr, extra) {
    var base = window.location.href.split('/ver_formulario_enviado.php')[0];
    var allExams = Array.from(document.querySelectorAll('[name="_c8j2g16[]"]:checked,[name="_c8j2g16"]:checked')).map(function(el){ return el.value; });
    var payload = Object.assign({ token: _mpCfgToken, tomas_config: config, exam_type: examType, exam_types: allExams }, extra || {});
    fetch(base + '/modules/turnero/api/configurar_tomas.php', {
        method: 'POST', headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(payload)
    })
    .then(function(r) { return r.json(); })
    .then(function(d) {
        if (d.ok) { try { sessionStorage.setItem('_mpExamRestore', allExams.join('||') || examType || ''); } catch(e) {} onOk(); }
        else onErr(d.error || 'Error desconocido');
    })
    .catch(function() { onErr('Error de conexión.'); });
}

function _mpChipLabel(label) {
    var m = label.match(/Minuto\s+(\d+)/i);
    if (m) return m[1];
    var pm = label.match(/(\d{1,2}):\d{2}\s*p\.?m/i);
    if (pm) return pm[1] + 'pm';
    if (/otro/i.test(label)) return 'Otro';
    return label.replace(/^Toma\s*·\s*/i, '').trim();
}

var _mpActiveConfigExam = '';

function _mpShowCfgPanel(examName, groups, isModify) {
    _mpActiveConfigExam = examName;
    var savedFirmas = window._mpSavedFirmas || {};
    var html = '';
    var multiGrupo = Object.keys(groups).length > 1;
    for (var gk in groups) {
        var secs = groups[gk];
        html += '<div class="mb-3">'
              + (multiGrupo ? '<div class="mp-cfg-gtitle">' + (secs[0] ? (secs[0].display || gk) : gk) + '</div>' : '')
              + '<div class="mp-chips">';
        for (var i = 0; i < secs.length; i++) {
            var s = secs[i];
            var isSigned = !!savedFirmas[s.firma];
            var _examCfg = _mpTomasConfig && _mpTomasConfig[examName];
            var inSaved  = _examCfg && _examCfg[gk] && _examCfg[gk].indexOf(s.firma) !== -1;
            var isMin0   = _mpChipLabel(s.label || '') === '0';
            var isActive = isSigned || inSaved || (!isModify && isMin0);
            var isOtro   = /otro/i.test(s.label || '');
            var chipLbl  = _mpChipLabel(s.label || ('Toma ' + (i+1)));
            // orden flex: numérico para Minuto X, valor guardado para Otro, 9999 si sin valor
            var chipOrder = isOtro
                ? ((_mpOtroHora && isActive) ? parseInt(_mpOtroHora) : 9999)
                : (parseInt(chipLbl) === chipLbl >> 0 ? parseInt(chipLbl) : i * 10);
            html += '<span class="mp-chip-grp"' + (isOtro ? ' data-otro-grp="1"' : '') + ' style="order:' + chipOrder + '">';
            // checkbox oculto — sigue siendo la fuente de verdad para el guardado
            html += '<input type="checkbox" class="mp-cfg-chk d-none" id="cfg-' + s.firma + '"'
                  + ' value="' + s.firma + '" data-group="' + gk + '"'
                  + (isActive ? ' checked' : '') + (isSigned ? ' disabled' : '') + '>';
            html += '<button type="button" class="mp-chip' + (isActive ? ' active' : '') + (isSigned ? ' signed' : '') + '"'
                  + ' data-target="cfg-' + s.firma + '"' + (isOtro ? ' data-otro="1"' : '')
                  + (isSigned ? ' disabled title="Ya firmada"' : '') + '>'
                  + chipLbl + (isSigned ? ' <i class="fas fa-check ms-1 small"></i>' : '') + '</button>';
            if (isOtro) {
                var otroVal = (isActive && _mpOtroHora) ? _mpOtroHora : '';
                html += '<input type="number" class="form-control form-control-sm mp-chip-otro-inp"'
                      + ' placeholder="min" min="1" step="1"'
                      + (otroVal ? ' value="' + otroVal + '"' : '')
                      + (isActive ? ' style="display:inline-block"' : '') + '>'
                      + '<span class="mp-chip-otro-unit' + (isActive ? '' : ' d-none') + '">min</span>';
            }
            html += '</span>';
        }
        html += '</div></div>';
    }
    var grupos = document.getElementById('mp-cfg-grupos');
    grupos.innerHTML = html;
    document.getElementById('mp-cfg-title').innerHTML = '<i class="fas fa-sliders-h me-2"></i>' + examName;
    document.getElementById('mp-cfg-overlay').classList.remove('d-none');

}

// Click en chips de tiempo (delegado al documento para evitar múltiples listeners)
document.addEventListener('click', function(e) {
    var chip = e.target.closest('.mp-chip');
    if (!chip || chip.disabled || !chip.closest('#mp-cfg-grupos')) return;
    var chk = document.getElementById(chip.getAttribute('data-target'));
    if (!chk || chk.disabled) return;
    chk.checked = !chk.checked;
    chip.classList.toggle('active', chk.checked);
    if (chip.dataset.otro) {
        var grp  = chip.closest('.mp-chip-grp');
        var inp  = grp && grp.querySelector('.mp-chip-otro-inp');
        var unit = grp && grp.querySelector('.mp-chip-otro-unit');
        if (inp) {
            inp.style.display = chk.checked ? 'inline-block' : 'none';
            if (unit) unit.classList.toggle('d-none', !chk.checked);
            if (chk.checked) setTimeout(function() { inp.focus(); }, 50);
        }
    }
});

// Reposicionar chip "Otro" en tiempo real según el minuto digitado
document.addEventListener('input', function(e) {
    if (!e.target.classList.contains('mp-chip-otro-inp')) return;
    var grp = e.target.closest('.mp-chip-grp');
    if (!grp) return;
    var v = parseInt(e.target.value);
    grp.style.order = (v >= 1) ? v : 9999;
});

function _mpGetGroupsForExam(examName) {
    var result = {};
    for (var gk in _mpAllGroups) {
        var parts = gk.split('|').filter(Boolean);
        var isCond = parts.some(function(p) { return _mpExamOptions.indexOf(p) !== -1; });
        if (!isCond || parts.indexOf(examName) !== -1) {
            result[gk] = _mpAllGroups[gk];
        }
    }
    return result;
}

// Visibilidad de secciones de tomas según examen seleccionado.
// Oculta TODOS los sep_ids de _mpAllGroups por defecto;
// muestra solo los del grupo cuya clave coincide con el examen.
// Acepta un examen o varios: un paciente puede tener Glicemia y Prolactina a la
// vez, y cada uno aporta sus propias secciones de toma.
function _mpApplyExamVisibility(selectedExams) {
    var exams = selectedExams == null ? []
              : (Array.isArray(selectedExams) ? selectedExams : [selectedExams]);

    // 1. Todos los sep_ids que son secciones de toma
    var allTomaSeps = {};
    for (var gk in _mpAllGroups) {
        _mpAllGroups[gk].forEach(function(s) { if (s.sep_id) allTomaSeps[s.sep_id] = gk; });
    }
    if (!Object.keys(allTomaSeps).length) return;

    // 2. Sep_ids de todos los exámenes seleccionados
    var examSeps = {};
    exams.forEach(function(ex) {
        if (!ex) return;
        var groups = _mpGetGroupsForExam(ex);
        for (var gk2 in groups) {
            groups[gk2].forEach(function(s) { if (s.sep_id) examSeps[s.sep_id] = true; });
        }
    });

    // 3. Recorrer hijos directos del contenedor de campos (.campos-grid).
    //    Los campos llevan data-sep-id, así que se ocultan junto a su encabezado
    //    en vez de quedar sueltos cuando la sección no aplica.
    var container = document.querySelector('.campos-grid') || document.querySelector('.doc-body');
    if (!container) return;
    var inHidden = false;
    Array.from(container.children).forEach(function(child) {
        if (child.classList.contains('esquema-sep')) {
            var cid = child.getAttribute('data-campo-id') || '';
            inHidden = allTomaSeps[cid] !== undefined ? !examSeps[cid] : false;
        } else {
            var sid = child.getAttribute('data-sep-id');
            if (sid && allTomaSeps[sid] !== undefined) inHidden = !examSeps[sid];
        }
        child.style.display = inHidden ? 'none' : '';
    });
}

// Helper: exámenes seleccionados (radio name=_c8j2g16 o checkbox name=_c8j2g16[])
function _mpGetExamNames() {
    return Array.from(document.querySelectorAll('[name="_c8j2g16"]:checked,[name="_c8j2g16[]"]:checked'))
                .map(function(el) { return el.value; });
}
// Compatibilidad: primer examen seleccionado
function _mpGetExamName() {
    var n = _mpGetExamNames();
    return n.length ? n[0] : null;
}
function _mpSetExamChecked(name) {
    var esc = name.replace(/"/g, '\\"');
    var el = document.querySelector('[name="_c8j2g16"][value="' + esc + '"],[name="_c8j2g16[]"][value="' + esc + '"]');
    if (el) el.checked = true;
}

// ── Badges de estado de examen ───────────────────────────────────────
function _mpRenderBadges() {
    var wrap = document.getElementById('mp-exam-badges');
    if (!wrap) return;
    var checked = Array.from(document.querySelectorAll('[name="_c8j2g16[]"]:checked,[name="_c8j2g16"]:checked'));
    if (!checked.length) { wrap.innerHTML = ''; return; }
    var html = '';
    checked.forEach(function(el) {
        var name = el.value;
        var groups = _mpGetGroupsForExam(name);
        var totalTomas = Object.values(groups).reduce(function(s, g) { return s + g.length; }, 0);
        var groupKeys  = Object.keys(groups);
        // Verificar si este examen ya está configurado en _mpTomasConfig (formato anidado por examen)
        var _examConf = _mpTomasConfig && _mpTomasConfig[name];
        var configured = !!_examConf && groupKeys.every(function(k) { return _examConf[k]; });
        var tomasCount = 0;
        if (configured) {
            groupKeys.forEach(function(k) { if (_examConf[k]) tomasCount += _examConf[k].length; });
        }
        html += '<div class="mp-exam-badge' + (configured ? ' configured' : '') + '">'
              + '<span class="mp-exam-badge-name">' + name + '</span>';
        if (configured) {
            html += '<span class="mp-exam-badge-info"><i class="fas fa-check-circle text-success me-1"></i>'
                  + tomasCount + ' toma' + (tomasCount !== 1 ? 's' : '') + ' configurada' + (tomasCount !== 1 ? 's' : '') + '</span>'
                  + '<button class="mp-exam-badge-btn" data-exam="' + name.replace(/"/g, '&quot;') + '" data-modify="1">'
                  + '<i class="fas fa-edit me-1"></i>Modificar</button>';
        } else {
            html += '<span class="mp-exam-badge-info text-warning"><i class="fas fa-clock me-1"></i>Pendiente configurar ('
                  + totalTomas + ' tomas disponibles)</span>'
                  + '<button class="mp-exam-badge-btn" data-exam="' + name.replace(/"/g, '&quot;') + '">'
                  + '<i class="fas fa-sliders-h me-1"></i>Configurar tiempos</button>';
        }
        html += '</div>';
    });
    wrap.innerHTML = html;
}

// Escuchar selección de tipo de examen
document.addEventListener('change', function(e) {
    var el = e.target;
    if (el.name.replace(/\[\]$/, '') !== '_c8j2g16') return;
    _mpApplyExamVisibility(_mpGetExamNames());
    if (_mpModoTurnero) _mpRenderBadges();
});

// Click en botón "Configurar tiempos" o "Modificar" del badge
document.addEventListener('click', function(e) {
    var btn = e.target.closest('.mp-exam-badge-btn');
    if (!btn) return;
    var examName = btn.getAttribute('data-exam');
    var isModify = btn.getAttribute('data-modify') === '1';
    var groups   = _mpGetGroupsForExam(examName);
    if (Object.keys(groups).length) _mpShowCfgPanel(examName, groups, isModify);
});

// Al cargar: aplicar visibilidad y renderizar badges
document.addEventListener('DOMContentLoaded', function() {
    var examName = _mpGetExamName();
    // Restaurar selección de examen(es) tras el reload del panel de config
    try {
        var saved = sessionStorage.getItem('_mpExamRestore');
        if (saved) {
            sessionStorage.removeItem('_mpExamRestore');
            saved.split('||').filter(Boolean).forEach(function(n) { _mpSetExamChecked(n); });
            if (!examName) examName = saved.split('||').filter(Boolean)[0] || '';
        }
    } catch(e) {}
    // Los checkboxes ya quedaron restaurados arriba: aplicar sobre todos los
    // marcados, no solo el primero.
    _mpApplyExamVisibility(_mpGetExamNames());
    if (_mpModoTurnero) {
        _mpRenderBadges();
        // Auto-abrir solo si hay un examen sin configurar (nuevo). Si todos configurados, no abrir.
        var _chkd = Array.from(document.querySelectorAll('[name="_c8j2g16[]"]:checked,[name="_c8j2g16"]:checked'));
        var _autoT = null;
        for (var _i = 0; _i < _chkd.length && !_autoT; _i++) {
            var _n = _chkd[_i].value, _g = _mpGetGroupsForExam(_n);
            if (!Object.keys(_g).length) continue;
            var _eCfg = _mpTomasConfig && _mpTomasConfig[_n];
            var _conf = !!_eCfg && Object.keys(_g).every(function(k){ return _eCfg[k]; });
            if (!_conf) _autoT = { name: _n, groups: _g, isModify: false };
        }
        if (_autoT) _mpShowCfgPanel(_autoT.name, _autoT.groups, _autoT.isModify);
    }
});

// Botón "Usar todas"
document.getElementById('mp-cfg-todas').addEventListener('click', function() {
    var examName = _mpActiveConfigExam || _mpGetExamName() || '';
    var cfg = {};
    document.querySelectorAll('.mp-cfg-chk').forEach(function(chk) {
        if (!cfg[chk.dataset.group]) cfg[chk.dataset.group] = [];
        cfg[chk.dataset.group].push(chk.value);
    });
    var btn = this;
    btn.disabled = true; btn.innerHTML = '<i class="fas fa-spinner fa-spin me-2"></i>Guardando...';
    _mpGuardarConfig(cfg, examName, function() { location.reload(); }, function(e) {
        document.getElementById('mp-cfg-msg').innerHTML = '<span class="text-danger">' + e + '</span>';
        btn.disabled = false; btn.innerHTML = '<i class="fas fa-forward me-1"></i>Usar todas';
    });
});

// Botón "Iniciar protocolo"
document.getElementById('mp-cfg-iniciar').addEventListener('click', function() {
    var examName = _mpActiveConfigExam || _mpGetExamName() || '';
    var byGroup  = {};
    document.querySelectorAll('.mp-cfg-chk').forEach(function(chk) {
        if (!byGroup[chk.dataset.group]) byGroup[chk.dataset.group] = [];
        if (chk.checked) byGroup[chk.dataset.group].push(chk.value);
    });
    for (var g in byGroup) {
        if (!byGroup[g].length) {
            document.getElementById('mp-cfg-msg').innerHTML =
                '<span class="text-danger"><i class="fas fa-exclamation-circle me-1"></i>Seleccione al menos una toma por protocolo.</span>';
            return;
        }
    }
    // Validar que "Otro" tenga minutos ingresados
    var otroHora = '';
    var otroBtn  = document.querySelector('#mp-cfg-grupos .mp-chip[data-otro="1"]');
    if (otroBtn) {
        var otroChk = document.getElementById(otroBtn.getAttribute('data-target'));
        if (otroChk && otroChk.checked) {
            var otroInp = otroBtn.parentElement.querySelector('.mp-chip-otro-inp');
            if (otroInp) otroHora = otroInp.value.trim();
            if (!otroHora || isNaN(parseInt(otroHora)) || parseInt(otroHora) < 1) {
                document.getElementById('mp-cfg-msg').innerHTML =
                    '<span class="text-danger"><i class="fas fa-exclamation-circle me-1"></i>Ingrese los minutos para la toma "Otro" (ej: 2).</span>';
                return;
            }
        }
    }
    var btn = this;
    btn.disabled = true; btn.innerHTML = '<i class="fas fa-spinner fa-spin me-2"></i>Guardando...';
    _mpGuardarConfig(byGroup, examName, function() { location.reload(); }, function(e) {
        document.getElementById('mp-cfg-msg').innerHTML = '<span class="text-danger">' + e + '</span>';
        btn.disabled = false; btn.innerHTML = '<i class="fas fa-play-circle me-2"></i>Iniciar protocolo';
    }, otroHora ? { otro_hora: otroHora } : {});
});

// Botón cerrar panel de config
document.getElementById('mp-cfg-cerrar').addEventListener('click', function() {
    document.getElementById('mp-cfg-overlay').classList.add('d-none');
    document.querySelectorAll('[name="_c8j2g16[]"]').forEach(function(el) { el.checked = false; });
    _mpApplyExamVisibility(null);
});
/* ── Wizard: añadir tipo de examen ── */
var _aewState = {}; // {cid: {count, rows}}

function aewOpen(cid) {
    var box = document.getElementById('aew-box-' + cid);
    if (!box) return;
    box.classList.remove('d-none');
    _aewState[cid] = { count: 1 };
    aewRenderTomas(cid);
    var ni = document.getElementById('aew-name-' + cid);
    if (ni) ni.focus();
}
function aewClose(cid) {
    var box = document.getElementById('aew-box-' + cid);
    if (box) box.classList.add('d-none');
}
function aewAddToma(cid, delta) {
    if (!_aewState[cid]) return;
    var n = (_aewState[cid].count || 1) + delta;
    if (n < 1) n = 1; if (n > 12) n = 12;
    _aewState[cid].count = n;
    var el = document.getElementById('aew-count-' + cid);
    if (el) el.textContent = n;
    aewRenderTomas(cid);
}
function aewRenderTomas(cid) {
    var n   = (_aewState[cid] || {}).count || 1;
    var cnt = document.getElementById('aew-tomas-' + cid);
    if (!cnt) return;
    // Guardar valores actuales antes de re-renderizar
    var prev = [];
    cnt.querySelectorAll('.aew-row').forEach(function(r) {
        prev.push({
            lbl:  r.querySelector('.aew-lbl')  ? r.querySelector('.aew-lbl').value  : '',
            tipo: r.querySelector('.aew-tipo') ? r.querySelector('.aew-tipo').value : 'minutos',
            val:  r.querySelector('.aew-val')  ? r.querySelector('.aew-val').value  : '',
        });
    });
    var html = '';
    for (var i = 0; i < n; i++) {
        var p    = prev[i] || {};
        var lbl  = p.lbl  || (i === 0 ? 'Minuto 0' : 'Minuto ' + (i * 30));
        var tipo = p.tipo || 'minutos';
        var val  = p.val  !== undefined ? p.val : (i === 0 ? '0' : String(i * 30));
        html += '<div class="aew-row border rounded p-2 mb-1 d-flex gap-2 align-items-end flex-wrap">'
             +  '<div style="flex:2;min-width:120px"><label class="form-label small mb-1">Label toma ' + (i+1) + '</label>'
             +  '<input class="form-control form-control-sm aew-lbl" value="' + lbl.replace(/"/g,'&quot;') + '" placeholder="Ej: Minuto 0"></div>'
             +  '<div style="flex:1;min-width:100px"><label class="form-label small mb-1">Tipo</label>'
             +  '<select class="form-select form-select-sm aew-tipo" onchange="aewTipoChange(this)">'
             +  '<option value="minutos"' + (tipo==='minutos'?' selected':'') + '>Minutos</option>'
             +  '<option value="hora_fija"' + (tipo==='hora_fija'?' selected':'') + '>Hora fija</option>'
             +  '</select></div>'
             +  '<div style="flex:1;min-width:80px"><label class="form-label small mb-1 aew-val-lbl">' + (tipo==='hora_fija'?'HH:MM':'Minutos') + '</label>'
             +  '<input class="form-control form-control-sm aew-val" value="' + String(val).replace(/"/g,'&quot;') + '" placeholder="' + (tipo==='hora_fija'?'08:00':'0') + '"></div>'
             +  '</div>';
    }
    cnt.innerHTML = html;
}
function aewTipoChange(sel) {
    var row = sel.closest('.aew-row');
    var lbl = row.querySelector('.aew-val-lbl');
    var inp = row.querySelector('.aew-val');
    if (sel.value === 'hora_fija') { lbl.textContent = 'HH:MM'; inp.placeholder = '08:00'; }
    else                           { lbl.textContent = 'Minutos'; inp.placeholder = '0'; }
}
function aewSave(cid) {
    var name = (document.getElementById('aew-name-' + cid) || {}).value;
    if (name) name = name.trim();
    var msg  = document.getElementById('aew-msg-' + cid);
    function showErr(e) { if (msg) msg.innerHTML = '<span class="text-danger"><i class="fas fa-exclamation-circle me-1"></i>' + e + '</span>'; }
    if (!name) { showErr('Ingrese el nombre del examen.'); return; }

    var tomas = [];
    document.querySelectorAll('#aew-tomas-' + cid + ' .aew-row').forEach(function(row) {
        tomas.push({
            label: (row.querySelector('.aew-lbl') || {}).value || '',
            tipo:  (row.querySelector('.aew-tipo') || {}).value || 'minutos',
            valor: (row.querySelector('.aew-val') || {}).value || '0',
        });
    });
    if (!tomas.length) { showErr('Configure al menos una toma.'); return; }
    for (var i = 0; i < tomas.length; i++) {
        if (!tomas[i].label.trim()) { showErr('Ingrese el label de la toma ' + (i+1) + '.'); return; }
    }

    var btn = document.getElementById('aew-save-' + cid);
    if (btn) { btn.disabled = true; btn.innerHTML = '<i class="fas fa-spinner fa-spin me-1"></i>Guardando…'; }
    if (msg) msg.innerHTML = '';

    var base = window.location.href.split('/ver_formulario_enviado.php')[0];
    fetch(base + '/api/lab/save_tomas_config.php', {
        method: 'POST', headers: {'Content-Type': 'application/json'},
        body: JSON.stringify({ action: 'add_exam', exam_name: name, tomas: tomas })
    })
    .then(function(r) { return r.json(); })
    .then(function(d) {
        if (d.ok) {
            if (msg) msg.innerHTML = '<span class="text-success"><i class="fas fa-check me-1"></i>Examen creado. Recargando…</span>';
            // Guardar selección en sessionStorage para auto-seleccionar al recargar
            try { sessionStorage.setItem('_aew_preselect', name); } catch(e) {}
            setTimeout(function() { location.reload(); }, 900);
        } else {
            showErr(d.error || 'Error desconocido');
            if (btn) { btn.disabled = false; btn.innerHTML = '<i class="fas fa-check me-1"></i>Crear examen'; }
        }
    })
    .catch(function() {
        showErr('Error de conexión.');
        if (btn) { btn.disabled = false; btn.innerHTML = '<i class="fas fa-check me-1"></i>Crear examen'; }
    });
}
// Auto-seleccionar el examen recién creado tras recargar
(function() {
    try {
        var pre = sessionStorage.getItem('_aew_preselect');
        if (!pre) return;
        sessionStorage.removeItem('_aew_preselect');
        _mpSetExamChecked(pre);
        var el = document.querySelector('[name="_c8j2g16"]:checked,[name="_c8j2g16[]"]:checked');
        if (el) el.dispatchEvent(new Event('change', {bubbles:true}));
    } catch(e) {}
})();

window._muestrasMap        = <?= json_encode($_mpMap, JSON_UNESCAPED_UNICODE) ?>;
window._mpSavedFirmas      = {};
window._mpTomaData         = {};
window._mpPrimeraPendiente = <?= json_encode($_mpPrimeraPendiente) ?>;

function _mpAutoFillHora(firmaId) {
    var entry = window._muestrasMap && window._muestrasMap[firmaId];
    if (!entry || !entry.hora_campo) return;
    var el = document.querySelector('[name="' + entry.hora_campo + '"]');
    if (!el || el.value) return;
    var now = new Date();
    el.value = ('0'+now.getHours()).slice(-2) + ':' + ('0'+now.getMinutes()).slice(-2);
}

// ── Tarjetas de toma ─────────────────────────────────────────────────
function _mpBuildSepFirmaMap() {
    var m = {};
    for (var gk in _mpAllGroups) {
        (_mpAllGroups[gk] || []).forEach(function(s) {
            if (s.sep_id && s.firma && window._muestrasMap[s.firma]) m[s.sep_id] = s.firma;
        });
    }
    return m;
}

function _mpEffectiveLabel(label) {
    return (/otro/i.test(label) && _mpOtroHora) ? label + ' - ' + _mpOtroHora : label;
}

function _mpCardHdrHtml(fid, num, label, state) {
    var badge = state === 'signed'
        ? '<span class="toma-card-badge toma-badge-done"><i class="fas fa-check-circle me-1"></i>Firmado</span>'
        : state === 'active'
            ? '<span class="toma-card-badge toma-badge-active"><i class="fas fa-pen me-1"></i>Firmar ahora</span>'
            : '<span class="toma-card-badge toma-badge-wait"><i class="fas fa-hourglass-half me-1"></i>En espera</span>';
    var extra = state === 'locked' ? '<span class="toma-card-cd" id="tc-cd-' + fid + '"></span>' : '';
    var hora  = state === 'signed' ? '<span class="toma-card-hora"></span>' : '';
    var obs   = state === 'signed' ? '<span class="toma-card-obs" id="tc-obs-' + fid + '"></span>' : '';
    return '<span class="toma-card-num">' + num + '</span>'
         + '<span class="toma-card-lbl">' + label + '</span>'
         + hora + obs + extra + badge;
}

function _mpRenderCards() {
    if (!window._muestrasMap || !Object.keys(window._muestrasMap).length) return;
    var container = document.querySelector('.campos-grid');
    if (!container || container.querySelector('.toma-card')) return;

    var sepToFirma = _mpBuildSepFirmaMap();
    if (!Object.keys(sepToFirma).length) return;

    var nodes = Array.from(container.childNodes);
    // Segment by toma separators
    var segs = [], cur = null;
    nodes.forEach(function(node) {
        if (node.nodeType !== 1) return;
        var sid = node.getAttribute('data-campo-id') || '';
        if (sid && sepToFirma[sid]) { cur = { fid: sepToFirma[sid], nodes: [node] }; segs.push(cur); }
        else if (cur)               { cur.nodes.push(node); }
    });
    if (!segs.length) return;

    // Pre-toma nodes
    var firstNode = segs[0].nodes[0], preNodes = [];
    for (var i = 0; i < nodes.length; i++) {
        if (nodes[i] === firstNode) break;
        if (nodes[i].nodeType === 1) preNodes.push(nodes[i]);
    }

    container.innerHTML = '';
    preNodes.forEach(function(n) { container.appendChild(n); });

    // Progress placeholder
    var progDiv = document.createElement('div');
    progDiv.id = 'mp-progress'; progDiv.className = 'mp-progress-wrap campo-full';
    container.appendChild(progDiv);

    var signedCount = 0;
    segs.forEach(function(seg, idx) {
        var fid = seg.fid, entry = window._muestrasMap[fid] || {};
        // State: signed if firma-box img exists OR saved this session; active if first pending
        var hasImg = seg.nodes.some(function(n) { return n.querySelector && !!n.querySelector('.firma-box img'); });
        var isSigned = hasImg || !!((window._mpSavedFirmas || {})[fid]);
        if (!isSigned) {
            // Check if fpw widget exists at all (if not → signed pre-load, edge case)
            var hasFpw = seg.nodes.some(function(n) {
                return n.id === ('fpw-' + fid) || !!(n.querySelector && n.querySelector('#fpw-' + fid));
            });
            if (!hasFpw) isSigned = true;
        }
        var isActive = !isSigned && fid === window._mpPrimeraPendiente;
        var state    = isSigned ? 'signed' : isActive ? 'active' : 'locked';
        if (isSigned) signedCount++;

        var card = document.createElement('div');
        card.className = 'toma-card toma-card--' + state + ' campo-full';
        card.id = 'tc-' + fid;
        card.setAttribute('data-firma', fid);

        var hdr = document.createElement('div');
        hdr.className = 'toma-card-hdr';
        hdr.innerHTML = _mpCardHdrHtml(fid, idx + 1, _mpEffectiveLabel(entry.label || ''), state);
        if (isSigned) {
            // Fill hora in header
            var horaEl = null;
            if (entry.hora_campo) seg.nodes.forEach(function(n) { if (!horaEl) horaEl = n.querySelector ? n.querySelector('[name="' + entry.hora_campo + '"]') : null; });
            if (horaEl && horaEl.value) { var hs = hdr.querySelector('.toma-card-hora'); if (hs) hs.textContent = horaEl.value; }
            // Fill obs in header
            if (entry.obs_campo) {
                var obsEl = null;
                seg.nodes.forEach(function(n) { if (!obsEl && n.querySelector) obsEl = n.querySelector('[name="' + entry.obs_campo + '"]'); });
                if (obsEl && obsEl.value) { var os = hdr.querySelector('.toma-card-obs'); if (os) os.textContent = obsEl.value; }
            }
            hdr.addEventListener('click', function() { card.classList.toggle('tc-expanded'); });
        }

        var body = document.createElement('div');
        body.className = 'toma-card-body';
        seg.nodes.forEach(function(n) { body.appendChild(n); });

        card.appendChild(hdr); card.appendChild(body);
        container.appendChild(card);
    });

    _mpUpdateProgress(signedCount, segs.map(function(s) { return s.fid; }));
}

function _mpUpdateProgress(signedCount, firmaIds) {
    var wrap = document.getElementById('mp-progress');
    if (!wrap) return;
    var total = firmaIds.length;
    var pct   = total ? Math.round(signedCount / total * 100) : 0;
    var steps = firmaIds.map(function(fid) {
        var isSigned = !!((window._mpSavedFirmas || {})[fid])
                    || (function(){ var c=document.getElementById('tc-'+fid); return c&&c.classList.contains('toma-card--signed'); }());
        var isActive = fid === window._mpPrimeraPendiente && !isSigned;
        var cls = isSigned ? 'done' : isActive ? 'act' : 'wait';
        var lbl = _mpEffectiveLabel(((window._muestrasMap || {})[fid] || {}).label || '');
        return '<span class="mp-step mp-step--' + cls + '" title="' + lbl.replace(/"/g,'&quot;') + '"></span>';
    }).join('');
    wrap.innerHTML = '<div class="mp-prog-hdr"><span class="mp-prog-text">Toma <strong>' + signedCount + '</strong> de <strong>' + total + '</strong></span><div class="mp-steps">' + steps + '</div></div>'
                   + '<div class="mp-prog-bar"><div class="mp-prog-fill" style="width:' + pct + '%"></div></div>';
}

function _mpRefreshCards(signedFid, nextFid) {
    window._mpPrimeraPendiente = nextFid || null;
    // Update signed card
    var sc = document.getElementById('tc-' + signedFid);
    if (sc) {
        sc.classList.remove('toma-card--active'); sc.classList.add('toma-card--signed');
        var shdr = sc.querySelector('.toma-card-hdr');
        if (shdr) {
            var se = window._muestrasMap[signedFid] || {}, snum = (sc.querySelector('.toma-card-num') || {}).textContent || '1';
            shdr.innerHTML = _mpCardHdrHtml(signedFid, parseInt(snum), _mpEffectiveLabel(se.label || ''), 'signed');
            if (se.hora_campo) { var horaEl = sc.querySelector('[name="' + se.hora_campo + '"]'); if (horaEl && horaEl.value) { var hs = shdr.querySelector('.toma-card-hora'); if (hs) hs.textContent = horaEl.value; } }
            if (se.obs_campo) { var obsEl = sc.querySelector('[name="' + se.obs_campo + '"]'); if (obsEl && obsEl.value) { var os = shdr.querySelector('.toma-card-obs'); if (os) os.textContent = obsEl.value; } }
            shdr.addEventListener('click', function() { sc.classList.toggle('tc-expanded'); });
        }
    }
    // Update next card
    if (nextFid) {
        var nc = document.getElementById('tc-' + nextFid);
        if (nc) {
            nc.classList.remove('toma-card--locked'); nc.classList.add('toma-card--active');
            var nhdr = nc.querySelector('.toma-card-hdr');
            if (nhdr) { var ne = window._muestrasMap[nextFid] || {}, nnum = (nc.querySelector('.toma-card-num') || {}).textContent || '1'; nhdr.innerHTML = _mpCardHdrHtml(nextFid, parseInt(nnum), _mpEffectiveLabel(ne.label || ''), 'active'); }
            setTimeout(function() { nc.scrollIntoView({ behavior:'smooth', block:'center' }); }, 150);
        }
    }
    // Refresh progress
    var cards = document.querySelectorAll('.toma-card');
    var fids = [], signed = 0;
    cards.forEach(function(c) { var fid = c.getAttribute('data-firma'); if (fid) { fids.push(fid); if (c.classList.contains('toma-card--signed')) signed++; } });
    _mpUpdateProgress(signed, fids);
}

// Auto-scroll y auto-fill hora a la primera firma pendiente al cargar
document.addEventListener('DOMContentLoaded', function() {
    // Aplicar visibilidad al abrir: antes solo corría al cambiar el examen o al
    // usar el panel de configuración, así que un formulario ya configurado que se
    // abría desde bandeja o historial mostraba secciones que no correspondían.
    _mpApplyExamVisibility(_mpGetExamNames());
    _mpRenderCards();
    setTimeout(function() {
        var fw = document.querySelector('.firma-pro-widget:not([style*="display:none"])');
        if (!fw) return;
        // Siempre auto-llenar la hora (incluso en Minuto 0 sin firmas previas)
        _mpAutoFillHora(fw.dataset.campo);
        // Solo hacer scroll si ya hay alguna firma iniciada
        var hayFirmaIniciada = Object.keys(window._mpSavedFirmas || {}).length > 0
                            || !!document.querySelector('.firma-box img');
        if (hayFirmaIniciada) {
            fw.scrollIntoView({ behavior: 'smooth', block: 'start' });
        }
    }, 350);
});
<?php endif; ?>
/* El formulario va embebido en la estación: no puede abrir el modal de
   corrección por su cuenta, le pide al contenedor que lo haga. */
function pedirCorregirPaciente() {
    try { window.parent.postMessage({ type: 'corregirPaciente' }, '*'); } catch (e) {}
}

window._fpwPreFirma    = <?= json_encode($firmaProfPreguardada) ?>;
window._profNombre     = <?= json_encode($profNombre) ?>;
window._profDocumento  = <?= json_encode($profDocumento) ?>;

// ── Cargar datos de la visita anterior (mismo formulario, mismo paciente) ──
(function() {
    var btn = document.getElementById('btnCargarAnterior');
    if (!btn) return;
    btn.addEventListener('click', function() {
        var base = window.location.href.split('/ver_formulario_enviado.php')[0];
        var msg  = document.getElementById('cargarAnteriorMsg');
        btn.disabled = true;
        btn.innerHTML = '<i class="fas fa-spinner fa-spin me-1"></i>Buscando…';
        var params = new URLSearchParams({
            paciente_id: btn.dataset.paciente,
            formulario_id: btn.dataset.formulario,
            exclude_turno_id: btn.dataset.turno,
        });
        fetch(base + '/modules/turnero/api/get_formulario_anterior.php?' + params)
            .then(function(r) { return r.json(); })
            .then(function(json) {
                btn.disabled = false;
                btn.innerHTML = '<i class="fas fa-history me-1"></i>Cargar datos de la visita anterior';
                if (!json.ok || !json.encontrado) {
                    msg.style.display = '';
                    msg.textContent = 'No hay una visita anterior con este formulario firmado.';
                    return;
                }
                var datos = json.datos_respuestas || {};
                var tocados = [], llenos = 0;

                document.querySelectorAll('[name]').forEach(function(el) {
                    var raw = el.name, isArr = raw.slice(-2) === '[]', name = isArr ? raw.slice(0, -2) : raw;
                    if (!(name in datos)) return;
                    var val = datos[name];
                    if (el.type === 'checkbox') {
                        var arr = Array.isArray(val) ? val : [val];
                        el.checked = arr.includes(el.value);
                    } else if (el.type === 'radio') {
                        el.checked = (el.value === val);
                    } else {
                        el.value = val;
                    }
                    tocados.push(el);
                    llenos++;
                });

                // Asignar el valor a mano no dispara ningún evento, y las secciones
                // condicionales del formulario —tipo de dolor, tipo de cáncer, grupo
                // sanguíneo— se despliegan escuchando 'change' e 'input'. Sin esto los
                // datos entraban pero las secciones seguían ocultas, así que parecía
                // que el botón no hacía nada.
                tocados.forEach(function(el) {
                    ['input', 'change'].forEach(function(tipo) {
                        el.dispatchEvent(new Event(tipo, { bubbles: true }));
                    });
                });

                msg.style.display = '';
                msg.textContent = llenos
                    ? 'Se cargaron ' + llenos + ' dato(s) de la visita ' + (json.turno_codigo || '') + '. Revise y firme.'
                    : 'La visita ' + (json.turno_codigo || '') + ' no tiene datos que correspondan a este formulario.';
            })
            .catch(function() {
                btn.disabled = false;
                btn.innerHTML = '<i class="fas fa-history me-1"></i>Cargar datos de la visita anterior';
                msg.style.display = '';
                msg.textContent = 'Error de conexión.';
            });
    });
})();
<?php if (!empty($_mpSiguienteTomAt)): ?>
// Fix 1: recuperar countdown si el modal fue cerrado y reabierto mientras una toma estaba en curso
(function() {
    var targetMs  = new Date('<?= str_replace(' ', 'T', $_mpSiguienteTomAt) ?>').getTime();
    if (targetMs <= Date.now()) return;
    var pendingId = <?= json_encode($_mpPrimeraPendiente) ?>;
    var entry     = window._muestrasMap && window._muestrasMap[pendingId];
    document.addEventListener('DOMContentLoaded', function() {
        _mpIniciarCountdown(targetMs, entry ? _mpEffectiveLabel(entry.label || '') : null, pendingId);
    });
})();
<?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;
        });
    }

    if (window._profNombre)    datosRespuestas['_pro_nombre'] = window._profNombre;
    if (window._profDocumento) datosRespuestas['_pro_cedula'] = window._profDocumento;
    const payload = isTurnero
        ? { turno_id: parseInt(turnoId), formulario_id: parseInt(formularioId), svg,
            datos_respuestas: datosRespuestas,
            ...(soloPro ? { solo_profesional: true } : {}) }
        : { envio_id: envioId, campo_id: campoId, svg,
            pro_nombre: window._profNombre || null, pro_cedula: window._profDocumento || null };

    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);
                var wrap = document.createElement('div');
                wrap.appendChild(box);
                if (window._profNombre || window._profDocumento) {
                    var ident = document.createElement('div');
                    ident.className = 'firma-pro-ident';
                    ident.style.cssText = 'font-size:13px;line-height:1.5;margin-top:4px';
                    if (window._profNombre) { var s = document.createElement('strong'); s.textContent = window._profNombre; ident.appendChild(s); ident.appendChild(document.createElement('br')); }
                    if (window._profDocumento) { var sp = document.createElement('span'); sp.style.cssText = 'color:#6c757d;font-size:12px'; sp.textContent = 'C.C. ' + window._profDocumento; ident.appendChild(sp); }
                    wrap.appendChild(ident);
                }
                widget.replaceWith(wrap);
            }
        } 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 || {});
    // Guardar con campoId directo (sin _svg) para que get_consentimientos lo cuente
    dr[campoId] = svg;
    // Identidad del firmante por toma (puede cambiar de profesional entre tomas)
    if (window._profNombre)    { dr[campoId + '_pro_nombre'] = window._profNombre; dr['_pro_nombre'] = window._profNombre; }
    if (window._profDocumento) { dr[campoId + '_pro_cedula'] = window._profDocumento; dr['_pro_cedula'] = window._profDocumento; }
    (window._mpSavedFirmas = window._mpSavedFirmas||{})[campoId] = svg;
    // Fix 5: cachear hora en el momento de guardar para el resumen final
    (window._mpTomaData = window._mpTomaData||{})[campoId] = {
        hora: entry.hora_campo ? (dr[entry.hora_campo] || '') : ''
    };

    var turnoId      = parseInt(widget.dataset.turno,      10);
    var formularioId = parseInt(widget.dataset.formulario, 10);
    var base = window.location.href.split('/ver_formulario_enviado.php')[0];

    fetch(base + '/modules/turnero/api/guardar_toma.php', {
        method:'POST', headers:{'Content-Type':'application/json'},
        body: JSON.stringify({
            turno_id:        turnoId,
            formulario_id:   formularioId,
            campo_id:        campoId,
            svg:             svg,
            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);
        var wrap = document.createElement('div');
        wrap.appendChild(box);
        if (window._profNombre || window._profDocumento) {
            var ident = document.createElement('div');
            ident.className = 'firma-pro-ident';
            ident.style.cssText = 'font-size:13px;line-height:1.5;margin-top:4px';
            if (window._profNombre) { var s = document.createElement('strong'); s.textContent = window._profNombre; ident.appendChild(s); ident.appendChild(document.createElement('br')); }
            if (window._profDocumento) { var sp = document.createElement('span'); sp.style.cssText = 'color:#6c757d;font-size:12px'; sp.textContent = 'C.C. ' + window._profDocumento; ident.appendChild(sp); }
            wrap.appendChild(ident);
        }
        widget.replaceWith(wrap);

        // Actualizar tarjetas
        _mpRefreshCards(campoId, data.completado ? null : entry.next_firma_id);

        if (data.completado) {
            // Toma completada: mostrar resumen 10 s y luego cerrar modal
            var cd = document.getElementById('mp-countdown');
            if (cd) { clearInterval(cd._mpTick); cd.remove(); }
            _mpMostrarResumen(entry.exam_type || 'Examen', 10);
            setTimeout(function() {
                try { window.parent.postMessage({ type: 'turneroFirmado' }, '*'); } catch(e) {}
            }, 10000);
        } else if (data.siguiente_toma_at) {
            // Fix 2: pasar timestamp exacto; evita el redondeo de Math.round
            var targetMs = new Date(data.siguiente_toma_at.replace(' ', 'T')).getTime();
            _mpIniciarCountdown(targetMs, _mpEffectiveLabel(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) : null;
            if (!nw) nw = document.querySelector('.firma-pro-widget:not([style*="display:none"])');
            if (nw) { nw.style.display = ''; nw.scrollIntoView({ behavior:'smooth', block:'center' }); _mpAutoFillHora(nw.dataset.campo); }
        } else {
            // Último firma del grupo pero hay más exámenes pendientes; desbloquear siguiente
            var nw = document.querySelector('.firma-pro-widget[style*="display:none"]');
            if (nw) {
                nw.style.display = '';
                nw.scrollIntoView({ behavior:'smooth', block:'center' });
                _mpAutoFillHora(nw.dataset.campo);
            } else {
                // Sin más firmas pendientes → cerrar
                // Fix 4: unificar delay con el caso data.completado (ambos 2000ms)
                setTimeout(function() {
                    try { window.parent.postMessage({ type: 'turneroFirmado' }, '*'); } catch(e) {}
                }, 2000);
            }
        }
    })
    .catch(function() {
        if (msgEl) msgEl.innerHTML = '<span class="text-danger">Error de conexión.</span>';
    });
}

function _mpPlayBeep() {
    try {
        var ctx  = new (window.AudioContext || window.webkitAudioContext)();
        var comp = ctx.createDynamicsCompressor();
        comp.threshold.value = -6; comp.ratio.value = 12; comp.attack.value = 0;
        comp.connect(ctx.destination);
        // 3 ciclos de alarma, cada ciclo 3 notas; 2 osciladores por nota para más volumen
        var notes = [1200, 880, 1200];
        for (var cycle = 0; cycle < 3; cycle++) {
            notes.forEach(function(freq, i) {
                var t0 = ctx.currentTime + cycle * 1.0 + i * 0.28;
                [0, 6].forEach(function(detune) {           // dos osciladores ligeramente desafinados
                    var osc = ctx.createOscillator();
                    var g   = ctx.createGain();
                    osc.type = 'sawtooth';
                    osc.frequency.value = freq;
                    osc.detune.value    = detune;
                    osc.connect(g); g.connect(comp);
                    g.gain.setValueAtTime(1.0, t0);
                    g.gain.exponentialRampToValueAtTime(0.001, t0 + 0.24);
                    osc.start(t0); osc.stop(t0 + 0.24);
                });
            });
        }
    } catch(e) {}
}

// Fix 2: acepta timestamp (ms > 1e10) o minutos (número pequeño)
function _mpIniciarCountdown(targetMsOrMins, nextLabel, nextFirmaId) {
    var targetMs = targetMsOrMins > 1e10 ? targetMsOrMins : Date.now() + targetMsOrMins * 60000;
    var box = document.getElementById('mp-countdown');
    if (!box) {
        box = document.createElement('div');
        box.id = 'mp-countdown';
        box.style.cssText = 'position:sticky;bottom:0;left:0;right:0;background:#1565c0;color:#fff;'
            + 'padding:14px 20px;margin:16px -14px -12px;text-align:center;z-index:60;'
            + 'box-shadow:0 -4px 20px rgba(21,101,192,.35)';
        document.querySelector('.doc-body').appendChild(box);
    }
    if (box._mpTick) clearInterval(box._mpTick);

    // Notificar al padre que bloquee el cierre
    try { window.parent.postMessage({ type: 'tomaProgresivaIniciada', targetMs: targetMs, label: nextLabel || '' }, '*'); } catch(e) {}

    var _tcCdEl = nextFirmaId ? document.getElementById('tc-cd-' + nextFirmaId) : null;

    box._mpTick = setInterval(function() {
        var left = targetMs - Date.now();
        if (left <= 0) {
            if (_tcCdEl) _tcCdEl.textContent = '';

            clearInterval(box._mpTick);
            box.style.background = '#dc3545';
            box.style.animation = 'mp-pulse 1s ease-in-out infinite';
            box.innerHTML = '<div style="font-size:1.1rem;font-weight:700"><i class="fas fa-bell me-2"></i>¡Hora de la siguiente muestra!</div>'
                + (nextLabel ? '<div style="font-size:.85rem;opacity:.9;margin-top:4px">' + nextLabel + '</div>' : '');
            if (navigator.vibrate) navigator.vibrate([900,150,900,150,900,150,900,150,900]);
            _mpPlayBeep();
            try { window.parent.postMessage({ type: 'tomaProgresivaAlerta', label: nextLabel || '¡Siguiente muestra!' }, '*'); } catch(e) {}
            // Desbloquear y mostrar siguiente canvas
            var nw = nextFirmaId ? document.getElementById('fpw-' + nextFirmaId) : null;
            if (!nw) nw = document.querySelector('.firma-pro-widget[style*="display:none"]');
            if (nw) {
                nw.style.display = '';
                nw.style.outline = '3px solid #dc3545';
                nw.style.borderRadius = '8px';
                _mpAutoFillHora(nw.dataset.campo);
                setTimeout(function() { nw.scrollIntoView({ behavior:'smooth', block:'center' }); }, 120);
            }
            return;
        }
        var m = Math.floor(left / 60000);
        var s = Math.floor((left % 60000) / 1000);
        var pct = 0;
        box.style.background = pct < 15 ? '#f59e0b' : '#1565c0';
        box.style.animation = '';
        box.innerHTML = '<div style="font-size:.75rem;opacity:.85;margin-bottom:4px;text-transform:uppercase;letter-spacing:.05em">'
            + '<i class="fas fa-clock me-1"></i>Próxima muestra' + (nextLabel ? ' · ' + nextLabel : '') + '</div>'
            + '<div style="font-size:2.4rem;font-weight:900;font-family:monospace;line-height:1;letter-spacing:.04em">'
            + String(m).padStart(2,'0') + '<span style="opacity:.6;animation:mp-pulse .8s infinite">:</span>' + String(s).padStart(2,'0')
            + '</div>';
        // Mini countdown en la tarjeta de la próxima toma
        if (_tcCdEl) _tcCdEl.textContent = '⏱ ' + String(m).padStart(2,'0') + ':' + String(s).padStart(2,'0');
    }, 1000);
}

function _mpMostrarResumen(examType, segs) {
    if (document.getElementById('mp-resumen')) return;
    segs = segs || 10;
    // Recopilar horas y resultados de las tomas visibles
    var rows = [];
    if (window._muestrasMap) {
        Object.keys(window._muestrasMap).forEach(function(fid) {
            var entry = window._muestrasMap[fid];
            if (!entry || entry.exam_type !== examType) return;
            // Fix 5: usar hora cacheada al momento de guardar; evita traversal DOM frágil
            var cached  = window._mpTomaData && window._mpTomaData[fid];
            var horaVal = (cached && cached.hora) || '';
            if (!horaVal && entry.hora_campo) {
                var horaEl = document.querySelector('[name="' + entry.hora_campo + '"]');
                horaVal = horaEl ? horaEl.value : '';
            }
            rows.push({ hora: horaVal, firmado: !!(window._mpSavedFirmas && window._mpSavedFirmas[fid]) });
        });
    }
    var wrap = document.createElement('div');
    wrap.id = 'mp-resumen';
    wrap.className = 'alert alert-success mt-4';
    var rowsHtml = rows.length
        ? '<table class="table table-sm mt-2 mb-0" style="font-size:.82rem"><thead><tr>'
          + '<th>Hora</th><th></th></tr></thead><tbody>'
          + rows.map(function(r) {
              return '<tr><td>' + (r.hora || '—') + '</td>'
                   + '<td style="color:#198754"><i class="fas fa-check-circle"></i></td></tr>';
          }).join('') + '</tbody></table>' : '';
    wrap.innerHTML = '<div class="d-flex align-items-center gap-2"><i class="fas fa-check-double fs-5"></i>'
        + '<div><strong>' + (examType || 'Examen') + ' completado.</strong><br>'
        + '<small>Todas las tomas registradas. Cerrando en <span id="mp-resumen-seg">' + segs + '</span>s…</small></div></div>'
        + rowsHtml
        + '<div style="margin-top:10px;height:5px;background:#c3e6cb;border-radius:3px">'
        + '<div id="mp-resumen-bar" style="height:100%;width:100%;background:#198754;border-radius:3px;'
        + 'transition:width ' + segs + 's linear"></div></div>';
    document.querySelector('.doc-body').appendChild(wrap);
    // Animar barra y cuenta regresiva
    requestAnimationFrame(function() { requestAnimationFrame(function() {
        var bar = document.getElementById('mp-resumen-bar');
        if (bar) bar.style.width = '0%';
    }); });
    var left = segs;
    var tick = setInterval(function() {
        left--;
        var el = document.getElementById('mp-resumen-seg');
        if (el) el.textContent = left;
        if (left <= 0) clearInterval(tick);
    }, 1000);
    window.addEventListener('unload', function() { clearInterval(tick); });
    // Ocultar countdown si queda
    var cd = document.getElementById('mp-countdown');
    if (cd) { clearInterval(cd._mpTick); cd.remove(); }
}

/* ── 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ó

        // Si hay canvas del profesional en el form, guardar como borrador (el profesional finaliza)
        var hayCanvasPro = !!document.querySelector('.firma-pro-widget');
        var payload = hayCanvasPro
            ? { datos_respuestas: campos }
            : { firma_svg: png, datos_respuestas: campos };

        fetch(window.location.href, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(payload)
        })
        .then(function(r) { return r.json(); })
        .then(function(data) {
            if (data.ok) {
                // Ocultar todos los canvas de firma del paciente (ya firmó)
                document.querySelectorAll('.turnero-firma-item').forEach(function(w) {
                    w.style.display = 'none';
                });
                if (hayCanvasPro) {
                    // Mostrar mensaje de éxito parcial y hacer scroll al canvas del profesional
                    msgEl.innerHTML = '<span class="text-success"><i class="fas fa-check me-1"></i>Firma del paciente guardada. Ahora firma el profesional.</span>';
                    var fpw = document.querySelector('.firma-pro-widget');
                    if (fpw) setTimeout(function() { fpw.scrollIntoView({ behavior:'smooth', block:'center' }); }, 150);
                } else {
                    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;
        });
    });
});
<?php if ($_formSoloPro): ?>
(function() {
    var btn = document.getElementById('btn-completar-pro');
    if (!btn) return;
    btn.addEventListener('click', function() {
        var msgEl = document.getElementById('btn-completar-pro-msg');
        btn.disabled = true;
        btn.innerHTML = '<i class="fas fa-spinner fa-spin me-2"></i>Guardando...';
        var campos = {};
        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(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;
        });
        fetch(window.location.href, {
            method: 'POST', headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ mp_completar: true, datos_respuestas: campos })
        })
        .then(function(r) { return r.json(); })
        .then(function(d) {
            if (d.ok) {
                document.getElementById('btn-completar-pro-wrap').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>Completado correctamente.</strong></div>';
                document.getElementById('btn-completar-pro-wrap').parentNode.appendChild(ok);
                try { window.parent.postMessage({ type: 'turneroFirmado' }, '*'); } catch(e) {}
            } else {
                msgEl.innerHTML = '<span class="text-danger">' + (d.error || 'Error al guardar') + '</span>';
                btn.disabled = false;
                btn.innerHTML = '<i class="fas fa-check-circle me-2"></i>Guardar y completar';
            }
        })
        .catch(function() {
            msgEl.innerHTML = '<span class="text-danger">Error de conexión.</span>';
            btn.disabled = false;
            btn.innerHTML = '<i class="fas fa-check-circle me-2"></i>Guardar y completar';
        });
    });
})();
<?php endif; ?>
<?php if ($modoTurnero && $embebido && !empty($_mpMap) && $envio['estado'] === 'en_progreso'): ?>
(function() {
    var btn = document.getElementById('btn-guardar-tiempos');
    if (!btn) return;
    btn.addEventListener('click', function() {
        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;
        });
        btn.disabled = true;
        btn.innerHTML = '<i class="fas fa-spinner fa-spin me-1"></i>Guardando...';
        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(d) {
            if (d.ok) {
                // Actualizar obs en headers de tarjetas firmadas
                if (window._muestrasMap) {
                    document.querySelectorAll('.toma-card--signed').forEach(function(card) {
                        var fid = card.getAttribute('data-firma');
                        var se = fid && window._muestrasMap[fid];
                        if (se && se.obs_campo) {
                            var obsEl = card.querySelector('[name="' + se.obs_campo + '"]');
                            var os = card.querySelector('.toma-card-obs');
                            if (obsEl && os) os.textContent = obsEl.value || '';
                        }
                    });
                }
                btn.innerHTML = '<i class="fas fa-check me-1"></i>Guardado';
                setTimeout(function(){ btn.disabled=false; btn.innerHTML='<i class="fas fa-save me-1"></i>Guardar tiempos'; }, 2000);
            } else {
                btn.disabled = false;
                btn.innerHTML = '<i class="fas fa-save me-1"></i>Guardar tiempos';
            }
        })
        .catch(function(){ btn.disabled=false; btn.innerHTML='<i class="fas fa-save me-1"></i>Guardar tiempos'; });
    });
})();
<?php endif; ?>

<?php if ($modoTurnero && $embebido && !empty($_mpMap) && $envio['estado'] === 'en_progreso'): ?>
(function() {
    var base  = window.location.href.split('/ver_formulario_enviado.php')[0];
    var token = <?= json_encode($tokenTurnero) ?>;
    document.querySelectorAll('.btn-olvidar-toma').forEach(function(btn) {
        btn.addEventListener('click', function() {
            var hc   = btn.dataset.hc;
            var fids = JSON.parse(btn.dataset.fids || '[]');
            var campos = [hc];
            fids.forEach(function(fid) { campos.push(fid); campos.push(fid + '_svg'); });
            if (!confirm('¿Olvidar esta toma? Se borrará la firma y podrás editar el tiempo.')) return;
            btn.disabled = true;
            fetch(base + '/modules/turnero/api/resetear_toma.php', {
                method: 'POST', headers: {'Content-Type':'application/json'},
                body: JSON.stringify({ token: token, campos: campos })
            })
            .then(function(r) { return r.json(); })
            .then(function(d) {
                if (d.ok) { window.location.reload(); }
                else { alert(d.error || 'Error al olvidar'); btn.disabled = false; }
            })
            .catch(function(e) { alert(e.message); btn.disabled = false; });
        });
    });
})();
<?php endif; ?>

<?php if ($modoTurnero && $embebido && !empty($_mpMap) && !in_array($envio['estado'], ['firmado','cerrado_anticipado'])): ?>
(function() {
    var btn    = document.getElementById('btn-cerrar-anticip');
    var modal  = document.getElementById('modal-cerrar-anticip');
    if (!btn || !modal) return;
    btn.addEventListener('click', function() {
        document.getElementById('mca-motivo').value = '';
        document.getElementById('mca-error').classList.add('d-none');
        modal.style.display = 'flex';
        setTimeout(function() { document.getElementById('mca-motivo').focus(); }, 50);
    });
    document.getElementById('mca-cancelar').addEventListener('click', function() {
        modal.style.display = 'none';
    });
    document.getElementById('mca-confirmar').addEventListener('click', function() {
        var motivo = document.getElementById('mca-motivo').value.trim();
        var errEl  = document.getElementById('mca-error');
        if (!motivo) { errEl.textContent = 'El motivo es obligatorio.'; errEl.classList.remove('d-none'); return; }
        errEl.classList.add('d-none');
        var confirmBtn = this;
        confirmBtn.disabled = true;
        confirmBtn.innerHTML = '<i class="fas fa-spinner fa-spin me-1"></i>Guardando...';
        fetch(window.location.href, {
            method: 'POST', headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ mp_cerrar_anticipado: true, motivo: motivo })
        })
        .then(function(r) { return r.json(); })
        .then(function(d) {
            if (d.ok) {
                modal.style.display = 'none';
                try { window.parent.postMessage({ type: 'turneroFirmado' }, '*'); } catch(e) {}
                location.reload();
            } else {
                errEl.textContent = d.error || 'Error al guardar.';
                errEl.classList.remove('d-none');
                confirmBtn.disabled = false;
                confirmBtn.innerHTML = '<i class="fas fa-times-circle me-1"></i>Confirmar cierre';
            }
        })
        .catch(function() {
            errEl.textContent = 'Error de conexión.';
            errEl.classList.remove('d-none');
            confirmBtn.disabled = false;
            confirmBtn.innerHTML = '<i class="fas fa-times-circle me-1"></i>Confirmar cierre';
        });
    });
})();
<?php endif; ?>
</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">
                <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>

<script>
// ── Topaz SigWeb ────────────────────────────────────────────────────
const topaz = (() => {
    const SIGWEB_URL = '<?= rtrim(defined('APP_URL') ? APP_URL : '', '/') ?>/assets/js/SigWebTablet.js';
    let _loaded = false, _target = 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);
        });
    }

    // Detectar disponibilidad al cargar; ocultar botones si no hay pad, o marcar en línea
    _loadScript().then(ok => {
        document.querySelectorAll('.btn-topaz').forEach(b => {
            if (!ok) { b.style.display = 'none'; return; }
            b.innerHTML = '<span style="display:inline-block;width:8px;height:8px;border-radius:50%;background:#22c55e;margin-right:5px;vertical-align:middle;box-shadow:0 0 4px #22c55e"></span>'
                + '<i class="fas fa-tablet-alt me-1"></i>Tableta en línea';
        });
    });

    function _toast(msg) {
        const t = document.createElement('div');
        t.style.cssText = 'position:fixed;bottom:24px;left:50%;transform:translateX(-50%);'
            + 'background:#1e293b;color:#fff;padding:12px 22px;border-radius:10px;'
            + 'font-size:.88rem;z-index:99999;box-shadow:0 4px 16px rgba(0,0,0,.3);'
            + 'max-width:90vw;text-align:center;line-height:1.5';
        t.innerHTML = '<i class="fas fa-exclamation-circle me-2" style="color:#f59e0b"></i>' + msg;
        document.body.appendChild(t);
        setTimeout(() => t.remove(), 5000);
    }

    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) {
            _toast('Pad biométrico no disponible en este equipo.<br>Instala el servicio Topaz SigWeb o usa la firma manual.');
            return;
        }
        _target = target;
        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) {
            _toast('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);

        let _lastPts = -1;
        _poll = setInterval(() => {
            try {
                const pts = NumberOfTabletPoints();
                document.getElementById('topaz-pts').textContent = pts;
                document.getElementById('topaz-btn-aceptar').disabled = pts === 0;
                if (pts > 0) {
                    _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 _cerrar() {
        _stopPoll();
        try { SetTabletState(0, _tmr); } catch(e) {}
        _tmr = 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() {
        const target = _target;
        _cerrar();
        try {
            GetSigImageB64(function(b64) {
                if (!b64) { _toast('No se capturó ninguna firma.'); return; }
                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;
            });
        } catch(e) {
            _toast('Error al capturar la firma: ' + e.message);
        }
    }

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

<?php if ($modoTurnero && $modoEditar): ?>
// ── Condiciones de visibilidad (secciones condicionales) ──────
(function() {
    var esquema = <?= json_encode($esquema, JSON_UNESCAPED_UNICODE) ?>;
    var secciones = [], secActual = null;
    esquema.forEach(function(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);
        }
    });
    var condicionales = secciones.filter(function(s) { return s.condicion; });
    if (!condicionales.length) return;

    function evaluar(evt) {
        condicionales.forEach(function(sec) {
            var cond = sec.condicion;
            var valoresCond = cond.valores ? cond.valores : (cond.valor ? [cond.valor] : []);
            var ctrl = document.querySelector('[data-campo-id="' + cond.campo_id + '"]');
            if (!ctrl) return;
            var activo = false;
            var checks = ctrl.querySelectorAll('input[type="checkbox"]');
            if (checks.length) {
                checks.forEach(function(cb) { if (valoresCond.includes(cb.value) && cb.checked) activo = true; });
            } else {
                var radio = ctrl.querySelector('input[type="radio"]:checked');
                var sel   = ctrl.querySelector('select');
                var txt   = ctrl.querySelector('input[type="text"], textarea');
                // Un campo vinculado se muestra como texto, no como control:
                // es el caso del sexo, que gobierna la sección obstétrica.
                var lnk   = ctrl.classList.contains('campo-linked')
                          ? ctrl.querySelector('.campo-linked-valor') : null;
                var v = radio ? radio.value
                      : sel   ? sel.value
                      : lnk   ? lnk.textContent.trim()
                      : txt   ? txt.value : '';
                activo = valoresCond.includes(v);
            }
            var ids = [sec.sepId].concat(sec.elemIds);
            ids.forEach(function(id) {
                var el = document.querySelector('[data-campo-id="' + id + '"]');
                if (!el) return;
                // El PHP marca con display:none los campos de secciones cuya
                // condición no se cumple. Esta función anima con max-height y
                // opacity, así que sin quitar el display el campo nunca aparece.
                if (activo) el.style.removeProperty('display');
                el.style.transition    = 'opacity .2s, max-height .3s';
                el.style.maxHeight     = activo ? '2000px' : '0';
                el.style.opacity       = activo ? '1' : '0';
                el.style.overflow      = activo ? '' : 'hidden';
                el.style.pointerEvents = activo ? '' : 'none';
                if (activo) { el.style.removeProperty('margin'); el.style.removeProperty('padding'); }
                else        { el.style.setProperty('margin','0','important'); el.style.setProperty('padding','0','important'); }
            });
        });

        // Toma progresiva: scroll a la primera firma pendiente
        if (window._muestrasMap) {
            setTimeout(function() {
                var hayFirmaIniciada = Object.keys(window._mpSavedFirmas || {}).length > 0
                                    || !!document.querySelector('.firma-box img');
                var fw = hayFirmaIniciada
                    ? document.querySelector('.firma-pro-widget:not([style*="display:none"])') : null;
                if (fw) {
                    fw.scrollIntoView({ behavior: 'smooth', block: 'start' });
                } else if (evt) {
                    // Firma no está en el DOM (examen recién seleccionado) → guardar y recargar
                    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;
                    });
                    fetch(window.location.href, {
                        method:'POST', headers:{'Content-Type':'application/json'},
                        body: JSON.stringify({ datos_respuestas: dr })
                    }).then(function() { window.location.reload(); })
                      .catch(function()  { window.location.reload(); });
                }
            }, 400);
        }
    }

    var ctrlIds = [...new Set(condicionales.map(function(s) { return s.condicion.campo_id; }))];
    ctrlIds.forEach(function(cid) {
        var el = document.querySelector('[data-campo-id="' + cid + '"]');
        if (el) el.addEventListener('change', evaluar);
    });
    evaluar();
})();
// ── 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>
<?php if ($autoprint): ?>
<script src="https://cdn.jsdelivr.net/npm/qz-tray@2.2.4/qz-tray.js"></script>
<script src="/assets/js/qz-print.js"></script>
<script>
window.addEventListener('load', function() {
    setTimeout(function() {
        if (typeof qz !== 'undefined') { qzPrint(); }
        else { window.print(); }
    }, 800);
});
</script>
<?php endif; ?>
</body>
</html>
