/* ================================================================
   ZOLEZZI Consulting — Concepto «Documento»
   Blanco dominante · bloques de azul noche a sangre · Inter monumental
   ----------------------------------------------------------------
   Los bloques oscuros no redefinen componentes: reasignan los
   tokens de color en su propio ámbito y todo lo de dentro se
   invierte solo. Cualquier componente nuevo debe usar los tokens,
   nunca colores literales, o romperá esa inversión.

   El azul --accent SÍ puede ser tinta (10.3:1 sobre blanco), a
   diferencia de la gama cálida anterior. Se reserva para estructura
   —numerales, foco, estado activo, filete de énfasis—, nunca para
   texto corrido ni para rellenos decorativos.
   ================================================================ */

@font-face {
  font-family: 'Inter';
  src: url('/assets/fonts/inter-latin.woff2') format('woff2');
  font-weight: 300 600;
  font-style: normal;
  font-display: swap;
}

/* ── Tokens ─────────────────────────────────────────────────── */
:root {
  color-scheme: light;

  /* ── Capa de alias ───────────────────────────────────────────
     Los componentes de esta hoja están escritos contra nombres
     internos (--canvas, --rule, --accent…). En lugar de renombrar
     unos 200 usos repartidos por todo el archivo, se mapean aquí
     contra los tokens de tokens.css, que es la única fuente de
     verdad. Ningún componente ha tenido que tocarse.

     --ink NO se declara: viene tal cual de tokens.css. */
  --canvas:   var(--bg-base);
  --canvas-2: var(--bg-surface);
  /* La paleta define dos niveles de tinta sobre claro, no tres. Los
     antiguos --ink-2 y --ink-3 colapsan ambos en --ink-soft: se
     pierde un escalón de jerarquía en antetítulos y metadatos, pero
     no se inventa ningún color fuera de la paleta. */
  --ink-2:    var(--ink-soft);
  --ink-3:    var(--ink-soft);
  --rule:     var(--borde);
  --rule-2:   var(--borde);

  /* Sobre fondo claro, todo lo que sea tinta o enlace va en
     --teal-deep. Turquesa y celeste están prohibidos ahí. */
  --accent:   var(--teal-deep);
  --navy:     var(--bg-dark);
  --blue:     var(--teal-deep);
  --sky:      var(--celeste);
  --teal:     var(--turquesa);
  --sky-ink:  var(--teal-deep);
  --teal-ink: var(--teal-deep);

  --c-navy: var(--c-dark);
  --c-blue: var(--c-teal);
  --c-sky:  var(--c-celeste);

  --sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;

  --margin: clamp(1.25rem, 5.5vw, 7.5rem);
  /* --maxw es el ancho del CONTENIDO. --shell le suma los márgenes,
     porque con box-sizing:border-box un max-width a secas se los come
     y en pantallas grandes la retícula quedaba 200px más estrecha. */
  --maxw:   1340px;
  --shell:  calc(var(--maxw) + var(--margin) * 2);
  --gap:    clamp(2rem, 4vw, 5rem);

  --hdr-h: 76px;
  --ease:  cubic-bezier(.22, .61, .36, 1);
  --ease-o: cubic-bezier(.16, 1, .3, 1);
}

/* Ámbito oscuro. Solo cambia el significado de los tokens y todo lo de
   dentro se adapta solo. El hero entra aquí: en el ritmo nuevo abre en
   oscuro.

   Los tonos intermedios se DERIVAN de --ink-invert con color-mix, no
   se inventan: la paleta define un solo tono de tinta sobre oscuro y
   hacen falta dos escalones más para antetítulos y filetes. El valor
   estático que precede a cada color-mix es el respaldo para navegadores
   que no lo soportan. */
.hero,
.sec--dark,
.cta,
.ftr {
  --canvas:   var(--bg-dark);
  --canvas-2: var(--bg-dark-alt);
  --ink:      var(--ink-invert);

  --ink-2: #a7b2b6;
  --ink-2: color-mix(in srgb, var(--ink-invert) 70%, var(--bg-dark));
  --ink-3: #859196;
  --ink-3: color-mix(in srgb, var(--ink-invert) 55%, var(--bg-dark));
  --rule: rgba(234, 244, 246, .20);
  --rule: color-mix(in srgb, var(--ink-invert) 20%, transparent);
  --rule-2: rgba(234, 244, 246, .10);
  --rule-2: color-mix(in srgb, var(--ink-invert) 10%, transparent);

  /* Sobre oscuro el acento sí puede ser de marca: 12:1. */
  --accent: var(--acento);

  position: relative;
  background: var(--canvas);
  color: var(--ink);
}

/* Los otros dos fondos claros del ritmo. --bg-base es el de por
   defecto y no necesita modificador.

   Ojo con --canvas-2 (el fondo de las tarjetas): sobre una sección
   blanca, una tarjeta blanca es invisible. Ahí las tarjetas bajan a
   --bg-tint, que es el único par que mantiene la separación sin
   depender solo del filete. */
.sec--surface {
  --canvas:   var(--bg-surface);
  --canvas-2: var(--bg-tint);
  background: var(--canvas);
}
.sec--tint {
  --canvas:   var(--bg-tint);
  --canvas-2: var(--bg-surface);
  background: var(--canvas);
}

/* ── Reset ──────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--hdr-h) + 2rem);
}

body {
  margin: 0;
  background: var(--canvas);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 1.0625rem;
  font-weight: 400;
  line-height: 1.65;
  letter-spacing: -.005em;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}

h1, h2, h3, p, ul, ol, dl, dd, figure { margin: 0; }
ul, ol { padding: 0; list-style: none; }
img, svg { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }
button { background: none; border: 0; padding: 0; cursor: pointer; }
button, input, textarea, select { font: inherit; color: inherit; }

.sprite { position: absolute; width: 0; height: 0; overflow: hidden; }
svg use, .sprite symbol * { fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: square; }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 4px;
  border-radius: 1px;
}

.skip {
  position: fixed; top: .75rem; left: .75rem; z-index: 300;
  transform: translateY(-200%);
  background: var(--ink); color: var(--canvas);
  padding: .75rem 1.25rem;
  font-size: .8125rem; letter-spacing: .01em;
  transition: transform .25s var(--ease);
}
.skip:focus { transform: none; }

/* ── Tipografía compartida ──────────────────────────────────── */
.eyebrow {
  font-size: .6875rem;
  font-weight: 500;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--ink-3);
  font-variant-numeric: tabular-nums;
}
/* El numeral en azul es la aparición más constante del acento como
   tinta: marca la estructura del documento sección a sección. */
.eyebrow__n { color: var(--accent); }

/* El tope de 2.875rem no es estético: es lo que cabe en la columna
   pegajosa sin que la palabra más larga («reconocibles.») se salga.
   Si se amplía el cuerpo, hay que ampliar antes .sec__head. */
.h2 {
  font-size: clamp(1.875rem, 3.2vw, 2.875rem);
  font-weight: 500;
  line-height: 1;
  letter-spacing: -.038em;
  margin-top: 1.5rem;
}

.lead {
  font-size: clamp(1.25rem, 2.1vw, 1.875rem);
  font-weight: 400;
  line-height: 1.42;
  letter-spacing: -.024em;
  text-wrap: balance;
}

/* El texto atenuado da jerarquía dentro de un mismo bloque sin
   recurrir a otro peso ni a otro color de marca. */
.dim { color: var(--ink-3); }
/* La línea que carga el mensaje. Es el otro destino de --gradiente.
   El respaldo va PRIMERO: si el navegador no soporta background-clip
   sobre texto, `color: transparent` dejaría el titular invisible. */
.hl { color: var(--acento); }
@supports ((-webkit-background-clip: text) or (background-clip: text)) {
  .hl {
    background-image: var(--gradiente);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    /* Sin esto, el degradado se recorta contra la caja de la línea y
       las colas de las letras pierden color. */
    -webkit-text-fill-color: transparent;
  }
}

/* Rótulo obligatorio en cualquier gráfico o contador: lo que muestran
   es la forma de nuestros indicadores, NO un resultado de cliente.
   Ninguna cifra de la página puede ir sin esto. */
