/* ============================================================
   styles.css — Estilos base (mobile-first)
   ============================================================ */
body {
  font-family: var(--font-body);
  font-size: var(--fs-base);
  color: var(--color-text);
  background-color: var(--color-bg);
}

h1, h2, h3, h4, h5, h6 {
  font-family: var(--font-heading);
}

.container {
  width: 100%;
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--container-pad);
}

/* Compensa el header sticky al saltar a un ancla (#seccion). */
section[id] {
  scroll-margin-top: calc(var(--header-h) + var(--space-sm));
}

/* ---------- Revelado al hacer scroll ---------- */
/* Estado inicial solo cuando hay JS (sin JS, el contenido se ve normal). */
.js .reveal {
  opacity: 0;
  transform: translateY(20px);
  transition: opacity 0.5s ease-out, transform 0.5s ease-out;
}
.js .reveal.is-visible {
  opacity: 1;
  transform: none;
}
@media (prefers-reduced-motion: reduce) {
  .js .reveal {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

/* ---------- Botones ---------- */
.btn {
  display: inline-block;
  padding: 0.75rem 1.5rem;
  border-radius: var(--radius-sm);
  font-weight: 600;
  text-align: center;
  transition: background-color var(--transition), transform var(--transition), color var(--transition);
}

.btn--primary {
  background-color: var(--color-accent);
  color: var(--color-white);
}
.btn--primary:hover {
  background-color: var(--color-accent-dark);
  transform: translateY(-2px);
}

.btn--ghost {
  border: 2px solid var(--color-white);
  color: var(--color-white);
}
.btn--ghost:hover {
  background-color: rgba(255, 255, 255, 0.14);
  color: var(--color-white);
}

/* ---------- Header / Nav ---------- */
.header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 1000;
  height: var(--header-h);
  /* Estado superior (sobre el hero): blanco translúcido como respaldo del nav.
     Fallback más opaco para navegadores sin backdrop-filter (sin blur que ayude). */
  background-color: rgba(255, 255, 255, 0.9);
  border-bottom: 1px solid var(--color-border);
  box-shadow: var(--shadow-sm);
  transition: background-color var(--transition), box-shadow var(--transition),
              border-color var(--transition), transform var(--transition);
}
/* Con soporte de backdrop-filter: más translúcido y más blur (deja ver más la foto). */
@supports ((-webkit-backdrop-filter: blur(12px)) or (backdrop-filter: blur(12px))) {
  .header {
    background-color: rgba(255, 255, 255, 0.62);
    -webkit-backdrop-filter: blur(12px);
    backdrop-filter: blur(12px);
  }
}
/* Estado sólido: al hacer scroll o con el menú móvil abierto (blanco opaco). */
.header--scrolled,
.header--menu-open {
  background-color: var(--color-white);
  border-bottom-color: var(--color-border);
  box-shadow: var(--shadow-sm);
}
/* Ocultar al bajar (estado sólido). */
.header--hidden {
  transform: translateY(-100%);
}
/* El menú abierto mantiene el header visible aunque estés arriba. Además
   quita el backdrop-filter (invisible igual sobre el fondo ya opaco de
   arriba): con él activo, .header pasa a ser el "containing block" del
   panel .nav__menu (position:fixed anidado dentro de .header), y su inset
   se calcularía relativo a los 72px del header en vez de al viewport. */
.header--menu-open {
  transform: none;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}
/* Con el panel de menú móvil abierto, la página detrás no debe scrollear. */
body.nav-open {
  overflow: hidden;
}

.header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 100%;
}

.header__logo {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  color: var(--color-text);
}
.header__logo-img {
  /* Wordmark ancho (~3.7:1, no cuadrado): en vez de ligar la altura al
     header (eso lo dejaba en 48px = 176px de ancho y no cabía junto al
     texto en móviles angostos), se fija una altura menor pensada para
     el espacio real disponible en este breakpoint; ancho proporcional. */
  height: 28px;
  width: auto;
}
.header__logo-text {
  font-family: var(--font-heading);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  /* Reducido junto con el logo (ver .header__logo-img) para que el
     conjunto logo + texto quepa sin solaparse con el botón hamburguesa
     en móviles angostos (probado desde 320px). */
  font-size: 0.78rem;
  line-height: 1;
  /* Oscuro en ambos estados (transparente y sólido). */
  color: var(--color-text);
}
.header__logo-suffix {
  margin-left: 0.35em;
  font-size: 0.7em;
  font-weight: 500;
  letter-spacing: 0.04em;
  opacity: 0.85;
}

/* El botón queda por encima del panel (.nav__menu) una vez abierto: aunque
   ambos son descendientes de .header, .nav__menu tiene mayor z-index LOCAL,
   así que sin esto el panel taparía el propio botón que lo cierra. */
.nav__toggle {
  position: relative;
  z-index: 2;
  display: flex;
  flex-direction: column;
  gap: 5px;
  padding: 0.5rem;
}
.nav__toggle span {
  width: 26px;
  height: 3px;
  /* Oscuras siempre: el panel .nav__menu arranca debajo del header (que
     durante la apertura pasa a fondo blanco sólido vía .header--menu-open),
     así que el botón nunca queda sobre el fondo oscuro del panel. */
  background-color: var(--color-text);
  border-radius: 2px;
  transition: transform 0.3s ease, opacity 0.3s ease;
}
/* Hamburguesa → X: la línea 1 baja y rota, la 2 se desvanece, la 3 sube y
   rota en sentido contrario. 8px = alto de línea (3px) + gap (5px), la
   distancia exacta para que 1 y 3 terminen sobre la posición de la 2. */
.nav__toggle[aria-expanded="true"] span:nth-child(1) {
  transform: translateY(8px) rotate(45deg);
}
.nav__toggle[aria-expanded="true"] span:nth-child(2) {
  opacity: 0;
}
.nav__toggle[aria-expanded="true"] span:nth-child(3) {
  transform: translateY(-8px) rotate(-45deg);
}

