/* Contenedor de <filter> SVG reutilizables (ej. #thesis-fluid) — sin
   contenido visual propio, solo definiciones referenciadas por otros
   elementos vía filter:url(#id). Clase en vez de style="" inline porque el
   CSP del sitio (style-src 'self', sin unsafe-inline) descarta cualquier
   atributo style — sin esto, el <svg> renderiza a su tamaño por defecto
   (~300x150) en flujo normal y empuja el contenido de abajo. */
.svg-filters { position: absolute; width: 0; height: 0; overflow: hidden; }

/* Video de fondo reutilizable (Home hero, hero del artículo de Nutrición):
   llena por completo a su contenedor padre. El padre SIEMPRE necesita
   tamaño propio (aspect-ratio, inset absoluto con min-height, etc.) — este
   video queda en position:absolute, así que no puede aportarle altura al
   padre él solo (si el padre no tiene otro hijo "en flujo", colapsa a 0px). */
.hero-bg-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* ===== Header — rediseño v4 "Sin Ruido": barra oscura translúcida con blur,
   flota sobre el contenido en vez de ser una franja sólida opaca. Tamaño y
   color fijos siempre (sin encogerse al hacer scroll — pedido explícito del
   usuario 2026-08-07: prefiere una barra estable). ===== */
.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: var(--color-header-bg);
  backdrop-filter: blur(14px) saturate(1.4);
  -webkit-backdrop-filter: blur(14px) saturate(1.4);
  border-bottom: 1px solid var(--color-border);
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 20px 24px;
  gap: 16px;
  flex-wrap: wrap;
}

.header-brand { display: flex; align-items: center; gap: 12px; min-width: 0; }
.logo-link { display: flex; align-items: center; flex-shrink: 0; }

/* Tagline junto al logo — efecto "cartel de puntuación" (split-flap, tipo
   marcador/aeropuerto): 18 columnas de ancho fijo (una por carácter, ambas
   frases rellenadas con espacios a esa longitud), cada columna con dos
   caracteres apilados (arriba = frase A, abajo = frase B) dentro de una
   ventana de altura 1em con overflow:hidden. Un `translateY` con `steps(1)`
   (sin argumento de posición — la variante con `end`/`jump-end` ya dio
   problemas de soporte) alterna instantáneamente entre mostrar la fila de
   arriba o la de abajo. El escalonado de `animation-delay` por columna
   (para que el cambio se vea como una ola cruzando el cartel, no todas las
   letras a la vez) va por `:nth-child()` en la hoja de estilos, NO por
   `style="--i:N"` inline — el CSP del sitio es `style-src 'self'` sin
   `unsafe-inline`, así que cualquier atributo `style=""` se descarta (mismo
   problema ya documentado en Seguridad.md con `--sku-accent` en las fichas
   de producto). Sin `ch` dinámico, sin position:absolute, sin timing
   function por-keyframe — nada de lo que falló en los intentos
   anteriores. Sin JS. */
