/* ============================================================================
   weBBlanca — réplica del lenguaje visual de hobro.digital (fase 1)
   Sistema extraído del sitio real: display grotesca 900 UPPERCASE lh .84,
   serif itálica ligera como acento, mono uppercase para labels, lienzo
   blanco + sección negra invertida, acentos neón/rosa, radios píldora,
   smooth-scroll + cursor custom + marquees + casos apilados.

   Fuentes sustitutas (las de Hobro son comerciales):
   Kamerik205→Archivo 900 · PPNeueMontreal→Instrument Sans ·
   FreigBigProLigIta→Instrument Serif italic · AkkuratMono→IBM Plex Mono.

   REGLA DURA: cero literales de color/sombra/radio fuera de los tokens.
   ========================================================================== */

/* ─── 1. Tokens ──────────────────────────────────────────────────────────── */

:root {
  --canvas: #FFFFFF;
  --ink: #000000;
  --ink-soft: #151515;
  --body-c: #1A1A1A;
  --muted: #636262;
  --hairline: #D5D4D4;
  --hairline-dark: #4C4C4C;
  --neon: #00FB96;
  --magenta: #FF3B93;
  --aqua: #56FFE2;
  /* Los antiguos tonos mate (#E6B0B0 / #DBDCCA) se retiraron el 2026-09-08:
     --pink y --sage quedan como alias de los neones para no reescribir las
     ~15 reglas que los usan. */
  --pink: var(--magenta);
  --sage: var(--aqua);
  --on-ink: #FFFFFF;

  --font-display: "Archivo", "Arial Black", sans-serif;
  --font-body: "Instrument Sans", "Helvetica Neue", Arial, sans-serif;
  --font-cursive: "Instrument Serif", Georgia, serif;
  --font-mono: "IBM Plex Mono", ui-monospace, monospace;

  --fs-display: clamp(2.7rem, 7.6vw, 8.5rem);   /* hero / footer wordmark */
  --fs-hero: clamp(2.5rem, 12.8vw, 14.5rem);    /* wordmark del hero */
  --fs-title: clamp(2.2rem, 5.8vw, 6.2rem);     /* títulos de sección */
  --fs-cursive: clamp(1.9rem, 4.2vw, 3.6rem);   /* acento serif itálica */
  --fs-list: clamp(1.15rem, 1.9vw, 1.5rem);     /* listas uppercase 22px */
  --fs-row: clamp(1.6rem, 3.4vw, 3.2rem);       /* filas grandes hover */
  --fs-body: 1rem;
  --fs-mono: 0.875rem;
  --fs-mono-sm: 0.75rem;

  --gutter: clamp(1rem, 2vw, 2rem);
  --section: clamp(3rem, 6.5vw, 5.5rem); /* (2026-09-08) a la mitad: había demasiado aire entre secciones */
  --nav-h: 60px;

  --r-chip: 33px;
  --r-card: 26px;
  --r-media: 20px;
  --r-sm: 6px;
  --r-round: 50%;

  --shadow-pop: 0 30px 80px rgba(0, 0, 0, 0.25);

  --ease-hobro: cubic-bezier(0.785, 0.135, 0.15, 0.86);
  --ease-out: cubic-bezier(0.34, 0, 0.3, 1);
  --dur-1: 0.4s; --dur-2: 0.8s; --dur-3: 1.2s;
}

/* ─── 2. Reset + base ────────────────────────────────────────────────────── */

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

/* El smooth lo hace nuestro lerp de rueda (main.js) — con scroll-behavior
   nativo activo, cada scrollTo del bucle se re-anima y el scroll se atasca. */
html { scroll-behavior: auto; }

/* Oculta la barra de scroll nativa (15px) en todo el sitio, a petición del
   dueño — el scroll SIGUE funcionando exactamente igual (rueda, trackpad,
   teclado, arrastre táctil): esto solo quita el indicador visual, no
   `overflow:hidden` ni nada que bloquee el scroll. `scrollbar-width:none`
   cubre Firefox; `::-webkit-scrollbar{display:none}` cubre
   Chrome/Safari/Edge. Se aplica a html Y body porque según el modo de
   render el navegador pone el scrollbar del documento en uno u otro. */
/* `overflow-x: clip` también en la raíz, no solo en body: en quienes-somos.html
   la página se dejaba arrastrar 18px a los lados aunque ningún elemento del
   contenido pasara del borde derecho (medido barriendo 6 posiciones de
   scroll). `clip` —a diferencia de `hidden`— NO crea un contenedor de scroll,
   así que los `position: sticky` de `.qsline` y `.xcards` siguen funcionando. */
html { overflow-x: clip; }
html, body { scrollbar-width: none; }
html::-webkit-scrollbar, body::-webkit-scrollbar { display: none; width: 0; height: 0; }

body {
  margin: 0;
  background: var(--canvas);
  color: var(--body-c);
  font-family: var(--font-body);
  font-size: var(--fs-body);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  overflow-x: clip;
}

img, video, svg { max-width: 100%; display: block; }
h1, h2, h3, h4, p, ul, ol, figure, blockquote { margin: 0; }
ul, ol { padding: 0; list-style: none; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: 0; padding: 0; cursor: pointer; }
table { border-collapse: collapse; width: 100%; }

:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }
.s-dark :focus-visible { outline-color: var(--on-ink); }

::selection { background: var(--neon); color: var(--ink); }

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.skip-link {
  position: fixed; top: 10px; left: 10px; z-index: 300;
  background: var(--ink); color: var(--on-ink);
  padding: 0.5rem 1rem; border-radius: var(--r-chip);
  transform: translateY(-300%); transition: transform var(--dur-1) var(--ease-out);
}
.skip-link:focus-visible { transform: none; }

.container { padding-inline: var(--gutter); max-width: 1800px; margin-inline: auto; }

/* Tipos base del sistema */
.t-display {
  font-family: var(--font-display);
  font-weight: 900;
  text-transform: uppercase;
  line-height: 0.86;
  letter-spacing: -0.01em;
}
.t-cursive {
  font-family: var(--font-cursive);
  font-style: italic;
  font-weight: 400;
  text-transform: uppercase;
  line-height: 0.9;
  letter-spacing: -0.01em;
}
.t-mono {
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.02em;
}
.t-list {
  font-family: var(--font-body);
  font-weight: 500;
  font-size: var(--fs-list);
  text-transform: uppercase;
  letter-spacing: -0.015em;
  line-height: 1.2;
}

/* Sección negra invertida */
.s-dark { background: var(--ink); color: var(--on-ink); }

/* ─── 3. Preloader ───────────────────────────────────────────────────────── */

.loader {
  position: fixed; inset: 0; z-index: 400;
  background: var(--ink); color: var(--on-ink);
  display: flex; align-items: center; justify-content: center;
  transition: transform 0.9s var(--ease-hobro), visibility 0s linear 0.9s;
}
.loader__word { font-size: clamp(2rem, 6vw, 4.5rem); overflow: clip; }
.loader__word span { display: inline-block; animation: loader-rise 0.8s var(--ease-out) both 0.1s; }
.is-loaded .loader { transform: translateY(-100%); visibility: hidden; }
@keyframes loader-rise { from { transform: translateY(110%); } }
@media (prefers-reduced-motion: reduce) { .loader { display: none; } }

/* ─── 4. Cursor custom ───────────────────────────────────────────────────── */

.cursor {
  position: fixed; top: 0; left: 0; z-index: 350;
  mix-blend-mode: difference;
  pointer-events: none;
  transform: translate(-100px, -100px);
}
.cursor__inner {
  display: grid; place-items: center;
  transform: translate(-50%, -50%);
  transition: width var(--dur-1) var(--ease-out), height var(--dur-1) var(--ease-out), border-radius var(--dur-1) var(--ease-out), transform var(--dur-1) var(--ease-out), opacity 0.2s var(--ease-out);
}
.cursor--dot .cursor__inner { width: 6px; height: 6px; border-radius: var(--r-round); background: var(--canvas); }
.cursor--ring .cursor__inner { width: 36px; height: 36px; border-radius: var(--r-round); border: 1px solid var(--canvas); }
.cursor--dot.is-hidden .cursor__inner { opacity: 0; }
.cursor--ring.is-grown .cursor__inner { transform: translate(-50%, -50%) scale(1.5); }
.cursor--ring.is-label .cursor__inner { height: 72px; background: var(--canvas); border-color: var(--canvas); padding: 0 14px; box-sizing: border-box; }
.cursor__label {
  font-family: var(--font-mono); font-size: 11px; font-weight: 500;
  letter-spacing: 0.04em;
  text-transform: uppercase; color: var(--ink);
  opacity: 0; transition: opacity var(--dur-1) var(--ease-out);
  white-space: nowrap; overflow: visible;
}
.cursor--ring.is-label .cursor__label { opacity: 1; }
@media (hover: none), (prefers-reduced-motion: reduce) { .cursor { display: none; } }

/* ─── 5. Header ──────────────────────────────────────────────────────────── */

.header {
  position: fixed; top: 0; left: 0; right: 0; z-index: 200;
  height: var(--nav-h);
  mix-blend-mode: difference;
  color: var(--on-ink);
}
.header__inner {
  height: 100%;
  display: flex; align-items: center; justify-content: space-between; gap: 1.5rem;
}
.header__logo { font-family: var(--font-display); font-weight: 900; font-size: 1.2rem; text-transform: uppercase; letter-spacing: -0.01em; }
.header__links { display: flex; gap: 1.75rem; }
.header__links a { position: relative; padding-block: 0.4rem; }
.header__links a::after {
  content: ""; position: absolute; left: 0; right: 100%; bottom: 0;
  height: 1px; background: currentColor;
  transition: right var(--dur-1) var(--ease-out);
}
.header__links a:hover::after { right: 0; }
.header__cta {
  border: 1px solid currentColor; border-radius: var(--r-chip);
  padding: 0.5rem 1.15rem;
  transition: background var(--dur-1) var(--ease-out), color var(--dur-1) var(--ease-out);
}
.header__cta:hover { background: var(--on-ink); color: var(--ink); }
.header__burger { display: none; width: 44px; height: 44px; align-items: center; justify-content: center; }

.sheet {
  position: fixed; inset: 0; z-index: 190;
  background: var(--ink); color: var(--on-ink);
  display: flex; flex-direction: column; justify-content: center; gap: 2rem;
  padding: var(--gutter);
  opacity: 0; visibility: hidden;
  transition: opacity var(--dur-1) var(--ease-out), visibility 0s linear var(--dur-1);
}
.sheet.is-open { opacity: 1; visibility: visible; transition-delay: 0s; }
.sheet__links { display: flex; flex-direction: column; gap: 0.6rem; }
.sheet__links a { font-family: var(--font-display); font-weight: 900; text-transform: uppercase; font-size: clamp(2rem, 8vw, 3.2rem); line-height: 1; }
.sheet__meta { display: flex; flex-direction: column; gap: 0.35rem; color: var(--hairline); }

@media (max-width: 900px) {
  .header__links, .header__cta { display: none; }
  .header__burger { display: inline-flex; }
}

/* ─── 6. Hero (about) ────────────────────────────────────────────────────── */

.hero {
  position: relative;
  min-height: 100svh;
  display: flex; flex-direction: column; justify-content: flex-end;
  color: var(--on-ink);
  overflow: clip;
  padding-block: calc(var(--nav-h) + 2rem) clamp(1.5rem, 4vh, 3rem);
}
.hero__video, .hero__cover {
  position: absolute; inset: 0; width: 100%; height: 100%;
}
.hero__video { object-fit: cover; }
.hero__cover { background: linear-gradient(180deg, rgba(0, 0, 0, 0.25), rgba(0, 0, 0, 0.55)); }

.hero__inner { position: relative; flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: clamp(1.25rem, 3vh, 2rem); }
.hero__title { font-size: var(--fs-hero); max-width: none; white-space: nowrap; letter-spacing: -0.02em; text-align: center; }
.hero__id { text-align: center; display: flex; flex-direction: column; gap: 0.35rem; }
.hero__id-meta { color: var(--hairline); font-size: var(--fs-mono-sm); }
.hero__bottom { display: flex; justify-content: center; margin-top: auto; }
.hero__sub {
  display: flex; align-items: center; justify-content: center; flex-wrap: wrap;
  column-gap: 0; row-gap: 0.5rem;
  max-width: none; white-space: nowrap;
  text-transform: uppercase; letter-spacing: 0.06em;
  font-size: var(--fs-mono-sm); line-height: 1.35; text-align: center;
}
.hero__sub span { display: inline-flex; align-items: center; }
.hero__sub span + span::before { content: "●"; color: var(--neon); font-size: 0.45em; margin-inline: 1.4em; }

/* Palabras que suben (split) — reversible: al perder .is-in bajan de nuevo.
   El padding/margen negativo expande la ventana de clip por encima de la
   caja de línea (0.86–0.9 lh) para no cortar ascendentes/descendentes ni
   tildes de los glifos reales; el margen negativo anula el impacto en layout. */
.w {
  display: inline-block; overflow: clip; vertical-align: top;
  padding: 0.18em 0.09em 0.24em;
  margin: -0.18em -0.09em -0.24em;
}
.wi { display: inline-block; transform: translateY(160%); transition: transform 0.7s var(--ease-hobro); }
.is-in .wi, .wi.is-in { transform: none; transition: transform 0.9s var(--ease-hobro); transition-delay: var(--wd, 0s); }
@media (prefers-reduced-motion: reduce) {
  .wi { transform: none !important; transition: none !important; }
  .wi--chip { opacity: 1 !important; scale: 1 !important; rotate: 0deg !important; transition: none !important; }
}

/* Chips arrastrables envueltos por el split ([data-nosplit]): no llevan
   máscara (deben poder salir de su celda al arrastrarse) y aparecen con un
   pop de escala/rotación en vez de subir desde el borde inferior.
   Sin uso desde 2026-09-08: los chips del statement salieron del split
   (ver .statement__chips en §7); no queda ningún [data-nosplit] en el repo. */
.w--chip { overflow: visible; }
.wi--chip { transform: none; opacity: 0; scale: 0.6; rotate: 6deg; transition: opacity 0.5s var(--ease-out), scale 0.5s var(--ease-out), rotate 0.5s var(--ease-out); }
.is-in .wi--chip, .wi--chip.is-in { opacity: 1; scale: 1; rotate: 0deg; transition: opacity 0.5s var(--ease-out), scale 0.5s var(--ease-out), rotate 0.5s var(--ease-out); transition-delay: var(--wd, 0s); }

/* Scrub por palabra ([data-scrub], ver §10 initScrollFX): el progreso lo
   marca el scroll, no --wd, y 0.9s/0.5s serían lentos frente a la mano. */
[data-scrub] .wi { transition-duration: 0.45s; transition-delay: 0s; }
[data-scrub] .wi--chip { transition-duration: 0.45s; transition-delay: 0s; }

/* Reveal genérico — reversible */
.reveal { opacity: 0; transform: translateY(30px); transition: opacity var(--dur-2) var(--ease-out), transform var(--dur-2) var(--ease-out); }
.reveal.is-in { opacity: 1; transform: none; transition-delay: var(--d, 0s); }
@media (prefers-reduced-motion: reduce) { .reveal { opacity: 1; transform: none; transition: none; } }

/* ─── 7. Statement (stickers) ────────────────────────────────────────────── */

.statement { padding-block: var(--section); }
.statement__label { color: var(--muted); margin-bottom: 3rem; display: flex; align-items: center; gap: 1rem; }
.statement__label::after { content: ""; flex: 1; height: 1px; background: var(--hairline); }
.statement__row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: clamp(1.5rem, 4vw, 3.5rem);
  align-items: center;
}
.statement__text {
  font-size: var(--fs-title);
  color: var(--ink);
  max-width: 24ch;
  word-spacing: 0.22em;
}
.statement__text .t-cursive { color: var(--ink); }

