/* ==========================================================================
   MOVIMIENTO — ArquiConstrucción
   Criterio: skill emil-design-eng. Solo transform y opacity (y clip-path en revelados de imagen).
   Los estados iniciales ocultos cuelgan de la clase .js (assets/js/motion-init.js): sin JS todo se ve.
   Cómo desactivar cada efecto: ver docs/MOVIMIENTO.md.
   ========================================================================== */

:root {
  /* Curvas (nunca ease-in en UI) */
  --ease-out: cubic-bezier(0.23, 1, 0.32, 1);      /* entradas y feedback */
  --ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);  /* movimiento en pantalla */
  --ease-slow: cubic-bezier(0.2, 0.6, 0.3, 1);     /* zoom lento del hero */

  /* Duraciones */
  --dur-fast: 160ms;   /* hover, pulsación */
  --dur-base: 220ms;   /* menú, lightbox, cabecera */
  --dur-slow: 640ms;   /* revelados de sección y entrada del hero (se ven una vez) */
  --stagger: 70ms;

  /* Elevación (sombras suaves y coherentes) */
  --elev-1: 0 1px 2px rgba(42, 45, 46, .06), 0 4px 14px rgba(42, 45, 46, .06);
  --elev-2: 0 2px 4px rgba(42, 45, 46, .06), 0 14px 34px rgba(42, 45, 46, .12);
  --elev-3: 0 4px 8px rgba(42, 45, 46, .08), 0 24px 50px rgba(42, 45, 46, .18);
}

/* ---- Botones: transición de estado (180–220 ms), pulsación y zoom del ícono ----
   Estados de color en styles.css (sistema de botones). Aquí: transición, :active y movimiento. */
.btn {
  transition:
    background-color var(--dur-base) var(--ease-out),
    border-color var(--dur-base) var(--ease-out),
    color var(--dur-base) var(--ease-out),
    translate var(--dur-base) var(--ease-out),
    scale var(--dur-fast) var(--ease-out),
    box-shadow var(--dur-base) var(--ease-out);
}
.btn:active { scale: .97; }

.btn svg, .wa-fab svg, .wa-call svg { transition: scale var(--dur-base) var(--ease-out), translate var(--dur-base) var(--ease-out); }
@media (hover: hover) and (pointer: fine) {
  .btn-primary:hover, .btn-cta:hover { translate: 0 -2px; }
  .btn:hover svg, .wa-fab:hover svg, .wa-call:hover svg { scale: 1.15; }    /* zoom pequeño del ícono */
  .btn-arrow:hover svg { translate: 4px 0; }                                  /* la flecha además avanza */
}

/* ==========================================================================
   1. HERO DE LA PORTADA
   Secuencia única al cargar: eyebrow → líneas del titular → subtítulo → botones (≈ 0,9 s).
   La imagen NO se anima en opacidad (es el LCP); solo hace un zoom lento de transform.
   ========================================================================== */
.hero-line { display: block; }

.js .hero-in {
  animation: hero-in var(--dur-slow) var(--ease-out) both;
  animation-delay: calc(60ms + var(--i, 0) * var(--stagger));
}
@keyframes hero-in {
  from { opacity: 0; transform: translateY(18px); }
  to   { opacity: 1; transform: none; }
}

/* Zoom lento tipo Ken Burns: una sola pasada, 1,00 → 1,06 */
.js .hero:not(.hero--page) .hero-media img {
  transform-origin: 60% 50%;
  animation: hero-zoom 18s var(--ease-slow) both;
}
@keyframes hero-zoom {
  from { transform: scale(1); }
  to   { transform: scale(1.06); }
}

/* Parallax mínimo (lo activa motion.js solo en escritorio con puntero fino): sobredimensiona el medio */
.hero-media.is-parallax { top: -32px; bottom: auto; height: calc(100% + 32px); will-change: transform; }

/* Indicador de scroll: un filete bronce que se dibuja una vez (sin bucle) */
.hero-scroll { display: none; }
@media (min-width: 1024px) {
  .hero:not(.hero--page) .hero-scroll {
    position: absolute;
    left: 50%;
    bottom: 22px;
    z-index: 1;
    display: block;
    width: 2px;
    height: 44px;
    background: linear-gradient(var(--bronce-claro), transparent);
  }
  .js .hero:not(.hero--page) .hero-scroll {
    animation: hero-scroll-in 800ms var(--ease-in-out) 1100ms both;
  }
}
@keyframes hero-scroll-in {
  from { clip-path: inset(0 0 100% 0); }
  to   { clip-path: inset(0); }
}

@media (prefers-reduced-motion: reduce) {
  .js .hero-in { animation: hero-fade 200ms linear both; animation-delay: 0ms; }
  @keyframes hero-fade { from { opacity: 0; } to { opacity: 1; } }
  .js .hero:not(.hero--page) .hero-media img,
  .js .hero:not(.hero--page) .hero-scroll { animation: none; }
  .btn, .btn svg, .wa-fab svg, .wa-call svg { transition: none; }
  .btn:active, .btn-primary:hover, .btn-cta:hover, .btn:hover svg, .wa-fab:hover svg, .wa-call:hover svg, .btn-arrow:hover svg { scale: none; translate: none; }
}

