/* =====================================================================
   ÍNDICE DEL ARCHIVO (para navegar rápido con Ctrl+F)
   1.  Variables globales (design tokens)
   2.  Reset & base (mobile-first)
   3.  Fondo animado global (dot-grid + blobs) — movimiento sutil
   4.  Utilidades / tipografía / botones
   5.  Navegación
   6.  Hero
   7.  Servicios
   8.  Portfolio
   9.  Por qué elegirme
   10. CTA + Formulario
   11. Footer
   12. Luz resplandeciente que sigue al cursor
   13. Animaciones de entrada (reveal)
   14. Botón flotante de WhatsApp
   15. Selector de idioma (Google Translate)
   16. Breakpoints (tablet 768px+ / desktop 1024px+ / xl 1280px+)
   ===================================================================== */

/* =====================================================================
   1. VARIABLES GLOBALES
   ===================================================================== */
:root{
    --bg-black: #0a0a0c;
    --bg-black-2: #050506;
    --bg-card: #121216;
    --border-soft: rgba(255,255,255,0.08);
    --border-soft-hover: rgba(255,255,255,0.16);

    --white: #f7f7f9;
    --gray-soft: rgba(247,247,249,0.65);
    --gray-mid: rgba(247,247,249,0.4);

    /* Fuente mono de acento: refuerza el perfil "developer" en etiquetas,
       numeros de servicio/metricas y tags de portfolio (ver indice item 4) */
    --font-mono: 'JetBrains Mono', 'SFMono-Regular', Consolas, monospace;

    /* Azul como color de acento principal (reemplaza el coral) */
    --accent: #2f6fed;
    --accent-hot: #5b8dff;
    --accent-dark: #13224f;
    --accent-glow: rgba(47,111,237,0.35);

    /* Segundo tono, usado en el canvas de ondas para dar profundidad */
    --wave-teal: #1fc9c3;

    --radius-lg: 20px;
    --radius-md: 14px;
    --radius-sm: 8px;

    --container: 1180px;

    /* Paddings fluidos: chicos en mobile, crecen en desktop */
    --section-pad: 78px;
    --container-pad: 20px;

    /* Amplitud del recorrido de los blobs "bailando" (ver sección 3):
       un solo lugar para ajustar qué tanto se mueven en todos lados
       donde se usan. clamp() evita que en pantallas muy anchas el
       recorrido en vw se dispare y termine sacando el blob 100% del
       área visible; el piso en px evita que en mobile el movimiento
       se sienta demasiado chico. */
    --blob-dance-x: clamp(120px, 22vw, 300px);
    --blob-dance-y: clamp(80px, 16vh, 190px);
}

/* =====================================================================
   2. RESET & BASE (mobile-first)
   ===================================================================== */
*{margin:0;padding:0;box-sizing:border-box;}
html{scroll-behavior:smooth;-webkit-text-size-adjust:100%;overflow-x:hidden;width:100%;}
body{
    font-family:'Manrope', -apple-system, BlinkMacSystemFont, sans-serif;
    background:var(--bg-black);
    color:var(--white);
    overflow-x:hidden;      /* evita el scroll horizontal que rompía el mobile */
    position:relative;
    width:100%;
    min-height:100vh;
}

img{max-width:100%;display:block;}
a{text-decoration:none;color:inherit;transition:.3s ease;}
ul{list-style:none;}
button{font-family:inherit;}

h1,h2,h3,h4{
    font-weight:800;
    line-height:1.18;
    letter-spacing:-0.02em;
}
p{color:var(--gray-soft);line-height:1.75;}

/* Contenedor: ancho fluido con padding lateral consistente */
.container{
    width:100%;
    max-width:var(--container);
    margin:0 auto;
    padding:0 var(--container-pad);
}

.section-pad{padding:var(--section-pad) 0;}

.text-accent{color:var(--accent-hot);}

/* =====================================================================
   3. FONDO ANIMADO GLOBAL — movimiento sutil a lo largo de toda la web
   ===================================================================== */

/* Capa fija de puntos que se desplaza muy lentamente (todo el documento) */
.global-bg{
    position:fixed;
    inset:0;
    z-index:0;
    pointer-events:none;
    opacity:.5;
    background-image:radial-gradient(rgba(255,255,255,0.05) 1px, transparent 1px);
    background-size:26px 26px;
    animation:driftGrid 40s linear infinite;
}
@keyframes driftGrid{
    0%{background-position:0 0;}
    100%{background-position:130px 130px;}
}

/* Blobs de color flotantes, reutilizables en cualquier sección */
.blob{
    position:absolute;
    border-radius:50%;
    filter:blur(70px);
    pointer-events:none;
    z-index:0;
    will-change:transform;
}
.blob-blue{background:var(--accent);opacity:.22;}
.blob-teal{background:var(--wave-teal);opacity:.16;}

/* Único movimiento de blobs del sitio: recorre un área mucho más amplia del fondo que
   floatSlow/floatSlowReverse (que solo se mueven unos px en el lugar).
   Se anima "transform" en vez de "top/left" a propósito: el navegador
   puede correrla en el compositor (GPU), sin recalcular layout en cada
   frame — importante porque estos blobs ya son costosos de pintar por
   el blur. Reutilizable en cualquier sección que quiera este efecto.
   El recorrido oscila para los DOS lados (no solo se aleja en una
   dirección y vuelve): los blobs suelen posicionarse ya pegados a un
   borde (offsets negativos, ver los "style" inline de cada uso), así
   que un desplazamiento que solo sigue alejándose terminaba sacando el
   blob 100% del área visible en algún punto del loop. Oscilando para
   ambos lados, siempre queda al menos una porción visible. La amplitud
   sale de --blob-dance-x/y (ver sección 1, con clamp() para que no se
   dispare en pantallas anchas): un solo lugar para ajustar qué tanto
   "sale" del diseño en todos lados donde se usa este efecto. */
@keyframes blobDanceA{
    0%{transform:translate(0,0);}
    25%{transform:translate(calc(var(--blob-dance-x) * -0.7), calc(var(--blob-dance-y) * 0.85));}
    50%{transform:translate(var(--blob-dance-x), calc(var(--blob-dance-y) * -0.8));}
    75%{transform:translate(calc(var(--blob-dance-x) * -0.55), var(--blob-dance-y));}
    100%{transform:translate(0,0);}
}
@keyframes blobDanceB{
    0%{transform:translate(0,0);}
    25%{transform:translate(calc(var(--blob-dance-x) * 0.7), calc(var(--blob-dance-y) * -0.85));}
    50%{transform:translate(calc(var(--blob-dance-x) * -1), calc(var(--blob-dance-y) * 0.8));}
    75%{transform:translate(calc(var(--blob-dance-x) * 0.55), calc(var(--blob-dance-y) * -1));}
    100%{transform:translate(0,0);}
}
.blob-dance-a{animation:blobDanceA 34s ease-in-out infinite;}
.blob-dance-b{animation:blobDanceB 40s ease-in-out infinite;}

/* Asegura que el contenido de cada sección quede sobre los blobs */
/* IMPORTANTE — bug de taps fantasma en mobile (filtros de portfolio, etc.):
   los blobs decorativos se posicionan a propósito con offsets negativos
   (ej. right:-100px) para que "sangren" fuera del viewport, y agrandan el
   scrollWidth de la sección más allá de su clientWidth. Con overflow-x:hidden
   la sección sigue siendo técnicamente un contenedor scrolleable (solo se
   bloquea el scroll por gesto/barra, no que sea "scrollable"): en iOS/Android,
   si el dedo se mueve unos px en horizontal al tocar un botón, el navegador
   puede interpretarlo como un intento de pan horizontal y cancela el click.
   overflow-x:clip elimina el scrollport por completo (inmune a esto) y se
   declara DESPUÉS como mejora progresiva: los navegadores que no soportan
   "clip" ignoran la línea y se quedan con "hidden" de fallback. touch-action
   refuerza lo mismo a nivel de gestos táctiles. */
section{position:relative;isolation:isolate;overflow-x:hidden;overflow-x:clip;touch-action:pan-y;}
section > .container{position:relative;z-index:2;}

/* Grano cinematográfico muy sutil sobre toda la web: aporta textura y
   profundidad al fondo oscuro sin afectar contraste ni legibilidad.
   z-index:2 lo ubica por encima del contenido de las secciones (que
   viven en su propio stacking context vía isolation:isolate) pero por
   debajo del header (z-index:1300) y de los widgets flotantes. */
