/* =========================================================================
   GALOPANTE MEDIA — Intro interactiva + Hero
   FASE 1: sólo estilos necesarios para el pin + scrub del Video 1.
   Las clases de las secuencias (seq1, seq2, seq3, seq4, hero) se agregan
   en las fases siguientes; acá sólo dejamos la paleta y el reset listos
   para que no haya que reescribir nada después.
   ========================================================================= */

:root {
  /* Paleta corporativa Galopante — única fuente de verdad en CSS.
     No introducir colores nuevos sin necesidad (sección 5 del brief). */
  --petro-galopante: #09121C;
  --aquamarina: #B3ECE6;
  --coral: #EB455D;
  --hueso: #F1F0F0;
  --cuarzo-ahumado: #89837A;
}

* {
  box-sizing: border-box;
}

html, body {
  margin: 0;
  padding: 0;
  background: var(--petro-galopante);
  color: var(--hueso);
  font-family: 'Montserrat', -apple-system, BlinkMacSystemFont, sans-serif;
  overflow-x: hidden;
}

/* Preparado para GOAT vía @font-face en cuanto estén los archivos web.
   No sustituir automáticamente por otra tipografía (sección 6). */
@font-face {
  font-family: 'GOAT';
  src: url('../assets/fonts/GOAT.woff2') format('woff2'),
       url('../assets/fonts/GOAT.woff') format('woff');
  font-weight: normal;
  font-style: normal;
  font-display: swap;
}

/* -------------------------------------------------------------------
   VIDEO 1 — sección pinneada, controlada por scroll

   OJO: #intro-scrub NO define su propia altura acá. La distancia de
   scroll (cuántos vh dura el recorrido completo) es una única fuente
   de verdad: GALOPANTE_CONFIG.SCROLL.distanceVh en config.js. GSAP
   (ScrollTrigger pin + pinSpacing) es quien reserva ese espacio en el
   documento automáticamente. Si esta sección tuviera además una
   altura fija en CSS, el espacio de scroll quedaría duplicado.
   ------------------------------------------------------------------- */
#intro-scrub {
  position: relative;
  width: 100%;
}

#intro-scrub .pin-wrap {
  position: relative;
  width: 100vw;
  height: 100vh;
  overflow: hidden;
  /* Mismo color que el primer frame del Video 1: evita cualquier
     flash negro mientras el video todavía está cargando metadata. */
  background: var(--coral);
}

#video1 {
  position: absolute;
  inset: 0;
  width: 100vw;
  height: 100vh;
  object-fit: cover;
  /* El video nunca se reproduce por sí mismo: sólo currentTime. */
  pointer-events: none;
}

/* -------------------------------------------------------------------
   SECUENCIA 1 — "scrolleá hacia el origen de las buenas ideas"

   Capa HTML por encima del video (mismo sistema de coordenadas
   fullscreen). pointer-events: none porque es puramente decorativa —
   nunca debe interceptar el scroll ni clicks futuros.
   ------------------------------------------------------------------- */
.seq {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
}

.seq1 {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
}

/* FASE 7.3 — isotipo de Galopante arriba de "scrolleá", pedido del
   cliente con una imagen de referencia (ver también sequence1.js: se
   anima en el MISMO timeline que .seq1-copy/.seq1-arrow, así que
   desaparece junto con el resto apenas arranca el scroll, no por
   separado). Medido sobre la referencia: ancho ~95px sobre 1920px de
   viewport (~5vw) — de ahí el clamp de abajo.

   FASE 7.4 — pedido del cliente: no quería un margen fijo respecto a
   "scrolleá", sino el isotipo centrado en el espacio vacío entre el
   borde superior de la pantalla y donde arranca "scrolleá". Por eso
   pasa a position:absolute (sale del flujo de flexbox de .seq1 — así
   .seq1-copy/.seq1-arrow se centran solos como si el isotipo no
   existiera, que es justo la referencia para medir ese hueco) y
   sequence1.js le calcula el "top" en cada carga/resize — ver
   positionIsotype() ahí. */
.seq1-isotype {
  position: absolute;
  /* top real lo calcula sequence1.js (positionIsotype) midiendo el
     hueco real hasta "scrolleá" — este 15% es sólo el valor de
     arranque antes de que corra ese cálculo, para que no haya un
     salto visible apenas carga la página. */
  top: 15%;
  left: 50%;
  width: clamp(3.5rem, 5vw, 6rem);
  height: auto;
  will-change: transform, opacity;
}

.seq1-copy {
  color: var(--hueso);
  will-change: transform, opacity;
}

.seq1-line {
  margin: 0;
}

.seq1-line--lead {
  font-weight: 300;
  font-size: clamp(2.75rem, 6vw, 4.5rem);
  letter-spacing: 0.01em;
  line-height: 1;
  margin-bottom: 1.25rem;
}

.seq1-line--sub {
  font-weight: 400;
  font-size: clamp(1.15rem, 2.1vw, 1.5rem);
  line-height: 1.4;
  letter-spacing: 0.005em;
}

.seq1-arrow {
  margin-top: 3rem;
  color: var(--hueso);
  will-change: transform, opacity;
}

.seq1-arrow-glyph {
  display: block;
  will-change: transform;
}

/* -------------------------------------------------------------------
   SECUENCIA 2 — "BIENVENIDO / A [logo]" (antes "GALOPAMOS / MARCAS")

   Sin fondo sólido detrás (se descartó): el texto queda directamente
   sobre el Video 1.

   .seq2-copy: BIENVENIDO arriba, "A [logo]" abajo, sin gap entre
   ambos — el borde compartido es el "eje horizontal invisible" del
   brief. Cada línea se revela con clip-path (ver sequence2.js), nunca
   con opacity, y vuelve a cerrarse (salida) antes de que termine el
   capítulo "galopamos". Los nombres de clase (--galopamos/--marcas)
   quedaron igual que antes del cambio de copy (FASE 9.2): sequence2.js
   selecciona por clase, no por el texto que contienen.
   ------------------------------------------------------------------- */
.seq2 {
  display: flex;
  align-items: center;
  justify-content: center;
}

.seq2-copy {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}

.seq2-word {
  margin: 0;
  line-height: 1;
  letter-spacing: 0.01em;
  color: var(--hueso);
}

.seq2-word--galopamos {
  font-weight: 300;
  /* Subido (pedido del cliente, con una imagen de referencia): el techo
     viejo (4.25rem) frenaba el tamaño mucho antes de lo que el 6vw
     hubiera dado en pantallas anchas — por eso se veía chico en
     desktop. Medido sobre la referencia (altura de mayúscula ~130px
     sobre una imagen de 1920x1080, prácticamente IGUAL entre GALOPAMOS
     y MARCAS pese al distinto peso) y llevado a clamp() para que siga
     respondiendo al viewport en vez de quedar fijo.
     FASE 9.2.7 — el cliente pidió achicar el sobreimpreso "a la
     mitad": clamp(2.75rem, 9.5vw, 10rem) → clamp(1.375rem, 4.75vw,
     5rem) (los tres valores exactamente a la mitad, así se mantiene
     la misma curva de escalado responsive, sólo que más chica). */
  font-size: clamp(1.375rem, 4.75vw, 5rem);
}

.seq2-word--marcas {
  /* FASE 9.2.7 — el cliente pidió que la "A" esté en Montserrat Light
     "tal como BIENVENIDO": 800 → 300, igual que .seq2-word--galopamos
     de acá arriba. Sólo afecta a la "A" (el peso de fuente no aplica
     sobre la imagen del logo). */
  font-weight: 300;
  /* Mismo criterio que .seq2-word--galopamos: los tres valores del
     clamp a la mitad. */
  font-size: clamp(1.5rem, 4.75vw, 5rem);
  /* FASE 9.2: ahora es "A" + el isotipo/wordmark en vez de la palabra
     MARCAS — se vuelve un contenedor flex en vez de texto plano para
     poder alinear verticalmente la letra "A" con el logo. El clip-path
     y el yPercent de sequence2.js siguen aplicando sobre esta misma
     caja (el mecanismo de revelación no distingue display:flex de
     texto normal), así que la animación no se tocó. */
  display: flex;
  align-items: flex-end;
  justify-content: center;
  gap: 0.18em;
  /* FASE 9.2.5 — el cliente pidió agrandar el logo "hasta la O de
     BIENVENIDO" (FASE 9.2.3) y eso se logró con margin-top negativo en
     este renglón, pero al verlo en vivo pidió lo contrario: "A
     Galopante debe quedar más abajo pq está superpuesto a BIENVENIDO
     ... como si fuese una misma línea de texto sobre un renglón" — o
     sea, SIN superposición entre los dos renglones, cada uno en su
     propio lugar (el logo agrandado alcanza igual bastante altura por
     sí solo — ver .seq2-word__logo más abajo — así que ya no hace
     falta forzarlo a invadir el renglón de arriba). Se saca el
     margin-top negativo; el renglón vuelve a su posición natural
     dentro de .seq2-copy (flex-column), sin tocar nada del mecanismo
     de alineación interna "A"/"Galopante" (eso sigue en
     .seq2-word__a, más abajo, sin cambios). */
}

.seq2-word__a {
  /* FASE 9.2.4 — el logo trae "Galopante" (+ el punto rojo) arriba y
     "media" chico abajo, como una sola imagen. El cliente pidió que el
     renglón de abajo de "Galopante" (no el de "media") quede alineado
     con la "A", y que "media" cuelgue suelto debajo de todo el
     bloque. Medido en el PNG real (assets/images/logo-galopante-
     wordmark-white.png, 3268×640): "Galopante" + el punto terminan en
     el pixel 431 de 640 (67.34% del alto de la imagen) — el 32.66%
     restante es el hueco + "media".

     OJO — por qué esto vive en la "A" y no en la imagen: la primera
     versión de este ajuste le daba margin-bottom NEGATIVO a la
     imagen para "estirarla" hacia abajo más allá de su propio
     renglón (y así asomar "media"), pero .seq2-word--marcas queda con
     clip-path: inset(0%) fijo como estado "revelado" de la animación
     de sequence2.js — y clip-path SIEMPRE recorta al borde exacto de
     la caja del elemento, aunque sea "0% de inset" (no es lo mismo
     que clip-path:none). Cualquier desborde -la imagen agrandada, o
     "media"- quedaba invisible, recortado por esa máscara. La imagen
     ahora vive ENTERA dentro de su propio renglón (sin overflow), y en
     cambio es la "A" la que se corre hacia arriba con este
     margin-bottom POSITIVO (empuja su propio borde inferior hacia
     arriba dentro del renglón, sin sacarla de la caja) hasta calzar
     con el final real de "Galopante" — así nada se desborda de
     .seq2-word--marcas y el clip-path nunca tiene nada que recortar. */
  margin-bottom: 0.36em;
}