.ilus {
  display: inline-flex;
  align-items: center;
  gap: .4rem;
  margin-top: .875rem;
  font-size: .6875rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.ilus::before {
  content: '';
  width: .3rem; height: .3rem;
  border-radius: 50%;
  background: currentColor;
  opacity: .55;
}

.sec__note {
  margin-top: 1.75rem;
  max-width: 34ch;
  color: var(--ink-2);
  font-size: .9375rem;
  line-height: 1.6;
}

/* ── Animación ──────────────────────────────────────────────── */

/* Revelado: 20px, 500ms, ease-out. --d escalona hermanos sin JS.
   Fuera de no-preference no hay desplazamiento ninguno: el bloque
   aparece ya colocado. */
.rv { opacity: 1; }
@media (prefers-reduced-motion: no-preference) {
  .rv {
    opacity: 0;
    transform: translateY(20px);
    transition:
      opacity .5s var(--ease-o) var(--d, 0s),
      transform .5s var(--ease-o) var(--d, 0s);
  }
  .rv.is-in { opacity: 1; transform: none; }
}

/* Titulares: cada línea sube desde debajo de su propia máscara.
   El clip-path enmascara solo en vertical —a diferencia de
   overflow:hidden, que además recortaba en silencio las palabras más
   anchas que la columna y parecía un error de contenido. */
.mask .ln {
  display: block;
  padding-bottom: .04em;
  /* El recorte se abre .11em por debajo de la caja para que quepan los
     descendentes: con line-height .94 la cola de la «g» sobresale .077em
     y quedaba cortada. El texto en espera se aleja abajo lo mismo. */
  clip-path: inset(0 -100vw -.11em 0);
}
.mask .ln > i {
  display: block;
  font-style: normal;
  /* 124% y no 108%: el recorte llega ahora más abajo y con el valor
     anterior la línea siguiente asomaba antes de tiempo. */
  transform: translateY(124%);
  transition: transform 1.05s var(--ease-o) var(--d, 0s);
}
.mask.is-in .ln > i { transform: none; }
.mask .ln:nth-child(2) > i { --d: .09s; }
.mask .ln:nth-child(3) > i { --d: .18s; }

/* Sin JS no hay observador que añada .is-in: todo visible. */
html:not(.js) .rv,
html:not(.js) .mask .ln > i { opacity: 1; transform: none; }

/* ── La onda ────────────────────────────────────────────────────
   Ya NO hay ningún fondo con degradado en toda la hoja. En su lugar
   entra el dispositivo gráfico del Brand Book: un haz de líneas finas
   paralelas que ondulan, presente en casi todas sus páginas.

   Se dibuja con una sola trayectoria repetida por <use>, cada copia
   desplazada y con la amplitud ligeramente escalada. Eso reproduce el
   morfeo del book sin 40 trayectorias distintas: un único path en el
   sprite y ~48 referencias, que pesan casi nada.

   El desvanecido de los extremos no se hace con degradado de color
   sino con máscara de opacidad sobre el trazo, así que el fondo sigue
   siendo blanco puro.
   ──────────────────────────────────────────────────────────────── */
.wave {
  position: absolute;
  left: 0; right: 0;
  width: 100%;
  z-index: 0;
  pointer-events: none;
  overflow: visible;
  -webkit-mask-image: linear-gradient(to bottom, transparent, #000 28%, #000 72%, transparent);
  mask-image: linear-gradient(to bottom, transparent, #000 28%, #000 72%, transparent);
}
.wave use {
  fill: none;
  stroke: var(--accent);
  stroke-width: 1;
  /* Sin esto, el escalado vertical de cada copia engordaría el trazo
     y el haz perdería la finura que lo define. */
  vector-effect: non-scaling-stroke;
  stroke-opacity: .55;
}

/* En el hero la onda se apoya abajo, bajo el titular. */
.wave--hero { bottom: 0; height: clamp(14rem, 26vw, 24rem); }
/* En los bloques de azul pleno las líneas van en blanco. */
.sec--dark .wave use,
.cta .wave use,
.ftr .wave use { stroke: var(--ink-invert); stroke-opacity: .28; }
.wave--foot { bottom: 0; height: clamp(10rem, 18vw, 17rem); }

/* ── Servicio desarrollado ──────────────────────────────────────
   El bloque largo de consultoría: cabecera con icono, dos columnas
   (qué resuelve / qué recibe), proceso visual y metadatos.
   ──────────────────────────────────────────────────────────────── */
.svc { padding: clamp(2.5rem, 5vw, 4rem) 0; border-top: 1px solid var(--rule); }
.svc:first-child { border-top: 0; padding-top: 0; }

.svc__head {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: clamp(1rem, 2vw, 1.75rem);
  align-items: start;
}
.svc__head .feat__t { margin-top: 0; }

.svc__cols {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(1.5rem, 3vw, 3rem);
  margin-top: clamp(1.75rem, 3vw, 2.5rem);
}
.svc__k {
  font-size: .625rem;
  font-weight: 500;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--sky-ink);
  padding-bottom: .625rem;
  border-bottom: 1px solid var(--rule);
}
.svc__k--wide { display: block; margin-top: clamp(2rem, 4vw, 3rem); }
/* Dentro de las columnas el inventario va a una sola columna: a dos
   quedaría de una línea por celda y parecería roto. */
.feat__l--one { columns: 1; margin-top: .5rem; }

/* ── Visual 2 · Radial de Go-to-Market ──────────────────────────
   Los nodos se colocan por left/top en porcentaje, NO por
   transform: translate(): los porcentajes de translate se refieren al
   tamaño del propio elemento, no al del contenedor, y los nodos
   habrían quedado a distancias distintas según lo largo de su texto.

   Con left/top en % y el SVG en viewBox 0 0 100 100 con
   preserveAspectRatio="none", las coordenadas de las líneas y las de
   los nodos son el mismo sistema y encajan a cualquier tamaño.
   ──────────────────────────────────────────────────────────────── */
.rad {
  position: relative;
  width: min(100%, 30rem);
  margin-inline: auto;
  aspect-ratio: 1;
}
.rad__web { position: absolute; inset: 0; width: 100%; height: 100%; }
.rad__web line { stroke: var(--borde); stroke-width: .4; }

.rad__core {
  position: absolute;
  left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  width: 42%;
  text-align: center;
  font-size: clamp(.75rem, 1.5vw, .875rem);
  font-weight: 500;
  line-height: 1.35;
  color: var(--ink);
  transition: color .3s var(--ease);
}
.rad.is-live .rad__core { color: var(--teal-deep); }

.rad__n {
  position: absolute;
  left: var(--x); top: var(--y);
  transform: translate(-50%, -50%) scale(var(--s, 1));
  padding: .4rem .7rem;
  max-width: 8.5rem;
  border-radius: 999px;
  background: var(--bg-surface);
  box-shadow: inset 0 0 0 1px var(--borde);
  color: var(--ink-2);
  font-size: .6875rem;
  font-weight: 500;
  line-height: 1.25;
  text-align: center;
  cursor: pointer;
  transition:
    transform .3s var(--ease-o),
    background-color .3s var(--ease),
    color .3s var(--ease),
    box-shadow .3s var(--ease);
}
/* Tinta oscura sobre el turquesa: el blanco ahí da 2.54:1. */
.rad__n:hover,
.rad__n:focus-visible {
  --s: 1.08;
  background: var(--turquesa);
  box-shadow: inset 0 0 0 1px var(--turquesa);
  color: var(--bg-dark);
}

/* Texto solo para lectores de pantalla. La descripción viaja aquí
   dentro del botón, así que no depende del intercambio visual del
   centro ni de un aria-live que estaría cantando en cada hover. */
.vh {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

@media (prefers-reduced-motion: no-preference) {
  .rad__n {
    opacity: 0;
    transform: translate(-50%, -50%) scale(.9);
    transition:
      opacity .45s var(--ease-o) calc(var(--i) * 70ms),
      transform .45s var(--ease-o) calc(var(--i) * 70ms),
      background-color .3s var(--ease),
      color .3s var(--ease),
      box-shadow .3s var(--ease);
  }
  .rad.is-in .rad__n { opacity: 1; transform: translate(-50%, -50%) scale(var(--s, 1)); }
}

/* ── Movimiento de botones y tarjetas ───────────────────────────
   200ms en transform y sombra. Nada rebota y nada pasa de 1.02: por
   encima de eso deja de leerse como respuesta y empieza a leerse como
   truco. Solo dentro de no-preference.
   ──────────────────────────────────────────────────────────────── */
@media (prefers-reduced-motion: no-preference) {
  .btn, .chal__i, .tcard, .mock__img, .tools li {
    transition-duration: .2s;
    transition-property: transform, box-shadow, background-color, border-color, color;
    transition-timing-function: var(--ease-o);
  }
  .btn:hover, .chal__i:hover, .tcard:hover { transform: translateY(-2px); }
  .btn:active, .chal__i:active, .tcard:active { transform: translateY(0); }
  /* El barrido interno del botón conserva su propia duración: es el
     gesto de marca, no una microinteracción de hover. */
  .btn::before { transition-duration: .5s; }
}

/* ── Visual 3 · SEO & GEO ───────────────────────────────────────
   Dos superficies enfrentadas. Ni una línea de texto real: lo que se
   compara es la FORMA de cada resultado, así que todo son barras.
   La única frase legible es la cita de marca, que es el punto.
   ──────────────────────────────────────────────────────────────── */
/* Rótulo de cabecera de un visual. `.svis__t` es el nombre genérico;
   `.geo__claim` es el original y se mantiene para no tocar su marcado. */
.geo__claim,
.svis__t {
  font-size: 1.0625rem;
  font-weight: 500;
  letter-spacing: -.02em;
  margin-bottom: 1rem;
}
.geo {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(.75rem, 1.6vw, 1.25rem);
}
.geo__p { margin: 0; }
.geo__k {
  font-size: .625rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin-bottom: .5rem;
}
.geo__box {
  padding: clamp(.875rem, 1.8vw, 1.25rem);
  background: var(--bg-surface);
  border: 1px solid var(--borde);
  border-radius: .5rem;
  min-height: 11rem;
}
.geo__box--ai { border-color: rgb(var(--c-turquesa) / .45); }

.geo__res { padding: .5rem 0; }
.geo__res + .geo__res { border-top: 1px solid var(--rule-2); }
.geo__url, .geo__ttl, .geo__ln {
  display: block;
  border-radius: 2px;
  background: rgb(var(--c-dark) / .12);
}
.geo__url { width: 38%; height: 5px; margin-bottom: .4rem; }
.geo__ttl { width: 72%; height: 9px; margin-bottom: .4rem; background: rgb(var(--c-dark) / .3); }
.geo__ln  { width: 92%; height: 5px; margin-bottom: .3rem; }
.geo__ln--s { width: 64%; }
/* El primero destaca: es la posición que se disputa en SEO clásico. */
.geo__res--top .geo__ttl { background: var(--celeste); }
.geo__res--top .geo__url { background: rgb(var(--c-celeste) / .5); }

.geo__cite {
  margin: .625rem 0;
  padding: .625rem .75rem;
  border-left: 2px solid var(--turquesa);
  background: rgb(var(--c-turquesa) / .07);
  font-size: .75rem;
  line-height: 1.5;
  color: var(--ink-2);
}
.geo__mark { color: var(--teal-deep); font-weight: 500; }

/* ── Visual 4 · Panel de campaña ────────────────────────────────
   Los contadores viven dentro de una ventana: el marco es lo que los
   convierte en «panel» y no en cuatro cifras sueltas.
   ──────────────────────────────────────────────────────────────── */
.dash {
  border: 1px solid var(--borde);
  border-radius: .625rem;
  overflow: hidden;
  background: var(--bg-surface);
}
.dash__bar {
  display: flex;
  align-items: center;
  gap: .375rem;
  padding: .625rem .875rem;
  background: var(--bg-tint);
  border-bottom: 1px solid var(--borde);
}
.dash__bar > span {
  width: 7px; height: 7px;
  border-radius: 50%;
  background: rgb(var(--c-dark) / .18);
}
.dash__ttl {
  margin-left: .625rem;
  font-size: .625rem;
  font-style: normal;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.dash .kpis { margin-top: 0; border: 0; border-bottom: 1px solid var(--borde); }
.dash__spark { display: block; width: 100%; height: clamp(4rem, 9vw, 5.5rem); }
.dash__line {
  fill: none;
  stroke: url(#spark-g);
  stroke-width: 2;
  vector-effect: non-scaling-stroke;
}

/* ── Visual 6 · Flujo de sistemas ───────────────────────────────── */
.flw { display: block; width: 100%; height: auto; }
.flw__l { stroke: var(--borde); stroke-width: 1.5; }
.flw__dot { fill: var(--turquesa); opacity: 0; }
.flw__ns {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: .5rem;
  margin-top: .375rem;
  text-align: center;
  font-size: .6875rem;
  font-weight: 500;
  color: var(--ink-2);
}

@media (prefers-reduced-motion: no-preference) {
  /* La sparkline se dibuja de izquierda a derecha. pathLength="1" en
     el marcado permite animarla sin conocer su longitud real. */
  .dash__line {
    stroke-dasharray: 1;
    stroke-dashoffset: 1;
    transition: stroke-dashoffset 1.4s var(--ease-o) .3s;
  }
  .dash.is-in .dash__line { stroke-dashoffset: 0; }

  /* El punto recorre el flujo en bucle de 3s. Solo se mueve cx, que
     el navegador compone sin recalcular estilo. */
  .flw__dot { animation: flw-run 3s linear infinite; }
  @keyframes flw-run {
    0%        { cx: 52px;  opacity: 0; }
    6%        { opacity: 1; }
    94%       { opacity: 1; }
    100%      { cx: 368px; opacity: 0; }
  }
}

/* ── Envoltorio de los visuales de servicio ─────────────────────
   `.svis` se usaba en los seis visuales desde la fase 4 y NO tenía
   ni una regla: era un gancho sin estilo. El resultado medido era el
   espaciado justo al revés de lo que pide la lectura —los visuales
   que van tras su propio rótulo tenían 13px de aire y los que van
   tras una lista, CERO, pegados al último elemento—.

   Un rótulo nombra al objeto que tiene debajo, así que va junto. Una
   lista es otro bloque y necesita separación. De ahí las dos reglas.
   ──────────────────────────────────────────────────────────────── */
.svis { margin-top: clamp(1.5rem, 3vw, 2.25rem); }
.svc__k--wide + .svis { margin-top: .75rem; }

/* ── Visuales 11-13 · Los tres mini de tecnología ───────────────
   Tres de las cuatro tarjetas de tecnología eran lista de viñetas y
   nada más, en la única sección oscura larga de la página: mucho
   peso de fondo sosteniendo puro texto.

   Comparten base para que se lean como una familia y no como tres
   ocurrencias. Viven sobre --canvas-2 (#12242F), donde turquesa y
   celeste SÍ son legibles —6.26 y 7.96:1—, al revés que sobre claro.
   ──────────────────────────────────────────────────────────────── */
.mini { display: block; width: 100%; height: auto; }
.mini__k {
  margin-top: .625rem;
  font-size: .625rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink-3);
}

/* Franja de disponibilidad. Alto fijo: con preserveAspectRatio="none"
   las barras se estiran a lo ancho y mantienen su grosor relativo. */
.upt { height: 2.25rem; }
.upt__b { fill: var(--turquesa); stroke: none; }
/* La incidencia no se pinta de rojo: no hubo caída. Se atenúa. */
.upt__b--x { fill: rgb(var(--c-acento) / .28); }

/* Capas de defensa. */
.lyr__r {
  fill: none;
  stroke: var(--rule);
  stroke-width: 1;
}
.lyr__r--out { stroke: rgb(var(--c-acento) / .45); stroke-dasharray: 4 4; }
.lyr__c { fill: var(--turquesa); stroke: none; }
/* Tinta oscura sobre el relleno turquesa, como en todo el sitio. */
.lyr__t {
  fill: var(--bg-dark);
  font-size: 11px;
  font-weight: 500;
  text-anchor: middle;
}

/* El expediente. */
.doc__p {
  fill: var(--bg-dark);
  stroke: rgb(var(--c-acento) / .35);
  stroke-width: 1;
}
.doc__p--top { fill: var(--bg-dark-alt); stroke: var(--turquesa); }
.doc__l { fill: none; stroke: rgb(var(--c-acento) / .3); stroke-width: 2; stroke-linecap: round; }
.doc__s { fill: var(--turquesa); stroke: none; }
.doc__k { fill: none; stroke: var(--bg-dark); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }

@media (prefers-reduced-motion: no-preference) {
  /* Las barras de disponibilidad entran en cascada de izquierda a
     derecha. El retardo se calcula sobre el índice del hermano para
     no tener que escribir 30 reglas. */
  .upt__b {
    opacity: 0;
    transition: opacity .3s var(--ease-o);
  }
  .rv.is-in .upt__b { opacity: 1; }
  .upt__b:nth-child(3n+1) { transition-delay: .10s; }
  .upt__b:nth-child(3n+2) { transition-delay: .22s; }
  .upt__b:nth-child(3n+3) { transition-delay: .34s; }
}

/* ── Visual 10 · Arquitectura de contenidos ─────────────────────
   Pilar → clústeres → apoyos, más el enlazado cruzado.

   Jerarquía de peso deliberada: la estructura va en gris y SOLO el
   enlazado interno va en color de marca. Es lo que distingue una
   arquitectura de un organigrama, así que es lo único que se resalta.
   ──────────────────────────────────────────────────────────────── */
.arq { width: min(100%, 32rem); }
.arq__svg { display: block; width: 100%; height: auto; }

.arq__ln {
  fill: none;
  stroke: var(--rule);
  stroke-width: 1.5;
  vector-effect: non-scaling-stroke;
}
.arq__ln--fine { stroke-width: 1; }

/* El enlazado interno: el único trazo de marca del dibujo. */
.arq__x {
  fill: none;
  stroke: var(--turquesa);
  stroke-width: 1.5;
  stroke-dasharray: 3 3;
  vector-effect: non-scaling-stroke;
}

.arq__pil { fill: var(--bg-dark); stroke: none; }
.arq__pl  { fill: var(--ink-invert); font-size: 12px; font-weight: 500; text-anchor: middle; }
.arq__cl  { fill: rgb(var(--c-turquesa) / .16); stroke: var(--turquesa); stroke-width: 1; }
.arq__pg  { fill: rgb(var(--c-dark) / .12); stroke: none; }

/* Leyenda. Cada viñeta reproduce la forma que nombra —relleno sólido,
   barra clara, trazo punteado—, así que se lee sin cruzar la vista con
   el gráfico. */
.arq__key {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem 1.25rem;
  margin-top: 1rem;
  font-size: .6875rem;
  color: var(--ink-2);
}
.arq__key li { display: flex; align-items: center; gap: .4rem; }
.arq__key li::before {
  content: '';
  width: .75rem; height: .625rem;
  border-radius: 2px;
  flex: none;
}
.arq__key--cl::before {
  background: rgb(var(--c-turquesa) / .16);
  box-shadow: inset 0 0 0 1px var(--turquesa);
}
.arq__key--pg::before { background: rgb(var(--c-dark) / .12); }
.arq__key--x::before {
  height: 0;
  border-radius: 0;
  border-top: 1.5px dashed var(--turquesa);
}

@media (prefers-reduced-motion: no-preference) {
  /* Solo se anima el enlazado: la estructura ya está, lo que aparece
     es la relación entre sus partes. */
  .arq__x {
    opacity: 0;
    transition: opacity .6s var(--ease-o) .5s;
  }
  .rv.is-in .arq__x { opacity: 1; }
}

/* ── Visual 9 · Matriz de priorización ──────────────────────────
   Dos ejes, cuatro cuadrantes, cinco burbujas. La convención es tan
   conocida que el gráfico no necesita explicarse; lo único que hay
   que resolver es que CONCLUYA algo, y de eso se encarga el relleno:
   sólido = entra, perfilado = no entra.
   ──────────────────────────────────────────────────────────────── */
.mx { width: min(100%, 34rem); margin-inline: auto; }
.mx__svg { display: block; width: 100%; height: auto; }

.mx__ax  { stroke: var(--ink-3); stroke-width: 1; }
.mx__tip { fill: var(--ink-3); stroke: none; }
.mx__div { stroke: var(--rule); stroke-width: 1; stroke-dasharray: 3 3; }
.mx__q   { fill: rgb(var(--c-turquesa) / .08); stroke: none; }

.mx__ql {
  fill: var(--ink-3);
  font-size: 9px;
  font-weight: 500;
  letter-spacing: .12em;
  text-transform: uppercase;
}
.mx__ql--end { text-anchor: end; }
.mx__ql--on  { fill: var(--teal-deep); }

/* Perfilados por defecto; el relleno se reserva a los que entran. */
.mx__b {
  fill: none;
  stroke: var(--ink-3);
  stroke-width: 1.25;
  stroke-dasharray: 3 3;
}
.mx__b--on { fill: var(--turquesa); stroke: none; }

.mx__bl {
  fill: var(--ink-2);
  font-size: 12px;
  font-weight: 600;
  text-anchor: middle;
}
/* Sobre el relleno turquesa la tinta va OSCURA: en blanco daría
   2.54:1. Es la misma regla que gobierna la pill de desafíos. */
.mx__bl--on { fill: var(--bg-dark); }

.mx__at {
  fill: var(--ink-3);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: .12em;
  text-transform: uppercase;
  text-anchor: middle;
}

.mx__key {
  margin-top: .5rem;
  text-align: center;
  font-size: .6875rem;
  color: var(--ink-3);
}

@media (prefers-reduced-motion: no-preference) {
  /* Las burbujas entran escalando desde su propio centro. El
     transform-origin va en unidades del viewBox, no en %: con
     porcentajes el origen se toma de la caja del SVG entero y las
     burbujas salían disparadas desde una esquina. */
  .mx__b, .mx__bl {
    opacity: 0;
    transition: opacity .5s var(--ease-o) var(--d, 0s);
  }
  .rv.is-in .mx__b,
  .rv.is-in .mx__bl { opacity: 1; }
  .mx__b:nth-of-type(1), .mx__bl:nth-of-type(1) { --d: .10s; }
  .mx__b:nth-of-type(2), .mx__bl:nth-of-type(2) { --d: .18s; }
  .mx__b:nth-of-type(3), .mx__bl:nth-of-type(3) { --d: .26s; }
  .mx__b:nth-of-type(4), .mx__bl:nth-of-type(4) { --d: .34s; }
  .mx__b:nth-of-type(5), .mx__bl:nth-of-type(5) { --d: .42s; }
}

/* ── Visual 7 · La meseta ───────────────────────────────────────
   El encuadre de la sección de desafíos. Vive en el hueco que dejaba
   la columna del titular, así que no cuesta altura de página.

   Lo que comunica no es ninguna de las dos curvas: es la BANDA entre
   ellas. Por eso el relleno lleva más peso visual que los trazos y la
   etiqueta va dentro, no en una leyenda aparte.
   ──────────────────────────────────────────────────────────────── */
.stall { margin: 0; }
.stall__svg { display: block; width: 100%; height: auto; overflow: visible; }

.stall__ax { stroke: var(--rule); stroke-width: 1; }

.stall__gap { fill: rgb(var(--c-turquesa) / .14); stroke: none; }

/* La trayectoria real, en degradado de marca: es la única línea que
   describe un hecho. */
.stall__now {
  fill: none;
  stroke: url(#stall-g);
  stroke-width: 2.5;
  stroke-linecap: round;
  vector-effect: non-scaling-stroke;
}
/* La potencial va punteada y en tinta, no en color de marca: es una
   hipótesis, y pintarla como la real sería venderla como un dato. */
.stall__pot {
  fill: none;
  stroke: var(--teal-deep);
  stroke-width: 1.5;
  stroke-dasharray: 4 4;
  vector-effect: non-scaling-stroke;
  opacity: .75;
}

.stall__mark { stroke: var(--rule); stroke-width: 1; stroke-dasharray: 3 3; }
.stall__pin  { fill: var(--turquesa); stroke: none; }

.stall__lb {
  fill: var(--ink-3);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: .1em;
  text-transform: uppercase;
}
.stall__lb--end { text-anchor: end; }
.stall__lb--mid { text-anchor: middle; }

.stall__gl {
  fill: var(--teal-deep);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: -.01em;
}
.stall__gl--end { text-anchor: end; }
.stall__lead { stroke: var(--teal-deep); stroke-width: 1; opacity: .5; }

@media (prefers-reduced-motion: no-preference) {
  /* Primero se dibuja el hecho —la curva real—, después aparecen la
     hipótesis y el hueco entre ambas. Ese orden ES el argumento.

     Solo se dibuja la curva sólida. La punteada NO se anima con
     stroke-dashoffset: su dasharray forma parte de su significado y
     animar el trazado obligaría a pisarlo, así que entra fundiéndose. */
  .stall__now {
    stroke-dasharray: 1;
    stroke-dashoffset: 1;
    transition: stroke-dashoffset 1.5s var(--ease-o) .2s;
  }
  .stall.is-in .stall__now { stroke-dashoffset: 0; }

  .stall__pot, .stall__gap, .stall__gl, .stall__lead, .stall__pin {
    opacity: 0;
    transition: opacity .6s var(--ease-o) 1.5s;
  }
  .stall__pot { transition-delay: 1.3s; }
  .stall.is-in .stall__pot  { opacity: .75; }
  .stall.is-in .stall__lead { opacity: .5; }
  .stall.is-in .stall__gap,
  .stall.is-in .stall__gl,
  .stall.is-in .stall__pin { opacity: 1; }
}

/* ── Línea de tiempo del proceso ────────────────────────────────
   Seis etapas en horizontal. El conector se dibuja de izquierda a
   derecha con stroke-dashoffset conforme la sección entra en pantalla,
   y cada etapa aparece escalonada detrás de él.

   El SVG del conector vive por debajo de los nodos y NO recibe
   puntero: si lo hiciera, se comería el hover de las etapas.
   ──────────────────────────────────────────────────────────────── */
.tl { position: relative; margin-top: clamp(2.5rem, 5vw, 4rem); }

.tl__line {
  position: absolute;
  top: 1.5rem; left: 0; right: 0;
  height: 2px;
  pointer-events: none;
  overflow: visible;
}
.tl__line path {
  fill: none;
  stroke: var(--rule);
  stroke-width: 2;
}
.tl__line path + path {
  stroke: var(--blue);
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
  /* pathLength="1" en el marcado permite animar el trazo sin conocer
     su longitud real, que cambia con el ancho de la ventana. */
  transition: stroke-dashoffset 1.6s var(--ease-o);
}
.tl.is-in .tl__line path + path { stroke-dashoffset: 0; }

.tl__steps {
  position: relative;
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: clamp(.75rem, 1.6vw, 1.5rem);
}
.tl__i {
  opacity: 0;
  transform: translateY(12px);
  transition:
    opacity .7s var(--ease-o) var(--d, 0s),
    transform .7s var(--ease-o) var(--d, 0s);
}
.tl.is-in .tl__i { opacity: 1; transform: none; }

.tl__dot {
  width: 3rem; height: 3rem;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--canvas);
  box-shadow: inset 0 0 0 1px var(--rule);
  color: var(--blue);
  transition: box-shadow .45s var(--ease), color .45s var(--ease);
}
.tl__i:hover .tl__dot { box-shadow: inset 0 0 0 2px var(--blue); }
.tl__dot svg { width: 1.25rem; height: 1.25rem; }

.tl__n {
  margin-top: 1rem;
  font-size: .625rem;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--sky-ink);
  font-variant-numeric: tabular-nums;
}
.tl__t {
  margin-top: .3rem;
  font-size: clamp(.9375rem, 1.3vw, 1.0625rem);
  font-weight: 500;
  letter-spacing: -.02em;
  line-height: 1.25;
}
.tl__d {
  margin-top: .4rem;
  font-size: .8125rem;
  line-height: 1.55;
  color: var(--ink-2);
}

/* ── Bloque de servicio ─────────────────────────────────────────
   Icono, frase, entregables y metadatos. Es el ladrillo con el que se
   arman marketing digital y tecnología.
   ──────────────────────────────────────────────────────────────── */
.feat {
  display: grid;
  gap: clamp(1.5rem, 3vw, 2.5rem);
  padding: clamp(1.75rem, 3vw, 2.5rem) 0;
  border-top: 1px solid var(--rule);
}
.feat__ico {
  width: 2.75rem; height: 2.75rem;
  display: grid;
  place-items: center;
  border-radius: .625rem;
  background: rgb(var(--c-blue) / .07);
  color: var(--blue);
}
.feat__ico svg { width: 1.375rem; height: 1.375rem; }
.feat__t {
  margin-top: 1.125rem;
  font-size: clamp(1.25rem, 2vw, 1.625rem);
  font-weight: 500;
  letter-spacing: -.03em;
  line-height: 1.15;
}
.feat__s { margin-top: .75rem; color: var(--ink-2); max-width: 54ch; }

/* Entregables: dos columnas de filetes finos, sin viñetas de disco. */
.feat__l {
  margin-top: 1.25rem;
  columns: 2;
  column-gap: clamp(1.25rem, 2.5vw, 2.5rem);
  font-size: .875rem;
  color: var(--ink-2);
}
.feat__l li {
  break-inside: avoid;
  padding: .4rem 0 .4rem 1rem;
  position: relative;
}
.feat__l li::before {
  content: '';
  position: absolute;
  left: 0; top: 1.05em;
  width: .375rem; height: 1px;
  background: var(--sky-ink);
}

/* `.feat__meta` —duración, modelo de cobro y recompra del mandato—
   se retiró el 2026-08-01 por decisión comercial: al vender consultoría
   de ticket alto no se publican plazos ni estructura de precios, se
   conversan. Se quitó también su CSS porque no quedaba ningún uso.
   Para recuperarlo: git log -S'feat__meta' -- assets/css/styles.css */

/* ── Herramientas ───────────────────────────────────────────────
   Wordmark tipográfico, no logotipos redibujados: aproximar marcas
   ajenas a mano se ve barato y además es terreno de marca registrada.
   Tratamiento uniforme = se leen como un instrumental, no como un
   collage de logos.
   ──────────────────────────────────────────────────────────────── */
.tools {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem;
  margin-top: 1.5rem;
}
.tools li {
  padding: .4rem .75rem;
  border: 1px solid var(--rule);
  border-radius: .375rem;
  font-size: .75rem;
  font-weight: 500;
  letter-spacing: .01em;
  color: var(--ink-2);
  transition: border-color .4s var(--ease), color .4s var(--ease);
}
.tools li:hover { border-color: var(--accent); color: var(--ink); }

/* ── Indicadores ────────────────────────────────────────────────
   Contadores. El valor de partida en el HTML es «0» para que sin JS
   no quede vacío, y main.js lo anima hasta data-count.
   ──────────────────────────────────────────────────────────────── */
.kpis {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 1px;
  margin-top: 1.75rem;
  background: var(--rule);
  border: 1px solid var(--rule);
}
.kpi { background: var(--canvas); padding: 1.125rem 1rem; }
.kpi__v {
  display: block;
  font-size: clamp(1.375rem, 2.4vw, 1.875rem);
  font-weight: 500;
  letter-spacing: -.035em;
  color: var(--accent);
  font-variant-numeric: tabular-nums;
}
.kpi__k {
  display: block;
  margin-top: .3rem;
  font-size: .625rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ink-3);
}

/* ── Mockups ────────────────────────────────────────────────────
   Dibujados en SVG, no imágenes: escalan sin peso y se recolorean
   solos con los tokens.
   ──────────────────────────────────────────────────────────────── */
.mocks {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(.75rem, 1.5vw, 1.25rem);
  margin-top: 1.75rem;
}
/* La perspectiva vive en el contenedor, no en la tarjeta: si se pone
   en el propio elemento que rota, el punto de fuga se mueve con él y
   el giro se ve plano. */
.mocks { perspective: 1000px; }
.mock { margin: 0; }
.mock__img {
  width: 100%;
  height: auto;
  display: block;
  border: 1px solid var(--rule);
  border-radius: .5rem;
  overflow: hidden;
  box-shadow: 0 1px 2px rgb(var(--c-dark) / .04);
  transition:
    transform .45s var(--ease-o),
    border-color .45s var(--ease),
    box-shadow .45s var(--ease);
}
@media (prefers-reduced-motion: no-preference) {
  .mock:hover .mock__img,
  .mock:focus-within .mock__img {
    transform: perspective(1000px) rotateY(-4deg) translateY(-4px);
    box-shadow: -12px 14px 32px rgb(var(--c-dark) / .12);
  }
}
.mock:hover .mock__img,
.mock:focus-within .mock__img { border-color: var(--turquesa); }
.mock figcaption {
  margin-top: .75rem;
  font-size: .8125rem;
  font-weight: 500;
  color: var(--ink-2);
}
.mock:hover figcaption { color: var(--ink); }
/* Los rellenos del mockup no heredan el trazo global de `svg use`. */
.mock__img rect, .mock__img circle { stroke: none; }
.mock__bg  { fill: var(--canvas); }
.mock__bar { fill: rgb(var(--c-navy) / .07); }
.mock__dot { fill: rgb(var(--c-navy) / .22); }
.mock__hi  { fill: rgb(var(--c-navy) / .55); }
.mock__ln  { fill: rgb(var(--c-navy) / .16); }
.mock__bl  { fill: rgb(var(--c-sky) / .18); }
.mock__cta { fill: var(--blue); }

/* ── Retícula de cuatro ─────────────────────────────────────────
   Los cuatro servicios de tecnología.
   ──────────────────────────────────────────────────────────────── */
.grid4 {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(1rem, 2vw, 1.75rem);
}
.tcard {
  padding: clamp(1.5rem, 2.6vw, 2.125rem);
  background: var(--canvas-2);
  border: 1px solid var(--rule);
  transition: border-color .4s var(--ease), background-color .4s var(--ease);
}
.tcard:hover { border-color: var(--accent); background: var(--canvas); }
.tcard__t {
  margin-top: 1.125rem;
  font-size: clamp(1.0625rem, 1.6vw, 1.3125rem);
  font-weight: 500;
  letter-spacing: -.028em;
  line-height: 1.2;
}
.tcard__d { margin-top: .625rem; font-size: .875rem; line-height: 1.6; color: var(--ink-2); }
.tcard__l { margin-top: 1.125rem; font-size: .8125rem; color: var(--ink-2); }
.tcard__l li { padding: .3rem 0 .3rem 1rem; position: relative; }
.tcard__l li::before {
  content: '';
  position: absolute;
  left: 0; top: .95em;
  width: .375rem; height: 1px;
  background: var(--sky-ink);
}

/* ── Botones ────────────────────────────────────────────────── */
.btn {
  --btn-bg: var(--ink);
  --btn-fg: var(--canvas);
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: .625rem;
  padding: 1.0625rem 1.75rem;
  background: var(--btn-bg);
  color: var(--btn-fg);
  font-size: .9375rem;
  font-weight: 500;
  letter-spacing: -.01em;
  white-space: nowrap;
  overflow: hidden;
  transition: color .4s var(--ease), background-color .4s var(--ease);
}
/* Barrido de relleno desde abajo, en lugar de un cambio de color seco. */
.btn::before {
  content: '';
  position: absolute; inset: 0;
  background: var(--ink-2);
  transform: translateY(101%);
  transition: transform .5s var(--ease-o);
}
.btn > * { position: relative; z-index: 1; }
.btn:hover::before { transform: none; }

.btn svg { width: 1.0625rem; height: 1.0625rem; position: relative; z-index: 1; transition: transform .45s var(--ease-o); }
.btn:hover svg { transform: translateX(4px); }

.btn--ghost {
  --btn-bg: transparent;
  --btn-fg: var(--ink);
  box-shadow: inset 0 0 0 1px var(--rule);
}
.btn--ghost::before { background: var(--ink); }
.btn--ghost:hover { color: var(--canvas); }

/* Sobre oscuro el secundario va en --acento (12:1). --teal-deep, que
   es el token de tinta de marca, ahí solo da 3.07:1 porque nació para
   fondo claro. El borde sí se queda en --borde: 13.9:1 sobre oscuro. */
.hero .btn--ghost,
.sec--dark .btn--ghost,
.cta .btn--ghost { --btn-fg: var(--acento); box-shadow: inset 0 0 0 1px var(--borde); }

/* Botón principal: uno de los dos únicos destinos de --gradiente.
   La tinta va OSCURA a propósito. Blanco sobre este gradiente da
   2.28-2.54:1 y sería ilegible; el oscuro da 6.96-7.96:1. */
.btn--grad {
  /* --bg-dark y NO --ink: dentro del hero el ámbito oscuro reasigna
     --ink a --ink-invert, y el botón salía con tinta clara sobre el
     gradiente (2.28:1). --bg-dark no se voltea nunca: 7.14-7.96:1. */
  --btn-fg: var(--bg-dark);
  background: var(--gradiente);
}
/* El barrido de relleno no puede taparlo con un color plano: se hace
   con un velo oscuro translúcido que oscurece el propio gradiente. */
.btn--grad::before { background: rgb(var(--c-dark) / .22); }
.btn--grad:hover { color: var(--bg-dark); }

.btn--sm { padding: .6875rem 1.125rem; font-size: .875rem; }

/* Enlace de texto con subrayado que crece desde la izquierda. */
.lnk {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  position: relative;
  font-size: .9375rem;
  font-weight: 500;
  padding-bottom: .25rem;
}
.lnk::after {
  content: '';
  position: absolute; left: 0; right: 0; bottom: 0;
  height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: right;
  transition: transform .5s var(--ease-o);
}
.lnk:hover::after { transform: none; transform-origin: left; }
.lnk svg { width: 1rem; height: 1rem; transition: transform .45s var(--ease-o); }
.lnk:hover svg { transform: translateX(4px); }

/* ── Cabecera ───────────────────────────────────────────────── */
.hdr {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 100;
  height: var(--hdr-h);
  transition: background-color .4s var(--ease), color .4s var(--ease), box-shadow .4s var(--ease);
}
/* Sólida a partir de 80px de scroll, con blur y una sombra discreta.
   Transición de 300ms, como el resto de la interfaz. */
.hdr {
  transition:
    background-color .3s var(--ease),
    color .3s var(--ease),
    box-shadow .3s var(--ease);
}
.hdr.is-scrolled {
  background: var(--bg-surface); /* respaldo si no hay color-mix */
  background: color-mix(in srgb, var(--bg-surface) 88%, transparent);
  backdrop-filter: saturate(180%) blur(18px);
  -webkit-backdrop-filter: saturate(180%) blur(18px);
  box-shadow: 0 1px 3px rgb(var(--c-dark) / .07);
}
/* Sobre el hero, que es oscuro, la cabecera sólida sería un parche
   claro. Ahí conserva su propio lienzo. */
.hdr.is-scrolled.is-inv {
  background: var(--bg-dark);
  background: color-mix(in srgb, var(--bg-dark) 88%, transparent);
  box-shadow: 0 1px 3px rgb(0 0 0 / .3);
}
/* Sobre un bloque oscuro la cabecera se invierte reasignando tokens.
   Estos valores se habían quedado en el negro de dos paletas atrás:
   ahora salen de los tokens, como el resto. */
.hdr.is-inv {
  --canvas: var(--bg-dark);
  --ink:    var(--ink-invert);
  --ink-2:  #a7b2b6;
  --ink-2:  color-mix(in srgb, var(--ink-invert) 70%, var(--bg-dark));
  --ink-3:  #859196;
  --ink-3:  color-mix(in srgb, var(--ink-invert) 55%, var(--bg-dark));
  --rule:   rgba(234, 244, 246, .20);
  --rule:   color-mix(in srgb, var(--ink-invert) 20%, transparent);
  --accent: var(--acento);
  color: var(--ink);
}

/* Respaldo sin JS: la página SIEMPRE abre sobre el hero, que es
   oscuro. Si `main.js` no llega a ejecutarse, nadie pone `.is-inv` y la
   cabecera se quedaría en tinta oscura sobre fondo oscuro. Con esto la
   marca sigue siendo legible aunque el script falle. */
html:not(.js) .hdr {
  --ink:   var(--ink-invert);
  --ink-2: #a7b2b6;
  --ink-3: #859196;
  color: var(--ink);
}

.hdr__in {
  max-width: var(--shell);
  margin: 0 auto;
  padding: 0 var(--margin);
  height: var(--hdr-h);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 2rem;
}

/* Barra de progreso de scroll: 2px con --gradiente. Va fija arriba,
   no pegada al borde inferior de la cabecera, para que se lea como
   progreso de página y no como subrayado del menú. */
.hdr__bar {
  position: fixed;
  left: 0; top: 0;
  z-index: 120;
  height: 2px;
  width: 100%;
  background: var(--gradiente);
  transform: scaleX(var(--p, 0));
  transform-origin: left;
}

.brand { display: flex; align-items: baseline; gap: .625rem; }
.brand__mark {
  font-size: 1.0625rem;
  font-weight: 600;
  letter-spacing: .06em;
}
.brand__sub {
  font-size: .6875rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ink-3);
  transition: color .4s var(--ease);
}

.nav { display: flex; gap: clamp(1.25rem, 2.2vw, 2.25rem); }
.nav__link {
  position: relative;
  font-size: .9375rem;
  color: var(--ink-2);
  transition: color .3s var(--ease);
}
.nav__link::after {
  content: '';
  position: absolute; left: 0; right: 0; bottom: -.375rem;
  height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: right;
  transition: transform .45s var(--ease-o);
}
.nav__link:hover { color: var(--ink); }
.nav__link:hover::after { transform: none; transform-origin: left; }
.nav__link.is-active { color: var(--ink); }

/* ── Grupo «Servicios» ────────────────────────────────────────── */
.nav__grp { position: relative; display: flex; }

.nav__btn {
  display: inline-flex;
  align-items: center;
  gap: .3rem;
  font: inherit;
  font-size: .9375rem;
  cursor: pointer;
}
.nav__chev {
  width: .75rem; height: .75rem;
  transition: transform .3s var(--ease);
}
.nav__btn[aria-expanded="true"] .nav__chev { transform: rotate(180deg); }

.nav__menu {
  position: absolute;
  top: 100%; left: 50%;
  transform: translateX(-50%);
  margin-top: .875rem;
  min-width: 13rem;
  display: flex;
  flex-direction: column;
  padding: .5rem;
  background: var(--bg-surface);
  border: 1px solid var(--borde);
  border-radius: .625rem;
  box-shadow: 0 12px 32px rgb(var(--c-dark) / .12);
}

/* PUENTE SOBRE EL HUECO. Entre el borde inferior del botón y el panel
   hay 14px de `margin-top`, y ese margen queda FUERA de la caja del
   panel: al bajar el ratón hacia el menú el puntero cruzaba 14px de
   tierra de nadie, `pointerleave` saltaba en el grupo y el panel se
   cerraba antes de llegar. El desplegable era inservible con ratón.

   Este pseudoelemento extiende el área sensible del panel hacia arriba
   justo esos 14px. Es invisible y, al ser parte de `.nav__menu`, el
   puntero nunca sale del grupo. Si se cambia el `margin-top` de arriba,
   hay que cambiar aquí `top` y `height` al mismo valor. */
.nav__menu::before {
  content: '';
  position: absolute;
  left: 0; right: 0;
  top: -.875rem;
  height: .875rem;
}
/* IMPRESCINDIBLE, no redundante: `[hidden]{display:none}` del navegador
   tiene especificidad casi nula y lo pisa cualquier regla de clase que
   declare `display` —aquí, el `display:flex` de arriba—. Sin esta línea
   el panel quedaría siempre abierto. Es la misma trampa documentada
   para `.drawer[hidden]`, y ya costó un fallo una vez. */
.nav__menu[hidden] { display: none; }

/* El panel es una tarjeta CLARA siempre, también cuando la cabecera
   está invertida sobre el hero. Ojo: dentro de `.hdr.is-inv` los alias
   --ink, --ink-2 y --ink-3 apuntan a la gama invertida, así que aquí no
   se pueden usar o el texto saldría casi blanco sobre blanco.

   Se tira de tokens que NO se voltean nunca: --ink-soft (tokens.css no
   lo redefine en ningún ámbito) y --bg-dark, que es el mismo recurso
   que ya usa `.btn--grad` para su tinta por este mismo motivo. */
.nav__menu a {
  padding: .5rem .625rem;
  border-radius: .375rem;
  font-size: .875rem;
  color: var(--ink-soft);
  white-space: nowrap;
  transition: background-color .2s var(--ease), color .2s var(--ease);
}
.nav__menu a:hover,
.nav__menu a:focus-visible { background: var(--bg-tint); color: var(--bg-dark); }
.nav__menu a.is-active { color: var(--teal-deep); font-weight: 500; }
/* La sección visible se subraya en turquesa, no en currentColor: es
   la única señal de «dónde estoy» y tiene que distinguirse del hover. */
.nav__link.is-active::after {
  transform: none;
  transform-origin: left;
  background: var(--turquesa);
  height: 2px;
}

.hdr__end { display: flex; align-items: center; gap: 1rem; }

.burger {
  display: none;
  width: 2.25rem; height: 2.25rem;
  flex-direction: column;
  justify-content: center;
  align-items: flex-end;
  gap: .375rem;
}
.burger span {
  display: block;
  height: 1px;
  width: 1.375rem;
  background: var(--ink);
  transition: transform .4s var(--ease), width .4s var(--ease), opacity .3s var(--ease);
}
.burger span:last-child { width: .875rem; }
.burger[aria-expanded="true"] span { width: 1.375rem; }
.burger[aria-expanded="true"] span:first-child { transform: translateY(3.5px) rotate(45deg); }
.burger[aria-expanded="true"] span:last-child { transform: translateY(-3.5px) rotate(-45deg); }

/* ── Cajón móvil ────────────────────────────────────────────── */
.drawer {
  position: fixed;
  inset: 0;
  z-index: 90;
  background: var(--canvas); /* respaldo si no hay color-mix */
  background: color-mix(in srgb, var(--canvas) 86%, transparent);
  backdrop-filter: saturate(180%) blur(22px);
  -webkit-backdrop-filter: saturate(180%) blur(22px);
  padding: calc(var(--hdr-h) + 3rem) var(--margin) 3rem;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  opacity: 0;
  transition: opacity .35s var(--ease);
}
.drawer.is-open { opacity: 1; }

/* El [hidden]{display:none} del navegador tiene especificidad casi nula y pierde
   contra .drawer{display:flex}. Sin esta regla el cajón queda invisible pero
   clicable, fijo sobre toda la ventana, y se come cada clic de la página. */
.drawer[hidden] { display: none; }
/* Durante los 350 ms del cierre `hidden` todavía es false: sin esto sigue
   interceptando clics mientras se desvanece. */
.drawer:not(.is-open) { pointer-events: none; }

.drawer__nav { display: flex; flex-direction: column; }
.drawer__nav a {
  display: flex;
  align-items: baseline;
  gap: 1rem;
  padding: 1.125rem 0;
  border-top: 1px solid var(--rule);
  font-size: clamp(1.75rem, 8vw, 2.5rem);
  font-weight: 500;
  letter-spacing: -.035em;
  opacity: 0;
  transform: translateY(12px);
  transition: opacity .5s var(--ease-o), transform .5s var(--ease-o);
}
.drawer.is-open .drawer__nav a,
.drawer.is-open .drawer__grp { opacity: 1; transform: none; }

/* El escalonado va sobre los HIJOS DIRECTOS del cajón, no sobre los
   enlaces: desde que «Servicios» es un grupo, uno de esos hijos es un
   <div> con tres enlaces dentro y `a:nth-child(n)` ya no numeraba las
   filas que se ven. */
.drawer__nav > * { transition-delay: calc(.06s + var(--i, 0) * .05s); }
.drawer__nav > :nth-child(1) { --i: 0; }
.drawer__nav > :nth-child(2) { --i: 1; }
.drawer__nav > :nth-child(3) { --i: 2; }
.drawer__nav > :nth-child(4) { --i: 3; }
.drawer__nav > :nth-child(5) { --i: 4; }

/* El grupo entra como un bloque y sus enlaces no se escalonan aparte:
   son una sola unidad de lectura. */
.drawer__grp {
  border-top: 1px solid var(--rule);
  padding: 1.125rem 0 .25rem;
  opacity: 0;
  transform: translateY(12px);
  transition: opacity .5s var(--ease-o), transform .5s var(--ease-o);
}
.drawer__gt {
  display: flex;
  align-items: baseline;
  gap: 1rem;
  font-size: clamp(1.75rem, 8vw, 2.5rem);
  font-weight: 500;
  letter-spacing: -.035em;
}
/* Los hijos ya no llevan filete ni retardo propio: los hereda el grupo. */
.drawer__nav .drawer__sub {
  border-top: 0;
  padding: .5rem 0 .5rem 2.5rem;
  font-size: clamp(1rem, 4.5vw, 1.25rem);
  font-weight: 400;
  letter-spacing: -.02em;
  color: var(--ink-2);
  opacity: 1;
  transform: none;
  transition-delay: 0s;
}
.drawer__nav .drawer__sub:hover { color: var(--ink); }

.drawer__n {
  font-size: .6875rem;
  letter-spacing: .16em;
  color: var(--ink-3);
  font-variant-numeric: tabular-nums;
}
.drawer__foot { display: flex; gap: 1.75rem; padding-top: 2rem; border-top: 1px solid var(--rule); }

/* ── Retícula de sección ────────────────────────────────────── */
.sec { padding: clamp(6rem, 13vh, 11rem) 0; }
.sec--dark { padding: clamp(7rem, 15vh, 13rem) 0; }

/* z-index sube el contenido por encima del rescoldo de los bloques
   oscuros, que es un ::before posicionado y si no lo taparía. */
.sec__grid {
  position: relative;
  z-index: 1;
  max-width: var(--shell);
  margin: 0 auto;
  padding: 0 var(--margin);
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  column-gap: var(--gap);
  row-gap: clamp(2.5rem, 5vw, 4rem);
}

/* El titular se queda fijo mientras el contenido de la derecha
   pasa por delante. Es el gesto que sostiene el ritmo del sitio. */
.sec__head {
  grid-column: 1 / 5;
  position: sticky;
  top: calc(var(--hdr-h) + clamp(2rem, 6vh, 4.5rem));
  align-self: start;
}
.sec__body { grid-column: 5 / 13; }

/* ── 01 · Hero ──────────────────────────────────────────────── */
/* Los márgenes verticales están calculados para que la fila de
   posicionamiento quede dentro de la ventana en un portátil de 880px
   de alto: es lo que ancla el hero y evita que parezca inacabado. */
.hero {
  position: relative;
  min-height: 100svh;
  padding-top: calc(var(--hdr-h) + clamp(2.5rem, 5vh, 4.5rem));
  padding-bottom: 2rem;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: clamp(2rem, 4vh, 3.5rem);
  overflow: hidden;
}

/* Halo detrás del titular: --acento al 8%, disuelto sin bordes duros.
   No usa --gradiente (ese tiene solo dos destinos), es un campo radial.
   z-index 0 y pointer-events:none para que no se coma ningún clic, que
   es el fallo que ya ocurrió una vez con el cajón móvil. */
.hero__glow {
  position: absolute;
  z-index: 0;
  pointer-events: none;
  left: clamp(-14rem, -6vw, -4rem);
  top: clamp(4rem, 16vh, 12rem);
  width: min(72rem, 120%);
  aspect-ratio: 3 / 2;
  background: radial-gradient(
    closest-side,
    rgb(var(--c-acento) / .08),
    rgb(var(--c-acento) / .03) 55%,
    transparent 78%
  );
}
/* El contenido va por encima del halo. */
.hero__in,
.hero__meta { position: relative; z-index: 1; }
.hero__in {
  max-width: var(--shell);
  width: 100%;
  margin: 0 auto;
  padding: 0 var(--margin);
}

/* El mínimo de 2.375rem es el mayor con el que «no se improvisa.»
   —la línea más larga— sigue cabiendo entera en una pantalla de
   375px. Subirlo parte el titular por donde no toca. */
.hero__title {
  font-size: clamp(2.375rem, 8.4vw, 8.5rem);
  font-weight: 500;
  line-height: .94;
  letter-spacing: -.048em;
  margin-top: clamp(1.25rem, 2vw, 2rem);
}

.hero__lead {
  margin-top: clamp(1.5rem, 2.6vw, 2.25rem);
  max-width: 50ch;
  font-size: clamp(1.0625rem, 1.35vw, 1.3125rem);
  line-height: 1.6;
  color: var(--ink-2);
}

/* Alcance geográfico: una línea, sin banderas ni lista de países. Va un
   escalón por debajo del lead para que sitúe sin competir con él. */
.hero__geo {
  margin-top: .875rem;
  font-size: .9375rem;
  color: var(--ink-3);
}

.hero__cta {
  margin-top: clamp(1.75rem, 2.8vw, 2.5rem);
  display: flex;
  flex-wrap: wrap;
  gap: .875rem;
}

.hero__meta {
  max-width: var(--shell);
  width: 100%;
  margin: 0 auto;
  padding: 0 var(--margin);
  display: grid;
  grid-template-columns: repeat(4, 1fr);
}
/* Los filetes son bordes, no huecos sobre un fondo: las celdas tienen
   que ser transparentes para que la atmósfera pase por debajo. Un
   fondo opaco aquí abre un rectángulo blanco en mitad del color. */
.hero__meta li {
  border-top: 1px solid var(--rule);
  padding: 1.5rem 1.5rem 0 0;
  font-size: .9375rem;
  color: var(--ink);
}
.hero__meta li + li {
  border-left: 1px solid var(--rule);
  padding-left: clamp(1rem, 2vw, 1.5rem);
}
.hero__meta span {
  display: block;
  margin-bottom: .5rem;
  font-size: .6875rem;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--ink-3);
}