/* ==========================================================================
   2. REVELADO AL SCROLL (componente único: [data-reveal], [data-reveal-delay])
   Lo activa motion.js añadiendo .reveal-ready; lo que ya está a la vista al cargar no se oculta.
   data-reveal="clip": la foto se destapa con clip-path (de abajo hacia arriba), solo en piezas destacadas.
   ========================================================================== */
.reveal-ready [data-reveal] {
  transition:
    opacity var(--dur-slow) var(--ease-out) var(--reveal-delay, 0ms),
    transform var(--dur-slow) var(--ease-out) var(--reveal-delay, 0ms);
}
.reveal-ready [data-reveal]:not(.is-in) { opacity: 0; transform: translateY(20px); }

/* Variantes laterales (introducciones y bloques de texto): 32 px + opacidad, 560 ms, una sola vez.
   El desplazamiento se contiene con overflow-x: clip en la sección (no en el body). */
.reveal-ready [data-reveal="left"],
.reveal-ready [data-reveal="right"] {
  transition:
    opacity 560ms var(--ease-out) var(--reveal-delay, 0ms),
    transform 560ms var(--ease-out) var(--reveal-delay, 0ms);
}
.reveal-ready [data-reveal="left"]:not(.is-in)  { opacity: 0; transform: translateX(-32px); }
.reveal-ready [data-reveal="right"]:not(.is-in) { opacity: 0; transform: translateX(32px); }
.section { overflow-x: clip; }

.reveal-ready [data-reveal="clip"] { transition: none; }
.reveal-ready [data-reveal="clip"]:not(.is-in) { opacity: 1; transform: none; }
[data-reveal="clip"] .media { clip-path: inset(0 round var(--radio)); }
.reveal-ready [data-reveal="clip"] .media {
  transition: clip-path 900ms var(--ease-in-out) var(--reveal-delay, 0ms), box-shadow var(--dur-base) var(--ease-out), transform var(--dur-base) var(--ease-out);
}
.reveal-ready [data-reveal="clip"]:not(.is-in) .media { clip-path: inset(0 0 100% 0 round var(--radio)); }
.reveal-ready [data-reveal="clip"] .project-body {
  transition: opacity var(--dur-slow) var(--ease-out) calc(var(--reveal-delay, 0ms) + 350ms), transform var(--dur-slow) var(--ease-out) calc(var(--reveal-delay, 0ms) + 350ms);
}
.reveal-ready [data-reveal="clip"]:not(.is-in) .project-body { opacity: 0; transform: translateY(12px); }

/* ==========================================================================
   3. PROYECTOS Y GALERÍAS
   Hover solo con puntero fino: elevación, zoom lento de la foto (el .media recorta con overflow)
   y una etiqueta decorativa que sube 8 px. El título y el enlace siempre están visibles (táctil y teclado).
   ========================================================================== */
.project .media img { transform-origin: 50% 50%; }
.project .media::after {
  content: "Ver proyecto" / "";
  position: absolute;
  left: 14px;
  bottom: 14px;
  padding: 8px 14px;
  border-radius: 999px;
  background: var(--carbon);
  color: var(--blanco);
  font-family: var(--fuente-titulo);
  font-size: .8125rem;
  font-weight: 700;
  opacity: 0;
  transform: translateY(8px);
  pointer-events: none;
  z-index: 2;
  transition: opacity var(--dur-base) var(--ease-out), transform var(--dur-base) var(--ease-out);
}
.project .media { box-shadow: var(--elev-1); }

@media (hover: hover) and (pointer: fine) {
  .project .media { transition: box-shadow var(--dur-base) var(--ease-out), transform var(--dur-base) var(--ease-out); }
  .project:hover .media,
  .project:focus-within .media { transform: translateY(-4px); box-shadow: var(--elev-3); }
  .project:hover .media::after,
  .project:focus-within .media::after { opacity: 1; transform: none; }
}

/* Imágenes interactivas (galerías con lightbox, tarjetas de servicios, guías y proyectos): zoom suave + «shine».
   Un solo valor y una sola curva para todas: --img-zoom, --img-zoom-dur y --ease-out.
   El zoom usa la propiedad scale de la <img> (el .media recorta con overflow y hereda el radio).
   El brillo es un ::before inclinado que cruza la foto de izquierda a derecha UNA vez con translateX
   (pointer-events: none; queda por debajo de las etiquetas, como «Ver proyecto», que van en ::after).
   Al salir se reinicia sin animación: no hay barrido de vuelta. No se aplica al hero, a logos ni a fondos. */
:root { --img-zoom: 1.06; --img-zoom-dur: 600ms; --img-shine-dur: 750ms; }

.gallery-item .media::before,
.project .media::before,
.card--media .card-media .media::before {
  content: "";
  position: absolute;
  inset: -10% auto -10% 0;
  width: 38%;
  background: linear-gradient(100deg, transparent 0%, rgba(255, 255, 255, .42) 50%, transparent 100%);
  transform: translateX(-135%) skewX(-20deg);
  z-index: 1; /* sobre la <img> (con scale forma su propio contexto) y bajo la etiqueta (z-index 2) */
  pointer-events: none;
}
.gallery-item .media img,
.project .media img,
.card--media .card-media .media img { transform-origin: 50% 50%; }