.seq2-word__logo {
  /* Alto en em (relativo al font-size fluido de .seq2-word--marcas)
     para seguir escalando en cualquier breakpoint. Sin "width" fijo:
     así el navegador respeta el aspect-ratio intrínseco del PNG. Este
     elemento NUNCA lleva margin negativo (ver el comentario grande en
     .seq2-word__a arriba) — toda la imagen, "media" incluida, queda
     siempre dentro de su propio renglón. */
  height: 1.3em;
  width: auto;
  display: block;
}

/* -------------------------------------------------------------------
   SECUENCIA 3 — "HACEMOS" + disciplinas

   .seq3-exit-mask: máscara exterior (clip-path animado sólo en la
   salida). .seq3-block: HACEMOS + disciplinas como bloque único, es lo
   que se desplaza hacia arriba en la salida. .seq3-hacemos-wrap: su
   propia máscara de revelación (independiente de la de salida).
   .seq3-discipline-wrap: overflow:hidden individual por disciplina.
   ------------------------------------------------------------------- */
.seq3 {
  display: flex;
  align-items: center;
  justify-content: center;
}

.seq3-exit-mask {
  display: flex;
}

.seq3-block {
  display: flex;
  flex-direction: row;
  align-items: center;
  /* HACEMOS pegado casi contra el bloque tipográfico (pedido del
     cliente: "más grande y más cerca de este bloque"). */
  gap: clamp(0.3rem, 1vw, 0.75rem);
}

.seq3-hacemos-wrap {
  overflow: hidden;
  display: flex;
  align-items: center;
}

.seq3-hacemos {
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.35em;
  /* Vuelto a achicar (pedido del cliente, según referencia): HACEMOS
     acompaña al bloque tipográfico pero no compite en peso visual con
     él — es una etiqueta lateral, no un título más. */
  font-size: clamp(0.85rem, 1.3vw, 1.1rem);
  color: var(--hueso);
  white-space: nowrap;
}

/* Línea delgada entre HACEMOS y el bloque ESTRATEGIA/BRANDING/CONTENIDO/
   GROWTH (pedido del cliente, según referencia). Se estira a la altura
   del wrap de HACEMOS (align-self: stretch dentro de .seq3-block, que
   es flex row align-items: center) para leerse como un separador, no
   como un adorno suelto. */
.seq3-divider {
  align-self: stretch;
  width: 1px;
  background: var(--hueso);
  opacity: 0.35;
}

/* -------------------------------------------------------------------
   Bloque tipográfico ESTRATEGIA/BRANDING/CONTENIDO/GROWTH

   Igual que en la referencia del cliente: cada palabra tiene su propio
   tamaño de fuente (no un tamaño único para las 4), calculado para que
   las 4 líneas ocupen un ancho similar entre sí y el conjunto se lea
   como un bloque prolijo — no como una lista con tamaños parejos.
   align-items: flex-start alinea los 4 renglones por su borde
   IZQUIERDO (igual que la referencia — todas las palabras arrancan
   pegadas a la línea divisoria/HACEMOS), dejando el borde derecho
   escalonado según el largo de cada palabra.

   FASE 30 — color: originalmente cada palabra tenía un color distinto
   (Estrategia hueso, Branding aquamarina, Contenido coral, Growth
   cuarzo ahumado) para diferenciarlas visualmente. A pedido del
   cliente ("podras dejar todas las letras color hueso?") las 4 pasan
   a compartir el mismo color var(--hueso) — sólo cambia el color,
   los tamaños por palabra se mantienen igual que antes.
   ------------------------------------------------------------------- */
.seq3-disciplines {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: clamp(0.15rem, 0.6vw, 0.4rem);
}

.seq3-discipline-wrap {
  overflow: hidden;
}

.seq3-discipline {
  display: block;
  font-weight: 800;
  line-height: 1.05;
  letter-spacing: 0.01em;
  white-space: nowrap;
}

.seq3-discipline--estrategia {
  color: var(--hueso);
  font-size: clamp(2rem, 5.5vw, 3.75rem);
}

.seq3-discipline--branding {
  color: var(--hueso);
  font-size: clamp(2.3rem, 6.4vw, 4.4rem);
}

.seq3-discipline--contenido {
  color: var(--hueso);
  font-size: clamp(2.1rem, 5.75vw, 4rem);
}

.seq3-discipline--growth {
  color: var(--hueso);
  font-size: clamp(2.75rem, 7.5vw, 5.25rem);
}

/* -------------------------------------------------------------------
   SECUENCIA 4 — "POTENCIAMOS" (letra por letra, detrás de Galopardo)

   FASE 7 — migrado desde dev/potenciamos-prototype.html (versión v3.8,
   la aprobada tras varias rondas de prueba — ver NOTES.md). El propio
   prototipo aislado sigue en dev/ como referencia histórica, pero deja
   de tocarse: esta es ahora la única versión que se edita.

   #seq4-bg-frame: un freeze-frame de Video 1 (exportado del mismo
   frame exacto al que llega el scrub cuando el progreso del scroll
   maestro es 1 — ver VIDEO1.trimEndFrames en config.js). Tapa a
   #video1 apenas arranca este capítulo: el recorte de Galopardo
   (#seq4-fg-mask) sólo calza pixel-perfect con ESE frame, y #video1
   sigue recibiendo su propio currentTime (mapeado 1:1 al progreso
   0..1 del scroll maestro, no al capítulo) durante todo este tramo —
   no se pausa ni se le pide nada especial. En vez de tocar esa lógica
   (que ya está pixel-perfect calibrada para el final del recorrido),
   es más simple y más seguro taparla con esta imagen fija — ver el
   corte casi instantáneo de opacity en sequence4.js.

   #seq4-text / #seq4-word / .letter: mismo modelo 3D que el prototipo
   — "fila india" (sharedMotion en sequence4.js), perspective 1600px,
   cada letra con su propia cara + canto (dos capas en Z, ver
   .letter-face/.letter-edge).

   BUG encontrado en vivo (POTENCIAMOS no aparecía detrás de la cabra
   — ver NOTES.md) y su fix: Chrome NO ordena por profundidad real
   entre hermanos que están a distinta PROFUNDIDAD DE ANIDAMIENTO
   dentro de la cadena preserve-3d, aunque ambos cuelguen en algún
   punto del mismo ancestro con perspective. Antes, #seq4-fg-mask
   colgaba directo de #seq4-text (1 nivel) pero cada .letter colgaba
   de #seq4-word dentro de #seq4-text (2 niveles) — con esa asimetría
   el navegador pintaba SIEMPRE las letras por encima del recorte,
   sin importar su z real (comprobado con el propio prototipo
   aislado, que tenía el mismo problema desde siempre — nunca se
   había verificado el occlusion visualmente, sólo valores numéricos
   de z/x). Fix: sequence4.js mide la posición natural de cada letra
   dentro de #seq4-word (que sigue existiendo sólo como scaffold de
   medición, ver measureNatural) y después las saca de ahí,
   insertándolas como HIJAS DIRECTAS de #seq4-text (mismo nivel que
   #seq4-fg-mask) con position:absolute + left/top calculados. Recién
   ahí preserve-3d compara profundidad real entre hermanos del mismo
   nivel y el recorte tapa correctamente a las letras que quedan
   detrás (ver translateZ(2px) más abajo, el desempate mínimo para
   que el recorte gane cuando una letra llega a z≈0, su lugar de
   reposo). Por eso los selectores de abajo son "#seq4-text .letter"
   y no "#seq4-word .letter": tienen que seguir aplicando después de
   que JS mueve la letra fuera de #seq4-word. Las propiedades
   tipográficas (font, color, etc.) están puestas directo en .letter
   y no en #seq4-word por la misma razón: dejan de heredar de
   #seq4-word en cuanto la letra se muda de contenedor.

   FASE 7.6 — root cause real de por qué el occlusion seguía fallando
   en vivo pese al fix de arriba y al de font-swap-race: #seq4-text ya
   NO es simplemente "inset:0" (el viewport completo) — sequence4.js
   (positionStage) le fija left/top/width/height por JS para que
   coincida EXACTAMENTE con el rectángulo que ocuparía la imagen
   1920x1080 completa bajo el mismo cálculo que hace el navegador para
   object-fit:cover en #seq4-bg-frame/#seq4-fg-mask (por eso puede
   terminar más grande que el viewport, con left/top negativos: el
   recorte visual lo hace el overflow:hidden de .seq4, igual que a la
   imagen). El inset:0 de acá abajo queda sólo como fallback
   antes de que corra JS (evita un salto/flash visible, mismo patrón
   que .seq1-isotype/top:15%). Ver el comentario largo al principio de
   sequence4.js para la explicación completa. Por la misma razón,
   #seq4-word ya NO tiene "transform: translateY(-4vh)" fijo acá (ver
   más abajo) — sequence4.js se lo aplica escalado por el mismo
   factor.
   ------------------------------------------------------------------- */
.seq4 {
  /* .seq ya pone position:absolute + inset:0 + z-index:2 (ver arriba)
     — alcanza para tapar #video1 y quedar por encima de seq1/2/3 (que
     para cuando este capítulo empieza ya terminaron de revelarse y
     ocultarse). Acá sólo hace falta el layout interno. */
  overflow: hidden;
}

#seq4-bg-frame {
  position: absolute;
  inset: 0;
  z-index: 1;
  width: 100%;
  height: 100%;
  object-fit: cover;
  pointer-events: none;
}

#seq4-text {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  transform-style: preserve-3d;
  perspective: 1600px;
}

#seq4-word {
  /* Scaffold de medición SOLAMENTE: sequence4.js arma acá las letras
     con flexbox para leer su posición de reposo natural (measureNatural)
     y enseguida las muda a ser hijas directas de #seq4-text (ver el
     bloque de comentarios de más arriba) — #seq4-word queda vacío en
     régimen normal, sin tamaño ni efecto visual. Se conserva el
     transform de acá para que la medición inicial ya incluya el mismo
     desplazamiento vertical que tenía el diseño aprobado. */
  display: flex;
  flex-direction: row;
  white-space: nowrap;
  /* FASE 7.6 — el nudge vertical fino (pedido del cliente: "mayor
     legibilidad" — la mitad de la letra queda tras el sofá y la otra
     mitad tras los cuernos de la cabra) ya NO es un "translateY(-4vh)"
     fijo acá: sequence4.js se lo aplica por JS (referenceYNudgePercent
     en config.js) escalado por el mismo factor que usa para el
     recorte de la imagen — ver el comentario grande de FASE 7.6 en
     sequence4.js. Si hace falta volver a ajustar ese valor tras un
     cambio de frame/composición, se toca en config.js, no acá.
     transform-style se mantiene: sigue haciendo falta para que el
     3D de las letras funcione mientras están en este scaffold. */
  transform-style: preserve-3d;
}