/* ---------- Menú móvil: panel a pantalla completa con entrada escalonada ----------
   Vive por debajo del header (inset arranca en --header-h) para no tapar el
   botón de cerrar sin necesidad de trucos de z-index adicionales. Oculto vía
   opacity+visibility (no display) para poder animar tanto la entrada como la
   salida; visibility se retrasa al cerrar para que el panel siga siendo
   interactivo mientras se desvanece, e instantáneo al abrir. */
.nav__menu {
  position: fixed;
  inset: var(--header-h) 0 0 0;
  z-index: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--space-sm);
  background-color: var(--color-primary-dark);
  padding: var(--space-xl) var(--container-pad);
  overflow-y: auto;
  opacity: 0;
  visibility: hidden;
  transition: opacity 300ms ease, visibility 0s linear 300ms;
}
.nav__menu.is-open {
  opacity: 1;
  visibility: visible;
  transition: opacity 300ms ease, visibility 0s linear;
}

/* Ítem grande con número de orden en superíndice a la derecha (patrón
   "staggered menu"). Estado inicial oculto/desplazado; el panel abierto los
   revela con transition-delay incremental por ítem (ver nth-child abajo). */
.nav__link {
  position: relative;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-sm);
  padding: 0.6rem 0;
  color: var(--color-white);
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: clamp(1.75rem, 9vw, 2.25rem);
  line-height: 1.15;
  opacity: 0;
  transform: translateY(14px);
  transition: color var(--transition), opacity 350ms ease-out, transform 350ms ease-out;
}
.nav__menu.is-open .nav__link {
  opacity: 1;
  transform: translateY(0);
}
/* Stagger: ~60ms entre ítem e ítem, solo mientras el panel está abierto. */
.nav__menu.is-open li:nth-child(1) .nav__link { transition-delay: 60ms; }
.nav__menu.is-open li:nth-child(2) .nav__link { transition-delay: 120ms; }
.nav__menu.is-open li:nth-child(3) .nav__link { transition-delay: 180ms; }
.nav__menu.is-open li:nth-child(4) .nav__link { transition-delay: 240ms; }
.nav__menu.is-open li:nth-child(5) .nav__link { transition-delay: 300ms; }
.nav__menu.is-open li:nth-child(6) .nav__link { transition-delay: 360ms; }

.nav__link-num {
  flex-shrink: 0;
  margin-top: 0.3em;
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: var(--fs-sm);
  color: var(--color-accent);
}

/* Subrayado rojo animado: crece de 0 a 100% del ancho al hover/foco. */
.nav__link::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  height: 2px;
  background-color: var(--color-accent);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform var(--transition);
}
.nav__link:hover::after,
.nav__link:focus-visible::after {
  transform: scaleX(1);
}
/* El CTA "Contacto" es un botón en escritorio: sin subrayado (en el panel
   móvil ya no lleva estilo de botón, ver responsive.css). */
.nav__link--cta::after {
  display: none;
}
/* reduce-motion: el subrayado aparece pero sin animar el crecimiento. */
@media (prefers-reduced-motion: reduce) {
  .nav__link::after {
    transition: none;
  }
}

/* Reduced motion: el panel y sus ítems aparecen/desaparecen sin animación
   (mismo criterio que .js .reveal). El botón hamburguesa también pierde su
   transición de rotación. */
@media (prefers-reduced-motion: reduce) {
  .nav__menu {
    transition: none;
  }
  .nav__link {
    transition: color var(--transition);
    opacity: 1;
    transform: none;
  }
  .nav__toggle span {
    transition: none;
  }
}

/* Reduced motion: no ocultar el header con transform. */
@media (prefers-reduced-motion: reduce) {
  .header--hidden {
    transform: none;
  }
}

/* ---------- Hero ---------- */
.hero {
  position: relative;
  display: flex;
  align-items: center;
  min-height: 88vh;
  min-height: 88svh;
  padding-block: var(--space-2xl);
  overflow: hidden;
  color: var(--color-white);
}
/* Imagen de fondo a pantalla (LCP, no lazy). Más alta que el hero y con margen
   superior para permitir el recorrido del parallax sin dejar bordes vacíos. */
.hero__img {
  position: absolute;
  left: 0;
  top: -20%;
  width: 100%;
  height: 140%;
  object-fit: cover;
  object-position: center;
  z-index: 0;
  will-change: transform;
}
/* Overlay VERTICAL: solo oscuro. La parte alta queda sobria (el header lleva su propio
   respaldo "frosted") y se densifica en el centro/inferior para el titular blanco. */
.hero::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  background-image: linear-gradient(
    to bottom,
    rgba(10, 10, 10, 0.32) 0%,
    rgba(10, 10, 10, 0.34) 14%,
    rgba(10, 10, 10, 0.44) 26%,
    rgba(10, 10, 10, 0.56) 40%,
    rgba(10, 10, 10, 0.62) 58%,
    rgba(10, 10, 10, 0.66) 100%
  );
  pointer-events: none;
}
/* Overlay HORIZONTAL: respaldo oscuro en la zona del texto (izquierda/centro),
   transparente hacia la derecha. Arranca bajo la franja del header (~12%) para no
   oscurecer el logo a color ni el nav oscuros. */