/* Columna de chips, ya fuera del titular (2026-09-08): la anota una nota
   "arrástrame" y los apila con un ligero escalonado horizontal para que no
   lean como una lista. aria-hidden porque repiten datos que ya dice
   .statement__para. */
.statement__chips {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.9rem;
  padding-top: 0.5rem;
}
.statement__chips .chip:nth-child(2) { margin-left: 1.5rem; }
.statement__chips .chip:nth-child(4) { margin-left: 0.5rem; }
.statement__chips-note {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  color: var(--muted);
  font-size: var(--fs-mono-sm);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin-bottom: 0.25rem;
}
.statement__chips-arrow { width: 1.1rem; height: auto; color: var(--muted); margin-top: 0.35rem; align-self: flex-start; }

.chip {
  --tilt: -3deg;
  display: inline-flex; align-items: center; gap: 0.5rem;
  font-family: var(--font-mono); font-size: clamp(0.8rem, 1.4vw, 1.05rem); font-weight: 500;
  background: var(--neon); color: var(--ink);
  border-radius: var(--r-chip);
  padding: 0.45em 1em;
  vertical-align: middle;
  transform: translate(var(--dx, 0px), var(--dy, 0px)) rotate(var(--tilt));
  white-space: nowrap;
}
.chip--pink { background: var(--pink); --tilt: 2.5deg; }
.chip--sage { background: var(--sage); --tilt: -1.5deg; }
.chip[data-drag] { cursor: grab; touch-action: none; user-select: none; position: relative; z-index: 40; }
.chip[data-drag]:hover { animation: chip-wobble 0.5s var(--ease-out); }
.chip.is-dragging { cursor: grabbing; animation: none; position: relative; z-index: 60; box-shadow: var(--shadow-pop); }
@keyframes chip-wobble {
  25% { transform: translate(var(--dx, 0px), var(--dy, 0px)) rotate(calc(var(--tilt) + 3deg)); }
  60% { transform: translate(var(--dx, 0px), var(--dy, 0px)) rotate(calc(var(--tilt) - 2deg)); }
}

.statement__para {
  margin-top: 4rem;
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.2fr); gap: 2rem;
}
.statement__para-label { color: var(--muted); }
.statement__para p { max-width: 52ch; font-size: clamp(1.05rem, 1.5vw, 1.35rem); line-height: 1.5; }

.statement__words {
  margin-top: var(--section);
  font-size: var(--fs-title);
  color: var(--ink);
  display: flex; flex-wrap: wrap; column-gap: 1.5rem;
  overflow-x: clip; overflow-y: visible;
}
.statement__words > span { display: inline-block; will-change: transform; }
.statement__words .t-cursive { color: var(--muted); }
@media (max-width: 800px) { .statement__para { grid-template-columns: 1fr; } }
@media (max-width: 800px) {
  .statement__row { grid-template-columns: 1fr; gap: 1.5rem; }
  .statement__chips { flex-direction: row; flex-wrap: wrap; gap: 0.6rem 0.75rem; }
  .statement__chips .chip:nth-child(n) { margin-left: 0; }
  .statement__chips-note { width: 100%; }
}

/* ─── 8. Marquee CTA (Got Project? START) ────────────────────────────────── */

.cta-marquee {
  display: block;
  border-block: 1px solid var(--hairline);
  padding-block: 1.4rem;
  overflow: clip;
  white-space: nowrap;
  background: var(--canvas); color: var(--ink);
  transition: background var(--dur-1) var(--ease-out), color var(--dur-1) var(--ease-out);
}
.cta-marquee:hover { background: var(--ink); color: var(--on-ink); }
.cta-marquee--inverse { background: var(--ink); color: var(--on-ink); border-color: var(--hairline-dark); }
.cta-marquee--inverse:hover { background: var(--neon); color: var(--ink); }
.cta-marquee__track {
  display: inline-flex; align-items: baseline; gap: 2.5rem; padding-right: 2.5rem;
  animation: marquee var(--marquee-dur, 24s) linear infinite;
}
.cta-marquee:hover .cta-marquee__track { animation-play-state: paused; }
.cta-marquee__big { font-size: clamp(1.6rem, 3.2vw, 2.6rem); }
.cta-marquee__cursive { font-size: clamp(1.6rem, 3.2vw, 2.6rem); }
.cta-marquee__arrow { font-family: var(--font-mono); font-size: clamp(1.2rem, 2.4vw, 1.8rem); }
@keyframes marquee { to { transform: translateX(-100%); } }
@media (prefers-reduced-motion: reduce) { .cta-marquee__track { animation: none; } }

/* ─── 9. Cabecera de sección (mono + título) ─────────────────────────────── */

.sec { padding-block: var(--section); }
.sec__head { display: block; margin-bottom: clamp(2.5rem, 6vw, 5rem); }
.sec__label { color: var(--muted); display: flex; align-items: center; gap: 0.75rem; margin-bottom: clamp(1.25rem, 3vw, 2rem); }
.sec__label::before { content: "●"; font-size: 0.6em; color: var(--neon); }
.s-dark .sec__label { color: var(--hairline); }

/* ─── 9b. Título .mark: fantasma serif + sólida grotesca (patrón Hobro) ──── */
/* Dos capas superpuestas: fantasma en contorno (serif itálica, ENORME,
   sangrando por la izquierda, z-index alto) sobre sólida (grotesca 900,
   empujada a la derecha). El .mark recorta con overflow y reserva alto
   para el fantasma; ambas capas reciben su transform de initScrollFX (§10,
   ver [data-slide] en main.js) — el desplazamiento de entrada ES el reveal,
   por eso .sec__head ya no lleva .reveal. */
.mark {
  position: relative;
  overflow-x: clip; overflow-y: visible;
  min-height: clamp(4.6rem, 14vw, 16rem);
}
.mark__ghost {
  position: absolute; left: -3%; top: 0.14em; z-index: 2;
  font-size: clamp(3.6rem, 12.5vw, 14.5rem); line-height: 0.88;
  color: var(--canvas); -webkit-text-stroke: 1px var(--ink);
  pointer-events: none; user-select: none;
  white-space: nowrap; will-change: transform;
}
.mark__solid {
  display: block; position: relative; z-index: 1;
  font-size: clamp(3rem, 9.6vw, 10.8rem); line-height: 0.88;
  margin-left: clamp(2.5rem, 22vw, 26rem);
  will-change: transform;
}
.s-dark .mark__ghost { color: var(--ink); -webkit-text-stroke-color: var(--on-ink); }
@media (max-width: 800px) { .mark__solid { margin-left: clamp(1.5rem, 12vw, 4rem); font-size: clamp(2.5rem, 9.6vw, 10.8rem); } .mark__ghost { left: -1%; } }
/* Caso ácido: "herramientas" (12 glifos) es más larga que el resto de sólidas
   y desborda su caja bajo el mismo clamp compartido a cualquier ancho; clamp
   propio (más bajo, misma curva fluida) solo para esta instancia — el resto
   de .mark__solid no se ve afectado. */
#integraciones .mark__solid { font-size: clamp(2rem, 8vw, 9rem); }
/* Mismo caso ácido para "letra pequeña." (14 glifos con espacio): más ancha
   aún que "herramientas" (medido con canvas measureText, ~8% más) y desborda
   .mark bajo el clamp compartido — mismo tratamiento reducido. */
#how-title .mark__solid { font-size: clamp(2rem, 8vw, 9rem); }
/* (01) «Miran la web / antes de llamarte.» — 2026-09-08: la sólida tiene 18
   caracteres (Archivo 900 uppercase) y con el clamp compartido desbordaría
   .mark; tamaño propio + data-indent="25" en el HTML (la sólida arranca al
   25% de la fantasma, no al 58%) para que el conjunto quepa en .container.
   Medido en Chrome a 1440/1280/1024/768/390 — ver CLAUDE.md § Lote 2026-09-08. */
#problem-title .mark__solid { font-size: clamp(1.9rem, 6vw, 6.6rem); }

/* ─── 10. El problema — filas grandes ────────────────────────────────────── */

.rows { border-top: 1px solid var(--hairline); }
.rows__item {
  display: grid; grid-template-columns: 5rem 1fr; gap: 1.5rem;
  align-items: baseline;
  padding-block: clamp(1.25rem, 3vw, 2rem);
  border-bottom: 1px solid var(--hairline);
}
.rows__num { color: var(--muted); }
.rows__claim { font-size: var(--fs-row); font-family: var(--font-display); font-weight: 900; text-transform: uppercase; line-height: 0.95; letter-spacing: -0.01em; color: var(--ink); }
.rows__detail { grid-column: 2; margin-top: 0.5rem; color: var(--muted); max-width: 54ch; }
.rows__close { margin-top: 3rem; font-size: clamp(1.1rem, 1.8vw, 1.4rem); max-width: 44ch; }
.rows__close strong { background: var(--neon); padding: 0.05em 0.25em; }

/* ─── 12. Approach (01-04 pilares) → mazo lateral sticky, 100% CSS ────────── */
/* Mismo mecanismo puro-CSS que .pcard (§26, "Cómo trabajamos contigo"), pero
   el montaje es lateral en vez de vertical: la columna sigue siendo
   vertical (.xcard es un flex item más de .xcards), con las cuatro
   tarjetas ancladas al mismo `top` (position:sticky) — al bajar, cada una
   se clava y la siguiente se le monta encima, exactamente como .pcard.
   Lo lateral lo da un desplazamiento horizontal FIJO por nth-child (no
   ligado al scroll, cero JS): ancho 79% + margin-left 21%/14%/7%/0% —
   escalón de 7% del ancho del contenedor por tarjeta (79+21=100: la
   primera nace pegada al borde derecho; la última, sin margen, pegada al
   izquierdo). Como cada tarjeta es posterior en el DOM, se pinta encima
   de la anterior por orden normal de apilamiento — el "mazo" se arma de
   derecha a izquierda sin transform ni z-index. Contenido completo
   siempre visible (métrica + tag + nombre + descripción): nada de
   acordeón ni opacity:0 ocupando sitio, que fue el bug de la versión con
   scroll horizontal que sustituye este bloque. */
.xcards {
  display: flex;
  flex-direction: column;
  gap: clamp(1.5rem, 4vw, 3rem);
}

.xcard {
  position: sticky;
  top: calc(var(--nav-h) + 1.5rem);
  width: 79%;
  min-height: 20rem;
  overflow: clip;
  border-radius: var(--r-media);
  box-shadow: var(--shadow-pop);
  padding: clamp(1.25rem, 2.5vw, 2rem);
  display: flex; flex-direction: column; gap: 1rem;
}
.xcard:nth-child(1) { margin-left: 21%; }
.xcard:nth-child(2) { margin-left: 14%; }
.xcard:nth-child(3) { margin-left: 7%; }
.xcard:nth-child(4) { margin-left: 0; }

.xcard--neon { background: var(--neon); color: var(--ink); }
.xcard--magenta { background: var(--magenta); color: var(--ink); }
.xcard--aqua { background: var(--aqua); color: var(--ink); }
.xcard--ink { background: var(--ink); color: var(--on-ink); }

.xcard__metric {
  font-size: clamp(2rem, 4vw, 3.4rem);
  line-height: 0.95;
}

.xcard__body { margin-top: 0; }

/* EXPERIMENTO DE ESTILO (petición explícita de Gonzalo, "por probar") ─────
   .xcard__name pasa de grotesca sólida a la misma familia visual que las
   palabras fantasma de los títulos de sección (.mark__ghost, §9b): serif
   itálica en outline. Aquí no hay una capa sólida detrás que tapar (como sí
   la hay en .mark), así que el relleno transparente basta para dejar ver
   directamente el color de fondo de la tarjeta a través del glifo — no hace
   falta igualar un color por variante. El trazo usa currentColor (no un
   token fijo) para heredar el color de texto que ya define cada .xcard/
   .xcard--* (var(--ink) en neon/magenta/aqua, var(--on-ink) en .xcard--ink),
   así funciona igual en las cuatro sin escribir 4 reglas.
   Para revertir a la grotesca sólida de antes: sustituir todo este bloque
   por font-family:var(--font-display); font-weight:900; text-transform:
   uppercase; line-height:0.95; sin -webkit-text-fill-color ni
   -webkit-text-stroke.
   line-height:1.08 (no 0.95, el valor de la grotesca que reemplaza): medido
   con canvas measureText/actualBoundingBox*, "Como tú la quieres" y "Tú no
   trabajas" (tilde + descendentes de "q"/"j") suman ~42px de tinta real a
   43.52px de font-size — con 0.95 (41.3px de caja) la tinta ya excedía la
   caja de línea en ~1px. 1.08 (~47px de caja) deja margen real. */
.xcard__name {
  font-size: var(--fs-row); font-family: var(--font-cursive); font-style: italic;
  font-weight: 400; text-transform: uppercase; line-height: 1.08; letter-spacing: -0.01em;
  margin-bottom: 0.6rem;
  -webkit-text-fill-color: transparent;
  -webkit-text-stroke: 1px currentColor;
}
.xcard__desc { max-width: 34ch; }

/* Entrada de folios (tarjetas 2-4 de .xcards, ver comentario junto al HTML):
   la 1ª (.xcard--neon) no lleva .reveal y nace fija — es la base del montón.
   Las otras reusan el IO único de .reveal (JS §5, banda -8%/-8%) pero con SU
   PROPIO transform de partida en vez del translateY(30px) genérico:
   desplazamiento -48px + rotación ±2.5/3deg (signo alternado para que no
   parezcan clonadas), asentando en transform:none con var(--ease-hobro) para
   que frene al posarse en vez de la ease-out estándar de .reveal. El
   transform vive en la propia .xcard (el elemento sticky), NUNCA en un
   contenedor por encima — un transform en un ANCESTRO de un position:sticky
   le crea un containing block nuevo y rompe el sticky.
   Sombra reforzada mientras "vuela" apilando DOS veces el mismo token
   --shadow-pop (cero literal nuevo) — se funde a una sola capa (la de
   siempre) al posarse, vendiendo el aterrizaje del folio. */
.xcard.reveal {
  transition: opacity var(--dur-2) var(--ease-hobro), transform var(--dur-2) var(--ease-hobro), box-shadow var(--dur-2) var(--ease-hobro);
}
.xcard.reveal:not(.is-in) { box-shadow: var(--shadow-pop), var(--shadow-pop); }
.xcard:nth-child(2).reveal:not(.is-in) { transform: translateX(-48px) rotate(-2.5deg); }
.xcard:nth-child(3).reveal:not(.is-in) { transform: translateX(-48px) rotate(2.5deg); }
.xcard:nth-child(4).reveal:not(.is-in) { transform: translateX(-48px) rotate(-3deg); }
@media (prefers-reduced-motion: reduce) {
  .xcard:nth-child(2).reveal,
  .xcard:nth-child(3).reveal,
  .xcard:nth-child(4).reveal { transform: none; box-shadow: var(--shadow-pop); transition: none; }
}

/* Fallback obligatorio ≤800px, igual que .pcard (§26): sin sticky ni
   desplazamiento lateral, apiladas en vertical normal. Es CSS puro (no hay
   JS que desactivar ni condición de prefers-reduced-motion que cubrir: el
   mazo no anima nada, solo apila por scroll nativo). !important porque los
   .xcard:nth-child(n) de arriba tienen más especificidad que un .xcard
   suelto — sin esto el margin-left decreciente sobrevive por debajo de
   800px y desborda el viewport (mismo patrón !important que ya usaba el
   fallback anterior de .xcards__track). */
@media (max-width: 800px) {
  .xcard {
    position: relative !important;
    top: 0 !important;
    width: 100% !important;
    margin-left: 0 !important;
    min-height: 0;
  }
}

/* ─── 13. CTA «¿Tienes proyecto?» — swap por letras + paréntesis ─────────── */
/* (antes: banda capabilities/boceto gratis, sustituida en Chat 6) */