#seq4-text .letter {
  position: relative;
  display: inline-block;
  transform-style: preserve-3d;
  will-change: transform;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.005em;
  line-height: 1;
  /* "POTENCIAMOS" ocupa ~80% del ancho del frame (medido en la
     referencia) — bastante más grande que el resto de los overprints,
     a propósito, porque acá es una sola línea larga.

     FASE 7.6 — este clamp() queda sólo como fallback pre-JS (antes de
     que corra sequence4.js, mismo patrón que el resto del archivo):
     el tamaño real que se ve en pantalla lo fija JS por inline style
     (applyBaseLetterSize/fitWordToStage), escalado por el mismo
     factor que usa positionStage() para el recorte de la imagen — no
     por vw directo, que es justo lo que rompía la calibración en
     viewports que no son 1920x1080 exacto. Ver el comentario grande
     de FASE 7.6 en sequence4.js. */
  font-size: clamp(4.25rem, 11vw, 8.5rem);
  color: var(--hueso);
  /* A propósito SIN backface-visibility:hidden: cuando la letra está
     de espaldas a cámara (rotationY inicial muy negativo) se ve
     espejada horizontalmente — es el efecto pedido, no un caso a
     esconder. */
}

#seq4-text .letter-face {
  position: relative;
  display: block;
  transform: translateZ(0);
}

/* Canto de la letra: una segunda copia del mismo carácter, apenas
   detrás en Z (chapita fina) y de otro color — de frente queda tapada
   por .letter-face; sólo se nota cuando la letra pasa de canto durante
   el giro (rotationY ≈ 90°), leyéndose como un borde 3D real. */
#seq4-text .letter-edge {
  position: absolute;
  inset: 0;
  display: block;
  color: var(--aquamarina);
  transform: translateZ(-9px);
}

#seq4-fg-mask {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  pointer-events: none;
  /* Desempate mínimo (2px, imperceptible en escala): cuando una letra
     termina de asentarse en z=0 (su lugar de reposo), el recorte gana
     el empate de forma consistente y la tapa — sin esto, z=0 contra
     z=0 podría alternar según el navegador. */
  transform: translateZ(2px);
}

/* -------------------------------------------------------------------
   FASE 7.7/7.7.1/7.7.2 — etiqueta "POTENCIAMOS" / "MARCAS" (dos
   líneas) bajo la mesita/pocillo del freeze-frame, alineada a la
   derecha con la "S" final de POTENCIAMOS.

   Pedido del cliente: como el grupo "hidden" (ENCIA) queda tapado
   detrás de Galopardo, agregar esta etiqueta, más chica y en
   Montserrat Light, que refuerce el concepto apenas termina de
   asentarse la palabra grande. FASE 7.7.2 (corrección sobre una
   captura real de mobile): en una sola línea "POTENCIAMOS MARCAS"
   quedaba escrito literalmente encima de la mesita/pocillo del
   freeze-frame — pasa a dos líneas ("POTENCIAMOS" / "MARCAS",
   sequence4.marcas.lines en config.js, fuente única de verdad),
   alineadas a la derecha ENTRE SÍ y con el borde derecho de la S
   (ya no centradas ni ancladas bajo "OS"), y la posición vertical deja
   de perseguir a las letras — pasa a ser un punto fijo calibrado
   contra la imagen nativa, bien debajo del borde inferior de la mesa
   (ver sequence4.marcas.referenceTopYPx en config.js). Se revela con
   el mismo tipo de máscara (clip-path) que ya usa GALOPAMOS/MARCAS en
   sequence2.js — nunca opacity — descubriéndose de arriba hacia abajo
   a medida que el borde inferior del clip-path retrocede (con dos
   líneas, de paso, ese descubrirse hacia abajo se nota más: primero
   aparece "POTENCIAMOS", después "MARCAS").

   display:flex column + align-items:flex-end: alinea las dos líneas a
   la derecha ENTRE SÍ (el bloque completo mide el ancho de la línea
   más larga, "POTENCIAMOS"; "MARCAS", más corta, queda pegada al
   mismo borde derecho) sin depender de text-align. position:absolute
   con left/top calculados por JS (positionMarcas en sequence4.js,
   relativo a #seq4-text — el mismo sistema de coordenadas
   "cover-scale" que usan las letras, ver FASE 7.6) — acá sólo
   transform-origin y el resto de las propiedades que no dependen del
   layout. FASE 7.7.2: xPercent pasa de -50 (centrado) a -100 (el
   "left" que fija JS es ahora el borde DERECHO del bloque, para que
   cuelgue hacia la izquierda desde ahí, alineado con la S) — el
   clip-path/y de la animación los sigue fijando GSAP (sequence4.js),
   nunca CSS estático, para poder animarlos.

   z: por encima de #seq4-fg-mask (que vive en translateZ(2px)) — a
   propósito, esta etiqueta NO debe quedar ocluida por Galopardo (se
   ancla sobre la S, letra del grupo "right", siempre visible). El
   translateZ(6px) real lo fija GSAP (sequence4.js, ver el gsap.set
   inicial con z:6) junto con xPercent/clipPath/y — no acá, porque un
   transform estático en CSS quedaría pisado apenas corra ese
   gsap.set (GSAP escribe su propio transform inline). */
.seq4-marcas {
  position: absolute;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  font-weight: 300;
  text-transform: uppercase;
  white-space: nowrap;
  /* FASE 7.7.1 — bajado de 0.22em (calibrado para "MARCAS" suelto, 6
     caracteres) a 0.12em: sobre "POTENCIAMOS" (11 caracteres) 0.22em
     por caracter sumaba demasiado ancho extra y se leía
     innecesariamente espaciado. */
  letter-spacing: 0.12em;
  line-height: 1;
  color: var(--hueso);
  pointer-events: none;
  will-change: transform, clip-path;
  /* Fallback pre-JS: el tamaño real lo fija sequence4.js
     (positionMarcas), escalado por el mismo factor "cover" que el
     resto de la secuencia. */
  font-size: clamp(0.75rem, 1.7vw, 1.1rem);
}

/* FASE 7.7.2 — separación entre las dos líneas: la fija sequence4.js
   por JS (marginTop en px, sequence4.marcas.lineGapPx escalado) sobre
   la segunda línea en adelante, no acá — mismo criterio que el resto
   de esta calibración (escala con "cover", no con una unidad de CSS
   fija). Acá sólo lo que no depende de esa escala. */
.seq4-marcas-line {
  display: block;
}

/* -------------------------------------------------------------------
   FASE 8.2 — VIDEO 2 (HOME), continuación después de POTENCIAMOS.

   Corrección sobre FASE 8: el cliente probó en vivo y avisó que el
   video aparecía "debajo, como una continuación de la página" — había
   que seguir scrolleando para verlo, y mientras tanto la pantalla SÍ
   se movía (se veía la página desplazándose desde la escena congelada
   hacia esta sección). La idea del cliente es lo opuesto: simular que
   el "viaje" dentro de la misma escena sigue, y el video es un nuevo
   plano de ESE mismo recorrido — tiene que aparecer exactamente en el
   mismo lugar de pantalla donde quedó congelado Video 1, sin que la
   página se desplace ni haya que scrollear más para descubrirlo.

   Primer intento descartado: `position: sticky`. En teoría "se pega"
   al tope de pantalla, pero en la práctica seguía habiendo un tramo de
   scroll (exactamente 1 viewport de alto) en el que esta sección
   todavía se desliza hacia arriba ENTRANDO en pantalla como un bloque
   normal ANTES de llegar a pegarse — porque el .pin-wrap de
   #intro-scrub, apenas el pin de GSAP libera, vuelve a flujo normal
   (deja de estar fixed) y sigue ocupando su propio alto (100vh) en el
   documento; recién después de que ESE bloque termina de desplazarse
   fuera de vista es que esta sección alcanza el punto en el que
   "sticky" empieza a hacer algo — o sea, se seguía viendo exactamente
   el mismo movimiento de página que se quería evitar.

   Solución real: `position: fixed`, superpuesta en el mismo lugar de
   pantalla que #intro-scrub, y oculta (visibility:hidden/opacity:0)
   hasta que el progreso maestro llega a 1 (el pin liberó) — recién ahí
   `js/video2-home.js` le agrega `.home-video-section--active` (ver
   showSection() ahí). Como el primer frame de #video2 es, a propósito,
   IDÉNTICO al freeze-frame en el que queda Video 1, este "corte" es
   invisible aunque no haya transición: no hace falta esconder nada
   gradualmente, sólo evitar que aparezca ANTES de tiempo (por eso
   sigue el mismo gate de progreso≥1 que ya usa arm() en
   video2-home.js) ni tampoco DESPUÉS de que el usuario scrollee para
   atrás por debajo de ese punto (showSection()/hideSection() ahí
   mismo, según el progreso suba o baje de 1 — soporta scroll reverso
   como el resto del sitio).

   El pin-spacer que arma GSAP para #intro-scrub YA reserva, por su
   cuenta, un tramo extra de scroll (exactamente 1 viewport, lo que
   tarda el propio .pin-wrap en volver a flujo normal tras liberarse)
   AUNQUE esta sección ya no ocupe espacio en el documento (al ser
   fixed) — ese tramo extra sigue generando eventos nativos `scroll`
   reales mientras el usuario scrollea después de progreso=1, que es
   justo lo que necesita el trigger "scroll o clic" de
   video2-home.js — con la diferencia de que ahora, al estar esta
   sección fixed y ya visible tapando todo, ese scroll de fondo queda
   completamente oculto: el usuario no ve absolutamente ningún
   movimiento de página mientras tanto.

   z-index alto (por encima de .seq, que usa 2) para tapar por completo
   #intro-scrub una vez activa. pointer-events:none (igual que #video1)
   porque el gesto que dispara la reproducción lo escucha
   video2-home.js en window, no esta sección — nunca debe interceptar
   ningún click/scroll. width/height 100vw/100vh + overflow:hidden para
   el mismo tratamiento object-fit:cover que #video1. background: mismo
   color que el freeze-frame de POTENCIAMOS para que, si #video2 tarda
   un instante en pintar su primer frame, no haya flash de otro color
   de por medio. */
