/* ============================================================================
   LAYOUT — MERIDIANO (demo). Grid editorial asimétrico, anatomía de secciones,
   cursor, canvas de firma. Cargar al final.
   ============================================================================ */

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

/* Rejilla base de 12 columnas; las secciones deciden dónde romperla. */
.grid12 {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  column-gap: var(--gutter);
}

/* Regla de contorno: no un hairline de periódico, sino una línea que insinúa nivel. */
.rule {
  height: var(--rule);
  border: 0;
  background: linear-gradient(90deg, var(--stroke-2) 0%, var(--stroke) 60%, transparent 100%);
  transform-origin: left center;
}

/* =========================== NAV =========================== */
.nav {
  position: fixed; inset-block-start: 0; inset-inline: 0; z-index: var(--z-nav);
  display: flex; align-items: center; justify-content: space-between;
  padding: clamp(1rem, 2vw, 1.6rem) var(--gutter);
}
/* Scrim del header, SIEMPRE presente (el nav es fixed): sin él, el contenido que sube por
   scroll choca con los links. Cambia de color, no de presencia: claro por default (secciones
   #FAFBFC), oscuro en modo dark (hero/contacto/footer). El crossfade lo hace la transición
   de background, en --d-chrome y SINCRONIZADO con los textos del chrome (change
   chrome-legible-en-transito: a 0.5s el cruce sostenía frames ~1:1 medidos).
   Meseta del gradiente dimensionada por COTAS, no por gusto: cada estado coherente del
   chrome cumple 4.5:1 contra el velo compuesto sobre cualquier contenido (blanco o negro
   puros detrás). La cota que manda en claro es contenido NEGRO bajo el CTA teal (alpha
   0.94 da 4.76:1; 0.93 daba 4.47 y reprobaba); en oscuro, contenido BLANCO bajo el mint
   (0.78 da 5.8:1, mínimo aritmético 0.65). El desvanecimiento empieza en 72% del ::before
   (~97% de la altura del nav): la franja del texto queda completa en la meseta. Antes la
   zona de 0.62 al 55% dejaba pasar el cuerpo de la página y el pill caía a 1.9-3.2:1. */
.nav::before {
  content: ''; position: absolute; inset: 0 0 auto 0; height: 135%; z-index: -1;
  pointer-events: none;
  background: linear-gradient(180deg, rgba(250, 251, 252, 0.95) 0%, rgba(250, 251, 252, 0.94) 72%, transparent 100%);
  transition: background var(--d-chrome) var(--ease-swift);
}
html.on-dark:not(.cycle-open) .nav::before {
  background: linear-gradient(180deg, rgba(4, 33, 29, 0.82) 0%, rgba(4, 33, 29, 0.78) 72%, transparent 100%);
}
.nav__brand {
  font-family: var(--font-display); font-weight: 700; font-size: 1.05rem;  /* tope Space Grotesk */
  letter-spacing: -0.02em; color: var(--ink);
}
.nav__meta { display: flex; gap: 1.6rem; align-items: center; }
.nav__meta .t-label { color: var(--ink); }

/* Marca real YCM360: icono oficial (arco + punto, gap a las 7:30) + wordmark. En reposo del
   hero va completo; al pasar el hero el wordmark se oculta y queda el icono (marca compacta). */
/* Área táctil 44px en AMBOS ejes y AMBOS estados (change targets-tactiles). min-height sola dejaba
   la marca en 22x44 al entrar en .past-hero (se oculta el wordmark y queda solo el ícono de 22px):
   mismo error que el switch de idioma del overlay. min-width + centrado lo cierra sin agrandar el
   ícono visible (el espacio extra es transparente). En el hero el contenido ya supera 44, sin
   efecto. 44px = estándar propio de la pieza (SC 2.5.5 AAA); el mínimo AA de 2.5.8 es 24. */
.nav__brand { position: relative; display: inline-flex; align-items: center; justify-content: center; gap: 0.55rem; min-height: 44px; min-width: 44px; }
.brand-icon { width: 22px; height: 22px; flex: none; }
.brand-icon__arc { stroke: var(--accent); }
.brand-icon__dot { fill: var(--accent); }
html.on-dark:not(.cycle-open) .brand-icon__arc { stroke: var(--ink-inverse); }
html.on-dark:not(.cycle-open) .brand-icon__dot { fill: var(--ink-inverse); }
.nav__brand-360 { color: var(--ink-2); }
html.on-dark:not(.cycle-open) .nav__brand-360 { color: var(--ink-inverse-2); }
/* El logo del nav queda ESTÁTICO (change toggle-movimiento-visible): el respiro se movió al
   toggle de movimiento (mini-Ensō), que es ahora el único Ensō vivo del nav — dos Ensōs latiendo
   competían. El logo conserva su estado canónico (dasharray 223.6/40.3, gap a 7:30). El keyframe
   cycle-breathe se conserva: ahora lo consume .motion-toggle__arc. */
@keyframes cycle-breathe {
  0%, 100% { stroke-dasharray: 223.6 40.3; stroke-dashoffset: 0; }
  50%      { stroke-dasharray: 205 58.9;   stroke-dashoffset: -9.3; }
}
html.past-hero .nav__brand-full { display: none; }

/* Nav de secciones con scroll-spy (robar #6). v3: subrayado DESLIZANTE compartido entre hover
   y estado activo (patrón topology: un mecanismo, dos significados) + atenuación de inactivos
   (señalar posición bajando el ruido, no subiendo el volumen). Easing firma propio. */
.nav__sections { display: flex; gap: 1.4rem; list-style: none; margin: 0; padding: 0; }
.nav__sections a {
  position: relative; overflow: hidden; display: inline-block;
  font-family: var(--font-mono); font-size: var(--fs-label); letter-spacing: var(--tracking-label);
  text-transform: uppercase; color: var(--ink-2); text-decoration: none;
  /* Área táctil 44px (change targets-tactiles): texto 18px + 13px arriba y abajo = 44 de alto,
     centrado. El overflow:hidden clipa un ::after de área, así que se crece la caja por padding.
     El subrayado se reancla abajo para seguir a 4px del texto (ver ::before). 44 = estándar propio
     (SC 2.5.5 AAA); el mínimo AA de 2.5.8 es 24, que estos ya cumplían por espaciado.
     min-width + centrado: en EN labels cortos ("Work", "Edge") medían 40px de ANCHO (la centinela
     bilingüe lo cazó); el eje menor también debe llegar a 44. Texto centrado en la caja. */
  padding-block: 13px; min-width: 44px; text-align: center;
  transition: color var(--d-chrome) var(--ease-swift);   /* sincronizado con el velo (chrome-legible-en-transito) */
}
.nav__sections a::before {
  content: ''; position: absolute; inset-inline: 0; bottom: 9px; height: 1px;
  background: var(--accent);
  transform: translateX(-101%);
  transition: transform var(--d-base) var(--ease-swift);
}
.nav__sections a:hover::before,
.nav__sections a:focus-visible::before,
.nav__sections a.is-active::before { transform: translateX(0); }
.nav__sections.has-active a:not(.is-active) { color: var(--ink-3); }
.nav__sections a.is-active,
.nav__sections a:hover { color: var(--ink); }

/* Separador del cluster de utilidades (idioma + movimiento) vs los destinos de página:
   mismo lenguaje de reglas hairline del sitio, cero JS. */
.nav__sep { width: 1px; height: 16px; background: var(--stroke-2); flex: none; }
html.on-dark:not(.cycle-open) .nav__sep { background: rgba(255, 255, 255, 0.28); }

/* CTA de agendar en el nav (panel de contenido: el embudo tenía una sola salida real).
   Mismo mecanismo de subrayado deslizante que los links de sección — un mecanismo, dos
   significados — con el acento marcando que es acción, no destino de página. */
.nav__book {
  position: relative; overflow: hidden; display: inline-block;
  font-family: var(--font-mono); font-size: var(--fs-label); letter-spacing: var(--tracking-label);
  text-transform: uppercase; color: var(--accent);
  /* Área táctil 44px, mismo patrón que .nav__sections a (change targets-tactiles): alto por
     padding, ancho por min-width para no caer bajo 44 con labels cortos en otro idioma. */
  padding-block: 13px; min-width: 44px; text-align: center;
  transition: color var(--d-chrome) var(--ease-swift);   /* sincronizado con el velo (chrome-legible-en-transito) */
}
.nav__book::before {
  content: ''; position: absolute; inset-inline: 0; bottom: 9px; height: 1px;
  background: var(--accent);
  transform: translateX(-101%);
  transition: transform var(--d-base) var(--ease-swift);
}
.nav__book:hover::before, .nav__book:focus-visible::before { transform: translateX(0); }
html.on-dark:not(.cycle-open) .nav__book { color: var(--mint); }
html.on-dark:not(.cycle-open) .nav__book::before { background: var(--mint); }

/* Switch de idioma: el activo es texto plano (aria-current), el otro es link y persiste
   la elección en localStorage antes de navegar (listener en main.js). */
/* Puerta a /servicios/ en la zona de UTILIDADES del nav, NO en la lista de anclas.
   Por que aqui y no alla: js/nav.js:10 lee exactamente `.nav__sections a[data-spy]`, asi que
   el indicador de seccion ni la ve. Y este enlace SALE del documento, igual que el switch de
   idioma que ya vive en esta zona sin que nadie lo confunda con un ancla: su posicion y su
   tipografia ya lo marcan como otra cosa.
   La primera version de este trabajo dejo la pagina con una sola puerta, en el pie, por
   confundir "no va en la lista de anclas" con "no va en el nav". Son dos decisiones distintas.
   Mismo registro tipografico que sus vecinos y area tactil de 44px (targets-tactiles), pero
   SIN el subrayado deslizante de .nav__sections a, que es la senal de "ancla de esta pagina". */
.nav__servicios {
  display: inline-flex; align-items: center; min-height: 44px;
  font-family: var(--font-mono); font-size: var(--fs-label);
  letter-spacing: var(--tracking-label); text-transform: uppercase;
  color: var(--ink-3); text-decoration: none;
  transition: color var(--d-chrome) var(--ease-swift);
}
.nav__servicios:hover, .nav__servicios:focus-visible { color: var(--ink); }
/* En el cajon movil el fondo es oscuro y sus utils se aclaran explicitamente (ver
   .mobile-menu .lang-switch y .mobile-menu__foot .t-label, abajo). Sin este override la puerta
   se quedaba con --ink-3, que es el gris apagado del tema CLARO, sobre #04211D: medido, 2.07:1,
   contra un piso AA de 4.5 y contra los 12.78:1 de sus vecinos ES, EN y MOVIMIENTO. Se veia
   deslavada al lado de ellos y lo reporto el dueno desde su telefono el 2026-08-21.
   Mismo par de colores que el switch de idioma, para que los cuatro utils se lean igual. */
