/* ===================================================================
   VB SPORTS DMC — styles
   =================================================================== */

:root {
  /* ---- Paleta de página (hero y servicios) ----
     Se probó en hueso cálido para subir el registro premium, pero dejaba el
     hero desconectado del resto del sitio, que es azul claro. Estos tokens
     son ahora los mismos que los `--light-*` de abajo: un solo sistema de
     color para toda la página, y el azul de marca reservado al logotipo y a
     los detalles mínimos.
     Se mantienen como tokens propios —y no se usa `--light-*` directamente—
     porque el hero los referencia en una treintena de sitios: tenerlos
     separados permite volver a diferenciarlo sin tocar nada más. */
  --page:   var(--light-bg);      /* fondo */
  --page-2: #E3EDFA;              /* superficie elevada */
  --ink:    var(--light-ink);     /* texto principal */
  --ink-2:  var(--light-ink-2);   /* secundario */
  --line:   #D3E3F6;              /* filetes */

  /* Superficie de la tarjeta de servicios: el mismo azul que la página pero
     un escalón por debajo, con algo más de saturación. Se despega del fondo
     sin salirse de la familia —que es lo que fallaba con el hueso— y deja
     que la foto de cabecera sea lo que aporta el color. El filete acompaña,
     un escalón más marcado, para que el canto se lea sin sombra. */
  --card:      #DFEAF8;
  --card-line: #C6D9EE;

  /* Light theme tokens (secciones aún sin rediseñar) */
  --light-bg:        #EFF6FF;
  --light-surface:   #FFFFFF;
  --light-border:    #DBEAFE;
  --light-ink:       #0F172A;     /* primary text */
  --light-ink-2:     #475569;     /* secondary text */
  --light-ink-3:     #94A3B8;     /* muted */
  --accent-sky:      #0EA5E9;     /* sky-500 */
  --accent-sky-2:    #0284C7;     /* sky-600 */
  --accent-sky-soft: #BAE6FD;     /* sky-200 */
}

html {
  /* Sin scroll-behavior: smooth. El motor de scroll ya suaviza la lectura;
     encadenar el suavizado nativo con el del lerp produce un desfase largo al
     pulsar un enlace del nav — la página llega antes que el hero. El salto
     suave de los enlaces lo hace scrollToSection() en main.js. */
  scroll-padding-top: 100px;
  -ms-overflow-style: none;
  scrollbar-width: none;
}
html::-webkit-scrollbar { display: none; }

body {
  /* Archivo en toda la página, sin segunda familia. El eje `wdth` da por sí
     solo el contraste que antes se buscaba mezclando dos tipografías. */
  font-family: 'Archivo', sans-serif;
  /* Fondo plano. Antes había una fotografía de cielo fija detrás de toda la
     página bajo un degradado azul; la referencia no usa ninguna imagen de
     fondo, y el hueso plano es lo que deja respirar al contenido. De paso
     desaparece el background-attachment: fixed, que era caro de repintar. */
  background: var(--page);
  color: var(--ink);
  overflow-x: hidden;
  cursor: auto;
}

/* Grotesca extendida en peso medio. El ancho y el tracking cerrado son lo que
   dan el registro, no la negrita: a 80px, un peso 500 afirma y un 800 grita. */
.font-heading {
  font-family: 'Archivo', sans-serif;
  font-variation-settings: 'wdth' 118;
  font-weight: 500;
  letter-spacing: -0.055em;
}
/* `body .font-mono` y no `.font-mono`: Tailwind trae su propia utilidad con
   el mismo nombre y la misma especificidad, y el CDN inyecta su hoja DESPUÉS
   de esta, así que ganaba y 32 elementos seguían en monoespaciada del
   sistema. Un descendiente de más basta; no hace falta !important. */
body .font-mono {
  font-family: 'Archivo', sans-serif;
  font-variation-settings: 'wdth' 105;
}

/* ==========================================================================
   NAV — flota sobre el hero, sin fondo, y solo lo gana al terminar el scroll
   ========================================================================== */
#nav {
  background: transparent;
  border-bottom: 1px solid transparent;
  transition: background .45s ease, border-color .45s ease, padding .3s ease;
}
#nav.nav-scrolled {
  background: var(--page);
  border-bottom-color: var(--line);
}

.nav-link,
.lang-toggle,
#nav-cta {
  font-family: 'Archivo', sans-serif;
  font-variation-settings: 'wdth' 105;
  font-weight: 500;
  font-size: .78rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink);
  background: none;
  border: 0;
  cursor: pointer;
  transition: color .3s ease, border-color .3s ease;
}
.nav-link:hover,
.lang-toggle:hover { color: var(--ink-2); }

/* El único botón con caja. Filete fino, sin relleno y sin esquinas redondas:
   el borde basta para señalar que es pulsable. */
#nav-cta {
  border: 1px solid var(--line);
  padding: .85rem 1.75rem;
}
#nav-cta:hover {
  border-color: var(--ink);
  color: var(--ink);
}

.lang-toggle {
  border: 1px solid var(--line);
  width: 2.6rem;
  height: 2.6rem;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
}
.lang-toggle:hover { border-color: var(--ink); }

#mobile-toggle {
  color: var(--ink);
  transition: color .3s ease;
}

/* ---------- Nav sobre fondo oscuro ----------
   Dos tramos. En el primero el nav no está sobre el vídeo sino sobre el
   fuselaje de #hero-window, que es casi negro; dura hasta que la ventanilla
   ha crecido lo suficiente para dejar el borde superior despejado. En el
   segundo, medido sobre el canvas, la luminancia cae a 93-131 entre el 67% y
   el 79% del recorrido: es el techo del vestíbulo en el clip del hotel.
   Los límites viven en PHASES.navWhite. */
#nav.nav-over-dark .nav-link,
#nav.nav-over-dark .lang-toggle,
#nav.nav-over-dark #nav-cta,
#nav.nav-over-dark #mobile-toggle { color: #fff; }
#nav.nav-over-dark .lang-toggle,
#nav.nav-over-dark #nav-cta { border-color: rgba(255, 255, 255, .5); }
#nav.nav-over-dark .nav-link:hover,
#nav.nav-over-dark .lang-toggle:hover { color: rgba(255, 255, 255, .72); }
#nav.nav-over-dark .lang-toggle:hover,
#nav.nav-over-dark #nav-cta:hover { border-color: #fff; }

/* ==========================================================================
   HERO — secuencia de fotogramas scrubeada
   #hero-wrap es la pista de scroll; #hero es sticky y contiene el canvas.
   ========================================================================== */
#hero-wrap {
  height: 720vh;              /* ~35 px de scroll por fotograma en escritorio */
  position: relative;
}
#hero {
  z-index: 1;
  /* --vh lo publica measure() desde JS: en móvil la barra de URL cambia
     innerHeight mientras svh no se entera, y con min-height el sticky podía
     exceder el viewport y dejar los últimos fotogramas sin verse nunca. */
  height: calc(var(--vh, 1vh) * 100);
  display: flex;
  align-items: center;
  justify-content: center;
  padding-inline: 1rem;
}

#hero-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
  background: var(--light-bg);   /* lo que se ve antes del primer fotograma */
}

/* Velo de legibilidad, deliberadamente ligero.
   Antes lavaba todo el cuadro y el vídeo salía descolorido. Ahora la carga de
   legibilidad la lleva el halo local de .hero-scene::before, que solo aclara
   lo que hay DETRÁS del texto: el resto del plano conserva su color. */
/* El velo general aclaraba todo el plano para sostener texto en tinta oscura.
   Ya no hace falta —de la escena 2 en adelante el texto va en blanco— y
   además lavaba el vídeo, que era media queja. Se queda solo un oscurecido
   muy leve arriba y abajo, que asienta la imagen sin tocar el centro. */
#hero-veil {
  position: absolute;
  inset: 0;
  z-index: 5;
  pointer-events: none;
  background:
    linear-gradient(180deg,
      rgba(15, 23, 42, .18) 0%,
      rgba(15, 23, 42, 0) 22%,
      rgba(15, 23, 42, 0) 78%,
      rgba(15, 23, 42, .14) 100%);
}

/* ---------- VELOS DIRECCIONALES ----------
   Debajo de cada panel el fondo del vídeo recorre de 5 a 248 de luminancia,
   así que no hay color de texto que aguante por sí solo: el blanco se pierde
   en los parches claros y la tinta en los oscuros. La solución no es elegir
   mejor el color, es comprimir el fondo ANTES de escribir encima.
   Van anclados al borde del encuadre y se desvanecen hacia dentro sin cerrar
   forma, así que se leen como un etalonaje del plano y no como una caja.
   La opacidad la mueve el motor con la visibilidad de las escenas de cada
   lado; las centradas encienden los dos a media fuerza, y eso produce un
   viñeteado simétrico. */