@media (hover: hover) and (pointer: fine) {
  .gallery-item .media img,
  .project .media img,
  .card--media .card-media .media img { transition: scale var(--img-zoom-dur) var(--ease-out); }

  .gallery-item:hover .media img,
  .project:hover .media img,
  .card--media:hover .card-media .media img { scale: var(--img-zoom); }

  /* En las tarjetas, el brillo espera 100 ms: el relleno carbón (320 ms) sube bajo la foto primero y no compiten */
  .gallery-item:hover .media::before,
  .project:hover .media::before,
  .card--media:hover .card-media .media::before {
    transform: translateX(310%) skewX(-20deg);
    transition: transform var(--img-shine-dur) var(--ease-in-out) 60ms;
  }
  .card--media:hover .card-media .media::before { transition-delay: 100ms; }
}

/* Lightbox: apertura ≤ 220 ms que nace en la miniatura pulsada (--origin lo fija motion.js);
   el cierre es inmediato y las flechas del teclado no animan. */
.lightbox[open] { animation: lb-fade var(--dur-base) var(--ease-out) both; }
.lightbox[open]::backdrop { animation: lb-fade var(--dur-base) var(--ease-out) both; }
.lightbox[open] .lightbox-figure { transform-origin: var(--origin, 50% 50%); animation: lb-in var(--dur-base) var(--ease-out) both; }
@keyframes lb-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes lb-in { from { opacity: 0; transform: scale(.94); } to { opacity: 1; transform: none; } }

@media (prefers-reduced-motion: reduce) {
  .reveal-ready [data-reveal]:not(.is-in) { transform: none; }
  .reveal-ready [data-reveal="left"]:not(.is-in),
  .reveal-ready [data-reveal="right"]:not(.is-in) { transform: none; }
  .reveal-ready [data-reveal="left"], .reveal-ready [data-reveal="right"] { transition-duration: 200ms; transition-delay: 0ms; }
  .reveal-ready [data-reveal="clip"]:not(.is-in) .media { clip-path: inset(0 round var(--radio)); }
  .reveal-ready [data-reveal="clip"]:not(.is-in) .project-body { transform: none; }
  .project:hover .media, .project:focus-within .media { transform: none; }
  .gallery-item .media::before, .project .media::before, .card--media .card-media .media::before { display: none; }
  .gallery-item:hover .media img, .project:hover .media img, .card--media:hover .card-media .media img { scale: none; }
  .lightbox[open] .lightbox-figure { animation-name: lb-fade; }
}

/* ==========================================================================
   4. ENCABEZADO Y MENÚ
   Todo el conjunto es fixed y .site-header reserva su altura completa (sin CLS). motion.js pone
   .is-scrolled pasados 80 px (escritorio) o 60 px (móvil), con histéresis. Entonces:
   - escritorio: la barra secundaria se pliega hacia arriba (transform + opacity) y la principal reduce
     su altura, sus íconos y el logo (transform solo sobre la imagen, nunca sobre texto);
   - móvil: la fila 2 (dirección y redes) se pliega y el logo se reduce con transform.
   El elemento oculto pasa a visibility:hidden al terminar para que sus enlaces no se tabulen.
   ========================================================================== */
.mh-sub {
  transition: transform var(--dur-base) var(--ease-out), opacity var(--dur-base) var(--ease-out), visibility 0s linear 0s;
}
.is-scrolled .mh-sub {
  transform: translateY(-100%);
  opacity: 0;
  visibility: hidden;
  transition: transform var(--dur-base) var(--ease-out), opacity var(--dur-base) var(--ease-out), visibility 0s linear var(--dur-base);
}
.mh-top { transition: box-shadow var(--dur-base) var(--ease-out); }
.is-scrolled .mh-top { box-shadow: 0 6px 24px rgba(42, 45, 46, .12); }
.mobile-header .brand-logo { transition: transform var(--dur-base) var(--ease-out); }
.is-scrolled .mobile-header .brand-logo { transform: scale(.85); }
.mh-social-link:active { transform: scale(.94); }
@media (hover: hover) and (pointer: fine) {
  a.mh-social-link:hover { color: var(--bronce-texto); }
}

