/* ============================================================
   Caliber Studio — v3
   Todo lo que hay aquí sale de medición, no de gusto:
   · pesos 100-400 (ninguna referencia usa 600 como display)
   · UNA voz grande y caída de ~1.6x por peldaño (no la meseta 83·82·69·67)
   · duraciones 0.15-0.3s (las refs miden eso; la v2 iba a 0.4-0.5s)
   · tracking negativo SOLO arriba; peso ligero pide tracking positivo
   ============================================================ */

/* ---------- 1 · TOKENS ---------- */
:root{
  /* Papel — nunca blanco de sistema (benorth #F4F3EF, rideradian #F5F3EF) */
  --papel:      #F4F1EA;
  --papel-alto: #EAE6DC;
  --papel-frio: #ECEDEF;

  /* Oscuro — las 4 superficies del portal, que ya están aprobadas */
  --tinta:   #08090C;
  --sup-1:   #101216;
  --sup-2:   #181B21;
  --sup-3:   #1F232B;

  /* Texto por rampa de opacidad (método del portal), no por 8 grises sueltos */
  --claro-1: rgba(9,10,13,.92);
  --claro-2: rgba(9,10,13,.60);
  --claro-3: rgba(9,10,13,.50);   /* mismo motivo que --osc-3, lado claro */
  --osc-1:   rgba(237,239,243,1);
  --osc-2:   rgba(237,239,243,.62);
  /* .40 daba 3.42 de contraste sobre el fondo #08090C — bajo el 4.5 de AA, y
     este token viste rótulos de 12px, que es justo donde menos se perdona.
     Medido compuesto sobre el fondo (el alfa hay que aplicarlo ANTES de medir;
     ignorarlo devuelve 17.3 y parece que todo está bien). A .50 da 4.76. */
  --osc-3:   rgba(237,239,243,.50);

  /* El único calor. Ya existe y ya lo ven los clientes en el portal */
  --oro:     #D8C58C;
  --oro-vivo:#C6A961;
  --bronce:  #7A5F1F;   /* el oro, pero legible como TEXTO sobre papel (4.84) */

  --linea-clara: rgba(9,10,13,.12);
  --linea-osc:   rgba(255,255,255,.12);

  /* Tipografía — la MISMA del portal. Un cliente que ve el portal y el sitio
     debe sentir una sola marca; eso vale más que estrenar una cara.
     Todo el carácter sale de cómo se usan, no de cuáles son. */
  /* ---- LA REVISIÓN DE AGO-2026 · UNA SOLA VOZ ----------------------------
     Censo en vivo de siete referencias (Hermès, Aesop, Kinfolk, Pentagram,
     Hodinkee, Cuberto, Apple) contra este sitio. Tres hallazgos, y los tres
     dicen lo mismo:
     · Nadie usa una DISPLAY como voz del sitio. Cuberto va con Suisse Intl,
       Aesop con Suisse, Pentagram con Plain, Apple con SF. El carácter sale
       del tamaño, el aire y el movimiento — no de la cara.
     · Clash Display en 200 junto a 650 dentro de la MISMA frase es el gesto
       tipográfico más replicado de los dos últimos años. Lee a «drop de moda»,
       no a estudio.
     · Los pesos del lujo: Kinfolk 259 de 263 nodos en 400. Pentagram 433 de
       437. Hermès 400/300. Este sitio tenía SEIS pesos y 113 de 176 nodos en
       500 — el token de interfaz vistiendo la página entera.
     Switzer (Fontshare, misma licencia y mismo CDN que ya se carga) es una
     grotesca neutra con 9 pesos y buenas versalitas. Hace de display Y de
     texto: una familia, un peso.
     Clash Display NO se va del todo — se queda EXCLUSIVAMENTE en el logotipo
     (`--marca`), que es reconocimiento de marca y no composición. */
  --display: 'Switzer', 'General Sans', system-ui, sans-serif;
  --texto:   'Switzer', 'General Sans', system-ui, sans-serif;
  --marca:   'Clash Display', 'Switzer', system-ui, sans-serif;

  /* Escala CON CAÍDA. Cinco peldaños, razón ~1.6 */
  /* UNA sola voz grande — y ahora de verdad. El token decía 132px pero el hero
     lo pisaba con su propio `clamp` (86px medidos), así que el sitio tenía DOS
     escalas grandes y la del cierre gritaba 50% más que la de la apertura. El
     token toma el valor que ya funcionaba en el hero y el override se fue: el
     cierre saca su fuerza de estar solo en una pantalla oscura, no de ser más
     grande que el titular con el que abre. */
  /* ---- LA ESCALA, REHECHA · SEIS PELDAÑOS Y EL LUJO SUSURRA -------------
     El token declaraba cinco peldaños y la página RENDERIZABA DIECISIETE: los
     overrides locales no se ven leyendo `:root`, sólo midiendo lo pintado.
     Aesop renderiza 6 tamaños, Hermès 7, Pentagram 4.
     Y el titular baja de 86 a 62. Medido: Hermès titula a 34px, Pentagram a
     52, Hodinkee a 51, Kinfolk a 60. Los que gritan a 85 son las AGENCIAS
     creativas (Cuberto 85, Obys 84), que venden otra cosa. El lujo susurra.
     El cuerpo sube de 13 a 17: el tamaño más usado del sitio era 12px y el de
     Cuberto 24.2 — la mitad del texto de aquí vivía en tamaños de interfaz,
     y ESA es la sensación de tosco. */
  /* 🔴 EL MÍNIMO DEL `clamp` ES LA MEDIDA DEL TELÉFONO, y estaba escrita
     pensando en el escritorio. Medido a 390px: el titular más grande de todo
     el sitio daba 32px contra los 62 de Cuberto móvil — un contraste de escala
     de 1.9×, que es casi no tener jerarquía. En escritorio el sistema va a
     3.6× con dos excepciones a 6.7 y 11.2; el teléfono no tenía ninguna.
     El impacto en móvil se RE-COMPONE, no se encoge. */
  --t1: clamp(3.25rem, 4.4vw, 3.875rem); /* 52px en teléfono · 62 arriba */
  --t2: clamp(2.5rem, 3.4vw, 3rem);      /* 40 en teléfono · 48 arriba */
  --t3: clamp(1.375rem, 1.9vw, 1.75rem);/* 28  · nombre de caso, subtítulo */
  --t4: clamp(1.125rem, 1.3vw, 1.1875rem);/* 19 · párrafo grande */
  --t5: 1.0625rem;                      /* 17  · cuerpo e interfaz */
  --t6: .875rem;                        /* 14  · rótulo y pie legal. PISO. */

  /* ---- PESO — la palanca que estaba apagada -----------------------
     Censo del sitio antes de esto: el display vivía en 200/300/400 y el
     tope de TODA la página era 500. Cero negritas. Con siete archivos
     estáticos no había de dónde: la variable ahora da 200–700 continuo.
     Y el eje del peso es lo que permite jerarquía SIN subir el tamaño —
     que es el problema real, porque medido en vivo el sitio renderiza
     15 escalones de tamaño cuando la mediana de las casas de lujo es 5.
     Nombres por FUNCIÓN, no por número: así el rol no se puede usar mal.

     ⚠️ REVISADO AGO-2026 — LA PALANCA SE APAGA A PROPÓSITO. El párrafo de
     arriba defiende abrir la brecha de PESO, y sigue siendo cierto para un
     sitio de agencia; no lo es para el registro que paco quiere. En el censo
     de las casas de lujo el peso NO es una palanca: es una constante. Kinfolk
     259 de 263 nodos en 400. Pentagram 433 de 437. Aesop 400. Hermès 400 con
     300 de apoyo. **El énfasis del lujo se hace con TONO, no con peso** — y
     eso es lo que ahora hace `.golpe`.
     Los nombres se quedan por si algún día se vuelve atrás, pero hoy los cinco
     valen 400. Un peso. */
  --p-aire:   400;
  --p-voz:    400;
  --p-cuerpo: 400;
  --p-ui:     400;
  --p-golpe:  400;
  /* --- lo que antes hacía el peso, ahora lo hace el tono ---------------
     El titular entra en un gris de apoyo y el REMATE es el que llega a
     opacidad plena. Los valores no son de gusto: son los que dejan el titular
     por encima de 3:1 (mínimo AA para texto grande) en los DOS mundos.
     Medido compuesto sobre su fondo — el alfa hay que aplicarlo ANTES de
     medir: oscuro .58 → 5.9:1 · papel .78 → 3.7:1. Con el .60 de `--tx-2`
     el papel daba 2.3 y se caía por debajo del mínimo.
     Los valores reales viven en las clases de tono (§3), que es donde se
     declara qué significa cada token en cada mundo. */

  /* Movimiento */
  --rapido: .16s;
  --base:   .24s;
  --lento:  .32s;
  --curva:  cubic-bezier(.23,1,.32,1);

  /* CAPA 2 · ago-2026 — el mínimo ERA 1.25rem = 20px, fuera del 8-point. Es el
     margen lateral de TODA la página en teléfono, así que era el valor fuera de
     grid con más superficie del sitio. A 1.5rem el contenido a 390 pasa de 350
     a 342px de ancho: se paga barato. El máximo (5rem = 80) ya cumplía. */
  --borde: clamp(1.5rem, 5vw, 5rem);
  --aire:  clamp(5rem, 12vh, 11rem);

  /* ---- C1 · EL GRANO QUE NUNCA EXISTIÓ ----------------------------
     El CSS lo invocaba desde el día uno y el token jamás se definió:
     `background-image:var(--grano)` resolvía a `none`. Eran 8,887px de
     papel absolutamente liso — ESA era la planitud, no el gusto.
     Va como feTurbulence en data-URI y no como PNG: ~500 bytes, misma
     nitidez a cualquier densidad de pantalla, cero peticiones. */
  --grano: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23g)'/%3E%3C/svg%3E");

  /* ---- C5 · LA SEGUNDA CAPA FIJA ----------------------------------
     Medido: basement 8 capas fijas, obys 3, lusion 2 — la v3 tenía UNA.
     Con `background-attachment:fixed` la rejilla se queda quieta y el
     contenido se desliza encima: es paralaje real sin JS y, a diferencia
     de animar background-position (lo que hace basement), no repinta
     una sección de 3,400px en cada cuadro. */
  --rejilla:
    linear-gradient(rgba(9,10,13,.055) 1px, transparent 1px),
    linear-gradient(90deg, rgba(9,10,13,.055) 1px, transparent 1px);
}

*,*::before,*::after{ box-sizing:border-box }
html{ -webkit-text-size-adjust:100% }
body{
  margin:0; background:var(--papel); color:var(--tx-1);
  font-family:var(--texto); font-size:var(--t5); font-weight:var(--p-ui);
  line-height:1.6; -webkit-font-smoothing:antialiased;
  overflow-x:hidden;
}
/* `height:auto` es parte del reset, no un adorno: sin él, el atributo
   `height` del HTML se queda fijo mientras `max-width` sí encoge el ancho, y
   la imagen se estira. Salió a la luz con la captura del panel: 741px de
   ancho con 1350 de alto en vez de 417. */
img,video,canvas{ max-width:100%; height:auto; display:block }
a{ color:inherit; text-decoration:none }

/* ---------- 2 · TIPOGRAFÍA ---------- */
/* El titular entra en el gris de apoyo y el REMATE (`.golpe`) es el que llega
   a opacidad plena. Antes era al revés —todo a tope y el remate en peso 650—,
   que es el gesto de la moda, no el del lujo. */
.d1,.d2,.d3{ font-family:var(--display); font-weight:var(--p-voz); margin:0;
             color:var(--tx-titular) }
.d1{ font-size:var(--t1); line-height:1.03; letter-spacing:-.028em }
.d2{ font-size:var(--t2); line-height:1.06; letter-spacing:-.024em }
.d3{ font-size:var(--t3); line-height:1.15; letter-spacing:-.018em }
/* `.fino` se queda como alias VACÍO: lo usan tres titulares del HTML y quitar
   la clase de golpe es más ruido que dejarla sin efecto. Con un solo peso ya
   no hay «fino» que distinguir. */
.d1.fino{ letter-spacing:-.028em }

/* ---- EL GOLPE — el remate de la frase, en peso, no en tamaño ni en color
   Esto es lo que hacen las marcas y lo que el sitio no hacía en ninguna parte:
   dentro de UN titular, la parte que carga el sentido pesa más. El tamaño no
   cambia, el color no cambia, la familia no cambia — solo el peso. Ventajas
   contra las alternativas que ya estaban descartadas: no mete un color de
   acento en una identidad monocroma, no agrega un escalón a una escala que ya
   tiene 15, y no depende de la cursiva (medido: las 6 refs usan CERO).
   Sobre el tracking me equivoqué a la primera: cerré a -.045em pensando que a
   más masa hace falta menos aire, y a 86px "no tiene." se leyó **"notiene"** —
   el `letter-spacing` negativo encoge TAMBIÉN el espacio entre palabras, y al
   peso 650 las letras ya traen su propia masa, así que el espacio es lo primero
   que se pierde. Queda en -.02em con el `word-spacing` compensado: el remate se
   ve compacto sin que las palabras se peguen.

   ⚠️ REVISADO AGO-2026 · EL GOLPE YA NO PESA, ILUMINA. Todo lo de arriba
   describe un sistema de agencia, no de casa de lujo: en el censo de Hermès,
   Aesop, Kinfolk y Pentagram el peso es CONSTANTE y el énfasis se hace con
   tono (y a veces con cursiva). El remate se queda en el mismo peso, el mismo
   tamaño y la misma familia — lo único que cambia es que llega a opacidad
   plena mientras el resto del titular se queda en el gris de apoyo.
   Beneficio lateral: se va el `word-spacing` compensado, que existía sólo
   porque el `letter-spacing` negativo del peso 650 pegaba «no tiene». */
.golpe{ color:var(--tx-1) }

.cursiva{ font-style:italic; font-weight:var(--p-voz) }   /* el segundo registro */
/* ⚠️ EL RÓTULO BAJA DE LAS VERSALES. Medido: este sitio tenía 2.2 rótulos por
   cada frase con punto; Cuberto 0.5 y Apple 0.3 —y los 49 «rótulos» de Apple
   son los numeritos de nota legal—. Las etiquetas de Cuberto, textuales:
   `What we do`, `Why Cuberto`, `Main office`. Frases cortas en minúscula, no
   códigos. Cuando TODO va en versales con tracking abierto, ninguna versal
   significa nada: el recurso vuelve a valer sólo si se usa una vez por
   pantalla. Sube de 12 a 14px, que es el piso de la escala. */
.rotulo{
  font-family:var(--texto); font-size:var(--t6); font-weight:var(--p-ui);
  letter-spacing:0; text-transform:none;
  /* NO mono: la JetBrains uppercase es lo que leía a "coordenadas" */
  /* `case` — formas para caja alta. Sube el punto medio, los paréntesis y
     los signos de apertura a altura de mayúscula. En un rótulo en versalitas
     un `·` o un `¿` colgados a altura de minúscula se ven caídos, y es de
     esos detalles que nadie nombra pero todos registran.
     Verificado por píxel: Clash Display NO expone NINGUNA feature en la
     versión web de Fontshare; General Sans expone ss01, salt y case. Por eso
     esto solo puede vivir en los elementos puestos en General Sans. */
  font-feature-settings:'case' 1;
}
/* El párrafo grande se queda en 400: a 22px el 500 se apelmaza. El contraste
   NO se busca subiendo todo — se busca ABRIENDO LA BRECHA. Censo de las refs:
   hildenkaira 500×227 nodos, bymonolog 500×187, la v3 iba en 300×31 con TODO
   entre 200 y 400, o sea sin jerarquía. Ahora: display 200-300 · cuerpo 500 ·
   párrafo largo 400. Tres registros que se distinguen a un metro. */
.parrafo{ font-size:var(--t4); line-height:1.55; max-width:34ch; font-weight:var(--p-cuerpo) }
.tenue{ color:var(--tx-2) }
.oscuro .tenue{ color:var(--tx-2) }

/* ---------- 3 · RITMO TONAL ---------- */
.seccion{ padding:var(--aire) var(--borde); position:relative }
/* ── EL MUNDO TONAL ES UNA CLASE, NO UN REFACTOR ──────────────────────────
   Antes cada sección cableaba sus colores de texto: 25 usos de tokens claros y
   23 de oscuros repartidos por todo el archivo. Cambiar una sección de papel a
   tinta dejaba su texto ilegible, así que "comprometerse con un mundo tonal"
   costaba una tarde en vez de una palabra.
   Ahora la clase de tono declara qué significan --tx-1/2/3 y la línea, y todo
   lo demás los consume sin saber en qué mundo vive. Cambiar el tono de una
   sección vuelve a ser cambiar una clase.
   Auditoría que lo motivó (2026-07-30): el sitio cruzaba entre claro y oscuro
   OCHO veces en 19 pantallas; basement, rive y linear no cruzan nunca y
   locomotive cruza una vez. Un sitio no tiene atmósfera si la cambia cada dos
   pantallas. ──────────────────────────────────────────────────────────────── */
.papel, .papel-alto, .frio, body{
  --tx-1: rgba(9,10,13,.92); --tx-2: rgba(9,10,13,.60); --tx-3: rgba(9,10,13,.50);
  --tx-linea: rgba(9,10,13,.12);
  /* el gris de apoyo del titular — ver §2 `.golpe`. .78 y no el .60 de --tx-2
     porque sobre papel ese .60 da 2.3:1, por debajo del 3:1 de texto grande. */
  --tx-titular: rgba(9,10,13,.78);
}
.oscuro{
  --tx-1: rgba(237,239,243,1); --tx-2: rgba(237,239,243,.62); --tx-3: rgba(237,239,243,.50);
  --tx-linea: rgba(255,255,255,.12);
  /* CAPA 1 · ago-2026 — ERA .58, a CUATRO por ciento de alfa del .62 de --tx-2.
     Cuatro por ciento no es jerarquía: nadie lo distingue, pero la sonda cuenta
     DOS colores en cada pantalla que lleve titular. Sobre papel sí se conserva
     la diferencia (.78 vs .60) y está justificada arriba por contraste — ahí no
     se toca. Este peldaño solo sobraba en oscuro. */
  --tx-titular: rgba(237,239,243,.62);
}
.papel      { background:var(--papel) }
.papel-alto { background:var(--papel-alto) }
.frio       { background:var(--papel-frio) }
.oscuro     { background:var(--tinta); color:var(--tx-1) }
.oscuro .rotulo{ color:var(--tx-3) }
.rotulo{ color:var(--tx-3) }

/* grano + rejilla fija — anti-planitud sobre papel.
   Dos capas en una sola pintura: la rejilla queda FIJA a la ventana
   (segunda capa de profundidad) y el grano viaja con la sección. */
.papel::after,.papel-alto::after,.frio::after{
  /* El grano baja de .55 a .16 en las superficies CLARAS. Medido aislando el
     fondo (todo el contenido escondido): el papel variaba 55 niveles (184-239)
     mientras basement, linear y locomotive varían CERO en el suyo. Ese mismo
     grano sobre tinta lee como grano de película; sobre papel lee como papel
     sucio, y es exactamente la asimetría que se sentía —"los oscuros sí se ven
     bien"—. No se quita: se baja hasta donde da piel sin dar mugre. */
  content:''; position:absolute; inset:0; pointer-events:none; opacity:.16;
  background-image:var(--rejilla), var(--grano);
  background-size:104px 104px, 104px 104px, 160px 160px;
  background-attachment:fixed, fixed, scroll;
  mix-blend-mode:multiply;
}
/* la rejilla se apaga en la sección de más texto: ahí compite con el renglón.
   Va acotado a `.papel` A PROPÓSITO: sin ese acote la regla también le ganaba
   a `.oscuro::after`, y como #trabajo pasó a tinta, le estaba borrando el
   CIELO COMPLETO. Se midió con el contenido escondido: #trabajo daba
   exactamente los mismos números con el cielo encendido y apagado. Un
   `#id::after` sin acotar es más específico que cualquier regla de clase, así
   que se lleva por delante lo que venga después. */
#trabajo.papel::after{ background-image:var(--grano); background-size:160px 160px;
                       background-attachment:scroll }

/* En móvil la rejilla fija se apaga: iOS repinta la capa `fixed` en cada
   cuadro de scroll con rebote, y a 390px de ancho una retícula de 104px es
   ruido, no profundidad. El grano sí se queda — es lo que da la piel. */
@media (max-width:760px){
  .papel::after,.papel-alto::after,.frio::after{
    background-image:var(--grano); background-size:160px 160px;
    background-attachment:scroll;
  }
}

/* ---------- 4 · CABECERA ---------- */
.cab{
  position:fixed; inset:0 0 auto; z-index:60; display:flex;
  align-items:center; justify-content:space-between;
  padding:1.1rem var(--borde);
  transition:background var(--base) var(--curva), color var(--base) var(--curva);
  mix-blend-mode:difference; color:#fff;
}
/* el ÚNICO sitio donde sobrevive Clash Display: el logotipo. Un logotipo es
   reconocimiento, no composición — cambiarle la cara al nombre de la marca no
   es una decisión tipográfica, es una decisión de identidad. */
.cab__marca{ font-family:var(--marca); font-weight:400; font-size:1.05rem; letter-spacing:-.01em }

/* ── LA CABECERA SE RETIRA AL BAJAR (sólo teléfono) ────────────────────────
   `mix-blend-mode:difference` garantiza que la cabecera se LEA sobre cualquier
   fondo, pero no impide que se monte encima de otro texto. A 390px eso pasa
   constantemente: capturado, "Caliber Studio" y "Empezar ↗" quedaban impresos
   sobre las palabras de las capturas del panel. En escritorio hay margen
   lateral de sobra y no ocurre, por eso el gesto es sólo de teléfono.

   Se retira al bajar y vuelve al subir — el movimiento la convierte en parte
   del lenguaje en vez de un parche. Nunca se esconde arriba del todo (el hero
   la necesita) ni con movimiento reducido. */
@media (max-width:899px){
  .cab{ transition:background var(--base) var(--curva), color var(--base) var(--curva),
                   transform .34s cubic-bezier(.32,.72,0,1) }
  .cab--oculta{ transform:translateY(-115%) }
}
@media (prefers-reduced-motion:reduce){
  .cab{ transition:none }
  .cab--oculta{ transform:none }
}

/* ---------- 5 · HERO ---------- */
.hero{ min-height:100svh; display:grid; align-content:center; overflow:clip }
.hero__lienzo{ position:absolute; inset:0; z-index:0 }
.hero__lienzo canvas{ width:100%; height:100%; display:block }
.hero__cuerpo{ position:relative; z-index:2 }
/* El hero cabe en UNA pantalla junto con la prueba. Un hero de 1.6 pantallas
   empuja el trabajo fuera de vista, que es justo el error que corregimos. */
.hero .d1{ max-width:13ch; line-height:.98 }   /* el tamaño ya lo da --t1 */
.hero__pie{
  position:relative; z-index:2; display:flex; gap:2.5rem; flex-wrap:wrap;
  margin-top:3.5rem; align-items:flex-end; justify-content:space-between;
}
/* la prueba asoma desde el primer pliegue: se ve que hacemos webs */
.hero__tiras{ display:flex; gap:.75rem }
.tira{
  width:clamp(72px,9vw,124px); aspect-ratio:9/16; border-radius:3px;
  background:var(--sup-2); border:1px solid var(--tx-linea);
  overflow:hidden; position:relative;
}

/* ---------- 6 · HUECOS DE MATERIAL ---------- */
/* Marcados a propósito. Un hueco honesto es mejor que una imagen de relleno. */
.hueco{
  position:relative; background:
     repeating-linear-gradient(135deg,transparent 0 11px,rgba(127,127,127,.10) 11px 12px);
  border:1px solid var(--tx-linea); border-radius:2px;
  display:grid; place-content:center; text-align:center; padding:1rem;
}
.oscuro .hueco{ border-color:var(--tx-linea) }
.hueco span{ font-size:var(--t6); letter-spacing:.12em; text-transform:uppercase; color:var(--tx-3) }
.oscuro .hueco span{ color:var(--tx-3) }
.hueco--16x9{ aspect-ratio:16/9 } .hueco--4x5{ aspect-ratio:4/5 }
.hueco--9x16{ aspect-ratio:9/16 } .hueco--1x1{ aspect-ratio:1/1 }

/* ---------- 7 · TRABAJO ---------- */
.trabajo__cab{ display:flex; justify-content:space-between; align-items:end; gap:2rem; margin-bottom:3.5rem }
/* El caso invertido va rotado 2° y el paralaje lo desplaza: entre las dos cosas
   el renglón sacaba 17px de desborde horizontal. `clip` y NO `hidden` a
   propósito — `hidden` crea un contenedor de scroll y rompería cualquier
   `position:sticky` que viva adentro; `clip` recorta sin crearlo. */
.casos{ overflow-x: clip }

.casos{ display:grid; gap:clamp(3rem,7vw,6rem) }
.caso{ display:grid; gap:1.5rem; grid-template-columns:1fr; align-items:end }
@media (min-width:900px){
  .caso{ grid-template-columns:1.35fr .65fr; gap:3rem }
  /* al invertir hay que invertir TAMBIÉN las columnas: si no, el medio cae en
     la columna angosta y el caso alternado se ve raquítico */
  .caso--invertido{ grid-template-columns:.65fr 1.35fr }
  .caso--invertido .caso__media{ order:2 }
}
.caso__meta{ display:flex; flex-direction:column; gap:.5rem }
/* el color entra por CADA CLIENTE, no por una paleta que inventemos */
.caso__punto{ width:10px; height:10px; border-radius:50%; background:var(--color-caso,var(--oro)) }
/* El número era el ancla del caso y no estaba anclando nada: el HTML llevaba
   `class="caso__num mono"` y `.mono` se declara 180 líneas MÁS ABAJO con la
   misma especificidad, así que ganaba entera — el número renderizaba en General
   Sans a 12px peso 500, no en display a 44. Se veía plausible (un numerito
   discreto arriba del nombre) y por eso nadie lo cuestionó; solo aparece
   mirando el estilo COMPUTADO. Se quitó el `mono` del HTML. */
.caso__num{
  font-family:var(--display); font-size:var(--t3);
  font-weight:var(--p-golpe); letter-spacing:-.04em;
  font-variant-numeric:tabular-nums;
  color:var(--tx-3);
}
.caso__titulo{ font-family:var(--display); font-size:var(--t2); font-weight:var(--p-voz); letter-spacing:-.02em; line-height:1.04; margin:.2rem 0 }
/* "diseño entregado", "en operación" — es un SELLO de estado, no un titular.
   A 44px en display y en color competía de frente con el nombre del cliente.
   Baja a CAJA: la segunda voz de la página. Medido en 17 casas de lujo, lo que
   las hace sentirse vivas no es el titular grande, es el segundo registro — y
   la caja alta con tracking es el más barato de todos: no agrega un byte, no
   agrega un escalón a la escala y no necesita una familia más. */
.caso__cifra{
  font-family:var(--texto); font-size:var(--t6);
  font-weight:var(--p-ui); letter-spacing:.16em; text-transform:uppercase;
  font-feature-settings:'case' 1;
  /* El sello va en TINTA, no en el color del cliente, y esto es una concesión
     consciente: en rojo se veía mejor. Pero al bajarlo de 44px a 12px el umbral
     de contraste sube de 3:1 a 4.5:1, y medidos sobre el papel los cuatro
     colores dan 6.55 (Black Secret) · 4.34 (Chilita) · 3.08 (Brushia) ·
     1.94 (LEVIA): TRES DE CUATRO no pasan. El rojo que se vio en la primera
     versión era el segundo mejor del lote y tampoco llegaba. En tinta da 13.68.
     El color del cliente sigue presente donde no tiene que ser legible: el
     punto de arriba y el derrame del marco. */
  color:var(--tx-1);
  margin-top:.35rem;
}

/* ---------- 8 · SERVICIOS ---------- */
.servicios{ display:grid; gap:1px; background:var(--tx-linea); margin-top:3.5rem }
@media (min-width:860px){ .servicios{ grid-template-columns:repeat(3,1fr) } }
.servicio{ background:var(--papel); padding:clamp(1.75rem,3vw,2.75rem); display:grid; gap:1rem; align-content:start }
.servicio__icono{ width:44px; height:44px }
.servicio__icono .hueco{ width:44px; height:44px; border-radius:50% }
.servicio h3{ font-family:var(--display); font-weight:var(--p-voz); font-size:var(--t3); margin:0; letter-spacing:-.015em }

/* ---------- 9 · PINES (2-2.5 pantallas, la medida real de las refs) ---------- */
/* Una sección con pin no lleva aire vertical: el `--aire` (108px en un
   portátil) empujaba el lienzo hacia abajo DENTRO del viewport clavado, así
   que un lienzo de 100svh terminaba 108px fuera de la pantalla y se comía el
   pie del marco. El aire ya lo pone el propio lienzo. */
.pin{ position:relative; padding-block:0 }
.pin__lienzo{ height:100svh; display:grid; place-items:center; overflow:clip }
.pasos{ display:grid; gap:1rem }
.paso{ opacity:.28; transition:opacity var(--base) var(--curva) }
.paso.vivo{ opacity:1 }

/* ---------- 10 · PORTAL (dirección A: la pantalla completa, a sangre) ----------
   La técnica es la de mercury.com (medido: 4 sticky, 0 pin, medio al 0.99 del
   viewport). Aquí NO hay pin: el texto se scrollea y se va, y el marco se PEGA.
   Con pin no cabía — el marco a 96vw mide 864px de alto y el texto arriba pedía
   otros 270 en una pantalla de 900. Al quitar el pin el texto deja de competir. */
/* 328svh = 4 tramos de 82. Escrito como una sola unidad a propósito:
   `calc(4 * 82svh)` NO aplicó (la escena medía 985px en vez de 2952, o sea el
   alto se quedó en auto y no había recorrido que repartir). Medido, no supuesto. */
/* 🔴 CAPA 17 · DE 328svh A 215svh — EL PEAJE POR IDEA.
   Medido con `_tools/sondas/hueco.py`: #portal tenia 2.139 px de hueco de un
   solo tiron, el 64% de la seccion y 2,4 pantallas de nada. No era relleno mal
   puesto: era la CARRERA del pegado, 2,28 pantallas de scroll para enseñar
   TRES vistas — 0,76 pantallas por pestaña.
   Es el mismo diagnostico que ya se hizo con los clips de servicios ("2.4
   pantallas de scroll por servicio, 7.2 para decir tres cosas"), y la misma
   cura: la carrera se paga por IDEA, no por seccion. A 215svh quedan 1,15
   pantallas para las tres vistas y las transiciones siguen leyendose.
   ⚠️ El pegado mide 100svh, asi que la carrera REAL es (altura - 100svh).
      Bajar de 200svh deja menos de una pantalla para tres pasos y las vistas
      empiezan a pasar de golpe. */