/* FASE 8.2 — ver el comentario grande junto a #post-intro-scroll-buffer
   en index.html: alto fijo de 1 viewport, puramente para reservar
   distancia de scroll real (el trigger de video2-home.js depende de
   eventos nativos "scroll", que sólo se disparan si scrollY puede
   seguir cambiando) — nunca debe verse, así que mismo color de fondo
   que el resto por las dudas. */
#post-intro-scroll-buffer {
  width: 100vw;
  height: 100vh;
  background: var(--petro-galopante);
}

.home-video-section {
  position: fixed;
  inset: 0;
  width: 100vw;
  height: 100vh;
  overflow: hidden;
  background: var(--petro-galopante);
  z-index: 10;
  visibility: hidden;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s ease;
}

.home-video-section--active {
  visibility: visible;
  opacity: 1;
}

#video2 {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  pointer-events: none;
}

/* =========================================================================
   FASE 9 — HEADER FIJO + MENÚ (offcanvas) + REDES SOCIALES FIJAS
   Réplica del patrón de galopante.uy (sitio de producción actual) — ver
   el comentario grande en index.html (justo antes de <header>) para el
   detalle completo de qué se replicó y por qué. Acá sólo el CSS.

   z-index: por encima de TODO lo demás del sitio (.home-video-section
   usa 10, que es el valor más alto que había hasta ahora) — el header,
   el menú y las redes tienen que quedar SIEMPRE visibles sin importar
   en qué parte del recorrido esté el usuario.
   ========================================================================= */

html {
  /* Necesario para que los links del menú (window.scrollTo/
     scrollIntoView en js/header-menu.js) scrolleen con animación en vez
     de saltar de golpe. No afecta el scroll nativo por rueda/trackpad/
     touch del usuario (sigue instantáneo, "1:1" como pide el brief) ni
     la lectura de posición de scroll que hace ScrollTrigger cuadro a
     cuadro — scroll-behavior sólo entra en juego en scrolls disparados
     por código o por un <a href="#ancla">. */
  scroll-behavior: smooth;
}

/* ---------------------------------------------------------------------
   Header
   --------------------------------------------------------------------- */
.site-header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 1000;
  padding: 18px 24px 0;
  transition: background-color 0.25s ease;
}

.site-header.is-scrolled {
  background: rgba(9, 18, 28, 0.82);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  padding-bottom: 10px;
}

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

.site-header__logo {
  display: inline-flex;
  line-height: 0;
}

.site-header__logo img {
  width: 28px;
  height: auto;
}

.site-header__menu-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: none;
  background: transparent;
  color: var(--hueso);
  cursor: pointer;
}

.site-header__menu-btn:hover .site-header__hamburger span {
  background-color: var(--coral);
}

.site-header__hamburger {
  display: inline-flex;
  flex-direction: column;
  justify-content: center;
  gap: 6px;
  width: 26px;
  height: 20px;
}

.site-header__hamburger span {
  display: block;
  width: 100%;
  height: 2px;
  background-color: currentColor;
  border-radius: 1px;
  transition: transform 0.25s ease, opacity 0.25s ease, background-color 0.2s ease;
}

/* Hamburguesa -> X mientras el menú está abierto */
.site-header__menu-btn[aria-expanded="true"] .site-header__hamburger span:nth-child(1) {
  transform: translateY(8px) rotate(45deg);
}

.site-header__menu-btn[aria-expanded="true"] .site-header__hamburger span:nth-child(2) {
  opacity: 0;
}

.site-header__menu-btn[aria-expanded="true"] .site-header__hamburger span:nth-child(3) {
  transform: translateY(-8px) rotate(-45deg);
}

/* ---------------------------------------------------------------------
   Menú (overlay de pantalla completa) + backdrop
   FASE 9.3 — rediseño pedido por el cliente tomando como referencia el
   menú de kochol.com/es-ES: en vez de un panel angosto deslizando desde
   la derecha con fondo claro, ahora es una superposición a pantalla
   completa, fondo oscuro (--petro-galopante), texto en mayúscula
   Montserrat Light (--hueso), que al pasar el mouse cambia a
   --cuarzo-ahumado. La mecánica de apertura/cierre (botón hamburguesa,
   ESC, click en backdrop, cierre al clickear un link) es la misma de
   siempre — js/header-menu.js no cambió nada, sólo CSS.

   El backdrop (#menuBackdrop) queda debajo del menú en z-index: como el
   menú ahora cubre TODA la pantalla al abrirse, el backdrop nunca llega
   a verse ni a poder clickearse — se deja el elemento y su lógica
   intactos (no hace daño, y evita tocar el JS) pero ya no cumple
   función visual.
   --------------------------------------------------------------------- */
/* Bloquea el scroll de fondo mientras el panel está abierto (igual que
   cualquier modal) — la clase la agrega/saca js/header-menu.js. */
body.site-menu-open {
  overflow: hidden;
}

.site-menu-backdrop {
  position: fixed;
  inset: 0;
  z-index: 1100;
  background: rgba(9, 18, 28, 0.55);
  opacity: 0;
  transition: opacity 0.3s ease;
}

.site-menu-backdrop.is-open {
  opacity: 1;
}

/* ---------------------------------------------------------------------
   FASE 9.4 — menú con video interactivo (referencia estática enviada por
   el cliente + video "GALOPARDO girando"): el fondo oscuro liso de FASE
   9.3 pasa a ser un <video id="menuVideo"> a pantalla completa
   (object-fit: cover) con la cabra arrancando de frente (frame 1, mismo
   encuadre que la referencia estática) y girando de perfil hacia el
   final del clip. Un scrim (gradiente oscuro) sobre el video asegura
   contraste de lectura para la lista, que ahora se ancla arriba a la
   derecha (igual que la referencia) en vez de centrada a pantalla
   completa. El scrubbing del video (mousemove sobre el menú) lo maneja
   js/header-menu.js — ver initMenuVideoScrub() ahí; este bloque sólo
   define capas y estática/hover.

   Los 6 <li> arrancan invisibles/desplazados y se revelan uno tras otro
   (transition-delay incremental por :nth-child, sólo bajo .is-open) para
   el efecto de aparición secuencial pedido por el cliente.
   --------------------------------------------------------------------- */
.site-menu {
  position: fixed;
  inset: 0;
  z-index: 1200;
  width: 100%;
  height: 100vh;
  height: 100dvh;
  background: var(--petro-galopante);
  transform: translateX(100%);
  transition: transform 0.4s ease;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.site-menu.is-open {
  transform: translateX(0);
}

.site-menu__video {
  position: absolute;
  inset: 0;
  z-index: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  pointer-events: none;
}

.site-menu__scrim {
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(
    100deg,
    rgba(9, 18, 28, 0.4) 0%,
    rgba(9, 18, 28, 0.1) 40%,
    rgba(9, 18, 28, 0.35) 65%,
    rgba(9, 18, 28, 0.72) 100%
  );
  pointer-events: none;
}

.site-menu__header {
  position: relative;
  z-index: 2;
  display: flex;
  justify-content: flex-end;
  padding: 1.5rem 1.75rem 0.5rem;
}

.site-menu__close {
  border: none;
  background: transparent;
  color: var(--hueso);
  font-size: 2.5rem;
  font-weight: 300;
  line-height: 1;
  padding: 0;
  margin: 0;
  cursor: pointer;
  transition: color 0.2s ease;
}

.site-menu__close:hover {
  color: var(--cuarzo-ahumado);
  opacity: 1;
}

/* ---------------------------------------------------------------------
   FASE 22 — "Centro de Marca" (acceso clientes)
   ---------------------------------------------------------------------
   Bloque nuevo arriba a la derecha del panel del menú, debajo del botón
   de cerrar. Se posiciona con position:absolute — mismo truco que
   .site-menu__footer más abajo — para no interferir con el auto-margin
   que centra verticalmente .site-menu__nav. El "top" está calculado a
   ojo contra la altura real de .site-menu__header (padding 1.5rem +
   botón de 2.5rem + padding 0.5rem ≈ 4.5rem) más un pequeño respiro.

   Es un botón CON BORDE (outline), no relleno — a propósito distinto
   del coral sólido de .somos-section__cta / .hacemos-section__cta /
   .hp-cta, para que se lea como un acceso secundario/utilitario y no
   como otro CTA de conversión.
   --------------------------------------------------------------------- */
.site-menu__clients {
  position: absolute;
  z-index: 2;
  top: 5.5rem;
  right: clamp(1.5rem, 5vw, 4rem);
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.6rem;
}

.site-menu__clients-label {
  margin: 0;
  font-family: 'Montserrat', sans-serif;
  font-weight: 400;
  font-size: 0.68rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--cuarzo-ahumado);
}

.site-menu__clients-link {
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  padding: 0.55em 1.3em;
  border: 1px solid var(--aquamarina);
  border-radius: 999px;
  font-family: 'Montserrat', sans-serif;
  font-weight: 500;
  font-size: 0.78rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--aquamarina);
  text-decoration: none;
  transition: background-color 0.2s ease, color 0.2s ease;
}

.site-menu__clients-link:hover {
  background-color: var(--aquamarina);
  color: var(--petro-galopante);
}

.site-menu__clients-icon {
  width: 0.85em;
  height: 0.85em;
  flex-shrink: 0;
}

.site-menu__nav {
  position: relative;
  z-index: 2;
  margin: auto 0 auto auto;
  padding: 1.5rem clamp(1.5rem, 7vw, 5.5rem);
  max-width: min(92vw, 640px);
  max-height: 100%;
  overflow-y: auto;
}

.site-menu__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: clamp(0.35rem, 1.6vh, 0.85rem);
}

.site-menu__list li {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 0.5s ease, transform 0.5s ease;
}

.site-menu.is-open .site-menu__list li:nth-child(1) { transition-delay: 0.15s; }
.site-menu.is-open .site-menu__list li:nth-child(2) { transition-delay: 0.22s; }
.site-menu.is-open .site-menu__list li:nth-child(3) { transition-delay: 0.29s; }
.site-menu.is-open .site-menu__list li:nth-child(4) { transition-delay: 0.36s; }
.site-menu.is-open .site-menu__list li:nth-child(5) { transition-delay: 0.43s; }
.site-menu.is-open .site-menu__list li:nth-child(6) { transition-delay: 0.5s; }