/* Botón píldora — componente compartido (tiers, footer, aims, no solo aquí) */
.btn-pill {
  display: inline-flex; align-items: center; gap: 0.6rem;
  font-family: var(--font-mono); font-size: var(--fs-mono); font-weight: 500; text-transform: uppercase;
  border: 1px solid var(--ink); border-radius: var(--r-chip);
  padding: 0.9em 1.8em;
  transition: background var(--dur-1) var(--ease-out), color var(--dur-1) var(--ease-out), transform var(--dur-1) var(--ease-out);
}
.btn-pill:hover { background: var(--ink); color: var(--on-ink); transform: translateY(-2px); }
.btn-pill--solid { background: var(--ink); color: var(--on-ink); }
.btn-pill--solid:hover { background: var(--neon); color: var(--ink); border-color: var(--neon); }
.btn-pill--neon { background: var(--neon); border-color: var(--neon); color: var(--ink); }
.btn-pill--neon:hover { background: var(--ink); color: var(--on-ink); border-color: var(--ink); }
.s-dark .btn-pill { border-color: var(--on-ink); color: var(--on-ink); }
.s-dark .btn-pill:hover { background: var(--on-ink); color: var(--ink); }
.s-dark .btn-pill--neon { background: var(--neon); border-color: var(--neon); color: var(--ink); }
.s-dark .btn-pill--neon:hover { background: var(--on-ink); border-color: var(--on-ink); }

/* Banda CTA — pantalla completa (100vh/100svh), contenido centrado en ambos
   ejes. Sigue a (04) sin costura: mismo --ink que .aims (s-dark en ambos),
   cero borde/margen entre las dos — la continuidad ya no depende de que el
   padding superior de aquí iguale al padding inferior de .aims (como antes),
   sino de que ambas comparten fondo y son adyacentes; con la sección a
   100vh casi toda esa altura es aire negro por debajo/encima del contenido
   centrado, así que la "costura" es simplemente no tener nada entre medias.
   La palabra gigante va en peso 400 (cargado en el <link> de Google Fonts)
   — sigue siendo un peso ligero, el contraste con la línea pequeña de
   arriba lo da el tamaño, no el grosor.
   `.ctaband` es un `<div>`, NO el enlace: si el `<a>` cubriera los 100vh,
   toda la pantalla navegaría a `contacto.html` con cualquier clic y el
   hover de los arcos se dispararía pasando el ratón por cualquier punto de
   la banda (mismo bug que ya se corrigió en `.ellcta`). El enlace real es
   `.ctaband__link`, que envuelve solo los arcos + los dos textos y se ajusta
   a su contenido (`inline-flex`, no estirado). */
.ctaband {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  min-height: 100svh;
  background: var(--ink);
  color: var(--on-ink);
  /* Gutter horizontal deliberadamente ajustado (antes clamp hasta 5rem/80px):
     verificado con Chrome headless + la fuente Archivo real que "ESCRÍBENOS"
     (10 letras, sin espacios donde partir) + los arcos a 38px + el gap de
     60px por lado NO caben los tres a la vez en 1280px si el gutter se deja
     ancho -- sobraban ~166px. Con esto en ~24px a 1280 sí entra con margen
     (ver nota en .ctaband__swap sobre el tamaño de palabra ajustado). */
  padding: clamp(2rem, 6vh, 4rem) clamp(1rem, 1.875vw, 1.5rem);
  overflow: clip;
}
/* Hitbox real del CTA: solo el contenido, nunca la sección entera. */
.ctaband__link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
/* Los arcos flanquean el BLOQUE ENTERO (pregunta pequeña + palabra gigante),
   no solo la palabra: .ctaband__center pasó de columna a fila (paréntesis /
   columna de texto .ctaband__stack / paréntesis) y align-items:center los
   centra verticalmente contra el conjunto de las dos líneas, no solo contra
   la más alta de las tres. */
.ctaband__center {
  display: flex;
  align-items: center;
  justify-content: center;
  /* Separación arco↔palabra ~60px a 1280px (antes 14px) — hobro deja mucho
     más aire entre los arcos y el bloque de texto. */
  gap: clamp(1.5rem, 4.7vw, 3.75rem);
}
.ctaband__stack { display: flex; flex-direction: column; align-items: center; }
.ctaband__ask {
  display: block;
  text-align: center;
  /* Caja de frase, no versales: quita el uppercase heredado de .t-display —
     «¿Tienes proyecto?», nunca «¿TIENES PROYECTO?». El resto de .t-display
     (familia display, line-height 0.86) se conserva, pero peso y tracking
     se sobrescriben a propósito: en hobro esta línea es más fina y más
     apretada que .t-display de serie (900/-0.01em) — aquí baja a ~500 de
     peso y ~-0.02em de tracking, ~48px a 1280px (antes 900/-0.01em/38.4px,
     lo que la hacía ver pesada y corta al lado de la de hobro). */
  text-transform: none;
  font-size: clamp(1.75rem, 3.75vw, 3.25rem);
  font-weight: 500;
  letter-spacing: -0.02em;
  margin-bottom: clamp(1rem, 3vh, 2rem);
}
/* Crossfade sin overflow: ambas palabras se apilan en la misma celda de
   grid (grid-area 1/1) en vez de una en flujo + otra position:absolute —
   así el ancho de la caja es el max-content de LA MÁS LARGA de las dos
   («ESCRÍBENOS»), no el de la primera («HABLAMOS»); con position:absolute
   la palabra larga desbordaba el box de la corta y podía recortarse contra
   el overflow:clip de .ctaband. */
.ctaband__swap {
  position: relative;
  display: grid;
  text-align: center;
  text-transform: uppercase;
  font-weight: 400;
  /* Objetivo pedido ~165px a 1280px (antes 115.2px) -- verificado con Chrome
     headless + la fuente real (Archivo 400) que a 165px "ESCRIBENOS" (10
     letras, sin espacio donde partir) mide ~1096px de ancho, y con arcos a
     38px + gap a 60px + el gutter de .ctaband por lado solo quedan ~930-
     1036px disponibles segun el gutter -- desborda incluso con el gutter
     reducido al minimo razonable (ver comentario en .ctaband). Bajado a
     ~150px (9.4rem) -- verificado con margen en 1280px (~37px) y en el
     borde del breakpoint movil a 640px (~4px, el punto mas ajustado de
     toda la curva) -- sigue siendo un salto grande sobre el original
     (115.2 a 150px, +30%), solo no llega al 165 literal por el ancho real
     de nuestra palabra mas larga en espanol (hobro no tiene esta
     restriccion porque su copy es mas corta). HABLAMOS (mas corta) sobra
     margen de sitio. El peso 400 ya estaba bien, no se toca.
     Suelo del clamp en 2.75rem (44px), NO escalado en la misma proporcion
     que el maximo: un suelo mas alto (se probo 3.7rem) fuerza 59px ya a
     375px de viewport y "ESCRIBENOS" deja de caber en la columna
     disponible junto a los arcos en movil tambien. Con 2.75rem el suelo no
     llega a pisar el valor natural del vw hasta bajar de ~370px de
     viewport. */
  font-size: clamp(2.75rem, 11.75vw, 9.4rem);
  line-height: 0.95;
  white-space: nowrap;
  /* Cinturón y tirantes: si aun así no cupiera en algún móvil muy estrecho,
     parte la palabra en vez de desbordar/recortarse (solo tiene efecto
     donde white-space pasa a normal, en el bloque móvil de abajo). */
  overflow-wrap: anywhere;
  /* Vanishing point compartido por las letras rotateX de abajo: sin esto
     el "giro" de rueda se vería como un simple aplastado sin profundidad. */
  perspective: 600px;
}
.ctaband__word { grid-area: 1 / 1; display: block; }

/* Letras (creadas por initCtaParens en JS, nunca a mano en el HTML — las
   palabras pueden cambiar sin dejar markup frágil). Cada letra cae/entra
   con su propio retardo (--chd) y su propia duración (--chdur), fijados
   por JS con un orden barajado determinista (LCG semilla 42, mismo patrón
   que initSectorDots) — nunca izquierda-a-derecha ni Math.random(). Reposo
   = "esperando arriba" (ver valores abajo): una palabra que aún no ha
   entrado se ve bien por defecto, sin depender de que el JS llegue a
   tiempo.
   Efecto "rueda"/cuentakilómetros: cada letra no solo sube/baja, gira sobre
   el eje X (rotateX) con un pellizco de scaleY -- sin perspective la
   rotación se aplanaría sin más, por eso .ctaband__swap fija un
   perspective compartido arriba. La saliente pasa de plano (0deg) a
   -55deg + translateY hacia abajo (se "tumba" y cae, como el borde
   inferior de un tambor); la entrante arranca a +55deg + translateY hacia
   arriba y llega a plano (mismo sentido de giro que la saliente, para que
   ambas lean como el mismo tambor continuando su vuelta, no dos animaciones
   independientes). */
.ctaband__ch {
  display: inline-block;
  opacity: 0;
  transform: translateY(-0.65em) rotateX(55deg) scaleY(0.82);
  transition: opacity var(--chdur, 0.9s) var(--ease-out),
              transform var(--chdur, 0.9s) var(--ease-out);
  transition-delay: var(--chd, 0s);
}
.ctaband__ch.is-shown { opacity: 1; transform: none; }
.ctaband__ch.is-fallen { opacity: 0; transform: translateY(0.65em) rotateX(-55deg) scaleY(0.82); }

/* Paréntesis — 4 arcos concéntricos finos y apretados junto al texto (como
   ondas de sonido saliendo de la palabra): mismo trazo repetido 4× vía
   --pi (índice), desplazado unos pocos % del ancho del propio svg — no
   grados sueltos como antes. Visibles en reposo (no solo al hover) con la
   misma degradación de opacidad 1/.66/.42/.22 hacia fuera; el hover solo
   acentúa un poco más la separación entre arcos. */
.ctaband__parens {
  position: relative;
  /* ~38 × 130px a 1280px (antes 28 × 112px). Ojo: 38:130 no es la relación
     de aspecto del viewBox del svg (60:240 = 1:4, que daría 152px de alto
     para 38px de ancho) -- son las medidas reales de hobro y no son 1:4,
     así que .ctaband__arc fija ambas dimensiones explícitamente (ver abajo,
     con height:100% en el svg en vez de height:auto) en lugar de dejar que
     el ancho derive el alto por su aspect-ratio nativo. Estira ligeramente
     el arco (deja de ser un arco de circunferencia perfecto), pendiente de
     ojo humano por si se nota. */
  width: clamp(1.25rem, 3.5vw, 2.375rem);
  height: clamp(4rem, 10.2vw, 8.125rem);
  flex: none;
}
.ctaband__parens--r { transform: scaleX(-1); }
/* Mecánica calcada de hobro.digital (leída de su CSS/JS de producción, 25-07):
   los 4 arcos están APILADOS en el mismo punto y en reposo solo se ve el
   primero — `.cta__arc:not(:first-child){opacity:0}` en su hoja. Al entrar el
   puntero se abren en abanico hacia fuera: su timeline hace
   `xPercent:-90*(3-i)`, `opacity:1-0.2*i`, `duration:.4` y stagger `0.1*i`,
   y la revierte al salir. Aquí sale igual sin JS: el desplazamiento se
   calcula de `--pi` y el escalonado es `transition-delay`. */
/* El arco va envuelto en un <span> y es ESE el que se transforma, no el
   <svg>: transformar un <svg> raíz tiene esquinas raras entre navegadores
   (`transform-box` por defecto en `view-box`, y los porcentajes resuelven
   contra el sistema de coordenadas del viewBox, no contra la caja CSS).
   Con el span, el porcentaje resuelve contra el ancho real del arco, que es
   lo que queremos. */
.ctaband__arc {
  position: absolute; top: 0; left: 0;
  display: block;
  width: clamp(1.25rem, 3.5vw, 2.375rem);
  height: clamp(4rem, 10.2vw, 8.125rem);
  opacity: 0;
  transition: transform 0.4s var(--ease-hobro), opacity 0.4s var(--ease-hobro);
  transition-delay: calc(var(--pi) * 0.1s);
}
/* height:100% (no auto): el arco rellena exactamente la caja 38×130 fijada
   arriba en vez de derivar su alto del aspect-ratio 1:4 del viewBox. */
.ctaband__arc svg { display: block; width: 100%; height: 100%; }
/* Trazo subido de 1 a 2 (a petición): a 1 se quedaba fino frente al
   original de hobro. */
.ctaband__parens svg path { fill: none; stroke: var(--on-ink); stroke-width: 2; }
.ctaband__arc:first-child { opacity: 1; }
/* El hover/focus vive en .ctaband__link (el <a>, solo arcos+texto), no en
   .ctaband (el <div> a 100vh): si colgara del div, pasar el ratón por
   cualquier punto vacío de la pantalla abriría el abanico de arcos. */
.ctaband__link:hover .ctaband__arc,
.ctaband__link:focus-visible .ctaband__arc {
  transform: translateX(calc((3 - var(--pi)) * -90%));
  opacity: calc(1 - var(--pi) * 0.2);
}

/* ─── 13b. CTA «Arrancamos» — mecánica real de hobro (dos óvalos + relleno
   de intersección), decodificada de su .callBtn de producción, con tres
   ideas propias encima de lo suyo:

   1) Giro continuo. Hobro deja sus óvalos fijos a -17deg; aquí giran sin
      parar (idea del dueño del proyecto, no está en hobro). Para que el
      giro no descuadre el truco del relleno — que depende de que la
      posición relativa entre ambos óvalos sea SIEMPRE la misma — la
      rotación continua vive en un contenedor propio (.ellcta__group,
      animation:ellcta-spin, 48s/vuelta) que envuelve a los DOS óvalos Y
      al enlace. Cada óvalo, dentro del grupo, solo lleva su tilt estático
      (-17deg, igual que hobro) + el translateY del hover — nunca la
      rotación de grupo. Capas separadas: la rotación vive en el padre,
      el desplazamiento de hover en cada hijo, así nunca compiten dos
      transforms por el mismo elemento.
   2) 10 destellos de 4 puntas repartidos alrededor del anillo de los
      óvalos (antes 6, y esos 6 quedaban lejos/diminutos) — mismo path
      que el grande de hobro (viewBox 40×39), con tamaños muy dispares
      (de ~2.7rem a ~0.3rem, como los 40px/5px de hobro) para que un par
      se lean claramente como destellos grandes y el resto como chispas
      menores. Parpadeo yoyo escalonado, igual que antes.
   3) El enlace («Arrancamos») vive DENTRO del grupo giratorio a
      propósito — así su área de click nunca se despega del óvalo — pero
      el texto (.ellcta__label) lleva su propia animación de giro en
      sentido contrario y misma duración (ellcta-spin-reverse, 48s
      linear), así que el giro del grupo y el contragiro del texto se
      cancelan exactamente y el texto queda siempre horizontal. Solo el
      texto se contrarrota: el <a> (su caja de click, invisible) sigue
      girando con el grupo sin que importe, porque no tiene borde/relleno
      propio visible.

   El truco del relleno (::before de .ellcta__shape--bottom) es el mismo
   de hobro: un óvalo idéntico relleno de tinta, oculto por el
   overflow:hidden del padre salvo en la franja que comparte con el óvalo
   de arriba (border-radius + overflow:hidden recortan al hijo a la forma
   ovalada, no al rectángulo). La posición de ese óvalo relleno se deriva
   así: top y bottom comparten el mismo tilt estático -17deg como PROPIO
   transform (no heredado), así que ese -17deg es también, literalmente,
   el transform que ve cualquier hijo de bottom — incluido su ::before.
   El pseudo-elemento no necesita deshacer ni repetir el tilt: al ser
   descendiente de bottom ya gira con él gratis. Solo necesita el mismo
   translateY que separaría "top" de "bottom" en el sistema SIN tiltar
   (el que usan top_hover/bottom_hover más abajo): Δ = top_hover −
   bottom_hover = -30% − 30% = -60% (de la propia altura del óvalo). Por
   eso el ::before lleva solo `translateY(-60%)`, sin rotate propio — un
   rotate(17deg) adicional ahí sería redundante/incorrecto: el -17deg de
   bottom ya se propaga a sus hijos sin que el pseudo tenga que repetirlo.
   Usar % en vez de los px fijos que trae hobro (16px/-56px, con un ajuste
   aparte por debajo de 1440px) es lo que permite saltarnos su breakpoint:
   los % se reescalan solos con el ancho fluido de .ellcta — al reducir el
   ancho máximo de 70rem a 34rem (tarea 3, más pequeño para compartir fila
   con las 4 palabras) el -60% sigue siendo válido sin recalcular: es un
   porcentaje de la propia altura del óvalo, no del ancho del bloque.

   4) Núcleo y órbitas (arreglo de bug, capturas del dueño): al girar el
      grupo, los óvalos cruzaban por encima de «Arrancamos» hacia 60-110°
      y la palabra se leía tachada — hobro no lo sufre porque sus óvalos
      no giran. Arreglo: .ellcta__link deja de ser el óvalo pequeño y pasa
      a ser una moneda circular opaca (--coin, aspect-ratio 1) en el
      centro, por encima de los óvalos (z-index); estos bajan a
      opacity:.85 en reposo y pasan a leerse como órbitas alrededor del
      núcleo. Como la moneda es un círculo, su propio giro es invisible:
      nada vuelve a cruzar el texto. */