.portal__escena{ height:215svh }
.portal__pegado{
  position:sticky; top:0; height:100svh;
  /* Dos columnas: los pasos mandan a la izquierda, la vista obedece a la
     derecha. Antes el marco iba centrado y solo y las pestañas vivían encima de
     él, robándole el pie a la interfaz. */
  display:grid; grid-template-columns:minmax(15rem, .8fr) minmax(0, 2.2fr);
  gap:clamp(1.5rem, 4vw, 4rem);
  /* ANCLADO ARRIBA, no centrado. Medido: con `center` quedaban 273px de hueco
     entre la cabecera de la sección y los pasos —114 eran holgura de centrado y
     el resto relleno—, y ese hueco es lo primero que se ve al llegar, antes de
     que el pegado se enganche. Anclado arriba se lee como un bloque, y de paso
     la posición no cambia entre el estado de llegada y el de recorrido. */
  align-items:start; align-content:start;
  /* aire para la cabecera fija: sin esto la barra del chasis quedaba DEBAJO del
     menú y el marco se veía sucio por arriba */
  padding-top:5.25rem;
}
/* El marco se dimensiona por el ALTO disponible, no solo por el ancho: si se
   fija a 96vw, en una laptop de 900 el propio marco mide 898 y no queda aire
   para las pestañas. Así llega a 0.96 en monitor grande y baja solo cuando la
   ventana es corta — pero nunca se sale. */
.portal__marco{
  /* Tres topes: el ancho de la ventana, el ancho REAL de la captura (1440
     lógicos — pasarse sería interpolar) y el alto disponible. */
  width:min(100%, 1440px, calc((100svh - 8.75rem) * 1.6));
  position:relative;
  margin-inline:auto; border-radius:8px;
  background:var(--sup-1); border:1px solid var(--tx-linea);
  /* 🔴 UNA SOMBRA NEGRA SOBRE FONDO NEGRO NO SEPARA NADA: LO CONTRARIO.
     Aquí había `0 40px 120px rgba(0,0,0,.6)` sobre una sección que ya vive en
     #08090C. No se veía —no puede: es negro sobre negro— y encima apagaba el
     poco cielo que rodeaba al marco, así que el aparato se fundía con la página
     en vez de posarse sobre ella. Sobre oscuro la separación la da la LUZ, no la
     sombra: un filo claro arriba (de donde viene la luz de la escena) y un halo
     frío y muy abierto que hace de aire. La sombra se queda, pero corta y sólo
     como contacto — lo que asienta el aparato en el suelo. */
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.10),
    0 0 0 1px rgba(255,255,255,.05),
    0 2px 6px rgba(0,0,0,.55),
    0 30px 90px -30px rgba(150,170,200,.16);
  overflow:hidden;
}
.portal__barra{ height:34px; background:var(--sup-2); border-bottom:1px solid var(--tx-linea);
  display:flex; align-items:center; gap:8px; padding:0 12px }
/* 🔴 LOS TRES PUNTOS DE SEMÁFORO SE FUERON (ago-2026). Eran el recurso más
   templado del sitio —el cromo de macOS que trae cualquier maqueta de plantilla—
   y no decían absolutamente nada: ni son botones, ni se pueden pulsar, ni
   informan de nada. La barra se queda porque la URL SÍ es información: dice
   dónde vive el portal de verdad. El marcado conserva los `<span>` vacíos por si
   se quisieran recuperar; aquí simplemente no se pintan. */
.portal__punto{ display:none }

/* Las cuatro vistas ocupan el mismo hueco. El `aspect-ratio` va en el
   contenedor —no en la imagen— para que el alto sea determinista antes de que
   cargue cualquier archivo: cero salto de layout. */
.portal__vistas{ position:relative; aspect-ratio:2880/1800; isolation:isolate }
/* La transición: un crossfade puro deja ver DOS pantallas superpuestas un
   instante, y eso es exactamente lo que se lee como "salto". Se añaden dos
   cosas mínimas que lo cosen:
     · un pelo de escala (1.015 → 1) para que la entrante llegue acercándose,
     · un desenfoque de 7px que se va, que hace de puente entre las dos capas
       en vez de mostrarlas como dos objetos distintos.
   Duraciones distintas a propósito: la opacidad cierra antes que la escala, así
   la saliente ya no está cuando la entrante termina de asentarse.
   Nada de scale(0): en el mundo real nada aparece de la nada. */
.portal__vistas > .vista{
  position:absolute; inset:0; margin:0;
  opacity:0; transform:scale(1.015); filter:blur(7px);
  transition:opacity .38s var(--curva),
             transform .62s var(--curva),
             filter .34s var(--curva);
  pointer-events:none;
}
.portal__vistas > .vista.viva{ opacity:1; transform:none; filter:blur(0) }
/* Con movimiento reducido se queda el fade y nada más: la escala y el
   desenfoque son adorno, la legibilidad no depende de ellos. */
@media (prefers-reduced-motion:reduce){
  .portal__vistas > .vista{ transform:none; filter:none; transition:opacity .2s linear }
}
.portal__vistas img{ display:block; width:100%; height:100% ; object-fit:cover }

/* Pestañas DENTRO del marco, sobre un velo: fuera se comían ~80px de alto y en
   una laptop de 800 la interfaz caía a escala 0.66 (12px de texto → 8). Adentro
   además leen como un control del propio producto, no como un widget del sitio. */
/* LOS PASOS, que ahora son las pestañas y el eje de la sección.
   Medido antes de tocar nada: #portal medía 3,442px —3.8 pantallas— para 219
   caracteres de texto, con solo TRES tamaños de tipo (12 · 22 · 72) y un hueco
   de 6× entre el más chico y el más grande, cuando las referencias viven entre
   3.3 y 4.8. Y los cuatro pasos —la respuesta literal al rótulo "cómo se
   trabaja con nosotros"— estaban a 12px, el tamaño más pequeño de la sección.
   El contraste NUNCA fue el problema: cero fallos AA, el peor a 11.65 sobre un
   mínimo de 4.5. Era jerarquía invertida.
   Ahora el paso activo vive a ~28px y los demás se retiran, así que cada
   pantalla de la escena tiene un protagonista en vez de cuatro pastillas. */
/* `order:-1` y no reordenar el HTML: en el marcado el marco va primero porque
   es el contenido y los pasos son su control, y así lo lee un lector de
   pantalla. Visualmente los pasos van a la izquierda —se leen antes— y el marco
   se queda con la columna ancha. Sin esto el marco caía en 400px y los pasos
   ocupaban 900: exactamente al revés. */
.portal__pest{
  order:-1;
  list-style:none; margin:0; padding:0;
  display:grid; gap:.15rem; align-content:center;
  min-width:0;
}
.portal__pest button{
  font:inherit; text-align:left; width:100%;
  background:none; border:0; cursor:pointer;
  display:grid; grid-template-columns:2.4rem minmax(0,1fr);
  gap:0 1rem; align-items:baseline;
  padding:.85rem 0; border-top:1px solid var(--tx-linea);
  color:var(--tx-3);
  transition:color var(--base) var(--curva), opacity var(--base) var(--curva);
}
.portal__pest li:last-child button{ border-bottom:1px solid var(--tx-linea) }
.pp__n{ font-size:var(--t6); font-variant-numeric:tabular-nums; letter-spacing:.06em;
        color:var(--tx-3) }
.pp__t{ font-size:clamp(1rem, 1.35vw, 1.188rem); line-height:1.28;
        font-weight:var(--p-ui) }
/* La línea de detalle solo existe en el paso vivo. Sin esto los cuatro pasos
   eran cuatro títulos secos y la sección seguía diciendo 219 caracteres en 3.7
   pantallas; ahora el paso activo explica lo que se está viendo en la pantalla
   de al lado. Se anima con `grid-template-rows` y no con `height:auto`, que no
   es animable. */
.pp__d{
  grid-column:2; font-size:.875rem; line-height:1.45; color:var(--tx-2);
  display:grid; grid-template-rows:0fr; opacity:0; overflow:hidden;
  transition:grid-template-rows var(--base) var(--curva), opacity var(--base) var(--curva),
             margin-top var(--base) var(--curva);
}
.pp__d > *{ min-height:0 }
.portal__pest button[aria-selected=true] .pp__d{
  grid-template-rows:1fr; opacity:1; margin-top:.45rem;
}
.portal__pest button:hover{ color:var(--tx-2) }
/* El paso vivo: sube de tamaño y de peso, y su número se vuelve el único punto
   cálido de la columna. El resto NO se apaga a un gris ilegible —se quedan en
   --tx-3, que ya mide 11.65 de contraste— porque son la lista completa del
   proceso y se tienen que poder leer todos. */
.portal__pest button[aria-selected=true]{ color:var(--tx-1) }
/* el portal también vive sobre tinta: bronce claro, ver §piezas */
.portal__pest button[aria-selected=true] .pp__n{ color:var(--bronce-claro, #C6A961) }
.portal__pest button[aria-selected=true] .pp__t{
  font-size:clamp(1.25rem, 2.1vw, 1.75rem); font-weight:var(--p-ui);
  transition:font-size var(--base) var(--curva);
}
.portal__pest button:focus-visible{ outline:2px solid var(--oro); outline-offset:3px }

@media (max-width:760px){
  /* En teléfono el portal se captura vertical (390×844 lógicos), así que el
     hueco cambia de forma. Y el marco puede irse casi a todo el ancho porque
     ya no hay texto compitiendo. */
  .portal__vistas{ aspect-ratio:780/1688 }
  /* ── EL MARCO SE VUELVE TELÉFONO ──────────────────────────────────────
     Lo cazó paco: dentro iba la captura vertical del portal (correcta) pero
     alrededor seguía el cromo de un NAVEGADOR DE ESCRITORIO —tres puntitos y
     `portal.caliberstudio.site`—. Un teléfono dentro de una ventana de Mac no
     es un objeto: es un error de escala, y se lee como tal.
     La barra se va y el marco toma silueta de aparato. La URL no se pierde:
     la captura trae la cabecera real del portal dentro. */
  .portal__barra{ display:none }
  .portal__marco{ border-radius:30px; padding:5px; background:#101318;
                  box-shadow:0 0 0 1px rgba(255,255,255,.07),
                             0 26px 60px -22px rgba(0,0,0,.85) }
  .portal__vistas{ border-radius:25px; overflow:clip }
  .portal__marco{ width:min(94vw, 390px, calc((100svh - 7.75rem) * 0.462)) }
  /* A 390px dos columnas dejarían los pasos en 120px de ancho: se apilan, y el
     paso vivo baja de tamaño para no comerse la pantalla del marco. */
  .portal__pegado{ grid-template-columns:1fr; align-content:center; gap:1rem }
  .portal__pest button{ padding:.5rem 0 }
  .portal__pest button[aria-selected=true] .pp__t{ font-size:1.0625rem }
  .pp__t{ font-size:.875rem }
  .portal__escena{ height:205svh }
}


/* ---------- RECORRIDO POR SCROLL ----------------------------------------
   ANTES iba en HOVER, y eso estaba medido como defecto: en las 5 referencias
   vivas del scout (Exo Ape, OBYS, Hello Monday, Phantom, Basic/Dept) el hover
   mueve entre 0.0% y 3.6% de la pantalla —allá es adorno—, mientras que aquí
   era el evento ENTERO. Consecuencia: en teléfono, donde no hay hover, los
   cuatro casos eran cuatro fotos muertas; y al bajar por la sección no pasaba
   nada salvo que el cursor cayera justo encima.

   Ahora lo mueve el scroll: `--rec` va de 0 a 1 conforme el caso cruza la
   ventana y la captura se recorre sola. Se conserva el truco de la traslación
   —`100cqh` es el alto de la VENTANA y `100%` el de la imagen, así que la
   resta da exactamente lo que hay que subir, sin número mágico— y se le
   multiplica el progreso. Necesita `container-type:size` en la ventana.
   Frente a un video del mismo recorrido: 130-189 KB contra 800-1800. */
.recorre__ventana{
  position:relative; aspect-ratio:16/10; overflow:clip;
  container-type:size;
}
.recorre__foto{
  position:absolute; top:0; left:0; width:100%; height:auto;
  transform:translateY(calc((100cqh - 100%) * var(--rec, 0)));
  will-change:transform;
}

/* ── QUIÉN MANDA EL RECORRIDO, POR DISPOSITIVO ───────────────────
   Se probó moverlo TODO con el scroll y paco lo cazó: las cuatro capturas
   viajando a la vez es ruido, no un evento — y tenía razón, con el cursor se
   recorre la que TÚ elegiste. Pero el hover solo tampoco servía: en teléfono
   no existe, y ahí quedaban cuatro fotos muertas (era el defecto medido).
   Así que manda el cursor donde hay cursor, y el scroll donde no lo hay.
   El barrido de Black Secret NO entra en este reparto: va por scroll en los
   dos, porque es UNA cosa que ocurre al pasar, no cuatro. */
@media (hover: hover){
  .recorre__foto{ transition:transform .8s var(--curva) }
  .recorre:hover .recorre__foto,
  .recorre:focus-visible .recorre__foto{
    transform:translateY(calc(100cqh - 100%));
    /* 20s y no 9: a 9 el recorrido corría a ~209 px/s y no se alcanzaba a LEER
       nada. A 20s van ~102 px/s. Lineal: un recorrido no acelera ni frena.
       `--recorrido-s` va por caso porque las capturas NO miden lo mismo; con
       una duración fija, la más alta correría más rápido. */
    transition:transform var(--recorrido-s, 20s) linear;
  }
}
.recorre:focus-visible{ outline:2px solid var(--oro); outline-offset:6px }

/* La pista: sin ella nadie sabe que hay algo que hacer. Se apaga al entrar,
   porque a partir de ahí el movimiento ya lo dice. Sin cursor se calla: ahí
   el recorrido lo lleva el scroll y no hay nada que pedirle a nadie. */
.recorre__pista{
  position:absolute; right:.9rem; bottom:.9rem; z-index:4;
  padding:.3rem .6rem; border-radius:999px;
  font-size:var(--t6); letter-spacing:.06em; text-transform:uppercase;
  color:var(--tx-2); background:rgba(8,9,12,.72);
  backdrop-filter:blur(6px);
  opacity:1; transition:opacity var(--base) var(--curva);
  pointer-events:none;
}
.recorre:hover .recorre__pista{ opacity:0 }
/* ⚠️ El segundo selector NO sobra. `.recorre__pista` pesa (0,1,0) y
   `.marco > *{ display:block }` —200 líneas más abajo— pesa (0,1,1): le gana,
   y la media query no suma especificidad. Resultado medido a 390px con
   `(hover:none)` en verdadero: la primera pista sí se ocultaba (su padre es
   `.chasis`) y las OTRAS TRES seguían pidiendo "pasa el cursor para
   recorrerlo" en un teléfono, que es donde no hay cursor. Lo cazó paco. */
@media (hover:none){
  .recorre__pista,
  .marco > .recorre__pista{ display:none }
}

/* ── EL BARRIDO (solo Black Secret) ──────────────────────────────
   La plantilla anterior está ENCIMA y se retira de izquierda a derecha.
   `clip-path:inset` y no opacidad: un fundido cruzado deja ver las dos a la
   vez y se lee a error de carga; un barrido se lee a mano que retira algo. */
.recorre__antes{
  position:absolute; inset:0; z-index:1;
  width:100%; height:100%; object-fit:cover; object-position:50% 0;
  clip-path:inset(0 0 0 var(--barre, 0%));
}
/* La costura: el canto que avanza. Sin ella el barrido se lee a imagen que
   falla; con ella se lee a objeto que se abre. Se apaga cuando termina de
   barrer —una línea parada no significa nada.
   1px de oro NO bastaba: medido contra la plantilla vieja, que es casi blanca,
   la línea desaparecía. Lleva además una sombra corta hacia la IZQUIERDA, o
   sea proyectada sobre lo entregado: es la capa vieja al retirarse la que la
   tira. Eso es lo que le da espesor en vez de parecer un corte de recorte. */
.recorre__costura{
  position:absolute; top:0; bottom:0; left:var(--barre, 0%); width:2px; z-index:2;
  background:linear-gradient(to bottom,
    color-mix(in oklab, var(--oro) 40%, transparent), var(--oro) 22%,
    var(--oro) 78%, color-mix(in oklab, var(--oro) 40%, transparent));
  opacity:var(--costura-op, 0);
  box-shadow:-10px 0 22px -6px rgba(0,0,0,.55),
             0 0 12px 0 color-mix(in oklab, var(--oro) 45%, transparent);
  pointer-events:none;
}
/* El rótulo VIAJA con la costura y se queda siempre del lado viejo. Fijo abajo
   a la izquierda quedaba encima de lo entregado, que es justo lo contrario de
   lo que nombra. */
.recorre__rotulo{
  position:absolute; left:calc(var(--barre, 0%) + .7rem); bottom:1rem; z-index:3;
  font-size:var(--t6); letter-spacing:.14em; text-transform:uppercase;
  /* Placa y no sombra de texto: lo que hay debajo es el sitio VIEJO del
     cliente, o sea un fondo que no controlamos —ahí medía casi blanco y el
     rótulo en blanco no pasaba contraste—. La placa lo garantiza sea cual
     sea el pixel que le toque. */
  color:#fff; background:rgba(8,9,12,.84);
  padding:.22rem .5rem; border-radius:2px;
  opacity:var(--costura-op, 0);
  white-space:nowrap; pointer-events:none;
}

@media (prefers-reduced-motion:reduce){
  /* sin movimiento se enseña lo ENTREGADO, no la plantilla vieja: si la
     animación no corre, el barrido se quedaría congelado en el "antes" y el
     caso contaría justo lo contrario de lo que quiere contar */
  .recorre__foto{ transform:none }
  .recorre__antes{ clip-path:inset(0 0 0 100%) }
  .recorre__costura, .recorre__rotulo{ opacity:0 }
}

/* ---------- 11 · ENTRADA + CIERRE ---------- */
.carriles{ display:grid; gap:1px; background:var(--tx-linea); margin-top:3rem }
@media (min-width:860px){ .carriles{ grid-template-columns:repeat(3,1fr) } }
.carril{ background:var(--papel); padding:2rem; display:grid; gap:.75rem; align-content:start }
.carril--eje{ background:var(--papel-alto); outline:1px solid var(--oro) }
.boton{
  display:inline-flex; align-items:center; gap:.5rem; padding:.85rem 1.5rem;
  border:1px solid currentColor; border-radius:999px; font-weight:var(--p-ui);
  transition:transform var(--rapido) var(--curva), background var(--rapido) var(--curva), color var(--rapido) var(--curva);
}
.boton:hover{ background:var(--oro); border-color:var(--oro); color:var(--tinta) }
.boton:active{ transform:scale(.97) }

.cierre{ min-height:92svh; display:grid; align-content:center; overflow:clip }

/* ---------- 12 · REVELADOS ---------- */
/* Nada arranca en opacity:0 desde CSS — si el JS falla, el texto igual se lee. */
/* 🔴 LA MÁSCARA DEL REVELADO RECORTA LOS DESCENDENTES. Medido con la escala
   nueva: la caja de línea mide 60.76px para una fuente de 62 —el `line-height`
   de 1.03 del titular es MENOR que su propio tamaño—, y con `overflow:hidden`
   la «g» de «negocio» sale cortada por abajo. Ya pasaba antes (84.5 contra 86)
   pero a peso 200 el trazo era tan fino que no se veía.
   El relleno agranda SOLO la caja de recorte; el margen negativo lo devuelve,
   así que la composición no se mueve ni un píxel. */
.linea{ display:block; overflow:hidden;
        padding-bottom:.2em; margin-bottom:-.2em }

@media (prefers-reduced-motion:reduce){
  *{ animation:none!important; transition-duration:.01ms!important }
}

/* ============================================================
   v3.1 — lo que el audit de composición dejó en evidencia.
   Medido contra 6 referencias (AUDIT_COMPOSICION.md):
   · densidad 10 cajas/pantalla vs 27-137  → hay que LLENAR
   · 33% de texto ≤13px vs 59-70%          → texto chico por todos lados
   · 4 enlaces y sin pie vs 40-69 y 1 pantalla de pie
   · 25% centrado vs 7-13%                 → descentrar
   · 0 carriles horizontales vs 2-11
   · cursivas: 9 vs CERO en las seis referencias
   ============================================================ */

/* --- El texto chico ya NO es mono. Por qué:
   Puse JetBrains Mono, que es la letra de VS Code. Usar la fuente de un editor
   de código como taquigrafía de "técnico" es el tell más barato que hay, y a
   105 nodos convertía el sitio entero en una máquina de escribir.
   Las referencias que sí usan mono (benorth, bymonolog) usan la HERMANA de su
   propia familia — Aeonik Mono, Suisse Mono — no una letra prestada de otro
   mundo. General Sans no tiene hermana mono, así que la respuesta correcta es
   quitarla, no cambiarla: el texto chico vive en la misma voz, a peso 500.
   `tabular-nums` conserva lo único que la mono aportaba de verdad: que los
   números de columnas distintas alineen. --- */
.mono{ font-family:var(--texto); font-size:var(--t6); font-weight:var(--p-ui);
       letter-spacing:.02em; font-variant-numeric:tabular-nums;
       font-feature-settings:'case' 1 }   /* los separadores `·` suben a caja alta */

/* --- un solo lienzo fijo detrás de todo --- */
#mundo{ position:fixed; inset:0; width:100%; height:100%; z-index:-1; display:block }
.oscuro{ background:transparent }        /* el shader se ve a través */
/* Respaldo sin WebGL. El fondo oscuro dependía POR COMPLETO del lienzo: al
   esconder `#mundo` la sección se volvía PAPEL con texto blanco encima
   —ilegible—, y eso es justo lo que veía cualquier motor sin WebGL o con la
   aceleración apagada. No se puede arreglar poniéndole tinta opaca a
   `.oscuro`: taparía el lienzo, que vive detrás. La marca la pone el propio
   `mundo.js` cuando no consigue contexto. */
.sin-mundo .oscuro{ background:var(--tinta) }

/* ═══════════════════════════════════════════════════════════════════════
   LAS PLACAS DE ATMÓSFERA — solo en dos secciones, a propósito
   ───────────────────────────────────────────────────────────────────────
   Medido en izanami, la referencia que dio paco: su fondo NO cambia todo el
   tiempo. Doce de dieciocho muestras viven entre 6.2 y 6.7 de luminancia y
   hay apenas CUATRO saltos grandes en todo el documento. Son ISLAS DE LUZ
   dentro de un negro largo, no variedad constante — y esa es justo la parte
   que se malinterpreta al mirarlo.

   Por eso va UNA sola, en #sistemas. NO en el hero ni en el cierre: ahí a paco
   le gustan los gradientes como están, y son justamente las dos secciones que
   ya tienen su propio cielo con fuente de luz propia (`--n1/--n2/--n3`) más el
   cuerpo del cierre. Meterles una placa encima habría tapado lo que funciona.
   Precios se queda CLARA, también por decisión suya.

   Va DEBAJO de la niebla —que no se toca— y ENCIMA de la veladura de tinta:
   debajo de ella, el 55% de opacidad la aplastaría a ~6 y no se vería.
   Es cálida (R-B +7.9), como el rgb(10,8,1) de izanami y al revés de nuestro
   #08090C frío.
   ═══════════════════════════════════════════════════════════════════════ */
#sistemas{ position:relative }
.placa{
  position:absolute; inset:0; z-index:0; pointer-events:none;
  background-size:cover; background-position:center; background-repeat:no-repeat;
  /* se apaga contra el borde de abajo para que la costura con la sección
     siguiente no sea un corte a cuchillo */
  -webkit-mask-image:linear-gradient(to bottom, #000 62%, transparent 100%);
          mask-image:linear-gradient(to bottom, #000 62%, transparent 100%);
}
.placa--polvo{
  background-image:url('../media/materia/placa-polvo.webp');
  /* .58 y no 1: a plena fuerza la placa dejaba la sección en luminancia 33.4,
     que es exactamente la zona donde el fondo empieza a aplanar lo que se le
     pone encima —el error que costó media sesión arreglar con la niebla—. Aquí
     encima van las vistas del panel, así que la isla de luz tiene que
     notarse SIN comerse el contraste de la captura. */
  opacity:.58;
}
/* La segunda isla. Se desvanece hacia ARRIBA —al revés que la de #sistemas—
   para que las dos no repitan el mismo gesto, y entra más baja (.46) porque el
   horizonte reparte su luz por todo el ancho mientras el haz la concentra. */
/* `placa-caida.webp` queda sin usar: paco la vio en #portal y el conjunto se
   leía anaranjado. Se conserva por si sirve en otro sitio con menos calor. */


/* El contenido va por encima de la placa */
#sistemas > :not(.placa){ position:relative; z-index:1 }
.oscuro::before{ content:''; position:absolute; inset:0; background:var(--tinta);
                 opacity:.55; z-index:-1 }
.papel,.papel-alto,.frio,.pie{ position:relative; z-index:1 }
.hero__lienzo{ display:none }            /* ya no hay lienzos por sección */

/* ============================================================
   C2 · EL CIELO — la lección de linear.app
   ------------------------------------------------------------
   Medido en vivo: linear.app no tiene UN SOLO canvas. Todo su
   cósmico son 10 radial-gradient pasados por filter:blur(80px) y
   montados con mix-blend-mode overlay/lighten. Su fondo es
   rgb(8,9,10); el nuestro rgb(8,9,12). La oscuridad ya estaba
   bien — lo que faltaba era LUZ ENCIMA.

   Aquí NO se copia el blur(80px): ellos lo necesitan porque sus
   gradientes salen de divs con borde duro. Un radial-gradient con
   caída larga ya llega desenfocado de fábrica, y desenfocar una
   capa a pantalla completa cuesta pintura en cada cuadro.

   `lighten` y no `screen`: screen levanta TODO el negro y el papel
   pierde su negro real; lighten solo deja pasar lo que supera al
   fondo, así la sombra se queda en sombra. Ley cósmica: sin luz
   ambiental de relleno.
   ============================================================ */
.oscuro::after{
  content:''; position:absolute; inset:0; z-index:-1; pointer-events:none;
  background:
    radial-gradient(62% 46% at var(--n1,78% 6%),  rgba(150,178,216,.13), transparent 63%),
    radial-gradient(48% 40% at var(--n2,10% 88%), rgba(216,197,140,.06), transparent 66%),
    radial-gradient(34% 30% at var(--n3,44% 54%), rgba(122,152,202,.05), transparent 70%),
    /* C3 · EL TERMINADOR — la línea donde la luz muere. En una esfera
       iluminada por una sola fuente es la imagen más cósmica que hay, y
       es literalmente un gradiente. Aquí vive el guiño; no hace falta
       dibujar un planeta. Va arriba y abajo: la costura contra el papel
       deja de ser un corte a cuchillo. */
    linear-gradient(to bottom, rgba(244,241,234,.04), transparent 190px),
    linear-gradient(to top,    rgba(244,241,234,.025), transparent 150px);
  background-size: auto;
  background-position: center;
  background-repeat: no-repeat;
  mix-blend-mode:lighten;
}
/* H1 · LA PLACA DE MATERIA — UNA capa fija para todo el sitio.
   Antes iba dentro del apilado de CADA sección oscura, y ahí estaba el
   problema que se sentía como "el oscuro no me termina de convencer":

   con `lighten` el píxel más claro gana, así que una placa cuya cima está
   horneada en 26/255 se impone en TODA la ventana sobre un fondo cuyo piso
   real es 11. Resultado medido: el fondo pasaba de 11 a 31 de luminancia y su
   rango de 15 a 71 niveles. Eso no es atmósfera, es un velo gris uniforme —y
   un velo uniforme aplana todo lo que se le ponga encima—. Las referencias
   oscuras miden 8-9 de fondo con 13-21 de rango; nosotros íbamos al TRIPLE.

   La placa se había horneado al 10% para competir contra un shader que
   entonces vivía en 29. Al bajar el shader a su sitio, la misma placa pasó de
   acompañar a dominar. Sacarla a UNA capa fija arregla las dos cosas: su
   fuerza vuelve a ser un número que se ajusta aquí —sin re-hornear el webp— y
   se pinta una vez en lugar de una por sección.

   Sobre papel no hace nada: con `lighten`, el papel (241) le gana siempre.

   La niebla NO se apagó: se volvió VARIABLE. Al medirla se vio que el problema
   nunca fue que existiera —se ve bien y da la atmósfera— sino que era PAREJA:
   el mismo velo en las seis secciones oscuras y siempre al mismo valor, así que
   todo lo que se le ponía encima perdía contraste contra ella por igual. Ahora
   cada sección declara su densidad con `data-niebla` y esta capa la persigue:

     densa    (1.00)  #top y #cierre — ahí la niebla ES el contenido y no
                      compite con nada; es donde paco la vio premium
     media    (0.60)  #sistemas — hay que leer, pero la escena es del fondo
     delgada  (0.34)  #trabajo, #servicios, #portal — hay trabajo que MIRAR,
                      y el negro real es lo que le devuelve el contraste
     nula     (0.00)  #entrada, la isla de papel

   La transición es larga a propósito (1.1s): a menos de eso el cambio se
   percibe como un parpadeo al cruzar la costura en vez de como aire que se
   despeja. `opacity` es de las dos propiedades que no cuestan reflow. */
.materia{
  position:fixed; inset:0; z-index:-1; pointer-events:none;
  background:url('../media/materia/velada.webp') center/cover no-repeat;
  mix-blend-mode:lighten;
  opacity:var(--niebla, .34);
  transition:opacity 1.1s cubic-bezier(.33,0,.2,1);
}
@media (prefers-reduced-motion:reduce){ .materia{ transition:none } }

/* Cada cielo con su propia fuente. Cuatro secciones oscuras con el mismo
   gradiente es exactamente la linealidad que había que romper. */
#top   { --n1:82% 4%;  --n2:6%  92%; --n3:52% 46% }
#portal{ --n1:14% 12%; --n2:88% 78%; --n3:38% 60% }
/* H2 · EL CUERPO CON TERMINADOR — la única pieza figurativa del sitio.
   Va SOLO en el cierre y va UNA vez: dos ya es decorado espacial.
   Su luz entra por la derecha, así que el cielo de esta sección mueve su
   fuente a 88% 18% para que coincidan. Dos fuentes distintas en la misma
   escena es exactamente lo que hace que algo se lea a pegote.
   El archivo lleva ALFA por luminancia: el vacío es transparente, así que
   puede ir ARRIBA de los gradientes sin taparlos, y solo sobrevive el
   creciente. Se sale del encuadre a propósito — un cuerpo entero y centrado
   es un planeta de stock; un fragmento gigante es escala. */
/* 🔴 CAPA 14 · AQUÍ SE FUE `cuerpo.webp`, Y NO POR PESO.
   Esta sección tenía un creciente PINTADO cuyo limbo se había igualado a mano
   con la fuente de luz de la sección (de ahí el `--n1:88% 18%` de abajo, que se
   conserva porque sigue mandando los halos). Desde la Capa 14 el shader
   resuelve un cuerpo de VERDAD en este mismo encuadre: misma luz, silueta que
   se recalcula con el puntero y superficie que gira con el scroll.
   Dejar los dos ponía un cuerpo pintado y uno renderizado en la misma toma —
   dos lunas, dos leyes de luz— que es exactamente el pegote que el comentario
   original quería evitar. Gana el que obedece a la luz de la escena.
   El archivo se queda en `media/materia/` por si hay que volver. */
#cierre{ --n1:88% 18%; --n2:8% 86%; --n3:40% 50% }
#cierre::after{
  background-image:
    radial-gradient(62% 46% at var(--n1), rgba(150,178,216,.13), transparent 63%),
    radial-gradient(48% 40% at var(--n2), rgba(216,197,140,.06), transparent 66%),
    radial-gradient(34% 30% at var(--n3), rgba(122,152,202,.05), transparent 70%),
    linear-gradient(to bottom, rgba(244,241,234,.04), transparent 190px),
    linear-gradient(to top,    rgba(244,241,234,.025), transparent 150px);
  /* Ya no hay capa de imagen que colocar: quedan solo halos y costuras, que
     son de tamaño automático. La placa tampoco va aquí — es una sola capa fija
     para todo el sitio (ver `.materia`). */
  background-repeat: no-repeat;
}
.pie   { --n1:96% 96%; --n2:4%  10%; --n3:60% 70% }

/* ============================================================
   C6 · EL BLUR COMO JERARQUÍA, NO COMO EFECTO
   ------------------------------------------------------------
   Medido: benorth 3 niveles (3/6/14px), cosmos.so 4 (1/10/30/32),
   trionn 1, la v3 CERO. Y ahí está el punto: cuando hay UN blur
   es decoración; cuando hay una escala, el desenfoque dice a qué
   distancia está cada cosa. Tres escalones y ninguno más:

     medio  →  8px   el marco de una captura flotando sobre el cielo
     lejos  → 16px   la superficie del portal, el plano más hondo

   Hubo un tercer escalón, `cerca → 3px`, para la banda del carril
   horizontal. El carril se quitó por decisión de paco y el token se fue
   con él: un peldaño de una escala que nadie pisa es ruido.

   El backdrop NO se ve si el fondo del elemento es opaco: hay que
   abrirle una rendija. Por eso los fondos pasan a color-mix con
   transparencia — antes eran sólidos y el blur no habría hecho nada.

   NO va en `.cab`: la cabecera lleva mix-blend-mode:difference, y
   backdrop-filter crea contexto de apilamiento — el difference
   acabaría invirtiendo el resultado ya borroso.
   ============================================================ */
:root{ --vidrio-medio:8px; --vidrio-lejos:16px }

/* Las tiras del hero son lo único que hoy FLOTA sobre el cielo; `.marco`
   sigue en el selector porque ahí caerá solo cuando una captura pase a
   una sección oscura, sin tener que volver a tocar esto. */
.oscuro .tira, .oscuro .marco{
  backdrop-filter:blur(var(--vidrio-medio));
  background:color-mix(in oklab, var(--sup-2) 72%, transparent);
}
.portal__marco{
  backdrop-filter:blur(var(--vidrio-lejos));
  background:color-mix(in oklab, var(--sup-1) 78%, transparent);
}
/* Safari viejo y cualquier motor sin backdrop-filter: se devuelven los
   fondos sólidos. Sin esto el marco quedaría semitransparente y el texto
   de encima se leería sobre el cielo. */
@supports not (backdrop-filter:blur(1px)){
  .oscuro .tira, .oscuro .marco{ background:var(--sup-2) }
  .portal__marco{ background:var(--sup-1) }
}

/* --- CABECERA con navegación real --- */
.cab{ gap:2rem }
.cab__nav{ display:none; gap:1.5rem }
@media (min-width:900px){ .cab__nav{ display:flex } }
.cab__nav a{ font-size:var(--t5); font-weight:var(--p-ui); opacity:.75;
             transition:opacity var(--rapido) var(--curva) }
.cab__nav a:hover{ opacity:1 }
.cab__cta{ font-size:var(--t5); font-weight:var(--p-ui); display:flex; gap:.5rem; align-items:center }
.cab__punto{ opacity:.45 }

/* --- HERO --- */
.hero__cuerpo{ display:grid; gap:2rem }
.hero__bajo{ display:grid; gap:2rem; align-items:end }
@media (min-width:900px){ .hero__bajo{ grid-template-columns:1fr auto; gap:4rem } }
@media (min-width:700px){ .ficha{ grid-template-columns:repeat(4,auto) } }
.hero__tiras{ display:flex; gap:.75rem; margin-top:3rem; position:relative; z-index:2 }
.tira{ margin:0; width:clamp(64px,10vw,150px); display:grid; gap:.5rem }
.tira .hueco{ aspect-ratio:9/16 }
.tira figcaption{ color:var(--tx-3) }
.hero__baja{ margin:2.5rem 0 0; color:var(--tx-3) }
/* el renglón que sustituye a la ficha técnica del hero — ver el porqué en el
   comentario del HTML. Cuerpo normal, tono de apoyo, sin versales. */
.hero__dice{ margin:0; font-size:var(--t4); color:var(--tx-2); max-width:44ch }

/* --- cabecera de sección: 3 registros en vez de 1 --- */
.cabecera-seccion{ display:grid; gap:1rem; margin-bottom:clamp(2.5rem,6vw,5rem); max-width:62ch }
.cabecera-seccion .d2{ margin:0 }

/* --- METADATOS por caso: aquí vive el texto chico --- */
.meta-lista{ display:grid; gap:.5rem; margin:1.25rem 0 0;
             border-top:1px solid var(--tx-linea); padding-top:1rem }
.meta-lista div{ display:grid; grid-template-columns:5.5rem 1fr; gap:1rem }
.meta-lista dt{ color:var(--tx-3); font-weight:500 }
.meta-lista dd{ margin:0; color:var(--tx-2); font-weight:500 }
.pie-foto{ margin:.65rem 0 0; color:var(--tx-3) }
.caso__texto{ font-size:.9375rem; line-height:1.6; max-width:42ch; font-weight:var(--p-ui) }
.caso__num{ margin:0; color:var(--tx-3) }
/* En teléfono el caso se apila y el número queda pegado al pie de la foto de
   arriba: medido, 33px de encimado ("04" pintado sobre "Marca propia. La
   tienda en vivo…"). En escritorio no pasa porque van en columnas distintas.
   El aire va aquí y no en `.caso{gap}` para no separar también los metadatos. */
/* El aire va en `.caso__meta` y NO en `.caso__num`: el primer hijo de la columna
   es `.caso__punto`, el círculo de color del cliente, así que un margen en el
   número deja al punto sin holgura y se encima con el pie de foto de arriba.
   Se vio al subir el pie a 15px —pasó a dos líneas— y el punto cayó sobre la
   segunda. El margen en el contenedor cubre a los dos. */
@media (max-width:899px){
  .caso__meta{ margin-top:2rem }
  .caso__num{ margin-top:0 }
}

/* --- SERVICIOS con más dentro --- */
.servicio__num{ color:var(--tx-3); margin:0 }
.lista-fina{ list-style:none; margin:.5rem 0 0; padding:0; display:grid; gap:.5rem }
.lista-fina li{ color:var(--tx-2); padding-left:.9rem; position:relative }
.lista-fina li::before{ content:''; position:absolute; left:0; top:.55em;
                        width:4px; height:1px; background:var(--tx-3) }
.servicio__plazo{ color:var(--oro-vivo); margin:.75rem 0 0 }

/* --- SISTEMAS y PORTAL: descentrados (25% → objetivo <12%) --- */
.sistemas__caja,.portal__caja{ display:grid; gap:2.5rem; width:100%; max-width:1180px;
                               text-align:left }
.sistemas__cuerpo{ display:grid; gap:2rem }
/* Igual que el portal: el lienzo clavado reparte en columna y el panel se queda
   con el hueco que sobra. Los tres enunciados NO se encogen. */
/* 🔴 EL RELLENO DE ARRIBA LE ROBABA 27 px A LA LÁMINA. Medido a 1512×900: el
   lienzo daba 99 px de relleno superior y la figura se quedaba en 612 de alto,
   con la imagen limitada por el ANCHO (755×472) y 140 px de hueco vertical sin
   usar. Ahora que el archivo tiene la forma del hueco, ese alto sí se aprovecha
   — pero sólo si existe. La barra de navegación mide 72; con 5.5rem quedan 16
   de holgura y el rótulo no la toca. */
#sistemas .pin__lienzo{ display:flex; flex-direction:column;
                        padding:clamp(4.4rem,8.5vh,5.5rem) 0 clamp(1.2rem,3vh,2.2rem) }
#sistemas .sistemas__caja{ flex:1; min-height:0; margin-inline:auto;
                           display:flex; flex-direction:column; gap:1.5rem }
#sistemas .sistemas__cuerpo{ flex:1; min-height:0; display:flex;
                             flex-direction:column; gap:1.5rem }