.grain-overlay{
    position:fixed;
    inset:0;
    z-index:2;
    pointer-events:none;
    opacity:.035;
    mix-blend-mode:overlay;
    background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.5 0'/%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23n)'/%3E%3C/svg%3E");
    background-size:180px 180px;
}

/* =====================================================================
   4. TIPOGRAFÍA / EYEBROW / BOTONES
   ===================================================================== */
.eyebrow{
    display:inline-flex;
    align-items:center;
    gap:8px;
    color:var(--accent-hot);
    font-family:var(--font-mono);
    font-weight:600;
    font-size:.76rem;
    text-transform:uppercase;
    letter-spacing:1.5px;
    margin-bottom:14px;
}
.eyebrow::before{
    content:'';
    width:8px;height:8px;
    border-radius:50%;
    background:var(--accent);
    box-shadow:0 0 0 4px var(--accent-glow);
}

.section-header{max-width:640px;margin-bottom:46px;}
.section-header.center{margin-left:auto;margin-right:auto;text-align:center;}
.section-header h2{font-size:clamp(1.7rem,6vw,2.85rem);margin-bottom:16px;position:relative;display:inline-block;}
.section-header p{font-size:1rem;}

/* Trazo de acento bajo el titulo de cada seccion: un detalle editorial que
   ancla visualmente el heading sin agregar otro elemento al layout */
.section-header h2::after{
    content:'';
    display:block;
    width:46px;
    height:3px;
    margin-top:14px;
    border-radius:3px;
    background:linear-gradient(90deg,var(--accent),var(--wave-teal));
}

.btn{
    display:inline-flex;
    align-items:center;
    justify-content:center;
    gap:10px;
    width:100%;
    padding:16px 28px;
    border-radius:50px;
    font-weight:700;
    font-size:.92rem;
    cursor:pointer;
    border:1px solid transparent;
    white-space:nowrap;
    transition:.35s cubic-bezier(.4,0,.2,1);
}
/* Los <button class="btn"> (p.ej. "Agendar reunión", que dispara el popup de
   Cal.com) deben verse idénticos en tamaño a los <a class="btn">: sin este
   reset, el estilo nativo del navegador para <button> (appearance + el
   borde interno oculto de Firefox) los renderiza levemente más grandes que
   un link con el mismo padding, aunque compartan exactamente las mismas
   reglas de arriba. */
button.btn{appearance:none;-webkit-appearance:none;-moz-appearance:none;}
button.btn::-moz-focus-inner{border:0;padding:0;}
.btn-primary{
    background:linear-gradient(135deg,var(--accent) 0%,var(--accent-hot) 100%);
    color:#fff;
    box-shadow:0 10px 28px -8px var(--accent-glow);
}
.btn-primary:hover{
    transform:translateY(-2px);
    box-shadow:0 16px 34px -6px rgba(47,111,237,.55);
}
.btn-outline{
    background:transparent;
    color:var(--white);
    border-color:var(--border-soft);
}
.btn-outline:hover{
    border-color:var(--accent-hot);
    color:var(--accent-hot);
}
.btn-sm{width:auto;padding:11px 22px;font-size:.82rem;}

/* =====================================================================
   5. NAVEGACIÓN (mobile-first: menú full-width desplegable)
   ===================================================================== */
header{
    position:fixed;
    top:0;left:0;right:0;
    z-index:1300;
    border-bottom:1px solid var(--border-soft);
    transition:border-color .3s ease, box-shadow .3s ease;
}
/* Al bajar el scroll, el header suma sombra y refuerza el borde: distingue
   visualmente "flotando sobre el contenido" de su estado inicial en el hero */
header.scrolled{
    border-bottom-color:var(--border-soft-hover);
    box-shadow:0 12px 30px -18px rgba(0,0,0,.6);
}
/* El blur/fondo va en un pseudo-elemento, no en el header directamente.
   Motivo: `backdrop-filter` en un elemento lo convierte en el "contenedor de
   referencia" de sus hijos con position:fixed (spec CSS). Como el menú mobile
   (.nav-links) es fixed y vive dentro del header, si el header tuviera el
   backdrop-filter, el menú se posicionaría relativo a la altura del header
   (~68px) en vez de a toda la pantalla, quedando cortado/aplastado. */
header::before{
    content:'';
    position:absolute;
    inset:0;
    z-index:-1;
    background:rgba(10,10,12,0.85);
    backdrop-filter:blur(14px);
}
.nav-wrap{
    position:relative;
    display:flex;
    align-items:center;
    justify-content:space-between;
    height:68px;
}
.logo{
    display:flex;
    align-items:center;
    gap:9px;
    font-size:1.1rem;
    font-weight:700;
    color:var(--white);
    margin-left:6px;
}
.logo-icon{
    height:38px;
    width:auto;
    flex-shrink:0;
    display:block;
}

.hamburger{
    display:flex;
    flex-direction:column;
    gap:5px;
    cursor:pointer;
    padding:8px;
    margin:-8px;
    z-index:1100;
}
.hamburger span{
    width:22px;height:2px;
    background:var(--white);
    border-radius:2px;
    transition:.3s ease;
}
.hamburger.active span:nth-child(1){transform:rotate(45deg) translate(5px,5px);}
.hamburger.active span:nth-child(2){opacity:0;}
.hamburger.active span:nth-child(3){transform:rotate(-45deg) translate(5px,-5px);}