.mobile-menu .nav__servicios { color: var(--ink-inverse-2); }
.mobile-menu .nav__servicios:hover,
.mobile-menu .nav__servicios:focus-visible { color: var(--ink-inverse); }
/* Y el MISMO problema sobre el hero oscuro en escritorio, que la primera correccion no cubrio:
   se arreglo el cajon movil y se dio por cerrado el asunto sin mirar el otro contexto oscuro.
   Lo cazo el dueno en una captura de escritorio, donde SERVICIOS se veia apagado al lado de
   CASOS, RESULTADO, PROCESO, VENTAJA, CONTACTO, ES, EN y AGENDAR.
   Todo elemento del nav tiene su regla html.on-dark (marca, separador, boton, switch de
   idioma); este era el unico sin ella. Mismo par de colores que .lang-switch a, que es su
   vecino visual directo. */
html.on-dark:not(.cycle-open) .nav__servicios { color: var(--ink-inverse-2); }
html.on-dark:not(.cycle-open) .nav__servicios:hover,
html.on-dark:not(.cycle-open) .nav__servicios:focus-visible { color: var(--ink-inverse); }

.lang-switch {
  display: inline-flex; align-items: center; gap: 1.1ch;
  font-family: var(--font-mono); font-size: var(--fs-label);
  letter-spacing: var(--tracking-label); text-transform: uppercase;
}
.lang-switch [aria-current] { color: var(--ink); }
.lang-switch a { color: var(--ink-3); transition: color var(--d-chrome) var(--ease-swift); }  /* sincronizado con el velo */
.lang-switch a:hover, .lang-switch a:focus-visible { color: var(--ink); }
html.on-dark:not(.cycle-open) .lang-switch [aria-current] { color: var(--ink-inverse); }
html.on-dark:not(.cycle-open) .lang-switch a { color: var(--ink-inverse-2); }
html.on-dark:not(.cycle-open) .lang-switch a:hover,
html.on-dark:not(.cycle-open) .lang-switch a:focus-visible { color: var(--ink-inverse); }
/* Área táctil 44x44 del switch de idioma de ESCRITORIO (change targets-tactiles): medía 18x18,
   por debajo hasta del piso AA de 24. Scoped a .nav__meta para no tocar el del overlay (que ya se
   resolvió con su propia regla). El activo (aria-current) se agranda igual para que ES/EN queden
   alineados; el ancho extra es transparente, la letra no cambia de tamaño. */
.nav__meta .lang-switch a,
.nav__meta .lang-switch [aria-current] {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 44px; min-width: 44px;
}
/* En el overlay del menú (dark siempre) */
.mobile-menu .lang-switch [aria-current] { color: var(--ink-inverse); }
.mobile-menu .lang-switch a { color: var(--ink-inverse-2); }
.mobile-menu .lang-switch a:hover, .mobile-menu .lang-switch a:focus-visible { color: var(--ink-inverse); }

/* Toggle de movimiento: hit area de 44px, que es el ESTÁNDAR PROPIO de esta pieza (equivale al
   SC 2.5.5 Target Size Enhanced, nivel AAA). Ojo: el mínimo AA es el SC 2.5.8, que pide 24x24 —
   este comentario lo citaba mal antes (panel 2026-07-20). En vez de la píldora con
   texto. El label vive como tooltip (.motion-toggle__tip): opacity 0 lo conserva en el árbol
   de accesibilidad como nombre del botón, y aparece en hover Y focus-visible (el title nativo
   no dispara con teclado ni touch — veredicto del panel a11y). Estados: relleno teal = on,
   anillo hueco = off (color + forma, 1.4.1); el dot --accent-bright certifica >=3:1 como UI
   no-texto sobre ambas superficies del nav (tokens.css). */
/* Cruce sincronizado del chrome (chrome-legible-en-transito): los elementos que cambiaban
   de color en corte seco entran al mismo crossfade corto del velo; si saltan antes que el
   velo, quedan del color nuevo sobre el tono viejo (blanco sobre velo claro). */
.nav__brand, .nav__brand-360, .nav__meta .t-label, .nav__sep {
  transition: color var(--d-chrome) var(--ease-swift), background-color var(--d-chrome) var(--ease-swift);
}
.brand-icon__arc { transition: stroke var(--d-chrome) var(--ease-swift); }
.brand-icon__dot { transition: fill var(--d-chrome) var(--ease-swift); }

.motion-toggle {
  position: relative;
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; padding: 0;
  /* Píldora con borde (lenguaje del .menu-toggle): el toggle se lee como CONTROL, no un punto
     suelto (change toggle-movimiento-visible). */
  border: var(--rule) solid rgba(33, 37, 41, 0.35); border-radius: 999px; background: transparent;
  transition: border-color var(--d-chrome) var(--ease-swift), color var(--d-chrome) var(--ease-swift);  /* sincronizado con el velo */
}
.motion-toggle:hover { border-color: currentColor; }
/* Mini-Ensō del toggle (el ÚNICO Ensō vivo del nav: el logo queda estático). currentColor sigue
   el estado tonal del header (el toggle recibe --ink-inverse on-dark). */
.motion-toggle__ens { display: block; transition: opacity var(--d-fast) var(--ease-swift); }
.motion-toggle__arc { stroke: currentColor; fill: none; }
.motion-toggle__pt { fill: currentColor; }
/* On: respira (reusa cycle-breathe, misma dasharray+dashoffset compensado: el gap fijo a 7:30,
   ADR-002). Off: quieto + atenuado, distinguible del activo. */
html.motion-on .motion-toggle__arc { animation: cycle-breathe 9s var(--ease-swift) infinite; }
html[data-motion="off"] .motion-toggle__ens { opacity: 0.55; }
/* El .motion-toggle__dot y sus variantes se conservan más abajo: los usa el toggle del menú
   móvil (.mobile-menu__motion), que NO cambia. */
.motion-toggle__dot {
  width: 10px; height: 10px; border-radius: 999px; background: var(--accent-bright);
  transition: transform var(--d-fast) var(--ease-swift), background var(--d-fast) var(--ease-swift);
}
html[data-motion="off"] .motion-toggle__dot { background: transparent; box-shadow: inset 0 0 0 1.5px var(--ink); }
html.on-dark:not(.cycle-open)[data-motion="off"] .motion-toggle__dot { box-shadow: inset 0 0 0 1.5px var(--ink-inverse); }
.motion-toggle__tip {
  position: absolute; top: calc(100% + 2px); right: 0; z-index: 1;
  font-family: var(--font-mono); font-size: var(--fs-label);
  letter-spacing: var(--tracking-label); text-transform: uppercase;
  color: var(--ink); background: var(--ground);
  border: var(--rule) solid var(--stroke-2);
  padding: 0.3rem 0.6rem; white-space: nowrap;
  opacity: 0; pointer-events: none;
  transition: opacity var(--d-fast) var(--ease-swift);
}
/* SC 1.4.13: visible => hoverable (pointer-events auto; el tip vive DENTRO del botón, así
   que pasar el puntero sobre él conserva el :hover) y descartable (clase tip-off vía Escape,
   main.js). Oculto conserva pointer-events none para no interceptar clics del contenido. */
.motion-toggle:hover .motion-toggle__tip,
.motion-toggle:focus-visible .motion-toggle__tip { opacity: 1; pointer-events: auto; }
.motion-toggle.tip-off .motion-toggle__tip { opacity: 0; pointer-events: none; }

/* =========================== MENÚ MÓVIL =========================== */
/* Hamburguesa: solo bajo 900px (arriba el nav de secciones cubre la navegación) */
.menu-toggle {
  display: none; align-items: center; justify-content: center;
  width: 48px; height: 48px; border-radius: 999px;
  border: var(--rule) solid rgba(33, 37, 41, 0.35); color: var(--ink);
  transition: border-color var(--d-chrome) var(--ease-swift), color var(--d-chrome) var(--ease-swift);  /* sincronizado con el velo */
}
.menu-toggle:hover { border-color: currentColor; }
.menu-toggle__bars { position: relative; display: block; width: 16px; height: 10px; }
.menu-toggle__bars::before, .menu-toggle__bars::after {
  content: ''; position: absolute; inset-inline: 0; height: 2px; background: currentColor; border-radius: 2px;
}
.menu-toggle__bars::before { top: 0; }
.menu-toggle__bars::after { bottom: 0; }
html.on-dark:not(.cycle-open) .menu-toggle { color: var(--ink-inverse); border-color: rgba(255, 255, 255, 0.35); }

/* Overlay: zona dark de marca; navegación real (funciona con motion off) */
.mobile-menu {
  position: fixed; inset: 0; z-index: calc(var(--z-nav) + 10);
  display: none; flex-direction: column; justify-content: flex-end; gap: 2.5rem;
  padding: clamp(1.5rem, 6vw, 3rem);
  background: var(--ground-dark);
}
.mobile-menu.is-open { display: flex; }
.mobile-menu__close {
  position: absolute; top: 1rem; right: clamp(1rem, 4vw, 2rem);
  width: 48px; height: 48px; border-radius: 999px; display: grid; place-items: center;
  border: var(--rule) solid rgba(255, 255, 255, 0.35); color: var(--ink-inverse);
  transition: border-color var(--d-fast) var(--ease-swift);
}
.mobile-menu__close:hover, .mobile-menu__close:focus-visible { border-color: var(--mint); }
.mobile-menu__list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.75rem; }
.mobile-menu__list li { display: flex; align-items: baseline; gap: 0.9rem; }
.mobile-menu__list .t-index { color: var(--mint); }
.mobile-menu__list a {
  font-family: var(--font-display); font-weight: 700; line-height: 1.15;
  font-size: clamp(1.9rem, 8.5vw, 2.9rem);
  color: var(--ink-inverse-2); text-decoration: none;
  /* Área táctil de 44px (estándar propio de la pieza) POR PADDING, sin tocar el tamaño de letra:
     eran los únicos controles del overlay sin área garantizada, siendo los más usados de esa
     pantalla (change targets-tactiles). El padding sustituye parte del gap del <li>. */
  display: block; min-height: 44px; padding-block: 0.15rem;
  transition: color var(--d-fast) var(--ease-swift);
}
.mobile-menu__list a:hover, .mobile-menu__list a:focus-visible { color: var(--ink-inverse); }
.mobile-menu__foot {
  display: grid; gap: 0.35rem;
  border-top: var(--rule) solid var(--stroke-dark); padding-block-start: 1.25rem;
}
.mobile-menu__foot .t-label { color: var(--ink-inverse-2); }
/* Salida de reserva del drawer (change cta-movil-persistente): única fila de acción del pie,
   misma familia mono del vocabulario del menú; área táctil por min-height (el ancho es de
   renglón completo). Mint = color de acción sobre el overlay oscuro (mismo rol que .nav__book
   en on-dark). */