#sistemas .pasos{ flex:none }

/* ═══════════════════════════════════════════════════════════════════════
   03 · SISTEMAS — EL RIEL Y LA LENTE
   ───────────────────────────────────────────────────────────────────────
   El texto se retira a un riel estrecho a la izquierda y la imagen se lleva
   el resto, saliéndose por el borde derecho. Medido antes de tocar nada: la
   imagen ocupaba 471×429 px —el 14 % de una pantalla de 1440×900— flotando
   con una sombra genérica, mientras el bloque de texto se llevaba 244 px de
   alto. Con esa proporción el relevo de la imagen no se percibe: lo que
   cambia es demasiado chico. Ahora la lámina se lleva el 62 % del ancho.
   ═══════════════════════════════════════════════════════════════════════ */
/* ── TRES COMPOSICIONES, NO UNA CON EL CONTENIDO CAMBIANDO ──────────────────
   Cada paso es una MINI SECCIÓN: el texto y la lámina cambian de SITIO, no sólo
   de contenido. En una columna fija con la imagen siempre a la derecha, tres
   pasos se leen como tres diapositivas del mismo molde; moviendo la composición
   entera, cada paso tiene su propio aire y el recorrido se siente como avanzar
   por tres pantallas en vez de esperar a que se recargue una.
     01 · texto a la izquierda, lámina a la derecha
     02 · se cruzan: lámina a la izquierda, texto a la derecha
     03 · el texto sube a una esquina y la lámina crece y baja
   🔴 LOS DOS BLOQUES VIVEN EN LA MISMA CELDA (`grid-area:1/1`) y se colocan con
      `transform`. Con columnas de rejilla habría que animar `grid-template`, que
      no interpola de forma útil —la misma familia del `flex-grow` que ya se
      pagó, donde un acordeón de 550 ms se consumaba en 38—. Con transform el
      movimiento es real y además va en la GPU.
   🔴 LOS PORCENTAJES SON DEL PROPIO ELEMENTO, no de la caja: el riel mide el
      34 % y la lámina el 62 %, así que un `translateX(188%)` del riel lo lleva
      al 64 % de la caja. Suma comprobada en los tres pasos para que nada se
      salga: 34+64=98, 62+38=100, 62+35=97. */
@media (min-width:960px){
  #sistemas .sistemas__caja{ max-width:none; width:100%;
    display:grid; grid-template-columns:1fr; align-items:center;
    padding-inline:var(--margen,clamp(1.25rem,5vw,4.5rem)) }
  #sistemas .sis__riel,
  #sistemas .sistemas__cuerpo{
    grid-area:1/1; align-self:center; justify-self:start;
    transition:transform .78s var(--curva);
    will-change:transform }
  /* 🔴 EL TOPE DE 440 px PARTÍA MAL EL TITULAR. A 1920 el riel se quedaba en
     440 y «Lo que no se ve es lo que sostiene» caía en cuatro líneas con un
     «que» solo en la tercera — un huérfano en el sitio donde más se mira. El
     tope sube a 580: a 72 px la frase entra en tres líneas con cortes que
     respetan el sentido. */
  #sistemas .sis__riel{ width:min(36%,580px); display:flex; flex-direction:column;
                        gap:clamp(1.5rem,3.5vh,2.5rem); z-index:2 }
  /* 🔴 EL ANCHO ES OBLIGATORIO. Al pasar a una sola celda con `justify-self:start`
     la caja de la lámina se encoge a su contenido, y como el panel es `flex:1`
     de una columna sin ancho, medía CERO —comprobado, 392 px de riel contra 0 de
     figura— y la imagen desaparecía sin error. Es la hermana del `flex:1` sin
     altura que ya se pagó en esta misma sección. */
  #sistemas .sistemas__cuerpo{ width:66%; margin-right:0 }
  /* 🔴 LA LÁMINA TAMBIÉN DERIVA CON EL SCROLL, no sólo salta entre pasos. Con
     los tres saltos y nada más, el recorrido se sentía a tirones: quieto,
     salto, quieto. Una deriva continua de 26 px durante todo el pin —en sentido
     contrario al texto, que sube— es lo que cose los tres momentos en un solo
     movimiento. Va en una capa aparte del salto de composición para que las dos
     transformaciones no se peleen por la misma propiedad.
     🔴 Y AHORA TAMBIÉN SE ACERCA. 26 px de deriva en 2.2 pantallas es un
     movimiento que existe pero no se percibe: son 12 px por pantalla, por
     debajo del umbral en el que el ojo lee «esto se está moviendo». Lo que sí
     se percibe es un cambio de TAMAÑO continuo — la cámara entrando— porque
     afecta a la imagen entera y no a su posición. Un 5 % repartido en todo el
     recorrido son 38 px de crecimiento en la lámina: lento, constante, y no
     compite con los tres saltos porque no va en la misma dirección. El origen
     está por encima del centro para que la lámina crezca hacia el reflejo y no
     al revés. */
  #sistemas .panel3d{
    transform:translate3d(0, calc(var(--av,0) * 30px - 15px), 0)
              scale(calc(1 + var(--av,0) * .05));
    transform-origin:50% 38%;
  }

  #sistemas[data-paso="1"] .sis__riel     { transform:translate(0, -4%) }
  #sistemas[data-paso="1"] .sistemas__cuerpo{ transform:translate(61%, 5%) }

  #sistemas[data-paso="2"] .sis__riel     { transform:translate(188%, 9%) }
  #sistemas[data-paso="2"] .sistemas__cuerpo{ transform:translate(0, -7%) }

  /* 🔴 EL PASO 3 NO PUEDE SUBIR MÁS DE LA CUENTA. A −32 % el riel se iba 155 px
     arriba y el rótulo «03 — SISTEMAS» aterrizaba a 45 px del borde, o sea
     DENTRO de la barra de navegación, pisando el logo. El techo lo pone la
     barra, no el gusto. */
  #sistemas[data-paso="3"] .sis__riel     { transform:translate(8%, -16%) }
  #sistemas[data-paso="3"] .sistemas__cuerpo{ transform:translate(56%, 9%) scale(1.06) }

  /* el velo sigue a la lámina: si se quedara clavado a la derecha, en el paso 02
     la imagen se iría a la izquierda y volvería a posarse sobre la nebulosa
     🔴 Y TIENE QUE LLEGAR ENTERO HASTA EL CANTO DEL ARCHIVO, NO CASI. Medido en
     el sitio: el aire de tinta de dentro del render da 16 niveles y la sección
     justo alrededor daba 27 — once de escalón, que es exactamente el «cuadro
     negro» que se ve. No era la máscara: era que el velo se quedaba corto. Su
     elipse cubría del 51 % al 97 % del ancho y la lámina empieza en el 43 %, o
     sea que ocho puntos de imagen se posaban sobre la nebulosa iluminada. Ahora
     el campo de tinta es MACIZO hasta el 46 % de su radio —no un degradado que
     empieza a aclarar desde el primer píxel— y el óvalo abarca la lámina entera
     por los cuatro lados. El escalón se mide, no se juzga a ojo: si vuelve a
     aparecer un rectángulo, lo primero es comparar estos dos números. */
  #sistemas[data-paso="2"] .pin__lienzo{
    background:radial-gradient(64% 86% at 32% 46%,
      var(--tinta) 0%, var(--tinta) 46%, rgba(8,9,12,.86) 74%, transparent 100%) }
  #sistemas[data-paso="3"] .pin__lienzo{
    background:radial-gradient(68% 88% at 66% 56%,
      var(--tinta) 0%, var(--tinta) 46%, rgba(8,9,12,.86) 74%, transparent 100%) }
  #sistemas .pin__lienzo{ transition:background .78s var(--curva) }
  /* la lámina SANGRA por la derecha: el render ya trae aire de sobra en el
     canto, así que salirse no corta nada y le quita a la sección el aire de
     «cuadro colgado en el centro».
     🔴 Y LLEVA ALTO PROPIO, no `flex:1`. Como hija de un grid con
        `align-items:center` la caja no tiene altura que repartir, así que el
        `flex:1` del panel daba CERO: la imagen desaparecía sin un error y sin
        un hueco —medido, 831×0 px—. Es la misma familia de trampa que el
        `aspect-ratio` con `stretch` que ya se pagó en la tira. */
  #sistemas .sistemas__cuerpo{ margin-right:calc(-1 * clamp(1rem,4vw,3.5rem));
                               height:min(68vh, 44vw) }
}
/* ── EL CUADRO NEGRO NO ERA DE LA IMAGEN, ERA DEL FONDO ─────────────────────
   🔴 MEDIDO sobre la sección real: la nebulosa de la sección da 53 niveles, el
      aire del render da 7 y la lámina 33. O sea que la lámina es MÁS OSCURA que
      el fondo sobre el que se posa: el render no se leía como una pantalla
      encendida sino como un agujero recortado en la nube. Ninguna máscara
      arregla eso, porque el problema no es el canto — es que la imagen aporta
      OSCURIDAD donde el fondo aporta luz.
      La regla del mundo oscuro es que el piso va a CERO y la luz la pone el
      contenido. Así que la nebulosa se apaga justo donde vive la lámina, con un
      velo ancho y suave: el plano queda en tinta y la lámina pasa a ser lo más
      claro del cuadro. Es lo mismo que hace el plató de la película, sólo que
      allí ya venía puesto.
   🔴 VA DETRÁS Y DESBORDA A PROPÓSITO. Si el velo midiera lo mismo que la
      figura, su propio canto sería el cuadro que estamos quitando: tiene que
      morir bastante más allá que la imagen. */
   🔴 Y NO PUEDE IR EN `z-index:-1`. Ahí es donde lo puse primero y no se pintó
      NADA: la nebulosa de esta sección la dibuja `.placa--polvo`, que es una
      capa `absolute` con `z-index:0`, así que un velo en −1 queda por DEBAJO de
      lo que venía a apagar. Medido antes y después del cambio: fuera de la
      figura seguía dando 35 niveles, exactamente los mismos que sin velo. Va en
      0, y la figura sube a 1 para quedar por encima. */
   🔴 Y EL RIEL TIENE QUE SUBIRSE. El velo vive en la columna de la imagen, que
      va DESPUÉS en el DOM: sin `z-index` propio, el texto quedaba por debajo y
      el titular se apagaba con la nebulosa —medido: el h2 pasó de blanco a gris
      medio—. Un velo que se come su propio titular no es atmósfera, es un bug. */
   🔴 Y NO PUEDE SER UN PSEUDO DENTRO DEL CUERPO. Ahí estaba, desbordando por
      los cuatro lados para morir lejos de la imagen, y el `overflow:clip` del
      lienzo del pin le cortaba la parte de arriba: un ESCALÓN de 22 niveles en
      cuatro píxeles, medido a lo largo de una columna. O sea que quitando un
      cuadro con canto duro se dibujaba otro. Va como fondo del propio lienzo,
      que es el elemento que el pin mueve: no hay caja que lo recorte. */
#sistemas .sis__riel{ position:relative; z-index:2 }
#sistemas .pin__lienzo{
  background:radial-gradient(64% 86% at 68% 52%,
    var(--tinta) 0%, var(--tinta) 46%, rgba(8,9,12,.86) 74%, transparent 100%);
}
#sistemas .panel3d{ perspective:none; min-height:0; flex:1;
                    position:relative; z-index:1 }
/* ── TELÉFONO: SE ENTRA A LA LÁMINA ─────────────────────────────────────────
   🔴 EL RENDER TRAE UN 30 % DE AIRE ALREDEDOR, y en un teléfono ese aire es
      carísimo: medido a 390 px, la interfaz quedaba en 200 px de ancho y no se
      leía una sola fila — «se ve que hay un panel» y nada más, que es
      exactamente el defecto que esta sección venía a corregir. En pantalla
      angosta se ENTRA a la imagen: se escala hasta que la lámina llena el ancho
      y el aire se va fuera del recuadro.
   🔴 Y LA MÁSCARA SE MUDA AL CONTENEDOR. Vive en la imagen, y al escalarla la
      banda de disolución se sale del recuadro: el canto volvería a ser una
      línea dura. En el contenedor se queda donde se ve. */
@media (max-width:959px){
  #sistemas .sistemas__cuerpo{ height:min(52vh, 78vw);
    margin-inline:calc(-1 * var(--margen,clamp(1.25rem,5vw,4.5rem))) }
  /* el velo se recentra: en escritorio la lámina vive a la derecha, en teléfono
     va centrada y algo más abajo, debajo del texto */
  #sistemas .pin__lienzo{
    /* mismo arreglo que en escritorio: campo de tinta MACIZO bajo la lámina, o
       el aire del archivo (16 niveles) vuelve a dibujar su rectángulo contra la
       nebulosa. En teléfono además la lámina va escalada 1.12, así que el óvalo
       tiene que ser más ancho que el recuadro. */
    background:radial-gradient(104% 56% at 50% 66%,
      var(--tinta) 0%, var(--tinta) 48%, rgba(8,9,12,.86) 76%, transparent 100%) }
  /* 🔴 LA MÁSCARA SE QUEDA EN LA IMAGEN, NO PASA AL CONTENEDOR. La moví aquí
     pensando que al escalar la banda de disolución se saldría del recuadro, y
     el resultado fue el mismo fallo de escritorio con otra cara: el contenedor
     mide 390×480 y la imagen escalada 554×346, así que el 14 % que se disolvía
     era HUECO y el canto de verdad quedaba a pelo — medido, un escalón de 22
     niveles justo donde empieza la imagen. En la imagen, la banda escala CON
     ella y sigue valiendo el 13 % del archivo. Y por eso el recuadro no puede
     recortar: la disolución vive fuera de él, sangrando por los lados, que es
     justo lo que se busca en un teléfono. */
  #sistemas .panel3d{ overflow:visible }
  /* 🔴 1.42 ERA PARA UN ARCHIVO CON 30 % DE AIRE. Ahora la lámina llega al 92 %
     del ancho en la pieza apaisada, así que ese mismo 1.42 la sacaría medio
     panel fuera del teléfono. Con 1.12 la interfaz sigue llenando el ancho y no
     se pierde ni una fila. */
  #sistemas .vista-p{ transform:scale(1.12) }
  /* En una columna no se pueden cruzar los bloques, pero la lámina sí se
     desplaza a un lado y a otro y el último paso se acerca: el recorrido sigue
     teniendo tres composiciones, sólo que en versión estrecha. El recorte lo
     absorbe el `overflow:clip` del lienzo, así que no aparece barra lateral. */
  #sistemas .sis__riel,
  #sistemas .sistemas__cuerpo{ transition:transform .7s var(--curva) }
  #sistemas[data-paso="1"] .sistemas__cuerpo{ transform:translateX(-7%) }
  #sistemas[data-paso="2"] .sistemas__cuerpo{ transform:translateX(7%) }
  #sistemas[data-paso="2"] .sis__riel{ transform:translateX(4%) }
  #sistemas[data-paso="3"] .sistemas__cuerpo{ transform:scale(1.06) }
  #sistemas .panel3d figcaption{ left:var(--margen,1.25rem) }
}

/* ── EL TITULAR SE MUEVE CON EL SCROLL ──────────────────────────────────────
   Dentro de un pin la página deja de avanzar, y una sección clavada en la que
   sólo cambia una imagen se siente detenida aunque el dedo siga bajando. El
   titular sube 46 px a lo largo del recorrido y el rótulo el doble de despacio:
   dos velocidades distintas es lo que se lee como profundidad — a la misma
   velocidad sería un bloque deslizándose.
   🔴 EL RECORRIDO ES CORTO A PROPÓSITO. Con 120 px el titular se despegaba del
      enunciado y a mitad de sección quedaba un hueco sin nada. Lo que se busca
      es que el riel RESPIRE, no que viaje.
   🔴 `--av` LO ESCRIBE EL MISMO `onUpdate` que decide el paso. Colgar esto de un
      `scrollY` propio dentro de un pin es la trampa ya pagada: dentro del pin el
      scroll sigue creciendo aunque la sección esté quieta, y el valor deriva. */
#sistemas .d2{
  transform:translate3d(0, calc(var(--av,0) * -46px), 0);
  will-change:transform;
}
#sistemas .rotulo{
  transform:translate3d(0, calc(var(--av,0) * -22px), 0);
}
/* 🔴 EL BLOQUE DEL PASO SUBE A OCUPAR EL SITIO DE LA TESIS. Sin esto, cuando el
   titular se retira queda un agujero de 450 px entre el rótulo y el enunciado —
   medido en la captura del paso 3—: el texto se va pero su hueco no, y eso se
   ve peor que dejarlo. `--tesis` lo escribe el JS con el alto real del titular,
   porque depende de la letra cargada y del ancho: cablearlo en píxeles lo
   rompería en cuanto la frase pase de tres a cuatro líneas. */
#sistemas .sis__paso{
  transform:translate3d(0, calc(var(--av,0) * -14px), 0);
  transition:transform .8s var(--curva);
}
#sistemas[data-paso="2"] .sis__paso,
#sistemas[data-paso="3"] .sis__paso{
  transform:translate3d(0, calc(var(--av,0) * -14px - var(--tesis,0px)), 0);
}
@media (prefers-reduced-motion:reduce){
  #sistemas .d2,
  #sistemas .rotulo,
  #sistemas .sis__paso{ transform:none }
}

/* EL PASO: número, enunciado y avance. Los tres enunciados viven APILADOS en
   la misma caja —el alto lo fija el más largo con `grid-area:1/1`— para que al
   relevarse no salte el resto del riel. */
.sis__paso{ display:grid; gap:1rem }
/* el número lo escribe el JS y no un `counter-reset:paso var(--paso)`: el
   contador con variable falla en silencio en cuanto el valor no llega como
   entero puro, y un rótulo vacío no se parece a un error */
.sis__num{ font-size:var(--t6); letter-spacing:.16em; color:var(--oro);
           font-variant-numeric:tabular-nums }
/* ── EL TITULAR CEDE EL PASO ────────────────────────────────────────────────
   La frase «Lo que no se ve es lo que sostiene» es la TESIS de la sección: se
   dice una vez. Manteniéndola a 72 px en los tres pasos competía con el
   enunciado vivo —que es la evidencia— y las tres composiciones se veían
   cargadas por el mismo motivo. A partir del segundo paso se retira: baja de
   peso óptico y se queda de fondo, y el enunciado pasa a ser lo más claro del
   riel. No se encoge de tamaño (eso se leería como un error de maquetación),
   se APAGA. */
   🔴 Y NO SE APAGA: SE VA. Primero se probó bajándola a `opacity:.30`, y a
      72 px una masa gris sigue pesando más que el enunciado vivo —se veía en la
      captura del paso 3: un bloque apagado ocupando media pantalla y el
      argumento del paso debajo, pequeño—. El movimiento lo lleva el JS, con el
      mismo recorte por líneas y la misma curva que el resto de la sección. */

#sistemas .pasos{ display:grid; align-items:start }
/* 🔴 EL ENUNCIADO ES EL TITULAR DE SU PASO, NO UN PIE. Estaba a 22 px porque
   competía con la tesis; pero desde que la tesis se retira, el paso se quedaba
   con una sola línea pequeña en media pantalla vacía — «no hay textos
   llamativos, se ve raro», y con razón. A `--t3` (44 px) cada mini-sección
   tiene su propio titular, que es lo que la hace sección. La brecha con la
   tesis sigue siendo sana: 72 contra 44 mientras conviven en el primer paso, y
   a partir del segundo ya no conviven. */
#sistemas .paso{
  grid-area:1/1; margin:0;
  font-family:var(--display); font-weight:var(--p-voz);
  font-size:var(--t3); line-height:1.12; letter-spacing:-.012em;
  max-width:15ch; color:var(--tx-1);
  /* el que no está vivo no se atenúa: se QUITA. Tres frases superpuestas a
     opacidad baja se leen como una mancha, no como texto de fondo */
  visibility:hidden;
  transition:visibility 0s linear .5s;
}
#sistemas .paso.vivo{ visibility:visible; transition:visibility 0s }

/* ── LAS LÍNEAS ─────────────────────────────────────────────────────────────
   El movimiento lo lleva GSAP con `SplitText` (`mask:'lines'`), que es lo que
   ya usa el resto del sitio en `.js-partir`. Aquí sólo queda lo que el CSS
   tiene que aportar: que la ventana de recorte respire por debajo, o las
   descendentes de la letra —la «j» de «deja», la «q» de «que»— se ven cortadas
   al ras mientras la línea viaja. */
#sistemas .sis-linea{ padding-bottom:.12em; margin-bottom:-.12em }
/* 🔴 SIN JS LA SECCIÓN SIGUE SIENDO LA DE SIEMPRE. `.paso` arranca oculto y es
   el script el que enciende uno; si el script no corre, no habría texto — una
   sección muda. Sin JS los tres se ven en columna, que es la lista de antes.
   🔴 LA CONDICIÓN ES `[data-paso]`, NO «no hay ninguno vivo». Con
      `:not(:has(.paso.vivo))` el respaldo también se cumplía en el instante
      entre que carga la página y el pin dispara por primera vez: al entrar en
      la sección se veía un PARPADEO con las tres frases apiladas encima del
      panel. Se cazó grabando el recorrido, no mirando estados sueltos. El
      script marca la sección en cuanto arranca, así que en cuanto hay JS el
      respaldo se apaga para siempre. */
#sistemas:not([data-paso]) .pasos{ display:grid; gap:1rem }
#sistemas:not([data-paso]) .paso{ grid-area:auto; visibility:visible }
@media (max-width:959px){
  .sis__paso{ gap:1rem }
  #sistemas .paso{ max-width:none }
}
/* ═══════════════════════════════════════════════════════════════════════
   EL PANEL EN ESQUIRLAS — sustituye a la captura completa
   ───────────────────────────────────────────────────────────────────────
   La captura entera al 100% del ancho salía CORTADA por los dos lados y
   además plana y de frente. Medido en 8 sitios cuyo producto es una
   interfaz: raycast enseña 17 recortes de 38 piezas, family 9 de 20, arc
   6 de 6 — entran DENTRO de su interfaz en vez de enseñarla completa, y a
   25-57% del ancho. La perspectiva existe pero es SUAVE y minoritaria
   (linear 1000px con giro -30°, resend 2000px, family 600px; raycast, arc,
   vercel y rive no usan ninguna). Y la técnica que de verdad domina son
   las MÁSCARAS: clerk 85, linear 16, raycast 13.

   Las tres esquirlas salen del MISMO archivo con distinto encuadre: son
   recortes reales del panel que operamos, no maquetas nuevas. Un solo
   asset, tres profundidades.
   ═══════════════════════════════════════════════════════════════════════ */