@media (min-width: 1180px) {
  .banner {
    transition: height var(--dur-base) var(--ease-out), box-shadow var(--dur-base) var(--ease-out);
  }
  .is-scrolled .banner { box-shadow: 0 6px 24px rgba(42, 45, 46, .12); }
  .subnav {
    transition: transform var(--dur-base) var(--ease-out), opacity var(--dur-base) var(--ease-out), visibility 0s linear 0s;
  }
  .is-scrolled .subnav {
    transform: translateY(-100%);
    opacity: 0;
    visibility: hidden;
    transition: transform var(--dur-base) var(--ease-out), opacity var(--dur-base) var(--ease-out), visibility 0s linear var(--dur-base);
  }
  .icon-item svg, .icon-item .svc-icon { transition: width var(--dur-base) var(--ease-out), height var(--dur-base) var(--ease-out); }
  .logo-space .brand-logo { transition: transform var(--dur-base) var(--ease-out); }
  .is-scrolled .logo-space .brand-logo { transform: scale(.78); }

  /* Subrayado animado (transform) y estado activo, en ambas barras */
  .icon-item::after, .subitem::after {
    content: "";
    position: absolute;
    left: 14%;
    right: 14%;
    bottom: 0;
    height: 3px;
    border-radius: 3px 3px 0 0;
    background: var(--bronce);
    transform: scaleX(0);
    transform-origin: left center;
    transition: transform var(--dur-base) var(--ease-out);
  }
  .icon-item[aria-current="page"] { color: var(--bronce-texto); }
  /* Relleno carbón de abajo hacia arriba (scaleY, origen abajo): sale en 200 ms, entra en 300 ms */
  .icon-item { isolation: isolate; }
  .icon-item::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    background: #2E3238;
    transform: scaleY(0);
    transform-origin: bottom;
    transition: transform 200ms var(--ease-out);
    pointer-events: none;
  }
  .icon-item { transition: color 250ms var(--ease-out); }
  .icon-item:focus-visible { color: var(--hueso); }
  .icon-item:focus-visible::before { transform: scaleY(1); transition-duration: 300ms; }
  .subitem[aria-current="page"] { color: var(--bronce-claro); }
  .icon-item[aria-current="page"]::after, .subitem[aria-current="page"]::after { transform: scaleX(1); }
  .icon-item:focus-visible::after, .subitem:focus-visible::after { transform: scaleX(1); }
}
@media (min-width: 1180px) and (hover: hover) and (pointer: fine) {
  .icon-item:hover, .icon-item[aria-current="page"]:hover { color: var(--hueso); }
  .icon-item:hover::before { transform: scaleY(1); transition-duration: 300ms; }
  .icon-item:hover::after, .subitem:hover::after { transform: scaleX(1); }
  .icon-item:hover svg, .icon-item:focus-visible svg { color: var(--hueso); }
  .subitem { transition: color var(--dur-fast) ease; }
  .subitem:hover { color: var(--bronce-claro); }
}

/* Menú móvil: se abre con opacidad + 8 px desde la esquina de la hamburguesa (220 ms);
   los enlaces entran escalonados 30 ms. El cierre es inmediato (salida más rápida que la entrada). */
.mobile-menu:not([hidden]) {
  transform-origin: 100% 0;
  animation: menu-in var(--dur-base) var(--ease-out) both;
}
@keyframes menu-in {
  from { opacity: 0; transform: translateY(-8px) scale(.98); }
  to   { opacity: 1; transform: none; }
}
.mobile-menu:not([hidden]) .mobile-menu-nav > a {
  animation: menu-link var(--dur-base) var(--ease-out) both;
  animation-delay: calc(40ms + var(--n, 0) * 30ms);
}
@keyframes menu-link {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: none; }
}
.mobile-menu-nav > a[aria-current="page"] { color: var(--bronce-claro); }
.mobile-menu-nav > a:active { background: rgba(255, 255, 255, .06); }

/* Hamburguesa → X: tres líneas que giran sobre sí mismas al abrir el panel */
.menu-x path { stroke: currentColor; stroke-width: 2; stroke-linecap: round; fill: none; transform-box: fill-box; transform-origin: center; }
.mobile-menu:not([hidden]) .menu-x-1 { animation: x-1 var(--dur-base) var(--ease-out) both; }
.mobile-menu:not([hidden]) .menu-x-2 { animation: x-2 var(--dur-base) var(--ease-out) both; }
.mobile-menu:not([hidden]) .menu-x-3 { animation: x-3 var(--dur-base) var(--ease-out) both; }
@keyframes x-1 { from { transform: translateY(-5px); } to { transform: rotate(45deg); } }
@keyframes x-2 { from { opacity: 1; } to { opacity: 0; } }
@keyframes x-3 { from { transform: translateY(5px); } to { transform: rotate(-45deg); } }
.menu-x { width: 24px; height: 24px; }
.menu-x-1 { transform: rotate(45deg); }
.menu-x-2 { opacity: 0; }
.menu-x-3 { transform: rotate(-45deg); }

@media (prefers-reduced-motion: reduce) {
  .banner, .subnav, .mh-sub, .mh-top, .icon-item svg, .icon-item .svc-icon, .logo-space .brand-logo, .mobile-header .brand-logo { transition: none !important; }
  .is-scrolled .mh-sub, .is-scrolled .subnav { transform: none; }
  .mobile-menu:not([hidden]) { animation-name: lb-fade; }
  .mobile-menu:not([hidden]) .mobile-menu-nav > a { animation: none; }
  .mobile-menu:not([hidden]) .menu-x path { animation: none; }
}

/* ==========================================================================
   5. TRANSVERSAL: ritmo visual, elevación y tarjetas
   ========================================================================== */
:root {
  --sombra: var(--elev-2);
  --sombra-fuerte: var(--elev-3);
  --espacio-seccion: clamp(72px, 10vw, 136px);
}

/* Jerarquía tipográfica: escala fluida un punto más marcada en los títulos de sección */
.section-title h1, .section-title h2 { font-size: clamp(2rem, 3.4vw + .9rem, 3.4rem); line-height: 1.06; text-wrap: balance; }
.section-title p { line-height: 1.65; text-wrap: pretty; }
.section-title { margin-bottom: clamp(36px, 5.5vw, 64px); }

/* Divisor fino en bronce entre secciones contiguas del mismo fondo (sin ruido entre fondos distintos) */
.section { position: relative; }
.section--light + .section--light::before,
.section--tint + .section--tint::before {
  content: "";
  position: absolute;
  top: 0;
  left: 50%;
  width: min(var(--contenedor), calc(100% - var(--gutter) * 2));
  height: 1px;
  transform: translateX(-50%);
  background: linear-gradient(90deg, transparent, var(--bronce) 50%, transparent);
  opacity: .5;
}

