/* =====================================================================
   PORTFOLIO ANIMADO — página dedicada, un proyecto por pantalla
   Reutiliza los design tokens y componentes de styles.css (header, nav,
   fondo global, blobs, botones, pf-tag). Este archivo solo agrega lo
   específico de la experiencia de scroll-snap por proyecto.

   ÍNDICE
   1. Track y paneles base
   2. Panel intro
   3. Panel de proyecto (media + contenido)
   4. Panel outro / CTA final
   5. Filtros por categoría
   6. Navegación lateral (dots) + contador
   7. Indicador de scroll (intro)
   8. Breakpoints
   ===================================================================== */

/* =====================================================================
   1. TRACK Y PANELES BASE
   ===================================================================== */
/* El snap lo maneja el navegador y NADA MÁS que el navegador, en desktop
   y en touch por igual. Antes esto estaba delegado a JS (wheel-jack en
   desktop, swipe interceptado en touch) MIENTRAS este "mandatory" seguía
   activo en pointer:fine: el navegador re-snapeaba por su cuenta cada
   window.scrollTo() de la animación manual, y encima "html" tiene
   scroll-behavior:smooth global — tres sistemas moviendo el mismo scroll,
   que es lo que se sentía "raro" en desktop. En touch pasaba lo inverso:
   el JS reseteaba el gesto cada 40px, así que un flick normal (~260px)
   avanzaba 5 proyectos de una.

   La pieza que hacía falta para "un gesto = un proyecto" sin escribir un
   solo scroll a mano es "scroll-snap-stop:always": obliga al navegador a
   frenar en CADA punto de snap, incluso en un flick largo con inercia, así
   que ya no se puede saltear un proyecto ni con el dedo ni con el trackpad.
   Es la misma garantía que intentaba dar el JS, pero hecha por el motor de
   scroll, que sí sabe distinguir el gesto real de su propia inercia. */
html[data-pa-scroll]{
    scroll-snap-type:y mandatory;
    /* "clip" en vez del "hidden" que trae styles.css, y no es un detalle:
       "hidden" convierte a html en un contenedor de scroll por derecho
       propio (queda con la altura de la pantalla y scrollea su contenido),
       y en ese estado el scroll-snap-type de arriba no llega a aplicarse:
       el scroll se sentía completamente normal, sin snap.
       "clip" recorta exactamente igual el desborde horizontal —que es lo
       único que se buscaba— pero NO crea contenedor de scroll, así que el
       que scrollea vuelve a ser el viewport y el snap por fin funciona. */
    overflow-x:clip;
}
/* CLAVE, y nada obvio: styles.css le pone "overflow-x:hidden" al body (para
   matar el scroll horizontal en mobile). Por spec, si UN eje del overflow no
   es "visible", el otro deja de serlo también y pasa a "auto" — así que ese
   body queda convertido en un contenedor de scroll por derecho propio.
   Y los puntos de snap se asocian SIEMPRE a su contenedor de scroll más
   cercano: los .pa-panel quedaban colgando de ese body (scroll-snap-type:
   none) en vez del viewport, que es donde vive el "y mandatory" de arriba.
   Resultado: los snap points quedaban huérfanos y el scroll se sentía
   completamente normal, sin snap.
   Devolviéndole "visible" al body en esta página, el contenedor de scroll de
   los paneles vuelve a ser el viewport y el snap de arriba por fin aplica.
   El desborde horizontal lo sigue recortando el "overflow-x:clip" de html
   (ver arriba), así que no se reabre ese agujero. */
html[data-pa-scroll] body{
    overflow-x:visible;
}
/* Excepción puntual: mientras dura un salto programático (dots, flechas del
   contador, teclado, "volver arriba") se relaja el snap. Con
   scroll-snap-stop:always el navegador NO puede "pasar por encima" de los
   paneles intermedios, así que pedirle ir del proyecto 1 al 12 lo deja a
   mitad de camino. La garantía de "un gesto = un proyecto" no se pierde:
   sigue intacta para el gesto del usuario, que es el único que la necesita.
   La clase la pone y la saca goTo() en portfolio-animado.js. */