.header-tagline {
  display: inline-flex;
  align-items: center;
  height: 1em;
  overflow: hidden;
  font: 600 11px/1 var(--font-data);
  color: var(--color-green-bright);
  text-transform: uppercase;
}
.tw-col {
  display: inline-block;
  width: 1ch;
  height: 1em;
  overflow: hidden;
  margin-right: .04em;
}
.tw-col-inner {
  display: flex;
  flex-direction: column;
  animation: tw-flip 6s steps(1) infinite;
  /* base.css tiene `*{animation-duration:.01ms !important}` bajo
     prefers-reduced-motion: reduce (accesibilidad global del sitio, ver esa
     regla) — sin este !important puntual, esa regla universal congela el
     cartel para cualquiera con esa preferencia activada en el SO/navegador,
     que es justo lo que se reportó. Excepción deliberada y acotada a este
     único elemento decorativo, a pedido explícito del usuario. */
  animation-duration: 6s !important;
  animation-iteration-count: infinite !important;
}
.tw-col-inner span {
  display: block;
  height: 1em;
  line-height: 1;
  text-align: center;
}
@keyframes tw-flip {
  0%   { transform: translateY(0); }
  50%  { transform: translateY(-1em); }
  100% { transform: translateY(0); }
}
.tw-col:nth-child(1) .tw-col-inner { animation-delay: 0s; }
.tw-col:nth-child(2) .tw-col-inner { animation-delay: .05s; }
.tw-col:nth-child(3) .tw-col-inner { animation-delay: .1s; }
.tw-col:nth-child(4) .tw-col-inner { animation-delay: .15s; }
.tw-col:nth-child(5) .tw-col-inner { animation-delay: .2s; }
.tw-col:nth-child(6) .tw-col-inner { animation-delay: .25s; }
.tw-col:nth-child(7) .tw-col-inner { animation-delay: .3s; }
.tw-col:nth-child(8) .tw-col-inner { animation-delay: .35s; }
.tw-col:nth-child(9) .tw-col-inner { animation-delay: .4s; }
.tw-col:nth-child(10) .tw-col-inner { animation-delay: .45s; }
.tw-col:nth-child(11) .tw-col-inner { animation-delay: .5s; }
.tw-col:nth-child(12) .tw-col-inner { animation-delay: .55s; }
.tw-col:nth-child(13) .tw-col-inner { animation-delay: .6s; }
.tw-col:nth-child(14) .tw-col-inner { animation-delay: .65s; }
.tw-col:nth-child(15) .tw-col-inner { animation-delay: .7s; }
.tw-col:nth-child(16) .tw-col-inner { animation-delay: .75s; }
.tw-col:nth-child(17) .tw-col-inner { animation-delay: .8s; }
.tw-col:nth-child(18) .tw-col-inner { animation-delay: .85s; }
@media (max-width: 480px) {
  .header-tagline { display: none; }
}
.logo-img {
  height: 30px; width: auto; display: block; aspect-ratio: 2949 / 671;
  /* logo-clear.png tiene canal alfa real (a diferencia de nutrivive-logo.png,
     ver footer) — el filter invierte solo el trazo, no un rectángulo entero. */
  filter: brightness(0) invert(1);
}

/* ===== Nav (desktop) ===== */
.site-nav {
  position: relative;
  display: flex;
  align-items: center;
  gap: 4px;
  flex-wrap: wrap;
}

.nav-btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 10px 16px;
  border: none;
  border-radius: var(--radius-btn);
  background: transparent;
  font: 600 14px var(--font-body);
  color: var(--color-header-text);
  cursor: pointer;
  text-align: left;
  transition: color .2s;
}
.nav-btn:hover { color: var(--color-header-text-hover); }
.nav-btn.active {
  color: var(--color-green-bright);
}

/* Subrayado "serpiente": una sola línea ondulada que se desliza por debajo
   del nav y se detiene bajo la palabra en hover (o bajo la página activa en
   reposo) — adaptado de "Wobbly underline" (CodePen ainalem/YmWqaO). El
   original anima con click y 3 clases de posición fijas (pos0/pos1/pos2)
   calculadas a mano para un menú de 3 columnas de ancho IGUAL; acá el nav
   tiene 5 items de ancho variable (Inicio/Nosotros/Productos ▾/Nutrición y
   Ciencia ▾/Contacto), así que la posición X se calcula en JS con
   getBoundingClientRect en cada hover/focus (ver setupNavSnake en nav.js)
   en vez de clases fijas por índice — mismo truco visual (stroke-dasharray
   deja visible un solo tramo ondulado del path, el resto es hueco), pero
   generalizado a cualquier layout en vez de solo 3 posiciones fijas.
   El <path> (mismo `d` que el original) sigue siendo lo único no tocado:
   define la forma del "bache" ondulado; cambian color, tamaño y cómo se
   posiciona, no la curva en sí. */