.hero::before {
  content: "";
  position: absolute;
  inset: 12% 0 0 0;
  z-index: 1;
  background-image: linear-gradient(
    to right,
    rgba(10, 10, 10, 0.55) 0%,
    rgba(10, 10, 10, 0.34) 30%,
    rgba(10, 10, 10, 0) 62%
  );
  pointer-events: none;
}
.hero__inner {
  position: relative;
  z-index: 2;
}
.hero__content {
  max-width: 720px;
}
/* Fondo propio semi-opaco (no solo el overlay general del Hero): garantiza
   el contraste mínimo del texto pase lo que pase detrás en la foto — el
   overlay del Hero por sí solo no es confiable ahí (varía según el
   recorte/foto). rgba(10,10,10,.7) da ≥4.5:1 incluso sobre un píxel
   blanco puro de fondo (peor caso matemático), verificado por muestreo.
   Radio pequeño (no 999px/pill): con texto largo que envuelve a 2-3
   líneas en mobile, un radio grande recorta triángulos de esquina donde
   el fondo semi-opaco no cubre nada — dejando pasar la foto sin filtrar
   justo ahí. Con --radius-sm esas esquinas son insignificantes. */
.hero__eyebrow {
  display: inline-flex;
  align-items: center;
  gap: var(--space-sm);
  padding: 0.3rem 0.6rem;
  border-radius: var(--radius-sm);
  background-color: rgba(10, 10, 10, 0.7);
  text-transform: uppercase;
  letter-spacing: 2px;
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--color-white);
  margin-bottom: var(--space-md);
}
/* Acento rojo de marca (decorativo; el texto se mantiene blanco para AA). */
.hero__eyebrow::before {
  content: "";
  width: 36px;
  height: 2px;
  background-color: var(--color-accent);
}
.hero__title {
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: clamp(2.5rem, 6vw, 4.75rem);
  line-height: 1.05;
  margin-bottom: var(--space-md);
}
.hero__text {
  font-size: var(--fs-md);
  max-width: 54ch;
  margin-bottom: var(--space-lg);
  color: rgba(255, 255, 255, 0.92);
}
/* Móvil: botones apilados a ancho completo. */
.hero__actions {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}
.hero__actions .btn {
  width: 100%;
}

/* ---------- Secciones genéricas ---------- */
.section {
  padding-block: var(--space-2xl);
}
.section--alt {
  background-color: var(--color-bg-alt);
}
/* Encabezado de sección: alineado a la IZQUIERDA en todo el sitio. */
.section__head {
  max-width: 640px;
  margin: 0 0 var(--space-xl);
}
/* Kicker numerado: fila número rojo + línea roja + etiqueta gris (patrón único
   para las 5 secciones; el hero no lleva número). */
.section__kicker {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: var(--space-sm);
}
.section__kicker-num {
  font-family: var(--font-heading);
  font-size: 0.8125rem;      /* ~13px */
  font-weight: 700;
  letter-spacing: 1px;
  line-height: 1;
  color: var(--color-accent);
}
.section__kicker-line {
  flex: 0 0 26px;
  width: 26px;
  height: 1.5px;
  background-color: var(--color-accent);
}
.section__kicker-label {
  font-family: var(--font-heading);
  font-size: 0.8125rem;      /* ~13px */
  font-weight: 600;          /* resuelve al Archivo 700 autoalojado */
  letter-spacing: 3px;
  text-transform: uppercase;
  line-height: 1.4;
  color: var(--color-text-muted);
}
.section__title {
  font-size: var(--fs-xl);
  color: var(--color-primary);
}
.section__subtitle {
  margin-top: var(--space-sm);
  color: var(--color-text-muted);
  font-size: var(--fs-md);
}

/* ---------- Nosotros ---------- */
/* Envoltorio de la transición Nosotros → Servicios (ver responsive.css para
   la mecánica sticky/scale, activa solo en ≥1024px vía la clase
   "pin-active"). El fondo oscuro es el MISMO que el de Servicios
   (--color-primary-dark): la costura entre ambos es imperceptible porque es
   literalmente el mismo color, no una imitación. Sin "pin-active" (móvil,
   reduced-motion o puntero táctil) es un <div> inerte: Nosotros lo cubre
   por completo y no hay ninguna diferencia visual con el diseño original. */
.transition-stage {
  position: relative;
  background-color: var(--color-primary-dark);
}
/* Nosotros nunca tuvo fondo propio (heredaba el blanco de <body>, ya que
   .section no lo define). Ahora que detrás hay un fondo oscuro, hace falta
   que lo declare explícitamente: si no, el oscuro se filtraría a través de
   ella incluso en reposo (progreso 0), rompiendo el "sin ninguna diferencia
   visual antes de la transición". Sin efecto en ningún otro breakpoint/modo. */
.transition-stage__panel {
  background-color: var(--color-bg);
}

/* Bloque superior: foto + enunciado (apilado en móvil; 2 columnas en ≥768px). */
.about__intro {
  display: grid;
  /* minmax(0, 1fr): mismo criterio que .contact (ver más abajo) — sin
     esto, el track de este grid puede forzarse por encima del ancho
     disponible y desbordar hacia el documento en mobile angosto. */
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-lg);
  margin-bottom: var(--space-xl);
}

/* Columna A: contenedor relativo (foto + tarjeta de cifras flotante). */
.about__media {
  position: relative;
}
/* Foto con proporción fija 4:3 (sin saltos de layout). */
.about__photo {
  margin: 0;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  border-radius: var(--radius-md);
  background-color: var(--color-primary-dark);
  box-shadow: var(--shadow-sm);
}
.about__img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Columna B: eyebrow + enunciado grande + reseña. */
.about__lead {
  display: flex;
  flex-direction: column;
  justify-content: center;
}
.about__statement {
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: clamp(1.5rem, 2.6vw, 2.1rem);
  line-height: 1.2;
  color: var(--color-primary);
  margin-bottom: var(--space-md);
}
/* Eslogan corto: --fs-sm (14px) es el token más cercano a los ~15px del
   mockup — empate exacto con --fs-base (16px), se elige --fs-sm porque es
   el mismo tamaño que ya usa .about__meta, la otra línea corta/acento de
   este bloque. margin-bottom: var(--space-md), el mismo valor que ya usa
   .about__statement para separarse del párrafo siguiente (ver arriba), así
   el ritmo vertical del bloque no cambia: mismo espacio antes y después. */