.ellcta-wrap {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: clamp(2rem, 6vw, 4.5rem);
  padding-block: var(--section);
  padding-inline: var(--gutter);
  max-width: 1800px;
  margin-inline: auto;
}
/* Las 4 fases del proceso, movidas aquí desde el statement (tarea 3): se
   conserva la clase .statement__words (mismo aria-label) pero en columna
   propia a la izquierda del CTA, con tamaño de titular kicker — recupera
   presencia ahora que ya no comparte fila con los óvalos. La regla
   genérica .statement__words (§7) sigue aportando el color de .t-cursive;
   aquí se sobreescribe el layout (columna en vez de fila) y el tamaño. */
.ellcta-wrap .statement__words {
  margin-top: 0;
  flex-direction: column;
  flex-wrap: nowrap;
  align-items: flex-start;
  text-align: left;
  font-size: clamp(2.1rem, 4.4vw, 3.6rem);
  row-gap: clamp(0.3rem, 1.2vw, 0.75rem);
  column-gap: 0;
}
.ellcta {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* Reducido de clamp(20rem,90vw,70rem) — el CTA compartía fila con las
     palabras y se comía la pantalla (1120×611px de máximo real). Medido
     tras el cambio: a 1280px de viewport, 42vw ≈ 538px (dentro del
     clamp) → alto ≈ 538/1.833 ≈ 293px; el conjunto con la columna de
     palabras a la izquierda no supera ~630px de alto total, cómodo en
     una pantalla de 800px. */
  width: clamp(16rem, 42vw, 34rem);
  aspect-ratio: 220 / 120;
  isolation: isolate;
  /* Diámetro de la moneda-núcleo (idea 4 arriba): % de la propia altura
     del bloque, no del ancho — así es circular real vía aspect-ratio:1 +
     height:var(--coin) en .ellcta__link, sin depender del ratio 220/120.
     58%, no el 46% de partida: medido con Chrome headless + la fuente
     real, «ARRANCAMOS» (uppercase por .t-mono) a font-size 20.8px/
     letter-spacing .09em mide 143.5px de ancho — un 46% de los 293px de
     alto a 1280px solo da 134.9px de diámetro, MENOS que el propio label
     (se saldría del círculo). Con 58% el diámetro sube a 170px, ~13.3px
     de aire a cada lado a 1280px. */
  --coin: 58%;
}
/* ≤900px: la fila de dos columnas pasa a una sola (palabras arriba,
   CTA debajo — mismo orden que en el HTML) y las palabras vuelven a
   centrarse, a juego con el CTA centrado debajo. */
@media (max-width: 900px) {
  .ellcta-wrap {
    grid-template-columns: 1fr;
    justify-items: center;
  }
  .ellcta-wrap .statement__words {
    align-items: center;
    text-align: center;
  }
}

/* Destellos decorativos: mismo path (4 puntas, viewBox 40×39) que el
   grande de hobro, repartidos en 10 tamaños/posiciones muy dispares
   (de ~2.7rem a ~0.3rem — como los 40px/5px de hobro, dos-tres grandes
   de verdad y el resto claramente menores) ciñendo el anillo que
   dibujan los dos óvalos: pegados al trazo (top/bottom del óvalo
   superior e inferior), alguno rozando por dentro (7/8, cerca del
   centro donde se solapan ambos óvalos) y alguno justo fuera del borde
   lateral (5/6, donde casi se tocan las puntas de ambos óvalos) — ya no
   en las esquinas del bloque. Parpadeo yoyo (0→1 opacidad+escala, ida y
   vuelta por animation-direction:alternate) con duraciones y delays
   negativos escalonados para que nunca coincidan en fase — mismo patrón
   que hobro (0.5/0.5/0.4/0.3s con arranques distintos). */
.ellcta__spark-defs { position: absolute; width: 0; height: 0; overflow: hidden; }
.ellcta__spark {
  position: absolute;
  fill: var(--muted);
  opacity: 0;
  transform: scale(0);
  transform-origin: 50% 50%;
  animation: ellcta-spark-blink var(--spark-dur, 0.5s) ease-in-out infinite alternate;
  animation-delay: var(--spark-delay, 0s);
  pointer-events: none;
}
.ellcta__spark--1  { width: clamp(1.7rem, 3.2vw, 2.7rem);   top: 6%;    left: 20%;  --spark-dur: 0.5s;  --spark-delay: -0.1s; }
.ellcta__spark--2  { width: clamp(1.3rem, 2.5vw, 2.1rem);   bottom: 8%; right: 18%; --spark-dur: 0.5s;  --spark-delay: -0.35s; }
.ellcta__spark--3  { width: clamp(0.7rem, 1.3vw, 1.15rem);  top: 18%;   right: 9%;  --spark-dur: 0.4s;  --spark-delay: -0.2s; }
.ellcta__spark--4  { width: clamp(0.65rem, 1.2vw, 1.05rem); bottom: 16%; left: 7%;  --spark-dur: 0.3s;  --spark-delay: -0.05s; }
.ellcta__spark--5  { width: clamp(0.55rem, 1vw, 0.8rem);    top: 46%;   left: -2%;  --spark-dur: 0.45s; --spark-delay: -0.28s; }
.ellcta__spark--6  { width: clamp(0.55rem, 1vw, 0.8rem);    top: 52%;   right: -2%; --spark-dur: 0.55s; --spark-delay: -0.15s; }
.ellcta__spark--7  { width: clamp(0.3rem, 0.55vw, 0.4rem);  top: 38%;   left: 33%;  --spark-dur: 0.35s; --spark-delay: -0.22s; }
.ellcta__spark--8  { width: clamp(0.3rem, 0.55vw, 0.4rem);  top: 58%;   right: 31%; --spark-dur: 0.4s;  --spark-delay: -0.08s; }
.ellcta__spark--9  { width: clamp(0.4rem, 0.75vw, 0.6rem);  top: 3%;    right: 40%; --spark-dur: 0.45s; --spark-delay: -0.32s; }
.ellcta__spark--10 { width: clamp(0.4rem, 0.75vw, 0.6rem);  bottom: 4%; left: 44%;  --spark-dur: 0.3s;  --spark-delay: -0.18s; }
@keyframes ellcta-spark-blink {
  from { opacity: 0; transform: scale(0); }
  to   { opacity: 1; transform: scale(1); }
}

/* Grupo giratorio: envuelve los dos óvalos y el enlace clicable — gira
   como bloque único para que la geometría relativa entre ambos óvalos
   (de la que depende el relleno) no cambie nunca. El enlace va DENTRO a
   propósito: si se quedara fuera (fijo), al cabo de unos segundos de giro
   su hit-box dejaría de coincidir con lo que se ve. */
.ellcta__group {
  position: absolute;
  inset: 0;
  animation: ellcta-spin 48s linear infinite;
}
@keyframes ellcta-spin { to { transform: rotate(360deg); } }

.ellcta__shape {
  position: absolute;
  top: 50%; left: 50%;
  width: 96%; height: 42%;
  border: 1px solid var(--ink);
  border-radius: var(--r-round);
  pointer-events: none;
  /* Órbitas (idea 4, cabecera): algo atenuadas en reposo para que el
     núcleo domine la lectura; a plena opacidad en hover/focus. */
  opacity: 0.85;
}
.ellcta__shape--top {
  transform: translate(-50%, -50%) rotate(-17deg) translateY(-14%);
  transition: transform 0.4s var(--ease-hobro) 0.3s, opacity 0.4s var(--ease-hobro) 0.3s;
}
.ellcta__shape--bottom {
  overflow: hidden;
  transform: translate(-50%, -50%) rotate(-17deg) translateY(9%);
  transition: transform 0.4s var(--ease-hobro) 0.3s, opacity 0.4s var(--ease-hobro) 0.3s;
}
/* El relleno: ver fórmula completa en el comentario de cabecera. */
.ellcta__shape--bottom::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: var(--r-round);
  background: var(--ink);
  opacity: 0;
  transform: translateY(-60%);
  transition: opacity 0.2s ease-in-out;
}

/* Coreografía de entrada: los óvalos se separan en 0.4s (sin retardo);
   a los 300ms de esa separación arranca el relleno de la intersección
   (delay .3s, entra en .3s) — por eso primero se ven separarse y solo
   entonces aparece la tinta. */
.ellcta:has(.ellcta__link:hover) .ellcta__shape--top,
.ellcta:focus-within .ellcta__shape--top {
  transform: translate(-50%, -50%) rotate(-17deg) translateY(-30%);
  opacity: 1;
  transition: transform 0.4s var(--ease-hobro), opacity 0.4s var(--ease-hobro);
}
.ellcta:has(.ellcta__link:hover) .ellcta__shape--bottom,
.ellcta:focus-within .ellcta__shape--bottom {
  transform: translate(-50%, -50%) rotate(-17deg) translateY(30%);
  opacity: 1;
  transition: transform 0.4s var(--ease-hobro), opacity 0.4s var(--ease-hobro);
}
.ellcta:has(.ellcta__link:hover) .ellcta__shape--bottom::before,
.ellcta:focus-within .ellcta__shape--bottom::before {
  opacity: 1;
  transition: opacity 0.3s ease-in-out 0.3s;
}

/* Moneda-núcleo (idea 4, cabecera): círculo real vía aspect-ratio:1 +
   height:var(--coin) (width:auto la deriva) — % de la propia altura del
   bloque, no del ancho, así el círculo no se deforma al variar 220/120.
   Sigue siendo la única área clicable (los dos .ellcta__shape llevan
   pointer-events:none) y ahora también pinta por encima de ambos óvalos
   (z-index:1) para taparlos donde cruzan por el centro. */
.ellcta__link {
  position: absolute;
  top: 50%; left: 50%;
  height: var(--coin);
  aspect-ratio: 1;
  width: auto;
  transform: translate(-50%, -50%);
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--canvas);
  border: 1px solid var(--ink);
  border-radius: var(--r-round);
  transition: background-color 0.3s ease-in-out 0.3s, transform 0.4s var(--ease-hobro), border-color 0.3s ease-in-out 0.3s;
}
/* Contragiro del texto (tarea 1): .ellcta__link vive dentro de
   .ellcta__group a propósito y gira con él (su hit-box de click nunca se
   despega del óvalo), pero .ellcta__label es quien realmente se ve, y
   lleva su propia animación en sentido contrario, misma duración y sin
   delay (ellcta-spin-reverse, 48s linear — idénticos a ellcta-spin) para
   que ambos giros arranquen en fase y se cancelen exactamente en todo
   momento: +360deg del grupo en 48s + -360deg del texto en 48s = 0deg
   netos, el texto queda siempre horizontal. */
.ellcta__label {
  display: inline-block;
  color: var(--ink);
  font-size: clamp(0.85rem, 1.8vw, 1.3rem);
  letter-spacing: 0.09em;
  transition: color 0.4s ease-in-out 0.3s;
  animation: ellcta-spin-reverse 48s linear infinite;
}
@keyframes ellcta-spin-reverse { to { transform: rotate(-360deg); } }
.ellcta:has(.ellcta__link:hover) .ellcta__label,
.ellcta:focus-within .ellcta__label {
  color: var(--on-ink);
  transition: color 0.3s ease-in-out;
}
/* La moneda invierte igual que el relleno de los óvalos: fondo e-ink,
   borde a juego, y crece un pelín (scale 1.08) para reforzar el foco —
   mismos selectores :has(hover)/:focus-within que ya usa el resto del
   componente. */
.ellcta:has(.ellcta__link:hover) .ellcta__link,
.ellcta:focus-within .ellcta__link {
  background: var(--ink);
  border-color: var(--ink);
  transform: translate(-50%, -50%) scale(1.08);
}

/* Táctil: no hay hover, así que el relleno se deja visible por defecto —
   se lee como botón sin necesidad de pasar el dedo por encima. La moneda
   nace ya invertida por el mismo motivo. */
@media (hover: none) {
  .ellcta__shape--top { transform: translate(-50%, -50%) rotate(-17deg) translateY(-30%); opacity: 1; }
  .ellcta__shape--bottom { transform: translate(-50%, -50%) rotate(-17deg) translateY(30%); opacity: 1; }
  .ellcta__shape--bottom::before { opacity: 1; }
  .ellcta__label { color: var(--on-ink); }
  .ellcta__link { background: var(--ink); border-color: var(--ink); }
}

/* Ambos CTA siguen siendo enlaces claros y pulsables: con
   prefers-reduced-motion se para TODO el movimiento continuo (giro de
   .ellcta__group + parpadeo de destellos, regla dura no negociable) y el
   hover pasa a ser un cambio directo, sin coreografía de transition-delay. */
@media (prefers-reduced-motion: reduce) {
  .ctaband__arc { transition: none; transition-delay: 0s; }
  /* Sin caída/entrada letra a letra: initCtaParens ya no arranca el
     intervalo con reduced-motion (queda "Hablamos" fija), esto es
     cinturón-y-tirantes por si algo más llegara a tocar las clases. */
  .ctaband__ch { transition: none; }
  .ellcta__group { animation: none; }
  .ellcta__label { animation: none; }
  .ellcta__spark { animation: none; opacity: 0.55; transform: scale(1); }
  .ellcta__shape,
  .ellcta__shape--bottom::before,
  .ellcta__label,
  .ellcta__link { transition: none !important; }
}

/* Moneda-núcleo en el rango móvil (≤900px, colocado aquí a propósito: DEBE
   ir después de la definición base de .ellcta__label más arriba — misma
   especificidad de clase simple, así que si esta regla fuera anterior en
   el archivo la base la pisaría igualmente dentro de la media query).
   El bloque entero se achica más rápido de lo que puede achicarse el
   label (su clamp tiene suelo — 0.85rem, o 0.62rem bajo el override de
   ≤640px del bloque móvil — mientras .ellcta ya toca su ancho mínimo de
   16rem). Medido con Chrome headless: en la banda 523-640px de ancho de
   viewport (label a su techo de 13.6px, .ellcta ya en su suelo de
   256×139.6px) el label mide 89.8px con letter-spacing .06em — el 58% de
   escritorio se quedaría en 81px de diámetro, MENOS que el propio label.
   68% da 95px, ~14px de aire a cada lado ahí; a 390px (font-size aún más
   reducido por el override de ≤640px, 10.15px) el label mide 70px y el
   aire sube a ~12.5px. */
@media (max-width: 900px) {
  .ellcta { --coin: 68%; }
  .ellcta__label { letter-spacing: 0.06em; }
}

/* ─── 14. Casos — destacado + pila ───────────────────────────────────────── */

.fresh { padding-block: var(--section) 0; }