.mobile-menu__book {
  display: inline-flex; align-items: center; min-height: 48px;
  font-family: var(--font-mono); font-size: var(--fs-label); letter-spacing: var(--tracking-label);
  text-transform: uppercase; color: var(--mint);
}
/* Fila de utilidades (idioma + movimiento) y fila de contacto: dos renglones parejos,
   nada de controles flotando sueltos entre los links grandes y el pie. */
.mobile-menu__utils, .mobile-menu__contactline {
  display: flex; justify-content: space-between; align-items: center; gap: 1rem;
}
/* Área táctil de los controles chicos del overlay (change targets-tactiles): el switch de idioma
   medía 20x18 y el correo 150x18 — bastante más chicos que los links que la auditoría sí atendió.
   min-height + inline-flex agranda el área sin mover el texto de su línea visual. */
.mobile-menu .lang-switch a,
.mobile-menu .lang-switch [aria-current],
.mobile-menu__contactline a,
.mobile-menu__contactline .t-label {
  display: inline-flex; align-items: center; min-height: 44px;
}
/* ES/EN además necesitan ANCHO: con min-height quedaban 20x44, y la dimensión MENOR es la que
   cuenta para el área táctil. min-width 44 + centrado, sin cambiar el texto ni su tamaño. */
.mobile-menu .lang-switch a,
.mobile-menu .lang-switch [aria-current] { min-width: 44px; justify-content: center; }
/* Toggle con label pleno dentro del menú (el dot del nav no tiene hover en touch) */
.mobile-menu__motion {
  display: inline-flex; align-items: center; gap: 0.7ch;
  min-height: 44px;
  font-family: var(--font-mono); font-size: var(--fs-label);
  letter-spacing: var(--tracking-label); text-transform: uppercase;
  color: var(--ink-inverse-2);
  transition: color var(--d-fast) var(--ease-swift);
}
.mobile-menu__motion:hover, .mobile-menu__motion:focus-visible { color: var(--ink-inverse); }
/* En el overlay dark, el anillo del estado off usa la tinta inversa (el --ink sería invisible) */
html[data-motion="off"] .mobile-menu .motion-toggle__dot { box-shadow: inset 0 0 0 1.5px var(--ink-inverse); }
/* Con el menú abierto no scrollea el fondo (Lenis se detiene desde nav.js) */
html.menu-open { overflow: hidden; }
@media (max-width: 900px) { .menu-toggle { display: inline-flex; } }
@media (min-width: 901px) { .mobile-menu { display: none !important; } }

/* Modo dual del nav: tinta clara sobre zona dark (html.on-dark, conmutado por nav.js), tinta
   oscura sobre contenido claro. Durante la APERTURA del clímax (html.cycle-open, invert > 0.55)
   el campo del hero ya es claro aunque la zona siga siendo el hero: el modo dark se suspende. */
html.on-dark:not(.cycle-open) .nav__brand,
html.on-dark:not(.cycle-open) .nav__meta .t-label,
html.on-dark:not(.cycle-open) .motion-toggle { color: var(--ink-inverse); border-color: rgba(255, 255, 255, 0.35); }
html.on-dark:not(.cycle-open) .nav__sections a { color: var(--ink-inverse-2); }
html.on-dark:not(.cycle-open) .nav__sections.has-active a:not(.is-active) { color: var(--ink-inverse-2); opacity: 0.6; }
html.on-dark:not(.cycle-open) .nav__sections a.is-active,
html.on-dark:not(.cycle-open) .nav__sections a:hover { color: var(--ink-inverse); }
html.on-dark:not(.cycle-open) .nav__sections a::before { background: var(--mint); }
html.on-dark:not(.cycle-open) .nav__brand-dot circle { stroke: var(--mint); }

/* =========================== HERO =========================== */
.hero {
  position: relative; min-height: 100svh;
  display: flex; flex-direction: column; justify-content: flex-end;
  padding-block-end: clamp(2.5rem, 6vh, 6rem);
  overflow: hidden;
}
/* Capa de fondo FIJA: el terreno persiste detrás de toda la página (robar #1). z-index -1 =
   encima del fondo raíz, debajo del contenido. Las secciones opacas lo tapan; las transparentes
   lo revelan (compositing #3). */
.bg-layer { position: fixed; inset: 0; z-index: 0; pointer-events: none; }
/* Velo tonal de página (patrón topology): UN elemento fijo entre el terreno y el contenido hace
   el arco tonal con un solo tween de opacidad. Comparte z-index 0 con .bg-layer: el orden del
   DOM lo pone encima del canvas y debajo de header/main/footer (z 1).
   Gradiente lateral anclado al shell (fix "pattern muerto al centro", 2026-07-16): el papel es
   SÓLIDO dentro de la columna de contenido (--shell 1560px + resto pleno) y baja a 0.62 solo en
   la franja que queda FUERA del shell — a <=1560px el despeje es 0px (max()), cero riesgo AA;
   en pantallas anchas los anillos respiran en los márgenes. will-change porque el scrub de GSAP
   escribe style.opacity por JS y Chrome no auto-promueve la capa (repintaría el gradiente). */
.page-veil { position: fixed; inset: 0; z-index: 0; pointer-events: none;
             --veil-edge: 0.62;   /* alpha de los márgenes; el CIERRE lo sube a 1 (scroll.js) para que el fade sea uniforme */
             background: linear-gradient(90deg,
               rgba(250, 251, 252, var(--veil-edge)) 0,
               rgb(250, 251, 252) calc(max(0px, (100vw - var(--shell)) / 2)),
               rgb(250, 251, 252) calc(100% - max(0px, (100vw - var(--shell)) / 2)),
               rgba(250, 251, 252, var(--veil-edge)) 100%);
             opacity: 0; will-change: opacity; }
/* Solo en modo velo (JS vivo) las secciones sueltan su papel opaco. Sin JS o con motion off,
   la regla no aplica y el compositing opaco de demo-v2 sigue siendo la verdad. */
html.veil-mode .section { background: transparent; }
html.veil-mode .footer { background: rgba(4, 33, 29, 0.55); } /* refuerzo local dark: el texto chico exige más piso que el velo 0.08 (lección medida en demo-v3) */
/* el contenido va por encima del terreno fijo; las secciones opacas lo tapan, las transparentes lo revelan */
header, main, footer { position: relative; z-index: 1; }
/* v3 fix (bug heredado de v1/v2, pendiente de port): con header y main al mismo z-index, main
   pinta encima por orden de DOM y la sección hero intercepta los clics del nav fijo. El header
   sube al nivel del nav para que los links sean clickeables y no queden bajo el contenido. */
header { z-index: var(--z-nav); }
.hero__canvas {
  position: absolute; inset: 0; z-index: 1;   /* por encima del poster de respaldo */
  width: 100%; height: 100%;
}
/* robustez-webgl-fusion: con el contexto WebGL perdido, el canvas (alpha:false = negro) se
   retira y el poster de atrás queda a la vista. La clase la pone el listener de webgl.js. */
html.terrain-lost .hero__canvas { display: none; }
.hero__poster {   /* frame estático DETRÁS del canvas: se ve bajo reduced-motion o si WebGL falla */
  position: absolute; inset: 0; z-index: 0;
  background-color: var(--ground-dark);       /* fallback sólido (regla dura del brief) */
  background-image: var(--gradient-hero);     /* gradiente canónico ADR-003 */
}
.hero__scrim {   /* garantiza contraste del titular sobre el canvas; el campo de arriba queda vivo.
     Fuerte hasta ~56% (cubre eyebrow + titular + lead), luego se disuelve. Calibrado midiendo
     el peor pixel del canvas detrás del titular (mint sobre el gradiente teal vivo). */
  position: absolute; inset: 0; z-index: 2; pointer-events: none;
  background: linear-gradient(to top,
    rgba(4, 33, 29, 0.95) 0%,
    rgba(4, 33, 29, 0.88) 42%,
    rgba(4, 33, 29, 0.5) 58%,
    transparent 74%);   /* scrim OSCURO: protege el titular blanco sobre el gradiente vivo */
}
.hero__inner { position: relative; z-index: var(--z-content); width: 100%; }

/* Secuencia inmersiva de frases (durante el pin). Todas apiladas y centradas. */
.hero__veil {   /* scrim central para legibilidad; su color lo cruza el JS (claro->oscuro)
     junto con el fondo y el texto, así el clímax voltea los tres a la vez */
  position: absolute; inset: 0; z-index: 11; pointer-events: none; opacity: 0;
  background: radial-gradient(62% 54% at 50% 50%,
    var(--veil-c0, rgba(4, 33, 29, 0.84)) 0%,
    var(--veil-c1, rgba(4, 33, 29, 0.5)) 46%,
    transparent 82%);
}
.hero__sequence {
  position: absolute; inset: 0; z-index: 12; pointer-events: none;
  display: grid; place-items: center; padding: var(--gutter);
}
.hero__phrase {
  grid-area: 1 / 1;                 /* todas en la misma celda: se superponen y hacen crossfade */
  margin: 0; text-align: center; opacity: 0; max-width: 22ch;
  font-family: var(--font-display); font-weight: 600;
  font-size: var(--fs-hero); line-height: 1.0; letter-spacing: var(--tracking-hero);
  /* color por variable: el JS lo cruza (dark->claro, apertura del ciclo) con el invert del campo */
  color: var(--phrase-ink, var(--ink-inverse)); text-wrap: balance;
}
.hero__phrase .accent { color: var(--phrase-accent, var(--mint)); }
/* Escalada 1->3: cada frase crece y aprieta un poco (el ojo percibe "esto sube"). */
.hero__phrase:nth-of-type(2) { font-size: calc(var(--fs-hero) * 1.06); letter-spacing: -0.022em; }
.hero__phrase--climax { font-size: calc(var(--fs-hero) * 1.14); letter-spacing: -0.025em; font-weight: 700; }
.hero__eyebrow {
  display: flex; gap: 1.25rem; flex-wrap: wrap; align-items: baseline;
  margin-block-end: clamp(1.5rem, 4vh, 3rem);
}
/* Jerarquía dentro del eyebrow: la 1ª etiqueta ancla (color pleno), la 2ª es contexto
   atenuado con separador; sin esto las dos leían como una sola frase corrida. */