.nav-snake-track {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 20px;
  overflow: hidden;
  pointer-events: none;
}
.nav-snake {
  position: absolute;
  left: 0;
  /* centro vertical de la pista (20px, overflow:hidden): el <path> vive en
     y=50 del viewBox y `transform: translateY(-50%)` lo lleva justo a este
     `top`, así que el centro del trazo cae aquí. Con 86% el tramo ondulado
     se salía por abajo y quedaba recortado. */
  top: 50%;
  opacity: 0;
  transition: transform .5s cubic-bezier(.16,.84,.44,1), width .5s cubic-bezier(.16,.84,.44,1), opacity .25s ease;
}
.nav-snake.is-visible { opacity: 1; }
.nav-snake path {
  fill: none;
  stroke: var(--color-green-bright);
  /* El ancho real del <svg> (por ende su factor de escala interno respecto
     al viewBox 0-100) cambia por JS en cada hover para que el tramo visible
     del dasharray quede proporcional a la palabra (ver moveSnakeTo en
     nav.js) — sin vector-effect="non-scaling-stroke" en el <path>, ese
     escalado también engordaría el trazo (llegó a verse "grueso y sólido"
     en items anchos). Con ese atributo el stroke-width de acá queda fijo
     en px de pantalla sin importar cuánto se escale el SVG. */
  stroke-width: 2px;
  /* El <path> es recta(0-30) + onda/"resorte"(30-60) + recta(60-100) sobre una
     longitud total ≈100. Con este dash sólo se ve el tramo 15-75: recta corta +
     el resorte entero + recta corta, centrado bajo la palabra. Antes (`29 100`,
     sin offset) el tramo visible era 0-29 — sólo la recta de entrada, el
     resorte quedaba escondido en el hueco del dash y el efecto no se veía. */
  stroke-dasharray: 60 200;
  stroke-dashoffset: -15;
}
@media (max-width: 759px) {
  .nav-snake-track { display: none; }
}

/* Envuelve nav + selector de idioma para que ambos queden agrupados a la
   derecha del header (space-between del .site-header solo ve brand/hamburger/
   este wrapper como hijos directos), en vez de que el selector quede a una
   distancia dispareja del nav. */
.header-right { display: flex; align-items: center; gap: 20px; flex-wrap: wrap; }

/* ===== Selector de idioma (ES/EN) — esquina superior derecha del header ===== */
.lang-switch { display: flex; align-items: center; gap: 6px; flex-shrink: 0; }
.lang-btn {
  font: 700 13px var(--font-body);
  color: var(--color-header-text);
  opacity: .75; /* .55 no llegaba a 4.5:1 sobre el header oscuro */
  padding: 4px 2px;
  transition: opacity .2s, color .2s;
}
.lang-btn:hover { opacity: 1; color: var(--color-header-text-hover); }
.lang-btn.is-active { opacity: 1; color: var(--color-green-bright); cursor: default; }
.lang-sep { color: var(--color-header-text); opacity: .3; font: 13px var(--font-body); }

.nav-dropdown { position: relative; }

.dropdown-panel {
  display: none;
  position: absolute;
  top: 100%;
  left: 0;
  background: var(--color-bg-elevated);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-panel);
  box-shadow: var(--shadow-dropdown);
  padding: 6px;
  min-width: 200px;
  flex-direction: column;
  gap: 2px;
  z-index: 5;
}
.dropdown-panel.open { display: flex; }

.dropdown-item {
  display: flex;
  align-items: center;
  min-height: 44px;
  text-align: left;
  padding: 10px 14px;
  border-radius: var(--radius-menu);
  font: 600 14px var(--font-body);
  color: var(--color-text-muted);
}
.dropdown-item:hover {
  background: var(--color-dropdown-hover-bg);
  color: var(--color-dropdown-hover-text);
}

/* ===== Hamburguesa (solo mobile) ===== */
.hamburger {
  display: none;
  width: 44px;
  height: 44px;
  border: 1px solid var(--color-border);
  background: var(--color-bg-elevated);
  color: var(--color-text);
  border-radius: var(--radius-btn);
  font-size: 18px;
}