/* Tarjetas: las curvas del proyecto y hover solo con puntero fino */
.card { transition: transform var(--dur-base) var(--ease-out), box-shadow var(--dur-base) var(--ease-out); box-shadow: var(--elev-1); }
.card::before { transition: transform 350ms var(--ease-out); }
.card:hover { transform: none; box-shadow: var(--elev-1); }
.card:hover::before { transform: scaleX(.22); }
@media (hover: hover) and (pointer: fine) {
  .card:hover { transform: translateY(-4px); box-shadow: var(--elev-2); }
  .card:hover::before { transform: scaleX(1); }
}
.card:active { transform: scale(.99); }
.section--dark .card { box-shadow: none; }

/* Relleno de abajo hacia arriba (tarjetas con enlace: servicios, guías, hub, relacionados).
   Un ::before con scaleY(0 → 1) y origen abajo; entra en 320 ms y baja en 200 ms (la salida es más rápida).
   Las tarjetas con foto rellenan solo lo que queda bajo la foto (que no se tapa); las simples, toda la tarjeta.
   El texto cambia de color en sincronía y queda SOBRE el relleno (z-index -1 dentro de un contexto aislado).
   Carbón #2E3238: título hueso 11,8:1, descripción hueso al 88 % 9,6:1, enlace --bronce-claro 5,75:1,
   caja del ícono bronce con ícono carbón 4,5:1. Cada tarjeta es un solo bloque clicable: el ::after del enlace la cubre. */
.card:has(.card-link) { isolation: isolate; }
.card:has(.card-link) .card-link::after { content: ""; position: absolute; inset: 0; z-index: 2; }
.card--media { container-type: inline-size; }

.card:has(.card-link)::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: #2E3238;
  transform: scaleY(0);
  transform-origin: bottom;
  transition: transform 200ms var(--ease-out);
  pointer-events: none;
}
.card:not(.card--media):has(.card-link)::after { inset: -1px; border-radius: var(--radio); }
/* Con foto 4:3 el relleno empieza justo bajo ella: su alto es el de la tarjeta menos 75 % del ancho */
.card--media:has(.card-link)::after { inset: 75cqw 0 0 0; }

.card:has(.card-link) :is(h3, h3 a, p, .card-link, .post-meta, svg) {
  transition: color 200ms var(--ease-out), background-color 200ms var(--ease-out);
}

/* Estado activo: teclado (siempre) y puntero fino con hover */
.card:has(.card-link:focus-visible)::after { transform: scaleY(1); transition-duration: 320ms; }
.card:has(.card-link:focus-visible) { outline: 3px solid var(--bronce); outline-offset: 3px; }
.card .card-link:focus-visible { outline: none; text-decoration: underline; text-underline-offset: 4px; }
@media (hover: hover) and (pointer: fine) {
  .card:has(.card-link):hover::after { transform: scaleY(1); transition-duration: 320ms; }
  .card:has(.card-link):hover { transform: translateY(-4px); box-shadow: var(--elev-2); }
}
@media (hover: hover) and (pointer: fine) {
  .card:has(.card-link):hover :is(h3, h3 a, p, .card-link, .post-meta, svg),
  .card:has(.card-link:focus-visible) :is(h3, h3 a, p, .card-link, .post-meta, svg) { transition-duration: 320ms; }
}

.card:has(.card-link:focus-visible) :is(h3, h3 a) { color: var(--hueso); }
.card:has(.card-link:focus-visible) p { color: rgba(247, 245, 241, .88); }
.card:has(.card-link:focus-visible) :is(.card-link, .post-meta) { color: var(--bronce-claro); }
.card:has(.card-link:focus-visible) svg { background: var(--bronce); color: var(--carbon); }
@media (hover: hover) and (pointer: fine) {
  .card:has(.card-link):hover :is(h3, h3 a) { color: var(--hueso); text-decoration: none; }
  .card:has(.card-link):hover p { color: rgba(247, 245, 241, .88); }
  .card:has(.card-link):hover :is(.card-link, .post-meta) { color: var(--bronce-claro); }
  .card:has(.card-link):hover svg { background: var(--bronce); color: var(--carbon); }
}

@media (prefers-reduced-motion: reduce) {
  .card:hover, .card:active { transform: none; }
  .card:has(.card-link)::after { transition: none; }
  .card:has(.card-link) :is(h3, h3 a, p, .card-link, .post-meta, svg) { transition: none; }
}

/* ==========================================================================
   6. DOCK FLOTANTE (tarjeta de WhatsApp + botón de WhatsApp + botón de llamar)
   Ajustes de tiempo (todo en este bloque):
   - entrada de los botones: `wa-in`, retraso 800 ms (la tarjeta se abre sola ~1,8 s después de cargar);
   - brillo del botón de WhatsApp y vibración del de llamar: periodo 6 s, 6 ciclos (≈ 36 s) y se detienen;
     el de llamar arranca 3 s después del brillo, así nunca coinciden (retrasos 2000 ms y 5000 ms);
   - tarjeta: 210 ms al abrir, 140 ms al cerrar.
   Para quitar los efectos: borrar los bloques «Brillo» y «Llamada entrante». Todo cuelga de .js (sin JS no se anima nada).
   Hover y :active usan las propiedades individuales translate y scale, que se componen con las animaciones de transform.
   ========================================================================== */