.hero__eyebrow .t-label + .t-label { opacity: 0.62; }
.hero__eyebrow .t-label + .t-label::before {
  content: '·'; margin-inline-end: 1.25rem; opacity: 0.6;
}
/* Grid roto: el título no arranca en la col 1, arranca corrido. */
.hero__title { margin-inline-start: clamp(0rem, -2vw + 1rem, 0rem); }
/* El hero es zona DARK (arco invertido): tintas inversas y acento mint.
   (el h1 ES .t-mega: el selector descendiente heredado de MERIDIANO nunca casaba) */
.hero__title { color: var(--ink-inverse); text-wrap: balance; }  /* balance: evita huérfanos a 375px */
.hero__title .accent-line { color: var(--mint); }
/* Acento de UNA palabra dentro del h1 (change copy-eje-decidir). Sin esta regla heredaría
   --accent-bright, que es el teal de zona CLARA, y se apagaría sobre el hero oscuro. El ámbar
   no es opción: en zona oscura es del CTA primario (regla dura 1 del brief). */
.hero__title .accent { color: var(--mint); }
.hero .t-body { color: var(--ink-inverse-2); }
.hero .t-body strong { color: var(--ink-inverse); }
.hero .t-label { color: var(--ink-inverse-2); }
.hero .scroll-cue { color: var(--ink-inverse-2); }
.hero .scroll-cue__bar { background: var(--stroke-dark); }
.hero .scroll-cue__bar::after { background: var(--mint); }
.hero__foot {
  margin-block-start: clamp(1.5rem, 4vh, 2.75rem);
  display: grid; grid-template-columns: 1fr auto; align-items: end; gap: 1.5rem;
}
.hero__lead { max-width: 34ch; }
.scroll-cue {
  display: inline-flex; flex-direction: column; align-items: center; gap: 0.5rem;
  font-family: var(--font-mono); font-size: var(--fs-label);
  letter-spacing: var(--tracking-label); text-transform: uppercase; color: var(--ink-3);
}
.scroll-cue__bar {
  width: var(--rule); height: 48px; background: var(--stroke-2); position: relative; overflow: hidden;
}
.scroll-cue__bar::after {
  content: ''; position: absolute; inset-inline: 0; top: 0; height: 40%;
  background: var(--accent); animation: cue 2.2s var(--ease-swift) infinite;
}
@keyframes cue { 0%{transform:translateY(-100%)} 60%,100%{transform:translateY(250%)} }

/* =========================== SECCIÓN genérica =========================== */
/* Secciones de contenido: fondo papel OPACO que tapa el terreno fijo. Contacto y footer
   se dejan transparentes (abajo) para que la superficie asome (la "vuelve a aparecer"). */
.section { position: relative; padding-block: var(--section-y); background: var(--ground); }
.section__head {
  display: grid; grid-template-columns: auto 1fr; gap: 1.5rem; align-items: baseline;
  margin-block-end: var(--space-head);   /* mismo valor de siempre, ahora en token: ver tokens.css */
}

/* --- Statement: una línea grande, corrida a la derecha (asimetría intencional) --- */
.statement__body { margin-inline-start: auto; max-width: 20ch; }
.statement .grid12 > .statement__body { grid-column: 4 / 13; }

/* --- Capacidades: filas con índice, título y descripción; hover con acento --- */
.cap-list { border-top: var(--rule) solid var(--stroke); }
.cap-row {
  position: relative;
  display: grid;
  grid-template-columns: 4rem minmax(0, 6fr) minmax(0, 5fr);
  gap: var(--gutter); align-items: start;
  padding-block: clamp(1.5rem, 3.5vw, 2.75rem);
  border-bottom: var(--rule) solid var(--stroke);
  transition: background var(--d-base) var(--ease-swift);
}
.cap-row::before {   /* barrido de acento desde la izquierda al hover */
  content: ''; position: absolute; inset-block: 0; inset-inline-start: calc(-1 * var(--gutter));
  width: 3px; background: var(--accent); transform: scaleY(0); transform-origin: top;
  transition: transform var(--d-base) var(--ease-arc);
}
.cap-row:hover { background: var(--ground-2); }
.cap-row:hover::before { transform: scaleY(1); }
.cap-row:hover .cap-row__title { color: var(--accent-bright); }
.cap-row__title { transition: color var(--d-base) var(--ease-swift); }
.cap-row .t-index { font-size: var(--fs-step-index); }
/* v3: la prosa TÉCNICA va en mono (patrón topology: el cambio de familia marca el tipo de
   contenido, no la jerarquía); las descripciones de capacidades son spec. */
/* tipografia-contratos: prosa de lectura en Inter al piso del brief (era mono 14px, violaba
   la regla de estratos del propio type.css). */
.cap-row__desc { color: var(--ink-2); font-size: var(--fs-body); line-height: 1.65; max-width: 46ch; }

/* Título de sección pinneado a la izquierda + contenido a la derecha (robar #4 de topology).
   position: sticky: Lenis corre sobre window con scroll real, así funciona sin JS y también
   bajo reduced-motion (el título se ancla mientras las filas pasan). */
.pinned-cols {
  display: grid; grid-template-columns: minmax(0, 4fr) minmax(0, 8fr);
  gap: var(--gutter); align-items: start;
}
/* z-index explícito (change proceso-titulo-y-plazo). `.cap-row` es `position: relative` con
   z-index auto y va DESPUÉS en el DOM, así que por orden de pintado su fondo opaco de hover
   (--ground-2) tapaba las glifos del título que se salían de la columna. El aside ya forma su
   propio contexto de apilamiento por ser sticky, así que este z-index queda contenido y NUNCA
   compite con los tokens globales (--z-canvas 0, --z-content 10, --z-nav 40, --z-cursor 90).
   Va en el aside y no en el h2 para que cubra también su índice 04. */
.pinned-aside { position: sticky; top: clamp(90px, 12vh, 140px); align-self: start; z-index: 1; }
/* El tamaño del título se mide contra SU COLUMNA, no contra el viewport (ver .pinned-title). */
.pinned-aside { container-type: inline-size; }
/* Condición del plazo (change proceso-titulo-y-plazo). Vive DENTRO del paso 04, no al pie de
   la lista: hereda la columna de las descripciones, así que ya no necesita `grid-column` ni el
   parche móvil que la devolvía a la 1. El `max-width` empata con .cap-row__desc para que se lea
   como letra chica de esa columna y no como bloque suelto de ancho completo. */
/* Ventana del paso de acompañamiento (change ventana-portal-en-acompanar). Mismo carril y mismo
   mecanismo que `.cap-row__nota`: cae en una fila NUEVA de la retícula del paso, hereda el
   canalón como separación vertical y lo cancela con margen negativo para quedar pegada al copy
   que ilustra. NO se abre un ancho nuevo en la retícula: a 1280 mide 286px y a 1920, 342px.
   El ancho de dos columnas se evaluó y se descartó (panel 2026-08-05): empataba en tamaño con el
   elemento mayor de la pared de casos, tomando prestada la gramática de "caso probado" para algo
   que no tiene cliente. */
.cap-row__ventana {
  grid-column: 3;
  margin-block-start: calc(1.25rem - var(--gutter));
}
.cap-row__nota {
  grid-column: 3; max-width: 46ch; color: var(--ink-3);
  /* La nota cae en una fila NUEVA de la retícula de .cap-row, así que hereda su `gap` (el
     canalón) como separación vertical: medido, 74px entre la descripción y la nota, o sea
     otra vez desprendida de lo que matiza. El margen negativo cancela el canalón y deja el
     aire real en ~12px, que es lo que hace que se lea como su letra chica. */
  margin-block-start: calc(0.75rem - var(--gutter));
}
/* El numeral acompaña al título más grande de la pieza, así que toma el token de índice display
   (change indice-de-seccion-manda-sobre-sus-pasos): en el token de label quedaba en 12px contra
   80px de título, y por debajo de los 18px de los numerales de los pasos que preside. */
.pinned-aside .t-index { display: block; margin-block-end: 0.9rem; font-size: var(--fs-display-index); }
/* Declaración de salida de la secuencia (change paso-datos-usables). Clase PROPIA a propósito:
   NO reusar `.cap-row__nota`, que lleva `grid-column: 3`. Esa columna existe dentro de una fila
   (`4rem / 6fr / 5fr`); colgada de `.pinned-cols`, que tiene DOS columnas, el navegador crea una
   tercera implícita y le roba ancho a las otras dos. Medido en maqueta: las columnas pasan de
   `436 / 872` a `286 / 572 / 406` y la descripción de los pasos de 334px a 198, o sea 41% menos,
   sin que nada falle. Hay un assert de regresión que vigila el ancho por esto mismo.
   Vive dentro del aside sticky, así que acompaña la lectura de los pasos; el `max-width` la
   mantiene dentro de su columna y por debajo del título, que es quien manda en ese bloque. */
.pinned-salida { max-width: 34ch; margin-block-start: 2rem; color: var(--ink-3); }
/* Enlace en la prosa de la salida del proceso. Necesita tratamiento propio porque base.css:44
   pone `a { color: inherit; text-decoration: none }` para TODO el sitio, asi que un enlace sin
   estilo aqui seria invisible: es el mismo defecto que se corrigio el 2026-08-21 en la pagina
   de servicios, donde el enlace a los casos tenia contraste 1 contra su prosa vecina.
   La home no tenia patron para esto: su unico enlace de cuerpo (.work__link) es un envoltorio
   de bloque alrededor de una tarjeta, y la afordancia la da la tarjeta, no el texto.
   Mismo tratamiento que los enlaces de prosa de las paginas de documento, para que el sitio
   senale igual en todas partes. Selector acotado a este bloque a proposito: no se abre una
   regla global de enlaces sin ver donde mas caeria. */
.pinned-salida a {
  color: var(--accent);
  text-decoration: underline;
  text-underline-offset: 0.2em;
  text-decoration-thickness: 1px;
}
.pinned-salida a:hover, .pinned-salida a:focus-visible { color: var(--accent-deep); }
/* Escala hero (change escala-editorial-display): el análogo del título pinneado de 120px de
   topology; el mega no cabe en esta columna (~436px vs ~510px que pide "trabajo" a 11rem).
   ACOTADO A SU COLUMNA (change proceso-titulo-y-plazo): el root es fluido (1rem = vw/120) pero
   `.shell` topa en 1560px, así que arriba de ese ancho la letra seguía creciendo y la columna
   no. Medido antes del fix: a 1440 el texto cabía por 0.06px (calibrado contra "trabajo" en
   singular, y el copy pasó a "trabajamos"); a 1920 desbordaba 94px; a 2560, 299px; y a 960
   (que es 1920 con zoom al 200%, POR ENCIMA del breakpoint de 900px que colapsa la retícula)
   desbordaba 100px, o sea que el defecto pegaba justo a quien amplía para poder leer.
   `min()` toma lo que sea menor: la escala hero de siempre, o lo que quepa en la columna.
   CALIBRACIÓN (medida, no estimada): "Cómo trabajamos" pide 5.37px de ancho por cada px de
   tamaño de letra, y la columna mide 436px a 1440. O sea que el máximo que cabe ahí son
   ~81.2px, y --fs-hero vale 81.25: la escala aprobada estaba EXACTAMENTE en el límite (cabía
   por 0.06px). Por eso NO existe un valor que conserve 81.25 y deje margen a la vez; 18.4cqi
   cede ~1.2% de tamaño a 1440 (80.3px, imperceptible) a cambio de ~6px de margen real en los
   anchos donde hoy desborda. El trade-off se declara aquí en vez de esconderlo. */