.hero-scrim {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 66%;
  z-index: 4;
  pointer-events: none;
  will-change: opacity;
}
/* Las paradas están puestas para que el velo siga fuerte a lo ancho de todo
   el panel —que ocupa hasta unos 340 px— y se apague después. Con una caída
   más rápida, el borde derecho del texto quedaba casi sin velar y ahí es
   justo donde el vídeo tiene los parches de 248. */
#hero-scrim-l {
  left: 0;
  opacity: var(--scrim-l, 0);
  background: linear-gradient(90deg,
    rgba(9, 15, 28, .84) 0%,
    rgba(9, 15, 28, .77) 26%,
    rgba(9, 15, 28, .59) 50%,
    rgba(9, 15, 28, .29) 74%,
    rgba(9, 15, 28, 0) 100%);
}
#hero-scrim-r {
  right: 0;
  opacity: var(--scrim-r, 0);
  background: linear-gradient(270deg,
    rgba(9, 15, 28, .84) 0%,
    rgba(9, 15, 28, .77) 26%,
    rgba(9, 15, 28, .59) 50%,
    rgba(9, 15, 28, .29) 74%,
    rgba(9, 15, 28, 0) 100%);
}

/* ---------- La costura con el resto de la página ----------
   Las declaraciones de fondo son una copia LITERAL de las de `body`. Con
   background-attachment: fixed + cover + una background-position constante,
   el fondo del body se pinta idéntico en cualquier posición de scroll; por
   tanto esta capa, que lo replica, es el mismo dibujo. Cuando llega a
   opacidad 1 al final del hero, lo que se ve YA es lo que verá el usuario en
   Servicios: no hay nada que sincronizar ni escala que espejar.
   Si algún navegador degrada `fixed` a `scroll`, degrada igual en `body` y
   aquí, así que la identidad se conserva por construcción. */
/* Ahora que el fondo del body es un color plano, la costura es trivial: basta
   con fundir a ese mismo color. Desaparece toda la réplica de degradado e
   imagen fija que hacía falta cuando el fondo era una fotografía. */
#hero-seam {
  position: absolute;
  inset: 0;
  z-index: 6;
  opacity: 0;
  pointer-events: none;
  will-change: opacity;
  background: var(--page);
}

/* ---------- Escenas ---------- */
.hero-scenes {
  position: relative;
  z-index: 7;
  width: 100%;
  max-width: 1400px;
  height: 100%;
  margin-inline: auto;
}

/* Todas las escenas ocupan el mismo sitio; el motor las cruza por opacidad
   y un desplazamiento corto. El estado inicial evita el destello. */
.hero-scene {
  position: absolute;
  top: 50%;
  opacity: 0;
  will-change: opacity, transform;
}
/* Aquí había un halo radial claro detrás de cada texto. Fuera: al ser una
   forma cerrada se leía como una pegatina pegada encima del vídeo, y encima
   aclaraba el plano justo donde más color tenía.
   Lo sustituye el velo direccional #hero-scrim-l/r, anclado al borde del
   encuadre. Ver el bloque VELOS más abajo. */
.hero-scene-center {
  left: 50%;
  width: min(100%, 60rem);
  text-align: center;
  /* `--sc` lo escribe el motor y solo la escena 1 lo mueve: nace del tamaño
     de la ventanilla y crece con el travelling. El escalado va DESPUÉS del
     centrado, así que ocurre alrededor del centro del bloque. */
  transform: translate(-50%, calc(-50% + var(--ty, 0px))) scale(var(--sc, 1));
}
.hero-scene-left {
  left: clamp(1.5rem, 5vw, 4rem);
  max-width: 340px;
  transform: translateY(calc(-50% + var(--ty, 0px)));
}
.hero-scene-right {
  right: clamp(1.5rem, 5vw, 4rem);
  max-width: 340px;
  text-align: right;
  transform: translateY(calc(-50% + var(--ty, 0px)));
}
/* La primera escena arranca visible: es lo primero que se ve al cargar. */
.hero-scene[data-scene="cielo"] { opacity: 1; }

/* ---------- Escena 1: sin halo ----------
   El halo existe para sostener texto oscuro sobre metraje de ciudad. La
   escena 1 no lo necesita: nace dentro de la ventanilla, que es cielo, y
   termina con el cuadro entero en cielo. Y sobre todo, un halo que escala
   con el bloque se lee como una mancha que se agranda, que es justo lo que
   rompía el efecto. Las escenas 2-5 lo conservan. */
.hero-scene[data-scene="cielo"]::before { display: none; }

/* Insignia, filete y subtítulo aparecen solo cuando el bloque ya ha crecido
   lo suficiente para que se lean. A escala 0,42 la insignia mide 5 px: sin
   esto, el arranque serían tres renglones de ruido ilegible bajo el título. */
.hero-scene[data-scene="cielo"] .hero-badge,
.hero-scene[data-scene="cielo"] .hero-rule,
.hero-scene[data-scene="cielo"] .hero-lead {
  opacity: var(--support, 1);
}

/* ---------- Escena 1: marca ---------- */
/* Sin píldora, sin cristal, sin borde. Solo texto: la referencia no usa ni un
   solo contenedor decorativo en el hero. */
.hero-badge {
  display: inline-flex;
  align-items: center;
  gap: .9rem;
  font-family: 'Archivo', sans-serif;
  font-variation-settings: 'wdth' 105;
  font-weight: 500;
  font-size: clamp(.68rem, .9vw, .8rem);
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--ink-2);
  margin-bottom: 1.75rem;
}
.hero-badge .pulse-dot {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--accent-sky);   /* el azul de marca, en su única aparición */
}
.hero-rule {
  width: 3rem;
  height: 1px;
  margin: clamp(1.25rem, 2.2vw, 2rem) auto clamp(1.25rem, 2.2vw, 1.75rem);
  background: var(--line);
}
.hero-lead {
  font-family: 'Archivo', sans-serif;
  font-variation-settings: 'wdth' 105;
  font-size: clamp(1rem, 1.5vw, 1.35rem);
  font-weight: 400;
  line-height: 1.5;
  letter-spacing: -.01em;
  max-width: 42rem;
  margin-inline: auto;
  color: var(--ink-2);
}

/* ---------- Escena 2: la línea del revelado ---------- */
.hero-line {
  font-family: 'Archivo', sans-serif;
  font-variation-settings: 'wdth' 118;
  font-weight: 500;
  font-size: clamp(1.7rem, 4.4vw, 4rem);
  line-height: .95;
  letter-spacing: -.06em;
  /* De aquí en adelante el texto va en blanco sobre el velo direccional. La
     sombra es corta y oscura, no un resplandor claro: viaja pegada a los
     trazos, así que define el canto de la letra sin dibujar ninguna mancha
     alrededor. Era el resplandor claro lo que hacía que el texto pareciese
     pegado ENCIMA del vídeo en vez de estar dentro del plano. */
  color: #FFFFFF;
  text-shadow: 0 1px 2px rgba(9, 15, 28, .45), 0 2px 22px rgba(9, 15, 28, .35);
}

/* ---------- Escenas 3-5: paneles de servicio ---------- */
.hero-side-eyebrow {
  display: block;
  font-family: 'Archivo', sans-serif;
  font-variation-settings: 'wdth' 105;
  /* Grande, pero sin mayúsculas monoespaciadas: el tamaño ordena la lectura
     y el tracking cerrado lo mantiene sobrio. */
  font-size: clamp(.95rem, 1.5vw, 1.3rem);
  font-weight: 500;
  letter-spacing: .12em;
  text-transform: uppercase;
  /* El azul de marca en su tono claro: sobre el velo oscuro el sky-600 se
     hundía hasta desaparecer. Este mantiene el color corporativo y se lee. */
  color: var(--accent-sky-soft);
  margin-bottom: .8rem;
  text-shadow: 0 1px 3px rgba(9, 15, 28, .5);
}
/* Con una sola línea por escena el titular puede ir mucho más grande, y el
   tamaño hace más por la legibilidad que cualquier velo. */