/* ── 02 · Desafíos ──────────────────────────────────────────────
   La sección que abre la página. En vez de la columna pegajosa, aquí
   el titular va arriba y la retícula ocupa las doce columnas: el
   visitante tiene que poder barrer los nueve desafíos de un vistazo y
   reconocerse en uno. Sin sombras —una tarjeta elevada lee como
   producto; un filete fino, como documento—.
   ──────────────────────────────────────────────────────────────── */
.sec__grid--wide .sec__head { grid-column: 1 / 13; position: static; max-width: 46ch; }
.sec__grid--wide .sec__body { grid-column: 1 / 13; }

/* Titular a la izquierda y una pieza visual ocupando el hueco de la
   derecha, que en --wide se quedaba vacío. El cuerpo sigue a todo
   ancho por debajo. Gana composición sin gastar altura. */
.sec__grid--wide.sec__grid--split .sec__head { grid-column: 1 / 6; max-width: 38ch; }
.sec__grid--split .stall { grid-column: 7 / 13; align-self: center; }

.chal {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(.75rem, 1.3vw, 1.125rem);
}

.chal__i {
  display: flex;
  flex-direction: column;
  gap: .5rem;
  padding: clamp(1.5rem, 2.5vw, 2.125rem);
  background: var(--canvas-2);
  border: 1px solid var(--rule);
  transition:
    border-color .4s var(--ease),
    background-color .4s var(--ease);
}
.chal__i:hover { background: var(--canvas); border-color: var(--turquesa); }