html[data-pa-scroll].pa-is-jumping{
    scroll-snap-type:none;
}
.pa-track{
    position:relative;
    z-index:1;
}
.pa-panel{
    position:relative;
    /* Altura EXACTA de pantalla, no "min-height". Es lo que hace que el
       snap sea preciso: si el panel es aunque sea unos px más alto que la
       pantalla, pasa a ser un snap area "oversized" y, por spec, CUALQUIER
       posición donde el panel la tape es válida — el snap se relaja, queda
       a mitad de camino y saltea proyectos. Y si es más CORTO, asoma el
       panel siguiente por abajo y se ve una costura.

       Por eso "dvh" y no "svh": "svh" es el alto de pantalla suponiendo
       toda la barra del navegador visible, arriba Y abajo. En iOS, con la
       barra de abajo oculta, el área visible real es más alta que eso, así
       que el panel quedaba corto y se veía asomar el fondo del proyecto
       siguiente. "dvh" sigue el alto visible real momento a momento, que
       es justo lo que se busca: un proyecto ocupando la pantalla completa,
       ni más ni menos, se muestren o no las barras. */
    height:100vh;  /* fallback para navegadores sin unidades dinámicas */
    height:100dvh;
    display:flex;
    align-items:center;
    /* Respiro vertical más ajustado que antes (era 34px arriba + 46px
       abajo). Al fijar la altura al alto de pantalla, ese aire de más era
       justo lo que empujaba al contenido fuera del panel en pantallas
       bajas. Los 68px de arriba son intocables: es el alto del header
       fijo, y sin ellos el contenido se le metería debajo. */
    padding:calc(68px + 16px) 0 24px;
    isolation:isolate;
    overflow:hidden;
    scroll-snap-align:start;
    scroll-snap-stop:always;
}
.pa-panel:nth-child(odd){background:var(--bg-black-2);}

/* =====================================================================
   2. PANEL INTRO
   ===================================================================== */
.pa-intro{
    text-align:center;
    justify-content:center;
}
/* ".pa-panel" trae padding asimétrico (102px arriba para no tapar el
   header fijo, 46px abajo) — bien para los paneles de proyecto, pero en
   el intro ese desbalance corre el centrado vertical (align-items:center)
   unos 28px hacia abajo del centro real de la pantalla. Se empareja solo
   acá y solo en mobile (el ".pa-scroll-cue" es position:absolute, así
   que no le afecta el padding-bottom extra). */
@media (max-width:767px){
    .pa-intro{padding-top:calc(68px + 34px);padding-bottom:calc(68px + 34px);}
}
.pa-intro-inner{
    max-width:900px;
    margin:0 auto;
    padding:0 var(--container-pad);
    position:relative;
    z-index:2;
}
.pa-intro .eyebrow{justify-content:center;}
/* La esfera del eyebrow "titila" con el mismo pulso que ya usa el
   hero-tag de index.html (@keyframes pulse, definido en styles.css):
   reutiliza el lenguaje visual del resto del sitio en vez de inventar
   una animación nueva. Escapado solo a esta pantalla a propósito. */
.pa-intro .eyebrow::before{
    animation:pulse 2s infinite;
}
.pa-intro h1{
    font-size:clamp(2.6rem,9vw,5.6rem);
    line-height:1.1;
    letter-spacing:-0.03em;
    margin:0;
}
.pa-intro h1 span{display:block;}

/* Canvas de malla que sigue al mouse (mismo efecto que ".porque" en
   index.html): se usa acá y también en los paneles de proyecto con el
   fondo negro más oscuro (ver sección 3 y portfolio-animado.js). */
.pa-grid-canvas{
    position:absolute;
    inset:0;
    z-index:1;
    pointer-events:none;
}

/* Esta página tiene dos fondos interactivos distintos: la malla de
   triángulos (".pa-grid-canvas", arriba) en los paneles impares, y el
   negro liso de los paneles ".pa-panel--reverse". La luz de cursor
   (".cursor-glow", ver styles.css) ya sigue al mouse en toda la web y
   ahora se ve un poco más notoria por default — pero en los paneles CON
   malla se deja en su intensidad original de siempre, para que ese brillo
   extra no compita con el efecto de la malla y cada fondo se sienta
   claramente distinto. portfolio-animado.js agrega "pa-grid-active" al
   body mientras el panel activo tiene malla (ver setActive). */
body.pa-grid-active .cursor-glow{
    width:420px;height:420px;
    background:radial-gradient(circle, rgba(47,111,237,0.13) 0%, transparent 70%);
}

/* La luz de fondo del hero usa ".blob-dance-a" (ver styles.css, sección
   3): mismo recurso y misma animación "bailando" que el resto del sitio,
   así el comportamiento es consistente en todas las secciones donde
   aparece un blob. */