.hero-side-title {
  font-family: 'Archivo', sans-serif;
  font-variation-settings: 'wdth' 118;
  font-weight: 500;
  font-size: clamp(1.9rem, 3.8vw, 3.4rem);
  line-height: .95;
  letter-spacing: -0.065em;
  color: #FFFFFF;
  margin-bottom: .9rem;
  text-shadow: 0 1px 2px rgba(9, 15, 28, .45), 0 2px 20px rgba(9, 15, 28, .35);
}
.hero-side-body {
  font-family: 'Archivo', sans-serif;
  font-variation-settings: 'wdth' 105;
  font-weight: 400;
  font-size: clamp(.95rem, 1.15vw, 1.15rem);
  line-height: 1.45;
  letter-spacing: -.01em;
  /* Blanco al 84% y peso 400: la jerarquía con el titular la marca la
     opacidad, no un gris, que sobre vídeo se ensucia. */
  color: rgba(255, 255, 255, .84);
  text-shadow: 0 1px 2px rgba(9, 15, 28, .5), 0 1px 14px rgba(9, 15, 28, .3);
}

/* ---------- Móvil: los paneles se centran abajo ---------- */
@media (max-width: 768px) {
  /* 400vh en vez de 720: menos pantallas de arrastre y menos trabajo para
     una GPU más modesta. Con 128 fotogramas salen ~18 px por fotograma, que
     es lo correcto porque un swipe recorre mucho más rápido que una rueda. */
  #hero-wrap { height: 400vh; }

  .hero-scene {
    top: auto;
    bottom: 14%;
    left: 50%;
    right: auto;
    width: 86vw;
    max-width: 86vw;
    text-align: center;
    transform: translate(-50%, var(--ty, 0px)) scale(var(--sc, 1));
  }
  .hero-scene-center { width: 86vw; }
  .hero-scene::before { inset: -1.5rem -1.5rem; }
}

/* Viewports bajos (móvil apaisado, ventanas achatadas): la escena más alta
   —hoteles, con cuerpo y tres puntos de lista— se metía por debajo del nav.
   Se comprime el texto en lugar de dejar que se recorte. */
@media (max-height: 620px) {
  .hero-scene { bottom: 8%; }
  .hero-side-title { font-size: clamp(1.1rem, 4.5vw, 1.6rem); margin-bottom: .6rem; }
  .hero-side-body  { font-size: .85rem; line-height: 1.4; }
  .hero-side-eyebrow { margin-bottom: .5rem; }
  .hero-lead { font-size: .9rem; line-height: 1.45; }
  .hero-rule { margin-block: .6rem 1rem; }
  .hero-badge { margin-bottom: .9rem; padding: .35rem 1rem; }
}

/* ---------- Gradient text (used in Servicios "ELITE") ---------- */
.gradient-text {
  background: linear-gradient(180deg, #ffffff 0%, #22d3ee 45%, #3b82f6 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  -webkit-text-fill-color: transparent;
  filter: drop-shadow(0 0 30px rgba(59, 130, 246, .25));
}

/* ---------- Título del hero ---------- */
#hero-title {
  font-family: 'Archivo', sans-serif;
  font-variation-settings: 'wdth' 118;
  font-weight: 500;
  color: var(--ink);
  font-size: clamp(2rem, 6vw, 6.5rem);
  line-height: .9;
  letter-spacing: -0.075em;
  text-transform: none;
  white-space: nowrap;
  /* Sin halo: pedido explícito. Además el drop-shadow se escalaba con el
     bloque y el desenfoque crecía con él, que era lo que más delataba el
     truco. El título se sostiene solo porque nace sobre cielo. */
  filter: none;
  perspective: 800px;
  will-change: transform, letter-spacing;
}
.hero-char {
  display: inline-block;
  opacity: 0;
  transform: translateY(40px) scale(1.4);
  filter: blur(12px);
  animation: hero-char-kick 1.1s cubic-bezier(.2, .8, .2, 1) forwards;
  will-change: transform, opacity, filter;
}
.hero-char.hero-space {
  width: .35em;
  animation: none;
  opacity: 1;
  transform: none;
  filter: none;
}
/* El escalonado lo escribe splitTitle() en main.js con animation-delay en
   línea. Antes eran 24 reglas :nth-child atadas a una cadena concreta de 13
   caracteres — y estaban duplicadas y en conflicto: el segundo bloque pisaba
   al primero. Con el título pasando ahora por i18n, la longitud varía y un
   escalonado por índice fijo no puede funcionar. */

@keyframes hero-char-kick {
  0%   { opacity: 0; transform: translateY(40px) scale(1.4) rotate(-2deg); filter: blur(12px); }
  60%  { opacity: 1; transform: translateY(-4px) scale(.96) rotate(0); filter: blur(0); }
  80%  { transform: translateY(0) scale(1.02); }
  100% { opacity: 1; transform: translateY(0) scale(1); filter: blur(0); }
}

/* ---------- Hero ambient orb ---------- */
.orb-pulse {
  animation: orb-pulse 8s ease-in-out infinite;
}
@keyframes orb-pulse {
  0%, 100% { transform: scale(.8); opacity: .2; }
  50%      { transform: scale(1.1); opacity: .4; }
}
/* Orbe ambiental retirado: un degradado azul flotando y derivando por detrás
   de toda la página es de los efectos que más delatan el registro de web de
   producto. La referencia no tiene nada equivalente. */
.ambient-orb { display: none; }
@keyframes drift {
  0%, 100% { transform: translate(0, 0); }
  50%      { transform: translate(20vw, 10vh); }
}

.pulse-dot { animation: pulse-dot 1.6s ease-in-out infinite; }
@keyframes pulse-dot {
  0%, 100% { opacity: 1; transform: scale(1); }
  50%      { opacity: .5; transform: scale(.7); }
}


/* ---------- Reveal entry animations ---------- */
.reveal {
  opacity: 0;
  transform: translateY(20px);
  animation: reveal-up 1s ease-out forwards;
}
@keyframes reveal-up {
  to { opacity: 1; transform: translateY(0); }
}
.reveal-line {
  transform-origin: center;
  transform: scaleX(0);
  animation: scale-x 1.5s cubic-bezier(.16,1,.3,1) .5s forwards;
}
@keyframes scale-x { to { transform: scaleX(1); } }

/* ---------- Scene system (Jesko-style reveals) ----------
   .scene-reveal stays invisible until IntersectionObserver adds .is-visible.
   Children use --stagger to delay sequentially. */
.scene-reveal {
  opacity: 0;
  transform: translateY(40px);
  /* El retardo escalonado va SOLO en la opacidad. Antes lo llevaban las dos
     propiedades, y como las tarjetas de una misma fila reciben --stagger
     distinto (2, 3, 4 por columna), durante los 900 ms de la transición las
     tres estaban a distinta altura: Madrid arriba, Barcelona a medias y
     Castellón todavía 40 px abajo. En una columna la cascada en diagonal
     funciona; dentro de una fila se lee como un fallo de maquetación.
     Ahora suben juntas y se encienden una detrás de otra. */
  transition:
    opacity .9s cubic-bezier(.2, .8, .2, 1) calc(var(--stagger, 0) * 80ms),
    transform .9s cubic-bezier(.2, .8, .2, 1);
  will-change: transform, opacity;
}
.scene-reveal.is-visible {
  opacity: 1;
  transform: translateY(0);
}

/* Scene background: a sticky layer behind the section content. */
.scene {
  position: relative;
}
.scene-bg {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  overflow: hidden;
}
.scene-bg-image {
  position: absolute;
  inset: -10%;                       /* over-render so parallax doesn't expose edges */
  width: 120%;
  height: 120%;
  object-fit: cover;
  opacity: .12;
  filter: blur(2px);
  will-change: transform;
}

@media (prefers-reduced-motion: reduce) {
  .scene-reveal { opacity: 1; transform: none; transition: none; }
}

/* ---------- Scroll indicator ---------- */
.scroll-line {
  position: relative;
  overflow: hidden;
}
.scroll-line::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(to bottom, #0EA5E9, transparent);
  transform: translateY(-100%);
  animation: scroll-flow 2s ease-in-out infinite;
}
@keyframes scroll-flow {
  0%   { transform: translateY(-100%); }
  100% { transform: translateY(100%); }
}

/* ---------- Custom cursor (desktop only) ---------- */
#cursor-dot, #cursor-ring {
  position: fixed;
  top: 0; left: 0;
  pointer-events: none;
  z-index: 9999;
  transform: translate(-50%, -50%);
  transition: opacity .25s ease;
  will-change: transform;
}
#cursor-dot {
  width: 6px; height: 6px;
  background: #22d3ee;
  border-radius: 50%;
  opacity: 0;
}
#cursor-ring {
  width: 36px; height: 36px;
  border: 1px solid rgba(255,255,255,.5);
  border-radius: 50%;
  opacity: 0;
  transition: transform .2s ease-out, width .2s ease, height .2s ease, border-color .2s ease, opacity .25s ease;
}
#cursor-ring.is-hover {
  width: 60px; height: 60px;
  border-color: #22d3ee;
  background: rgba(34, 211, 238, .08);
}
/* Cursor personalizado desactivado. Además de ser un tic de web de producto,
   el `cursor: none !important` sobre `body *` eliminaba toda affordance: ni
   el cursor de texto en los párrafos ni el de mano en los botones. Los
   elementos siguen en el DOM por si se quiere recuperar. */