/* ===== Mobile (<760px) ===== */
@media (max-width: 759px) {
  .hamburger { display: block; }

  .site-nav {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    background: var(--color-bg-elevated);
    flex-direction: column;
    align-items: stretch;
    box-shadow: var(--shadow-mobile-nav);
    padding: 8px;
  }
  .site-nav.open { display: flex; }

  /* El panel desplegado usa fondo claro (--color-bg-elevated), no la banda
     oscura del header — --color-header-text/--color-green-bright están
     pensados para texto sobre esa banda oscura (ver su comentario en
     tokens.css: "no usar en texto sobre el lienzo") y aquí se fundían con
     el fondo. Mismos tokens que .dropdown-item, que ya vive sobre esta
     misma superficie clara. */
  .nav-btn { color: var(--color-text-muted); }
  .nav-btn:hover { color: var(--color-text); }
  .nav-btn.active { color: var(--color-green); }

  .nav-dropdown { width: 100%; }
  .nav-btn--dropdown { width: 100%; text-align: left; }
  .dropdown-panel { position: static; box-shadow: none; border: none; width: 100%; }
}

/* Preview temporal del Módulo 1 — se reemplaza en Módulo 2 */
.module-check { padding: 64px 40px; max-width: 1120px; margin: 0 auto; }

/* ===== Footer ===== */
.site-footer {
  background: var(--color-dark-bg);
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 16px;
  padding: 32px 40px;
}
/* logo-clear.png tiene canal alfa real — sobre el footer claro (v4.1) se usa
   sin invertir, mostrando la tinta oscura original del logo (antes se
   invertía a blanco para el footer oscuro de v4; ver DESIGN.md). */
.footer-logo { height: 24px; width: auto; opacity: .85; aspect-ratio: 2949 / 671; }
/* logo-clear.png muestra su tinta oscura original sobre el footer claro
   (v4.1); en modo oscuro el footer pasa a fondo oscuro también, así que acá
   sí hace falta invertirlo a blanco (mismo filtro que ya usa el header). */
@media (prefers-color-scheme: dark) {
  .footer-logo { filter: brightness(0) invert(1); }
}
.footer-legal { display: flex; gap: 20px; flex-wrap: wrap; }
.footer-legal a { font: 13px var(--font-body); color: var(--color-text-muted); text-decoration: none; }
.footer-legal a:hover { color: var(--color-green); }
.footer-copy { font: 13px var(--font-body); color: var(--color-text-muted); margin: 0; }

/* ===== Banda de 3 valores (stats) — columnas separadas por hairline, sin
   caja/sombra/hover-lift: misma familia de card ya la usa el Showcase de
   arriba, esto queda deliberadamente distinto (grupo, no grid de cards). ===== */
.stats-band {
  background: var(--color-bg);
  padding: clamp(56px, 8vw, 88px) 40px;
}
.stats-grid {
  max-width: 1120px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 32px;
}
@media (min-width: 760px) {
  .stats-grid { gap: 0; }
  .stat-card { padding: 0 40px; }
  .stat-card:first-child { padding-left: 0; }
  .stat-card:last-child { padding-right: 0; }
  .stat-card + .stat-card { border-left: 1px solid var(--color-border); }
}
.stat-card {
  position: relative;
}
.stat-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: var(--radius-panel);
  background: var(--color-badge-bg-green);
  color: var(--color-green-dark);
  margin-bottom: 18px;
}
.stat-icon svg { width: 20px; height: 20px; }
.stat-num {
  display: block;
  font: 800 15px var(--font-data);
  letter-spacing: .04em;
  color: var(--color-green);
  margin-bottom: 10px;
}
.stat-card h3 { font: 800 17px var(--font-display); letter-spacing: -.01em; color: var(--color-text); margin: 0 0 8px; }
.stat-card p { font: 14px/1.6 var(--font-body); color: var(--color-text-muted); margin: 0; }

/* ===== Breadcrumb — wayfinding + señal jerárquica para Google (acompaña
   al schema BreadcrumbList inline de cada página). Barra fina bajo el
   header, primer elemento dentro de <main> en toda página interna
   (no en Home ni en 404). */
