/* ============================================================
   VISTA PREVIA DEL CV — DISEÑO PROFESIONAL
   v3.0 — Paginación A4 + Marca de Agua Div Overlay
   ============================================================ */

/* ── Contenedor externo de la vista previa ── */
#vistaPrevia {
    transition: opacity 0.5s ease, transform 0.5s ease;
    opacity: 0;
    transform: translateY(12px);
    /* En móvil/tablet: margen superior para separar del formulario */
    margin-top: 30px;
}

#vistaPrevia.visible {
    opacity: 1;
    transform: translateY(0);
}

/* En desktop: sin margen (el panel derecho ya tiene padding) */
@media (min-width: 1200px) {
    #vistaPrevia {
        margin-top: 0;
        /* Ocupa todo el ancho del panel */
        width: 100%;
    }
}

/* ── Wrapper de páginas A4 ── */
.cv-pages-wrapper {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0;
    padding: 0;
    width: 100%;
}

/* ============================================================
   HOJA A4 — Formato estricto 210×297mm
   ─────────────────────────────────────────────────────────────
   PRINCIPIO CLAVE:
   · aspect-ratio: 210 / 297 garantiza la proporción A4 exacta
     independientemente del ancho del contenedor.
   · overflow: hidden asegura que el contenido NUNCA desborde
     la hoja — si hay más contenido, se crea una nueva página.
   · La hoja "flota" sobre la mesa de trabajo con sombra realista.
   ============================================================ */
.cv-a4-page {
    position: relative;
    width: 100%;
    max-width: 700px;

    /* ── FORMATO A4 ESTRICTO ──
       210mm / 297mm = 0.7071... → aspect-ratio: 210 / 297
       El navegador calcula la altura automáticamente.
       Esto garantiza que la hoja siempre tenga proporciones A4 reales. */
    aspect-ratio: 210 / 297;

    /* Hoja blanca con sombra de "papel flotante" */
    background: #ffffff;
    border-radius: 2px;

    /* Sombra multicapa: realismo de papel sobre mesa */
    box-shadow:
        0 1px 1px rgba(0, 0, 0, 0.08),
        0 4px 8px rgba(0, 0, 0, 0.10),
        0 12px 32px rgba(0, 0, 0, 0.14),
        0 24px 56px rgba(0, 0, 0, 0.10);

    /* CRÍTICO: el contenido nunca desborda la hoja A4 */
    overflow: hidden;

    /* Transición suave al aparecer/actualizarse */
    transition: box-shadow 0.3s ease, transform 0.3s ease;

    /* Evitar cortes en impresión */
    break-inside: avoid;
    page-break-inside: avoid;
}

/* ── Desktop: el JS asigna width/height inline vía transform:scale() ──
   NO definir width/height fijos aquí: causaría que la página ocupe
   794×1123px antes de que el JS aplique la escala, desbordando el panel
   y colapsando el viewport (height:0 por race condition en el layout).
   Solo neutralizamos max-width y aspect-ratio para que el JS tenga
   control total sobre las dimensiones. */
@media (min-width: 1200px) {
    .cv-a4-page {
        max-width: none;
        aspect-ratio: unset;
        flex-shrink: 0;
        /* width y height los asigna el JS inline tras medir el panel */
    }
}

/* Efecto hover sutil en desktop — la hoja "se eleva" */
@media (min-width: 1200px) {
    .cv-a4-page:hover {
        box-shadow:
            0 2px 2px rgba(0, 0, 0, 0.08),
            0 8px 16px rgba(0, 0, 0, 0.12),
            0 20px 48px rgba(0, 0, 0, 0.16),
            0 32px 72px rgba(0, 0, 0, 0.10);
        /* Sin translateY: el transform ya lo gestiona el JS con scale() */
    }
}

/* Primera página: sin margen superior extra */
.cv-a4-page:first-child {
    margin-top: 0;
}

/* ── Separador visual entre páginas A4 ──
   Aparece entre cada hoja para indicar el salto de página.
   Se inserta dinámicamente desde JS. */
.cv-page-separator {
    display: flex;
    align-items: center;
    gap: 12px;
    width: 100%;
    max-width: 700px;
    padding: 14px 0;
    opacity: 0.65;
    transition: opacity 0.3s ease;
}

.cv-page-separator:hover {
    opacity: 1;
}

.cv-page-separator-line {
    flex: 1;
    height: 1px;
    background: linear-gradient(90deg, transparent, rgba(94, 129, 172, 0.5), transparent);
}

.cv-page-separator-label {
    font-size: 0.65rem;
    font-weight: 700;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: rgba(122, 163, 212, 0.7);
    font-family: 'Inter', system-ui, sans-serif;
    white-space: nowrap;
    background: rgba(26, 31, 46, 0.8);
    padding: 3px 10px;
    border-radius: 10px;
    border: 1px solid rgba(94, 129, 172, 0.2);
}

/* En móvil: separadores más compactos */
@media (max-width: 767px) {
    .cv-page-separator {
        padding: 10px 0;
    }
}

/* ============================================================
   MARCA DE AGUA — DIV OVERLAY (compatible con todos los motores)
   Posicionado absolutamente sobre cada página A4.
   pointer-events: none → no interfiere con la interacción.
   user-select: none → no se puede seleccionar el texto.
   ============================================================ */
.cv-watermark-overlay {
    position: absolute;
    inset: 0;
    z-index: 20;
    pointer-events: none;
    user-select: none;
    -webkit-user-select: none;
    overflow: hidden;

    /* Patrón de marca de agua repetido en diagonal */
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0;
}

/* Texto de marca de agua — múltiples filas para cubrir toda la página */
.cv-watermark-text {
    display: block;
    width: 200%;
    text-align: center;
    font-size: clamp(1.8rem, 5vw, 3rem);
    font-weight: 900;
    letter-spacing: 0.3em;
    color: rgba(74, 111, 165, 0.30);
    font-family: 'Inter', system-ui, sans-serif;
    white-space: nowrap;
    transform: rotate(-35deg);
    transform-origin: center center;
    line-height: 3.5;
    text-transform: uppercase;
    text-shadow: 0 1px 4px rgba(74, 111, 165, 0.10);
}

/* Wrapper interno para el patrón repetido */
.cv-watermark-pattern {
    position: absolute;
    /*
     * FIX 2 — Marca de agua ausente en primera página (móvil):
     * El problema anterior: top:-60% / left:-60% son relativos a la altura
     * de la página. En páginas cortas (primera página en móvil), el patrón
     * rotado quedaba completamente fuera del área visible de overflow:hidden.
     *
     * Solución: centrar el patrón con translate(-50%,-50%) desde el punto
     * central de la página (top:50%, left:50%). Así el patrón siempre está
     * centrado sobre la página independientemente de su altura, y el tamaño
     * 300%×300% garantiza cobertura total incluso tras la rotación de 35°.
     */
    top: 50%;
    left: 50%;
    width: 300%;
    height: 300%;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0;
    /* Centrado + rotación en una sola transformación */
    transform: translate(-50%, -50%) rotate(-35deg);
    transform-origin: center center;
}

.cv-watermark-row {
    display: flex;
    gap: 48px;
    white-space: nowrap;
    line-height: 1;
    padding: 22px 0;
    width: 100%;
    justify-content: center;
}

.cv-watermark-item {
    font-size: clamp(1.6rem, 3vw, 2.4rem);
    font-weight: 900;
    letter-spacing: 0.3em;
    /* Fix 4: opacidad aumentada a 0.22 para visibilidad real en desktop.
       El valor anterior (0.14) era invisible sobre el fondo blanco del CV.
       0.22 es el umbral mínimo perceptible sin interferir con la lectura. */
    color: rgba(74, 111, 165, 0.40);
    font-family: 'Inter', system-ui, sans-serif;
    text-transform: uppercase;
    user-select: none;
    -webkit-user-select: none;
    /* Sombra sutil para mejorar contraste en pantallas de alta densidad */
    text-shadow: 0 1px 4px rgba(74, 111, 165, 0.12);
}

/* ── Wrapper con posición relativa para el overlay ── */
/* FIX AUDITORÍA — CAMINO B (tipografía real = tipografía del PDF):
   Antes: 'Georgia' (serif), ~15-20% más ancha que 'Arial' por carácter.
   Esto provocaba MÁS saltos de línea en párrafos largos (Perfil,
   descripciones de Experiencia/Educación) que en el PDF real, que
   usa Arial/Helvetica (ver formal.html). Más saltos de línea = más
   altura medida = páginas de más que no existen en el PDF descargado.
   Ahora la vista previa usa la MISMA familia tipográfica que el PDF,
   así el número de líneas —y por tanto la altura— coincide exactamente
   entre lo que el usuario ve en pantalla y lo que recibe al pagar. */
#cvContent,
#cvContent-desktop {
    position: relative;
    font-family: 'Arial', 'Helvetica', sans-serif;
    color: #2c2c2c;
    background: transparent;
    max-width: 700px;
    margin: 0 auto;
    user-select: none;
    -webkit-user-select: none;
    width: 100%;
}


/* ── CABECERA DEL CV ──
   v4.0 — REDISEÑO PREMIUM (Fase 2), en paridad con formal.html:
   misma paleta (#123655 / #2f6fb0), mismo header-top con monograma,
   mismo tratamiento de contacto con iconos SVG. Mantiene EXACTAMENTE
   los mismos font-size/line-height que ya estaban alineados al PDF
   real (ver notas "FIX AUDITORÍA — CAMINO B" heredadas) para no
   desincronizar la medición del paginador embebido en cv.js. */
.cv-header {
    display: flex;
    align-items: stretch;
    min-height: 120px;
    break-inside: avoid;
    page-break-inside: avoid;
    /* FIX overflow: el flex child necesita min-width:0 para respetar overflow:hidden del padre */
    min-width: 0;
}

/* v6.0 (Rediseño "Ejecutivo Institucional" — espejo exacto de
   formal.html): color de marca SÓLIDO en vez de degradado — misma
   identidad visual en pantalla y en el PDF final, sin depender de
   ningún parche de impresión. */
.cv-accent-bar {
    width: 6px;
    flex-shrink: 0;
    background: #123655;
    border-radius: 0;
}

/* v6.0: fondo blanco puro (antes #f8f9fc) — espejo de
   .cv-header-content en formal.html: look de documento redactado,
   no de "tarjeta de app". El borde inferior fino se mantiene como
   separador visual. */
.cv-header-content {
    flex: 1;
    /* FIX overflow: min-width:0 es crítico en flex para que el contenido no expanda el contenedor */
    min-width: 0;
    padding: 28px 32px 22px;
    background: #ffffff;
    border-bottom: 1px solid #e8ecf2;
    /* FIX overflow: texto largo sin espacios no desborda la cabecera */
    overflow: hidden;
}


/* Fila superior: identidad (nombre+título) + monograma con iniciales,
   igual que .cv-header-top en formal.html. */
.cv-header-top {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 16px;
}

.cv-header-identity {
    flex: 1;
    min-width: 0;
}

/* v6.0: círculo con color de marca SÓLIDO (espejo del monograma
   rediseñado en formal.html) — mismo contraste en color y en
   escala de grises. */
.cv-monogram {
    width: 56px;
    height: 56px;
    min-width: 56px;
    border-radius: 50%;
    background: #123655;
    color: #fff;

    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1.15rem;
    font-weight: 700;
    letter-spacing: 0.02em;
    flex-shrink: 0;
    font-family: 'Arial', 'Helvetica', sans-serif;
}

.cv-header-content h1 {
    font-size: clamp(1.6rem, 4vw, 2.4rem);
    color: #123655;
    margin: 0 0 4px;
    letter-spacing: -0.5px;
    font-weight: 800;
    line-height: 1.1;
    /* FIX overflow: nombre muy largo */
    overflow-wrap: break-word;
    word-break: break-word;
}

/* FIX AUDITORÍA — CAMINO B: font-size alineado al PDF real (.cv-title
   12pt = 16px = 1rem). Font-family Inter → Arial (ver nota #cvContent).
   v4.0: se retira la cursiva (paridad con .cv-title de formal.html). */
.cv-header-content h3 {
    font-size: 1rem;
    color: #2f6fb0;
    font-weight: 500;
    font-style: normal;
    letter-spacing: 0.01em;
    margin: 0 0 14px;
    font-family: 'Arial', 'Helvetica', sans-serif;
    /* FIX overflow: título profesional largo */
    overflow-wrap: break-word;
    word-break: break-word;
}


/* FIX AUDITORÍA — CAMINO B: font-size alineado al PDF real (.cv-contact
   8.5pt = 11.33px ≈ 0.71rem). Font-family Inter → Arial.
   v7.0 (Consolidación de paleta — Fase 6, espejo de formal.html):
   antes #5a6a7e — ahora #3d4a58, idéntico a --color-muted del PDF real
   (~8.3:1 de contraste sobre blanco, ver nota completa en formal.html
   :root). Mantiene la vista previa web y el PDF 100% en paridad de
   color, no solo de estructura. */