.stack { padding-block: clamp(2.5rem, 6vw, 5rem) var(--section); }
.stack__filters { display: flex; gap: 1.75rem; flex-wrap: wrap; margin-bottom: 2.5rem; color: var(--muted); }
.stack__filters b { color: var(--ink); font-weight: 500; }
.stack__list { display: flex; flex-direction: column; gap: clamp(1.5rem, 4vw, 3rem); }
.case-card {
  position: sticky;
  top: calc(var(--nav-h) + 1rem);
  border-radius: var(--r-card);
  overflow: clip;
  background: var(--ink); color: var(--on-ink);
  aspect-ratio: 16 / 8.5;
  min-height: 24rem;
  display: flex; flex-direction: column; justify-content: space-between;
  isolation: isolate;
  box-shadow: var(--shadow-pop);
}
.case-card__media { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: top; z-index: -1; transition: transform 1.2s var(--ease-out); }
.case-card:hover .case-card__media { transform: scale(1.04); }
.case-card::after { content: ""; position: absolute; inset: 0; background: linear-gradient(180deg, rgba(0, 0, 0, 0.35), transparent 35%, transparent 60%, rgba(0, 0, 0, 0.55)); z-index: -1; }
.case-card__top { display: flex; justify-content: space-between; gap: 1rem; padding: 1.25rem 1.5rem; }
.case-card__bottom { display: flex; align-items: flex-end; justify-content: space-between; gap: 1.5rem; padding: 1.25rem 1.5rem; }
.case-card__name { font-size: clamp(2rem, 4.6vw, 4.4rem); }
.case-card__desc { max-width: 34ch; color: var(--hairline); font-size: var(--fs-mono); }
.case-card__visit {
  flex-shrink: 0;
  display: inline-flex; align-items: center; gap: 0.5rem;
  font-family: var(--font-mono); font-size: var(--fs-mono); text-transform: uppercase;
  border: 1px solid var(--on-ink); border-radius: var(--r-chip); padding: 0.7em 1.4em;
  transition: background var(--dur-1) var(--ease-out), color var(--dur-1) var(--ease-out);
}
.case-card__visit:hover { background: var(--neon); color: var(--ink); border-color: var(--neon); }
@media (max-width: 800px) {
  .case-card { aspect-ratio: auto; min-height: 70svh; position: relative; top: 0; }
  .case-card__bottom { flex-direction: column; align-items: flex-start; }
}

/* ─── 15. Sección negra (aims): por qué IA + proceso ─────────────────────── */

/* Padding inferior recortado a propósito: la .ctaband va justo detrás, mismo
   --ink sin borde/sombra entre medias — un solo tramo oscuro continuo.
   (Actualización: desde que .ctaband pasó a 100vh con el contenido
   centrado, ya no hace falta que este padding-bottom "cuadre" con el
   padding-top de .ctaband — ese ya no existe como tal, el aire de encima
   del CTA es simplemente el resto del viewport. La continuidad de tramo
   oscuro la da solo compartir --ink y no tener borde/margen entre medias;
   este recorte se mantiene para no duplicar respiro entre la nota del
   proceso y el borde superior de la banda.) */
.aims { padding-block: var(--section) clamp(3rem, 7vw, 5.5rem); }
.aims__statement { font-size: var(--fs-title); max-width: 20ch; color: var(--on-ink); }
.aims__statement .t-cursive { color: var(--neon); }
.aims__grid {
  margin-top: clamp(3rem, 7vw, 5.5rem);
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 2rem;
  border-top: 1px solid var(--hairline-dark);
  padding-top: 2.5rem;
}
.aims__cell-label { color: var(--hairline); margin-bottom: 0.8rem; }
.aims__cell-old { color: var(--muted); text-decoration: line-through; }
.aims__cell-new { font-family: var(--font-display); font-weight: 900; text-transform: uppercase; font-size: clamp(1.4rem, 2.6vw, 2.4rem); line-height: 1; margin-top: 0.4rem; }
.aims__cell-new em { font-style: normal; color: var(--neon); }

.aims__note { margin-top: 2rem; color: var(--muted); }
.aims__note a { color: var(--on-ink); text-decoration: underline; text-underline-offset: 3px; }
@media (max-width: 800px) {
  .aims__grid { grid-template-columns: 1fr; }
}

/* ─── 16. Planes ─────────────────────────────────────────────────────────── */

.tiers__grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.25rem; align-items: stretch; }
.tier {
  border: 1px solid var(--hairline);
  border-radius: var(--r-card);
  padding: clamp(1.5rem, 2.5vw, 2.25rem);
  display: flex; flex-direction: column; gap: 1.1rem;
  position: relative;
  transition: transform var(--dur-1) var(--ease-out), box-shadow var(--dur-1) var(--ease-out);
}
.tier:hover { transform: translateY(-6px); box-shadow: var(--shadow-pop); }
.tier--featured { background: var(--ink); color: var(--on-ink); border-color: var(--ink); }
.tier__badge {
  position: absolute; top: -0.9rem; left: 1.5rem;
  background: var(--neon); color: var(--ink);
  font-family: var(--font-mono); font-size: var(--fs-mono-sm); font-weight: 500; text-transform: uppercase;
  border-radius: var(--r-chip); padding: 0.4em 1em;
}
.tier__name { color: var(--muted); }
.tier--featured .tier__name { color: var(--hairline); }
.tier__price { font-family: var(--font-display); font-weight: 900; font-size: clamp(2.6rem, 4.5vw, 4.2rem); line-height: 0.9; text-transform: uppercase; letter-spacing: -0.01em; }
.tier__cycle { color: var(--muted); margin-top: -0.4rem; }
.tier__desc { font-size: 0.95rem; color: var(--muted); }
.tier--featured .tier__desc { color: var(--hairline); }
.tier__list { display: flex; flex-direction: column; flex: 1; font-family: var(--font-mono); font-size: var(--fs-mono-sm); text-transform: uppercase; letter-spacing: 0.01em; }
.tier__list li { display: flex; gap: 0.6rem; align-items: flex-start; padding-block: 0.45rem; border-bottom: 1px solid var(--hairline); }
.tier--featured .tier__list li { border-color: var(--hairline-dark); }
.tier__list li::before { content: "→"; color: var(--neon); }
.tier--featured .tier__list li::before { color: var(--neon); }
.tiers__note { margin-top: 2.5rem; color: var(--muted); max-width: 62ch; }
.tiers__note a { color: var(--ink); text-decoration: underline; text-underline-offset: 3px; }
@media (max-width: 900px) { .tiers__grid { grid-template-columns: 1fr; } }

/* Comparativa */
.compare__wrap { margin-top: clamp(2.5rem, 5vw, 4rem); overflow-x: auto; border: 1px solid var(--hairline); border-radius: var(--r-media); }
.compare__table { min-width: 40rem; font-family: var(--font-mono); font-size: var(--fs-mono-sm); text-transform: uppercase; }
.compare__table th, .compare__table td { padding: 0.9rem 1.25rem; border-bottom: 1px solid var(--hairline); text-align: left; }
.compare__table thead th { background: var(--ink); color: var(--on-ink); font-weight: 500; }
.compare__table tbody tr:last-child th, .compare__table tbody tr:last-child td { border-bottom: 0; }
.compare__table tbody th { font-weight: 500; }
.compare__yes { color: var(--ink); background: var(--neon); border-radius: var(--r-round); width: 1.3rem; height: 1.3rem; display: inline-grid; place-items: center; font-size: 0.7rem; }
.compare__no { color: var(--hairline); }

.choose { margin-top: clamp(2.5rem, 5vw, 4rem); display: flex; flex-direction: column; }
.choose__line { font-size: var(--fs-list); text-transform: uppercase; font-weight: 500; letter-spacing: -0.015em; color: var(--muted); padding-block: 0.9rem; border-top: 1px solid var(--hairline); }
.choose__line:last-child { border-bottom: 1px solid var(--hairline); }
.choose__line strong { color: var(--ink); font-weight: 500; background: var(--sage); padding: 0.05em 0.3em; border-radius: var(--r-sm); }

/* ─── 17. Mantenimiento ──────────────────────────────────────────────────── */

.maint__grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1.25rem; align-items: stretch; }
.maint__card { border: 1px solid var(--hairline); border-radius: var(--r-card); padding: clamp(1.5rem, 2.5vw, 2.25rem); display: flex; flex-direction: column; gap: 1.1rem; position: relative; }
.maint__card--pro { background: var(--sage); border-color: var(--sage); }
.maint__price { font-family: var(--font-display); font-weight: 900; font-size: clamp(2.6rem, 4.5vw, 4.2rem); line-height: 0.9; letter-spacing: -0.01em; }
.maint__price span { font-family: var(--font-mono); font-size: var(--fs-mono); font-weight: 500; color: var(--muted); }
.maint__iva { color: var(--muted); margin-top: -0.5rem; }
.maint__card ul { display: flex; flex-direction: column; font-family: var(--font-mono); font-size: var(--fs-mono-sm); text-transform: uppercase; }
.maint__card li { display: flex; gap: 0.6rem; padding-block: 0.45rem; border-bottom: 1px solid var(--hairline); }
.maint__card--pro li { border-color: rgba(0, 0, 0, 0.15); }
.maint__card li::before { content: "→"; color: var(--ink); }
.maint__argument { margin-top: 2.5rem; font-size: var(--fs-list); text-transform: uppercase; font-weight: 500; letter-spacing: -0.015em; max-width: 52ch; }
@media (max-width: 800px) { .maint__grid { grid-template-columns: 1fr; } }

/* ─── 18. Partners (sectores + integraciones) ────────────────────────────── */

.partners__cats { display: flex; gap: 1.75rem; flex-wrap: wrap; margin-bottom: 2.5rem; color: var(--muted); }
.partners__cats b { color: var(--ink); font-weight: 500; }
.partners__list { display: flex; flex-wrap: wrap; gap: 0.5rem 2rem; }
.partners__item { transition: color var(--dur-1) var(--ease-out), transform var(--dur-1) var(--ease-out); }
.partners__item:hover { color: var(--muted); transform: translateX(0.25rem); }
.partners__sub { margin-top: 0; } /* (2026-09-08) ya no hay sección Sectores encima: sin margen extra */
.integr__chips { display: flex; flex-wrap: wrap; gap: 0.6rem; }
.integr__chip {
  display: inline-flex; align-items: center; gap: 0.6rem;
  font-family: var(--font-mono); font-size: var(--fs-mono-sm); font-weight: 500; text-transform: uppercase;
  border: 1px solid var(--hairline); border-radius: var(--r-chip);
  padding: 0.6em 1.2em;
  transition: border-color var(--dur-1) var(--ease-out), transform var(--dur-1) var(--ease-out);
}
.integr__chip:hover { border-color: var(--ink); transform: translateY(-2px); }
.integr__chip svg { width: 1.25em; height: 1.25em; }
.integr__badge {
  width: 1.35em; height: 1.35em;
  display: inline-grid; place-items: center; border-radius: var(--r-sm);
  font-weight: 700; font-size: 0.72em;
  background: var(--badge-bg, var(--sage)); color: var(--badge-ink, var(--ink));
}

/* ─── 19. Voces (patrón team) ────────────────────────────────────────────── */

