/* ============================================================
   GBCO — replicación 1:1 de estructura/motion de eleos.la
   Fuente: Cabinet Grotesk self-hosted (única familia, 5 pesos) · Paleta: navy + teal (GBCO)
   ============================================================ */

@font-face {
  font-family: "Cabinet Grotesk";
  font-weight: 300;
  font-style: normal;
  font-display: swap;
  src: url("../fonts/CabinetGrotesk-Light.woff2") format("woff2");
}
@font-face {
  font-family: "Cabinet Grotesk";
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  src: url("../fonts/CabinetGrotesk-Regular.woff2") format("woff2");
}
@font-face {
  font-family: "Cabinet Grotesk";
  font-weight: 500;
  font-style: normal;
  font-display: swap;
  src: url("../fonts/CabinetGrotesk-Medium.woff2") format("woff2");
}
@font-face {
  font-family: "Cabinet Grotesk";
  font-weight: 700;
  font-style: normal;
  font-display: swap;
  src: url("../fonts/CabinetGrotesk-Bold.woff2") format("woff2");
}
@font-face {
  font-family: "Cabinet Grotesk";
  font-weight: 800;
  font-style: normal;
  font-display: swap;
  src: url("../fonts/CabinetGrotesk-Extrabold.woff2") format("woff2");
}

:root {
  --font-display: "Cabinet Grotesk", "Segoe UI", sans-serif;

  /* Paleta GBCO */
  --navy-deep: #10163a;      /* fondo oscuro hero/nav — equivalente a eleos #16151e */
  --navy: #1b2a5e;           /* azul marino de marca (portada del brochure) */
  --teal: #0ea391;           /* verde-teal del logo GBCO */
  --teal-soft: #0ea39122;
  --cream: #ffffff;          /* 2026-08-21: era #f5f4ee (equivalente a eleos #f4f4ed) —
     ese beige nunca fue parte de la paleta de GBCO (teal + navy, ver
     brochure), era solo el tono que traía el sitio de referencia. Leonardo
     pidió blanco puro en todo el sitio. */
  --text-dark: #14142b;
  --text-body: #40404a;
  --border: #e2e1d8;
  --sand: #b5a77f22;         /* grid decorativo sutil */

  --ease-brand: cubic-bezier(0.625, 0.05, 0, 1); /* [C] ease "eleos" replicada */
  --ease-loader: cubic-bezier(0.3589, 0.5033, 0, 0.9972);

  --nav-h: 84px;

  /* Sistema de color scroll-driven (patrón compsych-brand-hub, PATTERN-121 a
     124) — .bg-canvas (fixed, detrás de todo) interpola estas dos vars según
     qué <section data-main/data-sub> domina el área visible del viewport
     (ver scrollColorSystem en main.js). Creamos/Desarrollamos/Conectamos ya
     no tienen fondo propio (2026-08-17 v6): dependen de este sistema para
     que el cambio de color entre ellas sea gradual, no un corte de golpe. */
  --theme-main: #10163a;
  --theme-sub: #171d47;
}

/* Fondo fijo detrás de todo el contenido — el "canvas" que interpola color */
.bg-canvas {
  position: fixed;
  inset: 0;
  height: 100vh;
  z-index: -1;
  pointer-events: none;
  background-image: linear-gradient(180deg, var(--theme-main), var(--theme-sub));
}

* { box-sizing: border-box; margin: 0; padding: 0; }

html {
  scroll-behavior: auto;
  background: var(--cream); /* en html, no en body: evita que el bg-canvas (fixed, z-index:-1) quede oculto detrás del canvas del body */
}
html.lenis { height: auto; }

body {
  font-family: var(--font-display);
  color: var(--text-dark);
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
}

img { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }
section { position: relative; }

.container {
  width: 100%;
  max-width: 1600px;
  margin: 0 auto;
  padding: 0 clamp(24px, 5vw, 80px);
}

/* ---------- Grid lines decorativas (patrón visual de eleos) ---------- */
.grid-lines {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  display: grid;
  grid-template-columns: repeat(8, 1fr);
  max-width: 1600px;
  margin: 0 auto;
  padding: 0 clamp(24px, 5vw, 80px);
}
.grid-lines span { border-left: 1px solid var(--sand); height: 100%; }
.grid-lines span:last-child { border-right: 1px solid var(--sand); }
section.is-dark .grid-lines span { border-color: #ffffff0d; }

/* ---------- Tipografía base ---------- */
h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 700;
  text-transform: none;
  letter-spacing: -0.01em;
  line-height: 1.1;
}
p { line-height: 1.6; letter-spacing: 0; color: var(--text-body); font-weight: 400; }
section.is-dark p { color: #f4f4edcc; }

.split-line { overflow: hidden; display: block; }

.eyebrow {
  font-size: 12px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--teal);
  font-weight: 700;
  display: inline-block;
  margin-bottom: 1.2em;
}

/* ---------- NAV ---------- */
.nav {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 100;
  height: var(--nav-h);
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 clamp(24px, 5vw, 80px);
  mix-blend-mode: normal;
  transition: background-color 0.4s var(--ease-brand), border-color 0.4s var(--ease-brand);
}
.nav.is-solid {
  background: #ffffffb8;
  backdrop-filter: blur(14px) saturate(1.4);
  -webkit-backdrop-filter: blur(14px) saturate(1.4);
  border-bottom: 1px solid var(--border);
}
.nav.is-solid .nav__link { color: var(--text-dark); }
.nav.is-solid .nav__cta { color: var(--text-dark); border-color: var(--text-dark); }

.nav__logo {
  display: flex;
  align-items: center;
  position: relative;
  height: 34px;
}
.nav__logo-img {
  height: 34px;
  width: auto;
  transition: opacity 0.3s var(--ease-brand);
}
.nav__logo-img--color {
  position: absolute;
  left: 0; top: 0;
  opacity: 0;
}
.nav.is-solid .nav__logo-img--white { opacity: 0; }
.nav.is-solid .nav__logo-img--color { opacity: 1; }

.nav__links {
  display: flex;
  align-items: center;
  /* 2026-08-19: 20px→44px de gap funcionaba con 3 links cortos (Líneas/
     Modelo/Marcas); con las 5 etiquetas nuevas y más largas (Quiénes somos,
     Categorías, Nuestros Servicios, Nuestras Marcas, Socios Comerciales) ese
     máximo apretaba demasiado el logo/CTA en viewports medianos — se reduce
     el rango. */
  gap: clamp(14px, 1.8vw, 26px);
  list-style: none;
}
.nav__link {
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: #fff;
  position: relative;
  padding-bottom: 4px;
}
.nav__link::after {
  content: "";
  position: absolute;
  left: 0; right: 100%; bottom: 0;
  height: 1px;
  background: var(--teal);
  transition: right 0.4s var(--ease-brand), background-color 0.3s var(--ease-brand);
}
.nav__link:hover::after,
.nav__link.is-active::after { right: 0; }
.nav__link.is-active { color: var(--teal); }
.nav.is-solid .nav__link.is-active { color: var(--teal); }

.nav__cta {
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: #fff;
  border-bottom: 1px solid currentColor;
  padding-bottom: 3px;
  display: flex;
  align-items: center;
  gap: 8px;
}

/* ---------- NAV — mega-menú de Categorías ----------
   2026-08-19: pedido en Ptta de ajustes web gbco.mx v1.docx ("me gustaría
   ver el menú en cascada") — al pasar el mouse sobre "Categorías" se abre
   un panel con las 6 categorías y su listado de productos (mismo contenido
   que las tags de cada line-card, ver index.html). Solo desktop: en mobile
   .nav__links completo ya es display:none (menú hamburguesa aparte), así
   que no hace falta ocultarlo por separado. Panel oscuro fijo (no cambia
   con .nav.is-solid) para que el contraste del texto siempre sea legible
   sin depender del estado de scroll del nav. */
.nav__item--has-menu { position: relative; }
/* position:fixed a propósito (no absolute): un panel de hasta 880px con
   absolute+left del <li> podía salirse del viewport en desktop angosto,
   porque "Categorías" no vive centrado en el nav. `left` ya NO es estático
   — main.js lo calcula en cada apertura, pegado al borde izquierdo del
   link y sujeto (clamp) para nunca salirse del viewport. Antes vivía
   centrado en el viewport entero: eso alejaba el panel del trigger y
   obligaba a un recorrido diagonal largo para llegar a columnas lejanas,
   cruzando de sobra la "tierra de nadie" sobre el hero — el hover se
   cortaba a medio camino y el panel se cerraba antes de poder hacer click
   (reportado 2 veces por Leonardo). Pegado al link, ese recorrido se
   acorta al mínimo. */