.cv-contacto {
    display: flex;
    flex-wrap: wrap;
    gap: 10px 20px;
    font-size: 0.71rem;
    color: #3d4a58;
    font-family: 'Arial', 'Helvetica', sans-serif;

    /* FIX overflow: el contenedor de contacto no desborda */
    min-width: 0;
    overflow: hidden;
}


.cv-contacto span {
    display: flex;
    align-items: center;
    gap: 5px;
    /* FIX overflow: emails/URLs largos se rompen */
    overflow-wrap: break-word;
    word-break: break-all;
    min-width: 0;
    max-width: 100%;
}

/* FIX AUDITORÍA — CAMINO B: reducido a la par de .cv-contacto (misma
   línea visual, mismo tamaño de referencia).
   v4.0: los iconos ahora son SVG inline (ver cv.js) en vez de emoji
   — cero dependencia de la fuente de emoji del sistema, igual que en
   el PDF real. */
.cv-icon {
    display: inline-flex;
    align-items: center;
    color: #2f6fb0;
    opacity: 0.85;
}

.cv-icon svg {
    width: 11px;
    height: 11px;
    display: block;
}

/* ── PERFIL PROFESIONAL EN VISTA PREVIA ──
   FIX AUDITORÍA — CAMINO B (tipografía real = tipografía del PDF):
   font-size alineado al PDF real (.cv-summary 9.5pt = 12.67px ≈
   0.79rem, line-height:1.5). El ajuste anterior de line-height
   (1.75→1.55) era necesario pero insuficiente: con un font-size
   15% mayor que el real, un párrafo largo seguía generando MÁS
   líneas de las que produce el PDF, inflando la altura medida por
   el paginador. Con el tamaño real, el número de líneas —y por
   tanto la altura— coincide exactamente con el PDF descargado.
   v4.0: tratamiento "cita destacada" en paridad con .cv-summary de
   formal.html — sin cursiva, con borde izquierdo + fondo sutil. */
.cv-perfil {
    font-size: 0.79rem;
    line-height: 1.55;
    color: #1c1c1c;
    font-style: normal;
    margin: 0;
    background: #f4f7fb;
    border-left: 3px solid #2f6fb0;
    padding: 10px 14px;
    border-radius: 0 6px 6px 0;
    /* FIX overflow: texto largo sin espacios */
    overflow-wrap: break-word;
    word-break: break-word;
}




/* ── CHIPS EN VISTA PREVIA ── */
.cv-chips-wrap {
    display: flex;
    flex-wrap: wrap;
    gap: 6px 8px;
    margin-top: 2px;
    /* FIX overflow: el contenedor no desborda */
    min-width: 0;
    overflow: hidden;
}

/* FIX AUDITORÍA — CAMINO B: font-size alineado al PDF real (.cv-chip
   8.5pt = 11.33px ≈ 0.71rem). Font-family Inter → Arial.
   v4.0: fondo sutil + color de marca, en paridad con .cv-chip de
   formal.html.
   FASE 5.4 (QA — Frente 3 ATS, paridad vista previa↔PDF): el anterior
   max-width:200px + ellipsis truncaba VISUALMENTE el chip con "…",
   y ese mismo patrón replicado en el PDF (formal/ejecutivo/master.html)
   escribía el glifo "…" real en el texto extraíble del PDF — un ATS
   real perdía el dato completo. Se corrigió primero el PDF; ahora se
   replica aquí el MISMO fix para mantener paridad exacta: el chip
   envuelve en 2+ líneas en vez de truncar (overflow-wrap/word-break,
   agnóstico de idioma es/en/it, mismo patrón que .cv-perfil/.cv-section-text)
   y sigue sin poder desbordar gracias a max-width:100% sobre
   .cv-chips-wrap (flex-wrap). El paginador mide con offsetHeight en
   tiempo real, así que el crecimiento vertical del chip se reparte
   correctamente entre páginas sin necesitar ningún ajuste adicional. */
/* v6.0 (espejo exacto de .cv-chip en formal.html): contorno de
   marca + texto de marca sobre blanco (antes: fondo pastel + borde
   clarito). Se lee como insignia de documento, y el contorno nunca
   se "lava" en impresión porque no depende de un relleno claro. */
.cv-chip {
    display: inline-block;
    background: transparent;
    border: 1px solid #123655;
    color: #123655;
    padding: 2.5px 9px;
    border-radius: 4px;
    font-size: 0.71rem;
    font-weight: 600;
    font-family: 'Arial', 'Helvetica', sans-serif;
    letter-spacing: 0.02em;
    white-space: normal;
    max-width: 100%;
    overflow-wrap: break-word;
    word-break: break-word;
}



/* Variante para idiomas: mismo tratamiento de contorno, en verde
   para diferenciarlos visualmente del resto de chips. */
.cv-chip.cv-chip-idioma {
    background: transparent;
    border-color: #2e7d52;
    color: #2e7d52;
}


/* Contenedor inline para la fila "Idiomas: [chips]" */
.cv-section-idiomas {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    flex-wrap: wrap;
    margin-top: 6px;
}

/* FIX AUDITORÍA — CAMINO B: font-size alineado al PDF real
   (.cv-additional-label 9.5pt = 12.67px ≈ 0.79rem). Font-family Inter → Arial. */
.cv-idiomas-label {
    font-size: 0.79rem;
    color: #3a3a3a;
    font-family: 'Arial', 'Helvetica', sans-serif;
    white-space: nowrap;
    padding-top: 2px;
}


/* ── ENTRADAS ESTRUCTURADAS (Experiencia / Educación) ──
   FIX AUDITORÍA (densidad tipográfica vs. PDF real):
   Antes: margin-bottom:14px + padding-bottom:14px eran REDUNDANTES —
   sumaban 28px de espacio vertical entre cada entrada, cuando el
   template real del PDF (formal.html/.cv-entry) usa solo ~13px
   (3.5mm). Con 2-4 entradas de experiencia/educación, esa duplicidad
   por sí sola inflaba la altura medida en 60-100px "fantasma",
   provocando que el paginador (correctamente, según sus mediciones)
   abriera una 2ª página que el PDF real no necesita. Se reduce el
   padding a un valor simbólico — solo el aire mínimo para separar
   visualmente el texto del borde punteado, sin duplicar el margen. */
.cv-entry {
    margin-bottom: 14px;
    padding-bottom: 4px;
    border-bottom: 1px dashed #eef0f5;

    /* Evitar que una entrada se corte entre páginas */
    break-inside: avoid;
    page-break-inside: avoid;
}


.cv-entry:last-child {
    border-bottom: none;
    margin-bottom: 0;
    padding-bottom: 0;
}

/* ── ENTRADAS DE "INFORMACIÓN ADICIONAL" (FASE 1) ──
   Mismo margen que MARGEN_ENTRADA en cv.js (14px) para que el
   paginador granular (que añade este valor manualmente al repartir
   entradas entre páginas) coincida exactamente con el espaciado
   visual real. A diferencia de .cv-entry, NO lleva borde punteado:
   son simples filas de dato (Nivel de estudios, Disponibilidad...),
   no bloques de experiencia/educación. */
.cv-additional-entry {
    margin-bottom: 14px;
}

.cv-additional-entry:last-child {
    margin-bottom: 0;
}


/* FIX AUDITORÍA — FECHAS INVISIBLES:
   Antes: flexbox + margin-left:auto en .cv-entry-fechas. Con flex-wrap:wrap
   ese patrón es frágil — si "empresa + puesto" ocupa mucho ancho, la fecha
   puede envolver a su propia línea y el margen automático deja de
   calcularse de forma consistente entre motores/navegadores (la fecha
   podía "desaparecer" visualmente). Ahora usamos CSS Grid de 2 columnas:
   la columna de la fecha SIEMPRE se reserva a la derecha, sin depender de
   márgenes automáticos ni de cómo envuelva el contenido principal. */
.cv-entry-header {
    display: grid;
    grid-template-columns: 1fr auto;
    align-items: baseline;
    column-gap: 12px;
    margin-bottom: 4px;
}

.cv-entry-header-main {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 6px 12px;
    min-width: 0;
}

/* FIX AUDITORÍA — CAMINO B: font-size alineado al PDF real
   (.cv-entry-company 10pt = 13.33px ≈ 0.83rem). Font-family Inter → Arial. */
.cv-entry-empresa {
    font-size: 0.83rem;
    font-weight: 700;
    color: #1a2540;
    font-family: 'Arial', 'Helvetica', sans-serif;
    /* FIX overflow: nombres de empresa largos */
    overflow-wrap: break-word;
    word-break: break-word;
}

/* FIX AUDITORÍA — CAMINO B: font-size alineado al PDF real
   (.cv-entry-role 9.5pt = 12.67px ≈ 0.79rem). Font-family Inter → Arial.
   v4.0: se retira la cursiva (paridad con .cv-entry-role de formal.html). */
.cv-entry-puesto {
    font-size: 0.79rem;
    color: #2f6fb0;
    font-weight: 500;
    font-style: normal;
    font-family: 'Arial', 'Helvetica', sans-serif;
    /* FIX overflow: puestos largos */
    overflow-wrap: break-word;
    word-break: break-word;
}

/* FIX AUDITORÍA — CAMINO B: font-size alineado al PDF real
   (.cv-entry-period 8.5pt = 11.33px ≈ 0.71rem). Font-family Inter → Arial.
   v4.0: badge con fondo sutil, en paridad con .cv-entry-period de
   formal.html (antes: texto plano gris sin contenedor). */
/* v6.0 (espejo exacto de .cv-entry-period en formal.html): texto de
   marca en negrita, sin badge de fondo — patrón clásico de CV
   impreso profesional, sin depender de un relleno claro que pueda
   "lavarse" en impresión. */
.cv-entry-fechas {
    font-size: 0.68rem;
    font-weight: 700;
    color: #123655;
    font-family: 'Arial', 'Helvetica', sans-serif;
    white-space: nowrap;
    text-align: right;
    align-self: baseline;
}





/* ── CUERPO DEL CV ── */
.cv-body {
    padding: 24px 32px 28px;
}

/* ── SECCIONES — break-inside: avoid garantiza integridad de bloque ── */
.cv-section {
    margin-bottom: 22px;

    /* REGLA CLAVE: La sección completa se mueve a la siguiente página
       si no hay espacio suficiente. Nunca se corta a mitad. */
    break-inside: avoid;
    page-break-inside: avoid;
}

.cv-section:last-child {
    margin-bottom: 0;
}

/* FIX AUDITORÍA — CAMINO B: font-size alineado al PDF real
   (.cv-section-title 8pt = 10.67px ≈ 0.67rem). Font-family Inter → Arial. */
/* v6.0 (paridad de color con formal.html): color de marca principal
   #123655 (antes #4a6fa5, un azul distinto que no coincidía con el
   primario real de la plantilla) — ahora el título de sección se ve
   EXACTAMENTE del mismo color en la vista previa y en el PDF final. */
.cv-section-title {
    display: flex;
    align-items: center;
    gap: 10px;
    font-size: 0.67rem;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: #123655;
    font-family: 'Arial', 'Helvetica', sans-serif;
    font-weight: 700;
    margin-bottom: 10px;
    padding-bottom: 6px;
    border-bottom: 1px solid #e8ecf2;

    /* El título siempre va con su contenido */
    break-after: avoid;
    page-break-after: avoid;
}


/* v6.0: mismo color de marca sólido que .cv-section-dot en
   formal.html (antes #4a6fa5) — paridad exacta de color. */
.cv-section-dot {
    display: inline-block;
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: #123655;
    flex-shrink: 0;
}


/* ── Título "de continuación" — sección compuesta repartida en 2+ páginas ──
   Visible SOLO cuando el paginador granular (ver cv.js, paso 6) decide
   que Experiencia/Educación no cabe entera en una página.
   v7.0 (Consolidación de paleta — Fase 6, espejo de formal.html): antes
   se atenuaba con opacity:0.72 — ahora la diferenciación visual viene
   solo de `.cv-continuacion-tag` (itálica, más pequeña, color muted),
   el título en sí mantiene contraste pleno. Ver misma nota en
   formal.html. */

.cv-continuacion-tag {
    font-weight: 400;
    font-style: italic;
    text-transform: none;
    letter-spacing: normal;
    font-size: 0.65rem;
    color: #3d4a58;
}



/* FIX AUDITORÍA — CAMINO B (tipografía real = tipografía del PDF):
   font-size alineado al PDF real (.cv-entry-description 9pt = 12px
   = 0.75rem, line-height:1.45). Este es el bloque de MAYOR impacto
   en la medición del paginador: se usa en cada descripción de
   Experiencia/Educación y en "Información Adicional". El ajuste
   previo de line-height (1.7→1.5) reducía el espacio ENTRE líneas,
   pero con un font-size ~20% mayor que el real, un párrafo largo
   seguía generando líneas de más (cada línea "extra" completa suma
   ~12-16px por descripción, multiplicado por cada entrada del CV).
   Con el tamaño y line-height reales, el número de líneas —y por
   tanto la altura total— coincide exactamente con el PDF descargado. */