.nav-links{
    position:fixed;
    top:68px;left:0;right:0;
    bottom:0;
    background:rgba(8,8,10,0.98);
    backdrop-filter:blur(16px);
    display:flex;
    flex-direction:column;
    align-items:stretch;
    gap:0;
    padding:12px 20px 32px;
    overflow-y:auto;
    /* Estado cerrado: invisible, ligeramente desplazado hacia arriba,
       y sin interceptar clics (pointer-events:none) */
    opacity:0;
    visibility:hidden;
    transform:translateY(-14px);
    pointer-events:none;
    transition:opacity .32s ease, transform .32s cubic-bezier(.4,0,.2,1), visibility 0s linear .32s;
}
.nav-links.active{
    opacity:1;
    visibility:visible;
    transform:translateY(0);
    pointer-events:auto;
    transition:opacity .32s ease, transform .32s cubic-bezier(.4,0,.2,1), visibility 0s linear 0s;
}
.nav-links a:not(.btn){
    font-weight:600;
    font-size:1.05rem;
    color:var(--gray-soft);
    padding:16px 4px;
    border-bottom:1px solid var(--border-soft);
}
.nav-links a.active,
.nav-links a:hover{color:var(--white);}
.nav-links a.active{color:var(--accent-hot);}
.nav-links .btn{margin-top:20px;}
.nav-links .btn + .nav-link-shine{margin-top:20px;}
.nav-links .btn-primary{
    background:linear-gradient(135deg,#173d99 0%,var(--accent) 100%);
    box-shadow:0 10px 28px -8px rgba(23,61,153,.45);
}
.nav-links .btn-primary:hover{box-shadow:0 16px 34px -6px rgba(23,61,153,.6);}

/* Link "Portfolio Animado": brillo sutil tipo "shine" en el texto, más
   un subrayado animado debajo (mismo recurso que
   ".porque .section-header h2::after", @keyframes porqueLineSweep, acá
   en versión fina "de navbar"). Ambos efectos se aplican al <span>
   interno (no al <a>) para que el subrayado quede del ancho del TEXTO:
   en el menú mobile el <a> se estira a todo el ancho del panel (ver
   ".nav-links{align-items:stretch}"), y un <span> siempre se achica a
   su contenido sin importar el ancho del link que lo contiene. */
.nav-links a.nav-link-shine span{position:relative;display:inline-block;}
@supports ((background-clip:text) or (-webkit-background-clip:text)){
    .nav-links a.nav-link-shine span{
        background-image:linear-gradient(100deg,
            var(--gray-soft) 0%, var(--gray-soft) 40%,
            #fff 48%, var(--accent-hot) 52%, #fff 56%,
            var(--gray-soft) 64%, var(--gray-soft) 100%);
        background-size:220% 100%;
        background-position:200% 0;
        -webkit-background-clip:text;
        background-clip:text;
        color:transparent;
        animation:navShine 6s ease-in-out infinite;
    }
    .nav-links a.nav-link-shine.active span{
        background-image:linear-gradient(100deg,
            var(--accent-hot) 0%, var(--accent-hot) 40%,
            #fff 48%, var(--accent-hot) 52%, #fff 56%,
            var(--accent-hot) 64%, var(--accent-hot) 100%);
    }
}
@keyframes navShine{
    0%,15%{background-position:200% 0;}
    55%,100%{background-position:-120% 0;}
}

.nav-links a.nav-link-shine span::after{
    content:'';
    position:absolute;
    left:0;
    right:0;
    bottom:-6px;
    height:2px;
    border-radius:2px;
    background-image:linear-gradient(90deg,
        var(--accent) 0%, var(--accent) 32%,
        var(--wave-teal) 46%, #eafffb 50%, var(--wave-teal) 54%,
        var(--accent) 68%, var(--accent) 100%);
    background-size:300% 100%;
    background-position:200% 0;
    animation:porqueLineSweep 4.2s cubic-bezier(.83,0,.17,1) infinite;
}
@media (prefers-reduced-motion: reduce){
    .nav-link-shine span{animation:none;}
    .nav-link-shine span::after{animation:none;}
}

/* Botón "Ver portfolio" del hero: mismo lenguaje visual que el link
   "Portfolio Animado" del navbar (arriba) — brillo animado en el texto
   (@keyframes navShine) y un barrido de gradiente en el borde
   (@keyframes porqueLineSweep, el mismo del subrayado del navbar y del
   acento de "por qué elegirme"), para que este CTA llame más la atención
   sin introducir un efecto nuevo. El borde animado se logra con un
   pseudo-elemento con "mask" (técnica de "gradient border" animado): el
   gradiente se pinta como fondo completo y el mask recorta todo menos un
   anillo del grosor del padding, dejando ver solo el borde. */
.btn-shine-border{position:relative;}
.btn-shine-border::before{
    content:'';
    position:absolute;
    inset:0;
    border-radius:inherit;
    padding:1px;
    background-image:linear-gradient(90deg,
        var(--accent) 0%, var(--accent) 32%,
        var(--wave-teal) 46%, #eafffb 50%, var(--wave-teal) 54%,
        var(--accent) 68%, var(--accent) 100%);
    background-size:300% 100%;
    animation:porqueLineSweep 4.2s cubic-bezier(.83,0,.17,1) infinite;
    -webkit-mask:linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    -webkit-mask-composite:xor;
    mask-composite:exclude;
    pointer-events:none;
}
.shine-text{position:relative;display:inline-block;}
@supports ((background-clip:text) or (-webkit-background-clip:text)){
    .shine-text{
        background-image:linear-gradient(100deg,
            var(--gray-soft) 0%, var(--gray-soft) 40%,
            #fff 48%, var(--accent-hot) 52%, #fff 56%,
            var(--gray-soft) 64%, var(--gray-soft) 100%);
        background-size:220% 100%;
        background-position:200% 0;
        -webkit-background-clip:text;
        background-clip:text;
        color:transparent;
        animation:navShine 6s ease-in-out infinite;
    }
}
@media (prefers-reduced-motion: reduce){
    .btn-shine-border::before{animation:none;}
    .shine-text{animation:none;}
}

/* =====================================================================
   15. SELECTOR DE IDIOMA (Google Translate)
   Widget real de Google queda oculto (#google_translate_element); este
   bloque es un dropdown propio que solo dispara el cambio de idioma via
   cookie "googtrans" (ver assets/js/lang-switch.js) y oculta el chrome
   por defecto que agrega Google (banner superior, tooltip, iframe).
   ===================================================================== */
.lang-switch{
    /* Mobile: centrado absoluto respecto al header, no al espacio libre
       entre logo y hamburger (que no son del mismo ancho y lo desviarian) */
    position:absolute;
    left:50%;
    top:50%;
    transform:translate(-50%,-50%);
}
.lang-switch-btn{
    display:flex;
    align-items:center;
    gap:5px;
    height:36px;
    padding:0 10px;
    background:transparent;
    border:1px solid var(--border-soft);
    border-radius:8px;
    color:var(--gray-soft);
    font-family:var(--font-mono);
    font-size:.85rem;
    letter-spacing:.03em;
    cursor:pointer;
    transition:border-color .2s ease, color .2s ease, background .2s ease;
}
.lang-switch-btn:hover,
.lang-switch.open .lang-switch-btn{
    border-color:var(--border-soft-hover);
    color:var(--white);
    background:rgba(255,255,255,0.04);
}
.lang-switch-btn svg{transition:transform .2s ease;flex-shrink:0;}
.lang-switch.open .lang-switch-btn svg{transform:rotate(180deg);}

.lang-switch-menu{
    position:absolute;
    top:calc(100% + 8px);
    right:0;
    min-width:150px;
    background:var(--bg-card);
    border:1px solid var(--border-soft);
    border-radius:10px;
    padding:6px;
    box-shadow:0 16px 34px -10px rgba(0,0,0,.55);
    opacity:0;
    visibility:hidden;
    transform:translateY(-6px);
    transition:opacity .18s ease, transform .18s ease, visibility 0s linear .18s;
    z-index:1200;
}
.lang-switch.open .lang-switch-menu{
    opacity:1;
    visibility:visible;
    transform:translateY(0);
    transition:opacity .18s ease, transform .18s ease;
}
.lang-switch-menu li + li{margin-top:2px;}
.lang-switch-menu button{
    display:block;
    width:100%;
    text-align:left;
    padding:8px 10px;
    background:transparent;
    border:none;
    border-radius:6px;
    color:var(--gray-soft);
    font-size:.92rem;
    cursor:pointer;
    transition:background .15s ease, color .15s ease;
}
.lang-switch-menu button:hover{background:rgba(255,255,255,0.06);color:var(--white);}
.lang-switch-menu button.active{color:var(--accent-hot);}

/* Chrome que agrega Google Translate por defecto: se oculta por completo,
   el dropdown propio de arriba es la unica UI visible para el usuario */
.goog-te-banner-frame,
.skiptranslate > iframe{display:none !important;}
body{top:0 !important;}
#google_translate_element{display:none !important;}
.goog-tooltip,
.goog-tooltip:hover{display:none !important;}
.goog-text-highlight{background:none !important;box-shadow:none !important;}

/* =====================================================================
   6. HERO
   ===================================================================== */
.hero{
    min-height:118svh;
    display:flex;
    flex-direction:column;
    justify-content:center;
    text-align:center;
    padding:110px 0 60px;
    overflow:hidden;
}
.hero-content{position:relative;z-index:5;max-width:820px;margin:0 auto;padding:0 var(--container-pad);}

/* Entrada escalonada del hero: cada bloque aparece con un pequeño delay
   respecto al anterior, para que la primera pantalla se sienta "viva"
   apenas carga la pagina en vez de aparecer todo de golpe */
.hero-content > *{
    opacity:0;
    transform:translateY(22px);
    animation:heroFadeUp .8s cubic-bezier(.16,1,.3,1) forwards;
}
.hero-content > *:nth-child(1){animation-delay:.05s;}
.hero-content > *:nth-child(2){animation-delay:.16s;}
.hero-content > *:nth-child(3){animation-delay:.27s;}
.hero-content > *:nth-child(4){animation-delay:.38s;}
.hero-stats{
    opacity:0;
    transform:translateY(22px);
    animation:heroFadeUp .8s cubic-bezier(.16,1,.3,1) forwards;
    animation-delay:.5s;
}
@keyframes heroFadeUp{to{opacity:1;transform:none;}}

.hero-tag{
    display:inline-flex;
    align-items:center;
    gap:9px;
    padding:8px 16px;
    border:1px solid var(--border-soft);
    border-radius:50px;
    font-family:var(--font-mono);
    font-size:.74rem;
    color:var(--gray-soft);
    margin-bottom:24px;
}
.hero-tag .dot{
    width:7px;height:7px;
    border-radius:50%;
    background:var(--accent);
    box-shadow:0 0 0 4px var(--accent-glow);
    animation:pulse 2s infinite;
    flex-shrink:0;
}
@keyframes pulse{
    0%,100%{box-shadow:0 0 0 4px var(--accent-glow);}
    50%{box-shadow:0 0 0 8px rgba(47,111,237,0.12);}
}
.hero h1{
    font-size:clamp(2rem,8vw,4.6rem);
    margin-bottom:20px;
}
.hero-subtitle{
    font-size:1.02rem;
    max-width:600px;
    margin:0 auto 34px;
}
.hero-cta{
    display:flex;
    flex-direction:column;
    gap:14px;
    margin-bottom:60px;
}
.hero-stats{
    position:relative;
    z-index:5;
    display:grid;
    grid-template-columns:repeat(3,1fr);
    gap:16px;
    max-width:640px;
    margin:0 auto;
    padding:32px var(--container-pad) 0;
    border-top:1px solid var(--border-soft);
}
.stat{text-align:center;}
.stat-number{
    font-family:var(--font-mono);
    font-size:1.9rem;
    font-weight:700;
    background:linear-gradient(135deg,var(--white) 0%,var(--accent-hot) 100%);
    -webkit-background-clip:text;
    -webkit-text-fill-color:transparent;
    background-clip:text;
    margin-bottom:4px;
}
.stat-label{font-size:.7rem;color:var(--gray-mid);line-height:1.4;}

.particle-bg{
    position:absolute;
    left:0;right:0;bottom:0;
    width:100%;
    height:280px;
    pointer-events:none;
    z-index:0;
    /* Desvanece el patrón de puntos hacia los bordes (abajo y costados) para
       que no se vea un corte duro contra la sección siguiente */
    -webkit-mask-image:
        linear-gradient(to bottom, black 45%, transparent 100%),
        linear-gradient(to right, transparent 0%, black 12%, black 88%, transparent 100%);
    -webkit-mask-composite: source-in;
    mask-image:
        linear-gradient(to bottom, black 45%, transparent 100%),
        linear-gradient(to right, transparent 0%, black 12%, black 88%, transparent 100%);
    mask-composite: intersect;
}
.particle-bg canvas{width:100%;height:100%;display:block;}

/* =====================================================================
   7. SERVICIOS
   ===================================================================== */
.servicios{background:var(--bg-black);}
.servicios-grid{
    display:grid;
    grid-template-columns:1fr;
    gap:22px;
}
.service-card{
    background:var(--bg-card);
    border:1px solid var(--border-soft);
    border-radius:var(--radius-lg);
    padding:28px;
    transition:.35s ease;
    position:relative;
    overflow:hidden;
}
.service-card::before{
    content:'';
    position:absolute;
    inset:0;
    background:linear-gradient(135deg,rgba(47,111,237,0.08),transparent 60%);
    opacity:0;
    transition:.35s ease;
}
.service-card:hover{
    border-color:rgba(47,111,237,0.4);
    transform:translateY(-4px);
}
.service-card:hover::before{opacity:1;}
.service-num{
    font-family:var(--font-mono);
    font-size:.8rem;
    font-weight:700;
    color:var(--accent-hot);
    margin-bottom:16px;
    letter-spacing:1px;
    position:relative;
}
.service-icon{
    width:50px;height:50px;
    border-radius:var(--radius-sm);
    background:rgba(47,111,237,0.12);
    display:flex;align-items:center;justify-content:center;
    margin-bottom:18px;
    position:relative;
    color:var(--accent-hot);
}
.service-icon svg{width:24px;height:24px;}
.service-card h3{font-size:1.12rem;margin-bottom:10px;position:relative;}
.service-card p{font-size:.9rem;position:relative;}

/* Nota breve sobre identidad de marca, debajo de la grilla de servicios */
.servicios-note{
    position:relative;
    overflow:hidden;
    display:flex;
    align-items:flex-start;
    gap:12px;
    margin-top:32px;
    padding:18px 22px;
    background:rgba(47,111,237,0.06);
    border:1px solid rgba(47,111,237,0.18);
    border-radius:var(--radius-md);
    font-size:.88rem;
    color:var(--gray-soft);
    line-height:1.6;
}
.servicios-note svg{
    width:20px;height:20px;
    flex-shrink:0;
    color:var(--accent-hot);
    margin-top:2px;
}
/* Mismo reflejo/sheen que .portfolio-card::after (misma animación
   @keyframes cardSheen, ver sección 8): se reutiliza acá y en los botones
   "Ver portfolio"/"Ver portfolio animado" para que sea un único lenguaje
   visual reconocible en vez de un efecto por elemento. La animación sólo
   corre mientras el elemento está en pantalla (clase ".sheen-on", la
   agrega/saca un IntersectionObserver en script.js/sobre-mi.js) — así
   corre en cualquier dispositivo, mobile incluido, pero nunca de fondo
   fuera de vista. */
.servicios-note::after,
.btn-outline.btn-shine-border::after,
.btn-glow::after{
    content:'';
    position:absolute;
    inset:0;
    pointer-events:none;
    background:linear-gradient(115deg, transparent 0%, transparent 40%, rgba(255,255,255,.14) 50%, transparent 60%, transparent 100%);
    background-size:300% 300%;
    background-position:0% 0%;
    mix-blend-mode:screen;
}
.btn-outline.btn-shine-border,
.btn-glow{
    position:relative;
    overflow:hidden;
}
.servicios-note.sheen-on::after,
.btn-outline.btn-shine-border.sheen-on::after,
.btn-glow.sheen-on::after{animation:cardSheen 5s ease-in-out infinite alternate;}
@media (prefers-reduced-motion: reduce){
    .servicios-note::after, .servicios-note.sheen-on::after,
    .btn-outline.btn-shine-border::after, .btn-outline.btn-shine-border.sheen-on::after,
    .btn-glow::after, .btn-glow.sheen-on::after{animation:none !important;}
}

/* =====================================================================
   8. PORTFOLIO
   ===================================================================== */
.portfolio{background:var(--bg-black-2);}
.portfolio-header-row{
    display:flex;
    flex-direction:column;
    gap:20px;
    margin-bottom:36px;
}
/* Agrupa el CTA al portfolio animado justo arriba de los filtros de
   categoría, alineados ambos al mismo lado (ver breakpoint 768px+). */
.portfolio-filters-col{
    display:flex;
    flex-direction:column;
    align-items:flex-start;
    gap:14px;
}
/* En mobile el CTA "Ver portfolio animado" queda centrado y con más aire
   abajo para que tenga su propio protagonismo, en vez de mezclarse pegado
   contra los chips de categoría de abajo (ver reset en el breakpoint
   768px+, donde vuelve a alinearse con los filtros como en desktop). */
.portfolio-filters-col .btn-glow{
    align-self:center;
    margin-bottom:10px;
}
.portfolio-filters{
    display:flex;
    gap:8px;
    flex-wrap:wrap;
}
.filter-btn{
    padding:9px 18px;
    border-radius:50px;
    border:1px solid var(--border-soft);
    background:transparent;
    color:var(--gray-soft);
    font-family:'Manrope',sans-serif;
    font-size:.8rem;
    font-weight:600;
    cursor:pointer;
    transition:.3s ease;
}
.filter-btn:hover{border-color:var(--accent-hot);color:var(--white);}
.filter-btn.active{
    background:var(--accent);
    border-color:var(--accent);
    color:#fff;
}

.portfolio-grid{
    display:grid;
    grid-template-columns:1fr;
    gap:24px;
}
.portfolio-card{
    position:relative;
    border-radius:var(--radius-lg);
    overflow:hidden;
    /* Ojo si se agregan más tags o descripciones más largas: como .pf-image
       y .pf-overlay son ambos position:absolute (ver más abajo), NINGÚN
       hijo está en flujo normal, así que esta altura NO crece sola con el
       contenido — hay que mantenerla mayor o igual a la del proyecto con
       más texto/tags (hoy, Chiron Planet con 7 tags) o el contenido se
       corta arriba en vez de desbordar visiblemente. */
    min-height:380px;
    display:flex;
    cursor:pointer;
    background:linear-gradient(160deg,#1a1a20 0%,#0c0c0f 100%);
    border:1px solid var(--border-soft);
    transition:border-color .3s ease, box-shadow .3s ease, transform .5s cubic-bezier(.4,0,.2,1);
}
.portfolio-card:hover{
    border-color:rgba(47,111,237,0.4);
    box-shadow:0 30px 60px -24px rgba(47,111,237,.35);
}
/* Reflejo/sheen: una franja diagonal de luz que recorre la tarjeta en loop
   continuo, como el brillo de una superficie de vidrio. Va en ::after (pinta
   arriba de .pf-image y .pf-overlay porque es el último en el orden del
   documento) con mix-blend-mode:screen para no lavar el texto del overlay,
   y overflow:hidden en .portfolio-card evita que se salga del borde
   redondeado de la tarjeta.
   El tile de fondo es 300% y la franja de luz vive en el 40%-60% central:
   en los extremos del recorrido (0% / 100%) la ventana visible de la card
   cae DENTRO de una zona 100% transparente del tile (ver cuenta en el
   comentario de .pa-media-frame::before, mismo criterio) — así el reflejo
   arranca y termina totalmente invisible, sin el corte diagonal duro que
   se veía antes al quedar la animación "pausada" a mitad de camino.
   La animación en sí solo corre mientras la card está en pantalla: animar
   background-position no lo acelera la GPU como transform/opacity, y con
   15 cards en la grilla más los botones/nota que comparten el efecto,
   tenerlas todas animando a la vez (incluso fuera de vista) pesa en
   repintado/batería sin aportar nada. Un IntersectionObserver en
   script.js agrega/saca la clase ".sheen-on" según cada card entra o sale
   del viewport, así en cualquier momento sólo animan las 1-3 cards que el
   usuario tiene realmente delante — funciona igual en mobile que en
   desktop. */
.portfolio-card::after{
    content:'';
    position:absolute;
    inset:0;
    pointer-events:none;
    background:linear-gradient(115deg, transparent 0%, transparent 40%, rgba(255,255,255,.14) 50%, transparent 60%, transparent 100%);
    background-size:300% 300%;
    background-position:0% 0%;
    mix-blend-mode:screen;
}
@keyframes cardSheen{
    from{background-position:0% 0%;}
    to{background-position:100% 100%;}
}
.portfolio-card.sheen-on::after{animation:cardSheen 5s ease-in-out infinite alternate;}
/* Tilt 3D sutil (ver script.js, solo desktop con mouse fino): --rx/--ry
   son seteadas por JS en cada frame de mousemove sobre la card */
.portfolio-card.has-tilt{
    --rx:0deg;--ry:0deg;
    transform:perspective(900px) rotateX(var(--rx)) rotateY(var(--ry));
}

/* Logo/imagen real del proyecto: centrada, con padding, sin recorte agresivo.
   "position:absolute;inset:0" (igual que .pf-overlay) es clave: si esto fuera
   un hijo en flujo normal del flex de .portfolio-card, la altura REAL del
   logo (según su relación de aspecto propia + el padding-bottom en %% de
   pf-image-img) terminaba empujando la tarjeta mucho más alta que el
   min-height definido, sobre todo en mobile con logos verticales — la
   tarjeta quedaba gigante con el texto ocupando una porción chica. Al
   sacarlo del flujo, la altura de la tarjeta la manda 100% el min-height. */
.portfolio-card .pf-image{
    position:absolute;
    inset:0;
    display:flex;align-items:center;justify-content:center;
    font-size:3.2rem;
    transition:transform .6s cubic-bezier(.4,0,.2,1);
}
.pf-image-img{
    object-fit:contain;
    padding:10% 16% 38%;   /* deja espacio abajo para que no choque con el overlay de texto */
    filter:drop-shadow(0 8px 20px rgba(0,0,0,.4));
}
/* Logos oscuros/monocromáticos que se pierden contra el fondo negro:
   los invertimos a blanco manteniendo la transparencia del PNG original */
.pf-logo-white{
    filter:invert(1) brightness(1.4) drop-shadow(0 8px 20px rgba(0,0,0,.4));
}
/* Logo propio del sitio en la card "Este portfolio": es una marca angosta
   y vertical, necesita menos aire lateral que los logos externos */
.pf-logo-own{
    padding:6% 26% 40%;
    filter:drop-shadow(0 0 20px rgba(47,111,237,.45));
}
/* Logo de OrlandoStudio: en vez de <img>, SVG inline (coincide con como esta
   construido el sitio original) coloreado con su verde de marca */
.pf-logo-svg{
    padding:4% 10% 30%;
    color:#33b875;
    filter:drop-shadow(0 8px 24px rgba(51,184,117,.35));
}
/* Logo de Agencia Luz: sin logo definitivo todavía, se usa el signo
   "</>" (símbolo genérico de programación) en el acento azul del sitio
   como placeholder, hasta que el cliente tenga un logo propio */
.pf-logo-luz{
    padding:14% 24% 40%;
    color:var(--accent-hot);
    filter:drop-shadow(0 8px 24px var(--accent-glow));
}
.pf-logo-code{
    font-family:var(--font-mono);
    font-size:3.6rem;
    font-weight:700;
    letter-spacing:-1px;
}
/* Logo de Perfumería Fina: sin logo definitivo, se arma un wordmark en
   script (misma tipografía que usa el sitio real) en tono dorado */
.pf-logo-perfumeria{
    padding:18% 12% 42%;
    flex-direction:column;
    gap:10px;
    color:#d9b673;
    filter:drop-shadow(0 8px 24px rgba(217,182,115,.3));
}
.pf-logo-perfumeria .pf-logo-script{
    font-family:'Parisienne',cursive;
    font-size:2.2rem;
    line-height:1.1;
    text-align:center;
}
.pf-logo-perfumeria .pf-logo-rule{
    width:64px;
    height:1px;
    background:currentColor;
    opacity:.6;
}
.pf-logo-svg svg{width:100%;height:100%;}
.portfolio-card:hover .pf-image,
.portfolio-card:active .pf-image{transform:scale(1.04);}

.pf-overlay{
    position:absolute;inset:0;
    background:linear-gradient(180deg,transparent 8%,rgba(5,5,6,0.75) 38%,rgba(5,5,6,0.97) 100%);
    display:flex;
    flex-direction:column;
    justify-content:flex-end;
    padding:24px;
    opacity:1; /* siempre visible en mobile (sin hover) */
    transition:.4s ease;
}
.pf-category{
    color:var(--accent-hot);
    font-family:var(--font-mono);
    font-size:.68rem;
    font-weight:600;
    text-transform:uppercase;
    letter-spacing:1px;
    margin-bottom:8px;
}
.pf-overlay h3{font-size:1.1rem;margin-bottom:8px;}
.pf-overlay p{font-size:.85rem;margin-bottom:14px;line-height:1.6;}
.pf-tags{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:16px;}
.pf-tag{
    background:rgba(255,255,255,0.1);
    padding:4px 10px;
    border-radius:20px;
    font-family:var(--font-mono);
    font-size:.66rem;
    color:var(--white);
}

/* Botón "Ver sitio en vivo" dentro de cada card */
.pf-link{
    display:inline-flex;
    align-items:center;
    gap:6px;
    align-self:flex-start;
    font-size:.82rem;
    font-weight:700;
    color:var(--white);
    background:rgba(47,111,237,0.18);
    border:1px solid rgba(47,111,237,0.4);
    padding:9px 16px;
    border-radius:50px;
    transition:.3s ease;
}
.pf-link:hover{
    background:var(--accent);
    border-color:var(--accent);
}

/* Bloque "Mostrar más": reemplaza la paginación con flechas (no funcional,
   siempre marcaba "01/14" fijo) por un control real que refleja cuántas
   cards quedan ocultas según el filtro activo. Se oculta solo (JS) cuando
   ya no hay más cards para mostrar. */
.portfolio-more{
    display:flex;
    flex-direction:column;
    align-items:center;
    gap:22px;
    margin-top:36px;
    padding-top:28px;
    border-top:1px solid var(--border-soft);
}
.portfolio-more-inner{
    display:flex;
    flex-direction:column;
    align-items:center;
    gap:14px;
}
.portfolio-more-count{
    font-size:.82rem;
    color:var(--gray-mid);
    font-family:var(--font-mono);
}
.portfolio-more-inner svg{width:15px;height:15px;transition:transform .3s ease;}
.portfolio-more-inner .btn:hover svg{transform:translateY(2px);}

/* CTA hacia el Portfolio Animado: mismo lenguaje de "anillo pulsante"
   que ya usa el botón de WhatsApp (ver @keyframes wappPulse), para que
   el brillo se sienta sutil y coherente con el resto del sitio en vez
   de un efecto nuevo que compita por atención. */
.btn-glow{
    display:inline-flex;
    align-items:center;
    gap:9px;
    padding:13px 26px;
    border-radius:50px;
    font-weight:700;
    font-size:.88rem;
    color:var(--white);
    background:rgba(47,111,237,0.12);
    border:1px solid rgba(47,111,237,0.4);
    animation:btnGlowPulse 3s ease-in-out infinite;
    transition:background .3s ease, border-color .3s ease, transform .3s ease;
}
.btn-glow svg{width:16px;height:16px;flex-shrink:0;}
.btn-glow:hover{
    background:var(--accent);
    border-color:var(--accent);
    transform:translateY(-2px);
}
/* Variante compacta: junto a los filtros de categoría, del mismo porte
   que los ".filter-btn" en vez del tamaño de CTA grande. */
.btn-glow-sm{padding:9px 20px;font-size:.8rem;gap:7px;}
.btn-glow-sm svg{width:14px;height:14px;}
@keyframes btnGlowPulse{
    0%,100%{box-shadow:0 8px 22px -10px rgba(47,111,237,.5), 0 0 0 0 rgba(47,111,237,.35);}
    50%{box-shadow:0 8px 22px -10px rgba(47,111,237,.5), 0 0 0 9px rgba(47,111,237,0);}
}
@media (prefers-reduced-motion: reduce){
    .btn-glow{animation:none;}
}

/* =====================================================================
   9. POR QUÉ ELEGIRME
   ===================================================================== */
/* overflow-y (no shorthand "overflow"): el eje X sigue gobernado por la
   regla genérica de "section" más arriba (overflow-x:hidden/clip, clave
   para el fix de taps fantasma en mobile) — acá solo se agrega contención
   vertical para que el recorrido amplio del blob-dance no se filtre
   visualmente hacia la sección de abajo. */
.porque{background:var(--bg-black);overflow-y:hidden;}

/* Trazo de acento de esta sección: mucho más largo que el resto (ver
   regla genérica ".section-header h2::after" más arriba) y con una luz
   recorriéndolo de izquierda a derecha en loop. La curva de easing es a
   propósito muy pronunciada (lenta en los extremos, rápida en el tramo
   central) para que se sienta como un barrido premium, no un parpadeo. */
.porque .section-header h2::after{
    width:clamp(140px, 22vw, 320px);
    height:4px;
    margin-top:20px;
    background-image:linear-gradient(90deg,
        var(--accent) 0%, var(--accent) 32%,
        var(--wave-teal) 46%, #eafffb 50%, var(--wave-teal) 54%,
        var(--accent) 68%, var(--accent) 100%);
    background-size:300% 100%;
    background-position:200% 0;
    animation:porqueLineSweep 4.2s cubic-bezier(.83,0,.17,1) infinite;
}
@keyframes porqueLineSweep{
    0%{background-position:200% 0;}
    100%{background-position:-100% 0;}
}
@media (prefers-reduced-motion: reduce){
    .porque .section-header h2::after{animation:none;background-position:50% 0;}
}
.porque-grid-canvas{
    position:absolute;
    inset:0;
    z-index:1;
    pointer-events:none;
}
.porque-grid{
    display:grid;
    grid-template-columns:1fr;
    gap:36px;
}
/* Envoltorio que sólo se ocupa del fade-in (.reveal, ver script.js).
   El tilt 3D vive en el hijo ".porque-visual": ".reveal.show" define su
   propio "transform" con más especificidad (dos clases) que una regla de
   una sola clase, así que si el perspective/rotate estuviera en el MISMO
   elemento que ".reveal", quedaría pisado apenas termina el fade-in y la
   tarjeta se vería plana. Mismo motivo por el que portfolio-animado.css
   separa ".pa-media" (posición) de ".pa-media-frame" (el que rota). */
.porque-visual-wrap{display:block;}
/* Tarjeta 3D luminiscente: mismo recurso que las tarjetas de proyecto
   de portfolio-animado.css (perspective + rotateX/rotateY vía custom
   properties que mueve script.js, halo pulsante + brillo especular que
   sigue al mouse). --rx/--ry/--glow-x/--glow-y quedan neutros por
   defecto: sin JS (o en touch/reduced-motion) se ve plana y estática. */
.porque-visual{
    position:relative;
    width:100%;
    --rx:0deg;
    --ry:0deg;
    --glow-x:50%;
    --glow-y:50%;
    --card-glow-soft:rgba(97,218,251,.4);
    --glow-blur-a:26px;
    --glow-spread-a:-14px;
    --glow-blur-b:60px;
    --glow-spread-b:-30px;
    border-radius:var(--radius-lg);
    overflow:hidden;
    background:linear-gradient(145deg, #17171b, #0c0c0e);
    border:1px solid var(--border-soft);
    aspect-ratio:5/4;
    display:flex;
    align-items:center;
    justify-content:center;
    transform-style:preserve-3d;
    transform:perspective(1000px) rotateX(var(--rx)) rotateY(var(--ry));
    will-change:transform;
    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);
    animation:porqueGlowPulse 5s ease-in-out infinite;
}
@keyframes porqueGlowPulse{
    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: asoma por el borde al rotar, da sensación
   de grosor real en vez de un plano chato. */
.porque-visual::before{
    content:'';
    position:absolute;
    inset:0;
    border-radius:inherit;
    background:linear-gradient(160deg, rgba(97,218,251,.35), rgba(47,111,237,.25));
    transform:translateZ(-22px);
}
/* Brillo especular que sigue al mouse (overlay glossy, no toca el ícono) */
.porque-visual::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;
}
.porque-visual-icon{
    position:relative;
    width:150px;
    height:150px;
    filter:drop-shadow(0 10px 30px rgba(97,218,251,.35));
    transform:translateZ(34px);
}
.react-logo{color:#61dafb;}
@media (prefers-reduced-motion: reduce){
    .porque-visual{transform:none;animation:none;}
}

.porque-list{display:flex;flex-direction:column;gap:4px;}
.porque-item{
    display:flex;
    gap:16px;
    padding:26px 0;
    border-bottom:1px solid var(--border-soft);
}
.porque-item:last-child{border-bottom:none;}
.porque-num{
    font-family:var(--font-mono);
    font-size:1.2rem;
    font-weight:700;
    color:var(--accent-hot);
    min-width:40px;
}
.porque-text h3{font-size:1.02rem;margin-bottom:6px;}
.porque-text p{font-size:.9rem;}

/* Cierre de sección: agendar llamada directo, aprovechando que ya se leyó
   todo el argumento de "por qué elegirme" (ver index.html). */
.porque-cta{
    margin-top:44px;
    padding-top:32px;
    border-top:1px solid var(--border-soft);
    display:flex;
    flex-wrap:wrap;
    align-items:center;
    justify-content:space-between;
    gap:18px;
}
.porque-cta p{font-size:1rem;color:var(--white);margin:0;}
.porque-cta .btn{width:auto;}

/* =====================================================================
   10. CTA + FORMULARIO
   ===================================================================== */
.cta-final{
    background:linear-gradient(180deg,var(--bg-black) 0%, #060a16 100%);
    overflow-y:hidden; /* contiene el recorrido amplio del blob-dance (ver sección 3) */
}
.cta-grid{
    display:grid;
    grid-template-columns:1fr;
    gap:40px;
}
.cta-left h2{font-size:clamp(1.7rem,6vw,2.8rem);margin-bottom:18px;}
.cta-left p{font-size:1rem;margin-bottom:28px;}
.cta-points{display:flex;flex-direction:column;gap:18px;}
.cta-point{display:flex;align-items:center;gap:12px;font-size:.9rem;color:var(--white);}
.cta-book-btn{margin-top:32px;}
.cta-point .check{
    width:24px;height:24px;
    border-radius:50%;
    background:rgba(47,111,237,0.15);
    color:var(--accent-hot);
    display:flex;align-items:center;justify-content:center;
    font-size:.75rem;
    flex-shrink:0;
}

.form-card{
    position:relative;
    overflow:hidden;
    background-color:var(--bg-card);
    border:1px solid var(--border-soft);
    border-radius:var(--radius-lg);
    padding:26px;
}
/* Marca de agua: el isotipo del logo, muy tenue, centrado detrás de los
   campos del formulario. Solo dentro del modal (".modal-panel .form-card")
   — el mismo .form-card también vive inline en #contacto cuando el modal
   está cerrado, y ahí NO debe llevar watermark. ::before pinta antes que
   el <form> (que también es "position:relative" para volverse un elemento
   posicionado y quedar por delante en el orden de pintado), así que el
   watermark queda siempre detrás de los inputs sin taparlos. */
.modal-panel .form-card::before{
    content:'';
    position:absolute;
    inset:0;
    background:url('../img/logo-icon.webp') no-repeat center center;
    background-size:min(65%, 300px);
    opacity:.05;
    pointer-events:none;
}
.modal-panel .form-card form{position:relative;}
.form-row{display:grid;grid-template-columns:1fr;gap:16px;}
.form-group{margin-bottom:18px;}
.form-group label{
    display:block;
    font-size:.82rem;
    font-weight:700;
    margin-bottom:7px;
    color:var(--gray-soft);
}
.form-group input,
.form-group select,
.form-group textarea{
    width:100%;
    padding:13px 15px;
    background:rgba(255,255,255,0.04);
    border:1px solid var(--border-soft);
    border-radius:var(--radius-sm);
    color:var(--white);
    font-family:'Manrope',sans-serif;
    font-size:.9rem;
    transition:.3s ease;
}
.form-group select option{background:var(--bg-card);}
.form-group input::placeholder,
.form-group textarea::placeholder{color:var(--gray-mid);}
.form-group input:focus,
.form-group select:focus,
.form-group textarea:focus{
    outline:none;
    border-color:var(--accent-hot);
    background:rgba(255,255,255,0.06);
    box-shadow:0 0 0 4px rgba(47,111,237,0.12);
}
.form-group textarea{min-height:110px;resize:vertical;}
.form-submit{
    width:100%;
    border:none;
    cursor:pointer;
    padding:15px;
    border-radius:50px;
    background:linear-gradient(135deg,var(--accent),var(--accent-hot));
    color:#fff;
    font-family:'Manrope',sans-serif;
    font-weight:700;
    font-size:.92rem;
    margin-top:4px;
    transition:.35s ease;
    box-shadow:0 10px 26px -8px var(--accent-glow);
}
.form-submit:hover{transform:translateY(-2px);box-shadow:0 14px 30px -6px rgba(47,111,237,.55);}

/* Modal de contacto: "Pedir presupuesto"/"Solicitar presupuesto" abren esto
   en vez de saltar a #contacto (ver script.js). El .form-card real se
   mueve para adentro del .modal-panel al abrir y vuelve a su lugar al
   cerrar, así que no necesita estilos propios acá — ya trae los suyos. */
.modal-overlay{
    position:fixed;
    inset:0;
    z-index:2000;
    display:flex;
    align-items:center;
    justify-content:center;
    padding:24px;
    opacity:0;
    visibility:hidden;
    transition:opacity .35s ease, visibility 0s linear .35s;
}
.modal-overlay.active{
    opacity:1;
    visibility:visible;
    transition:opacity .35s ease;
}
.modal-backdrop{
    position:absolute;
    inset:0;
    background:rgba(5,6,10,.6);
    backdrop-filter:blur(2px);
}
.modal-panel{
    position:relative;
    width:100%;
    max-width:600px;
    max-height:88vh;
    overflow-y:auto;
    transform:scale(.94) translateY(16px);
    opacity:0;
    transition:transform .4s cubic-bezier(.4,0,.2,1), opacity .3s ease;
}
.modal-overlay.active .modal-panel{
    transform:scale(1) translateY(0);
    opacity:1;
}
.modal-panel .form-card{margin:0;}
/* Alternativa a completar el form: agendar la llamada de 30 min directo
   desde Cal.com (botón [data-cal-link], ver index.html). Va antes del
   .form-card en el DOM así queda arriba, sin desordenar el appendChild
   que hace script.js al abrir el modal. */
.modal-cal-cta{
    padding:56px 26px 0;
    text-align:center;
}
.modal-cal-cta p{
    display:flex;
    flex-wrap:wrap;
    align-items:center;
    justify-content:center;
    gap:10px;
    font-size:.85rem;
    color:var(--gray-soft);
    margin:0;
}
.modal-cal-cta .btn{width:auto;}
.modal-close{
    position:absolute;
    top:14px;
    right:14px;
    width:38px;
    height:38px;
    border-radius:50%;
    display:flex;
    align-items:center;
    justify-content:center;
    background:rgba(255,255,255,0.08);
    border:1px solid var(--border-soft);
    color:var(--white);
    cursor:pointer;
    z-index:1;
    transition:.25s ease;
}
.modal-close svg{width:18px;height:18px;}
.modal-close:hover{background:var(--accent);border-color:var(--accent);}
/* El resto de la página queda "detrás de vidrio" mientras el modal está
   abierto: blur en todos los hijos directos de <body> salvo el modal
   mismo (así funciona sin envolver todo el contenido existente en un
   wrapper nuevo). pointer-events:none evita que, con el mouse, se pueda
   interactuar con lo que queda debajo del blur. Se excluye también
   <cal-modal-box> (el popup de reserva de Cal.com, que se abre encima del
   modal desde el botón "Agendá 30 min gratis" de ahí adentro): Cal lo
   inyecta como hijo directo de <body>, así que sin esta excepción quedaba
   tan blureado e inclickeable como el resto del fondo. */
body.modal-open{overflow:hidden;}
body.modal-open > *:not(.modal-overlay):not(cal-modal-box){
    filter:blur(10px);
    pointer-events:none;
    user-select:none;
    transition:filter .35s ease;
}
@media (prefers-reduced-motion: reduce){
    .modal-overlay, .modal-panel{transition:none;}
    body.modal-open > *:not(.modal-overlay):not(cal-modal-box){transition:none;}
}

/* =====================================================================
   11. FOOTER
   ===================================================================== */
footer{
    background:var(--bg-black-2);
    border-top:1px solid var(--border-soft);
    padding:48px 0 24px;
}
.footer-grid{
    display:grid;
    grid-template-columns:1fr;
    gap:32px;
    margin-bottom:36px;
}
/* En mobile el logo del footer se agranda (3x) y se centra para que
   cierre la página con más presencia; en tablet+ vuelve a su tamaño
   normal alineado a la izquierda (ver breakpoint 768px). */
.footer-brand{text-align:center;}
.footer-brand .logo{justify-content:center;margin-left:0;}
.footer-brand .logo-icon{height:114px;}
.footer-brand p{font-size:.88rem;margin:16px auto 20px;max-width:320px;}
.footer-social{display:flex;gap:10px;justify-content:center;}
.social-icon{
    width:38px;height:38px;
    border-radius:50%;
    border:1px solid var(--border-soft);
    display:flex;align-items:center;justify-content:center;
    font-size:.78rem;
    font-weight:700;
    transition:.3s ease;
}
.social-icon:hover{background:var(--accent);border-color:var(--accent);}
.footer-col h4{font-size:.9rem;margin-bottom:16px;}
.footer-col a{
    display:block;
    color:var(--gray-soft);
    font-size:.88rem;
    margin-bottom:12px;
}
.footer-col a:hover{color:var(--accent-hot);}
.footer-bottom{
    border-top:1px solid var(--border-soft);
    padding-top:22px;
    display:flex;
    flex-direction:column;
    gap:8px;
    font-size:.78rem;
    color:var(--gray-mid);
}

/* =====================================================================
   12. LUZ RESPLANDECIENTE QUE SIGUE AL CURSOR (solo desktop/mouse)
   ===================================================================== */
.cursor-glow{
    position:fixed;
    top:0;left:0;
    width:440px;height:440px;
    border-radius:50%;
    background:radial-gradient(circle, rgba(47,111,237,0.10) 0%, transparent 70%);
    pointer-events:none;
    /* z-index:2, igual que ".grain-overlay" más abajo: por encima de
       ".pa-track" (portfolio-animado.css, z-index:1) para que el brillo
       no quede tapado por los paneles negros de esa página, y por debajo
       del contenido normal de las secciones (que vive a z-index:2 sobre
       su propio stacking context vía isolation:isolate, y gana el empate
       por venir después en el HTML), así sigue sintiéndose de fondo. */
    z-index:2;
    transform:translate(-50%,-50%);
    will-change:transform;
    opacity:0;
    transition:opacity .4s ease;
}
.cursor-glow.active{opacity:1;}

/* =====================================================================
   13. ANIMACIONES DE ENTRADA (reveal on scroll)
   ===================================================================== */
.reveal{
    opacity:0;
    transform:translateY(22px);
    transition:opacity .7s ease, transform .7s ease;
}
.reveal.show{opacity:1;transform:translateY(0);}

/* Cascada de entrada: dentro de un contenedor ".reveal-stagger", cada hijo
   directo con ".reveal" recibe una demora creciente para que aparezcan
   uno detrás del otro (título, párrafos, botón...) en vez de todos a la
   vez de golpe. El reduced-motion de más abajo ya deja ".reveal" sin
   transición, así que ahí la demora queda sin efecto automáticamente. */
.reveal-stagger > .reveal:nth-child(1){transition-delay:.05s;}
.reveal-stagger > .reveal:nth-child(2){transition-delay:.15s;}
.reveal-stagger > .reveal:nth-child(3){transition-delay:.25s;}
.reveal-stagger > .reveal:nth-child(4){transition-delay:.35s;}
.reveal-stagger > .reveal:nth-child(5){transition-delay:.45s;}
.reveal-stagger > .reveal:nth-child(6){transition-delay:.55s;}
.reveal-stagger > .reveal:nth-child(7){transition-delay:.65s;}

/* En touch (mobile/tablet) el desplazamiento vertical del reveal puede dejar
   un botón moviéndose justo cuando el usuario lo toca: el dedo queda quieto
   pero el elemento se desplaza por debajo entre el touchstart y el touchend,
   así que el navegador no dispara el click. Esto es la causa de los botones
   que "a veces andan y a veces no" en mobile — no es aleatorio, depende de
   si se toca mientras el elemento todavía está animando su entrada. En touch
   se deja solo el fade de opacidad, sin mover nada, así el botón nunca
   cambia de posición mientras se lo está tocando. */
@media (hover:none), (pointer:coarse){
    .reveal{transform:none;transition:opacity .5s ease;}
    .reveal.show{transform:none;}
    /* Mismo criterio para el botón flotante de WhatsApp: también tiene una
       animación de entrada con transform (scale+translateY) al cargar la
       página, y un tap justo en ese instante puede fallar igual que con
       .reveal. En touch aparece con opacidad nada más. */
    .whatsapp-float{transform:none;transition:opacity .4s ease;}
    .whatsapp-float.wa-visible{transform:none;}
}

@media (prefers-reduced-motion: reduce){
    .global-bg, .blob, .hero-tag .dot{animation:none !important;}
    .reveal{transition:none;opacity:1;transform:none;}
    .cursor-glow{display:none;}
    .nav-links{transition:opacity .15s ease, visibility 0s linear;transform:none;}
    .hero-content > *, .hero-stats{animation:none !important;opacity:1;transform:none;}
    .portfolio-card{transition:border-color .3s ease, box-shadow .3s ease;}
    .portfolio-card.has-tilt{transform:none;}
    .portfolio-card::after,
    .portfolio-card.sheen-on::after{animation:none !important;background-position:0% 0%;}
}

/* =====================================================================
   14. BOTÓN FLOTANTE DE WHATSAPP
   ===================================================================== */
.whatsapp-float{
    position:fixed;
    right:18px;
    bottom:18px;
    z-index:1200;
    display:flex;
    align-items:center;
    gap:10px;
    opacity:0;
    transform:scale(.6) translateY(20px);
    transition:opacity .5s cubic-bezier(.34,1.56,.64,1), transform .5s cubic-bezier(.34,1.56,.64,1);
}
.whatsapp-float.wa-visible{
    opacity:1;
    transform:scale(1) translateY(0);
}
@media (prefers-reduced-motion: reduce){
    .whatsapp-float{transition:opacity .3s ease;transform:none;}
}

.whatsapp-btn{
    position:relative;
    width:58px;height:58px;
    border-radius:50%;
    background:#25D366;
    display:flex;
    align-items:center;
    justify-content:center;
    box-shadow:0 10px 26px -6px rgba(37,211,102,.55);
    flex-shrink:0;
    animation:wappPulse 2.6s ease-in-out infinite;
}
.whatsapp-btn:hover{transform:scale(1.06);}
.whatsapp-btn svg{width:30px;height:30px;}

@keyframes wappPulse{
    0%,100%{box-shadow:0 10px 26px -6px rgba(37,211,102,.55), 0 0 0 0 rgba(37,211,102,.5);}
    50%{box-shadow:0 10px 26px -6px rgba(37,211,102,.55), 0 0 0 10px rgba(37,211,102,0);}
}

@media (prefers-reduced-motion: reduce){
    .whatsapp-btn{animation:none;}
}

@media (min-width:768px){
    .whatsapp-float{right:28px;bottom:28px;}
}

/* =====================================================================
   BOTÓN VOLVER ARRIBA — esquina inferior izquierda, sutil, sobre el
   asistente IA. Aparece recién al bajar un poco la página.
   ===================================================================== */
.back-to-top{
    position:fixed;
    left:18px;
    bottom:100px;
    z-index:1150;
    width:48px;height:48px;
    border-radius:50%;
    border:1px solid var(--border-soft);
    background:rgba(255,255,255,.05);
    backdrop-filter:blur(10px);
    -webkit-backdrop-filter:blur(10px);
    color:var(--white);
    display:flex;
    align-items:center;
    justify-content:center;
    cursor:pointer;
    opacity:0;
    visibility:hidden;
    pointer-events:none;
    transform:translateY(10px);
    transition:opacity .35s ease, transform .35s ease, visibility .35s, background .25s ease, border-color .25s ease;
}
.back-to-top svg{width:20px;height:20px;}
.back-to-top.show{
    opacity:1;
    visibility:visible;
    pointer-events:auto;
    transform:translateY(0);
}
.back-to-top:hover{
    background:var(--accent);
    border-color:var(--accent);
    transform:translateY(-2px);
}
@media (prefers-reduced-motion: reduce){
    .back-to-top{transition:opacity .25s ease, visibility .25s;}
    .back-to-top.show{transform:none;}
    .back-to-top:hover{transform:none;}
}
@media (min-width:768px){
    .back-to-top{left:28px;bottom:112px;}
}

/* =====================================================================
   16. BREAKPOINTS — mobile-first: agregamos complejidad hacia arriba
   ===================================================================== */

/* ---------- Tablet: 768px+ ---------- */
@media (min-width:768px){
    :root{
        --section-pad:96px;
        --container-pad:32px;
    }

    .nav-links{
        position:static;
        display:flex;
        flex-direction:row;
        align-items:center;
        gap:32px;
        background:transparent;
        backdrop-filter:none;
        padding:0;
        width:auto;
        overflow:visible;
        /* En desktop el menú es inline y siempre visible: anulamos los
           estados de animación de apertura/cierre usados en mobile */
        opacity:1;
        visibility:visible;
        transform:none;
        pointer-events:auto;
        transition:none;
    }
    .nav-links a:not(.btn){
        padding:0;
        border-bottom:none;
        font-size:.95rem;
    }
    .nav-links .btn{margin-top:0;}
    .nav-links .btn + .nav-link-shine{margin-top:0;}
    .hamburger{display:none;}
    .nav-wrap{height:78px;}
    .logo{margin-left:10px;}
    .logo-icon{height:44px;}
    .lang-switch{
        /* Desktop/tablet: vuelve al flujo normal, pegado a los botones del nav */
        position:relative;
        left:auto;
        top:auto;
        transform:none;
        margin-left:10px;
    }

    .hero{padding:150px 0 0;min-height:100vh;}
    .hero-cta{flex-direction:row;justify-content:center;flex-wrap:wrap;}
    .hero-cta .btn{width:auto;}
    .hero-stats{gap:40px;}
    .stat-number{font-size:2.3rem;}
    .stat-label{font-size:.8rem;}
    .particle-bg{height:300px;}

    .servicios-grid{grid-template-columns:repeat(2,1fr);gap:22px;}
    .portfolio-grid{grid-template-columns:repeat(2,1fr);}
    .portfolio-header-row{flex-direction:row;justify-content:space-between;align-items:flex-end;}
    .portfolio-header-row .section-header{margin-bottom:0;}
    .portfolio-filters-col{align-items:flex-end;}
    .portfolio-filters-col .btn-glow{align-self:auto;margin-bottom:0;}

    .porque-grid{grid-template-columns:0.9fr 1.1fr;gap:56px;align-items:center;}
    .cta-grid{grid-template-columns:1fr;gap:50px;}
    .form-row{grid-template-columns:1fr 1fr;}
    .footer-grid{grid-template-columns:1fr 1fr;}
    .footer-bottom{flex-direction:row;justify-content:space-between;}
    .footer-brand{text-align:left;}
    .footer-brand .logo{justify-content:flex-start;margin-left:6px;}
    .footer-brand .logo-icon{height:38px;}
    .footer-brand p{margin:16px 0 20px;}
    .footer-social{justify-content:flex-start;}
}

/* ---------- Desktop: 1024px+ ---------- */
@media (min-width:1024px){
    :root{--section-pad:130px;}

    .logo{margin-left:16px;}
    .logo-icon{height:50px;}

    .hero-cta{margin-bottom:90px;}
    .servicios-grid{grid-template-columns:repeat(3,1fr);}
    .portfolio-grid{grid-template-columns:repeat(3,1fr);}
    .portfolio-card{min-height:400px;}
    .cta-grid{grid-template-columns:0.85fr 1.15fr;align-items:flex-start;}
    .footer-grid{grid-template-columns:1.4fr 1fr 1fr 1fr;}
    .form-card{padding:40px;}
    .modal-cal-cta{padding:56px 40px 0;}
    .service-card{padding:36px;}
}

/* ---------- XL: 1280px+ (afinado tipográfico) ---------- */
@media (min-width:1280px){
    .hero h1{font-size:4.8rem;}
}
