/* ==========================================================================
   Fase 3 — Sistema visual global (estudio premium)
   Capa aditiva: no reemplaza site.css ni staging-readiness.css, los refina.
   Paleta y tipografía se mantienen exactamente como en el brandbook oficial.
   ========================================================================== */

:root{
  --dur-fast:.2s;
  --dur-base:.35s;
  --dur-slow:.6s;
  --ease-premium:cubic-bezier(.22,1,.36,1);
  --shadow-soft:0 10px 30px -14px rgba(17,17,17,.22);
  --shadow-lift:0 18px 44px -16px rgba(17,17,17,.32);
}

/* --------------------------------------------------------------------------
   Enlaces de texto independientes (.text-link): antes no tenían estilo
   propio y eran indistinguibles del texto normal. Se les da identidad
   coherente con el resto del sistema (subrayado que crece al interactuar).
   -------------------------------------------------------------------------- */
.text-link{
  position:relative;
  display:inline-flex;
  align-items:center;
  gap:.4em;
  color:var(--blue);
  font-weight:700;
  padding-bottom:2px;
  border-bottom:1px solid rgba(0,113,185,.35);
  transition:border-color var(--dur-base) var(--ease-premium),
             gap var(--dur-base) var(--ease-premium),
             color var(--dur-base) var(--ease-premium);
}
.text-link:hover,
.text-link:focus-visible{
  border-color:currentColor;
  gap:.65em;
}
.dark .text-link{ color:#6cc4ff; border-color:rgba(108,196,255,.4); }

/* --------------------------------------------------------------------------
   Botones primarios: misma posición y tamaño, microinteracción más cuidada.
   -------------------------------------------------------------------------- */
.button{
  box-shadow:var(--shadow-soft);
  transition:background .2s ease,
             transform var(--dur-base) var(--ease-premium),
             box-shadow var(--dur-base) var(--ease-premium);
}
.button:hover{ box-shadow:var(--shadow-lift); }
.button:active{ transform:translateY(0); box-shadow:var(--shadow-soft); }

/* --------------------------------------------------------------------------
   Reveal con stagger: las tarjetas de una misma cuadrícula entran con una
   cascada breve en vez de aparecer todas a la vez. Mantiene el principio de
   quietud → movimiento con intención → quietud, y respeta reduced-motion
   a través de la clase .reveal-ready que ya gestiona site.js.
   -------------------------------------------------------------------------- */
.reveal-ready .section.reveal.is-visible .service-list > .service-card,
.reveal-ready .section.reveal.is-visible .project-grid > .project-card{
  opacity:0;
  transform:translateY(18px);
  animation:jg-stagger-in .65s var(--ease-premium) forwards;
}
.service-list > .service-card:nth-child(1),.project-grid > .project-card:nth-child(1){ animation-delay:.05s; }
.service-list > .service-card:nth-child(2),.project-grid > .project-card:nth-child(2){ animation-delay:.16s; }
.service-list > .service-card:nth-child(3),.project-grid > .project-card:nth-child(3){ animation-delay:.27s; }
.service-list > .service-card:nth-child(4),.project-grid > .project-card:nth-child(4){ animation-delay:.38s; }
.service-list > .service-card:nth-child(5),.project-grid > .project-card:nth-child(5){ animation-delay:.49s; }
@keyframes jg-stagger-in{ to{ opacity:1; transform:none; } }

@media(prefers-reduced-motion:reduce){
  .reveal-ready .section.reveal.is-visible .service-list > .service-card,
  .reveal-ready .section.reveal.is-visible .project-grid > .project-card{
    opacity:1; transform:none; animation:none;
  }
}

/* --------------------------------------------------------------------------
   Foco consistente en tarjetas completas (antes solo definido para nav y
   elementos de formulario).
   -------------------------------------------------------------------------- */
.service-card a:focus-visible,
.project-card a:focus-visible{ outline:3px solid #005a94; outline-offset:-3px; }
.dark .service-card a:focus-visible,
.dark .project-card a:focus-visible{ outline-color:#6cc4ff; }

/* --------------------------------------------------------------------------
   Elemento flotante ".joinchat" (no es un plugin de este sitio — ver nota
   completa en Fase 8 más abajo — probablemente inyectado de forma remota
   por el servicio de reseñas): margen seguro en dispositivos con home
   indicator (iOS) y, sobre todo, despegado del botón propio del tema
   (.whatsapp-float) para que, si llega a aparecer, no se solape con él.
   -------------------------------------------------------------------------- */
.joinchat{ bottom:max(90px, calc(env(safe-area-inset-bottom) + 84px)) !important; }

@media(prefers-reduced-motion:reduce){
  .button,.text-link{ transition:none; }
}

/* ==========================================================================
   Fase 4 — Hero: entrada escalonada, profundidad y llamada a explorar.
   Estructura y contenido del hero no cambian, solo se refuerza la puesta
   en escena de lo que ya existe.
   ========================================================================== */

/* Entrada escalonada de la copy: eyebrow → titular → descripción → CTA,
   en vez de un único bloque animado a la vez. */
.hero-copy{ animation:none; }
.hero-copy > *{
  opacity:0;
  transform:translateY(16px);
  animation:jg-hero-in .7s var(--ease-premium) both;
}
.hero-copy > .eyebrow{ animation-delay:.15s; }
.hero-copy > h1{ animation-delay:.28s; }
.hero-copy > p:not(.eyebrow){ animation-delay:.44s; }
.hero-copy > .button{ animation-delay:.58s; }
@keyframes jg-hero-in{ to{ opacity:1; transform:none; } }

/* Profundidad sutil: la imagen de fondo se desplaza un poco más lento que
   el scroll mientras el hero está en pantalla (ver hook en site.js). Se
   inhabilita en mobile y con reduced-motion. */
@media(min-width:761px){
  .hero{ overflow:clip; }
  .hero-image{ will-change:transform; }
}

/* Indicador de scroll: quietud con un detalle mínimo de intención, no un
   adorno permanente — se retira en cuanto el usuario empieza a hacer scroll
   (clase .is-scrolled-past en <html>, gestionada por site.js). */
.hero-scroll-cue{
  position:absolute;
  left:clamp(20px,6vw,90px);
  bottom:28px;
  z-index:1;
  display:flex;
  align-items:center;
  gap:10px;
  color:#fff;
  opacity:.85;
  font-size:.72rem;
  letter-spacing:.14em;
  text-transform:uppercase;
  font-weight:700;
  animation:jg-hero-in .7s var(--ease-premium) .8s both;
}
.hero-scroll-cue::before{
  content:"";
  width:1px;
  height:34px;
  background:linear-gradient(to bottom, rgba(255,255,255,.9), rgba(255,255,255,0));
  animation:jg-scroll-cue 1.8s ease-in-out infinite;
}
@keyframes jg-scroll-cue{
  0%,100%{ transform:scaleY(1); transform-origin:top; opacity:.9; }
  50%{ transform:scaleY(.55); transform-origin:top; opacity:.5; }
}
html.is-scrolled-past .hero-scroll-cue{ opacity:0; pointer-events:none; transition:opacity .3s ease; }

@media(prefers-reduced-motion:reduce){
  .hero-copy > *{ opacity:1; transform:none; animation:none; }
  .hero-scroll-cue{ opacity:.85; animation:none; }
  .hero-scroll-cue::before{ animation:none; }
}

/* ==========================================================================
   Fase 5 — Navegación: el menú móvil pasa de aparecer/desaparecer en seco a
   una entrada suave con una cascada breve por enlace. Misma estructura,
   mismos enlaces, mismo comportamiento accesible (aria-expanded ya existente).
   ========================================================================== */
@media(max-width:760px){
  .primary-nav{
    transform:translateY(-8px);
    transition:opacity var(--dur-base) var(--ease-premium),
               transform var(--dur-base) var(--ease-premium),
               visibility var(--dur-base);
  }
  .primary-nav[data-open="true"]{ transform:none; }
  .primary-nav ul li{
    opacity:0;
    transform:translateY(6px);
  }
  .primary-nav[data-open="true"] ul li{
    animation:jg-nav-item-in .45s var(--ease-premium) forwards;
  }
  .primary-nav[data-open="true"] ul li:nth-child(1){ animation-delay:.05s; }
  .primary-nav[data-open="true"] ul li:nth-child(2){ animation-delay:.1s; }
  .primary-nav[data-open="true"] ul li:nth-child(3){ animation-delay:.15s; }
  .primary-nav[data-open="true"] ul li:nth-child(4){ animation-delay:.2s; }
  .primary-nav[data-open="true"] ul li:nth-child(5){ animation-delay:.25s; }
  @keyframes jg-nav-item-in{ to{ opacity:1; transform:none; } }
}
@media(prefers-reduced-motion:reduce){
  .primary-nav{ transition:none; }
  .primary-nav ul li{ opacity:1; transform:none; animation:none; }
}

/* ==========================================================================
   Fase 6 — Tipografía y spacing: refuerzos puntuales de ritmo vertical y
   calidad de línea, sin cambiar familia, tamaños base ni jerarquía.
   ========================================================================== */
h1,h2,h3,.display{ text-wrap:balance; }
.eyebrow{ display:inline-block; margin-bottom:.6em; }
.rich-text p + p,
.about-content p + p{ margin-top:1.1em; }

/* ==========================================================================
   Fase 7 — Secciones principales
   ========================================================================== */

/* --------------------------------------------------------------------------
   "Sobre Jaime" en home: se reutiliza el patrón .about-intro/.about-portrait
   ya existente en la página "Sobre Jaime" (misma retícula, misma imagen de
   retrato vía la opción jg_portrait_image_id) en vez de crear un layout
   nuevo. Antes era solo texto a dos columnas; ahora tiene composición
   editorial con imagen, consistente con el resto del sitio.
   -------------------------------------------------------------------------- */
.about-intro.section{ padding-block:clamp(64px,9vw,120px); }

/* --------------------------------------------------------------------------
   Footer: nueva columna de navegación ("Explorar") que añade profundidad
   real sin tocar la marca, identidad ni enlaces sociales existentes. Se
   añade una cuarta pista a la retícula ya definida en staging-readiness.css
   en vez de sobreescribirla por completo.
   -------------------------------------------------------------------------- */
.site-footer__nav{ display:grid; gap:10px; align-content:start; }
.site-footer__nav-title{
  margin:0 0 4px;
  color:#cacaca;
  font-size:.72rem;
  letter-spacing:.12em;
  text-transform:uppercase;
  font-weight:700;
}
.site-footer__nav ul{ list-style:none; margin:0; padding:0; display:grid; gap:10px; }
.site-footer__nav a{
  width:fit-content;
  border-bottom:1px solid transparent;
  transition:color var(--dur-base) var(--ease-premium), border-color var(--dur-base) var(--ease-premium);
}
.site-footer__nav a:hover,
.site-footer__nav a:focus-visible{ color:#6cc4ff; border-color:currentColor; }

@media(min-width:761px){
  .site-footer__inner{
    grid-template-columns:minmax(160px,.7fr) minmax(280px,1.15fr) minmax(130px,.55fr) minmax(130px,.55fr) !important;
  }
}
@media(max-width:1024px) and (min-width:761px){
  .site-footer__inner{ grid-template-columns:.7fr 1.15fr .55fr .55fr !important; }
}
@media(max-width:760px){
  .site-footer__nav ul{ grid-template-columns:repeat(2,max-content); gap:10px 24px; }
}

@media(prefers-reduced-motion:reduce){
  .site-footer__nav a{ transition:none; }
}

/* --------------------------------------------------------------------------
   Widget de reseñas (Trustindex, plugin de terceros — no se toca su código
   ni shortcode). Su tarjeta por defecto usa tipografía Poppins y un embalaje
   genérico (avatar redondo grande, icono de Google flotante, sombra suave)
   que desentona con el resto del sitio, muy cuidado tipográficamente. Se
   sobreescribe únicamente la piel visual, dentro del contenedor
   .trustindex-slot, sin tocar la estructura ni el comportamiento del slider.
   -------------------------------------------------------------------------- */
.trustindex-slot .ti-widget,
.trustindex-slot .ti-widget *{
  font-family:"Lato",Arial,sans-serif !important;
}
.trustindex-slot .ti-review-item .ti-inner{
  background:#fff !important;
  border:1px solid var(--soft) !important;
  border-radius:0 !important;
  box-shadow:none !important;
  padding:clamp(28px,4vw,40px) !important;
}
.trustindex-slot .ti-name{
  font-family:"IBM Plex Sans Condensed",Arial,sans-serif !important;
  font-size:1.15rem !important;
  font-weight:600 !important;
  color:#111 !important;
}
.trustindex-slot .ti-date{ color:#767676 !important; font-size:.85rem !important; }
.trustindex-slot .ti-platform-icon{ opacity:.55; }
.trustindex-slot .ti-review-text-container{
  font-size:1.08rem !important;
  line-height:1.7 !important;
  color:#333 !important;
  margin-top:16px !important;
}

/* ==========================================================================
   Fase 8 — Botones y CTAs
   ========================================================================== */

/* --------------------------------------------------------------------------
   Hallazgo de Fase 7, revisado tras investigar más a fondo: el elemento
   ".joinchat" que aparecía sobre ".whatsapp-float" NO es un plugin instalado
   en este sitio — no hay ningún plugin de WhatsApp activo (se verificó la
   lista completa de plugins) ni una sola referencia a "joinchat" en el
   código del tema o de wp-content. Todo indica que es un widget adicional
   que el propio servicio externo de reseñas (Trustindex, cargado vía
   https://cdn.trustindex.io/loader.js) inyecta de forma remota y variable
   según su configuración en la nube — no algo que este sitio controle, ni
   que esté garantizado que aparezca siempre (en pruebas posteriores ya no
   apareció). Por eso NO se oculta ".whatsapp-float": es la única CTA de
   WhatsApp fiable y propia del tema, y ocultarla habría arriesgado dejar el
   sitio sin ninguna vía de contacto por WhatsApp si ese widget remoto no
   carga. La reconciliación real está arriba, en la regla ".joinchat" ya
   existente desde Fase 3: se le añadió despegue suficiente para que, si el
   widget externo llega a aparecer, quede apilado por encima de
   ".whatsapp-float" en vez de solaparse con él.
   -------------------------------------------------------------------------- */

/* --------------------------------------------------------------------------
   CTA secundaria "por WhatsApp" junto a un botón primario (.plan-whatsapp,
   en la reserva de sesiones y en el mensaje de pago recibido): antes era un
   subrayado estático suelto, con un tratamiento distinto al del resto de
   enlaces secundarios del sitio. Se alinea con la identidad ya establecida
   en .text-link (Fase 3) para que toda CTA secundaria del sitio se sienta
   igual, sin cambiar su posición ni su jerarquía (sigue siendo secundaria
   frente al botón sólido).
   -------------------------------------------------------------------------- */
.plan-whatsapp{
  display:inline-flex;
  align-items:center;
  border-bottom:1px solid rgba(0,95,158,.35);
  text-decoration:none !important;
  transition:border-color var(--dur-base) var(--ease-premium);
}
.plan-whatsapp:hover,
.plan-whatsapp:focus-visible{ border-color:currentColor; }

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

/* ==========================================================================
   Fase 9 — Imágenes y dirección visual fotográfica
   Auditoría previa (sin cambios de código, solo hallazgos):
   - Hero: bien configurado (fetchpriority=high, loading=eager, focal point
     propio por imagen vía _jg_object_position/_jg_object_position_mobile,
     srcset correcto) pero la foto fuente actual ("cover-template-1.jpg")
     solo tiene candidatos de srcset hasta 1200w — en monitores grandes o
     pantallas retina se vería algo suave. No es un problema de código: hace
     falta una foto de origen de mayor resolución (≥2400px de ancho) cuando
     Jaime tenga una; mientras tanto no hay nada que el CSS pueda corregir.
   - Portafolio (home): algunas fotos subidas en 2026/09 (.webp) no tienen
     srcset/sizes generado por WordPress (solo la foto de 1200×900 exacta),
     a diferencia de las fotos más antiguas (2020) que sí tienen el juego
     completo de tamaños. En mobile se descarga el mismo archivo que en
     desktop — no es grave (son WebP livianos) pero sí es una oportunidad de
     mejora real que requiere regenerar miniaturas por WP-CLI/wp-admin, no
     algo resoluble desde el tema.
   - Retrato "Sobre Jaime" (home y página interna): el crop 4:3 ya funciona
     bien en 375/430/768/1440 con el punto focal por defecto (50% 50%) — el
     rostro queda compuesto correctamente en todos los anchos probados, así
     que no se fuerza un object-position distinto por breakpoint para no
     tocar algo que ya se ve bien.
   - Galería de proyecto individual (.gallery) y de servicio (.service-gallery):
     ya son masonry de 2 columnas que respeta la proporción natural de cada
     foto (sin recorte forzado) — coherente con la dirección editorial. Solo
     le faltaba una microinteracción de hover, que se añade abajo.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Portafolio (home, sección oscura): se preserva la dirección ya detectada
   (grid de 2 columnas, captions editoriales debajo de la foto, sin overlays
   de texto sobre imagen) y se eleva la microinteracción — la foto entra con
   un asentamiento sutil de escala (además del fade en cascada ya existente
   de Fase 3) y la tarjeta completa gana una elevación ligera al pasar el
   cursor, en vez de solo la imagen, para que el "levantamiento" se sienta
   igual que en las tarjetas de servicio (.service-card ya usa
   translateY(-4px)).
   -------------------------------------------------------------------------- */
.project-card a{
  transition:transform var(--dur-base) var(--ease-premium);
}
.project-card a:hover{ transform:translateY(-4px); }
.project-card img{
  transition:transform .7s var(--ease-premium), filter .5s ease;
}
.project-card a:hover img{ transform:scale(1.035); filter:saturate(1.08); }

@media(prefers-reduced-motion:reduce){
  .project-card a,
  .project-card img{ transition:none; }
}

/* --------------------------------------------------------------------------
   Galería de proyecto individual y de servicio (.gallery, .service-gallery):
   preservan la proporción natural de cada foto (masonry, sin recorte) — solo
   les faltaba una respuesta táctil al pasar el cursor, consistente con el
   resto del sitio.
   -------------------------------------------------------------------------- */
.gallery button img{
  transition:transform .5s var(--ease-premium);
}
.gallery button:hover img{ transform:scale(1.015); }

@media(prefers-reduced-motion:reduce){
  .gallery button img{ transition:none; }
}

/* ==========================================================================
   FASE 10 — MOTION
   ========================================================================== */

/* --------------------------------------------------------------------------
   Hero: el parallax de scroll de site.js (Fase 4) nunca se veía. Causa raíz:
   site.css anima `.hero-image` con `animation: hero-image 1.2s ... both`, y
   esa animación mueve `transform` (scale) además de `opacity`. Con
   fill-mode:both, el valor final de `transform` de la animación queda
   "sostenido" indefinidamente y gana por encima del `transform` inline que
   site.js escribe en cada frame de scroll — el navegador nunca pinta el
   parallax aunque el JS sí se ejecuta correctamente.

   Fix (mismo patrón que `.hero-copy{ animation:none }` en Fase 4): se
   reemplaza la animación por una que sólo anima opacity, dejando `transform`
   completamente libre para que el parallax de site.js lo controle. Se
   preserva la sensación de entrada (fade) y se gana el efecto de profundidad
   que antes estaba roto.
   -------------------------------------------------------------------------- */
@keyframes hero-image-fade{
  from{ opacity:.25; }
  to{ opacity:.68; }
}
.hero-image{
  animation:hero-image-fade 1.2s cubic-bezier(.22,1,.36,1) both;
}
@media(prefers-reduced-motion:reduce){
  .hero-image{ animation:none; opacity:.68; }
}

/* ==========================================================================
   FASE 11 — RESPONSIVE (375–1440px, revisión profunda)
   Nota de metodología: además de la inspección visual, cada hallazgo se
   verificó de forma objetiva con getBoundingClientRect/computedStyle en los
   7 anchos pedidos (375/390/430/768/1024/1280/1440) — cero overflow
   horizontal detectado en home, servicios, portafolio, proyecto individual,
   contacto y WooCommerce en ningún ancho. Los tres ajustes de esta fase son
   aditivos, no tocan site.css ni staging-readiness.css, y están acotados a
   mobile o a landscape corto — nada cambia en desktop/tablet.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1) Cards de servicio en mobile: `.service-card__content` trae un
   `min-height:250px` (staging-readiness.css) pensado para alinear la altura
   de las 3 tarjetas en la retícula de 3 columnas de desktop — con
   descripciones cortas eso es correcto ahí. En mobile (1 columna, sin fila
   que igualar) ese mismo min-height fuerza un salto de línea en blanco de
   45–85px antes de "Conocer el servicio →" en las 8 tarjetas de servicio del
   sitio (home y /servicios/), exactamente la "altura incómoda" señalada
   desde el diagnóstico de Fase 1. Se libera el alto mínimo solo en mobile y
   se fija un espaciado editorial fijo antes del enlace.
   -------------------------------------------------------------------------- */
@media(max-width:760px){
  .service-card__content{ min-height:auto; }
  .service-card__content span{ margin-top:20px; }
}

/* --------------------------------------------------------------------------
   2) Lightbox de galería en mobile: `site.css` fija `.lightbox{padding:60px
   70px}` y la imagen a `max-width:90vw`. En pantallas < ~700px de ancho la
   suma (70px de padding + 90vw de imagen) supera el viewport, y como
   `place-items:center` en Chrome desborda desde el borde de inicio (no de
   forma simétrica), la imagen queda recortada contra el borde derecho de la
   pantalla — confirmado midiendo el rect de la imagen en 375px (llegaba a
   407px de ancho real de viewport 375px). Los botones prev/next/close son
   `position:absolute` con offsets fijos, así que no dependen del padding y
   siguen funcionando igual. Se reduce el padding horizontal solo en mobile y
   se ajusta el ancho máximo de la imagen para que siempre quepa.
   -------------------------------------------------------------------------- */
@media(max-width:640px){
  .lightbox{ padding:56px 20px; }
  .lightbox figure img{ max-width:calc(100vw - 40px); }
}

/* --------------------------------------------------------------------------
   3) Hero en landscape móvil (poca altura, ej. 812×375): la copy del hero
   (eyebrow + título + descripción + CTA) mide de forma natural más que la
   altura del viewport, dejando el botón "Solicitar cotización" fuera de la
   pantalla sin hacer scroll — confirmado: con 375px de alto, la copy medía
   392px de alto. Se comprime el ritmo vertical del hero únicamente cuando la
   altura disponible es corta (no afecta el mobile vertical habitual, que
   tiene alto de sobra), y se oculta el indicador "Explorar" en ese caso
   concreto porque no hay espacio para que cumpla su función.
   -------------------------------------------------------------------------- */
@media(max-height:500px) and (max-width:900px){
  .hero-copy{ padding-block:clamp(16px,5vh,32px); }
  .hero-copy h1{ font-size:clamp(1.8rem,6vh,2.4rem); }
  .hero-copy > .eyebrow{ margin-bottom:.3em; }
  .hero-copy > p:not(.eyebrow){ margin-block:.4em; }
  .hero-copy > .button{ margin-top:8px; }
  .hero-scroll-cue{ display:none; }
}