#cursor-dot, #cursor-ring { display: none; }

/* ---------- Partner card ---------- */
.partner-card {
  position: relative;
  aspect-ratio: 4 / 5;
  overflow: hidden;
  border-right: 1px solid rgba(255,255,255,.05);
  border-bottom: 1px solid rgba(255,255,255,.05);
  cursor: pointer;
  background: #0F172A;
}
.partner-card .partner-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: grayscale(1) brightness(.6);
  transition: filter .7s ease, transform 1.2s ease;
  will-change: transform;
}
.partner-card:hover .partner-img {
  filter: grayscale(0) brightness(.85);
  transform: scale(1.08);
}
.partner-card .partner-overlay {
  position: absolute;
  inset: 0;
  background: linear-gradient(to top, rgba(15, 23, 42, .95) 0%, rgba(15, 23, 42, .3) 50%, transparent 100%);
}
.partner-card .partner-content {
  position: absolute;
  bottom: 0; left: 0; right: 0;
  padding: 2rem;
  z-index: 2;
  transform: translateY(20px);
  transition: transform .5s ease;
}
.partner-card:hover .partner-content { transform: translateY(0); }
.partner-card .partner-arrow {
  position: absolute;
  top: 1.5rem; right: 1.5rem;
  z-index: 2;
  width: 36px; height: 36px;
  display: flex; align-items: center; justify-content: center;
  background: rgba(255,255,255,.05);
  border: 1px solid rgba(255,255,255,.1);
  border-radius: 50%;
  color: #22d3ee;
  transition: all .3s ease;
}
.partner-card:hover .partner-arrow {
  background: #22d3ee;
  color: #0F172A;
  border-color: #22d3ee;
}

/* ---------- Destino card ---------- */
.destino-card {
  position: relative;
  overflow: hidden;
  border-radius: 1rem;
  height: 400px;
  border: 1px solid rgba(255,255,255,.1);
  cursor: pointer;
  transition: transform .4s ease;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
}
.destino-card:hover { transform: translateY(-10px) scale(1.02); }
@media (min-width: 768px) { .destino-card { height: 450px; } }
.destino-card .destino-img {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  transition: transform .7s ease;
}
.destino-card:hover .destino-img { transform: scale(1.10); }
.destino-card .destino-overlay {
  position: absolute;
  inset: 0;
  background: linear-gradient(to top, rgba(2, 6, 23, 1) 0%, rgba(15, 23, 42, .6) 40%, transparent 100%);
  transition: opacity .3s ease;
}
.destino-card .destino-content {
  position: relative;
  z-index: 2;
  padding: 1.5rem 1.5rem 1.75rem;
}
@media (min-width: 768px) { .destino-card .destino-content { padding: 2rem; } }
.destino-card .destino-extra {
  transform: translateY(16px);
  opacity: 0;
  transition: transform .5s ease, opacity .5s ease;
}
/* La descripción está oculta hasta el hover, pero SIGUE ocupando su sitio, y
   el contenido de la tarjeta va anclado abajo (justify-content: flex-end).
   Resultado: una descripción de dos líneas en vez de tres dejaba el título
   24 px más abajo que el de las tarjetas vecinas —el caso de Castellón junto
   a Madrid y Barcelona—. Reservando tres líneas fijas, el título cae siempre
   a la misma altura en toda la fila.
   El recorte a tres líneas evita el problema simétrico: si en alguna anchura
   una descripción se fuese a cuatro, ese título quedaría 24 px más ALTO. */
.destino-card p.destino-extra {
  min-height: 4.875em;              /* 3 líneas × 1.625 de interlineado */
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.destino-card:hover .destino-extra {
  transform: translateY(0);
  opacity: 1;
}
.destino-card .destino-cta {
  transition-delay: .1s;
}

/* ---------- Modal ---------- */
.modal {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(2, 6, 23, .85);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  padding: 1rem;
  opacity: 0;
  transition: opacity .25s ease;
}
.modal.is-open {
  opacity: 1;
}
.modal .modal-card {
  transform: translateY(20px) scale(.95);
  opacity: 0;
  transition: transform .4s cubic-bezier(.16,1,.3,1), opacity .35s ease;
}
.modal.is-open .modal-card {
  transform: translateY(0) scale(1);
  opacity: 1;
}

.modal-scroll { overflow-y: auto; max-height: 90vh; }
.modal-scroll::-webkit-scrollbar { width: 8px; display: block; }
.modal-scroll::-webkit-scrollbar-track { background: transparent; }
.modal-scroll::-webkit-scrollbar-thumb { background: rgba(59, 130, 246, .25); border-radius: 8px; }
.modal-scroll::-webkit-scrollbar-thumb:hover { background: rgba(59, 130, 246, .5); }

/* ---------- Section heading underline ---------- */
.gradient-divider {
  background: linear-gradient(90deg, transparent, rgba(59, 130, 246, .5), transparent);
}

/* ---------- Mobile menu open state ---------- */
#mobile-menu.is-open {
  display: flex;
}

/* ---------- Reduce motion ---------- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}

/* ---------- Movimiento reducido: el hero sale del scroll ----------
   La regla de arriba solo anula animaciones y transiciones CSS. El motor del
   hero es JS puro, así que sin esto quedaban 720vh de scroll con un canvas
   congelado. main.js pone data-motion="reduced" y no registra ningún
   actualizador; aquí el hero pasa a flujo normal con las cinco escenas
   visibles y legibles, una debajo de otra. */
[data-motion="reduced"] #hero-wrap { height: auto; }
[data-motion="reduced"] #hero {
  position: relative;
  height: auto;
  min-height: 100svh;
  padding-block: clamp(7rem, 14vh, 10rem);
}
[data-motion="reduced"] .hero-scenes {
  height: auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(3rem, 7vw, 5rem);
}
[data-motion="reduced"] .hero-scene {
  position: relative;
  inset: auto;
  opacity: 1;
  transform: none;
  max-width: min(100%, 46rem);
  text-align: left;
  will-change: auto;
}
[data-motion="reduced"] .hero-scene-center { text-align: center; }
[data-motion="reduced"] #hero-seam,
[data-motion="reduced"] #scroll-indicator { display: none; }

/* ===================================================================
   LIGHT THEME — post-hero sections (Partners, Services, Destinos, Footer)
   Hero stays dark/cinematic; the body switches to a clean sky palette.
   =================================================================== */

/* Sections post-hero sit OVER the body's runway-tinted background image.
   #partners starts almost fully transparent so the hero blends in seamlessly,
   and only solidifies as the user scrolls deeper into the section. */
#partners {
  background: rgba(239, 246, 255, .85);
  color: var(--light-ink);
  backdrop-filter: blur(2px);
}

/* Partners */
#partners h2 {
  color: var(--light-ink);
  text-shadow: none;
}
#partners h2 .text-transparent {
  background-image: linear-gradient(90deg, var(--accent-sky) 0%, var(--accent-sky-2) 100%) !important;
}
#partners p.text-slate-400 {
  color: var(--light-ink-2) !important;
  border-color: var(--accent-sky) !important;
}
#partners-grid {
  background: rgba(255, 255, 255, .55) !important;
  backdrop-filter: blur(4px);
  border-color: var(--light-border) !important;
  box-shadow: 0 30px 80px -40px rgba(14, 165, 233, .25);
}
.partner-card {
  background: #FFFFFF;
  border-right-color: var(--light-border) !important;
  border-bottom-color: var(--light-border) !important;
}
/* Tint the resting image in brand sky-blue. On hover, fade smoothly back
   to the original colors. Uses sepia+hue-rotate to map all hues onto a
   single blue tone (a duotone effect) without needing a CSS overlay. */
.partner-card .partner-img {
  filter:
    grayscale(.5)
    brightness(.95)
    sepia(.35)
    hue-rotate(180deg)
    saturate(.9);
  transition: filter 1s cubic-bezier(.2,.8,.2,1), transform 1.2s ease;
}
.partner-card:hover .partner-img {
  filter:
    grayscale(0)
    brightness(1)
    sepia(0)
    hue-rotate(0deg)
    saturate(1);
}
.partner-card .partner-overlay {
  background: linear-gradient(to top, rgba(15, 23, 42, .88) 0%, rgba(15, 23, 42, .25) 55%, transparent 100%);
}
.partner-card .partner-arrow {
  background: rgba(255, 255, 255, .9);
  border-color: var(--accent-sky);
  color: var(--accent-sky);
}
.partner-card:hover .partner-arrow {
  background: var(--accent-sky);
  color: #FFFFFF;
  border-color: var(--accent-sky);
}