/* Etiqueta del servicio que resuelve el desafío. Es lo que convierte
   la sección en enrutador: la tarjeta dice el problema, la pill dice
   a dónde lleva. margin-top:auto la ancla al pie aunque los textos
   tengan distinta longitud. */
.chal__tag {
  align-self: flex-start;
  margin-top: auto;
  padding: .3rem .625rem;
  border-radius: 999px;
  background: var(--bg-tint);
  color: var(--teal-deep);
  font-size: .625rem;
  font-weight: 500;
  letter-spacing: .1em;
  text-transform: uppercase;
  transition: background-color .3s var(--ease), color .3s var(--ease);
}
/* Tinta OSCURA sobre el turquesa, no blanca: el blanco ahí da 2.54:1
   y no pasa AA. Con --bg-dark queda en 7.14:1 y se conserva el mismo
   golpe de color. */
.chal__i:hover .chal__tag {
  background: var(--turquesa);
  color: var(--bg-dark);
}
/* La tarjeta de contacto no lleva pill; su flecha ya hace de remate. */
.chal__i--cta:hover .chal__tag { background: var(--bg-tint); }

.chal__t {
  font-size: clamp(1.0625rem, 1.55vw, 1.3125rem);
  font-weight: 500;
  letter-spacing: -.028em;
  line-height: 1.2;
}
.chal__d { font-size: .875rem; line-height: 1.6; color: var(--ink-2); }