.pinned-title {
  color: var(--ink); font-weight: 700; line-height: 0.98;
  font-size: min(var(--fs-hero), 18.4cqi);
}

/* --- Trabajo: los paneles son ventanas de video (.case-window, bloque CASOS EN VIDEO abajo).
   .work--a/.work--b conservan solo su posición asimétrica en el grid. El antiguo .work__panel
   (4:3 + contorno SVG) y .work__media (capa parallax) se eliminaron con casos-producto-en-vivo:
   ya no hay markup que los use (ni ningún [data-parallax]). --- */
/* Bajada de la pared (change pared-con-bajada): marco de la sección, no un caso. Medida en
   ch para que corte en dos renglones a escritorio. Sin caja ni borde: es prosa, y una tarjeta
   aquí sería el tell de plantilla.
   PERTENENCIA POR PROXIMIDAD (change bajada-se-agrupa-con-su-rotulo). Aquí decía que llevaba
   "el mismo aire que separa el head de la primera fila", y esa era la causa escrita del defecto:
   con el mismo aire arriba y abajo el párrafo no pertenece a nada, y como el de abajo salía más
   corto (medido asentado: 50px al caso contra 62.5px al rótulo a 1440; a 390, 31.3 contra 31.3,
   empate exacto) la proximidad lo agrupaba con la primera ventana. El dueño lo leyó exactamente
   así: "parecería que hablara sobre ese caso".
   Ahora se pega a su rótulo cancelando el margen del head con la misma técnica de
   `.cap-row__nota`, y suelta abajo el ritmo con que la pared separa sus filas, así que el primer
   caso arranca como fila y no como continuación del párrafo. */
.pared-bajada {
  max-width: 52ch;
  margin-block-start: calc(1.25rem - var(--space-head));
  margin-block-end: var(--space-case);
}

/* .work-grid quedó con UN solo hijo (el CV) al promover el segundo cerebro a fila propia
   (change segundo-cerebro-fila-propia): el CV pasa al carril DERECHO para que el zigzag de
   la pared alterne sin excepción (izq · der · izq · der-CV · izq-cerebro · der-Redevia) y
   para despegar el único enlace saliente de la mejor prueba de la página. El margen inferior
   lo daba antes la fila siguiente; con un hijo, el row-gap no aplica y hay que declararlo. */
/* row-gap en la escala del GUTTER, no del separador (change fronteras-moviles-pared): solo actua
   en columna unica (en desktop la fila CV es una sola fila del grid12) y con el separador de la
   pared medía EXACTAMENTE lo mismo, asi que la costura interna del CV era indistinguible de una
   frontera entre casos en todo el rango movil. */
.work-grid { display: grid; grid-template-columns: repeat(12, 1fr); gap: var(--gutter) var(--gutter); row-gap: var(--gutter); margin-block-end: var(--space-case); }
.work { position: relative; }
.work--a { grid-column: 6 / 13; }
/* Lead del CV (change cv-con-gramatica-de-familia): ocupa el carril izquierdo que quedó
   vacío al promoverse el cerebro — mini-espejo de Redevia (texto-izq, ventana-der) sin
   estrenar layout. Mismo patrón de los leads hermanos. */
.work-grid__lead { grid-column: 1 / 6; display: grid; gap: 1rem; align-content: center; }
.work-grid__lead .t-label { color: var(--accent); }
.work__caption { display: flex; justify-content: space-between; align-items: baseline; margin-block-start: 1rem; gap: 1rem; }
.work__title { font-family: var(--font-display); font-weight: 600; font-size: clamp(1.25rem, 2vw, 1.75rem); letter-spacing: -0.01em; }

/* =========================== CORNER-BRACKETS: ELIMINADO =========================== */
/* Aquí vivía el sistema paramétrico "split-border" (4 tiras + caps de esquina). Se eliminó
   con el change cierre-una-sola-firma. Su propio comentario declaraba un presupuesto de "2
   momentos (work__panel y contact__row)", pero .work__panel se había eliminado antes con
   casos-producto-en-vivo: quedaba UN solo uso vivo en toda la pieza, el marco del CTA, que
   enmarcaba 70% de aire (contenido ~380px dentro de 1353px a 1440). Sin rima que sostenerlo,
   el marco era un ornamento huérfano; el cierre pasa a ser tipográfico. */

/* --- Contacto / CTA: zona DARK, el ciclo se cierra --- */
/* Base sin veil-mode = fondo sólido por sección (ruta reduced-motion, riesgo R5);
   con veil-mode el velo (0.08 aquí) deja ver los arcos vivos y esta base se suelta. */
.contact { text-align: left; background: var(--ground-dark); }
.contact__line .t-hero { color: var(--ink-inverse); }
.contact .t-label { color: var(--ink-inverse-2); }
.contact .t-index { color: var(--mint); }
/* CTA ámbar sobre dark (única zona permitida para ámbar como CTA, regla dura 1 del brief) */
.contact .btn-magnetic { background: var(--ember); color: var(--ground-dark); }
.contact .btn-magnetic:hover { background: var(--ember-press); }
/* Presentación del fundador: la venta local se basa en relación; el nombre en tinta plena */
/* El aire de abajo pasó a .contact__creds (change credenciales-del-fundador): medido, con los
   dos márgenes puestos la línea de credenciales quedaba a 42px del párrafo y a 3px del titular
   gigante, o sea pegada a la frase equivocada. El párrafo cierra corto y el aire grande va
   DESPUÉS de la línea, que es lo que la ata a lo que respalda. */
.contact__intro { max-width: 52ch; margin-block-end: 0.5rem; color: var(--ink-inverse-2); }
.contact__intro strong { color: var(--ink-inverse); }
/* Credenciales del fundador (change credenciales-del-fundador): letra chica pegada al párrafo
   que respalda, con el ancho de la intro para que se lea como su pie y no como bloque suelto de
   ancho completo (medido sin acote: 1448px heredados del contenedor). Sin borde, sin fondo, sin
   chip: los tres son marcadores de look-AI. La caja baja la da t-caption; aquí solo se acota. */
.contact__creds {
  /* El tope NO es el 52ch de la intro (design.md D4 lo proponía y la medición lo tumbó): `ch`
     escala con el tamaño de la propia letra, así que 52ch aquí son 472px contra los 603px de la
     intro, y la línea caía en 3 renglones a 1920, o sea leía a párrafo y no a línea de datos.
     Medido a 1920: sin tope 1 renglón (989px de texto), 72ch 2 renglones, 52ch 3 renglones.
     El tope quedó como red de seguridad de un renglón con 3 credenciales; con CORe (change
     core-en-credenciales) el texto necesita 1169px y la línea vive en 2 renglones a 1920. */
  max-width: 110ch;
  margin-block-start: 0;
  /* Aire ASIMÉTRICO a propósito: la línea se ata al párrafo que respalda, no al titular gigante
     que le sigue. Medido a 1920 (texto contra texto, no cajas): arriba 14px fijo; abajo 21px con
     el margen que traía la intro, 33px con 3rem y 41px con 3.5rem. Se elige 3.5rem porque el
     line-height 0.98 del titular se come parte del blanco, así que la razón que se VE (1 a 3) no
     es la que declara el CSS. */
  margin-block-end: clamp(2rem, 4vw, 3.5rem);
  color: var(--ink-inverse-2);
}
/* Cada credencial es inquebrantable (change core-en-credenciales): el quiebre libre cortaba
   dentro del último ítem ("PMP," arriba, el instituto abajo). La línea rompe solo en los
   separadores · que viven entre spans. SOLO desde 480px: en un teléfono angosto el span más
   largo (~302px con el root en su piso de 12.5px) no cabe y el nowrap estiraba el scrollWidth
   de la página (33-38px de overflow a 360, cazado por el assert del header); ahí el
   envolvimiento libre es el comportamiento de siempre. El em de media query es fijo (16px). */
@media (min-width: 30em) { .contact__creds span { white-space: nowrap; } }
/* Qué pasa en la llamada: responde la objeción "¿es una llamada de venta?" junto al CTA */
.contact__note { max-width: 52ch; margin-block-start: clamp(1.25rem, 3vw, 2rem); color: var(--ink-inverse-2); }
/* Remate tipográfico (cierre-una-sola-firma): el marco se fue; el botón queda como único
   objeto pesado del cierre y el correo baja a su propia línea (columna, no renglón compartido)
   para que el ojo remate en el botón y no reparta el peso a su derecha. */
.contact__row {
  display: grid; justify-items: start;
  /* Aire medido sobre el TEXTO, no sobre las cajas (change cierre-canales-legibles). Con
     `gap: 1.5rem` (24px) el blanco visible entre los dos canales era de 41px, porque cada uno
     lleva `min-height: 44px` con centrado y aporta ~13px de relleno por lado. Los 44px del
     área táctil NO se tocan (los puso whatsapp-directo): el aire se aprieta desde el gap. */
  gap: 0.25rem;
  margin-block-start: clamp(2rem, 5vw, 3.5rem);
}
/* El botón conserva su aire propio: es el único objeto pesado del cierre y su separación la
   fijó cierre-una-sola-firma. Sin esto, apretar el gap lo pegaría a los canales de texto. */
.contact__row .btn-magnetic { margin-block-end: 0.75rem; }
/* Área táctil de los canales de texto del cierre (change whatsapp-directo): el correo medía
   18px de alto (defecto pendiente que marcó el panel del cierre) y el WhatsApp nuevo lo
   heredaba. Ambos suben al estándar de 44px de la pieza sin cambiar el tamaño de letra: el
   gap del grid ya los separa, así que el min-height no pega uno con otro. */