/* Services — horizontal expertise track (pinned) */
.servicios-scroll {
  position: relative;
  /* height is set by JS on desktop to provide horizontal scroll distance */
}
#servicios-pin {
  position: sticky;
  top: 0;
  /* --vh viene de measure(): 100vh en móvil incluye la barra de URL y el pin
     acaba desbordando el viewport. */
  height: calc(var(--vh, 1vh) * 100);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  background: transparent;   /* el fondo lo pone `body`, que es fijo */
  color: var(--light-ink);
}

/* El fondo de esta sección lo pinta `body` con background-attachment: fixed,
   así que no se desliza al scrollear. Es lo que hacía a mano el antiguo
   #runway-fixed, y por eso #servicios-pin es transparente. */

/* Heading bar at the top of the pinned viewport */
.exp-head {
  position: relative;
  z-index: 2;
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 2rem;
  padding: clamp(5.5rem, 9vh, 7rem) clamp(1.5rem, 6vw, 5rem) 0;
  flex-shrink: 0;
}
.exp-headline, .exp-eyebrow { text-shadow: 0 2px 14px rgba(255, 255, 255, .6); }
.exp-eyebrow {
  display: block;
  font-size: clamp(.62rem, .8vw, .78rem);
  letter-spacing: .35em;
  text-transform: uppercase;
  color: var(--accent-sky-2);
  margin-bottom: .6rem;
}
.exp-headline {
  font-weight: 800;
  font-size: clamp(1.6rem, 3.4vw, 3rem);
  letter-spacing: -.03em;
  line-height: 1;
  color: var(--light-ink);
}
.exp-head-right {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: .8rem;
  min-width: 180px;
}
.exp-counter {
  font-size: clamp(.85rem, 1.1vw, 1rem);
  color: var(--light-ink);
  letter-spacing: .1em;
}
.exp-counter #exp-current { color: var(--accent-sky); font-weight: 600; }
.exp-counter-sep { margin: 0 .35em; color: var(--light-ink-3); }
.exp-progress {
  width: 160px;
  height: 3px;
  background: rgba(14, 165, 233, .18);
  border-radius: 999px;
  overflow: hidden;
}
#exp-progress-fill {
  display: block;
  height: 100%;
  width: 0%;
  background: linear-gradient(90deg, var(--accent-sky), var(--accent-sky-2));
  border-radius: 999px;
  transition: width .15s linear;
}

/* The horizontal track */
#servicios-track {
  position: relative;
  z-index: 2;
  display: flex;
  flex: 1 1 auto;
  align-items: center;
  will-change: transform;
}

/* El panel es el que fija la separación entre tarjetas, no un `gap`: la
   tarjeta se topa en 560px, así que con paneles de 50vw el hueco crecía con
   la pantalla —80px a 1280px, 400px a 1920px—. Topando el panel en 600 el
   hueco queda en 40px constantes por encima de 1200px de ancho, y por debajo
   vuelve a 50vw, donde las tarjetas encogen a la vez y siguen midiendo todas
   lo mismo. */
.exp-panel {
  flex: 0 0 min(50vw, 600px);
  width: min(50vw, 600px);
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 clamp(.5rem, 1vw, 1rem);
}
/* ---------- Premium interactive card (two visible over the tarmac) ---------- */
/* Todas las tarjetas miden lo mismo. Sin esto, la altura la dictaba el
   contenido: la primera tiene cinco chips en vez de cuatro, esa línea extra
   la hacía 41 px más alta y con los paneles centrados verticalmente se
   notaba el desnivel al pasar de una a otra. Ahora la altura es fija y el
   cuerpo crece para rellenar, así que la variación de texto solo cambia el
   aire de abajo, no el tamaño de la tarjeta. */
.exp-card {
  position: relative;
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: 560px;
  /* El mínimo lo marca la tarjeta más cargada (cinco chips, que ocupan dos
     líneas). Si se queda corto, esa tarjeta recorta por abajo. */
  height: clamp(545px, 64vh, 620px);
  /* Radio pequeño y un solo filete. El resplandor azul de 110px y las tres
     sombras superpuestas eran lo que hacía que la tarjeta leyera como un
     componente de aplicación en vez de como una pieza impresa. */
  border-radius: 4px;
  border: 1px solid var(--card-line);
  background: var(--card);
  overflow: hidden;
  outline: none;
  transition: transform .5s cubic-bezier(.2,.8,.2,1), border-color .4s ease;
}

.exp-card:hover,
.exp-card:focus-visible {
  transform: translateY(-4px);
  border-color: var(--ink-2);
}

/* Photo header */
.exp-card-photo {
  position: relative;
  flex: 0 0 auto;
  height: clamp(150px, 17vw, 215px);
  background: rgba(14, 165, 233, .08);
  overflow: hidden;
}
.exp-card-photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 1.1s cubic-bezier(.2,.8,.2,1);
}
.exp-card:hover .exp-card-photo img { transform: scale(1.06); }
.exp-card-photo::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(15,23,42,.15) 0%, transparent 35%, transparent 60%, rgba(255,255,255,.25) 100%);
  pointer-events: none;
}
/* Watermark number on the photo, top-right */
.exp-card-num {
  position: absolute;
  top: 1rem;
  right: 1.25rem;
  z-index: 2;
  font-size: clamp(2rem, 3.4vw, 2.9rem);
  font-weight: 800;
  line-height: 1;
  letter-spacing: -.03em;
  color: rgba(255, 255, 255, .9);
  text-shadow: 0 2px 10px rgba(15, 23, 42, .4);
}
/* Icon badge straddling the photo/body seam, bottom-left */
.exp-card-icon {
  position: absolute;
  left: clamp(1.4rem, 2.4vw, 2rem);
  bottom: 0;
  transform: translateY(50%);
  z-index: 2;
  width: clamp(50px, 4.6vw, 62px);
  height: clamp(50px, 4.6vw, 62px);
  border-radius: 3px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--page);
  border: 1px solid var(--line);
  color: var(--accent-sky-2);
  transition: color .45s ease, border-color .45s ease;
}
.exp-card-icon svg { width: 50%; height: 50%; }
.exp-card:hover .exp-card-icon,
.exp-card:focus-visible .exp-card-icon {
  border-color: var(--ink-2);
}

.exp-card-body {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  padding: clamp(2.4rem, 3.2vw, 3rem) clamp(1.8rem, 3vw, 2.6rem) clamp(1.8rem, 3vw, 2.4rem);
  background: var(--card);
}
.exp-cat {
  display: block;
  font-family: 'Archivo', sans-serif;
  font-variation-settings: 'wdth' 105;
  font-size: clamp(.66rem, .82vw, .76rem);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ink-2);
  font-weight: 500;
  margin-bottom: .8rem;
}
.exp-title {
  font-family: 'Archivo', sans-serif;
  font-variation-settings: 'wdth' 118;
  font-weight: 500;
  font-size: clamp(1.6rem, 2.8vw, 2.4rem);
  line-height: .96;
  letter-spacing: -.065em;
  color: var(--ink);
  margin-bottom: 1rem;
}
.exp-desc {
  font-family: 'Archivo', sans-serif;
  font-variation-settings: 'wdth' 105;
  font-size: clamp(.92rem, 1.02vw, 1.02rem);
  line-height: 1.55;
  letter-spacing: -.01em;
  color: var(--ink-2);
  margin-bottom: 1.6rem;
}
.exp-chips {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem;
  list-style: none;
  padding: 0;
  margin: 0;
}
.exp-chips li {
  font-family: 'Archivo', sans-serif;
  font-variation-settings: 'wdth' 105;
  font-size: .78rem;
  font-weight: 500;
  color: var(--ink-2);
  background: transparent;
  border: 1px solid var(--card-line);
  padding: .4rem .85rem;
  border-radius: 2px;
  transition: border-color .3s ease, color .3s ease;
}
.exp-card:hover .exp-chips li { border-color: var(--ink-2); color: var(--ink); }

/* Sky sheen that sweeps across on hover */
.exp-card-glow {
  position: absolute;
  top: 0;
  left: -60%;
  width: 50%;
  height: 100%;
  background: linear-gradient(105deg, transparent, rgba(255, 255, 255, .55), transparent);
  transform: skewX(-18deg);
  pointer-events: none;
  opacity: 0;
  transition: none;
}
.exp-card:hover .exp-card-glow {
  animation: exp-sheen .9s ease;
}
@keyframes exp-sheen {
  0%   { left: -60%; opacity: .9; }
  100% { left: 120%; opacity: 0; }
}