/* La novena cierra la retícula invirtiéndose. Reasigna tokens en su
   propio ámbito, igual que las secciones oscuras, así que el texto de
   dentro se adapta solo. */
.chal__i--cta {
  --ink:    var(--ink-invert);
  --ink-2:  #a7b2b6;
  --ink-2:  color-mix(in srgb, var(--ink-invert) 70%, var(--bg-dark));
  --accent: var(--acento);
  background: var(--bg-dark);
  border-color: var(--bg-dark);
  color: var(--ink);
}
.chal__i--cta:hover { background: var(--bg-dark-alt); border-color: var(--accent); }
.chal__i--cta svg {
  width: 1.125rem; height: 1.125rem;
  margin-top: auto;
  transition: transform .45s var(--ease-o);
}
.chal__i--cta:hover svg { transform: translateX(5px); }

/* ── 05 · Proceso ───────────────────────────────────────────── */

/* La escala del roadmap. Va fuera de la <ol> y alineada con los
   tramos: comparte su sangrado izquierdo, o los extremos no
   coincidirían con el principio y el final de las barras. */
.proc__scale {
  display: flex;
  justify-content: space-between;
  margin-left: clamp(2rem, 4vw, 3.5rem);
  padding-bottom: .5rem;
  margin-bottom: clamp(1.5rem, 3vw, 2.25rem);
  border-bottom: 1px solid var(--rule);
  font-size: .625rem;
  font-weight: 500;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ink-3);
}