.cv-section-text {
    font-size: 0.75rem;
    line-height: 1.45;
    color: #3a3a3a;
    margin: 0;
    /* FIX overflow: descripciones largas sin espacios */
    overflow-wrap: break-word;
    word-break: break-word;
}



/* ── CTA AISLADO — botón "Obtener PDF" fuera del carrusel ──
   En móvil/tablet: debajo del carrusel, flujo normal.
   En desktop ≥ 1200px: sticky al fondo del panel derecho,
   siempre visible sin importar el scroll del CV.
── */
.cv-cta-wrapper {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    padding: 20px 0 8px;
    text-align: center;
    width: 100%;
}

.cv-cta-wrapper .cv-blur-icon {
    font-size: 1.2rem;
    color: #4a6fa5;
    animation: sparkleAnim 2s ease-in-out infinite;
}

.cv-cta-wrapper p {
    font-size: 0.8rem;
    color: #5a6a7e;
    font-family: 'Inter', system-ui, sans-serif;
    margin: 0;
    letter-spacing: 0.02em;
}

/* ── CTA DESKTOP — sticky al fondo del panel derecho ──────────
   En desktop ≥ 1200px el .cv-cta-wrapper sale del flujo del
   carrusel y se ancla al fondo del panel con position:sticky.
   Un gradiente de fondo lo separa visualmente del CV sin taparlo.
   El inner tiene padding-bottom para que el último slide no quede
   oculto detrás del CTA cuando el usuario hace scroll.
── */
@media (min-width: 1200px) {
    /* El inner necesita espacio inferior para que el CTA no tape el CV */
    #cv-preview-panel-inner {
        padding-bottom: 100px;
    }

    /* El CTA se ancla al fondo del panel derecho */
    #cv-preview-panel-inner .cv-cta-wrapper {
        position: sticky;
        bottom: 0;
        /* Ancho completo del panel — salir del flujo centrado */
        width: calc(100% + 48px); /* compensar padding lateral del inner (24px × 2) */
        margin-left: -24px;
        margin-right: -24px;
        /* Fondo con gradiente para separarse del CV con elegancia */
        background: linear-gradient(
            to bottom,
            transparent 0%,
            rgba(17, 24, 39, 0.85) 30%,
            rgba(17, 24, 39, 0.97) 100%
        );
        backdrop-filter: blur(8px);
        -webkit-backdrop-filter: blur(8px);
        /* Padding generoso para que el botón respire */
        padding: 20px 24px 20px;
        /* Separador superior sutil */
        border-top: 1px solid rgba(74, 111, 165, 0.15);
        /* Asegurar que queda por encima del CV */
        z-index: 10;
        /* Transición suave al aparecer */
        transition: opacity 0.3s ease;
        /* Resetear margin-top del flujo normal */
        margin-top: 0;
    }

    /* El texto descriptivo encima del botón — más visible sobre fondo oscuro */
    #cv-preview-panel-inner .cv-cta-wrapper p {
        color: rgba(122, 163, 212, 0.7);
        font-size: 0.78rem;
    }

    /* El icono ✦ — más visible sobre fondo oscuro */
    #cv-preview-panel-inner .cv-cta-wrapper .cv-blur-icon {
        color: rgba(122, 163, 212, 0.8);
    }

    /* El botón en desktop: ancho máximo contenido, no 100% */
    #cv-preview-panel-inner .cv-cta-wrapper .cv-blur-btn {
        min-width: 220px;
        max-width: 320px;
        width: auto;
        padding: 13px 36px;
        font-size: 0.9rem;
    }
}