.panel3d{
  margin:0; flex:1; width:100%; min-height:260px; position:relative;
  /* 1400px: dentro del rango medido y del lado suave. Con 600px el giro de
     8° deformaría el texto de la captura hasta hacerlo ilegible. */
  perspective:1400px; perspective-origin:58% 42%;
}
/* UNA VISTA A LA VEZ. Las tres viven apiladas en el mismo sitio y solo una
   está descubierta; el relevo es una CORTINA, no un fundido.
   `clip-path:inset(0 100% 0 0)` es el mecanismo exacto de izanami —17
   clip-paths y cero máscaras en todo su sitio—: la imagen se descubre
   barriendo. Un fundido entre dos capturas del mismo panel casi no se
   percibe, y eso era justo el problema: "no se ve cambio a la vista". */
/* 🔴 SIN SOMBRA Y SIN RADIO: LA LUZ YA VIENE EN EL PÍXEL. Estas tres imágenes
   están renderizadas en el plató —la lámina trae su propio canto, su derrame y
   su viñeta, y el borde del archivo ya es tinta pura (medido: 6 niveles, contra
   los 8 del fondo del sitio)—. Una `box-shadow` de CSS encima le dibujaría un
   segundo canto al canto, y un `border-radius` recortaría el aire que el render
   dejó a propósito. La imagen entra a sangre y desaparece en el fondo.  */
/* 🔴 LA CAJA TIENE QUE MEDIR LO QUE MIDE LA IMAGEN. Con `width/height:100%` +
   `object-fit:contain` el elemento seguía siendo el rectángulo del contenedor y
   la imagen se dibujaba centrada dentro, con 93 px de vacío arriba y abajo: la
   máscara —que se aplica al ELEMENTO— se gastaba disolviendo ese vacío y
   llegaba al canto de verdad completamente opaca. Resultado: un borde duro
   exactamente donde empieza la imagen, medido como un escalón de 22 niveles en
   cuatro píxeles. Con `max-width/max-height` y las medidas en `auto` la caja se
   ajusta a la imagen escalada, y entonces el 13 % que se disuelve es el 13 % del
   archivo. */