.proc {
  position: relative;
  padding-left: clamp(2rem, 4vw, 3.5rem);
}
/* Raíl gris de fondo y raíl de tinta que se llena con el scroll. */
.proc::before,
.proc::after {
  content: '';
  position: absolute;
  left: 4px; top: .75rem; bottom: .75rem;
  width: 1px;
}
.proc::before { background: var(--rule); }
/* La línea que avanza va en --gradiente y se revela con scaleY según
   el progreso de scroll. Es el equivalente de stroke-dashoffset para
   un elemento que no es SVG: mismo efecto de «trazo que se dibuja»,
   sin añadir un SVG solo para esto. */
.proc::after {
  background: var(--gradiente);
  transform: scaleY(var(--p, 0));
  transform-origin: top;
}

/* El aire entre etapas se apretó al entrar los tramos: un diagrama de
   fases solo se lee como UN gráfico si sus filas están cerca. Con la
   separación anterior (hasta 5rem) los seis tramos parecían seis
   barras sueltas en vez de una escala continua. */
.proc__i { position: relative; padding: 0 0 clamp(2.25rem, 4vw, 3.25rem); }
.proc__i:last-child { padding-bottom: 0; }

.proc__rail { position: absolute; left: calc(-1 * clamp(2rem, 4vw, 3.5rem)); top: .5rem; }
.proc__dot {
  display: block;
  width: 9px; height: 9px;
  border-radius: 50%;
  background: var(--canvas);
  box-shadow: inset 0 0 0 1px var(--ink-3);
  transition: box-shadow .6s var(--ease), background-color .6s var(--ease), transform .6s var(--ease-o);
}
/* La etapa alcanzada se pinta en turquesa y crece 1.3. */
.proc__i.is-in .proc__dot {
  background: var(--turquesa);
  box-shadow: inset 0 0 0 1px var(--turquesa);
  transform: scale(1.3);
}