.site-menu.is-open .site-menu__list li {
  opacity: 1;
  transform: none;
}

.site-menu__link {
  display: flex;
  align-items: baseline;
  justify-content: flex-end;
  gap: 0.55em;
  padding: 0.3em 0;
  font-family: 'Montserrat', sans-serif;
  font-weight: 300;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  font-size: clamp(1.25rem, 3.3vw, 2.3rem);
  color: var(--hueso);
  text-decoration: none;
  transition: color 0.2s ease;
}

.site-menu__link:hover,
.site-menu__link.is-active {
  color: var(--cuarzo-ahumado);
}

.site-menu__link-num {
  font-size: 0.42em;
  font-weight: 400;
  letter-spacing: 0.05em;
  color: currentColor;
  opacity: 0.55;
}

.site-menu__link-divider {
  display: inline-block;
  width: 1px;
  height: 0.85em;
  background: currentColor;
  opacity: 0.4;
}

.site-menu__link-label {
  color: currentColor;
}

.site-menu__footer {
  position: absolute;
  z-index: 2;
  right: clamp(1.5rem, 5vw, 4rem);
  bottom: clamp(1.25rem, 4vh, 2.25rem);
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  pointer-events: none;
}

/* Isotipo: el PNG de origen es un silueta blanca lisa con canal alfa
   (un solo color, sin degradés — verificado con PIL antes de este
   cambio), así que en vez de mostrarlo como <img> — lo que dejaría el
   ícono blanco fijo, sin poder cambiarle el color — se usa como
   mask-image sobre un <span> vacío con background-color: se pinta con
   el color de marca que haga falta (acá cuarzo ahumado, igual que pidió
   el cliente) simplemente cambiando esa variable, sin tocar el PNG. */
.site-menu__footer-logo {
  display: block;
  width: clamp(26px, 2.8vw, 38px);
  height: clamp(23px, 2.48vw, 34px); /* respeta el aspect-ratio real del PNG (643x570) */
  background-color: var(--cuarzo-ahumado);
  -webkit-mask-image: url('../assets/images/isotipo-galopante.png');
  mask-image: url('../assets/images/isotipo-galopante.png');
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-size: contain;
  mask-size: contain;
}

.site-menu__footer-divider {
  width: clamp(60px, 6vw, 90px);
  height: 1px;
  margin: 0.5rem 0;
  background: var(--cuarzo-ahumado);
}

.site-menu__footer-tagline {
  margin: 0;
  font-family: 'Montserrat', sans-serif;
  font-weight: 300;
  font-size: clamp(0.58rem, 0.95vw, 0.72rem);
  letter-spacing: 0.09em;
  line-height: 1.45;
  color: var(--cuarzo-ahumado);
  text-transform: uppercase;
  text-align: right;
}

@media (max-width: 640px) {
  .site-menu__nav {
    margin: auto auto auto auto;
    max-width: 100%;
    padding: 1.5rem 1.5rem;
  }

  .site-menu__list {
    align-items: flex-end;
  }

  .site-menu__footer {
    right: 1.25rem;
    bottom: 1.1rem;
  }

  .site-menu__clients {
    top: 5.25rem;
    right: 1.25rem;
    gap: 0.45rem;
  }

  .site-menu__clients-label {
    font-size: 0.6rem;
  }

  .site-menu__clients-link {
    padding: 0.5em 1.1em;
    font-size: 0.7rem;
  }
}

/* ---------------------------------------------------------------------
   Redes sociales fijas
   --------------------------------------------------------------------- */
.social-fixed {
  position: fixed;
  left: 2.91vw;
  bottom: 4vh;
  z-index: 900;
  display: flex;
  flex-direction: column;
  gap: 5px;
  opacity: 1;
  transition: opacity 0.25s ease;
}

/* FASE 11.1 — ver el comentario grande en js/header-menu.js sobre por
   qué se oculta mientras el footer está en pantalla (el footer trae
   su propio bloque de redes/contacto, y al ser #socialFixed
   position:fixed quedaba superpuesto arriba de esos mismos elementos).
   pointer-events:none además de opacity:0 para que, ya invisible,
   tampoco intercepte clicks sobre lo que quedó debajo. */
.social-fixed--hidden {
  opacity: 0;
  pointer-events: none;
}

.social-fixed a {
  width: 60px;
  height: 60px;
  border-radius: 50%;
  background: var(--petro-galopante);
  color: var(--hueso);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: opacity 0.2s ease, transform 0.2s ease;
}

.social-fixed a svg {
  width: 26px;
  height: 26px;
}

.social-fixed a:hover {
  opacity: 0.9;
  transform: scale(0.95);
}

.social-fixed__whatsapp {
  background: #25d366 !important;
}

@media (max-width: 767px) {
  .social-fixed {
    left: 0;
    right: 0;
    bottom: 0;
    flex-direction: row;
    justify-content: center;
    gap: 0;
    padding: 0.5rem;
    background: rgba(9, 18, 28, 0.85);
  }

  .social-fixed a {
    width: 44px;
    height: 44px;
    margin: 0 4px;
  }

  .social-fixed a svg {
    width: 20px;
    height: 20px;
  }
}

/* ---------------------------------------------------------------------
   Sección "SOMOS" (ver comentario grande en index.html). z-index:20 por
   el mismo motivo que las secciones placeholder de abajo: Video 2 es
   position:fixed y se queda cubriendo toda la pantalla en loop de fondo
   aunque el usuario siga scrolleando, así que esta sección (en flujo
   normal) necesita quedar por encima para taparlo visualmente.
   --------------------------------------------------------------------- */
.somos-section {
  position: relative;
  z-index: 20;
  min-height: 100vh;
  display: flex;
  align-items: center;
  overflow: hidden;
  background: var(--petro-galopante);
  border-top: 1px solid rgba(241, 240, 240, 0.08);
  padding: 7rem 0;
}

.somos-section__bg-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: 0;
}

.somos-section__overlay {
  position: absolute;
  inset: 0;
  z-index: 1;
  /* FASE 9.1.1 — el overlay original (0.8-0.94 de opacidad) era
     demasiado oscuro y prácticamente tapaba el video de fondo (el
     cliente: "el video que está por detras de SOMOS apenas se ve").
     Se bajó bastante la opacidad para que el collage se vea con
     claridad, dejando sólo el mínimo necesario para que el texto siga
     siendo legible (más oscuro en los bordes, donde vive el texto y el
     gráfico, más claro al centro). */
  background:
    linear-gradient(90deg, rgba(9, 18, 28, 0.55) 0%, rgba(9, 18, 28, 0.4) 45%, rgba(9, 18, 28, 0.62) 100%);
}

.somos-section__inner {
  position: relative;
  z-index: 2;
  width: 100%;
  max-width: 1400px;
  margin: 0 auto;
  padding: 0 3rem;
  display: grid;
  grid-template-columns: minmax(220px, 380px) 1fr;
  gap: 3rem;
  align-items: center;
}

.somos-section__graphic {
  width: 100%;
  max-width: 340px;
  height: auto;
  justify-self: start;
  filter: drop-shadow(0 4px 22px rgba(9, 18, 28, 0.5));
}

.somos-section__content {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 1.25rem;
  /* Antes 620px — quedaba mucho margen sin usar del lado derecho
     (el grid ya le da todo el espacio de 1fr, ver .somos-section__inner
     más arriba). Se lo estira bastante para aprovechar ese margen. */
  max-width: 820px;
}

.somos-section__logo {
  height: 42px;
  width: auto;
  margin-bottom: 0.5rem;
  /* Con el overlay más liviano (FASE 9.1.1) el video de fondo puede
     tener zonas claras justo detrás del isotipo — este drop-shadow
     sutil le da separación sin necesidad de oscurecer todo el fondo. */
  filter: drop-shadow(0 2px 10px rgba(9, 18, 28, 0.55));
}

.somos-section__heading {
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 0.05em;
  /* Columna de texto más ancha (FASE 9.1.3) -> título bastante más
     grande que antes, para que siga viéndose proporcionado al resto
     del bloque. Base en Montserrat Light (300); "marcas memorables"
     va en bold — ver .somos-section__heading strong más abajo. */
  font-size: clamp(1.6rem, 2.9vw, 2.5rem);
  font-weight: 300;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--hueso);
  line-height: 1.15;
  /* El font-weight:300 (Light) ya se aplica bien (Montserrat Light SÍ
     está cargada, ver el <link> de Google Fonts), pero el cliente lo
     veía "regular" — el text-shadow grande que tenía antes (mismo que
     el párrafo, pensado para un fondo más oscuro) rellenaba los trazos
     finos de la tipografía light y la engordaba visualmente. Se achicó
     bastante (menos blur, menos opacidad) para que el peso liviano se
     note de verdad; ya alcanza para separarse del video de fondo. */
  text-shadow: 0 1px 6px rgba(9, 18, 28, 0.45);
}

.somos-section__heading strong {
  font-weight: 700;
}

.somos-section__text {
  margin: 0;
  /* Un poco más grande que antes (0.88-1rem) — el cliente lo pidió como
     ajuste de "pieza de diseño cuidada" junto con el interlineado
     parejo de acá abajo. */
  font-size: clamp(1rem, 1.1vw, 1.125rem);
  line-height: 1.75;
  color: var(--hueso);
  font-weight: 400;
  text-shadow: 0 2px 16px rgba(9, 18, 28, 0.6);
}

.somos-section__text strong {
  font-weight: 700;
}