.nav__megamenu {
  position: fixed;
  top: var(--nav-h);
  left: 0;
  transform: translateY(-6px);
  width: min(880px, calc(100vw - 48px));
  background: var(--navy-deep);
  border: 1px solid #ffffff1a;
  /* Leonardo pidió esquinas normales (sin border-radius) en todos los
     recuadros del sitio — aplica a todos los paneles/tarjetas de esta
     hoja, no solo al nav. */
  border-radius: 0;
  padding: 26px;
  box-shadow: 0 30px 70px rgba(0,0,0,0.4);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 0.3s var(--ease-brand), transform 0.3s var(--ease-brand), visibility 0.3s;
}
/* .is-open la controla JS (main.js) con un pequeño grace-period en el
   mouseleave — un cursor moviéndose en diagonal desde "Categorías" hacia
   una columna lejana del panel (centrado en el viewport, no en el <li>)
   pasa por encima de contenido ajeno (el hero) durante unos ms; con solo
   :hover/:focus-within eso corta la cadena de hover a mitad de camino y
   el panel se cierra antes de llegar (reportado por Leonardo, no se
   arreglaba solo con position/top). El grace-period tolera ese tránsito. */
.nav__item--has-menu:hover .nav__megamenu,
.nav__item--has-menu:focus-within .nav__megamenu,
.nav__item--has-menu.is-open .nav__megamenu {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transform: translateY(0);
}
.nav__megamenu-inner {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 4px 22px;
}
.nav__megamenu-col {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 14px 16px;
  border-radius: 0;
  transition: background-color 0.25s var(--ease-brand);
}
.nav__megamenu-col:hover,
.nav__megamenu-col:focus-visible { background: #ffffff0f; }
.nav__megamenu-col-title {
  font-size: 13px;
  font-weight: 700;
  text-transform: none;
  letter-spacing: 0;
  color: #fff;
}
.nav__megamenu-col-list {
  font-size: 11px;
  line-height: 1.6;
  text-transform: none;
  letter-spacing: 0;
  font-weight: 400;
  color: #f4f4edb3;
}

/* ---------- NAV — mega-menú de Socios Comerciales (logos) ----------
   2026-08-21: Leonardo pidió quitar "Nuestras Marcas" del nav y dejar
   "Socios Comerciales" como el ítem, con vista previa de los logos de
   retail al hover (mismo patrón que Categorías) en vez de una lista de
   texto. Reusa por completo .nav__megamenu (posición fixed anclada al
   link vía JS, grace-period de cierre, todo ya resuelto en main.js/CSS
   genérico por selector de clase) — solo cambia el contenido interno. */
.nav__megamenu-logos {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 10px;
}
.nav__megamenu-logos a {
  position: relative;
  aspect-ratio: 4 / 3;
  background: #f4f4ed;
  transition: background-color 0.25s var(--ease-brand);
}
.nav__megamenu-logos a:hover,
.nav__megamenu-logos a:focus-visible { background: #ffffff; }
.nav__megamenu-logos img {
  position: absolute;
  inset: 0;
  margin: auto;
  width: 78%;
  height: 62%;
  object-fit: contain;
  filter: grayscale(1);
  opacity: 0.75;
  transition: opacity 0.25s var(--ease-brand);
}
.nav__megamenu-logos a:hover img,
.nav__megamenu-logos a:focus-visible img { opacity: 1; }

/* ---------- NAV — submenú compacto de Nuestras Marcas ----------
   2026-08-20: Leonardo sentía el nav "cargado" con 5 ítems de texto.
   Se agrupan "Nuestras Marcas" + "Socios Comerciales" bajo un solo
   trigger (mismo patrón hover/focus-within que .nav__megamenu, pero
   absolute+right:0 en vez de fixed — el panel es angosto (200px) y
   vive cerca del borde derecho del nav, no hay riesgo real de overflow
   como con el mega-menú de 880px). */
.nav__submenu {
  position: absolute;
  /* Mismo motivo que .nav__megamenu: top pegado a 100% (sin +20px) para que
     no exista una franja sin elemento del <li> entre el link y el panel. */
  top: 100%;
  right: 0;
  min-width: 210px;
  background: var(--navy-deep);
  border: 1px solid #ffffff1a;
  border-radius: 0;
  padding: 8px;
  box-shadow: 0 30px 70px rgba(0,0,0,0.4);
  display: flex;
  flex-direction: column;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transform: translateY(-6px);
  transition: opacity 0.3s var(--ease-brand), transform 0.3s var(--ease-brand), visibility 0.3s;
}
.nav__item--has-menu:hover .nav__submenu,
.nav__item--has-menu:focus-within .nav__submenu,
.nav__item--has-menu.is-open .nav__submenu {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transform: translateY(0);
}
.nav__submenu-link {
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: #f4f4ed;
  padding: 12px 14px;
  border-radius: 0;
  transition: background-color 0.25s var(--ease-brand), color 0.25s var(--ease-brand);
}
.nav__submenu-link:hover,
.nav__submenu-link:focus-visible { background: #ffffff0f; color: #fff; }

/* ---------- HERO ---------- */
.hero {
  position: relative;
  min-height: 100vh;
  background: transparent;
  color: #fff;
  display: flex;
  align-items: flex-end;
  overflow: hidden;
}
.hero__media {
  position: absolute;
  inset: 0;
  z-index: 0;
}
.hero__media img {
  width: 100%; height: 120%;
  position: absolute; top: -10%; left: 0;
  object-fit: cover;
  object-position: center;
  opacity: 0.55;
  will-change: transform;
}
/* 2026-08-17: la foto ancha (horizontal) del hero recortaba mal en mobile —
   object-fit:cover con un encuadre pensado para pantallas anchas dejaba a la
   familia muy zoomeada o cortada. Leonardo aportó un crop vertical de la
   misma foto pensado para mobile — se muestra una u otra según el ancho,
   mismo patrón que .nav__logo-img--white/--color (dos <img>, toggle por
   media query, sin JS). */
.hero__media-img--mobile { display: none; }
.hero__media::after {
  content: "";
  position: absolute; inset: 0;
  background: linear-gradient(180deg, var(--navy-deep) 0%, transparent 30%, transparent 60%, var(--navy-deep) 100%);
}
.hero__tiles {
  position: absolute;
  inset: 0;
  z-index: 2;
}
.hero__tile {
  position: absolute;
  background: var(--navy-deep);
  transform-origin: top;
}

.hero__content {
  position: relative;
  z-index: 3;
  width: 100%;
  padding: 0 clamp(24px, 5vw, 80px) clamp(60px, 8vw, 110px);
  display: flex;
  justify-content: flex-end;
}
/* 2026-08-17: a Leonardo le gusta el copy pero sentía que el tratamiento
   se quedaba corto frente a la foto — 34px/300 se leía tímido, no
   editorial. Sube de escala (hasta 44px) y cierra un poco el interlineado
   (1.35→1.25) para que se sienta como un titular deliberado sobre la
   imagen en vez de un párrafo flotando; se mantiene el peso 300 (ligero)
   porque ESE contraste — tipo fino y grande sobre foto — es justo el
   lenguaje editorial premium que se buscaba, solo necesitaba más tamaño
   para sostenerlo. */
.hero__title {
  max-width: 640px;
  font-size: clamp(26px, 3.2vw, 44px);
  line-height: 1.25;
  font-weight: 300;
  letter-spacing: -0.01em;
  text-align: right;
}
.hero__scroll {
  position: absolute;
  left: clamp(24px, 5vw, 80px);
  bottom: clamp(60px, 8vw, 110px);
  z-index: 3;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  border-bottom: 1px solid #fff;
  padding-bottom: 4px;
}

/* ---------- CREAMOS / DESARROLLAMOS — actos normales, sin pin ----------
   2026-08-17 (v5): cada uno es una <section> normal en el flujo del
   documento (min-height:100vh, sin pin/scrub/cortina) — Leonardo pidió dejar
   de animar la "transición" entre estados y tratar los tres actos
   (Creamos/Desarrollamos/Conectamos) como tres secciones que el usuario
   descubre con scroll normal, cada una ya armada en su lugar. El dinamismo
   sin animación viene del acomodo: producto grande + título flotando en la
   esquina, alternando lado — derecha en Creamos, izquierda en Desarrollamos,
   derecha de nuevo en Conectamos (ver más abajo) — mismo lenguaje visual que
   ya funcionaba bien en Conectamos, ahora consistente en los tres.
   2026-08-17 (v6) — sin background propio a propósito: Leonardo pidió que el
   cambio de color entre Creamos y Desarrollamos sea "súper suave, casi
   imperceptible", como el patrón dominant-section-color-detection de
   ComPsych Brand Hub (ver base de datos/compsych-brand-hub, PATTERN-121 a
   124 en PATTERNS.txt). El sitio YA tenía ese sistema implementado
   (scrollColorSystem en main.js interpola --theme-main/--theme-sub del
   .bg-canvas fijo según qué <section> domina el área visible) — pero estas
   secciones tenían su PROPIO fondo sólido opaco, que tapaba el bg-canvas
   por completo: el color cambiaba de golpe en el borde exacto entre
   secciones en vez de fundirse. Quitando el fondo propio, el color real que
   se ve viene del bg-canvas ya interpolado — la transición se vuelve
   automáticamente tan suave como el resto del sitio (ver data-main/data-sub
   en index.html, corregidos para que el bg-canvas resuelva al color
   correcto de cada acto). */
.process-act {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: clamp(110px, 16vh, 180px) 0 clamp(40px, 6vh, 80px);
  background: transparent;
}

.process-act__media {
  flex: 1;
  min-height: 0;
  width: 100%;
  max-width: min(780px, 78vw);
  margin: 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
}
/* 2026-08-17: el producto centrado quedaba pegado (o encima) del copy que
   flota en la esquina — Creamos a la derecha, Desarrollamos a la izquierda.
   Se recarga cada imagen hacia el lado contrario a su copy (translateX,
   relativo al propio ancho de la caja, así escala igual en cualquier
   viewport) para abrir aire entre texto y producto sin tocarse. Creamos
   además se achica un poco (780/78vw → 700/70vw) a pedido de Leonardo. */
.process-act--creamos .process-act__media {
  max-width: min(620px, 62vw);
  transform: translateX(-9%);
}
.process-act--desarrollamos .process-act__media {
  max-width: min(680px, 68vw);
  transform: translateX(24%);
}
.process-act__product {
  width: 100%;
  height: 100%;
  object-fit: contain;
  /* 2026-08-17: la sombra original (50px de blur, 0.35 de opacidad) se veía
     bien sobre el teal de Creamos pero sobre el blanco de Desarrollamos
     dejaba un borrón gris grande debajo del producto — se leía como una
     "división" entre Desarrollamos y Conectamos en vez de una sombra de
     elevación normal. Se aligera en los dos actos para que sea solo un
     acompañamiento sutil, no una mancha. */
  filter: drop-shadow(0 18px 26px rgba(15, 23, 42, 0.16));
}

/* Copy flotando en la esquina superior — misma mecánica que .conectamos__copy,
   default a la derecha (Creamos), .process-act__copy--left la manda a la
   izquierda (Desarrollamos). */
.process-act__copy {
  position: absolute;
  top: clamp(90px, 14vh, 160px);
  right: clamp(24px, 5vw, 80px);
  max-width: 600px;
  text-align: right;
  z-index: 1;
}
.process-act__copy--left {
  right: auto;
  left: clamp(24px, 5vw, 80px);
  text-align: left;
  max-width: 440px; /* Desarrollamos: el copy angosto quiebra el subtítulo a dos
    líneas cortas en vez de una línea larga que llega a tocar el producto
    recargado a la derecha (ver .process-act--desarrollamos .process-act__media). */
}
.process-act__copy .process__sub { max-width: none; margin: 0; }
/* Desarrollamos vive sobre fondo blanco (no navy) — título y subtítulo
   heredan colores pensados para fondo oscuro por defecto, se sobreescriben
   aquí para este acto específico. */
.process-act--desarrollamos .process__title { color: var(--text-dark); }
.process-act--desarrollamos .process__sub { color: var(--text-body); }

.process__title {
  font-size: clamp(2.4rem, 5vw, 5.2rem);
  font-weight: 800;
  line-height: 0.98;
  letter-spacing: -0.01em;
  color: #f7f6f1;
  margin-bottom: 0.32em;
  overflow-wrap: break-word; /* "Desarrollamos" es una sola palabra sin espacios —
    sin esto, a tamaño clamp grande podía desbordar su max-width (460→600px)
    y solaparse con el producto en vez de partirse si hiciera falta. */
}
.process__sub {
  font-size: clamp(14px, 1.3vw, 18px);
  line-height: 1.55;
  color: #f4f4edb3;
  font-weight: 400;
  max-width: 40ch;
  margin: 0 auto;
}

/* Placeholder de producto — mismo lenguaje visual que .line-card__cutout.is-placeholder
   (icono lucide "image" + pill), así toda la web habla el mismo idioma mientras
   faltan los assets definitivos. Se activa solo si la imagen real 404ea (ver
   main.js, mismo patrón que usa para las líneas de producto). La clase va en
   el WRAPPER (.process-act__media / .conectamos__media), nunca en el <img>:
   un elemento reemplazado no pinta ::before/::after propio. */
.process-act__media.is-placeholder,
.conectamos__media.is-placeholder {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  background: linear-gradient(150deg, #ffffff1f, #ffffff08);
  border: 1px solid #ffffff26;
}
.process-act__media.is-placeholder { aspect-ratio: 16 / 10; border-radius: 0; }
.conectamos__media.is-placeholder { aspect-ratio: 1562 / 1007; } /* misma proporción que conectamos-truck.jpg */
.process-act__media.is-placeholder::before,
.conectamos__media.is-placeholder::before {
  content: "";
  width: 34px; height: 34px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23f4f4ed' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='3' width='18' height='18' rx='2'/%3E%3Ccircle cx='9' cy='9' r='2'/%3E%3Cpath d='m21 15-3.1-3.1a2 2 0 0 0-2.8 0L6 21'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-size: contain;
  opacity: 0.6;
}
.process-act__media.is-placeholder::after,
.conectamos__media.is-placeholder::after {
  font-size: 10px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase;
  color: #f4f4edb3;
  padding: 6px 13px;
  border: 1px solid #ffffff2e;
  border-radius: 0;
  background: #ffffff0d;
}
.process-act__media.is-placeholder::after { content: "PNG sin fondo"; }
.conectamos__media.is-placeholder::after { content: "Imagen no disponible"; }
.process-act__media.is-placeholder .process-act__product,
.conectamos__media.is-placeholder .conectamos__truck { display: none; }

/* ---- Conectamos — solo la foto, sin pin, sin scrub, sin fade de JS ----
   El único movimiento es el scroll normal. Tercer acto de la secuencia
   Creamos (derecha) / Desarrollamos (izquierda) / Conectamos (derecha),
   hermano normal de .process-act en el documento. Sin background propio —
   mismo criterio que .process-act (ver v6 arriba): el color real viene del
   .bg-canvas interpolado (data-main="#ffffff" en el <section>), consistente
   con el sistema de color suave del resto del sitio en vez de un fondo
   sólido que corte de golpe contra Desarrollamos. La foto se muestra a su
   alto natural completo (ancho 100%, alto automático, sin object-fit) — el
   tráiler sale entero, sin recortar un pixel; simplemente mide más de un
   viewport de alto, y eso está bien. */
.conectamos {
  position: relative;
  background: transparent;
}
.conectamos__media img {
  width: 100%;
  height: auto;
  display: block;
}
/* El título vive como overlay fijo en la esquina superior derecha de la foto
   del camión. Esa esquina es cielo blanco de la imagen (no la caja azul del
   camión), por eso el texto oscuro sigue siendo legible sin necesitar scrim
   ni gradiente nuevo. */
.conectamos__copy {
  position: absolute;
  top: clamp(90px, 14vh, 160px);
  right: clamp(24px, 5vw, 80px);
  max-width: 460px;
  text-align: right;
  z-index: 1;
}
/* Compacto respecto a Creamos/Desarrollamos (títulos a pantalla completa,
   mismo peso 800): Conectamos sigue siendo una esquina, no un acto
   fullscreen, así que no llega al tamaño de los otros dos. */
.conectamos__copy .process__title {
  color: var(--text-dark);
  font-size: clamp(40px, 4.5vw, 64px);
  margin-bottom: 0.3em;
}
.conectamos__copy .process__sub {
  color: var(--text-body);
  font-size: clamp(13px, 1.1vw, 16px);
  max-width: none;
  margin: 0;
}

/* ---------- MANIFESTO ---------- */
.manifesto {
  background: transparent;
  color: #fff;
  padding: clamp(80px, 12vw, 180px) 0 clamp(100px, 14vw, 200px);
}
.manifesto__grid {
  display: grid;
  grid-template-columns: 1fr 1.4fr;
  gap: clamp(30px, 5vw, 80px);
  align-items: center;
}
.manifesto__img {
  aspect-ratio: 3/4;
  overflow: hidden;
  background: #ffffff10;
}
.manifesto__img img {
  width: 100%; height: 122%;
  position: relative; top: -11%;
  object-fit: cover;
  will-change: transform;
}
.manifesto__text {
  font-size: clamp(24px, 3.4vw, 46px);
  line-height: 1.08;
  font-weight: 800;
}

/* ---------- CTA INTERMEDIO (entre Conectamos y Manifesto) ----------
   Jerarquía invertida a pedido de Leonardo: "¿Ya tienes la idea?" es ahora
   el titular dominante de la banda (mismo peso 800 que los actos de
   Creamos/Desarrollamos, aunque un punto más chico — esto es una banda de
   pausa, no un acto fullscreen). "Hablemos de cómo..." baja a subtítulo. */
.cta-band {
  background: transparent;
  color: #fff;
  padding: clamp(60px, 9vw, 120px) 0 clamp(80px, 11vw, 160px);
  border-top: 1px solid #ffffff1a;
}
.cta-band__inner {
  max-width: 820px;
  text-align: center;
}
.cta-band__title {
  font-size: clamp(40px, 6vw, 84px);
  font-weight: 800;
  line-height: 1.02;
  letter-spacing: -0.01em;
  margin-bottom: 0.4em;
}
.cta-band__subtitle {
  font-size: clamp(17px, 1.8vw, 22px);
  font-weight: 400;
  line-height: 1.4;
  color: #f4f4edcc;
  max-width: 46ch;
  margin: 0 auto clamp(28px, 4vw, 44px);
}
.cta-band__link {
  display: inline-flex; align-items: center; gap: 10px;
  font-size: 13px; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.06em;
  border-bottom: 1px solid #fff;
  padding-bottom: 4px;
}

/* ---------- SECTION HEADER (reused pattern) ----------
   2026-08-17: Leonardo pidió un tratamiento más editorial para los
   encabezados de sección ("Nuestro modelo", "Nuestro portafolio" y demás)
   — congruente con el resto del sitio, que ya maneja tipografía grande y
   segura en Creamos/Desarrollamos y en los números de Modelo. El h2 pasa
   de un tamaño más bien de "subtítulo" (28-48px) a un titular real
   (34-68px) con line-height apretado (0.98) y tracking negativo (-0.02em)
   — la fórmula típica de titular editorial grande: cuanto más grande el
   tipo, más se cierra el interlineado para que se sienta como una sola
   declaración, no un párrafo. El eyebrow gana más aire debajo (kicker de
   revista) y el párrafo sube un poco de tamaño con más line-height para
   que no se sienta chico al lado del h2 nuevo. Afecta a las 4 secciones
   que reusan .section-head (Líneas, Modelo, Marcas, footer) — footer ya
   tiene su propio font-size más específico y no cambia. */
.section-head {
  max-width: 760px;
  margin-bottom: clamp(56px, 7vw, 110px);
}
.section-head .eyebrow { margin-bottom: 1.6em; }
.section-head h2 {
  font-size: clamp(34px, 5vw, 68px);
  line-height: 0.98;
  letter-spacing: -0.02em;
  margin-bottom: 0.55em;
}
.section-head p {
  font-size: clamp(15px, 1.2vw, 18px);
  line-height: 1.65;
  max-width: 560px;
}

/* ---------- LINES (scroll-story de líneas de producto) ---------- */
/* [E] Composición inspirada en likova.space: fondo ambientado blureado (full-bleed) +
   panel oscuro con título/progreso + producto recortado que "sale" del panel + texto
   sobre la foto. Cada .line-card trae su propio fondo/cutout — ver comentario en index.html. */
.lines {
  background: transparent;
  padding: clamp(80px, 10vw, 140px) 0 0;
}
.lines__intro { padding-bottom: clamp(40px, 6vw, 80px); }
.lines__sticky {
  position: sticky;
  top: 0;
  height: 100vh;
  overflow: hidden;
}
/* Carrusel horizontal — reemplaza el crossfade de opacity que usaba antes
   (.model-card, más abajo, sigue con crossfade — esto es solo para las
   líneas). Leonardo pidió más dinamismo que un "flash": al cambiar de
   línea, la card activa sale por la izquierda y la siguiente entra por la
   derecha, como un slider real. El movimiento lo maneja GSAP por completo
   (xPercent, ver createLinesCarousel en main.js) — sin transition de CSS,
   para no competir con los tweens. backdrop y cutout van SEPARADOS (no en
   un wrapper compartido): si compartieran contenedor, su z-index quedaría
   atrapado dentro de ese contenedor y el panel (que vive afuera) los
   taparía sin importar qué z-index tuvieran adentro. */
.line-card {
  position: absolute;
  inset: 0;
  overflow: hidden;
  opacity: 1;
  pointer-events: none;
  will-change: transform;
  /* Geometría del panel expuesta como variables — .line-card__cutout las lee
     para anclarse SIEMPRE contra el borde derecho real del panel (ver más
     abajo), en vez de tener su propio `left` independiente que se podía
     desalinear del panel a ciertos anchos de viewport. */
  --panel-l: clamp(20px, 5vw, 80px);
  --panel-w: clamp(320px, 40vw, 620px);
  --info-w: clamp(220px, 24vw, 400px);
}
.line-card.is-active { pointer-events: auto; z-index: 1; }

/* left/right -3px (en vez de inset:0 a secas): el fondo saliente y el
   entrante se cruzan en el mismo punto matemático exacto durante el
   carrusel (ver createLinesCarousel en main.js), pero cada uno redondea su
   propia posición a subpíxel por separado — eso dejaba una línea blanca de
   un frame entre los dos (el .bg-canvas de esta sección es claro). Este
   pequeño "sangrado" hace que siempre se solapen 6px de sobra durante el
   cruce, así el redondeo nunca alcanza a abrir una costura visible. Sin
   efecto en reposo: .line-card (el padre) sigue recortando con su propio
   overflow:hidden exactamente al ancho real de la pantalla. */
.line-card__backdrop {
  position: absolute;
  top: 0;
  bottom: 0;
  left: -3px;
  right: -3px;
  overflow: hidden;
  z-index: 1;
  will-change: transform;
}

/* Acento por línea — solo tiñe el placeholder mientras no hay foto real.
   --cutout-scale corrige el margen transparente propio de cada PNG (Photoroom
   no recorta igual todas las fotos) para que el producto se vea del mismo
   tamaño real en pantalla en las 6 líneas — medido con PIL (bbox de alpha):
   2026-08-15: las 6 líneas se re-recortaron al bbox real del alpha (padding
   uniforme ~20px, ~94-99% de alto útil en las 6) — antes higiene (61%) y
   alimentos (86%) traían mucho margen transparente de fábrica y se veían
   chicas/disparejas aunque el --cutout-scale intentara compensarlo. Con
   todas recortadas igual de ajustado, --cutout-scale ya no necesita un
   valor distinto por línea: 1 en las 6 las deja del mismo tamaño relativo
   y con el mismo efecto de "pegado al piso". Si se sube una foto nueva sin
   recortar, hay que volver a ajustarlo aquí en vez de solo subir el PNG. */
/* 2026-08-16 — Leonardo marcó la bolsa de alimentos (0.75) como el estándar
   de tamaño perfecto ("ese es el estándar de belleza") y pidió igualar el
   resto contra ella en vez de que cada quien tenga su propia lógica de
   "porte real": hogar y mascotas se veían demasiado grandes → mismo 0.75
   que alimentos. bebé se veía muy grande → un poco MÁS CHICO que alimentos
   (una crema es más chica que una bolsa de snacks). higiene y piel no se
   mencionaron como problema, se dejan igual. */
/* 2026-08-21 — Leonardo subió fotos nuevas para las 6 categorías: ya no
   es 1 producto suelto, es la composición de 2-3 productos en una sola
   foto (el "2-3 imágenes por categoría" que pidió en el doc de ajustes,
   resuelto como una sola toma agrupada en vez de varias imágenes
   sueltas). Las calibraciones de scale/shift de abajo eran específicas
   de las fotos viejas (forma/proporción de un solo envase) y ya no
   aplican — las 6 quedan en el mismo 0.75 porque ahora las 6 comparten
   estilo de composición (mismo tipo de lineup de 2-3 productos, ratio
   ~3:2 casi idéntico entre todas salvo bebé). */
.line-card--higiene   { --line-accent-soft: #0ea3914d; --cutout-scale: 0.75; }
.line-card--piel      { --line-accent-soft: #d98a6b4d; --cutout-scale: 0.75; }
.line-card--hogar     { --line-accent-soft: #4f7cff4d; --cutout-scale: 0.75; }
.line-card--bebe      { --line-accent-soft: #f2b8c64d; --cutout-scale: 0.75; }
.line-card--mascotas  { --line-accent-soft: #e0a63c4d; --cutout-scale: 0.75; }
.line-card--alimentos { --line-accent-soft: #8bbf5a4d; --cutout-scale: 0.75; }

/* Fondo ambientado, full-bleed, blureado vía CSS (no se sube ya blureado) */
.line-card__bg {
  position: absolute;
  inset: 0;
  overflow: hidden;
  background: linear-gradient(160deg, var(--navy-deep), var(--navy));
}
.line-card__bg img {
  width: 100%; height: 100%;
  object-fit: cover;
  /* [E] blur ligero tipo likova.space: la foto se reconoce (troncos, textura),
     no se vuelve una mancha de color — el scrim es el que da la legibilidad */
  filter: blur(10px) saturate(1.05) brightness(0.92);
  transform: scale(1.05);
  opacity: 0;
  transition: opacity 0.5s var(--ease-brand);
}
.line-card__bg img.is-loaded { opacity: 1; }
.line-card__bg.is-placeholder::after {
  content: "";
  position: absolute; inset: 0;
  background:
    linear-gradient(180deg, #ffffff0d, transparent 30%),
    radial-gradient(90% 70% at 14% 12%, var(--line-accent-soft), transparent 60%);
}

.line-card__scrim {
  position: absolute; inset: 0;
  background:
    linear-gradient(100deg, #0a0e26e6 0%, #0a0e26b8 32%, #0a0e2645 56%, transparent 76%),
    linear-gradient(0deg, #0a0e2666 0%, transparent 32%);
  pointer-events: none;
}

/* Panel oscuro: liso, azul profundo opaco (sin glass/blur) — título de línea + progreso.
   [E] Mucho más grande que la v1: ancho ~45vw y alto fijo ~70vh, como el
   recuadro de likova.space — así deja aire real abajo, no envuelve el texto
   al ras. panel-top y panel-bottom se separan con justify-content:space-between
   para lograr ese "vacío" intencional entre el título y el índice/ticks.
   Visibilidad heredada de .line-card (opacity crossfade del contenedor) —
   panel/info ya no manejan su propia opacity/pointer-events por separado,
   así todo el bloque (fondo, producto, texto) aparece y desaparece a la vez. */
.line-card__panel {
  position: absolute;
  left: var(--panel-l);
  top: clamp(90px, 12vh, 150px);
  width: var(--panel-w);
  height: clamp(420px, 70vh, 660px);
  padding: clamp(26px, 2.8vw, 36px) clamp(26px, 2.8vw, 34px);
  background: var(--navy-deep);
  border: 1px solid #ffffff14;
  z-index: 5; /* siempre por encima de .line-card__backdrop (z 1-2) de CUALQUIER card */
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  will-change: transform;
}
.line-card__panel-top { display: flex; flex-direction: column; }
.line-card__eyebrow {
  display: block;
  font-size: 11px; font-weight: 700; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--teal);
  margin-bottom: 0.9em;
}
.line-card__title {
  font-size: clamp(22px, 2.5vw, 32px);
  font-weight: 700;
  line-height: 1.06;
  color: #f7f6f1;
}
/* Barra de progreso arriba del título (no abajo): eyebrow > ticks > título,
   con aire antes del título para que se sienta "bajado" a propósito */
.line-card__ticks { display: flex; gap: 5px; margin-bottom: 1.4em; }
/* position:relative + ::after con inset negativo: amplía el área de click
   muy por encima de los 2px visuales de la barra (main.js ya escucha click
   en el span mismo — el ::after solo agranda el hit-area, no cambia el
   look). Permite saltar directo a una línea sin depender de la velocidad de
   scroll — ver createLinesCarousel en main.js. */
.line-card__ticks span {
  position: relative;
  flex: 1;
  height: 2px;
  background: #ffffff26;
  border-radius: 0;
  cursor: pointer;
  transition: background-color 0.3s var(--ease-brand);
}
.line-card__ticks span::after {
  content: "";
  position: absolute;
  inset: -9px 0;
}
.line-card__ticks span:hover { background: #ffffff55; }
.line-card__ticks span.is-current { background: var(--teal); }
.line-card__ticks span.is-current:hover { background: var(--teal); }

/* Producto recortado — mismo ancho/lado que el panel (nunca se sale por
   izquierda/derecha, referencia [C] verificada contra likova.space: la
   corredora tampoco se sale del cuadro por los costados), pero por ABAJO sí
   rompe el borde del panel y sigue bajando hasta tocar el piso real de la
   sección — en la referencia las piernas de la corredora salen del cuadro
   y llegan hasta el piso de la pantalla, el cuadro solo queda detrás de la
   mitad de arriba. z-index:10 — por encima del texto del panel (5); como
   sigue anclado al lado derecho, dentro del ancho del panel, nunca llega a
   tapar el título (ver medición real con Range API, sin falsos positivos
   del bounding-box de bloque). Visibilidad heredada del padre .line-card.

   2026-08-16 — height/bottom vuelven a anclarse contra el PISO DE LA
   SECCIÓN (no el del panel): el `--panel-l/--panel-w` siguen controlando
   left/width (eso es lo que ya quedó bien — "del lado correcto"), pero
   ahora crece hacia arriba desde el piso real en vez de quedar encerrado
   en la altura del panel. Mismos --cutout-scale de antes (0.63–1), solo
   cambia la base del clamp para que alcancen a romper el borde inferior
   del panel y llegar hasta abajo. */
.line-card__cutout {
  position: absolute;
  left: var(--panel-l);
  width: var(--panel-w);
  bottom: clamp(-24px, -3vh, -8px);
  height: min(calc(clamp(460px, 80vh, 780px) * var(--cutout-scale, 1)), 92vh);
  padding: 0 clamp(26px, 2.8vw, 34px) 0 0;
  display: flex;
  align-items: flex-end;
  justify-content: flex-end;
  z-index: 10;
  will-change: transform;
}
.line-card__cutout img {
  max-height: 100%;
  max-width: 100%;
  width: auto;
  height: auto;
  object-fit: contain;
  display: block;
  opacity: 0;
  filter: drop-shadow(0 20px 28px rgba(0,0,0,0.45));
  transition: opacity 0.5s var(--ease-brand);
  /* Empujón horizontal fino por línea, sin tocar tamaño ni posición vertical
     — solo translateX, no participa del layout (no cambia --cutout-scale ni
     el alto/ancho real). Default 0 = sin cambio para las 5 líneas normales;
     higiene lo usa para deslizarse un poco a la derecha dentro del cuadro. */
  transform: translateX(var(--cutout-shift-x, 0px));
}
.line-card__cutout img.is-loaded { opacity: 1; }
/* Placeholder del cutout: mismo lenguaje liso/opaco del panel (sin glass) +
   ícono lucide "image" + etiqueta en pill — se lee como un slot de asset a
   la espera de foto, no como un bloque roto. Tamaño fijo (no depende de img).
   2026-08-15: reposicionado para vivir DENTRO del panel igual que una foto
   real (antes se anclaba debajo del panel, que ya no está permitido) — se
   calcula para que su esquina inferior-derecha caiga justo en la esquina
   inferior-derecha interna del panel (mismo padding que usa la foto real). */
.line-card__cutout.is-placeholder {
  top: calc(clamp(90px, 12vh, 150px) + clamp(420px, 70vh, 660px) - clamp(220px, 30vh, 340px) - clamp(26px, 2.8vw, 36px));
  left: calc(var(--panel-l) + var(--panel-w) - clamp(260px, 24vw, 380px) - clamp(26px, 2.8vw, 34px));
  bottom: auto;
  padding: 0;
  width: clamp(260px, 24vw, 380px);
  height: clamp(220px, 30vh, 340px);
  max-width: none;
  border-radius: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  background: linear-gradient(150deg, var(--line-accent-soft), var(--navy-deep) 78%);
  border: 1px solid #ffffff14;
}
.line-card__cutout.is-placeholder img { display: none; }
.line-card__cutout.is-placeholder::before {
  content: "";
  width: 30px; height: 30px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23f4f4ed' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='3' width='18' height='18' rx='2'/%3E%3Ccircle cx='9' cy='9' r='2'/%3E%3Cpath d='m21 15-3.1-3.1a2 2 0 0 0-2.8 0L6 21'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-size: contain;
  opacity: 0.55;
}
.line-card__cutout.is-placeholder::after {
  content: "PNG sin fondo";
  font-size: 10px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase;
  color: #f4f4edb3;
  padding: 6px 13px;
  border: 1px solid #ffffff2e;
  border-radius: 0;
  background: #ffffff0d;
}

/* Texto descriptivo — centrado verticalmente sobre la foto, a la derecha.
   No pegado al piso: así no compite con el producto anclado abajo. */
.line-card__info {
  position: absolute;
  right: clamp(24px, 6vw, 100px);
  top: 50%;
  transform: translateY(-50%);
  max-width: var(--info-w);
  z-index: 6; /* por encima del panel (5) y del producto (10 en reposo, pero éste no llega a esta zona) */
  will-change: transform;
}
.line-card__info p { font-size: 14px; color: #f4f4edd9; margin-bottom: 1.5em; }
/* Leonardo pidió el listado de productos "en cascada" (doc de ajustes,
   repetido en las 6 categorías) — antes eran pills en fila. Ahora es una
   lista vertical simple; cada <li> entra por separado con su propio stagger
   en animateCardEntrance (main.js) — el efecto cascada es ese reveal
   escalonado en scroll. La indentación estática (staircase) se probó y se
   quitó: con distinto ancho por línea, el border-bottom quedaba de largo
   desigual y se veía "chueco" en vez de intencional. */
.line-card__tags {
  display: flex;
  flex-direction: column;
  gap: 9px;
  list-style: none;
}
.line-card__tags li {
  font-size: 13px;
  letter-spacing: 0.01em;
  color: #f4f4ed;
  padding-bottom: 9px;
  border-bottom: 1px solid #ffffff26;
}
.line-card__tags li::before { content: "—"; margin-right: 10px; color: #f4f4ed80; }

/* ---------- MODELO — scroll-story fullscreen, color por card ---------- */
.model-story {
  background: transparent;
  padding-top: clamp(80px, 10vw, 140px);
  border-top: 1px solid var(--border);
}
.model-story__intro { padding-bottom: clamp(50px, 7vw, 90px); }
.model-story__intro .eyebrow,
.model-story__intro h2,
.model-story__intro p { color: var(--text-dark); }

.model-story__sticky {
  position: sticky;
  top: 0;
  height: 100vh;
  overflow: hidden;
}
/* Acomodo editorial: número fijo en su propia columna a la izquierda, con
   una línea vertical divisoria, y el cuerpo en columna aparte con más aire —
   mismo patrón de grid que .manifesto__grid (1fr/1.4fr), para no introducir
   un lenguaje de grid nuevo en el sitio. Antes el número gigante flotaba
   apilado arriba del texto; ahora vive alineado con él como un índice fijo. */
.model-card {
  position: absolute;
  inset: 0;
  display: grid;
  grid-template-columns: 1fr 1.4fr;
  align-items: center;
  gap: clamp(30px, 5vw, 80px);
  padding: 0 clamp(24px, 5vw, 80px);
  color: #fff;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.5s var(--ease-brand);
}
.model-card.is-active { opacity: 1; pointer-events: auto; }
.model-card__num {
  position: relative;
  font-size: clamp(150px, 16vw, 320px);
  font-weight: 700;
  line-height: 1;
  opacity: 0.18;
  padding-right: clamp(24px, 3vw, 48px);
}
.model-card__num::after {
  content: "";
  position: absolute;
  top: 8%; bottom: 8%; right: 0;
  width: 1px;
  background: #ffffff26;
}
.model-card__body { max-width: 640px; }
.model-card__body h3 { font-size: clamp(28px, 4vw, 52px); margin-bottom: 0.5em; font-weight: 700; }
.model-card__body p { font-size: clamp(15px, 1.4vw, 18px); color: #ffffffcc; max-width: 560px; }
/* 2026-08-25: logo de cada división (G Labs/Trade/Brands/Pack) sobre placa
   blanca — los 4 colores de marca (celeste/rojo/magenta/verde) no garantizan
   contraste propio contra el degradado teal→navy de cada card, así que se
   asegura legibilidad y un acabado "sello" consistente entre las 4 tarjetas. */
.model-card__logo {
  display: inline-flex;
  align-items: center;
  background: #fff;
  border-radius: 12px;
  padding: clamp(10px, 1.3vw, 15px) clamp(18px, 2.2vw, 26px);
  margin-bottom: clamp(20px, 2.6vw, 30px);
  box-shadow: 0 14px 32px -14px rgba(0, 0, 0, 0.45);
}
.model-card__logo img { display: block; height: clamp(24px, 2.4vw, 36px); width: auto; }

/* ---------- MARCAS — 3 tratamientos: grid / marquee / logo wall ---------- */
.brands {
  background: transparent;
  padding: clamp(80px, 10vw, 140px) 0 0;
  border-top: 1px solid var(--border);
}
.brands__intro { padding-bottom: clamp(50px, 7vw, 90px); }

.brands__group { padding: clamp(60px, 8vw, 100px) 0; }
.brands__group-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 20px;
  margin-bottom: clamp(32px, 4vw, 56px);
}
.brands__group-tag {
  font-size: 12px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  font-weight: 700;
  white-space: nowrap;
}
.brands__group-head h3 {
  font-size: clamp(22px, 2.6vw, 32px);
  font-weight: 500;
}
/* Marcas exclusivas (03) es el único grupo con descripción propia debajo
   del título — flex-basis:100% la baja a su propia línea en vez de
   apretarse junto al tag/h3 en la misma fila baseline. */
.brands__group-head p {
  flex-basis: 100%;
  font-size: 14px;
  color: var(--text-body);
}

/* --- G Brands: logo wall 3x3 simétrico sobre fondo teal→navy (mismo del brochure), reveal de color en hover --- */
.brands__group--own .brands__group-tag { color: var(--teal); }
.brands__group--exclusive .brands__group-tag { color: var(--teal); }
.brands__group-head p { flex-basis: 100%; margin-top: -4px; }
.brand-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1px;
  background: rgba(255, 255, 255, 0.1);
  border: 1px solid rgba(255, 255, 255, 0.1);
}
.brand-tile {
  position: relative;
  aspect-ratio: 4 / 3;
  background: linear-gradient(135deg, var(--navy-deep) 0%, #142050 65%, var(--navy-deep) 100%);
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  isolation: isolate;
  padding: clamp(20px, 3vw, 40px);
}
.brand-tile::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--teal);
  transform: translateY(101%);
  transition: transform 0.55s var(--ease-brand);
  z-index: 0;
}
.brand-tile:hover::before { transform: translateY(0); }
.brand-tile__logo {
  /* 2026-08-21: era max-width/max-height con width/height:auto dentro de un
     flex item — % height en un flex item no resuelve confiable (necesita
     que el propio flex item tenga alto definido en el eje cruzado, que acá
     no lo tenía a pesar del aspect-ratio del padre), así que cada logo
     terminaba con un tamaño distinto según su proporción intrínseca (ej.
     Nanna casi cuadrado se salía del tile por completo, escondido por el
     overflow:hidden; Smarttape ancho y delgado quedaba diminuto) — reportado
     como "no están bien alineadas/centradas". position:absolute + inset en
     % SÍ resuelve confiable (el containing block, .brand-tile, tiene alto
     definido vía aspect-ratio) — los 9 logos comparten ahora exactamente el
     mismo marco (70% ancho / 56% alto), object-fit:contain solo decide
     cómo cada uno llena ESE marco según su propia proporción. */
  position: absolute;
  inset: 0;
  margin: auto;
  z-index: 1;
  width: 70%;
  height: 56%;
  object-fit: contain;
  transition: transform 0.45s var(--ease-brand);
}
.brand-tile:hover .brand-tile__logo { transform: scale(1.06); }

/* --- G Trade: marquee horizontal infinito, dos filas --- */
.brands__group--trade {
  background: var(--navy-deep);
  color: #fff;
}
.brands__group--trade .brands__group-tag { color: var(--teal); }
.brands__group--trade .brands__group-head h3 { color: #fff; }
.marquee {
  overflow: hidden;
  display: flex;
  flex-direction: column;
  gap: 14px;
  mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
}
.marquee__track {
  display: flex;
  width: max-content;
  gap: 40px;
  white-space: nowrap;
  animation: marquee-scroll 38s linear infinite;
}
.marquee__row--2 .marquee__track {
  animation-direction: reverse;
  animation-duration: 44s;
}
/* 2026-08-21: era height:100%/width:auto/max-width — sin una caja propia
   definida, cada logo resolvía su alto contra su propia proporción
   intrínseca en vez de contra un marco común: logos como "Clean & Dry"
   terminaban mucho más altos que sus vecinos y se metían visualmente en
   la fila de arriba/abajo (reportado por Leonardo con captura). El span
   ahora tiene ancho Y alto fijos (mismo criterio que .brand-tile__logo
   más arriba) y el img se ajusta con position:absolute + object-fit
   dentro de esa caja fija — los ~30 logos del marquee comparten el mismo
   marco, sin depender de su proporción individual. */
.marquee__track span {
  position: relative;
  width: clamp(120px, 12vw, 190px);
  height: clamp(30px, 3.6vw, 46px);
}
.marquee__track img {
  position: absolute;
  inset: 0;
  margin: auto;
  width: 100%;
  height: 100%;
  object-fit: contain;
  filter: brightness(0) invert(1);
  opacity: 0.5;
  transition: opacity 0.3s var(--ease-brand);
}
.marquee__track span:hover img { opacity: 1; }
@keyframes marquee-scroll {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}

/* --- Marcas exclusivas (03): mismo grid que G Brands, tag propio en navy
   para diferenciarse del teal de G Brands/G Trade --- */
.brands__group--exclusive .brands__group-tag { color: var(--navy); }

/* --- Retailers: logo wall simétrico 6x2, mismo lenguaje que G Brands en tono claro --- */
.brands__group--retail .brands__group-tag { color: var(--navy); }
.retail-grid {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 1px;
  background: var(--border);
  border: 1px solid var(--border);
}
.retail-tile {
  position: relative;
  aspect-ratio: 4 / 3;
  background: var(--cream);
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  padding: clamp(18px, 2.6vw, 34px);
  transition: background 0.4s var(--ease-brand);
}
.retail-tile:hover { background: #fff; }
.retail-tile__logo {
  max-width: 80%;
  max-height: 58%;
  width: auto;
  height: auto;
  object-fit: contain;
  filter: grayscale(1);
  opacity: 0.75;
  transition: opacity 0.4s var(--ease-brand), transform 0.45s var(--ease-brand);
}
.retail-tile:hover .retail-tile__logo { opacity: 1; transform: scale(1.06); }

/* ---------- CIERRE / CTA ---------- */
.closing {
  position: relative;
  min-height: 70vh;
  background: transparent;
  color: #fff;
  display: flex;
  align-items: center;
  overflow: hidden;
}
.closing__media { position: absolute; inset: 0; z-index: 0; overflow: hidden; }
.closing__media img {
  width: 100%; height: 122%;
  position: absolute; top: -11%; left: 0;
  object-fit: cover; opacity: 0.35;
  will-change: transform;
}
.closing__media::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(90deg, var(--navy-deep) 20%, transparent 70%);
}
.closing__content { position: relative; z-index: 2; max-width: 640px; }
.closing__content h2 {
  font-size: clamp(28px, 4vw, 52px);
  margin-bottom: 0.7em;
}
.closing__cta {
  display: inline-flex; align-items: center; gap: 10px;
  font-size: 13px; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.06em;
  border-bottom: 1px solid #fff;
  padding-bottom: 4px;
}

/* ---------- FOOTER / CONTACT — rediseño con textura de marca ---------- */
.footer {
  position: relative;
  background: transparent;
  color: #fff;
  padding: clamp(90px, 11vw, 160px) 0 40px;
  overflow: hidden;
}
/* 2026-08-17: la textura era un anillo genérico dibujado por CSS
   (radial-gradient) — Leonardo pidió usar el monograma real de marca en su
   lugar: el glifo "CO" del wordmark GBCO, extraído del patrón que ya usa el
   brochure oficial (portada de Brochure GBCO 2026.pdf) como PNG/WebP con
   transparencia, tileado igual que el patrón anterior. Mismo mecanismo de
   fade (mask-image con radial-gradient hacia la esquina) — mismo efecto,
   ahora con el patrón de marca real en vez de una forma genérica. */
.footer__pattern {
  position: absolute;
  inset: 0;
  z-index: 0;
  opacity: 0.16;
  pointer-events: none;
  background-image: url("../img/patterns/monogram-tile.webp");
  background-size: 96px 265px;
  background-repeat: repeat;
  mask-image: radial-gradient(ellipse 70% 70% at 100% 0%, #000 0%, transparent 75%);
}
.footer .container { position: relative; z-index: 1; }

.footer__intro { margin-bottom: clamp(56px, 7vw, 96px); }
.footer__intro .eyebrow { color: var(--teal); }
.footer__intro h2 { color: #fff; font-size: clamp(32px, 4.2vw, 56px); max-width: 780px; }
.footer__intro p { color: #ffffffaa; }

.footer__top {
  display: grid;
  grid-template-columns: 1.3fr 1fr;
  gap: clamp(40px, 6vw, 100px);
  margin-bottom: clamp(70px, 9vw, 110px);
}

.footer__form { display: flex; flex-direction: column; gap: 26px; }
.footer__field { display: flex; flex-direction: column; gap: 8px; position: relative; }
.footer__field label {
  font-size: 11px; text-transform: uppercase; letter-spacing: 0.08em;
  color: #ffffff88;
}
.footer__field input, .footer__field textarea {
  font-family: var(--font-display);
  background: transparent;
  border: none;
  border-bottom: 1px solid #ffffff33;
  padding: 10px 0;
  font-size: 18px;
  font-weight: 300;
  color: #fff;
  outline: none;
  resize: none;
  transition: border-color 0.3s var(--ease-brand);
}
.footer__field input::placeholder, .footer__field textarea::placeholder { color: #ffffff44; }
.footer__field input:focus, .footer__field textarea:focus { border-color: var(--teal); }

.footer__form button {
  margin-top: 8px;
  align-self: flex-start;
  font-family: var(--font-display);
  font-size: 13px; font-weight: 500; text-transform: uppercase;
  letter-spacing: 0.06em;
  background: var(--teal);
  color: #fff;
  border: none;
  padding: 16px 32px;
  cursor: pointer;
  transition: background-color 0.3s var(--ease-brand), transform 0.3s var(--ease-brand);
}
.footer__form button:hover { background: #fff; color: var(--navy-deep); transform: translateX(4px); }

.footer__info { display: flex; flex-direction: column; gap: clamp(28px, 4vw, 44px); }
.footer__info-block h4 {
  font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--teal); margin-bottom: 0.9em; font-weight: 700;
}
.footer__info-block p, .footer__info-block a {
  font-size: 15px; font-weight: 300; display: block;
  margin-bottom: 6px; color: #ffffffdd; line-height: 1.5;
}

.footer__bottom {
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: 24px;
  padding-top: 24px;
  border-top: 1px solid #ffffff22;
  font-size: 12px;
  color: #ffffff88;
}

/* ---------- NAV BURGER + MOBILE MENU (solo visible <900px) ---------- */
.nav__burger {
  display: none;
  width: 28px; height: 20px;
  background: none; border: none;
  cursor: pointer;
  flex-direction: column;
  justify-content: space-between;
  padding: 0;
  z-index: 210;
}
.nav__burger span {
  display: block;
  width: 100%; height: 2px;
  background: #fff;
  transition: transform 0.4s var(--ease-brand), opacity 0.3s var(--ease-brand), background-color 0.3s;
}
.nav.is-solid .nav__burger span { background: var(--text-dark); }
.nav__burger.is-open span { background: #fff !important; }
.nav__burger.is-open span:first-child { transform: translateY(9px) rotate(45deg); }
.nav__burger.is-open span:last-child { transform: translateY(-9px) rotate(-45deg); }

.mobile-menu {
  position: fixed;
  inset: 0;
  z-index: 200;
  background: var(--navy-deep);
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: clamp(40px, 8vh, 70px);
  padding: 0 clamp(24px, 8vw, 60px);
  transform: translateY(-100%);
  transition: transform 0.55s var(--ease-brand);
}
.mobile-menu.is-open { transform: translateY(0); }
.mobile-menu__links { list-style: none; display: flex; flex-direction: column; gap: 20px; }
.mobile-menu__links a {
  font-size: clamp(32px, 9vw, 48px);
  font-weight: 500;
  color: #fff;
}
.mobile-menu__cta {
  font-size: 14px; font-weight: 500; text-transform: uppercase;
  letter-spacing: 0.06em; color: var(--teal);
  border-bottom: 1px solid var(--teal);
  display: inline-flex; align-self: flex-start; padding-bottom: 4px;
}

/* ---------- RESPONSIVE ---------- */
@media (max-width: 900px) {
  :root { --nav-h: 68px; }

  /* Nav: oculta links de escritorio, muestra hamburguesa */
  .nav__links,
  .nav__cta--desktop { display: none; }
  .nav__burger { display: flex; }

  /* Hero */
  .hero__content { justify-content: flex-start; padding-bottom: 60px; }
  .hero__title { text-align: left; font-size: clamp(19px, 5vw, 24px); }
  .hero__scroll { display: none; }
  .hero__media-img--desktop { display: none; }
  .hero__media-img--mobile { display: block; }

  /* Creamos / Desarrollamos en mobile — mismo criterio que Conectamos (ver
     abajo): el copy flotando en la esquina, pensado para un producto grande
     a pantalla completa, se apretaba contra la imagen en pantallas angostas.
     Se aplana a bloque normal en flujo — texto arriba, producto debajo —
     mismo tratamiento en los tres actos para consistencia total en mobile;
     el zigzag derecha/izquierda es un detalle de composición de escritorio
     que no tiene el mismo sentido con tan poco ancho disponible. */
  /* 2026-08-17: el texto no tenía padding horizontal propio en mobile (solo
     heredaba el `right`/`left` del posicionamiento absoluto de escritorio,
     que deja de aplicar en cuanto pasa a position:relative aquí) — quedaba
     pegado al borde real de la pantalla. Tampoco había un espaciado
     controlado entre el bloque de texto y el producto (dependía de un
     flex:1 + justify-content:center heredados de la regla base de
     escritorio, que en un contenedor de alto automático da un resultado
     impredecible) — de ahí el "algo le falta, no se ve armónico" reportado
     por Leonardo. Ahora: padding horizontal real, gap explícito y
     controlado entre texto/producto, flex:none en el producto para que no
     dependa de flex-grow. Solo mobile — desktop no se toca. */
  .process-act {
    min-height: auto;
    display: flex;
    flex-direction: column;
    justify-content: flex-start;
    gap: clamp(28px, 6vh, 44px);
    padding: calc(var(--nav-h) + 32px) 24px 56px;
  }
  .process-act__media {
    order: 2;
    flex: none;
    width: min(420px, 78vw);
    max-width: 100%;
    margin: 0 auto;
    min-height: 260px;
    transform: none;
  }
  .process-act__copy,
  .process-act__copy--left {
    order: 1;
    position: relative;
    top: auto; right: auto; left: auto;
    max-width: none;
    text-align: left;
    margin-bottom: 0;
  }
  .process__title { font-size: clamp(2.3rem, 10vw, 3.3rem); }
  .process__sub { max-width: 34ch; margin: 0; }

  /* Conectamos en mobile — interfaz propia, no el mismo overlay de escritorio
     achicado. 2026-08-16 (v12): en desktop el título flota en la esquina
     superior derecha de la foto porque ahí cae sobre cielo blanco (la foto
     es muy ancha, por lo tanto muy alta en proporción). En mobile la foto es
     angosta y por lo tanto mucho más baja en proporción — ese mismo overlay
     terminaba cayendo directo sobre el camión, tapándolo (reportado por
     Leonardo con screenshot real). En vez de perseguir un punto del cielo
     que se mueve según el ancho de pantalla, el texto en mobile deja de ser
     un overlay: es un bloque normal en flujo, ANTES de la foto (order:1),
     con su propio fondo y padding — la foto se ve siempre completa, sin
     nada encima. */
  .conectamos { display: flex; flex-direction: column; }
  .conectamos__media { order: 2; }
  .conectamos__copy {
    order: 1;
    position: relative;
    top: auto; right: auto; left: auto;
    max-width: none;
    text-align: left;
    padding: clamp(36px, 9vw, 56px) 20px clamp(24px, 6vw, 36px);
  }
  .conectamos__copy .process__title { font-size: clamp(30px, 9vw, 42px); }
  .conectamos__copy .process__sub { font-size: 14px; max-width: 40ch; }

  /* Manifesto */
  .manifesto__grid { grid-template-columns: 1fr; gap: 32px; }
  .manifesto__img { aspect-ratio: 16/10; }

  /* Líneas de producto — interfaz mobile propia, no una adaptación del
     carrusel de escritorio.
     2026-08-16 (v2): el diseño de esa fecha mantenía el pin de escritorio
     (.lines__sticky a 100vh, cada .line-card en position:absolute con
     overflow-y:auto interno) — pero el pin usa el gesto de scroll del
     usuario para AVANZAR a la siguiente línea, nunca le daba oportunidad de
     hacer scroll DENTRO de la card para leer las tags de más abajo antes de
     que cambiara ("se corta", reportado por Leonardo). Se quitó el pin y se
     aplanó todo a tarjetas compactas en flujo normal.
     2026-08-17 (v3): las tarjetas compactas resolvían el corte de texto pero
     Leonardo reportó dos problemas nuevos: (1) se sentían "como muchas
     slides corridas" en vez de una experiencia con intención — pidió algo
     más parecido a escritorio, "que se bloquea en una página y pasan
     movimientos mientras hacemos scroll"; (2) con tarjetas compactas de
     alto fijo/chico, el producto (hogar, bebé, mascotas) quedaba apretado
     contra los bordes o el fondo no alcanzaba a cubrir todo — "sobra
     espacio para que no se corten". Solución: cada línea vuelve a ser una
     sección de pantalla completa (min-height:100vh, MISMO patrón que
     Creamos/Desarrollamos/Conectamos más arriba en este archivo) pero SIN
     pin ni ScrollTrigger — es scroll normal, así que si una línea necesita
     más de un alto de pantalla para su texto, simplemente crece (min-height,
     no height) en vez de cortar nada. El efecto "una línea, una pantalla"
     que pidió Leonardo sale gratis de darle a cada acto su propio alto real,
     sin reintroducir el bug del pin. El leve fade-in + y al entrar en
     viewport (ver createLinesCarousel en main.js, rama isMobile) ya da el
     "pasan movimientos mientras hacemos scroll" sin más JS. */
  .lines__sticky {
    position: static;
    height: auto;
    overflow: visible;
    display: flex;
    flex-direction: column;
    gap: 0;
    padding: 0;
  }
  .line-card {
    position: relative;
    inset: auto;
    overflow: hidden;
    opacity: 1;
    pointer-events: auto;
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: clamp(20px, 4vh, 36px);
    min-height: 100vh;
    padding: calc(var(--nav-h) + 32px) 24px 48px;
    border-radius: 0;
    /* Respaldo sólido detrás de la foto: mismo tono que el stop más oscuro
       del scrim (#0a0e26), se ve mientras la foto carga (fade-in vía
       .is-loaded) y si algún día una card se queda sin imagen. */
    background: #0a0e26;
  }
  /* Orden narrativo: título primero (contexto inmediato), el producto como
     momento grande al centro, texto + tags al final — antes el producto
     vivía arriba y se traslapaba con el panel vía margin negativo; con
     pantalla completa ya no hace falta ese ajuste. */
  .line-card__panel { order: 1; }
  .line-card__cutout { order: 2; }
  .line-card__info { order: 3; }
  .line-card__backdrop { border-radius: inherit; }
  /* inset:0 estira la foto para cubrir SIEMPRE el alto real de la card
     completa (título + producto + texto + tags) — igual que en desktop, una
     sola foto de punta a punta, sin costura. object-fit:cover recorta lo
     necesario si la card queda más alta que una pantalla. */
  .line-card__bg, .line-card__scrim { position: absolute; inset: 0; }
  /* Leonardo pidió el mismo parallax vertical del Manifesto ("cada familia
     es distinta") para el fondo blureado de Líneas en mobile — se ve seco
     sin nada de movimiento al entrar. El parallax mueve la foto ±10% de su
     alto (ver gsap.matchMedia isMobile en main.js); el scale(1.05) base
     solo cubre el ligero desenfoque de bordes del blur, no alcanza para
     ese recorrido — se sube a 1.28 aquí (mismo criterio que
     .manifesto__img img: height 122%/top -11%) para que nunca se asome un
     borde vacío mientras la foto sube/baja. */
  /* will-change: aquí es la sospecha #1 de por qué el parallax "no aparecía"
     en el teléfono de Leonardo aunque el mismo mecanismo sí se ve en el
     Manifesto: esta imagen tiene filter:blur() ADEMÁS del transform — Safari
     iOS es conocido por no recomponer bien un elemento con filter+transform
     animados juntos sin este hint, y puede quedarse "congelado" visualmente
     aunque el valor sí esté cambiando (el Manifesto no tiene filter, por
     eso ahí nunca fue problema). */
  .line-card__bg img { transform: scale(1.28); will-change: transform; }
  .line-card__panel { position: relative; left: auto; top: auto; width: 100%; height: auto; margin-bottom: 0; }
  .line-card__cutout {
    position: relative; top: auto; left: auto; right: auto; bottom: auto;
    width: auto; padding: 0; margin: 0 auto; justify-content: center;
    height: calc(min(300px, 32vh) * var(--cutout-scale, 1));
  }
  .line-card__cutout.is-placeholder { top: auto; width: min(220px, 60vw); height: min(220px, 60vw); }
  /* El drop-shadow (28px de blur) se ve bien en desktop pero en paquetes
     anchos/rectangulares como el de higiene se leía como una mancha gris
     cuadrada flotando sobre el fondo blureado en mobile ("mal efecto",
     reportado por Leonardo) — se quita solo aquí, desktop no cambia. */
  .line-card__cutout img { filter: none; }
  .line-card__info { position: relative; right: auto; top: auto; transform: none; max-width: 100%; margin-top: 0; }
  .line-card__info p, .line-card__tags { max-width: 100%; }
  /* --cutout-shift-x (140px) es un ajuste SOLO de escritorio — Leonardo pidió
     ese ángulo/desplazamiento únicamente para cómo se ve el paquete de
     higiene en su recuadro ancho de escritorio; en mobile el cuadro ya
     centra la imagen por su cuenta (justify-content:center), así que ese
     shift la sacaba de centro sin ninguna razón. Se resetea a 0 solo aquí. */
  .line-card--higiene { --cutout-shift-x: 0px; }

  /* Modelo (scroll-story fullscreen) */
  /* El grid editorial de 2 columnas (número + línea divisoria vertical) no
     cabe en mobile — vuelve a apilarse en una columna, número arriba. */
  .model-card { grid-template-columns: 1fr; align-content: center; justify-items: start; padding-top: calc(var(--nav-h) + 36px); }
  .model-card__num { font-size: clamp(56px, 22vw, 100px); padding-right: 0; margin-bottom: 0.1em; }
  .model-card__num::after { display: none; }
  .model-card__logo { padding: 9px 18px; margin-bottom: 16px; }
  .model-card__logo img { height: 22px; }

  /* Marcas */
  .brand-grid { grid-template-columns: repeat(3, 1fr); }
  .brands__group-head { flex-direction: column; align-items: flex-start; gap: 6px; }
  .retail-grid { grid-template-columns: repeat(3, 1fr); }

  /* Modelo / footer grids */
  .footer__top { grid-template-columns: 1fr; }

  /* Grid decorativas: menos columnas para que no se vean apretadas */
  .grid-lines { grid-template-columns: repeat(4, 1fr); }
}

@media (max-width: 480px) {
  .brand-grid { grid-template-columns: repeat(2, 1fr); }
  .retail-grid { grid-template-columns: repeat(2, 1fr); }
}