.proc__c {
  opacity: 0;
  transform: translateY(16px);
  transition: opacity .8s var(--ease-o), transform .8s var(--ease-o);
}
.proc__i.is-in .proc__c { opacity: 1; transform: none; }
html:not(.js) .proc__c { opacity: 1; transform: none; }

.proc__n {
  font-size: .6875rem;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--ink-3);
  font-variant-numeric: tabular-nums;
}
.proc__t {
  font-size: clamp(1.875rem, 4vw, 3.25rem);
  font-weight: 500;
  line-height: 1;
  letter-spacing: -.042em;
  margin: .75rem 0 1rem;
}
.proc__d { color: var(--ink-2); max-width: 54ch; }

/* El tramo de cada etapa sobre una escala común. Comparte el
   max-width de .proc__d: si las pistas no midieran todas lo mismo, la
   comparación entre etapas —que es el único punto del gráfico— sería
   falsa. Cualquier cambio a uno de los dos hay que hacerlo a los dos. */
.proc__bar {
  position: relative;
  height: 6px;
  max-width: 54ch;
  margin-top: 1.5rem;
  border-radius: 3px;
  background: var(--rule);
}
.proc__seg {
  position: absolute;
  top: 0; bottom: 0;
  left: var(--a);
  width: var(--b);
  border-radius: 3px;
  background: var(--turquesa);
}
/* La puerta: rombo hueco al final del tramo. Se rellena con --canvas
   para que recorte la pista en lugar de posarse encima. */
.proc__gate {
  position: absolute;
  top: 50%;
  left: calc(var(--a) + var(--b));
  width: 9px; height: 9px;
  transform: translate(-50%, -50%) rotate(45deg);
  background: var(--canvas);
  box-shadow: inset 0 0 0 1.5px var(--teal-deep);
}
/* Optimize no cierra puerta: se desvanece porque el ciclo vuelve a
   empezar. Es máscara de opacidad, no un degradado de color, así que
   el fondo de la sección sigue siendo el suyo. */