.about__tagline {
  color: var(--color-accent);
  font-weight: 700;
  font-style: italic;
  font-size: var(--fs-sm);
  border-left: 3px solid var(--color-accent);
  padding-left: 12px;
  margin-bottom: var(--space-md);
}
.about__text {
  font-size: var(--fs-md);
  color: var(--color-text-muted);
}
/* Datos secundarios (los 2 stats que no van en la tarjeta-acento). */
.about__meta {
  margin-top: var(--space-md);
  font-size: var(--fs-sm);
  color: var(--color-text-muted);
}

/* Tarjeta-acento ROJA compacta (2 cifras). Móvil: solapa el pie de la foto.
   ≥768: flota pequeña sobre la esquina inferior izquierda (ver responsive.css). */
.about__stats {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  margin-top: calc(-1 * var(--space-lg));   /* solapa el borde inferior de la foto */
  margin-inline: var(--space-md);            /* flota más estrecha que la foto */
  padding: var(--space-sm) var(--space-md);
  background-color: var(--color-accent);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
}
.stat {
  padding: var(--space-xs) var(--space-md);
}
/* Divisor sutil entre las 2 celdas. */
.stat + .stat {
  border-left: 1px solid rgba(255, 255, 255, 0.18);
}
.stat__num {
  display: block;
  font-family: var(--font-heading);
  font-size: clamp(1.5rem, 3vw, 2rem);
  font-weight: 800;
  line-height: 1;
  color: var(--color-white);
}
.stat__label {
  display: block;
  margin-top: var(--space-xs);
  font-size: var(--fs-sm);
  color: rgba(255, 255, 255, 0.9);
}

/* ---------- Servicios (zona oscura) ---------- */
/* Sección oscura con filete rojo de entrada (alterna con Nosotros, claro). */
.section--dark {
  background-color: var(--color-primary-dark);
  border-top: 4px solid var(--color-accent);
}
/* Encabezado on-dark (scoped: no afecta otras secciones). */
.section--dark .section__kicker-num {
  color: var(--color-secondary);   /* rojo más claro: legible sobre #1A1A1A */
}
.section--dark .section__kicker-label {
  color: var(--color-text-on-dark-muted);   /* gris legible sobre #1A1A1A */
}
.section--dark .section__title {
  color: var(--color-text-on-dark);
}
.section--dark .section__subtitle {
  color: var(--color-text-on-dark-muted);
}
.section--dark .fleet-list__title {
  color: var(--color-text-on-dark);
}
.section--dark .fleet-list__text {
  color: var(--color-text-on-dark-muted);
}

/* Microtransición Servicios → Experiencia: velo degradado dentro del propio
   padding inferior de #servicios (no añade alto ni toca el flujo) que
   anticipa el tono claro de Experiencia antes del corte, suavizando el salto
   oscuro/claro. Scoped a #servicios (no a .section--dark) para no afectar la
   franja de Equipos, que comparte esa clase. */
#servicios {
  position: relative;
}
#servicios::after {
  content: "";
  position: absolute;
  inset: auto 0 0 0;
  height: 12px;
  background: linear-gradient(to bottom, transparent, rgba(245, 245, 245, 0.14));
  pointer-events: none;
}

.services {
  display: grid;
  gap: var(--space-md);
}
.service-card {
  position: relative;
  overflow: hidden;                                /* la foto/placeholder respeta el radio */
  background-color: var(--color-primary);          /* superficie #2B2B2B */
  border: 1px solid var(--color-border-dark);      /* borde sutil para elevación */
  border-radius: var(--radius-md);
  transition: transform var(--transition), box-shadow var(--transition),
              border-color var(--transition);
}
.service-card:hover {
  transform: translateY(-6px);
  box-shadow: var(--shadow-md);
  border-color: var(--color-accent);
}
/* Área de foto 4:3: contiene el carrusel (.service-card__track/__slide) o, si una
   carpeta quedara vacía, el placeholder centrado (__media-icon + __soon). */
.service-card__media {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 4 / 3;
  background-color: var(--color-surface-dark);
}
.service-card__media-icon {
  color: var(--color-text-on-dark);
  opacity: 0.22;                                   /* ícono de fondo tenue */
}
.service-card__media-icon svg {
  width: 64px;
  height: 64px;
}
.service-card__soon {
  position: absolute;
  bottom: var(--space-sm);
  left: 50%;
  transform: translateX(-50%);
  text-transform: uppercase;
  letter-spacing: 1px;
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--color-text-on-dark-muted);
}
/* Contenido bajo la foto. */
.service-card__body {
  position: relative;
  padding: var(--space-lg) var(--space-md) var(--space-md);
}
/* Filete rojo corto en el límite foto/contenido (acento de marca; crece en hover). */
.service-card__body::before {
  content: "";
  position: absolute;
  top: 0;
  left: var(--space-md);
  width: 40px;
  height: 4px;
  background-color: var(--color-accent);
  transition: width var(--transition);
}
.service-card:hover .service-card__body::before {
  width: 64px;
}
/* Ícono de línea pequeño: claro en reposo, rojo en hover de la tarjeta. */
.service-card__icon {
  display: block;
  color: var(--color-text-on-dark);
  margin-bottom: var(--space-sm);
  transition: color var(--transition);
}
.service-card__icon svg {
  width: 32px;
  height: 32px;
}
.service-card:hover .service-card__icon {
  color: var(--color-accent);
}
.service-card__title {
  font-family: var(--font-heading);
  color: var(--color-text-on-dark);
  margin-bottom: var(--space-xs);
  font-size: var(--fs-md);
}
.service-card__text {
  color: var(--color-text-on-dark-muted);
  font-size: var(--fs-sm);
}