.contact__row .t-label { display: inline-flex; align-items: center; min-height: 44px; }
/* Los canales tienen que VERSE tocables. Medido sobre el sitio publicado el 2026-08-23: color,
   subrayado y opacidad eran IDÉNTICOS en reposo y con el cursor encima, así que las únicas señales
   de interacción eran la posición y el cursor del navegador. Y el problema se agrava porque
   `.t-label` es, como dice el comentario de abajo, la misma clase de los eyebrows y los captions:
   un visitante no tiene cómo distinguir un canal tocable de un rótulo decorativo.
   El subrayado aparece al apuntar y al enfocar (no permanente: la pila se lee más limpia sin él y
   el foco de teclado ya tenía su outline, que no se toca). El `:active` da la realimentación que
   en pantalla táctil no existe, porque ahí no hay hover: sin él, el medio segundo entre el toque y
   el cambio de app se lee como "no pasó nada".
   Transición barata y de una sola propiedad; bajo reduced-motion el reset global la apaga. */
.contact__row .t-label { transition: opacity var(--d-chrome) var(--ease-swift); }
.contact__row .t-label:hover,
.contact__row .t-label:focus-visible { text-decoration: underline; text-underline-offset: 0.25em; }
.contact__row .t-label:active { opacity: 0.6; }
/* El correo se muestra como DIRECCIÓN, no como rótulo (change cierre-canales-legibles).
   Hereda de .t-label el tamaño, el color y los 44px de área táctil, pero NO sus versalitas:
   con `text-transform: uppercase` y 1.22px de tracking se renderizaba HOLA@YCM360.COM, que se
   lee gritado y pierde la forma con la que se reconoce un correo (la arroba y los puntos dejan
   de agrupar). Además la MISMA dirección iba en caja baja en el pie: la página se contradecía.
   Clase propia y no un override sobre .t-label, que es compartida por eyebrows y captions:
   una excepción invisible ahí engaña al siguiente que lea la clase. */
.contact__mail { text-transform: none; letter-spacing: normal; }
/* cuestionario-calificador D4: .contact era la única zona dark sin parche veil-mode (heredaba
   el transparent de .section y dependía de que el shader "volviera a oscuro" ahí: suposición
   no medida, el mismo patrón que falló en la 028). Igual que .result-band: no se suelta. */
html.veil-mode .contact { background: var(--ground-dark); }

/* (Aquí vivía el CSS del cuestionario calificador, ~70 líneas con su jerarquía de
   escalera y área táctil. Se retiró COMPLETO con el change discovery-vive-en-el-agendado:
   la calificación vive en las preguntas de reserva de Cal.com, RB-002. Sin reglas muertas.) */

.btn-magnetic {
  position: relative; display: inline-flex; align-items: center; gap: 0.75ch;
  font-family: var(--font-mono); font-size: var(--fs-label);
  letter-spacing: var(--tracking-label); text-transform: uppercase;
  color: var(--ground); background: var(--accent);
  padding: 1.15rem 2rem; border-radius: var(--r-btn); min-height: 48px; will-change: transform;  /* kit: botón 48px r8 */
  transition: background var(--d-base) var(--ease-swift);
}
.btn-magnetic:hover { background: var(--accent-bright); }
.btn-magnetic__label { display: inline-block; will-change: transform; }

/* =========================== FOOTER =========================== */
/* footer: zona DARK del cierre; base sólida sin veil-mode (R5), refuerzo local con veil-mode */
.footer { padding-block: clamp(3rem, 7vw, 6rem) 2rem; border-top: var(--rule) solid var(--stroke-dark); background: var(--ground-dark); color: var(--ink-inverse-2); }
.footer .t-section { color: var(--ink-inverse); }
.footer .t-body { color: var(--ink-inverse-2); }
.footer .t-body strong { color: var(--ink-inverse); }
.footer .t-label { color: var(--ink-inverse-2); }
.footer a { color: var(--ink-inverse); }
.footer__grid { display: grid; grid-template-columns: repeat(12, 1fr); gap: var(--gutter); }
.footer__brand { grid-column: 1 / 6; }
/* cierre-una-sola-firma: aquí vivía .footer__mark (marca de agua SVG de 180px, opacity 0.14,
   animada por footerArc()). Se eliminó junto con su markup y su tween: el canvas persistente
   ya dibuja el Ensō en esta banda y dos instancias del símbolo se fundían en una mancha
   (además, a 390px el SVG no tenía regla responsive y cruzaba el wordmark). */
.footer__cols { grid-column: 7 / 13; display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--gutter); }
/* Texto chico sobre dark: ink-inverse-2 (#C8E6E0, 12.6:1 sobre surface-dark; lección demo-v3) */
/* tipografia-contratos: la regla .footer__disclaimer era CSS MUERTO (ningún elemento la usa
   en esta candidata; la línea legal real es .footer__legal.t-label, corta y legítima como
   label). Eliminada en vez de corregida. */

/* =========================== CURSOR custom (decorativo) =========================== */
.cursor {
  position: fixed; top: 0; left: 0; z-index: var(--z-cursor); pointer-events: none;
  width: 12px; height: 12px; border-radius: 999px; border: 1px solid var(--accent);
  transform: translate(-50%, -50%);
  /* Sin mix-blend-mode: difference (panel 2026-07-16): la resta óptica de teal contra el papel
     del clímax daba un rojo fuera de paleta (~#C76477). El color lo decide el mismo contrato
     de zonas del nav (on-dark / cycle-open), no la composición contra lo que haya debajo. */
  transition: width var(--d-fast) var(--ease-swift), height var(--d-fast) var(--ease-swift),
              background var(--d-fast) var(--ease-swift), border-color var(--d-fast) var(--ease-swift);
}
html.on-dark:not(.cycle-open) .cursor { border-color: var(--mint); }
.cursor--active { width: 44px; height: 44px; background: rgba(13,116,100,0.12); }
/* Solo ocultamos el cursor del sistema en dispositivos de puntero fino y con motion encendido.
   Nunca en touch, nunca con motion apagado: el cursor del sistema debe volver. */
@media (hover: hover) and (pointer: fine) {
  html.motion-on.has-cursor, html.motion-on.has-cursor * { cursor: none; }
}
html:not(.motion-on) .cursor, html[data-motion="off"] .cursor { display: none; }
/* El cursor NO existe en punteros gruesos (change magnetismo-con-criterio). La visibilidad
   dependía solo de data-motion, mientras el gate de puntero fino vivía en cursor.js y solo
   evitaba POSICIONARLO, no pintarlo: en un teléfono el círculo quedaba clavado en su posición
   por defecto (esquina superior izquierda) y se veía. Confirmado en el Note 8 del dueño, y
   visible en retrospectiva en todas las capturas móviles de la sesión 039. */
@media (hover: none), (pointer: coarse) {
  .cursor { display: none; }
}

/* =========================== RESPONSIVE =========================== */
@media (max-width: 900px) {
  .cap-row { grid-template-columns: 3rem 1fr; }
  .cap-row__desc { grid-column: 2; margin-block-start: 0.75rem; }
  /* La retícula colapsa a DOS columnas, así que la ventana tiene que soltar la tercera. Sin
     esto quedaba en una columna implícita, estrujada, y su pie se partía en 5 renglones: lo
     cazaron los asserts de caption a 360, 390 y 768, no una mirada. */
  .cap-row__ventana { grid-column: 2; margin-block-start: 1.25rem; }
  .pinned-cols { grid-template-columns: 1fr; }        /* móvil: apila título y contenido */
  .pinned-aside { position: static; margin-block-end: 2rem; }
  /* La nota del plazo sigue a su paso: en móvil las filas colapsan a 2 columnas y la
     descripción cae en la 2, así que la nota la acompaña (antes era `.pinned-note` en la 1,
     cuando colgaba de toda la lista). */
  .cap-row__nota { grid-column: 2; }
  /* La declaración de salida (change paso-datos-usables): aquí el aside deja de ser sticky y se
     apila, así que la declaración cae entre el título y el primer paso. Menos aire arriba para
     que se lea como bajada del título y no como un bloque suelto entre los dos, y más ancho
     porque ya no compite con la columna de la lista. Verificar en el teléfono físico: el
     headless solo cubre layout. */
  .pinned-salida { margin-block-start: 1.25rem; max-width: 46ch; }
  .statement .grid12 > .statement__body { grid-column: 1 / 13; }
  .work--a, .work--b { grid-column: 1 / 13; margin-block-start: 0; }
  .work-grid__lead { grid-column: 1 / 13; }   /* móvil: texto→ventana, como junta y solar */
  .footer__brand { grid-column: 1 / 13; }
  .footer__cols { grid-column: 1 / 13; margin-block-start: 2rem; }
  .hero__foot { grid-template-columns: 1fr; }
  .scroll-cue { display: none; }
  .nav__sections { display: none; }   /* en móvil el nav se reduce a marca + toggle */
  .nav__sep { display: none; }        /* sin lista de secciones no hay nada que separar */
}
@media (max-width: 560px) {
  .nav__meta .nav__meta-hide { display: none; }
  /* El 2o segmento del eyebrow se APILA, no se oculta (change categoria-visible-en-movil).
     tipografia-contratos lo ocultaba aquí "sin pérdida de significado (ya es aria-hidden)", y ese
     argumento era falso: `aria-hidden` describe lo que anuncia un lector de pantalla, en TODO
     ancho, y no dice nada sobre lo que necesita ver quien mira la pantalla. La regla dejaba a los
     teléfonos sin nada arriba del pliegue que nombrara la categoría del negocio; el h1 dice el
     resultado ("empieza a decidir"), no el qué. El problema que motivó ocultarlo era real y sigue
     resuelto: la línea no cabe al lado de "Tijuana, B.C." y el separador quedaba huérfano, así que
     al apilarse el separador desaparece. Medido a 390: el eyebrow pasa de 18px de alto a 59 en ES
     y 41 en EN, el h1 NO se mueve y los dos CTA siguen sobre el pliegue. */
  .hero__eyebrow { row-gap: 0.4rem; }
  .hero__eyebrow .t-label + .t-label { flex-basis: 100%; opacity: 0.8; }
  .hero__eyebrow .t-label + .t-label::before { content: none; }
  .cap-row { grid-template-columns: 2.5rem 1fr; }
}

/* =========================== F3 — contenido YCM360 =========================== */
/* CTAs del hero (zona dark: el primario va en ámbar, regla dura 1 del brief) */
.hero__cta { display: flex; flex-wrap: wrap; gap: 1rem 1.25rem; align-items: center; margin-block-start: clamp(1.25rem, 3vh, 2rem); }
.hero .btn-magnetic { background: var(--ember); color: var(--ground-dark); }
.hero .btn-magnetic:hover { background: var(--ember-press); }
.btn-ghost {
  display: inline-flex; align-items: center; min-height: 48px; padding: 1.15rem 2rem;  /* padding parejo con el primario */
  border: var(--rule) solid rgba(94, 234, 212, 0.4); border-radius: var(--r-btn);
  font-family: var(--font-mono); font-size: var(--fs-label); letter-spacing: var(--tracking-label);
  text-transform: uppercase; color: var(--ink-inverse); text-decoration: none;
  transition: border-color var(--d-fast) var(--ease-swift);
}
.btn-ghost:hover, .btn-ghost:focus-visible { border-color: var(--mint); }