.js .wa-fab, .js .wa-call { animation: wa-in 260ms var(--ease-out) 800ms backwards; }
@keyframes wa-in {
  from { opacity: 0; transform: scale(.9); visibility: hidden; }
  to   { opacity: 1; transform: none; visibility: visible; }
}
.wa-dock { transition: opacity var(--dur-base) var(--ease-out), visibility 0s linear 0s; }
.wa-dock.is-away, body.menu-open .wa-dock, body.lightbox-open .wa-dock { transition: opacity var(--dur-base) var(--ease-out), visibility 0s linear var(--dur-base); }
.wa-fab:active, .wa-call:active { scale: .97; }
.wa-close:active { transform: scale(.94); }
@media (hover: hover) and (pointer: fine) {
  .wa-fab, .wa-call { transition: translate var(--dur-fast) var(--ease-out), scale var(--dur-fast) var(--ease-out), box-shadow var(--dur-base) var(--ease-out); }
  .wa-fab:hover, .wa-call:hover { translate: 0 -3px; box-shadow: 0 0 0 2px var(--blanco), var(--elev-3); }
  .wa-fab:active, .wa-call:active { translate: 0 0; scale: .97; }
  .wa-close:hover { background: rgba(255, 255, 255, .12); }
}
.wa-card:not([hidden]) { animation: wa-card-in 210ms var(--ease-out) both; }
.wa-card.is-closing { animation: wa-card-out 140ms var(--ease-out) both; }
@keyframes wa-card-in  { from { opacity: 0; transform: translateY(8px) scale(.92); } to { opacity: 1; transform: none; } }
@keyframes wa-card-out { from { opacity: 1; transform: none; } to { opacity: 0; transform: translateY(4px) scale(.96); } }

/* Brillo: un reflejo diagonal cruza el botón de WhatsApp (translateX, recortado por el círculo) */
.wa-fab { overflow: hidden; isolation: isolate; }
.wa-fab svg { position: relative; z-index: 1; }
.js .wa-fab::after {
  content: "";
  position: absolute;
  inset: -20% auto -20% 0;
  width: 40%;
  background: linear-gradient(90deg, transparent 0%, rgba(255, 255, 255, .6) 50%, transparent 100%);
  transform: translateX(-160%) skewX(-25deg);
  pointer-events: none;
  animation: wa-shine 6s linear 2000ms 6 both;
}
@keyframes wa-shine {
  0%    { transform: translateX(-160%) skewX(-25deg); animation-timing-function: var(--ease-in-out); }
  16.7% { transform: translateX(380%) skewX(-25deg); }
  100%  { transform: translateX(380%) skewX(-25deg); }
}

/* Llamada entrante: ráfaga de 0,7 s con giros alternados de ±11° y una onda que se expande y se desvanece */
.js .wa-call-icon { animation: wa-ring 6s linear 5000ms 6 both; }
.js .wa-call-icon::before {
  content: "";
  position: absolute;
  inset: 0;
  border: 2px solid var(--cta);
  border-radius: 14px;
  opacity: 0;
  pointer-events: none;
  animation: wa-wave 6s linear 5000ms 6 forwards;
}
/* Cinta «Llámanos»: reflejo discreto 0,7 s después de la vibración (WhatsApp brilla en 2 s; la píldora, en 5–6,7 s del ciclo: nunca coinciden) */
.wa-call-tag::after {
  content: "";
  position: absolute;
  inset: 0 auto 0 0;
  width: 45%;
  background: linear-gradient(90deg, transparent 0%, rgba(255, 255, 255, .65) 50%, transparent 100%);
  transform: translateX(-160%) skewX(-25deg);
  pointer-events: none;
  display: none;
}
.js .wa-call-tag::after { display: block; animation: wa-shine 6s linear 5700ms 6 both; }
@keyframes wa-ring {
  0%     { transform: none; }
  1.5%   { transform: rotate(-11deg) translateX(-1px); }
  3%     { transform: rotate(10deg) translateX(1px); }
  4.5%   { transform: rotate(-9deg) translateX(-1px); }
  6%     { transform: rotate(8deg) translateX(1px); }
  7.5%   { transform: rotate(-5deg); }
  9%     { transform: rotate(3deg); }
  10.5%  { transform: rotate(-1deg); }
  11.67%, 100% { transform: none; }
}
@keyframes wa-wave {
  0%   { opacity: .55; transform: scale(1); }
  14%  { opacity: 0; transform: scale(1.7); }
  100% { opacity: 0; transform: scale(1.7); }
}

/* Pausas: solo hover o foco sobre el propio botón y pestaña oculta (clase is-paused de whatsapp.js).
   NO se pausan con la tarjeta abierta: en escritorio se abre sola y queda abierta, y los efectos no llegarían a verse. */
.wa-fab:hover::after, .wa-fab:focus-visible::after,
.wa-call:hover .wa-call-icon, .wa-call:focus-visible .wa-call-icon, .wa-call:hover .wa-call-icon::before, .wa-call:focus-visible .wa-call-icon::before,
.wa-call:hover .wa-call-tag::after, .wa-call:focus-visible .wa-call-tag::after,
.wa-dock.is-paused .wa-fab::after, .wa-dock.is-paused .wa-call-icon, .wa-dock.is-paused .wa-call-icon::before, .wa-dock.is-paused .wa-call-tag::after { animation-play-state: paused; }