/* ---------- Carrusel de fotos por servicio ---------- */
/* Cada .service-card__media con 2+ fotos se activa como carrusel (JS):
   las diapositivas se apilan en absoluto y se funden; los controles se
   inyectan por JS. Con 1 foto queda imagen fija sin controles. Las
   diapositivas van en position:absolute, así que no afectan al centrado
   flex que conserva el placeholder "Próximamente" si una carpeta está vacía. */
.service-card__track {
  position: absolute;
  inset: 0;
}
.service-card__slide {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 0.6s ease;
}
.service-card__slide.is-active {
  opacity: 1;
}

/* Scrim: degradado sutil en la base de la foto para que los puntos se lean
   sobre cualquier imagen. Sobre las diapositivas, bajo puntos y flechas. */
.service-card__track::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 42%;
  z-index: 1;
  background-image: linear-gradient(to top, rgba(0, 0, 0, 0.55), rgba(0, 0, 0, 0));
  pointer-events: none;
}

/* Flechas prev/next: sobrias, con fondo oscuro translúcido para contraste
   AA sobre la foto; rojo de marca en hover. En escritorio quedan ocultas
   (opacity, no display: conservan su sitio y no hay salto de layout) hasta
   hacer hover en la tarjeta o enfocarlas con teclado. */
.service-card__nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  padding: 0;
  color: var(--color-white);
  background-color: rgba(26, 26, 26, 0.72);   /* AA del chevron blanco aun sobre foto clara */
  border: none;
  border-radius: 50%;
  cursor: pointer;
  opacity: 0;
  pointer-events: none;
  transition: background-color var(--transition), opacity var(--transition);
}
.service-card:hover .service-card__nav,
.service-card__nav:focus-visible {
  opacity: 1;
  pointer-events: auto;
}
/* Táctil: sin hover fiable, las flechas quedan siempre visibles. */
@media (hover: none), (pointer: coarse) {
  .service-card__nav {
    opacity: 1;
    pointer-events: auto;
  }
}
.service-card__nav:hover {
  background-color: var(--color-accent);
}
.service-card__nav:focus-visible {
  outline: 2px solid var(--color-white);
  outline-offset: 2px;
}
.service-card__nav svg {
  width: 20px;
  height: 20px;
}
.service-card__nav--prev {
  left: var(--space-sm);
}
.service-card__nav--next {
  right: var(--space-sm);
}

/* Puntos indicadores: activo en rojo de marca. */
.service-card__dots {
  position: absolute;
  left: 0;
  right: 0;
  bottom: var(--space-sm);
  z-index: 2;
  display: flex;
  justify-content: center;
  gap: 8px;
}
.service-card__dot {
  width: 8px;
  height: 8px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background-color: rgba(255, 255, 255, 0.6);
  box-shadow: 0 0 2px rgba(0, 0, 0, 0.5);
  cursor: pointer;
  transition: background-color var(--transition), transform var(--transition);
}
.service-card__dot:hover {
  background-color: var(--color-white);
}
.service-card__dot.is-active {
  background-color: var(--color-accent);
  transform: scale(1.2);
  /* Anillo blanco + filete oscuro: separa el rojo de fotos rojizas y claras. */
  box-shadow: 0 0 0 2px var(--color-white), 0 0 0 3px rgba(0, 0, 0, 0.35);
}
.service-card__dot:focus-visible {
  outline: 2px solid var(--color-white);
  outline-offset: 2px;
}

/* ---------- Experiencia / Proyectos ---------- */
/* Mosaico de 3 fotos: móvil 1 columna; ≥768px 2 columnas con la vertical
   ocupando 2 filas (ver responsive.css). El placeholder queda debajo del
   <img>; si la foto falla, main.js oculta la imagen y se ve el placeholder. */
.exp-mosaic {
  display: grid;
  gap: var(--space-md);
  margin-bottom: var(--space-xl);
}
.exp-mosaic__item {
  position: relative;
  margin: 0;
  aspect-ratio: 3 / 2;
  overflow: hidden;
  border-radius: var(--radius-md);
  background-color: var(--color-surface-dark);
  box-shadow: var(--shadow-sm);
}
.exp-mosaic__placeholder {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--color-text-on-dark);
  opacity: 0.22;
}
.exp-mosaic__placeholder svg {
  width: 56px;
  height: 56px;
}
.exp-mosaic__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.credentials {
  display: grid;
  gap: var(--space-md);
  margin-bottom: var(--space-xl);
}
.credential {
  text-align: center;
  background-color: var(--color-primary);
  color: var(--color-white);
  border-top: 4px solid var(--color-accent);
  border-radius: var(--radius-md);
  padding: var(--space-lg) var(--space-md);
  box-shadow: var(--shadow-sm);
}
.credential__num {
  display: block;
  font-size: var(--fs-xl);
  font-weight: 800;
  color: var(--color-white);
  line-height: 1.1;
  margin-bottom: var(--space-xs);
}
.credential__label {
  font-size: var(--fs-sm);
  color: rgba(255, 255, 255, 0.9);
}

/* Línea de tiempo de trayectoria: cuadrícula compacta (1 columna en móvil,
   2 en ≥768px — ver responsive.css). Cada item con acento rojo a la
   izquierda, mismo lenguaje visual que .fleet-list (Equipos). */
.timeline {
  display: grid;
  gap: var(--space-md) var(--space-xl);
}
.timeline__item {
  border-left: 3px solid var(--color-accent);
  padding-left: var(--space-sm);
}
.timeline__year {
  display: block;
  font-size: var(--fs-sm);
  font-weight: 700;
  color: var(--color-accent);
  letter-spacing: 0.02em;
  margin-bottom: var(--space-xs);
}
.timeline__title {
  color: var(--color-text);
  font-weight: 700;
  font-size: var(--fs-md);
  margin-bottom: var(--space-xs);
}
.timeline__client {
  color: var(--color-text-muted);
  font-size: var(--fs-sm);
}