/* ── BLUR OVERLAY (protección PDF) ── */
.cv-blur-overlay {
    position: relative;
    height: 120px;
    margin-top: -60px;
    background: linear-gradient(to bottom, transparent 0%, rgba(255, 255, 255, 0.92) 50%, #ffffff 100%);
    display: flex;
    align-items: flex-end;
    justify-content: center;
    padding-bottom: 20px;
    z-index: 25;
}

.cv-blur-cta {
    text-align: center;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
}

.cv-blur-icon {
    font-size: 1.2rem;
    color: #4a6fa5;
    animation: sparkleAnim 2s ease-in-out infinite;
}

@keyframes sparkleAnim {
    0%, 100% { opacity: 0.3; transform: scale(1); }
    50%       { opacity: 1;   transform: scale(1.3); }
}

.cv-blur-cta p {
    font-size: 0.8rem;
    color: #5a6a7e;
    font-family: 'Inter', system-ui, sans-serif;
    margin: 0;
}

/* ── Botón "Obtener PDF" — diseño premium centrado ──
   Tamaño generoso, gradiente oscuro profesional, glow azul,
   hover con elevación. Idéntico al lenguaje visual de la app.
── */
.cv-blur-btn {
    /* Gradiente premium: azul marino → azul medio */
    background: linear-gradient(135deg, #1a2540 0%, #4a6fa5 60%, #5d8bc9 100%);
    color: #ffffff;
    border: none;
    /* Más grande y con más padding para mayor presencia */
    padding: 14px 40px;
    border-radius: 28px;
    font-size: 0.95rem;
    font-weight: 700;
    cursor: pointer;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    /* Ancho mínimo para que no quede demasiado estrecho */
    min-width: 200px;
    width: auto;
    /* Transición suave en todas las propiedades */
    transition: transform 0.25s cubic-bezier(0.34, 1.56, 0.64, 1),
                box-shadow 0.25s ease,
                background 0.2s ease;
    /* Glow azul multicapa */
    box-shadow:
        0 4px 16px rgba(74, 111, 165, 0.45),
        0 8px 32px rgba(26, 37, 64, 0.30),
        0 0 0 1px rgba(93, 139, 201, 0.15);
    /* Animación de pulso suave para llamar la atención */
    animation: ctaPulse 3s ease-in-out infinite;
}

@keyframes ctaPulse {
    0%, 100% {
        box-shadow:
            0 4px 16px rgba(74, 111, 165, 0.45),
            0 8px 32px rgba(26, 37, 64, 0.30),
            0 0 0 1px rgba(93, 139, 201, 0.15);
    }
    50% {
        box-shadow:
            0 6px 24px rgba(74, 111, 165, 0.65),
            0 12px 40px rgba(26, 37, 64, 0.40),
            0 0 0 1px rgba(93, 139, 201, 0.25);
    }
}

.cv-blur-btn:hover {
    /* Elevación con micro-rebote (cubic-bezier con overshoot) */
    transform: translateY(-3px) scale(1.02);
    background: linear-gradient(135deg, #0f1a30 0%, #3d6a9e 60%, #5d8bc9 100%);
    box-shadow:
        0 8px 28px rgba(74, 111, 165, 0.60),
        0 16px 48px rgba(26, 37, 64, 0.40),
        0 0 0 1px rgba(93, 139, 201, 0.30);
    animation: none; /* pausar pulso en hover */
}

.cv-blur-btn:active {
    transform: translateY(-1px) scale(0.99);
    transition-duration: 0.1s;
}

/* ============================================================
   RESPONSIVE — VISTA PREVIA CV
   ============================================================ */
@media (max-width: 767px) {
    .cv-a4-page {
        border-radius: 0;
        box-shadow: 0 4px 20px rgba(0, 0, 0, 0.12);
    }

    .cv-blur-btn {
        padding: 10px 22px;
        font-size: 0.85rem;
    }
}

/* ============================================================
   MODAL DE PREVISUALIZACIÓN — rv-modal-*
   Generado dinámicamente por mostrarModalPreview() en cv.js
   ============================================================ */

/* ── Overlay (fondo oscuro) ───────────────────────────────────
   ANIMACIÓN PREMIUM:
   · El fondo negro aparece con opacity 0→1 en 380ms.
   · El backdrop-filter blur va de 0px→8px progresivamente
     gracias a la transición CSS (requiere que el valor inicial
     sea blur(0px) y el final blur(8px)).
   · Efecto: el fondo "se materializa" desenfocándose suavemente.
─────────────────────────────────────────────────────────── */
.rv-modal-overlay {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.72);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 9000;
    padding: 16px;
    /* Estado inicial: invisible + sin blur */
    opacity: 0;
    backdrop-filter: blur(0px);
    -webkit-backdrop-filter: blur(0px);
    /* Transición del overlay: opacity + blur simultáneos */
    transition:
        opacity 0.38s cubic-bezier(0.4, 0, 0.2, 1),
        backdrop-filter 0.38s cubic-bezier(0.4, 0, 0.2, 1),
        -webkit-backdrop-filter 0.38s cubic-bezier(0.4, 0, 0.2, 1);
}

.rv-modal-overlay--visible {
    opacity: 1;
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
}

/* ── Contenedor del modal ─────────────────────────────────────
   ANIMACIÓN PREMIUM tipo Apple/Stripe:
   · Estado inicial: scale(0.92) + translateY(24px) + opacity(0)
   · Estado final:   scale(1)    + translateY(0)    + opacity(1)
   · Easing: cubic-bezier(0.34, 1.56, 0.64, 1) — micro-rebote
     al final que da sensación de "snap" premium sin ser exagerado.
   · Timing: 380ms — rápido pero apreciable.
─────────────────────────────────────────────────────────── */
.rv-modal-container {
    background: #ffffff;
    border-radius: 16px;
    box-shadow: 0 24px 80px rgba(0, 0, 0, 0.35), 0 4px 16px rgba(0, 0, 0, 0.15);
    width: 100%;
    max-width: 680px;
    max-height: 92vh;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap: 0;
    /* Estado inicial: encogido, desplazado hacia abajo, invisible */
    opacity: 0;
    transform: scale(0.92) translateY(24px);
    /* Easing con micro-rebote: overshoot sutil al llegar a scale(1) */
    transition:
        opacity 0.38s cubic-bezier(0.4, 0, 0.2, 1),
        transform 0.38s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.rv-modal-overlay--visible .rv-modal-container {
    opacity: 1;
    transform: scale(1) translateY(0);
}

/* ── Header del modal ─────────────────────────────────────── */
.rv-modal-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 20px 24px 16px;
    border-bottom: 1px solid #f0f0f0;
    position: sticky;
    top: 0;
    background: #ffffff;
    z-index: 1;
    border-radius: 16px 16px 0 0;
}

.rv-modal-title {
    font-size: 1.1rem;
    font-weight: 700;
    color: #1e3a5f;
    margin: 0;
}

.rv-modal-close {
    background: none;
    border: none;
    font-size: 1.2rem;
    color: #888;
    cursor: pointer;
    padding: 4px 8px;
    border-radius: 6px;
    line-height: 1;
    transition: background 0.15s, color 0.15s;
}

.rv-modal-close:hover {
    background: #f5f5f5;
    color: #333;
}

/* ── Indicador de intentos ────────────────────────────────── */
.rv-modal-intentos {
    margin: 12px 24px 0;
    padding: 10px 14px;
    border-radius: 8px;
    font-size: 0.875rem;
    font-weight: 500;
}

.rv-modal-intentos--ok {
    background: #f0f7ff;
    color: #1e3a5f;
    border: 1px solid #c8dff5;
}

.rv-modal-intentos--agotado {
    background: #fff8f0;
    color: #c0392b;
    border: 1px solid #f5c6c0;
}

/* ── Aviso de marca de agua ───────────────────────────────── */
.rv-modal-watermark-notice {
    margin: 10px 24px 0;
    padding: 8px 12px;
    background: #fafafa;
    border-radius: 6px;
    font-size: 0.8rem;
    color: #888;
    display: flex;
    align-items: center;
    gap: 6px;
}

.rv-modal-watermark-icon {
    font-size: 0.9rem;
}

/* ── Carrusel de páginas (sistema translateX) ─────────────── */
/*
 * ARQUITECTURA DEL CARRUSEL — Layout estricto sin scroll vertical:
 *
 * 1. .rv-modal-carousel: overflow:hidden — recorta todo lo que salga
 *    del área visible. CRÍTICO: sin esto, los slides fuera de vista
 *    siguen siendo visibles y causan scroll horizontal/vertical.
 *
 * 2. .rv-modal-track: display:flex — alinea los slides en fila.
 *    width = N*100% (aplicado inline desde JS). El track es N veces
 *    más ancho que el carrusel, pero overflow:hidden lo recorta.
 *
 * 3. .rv-modal-slide: flex:0 0 auto + width inline desde JS.
 *    JS aplica style="width:Xpx" igual al ancho del carrusel,
 *    garantizando que cada slide ocupa exactamente el 100% visible.
 *
 * 4. Navegación: transform:translateX(-(idx * 100/N)%) mueve el track.
 *    Solo UN slide es visible a la vez. Sin scroll vertical.
 */
.rv-modal-carousel {
    margin: 16px 24px 0;
    border-radius: 8px;
    /* CRÍTICO: recorta los slides fuera de vista */
    overflow: hidden;
    background: #f5f5f5;
    position: relative;
    /*
     * FIX 1 — Scroll vertical en el carrusel:
     * Reservar el espacio exacto de una página A4 ANTES de que cargue la imagen.
     * Proporción A4: 210mm × 297mm → ratio 1:√2 ≈ 1:1.4142.
     * Esto elimina el layout shift y evita que el track expanda verticalmente
     * al contener N slides en fila (el contenedor siempre mide 1 página de alto).
     * Se elimina min-height: 200px que era el valor provisional anterior.
     */
    aspect-ratio: 1 / 1.4142;
}

/* Track: contenedor flex que se desplaza horizontalmente */
.rv-modal-track {
    /* display:flex, width y transition se aplican inline desde JS */
    will-change: transform;
    /* Evitar que el track cree scroll vertical */
    overflow: hidden;
    /*
     * FIX — Scroll vertical en el carrusel:
     * Sin height:100%, el track toma la altura de su contenido (la imagen
     * a tamaño natural), desbordando el contenedor y activando el scroll
     * en .rv-modal-container. Con height:100% el track queda confinado
     * exactamente al espacio reservado por aspect-ratio del carrusel.
     */
    height: 100%;
}

/* Cada slide: no se encoge ni crece, ocupa el ancho que JS le asigne */
.rv-modal-slide {
    flex: 0 0 auto;
    min-width: 0;
    overflow: hidden;
    /*
     * FIX — Igual que el track: sin height:100% el slide crece hasta
     * la altura natural de la imagen, rompiendo el confinamiento del carrusel.
     */
    height: 100%;
}

.rv-modal-img {
    width: 100%;
    /*
     * FIX — height:100% + object-fit:contain:
     * La imagen ocupa todo el espacio del slide (que ya está confinado
     * al carrusel) y se escala proporcionalmente sin distorsión.
     * Reemplaza height:auto que permitía que la imagen creciera más allá
     * del contenedor y causara el scroll vertical.
     */
    height: 100%;
    object-fit: contain;
    display: block;
    border-radius: 4px;
    /* Prevenir que el usuario guarde la imagen con clic derecho */
    pointer-events: none;
    user-select: none;
    -webkit-user-select: none;
    -webkit-user-drag: none;
}

/* ── Navegación entre páginas ─────────────────────────────── */
.rv-modal-nav {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 16px;
    padding: 12px 24px 0;
}

.rv-modal-nav-btn {
    background: #f0f0f0;
    border: none;
    border-radius: 8px;
    width: 36px;
    height: 36px;
    font-size: 1.2rem;
    cursor: pointer;
    color: #333;
    transition: background 0.15s;
    display: flex;
    align-items: center;
    justify-content: center;
}

.rv-modal-nav-btn:hover:not(:disabled) {
    background: #e0e0e0;
}

.rv-modal-nav-btn:disabled {
    opacity: 0.35;
    cursor: not-allowed;
}

.rv-modal-nav-info {
    font-size: 0.875rem;
    color: #666;
    min-width: 48px;
    text-align: center;
}

/* ── Acciones (botones de descarga y pago) ────────────────── */
.rv-modal-actions {
    display: flex;
    flex-direction: column;
    gap: 10px;
    padding: 20px 24px 24px;
}

/* Botón de descarga — desactivado por defecto */
.rv-modal-btn-download {
    width: 100%;
    padding: 13px 20px;
    border-radius: 10px;
    border: 2px solid #dcdcdc;
    background: #f9f9f9;
    color: #aaa;
    font-size: 0.95rem;
    font-weight: 600;
    cursor: not-allowed;
    transition: all 0.2s ease;
    text-align: center;
}

/* Botón de descarga — activado tras el pago */
.rv-modal-btn-download--activo {
    border-color: #27ae60;
    background: #27ae60;
    color: #ffffff;
    cursor: pointer;
}

.rv-modal-btn-download--activo:hover {
    background: #219a52;
    border-color: #219a52;
    transform: translateY(-1px);
    box-shadow: 0 4px 16px rgba(39, 174, 96, 0.3);
}

/* Botón de pago — estilo secundario por defecto */
.rv-modal-btn-pay {
    width: 100%;
    padding: 13px 20px;
    border-radius: 10px;
    border: 2px solid #1e3a5f;
    background: transparent;
    color: #1e3a5f;
    font-size: 0.95rem;
    font-weight: 600;
    cursor: pointer;
    transition: all 0.2s ease;
    text-align: center;
}

.rv-modal-btn-pay:hover {
    background: #1e3a5f;
    color: #ffffff;
    transform: translateY(-1px);
    box-shadow: 0 4px 16px rgba(30, 58, 95, 0.25);
}

/* Botón de pago — destacado cuando los intentos están agotados */
.rv-modal-btn-pay--destacado {
    background: linear-gradient(135deg, #1e3a5f 0%, #4a7ab5 100%);
    color: #ffffff;
    border-color: transparent;
    box-shadow: 0 4px 20px rgba(30, 58, 95, 0.35);
    animation: rv-pulse 2s ease-in-out infinite;
}

.rv-modal-btn-pay--destacado:hover {
    background: linear-gradient(135deg, #162d4a 0%, #3d6a9e 100%);
    transform: translateY(-2px);
    box-shadow: 0 6px 24px rgba(30, 58, 95, 0.45);
}

@keyframes rv-pulse {
    0%, 100% { box-shadow: 0 4px 20px rgba(30, 58, 95, 0.35); }
    50%       { box-shadow: 0 4px 28px rgba(30, 58, 95, 0.55); }
}

/* ── Responsive — móvil ───────────────────────────────────── */
@media (max-width: 600px) {
    .rv-modal-container {
        border-radius: 12px 12px 0 0;
        max-height: 95vh;
        position: fixed;
        bottom: 0;
        left: 0;
        right: 0;
        width: 100%;
        max-width: 100%;
    }

    .rv-modal-overlay {
        /*
         * PANTALLA COMPLETA: el modal ya no es un bottom sheet.
         * align-items: stretch hace que el contenedor llene todo el overlay
         * sin gaps en los bordes. padding: 0 elimina el espacio lateral.
         */
        align-items: stretch;
        justify-content: stretch;
        padding: 0;
    }

    .rv-modal-header,
    .rv-modal-intentos,
    .rv-modal-watermark-notice,
    .rv-modal-carousel,
    .rv-modal-nav,
    .rv-modal-actions {
        margin-left: 16px;
        margin-right: 16px;
        
    }

    .rv-modal-carousel {
        margin-left: 0;
        margin-right: 0;
        border-radius: 0;
    }

    .rv-modal-actions {
        padding-bottom: env(safe-area-inset-bottom, 16px);
    }
}

/* ============================================================
   MODAL v4.0 — 3 COLUMNAS DE ESTILOS
   ============================================================ */

/* ── Modal ancho (override del contenedor base) ─────────────────
   ANIMACIÓN PREMIUM — hereda la misma lógica que .rv-modal-container:
   · Estado inicial: scale(0.92) + translateY(24px) + opacity(0)
   · Estado final:   scale(1)    + translateY(0)    + opacity(1)
   · Easing con micro-rebote: cubic-bezier(0.34, 1.56, 0.64, 1)
   · Timing: 380ms
   NOTA: En móvil (@media max-width:767px) se sobreescribe con
   translateY(4px) para el efecto de "slide desde abajo".
─────────────────────────────────────────────────────────── */
.rv-modal-container--wide {
    max-width: 980px;
    max-height: 96vh;
    overflow-y: auto;
    overflow-x: hidden;
    display: flex;
    flex-direction: column;
    gap: 0;
    scrollbar-width: thin;
    scrollbar-color: rgba(255,255,255,0.25) transparent;
    border-radius: 20px;
    box-shadow: 0 24px 80px rgba(10, 20, 50, 0.45), 0 4px 16px rgba(10, 20, 50, 0.2);
    /* Estado inicial: encogido, desplazado hacia abajo, invisible */
    opacity: 0;
    transform: scale(0.92) translateY(24px);
    /* Easing con micro-rebote */
    transition:
        opacity 0.38s cubic-bezier(0.4, 0, 0.2, 1),
        transform 0.38s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.rv-modal-overlay--visible .rv-modal-container--wide {
    opacity: 1;
    transform: scale(1) translateY(0);
}

.rv-modal-container--wide::-webkit-scrollbar {
    width: 5px;
}
.rv-modal-container--wide::-webkit-scrollbar-thumb {
    background: rgba(255,255,255,0.25);
    border-radius: 4px;
}

/* ── Header del modal v4.0 — gradiente oscuro premium ───────── */
.rv-modal-container--wide .rv-modal-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 18px 20px 16px;
    background: linear-gradient(135deg, #0f1a30 0%, #1e3a5f 50%, #2d5080 100%);
    border-bottom: none;
    border-radius: 20px 20px 0 0;
    flex-shrink: 0;
}

.rv-modal-container--wide .rv-modal-title {
    flex: 1;
    text-align: center;
    font-size: 1.05rem;
    font-weight: 700;
    color: #ffffff;
    font-family: 'Inter', system-ui, sans-serif;
    letter-spacing: 0.02em;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    margin: 0;
}

/* ── Botón "← Volver" — compacto y discreto ────────────────── */
.rv-modal-back {
    background: transparent;
    border: 1px solid rgba(255, 255, 255, 0.2);
    border-radius: 6px;
    padding: 5px 10px;
    font-size: 0.78rem;
    font-weight: 500;
    color: rgba(255, 255, 255, 0.7);
    cursor: pointer;
    transition: background 0.15s, color 0.15s;
    white-space: nowrap;
    font-family: 'Inter', system-ui, sans-serif;
    flex-shrink: 0;
    /* Evitar zoom involuntario en móvil al tocar el botón */
    touch-action: manipulation;
    -webkit-tap-highlight-color: transparent;
    user-select: none;
    -webkit-user-select: none;
}

.rv-modal-back:hover {
    background: rgba(255, 255, 255, 0.1);
    color: rgba(255, 255, 255, 0.95);
}

/* ── Botón cerrar ✕ en header oscuro ────────────────────────── */
.rv-modal-container--wide .rv-modal-close {
    background: rgba(255, 255, 255, 0.1);
    border: 1.5px solid rgba(255, 255, 255, 0.2);
    color: rgba(255, 255, 255, 0.85);
    border-radius: 8px;
    width: 34px;
    height: 34px;
    font-size: 1rem;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: background 0.15s, border-color 0.15s;
    flex-shrink: 0;
    font-family: 'Inter', system-ui, sans-serif;
}

.rv-modal-container--wide .rv-modal-close:hover {
    background: rgba(255, 255, 255, 0.2);
    border-color: rgba(255, 255, 255, 0.45);
    color: #ffffff;
}

/* ── Aviso de marca de agua — rediseñado ────────────────────── */
.rv-modal-watermark-notice {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 10px 20px;
    background: linear-gradient(90deg, #f0f5ff 0%, #f8faff 100%);
    border-left: 3px solid #4a6fa5;
    font-size: 0.8rem;
    color: #3a5a8a;
    font-family: 'Inter', system-ui, sans-serif;
    font-weight: 500;
    flex-shrink: 0;
}

.rv-modal-watermark-icon {
    font-size: 0.9rem;
    flex-shrink: 0;
}

/* ── Grid de estilos — escalable para N columnas ────────────── */
/* auto-fit + minmax: con 3 estilos → 3 columnas.
   Con 4 estilos → 4 columnas (si caben) o 2 filas de 2.
   El modal tiene max-width: 980px, así que con minmax(260px,1fr)
   caben hasta 3 columnas cómodamente. Con 4+ estilos se adapta. */
.rv-styles-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: 16px;
    padding: 16px 20px;
    background: #f4f7fb;
}

/* ── Columna individual de estilo ───────────────────────────── */
.rv-style-col {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    background: #f8f9fc;
    border: 2px solid #e8ecf2;
    border-radius: 14px;
    padding: 12px 10px 14px;
    transition: border-color 0.2s, box-shadow 0.2s, background 0.2s;
    position: relative;
}

/* Estado: generado (preview disponible) */
.rv-style-col.rv-col--generated {
    border-color: #c8dff5;
    background: #f5f9ff;
}

/* Estado: seleccionado para comprar */
.rv-style-col.rv-col--selected {
    border-color: #27ae60;
    background: #f0fff6;
    box-shadow: 0 0 0 3px rgba(39, 174, 96, 0.12);
}

/* ── Badge (Popular / Premium) ──────────────────────────────── */
.rv-badge {
    display: inline-block;
    font-size: 0.7rem;
    font-weight: 700;
    padding: 3px 10px;
    border-radius: 20px;
    letter-spacing: 0.03em;
    font-family: 'Inter', system-ui, sans-serif;
    white-space: nowrap;
    min-height: 22px;
}

.rv-badge--popular {
    background: linear-gradient(135deg, #fff3cd, #ffe08a);
    color: #7a5c00;
    border: 1px solid #f0c040;
}

.rv-badge--premium {
    background: linear-gradient(135deg, #e8d5ff, #c9a8ff);
    color: #5a1fa0;
    border: 1px solid #b08ae0;
}

.rv-badge--ejecutivo {
    background: linear-gradient(135deg, #2b2411, #4a3a12);
    color: #ffd77a;
    border: 1px solid #caa14a;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), inset 0 1px 0 rgba(255, 255, 255, 0.08);
    text-shadow: 0 1px 1px rgba(0, 0, 0, 0.4);
}

.rv-badge--hidden {

    visibility: hidden;
    pointer-events: none;
}

/* ── Etiqueta del estilo ────────────────────────────────────── */
.rv-col-label {
    font-size: 0.9rem;
    font-weight: 700;
    color: #1a2540;
    font-family: 'Inter', system-ui, sans-serif;
    letter-spacing: 0.04em;
    text-transform: uppercase;
}

/* ── Área de preview (carrusel mini) ────────────────────────── */
.rv-col-preview-wrap {
    width: 100%;
    flex: 1;
}

.rv-col-carousel {
    position: relative;
    width: 100%;
    aspect-ratio: 1 / 1.4142;
    overflow: hidden;
    border-radius: 8px;
    background: #e8ecf2;
}

.rv-col-track {
    display: flex;
    height: 100%;
    transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
    will-change: transform;
}

.rv-col-slide {
    flex: 0 0 100%;
    height: 100%;
    position: relative;
}

.rv-col-img-wrap {
    position: relative;
    width: 100%;
    height: 100%;
}

.rv-col-img {
    width: 100%;
    height: 100%;
    /* contain: muestra el CV completo centrado sin recortar.
       Permite ver la navegación de páginas cuando hay más de 1. */
    object-fit: contain;
    object-position: center top;
    display: block;
    border-radius: 6px;
    pointer-events: none;
    user-select: none;
    -webkit-user-select: none;
    -webkit-user-drag: none;
}

/* ── Marca de agua incrustada en cada slide ─────────────────── */
.rv-col-watermark-overlay {
    position: absolute;
    inset: 0;
    z-index: 10;
    pointer-events: none;
    user-select: none;
    -webkit-user-select: none;
    overflow: hidden;
    display: flex;
    align-items: center;
    justify-content: center;
}

.rv-col-watermark-pattern {
    position: absolute;
    top: 50%;
    left: 50%;
    width: 300%;
    height: 300%;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 12px;
    transform: translate(-50%, -50%) rotate(-35deg);
    transform-origin: center center;
}

.rv-col-watermark-item {
    font-size: clamp(0.7rem, 1.5vw, 1rem);
    font-weight: 900;
    letter-spacing: 0.3em;
    color: rgba(74, 111, 165, 0.32);
    font-family: 'Inter', system-ui, sans-serif;
    text-transform: uppercase;
    white-space: nowrap;
    display: block;
    padding: 8px 16px;
}

/* ── Error de carga de imagen (sesión expirada / regenerando) ──
   Ver cv.js::_manejarErrorImagenPreview — auditoría rovnad_session. */
.rv-col-img-error {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 8px;
    width: 100%;
    height: 100%;
    min-height: 220px;
    padding: 16px;
    text-align: center;
    font-size: 0.85rem;
    color: #6b7280;
    background: #f8f9fb;
    border-radius: 8px;
}

.rv-col-img-error .rv-col-gen-icon {
    font-size: 1.4rem;
    animation: rv-spin-suave 1.6s linear infinite;
}

@keyframes rv-spin-suave {
    from { transform: rotate(0deg); }
    to   { transform: rotate(360deg); }
}

/* ── Placeholder (estilo no generado) ──────────────────────── */
.rv-col-placeholder {

    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background: linear-gradient(135deg, #f0f4fa 0%, #e8ecf5 100%);
    border-radius: 6px;
    z-index: 5;
}

.rv-col-gen-btn {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    background: #ffffff;
    border: 2px dashed #4a6fa5;
    border-radius: 12px;
    padding: 16px 20px;
    cursor: pointer;
    color: #4a6fa5;
    font-size: 0.8rem;
    font-weight: 600;
    font-family: 'Inter', system-ui, sans-serif;
    transition: background 0.15s, border-color 0.15s, transform 0.15s;
    text-align: center;
}

.rv-col-gen-btn:hover:not(:disabled) {
    background: #f0f5ff;
    border-color: #1e3a5f;
    color: #1e3a5f;
    transform: translateY(-2px);
}

.rv-col-gen-btn:disabled {
    opacity: 0.6;
    cursor: not-allowed;
    transform: none;
}

.rv-col-gen-icon {
    font-size: 1.4rem;
}

/* ── Navegación mini-carrusel ───────────────────────────────── */
.rv-col-nav {
    display: flex;
    align-items: center;
    gap: 8px;
    justify-content: center;
}

.rv-col-nav-btn {
    background: #f0f0f0;
    border: none;
    border-radius: 6px;
    width: 28px;
    height: 28px;
    font-size: 1rem;
    cursor: pointer;
    color: #333;
    transition: background 0.15s;
    display: flex;
    align-items: center;
    justify-content: center;
    line-height: 1;
}

.rv-col-nav-btn:hover:not(:disabled) {
    background: #e0e0e0;
}

.rv-col-nav-btn:disabled {
    opacity: 0.3;
    cursor: not-allowed;
}

.rv-col-page-info {
    font-size: 0.75rem;
    color: #888;
    min-width: 36px;
    text-align: center;
    font-family: 'Inter', system-ui, sans-serif;
}

/* ── Footer de columna: precio + checkbox ───────────────────── */
.rv-col-footer {
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 100%;
    padding: 4px 4px 0;
}

.rv-col-price {
    font-size: 1rem;
    font-weight: 700;
    color: #1a2540;
    font-family: 'Inter', system-ui, sans-serif;
}

/* ── Checkbox personalizado ─────────────────────────────────── */
.rv-col-check-label {
    display: flex;
    align-items: center;
    cursor: pointer;
    position: relative;
}

.rv-col-checkbox {
    position: absolute;
    opacity: 0;
    width: 0;
    height: 0;
}

.rv-col-check-custom {
    width: 26px;
    height: 26px;
    border-radius: 50%;
    border: 2px solid #c0c8d8;
    background: #ffffff;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: border-color 0.2s, background 0.2s, box-shadow 0.2s;
    flex-shrink: 0;
}

.rv-col-check-custom::after {
    content: '';
    display: none;
    width: 8px;
    height: 5px;
    border-left: 2.5px solid #ffffff;
    border-bottom: 2.5px solid #ffffff;
    transform: rotate(-45deg) translateY(-1px);
}

.rv-col-checkbox:checked + .rv-col-check-custom {
    background: #27ae60;
    border-color: #27ae60;
    box-shadow: 0 0 0 3px rgba(39, 174, 96, 0.2);
}

.rv-col-checkbox:checked + .rv-col-check-custom::after {
    display: block;
}

.rv-col-checkbox:disabled + .rv-col-check-custom {
    opacity: 0.4;
    cursor: not-allowed;
}

/* ── Footer del modal ───────────────────────────────────────── */
.rv-modal-footer {
    display: flex;
    gap: 16px;
    padding: 16px 20px 20px;
    border-top: 1px solid #f0f0f0;
    align-items: flex-start;
    background: #ffffff;
    position: sticky;
    bottom: 0;
    z-index: 2;
}

/* ── Panel de descargas (colapsable) ────────────────────────── */
.rv-downloads-panel {
    flex: 1;
    min-width: 0;
    border: 1.5px solid #e8ecf2;
    border-radius: 12px;
    overflow: hidden;
    background: #f8f9fc;
}

.rv-downloads-toggle {
    width: 100%;
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 12px 14px;
    background: none;
    border: none;
    cursor: pointer;
    font-size: 0.875rem;
    font-weight: 600;
    color: #1a2540;
    font-family: 'Inter', system-ui, sans-serif;
    text-align: left;
    transition: background 0.15s;
}

.rv-downloads-toggle:hover {
    background: #f0f4fa;
}

.rv-downloads-toggle-icon {
    font-size: 1rem;
}

.rv-downloads-badge {
    background: #4a6fa5;
    color: #ffffff;
    font-size: 0.7rem;
    font-weight: 700;
    padding: 1px 7px;
    border-radius: 10px;
    font-family: 'Inter', system-ui, sans-serif;
}

.rv-downloads-chevron {
    margin-left: auto;
    font-size: 0.9rem;
    color: #888;
    transition: transform 0.2s;
}

.rv-downloads-body {
    padding: 0 14px 14px;
    border-top: 1px solid #e8ecf2;
    /* Scroll interno: máximo 3-4 ítems visibles (~180px).
       Con más compras aparece scrollbar interna sin romper el modal. */
    max-height: 180px;
    overflow-y: auto;
    scrollbar-width: thin;
    scrollbar-color: rgba(74, 111, 165, 0.3) transparent;
}

.rv-downloads-body::-webkit-scrollbar {
    width: 4px;
}

.rv-downloads-body::-webkit-scrollbar-thumb {
    background: rgba(74, 111, 165, 0.3);
    border-radius: 4px;
}

.rv-downloads-body::-webkit-scrollbar-track {
    background: transparent;
}

.rv-downloads-empty {
    font-size: 0.8rem;
    color: #aaa;
    font-family: 'Inter', system-ui, sans-serif;
    margin: 10px 0 0;
    text-align: center;
}

/* ── Panel de precio dinámico ───────────────────────────────── */
.rv-price-panel {
    display: flex;
    flex-direction: column;
    gap: 10px;
    min-width: 220px;
    max-width: 280px;
}

.rv-price-display {
    text-align: center;
    min-height: 40px;
    display: flex;
    align-items: center;
    justify-content: center;
}

.rv-price-hint {
    font-size: 0.8rem;
    color: #aaa;
    font-family: 'Inter', system-ui, sans-serif;
    text-align: center;
}

.rv-price-total {
    font-size: 1.6rem;
    font-weight: 800;
    color: #1a2540;
    font-family: 'Inter', system-ui, sans-serif;
}

.rv-price-bundle {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
}

.rv-price-original {
    font-size: 0.85rem;
    color: #aaa;
    text-decoration: line-through;
    font-family: 'Inter', system-ui, sans-serif;
}

.rv-price-arrow {
    font-size: 0.8rem;
    color: #888;
    display: none; /* se muestra en línea con flex-direction: row en desktop */
}

.rv-price-save {
    font-size: 0.75rem;
    font-weight: 700;
    color: #27ae60;
    font-family: 'Inter', system-ui, sans-serif;
    background: rgba(39, 174, 96, 0.08);
    padding: 2px 10px;
    border-radius: 10px;
}

/* ── Checkbox de consentimiento — bloquea el pago hasta marcar ── */
.rv-consent-check {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    cursor: pointer;
    padding: 6px 2px;
    user-select: none;
}

.rv-consent-check input[type="checkbox"] {
    flex-shrink: 0;
    width: 16px;
    height: 16px;
    margin-top: 1px;
    accent-color: #1a2540;
    cursor: pointer;
}

.rv-consent-text {
    font-size: 0.72rem;
    line-height: 1.35;
    color: #888;
    font-family: 'Inter', system-ui, sans-serif;
    text-align: left;
}

/* ── Botón Stripe — compacto y elegante en desktop ──────────── */
.rv-btn-stripe {
    width: 100%;

    padding: 10px 16px;
    border-radius: 10px;
    border: 1.5px solid #c0c8d8;
    background: #f0f0f0;
    color: #aaa;
    font-size: 0.85rem;
    font-weight: 600;
    cursor: not-allowed;
    transition: all 0.2s ease;
    text-align: center;
    font-family: 'Inter', system-ui, sans-serif;
    letter-spacing: 0.01em;
}

.rv-btn-stripe.rv-btn-stripe--active {
    background: linear-gradient(135deg, #1a2540 0%, #4a6fa5 100%);
    border-color: transparent;
    color: #ffffff;
    cursor: pointer;
    box-shadow: 0 3px 14px rgba(30, 58, 95, 0.3);
    animation: rv-stripe-pulse 2.5s ease-in-out infinite;
}

.rv-btn-stripe.rv-btn-stripe--active:hover {
    background: linear-gradient(135deg, #0f1a30 0%, #3d6a9e 100%);
    transform: translateY(-1px);
    box-shadow: 0 5px 20px rgba(30, 58, 95, 0.4);
    animation: none;
}

@keyframes rv-stripe-pulse {
    0%, 100% { box-shadow: 0 4px 20px rgba(30, 58, 95, 0.35); }
    50%       { box-shadow: 0 4px 32px rgba(30, 58, 95, 0.55); }
}

/* ── FASE 7 — Frente C: bloque de confianza bajo el botón Stripe ──
   Minimalista a propósito: logo + una sola línea de micro-copy, sin
   iconos adicionales ni listas de condiciones. Altura deliberadamente
   pequeña (line-height ajustado, sin padding vertical extra) para no
   sumar peso al footer del modal — crítico en móvil (ver notas "FIX
   AUDITORÍA MÓVIL" en la media query de abajo, el footer es delicado
   en pantallas cortas). */
.rv-trust-badge {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    margin-top: 2px;
    line-height: 1;
}

.rv-trust-badge svg {
    flex-shrink: 0;
    display: block;
}

.rv-trust-badge span {
    font-size: 0.75rem;
    color: #8a94a6;
    font-family: 'Inter', system-ui, sans-serif;
    white-space: nowrap;
}


/* ── Vista de Términos "step navigation" DENTRO del mismo modal ──
   Alternativa al patrón "overlay-sobre-modal + iframe": en vez de abrir
   una segunda capa flotando sobre el modal de pago (con su propio
   z-index, su propia carga de red y su propio "body"), el contenido
   interno del MISMO modal se intercambia. Mismo overlay, mismo
   z-index, cero apilamiento — solo se oculta .rv-modal-scroll-area +
   .rv-modal-footer y se muestra este bloque en su lugar. Reutiliza el
   layout flex-column ya definido en .rv-modal-container--wide. ── */
.rv-terms-view {
    display: none;
    flex-direction: column;
    min-height: 0;
    flex: 1;
}

/* Sin header propio: el ÚNICO botón "← Volver" es el del header del
   modal (#rv-modal-back en .rv-modal-header), que ahora es consciente
   del contexto (ver mostrarModalPreview/_volverDeVistaTerminos en
   cv.js). Esto elimina el bug del "doble botón Volver" — uno cerraba
   todo el popup de compra, el otro solo cambiaba de vista, y ambos
   apilados confundían al usuario. */
.rv-terms-view-body {
    padding: 20px 28px 32px;
    overflow-y: auto;
    color: #333;
    font-family: 'Inter', system-ui, sans-serif;
}

.rv-terms-view-updated {
    font-size: 0.78rem;
    color: #999;
    margin: 0 0 18px;
}

/* ── Tarjetas con insignia numerada ──
   Mismo lenguaje visual reconocible de terminos.html (badge circular con
   gradiente de marca + tarjeta con borde sutil), adaptado a la paleta
   CLARA del modal de pago (terminos.html usa tema oscuro standalone) —
   así el panel se "siente" como el mismo componente reutilizado, no como
   una vista improvisada de lista plana <h4>/<p>. */
.rv-terms-card {
    position: relative;
    background: #f8f9fc;
    border: 1px solid #e8ecf2;
    border-radius: 14px;
    padding: 18px 20px 18px 60px;
    margin-bottom: 14px;
    transition: border-color 0.2s ease, background 0.2s ease;
}

.rv-terms-card:hover {
    border-color: #c8d4e8;
    background: #f5f8ff;
}

.rv-terms-card::before {
    content: attr(data-num);
    position: absolute;
    top: 18px;
    left: 16px;
    width: 28px;
    height: 28px;
    border-radius: 8px;
    background: linear-gradient(135deg, #4a6fa5 0%, #5e81ac 100%);
    color: #fff;
    font-size: 0.78rem;
    font-weight: 800;
    display: flex;
    align-items: center;
    justify-content: center;
    box-shadow: 0 3px 10px rgba(74, 111, 165, 0.3);
    flex-shrink: 0;
}

.rv-terms-card h4 {
    font-size: 0.92rem;
    font-weight: 700;
    color: #1a2540;
    margin: 0 0 8px;
}

.rv-terms-card p,
.rv-terms-card li {
    font-size: 0.83rem;
    line-height: 1.55;
    color: #4a5568;
    margin: 0;
}

.rv-terms-card ul {
    margin: 4px 0 0 18px;
    padding: 0;
}

.rv-terms-card li {
    margin-bottom: 4px;
}

.rv-terms-view-footer {
    margin-top: 10px;
    padding-top: 16px;
    border-top: 1px solid #f0f0f0;
    font-size: 0.75rem;
    color: #999;
    text-align: center;
}

@media (max-width: 767px) {
    .rv-terms-view-body {
        padding: 16px 20px 28px;
    }
    .rv-terms-card {
        padding: 16px 16px 16px 52px;
    }
    .rv-terms-card::before {
        left: 12px;
        top: 16px;
        width: 26px;
        height: 26px;
        font-size: 0.74rem;
    }
}

/* ── Compras en el panel de descargas ───────────────────────── */
.rv-purchase-item-mini {

    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    padding: 8px 0;
    border-bottom: 1px dashed #e8ecf2;
}

.rv-purchase-item-mini:last-child {
    border-bottom: none;
}

.rv-purchase-name-mini {
    font-size: 0.8rem;
    font-weight: 600;
    color: #1a2540;
    font-family: 'Inter', system-ui, sans-serif;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 120px;
}

/* FIX (Fase PWA/móvil): este elemento pasó de <a> a <button> (ver cv.js
   _descargarPdfComoBlob) para evitar que la descarga navegue fuera de la
   app en contexto PWA instalado. Se añaden reglas explícitas de reseteo
   de <button> (border:none, cursor:pointer) que un <a> no necesitaba. */
.rv-purchase-btn-mini {
    /* FIX (bug botón gigante): la regla global `button { width:100% }`
       de formulario.css afecta a todo <button> del sitio. Este elemento
       pasó de <a> a <button> (ver comentario más abajo) y nunca se le
       neutralizó ese ancho heredado, por lo que ocupaba todo el ancho
       del contenedor y desplazaba/ocultaba el nombre del CV al lado.
       width:auto restaura el tamaño compacto original. */
    width: auto;
    flex-shrink: 0;
    padding: 4px 10px;
    border: none;
    border-radius: 6px;
    background: #27ae60;
    color: #ffffff;
    font-size: 0.75rem;
    font-weight: 600;
    text-decoration: none;
    font-family: 'Inter', system-ui, sans-serif;
    transition: background 0.15s;
    white-space: nowrap;
    cursor: pointer;
}



.rv-purchase-btn-mini:hover {
    background: #219a52;
}

/* ============================================================
   RESPONSIVE — MODAL v4.0
   ============================================================ */
@media (max-width: 767px) {

    /* ── Contenedor: bottom sheet con footer siempre visible ─────
       CLAVE: overflow-y en el contenedor rompe position:sticky del
       footer. Solución: el contenedor es flex column con altura fija.
       Solo el área de contenido (todo excepto header y footer) hace
       scroll. El footer queda pegado al fondo siempre.
    ─────────────────────────────────────────────────────────── */
    .rv-modal-container--wide {
        /*
         * PANTALLA COMPLETA en móvil:
         * inset: 0 cubre toda la pantalla (top, right, bottom, left = 0).
         * Esto resuelve de raíz dos problemas en iOS PWA (modo standalone):
         *
         * 1. PRECIOS NO VISIBLES: Con height: 96dvh, la barra de gestos
         *    inferior de iOS consumía espacio extra y el footer quedaba
         *    fuera de la pantalla visible. Con inset:0 + 100dvh el modal
         *    ocupa exactamente el espacio disponible real.
         *
         * 2. SCROLL DEL FONDO: En modo PWA, overflow:hidden en body no
         *    funciona en iOS Safari. Con inset:0 no hay "fondo" que pueda
         *    hacer scroll — el modal ES la pantalla completa.
         */
        max-width: 100%;
        border-radius: 0;           /* sin bordes — ocupa toda la pantalla */
        position: fixed;
        inset: 0;                   /* top:0 right:0 bottom:0 left:0 */
        height: 100vh;
        height: 100svh;
        height: 100dvh;             /* altura real disponible (excluye barras dinámicas) */
        max-height: none;           /* sin límite — inset:0 ya lo controla */
        /* Flex column: header fijo + contenido scrollable + footer fijo */
        display: flex;
        flex-direction: column;
        overflow: hidden;           /* el scroll lo gestiona .rv-modal-scroll-area */
        box-shadow: none;           /* sin sombra — es pantalla completa */
        /* Animación de entrada: slide desde abajo */
        transform: translateY(4px);
        transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
    }

    /* Cuando el overlay está visible, el contenedor sube a su posición final */
    .rv-modal-overlay--visible .rv-modal-container--wide {
        transform: translateY(0);
    }

    /* ── Área scrollable: sticky footer puro ────────────────────
     * CLAVE: flex: 1 + min-height: 0 es la única combinación correcta.
     * - flex: 1 → ocupa TODO el espacio entre header y footer.
     * - min-height: 0 → CRÍTICO en iOS Safari: sin esto el flex child
     *   ignora el límite del contenedor y desborda empujando el footer.
     * - NO usar max-height aquí: compite con flex: 1 y causa que el
     *   scroll-area crezca más allá del espacio disponible en iOS PWA.
     ─────────────────────────────────────────────────────────── */
    .rv-modal-scroll-area {
        flex: 1;
        min-height: 0;
        overflow-y: auto;
        overflow-x: hidden;
        -webkit-overflow-scrolling: touch;
        display: flex;
        flex-direction: column;
    }

    /* ── Footer: anclado al fondo — nunca se encoge ni desplaza ──
     * flex: 0 0 auto es más explícito que flex-shrink: 0:
     * - flex-grow: 0   → no crece
     * - flex-shrink: 0 → NUNCA se encoge (garantía absoluta)
     * - flex-basis: auto → toma su altura natural
     * padding-bottom con safe-area-inset para home indicator iPhone.
     ─────────────────────────────────────────────────────────── */
    .rv-modal-footer {
        flex: 0 0 auto;
        position: static;
        border-top: 1px solid #e8ecf2;
        background: #ffffff;
        padding-bottom: max(12px, env(safe-area-inset-bottom));
    }

    .rv-modal-overlay {
        /*
         * PANTALLA COMPLETA: el modal ya no es un bottom sheet.
         * align-items + justify-content: stretch hacen que el contenedor
         * llene todo el overlay sin gaps. padding: 0 elimina el espacio lateral.
         */
        align-items: stretch;
        justify-content: stretch;
        padding: 0;
    }

    /* ── Header en móvil — safe-area-inset-top para notch/Dynamic Island ── */
    .rv-modal-container--wide .rv-modal-header {
        /*
         * PANTALLA COMPLETA: el header toca el borde superior de la pantalla.
         * padding-top con safe-area-inset-top garantiza que el contenido
         * no quede tapado por el notch o el Dynamic Island del iPhone 15 Pro.
         * max(14px, ...) asegura un mínimo de 14px incluso en dispositivos sin notch.
         */
        border-radius: 0;
        padding-top: max(14px, env(safe-area-inset-top));
        padding-left: 16px;
        padding-right: 16px;
        padding-bottom: 12px;
    }

    .rv-modal-container--wide .rv-modal-title {
        font-size: 0.9rem;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }

    /* Fix crítico: el botón "← Volver" heredaba width:100% de reglas globales */
    .rv-modal-back {
        width: auto;
        flex-shrink: 0;
        padding: 5px 10px;
        font-size: 0.78rem;
    }

    /* Fix: botón ✕ con tamaño fijo en móvil */
    .rv-modal-container--wide .rv-modal-close {
        width: 30px;
        height: 30px;
        flex-shrink: 0;
    }

    /* ── Aviso marca de agua en móvil ────────────────────────── */
    .rv-modal-watermark-notice {
        padding: 8px 16px;
        font-size: 0.75rem;
    }

    /* ── Grid → scroll snap horizontal ──────────────────────────
       Las 3 columnas se convierten en cards deslizables tipo
       carrusel. El usuario hace swipe para ver cada estilo.
       scroll-snap garantiza que siempre quede centrada una card.
       ─────────────────────────────────────────────────────────
       FIX AUDITORÍA MÓVIL (iPhone 15 Pro y pantallas de poca altura):
       El grid ahora ocupa TODO el alto disponible del área scrollable
       (flex:1 + min-height:0, igual patrón que .rv-modal-scroll-area).
       overflow-y pasa de "hidden" a "visible": ya no recorta el
       contenido inferior de la card (precio, paginación) cuando la
       suma de elementos internos supera el alto de pantalla. El
       overflow real (si llegara a hacer falta) lo sigue gestionando
       el padre .rv-modal-scroll-area, que ya tiene overflow-y:auto.
    ─────────────────────────────────────────────────────────── */
    .rv-styles-grid {
        display: flex;
        flex-direction: row;
        flex: 1;
        min-height: 0;
        /*
         * FIX 1 — Cards cortadas por los lados (ancho):
         * Sin gap: el ancho de cada card se calcula exacto al 100%
         * del viewport del grid (ver .rv-style-col), evitando que
         * se asome un "sliver" de la card contigua en Safari iOS.
         */
        gap: 0;
        padding: 14px 16px 10px;
        overflow-x: auto;
        overflow-y: visible;
        scroll-snap-type: x mandatory;
        -webkit-overflow-scrolling: touch;
        scrollbar-width: none;
        background: #f4f7fb;
        /*
         * scroll-padding-inline centra el snap en ambos extremos.
         * Reemplaza scroll-padding-left que solo funcionaba al inicio.
         */
        scroll-padding-inline: 16px;
    }

    .rv-styles-grid::-webkit-scrollbar {
        display: none;
    }

    /* Cada columna: una card por pantalla, sin asomar las laterales */
    .rv-style-col {
        /*
         * FIX 1 — Una card por pantalla:
         * min(100%, 360px) = ancho del grid (o 360px máx en pantallas
         * grandes) sin depender de gap externo, evitando el desajuste
         * de scroll-snap que dejaba asomar la card siguiente.
         */
        flex: 0 0 min(100%, 360px);
        width: min(100%, 360px);
        max-width: 360px;
        /*
         * FIX AUDITORÍA MÓVIL — Card ocupa TODO el alto del grid:
         * height:100% + flex-shrink:0 en los hijos "fijos" garantiza
         * que precio/paginación NUNCA se recorten: solo el carrusel
         * de imagen (que es flex:1) cede espacio cuando la pantalla
         * es más baja (iPhone con Dynamic Island/gestos).
         */
        /*
         * FIX AUDITORÍA — iPhone 15 Pro / pantallas cortas con toolbar
         * dinámica de Safari (dvh no siempre refleja el espacio real
         * disponible cuando la barra de direcciones está visible):
         *
         * ANTES: height:100% + overflow-y:visible en el padre permitía
         * que, si el contenido mínimo de la card (badge+label+carrusel+
         * nav+footer+botón) superaba el alto real disponible, el exceso
         * se desbordara visualmente FUERA de la card. Ese desborde caía
         * justo donde se insertan los "scroll dots" (hermano posterior
         * del grid), cuyo fondo opaco terminaba TAPANDO el precio y la
         * paginación — exactamente el bug reportado en iPhone 15 Pro
         * (en Samsung S20 Ultra hay más alto disponible y nunca se
         * llegaba a desbordar, por eso allí funcionaba bien).
         *
         * AHORA: min-height/max-height:100% + overflow-y:auto convierte
         * la card en su propio contenedor con scroll interno. Si el
         * contenido no cabe, el USUARIO puede hacer un pequeño scroll
         * vertical DENTRO de la card para ver precio/paginación/botón,
         * pero JAMÁS quedan ocultos o tapados por otro elemento — es
         * una red de seguridad válida para cualquier alto de pantalla,
         * independientemente de barras de navegador, notch o Dynamic
         * Island.
         */
        min-height: 100%;
        max-height: 100%;
        overflow-y: auto;
        -webkit-overflow-scrolling: touch;
        scrollbar-width: none;
        display: flex;
        flex-direction: column;
        align-items: center;
        gap: 8px;
        padding: 12px 10px 14px;
        margin-right: 16px;
        scroll-snap-align: start;
        scroll-margin-inline-start: 16px;
        /* Tap-to-select: cursor pointer en toda la columna */
        cursor: pointer;
        /* Transición suave al seleccionar */
        transition: border-color 0.2s, box-shadow 0.2s, background 0.2s, transform 0.15s;
    }

    .rv-style-col::-webkit-scrollbar {
        display: none;
    }

    /* Última card: sin margen extra a la derecha (ya lo da el padding del grid) */
    .rv-style-col:last-child {
        margin-right: 0;
    }


    /* Efecto de press al tocar */
    .rv-style-col:active {
        transform: scale(0.97);
    }

    /*
     * FIX AUDITORÍA MÓVIL — Elementos "fijos" de la card NUNCA se comprimen.
     * Antes, al no tener flex-shrink:0 explícito, en pantallas bajas
     * (iPhone 15 Pro: notch + gestos restan altura útil) el navegador
     * comprimía silenciosamente badge/label/nav/footer/botón para intentar
     * que todo cupiera, y como el padre tenía overflow-y:hidden, lo que
     * no cabía se recortaba de raíz (precio y paginación desaparecían).
     * Con flex-shrink:0 estos elementos SIEMPRE mantienen su tamaño natural;
     * el único elemento "elástico" es el carrusel de imagen (flex:1 abajo).
     */
    .rv-badge,
    .rv-col-label,
    .rv-col-nav,
    .rv-col-footer,
    .rv-col-select-btn {
        flex-shrink: 0;
    }

    /* ── Preview más grande en móvil — ahora 100% flexible ─────
       FIX AUDITORÍA MÓVIL: en vez de un alto fijo por aspect-ratio/vh
       (que no considera el alto REAL disponible en cada dispositivo),
       el wrap del preview crece con flex:1 y min-height:0 para ocupar
       exactamente el espacio que sobra tras reservar el resto de
       elementos fijos de la card. Así el carrusel se adapta solo a
       cualquier altura de pantalla sin recortar nada por debajo.
    ─────────────────────────────────────────────────────────── */
    .rv-col-preview-wrap {
        width: 100%;
        flex: 1;
        min-height: 0;
        display: flex;
    }

    .rv-col-carousel {
        /*
         * FIX AUDITORÍA MÓVIL — Carrusel 100% flexible, sin aspect-ratio
         * ni max-height/vh fijos. Ocupa todo el alto que le cede su
         * padre (.rv-col-preview-wrap, flex:1). object-fit:contain en
         * la imagen (ya definido en la regla base .rv-col-img) evita
         * distorsión: si el espacio es más "cuadrado" que un A4, se ve
         * la imagen completa con letterboxing en vez de recortarla.
         * min-height conserva un tamaño mínimo legible en pantallas
         * extremadamente bajas.
         */
        width: 100%;
        height: 100%;
        min-height: 140px;
        aspect-ratio: unset;
        /* Sombra sutil para dar profundidad al preview */
        box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
    }


    /* ── Botón "Seleccionar" explícito en móvil ──────────────────
     * Reemplaza el tap-en-toda-la-card que era ambiguo con el zoom.
     * Solo visible en móvil (display: none en desktop).
     * Estado: inactivo (gris) → activo (verde) al seleccionar.
     ─────────────────────────────────────────────────────────── */
    .rv-col-select-btn {
        display: flex;
        align-items: center;
        justify-content: center;
        gap: 6px;
        width: 100%;
        padding: 10px 16px;
        border-radius: 10px;
        border: 2px solid #c0c8d8;
        background: #f8f9fc;
        color: #5a6a7e;
        font-size: 0.85rem;
        font-weight: 600;
        font-family: 'Inter', system-ui, sans-serif;
        cursor: pointer;
        transition: all 0.2s ease;
        touch-action: manipulation;
        -webkit-tap-highlight-color: transparent;
        /* Área táctil mínima WCAG: 44px */
        min-height: 44px;
    }

    .rv-col-select-btn:disabled {
        opacity: 0.45;
        cursor: not-allowed;
    }

    /* Estado: seleccionado */
    .rv-col-select-btn--selected {
        border-color: #27ae60;
        background: #27ae60;
        color: #ffffff;
        box-shadow: 0 2px 12px rgba(39, 174, 96, 0.3);
    }

    .rv-col-select-btn--selected:active {
        background: #219a52;
        border-color: #219a52;
    }

    /* ── Badges visibles en móvil (scroll horizontal da espacio) */
    .rv-badge {
        display: inline-block;
    }

    /* ── Checkbox oculto en móvil — tap en columna = seleccionar */
    .rv-col-check-label {
        display: none;
    }

    /* Indicador visual de selección en móvil:
       Cuando la columna está seleccionada, mostrar un checkmark
       en la esquina superior derecha de la card */
    .rv-style-col.rv-col--selected::after {
        content: '✓';
        position: absolute;
        top: 8px;
        right: 8px;
        width: 22px;
        height: 22px;
        background: #27ae60;
        color: #ffffff;
        border-radius: 50%;
        font-size: 0.7rem;
        font-weight: 700;
        display: flex;
        align-items: center;
        justify-content: center;
        line-height: 22px;
        text-align: center;
        box-shadow: 0 2px 8px rgba(39, 174, 96, 0.4);
        z-index: 5;
    }

    /* ── Dots indicadores de posición ────────────────────────── */
    /* Se añaden via JS — ver _initMobileInteractions() en cv.js.
       FIX AUDITORÍA (iPhone 15 Pro): antes se insertaban como hermano
       de #rv-styles-grid DENTRO de .rv-modal-scroll-area, robando
       espacio flex al grid en pantallas cortas y, peor aún, su fondo
       opaco terminaba pintándose ENCIMA del precio/paginación de la
       card cuando esta desbordaba (ver notas en .rv-style-col).
       Ahora se insertan dentro de .rv-modal-footer (flex-shrink:0,
       siempre visible, fuera del área que hace scroll), así nunca
       compiten por espacio ni pueden tapar nada. order:-2 los coloca
       por encima del panel de precio dentro del footer en reverse. */
    .rv-scroll-dots {
        display: flex;
        justify-content: center;
        gap: 6px;
        padding: 0 0 4px;
        background: transparent;
        width: 100%;
        order: -2;
    }

    .rv-scroll-dot {
        width: 6px;
        height: 6px;
        border-radius: 50%;
        background: #c8d4e8;
        transition: background 0.2s, transform 0.2s;
        flex-shrink: 0;
    }

    .rv-scroll-dot--active {
        background: #4a6fa5;
        transform: scale(1.3);
    }

    /* ── Footer en móvil: precio PRIMERO, descargas debajo ──────
       En desktop el footer es horizontal (precio a la derecha).
       En móvil lo invertimos: precio + botón Stripe visible sin
       scroll, panel de descargas colapsable debajo.
    ─────────────────────────────────────────────────────────── */
    .rv-modal-footer {
        flex-direction: column-reverse; /* precio arriba, descargas abajo */
        gap: 10px;
        padding: 12px 16px;
        padding-bottom: max(env(safe-area-inset-bottom, 12px), 12px);
    }

    .rv-price-panel {
        min-width: 0;
        max-width: 100%;
        width: 100%;
        order: -1; /* garantiza que aparece primero aunque column-reverse */
    }

    .rv-downloads-panel {
        width: 100%;
    }

    /* Botón Stripe más grande en móvil */
    .rv-btn-stripe {
        padding: 16px 20px;
        font-size: 0.95rem;
    }
}

@media (min-width: 768px) and (max-width: 900px) {
    .rv-styles-grid {
        gap: 10px;
        padding: 14px 16px;
    }

    .rv-col-gen-btn {
        padding: 12px 10px;
        font-size: 0.72rem;
    }
}

/* ============================================================
   LIGHTBOX DE ZOOM — rv-lightbox-*
   Se abre al hacer clic/tap en cualquier imagen del mini-carrusel.
   Permite al usuario leer el contenido del CV antes de comprar.

   Principios de accesibilidad y buenas prácticas:
   · role="dialog" + aria-modal="true" + aria-label
   · Cierre con Escape, clic en overlay o botón ✕
   · focus-trap: el foco queda dentro del lightbox mientras está abierto
   · pointer-events: none en la imagen (no se puede guardar con clic derecho)
   · touch-action: pinch-zoom — zoom nativo con dos dedos en iOS/Android
   · user-scalable=yes en el meta viewport (ya corregido) es NECESARIO
     para que pinch-zoom funcione en iOS Safari
   ============================================================ */

/* ── Overlay del lightbox ────────────────────────────────────── */
.rv-lightbox-overlay {
    position: fixed;
    inset: 0;
    z-index: 10000; /* por encima del modal (z-index: 9000) */
    background: rgba(0, 0, 0, 0.92);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    opacity: 0;
    transition: opacity 0.2s ease;
    /* Permitir scroll/zoom táctil en el contenedor */
    touch-action: pan-x pan-y pinch-zoom;
    -webkit-tap-highlight-color: transparent;
}

.rv-lightbox-overlay--visible {
    opacity: 1;
}

/* ── Contenedor de la imagen ─────────────────────────────────── */
.rv-lightbox-img-wrap {
    position: relative;
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: auto;
    /* Permitir pinch-zoom nativo en iOS/Android */
    touch-action: pan-x pan-y pinch-zoom;
}

/* ── Imagen del lightbox ─────────────────────────────────────── */
.rv-lightbox-img {
    /*
     * En móvil: la imagen ocupa el 100% del ancho de pantalla.
     * El usuario puede hacer pinch-zoom para ampliar.
     * En desktop: la imagen se escala para caber en la pantalla
     * con un margen de 48px en cada lado para el botón de cierre.
     */
    max-width: 100vw;
    max-height: 100dvh;
    max-height: 100svh;
    max-height: 100vh;
    width: auto;
    height: auto;
    object-fit: contain;
    display: block;
    border-radius: 0;
    /* Prevenir guardado con clic derecho / long-press */
    pointer-events: none;
    user-select: none;
    -webkit-user-select: none;
    -webkit-user-drag: none;
    /* Suavizar la aparición */
    transition: opacity 0.15s ease;
}

/* En desktop: añadir margen para que el botón ✕ no tape la imagen */
@media (min-width: 768px) {
    .rv-lightbox-img {
        max-width: calc(100vw - 96px);
        max-height: calc(100vh - 96px);
        border-radius: 4px;
        box-shadow: 0 8px 40px rgba(0, 0, 0, 0.5);
    }
}

/* ── Botón de cierre ─────────────────────────────────────────── */
.rv-lightbox-close {
    position: fixed;
    top: 16px;
    right: 16px;
    z-index: 10001;
    width: 44px;
    height: 44px;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.15);
    border: 1.5px solid rgba(255, 255, 255, 0.3);
    color: #ffffff;
    font-size: 1.2rem;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: background 0.15s, transform 0.15s;
    /* Tamaño mínimo de área táctil recomendado por WCAG: 44×44px */
    touch-action: manipulation;
    -webkit-tap-highlight-color: transparent;
    font-family: 'Inter', system-ui, sans-serif;
    line-height: 1;
}

.rv-lightbox-close:hover {
    background: rgba(255, 255, 255, 0.28);
    transform: scale(1.1);
}

.rv-lightbox-close:active {
    transform: scale(0.95);
}

/* ── Hint de zoom (solo en móvil, desaparece tras 2s) ────────── */
.rv-lightbox-hint {
    position: fixed;
    bottom: max(24px, env(safe-area-inset-bottom, 24px));
    left: 50%;
    transform: translateX(-50%);
    background: rgba(0, 0, 0, 0.65);
    color: rgba(255, 255, 255, 0.9);
    font-size: 0.78rem;
    font-family: 'Inter', system-ui, sans-serif;
    padding: 8px 18px;
    border-radius: 20px;
    white-space: nowrap;
    pointer-events: none;
    opacity: 1;
    transition: opacity 0.5s ease;
    z-index: 10001;
}

.rv-lightbox-hint--hidden {
    opacity: 0;
}

/* ── Botón "Seleccionar" oculto en desktop (solo visible en móvil) ── */
.rv-col-select-btn {
    display: none;
}

/* ============================================================
   VISTA PREVIA MÓVIL — max-height + FAB "Obtener PDF"
   ============================================================ */

/* En móvil: la vista previa tiene altura máxima y scroll interno
   para que no ocupe toda la pantalla y el usuario pueda seguir
   editando el formulario sin hacer scroll infinito. */
@media (max-width: 1199px) {
    #vistaPrevia {
        max-height: 70vh;
        overflow-y: auto;
        -webkit-overflow-scrolling: touch;
        /* Espacio inferior para que el FAB no tape el contenido */
        padding-bottom: 80px;
        /* Scrollbar discreta */
        scrollbar-width: thin;
        scrollbar-color: rgba(74, 111, 165, 0.3) transparent;
    }

    #vistaPrevia::-webkit-scrollbar {
        width: 4px;
    }

    #vistaPrevia::-webkit-scrollbar-thumb {
        background: rgba(74, 111, 165, 0.3);
        border-radius: 4px;
    }

    /* El CTA wrapper se oculta en móvil/tablet — lo reemplaza el FAB fijo */
    .cv-cta-wrapper {
        display: none;
    }
}

/* ── FAB "Obtener PDF" — barra flotante fija en la parte inferior (móvil) ──
   Solo visible en móvil cuando la vista previa está activa.
   Se inyecta dinámicamente desde JS al generar la vista previa.
── */
.cv-fab-pdf-bar {
    display: none; /* oculto por defecto — solo visible en móvil */
}

@media (max-width: 1199px) {
    .cv-fab-pdf-bar {
        display: flex;
        align-items: center;
        justify-content: center;
        gap: 10px;

        /* Posición fija en la parte inferior */
        position: fixed;
        bottom: 0;
        left: 0;
        right: 0;
        z-index: 500;

        /* Altura generosa para área táctil cómoda */
        padding: 12px 20px;
        padding-bottom: max(12px, env(safe-area-inset-bottom));

        /* Fondo con blur para separarse del contenido */
        background: rgba(10, 15, 30, 0.88);
        backdrop-filter: blur(12px);
        -webkit-backdrop-filter: blur(12px);

        /* Sombra superior para separar del contenido */
        box-shadow: 0 -4px 24px rgba(0, 0, 0, 0.35), 0 -1px 0 rgba(74, 111, 165, 0.2);

        /* Animación de entrada desde abajo */
        transform: translateY(100%);
        transition: transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1);
    }

    /* Estado visible — se activa desde JS añadiendo la clase .visible */
    .cv-fab-pdf-bar.visible {
        transform: translateY(0);
    }

    /* Botón dentro del FAB */
    .cv-fab-pdf-bar .cv-blur-btn {
        /* Ocupa casi todo el ancho disponible */
        width: 100%;
        max-width: 400px;
        padding: 16px 24px;
        font-size: 1rem;
        border-radius: 14px;
        /* Quitar animación de pulso en el FAB — ya llama la atención por su posición */
        animation: none;
        box-shadow:
            0 4px 20px rgba(74, 111, 165, 0.5),
            0 2px 8px rgba(0, 0, 0, 0.3);
    }

    .cv-fab-pdf-bar .cv-blur-btn:hover,
    .cv-fab-pdf-bar .cv-blur-btn:active {
        transform: scale(0.98);
        transition-duration: 0.1s;
    }

    /* El formulario necesita padding-bottom para que el FAB no tape el último campo */
    #app-container {
        padding-bottom: 90px;
    }
}

/* Nota: rango 768-1199 (tablet) — el FAB ya se muestra (ver arriba, ahora
   con umbral 1199px), pero conserva el mismo estilo de barra flotante que
   en móvil, priorizando consistencia sobre densidad de layout. */

/* ============================================================
   LAYOUT SPLIT-SCREEN — DESKTOP ≥ 1200px
   ============================================================
   NOTA: Este bloque ha sido neutralizado.
   El layout split-screen está gestionado íntegramente por
   formulario.css mediante #cv-workspace (position:fixed) +
   #cv-form-panel + #cv-preview-panel.
   Las definiciones de #app-container con display:grid y
   #cv-preview-panel con position:sticky aquí causaban conflictos
   directos con ese sistema, rompiendo la barra de progreso y
   el scroll del panel izquierdo.
   ============================================================ */

/* ============================================================
   CARRUSEL A4 — DESKTOP
   ============================================================
   Estructura generada por JS:
   .cv-a4-carousel
     .cv-a4-carousel-viewport   ← overflow:hidden, aspect-ratio A4
       .cv-a4-carousel-track    ← flex, translateX para navegar
         .cv-a4-carousel-slide  ← flex:0 0 100%, contiene .cv-a4-page
     .cv-a4-carousel-nav        ← prev | dots | page-info | next
   ============================================================ */
.cv-a4-carousel {
    width: 100%;
    display: flex;
    flex-direction: column;
    gap: 12px;
}

/* Viewport: recorta los slides fuera de vista */
.cv-a4-carousel-viewport {
    width: 100%;
    overflow: hidden;
    border-radius: 4px;
    /* Sombra de "mesa de trabajo" */
    box-shadow:
        0 2px 4px rgba(0, 0, 0, 0.08),
        0 8px 24px rgba(0, 0, 0, 0.12),
        0 20px 48px rgba(0, 0, 0, 0.10);
}

/* Track: fila de slides, se desplaza con translateX */
.cv-a4-carousel-track {
    display: flex;
    transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);
    will-change: transform;
    /* FIX AUDITORÍA (swipe accidental): ver nota equivalente en
       formulario.css — permite scroll vertical nativo por defecto. */
    touch-action: pan-y;
}

/* Sin animación (para restaurar posición sin transición visible) */
.cv-a4-carousel-track.no-transition {
    transition: none !important;
}

/* Cada slide: el JS asigna width/min-width inline (ancho escalado) */
.cv-a4-carousel-slide {
    flex: 0 0 auto;
    min-width: 0;
    overflow: hidden;
}

/* La página A4 dentro del slide: el JS asigna width/height/transform inline */
.cv-a4-carousel-slide .cv-a4-page {
    /* No sobreescribir — el JS gestiona dimensiones en desktop */
}

/* ── Marca de agua del viewport desktop ──
   Aparece sobre el carrusel completo (no por página).
   Indica al usuario que es solo una guía visual.
   pointer-events: none → no interfiere con la interacción.
   z-index: 5 → por encima del carrusel pero debajo del nav.
── */
.cv-a4-viewport-watermark {
    position: absolute;
    bottom: 8px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 5;
    pointer-events: none;
    user-select: none;
    -webkit-user-select: none;
    white-space: nowrap;
}

.cv-a4-viewport-watermark span {
    display: inline-block;
    font-size: 0.62rem;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: rgba(122, 163, 212, 0.45);
    font-family: 'Inter', system-ui, sans-serif;
    background: rgba(10, 15, 30, 0.55);
    padding: 3px 10px;
    border-radius: 10px;
    border: 1px solid rgba(74, 111, 165, 0.15);
}

/* ── Barra de navegación del carrusel A4 ── */
.cv-a4-carousel-nav {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    padding: 4px 0;
}

.cv-a4-carousel-btn {
    background: rgba(74, 111, 165, 0.12);
    border: 1px solid rgba(74, 111, 165, 0.25);
    border-radius: 8px;
    width: 32px;
    height: 32px;
    font-size: 1.1rem;
    cursor: pointer;
    color: rgba(122, 163, 212, 0.9);
    transition: background 0.15s, border-color 0.15s, color 0.15s;
    display: flex;
    align-items: center;
    justify-content: center;
    line-height: 1;
    /* Ancho fijo — no heredar width:100% del botón global */
    width: 32px !important;
    padding: 0 !important;
}

.cv-a4-carousel-btn:hover:not(:disabled) {
    background: rgba(74, 111, 165, 0.25);
    border-color: rgba(74, 111, 165, 0.5);
    color: #7aa3d4;
}

.cv-a4-carousel-btn:disabled {
    opacity: 0.25;
    cursor: not-allowed;
}

/* Dots de posición */
.cv-a4-carousel-dots {
    display: flex;
    gap: 6px;
    align-items: center;
}

.cv-a4-carousel-dot {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: rgba(94, 129, 172, 0.3);
    transition: background 0.2s, transform 0.2s;
    cursor: pointer;
    flex-shrink: 0;
}

.cv-a4-carousel-dot.active {
    background: #4a6fa5;
    transform: scale(1.4);
}

/* Indicador de página "1 / 2" */
.cv-a4-carousel-page-info {
    font-size: 0.72rem;
    color: rgba(122, 163, 212, 0.6);
    font-family: 'Inter', system-ui, sans-serif;
    min-width: 36px;
    text-align: center;
    letter-spacing: 0.04em;
}

/* ── Cursor zoom-in en desktop sobre las imágenes del carrusel ── */
.rv-col-img-wrap {
    cursor: zoom-in;
}

/* En móvil el cursor no aplica, pero el tap-to-zoom sí */
@media (max-width: 767px) {
    .rv-col-img-wrap {
        cursor: default;
    }

    /*
     * FIX 4 — Icono lupa sobre las imágenes en móvil:
     * En desktop el cursor zoom-in ya indica que se puede ampliar.
     * En móvil no hay cursor, así que añadimos un pseudo-elemento
     * con el icono 🔍 en la esquina inferior derecha de cada imagen
     * generada. Solo aparece en columnas con preview disponible
     * (.rv-col--generated) para no confundir en placeholders.
     */
    .rv-col--generated .rv-col-img-wrap::after {
        content: '🔍';
        position: absolute;
        bottom: 8px;
        right: 8px;
        width: 32px;
        height: 32px;
        background: rgba(0, 0, 0, 0.45);
        border-radius: 50%;
        font-size: 0.9rem;
        display: flex;
        align-items: center;
        justify-content: center;
        pointer-events: none;
        z-index: 15;
        line-height: 32px;
        text-align: center;
    }
}

/* ============================================================
   AVISO IN-APP DE DESCARGA (rv-alert-*)
   ============================================================
   FIX (Fase PWA/móvil — feedback claro sin abandonar la app):
   Overlay modal ligero, mismo lenguaje visual del lightbox
   (fondo oscurecido con blur + tarjeta centrada), usado por
   _mostrarAvisoDescarga() en cv.js para comunicar con total
   transparencia los errores de descarga (enlace expirado,
   límite de 3 descargas, error de red, etc.) devueltos por
   download.php — sin navegar nunca fuera del popup de compras
   y sin depender de un toast efímero para un mensaje importante.
   ============================================================ */
.rv-alert-overlay {
    position: fixed;
    inset: 0;
    z-index: 100000;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 20px;
    background: rgba(15, 15, 20, 0.6);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    opacity: 0;
    transition: opacity 0.28s ease;
}

.rv-alert-overlay--visible {
    opacity: 1;
}

.rv-alert-card {
    background: #ffffff;
    border-radius: 16px;
    max-width: 380px;
    width: 100%;
    padding: 28px 24px 24px;
    text-align: center;
    box-shadow: 0 20px 60px rgba(0, 0, 0, 0.35);
    transform: translateY(8px) scale(0.97);
    transition: transform 0.28s ease;
}

.rv-alert-overlay--visible .rv-alert-card {
    transform: translateY(0) scale(1);
}

.rv-alert-icon {
    font-size: 2.4rem;
    margin-bottom: 10px;
    line-height: 1;
}

.rv-alert-title {
    font-size: 1.05rem;
    font-weight: 700;
    color: #1a1a1a;
    margin: 0 0 8px;
}

.rv-alert-message {
    font-size: 0.9rem;
    line-height: 1.5;
    color: #555;
    margin: 0 0 20px;
}

.rv-alert-btn {
    display: inline-block;
    background: #4a6fa5;
    color: #fff;
    border: none;
    border-radius: 10px;
    padding: 10px 28px;
    font-size: 0.9rem;
    font-weight: 600;
    cursor: pointer;
    transition: background 0.2s ease, transform 0.15s ease;
}

.rv-alert-btn:hover {
    background: #3d5c8a;
}

.rv-alert-btn:active {
    transform: scale(0.97);
}