/* Scroll cue */
.exp-scrollcue {
  position: absolute;
  z-index: 3;
  bottom: 1.6rem;
  left: 50%;
  transform: translateX(-50%);
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  font-size: .65rem;
  letter-spacing: .3em;
  text-transform: uppercase;
  color: var(--accent-sky-2);
  background: rgba(255, 255, 255, .7);
  border: 1px solid rgba(14, 165, 233, .25);
  padding: .45rem .9rem;
  border-radius: 999px;
  backdrop-filter: blur(6px);
  transition: opacity .4s ease;
}
.exp-scrollcue svg { animation: exp-nudge 1.6s ease-in-out infinite; }
@keyframes exp-nudge {
  0%, 100% { transform: translateX(0); }
  50%      { transform: translateX(4px); }
}

/* ---------- Mobile: drop the pin, stack panels vertically ---------- */
@media (max-width: 768px) {
  .servicios-scroll { height: auto !important; }
  #servicios-pin {
    position: relative;
    height: auto;
    overflow: visible;
    padding-bottom: 3rem;
    background: rgba(239, 246, 255, .85);
    backdrop-filter: blur(2px);
  }
  .exp-head {
    flex-direction: column;
    align-items: flex-start;
    gap: 1rem;
  }
  .exp-head-right { align-items: flex-start; }
  #servicios-track {
    flex-direction: column;
    transform: none !important;
    gap: 1.5rem;
    padding: 2.5rem clamp(1.25rem, 6vw, 2rem) 0;
  }
  .exp-panel {
    flex: 0 0 auto;
    width: 100%;
    height: auto;
    padding: 0;
  }
  /* Cards always visible on mobile (no horizontal scroll). Aquí la altura
     vuelve a ser automática: apiladas en vertical no hay comparación lado a
     lado, y fijarla dejaría huecos grandes en las tarjetas cortas. */
  .exp-card {
    width: 100%;
    height: auto;
    opacity: 1;
    transform: none;
  }
  .exp-scrollcue { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .exp-scrollcue svg { animation: none; }
}

/* Destinos */
#destinos {
  background: rgba(239, 246, 255, .85) !important;
  backdrop-filter: blur(2px) !important;
  color: var(--light-ink);
}
#destinos > .scene-bg { display: none; }
#destinos h2 {
  color: var(--light-ink) !important;
  opacity: .06 !important;
}
#destinos p.text-cyan-400 {
  color: var(--accent-sky-2) !important;
}
.destino-card {
  border-color: var(--light-border) !important;
  box-shadow: 0 30px 60px -35px rgba(14, 165, 233, .35);
}
.destino-card .destino-overlay {
  background: linear-gradient(to top,
    rgba(15, 23, 42, .92) 0%,
    rgba(15, 23, 42, .55) 35%,
    rgba(15, 23, 42, .15) 65%,
    transparent 100%);
}
.destino-card .destino-cta {
  border-color: rgba(14, 165, 233, .7);
  color: var(--accent-sky);
}
.destino-card .destino-cta:hover {
  background: var(--accent-sky);
  color: #FFFFFF;
}

/* Footer */
footer.relative.z-10 {
  background: rgba(255, 255, 255, .9) !important;
  border-top: 1px solid var(--light-border);
  color: var(--light-ink-2);
  backdrop-filter: blur(4px) !important;
}
footer .text-white {
  color: var(--light-ink) !important;
}
footer .text-slate-500 {
  color: var(--light-ink-3) !important;
}
footer .text-slate-400 {
  color: var(--light-ink-2) !important;
}
footer a:hover {
  color: var(--accent-sky) !important;
}
footer .bg-blue-600 {
  background-color: var(--accent-sky) !important;
}

/* Section reveals look good on light bg too — no changes needed */


/* ==========================================================================
   BARRA DE CONTACTO
   Fija al borde inferior: sobrevive al scroll porque nunca entra en el flujo
   de la página. No lleva velo ni bloquea el fondo — se puede seguir leyendo
   el sitio con ella abierta, que es la diferencia entre una barra y un modal.
   ========================================================================== */
#contact-bar {
  position: fixed;
  left: clamp(.7rem, 2vw, 1.75rem);
  right: clamp(.7rem, 2vw, 1.75rem);
  bottom: clamp(.7rem, 2vw, 1.75rem);
  z-index: 45;
  background: var(--light-surface);
  border: 1px solid var(--line);
  border-radius: 4px;
  box-shadow: 0 18px 50px rgba(15, 23, 42, .16);
  /* Cerrada: fuera de pantalla Y sin visibilidad, para que no se pueda tabular
     hasta sus campos. El retardo de `visibility` deja terminar la salida. */
  transform: translateY(calc(100% + 2.5rem));
  opacity: 0;
  visibility: hidden;
  transition: transform .55s cubic-bezier(.2,.8,.2,1), opacity .3s ease, visibility 0s .55s;
}
#contact-bar.is-open {
  transform: translateY(0);
  opacity: 1;
  visibility: visible;
  transition: transform .55s cubic-bezier(.2,.8,.2,1), opacity .3s ease, visibility 0s;
}

.contact-form {
  position: relative;
  display: grid;
  grid-template-columns: minmax(160px, .75fr) 3fr auto;
  align-items: end;
  gap: clamp(1.1rem, 2.4vw, 2.4rem);
  padding: clamp(1.4rem, 2.2vw, 1.9rem) clamp(1.4rem, 2.4vw, 2.4rem);
}

.contact-intro { display: flex; flex-direction: column; gap: .35rem; }
.contact-eyebrow {
  font-variation-settings: 'wdth' 118;
  font-weight: 500;
  font-size: clamp(1.1rem, 1.6vw, 1.5rem);
  letter-spacing: -.05em;
  color: var(--ink);
}
.contact-note { font-size: .82rem; line-height: 1.4; color: var(--ink-2); }

.contact-fields {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: clamp(.9rem, 1.8vw, 1.9rem);
}
.contact-field { display: flex; flex-direction: column; gap: .4rem; }
.contact-field > span {
  font-size: .66rem;
  font-weight: 500;
  font-variation-settings: 'wdth' 105;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ink-2);
}
/* Campo subrayado, sin caja ni esquinas: la referencia no usa un solo
   recuadro relleno, y sobre superficie blanca el filete basta. */
.contact-field input {
  width: 100%;
  border: 0;
  border-bottom: 1px solid var(--line);
  border-radius: 0;
  background: transparent;
  padding: .45rem 0 .5rem;
  font-family: 'Archivo', sans-serif;
  font-size: 1rem;
  color: var(--ink);
  outline: none;
  transition: border-color .3s ease;
}
.contact-field input:focus { border-bottom-color: var(--accent-sky); }
.contact-field input:focus-visible { outline: none; }

.contact-send {
  border: 1px solid var(--ink);
  background: var(--ink);
  color: #fff;
  padding: .9rem 2.1rem;
  font-family: 'Archivo', sans-serif;
  font-size: .76rem;
  font-weight: 500;
  letter-spacing: .12em;
  text-transform: uppercase;
  border-radius: 2px;
  cursor: pointer;
  white-space: nowrap;
  transition: background .3s ease, color .3s ease;
}
.contact-send:hover { background: transparent; color: var(--ink); }
.contact-send[disabled] { opacity: .5; cursor: default; }

.contact-status {
  grid-column: 1 / -1;
  margin: 0;
  font-size: .85rem;
  color: var(--ink-2);
  min-height: 0;
}
.contact-status:empty { display: none; }
.contact-status[data-state="error"] { color: #B4341F; }

#contact-close {
  position: absolute;
  top: .55rem;
  right: .6rem;
  width: 2rem;
  height: 2rem;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 0;
  background: none;
  color: var(--ink-2);
  cursor: pointer;
  transition: color .3s ease;
}
#contact-close:hover { color: var(--ink); }

/* Trampa antispam de Netlify: tiene que existir en el DOM y ser rellenable
   por un robot, pero invisible para una persona. `display:none` no vale —
   algunos robots lo detectan. */
.contact-hp { position: absolute; left: -9999px; top: auto; width: 1px; height: 1px; overflow: hidden; }