/* ---------- Clientes ---------- */
/* Módulo institucional: la propia .clients pasa a ser el contenedor (sin
   envoltorio nuevo) con fondo blanco (--color-bg) apenas distinto del
   fondo alt (--color-bg-alt) de la sección, borde y sombra suaves — mismo
   lenguaje que las demás tarjetas del sitio. La jerarquía del bloque viene
   del conjunto (contenedor + padding + título un paso más grande), no de
   color de acento. */
.clients {
  margin-top: var(--space-xl);
  padding: var(--space-lg) var(--space-md);
  background-color: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-sm);
  text-align: center;
}
.clients__title {
  font-size: var(--fs-lg);
  color: var(--color-primary);
  margin-bottom: var(--space-md);
}
.clients__list {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-sm);
  margin-bottom: var(--space-md);
}
.clients__list li {
  background-color: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: 999px;
  padding: 0.5rem 1.1rem;
  font-weight: 600;
  color: var(--color-primary);
  font-size: var(--fs-sm);
}

/* ---------- Respaldo y estándares (sello de calidad/HSE) ---------- */
/* Franja de respaldo secundaria: mismo fondo oscuro que antes, pero sin
   filete rojo (reservado a elementos protagonistas) y compacta — el título
   reutiliza el tratamiento tipográfico de .section__kicker-label (mismo
   peso/color que ya se usa para texto muted sobre fondo oscuro) en vez de
   comportarse como encabezado. Las certificaciones bastan como mensaje. */
.standards {
  margin-top: var(--space-lg);
  padding: var(--space-sm) var(--space-md);
  background-color: var(--color-primary);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-sm);
  text-align: center;
}
.standards__title {
  font-family: var(--font-heading);
  font-size: 0.8125rem;
  font-weight: 600;
  letter-spacing: 3px;
  text-transform: uppercase;
  line-height: 1.4;
  color: var(--color-text-on-dark-muted);
  margin-bottom: var(--space-sm);
}
.standards__badges {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-sm);
}
.standards__badges li {
  border: 1px solid var(--color-border-dark);
  border-radius: 999px;
  padding: 0.5rem 1.1rem;
  font-weight: 700;
  color: var(--color-text-on-dark);
  font-size: var(--fs-sm);
}

/* ---------- Equipos y maquinaria (marquee) ---------- */
/* Tira horizontal que se desliza sola en bucle: el track contiene el set de
   slides DUPLICADO, y la animación recorre exactamente la mitad de su ancho
   (-50%), de modo que el reinicio coincide pixel a pixel y no hay salto.
   El gap va como margin-right de cada item para que la mitad sea exacta. */
.fleet {
  overflow: hidden;
  margin-bottom: var(--space-lg);
}
.fleet__track {
  display: flex;
  width: max-content;
  animation: fleet-scroll 36s linear infinite;
}
@keyframes fleet-scroll {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}
/* Pausa al pasar el mouse o al enfocar la región/algún elemento interno. */
.fleet:hover .fleet__track,
.fleet:focus-within .fleet__track {
  animation-play-state: paused;
}
.fleet__item {
  flex: 0 0 auto;
  width: min(320px, 78vw);
  margin-right: var(--space-md);
}
.fleet__figure {
  position: relative;
  margin: 0;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  border-radius: var(--radius-md);
  background-color: var(--color-surface-dark);
  box-shadow: var(--shadow-sm);
}
/* Placeholder elegante (fondo gris + ícono de línea): visible mientras no
   exista la foto; al subir equipo-N.webp la imagen lo cubre automáticamente. */
.fleet__placeholder {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--color-text-on-dark);
  opacity: 0.22;
}
.fleet__placeholder svg {
  width: 56px;
  height: 56px;
}
.fleet__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* Caption sobre scrim inferior: legible sobre foto o sobre el placeholder. */
.fleet__caption {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 1;
  padding: var(--space-md) var(--space-sm) var(--space-sm);
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: var(--fs-sm);
  color: var(--color-white);
  background-image: linear-gradient(to top, rgba(0, 0, 0, 0.65), rgba(0, 0, 0, 0));
}
/* reduce-motion: sin animación; la tira queda estática y navegable por
   scroll horizontal. Los clones se ocultan para no duplicar contenido. */
@media (prefers-reduced-motion: reduce) {
  .fleet {
    overflow-x: auto;
  }
  .fleet__track {
    animation: none;
  }
  .fleet__item--clone {
    display: none;
  }
}

/* Lista compacta bajo el marquee: conserva los 6 tipos de equipo. */
.fleet-list {
  display: grid;
  gap: var(--space-sm) var(--space-md);
}
.fleet-list__item {
  padding-left: var(--space-sm);
  border-left: 3px solid var(--color-accent);
}
.fleet-list__title {
  display: block;
  font-family: var(--font-heading);
  font-weight: 700;
  color: var(--color-primary);
}
.fleet-list__text {
  font-size: var(--fs-sm);
  color: var(--color-text-muted);
}

/* ---------- Contacto (sección clara; la tarjeta del formulario es oscura) ---------- */
/* 2 columnas en ≥768px (ver responsive.css); en móvil apilan: datos de
   contacto primero, formulario después. */
.contact {
  display: grid;
  /* minmax(0, 1fr): sin esto, el texto largo de .contact__list (teléfono/
     correo/dirección) fuerza su min-content por encima del ancho
     disponible y el track del grid se desborda hacia el documento
     (mismo criterio que ya usa .contact__form para su propio grid interno,
     ver más abajo). */
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-lg);
}
/* margin-top:0 explícito en ambas columnas: con align-items:start (ver
   responsive.css) sus bordes superiores ya arrancan a la misma altura; se
   deja explícito para no depender del reset global. */
/* Tarjeta clara con filete rojo superior: mismo lenguaje que .credential/
   .contact__form (padding, radio, sombra), pero en versión clara para que
   contraste con la tarjeta oscura del formulario a su lado. */