@media (prefers-reduced-motion: reduce) {
  .js .wa-fab, .js .wa-call { animation: hero-fade 200ms linear 800ms backwards; }
  .js .wa-fab::after, .js .wa-call-icon::before, .js .wa-call-tag::after { display: none; }
  .js .wa-call-icon { animation: none; }
  .wa-fab, .wa-call { transition: none; }
  .wa-fab:hover, .wa-call:hover, .wa-fab:active, .wa-call:active { translate: none; scale: none; }
  .wa-close:active { transform: none; }
  .wa-dock { transition: none !important; }
  .wa-card:not([hidden]) { animation: hero-fade 150ms linear both; }
  .wa-card.is-closing { animation: none; }
}

/* ==========================================================================
   7. CTA «Solicitar cotización» (.btn-cta): shimmer
   Banda de luz diagonal que cruza el botón con translateX (::after, periódico) y un barrido extra al hover/foco (::before).
   Ritmo: 1,2 s de barrido cada 5 s, 5 ciclos y se detiene. Los CTA que coinciden en pantalla se desfasan con --cta-delay
   (cabecera 2,6 s, resto 4,9 s: nunca brillan a la vez). Los de más abajo (.btn-cta--lazy) esperan a verse (motion.js).
   Se pausa con la pestaña oculta (html.tab-hidden) y se desactiva con reduced-motion.
   Para desactivar solo el shimmer: borrar ::after de este bloque; todo el efecto: quitar «btn-cta» de .btn-cta::before/::after.
   ========================================================================== */
.btn-cta::before, .btn-cta::after {
  content: "";
  position: absolute;
  inset: -20% auto -20% 0;
  z-index: -1; /* sobre el fondo del botón y bajo el texto */
  width: 38%;
  background: linear-gradient(100deg, transparent 0%, rgba(255, 255, 255, .55) 50%, transparent 100%);
  transform: translateX(-150%) skewX(-22deg);
  pointer-events: none;
}
.btn-cta::after { animation: cta-shimmer 5s linear var(--cta-delay, 4.9s) 5 both; }
.header-cta, .mh-cta { --cta-delay: 2.6s; }
.mobile-menu-actions .btn-cta { --cta-delay: 3.6s; }
@keyframes cta-shimmer {
  0%    { transform: translateX(-150%) skewX(-22deg); animation-timing-function: var(--ease-in-out); }
  24%   { transform: translateX(340%) skewX(-22deg); }
  100%  { transform: translateX(340%) skewX(-22deg); }
}
.btn-cta--lazy::after { animation-play-state: paused; }
.btn-cta--lazy.is-seen::after { animation-play-state: running; }
.tab-hidden .btn-cta::after, .tab-hidden .btn-cta--lazy.is-seen::after { animation-play-state: paused; }

/* Barrido extra al hover (solo puntero fino) y al foco con teclado; al salir se reinicia sin animar */
.btn-cta:focus-visible::before { transform: translateX(340%) skewX(-22deg); transition: transform 700ms var(--ease-in-out); }
@media (hover: hover) and (pointer: fine) {
  .btn-cta:hover::before { transform: translateX(340%) skewX(-22deg); transition: transform 700ms var(--ease-in-out); }
}
@media (prefers-reduced-motion: reduce) {
  .btn-cta::before, .btn-cta::after { display: none; }
}

/* ==========================================================================
   7. ÍCONOS 3D DE SERVICIOS (partial service-icon)
   Hover/foco: sobre el relleno carbón el ícono pasa a una versión blanca CON relieve (grayscale + brightness
   + contrast; nunca silueta plana), sube 3 px, crece 10 % y gira 2,5°, con la sombra de contacto más marcada.
   Filtro y transform duran 250 ms, sincronizados con el relleno. :active lo aprieta al 97 %.
   Con prefers-reduced-motion el filtro cambia al instante y no hay desplazamiento, giro ni escala.
   ========================================================================== */
.svc-icon img {
  --svc-shadow: drop-shadow(0 2px 3px rgba(42, 45, 46, .22));
  filter: grayscale(0) brightness(1) contrast(1) var(--svc-shadow);
  transition: transform 250ms var(--ease-out), filter 250ms var(--ease-out);
}
.icon-item:focus-visible .svc-icon img,
.card:has(.card-link:focus-visible) .svc-icon img,
.mobile-menu-nav > a:focus-visible .svc-icon img,
.mobile-menu-nav > a:active .svc-icon img {
  --svc-shadow: drop-shadow(0 7px 5px rgba(0, 0, 0, .45));
  filter: grayscale(1) brightness(1.9) contrast(.85) var(--svc-shadow);
  transform: translateY(-3px) scale(1.1) rotate(2.5deg);
}
@media (hover: hover) and (pointer: fine) {
  .icon-item:hover .svc-icon img,
  .card:has(.card-link):hover .svc-icon img,
  .mobile-menu-nav > a:hover .svc-icon img {
    --svc-shadow: drop-shadow(0 7px 5px rgba(0, 0, 0, .45));
    filter: grayscale(1) brightness(1.9) contrast(.85) var(--svc-shadow);
    transform: translateY(-3px) scale(1.1) rotate(2.5deg);
  }
}
.icon-item:active .svc-icon img,
.card:has(.card-link):active .svc-icon img { transform: scale(.97); }
.mobile-menu-nav > a:active .svc-icon img { transform: translateY(-3px) scale(1.1) rotate(2.5deg) scale(.97); }