/* El problema y Por qué yo: pila de golpes con el mecanismo statement reutilizado */
/* Denominador fino DENTRO del stack (change asimetria-reticula-zigzag, P-05): 24 fracciones dan
   pasos de 4.2% donde la asimetría se juega, sin migrar el grid12 global (que sostiene Casos y
   Resultado, con la media ya verificada). */
/* minmax(0, 1fr) y NO 1fr: `1fr` es `minmax(auto, 1fr)` y su piso es el min-content, así que con
   24 pistas el texto de los statements empujaba el grid más allá del viewport (regresión medida:
   413px de ancho en un viewport de 390). Con piso 0 las pistas sí pueden encoger. */
/* El gap va con DOS valores (fila, columna=0): con una sola cifra aplicaba también al eje
   horizontal y, con 24 pistas, los 23 huecos sumaban más que el contenedor — las pistas
   colapsaban a 0px y el contenido desbordaba (medido: 734px de scroll en un viewport de 390). */
.statement__stack { grid-column: 4 / 13; display: grid; grid-template-columns: repeat(24, minmax(0, 1fr)); gap: clamp(2.5rem, 6vw, 4.5rem) 0; }
.statement__stack .statement__body { margin-inline-start: 0; }
/* Por defecto todo hijo del stack ocupa el ancho completo: así Problema (3 golpes cortos) queda
   EXACTAMENTE como estaba; solo los .why de Ventaja alternan. */
.statement__stack > * { grid-column: 1 / -1; }
/* Zigzag de Ventaja (P-06): alternancia CORTA (3/24 = 12.5%), no volteo de mitades — el volteo
   izquierda-derecha es el patrón imagen/texto de landing corporativa que la radiografía marca
   como tell. Ancho constante (21/24) para que ningún beat pese más que otro; el ancla visual es
   el .why__tag mono que ya existe, no un número nuevo (habría 3 sistemas de numeración). */
.statement__stack > .why { grid-column: 1 / span 21; }
.statement__stack > .why:nth-of-type(even) { grid-column: 4 / span 21; }
.statement .accent { color: var(--accent); }   /* acento teal pleno sobre el velo claro */
.why__tag { display: block; margin-block-end: 0.75rem; color: var(--accent); }

/* Lo que ya construí: paneles como evidencia. Desde casos-producto-en-vivo los paneles son
   ventanas de video 16:10 (.case-window), no el panel 4:3 con captura/fallback: ver el bloque
   CASOS EN VIDEO arriba. .work--a/.work--b conservan solo su posición en el grid. */
.work__link { display: block; color: inherit; text-decoration: none; }
/* Stat-line: el remate de la prueba, no una nota al pie. Regla de acento arriba (como el
   sistema de reglas del sitio) y anclada al ancho de lectura para que no flote suelta. */
.stat-line { margin-block-start: clamp(2rem, 4vw, 3rem); padding-block-start: clamp(1.25rem, 2.5vw, 1.75rem); border-top: 2px solid var(--accent); display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.75rem 1.5rem; }
/* La banda propia del clímax numérico (`.stat-band`, change climax-retrieval) se ELIMINÓ con el
   change numero-junto-a-su-caso. Vivía entre Resultado y Proceso con el MISMO fondo que Resultado
   (`--ground-dark` en las dos), sin rótulo y con el pie debajo del número: el ojo la leía como el
   remate estadístico de la historia de la alerta, que es justo lo que su requirement prohíbe. La
   prohibición se hacía cumplir en el nombre accesible y no en la pantalla.
   El número ahora remata la fila del segundo cerebro, que es el caso que lo prueba (ver
   `.work-cerebro__stat`). Con la mudanza se pierde el tratamiento ámbar sobre oscuro que el
   bake-off recomendó robar: medido, `--ember` rinde 1.88:1 sobre el fondo claro de la pared, ni
   siquiera el 3:1 de texto grande, contra 5.48:1 del teal. Lo pidió el dueño el 2026-07-30
   sabiendo el intercambio. */
/* El ÚNICO momento mega de la página (change escala-editorial-display): el dato de prueba en
   dos líneas deliberadas (br en ambos HTML). Numerales como estructura, gramática topology §8. */
.stat-line strong { font-family: var(--font-mono); font-weight: 700; font-size: var(--fs-mega); line-height: 0.98; color: var(--accent); }

/* Footer YCM360 */
.footer__360 { color: var(--ink-inverse-2); }
.footer__legal { margin-block-start: 2.5rem; }

@media (max-width: 900px) {
  .statement__stack { grid-column: 1 / 13; }
  /* Móvil: manda la medida de lectura, el zigzag se neutraliza (todos al ancho completo) */
  .statement__stack > .why,
  .statement__stack > .why:nth-of-type(even) { grid-column: 1 / -1; }
}

/* =========================== CASOS EN VIDEO (change casos-producto-en-vivo) =========================== */
/* Ventana de video 16:10 (formato nativo de los clips Remotion): sin recorte ni CLS (aspect-ratio
   reserva la caja). El caption vive FUERA del video (patrón 8792, más seguro en contraste que el
   overlay de 8791: aval del panel a11y 2026-07-19). preload="none" en el markup: 0 red hasta
   que video.js llama play() en el visible. */
.case-window {
  position: relative; aspect-ratio: 16 / 10; overflow: hidden;
  background: var(--surface-dark);
  /* Marco suave (change ventanas-marco-suave): primer consumidor de --r-card. SOLO ventanas
     de video; nada más de la pieza se redondea (el radio global de B = marcador look-AI). */
  border-radius: var(--r-card);
}
/* Sangrado de 1px (change ventanas-seam-subpixel): a anchos fraccionarios y DPR fraccionario
   (1.25/1.5), Chromium rasteriza la textura del video por debajo del borde de la caja y el
   fondo --surface-dark asoma como línea en el contorno derecho. La media desborda 1px y el
   overflow:hidden del contenedor recorta el excedente; el fondo se conserva como placeholder
   pre-carga (preload="none"). NO usar transform:scale (resamplea el frame completo). */
.case-window__media { position: absolute; inset: -1px; width: calc(100% + 2px); height: calc(100% + 2px); object-fit: cover; display: block; }
/* Caption overlay (change ventanas-marco-suave): SOLO las 5 ventanas standalone (CV, Vault,
   Tablero, Push, Bot) llevan su caption DENTRO como figcaption sobre scrim; las filas líder
   (líder, Junta, Redevia) conservan su copy fuera (asimetría deliberada de la pared).
   El scrim es fondo del PROPIO figcaption calibrado a 2 líneas: opacidad plena (0.85 de
   ground-dark) desde el fondo hasta pasado el tope de la primera línea (el padding-top de
   1.75rem + el gradiente ponen el tramo translúcido ARRIBA de los glifos), así un frame
   blanco puro detrás compone AA (contrato de la spec casos-en-video). */
.case-window__caption {
  position: absolute; inset-inline: 0; bottom: 0;
  display: grid; gap: 0.2rem;   /* apilado: el lado-a-lado del caption externo no cabe en los ecos (~341px) */
  padding: 1.75rem 1.1rem 0.85rem;
  background: linear-gradient(to top,
    rgba(4, 33, 29, 0.85) 0,
    rgba(4, 33, 29, 0.85) calc(100% - 1.6rem),
    rgba(4, 33, 29, 0) 100%);
}
.case-window__caption .work__title { color: var(--ink-inverse); }
.case-window__caption .t-label { color: var(--ink-inverse-2); }
/* Velo COMPACTO (change ventana-portal-en-acompanar): el padding de arriba está calibrado para
   ventanas grandes con caption de dos renglones. La ventana del paso de acompañamiento mide
   ~179px de alto a 1280, así que ese padding se comería ~23% del clip. Aquí el pie es UNA línea
   (la declaración de datos de ejemplo), así que baja el aire y se conserva el velo: el velo es lo
   que hace que un recorte de la ventana se lleve la declaración, que es la razón por la que el
   requirement la puso en el pie y no al final de la página. */
/* Padding lateral CORTO a propósito: el pie de esta ventana ("tablero de monitoreo · datos de
   ejemplo") mide 268px contra 268.5 disponibles con el padding de 0.7rem, o sea que cabía en un
   renglón por medio píxel. Eso no es caber, es tener suerte: cualquier cosa que mueva un píxel lo
   parte en dos y nadie se entera. Con 0.45rem quedan ~13px de holgura, y un assert vigila que
   siga en UN renglón en los cuatro anchos. */
.case-window__caption--compacto {
  padding: 0.9rem 0.45rem 0.5rem;
  /* Velo con degradado CORTO. El de arriba se desvanece en sus últimos 1.6rem, calibrado para
     captions altos; en este, que mide ~39px, esa zona de desvanecimiento era la mitad del pie y
     el texto caía donde el velo aún es transparente. Sobre un clip que replica una interfaz
     CLARA eso dio 3.13:1 contra un piso de 4.5:1, y lo cazó el assert de contraste sobre el peor
     frame, no una revisión a ojo. */
  background: linear-gradient(to top,
    rgba(4, 33, 29, 0.9) 0,
    rgba(4, 33, 29, 0.9) calc(100% - 0.5rem),
    rgba(4, 33, 29, 0) 100%);
}
.case-window__caption--compacto .t-label { color: var(--ink-inverse); }

/* Caso LÍDER de la pared (change caso-asistente-lider): el asistente que atiende el WhatsApp
   que el propio sitio publica. Toma el carril ESPEJO (copy-izq 5fr + ventana-der 7fr), y no es
   capricho: la pared alterna carril fila por fila, así que si el líder nuevo entrara con
   ventana-izquierda voltearía a las tres filas de abajo y sus clases, que se llaman por caso,
   pasarían a mentir. Con el espejo, comprobantes / junta / solar conservan el suyo sin tocarse.
   El liderazgo lo da la POSICIÓN (primera) más la ventana ancha, no el selector. */
.work-asistente {
  display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
  gap: var(--gutter); align-items: center;
  margin-block-end: var(--space-case);
}
.work-asistente__lead { display: grid; gap: 1rem; align-content: center; }
.work-asistente__lead .t-label { color: var(--accent); }
/* Carril de copy-izquierda: en columna única ya abre con su texto, así que NO necesita el
   reorden por `order` que sí necesitan las filas de ventana-izquierda. */