/* ---------------------------------------------------------------------
   "Ventanas" de foto dentro del párrafo (inspirado en kochol.com/es-ES,
   referencia que pasó el cliente): pequeños recortes rectangulares
   incrustados en medio del texto corrido, con la foto paneando dentro
   de esa "máscara" — el contenedor (.somos-window) tiene
   overflow:hidden con un tamaño fijo, más chico que la imagen real,
   que sólo se mueve en el eje Y.

   FASE 9.1.3 — el pan ya NO es una animación CSS en loop propio: el
   cliente pidió que vaya acompasado con el scroll (baja al scrollear
   hacia abajo, sube al scrollear hacia arriba). Ahora lo maneja
   js/somos-section.js leyendo --pan-dist de cada ventana (sigue como
   variable CSS, así el breakpoint mobile de abajo redefine el recorrido
   sin tocar JS) y seteando transform directo según qué tan avanzado
   está el scroll de la sección — ver ese archivo para el detalle.

   FASE 9.1.5 — interlineado parejo: cada ventana es más alta que una
   línea normal del párrafo (line-height: 1.75 × font-size, ver
   .somos-section__text más arriba), así que sin corregir nada el
   line-box de esa línea puntual crecía para poder mostrarla completa
   —y eso desparejaba el interlineado del resto del párrafo, que el
   cliente notó ("el interlineado desparejo"). El margin-block negativo
   de .somos-window (más abajo) "le resta" al cálculo de layout ese
   excedente sin recortar ni achicar nada (se sigue viendo a tamaño
   completo, sólo ocupa menos lugar en el flujo del texto). Se calcula
   con 1.75em (mismo múltiplo que el line-height del párrafo) en vez de
   un valor fijo en px: como .somos-window hereda el mismo font-size
   fluido del párrafo (clamp() de acá arriba), "1.75em" siempre
   equivale exactamente al alto real de una línea a cualquier ancho de
   pantalla, sin necesitar recalcularlo a mano por breakpoint — sólo el
   alto FIJO de la ventana (46px acá, 32px en mobile) cambia en el
   media query de abajo.

   FASE 9.2.1 — vuelven a ser dos ventanas sueltas en puntos distintos
   del párrafo (no un par unido): el cliente pidió específicamente que
   la cabra quede junto a la palabra "locura" y el isotipo junto a
   "reversos" — dos anclas de texto bien separadas entre sí, así que ya
   no tiene sentido agruparlas (el problema que resolvía el .somos-
   window-pair de FASE 9.1.6, que quedaran "una bajo la otra", era
   porque ANTES las dos vivían juntas en el mismo punto del texto; acá
   ni siquiera están cerca una de la otra). El margin-block/vertical-
   align que vivía en .somos-window-pair vuelve a .somos-window, que es
   de nuevo el elemento embebido directamente en el flujo inline del
   párrafo.
   --------------------------------------------------------------------- */
.somos-window {
  display: inline-block;
  position: relative;
  overflow: hidden;
  border-radius: 6px;
  vertical-align: middle;
  margin-inline: 0.3em;
  margin-block: calc((1.75em - 46px) / 2);
  transform: translateY(-0.05em);
}

/* FASE 9.6 y 9.6.1 (abandonadas) — se había probado agrandar la foto
   dentro de la ventana (--pan-zoom, muy por encima de 100% del ancho)
   para ganar alto de sobra y paneear más, con un --pan-offset para
   saltear las zonas "muertas" (sin el rasgo reconocible) de los
   recortes fuente, que eran muy chicos (400px de ancho). El cliente
   reportó que el zoom "destrozó la forma": al agrandar tanto un
   recorte tan chico, buena parte del recorrido del pan caía en zonas
   borrosas/sin contenido reconocible de la foto.

   FASE 9.7 — el cliente mandó las fotos originales a resolución
   completa (cabra 1254×1254, cristal 1370×1148) con una instrucción
   explícita: "el ancho es el de la foto, el recorrido del scrolling es
   abarcando toda su extension de altura". Se vuelve entonces al modelo
   simple, SIN zoom ni offset: la imagen se muestra a 100% del ancho
   del contenedor (sin recorte horizontal) y el pan recorre el alto
   completo disponible (alto renderizado de la imagen a ese ancho menos
   el alto fijo de la ventana). Los nuevos archivos fuente
   (somos-window-goat-v2.jpg 620×620, somos-window-crystal-v2.jpg
   620×520 — reescalados sin recortar desde los originales del
   cliente) tienen contenido reconocible en prácticamente toda su
   altura (cabra: sombrero → cuernos → anteojos → hocico → boca →
   mentón → saco/corbata; cristal: el objeto facetado en distintos
   ángulos), así que ya no hace falta esquivar ninguna zona "muerta" —
   el pan a alto completo es a la vez más radical (mucho más recorrido
   que con el zoom, porque las fotos nuevas son mucho más altas a
   igual ancho) y coherente en todo el trayecto. --pan-dist se
   recalculó para cada ventana/breakpoint a partir del alto real que
   rendariza cada foto nueva a ese ancho, con un pequeño margen de
   seguridad para que el pan nunca llegue a mostrar un borde vacío. */
.somos-window img {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: auto;
  will-change: transform;
}

.somos-window--goat {
  width: 184px;
  height: 46px;
  --pan-dist: 134px;
}

.somos-window--crystal {
  width: 184px;
  height: 46px;
  --pan-dist: 104px;
}

.somos-section__cta {
  margin-top: 0.5rem;
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.9rem 1.75rem;
  background: var(--coral);
  color: var(--hueso);
  font-family: inherit;
  font-weight: 700;
  font-size: 0.85rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  border: none;
  border-radius: 999px;
  cursor: pointer;
  transition: opacity 0.2s ease, transform 0.2s ease;
}

.somos-section__cta:hover {
  opacity: 0.88;
  transform: translateY(-1px);
}

@media (max-width: 900px) {
  .somos-section {
    padding: 5.5rem 0;
  }

  .somos-section__inner {
    grid-template-columns: 1fr;
    text-align: center;
    gap: 2rem;
  }

  .somos-section__graphic {
    justify-self: center;
    max-width: 220px;
    margin: 0 auto;
  }

  .somos-section__content {
    align-items: center;
    max-width: 100%;
  }
}

@media (max-width: 600px) {
  .somos-section {
    padding: 4.5rem 0;
  }

  .somos-section__inner {
    padding: 0 1.5rem;
  }

  .somos-section__graphic {
    max-width: 170px;
  }

  /* Ventanas de foto (ver bloque .somos-window más arriba en este mismo
     archivo) más chicas en mobile, con --pan-dist recalculado para la
     nueva proporción (FASE 9.7: modelo simple sin zoom, foto a 100%
     del ancho del contenedor, pan a lo largo de todo el alto de sobra
     que rendariza cada foto a este ancho, con un margen de seguridad
     para que el pan nunca llegue a mostrar un borde vacío). El pan en
     sí lo sigue manejando js/somos-section.js — acá sólo se redefine
     cuánto recorrido tiene disponible a este tamaño. El margin-block
     también se redefine acá porque depende del alto FIJO de la ventana
     (32px en vez de 46px) — el "1.75em" del cálculo se sigue ajustando
     solo al font-size real en este breakpoint. */
  .somos-window {
    margin-block: calc((1.75em - 32px) / 2);
  }

  .somos-window--goat,
  .somos-window--crystal {
    width: 120px;
    height: 32px;
  }

  .somos-window--goat {
    --pan-dist: 84px;
  }

  .somos-window--crystal {
    --pan-dist: 65px;
  }
}

/* ---------------------------------------------------------------------
   FASE 17 — Sección "HACEMOS", entre el video del Galopardo
   (#como-galopamos) y la galería. Mismo motivo de z-index:20 +
   background petro-galopante + border-top que ya usan
   .marca-circle-section (arriba) y .gallery-section (abajo): Video 2
   es position:fixed y sigue cubriendo la pantalla en loop de fondo
   aunque el usuario haya scrolleado más abajo de su propio buffer —
   estas secciones, en flujo normal, necesitan ese z-index más alto
   para "taparlo" visualmente al subir por encima (ver el detalle
   completo en el comentario grande de FASE 9/FASE 11 más abajo).

   Layout: mismo criterio de 2 columnas que .somos-section__inner (más
   arriba) — gráfico angosto a la izquierda + columna de texto a la
   derecha — pero sin video de fondo, sin overlay y sin CTA (el
   cliente no pidió ninguno acá, sólo el gráfico "HACEMOS" + el texto).
   --------------------------------------------------------------------- */
.hacemos-section {
  position: relative;
  z-index: 20;
  /* El header fijo (.site-header, ~90px con el fondo ya "scrolled")
     queda por encima de todo (z-index:1000) — sin este scroll-margin,
     saltar acá desde el menú (scrollIntoView) dejaría el título pegado
     contra el header y parcialmente tapado. */
  scroll-margin-top: 90px;
  padding: 6rem 0;
  background: var(--petro-galopante);
  border-top: 1px solid rgba(241, 240, 240, 0.08);
  overflow: hidden;
}

.hacemos-section__inner {
  width: 100%;
  max-width: 1400px;
  margin: 0 auto;
  padding: 0 3rem;
  display: grid;
  grid-template-columns: minmax(220px, 380px) 1fr;
  gap: 3rem;
  align-items: center;
}

.hacemos-section__graphic {
  width: 100%;
  max-width: 340px;
  height: auto;
  justify-self: start;
}

.hacemos-section__content {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 1.25rem;
  max-width: 820px;
}

.hacemos-section__heading {
  margin: 0;
  font-size: clamp(1.6rem, 2.9vw, 2.5rem);
  font-weight: 300;
  letter-spacing: 0.02em;
  color: var(--hueso);
  line-height: 1.15;
}

.hacemos-section__text {
  margin: 0;
  font-size: clamp(1rem, 1.1vw, 1.125rem);
  line-height: 1.75;
  color: var(--hueso);
  font-weight: 400;
}

.hacemos-section__text strong {
  font-weight: 700;
  color: var(--aquamarina);
}

/* FASE 18 — mismo patrón visual que .somos-section__cta (botón coral,
   pill, uppercase por CSS). Sin destino/acción todavía, igual que ese
   otro CTA cuando se agregó.

   FASE 25 — dos ajustes pedidos por el cliente: (1) es un <a> (desde
   la FASE 20, cuando pasó a linkear a hacemos.html), así que por
   default el navegador le pone subrayado — .somos-section__cta nunca
   lo tuvo porque es un <button>, no un link. Se agrega
   text-decoration:none acá para que se vean iguales. (2) se agrega la
   flechita "→" al final del texto en el HTML (index.html), mismo
   recurso que ya usa .somos-section__cta ("COMENZÁ TU BRIEF →") — el
   cliente pidió explícitamente "tal como el CTA comenzá tu brief". */
.hacemos-section__cta {
  margin-top: 0.5rem;
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.9rem 1.75rem;
  background: var(--coral);
  color: var(--hueso);
  font-family: inherit;
  font-weight: 700;
  font-size: 0.85rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  text-decoration: none;
  border: none;
  border-radius: 999px;
  cursor: pointer;
  transition: opacity 0.2s ease, transform 0.2s ease;
}

.hacemos-section__cta:hover {
  opacity: 0.88;
  transform: translateY(-1px);
}

@media (max-width: 900px) {
  .hacemos-section__inner {
    grid-template-columns: 1fr;
    gap: 2.5rem;
    text-align: left;
  }

  .hacemos-section__graphic {
    max-width: 220px;
  }
}