@media (prefers-reduced-motion: reduce) {
  .svc-icon img { transition: none; }
  .icon-item:hover .svc-icon img, .icon-item:focus-visible .svc-icon img,
  .card:has(.card-link):hover .svc-icon img, .card:has(.card-link:focus-visible) .svc-icon img,
  .mobile-menu-nav > a:hover .svc-icon img, .mobile-menu-nav > a:focus-visible .svc-icon img,
  .icon-item:active .svc-icon img, .card:has(.card-link):active .svc-icon img, .mobile-menu-nav > a:active .svc-icon img { transform: none; }
  .icon-item, .icon-item::before { transition: none; }
}

/* ==========================================================================
   HERO CARRUSEL (portada). Fundido del fondo (600 ms) y, al activarse la diapositiva, el panel, la foto enmarcada
   y el texto entran con translate + opacity y un escalonado corto: todo termina en ≤ 700 ms.
   La diapositiva 1 está activa desde el HTML, así que al cargar no hay transición (el LCP no se anima).
   Solo transform y opacity. Desactivar: quitar este bloque (las diapositivas cambian sin animar).
   Con prefers-reduced-motion: sin movimiento y fundido de 200 ms.
   ========================================================================== */
.js .hc-slide { transition: opacity 600ms var(--ease-out), visibility 0s linear 0s; }
.js .hc-slide:not(.is-active) {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 600ms var(--ease-out), visibility 0s linear 600ms;
}
.js .hc-panel, .js .hc-photo, .js .hc-text > * {
  transition: opacity 450ms var(--ease-out), transform 450ms var(--ease-out);
}
.js .hc-slide:not(.is-active) .hc-panel { opacity: 0; transform: translateY(16px); transition-duration: 200ms; }
.js .hc-slide:not(.is-active) .hc-photo { opacity: 0; transform: translateX(-24px); transition-duration: 200ms; }
.js .hc-slide:not(.is-active) .hc-text > * { opacity: 0; transform: translateX(20px); transition-duration: 200ms; }
.js .hc-slide.is-active .hc-photo { transition-delay: 60ms; }
.js .hc-slide.is-active .hc-text > :nth-child(1) { transition-delay: 100ms; }
.js .hc-slide.is-active .hc-text > :nth-child(2) { transition-delay: 150ms; }
.js .hc-slide.is-active .hc-text > :nth-child(3) { transition-delay: 200ms; }
.js .hc-slide.is-active .hc-text > :nth-child(4) { transition-delay: 250ms; }
.hc-btn { transition: background-color var(--dur-base) var(--ease-out), color var(--dur-base) var(--ease-out), scale var(--dur-fast) var(--ease-out); }
.hc-btn:active { scale: .94; }
.hc-dot::before { transition: background-color var(--dur-base) var(--ease-out), border-color var(--dur-base) var(--ease-out); }

@media (prefers-reduced-motion: reduce) {
  .js .hc-slide, .js .hc-slide:not(.is-active) { transition-duration: 200ms, 0s; transition-delay: 0s, 200ms; }
  .js .hc-panel, .js .hc-photo, .js .hc-text > * { transition: none; }
  .js .hc-slide:not(.is-active) .hc-panel,
  .js .hc-slide:not(.is-active) .hc-photo,
  .js .hc-slide:not(.is-active) .hc-text > * { transform: none; }
  .hc-btn, .hc-dot::before { transition: none; }
  .hc-btn:active { scale: none; }
}

/* ==========================================================================
   BARRA SOCIAL (solo escritorio ≥ 1280 px). Entra una vez desde el borde derecho (transform + opacity); el hover solo con
   puntero fino. Desactivar: borrar este bloque. Con prefers-reduced-motion no hay movimiento.
   ========================================================================== */
@media (min-width: 1280px) and (min-height: 560px) {
  .js .social-bar { animation: social-in 500ms var(--ease-out) 900ms both; }
  .social-bar-link { transition: background-color var(--dur-base) var(--ease-out), color var(--dur-base) var(--ease-out); }
  .social-bar-link:active { scale: .92; }
}
@keyframes social-in {
  from { opacity: 0; transform: translateX(100%); }
  to   { opacity: 1; transform: none; }
}
@media (hover: hover) and (pointer: fine) {
  a.social-bar-link:hover { background: var(--bronce); color: var(--carbon); }
}
@media (prefers-reduced-motion: reduce) {
  .js .social-bar { animation: none; }
  .social-bar-link { transition: none; }
  .social-bar-link:active { scale: none; }
}

/* Mosaico de proyectos de la portada: zoom suave de la foto solo con puntero fino (mismos --img-zoom y --ease-out) */
.tile .media img { transition: scale var(--img-zoom-dur, 500ms) var(--ease-out); }
@media (hover: hover) and (pointer: fine) {
  .tile-link:hover .media img { scale: var(--img-zoom, 1.05); }
}
@media (prefers-reduced-motion: reduce) {
  .tile .media img { transition: none; }
  .tile-link:hover .media img { scale: none; }
}