.breadcrumb { max-width: 1200px; margin: 0 auto; padding: 16px 24px 0; }
.breadcrumb ol { display: flex; flex-wrap: wrap; list-style: none; margin: 0; padding: 0; font: 600 13px var(--font-body); }
.breadcrumb li { color: var(--color-text-muted); }
.breadcrumb li + li::before { content: "/"; margin: 0 8px; color: var(--color-border); }
.breadcrumb a { color: inherit; text-decoration: none; transition: color .2s; }
.breadcrumb a:hover { color: var(--color-green); }
.breadcrumb li[aria-current="page"] { color: var(--color-text); font-weight: 700; }

/* Variante superpuesta al hero (páginas con hero full-bleed justo debajo
   del header): sin franja propia — el breadcrumb queda flotando sobre el
   fondo del hero (video, scrim o color) en vez de abrir una banda de color
   de página entre el header y el hero. Requiere que el contenedor del hero
   tenga position:relative. */
.breadcrumb--overlay { position: absolute; inset: clamp(24px, 4vw, 40px) auto auto clamp(24px, 4vw, 40px); z-index: 3; max-width: none; padding: 0; }
/* Sobre video/scrim oscuro (about-hero, nutricion-hero) — texto fijo a
   blanco, igual que el resto del contenido del hero, no var(--color-text)
   que se invierte con el tema claro/oscuro. */
.breadcrumb--light li { color: rgba(255, 255, 255, .78); }
.breadcrumb--light li + li::before { color: rgba(255, 255, 255, .35); }
.breadcrumb--light li[aria-current="page"] { color: #fff; }
.breadcrumb--light a:hover { color: var(--color-green-bright); }

/* ===== Botón "volver arriba" — aparece tras cierto scroll (ver
   js/back-to-top.js), fijo en la esquina inferior derecha en toda página. */
.back-to-top {
  position: fixed;
  right: 20px;
  bottom: 20px;
  z-index: 40;
  width: 44px;
  height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--color-border);
  border-radius: 50%;
  background: var(--color-bg-elevated);
  color: var(--color-text-muted);
  box-shadow: var(--shadow-card-hover);
  cursor: pointer;
  opacity: 0;
  visibility: hidden;
  transform: translateY(8px);
  transition: opacity .25s ease, transform .25s ease, visibility .25s, color .2s, border-color .2s;
}
.back-to-top.is-visible { opacity: 1; visibility: visible; transform: translateY(0); }
.back-to-top:hover { color: var(--color-green); border-color: oklch(70% 0.15 142 / .4); }
.back-to-top svg { width: 20px; height: 20px; }
@media (max-width: 759px) {
  .back-to-top { right: 14px; bottom: 14px; }
}
@media (prefers-reduced-motion: reduce) {
  .back-to-top { transition: opacity .15s ease, visibility .15s; transform: none; }
}

/* ===== Skeleton/shimmer para <img loading="lazy"> mientras carga — evita
   la sensación de "pantalla rota" en la primera carga con conexión lenta,
   sobre todo en imágenes fuera del viewport inicial. Se retira sola (clase
   .is-loaded, ver js/img-skeleton.js) en cuanto el navegador dispara load
   (o error, para no dejarla colgada si la imagen falla). */
img[loading="lazy"]:not(.is-loaded) {
  background-color: var(--color-bg-elevated-2);
  background-image: linear-gradient(100deg, transparent 30%, oklch(100% 0 0 / .35) 50%, transparent 70%);
  background-size: 200% 100%;
  animation: img-skeleton-shimmer 1.3s ease-in-out infinite;
}
img.is-loaded { animation: none; background: none; }
@keyframes img-skeleton-shimmer {
  0% { background-position: 150% 0; }
  100% { background-position: -50% 0; }
}
@media (prefers-reduced-motion: reduce) {
  img[loading="lazy"]:not(.is-loaded) { animation: none; }
}