@media (max-width: 520px) {
  .hacemos-section {
    padding: 4rem 0;
  }

  .hacemos-section__inner {
    padding: 0 1.5rem;
  }

  .hacemos-section__graphic {
    max-width: 170px;
  }
}

/* ---------------------------------------------------------------------
   FASE 11 — Galería (carrousel) + footer, cierre del home.
   Reemplaza las 3 secciones placeholder que había acá (LABORATORIO /
   EL REBAÑO / HABLEMOS, FASE 9) — ver el comentario grande en
   index.html sobre esa decisión. z-index:20 por el mismo motivo que
   ya explicaba el comentario de las secciones placeholder: Video 2 es
   position:fixed y se queda cubriendo toda la pantalla en loop de
   fondo aunque el usuario siga scrolleando más abajo de su propio
   buffer; con z-index:20 estas secciones (en flujo normal) "suben"
   por encima visualmente a medida que se scrollea, tapándolo sin
   necesidad de tocar su máquina de estados de visibilidad (FASE 8.2/8.3).

   FASE 11.1 — el cliente pasó una captura de referencia del footer y
   carrousel actuales de galopante.uy (producción) y pidió repetir ese
   mismo diseño acá (mismas fotos, fondo petro galopante). Ver el
   comentario grande en index.html sobre qué se pudo replicar 1:1 (el
   footer, texto exacto de la captura) y qué quedó como placeholder
   (las 6 fotos del carrousel — no están publicadas todavía en
   galopante.uy, no se pudieron traer los archivos reales).
   --------------------------------------------------------------------- */
.gallery-section {
  position: relative;
  z-index: 20;
  padding: 5rem 0;
  background: var(--petro-galopante);
  border-top: 1px solid rgba(241, 240, 240, 0.08);
  overflow: hidden;
}

/* Acordeón de "máscara" (FASE 15.1, corrige el primer intento de FASE
   15): por defecto cada tarjeta es una MÁSCARA (overflow:hidden) que
   ocupa 1/--gallery-cols del ancho visible del track (ver
   .gallery-card) — pero la <img> de adentro NO se reescala nunca: mide
   siempre 100vw fijo (el ancho que tendría a pantalla completa) y
   queda centrada con left:50% + translateX(-50%) (ver
   .gallery-card__img). Lo único que anima es el ANCHO DE LA MÁSCARA
   (.gallery-card--expanded lleva su flex-basis a ~100% del track): al
   crecer esa máscara simétricamente hacia los dos lados, la ventana
   visible se corre sobre la foto QUIETA y de tamaño fijo, revelando
   más foto a cada lado — nunca se ve un zoom/reescalado, sólo se
   "descubre" más foto. La primera versión de FASE 15 usaba
   width:100%/object-fit:cover en la propia tarjeta (que sí reescalaba
   la imagen al expandir — un efecto de zoom que el cliente pidió
   explícitamente sacar); esta versión lo reemplaza por el mecanismo de
   máscara fija descrito arriba.

   FASE 34.2 — el cliente pidió ver 6 tarjetas a la vez en vez de 3 (en
   desktop). Como --gallery-cols ya manejaba el ancho de cada máscara
   como fracción del track (ver .gallery-carousel__track) y tanto
   js/gallery-carousel.js (dots/páginas se recalculan por
   scrollWidth/clientWidth reales, sin ningún "3" fijo en el código)
   como el propio mecanismo de máscara de arriba son agnósticos a
   cuántas columnas haya, el cambio fue de una sola línea:
   --gallery-cols pasó de 3 a 6. Cada máscara ahora muestra una porción
   más angosta (centrada) de la misma foto de 100vw — verificado con
   captura local que ninguna de las 19 fotos queda irreconocible o
   demasiado "zoomeada" con el recorte más angosto. El breakpoint
   mobile (≤700px, más abajo) se dejó en 2 columnas — 6 en una pantalla
   angosta dejaría cada foto en una tira de unos px, ilegible.

   El resto de las tarjetas, con flex-shrink:0, queda empujado hacia el
   costado al expandir una (scrolleable, no desaparece). El track en sí
   sigue siendo overflow-x:auto + scroll-snap-type:x, que da swipe
   táctil nativo gratis en mobile (el cliente pidió mantenerlo así); en
   desktop además se puede arrastrar con el mouse (mousedown +
   mousemove sobre el track) — ver js/gallery-carousel.js. */
.gallery-carousel {
  position: relative;
}

.gallery-carousel__track {
  --gallery-cols: 6;
  display: flex;
  gap: 4px;
  list-style: none;
  margin: 0;
  padding: 0;
  height: clamp(320px, 38vw, 560px);
  overflow-x: auto;
  scroll-snap-type: x proximity;
  scroll-behavior: smooth;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  cursor: grab;
}

.gallery-carousel__track::-webkit-scrollbar {
  display: none;
}

.gallery-carousel__track--dragging {
  cursor: grabbing;
  scroll-snap-type: none;
  scroll-behavior: auto;
}

.gallery-card {
  position: relative;
  flex: 0 0 calc((100% - (var(--gallery-cols) - 1) * 4px) / var(--gallery-cols));
  height: 100%;
  scroll-snap-align: start;
  overflow: hidden;
  transition: flex-basis 0.5s cubic-bezier(0.65, 0, 0.35, 1);
}

.gallery-card--expanded {
  flex-basis: 100%;
}

.gallery-card__trigger {
  position: relative;
  display: block;
  width: 100%;
  height: 100%;
  border: none;
  padding: 0;
  margin: 0;
  background: none;
  cursor: pointer;
  overflow: hidden;
  -webkit-user-drag: none;
}

.gallery-card__trigger:focus-visible {
  outline: 2px solid var(--aquamarina);
  outline-offset: -2px;
}

/* Máscara fija: la foto mide 100vw SIEMPRE (colapsada o expandida) y
   está centrada dentro de la tarjeta — ver el comentario grande más
   arriba. object-fit:cover acá sólo define el recorte vertical (con
   el alto del track), nunca cambia de escala horizontal porque el
   ancho es un valor fijo (100vw), no un porcentaje del contenedor. */
.gallery-card__img {
  position: absolute;
  top: 0;
  left: 50%;
  height: 100%;
  width: 100vw;
  max-width: none;
  transform: translateX(-50%);
  object-fit: cover;
  display: block;
  -webkit-user-drag: none;
  user-select: none;
}

.gallery-card__expand-icon {
  position: absolute;
  right: 10px;
  bottom: 10px;
  width: 30px;
  height: 30px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: rgba(9, 18, 28, 0.55);
  backdrop-filter: blur(2px);
  color: var(--hueso);
  opacity: 0;
  transform: translateY(4px);
  transition: opacity 0.2s ease, transform 0.2s ease, background 0.2s ease;
  pointer-events: none;
}

.gallery-card__expand-icon svg {
  width: 15px;
  height: 15px;
}

.gallery-card__trigger:hover .gallery-card__expand-icon,
.gallery-card__trigger:focus-visible .gallery-card__expand-icon {
  opacity: 1;
  transform: translateY(0);
}

.gallery-card--expanded .gallery-card__expand-icon {
  background: var(--coral);
}

.gallery-carousel__arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 56px;
  height: 56px;
  border-radius: 50%;
  border: none;
  background: rgba(9, 18, 28, 0.55);
  backdrop-filter: blur(2px);
  color: var(--hueso);
  cursor: pointer;
  transition: background 0.2s ease, opacity 0.2s ease;
}

.gallery-carousel__arrow--prev {
  left: 28px;
}

.gallery-carousel__arrow--next {
  right: 28px;
}

.gallery-carousel__arrow svg {
  width: 20px;
  height: 20px;
}

.gallery-carousel__arrow:hover {
  background: var(--coral);
}

.gallery-carousel__arrow:disabled {
  opacity: 0.35;
  cursor: default;
  background: rgba(9, 18, 28, 0.55);
}

/* Dots por "página" (ancho visible del track), no uno por foto — igual
   criterio que la referencia, donde son pocos indicadores anchos en
   vez de un dot por imagen (ver computePages() en
   js/gallery-carousel.js). El activo es una píldora más larga; el
   resto, guiones finos. */
.gallery-carousel__dots {
  display: flex;
  justify-content: center;
  gap: 0.5rem;
  margin-top: 1.75rem;
}

.gallery-carousel__dot {
  width: 22px;
  height: 4px;
  border-radius: 999px;
  border: none;
  padding: 0;
  background: rgba(241, 240, 240, 0.25);
  cursor: pointer;
  transition: background 0.2s ease, width 0.2s ease;
}

.gallery-carousel__dot--active {
  background: var(--hueso);
  width: 34px;
}

@media (max-width: 700px) {
  .gallery-carousel__arrow {
    display: none;
  }

  .gallery-carousel__track {
    --gallery-cols: 2;
    height: 60vw;
  }
}

/* ---------------------------------------------------------------------
   Footer — 7 bloques (marca / navegación / hacemos / laboratorio /
   centro de marca / comenzá tu brief / hablemos) + barra inferior,
   replicando el layout de la captura de referencia del cliente. Ver
   el comentario grande en index.html sobre qué contenido es real vs.
   placeholder.
   --------------------------------------------------------------------- */
.site-footer {
  position: relative;
  z-index: 20;
  background: #060d15; /* un toque más oscuro que --petro-galopante para separarse visualmente de la galería */
  border-top: 1px solid rgba(241, 240, 240, 0.08);
  padding: 4.5rem 2.5rem 2rem;
}

.site-footer__top {
  display: grid;
  grid-template-columns: 1.3fr 0.85fr 0.85fr 0.85fr 0.95fr 0.95fr 1fr;
  gap: 2rem;
  max-width: 1600px;
  margin: 0 auto;
  padding-bottom: 3.5rem;
}

.site-footer__brand {
  display: flex;
  flex-direction: column;
  gap: 1.1rem;
  grid-column: span 1;
}

/* FASE 16: logo oficial (antes era texto "GALOPANTE / MEDIA" armado
   con CSS — ver FASE 14/16 en el comentario del footer en index.html
   para el detalle). Mismo archivo que ya usaban la secuencia
   "BIENVENIDO A [logo]" y la sección "Somos". */
.site-footer__logo {
  display: block;
  width: 100%;
  max-width: 210px;
  height: auto;
}

.site-footer__dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--coral);
}

.site-footer__divider {
  display: block;
  width: 40px;
  height: 2px;
  background: rgba(241, 240, 240, 0.25);
}