@media (max-width: 1100px) {
  .contact-form { grid-template-columns: 1fr; gap: 1.1rem; }
  .contact-fields { grid-template-columns: 1fr 1fr; }
  .contact-send { justify-self: start; }
}
@media (max-width: 560px) {
  .contact-fields { grid-template-columns: 1fr; }
  .contact-send { width: 100%; }
  /* En pantallas cortas la barra puede pasarse de alto: que haga scroll
     dentro de sí misma en vez de salirse por arriba. */
  #contact-bar { max-height: 86vh; overflow-y: auto; }
}

/* ==========================================================================
   INTRO — réplica de la entrada de jeskojets.com
   El estado inicial del velo vive en un <style> en línea del <head>: la hoja
   externa llega una vuelta de red más tarde y se vería la página un instante
   antes de que cayera el telón, que es justo lo que el intro evita.
   ========================================================================== */
#intro { position: fixed; inset: 0; z-index: 9998; pointer-events: none; }

/* ---------- 1. El velo oscuro con el título ---------- */
/* Se retira con una máscara de degradado que se desliza, que es el
   equivalente en CSS puro de su animación de --mask-size/--mask-y. Va también
   la opacidad: si algún navegador no interpolase mask-position, el velo
   seguiría desapareciendo con un fundido en lugar de saltar de golpe. */
#intro-veil {
  -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 45%, transparent 78%);
          mask-image: linear-gradient(to bottom, #000 0%, #000 45%, transparent 78%);
  -webkit-mask-size: 100% 220%;
          mask-size: 100% 220%;
  -webkit-mask-position: 50% 0%;
          mask-position: 50% 0%;
  transition:
    -webkit-mask-position 1s cubic-bezier(.76, 0, .24, 1),
            mask-position 1s cubic-bezier(.76, 0, .24, 1),
    opacity .8s ease .2s;
}
#intro-veil.is-gone {
  -webkit-mask-position: 50% 100%;
          mask-position: 50% 100%;
  opacity: 0;
}

#intro-copy {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: clamp(.7rem, 1.4vw, 1.1rem);
  padding-inline: 1.5rem;
  text-align: center;
}

/* blur(36px) es el valor literal de la referencia. El desenfoque es lo que
   hace que el texto parezca materializarse en vez de solo aparecer. */
.intro-line {
  margin: 0;
  opacity: 0;
  filter: blur(36px);
  transition: opacity .66s ease, filter .66s ease;
  will-change: opacity, filter;
}
.intro-line.is-in { opacity: 1; filter: blur(0); }

.intro-brand {
  font-variation-settings: 'wdth' 118;
  font-weight: 500;
  font-size: clamp(1.9rem, 5.4vw, 5rem);
  line-height: .95;
  letter-spacing: .015em;   /* positivo: en versales el tracking cerrado ahoga */
  text-transform: uppercase;
  color: #F1F5F9;
}
.intro-tagline {
  font-variation-settings: 'wdth' 105;
  font-weight: 400;
  font-size: clamp(.9rem, 1.35vw, 1.15rem);
  line-height: 1.45;
  letter-spacing: .01em;
  max-width: 34rem;
  color: #94A3B8;
}

/* ---------- 2. La ventanilla y su cortinilla ---------- */
/* La caja la coloca JS midiendo el fotograma 0; aquí solo va la forma. El
   radio en porcentaje —y no en píxeles— mantiene el óvalo tanto si la
   ventanilla que se detecta es vertical como apaisada. */
#intro-window {
  position: absolute;
  overflow: hidden;
  border-radius: 50% / 20%;        /* el mismo perfil que #hero-window */
  opacity: 0;                      /* hasta que JS la mide y la coloca */
  transition: opacity .25s ease;
}
#intro-window.is-placed { opacity: 1; }

#intro-shade {
  position: absolute;
  inset: 0;
  background: #DBDBDB;             /* el gris exacto de la cortinilla de Jesko */
  transform: translateY(0);
  transition: transform 1.15s cubic-bezier(.76, 0, .24, 1);
  will-change: transform;
}
#intro-shade.is-open { transform: translateY(-100%); }

/* El tirador: en la referencia es una barra de 16 px pegada al borde inferior
   de la cortinilla, y sube con ella. Es el detalle que convierte un panel
   gris en una ventanilla. */
#intro-grip {
  position: absolute;
  left: 50%;
  bottom: 6%;
  transform: translateX(-50%);
  width: 34%;
  height: 7px;
  min-height: 5px;
  border-radius: 4px;
  background: #B4B4B4;
}

/* Sin movimiento no hay telón: el intro se salta entero desde JS, pero si
   algo fallara esto garantiza que nunca quede la pantalla tapada. */
@media (prefers-reduced-motion: reduce) {
  #intro { display: none !important; }
  html.intro-active, html.intro-active body { overflow: visible; }
}


/* ==========================================================================
   VENTANILLA DEL HERO — marco dibujado, no filmado
   Réplica de jeskojets.com: relación 0,725 · 35,7% del ancho · 52,3% del
   alto · centrada. Verificado contra la suya con 2 px de diferencia.
   ========================================================================== */

/* ---------- Nubes en movimiento continuo ----------
   Debajo del fuselaje, así que solo asoman por el hueco. El vídeo del hero
   lo mueve el scroll, o sea que con la página quieta se queda congelado; esta
   capa es la que da vida al primer pantallazo, que es justo donde la gente se
   detiene a leer. Es también lo que hace la referencia: una tira repetida en
   una lista que se desplaza en bucle. */
#hero-clouds {
  position: absolute;
  inset: 0;
  z-index: 1;
  overflow: hidden;
  pointer-events: none;
  opacity: var(--clouds-op, 1);
}
#hero-clouds-track {
  position: absolute;
  inset: 0;
  display: flex;
  width: 400%;                       /* dos mitades de dos pantallas cada una */
  animation: nubes-deriva 150s linear infinite;
}
#hero-clouds-track span {
  flex: 0 0 50%;
  background: url('assets/hero/clouds.webp') center/cover no-repeat;
}
/* -50% del carril = exactamente el ancho de una mitad, así que al terminar el
   ciclo la segunda copia está donde arrancó la primera. Sin salto. */
@keyframes nubes-deriva {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}

/* ---------- Fuselaje y hueco ---------- */
#hero-window {
  position: absolute;
  top: 49%;                        /* su centro medido, no 50% */
  left: 50.4%;
  /* La altura manda y el ancho sale de la relación: el canvas recorta con
     `cover` ajustando por altura, así que atar la ventanilla al alto la
     mantiene proporcional al cielo que se ve por ella. A 910px de alto esto
     da 473×343, y lo medido en su web fue 476×345. */
  height: clamp(260px, 52vh, 620px);
  aspect-ratio: .725;
  /* Laterales rectos con arriba y abajo muy redondeados. El 50%/20% deja el
     60% central de la altura recto, que es la proporción de su contorno. */
  border-radius: 50% / 20%;
  z-index: 3;                      /* sobre las nubes, bajo el velo y la costura */
  pointer-events: none;

  /* El fuselaje. Muestreé su capa por anillos de distancia al hueco y no es
     un color plano, es una caída: #A39C96 pegado al cristal → #6D6258 →
     #50463B → #322A20 en los bordes. Un degradado radial no serviría porque
     seguiría una elipse, y el hueco es un rectángulo redondeado; las sombras
     apiladas SÍ siguen el border-radius, así que la caída abraza la forma
     real de la ventanilla. La última, con 100vmax de extensión, rellena todo
     lo que quede de pantalla.

     La caída es la suya trasladada a nuestro azul: mantiene los mismos
     escalones de luminancia (0,60 → 0,34 → 0,20) pero termina justo en
     #0F172A, que es el color con el que arranca el velo del intro. Así la
     cabina no cambia de tono cuando el telón se retira: es el mismo azul
     antes y después. */
  box-shadow:
    inset 0 0 0 2px rgba(255, 255, 255, .22),
    inset 0 0 34px 10px rgba(0, 0, 0, .28),
    0 0 70px 24px #8C99AD,
    0 0 0 64px #4A5872,
    0 0 150px 78px #26334C,
    0 0 0 100vmax #0F172A;

  transform: translate(-50%, -50%) scale(var(--win-sc, 1));
  opacity: var(--win-op, 1);
  will-change: transform, opacity;
}

/* Reflejo suave en el cristal: dos manchas de luz en diagonal. Es lo que
   impide que el hueco se lea como un recorte de cartulina. */
#hero-window-glare {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background:
    linear-gradient(118deg,
      rgba(255, 255, 255, .26) 0%,
      rgba(255, 255, 255, .09) 26%,
      rgba(255, 255, 255, 0) 42%,
      rgba(255, 255, 255, 0) 68%,
      rgba(255, 255, 255, .12) 84%,
      rgba(255, 255, 255, .03) 100%);
  pointer-events: none;
}