@media (max-width: 900px) { .work-asistente { grid-template-columns: 1fr; } }

/* Comprobantes (antes .work-hero-case, renombrada en caso-asistente-lider porque su nombre
   declaraba un PUESTO y el puesto se movió): ventana ANCHA (7fr) a la izquierda + copy (5fr) a
   la derecha. Ya no es la primera fila; conserva su peso de composición y su carril intactos.
   Asimetría distinta de Redevia, para que las ventanas no se lean como grid parejo (tell
   look-AI). Sin cifra. */
.work-comprobantes {
  display: grid; grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
  gap: var(--gutter); align-items: center;
  margin-block-end: var(--space-case);
}
.work-comprobantes__lead { display: grid; gap: 1rem; align-content: center; }
.work-comprobantes__lead .t-label { color: var(--accent); }
/* En columna unica el lead abre la fila (change fronteras-moviles-pared): reorden SOLO visual y
   solo movil via order (el DOM no cambia: los reveals indexan por documento y el video va
   aria-hidden). Sin esto la fila hereda ventana-primero del carril izquierdo de desktop y la
   frontera con el caso anterior queda ventana contra ventana. */
@media (max-width: 900px) { .work-comprobantes { grid-template-columns: 1fr; } .work-comprobantes > .work { order: 2; } }

/* Junta: fila ESPEJO del líder (change caso-junta-a-compromiso): copy a la IZQUIERDA (5fr) y
   ventana a la derecha (7fr) — inversión exacta de .work-comprobantes para sostener el zigzag de
   la pared con 5 casos sin estrenar gramática. Sin cifra; el cierre del lead nombra a Resultado
   (la alerta que este caso siembra). */
.work-junta {
  display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
  gap: var(--gutter); align-items: center;
  margin-block-end: var(--space-case);
}
.work-junta__lead { display: grid; gap: 1rem; align-content: center; }
.work-junta__lead .t-label { color: var(--accent); }
@media (max-width: 900px) { .work-junta { grid-template-columns: 1fr; } }

/* Cotizador solar (change caso-cotizador-solar): ventana-IZQ 7fr / copy-DER 5fr, mismo ratio
   que el líder pero clase distinta (no reclama el puesto de work-comprobantes). Alterna carril
   contra la junta, que es ventana-derecha. Reusa el patrón, no inventa layout. */
.work-solar {
  display: grid; grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
  gap: var(--gutter); align-items: center;
  margin-block-end: var(--space-case);
}
.work-solar__lead { display: grid; gap: 1rem; align-content: center; }
.work-solar__lead .t-label { color: var(--accent); }
/* Mismo reorden lead-primero que el lider (fronteras-moviles-pared): esta fila tambien es
   ventana-izquierda en desktop y por eso abria con video en columna unica. */
@media (max-width: 900px) { .work-solar { grid-template-columns: 1fr; } .work-solar > .work { order: 2; } }

/* Segundo cerebro (change segundo-cerebro-fila-propia): ventana-IZQ 7fr / copy-DER 5fr, mismo
   ratio y misma gramática que el líder y el solar. Sube de tarjeta silenciosa del grid a fila
   con copy propio porque es la prueba de ordenar la información, que es lo que demuestra el
   número grande de la banda; su peso visual tenía que igualar al narrativo. Alterna carril
   contra el CV, que quedó a la derecha. */
.work-cerebro {
  display: grid; grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
  /* Dos ejes distintos (change numero-junto-a-su-caso): el canalón manda entre columnas, pero la
     fila del remate necesita quedar PEGADA al caso, no a la distancia de columna. Con el gutter
     en ambos ejes el número quedaba a 56px de su caso contra 75 de Redevia: 1.3x, ambiguo. */
  gap: clamp(1.5rem, 3vw, 2.25rem) var(--gutter); align-items: center;
  margin-block-end: var(--space-case);
}
/* Remate del caso: el ÚNICO momento mega de la página, pegado a la prueba que lo sostiene. Fila
   completa del bloque (no hermano suelto: así el margen del bloque lo separa de Redevia sin
   márgenes negativos). El margen propio de `.stat-line` se anula porque aquí el ritmo lo pone el
   row-gap; el resto del tratamiento (regla de acento arriba, número en teal, pie en caption) es
   el de la clase base, que siempre estuvo escrita para fondo claro. */
.work-cerebro__stat { grid-column: 1 / -1; margin-block-start: 0; }
/* El pie SIEMPRE en su propio renglón (change mega-a-la-escala-de-su-caso). Hoy cae debajo por
   accidente de ancho: con el número acotado cabría a su lado, y como el ancho depende del string
   ("a una pregunta" contra "to a question"), ES y EN podrían componer distinto. Determinista. */
.work-cerebro__stat .t-caption { flex-basis: 100%; }
/* Acote contra SU COLUMNA, el mismo mecanismo de `.pinned-title` y por la misma causa: arriba del
   tope del shell el root sigue creciendo y la columna no. Medido antes del acote: el número
   ocupaba 81% de su bloque a 1440, 97% a 1920 y 100% a 2560, o sea que dejaba de ser el remate de
   un caso y se volvía un letrero. Con 7.7cqi quedaba en 61% constante de 1200 a 2560; la afinada
   del dueño del 2026-08-01 (change mega-de-horas-a-pregunta, la conversación que abrió señalando
   el protagonismo del mega) lo baja a 6.2cqi = ~49%, presente sin dominar el scroll (techo
   normativo 55% en la spec, assert <=55). Vive en la clase y NO toca `--fs-mega`: el token
   declara el estrato mega del sistema.
   Acotado a min-width 901 a propósito: por debajo el bloque se apila y manda el tratamiento móvil
   ya afinado (piso propio de 36px al final de este archivo, que preserva las dos líneas). */
@media (min-width: 901px) {
  .work-cerebro { container-type: inline-size; }
  .work-cerebro__stat strong { font-size: min(var(--fs-mega), 6.2cqi); }
}
.work-cerebro__lead { display: grid; gap: 1rem; align-content: center; }
.work-cerebro__lead .t-label { color: var(--accent); }
/* Reorden lead-primero con los 3 hijos explicitos (fronteras-moviles-pared): el mega SIGUE
   cerrando la fila (lead, ventana, stat); sin order el DOM movil era ventana, lead, stat. */
@media (max-width: 900px) { .work-cerebro { grid-template-columns: 1fr; } .work-cerebro__lead { order: 1; } .work-cerebro > .work { order: 2; } .work-cerebro__stat { order: 3; } }

/* Redevia: fila asimétrica lead + ventana (otra prueba; "sistema completo"). No un grid parejo.
   5fr/7fr (change consistencia-carriles-pared): con 4fr/7fr la ventana caía al 36.4% del shell,
   un "casi alineada" de 5.3pp respecto al carril derecho (41.67%) que junta y el segundo cerebro
   ya comparten exacto; el casi-empate se leía como error, no como variación (panel 2026-07-22). */
.work-redevia {
  display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
  gap: var(--gutter); align-items: center;
  /* -start a proposito (unica fila que lo usa): colapsa con el -end del cerebro y el hueco no se
     duplica; unificarla a -end cambiaria la frontera con la seccion 03. Mismo token que las demas. */
  margin-block-start: var(--space-case);
}
.work-redevia__lead { display: grid; gap: 1rem; align-content: center; }
.work-redevia__lead .t-label { color: var(--accent); }
@media (max-width: 900px) { .work-redevia { grid-template-columns: 1fr; } }

/* Clímax "Resultado": el producto trabajando. Banda oscura (isla de tono ganado, P-01/P-04) al
   estilo de .contact — el shader fijo NO alterna aquí (8792 no tiene canvas-flip; esa es la firma
   de 8791). Fondo dark SÓLIDO que NO se suelta con veil-mode (a media página el velo es claro:
   sin esta regla el clímax saldría claro). */
.result-band { background: var(--ground-dark); }
html.veil-mode .result-band { background: var(--ground-dark); }   /* gana al 'transparent' de .section (misma especificidad, va después) */
.result-band .t-section, .result-band .work__title { color: var(--ink-inverse); }
.result-band .t-statement { color: var(--ink-inverse); }
.result-band .t-body { color: var(--ink-inverse-2); }
.result-band .t-label { color: var(--ink-inverse-2); }
.result-band .t-index { color: var(--mint); }
/* La ventana sobre la banda oscura conserva --surface-dark (#073A33): un paso MÁS claro que el
   fondo --ground-dark (#04211d) de la banda, así el marco de la ventana se distingue. (Antes
   usaba --ground-dark-2/--glow-dark, tokens inexistentes cuyo fallback la dejaba invisible.) */
.result-band .case-window { box-shadow: 0 8px 40px rgba(0, 0, 0, 0.35); }
.result__copy { max-width: 46ch; margin-block-end: clamp(2.5rem, 6vw, 4.5rem); display: grid; gap: 1rem; }
/* claim de cierre en mono (NO un segundo mega). La especificidad debe ganar a
   `.result-band .t-label` (que lo pintaría ink-inverse-2): por eso va calificado con la banda. */
.result-band .result__claim { color: var(--mint); }
.result__frames { display: grid; gap: var(--gutter); justify-items: start; }
.result__lead { width: min(74%, 100%); }
.result__pair { width: min(58%, 100%); display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--gutter); }
.result-band .work__caption { display: flex; justify-content: space-between; align-items: baseline; margin-block-start: 1rem; gap: 1rem; }
@media (max-width: 900px) {
  .result__lead, .result__pair { width: 100%; }
}
@media (max-width: 600px) {
  .result__pair { grid-template-columns: 1fr; }
}

/* El piso móvil del mega (48px) rompe el quiebre de dos líneas del stat en angosto: piso propio
   que preserva las DOS líneas en ambos idiomas a 320-560px. Recalibrado por el change
   mega-de-horas-a-pregunta: la línea larga ya no es "to 10 seconds" (13ch) sino "a una
   pregunta" (14ch, ES), y el clamp 36px/11vw calibrado a 13ch quebraba a TRES líneas en
   320-390 (medido: el umbral de dos líneas es 33px a 320 y 38px a 360). Con 32px/10.2vw las
   dos líneas aguantan con margen en todo el rango. Sigue siendo el texto más grande del
   viewport móvil. Va al FINAL del archivo: la regla base del stat vive más abajo que el bloque
   responsive general y este override debe ganar por orden. */
@media (max-width: 560px) {
  .stat-line strong { font-size: clamp(32px, 10.2vw, 136px); }
}