.proc__bar--loop .proc__seg {
  -webkit-mask-image: linear-gradient(to right, #000 45%, transparent);
  mask-image: linear-gradient(to right, #000 45%, transparent);
}

@media (prefers-reduced-motion: no-preference) {
  .proc__seg {
    transform: scaleX(0);
    transform-origin: left;
    transition: transform .7s var(--ease-o) .15s;
  }
  .proc__i.is-in .proc__seg { transform: scaleX(1); }
  .proc__gate { opacity: 0; transition: opacity .4s var(--ease) .75s; }
  .proc__i.is-in .proc__gate { opacity: 1; }
}

/* ── 08 · FAQ ───────────────────────────────────────────────── */
.faq { display: flex; flex-direction: column; }
.faq__i { border-top: 1px solid var(--rule); }
.faq > :last-child { border-bottom: 1px solid var(--rule); }
.faq__i h3 { font: inherit; }

.faq__q {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 2rem;
  text-align: left;
  padding: clamp(1.375rem, 2.4vw, 1.875rem) 0;
  font-size: clamp(1.0625rem, 1.7vw, 1.375rem);
  font-weight: 500;
  letter-spacing: -.025em;
  line-height: 1.35;
  color: var(--ink-2);
  transition: color .35s var(--ease);
}
.faq__q:hover { color: var(--ink); }
.faq__i.is-open .faq__q { color: var(--ink); }
.faq__q svg {
  flex: none;
  width: 1.25rem; height: 1.25rem;
  transition: transform .5s var(--ease-o);
}
.faq__i.is-open .faq__q svg { transform: rotate(135deg); }

/* grid-template-rows anima la altura sin conocerla de antemano. */
.faq__a {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows .55s var(--ease-o);
}
.faq__i.is-open .faq__a { grid-template-rows: 1fr; }
.faq__a > div { overflow: hidden; }
.faq__a p {
  padding-bottom: clamp(1.5rem, 2.4vw, 2rem);
  padding-right: clamp(0rem, 6vw, 5rem);
  color: var(--ink-2);
  max-width: 62ch;
}
html:not(.js) .faq__a { grid-template-rows: 1fr; }

/* ── 09 · CTA final ─────────────────────────────────────────── */
.cta { padding: clamp(7rem, 18vh, 15rem) 0; }
.cta__in {
  position: relative;
  z-index: 1;
  max-width: var(--shell);
  margin: 0 auto;
  padding: 0 var(--margin);
}
.cta__t {
  font-size: clamp(2.5rem, 8vw, 8rem);
  font-weight: 500;
  line-height: .94;
  letter-spacing: -.048em;
}
.cta__d {
  margin-top: clamp(2rem, 4vw, 2.75rem);
  max-width: 44ch;
  color: var(--ink-2);
  font-size: clamp(1rem, 1.3vw, 1.1875rem);
  line-height: 1.6;
}
.cta__btns { margin-top: clamp(2.25rem, 4vw, 3rem); display: flex; flex-wrap: wrap; gap: .875rem; }

/* ── 10 · Formulario ────────────────────────────────────────── */
.form { max-width: 46rem; }
.form__row {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  column-gap: var(--gap);
}

.f { position: relative; padding-bottom: 1.75rem; }
.f label {
  display: block;
  margin-bottom: .5rem;
  font-size: .6875rem;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.f input,
.f textarea,
.f select {
  width: 100%;
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--rule);
  border-radius: 0;
  padding: .625rem 0;
  font-size: 1.0625rem;
  transition: border-color .35s var(--ease);
  appearance: none;
}
.f textarea { resize: vertical; min-height: 7rem; line-height: 1.6; }
.f select {
  cursor: pointer;
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%), linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: right 6px center, right 1px center;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}
.f input::placeholder,
.f textarea::placeholder { color: var(--ink-3); }
.f input:hover, .f textarea:hover, .f select:hover { border-color: var(--ink-3); }
.f input:focus, .f textarea:focus, .f select:focus {
  outline: none;
  border-color: var(--ink);
}
.f.is-err input,
.f.is-err textarea { border-color: var(--ink); border-bottom-width: 2px; }

.err {
  position: absolute;
  bottom: .375rem; left: 0;
  font-size: .8125rem;
  color: var(--ink-2);
}

.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

.form__foot {
  margin-top: 1.25rem;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 1.25rem 1.75rem;
}
.form__fine { font-size: .8125rem; color: var(--ink-3); max-width: 30ch; }

.form__msg {
  margin-top: 1.75rem;
  padding: 1.125rem 1.25rem;
  font-size: .9375rem;
  background: rgb(255 255 255 / .5);
  box-shadow: inset 0 0 0 1px var(--rule);
}
.form__msg--ko { box-shadow: inset 0 0 0 1px var(--ink); }

button[aria-busy="true"] { opacity: .6; pointer-events: none; }

/* ── 11 · Pie ───────────────────────────────────────────────── */
.ftr { padding: clamp(4rem, 8vw, 6.5rem) 0 2.5rem; }
.ftr__in {
  position: relative;
  z-index: 1;
  max-width: var(--shell);
  margin: 0 auto;
  padding: 0 var(--margin);
}
.ftr__top {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 2fr);
  column-gap: var(--gap);
  row-gap: 3.5rem;
  padding-bottom: clamp(3rem, 6vw, 5rem);
}
.ftr__mark {
  font-size: 1.25rem;
  font-weight: 600;
  letter-spacing: .06em;
}
.ftr__tag {
  margin-top: .5rem;
  font-size: .9375rem;
  letter-spacing: -.015em;
  color: var(--ink);
}
.ftr__caps {
  margin-top: .875rem;
  font-size: .6875rem;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.ftr__claim {
  margin-top: 1.5rem;
  max-width: 30ch;
  font-size: .9375rem;
  line-height: 1.6;
  color: var(--ink-3);
}

.ftr__cols {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 2rem;
}
.ftr__col h2 {
  font-size: .6875rem;
  font-weight: 500;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin-bottom: 1.25rem;
}
.ftr__col li { margin-bottom: .625rem; }
.ftr__col a, .ftr__col span {
  font-size: .9375rem;
  color: var(--ink-2);
  transition: color .3s var(--ease);
}
.ftr__col a:hover { color: var(--ink); }

.ftr__bot {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 1rem;
  padding-top: 2rem;
  border-top: 1px solid var(--rule-2);
  font-size: .8125rem;
  color: var(--ink-3);
}
.ftr__up a { display: inline-flex; align-items: center; gap: .5rem; transition: color .3s var(--ease); }
.ftr__up a:hover { color: var(--ink); }
.ftr__up svg { width: .875rem; height: .875rem; transform: rotate(-90deg); transition: transform .45s var(--ease-o); }
.ftr__up a:hover svg { transform: rotate(-90deg) translateX(3px); }

/* ── Adaptación ─────────────────────────────────────────────── */
@media (max-width: 1180px) {
  .nav { display: none; }
  .burger { display: flex; }
}

@media (max-width: 1024px) {
  .sec__grid { grid-template-columns: 1fr; }
  .sec__head,
  .sec__body { grid-column: 1; }
  .sec__head { position: static; }
  .sec__note { max-width: 52ch; }

  /* En una columna el reparto izquierda/derecha ya no existe. El
     selector de --split gana por especificidad (0,3,0) al de arriba,
     así que hay que anularlo con el suyo propio, no con .sec__head. */
  .sec__grid--wide.sec__grid--split .sec__head { grid-column: 1; max-width: 46ch; }
  .sec__grid--split .stall { grid-column: 1; max-width: 34rem; }

  /* A dos columnas el filete vertical solo separa la segunda de cada
     fila, no cada celda de la anterior. */
  .hero__meta { grid-template-columns: repeat(2, 1fr); }
  .hero__meta li { padding: 1.25rem 1.25rem 1.25rem 0; }
  .hero__meta li + li { border-left: 0; padding-left: 0; }
  .hero__meta li:nth-child(even) {
    border-left: 1px solid var(--rule);
    padding-left: clamp(1rem, 2vw, 1.5rem);
  }
}

@media (max-width: 760px) {
  body { font-size: 1rem; }

  /* El descriptor parte la marca en dos líneas y revienta el alto de
     la cabecera. En móvil basta con el logotipo. */
  .brand__sub { display: none; }

  .hero { min-height: 0; gap: 3.5rem; }
  /* En una sola columna no hay nada que separar en horizontal: el
     filete vertical quedaría como una línea suelta. */
  .hero__meta { grid-template-columns: 1fr; }
  .hero__meta li { padding: 1rem 0; }
  .hero__meta li:nth-child(even) { border-left: 0; padding-left: 0; }
  .hero__cta .btn { flex: 1 1 100%; justify-content: center; }

  .chal { grid-template-columns: 1fr; }

  /* La línea de tiempo se vuelve vertical: seis columnas en un móvil
     dejan cada etapa en 50px y el texto ilegible. El conector
     horizontal desaparece porque ya no une nada. */
  .tl__line { display: none; }
  .tl__steps { grid-template-columns: 1fr; gap: 1.5rem; }
  .tl__i {
    display: grid;
    grid-template-columns: 3rem minmax(0, 1fr);
    column-gap: 1rem;
    align-items: start;
  }
  .tl__dot { grid-row: 1 / 4; }
  .tl__n, .tl__t, .tl__d { margin-top: 0; grid-column: 2; }
  .tl__t { margin-top: .15rem; }
  .tl__d { margin-top: .3rem; }

  .svc__cols { grid-template-columns: 1fr; }
  .svc__head { grid-template-columns: 1fr; }
  /* El embudo apilado: la silueta arriba, los datos debajo. A dos
     columnas en móvil el SVG queda en 150px y no se lee nada. */
  .fnl { grid-template-columns: 1fr; }

  /* El radial NO cabe en móvil: medido a 390px, un nodo se salía del
     contenedor y dos se montaban sobre el centro. Aquí se convierte en
     lista apilada, con la descripción ya visible —que en escritorio
     solo la oye el lector de pantalla—. La telaraña sobra. */
  .rad { aspect-ratio: auto; width: 100%; }
  .rad__web { display: none; }
  .rad__core {
    position: static;
    transform: none;
    width: auto;
    text-align: left;
    padding-bottom: .625rem;
    margin-bottom: .25rem;
    border-bottom: 1px solid var(--rule);
    font-size: .625rem;
    letter-spacing: .16em;
    text-transform: uppercase;
    color: var(--teal-deep);
  }
  /* .rad.is-in .rad__n del bloque no-preference tiene más
     especificidad (0,2,1) y volvía a imponer el translate del radial.
     Hay que anularlo con el mismo selector, no solo con .rad__n. */
  .rad.is-in .rad__n,
  .rad__n {
    position: static;
    transform: none;
    display: block;
    width: 100%;
    max-width: none;
    text-align: left;
    border-radius: .5rem;
    padding: .75rem .875rem;
    margin-bottom: .5rem;
    font-size: .875rem;
    color: var(--ink);
  }
  .rad__d {
    position: static;
    width: auto; height: auto;
    margin: .25rem 0 0;
    clip-path: none;
    white-space: normal;
    display: block;
    font-size: .8125rem;
    font-weight: 400;
    line-height: 1.5;
    color: var(--ink-2);
  }
  .rad__n:hover .rad__d,
  .rad__n:focus-visible .rad__d { color: var(--bg-dark); }
  .grid4  { grid-template-columns: 1fr; }
  .mocks  { grid-template-columns: 1fr; }
  /* Las dos superficies del visual SEO se apilan: a 170px de ancho
     cada caja, las barras no comunican nada. */
  .geo    { grid-template-columns: 1fr; }
  .flw__ns { font-size: .625rem; }

  /* El texto dentro de un SVG escala con el viewBox, no con la raíz:
     en un móvil de 390px la meseta se dibuja a 0.73 y la matriz a
     0.79, así que un rótulo de 9px acaba renderizado a 6.5px reales
     —ilegible—. Aquí se compensa la escala para que TODOS caigan
     alrededor de 10-11px de pantalla.

     Si algún día cambia el viewBox de uno de estos gráficos, hay que
     recalcular su factor o el rótulo volverá a encogerse. */
  .stall__lb { font-size: 14px; }
  .stall__gl { font-size: 13px; }
  .mx__ql    { font-size: 12px; }
  .mx__at    { font-size: 12px; }
  .mx__bl    { font-size: 15px; }
  .arq__pl   { font-size: 14px; }
  .lyr__t    { font-size: 13px; }
  .kpis   { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .feat__l { columns: 1; }

  .form__row { grid-template-columns: 1fr; }

  .ftr__top { grid-template-columns: 1fr; }
  .ftr__cols { grid-template-columns: repeat(2, 1fr); gap: 2.5rem 1.5rem; }
}

@media (max-width: 420px) {
  .ftr__cols { grid-template-columns: 1fr; }
}

/* ── Movimiento reducido ────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
  .rv, .proc__c { opacity: 1; transform: none; }
  .mask .ln > i { transform: none; }
  .proc::after { transform: scaleY(1); }

  /* Estado final de los visuales de la fase 4. La regla `*` de arriba
     deja las transiciones en .01ms, pero eso las hace instantáneas, no
     inexistentes: un elemento que arranca en opacity:0 seguiría
     necesitando que el observador le ponga .is-in. Aquí se les da
     directamente el estado final, así que se ven aunque el observador
     no llegue nunca. */
  .fnl__b   { clip-path: none; }
  .fnl__r,
  .rad__n,
  .stall__gap, .stall__gl, .stall__pin,
  .tl__i    { opacity: 1; }
  .stall__now  { stroke-dasharray: none; stroke-dashoffset: 0; }
  .stall__pot  { opacity: .75; }
  .stall__lead { opacity: .5; }
  .proc__seg   { transform: none; }
  .proc__gate  { opacity: 1; }
  .mx__b, .mx__bl { opacity: 1; }
  .arq__x { opacity: 1; }
  .upt__b { opacity: 1; }
  .rad__n   { transform: translate(-50%, -50%); }
  .tl__i    { transform: none; }
  .dash__line { stroke-dasharray: none; stroke-dashoffset: 0; }
  /* El punto que recorre el flujo desaparece: es puro movimiento y sin
     él la ilustración sigue leyéndose. */
  .flw__dot { animation: none; opacity: 0; }
  .mock:hover .mock__img { transform: none; }

  /* La atmósfera se queda: lo que se detiene es su deriva. El color
     no es decoración prescindible, la animación sí. */
  body::before, body::after, .aura span { animation: none; }
}