.voices__index { display: flex; gap: 0.75rem; flex-wrap: wrap; color: var(--muted); margin-bottom: 2.5rem; }
.voices__grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1.25rem; }
.voice {
  border: 1px solid var(--hairline); border-radius: var(--r-card);
  padding: clamp(1.5rem, 2.5vw, 2.25rem);
  display: flex; flex-direction: column; justify-content: space-between; gap: 2rem;
  transition: background var(--dur-1) var(--ease-out);
}
.voice:hover { background: var(--sage); border-color: var(--sage); }
.voice:nth-child(2):hover { background: var(--pink); border-color: var(--pink); }
.voice:nth-child(3):hover { background: var(--neon); border-color: var(--neon); }
.voice__quote { font-size: clamp(1.05rem, 1.6vw, 1.35rem); line-height: 1.45; }
.voice__quote::before { content: "“ "; font-family: var(--font-cursive); font-style: italic; }
.voice__author { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
.voice__who { display: flex; flex-direction: column; }
.voice__who strong { font-weight: 500; font-size: 1.1rem; }
.voice__who span { color: var(--muted); font-family: var(--font-mono); font-size: var(--fs-mono-sm); text-transform: uppercase; }
.voice__num { color: var(--muted); }
@media (max-width: 800px) { .voices__grid { grid-template-columns: 1fr; } }

/* ─── 20. FAQ ────────────────────────────────────────────────────────────── */

.faq__list { border-top: 1px solid var(--hairline); }
.faq__item { border-bottom: 1px solid var(--hairline); }
.faq__q {
  width: 100%; display: grid; grid-template-columns: 4rem 1fr auto; gap: 1.25rem; align-items: center;
  padding-block: 1.4rem; text-align: left;
  transition: color var(--dur-1) var(--ease-out);
}
.faq__q:hover { color: var(--muted); }
.faq__q-num { color: var(--muted); }
.faq__q-text { font-size: var(--fs-list); text-transform: uppercase; font-weight: 500; letter-spacing: -0.015em; }
.faq__plus { width: 2.2rem; height: 2.2rem; border: 1px solid var(--hairline); border-radius: var(--r-round); display: grid; place-items: center; }
.faq__plus svg { width: 0.85rem; height: 0.85rem; transition: transform var(--dur-1) var(--ease-out); }
.faq__q[aria-expanded="true"] .faq__plus { background: var(--ink); color: var(--on-ink); border-color: var(--ink); }
.faq__q[aria-expanded="true"] .faq__plus svg { transform: rotate(45deg); }
.faq__a { display: grid; grid-template-rows: 0fr; transition: grid-template-rows var(--dur-2) var(--ease-out); }
.faq__a[aria-hidden="false"] { grid-template-rows: 1fr; }
.faq__a-in { overflow: hidden; min-height: 0; }
.faq__a p { padding: 0 0 1.5rem 5.25rem; max-width: 62ch; color: var(--muted); }
@media (max-width: 800px) {
  .faq__q { grid-template-columns: 1fr auto; }
  .faq__q-num { display: none; }
  .faq__a p { padding-left: 0; }
}

/* ─── 21. Contacto — bento de tarjetas (patrón hobro/contact) ────────────── */
/* Layout: columna estrecha de atención directa + columna ancha que contiene
   eyebrow, título Y formulario. El título va DENTRO de la columna derecha
   (alineado con las tarjetas, no con la dirección) — por eso baja de
   --fs-display: ya no dispone del ancho de página. */

.contact__layout {
  display: grid; grid-template-columns: minmax(13rem, 0.42fr) 2fr;
  gap: clamp(2.5rem, 6vw, 6rem);
  align-items: start;
}
.contact__main { min-width: 0; }
.contact__title { font-size: clamp(2.4rem, 4.6vw, 5.2rem); }
.contact__title .t-cursive { color: var(--muted); }

.contact__direct { display: flex; flex-direction: column; gap: 1.5rem; }
.contact__dot { width: 8px; height: 8px; border-radius: var(--r-round); background: var(--ink); }
.contact__block { display: flex; flex-direction: column; gap: 0.4rem; }
.contact__block-label { color: var(--muted); }
.contact__link { font-weight: 500; transition: color var(--dur-1) var(--ease-out); }
.contact__link:hover { color: var(--muted); }

/* Bento: tres columnas en flex vertical, NO grid-row: span — los spans se
   rompen en cuanto se añade o se quita un campo. La tarjeta de la columna
   central lleva flex:1 y estira hasta igualar la columna más alta: eso es lo
   que da el ritmo del bento en la referencia. */
.cform { margin-top: clamp(2rem, 4vw, 3.5rem); }
.cform__bento {
  display: grid; grid-template-columns: repeat(3, 1fr);
  gap: clamp(0.75rem, 1.2vw, 1rem);
  align-items: stretch;
}
.cform__col { display: flex; flex-direction: column; gap: clamp(0.75rem, 1.2vw, 1rem); min-width: 0; }

.cbox {
  display: flex; flex-direction: column; gap: 0.5rem;
  border: 1px solid var(--hairline); border-radius: var(--r-card);
  padding: clamp(1.1rem, 1.8vw, 1.5rem);
  transition: border-color var(--dur-1) var(--ease-out);
}
.cbox:focus-within { border-color: var(--ink); }
.cbox--grow { flex: 1; }
.cbox .cform__label:not(:first-child) { margin-top: 0.9rem; }

.cform__label { color: var(--muted); }
.cform__label b { color: var(--ink); font-weight: 500; }
.cform .input, .cform .select, .cform .textarea {
  width: 100%; background: transparent; color: var(--ink);
  border: 0; border-radius: 0; padding: 0;
  font-family: var(--font-body); font-weight: 500;
  font-size: clamp(1rem, 1.25vw, 1.2rem); line-height: 1.35;
}
.cform .input::placeholder, .cform .textarea::placeholder { color: var(--hairline); opacity: 1; }
.cform .input:focus, .cform .select:focus, .cform .textarea:focus { outline: none; }
.cform .textarea { flex: 1; resize: none; min-height: 9rem; }
.cform .select { appearance: none; cursor: pointer; }
.cform__hp { position: absolute; left: -9999px; width: 1px; height: 1px; opacity: 0; }

/* Adjunto de brief */
.cfile {
  display: flex; align-items: center; gap: 0.6rem;
  margin-top: 0.35rem; cursor: pointer; color: var(--muted);
  transition: color var(--dur-1) var(--ease-out);
}
.cfile:hover, .cfile.is-set { color: var(--ink); }
.cfile:focus-within { outline: 2px solid var(--ink); outline-offset: 3px; border-radius: var(--r-sm); }
.cfile__icon { width: 1.35rem; height: 1.35rem; flex: none; }
.cfile__text { font-size: var(--fs-mono-sm); text-transform: uppercase; line-height: 1.4; }
.cfile__input { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.cfile__clear {
  align-self: flex-start; margin-top: 0.5rem;
  font-size: var(--fs-mono-sm); color: var(--muted); text-decoration: underline;
}
.cfile__clear:hover { color: var(--ink); }
.cfile__clear[hidden] { display: none; }

/* Consentimiento de privacidad */
.ccheck { display: flex; align-items: flex-start; gap: 0.7rem; cursor: pointer; padding: 0.25rem; }
.ccheck__input { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.ccheck__box {
  flex: none; width: 22px; height: 22px;
  border: 1px solid var(--hairline); border-radius: var(--r-sm);
  display: grid; place-items: center; color: transparent;
  transition: background var(--dur-1) var(--ease-out), border-color var(--dur-1) var(--ease-out);
}
.ccheck__box svg { width: 13px; height: 13px; }
.ccheck:hover .ccheck__box { border-color: var(--ink); }
.ccheck__input:checked + .ccheck__box { background: var(--ink); border-color: var(--ink); color: var(--on-ink); }
.ccheck__input:focus-visible + .ccheck__box { outline: 2px solid var(--ink); outline-offset: 3px; }
.ccheck__text { color: var(--muted); font-size: var(--fs-mono-sm); line-height: 1.5; }
.ccheck__text a { color: var(--ink); text-decoration: underline; }

/* Banda de envío a sangre — vive FUERA de .container, así no hacen falta ni
   width:100vw ni márgenes negativos. El botón está fuera del <form> y se
   asocia con form="cform" (HTML estándar). */
.cform__band {
  position: relative;
  display: flex; flex-direction: column; align-items: center; gap: 0.75rem;
  margin-top: clamp(3rem, 7vw, 5.5rem);
  padding: clamp(1.5rem, 3vw, 2.25rem) var(--gutter);
}
.cform__band-dot {
  position: absolute; left: clamp(1rem, 4vw, 4.5rem); top: 50%;
  transform: translateY(-50%);
  width: 34px; height: 34px; border-radius: var(--r-round);
  border: 1px solid var(--hairline-dark);
  display: grid; place-items: center;
}
.cform__band-dot::after {
  content: ""; width: 9px; height: 9px;
  border-radius: var(--r-round); background: var(--neon);
}
.cform__band-btn {
  align-self: stretch; text-align: center;
  padding-block: clamp(0.75rem, 1.6vw, 1.25rem);
}
.cform__band-label {
  display: inline-block;
  font-size: var(--fs-mono); text-transform: uppercase; letter-spacing: 0.08em;
  transition: letter-spacing var(--dur-1) var(--ease-out);
}
.cform__band-btn:hover .cform__band-label { letter-spacing: 0.22em; }
.cform__band-btn.is-loading { opacity: 0.55; pointer-events: none; }

.contact__sent { display: flex; align-items: center; gap: 0.6rem; font-size: var(--fs-mono-sm); }
.contact__sent[hidden] { display: none; }
.contact__check { width: 1.35em; height: 1.35em; flex: none; color: var(--neon); }
.contact__check-tick { stroke-dasharray: 20; stroke-dashoffset: 20; }
.contact__sent.is-visible .contact__check-tick { animation: tick var(--dur-2) var(--ease-out) forwards 0.2s; }
@keyframes tick { to { stroke-dashoffset: 0; } }
.contact__error {
  color: var(--ink); background: var(--pink);
  padding: 0.4em 0.8em; border-radius: var(--r-sm);
  font-size: var(--fs-mono-sm); text-align: center;
}
.contact__error[hidden] { display: none; }
.btn-pill.is-loading { opacity: 0.55; pointer-events: none; }

/* A ≤1100px el aside baja y el bento se queda con el ancho completo: las tres
   columnas siguen cabiendo. Solo a ≤820px se apila del todo. */
@media (max-width: 1100px) {
  .contact__layout { grid-template-columns: 1fr; }
  .contact__main { order: 1; }
  .contact__direct { order: 2; }
}
@media (max-width: 820px) {
  .cform__bento { grid-template-columns: 1fr; }
  .cbox--grow { flex: none; }
  .cform .textarea { min-height: 7rem; }
  .cform__band-dot { display: none; }
}

/* ─── 22. Footer (cortina con vídeo + wordmark gigante) ──────────────────── */
/* El main lleva z-index y fondo propios: al llegar al final, el contenido se
   desliza por encima del footer fijo (sticky bottom) y lo va destapando. */

body { background: var(--ink); }
main { position: relative; z-index: 2; background: var(--canvas); }
.hero + * { position: relative; }

.footer {
  position: sticky; bottom: 0; z-index: 1;
  background: var(--ink);
  color: var(--on-ink);
  overflow: clip;
  padding-block: clamp(3rem, 7vh, 5rem) 1.5rem;
  min-height: min(92svh, 60rem);
  display: flex; flex-direction: column; justify-content: flex-end;
  isolation: isolate;
}

.footer__mark { font-size: clamp(3rem, 12.5vw, 15rem); line-height: 0.8; color: var(--on-ink); display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.2em; }
.footer__grid {
  margin-top: clamp(2rem, 5vh, 3.5rem);
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 2rem;
  border-top: 1px solid var(--hairline-dark);
  padding-top: 2rem;
}
.footer__col { display: flex; flex-direction: column; gap: 0.5rem; }
.footer__col-title { color: var(--muted); margin-bottom: 0.5rem; }
.footer__col a { transition: color var(--dur-1) var(--ease-out); }
.footer__col a:hover { color: var(--neon); }
.footer__bottom {
  margin-top: 2.5rem; padding-top: 1.25rem;
  border-top: 1px solid var(--hairline-dark);
  display: flex; justify-content: space-between; gap: 1rem; flex-wrap: wrap;
  color: var(--muted); font-family: var(--font-mono); font-size: var(--fs-mono-sm); text-transform: uppercase;
}
.footer__bottom a:hover { color: var(--neon); }
@media (max-width: 800px) { .footer__grid { grid-template-columns: 1fr 1fr; } .footer { position: relative; min-height: 0; } }
@media (prefers-reduced-motion: reduce) { .footer { position: relative; min-height: 0; } }

/* ─── 23. Páginas legales ────────────────────────────────────────────────── */

.legal { padding-block: calc(var(--nav-h) + var(--section)) var(--section); }
.legal .container { max-width: 46rem; }
.legal h1 { font-family: var(--font-display); font-weight: 900; text-transform: uppercase; font-size: var(--fs-title); line-height: 0.9; margin-bottom: 2.5rem; }
.legal h2 { font-family: var(--font-body); font-weight: 500; text-transform: uppercase; font-size: var(--fs-list); letter-spacing: -0.015em; margin: 2.5rem 0 1rem; }
.legal p, .legal li { color: var(--muted); margin-bottom: 0.9rem; }
.legal ul, .legal ol { list-style: disc; padding-left: 1.2em; margin-bottom: 1rem; }
.legal a { color: var(--ink); text-decoration: underline; text-underline-offset: 3px; }
.legal table th, .legal table td { border: 1px solid var(--hairline); padding: 0.7rem; font-size: var(--fs-mono-sm); text-align: left; }

/* ─── 24. Motion extra: drift, líneas, roll-hover ────────────────────────── */

/* Desplazamiento horizontal ligado al scroll (JS escribe el transform) */
[data-drift] { will-change: transform; }

/* Párrafos que aparecen línea a línea (rotación sutil desde la base) */
.line { display: block; overflow: clip; }
.line-in {
  display: block;
  transform: translateY(130%) rotate(1.8deg);
  transform-origin: 0 100%;
  transition: transform 0.8s var(--ease-hobro);
}
.is-in > .line > .line-in, .is-in .line-in {
  transform: none;
  transition: transform 1s var(--ease-hobro);
  transition-delay: var(--ld, 0s);
}
@media (prefers-reduced-motion: reduce) { .line-in { transform: none !important; transition: none !important; } }

/* Texto que rueda al hover (Let's Talk ↔ Ping Us) */
.roll { display: inline-grid; overflow: clip; }
.roll__a, .roll__b {
  grid-area: 1 / 1;
  white-space: nowrap;
  transition: transform 0.45s var(--ease-hobro);
}
.roll__b { transform: translateY(110%); }
a:hover .roll .roll__a, button:hover .roll .roll__a,
.roll-host:hover .roll__a { transform: translateY(-110%); }
a:hover .roll .roll__b, button:hover .roll .roll__b,
.roll-host:hover .roll__b { transform: none; }
@media (prefers-reduced-motion: reduce) {
  .roll__a, .roll__b { transition: none; }
  a:hover .roll .roll__a, button:hover .roll .roll__a { transform: none; }
  a:hover .roll .roll__b, button:hover .roll .roll__b { transform: translateY(110%); }
}

/* Los marquees reciben skew por velocidad de scroll (JS) */
.cta-marquee { will-change: transform; }

/* ─── 25. Galería de proyectos (preview flotante) ────────────────────────── */

.gal { position: relative; }
.gal__rows { border-top: 1px solid var(--hairline); }
.gal__row {
  display: grid; grid-template-columns: 4rem minmax(0, 1fr) auto auto; gap: 1.5rem;
  align-items: center;
  padding-block: clamp(1.5rem, 3.5vw, 2.5rem);
  border-bottom: 1px solid var(--hairline);
}
.gal__num { color: var(--muted); }
.gal__name {
  font-size: clamp(2rem, 5vw, 4.8rem);
  font-family: var(--font-display); font-weight: 900; text-transform: uppercase;
  line-height: 0.9; letter-spacing: -0.01em; color: var(--ink);
  transition: transform var(--dur-1) var(--ease-out), color var(--dur-1) var(--ease-out);
}
.gal__row:hover .gal__name { transform: translateX(0.6rem); }
.gal.is-hover .gal__row:not(:hover) .gal__name { color: var(--hairline); }
.gal__meta { color: var(--muted); text-align: right; }
.gal__visit {
  display: inline-flex; align-items: center;
  font-family: var(--font-mono); font-size: var(--fs-mono); text-transform: uppercase;
  border: 1px solid var(--hairline); border-radius: var(--r-chip); padding: 0.6em 1.2em;
  transition: background var(--dur-1) var(--ease-out), color var(--dur-1) var(--ease-out), border-color var(--dur-1) var(--ease-out);
}
.gal__visit:hover { background: var(--ink); color: var(--on-ink); border-color: var(--ink); }
.gal__thumb { display: none; }

.gal__preview {
  position: absolute; top: 0; left: 0; z-index: 20;
  width: min(28rem, 36vw); aspect-ratio: 2258 / 1080;
  pointer-events: none;
}
.gal__preview-in {
  position: absolute; inset: 0;
  border-radius: var(--r-media); overflow: clip;
  background: var(--ink);
  box-shadow: var(--shadow-pop);
  opacity: 0;
  transform: perspective(800px) rotateY(9deg) skewY(-10deg) scale(0.77);
  transition: opacity 0.2s var(--ease-out), transform 0.375s var(--ease-out);
}
.gal.is-hover .gal__preview-in {
  opacity: 1;
  transform: none;
  transition: opacity 0.25s var(--ease-out), transform 0.75s var(--ease-out);
}
.gal__preview [data-gal-media] {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: contain; object-position: center;
  opacity: 0; transition: opacity var(--dur-1) var(--ease-out);
}
.gal__preview [data-gal-media].is-on { opacity: 1; }

.gal__row { cursor: none; }

.gal__lightbox {
  position: fixed; inset: 0; z-index: 100;
  background: color-mix(in srgb, var(--ink) 92%, transparent);
  display: flex; align-items: center; justify-content: center;
  padding: clamp(1rem, 4vw, 2.5rem);
  opacity: 0; pointer-events: none;
  transition: opacity 0.28s var(--ease-out);
}
.gal__lightbox.is-open { opacity: 1; pointer-events: auto; }
.gal__lightbox-in {
  position: relative;
  display: flex;
  align-items: stretch;
  width: min(96vw, 78rem);
  max-height: 88vh;
  border-radius: var(--r-media);
  overflow: clip;
  background: var(--ink);
  box-shadow: var(--shadow-pop);
  transform: scale(0.94);
  transition: transform 0.36s var(--ease-out);
}
.gal__lightbox.is-open .gal__lightbox-in { transform: scale(1); }
.gal__lightbox-media {
  position: relative;
  flex: 1.5 1 0%;
  min-width: 0;
  aspect-ratio: 2258 / 1080;
  max-height: 88vh;
  background: var(--ink);
}
.gal__lightbox-in video {
  width: 100%; height: 100%;
  object-fit: contain; object-position: center;
  display: block;
}
.gal__lightbox-case {
  flex: 1 1 0%;
  min-width: 18rem;
  max-width: 26rem;
  padding: clamp(1.5rem, 3vw, 2.5rem);
  color: var(--on-ink);
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
}
.gal__case-name {
  font-size: clamp(1.5rem, 2.6vw, 2.2rem);
}
.gal__case-meta { color: var(--hairline); margin-top: -0.75rem; }
.gal__case-block { display: flex; flex-direction: column; gap: 0.5rem; }
.gal__case-label { color: var(--hairline); }
.gal__case-value { color: var(--on-ink); line-height: 1.4; }
.gal__case-chips { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.gal__case-chip {
  display: inline-flex; align-items: center;
  border: 1px solid var(--hairline-dark);
  border-radius: var(--r-chip);
  padding: 0.4em 0.9em;
  color: var(--on-ink);
  white-space: nowrap;
}
.gal__case-block--result { margin-top: 0.25rem; }
.gal__case-result {
  font-size: clamp(1.8rem, 3.4vw, 2.8rem);
  color: var(--neon);
}
.gal__case-fecha { color: var(--hairline); margin-top: auto; padding-top: 0.5rem; }
.gal__case-visit {
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--on-ink); border-radius: var(--r-chip);
  padding: 0.7em 1.4em;
  color: var(--on-ink);
  transition: background var(--dur-1) var(--ease-out), color var(--dur-1) var(--ease-out), border-color var(--dur-1) var(--ease-out);
}
.gal__case-visit:hover { background: var(--neon); color: var(--ink); border-color: var(--neon); }
.gal__case-visit[hidden] { display: none; }
.gal__lightbox-close {
  position: absolute; top: -3rem; right: 0;
  background: transparent; border: 1px solid var(--on-ink);
  color: var(--on-ink);
  width: 2.4rem; height: 2.4rem; border-radius: 999px;
  font: inherit; font-size: 1.2rem; line-height: 1;
  cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  transition: background var(--dur-1) var(--ease-out), color var(--dur-1) var(--ease-out);
}
.gal__lightbox-close:hover { background: var(--on-ink); color: var(--ink); }
@media (prefers-reduced-motion: reduce) {
  .gal__lightbox, .gal__lightbox-in { transition: none; }
  .gal__lightbox-in { transform: none; }
}

@media (max-width: 900px), (hover: none) {
  .gal__preview { display: none; }
  .gal__row { grid-template-columns: 1fr auto; row-gap: 0.75rem; }
  .gal__num, .gal__meta { display: none; }
  .gal__thumb { display: block; grid-column: 1 / -1; border-radius: var(--r-media); overflow: clip; aspect-ratio: 16 / 9; }
  .gal__thumb img, .gal__thumb video { width: 100%; height: 100%; object-fit: cover; object-position: top; }
}

/* ─── 26. Cómo trabajamos contigo (tarjetas apiladas, patrón stack de PX PUSH) ─ */

.pstack__list { display: flex; flex-direction: column; gap: clamp(1.5rem, 4vw, 3rem); }
.pcard {
  position: sticky;
  top: calc(var(--nav-h) + 1.5rem);
  border-radius: var(--r-card);
  border: 1px solid var(--hairline);
  background: var(--canvas);
  min-height: 22rem;
  padding: clamp(1.5rem, 3vw, 2.75rem);
  display: flex; flex-direction: column; justify-content: space-between; gap: 2rem;
  box-shadow: var(--shadow-pop);
}
.pcard:nth-child(2) { background: var(--ink); border-color: var(--ink); color: var(--on-ink); }
.pcard:nth-child(3) { background: var(--magenta); border-color: var(--magenta); }
.pcard:nth-child(4) { background: var(--neon); border-color: var(--neon); }
.pcard__top { display: flex; justify-content: space-between; gap: 1rem; color: inherit; }
.pcard__top .t-mono { opacity: 0.6; }
.pcard__title {
  font-family: var(--font-display); font-weight: 900; text-transform: uppercase;
  font-size: clamp(2.2rem, 5.5vw, 5rem); line-height: 0.9; letter-spacing: -0.01em;
  max-width: 14ch;
}
.pcard__body { max-width: 44ch; font-size: clamp(1rem, 1.4vw, 1.2rem); }
@media (max-width: 800px) { .pcard { position: relative; top: 0; min-height: 0; } }

/* Compat footer legales (markup __brand + __cols antiguos) */
.footer__wordmark { font-family: var(--font-display); font-weight: 900; text-transform: uppercase; font-size: 2rem; }
.footer__tagline { color: var(--muted); margin-top: 0.5rem; }
.footer__cols { display: grid; grid-template-columns: repeat(3, 1fr); gap: 2rem; margin-top: 2.5rem; border-top: 1px solid var(--hairline); padding-top: 2rem; }
.footer__col-title { font-family: var(--font-mono); font-size: var(--fs-mono-sm); font-weight: 500; text-transform: uppercase; }
.footer__cols ul { display: flex; flex-direction: column; gap: 0.4rem; margin-top: 0.75rem; }
.footer__contact { display: flex; gap: 1.5rem; flex-wrap: wrap; margin-top: 2rem; }
.footer__contact-link { display: inline-flex; gap: 0.5rem; align-items: center; font-weight: 500; }
.footer__contact-link svg { width: 1em; height: 1em; }
.logo-bb { color: inherit; }

/* ═══ Anclas Fase 0 — lote 2026-07-24 (ver docs/superpowers/plans/2026-07-24-lote-redisenos/) ═══
   Cada chat de la Fase 2 escribe SOLO dentro de su ancla. No reordenar. */

/* ─── 26b. [Chat 4] WWD v2 — columnas + cuerdas ──────────────────────────── */

.wwd2__row { display: grid; grid-template-columns: repeat(4, 1fr); gap: 2rem; padding-block: 1.1rem; }
.wwd2__row--head { padding-block: 0 0.9rem; }
.wwd2__col { justify-self: start; background: var(--neon); color: var(--ink); padding: 0.1em 0.45em; border-radius: var(--r-sm); }

/* Item: copia el trato tipográfico + hover/hl del antiguo .wwd__item (purgado
   junto con el resto de §11 al integrar esta rama) para no depender de una
   clase t-list adicional en cada fila. */
.wwd2__item {
  font-family: var(--font-body);
  font-weight: 500;
  font-size: var(--fs-list);
  text-transform: uppercase;
  letter-spacing: -0.015em;
  line-height: 1.2;
  position: relative;
  transition: color var(--dur-1) var(--ease-out);
  cursor: pointer;
}
.wwd2__item:hover { color: var(--muted); }
.wwd2__item:focus-visible { outline: 1px solid var(--ink); outline-offset: 4px; }
.wwd2__item--hl { color: var(--ink); }
.wwd2__item--hl::after { content: "✺"; margin-left: 0.35em; color: var(--neon); -webkit-text-stroke: 1px var(--ink); }
.wwd2__item.is-tip { color: var(--ink); }

/* Cuerda de guitarra: hairline en reposo (§stroke-width:1) que la física en
   JS (initWwdStrings) curva hacia el cursor. Alto 40 para dar amplitud a la
   curva; márgenes negativos idénticos para que en reposo ocupe visualmente
   1-2px como un separador de fila normal. overflow:visible evita que el UA
   recorte la curva cuando el desplazamiento supera el alto del viewBox. */
.wwd2__string { display: block; width: 100%; height: 40px; margin-block: -19px; overflow: visible; }
.wwd2__string path { fill: none; stroke: var(--hairline); stroke-width: 1; transition: stroke 0.3s; }
.wwd2__string.is-live path { stroke: var(--ink); }

/* Popup explicativo "sigiloso" (click o hover parado ≥1s) — un solo nodo
   reutilizado en JS (initWwdTips), movido al item activo con appendChild. */
.wwd2__tip {
  position: absolute;
  left: 0;
  top: calc(100% + 0.7rem);
  z-index: 5;
  width: max-content;
  max-width: min(28ch, 80vw);
  padding: 0.85rem 1rem;
  background: var(--canvas);
  color: var(--body-c);
  border: 1px solid var(--ink);
  border-radius: var(--r-media);
  box-shadow: var(--shadow-pop);
  font-family: var(--font-body);
  font-size: 0.92rem;
  font-weight: 400;
  line-height: 1.45;
  text-transform: none;
  letter-spacing: 0;
  opacity: 0;
  transform: translateY(6px);
  pointer-events: none;
  transition: opacity 0.35s var(--ease-out), transform 0.35s var(--ease-out);
}
.wwd2__tip.is-on { opacity: 1; transform: none; pointer-events: auto; }
.wwd2__tip.is-right { left: auto; right: 0; }
.wwd2__tip-cat {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--fs-mono-sm);
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 0.35rem;
}
@media (prefers-reduced-motion: reduce) {
  .wwd2__tip { transition: none; transform: none; }
}

/* ─── 26c. [Chat 5] Approach v2 — tarjetas expandibles ───────────────────── */

/* ─── 26d. [Chat 6] CTA banda — ¿Tienes proyecto? + paréntesis ───────────── */

/* ─── 26e. [Chat 7] Timeline del proceso (aims) ──────────────────────────── */

.tl { position: relative; margin-top: clamp(3rem, 8vh, 5rem); padding-left: clamp(2.5rem, 6vw, 4rem); }
.tl__rail {
  position: absolute; left: clamp(0.6rem, 1.5vw, 1rem); top: 0; bottom: 0; width: 2px;
  background: linear-gradient(to bottom, transparent, var(--hairline-dark) 12%, var(--hairline-dark) 88%, transparent);
}
.tl__fill {
  /* El JS (initProcessTimeline) escribe height en px cada frame (scrub atado
     al scroll) — sin transition: la anima el propio rAF, no el navegador. */
  position: absolute; inset: 0 0 auto 0; height: 0;
  background: linear-gradient(to bottom, transparent, var(--neon));
}
.tl__item {
  display: grid; grid-template-columns: minmax(8rem, 0.8fr) minmax(0, 1.6fr);
  gap: 2rem; padding-block: clamp(2.5rem, 8vh, 5rem);
}
.tl__when { position: sticky; top: clamp(6rem, 22vh, 10rem); align-self: start; }
.tl__when h3 { font-size: clamp(2rem, 5vw, 4rem); color: var(--hairline); margin: 0; }
.tl__dot {
  /* Centrado sobre el rail: el rail vive a la izquierda del padding-left de
     .tl, así que se compensa con el mismo par de clamps en negativo. */
  position: absolute; top: 50%; transform: translateY(-50%);
  left: calc(clamp(0.6rem, 1.5vw, 1rem) - clamp(2.5rem, 6vw, 4rem) - 4px);
  width: 10px; height: 10px; border-radius: var(--r-round); background: var(--neon);
}
.tl__name {
  margin: 0 0 0.75rem;
  font-size: var(--fs-row); font-family: var(--font-display); font-weight: 900;
  text-transform: uppercase; line-height: 0.95;
}
.tl__desc { max-width: 44ch; color: var(--hairline); }
.tl__media {
  aspect-ratio: 4 / 3; max-width: 26rem;
  border: 1px solid var(--hairline-dark); border-radius: var(--r-media);
  display: grid; place-items: center; color: var(--hairline); margin-top: 1.25rem;
}

/* ─── 26g. [Chat 9] Herramientas — órbitas ───────────────────────────────── */
/* Radio: --r es un número sin unidad (27/40/50) que representa % del ancho
   de .orbits. Para el tamaño del anillo (inset/width) se multiplica por 1%
   (porcentaje, resuelve contra el contenedor). Para el translateX del sat
   NO podemos usar % (un % en translate() resuelve contra el tamaño del
   propio elemento trasladado, no contra el contenedor — por eso el % de
   --r no sirve ahí). Truco: .orbits es un query-container
   (container-type: inline-size), así --r * 1cqw da una LONGITUD real
   (no un %) igual al mismo porcentaje del ancho de .orbits, y esa unidad sí
   funciona dentro de translateX(). Verificado con getComputedStyle: ver
   informe de verificación del commit. */
.orbits {
  container-type: inline-size;
  container-name: orbits;
  position: relative;
  aspect-ratio: 1;
  width: min(100%, clamp(20rem, 58vmin, 34rem));
  margin-inline: auto;
  margin-top: clamp(2rem, 5vh, 3.5rem);
  display: grid;
  place-items: center;
}
.orbits__core { font-size: clamp(2.5rem, 6vw, 4.5rem); }
.orbits__ring {
  position: absolute;
  inset: calc(50% - (var(--r) * 1%));
  width: calc(var(--r) * 1% * 2);
  height: calc(var(--r) * 1% * 2);
  border: 1px solid var(--hairline);
  border-radius: 50%;
  /* (2026-09-08) Los tres anillos son cajas CUADRADAS concéntricas: el
     exterior, último en el DOM, tapaba el área de puntero de los dos
     interiores y solo se podía hacer hover en el anillo de fuera. El
     anillo no recibe puntero; lo reciben solo los satélites. */
  pointer-events: none;
}
.orbits__sat {
  position: absolute;
  left: 50%;
  top: 50%;
  pointer-events: auto;
  animation: orbit var(--dur) linear infinite;
  animation-direction: var(--dir, normal);
}
@keyframes orbit {
  from { transform: translate(-50%, -50%) rotate(var(--a0)) translateX(calc(var(--r) * 1cqw)) rotate(calc(-1 * var(--a0))); }
  to   { transform: translate(-50%, -50%) rotate(calc(var(--a0) + 360deg)) translateX(calc(var(--r) * 1cqw)) rotate(calc(-1 * (var(--a0) + 360deg))); }
}
/* Pausa global: los tres anillos son concéntricos, así que pausar solo el
   anillo bajo el ratón dejaba los otros dos girando — se veía incoherente.
   :has() detiene los tres satélites a la vez mientras el ratón está sobre
   cualquier badge (o lo tiene enfocado por teclado), y los tres reanudan
   juntos al salir. */
.orbits:has(.integr__badge:hover) .orbits__sat,
.orbits:has(.integr__badge:focus-visible) .orbits__sat { animation-play-state: paused; }
.integr__badge {
  cursor: default;
  transition: transform 0.25s var(--ease-out);
}
.integr__badge:hover { transform: scale(1.12); }
/* (2026-09-08) Badges de las órbitas más grandes (antes 1.35em ≈ 22px):
   ~36-48px según el ancho de .orbits (cqw = % del contenedor). */
.orbits .integr__badge { width: clamp(2.25rem, 7cqw, 3rem); height: clamp(2.25rem, 7cqw, 3rem); border-radius: var(--r-media); }
.integr__badge img { width: 58%; height: 58%; object-fit: contain; display: block; }
.orbits__tip {
  position: absolute;
  top: calc(100% + 0.5rem);
  left: 50%;
  translate: -50% 0;
  white-space: nowrap;
  font-size: var(--fs-mono-sm);
  background: var(--ink);
  color: var(--on-ink);
  padding: 0.35em 0.7em;
  border-radius: var(--r-chip);
  opacity: 0;
  pointer-events: none;
  z-index: 3;
  transition: opacity 0.25s var(--ease-out);
}
.orbits__sat:hover .orbits__tip,
.integr__badge:focus-visible + .orbits__tip,
.orbits__sat.is-tip .orbits__tip { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  .orbits__sat {
    animation: none;
    transform: translate(-50%, -50%) rotate(var(--a0)) translateX(calc(var(--r) * 1cqw)) rotate(calc(-1 * var(--a0)));
  }
}

/* ═══ [rebrief 06] quienes-somos.html — timeline de eje horizontal ═══════════
   Página nueva (docs/superpowers/plans/2026-07-25-lote-copy/chat-6-quienes-
   somos.md). Contenido de hitos y bios PENDIENTE de confirmación de Gonzalo —
   ver comentarios HTML en la propia página. Esta ancla es solo de esta
   página; no reordenar ni mezclar con las anclas Fase 0 de arriba. */

.qs-hero { padding-block: calc(var(--nav-h) + clamp(3rem, 8vh, 5rem)) clamp(1.5rem, 5vh, 3rem); }
.qs-hero__intro { max-width: 46ch; color: var(--muted); margin-top: 1.5rem; }

/* Historia guiada por un trazo (.qsline) — 6 paradas × 90vh = 540vh.
   90vh y no 100vh a propósito: la parada siguiente asoma lo justo para tirar
   del lector hacia abajo, sin llegar a compartir pantalla con la anterior
   (cada bloque de texto mide ~30vh).

   El trazo (viewBox + `d`) lo GENERA qslBuild() en main.js, en píxeles reales
   de la sección, para que la escala sea 1:1 y los rizos salgan circulares.
   El dibujado progresivo también lo lleva el JS: dasharray = longitud total y
   dashoffset de total→0. Aquí no se declara ni dasharray ni dashoffset a
   propósito — la versión anterior los ponía en CSS junto a pathLength="1" y
   vector-effect="non-scaling-stroke", y esa combinación, con la distorsión
   de preserveAspectRatio="none" (medida: 15.16 en x contra 7.80 en y),
   pintaba el trazo a trozos sueltos.
   `overflow-x: clip` es el cinturón de seguridad contra el scroll horizontal;
   el tirante es que las cajas de texto quepan (ver .qsline__step). */
.qsline { position: relative; overflow-x: clip; }
.qsline__svg {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  pointer-events: none;
  z-index: 0;
}
.qsline__path {
  fill: none;
  stroke: var(--neon);
  stroke-width: 4px;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.qsline__dot { fill: var(--neon); }

.qsline__steps { position: relative; z-index: 1; }
/* max-width en % y no en `ch`: una palabra que no puede partirse NO encoge por
   debajo de su ancho mínimo, así que un tope en ch más pequeño que el titular
   no lo contiene — lo desborda. Medido con 32ch: caja 341px contra 539px de
   «HORIZONTE», y el sobrante empujaba el documento a 1606px de ancho. */
.qsline__step {
  min-height: 90vh;
  max-width: min(46%, 44rem);
  display: flex; flex-direction: column; justify-content: center;
  gap: 0.9rem;
  opacity: 0;
  transform: translateY(28px);
}
/* El texto cae siempre al lado contrario de la cresta del trazo, así ni la
   línea ni su rizo pasan por debajo del párrafo que se está leyendo: parada
   impar (cresta a la izquierda) → texto a la derecha, y al revés. */
.qsline__step:nth-child(odd) { margin-left: auto; }
.qsline__step:nth-child(even) { margin-right: auto; }
.qsline__num { color: var(--muted); }
.qsline__word {
  margin: 0;
  font-size: var(--fs-title);
  font-weight: 900;
  line-height: 0.9;
  letter-spacing: -0.02em;
  color: var(--ink);
}
/* La caja es ancha para que quepa el titular; el párrafo se limita aparte para
   no quedarse con un renglón de 44rem, que sería ilegible. */
.qsline__body { margin: 0; color: var(--muted); max-width: 34ch; }

/* Fallback obligatorio: lista vertical normal, sin trazo. El JS comprueba la
   misma condición y no toca ningún estilo cuando aplica, así que basta con
   este bloque para dejarlo en reposo.
   OJO a la especificidad: .qsline__step:nth-child(odd) puntúa (0,2,0), así
   que un `.qsline__step` suelto aquí NO ganaría y los márgenes automáticos
   sobrevivirían. Por eso se repite el :nth-child(n), que empata a (0,2,0) y
   gana por orden. Es la misma trampa documentada para .xcard en el CLAUDE.md. */
@media (prefers-reduced-motion: reduce) {
  .qsline__svg { display: none; }
  .qsline__step:nth-child(n) {
    min-height: auto;
    max-width: 46ch;
    margin-inline: 0;
    opacity: 1;
    transform: none;
    padding-block: clamp(2rem, 6vh, 3.5rem);
  }
}

/* Cierre: tarjetas de personas (contenido real varía por página) */
.qs-team { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.25rem; margin-top: clamp(1rem, 3vw, 2rem); }
.qs-team__card { border: 1px solid var(--hairline); border-radius: var(--r-card); padding: clamp(1.5rem, 2.5vw, 2.25rem); display: flex; flex-direction: column; gap: 1.25rem; }
.qs-team__card .tl__media { margin-top: 0; }
.qs-team__name { font-size: clamp(1.5rem, 2.6vw, 2.1rem); }
.qs-team__role { color: var(--muted); }
.qs-team__bio { color: var(--muted); max-width: 38ch; }
.qs-team__contact { display: flex; flex-direction: column; gap: 0.35rem; margin-top: 0.25rem; }
@media (max-width: 800px) { .qs-team { grid-template-columns: 1fr; } }

/* ─── 27. Móvil ≤640px — recalibración editorial ─────────── */
/* Bajamos los floors de los clamps de tipografía (tokens) y afinamos
   padding/gap por sección para devolver escala editorial (revista) en vez
   de "cartel/highway" en pantallas pequeñas. */

@media (max-width: 640px) {

  :root {
    --fs-hero: clamp(1.75rem, 8vw, 2.4rem);
    --fs-title: clamp(1.5rem, 6.4vw, 2rem);
    --fs-display: clamp(1.65rem, 6.6vw, 2.15rem);
    --fs-row: clamp(1.25rem, 5.2vw, 1.8rem);
    --fs-cursive: clamp(1.2rem, 4.8vw, 1.7rem);
    --fs-list: 0.95rem;
    --fs-mono: 0.8125rem;
    --fs-mono-sm: 0.6875rem;
    --section: clamp(1.75rem, 5vw, 2.5rem); /* (2026-09-08) a la mitad: había demasiado aire entre secciones */
    --gutter: 1.25rem;
  }

  /* Hero (§6) */
  .hero { padding-block: calc(var(--nav-h) + 1.25rem) 1.5rem; }
  .hero__inner { gap: 1rem; }
  .hero__title { white-space: normal; letter-spacing: -0.015em; }
  .hero__id { gap: 0.25rem; }
  .hero__id-meta { font-size: 0.7rem; }
  .hero__sub { flex-direction: column; row-gap: 0.45rem; white-space: normal; font-size: 0.72rem; }
  .hero__sub span + span::before { display: none; }

  /* .mark — títulos de sección (§9b) */
  .mark { min-height: clamp(3rem, 9.5vw, 4.2rem); }
  .mark__ghost { font-size: clamp(2rem, 7.6vw, 3.2rem); left: 0; top: 0.1em; }
  .mark__solid { font-size: clamp(1.55rem, 6vw, 2.3rem); margin-left: clamp(1rem, 7vw, 2.6rem); }
  #integraciones .mark__solid { font-size: clamp(1.4rem, 5.4vw, 2.1rem); }
  #problem-title .mark__solid { font-size: clamp(1.15rem, 5.6vw, 2.3rem); }

  /* Statement (§7) */
  .statement__label { margin-bottom: 1.5rem; }
  .statement__text { max-width: 18ch; }
  .statement__para { margin-top: 2rem; gap: 1.25rem; }
  .statement__para p { font-size: 0.98rem; }
  .statement__words { margin-top: 2.5rem; column-gap: 0.6rem; row-gap: 0.3rem; }
  .chip { font-size: 0.72rem; padding: 0.35em 0.85em; }

  /* CTA marquee (§8) */
  .cta-marquee { padding-block: 1rem; }
  .cta-marquee__big, .cta-marquee__cursive { font-size: 1.35rem; }
  .cta-marquee__arrow { font-size: 1rem; }

  /* Section head (§9) */
  .sec__label { margin-bottom: 1rem; }
  .sec__head { margin-bottom: 1.75rem; }

  /* Rows (§10) y approach (§12) */
  .rows__item { grid-template-columns: 2.5rem 1fr; gap: 1rem; }
  .rows__detail { font-size: 0.9rem; }
  .rows__close { margin-top: 2rem; font-size: 1rem; }
  .approach__row { grid-template-columns: 2.5rem 1fr; gap: 1rem; padding-block: 1.25rem; }
  .approach__desc { font-size: 0.9rem; }

  .btn-pill { padding: 0.75em 1.5em; }

  /* Casos apilados (§14) */
  .case-card { min-height: 60svh; }
  .case-card__top, .case-card__bottom { padding: 1rem 1.15rem; }
  .case-card__name { font-size: clamp(1.5rem, 5.6vw, 2.2rem); }
  .case-card__desc { font-size: 0.75rem; }

  /* Aims (§15) */
  .aims__grid { margin-top: 2rem; padding-top: 1.5rem; gap: 1.25rem; }
  .aims__cell-new { font-size: 1.3rem; }
  .aims__note { font-size: 0.85rem; margin-top: 1.25rem; }

  /* Planes (§16) y mantenimiento (§17) */
  .tier { padding: 1.5rem 1.35rem; gap: 0.9rem; }
  .tier__price { font-size: clamp(2rem, 7vw, 2.8rem); }
  .tier__badge { top: -0.8rem; font-size: 0.65rem; padding: 0.35em 0.9em; }
  .maint__card { padding: 1.5rem 1.35rem; gap: 0.9rem; }
  .maint__price { font-size: clamp(2rem, 7vw, 2.8rem); }
  .maint__argument { font-size: 0.95rem; margin-top: 1.75rem; }
  .tiers__note { font-size: 0.9rem; margin-top: 1.5rem; }

  /* Partners/integraciones (§18) */
  .partners__cats { gap: 0.9rem; margin-bottom: 1.5rem; }
  .partners__list { gap: 0.35rem 1.1rem; }
  .integr__chip { padding: 0.5em 1em; }

  /* Voces (§19) */
  .voice { padding: 1.5rem 1.35rem; gap: 1.25rem; }
  .voice__quote { font-size: 1rem; line-height: 1.4; }
  .voice__who strong { font-size: 1rem; }

  /* FAQ (§20) */
  .faq__q { padding-block: 1.1rem; grid-template-columns: 1fr 2rem; gap: 0.75rem; }
  .faq__plus { width: 1.9rem; height: 1.9rem; }
  .faq__a p { font-size: 0.9rem; padding-bottom: 1.1rem; }

  /* Galería (§25) */
  .gal__row { padding-block: 1.5rem; gap: 0.75rem; }
  .gal__name { font-size: clamp(1.55rem, 6vw, 2.3rem); }
  .gal__visit { font-size: 0.7rem; padding: 0.5em 1em; }
  .gal__lightbox-in { flex-direction: column; width: min(94vw, 28rem); max-height: 92vh; overflow-y: auto; }
  .gal__lightbox-media { flex: none; width: 100%; max-height: 42vh; }
  .gal__lightbox-case { max-width: none; min-width: 0; max-height: none; overflow-y: visible; }
  .gal__case-name { font-size: clamp(1.3rem, 5.4vw, 1.7rem); }
  .gal__case-result { font-size: clamp(1.5rem, 6.5vw, 2rem); }
  .gal__case-chip { font-size: 0.72rem; padding: 0.35em 0.8em; }

  /* Cómo trabajamos contigo (§26) */
  .pcard { padding: 1.5rem 1.35rem; gap: 1.25rem; }
  .pcard__title { font-size: clamp(1.7rem, 6.4vw, 2.4rem); max-width: 12ch; }
  .pcard__body { font-size: 0.95rem; }

  /* Footer (§22) */
  .footer { padding-block: 2.5rem 1.25rem; }
  .footer__mark { font-size: clamp(2rem, 10vw, 3.4rem); line-height: 0.85; gap: 0.15em; }
  .footer__grid { margin-top: 1.75rem; padding-top: 1.5rem; gap: 1.5rem 1rem; }
  .footer__col-title { font-size: 0.7rem; }
  .footer__col a { font-size: 0.9rem; }
  .footer__bottom { margin-top: 1.75rem; padding-top: 1rem; gap: 0.5rem; }

  /* Container */
  .container { padding-inline: 1.25rem; }

  /* ── Anclas móviles Fase 0 (lote 2026-07-24) — cada chat solo la suya ── */

  /* [Chat 4] wwd v2 móvil — el brief da dos anchos ("1fr 1fr" y "1 columna")
     para ≤640, pero no hay ancla ≤800 disponible en este archivo: se
     interpreta como dos tramos dentro de este mismo bloque consolidado —
     2 columnas hasta 480px, 1 columna + padding más ajustado por debajo. */
  .wwd2__row { grid-template-columns: 1fr 1fr; gap: 1rem; }
  /* La cabecera solo tiene sentido con las 4 columnas: por debajo las
     etiquetas dejarían de caer sobre su columna y engañarían. */
  .wwd2__row--head { display: none; }
  .wwd2__tip { max-width: min(32ch, calc(100vw - 2 * var(--gutter))); }
  @media (max-width: 480px) {
    .wwd2__row { grid-template-columns: 1fr; padding-block: 0.8rem; }
  }

  /* [Tarea 1] mazo lateral móvil: el fallback sin sticky/desplazamiento ya
     lo cubre @media (max-width: 800px) junto a .xcard (arriba) — aquí solo
     el ajuste fino de espaciado bajo 640px. */
  .xcard { padding: 1rem; }
  .xcard__desc { font-size: 0.9rem; max-width: none; }

  /* [Chat 6] cta banda móvil -- sigue a 100vh/100svh (regla base), aquí solo
     se reajustan gutters y escalas; el centrado vertical ya lo da el flex
     de .ctaband, así que el padding vuelve simétrico. */
  .ctaband { padding: clamp(2rem, 6vh, 3rem) 1.25rem; }
  .ctaband__ask { font-size: clamp(1.1rem, 4.5vw, 1.4rem); margin-bottom: 0.75rem; }
  .ctaband__center { gap: clamp(0.9rem, 4vw, 1.5rem); }
  .ctaband__parens { width: clamp(0.75rem, 3.5vw, 1.35rem); height: clamp(2.55rem, 12.8vw, 4.2rem); }
  .ctaband__arc { width: clamp(0.75rem, 3.5vw, 1.35rem); height: clamp(2.55rem, 12.8vw, 4.2rem); }
  /* font-size con curva propia, no la del desktop: el suelo desktop
     (2.75rem/44px) verificado con Chrome headless SOLO es seguro desde
     ~370px hacia arriba -- por debajo (320px, iPhone SE) "ESCRÍBENOS" +
     los 20px de padding fijo + arcos/gap ya no caben y se recorta contra
     el overflow:clip de .ctaband. Con esta curva, a 320px da ~34px (cabe
     con ~5px de margen) y a 640px ~67px (empalma razonablemente con los
     ~75px del desktop justo por encima del breakpoint). */
  .ctaband__swap { font-size: clamp(1.9rem, 10.5vw, 4.5rem); white-space: normal; line-height: 1.05; }

  /* CTA «Arrancamos» móvil — el ancho ya es fluido (clamp+vw, sin
     height fija: aspect-ratio la deriva), solo se ajusta el respiro y la
     etiqueta. El paso a una sola columna (palabras arriba, CTA debajo) ya
     lo cubre @media (max-width: 900px) junto a .ellcta-wrap (§13b), esto
     solo afina el espaciado bajo 640px. */
  .ellcta-wrap { padding-block: clamp(2.5rem, 9vw, 4rem); gap: clamp(1rem, 4vw, 1.75rem); }
  .ellcta__label { font-size: clamp(0.62rem, 2.6vw, 0.85rem); }

  /* [Chat 7] timeline móvil */
  .tl { padding-left: 1.75rem; margin-top: 2.5rem; }
  .tl__rail { left: 0.4rem; }
  .tl__item { grid-template-columns: 3.5rem minmax(0, 1fr); gap: 1rem; padding-block: 2rem; }
  .tl__when { top: 1.25rem; }
  .tl__when h3 { font-size: clamp(1.5rem, 7vw, 2.2rem); }
  .tl__dot { left: calc(0.4rem - 1.75rem - 4px); }
  .tl__desc { max-width: 32ch; font-size: 0.9rem; }
  .tl__media { max-width: 100%; margin-top: 1rem; }

  /* [Chat 9] órbitas móvil */
  .orbits { width: min(100%, 86vw); }
  /* Override de tamaño (no toca .integr__badge de §18): usa cqw del propio
     .orbits para que los 14 badges no se amontonen bajo 400px, sin
     necesitar un breakpoint anidado extra. */
  .orbits .integr__badge { width: clamp(1.75rem, 10cqw, 2.5rem); height: clamp(1.75rem, 10cqw, 2.5rem); font-size: clamp(0.6em, 3.4cqw, 0.8em); }

  /* [rebrief 06] quienes-somos móvil — mismo fallback que reduced-motion:
     lista vertical normal, sin sticky ni translate. */
  .qs-hero { padding-block: calc(var(--nav-h) + 1.5rem) 1rem; }
  .qsline__svg { display: none; }
  .qsline__step:nth-child(n) {
    min-height: auto;
    max-width: none;
    margin-inline: 0;
    opacity: 1;
    transform: none;
    padding-block: 2rem;
  }
  .qsline__word { font-size: clamp(2rem, 11vw, 3rem); }
  .qs-team { grid-template-columns: 1fr; gap: 1rem; margin-top: 0.75rem; }
  .qs-team__card { padding: 1.5rem 1.35rem; gap: 1rem; }

}