/* En móvil el viewport es mucho más alto que ancho: con 52vh la ventanilla se
   saldría de lado. Manda el ancho. */
@media (max-width: 768px) {
  #hero-window {
    height: auto;
    width: min(62vw, 320px);
    aspect-ratio: .725;
  }
}

@media (prefers-reduced-motion: reduce) {
  #hero-window { display: none; }   /* sin recorrido no hay travesía del cristal */
  #hero-clouds { display: none; }
  #hero-clouds-track { animation: none; }
}

/* ---------- Las dos declaraciones que flanquean la ventanilla ----------
   Registro de jeskojets.com: peso 500, caja de frase, tracking muy cerrado.
   Sus «We are movement» / «We are distinction» miden 103,87 px con -0,08em.

   El cuerpo NO es un número fijo. El lateral disponible depende del hueco que
   deja la ventanilla, que a su vez depende de la altura del viewport, así que
   cualquier valor escrito a mano acierta en una pantalla y falla en la
   siguiente. Aquí se calcula: --hueco es lo que queda a cada lado, y el
   cuerpo es esa medida partida por la palabra más larga. */
#hero {
  --win-w:  calc(clamp(260px, 52vh, 620px) * .725);   /* igual que #hero-window */
  --al-cristal: clamp(1.5rem, 3vw, 3rem);             /* aire hasta el vidrio */
  --al-borde:   clamp(1rem, 2.5vw, 3.5rem);           /* aire hasta la pantalla */
  --hueco: calc((100vw - var(--win-w)) / 2 - var(--al-cristal) - var(--al-borde));
}

#hero-sides {
  position: absolute;
  inset: 0;
  z-index: 4;                       /* sobre el fuselaje, bajo el velo */
  pointer-events: none;
  opacity: 0;
  transition: opacity .9s ease .35s;
}
/* La clase la quita el motor del intro al acabar; hasta entonces, invisibles:
   si no, asomarían por debajo del telón. */
html:not(.intro-active) #hero-sides { opacity: var(--sides-op, 1); }

.hero-side {
  position: absolute;
  top: 49%;                         /* el mismo centro que la ventanilla */
  transform: translateY(-50%);
  width: var(--hueco);
  margin: 0;
  font-variation-settings: 'wdth' 118;
  font-weight: 500;
  /* 4,25 es lo que ocupa «Portugal» —la palabra más larga de las cuatro— en
     ems a este ancho y tracking. El clamp evita que se dispare en pantallas
     muy anchas y que se vuelva ilegible en las estrechas. */
  font-size: clamp(1.6rem, calc(var(--hueco) / 4.25), 7rem);
  line-height: .88;
  letter-spacing: -.08em;           /* el suyo, exacto */
  color: #F1F5F9;
  text-wrap: balance;
}
/* 49,6% y no 50,4%: `right` se mide desde el borde OPUESTO, así que el espejo
   del centro de la ventanilla es 100% − 50,4%. */
.hero-side-l {
  right: calc(49.6% + var(--win-w) / 2 + var(--al-cristal));
  text-align: right;
}
.hero-side-r {
  left: calc(50.4% + var(--win-w) / 2 + var(--al-cristal));
  text-align: left;
}

/* Por debajo de 900 px el lateral no da para tipografía grande: las dos
   líneas pasan a leerse una sobre otra bajo la ventanilla, como una sola
   frase, que es como está escrita. */
@media (max-width: 900px) {
  .hero-side {
    position: static;
    transform: none;
    width: auto;
    font-size: clamp(1.7rem, 9vw, 3rem);
    text-align: center;
    letter-spacing: -.07em;
  }
  .hero-side br { display: none; }
  #hero-sides {
    top: auto;
    bottom: 7%;
    height: auto;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .1em;
    padding-inline: 1.25rem;
  }
}

@media (prefers-reduced-motion: reduce) {
  #hero-sides { opacity: 1; }
}

/* ==========================================================================
   TICKETING — los sellos de agencia autorizada, bajo la retícula de clientes
   Cada sello va sobre baldosa blanca con el mismo alto y el mismo aire. Los
   originales llegan dispares —unos con fondo blanco incrustado, otros con
   alfa, y el del Athletic es un sello plateado circular—, así que sin la
   baldosa la tira se leería como un recorte de periódico. Normalizarlos es
   lo que hace que parezcan un sistema y no siete archivos sueltos.
   ========================================================================== */
#ticketing {
  margin-top: clamp(2.25rem, 4.5vw, 4rem);
  padding: clamp(1.4rem, 2.4vw, 2rem) 1rem 0;
  border-top: 1px solid var(--line);
  text-align: center;
}

.ticketing-label {
  display: block;
  margin-bottom: clamp(1rem, 1.8vw, 1.5rem);
  font-variation-settings: 'wdth' 105;
  font-weight: 500;
  font-size: clamp(.62rem, .78vw, .72rem);
  letter-spacing: .17em;
  text-transform: uppercase;
  color: var(--accent-sky-2);
}

.ticketing-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;   /* centrados, no alineados a la izquierda */
  gap: clamp(.5rem, 1vw, .85rem);
}
.ticketing-list li {
  display: flex;
  align-items: center;
  justify-content: center;
  /* Alto fijo y ancho libre: los sellos van de 1:1 —el circular del Athletic—
     a 2,8:1 —la banda del Betis—, y forzar un ancho común dejaría a los
     cuadrados diminutos. */
  height: clamp(58px, 7vh, 76px);
  padding: 0 clamp(.7rem, 1.2vw, 1.1rem);
  background: #FFFFFF;
  border: 1px solid var(--card-line);
  border-radius: 3px;
  transition: border-color .3s ease, transform .3s ease;
}
.ticketing-list li:hover {
  border-color: var(--accent-sky);
  transform: translateY(-2px);
}
.ticketing-list img {
  height: clamp(34px, 4.4vh, 46px);
  width: auto;
  max-width: clamp(110px, 14vw, 170px);
  object-fit: contain;
  display: block;
}

@media (max-width: 640px) {
  .ticketing-list { gap: .45rem; }
  .ticketing-list li { height: 52px; padding: 0 .6rem; }
  .ticketing-list img { height: 30px; max-width: 96px; }
}

/* La dirección, a la vista dentro de la barra de contacto. */
.contact-mail {
  font-size: .78rem;
  color: var(--ink-2);
  text-decoration: none;
  border-bottom: 1px solid var(--line);
  padding-bottom: 1px;
  align-self: flex-start;
  transition: color .3s ease, border-color .3s ease;
}
.contact-mail:hover { color: var(--ink); border-bottom-color: var(--ink); }

/* El titular de destino, con dos líneas reservadas. Mismo motivo que la
   descripción: el bloque va anclado abajo, así que un nombre que parte en dos
   —«San Sebastián» por debajo de 1100 px— subía su título 40 px respecto a
   los vecinos. Reservando el hueco de dos líneas, todos los nombres arrancan
   a la misma altura ocupen una o dos.
   `lh` es la unidad de interlineado calculado: evita tener que replicar a
   mano el line-height, que además cambia de tamaño entre breakpoints. */
.destino-card .destino-content h3 {
  min-height: 2em;    /* reserva para navegadores sin unidad lh */
  min-height: 2lh;
}

/* ==========================================================================
   CLIENTES — centrado de la última fila
   Con cinco fichas en una retícula de tres, la sexta celda quedaba vacía a la
   derecha y, como la retícula dibuja líneas continuas, ese hueco se leía como
   una tarjeta en blanco.

   El truco: se duplica el número de columnas y cada ficha ocupa dos. Eso deja
   media columna de maniobra, que es justo lo que hace falta para desplazar la
   última fila y repartir el sobrante a los dos lados. La alternativa —pasar a
   flexbox con `justify-content: center`— obligaría a fijar anchos a mano y se
   rompería en cada breakpoint.
   ========================================================================== */
@media (min-width: 1024px) {
  #partners-grid { grid-template-columns: repeat(6, 1fr); }
  #partners-grid > .partner-card { grid-column: span 2; }
  /* La cuarta abre fila una media celda más adentro; la quinta la sigue. */
  #partners-grid > .partner-card:nth-child(4) { grid-column: 2 / span 2; }
}

/* A dos columnas la descolgada es la quinta, sola en su fila. */
@media (min-width: 768px) and (max-width: 1023.98px) {
  #partners-grid { grid-template-columns: repeat(4, 1fr); }
  #partners-grid > .partner-card { grid-column: span 2; }
  #partners-grid > .partner-card:nth-child(5) { grid-column: 2 / span 2; }
}