.contact__info {
  margin-top: 0;
  padding: var(--space-lg) var(--space-md);
  /* Aire extra abajo (+~16px) para cerrar el desfase con la tarjeta del
     formulario, que suele quedar más alta. */
  padding-bottom: calc(var(--space-lg) + var(--space-sm));
  background-color: var(--color-bg);
  border: 1px solid var(--color-border);
  border-top: 4px solid var(--color-accent);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-sm);
}
/* Tarjeta de acento oscuro (mismo lenguaje que .standards/.credential: filete
   rojo superior) flotando sobre la sección clara. Sombra más pronunciada que
   --shadow-md (pensada para superficies claras) para que se lea como pieza
   elevada e intencional, no como un resto de sección oscura. */
.contact__form {
  display: grid;
  /* minmax(0,1fr): la opción larga del select no puede forzar desborde. */
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-sm) var(--space-md);
  margin-top: 0;
  padding: var(--space-lg);
  background-color: var(--color-primary);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-top: 4px solid var(--color-accent);
  border-radius: var(--radius-md);
  box-shadow: 0 20px 45px rgba(26, 26, 26, 0.3);
}
.contact__form .field__label {
  color: var(--color-text-on-dark);
}
/* Inputs/textarea/select sobre la tarjeta oscura: fondo y borde translúcidos
   (AA verificado: ~6:1 el placeholder, ~13:1 las labels, contra este fondo). */
.contact__form .field__input {
  background-color: rgba(255, 255, 255, 0.05);
  border-color: rgba(255, 255, 255, 0.15);
  color: var(--color-text-on-dark);
}
.contact__form .field__input::placeholder {
  color: var(--color-text-on-dark-muted);
}
/* Anillo blanco además del borde rojo: el borde solo (~2:1) no alcanza el
   3:1 mínimo (WCAG 1.4.11) contra el fondo oscuro del campo/la tarjeta;
   el anillo, medido por muestreo de píxeles, sí. Scoped a este contexto
   oscuro — el foco en fondos claros del sitio no se toca. */
.contact__form .field__input:focus {
  border-color: var(--color-accent);
  box-shadow: 0 0 0 2px var(--color-white);
}
/* Select con la misma altura/estética de los inputs; flecha propia en el
   gris de --color-text-muted (hard-coded: url() no admite variables CSS). */
select.field__input {
  width: 100%;
  appearance: none;
  -webkit-appearance: none;
  padding-right: 2.25rem;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'><path d='M1 1l5 5 5-5' fill='none' stroke='%235c5c5c' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/></svg>");
  background-repeat: no-repeat;
  background-position: right 0.75rem center;
}
/* Misma flecha, en el gris claro on-dark, para que se vea sobre el input oscuro. */
.contact__form select.field__input {
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'><path d='M1 1l5 5 5-5' fill='none' stroke='%23b8b8b8' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/></svg>");
}
/* Las <option> del desplegable NO heredan el fondo de la tarjeta oscura
   (el navegador les da su propio fondo, blanco por defecto en la mayoría
   de navegadores de escritorio) pero sí heredan el color de texto claro
   del <select> — de ahí el bug de texto blanco sobre blanco. Se fija
   fondo y texto explícitos con los mismos tokens del resto del formulario.
   Soporte: Chrome/Edge y Firefox de escritorio respetan esto en la lista
   desplegada; Safari de escritorio y el selector nativo (rueda) de iOS
   ignoran el estilo de <option> y usan su propio look (no verificable
   desde este entorno). */
.contact__form select.field__input option {
  background-color: var(--color-primary);
  color: var(--color-text-on-dark);
}
.field {
  display: grid;
  gap: 0.35rem;
}
.field__label {
  font-weight: 600;
  font-size: var(--fs-sm);
  color: var(--color-primary);
}
.field__input {
  padding: 0.75rem;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  background-color: var(--color-bg);
  transition: border-color var(--transition);
}
.field__input:focus {
  border-color: var(--color-secondary);
  outline: none;
}
.field__input.is-invalid {
  border-color: var(--color-error);
}
.field__error {
  font-size: var(--fs-sm);
  color: var(--color-error);
  min-height: 1.1em;
}
.contact__feedback {
  font-weight: 600;
}
/* Como bloque de alerta solo cuando hay mensaje (contraste AA). */
.contact__feedback.is-success,
.contact__feedback.is-error {
  padding: var(--space-sm) var(--space-md);
  border-radius: var(--radius-sm);
}
/* Texto oscuro sobre verde: ~5.5:1 AA */
.contact__feedback.is-success {
  background-color: var(--color-success);
  color: var(--color-primary-dark);
}
/* Texto blanco sobre rojo: ~4.9:1 AA */
.contact__feedback.is-error {
  background-color: var(--color-error);
  color: var(--color-white);
}

.contact__info-title {
  color: var(--color-primary);
  margin-bottom: var(--space-sm);
}
.contact__list {
  display: grid;
  /* minmax(0, 1fr): mismo criterio que .contact — sin esto, el correo
     (una sola palabra larga sin espacios) fuerza el track por encima del
     ancho disponible. */
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-sm);
  color: var(--color-text-muted);
}
.contact__list a:hover,
.contact__list a:focus-visible {
  color: var(--color-accent);
}
.contact__item {
  display: flex;
  align-items: flex-start;
  gap: 10px;
}
/* El correo (Gerencia@glpmultiserviciossas.com.co) no tiene espacios: sin
   esto, aun con el track ya contenido, ese texto no se achica ni envuelve
   y sigue desbordando el <li> (min-width:auto es el default de los hijos
   flex). Se aplica a cualquier hijo que no sea el ícono, defensivamente,
   por si algún dato futuro es igual de largo. */
.contact__item > :not(.contact__icon) {
  min-width: 0;
  overflow-wrap: break-word;
}
.contact__icon {
  flex-shrink: 0;
  width: 20px;
  height: 20px;
  color: var(--color-accent);
}
.contact__icon svg {
  width: 100%;
  height: 100%;
}
.contact__item-sub {
  display: block;
  font-size: var(--fs-sm);
  color: var(--color-text-muted);
}