/* =====================================================================
   3. PANEL DE PROYECTO
   ===================================================================== */
.pa-panel-inner{
    display:grid;
    grid-template-columns:1fr;
    gap:36px;
    align-items:center;
    position:relative;
    z-index:2;
}

/* --- Lado visual --- */
.pa-media{
    display:flex;
    align-items:center;
    justify-content:center;
}
/* Tarjeta 3D: perspective + rotateX/rotateY vía custom properties (las
   mueve portfolio-animado.js, "mirando" hacia donde está el mouse en
   toda la pantalla — mismo recurso que el tilt 3D de las cards del
   portfolio clásico en script.js, pero sin depender de estar encima).
   --rx/--ry/--glow-x/--glow-y quedan en 0/50% por defecto: sin JS (o en
   touch/reduced-motion, donde el script ni se ejecuta) la tarjeta se ve
   perfectamente plana y estática, sin depender del script para verse bien. */
.pa-media-frame{
    --rx:0deg;
    --ry:0deg;
    --glow-x:50%;
    --glow-y:50%;
    /* Color de la luminiscencia: cada proyecto lo pisa con un inline
       style (--card-glow) acorde a los colores de SU logo (ver HTML).
       --accent-glow es el fallback si alguna tarjeta se agrega sin
       definirlo. --card-glow-soft baja la opacidad UNA sola vez acá
       (color-mix) en vez de tener que definir un rgba() distinto por
       cada una de las 14 tarjetas: el brillo real en pantalla queda
       bastante más sutil que el color sólido de --card-glow. */
    --card-glow: var(--accent-glow);
    --card-glow-soft: color-mix(in srgb, var(--card-glow) 45%, transparent);
    /* Tamaño del halo en variables (no hardcodeado en el box-shadow): así
       el breakpoint mobile de más abajo solo pisa estos 4 números en vez
       de reescribir el box-shadow entero. En mobile la tarjeta queda
       mucho más cerca del texto de abajo, así que el halo tiene que ser
       bastante más chico para no "pisarlo". */
    --glow-blur-a:26px;
    --glow-spread-a:-14px;
    --glow-blur-b:60px;
    --glow-spread-b:-30px;
    position:relative;
    width:100%;
    max-width:230px; /* mobile: bastante más chica (ver breakpoints 680px/768px) */
    aspect-ratio:1/1;
    border-radius:var(--radius-lg);
    background:linear-gradient(160deg,#1a1a20 0%,#0c0c0f 100%);
    border:1px solid var(--border-soft);
    display:flex;
    align-items:center;
    justify-content:center;
    padding:16%;
    box-shadow:
        0 40px 80px -30px rgba(0,0,0,.55),
        0 0 var(--glow-blur-a) var(--glow-spread-a) var(--card-glow-soft),
        0 0 var(--glow-blur-b) var(--glow-spread-b) var(--card-glow-soft);
    transform-style:preserve-3d;
    transform:perspective(1000px) rotateX(var(--rx)) rotateY(var(--ry));
    will-change:transform;
    animation:cardGlowPulse 5s ease-in-out infinite;
}
/* Respiración lenta y sutil del halo: mismo lenguaje "vivo, no estático"
   que ya usan el botón de WhatsApp y el CTA al portfolio animado (ver
   @keyframes wappPulse / btnGlowPulse en styles.css). */
@keyframes cardGlowPulse{
    0%,100%{
        box-shadow:
            0 40px 80px -30px rgba(0,0,0,.55),
            0 0 var(--glow-blur-a) var(--glow-spread-a) var(--card-glow-soft),
            0 0 var(--glow-blur-b) var(--glow-spread-b) var(--card-glow-soft);
    }
    50%{
        box-shadow:
            0 40px 80px -30px rgba(0,0,0,.55),
            0 0 calc(var(--glow-blur-a) + 8px) calc(var(--glow-spread-a) + 3px) var(--card-glow-soft),
            0 0 calc(var(--glow-blur-b) + 16px) calc(var(--glow-spread-b) + 4px) var(--card-glow-soft);
    }
}
/* Capa trasera, desplazada en Z: al rotar la tarjeta asoma por el borde
   que gira "hacia afuera" y da la sensación de grosor/canto real, en vez
   de un plano chato que solo se inclina. */
.pa-media-frame::before{
    content:'';
    position:absolute;
    inset:0;
    border-radius:inherit;
    background:linear-gradient(160deg, rgba(47,111,237,.4), rgba(31,201,195,.3));
    transform:translateZ(-22px);
}
/* Brillo especular que sigue al mouse, como una superficie glossy con
   luz real pegándole — refuerza la sensación de profundidad además de
   la sola rotación. */
.pa-media-frame::after{
    content:'';
    position:absolute;
    inset:0;
    border-radius:inherit;
    background:radial-gradient(circle at var(--glow-x) var(--glow-y), rgba(255,255,255,.18), transparent 55%);
    mix-blend-mode:screen;
    pointer-events:none;
}
/* El contenido (logo/ícono) "flota" adelante del plano de la tarjeta */
.pa-media-frame > img,
.pa-media-frame > span,
.pa-media-frame > svg{
    position:relative;
    transform:translateZ(34px);
}
.pa-media-frame img{
    width:100%;
    height:100%;
    object-fit:contain;
    filter:drop-shadow(0 8px 22px rgba(0,0,0,.4));
}
.pa-media-frame.pf-logo-white img{filter:invert(1) brightness(1.4) drop-shadow(0 8px 22px rgba(0,0,0,.4));}
.pa-media-frame.pa-media-code{
    color:var(--accent-hot);
    font-family:var(--font-mono);
    font-size:4.4rem;
    font-weight:700;
    filter:drop-shadow(0 8px 24px var(--accent-glow));
}
.pa-media-frame.pa-media-svg{
    color:#33b875;
    filter:drop-shadow(0 8px 24px rgba(51,184,117,.35));
}
.pa-media-frame.pa-media-svg svg{width:80%;height:80%;}
.pa-media-frame.pa-media-own img{filter:drop-shadow(0 0 20px rgba(47,111,237,.45));}
.pa-media-frame.pa-media-perfumeria{
    flex-direction:column;
    gap:14px;
    color:#d9b673;
    filter:drop-shadow(0 8px 24px rgba(217,182,115,.3));
}
/* El wordmark es chico y deja mucho "aire" alrededor (a diferencia de un
   logo/foto grande que tapa casi toda la tarjeta), así que el degradé
   azul/verdoso de .pa-media-frame::before queda demasiado a la vista y la
   tarjeta se lee "celeste" en vez de oscura como el resto. Se pisa acá con
   un plano oscuro neutro para que quede pareja con las demás. */
.pa-media-frame.pa-media-perfumeria::before{
    background:linear-gradient(160deg, #17140f 0%, #0a0908 100%);
}
.pa-media-frame.pa-media-perfumeria .pf-logo-script{
    font-family:'Parisienne',cursive;
    font-size:2rem;
    line-height:1.1;
    text-align:center;
}
.pa-media-frame.pa-media-perfumeria .pf-logo-rule{
    width:70px;
    height:1px;
    background:currentColor;
    opacity:.6;
}
@media (prefers-reduced-motion: reduce){
    .pa-media-frame{transform:none;animation:none;}
}
/* La tarjeta entera abre el proyecto (ver portfolio-animado.js): el
   cursor y el foco de teclado son la única señal visual que hace
   falta, el brillo/tilt que ya tiene de por sí funciona como affordance. */
.pa-media-frame--clickable{cursor:pointer;}
.pa-media-frame--clickable:focus-visible{
    outline:2px solid var(--accent-hot);
    outline-offset:6px;
}

/* --- Lado de contenido --- */
.pa-content{position:relative;}
.pa-category{
    color:var(--accent-hot);
    font-family:var(--font-mono);
    font-size:.7rem;
    font-weight:600;
    text-transform:uppercase;
    letter-spacing:1.2px;
    margin-bottom:10px;
}
.pa-content h2{
    font-size:clamp(1.5rem,4.6vw,2.15rem);
    margin-bottom:14px;
}
.pa-content p{
    font-size:.95rem;
    margin-bottom:18px;
    max-width:52ch;
}
.pa-tags{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:22px;}
.pa-actions{display:flex;gap:12px;flex-wrap:wrap;}
.pa-actions .btn{width:auto;}

/* Pantallas bajas (un celular con la barra del navegador a la vista, o una
   ventana corta). Ahora que el panel mide EXACTAMENTE el alto de pantalla
   —condición para que el snap sea preciso, ver el bloque 1— el proyecto de
   descripción más larga no entraba por unos 40px y se le recortaba justo el
   botón de abajo. Se aprieta solo el ritmo vertical, sin tocar tamaños de
   fuente ni el layout, así que en pantallas normales no cambia nada. */
@media (max-height:800px){
    .pa-panel-inner{gap:20px;}
    .pa-content h2{margin-bottom:10px;}
    .pa-content p{line-height:1.6;margin-bottom:14px;}
    .pa-tags{margin-bottom:16px;}
}

/* Cada mitad del panel entra con fade + desplazamiento + un leve "grow"
   (scale .95→1); se activa por JS (clase .is-active en el panel), no por
   scroll-linked scrubbing, para que el timing sea siempre el mismo sin
   importar la velocidad del gesto de scroll del usuario. La curva
   cubic-bezier(.16,1,.3,1) frena de golpe al final (ease-out-expo): da
   la sensación "premium" de que cada elemento cae en su lugar con peso
   real, en vez de deslizar parejo hasta pararse. */
.pa-media,
.pa-content > *{
    opacity:0;
    transform:translateY(38px) scale(.95);
    transition:opacity .85s cubic-bezier(.16,1,.3,1), transform .85s cubic-bezier(.16,1,.3,1);
}
.pa-media{transition-delay:.05s;}
.pa-content > *:nth-child(1){transition-delay:.1s;}
.pa-content > *:nth-child(2){transition-delay:.18s;}
.pa-content > *:nth-child(3){transition-delay:.26s;}
.pa-content > *:nth-child(4){transition-delay:.34s;}
.pa-content > *:nth-child(5){transition-delay:.42s;}
.pa-panel.is-active .pa-media,
.pa-panel.is-active .pa-content > *{
    opacity:1;
    transform:none;
}
/* El bloque de tags entra entero con el resto de .pa-content de arriba,
   pero además cada tag "aparece" por separado con un pequeño rebote
   (overshoot en la curva): en vez de un fade parejo, se sienten como
   piezas que van encastrando una a una — el tipo de detalle que separa
   una landing genérica de una con dirección de arte real. */
.pa-content > .pa-tags{opacity:1;transform:none;transition:none;}
.pa-tags .pf-tag{
    opacity:0;
    transform:translateY(10px) scale(.85);
    transition:opacity .5s cubic-bezier(.34,1.56,.64,1), transform .5s cubic-bezier(.34,1.56,.64,1);
}
.pa-panel.is-active .pa-tags .pf-tag{opacity:1;transform:none;}
.pa-tags .pf-tag:nth-child(1){transition-delay:.5s;}
.pa-tags .pf-tag:nth-child(2){transition-delay:.56s;}
.pa-tags .pf-tag:nth-child(3){transition-delay:.62s;}
.pa-tags .pf-tag:nth-child(4){transition-delay:.68s;}
.pa-tags .pf-tag:nth-child(5){transition-delay:.74s;}
/* Efecto "materializándose": en vez de deslizar desde abajo, el texto
   entra desenfocado y translúcido y se va enfocando/asentando en su
   lugar, lento y gradual (a diferencia del resto de los paneles, que
   entran con un fade + desplazamiento corto). */
.pa-intro-inner > *{
    opacity:0;
    filter:blur(16px);
    transform:scale(1.035);
    transition:opacity 1.7s cubic-bezier(.19,1,.22,1),
                filter 1.7s cubic-bezier(.19,1,.22,1),
                transform 1.7s cubic-bezier(.19,1,.22,1);
}
.pa-intro-inner > *:nth-child(1){transition-delay:.1s;}
.pa-intro-inner > *:nth-child(2){transition-delay:.4s;}
.pa-panel.is-active .pa-intro-inner > *{opacity:1;filter:blur(0);transform:none;}

/* =====================================================================
   4. PANEL OUTRO / CTA FINAL
   ===================================================================== */
.pa-outro{
    text-align:center;
    justify-content:center;
}
.pa-outro-inner{
    max-width:600px;
    margin:0 auto;
    padding:0 var(--container-pad);
    position:relative;
    z-index:2;
}
.pa-outro h2{font-size:clamp(1.8rem,5.6vw,2.6rem);margin-bottom:16px;}
.pa-outro p{margin-bottom:28px;}
.pa-outro-actions{display:flex;gap:14px;justify-content:center;flex-wrap:wrap;}
.pa-outro-actions .btn{width:auto;}
.pa-outro-inner > *{
    opacity:0;
    transform:translateY(24px);
    transition:opacity .7s cubic-bezier(.16,1,.3,1), transform .7s cubic-bezier(.16,1,.3,1);
}
.pa-outro-inner > *:nth-child(1){transition-delay:.05s;}
.pa-outro-inner > *:nth-child(2){transition-delay:.12s;}
.pa-outro-inner > *:nth-child(3){transition-delay:.19s;}
.pa-panel.is-active .pa-outro-inner > *{opacity:1;transform:none;}

/* =====================================================================
   5. FILTROS POR CATEGORÍA
   Mismas categorías y misma clase ".filter-btn" que #portfolio en
   index.html (ver styles.css): look consistente, sin inventar un
   componente nuevo. En desktop es una barra fija centrada arriba; en
   mobile (ver breakpoint más abajo) el mismo elemento se convierte en un
   dropdown que despliega ".pa-filter-fab" — con el header fijo + el
   contador + los dots ya ocupando pantalla, otra barra fija angosta ahí
   rompía la estética prolija del recorrido.
   ===================================================================== */
.pa-filters{
    display:flex;
    position:fixed;
    top:94px;
    left:50%;
    transform:translateX(-50%);
    z-index:1150;
    gap:8px;
    flex-wrap:wrap;
    justify-content:center;
    /* "width" (no "max-width"): con flex-wrap, un ancho sin comprometer
       hace que el navegador intente minimizar el área total y envuelva
       antes de lo necesario. Fijar el ancho real evita ese salto a dos
       líneas prematuro y deja que los filtros usen todo el espacio
       disponible antes de partir a la siguiente línea. */
    width:min(840px, calc(100vw - 48px));
    padding:10px 12px;
    background:rgba(10,10,12,0.65);
    border:1px solid var(--border-soft);
    border-radius:var(--radius-lg);
    backdrop-filter:blur(12px);
    -webkit-backdrop-filter:blur(12px);
}
/* Botón redondo que despliega el dropdown de filtros — mismo lenguaje
   visual que las flechitas del contador (".pa-counter-arrow": círculo,
   fondo oscuro con blur, borde sutil, se pinta de acento al interactuar)
   para que se sienta parte de la misma familia de controles flotantes. */
.pa-filter-fab{
    display:none; /* aparece solo en mobile, ver breakpoint */
    position:fixed;
    /* Debajo del header (68px de alto), no encima ni pisándolo: el header
       manda arriba de todo y este botón vive en el espacio que queda
       debajo, pegado a la esquina superior derecha de ese resto de
       pantalla — por eso alcanza con el z-index normal de los controles
       flotantes (1150), sin necesidad de superar al header (1300). */
    top:82px;
    right:16px;
    z-index:1150;
    width:40px;height:40px;
    align-items:center;
    justify-content:center;
    border-radius:50%;
    background:rgba(10,10,12,0.6);
    border:1px solid var(--border-soft);
    color:var(--gray-soft);
    cursor:pointer;
    backdrop-filter:blur(10px);
    -webkit-backdrop-filter:blur(10px);
    transition:background .25s ease, border-color .25s ease, color .25s ease, transform .25s ease;
}
.pa-filter-fab svg{width:16px;height:16px;}
.pa-filter-fab:hover,
.pa-filter-fab.is-open{
    background:var(--accent);
    border-color:var(--accent);
    color:#fff;
}
/* Un leve "tick" de escala al abrir/cerrar: mismo tipo de feedback táctil
   que ya usan los botones del sitio, no una animación nueva inventada. */
.pa-filter-fab:active{transform:scale(.92);}

/* =====================================================================
   6. NAVEGACIÓN LATERAL (dots) + CONTADOR
   ===================================================================== */
.pa-dots{
    position:fixed;
    right:18px;
    top:50%;
    transform:translateY(-50%);
    z-index:1150;
    display:none; /* aparece en tablet+ (ver breakpoints) */
    flex-direction:column;
    gap:10px;
}
.pa-dot{
    width:8px;height:8px;
    border-radius:50%;
    background:var(--border-soft-hover);
    border:none;
    padding:0;
    cursor:pointer;
    transition:.3s ease;
}
.pa-dot:hover{background:var(--accent-hot);}
.pa-dot.is-active{
    background:var(--accent);
    width:9px;height:22px;
    border-radius:5px;
    box-shadow:0 0 0 4px var(--accent-glow);
}

.pa-counter{
    position:fixed;
    /* Centrado en mobile: pegado a la izquierda quedaba muy cerca del
       botón "Ver demo/Ver sitio en vivo" de .pa-actions y en pantallas
       angostas llegaban a superponerse. Desde tablet (768px+, ver
       breakpoints) vuelve a la esquina inferior izquierda, que es donde
       hay espacio de sobra al lado del contenido. */
    left:50%;
    bottom:18px;
    transform:translateX(-50%);
    z-index:1150;
    font-family:var(--font-mono);
    font-size:.82rem;
    color:var(--gray-mid);
    letter-spacing:1px;
    display:flex;
    align-items:center;
    gap:10px;
    background:rgba(10,10,12,0.6);
    border:1px solid var(--border-soft);
    border-radius:50px;
    padding:8px 10px 8px 14px;
    backdrop-filter:blur(10px);
    /* Oculto en el intro (index 0, ver setActive() en portfolio-animado.js):
       ahí abajo ya está el .pa-scroll-cue del hero, y mostrar los dos juntos
       hace que se pisen — el paginador solo tiene sentido una vez que hay
       un proyecto activo para contar. */
    opacity:0;
    visibility:hidden;
    pointer-events:none;
    transition:opacity .25s ease, visibility .25s;
}
.pa-counter.show{
    opacity:1;
    visibility:visible;
    pointer-events:auto;
}
.pa-counter strong{color:var(--white);font-weight:700;}
.pa-counter-nums{display:flex;align-items:center;gap:6px;}
.pa-counter-arrow{
    width:26px;height:26px;
    border-radius:50%;
    border:1px solid var(--border-soft);
    background:transparent;
    color:var(--gray-soft);
    display:flex;
    align-items:center;
    justify-content:center;
    cursor:pointer;
    transition:.25s ease;
    flex-shrink:0;
}
.pa-counter-arrow svg{width:13px;height:13px;}
.pa-counter-arrow:hover{background:var(--accent);border-color:var(--accent);color:#fff;}
.pa-counter-arrow:disabled{opacity:.3;cursor:default;background:transparent;border-color:var(--border-soft);color:var(--gray-soft);}

/* Volver al inicio del recorrido — mismo lenguaje visual que el
   contador/paginador de acá arriba (círculo, fondo oscuro con blur,
   borde sutil). Se muestra/oculta por JS (clase ".show") según el panel
   activo, no por scroll-position: con la navegación por paneles el
   scrollY salta de a un tramo entero, así que "current === 0" (el
   intro) es la señal exacta, no un umbral de píxeles aproximado. */
.pa-to-top{
    position:fixed;
    left:auto;
    right:16px;
    bottom:18px;
    z-index:1150;
    width:42px;height:42px;
    border-radius:50%;
    display:flex;
    align-items:center;
    justify-content:center;
    background:rgba(10,10,12,0.6);
    border:1px solid var(--border-soft);
    color:var(--gray-soft);
    cursor:pointer;
    backdrop-filter:blur(10px);
    -webkit-backdrop-filter:blur(10px);
    opacity:0;
    visibility:hidden;
    pointer-events:none;
    transform:translateY(10px);
    transition:opacity .3s ease, transform .3s ease, visibility .3s,
                background .25s ease, border-color .25s ease, color .25s ease;
}
.pa-to-top svg{width:17px;height:17px;}
.pa-to-top.show{
    opacity:1;
    visibility:visible;
    pointer-events:auto;
    transform:none;
}
.pa-to-top:hover{background:var(--accent);border-color:var(--accent);color:#fff;}
@media (prefers-reduced-motion: reduce){
    .pa-to-top{transition:opacity .2s ease, visibility .2s;}
    .pa-to-top.show{transform:none;}
}
/* Desktop (mismo punto donde el contador vuelve a su esquina, ver
   breakpoint 768px más abajo): queda pegado a la izquierda, justo
   arriba del paginador (left:28px;bottom:28px + su alto ~42px + aire) —
   "por encima del paginador" tal cual se pidió, no flotando suelto. En
   mobile el paginador está centrado abajo (ver .pa-counter), así que acá
   se deja en la esquina inferior derecha para no competir con él. */
@media (min-width:768px){
    .pa-to-top{left:28px;right:auto;bottom:84px;}
}

/* =====================================================================
   7. INDICADOR DE SCROLL (panel intro)
   ===================================================================== */
.pa-scroll-cue{
    position:absolute;
    left:50%;
    /* Un poco más arriba que antes: en mobile este indicador vivía en la
       misma franja que el paginador (.pa-counter) y quedaba tapado por él.
       El paginador ahora arranca oculto en el intro (ver setActive() en
       portfolio-animado.js), pero se sube igual para que el indicador
       tenga aire propio sin depender de eso. */
    bottom:52px;
    transform:translateX(-50%);
    display:flex;
    flex-direction:column;
    align-items:center;
    gap:10px;
    color:var(--gray-mid);
    font-family:var(--font-mono);
    font-size:.9rem;
    font-weight:600;
    letter-spacing:2px;
    text-transform:uppercase;
    z-index:2;
}
.pa-scroll-cue svg{
    width:22px;height:22px;
    animation:paScrollBounce 1.8s ease-in-out infinite;
}
@keyframes paScrollBounce{
    0%,100%{transform:translateY(0);opacity:.5;}
    50%{transform:translateY(6px);opacity:1;}
}

@media (prefers-reduced-motion: reduce){
    .pa-media, .pa-content > *, .pa-intro-inner > *, .pa-outro-inner > *, .pa-tags .pf-tag{
        transition:opacity .3s ease;
        transform:none;
        filter:none;
    }
    .pa-scroll-cue svg{animation:none;}
    .pa-intro .eyebrow::before{animation:none;}
    .pa-filters{transition:opacity .15s ease;}
}

/* =====================================================================
   8. BREAKPOINTS
   ===================================================================== */
/* Única excepción al mobile-first del archivo (igual que el
   @media(pointer:fine) de la sección 1): achica específicamente la
   franja angosta de teléfono sin tocar el breakpoint de 680px+, que ya
   está bien. Motivo del ajuste: la tarjeta quedaba grande y muy pegada
   al texto de abajo, y el halo de luz llegaba a "pisarlo" visualmente. */
@media (max-width:679px){
    .pa-panel-inner{gap:44px;}
    .pa-media-frame{
        /* Un poco más chica que antes (260px): en teléfonos angostos
           ocupaba demasiada pantalla y dejaba poco lugar para el texto
           de abajo sin scrollear dentro del propio panel. */
        max-width:206px;
        padding:12%; /* logo más grande dentro de la tarjeta */
        --glow-blur-a:12px;
        --glow-spread-a:-8px;
        --glow-blur-b:24px;
        --glow-spread-b:-15px;
    }
}
@media (min-width:680px){
    .pa-panel-inner{
        grid-template-columns:1fr 1fr;
        gap:60px;
    }
    /* Paneles pares invierten el orden: alterna qué lado ocupa la imagen
       para variar el ritmo visual proyecto a proyecto */
    .pa-panel--reverse .pa-media{order:2;}
    .pa-panel--reverse .pa-content{order:1;}
    .pa-media-frame{max-width:320px;}
}
/* Mismo límite que el breakpoint de abajo (min-width:768px) donde la
   barra de filtros de desktop aparece: por debajo de eso, el botón
   redondo la reemplaza por un dropdown. Nada de "display:none" acá — el
   dropdown necesita animar entrada/salida, así que se oculta con
   opacity+visibility (si no, no habría nada que transicionar). */
@media (max-width:767px){
    .pa-filter-fab{display:flex;}
    .pa-filters{
        top:130px; /* justo debajo del botón redondo (top:82px + 40px + aire) */
        left:auto;
        right:16px;
        transform-origin:top right;
        flex-direction:column;
        align-items:stretch;
        flex-wrap:nowrap;
        width:198px;
        padding:8px;
        opacity:0;
        visibility:hidden;
        transform:scale(.92) translateY(-8px);
        transition:opacity .22s cubic-bezier(.16,1,.3,1),
                    transform .22s cubic-bezier(.16,1,.3,1),
                    visibility .22s;
    }
    .pa-filters.pa-filters-open{
        opacity:1;
        visibility:visible;
        transform:none;
    }
    .pa-filters .filter-btn{width:100%;text-align:left;}
}
@media (min-width:768px){
    .pa-filters{display:flex;}
    .pa-dots{display:flex;}
    .pa-counter{left:28px;bottom:28px;transform:none;}
    .pa-media-frame{max-width:420px;}
}
@media (min-width:1024px){
    .pa-panel{padding:calc(68px + 50px) 0 60px;}
    .pa-content p{font-size:1rem;}
}