.vista-p{
  position:absolute; inset:0; margin:auto;
  width:auto; height:auto; max-width:100%; max-height:100%;
  clip-path:inset(0 100% 0 0);
  /* 🔴 LA IMAGEN VA PRIMERO Y EL TEXTO LA SIGUE. Grabando el scroll y mirando
     cuadro a cuadro salió el desorden: el número saltaba al instante, el
     enunciado entraba en .40 s y la cortina de la imagen tardaba .62 — o sea
     que se leía la frase nueva sobre la imagen vieja. Aquí el sujeto es la
     imagen y el texto es su pie: la cortina baja a .52 s y el enunciado entra
     con .20 s de retraso, así que aterrizan juntos y en ese orden. */
  transition:clip-path .52s cubic-bezier(.62,0,.2,1);
  will-change:clip-path;
  /* 🔴 EL CANTO DEL ARCHIVO SE DISUELVE, O SE VE EL RECTÁNGULO. El render sale
     con el borde en tinta pura (6 niveles) y encima de esta sección hay una
     placa de polvo que la aclara: la imagen quedaba dibujando un rectángulo
     OSCURO sobre un fondo más claro — otra vez «una captura pegada», ahora al
     revés. La máscara se lleva el 30 % exterior del archivo, que es justo el
     aire que el render dejó de sobra alrededor de la lámina, así que no recorta
     nada de la interfaz. Y va sobre la imagen, no sobre `.panel3d`: el
     contenedor también sostiene el pie, que sí tiene que verse entero. */
     🔴 Y NO SIRVE UN RADIAL. Se probó `radial-gradient(112% 112%, #000 58%,
     transparent 92%)` y el rectángulo seguía dibujándose: en un radial, el
     punto medio de cada lado queda a 0.89 del radio, o sea todavía casi opaco —
     se disuelven las ESQUINAS y no los cantos, que es justo al revés de lo que
     hace falta. Son dos degradados lineales cruzados, uno por eje, y sólo
     comen el 13 % exterior: la lámina vive entre el 15 % y el 85 % del archivo,
     así que no se toca ni un píxel de interfaz. */
     🔴 LA BANDA ES DE CADA PIEZA, NO DEL CSS — `--banda`, en el HTML. Aquí
     estaba el «cuadro negro» que volvía una y otra vez, y el motivo es que las
     tres piezas NO tienen la misma proporción: en el cuadro que les toca, la
     prioridad se lleva el 57 % del ancho y le quedan 21 puntos de tinta a cada
     lado, mientras la bandeja se lleva el 84 % y le quedan 8. Con un número
     fijo sólo puede acertar en una: a 6 % la prioridad conservaba QUINCE
     PUNTOS de negro opaco contra un fondo iluminado —el rectángulo, otra vez—
     y a 19 % la bandeja se habría quedado sin dos columnas de interfaz. Lo
     calcula `lente-sis.py`, que es el único que sabe cuánto aire dejó, y lo
     imprime listo para pegar: `--banda: (1−lámina)/2 − 2 puntos`. Si se
     recompone una pieza, se vuelve a copiar el número. */
     🔴 Y EL EJE VERTICAL YA NO ES SIMÉTRICO, porque la escena tampoco lo es. El
     panel se subió en el cuadro para dejarle sitio al reflejo, y a 9 % la banda
     de arriba le mordía la cabecera —medido: 32 niveles de panel dentro de la
     banda en la pieza 02—. Arriba baja a 5 %; abajo sube a 12 %, que es lo que
     hace que el reflejo se apague solo contra el borde en vez de terminar en
     una línea recta. */
  -webkit-mask-image:linear-gradient(to right,transparent 0,#000 var(--banda,6%),#000 calc(100% - var(--banda,6%)),transparent 100%),
                     linear-gradient(to bottom,transparent 0,#000 5%,#000 88%,transparent 100%);
          mask-image:linear-gradient(to right,transparent 0,#000 var(--banda,6%),#000 calc(100% - var(--banda,6%)),transparent 100%),
                     linear-gradient(to bottom,transparent 0,#000 5%,#000 88%,transparent 100%);
  -webkit-mask-composite:source-in; mask-composite:intersect;
}
.vista-p.viva{ clip-path:inset(0 0 0 0) }
/* La que sale se va por el OTRO lado: si entrara y saliera por el mismo borde
   el gesto se leería como un rebobinado. */
.vista-p.saliendo{ clip-path:inset(0 0 0 100%) }
@media (prefers-reduced-motion:reduce){
  .vista-p{ transition:none }
}
/* 🔴 EL PIE SE ANCLA A LA IZQUIERDA. Con la figura sangrando por el borde
   derecho, un pie pegado a `right:0` se sale de la pantalla con ella: quedaba
   «VISTA DE DEMOSTRACIÓN · DATOS INV», cortado a media palabra. Y esa frase es
   justo la que no puede quedar a medias — es la que dice que los datos son
   inventados. */
/* 🔴 Y VA DENTRO DE LA FIGURA, NO COLGANDO DEBAJO. Con `bottom:-1.35rem` el pie
   vivía fuera de la caja, así que cualquier cosa que empujara la figura hacia
   abajo lo sacaba de la pantalla: en el paso 3 —que baja un 9 % y encima
   escala— quedaba cortado a media línea. Y es la frase que dice que los datos
   son inventados: la única de la sección que no se puede leer a medias. Dentro
   de la figura no le pasa, y además el sitio donde aterriza es la cola del
   reflejo, que ya está apagada: se lee sin pelearse con nada. */
.panel3d figcaption{ position:absolute; left:0; bottom:.35rem; z-index:2;
  font-size:var(--t6); letter-spacing:0; text-transform:none;
  color:var(--tx-3) }
@media (max-width:899px){
  /* A 390px tres esquirlas superpuestas se tapan entre ellas. Se apilan en
     columna, sin perspectiva: el 3D a media pantalla es ruido, no profundidad. */
  /* A 390px la vista ocupa el ancho entero: el relevo se sigue viendo igual
     de bien y no hace falta perspectiva —el 3D a media pantalla es ruido. */
  .panel3d{ perspective:none; min-height:clamp(12rem,52vw,20rem) }
  .vista-p{ max-width:100%; border-radius:8px; box-shadow:0 18px 40px rgba(0,0,0,.5) }
}
@media (min-width:960px){
  .sistemas__cuerpo{ grid-template-columns:1.25fr .75fr; align-items:center }
  /* El texto se acuesta en dos columnas para dejarle el alto a la lente:
     titular a la izquierda, el cómo a la derecha. Antes el texto era una
     columna vertical y empujaba el marco a un tercio del ancho. */
  /* El párrafo se alinea por ABAJO con el titular, no por arriba. Con
     `align-items:start` arrancaba por encima de la primera línea del titular y
     a media pantalla de distancia: dos bloques sueltos que no se hablaban.
     Alineados por la base, la última línea del párrafo cae al ras de "propio
     lugar." y el conjunto se lee como un solo renglón editorial.
     La columna del titular crece porque es la que tiene 72px que colocar. */
  .portal__texto{ grid-template-columns:1.18fr .82fr; gap:clamp(2rem,4vw,4rem);
                  align-items:end }
  .portal__texto > :last-child{ padding-bottom:.35rem }
  .portal__texto .parrafo{ max-width:34ch }
}
/* El contenedor sigue al marco: en una pantalla ancha el tope de 1180 dejaba
   el producto flotando chico en medio de dos márgenes enormes. */
.portal__caja{ max-width:1440px }
.portal__texto{ display:grid; gap:1.5rem }
.portal__url{ margin-left:auto; color:var(--tx-3) }
.pasos-num{ list-style:none; margin:1rem 0 0; padding:0; display:grid; gap:.75rem;
            border-top:1px solid var(--tx-linea); padding-top:1rem }
.pasos-num li{ display:grid; grid-template-columns:2.2rem 1fr; gap:.75rem; color:var(--tx-2) }
.pasos-num span{ color:var(--oro) }

/* ═══ #entrada · LAS TRES PUERTAS ═════════════════════════════════════════
   Cuatro elementos por puerta: marca, plazo, una línea, CTA. Nada más — paco:
   "está muy cargado, no se ve clean". Las referencias limpias ponen DOS cosas
   por elemento (hello monday: una imagen, un título y dos etiquetas); nosotros
   traíamos cinco. */
.puertas{ margin-top:clamp(2.5rem,5vw,3.75rem); display:grid;
          border-top:1px solid var(--tx-linea) }
.pu{
  display:grid; align-items:baseline;
  grid-template-columns:1fr; gap:.5rem;
  padding:clamp(1.75rem,3.5vw,2.5rem) 0;
  border-bottom:1px solid var(--tx-linea);
}
@media (min-width:900px){
  /* marca · nombre · plazo · una línea · CTA — un solo renglón que se lee de
     izquierda a derecha, sin tarjetas y sin columnas que compitan.
     🔴 SON CINCO COLUMNAS PORQUE SON CINCO HIJOS. Al quitar `.pu__n` (el 01/02/03,
     que era mobiliario) la plantilla se quedó en seis y TODO SE CORRIÓ UNA
     COLUMNA: «Estás empezando» se encimó con «3 – 5». La rejilla explícita no
     avisa cuando le falta un hijo, sólo desplaza — es la misma familia de fallo
     que romper una sección quitándole un nodo. Si se agrega o se quita un hijo
     de `.pu`, se cuenta aquí. */
  .pu{ grid-template-columns:2.75rem minmax(0,1.05fr) auto minmax(0,1.35fr) auto;
       gap:0 clamp(1.5rem,2.6vw,2rem); align-items:center }
}
.pu__t{ font-family:var(--display); font-weight:400;
        font-size:clamp(1.375rem,2.1vw,1.75rem); letter-spacing:-.015em;
        line-height:1.05; margin:0 }
/* El plazo ocupa el lugar del precio, que es lo que la ley medida le da
   (webflow 32 contra título 24 · squarespace 40). Peso 400: la moda de las seis
   referencias frescas; veníamos en 500 y éramos de los pesados del grupo. */
.pu__plazo{ font-family:var(--display); font-weight:400;
            font-size:clamp(2.25rem,4.2vw,3.5rem); line-height:.9;
            letter-spacing:-.04em; margin:0; white-space:nowrap }
.pu__plazo em{ font-style:normal; font-size:var(--t6); color:var(--tx-2); letter-spacing:0 }
.pu__una{ font-size:var(--t5); line-height:1.5;
          color:var(--tx-2); margin:0; max-width:40ch }
/* CAPA 1 · ago-2026 — fuera el salto de tono a media frase. Ver trabajo.css. */
.pu__una b{ font-weight:var(--p-ui) }
.pu__cta{ margin:0 }
.pu__cta .boton{ padding:.6rem 1.15rem; font-size:var(--t6); white-space:nowrap }

/* ── LA MARCA QUE SE DIBUJA ────────────────────────────────────────────────
   NO es un ícono decorativo encima del título — eso es un tic reconocible que
   el canon nombra. Es una línea que OCURRE al llegar: se traza sola con
   `stroke-dashoffset` y el punto de acento aparece al final.
   Trazo 0.9 sobre un viewBox de 24 (el archivo trae 1.6): a 44px de tamaño,
   1.6 daría 2.9px efectivos y se vería gordo. La regla del canon es línea
   ultraligera. */
.pu__marca{ width:2.75rem; height:2.75rem; color:var(--tx-1); overflow:visible }
.pu__marca *{ fill:none; stroke:currentColor; stroke-width:.9;
              stroke-linecap:round; stroke-linejoin:round }
/* NADA de `vector-effect:non-scaling-stroke` aquí. Medido: con él, el navegador
   mide el guion en PÍXELES DE PANTALLA e ignora el `pathLength`, así que
   `stroke-dasharray:1` se volvía un guion de 1px con hueco de 1px y las marcas
   salían dibujadas a la mitad. Sin él, el guion vive en unidades del viewBox y
   `pathLength="1"` normaliza: un solo valor sirve para rectángulos, arcos y
   curvas por igual. El trazo escala con el tamaño, que es lo correcto. */
.pu__marca .ll{ fill:currentColor; stroke:none }
.pu-mov .pu__marca *{ stroke-dasharray:1; stroke-dashoffset:1;
                      /* `pathLength` normaliza: cada trazo mide 1, así que un
                         solo valor sirve para rectángulos, arcos y curvas */
                      }
.pu-mov .pu__marca .ll{ opacity:0 }
.pu.puesta .pu__marca *{ stroke-dashoffset:0;
  transition:stroke-dashoffset 1.05s cubic-bezier(.22,1,.3,1) }
.pu.puesta .pu__marca .ll{ opacity:1; transition:opacity .35s ease .75s }

/* ── LA SECCIÓN ANCLADA: UNA PUERTA A LA VEZ ───────────────────────────────
   Reusa `[data-paso]` del mecanismo de pin, que ya toggle-a `.vivo`. Lo que
   antes era un desplazamiento parejo de tres renglones —que a paco no se le
   veía, y con razón: 22.2% de cambio de pantalla— ahora es un relevo: la
   puerta activa se abre y las otras dos retroceden.

   Sigue cumpliendo la ley medida: nada se mueve solo. Todo está colgado del
   avance del pin, así que al parar el scroll se detiene en seco. */
#entrada .pin__lienzo{ display:grid; align-content:center;
                       /* `justify-items:stretch` explícito: `.pin__lienzo` trae
                          `place-items:center` y centraba la cabecera, que en
                          esta sección va alineada a la izquierda como el resto
                          del sitio. */
                       justify-items:stretch;
                       padding:clamp(3rem,7vh,5rem) 0 clamp(2rem,5vh,3.5rem) }
#entrada .cabecera-seccion{ margin-bottom:clamp(1.5rem,4vh,2.5rem) }

.pu{ transition:opacity .5s var(--curva) }
.pu-mov:not(.vivo){ opacity:.26 }
.pu-mov:not(.vivo) .pu__cta{ opacity:0; pointer-events:none }
.pu-mov.vivo{ opacity:1 }

/* La puerta activa CRECE. Es lo que hace que el relevo se vea sin necesidad de
   más tinta en pantalla: cambia la escala, no la cantidad. */
.pu-mov > *{ transition:transform .55s var(--curva), opacity .5s var(--curva) }
.pu-mov .pu__t{ font-size:var(--t4); transition:font-size .55s var(--curva) }
.pu-mov.vivo .pu__t{ font-size:var(--t3) }
/* La puerta activa no "crece un poco": SE APODERA. Medido, el escalón de 1.16
   daba 1.6% de cambio de pantalla por paso —invisible—. El plazo de la activa
   salta a escala de titular y el de las dormidas se encoge a una línea; eso sí
   reescribe la pantalla en cada relevo, sin agregar una sola cosa nueva. */
.pu-mov .pu__plazo{ font-size:var(--t3); transition:font-size .55s var(--curva) }
.pu-mov.vivo .pu__plazo{ font-size:var(--t1); transform:none }
.pu-mov .pu__una{ opacity:0; max-height:0; overflow:hidden;
                  transition:opacity .45s var(--curva), max-height .55s var(--curva) }
.pu-mov.vivo .pu__una{ opacity:1; max-height:9rem }
.pu-mov .pu__cta{ transition:opacity .45s var(--curva) .1s }

/* La marca se traza cuando SU puerta se enciende, no las tres a la vez. */
.pu-mov .pu__marca *{ stroke-dasharray:1; stroke-dashoffset:1 }
.pu-mov .pu__marca .ll{ opacity:0 }
.pu-mov.vivo .pu__marca *{ stroke-dashoffset:0;
  transition:stroke-dashoffset .95s cubic-bezier(.22,1,.3,1) }
.pu-mov.vivo .pu__marca .ll{ opacity:1; transition:opacity .3s ease .7s }
.pu-mov.vivo .pu__marca{ transform:scale(1.1) }

@media (prefers-reduced-motion:reduce){
  .pu-mov:not(.vivo){ opacity:1 }
  .pu-mov:not(.vivo) .pu__cta{ opacity:1; pointer-events:auto }
  .pu-mov .pu__una{ opacity:1 }
  .pu-mov .pu__marca *{ stroke-dasharray:none; stroke-dashoffset:0 }
  .pu-mov .pu__marca .ll{ opacity:1 }
  .pu-mov > *{ transform:none }
}

.entrada__nota{ margin:clamp(1.25rem,2.5vw,1.75rem) 0 0; font-size:var(--t6);
                color:var(--tx-3); max-width:56ch }
.entrada__prueba{
  list-style:none; margin:clamp(2rem,4vw,2.75rem) 0 0; padding:1.15rem 0 0;
  border-top:1px solid var(--tx-linea);
  display:flex; flex-wrap:wrap; gap:.75rem 2.5rem;
  font-size:var(--t5); color:var(--tx-2);
}
.entrada__prueba b{ font-family:var(--display); font-weight:var(--p-voz);
                    font-size:var(--t3); letter-spacing:-.02em; color:var(--tx-1);
                    margin-right:.35rem }

/* ── #entrada EN VENTANA BAJA ────────────────────────────────────────────────
   🔴 EL AIRE DE ESTA SECCIÓN ESTABA MEDIDO EN ANCHO Y EL PROBLEMA ES DE ALTO.
      Todos sus huecos son `vw` (`3.5vw` en cada puerta, `5vw` sobre la lista,
      `4vw` sobre la prueba), así que en una ventana ANCHA Y BAJA —que es
      justamente la de un portátil— el aire CRECE mientras el sitio para ponerlo
      se encoge. Medido: el contenido pide 889 px en un lienzo de 780, 952 en uno
      de 900. Y como el lienzo anclado centra con `align-content:center`, lo que
      sobra se reparte por los DOS lados: el rótulo «05 — POR DÓNDE ENTRAS» se
      iba a **y = −82** a 1440×780 y a −23 en un MacBook de 900 — o sea detrás de
      la barra de navegación, o directamente fuera de la pantalla.
   🔴 `safe center` ES LA MITAD DEL ARREGLO, no todo. Impide que el desbordamiento
      salga por ARRIBA —que es el lado donde hay una barra fija y donde perderlo
      significa perder el título de la sección—, pero si el contenido sigue sin
      caber, lo que se pierde es el pie por abajo. Así que además se aprieta, y
      se aprieta con `vh`: los mismos huecos, medidos contra la dimensión que de
      verdad escasea. */
@media (min-width:900px) and (max-height:1080px){
  #entrada .pin__lienzo{
    align-content:safe center;
    /* la barra mide 72: por debajo de 5.5rem el rótulo se le mete debajo */
    padding-top:max(5.5rem, 6vh);
    padding-bottom:clamp(1.25rem,3vh,2.5rem);
  }
  #entrada .cabecera-seccion{ margin-bottom:clamp(1rem,2.4vh,2rem) }
  #entrada .puertas{ margin-top:clamp(1rem,2.6vh,3.75rem) }
  #entrada .pu{ padding:clamp(.9rem,2.4vh,2.5rem) 0 }
  #entrada .entrada__nota{ margin-top:clamp(.75rem,1.8vh,1.75rem) }
  #entrada .entrada__prueba{ margin-top:clamp(1rem,2.2vh,2.75rem); padding-top:.85rem }
}
/* Y en una ventana MUY baja el titular también cede. A 1440×780 el contenido
   seguía pidiendo 845 px, y lo que sobraba se comía la fila de prueba por abajo
   —«4 negocios operando hoy»—, que es el único dato duro de la sección. El
   titular es lo único que queda con margen para dar: baja de la escala de
   display a la siguiente, sigue mandando en la pantalla, y con eso entra todo. */
@media (min-width:900px) and (max-height:860px){
  #entrada .d2{ font-size:clamp(2rem,3.4vw,2.75rem); line-height:1.04 }
}

.cierre{ text-align:left }
.cierre .d1{ max-width:15ch }

/* --- EL PIE: una SECCIÓN (0 → ~1 pantalla, 17 enlaces) --- */
.pie{ padding:var(--aire) var(--borde) 2rem; display:grid; gap:clamp(3rem,7vw,6rem);
      border-top:1px solid var(--tx-linea) }
.pie__alto{ display:grid; gap:1.5rem; max-width:46ch }
.pie__marca{ font-family:var(--marca); font-weight:var(--p-voz); margin:0 }
.pie__rejilla{ display:grid; gap:2.5rem 1.5rem; grid-template-columns:repeat(2,1fr) }
@media (min-width:860px){ .pie__rejilla{ grid-template-columns:repeat(5,1fr) } }
.pie__col{ display:grid; gap:.5rem; align-content:start }
.pie__col a{ font-size:var(--t5); color:var(--tx-2);
             transition:color var(--rapido) var(--curva) }
.pie__col a:hover{ color:var(--tx-1) }
.pie__dato{ color:var(--tx-3); margin:0; line-height:1.7 }
.pie__bajo{ display:flex; gap:1.5rem; flex-wrap:wrap; justify-content:space-between;
            color:var(--tx-3); border-top:1px solid var(--tx-linea); padding-top:1.5rem }

.boton--claro{ border-color:var(--tx-1) }

/* --- el hueco se ve igual en claro y en oscuro --- */
.hueco{ background:repeating-linear-gradient(135deg,transparent 0 11px,rgba(127,127,127,.10) 11px 12px) }
.oficio__plazo{ margin: 0; color: var(--tx-3); font-size: var(--t6); white-space: nowrap }
/* El oro #C6A961 sobre papel da 1.82 a 12px — casi invisible. El bronce
   mantiene la familia cálida y da 4.84. */
/* mismo caso que `.pieza__si`: 12px de bronce de papel sobre la tinta */
.oficio__plazo .mono{ color: var(--bronce-claro, #C6A961); font-size: var(--t6); font-weight: var(--p-ui) }

/* ═══════════════════════════════════════════════════════════════════════
   LOS FRAGMENTOS — sustituyen al visor a sangre
   ───────────────────────────────────────────────────────────────────────
   El visor a sangre se va, y la razón está medida: de 11 referencias,
   stripe y rive —los dos más limpios— tienen CERO piezas a sangre completa.
   Ponerlo a sangre fue sobrecorrección mía tras medir que la sección no
   tenía media; paco lo dijo enseguida ("se ve muy grande, el texto pasa a
   ser secundario") y los números le dan la razón.

   Lo que entra es la escuela de stripe (251 piezas medidas) y family (259):
   · CERO bordes y CERO sombras — ninguna de esas 510 piezas tiene
   · proporciones de 0.59 a 2.48 en stripe; aquí de ~0.6 a ~4.7
   · rango de ancho del 6% al 96.7% de la ventana; tres niveles de tamaño
   · radios: solo 0 y 6px
   Lo que separa una pieza de otra NO es un borde: es su TONO y su forma.

   Y la regla que las hace valer, que salió de la corrección de paco: cada
   fragmento demuestra UNA de las partes que el renglón promete arriba. El
   visual deja de decorar y pasa a ser la prueba del texto de al lado.
   ═══════════════════════════════════════════════════════════════════════ */
/* El recorte va en la SECCIÓN y no en el artículo: recortando el artículo se
   perdería el sangrado de la pieza que se sale por la derecha, que es parte del
   lenguaje. Y va `clip` y no `hidden` — `hidden` crea un contenedor de scroll y
   rompería cualquier `sticky` de adentro.

   SIN MARCA DE AGUA. Estuvo aquí el nombre del oficio en enorme al 6% detrás de
   todo; paco lo quitó: "parece marca de agua y se ve barato". Tenía razón en lo
   que señala — un texto gigante al 6% es un recurso de plantilla, no una capa
   con intención, y encima obligaba a que TODO lo demás cargara `z-index` para
   librarlo. La presencia que daba la escala grande vuelve donde corresponde:
   al titular de verdad, que subió (ver `.oficio__titulo`). */
#servicios{ overflow-x: clip }
.pieza{
  border-radius: 6px; padding: 1.05rem 1.2rem; overflow: hidden;
  display: grid; gap: .75rem; align-content: start;
  background: var(--pz-1);
  /* el ensamblado por scroll las mueve: se les da su propia capa */
  will-change: transform;
}
/* Tres tonos, que es lo que hace el trabajo del borde ausente.
   SUBIERON de luminancia respecto a la primera versión: paco pidió que se leyera
   más claro, y las piezas vivían en #101216–#1F232B, o sea gris sobre gris a
   dos o tres niveles de la tinta. Ahora la separación contra el fondo es real
   sin que ninguna deje de ser oscura. */
:root{ --pz-1:#181C24; --pz-2:#12151B; --pz-3:#232833 }
.pieza--hundida{ background: var(--pz-2) }
.pieza--alta   { background: var(--pz-3) }
/* Dos piezas SIN caja: viven directo sobre la tinta. Es lo que impide que la
   rejilla entera se lea como un tablero de control. */
.pieza--suelta{ background: none; border-radius: 0; padding: 0 1rem 0 0 }

/* Los rótulos subieron de --tx-3 (.50) a --tx-2 (.62) y de 9.5 a 10.5px: a
   .594rem sobre una pieza oscura eran letra decorativa, no información. Y las
   filas subieron a --tx-1: son el dato que prueba la promesa del renglón, así
   que no pueden ser lo más tenue de la pantalla. */
.pieza__rot{ font-size: .656rem; letter-spacing: .11em; text-transform: uppercase;
             color: var(--tx-2); font-weight: var(--p-ui); margin: 0 }
.pieza__cifra{ font-family: var(--display); font-weight: var(--p-aire);
               letter-spacing: -.01em; line-height: 1; margin: 0;
               font-size: clamp(1.5rem, 3vw, 2.875rem) }
.pieza__nota{ font-size: var(--t6); color: var(--tx-2); margin: 0 }
.pieza__fila{ display: flex; justify-content: space-between; gap: .75rem;
              font-size: .781rem; color: var(--tx-1);
              padding: .47rem 0; border-bottom: 1px solid var(--tx-linea) }
.pieza__fila:last-of-type{ border-bottom: 0 }
.pieza__fila b{ font-weight: var(--p-ui); color: var(--tx-1) }
/* ── EL BRONCE TIENE DOS VERSIONES, Y SE ESTABA USANDO LA EQUIVOCADA ───────
   El token dice de sí mismo, arriba: "el oro, pero legible como TEXTO sobre
   PAPEL (4.84)". #7A5F1F está calculado contra fondo claro. Estas piezas son
   oscuras, y ahí el mismo color cae a ~3.3:1 con texto de 12px — por debajo
   del 4.5 que pide AA. Calculado contra la tinta real (#08090C) y contra la
   pieza más clara (#232833): #C6A961 da 8.8:1 y 6.9:1.
   La portada SÍ es papel, así que ahí vuelve el bronce oscuro. */
:root{ --bronce-claro: #C6A961 }
.pieza__si{ color: var(--bronce-claro); font-weight: var(--p-ui) }
.pieza--portada .pieza__si{ color: var(--bronce) }
.pieza__punto{ width: 5px; height: 5px; border-radius: 50%; background: var(--bronce);
               display: inline-block; vertical-align: middle; margin-right: .375rem }

/* EL ANCLA — el recorte de la portada entregada. Es la única pieza vertical
   (razón ~0.6, como la más alta de stripe) y el único papel dentro de la tinta
   en toda la sección. Es correcto justamente porque el papel ES el entregable:
   no es un hueco claro, es el producto. */
.pieza--portada{
  background: var(--papel); color: #0A1F3D; padding: 0;
  height: clamp(19rem, 30vw, 26.875rem);
  grid-template-rows: auto 1fr; gap: 0;
}
.portada__barra{ display: flex; justify-content: space-between; align-items: center;
                 padding: .7rem .875rem; border-bottom: 1px solid rgba(10,31,61,.10) }
.portada__marca{ font-family: var(--display); font-size: var(--t6) }
.portada__menu{ display: flex; gap: .75rem; color: rgba(10,31,61,.55); font-size: .594rem }
.portada__cuerpo{ padding: 1.25rem .875rem; display: grid; gap: .5rem; align-content: start }
.portada__rot{ font-size: .531rem; letter-spacing: .13em; color: rgba(10,31,61,.45) }
.portada__tit{ font-family: var(--display); font-size: clamp(1.25rem, 2.4vw, 1.688rem);
               line-height: 1.03; font-weight: var(--p-aire); margin: 0 }
.portada__tit b{ font-weight: 600 }
.portada__sub{ font-size: .625rem; color: rgba(10,31,61,.60); line-height: 1.45; margin: 0 }
.portada__cta{ justify-self: start; background: #0A1F3D; color: var(--papel);
               border-radius: 999px; padding: .44rem .94rem; font-size: .594rem }
.portada__foto{ margin-top: .25rem; height: 7.375rem; border-radius: 4px;
                background: linear-gradient(148deg, #D9CFC0, #B9A88F 62%, #8E7C64) }

/* ── EL LIENZO: una superficie de imagen, no una caja ──────────────────────
   La diferencia con `.pieza` no es de estilo, es de naturaleza: aquí la prueba
   ES la captura del producto, no una lista que lo describe. Por eso no lleva
   fondo, ni relleno, ni sombra, ni borde — medido en 510 piezas de referencia:
   cero bordes y cero sombras, sin una sola excepción. Lo único que la enmarca
   es su propio recorte. */
.pieza--lienzo{
  background: none; padding: 0; border-radius: 0; gap: .5rem;
  overflow: visible;                     /* el recorte lo hace la sección */
  /* `figure` trae `margin: 0 40px` del navegador. Sin esto el lienzo medía 1143
     dentro de una celda de 1223 —los 80px del margen— y el sangrado que se
     había calculado se lo comía el margen antes de llegar al borde. En teléfono
     era peor: 270 de ancho en una celda de 350. */
  margin: 0;
}
.pieza--lienzo picture{ display: block }
.pieza--lienzo img{ display: block; width: 100%; height: auto; border-radius: 6px }
.pieza__pie{ font-size: .656rem; letter-spacing: .08em; text-transform: uppercase;
             color: var(--tx-3); font-weight: var(--p-ui); margin: 0 }

/* ── LA CORTINA ────────────────────────────────────────────────────────────
   El gesto grande del renglón. Es el mecanismo de izanami, la referencia que
   dio paco: 17 clip-paths y CERO máscaras en todo su documento. Con esto el
   sitio se queda con un solo idioma para revelar imagen —clip-path— en vez de
   mezclarlo con las máscaras que traía #sistemas.

   El estado cerrado lo pone el JS con `.cortina`, no esta hoja: si el script no
   corre, la imagen se ve. Esconder desde el CSS y depender de JS para mostrar
   es la manera de dejar la sección en blanco cuando algo falla. */
.cortina{ clip-path: inset(0 100% 0 0) }
.cortina.abierta{
  clip-path: inset(0 0 0 0);
  /* Curva de SALIDA, no de entrada. Se midió la primera versión con
     `cubic-bezier(.62,0,.2,1)` —la misma del relevo de #sistemas— y a los 330 ms
     la cortina llevaba abierto el 1.8%: esa curva se queda casi quieta el primer
     tercio y luego corre. Es lo contrario de lo medido en las referencias, donde
     el 100% del cambio de pantalla ocurre en los primeros 150 ms. Con esta, a
     los 150 ms ya va por ~70% y el resto es el asentamiento. */
  transition: clip-path .78s cubic-bezier(.22, 1, .3, 1);
}
@media (prefers-reduced-motion: reduce){ .cortina{ clip-path: none } }

/* Se llama `pauta` y NO `tira`: ya existía un `.tira` en esta misma hoja
   (línea ~859) con `width: clamp(64px,10vw,150px)`. Mi bloque quedaba de 150px
   de ancho y los cinco recuadros a 22 — medido. Una clase genérica agarra otro
   componente en silencio; el síntoma no dice nunca que fue una colisión. */
.pauta{ display: grid; gap: .5rem; align-content: start; min-width: 0 }
.pauta__cab{ display: flex; justify-content: space-between; align-items: baseline; gap: 1rem }
.pauta__nota{ color: var(--tx-3) }
/* `align-items:start` es lo que hace el escalonado: las 9:16 caen más abajo que
   las 1:1 sin que haya que darles una posición a mano. */
.pauta__posts{ display: grid; grid-template-columns: repeat(5, 1fr); gap: .5rem;
              align-items: start }

.post{ margin: 0; display: grid; gap: 0; border-radius: 6px; overflow: hidden;
       background: var(--pz-2) }
.post__marco{
  position: relative; display: grid; align-content: space-between; gap: .5rem;
  padding: .7rem .75rem;
  /* Cada formato trae su propia superficie. NO son cinco recuadros iguales con
     distinto texto: el reel es un degradado en diagonal, el carrusel plano con
     su indicador, la historia una barra arriba, el testimonio centrado. */
  aspect-ratio: 9 / 16;
}
.post--carr .post__marco, .post--test .post__marco{ aspect-ratio: 1 / 1 }
.post--reel .post__marco{ background: linear-gradient(158deg, #262D3A, #14181F 72%) }
.post--carr .post__marco{ background: linear-gradient(180deg, #212734, #171B23) }
.post--hist .post__marco{ background: linear-gradient(200deg, #232A36, #12161D) }
.post--test .post__marco{ background: var(--pz-3) }
.post--test .post__marco{ align-content: center; justify-items: center; text-align: center }

.post__fmt{ font-size: .625rem; letter-spacing: .1em; color: var(--tx-3); justify-self: start }
.post__tit{ font-family: var(--display); font-weight: var(--p-aire);
            font-size: clamp(.9rem, 1.15vw, 1.125rem); line-height: 1.12;
            letter-spacing: -.01em; color: var(--tx-1); margin: 0 }
.post__meta{ font-size: .625rem; letter-spacing: .06em; color: var(--bronce-claro, #C6A961);
             justify-self: start }
.post--test .post__meta{ justify-self: center }
/* La historia lleva su barra de progreso arriba — el único gesto de formato que
   se dibuja, porque es el que identifica el formato de un vistazo. */
.post--hist .post__marco::before{
  content: ''; position: absolute; top: .5rem; left: .75rem; right: .75rem; height: 2px;
  background: linear-gradient(to right, var(--tx-1) 38%, var(--tx-linea) 38%);
  border-radius: 2px;
}
.post--hist .post__fmt{ margin-top: .5rem }
.post__pie{ padding: .55rem .75rem .65rem; display: grid; gap: .1rem }
.post__d{ font-size: .625rem; color: var(--tx-3) }
.post__q{ font-size: var(--t6); color: var(--tx-1); line-height: 1.2 }

@media (max-width: 899px){
  .oficio__frag--contenido > :nth-child(n){ grid-column: 1 / -1; grid-row: auto }
  /* Cinco piezas a 70px cada una no se leen. En teléfono la tira se recorre a
     lo ancho, que además es exactamente el gesto del formato que enseña. */
  /* Los dos rótulos a los lados NO caben en 350px: cada uno se partía en dos
     renglones y quedaban entrelazados. Apilados se leen. */
  .pauta__cab{ display: grid; gap: .1rem }
  .pauta__posts{ display: flex; overflow-x: auto; scroll-snap-type: x mandatory;
                margin-right: calc(var(--borde) * -1); padding-right: var(--borde);
                scrollbar-width: none }
  .pauta__posts::-webkit-scrollbar{ display: none }
  .post{ flex: 0 0 58vw; max-width: 15rem; scroll-snap-align: start }
}

@media (max-width: 899px){
  /* En teléfono la rejilla de 12 se vuelve de 6: las piezas conservan su
     jerarquía relativa (la de span 3 sigue siendo la mitad de la de span 6)
     sin quedar de 60px de ancho. La portada pasa a ancho completo porque
     vertical y estrecha a la vez es ilegible. */
  .oficio__frag{ grid-template-columns: repeat(6, 1fr); margin-right: 0 }
  .pieza--portada{ grid-column: 1 / -1 !important; height: auto }
  .pieza--suelta{ padding-right: 0 }
  /* La composición de dos columnas no cabe: todo se apila y el lienzo va
     primero, entero y sin sangrado —cortado a 390px no quedaría nada legible
     de la tabla. El `auto` de la fila deja que el flujo natural mande. */
  /* `:nth-child(n)` y no `*`: con el universal esta regla pesa (0,1,0) y pierde
     contra las de escritorio, que llevan `:nth-child(1)` y pesan (0,2,0). Se
     midió el resultado — el lienzo quedaba de 17px de ancho en el teléfono,
     porque seguía obedeciendo `grid-column: 5 / -1` sobre una rejilla de 6. */
  .oficio__frag--sistemas > :nth-child(n){ grid-column: 1 / -1; grid-row: auto }
  .oficio__frag--sistemas > :nth-child(1) img{ width: 100% }
}

/* ── LA COMPOSICIÓN EN TELÉFONO: apilar NO es adaptar ──────────────────────
   Reporte de paco: "en servicios se ve demasiado largo y poco estético".
   Medido a 390px y era literal: #servicios medía 5.5 pantallas en teléfono
   contra 3.6 en escritorio, y de las 16 piezas 11 salían a ancho completo.
   Con todo a 350px de ancho la jerarquía desaparece —el dato suelto pesa lo
   mismo que la prueba— y la sección se lee como una lista de tarjetas, que es
   justo el defecto que la rejilla de 12 existe para evitar.

   El arreglo no es encoger: es RE-COMPONER. En la rejilla de 6 la PRUEBA
   (el lienzo, la pauta, la portada) se queda a ancho completo, y los DATOS
   —que son bloques bajos de dos o tres renglones— se aparean de tres columnas.
   Se conserva la relación grande/chico del escritorio y se ahorran renglones.

   Se aparean SOLO los bajos. Las piezas con filas `space-between` a 170px
   parten las palabras, así que las que llevan tabla siguen enteras. */
/* ── EN TELÉFONO, TRES PIEZAS POR RENGLÓN Y NO SEIS ────────────────────────
   Reporte de paco: "se ve mucho texto y agobia mucho".
   No era percepción: es REPETICIÓN literal. `.oficio__bajo` ya lista, en
   viñetas, "Panel de administración · Cobro y facturación · Clientes y
   seguimiento · Reportes que sí lees" — y la rejilla de abajo vuelve a decir
   esos MISMOS rótulos en tarjetas. La pieza "Entrega · 5 sem" duplica el plazo
   que el encabezado del renglón ya trae ("4 – 8 semanas"). En escritorio eso se
   lee como una composición porque las piezas conviven a lo ancho; apiladas en
   390px se leen como la misma frase dicha tres veces.

   Se quedan las TRES primeras de cada renglón, que es lo que no se repite:
     · la PRUEBA  — la portada de papel, la captura del panel, la tira de posts
     · una CIFRA  — $18,420 · $126,400 · $38 por mensaje
     · el DIFERENCIADOR — "tú cambias el precio", "cada cliente ve su proyecto"
   Y se van de la 4ª en adelante, que son los rótulos ya dichos arriba.

   Efecto lateral buscado: la que se iba encima del título del oficio siguiente
   era justamente la ÚLTIMA (`.pieza--hundida`). Al no estar, el choque que paco
   siguió viendo en su foto desaparece por construcción, no por z-index. */
@media (max-width: 899px){
  .oficio__frag > :nth-child(n+4){ display: none }

  /* La rejilla no tenía margen INFERIOR: en escritorio no hace falta porque el
     renglón siguiente trae su propio aire y el ancho separa, pero apilado a
     390px el último dato ("Transferencia · 3 · $6,520") quedaba pegado al
     titular del oficio siguiente y se leía como una sola pieza rota. El
     ensamblado, que aún empuja la pieza un poco hacia abajo, lo agravaba.
     Aquí va el aire, y va abajo de la rejilla y no arriba del titular, para que
     el último oficio también cierre con respiro antes del final de la sección. */
  .oficio__frag{ margin-bottom: clamp(3rem, 9vw, 4.5rem) }

  /* Cinturón que se queda igual: si alguna pieza viaja de más durante el
     ensamblado, el título del oficio siguiente pinta ENCIMA en vez de quedar
     tapado. Las piezas llevan fondo opaco. */
  .oficio__renglon{ position: relative; z-index: 2 }
  .oficio__frag{ position: relative; z-index: 1 }

  /* Con tres piezas, las dos de datos se aparean debajo de la prueba: una
     columna de tres tarjetas iguales vuelve a ser la lista que estorbaba. */
  .oficio__frag > :nth-child(2),
  .oficio__frag > :nth-child(3){ grid-column: span 3 }
  /* salvo cuando la 2ª es la pieza alta con tabla: a 170px parte las palabras */
  .oficio__frag > .pieza--alta{ grid-column: 1 / -1 }
}

@media (max-width: 899px){
  /* En teléfono las partes vuelven a una columna y el bajo se apila: dos
     columnas de lista en 390px dejan las palabras partidas. */
  .oficio__bajo{ grid-template-columns: 1fr }
  .oficio__partes{ grid-template-columns: 1fr }
}

/* Se quitó el acercamiento en Z al pasar el cursor. Con renglones cortos en dos
   columnas leía como profundidad; con un renglón a sangre de borde a borde, que
   la franja entera avance 38px hacia la cara se lee como un salto, no como
   craft. El movimiento de esta sección ya lo lleva el gesto del visor. */

/* OJO: `ch` en el contenedor se mide con la fuente del CONTENEDOR (General
   Sans 15px), no con la del titular. Un `max-width:24ch` aquí valía ~168px y
   partía el título en cinco líneas. En un bloque que mezcla dos familias, la
   medida va en rem. */
.cabecera-seccion--ancha{ max-width: min(92vw, 44rem) }

/* ============================================================
   v3.3 — MARCO: cómo se presenta una interfaz capturada
   ------------------------------------------------------------
   Técnica medida en outrank.so (2026-07-29): los paneles de
   producto NO van planos ni a sangre. Van en un marco con
   hairline y sombra suave, y ROTADOS ±2° ALTERNANDO — divs de
   614×317 con rotate(-2deg) / rotate(2deg) y transition-transform
   para que se enderecen al pasar el cursor. Sin perspectiva ni
   matrix3d: es rotación 2D pura.

   Lo que cambio respecto a ellos:
   · su inclinación es FIJA; aquí se endereza conforme el panel
     entra en la ventana, que es la misma idea pero viva.
   · su marco es blanco de SaaS; aquí es la cáscara concéntrica
     del sistema (radio interno = radio externo − padding), en
     los tokens de papel/tinta. El oro sigue siendo el único calor.
   · NO copio su resolución: sirven 825px estirados a 1440. Con
     fotos eso se ve suave; aquí el sobremuestreo es 2× real.
   ============================================================ */

.marco{
  --r: 14px;
  --inc: -2deg;                       /* la alternancia la pone :nth-child */
  padding: 10px;
  border-radius: var(--r);
  background: color-mix(in oklab, var(--papel) 82%, white);
  border: 1px solid var(--tx-linea);
  /* sombra en DOS capas: una de contacto corta y una de ambiente larga.
     Una sola sombra es lo que hace que las tarjetas se lean a plantilla. */
  box-shadow:
    0 1px 2px rgba(9,10,13,.05),
    0 18px 40px -18px rgba(9,10,13,.22);
  transform: rotate(var(--inc));
  transform-origin: 50% 60%;
  transition: transform .5s var(--curva), box-shadow .5s var(--curva);
  will-change: transform;
}
.marco > *{
  /* radio concéntrico: el interior tiene que ser el externo MENOS el padding,
     si no las curvas no son paralelas y se nota aunque nadie sepa por qué */
  border-radius: calc(var(--r) - 10px);
  overflow: hidden;
  display: block;
}

/* Alternancia. OJO: NO se puede colgar de .marco:nth-child(even) — cada marco
   es el primer hijo de SU propio contenedor, así que todos son :first-child y
   los cuatro salían inclinados hacia el mismo lado. La alternancia tiene que
   leerse del ancestro que sí tiene hermanos. */
.caso:nth-child(even) .marco,
.marco:nth-of-type(even){ --inc: 2deg }

/* enderezado: la clase la pone el JS al entrar en la ventana */
.marco.derecho{ --inc: 0deg }

/* ============================================================
   INTEGRACIÓN DE CAPTURAS — la "magia" SIN filtro
   ------------------------------------------------------------
   El problema: cada cliente tiene su propio tema y sus propios
   colores; pegados sobre esta página se leen a recorte. La salida
   fácil es un filtro —desaturar, duotono, velo de color— y es
   justo lo que hace que un portafolio se vea a plantilla, porque
   deja de enseñar el trabajo y empieza a enseñar el filtro.

   La técnica real es la inversa: NO se toca la imagen, se hace que
   LA PÁGINA REACCIONE A ELLA. Tres capas, todas alimentadas por un
   solo dato — `--derrame`, el color dominante medido de cada
   captura (Black Secret ámbar 30°, Chilita rojo 355° a 77%):

     1 · DERRAME — la pieza emite su propia luz sobre el fondo.
         Es lo que hace que "deba ir ahí": la página se tiñe de la
         captura, no al revés.
     2 · SOMBRA TEÑIDA — la sombra larga lleva el color de la pieza
         en vez de gris neutro. Un objeto real tiñe su sombra.
     3 · ARISTA QUE ATRAPA LA LUZ — 1px claro en el canto superior.
         Sin esto el marco es un rectángulo; con esto es un objeto
         con espesor bajo una fuente de luz.
   ============================================================ */
.marco{
  --derrame: rgba(160,160,170,.55);        /* neutro si nadie lo declara */
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.5),                                   /* 3 */
    0 1px 2px rgba(9,10,13,.05),
    0 18px 40px -18px rgba(9,10,13,.22),
    0 46px 90px -40px color-mix(in oklab, var(--derrame) var(--tinte-sombra,34%), transparent); /* 2 */
}
.marco::before{                                                            /* 1 */
  content:''; position:absolute; inset:-7% -5%; z-index:-1;
  pointer-events:none; border-radius:50%;
  /* Sobre PAPEL el derrame va bajísimo. Un rojo al 52% sobre un fondo claro
     inunda la sección y se convierte justo en el filtro que había que evitar;
     sobre tinta el mismo valor apenas se nota. La regla no es un número: es
     que el derrame se SIENTA y no se VEA. Aquí se siente a 16%. */
  background:radial-gradient(closest-side,
              color-mix(in oklab, var(--derrame) 16%, transparent), transparent 70%);
  filter:blur(66px);
  opacity:0; transition:opacity .9s var(--curva);
}
/* el derrame se enciende cuando la pieza entra de verdad — la misma clase
   que ya endereza el marco, así luz y gesto ocurren en el mismo momento */
.marco.derecho::before{ opacity:1 }
/* sobre tinta sí puede crecer: el negro se come la luz y ahí es donde el
   derrame hace el trabajo de integrar */
.oscuro .marco::before{
  inset:-13% -9%; filter:blur(54px);
  background:radial-gradient(closest-side,
              color-mix(in oklab, var(--derrame) 44%, transparent), transparent 72%);
}
/* La sombra teñida también se modera sobre papel */
.marco{ --tinte-sombra:34% }
.oscuro .marco{ --tinte-sombra:62% }

/* Un vídeo dentro del chasis se comporta igual que una captura */
.chasis video{ display:block; width:100%; height:auto }
.chasis--telefono video{ border-radius:22px }

@media (hover: hover){
  .marco:hover{
    --inc: 0deg;
    box-shadow: 0 2px 4px rgba(9,10,13,.06), 0 30px 60px -20px rgba(9,10,13,.28);
  }
}

/* sobre fondo oscuro el marco se invierte: cáscara de superficie, no de papel */
.oscuro .marco{
  background: var(--sup-2);
  border-color: var(--tx-linea);
  box-shadow: 0 1px 2px rgba(0,0,0,.4), 0 24px 50px -20px rgba(0,0,0,.6);
}

/* nadie inclina en móvil: a una columna los bordes rotados chocan entre sí */
@media (max-width: 760px){
  .marco{ --inc: 0deg !important; padding: 7px; --r: 11px }
}

@media (prefers-reduced-motion: reduce){
  .marco{ --inc: 0deg !important; transition: none }
}

/* ============================================================
   MARCO DESNUDO — el estuche, medido
   ------------------------------------------------------------
   Scout del 2026-07-30, 73 imágenes de 5 estudios en vivo (Exo Ape,
   OBYS, Hello Monday, Phantom, Basic/Dept): **0 con sombra, 0 con
   borde, 0 anidadas** en un contenedor con relleno + fondo + radio,
   y radio mediano **0**. Nuestro marco tenía las cuatro cosas a la
   vez, y encima un `.chasis` dentro = doble bisel. Ésa —y no la
   captura— es la parte que se lee "hecha por IA".

   Contradice al skill `high-end-visual-design`, que ordena doble
   bisel. Manda lo medido; es la tercera vez que la misma medición
   da lo mismo en este sitio.

   Lo que SÍ se conserva: el DERRAME (`.marco::before`). Eso es luz,
   no caja — es lo que integra la captura con la página sin recurrir
   a un filtro, que es la salida barata. El `.chasis` también se
   queda: la barra del navegador no decora, dice "esto es un sitio".
   ============================================================ */
.marco--desnudo,
.oscuro .marco.marco--desnudo{
  --inc: 0deg;
  padding: 0;
  background: none;
  border: 0;
  border-radius: 0;
  box-shadow: none;
}
.marco--desnudo > *{ border-radius: 0 }
@media (hover: hover){ .marco--desnudo:hover{ box-shadow: none } }
/* el chasis de teléfono conserva su cáscara: un teléfono SIN marco deja de
   leerse como teléfono, y ahí el borde no es adorno sino el objeto */
.marco--desnudo .chasis--telefono{ border-radius: 30px }

/* ============================================================
   NINGÚN CASO MIDE LO QUE EL ANTERIOR (ley Exo Ape)
   ------------------------------------------------------------
   Estaban los cuatro en la misma rejilla: tres de cuatro ocupaban
   exactamente el 60% del ancho. En Exo Ape ninguna pieza mide lo
   que la de arriba —cambian de escala Y de sangría—, y eso es lo
   que se lee compuesto en vez de listado. No es motion: es medida.
   ============================================================ */
@media (min-width: 900px){
  .caso{ grid-template-columns: var(--m, 1.35fr) var(--t, .65fr) }
  .caso--invertido{ grid-template-columns: var(--t, .65fr) var(--m, 1.35fr) }

  .casos > .caso:nth-child(1){ --m: 1.9fr;  --t: .75fr }   /* el más grande: lleva el barrido */
  .casos > .caso:nth-child(2){ --m: .58fr;  --t: 1.55fr }  /* un teléfono debe leerse pequeño */
  .casos > .caso:nth-child(3){ --m: 1.15fr; --t: .9fr }
  .casos > .caso:nth-child(4){ --m: 1.55fr; --t: .8fr }

  /* y ninguno arranca en el mismo renglón que el de arriba */
  .casos > .caso:nth-child(2){ margin-left: 8% }
  .casos > .caso:nth-child(2) .caso__media{ margin-top: 3.5rem }
  .casos > .caso:nth-child(3){ margin-right: 6% }
  .casos > .caso:nth-child(4) .caso__media{ margin-top: 2rem }
}

/* ============================================================
   v3.4 — ICONOGRAFÍA derivada de la C
   ------------------------------------------------------------
   La gramática sale del c-mark real, no de un gusto: relleno
   plano sin contorno, construcción sobre retícula, DOS esquinas
   opuestas abiertas, un módulo cuadrado suelto en cada hueco,
   grosor de barra constante y remates a escuadra.

   No se cortan en 14 archivos: las dos rejillas son SVG de
   2048×2048 con 3×3 celdas de 682.67px, y cada icono se
   direcciona por posición. Dos peticiones de red en vez de
   catorce.

   Van con `mask-image`, no con `background-image`: así el icono
   toma currentColor y el MISMO archivo sirve sobre papel y sobre
   oscuro. Con background habría que producir una versión blanca.
   ============================================================ */

.ico{
  --hoja: url('./iconos/rejilla-b.svg');
  --cx: 0; --cy: 0;                       /* columna y fila, 0-2 */
  display: inline-block;
  width: 1em; height: 1em;
  background: currentColor;
  -webkit-mask-image: var(--hoja);        mask-image: var(--hoja);
  -webkit-mask-size: 300% 300%;           mask-size: 300% 300%;
  -webkit-mask-repeat: no-repeat;         mask-repeat: no-repeat;
  -webkit-mask-position: calc(var(--cx) * 50%) calc(var(--cy) * 50%);
          mask-position: calc(var(--cx) * 50%) calc(var(--cy) * 50%);
  flex: none;
}
.ico--a{ --hoja: url('./iconos/rejilla-a.svg') }

/* Hoja B — lo que se entrega */
.ico-tienda    { --cx:0; --cy:0 }
.ico-cobro     { --cx:1; --cy:0 }
.ico-correo    { --cx:2; --cy:0 }
.ico-panel     { --cx:0; --cy:1 }
.ico-documento { --cx:1; --cy:1 }
.ico-grafica   { --cx:2; --cy:1 }
.ico-calendario{ --cx:0; --cy:2 }
.ico-bolsa     { --cx:1; --cy:2 }
.ico-engrane   { --cx:2; --cy:2 }

/* Hoja A — el proceso y la gente */
.ico-persona   { --cx:0; --cy:0 }
.ico-megafono  { --cx:1; --cy:0 }
.ico-palomita  { --cx:0; --cy:1 }
.ico-archivero { --cx:2; --cy:1 }
.ico-reproducir{ --cx:0; --cy:2 }
.ico-reloj     { --cx:1; --cy:2 }
/* las celdas 2,0 · 1,1 · 2,2 de la hoja A quedaron fuera del sistema
   (la llave se lee a código de barras, las cajas a signo de admiración
   y el acuerdo a bloques sueltos). No se usan. */

/* ============================================================
   v3.5 — CHASIS: el error de mis primeras capturas
   ------------------------------------------------------------
   Lo que hacía mal: pegaba una foto de pantalla del navegador
   —con la burbuja de chat, el popup y la barra de anuncio del
   sitio incluidas— dentro de una caja. Eso se lee a captura de
   soporte técnico, no a portafolio.

   Lo que hacen las marcas: la captura va DESNUDA (recortada a
   ras del contenido, sin el cromo del navegador real) y el
   cromo se DIBUJA alrededor, limpio y consistente. Así las
   cuatro fichas tienen el mismo marco aunque las capturas
   vengan de sitios distintos.

   Va en CSS y no en SVG a propósito: el cromo hereda los tokens
   (sirve igual sobre papel y sobre oscuro), queda nítido a
   cualquier densidad y no cuesta una petición más.

   Anidado: .marco (cáscara + sombra + rotación) > .chasis > img
   ============================================================ */

.chasis{
  --cromo: color-mix(in oklab, var(--papel-alto) 88%, var(--tx-1) 12%);
  --raya:  var(--tx-linea);
  position: relative;
  background: var(--cromo);
  overflow: hidden;
  display: block;
}
.oscuro .chasis{
  --cromo: var(--sup-3);
  --raya: var(--tx-linea);
}
.chasis img, .chasis video{
  display: block; width: 100%; height: auto;
}

/* ── 1 · ESCRITORIO ───────────────────────────────────────────
   Barra de 30px con tres puntos y una píldora de dirección. Los
   puntos NO son de colores: en monocromo el semáforo de macOS es
   lo que grita "captura de pantalla". Van al 22% de la tinta. */
.chasis--escritorio{ padding-top: 30px }
.chasis--escritorio::before{
  content:''; position:absolute; inset:0 0 auto; height:30px;
  border-bottom:1px solid var(--raya);
  background:
    /* tres puntos */
    radial-gradient(circle 3px at 14px 15px, currentColor 99%, transparent 100%),
    radial-gradient(circle 3px at 27px 15px, currentColor 99%, transparent 100%),
    radial-gradient(circle 3px at 40px 15px, currentColor 99%, transparent 100%);
  opacity:.22;
}
/* la píldora de dirección va aparte para poder darle su propio tono */
.chasis--escritorio::after{
  content:''; position:absolute; top:8px; left:58px; right:14px; height:14px;
  border-radius:99px; background:currentColor; opacity:.07;
}

/* ── 2 · TELÉFONO ─────────────────────────────────────────────
   Marco parejo de 8px y la isla arriba. Nada de botones de
   volumen ni de reflejo de cristal: eso es maqueta de dribbble. */
.chasis--telefono{
  padding: 8px; border-radius: 30px;
  background: var(--cromo); border: 1px solid var(--raya);
}
.chasis--telefono > *{ border-radius: 23px; overflow:hidden; display:block }
.chasis--telefono::after{
  content:''; position:absolute; top:15px; left:50%; transform:translateX(-50%);
  width:64px; height:8px; border-radius:99px;
  background:currentColor; opacity:.30; z-index:2;
}

/* ── 3 · VENTANA DE PANEL ─────────────────────────────────────
   Para el software a la medida: riel lateral + barra corta. Es
   el chasis que dice "esto es una herramienta", no "esto es un
   sitio". */
.chasis--panel{ padding: 26px 0 0 44px }
.chasis--panel::before{
  content:''; position:absolute; inset:0 0 auto; height:26px;
  border-bottom:1px solid var(--raya);
  background: radial-gradient(circle 2.5px at 13px 13px, currentColor 99%, transparent 100%);
  opacity:.22;
}
.chasis--panel::after{
  content:''; position:absolute; top:26px; bottom:0; left:0; width:44px;
  border-right:1px solid var(--raya);
  /* cinco renglones de menú, insinuados */
  background:
    linear-gradient(currentColor,currentColor) 12px 18px / 20px 3px no-repeat,
    linear-gradient(currentColor,currentColor) 12px 34px / 20px 3px no-repeat,
    linear-gradient(currentColor,currentColor) 12px 50px / 20px 3px no-repeat,
    linear-gradient(currentColor,currentColor) 12px 66px / 20px 3px no-repeat,
    linear-gradient(currentColor,currentColor) 12px 82px / 20px 3px no-repeat;
  background-color: color-mix(in oklab, var(--cromo) 70%, var(--tx-1) 6%);
  opacity:1;
}
.chasis--panel::after{ }

/* en móvil el chasis de escritorio se encoge: una barra de 30px
   sobre una captura de 300px de ancho ocupa el 10% del alto */
@media (max-width: 620px){
  .chasis--escritorio{ padding-top: 22px }
  .chasis--escritorio::before{ height:22px;
    background:
      radial-gradient(circle 2.2px at 11px 11px, currentColor 99%, transparent 100%),
      radial-gradient(circle 2.2px at 21px 11px, currentColor 99%, transparent 100%),
      radial-gradient(circle 2.2px at 31px 11px, currentColor 99%, transparent 100%) }
  .chasis--escritorio::after{ top:6px; left:44px; height:10px }
  .chasis--panel{ padding-left: 34px }
  .chasis--panel::after{ width:34px }
}

/* El caso vertical no puede ocupar el mismo ancho que uno de escritorio:
   un teléfono a 16:9 de ancho se vuelve una torre de 1,700px de alto. */
.marco--vertical{ width:min(300px,62vw); margin-inline:auto }
@media (min-width:900px){ .marco--vertical{ margin-inline:0 } }

/* (La captura del portal ya no se recorta con object-fit: entra completa y la
   lente la recorre — ver §10.) */

/* ============================================================
   v3.5 — LA PUERTA · el calibre que cierra
   ------------------------------------------------------------
   Por qué existe. Medido el arranque del sitio cada 250 ms: el
   hero llegaba a su estado final a los 0.25 s y de ahí la
   diferencia entre cuadros caía a 0.3 y luego a 0.0. No es que la
   entrada fuera sutil: NO HABÍA. La página simplemente ya estaba.
   De las seis referencias medidas, cuatro sí cierran la puerta
   antes de dejar entrar (exoape 1.75 s · lusion 3.0 · obys 5.0 ·
   basic con su logotipo). Las dos que no lo hacen —igloo y
   14islands— son también las dos que menos se sienten producidas.

   Por qué ESTE gesto y no un contador cualquiera. La marca cae
   exacta en una retícula de 5x5 con módulo 140, y se parte sola en
   DOS piezas: la de arriba-izquierda y la de abajo-derecha, con el
   cuadrado suelto en medio. O sea que el logo YA ERA un calibre:
   dos mordazas y lo que miden. Aquí entran separadas por el eje de
   45° —el mismo ángulo de sus chaflanes, que van por delante como
   filo—, se cierran, y el cuadrado del centro es la apertura por
   la que se ve el sitio.

   Presupuesto 1.6 s, no 5. Obys puede cobrar cinco segundos; un
   estudio que le vende velocidad a negocios mexicanos no, y en 4G
   una puerta larga se lee como sitio roto.

   ⚠️ SEGURIDAD: una puerta que no abre es una pantalla negra y un
   visitante perdido. Por eso hay TRES salidas independientes:
   (1) el JS abre cuando la carga real terminó; (2) si el JS nunca
   corre, el rescate en CSS de aquí abajo la borra a los 2.9 s;
   (3) si `@property` no existe, `--a` salta a su valor final y la
   apertura ocurre de golpe — sin animar, pero ocurre.
   ============================================================ */

/* La apertura se mide en FRACCIÓN de media pantalla, no en píxeles. Con una
   sola longitud contra media pantalla el hueco crecía a la misma velocidad en
   los dos ejes, y como el alto es menor que el ancho terminaba de abrir arriba
   y abajo mucho antes que de lado: se leía como una persiana, no como una
   apertura. Con una fracción, los cuatro bordes llegan juntos. */
/* Arranca en NEGATIVO: las cuatro hojas se traslapan un pelo en el centro en
   vez de dejar un hueco. Con 0.02 quedaba una ventanita de ~18 px por la que
   se veía la página, y como el contador empujaba la marca hacia arriba, ese
   hueco asomaba DEBAJO del logo como una mancha gris. La apertura tiene que
   nacer detrás del cuadrado de la marca, no al lado. */
@property --k{ syntax:'<number>'; inherits:true; initial-value:-0.006 }

.puerta{
  position:fixed; inset:0; z-index:200;
  display:grid; place-items:center;
  color:var(--papel);
  transition:--k .58s var(--curva);
  animation:puerta-rescate .4s 2.4s forwards;   /* salida (2) */
}
.puerta--abre{ --k:1.06 }
.puerta--fuera{ opacity:0; visibility:hidden; pointer-events:none;
                transition:opacity .3s ease, visibility 0s .3s }

/* Las cuatro hojas son cada una la ventana COMPLETA, corridas media
   pantalla más la apertura. Así el hueco del centro es exactamente
   2·--a y todo el movimiento es `transform` — cero reflujo. */
.puerta__hoja{ position:absolute; inset:0; background:var(--tinta) }
.puerta__hoja--n{ transform:translateY(calc(-50% - var(--k) * 50%)) }
.puerta__hoja--s{ transform:translateY(calc( 50% + var(--k) * 50%)) }
.puerta__hoja--o{ transform:translateX(calc(-50% - var(--k) * 50%)) }
.puerta__hoja--e{ transform:translateX(calc( 50% + var(--k) * 50%)) }

/* El contador va ABSOLUTO: si estuviera en el flujo, su altura correría la
   marca hacia arriba y la marca dejaría de estar en el centro de la ventana,
   que es justo donde nace la apertura. */
.puerta__centro{ position:relative; z-index:2; display:grid; place-items:center }
.puerta__marca{
  /* Medido en las referencias: la marca de exoape ocupa ~2.8% del ancho
     de la ventana durante su precarga. A 82px iba en 5.7% y se leía como
     pantalla de logo, no como un detalle. 64px la deja en 4.4%. */
  width:clamp(44px, 4.4vw, 64px); height:auto; display:block;
  fill:currentColor; overflow:visible;      /* las mordazas empiezan FUERA de su caja */
}
.puerta__sup, .puerta__inf, .puerta__nucleo{ transform-box:view-box; transform-origin:center }
.puerta__sup{ animation:calibre-sup .72s .06s var(--curva) both }
.puerta__inf{ animation:calibre-inf .72s .06s var(--curva) both }
.puerta__nucleo{ animation:calibre-nucleo .3s .74s var(--curva) both }
.puerta__medida{
  position:absolute; top:calc(100% + 1.15rem); left:50%; transform:translateX(-50%);
  margin:0; white-space:nowrap;
  /* NO `--tx-3`: ese token se calcula sobre la tinta y aquí el fondo ES la
     tinta — el número salía en rgba(9,10,13,.5) sobre #08090C, o sea invisible.
     No se vio en un solo cuadro de la grabación. Se mezcla contra el papel
     que la puerta ya tiene como color propio. */
  font-size:var(--t6); letter-spacing:.22em;
  color:color-mix(in oklab, currentColor 62%, transparent);
  animation:calibre-medida .4s .18s var(--curva) both;
}

@keyframes calibre-sup{ from{ transform:translate(-300px,-300px); opacity:0 } to{ transform:none; opacity:1 } }
@keyframes calibre-inf{ from{ transform:translate( 300px, 300px); opacity:0 } to{ transform:none; opacity:1 } }
/* nunca desde scale(0): nada en el mundo real aparece de la nada */
@keyframes calibre-nucleo{ from{ transform:scale(.74); opacity:0 } to{ transform:none; opacity:1 } }
@keyframes calibre-medida{ from{ opacity:0 } to{ opacity:1 } }
@keyframes puerta-rescate{ to{ opacity:0; visibility:hidden; pointer-events:none } }

/* El titular del hero no puede jugar detrás de la puerta: si se parte y sube
   mientras el calibre está cerrado, cuando la apertura abre el hero YA PASÓ.
   Se esconde solo si la puerta de verdad va a correr —la clase la pone el JS—,
   así que si el JS nunca arranca el hero se ve, como siempre. */
html.puerta-activa .hero [data-rev] > *{ opacity:.001; transform:translate3d(0,26px,0) }

/* La marca del encabezado nace apagada SOLO con la puerta activa: es la que
   recibe el vuelo del calibre al final. Sin puerta, se ve desde el principio. */
.cab__marca{ display:flex; align-items:center; gap:.5rem }
.cab__marca svg{ width:.95em; height:.95em; fill:currentColor; flex:none }
html.puerta-activa .cab__marca svg{ opacity:0 }

/* Segunda visita en la misma sesión: se entra directo. Y con movimiento
   reducido no hay puerta en absoluto. */
html.visto .puerta{ display:none }
@media (prefers-reduced-motion:reduce){
  .puerta{ display:none }
  html.puerta-activa .hero [data-rev] > *{ opacity:1; transform:none }
  html.puerta-activa .cab__marca svg{ opacity:1 }
}
.puerta--abre .puerta__medida{ opacity:0; transition:opacity .22s ease }

/* La marca firmada en el pie. El símbolo va al tamaño del texto que acompaña
   —`1em` sobre un `d3`— y alineado por su caja óptica, no por la línea base:
   una C cuadrada sentada en la línea base se ve caída junto a una mayúscula. */
.pie__marca{ display:flex; align-items:center; gap:.62em; margin:0 }
.pie__simbolo{ width:.92em; height:.92em; fill:currentColor; flex:none; translate:0 -.02em }

/* ============================================================
   v3.6 — EL ECLIPSE en el hero
   ------------------------------------------------------------
   Va DEBAJO del texto y ENCIMA del lienzo del shader. El orden
   importa: la niebla del shader pasa por arriba del cuerpo y lo
   mete dentro de la escena — sin eso se lee como estampa pegada,
   que es la misma trampa del PNG sobrepuesto ya anotada.
   Por eso el grano y la viñeta del sitio también van encima del
   video, no debajo: un plano limpio sobre un fondo con grano se
   delata al instante.
   ============================================================ */
.eclipse{ position:absolute; inset:0; z-index:1; overflow:clip; pointer-events:none }
.eclipse__foto{
  position:absolute; inset:0; width:100%; height:100%;
  object-fit:cover; object-position:58% 46%; display:block;
}
/* la izquierda se apaga para que el titular no pelee con el filo */
.eclipse__cortina{
  position:absolute; inset:0;
  background:linear-gradient(100deg, #08090C 20%, rgba(8,9,12,.86) 38%, rgba(8,9,12,0) 62%);
}
.eclipse__polvo{
  position:absolute; inset:0; mix-blend-mode:overlay; opacity:.4;
  background-image:url('../media/materia/velada.webp'); background-size:cover;
}
.eclipse__vineta{
  position:absolute; inset:0;
  background:radial-gradient(120% 92% at 62% 46%, rgba(0,0,0,0) 32%, rgba(0,0,0,.7) 100%);
}
.hero__cuerpo{ position:relative; z-index:3 }

/* En teléfono NO es el mismo encuadre encogido: es su propia toma, generada en
   vertical. Encogiendo la horizontal, el filo cruzaba por detrás del titular.
   El wow se RE-COMPONE, no se reduce. */
@media (max-width:760px){
  .eclipse__foto{ object-position:50% 0% }
  .eclipse__cortina{ background:linear-gradient(185deg, rgba(8,9,12,0) 22%, rgba(8,9,12,.55) 46%, #08090C 66%) }
  .eclipse__vineta{ background:radial-gradient(110% 60% at 62% 18%, rgba(0,0,0,0) 30%, rgba(0,0,0,.6) 100%) }
}

/* Las dos rutas de contacto del pie */
.pie__rutas{ display:flex; align-items:center; gap:1.5rem; flex-wrap:wrap; margin:0 }
.pie__correo{ font-size:var(--t5); color:var(--tx-2); border-bottom:1px solid currentColor;
              padding-bottom:.1em; transition:color var(--base) var(--curva) }
.pie__correo:hover{ color:var(--tx-1) }

/* ============================================================
   v3.7 — PISO DE LECTURA EN TELÉFONO
   ------------------------------------------------------------
   Reporte de paco: "en móvil se ve demasiado texto y eso cansa",
   y autorizó que la página se ALARGUE si a cambio se digiere mejor.

   Medido en vivo a 390px antes de tocar nada, y el número desmiente
   la primera hipótesis —la mía incluida—: el sitio tiene 941 palabras
   repartidas en 22.2 pantallas, o sea 42 por pantalla. Eso NO es mucho
   texto. El problema es otro y sale en la tabla de tamaños:

     10px  →  98 palabras
     12px  → 286 palabras
     14px  →  77
     15px  → 242
     18px  → 124

   **384 palabras, el 41% del texto del sitio, viven entre 10 y 12px**
   en un teléfono que se lee a 40 cm. Eso es letra de contrato. Lo que
   cansa no es la cantidad: es que casi la mitad haya que descifrarla.

   Y la carga está concentrada, no repartida:
     #servicios  76 palabras por pantalla
     #trabajo    55
     #entrada    36
     #portal     24
     #sistemas   13   ← la que se siente premium y aireada
     #cierre     11

   Subir el tamaño resuelve las dos cosas a la vez: se lee sin esfuerzo
   y, como el bloque ocupa más alto, la densidad por pantalla BAJA sola.
   La página se alarga; es exactamente el intercambio que paco aceptó.

   ⚠️ LO QUE NO SE TOCA: el texto dentro de las maquetas —`.portada__*`
   de la portada de papel y `.post__*` de la tira de publicaciones—. Ahí
   la letra chica ES el diseño de la pantalla que se está simulando; al
   agrandarla deja de leerse como una captura y pasa a leerse como
   nuestro propio texto mal maquetado. Por eso esta regla enumera clases
   y no usa un selector universal.
   ============================================================ */
@media (max-width: 899px){
  /* 1 · TEXTO DE LECTURA — nada por debajo de 15px */
  .pieza__nota,
  .pieza__fila, .pieza__fila b, .pieza__fila span, .pieza__si,
  .pie-foto, .caso__cifra, .entrada__nota,
  .meta-lista dt, .meta-lista dd,
  .lista-fina li, .oficio__partes li{
    font-size: var(--t5);
    line-height: 1.5;
  }

  /* 2 · RÓTULOS EN CAJA ALTA — se leen en ráfaga, no en línea, así que
     pueden ir un punto abajo. Pero 10px con tracking es un jeroglífico:
     el piso son 13. Y baja el tracking, porque a mayor tamaño la misma
     separación empieza a desarmar la palabra. */
  .pieza__rot, .pieza__pie, .rotulo, .oficio__plazo .mono{
    font-size: var(--t6);
    letter-spacing: .07em;
    line-height: 1.35;
  }

  /* 3 · AIRE. Subir el cuerpo sin subir el interlineado deja el párrafo
     más apretado que antes, no más legible. */
  .caso__texto, .oficio__dicho{ line-height: 1.65 }
  .pieza{ gap: .75rem; padding: 1.15rem 1.25rem }
  .meta-lista{ gap: .75rem }
  .oficio__partes{ gap: .75rem }
}

/* ============================================================
   v3.8 — EN TELÉFONO MANDA LA IMAGEN
   ------------------------------------------------------------
   Reporte de paco: "el texto opaca a las imágenes, parece una
   biblia de texto con números y no se le da peso a las imágenes".

   Medido: superficie de imagen dividida entre superficie de texto,
   por sección, móvil contra escritorio.

     sección      móvil   escritorio
     #trabajo      2.71      7.50     ← se desploma casi 3×
     #servicios    0.15      0.30     ← texto casi puro
     #portal       6.84      5.84
     #sistemas     0         0

   O sea que no es impresión: en el teléfono la prueba visual pierde
   dos tercios de su peso relativo justo en la sección que existe para
   enseñar trabajo. Y la causa es geométrica, no de cantidad de texto:
   la captura mide 350px dentro de una ventana de 390 —no llega a los
   bordes— y su ventana es 16/10, o sea 350×219. Una imagen que deja
   márgenes a los lados se lee como TARJETA; una que toca los dos
   bordes se lee como PLANO. Es la misma imagen y pesa distinto.

   Tres movimientos, sólo aquí:
     · sangrado completo — la captura toca los dos bordes
     · la ventana pasa de 16/10 a 1/1 — casi el doble de área
     · fuera el estuche (relleno, radio, inclinación): a sangre el
       marco es contradictorio, no hay borde que enmarcar

   NO se toca `.marco--vertical` (el teléfono de Chilita): a sangre
   dejaría de leerse como un aparato. Ése sólo crece.
   ============================================================ */
@media (max-width: 899px){
  #trabajo .caso__media .marco:not(.marco--vertical){
    margin-inline: calc(var(--borde) * -1);
    padding: 0;
    --r: 0px;
    --inc: 0deg;
    border-radius: 0;
    box-shadow: none;
  }
  #trabajo .caso__media .marco:not(.marco--vertical) .recorre__ventana{
    border-radius: 0;
    /* 350×219 → 390×390: el área pasa de 76k a 152k px². El recorrido
       vertical de la captura sigue funcionando igual —lo mueve `--rec`
       contra `100cqh`, que se recalcula solo. */
    /* 4/5 y no 1/1: la captura de un sitio se recorre a lo ALTO, así que
       cada píxel de alto es recorrido visible. 350×219 → 390×487, o sea el
       área pasa de 76k a 190k px² — dos veces y media. */
    aspect-ratio: 4 / 5;
  }
  /* el pie vuelve al margen del texto: es texto, no imagen */
  #trabajo .pie-foto{ margin-inline: var(--borde) }

  /* el teléfono de Chilita no va a sangre —dejaría de ser un aparato—
     pero sí crece: 300px de tope era medio ancho de pantalla */
  .marco--vertical{ width: min(340px, 86vw) }

  /* #servicios, ratio 0.15. Aquí hay DOS pruebas visuales y sólo una es
     una imagen de verdad:
       · `.pieza--portada` es una maqueta hecha con CSS —por eso no aparecía
         en la medición de superficie de imagen, que sólo cuenta img/video—
         pero ocupa pantalla igual y se lee como prueba.
       · `.pieza--lienzo` sí es una captura real del panel, y medía 310px.
     Las dos toman el ancho entero. */
  .pieza--portada,
  .oficio__frag .pieza--lienzo{
    margin-inline: calc(var(--borde) * -1);
    border-radius: 0;
  }
  .oficio__frag .pieza--lienzo img{ border-radius: 0 }
  /* el pie del lienzo es texto: vuelve al margen */
  .oficio__frag .pieza--lienzo .pieza__pie{ margin-inline: var(--borde) }
}

/* Consecuencia del piso de lectura (§v3.7): a 15px las filas de datos ya no
   caben en una línea dentro de una pieza de ~310px y partían la palabra
   ("Pieza mediana · 22 / cm", "$740 → / $690"). `wrap` deja que el valor baje a
   su propio renglón SÓLO cuando no cabe; las cortas siguen en una línea con el
   valor a la derecha, que es lo que hace que se lean como tabla. */
@media (max-width: 899px){
  .pieza__fila{ flex-wrap: wrap; row-gap: .1rem }
  /* Se probó darle a la etiqueta una base del 58% para que bajara entera en vez
     de partirse: MEDIDO, empeoró de 2 filas partidas a 6 — al fijarle base se
     vuelve más angosta de lo que el contenido pide. `wrap` solo es mejor. */
}

/* ============================================================
   v3.9 — PATRONES DE TELÉFONO: ACORDEÓN Y PUERTAS SUELTAS
   ------------------------------------------------------------
   Propuesta de paco: en móvil no apilar todo hacia abajo, usar
   acordeones y carruseles donde den versatilidad, con sensación
   de app y mejor conversión.

   DÓNDE SÍ — los tres oficios de #servicios. Son hermanos
   equivalentes y mutuamente excluyentes (quien llega quiere UNO)
   y suman 5.1 pantallas de teléfono. El acordeón cambia "bajar
   cinco pantallas" por "elegir uno": scroll → decisión.

   DÓNDE NO — los cuatro casos de #trabajo. Son la prueba de que
   sabemos trabajar; en un carrusel la mayoría no pasa del primer
   elemento y esconderíamos tres de cuatro. Ahí la longitud no es
   un defecto, es el argumento. Se quedan verticales.
   ============================================================ */
@media (max-width: 899px){
  /* El renglón es una rejilla de tres columnas; con el botón dentro, el botón
     era UNA celda y el signo no llegaba al extremo. En teléfono el renglón pasa
     a bloque y el botón hereda la rejilla: así el `auto` del signo empuja
     contra el borde derecho de verdad. */
  .oficio__renglon{ display: block }
  .oficio__disparo{
    all: unset; box-sizing: border-box;
    display: grid !important;
    grid-template-columns: auto minmax(0,1fr) auto auto;
    align-items: baseline; gap: clamp(.75rem, 2.5vw, 1.5rem);
    width: 100%;
    min-height: 44px;                    /* objetivo táctil */
    padding-block: .35rem;
    cursor: pointer; -webkit-tap-highlight-color: transparent;
  }
  .oficio__disparo:focus-visible{ outline: 2px solid var(--bronce-claro); outline-offset: 6px }
  /* el signo empuja a la derecha y gira: el único indicador que hace falta */
  .oficio__signo{
    margin-left: auto; flex: none; position: relative;
    width: 1.1rem; height: 1.1rem; align-self: center;
    transition: transform .42s var(--curva);
  }
  .oficio__signo::before,
  .oficio__signo::after{
    content: ''; position: absolute; inset: 50% 0 auto 0;
    height: 1px; background: currentColor; opacity: .55;
  }
  .oficio__signo::after{ transition: transform .42s var(--curva); transform: rotate(90deg) }
  .oficio.abierto .oficio__signo::after{ transform: rotate(0deg) }

  /* El alto se anima con la rejilla 0fr→1fr: no hace falta medir nada en JS y
     funciona con contenido de alto desconocido. */
  .oficio__panel{
    display: grid; grid-template-rows: 0fr;
    transition: grid-template-rows .42s var(--curva);
  }
  .oficio.abierto .oficio__panel{ grid-template-rows: 1fr }
  .oficio__panel-int{ min-height: 0 }
  /* ⚠️ El recorte SÓLO mientras dura la transición. Permanente recortaría las
     capturas que salen a sangre con margen negativo (§v3.8) — justo lo que se
     acaba de conseguir. */
  .oficio.animando .oficio__panel-int{ overflow: hidden }
  .oficio:not(.abierto) .oficio__panel-int{ overflow: hidden }

  @media (prefers-reduced-motion: reduce){
    .oficio__panel, .oficio__signo, .oficio__signo::after{ transition: none }
  }

}

/* ============================================================
   v3.10 — MENOS CIFRAS Y MÁS PRODUCTO (teléfono)
   ------------------------------------------------------------
   Reporte de paco, sobre su foto de #servicios: "alguien que no
   mueve mucho la tecnología se puede ver afectado por tanto
   número". Y tenía delante la prueba: "$126,400 · Cobrado en el
   mes · 9 pagos" pegado a "Diseño aprobado · 3 de 5", dos tarjetas
   de datos en columnas de 170px con las palabras partidas en dos
   y tres renglones. Eso deja de leerse como prueba y pasa a
   leerse como estado de cuenta.

   El público de Caliber son negocios que NO son técnicos. Una
   pared de cifras no les dice "esto funciona": les dice "esto es
   complicado". El argumento humano ya está arriba, en el párrafo
   y en las cuatro viñetas ("Sitio o tienda completa", "Cobro en
   línea"), que sí hablan su idioma.

   Así que en teléfono cada renglón se queda con DOS piezas:
     · la PRUEBA — la portada de papel, la captura del panel, la
       tira de publicaciones. Es lo que enseña el producto.
     · UNA sola pieza de datos, a ANCHO COMPLETO. Una tabla que
       cabe se lee; dos apretadas a media pantalla, no.
   En escritorio siguen las seis: ahí hay ancho y se leen como
   composición, no como tablero.
   ============================================================ */
@media (max-width: 899px){
  .oficio__frag > :nth-child(n+3){ display: none }
  /* y la que queda deja de estar apareada: el ancho entero es lo que evita
     que la frase se parta y que la tabla se lea apretada */
  /* `!important` porque el primer renglón lleva `grid-column:span 5` en estilo
     INLINE desde el HTML, y un estilo inline le gana a cualquier hoja. */
  .oficio__frag > :nth-child(2){ grid-column: 1 / -1 !important }
}

/* ============================================================
   v3.11 — EL PORTAL SE VE (teléfono)
   ------------------------------------------------------------
   "Aquí no se aprecia mucho el portal, se ve más el texto."
   Medido: los cuatro pasos ocupaban ocho renglones —cada uno se
   partía en dos— antes de que apareciera el teléfono, y el marco
   quedaba en 325px porque su ancho se calcula contra el ALTO que
   sobra. Menos alto para el texto = más teléfono, directo.

   Los pasos que no están activos se recortan a UNA línea: siguen
   dando el mapa de dónde estás sin gastar media pantalla. El
   activo conserva sus dos líneas y su detalle.
   ============================================================ */
@media (max-width: 760px){
  .portal__pest button:not([aria-selected=true]) .pp__t{
    display: -webkit-box; -webkit-line-clamp: 1; -webkit-box-orient: vertical;
    overflow: hidden;
  }
  .portal__pest button{ padding: .34rem 0 }
  /* La cabecera fija se retira al bajar (§4), así que ya no hace falta
     reservarle 5.25rem de aire aquí arriba. */
  .portal__pegado{ padding-top: 3rem }
  .portal__marco{ width: min(96vw, 420px, calc((100svh - 4.5rem) * 0.462)) }
}

/* ============================================================
   v3.12 — LOS PROYECTOS SE RECORREN A LO ANCHO (teléfono)
   ------------------------------------------------------------
   Idea de paco, y va CONTRA lo que yo había argumentado. Mi
   objeción era buena en general —en un carrusel la mayoría no
   pasa del primer elemento, y aquí eso escondería tres de los
   cuatro casos, que son la única prueba de que sabemos trabajar—.
   Su contra-argumento es mejor: apilados, cada caso compite con
   el siguiente por la misma pantalla; en horizontal cada web
   ocupa el ancho entero y tiene su momento sola.

   Lo que hace la diferencia entre un carrusel que se recorre y
   uno que se abandona en el primero son dos señales, y las dos
   van aquí:
     · el siguiente ASOMA (87vw, no 100vw). Un borde cortado es
       la señal más barata y más clara de que hay más.
     · un CONTADOR de posición, para que se sepa cuántos faltan.

   ⚠️ El sangrado de §v3.8 se ANULA aquí: una captura con margen
   negativo dentro de una tarjeta de carrusel se sale de su celda.
   Aquí la tarjeta ya ocupa el ancho, que era el objetivo.
   ⚠️ Y el paralaje del `.caso` (§caliber.js) se pone a cero: un
   desplazamiento vertical dentro de un carril horizontal
   descuadra las tarjetas entre sí.
   ============================================================ */
@media (max-width: 899px){
  .casos{
    display: flex !important;
    gap: 1rem;
    overflow-x: auto;
    overflow-y: visible;
    scroll-snap-type: x mandatory;
    scroll-padding-inline: var(--borde);
    margin-inline: calc(var(--borde) * -1);
    padding-inline: var(--borde);
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
  }
  .casos::-webkit-scrollbar{ display: none }

  .caso{
    flex: 0 0 87vw;
    scroll-snap-align: start;
    display: grid; grid-template-columns: 1fr;
    gap: 1.5rem; align-content: start;
  }
  /* dentro del carril, la captura llena la tarjeta y NO se sale */
  #trabajo .caso__media .marco:not(.marco--vertical){
    margin-inline: 0;
  }
  #trabajo .pie-foto{ margin-inline: 0 }
  .caso__meta{ margin-top: 0 }

  /* el contador: una sola línea, discreta, encima del carril */
  .casos-guia{
    display: flex; align-items: center; gap: .75rem;
    margin: 0 0 1rem; color: var(--tx-3);
  }
  .casos-guia__n{ font-variant-numeric: tabular-nums; letter-spacing: .06em }
  .casos-guia__barra{
    flex: 1; height: 1px; background: var(--tx-linea); position: relative;
  }
  .casos-guia__barra::after{
    content: ''; position: absolute; inset: 0 auto 0 0;
    width: calc(100% / var(--total, 4));
    background: var(--bronce-claro, #C6A961);
    transform: translateX(calc(var(--i, 0) * 100%));
    transition: transform .3s var(--curva);
  }
}
@media (min-width: 900px){ .casos-guia{ display: none } }

/* ============================================================
   v3.13 — LA PROFUNDIDAD 3D ROMPÍA EL TOQUE
   ------------------------------------------------------------
   Bug reportado dos veces por paco: "Sistemas sigue sin poder
   desplegarse". Diagnóstico MEDIDO, no supuesto:

     getBoundingClientRect del botón  → y ≈ 389–441
     elementFromPoint(194, 414)       → DIV.oficios  ← ¡no el botón!
     eventos que llegan al botón      → CERO

   O sea: el botón se PINTA en un sitio y el toque aterriza en el
   contenedor. Es la firma de `translateZ` bajo una perspectiva:
   `.oficios` declara `perspective:1400px` con origen en 30% 50%,
   y cada oficio lleva `data-z` (-90 y -45), así que la proyección
   no coincide con la caja de layout y el hit-testing falla.
   Por eso "algunos sí y otros no": depende de la Z de cada uno.

   En teléfono la perspectiva se va. Ya está escrito antes en esta
   misma hoja para `.panel3d`: "el 3D a media pantalla es ruido, no
   profundidad". Aquí además rompía la interacción.
   ============================================================ */
@media (max-width: 899px){
  .oficios{ perspective: none; transform-style: flat }
}

/* ============================================================
   v3.14 — EL RECORRIDO CON EL DEDO
   ------------------------------------------------------------
   Pedido de paco: "cuando pongas el dedo encima que se bajen en
   los casos, igual que en escritorio cuando pones el cursor".

   Hasta ahora en teléfono el recorrido lo movía el scroll VERTICAL
   —así se resolvió cuando los casos iban apilados—. Con el carril
   horizontal (§v3.12) ese motor dejó de tener sentido: la tarjeta
   ya no cruza la pantalla de arriba abajo. El gesto natural pasa a
   ser el mismo que en escritorio, con el dedo en vez del cursor.

   `--rec` se registra con `@property` para que pueda TRANSICIONAR:
   una variable sin registrar salta de 0 a 1 sin pasar por en medio,
   y el recorrido no se vería. */
@property --rec{ syntax: '<number>'; inherits: true; initial-value: 0 }

@media (hover: none){
  /* 3.4 s de ida: es el ritmo del hover de escritorio (~100 px/s),
     que es donde se alcanza a LEER lo que va pasando. */
  .recorre{ transition: --rec 3.4s linear }
  .recorre.recorriendo{ --rec: 1 }
  /* la vuelta es rápida: soltar es una cancelación, no un recorrido */
  .recorre:not(.recorriendo){ transition: --rec .45s var(--curva) }
}
@media (prefers-reduced-motion: reduce){
  .recorre, .recorre.recorriendo{ transition: none }
}

/* ============================================================
   v3.15 — EL CARRIL, MÁS LIMPIO
   ------------------------------------------------------------
   Menos cromo alrededor de cada web y más aire entre tarjetas:
   lo que se mira es la captura, no la caja que la contiene.
   ============================================================ */
@media (max-width: 899px){
  .casos{ gap: .75rem }
  .caso{ gap: 1rem }
  /* la ficha técnica pesaba tanto como el caso: baja a dato de pie */
  .caso .meta-lista{ margin-top: .9rem; padding-top: .75rem; gap: .5rem }
  .caso__texto{ max-width: none }
  /* el carril no necesita el borde de la sección: respira solo */
  .casos-guia{ margin-bottom: .85rem }
}

/* ============================================================
   v3.16 — #entrada vuelve a anclarse, con el reparto de teléfono
   ------------------------------------------------------------
   paco pidió "el mismo movimiento que en escritorio". Vuelve el
   anclaje y el relevo de las tres puertas, pero con el recorrido
   acortado (1.1 pantallas en vez de 1.7) y menos separación entre
   puertas: repartir 1.7 pantallas entre tres bloques apilados a
   390px es lo que dejaba los huecos verticales con dos precios
   fantasma que él mismo fotografió. Mismo gesto, re-repartido.
   ============================================================ */
@media (max-width: 899px){
  #entrada .puertas{ gap: clamp(1rem, 3vw, 2rem) }
  /* la puerta dormida se atenúa MENOS que en escritorio: a media pantalla el
     26% de opacidad se lee como "está roto", no como "no es tu turno" */
  #entrada .pu-mov:not(.vivo){ opacity: .42 }

  /* ── Y AQUÍ ESTABA EL HUECO DE VERDAD ──────────────────────────────────
     No era el `gap`: la puerta dormida escondía su párrafo y su botón con
     OPACIDAD, y una cosa invisible sigue ocupando su altura. Medido en la
     captura: ~250px de vacío entre una puerta y la siguiente. En escritorio no
     molesta porque hay ancho de sobra; apilado a 390px es media pantalla en
     blanco entre precio y precio.
     Se colapsan de verdad, con `max-height` para que la transición siga
     existiendo — `display:none` haría saltar el layout en cada relevo. */
  #entrada .pu-mov:not(.vivo) .pu__una,
  #entrada .pu-mov:not(.vivo) .pu__cta,
  #entrada .pu-mov:not(.vivo) .pu__marca{
    max-height: 0; margin-block: 0; opacity: 0; overflow: hidden;
    transition: max-height .5s var(--curva), opacity .35s var(--curva),
                margin-block .5s var(--curva);
  }
  #entrada .pu-mov .pu__una,
  #entrada .pu-mov .pu__cta,
  #entrada .pu-mov .pu__marca{
    max-height: 14rem;
    transition: max-height .55s var(--curva), opacity .45s var(--curva),
                margin-block .55s var(--curva);
  }
}

/* ============================================================
   v3.17 — EL TELÉFONO DEL PORTAL, COMPLETO
   ------------------------------------------------------------
   "Que no se vea la imagen completa se me hace raro." Y medido,
   se salía de la pantalla:

     ventana                    844
     relleno superior            48
     los cuatro pasos           205   → el marco arranca en 269
     alto disponible            575
     alto real del marco        758   → SE SALÍA 182px

   La causa es un número mágico: el ancho se calculaba con
   `calc((100svh - 4.5rem) * 0.462)`, o sea descontando 72px de
   cabecera… cuando arriba hay 269. Cualquier cambio en los pasos
   volvía a romperlo.

   El arreglo no es afinar la resta: es dejar de restar. El pegado
   pasa a rejilla de dos filas —pasos `auto`, marco `1fr`— y el
   marco se dimensiona por ALTO con `aspect-ratio`, así que el
   ancho lo deduce el navegador de lo que de verdad sobra. Si
   mañana los pasos crecen o encogen, el teléfono se ajusta solo.
   ============================================================ */
@media (max-width: 760px){
  .portal__pegado{
    grid-template-columns: 1fr;
    grid-template-rows: auto minmax(0, 1fr);
    align-items: stretch;
    padding-top: 3rem;
    padding-bottom: 1.25rem;
    gap: 1rem;
  }
  .portal__marco{
    /* el ALTO manda y el ancho sale del ratio: exactamente al revés que antes */
    width: auto;
    height: 100%;
    max-width: 94vw;
    aspect-ratio: 780 / 1688;
    margin-inline: auto;
    justify-self: center;
    align-self: center;
  }
  /* la vista ya no impone su propio ratio: lo hereda del marco */
  .portal__vistas{ height: 100%; aspect-ratio: auto }
  .portal__vistas > .vista{ height: 100% }

  /* Y ahora el reparto: cada píxel que sueltan los pasos se lo queda el
     teléfono, porque el ancho sale del alto. El detalle del paso activo cuesta
     ~50px y lo que dice ya lo dice el propio portal en la captura de al lado;
     el título del paso se queda, que es el mapa. Medido: con el detalle el
     marco daba 257px de ancho; sin él, 300. */
  /* ⚠️ AGO-2026 · YA NO SE ESCONDE. El razonamiento de arriba era de espacio y
     era correcto cuando los cuatro detalles estaban a la vez; pero sólo se abre
     el del paso ACTIVO —los demás viven en `grid-template-rows:0fr`— así que el
     costo real es un renglón, no cuatro. Y lo que dice («Sin PDF ni ir y venir.
     Queda firmada, con fecha.») es de las frases que más venden del sitio: el
     visitante de teléfono no la estaba viendo. */
  .portal__pest .pp__d{ font-size:var(--t6) }
  .portal__pegado{ padding-top: 1.5rem }
}

/* ═══════════════════════════════════════════════════════════════════════
   §v3.18 · CALIDAD DE VIDA — los detalles que separan "bien hecho" de premium

   Medido contra tres referencias vivas (31 jul 2026):
     · Linear   → foco `1px solid #5E69D1` con `outline-offset:2px`, del color
                  de MARCA, no el azul del navegador. Enlace de salto como
                  primer tabulable.
     · Resend   → `-webkit-tap-highlight-color:transparent` global,
                  `text-wrap:balance/pretty`, `scroll-margin-top` en anclas.
     · Apple    → el anillo de foco es un TOKEN
                  (`--sk-focus-color` / `--sk-focus-offset`), no un valor suelto.

   Estado de Caliber antes de este bloque: 0 reglas de `::selection`,
   4 de `:focus-visible` para 31 elementos interactivos, 1 de `:active`,
   0 de `scroll-margin` con 18 anclas y una cabecera fija de 62px.
   ═══════════════════════════════════════════════════════════════════════ */

/* ── 1 · EL LIENZO DEL DOCUMENTO ────────────────────────────────────────
   LA FRANJA BLANCA. `body` pinta `--papel` y, como `html` no tenía fondo,
   ese color se PROPAGABA al lienzo del navegador. El hero y el pie son
   `oscuro`, así que el rebote elástico de iOS al llegar arriba o abajo
   destapaba #ECEDEF: una franja casi blanca contra secciones negras.
   No sale en capturas de escritorio porque no hay rebote — solo en el
   teléfono, que es justo donde paco la vio.
   Dándole fondo a `html` se corta la propagación: `body` sigue pintando su
   caja igual (el sitio se ve idéntico) y solo el hueco del rebote se
   oscurece.

   ⚠️ TRAMPA, y me costó una regresión completa: NO basta con darle fondo a
   `html`. En cuanto `html` tiene fondo propio, `body` DEJA DE PROPAGAR y pasa
   a pintar su papel sobre su propia caja — que cubre el documento entero — y
   tapa el lienzo `#mundo` que va debajo. El sitio salió color crema de arriba
   abajo, sin un solo error en consola. Por eso van las DOS reglas: `html`
   pinta el hueco del rebote y `body` se queda transparente para que el
   lienzo siga viéndose. */
html{ background:var(--tinta); color-scheme:dark }
body{ background:transparent }

/* ── 2 · SELECCIÓN DE TEXTO ─────────────────────────────────────────────
   Seleccionar texto mostraba el azul de sistema, que es el único color de
   toda la página que no eligió nadie. */
::selection    { background:var(--oro); color:var(--tinta); text-shadow:none }
::-moz-selection{ background:var(--oro); color:var(--tinta); text-shadow:none }
.papel ::selection, .papel::selection{ background:var(--bronce); color:var(--papel) }

/* ── 3 · ANILLO DE FOCO, TOKENIZADO ─────────────────────────────────────
   Un token y no un valor suelto, como Apple: la sección clara necesita
   bronce (el oro no contrasta contra papel) y hereda sola.
   `:focus:not(:focus-visible)` apaga el anillo con el ratón sin quitárselo
   al teclado — que es la razón de que exista `:focus-visible`. */
:root  { --foco:var(--oro-vivo) }
.papel { --foco:var(--bronce) }
a:focus-visible, button:focus-visible, [tabindex]:focus-visible,
summary:focus-visible, input:focus-visible{
  outline:2px solid var(--foco); outline-offset:3px; border-radius:2px;
}
a:focus:not(:focus-visible), button:focus:not(:focus-visible){ outline:none }

/* ── 4 · SALTO DE ANCLA BAJO LA CABECERA ────────────────────────────────
   18 enlaces internos contra una cabecera fija de 62px y CERO
   `scroll-margin`: cada salto metía el arranque de la sección debajo de la
   cabecera. Es el rótulo "OPERANDO" pisado que quedó sin diagnosticar.
   No se añade `scroll-behavior:smooth`: pelea con las secciones ancladas
   de ScrollTrigger. */
/* El mínimo subió de 4.75rem a 5.5rem cuando el CTA se volvió píldora y la
   cabecera pasó de 62 a 72px: en teléfono el mínimo mandaba y dejaba 4px de
   holgura contra la barra. Ahora deja 16. */
[id]{ scroll-margin-top:clamp(5.5rem, 9vh, 7rem) }

/* ── 5 · ENLACE DE SALTO ────────────────────────────────────────────────
   Primer tabulable del documento, como en Linear. Invisible hasta que el
   teclado lo toca. z-index por encima de la puerta, que si no nace debajo. */
.saltar{
  position:fixed; top:.75rem; left:.75rem; z-index:200;
  padding:.7rem 1.1rem; border-radius:999px;
  background:var(--oro); color:var(--tinta);
  font-size:var(--t6); font-weight:var(--p-ui); text-decoration:none;
  transform:translateY(-160%); transition:transform .22s var(--curva);
}
.saltar:focus{ transform:translateY(0) }

/* ── 6 · TACTO ──────────────────────────────────────────────────────────
   Sin esto, cada toque en el teléfono pinta el rectángulo gris del sistema
   por encima del diseño. Estaba puesto en UN elemento de treinta y uno. */
html{ -webkit-tap-highlight-color:transparent }

/* Respuesta al pulsar. El navegador no da ninguna en móvil —sin `:hover`,
   un botón que no se hunde se siente muerto. Solo en controles reales: una
   tarjeta de caso entera encogiéndose se vería como un fallo. */
.boton:active, .cab__cta:active, .cab__marca:active,
.pie__col a:active, .pie__correo:active, .portal__pest button:active{
  transform:scale(.97);
}
@media (prefers-reduced-motion:reduce){
  .boton:active, .cab__cta:active, .cab__marca:active,
  .pie__col a:active, .pie__correo:active, .portal__pest button:active{ transform:none }
}

/* ── 7 · BLANCOS TÁCTILES ───────────────────────────────────────────────
   Medido en vivo: los enlaces del pie daban 163×21 px y el CTA de cabecera
   69×21. El mínimo cómodo es 44.
   En la cabecera se agranda con un pseudo-elemento y NO con relleno: el
   relleno crecería la barra de 62 a 79px. Así el blanco llega a 44 y la
   cabecera no se mueve un píxel. */
.cab__cta, .cab__marca{ position:relative }
.cab__cta::after, .cab__marca::after{
  content:''; position:absolute; inset:-12px -10px;
}
@media (max-width:899px){
  .pie__col{ gap:0 }
  .pie__col a{ min-height:44px; display:flex; align-items:center }
  .pie__bajo a{ min-height:44px; display:inline-flex; align-items:center }
}

/* ── 8 · REMATE TIPOGRÁFICO ─────────────────────────────────────────────
   `balance` reparte el título entre sus renglones en vez de dejar una
   palabra huérfana abajo; `pretty` hace lo mismo con el último renglón de
   un párrafo. Dos líneas que quitan el defecto más visible de un titular
   grande en móvil. */
h1, h2, h3, .d1, .d2, .d3, .caso__titulo, .oficio__nombre{ text-wrap:balance }
/* `.pp__t` y `.oficio__plazo` no son `<p>` —son `<span>` dentro de un botón— y
   por eso se quedaban fuera. Medido a 390: los cuatro pasos del portal partían
   en dos líneas con la segunda de una palabra. */
p, .pieza__nota, .entrada__nota, .pie-foto, .pp__t, .pp__d span{ text-wrap:pretty }

/* ── 9 · BARRA DE DESPLAZAMIENTO ────────────────────────────────────────
   PROBADO Y DESCARTADO. Un `::-webkit-scrollbar` propio obliga al navegador
   a pasar de barra superpuesta a barra CLÁSICA, que ocupa layout: medido,
   el viewport pasó de 390 a 379px y todo el ancho de la página se recorrió.
   Ninguna de las tres referencias vivas (Linear, Resend, Apple) la pinta —
   Apple solo la ESCONDE dentro de sus galerías horizontales.
   `color-scheme:dark` (arriba, §1) ya deja la barra nativa oscura y a juego
   sin tocar una sola medida. No volver a intentarlo. */

/* ── 10 · LA CAPTURA CLARA CONTRA EL FONDO OSCURO ───────────────────────
   Medido en la captura completa de móvil: la única zona clara real dentro
   del tramo oscuro es la ventana de Black Secret — 200px que pasan de 10 a
   255 de luminancia en un píxel. El sitio del cliente es blanco y no se
   toca; lo que se arregla es el CANTO, que hoy es un corte a navaja.
   Una viñeta interior hace que la captura ENTRE en la página en vez de
   estrellarse contra ella, y el filo de oro al 14% la enmarca como pieza. */
.recorre__ventana::after{
  content:''; position:absolute; inset:0; pointer-events:none; z-index:2;
  box-shadow:
    inset 0  0 0 1px rgba(216,197,140,.14),
    inset 0  54px 60px -56px rgba(8,9,12,.50),
    inset 0 -54px 60px -56px rgba(8,9,12,.58);
}

/* ═══════════════════════════════════════════════════════════════════════════
   v3.19 · QoL — SEGUNDA TANDA
   Tres cosas que la primera tanda no cubrió, más una corrección de mi propio
   diagnóstico anterior.

   ⚠️ CORRECCIÓN — la alarma del `vh` era FALSA. Conté 13 usos de `vh` y avisé
   de salto de viewport en Safari de iPhone. Al mirarlos uno por uno: TODOS
   están dentro de `clamp()` para ESPACIADO, donde el salto no aplica. Todo el
   dimensionado real ya usa `svh` —`.hero`, `.pin__lienzo`, `.portal__escena`,
   `.cierre`—, que es justo la unidad estable que no se mueve cuando la barra
   de Safari se encoge. Ya estaba bien. No cambiar a `dvh`: `dvh` SÍ se mueve
   con la barra, sería un retroceso.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── 1 · ZONA SEGURA · IPHONE EN HORIZONTAL ──────────────────────────────
   La cabecera es `position:fixed; inset:0 0 auto`, o sea de borde a borde.
   En vertical no hay problema: Safari ya reserva la muesca. En HORIZONTAL la
   muesca pasa a un costado y se come la esquina donde viven la marca y el
   CTA — precisamente los dos controles que nunca deben quedar tapados.

   `max()` y no suma: en un teléfono sin muesca `env()` vale 0 y el borde
   normal manda; donde sí hay muesca, gana la que sea mayor. Sumar los dos
   habría empujado la cabecera hacia adentro en TODOS los aparatos. */
.cab{
  padding-left:  max(var(--borde), env(safe-area-inset-left));
  padding-right: max(var(--borde), env(safe-area-inset-right));
}
/* El enlace de salto está anclado a la esquina superior izquierda con `fixed`:
   mismo riesgo, misma cura. */
.saltar{ left:max(.75rem, env(safe-area-inset-left)) }

/* ── 2 · ALTO CONTRASTE Y COLORES FORZADOS ───────────────────────────────
   Windows en modo alto contraste sustituye TODOS los colores del autor por la
   paleta del sistema. Un diseño que confía en color de fondo para separar
   piezas se queda sin separación: aquí las ventanas de las capturas y las
   tarjetas del portal se volvían rectángulos invisibles. `forced-colors` es
   la única forma de recuperar el borde, y va con colores de SISTEMA
   (`CanvasText`), nunca con los nuestros — el modo los ignoraría. */
@media (forced-colors: active){
  .recorre__ventana, .portal__pest button, .oficio__disparo, .boton{
    border:1px solid CanvasText;
  }
  .recorre__ventana::after{ display:none }   /* la viñetita no sobrevive, estorba */
  a:focus-visible, button:focus-visible{ outline:2px solid Highlight }
}

/* ── 3 · IMPRESIÓN ───────────────────────────────────────────────────────
   Un estudio manda cotizaciones y hay clientes que las imprimen o las guardan
   en PDF desde el navegador. Sin esto salía el fondo `--tinta` a toda página:
   litros de tinta y texto claro sobre negro que muchas impresoras degradan a
   gris ilegible.

   Se invierte a papel blanco con texto negro, se retira todo lo que no
   significa nada en papel (el lienzo WebGL, la niebla, la cabecera fija, el
   enlace de salto) y se le pide al navegador que NO recorte los bloques a la
   mitad entre páginas. Los enlaces externos revelan su destino: en papel un
   subrayado sin URL es un callejón sin salida. */
@media print{
  html, body{ background:#fff !important; color:#000 !important }
  #mundo, .cab, .saltar, video, .niebla, [data-niebla]::before{ display:none !important }

  /* Las revelaciones por scroll dejan piezas en `opacity:0` esperando a GSAP,
     que en la vista de impresión puede no haber corrido. Sin esto, media página
     sale en blanco. */
  *{ opacity:1 !important; transform:none !important;
     box-shadow:none !important; text-shadow:none !important;
     color:#000 !important; background:transparent !important }

  .seccion{ page-break-inside:avoid; break-inside:avoid; padding:1.5rem 0 }
  h1, h2, h3{ page-break-after:avoid; break-after:avoid }
  img{ max-width:100% !important }

  a[href^="http"]::after{ content:" (" attr(href) ")"; font-size:.75em; color:#555 !important }
  a[href^="#"]::after, a[href^="mailto"]::after{ content:"" }   /* internos: la URL no aporta */
}

/* ═══════════════════════════════════════════════════════════════════════════
   v3.20 · LA CAPA DE TEMPERATURA · Y EL CTA SIEMPRE VISIBLE
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── 1 · LA CAPA DE TEMPERATURA ──────────────────────────────────────────
   El color del momento firma vivía en `body`. Funcionaba solo mientras `html`
   no tenía fondo: entonces `body` PROPAGABA su color al lienzo del navegador
   y no pintaba su propia caja. Al darle fondo a `html` en la v34 —para tapar
   la franja blanca del rebote de iOS— la propagación se cortó y `body` empezó
   a pintar 19,306px de casi-blanco ENCIMA del shader. Ese era el fallo que
   paco veía al volver a subir.

   Aquí el color tiene caja propia, fija, DEBAJO del lienzo. Arranca en
   `--tinta` para que antes de llegar al portal el sitio ya esté oscuro, que es
   como se comportaba con `immediateRender:false`. */
.temperatura{
  position:fixed; inset:0; z-index:-2; pointer-events:none;
  background:var(--tinta);
}

/* ── 2 · EL CTA DE LA CABECERA SE LEE SIEMPRE ────────────────────────────
   paco: "siempre dejar visible el empezar, porque se ve transparente y no se
   aprecia mucho".

   La causa es `mix-blend-mode:difference` en `.cab`. Esa mezcla es un acierto
   para la marca y el menú —garantiza que se lean sobre cualquier fondo sin
   pintarles una placa detrás— pero al CTA le quitaba el color de marca y sobre
   medios tonos lo dejaba lavado. Un botón de conversión no puede depender del
   fondo que le toque.

   La mezcla NO se quita del sitio: se BAJA del contenedor a los dos hijos que
   sí la quieren. Así el CTA queda fuera del grupo mezclado y puede ser una
   píldora de oro sólida, con su color real, siempre.

   Medido: la cabecera pasa de 62 a 67px de alto. Las anclas aterrizan en 76px
   (`scroll-margin-top`, §v3.18-4), así que siguen despejadas. */
.cab{ mix-blend-mode:normal }
/* ── CAPA 6 · ago-2026 — LA MEZCLA ERA CÓDIGO MUERTO, Y ADEMÁS DEJABA EL MENÚ
   INVISIBLE SOBRE LAS PIEZAS CLARAS ─────────────────────────────────────────
   Medido con getComputedStyle en los tres contextos —oscuro, sobre pieza clara
   y papel—: `mixBlendMode` daba **normal** en LOS TRES. La regla `.cab{
   mix-blend-mode:normal }` de más abajo la anulaba, así que la mezcla no llegó
   a mezclarse con nada: era blanco liso, siempre.
   Y no podía funcionar aunque se reactivara: `.cab` lleva `z-index:60`, o sea
   que CREA CONTEXTO DE APILADO, y `mix-blend-mode` solo mezcla con el fondo de
   su propio contexto. `.cab` no tiene fondo → mezcla contra nada.
   La inversión sobre papel que sí se ve NO viene de aquí: la hace `.cab--claro`,
   que en papel computa rgb(8,9,12). Verificado en píxel.
   Entonces: fuera la mezcla muerta, y el color pasa al token del sistema
   —#fff no era ni un token de la marca—. */
/* 🔴 NO USAR `--tx-1` AQUÍ. Se probó y dejó la cabecera INVISIBLE en el hero:
   `.cab` es `position:fixed`, vive FUERA de las secciones `.oscuro`, así que
   hereda la rampa de `body` —la de papel, rgba(9,10,13,.92)— y pintaba texto
   oscuro sobre tinta. La cabecera necesita su PROPIO par, que es justo el
   trabajo que hacía el `#fff` original. */
.cab__marca, .cab__nav{ color:var(--cab-tinta, rgba(237,239,243,1)) }

/* ── EL SUELO DE LA CABECERA ────────────────────────────────────────────────
   Lo que quedaba roto de verdad: la barra es transparente y TODO pasa visible
   por detrás. Dos síntomas medidos:
     · sobre la pieza rosa de Brushia, «Portal» y «Cuánto cuesta» desaparecen
       —texto claro sobre crema—, verificado con recorte ampliado a scrollY 1790;
     · el rótulo «Trabajo reciente» se imprime ENCIMA del menú con 10,134 px²
       de solape a opacidad plena, en scrollY 979 (test geométrico sobre todo el
       documento; es el peor de varios).
   El suelo es un degradado del propio fondo, no un panel: sobre tinta es
   invisible el 95 % del recorrido y solo aparece cuando algo claro cruza por
   debajo. `--cab-suelo` cambia con `.cab--claro` para que en papel el degradado
   sea de papel y la cabecera oscura siga leyéndose.
   🔴 NADA DE `backdrop-filter` AQUÍ: ya hay una trampa documentada —el filtro
      del header atrapa el drawer del menú—. Un degradado plano no la tiene. */
.cab::before{
  content:''; position:absolute; left:0; right:0; top:0;
  height:calc(100% + 2.5rem); z-index:-1; pointer-events:none;
  background:linear-gradient(to bottom,
    var(--cab-suelo, rgba(8,9,12,.96)) 0%,
    color-mix(in oklab, var(--cab-suelo, rgba(8,9,12,.96)) 62%, transparent) 58%,
    transparent 100%);
}
/* la tinta de papel va OPACA: con .92 el peor enlace medía 3.82:1 y antes de
   este cambio la cabecera clara era color pleno. No se hereda una regresión. */
.cab--claro{ --cab-suelo:rgba(244,241,234,.97); --cab-tinta:rgb(8,9,12) }

.cab__cta{
  padding:.5rem .9rem; border-radius:999px;
  background:var(--oro); color:var(--tinta);
  font-weight:var(--p-ui); text-decoration:none;
  transition:background var(--base) var(--curva), transform .22s var(--curva);
}
.cab__cta:hover{ background:var(--oro-vivo, #C6A961) }
/* El área táctil ya la da el pseudo-elemento de §v3.18-7; la píldora es lo
   que se VE, y con 32px de alto real más ese margen invisible cumple los 44. */

/* ═══════════════════════════════════════════════════════════════════════════
   v3.21 · LOS CASOS SE PUEDEN ABRIR
   Hasta la v39, #trabajo enseñaba cuatro casos y NINGUNO era clicable: 28
   bloques de caso y cero enlaces. Las dos páginas que existían (levia, chilita)
   sólo se alcanzaban por el sitemap. Ahora el título es la puerta.
   Se enlaza el TÍTULO y no el <article> entero a propósito: el bloque de media
   lleva el recorrido por hover/tacto y envolverlo en un <a> le robaría el gesto.
   ═══════════════════════════════════════════════════════════════════════════ */

.caso__enlace{
  color:inherit; text-decoration:none;
  display:inline-flex; align-items:baseline; gap:.4em;
  position:relative;
}
/* Área táctil por pseudo-elemento, no por relleno: el título no cambia de caja
   y la maqueta del caso no se mueve ni un píxel (§v3.18-7). */
.caso__enlace::before{ content:''; position:absolute; inset:-14px -10px }

/* El subrayado crece desde la izquierda al pasar el cursor. Es el gesto más
   discreto que dice "esto lleva a algún lado" sin pintar un botón dentro de una
   ficha que ya tiene tres niveles de texto. */
.caso__enlace::after{
  content:''; position:absolute; left:0; right:0; bottom:-.14em; height:1px;
  background:currentColor; transform:scaleX(0); transform-origin:left;
  transition:transform .42s var(--curva);
}
.caso__enlace:hover::after, .caso__enlace:focus-visible::after{ transform:scaleX(1) }

.caso__flecha{
  font-size:.5em; color:var(--oro); opacity:.7;
  transition:transform .42s var(--curva), opacity .3s;
}
.caso__enlace:hover .caso__flecha{ transform:translate(2px,-2px); opacity:1 }

/* Una salida explícita además del título: un titular que TAMBIÉN es enlace no
   siempre se lee como enlace, y este es el único camino a la página del caso. */
.caso__vermas{ margin-top:1.1rem }
.caso__vermas a{
  display:inline-flex; align-items:center; gap:.5em; min-height:44px;
  font-size:.875rem; font-weight:500; color:var(--oro);
  text-decoration:none; transition:gap .35s var(--curva);
}
.caso__vermas a:hover{ gap:.9em }

@media (prefers-reduced-motion:reduce){
  .caso__enlace::after{ transition:none }
  .caso__flecha, .caso__vermas a{ transition:none }
}

/* ═══════════════════════════════════════════════════════════════════════════
   v3.22 · CERRAR LO QUE QUEDABA
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── 1 · EL RÓTULO QUE MENTÍA ──
   Decía "pasa el cursor para recorrerlo" en los cuatro casos. Desde la v29, en
   teléfono el recorrido ARRANCA SOLO al tocar: el rótulo pedía un gesto que no
   existe en ese aparato.
   No se resuelve detectando el dispositivo desde JS —Playwright siempre reporta
   `hover:hover` y nunca podría verificarlo—. Se resuelve dejando que lo decida
   quien sí sabe: la media query corre EN el teléfono. */
.solo-tacto{ display:none }
@media (hover:none), (pointer:coarse){
  .solo-cursor{ display:none }
  .solo-tacto { display:inline }
}

/* ── 2 · LOS ÚLTIMOS OBJETIVOS TÁCTILES ──
   Medido en producción a 390px: enlace de salto 43px (a UNO de distancia), los
   tres pasos del portal 31-32px, el correo del pie 27px. Todos crecen por
   pseudo-elemento para no mover una sola caja de la maqueta. */
.saltar{ min-height:44px; display:inline-flex; align-items:center }

.portal__pest button{ position:relative }
.portal__pest button::after{ content:''; position:absolute; inset:-7px 0 }

.pie__correo{ position:relative; display:inline-flex; align-items:center }
.pie__correo::after{ content:''; position:absolute; inset:-9px 0 }

/* Los tres botones de #entrada quedaban en 44 justos: el redondeo los tiraba
   por debajo en algunos anchos. Un pelo más y dejan de depender del decimal. */
@media (max-width:899px){
  .boton{ min-height:46px }
}


/* ═══════════════════════════════════════════════════════════════════════════
   v3.22 · LO QUE EL AUDIT DE AGOSTO ENCONTRÓ ROTO
   Tres hallazgos medidos en producción (1440x900 y 390x844), no supuestos.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── 1 · EN TELÉFONO NO HABÍA NAVEGACIÓN ─────────────────────────────────────
   `.cab__nav{display:none}` por debajo de 900px, y NADA que lo sustituyera: no
   había botón de menú en ninguna parte del documento. Medido en 390px, la
   cabecera entera se reducía a la marca y el CTA. Para llegar a Precios había
   que recorrer 13,974px a mano — 16.6 pantallas.

   El panel no vive dentro de `.cab` a propósito: la cabecera le pone
   `mix-blend-mode:difference` a sus hijos y un panel mezclado sobre el shader
   del mundo se vuelve ilegible. Fuera de ella puede llevar tinta sólida. */
.visualmente-oculto{
  position:absolute; width:1px; height:1px; padding:0; margin:-1px;
  overflow:hidden; clip-path:inset(50%); white-space:nowrap; border:0;
}

/* 🔴 EL CONTROL MÁS IMPORTANTE DEL TELÉFONO ERA EL MÁS PEQUEÑO. Medido a
   390px con el área efectiva (caja + pseudo-elementos): 41×28. El mínimo
   táctil es 44×44 y éste es el único de toda la página que no llegaba — los
   demás ya crecían por `::after`, que `getBoundingClientRect` no ve, así que
   una medida ingenua los acusa de más. El relleno crece y el margen negativo
   lo devuelve: la maqueta no se mueve un píxel. */
.cab__abre{
  display:none; background:none; border:0;
  min-width:44px; min-height:44px; padding:.85rem; margin:-.85rem;
  color:inherit; cursor:pointer; line-height:0;
}
.cab__rayas{ display:block; width:22px }
.cab__rayas i{
  display:block; height:1.5px; background:currentColor; border-radius:1px;
  transition:transform .3s var(--curva), opacity .2s linear;
}
.cab__rayas i + i{ margin-top:6px }
/* La cruz se arma girando las dos rayas sobre el mismo centro. Se traslada
   ANTES de girar (3.75px = medio hueco + medio grosor) o el aspa queda abierta. */
[aria-expanded="true"] .cab__rayas i:first-child{ transform:translateY(3.75px) rotate(45deg) }
[aria-expanded="true"] .cab__rayas i:last-child { transform:translateY(-3.75px) rotate(-45deg) }

@media (max-width:899px){
  .cab__abre{ display:block }
  /* El botón mezcla como la marca y el menú: sobre el hero oscuro y sobre el
     papel de #entrada tiene que leerse igual, y es la misma garantía que ya
     usan los otros dos. */
  .cab__abre{ mix-blend-mode:difference; color:#fff }
}

.menu{
  position:fixed; inset:0; z-index:70;
  /* El panel es hijo de <body>, y `body` declara la terna de texto para FONDO
     CLARO (--tx-1 = tinta al 92%). Sobre la tinta del panel eso es tinta sobre
     tinta: la primera prueba salió con el fondo negro y CERO texto, sólo el
     filo de oro del botón. La terna se vuelve a declarar aquí, igual que hace
     `.oscuro`, en vez de heredar la equivocada. */
  --tx-1: rgba(237,239,243,1); --tx-2: rgba(237,239,243,.62); --tx-3: rgba(237,239,243,.50);
  --tx-linea: rgba(255,255,255,.12);
  background:var(--tinta); color:var(--tx-1);
  display:flex; flex-direction:column; justify-content:center;
  gap:clamp(2rem,7vh,4rem); padding:5rem var(--borde) calc(2rem + env(safe-area-inset-bottom));
  overscroll-behavior:contain;
}
/* Sin esta línea el atributo `hidden` NO cierra el panel: un `display:flex` de
   autor le gana siempre al `display:none` del user-agent. Ya nos costó una
   vuelta en otro proyecto — queda escrito. */
.menu[hidden]{ display:none }

.menu__lista{ display:grid; gap:.15rem }
.menu__lista a{
  display:flex; align-items:baseline; gap:1rem;
  font-family:var(--display); font-weight:var(--p-aire);
  font-size:clamp(1.75rem,9vw,2.5rem); line-height:1.18;
  letter-spacing:-.025em; color:var(--tx-1); text-decoration:none;
  padding-block:.35rem;                       /* 44px de alto real sin tocar la maqueta */
}
/* `.menu__n` murió con la numeración del menú móvil: una navegación no es una
   secuencia, y numerarla no informa de nada. Regla ya aplicada a los rótulos de
   sección en julio; el menú se había quedado atrás. */
.menu__pie{ display:flex; align-items:center; gap:1.5rem; flex-wrap:wrap }
.menu__wa{
  font-size:.875rem; color:var(--tx-2); text-decoration:none;
  display:inline-flex; align-items:center; gap:.25rem; min-height:44px;
}
@media (prefers-reduced-motion:no-preference){
  .menu__lista a{ transition:opacity .2s linear }
  .menu__lista a:active{ opacity:.55 }
}
/* Con el menú abierto la cabecera no puede retirarse: se llevaría el botón de
   cerrar fuera de pantalla y dejaría al panel sin salida visible. */
html.menu-abierto .cab--oculta{ transform:none }
html.menu-abierto{ overflow:hidden }
/* Y tiene que quedar POR ENCIMA del panel. La primera prueba salió con el panel
   a z-70 tapando la cabecera a z-60: el menú abría bien y no había forma de
   cerrarlo más que con Escape, que en un teléfono no existe. */
html.menu-abierto .cab{ z-index:80 }
/* Y el CTA de la cabecera se retira mientras el panel está abierto: el panel ya
   lleva el suyo, más grande y al alcance del pulgar. Dos "Empezar" a 40px de
   distancia no son dos oportunidades, son una duda. */
html.menu-abierto .cab__cta{ opacity:0; pointer-events:none }

/* ── 2 · LA CABECERA DESAPARECÍA SOBRE EL PAPEL ──────────────────────────────
   Medido sobre los PÍXELES de la captura, no sobre el DOM: en #entrada los
   enlaces daban 1.13:1 contra el fondo. AA pide 4.5:1. Al ojo, invisibles.

   La causa es que `mix-blend-mode:difference` mezcla contra el trasfondo de SU
   grupo de apilado, y #entrada está pinneada por GSAP —o sea que su transform
   le abre un grupo propio y la mezcla nunca la ve—. Lo que la cabecera acaba
   restando es el fondo oscuro de `html` (#08090C), y 255-8 = 247: blanco sobre
   crema. La mezcla no está mal escrita; está midiendo la capa equivocada.

   El DOM decía 19.9:1 y mintió. Por eso el arreglo no confía en la mezcla para
   la corrección: la APAGA sobre papel y pinta tinta explícita. La mezcla se
   queda donde sí funciona, que es todo lo oscuro. */
.cab--claro .cab__marca,
.cab--claro .cab__nav,
.cab--claro .cab__abre{
  mix-blend-mode:normal;
  color:var(--tinta);
}
.cab--claro .cab__nav a{ opacity:.72 }
.cab--claro .cab__nav a:hover{ opacity:1 }
/* El CTA ya era una píldora sólida fuera del grupo mezclado: sobre papel el oro
   pierde separación contra el crema, así que ahí se apoya en un filo de tinta. */
.cab--claro .cab__cta{ box-shadow:inset 0 0 0 1px rgba(9,10,13,.22) }

/* ── 3 · EL TAMAÑO DE TITULAR EN TELÉFONO ────────────────────────────────────
   Este bloque bajó `--t1` a 8.2% del ancho (32px a 390) porque el suelo del
   clamp estaba escrito mirando un escritorio y el titular se leía como un muro.
   La corrección era buena; el número, corto.

   ⚠️ REVISADO AGO-2026 CONTRA MEDIDA EN VIVO. Cuberto en teléfono titula a
   **62px = 15.9% del ancho**; Apple a 48. Con 32px el contraste de escala en
   móvil quedaba en **1.9×** —contra 3.6× en escritorio y 3× de Cuberto móvil—,
   o sea prácticamente sin jerarquía. Y como el titular a sangre del cierre se
   desactiva aquí (quince caracteres a sangre en 390px dan 26px de tipo), el
   teléfono se quedaba SIN un solo momento de impacto.
   13.4vw da 52px a 390 y 62 a 460. El impacto en móvil se RE-COMPONE, no se
   encoge: el titular vale más grande y en más líneas que pequeño y en dos. */
@media (max-width:560px){
  :root{ --t1: clamp(2.75rem, 13.4vw, 3.5rem) }  /* 390px → 52px = 13.4% */
}

/* ── 4 · LA ANIMACIÓN SE COMÍA EL CONTENIDO ──────────────────────────────────
   Los relevos apagaban lo inactivo a .28 y .26. La animación se ve bien en
   movimiento, pero la gente PARA de scrollear: en reposo dos de cada tres
   renglones de #sistemas y dos de tres precios de #entrada quedaban ilegibles.
   Se sube el piso lo justo para que se lean sin que el relevo pierda su
   jerarquía — lo vivo sigue siendo lo único a opacidad plena. */
.paso{ opacity:.46 }
.pu-mov:not(.vivo){ opacity:.5 }
/* El CTA de la tarjeta apagada sigue oculto: leer la fila no es poder pulsarla. */

/* ═══════════════════════════════════════════════════════════════════════════
   IMPACTO TIPOGRÁFICO · ago-2026 — LOS CUATRO RECURSOS DE COMPOSICIÓN
   ───────────────────────────────────────────────────────────────────────────
   El sistema (una familia, un peso, seis tamaños) dejó el sitio limpio y
   PLANO. Medido contra las referencias: contraste de escala 3.6× aquí contra
   5× de Cuberto, 5.3× de Obys y 6.7× de basement. Y el titular más ancho
   ocupaba el 52 % del lienzo cuando basement llega al 98 %.
   La salida NO es subir la escala entera —eso devuelve el ruido que costó
   quitar—: el sistema se queda en 3.6× y se abren DOS momentos. El impacto
   sale de la excepción, no del promedio.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── 1 · EL TITULAR A SANGRE ──────────────────────────────────────────────
   Un texto que toca los dos márgenes deja de ser texto y pasa a ser
   arquitectura. Va UNA vez en todo el documento, en el cierre.
   El tamaño sale del ancho de la VENTANA, no de la escala: es la única forma
   de que la línea toque los dos bordes a cualquier medida. `nowrap` porque
   una línea a sangre que se parte deja de ser el recurso.
   Medido a 1512: 1336px de 1336 disponibles, cero desborde. */
#cierre .d1{
  font-size:clamp(2.75rem, 12.55vw, 13rem);
  line-height:.9; letter-spacing:-.045em;
  white-space:nowrap; max-width:none;
}
#cierre .cierre__bajo{
  display:flex; justify-content:space-between; align-items:baseline;
  gap:1.5rem; flex-wrap:wrap; margin-top:1.6rem;
}
#cierre .cierre__dice{ margin:0; color:var(--tx-2); font-size:var(--t5) }
#cierre .cierre__cta{ margin:0 }
@media (max-width:640px){
  /* a 390px una línea a sangre de quince caracteres da 26px de tipo: ilegible.
     El recurso NO se encoge: se re-compone. En teléfono el titular parte en
     líneas y crece a 16vw —62px a 390, exactamente lo que mide Cuberto en
     móvil— para que el cierre siga siendo el golpe también aquí. */
  #cierre .d1{ white-space:normal; font-size:16vw; line-height:.95;
               letter-spacing:-.035em }
}

/* ── 2 · UN SOLO MOMENTO A 6.7× ───────────────────────────────────────────
   La cifra que ya existía («4 negocios operando hoy») era un renglón de 17px
   perdido en una tira de tres. Sola y a 114px es la prueba social más fuerte
   de la página, y no cuesta un elemento nuevo: es el mismo dato.
   6.7× sobre el cuerpo de 17 — el contraste que mide basement. */
#entrada .entrada__prueba{
  display:grid; grid-template-columns:auto minmax(0,1fr);
  gap:0 clamp(1.5rem,3vw,2.5rem); align-items:center;
}
#entrada .entrada__prueba .cifra{
  font-size:clamp(3.5rem, 7.5vw, 7.125rem); line-height:.82;
  letter-spacing:-.05em; color:var(--bronce); font-variant-numeric:tabular-nums;
}
.oscuro .entrada__prueba .cifra{ color:var(--oro) }
#entrada .entrada__prueba p{ margin:0; font-size:var(--t4); line-height:1.5;
                             color:var(--tx-2); max-width:38ch }
/* 🔴 `font-size:inherit` NO es de adorno: la regla vieja de `.entrada__prueba b`
   ponía el dato en --t3 porque antes ERA el dato (tres renglones con su cifra
   en negrita). Ahora la cifra vive fuera y el `b` es sólo el remate de una
   frase: sin esto, media oración se renderiza a 28px dentro de un párrafo
   de 19 y parece un error de maquetación. */
/* CAPA 1 · ago-2026 — fuera el salto de tono a media frase. Ver trabajo.css. */
/* `color:inherit` EXPLÍCITO y no simplemente quitar la línea: la regla base
   `.entrada__prueba b` (arriba) sigue pintando --tx-1 y ganaba en cuanto este
   bloque dejó de declarar color. Medido: el salto .60 → .92 seguía vivo. */
#entrada .entrada__prueba p b{ font:inherit; font-weight:var(--p-cuerpo);
                               letter-spacing:inherit; color:inherit }

/* ── 3 · ROMPER LA ALINEACIÓN ÚNICA ───────────────────────────────────────
   El defecto más medible de todos: 134 nodos alineados a la izquierda, CERO
   centrados y CERO a la derecha, en dieciocho pantallas. Los cuatro
   referentes mezclan tres alineaciones (Cuberto 158/36/6, Obys 392/41/39).
   Una sola alineación en un documento largo es la definición de monotonía, y
   se arregla sin tocar una palabra.
   `#entrada` es la sección de decisión: centrar su titular la separa de las
   cinco anteriores justo donde la persona tiene que detenerse. */
#entrada .cabecera-seccion{ max-width:none; justify-items:center; text-align:center }
#entrada .cabecera-seccion .d2{ max-width:20ch }
@media (max-width:640px){
  #entrada .cabecera-seccion{ justify-items:start; text-align:left }
}

/* ── 4 · EL DATO COMO TIPOGRAFÍA ──────────────────────────────────────────
   El plazo era una etiqueta pegada al nombre. Alineado a la DERECHA contra el
   nombre, el renglón se vuelve composición: el ojo cruza de un extremo al
   otro y el número —que es lo que el cliente de verdad quiere saber— se lleva
   la mitad del peso. Es lo que hacen Apple y Hodinkee con sus
   especificaciones, y de paso mete la tercera alineación que faltaba. */
#entrada .pu__plazo{ text-align:right; justify-self:end }
#entrada .pu__plazo em{ display:inline-block; margin-left:.45rem }

/* el pie de la cinta medía 160 caracteres por renglón. El máximo legible son
   unos 75; a 160 el ojo pierde el salto de línea al volver a la izquierda. */
/* Era 74ch declarados, que a 17px de Switzer RENDERIZAN 79 caracteres reales —
   la medida más ancha de toda la página contra una mediana de 50. Por encima de
   ~75 el ojo pierde el salto de línea al volver a la izquierda. 68 declarados
   caen en 72 reales. */
#servicios .cinta-nota{ max-width:68ch }

/* ── LA FIRMA · EL ECLIPSE SE COME LA PANTALLA ────────────────────────────
   Un momento firma por página, no cinco: el resto del sitio es contención
   para que éste golpee. Va en el único cruce papel→tinta que queda.
   El mecanismo es un `clip-path:circle()` que crece: debajo está el mundo
   oscuro con el astro, y el círculo lo destapa. El papel no se pinta aquí —
   es el de `#entrada`, que se ve por el hueco que el círculo aún no abrió. */
.eclipse-paso{ height:115svh; position:relative }
.eclipse-paso__fijo{
  position:sticky; top:0; height:100svh; overflow:hidden; isolation:isolate;
}
.eclipse-paso__mundo{
  position:absolute; inset:0; z-index:1; background:var(--tinta);
  display:grid; place-items:center;
}
/* el papel ENCIMA, con un agujero que crece. `--hueco` lo escribe el JS, y el
   corte es duro a propósito —las dos paradas del degradado en el mismo tope—:
   un canto suave aquí lee a viñeta, no a eclipse. */
.eclipse-paso__papel{
  position:absolute; inset:0; z-index:2; background:var(--papel);
  -webkit-mask-image:radial-gradient(circle at 50% 50%,
     transparent var(--hueco,0%), #000 var(--hueco,0%));
          mask-image:radial-gradient(circle at 50% 50%,
     transparent var(--hueco,0%), #000 var(--hueco,0%));
}
.eclipse-paso__astro{
  width:min(78svh, 92vw); height:auto;
  transform:scale(var(--astro, .82));
  /* el JPEG del astro trae su propio negro; sobre la tinta el canto del
     cuadrado desaparece porque son el mismo valor, pero el degradado lo
     asegura a cualquier densidad de pantalla */
  -webkit-mask-image:radial-gradient(circle at 50% 50%, #000 62%, transparent 78%);
          mask-image:radial-gradient(circle at 50% 50%, #000 62%, transparent 78%);
}
@media (prefers-reduced-motion:reduce){
  /* sin movimiento el paso no tiene sentido: se queda el mundo oscuro entero
     y el documento sigue leyéndose igual */
  .eclipse-paso{ height:0; overflow:hidden }
}
@media (max-width:640px){
  /* en teléfono el paso cuesta más de lo que da: 115svh de scroll para un
     gesto que a 390px de ancho no llena la vista con el mismo efecto. Se
     reduce a un cruce corto en vez de quitarlo del todo. */
  .eclipse-paso{ height:70svh }
  .eclipse-paso__astro{ width:min(52svh, 88vw) }
}

/* ═══════════════════════════════════════════════════════════════════════════
   EL PIE, COMPUESTO · ago-2026

   Medido a 1512: la rejilla de cinco columnas ocupa 1361px de ancho y el bloque
   de arriba —marca, frase de cierre y las dos vías de contacto— ocupaba 450.
   El 33%. Es decir: dos tercios de vacío arriba, y debajo una rejilla a sangre
   completa. No es aire, es un borde superior que no cierra con nada.

   El arreglo no es agrandar el texto: es DARLE LA OTRA ORILLA. La frase se
   queda a su medida cómoda (46ch) a la izquierda y las dos vías de contacto se
   van al canto derecho, que es exactamente donde termina la columna «Dónde».
   Así el pie abre y cierra con el mismo ancho, y las dos acciones caen donde
   el ojo ya venía bajando.

   Debajo de 900px vuelve a una sola columna: apilado es lo correcto ahí, y el
   botón de WhatsApp tiene que quedar bajo la frase que lo justifica.
   ═══════════════════════════════════════════════════════════════════════ */
@media (min-width:900px){
  .pie__alto{
    max-width:none;
    grid-template-columns:minmax(0,34ch) minmax(0,auto);
    grid-template-rows:auto auto;
    column-gap:clamp(2rem,6vw,6rem);
    align-items:end;
  }
  .pie__marca            { grid-column:1; grid-row:1 }
  /* 🔴 LA FRASE DE CIERRE SUBE A 28px. Estaba a 17 —tamaño de nota al pie— y
     era lo ÚLTIMO que dice el sitio después de un titular de 190px. El pie
     abría con un susurro. A 28 en tono --tx-1 el bloque izquierdo tiene peso
     suficiente para sostener las dos vías al otro extremo; a 17 el hueco entre
     los dos se leía como olvido, no como aire. */
  .pie__alto .parrafo{
    grid-column:1; grid-row:2;
    font-size:var(--t3); line-height:1.22; letter-spacing:-.016em;
    color:var(--tx-1); max-width:none;
  }
  /* fila 2 y alineado al fondo: las vías caen a la altura de la ÚLTIMA línea de
     la frase, no flotando a media caja */
  .pie__rutas{ grid-column:2; grid-row:2; justify-self:end; align-self:end }
}

/* ══════════════════════════════════════════════════════════════════════════
   CAPA 8 · LA PANTALLA DE RESPIRO — ago-2026
   Rango dinámico: las 16 pantallas del sitio cargaban entre 35 y 124 palabras
   (razón 3.5). Cuberto va de 5 a 627. Ésta carga siete.

   El arco es EL INSTRUMENTO, no un adorno: el mismo que mide el avance por el
   sitio, a tamaño real, con su tramo recorrido en oro y su lectura.

   🔴 DOS TRAMPAS DE GEOMETRÍA, las dos costaron una tanda de capturas:

   1. SE POSICIONA POR `top`, NUNCA POR `bottom`. La máscara conserva la
      CORONILLA del círculo; con `bottom` y un círculo de 172vw esa coronilla
      queda a miles de píxeles del borde inferior — fuera de pantalla, la
      sección sale VACÍA y la consola queda limpia. Cero señal del fallo.

   2. EL ÁNGULO DEL ORO NO ES LIBRE. Con radio 86vw y viewport de 1512 solo se
      ven los ángulos 324°–36° del anillo. A 300° el tramo dorado caía fuera y
      el arco salía todo gris. `from 331deg` lo mete en el tramo visible.
   ══════════════════════════════════════════════════════════════════════════ */
.respiro{
  min-height:100svh; position:relative; overflow:hidden;
  display:grid; align-content:center;
  padding-inline:clamp(1.5rem, 9vw, 9rem);
}

/* 🔴 `top:42%` CRUZABA EL TITULAR, y solo se vio mirando la captura.
   Medido a 1512×900: la coronilla caía en y=378 y el bloque de texto ocupa
   352-547 — o sea justo por en medio, partiendo «Tres oficios.» y «sola».
   En teléfono nunca pasó porque ahí la regla de abajo ya lo mandaba a 72%.
   A 68% la coronilla queda en ~612, despejada de los 547 del texto, y con un
   círculo de 172vw (radio ~1300 px) el arco baja 242 px hacia los bordes: se
   va a ~854 y sigue cabiendo en la ventana sin recortarse.
   La tipografía queda ARRIBA del horizonte, que es la lectura que ya daba el
   móvil y compone mejor que el arco atravesando las palabras. */
.respiro__arco{
  position:absolute; left:50%; top:68%; translate:-50% 0;
  width:172vw; aspect-ratio:1; border-radius:50%;
  border:1.5px solid transparent;
  background:conic-gradient(from 331deg,
      var(--oro-vivo) 0turn, var(--oro) .052turn,
      rgba(237,239,243,.17) .0521turn, rgba(237,239,243,.17) .30turn,
      transparent .301turn) border-box;
  mask:linear-gradient(#000 0 0) padding-box, linear-gradient(#000 0 0);
  -webkit-mask:linear-gradient(#000 0 0) padding-box, linear-gradient(#000 0 0);
  mask-composite:exclude; -webkit-mask-composite:xor;
  pointer-events:none;
}
/* El canto encendido es una COPIA DIFUMINADA del mismo anillo, no una barra
   colocada a mano: cae exactamente sobre el borde a cualquier ancho, sin
   trigonometría que se rompa al cambiar de pantalla. */
.respiro__arco::after{
  content:''; position:absolute; inset:-2px; border-radius:50%;
  border:3px solid transparent;
  background:conic-gradient(from 331deg,
      transparent 0turn, rgba(255,247,224,.9) .046turn,
      var(--oro) .053turn, transparent .062turn) border-box;
  mask:linear-gradient(#000 0 0) padding-box, linear-gradient(#000 0 0);
  -webkit-mask:linear-gradient(#000 0 0) padding-box, linear-gradient(#000 0 0);
  mask-composite:exclude; -webkit-mask-composite:xor;
  filter:blur(2px);
}

/* Alineado a la IZQUIERDA, no centrado: es la única pantalla del sitio
   compuesta así, y esa asimetría es la que la vuelve un respiro y no otra
   portada. El texto cuelga del arco en vez de vivir dentro de él. */
.respiro__dice{
  position:relative; z-index:2; margin:0; max-width:19ch;
  text-align:left; justify-self:start;
}
.respiro__dice b{ font-weight:var(--p-ui); color:var(--oro) }
/* ⚠️ Esto NO es mobiliario de texto de lo que el sitio se quitó: es la LECTURA
   del aparato —dónde estás— y es lo que justifica que el arco vuelva en todo
   el sitio en vez de ser un arco bonito más. Servicios es la 2ª de las cinco
   secciones de contenido (trabajo · servicios · sistemas · portal · entrada). */
.respiro__dice::before{
  content:'02 / 05 · los servicios';
  display:block; margin-bottom:1.75rem;
  font-size:var(--t6); letter-spacing:.14em;
  color:var(--oro-vivo); font-weight:var(--p-ui);
}

/* Al salir, el arco se encoge hacia el borde superior: se va a su puesto.
   `view()` y no un temporizador — el ritmo lo pone quien navega. */
@supports (animation-timeline: view()){
  @media (prefers-reduced-motion: no-preference){
    .respiro__arco{
      animation:respiro_a_su_puesto linear both;
      animation-timeline:view(); animation-range:exit 0% exit 100%;
    }
    @keyframes respiro_a_su_puesto{
      from{ scale:1; opacity:1 }
      to  { scale:.34; opacity:.45 }
    }
  }
}

/* Móvil: el círculo crece en proporción al ancho, así que a 390 px la curva se
   vuelve casi recta y deja de leerse como un cuerpo. Se achica el radio para
   que la curvatura siga siendo visible. */
@media (max-width:640px){
  /* ⚠️ El `top` NO se hereda de escritorio. A 390 el titular ocupa cuatro
     renglones en vez de tres y el arco le pasaba POR ENCIMA, entre la primera
     y la segunda línea. Va debajo del bloque de texto, no a media frase. */
  .respiro__arco{ width:230vw; top:72% }
}

/* ══════════════════════════════════════════════════════════════════════════
   CAPA 9 · EL ARCO VUELVE, Y UNA VEZ MIDE — ago-2026
   El arco salía dos veces (el eclipse del hero y el respiro): dos apariciones
   no son un sistema, son una coincidencia. Aquí vuelve dos veces más, y una de
   ellas PORTA INFORMACIÓN — que es lo único que lo separa de "un arco bonito".

   🔴 NI `::before` NI `::after` DE SECCIÓN: los dos están ocupados en
      `.oscuro` (fondo de tinta en 863, grano en 887). Por eso la junta es un
      elemento real, `<span class="arco-junta">`, y no un pseudo.
   🔴 EL MEDIDOR NO CUELGA DE `.cab`. `.cab` lleva `z-index:60` y crea contexto
      de apilado — meterlo dentro repetiría el error de la Capa 6, donde el
      `mix-blend-mode` quedó como código muerto por exactamente esto. Va suelto
      en el body, en z-index 61.
   🔴 Y VA POR ENCIMA DE `.cab` (60), NO por debajo. A 55 quedaba DEBAJO del
      suelo `.cab::before` de la Capa 6 —que vive dentro del contexto de `.cab`
      y por tanto sube con él— y la cabecera atenuaba el medidor hasta hacerlo
      ilegible sobre las secciones claras. Medido: el oro solo se veía en la
      zona oscura de la izquierda. No estorba a la nav porque es una hairline
      en y=0..42 y el texto de la nav está centrado en y≈52.
   ══════════════════════════════════════════════════════════════════════════ */

/* Registrada para que el navegador SEPA interpolarla. Sin `@property` un
   custom property se anima a saltos (de un valor al otro, sin intermedios) y
   el llenado del medidor daría un brinco en vez de crecer. */
@property --avance{ syntax:'<number>'; initial-value:0; inherits:true }
/* `inherits:true` a propósito: la animación corre en `.medidor` y el degradado
   vive en su `::before`. Con `inherits:false` el pseudo no ve el valor y la
   barra se queda vacía para siempre, sin ningún error. */

/* ── APARICIÓN 3 · EL MEDIDOR (la estructural) ───────────────────────────
   El mismo arco del respiro, visto desde lejos.
   🔴 LA FLECHA DEL ARCO ESTÁ ACOTADA POR LA NAV. A 900vw bajaba 42 px del
      centro a las esquinas y a 42 px vive el texto de la nav (centrado en
      y≈52): el arco le pasaba por encima y mordía la píldora «Empezar».
      A 1800vw baja 21 px y despeja. Si alguien vuelve a tocar este ancho:
      flecha = r − √(r² − 756²), y tiene que quedar por debajo de 40.
   El tramo dorado crece con el scroll: eso es lo que lo vuelve un aparato. */
/* 🔴 EL ARCO VA DENTRO DE UNA BANDA QUE RECORTA, no suelto. Un círculo de
   1800vw es un elemento con CAJA DE LAYOUT: aunque la máscara solo pinte la
   coronilla, la caja sigue empujando el ancho del documento y aparece scroll
   horizontal. Medido: `scrollWidth > innerWidth` en 1512 Y en 390. La banda
   con `overflow:hidden` corta la caja; el círculo va en el `::before`. */
.medidor{
  position:fixed; top:0; left:0; right:0; height:64px; z-index:61;
  overflow:hidden; pointer-events:none;
  animation:medir linear both;
}
.medidor::before{
  content:''; position:absolute; top:0; left:50%; translate:-50% 0;
  width:1800vw; aspect-ratio:1; border-radius:50%;
  border:1.5px solid transparent;
  /* 🔴 EL ÁNGULO NO ES DECORATIVO, ES GEOMETRÍA: solo se ve el tramo del
     anillo que cabe en el viewport. Con radio 900vw ese tramo son 6.37°
     —2·asin(0.5/9)— y empieza en 360−3.185 = 356.815°. Si se cambia el ancho
     del medidor HAY QUE recalcular los dos: con el ancho móvil y estos mismos
     números el oro se sale de la pantalla y la línea queda gris para siempre.
     Por eso van en tokens y no cableados. */
  --arco-desde:356.815deg; --arco-tramo:.017694turn;
  background:conic-gradient(from var(--arco-desde),
      var(--oro-vivo) 0turn,
      var(--oro) calc(var(--avance) * var(--arco-tramo)),
      rgba(237,239,243,.20) calc(var(--avance) * var(--arco-tramo) + .0001turn),
      rgba(237,239,243,.20) var(--arco-tramo),
      transparent calc(var(--arco-tramo) + .0001turn)) border-box;
  mask:linear-gradient(#000 0 0) padding-box, linear-gradient(#000 0 0);
  -webkit-mask:linear-gradient(#000 0 0) padding-box, linear-gradient(#000 0 0);
  mask-composite:exclude; -webkit-mask-composite:xor;
}
@supports (animation-timeline: scroll()){
  .medidor{ animation-timeline:scroll(root block) }
  @keyframes medir{ from{ --avance:0 } to{ --avance:1 } }
}
/* Sin soporte de scroll-timeline el medidor se quedaría vacío para siempre —
   una línea gris que no dice nada. Mejor no enseñarlo. */
@supports not (animation-timeline: scroll()){ .medidor{ display:none } }
@media (prefers-reduced-motion: reduce){ .medidor{ display:none } }

/* ── APARICIÓN 4 · LA JUNTA ENTRE SECCIONES ──────────────────────────────
   El mismo arco tumbado, marcando dónde acaba una sección y empieza otra.
   Tiene que quedar POR DEBAJO del umbral de ruido tonal que ganó la Capa 1:
   si se nota, sobra. Por eso .09 de alfa y radio grande. */
/* 🔴 LA REGLA DE #sistemas SE COME ESTA. `#sistemas > :not(.placa)` (arriba,
   ~862) tiene especificidad de ID y forzaba `position:relative` sobre la junta:
   quedaba en FLUJO, con ancho 0, y su círculo de 260vw se desbordaba libre —
   141 px de scroll horizontal a 390, con la consola limpia. Es la misma trampa
   que obligó a prefijar toda la sección de Trabajo con `tr-`.
   Por eso la banda se declara TAMBIÉN con el selector de ID. */
.arco-junta,
#sistemas > .arco-junta{
  position:absolute; left:0; right:0; top:0; height:44px;
  overflow:hidden; pointer-events:none;   /* la banda que recorta — ver .medidor */
}
.arco-junta::before{
  content:''; position:absolute; left:50%; top:0; translate:-50% 0;
  width:min(190vw,120rem); aspect-ratio:1; border-radius:50%;
  border-top:1px solid var(--junta-tinta, rgba(237,239,243,.09));
}
/* La junta que precede al cierre lleva el oro: es la última del recorrido y
   cierra el arco que el medidor viene llenando desde arriba. */
.arco-junta--fin{ --junta-tinta:rgba(216,197,140,.26) }

@media (max-width:640px){
  /* mismo motivo que en el respiro: a 390 px el radio en vw deja la curva casi
     recta. Se achica para que siga leyéndose como un cuerpo y no como una raya. */
  .arco-junta::before{ width:260vw }
  /* 600vw a 390 px → r=1170, medio ancho 195 → asin = 9.59°.
     Tramo visible 19.18° = .05328turn, desde 360−9.59 = 350.41°. */
  /* ⚠️ los tokens van en el ::before, NO en .medidor: el pseudo declara los
     suyos en su propio bloque y su valor propio le gana a la herencia. */
  .medidor::before{ width:600vw; --arco-desde:350.41deg; --arco-tramo:.05328turn }
}

/* ══════════════════════════════════════════════════════════════════════════
   CAPA 10 · EL ORO CARGA ESTRUCTURA — ago-2026
   🔴 ESTA CAPA RESTA, NO SUMA. Orden de paco: el oro no debe ganar
      protagonismo. Medido antes de tocar nada (`_tools/sondas/oro.py`): el oro
      ya ocupaba 0.35% de superficie en reposo y 1.0% de media — ya estaba
      contenido. El problema no era CUÁNTO oro había, era DÓNDE.

   La regla que queda: el oro solo aparece donde dice algo que el gris no
   podría decir — posición en una secuencia, avance, o la acción principal.
   Donde solo tintaba un dato, se va.

   Después de esta capa el oro vive en CUATRO sitios y ninguno es adorno:
     · la píldora del CTA          — la acción
     · el medidor del borde        — el avance (Capa 9)
     · los números de secuencia    — la posición
     · la junta que cierra         — el final del recorrido (Capa 9)
   ══════════════════════════════════════════════════════════════════════════ */

/* ── SE VA · el plazo iba tintado sin motivo ─────────────────────────────
   «3 – 5 semanas» y «Mensual» son datos, no jerarquía. Tintarlos es
   exactamente el salto de tono que la Capa 1 quitó de la prosa; aquí seguía
   vivo porque el énfasis era de COLOR y `tono.py` no lo cazaba. */
.oficio__plazo .mono{ color:var(--tx-2) }

/* ── SE VA · rompí mi propia regla en la Capa 8 ──────────────────────────
   Puse «Un responsable.» en oro. La regla de la marca dice que dentro de un
   titular la parte que carga el sentido SUBE DE TONO, no cambia de color
   (`.golpe` → `--tx-1`). El respiro es un titular. Vuelve al tono. */
.respiro__dice b{ color:var(--tx-1) }

/* ── SE QUEDA Y SE UNIFICA · los números de secuencia ────────────────────
   `01`, `01 / 03`, `01 / 05 · los servicios` marcan POSICIÓN: eso es
   estructura, y es lo único que el gris no diría igual. Estaban en tres
   tonos distintos de oro (--oro, --oro-vivo, un #C6A961 cableado). Un solo
   token para los tres, y el más apagado de los dos: es señal, no acento. */
.sis__num,
.respiro__dice::before,
.portal__pest button[aria-selected=true] .pp__n{ color:var(--oro-vivo) }

/* ── SE QUEDA · la cifra del contador ────────────────────────────────────
   Iba a quitarle el oro y me eché para atrás midiendo: `#entrada` es una
   sección de PAPEL y esa cifra usa `--bronce` (#7A5F1F), que ya es el token
   apagado —el oro legible como texto sobre papel, 4.84 de contraste—, no el
   `--oro` brillante. Quitárselo habría sido pasarse de la raya de paco, que
   pidió oro discreto, no menos oro en todas partes.
   Además mi regla no habría ganado: `#entrada .entrada__prueba .cifra` lleva
   especificidad de ID. Se queda como está. */