/* ---------- Mapa interactivo (activación por clic) ---------- */
/* El iframe queda sin pointer-events hasta que se activa: así el mapa no
   secuestra el scroll de la página al pasar el cursor/dedo sobre él. */
.map-embed {
  position: relative;
  width: 100%;
  height: 140px;
  /* +~8px sobre --space-md para separar mejor del último ítem de la lista. */
  margin-top: calc(var(--space-md) + var(--space-xs));
  border-radius: var(--radius-md);
  overflow: hidden;
}
.map-embed iframe {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
  pointer-events: none;
}
.map-embed__overlay {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  /* .35 medía 2.19-2.65:1 (falla 4.5:1). .65 pasa incluso contra un tile de
     mapa blanco puro (peor caso teórico) con margen, verificado por
     muestreo, sin oscurecer el mapa a bloque sólido. */
  background-color: rgba(26, 26, 26, 0.65);
  border: 0;
  color: var(--color-white);
  font-size: 0.75rem;
  font-weight: 600;
  cursor: pointer;
}
.map-embed.is-active .map-embed__overlay {
  display: none;
}
.map-embed.is-active iframe {
  pointer-events: auto;
}

/* ---------- Cortina de scroll: Equipos se detiene bajo el header mientras
   Contacto sube y la cubre ---------- */
/* El buffer de "stick" vive en el wrapper (no en la sección), para no alterar
   el padding-block propio de Equipos: con .curtain-wrap__panel pegada a
   top: var(--header-h), este padding-bottom es lo que la mantiene fija antes
   de que el siguiente elemento (Contacto) la empuje fuera de flujo. Fondo
   oscuro igual al de Equipos: sin él, ese buffer queda transparente y se ve
   como un hueco vacío entre el final de Equipos y la sábana de Contacto. */
.curtain-wrap {
  position: relative;
  padding-bottom: var(--space-2xl);
  background-color: var(--color-primary-dark);
}
.curtain-wrap__panel {
  position: sticky;
  top: var(--header-h);   /* no queda tapada por el header fijo */
  z-index: 1;
}
/* Contacto sube: margin-top negativo + z-index mayor que Equipos, esquinas
   superiores redondeadas y sombra hacia arriba para leerse como una hoja que
   cubre el borde inferior de Equipos. Sin línea ni divisor: el efecto
   depende solo del radio + la sombra. */
.curtain-sheet {
  position: relative;
  z-index: 2;
  margin-top: -32px;
  border-top-left-radius: 24px;
  border-top-right-radius: 24px;
  box-shadow: 0 -16px 32px rgba(0, 0, 0, 0.25);
}
/* prefers-reduced-motion: se anula toda la cortina (sticky, solape, radio,
   sombra) y queda el apilado normal; Equipos ya trae su propio filete rojo
   superior (.section--dark), así que no hace falta ningún elemento extra. */
@media (prefers-reduced-motion: reduce) {
  .curtain-wrap {
    padding-bottom: 0;
  }
  .curtain-wrap__panel {
    position: static;
  }
  .curtain-sheet {
    margin-top: 0;
    border-top-left-radius: 0;
    border-top-right-radius: 0;
    box-shadow: none;
  }
}

/* ---------- Footer ---------- */
.footer {
  background-color: var(--color-primary-dark);
  color: var(--color-text-on-dark-muted);
  /* Mismo filete de entrada que las demás secciones oscuras (.section--dark). */
  border-top: 4px solid var(--color-accent);
}
/* Columnas: marca / navegación / contacto (apiladas en móvil). */
.footer__grid {
  display: grid;
  gap: var(--space-lg);
  padding-block: var(--space-xl) var(--space-lg);
}
.footer__brand {
  font-family: var(--font-heading);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--color-white);
  margin-bottom: var(--space-xs);
}
.footer__desc {
  font-size: var(--fs-sm);
  max-width: 38ch;
}
.footer__heading {
  font-family: var(--font-heading);
  font-size: var(--fs-sm);
  text-transform: uppercase;
  letter-spacing: 2px;
  color: var(--color-white);
  margin-bottom: var(--space-sm);
}
.footer__nav,
.footer__contact {
  display: grid;
  gap: var(--space-xs);
  font-size: var(--fs-sm);
}
/* El correo corporativo es largo: que parta en vez de desbordar en móvil. */
.footer__contact {
  overflow-wrap: anywhere;
}
.footer__link {
  transition: color var(--transition);
}
.footer__link:hover,
.footer__link:focus-visible {
  color: var(--color-white);
}
/* Barra inferior: NIT · ciudad · cobertura · © año. */
.footer__bar {
  border-top: 1px solid var(--color-border-dark);
  padding-top: var(--space-sm);
  /* Espacio extra para que el botón flotante de WhatsApp no tape el contenido. */
  padding-bottom: calc(var(--space-sm) + 64px);
}
.footer__bar-inner {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  font-size: var(--fs-sm);
  text-align: center;
}

/* ---------- Botón flotante de WhatsApp ---------- */
.whatsapp-fab {
  position: fixed;
  right: var(--space-md);
  bottom: var(--space-md);
  z-index: 1500;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 56px;
  height: 56px;
  border-radius: 50%;
  background-color: #25d366;
  color: var(--color-white);
  box-shadow: var(--shadow-md);
  transition: transform var(--transition), box-shadow var(--transition);
}
.whatsapp-fab:hover {
  transform: scale(1.08);
  box-shadow: var(--shadow-md);
}
.whatsapp-fab__icon {
  width: 32px;
  height: 32px;
}
@media (prefers-reduced-motion: reduce) {
  .whatsapp-fab,
  .whatsapp-fab:hover {
    transform: none;
  }
}