.site-footer__tagline {
  margin: 0;
  font-size: 0.85rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  line-height: 1.5;
  color: var(--cuarzo-ahumado);
}

.site-footer__social {
  display: flex;
  gap: 1rem;
  margin-top: 0.25rem;
}

.site-footer__social a {
  display: flex;
  color: var(--hueso);
  opacity: 0.8;
  transition: opacity 0.2s ease, color 0.2s ease;
}

.site-footer__social svg {
  width: 17px;
  height: 17px;
}

.site-footer__social a:hover {
  opacity: 1;
  color: var(--aquamarina);
}

.site-footer__col {
  display: flex;
  flex-direction: column;
  gap: 0.65rem;
}

.site-footer__col-title {
  margin: 0 0 0.15rem;
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--hueso);
}

.site-footer__nav-link {
  color: var(--cuarzo-ahumado);
  text-decoration: none;
  font-size: 0.88rem;
  transition: color 0.2s ease;
  width: fit-content;
}

.site-footer__nav-link:hover {
  color: var(--aquamarina);
}

.site-footer__promo {
  display: flex;
  flex-direction: column;
  gap: 0.65rem;
}

.site-footer__promo-text {
  margin: 0;
  font-size: 0.85rem;
  line-height: 1.5;
  color: var(--cuarzo-ahumado);
}

.site-footer__promo-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  width: fit-content;
  padding: 0.55rem 1rem;
  border: 1px solid var(--aquamarina);
  border-radius: 6px;
  color: var(--aquamarina);
  text-decoration: none;
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  transition: background 0.2s ease, color 0.2s ease;
}

.site-footer__promo-btn:hover {
  background: var(--aquamarina);
  color: var(--petro-galopante);
}

.site-footer__promo-thumb {
  width: 100%;
  max-width: 220px;
  border-radius: 8px;
  display: block;
  margin-top: 0.25rem;
}

.site-footer__col--contact {
  gap: 0.85rem;
}

.site-footer__contact-line {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  color: var(--hueso);
  text-decoration: none;
  font-size: 0.88rem;
  transition: color 0.2s ease;
}

.site-footer__contact-line svg {
  flex: 0 0 auto;
  width: 15px;
  height: 15px;
  color: var(--aquamarina);
}

a.site-footer__contact-line:hover {
  color: var(--aquamarina);
}

.site-footer__contact-line--static {
  cursor: default;
}

.site-footer__bottom {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  max-width: 1600px;
  margin: 0 auto;
  padding-top: 1.75rem;
  border-top: 1px solid rgba(241, 240, 240, 0.08);
}

.site-footer__copyright {
  margin: 0;
  font-size: 0.78rem;
  color: var(--cuarzo-ahumado);
}

.site-footer__legal {
  display: flex;
  gap: 1.5rem;
}

.site-footer__legal a {
  color: var(--cuarzo-ahumado);
  text-decoration: none;
  font-size: 0.78rem;
  transition: color 0.2s ease;
}

.site-footer__legal a:hover {
  color: var(--aquamarina);
}

.site-footer__signature {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin: 0;
  font-size: 0.78rem;
  color: var(--cuarzo-ahumado);
}

.site-footer__signature .site-footer__dot {
  position: static;
  flex: 0 0 auto;
}

@media (max-width: 1300px) {
  .site-footer__top {
    grid-template-columns: 1fr 1fr 1fr;
  }

  .site-footer__brand {
    grid-column: 1 / -1;
  }
}

@media (max-width: 900px) {
  .site-footer {
    padding: 3.5rem 1.5rem 2rem;
  }

  .site-footer__top {
    grid-template-columns: 1fr 1fr;
    gap: 2.5rem;
  }

  .site-footer__brand {
    grid-column: 1 / -1;
  }

  .site-footer__bottom {
    flex-direction: column;
    align-items: flex-start;
  }

  /* padding-bottom extra: en mobile .social-fixed (ver esa regla más
     arriba) pasa a ser una barra FIJA de ~60px pegada al borde
     inferior real de la pantalla — al final del todo de la página
     (scroll máximo), el contenido de acá abajo quedaba tapado por esa
     barra porque el padding normal no alcanzaba para despejarla.
     Mismo tipo de ajuste que ya se hizo para el CTA/mute del círculo
     de video (FASE 10.1) por el mismo motivo. */
  .site-footer {
    padding-bottom: 5.5rem;
  }
}

@media (max-width: 560px) {
  .site-footer__top {
    grid-template-columns: 1fr;
  }
}

/* ---------------------------------------------------------------------
   FASE 10 — "DIAGNOSTICAR MI MARCA": círculo que se abre a un video EN
   PANTALLA COMPLETA (FASE 10.1: el cliente pidió que el círculo no se
   quede en un tamaño chico/mediano — tiene que crecer hasta que el
   video ocupe TODA la pantalla, rectangular, con el CTA superpuesto
   arriba del video). Ver comentario grande en index.html sobre el
   reemplazo del stub "CÓMO GALOPAMOS" y ver js/marca-circle.js para
   toda la mecánica de scroll.

   Técnica — "iris wipe" con clip-path, no con width/height: el video
   (`.marca-circle__video`) SIEMPRE ocupa el 100% del alto/ancho del
   contenedor sticky (pantalla completa) — lo que crece y se achica es
   la VENTANA CIRCULAR por la que se lo ve, vía
   `clip-path: circle(var(--circle-radius) at 50% 50%)` en
   `.marca-circle`. JS interpola --circle-radius entre un radio chico
   (círculo, "cerrado") y un radio lo bastante grande como para cubrir
   toda la diagonal de la pantalla (en ese punto el clip-path ya no
   recorta nada visible — el círculo "desaparece" porque sus bordes
   quedan fuera de cualquier lado de la pantalla, y lo que se ve es el
   video ocupando el rectángulo completo, tal como pidió el cliente).
   Cerrar es exactamente lo mismo al revés. Un solo mecanismo cubre
   "círculo chico" y "video fullscreen": no hace falta animar
   width/height por separado.
   --------------------------------------------------------------------- */
.marca-circle-section {
  position: relative;
  z-index: 20;
  min-height: 130vh;
  background: var(--petro-galopante);
  border-top: 1px solid rgba(241, 240, 240, 0.08);
  overflow: hidden;
}

/* position:sticky (no GSAP pin) — el contenido queda centrado respecto
   al VIEWPORT mientras dura el recorrido de scroll de la sección
   (130vh), en vez de centrarse respecto a la caja de la sección (que,
   al ser más alta que el viewport, haría que el punto central del
   iris se corriera verticalmente a medida que se scrollea dentro de
   la meseta de apertura). Con sticky, ese punto se mantiene centrado
   en la pantalla durante todo ese recorrido, tal como si estuviera
   "pineado" — pero sin el costo/complejidad de un pin real de
   ScrollTrigger, ver comentario grande en js/marca-circle.js sobre
   por qué esta sección no usa un pin. */
.marca-circle-section__inner {
  position: sticky;
  top: 0;
  width: 100%;
  height: 100vh;
  overflow: hidden;
}

.marca-circle {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  background: #000;
  clip-path: circle(var(--circle-radius, 100px) at 50% 50%);
  -webkit-clip-path: circle(var(--circle-radius, 100px) at 50% 50%);
}

.marca-circle__video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.marca-circle__mute {
  /* position:fixed (no absolute) a propósito: este botón es un hermano
     de .marca-circle dentro de .marca-circle-section__inner, que es
     position:sticky — pero un elemento sticky sólo queda con su caja
     alineada al viewport MIENTRAS está efectivamente "pegado" (top:0);
     durante la transición de entrada/salida (todavía en flujo normal,
     acercándose al punto de pegado) su caja se desplaza hacia abajo,
     y un bottom:% en position:absolute se mide contra ESA caja, no
     contra el viewport real — el botón terminaba corriéndose varias
     decenas de píxeles hacia abajo (y chocando con la barra fija de
     redes sociales en mobile) durante esa transición. position:fixed
     se mide siempre contra el viewport real, sin este problema. */
  position: fixed;
  left: 50%;
  bottom: 8%;
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 46px;
  height: 46px;
  border-radius: 50%;
  border: 1px solid rgba(241, 240, 240, 0.35);
  background: rgba(9, 18, 28, 0.55);
  color: var(--hueso);
  cursor: pointer;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.25s ease, background 0.2s ease;
  z-index: 950;
}

.marca-circle__mute--visible {
  opacity: 1;
  pointer-events: auto;
}

.marca-circle__mute:hover {
  background: rgba(9, 18, 28, 0.85);
}

.marca-circle__mute-icon {
  width: 18px;
  height: 18px;
}

.marca-circle__mute-icon--off {
  display: none;
}

/* JS agrega esta clase al botón (no al ícono) cuando el video está
   silenciado — ver updateMuteIcon() en js/marca-circle.js. */
.marca-circle__mute--is-muted .marca-circle__mute-icon--on {
  display: none;
}

.marca-circle__mute--is-muted .marca-circle__mute-icon--off {
  display: block;
}

/* CTA superpuesto arriba del video (no debajo, en flujo, como en la
   FASE 10 original) — position:fixed (mismo motivo que .marca-circle__mute
   arriba: mide contra el viewport real, no contra la caja de .inner
   durante su transición de entrada/salida como sticky). */
.marca-circle__cta {
  position: fixed;
  left: 50%;
  bottom: 16%;
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.95rem 2rem;
  background: var(--coral);
  color: var(--hueso);
  font-family: inherit;
  font-weight: 700;
  font-size: 0.85rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  border: none;
  border-radius: 999px;
  cursor: pointer;
  opacity: 0;
  transform: translateX(-50%) translateY(14px);
  transition: opacity 0.3s ease, transform 0.3s ease;
  z-index: 950;
  pointer-events: none;
}

.marca-circle__cta--visible {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
  pointer-events: auto;
}

.marca-circle__cta:hover {
  opacity: 0.88;
}

.marca-circle__cta--visible:hover {
  opacity: 0.88;
}

@media (max-width: 900px) {
  .marca-circle__cta {
    padding: 0.85rem 1.6rem;
    font-size: 0.78rem;
    bottom: 12%;
  }

  /* bottom subido de 6% a 9%: en mobile la barra fija de redes
     sociales (.social-fixed, ver esa regla más arriba) ocupa una
     franja horizontal de ~60px pegada al borde inferior real del
     viewport — con 6% (~51px en un viewport típico de 844px) el
     botón de mute quedaba parcialmente tapado por esa barra. */
  .marca-circle__mute {
    bottom: 9%;
  }
}
