/* ==========================================================================
   ZNAPP HOME v10
   Hoja propia y completa. El CSS de Webflow no se carga en esta pagina a
   proposito: sus ~1.400 reglas con especificidad de clase ganaban a cualquier
   override. Las otras paginas del sitio siguen usando Webflow sin cambios.

   ==========================================================================
   QUE CAMBIO EN LA v10 (cuatro cambios, uno estructural)
   ==========================================================================
   A. CAMBIA TODA LA FAMILIA TIPOGRAFICA. Space Grotesk -> ARCHIVO, de
      Omnibus-Type. Variable, un solo eje (peso 100-900), 24 KB. Y con ella
      cambia el TRATAMIENTO, que era la otra mitad de lo "generico": fuera la
      etiqueta en caja alta con tracking abierto encima de cada titulo, fuera
      el letter-spacing negativo por defecto (ahora es una decision por
      tamano, cinco valores) y fuera la formula del titular con una palabra en
      campo de color, que estaba en seis secciones y quedo en una.
      Ver seccion 1 (metrica de la fuente) y seccion 5 (ritmo editorial).
   B. LO QUE SALE DEL CELULAR SE REHIZO ENTERO. Ya no flotan un donut, unas
      barras y una curva: sale el pin de la mision con el nombre del comercio,
      la foto relevada, el precio que esa foto produjo y el hallazgo que ese
      precio produjo, encadenados con el bucle de adentro. Ver seccion 9.7.
   C. LOS BOTONES DE TIENDA, discretos, con el aparato. Ver seccion 9.0.
   D. LA INTEGRACION CON LA SECCION DE ABAJO, sin degradado. Ver seccion 10.

   ==========================================================================
   QUE HABIA CAMBIADO EN LA v9 (sigue vigente)
   ==========================================================================
   1. TITULAR SEGUN LA REFERENCIA. "EL PUNTO DE VENTA / MEDIDO POR /
      PERSONAS." con un tratamiento distinto por palabra: tamano, peso y
      color escalonados. Se hace con font-size, font-weight y color, jamas
      deformando el glifo. Contrastes medidos palabra por palabra:
        EL PUNTO DE VENTA  --text-2    7.16:1
        MEDIDO             --teal-deep 3.50:1  (>=24px: umbral 3:1)
        POR                --text-3    3.71:1  (>=24px: umbral 3:1)
        PERSONAS.          --text     12.95:1
      El teal puro #13c1ac da 2.27:1 y NO se usa como texto en ningun lado.
   2. EL CELULAR VA DE FRENTE. Textual del cliente: "con que este inclinada
      no me entendiste bien". Fuera el rotateY/rotateX y fuera el resorte que
      lo inclinaba. Lo que da la sensacion de realidad aumentada son las
      PIEZAS QUE FLOTAN FUERA DE LA PANTALLA (seccion 9.9): tarjetas de
      grafico, pines de mapa y la tarjeta de validado. El resorte del puntero
      no se tiro: ahora mueve esas piezas por profundidad, que es donde el
      paralaje si construye espacio. El bucle de adentro se conserva entero.
   3. MAS MOVIMIENTO EN TODA LA PAGINA, y continuo o interactivo, no mas
      entradas de scroll: piezas flotantes con desfasajes distintos, paralaje
      de puntero, cadena de eslabones operable, barra de lectura del nav.
   4. HERO Y MECANISMO INTEGRADOS: el titular de abajo retoma la palabra
      "persona" del de arriba, un hilo cruza el borde, el fondo del hero se
      tine de teal antes de llegar y el celular aterriza sobre el campo.
   5. LAS CAPTURAS SE VEN ENTERAS. La v8 se habia ido al otro extremo con
      object-fit: none a escala 1:1 y se veia un fragmento suelto. Ahora el
      marco se sale del wrap para darle ancho de verdad a la captura completa
      (ver .board__ui) y el par de evidencia se reduce a una sola pieza
      grande (ver .act__proof).
   6. "UNA RED SOLA NO ALCANZA" REHECHA. Textual: "no transmite ningun
      mensaje, la paso por alto por las pocas ganas y la fatiga visual".
      Gancho en la primera linea y UN eslabon por vez, operable (seccion 13).
   7. EL CIERRE. El titular sale de la foto y entra en un campo de tinta
      opaco (11.80:1 y 5.70:1 medidos, seccion 17), y el objeto que cruza el
      borde pasa de 90px a 44px y se lee como tarjeta con sombra y forma.
   La seccion .det ("Una foto de gondola y cinco lecturas") NO se toco.

   ==========================================================================
   EL MOTIVO VISUAL DE LA PAGINA: **LA ANOTACION SOBRE LO REAL**
   ==========================================================================
   El reclamo central de la v6 fue "no tiene un hilo de diseno y a medida que
   scrolleas esta todo muy partido". La respuesta no es mas padding: es UN
   sistema grafico, sacado del producto, que se repite con variacion.

   El motivo es lo que la app hace con una gondola: la mira y la ANOTA.
   Tres primitivas, y nada mas que estas tres en toda la pagina:

     1. .anot-box   RECUADRO DE DETECCION. Cuatro escuadras teal y un relleno
                    teal al 10%. Marca "esto es lo que estoy mirando".
     2. .anot-tag   ETIQUETA DE DATO. Chip de tinta en mono, mayusculas, con
                    una linea guia que lo ata a lo que anota. Dice "esto es".
     3. .anot-pill  PILDORA DE VALOR. Campo teal con --on-teal. Es literal del
                    producto: la recompensa aparece asi en la app ($ 2.500,00).

   Donde se repite (siete lugares, el brief pedia cuatro):
     - Hero .......... sobre la captura real de la app: pings de deteccion
                       sobre el mapa, recuadro sobre el comercio elegido,
                       etiqueta de distancia y pildora de recompensa saliendo
                       del marco del celular.
     - Mecanismo ..... etiqueta sobre cada captura de los tres pasos, y
                       recuadro sobre el celular del Znapper en la foto real.
     - Que se obtiene  el motivo en su forma mas literal Y ADEMAS interactivo:
                       el recuadro se mueve sobre una gondola real segun la
                       variable que elijas.
     - Eslabones ..... la escuadra de deteccion hace de vineta en cada eslabon
                       de la cadena.
     - Confianza ..... recuadro + etiqueta sobre la foto del Znapper.
     - Accion ........ etiqueta sobre las dos piezas de evidencia del par
                       "desvio detectado -> comercio por comercio".
     - Cierre/nav .... la pildora de valor es el mismo objeto que el .accent
                       de los titulares y que el boton primario.

   CONTINUIDAD DE SCROLL (lo otro que estaba "partido")
   Cuatro objetos cruzan el borde entre secciones, para que nada empiece ni
   termine en seco:
     a) el celular del hero baja por debajo del hero y aterriza sobre el campo
        teal del mecanismo;
     b) .stitch, un hilo teal con nodo, cruza el borde det -> cadena;
     c) el mismo .stitch cruza cadena -> confianza;
     d) .act__proof nace en la seccion de accion y aterriza sobre el campo
        teal del cierre.

   ==========================================================================
   POLITICA DE LA MONOESPACIADA (v8)
   ==========================================================================
   El cliente noto que la pildora del hero "es otra tipografia y hace ruido".
   Tenia razon, y el problema era mas grande: la mono estaba usada como
   decoracion en media pagina. Ahora la regla es una sola y se puede recitar:

     LA MONO SE USA SOLO DONDE APORTA LECTURA DE DATO.
     Es decir: cifras tabulares, y etiquetas de anotacion apoyadas sobre una
     imagen. En ningun otro lado.

   Con mono quedan:  .stat__n (cifras)  .board__num y .chain__n (ordinales)
                     .anot-tag__box y .anot-box__tag (etiquetas sobre imagen)
                     .anot-pill ($ 2.500,00)  .mcard__k y .mcount (dentro de
                     la captura de la app)
   Pasaron a la familia de texto: la pildora del hero, las etiquetas de
   .board__meta y .trust__tags, el badge del riel, los rotulos de columna del
   pie y las etiquetas de las metricas. Ahora la mono se lee como una decision
   y no como una inconsistencia.
   v10: se sumaron a la lista de mono .dread__v ($ 1.968,94, el precio que
   sale de la foto) porque es cifra tabular, y desaparecieron los eyebrow
   porque desaparecio la clase entera.

   ==========================================================================
   REGLAS DURAS QUE GOBIERNAN LA HOJA
   ==========================================================================
   1. El teal #13c1ac da 2.27:1 sobre blanco. NUNCA es color de texto sobre
      fondo claro. Y TAMPOCO ES FONDO DE TEXTO: ver la regla 1b.
   1b. EL TEAL SE PARTIO EN DOS (v13), Y HOY LOS DOS VALEN LO MISMO (v14b).
      La particion separo dos USOS que vivian en un solo token:
        --teal        ACENTO. Pines, puntos, lineas, rellenos, barras de
                      progreso. Todo lo que es color y NO tiene texto encima.
        --teal-field  CAMPO. Fondo de cualquier bloque CON texto encima.
      Cual usar se decide por una sola pregunta: hay texto encima? Entonces es
      --teal-field. Esa pregunta sigue valiendo y por eso el token sigue.
      LO QUE CAMBIO ES EL VALOR. En la v13 el campo era #0d8375 (blanco encima:
      4.65:1). El cliente comparo contra znapp.la, vio que los bloques grandes
      se veian mas oscuros que la marca, y lo resolvio a favor de la marca:
      HOY --teal-field VALE #13c1ac, igual que --teal, y el blanco encima da
      2.27:1. Es una decision explicita y medida, no un descuido: el bloque de
      abajo, "BLANCO SOBRE EL TEAL DE MARCA", tiene el recorrido entero.
   2. Sobre campo tinta el teal SI es texto: 5.70:1 sobre #34312C.
   3. --teal-deep SE RETIRO EN LA v14 y hoy apunta a --teal: ya no es un color
      propio. Valia #0e9a89 y existia para llegar al 3:1 de elemento grafico.
      Lo unico que de verdad lo necesitaba era el anillo de foco, Y ESE SE FUE
      A LA TINTA. No agregar variantes nuevas del teal.
   3b. SOBRE FONDO OSCURO, EL TEXTO ES BLANCO (v13). El gris calido --on-ink-2
      #C7C0B5 se retiro de los campos de tinta por pedido del cliente. Daba
      7.17:1, o sea que pasaba, pero la jerarquia sobre fondo oscuro no se
      construye bajando el contraste: se construye con tamano y con peso. El
      gris sobre oscuro se lee apagado, no secundario.
   4. Neutros CALIDOS. La tinta sale del logo (#34312C). Ningun gris frio.
   5. Jerarquia por peso, tamano y color. Cero deformacion de letra: ni
      font-stretch, ni text-stroke, ni scale sobre texto, ni calibrado por JS.
      Lo unico que escala tipografia son unidades de contenedor (cqw) dentro
      del celular, que es cambio de font-size, no deformacion del glifo.
   6. Los gradientes cierran en rgba(r,g,b,0) del mismo color, nunca en
      `transparent`: eso interpola por negro y deja un borde sucio.

   CONTRASTES MEDIDOS (formula WCAG sobre los hex reales, ver verify.py)
     --text     #34312C s/blanco      12.95:1
     --text-2   #5C5750 s/blanco       7.16:1   s/--surface-2  6.13:1
     --text-3   #8A847B s/blanco       3.71:1   solo >=18px
     --on-teal  #ffffff s/--teal-field  2.27:1   <- NO PASA AA, y es la
                decision explicita del cliente en la v14b: ver el bloque
                "BLANCO SOBRE EL TEAL DE MARCA" en los tokens
     --teal-field #13c1ac s/blanco      2.27:1   (vale igual que --teal desde
                la v14b; en la v13 era #0d8375 y daba 4.65:1)
     --teal     #13c1ac s/tinta        5.70:1   (acento, sin texto encima)
     --on-ink   #F7F4EF s/tinta       11.80:1   s/--ink-2     14.13:1
     --on-ink-2 = --on-ink desde la v13: sobre oscuro no hay gris
     --teal-deep = --teal desde la v14: no es un color propio

   ==========================================================================
   MOVIMIENTO: EL CRITERIO DE KOWALSKI, APLICADO
   ==========================================================================
   La v6 tenia diez animaciones corriendo y el cliente dijo "no veo
   animaciones": eran TODAS de entrada al scrollear. Se ven una vez y nunca
   mas. Esta version tiene SEIS momentos de movimiento CONTINUO o
   INTERACTIVO, cada uno con un proposito en una frase:

     1. Bucle del celular (hero)  EXPLICAR: ensena el mecanismo completo sin
                                  que nadie lea una palabra. Corre siempre.
     2. Flotacion del aparato     PRESENCIA: el objeto esta vivo, no es una
                                  captura pegada. 6.8s, +-9px, sin rebote.
     3. Lo que sale del celular   EXPLICAR: el pin de la mision, la foto
        (rehecho en la v10)       relevada, el precio que esa foto produjo y
                                  el hallazgo que ese precio produjo, saliendo
                                  del aparato EN ESE ORDEN y encadenados con
                                  lo que pasa dentro de la pantalla. Antes
                                  eran formas de grafico vacias: se movian,
                                  pero no significaban nada, y por eso el
                                  cliente las leyo como estaticas.
     4. Paralaje de puntero (v9)  PROFUNDIDAD: el mismo resorte de
                                  amortiguamiento critico (zeta = 1,
                                  respuesta .45s) integrado por rAF, pero
                                  ahora desplaza las piezas segun su
                                  profundidad en vez de inclinar el celular,
                                  que el cliente pidio de frente. Detras de
                                  (hover:hover) and (pointer:fine).
     5. Tablero de 3 pasos        EXPLICAR + ESTADO: avanza solo y la barra
                                  dice cuanto falta para el paso siguiente.
     6. Riel de rubros            NARRAR: la red esta trabajando en muchos
                                  rubros a la vez, ahora.
     7. Detector de gondola       FEEDBACK + EXPLICAR: el recuadro viaja hasta
                                  la variable que el usuario pidio ver.
     8. Cadena de eslabones (v9)  EXPLICAR + ESTADO: avanza sola y se puede
                                  tomar. Sustituye siete parrafos estaticos.
     9. Barra de lectura (v9)     ORIENTACION: cuanto de la pagina va
                                  recorrido. animation-timeline: scroll(),
                                  sin un solo listener de scroll.

   UNICA EXCEPCION A "SOLO transform Y opacity": el trazo del recorrido del
   hero anima `stroke-dashoffset`. Es la unica forma de DIBUJAR una linea (y
   de mover un punto sobre ella, con un guion redondo de longitud casi cero);
   son tres <path> de un SVG de 226px de ancho, corren una vez por ciclo y no
   provocan layout. Esta documentado aca porque es una excepcion consciente.

   Reglas aplicadas al pie de la letra:
     - Curvas custom. `ease-in` NUNCA: arranca lento justo cuando el ojo mira.
     - Nada arranca en scale(0). El minimo de la hoja es scale(.55) y solo en
       los pings, que son "aterrizajes"; la UI arranca en .97-.98.
     - Todo lo pulsable lleva :active { transform: scale(.97) } a 160ms.
     - UI por debajo de 300ms. Solo el bucle del hero, que es marketing
       explicativo, se toma tiempos largos.
     - TRANSICIONES, no keyframes, para todo lo interrumpible: el bucle del
       hero se maneja con clases y transiciones, asi que se puede cortar y
       reorientar en cualquier frame (los keyframes reinician de cero).
     - Solo `transform` y `opacity`. El recuadro del detector se mueve y
       cambia de tamano con translate + scale, y las escuadras se
       contra-escalan para no deformarse: cero width/height animados.
     - Stagger de 30-80ms. Aca: 70ms.
     - Salidas mas rapidas que entradas (500ms entra, 260ms sale).
     - Hover solo detras de (hover: hover) and (pointer: fine).

   MOVIMIENTO REDUCIDO
   `prefers-reduced-motion` NO apaga todo: apaga el DESPLAZAMIENTO. Se hace
   con un unico multiplicador, --mv, que vale 1 y pasa a 0. Todas las
   distancias de la hoja estan escritas como calc(var(--mv) * Npx), asi que
   con --mv:0 la opacidad, el color y la secuencia siguen ocurriendo y lo
   unico que desaparece es el traslado. Ver seccion 22.
   ========================================================================== */

/* ---------- 1. FUENTES ----------
   v10. CAMBIA TODA LA FAMILIA. Textual del cliente: "la tipografia es muy
   Claude, me gustaria que no se sienta todo tan generico".

   ARCHIVO, de Omnibus-Type (fundidora argentina). Grotesco con caracter
   editorial. El archivo esta subseteado a 24 KB y trae UN SOLO EJE, el de
   peso (100-900): el eje de ancho fue eliminado del binario a proposito, asi
   que la regla de la casa "nunca deformar la letra" queda garantizada por el
   archivo y no por disciplina.

   OJO CON EL PESO POR DEFECTO. La instancia default del archivo es wght 600.
   Si el @font-face no declarara el rango, cualquier texto sin `font-weight`
   explicito se dibujaria en 600. Por eso van las dos cosas juntas:
     (a) `font-weight: 100 900` en el @font-face, que le dice al navegador que
         mapee el valor CSS al eje en vez de usar la instancia default;
     (b) `font-weight` declarado explicitamente en <body> y en cada regla de
         texto de la hoja. Dos redes, no una.

   METRICA MEDIDA CONTRA LA FAMILIA ANTERIOR (fontTools, ver verify.py):
     altura de x    Archivo .526 em   Space Grotesk .486 em   -> +8.2%
     altura de caja Archivo .686 em   Space Grotesk .700 em   -> -2.0%
     avance de CAJA ALTA                                      -> +11 a +15%
   O sea: el CUERPO se lee MAS GRANDE a igual font-size (la altura de x manda
   en la lectura), asi que ningun texto chico quedo corto al cambiar de
   familia. Lo que si cambia es el ancho de la CAJA ALTA, que crece hasta un
   15%: por eso se recalculo la aritmetica del titular del hero (seccion 9). */
@font-face {
  font-family: "Archivo";
  src: url("../fonts/Archivo-Variable.woff2") format("woff2-variations"),
       url("../fonts/Archivo-Variable.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

/* ---------- 2. TOKENS ---------- */
:root {
  color-scheme: light;

  --paper:      #ffffff;
  --surface:    #FAF8F5;
  --surface-2:  #F2EEE8;
  --surface-3:  #E9E4DB;

  --line:       #E5E1DA;
  --line-2:     #D3CDC3;

  /* ACENTO. El teal de marca, muestreado del logo. Pines, puntos, lineas,
     rellenos, barras. Nunca lleva texto encima: para eso esta --teal-field. */
  --teal:       #13c1ac;
  /* CAMPO: el fondo de cualquier bloque teal que lleve texto encima.
     v14: VUELVE A VALER EL TEAL DE MARCA. En la v13 valia #0d8375, un teal mas
     oscuro, y la razon era buena: el cliente habia pedido letra BLANCA sobre
     teal y el blanco sobre #13c1ac da 2.27:1, o sea que no habia forma.
     Despues comparo el sitio contra znapp.la y vio lo que el cambio costaba:
     los botones y las secciones grandes se veian mas oscuras que la marca. Es
     una disyuntiva sin solucion tecnica —no existe un teal que sea #13c1ac y
     lleve letra blanca legible— y la resolvio a favor de la marca.
     EL TOKEN SE CONSERVA aunque hoy valga lo mismo que --teal, y no es
     redundancia: sigue marcando "aca va un campo CON TEXTO ENCIMA", que es lo
     que obliga a mirar el contraste. Si algun dia se vuelve al blanco, se
     cambia este valor y nada mas. */
  --teal-field: #13c1ac;
  /* :hover del primario. Con el campo de vuelta en el teal de marca y el
     texto en tinta, el hover puede volver a ACLARAR: la tinta sobre #17d6bf
     da 9.4:1, mas que sobre el campo en reposo. */
  --teal-hi:    #17d6bf;
  /* v14: --teal-deep DEJA DE SER UN COLOR PROPIO y apunta al de marca.
     Regla del cliente, textual: "siempre el color de marca de Znapp, nunca
     variaciones para nada, excepto para cuando uno se para arriba de algun
     boton o algo asi a modo de animacion".
     Existia porque #13c1ac da 2.27:1 sobre papel y no llega al 3:1 de
     elemento grafico, y #0e9a89 da 3.50:1. Ese umbral aplica a elementos que
     transmiten informacion esencial o a componentes de interfaz; las
     escuadras, los puntos y las lineas del motivo son DECORACION al lado de
     texto que ya dice todo, asi que no lo necesitan.
     Lo que si lo necesitaba era el anillo de foco, y ese se fue a la tinta.
     El token se conserva apuntando a --teal para no reescribir las reglas que
     lo usaban -hoy quedan tres declaraciones que lo leen- y para que quede
     escrito que la variacion existio y por que se retiro. */
  --teal-deep:  var(--teal);
  --teal-tint:  rgba(19,193,172,.12);
  /* ==========================================================================
     BLANCO SOBRE EL TEAL DE MARCA. DECISION EXPLICITA DEL CLIENTE.
     ==========================================================================
     Textual: "no importa lo de la letra blanca, quiero utilizar la letra
     blanca igual sobre el fondo de color znapp... y en todos lados que
     corresponda color de marca znapp usalo. forcemoslo asi".

     QUE IMPLICA, MEDIDO, PARA QUE NADIE LO "ARREGLE" SIN SABER:
       blanco sobre #13c1ac  =  2.27:1
       minimo AA texto normal =  4.5:1
       minimo AA texto grande =  3.0:1
     O sea que el texto sobre campo teal NO PASA AA. No es un descuido ni un
     valor que se escapo: se midio, se le mostro al cliente una comparacion
     lado a lado con los numeros, y eligio la marca.

     El recorrido, para que no se vuelva a discutir tres veces:
       v13  se pidio blanco -> se profundizo el campo a #0d8375 (4.65:1)
       v14  el cliente comparo contra znapp.la y vio el campo mas oscuro ->
            se volvio al teal de marca con texto tinta (7.80:1)
       v14b el cliente pidio blanco IGUAL, sobre el teal de marca -> esto
     Si algun dia se prioriza accesibilidad sobre fidelidad de marca, el unico
     cambio es este token y --teal-field. Todo lo demas ya esta preparado.
     ========================================================================== */
  --on-teal:    #ffffff;

  /* LA JERARQUIA DE TRES NIVELES, EN TINTA SOBRE PAPEL. `--text` es el
     protagonista y el soporte fuerte, `--text-2` el soporte y `--text-3` el
     contexto. `--text-4` es lo mas callado —notas al pie, atribucion— y se
     agrega en la v24 porque hasta ahora todo lo que tenia que susurrar usaba
     `--text-3`, que es el mismo valor que usan los rotulos que SI hay que
     leer: dos roles en un token es como se llega a que nada tenga jerarquia. */
  --text:       #34312C;
  --text-2:     #57524B;                 /* sube de #5C5750: 8,05:1 */
  --text-3:     #837D74;
  --text-4:     #A29B90;


  /* ==================== LA JERARQUIA DE PROTAGONISMO ====================
     TRES NIVELES, Y EN CADA PANTALLA HAY UN SOLO PROTAGONISTA.
     Este es el criterio que faltaba, y su ausencia era el problema: sin el,
     cada pieza se defendia sola y todas terminaban con el mismo peso. Una
     pantalla donde todo resalta es una pantalla donde no resalta nada.

       NIVEL 1 · EL PROTAGONISTA. La unica cosa que la escena vino a mostrar.
         Uno por pantalla, sin excepcion. Contraste maximo, tamaño maximo, y es
         lo unico con derecho a MOVERSE. Va en `--text`, en teal pleno, o es una
         foto a plena luz.
       NIVEL 2 · EL SOPORTE. Lo que hace falta para entender al protagonista:
         el titular, los datos, los rotulos que nombran. Alto pero no maximo, y
         QUIETO. Va en `--text-2`.
       NIVEL 3 · EL CONTEXTO. Lo que da lugar y credibilidad: la ciudad, la
         leyenda, el espinazo, la nota de honestidad. Bajo A PROPOSITO. Nunca
         se mueve y nunca brilla. Va en `--text-3` y, lo mas callado de todo,
         en `--text-4`.

     LOS TRES NIVELES SE ESCRIBIERON CONTRA LA PALETA DE NOCHE de la v22, y esa
     paleta ya no existe: la ruta volvio entera a la luz en la v25 y el sistema
     oscuro se retiro en la v27. Los niveles son los mismos; lo que cambio son
     los tokens con los que se pintan.

     QUIEN ES EL PROTAGONISTA DE CADA ESCENA:
       1 la red        la EVIDENCIA que vuelve (la foto y su tarjeta)
       2 el hallazgo   los 53 puntos, 15 contra 39
       3 la lente      la gondola medida adentro del circulo
       4 la cadena     la barra de siete tramos
       5 el tablero    la captura del producto
       6 la cobertura  la foto del canal
       7 empezar       las tres puertas

     LOS DOS DE ARRIBA SUBEN Y EL DE ABAJO BAJA respecto de la v22, y esa es la
     correccion: antes el secundario y el terciario estaban a .74 y .52, o sea
     demasiado juntos entre si y los dos demasiado cerca del maximo. Tres
     niveles que se parecen no son tres niveles. */

  /* campos de tinta */
  --ink:        #34312C;
  --ink-2:      #26241F;
  --on-ink:     #F7F4EF;
  /* v13: era #C7C0B5, un gris calido. Pedido del cliente: sobre fondo oscuro,
     blanco. Apunta al mismo valor que --on-ink en vez de borrarse, porque el
     token esta en nueve reglas y lo que cambia es el CRITERIO, no cada regla:
     si algun dia vuelve a hacer falta un secundario sobre oscuro, se toca acá
     y no en nueve lugares. Sobre tinta pasa de 7.17:1 a 11.80:1. */
  --on-ink-2:   #F7F4EF;
  --line-ink:   rgba(247,244,239,.16);
  --line-ink-2: rgba(247,244,239,.28);

  --err:        #B03A1E;
  --err-tint:   #FBEFEA;

  --sh-1: 0 1px 2px rgba(52,49,44,.05);
  --sh-2: 0 2px 6px rgba(52,49,44,.05), 0 14px 32px rgba(52,49,44,.08);
  --sh-3: 0 4px 10px rgba(52,49,44,.06), 0 28px 60px rgba(52,49,44,.14);

  /* UN SOLO SISTEMA DE RADIOS, de mayor a menor:
     interactivo pill, superficie 20, media 16, campo 12, y la escala de
     anotacion 9/6 que corresponde a piezas de 20-30px de alto. */
  /* la altura real de la barra pegada. Las escenas la descuentan de su
     relleno superior: sin esto el titular de cada pantalla queda abajo del
     logo. Medida sobre el DOM, no estimada. */
  --navh: 64px;

  --r-pill: 999px;
  --r-surface: 20px;
  --r-media: 16px;
  --r-input: 12px;
  --r-anot: 9px;
  --r-anot-2: 6px;

  /* ==========================================================================
     EL MARGEN LATERAL: EL CRITERIO DE CABIFY (v13)
     ==========================================================================
     La ronda dio tres vueltas con esto (ensanchar, volver, +25%) hasta que el
     cliente eligio una referencia concreta: cabify.com. Se MIDIO el sitio real
     en el navegador, no se estimo. Lo que usan:

       .g-container   --grid-container   el contenido normal

       > 1540px    --grid-container: 1200px          12 columnas
       <= 1540px   --grid-container:  896px          12 columnas
       <= 1024px   --grid-container:  576px           6 columnas
       <=  767px   --grid-container: calc(100% - 64px)  2 columnas

     DOS COSAS DEL CRITERIO, Y LA SEGUNDA IMPORTA MAS QUE LA PRIMERA:

     1. NO ES UN clamp CONTINUO, SON ESCALONES. El contenedor tiene cuatro
        anchos fijos y el margen es lo que sobra. Un clamp reparte el cambio
        de forma pareja; los escalones mantienen la MEDIDA DE LECTURA quieta
        dentro de cada banda y hacen todo el ajuste de golpe al cambiar de
        banda. Es mas facil de razonar y no deja anchos intermedios raros.

     2. EL CONTENEDOR ES UN MAXIMO DURO. Cabify tiene ademas un contenedor
        "full" de 1824px; aca NO se adopto, por decision explicita del
        cliente: "no se puede sobrepasar ese maximo, si se puede usar menos
        pero nunca pasarlo". Un componente puede ser mas angosto que --wrap
        (el texto de los pasos lo es), nunca mas ancho.

     El gutter es 0 arriba de 767px A PROPOSITO: en Cabify el margen sale
     puramente del centrado, no de un padding. Medido en su home a 1920, el
     borde de contenido cae en 353px, que es (1905 - 1200) / 2 exacto. Por
     debajo de 767 su contenedor es calc(100% - 64px), que es lo mismo que
     decir 32px de margen: eso aca se escribe como --gut: 32px.
     ========================================================================== */
  --wrap: 1200px;
  --gut: 0px;
  /* EL MARGEN LATERAL, una sola vez. Reproduce el borde de contenido de .wrap
     para cualquier bloque a sangre completa. Ver el comentario largo en la
     seccion 4 de esta hoja. */
  --margen: max(var(--gut), calc((100% - var(--wrap)) / 2 + var(--gut)));
  /* LA ALTURA DE LA BARRA, Y POR QUE SON DOS TOKENS Y NO UNO.
     `--nav-h` es lo que la barra MIDE: se la fija esta hoja y de aca sale.
     `--navh` es lo que el resto de la pagina DESCUENTA por ella, y lo reescribe
     el script con la altura real medida del DOM.
     Parecen el mismo numero y no se pueden unificar: si la barra tomara su alto
     de `--navh`, y `--navh` saliera de medir la barra, cada uno dependeria del
     otro. Se intento y la barra se desplomo a 45px, que es lo que mide su
     contenido: la escena 1 crecio 24px y todos los puntos de encaje se
     corrieron. Los dos tokens son la forma de cortar ese lazo. */
  --nav-h: 68px;

  --sec-pt: clamp(72px, 8vw, 112px);
  --sec-pb: clamp(48px, 5.5vw, 80px);

  /* curvas fuertes. ease-in nunca. */
  --ease:    cubic-bezier(.23, 1, .32, 1);      /* entradas y UI */
  --ease-io: cubic-bezier(.77, 0, .175, 1);     /* movimiento en pantalla */
  /* v22: LA CURVA DE LAS ENTRADAS GRANDES. Es exponencial de salida: arranca
     mas rapido que `--ease` y frena mucho mas largo. La diferencia se siente
     justo donde importa acá —una pieza que entra a una escena de pantalla
     completa— porque el final largo lee como que la pieza SE APOYA en vez de
     llegar. Es la misma familia que `--ease`, un escalon mas marcada; no se
     reemplaza la de UI, que a 200 ms con esta curva se siente floja. */
  --ease-exp: cubic-bezier(.16, 1, .3, 1);
  --dur: .28s;

  /* ALIAS QUE FALTABAN, Y NO ERAN COSMETICOS.
     `--ease-out` se usaba en DIECIOCHO declaraciones de esta hoja y no estaba
     definido en ninguna parte. Un `var()` sin valor no cae al valor por
     defecto de la propiedad: invalida la declaracion ENTERA en tiempo de
     computo, asi que esas dieciocho `transition` y `animation` no existian.
     Media escena de cobertura estaba escrita y no corria: el subrayado reloj
     del selector, la entrada de cada panel y el resaltado de `.lnk`.
     Lo mismo con `--tr-h1/h2/h3`, que se usan en nueve reglas de titular: sin
     valor, `letter-spacing` quedaba heredado y los titulares grandes perdian
     el ajuste optico entero.
     SE DEFINEN COMO ALIAS Y NO COMO VALORES NUEVOS: los nombres buenos son
     `--ease` y la escala `--tr-*`, y lo que hubo fue un renombre a medio
     terminar. Un alias deja el arreglo en un solo lugar y no reescribe
     veintisiete reglas. */
  --ease-out: var(--ease);
  --tr-h1: var(--tr-display);
  --tr-h2: var(--tr-title);
  --tr-h3: var(--tr-sub);

  /* --- MULTIPLICADOR DE MOVIMIENTO ---
     Vale 1 normalmente y 0 con prefers-reduced-motion. Toda distancia de
     traslado de la hoja pasa por aca. */
  --mv: 1;

  --font: "Archivo", "Segoe UI", system-ui, -apple-system, sans-serif;
  --mono: "JetBrains Mono", ui-monospace, "SFMono-Regular", Menlo, monospace;

  /* --- TRACKING POR TAMANO, NO POR DEFECTO (v10) ---
     El tic que el cliente llamo "muy Claude" no era solo la familia: era
     aplicarle letter-spacing negativo a TODO. El tracking optico depende del
     tamano y de nada mas, asi que la hoja tiene cinco valores y cada regla
     elige el suyo. Por encima de 48px la letra se cierra; el cuerpo va en
     cero; por debajo de 14px se ABRE, que es lo contrario de lo que hacia la
     version anterior. */
  --tr-display: -.03em;    /* >= 48px */
  --tr-title:   -.018em;   /* 28-48px */
  --tr-sub:     -.008em;   /* 19-28px */
  --tr-body:     0em;      /* 15-19px: el cuerpo no se toca */
  --tr-micro:    .012em;   /* <= 14px: se abre, no se cierra */
}

/* --- LOS ESCALONES DEL CONTENEDOR (criterio Cabify, medido en su sitio) ---
   Van ACA, pegados a :root, y no repartidos entre las media queries del final
   de la hoja: es una sola decision de layout y tiene que leerse de una sola
   vez. Cada escalon cambia UNICAMENTE el ancho del contenedor; todo lo demas
   de la pagina se acomoda solo, porque el margen es lo que sobra.

   El escalon de 1024 es el mas fuerte (896 -> 576) y en Cabify viene junto con
   un cambio de 12 a 6 columnas de grilla. Aca no hay grilla de columnas, asi
   que se toma el ancho tal cual y lo que cambia es cuanto respira el
   contenido. Verificado a 1024: sin desborde y sin fallos de contraste. */
/* v26: FALTABA UN ESCALON, Y SE LO COMIAN LOS PORTATILES.
   El salto de 1200 a 896 caia en 1540, asi que 1440, 1512 y 1536 —o sea las
   tres resoluciones de MacBook— aterrizaban todas en el escalon angosto: el
   contenido ocupaba el 59% del ancho en 1512 y el 62% en 1440, con 300px de
   margen muerto de cada lado.
   EN CABIFY ESO NO MOLESTA Y ACA SI, y la diferencia es la regla de esta ruta:
   una escena es UNA PANTALLA. El ancho que sobra no queda en blanco al costado,
   se paga en ALTO —las piezas se apilan y la escena llena el 46% de la
   pantalla—. El escalon de 1040 devuelve ese ancho sin tocar el maximo duro de
   1200, que es lo que la decision del cliente fija. */
@media (max-width: 1540px) { :root { --wrap: 1040px; } }
@media (max-width: 1365px) { :root { --wrap: 896px; } }
@media (max-width: 1024px) { :root { --wrap: 576px; } }
/* por debajo de 767 el contenedor es todo el ancho menos 64px, o sea 32px de
   margen de cada lado. Se escribe con el gutter porque asi lo heredan tambien
   los bloques a sangre completa, que calculan su margen desde --gut. */
@media (max-width: 767px)  { :root { --wrap: 100%; --gut: 32px; } }

/* ---------- 3. RESET ---------- */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }

html {
  -webkit-text-size-adjust: 100%;
  /* SIN `scroll-behavior: smooth`. Estaba declarado aca Y otra vez en el bloque
     del encaje, y las dos veces por el mismo motivo (que un salto con ancla no
     sea un corte seco) y con el mismo efecto colateral: con `scroll-snap-type:
     mandatory`, el re-encaje del navegador hereda el suavizado y la pagina
     tarda en asentarse despues de cada gesto. El salto suave ahora lo hace
     `anclasSuaves()` en el script, por gesto. Ver el bloque del encaje. */
  scroll-padding-top: calc(var(--navh, 64px) + 16px);
}

body {
  background: var(--paper);
  color: var(--text);
  font-family: var(--font);
  /* Archivo tiene la altura de x un 8.2% mas alta que Space Grotesk, asi que
     a igual font-size el cuerpo se lee MAS grande. 17px se conserva: bajarlo
     seria aprovechar la altura de x para achicar, que es justo lo contrario
     de lo que pide una lectura comoda. */
  font-size: 17px;
  line-height: 1.6;
  font-weight: 400;              /* explicito: la instancia default es 600 */
  letter-spacing: var(--tr-body);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: clip;
}

img, svg, video { display: block; max-width: 100%; }
img { height: auto; }
button, input, textarea, select { font: inherit; color: inherit; }
button { background: none; border: 0; cursor: pointer; }
a { color: inherit; text-decoration: none; }
ul, ol { list-style: none; padding: 0; }

/* el anillo de foco es elemento grafico: necesita 3:1. --teal da 2.27:1 sobre
   papel, --teal-deep da 3.50:1. Sobre campo tinta el teal da 5.70:1. */
/* v14: EL ANILLO DE FOCO PASA A TINTA.
   Regla nueva del cliente: nunca variaciones del teal de marca, salvo en
   hover. El anillo estaba en --teal-deep justamente porque el teal de marca
   da 2.27:1 sobre papel y un foco que no se ve deja sin navegar por teclado a
   quien depende de el. La salida no es desobedecer la regla: es que el foco
   NO SEA TEAL. La tinta da 12.95:1 y no es una variacion de nada. */
:focus-visible { outline: 2px solid var(--text); outline-offset: 3px; border-radius: 4px; }
.oninkfield :focus-visible { outline-color: var(--teal); }
.ontealfield :focus-visible { outline-color: var(--on-teal); }

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

.skip-link {
  position: fixed; top: 8px; left: 8px; z-index: 200;
  transform: translateY(-200%);
  background: var(--ink); color: var(--on-ink);
  padding: 10px 18px; border-radius: var(--r-pill); font-weight: 600;
}
.skip-link:focus { transform: none; }

#top-sentinel { position: absolute; top: 0; left: 0; width: 1px; height: 1px; }

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

/* --- EL MARGEN LATERAL, UNA SOLA DEFINICION (v13) ---
   Ver la explicacion completa arriba, en :root. Aca vive la EXPRESION, que es
   el borde de contenido de .wrap reproducido para un bloque a sangre completa:
       (ancho del padre - wrap) / 2  +  gutter
   Se declara como CUSTOM PROPERTY y no como clase, y la razon es concreta: la
   usan cuatro lugares en TRES propiedades distintas (padding-inline del menu
   movil, padding del riel, left del hilo del hero). Una clase puede dar
   padding; no puede dar `left`. Como token sirve a las tres.
   El `100%` se resuelve en el elemento que la usa, no aca: los custom
   properties se sustituyen como texto y el porcentaje se calcula recien en la
   propiedad final. Por eso funciona igual en un padding que en un left.
   Y se usa `100%` y no `100vw`: 100vw incluye la barra de scroll y .wrap se
   centra SIN ella, asi que con vw todo quedaria medio scrollbar corrido. */

/* ---------- 4. LOS DOS CAMPOS PLENOS ---------- */
.oninkfield  { background: var(--ink);  color: var(--on-ink); }
.ontealfield { background: var(--teal-field); color: var(--on-teal); }

/* El acento del titular. Es la MISMA pieza que .anot-pill: campo teal con
   texto encima, que es el codigo exacto de la tarjeta fisica de Znapp.
   v13: el fondo pasa a --teal-field porque LLEVA TEXTO. Con el acento de
   marca y el blanco nuevo daria 2.27:1. */
.accent {
  display: inline-block;
  background: var(--teal-field);
  color: var(--on-teal);
  line-height: 1;
  padding: .09em .16em .12em;
  border-radius: var(--r-input);
  -webkit-box-decoration-break: clone;
          box-decoration-break: clone;
}
/* invertida sobre el campo teal: barra de TINTA con el teal como texto, 8.35:1.
   Se fija en --ink y no en var(--on-teal) porque ese token cambio de valor dos
   veces en dos rondas y esta pieza necesita fondo oscuro siempre. */
.ontealfield .accent { background: var(--ink); color: var(--teal); }

.oninkfield .lead,  .oninkfield p  { color: var(--on-ink-2); }
.ontealfield .lead, .ontealfield p { color: var(--on-teal); }
.oninkfield .h2, .oninkfield .h3, .ontealfield .h2, .ontealfield .h3 { color: inherit; }

/* ==========================================================================
   5. TIPOGRAFIA: RITMO EDITORIAL
   ==========================================================================
   v10. El cliente pidio dos cosas a la vez: otra familia Y otro tratamiento.
   Estos eran los tres tics que se leian como plantilla, y como se atacaron:

   1. LA ETIQUETA EN MAYUSCULA CON TRACKING ABIERTO ENCIMA DE CADA TITULO.
      Era la marca registrada del sitio generado. Se elimino la clase
      .eyebrow entera y con ella el rotulo "POR QUE CONFIAR". Queda UN solo
      rotulo de seccion en toda la pagina (.board__k), en caja baja, con
      tracking cero, y solo porque dice algo que el titular no dice: que lo
      que sigue es lo que se acaba de ver arriba.

   2. LETTER-SPACING NEGATIVO EN TODO. Ahora es una decision por tamano, con
      cinco valores (--tr-display .. --tr-micro). Por debajo de 14px el
      tracking es POSITIVO, que es lo que pide la optica y lo contrario de lo
      que hacia la version anterior.

   3. EL TITULAR CON UNA PALABRA EN CAMPO DE COLOR, REPETIDO EN CADA SECCION.
      Estaba en seis titulares y se habia vuelto formula. Queda en UNO solo,
      el de "Una foto de gondola y cinco lecturas", que es la seccion que el
      cliente pidio no tocar. Los otros cinco pasaron a jerarquia por PESO:
      la primera linea en 300 y --text-2, el remate en 700 y tinta plena.
      Con Archivo eso es un contraste real, porque el eje llega a 100.

   La escala tiene TRES escalones de titular y no uno solo, que es la otra
   mitad del ritmo editorial: --xl para los dos pilares (el mecanismo y la
   accion), el tamano base para las secciones de argumento, y --sm para las
   de servicio (FAQ). Antes todos los h2 median lo mismo.
   ========================================================================== */
.h2 {
  font-size: clamp(30px, 3.7vw, 46px);
  line-height: 1.04;
  letter-spacing: var(--tr-display);
  font-weight: 700;
}
/* pilares: el mecanismo y el cierre del arco */
.h2--xl { font-size: clamp(34px, 4.9vw, 64px); line-height: 1.0; }
/* La escala de titulares en uso, de mayor a menor, y cada escalon dice algo:
     72px  .finale__h   el cierre, que es donde termina el arco
     64px  .h2--xl      los pilares: el mecanismo, la cadena, la confianza, la accion
     46px  .h2          las secciones de argumento y de servicio
   La v10 habia bajado la FAQ a un cuarto escalon de 34px y se volvio
   invisible: el cliente la pasaba de largo. Volvio a 46px. */

/* LA PRIMERA LINEA, EN PESO FINO.
   Sustituye al campo de color como recurso de jerarquia. El contraste no se
   toca: --text-2 da 7.16:1 y el texto es grande. El peso 300 existe de
   verdad porque el eje de Archivo llega a 100; con la familia anterior, que
   arrancaba en 300 y pesaba distinto, este contraste no era posible. */
.h2__soft { font-weight: 300; color: var(--text-2); }

/* --- EL SUBRAYADO DEL REMATE (v10.2) ---
   No es adorno: es el senalador que le faltaba a la FAQ para que el ojo se
   detenga. Se dibuja de izquierda a derecha cuando el titular entra, o sea que
   se ve APARECER en vez de estar puesto: eso es lo que llama la atencion.
   Va en --teal-deep, que sobre --surface da 3.30:1, el umbral de elemento
   grafico; el teal puro daria 2.24:1 y no pasaria ni como linea.
   El grosor y la separacion van en `em`, asi que acompanan al tamano del
   titular en cualquier ancho sin recalcular nada.
   Con movimiento reducido `scaleX(1 - 0)` vale 1: la linea esta desde el
   principio y lo unico que se pierde es el trazo. */
.uline { position: relative; display: inline-block; }
.uline::after {
  content: ""; position: absolute;
  left: 0; right: 0; bottom: -.04em;
  height: .075em; border-radius: 999px;
  background: var(--teal);
  transform: scaleX(calc(1 - var(--mv)));
  transform-origin: left center;
  transition: transform .62s var(--ease-io) .2s;
}
.reveal.is-in .uline::after { transform: scaleX(1); }
.oninkfield  .h2__soft { color: var(--on-ink-2); }   /* 7.17:1 */
.ontealfield .h2__soft { color: var(--on-teal); }    /* 7.80:1 */

.h3 {
  font-size: clamp(20px, 1.9vw, 26px);
  line-height: 1.2;
  letter-spacing: var(--tr-sub);
  font-weight: 600;
}
.lead {
  color: var(--text-2);
  font-size: clamp(16.5px, 1.4vw, 19.5px);
  font-weight: 400;
  line-height: 1.55;
  letter-spacing: var(--tr-body);
  max-width: 58ch;
  margin-top: 20px;
}

/* --- LA PALABRA QUE CARGA EL SIGNIFICADO ---
   Herramienta principal de jerarquia dentro de un parrafo: peso + tinta
   plena contra el resto del texto, que vive en --text-2. Una por bloque y
   nada mas: si todo esta resaltado, nada lo esta. Cero deformacion: no se
   cambia el ancho del glifo, solo su peso y su color. */
.key { font-weight: 600; color: var(--text); }
.oninkfield  .key { color: var(--on-ink); }        /* 11.80:1 sobre tinta */
.ontealfield .key { color: var(--on-teal); font-weight: 700; }

/* LAS CIFRAS SOBRE CAMPO TINTA.
   Esto NO es un parche del hero de znappers: es un agujero del sistema que
   aparecio recien cuando algo puso cifras sobre tinta por primera vez.
   `.hero__fign` va en `--text`, que es #34312C, y `--ink` es #34312C: el mismo
   color. Medido sobre el DOM daba 1.00:1 y en pantalla no habia numero, solo
   el "+" teal flotando arriba de su etiqueta.
   La linea separadora tenia el mismo problema al reves: `--line` es una linea
   de papel y sobre tinta se veia como un trazo blanco duro. */
.oninkfield .hero__fign { color: var(--on-ink); }     /* 11.80:1 */
.oninkfield .hero__figl { color: var(--on-ink-2); }   /* 11.80:1 */

/* ==========================================================================
   6. EL MOTIVO: LAS TRES PRIMITIVAS DE ANOTACION
   Se definen una sola vez y se reutilizan en las seis secciones. Cambian de
   posicion y de escala, nunca de lenguaje.
   ========================================================================== */

/* --- 6.1 RECUADRO DE DETECCION --- */
.anot-box {
  position: absolute;
  pointer-events: none;
  background: rgba(19,193,172,.10);
  border-radius: var(--r-anot-2);
}
.anot-box__c {
  position: absolute;
  width: 15px; height: 15px;
  border: 2px solid var(--teal);
}
.anot-box__c--tl { top: 0; left: 0;  border-right: 0; border-bottom: 0; border-radius: 5px 0 0 0; transform-origin: 0 0; }
.anot-box__c--tr { top: 0; right: 0; border-left: 0;  border-bottom: 0; border-radius: 0 5px 0 0; transform-origin: 100% 0; }
.anot-box__c--bl { bottom: 0; left: 0;  border-right: 0; border-top: 0; border-radius: 0 0 0 5px; transform-origin: 0 100%; }
.anot-box__c--br { bottom: 0; right: 0; border-left: 0;  border-top: 0; border-radius: 0 0 5px 0; transform-origin: 100% 100%; }

/* --- 6.2 ETIQUETA DE DATO --- */
.anot-tag { position: absolute; display: flex; align-items: center; pointer-events: none; z-index: 3; }
.anot-tag__box {
  display: inline-flex; align-items: center; gap: 6px;
  min-width: 0;
  padding: 6px 10px;
  border-radius: var(--r-anot);
  background: var(--ink);          /* --on-ink encima: 11.80:1 */
  color: var(--on-ink);
  font-family: var(--mono); font-size: 10.5px; font-weight: 500;
  line-height: 1.25; letter-spacing: .07em; text-transform: uppercase;
  box-shadow: 0 2px 6px rgba(26,24,21,.16), 0 10px 24px rgba(26,24,21,.14);
}
.anot-tag__box--ok { background: var(--teal-field); color: var(--on-teal); }  /* 7.80:1 */
.anot-tag__box svg { flex: none; }
/* la linea guia ata la etiqueta a lo que anota. --teal-deep: 3.50:1 sobre
   papel, que es el umbral de elemento grafico. */
.anot-tag__line { flex: none; width: 22px; height: 1.5px; background: var(--teal); border-radius: 1px; }

/* --- 6.3 PILDORA DE VALOR --- */
.anot-pill {
  display: inline-flex; align-items: center;
  padding: 7px 14px;
  border-radius: var(--r-pill);
  background: var(--teal-field); color: var(--on-teal);   /* 7.80:1 */
  font-family: var(--mono); font-size: 12.5px; font-weight: 700;
  letter-spacing: -.01em; line-height: 1.2; white-space: nowrap;
  box-shadow: 0 2px 6px rgba(26,24,21,.14), 0 12px 26px rgba(26,24,21,.16);
}

/* --- 6.4 la etiqueta apoyada sobre una imagen ---
   No entra junto con la foto: cae 90ms despues. Primero llega la evidencia,
   despues se la anota. Ese medio latido es el motivo hecho movimiento. */
.anot-tag--onmedia {
  left: 14px; bottom: 14px;
  opacity: 0;
  transform: translateY(calc(var(--mv) * 8px));
  transition: opacity .32s var(--ease) .09s, transform .32s var(--ease) .09s;
}
.board__panel.is-active .anot-tag--onmedia,
.act__proof.is-in .anot-tag--onmedia,
/* LA ESCENA DE COBERTURA FALTABA EN ESTA LISTA, y esa etiqueta es justo la que
   sostiene la regla de honestidad de la escena: no hay una foto verificada de
   cada canal, asi que la foto no dice "esto es una farmacia", dice QUE ES
   ("Captura de la red · Góndola de cuidado personal"). Con `opacity: 0` y
   ningun selector que la encendiera, las seis etiquetas nunca se vieron: la
   foto quedaba muda abajo del rotulo de un canal, que es exactamente lo que el
   comentario del HTML dice que hay que evitar.
   No hace falta condicionarla a un estado: el panel del canal que no esta
   elegido va con `hidden` y no se pinta. */
.cov__ph .anot-tag--onmedia,
.trust__ph.is-in .anot-tag--onmedia { opacity: 1; transform: none; }
/* la segunda pieza del par se anota medio latido despues que la primera */
.act__proof.is-in .act__pf--ev .anot-tag--onmedia { transition-delay: .16s; }

/* ---------- 7. BOTONES ---------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center;
  gap: 10px;
  height: 54px;
  padding: 0 28px;
  border-radius: var(--r-pill);
  font-size: 16px; font-weight: 600; letter-spacing: var(--tr-body);
  white-space: nowrap;
  transition: transform .16s var(--ease), background-color var(--dur) var(--ease),
              border-color var(--dur) var(--ease), color var(--dur) var(--ease),
              box-shadow var(--dur) var(--ease);
}
.btn--sm { height: 44px; padding: 0 20px; font-size: 15px; }
.btn--lg { height: 60px; padding: 0 34px; font-size: 17px; }
/* NO HABIA REGLA PARA `:disabled` Y HAY DOS BOTONES QUE LO USAN. El del modal
   queda deshabilitado despues de armar el mensaje, y sin esto sigue pintando
   como un primario vivo: dice "toca aca" cuando ya no hay nada que tocar. */
.btn:disabled { opacity: .55; cursor: default; box-shadow: none; }
.btn:disabled:hover { transform: none; }

/* v10.1: LOS DOS BOTONES TENIAN EL MISMO PROBLEMA, APAGADOS Y GENERICOS.
   El primario era un campo teal plano con la sombra mas chica de la hoja, o
   sea que no se despegaba del papel. El secundario era un borde gris de 1px
   sobre blanco: el aspecto exacto de un boton sin decidir.

   Ahora forman un PAR con dos voces distintas y las dos firmes:
     primario    campo teal, peso 700, y una sombra TENIDA DE TEAL, no gris.
                 Un objeto de color proyecta sombra de su color; con sombra
                 neutra se lee pegado. Ese halo es lo que lo levanta.
     secundario  contorno de TINTA de 1.5px, no un gris de 1px. Al pasar por
                 encima se rellena de tinta entero, o sea que el hover no es
                 un cambio de matiz sino una inversion: el objeto responde.
   v13: EL PRIMARIO ES EL BOTON QUE EL CLIENTE NOMBRO. Pasa a letra blanca
   sobre --teal-field, que entonces era #0d8375 y daba 4.65:1.
   v14b: EL CAMPO VOLVIO AL TEAL DE MARCA, asi que el rotulo blanco de este
   boton da 2.27:1 y NO PASA AA. Esta medido y elegido: el bloque "BLANCO
   SOBRE EL TEAL DE MARCA" en los tokens tiene el recorrido. No "arreglarlo"
   oscureciendo el campo, que es de donde se vuelve cada vez.
   El brillo interior se quedo en .18: sobre el campo mas oscuro de la v13 una
   linea blanca al 34% se leia como un borde despegado en vez de como luz.
   Contrastes: --on-teal sobre --teal-field 2.27:1; --text sobre papel
   12.95:1; al rellenarse, --on-ink sobre tinta 11.80:1. */
.btn--primary {
  background: var(--teal-field);
  color: var(--on-teal);
  font-weight: 700;
  box-shadow: inset 0 1px 0 rgba(255,255,255,.22),
              0 1px 2px rgba(14,154,137,.30),
              0 8px 20px rgba(14,154,137,.26);
}
.btn--ghost {
  border: 1.5px solid var(--text);
  color: var(--text);
  background: rgba(255,255,255,0);
}

/* el primario sobre campo teal se invierte a TINTA con el rotulo en teal:
   8.35:1, y es la unica combinacion legible que no sale de la paleta de marca.
   Fondo fijo en --ink por la misma razon que .accent. */
.ontealfield .btn--primary { background: var(--ink); color: var(--teal); box-shadow: inset 0 1px 0 rgba(247,244,239,.10), var(--sh-2); }
.ontealfield .btn--ghost { border-color: var(--on-teal); color: var(--on-teal); background: rgba(52,49,44,0); }
.oninkfield .btn--ghost { border-color: var(--on-ink); color: var(--on-ink); background: rgba(247,244,239,0); }

.btn__ico { display: grid; place-items: center; transition: transform .26s var(--ease); }

/* TODO EL HOVER DE LA PAGINA VIVE ACA DENTRO.
   En pantalla tactil un tap dispara :hover y lo deja pegado: el riel de
   rubros se congelaba para siempre y las tarjetas quedaban levantadas.
   Un solo bloque, una sola regla que recordar. */
@media (hover: hover) and (pointer: fine) {
  .btn:hover .btn__ico { transform: translate(calc(var(--mv) * 3px), calc(var(--mv) * -3px)); }
  /* el badge vive al 80% para no competir con el CTA primario; al pasar por
     encima sube a opacidad plena. Es la unica diferencia de estado que
     necesita, porque es un enlace, no un boton. */
  .appdl__b:hover { opacity: 1; }
  .btn--primary:hover { background: var(--teal-hi); box-shadow: inset 0 1px 0 rgba(255,255,255,.34), 0 2px 4px rgba(14,154,137,.32), 0 12px 26px rgba(14,154,137,.32); }
  /* el secundario no cambia de matiz: se INVIERTE. Campo de tinta con el
     texto claro encima, 11.80:1. */
  .btn--ghost:hover { background: var(--ink); border-color: var(--ink); color: var(--on-ink); }
  .ontealfield .btn--primary:hover { background: var(--ink); }
  .ontealfield .btn--ghost:hover { background: var(--ink); color: var(--teal); }
  .oninkfield .btn--ghost:hover { background: var(--on-ink); border-color: var(--on-ink); color: var(--ink); }
  /* el fondo lo pone el pulgar que viaja: aca solo se oscurece el rotulo, que
     es lo que confirma cual de los tres esta senalado */
  .nav__links a:hover { color: var(--text); }
  /* el hover profundiza. Aclarar le bajaria el contraste al rotulo justo
     cuando el usuario esta apuntando la pastilla. */
  .board__tab:hover { background: rgba(26,24,21,.28); }
  /* v13: la captura responde al puntero. La etiqueta de anotacion se separa
     apenas del borde y la sombra crece: es la invitacion minima a mirarla de
     cerca, sin prometer un click que no existe. Solo transform y opacidad. */
  .board__media:hover { box-shadow: var(--sh-2); }
  .board__media:hover .anot-tag__box {
    transform: translate(calc(var(--mv) * 3px), calc(var(--mv) * -3px));
  }
  .board__well:hover::before {
    border-color: var(--teal);
  }
  .det__opt:hover { border-color: var(--line-2); background: var(--surface-2); }
  /* el paso levanta apenas y su escuadra se dibuja: confirma que hay algo
     ahi sin prometer un click, porque no lo hay */
  .acc__q:hover { color: var(--text); }
  .chan:hover { border-bottom-color: var(--text); }
  .modal__close:hover { color: var(--text); border-color: var(--text-2); }
  /* sobre campo teal el hover se hace con TINTA, nunca con mas teal: el
     teal-sobre-teal no se distingue y el on-teal ya es el color del bloque */
  /* v10.1: estos cuatro hover venian de cuando el cierre era campo TEAL y
     pintaban con --on-teal, que es casi negro. Sobre el campo de tinta de
     ahora, un subrayado casi negro sobre fondo casi negro no se ve: el hover
     existia y no se notaba. Pasan a --on-ink. */
  .finale__col a:hover, .finale__big:hover { border-bottom-color: var(--on-ink); }
  .finale__mini a:hover { border-bottom-color: var(--on-ink); }
  .finale__social a:hover { border-color: var(--on-ink); background: rgba(247,244,239,.10); }
  .finale__quiet:hover { border-bottom-color: var(--on-ink); }
  .finale .chan:hover { border-bottom-color: var(--on-ink); }

  /* los canales de ayuda.html levantan al apuntar. Las dos "puertas" que
     compartian esta regla se retiraron en la v26: prometian separar los dos
     publicos y lo unico que hacian era scrollear. */
  .cnl__c:hover { transform: translateY(calc(var(--mv) * -3px)); }

  /* --- v13.2: EL RECORRIDO DE LA EVIDENCIA RESPONDE AL PUNTERO ---
     Es lo que el cliente pidio con "que se sienta vivo... casi invitandote a
     interactuar con pequenas cosas". Son cuatro cosas a la vez y ninguna
     promete un click, porque no hay ninguno que dar:
       el paso levanta y se le pinta el borde
       su escuadra pasa a teal y se corre en diagonal
       LA TARJETA DE EVIDENCIA sube un poco mas que el paso, asi que se
         despega: se lee como un objeto apoyado, no como parte del fondo
       la flecha que lleva al paso siguiente se pinta y avanza
     Esa ultima es la que hace el trabajo: al apuntar un paso, la figura te
     muestra a donde va. */
  .flow__s:hover {
    transform: translateY(calc(var(--mv) * -4px));
    box-shadow: var(--sh-2);
    border-color: var(--line-2);
  }
  .flow__s:hover::before {
    border-color: var(--teal);
    transform: translate(calc(var(--mv) * -3px), calc(var(--mv) * -3px));
  }
  .flow__s:hover::after {
    border-color: var(--teal);
    transform: translate(calc(50% + var(--mv) * 4px), -50%) rotate(45deg);
  }
  .flow__s:hover .ev {
    transform: translateY(calc(var(--mv) * -3px));
    box-shadow: var(--sh-2);
  }
  /* y los cuatro partners responden uno por uno: es la lista de a quien le
     podes reclamar, asi que tiene sentido que se puedan recorrer con el ojo */
  .flow__who li:hover { color: var(--text); transform: translateX(calc(var(--mv) * 3px)); }
  .flow__who li:hover::before { transform: translate(calc(var(--mv) * -2px), calc(var(--mv) * -2px)); }

  /* --- v13.6: LAS RUTAS RESPONDEN AL PUNTERO CON EL MISMO GESTO ---
     Un solo idioma para toda la pagina: lo que se puede apuntar LEVANTA, y su
     escuadra se corre en diagonal. Nada estrena un gesto propio. */
  /* la nota levanta y su foto se acerca apenas: el unico zoom de la hoja, y
     esta acotado a 1.03 porque mas que eso se lee como un anuncio */
  .post__a:hover { transform: translateY(calc(var(--mv) * -3px)); }
  .post__a:hover .post__ph img { transform: scale(1.03); }
  .post__a:hover .post__go::after { transform: rotate(45deg) translate(calc(var(--mv) * 2px), calc(var(--mv) * -2px)); }
  .err__l a:hover { transform: translateX(calc(var(--mv) * 4px)); }

  /* el riel se detiene para poder leer un rubro. Sin este gate, en tactil se
     detenia para siempre al primer toque. */
  .rail:hover .rail__track { animation-play-state: paused; }
}

/* FEEDBACK FISICO, NO NEGOCIABLE.
   Todo lo pulsable confirma la pulsacion al instante. Un solo lugar. */
.btn, .chan, .board__tab, .det__opt, .acc__q, .nav__burger,
.modal__close, .finale__social a, .chip, .appdl__b, .cnl__c, .post__a {
  transition-property: transform, background-color, border-color, color, box-shadow, opacity;
  transition-duration: .16s, var(--dur), var(--dur), var(--dur), var(--dur), var(--dur);
  transition-timing-function: var(--ease);
}
.btn:active, .chan:active, .board__tab:active, .det__opt:active,
.acc__q:active, .nav__burger:active, .modal__close:active, .finale__social a:active,
.cnl__c:active, .post__a:active {
  transform: scale(.97);
}

/* ---------- 8. NAV ----------
   v8: se elimino la franja teal superior ("Somos los ojos de tu marca").
   Textual del cliente: "da la sensacion de que hay una doble top bar". El
   nav es ahora la unica barra de la pagina; el eslogan cierra en el pie. */
.nav {
  position: sticky; top: 0; z-index: 60;
  background: rgba(255,255,255,.72);
  border-bottom: 1px solid var(--line);
  /* 520 ms y no 280. El valor lo puso la v22, cuando la barra cruzaba entera
     de dia a noche y a 280 ms ese cruce se leia como un parpadeo. El cruce se
     retiro en la v27 con el resto del sistema oscuro y el valor SE QUEDA: hoy
     lo unico que transiciona es el estado "pegada" —fondo, borde y sombra— y
     medio segundo hace que la barra ACOMPAÑE al scroll en vez de saltar con
     el. Si alguna vez se acorta, es una decision de ritmo, no una correccion. */
  transition: background-color .52s var(--ease-exp), border-color .52s var(--ease-exp),
              box-shadow var(--dur) var(--ease);
}
/* v13: SE ELIMINO LA FRANJA TEAL DE 3px QUE APARECIA ARRIBA AL SCROLLEAR.
   Pedido del cliente, y tenia razon: el estado pegado ya se anuncia con el
   desenfoque, el fondo mas opaco y la sombra. La franja era una CUARTA senal
   para el mismo hecho, y ademas cambiaba la altura visual de la barra en
   pleno scroll, que es el momento en que menos hay que mover cosas.
   No se reemplaza por nada: sacarla es la mejora. */
.nav.is-stuck {
  background: rgba(255,255,255,.90);
  backdrop-filter: blur(18px) saturate(150%);
  -webkit-backdrop-filter: blur(18px) saturate(150%);
  box-shadow: 0 1px 0 var(--line), 0 12px 28px rgba(52,49,44,.07);
}

/* ==========================================================================
   LA BARRA, CUANDO ATRAS HAY UN MAPA
   ==========================================================================
   MEDIDO EN EL CHROME DEL CLIENTE: la barra vale `rgba(255,255,255,.72)` y
   `backdrop-filter: none` en su estado de reposo. Sobre el papel del resto del
   sitio eso es correcto —atras no hay nada— y sobre el mapa de `/empresas` es
   un vidrio SIN esmerilar puesto encima de una ciudad: las avenidas y los
   nombres de barrio se leen a traves del logo y de los cuatro enlaces.
   Hasta la v24 no se notaba porque esa escena era de noche y la barra se
   pintaba oscura y opaca; al volver el mapa a la luz, la barra volvio a su
   estado claro y quedo el vidrio sin nada atras.

   SUBE A .94 Y SE LE PONE DESENFOQUE. El desenfoque estaba prohibido en esta
   ruta por una razon medida —era una de las cinco capas que dejaron la pagina
   en 0,2 cuadros por segundo— y esa razon YA NO APLICA: costaba caro porque el
   fondo se MOVIA (la camara volaba en cada mision). Desde la v23 la camara no
   se mueve, asi que el desenfoque se rasteriza una vez y se queda cacheado.
   Lo que sigue moviendose son marcadores, que son capas propias.

   VA DETRAS DE `:has()` Y NO EN `.nav` A SECAS: es la unica ruta del sitio con
   una imagen viva debajo de la barra. En las otras siete el vidrio al 72% es
   lo correcto y cambiarlo seria arreglar una pagina rompiendo el sistema. */
body:has(.red__mapa) .nav {
  background: rgba(250,248,245,.94);
  backdrop-filter: blur(14px) saturate(140%);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
}


/* --- BARRA DE LECTURA (v9) ---
   Proposito, en una frase: dice cuanto de la pagina va recorrido, que es
   orientacion, no adorno. Se dibuja con `animation-timeline: scroll()`, o sea
   que la avanza el compositor y no hay un solo listener de scroll. Donde no
   haya soporte simplemente no aparece: no es informacion imprescindible. */
.nav__prog {
  position: absolute; left: 0; right: 0; bottom: -1px; height: 2px;
  background: var(--teal);
  transform: scaleX(0); transform-origin: left;
  pointer-events: none;
}
@supports (animation-timeline: scroll()) {
  .nav__prog {
    animation: navProg linear forwards;
    animation-timeline: scroll(root block);
  }
}
@keyframes navProg { from { transform: scaleX(0); } to { transform: scaleX(1); } }

/* --- v11: MARCA Y SECCIONES SON UN SOLO BLOQUE A LA IZQUIERDA ---
   Historia corta: la version original era un flex con `margin-left:auto` y
   todo se amontonaba contra el logo. La v10.1 lo corrigio centrando las
   secciones (`1fr auto 1fr`), pero centradas quedaban a la deriva en el medio
   de una barra de 1240px: no pertenecian ni a la marca ni a las acciones, y
   el recorrido del ojo pasaba a tener tres paradas.

   Ahora son DOS anclas. Columna 1: logo. Columna 2: las cuatro secciones,
   alineadas a la izquierda pegadas a el. Columna 3: las acciones, contra el
   canalon derecho. La columna del medio se lleva el `1fr` sobrante, asi que
   los rotulos no se mueven cuando cambia el ancho de la ventana.

   EL AIRE DEL LOGO, POR ARITMETICA Y NO A OJO. El blanco real entre el
   wordmark y la "S" de "Soy empresa" es el canalon de la grilla mas el
   padding interno del enlace (16px), porque el enlace tiene campo propio al
   pasar el puntero y su caja empieza antes que su letra:
     minimo  20 + 16 = 36px
     maximo  32 + 16 = 48px
   Contra un wordmark de 144x27, eso es entre 1.3 y 1.8 veces su altura: el
   grupo respira y se sigue leyendo como un grupo. */
.nav__inner {
  position: relative;
  height: var(--nav-h);
  max-width: var(--wrap); margin-inline: auto; padding-inline: var(--gut);
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: clamp(20px, 2.2vw, 32px);
}
/* `display: grid` y no el inline por defecto: sin el, la linea base del `<img>`
   le cuelga al ancla unos pixeles por debajo y corre el logotipo. */
.nav__logo { grid-column: 1; justify-self: start; display: grid; }
/* el ancho va en `.nav__logo img` y no en una clase: las ocho paginas llevan
   un solo `<img>` sin clase adentro del ancla. Habia una `.nav__lg` mientras
   el nav apilaba dos logotipos para el cruce de dia a noche; se fue con el. */
.nav__logo img { width: 144px; height: auto; }

/* ==========================================================================
   LA NAVEGACION: UN PULGAR QUE VIAJA  (v10.5)
   ==========================================================================
   La v10.1 metio los tres enlaces dentro de una pastilla de --surface-2 con
   borde. Resolvia el problema de que fueran texto suelto, pero traia otro: el
   campo gris no dice NADA de la marca. Es el contenedor por defecto, el que
   sale cuando uno no decidio nada, y por eso se leia generico.

   El campo gris se elimino. Los tres rotulos van sueltos y lo que los agrupa
   es un PULGAR que se desliza hasta el que estas senalando.

   POR QUE ESTE PULGAR Y NO OTRO: el tinte es --teal-tint, exactamente el
   mismo relleno del recuadro de deteccion que la pagina usa en el hero, en la
   gondola y en las metricas. O sea que la barra superior deja de tener un
   lenguaje propio y pasa a hablar el de la pagina: senalar algo es ponerle un
   campo teal encima. Es la misma idea que .anot-box, a escala de nav.

   POR QUE SE MUEVE, en una frase: el pulgar viaja en vez de aparecer, asi que
   el ojo sigue un objeto unico entre tres destinos en lugar de ver tres
   fondos prendiendose y apagandose. Eso es continuidad espacial, no adorno.

   COMO SE MUEVE SIN TOCAR EL LAYOUT: el pulgar mide 100px de ancho fijo y el
   JS le escribe `translateX(...) scaleX(ancho/100)` directo sobre el
   elemento. Solo transform, nunca `left` ni `width`, y se escribe sobre el
   pulgar y no sobre el padre para no recalcular el estilo de los tres hijos
   en cada cuadro. La curva es --ease-io, que es la de movimiento EN pantalla:
   el objeto ya esta ahi, se reubica.
   ========================================================================== */
.nav__links {
  /* v11: justify-self start. El pulgar sigue funcionando igual porque el JS
     lee offsetLeft dentro de este contenedor, no coordenadas de pantalla. */
  grid-column: 2; justify-self: start;
  position: relative;
  display: flex; align-items: center; gap: 2px;
}
.nav__ink {
  /* se estira a lo alto del contenedor en vez de centrarse con un margen
     negativo: el margen negativo de la hoja esta reservado para los cruces de
     seccion, y este no es uno */
  position: absolute; left: 0; top: 0; bottom: 0;
  width: 100px;
  border-radius: var(--r-pill);
  background: var(--teal-tint);              /* el relleno del recuadro de deteccion */
  transform-origin: left center;
  opacity: 0;
  pointer-events: none;
  /* 260ms: dentro del presupuesto de UI. Con --ease-io el recorrido ya tiene
     peso; estirarlo mas lo volveria lento, que es lo contrario de lo pedido. */
  transition: transform .26s var(--ease-io), opacity .18s var(--ease);
}
.nav__links.is-live .nav__ink { opacity: 1; }
/* v12: LA BARRA TIENE QUE DECIR EN QUE PAGINA ESTAS.
   Hasta la v11 el sitio propio era una sola pagina, asi que el pulgar podia
   vivir solo en el hover. Con ayuda.html ya son dos y un menu que no marca la
   posicion obliga a leer la URL para saber donde estas. Cuando hay un enlace
   con aria-current, el pulgar queda visible en reposo apoyado ahi (el JS lo
   coloca) y se atenua a la mitad para no competir con el estado señalado:
   presente, pero claramente por detras del que estas apuntando. */
.nav__links:has(a[aria-current="page"]) .nav__ink { opacity: .55; }
.nav__links.is-live:has(a[aria-current="page"]) .nav__ink { opacity: 1; }
.nav__links a {
  position: relative; z-index: 1;
  font-size: 15px; font-weight: 600; letter-spacing: var(--tr-body);
  color: var(--text-2);                      /* 7.16:1 sobre papel */
  padding: 9px 16px; border-radius: var(--r-pill);
  transition: color .18s var(--ease);
}
/* la pagina actual va en tinta plena (12.95:1) contra el 7.16:1 del resto: la
   jerarquia se construye con color, nunca con peso, porque cambiar el peso
   correria de lugar a los enlaces vecinos */
.nav__links a[aria-current="page"] { color: var(--text); }
/* el foco de teclado mueve el pulgar igual que el puntero: la navegacion con
   tabulador tiene que ver lo mismo que la del mouse */
.nav__links a:focus-visible { color: var(--text); }
/* la hamburguesa vive DENTRO de esta zona: si fuera un item mas de la grilla
   se llevaria una columna propia y, al ocultarse las secciones, las acciones
   se irian al centro */
.nav__actions { grid-column: 3; justify-self: end; display: flex; align-items: center; gap: 12px; }
/* el icono del CTA achica en la barra: a 44px de alto, 20px pesaba demasiado */
.nav__actions .btn__ico svg { width: 17px; height: 17px; }

/* --- EL CTA DE LA BARRA, FIJO (v11) ---
   La v10.2 lo hacia entrar por scroll con `display` interpolable
   (allow-discrete + @starting-style) cuando el CTA del hero se iba de
   pantalla. Era ingenioso y era un error: la barra cambiaba de forma sola
   mientras alguien leia, y el unico elemento permanente del sitio se quedaba
   sin la unica accion permanente.
   Ahora esta desde el arranque. Lo que hacia falta para que la barra no se
   sintiera apretada no era esconder un boton: era dejar de pelear por el
   centro. Con las secciones corridas a la izquierda, la derecha tiene sitio
   de sobra para los dos botones.
   Queda solo el ajuste de contorno: al ser el ultimo elemento antes del
   canalon, no lleva sombra que lo despegue del borde. */
.nav__cta { flex: none; }

.nav__burger {
  display: none;
  flex: none;
  width: 44px; height: 44px; border-radius: var(--r-pill);
  border: 1.5px solid var(--text);   /* mismo contorno que el boton secundario */
  flex-direction: column; align-items: center; justify-content: center; gap: 5px;
}
.nav__burger span {
  display: block; width: 18px; height: 1.5px; background: var(--text);
  transition: transform .26s var(--ease), opacity .18s var(--ease);
}
/* Unica excepcion documentada al multiplicador --mv de la hoja: estos 3.25px
   no son desplazamiento, son la forma de la X. Con --mv:0 las dos barras
   cruzarian separadas y el icono dejaria de leerse como "cerrar", que es
   indicacion de estado y tiene que sobrevivir al movimiento reducido. */
.nav__burger[aria-expanded="true"] span:first-child { transform: translateY(3.25px) rotate(45deg); }
.nav__burger[aria-expanded="true"] span:last-child { transform: translateY(-3.25px) rotate(-45deg); }

.mnav {
  display: none; flex-direction: column; gap: 28px;
  /* v13: el menu movil aparece a partir de 1140px, y ahi --gut vale 0 porque
     el margen sale del centrado. Un padding de 0 dejaria los enlaces pegados
     al borde de la pantalla, asi que se calcula igual que cualquier bloque a
     sangre completa: el borde de contenido del contenedor. */
  padding: 24px var(--margen) 40px;
  border-top: 1px solid var(--line);
  background: var(--paper);
}
.mnav[hidden] { display: none; }
/* el panel aparecia de golpe. Keyframe y no transicion porque el cambio es de
   `display`, que no es interpolable sin allow-discrete. Entra en 220ms; sale
   instantaneo, que es la salida mas rapida posible. */
.mnav.is-open { display: flex; animation: mnavIn .22s var(--ease) both; }
@keyframes mnavIn {
  from { opacity: 0; transform: translateY(calc(var(--mv) * -8px)); }
  to   { opacity: 1; transform: none; }
}
.mnav__links { display: flex; flex-direction: column; gap: 4px; }
.mnav__links a { font-size: 22px; font-weight: 500; padding: 10px 0; }
/* mismo criterio que la barra de escritorio: la pagina actual se marca con la
   escuadra del motivo y tinta plena. En movil el pulgar deslizante no existe
   —no hay puntero que lo mueva— asi que la marca tiene que ser estatica. */
.mnav__links a[aria-current="page"] { color: var(--text); display: flex; align-items: center; gap: 12px; }
.mnav__links a[aria-current="page"]::before {
  content: ""; flex: none;
  width: 10px; height: 10px;
  border-top: 1.5px solid var(--teal); border-left: 1.5px solid var(--teal);
}
.mnav__actions { display: flex; flex-direction: column; gap: 12px; }
.mnav__actions .btn { width: 100%; }

/* ==========================================================================
   9. HERO: LA PROPUESTA DE VALOR Y EL CELULAR AUMENTADO
   v8: el titular ya no cuenta el proceso. Dice que es esto en terminos
   elementales (personas mirando una gondola) y el celular MUESTRA el
   mecanismo en bucle. La captura es producto real; lo que se dibuja encima
   son anotaciones de interfaz, que es el motivo de toda la pagina.
   z-index 2 porque el celular tiene que pasar por encima del campo teal
   de la seccion siguiente.
   ========================================================================== */
/* v9.1: mas aire. Medido, el hero tenia 93 elementos visibles en 526px de
   alto con 52/44 de padding: denso y sin respiro, y las piezas que flotan
   alrededor del celular no tenian lugar para moverse sin chocar entre si.
   Se sube el padding y se ensancha la columna del celular para que la orbita
   de las piezas tenga espacio propio. */
/* v11: MAS AIRE ARRIBA, PORQUE SE FUE LA PILDORA.
   La pildora media 34px de alto mas 20px de separacion al titular: 54px que
   sostenian el titular lejos de la barra. Al sacarla, el titular subia a
   pegarse al nav. Se devuelve ese aire al padding de la seccion, que es donde
   corresponde: ahora es el blanco de la pagina el que separa la barra del
   titular, y no un objeto puesto en el medio.
   El techo pasa de 92 a 116px (los 54 de la pildora, menos los 30 que el
   titular gana al no tener nada encima). El piso, de 44 a 56. */
.hero {
  position: relative; z-index: 2;
  padding-top: clamp(56px, 7.2vw, 116px);
  padding-bottom: clamp(48px, 6.5vw, 96px);
}
/* v10: SE ELIMINO EL DEGRADADO TEAL DEL PIE DEL HERO.
   Textual del cliente: "que no se sienta que hay una linea verde random ahi
   separado". El degradado no unia nada: anunciaba el campo teal antes de que
   llegara y por eso mismo lo convertia en una franja que aparecia sola.
   La union ahora es ESTRUCTURAL y son tres cosas a la vez, ninguna un
   degradado (ver .board, seccion 10):
     a) el campo teal dejo de ser una banda y es un PANEL con esquinas
        redondeadas, o sea un objeto con forma, no un corte del fondo;
     b) ese panel SUBE a buscar al hero con margin-top negativo, asi que su
        borde superior no coincide con el final del hero: lo invade;
     c) el celular se para encima del panel y lo cruza con casi el doble de
        sangrado que antes, mas el hilo .stitch--hero por la izquierda.
   Un borde que tres objetos cruzan deja de leerse como una linea. */
.hero__grid {
  display: grid;
  /* la columna del celular pasa de .86 a 1.02: las piezas que orbitan
     necesitan margen para moverse sin pisarse ni pisar el titular */
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.02fr);
  gap: clamp(32px, 4.5vw, 72px);
  align-items: start;
}
/* v11: LAS DOS COLUMNAS ARRANCAN EN LA MISMA LINEA.
   Estaban en `center`. Centrado, el bloque de texto quedaba 17px por debajo
   del borde superior del celular: no era una alineacion, era el resto de una
   division. Medido en el navegador y no estimado.
   Con `start`, el tope del titular y el tope del aparato caen exactamente en
   la misma horizontal, que es una relacion que el ojo puede comprobar. El
   desbalance que queda abajo es intencional y esta explicado: el celular
   sigue bajando 100px dentro del campo teal, que es el cruce que une las dos
   secciones. Alinear ARRIBA es lo unico que se puede alinear cuando el objeto
   de al lado esta disenado para desbordar por abajo. */
@media (max-width: 1024px) { .hero__grid { align-items: center; } }
/* min-width: 0 en las celdas de grid. Sin esto una palabra larga fuerza el
   ancho minimo de contenido y la columna se pasa de su fraccion. */
.hero__copy, .hero__stage, .hero__scene, .board__panels, .trust__copy { min-width: 0; }

/* v8: paso de JetBrains Mono a la familia de texto.
   v10: sale de la CAJA ALTA con tracking abierto. Era el mismo tic que los
   rotulos de seccion, solo que en forma de pildora: mayuscula + .12em sobre
   un dato que se lee mejor en caja baja. Ahora es una linea de estado, a
   13px, con tracking cero. Sube de 11.5 a 13 porque al salir de la caja alta
   el ojo pierde la altura de las mayusculas. */
/* v11: LA PILDORA DE ESTADO SE ELIMINO ENTERA.
   Aca vivian .livepill, .livepill__sep y .livepill__dot con su latido. La
   quito el cliente y la razon es buena: era un rotulo de estado por encima
   del titular, o sea una parada mas antes de la frase que dice que es esto.
   Los tres datos que llevaba ("red activa", "Argentina", el conteo de
   Znappers) no se perdieron: estan en la columna de cifras del hero y, con su
   explicacion, en la seccion de evidencia.
   El @keyframes ping SI se conserva: lo usan los .ping del mapa del celular
   latiendo de "Relevando ahora" en esa seccion. */
@keyframes ping {
  0%   { box-shadow: 0 0 0 0 rgba(19,193,172,.55); }
  70%  { box-shadow: 0 0 0 calc(var(--mv) * 8px) rgba(19,193,172,0); }
  100% { box-shadow: 0 0 0 0 rgba(19,193,172,0); }
}

/* ---------- EL TITULAR EN CUATRO TRATAMIENTOS (v9) ----------
   La referencia del cliente escalona cada palabra: la escala crece hacia
   abajo y el peso tambien. Cada palabra es un escalon.

     EL PUNTO DE VENTA   chico    500   --text-2     7.16:1
     MEDIDO              grande   700   --teal-deep  3.50:1  (texto grande)
     POR                 grande   700   --text-3     3.71:1  (texto grande)
     PERSONAS.           el mayor 700   --text      12.95:1

   Los dos tonos bajados solo pasan el umbral de TEXTO GRANDE (3:1 a partir
   de 24px, o 18.66px en negrita). Por eso el minimo del clamp de esas dos
   palabras es 34px y no puede bajar: esta chequeado en verify.py.
   Ningun tratamiento toca la forma de la letra: ni font-stretch, ni
   text-stroke, ni scale. Solo tamano, peso y color, que es lo que la regla
   de la casa permite.

   ARITMETICA RECALCULADA PARA ARCHIVO (v10), porque la caja alta de Archivo
   avanza entre un 11% y un 15% mas que la de Space Grotesk. Medido con
   fontTools sobre el binario, a wght 700, sumando el avance glifo por glifo:

     "PERSONAS."          Archivo 6.030 em   Space Grotesk 5.280 em   +14.2%
     "MEDIDO POR"         Archivo 6.527 em   Space Grotesk 5.874 em   +11.1%
     "EL PUNTO DE VENTA"  Archivo 10.397 em  Space Grotesk 9.352 em   +11.2%

   Con el tracking aplicado, "PERSONAS." mide 6.030 - .03*9 = 5.76 em. La
   comprobacion se corre entera en verify.py; estos son los peores casos:
     viewport 1680/1440 -> columna 553px, PERSONAS a 80px -> 461px  cabe
     viewport 1240      -> columna 556px, PERSONAS a 80px -> 461px  cabe
     viewport 1000      -> columna 441px, PERSONAS a 68px -> 392px  cabe
     viewport  940      -> columna 865px (apilado), 66px  -> 380px  cabe
     viewport  390      -> columna 350px, PERSONAS a 46px -> 265px  cabe
   O sea que entra sin bajar un solo tamano, y por eso no se toco la escala.
   Nada de esto se resuelve deformando: solo tamano, peso y color. */
.hero__h1 {
  /* v11: era 20px, la separacion contra la pildora. Sin pildora, el titular
     es el primer elemento de la columna y su tope lo pone el padding de la
     seccion. Cualquier margen aca seria un blanco duplicado. */
  margin-top: 0;
  letter-spacing: var(--tr-display);
  line-height: 1;
  font-weight: 700;
  font-size: clamp(46px, 6.8vw, 80px);   /* la referencia: PERSONAS. */
}
.hero__w { display: block; }

/* escalon 1: el mas chico, peso medio, gris secundario */
.hero__w--eyeb {
  font-size: clamp(17px, 1.8vw, 25px);
  font-weight: 500;
  letter-spacing: .005em;
  line-height: 1.15;
  color: var(--text-2);                  /* 7.16:1 */
  margin-bottom: .28em;
}
/* escalon 2 y 3: grandes, negrita maxima, los dos tonos bajados */
.hero__w--mid {
  font-size: clamp(34px, 5.1vw, 60px);
  color: var(--teal);               /* 3.50:1, texto grande */
  line-height: .98;
}
.hero__soft { color: var(--text-3); }    /* 3.71:1, texto grande */
/* escalon 4: el mayor, tinta plena */
.hero__w--big { color: var(--text); line-height: .96; }  /* 12.95:1 */

/* el acento de forma: la escuadra de deteccion del motivo, apoyada sobre la
   ultima palabra. Acentua sin sumar un campo teal detras de texto teal. */
.hero__w--big { position: relative; }
.hero__w--big::before {
  content: ""; position: absolute;
  left: -.16em; top: -.06em;
  width: .22em; height: .22em;
  border-top: .045em solid var(--teal);
  border-left: .045em solid var(--teal);
  transform: translateX(calc(var(--mv) * -6px));
  opacity: 0;
  animation: heroSquare .5s var(--ease) .46s forwards;
}
@keyframes heroSquare { to { opacity: 1; transform: none; } }

/* v11: el aire interno del bloque de texto sube en proporcion al titular.
   22 y 30 fijos eran los valores de una columna que empezaba 54px mas abajo;
   ahora el titular tiene mas presencia y necesita mas respiro debajo. */
.hero__sub {
  margin-top: clamp(22px, 2.1vw, 30px);
  max-width: 46ch;
  color: var(--text-2);
  font-size: clamp(16.5px, 1.35vw, 19.5px);
  font-weight: 400;
  letter-spacing: var(--tr-body);
  line-height: 1.55;
}
.hero__cta { display: flex; flex-wrap: wrap; gap: 12px; margin-top: clamp(30px, 2.8vw, 40px); }

/* ---------- 9.0 LOS BOTONES DE TIENDA (v10) ----------
   Textual: "le podes incluir en algun lugar cerca del celular los dos botones
   de descargar en iOS y descargar en Android para hacer referencia a eso".

   TRES DECISIONES, y las tres son de jerarquia:
   1. VIVEN CON EL APARATO, no en la columna del titular. Van arriba del
      celular, dentro de .hero__stage: pertenecen al objeto que muestran.
      Debajo no podian ir, porque abajo el celular cruza hacia el campo teal
      y los botones habrian quedado a caballo del borde.
   2. SON DISCRETOS A PROPOSITO. La home le habla a empresas y el CTA primario
      sigue siendo la demo. Estos son una referencia al producto, no una
      segunda conversion: badge de 108px, sin campo de color, con un rotulo
      que aclara de quien es la app. Si compitieran con el CTA, la pagina
      dejaria de tener un solo destino.
   3. LOS BADGES SON LOS OFICIALES, no un wordmark rehecho a mano. "App Store"
      y "Google Play" son marcas registradas: se usan los archivos que ya
      trae el sitio (images/English-copy-2.png y images/English.png), que son
      los badges oficiales, y no se recrea el logotipo con la tipografia de
      la casa.
   Los destinos son las fichas REALES de la app, las mismas que usa
   znappers.html en el sitio publicado. No hay ninguna URL inventada. */
/* La pieza vive ahora en la cabecera del mecanismo (ver seccion 10), apoyada
   sobre el campo teal y centrada bajo el celular que acaba de bajar del hero.
   Rotulo arriba, badges abajo: se lee de corrido, "esta es la app -> bajala",
   en vez de una etiqueta y dos logos en la misma linea. */
/* v11: se elimino .appdl__k, el rotulo "La app que usan los Znappers". Con
   los badges oficiales colgando del celular, el rotulo repetia con palabras
   lo que la composicion ya decia. Al sacarlo, los badges suben 26px solos
   (13.5px de rotulo + 10 de separacion + su interlinea). */
.appdl {
  display: flex; flex-direction: column; align-items: center;
  align-self: start;
}
.appdl__bs { display: flex; flex-wrap: wrap; justify-content: center; gap: 10px; }
.appdl__b {
  display: block; border-radius: 8px;
  opacity: .92;
  transition: opacity var(--dur) var(--ease), transform .16s var(--ease);
}
.appdl__b img { width: 108px; height: auto; }
.appdl__b:active { transform: scale(.97); }

/* --- 9.1 EL MARCO DEL CELULAR, EN CSS, DE FRENTE ---
   v9. Textual del cliente sobre la v8: "con que este inclinada no me
   entendiste bien". Fuera la rotacion 3D y fuera el resorte que inclinaba el
   aparato. El celular esta DE FRENTE y quieto en su eje; lo que se mueve es
   lo que flota alrededor.

   Las capas siguen separadas porque `transform` es una sola propiedad y no
   se puede compartir:
     .hero__stage  se lleva la animacion de ENTRADA.
     .hero__ar     es el sistema de coordenadas de todo lo que flota. No se
                   transforma nunca: si se moviera, arrastraria las piezas.
     .phone__body  se lleva la FLOTACION continua del aparato.
     .fx > .fx__i  paralaje (padre) + flotacion propia (hijo), por pieza. */
.hero__stage {
  align-self: end;   /* el sangrado sale entero por abajo, no repartido */
  display: flex; flex-direction: column; align-items: center;
  gap: clamp(14px, 1.8vw, 22px);
}
/* la fila del aparato: celular + columna de cifras. Se separo de .hero__stage
   para poder apoyar los badges de tienda ARRIBA sin meterlos en la orbita de
   las piezas que salen del celular. */
.hero__scene {
  display: flex; align-items: center; justify-content: center;
  gap: clamp(14px, 1.6vw, 26px);
}
/* el escenario de realidad aumentada: mide exactamente lo que el celular, y
   todo lo que flota se posiciona en porcentajes de ese ancho, asi que la
   composicion escala entera sin recalcular una sola coordenada */
.hero__ar {
  position: relative;
  flex: none;
  width: clamp(200px, 18.6vw, 250px);
  /* valores de reposo del resorte del puntero: sin JS quedan en cero */
  --par-x: 0px;
  --par-y: 0px;
  /* el celular baja por debajo del hero y aterriza sobre el panel teal de la
     seccion siguiente. v10: el sangrado maximo pasa de 92px a 132px, casi el
     doble. No es capricho: si el objeto que cruza apenas asoma, el borde
     sigue leyendose como un borde. Para que el cruce SEA la union, el celular
     tiene que estar claramente parado sobre el campo, no rozandolo. */
  margin-bottom: calc(var(--phone-bleed) * -1);
  --phone-bleed: clamp(56px, 9vw, 132px);
}
.phone {
  position: relative;
  width: 100%;
  display: flex; justify-content: center;
}
.phone__body {
  position: relative;
  width: 100%;
  padding: 9px;
  border-radius: 34px;
  background: linear-gradient(160deg, #3d3a34 0%, #201e1a 46%, #2e2b26 100%);
  box-shadow:
    inset 0 0 0 1px rgba(247,244,239,.14),
    0 2px 4px rgba(26,24,21,.20),
    0 26px 54px rgba(26,24,21,.30),
    0 60px 100px rgba(26,24,21,.18);
  /* v9.1: el vaiven pasa de 5.6s a 6.8s. La duracion larga es lo que hace que
     el aparato se lea vivo sin marear. */
  animation: phoneFloat 6.8s var(--ease-io) infinite alternate;
}
/* flotacion: +-5px, sin rebote y sin escala. Con --mv:0 los dos extremos
   valen 0px, asi que se detiene sola con movimiento reducido.
   v10.5: vuelve a +-9px. Se habia bajado a +-5px en la v10.3 por una
   restriccion que ya no existe (los badges estaban a 9px del borde inferior y
   el marco los tocaba en el extremo del ciclo). Con 47px de separacion la
   holgura es de 38px y el aparato puede respirar como lo pedia la v9.1. */
@keyframes phoneFloat {
  from { transform: translateY(calc(var(--mv) * -9px)); }
  to   { transform: translateY(calc(var(--mv) * 9px)); }
}
.phone__btn { position: absolute; width: 2px; background: #17150f; border-radius: 2px; }
.phone__btn--vol { left: -2px; top: 21%; height: 9%; }
.phone__btn--pwr { right: -2px; top: 28%; height: 12%; }

.phone__screen {
  position: relative;
  border-radius: 26px;
  overflow: hidden;
  background: var(--surface-2);
  /* las anotaciones internas se dimensionan con unidades de contenedor: el
     celular entero escala coherente en cualquier ancho sin deformar glifos */
  container-type: inline-size;
}
.phone__shot { width: 100%; height: auto; }

/* --- 9.2 EL RECORRIDO HASTA EL COMERCIO ELEGIDO ---
   El viewBox del SVG es el de la captura real (589x1280), asi que el trazo
   cae sobre las calles del mapa y no sobre coordenadas inventadas.
   Se dibuja con stroke-dashoffset sobre pathLength="1": es la unica
   excepcion documentada a "solo transform y opacity" de toda la hoja.
   El "punto que camina" es el mismo truco al reves: un guion redondo de
   longitud casi cero que se desplaza a lo largo del trazo. */
/* sin z-index: el trazo tiene que quedar POR DEBAJO del contador, del
   recuadro y de la ficha, y para eso alcanza el orden del marcado */
.route { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
.route__p, .route__dot {
  fill: none;
  stroke-linecap: round; stroke-linejoin: round;
  opacity: 0;
  transition: stroke-dashoffset .95s var(--ease-io), opacity .3s var(--ease);
}
.route__p {
  stroke: var(--teal); stroke-width: 9;
  stroke-dasharray: 1; stroke-dashoffset: 1;
}
/* el modificador va DESPUES de la base: con la misma especificidad gana la
   ultima regla, y al reves el halo blanco se pintaba del color del punto */
.route__dot { stroke: var(--teal); stroke-width: 26; stroke-dasharray: .0001 2; stroke-dashoffset: 0; }
.route__dot--halo { stroke: var(--paper); stroke-width: 40; }

.phone.is-route .route__p   { opacity: 1; stroke-dashoffset: 0; }
.phone.is-route .route__dot { opacity: 1; stroke-dashoffset: -1; }
/* cuando entra la ficha del comercio, el recorrido ya conto lo suyo y sale */
.phone.is-card .route__p, .phone.is-card .route__dot { opacity: 0; }

/* --- 9.3 CONTADOR DE DETECCIONES ---
   Cuenta los pines que se acaban de anotar sobre el mapa: no es una metrica
   inventada, es la lectura de lo que hay en pantalla. Mono porque es cifra
   tabular apoyada sobre una imagen: los dos criterios de la politica. */
.mcount {
  position: absolute; left: 5%; top: 13%;
  display: inline-flex; align-items: center; gap: 2.2cqw;
  padding: 1.4cqw 3cqw;
  border-radius: var(--r-pill);
  background: var(--ink); color: var(--on-ink);     /* 11.80:1 */
  font-family: var(--mono); font-size: 3.1cqw; font-weight: 500;
  letter-spacing: .09em; text-transform: uppercase; line-height: 1.5;
  box-shadow: 0 1cqw 3cqw rgba(26,24,21,.28);
  opacity: 0;
  transform: translateY(calc(var(--mv) * -6px));
  transition: opacity .26s var(--ease), transform .26s var(--ease);
}
.phone.is-pins .mcount { opacity: 1; transform: translateY(0); transition-duration: .42s; }
.mcount__l { color: var(--on-ink-2); }               /* 7.17:1 sobre tinta */
.mcount__n {
  color: var(--teal); font-weight: 700; font-size: 3.6cqw;
  font-variant-numeric: tabular-nums;                /* 5.70:1 sobre tinta */
}

/* --- 9.4 PINGS DE DETECCION sobre los pines reales del mapa ---
   Cada anillo cae exactamente encima de un pin de comercio que YA esta en la
   captura. No son pines falsos: son la deteccion anotada. */
.ping {
  position: absolute;
  width: 9%; aspect-ratio: 1;
  border-radius: 50%;
  border: 2px solid var(--teal);
  background: rgba(19,193,172,.16);
  opacity: 0;
  transform: translate(-50%, -50%) scale(calc(1 - .45 * var(--mv)));
  transition: opacity .26s var(--ease), transform .26s var(--ease);
}
.ping--1 { left: 64%;   top: 42%; }
.ping--2 { left: 44.5%; top: 53%; }
.ping--3 { left: 10%;   top: 62%; }
.ping--4 { left: 22%;   top: 71.5%; }
.phone.is-pins .ping {
  opacity: 1;
  transform: translate(-50%, -50%) scale(1);
  transition-duration: .5s;
}
/* stagger 70ms: dentro del rango 30-80ms. Solo al entrar; al salir, 0. */
.phone.is-pins .ping--2 { transition-delay: 70ms; }
.phone.is-pins .ping--3 { transition-delay: 140ms; }
.phone.is-pins .ping--4 { transition-delay: 210ms; }

/* --- 9.5 RECUADRO sobre el comercio elegido ---
   v8: se movio del cluster de Almagro al pin de Recoleta, que es donde
   termina el recorrido. Ademas, ahi no lo tapa la ficha del comercio: en la
   v7 la tarjeta subia y se comia el recuadro a los 600ms de haberlo puesto. */
.anot-box--hero {
  left: 50.5%; top: 36.5%; width: 27%; height: 11%;
  opacity: 0;
  transform: scale(calc(1 + .14 * var(--mv)));
  transition: opacity .24s var(--ease), transform .24s var(--ease);
}
.phone.is-focus .anot-box--hero {
  opacity: 1; transform: scale(1);
  transition-duration: .46s;
}
.anot-box--hero .anot-box__c { width: 11px; height: 11px; border-width: 1.5px; }

/* --- 9.6 TARJETA DE MISION que entra en la pantalla ---
   Datos reales del producto: comercio, direccion y campana. */
.mcard {
  position: absolute;
  left: 5%; right: 5%; bottom: 16.5%;
  padding: 3.8cqw 4cqw;
  border-radius: 5cqw;
  background: var(--paper);
  box-shadow: 0 1cqw 4cqw rgba(26,24,21,.24);
  opacity: 0;
  transform: translateY(calc(var(--mv) * 26px)) scale(calc(1 - .03 * var(--mv)));
  transition: opacity .26s var(--ease), transform .26s var(--ease);
}
.phone.is-card .mcard {
  opacity: 1; transform: translateY(0) scale(1);
  transition-duration: .5s;
}
.mcard__k {
  display: inline-flex; align-items: center;
  padding: .7cqw 2.2cqw; border-radius: var(--r-pill);
  background: var(--teal-field); color: var(--on-teal);      /* 7.80:1 */
  font-family: var(--mono); font-size: 3.1cqw; font-weight: 500;
  letter-spacing: .1em; text-transform: uppercase; line-height: 1.5;
}
.mcard__n {
  margin-top: 2.4cqw;
  font-size: 4.9cqw; font-weight: 700; letter-spacing: var(--tr-body); line-height: 1.15;
  color: var(--text);                                   /* 12.95:1 */
}
.mcard__a { margin-top: 1.2cqw; font-size: 3.9cqw; line-height: 1.35; color: var(--text-2); }  /* 7.16:1 */
.mcard__c { margin-top: 3cqw; }
/* el nombre de la campana es un nombre, no un dato tabular: va en la
   familia de texto, no en la mono */
.mcard__camp {
  display: inline-flex; align-items: center;
  padding: 1cqw 2.6cqw; border-radius: var(--r-pill);
  background: var(--surface-2); border: 1px solid var(--line);
  color: var(--text-2);                                 /* 6.13:1 sobre surface-2 */
  font-size: 3.4cqw; font-weight: 500; letter-spacing: .01em; line-height: 1.5;
}

/* ==========================================================================
   9.7  LO QUE SALE DEL CELULAR    (reescrito entero en la v10)
   ==========================================================================
   Textual del cliente sobre la version anterior: "no me gustaron los graficos
   que quedaron sobre el celular, muy estaticos. La idea era que el celular
   sea algo animado, vivo y con referencias propias de cosas que salen del
   celular de la app de Znapp, como misiones (pines de misiones) y data
   (insights, fotografias, metricas)".

   EL ERROR DE FONDO NO ERA QUE SE MOVIERAN POCO: ERA QUE NO SIGNIFICABAN
   NADA. Flotaban un donut, unas barras y una curva. Formas de grafico sin un
   solo dato adentro y sin relacion con el producto: decoracion de plantilla.
   Se puede mover una forma vacia todo lo que uno quiera y va a seguir siendo
   una forma vacia.

   Lo que flota ahora son ARTEFACTOS RECONOCIBLES DE LA APP, y cada uno lleva
   un dato real:

     PIN DE MISION    el pin salta del mapa con el nombre del comercio:
                      Genmai - Almacen Organico, Tucuman 3617, Almagro.
     DISTANCIA        14.31 Kms., como la muestra la app.
     RECOMPENSA       $ 2.500,00 de la campana Granger Foods.
     FOTO RELEVADA    una foto REAL del set (gondola-precio-oferta), saliendo
                      de la pantalla como miniatura.
     METRICA LEIDA    $ 1.968,94 - Precio detectado. Sale de esa misma foto.
     INSIGHT          Faltante en 3 de 12 comercios.

   LA SECUENCIA ES EL ARGUMENTO. No estan puestas a flotar todas juntas: se
   encadenan con lo que ya pasaba DENTRO de la pantalla, y en este orden:

     dentro  se detectan los comercios sobre el mapa
     dentro  se traza el recorrido y se abre la ficha de la mision
     FUERA   1. salta el PIN con el nombre del comercio
     FUERA   2. la distancia y la recompensa de esa mision
     FUERA   3. sale la FOTO que el Znapper acaba de tomar
     FUERA   4. el DATO que esa foto produjo: el precio detectado
     FUERA   5. el INSIGHT que ese dato produjo: el faltante
     FUERA   6. el dato queda validado

   Pin -> foto -> dato -> insight. Eso cuenta el mecanismo completo sin una
   sola palabra de copy, que es exactamente lo que se le pide a la escena.
   El bucle esta en js/znapp-v4.js (phoneLoop) y dura 12s.

   TRES CAPAS POR PIEZA, y no una, porque `transform` es UNA propiedad y dos
   animaciones sobre el mismo elemento no se suman: gana la ultima.
     .fx      posicion + PARALAJE de puntero, escalado por profundidad --d
     .fx__i   FLOTACION continua, con su duracion y su desfasaje propios
     .fx__c   ENTRADA Y SALIDA, atadas al estado del bucle

   POR QUE LA CAPA 3 VA CON TRANSICIONES Y NO CON KEYFRAMES: el bucle se pausa
   al salir del viewport y al cambiar de pestana. Un keyframe reinicia desde
   cero al interrumpirse; una transicion se reorienta desde el valor que
   tiene en pantalla. Con keyframes se ve el salto.

   DE DONDE ENTRA CADA PIEZA: de adentro del celular. --ox/--oy apuntan hacia
   el aparato, asi que la pieza nace pegada al marco y se separa. Ninguna
   arranca en scale(0): el minimo es .95.

   DESFASAJE. Ninguna comparte duracion ni fase: 6.4s, 5.1s, 7.3s, 5.8s y dos
   pines de 5.5 y 8.2s, cada uno con un retardo negativo distinto, asi que
   arrancan a mitad de ciclo y no se sincronizan nunca. Cuando dos piezas
   suben y bajan a la vez el ojo las lee como una sola lamina.

   PROFUNDIDAD. --d va de .45 (lejos, se mueve poco) a 1.15 (cerca, se mueve
   mas): el orden correcto del paralaje, lo cercano se desplaza mas.
   ========================================================================== */

/* --- 9.7.1 LA MAQUINARIA COMUN --- */
.fx {
  position: absolute;
  z-index: 5;
  pointer-events: none;
  --d: 1;
  /* --par-x/--par-y los escribe el resorte del puntero sobre .hero__ar,
     cuadro a cuadro. Sin JS valen 0 y la pieza queda donde se declaro.
     Pasan por --mv como toda distancia de traslado de la hoja, aunque el JS
     ademas ni se instale con movimiento reducido: dos redes, no una. */
  transform: translate(calc(var(--par-x) * var(--d) * var(--mv)), calc(var(--par-y) * var(--d) * var(--mv)));
}
/* capa 2: SOLO la flotacion. Nada mas puede tocarle el transform. */
.fx__i {
  display: block;
  animation: fxFloat var(--fdur, 6s) var(--ease-io) var(--fdelay, 0s) infinite alternate;
}
@keyframes fxFloat {
  from { transform: translateY(calc(var(--mv) * var(--famp, 6px) * -1)) rotate(var(--frot, 0deg)); }
  to   { transform: translateY(calc(var(--mv) * var(--famp, 6px))) rotate(calc(var(--frot, 0deg) * -1)); }
}
/* capa 3: SOLO la entrada y la salida, atadas al bucle.
   La salida (260ms) es el DOBLE de rapida que la entrada (520ms), como en
   toda la hoja: la escena se arma con calma y se recoge de una. */
.fx__c {
  display: block;
  opacity: 0;
  transform: translate(calc(var(--mv) * var(--ox, 0px)), calc(var(--mv) * var(--oy, 10px)))
             scale(calc(1 - .05 * var(--mv)));
  transition: opacity .26s var(--ease) var(--outd, 0ms), transform .26s var(--ease) var(--outd, 0ms);
}
.phone.is-mish    ~ .fx--mission .fx__c,
.phone.is-metric  ~ .fx--metric  .fx__c,
.phone.is-insight ~ .fx--insight .fx__c,
.phone.is-pins    ~ .fx--pin     .fx__c {
  opacity: 1; transform: none;
  transition-duration: .52s;
  transition-delay: 0s;              /* la entrada la agenda el guion del JS */
}
/* EL REINICIO DEL BUCLE, EN CASCADA INVERSA.
   Al cerrar el ciclo el JS saca todas las clases de una, asi que las ocho
   piezas desaparecian en el mismo cuadro: un parpadeo, que es exactamente el
   cambio brusco que una animacion tiene que evitar. Ahora la escena se
   RECOGE en el orden inverso al que se armo (primero lo ultimo que salio),
   con 40ms entre pieza, dentro del rango de stagger de 30-80ms. La salida
   completa tarda 500ms y todavia quedan 700ms de silencio antes de que el
   ciclo vuelva a empezar.
   Se implementa con --outd, declarado en la regla de posicion de cada pieza
   (0ms el insight, 40 la metrica, 80 la foto, 120 la recompensa y el pin de
   la derecha, 160 el otro pin, 200 el pin de mision, 240 la distancia). El
   retardo vive en el estado OCULTO, asi que solo afecta a la SALIDA: la
   entrada la sigue agendando el guion del JS, que por eso lo pone en 0s. */

/* la superficie de las piezas es la MISMA que la de .mcard, o sea la tarjeta
   que la app dibuja dentro de la pantalla, sacada afuera y puesta a levitar.
   Que sea la misma superficie es lo que hace que se lean como algo que salio
   del telefono y no como una tarjeta de otro sistema. */
.fxcard {
  padding: 9px 11px;
  border-radius: 13px;
  background: var(--paper);
  box-shadow: 0 2px 6px rgba(26,24,21,.10), 0 16px 38px rgba(26,24,21,.18);
}

/* --- 9.7.2  EL PIN DE MISION QUE SALTA DEL MAPA ---
   Primera pieza que sale. Es el pin del mapa con su ficha al lado: el mismo
   objeto que el usuario toca en la app para tomar una mision. Los dos datos
   son reales y son los mismos que muestra la ficha de adentro de la pantalla:
   el comercio y su direccion. */
/* REPARTO DE LA ESCENA. La izquierda tiene espacio de verdad (el titular
   deja hueco a la derecha de PERSONAS.); la derecha tiene el ancho justo
   antes de la columna de cifras. Por eso las tres piezas de texto largo
   (pin, distancia, insight) mas la foto van a la izquierda, y a la derecha
   quedan solo la metrica, la recompensa y un pin. Que cada pieza no se coma
   la pantalla se comprueba por calculo en verify.py, seccion S bis. */
.fx--mission {
  left: -44%; top: 15%;
  --outd: 200ms;                    /* cascada inversa de salida */
  --fdur: 6.4s; --fdelay: -2.1s; --famp: 11px; --frot: -.9deg;
  --ox: 26px; --oy: -8px;
}
/* 176px y no menos: la direccion 'Tucuman 3617, Almagro' mide 116px a 10.5px
   en Archivo, y con 168 se partia en dos lineas y descuadraba la tarjeta */
.mish { display: flex; align-items: center; gap: 9px; width: 176px; }
.mish__pin { flex: none; display: grid; place-items: center; width: 24px; height: 24px; }
/* el pin conserva su inclinacion de -45deg: es su forma, no un movimiento */
.mish__pin .pin { width: 19px; height: 19px; transform: rotate(-45deg); }
.mish__pin .pin::after { width: 7px; height: 7px; margin: -3.5px 0 0 -3.5px; }
.mish__b { display: block; min-width: 0; }
.mish__n {
  display: block;
  font-size: 12px; font-weight: 700; line-height: 1.18;
  letter-spacing: var(--tr-body); color: var(--text);        /* 12.95:1 */
}
.mish__a {
  display: block; margin-top: 2px;
  font-size: 10.5px; font-weight: 500; font-style: normal; line-height: 1.25;
  letter-spacing: var(--tr-micro); color: var(--text-2);     /* 7.16:1 */
}

/* --- 9.7.3  LA METRICA LEIDA ---
   Tercera pieza. Es el dato que produjo la foto anterior, y por eso entra
   despues de ella y no antes: si entrara antes, la escena diria que el dato
   existe sin la evidencia, que es justo lo contrario de lo que vende Znapp.
   El precio sale de una foto real del set. La cifra va en monoespaciada
   porque es dato tabular, que es la unica regla que habilita la mono. */
.fx--metric {
  right: -32%; top: 20%;
  --outd: 40ms;                    /* cascada inversa de salida */
  --fdur: 5.1s; --fdelay: -3.4s; --famp: 13px; --frot: 1.2deg;
  --ox: -26px; --oy: 6px;
}
.dread { width: 138px; }
.dread__row { display: flex; align-items: center; gap: 8px; }
.dread__v {
  font-family: var(--mono); font-size: 15px; font-weight: 700;
  letter-spacing: -.02em; line-height: 1.1;
  font-variant-numeric: tabular-nums;
  color: var(--text);                                        /* 12.95:1 */
}
.dread__k {
  display: block; margin-top: 4px;
  font-size: 10.5px; font-weight: 500; letter-spacing: var(--tr-micro);
  color: var(--text-2);                                      /* 7.16:1 */
}
/* el sello de validado no es una pieza aparte: es el ultimo latido de esta.
   El dato aparece, y un segundo despues queda validado. Asi el cierre del
   bucle ocurre sobre el dato y no sobre una tarjeta generica de documento. */
.dread__ok {
  margin-left: auto; flex: none;
  display: grid; place-items: center;
  width: 19px; height: 19px; border-radius: 50%;
  background: var(--teal-field); color: var(--on-teal);             /* 7.80:1 */
  transform: scale(calc(1 - .2 * var(--mv)));
  opacity: .3;
  transition: transform .42s var(--ease), opacity .42s var(--ease);
}
.phone.is-val ~ .fx--metric .dread__ok { transform: none; opacity: 1; }
.fx__tick { stroke-dasharray: 1; stroke-dashoffset: 1; transition: stroke-dashoffset .4s var(--ease-io) .1s; }
.phone.is-val ~ .fx--metric .fx__tick { stroke-dashoffset: 0; }

/* --- 9.7.4  EL INSIGHT ---
   Cuarta pieza y la de mas arriba en la cadena de valor: el dato de un
   comercio se vuelve un hallazgo de la categoria. Lleva la escuadra de
   deteccion, que es el motivo de la pagina en su escala mas chica. */
/* el insight va DEBAJO de la metrica, del mismo lado: son los dos datos que
   produjo la foto, y leerlos apilados en la misma columna hace que la cadena
   se entienda tambien en el espacio y no solo en el tiempo */
.fx--insight {
  right: -32%; bottom: 6%;
  --outd: 0ms;                    /* cascada inversa de salida */
  --fdur: 7.3s; --fdelay: -1.2s; --famp: 12px; --frot: -1.1deg;
  --ox: -24px; --oy: -6px;
}
.dins { display: flex; align-items: flex-start; gap: 9px; width: 146px; }
.dins__m {
  flex: none; margin-top: 3px;
  width: 11px; height: 11px;
  border-top: 1.6px solid var(--teal);                  /* 3.50:1 grafico */
  border-left: 1.6px solid var(--teal);
}
.dins__t {
  font-size: 11.5px; font-weight: 500; line-height: 1.3;
  letter-spacing: var(--tr-micro); color: var(--text-2);      /* 7.16:1 */
}
.dins__t b { font-weight: 700; color: var(--text); }          /* 12.95:1 */

/* --- 9.7.5  LAS ETIQUETAS DE LA MISION QUE SALEN DEL MARCO ---
   Distancia y recompensa. Son literales del producto: la app muestra los dos
   valores asi. Entran despues del pin porque pertenecen a esa mision. */
.anot-tag--dist, .anot-tag--rew {
  opacity: 0;
  /* mismo criterio: se recogen despues de las tarjetas de dato */
  transition: opacity .24s var(--ease) var(--outd, 0ms), transform .24s var(--ease) var(--outd, 0ms);
}
.anot-tag--dist { --outd: 240ms; left: -42%; top: 33%; transform: translateX(calc(var(--mv) * 14px)); }
.anot-tag--rew  { --outd: 120ms; right: -26%; top: 44%; transform: translateX(calc(var(--mv) * -14px)); }
.phone.is-dist .anot-tag--dist { opacity: 1; transform: none; transition-duration: .46s; transition-delay: 0s; }
.phone.is-rew  .anot-tag--rew  { opacity: 1; transform: none; transition-duration: .46s; transition-delay: 0s; }
/* la linea guia se dibuja desde el celular hacia afuera */
.anot-tag--dist .anot-tag__line { transform-origin: right center; }
.anot-tag--rew  .anot-tag__line { transform-origin: left center; }
.anot-tag--dist .anot-tag__line,
.anot-tag--rew  .anot-tag__line {
  transform: scaleX(calc(1 - var(--mv)));
  transition: transform .2s var(--ease);
}
.phone.is-dist .anot-tag--dist .anot-tag__line,
.phone.is-rew  .anot-tag--rew  .anot-tag__line { transform: scaleX(1); transition-duration: .34s; transition-delay: .1s; }

/* --- 9.7.6  LA FOTO RELEVADA SALE DE LA PANTALLA ---
   Segunda pieza de la cadena y la bisagra de todo el relato: es el momento en
   que el Znapper ya disparo y la evidencia deja de estar dentro del telefono
   para apoyarse delante de el. La imagen es REAL, del set de la empresa
   (`gondola-precio-oferta`), y es la misma foto de la que sale el precio que
   aparece un segundo despues. No es una foto de relleno: es la que cierra la
   cadena foto -> dato. */
.shot {
  position: absolute; left: -30%; bottom: 2%;
  width: 40%;
  padding: 5px 5px 4px;
  border-radius: 10px;
  background: var(--paper);
  box-shadow: 0 3px 10px rgba(26,24,21,.22), 0 18px 40px rgba(26,24,21,.24);
  opacity: 0;
  /* nace pegada al marco (a la derecha y abajo, o sea hacia el telefono) y se
     separa girando apenas: el gesto de sacar una foto de una pila */
  transform: rotate(-5deg) translate(calc(var(--mv) * 22px), calc(var(--mv) * 14px)) scale(calc(1 - .06 * var(--mv)));
  /* 80ms de retardo en la SALIDA: es la tercera pieza de la cascada inversa
     con la que se recoge la escena al cerrar el ciclo (ver .fx__c) */
  transition: opacity .28s var(--ease) 80ms, transform .28s var(--ease) 80ms;
  z-index: 4;
}
.phone.is-shot .shot {
  opacity: 1;
  transform: rotate(-5deg);
  transition-duration: .52s;
  transition-delay: 0s;
}
.shot img { width: 100%; height: auto; border-radius: 6px; aspect-ratio: 3 / 4; object-fit: cover; object-position: 50% 40%; }
.shot__tag {
  display: block; margin-top: 4px;
  font-family: var(--mono); font-size: 8.5px; font-weight: 500;
  letter-spacing: .08em; text-transform: uppercase;
  color: var(--text-2); text-align: center;          /* 7.16:1 sobre papel */
  font-variant-numeric: tabular-nums;
}

/* --- 9.7.7  PINES DE MAPA SUELTOS ---
   Los unicos elementos de la escena que no llevan dato, y estan permitidos
   porque el pin ES la unidad del producto: son los otros comercios con
   mision, los mismos que se estan detectando dentro de la pantalla. Por eso
   entran con `is-pins` y no por su cuenta.
   Dos tonos y nada mas: teal para el comercio con mision (es campo, no texto)
   y tinta para el que ya se relevo. La referencia traia un tercer pin naranja
   y se descarto para no sumar un cuarto tono al sistema por una decoracion.
   Eran tres y quedaron dos: con las cuatro tarjetas de dato en escena, el
   tercero era ruido. */
.pin {
  position: relative;
  display: block;
  width: 22px; height: 22px;
  border-radius: 50% 50% 50% 2px;
  transform: rotate(-45deg);
  box-shadow: 0 3px 8px rgba(26,24,21,.24), 0 10px 22px rgba(26,24,21,.16);
}
.pin::after {
  content: ""; position: absolute; left: 50%; top: 50%;
  width: 8px; height: 8px; margin: -4px 0 0 -4px;
  border-radius: 50%;
}
/* los pines se quedan en el TEAL DE MARCA: son la unidad del producto, salen
   del mapa de la app y ahi son #13c1ac. No llevan texto, asi que la regla del
   campo no los alcanza. Lo que si cambia es su punto interior: heredaba
   --on-teal y con el blanco nuevo daria 2.27:1 contra el pin. Se fija en
   tinta, que es lo que era antes de que --on-teal cambiara. */
.pin--shop { background: var(--teal); }
.pin--shop::after { background: var(--ink-2); }
.pin--ink { background: var(--ink); }
.pin--ink::after { background: var(--teal); }
/* los pines viajan por delante de las tarjetas: son la unidad del producto y
   tienen que leerse como lo mas cercano al ojo */
.fx--pin { z-index: 6; }
/* el pin de arriba se mantiene por debajo del borde superior del escenario:
   arriba estan los badges de tienda y con --famp 17px llegaba a rozarlos */
.fx--pin1 { left: -15%; top: 1%;     --outd: 160ms; --fdur: 5.5s; --fdelay: -2.7s; --famp: 14px; --ox: 14px; --oy: 12px; }
.fx--pin2 { right: -11%; top: 60%;   --outd: 120ms; --fdur: 8.2s; --fdelay: -4.9s; --famp: 19px; --ox: -14px; --oy: 10px; }
/* stagger de 70ms entre los dos, dentro del rango 30-80ms */
.phone.is-pins ~ .fx--pin2 .fx__c { transition-delay: 70ms; }

/* ---------- 9.10 LA COLUMNA DE CIFRAS DE LA REFERENCIA ----------
   Solo a partir de 1240px: por debajo aprieta el titular, y los mismos
   numeros estan completos y con su explicacion en "Por que confiar".
   El signo va en --teal-deep (3.50:1) y no en teal puro (2.27:1), y solo
   porque a ese tamano es texto grande. */
.hero__figs { display: none; }
.hero__fign {
  display: block;
  font-family: var(--mono); font-weight: 700;
  font-size: clamp(24px, 2.1vw, 30px);
  letter-spacing: -.025em; line-height: 1;
  font-variant-numeric: tabular-nums;
  color: var(--text);                    /* 12.95:1 */
}
.hero__fign b { color: var(--teal); font-weight: 700; }   /* 3.50:1 */
/* v10: sale de la caja alta con tracking abierto. Sube de 11px a 12.5px
   porque la caja baja pierde la altura de las mayusculas, y el peso baja de
   600 a 500: contra una cifra de 30px en 700, la etiqueta no necesita pelear.
   Ese es el contraste de escala que reemplaza al de tracking. */
.hero__figl {
  display: block; margin-top: 7px;
  font-size: 13px; font-weight: 500; letter-spacing: var(--tr-micro);
  line-height: 1.25; color: var(--text-2);                     /* 7.16:1 */
}

/* ==========================================================================
   10. MECANISMO PASO A PASO (CAMPO TEAL)
   ==========================================================================
   v10. AQUI SE RESUELVE LA INTEGRACION CON EL HERO.
   Textual del cliente: "me gustaria que quede mas integrado con la parte de
   abajo para que no se sienta que hay una linea verde random ahi separado".

   La version anterior lo intentaba con un degradado teal al pie del hero. No
   funcionaba, y el diagnostico es simple: un degradado no une dos cosas, solo
   avisa que una esta por llegar. El borde seguia siendo un corte horizontal a
   todo lo ancho, y como el unico objeto que lo cruzaba estaba en la columna
   derecha, la mitad izquierda de la pantalla veia una franja verde y nada
   mas. El degradado se elimino.

   La union ahora es ESTRUCTURAL, y son tres cosas que trabajan juntas:

   a) EL CAMPO DEJA DE SER UNA BANDA Y PASA A SER UN PANEL. Las esquinas
      superiores redondeadas le dan forma de objeto apoyado sobre el papel. Un
      rectangulo redondeado que empieza y termina se lee como una pieza; una
      banda a sangre se lee como un corte del fondo. Es el mismo radio que
      usan las superficies de la pagina (--r-surface, en su escala grande).

   b) EL PANEL SUBE A BUSCAR AL HERO. `margin-top` negativo: el borde superior
      del campo ya no coincide con el final del hero, lo invade. Es el recurso
      "que la seccion de abajo suba a buscar al hero" que pedia el brief, y es
      el tercer cruce de borde declarado de la hoja.

   c) TRES OBJETOS LO CRUZAN. El celular se para encima del panel con un
      sangrado que paso de 92px a 132px como maximo; el hilo .stitch--hero
      baja por la izquierda y lo atraviesa; y el rotulo .board__k, que arranca
      con la escuadra del motivo, ancla el borde por dentro.

   ARITMETICA DEL SOLAPE, para que el celular no toque nunca el titular:
     cruce total = sangrado del celular + subida del panel
     maximo: 132 + 64 = 196px   contra un padding-top maximo de 232px
     minimo:  56 + 28 =  84px   contra un padding-top minimo de 124px
   Los dos casos dejan margen, y se comprueban por calculo en verify.py. */
.board {
  position: relative; z-index: 1;
  --board-rise: clamp(28px, 4vw, 64px);
  margin-top: calc(var(--board-rise) * -1);
  border-radius: clamp(26px, 3.2vw, 44px) clamp(26px, 3.2vw, 44px) 0 0;

  /* --- v10.2: EL PADDING DEJA DE SER UN clamp SUELTO Y SE CALCULA ---
     Los badges tienen que caer siempre a la misma distancia del celular, y
     con un clamp propio eso no se podia: el sangrado del aparato y el padding
     del panel topean en anchos distintos, asi que la separacion variaba entre
     28 y 44px segun el viewport, y a veces el celular casi los tocaba.
     Ahora el padding se DERIVA del cruce. --board-cross repite la misma
     expresion que --phone-bleed en .hero__ar mas la subida del panel, o sea
     exactamente donde termina el celular; y encima se suma --board-gap, que
     es la separacion, una sola y declarada. Medido: 18px a 941, 26px a 1680,
     en vez de 28 y 44.
     v10.4: FALTABA UN TERMINO EN LA CUENTA, y por eso los badges no se
     movian por mas que se bajara --board-gap. Entre el borde inferior del
     celular y el techo del panel no esta solo el cruce: esta ademas el
     `padding-bottom` del propio .hero, que son otros 48-96px. La cuenta
     completa es:
        separacion = padding-top - (sangrado + subida) + padding-bottom del hero
     Con --board-gap en 9px, la separacion REAL medida daba 57px a 390 y
     109px a 1680. Ahora se resta --hero-pb, que espeja el padding-bottom de
     .hero, y la separacion es la declarada y nada mas.
     Si alguien cambia el padding-bottom de .hero y se olvida de --hero-pb,
     verify.py lo detecta: compara las dos expresiones.

     v10.5: con la cuenta ya corregida, --board-gap se sube a 47-51px, que es
     aproximadamente un centimetro por debajo de donde quedo en la v10.3. Es
     el valor que el cliente pidio despues de verlo funcionando.
     Y como la separacion vuelve a ser holgada, el celular RECUPERA su vaiven
     de +-9px: en la v10.3 se habia bajado a +-5px solo porque con 9px de
     separacion el marco llegaba al rotulo en el extremo del ciclo. Esa
     restriccion ya no existe, asi que la amplitud vuelve al valor que la v9.1
     habia elegido a proposito.
     La comprobacion vive en verify.py, seccion L bis.

     v11: LOS BADGES SUBEN, y son DOS movimientos que se suman.
       1. se elimino el rotulo .appdl__k, que era el primer hijo de .appdl:
          los badges suben 26px sin tocar una sola medida;
       2. --board-gap baja de 47-51 a 32-38.
     El badge queda a 32-38px del borde inferior del celular, contra los 73px
     efectivos que tenia antes (47 de separacion + 26 de rotulo): la mitad.
     La holgura contra el vaiven de +-9px del aparato sigue siendo de 23px en
     el peor caso, asi que el marco no llega a tocarlos en ningun punto del
     ciclo. Verificado en el navegador, no por calculo. */
  --board-cross: calc(clamp(56px, 9vw, 132px) + var(--board-rise));
  --board-gap: clamp(32px, 2.4vw, 38px);
  /* espeja el padding-bottom de .hero. Ver la explicacion de arriba: sin
     restarlo, la separacion medida daba 57-109px en vez de 9-13px. */
  --hero-pb: clamp(48px, 6.5vw, 96px);
  padding-top: clamp(88px, 11vw, 140px);
  padding-bottom: clamp(56px, 6vw, 88px);
}
/* v13: .board__k se elimino con su marcado. Era el ultimo rotulo de seccion
   de toda la pagina ("Lo que acabas de ver, paso a paso") y decia con palabras
   la relacion entre dos secciones. Esa relacion ahora la dice la forma: el
   hilo baja del hero y entra en el titular, y el titular vive en la misma
   columna que el del hero. Un rotulo que repite lo que el diseno ya dijo es
   ruido, no ayuda. */
/* v13: ESTE TITULAR BAJA UN ESCALON, Y ES EL UNICO.
   Con el margen al +25% la columna del titular quedo en 447px medidos, y
   "convierte una tienda en datos." a 64px necesita ~955px: se partia en
   CUATRO lineas. El otro pilar de la pagina ("El dato no cierra el trabajo")
   no tiene el problema porque vive en el wrap entero, no en media columna.
   Las dos salidas alternativas se descartaron a proposito:
     - darle mas columna al titular descentraria los badges del celular, que
       estan alineados por aritmetica y no a ojo;
     - dejar que el mecanismo use menos margen lo volveria a desalinear del
       hero, que es justo lo que se acaba de arreglar.
   Bajar la escala es el unico cambio que no rompe nada de eso, y ademas es lo
   que corresponde: 64px en 447px de ancho no era jerarquia, era desborde.
   Sigue siendo el titular mas grande de su seccion. */
.board__h { margin-bottom: 0; font-size: clamp(34px, 3.6vw, 56px); }

/* --- LA CABECERA DEL MECANISMO (v10.1, replanteada en la v10.4) ---
   DOS COLUMNAS PERO TAMBIEN DOS FILAS, y la diferencia importa:
     fila 1, columna 2 -> los badges, debajo del celular
     fila 2, ancho completo -> el rotulo y el titular
   En la v10.1 el titular compartia fila con los badges y por lo tanto vivia
   en una columna de 553px: "convierte una tienda en datos." mide 855px a 64px,
   asi que el titular se partia en el doble de lineas y perdia toda la
   presencia que se le habia dado al subirlo a --xl. Con el titular en su
   propia fila recupera el ancho entero del wrap y vuelve a entrar en dos
   lineas.
   La fila de los badges repite EXACTAMENTE la grilla del hero (1fr / 1.02fr y
   el mismo canalon), asi que cae debajo de la columna del celular, y a partir
   de 1240px se le descuenta por la derecha el ancho de la columna de cifras
   mas su separacion (104 + 88), que es lo que corre al celular hacia la
   izquierda dentro de su columna. Con ese descuento el eje de los badges
   coincide con el del aparato: centrados por aritmetica, no a ojo. */
.board__head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.02fr);
  gap: clamp(26px, 3.4vw, 44px) clamp(32px, 4.5vw, 72px);
  margin-bottom: clamp(32px, 4vw, 52px);
}
/* --- v13: EL TITULAR SUBE A LA FILA 1 Y SE PONE AL LADO DE LOS BADGES ---
   Textual del cliente: "ahora esta muy partido y muy abajo... integrar este
   titulo con el diseno de la flecha de arriba que sale del hero".
   El diagnostico exacto: la fila 1 tenia la COLUMNA 1 VACIA. Los badges viven
   en la columna 2 porque cuelgan del celular, y el titular estaba en una fila
   2 a ancho completo, o sea empujado hacia abajo por la altura entera de los
   badges mas el canalon vertical. Habia un hueco del tamano de media pantalla
   entre el hero y la frase que lo continua.
   Ahora el titular ocupa esa columna 1 vacia. Eso hace tres cosas de una:
     1. sube ~90px y arranca a la misma altura que los badges;
     2. cae bajo la MISMA columna que el titular del hero, asi que las dos
        frases se leen como una sola idea en la misma linea vertical;
     3. el hilo que baja del hero (.stitch--hero, a 16% de la izquierda)
        aterriza dentro del titular en vez de al lado de un espacio vacio.
   La composicion ademas espeja al hero: texto grande a la izquierda, objeto a
   la derecha. Es la misma escena, un piso mas abajo. */
.appdl        { grid-column: 2; grid-row: 1; align-self: start; }
.board__intro { grid-column: 1; grid-row: 1; align-self: start; min-width: 0; }
@media (min-width: 1541px) { .appdl { padding-right: 192px; } }
/* la formula derivada solo vale cuando los badges estan REALMENTE debajo del
   celular, o sea con el hero en dos columnas. Apilado, el celular queda
   centrado y los badges dejan de colgar de el: ahi manda un padding normal,
   que es el declarado como valor base de .board. */
@media (min-width: 1025px) {
  .board { padding-top: calc(var(--board-cross) + var(--board-gap) - var(--hero-pb)); }
}
@media (max-width: 1024px) {
  /* apilado: la cabecera vuelve a una columna, pero los badges SIGUEN siendo
     lo primero, porque arriba de ellos queda el celular, centrado. Se centran
     con el: son su pie de foto, no un item mas de la lista de texto. */
  .board__head { grid-template-columns: 1fr; gap: clamp(26px, 5vw, 40px); }
}

/* --- v13: EL MARCO VUELVE ADENTRO DEL WRAP ---
   Desde la v9 este bloque se salia del wrap a proposito, y el motivo estaba
   escrito: las capturas de dashboard miden 1679px y adentro del wrap de 1240
   el marco daba 1112px, o sea 0.66 de escala, y a esa escala la letra de la
   interfaz no se leia.

   ESE MOTIVO YA NO EXISTE, y hacia rondas que no existia: las rondas v10.x le
   pusieron `max-width: 880px` a .board__media, asi que la captura se dibuja a
   880px ANCHO SEA CUAL SEA el ancho del marco. Sacar el bloque del wrap no le
   estaba dando un pixel a la captura desde entonces; lo unico que hacia era
   que las tres pestanas arrancaran 148px afuera del margen de la pagina.
   Un comentario que explica una decision que ya no aplica es exactamente lo
   que hace que nadie se anime a tocarla.

   v13: se probo darle un contenedor ancho propio (el equivalente al
   .g-container--full de Cabify) y el cliente lo rechazo. Ver abajo. */
.board__ui {
  /* EL MAXIMO ES DURO. Correccion del cliente, y yo lo habia entendido al
     reves: "no se puede sobrepasar ese maximo, si se puede usar menos pero
     nunca pasarlo". El maximo es de ANCHO, no de blanco. Ningun componente
     puede ser mas ancho que el contenedor; puede ser mas angosto.
     Por eso --wrap-full se elimino en vez de quedar sin uso: un token que
     autoriza pasarse del maximo no puede existir en la hoja, porque el
     proximo que lo vea va a suponer que esta permitido.
     Esta es la tercera vez que este bloque se sale del wrap y vuelve.
     OJO: hasta la v14 el argumento contra sacarlo era que la captura estaba
     acotada a 880px, asi que ensanchar el marco no le daba un solo pixel. ESE
     ARGUMENTO YA NO VALE: en la v14 se le quito el tope y la captura usa el
     ancho entero del panel. Hoy el unico motivo es el que importa, y alcanza:
     el maximo es duro por decision del cliente. */
  width: calc(100% - 2 * var(--gut));
  max-width: calc(var(--wrap) - 2 * var(--gut));
  margin-inline: auto;
}

.board__tabs {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px;
  margin-bottom: 14px;
}
/* v13: EL RELLENO OSCURECE, EL CONTORNO ACLARA. Y no es una eleccion
   estetica: es la unica combinacion que mide bien.
   Primero puse los dos en tinte BLANCO, razonando que sobre fondo oscuro se
   separa aclarando. El barrido sobre el DOM real lo desmintio: un velo blanco
   al 10% sube el campo de #0d8375 a rgb(37,143,131), y ahi el rotulo blanco
   de la pastilla cae de 4.65:1 a 3.92:1. Cuatro fallos de contraste, y los
   cuatro los habia creado yo tratando de que se vieran mejor.
   El relleno vuelve a oscurecer, que ademas SUBE el contraste del texto en vez
   de bajarlo. Lo que si se queda en blanco es el CONTORNO: es un elemento
   grafico, le alcanza con 3:1, y es lo que dibuja el borde de la pastilla. */
.board__tab {
  position: relative; overflow: hidden;
  display: flex; align-items: center; gap: 14px;
  padding: 15px 18px;
  border: 1px solid rgba(255,255,255,.30); border-radius: var(--r-surface);
  background: rgba(26,24,21,.16);
  text-align: left;
}
/* La activa es un CHIP DE TINTA y tiene que seguir siendolo. Estaba escrita
   como var(--on-teal) porque ese token valia #1a1815, o sea tinta. Al pasar
   --on-teal a blanco, la pastilla activa quedaba blanca con texto --on-ink
   (casi blanco) encima: invisible. Se fija en --ink-2, que es lo que siempre
   quiso decir. */
.board__tab.is-active { background: var(--ink-2); border-color: var(--ink-2); }
/* sin opacity: bajar el alfa de un numeral lo convierte en texto por debajo
   de 4.5:1. La diferencia de estado la hace el campo, no la transparencia. */
.board__num {
  flex: none;
  font-family: var(--mono); font-size: 15px; font-weight: 700;
  letter-spacing: -.02em; color: var(--on-teal);
  transition: color var(--dur) var(--ease);
}
.board__tab.is-active .board__num { color: var(--teal); }
.board__tabtxt { font-size: 15.5px; font-weight: 600; color: var(--on-teal); letter-spacing: var(--tr-body); }
.board__tab.is-active .board__tabtxt { color: var(--on-ink); }
/* la barra no es adorno: dice cuanto falta para que el tablero avance solo */
.board__fill {
  position: absolute; left: 0; bottom: 0; height: 3px; width: 100%;
  background: var(--teal); transform: scaleX(0); transform-origin: left;
}
.board__tab.is-active .board__fill { transform: scaleX(1); }
.board__ui.is-auto .board__tab.is-active .board__fill { animation: fillBar 7s linear forwards; }
.board__ui.is-auto.is-paused .board__tab.is-active .board__fill { animation-play-state: paused; }
@keyframes fillBar { from { transform: scaleX(0); } to { transform: scaleX(1); } }

/* v9: el texto arriba, la captura debajo y a todo el ancho del marco. En v8
   la captura compartia fila con el texto y se quedaba con 557px, que era la
   raiz del problema de legibilidad. */
/* v13: EL DOBLE DE AIRE ARRIBA Y ABAJO, pedido del cliente. El horizontal se
   queda como estaba: la captura de dashboard necesita todo el ancho que pueda
   conseguir, y ese ya era el motivo por el que .board__ui se sale del wrap. */
/* v14: EL AIRE LATERAL x3, Y EN UNA SOLA VARIABLE.
   Pedido del cliente: "un poco mas de espaciado en los laterales, 3 veces mas
   de lo que hay ahora". Era 18-26px y pasa a 54-78.
   Va como --panel-x y no como valor suelto porque el POZO de la evidencia lo
   compensa con margenes negativos para sangrar a los lados: si los dos numeros
   se escriben aparte, el primero que se toque desalinea el pozo y el error se
   ve como una banda corrida. Con la variable no se pueden desincronizar. */
.board__panel {
  --panel-x: clamp(54px, 6.6vw, 78px);
  display: grid; grid-template-columns: minmax(0, 1fr);
  gap: clamp(18px, 2.2vw, 26px);
  padding: clamp(36px, 4.4vw, 52px) var(--panel-x);
  border-radius: var(--r-surface);
  background: var(--paper);
  color: var(--text);
  box-shadow: 0 18px 44px rgba(26,24,21,.16);
  animation: panelIn .42s var(--ease) both;
}
/* --- v14: EL INTERIOR DEL RECUADRO APROVECHA EL ANCHO ---
   Textual del cliente: "que el recuadro de abajo aproveche mejor el espacio,
   que el texto este mas alineado a la izquierda con algun criterio mejor, para
   que se vea mas prolijo y armonico".

   DE DONDE VENIA. Tres intentos, cada uno arreglando el problema del anterior:
     1. dos columnas (titulo | bajada) y la imagen a ancho completo. TRES ejes
        verticales distintos dentro de una misma tarjeta.
     2. todo centrado. Un solo eje, pero centrar el titulo lo despega del
        bloque y obliga al ojo a volver al medio en cada paso.
     3. todo a bandera izquierda, acotado a 880px y CENTRADO en el panel. Un
        solo eje, pero 880 dentro de un panel de 1160 deja 140px muertos de
        cada lado y la captura se dibuja mas chica de lo que podria.

   EL CRITERIO DE AHORA, que es lo que faltaba:
   **UN SOLO EJE IZQUIERDO, y el ancho sobrante se llena con CONTENIDO.**
     - rotulo, titular, bajada y captura arrancan todos en el borde de
       contenido del panel: una sola linea vertical;
     - el titular y la bajada se acotan a su medida de lectura, que es lo que
       los hace legibles, y NO se estiran a 1160px;
     - el hueco que eso deja a la derecha lo ocupan los metadatos, que antes
       vivian debajo;
     - y la captura deja de estar acotada a 880: usa el ancho entero del panel.
       Para una captura de 1679px eso es 0.69 de escala contra 0.52.
   Mismo contenido, un solo eje, imagen mas grande y cero blanco muerto. */
.board__text {
  display: grid;
  grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr);
  gap: 0 clamp(28px, 3.4vw, 60px);
  align-items: start;
  width: 100%; max-width: none; margin-inline: 0;
  text-align: left;
}
.board__step { grid-column: 1 / -1; }
.board__t    { grid-column: 1; max-width: 20ch; }
.board__d    { grid-column: 1; max-width: 54ch; }

/* --- v13: EL INTERIOR DEL RECUADRO, REHECHO ---
   Textual del cliente: "tiene muy poco diseno, esta muy abandonado y se
   siente poco vivo... quedo ese recuadro como muy blanco y las cosas ahi
   tiradas".
   Tenia razon y el diagnostico es preciso: adentro habia TRES elementos del
   mismo peso visual (un h3 generico, un parrafo y una lista) sobre un fondo
   blanco liso, sin nada que dijera cual se lee primero ni donde empieza y
   termina cada zona. Un recuadro no es una composicion.

   Ahora hay CUATRO cosas que antes no estaban:
     1. EL NUMERO DEL PASO, grande y en mono, como ancla de entrada. Repite el
        de la pestana de arriba, asi que ademas ata el panel con el control
        que lo abrio: al cambiar de pestana el numero confirma donde estas.
     2. UNA ESCALA DE VERDAD entre rotulo (14px), titular (28-34px) y bajada
        (16.5px). Antes el titular era .h3 generico y competia con su bajada.
     3. UN FILETE que cierra la zona de texto y abre la de evidencia. Las dos
        zonas dejan de ser una sola pila suelta.
     4. UN POZO DE SUPERFICIE bajo la captura. Ese es el arreglo del "muy
        blanco": la imagen deja de flotar sobre papel y se apoya en un campo,
        que es el mismo recurso que usa el resto de la pagina. */
.board__step {
  display: flex; align-items: center; gap: 14px;
  font-size: 14px; font-weight: 600; letter-spacing: var(--tr-body);
  color: var(--text-2);
  margin-bottom: 14px;
}
/* el numeral es la pieza de entrada: mono, teal profundo y con la escuadra
   del motivo apoyada arriba a la izquierda, igual que en las cifras */
/* EL CHIP SE PINTA, EN VEZ DE PINTAR LA CIFRA.
   Primera version: numeral en --teal-deep sobre --surface-2. El barrido lo
   tumbo con 3.03:1, y con razon: a 17px hace falta 4.5:1, y la regla 3 del
   proyecto dice explicitamente que --teal-deep NUNCA es texto. Medido,
   tampoco alcanzaba --teal-field (4.02:1 sobre esa superficie).
   La salida no es apagar el numeral, es INVERTIR el chip: el color se lleva al
   fondo, que es donde el teal siempre funciono en esta hoja, y ademas queda
   mas vivo que antes, que es lo que el cliente pidio.
   OJO CON EL NUMERO: con el campo de la v13 (#0d8375) la cifra blanca daba
   4.65:1. Desde la v14b el campo es el teal de marca y da 2.27:1, igual que
   todos los campos teal del sitio. Es la decision del cliente, no un error. */
.board__stepn {
  position: relative;
  display: inline-flex; align-items: center; justify-content: center;
  flex: none; min-width: 42px; height: 42px; padding-inline: 8px;
  border-radius: var(--r-anot);
  background: var(--teal-field); color: var(--on-teal);   /* 2.27:1, ver arriba */
  font-family: var(--mono); font-size: 17px; font-weight: 700;
  letter-spacing: -.02em;
  box-shadow: 0 2px 6px rgba(11,116,103,.22);
}
/* el titular del paso baja del .h3 generico a su propia escala. Sube de
   20-26 a 26-34 y toma el tracking de titulo: es el titular de una tarjeta,
   no un subtitulo de parrafo. */
.board__t {
  margin-top: 0;
  font-size: clamp(24px, 2.3vw, 34px);
  line-height: 1.14; font-weight: 700;
  letter-spacing: var(--tr-title);
  color: var(--text);
  max-width: 24ch;
}
.board__d {
  margin-top: 14px;
  max-width: 62ch;
}

/* --- v13: EL PANEL BLANCO CANCELA EL CONTEXTO DEL CAMPO TEAL ---
   BUG REAL, ENCONTRADO MIRANDO LA PANTALLA Y NO EL CODIGO. El panel de cada
   paso es una tarjeta BLANCA que vive dentro de <section class="ontealfield">,
   asi que hereda las reglas del campo. Mientras --on-teal valia #1a1815 (casi
   negro) eso no se notaba: la palabra resaltada salia oscura sobre blanco por
   casualidad. Al pasar --on-teal a blanco en esta misma ronda,
   `.ontealfield .key` pinto de BLANCO SOBRE BLANCO la palabra clave de cada
   paso, y "personalizá tu campaña" desaparecio de la pagina.
   El barrido de contraste no lo habria encontrado nunca: un texto invisible no
   falla ningun umbral, porque no hay nada que medir. Se vio en una captura.
   La leccion, para la proxima vez que se toque un token de campo: un contenedor
   que cambia de fondo tiene que CANCELAR el contexto que hereda, no confiar en
   que los valores heredados sigan sirviendo. */
/* EL POZO: el campo sobre el que se apoya la evidencia.
   Sangra a los lados hasta el borde del panel con margenes negativos (el
   panel tiene el padding y aca se lo devuelve), asi que se lee como una
   BANDA que ocupa el ancho entero y no como otra caja adentro de la caja.
   Arriba lleva el filete que separa la zona de texto de la de evidencia. */
.board__well {
  position: relative;
  /* los laterales se los devuelve al panel para sangrar hasta el borde, y el
     de abajo tambien: por eso lee --panel-x en vez de repetir el numero */
  margin: clamp(22px, 2.4vw, 32px) calc(var(--panel-x) * -1) calc(clamp(36px, 4.4vw, 52px) * -1);
  padding: clamp(24px, 2.6vw, 34px) var(--panel-x);
  background: var(--surface);
  border-top: 1px solid var(--line);
  border-radius: 0 0 var(--r-surface) var(--r-surface);
}
/* SIN escuadra propia. Se probo una arriba a la izquierda del pozo y quedaba
   flotando: el pozo va a sangre y su esquina no coincide con ningun eje del
   contenido, asi que la marca aterrizaba 113px a la izquierda del texto.
   El campo ya se separa solo con la superficie y el filete; una marca de mas
   en un eje que no existe es ruido, no motivo. */

.board__panel .key    { color: var(--text); }
.board__panel .lead,
.board__panel p       { color: var(--text-2); }
.board__panel .accent { background: var(--teal-field); color: var(--on-teal); }
.board__panel[hidden] { display: none; }
@keyframes panelIn {
  from { opacity: 0; transform: translateY(calc(var(--mv) * 14px)); }
  to   { opacity: 1; transform: none; }
}

/* HISTORIA DEL ANCHO DE ESTA CAPTURA, porque cambio de criterio dos veces:
   v10  ocupaba el ancho entero del panel (1426px medidos) contra un texto de
        13-19px al lado. La imagen pesaba seis veces mas que la palabra y el
        paso dejaba de leerse, asi que se acoto a 880px y se centro.
   v14  el panel se rehizo: el texto ya no es un renglon suelto debajo sino un
        bloque con titular de 34px, bajada y una columna de metadatos al lado.
        Con ese peso tipografico la imagen ya no aplasta nada, y el tope de 880
        solo dejaba aire muerto a los costados. Se saco. */
.board__media {
  position: relative;
  border-radius: var(--r-media); overflow: hidden;
  background: var(--surface-2); border: 1px solid var(--line);
  /* v14: SIN max-width. Estaba acotada a 880px desde la v10, cuando el panel
     era mas angosto y ese tope protegia la proporcion. Hoy el panel mide 1160
     y el tope solo achicaba la captura dejando aire muerto a los costados. La
     proporcion la fija aspect-ratio, no el ancho. */
  width: 100%;
  /* la captura ya tenia sombra propia en el pozo; la transicion es para el
     hover, que la levanta apenas. Solo box-shadow: nada que reflowee. */
  box-shadow: var(--sh-1);
  transition: box-shadow .24s var(--ease);
}
.anot-tag__box { transition: transform .24s var(--ease); }
.board__media img { width: 100%; height: 100%; object-fit: cover; }
/* la captura ENTERA: el marco toma la proporcion nativa del archivo, asi que
   no se recorta absolutamente nada y la pantalla conserva su forma. Lo unico
   que cambia respecto del original es la escala, y es 0.85, no 0.33.
   El marco toma la proporcion nativa del archivo (1679x918 y 1678x920, que
   difieren en un 0.2%), asi que `cover` recorta 2px y nada mas: la captura
   se ve entera y sin banda vacia alrededor. */
.board__media--shot { aspect-ratio: 1679 / 918; }
.board__media--shot img { object-fit: cover; object-position: 50% 50%; }
/* la foto vertical del Znapper no es una captura: no hay texto de interfaz
   que leer, asi que no necesita ancho y se encuadra como cualquier
   fotografia, centrada y en una proporcion que no la achate */
.board__media--tall {
  aspect-ratio: 4 / 3;
  max-width: 720px; margin-inline: auto;
}

/* --- HISTORIA DEL ENCUADRE DE LAS CAPTURAS ---
   v7: la pantalla entera reducida tres veces dentro de un marco de 558px.
       Ilegible, y se leia como imagen barata.
   v8: `object-fit: none`, o sea escala 1:1 y recorte de un cuarto. Legible,
       pero se veia un fragmento suelto que no se entendia como pantalla.
       Textual del cliente: "no se muestran completas o casi completas con la
       forma que corresponde".
   v9: el punto medio, y no se resolvio con encuadre sino con ANCHO. El marco
       sale del wrap (ver .board__ui) y la captura se muestra COMPLETA, sin
       recortar un pixel, a 0.85 de escala en pantallas grandes. La forma de
       pantalla se reconoce y el texto de la interfaz se lee.

   la foto del Znapper es vertical de camara: se encuadra sobre el celular */
.board__media--tall img { object-position: 50% 46%; }
.anot-box--znapper { left: 34%; top: 6%; width: 34%; height: 86%; }
.anot-box--znapper .anot-box__c { width: 13px; height: 13px; }

.board__panel .board__text p { color: var(--text-2); max-width: 62ch; }
/* v13: TERCERA Y ULTIMA LISTA DE PILDORAS QUE SE VA.
   El cliente marco las del riel y las de CONTROLA/ZNAPPEA/MEJORA. Estas son
   el mismo objeto y estaban a dos secciones de distancia, asi que dejarlas
   habria hecho que la pagina tuviera dos lenguajes para lo mismo. Toman el
   del riel: palabras separadas por un punto del teal de marca. */
/* v14: LOS METADATOS SE MUDAN A LA COLUMNA DE LA DERECHA.
   Antes eran un renglon horizontal debajo de la bajada. Ahi cumplian, pero
   eran la cuarta cosa de una pila y competian con la bajada por el mismo lugar
   en la lectura. En la columna de al lado hacen DOS trabajos: siguen
   enumerando, y ocupan el ancho que el texto acotado dejaba vacio.
   El filete pasa de horizontal a vertical, que es lo que corresponde cuando lo
   que separa son columnas y no filas. */
.board__meta {
  grid-column: 2; grid-row: 2 / span 2;
  display: grid; gap: 0;
  align-content: start;
  padding-left: clamp(16px, 1.6vw, 26px);
  border-left: 1px solid var(--line);
}
.board__meta li {
  display: inline-flex; align-items: center;
  color: var(--text-2); font-size: 14px; font-weight: 500;
  letter-spacing: var(--tr-body);
}
/* apilados, el punto separador ya no sirve: en una fila el punto SEPARABA, en
   una columna cada item necesita su propia marca. Vuelve la escuadra. */
.board__meta li { padding: 7px 0; }
.board__meta li::before {
  content: ""; flex: none; margin-right: 10px;
  width: 8px; height: 8px;
  border-top: 1.5px solid var(--teal); border-left: 1.5px solid var(--teal);
}

/* ---------- 11. RIEL DE RUBROS ---------- */
/* v13: la pastilla "Relevando" arrancaba en var(--gut), o sea pegada al borde
   de la ventana y 163px afuera del margen de la pagina.
   Primera correccion: se alineo la izquierda y se dejo sangrar la derecha,
   razonando que un marquee tiene que "seguir de largo".
   Segunda correccion, del cliente: el margen derecho tambien se respeta.
   Y tenia razon: el margen es un maximo para TODOS los lados, no solo para el
   que se mira primero. Lo que hacia falta no era romper el margen, era que el
   corte no se viera duro. Eso lo resuelve la mascara: los rubros aparecen
   DIFUMINADOS desde la derecha en vez de cortarse en seco contra el borde.
   El campo (fondo --surface y filete) sigue yendo a sangre completa: lo que
   se alinea es el CONTENIDO, no la franja. */
.rail {
  position: relative;
  display: flex; align-items: center; gap: 20px;
  padding: 16px var(--margen);
  border-bottom: 1px solid var(--line);
  background: var(--surface);
  overflow: clip;
}
.rail__badge {
  flex: none;
  display: inline-flex; align-items: center; gap: 8px;
  height: 32px; padding: 0 15px;
  border-radius: var(--r-pill);
  background: var(--teal-field); color: var(--on-teal);              /* 7.80:1 */
  font-size: 13px; font-weight: 600;
  letter-spacing: var(--tr-body);
}
/* El riel no lleva punto de estado: ya dice "Relevando" y ya se mueve. Un
   punto de color mas era decoracion, no informacion. Los dos que quedan en la
   pagina (el del hero y el de "Relevando ahora") si dicen algo. */
/* LA MASCARA ES LO QUE HACE QUE EL RIEL SIGA SINTIENDOSE INFINITO AHORA QUE
   RESPETA EL MARGEN. Antes el corte de la derecha caia fuera de la pantalla y
   no se veia; adentro del margen se veria un tajo seco justo donde termina el
   contenido, y el riel dejaria de leerse como algo que viene de mas alla.
   La rampa de salida es LARGA (140px) y la de entrada corta (16px): las
   palabras entran ya formadas por la izquierda y se DESVANECEN hacia la
   derecha, que es el sentido en el que viaja la animacion. Simetricas se
   sentiria una ventana; asimetricas se siente un flujo.
   Es mask y no un degradado encima: asi funciona sobre cualquier fondo y no
   hay que repintar el color del campo. */
.rail__mask {
  position: relative; flex: 1 1 auto; min-width: 0; overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, rgba(0,0,0,0), #000 16px, #000 calc(100% - 140px), rgba(0,0,0,0));
          mask-image: linear-gradient(90deg, rgba(0,0,0,0), #000 16px, #000 calc(100% - 140px), rgba(0,0,0,0));
}
.rail__track { display: flex; width: max-content; animation: railScroll 34s linear infinite; }
.rail__set { display: flex; align-items: center; gap: 0; }
@keyframes railScroll { from { transform: translateX(0); } to { transform: translateX(-50%); } }

/* v13: LOS RUBROS DEJAN DE SER PILDORAS.
   Textual del cliente: "que sea mas bonito y no caer en ese diseno estandard".
   Y era exactamente eso: ocho pastillas con borde de 1px y radio pill, que es
   la forma por defecto de "lista de tags" en cualquier sitio. Ademas competian
   con la unica pastilla que ahi SI dice algo, la de "Relevando", que quedaba
   como una mas entre nueve.
   Ahora son PALABRAS, separadas por un punto del teal de marca. Sin caja, sin
   borde y sin fondo: lo que se lee es la lista de rubros, no ocho botones que
   no se pueden apretar. El punto es el separador y ademas es lo unico de color
   del riel, asi que el teal marca el ritmo de la enumeracion.
   El punto va en --teal (el de marca, #13c1ac) y no en --teal-field: es
   decoracion pura, no lleva texto encima y ahi el brillo del color de marca es
   justamente lo que se quiere. */
.chip {
  display: inline-flex; align-items: center;
  font-size: clamp(15px, 1.35vw, 17px); font-weight: 500;
  letter-spacing: var(--tr-body);
  color: var(--text); white-space: nowrap;
}
.chip::after {
  content: ""; flex: none;
  width: 5px; height: 5px; border-radius: 50%;
  background: var(--teal);
  margin: 0 clamp(14px, 1.4vw, 20px);
}

/* ==========================================================================
   12. QUE SE OBTIENE: EL DETECTOR  ***SECCION INTOCADA EN LA v8***
   Textual del cliente: "me fascina absolutamente, muy buen trabajo alli".
   No se modifico ni una declaracion. Es la vara de calidad del resto.
   El motivo en su forma mas literal y, ademas, interactivo. El recuadro se
   mueve y cambia de tamano SOLO con transform: el contenedor traslada, un
   hijo escala, y las escuadras se contra-escalan con 1/escala para no
   deformarse. Cero width/height animados.
   ========================================================================== */
.det { position: relative; padding-top: var(--sec-pt); padding-bottom: var(--sec-pb); }
.det .lead { margin-bottom: clamp(28px, 3.5vw, 44px); }

.det__ui {
  display: grid;
  grid-template-columns: minmax(0, 320px) minmax(0, 1fr);
  grid-template-areas: "list media" "panel media";
  gap: 18px clamp(24px, 3.4vw, 48px);
  align-content: start;
}
.det__list  { grid-area: list; display: flex; flex-direction: column; gap: 6px; }
.det__media { grid-area: media; }
.det__panels{ grid-area: panel; align-self: start; }

.det__opt {
  display: flex; align-items: center; gap: 14px;
  padding: 13px 16px;
  border: 1px solid var(--line); border-radius: var(--r-input);
  background: var(--surface);
  text-align: left;
}
.det__opt.is-active { background: var(--ink); border-color: var(--ink); }
.det__optt { font-size: 15.5px; font-weight: 600; letter-spacing: var(--tr-body); color: var(--text); }
.det__opt.is-active .det__optt { color: var(--on-ink); }   /* 11.80:1 */
/* la escuadra del motivo hace de vineta: el estado activo no necesita un
   numeral, que ademas era numeracion decorativa */
.det__opt::before {
  content: ""; flex: none; width: 9px; height: 9px;
  border-top: 1.5px solid var(--teal); border-left: 1.5px solid var(--teal);
  transition: border-color var(--dur) var(--ease);
}
/* v13: la escuadra del estado activo iba a --teal y la de reposo a
   --teal-deep (3.30:1), o sea que SELECCIONAR una opcion le bajaba el
   contraste a su marca. Se paso a --teal-field, que entonces era #0d8375 y
   era mas oscuro, para que el activo fuera mas fuerte que el reposo.
   v14b: --teal-field VOLVIO A VALER #13c1ac, o sea LO MISMO QUE --teal, asi
   que esta declaracion ya no cambia nada: la escuadra pinta igual en los dos
   estados. Se conserva por el token, que sigue diciendo "aca hay campo", pero
   NO ES LO QUE SEPARA LOS ESTADOS.
   Lo que de verdad los separa es el fondo: .is-active se pinta de --ink con
   el rotulo en --on-ink (11.80:1), que es un salto que no depende del teal. */
.det__opt.is-active::before { border-color: var(--teal-field); }

.det__media {
  position: relative;
  aspect-ratio: 16 / 9;
  border-radius: var(--r-media);
  overflow: hidden;
  background: var(--ink-2);
  border: 1px solid var(--line-2);
  box-shadow: var(--sh-2);
}
.det__media img { width: 100%; height: 100%; object-fit: cover; }

/* el contenedor SOLO traslada; --bx y --by son porcentajes del ancho y del
   alto de la foto porque el elemento mide exactamente lo que la foto */
.anot-box--live {
  inset: 0;
  background: none;
  --bx: 41%; --by: 21%; --bw: .21; --bh: .13;
  transform: translate(var(--bx), var(--by));
  opacity: 0;
  transition: transform .5s var(--ease-io), opacity .34s var(--ease);
}
/* el recuadro "engancha" cuando la seccion entra: las cuatro escuadras caen
   escalonadas a 60ms, en el sentido de las agujas del reloj. Anuncia que hay
   un detector antes de que nadie toque nada. */
.anot-box--live.is-live { opacity: 1; }
/* el hijo SOLO escala */
.anot-box__rect {
  position: absolute; inset: 0;
  transform-origin: 0 0;
  transform: scale(var(--bw), var(--bh));
  transition: transform .5s var(--ease-io);
  border-radius: var(--r-anot-2);
  /* sin fondo propio: el tinte lo pone .anot-box__fill, que respira. Dos
     capas teal encima daban un verde mas oscuro que el resto del sistema. */
}
/* contra-escala: 2px de borde por 15px de escuadra, exactos en pantalla */
.anot-box--live .anot-box__c {
  transform: scale(calc(1 / var(--bw)), calc(1 / var(--bh)));
  opacity: 0;
  transition: transform .5s var(--ease-io), opacity .26s var(--ease);
}
.anot-box--live.is-live .anot-box__c { opacity: 1; }
.anot-box--live.is-live .anot-box__c--tr { transition-delay: 0s, 60ms; }
.anot-box--live.is-live .anot-box__c--br { transition-delay: 0s, 120ms; }
.anot-box--live.is-live .anot-box__c--bl { transition-delay: 0s, 180ms; }
/* respiracion del relleno: movimiento continuo, solo opacidad */
.anot-box__fill {
  position: absolute; inset: 0; border-radius: var(--r-anot-2);
  background: var(--teal);
  opacity: .10;
  animation: detBreathe 2.8s var(--ease-io) infinite;
}
@keyframes detBreathe { 0%, 100% { opacity: .08; } 50% { opacity: .22; } }
/* la etiqueta cuelga del contenedor, que no escala: nunca se deforma */
.anot-box__tag {
  position: absolute; left: 0; top: 0;
  transform: translateY(-100%) translateY(-9px);
  display: inline-flex; align-items: center;
  padding: 6px 10px; border-radius: var(--r-anot);
  background: var(--ink); color: var(--on-ink);
  font-family: var(--mono); font-size: 10.5px; font-weight: 500;
  letter-spacing: .07em; text-transform: uppercase; white-space: nowrap;
  box-shadow: 0 2px 6px rgba(26,24,21,.22), 0 10px 24px rgba(26,24,21,.18);
}

.det__panel p { color: var(--text-2); font-size: 15.5px; line-height: 1.5; max-width: 42ch; }
.det__panel[hidden] { display: none; }
.det__panel { animation: detPanelIn .3s var(--ease) both; }
@keyframes detPanelIn {
  from { opacity: 0; transform: translateY(calc(var(--mv) * 6px)); }
  to   { opacity: 1; transform: none; }
}
.det__more {
  margin-top: clamp(26px, 3vw, 38px);
  color: var(--text-2); font-size: 15px; max-width: 60ch;
}

/* --- EL HILO QUE CRUZA EL BORDE ENTRE SECCIONES ---
   Nace dentro de una seccion y termina dentro de la siguiente. Sin esto el
   scroll se lee como bloques pegados. */
.stitch {
  position: absolute; left: 50%; bottom: 0;
  width: 1.5px; height: 92px;
  transform: translate(-50%, 46px);
  background: linear-gradient(180deg, rgba(14,154,137,0) 0%, rgba(14,154,137,.85) 55%, rgba(14,154,137,.85) 100%);
  pointer-events: none; z-index: 3;
}
.stitch::after {
  content: ""; position: absolute; left: 50%; bottom: -3.5px;
  width: 8px; height: 8px; margin-left: -4px;
  border-radius: 50%; background: var(--teal);
}
/* el hilo se dibuja de arriba hacia abajo justo cuando se llega al borde:
   se ve literalmente coser una seccion con la siguiente */
/* Esta SI esconde sin depender de `.js`, y esta bien: `.stitch` es el conector
   decorativo entre secciones, va vacio y con aria-hidden. Sin JS conviene que no
   aparezca: una linea suelta sin el recorrido que la justifica se lee como un
   error de render. Es contenido cero. NO "arreglar" agregandole `.js`. */
.stitch.reveal {
  opacity: 0;
  transform: translate(-50%, 46px) scaleY(calc(1 - var(--mv)));
  transform-origin: top center;
  transition: opacity .24s var(--ease), transform .66s var(--ease-io);
}
.stitch.reveal.is-in { opacity: 1; transform: translate(-50%, 46px) scaleY(1); }

/* --- EL HILO QUE ATA EL HERO CON EL MECANISMO (v9) ---
   Textual del cliente: "me gustaria integrar mas esa parte debajo del header
   e integrarla un poco mas con el storytelling de arriba, que no se sienta
   tan partido". El hilo baja del hero, cruza el borde y entra en el campo
   teal. Va en tinta y no en teal porque tiene que leerse en los dos fondos:
   sobre papel la tinta contrasta, y sobre el campo teal tambien (7.80:1).
   Se corre a la izquierda para no chocar con el celular, que baja centrado
   en su propia columna. */
/* v10: el hilo pasa de 108px a 168px porque el panel teal ahora SUBE hasta
   64px por encima del final del hero. Con la altura vieja el hilo quedaba
   entero dentro del campo y dejaba de cruzar nada, que es lo unico que tiene
   que hacer. Empieza 122px arriba del borde del hero y termina 46px abajo. */
/* --- v13: EL HILO SE ALINEA CON EL TITULAR, Y CAMBIA DE COLOR A MITAD ---
   Textual del cliente: "integrar este titulo con el diseno de la flecha de
   arriba que sale del hero".
   (a) POSICION. Estaba en `left: 16%`, un porcentaje puesto a ojo sobre el
       ancho de la ventana. Medido a 1920px caia 69px a la IZQUIERDA del
       titular: el hilo bajaba al lado de la frase, no hacia ella. Ahora se
       calcula desde el borde de contenido del wrap, que es exactamente donde
       arrancan los dos titulares (el del hero y el del mecanismo, verificado:
       los dos en x=373, desalineacion 0). El hilo pasa a ser el eje vertical
       del que cuelgan las dos frases, y eso es lo que las ata.
   (b) COLOR. Iba de transparente a tinta al 55%. Servia cuando el campo de
       abajo era el teal brillante; sobre --teal-field una linea de tinta
       desaparece justo en la mitad que importa, que es la que cruza. Ahora
       vira: TINTA mientras baja por el papel, BLANCO en cuanto entra al campo.
       El viraje ocurre al 73%, que es donde esta el borde entre las dos
       secciones (122px de los 168 quedan arriba). Se lee como un hilo que
       cambia de color al atravesar una tela, que es literalmente lo que hace. */
.stitch--hero {
  left: var(--margen);
  bottom: 0;
  height: 168px;
  background: linear-gradient(180deg,
              rgba(52,49,44,0) 0%,
              rgba(52,49,44,.32) 42%,
              rgba(52,49,44,.55) 70%,
              rgba(255,255,255,.55) 76%,
              rgba(255,255,255,.92) 100%);
}
.stitch--hero::after { background: var(--on-teal); }
@media (max-width: 1024px) { .stitch--hero { display: none; } }

/* ==========================================================================
   13. (ELIMINADA en la v11) LA CADENA COMPLETA
   ==========================================================================
   Aca vivian .chain, .chain__rail, .chain__node, .chain__nn, .chain__nt,
   .chain__stage, .chain__panel, .chain__if, .chain__note y el @keyframes
   chainIn: 137 lineas para la seccion de los siete eslabones, que el
   cliente elimino en la v11.
   El @keyframes fillBar NO se toca: lo comparte .board__fill, que es la
   barra de progreso del mecanismo de tres pasos.
   ========================================================================== */

/* ---------- 14. POR QUE CONFIAR (CAMPO TINTA) ---------- */
.trust { padding-top: var(--sec-pt); padding-bottom: clamp(56px, 6vw, 88px); }
.trust .lead { margin-bottom: clamp(36px, 4.5vw, 56px); }

/* v13: CUATRO COLUMNAS, NO CINCO. Se retiro "847 / Relevando ahora", que era
   la unica cifra que necesitaba una nota al pie para no mentir. Con cuatro,
   cada una gana ancho y ninguna etiqueta se parte en dos renglones. */
.stats__row {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  border-top: 1px solid var(--line-ink);
}
.stat {
  position: relative;
  padding: 26px 22px 4px;
  border-left: 1px solid var(--line-ink);
  display: flex; flex-direction: column; gap: 8px;
}
.stat:first-child { border-left: 0; padding-left: 0; }
/* el motivo, en su version mas chica: la escuadra de deteccion sobre la cifra */
.stat::before {
  content: ""; position: absolute; top: 18px; left: 22px;
  width: 9px; height: 9px;
  border-top: 1.5px solid var(--teal); border-left: 1.5px solid var(--teal);
}
.stat:first-child::before { left: 0; }
.stat__n {
  font-family: var(--mono); font-weight: 700;
  font-size: clamp(30px, 3.4vw, 44px);
  letter-spacing: -.025em; line-height: 1;
  font-variant-numeric: tabular-nums;
  color: var(--teal);           /* 5.70:1 sobre tinta */
}
/* v13: el "<" y la "h" de "<24h". Van mas chicos y en peso 500 porque son
   OPERADOR y UNIDAD, no la cifra: si midieran lo mismo, "<24h" se leeria como
   cuatro caracteres del mismo rango y el ojo tendria que separarlos solo. El
   "<" ademas lleva un pelo de aire a la derecha, que en mono no existe. */
.stat__lt, .stat__su { font-size: .58em; font-weight: 500; }
.stat__lt { margin-right: .06em; }
/* la CIFRA queda en mono (dato tabular); su ETIQUETA, que son palabras, va
   en la familia de texto. El contraste entre las dos familias significa algo:
   mono = numero, Archivo = lengua. */
/* v10: la etiqueta sale de la caja alta y sube a 15px con peso 700. Contra
   una cifra de 44px en mono, el contraste ahora lo hacen la ESCALA y la
   FAMILIA (mono = numero, Archivo = lengua), no el tracking abierto. */
.stat__l {
  display: flex; align-items: center; gap: 8px;
  font-size: 15px; font-weight: 700; letter-spacing: var(--tr-body);
  color: var(--on-ink);                                        /* 11.80:1 */
}
/* v13.5: .stat__live se elimino. Era el punto pulsante de "~847 relevando
   ahora", la cifra que salio de la pagina en la v13 por necesitar una nota al
   pie para no mentir. La regla quedo huerfana dos rondas. */
.stat__c {
  font-size: 14px; line-height: 1.45; color: var(--on-ink-2);   /* 7.17:1 */
  max-width: 26ch; margin-top: 2px;
}

.trust__grid {
  display: grid; grid-template-columns: minmax(0, .8fr) minmax(0, 1fr);
  gap: clamp(28px, 4vw, 56px);
  align-items: center;
  margin-top: clamp(48px, 6vw, 80px);
}
.trust__ph {
  position: relative;
  border-radius: var(--r-media); overflow: hidden; background: var(--ink-2);
  aspect-ratio: 4 / 5;
}
.trust__ph img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 32%; }
.anot-box--bici { left: 34%; top: 48%; width: 32%; height: 15%; }

.trust__lema {
  display: flex; flex-direction: column; color: var(--on-ink);
  font-weight: 700; text-transform: uppercase;
  font-size: clamp(34px, 4.6vw, 62px);
  line-height: .98; letter-spacing: var(--tr-display);
}
.trust__word--teal { color: var(--teal); }
/* v13: LAS SIETE VARIABLES DEJAN DE SER PILDORAS.
   Textual del cliente: "muy feas y normales, no pegan con la personalidad de
   la pagina". Tenia razon y el diagnostico es el mismo que el del riel: siete
   pastillas con contorno son la forma por defecto de "lista de tags", no
   dicen nada de Znapp, y encima estaban debajo de CONTROLA / ZNAPPEA /
   MEJORA, que es el momento de mas voz de toda la pagina.

   La personalidad de esta pagina es LA ESCUADRA DE ANOTACION: la marca que
   deja alguien que senala algo sobre una foto. Esta en el detector, en el
   hero, en las preguntas, en los pasos. Aca es donde mas corresponde, porque
   estas siete cosas son literalmente lo que Znapp SENALA en una gondola.

   Cada variable pasa a ser una linea de indice: escuadra teal, la palabra, y
   un filete que la separa de la siguiente. Dos columnas, para que las siete
   entren sin volverse una lista larga. La tipografia sube de 13.5 a 16px:
   dentro de una pastilla el texto tiene que achicarse para que la pastilla no
   se coma el renglon; sin pastilla, no.
   La escuadra va en --teal de marca: sobre tinta da 5.70:1 y aca el brillo
   del color es deseable, es lo que hace de acento en un campo oscuro. */
.trust__tags {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0 clamp(20px, 2.6vw, 40px);
  margin-top: clamp(28px, 3vw, 38px);
}
.trust__tags li {
  position: relative;
  display: flex; align-items: center; gap: 12px;
  padding: 13px 0;
  border-bottom: 1px solid var(--line-ink);
  font-size: clamp(15px, 1.3vw, 16px); font-weight: 500;
  letter-spacing: var(--tr-body);
  color: var(--on-ink);                                   /* 11.80:1 */
}
.trust__tags li::before {
  content: ""; flex: none;
  width: 9px; height: 9px;
  border-top: 1.5px solid var(--teal); border-left: 1.5px solid var(--teal);
  transition: transform .22s var(--ease);
}
/* la ultima de cada columna cierra sin filete: un filete al final de una lista
   no separa nada de nada, solo subraya el aire */
.trust__tags li:nth-last-child(-n+2) { border-bottom: 0; }

/* ---------- 15. FAQ ----------
   v10.1: era la seccion que el lector se saltaba. Tres causas y tres arreglos:
     (a) NO TENIA CAMPO. Estaba en blanco, entre el campo de tinta de arriba y
         el blanco de abajo, asi que no empezaba en ningun lado. Ahora va sobre
         --surface con filete arriba y abajo, igual que la cadena de eslabones.
     (b) EL TITULAR ERA EL MAS CHICO DE LA PAGINA. Vuelve a la escala base
         (46px de tope). Sigue por debajo de los 64px de los pilares, asi que
         el escalonado se conserva, pero deja de leerse como una nota al pie.
     (c) NO TENIA BAJADA. Un titular solo no da motivo para entrar; una linea
         que dice de que se trata la lista, si.
   Y cada pregunta abre con la escuadra de deteccion, que es el motivo de la
   pagina: la seccion deja de ser la unica sin lenguaje propio. */
.faq {
  padding-top: var(--sec-pt); padding-bottom: var(--sec-pb);
  background: var(--surface);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}
.faq .lead { margin-bottom: clamp(28px, 3.5vw, 44px); }
.acc { display: grid; grid-template-columns: 1fr 1fr; gap: 0 clamp(28px, 4vw, 56px); }
.acc__item { border-top: 1px solid var(--line-2); }
.acc__item:nth-child(3), .acc__item:nth-child(4) { border-bottom: 1px solid var(--line-2); }

/* v12: MODIFICADOR PARA LISTAS LARGAS (la pagina de ayuda).
   La regla de arriba cierra el borde de abajo con nth-child(3) y (4) porque en
   la home el acordeon tiene EXACTAMENTE cuatro preguntas en dos columnas. Con
   ocho, ese borde cae en el medio de la lista y el ultimo par queda abierto.
   En ayuda va a una sola columna a proposito, y no por falta de espacio: son
   listas de largo distinto (ocho y cinco) y en dos columnas la segunda columna
   arranca a media altura, asi que el orden de lectura deja de ser evidente.
   Una columna se lee de arriba a abajo y el borde de cierre es siempre el
   ultimo item, sin importar cuantos haya. */
.acc--lista { grid-template-columns: 1fr; }
.acc--lista .acc__item:nth-child(3), .acc--lista .acc__item:nth-child(4) { border-bottom: 0; }
.acc--lista .acc__item:last-child { border-bottom: 1px solid var(--line-2); }
.acc--lista .acc__a p { max-width: 68ch; }
.acc__q {
  width: 100%; display: flex; align-items: center; gap: 14px;
  padding: 22px 0; text-align: left;
  font-size: clamp(17px, 1.5vw, 19.5px); font-weight: 600; letter-spacing: var(--tr-sub);
  color: var(--text-2);                        /* 6.75:1 sobre --surface */
}
/* la escuadra del motivo hace de vineta y marca el estado abierto */
.acc__q::before {
  content: ""; flex: none;
  width: 10px; height: 10px;
  border-top: 1.5px solid var(--line-2); border-left: 1.5px solid var(--line-2);
  transition: border-color var(--dur) var(--ease);
}
.acc__q[aria-expanded="true"]::before { border-color: var(--teal); }   /* 3.30:1 grafico */
/* el estado abierto se marca con COLOR y con la escuadra, nunca con el peso:
   cambiar el peso reflowea la linea en el mismo cuadro del click */
.acc__q[aria-expanded="true"] { color: var(--text); }
.acc__ico {
  flex: none; margin-left: auto; position: relative;
  width: 32px; height: 32px; border-radius: var(--r-pill);
  border: 1px solid var(--line-2);
  transition: border-color var(--dur) var(--ease), background-color var(--dur) var(--ease), color var(--dur) var(--ease);
}
.acc__ico::before, .acc__ico::after {
  content: ""; position: absolute; top: 50%; left: 50%;
  width: 12px; height: 1.5px; background: currentColor;
  transform: translate(-50%, -50%);
  transition: transform .22s var(--ease), opacity .22s var(--ease);
}
.acc__ico::after { transform: translate(-50%, -50%) rotate(90deg); }
.acc__q[aria-expanded="true"] .acc__ico { border-color: var(--teal); background: var(--teal-field); color: var(--on-teal); }
.acc__q[aria-expanded="true"] .acc__ico::after { transform: translate(-50%, -50%) rotate(0deg); opacity: 0; }

.acc__a { display: grid; grid-template-rows: 1fr; transition: grid-template-rows .28s var(--ease); }
/* v9: la respuesta no solo se despliega, tambien aparece. Sin esto el texto
   se veia recortado a media altura durante los 280ms de la apertura, que es
   justo el cambio brusco que la animacion tenia que evitar. La salida es mas
   rapida que la entrada (160ms contra 240ms), como en toda la hoja. */
.acc__a > div {
  overflow: hidden;
  opacity: 1;
  transition: opacity .16s var(--ease);
}
.acc.is-js .acc__a { grid-template-rows: 0fr; }
.acc.is-js .acc__a > div { opacity: 0; }
.acc.is-js .acc__a.is-open { grid-template-rows: 1fr; }
.acc.is-js .acc__a.is-open > div { opacity: 1; transition-duration: .24s; transition-delay: .06s; }
.acc__a p { color: var(--text-2); padding-bottom: 22px; max-width: 54ch; font-size: 15.5px; }
/* v12: la salida de la FAQ hacia ayuda.html. Va como .chan —el mismo enlace
   subrayado del cierre— y no como boton: el CTA de la pagina es la demo y un
   segundo boton primario acá le competiria. */
.faq__more { margin-top: clamp(26px, 3vw, 38px); }

/* ==========================================================================
   16. DE LA EVIDENCIA A LA ACCION
   Seccion nueva de la v8, y el pedido mas importante de la ronda. Textual:
   "que el lector se lleve que con esta informacion se puede accionar...
   utilizar la evidencia y las medidas para mejorar y medir la evolucion.
   Que sea algo tangible y concreto."

   El arco es evidencia -> accion con el equipo y los partners -> medicion.
   Los tres bloques dicen QUIEN actua, SOBRE QUE y COMO se comprueba. No hay
   ningun caso ni ninguna cifra inventada: se describe el mecanismo, que es
   verificable, y no un resultado, que no lo seria.
   ========================================================================== */
/* --flow-gap vive ACA y no en .flow: lo usan el recorrido Y el retorno, que
   son hermanos. Declarado en .flow, el calc del retorno quedaba invalido y el
   navegador descartaba el padding entero en silencio: el trazo aparecia en el
   borde izquierdo, pisando el texto. Los custom properties no heredan hacia
   los costados. */
.act {
  position: relative; z-index: 2;
  padding-top: var(--sec-pt); padding-bottom: 0;
  --flow-gap: clamp(20px, 2.4vw, 34px);
}
.act .lead { margin-bottom: clamp(34px, 4.2vw, 52px); }

/* ==========================================================================
   EL CIRCUITO QUE CIERRA  (v11, reemplaza a .act__flow / .act__step)
   ==========================================================================
   La version anterior eran tres tarjetas de 482px de alto, del mismo peso y
   sin nada que las encadenara. El argumento de la seccion es que esto es un
   ciclo, y la forma no lo decia: habia que leer los tres parrafos enteros
   para descubrir lo que una figura puede dar en un segundo.

   LA FIGURA, en una frase: tres movimientos encadenados por flechas y una
   linea que vuelve por debajo del tercero al primero.

   EL RETORNO ES LA PIEZA, y esta dibujado con BORDES, no con un SVG ni con
   un grafico importado. Es la misma gramatica que el resto de la hoja: la
   escuadra de deteccion del motivo tambien es un borde, y el hilo que cose
   las secciones tambien. Un elemento con borde inferior, los dos laterales,
   radio en las esquinas de abajo y una punta de flecha en el extremo
   izquierdo alcanza para cerrar el ciclo. Cero peso extra.

   ARITMETICA DEL ALTO, que es lo que el cliente pidio:
     antes  482px las tarjetas + 3 lineas de bajada
     ahora  ~200px la fila + 1 linea de bajada + 46px del retorno
   El argumento no perdio nada: lo que se saco es texto que la figura ya dice.
   ========================================================================== */
/* --- v13.3: EL ENCABEZADO DE LA SECCION, EN DOS COLUMNAS ---
   Titular a la izquierda, bajada a la derecha, apoyados en la MISMA LINEA DE
   BASE (align-items: end) y cerrados por un filete. Tres razones:
     1. jerarquia. Apilados, titular y bajada competian: los dos arrancaban en
        el mismo eje y a la misma altura de lectura. Enfrentados, el titular
        manda por tamano y la bajada se lee como su nota al pie.
     2. orden. El filete convierte dos parrafos sueltos en un bloque con
        principio y fin, que es lo que pedia "mas ordenado a golpe de vista".
     3. alto. Medido, el encabezado baja de 188px a ~130.
   La bajada se despega del fondo del titular con un padding-bottom propio,
   porque alinear por la linea de base de dos textos de 64px y 19px los deja
   opticamente pegados aunque la caja diga que estan alineados. */
.act__head {
  display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
  gap: clamp(24px, 3.4vw, 56px);
  align-items: end;
  /* medido: con padding 38 + margin 48 el encabezado SUBIA de 188 a 231px, o
     sea que el filete se comia lo que ahorraban las dos columnas. Ajustado a
     22/30 queda en ~180 y ademas se lee mas pegado a lo que introduce, que es
     lo que un encabezado tiene que hacer. */
  padding-bottom: clamp(18px, 1.9vw, 22px);
  border-bottom: 1px solid var(--line);
  margin-bottom: clamp(24px, 2.4vw, 30px);
}
.act__head .lead { margin-top: 0; padding-bottom: .35em; max-width: 46ch; }

/* --- v13.2: EL RECORRIDO DE LA EVIDENCIA ---
   Tres pasos, y en los tres aparece LA MISMA TARJETA (.ev) en otro estado.
   Ese es todo el truco: reconocer el mismo objeto tres veces cuenta el viaje
   sin que haya que leer una palabra. Las variantes son --sent y --ok. */
.flow {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--flow-gap);
  counter-reset: flow;
}
/* v13.3: mas compacto. Titulares y bajadas pasaron a una linea, asi que el
   aire interno que los sostenia sobraba. Medido, el recorrido baja de 469 a
   ~370px sin que ninguna pieza quede apretada. */
.flow__s {
  position: relative;
  display: flex; flex-direction: column;
  padding: 20px 20px 22px;
  border: 1px solid var(--line); border-radius: var(--r-surface);
  background: var(--surface);
  transition: transform .24s var(--ease), box-shadow .24s var(--ease), border-color .24s var(--ease);
}
/* la escuadra del motivo abre cada paso */
.flow__s::before {
  content: ""; position: absolute; top: 17px; left: 20px;
  width: 10px; height: 10px;
  border-top: 1.5px solid var(--line-2); border-left: 1.5px solid var(--line-2);
  transition: border-color .24s var(--ease), transform .24s var(--ease);
}
/* la flecha que encadena un paso con el siguiente, centrada en el canalon.
   Nace apagada y se pinta al apuntar el paso: la figura confirma que esta
   viva sin prometer un click, porque no hay ninguno. */
.flow__s:not(:last-child)::after {
  content: ""; position: absolute; z-index: 2;
  top: 50%; right: calc(var(--flow-gap) / -2);
  width: 9px; height: 9px;
  border-top: 1.5px solid var(--line-2); border-right: 1.5px solid var(--line-2);
  transform: translate(50%, -50%) rotate(45deg);
  transition: border-color .24s var(--ease), transform .24s var(--ease);
}
.flow__k {
  display: flex; align-items: center; gap: 12px;
  margin-left: 22px;
  font-size: 13px; font-weight: 600; letter-spacing: var(--tr-micro);
  text-transform: none; color: var(--text-2);
}
/* mismo problema, otra escala: a 13px el teal no llega a 4.5:1 sobre ninguna
   de las superficies. Aca no se invierte el chip porque no hay chip: la cifra
   va en TINTA PLENA, que ademas la separa del rotulo que la acompana
   (--text-2). La jerarquia la hace el color, no el matiz. */
.flow__n {
  font-family: var(--mono); font-size: 13px; font-weight: 700;
  letter-spacing: -.02em; color: var(--text);   /* 11.61:1 sobre superficie */
}
.flow__t {
  margin-top: 10px;
  font-size: clamp(19px, 1.6vw, 23px); font-weight: 700;
  line-height: 1.18; letter-spacing: var(--tr-sub);
  color: var(--text);
}
.flow__d {
  margin-top: 7px;
  font-size: 15px; line-height: 1.5; letter-spacing: var(--tr-body);
  color: var(--text-2);
  max-width: 34ch;
}

/* --- LA TARJETA DE EVIDENCIA, LA PIEZA QUE VIAJA --- */
/* LA TARJETA ES SIEMPRE EL ULTIMO ELEMENTO DE CADA PASO, y eso es una
   decision de composicion, no un detalle. Con `margin-top: auto` los tres
   quedan apoyados en la misma linea de base, asi que el ojo puede cruzar la
   fila horizontalmente y ver LA MISMA PIEZA cambiando de estado. Si cada una
   flotara a la altura que le toca por su contenido, habria que buscarlas.
   Primera version: la tarjeta iba antes de la lista de partners, y ahi el
   `auto` la empujaba contra el parrafo de arriba y la pildora "Para" se
   montaba sobre el texto. */
.ev {
  position: relative;
  display: flex; align-items: center; gap: 12px;
  margin-top: auto;
  padding: 12px;
  /* LAS TRES TARJETAS MIDEN LO MISMO, y eso sostiene la idea entera de la
     seccion: es UNA pieza en tres estados, no tres tarjetas distintas. Si cada
     una toma la altura de su contenido no se reconoce como la misma cosa.
     v16.1: el valor lo fijaba la miniatura del paso 1 (46px + padding = 70px).
     Esa foto ya no esta y los tres pasos pasaron a tener distinta cantidad de
     lineas —tres, cuatro y dos—, asi que medidas quedaban en 90, 109 y 70.
     Ahora el piso lo fija el paso mas alto y los otros dos centran su
     contenido (`align-items: center`, arriba). */
  min-height: 110px;
  border: 1px solid var(--line-2); border-radius: var(--r-anot);
  background: var(--paper);
  box-shadow: var(--sh-1);
  transition: transform .24s var(--ease), box-shadow .24s var(--ease);
}
.ev__b { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.ev__t {
  font-size: 14px; font-weight: 700; letter-spacing: var(--tr-body);
  color: var(--text); font-style: normal;
}
.ev__m {
  font-family: var(--mono); font-size: 11.5px; font-style: normal;
  letter-spacing: var(--tr-micro); color: var(--text-2);   /* 6.75:1 */
}
/* ESTADO 2: la tarjeta lleva un rotulo de destinatario. La pestana de arriba
   es lo unico que cambia respecto del estado 1, y alcanza para que se lea
   como "la misma evidencia, ahora enviada". */
/* v16.1: EL RÓTULO DE CATEGORÍA, arriba de la cifra.
   La tarjeta pasó de mostrar un caso con nombre y apellido ("Producto
   faltante / Genmai · Almagro") a mostrar UN DATO AGREGADO. Un dato necesita
   decir primero DE QUÉ es, y despues cuanto: por eso el rotulo va arriba, en
   mono y en versalitas, y la cifra abajo en el peso fuerte que ya tenia. */
.ev__k {
  font-family: var(--mono); font-size: 10.5px; font-weight: 500;
  letter-spacing: .07em; text-transform: uppercase;
  color: var(--text-2);            /* 6.75:1 */
}

/* LA PISTA DEL EQUIPO. Es lo mas chico y lo mas claro de la tarjeta a
   proposito: el paso no reparte responsabilidades, sugiere por donde sigue.
   Si tuviera el mismo peso que la cifra se leeria como una asignacion, que es
   justo lo que el cliente pidio NO comunicar. */
.ev__hint {
  font-family: var(--mono); font-size: 10.5px; font-style: normal;
  letter-spacing: var(--tr-micro);
  color: var(--text-2);            /* 6.75:1: es secundario por tamano, no por contraste */
}

/* la flecha del antes-y-despues, mas liviana que las dos cifras que une */
.ev__flecha { font-weight: 400; color: var(--text-2); padding: 0 2px; }

.ev--sent { border-color: var(--teal); }
/* ESTADO 3: comprobado. */
.ev--ok { border-color: var(--teal); }
.ev__ok {
  flex: none; margin-left: auto;
  display: inline-flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; border-radius: 50%;
  background: var(--teal-field); color: var(--on-teal);
}

/* los 22px de abajo son la holgura de la pildora "Para", que sobresale 10px
   del borde superior de la tarjeta: sin esto se monta sobre la ultima fila */
.flow__who, .flow__when { margin-bottom: 20px; }
/* los cuatro partners: misma gramatica que .trust__tags */
.flow__who { margin-top: 14px; display: grid; }
.flow__who li {
  display: flex; align-items: center; gap: 10px;
  padding: 7px 0;
  border-bottom: 1px solid var(--line);
  font-size: 14.5px; font-weight: 600; letter-spacing: var(--tr-body);
  color: var(--text);
  transition: color .2s var(--ease), transform .2s var(--ease);
}
.flow__who li:last-child { border-bottom: 0; }
/* la lista del paso 1 enumera lo que TRAE la evidencia, no a quien se le
   manda: mismo objeto, otro peso. Va en secundario para que no compita con
   los cuatro partners, que si son una decision del lector. */
.flow__who--what li { font-weight: 500; color: var(--text-2); }
.flow__who--what li::before { border-color: var(--line-2); }
.flow__who li::before {
  content: ""; flex: none; width: 8px; height: 8px;
  border-top: 1.5px solid var(--teal); border-left: 1.5px solid var(--teal);
  transition: transform .2s var(--ease);
}
/* los dos relevamientos del paso 3 */
.flow__when { margin-top: 14px; display: grid; gap: 9px; }
.flow__when li {
  display: flex; flex-direction: column; gap: 2px;
  padding-left: 16px; position: relative;
  font-size: 14px; letter-spacing: var(--tr-body); color: var(--text);
}
.flow__when li span {
  font-family: var(--mono); font-size: 11px; letter-spacing: var(--tr-micro);
  color: var(--text-2); text-transform: uppercase;
}
.flow__when li b { font-weight: 600; }
/* la linea de tiempo que une los dos relevamientos */
.flow__when li::before {
  content: ""; position: absolute; left: 3px; top: 6px;
  width: 7px; height: 7px; border-radius: 50%;
  border: 1.5px solid var(--teal); background: var(--surface);
}
.flow__when li:first-child::after {
  content: ""; position: absolute; left: 6px; top: 15px; bottom: -12px;
  width: 1.5px; background: var(--line-2);
}

/* v13.4: aca vivian .flow__ret y .flow__reti, la flecha de retorno que decia
   "y el nuevo relevamiento es el punto de partida del proximo".
   La saco el cliente: "no tiene mucho sentido que significa ni de donde sale".
   El diagnostico es correcto y vale anotarlo porque es un error de figura, no
   de codigo: la flecha NACIA del paso 2 y APUNTABA al paso 1, o sea que
   dibujaba un ciclo, mientras el paso 3 ya decia "volves a medir" con su
   linea de tiempo de dos relevamientos. Eran DOS figuras para la misma idea, y
   la que sobraba era la que no tenia con que explicarse.
   En su lugar hay un conector que dice algo que no estaba dicho en ningun
   lado: que la evidencia del paso 2 SALE del Informe PRO. Ver .act__from. */

.act__proof {
  position: relative; z-index: 2;
  /* v13: SIN margin-top. Los 48-80px de arriba existian para separar la
     captura del circuito que vivia encima. Ahora la captura ES la columna
     izquierda de la grilla, y ese margen la hundia 80px respecto de la
     columna de texto: medido, las dos columnas arrancaban desniveladas. */
  margin-bottom: calc(var(--proof-bleed) * -1);
  --proof-bleed: clamp(28px, 3vw, 44px);
}
.act__pf {
  position: relative;
  border-radius: var(--r-media); overflow: hidden;
  background: var(--ink-2);
  border: 1px solid var(--line-2);
  box-shadow: var(--sh-3);
}
.act__pf img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 58%; }
/* el informe ENTERO, con la proporcion nativa del archivo: ni recorte ni
   escala 1:1 sobre un fragmento. Mismo criterio que las capturas del
   tablero. */
.act__pf--ui { background: var(--paper); aspect-ratio: 1678 / 918; }
.act__pf--ui img { object-position: 50% 50%; }
/* la evidencia se apoya SOBRE el informe, en su esquina: la relacion "este
   numero sale de esta foto" queda dicha por la composicion y no por una
   flecha. Es el motivo de anotacion en su escala mas grande. */
.act__stack { position: relative; }
.act__pf--ev {
  position: absolute; right: clamp(14px, 2vw, 28px); bottom: clamp(14px, 2vw, 28px);
  width: clamp(132px, 17vw, 244px);
  aspect-ratio: 3 / 4;
  border: 3px solid var(--paper);
  box-shadow: 0 6px 16px rgba(26,24,21,.22), 0 26px 54px rgba(26,24,21,.26);
  transform: rotate(1.6deg);
}
.act__pf--ev .anot-tag--onmedia { left: 8px; bottom: 8px; }
/* v10: era 9px, el texto mas chico de la pagina. Sube a 10px, que es el piso
   de la escala de anotacion (la misma de .anot-tag__box y .shot__tag). */
.act__pf--ev .anot-tag__box { font-size: 10px; padding: 4px 7px; letter-spacing: .05em; }
/* v10.1: el pie va arriba de la pieza, asi que es margin-BOTTOM. Se sube a
   15px y a --text-2 pleno: ahora abre la pieza en vez de cerrarla. */
/* --- v13.3: EL PIE DICE DE DONDE SALE EL ENTREGABLE ---
   Textual del cliente: "el entregable ese (el paso 2) sale del Informe PRO que
   podes adquirir con Znapp en tus relevamientos".
   Era una linea de texto secundario y por eso se leia como una aclaracion al
   margen. Pasa a ser una TRANSICION en dos tiempos: un rotulo corto con la
   escuadra del motivo, que anuncia el cambio de plano ("esto que viste sale de
   un lugar"), y debajo la frase, en tinta, con el nombre del entregable en
   negrita. Deja de ser un pie de foto y pasa a ser el puente entre el
   recorrido y la prueba. */
/* EL PIE ARRANCA BAJO LA COLUMNA 2, no en el borde de la seccion: es lo que
   lo ata al paso del que cuelga. El calculo es el mismo canalon de la grilla,
   asi que si cambia, el pie lo sigue solo. */
.act__proof figcaption {
  margin-top: clamp(34px, 3.4vw, 48px);
  margin-bottom: clamp(18px, 2vw, 26px);
  padding-left: calc((100% - 2 * var(--flow-gap)) / 3 + var(--flow-gap));
  display: flex; flex-direction: column; gap: 8px;
  max-width: calc((100% - 2 * var(--flow-gap)) / 3 + var(--flow-gap) + 62ch);
}
.act__from {
  position: relative;
  display: inline-flex; align-items: center; gap: 12px;
  font-size: 13px; font-weight: 600; letter-spacing: var(--tr-micro);
  color: var(--text-2);
}
.act__from::before {
  content: ""; flex: none; width: 9px; height: 9px;
  border-top: 1.5px solid var(--teal); border-left: 1.5px solid var(--teal);
}
/* EL CONECTOR: baja del borde inferior del paso 2 y aterriza en la escuadra
   del rotulo. Es una linea recta y no una curva a proposito: no es un ciclo
   ni un desvio, es una PROCEDENCIA. Una linea recta entre dos cosas dice "de
   aca sale aquello" y no admite otra lectura. */
.act__from::after {
  content: ""; position: absolute;
  left: 4px; bottom: 100%;
  width: 1.5px; height: clamp(34px, 3.4vw, 48px);
  background: linear-gradient(180deg, rgba(14,154,137,0) 0%, rgba(14,154,137,.9) 55%, rgba(14,154,137,.9) 100%);
}
.act__what {
  font-size: clamp(16px, 1.4vw, 18.5px); line-height: 1.5;
  letter-spacing: var(--tr-body);
  color: var(--text-2);
}
.act__what b { color: var(--text); font-weight: 700; }

/* ==========================================================================
   17. CIERRE + PIE = UNA SOLA PIEZA (CAMPO TEAL CON FOTO)
   Textual: "quiero que la mejores sustancialmente y la unifiques un poco con
   el footer... capaz que tenga de fondo, con transparencia, una foto de
   gondola de nuestra libreria".

   CONTRASTE SOBRE FOTO, RESUELTO POR CALCULO
   La foto va en `mix-blend-mode: screen` sobre el campo teal. Screen SOLO
   ACLARA: el resultado nunca puede ser mas oscuro que el teal puro. Como
   --on-teal #1a1815 da 7.80:1 sobre #13c1ac, ese 7.80:1 es el PISO
   GARANTIZADO de todo el bloque, caiga el pixel que caiga detras. Es la
   unica manera de poner texto sobre foto sin medir el peor caso pixel a
   pixel, y por eso la regla de la casa es esta y no un scrim a ojo.
   `isolation: isolate` es obligatorio: sin el, la foto blendearia contra lo
   que haya debajo del bloque y la garantia se cae.
   ========================================================================== */
/* ==== CIERRE SOBRE CAMPO DE TINTA (v9.1) ====
   El campo teal con la foto encima daba 7.80:1, o sea que pasaba, pero el
   cliente lo leia mal igual: el problema era el RUIDO de la foto detras de la
   letra, no la luminancia. Y la salida intuitiva no servia: blanco sobre teal
   da 2.27:1 y falla.

   Ahora el bloque entero es campo de tinta y el texto va claro: --on-ink sobre
   --ink-2 da 14.13:1. La foto se conserva como textura, pero en `multiply`,
   que SOLO OSCURECE. Igual que el truco de `screen` sobre teal pero al reves:
   el compuesto nunca puede quedar mas claro que --ink-2, asi que 14.13:1 es un
   piso garantizado por construccion y no una estimacion sobre una foto. */
.finale {
  position: relative; z-index: 1;
  isolation: isolate;
  overflow: clip;
  /* v10.1: mas aire. La tarjeta de evidencia baja hasta 44px dentro de este
     bloque y el titular arrancaba demasiado cerca: todo el cierre se leia
     apretado contra la seccion anterior. */
  padding-top: clamp(150px, 16vw, 216px);
  background: var(--ink-2);
  color: var(--on-ink);
}
/* La foto es una BANDA de 900px de alto como maximo, anclada arriba, no un
   `inset: 0`. El motivo es aritmetico: la foto mide 1600x900 y el bloque de
   cierre es mucho mas alto que 900px, asi que con `cover` sobre todo el alto
   el navegador tendria que AMPLIARLA (hasta 1.2x medido) y se veria blanda.
   Acotada a 1600x900 la escala de render nunca pasa de 1:1. Debajo de la
   banda el campo de tinta sigue solo, y la mascara vertical hace que una cosa
   se funda en la otra sin borde.

   OJO CON EL z-index AQUI, Y ES UN BUG QUE ESTUVO VIVO:
   este contenedor tenia `z-index: 0`. Un elemento posicionado CON z-index
   crea un contexto de apilamiento, y `mix-blend-mode` solo mezcla contra el
   fondo de SU contexto. O sea que la foto no estaba multiplicando contra el
   campo de tinta: se dibujaba en modo normal al 55% sobre la nada y despues
   se componia encima. Por eso se veia brillosa y con peso, y por eso el piso
   de contraste que este archivo daba por garantizado no lo estaba.
   Sin z-index el elemento sigue pintando debajo del contenido (que lleva
   z-index: 1) y la mezcla si alcanza el fondo del bloque. */
.finale__ph {
  position: absolute; left: 0; right: 0; top: 0;
  height: min(900px, 100%);
  pointer-events: none;
}
.finale__ph img {
  width: 100%; height: 100%;
  max-width: 1600px;      /* nunca se amplia: 1600 nativos, 1600 de render */
  margin-inline: auto;
  object-fit: cover; object-position: 50% 58%;
  /* multiply sobre tinta: solo puede oscurecer, nunca aclarar */
  mix-blend-mode: multiply;
  /* v10.1: de .55 a .34. Con la mezcla ya funcionando, .55 dejaba la textura
     de la gondola demasiado presente detras de la letra. La foto tiene que
     dar materia, no competir. */
  opacity: .34;
  /* la foto se disuelve en el campo teal por los cuatro lados: asi el tope
     de 1600px no se lee como un recorte sino como un desvanecido */
  -webkit-mask-image:
    linear-gradient(90deg, rgba(0,0,0,0) 0%, #000 14%, #000 86%, rgba(0,0,0,0) 100%),
    linear-gradient(180deg, #000 0%, #000 46%, rgba(0,0,0,0) 100%);
  -webkit-mask-composite: source-in;
          mask-image:
    linear-gradient(90deg, rgba(0,0,0,0) 0%, #000 14%, #000 86%, rgba(0,0,0,0) 100%),
    linear-gradient(180deg, #000 0%, #000 46%, rgba(0,0,0,0) 100%);
          mask-composite: intersect;
}
.finale > .wrap { position: relative; z-index: 1; }

/* v10.2: la columna de accion estaba `align-items: end`, o sea pegada al
   borde inferior del panel de tinta y alineada a la izquierda de su columna:
   el boton quedaba colgando de una esquina y los dos canales debajo, sueltos.
   Ahora la columna se centra CONTRA el panel, en los dos ejes, y el bloque
   de accion ocupa su ancho: deja de ser un resto al costado del titular y
   pasa a ser la otra mitad de la composicion. */
.finale__top {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, .95fr);
  gap: clamp(28px, 4vw, 56px);
  align-items: center;
  padding-bottom: clamp(48px, 6vw, 76px);
}
/* --- EL TITULAR DEL CIERRE, SOBRE CAMPO DE TINTA (v9) ---
   El cliente percibia mal el contraste del titular. Tenia razon, pero la
   causa no era la luminancia: --on-teal sobre teal da 7.80:1 y pasa de
   sobra. Era el RUIDO VISUAL de la foto de gondola detras de la letra.

   Ojo con la salida intuitiva: blanco sobre teal da 2.27:1 y falla. No se
   puede arreglar aclarando el texto.

   La salida elegida es la primera de las dos validas: el titular pasa a un
   campo de tinta opaco, que es la misma pieza .accent que usa toda la
   pagina, en su version grande. Sobre tinta:
     --on-ink #F7F4EF   11.80:1   el titular y la bajada
     --teal   #13c1ac    5.70:1   la palabra acentuada
   Y como el panel es opaco, la foto deja de estar detras de la letra: el
   ruido desaparece por construccion, no por opacidad a ojo.
   El resto del bloque (botones, pie) sigue sobre campo teal con su piso
   garantizado de 7.80:1 por el `mix-blend-mode: screen`. */
.finale__panel {
  min-width: 0;
  padding: clamp(26px, 3.4vw, 44px) clamp(24px, 3.2vw, 42px) clamp(28px, 3.6vw, 46px);
  border-radius: var(--r-surface);
  /* mas oscuro que el --ink-2 del bloque: separa el titular de la textura de
     la foto sin agregar un borde, y como solo puede oscurecer, el piso de
     contraste del texto claro unicamente mejora. */
  /* v10.1: de .78 a .88. Con el panel mas opaco la foto desaparece del todo
     detras del titular y el contraste sube de 16.35:1 a 16.9:1. */
  background: rgba(20,19,16,.88);
  color: var(--on-ink);
  box-shadow: none;
}
.finale__h {
  font-size: clamp(38px, 5.6vw, 72px);
  line-height: .96; letter-spacing: var(--tr-display); font-weight: 700;
  max-width: 14ch;
  color: var(--on-ink);                       /* 11.80:1 sobre tinta */
}
/* sobre campo tinta el teal SI es texto, y aca ademas es texto grande */
.finale__hi { color: var(--teal); }           /* 5.70:1 */
/* especificidad deliberada: `.ontealfield p` (0,1,1) le ganaria a una sola
   clase, y el panel de tinta tiene que imponerse sobre el campo teal */
.finale__panel .finale__sub {
  margin-top: 22px; max-width: 46ch;
  font-size: clamp(16px, 1.4vw, 19px); font-weight: 400;
  color: var(--on-ink-2);                     /* 7.17:1 sobre tinta */
}
.finale__panel .finale__key { color: var(--on-ink); font-weight: 600; }   /* 11.80:1 */
.finale__act {
  display: flex; flex-direction: column; align-items: center;
  gap: 16px;
  width: 100%;
  text-align: center;
}
/* el boton toma el ancho de su columna hasta un tope: un blanco de 340px es
   un objetivo evidente, uno de 210px al costado del titular es una nota */
.finale__act .btn { width: 100%; max-width: 340px; }
/* UN SOLO CTA. Los dos canales de abajo no compiten: son texto, no boton.
   Centrados debajo del boton se leen como sus alternativas; alineados a la
   izquierda parecian otra cosa. */
.finale__alt { display: flex; flex-wrap: wrap; justify-content: center; gap: 4px 30px; width: 100%; }
.chan {
  display: inline-flex; align-items: center; gap: 9px;
  height: 44px;
  color: var(--text); font-size: 15.5px; font-weight: 500;
  border-bottom: 1px solid rgba(52,49,44,0);
}
.chan svg { color: var(--teal); }
.finale .chan { color: var(--on-ink); border-bottom-color: rgba(247,244,239,.32); }
.finale .chan svg { color: var(--teal); }

/* v10.1: DE CUATRO COLUMNAS A DOS, y con el doble de aire.
   Cuatro columnas para once enlaces convertian el pie en una grilla apretada
   justo donde la pagina tiene que aflojar. Ahora: marca a la izquierda,
   contacto a la derecha, y la navegacion se fue a la banda inferior. El
   canalon pasa de 24px a un maximo de 96px, que es lo que hace la diferencia
   entre "ordenado" y "aireado". */
.finale__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, auto);
  gap: clamp(36px, 6vw, 96px);
  padding-top: clamp(48px, 6vw, 76px);
  padding-bottom: clamp(40px, 5vw, 60px);
  border-top: 1px solid rgba(247,244,239,.16);
}
.finale__brand { display: flex; flex-direction: column; align-items: flex-start; gap: 18px; }
.finale__brand img { width: 158px; height: auto; }
.finale__slogan { font-size: 17px; font-weight: 700; letter-spacing: var(--tr-body); }
.finale__col { display: flex; flex-direction: column; gap: 8px; align-items: flex-start; }
/* el contacto es lo unico que alguien viene a buscar en un pie: se le da
   tamano de dato, no de nota al pie. --on-ink sobre --ink-2: 14.13:1 */
.finale__big {
  font-size: clamp(18px, 1.7vw, 22px); font-weight: 600;
  letter-spacing: var(--tr-sub); line-height: 1.35;
  border-bottom: 1px solid rgba(247,244,239,.22);
  transition: border-color var(--dur) var(--ease);
}
.finale__addr {
  margin-top: 10px; font-size: 14.5px; line-height: 1.5;
  color: var(--on-ink-2);                      /* 8.59:1 sobre --ink-2 */
}
/* v10: los tres rotulos de columna del pie salen de la caja alta con .17em.
   Se resuelven con peso (700) contra los enlaces (500), que es la unica
   diferencia que hace falta en una lista de tres. */
.finale__t {
  font-size: 14px; font-weight: 700; letter-spacing: var(--tr-body);
  margin-bottom: 6px; color: var(--on-ink);                    /* 14.13:1 */
}
.finale__col a {
  font-size: 15px; font-weight: 500;
  border-bottom: 1px solid rgba(247,244,239,0);
  transition: border-color var(--dur) var(--ease);
}
.finale__social { display: flex; gap: 10px; }
.finale__social a {
  display: grid; place-items: center; width: 38px; height: 38px;
  border: 1px solid rgba(247,244,239,.26); border-radius: var(--r-pill);
  border-bottom-width: 1px;
}
.finale__social svg { width: 18px; height: 18px; }
.finale__bottom {
  display: flex; flex-wrap: wrap; gap: 10px 32px; justify-content: space-between;
  align-items: center;
  padding-top: 22px; padding-bottom: 34px;
  border-top: 1px solid rgba(247,244,239,.16);
  font-size: 13.5px;
  color: var(--on-ink-2);                      /* 8.59:1 */
}
/* la navegacion que estaba en columna propia baja aca: cuatro enlaces en una
   linea, que es la forma mas liviana de tenerlos sin abrir un bloque */
.finale__mini { display: flex; flex-wrap: wrap; gap: 6px 24px; }
.finale__mini a {
  font-size: 13.5px; font-weight: 500; color: var(--on-ink);   /* 14.13:1 */
  border-bottom: 1px solid rgba(247,244,239,0);
  transition: border-color var(--dur) var(--ease);
}
/* unico camino a Znappers en la home: discreto, al pie */
.finale__quiet { border-bottom: 1px solid rgba(247,244,239,.30); transition: border-color var(--dur) var(--ease); }

/* LA FILA LEGAL — v28.
   Tres documentos que la app abre desde adentro. Van DEBAJO de la fila del
   copyright y no adentro de ella: no son secciones que alguien quiera
   visitar, son documentos que se consultan, y mezclarlos con "Soy empresa"
   los pondria al mismo nivel de invitacion. Sin borde propio y en el tono
   mas apagado del pie, que es lo que dice "esto esta, no te lo estoy
   ofreciendo". El `padding-top: 0` es porque .finale__bottom ya cerro con
   34px: sumar el suyo daria 34+22 (regla 9 de las de diseño). */
.finale__legal { padding-top: 0; padding-bottom: 34px; }
.finale__legal .finale__mini a {
  font-size: 12.5px; font-weight: 400;
  color: var(--on-ink-2);                      /* 8.59:1, sobre la tinta */
}
.finale__legal .finale__mini a:hover { color: var(--on-ink); border-bottom-color: var(--on-ink); }

/* ---------- 18. MODAL + FORM ---------- */
/* `* { margin: 0 }` mata el `margin:auto` del UA stylesheet de <dialog>:
   hay que reponerlo o el modal queda pegado arriba a la izquierda. */
.modal {
  border: 0; padding: 0; background: rgba(255,255,255,0);
  max-width: min(560px, 92vw); width: 100%; color: var(--text);
  margin: auto;
  /* el cierre era un corte seco: `display` se descarta al instante. Con
     allow-discrete la salida dura 160ms, mas rapida que la entrada de 240ms.
     Donde no haya soporte, el cierre queda como estaba: instantaneo. */
  opacity: 0;
  transition: opacity .16s var(--ease), overlay .16s var(--ease) allow-discrete, display .16s var(--ease) allow-discrete;
}
.modal[open] { opacity: 1; transition-duration: .24s; }
@starting-style { .modal[open] { opacity: 0; } }
.modal::backdrop { background: rgba(38,36,31,.62); backdrop-filter: blur(6px); }
html:has(dialog[open]) { overflow: hidden; }
.modal__box {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-radius: var(--r-surface);
  padding: clamp(24px, 4vw, 36px);
  /* dvh y no vh: en iOS Safari, vh cuenta la barra de direcciones como si no
     existiera y el modal se pasa de pantalla. La linea vh queda de reserva
     para navegadores sin dvh. */
  max-height: 88vh;
  max-height: 88dvh;
  overflow-y: auto;
  box-shadow: var(--sh-3);
}
/* el modal no se ancla a un disparador: escala desde el centro, y nunca
   desde scale(0) */
.modal[open] .modal__box { animation: modalIn .24s var(--ease) both; }
@keyframes modalIn {
  from { opacity: 0; transform: scale(calc(1 - .03 * var(--mv))); }
  to   { opacity: 1; transform: none; }
}
.modal__close {
  position: absolute; top: 16px; right: 16px;
  width: 40px; height: 40px; display: grid; place-items: center;
  border-radius: var(--r-pill); border: 1px solid var(--line-2); color: var(--text-2);
}

/* EL LEAD DEL MODAL NO ES EL LEAD DE UNA SECCION. `.lead` esta calibrado para
   un ancho de pagina: adentro de una caja de 560px mide 19,5px y compite con su
   propio titulo, que mide casi lo mismo, y se come tres renglones.
   Y las tarjetas de canal necesitan aire propio: `.cnl__cs` no trae margen
   porque en /ayuda se lo da la grilla que las contiene. */
.modal__box .lead { font-size: 16px; margin-top: 12px; max-width: none; }
.modal__box .cnl__cs { margin-top: 18px; }

.form { display: grid; gap: 16px; margin-top: 24px; }
.field { display: grid; gap: 8px; }
.field label { font-size: 13.5px; font-weight: 500; color: var(--text); letter-spacing: .01em; }
.field input, .field textarea {
  width: 100%;
  background: var(--paper);
  border: 1px solid var(--line-2);
  border-radius: var(--r-input);
  padding: 13px 15px;
  font-size: 15.5px; color: var(--text);
  transition: border-color var(--dur) var(--ease), background-color var(--dur) var(--ease);
}
.field textarea { resize: vertical; min-height: 96px; }
.field input::placeholder, .field textarea::placeholder { color: var(--text-2); }
.field input:focus, .field textarea:focus { outline: none; border-color: var(--text); }
.field input:focus-visible, .field textarea:focus-visible { outline: 2px solid var(--text); outline-offset: 1px; }
.field.is-invalid input, .field.is-invalid textarea { border-color: var(--err); background: var(--err-tint); }
.field__err { font-size: 13px; color: var(--err); }
.form__submit { width: 100%; margin-top: 6px; }
/* enviar el formulario es un momento raro y de alta carga: es el unico lugar
   de la pagina donde el presupuesto de deleite se gasta de verdad */
.form__done, .form__fail {
  margin-top: 20px; padding: 18px 20px; border-radius: var(--r-input); font-size: 15px;
  animation: doneIn .32s var(--ease) both;
}
@keyframes doneIn {
  from { opacity: 0; transform: translateY(calc(var(--mv) * 8px)) scale(calc(1 - .02 * var(--mv))); }
  to   { opacity: 1; transform: none; }
}
.form__done { background: var(--teal-tint); border: 1px solid var(--teal); color: var(--text); }
.form__fail { background: var(--err-tint); border: 1px solid var(--err); color: var(--err); }

/* ---------- 19. ENTRADA AL VIEWPORT ----------
   Sigue existiendo, pero ya no es la unica animacion de la pagina: es el
   piso, no el argumento. */
/* SOLO SE ESCONDE SI EL JS ESTA VIVO. La marca `.js` la pone un script inline
   en el <head> de cada pagina. Antes esta regla escondia SIEMPRE, y el guard
   era `.no-js`, una clase que ninguna pagina ponia: con el JS caido quedaban 24
   elementos invisibles para siempre. Invertido, el peor caso es que no haya
   animacion de entrada, que es exactamente lo que tiene que pasar. */
.js .reveal {
  opacity: 0;
  transform: translateY(calc(var(--mv) * 18px));
  transition: opacity .55s var(--ease), transform .55s var(--ease);
}
.js .reveal.is-in { opacity: 1; transform: none; }
/* cascada corta: 3 tiempos para un lema de 3 tiempos. Dentro de 30-80ms. */
.trust__word.reveal { transition-duration: .5s; }
.trust__word:nth-child(2).reveal { transition-delay: .07s; }
.trust__word:nth-child(3).reveal { transition-delay: .14s; }
.stat:nth-child(2).reveal { transition-delay: .05s; }
.stat:nth-child(3).reveal { transition-delay: .10s; }
.stat:nth-child(4).reveal { transition-delay: .15s; }
.stat:nth-child(5).reveal { transition-delay: .20s; }

/* entrada del hero al cargar: ordena la lectura del titular al boton */
@keyframes heroIn {
  from { opacity: 0; transform: translateY(calc(var(--mv) * 14px)); }
  to   { opacity: 1; transform: none; }
}
@keyframes phoneIn {
  from { opacity: 0; transform: translateY(calc(var(--mv) * 22px)) scale(calc(1 - .02 * var(--mv))); }
  to   { opacity: 1; transform: none; }
}
/* las tres lineas del titular entran escalonadas a 70ms, asi que la frase se
   arma en el orden en que se lee, y ademas en el orden en que crece la
   escala: chico, mediano, grande. La cascada cuenta el escalonado. */
.hero__w, .hero__sub, .hero__cta { animation: heroIn .6s var(--ease) both; }
.hero__w:nth-child(1) { animation-delay: .06s; }
.hero__w:nth-child(2) { animation-delay: .13s; }
.hero__w:nth-child(3) { animation-delay: .20s; }
.hero__sub { animation-delay: .27s; }
.hero__cta { animation-delay: .33s; }
/* la entrada va en el ESCENARIO y no en el celular: .phone se lleva la
   rotacion 3D y .phone__body la flotacion. Tres transformaciones, tres
   elementos: `transform` no se puede compartir. */
.hero__stage { animation: phoneIn .8s var(--ease) .1s both; }

/* ==========================================================================
   18. AYUDA (ayuda.html)
   ==========================================================================
   Lo único propio de la ruta de ayuda. Todo lo demás —barra, acordeón, pie,
   modal, botones, el motivo de la escuadra, el patrón de pestañas— es el mismo
   componente que usa el resto del sitio, sin una sola regla duplicada.

   ---------------------------------------------------------------------------
   EL PROTAGONISTA DE ESTA RUTA ES EL BUSCADOR, Y ES EL ÚNICO
   ---------------------------------------------------------------------------
   El criterio de tres niveles vive en `:root` y acá se aplica así:

     NIVEL 1 · la caja de búsqueda. Única superficie ELEVADA de la página
              (--paper con sombra sobre --surface), único borde que se enciende,
              y lo único que se mueve por sí solo. Es lo que la ruta vino a dar.
     NIVEL 2 · el titular, el conmutador de público y los títulos de tema.
              Alto contraste y QUIETOS.
     NIVEL 3 · los contadores, el índice de temas, los atajos, la dirección.
              Bajos a propósito. No brillan y no se mueven.

   Por qué el buscador y no el titular: la home vende, y por eso su primera
   pantalla es un argumento. Acá nadie viene a que lo convenzan, viene con una
   pregunta hecha. La primera pantalla tiene que ser el instrumento, no el
   discurso. El titular se queda en el escalón de 46px y baja a soporte.

   ---------------------------------------------------------------------------
   DOS PÚBLICOS QUE NO COMPARTEN NI UNA PREGUNTA
   ---------------------------------------------------------------------------
   Las empresas compran datos; los Znappers ganan dinero completando misiones.
   Hasta la v25 esto eran dos acordeones seguidos, así que un Znapper tenía que
   pasar de largo ocho preguntas de empresa para llegar a la primera suya.
   Ahora son dos paneles excluyentes con un conmutador arriba, y adentro de cada
   uno las preguntas están agrupadas por TEMA con un índice al costado: ocho
   seguidas son una lista larga, ocho en tres temas son tres listas cortas.
   El acordeón deja de ser la única forma de recorrer la página.

   EL RITMO DE CAMPOS, con paddings ASIMÉTRICOS porque el padding-bottom de una
   sección y el padding-top de la siguiente se suman:
     la consulta    --surface   (y la caja del buscador en --paper, elevada)
     las preguntas  --paper     con hairline arriba y abajo
     los canales    --surface-2
     cierre y pie   campo de tinta (el mismo bloque de la home)
   ========================================================================== */

/* `.ayhero` SE BORRO EN LA v26, y su historia explica por que sobrevivio tanto:
   era la entrada de /ayuda, /blog la copio, y despues /ayuda dejo de usarla. Con
   un solo usuario prestado siguio viva tres rondas. En esta v26 las dos rutas se
   rehicieron a la vez —ayuda con `.ayq`, blog con `.blog__head`— y se quedo sin
   ninguno. Una clase que dos paginas se prestan es una clase que nadie borra
   cuando le toca. */

/* ==========================================================================
   18.1  LA CONSULTA
   ========================================================================== */
/* LA ENTRADA VA CENTRADA, Y ES LA ÚNICA DEL SITIO QUE LO HACE.
   No es un capricho de composición: el buscador es el eje de la página, y un
   eje va en el eje. Alineado a la izquierda como el resto de las rutas dejaba
   media pantalla vacía a la derecha —se ve en la captura de la v25— y el
   instrumento quedaba arrinconado en una esquina en vez de presidir. Centrado,
   la primera pantalla es una sola columna que baja titular, promesa,
   herramienta y atajos, y no hay nada más adonde mirar.
   Y de paso le da identidad propia a la ruta, que es lo que el cliente pidió:
   ninguna otra página del sitio se compone así. */
.ayq {
  padding-top: clamp(48px, 5.5vw, 80px);
  padding-bottom: clamp(48px, 5.5vw, 76px);
  background: var(--surface);
  text-align: center;
}
/* la medida la fija cada pieza, no el bloque: con un maximo comun el titular
   se partia en tres lineas para respetar la medida de lectura de la bajada */
.ayq__copy { max-width: 760px; margin-inline: auto; }
.ayq__h { margin-bottom: 0; max-width: none; }
.ayq .lead { margin-top: 20px; max-width: 46ch; margin-inline: auto; }

/* ==========================================================================
   18.2  EL CANAL DE TEMAS Y LOS PANELES
   ========================================================================== */
.ayfaq {
  padding-top: clamp(44px, 5vw, 68px);
  padding-bottom: clamp(56px, 6.5vw, 92px);
  background: var(--paper);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}

/* --- EL CONMUTADOR DE PÚBLICO ---
   Dos pestañas anchas, no dos anclas. La versión anterior tenía dos "puertas"
   que sólo scrolleaban: prometían separar los públicos y no separaban nada,
   porque abajo seguían las trece preguntas seguidas. Acá elegir un lado APAGA
   el otro, que es lo que la palabra "puerta" prometía.

   Lleva el patrón `data-sel` del sitio, o sea que ya trae flechas del teclado y
   los roles de tablist sin una línea de JS nueva. */
.ayseg {
  position: relative;
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(8px, 1vw, 14px);
  margin-bottom: clamp(34px, 4vw, 56px);
}
/* LA PASTILLA QUE VIAJA. Es el ÚNICO desplazamiento de toda la ruta, y dice
   algo concreto: de qué lado del mostrador estás parado. Va detrás de las dos
   pestañas, ocupa exactamente una de las dos celdas y se corre a la otra.
   El ancho sale de la misma expresión que la grilla para que no haya dos
   números que mantener sincronizados a mano. */
.ayseg::before {
  content: ""; position: absolute; z-index: 0;
  top: 0; bottom: 0; left: 0;
  width: calc(50% - clamp(8px, 1vw, 14px) / 2);
  border-radius: var(--r-surface);
  background: var(--surface-2);
  transition: transform .34s var(--ease);
}
.ayseg:has(.ayseg__t:nth-child(2)[aria-selected="true"])::before {
  transform: translateX(calc(100% + clamp(8px, 1vw, 14px)));
}
/* `prefers-reduced-motion` REDUCE, NO ELIMINA: la pastilla sigue estando y
   sigue marcando el lado elegido, lo único que se pierde es el viaje. */
@media (prefers-reduced-motion: reduce) {
  .ayseg::before { transition: none; }
}
.ayseg__t {
  position: relative; z-index: 1;
  display: block; text-align: left;
  padding: clamp(18px, 2vw, 24px) clamp(18px, 2vw, 24px) clamp(15px, 1.7vw, 20px);
  /* LAS DOS LLEVAN BORDE SIEMPRE. Sin él, la pestaña no elegida no tenía
     ninguna forma —era texto suelto sobre el papel— y el conmutador no se leía
     como un control: se leía como un título con una tarjeta al lado. */
  border: 1px solid var(--line); border-radius: var(--r-surface);
  background: none; cursor: pointer;
  transition: border-color .22s var(--ease-out);
}
.ayseg__t[aria-selected="true"] { border-color: var(--line-2); }
.ayseg__t:hover { border-color: var(--line-2); }
.ayseg__k {
  display: block;
  font-size: clamp(18px, 1.6vw, 22px); font-weight: 700;
  letter-spacing: var(--tr-sub); line-height: 1.2;
  color: var(--text-2);                            /* 8.05:1 sobre --paper */
  transition: color .22s var(--ease-out);
}
.ayseg__t[aria-selected="true"] .ayseg__k { color: var(--text); }   /* 11.65:1 */
.ayseg__d {
  display: block; margin-top: 9px; max-width: 34ch;
  font-size: 15px; line-height: 1.45; letter-spacing: var(--tr-body);
  color: var(--text-2);
}
/* el contador va en --text-2 y no en --text-3: a 13px hace falta 4.5:1 y el
   terciario da 3.48:1 sobre superficie. La jerarquía la construye el tamaño */
.ayseg__n {
  display: inline-flex; align-items: center; gap: 8px; margin-top: 14px;
  font-family: var(--mono); font-size: 12.5px; letter-spacing: var(--tr-micro);
  color: var(--text-2);
}
.ayseg__n::before {
  content: ""; width: 15px; height: 1.5px; background: var(--line-2);
  transition: background-color .22s var(--ease-out);
}
.ayseg__t[aria-selected="true"] .ayseg__n::before { background: var(--teal); }
.ayseg__t:hover .ayseg__k { color: var(--text); }

/* --- EL PANEL DE UN PÚBLICO ---
   `display` EXPLÍCITO CONTRA EL ATRIBUTO `hidden`: el script apaga el panel que
   no está elegido poniéndole `hidden`, y cualquier regla de autor con `display`
   le gana. Sin la línea de abajo se verían los dos paneles a la vez. Es la
   quinta vez que esta trampa aparece en este proyecto, y `verificar.py` la
   revisa para todo lo que sea `role="tabpanel"`. */
.aypan[hidden] { display: none; }
.aypan {
  display: grid;
  grid-template-columns: minmax(0, 268px) minmax(0, 1fr);
  gap: clamp(28px, 4vw, 64px);
  align-items: start;
}
.aypan:focus-visible { outline-offset: 8px; }

/* el rótulo del público. Mientras se navega es redundante con la pestaña
   elegida, así que no está; aparece SOLO durante una búsqueda, que es cuando
   los dos paneles se muestran juntos y hay que saber de quién es cada bloque */
.aypan__h {
  display: none;
  grid-column: 1 / -1;
  margin-bottom: 4px;
  font-family: var(--mono); font-size: 11.5px; font-weight: 700;
  letter-spacing: .1em; text-transform: uppercase; color: var(--text-2);
}
.aypan__cuerpo { min-width: 0; }

/* --- EL ÍNDICE DE TEMAS ---
   NIVEL 3, y es la segunda forma de recorrer la página: se queda pegado al
   scroll y deja ver de un vistazo cuántos temas hay y cuánto pesa cada uno, sin
   abrir nada. La queja del cliente era que el acordeón fuera la única entrada;
   esto lo contesta sin agregar contenido que no existe. */
.ayidx {
  position: sticky; top: calc(var(--navh) + 24px);
  display: grid; gap: 2px;
  padding: 16px 12px 14px;
  border-radius: var(--r-surface);
  background: var(--surface);
}
.ayidx__k {
  margin: 0 0 8px 14px;
  font-family: var(--mono); font-size: 11.5px; font-weight: 700;
  letter-spacing: .1em; text-transform: uppercase; color: var(--text-2);
}
.ayidx__a {
  display: flex; align-items: baseline; gap: 10px;
  min-height: 44px; padding: 11px 12px 11px 14px;
  border-radius: var(--r-input);
  font-size: 15px; line-height: 1.35; letter-spacing: var(--tr-body);
  color: var(--text-2); text-decoration: none;
  transition: background-color .2s var(--ease-out), color .2s var(--ease-out);
}
.ayidx__a::before {
  content: ""; flex: none; align-self: center;
  width: 7px; height: 7px;
  border-top: 1.5px solid var(--line-2); border-left: 1.5px solid var(--line-2);
  transition: border-color .2s var(--ease-out);
}
/* el contador de cada tema va en --text-2 y no en --text-3: a 12px hace falta
   4.5:1 y el terciario da 3.71:1 sobre papel. La jerarquía contra el nombre del
   tema la marca el TAMAÑO, no el gris. */
.ayidx__a span {
  margin-left: auto; flex: none;
  font-family: var(--mono); font-size: 12px; color: var(--text-2);
}
.ayidx__a:hover, .ayidx__a:focus-visible {
  background: var(--paper); color: var(--text);
}
.ayidx__a:hover::before, .ayidx__a:focus-visible::before { border-color: var(--teal); }

/* --- UN TEMA --- */
.aygr { scroll-margin-top: calc(var(--navh) + 20px); }
.aygr + .aygr { margin-top: clamp(30px, 3.4vw, 46px); }
.aygr[hidden] { display: none; }
/* UN TEMA ESCONDIDO SIGUE SIENDO UN HERMANO. `+` no distingue entre "el
   anterior" y "el anterior VISIBLE", así que al filtrar, el primer tema con
   resultados heredaba los 46px de separación de un tema que ya no está y
   quedaba un hueco de la nada abajo del rótulo del público. Se ve como que
   falta algo, y no falta nada. */
.aygr[hidden] + .aygr { margin-top: 0; }
.aygr__h {
  display: flex; align-items: baseline; gap: 12px;
  padding-bottom: 12px;
  font-size: clamp(17px, 1.5vw, 20px); font-weight: 700;
  letter-spacing: var(--tr-sub); color: var(--text);
}
/* de qué público es este tema. Sólo hace falta durante una búsqueda, cuando los
   resultados de los dos públicos se muestran mezclados */
.aygr__who {
  display: none; margin-left: auto; flex: none;
  padding: 3px 9px 4px; border-radius: var(--r-pill);
  background: var(--surface-2);
  font-family: var(--mono); font-size: 11px; font-weight: 500;
  letter-spacing: var(--tr-micro); text-transform: none; color: var(--text-2);
}

/* --- MIENTRAS SE BUSCA ---
   El conmutador y los índices contradicen al buscador: dicen "8 preguntas" y
   "3" cuando el filtro ya dejó dos. Se apagan, los dos paneles se muestran
   juntos y cada tema declara de quién es. */
/* .42 y no .3: apagado tiene que leerse como apagado, no como roto. A .3 el
   rótulo de la pestaña quedaba en 2,4:1 y no se distinguía del fondo. */
.ayfaq.is-buscando .ayseg { opacity: .42; pointer-events: none; }
.ayfaq.is-buscando .ayidx { display: none; }
/* el gap de la grilla de dos columnas es aire LATERAL; en una sola columna se
   vuelve aire vertical entre el rotulo del publico y su primera pregunta, y a
   60px eso deja un hueco que se lee como que falta algo */
.ayfaq.is-buscando .aypan { grid-template-columns: minmax(0, 1fr); gap: 16px; }
.ayfaq.is-buscando .aypan + .aypan { margin-top: clamp(30px, 3.4vw, 46px); }
.ayfaq.is-buscando .aypan__h { display: block; }
.ayfaq.is-buscando .aygr__who { display: inline-block; }
.ayseg { transition: opacity .24s var(--ease-out); }

/* --- LO QUE VIVE ADENTRO DE UNA RESPUESTA --- */
/* la respuesta de "cómo empiezo" termina en los badges de las tiendas: se
   alinean a la izquierda con el texto, no centrados como cuelgan del celular
   en la home, porque acá cuelgan de un párrafo */
.ayfaq .appdl { align-items: flex-start; margin-top: 18px; padding-bottom: 22px; padding-right: 0; }
.ayfaq .appdl__bs { justify-content: flex-start; }

/* listas dentro de una respuesta: la de las seis dimensiones y la de los siete
   pasos de una misión. Misma medida de texto que el párrafo del acordeón. */
.aylist, .aysteps {
  margin-top: 12px; padding-bottom: 22px;
  display: grid; gap: 7px;
  max-width: 68ch;
  font-size: 15.5px; line-height: 1.5; letter-spacing: var(--tr-body);
  color: var(--text-2);
}
.aylist li, .aysteps li { padding-left: 20px; position: relative; }
.aylist b, .aysteps b { color: var(--text); font-weight: 600; }
/* la viñeta es la escuadra del motivo, reducida a la escala de una línea */
.aylist li::before {
  content: ""; position: absolute; left: 0; top: .52em;
  width: 7px; height: 7px;
  border-top: 1.5px solid var(--teal); border-left: 1.5px solid var(--teal);
}
/* los pasos SÍ van numerados: el orden es la mitad de lo que la respuesta
   dice, y una viñeta igual para los siete lo perdería */
.aysteps { counter-reset: paso; padding-left: 0; list-style: none; }
.aysteps li { counter-increment: paso; padding-left: 26px; }
.aysteps li::before {
  content: counter(paso, decimal-leading-zero);
  position: absolute; left: 0; top: 0;
  font-family: var(--mono); font-size: 12px; line-height: 1.95;
  color: var(--teal);                        /* 3.34:1, y es cifra de apoyo */
  font-weight: 500;
}

/* ==========================================================================
   18.3  CANALES: LO QUE NO TIENE RESPUESTA GENERAL
   ==========================================================================
   Dos columnas: a la izquierda las cinco preguntas que no tienen respuesta
   general, a la derecha los cuatro canales que sí las contestan. Enfrentadas,
   la sección se lee como una sola frase —esto no está escrito, esto es por
   dónde se pregunta— en vez de como dos bloques apilados sin relación. */
.cnl {
  padding-top: clamp(60px, 6.5vw, 92px);
  padding-bottom: clamp(56px, 6.5vw, 88px);
  background: var(--surface-2);
}
.cnl__grid {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 430px);
  gap: clamp(32px, 5vw, 72px); align-items: start;
}
.cnl__col { min-width: 0; }
.cnl .lead { margin-bottom: clamp(24px, 3vw, 34px); }
/* las cinco preguntas sin respuesta general. Van como lista y no como acordeón
   a propósito: un acordeón promete que adentro hay una respuesta. */
.cnl__qs { display: grid; gap: 0; max-width: 46ch; }
.cnl__qs li {
  position: relative;
  padding: 13px 0 13px 24px;
  border-bottom: 1px solid var(--line-2);
  font-size: 15.5px; line-height: 1.45; letter-spacing: var(--tr-body);
  color: var(--text-2);                          /* 7.45:1 sobre --surface-2 */
}
.cnl__qs li:first-child { border-top: 1px solid var(--line-2); }
/* la escuadra de estas cinco va APAGADA y no en teal, que es lo contrario de
   lo que hace .aylist: el teal marca lo que tiene respuesta, y estas no la
   tienen. Es --text-3 y no --line-2 porque el hairline no llega al umbral de
   3:1 de elemento gráfico. */
.cnl__qs li::before {
  content: ""; position: absolute; left: 0; top: 1.28em;
  width: 8px; height: 8px;
  border-top: 1.5px solid var(--text-3); border-left: 1.5px solid var(--text-3);
}

.cnl__cs { display: grid; gap: 10px; min-width: 0; }
.cnl__c {
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center; gap: 14px;
  width: 100%; min-height: 44px;
  padding: 15px 16px; text-align: left; text-decoration: none;
  border: 1px solid var(--line); border-radius: var(--r-surface);
  background: var(--paper);
  transition: border-color .22s var(--ease-out), box-shadow .22s var(--ease-out);
}
.cnl__c:hover { border-color: var(--line-2); box-shadow: var(--sh-2); }
/* el disco del canal es campo teal con el glifo en --on-teal: es la misma
   decisión de marca del resto del sitio, y acá además ancla los cuatro canales
   como una familia sin necesidad de un rótulo que los agrupe */
.cnl__ico {
  flex: none; display: grid; place-items: center;
  width: 40px; height: 40px; border-radius: 50%;
  background: var(--teal-field); color: var(--on-teal);
}
.cnl__t { display: grid; gap: 2px; min-width: 0; }
.cnl__k {
  font-size: 15.5px; font-weight: 700; letter-spacing: var(--tr-sub);
  color: var(--text);
}
.cnl__v {
  font-size: 14px; line-height: 1.4; letter-spacing: var(--tr-micro);
  color: var(--text-2);                            /* 8.05:1 sobre papel */
  word-break: break-word;
}
.cnl__go {
  flex: none; display: grid; place-items: center;
  width: 30px; height: 30px; border-radius: 50%;
  color: var(--text-3);
  transition: color .22s var(--ease-out), transform .22s var(--ease-out);
}
.cnl__c:hover .cnl__go { color: var(--text); transform: translateX(calc(var(--mv) * 3px)); }
.cnl__addr {
  margin-top: 14px;
  font-size: 13.5px; line-height: 1.5; letter-spacing: var(--tr-micro);
  color: var(--text-2);
}

/* ==========================================================================
   18.4  RESPONSIVE PROPIO DE LA RUTA
   Va acá y no en la sección 20 a propósito: el bloque de allá abajo es del
   sitio entero, y una regla de ayuda metida ahí es una regla que nadie
   encuentra cuando busca por qué esta página se ve así.
   ========================================================================== */
/* EL CORTE SIGUE A LA ESCALERA DEL CONTENEDOR, no a un número de viewport: en
   1024 el contenedor cae de 896 a 576px, y una grilla de 250 + 576 no entra. */
@media (max-width: 1024px) {
  .aypan { grid-template-columns: minmax(0, 1fr); }
  /* sin columna al lado, el índice deja de estar pegado y pasa a ser una fila
     de pastillas: sigue diciendo cuántos temas hay y sigue llevando a cada uno.
     ENVUELVE, NO SE DESPLAZA. La primera versión era una tira con scroll
     horizontal y a 390px cortaba el tercer tema a mitad de palabra: una pieza
     cortada se lee como un error de maquetación, no como "seguí desplazando",
     porque no hay ninguna otra tira desplazable en esta página que lo enseñe. */
  .ayidx {
    position: static; padding: 0; background: none;
    display: flex; flex-wrap: wrap; gap: 8px;
    margin-bottom: clamp(20px, 3vw, 28px);
  }
  .ayidx__k { display: none; }
  .ayidx__a {
    border: 1px solid var(--line); border-radius: var(--r-pill);
    padding: 11px 15px;
  }
  .ayidx__a span { margin-left: 4px; }
  .ayseg { grid-template-columns: minmax(0, 1fr); }
  .ayseg::before { width: 100%; height: calc(50% - clamp(8px, 1vw, 14px) / 2); bottom: auto; }
  .ayseg:has(.ayseg__t:nth-child(2)[aria-selected="true"])::before {
    transform: translateY(calc(100% + clamp(8px, 1vw, 14px)));
  }
  .ayseg__d { max-width: none; }
  .cnl__grid { grid-template-columns: minmax(0, 1fr); }
}

/* ---------- 20. RESPONSIVE ---------- */
/* la columna de cifras de la referencia solo aparece cuando entra sin
   apretar el titular. Por debajo de 1240px no entra: los mismos numeros
   viven completos, y con su explicacion, en "Por que confiar". */
@media (min-width: 1541px) {
  .hero__figs { display: grid; gap: clamp(16px, 1.6vw, 24px); flex: none; width: 104px; }
  .hero__figs li { padding-left: 13px; border-left: 2px solid var(--line-2); }
  /* La v9 resolvia la convivencia con la columna de cifras metiendo las
     piezas de la derecha DENTRO del celular. Medido, eso las dejaba tapando
     mas de la mitad de la pantalla, y una tarjeta que tapa la interfaz deja
     de leerse como algo que salio de ella.
     Se resuelve al reves: la columna de cifras se separa 88px, que es
     exactamente lo que necesitan la metrica y la recompensa para volar fuera
     del marco sin llegar a los numeros. Entra: 250 del celular + 88 de
     separacion + 104 de cifras = 442px contra una columna de 564px. */
  .hero__scene { gap: 88px; }
}
@media (max-width: 1140px) {
  .nav__links { display: none; }
  .nav__actions .btn--ghost { display: none; }
  .nav__burger { display: flex; }
  /* sin la zona central, la barra vuelve a ser marca | acciones */
  .nav__inner { grid-template-columns: auto 1fr; gap: 14px; }
  .nav__actions { grid-column: 2; }
  .det__ui { grid-template-columns: minmax(0, 270px) minmax(0, 1fr); }
}
@media (max-width: 1040px) {
  .stats__row { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .stat { border-top: 1px solid var(--line-ink); padding-top: 22px; }
  .stat:nth-child(-n+3) { border-top: 0; }
  .stat:nth-child(3n+1) { border-left: 0; padding-left: 0; }
  .stat:nth-child(3n+1)::before { left: 0; }
}
/* v13: ESTE CORTE ERA 940px Y AHORA ES 1024px, Y NO ES UN AJUSTE FINO.
   Con la escalera de contenedores de Cabify, entre 768 y 1024 el contenedor
   mide 576px. Un corte en 940 dejaba TODAS las grillas de dos columnas
   (el hero, el detector, la evidencia, el acordeon, las puertas de ayuda)
   metidas en 576px entre 941 y 1024: columnas de 250px con titulares de 66px
   adentro. El breakpoint tiene que seguir a la ESCALERA DEL CONTENEDOR, no a
   un numero de viewport elegido antes de que la escalera existiera.
   Misma razon para los otros dos que se movieron:
     min-width 941  -> 1025   (la formula del padding del mecanismo, que solo
                               vale con el hero en dos columnas)
     min-width 1240 -> 1541   (la columna de cifras del hero: recien aparece
                               cuando el contenedor sube a 1200 y hay lugar) */
@media (max-width: 1024px) {
  /* el hero se apila: el titular arriba, el celular debajo y centrado */
  .hero__grid { grid-template-columns: 1fr; gap: clamp(30px, 6vw, 48px); }
  .hero__h1 { font-size: clamp(46px, 8.4vw, 66px); }
  .hero__w--mid { font-size: clamp(34px, 6.3vw, 50px); }
  .hero__w--eyeb { font-size: clamp(17px, 2.4vw, 22px); }
  .hero__ar { --phone-bleed: clamp(40px, 7vw, 64px); width: clamp(214px, 44vw, 268px); }

  /* apilado: el celular se centra y los badges dejan de colgar de el, asi que
     se alinean con el resto del texto y el padding vuelve a ser el base */
  .board { --board-cross: calc(clamp(40px, 7vw, 64px) + var(--board-rise)); }
  .appdl        { grid-column: 1; grid-row: 1; }
  .board__intro { grid-column: 1; grid-row: 2; }
  .board__tabs { grid-template-columns: 1fr; gap: 8px; }
  /* apilado el panel vuelve a una columna, y el filete de los metadatos gira
     de vertical a horizontal: ya no hay una columna al lado que separar */
  /* a 576px de contenedor, 54px de cada lado se comen el 19% del panel. El
     aire lateral vuelve a su valor chico: el pedido de x3 era para escritorio,
     donde ese ancho sobra. */
  .board__panel { --panel-x: clamp(18px, 3.4vw, 30px); }
  .board__text { grid-template-columns: 1fr; }
  .board__meta {
    grid-column: 1; grid-row: auto;
    padding-left: 0; border-left: 0;
    margin-top: 18px; padding-top: 14px; border-top: 1px solid var(--line);
  }
  .board__text .h3 { grid-column: 1; grid-row: auto; max-width: none; }
  .board__text > p, .board__meta { grid-column: 1; grid-row: auto; }

  .det__ui { grid-template-columns: 1fr; grid-template-areas: "media" "list" "panel"; gap: 20px; }
  .det__list { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }

  .trust__grid { grid-template-columns: 1fr; }
  /* las siete variables pasan a una columna: en dos, con el ancho de un
     telefono, "Exhibiciones y POP" se parte en dos renglones y la lista deja
     de leerse como lista. Y el filete de cierre vuelve a ser solo el ultimo. */
  .trust__tags { grid-template-columns: 1fr; }
  .trust__tags li:nth-last-child(-n+2) { border-bottom: 1px solid var(--line-ink); }
  .trust__tags li:last-child { border-bottom: 0; }
  /* 3/4 y no 16/10: con 16/10 el recorte se comia la cabeza y dejaba solo
     la bici. A 3/4 la leyenda de la remera queda en el mismo lugar relativo
     que en 4/5, asi que el recuadro no necesita otras coordenadas. */
  .trust__ph { aspect-ratio: 3 / 4; }

  .acc { grid-template-columns: 1fr; }
  .acc__item:nth-child(3) { border-bottom: 0; }

  /* v11: EL CIRCUITO APILADO. La figura no se pierde, gira 90 grados.
     Las flechas que encadenaban de izquierda a derecha ahora bajan (rotate
     135deg en vez de 45), y el retorno deja de ser una U horizontal: en una
     columna, "volver al primero" es SUBIR. Se convierte en una linea vertical
     corta con la misma punta de flecha apuntando hacia arriba, centrada bajo
     el tercer movimiento. Mismo argumento, mismo lenguaje grafico, otro eje. */
  /* v13: la seccion de la accion se apila. La captura primero, porque es el
     entregable y es el argumento; la lista de destinatarios debajo. */
  /* v13.2: EL RECORRIDO SE APILA, Y LAS FLECHAS GIRAN 90 GRADOS.
     La figura no se pierde al apilar: la flecha que iba de izquierda a derecha
     ahora baja. Si se ocultaran, los tres pasos quedarian como tres tarjetas
     sueltas y se perderia justo lo que la seccion tiene que decir, que es que
     una cosa lleva a la siguiente. */
  /* --- v13.6: LAS RUTAS, APILADAS ---
     Todas las grillas de las paginas de ruta bajan de golpe en el mismo corte
     que el resto, que es donde el contenedor cae a 576px. Los siete eslabones
     son el caso extremo: en una fila de 7 dentro de 576px cada uno mediria
     72px, o sea menos que una palabra. Pasan a dos columnas y la flecha que
     los encadena gira 90 grados, igual que el recorrido de la home. */

  /* la cadena: siete eslabones en 576px son columnas de 70px, ilegibles. Pasa
     a tira desplazable con un ancho minimo por eslabon, que ademas deja ver
     que la cadena SIGUE hacia la derecha. El panel se apila. */

  .flow { grid-template-columns: 1fr; gap: 34px; }
  .flow__s:not(:last-child)::after {
    top: auto; bottom: -17px; right: auto; left: 50%;
    transform: translate(-50%, 50%) rotate(135deg);
  }
  .flow__s:hover:not(:last-child)::after {
    transform: translate(-50%, calc(50% + var(--mv) * 4px)) rotate(135deg);
  }
  /* apilado, la tarjeta deja de tener que alinearse con las otras dos */
  .ev { margin-top: 18px; }
  /* apilado no hay "columna 2": el conector del pie vuelve al borde */
  .act__proof figcaption { padding-left: 0; margin-top: 34px; }
  .act__from::after { left: 4px; height: 34px; }

  .finale__top { grid-template-columns: 1fr; align-items: start; }
  .finale__act { align-items: center; }
  .finale__grid { grid-template-columns: 1fr; gap: clamp(32px, 5vw, 44px); }
}
@media (max-width: 860px) {
  /* la evidencia apoyada sale de la esquina y se achica: en pantalla chica
     tapaba media captura */
  .act__pf--ev { width: 28%; right: 10px; bottom: 10px; }
}
@media (max-width: 760px) {
  .stats__row { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .stat:nth-child(-n+3) { border-top: 1px solid var(--line-ink); }
  .stat:nth-child(-n+2) { border-top: 0; }
  .stat:nth-child(3n+1) { border-left: 1px solid var(--line-ink); padding-left: 22px; }
  .stat:nth-child(3n+1)::before { left: 22px; }
  .stat:nth-child(odd) { border-left: 0; padding-left: 0; }
  .stat:nth-child(odd)::before { left: 0; }
  .det__list { grid-template-columns: 1fr; }
}
@media (max-width: 620px) {
  /* v11: EL CTA SOBREVIVE EN MOVIL. Antes se ocultaban los dos botones y la
     barra quedaba con marca y hamburguesa nada mas. Con el CTA fijo, eso
     vaciaba de accion justo la pantalla donde el usuario menos ganas tiene de
     abrir un menu para encontrarla.
     Entra por medida, no por optimismo: a 390px el canalon deja 358px utiles
     y la fila mide logo 144 + 12 + Contacto ~96 + 10 + hamburguesa 44 = 306.
     Sobran 52px. Comprobado en el navegador a 390.
     El que se va es "Iniciar Sesion", que ya estaba oculto desde 1140 y vive
     en el panel movil: no es la accion que la home viene a pedir. */
  .nav__actions { gap: 10px; }
  .nav__cta { padding-inline: 16px; font-size: 14px; }
}
@media (max-width: 560px) {

  body { font-size: 16px; }
  .hero__cta { flex-direction: column; align-items: stretch; }
  .hero__cta .btn { width: 100%; }
  /* v13: SOLO el gap. El padding-left estaba clavado en 16px y pisaba el
     calculo del margen: medido a 390px dejaba la pastilla 4px adentro del
     margen de la pagina, que es justo el tipo de desvio que nadie nombra y
     que hace que una pagina se sienta descuidada. El margen lo decide una
     sola expresion y ninguna media query lo sobreescribe. */
  .rail { gap: 12px; }
  .stats__row { grid-template-columns: 1fr; }
  .stat { border-left: 0 !important; padding-left: 0 !important; border-top: 1px solid var(--line-ink); }
  .stat::before { left: 0 !important; }
  .stat:first-child { border-top: 0; }
  .stat__c { max-width: none; }
  .finale__act, .finale__act .btn { width: 100%; }
  .finale__grid { grid-template-columns: 1fr; }
  .finale__bottom { flex-direction: column; }
  .finale__legal .finale__mini { gap: 6px 18px; }
  /* La escena aumentada se compacta: las piezas se acercan al marco y las
     tarjetas se angostan. Ninguna se esconde, porque cada una lleva un dato
     del mecanismo y esconderla seria contar la historia a medias; lo que se
     achica es la caja, no el relato. Lo unico que se retira es un pin suelto,
     que es la unica pieza sin dato. */
  /* solo se corrigen las coordenadas HORIZONTALES y los anchos: el reparto
     vertical esta en porcentajes y escala solo con el celular */
  .fx--mission { left: -30%; }
  .fx--insight { right: -30%; }
  .fx--metric  { right: -30%; }
  /* en movil la ficha del comercio ocupa una linea mas (la direccion se
     parte en dos), asi que la etiqueta de distancia baja para no tocarla */
  .anot-tag--dist { left: -30%; top: 37%; }
  .anot-tag--rew  { right: -26%; }
  .shot { left: -24%; width: 34%; }
  .mish  { width: 128px; gap: 7px; }
  .mish__n { font-size: 11px; }
  .mish__a { font-size: 10px; }
  .dread { width: 118px; }
  .dread__v { font-size: 13px; }
  .dins  { width: 122px; gap: 7px; }
  .dins__t { font-size: 10.5px; }
  .fxcard { padding: 8px 9px; }
  .fx--pin2 { display: none; }
  .anot-tag--dist .anot-tag__line, .anot-tag--rew .anot-tag__line { width: 10px; }
  .appdl__b img { width: 96px; }

}

/* ==========================================================================
   21. MOVIMIENTO REDUCIDO
   No se apaga todo: se apaga el DESPLAZAMIENTO. Con --mv:0 todas las
   distancias de traslado de la hoja valen 0px y todas las escalas valen 1,
   asi que la opacidad, el color y la SECUENCIA (el bucle del celular sigue
   contando su historia, el tablero sigue avanzando, el detector sigue
   respondiendo) se conservan intactos.
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  :root { --mv: 0; }

  /* lo unico que se apaga del todo son los bucles de traslado grandes y las
     oscilaciones lentas, que son los que producen mareo */
  .rail__track { animation: none; }
  .anot-box__fill { animation: none; opacity: .16; }
  /* la flotacion del celular es una oscilacion lenta de pantalla completa en
     el campo visual: es justo el tipo de movimiento que marea. Con --mv:0 ya
     queda en 0px, y ademas se detiene la animacion para no gastar frames.
     La POSE en perspectiva se conserva: es composicion, no movimiento, y el
     seguimiento del puntero ni siquiera se instala (lo decide el JS). */
  .phone__body { animation: none; }
  /* mismo criterio para las piezas flotantes: con --mv:0 su amplitud ya vale
     0px, pero son siete oscilaciones lentas simultaneas y se detienen del
     todo. Lo que NO se pierde es la escena: las piezas siguen entrando, el
     grafico sigue armandose y la tarjeta de validado sigue reaccionando al
     bucle. Se apaga el vaiven, no el relato. */
  .fx__i { animation: none; }
  /* la escuadra del titular es una aparicion, no un desplazamiento: se queda */

  /* el detector tiene que moverse para funcionar: corta en vez de viajar */
  .anot-box--live, .anot-box__rect, .anot-box--live .anot-box__c { transition-duration: 90ms; }
  /* mismo criterio para el pulgar de la navegacion: sigue marcando cual esta
     senalado, pero salta en vez de recorrer la barra */
  .nav__ink { transition-duration: 90ms; }

  /* la barra de progreso del tablero sigue: es indicacion de estado, no
     adorno, y es un elemento de 3px */
}

/* ---------- 22. TRANSPARENCIA Y CONTRASTE ---------- */
@media (prefers-reduced-transparency: reduce) {
  .nav.is-stuck { backdrop-filter: none; -webkit-backdrop-filter: none; background: var(--paper); }
  .nav { background: var(--paper); }
  .modal::backdrop { backdrop-filter: none; background: rgba(38,36,31,.86); }
  /* el cierre queda como campo teal pleno: la foto era una capa translucida */
  .finale__ph { display: none; }
}
@media (prefers-contrast: more) {
  .chip, .board__meta li, .trust__tags li, .det__opt { border-width: 2px; }
  .nav { background: var(--paper); }
  .anot-box__c { border-width: 3px; }
  /* sobre foto, el maximo contraste posible es el campo teal limpio */
  .finale__ph { display: none; }
  .finale__col a, .finale__quiet { border-bottom-color: var(--on-teal); }
}

/* ==========================================================================
   19. LAS RUTAS DEL SITIO (empresas, znappers, blog, errores)
   ==========================================================================
   Todo lo propio de las seis paginas que no son la home. Vive al final y en un
   solo bloque a proposito: la Fase 2 va a partir la hoja en capas (base /
   componentes / por ruta) y este es, tal cual, el material de la capa "por
   ruta". Mientras tanto, junto y rotulado.

   EL CRITERIO, Y ES EL PEDIDO TEXTUAL DEL CLIENTE: "que no se sienta que es
   otra pagina". Asi que estas paginas NO estrenan lenguaje. Reusan lo que la
   home ya resolvio:
     .h2 / .h2--xl / .h2__soft / .uline     la escala y el remate subrayado
     .act__head                             encabezado en dos columnas
     .board__stepn                          el numeral en chip teal
     .anot-tag / .anot-box                  el motivo de anotacion
     .stitch                                el hilo que cose una seccion con la siguiente
     .chan / .btn / .acc / .modal           los controles
     .ontealfield / .oninkfield             los dos campos plenos
   Lo que se agrega aca son COMPOSICIONES nuevas, no piezas nuevas.
   ========================================================================== */

/* --- HERO DE RUTA ---
   Mas bajo que el de la home y sin celular animado: la home tiene que seducir,
   estas paginas ya tienen a alguien que decidio entrar. El titular baja un
   escalon y la captura ocupa la columna derecha. */

/* ==========================================================================
   /empresas v18: LA PAGINA COMO SECUENCIA DE ESCENAS
   ==========================================================================
   Ver el comentario de cabecera del HTML para el POR QUE se tiro la version
   anterior. Aca, el sistema.

   UNA ESCENA = UNA PANTALLA = UNA IDEA. Cada `.esc` mide `100svh`, se encaja
   sola con `scroll-snap` y dice una sola cosa. La consecuencia de diseño mas
   importante es una restriccion: NADA puede pasarse de alto. Si un bloque no
   entra, se corta el contenido, no se estira la escena. Es lo que obliga a que
   cada pantalla sea una idea y no tres.

   `svh` Y NO `vh`, y no es un detalle: en un telefono, `100vh` cuenta la barra
   del navegador como si no existiera, asi que la escena queda mas alta que la
   pantalla, el snap pelea con el scroll y siempre asoma un borde de la
   siguiente. `svh` usa el viewport chico, que es el unico que esta garantizado
   visible.

   EL RITMO DE FONDOS NO ES DECORATIVO, SIGUE AL ARGUMENTO:
     oscuro  cuando habla el PRODUCTO (la red, la lente, el tablero)
     claro   cuando habla EL QUE COMPRA (el hallazgo, la cobertura, empezar)
     teal    una sola vez, en el diferencial, que es lo que la marca reclama
   Alternar por alternar seria justo lo que un buen diseño no hace; alternar
   por quien esta hablando convierte el fondo en parte del relato.
   ========================================================================== */

/* ==========================================================================
   EL SISTEMA DE SUPERFICIES DE LAS ESCENAS
   ==========================================================================
   POR QUE EXISTE. La critica fue "no se siente estetico, uniforme, agradable a
   la vista", y mirando la pagina de cerca tenia razon: cada caja se habia
   resuelto por su cuenta. El chip de canal era una pildora teal, la caja de
   datos tenia esquinas duras adentro de un panel redondeado, la ficha usaba un
   radio y la leyenda otro, y el titular directamente no tenia superficie. Ocho
   piezas con ocho criterios distintos no se leen como un producto: se leen como
   ocho pantallas pegadas.

   La regla es una sola: HAY DOS SUPERFICIES, Y CADA VEZ MENOS.

     .pan          el panel. Una sola forma, un solo borde, un solo relleno.
     .pan--luz     el mismo panel sobre escena clara

   En la v21 quedaron en un solo uso: la banda de anotacion del tablero. La
   escena del mapa dejo de tener paneles —su titular va sobre un velo y su
   tarjeta de evidencia tiene forma propia porque empieza con una foto a
   sangre— y `.rot` y `.caja`, que eran las otras dos primitivas, se borraron
   con la ficha que las usaba (el porque, mas abajo).

   Todo lo que no sea una de esas dos cosas no lleva fondo ni borde. Menos
   cajas es mas legible, y ademas es lo que hace que las siete escenas se
   sientan del mismo lugar. La direccion del rediseño es esa: cada ronda deberia
   dejar MENOS rectangulos, no mas.
   ========================================================================== */
:root {
  --pan-r: 18px;                       /* el unico radio de panel */
  --pan-pad: clamp(16px, 1.5vw, 24px); /* el unico relleno de panel */
}

/* EL PANEL SOBRE ESCENA OSCURA. Es vidrio: deja ver el mapa o la foto por
   atras, que es lo que hace que se lea como parte del aparato y no como una
   tarjeta pegada encima. El borde interior claro le da canto; sin el, un
   `backdrop-filter` solo se ve como una mancha. */
.pan {
  padding: var(--pan-pad);
  border-radius: var(--pan-r);
  border: 1px solid rgba(247,244,239,.14);
  background: rgba(18, 17, 15, .72);
  -webkit-backdrop-filter: blur(22px) saturate(1.2);
          backdrop-filter: blur(22px) saturate(1.2);
  box-shadow: inset 0 1px 0 rgba(247,244,239,.10), 0 20px 56px rgba(0,0,0,.40);
}
/* sin transparencias: el mismo panel, opaco. Un texto sobre un vidrio que no
   difumina queda ilegible. */
@media (prefers-reduced-transparency: reduce) {
  .pan { background: rgba(18,17,15,.97); backdrop-filter: none; -webkit-backdrop-filter: none; }
}
/* el mismo panel sobre papel: no hay nada que dejar ver atras, asi que en vez
   de vidrio es una superficie elevada. Mismo radio, mismo relleno, misma
   familia de sombra. */
.pan--luz {
  background: var(--paper);
  border-color: var(--line);
  -webkit-backdrop-filter: none; backdrop-filter: none;
  box-shadow: inset 0 1px 0 #fff, 0 18px 44px rgba(52,49,44,.10);
}

/* ==========================================================================
   `.rot` Y `.caja` SE RETIRARON EN LA v21
   ==========================================================================
   Eran dos de las cuatro primitivas del sistema visual de esta ruta: la
   pildora de rotulo y la subdivision interna de un panel. Las usaba UNA sola
   pieza, la ficha de la escena del mapa, y esa ficha se reemplazo por la
   tarjeta de evidencia, que no las necesita: su rotulo va sobre la foto y sus
   tres datos van en una fila con hairline, sin caja anidada.

   NO SE DEJAN "POR SI ACASO". La regla del repo es que lo que se reemplaza se
   borra en el mismo commit, y este archivo ya pago tres veces el precio de no
   hacerlo: una edicion que cae en un bloque muerto funciona de casualidad, y
   una primitiva que nadie usa igual se lee como parte del sistema y alguien la
   elige creyendo que esta viva. Estan enteras en el historial de git.

   `.pan` y `.pan--luz` SIGUEN VIVAS: las usa la banda de anotacion del tablero.
   ========================================================================== */


/* EL ENCAJE. `mandatory` y no `proximity` porque el pedido era explicito: que
   al scrollear te lleve a la escena siguiente entera. `scroll-snap-stop:
   always` impide que un gesto largo de trackpad se saltee dos escenas: cada
   una tiene que poder verse. */
/* EL ENCAJE, Y POR QUE YA NO FRENA EN CADA ESCENA.
   La primera version usaba `scroll-snap-stop: always`, que obliga a detenerse
   en TODAS las escenas aunque el gesto haya sido largo. Encaja bien y se
   siente pegajoso: un movimiento decidido choca contra una pared invisible en
   cada pantalla. Sin `always`, un gesto corto sigue encajando de a una —que es
   lo que se pidio— y uno largo puede recorrer varias sin pelearse con la mano.

   `scroll-behavior: smooth` SE SACO DE `html` EN LA v24, Y ES LA CORRECCION DE
   FLUIDEZ MAS GRANDE DE ESTA RONDA.
   Estaba puesto para que un salto con ancla no fuera un corte seco, que es un
   motivo cierto. El problema es que `scroll-behavior` no distingue entre un
   salto que pide la pagina y el RE-ENCAJE que hace el navegador: con
   `scroll-snap-type: mandatory`, cada encaje hereda el suavizado y se anima dos
   veces —la animacion propia del encaje y encima la de `smooth`—. El resultado
   es que la pagina tarda en asentarse despues de cada gesto y se siente
   pastosa, que es exactamente lo que el cliente describio como "no se siente
   fluido". Sin `smooth`, el encaje lo resuelve el compositor con la curva
   nativa del navegador, que es la que la mano espera.
   LO QUE SE PIERDE SE RECUPERA DONDE HACE FALTA: los tres unicos saltos por
   ancla de esta ruta —el espinazo, el enlace de saltar al contenido y el foco
   del teclado— los suaviza el script con `scrollIntoView({behavior:'smooth'})`,
   que es por gesto y no por documento. */
/* ==========================================================================
   EL ENCAJE VALE SOLO DONDE HAY ESCENAS — v25
   ==========================================================================
   QUE ERROR REAL LO MOTIVA, Y ES DE LOS CAROS. Esto estaba escrito en `html` a
   secas, o sea EN LAS OCHO PAGINAS. Siete de las ocho no tienen una sola
   `.esc`, asi que su unico punto de encaje era el pie —que si lleva
   `scroll-snap-align` y esta en el esqueleto compartido—. Con `mandatory` el
   scroll NO PUEDE quedarse quieto fuera de un punto de encaje: en la home, al
   primer gesto de rueda el navegador se iba derecho al pie y no habia forma de
   leer la pagina. El cliente lo reporto asi: "en el home te aparece
   directamente en el footer".

   No lo agarro nadie porque las siete escenas se venian mirando de a una con
   `scrollIntoView`, que salta a una posicion exacta y no pasa por el encaje, y
   porque `verificar.py` es estatico y `mirar.js` solo abre `/empresas`.

   `:has()` Y NO UNA CLASE EN EL `<body>`: la condicion es "esta pagina esta
   armada como una secuencia de escenas", y eso ya lo dice el marcado. Una clase
   aparte seria un segundo lugar donde declarar lo mismo, o sea la forma de que
   un dia digan cosas distintas.
   `.finale` HEREDA LA MISMA CONDICION: su punto de encaje existe para que se
   pueda LLEGAR al pie desde la septima escena. Sin escenas no hay de donde
   llegar, y lo unico que hace es atraer el scroll. */
html:has(.esc) { scroll-snap-type: y mandatory; }
/* EL PIE TAMBIEN ENCAJA, Y SIN ESTO NO SE PODIA LLEGAR A EL.
   Con `mandatory` el scroll SOLO puede quedarse quieto en un punto de encaje, y
   los unicos que habia eran las siete escenas. Debajo de la septima hay dos mil
   pixeles de pie —el CTA de la demo, "Hablar con ventas", el WhatsApp y el
   mail— sin un solo punto de encaje, asi que la rueda rebotaba contra la
   septima y volvia. Medido con rueda de verdad: diez rafagas seguidas frenando
   en el mismo pixel.
   Es un bug viejo, de cuando la ruta paso a escenas, y es de los caros: toda la
   pagina empuja a contactar y el contacto quedaba atras de una pared. */
html:has(.esc) .finale { scroll-snap-align: start; }


.esc {
  position: relative;
  /* CADA ESCENA ES SU PROPIO CONTEXTO DE APILADO, y esto se volvio obligatorio
     cuando el mapa paso a ser `fixed`. Sin el `z-index`, una escena con
     `position: relative` y `z-index: auto` NO crea contexto, asi que el
     `z-index: 1` del mapa subia a la raiz y se pintaba por encima de TODAS las
     escenas siguientes: la del hallazgo desaparecia entera detras de la ciudad.
     Con las siete en el mismo plano, se apilan en el orden del marcado, que es
     el orden en que se recorren. Y el mapa queda encerrado en la primera, que
     es donde su `z-index` tiene sentido. */
  z-index: 0;
  /* ==================================================================
     LAS ESCENAS CANCELAN EL `scroll-padding-top` DEL DOCUMENTO
     ==================================================================
     `html` lleva `scroll-padding-top: navh + 16px` para que un salto con ancla
     no deje el destino abajo de la barra pegada. Correcto para un ancla, y
     VENENO para el encaje: ese relleno corre TODOS los puntos de encaje hacia
     arriba, asi que cada escena frenaba 84px baja y quedaba asomando una franja
     de la escena anterior arriba, con la propia cortada abajo.
     Medido con gestos de rueda reales: las siete frenaban en 816, 1716, 2616,
     3516, 4416, 5316 en vez de 900, 1800, 2700, 3600, 4500, 5400. Exactamente
     84px cada una, siempre. La pagina prometia una escena por pantalla y nunca
     mostro una escena entera.
     Se cancela con un `scroll-margin-top` negativo del mismo tamaño: el encaje
     resuelve `scrollTop = borde - margen - relleno`, asi que con margen igual
     a menos el relleno las dos cuentas se van y la escena frena en su borde.
     No se toca el `scroll-padding` global porque lo necesitan los anclajes de
     ayuda y del indice de las notas, que no tienen relleno propio. */
  scroll-margin-top: calc(-1 * (var(--navh, 64px) + 16px));
  /* Y FONDO POR DEFECTO, QUE ES LA OTRA MITAD DE LA MISMA HISTORIA.
     Con el mapa fijo detras de todo, una escena sin fondo propio deja pasar la
     ciudad. `.esc--cov` no tenia ninguno —nunca lo habia necesitado, porque
     antes el mapa vivia adentro de la primera escena— y quedo con los seis
     canales y su foto flotando sobre las avenidas de CABA, ilegible.
     Se pone el fondo en la clase base para que el olvido no sea posible: una
     escena nueva nace opaca, y la que quiera dejar pasar algo lo declara. */
  background: var(--surface);
  min-height: 100svh;
  scroll-snap-align: start;
  display: grid;
  align-content: center;
  /* la barra flota sobre las escenas; sin esto el titular queda abajo del logo */
  padding: calc(var(--navh, 64px) + clamp(14px, 2.4vh, 34px)) 0 clamp(24px, 4vh, 52px);
  /* `clip` y no `hidden`: no crea contexto de scroll, asi que no interfiere con
     el snap del documento */
  overflow: clip;
}
/* LA PRIMERA ESCENA MIDE MENOS, Y ES POR LA BARRA.
   `.nav` es `position: sticky`, o sea que OCUPA LUGAR EN EL FLUJO: la primera
   escena empieza 69px mas abajo que el borde de la pantalla. Con 100svh de
   alto, esos 69px se le iban abajo del pliegue y el pie de la escena quedaba
   afuera. De la segunda en adelante no pasa: cuando encajan, la barra ya esta
   pegada y las TAPA en vez de empujarlas, asi que ahi 100svh es exacto.
   Se descuenta la altura medida, no una estimacion: la publica el script. */
main > .esc:first-of-type {
  min-height: calc(100svh - var(--navh, 64px));
  /* Y SU PUNTO DE ENCAJE ES EL BORDE DEL DOCUMENTO, NO SU PROPIO BORDE.
     Sin esto, `scroll-snap-align: start` encaja la primera escena en su
     `offsetTop`, que son los 69px de la barra: al volver desde la escena 2 el
     scroll frenaba con la barra PEGADA ENCIMA del titular (el rotulo "Una
     campaña, paso a paso" quedaba atras del logo) y encima asomaba una franja
     de la escena siguiente abajo. `scroll-margin-top` corre el punto de encaje
     esos mismos 69px hacia arriba, o sea al cero del documento, que es donde
     la barra vuelve a ocupar su lugar en el flujo y la escena mide exacto una
     pantalla. Se ve solo al subir, que es justo el caso que nadie prueba. */
  /* la primera va aparte: su borde ya esta 69px abajo porque la barra ocupa
     lugar en el flujo, asi que su punto de encaje tiene que ser el cero del
     documento. Con el relleno de 85px cancelado, alcanzan 16px. */
  scroll-margin-top: -16px;
}

/* LA ENTRADA DE CADA ESCENA.
   Transiciones y no keyframes: si alguien scrollea rapido y entra y sale de
   una escena, una transicion se reorienta desde donde esta y un keyframe
   reinicia desde cero. Se nota.

   VA DETRAS DE `.js`, y esto es una regla dura de este proyecto que ya costo
   caro tres veces: si el estado escondido lo pusiera la hoja sin condicion,
   con el script caido quedaria media pagina invisible para siempre. Sin JS,
   todo se ve; lo unico que se pierde es el escalonado. */
/* LA ENTRADA DE CADA ESCENA.
   Doce pixeles y no dieciocho: el desplazamiento tiene que insinuar de donde
   viene la pieza, no hacer un viaje. Y el escalonado baja de 70 a 55 ms, que es
   el rango en el que una cascada se lee como una sola cosa entrando en orden;
   arriba de 80 se empieza a leer como piezas separadas llegando tarde.
   La duracion es 420 ms: por encima de medio segundo una entrada se siente
   lenta aunque el ojo no sepa por que. */
/* v22: 560 ms Y CURVA EXPONENCIAL, EN VEZ DE 420 Y `--ease`.
   No es "mas lento": con salida exponencial el 80% del recorrido pasa en los
   primeros 200 ms y el resto es el frenado. La pieza llega igual de rapido y
   deja de tener el corte seco que tenia al final. Es la mejora de fluidez mas
   barata que hay en esta hoja, porque toca las siete escenas a la vez.
   El desplazamiento sube de 12 a 16px por la misma razon: con un frenado tan
   largo, 12 no se llegan a ver. */
.js [data-en] {
  opacity: 0;
  transform: translateY(calc(var(--mv) * 16px));
  transition: opacity .56s var(--ease-exp) calc(var(--i, 0) * 60ms),
              transform .56s var(--ease-exp) calc(var(--i, 0) * 60ms);
}
/* LOS PANELES ADEMAS SE ASIENTAN. Entran un pelo mas chicos y llegan a su
   tamaño: es la diferencia entre una tarjeta que aparece y una que se apoya.
   El 0.985 es a proposito casi imperceptible; nada en el mundo real aparece
   desde la nada, pero tampoco crece de golpe. */
.js .pan[data-en] { transform: translateY(calc(var(--mv) * 16px)) scale(.985); }
.js .esc.is-en .pan[data-en] { transform: none; }
@media (prefers-reduced-motion: reduce) {
  .js .pan[data-en] { transform: none; }
}
.js .esc.is-en [data-en] { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  /* reduce, no elimina: se conserva el desvanecido, que ayuda a entender que
     algo entro, y se quita el desplazamiento, que es lo que marea */
  .js [data-en] { transform: none; transition-duration: .3s; }
}

/* --- LA TIPOGRAFIA DE LAS ESCENAS ---
   Una sola escala para las siete. Sin esto cada escena inventa su tamaño y la
   pagina deja de leerse como una sola cosa, que es exactamente el sintoma que
   habia que curar. */
.esc__ojo {
  display: flex; align-items: center; gap: 9px;
  font-family: var(--mono); font-size: clamp(10.5px, 1vw, 12px); font-weight: 600;
  letter-spacing: .16em; text-transform: uppercase; color: var(--text-2);
}

/* EL UNICO PULSO PERMANENTE DE LA RUTA, y se gana el lugar: dice que el mapa
   se esta REPRODUCIENDO, que es un estado real y el unico aviso de que lo que
   se ve abajo no es una captura. Es la excepcion a "el contexto no se mueve".

   LATE CON UN ANILLO, NO CON `box-shadow`. Animar `box-shadow` repinta el
   elemento en cada cuadro; un `::after` con `transform` y `opacity` lo resuelve
   el compositor. Se ve igual y no toca el hilo principal, que es la regla de
   esta hoja para todo lo continuo. */
.esc__punto {
  position: relative;
  width: 7px; height: 7px; border-radius: 50%; background: var(--teal); flex: none;
}
.esc__punto::after {
  content: ''; position: absolute; inset: -1px;
  border-radius: 50%; border: 1.5px solid rgba(19,193,172,.6);
  opacity: 0;
}
@media (prefers-reduced-motion: no-preference) {
  .esc__punto::after { animation: escLate 2.6s var(--ease-exp) infinite; }
}
@keyframes escLate {
  0%   { opacity: .75; transform: scale(1); }
  70%, 100% { opacity: 0; transform: scale(3.2); }
}

/* LA ESCALA BAJA DE 52 A 44, Y ES LA CORRECCION MAS BARATA DE LA v25.
   Medido en la ventana del cliente (1920x919): con el tope en 52 y `3.5vw`, el
   titular del tablero daba un campo de marca de 745x62 px. Una sola pieza
   ocupando ese lugar no deja aire para el resto y ademas obliga a que cualquier
   cosa al lado crezca para no desaparecer — que es como esta ruta llego a tener
   escenas con DOS bloques de tipografia grande peleandose.
   Bajar la escala no es achicar: es lo que devuelve la jerarquia. El titular
   sigue siendo lo mas grande de cada pantalla por un margen amplio.
   `3vw` en vez de `3.5vw` para que el tope se alcance recien arriba de 1.460px:
   entre 1.100 y 1.400, que es donde vive la mayoria de los escritorios, el
   titular crece con la ventana en vez de quedarse clavado en el tope. */
/* v26: EL TITULAR MIRA EL ALTO TAMBIEN, y esa es la correccion.
   Con `3vw` a secas el tope de 44 se alcanzaba en 1.467px, asi que 1512 y 1920
   daban EXACTAMENTE el mismo titular mientras el alto disponible pasaba de 850
   a 942: en la pantalla grande el ratio titular/cuerpo quedaba en 2,59, igual
   que en una notebook, y la escena se leia chica y perdida en el medio.
   Subir el tope a secas no servia: la escala habia BAJADO de 52 a 44 en la v25
   por un pedido textual ("todo muy grande y poco aireado") medido en 1920x919,
   o sea una ventana ANCHA Y BAJA. Las dos cosas son ciertas y no se
   contradicen — lo que faltaba era mirar el eje que escasea.
   `min(3vw, 5vh)` crece con la ventana pero se frena con el alto: 1920x1080 da
   48, 1920x919 da 46, 1512x900 sigue en 45 y 1440x900 no se mueve. La ventana
   baja no vuelve al tamaño que se rechazo. */
.esc__h1, .esc__h2 {
  margin-top: clamp(12px, 1.6vh, 20px);
  font-size: clamp(30px, min(3vw, 5vh), 48px); font-weight: 700;
  line-height: 1.05; letter-spacing: var(--tr-h1); color: var(--text);
  text-wrap: balance;
}

/* el campo de marca detras de la frase clave. `box-decoration-break` lo
   mantiene entero si la linea se parte: sin eso el campo se corta al medio y
   queda como un error de render. */
/* EL CAMPO DE MARCA SE PINTA SOLO AL ENTRAR LA ESCENA, de izquierda a derecha,
   como si alguien lo resaltara. Es el gesto que se repite en las siete
   pantallas, y por eso es lo que le da FIRMA al recorrido: no son siete
   titulares que aparecen, es el mismo subrayado recorriendo la pagina.

   SE ANIMA `background-size`, NO `clip-path`. Recortar el elemento esconderia
   tambien las letras, y lo que tiene que entrar es el campo. El texto arranca
   en tinta y se pasa a blanco cuando el campo ya lo cubrio: sin ese relevo
   habria un instante de blanco sobre blanco.

   Y VA DETRAS DE `.js`: sin script el campo esta pintado desde el primer
   cuadro. Nada que sea contenido puede depender de que una animacion corra. */
.esc__mark {
  display: inline; padding: .04em .16em .1em; margin-left: -.06em;
  background-image: linear-gradient(var(--teal), var(--teal));
  background-repeat: no-repeat; background-size: 100% 100%;
  color: var(--on-teal);
  border-radius: var(--r-anot-2);
  box-decoration-break: clone; -webkit-box-decoration-break: clone;
}
/* EL BARRIDO ES LA FIRMA DEL RECORRIDO —el mismo gesto en las siete
   pantallas— asi que es lo que mas se gana suavizando. Pasa a la curva
   exponencial y a 640 ms: el campo entra rapido y el ultimo tercio se desliza,
   que es como se mueve un resaltador de verdad. El relevo de color se corre
   con el: tiene que pasar cuando el campo YA cubrio la letra, ni antes
   —blanco sobre blanco— ni despues. */
.js .esc__mark {
  background-size: 0% 100%; color: var(--text);
  transition: background-size .64s var(--ease-exp) .18s, color .16s linear .44s;
}
.js .esc.is-en .esc__mark { background-size: 100% 100%; color: var(--on-teal); }
/* EL ESTADO PREVIO AL BARRIDO ES TINTA, Y ESO VALE PORQUE LAS SIETE ESCENAS
   SON DE PAPEL. `.js .esc__mark` arranca en `--text` (#34312C): sobre papel eso
   es el titular, asi que la frase clave se lee aunque el observador no dispare
   nunca. La regla que le hacia excepcion —`.js .esc--red .esc__mark,
   .js .esc--hall .esc__mark`, que la arrancaba en blanco— se retiro en la v25
   junto con el fondo oscuro de esas dos escenas.
   LA LECCION QUEDA, y es la que pago `.esc__h2--claro` en la v18: si alguna
   escena vuelve a pintarse a sangre sobre una foto, el estado de partida del
   resaltado hay que revisarlo aca, o la frase nace invisible. */
@media (prefers-reduced-motion: reduce) {
  .js .esc__mark { background-size: 100% 100%; color: var(--on-teal); transition: none; }
}
.esc__sub {
  margin-top: clamp(12px, 1.6vh, 20px); max-width: 46ch;
  font-size: clamp(15px, 1.25vw, 18px); line-height: 1.5; color: var(--text-2);
}

/* SOBRE ESCENA OSCURA. La lente es la unica pantalla de la ruta que va a
   sangre sobre una foto, y ahi el titular y el ojo tienen que invertirse.
   ESTOS DOS MODIFICADORES VIAJABAN EN EL MARCADO DESDE LA v18 SIN NINGUNA
   REGLA QUE LOS RECOGIERA. El titular heredaba `--text` (#34312C) y caia sobre
   una foto de gondola cuyo fondo mide #26241F: tinta sobre tinta. Encima
   `.lens__cabe` le pone una sombra negra, pensada para texto claro, que sobre
   texto oscuro no separa nada y lo hunde mas. En pantalla se leia "Esto no es
   una foto." apenas como una mancha, y sobrevivia solo la segunda linea porque
   va dentro de `.esc__mark` y se pinta sobre el campo teal.
   NO LO PODIA VER NINGUN CHEQUEO: `detectar-muerto.py` busca CSS que ningun
   HTML usa, y esto era al reves, marcado que ninguna regla recoge. */
.esc__h2--claro { color: var(--on-ink); }

/* EL RESALTADO SOBRE FONDO OSCURO. `.key` vale `--text` (#34312C, la tinta) y
   eso sobre un campo oscuro es tinta sobre tinta: la frase "cuánto salió y
   cuánto tardó" quedaba INVISIBLE en el hero. Un barrido de contraste no lo
   detecta —un texto invisible no falla ningun umbral porque no hay nada que
   medir— y se vio mirando la pantalla, que es la nota 1 de este proyecto.
   Las escenas oscuras no llevan `.oninkfield`, que es la clase que ya resolvia
   esto en el resto del sitio, porque no son campos: son escenas. Asi que la
   correspondencia se declara aca, una vez. */
/* `.esc--lente .key` SE RETIRO EN LA v25. Existia porque la escena era de
   noche: `.key` vale `--text`, que sobre un campo oscuro es tinta sobre tinta.
   Con la escena en luz, `.key` vuelve a ser correcto por si solo. */

/* ==========================================================================
   EL ESPINAZO: LOS TRES ACTOS Y LAS SIETE ESCENAS
   ==========================================================================
   El motivo esta en el HTML. Acá va la forma, y tiene tres decisiones.

   1. VIVE EN EL MARGEN, QUE YA ESTABA VACIO. El contenido de la ruta se
      encuadra en `--wrap`, asi que en una pantalla de 1512 sobran 300px de
      cada lado. No se le quita lugar a nada: se ocupa lo que no se usaba.
      Por eso la posicion se calcula DESDE el margen y no con un numero fijo:
      si el contenedor cambia de escalon, el espinazo se corre con el.

   2. LOS NOMBRES SE MUESTRAN SIEMPRE. La primera version los escondia y los
      sacaba al apuntar, con los puntos solos en reposo. Siete puntos sin texto
      no orientan a nadie: dicen "hay siete cosas" y esa es justo la parte que
      ya se adivina. Lo que hay que contestar es QUE hay, y eso es una palabra.

   3. NO ES UNA BARRA DE PROGRESO. Marca en cual de las siete estas, no cuanto
      te falta: la pagina es un bucle y un porcentaje de avance no significa
      nada cuando el mapa de la primera pantalla se esta reproduciendo de nuevo.

   `position: fixed` y no `sticky`: tiene que quedarse quieto mientras las
   siete escenas encajan por abajo. Es el unico elemento de la ruta que no
   pertenece a ninguna escena, y esa es exactamente su funcion. */
.ruta {
  position: fixed; z-index: 40;
  left: max(18px, calc(var(--margen) * .5 - 62px));
  top: 50%; transform: translateY(-50%);
  pointer-events: none;
}
/* SE RETIRA EN EL PIE. Ver `guardar()` en el script: pasadas las siete escenas
   el espinazo no tiene nada que indexar, y ademas el pie es un campo oscuro
   donde su tinta no se lee. Quedaba flotando ilegible sobre el cierre.
   Se va HACIA LA IZQUIERDA, que es de donde vino: una pieza fija que solo se
   desvanece parece que se apago; una que se va para afuera parece que se
   guardo. `visibility` se demora hasta el final del viaje para que el elemento
   deje de existir para el teclado recien cuando termino de irse. */
.ruta {
  transition: opacity .34s var(--ease), transform .34s var(--ease),
              visibility 0s linear .34s;
}
.ruta.is-fuera {
  opacity: 0; visibility: hidden;
  transform: translateY(-50%) translateX(calc(var(--mv) * -14px));
}
.ruta:not(.is-fuera) { transition-delay: 0s, 0s, 0s; }

/* ==========================================================================
   EL ESPINAZO ES UN RIEL, Y SOLO HABLA LA ESCENA EN LA QUE ESTAS — v24
   ==========================================================================
   QUE ESTABA MAL. Eran once lineas de texto —tres rotulos de acto y siete
   nombres de escena— en 12px y a media luz, flotando en el margen de las
   siete pantallas. La idea era buena y la ejecucion la contradecia: un indice
   que existe para ORIENTAR no puede ser, el mismo, once cosas mas que leer.
   Y contradecia el criterio de esta hoja: el espinazo es NIVEL 3, contexto, y
   el contexto no compite.

   COMO SE APLICA EL CRITERIO A UN INDICE. Solo habla el que importa:

     - las siete escenas son siete MARCAS sobre una linea. Eso ya dice cuantas
       hay, en cual estas y cuanto pasaste, que es todo lo que un indice tiene
       que decir de un vistazo;
     - la escena ACTUAL muestra su nombre, y es la unica;
     - el acto actual muestra el suyo, y es el unico;
     - y con el puntero encima o con el foco del teclado adentro, se abre
       entero. La informacion no se pierde: se pide.

   Es progresivo por la misma razon por la que el mapa dejo de volar: lo que
   esta siempre a la vista tiene que ser lo minimo, y lo demas aparece cuando
   alguien lo busca.

   ACCESIBILIDAD: los nombres se atenuan con `opacity`, no con `display`, asi
   que siguen en el arbol y se llegan con Tab. Y `:focus-within` abre el riel
   entero, o sea que quien navega con teclado ve exactamente lo mismo que quien
   pasa el puntero. */
.ruta__l {
  list-style: none; margin: 0; padding: 0; display: grid; gap: 0;
  pointer-events: auto;
  position: relative;
  padding: 10px 14px 12px 13px;
  border-radius: var(--r-surface);
  background: rgba(20,17,13,0);
  transition: background-color .3s var(--ease);
}
/* LA LINEA DEL RIEL. Va detras de las marcas y recorre el alto entero: es lo
   que convierte siete puntos sueltos en un recorrido. */
.ruta__l::before {
  content: ''; position: absolute; z-index: 0;
  left: 16px; top: 16px; bottom: 18px; width: 1px;
  background: var(--line-2);
}

/* el rotulo del acto. Es el cartel de la oficina y por eso va en mono y en
   caja alta: es una consigna, no un titulo de seccion. */
.ruta__acto {
  position: relative; z-index: 1;
  height: 0; overflow: hidden;
  transition: height .3s var(--ease), opacity .3s var(--ease);
  opacity: 0;
}
/* SOLO SE VE EL ACTO EN EL QUE ESTAS. Los otros dos ocupan cero: tres
   consignas a la vez son tres cosas que leer, y en un indice de contexto eso
   es exactamente lo que sobra. */
.ruta__l.en-1 .ruta__acto:nth-of-type(1),
.ruta__l.en-2 .ruta__acto:nth-of-type(2),
.ruta__l.en-3 .ruta__acto:nth-of-type(3) { height: 22px; opacity: 1; }
.ruta__an {
  display: block; padding-left: 21px;
  font-family: var(--mono); font-size: 9.5px; font-weight: 700;
  letter-spacing: .17em; text-transform: uppercase; color: var(--teal);
  transition: color .3s var(--ease);
}
.ruta__l.en-1 .ruta__acto:nth-of-type(1) .ruta__an,
.ruta__l.en-2 .ruta__acto:nth-of-type(2) .ruta__an,
.ruta__l.en-3 .ruta__acto:nth-of-type(3) .ruta__an { color: var(--teal); }

.ruta__i a {
  position: relative; z-index: 1;
  pointer-events: auto;
  display: flex; align-items: center; gap: 12px;
  padding: 7px 6px 7px 0;
  color: var(--text-3); text-decoration: none;
  font-size: 12.5px; line-height: 1.2; letter-spacing: var(--tr-sub);
  transition: color .24s var(--ease);
}
/* LA MARCA. Un punto sobre la linea, y el de la escena activa se llena de
   marca y se rodea de un halo. Crece desde su centro y NUNCA desde cero: una
   marca de posicion que aparece de la nada se lee como un parpadeo. */
.ruta__p {
  flex: none; width: 7px; height: 7px; border-radius: 50%;
  margin-left: 3px;
  background: var(--line-2);
  box-shadow: 0 0 0 3px var(--surface);
  transition: background-color .24s var(--ease), transform .3s var(--ease-exp),
              box-shadow .3s var(--ease);
}

/* EL NOMBRE SOLO APARECE EN LA ESCENA ACTIVA. Los otros seis se atenuan y se
   corren un pelo hacia la linea: no desaparecen —siguen en el arbol y se
   llegan con Tab— pero dejan de pedir atencion. */
.ruta__n {
  white-space: nowrap;
  opacity: 0;
  transform: translateX(calc(var(--mv) * -5px));
  transition: opacity .26s var(--ease), transform .3s var(--ease-exp);
}
.ruta__i a.is-aqui .ruta__n { opacity: 1; transform: none; }

/* LA ESCENA EN LA QUE ESTAS. La clase la pone el script sobre el enlace. */
.ruta__i a.is-aqui { color: var(--text); font-weight: 600; }
.ruta__i a.is-aqui .ruta__p {
  background: var(--teal); transform: scale(1.45);
  box-shadow: 0 0 0 3px var(--surface), 0 0 0 6px rgba(19,193,172,.22);
}
/* las que ya pasaron quedan marcadas: un recorrido que no muestra lo andado no
   se lee como recorrido. Es el mismo criterio que la barra de la cadena. */
.ruta__i a.is-visto .ruta__p { background: var(--teal); opacity: .45; }

/* ==================== SE ABRE ENTERO CUANDO SE LO PIDE ====================
   Con el puntero encima o con el foco del teclado adentro. Ahi el riel deja de
   ser contexto y pasa a ser un control, asi que recien ahi se gana una
   superficie propia y todos los nombres. */
.ruta__l:hover, .ruta__l:focus-within { background: rgba(255,255,255,.72); }
.ruta__l:hover .ruta__n, .ruta__l:focus-within .ruta__n { opacity: 1; transform: none; }
.ruta__l:hover .ruta__acto, .ruta__l:focus-within .ruta__acto { height: 22px; opacity: 1; }
.ruta__l:hover .ruta__an, .ruta__l:focus-within .ruta__an { color: var(--text-3); }
.ruta__l:hover .ruta__l.en-1 .ruta__acto:nth-of-type(1) .ruta__an { color: var(--teal); }
@media (hover: hover) and (pointer: fine) {
  .ruta__i a:hover { color: var(--text); }
  .ruta__i a:hover .ruta__p { transform: scale(1.3); }
}
.ruta__i a:active .ruta__p { transform: scale(1.12); }

/* SE VA POR DEBAJO DE 1240px, y no es una rendicion: abajo de ese ancho el
   margen lateral deja de alcanzar y el espinazo se le montaria al contenido.
   Un indice que tapa lo que indexa no sirve. En telefono el problema que
   resuelve tampoco existe igual: el pulgar ya da la escala del documento. */
@media (max-width: 1239px) { .ruta { display: none; } }
@media (prefers-reduced-motion: reduce) {
  .ruta__p, .ruta__an, .ruta__i a { transition-duration: .12s; }
}

/* ==========================================================================
   ESCENA 1 · LA RED
   ==========================================================================
   El mapa ocupa la pantalla y los paneles flotan encima, que es exactamente lo
   que hace la demo. La alternativa —un panel con el mapa adentro— fue la v17 y
   se leia como una ilustracion, no como una herramienta.
   ========================================================================== */
.esc--red { background: var(--surface); color: var(--ink); padding: 0; align-content: stretch; grid-template-rows: 1fr; }

/* ==========================================================================
   EL ZNAPPER QUE RECORRE LA CIUDAD
   ==========================================================================
   Es lo que le faltaba a la escena. Un pin que cambia de color no cuenta que
   ALGUIEN fue hasta un comercio, lo relevo y volvio con un dato; el recorrido
   si. El mecanismo entero de Znapp se entiende sin leer una palabra.

   EL VIAJE LO INTERPOLA EL NAVEGADOR. El script llama a `setLatLng` UNA vez
   por tramo y Leaflet escribe un `transform`; esta transicion lo convierte en
   un viaje de dos segundos y medio. Son dos escrituras de estilo por tramo en
   vez de ciento cincuenta, y lo resuelve el compositor. Es la misma regla que
   ya rige en la lente: lo continuo no lo hace el hilo principal.
   ========================================================================== */
/* ==========================================================================
   SON CUATRO, Y VAN EN NARANJA
   ==========================================================================
   NARANJA #F44D30, Y NO ES UNA ELECCION DE GUSTO. La demo publica de Znapp usa
   pines naranjas para "Znapper en zona" (`CONTEXTO-EXTRA/demo-znapp.md`), y
   `ZNAPP.md` confirma ese valor como el segundo color de la marca. El
   significado ya estaba declarado por el producto: aca se respeta.
   Es ademas el unico color que se puede poner al lado del teal sin pelearle:
   sobre la noche los dos brillan y ninguno se lee como una VARIACION del otro,
   que es lo que la regla del teal prohibe.

   LA CURVA CAMBIA, Y ESO ES LO QUE SE SENTIA MAL.
   Era `cubic-bezier(.5, 0, .3, 1)`: con el primer punto de control en (.5, 0),
   ARRANCA LENTO. Es una curva de entrada-salida, correcta para algo que se
   mueve de un lado a otro de la pantalla por decision del sistema, y equivocada
   para esto: el Znapper sale porque acaba de aparecer una mision, o sea que su
   salida es una RESPUESTA y tiene que verse enseguida. Media curva gastada en
   despegar es medio segundo en el que no pasa nada.
   Va `cubic-bezier(.32, .06, .18, 1)`: sale rapido, cruza, y el ultimo tercio
   lo usa para frenar. Es la misma familia que `--ease-exp` estirada al tiempo
   de un viaje, asi que el movimiento del mapa y las entradas de las escenas se
   sienten de la misma mano.

   LA DURACION LA PUBLICA EL SCRIPT por elemento (`transitionDuration`), porque
   no es la misma para el que va a una mision que para los que hacen su ronda.
   Lo que queda aca es la curva y el valor de reserva. */
.znap { background: none; border: 0; }
/* v25: EL VIAJE PASA DE 2.150 A 3.400 ms. El pedido fue "que se muevan un poco
   mas lento", y hay un motivo que lo sostiene: un disco con una persona adentro
   cruzando la ciudad en dos segundos se lee como un objeto disparado, no como
   alguien yendo. Con la cadencia tambien mas larga, el Znapper sigue llegando
   antes de que la tarjeta cambie, que es la unica restriccion dura del ciclo.
   LA DURACION LA PUBLICA EL SCRIPT por elemento, porque no es la misma para el
   que va a una mision que para los que hacen su ronda: lo que queda aca es la
   curva y el valor de reserva. */
.leaflet-marker-icon.znap {
  transition: transform 3400ms cubic-bezier(.32, .06, .18, 1);
  z-index: 1000;
}
@media (prefers-reduced-motion: reduce) {
  .leaflet-marker-icon.znap { transition: none; }
}
/* ==========================================================================
   v25: EL ZNAPPER DEJA DE SER UN PUNTO Y PASA A SER ALGUIEN
   ==========================================================================
   Era un circulo naranja de 13px. Con eso, la escena mostraba cuatro PUNTOS
   moviendose, y un punto que se traslada puede ser cualquier cosa: un vehiculo,
   un paquete, un cursor. Lo que la escena tiene que decir es que va UNA PERSONA
   hasta un comercio, la saca la foto y vuelve — el producto entero de Znapp es
   que hay gente ahi.
   Ahora es un disco de 26px con una figura caminando adentro. El glifo va en
   SVG y no en un PNG por lo mismo que el de tienda: no cuesta un pedido por
   marcador, no se pixela, y hereda el color.
   EL ARO ES DE PAPEL Y ESO CAMBIO CON LA CIUDAD. Sobre la noche un aro blanco
   era lo mas claro de la pantalla y le ganaba a la campaña; sobre la ciudad
   clara el aro de papel es lo que despega al disco del mapa, igual que en los
   pines de comercio. */
.znap__p {
  position: absolute; left: 50%; top: 50%;
  width: 26px; height: 26px; margin: -13px 0 0 -13px;
  border-radius: 50%;
  display: grid; place-items: center;
  background: #F44D30; color: #fff;
  box-shadow: 0 0 0 2px rgba(250,248,245,.95), 0 6px 14px -5px rgba(52,49,44,.55);
  transition: scale .38s var(--ease-exp), box-shadow .38s var(--ease);
}
.znap__ic { width: 14px; height: 14px; display: block; }
/* EL QUE ESTA EN LA MISION DE ESTA CAMPAÑA CRECE. Es lo que permite que haya
   cuatro sin perder el hilo: el ojo sigue al que importa y los otros tres
   quedan como contexto vivo. Sin esta distincion, cuatro discos naranjas
   moviendose son ruido.
   CRECE CON `scale` Y NO CON `width`, que es lo que hacia antes: cambiar el
   ancho y el margen de un marcador dispara layout adentro del panel de Leaflet
   en cada cuadro de la transicion. `scale` lo resuelve el compositor y ademas
   se compone con el `transform` que Leaflet le escribe para colocarlo. */
.leaflet-marker-icon.znap.en-mision { z-index: 1200; }
/* Y SE CORRE, PARA NO TAPAR EL COMERCIO AL QUE LLEGA.
   Medido en el Chrome del cliente: el Znapper y el pin de su mision comparten
   la MISMA coordenada, asi que al llegar el disco naranja se apoyaba encima del
   disco teal y tapaba justo la tienda que acababa de relevar. Los dos discos
   miden 26 y 34 px: superpuestos se leen como un solo objeto de dos colores.
   Corrido abajo y a la izquierda queda A LA PUERTA del comercio, que ademas es
   lo que la escena quiere decir. El corrimiento va en `translate` y el tamaño
   en `scale` —propiedades individuales, se componen— y ninguno de los dos toca
   el `transform` que Leaflet le escribe para COLOCARLO en el mapa. */
.znap.en-mision .znap__p {
  scale: 1.26;
  translate: -15px 13px;
  box-shadow: 0 0 0 2.5px rgba(250,248,245,.98), 0 8px 20px -5px rgba(244,77,48,.75);
}
.znap.en-mision .znap__r { translate: -15px 13px; }
/* LA ONDA LA TIENE SOLO EL QUE ESTA EN MISION, y esto es jerarquia, no
   ahorro: cuatro ondas latiendo a la vez convierten el mapa en un semaforo y
   ninguna de las cuatro dice nada. Una sola dice "este esta yendo a tu
   comercio", que es la unica que la escena necesita. */
.znap__r {
  position: absolute; left: 50%; top: 50%;
  width: 28px; height: 28px; margin: -14px 0 0 -14px;
  border-radius: 50%; border: 2px solid rgba(244,77,48,.7);
  opacity: 0;
}
@media (prefers-reduced-motion: no-preference) {
  .znap.en-mision .znap__r { animation: znapOnda 2.6s var(--ease-exp) infinite; }
}
@keyframes znapOnda {
  0%   { opacity: .7; transform: scale(1); }
  100% { opacity: 0;  transform: scale(2.9); }
}

/* ==========================================================================
   LA ESTELA DEL ZNAPPER
   ==========================================================================
   Dos marcadores que reciben la MISMA coordenada al mismo tiempo que el
   Znapper, con la misma curva y la misma duracion, pero arrancando 160 y 320 ms
   mas tarde. Durante el viaje quedan atras y dibujan una cola; al llegar se
   juntan con el y desaparecen debajo.

   Por que hace falta: un punto que se traslada de A a B se lee como un punto
   que CAMBIO DE LUGAR. Con cola se lee como alguien YENDO, que es lo que la
   escena tiene que contar. Y no cuesta un solo cuadro de hilo principal: son
   dos escrituras de estilo por tramo, igual que el Znapper.

   El retardo va en la `transition`, no en un `setTimeout`: asi el compositor
   hace las tres interpolaciones y nunca se desincronizan entre si. */
.znap-eco { background: none; border: 0; }
/* MISMA CURVA Y MISMA DURACION QUE EL ZNAPPER, o la cola se desincroniza de
   quien la deja: lo unico que cambia es el retardo. */
.leaflet-marker-icon.znap-eco {
  transition: transform 3400ms cubic-bezier(.32, .06, .18, 1);
  z-index: 999;
}
.leaflet-marker-icon.znap-eco--1 { transition-delay: .17s; }
.leaflet-marker-icon.znap-eco--2 { transition-delay: .34s; }
.znap-eco::after {
  content: ''; position: absolute; left: 50%; top: 50%;
  border-radius: 50%; background: rgba(244,77,48,.32);
}
.znap-eco--1::after { width: 17px; height: 17px; margin: -8.5px 0 0 -8.5px; }
.znap-eco--2::after { width: 11px; height: 11px; margin: -5.5px 0 0 -5.5px; background: rgba(244,77,48,.14); }
@media (prefers-reduced-motion: reduce) {
  /* sin movimiento la cola no significa nada: sin viaje que seguir, son dos
     puntos naranjas encimados */
  .leaflet-marker-icon.znap-eco { display: none; }
}

/* LA MISION, ANTES DE QUE LLEGUE NADIE. El estado `is-mision` y su latido
   viven ahora con el resto de los pines, mas abajo, junto a la gota que
   modifican: tenerlos aca arriba, a seiscientas lineas del elemento, es como se
   llega a que una regla siga escrita para una forma que ya no existe. Estas
   dos lo estaban: pintaban un aro blanco de 3px y un cuadrado de 16px con
   radio 5, que era el pin cuadrado de la v20.
   El unico que se queda es el fotograma, porque lo usa el estado nuevo. */
@keyframes redPide {
  0%   { opacity: .75; transform: scale(1); }
  100% { opacity: 0;   transform: scale(2.6); }
}

/* --- EL MAPA --- */
/* ==========================================================================
   EL MAPA NO TERMINA CON SU ESCENA: ES EL FONDO DEL PRIMER ACTO
   ==========================================================================
   El espinazo agrupa las siete escenas en tres actos, y el primero —CONTROLA—
   son las dos primeras: la red y el hallazgo. Ese acto es "ver lo que pasa en
   tus tiendas", y el mapa ES el ver. Que se apagara al terminar la primera
   pantalla partia el acto justo en la mitad: la escena del hallazgo hablaba de
   53 comercios sin ningun lugar donde ponerlos, y las dos primeras pantallas se
   leian como dos temas en vez de como uno.

   `fixed` Y NO UN CONTENEDOR PEGAJOSO, y esa fue la decision de fondo. Para que
   el mapa cruce dos escenas con `sticky` habria que envolverlas en un padre
   comun, y eso mueve de lugar a `main > .esc:first-of-type`, al encaje de
   scroll y al descuento de la barra: tres sistemas que ya estan medidos y
   andando, tocados de una vez por un efecto de fondo. Fijo no mueve el
   marcado: el mapa se queda quieto y las escenas le pasan por encima. Las
   opacas lo tapan solas, sin una sola regla nueva.

   v22: ARRANCA EN EL CERO DEL DOCUMENTO, Y ES LO CONTRARIO DE ANTES.
   Hasta la v21 empezaba debajo de la barra (`top: var(--navh)`) porque la
   barra era papel translucido y el desenfoque dejaba la ciudad borrosa atras
   del logo. Con la barra de noche esa objecion se dio vuelta: lo que quedaba
   arriba del mapa era una franja de 69px de PAPEL BLANCO —el fondo del
   documento— y la barra oscura al 62% encima daba un gris sucio de 1512px de
   ancho, que es lo primero que se ve al entrar. Medido: `rgba(22,19,15,.62)`
   sobre blanco vale #6E6B68.
   Con el mapa desde el cero, la ciudad pasa POR DEBAJO de la barra y la barra
   se lee como vidrio sobre la ciudad, que es lo que tiene que ser. El
   desenfoque ahora ayuda: la ciudad se difumina justo donde vive el logo. */
.red__mapa { position: fixed; top: 0; left: 0; right: 0; bottom: 0; z-index: 1; }
/* por debajo de 1024 vuelve a vivir adentro de su escena. Un mapa fijo a
   pantalla completa detras de un documento que se scrollea con el dedo obliga
   al navegador a recomponer la capa en cada cuadro, y ahi el costo se nota. */
@media (max-width: 1024px) {
  .red__mapa { position: absolute; top: 0; }
}
.red__lienzo { position: absolute; inset: 0; background: var(--surface); }

/* ==========================================================================
   LOS MOSAICOS DE CARTO, LLEVADOS A LA MARCA — v25, DE VUELTA A LA LUZ
   ==========================================================================
   HISTORIA DE SEIS INTENTOS, Y EL SEXTO EMPIEZA POR CORREGIR UN DIAGNOSTICO:

     v18  `light_all` con contraste .88 y saturacion .5 -> casi blanco, no se
          leia una calle. Se bajaba el contraste de un mapa que ya era palido.
     v19  `voyager` con saturacion .72 y contraste 1.16 -> campo crema.
     v20  contraste a 1.34 -> mejora la trama y sigue crema.
     v21  se da por perdido.
     v22  se cambia a `dark_all` y se escribe que sobre claro NO HAY
          TRATAMIENTO POSIBLE. Esa conclusion era falsa, y se sostuvo dos
          rondas porque el filtro que la habria refutado NUNCA CORRIO:
     v24  MEDIDO EN CHROME: `.red__lienzo .leaflet-tile-pane` llegaba al
          navegador como `{ }`. Faltaba la APERTURA de este mismo comentario
          —las dos barras—, asi que el parser se comia la declaracion entera. El
          mapa que se vio durante tres rondas era `dark_all` CRUDO —azul y a
          #0e1013— y ninguna de las dos partes lo sabia. De ahi salio
          "esta muy oscuro, mas alla de ser dark mode": tenia razon, y no era
          una opinion sobre el modo oscuro.
     v25  SE VUELVE A LA LUZ, con el tratamiento al derecho.

   POR QUE SOBRE CLARO SI SE PUEDE, Y DONDE ESTABA EL ERROR DE LA v22.
   Decia: el teal (#13c1ac, luminancia .417) y `--surface` (.89) estan cerca en
   valor tonal, asi que sobre crema el teal no puede destacarse. La aritmetica
   es correcta y la conclusion no, porque el contraste no es solo luminancia:
   es TAMBIEN croma. Si el mapa no tiene saturacion, el teal es lo unico
   saturado de la pantalla y se ve de lejos aunque su valor tonal sea parecido.
   Es como se lee un pin en cualquier mapa claro que exista.
   Por eso lo primero que hace este filtro es BAJARLE LA SATURACION A LA
   CIUDAD, no subirsela: el color se lo reserva la campaña.

   EL TRATAMIENTO, FUNCION POR FUNCION. Positron viene blanco y frio.
     - `saturate(.3)` deja apenas el azul del rio, que es geografia y sirve
       para ubicarse, y le saca el resto: la trama urbana queda neutra.
     - `sepia(.4)` + `hue-rotate(-6deg)` + `saturate(1.4)` le devuelven un
       tinte calido. Sin esto el mapa es gris, que es el unico color que este
       sitio no admite: al lado de `--surface` (#FAF8F5) un gris frio se lee
       como un agujero.
     - `brightness(.84)` ES LA PIEZA QUE HACE QUE HAYA CALLES, y es lo
       contrario de lo que parece. Positron dibuja las CALLES EN BLANCO sobre
       una tierra casi blanca; la diferencia entre las dos existe, pero es de
       dos puntos de luminancia, o sea invisible arriba del 95%. Bajando el
       brillo la tierra se convierte en un campo de arena calida y el blanco de
       las calles queda como lo mas claro de la pantalla: la misma diferencia,
       leida en la zona donde el ojo si la distingue.
     - `contrast(.98)` BAJA, Y ESTO SE MIDIO. Subirlo era lo intuitivo y es el
       error: `contrast()` separa alrededor del 50% de gris, y aca los dos
       valores que hay que separar estan los DOS arriba del 95%, asi que los
       empuja a los dos a blanco puro. Probado en pantalla con 1,5 / 1,75 /
       2,05: a 1,75 el mapa queda LISO, sin una calle. Es exactamente la falla
       de la v18 —"un rectangulo casi blanco"— alcanzada desde el otro lado, y
       explica por que tres rondas de subir y bajar el contraste no arreglaron
       nada. EL CONTRASTE NUNCA FUE LA PALANCA: es el brillo.

   EL ORDEN NO ES INDIFERENTE, y esto ya costo una vuelta en la v22: `sepia()`
   tiñe los MEDIOS TONOS y casi no toca lo que ya es blanco. Va DESPUES de
   bajar el brillo, o no tiene nada que teñir y el mapa sale neutro. */
.red__lienzo .leaflet-tile-pane {
  filter: saturate(.42) brightness(.84) sepia(.37) hue-rotate(-8deg) saturate(1.42) contrast(.98);
}
/* `.red__lienzo` ES el contenedor de Leaflet, no su ancestro: con
   `.red__lienzo .leaflet-container` la regla no matcheaba nunca y quedaba el
   gris #ddd que trae `leaflet.css`, que sobre un sitio de neutros calidos se
   ve como un agujero. */
.red__lienzo.leaflet-container { background: var(--surface); font-family: inherit; }
/* EL RESET DE ESTA HOJA CONTRA LOS PANELES DE LEAFLET.
   Arriba de todo hay `img, svg, video { max-width: 100% }`. Los paneles de
   Leaflet son `div` absolutos SIN ancho, asi que ese 100% vale 0 y cualquier
   `<svg>` que cuelgue de un panel se recorta a nada. Leaflet trae el escape
   —`max-width: none !important`— pero escrito solo para `.leaflet-overlay-pane
   svg`, asi que un panel propio se queda afuera y la capa desaparece sin un
   solo error en consola.
   Ya paso una vez con el trazo del recorrido. Se generaliza a TODOS los
   paneles para que el proximo que agregue una capa no lo vuelva a diagnosticar
   desde cero. */
.red__lienzo .leaflet-pane > svg { max-width: none; max-height: none; }
/* la atribucion es obligatoria y no se saca: se pone discreta y legible */
.red__lienzo .leaflet-control-attribution {
  background: rgba(250,248,245,.78); color: var(--text-2);
  font-family: var(--mono); font-size: 9px; letter-spacing: .04em;
  padding: 2px 7px; border-radius: var(--r-anot) 0 0 0;
}
.red__lienzo .leaflet-control-attribution a { color: var(--text); }

/* LA MANCHA DE DENSIDAD. Un halo tenue donde la red concentra misiones. No
   inventa un dato: es donde estan los 46 pines que ya se ven, dicho con luz en
   vez de con un numero. Va encima de los mosaicos y debajo de los pines. */
.red__mapa::after {
  content: ''; position: absolute; inset: 0; z-index: 400; pointer-events: none;
  /* LA MANCHA DE DENSIDAD SE RETIRO Y LA REEMPLAZA HUELLA GANADA.
     Era un halo fijo, pintado a mano sobre el racimo de pines, que decia "aca
     trabaja la red" antes de que la red hiciera nada. Ahora cada comercio deja
     su propia mancha cuando entrega el dato (`.red__cob`), asi que la ciudad se
     cubre porque PASO algo, no porque haya un degradado puesto ahi. Un halo
     decorativo al lado de uno que significa algo vuelve ilegibles a los dos.

     v25: EL VELO VUELVE A SER DE PAPEL, Y ES MAS SUAVE QUE NUNCA.
     Sobre la noche era una viñeta que oscurecia los bordes; sobre la ciudad
     clara vuelve a ser lo que fue: papel saliendo del borde izquierdo, para
     que el titular no se apoye sobre una avenida.
     BAJA A UNA SOLA CAPA. La segunda —una viñeta radial— existia para meter el
     ojo al centro cuando toda la interfaz estaba a la izquierda. Con la
     tarjeta arriba a la derecha, el centro del mapa ya no es el unico lugar
     donde mirar, y una viñeta encima de la ciudad clara solo la ensucia.
     Empieza mas afuera que en la v21 (26% y no 22%) porque abajo hay menos que
     tapar: la columna de tres paneles se convirtio en un bloque centrado. */
  /* SE HACE CARGO DE LOS DOS TRABAJOS desde la v25, porque el velo propio del
     titular se retiro (ver arriba). Sube de .72 a .95 en el borde y estira la
     zona fuerte hasta el 30%, que es donde termina el bloque de texto en las
     dos ventanas medidas (1512 y 1920).
     LA DISOLUCION TERMINA EN 56% Y NO EN 38%: mas corto, el paso de "papel" a
     "ciudad" ocurre en 130px y se lee como una linea; estirado, no hay punto
     donde se pueda decir donde termina. Es la diferencia entre un velo y un
     recorte, y es la unica que importa aca.
     SIGUE SIENDO UNA SOLA CAPA, de borde a borde en las dos direcciones: su
     unica frontera es el borde de la pantalla, donde no hay nada con que
     contrastar. */
  background:
    linear-gradient(96deg,
      rgba(250,248,245,.95) 0%,
      rgba(250,248,245,.90) 22%,
      rgba(250,248,245,.62) 36%,
      rgba(250,248,245,.22) 47%,
      rgba(250,248,245,0) 58%);
}

/* ==========================================================================
   LA HUELLA DE CADA RELEVAMIENTO — v25
   ==========================================================================
   SE LLAMABA `.red__luz` Y SOBRE LA CIUDAD CLARA ESE NOMBRE MIENTE, asi que
   cambia. La historia de este disco es la de una pieza correcta con dos
   nombres equivocados seguidos:

     v21  sobre claro iba en `multiply`, o sea que para verse TENIA QUE
          OSCURECER la ciudad. Se llamaba luz y hacia lo contrario de la luz.
     v22  con la ciudad de noche paso a `plus-lighter` y el nombre se volvio
          cierto: cada comercio encendia su pedazo de ciudad.
     v25  la ciudad vuelve a ser clara. En vez de repetir el error de la v21,
          el disco deja de pretender ser luz: es una MANCHA DE MARCA, tenue,
          en mezcla normal. Sobre un mapa sin saturacion un teal al 20% se ve
          perfectamente sin tener que apagar nada, y donde la campaña paso dos
          veces el tinte se acumula solo.

   Y NO ES UN RADIO DE COBERTURA. No tiene borde, ni canto, ni escala, y por eso
   tampoco esta en la leyenda: no hay dato de cuanto territorio cubre una tienda
   y dibujar uno seria inventarlo. Dice "la campaña llego hasta aca", nada mas.

   SIN `mix-blend-mode`, Y ESO ES ADEMAS SEIS CAPAS COMPUESTAS MENOS. Una capa
   con mezcla obliga al navegador a aislarla y recomponerla contra el fondo;
   eran seis discos de 260px sobre el panel de mosaicos, y estaban en la lista
   de las cinco causas que dejaron la pagina en 0,2 cuadros por segundo.

   NO ANIMA `width`. Crece con `transform: scale()`, que corre en el
   compositor; animar el tamaño de un disco de 260px obligaria a repintar el
   mapa entero en cada cuadro. */
.red__cob {
  background: radial-gradient(circle closest-side,
              rgba(19,193,172,.26), rgba(19,193,172,.11) 44%, rgba(19,193,172,0) 70%);
  border-radius: 50%;
  opacity: 0;
  transform: scale(.28);
  transition: opacity 1.1s var(--ease), transform 2.2s var(--ease);
}
.red__cob.is-on { opacity: 1; transform: scale(1); }
/* el cierre del bucle: lo acumulado se va junto y rapido, para que el reinicio
   se lea como un corte de escena y no como que la pagina se apago */
.is-cierre .red__cob.is-on { opacity: 0; transition: opacity .7s var(--ease); }
@media (prefers-reduced-motion: reduce) {
  .red__cob { transition: opacity .3s linear; transform: scale(1); }
}

/* EL TRAZO DEL RECORRIDO. Se dibuja al mismo tiempo que el Znapper lo camina
   (el script le da el largo real del path y la duracion del tramo). Lo que
   queda dibujado ES la red: seis comercios sueltos son puntos, seis comercios
   unidos son un recorrido. */
/* `.red__tramo` SE RETIRO EN LA v23, con su capa de SVG entera.
   Dibujaba el recorrido del Znapper tramo a tramo, y hacia dos cosas mal a la
   vez. En pantalla: con la camara pegada a un comercio, la linea hacia el
   siguiente salia del cuadro y se leia como un rayo laser cruzando la ciudad.
   En rendimiento: era un `<path>` animado con `filter: drop-shadow()`, o sea
   una de las cinco capas que entre todas dejaron a la pagina en 0,2 cuadros
   por segundo.
   Y con cuatro Znappers viniendo de lugares distintos ya no significaba nada:
   un grafo de lineas rectas entre comercios afirma un recorrido que nadie
   hizo. Lo que queda de una mision cumplida es el pin encendido. */

/* LA LISTA DE PINES. Existe en el marcado antes que el mapa y es la que se
   recorre con Tab. Cuando Leaflet arma el mapa, cada boton SE MUDA adentro de
   su marcador; hasta entonces (o si Leaflet no carga nunca) queda fuera de
   pantalla pero accesible. Nunca se borra: es el control de verdad. */
.red__lista {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}
/* `.is-mapa` la pone el script sobre `.red__mapa`, no sobre un `.red`:
   ese elemento no existe desde la v18. */
.is-mapa .red__lista {
  position: static; width: auto; height: auto; overflow: visible;
  clip-path: none; white-space: normal;
}

/* LAS MISIONES DE OTRAS EMPRESAS. Titilan despacio y DESFASADAS entre si (el
   script les reparte un `--d` distinto): es la prueba social de la demo —la red
   trabaja para otros— dicha con movimiento en vez de con una frase. Todas al
   mismo tiempo se leerian como un parpadeo de la pantalla, no como actividad. */
/* el desfase se reparte con `:nth-of-type` y no con una variable por elemento:
   son cuarenta circulos que dibuja Leaflet, y alcanzan cinco tiempos distintos
   para que no se lea como un parpadeo de la pantalla. */
/* v20: LATEN, NO TITILAN. Antes solo cambiaban de opacidad, y cuarenta puntos
   subiendo y bajando de brillo se leen como un defecto de la pantalla. Ahora
   cada uno se hincha apenas al encenderse: es el mismo gesto que hace un pin de
   la campaña al recibir su dato, en chico y en gris. La ciudad queda respirando
   alrededor del recorrido en vez de parpadeando.
   `transform-box: fill-box` NO ES OPCIONAL: son circulos SVG que dibuja
   Leaflet, y sin eso el origen del `scale` es el de todo el lienzo del mapa, o
   sea que los cuarenta salen despedidos hacia una esquina en vez de crecer en
   su lugar.
   OCHO DESFASES Y NO CINCO: con cinco, los cuarenta caian en cinco grupos
   sincronizados y se veian las filas latir juntas. */
/* ==========================================================================
   LAS 40 MISIONES DE OTRAS EMPRESAS DEJARON DE TITILAR — v24
   ==========================================================================
   Latian despacio y desfasadas, y eran cuarenta. Se sacan por DOS motivos que
   apuntan al mismo lado.

   EL DE JERARQUIA, que es el que manda: son NIVEL 3, contexto. El criterio de
   esta hoja dice que el contexto no se mueve y no brilla. Su trabajo es decir
   "la red tambien trabaja para otros", y eso se dice UNA vez estando ahi, no
   cuarenta veces por minuto. Con la campaña encendiendose comercio por comercio
   al lado, cuarenta puntos latiendo son cuarenta competidores del protagonista.

   EL DE COSTO: son `<path>` de SVG, y una animacion de `transform` sobre SVG no
   la resuelve el compositor. Cada cuadro obliga a re-rasterizar la capa entera
   de superposiciones. Cuarenta elementos, para siempre, mientras la escena este
   a la vista.

   Lo que queda es la prueba social entera —los cuarenta puntos siguen ahi— y
   ademas siguen siendo el recorrido de los Znappers de fondo, que es un
   movimiento que SI significa algo: alguien yendo a una de esas misiones. */
/* ==========================================================================
   LOS PINES DE COMERCIO — v25: DISCOS QUE FLOTAN
   ==========================================================================
   QUE SE TIRO. La gota: un cuadrado con tres esquinas redondeadas girado 45
   grados, de 29px, oscura y con aro teal. Era correcta y decia lo que no habia
   que decir. Una gota apoyada es la forma de SEÑALAR UNA COORDENADA —"esto
   esta aca"— y esta escena no habla de coordenadas: habla de LUGARES a los que
   la red entra y a los que el visitante puede entrar con el dedo.

   EL DISCO LEVANTADO DICE LAS DOS COSAS. Tiene glifo adentro, asi que nombra
   que es un comercio sin leyenda; tiene sombra abajo, asi que se lee como un
   objeto separado del mapa; y como esta levantado, se puede tocar. Es la misma
   afordancia que un boton flotante, aplicada a un mapa.

   Y FLOTA DE VERDAD, QUE ERA EL PEDIDO. Sube y baja cinco pixeles en cuatro
   segundos y su sombra se achica cuando sube, que es lo unico que convierte
   dos movimientos en un objeto con peso. Sin la sombra acompañando, un disco
   que sube y baja se lee como que parpadea el tamaño.
   EL DESFASE ES EL `--d` QUE YA EXISTIA para escalonar la entrada: seis discos
   subiendo en el mismo cuadro se leen como que late la pantalla.

   COMO SE REPARTEN LAS PROPIEDADES, Y NO ES UN DETALLE. La flotacion usa
   `translate` y la entrada, el apuntado y el estado activo usan `scale`. Son
   las propiedades individuales de transformacion, o sea que SE COMPONEN: una
   animacion infinita y una transicion pueden convivir sobre el mismo elemento
   sin pisarse. Escritas las dos como `transform`, la segunda gana y el pin
   entra sin flotar —o flota sin entrar—, que es exactamente el bug que dejo la
   nota "el giro va en el mismo transform que la caida" en la v22. */
.red__marca { background: none; border: 0; }
.red__pin {
  position: absolute; inset: 0;
  padding: 0; border: 0; background: none; font: inherit;
  cursor: pointer;
}

/* LA SOMBRA EN EL PISO. Es lo que ancla el disco a una coordenada ahora que el
   disco ya no la toca: la punta de la gota se reemplaza por esto. */
.red__pinsom {
  position: absolute; left: 50%; bottom: 3px;
  width: 20px; height: 7px; margin-left: -10px;
  border-radius: 50%;
  background: rgba(52,49,44,.30);
  filter: blur(2.5px);
}

/* EL DISCO. Papel, aro de marca y el glifo de tienda adentro. */
.red__pinp {
  position: absolute; left: 50%; top: 3px;
  width: 34px; height: 34px; margin-left: -17px;
  border-radius: 50%;
  display: grid; place-items: center;
  background: var(--paper);
  color: var(--teal-field);
  box-shadow: 0 0 0 2px var(--teal), 0 7px 16px -6px rgba(52,49,44,.5);
  transition: background-color .34s var(--ease), color .34s var(--ease),
              box-shadow .34s var(--ease), scale .3s var(--ease);
}
.red__ic { width: 17px; height: 17px; display: block; }

/* LOS PINES CAEN, no aparecen: entran escalonados y la red se ve apareciendo
   comercio por comercio, que es lo que la escena tiene que decir. */
.js .red__pin { opacity: 0; }
.js .red__pin .red__pinp { scale: .45; }
.js .red__pin .red__pinsom { opacity: 0; }
.js .esc.is-en .red__pin {
  opacity: 1;
  transition: opacity .42s var(--ease-exp) var(--d, 0ms);
}
.js .esc.is-en .red__pin .red__pinp {
  scale: 1;
  transition: scale .56s var(--ease-exp) var(--d, 0ms),
              background-color .34s var(--ease), color .34s var(--ease),
              box-shadow .34s var(--ease);
}
.js .esc.is-en .red__pin .red__pinsom {
  opacity: 1; transition: opacity .5s var(--ease) var(--d, 0ms);
}

/* LA FLOTACION. Cuatro segundos y medio de ida y vuelta: por encima de eso se
   lee como que la pagina se colgo, por debajo como un nervio. Solo corre
   cuando la escena esta a la vista —fuera de ella no la ve nadie y sigue
   costando composicion— y solo despues de que el pin entro. */
@media (prefers-reduced-motion: no-preference) {
  .js .esc.is-en .red__pin .red__pinp {
    animation: pinFlota 4.5s var(--ease-io) var(--d, 0ms) infinite;
  }
  .js .esc.is-en .red__pin .red__pinsom {
    animation: pinPiso 4.5s var(--ease-io) var(--d, 0ms) infinite;
  }
}
@keyframes pinFlota {
  0%, 100% { translate: 0 0; }
  50%      { translate: 0 -5px; }
}
@keyframes pinPiso {
  0%, 100% { transform: scale(1);   opacity: .3; }
  50%      { transform: scale(.74); opacity: .16; }
}

/* APUNTAR Y APRETAR. La respuesta es al APRETAR, no al soltar: es la regla de
   respuesta directa y ademas es lo unico que hace que un pin se sienta un
   objeto y no una region clicable. */
@media (hover: hover) and (pointer: fine) {
  .red__pin:hover .red__pinp {
    scale: 1.18;
    box-shadow: 0 0 0 2px var(--teal), 0 12px 22px -6px rgba(52,49,44,.55);
  }
  .red__pin:hover .red__pinsom { opacity: .38; }
}
.red__pin:active .red__pinp { scale: 1.04; }

/* EN CAMINO. El disco late en marca desde que el Znapper sale y hasta que
   llega: es la mision PEDIDA, que es la mitad que la empresa compra. */
.red__pin.is-mision .red__pinp {
  box-shadow: 0 0 0 2.5px var(--teal), 0 7px 16px -6px rgba(52,49,44,.5);
}
/* EL LATIDO DE LA MISION PEDIDA. Va en `::after` y el destello de llegada en
   `::before`: son dos hechos distintos —"esto se pidio" y "esto llego"— y
   pueden solaparse un instante, asi que no pueden compartir pseudo-elemento. */
.red__pin::after {
  content: ''; position: absolute; left: 50%; top: 20px;
  width: 34px; height: 34px; margin: -17px 0 0 -17px;
  border-radius: 50%; border: 2px solid var(--teal);
  opacity: 0; pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
  .red__pin.is-mision::after { animation: redPide 1.7s var(--ease) infinite; }
}

/* CUMPLIDA. El disco se llena de marca y el glifo se da vuelta a papel. Sobre
   la ciudad clara eso se lee de lejos sin que el pin tenga que crecer ni
   brillar: es el unico teal pleno del mapa. */
.red__pin.is-lista .red__pinp {
  background: var(--teal-field); color: var(--on-teal);
  box-shadow: 0 0 0 2px var(--teal-field), 0 8px 18px -6px rgba(13,131,117,.55);
}
/* el que se esta mostrando en la tarjeta, ademas, crece */
.red__pin[aria-current="true"] .red__pinp { scale: 1.24; }
.js .esc.is-en .red__pin[aria-current="true"] .red__pinp { scale: 1.24; }

/* EL DESTELLO DE LLEGADA. Un anillo que se abre una sola vez cuando el dato
   entra: es el acuse de recibo de la escena. Sin el, el pin cambia de color y
   no se registra que ACABA de pasar algo. */
.red__pin::before {
  content: ''; position: absolute; left: 50%; top: 20px;
  width: 34px; height: 34px; margin: -17px 0 0 -17px;
  border-radius: 50%; border: 2px solid var(--teal);
  opacity: 0; pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
  .red__pin[aria-current="true"].is-lista::before { animation: redLlega 1s var(--ease-exp) 1; }
}
@keyframes redLlega {
  0%   { opacity: .8; transform: scale(1); }
  100% { opacity: 0;  transform: scale(3.4); }
}

/* el nombre del comercio al apuntar, como en la demo */
.red__pin .sr-only {
  position: absolute; width: auto; height: auto; overflow: visible;
  clip-path: none; white-space: nowrap;
  left: 50%; bottom: calc(100% - 6px);
  transform: translateX(-50%) translateY(4px);
  padding: 5px 9px; border-radius: var(--r-anot);
  background: var(--ink); color: var(--on-ink);
  font-family: var(--mono); font-size: 10px; font-weight: 600;
  letter-spacing: .04em;
  opacity: 0; pointer-events: none;
  transition: opacity .18s var(--ease), transform .18s var(--ease);
}
@media (hover: hover) and (pointer: fine) {
  .red__pin:hover .sr-only { opacity: 1; transform: translateX(-50%); }
}
.red__pin:focus-visible .sr-only { opacity: 1; transform: translateX(-50%); }

@media (prefers-reduced-motion: reduce) {
  .js .red__pin .red__pinp { scale: 1; }
  .js .red__pin .red__pinsom { opacity: 1; }
  .js .esc.is-en .red__pin { transition: opacity .3s var(--ease); }
}

/* ==========================================================================
   LA CAPA DE INTERFAZ DE LA ESCENA 1 — v25
   ==========================================================================
   TRES PIEZAS Y CADA UNA DECLARA SU NIVEL, que es lo que el criterio de
   protagonismo pide antes de agregar nada a una escena:

     NIVEL 1 · PROTAGONISTA   el mapa. No esta en esta capa: ES el fondo.
     NIVEL 2 · SOPORTE        `.red__cabe` (titular + llamada) a la izquierda,
                              y `.vuelta` (la evidencia) arriba a la derecha.
     NIVEL 3 · CONTEXTO       `.red__ley`, abajo a la izquierda. Sin superficie
                              propia, sin movimiento y sin brillo.

   QUE SE TIRO DE LA v21. Las tres piezas vivian en UNA columna de 452px
   apilada contra el borde izquierdo, de piso a techo. Fallaba en dos planos:

     - DE LECTURA: la tarjeta nombraba un comercio de Villa Urquiza a
       seiscientos pixeles del pin de Villa Urquiza. Nada ataba el dato al
       lugar; el mapa y la tarjeta se leian como dos cosas que no se hablan.
     - DE JERARQUIA: una columna llena de piso a techo contra el borde tapa un
       tercio del protagonista y se lee como el panel de una herramienta.

   AHORA HAY UN LADO PARA CADA COSA y el centro del mapa queda libre. La
   objecion vieja —"con paneles en los dos costados la banda de mapa que queda
   mide 300px"— era de cuando los dos paneles iban de piso a techo. La tarjeta
   ocupa el tercio superior derecho y nada mas: abajo a la derecha el mapa
   respira entero. */
.red__ui {
  position: relative; z-index: 4;
  display: grid;
  grid-template-rows: auto 1fr auto;
  /* LLENA SU ESCENA, NO SE FIJA UN ALTO PROPIO. Con `min-height: 100svh` esta
     capa medía la pantalla entera mientras su escena medía la pantalla MENOS
     la barra pegada: sobraban 69px y el pie caia abajo del pliegue. */
  height: 100%;
  /* LA BARRA SE DESCUENTA UNA SOLA VEZ, Y ANTES SE DESCONTABA DOS.
     `main > .esc:first-of-type` ya le resta `--navh` al alto de esta escena
     (la barra es `sticky`, o sea que ocupa lugar en el flujo y la primera
     pantalla empieza DEBAJO de ella). Sumarle aca otro `--navh` de relleno
     superior no separaba el titular de nada: lo bajaba 69px porque si.
     Las escenas 2 a 7 SI necesitan el descuento, porque ahi la barra flota por
     encima; por eso el `--navh` sigue en `.esc` y sale solo de esta. */
  padding: clamp(10px, 2vh, 26px) var(--margen) clamp(16px, 3vh, 30px);
  pointer-events: none;   /* el mapa de abajo tiene que poder recibir el puntero */
}
.red__ui > * { pointer-events: auto; }

/* ==================== EL TITULAR, CENTRADO EN SU MITAD ====================
   Va en la fila elastica y centrado en ella, no arriba de una pila. Eso es lo
   que hace que la escena tenga un lado de texto y un lado de mapa en vez de
   una columna y un sobrante.

   EL VELO SIGUE EXISTIENDO Y AHORA ES DE PAPEL. La regla del proyecto es que
   ningun texto se apoya sobre el mapa sin superficie propia, y sigue en pie:
   lo que cambia es de que lado. Sobre la ciudad clara el velo vuelve a ser
   `--surface` saliendo del borde izquierdo y disolviendose hacia el mapa.
   No lleva borde, ni radio, ni sombra —eso lo convertiria otra vez en un
   OBJETO compitiendo con la ciudad—: es el mapa, mas apagado.
   Se extiende MAS ALLA del texto por los cuatro lados; si terminara donde
   termina el texto se veria el rectangulo que vino a evitar. */
/* SIN RELLENO A LA DERECHA. Eran 40px que no separaban de nada —el bloque ya
   tiene `max-width`— y que el encuadre SI reservaba: `getBoundingClientRect`
   devuelve la caja con su relleno, asi que el mapa perdia 40px de ancho util
   por un valor decorativo. */
.red__cabe {
  grid-row: 2; align-self: center; justify-self: start;
  position: relative; padding: 6px 0;
  /* 460 Y NO 600, Y CADA RECORTE SE MIDIO CONTRA EL ANCHO DE LA CAMPAÑA.
     Este bloque declara `data-red-panel="izq"`, o sea que su borde derecho es
     el borde izquierdo del mapa util: cada pixel que ocupa es un pixel menos
     de ciudad donde repartir seis comercios. A 600 el titular igual se partia
     en dos lineas —los 140px de mas no compraban ni un renglon— y la campaña
     quedaba en 583px de ancho sobre una pantalla de 1512. */
  max-width: min(460px, 37vw);
}
/* ==========================================================================
   `.red__cabe::before` SE RETIRO EN LA v25, Y ES LA TERCERA FORMA QUE FALLA
   ==========================================================================
   Era el velo propio del titular, y tuvo tres versiones. v20: un panel blanco
   con borde, radio y sombra —un objeto apoyado sobre la ciudad—. v21-v24: un
   degradado lineal, que se disuelve hacia la derecha y CORTA EN SECO arriba y
   abajo, o sea que se veia el rectangulo que el panel vino a evitar. v25, en
   esta misma ronda: radial desde el borde izquierdo, que no corta por los lados
   pero **sigue siendo una forma**: una elipse palida flotando sobre el mapa, con
   su borde perfectamente visible contra la trama de la ciudad. El cliente lo
   describio como "un fondo blanco con una transparencia que queda mal y se
   notan esos recortes feos", y tenia razon las tres veces.

   EL DIAGNOSTICO, DESPUES DE TRES INTENTOS: **cualquier superficie que empiece y
   termine adentro de la pantalla tiene un canto, y un canto sobre una imagen
   ocupada se ve.** No hay opacidad ni difuminado que lo arregle: lo unico que no
   tiene canto es algo que llega a los cuatro bordes del viewport.

   POR ESO EL VELO ES UNO SOLO Y ES EL DE LA ESCENA. `.red__mapa::after` va de
   borde a borde en las dos direcciones: su unica frontera es el borde de la
   pantalla, donde no hay nada con que contrastar. La regla del proyecto —ningun
   texto se apoya sobre el mapa sin superficie propia— se sigue cumpliendo; lo
   que cambia es que la superficie es la escena y no un recuadro alrededor de
   las letras.
   ========================================================================== */
.esc--red .esc__h1 {
  /* MIRA EL ALTO, IGUAL QUE LA ESCALA COMUN. Esta escena declara su propio tope
     porque su titular comparte pantalla con el mapa y no puede crecer tanto
     como los otros seis. Al pasar la escala general a `min(vw, vh)` este se
     quedo atras: medido en la ventana del cliente (1920x919) daba 44 contra 46
     del resto, o sea la PRIMERA pantalla con el titular mas chico de la ruta. */
  font-size: clamp(30px, min(2.95vw, 4.9vh), 47px); line-height: 1.05; color: var(--ink);
}
.esc--red .esc__sub { color: var(--text-2); max-width: 46ch; }

/* ==================== LA LLAMADA, Y LA INVITACION AL LADO ====================
   EL BOTON NO FLOTA SOBRE EL MAPA, Y ES JERARQUIA. Un primario suelto encima
   de la ciudad compite con los pines por ser lo primero que se toca, y los
   pines son el protagonista. Al final del bloque de texto queda donde termina
   de leerse el argumento, que es cuando alguien decide.

   LA INVITACION A TOCAR ES NIVEL 3 Y SE PARECE A NIVEL 3: mono chico, tinta
   apagada, sin caja. Lo unico que se mueve es un punto que se desliza hacia el
   mapa, que es el gesto que nombra. La afordancia de verdad la dan los pines:
   flotan, crecen al apuntarlos y llevan cursor de mano. */
.red__acc {
  margin-top: clamp(16px, 2.4vh, 26px);
  display: flex; align-items: center; flex-wrap: wrap; gap: 10px 16px;
}
.red__cta { box-shadow: 0 8px 22px -8px rgba(19,193,172,.6); }
.red__toca {
  display: inline-flex; align-items: center; gap: 8px;
  font-family: var(--mono); font-size: 10.5px; font-weight: 600;
  /* `--text-3` (#837D74) sobre el papel del velo da 4,0:1, y esto es mono de
     10,5px EN VERSALITAS: texto chico, o sea que el umbral es 4,5. Ademas se
     apoya sobre el mapa, donde el fondo no es un color sino una foto. Sube a
     `--text-2` (#57524B), que da 6,9:1 y sigue leyendose como nivel 3 por
     tamaño y por caja, que es de donde tiene que salir la jerarquia. */
  letter-spacing: .1em; text-transform: uppercase; color: var(--text-2);
}
.red__tocap {
  width: 6px; height: 6px; border-radius: 50%; background: var(--teal); flex: none;
}
@media (prefers-reduced-motion: no-preference) {
  .red__tocap { animation: tocaAlla 2.8s var(--ease-io) infinite; }
}
@keyframes tocaAlla {
  0%, 62%  { transform: translateX(0);    opacity: .35; }
  80%      { transform: translateX(7px);  opacity: 1; }
  100%     { transform: translateX(14px); opacity: 0; }
}

/* ==========================================================================
   LA TARJETA DE EVIDENCIA — v25, SALE DEL PIN
   ==========================================================================
   REEMPLAZA A `.ficha`, QUE ERA UNA PLANILLA. La anterior listaba costo,
   duracion, fotos y cobertura en una grilla de cuatro celdas: cuatro numeros
   correctos que no muestran lo que Znapp devuelve. Lo que devuelve es una FOTO
   de la gondola, y no habia una sola en toda la escena.

   LA FOTO ARRIBA Y LOS NUMEROS ABAJO, SEPARADOS. Arriba lo que vuelve, con su
   rotulo diciendo QUE MUESTRA —no de donde es, porque no hay foto verificada
   de estos seis locales—. Abajo los datos de ese comercio, que si son suyos.
   La linea entre las dos mitades es lo que hace que la tarjeta no afirme algo
   que nadie puede sostener.

   POR QUE VUELVE A SER DE PAPEL. En la v22 se pinto de noche con un argumento
   correcto: sobre una ciudad oscura, un rectangulo blanco es un objeto pegado
   encima. Con la ciudad clara el argumento se da vuelta solo — una tarjeta
   oscura sobre un mapa claro es exactamente el mismo objeto pegado encima, y
   ademas le pone marco de luto a una foto de gondola.

   SE ANCLA ARRIBA A LA DERECHA Y NO ES DECORATIVO: es del lado donde estan los
   comercios de la campaña, asi que el hilo que la ata a su pin cruza poco mapa.
   `position: absolute` y no una celda de la grilla, porque tiene que poder
   solaparse con la fila elastica sin empujar al titular. */
.vuelta {
  position: absolute; z-index: 5;
  /* v26: SE DESPEGA DEL TECHO. Con `1.8vh` la tarjeta quedaba a 16px del borde
     de la escena, o sea a 17px de la barra: pegada, y leyendose como parte de
     ella en vez de como un objeto apoyado sobre el mapa. El aire de arriba es
     lo que la separa de la navegacion. */
  top: clamp(14px, 3.4vh, 34px); right: var(--margen);
  width: min(330px, 29vw);
  color: var(--ink);
  /* PAPEL Y NO `--surface`: es la superficie mas alta de la escena —flota sobre
     el mapa y sobre el velo, que ya es `--surface`— y una tarjeta del mismo tono
     que su fondo no se lee como apoyada encima, se lee como un recorte. */
  background: var(--paper);
  /* EL CANTO SE REHACE, Y EL DE MARCA SE VA.
     Tenia `border-left: 3px solid var(--teal)` para nombrarla como la respuesta.
     Fallaba en dos cosas: un slab de 3px PELEA CON EL RADIO —el vertice
     izquierdo quedaba como un arco teal grueso, que es el detalle que delata una
     caja armada a mano— y ademas repetia un color que la tarjeta ya lleva tres
     veces adentro (la chapa de la foto, el punto de estado y la barra de
     cobertura). La marca ya estaba dicha.
     Lo que queda es un canto de UN pixel en la linea marcada, no en la suave: a
     1px sobre papel, `--line` no separa nada y la tarjeta parece flotar sin
     borde. Con `--line-2` el canto existe y sigue siendo una linea. */
  border: 1px solid var(--line-2);
  border-radius: var(--pan-r);
  /* SIN `backdrop-filter`, Y ES UNA DE LAS CINCO CAPAS QUE MATABAN LOS
     CUADROS. Un desenfoque de fondo obliga al navegador a LEER lo que hay
     detras, borronearlo y recomponer; apoyada justo encima de un mapa, eso
     pasa en cada cuadro en que el mapa cambia. Lo que hacia el desenfoque
     —separarla del mapa— lo hace mejor la sombra, y gratis. */
  /* TRES SOMBRAS Y CADA UNA HACE UNA COSA: el contacto (2px, corta y densa),
     la elevacion (40px, amplia y suave) y un pelo de contorno abajo, que es lo
     que evita que el borde inferior se disuelva contra un mapa claro. Una sola
     sombra grande da una nube; tres capas dan un objeto apoyado. */
  box-shadow:
    0 1px 2px rgba(52,49,44,.10),
    0 18px 40px -16px rgba(52,49,44,.38),
    0 2px 5px -2px rgba(52,49,44,.14);
  overflow: hidden;
  /* EL ORIGEN DE LA ENTRADA LO PUBLICA EL SCRIPT. `--ox`/`--oy` son la
     direccion en la que queda el pin que acaba de entregar, medida en pantalla
     y en porcentaje de la propia tarjeta: con eso el crecimiento arranca del
     lado correcto sin que la hoja sepa donde cae ningun comercio. */
  transform-origin: var(--ox, 50%) var(--oy, 0%);
}

/* EL HILO QUE ATA LA TARJETA A SU PIN.
   Es lo que convierte "una tarjeta que aparece" en "este comercio entrego".
   Vive AFUERA de la tarjeta porque tiene que cruzar el mapa hasta el pin, y
   adentro lo cortaria el redondeo.
   Todo lo publica el script en cuatro variables medidas en pantalla: el punto
   de partida, el largo y el angulo. La hoja no sabe donde cae ningun comercio,
   que es lo unico que la mantiene honesta cuando los datos cambien. */
/* v25.1: ES UN TRAZO, NO UNA BARRA. La version anterior era un `div` de 1,5px
   de alto rotado con `transform`, y tenia tres defectos que se ven todos a la
   vez en una captura:
     - un rectangulo rotado tiene CANTO: sobre 500px de diagonal se lee como una
       regla de plano, no como un hilo;
     - salia fijo de la esquina inferior izquierda de la tarjeta, asi que para
       los comercios que caen a la derecha el trazo pasaba por debajo de la
       tarjeta antes de empezar a verse;
     - y crecia con `scaleX`, que estira el degrade junto con la forma.
   Ahora es un `path` que el script recalcula: sale del punto del borde de la
   tarjeta que MIRA al pin y llega en curva. Se dibuja con `stroke-dashoffset`,
   que es la forma de que un trazo aparezca AVANZANDO en vez de estirandose: el
   grosor y el degrade se quedan quietos mientras el largo crece.
   LA CAPA CUBRE LA ESCENA ENTERA (`inset: 0`) y las coordenadas del `path` son
   pixeles de esa capa, o sea el mismo sistema en el que `anclar()` ya medía. */
.red__hilo {
  position: absolute; inset: 0; z-index: 4;
  width: 100%; height: 100%;
  pointer-events: none; overflow: visible;
  opacity: 0;
  transition: opacity .34s var(--ease);
}
.red__hilot {
  /* 1,25px y punta redonda: a 1,5 con punta cuadrada el extremo debil terminaba
     en un tajo. Con `round` el trazo se apaga en vez de cortarse. */
  stroke-width: 1.25; stroke-linecap: round;
  /* el largo real lo publica el script en `--largo`, medido con
     `getTotalLength()`: una curva no mide lo que mide su cuerda */
  stroke-dasharray: var(--largo, 0);
  stroke-dashoffset: var(--largo, 0);
  transition: stroke-dashoffset .58s var(--ease-exp);
}
/* SIN `.is-mapa` DELANTE, Y ESTO COSTO UNA SEGUNDA VUELTA SOBRE EL MISMO BUG.
   `is-mapa` la pone el script sobre `.red__mapa`; el hilo vive en `.red__ui`,
   que es su HERMANA, asi que el descendiente nunca matcheaba y el hilo quedaba
   en opacidad 0 con todas sus medidas bien escritas. Medido con una sonda: las
   cuatro variables estaban puestas y `opacity` valia 0.
   No hace falta el guard: `is-on` la pone `anclar()`, que ya sale antes si el
   mapa no se armo. */
.red__hilo.is-on { opacity: 1; }
.red__hilo.is-on .red__hilot { stroke-dashoffset: 0; }
@media (prefers-reduced-motion: reduce) {
  .red__hilo { transition: opacity .2s linear; }
  /* sin movimiento reducido el trazo aparece entero, no dibujandose */
  .red__hilot { transition: none; stroke-dashoffset: 0; }
}
/* abajo de 1024 la tarjeta deja de flotar sobre el mapa y el hilo no ata nada */
@media (max-width: 1024px) {
  .red__hilo { display: none; }
}

/* --- LO QUE VUELVE --- */
/* LA FOTO LLEVA SU PROPIA LINEA ABAJO. Sin ella, el borde inferior de la imagen
   es el borde de la imagen: cuando la foto que entra es clara —hay seis y no
   todas son oscuras— el limite entre la foto y los datos desaparece y la
   tarjeta se lee como un bloque. Una linea de 1px lo declara siempre. */
.vuelta__ph {
  position: relative; margin: 0; line-height: 0;
  background: var(--surface-3);
  border-bottom: 1px solid var(--line-2);
}
.vuelta__img {
  /* LA FOTO BAJA DE 190 A 156, Y NO ES ESTETICA: ES EL ENCUADRE.
     La tarjeta declara `data-red-panel="arriba"`, o sea que su alto es alto que
     el mapa NO puede usar para poner la campaña. Con la foto a 190 la tarjeta
     medía 490px sobre una escena de 830 —el 59%— y los seis comercios quedaban
     apretados en una franja de 310px contra el borde de abajo.
     Sigue siendo lo mas grande de la tarjeta, que es lo que la v22 vino a
     arreglar: la respuesta a "¿que vuelve de una mision?" es la foto. */
  display: block; width: 100%; height: clamp(104px, 14vh, 134px);
  object-fit: cover;
}
/* el pie va SOBRE la foto y con degradado propio: una foto de gondola tiene
   zonas claras y oscuras, asi que el texto necesita su propio piso o se pierde
   en la mitad clara */
.vuelta__pie {
  position: absolute; left: 0; right: 0; bottom: 0;
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 20px 12px 8px;
  background: linear-gradient(rgba(26,24,21,0), rgba(26,24,21,.84));
  font-size: 10.5px; line-height: 1.3; color: rgba(247,244,239,.94);
}
.vuelta__pk {
  font-family: var(--mono); font-size: 9px; font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase;
  padding: 3px 7px; border-radius: var(--r-anot-2);
  background: var(--teal-field); color: var(--on-teal);
}

/* --- DE QUE COMERCIO SALIO --- */
.vuelta__cuerpo { padding: clamp(12px, 1.25vw, 16px); }
.vuelta__de {
  display: flex; align-items: center; gap: 8px;
  font-family: var(--mono); font-size: 10px; font-weight: 700;
  letter-spacing: .14em; text-transform: uppercase; color: var(--text-3);
}
.vuelta__pt { width: 7px; height: 7px; border-radius: 50%; background: var(--teal); flex: none; }
/* EL PUNTO DE LA TARJETA NO PULSA. Latia igual que el del rotulo de la escena,
   y esta ADENTRO del soporte: un punto parpadeando al lado de la frase
   "Misión cumplida" —que ya dice lo mismo con palabras— le compite a la foto.
   Es ademas una animacion de `box-shadow`, o sea repintado por cuadro. */
.vuelta__n { margin-left: auto; font-size: 13px; color: var(--ink); }
.vuelta__nk { font-size: 9px; letter-spacing: .1em; color: var(--text-3); }

.vuelta__tienda {
  margin-top: 8px;
  font-size: clamp(16px, 1.35vw, 20px); font-weight: 700;
  line-height: 1.12; letter-spacing: var(--tr-h2);
}
.vuelta__dir { margin-top: 3px; font-size: 11.5px; line-height: 1.35; color: var(--text-2); }

/* ==================== LA COBERTURA DE SURTIDO ====================
   El unico dato por comercio de la campaña publicada que habla del NEGOCIO y
   no de la operacion: cuanto del surtido esperado estaba efectivamente en esa
   gondola. Estaba en `mapa.py` desde el principio y no se pintaba en ningun
   lado.
   La barra se llena con un ancho animado sobre un fondo fijo; entra con la
   tarjeta y con un retardo corto, para que se lea como el remate del dato y no
   como una barra que ya estaba. */
.vuelta__cob { margin-top: 9px; }
.vuelta__cobk {
  display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
  font-family: var(--mono); font-size: 9px; font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase; color: var(--text-3);
}
.vuelta__cobk b {
  font-family: var(--font); font-size: 16px; font-weight: 700;
  letter-spacing: var(--tr-sub); color: var(--ink);
}
.vuelta__barra {
  /* LA PISTA SE HUNDE Y LA BARRA SE LLENA. A 4px con los dos extremos al ras,
     la barra y su pista se leian como una sola linea de dos colores. Con la
     pista un tono mas oscura que el papel de la tarjeta y las dos puntas
     redondeadas del todo, hay un canal y algo adentro, que es lo que un
     porcentaje tiene que parecer. */
  display: block; margin-top: 7px; height: 5px; border-radius: 999px;
  background: var(--surface-3);
  box-shadow: inset 0 1px 1px rgba(52,49,44,.07);
  position: relative; overflow: hidden;
}
.vuelta__barra::after {
  content: ''; position: absolute; inset: 0;
  width: var(--pc, 0%);
  border-radius: 999px;
  /* `--teal-field` y no `--teal`: la barra se apoya sobre `--surface-3`, y el
     teal de acento contra ese tono da 1,9:1. Es el mismo motivo por el que
     existen los dos tokens. */
  background: var(--teal-field);
  transition: width .62s var(--ease-exp) .1s;
}
@media (prefers-reduced-motion: reduce) {
  .vuelta__barra::after { transition: none; }
}

/* TRES DATOS EN UNA FILA, no cuatro en una grilla de dos por dos. Lo que
   importa de una mision es que se relevo, en cuanto y cuanto costo.
   LOS VALORES PESAN MAS QUE SUS ROTULOS: en una tarjeta de evidencia lo que se
   mira son los numeros, y hasta la v21 tenian el mismo peso que la palabra
   "Relevado". */
.vuelta__datos {
  /* LA LINEA VA A SANGRE, DE CANTO A CANTO. Con el relleno de la tarjeta a los
     lados quedaba una raya flotando en el medio, con dos huecos de 16px; a
     sangre es una DIVISION de la tarjeta, que es lo que efectivamente separa:
     arriba el comercio, abajo los numeros de la operacion. Los margenes
     negativos son exactamente el relleno del cuerpo. */
  margin: 10px calc(clamp(12px, 1.25vw, 16px) * -1) 0;
  padding: 9px clamp(12px, 1.25vw, 16px) 0;
  border-top: 1px solid var(--line);
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px;
}
.vuelta__datos dt {
  font-family: var(--mono); font-size: 8.5px; font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase; color: var(--text-3);
}
.vuelta__datos dd {
  margin: 3px 0 0; font-family: var(--mono);
  font-size: 13px; font-weight: 600; letter-spacing: -.01em; white-space: nowrap;
  color: var(--ink);
}
/* `.vuelta__lee` SE RETIRO EN LA v22, con su dato y su regla.
   Era la linea de analisis al pie de la tarjeta, dos renglones por comercio.
   Se saco por el pedido explicito de menos texto, y resistio poco: es la frase
   mas vaga de la pagina, dice lo mismo con otras palabras en los seis
   comercios, y compite justo con la foto. El campo `lectura` salio tambien de
   `mapa.py`: un dato que no se pinta en ningun lado es la clase de cosa que
   dentro de tres meses alguien vuelve a cablear creyendo que falta. */

/* `.vuelta.is-nueva` SE RETIRO EN LA v23, Y ES EL BUG QUE EL CLIENTE VIO.
   La entrada de la tarjeta se hacia poniendo esa clase —que atenua al 20% y
   desenfoca 4px— y sacandola en un doble `requestAnimationFrame`. Cuando la
   pagina dejo de entregar cuadros, esos dos nunca llegaban: medido en Chrome,
   la tarjeta se quedaba borrosa y vacia CUATRO SEGUNDOS SEGUIDOS.
   El razonamiento original era bueno —el estado por defecto tiene que ser
   VISIBLE— y se rompio igual, porque seguia dependiendo de que alguien contara
   cuadros. Ahora la entrada la corre `element.animate()` desde el script: la
   maneja el mismo motor que las transiciones y al terminar vuelve sola al
   estado del documento. */

/* ==================== LA LEYENDA, NIVEL 3 ====================
   Era el tercer bloque de la escena para decir cuatro palabras. Ahora es una
   linea al pie con su velo minimo, y nada mas. La nota de honestidad se queda:
   no es negociable. */
.red__ley {
  grid-row: 3; align-self: end; justify-self: start;
  display: flex; align-items: center; flex-wrap: wrap; gap: 6px 14px;
  padding: 7px 12px; border-radius: var(--r-pill);
  /* sin desenfoque de fondo, por el mismo motivo que la tarjeta */
  background: rgba(250,248,245,.88);
  font-family: var(--mono); font-size: 9.5px; font-weight: 500;
  /* NIVEL 3. La leyenda existe para que un pin signifique algo la primera vez
     que alguien lo mira, y despues sobra: no puede pedir atencion cada vez que
     la vista pasa por abajo. */
  /* MISMO CASO QUE LA INVITACION, y aca el texto es todavia mas chico (9,5px).
     `--text-3` daba 3,9:1 sobre el velo al 88%. La leyenda es nivel 3 y tiene
     que PODER LEERSE: es lo que hace que un pin signifique algo la primera vez.
     Lo que la mantiene callada es el tamaño y que no tiene color, no que sea
     ilegible. */
  letter-spacing: .08em; text-transform: uppercase; color: var(--text-2);
}
.red__lg { display: inline-flex; align-items: center; gap: 6px; }
/* LAS MUESTRAS SON LAS FORMAS DE VERDAD, no puntos de colores. Si el mapa
   distingue un punto de venta de un Znapper por la FORMA, la leyenda tiene que
   usar esa misma forma o no explica nada. */
.red__lp { width: 10px; height: 10px; flex: none; border-radius: 50%; }
.red__lp--pdv { border: 2px solid var(--teal); background: var(--paper); }
.red__lp--on  { background: var(--teal); box-shadow: 0 0 0 2px rgba(19,193,172,.22); }
.red__lp--znap { background: #F44D30; box-shadow: 0 0 0 2px rgba(244,77,48,.22); }
/* la muestra de "otra empresa" tiene que ser el MISMO punto que el mapa dibuja,
   y sobre la ciudad clara ese punto es tinta apagada, no una brasa */
.red__lp--off { background: rgba(131,125,116,.55); }
/* `--text-4` (#A29B90) sobre el velo al 88% da 2,6:1, o sea que la linea de
   honestidad de la escena —la que dice que esto es una reconstruccion de una
   campaña publicada— era la menos legible de la pantalla. Es justo la que no
   se puede esconder. Sube a `--text-3`: 4,0:1, y sigue siendo lo mas callado
   de la leyenda porque va sin versalitas y con la misma tinta que su etiqueta. */
.red__nota { color: var(--text-3); text-transform: none; letter-spacing: .02em; }

/* EL BLOQUE DE SIMBOLOS SVG NO OCUPA LUGAR NI SE LEE.
   Define UNA vez el glifo de tienda que usan los seis pines del mapa y los 53
   comercios de la grilla del hallazgo. Tiene que estar EN EL DOCUMENTO —`<use>`
   resuelve por id en todo el documento, no por cercania— y no tiene que verse.
   NO SE ESCONDE CON `display: none`: Chrome y Firefox lo aceptan, pero un
   `<symbol>` referenciado desde un ancestro apagado ha dado problemas en Safari
   mas de una vez. Un bloque de 0x0 con `overflow: hidden` no depende de eso.
   Y NO LLEVA `hidden`: el atributo lo apagaria con `display: none` desde el
   reset, que es justo lo que se esta evitando. */
.simbolos { position: absolute; width: 0; height: 0; overflow: hidden; }

/* ==========================================================================
   ESCENA 2 · EL HALLAZGO — v25
   ==========================================================================
   UNA SOLA GRILLA Y TRES LECTURAS. La version de la v19 ponia las dos
   mediciones en dos filas paralelas y el visitante tenia que comparar dos
   dibujos. Con una sola grilla, la comparacion la hace el MOVIMIENTO: se ven
   los mismos 53 comercios encenderse, vaciarse y volver a llenarse.

   v25: LOS 53 SON GLIFOS DE TIENDA, NO CIRCULOS. Ver el comentario de la
   escena en `escenas_1.html`. Y son TRES estados, no dos: el del medio es la
   misma medicion dada vuelta —las 38 donde no habia nada— y es el que duele.

   EL UMBRAL LO PONE UNA VARIABLE Y UN `nth-child`, NO UNA CLASE POR COMERCIO.
   Cada `<i>` sabe su posicion (`--k`) y la hoja mira que boton esta activo. El
   script escribe UNA clase en UN boton y los 53 se reacomodan solos. Sin eso
   habria que recorrer 53 nodos en JavaScript cada vez que el ciclo avanza.
   ========================================================================== */
/* LA CIUDAD SIGUE ATRAS, PERO CALLADA. El velo la deja como textura, no como
   mapa que se lee: aca el contenido son 53 comercios y una frase, y una avenida
   con nombre atras de eso compite y gana. Lo que aporta es CONTINUIDAD —los 53
   comercios estan en la ciudad que se acaba de ver— y eso se consigue con que
   la ciudad se INSINUE, no con que se lea.

   v25: VUELVE A SER PAPEL, Y EL NUMERO SE ELIGIO MIRANDO.
   En la v22 esta escena se pinto de noche para acompañar al mapa, con un velo
   al 70%. Con el mapa de vuelta en luz, ese mismo velo dejaba un scrim marron
   sobre una ciudad clara y los pines de la escena anterior asomaban por debajo
   de la grilla: se veia una tienda teal flotando entre los 53 comercios y no
   habia forma de saber cual era cual.
   A .94 sobre el mapa el texto en `--text` conserva su contraste contra
   `--surface` y la trama de la ciudad queda apenas insinuada.
   Abajo de 1024 el mapa vuelve a su escena, asi que esto queda opaco solo. */
.esc--hall { background: var(--surface); color: var(--ink); }
/* EL TITULAR DE ESTA ESCENA BAJA UN ESCALON. Comparte columna con la lectura
   —que son tres frases de hasta dos renglones— y a la escala general de la ruta
   se partia en tres lineas dejando el campo de marca en dos bloques desparejos. */
/* `text-wrap: pretty` Y NO `balance`, QUE ES LO QUE HEREDA.
   El titular general de la ruta usa `balance`, y esta bien casi siempre:
   reparte los renglones para que no quede uno de dos palabras. Aca hacia lo
   contrario de lo que hace falta. "Salió de tu depósito." ENTRA en una linea, y
   `balance` la partia igual en "Salió de / tu depósito." para equilibrarla con
   la segunda frase; con las dos partidas, el titular salia en CUATRO renglones
   y el campo de marca en dos bloques de ancho distinto.
   `pretty` no equilibra: solo evita que la ultima linea quede huerfana, que es
   lo unico que hace falta cuando las lineas ya vienen decididas por un `<br>`. */
.esc--hall .esc__h2 { font-size: clamp(28px, 2.85vw, 41px); text-wrap: pretty; }
@media (min-width: 1025px) {
  .esc--hall { background: rgba(250,248,245,.94); }
}
.hall {
  width: 100%; max-width: var(--wrap); margin-inline: auto; padding-inline: var(--gut);
  /* LA COLUMNA DE TEXTO ES LA MAS ANCHA, Y ESO SE INVIRTIO EN LA v25.
     Venia .9 contra 1 a favor de la grilla, de cuando la grilla era el
     contenido y el texto un pie. Ahora el contenido son TRES FRASES que hay que
     leer y la grilla las ilustra: nueve columnas de iconos entran comodas en
     480px y no ganan nada con mas. */
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.02fr);
  gap: clamp(28px, 4vw, 64px); align-items: center;
}
.hall__pie {
  margin-top: clamp(16px, 2.4vh, 26px); padding-top: 12px;
  border-top: 1px solid var(--line);
  font-size: 12px; line-height: 1.45; color: var(--text-3); max-width: 44ch;
}

/* ==================== LAS TRES LECTURAS ====================
   NIVEL 2. Es lo que hay que LEER, y por eso el bloque tiene alto reservado:
   las tres frases no miden lo mismo y sin un piso fijo el pie de abajo salta en
   cada cambio de estado. Un texto que empuja al de abajo se lee como un error
   de carga, no como un cambio de dato. */
.hall__leer {
  margin-top: clamp(18px, 2.6vh, 28px);
  min-height: 8.4em;
}
.hall__p[hidden] { display: none; }
.hall__d {
  font-size: clamp(16px, 1.45vw, 20px); line-height: 1.42; color: var(--ink);
  letter-spacing: var(--tr-sub);
}
.hall__d b { font-weight: 700; }
.hall__x {
  margin-top: 9px; max-width: 46ch;
  font-size: clamp(13px, 1.05vw, 14.5px); line-height: 1.55; color: var(--text-2);
}

/* ==================== EL SELECTOR ====================
   TRES ESTADOS, NO UN MENU: la escena compara tres lecturas de UNA medicion y
   cualquier control con mas opciones estaria mintiendo sobre lo que hay. */
.hall__sw {
  display: inline-flex; gap: 4px; padding: 4px;
  border-radius: var(--r-pill);
  background: var(--surface-2); border: 1px solid var(--line);
}
.hall__op {
  padding: 7px 15px; border: 0; border-radius: var(--r-pill);
  background: none; font: inherit;
  font-size: 12.5px; font-weight: 600; letter-spacing: var(--tr-sub);
  color: var(--text-2); cursor: pointer;
  transition: background-color .22s var(--ease), color .22s var(--ease), transform .16s var(--ease);
}
@media (hover: hover) and (pointer: fine) { .hall__op:hover { color: var(--ink); } }
/* TODO LO QUE SE APRIETA SE HUNDE. Es la unica forma de que un control se
   sienta conectado con el dedo o con el clic, y tiene que ser la misma en toda
   la pagina: distintos gestos para el mismo tipo de accion es justo lo que hace
   que una interfaz se sienta armada por pedazos. */
.hall__op:active, .cov__opt:active,
.tab__pt:active .tab__ptn { transform: scale(.97); }
/* EL PIN DEL MAPA SE HUNDE CON `scale`, NO CON `transform`, y no puede entrar
   en la regla comun de arriba: sobre el disco corre ademas una animacion
   infinita de flotacion escrita en `translate`. Las propiedades individuales se
   componen; un `transform` las pisaria y el pin dejaria de flotar al apretarlo. */
/* LA COLUMNA DE LA CADENA SE HUNDE CON LUZ, NO CON ESCALA. La regla comun de
   arriba achica el control al 97%, y en una columna de 420px de alto eso se
   ve como que la pieza se despega de sus vecinas y deja ver el fondo entre
   medio, que es justo lo contrario de "esto es una barra continua". El gesto
   tiene que ser el mismo —algo pasa al apretar— y la forma la que le sirva a
   la pieza. */
.cad__col:active { filter: brightness(.94); }
.hall__op.is-active { background: var(--teal-field); color: var(--on-teal); }

/* ==================== LOS 53 COMERCIOS ====================
   EL APAGADO ES UN COMERCIO QUE EXISTE Y NO TIENE NADA TUYO, no un hueco: por
   eso se dibuja igual que los demas y en la tinta mas callada de la paleta. Los
   53 estuvieron todos relevados; lo que cambia es que encontro la red en cada
   uno. Dibujar los 38 como vacios diria que nadie fue, que es falso y ademas
   mas debil. */
/* v26: LA GRILLA CRECE CON EL ALTO, PORQUE ES EL PROTAGONISTA.
   El hallazgo llenaba el 52% de la pantalla en 1920 —la escena mas vacia de la
   ruta— y la razon es que sus 53 glifos son un objeto de tamaño FIJO: nueve
   columnas de circulos cuya medida sale del ancho de su columna, que a su vez
   sale del contenedor. En una pantalla mas alta la escena crecia y la pieza que
   hay que mirar no.
   La separacion entre glifos ahora sale tambien del alto (`.9vh`), asi que en
   una ventana alta la grilla se abre en vez de quedar apretada arriba. No es
   rellenar por rellenar: es que la ilustracion de "53 comercios" ocupe el lugar
   que le corresponde en la unica escena que la usa. */
.hall__grid {
  display: grid; grid-template-columns: repeat(9, 1fr);
  gap: clamp(6px, max(.7vw, .9vh), 14px);
  margin-top: clamp(16px, 3vh, 30px);
}
.hall__grid i {
  aspect-ratio: 1; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--surface-2);
  color: var(--line-2);
  box-shadow: inset 0 0 0 1px var(--line);
  /* el retardo sale de la POSICION del comercio: se encienden en orden y la
     grilla se ve LLENARSE. Sin escalonado son 53 iconos que cambian de color a
     la vez, y eso no cuenta nada. */
  transition: background-color .34s var(--ease) calc(var(--k) * 12ms),
              color .34s var(--ease) calc(var(--k) * 12ms),
              box-shadow .34s var(--ease) calc(var(--k) * 12ms);
}
.hall__ic { width: 56%; height: 56%; display: block; }

/* EL UMBRAL SE RESUELVE CON `:has()` Y `nth-child`, SIN UNA LINEA DE
   JAVASCRIPT. `selectores()` ya le pone `is-active` al boton elegido; la hoja
   mira cual esta activo y enciende los primeros 15, los ultimos 38 o los
   primeros 39. La alternativa era que el script recorriera 53 nodos en cada
   paso del ciclo para ponerles y sacarles una clase.
   Va detras de `.js` y de `.is-en` por la misma razon de siempre: sin script la
   grilla se ve completa y apagada, que es un estado valido; nunca en blanco. */
.js .esc.is-en .hall:has(#hl1.is-active) .hall__grid i:nth-child(-n+15),
.js .esc.is-en .hall:has(#hl3.is-active) .hall__grid i:nth-child(-n+39) {
  background: var(--teal-field);
  color: var(--on-teal);
  box-shadow: inset 0 0 0 1px var(--teal-field);
}
/* EL ESTADO QUE DUELE. Los 38 donde no habia nada se pintan en TINTA, no en
   rojo ni en naranja: el naranja ya significa "Znapper en zona" en el mapa de
   la escena anterior y reusarlo aca para "faltante" haria que el mismo color
   dijera dos cosas en el mismo acto. La tinta plena sobre papel es lo mas
   pesado que tiene esta hoja, y lo pesado es exactamente lo que hace falta:
   son 38 de 53, o sea que la pantalla se pone oscura. */
.js .esc.is-en .hall:has(#hl2.is-active) .hall__grid i:nth-child(n+16) {
  background: var(--ink);
  color: var(--on-ink);
  box-shadow: inset 0 0 0 1px var(--ink);
}

/* ==================== EL CONTADOR ====================
   TRES NUMEROS EN EL MARCADO Y LA HOJA MUESTRA EL QUE CORRESPONDE. Asi el dato
   existe aunque el script no corra, y no hay que sincronizar un texto desde
   JavaScript con una clase que pone otro modulo.

   EL NUMERO SE DESPLAZA, NO SALTA, Y HASTA LA v22 ESO ERA UN COMENTARIO QUE
   MENTIA: el marcado decia "no cambia de golpe: sube" y lo que habia era un
   fundido cruzado de dos `<b>` apilados, correcto de contenido y sin una sola
   señal de direccion. Un documento que se contradice con el codigo es peor que
   no tenerlo, y ese llevaba tres rondas.
   `overflow: clip` recorta el viaje; no crea contexto de scroll, asi que no
   interfiere con el encaje del documento. Los tres numeros son de dos digitos
   y sin descendentes, o sea que el recorte no come nada. */
.hall__k {
  display: flex; align-items: baseline; gap: 10px;
  margin-top: clamp(14px, 2vh, 20px);
}
.hall__kn {
  display: inline-grid; overflow: clip;
  font-size: clamp(30px, 3.4vw, 46px); font-weight: 700;
  line-height: 1; letter-spacing: var(--tr-h2); color: var(--ink);
}
.hall__kn b {
  grid-area: 1 / 1; font-weight: inherit;
  transition: opacity .28s var(--ease), transform .42s var(--ease-exp);
}
.hall__kn b:nth-child(2), .hall__kn b:nth-child(3) {
  opacity: 0; transform: translateY(calc(var(--mv) * .7em));
}
.hall:has(#hl2.is-active) .hall__kn b:nth-child(1),
.hall:has(#hl3.is-active) .hall__kn b:nth-child(1),
.hall:has(#hl3.is-active) .hall__kn b:nth-child(2) {
  opacity: 0; transform: translateY(calc(var(--mv) * -.7em));
}
.hall:has(#hl2.is-active) .hall__kn b:nth-child(2),
.hall:has(#hl3.is-active) .hall__kn b:nth-child(3) {
  opacity: 1; transform: none;
}
/* el rotulo del contador cambia con el estado del medio: los 38 no son
   comercios "de 53" en el mismo sentido, son los que faltaban */
.hall__kd {
  font-family: var(--mono); font-size: 10px; font-weight: 600;
  letter-spacing: .12em; text-transform: uppercase; color: var(--text-3);
}

/* ==========================================================================
   ESCENA 3 · LA LENTE — v25
   ==========================================================================
   QUE SE TIRO. El aparato entero: una foto a sangre, un circulo que la
   recorria con keyframes espejados, una reticula teal, lineas de barrido y
   cinco rectangulos translucidos encendidos todos a la vez. Con el se fueron
   `.lens__stage` como escenario absoluto, `.lens__eye`, `.lens__aug`,
   `.lens__grid`, `.lens__scan`, `.lens__ring`, `.lens__proc`, `.lens__hud`,
   los dos juegos de keyframes y un modulo de noventa lineas del script.

   POR QUE, EN UNA LINEA: mostraba el ACTO DE MIRAR y no el RESULTADO DE MIRAR.
   El detalle esta en `escenas_2.html`, con las cinco frases del cliente.

   LO QUE SE APRENDIO Y NO SE VE EN EL CODIGO: las tres rondas anteriores
   trabajaron sobre COMO se veia el aparato —la mascara congelaba el renderer,
   el interior salia lechoso, el anillo no se leia como aparato— y ninguna se
   pregunto si el aparato tenia que existir. Un problema de concepto absorbe
   cualquier cantidad de correcciones de ejecucion sin moverse.

   LO QUE SE QUEDA. La cifra de contraste que motivo oscurecer la foto sigue
   valiendo y ahora la usa el velo: el teal da 2,27:1 sobre claro y 8,7:1 sobre
   oscuro, asi que una medicion se lee sobre fondo oscuro. Es el mismo motivo
   por el que la escena entera es de noche.
   ========================================================================== */
/* `--surface-3` ES EL ESCALON MAS PROFUNDO DE LA ESCALA CLARA, y es el que le
   corresponde: esta escena es UNA LAMINA APOYADA EN UNA MESA. Un papel mas
   oscuro que sus dos vecinas hace de mesa sin salirse de la luz, y ademas le
   devuelve a la foto el marco que antes le daba el fondo de tinta. */
.esc--lente { background: var(--surface-3); }
.lens {
  width: 100%; max-width: var(--wrap); margin-inline: auto; padding-inline: var(--gut);
  /* LA LAMINA SE LLEVA LA COLUMNA GRANDE, Y ES JERARQUIA: el protagonista de
     esta escena es la gondola medida, no la lista de variables. Con .78 contra
     1 la foto salia de 480px de ancho —mas chica que el titular— y la escena
     se leia como un indice con una miniatura al lado. */
  display: grid; grid-template-columns: minmax(0, .62fr) minmax(0, 1fr);
  gap: clamp(28px, 3.6vw, 56px); align-items: center;
}
/* `text-wrap: pretty` Y NO `balance`, QUE ES LO QUE HEREDA. Mismo caso que en
   el hallazgo: `balance` reparte los renglones para equilibrarlos y aca eso
   partia "Esto no es una foto." en dos lineas que entraban en una, dejando el
   titular en CUATRO renglones y el campo de marca en dos bloques desparejos. */
.esc--lente .esc__h2 {
  color: var(--text);
  /* 36 Y NO 40. El campo de marca lleva relleno horizontal, asi que "Son cinco
     variables." necesita ~24px mas que su ancho de texto: a 40 se pasaba de los
     425 de la columna por quince pixeles y se partia en dos renglones. */
  font-size: clamp(26px, 2.45vw, 36px); text-wrap: pretty;
}

/* ==================== LAS CINCO VARIABLES ====================
   MISMA FORMA QUE LA CADENA Y QUE EL SELECTOR DE CANAL, girada. En una ruta de
   siete pantallas, dos mecanismos distintos para "elegir uno de N" se leen como
   dos productos.
   LA INACTIVA NO ES UN BOTON APAGADO: es una linea de indice. Cinco botones
   con borde apilados serian cinco objetos compitiendo, y la escena ya tiene un
   protagonista, que es la lamina. */
.lens__lista {
  margin-top: clamp(18px, 2.6vh, 28px);
  display: grid; gap: 1px;
  border-top: 1px solid var(--line-2); border-bottom: 1px solid var(--line-2);
}
.lens__v {
  width: 100%; display: flex; align-items: baseline; gap: 12px;
  padding: clamp(8px, 1.1vh, 11px) 4px;
  border: 0; background: none; font: inherit; text-align: left; cursor: pointer;
  color: var(--text-3);
  transition: color .24s var(--ease);
}
.lens__vn {
  font-family: var(--mono); font-size: 10px; font-weight: 700;
  letter-spacing: .1em; color: var(--text-4);
  transition: color .24s var(--ease);
}
.lens__vt {
  font-size: clamp(15px, 1.35vw, 19px); font-weight: 600;
  letter-spacing: var(--tr-sub);
}
@media (hover: hover) and (pointer: fine) {
  .lens__v:hover { color: var(--text); }
}
.lens__v.is-active { color: var(--text); }
/* `--teal-deep` y no `--teal`: el numeral es texto de 10px sobre papel, y el
   teal de acento da 2,27:1 ahi. Es el mismo motivo por el que existen los dos
   tokens desde la v13. */
.lens__v.is-active .lens__vn { color: var(--teal-deep); }

/* LA LECTURA DE LA VARIABLE ACTIVA. Alto reservado: las cinco frases no miden
   lo mismo y sin un piso fijo la lamina de al lado se corre en cada paso del
   ciclo. Una lamina que salta se lee como un error de carga. */
.lens__stage { margin-top: clamp(14px, 2vh, 20px); min-height: 4.4em; }
.lens__p[hidden] { display: none; }
.lens__p p {
  font-size: clamp(14px, 1.15vw, 16px); line-height: 1.5;
  color: var(--text-2); max-width: 42ch;
}

/* ==================== LA LAMINA ====================
   LA FOTO ES UNA LAMINA, NO EL FONDO. A sangre medía 1512x830 al lado de un
   titular: el cliente lo dijo como "la foto queda desproporcionadamente grande
   de gondola con el resto", y era literal — ninguna otra pieza de la ruta se
   acerca a ese tamaño. Contenida, queda del orden de la captura del tablero,
   que es la otra lamina del recorrido. */
.lens__foto {
  position: relative; margin: 0;
  border-radius: var(--r-media); overflow: hidden;
  /* SOBRE PAPEL LA SOMBRA SE ACHICA Y SE ACLARA. Una sombra de 70px al 70%
     sobre crema no separa: mancha. Lo que despega a la lamina de la mesa es el
     canto y una sombra corta, no una nube. */
  box-shadow: 0 22px 48px -20px rgba(52,49,44,.45), 0 2px 6px rgba(52,49,44,.10);
}
/* SE RECORTA A 4:3 Y NO SE MUESTRA EN SU 16:9 NATURAL. A lo ancho de la
   columna grande, el original daba una banda de 700x394 en una escena de 830 de
   alto: quedaba flotando en el medio con doscientos pixeles muertos arriba y
   abajo. El recorte centrado ademas acerca la gondola, que es lo que hay que
   mirar; los bordes del original son la punta del pasillo y una caja
   registradora. */
.lens__img { display: block; width: 100%; aspect-ratio: 4 / 3; object-fit: cover; }

/* EL VELO QUE DEJA VER UNA SOLA REGION.
   No es un `mask-image` ni un `clip-path`: los dos obligan a rasterizar la capa
   entera en cada cambio, y esta capa tiene adentro una foto de 1600px. Lo que
   hay es UNA sombra interior enorme sobre la caja activa, o sea que el
   oscurecimiento lo dibuja la misma caja que ya esta ahi. Cero capas nuevas.
   9999px de radio es "todo lo que haya afuera": no depende del tamaño de la
   lamina, asi que no hay que medirla ni actualizarla al cambiar de ventana. */
.lens__caja {
  position: absolute; z-index: 3;
  left: var(--x); top: var(--y); width: var(--w); height: var(--h);
  border-radius: var(--r-anot-2);
  opacity: 0;
  transition: opacity .42s var(--ease);
  pointer-events: none;
}
.lens__caja::before {
  content: ''; position: absolute; inset: 0;
  border-radius: inherit;
  box-shadow: 0 0 0 9999px rgba(10,14,13,.62);
}
/* LA REGION ES UN RECTANGULO CON ESQUINAS, NO CUATRO ESQUINAS SUELTAS.
   Las escuadras solas funcionan cuando el fondo es parejo; sobre una gondola
   —que es todo canto vertical— las cuatro piezas se leen como cuatro reflejos y
   el ojo no cierra el rectangulo. Un contorno de 1px al 45% lo cierra sin
   competir con las escuadras, que siguen siendo lo que dice "esto se esta
   midiendo".
   Y LAS ESCUADRAS BAJAN DE 2,5 A 2 px: a 2,5 sobre un recorte de 700px pesaban
   mas que el rotulo, que es la pieza que trae la informacion. */
.lens__caja::after {
  content: ''; position: absolute; inset: 0;
  border-radius: inherit;
  border: 1px solid rgba(19,193,172,.45);
}
.lens__caja .anot-box__c { width: 20px; height: 20px; border-width: 2px; border-color: var(--teal); }

/* EL ROTULO VA ADENTRO DE LA CAJA, y esa era la queja mas concreta de las
   cinco: "tampoco hay texto dentro". Una caja sin nombre sobre una foto no
   dice que se esta midiendo, dice que hay un rectangulo.
   VA ARRIBA A LA IZQUIERDA Y POR AFUERA DEL BORDE SUPERIOR cuando entra: es
   donde va la etiqueta de una anotacion en cualquier herramienta de
   deteccion, y adentro taparia justo la parte de la gondola que la caja
   señala. */
.lens__rot {
  position: absolute; left: -2px; bottom: calc(100% + 6px);
  padding: 4px 9px 4px 8px; border-radius: var(--r-anot-2);
  background: var(--teal-field); color: var(--on-teal);
  font-family: var(--mono); font-size: 10px; font-weight: 700;
  letter-spacing: .1em; text-transform: uppercase; white-space: nowrap;
  /* LA CHAPA SE APOYA EN LA FOTO Y NECESITA DESPEGARSE DE ELLA. Sin sombra, un
     rectangulo teal plano sobre una gondola oscura se lee como parte de la
     imagen; con una sombra corta se lee como una etiqueta puesta encima, que es
     lo que es. */
  box-shadow: 0 2px 6px rgba(10,14,13,.45);
}
/* LA CAJA PEGADA AL TECHO METE EL ROTULO PARA ADENTRO —afuera lo cortaria el
   marco— PERO ABAJO, NO ARRIBA. Arriba tapaba justo el cartel de categoria, que
   es lo unico que esa region vino a señalar: la etiqueta se comia a su propio
   sujeto. Abajo a la izquierda cae sobre botellas, que es fondo. */
.lens__caja:nth-child(5) .lens__rot { bottom: 6px; top: auto; left: 6px; }

/* SE ENCIENDE UNA SOLA, Y ES LO QUE HACE QUE MARCAR SIGNIFIQUE ALGO.
   Antes las cinco estaban puestas siempre, con un relleno teal al 22% que
   sobre una foto de gondola no se distingue de un reflejo. Con una sola, el
   resto de la lamina se apaga y la region marcada es lo unico que se ve.
   `+ 1` y no `nth-child(n)`: el primer hijo de la figura es la `<img>`. */
.lens:has(#ln1.is-active) .lens__caja:nth-of-type(1),
.lens:has(#ln2.is-active) .lens__caja:nth-of-type(2),
.lens:has(#ln3.is-active) .lens__caja:nth-of-type(3),
.lens:has(#ln4.is-active) .lens__caja:nth-of-type(4),
.lens:has(#ln5.is-active) .lens__caja:nth-of-type(5) { opacity: 1; }
/* sin script no hay `is-active` en ningun boton: la lamina se ve entera y sin
   marcar, que es un estado valido. Nunca en negro. */

.lens__nota {
  margin-top: 0; position: absolute; left: 0; right: 0; bottom: 0; z-index: 4;
  padding: 26px clamp(12px, 1.4vw, 18px) clamp(9px, 1.2vh, 13px);
  background: linear-gradient(rgba(10,14,13,0), rgba(10,14,13,.86));
  font-size: 11px; line-height: 1.4; color: rgba(247,244,239,.78);
}
.lens__nota b { color: var(--on-ink); font-weight: 700; }

@media (prefers-reduced-motion: reduce) {
  .lens__caja { transition: opacity .2s linear; }
}

/* ==========================================================================
   ESCENA 4 · LA CADENA
   ==========================================================================
   Siete nodos sobre un riel y un token que los recorre. La forma tiene que
   decir ENCADENADO de un vistazo: es la instruccion que dejo `ROADMAP.md`
   cuando el cliente mando borrar la version anterior por leerse como una lista.
   ========================================================================== */
/* LA CADENA, EN CLARO CON ACENTO. Era un campo teal pleno a pantalla
   completa, y con el resto del sitio en light mode ese bloque saturado en el
   medio del recorrido cortaba la pagina en dos. El pedido fue "que se sienta
   todo parte de lo mismo".
   El ritmo NO se pierde: esta escena usa `--surface-2`, un escalon mas oscuro
   que sus vecinas, y el color de marca queda donde estaba trabajando de
   verdad, que es en los nodos del riel. Se da variacion con TONO, no
   invirtiendo el tema. */
/* v22: LA ESCENA SE ARMA POR ALTO, PORQUE ANTES SOBRABAN 350px.
   `align-content: stretch` y una grilla de dos filas: el encabezado toma lo
   que necesita y la cadena se queda con TODO el resto. Sin esto la escena
   centraba un bloque de 250px en una pantalla de 900 y el aire de abajo se
   leia como que faltaba algo. */
/* v25: LA ESCENA VUELVE A CENTRAR SU BLOQUE, Y NO ES VOLVER ATRAS.
   La v22 la puso en `stretch` porque tenia 250px de contenido en una pantalla
   de 900 y el aire de abajo se leia como que faltaba algo. Con la lectura mudada
   al pie, el bloque mide ~630px y el problema se dio vuelta: estirando, los
   222px que sobraban se metian TODOS adentro —entre el titular y la barra, y
   entre la barra y la lectura— porque la fila elastica es la del medio. Se veian
   tres piezas sueltas con cien pixeles de nada entre cada dos.
   Centrado, ese aire se reparte arriba y abajo del bloque, que es donde el aire
   junta en vez de separar. */
.esc--cad {
  background: var(--surface-2); color: var(--text);
  align-content: center;
}
/* TRES PISOS EN UNA COLUMNA: TITULAR, CADENA, LECTURA.
   QUE SE TIRO: la lectura del eslabon vivia ARRIBA A LA DERECHA, al lado del
   titular, y el comentario que la puso ahi decia que competir con el titular
   "es exactamente lo que tiene que hacer". Estaba mal, y contra el criterio de
   protagonismo: la escena tiene UN protagonista —la barra de siete tramos
   llenandose— y ahi habia DOS bloques de tipografia grande arriba, uno en cada
   esquina, con la barra abajo de los dos. De lejos se leia como dos titulares.

   Ahora la lectura va DEBAJO DE LA BARRA, que es a lo que se refiere. Pegada a
   su tramo activo, el orden de lectura es el orden del argumento: que hacemos,
   cuales son los siete pasos, que pasa si falta este.

   SE PIERDE LA VARIACION DE MOLDE que este layout aportaba —seis de las siete
   escenas ponen el titular arriba a la izquierda— y se acepta: esa variacion
   estaba comprada con una pieza en el lugar equivocado. La variacion la dan
   ahora la escena de la lente (columna angosta + lamina) y la del hallazgo
   (texto y grilla en paralelo). */
.cad {
  width: 100%; max-width: var(--wrap); margin-inline: auto; padding-inline: var(--gut);
  display: grid;
  grid-template-areas: "cabe" "tira" "lee";
  row-gap: clamp(18px, 3vh, 34px);
  min-height: 0;
}
/* `align-self` PIEZA POR PIEZA Y NO `align-items` EN EL PADRE. Con
   `align-items: start` en la grilla la tira dejaba de estirarse a su fila y se
   quedaba en la altura de su contenido: 115px de cadena y 400px de aire abajo,
   o sea el mismo defecto que esta escena vino a arreglar. */
.cad__cabe  { grid-area: cabe; align-self: start; }
/* LA TIRA SE ESTIRA A SU FILA PERO CON TECHO, Y EL TECHO BAJA.
   Sin techo, en 900px de pantalla quedaban columnas de 550px con un numeral
   arriba y una palabra abajo: un campo teal enorme y vacio se lee como un error
   de maquetado, no como una cadena. Con 420 seguia habiendo 250px de nada entre
   el numeral y el nombre —era el pendiente 5 de la v24, "la cadena deja aire
   dentro de sus columnas"— y ademas la lectura no entraba abajo.
   A 320 el vacio interior baja a la mitad y la escena gana el piso que
   necesitaba la lectura. */
.cad__tira  { grid-area: tira; height: clamp(210px, 32vh, 310px); }
.esc--cad .esc__ojo { color: var(--text-2); }
/* el titular de esta escena baja un escalon de la escala comun: comparte fila
   con la lectura, asi que tiene la mitad del ancho que tiene en las otras seis
   y a 52px "Juntar gente es fácil." se partia en tres renglones */
/* VUELVE A LA ESCALA COMUN. Bajaba un escalon porque compartia fila con la
   lectura y tenia la mitad del ancho; desde que la lectura se mudo abajo, tiene
   el ancho entero y no hay motivo para que sea distinto de las otras seis. */
.esc--cad .esc__h2 { color: var(--text); }

/* ==========================================================================
   LA TIRA: SIETE COLUMNAS QUE SON UNA SOLA BARRA
   ==========================================================================
   TRES DECISIONES, Y NINGUNA ES DECORATIVA.

   1. NO HAY SEPARACION ENTRE COLUMNAS. Siete cajas con aire entre medio se
      leen como siete opciones; siete tramos pegados y separados por una linea
      fina se leen como UNA cosa dividida, que es lo que una cadena es. Esta
      escena existe para decir "el diferencial no es tener una red, es tenerla
      ENTERA", y la forma tiene que decirlo antes que el texto.

   2. LA ACTIVA SE ENSANCHA en vez de solo pintarse. Es lo que le da lugar al
      nombre largo y lo que hace que el ciclo se SIENTA avanzar: algo que
      cambia de tamaño se mira, algo que cambia de color hay que buscarlo.
      Se anima `grid-template-columns` con `fr`, que Chrome interpola sin
      recalcular el contenido de cada columna.

   3. LAS RECORRIDAS QUEDAN TEÑIDAS. Es el mismo criterio del espinazo y del
      riel viejo: un recorrido que no muestra lo andado no se lee como
      recorrido. Con eso la barra se LLENA de izquierda a derecha sola, y ya no
      hace falta el `.cad__linea` con sus siete reglas de `scaleX`, que era la
      forma anterior de contar exactamente esto. */
.cad__tira {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  min-height: 0;
  border: 1px solid var(--line-2);
  border-radius: var(--r-surface);
  overflow: hidden;
  background: var(--paper);
  transition: grid-template-columns .46s var(--ease-io);
}
/* la activa se lleva el triple de ancho. Sale de `:has()` y no de una clase en
   el padre: el estado ya vive en el boton y duplicarlo arriba es la forma de
   que un dia digan cosas distintas. */
.cad__tira:has(#cd1.is-active) { grid-template-columns: 3.1fr 1fr 1fr 1fr 1fr 1fr 1fr; }
.cad__tira:has(#cd2.is-active) { grid-template-columns: 1fr 3.1fr 1fr 1fr 1fr 1fr 1fr; }
.cad__tira:has(#cd3.is-active) { grid-template-columns: 1fr 1fr 3.1fr 1fr 1fr 1fr 1fr; }
.cad__tira:has(#cd4.is-active) { grid-template-columns: 1fr 1fr 1fr 3.1fr 1fr 1fr 1fr; }
.cad__tira:has(#cd5.is-active) { grid-template-columns: 1fr 1fr 1fr 1fr 3.1fr 1fr 1fr; }
.cad__tira:has(#cd6.is-active) { grid-template-columns: 1fr 1fr 1fr 1fr 1fr 3.1fr 1fr; }
.cad__tira:has(#cd7.is-active) { grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 3.1fr; }
@media (prefers-reduced-motion: reduce) {
  .cad__tira { transition: none; }
}

.cad__col {
  position: relative; min-width: 0;
  display: grid; align-content: space-between; justify-items: start;
  gap: 12px;
  padding: clamp(14px, 2.2vh, 24px) clamp(10px, 1.1vw, 18px);
  border: 0; border-left: 1px solid var(--line);
  background: none; font: inherit; text-align: left; cursor: pointer;
  transition: background-color .38s var(--ease), color .38s var(--ease);
}
.cad__col:first-child { border-left: 0; }

/* EL NUMERAL ES LA PIEZA GRAFICA DE LA ESCENA. Grande, en mono y arriba de
   todo: siete numerales de 34px en fila ya cuentan "son siete y van en orden"
   sin leer una palabra. */
.cad__num {
  font-family: var(--mono); font-size: clamp(20px, 2.4vw, 34px);
  font-weight: 700; line-height: 1; letter-spacing: -.02em;
  color: var(--line-2);
  transition: color .38s var(--ease);
}
.cad__rot {
  font-size: clamp(11px, 1.05vw, 14.5px); font-weight: 600; line-height: 1.22;
  letter-spacing: var(--tr-sub); color: var(--text-2);
  transition: color .38s var(--ease);
}
/* EL NOMBRE LARGO SOLO EN LA ACTIVA, que es la unica con ancho para el. En una
   columna de 92px "Diseño de campaña" se parte en cuatro renglones; el rotulo
   corto ya venia en los datos y hasta ahora solo se usaba abajo de 640px. */
.cad__largo { display: none; }
.cad__col.is-active .cad__largo { display: inline; }
.cad__col.is-active .cad__corto { display: none; }

/* LAS RECORRIDAS. Tenidas, no llenas: dicen "por aca ya paso" sin competir con
   la activa, que es la que hay que mirar. */
.cad__col.is-done { background: var(--teal-tint); }
.cad__col.is-done .cad__num { color: var(--teal); }
.cad__col.is-done .cad__rot { color: var(--text); }

.cad__col.is-active { background: var(--teal); }
/* EL NUMERAL DE LA ACTIVA CRECE AL DOBLE. Es lo que llena la columna ancha
   sin agregar una palabra —que es justo lo que esta ronda vino a no hacer— y
   es ademas la señal mas rapida de en que eslabon esta parado el ciclo. */
.cad__col.is-active .cad__num {
  color: var(--on-teal); opacity: .8;
  font-size: clamp(30px, 4.4vw, 66px);
}
.cad__col.is-active .cad__rot {
  color: var(--on-teal);
  font-size: clamp(14px, 1.5vw, 21px); font-weight: 700; line-height: 1.14;
  letter-spacing: var(--tr-h3);
}
@media (hover: hover) and (pointer: fine) {
  .cad__col:not(.is-active):hover { background: var(--surface); }
  .cad__col:not(.is-active):hover .cad__num { color: var(--text-2); }
  .cad__col:not(.is-active):hover .cad__rot { color: var(--text); }
}

/* ==================== LA LECTURA DEL ESLABON ACTIVO ====================
   VA DEBAJO DE LA BARRA Y EN DOS COLUMNAS, no al lado del titular y apilada.
   El motivo esta arriba, en el comentario de `.cad`: eran dos bloques de
   tipografia grande peleandose en las dos esquinas de arriba.
   DOS COLUMNAS Y NO UNA PILA porque la pieza que tiene debajo es horizontal:
   una lectura apilada al pie de una barra de 1.200px de ancho deja el 60% del
   renglon vacio. El alto reservado evita que la barra suba y baje en cada paso
   del ciclo — siete frases de largo distinto mueven la escena entera. */
.cad__stage { grid-area: lee; min-height: clamp(74px, 11vh, 104px); }
.cad__p {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.12fr);
  gap: clamp(20px, 3vw, 52px); align-items: start;
}
/* UN `display` EXPLICITO LE GANA AL ATRIBUTO `hidden`, y en este proyecto ya
   paso cuatro veces. `selectores()` apaga los paneles poniendoles `hidden`,
   pero la regla de arriba les pone `display: grid` con mas especificidad que
   la regla de agente de usuario: sin esta linea los SIETE eslabones se ven a
   la vez. Todo lo que el script apague con `hidden` necesita su propia regla. */
.cad__p[hidden] { display: none; }
.cad__d {
  font-size: clamp(14px, 1.15vw, 16.5px); line-height: 1.5; color: var(--text-2);
  padding-top: clamp(10px, 1.4vh, 16px); border-top: 1px solid var(--line-2);
}
/* LA CONSECUENCIA BAJA DE 32 A 22. Era la segunda tipografia mas grande de la
   pagina despues de los titulares, y estaba diciendo el pie de un eslabon.
   Sigue siendo lo mas pesado de su fila —va en negrita y en tinta plena contra
   una descripcion en `--text-2`— que es toda la jerarquia que necesita. */
.cad__x {
  font-size: clamp(16px, 1.5vw, 22px); font-weight: 700; line-height: 1.25;
  letter-spacing: var(--tr-sub); color: var(--text);
  padding-top: clamp(10px, 1.4vh, 16px); border-top: 1px solid var(--teal);
}
.cad__xk {
  display: block; margin-bottom: 6px;
  font-family: var(--mono); font-size: 10px; font-weight: 600;
  letter-spacing: .14em; text-transform: uppercase; color: var(--teal-deep);
}

/* ==========================================================================
   ESCENA 5 · EL TABLERO
   ==========================================================================
   Cuatro puntos sobre una captura del producto. Corre con `selectores()`.
   Va sobre campo de tinta y no sobre papel: la captura del dashboard es casi
   blanca, y sobre papel el borde de la imagen desaparecia y la escena se leia
   como un hueco. Sobre tinta la captura se recorta sola.
   ========================================================================== */
/* EL TABLERO EN CLARO. Iba sobre campo de tinta con un argumento que era
   cierto en su momento: la captura del dashboard es casi blanca y sobre papel
   su borde desaparecia. Con el sitio en light mode eso se resuelve con un
   BORDE y una sombra, no invirtiendo la escena entera: una pagina clara con una
   pantalla negra en el medio es justo el corte que hace que no se sienta todo
   parte de lo mismo. */
/* v22: LA CAPTURA VA EN UN MARCO OSCURO, Y ESO CIERRA UNA DISCUSION DE TRES
   RONDAS. La escena empezo sobre campo de tinta (correcto: la captura del
   dashboard es casi blanca y sobre papel su borde desaparece), despues paso a
   papel (tambien correcto: una pantalla negra en el medio de una pagina clara
   la parte en dos), y quedaba el problema original sin resolver — blanco sobre
   blanco separado por una linea de 1px que a tamaño real no se ve.
   Las dos cosas se pueden tener a la vez: el contraste que la imagen necesita
   NO tiene que medir una pantalla, tiene que medir 12px. Un marco de tinta
   alrededor de la captura y la escena se queda clara.
   Y ademas dice algo cierto: eso es una PANTALLA, no una ilustracion. */
.esc--tab { background: var(--paper); color: var(--text); }
.tab { width: 100%; max-width: var(--wrap); margin-inline: auto; padding-inline: var(--gut); }
/* v20: LA CAPTURA PASA A OCUPAR EL ANCHO ENTERO, Y LA ANOTACION VA ABAJO.
   Esta escena existe para probar que el producto existe, y con dos columnas la
   prueba medía 600px de ancho para una imagen de 1678: el tablero se leia como
   una miniatura, que es lo contrario de lo que tiene que decir. Mientras tanto
   sobraban 150px de pantalla vacia abajo, o sea que el alto estaba de sobra y
   el problema era solo el reparto.
   Apilada, la captura mide los 896px del contenedor y crece un 50%. `--wrap` no
   se sobrepasa: es maximo duro por decision del cliente, asi que la unica forma
   de agrandar la imagen era dejar de compartirle el ancho al panel. */
.tab__ui {
  margin-top: clamp(14px, 2.2vh, 24px);
  display: grid; grid-template-columns: minmax(0, 1fr);
  gap: clamp(12px, 1.6vh, 20px); align-items: start;
}
/* LA CAPTURA SE MIDE POR EL ALTO QUE SOBRA, NO POR EL ANCHO QUE HAY.
   Al pasar a una sola columna la imagen se fue a 894px y la escena empezo a
   medir 919 en una ventana de 900: entraba de ancho y se pasaba de alto.
   Esta es la unica escena de la ruta con una pieza de relacion fija, asi que
   es la unica donde se puede resolver bien: se calcula el ancho A PARTIR del
   alto libre, multiplicando por la relacion de la imagen (1678/920 = 1,824).
   `min()` con el 100% la deja crecer hasta el contenedor y ni un pixel mas.
   `--tab-cromo` es lo que ocupa todo lo que no es la imagen: relleno de la
   escena, titular, banda de anotacion y pie. Medido, no estimado. */
.tab__lienzo {
  /* `--tab-cromo` sube 22px: son los 11px de marco arriba y abajo. Es la misma
     cuenta de siempre —todo lo que no es la imagen— y si no se actualiza la
     escena se pasa de alto exactamente esos 22px. */
  --tab-cromo: 468px;
  width: min(100%, calc((100svh - var(--tab-cromo)) * 1.824));
  margin-inline: auto;
  padding: 11px;
  border-radius: var(--pan-r); border: 0;
  background: var(--ink-2);
  box-shadow: 0 26px 60px -20px rgba(38,36,31,.42), 0 3px 10px rgba(38,36,31,.16);
}
/* la imagen lleva su propio radio, un escalon menor que el del marco: dos
   radios iguales con relleno entre medio dejan la esquina interior cuadrada */
.tab__vista { position: relative; border-radius: calc(var(--pan-r) - 6px); overflow: hidden; }
.tab__img { display: block; width: 100%; height: auto; }

/* LOS CUATRO PUNTOS. El area clicable mide 44px aunque el circulo mida 30: es
   el minimo tactil, y un punto sobre una imagen es justo el caso donde apuntar
   cuesta. */
.tab__pt {
  position: absolute; left: var(--x); top: var(--y);
  width: 44px; height: 44px; margin: -22px 0 0 -22px;
  display: grid; place-items: center;
  padding: 0; border: 0; background: none; font: inherit;
  cursor: pointer; z-index: 2;
}
.tab__ptn {
  display: grid; place-items: center;
  width: 30px; height: 30px; border-radius: 50%;
  /* el punto en reposo va en tinta con el numeral teal: sobre tinta el teal da
     8.7:1. Al activarse se da vuelta y queda teal con el numeral en tinta,
     7.80:1. Los dos estados se leen; ninguno depende del contorno. */
  background: var(--ink); color: var(--teal);
  border: 2px solid #fff;
  font-family: var(--mono); font-size: 11px; font-weight: 700;
  box-shadow: 0 2px 10px rgba(26,24,21,.3);
  transition: transform .2s var(--ease), background-color .2s var(--ease), color .2s var(--ease);
}
@media (hover: hover) and (pointer: fine) { .tab__pt:hover .tab__ptn { transform: scale(1.12); } }
.tab__pt:active .tab__ptn { transform: scale(.97); }
.tab__pt.is-active .tab__ptn {
  transform: scale(1.12);
  background: var(--teal); color: var(--ink); border-color: #fff;
  box-shadow: 0 0 0 7px rgba(19,193,172,.26), 0 2px 12px rgba(26,24,21,.28);
}
@media (prefers-reduced-motion: no-preference) {
  .sel-auto:not(.sel-tomado) .tab__pt.is-active .tab__ptn {
    animation: tabPulso var(--sel-auto, 4600ms) linear infinite;
  }
}
@keyframes tabPulso {
  0%   { box-shadow: 0 0 0 7px  rgba(19,193,172,.26), 0 2px 12px rgba(26,24,21,.28); }
  100% { box-shadow: 0 0 0 18px rgba(19,193,172,0),   0 2px 12px rgba(26,24,21,.28); }
}
/* EL ROTULO NO PUEDE IR EN TEAL SOBRE PAPEL: da 2.27:1. Va en el secundario
   (7.16:1) y el teal se reserva para el punto activo del tablero, que es un
   elemento grafico y no texto. */
/* LA ANOTACION, AHORA EN BANDA. Con la captura a ancho completo el panel dejo
   de ser una columna y paso a ser una linea debajo: el rotulo a la izquierda y
   la frase a la derecha, que es como se lee un pie de figura. Se le fija un
   alto minimo porque los cuatro textos no miden lo mismo, y sin eso la captura
   de arriba SALTA cada vez que el selector cambia de punto. Un dispositivo que
   mueve la pagina sola mientras alguien la mira es peor que uno que no anima. */
.tab__stage { min-height: 84px; display: grid; align-items: center; }
.tab__p { display: grid; grid-template-columns: minmax(0, 148px) minmax(0, 1fr); gap: 4px 22px; align-items: baseline; }
.tab__p[hidden] { display: none; }
.tab__k {
  font-family: var(--mono); font-size: 10px; font-weight: 700;
  letter-spacing: .1em; text-transform: uppercase; color: var(--text-2);
}
.tab__d { font-size: clamp(13px, 1.1vw, 14.5px); line-height: 1.5; color: var(--text); }
@media (max-width: 760px) {
  .tab__p { grid-template-columns: minmax(0, 1fr); }
}
.tab__nota { margin-top: clamp(10px, 1.6vh, 16px); font-size: 11px; color: var(--text-3); }

/* ==========================================================================
   ESCENA 6 · LA COBERTURA
   ==========================================================================
   El selector de canal es de la version anterior y se conserva entero: el copy
   y las fotos estan verificados uno por uno (ver el comentario del HTML). Lo
   unico que cambia es que ahora tiene que ENTRAR EN UNA PANTALLA.

   Medido: la escena daba 1018px contra un viewport de 863, o sea que asomaba
   155px de la siguiente y el encaje peleaba con el scroll. Casi todo era la
   foto, que a 440px de alto se llevaba la mitad de la pantalla. Se ata al alto
   del viewport en vez de a un valor fijo, que es lo unico que funciona cuando
   la escena TIENE que medir lo que mide la pantalla.
   ========================================================================== */
/* `padding-block: 0` NO ES COSMETICA: son 178px que nadie estaba contando.
   `.cov` nacio como seccion de una pagina de scroll largo y conserva
   `padding-top: var(--sec-pt)` (hasta 112px) y `padding-bottom` (hasta 72px).
   Esta regla la re-encuadra para la escena pero solo tocaba `padding-inline`,
   asi que el relleno vertical de la maqueta vieja seguia vivo DENTRO de una
   caja que promete medir una pantalla. Y es relleno atado a `vw`: en un
   monitor ancho y bajo valia el maximo justo cuando menos alto habia.
   Es la unica razon por la que esta escena medía 888px en una ventana de
   833px. */
/* v22: LA ESCENA SE ARMA POR ALTO Y LA FOTO SE QUEDA CON LO QUE SOBRA.
   Antes la foto tenia un techo de 340px y la escena dejaba 400px muertos
   abajo, o sea que la unica pieza visual de la pantalla estaba achicada para
   dejar aire vacio. Ahora la pila es titular / canales / panel, el panel se
   estira a la fila que le toca, y la foto mide lo que mide la escena.
   Es el mismo defecto que tenia la cadena, y se arregla igual. */
.esc--cov { align-content: stretch; grid-template-rows: 1fr; }
.esc--cov .cov {
  width: 100%; max-width: var(--wrap); margin-inline: auto;
  padding-inline: var(--gut); padding-block: 0;
  /* LAS TRES FILAS SON cabe / ui / foot, QUE SON LOS TRES HIJOS DE `.cov`.
     La primera version escribio `auto auto 1fr` pensando en titular, canales y
     panel, pero los canales y el panel viven los dos adentro de `.cov__ui`:
     la fila elastica le tocaba al pie, `.cov__ui` se quedaba en `auto` y la
     foto no crecia ni un pixel. Se veia igual que antes y el CSS estaba
     puesto, que es la peor forma de equivocarse. */
  display: grid; grid-template-rows: auto minmax(0, 1fr) auto; min-height: 0;
}
.esc--cov .cov__ui {
  margin-top: clamp(16px, 2.4vh, 28px); min-height: 0;
  display: grid; grid-template-rows: auto minmax(0, 1fr);
}
.esc--cov .cov__stage { margin-top: clamp(14px, 2.2vh, 26px); min-height: 0; }
.esc--cov .cov__p { align-items: stretch; }
/* la altura fija se reemplaza por "toda la fila". El motivo original de que
   fuera fija sigue vivo y se respeta: las seis fotos vienen en tres relaciones
   distintas, asi que el alto NO puede salir de la imagen o el panel salta al
   cambiar de canal. Sale de la escena, que es constante. */
.esc--cov .cov__ph { height: auto; min-height: clamp(180px, 30vh, 520px); }
.esc--cov .cov__say { align-self: center; }
.esc--cov .cov__hd { font-size: clamp(18px, 1.9vw, 27px); }
.esc--cov .cov__vars li { padding-block: 9px; }
.esc--cov .cov__foot { margin-top: clamp(12px, 2vh, 20px); font-size: 13px; }

/* ==========================================================================
   ESCENA 7 · EMPEZAR: LA MATRIZ DE DECISION
   ==========================================================================
   Las lineas son solo VERTICALES entre columnas y muy tenues entre filas: una
   tabla con borde en las cuatro caras de cada celda se lee como un formulario.
   ========================================================================== */
/* LA COBERTURA BAJA UN ESCALON, PARA QUE NO SEA IGUAL QUE LA QUE SIGUE.
   Las escenas 6 y 7 compartian `--surface` y se veian como una sola pantalla
   larga cortada al medio. Con esto el dia entero es una escala que sube y
   vuelve a bajar: cadena F2EEE8 -> tablero FFFFFF -> cobertura FAF8F5 ->
   empezar F2EEE8. El ritmo se da con TONO, que es la regla de esta hoja. */
.esc--cov { background: var(--surface); }
/* ==========================================================================
   v22: LA ULTIMA ESCENA ES DE NOCHE, Y ES LA DECISION QUE MAS SE DEFIENDE SOLA
   ==========================================================================
   Es la pantalla donde alguien elige por cual de las tres puertas entra, o sea
   la unica de las siete que tiene que CONVERTIR, y era la mas plana del
   recorrido: una tabla de texto gris sobre crema, con 130px de aire arriba y
   130 abajo.

   Y hay un argumento estructural, no solo de gusto: LO QUE SIGUE YA ERA
   OSCURO. `.finale` es una foto de gondola a sangre con una tarjeta de tinta
   encima. Con la matriz en crema, el recorrido terminaba con un salto de papel
   a negro justo en el unico punto donde no puede haber una costura. Ahora el
   cierre es un solo bloque: decision, y despues el pedido.

   Con eso la ruta queda con una forma que se siente al scrollear: la calle de
   noche (1, 2, 3), el dato a la luz (4, 5, 6) y la decision otra vez en
   oscuro (7 y el pie). No son siete pantallas claras con una foto en el medio.

   Y SE ARMA POR ALTO como la cadena y la cobertura: la matriz se estira a lo
   que sobra en vez de flotar centrada. */
/* v25: LA ULTIMA ESCENA VUELVE A LA LUZ, y con ella se cierra el arco tonal.
   El arco de la v22 existia por un motivo que ya no esta: el mapa era
   `dark_all` y esa escena tenia que ser oscura, asi que las dos siguientes y la
   ultima se pintaron de noche para que el recorrido tuviera forma. Con el mapa
   en luz, el arco quedo sosteniendose solo a si mismo.
   QUEDA UN SALTO Y NO SE OCULTA: el pie es una foto de gondola a sangre, o sea
   oscuro, y la matriz en crema entra a el de golpe. La v22 lo llamo "una
   costura en el unico punto donde no puede haber una". Sigue habiendo salto; lo
   que cambio es que ahora es EL UNICO de la pagina en vez de uno de cuatro, y
   un salto solo, al final, contra una foto, se lee como cierre.

   Y SE DEJA DE ESTIRAR. `align-content: stretch` repartia el alto sobrante
   ADENTRO de la tabla: en 919px la fila "Que compras" media 160px para dos
   renglones de texto, y las cuatro filas juntas se leian como celdas vacias con
   una linea de texto arriba. Centrada, el aire queda afuera del bloque. */
.esc--pta { background: var(--surface-2); color: var(--text); align-content: center; }
.esc--pta .esc__ojo { color: var(--text-2); }
.esc--pta .esc__h2  { color: var(--text); }
.pta {
  width: 100%; max-width: var(--wrap); margin-inline: auto; padding-inline: var(--gut);
}
.pta__caja { margin-top: clamp(20px, 3.4vh, 38px); }

/* LA MATRIZ SE ARMA FILA POR FILA, y las tres puertas entran ultimas.
   Era la unica escena de las siete que aparecia entera de golpe: una tabla de
   cuatro filas y tres columnas puesta de una vez se lee como un bloque de
   texto, y es la pantalla donde hay que COMPARAR, o sea la que mas necesita que
   el ojo entre por algun lado. En cascada, cada fila se lee como una pregunta
   nueva sobre las mismas tres opciones.

   LAS FILAS SOLO CAMBIAN DE OPACIDAD. `transform` sobre un `<tr>` lo convierte
   en contenedor de bloque y hay navegadores donde eso corre los bordes
   colapsados de la tabla medio pixel. Las celdas del pie son `div` y ahi si se
   puede desplazar.

   El escalonado es de 70 ms y no de 55: aca cada fila es una unidad de lectura
   completa, no una pieza de una misma cosa. */
.js .esc--pta .pta__t tbody tr { opacity: 0; }
.js .esc--pta.is-en .pta__t tbody tr {
  opacity: 1;
  transition: opacity .44s var(--ease) var(--f, 0ms);
}
.js .esc--pta .pta__t tbody tr:nth-child(1) { --f: 200ms; }
.js .esc--pta .pta__t tbody tr:nth-child(2) { --f: 270ms; }
.js .esc--pta .pta__t tbody tr:nth-child(3) { --f: 340ms; }
.js .esc--pta .pta__t tbody tr:nth-child(4) { --f: 410ms; }
.js .esc--pta .pta__cel { opacity: 0; transform: translateY(calc(var(--mv) * 10px)); }
.js .esc--pta.is-en .pta__cel {
  opacity: 1; transform: none;
  transition: opacity .44s var(--ease) 500ms, transform .44s var(--ease) 500ms;
}
@media (prefers-reduced-motion: reduce) {
  .js .esc--pta .pta__cel { transform: none; }
}

.pta__t { width: 100%; border-collapse: collapse; text-align: left; }
.pta__t thead th { vertical-align: bottom; padding: 0 16px 14px; border-bottom: 1px solid var(--line-2); }
.pta__k {
  display: block;
  font-family: var(--mono); font-size: 10px; font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase; color: var(--text-3);
}
.pta__n {
  display: block; margin-top: 7px;
  font-size: clamp(15px, 1.35vw, 19px); font-weight: 700;
  line-height: 1.18; letter-spacing: var(--tr-sub); color: var(--text); text-wrap: balance;
}
.pta__t tbody th {
  width: 15%; padding: clamp(12px, 1.7vh, 18px) 16px clamp(12px, 1.7vh, 18px) 0;
  font-family: var(--mono); font-size: 10px; font-weight: 500;
  letter-spacing: .1em; text-transform: uppercase; color: var(--text-3);
  vertical-align: top; white-space: nowrap;
  border-bottom: 1px solid var(--line);
}
.pta__t tbody td {
  width: 28.33%; padding: clamp(12px, 1.7vh, 18px) 16px;
  font-size: clamp(13px, 1.15vw, 15.5px); line-height: 1.45; color: var(--text-2);
  vertical-align: top;
  border-bottom: 1px solid var(--line); border-left: 1px solid var(--line);
}
/* la columna del medio es la recomendada y se marca con un CAMPO, no con un
   borde teal alrededor: el borde competia con las lineas de la tabla y se leia
   como "esta celda esta seleccionada" */
/* la columna recomendada se marca con un CAMPO, no con un borde teal
   alrededor: el borde competia con las lineas de la tabla y se leia como
   "esta celda esta seleccionada". Sobre la noche ese campo es luz apenas
   levantada, y el texto de esa columna sube a blanco pleno. */
.pta__mid { background: var(--paper); }
.pta__t tbody td.pta__mid { color: var(--text); }
.pta__t thead .pta__mid { border-bottom-color: var(--teal); }
.pta__pie { display: grid; grid-template-columns: 15% 28.33% 28.33% 28.33%; }
.pta__cel { padding: clamp(14px, 2vh, 22px) 16px 0; border-left: 1px solid var(--line); }
.pta__cel:first-child { grid-column: 2; }
/* el boton ocupa la celda entera: tres puertas comparadas se eligen mirando la
   fila, y tres anchos distintos meten una jerarquia que la tabla no dice */
.pta__b { width: 100%; justify-content: center; }

/* --- COBERTURA: EL SELECTOR DE CANAL ---
   Reemplaza la grilla de seis items con una linea cada uno. La grilla decia lo
   mismo, pero habia que leerla entera y no se podia tocar: era el sintoma de
   fondo de todas las rutas, que no invitaban a interactuar.

   El comportamiento vive en selectores() del script y es generico. Aca solo
   esta la forma. */
/* `position: relative` porque `.stitch` se ancla al borde de abajo de su
   seccion. Cobertura y las tres puertas son las dos unicas secciones de papel
   SEGUIDAS de la pagina: sin el hilo quedaban pegadas y ese era el unico punto
   donde el scroll volvia a leerse como bloques sueltos. */
.cov { position: relative; padding-top: var(--sec-pt); padding-bottom: clamp(40px, 4.6vw, 72px); }
.cov .lead { margin-bottom: 0; }

.cov__ui { margin-top: clamp(30px, 3.4vw, 46px); }

/* LA LISTA. Es una fila de pestanas, no pildoras sueltas: comparten una linea
   de base continua, asi que se leen como un mismo objeto con una parte activa.
   El cliente rechazo dos veces las pildoras estandar, con razon. */
.cov__list {
  position: relative;
  display: flex; flex-wrap: wrap; gap: 0 clamp(6px, .9vw, 14px);
  border-bottom: 1px solid var(--line);
}
.cov__opt {
  position: relative;
  padding: 0 clamp(10px, 1.1vw, 16px) 14px;
  background: none; border: 0; cursor: pointer;
  font: inherit; text-align: left;
  /* --text-3 mide 3.71:1 sobre blanco y estas son PESTANAS, no texto de apoyo:
     una que no se lee no se puede elegir. --text-2 da 7.16:1 y la jerarquia
     entre activa e inactiva la sigue marcando el peso y el subrayado. */
  color: var(--text-2);
  transition: color 160ms var(--ease-out);
}
/* EL SUBRAYADO ES TAMBIEN EL RELOJ.
   Marca cual es la opcion elegida y, mientras el selector avanza solo, cuanto
   falta para el proximo salto: crece de 0 a 1 en `--sel-auto`.
   Antes el progreso era una barra suelta cruzando toda la fila, a la misma
   altura y del mismo color. En pantalla se leian DOS lineas teal a la vez y no
   habia manera de saber cual era la seleccion. Un solo objeto no se confunde
   con si mismo. */
.cov__opt::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: -1px; height: 2px;
  background: var(--teal);
  transform: scaleX(0); transform-origin: 0 50%;
  transition: transform 220ms var(--ease-out);
}
.cov__opt:hover  { color: var(--text); }
.cov__opt.is-active { color: var(--text); }
.cov__opt.is-active::after { transform: scaleX(1); }

/* mientras nadie toco nada, el subrayado de la activa cuenta el tiempo.
   `.sel-tomado` lo pone el script al primer gesto y a partir de ahi el
   subrayado vuelve a ser solo una marca de seleccion. */
.sel-auto:not(.sel-tomado) .cov__opt.is-active::after {
  animation: selFill var(--sel-auto, 4200ms) linear;
}
@keyframes selFill { from { transform: scaleX(0); } to { transform: scaleX(1); } }
/* con movimiento reducido no hay reloj: el subrayado aparece entero y listo */
@media (prefers-reduced-motion: reduce) {
  .sel-auto:not(.sel-tomado) .cov__opt.is-active::after { animation: none; }
}

.cov__on { font-size: clamp(15px, 1.35vw, 18px); font-weight: 700; letter-spacing: var(--tr-sub); }

/* EL PANEL. Dos columnas: lo que se dice y la captura. La altura la fija el
   contenido mas alto de los seis via grid apilado, asi que al cambiar de canal
   la pagina NO salta. Un selector que empuja el pie cada 4 s es peor que la
   grilla que reemplaza. */
.cov__stage { display: grid; margin-top: clamp(24px, 2.8vw, 38px); }
.cov__p {
  grid-area: 1 / 1;
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(24px, 3vw, 52px); align-items: start;
}
.cov__p[hidden] { display: none; }
/* entra desde abajo y con desenfoque: el desenfoque tapa el cruce entre dos
   fotos distintas, que sin el se lee como dos objetos superpuestos */
.cov__p:not([hidden]) { animation: covIn 320ms var(--ease-out) both; }
@keyframes covIn {
  from { opacity: 0; transform: translateY(10px); filter: blur(6px); }
  to   { opacity: 1; transform: none;             filter: blur(0); }
}
@media (prefers-reduced-motion: reduce) { .cov__p:not([hidden]) { animation: none; } }

/* a 12px en versalitas y con interletrado abierto, --text-3 (3.71:1) es
   ilegible de verdad, no "sutil". Va --text-2, que da 7.16:1. */
.cov__fmt {
  font-family: var(--mono); font-size: 12px; font-weight: 500;
  letter-spacing: .1em; text-transform: uppercase; color: var(--text-2);
}
.cov__hd {
  margin-top: 12px; max-width: 22ch;
  font-size: clamp(21px, 2.2vw, 30px); font-weight: 700;
  line-height: 1.16; letter-spacing: var(--tr-h3); color: var(--text);
}
/* las variables no son pildoras: son una lista con la escuadra de anotacion,
   que es el hilo visual del sitio entero */
.cov__vars { margin-top: clamp(20px, 2.2vw, 30px); display: grid; gap: 2px; }
.cov__vars li {
  position: relative; padding: 11px 0 11px 22px;
  border-top: 1px solid var(--line);
  font-size: 15px; font-weight: 600; letter-spacing: var(--tr-sub); color: var(--text-2);
}
/* la escuadra, chica y fina. A 9px con borde de 2px se leia como una L
   mayuscula, no como la marca de anotacion del sitio: a este tamano el trazo
   pesaba mas que la forma. */
.cov__vars li::before {
  content: ''; position: absolute; left: 2px; top: 50%; width: 6px; height: 6px;
  margin-top: -3px;
  border-left: 1.5px solid var(--teal); border-bottom: 1.5px solid var(--teal);
}
/* ALTURA FIJA, no aspect-ratio.
   Las seis fotos vienen en tres relaciones distintas (16/9, 9/16 y 3/4). Con
   una relacion unica el panel media 720px de alto contra 330px de texto, y
   ademas el recorte vertical se comia justo la perspectiva del pasillo, que es
   lo mejor que tiene esa toma. Con altura fija el panel no salta al cambiar de
   canal Y ninguna foto se estira. */
.cov__ph {
  position: relative; border-radius: var(--r-media); overflow: hidden;
  border: 1px solid var(--line);
  height: clamp(300px, 32vw, 440px);
}
.cov__ph img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 46%; }

.cov__foot {
  margin-top: clamp(28px, 3.2vw, 44px); max-width: 62ch;
  font-size: 15px; line-height: 1.6; color: var(--text-2);
}

/* enlace de texto que abre el modal. Existe para que un CTA pueda vivir DENTRO
   de una frase: un boton grande ahi cortaria la lectura. */
.lnk {
  padding: 0; background: none; border: 0; cursor: pointer;
  font: inherit; color: var(--text); font-weight: 700;
  text-decoration: underline; text-underline-offset: .18em;
  text-decoration-color: var(--teal); text-decoration-thickness: 2px;
  transition: text-decoration-color 160ms var(--ease-out);
}
.lnk:hover { text-decoration-color: var(--teal-hi); }

/* los badges del cierre, cuando el CTA es descargar la app y no ver la demo */
.appdl--finale { align-items: flex-start; padding-right: 0; }
.appdl--finale .appdl__b img { width: 132px; }

/* ---------- ZNAPPERS ---------- */
/* la foto del hero es de una PERSONA, no de un dashboard: es lo unico del
   sistema visual que cambia en esta pagina, y cambia porque el publico cambia */
.appdl--hero { align-items: flex-start; padding-right: 0; margin-top: clamp(26px, 3vw, 36px); }
.appdl--hero .appdl__b img { width: 132px; }

/* ==========================================================================
   EL BUSCADOR DE AYUDA
   ==========================================================================
   Dispositivo propio de esta ruta, y el único del sitio que es una HERRAMIENTA
   y no una narración: el que llega a Ayuda no viene a leer, viene a encontrar
   una cosa. Trece preguntas repartidas en dos públicos son muchas para barrer
   con el ojo.

   ES EL PROTAGONISTA DE LA PÁGINA, Y SE CONSTRUYE POR SUPERFICIE, NO POR
   TAMAÑO DE LETRA. La sección va sobre `--surface` y la caja sobre `--paper`
   con sombra: es la única pieza ELEVADA de la ruta, así que se lee como un
   instrumento apoyado encima de la página en vez de como un campo dibujado en
   ella. Agrandar la tipografía habría competido con el titular y habría dejado
   dos protagonistas, que es exactamente lo que el criterio prohíbe.

   El disco teal de la lupa es lo que la ancla a la marca. Va en `--teal-field`
   con el glifo en `--on-teal`, la misma decisión que el resto del sitio.
   ========================================================================== */
.busca {
  margin-top: clamp(28px, 3.4vw, 42px); margin-inline: auto;
  max-width: 660px; text-align: left;
}
.busca__l {                      /* la etiqueta existe para el lector de
                                    pantalla; en pantalla la dice el placeholder */
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}
.busca__caja {
  display: flex; align-items: center; gap: 13px;
  padding: 10px 10px 10px 11px;
  border: 1px solid var(--line-2); border-radius: var(--r-pill);
  background: var(--paper);
  box-shadow: var(--sh-2);
  transition: border-color .2s var(--ease-out), box-shadow .2s var(--ease-out);
}
.busca__caja:focus-within {
  border-color: var(--teal);
  box-shadow: var(--sh-3), 0 0 0 3px rgba(19,193,172,.20);
}
.busca__ico {
  flex: none; display: grid; place-items: center;
  width: 44px; height: 44px; border-radius: 50%;
  background: var(--teal-field); color: var(--on-teal);
}
.busca__i {
  flex: 1 1 auto; min-width: 0;
  padding: 14px 0; border: 0; background: none;
  font: inherit; font-size: 17px;   /* 16px o más, o iOS hace zoom al enfocar */
  letter-spacing: var(--tr-body);
  color: var(--text);
}
.busca__i::placeholder { color: var(--text-3); }
.busca__i:focus { outline: none; }   /* el foco lo dibuja la caja, no el campo */
.busca__i::-webkit-search-cancel-button { display: none; }
/* EL ATRIBUTO `hidden` NO GANA CONTRA UN `display` EXPLICITO. Es la misma
   trampa que ya costó una vuelta entera en este proyecto (una portada con
   `display:flex` tapaba toda la pantalla estando `hidden`). El script apaga
   este botón con `hidden` cuando el campo está vacío, y sin esta regla la cruz
   de borrar se veía siempre, invitando a borrar una búsqueda que no existe. */
.busca__x[hidden] { display: none; }
.busca__x {
  flex: none; display: grid; place-items: center;
  width: 44px; height: 44px; border-radius: 50%;
  border: 0; background: var(--surface-2); color: var(--text-2);
  cursor: pointer;
  transition: background-color .2s var(--ease-out), color .2s var(--ease-out);
}
.busca__x:hover { background: var(--text); color: var(--paper); }
/* EL CONTADOR NUNCA MIENTE SOBRE LO QUE HAY ABAJO. Dice cuántas quedan, y dice
   también cuándo la búsqueda se aflojó a "alguna de tus palabras": una lista
   filtrada sin contador obliga a contar a mano para saber si falta algo. */
.busca__n {
  margin-top: 14px; text-align: center;
  font-family: var(--mono); font-size: 11.5px; font-weight: 500;
  letter-spacing: .08em; text-transform: uppercase; color: var(--text-2);
}

/* --- LOS ATAJOS ---
   NIVEL 3. Cada uno escribe esa palabra en el campo y dispara la misma
   búsqueda. Los cinco están elegidos sobre términos que DEVUELVEN respuesta:
   un atajo que lleva al estado vacío sería una promesa rota, y esa es
   justamente la falla que esta página no puede cometer. */
.busca__at {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: 8px; margin-top: 18px;
}
/* el rótulo va en --text-2 y no en --text-3: a 11,5px hace falta 4,5:1 y el
   terciario da 3,85:1 sobre --surface. Medido en el navegador, no estimado. */
.busca__atl {
  margin-right: 2px;
  font-family: var(--mono); font-size: 11.5px; font-weight: 500;
  letter-spacing: .08em; text-transform: uppercase; color: var(--text-2);
}
.busca__a {
  min-height: 44px; padding: 10px 16px 11px;
  border: 1px solid var(--line-2); border-radius: var(--r-pill);
  background: none; cursor: pointer;
  font: inherit; font-size: 14px; letter-spacing: var(--tr-micro);
  color: var(--text-2);
  transition: background-color .2s var(--ease-out), border-color .2s var(--ease-out),
              color .2s var(--ease-out);
}
.busca__a:hover { background: var(--text); border-color: var(--text); color: var(--paper); }

/* --- EL ESTADO VACÍO ---
   Ocupa el lugar exacto de lo que no encontró, adentro del cuerpo de preguntas
   y no como sección aparte. Dice la consulta TEXTUAL y no una fórmula, y no se
   queda en "no hay resultados": manda a los canales, que es la respuesta
   honesta a una pregunta que no está en la lista. */
.nada[hidden] { display: none; }
.nada {
  display: grid; justify-items: start; max-width: 780px;
  padding: clamp(30px, 3.4vw, 46px) clamp(24px, 3vw, 40px) clamp(34px, 3.8vw, 50px);
  border: 1px solid var(--line); border-radius: var(--r-surface);
  background: var(--surface);
}
.nada__k {
  font-family: var(--mono); font-size: 11.5px; font-weight: 700;
  letter-spacing: .1em; text-transform: uppercase; color: var(--text-2);
}
.nada__t {
  margin-top: 14px; max-width: 22ch;
  font-size: clamp(24px, 2.6vw, 36px); font-weight: 700;
  line-height: 1.14; letter-spacing: var(--tr-h2); color: var(--text);
}
/* la consulta se repite tal cual la escribió el visitante: el subrayado teal es
   el mismo señalador del resto del sitio, y acá dice "esto es lo que buscaste",
   que es lo que vuelve honesto al mensaje */
.nada__q {
  box-shadow: inset 0 -.09em 0 var(--teal);
  overflow-wrap: anywhere;
}
.nada__d { margin-top: 16px; max-width: 56ch; font-size: 16px; line-height: 1.6; color: var(--text-2); }
.nada__cta { margin-top: clamp(22px, 2.4vw, 30px); }

/* ==========================================================================
   /znappers v27: LA RUTA DE LAS SEIS ESCENAS
   ==========================================================================
   El POR QUE se rehizo entera esta en la cabecera del HTML. Acá va la forma.

   LA FILOSOFIA SE COPIA DE /empresas Y LOS DISPOSITIVOS NO, que es pedido
   explicito del cliente. De /empresas se reusa el SISTEMA —`.esc`, el encaje,
   la escala tipografica de escena (`.esc__ojo`, `.esc__h2`, `.esc__sub`), el
   escalonado de entrada por `data-en`— y NO se reusa ninguna de sus piezas: ni
   el espinazo, ni el mapa, ni la lamina anotada, ni la cadena de siete
   columnas, ni el barrido de `.esc__mark`, que es la firma de aquella ruta.

   LO QUE ESTA RUTA ESTRENA, Y ES LO QUE LA HACE SUYA:

     `.fono`     EL TELEFONO. /empresas no tiene uno solo, y esta pagina habla
                 de una app: el objeto que se mira tiene que ser el aparato.
                 Una unidad, dos usos: `--solo` en la escena de la plata,
                 `--fila` cinco veces en la escena de los pasos. Como las dos
                 escenas encajan al mismo pixel, el aparato no se mueve entre
                 pantallas: cambia de pantalla.

     `.zn__pill` LA PASTILLA DE RECOMPENSA, y no se invento acá: es como la
                 dibuja la app. En la captura real `$ 2.500,00` va sobre un
                 campo teal redondeado. La pagina la agranda; no le cambia la
                 forma. Es la firma tipografica de la ruta, del mismo modo que
                 el barrido del resaltador lo es de /empresas.

     `.zbaja`    LA BARRA DE DESCARGA, que acompaña toda la ruta y se apaga en
                 las dos pantallas que ya tienen los mismos botones.

   EL RITMO DE TONOS. Seis escenas, todas de luz, y el tono ordena el relato en
   vez de alternar por alternar:
     calle  --surface     la calle, que es de donde venis
     plata  --paper       el papel mas limpio para el dato que mas importa
     paso   --surface-2   el mecanismo, un escalon adentro
     somos  --surface-3   el mas hondo, y es donde estan las caras
     pide   --paper       vuelve al papel para la letra chica
     preg   --surface     y cierra donde empezo
   ========================================================================== */

/* --- LA PASTILLA DE RECOMPENSA ---
   Blanco sobre el teal de marca da 2,27:1 y no pasa AA. Es decision explicita
   del cliente, tomada con los numeros delante, y ademas acá es lo que hace la
   app: replicar su pastilla con otro color seria inventar un producto que no
   existe. El detalle completo esta en `ZNAPP.md`. */
.zn__pill {
  display: inline-block;
  padding: .1em .46em .16em;
  border-radius: 999px;
  background: var(--teal-field);
  color: var(--on-teal);
  font-weight: 700;
  box-decoration-break: clone; -webkit-box-decoration-break: clone;
}
/* dentro del titular del hero: el radio completo la ata a la pastilla de la
   app, o sea que la promesa del titular y el dato de la captura se dibujan
   igual. `.1em` de recorte lateral para que no se despegue de la palabra. */
.zn__pill--h1 { padding-inline: .28em; }
/* LA CIFRA. `tabular-nums` es obligatorio y no cosmetico: el numero se cuenta
   solo al entrar la escena, y con cifras de ancho variable la pastilla cambia
   de tamaño en cada cuadro del conteo. */
.zn__pill--xl {
  margin-top: 4px;
  font-size: clamp(30px, min(3.4vw, 5.4vh), 52px);
  letter-spacing: var(--tr-h2);
  font-variant-numeric: tabular-nums;
  padding: .12em .34em .2em;
}
/* Y SE APOYA AL ENTRAR. Llega un pelo mas chica y se asienta: es la diferencia
   entre un numero que aparece y una plata que llega. Detras de `.js` para que
   sin script la pastilla este puesta desde el primer cuadro. */
.js .esc .zn__pill--xl {
  transform: scale(.88); transform-origin: 0 50%;
  transition: transform .62s var(--ease-exp) .3s;
}
.js .esc.is-en .zn__pill--xl { transform: none; }
@media (prefers-reduced-motion: reduce) {
  .js .esc .zn__pill--xl { transform: none; transition: none; }
}

/* ==========================================================================
   EL TELEFONO
   ==========================================================================
   LAS CAPTURAS SON PANTALLAZOS CRUDOS, sin aparato alrededor: puestas tal cual
   se leen como rectangulos flotando, y a esa altura nadie las lee como "esto
   es la app". El marco es la pieza mas barata que existe para decirlo, y es
   CSS —un radio, un canto y una sombra—, no un dibujo.

   ALTO DEFINIDO Y `aspect-ratio`, NO `width` + alto automatico. Es la leccion
   6 de este proyecto: `height: 100%` contra un `min-height` vale `auto`, y una
   captura de 589x1280 en una columna de 667 mide 1.186px, o sea mas que la
   ventana. Acá el alto manda y el ancho lo deduce la proporcion, asi que un
   telefono NUNCA puede pasarse de la escena. */
.fono {
  position: relative; margin: 0;
  aspect-ratio: 589 / 1280;
  border-radius: clamp(18px, 1.9vw, 30px);
  background: var(--ink);
  padding: clamp(4px, .5vw, 7px);
  overflow: hidden;
  box-shadow: 0 2px 4px rgba(52,49,44,.14), 0 30px 60px -26px rgba(52,49,44,.52);
}
.fono__pant {
  display: block; width: 100%; height: 100%;
  object-fit: cover; object-position: 50% 0;
  border-radius: clamp(13px, 1.4vw, 24px);
}
/* el canto interior. Sin el, el marco es una mancha oscura y no un aparato. */
.fono__marco {
  position: absolute; inset: 0; border-radius: inherit; pointer-events: none;
  box-shadow: inset 0 0 0 1px rgba(247,244,239,.16), inset 0 1px 0 rgba(247,244,239,.24);
}

/* EL DE LA ESCENA DE LA PLATA: uno solo, grande, y flota apenas.
   La deriva es de 6px en 7 s. No es un efecto: es lo unico que evita que una
   captura quieta se lea como una imagen pegada. `transform` lo resuelve el
   compositor, asi que no cuesta un repintado. */
.fono--solo {
  height: clamp(300px, 66svh, 660px);
  justify-self: center; align-self: center;
  animation: fonoFlota 7s var(--ease-io) infinite alternate;
}
@keyframes fonoFlota { from { transform: translateY(0); } to { transform: translateY(-7px); } }
@media (prefers-reduced-motion: reduce) { .fono--solo { animation: none; } }

/* ==========================================================================
   ESCENA 1 — LA CALLE
   ==========================================================================
   SU PADDING Y SU ESPACIADO SON LA REFERENCIA QUE PIDIO EL CLIENTE, textual,
   asi que la geometria se conserva entera de la version anterior: la columna
   de texto apoyada en el margen de la ruta —el mismo que usa `--wrap` al
   centrarse, para que el titular arranque donde arranca todo lo demas— y la
   foto saliendose por la derecha.

   VA A SANGRE Y REDONDEADA SOLO DEL LADO QUE SE VE. Un recorte que termina en
   el borde se lee como una VENTANA; el mismo recorte con aire alrededor se lee
   como un `<img>` puesto al lado del texto, que es lo que hace cualquier
   plantilla. Y redondear el canto que se sale dibujaria una esquina flotando
   contra el borde, que es el detalle que delata un bloque "a sangre" que en
   realidad no lo es. */
.esc--calle {
  padding: 0;
  align-content: stretch;
  background: var(--surface);
}
.calle__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, .84fr);
  align-items: stretch;
  gap: clamp(24px, 3.4vw, 56px);
  /* EL MARGEN VA ACA Y NO EN LA COLUMNA DE TEXTO, Y ES UNA CORRECCION MEDIDA.
     La version anterior escribia `max(24px, calc((100% - var(--wrap)) / 2))`
     sobre `.calle__copy`, y un porcentaje de relleno se resuelve contra el
     ANCHO DEL CONTENEDOR, que ahi es la columna —unos 790px— y no la ventana.
     Con `--wrap` en 1040 la cuenta daba negativa y ganaba el piso de 24px: el
     titular del hero arrancaba en x=24 mientras las otras cinco escenas
     arrancaban en 236. Medido en 1512: 212px de diferencia entre la primera
     pantalla y todas las demas, que es exactamente lo que se lee como que el
     espaciado no esta cuidado.
     En el contenedor, `100%` SI es el ancho de la escena, y ademas descuenta la
     barra de scroll sola, cosa que `100vw` no hace. */
  padding-left: max(24px, calc((100% - var(--wrap)) / 2));
}
.calle__copy {
  align-self: center; min-width: 0;
  padding-block: clamp(40px, 6vh, 84px);
}
/* EL TITULAR MAS GRANDE DEL SITIO. Es la unica pagina que le habla a una
   persona sobre su plata, y el tamaño es parte del argumento.
   MIRA EL ALTO TAMBIEN, igual que la escala de /empresas: comparte pantalla
   con una foto de piso a techo, asi que en una ventana baja tiene que ceder o
   la foto se corta. */
.calle__h1 {
  margin-top: clamp(16px, 1.8vw, 24px);
  font-size: clamp(34px, min(4vw, 7.4vh), 60px); font-weight: 700;
  line-height: 1.06; letter-spacing: var(--tr-h1); color: var(--text);
}
.calle__h1 > span { display: block; }
.calle__l1 { color: var(--text-2); }
.calle__l2 { color: var(--text); }
.calle__sub {
  margin-top: clamp(18px, 2vw, 26px); max-width: 42ch;
  font-size: clamp(16px, 1.45vw, 19px); line-height: 1.55; color: var(--text-2);
}
.calle__sub .key { color: var(--text); font-weight: 700; }

.calle__foto {
  position: relative; margin: 0;
  /* ALTO DEFINIDO Y NO `min-height`: ver la leccion 6, arriba en `.fono`. El
     tope de 820 existe para las ventanas altas, donde sin el la foto mide mil
     pixeles y el titular queda flotando en el medio de un vacio. */
  height: clamp(420px, calc(100svh - var(--navh, 64px)), 820px);
  overflow: hidden;
  border-radius: clamp(18px, 2vw, 30px) 0 0 clamp(18px, 2vw, 30px);
  background: var(--surface-3);
}
.calle__img {
  width: 100%; height: 100%; object-fit: cover;
  /* 28% Y NO EL CENTRO: la foto es vertical de 1151x2048 y en una ventana
     apaisada el recorte se come la cabeza o los pies. A 28 quedan adentro la
     cara, la remera con la marca, el celular y el producto en la otra mano,
     que son las cuatro cosas que esta imagen tiene que decir. */
  object-position: 50% 28%;
  animation: calleDeriva 26s var(--ease-io) infinite alternate;
}
@keyframes calleDeriva { from { transform: scale(1); } to { transform: scale(1.045); } }
@media (prefers-reduced-motion: reduce) { .calle__img { animation: none; } }

/* ==========================================================================
   ESCENA 2 — CUANTO
   ==========================================================================
   Es el eje de la pagina. La composicion es simple a proposito: el argumento a
   la izquierda, la prueba a la derecha, y nada mas en pantalla. */
.esc--plata { background: var(--paper); }
.plata__grid {
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, .58fr);
  gap: clamp(28px, 4vw, 64px);
  align-items: center;
}
.plata__say { min-width: 0; }
.plata__lista { margin-top: clamp(20px, 2.8vh, 34px); }
/* UNA SOLA LINEA POR FILA, ARRIBA. Dos hairlines por fila —una arriba y otra
   abajo— dibujan una reja: con tres filas es exactamente el doble de tinta que
   hace falta para separarlas. */
.plata__f {
  display: grid; grid-template-columns: minmax(0, 15ch) minmax(0, 1fr);
  align-items: baseline; gap: clamp(12px, 1.6vw, 26px);
  padding: clamp(11px, 1.5vh, 16px) 0;
  border-top: 1px solid var(--line);
}
.plata__f dt {
  font-family: var(--mono); font-size: clamp(10.5px, .95vw, 12px); font-weight: 600;
  letter-spacing: .13em; text-transform: uppercase; color: var(--text-2);
}
.plata__f dd {
  margin: 0; font-size: clamp(16px, 1.5vw, 20px); font-weight: 700;
  letter-spacing: var(--tr-sub); color: var(--text);
}
.plata__dir {
  display: block; margin-top: 3px;
  font-size: clamp(13.5px, 1.15vw, 15px); font-weight: 400; color: var(--text-2);
}
/* la fila del pago no lleva linea propia arriba de la pastilla: la pastilla ya
   es la separacion mas fuerte del bloque */
.plata__f--pago { align-items: center; }
.plata__nota {
  margin-top: clamp(16px, 2.2vh, 24px); max-width: 52ch;
  font-size: clamp(13.5px, 1.15vw, 15px); line-height: 1.55; color: var(--text-2);
}
.plata__nota b { color: var(--text); }

/* ==========================================================================
   ESCENA 3 — LA MISION: LOS CINCO TELEFONOS
   ==========================================================================
   POR QUE CINCO Y NO UNO QUE SE CAMBIA A SI MISMO. Un telefono solo explica el
   MECANISMO y no la SECUENCIA: en cualquier cuadro es una pantalla suelta. Con
   los cinco en fila se ve que es un recorrido, y ademas se ve cuanto falta.

   LOS BOTONES CAEN DEBAJO DE SU TELEFONO, columna por columna: la misma
   grilla, el mismo hueco. Sin eso serian un control al lado de una imagen; asi
   son la etiqueta de cada pantalla.

   CERO JAVASCRIPT NUEVO. Corre con `selectores()`, el modulo generico. La hoja
   pregunta cual boton esta activo con `:has()` y de ahi saca cual telefono se
   levanta. El activo NO cambia de alto —animar `height` repinta la fila
   entera—: crece con `transform` desde su base, que lo resuelve el compositor.

   `transform-origin: 50% 100%` Y NO EL CENTRO: los cinco estan apoyados sobre
   la misma linea de piso, y creciendo desde el centro el activo se hundiria
   media pantalla abajo de esa linea. */
.esc--paso { background: var(--surface-2); }
/* EL TOPE DE ANCHO VA EN EL TITULAR, NO EN EL `.wrap` QUE LO CONTIENE.
   Escrito sobre `.paso__cabe`, que YA ES un `.wrap`, le ganaba a
   `max-width: var(--wrap)` y el bloque se centraba en la ventana entera: el
   titular quedaba flotando en el medio mientras los cinco telefonos y la
   lectura, que si usan la cuenta del margen, arrancaban 300px a su izquierda.
   Se veia como dos piezas de dos paginas distintas pegadas. */
.paso__cabe .esc__h2 { max-width: 18ch; }
.paso__ui {
  --hueco: clamp(10px, 1.3vw, 22px);
  margin-top: clamp(18px, 3vh, 40px);
}
.paso__fila {
  display: grid; grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: var(--hueco);
  align-items: end; justify-items: center;
  width: 100%; max-width: var(--wrap); margin-inline: auto; padding-inline: var(--gut);
  /* aire para que el telefono levantado no se corte contra el borde de arriba */
  padding-top: clamp(10px, 2vh, 26px);
}
.fono--fila {
  height: clamp(150px, 33svh, 360px);
  /* LOS QUE NO SON EL ACTIVO NO SE ESCONDEN: SE APAGAN. Escondidos volveriamos
     a tener una sola pantalla en escena, que es el problema que esta fila vino
     a resolver. Apagados siguen contando que el recorrido tiene cinco pasos. */
  opacity: .48; filter: saturate(.55);
  transform: scale(.9); transform-origin: 50% 100%;
  box-shadow: 0 1px 2px rgba(52,49,44,.1);
  transition: opacity .5s var(--ease), transform .5s var(--ease-exp),
              filter .5s var(--ease), box-shadow .5s var(--ease);
}
.paso__ui:has(#pn1.is-active) .fono--fila[data-paso="1"],
.paso__ui:has(#pn2.is-active) .fono--fila[data-paso="2"],
.paso__ui:has(#pn3.is-active) .fono--fila[data-paso="3"],
.paso__ui:has(#pn4.is-active) .fono--fila[data-paso="4"],
.paso__ui:has(#pn5.is-active) .fono--fila[data-paso="5"] {
  opacity: 1; filter: none; transform: scale(1.1);
  box-shadow: 0 2px 4px rgba(52,49,44,.12), 0 30px 58px -22px rgba(52,49,44,.5);
}
@media (prefers-reduced-motion: reduce) {
  .fono--fila { transition: none; }
}

.paso__pie {
  width: 100%; max-width: var(--wrap); margin-inline: auto; padding-inline: var(--gut);
  margin-top: clamp(16px, 2.4vh, 30px);
}
.paso__botones {
  display: grid; grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: var(--hueco);
}
.paso__b {
  position: relative;
  display: grid; gap: 3px; justify-items: center;
  padding: clamp(8px, 1.2vh, 12px) 4px clamp(9px, 1.3vh, 13px);
  border: 0; border-top: 1px solid var(--line);
  background: none; font: inherit; text-align: center; cursor: pointer;
  /* --text-2 Y NO --text-3, MEDIDO: el verbo mide 17px y a ese tamaño hace
     falta 4,5:1. Sobre `--surface-2`, `--text-3` daba 3,53. */
  color: var(--text-2);
  transition: color .26s var(--ease);
}
/* EL NUMERAL VA EN --text-2 Y EL ACTIVO EN TINTA, Y LOS DOS SON CORRECCIONES
   MEDIDAS. A 12px hace falta 4,5:1 sobre el fondo de la escena, y el barrido
   dio 2,38 con `--text-4` y 1,96 con `--teal-deep` — que desde la v14 apunta al
   teal de marca, o sea que un numeral teal sobre luz es ilegible.
   La marca de "este es el paso" no se pierde: la lleva el subrayado, que ademas
   es el reloj del avance automatico. Un color de texto no tiene que hacer el
   trabajo que ya hace una linea. */
.paso__bn {
  font-family: var(--mono); font-size: clamp(10px, .9vw, 11.5px); font-weight: 700;
  letter-spacing: .1em; color: var(--text-2);
  transition: color .26s var(--ease);
}
.paso__bt {
  font-size: clamp(13px, 1.15vw, 17px); font-weight: 700; letter-spacing: var(--tr-sub);
}
@media (hover: hover) and (pointer: fine) { .paso__b:hover { color: var(--text); } }
/* LO ANDADO SE MARCA. Un recorrido que no muestra por donde pasaste no se lee
   como recorrido: `is-done` lo pone `selectores()` en los pasos ya vistos. */
.paso__b.is-done { color: var(--text-2); }
.paso__b.is-active { color: var(--text); }
.paso__b.is-active .paso__bn { color: var(--text); }
/* EL SUBRAYADO ES TAMBIEN EL RELOJ: marca cual paso esta elegido y, mientras el
   selector avanza solo, cuanto falta para el proximo salto. Es el mismo
   criterio que el selector de canal de /empresas —que el reloj y la seleccion
   sean el mismo objeto los vuelve imposibles de confundir— con el mismo
   `@keyframes selFill`. */
.paso__b::after {
  content: ''; position: absolute; left: 0; right: 0; top: -1px; height: 2px;
  background: var(--teal);
  transform: scaleX(0); transform-origin: 0 50%;
  transition: transform .22s var(--ease-out);
}
.paso__b.is-active::after { transform: scaleX(1); }
.sel-auto:not(.sel-tomado) .paso__b.is-active::after {
  animation: selFill var(--sel-auto, 4600ms) linear;
}
@media (prefers-reduced-motion: reduce) {
  .sel-auto:not(.sel-tomado) .paso__b.is-active::after { animation: none; }
}

.paso__stage {
  margin-top: clamp(12px, 1.8vh, 20px);
  /* ALTO RESERVADO: las cinco lecturas no miden lo mismo, y sin un piso la
     pantalla salta cada vez que el selector avanza solo. */
  min-height: clamp(76px, 9vh, 104px);
}
.paso__t {
  font-size: clamp(17px, 1.7vw, 24px); font-weight: 700; line-height: 1.15;
  letter-spacing: var(--tr-h2); color: var(--text);
}
.paso__d {
  margin-top: 8px; max-width: 62ch;
  font-size: clamp(14px, 1.2vw, 16.5px); line-height: 1.55; color: var(--text-2);
}
.paso__d b { color: var(--text); font-weight: 700; }

/* ==========================================================================
   ESCENA 4 — LA RED: LAS CARAS Y LAS CIFRAS
   ==========================================================================
   VAN JUNTAS PORQUE POR SEPARADO NO DICEN NADA. Un numero sin caras al lado se
   lee como marketing; las caras sin numero no dicen la escala.

   NO ES EL MURO DE DOCE FOTOS QUE SE RECHAZO. Aquel eran doce recortes de
   230px en el hero, y a ese tamaño una persona se lee como TEXTURA. Acá es UNA
   fila con las fotos grandes, y su trabajo es que se vean caras.

   SE MUEVE PORQUE LA CIFRA ES UN PRESENTE: "6.000 activos" no es un numero de
   archivo. Es `transform` sobre el compositor, sin un solo listener, y la
   lista va duplicada para que el bucle no deje hueco.
   ES EL UNICO CARRUSEL CONTINUO DE LA PAGINA; dos serian relleno. */
.esc--somos { background: var(--surface-3); }
.somos__cabe .esc__h2 { max-width: 24ch; }
.somos__cifras {
  width: 100%; max-width: var(--wrap); margin-inline: auto; padding-inline: var(--gut);
  margin-top: clamp(20px, 3vh, 40px);
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(18px, 3vw, 48px);
}
.somos__n {
  display: block;
  font-size: clamp(34px, min(4vw, 6vh), 58px); font-weight: 700;
  line-height: 1; letter-spacing: var(--tr-h1); color: var(--text);
  font-variant-numeric: tabular-nums;
}
/* EL "+" NO VA EN TEAL. Medido: `--teal-deep` sobre `--surface-3` da 1,79 y a
   54px el minimo es 3. Y ademas es parte del numero, no un adorno al lado: en
   la misma tinta un poco mas clara se lee como lo que es, "seis mil y algo". */
.somos__n b { color: var(--text-2); font-weight: 700; }
.somos__l {
  display: block; margin-top: 8px; max-width: 25ch;
  font-size: clamp(13.5px, 1.15vw, 15.5px); line-height: 1.45; color: var(--text-2);
  /* `pretty` y no `balance`: con tres columnas cortas, `balance` deja una
     palabra huerfana en el segundo renglon ("mas" sola) */
  text-wrap: pretty;
}

/* LA BANDA VA A SANGRE Y SE DESVANECE EN LOS DOS BORDES. Sin el desvanecido,
   una foto cortada en seco contra el borde se lee como un error de recorte; con
   el, la fila se lee como que sigue afuera de la pantalla, que es lo que dice.
   `--paso-b` es el ancho de una foto mas su hueco: el bucle corre exactamente
   ocho pasos, que es medio riel, asi que empalma sin salto. */
.somos__banda {
  --paso-b: calc(clamp(146px, 15vw, 236px) + clamp(10px, 1.2vw, 18px));
  margin-top: clamp(22px, 3.4vh, 46px);
  overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, rgba(0,0,0,0) 0, #000 7%, #000 93%, rgba(0,0,0,0) 100%);
          mask-image: linear-gradient(90deg, rgba(0,0,0,0) 0, #000 7%, #000 93%, rgba(0,0,0,0) 100%);
}
.somos__riel {
  display: flex; gap: clamp(10px, 1.2vw, 18px); width: max-content;
  animation: somosCorre 64s linear infinite;
  will-change: transform;
}
.somos__riel img {
  display: block; flex: none;
  width: clamp(146px, 15vw, 236px);
  height: clamp(184px, 24svh, 300px);
  object-fit: cover; object-position: 50% 32%;
  border-radius: clamp(12px, 1.2vw, 18px);
  background: var(--surface-2);
}
@keyframes somosCorre {
  from { transform: translateX(0); }
  to   { transform: translateX(calc(var(--paso-b) * -8)); }
}
@media (hover: hover) and (pointer: fine) {
  .somos__banda:hover .somos__riel { animation-play-state: paused; }
}
@media (prefers-reduced-motion: reduce) { .somos__riel { animation: none; } }

/* ==========================================================================
   ESCENA 5 — QUE HACE FALTA
   ==========================================================================
   Las dos listas enfrentadas. La de la derecha es la que le habla de verdad a
   este publico: alguien que ya reparte con una app no necesita que le digan
   que SI hace falta, necesita saber que NO.

   LOS TILDES SE DIBUJAN SOLOS al entrar la escena, uno detras del otro. Es
   `stroke-dashoffset` sobre un trazo —la misma primitiva que el detector de la
   home— y es la unica animacion de esta pantalla: dice lo que la pantalla dice.
   El `--i` de cada `li` sale del orden, con `nth-child`, y no del marcado: un
   retardo escrito a mano en ocho elementos es la forma de que un dia falte. */
.esc--pide { background: var(--paper); }
.pide__grid {
  margin-top: clamp(22px, 3.4vh, 46px);
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(24px, 4vw, 64px);
}
.pide__k {
  font-family: var(--mono); font-size: clamp(10.5px, .95vw, 12px); font-weight: 600;
  letter-spacing: .14em; text-transform: uppercase; color: var(--text-2);
  padding-bottom: clamp(10px, 1.4vh, 15px);
  border-bottom: 1px solid var(--line);
}
.pide__l { display: grid; }
.pide__l li {
  display: grid; grid-template-columns: auto minmax(0, 1fr);
  align-items: center; gap: 12px;
  /* ESTA ES LA PANTALLA QUE MENOS TIENE, ASI QUE LO QUE TIENE OCUPA MAS. Con la
     escala anterior el bloque medía 678 de 900 y la mitad de abajo quedaba
     vacia — no aireada, vacia. Ocho renglones no se resuelven agregando una
     novena pieza: se resuelven dandole a los ocho el tamaño que la pantalla
     puede pagar. */
  padding: clamp(13px, 2.1vh, 21px) 0;
  font-size: clamp(16px, 1.6vw, 22px); font-weight: 700; letter-spacing: var(--tr-sub);
  color: var(--text);
}
.pide__g { flex: none; }
.pide__col--si .pide__g { color: var(--teal-deep); }
/* la cruz NO va en rojo: no es un error ni una advertencia, es una cosa que no
   hace falta. En gris dice "esto no aplica", que es lo que se quiere decir. */
.pide__col--no .pide__g { color: var(--text-4); }
.pide__col--no .pide__l li { color: var(--text-2); }

.pide__tr { stroke-dasharray: 34; stroke-dashoffset: 0; }
.js .esc--pide .pide__tr { stroke-dashoffset: 34; }
.js .esc--pide.is-en .pide__tr {
  stroke-dashoffset: 0;
  transition: stroke-dashoffset .5s var(--ease-io) calc(.36s + var(--gi, 0) * 90ms);
}
.pide__l li:nth-child(1) { --gi: 0; }
.pide__l li:nth-child(2) { --gi: 1; }
.pide__l li:nth-child(3) { --gi: 2; }
.pide__l li:nth-child(4) { --gi: 3; }
.pide__col--no .pide__l li:nth-child(1) { --gi: .5; }
.pide__col--no .pide__l li:nth-child(2) { --gi: 1.5; }
.pide__col--no .pide__l li:nth-child(3) { --gi: 2.5; }
.pide__col--no .pide__l li:nth-child(4) { --gi: 3.5; }
/* la segunda barra de la cruz sale un pelo despues que la primera */
.pide__tr--b { transition-delay: calc(.44s + var(--gi, 0) * 90ms) !important; }
@media (prefers-reduced-motion: reduce) {
  .js .esc--pide .pide__tr { stroke-dashoffset: 0; transition: none; }
}

/* ==========================================================================
   ESCENA 6 — LAS PREGUNTAS
   ==========================================================================
   LAS CINCO ESTAN ABIERTAS, Y ESO ES A PROPOSITO. Antes eran un acordeon:
   cinco titulos y cuatro respuestas escondidas. En una pagina que se recorre de
   una pasada, una respuesta escondida es una respuesta que nadie lee — y un
   acordeon adentro de una escena que mide una pantalla la haria CRECER al
   abrirse y pelearse con el encaje, que es un bug y no una molestia.
   Son cinco respuestas cortas: entran las cinco.

   DOS COLUMNAS CON `columns` Y NO CON GRID: las cinco preguntas no miden lo
   mismo y con grid la quinta dejaria una celda vacia al lado. El flujo por
   columnas reparte por ALTO, que es lo que hace que las dos terminen parejas. */
.esc--preg { background: var(--surface); }
.preg__grid {
  margin-top: clamp(22px, 3.4vh, 46px);
  columns: 2; column-gap: clamp(28px, 4.4vw, 72px);
}
.preg__q {
  break-inside: avoid;
  padding: clamp(12px, 1.7vh, 18px) 0;
  border-top: 1px solid var(--line);
}
.preg__q h3 {
  font-size: clamp(15.5px, 1.35vw, 18.5px); font-weight: 700;
  line-height: 1.25; letter-spacing: var(--tr-sub); color: var(--text);
}
.preg__q p {
  margin-top: 6px;
  font-size: clamp(13.5px, 1.15vw, 15.5px); line-height: 1.55; color: var(--text-2);
}
.preg__more { break-inside: avoid; margin-top: clamp(16px, 2.4vh, 26px); }

/* ==========================================================================
   LA BARRA DE DESCARGA
   ==========================================================================
   LA RESPUESTA DIRECTA A "no gira en torno al CTA de buscar descargas": las dos
   tiendas viajan pegadas al borde de abajo durante toda la ruta.

   Y ES TAMBIEN EL INDICE DEL RECORRIDO. La linea teal de arriba se llena con el
   avance de la pagina, asi que la barra dice a la vez que hacer y donde estas.
   Es lo que /empresas resuelve con el espinazo de la izquierda, resuelto de la
   unica forma que ademas empuja a descargar — y por eso no es un calco.

   VA DETRAS DE `.js` Y NACE CON `hidden`. Una barra fija que no se sabe apagar
   taparia contenido para siempre, asi que sin script no existe.
   Y LLEVA SU PROPIA REGLA `[hidden]`: un `display` explicito le gana al
   atributo, y esta hoja ya pago ese error dos veces. */
.zbaja {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 60;
  display: block;
  border-top: 1px solid var(--line);
  background: rgba(250, 248, 245, .88);
  -webkit-backdrop-filter: blur(18px) saturate(1.3);
          backdrop-filter: blur(18px) saturate(1.3);
  box-shadow: 0 -14px 34px -22px rgba(52,49,44,.5);
  transform: translateY(110%);
  transition: transform .48s var(--ease-exp);
}
.zbaja[hidden] { display: none; }
/* LAS ESCENAS DEJAN LUGAR PARA LA BARRA, Y SIN ESTO TAPABA CONTENIDO.
   Medido en 390x844: la escena de las preguntas terminaba a 24px del borde y la
   barra mide 62, asi que el enlace a /ayuda quedaba abajo de las tiendas. Una
   barra fija que no reserva su lugar no es una barra, es una tapa.
   EL ALTO SALE MEDIDO, NO ESTIMADO: lo publica `barraDescarga()` en `--zbajah`,
   igual que `escenas()` publica el de la nav. Escrito a mano, cambiar el
   contenido de la barra desalinea las cinco escenas de golpe y nadie se entera.
   El hero queda afuera a proposito: es la unica escena donde la barra esta
   apagada, y ademas su relleno es cero por diseño. */
body:has(.zbaja) .esc:not(.esc--calle) {
  padding-bottom: calc(clamp(24px, 4vh, 52px) + var(--zbajah, 0px));
}
.zbaja.is-ahi { transform: none; }
@media (prefers-reduced-transparency: reduce) {
  .zbaja { background: var(--surface); backdrop-filter: none; -webkit-backdrop-filter: none; }
}
@media (prefers-reduced-motion: reduce) { .zbaja { transition: none; } }

/* EL AVANCE. Se anima con la linea de tiempo del scroll del documento, que la
   resuelve el navegador sin un solo listener. Donde no exista, no hay linea:
   nace en `scaleX(0)` y solo el `@supports` la enciende. Es informacion de
   apoyo, asi que perderla no rompe nada. */
.zbaja__prog {
  position: absolute; left: 0; right: 0; top: -1px; height: 2px;
  background: var(--teal); transform: scaleX(0); transform-origin: 0 50%;
}
@supports (animation-timeline: scroll()) {
  @media (prefers-reduced-motion: no-preference) {
    .zbaja__prog {
      animation: zbajaProg linear;
      animation-timeline: scroll(root block);
    }
  }
}
@keyframes zbajaProg { from { transform: scaleX(0); } to { transform: scaleX(1); } }

.zbaja__wrap {
  display: flex; align-items: center; justify-content: space-between;
  gap: clamp(12px, 2vw, 28px);
  padding-block: clamp(8px, 1.1vh, 12px);
}
.zbaja__t {
  font-size: clamp(13.5px, 1.2vw, 16px); font-weight: 700;
  letter-spacing: var(--tr-sub); color: var(--text);
}
.appdl--baja { align-items: flex-end; padding-right: 0; margin: 0; }
/* 132px Y NO 116: la imagen de la tienda es de 3:1, asi que 116 de ancho dan
   38,6px de alto y el minimo tactil del proyecto es 44. Es la unica medida de
   esta barra que no se puede elegir por estetica. */
.appdl--baja .appdl__b img { width: 132px; }

/* ==========================================================================
   EL RESPONSIVE DE /znappers VIVE ACA, PEGADO A SUS REGLAS
   ==========================================================================
   Y NO EN EL BLOQUE GRANDE DEL FINAL, a proposito: las piezas de esta pagina
   son de esta pagina y de ninguna otra, asi que quien toque una ve la otra. El
   bloque del final se reserva para lo que cruza rutas.
   Se declara DESPUES de la regla base, que es la unica forma de que pinte: las
   media queries no suman especificidad y gana la ultima del archivo. Esta hoja
   ya pago ese error dos veces.
   ========================================================================== */
@media (max-width: 1024px) {
  /* EL HERO SE APILA CON LA FOTO ARRIBA. Es la unica pieza de la pagina que
     puede convencer antes de que alguien lea una palabra, y en un telefono
     nadie llega al segundo bloque si el primero no le dio un motivo.
     `auto` para la foto y `1fr` para el texto: la foto pide su alto y el texto
     se queda con lo que sobra, en vez de repartirse la pantalla a la mitad. */
  .esc--calle { padding-bottom: clamp(20px, 3vh, 32px); }
  .calle__grid { grid-template-columns: minmax(0, 1fr); grid-template-rows: auto minmax(0, 1fr); gap: clamp(18px, 3vh, 28px); }
  .calle__foto { order: -1; height: clamp(230px, 38svh, 380px); border-radius: 0 0 clamp(18px, 3vw, 26px) clamp(18px, 3vw, 26px); }
  /* apilada la ventana es apaisada y mas baja, asi que el recorte sube: a 24
     la cabeza entra entera y el celular con el producto siguen adentro */
  .calle__img { object-position: 50% 24%; }
  .calle__grid { padding-left: 0; }
  .calle__copy { align-self: start; padding: 0 var(--gut); }
  .calle__h1 { font-size: clamp(30px, 6.4vw, 44px); }
  .calle__sub { max-width: none; }

  /* LA FICHA SIGUE EN DOS COLUMNAS HASTA 767, Y ESO SE MIDIO. Apilada desde
     1024, en una tablet apaisada (1024x768) la escena media 868 contra 768 de
     alto: el telefono y el texto uno arriba del otro necesitan un alto que una
     pantalla ancha y baja no tiene. Al lado, el alto lo fija la pieza mas alta
     de las dos y no la suma. El apilado vive en el corte de 767, mas abajo.
     Lo que si baja acá es el telefono, porque la columna se angosta. */
  .fono--solo { height: clamp(200px, 46svh, 420px); }
  /* LA ENUMERACION SE CAE, Y NO ES CONTENIDO PERDIDO: dice "ves que comercio
     es, a que distancia queda, de que campaña es y cuanto paga" JUSTO ARRIBA de
     una lista que muestra esos mismos campos con sus valores reales. En
     escritorio las dos cosas conviven; en 390px son tres renglones que empujan
     la nota honesta —"no es una tarifa"— fuera de la pantalla, y de las dos la
     que no se negocia es la nota.
     Lo que queda es la frase que remata, que es la que carga el argumento. */
  .plata__enum { display: none; }
  .plata__f { grid-template-columns: minmax(0, 1fr); gap: 3px; }
  .plata__f dd { font-size: clamp(15px, 3.6vw, 18px); }

  /* las dos columnas enfrentadas se apilan, y la de "no necesitas" queda
     abajo: es el remate del argumento, no su apertura */
  .pide__grid { grid-template-columns: minmax(0, 1fr); gap: clamp(14px, 2.4vh, 24px); }
  .pide__l li { padding: clamp(8px, 1.3vh, 13px) 0; font-size: clamp(14.5px, 3.6vw, 17px); }

  /* dos columnas de 250px no son dos columnas: son dos listas rotas */
  .preg__grid { columns: 1; }
  .preg__q { padding: clamp(9px, 1.4vh, 14px) 0; }
}

@media (max-width: 767px) {
  /* LOS CINCO TELEFONOS NO ENTRAN, Y ACHICARLOS NO ES LA SALIDA. En 390px de
     ancho cinco columnas dan 66px por telefono: a ese tamaño una captura de app
     no es una captura, es una mancha, y la fila deja de probar lo que vino a
     probar. Queda el ACTIVO, grande y legible, y la evidencia de que son cinco
     pasos pasa a los cinco botones numerados de abajo, que si entran.
     Es `display: none` directo sobre la regla, no el atributo `hidden` sobre un
     elemento que ya trae `display`: son dos mecanismos y mezclarlos es el error
     que esta hoja ya pago dos veces. */
  /* LA PRUEBA VA PRIMERO: apilada, la captura tiene que quedar ARRIBA del
     texto, porque es lo que sostiene todo lo que el texto afirma, y en un
     telefono nadie llega al segundo bloque si el primero no le dio un motivo. */
  .plata__grid { grid-template-columns: minmax(0, 1fr); gap: clamp(10px, 1.6vh, 18px); }
  /* LAS TRES FILAS DE LA FICHA SE APRIETAN. En 360x740 median 261px de los 740
     de pantalla, y de eso 66 eran relleno: en una fila de dos renglones el aire
     de arriba y abajo hace menos por separarlas que la propia linea. */
  .plata__f { padding: 6px 0; }
  .plata__nota { font-size: 13px; line-height: 1.5; margin-top: 12px; }
  /* MEDIDO EN 390x844: con el telefono en 30svh la escena media 932 contra 844
     de pantalla, o sea que la nota honesta —"no es una tarifa"— caia afuera. Se
     cede alto del telefono, que sigue siendo legible, y no la nota. */
  /* EN TELEFONO EL APARATO SE ABRE EN LENTE, Y ES LA CORRECCION MAS IMPORTANTE
     DE ESTE CORTE. Con la proporcion del telefono entera, en 390px el aparato
     media 150x69: la ficha —que es LA PRUEBA de toda la escena— quedaba en
     treinta pixeles de alto y no se leia una palabra. Se veia un mapa.
     Se conserva el marco, que es lo que dice "esto es la pantalla de la app", y
     se cambia la VENTANA: el recorte se corre a la ficha con `object-position`,
     asi que lo que ocupa el ancho de la columna es la tarjeta con el comercio,
     la distancia y la recompensa, que es lo unico que esta escena necesita
     mostrar. El 65,5% sale de la cuenta: la tarjeta vive entre el 50% y el 74%
     del alto de la captura, y una ventana de 320 sobre 1280 la cubre entera.
     Y DEJA DE FLOTAR: una banda ancha que sube y baja no se lee como un objeto
     apoyado, se lee como algo suelto. */
  .fono--solo {
    order: -1;
    aspect-ratio: 589 / 320; width: 100%; height: auto;
    animation: none;
  }
  .fono--solo .fono__pant { object-position: 50% 66%; }
  .zn__pill--xl { font-size: clamp(26px, 8vw, 40px); }

  .paso__fila { grid-template-columns: minmax(0, 1fr); padding-top: 0; }
  .fono--fila { display: none; }
  .paso__ui:has(#pn1.is-active) .fono--fila[data-paso="1"],
  .paso__ui:has(#pn2.is-active) .fono--fila[data-paso="2"],
  .paso__ui:has(#pn3.is-active) .fono--fila[data-paso="3"],
  .paso__ui:has(#pn4.is-active) .fono--fila[data-paso="4"],
  .paso__ui:has(#pn5.is-active) .fono--fila[data-paso="5"] {
    display: block; height: clamp(210px, 34svh, 330px); transform: none;
  }
  /* el verbo se cae y quedan los numerales: cinco palabras en 66px se parten
     en dos renglones cada una y la fila mide mas que el telefono */
  .paso__bt { display: none; }

  /* LAS CINCO PREGUNTAS EN UNA COLUMNA MEDIAN 894 CONTRA 844 DE PANTALLA, y una
     escena que no entra en la pantalla es el unico error grave de este sistema.
     Se aprieta el interlineado y el relleno de cada una: son respuestas de dos
     renglones, no un articulo. */
  .preg__q { padding: 6px 0; }
  .preg__q p { font-size: 13.5px; line-height: 1.5; margin-top: 4px; }
  .preg__grid { margin-top: clamp(14px, 2.2vh, 26px); }
  .preg__more { margin-top: 12px; }
  .paso__b { padding-inline: 0; padding-block: 13px; }
  .paso__bn { font-size: 12px; }
  .paso__stage { min-height: clamp(92px, 14vh, 120px); }

  /* LAS TRES CIFRAS SE PARAN EN FILA, NO EN COLUMNA. En 360px, tres columnas
     dejan 100px por cifra y "Znappers activos en el pais" cae en cuatro
     renglones. Apiladas con el numero al lado del rotulo, cada una es una linea
     que se lee de un vistazo. */
  .somos__cifras { grid-template-columns: minmax(0, 1fr); gap: 0; }
  .somos__cifras li {
    display: grid; grid-template-columns: auto minmax(0, 1fr);
    align-items: baseline; gap: 14px;
    padding: clamp(8px, 1.4vh, 13px) 0; border-bottom: 1px solid var(--line-2);
  }
  .somos__cifras li:last-child { border-bottom: 0; }
  .somos__n { font-size: clamp(26px, 8vw, 34px); }
  .somos__l { margin-top: 0; max-width: none; }
  .somos__riel img { height: clamp(150px, 22svh, 210px); }

  /* LA BARRA DE DESCARGA SE PARTE EN DOS RENGLONES. En 390px, la frase y los
     dos botones en una fila dejan 90px para la frase y la parten en cuatro. */
  /* LA FRASE SE CAE Y QUEDAN LAS DOS TIENDAS. En 390px no entra en la misma
     fila que los botones, y partida en dos renglones la barra pasa de 62 a 96px
     de alto: casi un octavo de la pantalla, permanente, en la pagina donde cada
     escena tiene que entrar entera. Dos insignias de tienda en una barra fija
     son un patron que no necesita que se lo expliquen; el alto, si. */
  .zbaja__t { display: none; }
  .zbaja__wrap { justify-content: center; padding-block: 9px; }
}

/* ==========================================================================
   EL BLOG: LA DESTACADA Y EL ARCHIVO
   ==========================================================================
   QUE HABIA ANTES Y POR QUE SE TIRO. Un indice de seis filas numeradas: numero,
   rotulo de categoria, titulo, flecha. Sin foto (aparecia solo al apuntar la
   fila, o sea nunca en pantalla quieta y nunca en telefono), sin bajada, sin
   jerarquia entre las seis. El cliente lo llamo "viejo, feo, desactualizado" y
   la descripcion es exacta: se leia como el indice de un PDF.

   El argumento con el que se defendia era este: de las seis notas UNA SOLA
   tenia bajada, asi que una grilla con cinco huecos se veia rota. El argumento
   era bueno y ya no vale: ahora las seis tienen bajada, escritas describiendo
   el ALCANCE del titulo, sin una cifra, sin fecha y sin autor, porque nada de
   eso existe documentado.

   EL CRITERIO DE PROTAGONISMO, DECLARADO, que es obligatorio para toda pieza
   nueva (ESTADO.md seccion 1). La ruta tiene tres pantallas y un protagonista
   en cada una:

     pantalla    protagonista            soporte                contexto
     ---------   ---------------------   --------------------   ------------------
     entrada     el titular              la bajada              el rotulo del blog
     destacada   la foto y su titulo     categoria y bajada     minutos de lectura
     archivo     las cinco fichas        el filtro              el contador y las
                                                                lineas divisorias

   La destacada es la UNICA nota que puede ser protagonista y no es una
   decision estetica: es la unica que tiene cuerpo escrito. Por eso es la unica
   que recibe foto grande, marco de anotacion y minutos de lectura. Darle lo
   mismo a las otras cinco seria prometer lo que no hay.

   EL RITMO TONAL, sin estrenar un solo color: la entrada y la destacada sobre
   `--paper`, el archivo sobre `--surface` a sangre. El corte de tono es lo que
   separa "la nota que elegimos" de "todo lo demas", y hace el trabajo que en
   la version vieja no hacia nadie.
   ========================================================================== */

/* --- LA ENTRADA ---
   Ya no usa `.ayhero`, que es el hero de /ayuda: dos rutas distintas abrian
   con la misma pieza, que es literalmente lo que el cliente viene pidiendo que
   deje de pasar. Una sola columna, con su rotulo propio arriba. */
.blog__head {
  padding-top: clamp(46px, 5.4vw, 76px);
  padding-bottom: clamp(30px, 3.4vw, 46px);
}
.blog__flag {
  display: flex; align-items: center; gap: 10px;
  font-family: var(--mono); font-size: 11.5px; font-weight: 500;
  letter-spacing: .12em; text-transform: uppercase; color: var(--text-2);
}
/* el unico teal de la pantalla de entrada: una raya corta, no un campo */
.blog__flag::before {
  content: ""; flex: none; width: 24px; height: 2px;
  border-radius: 999px; background: var(--teal);
}
.blog__h { margin-top: clamp(14px, 1.6vw, 20px); max-width: 14ch; }
.blog__head .lead { margin-top: clamp(18px, 2vw, 24px); max-width: 60ch; }

/* ==========================================================================
   LA NOTA DESTACADA
   ==========================================================================
   Foto a la izquierda, texto a la derecha, y la foto pesa mas que el texto
   (1.08fr contra .92fr) porque el protagonista de esta pantalla es la
   evidencia, no el titular: es un blog sobre leer fotos de gondola.

   `[hidden]` CON SU PROPIA REGLA. La seccion la apaga el filtro poniendole el
   atributo, y en este proyecto un `display` de autor le gana al atributo
   `hidden` cuatro veces ya (`.mnav`, `.board__panel`, `.det__panel`, `.cad__p`).
   Se escribe aunque hoy `.dest` no declare `display`: el dia que declare uno,
   la nota destacada quedaria visible con el filtro puesto en otra categoria.
   ========================================================================== */
.dest { padding-bottom: clamp(48px, 5.5vw, 80px); }
.dest[hidden] { display: none; }
.dest__a {
  display: grid;
  grid-template-columns: minmax(0, 1.08fr) minmax(0, .92fr);
  gap: clamp(26px, 3.2vw, 52px);
  align-items: center;
}
/* LA CAJA QUE RECORTA es `.dest__ph`, y por eso el marco va ADENTRO. Si el
   marco cayera afuera de la foto, `overflow: hidden` lo cortaria; y sin
   `overflow: hidden` la foto se sale de sus esquinas redondeadas al escalar. */
.dest__ph {
  position: relative; display: block;
  border-radius: var(--r-media); overflow: hidden;
  border: 1px solid var(--line); background: var(--surface-2);
}
/* 4/3 Y NO 16/10, medido en pantalla: con la foto apaisada el bloque de texto
   de al lado quedaba 100px mas alto que ella y la pieza se leia descentrada.
   Es la unica proporcion de foto del sitio que no es panoramica, y la razon es
   esa: aca la foto convive con una columna de texto, no la corona. */
.dest__img {
  display: block; width: 100%; aspect-ratio: 4 / 3; object-fit: cover;
  transition: transform .6s var(--ease-io);
}
/* EL MOTIVO DE LA CASA, EN SU LUGAR NATURAL: las dos escuadras que en el resto
   del sitio marcan "esto se esta midiendo", puestas sobre la unica foto de la
   pagina que es protagonista. No es adorno importado: la nota trata
   exactamente de eso.
   La sombra existe porque un trazo teal de 2px sobre una foto clara se pierde;
   sobre el papel no haria falta. */
.dest__marco {
  position: absolute; inset: clamp(12px, 1.3vw, 18px);
  pointer-events: none;
  filter: drop-shadow(0 1px 2px rgba(22,19,15,.45));
  transition: transform .42s var(--ease-out);
}
/* LAS DOS ESCUADRAS VAN EN LA DIAGONAL ARRIBA-DERECHA / ABAJO-IZQUIERDA, y no
   en la de siempre: la esquina de arriba a la izquierda ya esta ocupada por la
   pastilla "Nota destacada" y en la primera captura las dos piezas quedaron una
   encima de la otra. Se ve en `blog-1.png`. */
.dest__marco::before, .dest__marco::after {
  content: ""; position: absolute;
  width: clamp(24px, 2.2vw, 34px); height: clamp(24px, 2.2vw, 34px);
  border: 2.5px solid var(--teal);
}
.dest__marco::before {
  top: 0; right: 0; border-left: 0; border-bottom: 0;
  border-radius: 0 var(--r-anot) 0 0;
}
.dest__marco::after {
  bottom: 0; left: 0; border-right: 0; border-top: 0;
  border-radius: 0 0 0 var(--r-anot);
}
/* campo de marca con letra blanca: la decision explicita del cliente, la misma
   que llevan los botones primarios (ver seccion 2, --on-teal) */
.dest__flag {
  position: absolute; top: clamp(12px, 1.3vw, 18px); left: clamp(12px, 1.3vw, 18px);
  padding: 7px 12px 6px;
  border-radius: var(--r-anot);
  background: var(--teal-field); color: var(--on-teal);
  font-family: var(--mono); font-size: 10.5px; font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase;
  box-shadow: 0 1px 2px rgba(14,154,137,.34);
}

.dest__txt { display: block; }
.dest__k {
  display: flex; align-items: center; gap: 9px;
  font-family: var(--mono); font-size: 11.5px; font-weight: 500;
  letter-spacing: .1em; text-transform: uppercase; color: var(--text-2);
}
.dest__k::before {
  content: ""; flex: none; width: 9px; height: 9px;
  border-top: 1.5px solid var(--teal); border-left: 1.5px solid var(--teal);
}
/* EL SUBRAYADO SE DIBUJA CON `text-decoration`, no con un `::after`: sigue el
   contorno de la ultima linea del titulo en vez de cruzar la caja entera, que
   es lo que se ve mal cuando el titulo ocupa dos lineas y media. Arranca en
   teal con alfa 0 y no en `transparent`: la regla 7 de la casa. */
/* SIN TOPE DE MEDIDA, y es a proposito: la columna ya lo pone. Con `17ch` el
   titulo salia en CINCO lineas dentro de una columna que daba para tres, que
   es la forma de que un titular se lea apretado sin que ninguna medida este
   mal. Lo mide la grilla, no un `max-width` de mas. */
.dest__t {
  margin-top: 14px;
  font-size: clamp(24px, 2.6vw, 36px); font-weight: 700;
  line-height: 1.1; letter-spacing: var(--tr-title); color: var(--text);
  text-wrap: balance;
  text-decoration: underline;
  text-decoration-color: rgba(19,193,172,0);
  text-decoration-thickness: 2px;
  text-underline-offset: 6px;
  transition: text-decoration-color .3s var(--ease-out);
}
.dest__d {
  display: block; margin-top: 16px; max-width: 44ch;
  font-size: clamp(15.5px, 1.35vw, 17.5px); line-height: 1.55; color: var(--text-2);
}
/* NIVEL 3, CONTEXTO. Los minutos son el unico dato duro de la pieza y estan
   medidos, no estimados: el cuerpo de la nota tiene 409 palabras, que a 200
   por minuto son 2 minutos. Hasta hoy la pagina decia 7. */
.dest__meta {
  display: block; margin-top: clamp(18px, 2vw, 24px); padding-top: 14px;
  max-width: 44ch;
  border-top: 1px solid var(--line);
  font-family: var(--mono); font-size: 12px; letter-spacing: .04em;
  color: var(--text-2);
}
.dest__go {
  display: inline-flex; align-items: center; gap: 9px;
  margin-top: clamp(16px, 1.8vw, 22px);
  font-size: 15.5px; font-weight: 600; letter-spacing: var(--tr-body);
  color: var(--text);
}
.dest__go svg { color: var(--teal); transition: transform .26s var(--ease-out); }

/* ==========================================================================
   EL ARCHIVO Y SU FILTRO
   ==========================================================================
   LAS CATEGORIAS YA ESTABAN ESCRITAS EN EL MARCADO Y NO HACIAN NADA. Ahora
   filtran, y filtran tambien a la destacada: es una nota mas de la coleccion,
   no un banner aparte. Filtrar por "Shelf analytics" deja la destacada sola en
   pantalla, que es la respuesta correcta.

   EL FILTRO NACE `hidden` EN EL HTML Y LO ENCIENDE EL SCRIPT. Con el JS caido
   no quedan siete botones que no filtran nada: queda la lista entera, que es
   el estado correcto de la pagina. Es la misma regla que la marca `.js` del
   <head>, y por eso `.flt` lleva su propia regla `[hidden]`: declara
   `display: flex`, que le gana al atributo.
   ========================================================================== */
.arch {
  background: var(--surface);
  padding-top: clamp(44px, 5vw, 72px);
  padding-bottom: var(--sec-pb);
}
/* DOS RENGLONES, NO UNO. El primer intento puso el titulo, los siete botones y
   el contador en una sola linea: a 1040px de contenedor quedaban siete
   pastillas apretadas contra el borde y el contador aplastado en la punta. El
   rotulo manda arriba y el riel de categorias ocupa su propio ancho completo,
   con el contador como remate a la derecha. */
.arch__bar {
  display: grid; gap: clamp(14px, 1.6vw, 20px);
  padding-bottom: clamp(14px, 1.6vw, 20px);
  border-bottom: 1px solid var(--text);
}
.arch__h {
  font-size: clamp(19px, 1.7vw, 23px); font-weight: 700;
  letter-spacing: var(--tr-sub); color: var(--text);
}

.flt {
  display: flex; flex-wrap: wrap; align-items: center;
  justify-content: space-between; gap: clamp(12px, 1.4vw, 20px);
}
.flt[hidden] { display: none; }
.flt__l { display: flex; flex-wrap: wrap; gap: 8px; }
.flt__b {
  height: 38px; padding: 0 16px;
  border-radius: var(--r-pill);
  border: 1px solid var(--line-2); background: var(--paper);
  font-size: 14px; font-weight: 600; letter-spacing: var(--tr-micro);
  color: var(--text-2); white-space: nowrap; cursor: pointer;
  transition: background-color .2s var(--ease), border-color .2s var(--ease),
              color .2s var(--ease);
}
.flt__b.is-on {
  background: var(--teal-field); border-color: var(--teal-field);
  color: var(--on-teal); font-weight: 700;
}
/* NIVEL 3: dice cuantas quedan y nada mas. Lleva `aria-live` en el HTML,
   porque un filtro que cambia la lista sin anunciar el resultado deja al
   lector de pantalla sin saber si paso algo. */
.flt__n {
  font-family: var(--mono); font-size: 11.5px; letter-spacing: .04em;
  color: var(--text-2); white-space: nowrap;
}

.arch__l {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(14px, 1.6vw, 20px);
  margin-top: clamp(22px, 2.6vw, 34px);
}
.arch__i { display: block; }
.arch__i[hidden] { display: none; }
/* La ficha es una tarjeta HORIZONTAL, y esa es la diferencia con la grilla de
   tarjetas verticales del pie de la nota: aca lo que se compara son titulos, y
   una fila los pone uno debajo del otro a la misma altura de ojo.
   `height: 100%` para que dos fichas de la misma fila midan igual aunque una
   tenga la bajada mas larga. */
.arch__a {
  display: grid; grid-template-columns: auto minmax(0, 1fr);
  gap: clamp(14px, 1.5vw, 20px); align-items: start;
  height: 100%;
  padding: clamp(16px, 1.7vw, 22px);
  background: var(--paper);
  border: 1px solid var(--line); border-radius: var(--r-media);
  transition: transform .24s var(--ease), border-color .24s var(--ease),
              box-shadow .24s var(--ease);
}
.arch__ph {
  display: block; flex: none;
  width: clamp(88px, 8.4vw, 112px); aspect-ratio: 1 / 1;
  border-radius: var(--r-input); overflow: hidden;
  background: var(--surface-2);
}
.arch__img {
  display: block; width: 100%; height: 100%; object-fit: cover;
  transition: transform .55s var(--ease-io);
}
.arch__mid { min-width: 0; }
.arch__k {
  display: flex; align-items: center; gap: 8px;
  font-family: var(--mono); font-size: 10.5px; font-weight: 500;
  letter-spacing: .1em; text-transform: uppercase; color: var(--text-2);
}
.arch__k::before {
  content: ""; flex: none; width: 8px; height: 8px;
  border-top: 1.5px solid var(--teal); border-left: 1.5px solid var(--teal);
}
.arch__t {
  display: block; margin-top: 9px;
  font-size: clamp(17px, 1.5vw, 20px); font-weight: 700;
  line-height: 1.2; letter-spacing: var(--tr-sub); color: var(--text);
  text-decoration: underline;
  text-decoration-color: rgba(19,193,172,0);
  text-decoration-thickness: 2px;
  text-underline-offset: 4px;
  transition: text-decoration-color .26s var(--ease-out);
}
.arch__d {
  display: block; margin-top: 9px;
  font-size: 14.5px; line-height: 1.5; color: var(--text-2);
}

/* LA ENTRADA ESCALONADA. Cinco tiempos de 60 ms: la cascada se lee como una
   sola cosa que se arma, no como cinco animaciones. Con movimiento reducido
   `--mv` vale 0 y sobreviven la opacidad y el orden, que es justo lo que la
   regla de la casa pide conservar. */
.arch__i:nth-child(2).reveal { transition-delay: .06s; }
.arch__i:nth-child(3).reveal { transition-delay: .12s; }
.arch__i:nth-child(4).reveal { transition-delay: .18s; }
.arch__i:nth-child(5).reveal { transition-delay: .24s; }

/* LO QUE DICE "ESTO SE PUEDE ABRIR", y nada mas que eso. Va en un bloque
   `hover: hover` para que en tactil no quede un estado pegado despues del
   toque. Cada distancia pasa por `--mv`, asi que con movimiento reducido
   quedan el color y el subrayado y se va el desplazamiento. */
@media (hover: hover) {
  .dest__a:hover .dest__img { transform: scale(calc(1 + .025 * var(--mv))); }
  .dest__a:hover .dest__marco { transform: scale(calc(1 + .02 * var(--mv))); }
  .dest__a:hover .dest__t,
  .dest__a:focus-visible .dest__t { text-decoration-color: var(--teal); }
  .dest__a:hover .dest__go svg { transform: translateX(calc(var(--mv) * 4px)); }

  .arch__a:hover {
    transform: translateY(calc(var(--mv) * -3px));
    border-color: var(--line-2); box-shadow: var(--sh-2);
  }
  .arch__a:hover .arch__img { transform: scale(calc(1 + .05 * var(--mv))); }
  .arch__a:hover .arch__t,
  .arch__a:focus-visible .arch__t { text-decoration-color: var(--teal); }

  .flt__b:hover { border-color: var(--text); color: var(--text); }
  .flt__b.is-on:hover {
    background: var(--teal-hi); border-color: var(--teal-hi); color: var(--on-teal);
  }
}

/* Abajo de 1024px el contenedor mide 576px: dos columnas de ficha dejarian el
   titulo en cinco lineas, y la destacada al lado de su foto no entra. Las dos
   piezas pasan a una sola columna. */
@media (max-width: 1024px) {
  .dest__a { grid-template-columns: minmax(0, 1fr); gap: clamp(20px, 3vw, 28px); }
  .dest__t { max-width: none; }
  .dest__d, .dest__meta { max-width: none; }
  .arch__l { grid-template-columns: minmax(0, 1fr); }
  .arch__bar { align-items: flex-start; }
}
@media (max-width: 767px) {
  .arch__ph { width: 84px; }
  .arch__d { font-size: 14px; }
  .blog__h { max-width: none; }
}

/* ---------- EL PIE DE LA NOTA: LAS RELACIONADAS ----------
   La unica grilla de tarjetas verticales que queda, y queda a proposito: es el
   final de una lectura, no un indice, asi que se mira de un vistazo y no se
   compara titulo contra titulo. Comparte con el archivo el rotulo de categoria
   en mono con su escuadra, el mismo tratamiento de titulo y el mismo subrayado
   al apuntar: lo que cambia es la forma, no el lenguaje. */
.posts__grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: clamp(20px, 2.4vw, 34px); }
.post__a {
  display: flex; flex-direction: column; height: 100%;
  padding: 0 0 20px;
  border-bottom: 1px solid var(--line);
  transition: transform .24s var(--ease);
}
.post__ph {
  position: relative;
  border-radius: var(--r-media); overflow: hidden;
  border: 1px solid var(--line); background: var(--surface-2);
  aspect-ratio: 16 / 10; margin-bottom: 18px;
}
.post__ph img { width: 100%; height: 100%; object-fit: cover; transition: transform .5s var(--ease-io); }
.post__k {
  display: inline-flex; align-items: center; gap: 8px;
  font-family: var(--mono); font-size: 10.5px; font-weight: 500;
  letter-spacing: .1em; text-transform: uppercase;
  color: var(--text-2);
}
.post__k::before {
  content: ""; flex: none; width: 8px; height: 8px;
  border-top: 1.5px solid var(--teal); border-left: 1.5px solid var(--teal);
}
.post__t {
  margin-top: 11px;
  font-size: clamp(17px, 1.5vw, 20px); font-weight: 700;
  line-height: 1.2; letter-spacing: var(--tr-sub); color: var(--text);
  text-decoration: underline;
  text-decoration-color: rgba(19,193,172,0);
  text-decoration-thickness: 2px;
  text-underline-offset: 4px;
  transition: text-decoration-color .26s var(--ease-out);
}
.post__go {
  margin-top: auto; padding-top: 18px;
  display: inline-flex; align-items: center; gap: 8px;
  font-size: 14px; font-weight: 600; color: var(--text);
}
.post__go::after {
  content: ""; width: 7px; height: 7px;
  border-top: 1.5px solid var(--teal); border-right: 1.5px solid var(--teal);
  transform: rotate(45deg);
  transition: transform .22s var(--ease);
}
@media (hover: hover) {
  .post__a:hover .post__t, .post__a:focus-visible .post__t { text-decoration-color: var(--teal); }
}

/* ---------- LA NOTA ---------- */
.art { padding-top: clamp(34px, 4vw, 60px); }
.art__head { max-width: 78ch; }
.art__back { margin-bottom: clamp(22px, 2.4vw, 32px); }
/* `flex` Y NO `inline-flex`, y esto era un bug de verdad: siendo de nivel
   linea, el rotulo de categoria se pegaba a la DERECHA de "Volver al blog",
   en la misma linea, y el `margin-bottom` del enlace no lo separaba de nada.
   Medido: el enlace terminaba en x=496 y el rotulo empezaba en x=499.
   Y pasa a MONO, que es el mismo rotulo de categoria que usan el indice del
   blog y las notas relacionadas: la nota y su indice tienen que hablar igual. */
.art__k {
  display: flex; align-items: center; gap: 9px;
  font-family: var(--mono); font-size: 11.5px; font-weight: 500;
  letter-spacing: .1em; text-transform: uppercase;
  color: var(--text-2);
}
.art__k::before {
  content: ""; flex: none; width: 9px; height: 9px;
  border-top: 1.5px solid var(--teal); border-left: 1.5px solid var(--teal);
}
.art__t {
  margin-top: 16px;
  font-size: clamp(30px, 3.7vw, 52px); font-weight: 700;
  line-height: 1.06; letter-spacing: var(--tr-display); color: var(--text);
}
.art__sub { margin-top: 18px; font-size: clamp(17px, 1.5vw, 20px); line-height: 1.5; color: var(--text-2); max-width: 54ch; }
.art__meta {
  margin-top: 22px; padding-top: 16px;
  border-top: 1px solid var(--line);
  display: flex; flex-wrap: wrap; gap: 0; align-items: center;
  /* mono, igual que `.dest__meta` del indice: los dos dicen el mismo dato */
  font-family: var(--mono); font-size: 12px; letter-spacing: .04em;
  color: var(--text-2);
}
/* v28: el separador vale entre CUALQUIER par de hijos, no solo entre dos
   <span>. La fecha de la nota entra como <time> -que es la etiqueta
   correcta y la que leen los buscadores-, y con el selector viejo
   (span + span) ninguno de los dos puntos teal se dibujaba: ni antes
   del <time> ni despues. */
.art__meta > * + *::before {
  content: ""; display: inline-block;
  width: 4px; height: 4px; border-radius: 50%; background: var(--teal);
  margin: 0 12px; vertical-align: middle;
}
.art__hero {
  position: relative;
  margin: clamp(28px, 3.4vw, 48px) auto clamp(30px, 3.6vw, 52px);
  width: 100%; max-width: calc(var(--wrap) - 2 * var(--gut));
  border-radius: var(--r-media); overflow: hidden;
  border: 1px solid var(--line);
  aspect-ratio: 16 / 8;
}
.art__hero img { width: 100%; height: 100%; object-fit: cover; }

/* LA NOTA CON SU INDICE.
   El indice va a la IZQUIERDA y el cuerpo conserva su medida de lectura, que
   es lo unico que de verdad importa en una nota. La columna del indice es fija
   y angosta: si creciera, se comeria la medida del texto, que es justo lo que
   no se puede tocar. */
.art__grid {
  display: grid;
  grid-template-columns: 172px minmax(0, 1fr);
  gap: clamp(28px, 4vw, 68px);
  align-items: start;
}
.art__idx { position: sticky; top: 104px; }   /* 104px = la barra + aire */
.art__idxh {
  font-family: var(--mono); font-size: 11.5px; font-weight: 700;
  letter-spacing: .1em; text-transform: uppercase; color: var(--text-2);
  padding-bottom: 12px; border-bottom: 1px solid var(--line);
}
.art__idxl { display: grid; counter-reset: idx; }
.art__idxl a {
  position: relative; display: block; padding: 11px 0 11px 20px;
  font-size: 13.5px; line-height: 1.35; letter-spacing: var(--tr-micro);
  color: var(--text-2); text-decoration: none;
  transition: color 180ms var(--ease-out);
}
/* la marca de la seccion en curso: una barra en el canal de la izquierda, que
   es el mismo lugar donde el resto del sitio pone la escuadra de anotacion */
.art__idxl a::before {
  content: ''; position: absolute; left: 0; top: 11px; bottom: 11px; width: 2px;
  background: var(--teal);
  transform: scaleY(0); transform-origin: 50% 0;
  transition: transform 220ms var(--ease-out);
}
.art__idxl a:hover { color: var(--text); }
.art__idxl a.is-here { color: var(--text); font-weight: 700; }
.art__idxl a.is-here::before { transform: scaleY(1); }

.art__body { max-width: calc(68ch + 2 * var(--gut)); }
.art__lead {
  font-size: clamp(19px, 1.7vw, 23px); line-height: 1.45; font-weight: 500;
  letter-spacing: var(--tr-sub); color: var(--text);
  padding-bottom: clamp(22px, 2.4vw, 32px);
  border-bottom: 1px solid var(--line);
}
.art__body h2 {
  margin-top: clamp(34px, 3.6vw, 50px);
  font-size: clamp(22px, 2vw, 28px); font-weight: 700;
  line-height: 1.18; letter-spacing: var(--tr-title); color: var(--text);
}
.art__body p { margin-top: 16px; font-size: 17px; line-height: 1.65; color: var(--text-2); }
/* EL <picture> NO TIENE QUE EXISTIR PARA EL LAYOUT — v28.
   `construir-deploy.py` envuelve cada <img> de foto en un <picture> para
   ofrecer AVIF, y eso mete un elemento NUEVO entre el contenedor y la imagen.
   Donde el contenedor es grid o flex, el hijo pasaria a ser el <picture> y la
   imagen dejaria de recibir las reglas que la posicionan.
   `display: contents` lo saca del arbol de cajas: el <img> vuelve a ser hijo
   directo de lo que lo contenia, exactamente como antes de envolverlo.
   Se comprobo que ninguna regla de esta hoja usa `> img` ni `img:first-child`,
   asi que esta linea alcanza para que la envoltura sea invisible. */
picture { display: contents; }

.art__body p b { color: var(--text); font-weight: 600; }

/* ---------- v28: EL CUERPO DE NOTA APRENDE A SER UN DOCUMENTO ----------
   Hasta la v27 `.art__body` solo sabia `h2` y `p`, porque la unica nota que
   existia era una nota de blog de cuatro secciones y prosa corrida. Con los
   legales y los instructivos entra otra clase de texto: subsecciones (1.1,
   1.2) y listas de clausulas. Faltaban las dos piezas.

   Y LAS VIÑETAS HAY QUE REPONERLAS A MANO. La linea 614 apaga `list-style` y
   el `padding` de TODA lista del sitio, porque en una interfaz las listas son
   estructura y no enumeraciones. En un documento legal son exactamente lo
   contrario: si no se ve donde empieza cada item, no se puede citar una
   clausula. Por eso esto no es un `list-style` de mas, es la excepcion. */
.art__body h3 {
  margin-top: clamp(26px, 2.6vw, 34px);
  font-size: clamp(17px, 1.4vw, 19px); font-weight: 700;
  line-height: 1.3; letter-spacing: var(--tr-sub); color: var(--text);
}
/* LA VIÑETA ES LA ESCUADRA DEL MOTIVO, NO UN DISCO. Ya existe asi en
   `.aylist` (linea ~4160), que es la lista de las respuestas de /ayuda: si el
   cuerpo de nota inventara un `list-style: disc` habria dos idiomas de lista
   en el mismo sitio. Es la misma pieza a la escala del articulo.
   EL `:not([class])` NO ES DEFENSIVO, ES EL CONTRATO: las listas que ya
   traen clase propia -`.aylist`, `.aysteps`- se estilan solas y esta regla,
   que vive 4.700 lineas mas abajo, les ganaria el tamaño y les agregaria una
   sangria encima de la suya. Lista sin clase, la estiliza el cuerpo; lista
   con clase, manda su clase. */
.art__body ul:not([class]), .art__body ol:not([class]) {
  margin-top: 16px; display: grid; gap: 9px;
  color: var(--text-2); font-size: 17px; line-height: 1.65;
}
.art__body ul:not([class]) > li, .art__body ol:not([class]) > li {
  position: relative; padding-left: 24px;
}
.art__body ul:not([class]) > li::before {
  content: ""; position: absolute; left: 0; top: .58em;
  width: 8px; height: 8px;
  border-top: 1.5px solid var(--teal); border-left: 1.5px solid var(--teal);
}
/* el orden importa en un legal: la clausula se cita por su numero */
.art__body ol:not([class]) { counter-reset: cla; }
.art__body ol:not([class]) > li { counter-increment: cla; padding-left: 30px; }
.art__body ol:not([class]) > li::before {
  content: counter(cla) ".";
  position: absolute; left: 0; top: 0;
  font-family: var(--mono); font-size: 13px; line-height: 2.15;
  font-weight: 500; color: var(--text-3);
}
.art__body li b { color: var(--text); font-weight: 600; }
/* un <p> adentro de un <li> no abre parrafo nuevo: es el mismo item */
.art__body li > p:first-child { margin-top: 0; }
.art__body li > p { font-size: inherit; line-height: inherit; }
.art__body li ul, .art__body li ol { margin-top: 9px; }
/* la cita es la filosofia del documento canonico, y se trata como tal: campo
   propio, escuadra del motivo, y la tipografia sube un escalon */
.art__q {
  position: relative;
  margin: clamp(30px, 3.4vw, 46px) 0 0;
  padding: 26px 26px 28px 30px;
  background: var(--surface); border-radius: var(--r-surface);
  border-left: 2px solid var(--teal);
}
.art__q p {
  margin: 0;
  font-size: clamp(18px, 1.6vw, 21px); line-height: 1.45; font-weight: 500;
  letter-spacing: var(--tr-sub); color: var(--text);
}
.art__more { margin-top: clamp(48px, 5.4vw, 84px); }
.art__more .h2 { margin-bottom: clamp(24px, 2.6vw, 34px); }
.posts__grid--rel { grid-template-columns: repeat(3, minmax(0, 1fr)); }

/* ---------- LAS DOS PAGINAS DE ERROR ---------- */
.err { padding-top: clamp(40px, 5vw, 80px); padding-bottom: clamp(56px, 6vw, 96px); position: relative; }
.err__grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 380px); gap: clamp(30px, 4vw, 72px); align-items: center; }
.err__grid--401 { grid-template-columns: minmax(0, 1fr); max-width: calc(62ch + 2 * var(--gut)); margin-inline: 0; }
/* EL NUMERAL. Sirve para reconocer de que error se trata antes de leer nada.
   Primera version: --surface-3, o sea marca de agua. El barrido lo midio en
   1.27:1 y el numero es correcto: era practicamente invisible, y una marca de
   agua que no se ve no es sutil, es un elemento que no esta. Sube a --text-3,
   que da 3.71:1 y a 84px pasa de sobra el umbral de texto grande.
   Sigue con aria-hidden porque el h1 de al lado ya dice "esta pagina no
   existe": para un lector de pantalla el numero seria repetir lo mismo en un
   idioma peor. */
.err__n {
  font-family: var(--mono); font-size: clamp(48px, 6vw, 84px); font-weight: 700;
  line-height: 1; letter-spacing: -.04em;
  color: var(--text-3);
}
.err__t { margin-top: clamp(6px, .8vw, 12px); }
.err .lead { margin-top: 18px; }
.err__l { margin-top: clamp(26px, 3vw, 38px); display: grid; }
.err__l a {
  display: flex; align-items: baseline; gap: 14px; flex-wrap: wrap;
  padding: 14px 0;
  border-bottom: 1px solid var(--line);
  transition: transform .2s var(--ease);
}
.err__l li:last-child a { border-bottom: 0; }
.err__l b { font-size: 16.5px; font-weight: 700; letter-spacing: var(--tr-sub); color: var(--text); }
.err__l span { font-size: 14.5px; color: var(--text-2); }
.err__alt { margin-top: clamp(22px, 2.4vw, 32px); display: flex; flex-wrap: wrap; gap: 12px 26px; }
.err__act { margin-top: clamp(22px, 2.4vw, 32px); max-width: 420px; }
.err__ph {
  position: relative;
  border-radius: var(--r-media); overflow: hidden;
  border: 1px solid var(--line);
  aspect-ratio: 4 / 5;
  box-shadow: var(--sh-3);
}
.err__ph img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 40%; }



/* ==========================================================================
   LA TRANSICION ENTRE PAGINAS
   ==========================================================================
   Pedido concreto: que al entrar a "Soy empresa" pase algo, y que ese algo sea
   coherente con el resto. Son dos piezas distintas y conviene no confundirlas.

   1. EL CRUCE ENTRE DOCUMENTOS. `@view-transition` hace que el navegador
      fotografie la pagina que se va y la que llega y las funda. No es un
      truco de JavaScript ni un `fade` casero: lo resuelve el compositor, no
      corre en el hilo principal, y donde no esta soportado no pasa
      absolutamente nada (el sitio navega como siempre). Por eso se puede
      poner sin red de contencion.

      EL HEADER NO SE FUNDE, SE QUEDA. Al darle un `view-transition-name`
      propio, el navegador entiende que es el MISMO elemento en las dos
      paginas y lo deja quieto mientras el cuerpo cambia. Sin esto el logo y
      la navegacion parpadean en cada clic, que es peor que no tener
      transicion: llama la atencion sobre lo unico que no cambio.

   2. LA ENTRADA DEL HERO. Las cinco piezas del hero entran escalonadas, en el
      orden en que se leen: titular, bajada, botones, mapa y ficha. El mapa
      entra antes que la ficha a proposito, porque la ficha es la RESPUESTA
      del mapa y no puede llegar primero.

      LA ENTRADA MUEVE, NO DESVANECE, Y ESTO NO ES UNA PREFERENCIA.
      La primera version animaba `opacity` de 0 a 1 con `fill-mode: both`, y
      arriba de estas mismas lineas habia un comentario afirmando que era
      seguro porque "arrancan visibles y la animacion las esconde". ERA FALSO:
      `both` incluye el relleno HACIA ATRAS, asi que durante el retardo —y
      para siempre, si la animacion nunca llega a arrancar— el elemento se
      queda en el estado inicial, o sea invisible. Se descubrio midiendo la
      pagina adentro de un iframe: las cinco piezas del hero daban
      `opacity: 0`, incluido el titular.

      Es exactamente la trampa que este proyecto ya pago tres veces (`hidden`
      contra `display`, `.reveal` sin la marca de JS): NADA QUE SEA CONTENIDO
      PUEDE DEPENDER DE QUE UNA ANIMACION CORRA PARA VERSE. Y un comentario
      que afirma una seguridad que el codigo no da es peor que el bug, porque
      hace que el proximo no lo revise.

      Por eso ahora la entrada anima SOLO `transform`. Si no corre, las piezas
      quedan 12px mas abajo de donde irian, que no lo nota nadie; si corre, se
      lee igual de bien. El desvanecido no valia ese riesgo.
   ========================================================================== */
/* v26: LA ENTRADA SE ABRE, NO SE FUNDE.
   Lo que habia era el cruce por defecto con otra duracion: un `cross-fade` de
   dos capas, o sea la pagina vieja bajando de opacidad mientras la nueva sube.
   Eso resuelve el parpadeo pero no dice NADA — pedido textual: "una animacion
   de entrada, suave y sutil, que abra todo".

   ABRIR ES UN GESTO DE ORIGEN, no un fundido. La que se va cae un poco y se
   apaga rapido; la que llega SUBE los ultimos 10px hasta su lugar mientras
   aparece, con una curva que desacelera al final (`--ease-exp`). El ojo lee un
   plano que se acomoda, que es lo mismo que hace el hero de la escena 1 con sus
   cinco piezas — o sea que la transicion entre paginas y la entrada de la
   primera pantalla ahora son EL MISMO gesto a dos escalas, y no dos ideas.

   NO SE ESCALA EL PLANO. Un `scale()` sobre la instantanea de una pagina de
   1920 la rasteriza a otro tamaño y el texto sale borroneado durante toda la
   animacion: se ve como un problema de nitidez, no como una transicion.
   Movimiento y opacidad, que es lo unico que resuelve el compositor gratis.

   LA QUE SE VA DURA MENOS QUE LA QUE LLEGA (.2 contra .42) y arranca antes: si
   las dos duran lo mismo, en el medio se ven las dos al 50% y eso es un
   fantasma, no un cruce.

   EL HEADER NO ENTRA EN NADA DE ESTO: `view-transition-name: znav` le dice al
   navegador que es el MISMO elemento en las dos paginas y lo deja quieto. */
@view-transition { navigation: auto; }
.nav { view-transition-name: znav; }
/* ============ LA INSTANTANEA NO SE PUEDE DEFORMAR, Y POR DEFECTO SE DEFORMA.
   Medido capturando el cruce cuadro a cuadro: a mitad de la transicion la
   pagina entera aparecia un 19% mas grande, centrada. No es un bug del sitio:
   es el comportamiento por defecto. El navegador le da al grupo raiz el tamaño
   de la instantanea VIEJA y lo interpola hasta el de la NUEVA, y como /home y
   /empresas no miden lo mismo de alto, las dos capas se estiran para llenar un
   marco que va cambiando. Con un `cross-fade` de 0,3 s casi no se nota; con una
   entrada de 0,42 s que ademas mueve, se ve como un zoom.
   El remedio son las tres lineas de abajo, y cada una tapa un agujero:
     - `object-fit: none` + `object-position: top left` — la instantanea se
       dibuja a su tamaño real anclada arriba a la izquierda, en vez de
       escalarse para entrar en el marco;
     - `height: 100%` sobre las dos capas, para que el marco no las recorte;
     - `isolation: auto` en el par, porque el aislamiento por defecto existe
       para que el `mix-blend-mode` del cross-fade funcione, y aca no hay
       cross-fade: hay una que se va y otra que llega. */
::view-transition-group(root) { animation-duration: .42s; }
::view-transition-image-pair(root) { isolation: auto; }
::view-transition-old(root),
::view-transition-new(root) {
  height: 100%; object-fit: none; object-position: top left;
  mix-blend-mode: normal;
}
::view-transition-old(root) { animation: vt-sale .2s var(--ease) both; }
::view-transition-new(root) { animation: vt-entra .42s var(--ease-exp) both; }
@keyframes vt-sale  { to   { opacity: 0; transform: translateY(4px); } }
@keyframes vt-entra { from { opacity: 0; transform: translateY(10px); } }

/* Y SE APAGA ENTERA CON MOVIMIENTO REDUCIDO. `animation: none` sobre los dos
   pseudo deja el cruce instantaneo, que es lo que corresponde: la transicion es
   decoracion, y la navegacion tiene que funcionar igual sin ella. */
@media (prefers-reduced-motion: reduce) {
  ::view-transition-old(root),
  ::view-transition-new(root) { animation: none; }
}


/* ==========================================================================
   /zlite v28: ZNAPP LITE, LA RUTA DE LAS TRES ESCENAS
   ==========================================================================
   POR QUE SON TRES Y NO SEIS. La fuente de contenido da un titular de tres
   verbos, tres pilares de una linea cada uno, tres sectores con un parrafo, y
   cinco preguntas SIN RESPUESTA. Con eso, una cuarta escena no se llena: se
   rellena. Y en una ruta de pantallas completas el relleno no es un parrafo de
   mas, es una pantalla entera que hay que scrollear y que no dice nada.

   LA RESTRICCION QUE ORDENO TODO EL DISEÑO: TRES PILARES Y TRES CASOS DE USO
   PIDEN SOLOS DOS FILAS DE TRES TARJETAS IGUALES. Es el patron mas templado
   que existe, y ademas seria falso dos veces. Las tres piezas del producto no
   son la misma clase de cosa (dos son pantallas y la tercera es una propiedad
   sin pantalla), y los tres sectores no se leen: se elige uno. Asi que ninguna
   de las dos secciones es una fila de tarjetas, y ninguna comparte familia de
   layout con la otra ni con las de `/empresas` y `/znappers`:

     .ciclo  TABLA TIPOGRAFICA. Tres filas con hairline a todo el ancho:
             numeral, verbo grande, complemento. Sin imagen. Es la unica
             pantalla del sitio sostenida solo por tipografia, y aca se puede
             porque no hay una sola imagen honesta de Znapp Lite: las que hay
             son del producto, no de la promesa.

     .piez   MOSAICO ASIMETRICO. Tres piezas de tamaños y formas DISTINTAS: un
             telefono alto que ocupa las dos filas de la izquierda, una captura
             apaisada que es la pieza mas grande, y un bloque de texto sobre
             campo. La unica con superficie propia es la que no tiene imagen,
             porque un texto solo al lado de dos capturas se lee como un hueco.

     .uso    SELECTOR DONDE LAS OPCIONES SON EL TITULAR. `/empresas` resuelve
             "elegir uno de N" dos veces y las dos con la eleccion en una pieza
             CHICA al lado del protagonista: pildoras de 15px arriba de un panel
             (`.cov`) e indice de 10px al lado de una lamina (`.lens`). Aca es
             al reves y por eso no se confunde con ninguna de las dos: los tres
             nombres de sector son lo mas grande de la pantalla.

   CERO JAVASCRIPT NUEVO. El selector corre con `selectores()`, el modulo
   generico que ya mueve la cadena de `/empresas` y los cinco telefonos de
   `/znappers`. Lo unico que esta hoja agrega es el dibujo del riel activo, que
   es tambien el reloj del avance automatico, igual que el subrayado de
   `.cov__opt`.

   UNA SOLA COSA SE MUEVE SOLA EN TODA LA RUTA, y es el selector: avanza y su
   riel cuenta el tiempo. El telefono NO flota como el de `/znappers`, y no es
   un olvido: en aquella ruta el telefono es el protagonista y por eso tiene
   derecho a moverse; aca el protagonista de esa pantalla es la captura del
   tablero, asi que el telefono es soporte y el soporte esta quieto.

   EL RITMO DE TONOS, tres escalones que bajan:
     ciclo   --surface     de donde se arranca
     piezas  --paper       el papel mas limpio para las capturas del producto
     usos    --surface-2   un escalon adentro para la eleccion
   ========================================================================== */

/* ==================== ESCENA 1 — EL CICLO ====================
   "CONTROLA. MEDI. MEJORA." son tres ordenes sueltas: en una sola linea las
   podria firmar cualquier software de gestion. Como tres filas de una tabla,
   cada una con su complemento, cada orden se vuelve una frase que se puede
   discutir, y las tres juntas se leen como un ciclo.

   EL VERBO Y EL COMPLEMENTO COMPARTEN LINEA DE BASE Y COLUMNA. La columna del
   verbo es `auto`, asi que la fija el mas ancho y los tres complementos
   arrancan en el mismo pixel: eso es lo que hace que se lea como una tabla y no
   como tres titulos apilados. Sin esa alineacion son tres renglones sueltos.

   LA JERARQUIA ES POR TAMAÑO Y COLOR, NUNCA POR DEFORMACION (regla 1): el
   verbo es lo mas grande de la pantalla, el complemento baja a cuerpo de
   lectura y el numeral susurra en mono. */
.esc--ciclo { background: var(--surface); }

.ciclo {
  margin: clamp(16px, 2.8vh, 32px) 0 0;
  display: grid;
  border-top: 1px solid var(--line-2);
  font-weight: 700;
  letter-spacing: var(--tr-h1);
}
.ciclo__f {
  display: grid;
  grid-template-columns: clamp(30px, 3.6vw, 58px) auto minmax(0, 1fr);
  align-items: baseline;
  gap: 0 clamp(10px, 1.4vw, 22px);
  padding: clamp(9px, 1.6vh, 19px) 0;
  border-bottom: 1px solid var(--line-2);
}
/* EL NUMERAL VA EN `--text-2`, Y NO EN `--text-4`, AUNQUE SEA NIVEL 3.
   Medido: `--text-4` (#A29B90) sobre `--surface` da 2,59:1, y la regla 5 pide
   4,5 para texto de 10px. La hoja ya tiene un caso igual sin corregir en
   `.lens__vn`, y no se replica: un numeral que no se lee no es sutil, es un
   error que nadie va a poder señalar. Lo que lo mantiene callado es el
   TAMAÑO y el interletrado, que es exactamente como resuelve `.esc__ojo`. */
.ciclo__n {
  font-family: var(--mono); font-size: clamp(10px, .8vw, 12px);
  font-weight: 600; letter-spacing: .14em; color: var(--text-2);
}
/* `min(5.4vw, 8.2vh)` y no `5.4vw` a secas: es la leccion de la v26 sobre el
   titular de escena. Con el ancho solo, una ventana ancha y BAJA da el mismo
   tamaño que una alta y la fila se come la pantalla. Mira el eje que escasea. */
.ciclo__v {
  font-size: clamp(34px, min(5.4vw, 8.2vh), 84px);
  line-height: .95; color: var(--text);
}
/* EL COMPLEMENTO SE ANCLA AL MARGEN DERECHO, Y ESO NO ES UNA PREFERENCIA.
   Alineado a la izquierda, pegado al verbo, la fila terminaba a los dos
   tercios y quedaban 470px de hairline vacia a la derecha en 1512: exactamente
   el defecto que `PENDIENTES.md` tiene abierto como "el espacio mas muerto de
   /empresas". Con los dos extremos anclados, el hueco del medio deja de ser un
   sobrante y pasa a ser lo que hace que tres renglones se lean como una tabla:
   el ojo baja por el borde izquierdo y vuelve por el derecho. */
.ciclo__g {
  justify-self: end; text-align: right;
  font-size: clamp(15px, min(1.45vw, 2.4vh), 21px);
  font-weight: 500; line-height: 1.35;
  letter-spacing: var(--tr-sub); color: var(--text-2);
}

/* LA VUELTA ES LO QUE CONVIERTE TRES ORDENES EN UN CICLO. Va en mono y chica
   porque es contexto: dice el mecanismo sin pelearle el tamaño a los verbos.
   VA EN `--text-2` Y NO EN `--text-3` aunque sea nivel 3: a 11px, `--text-3`
   mide 3,71:1 y la regla 5 pide 4,5. La jerarquia la marcan el tamaño, el peso
   y el interletrado, que es lo que ya hace `.esc__ojo` por el mismo motivo. */
.ciclo__vuelta {
  display: flex; align-items: center; gap: 8px;
  margin-top: clamp(10px, 1.4vh, 16px);
  font-family: var(--mono); font-size: clamp(10px, .85vw, 11.5px);
  font-weight: 600; letter-spacing: .14em; text-transform: uppercase;
  color: var(--text-2);
}
.ciclo__vuelta svg { flex: none; color: var(--teal); }
.ciclo__sub { margin-top: clamp(14px, 2vh, 24px); max-width: 56ch; }
.ciclo__cta { margin-top: clamp(18px, 2.6vh, 32px); }

/* ==================== ESCENA 2 — LAS PIEZAS ====================
   EL MOSAICO ES ASIMETRICO A PROPOSITO Y LA ASIMETRIA ES EL ARGUMENTO: las
   tres piezas no pesan lo mismo. La captura del tablero es la mas grande
   porque es lo que compra el que lee esta pagina (el no va a usar la app: la
   van a usar sus vendedores), el telefono la acompaña, y la adaptabilidad no
   tiene pantalla que mostrar.

   EL TELEFONO OCUPA LAS DOS FILAS DE LA IZQUIERDA. Es la unica forma de meter
   una proporcion 589x1280 al lado de una 1678x918 sin que una de las dos quede
   ridicula, y de paso es lo que impide que esto se lea como tres tarjetas.

   LAS DOS ALTURAS SALEN DE `svh` Y NO DE `100%`. Es la leccion 6 de este
   proyecto: `height: 100%` contra una pista de grilla no siempre resuelve, y
   una captura de 589x1280 en una columna de 400 mide 869px, o sea mas que la
   escena entera. Con el alto atado a la pantalla, ninguna pieza puede pasarse.
   El `<img>` de la captura si puede usar `height: 100%`, porque su figura
   tiene alto DEFINIDO. */
.esc--piezas { background: var(--paper); }

.piez__grid {
  margin-top: clamp(18px, 2.8vh, 34px);
  display: grid;
  /* .58 y no .78: la columna izquierda la mide el TELEFONO, que a 40svh mide
     unos 166px de ancho, no el texto de su leyenda. Con .78 quedaban 240px de
     papel entre el aparato y el borde de su propia columna, y como la leyenda
     si arranca en el margen, el conjunto se leia descolgado. */
  grid-template-columns: minmax(0, .58fr) minmax(0, 1.42fr);
  gap: clamp(14px, 1.8vw, 26px);
  align-items: start;
}
.piez__t { display: grid; gap: clamp(9px, 1.3vh, 15px); min-width: 0; }
.piez__t--app   { grid-column: 1; grid-row: 1 / 3; }
.piez__t--dash  { grid-column: 2; grid-row: 1; }
.piez__t--adapt { grid-column: 2; grid-row: 2; }

.fono--zl { height: clamp(210px, 40svh, 440px); justify-self: center; }

.piez__ph {
  margin: 0; overflow: hidden;
  height: clamp(140px, 25svh, 300px);
  border-radius: var(--pan-r);
  border: 1px solid var(--line);
  background: var(--surface);
  box-shadow: 0 18px 40px -26px rgba(52,49,44,.5);
}
/* `object-position: 50% 0` recorta por ABAJO. Un tablero cortado abajo se lee
   como una pantalla que sigue; cortado arriba pierde la barra que dice que es
   un tablero. */
.piez__img { display: block; width: 100%; height: 100%; object-fit: cover; object-position: 50% 0; }

.piez__k {
  font-size: clamp(15px, 1.3vw, 19px); font-weight: 700;
  letter-spacing: var(--tr-sub); color: var(--text);
}
.piez__d {
  margin-top: 5px; max-width: 36ch;
  font-size: clamp(13.5px, 1.1vw, 16px); line-height: 1.45; color: var(--text-2);
}
/* LA UNICA PIEZA CON CAMPO PROPIO ES LA QUE NO TIENE IMAGEN, y es deliberado:
   con las tres sobre el papel, la tercera se leia como el hueco que quedo
   despues de las otras dos. El campo no la asciende de nivel, la completa. */
.piez__t--adapt {
  align-content: center;
  padding: clamp(16px, 1.8vw, 26px);
  border-radius: var(--pan-r);
  background: var(--surface-2);
}
.piez__t--adapt .piez__d { max-width: 42ch; }

.piez__nota {
  margin-top: clamp(12px, 1.8vh, 20px); max-width: 78ch;
  font-size: 12.5px; line-height: 1.45; color: var(--text-2);
}

/* ==================== ESCENA 3 — LOS USOS ====================
   TRES SECTORES COMO SELECTOR Y NO COMO TARJETAS. Tres tarjetas obligan a leer
   las tres para encontrar la propia; un selector deja elegir primero y leer
   despues, que es como lee alguien que ya sabe de que sector es. Y la
   comparacion no se pierde: los tres nombres estan siempre a la vista.

   LAS OPCIONES SON EL TITULAR DE LA PANTALLA. No hay foto honesta de un
   servicio tecnico ni de un reparto, asi que el protagonista tiene que ser el
   texto que se elige. Eso ademas es lo que separa a este selector de los dos
   de `/empresas`, donde la eleccion siempre es la pieza chica.

   EL RIEL ES LA SELECCION Y ES EL RELOJ, un solo objeto. Es la leccion de
   `.cov__opt`: cuando el progreso era una barra aparte habia dos marcas teal a
   la vez y no se sabia cual era la elegida. Un objeto no se confunde consigo
   mismo. Y va en VERTICAL porque la lista es vertical, que es lo unico que esta
   hoja no tenia ya escrito.

   LA INACTIVA VA EN `--text-3` Y ESO ES CORRECTO ACA aunque `.cov__opt` lo
   haya rechazado: alla el rotulo mide 15px, donde la regla 5 pide 4,5:1 y
   `--text-3` da 3,71. Aca mide 24px o mas y es bold, o sea texto grande, donde
   el umbral es 3:1 y sobre `--surface-2` mide 3,52. */
.esc--usos { background: var(--surface-2); }

/* LAS DOS COLUMNAS SE ESTIRAN A LA MISMA ALTURA Y LA LECTURA SE CENTRA CONTRA
   LA LISTA. Con `align-items: start` el panel terminaba 115px arriba de la
   ultima opcion y el cuarto inferior derecho de la pantalla quedaba vacio: no
   se leia como aire, se leia como un hueco. Estirando, el vacio se reparte
   arriba y abajo de la lectura, que es aire de verdad. */
.uso__ui {
  margin-top: clamp(20px, 3.2vh, 42px);
  display: grid;
  grid-template-columns: minmax(0, 1.06fr) minmax(0, .94fr);
  gap: clamp(22px, 4vw, 72px);
  align-items: stretch;
}

.uso__lista { display: grid; }
.uso__b {
  position: relative; width: 100%;
  padding: clamp(13px, 2.6vh, 32px) 0 clamp(13px, 2.6vh, 32px) clamp(13px, 1.3vw, 22px);
  border: 0; border-top: 1px solid var(--line-2);
  background: none; font: inherit; text-align: left; cursor: pointer;
  color: var(--text-3);
  transition: color .24s var(--ease);
}
.uso__b:last-child { border-bottom: 1px solid var(--line-2); }
.uso__bt {
  display: block;
  font-size: clamp(24px, min(2.7vw, 4.3vh), 44px); font-weight: 700;
  line-height: 1.06; letter-spacing: var(--tr-h1);
}
@media (hover: hover) and (pointer: fine) {
  .uso__b:hover { color: var(--text); }
}
.uso__b.is-active { color: var(--text); }

.uso__b::after {
  content: ''; position: absolute; left: 0; top: -1px; bottom: -1px; width: 2px;
  background: var(--teal);
  transform: scaleY(0); transform-origin: 50% 0;
  transition: transform .24s var(--ease-out);
}
.uso__b.is-active::after { transform: scaleY(1); }
/* mientras nadie toco nada, el riel de la activa cuenta lo que falta para el
   proximo salto. `.sel-tomado` lo pone el script al primer gesto y a partir de
   ahi el riel vuelve a ser solo una marca de seleccion. */
.sel-auto:not(.sel-tomado) .uso__b.is-active::after {
  animation: usoRiel var(--sel-auto, 6000ms) linear;
}
@keyframes usoRiel { from { transform: scaleY(0); } to { transform: scaleY(1); } }
/* con movimiento reducido no hay reloj: el riel aparece entero y listo */
@media (prefers-reduced-motion: reduce) {
  .sel-auto:not(.sel-tomado) .uso__b.is-active::after { animation: none; }
}

/* EL PANEL. Los tres van apilados en la misma celda de grilla y el alto lo
   reserva `min-height`: sin eso, al saltar de sector el pie de la escena sube y
   baja cada seis segundos, que es peor que el espacio que ahorra.
   `.uso__p[hidden]` NO ES OPCIONAL: `.uso__p` declara `display`, y una clase
   con `display` explicito le gana al atributo `hidden` de la hoja del
   navegador. Sin esta linea los tres paneles se ven a la vez. Ya paso cuatro
   veces en este archivo y `verificar.py` lo chequea. */
.uso__stage { display: grid; align-content: center; }
.uso__p {
  grid-area: 1 / 1;
  display: grid; align-content: start; gap: clamp(10px, 1.5vh, 16px);
}
.uso__p[hidden] { display: none; }
.uso__p:not([hidden]) { animation: usoIn 300ms var(--ease-out) both; }
@keyframes usoIn {
  from { opacity: 0; transform: translateY(calc(var(--mv) * 8px)); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) { .uso__p:not([hidden]) { animation: none; } }

.uso__k {
  font-family: var(--mono); font-size: clamp(10px, .85vw, 11.5px);
  font-weight: 600; letter-spacing: .16em; text-transform: uppercase;
  color: var(--text-2);
}
/* las tres necesidades son la enumeracion que ya hacia el parrafo de la
   fuente, puesta como lista. Hairlines y nada mas: tres cajas adentro de una
   escena que ya tiene tres opciones serian seis objetos peleandose. */
.uso__pide { display: grid; border-top: 1px solid var(--line-2); }
.uso__pide li {
  padding: clamp(7px, 1.1vh, 11px) 0;
  border-bottom: 1px solid var(--line-2);
  font-size: clamp(14.5px, 1.2vw, 17px); line-height: 1.35;
  font-weight: 600; letter-spacing: var(--tr-sub); color: var(--text);
}
.uso__frase {
  font-size: clamp(14px, 1.15vw, 16.5px); line-height: 1.5;
  color: var(--text-2); max-width: 48ch;
}
/* EL UNICO RESTO DE LAS CINCO PREGUNTAS DE LA FUENTE, que venian sin una sola
   respuesta y se sacaron enteras. Va en nivel 3 y al pie porque no es la
   llamada de la pagina: la llamada la hace el cierre. */
.uso__pie { margin-top: clamp(16px, 2.4vh, 28px); }


/* ==========================================================================
   RESPONSIVE DE LAS RUTAS. VA AL FINAL DEL ARCHIVO, Y NO ES UN DETALLE.

   Estas reglas vivian en el bloque @media grande de mas arriba, junto con las
   de la home. Ahi estaban MUERTAS: las media queries NO suman especificidad,
   asi que `.run__p` declarado despues en el archivo le ganaba a `.run__p`
   dentro del @media, a cualquier ancho. El sintoma no era desborde  -por los
   minmax(0,...) nada se salia de la pantalla-  sino columnas aplastadas: a
   390px el panel de la mision quedaba en "230px 55px" en vez de apilarse.

   Por eso un chequeo de desborde horizontal NO alcanza para dar por buena una
   pagina angosta: hay que mirar que la regla se este aplicando de verdad.

   Todo lo de acá abajo es de las rutas (empresas, znappers, blog, nota,
   errores). La home no toca nada de esto.
   ========================================================================== */
@media (max-width: 1024px) {
  /* el selector de canal: el panel se apila y la foto va primero. En una
     columna angosta la captura es lo que hace entender de que se habla; el
     texto abajo se lee con la imagen ya en la cabeza.
     La lista de canales pasa a scroll horizontal y NO se envuelve: seis
     pestanas en dos filas hacen saltar el panel entero cada vez que cambia la
     altura de la lista. */
  .cov__list {
    flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none;
    scroll-snap-type: x proximity;
    margin-inline: calc(var(--gut) * -1); padding-inline: var(--gut);
  }
  .cov__list::-webkit-scrollbar { display: none; }
  .cov__opt { flex: 0 0 auto; scroll-snap-align: start; }
  .cov__p { grid-template-columns: 1fr; gap: clamp(20px, 4vw, 30px); }
  .cov__ph { grid-row: 1; height: clamp(220px, 46vw, 300px); }
  .cov__hd { max-width: none; }

  /* blog, nota y errores: todo a una columna en el mismo corte */
  /* el indice de la nota se va: en una sola columna, un indice pegajoso de
     cuatro items le come el alto util a la lectura, que es lo unico que esta
     pagina tiene que proteger. Los titulos siguen ahi y la barra de lectura
     del nav sigue diciendo cuanto falta. */
  .art__grid { grid-template-columns: 1fr; }
  .art__idx { display: none; }

  /* PERO EN UN DOCUMENTO DE CONSULTA EL INDICE SE QUEDA, Y NO ES UN CAPRICHO.
     La regla de arriba lo esconde porque en una nota de blog de cuatro
     secciones un indice pegajoso le come el alto util a la lectura, que es lo
     unico que esa pagina protege. Un legal es otra cosa: son diez a doce
     clausulas, LA APP LOS ABRE DESDE ADENTRO, y quien entra viene a buscar
     UNA, no a leerlo entero. Sin indice en el telefono, que es donde se lee,
     la unica forma de encontrarla es barrer el documento con el pulgar.
     Deja de ser pegajoso —en una columna eso seria una barra fija comiendo
     pantalla— y pasa a ser un bloque de entrada arriba del texto. */
  .art--doc .art__idx {
    display: block; position: static;
    margin-bottom: clamp(26px, 3vw, 36px);
    padding: 18px 20px 20px;
    background: var(--surface); border-radius: var(--r-surface);
  }
  .art--doc .art__idxl { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 2px 18px; }

  .posts__grid, .posts__grid--rel { grid-template-columns: 1fr; }
  .err__grid { grid-template-columns: 1fr; }
  .err__ph { max-width: 380px; aspect-ratio: 3 / 4; }

  /* el encabezado se apila: dos columnas de texto en 576px dejarian el
     titular en cuatro lineas y la bajada en siete */
  .act__head { grid-template-columns: 1fr; gap: clamp(16px, 3vw, 24px); align-items: start; }
  .act__head .lead { padding-bottom: 0; }


  /* ======================================================================
     LAS ESCENAS EN PANTALLA ANGOSTA
     ======================================================================
     La regla sigue siendo una escena = una pantalla, pero abajo de 1024px hay
     que rearmar cada una: lo que en escritorio va al costado, aca va apilado.

     Y EL ENCAJE PASA DE `mandatory` A `proximity`, que es lo mas importante de
     este bloque. Con `mandatory`, si una escena termina midiendo un pixel mas
     que la pantalla —y en un telefono el alto de la barra del navegador cambia
     al scrollear— el navegador la vuelve a encajar y el usuario queda atrapado
     sin poder llegar al final. `proximity` encaja cuando esta cerca y suelta
     cuando no. En escritorio, donde el alto no cambia solo, se queda
     `mandatory`, que es lo que se pidio.
     ====================================================================== */
  /* Y ACOTADO A LAS PAGINAS CON ESCENAS, igual que el de escritorio: escrito
     en `html` a secas alcanzaba a las ocho, y en las siete sin escenas el unico
     punto de encaje es el pie. En telefono el sintoma era mas suave que en
     escritorio —`proximity` no arrastra, solo tironea cerca del final— pero es
     el mismo bug. */
  html:has(.esc) { scroll-snap-type: y proximity; }

  /* ESCENA 1. Los paneles se apilan arriba y el mapa queda de fondo a pantalla
     completa. La ficha se achica: en un telefono, la foto del mapa y los
     numeros no pueden pelearse los mismos 400px de alto. */
  /* EN TELEFONO EL MAPA SE QUEDABA SIN AIRE. Los dos paneles y la leyenda se
     apilaban pegados y cubrian casi la pantalla entera: la pieza central de la
     escena —la ciudad con la campaña reproduciendose— quedaba reducida a un
     borde. Se declaraban cuatro filas para tres paneles, pero los tres se
     colocaban solos en las tres primeras y la cuarta quedaba vacia ABAJO de
     todo, o sea del lado que no servia.
     Mandando la leyenda a la fila 4, la fila flexible queda ENTRE la ficha y
     la leyenda: ahi se abre la franja por donde se ve el mapa, y la leyenda
     ancla al pie, que es donde tiene que estar porque habla de toda la escena.
     Las dos primeras siguen en `auto`: los datos no se estiran. */
  /* `minmax(0, 1fr)` y no un minimo duro: forzando 96px la escena pasaba a
     medir 847 en una ventana de 844. La franja de mapa sale de APRETAR la
     ficha, unas lineas mas abajo, no de reservar espacio que no hay. */
  .red__ui { grid-template-columns: 1fr; grid-template-rows: auto auto minmax(0, 1fr) auto; }
  .red__cabe { grid-row: 1; max-width: none; }
  .red__ley { grid-row: 4; }
  /* LA TARJETA VUELVE AL FLUJO, Y SIN ESTO EL TELEFONO ESTABA ROTO.
     En escritorio es `position: absolute` arriba a la derecha, con
     `width: min(330px, 29vw)`. En un telefono de 390px eso vale 113px: la
     tarjeta quedaba en una tira donde "Supermercados DIA" se cortaba al medio y
     los tres datos del pie se encimaban unos con otros. Medido por el banco, que
     lo informo como texto cortado en las siete escenas.
     La regla vieja de este bloque mandaba `.ev` a la fila 2 — una clase que dejo
     de existir hace rondas—, asi que no colocaba nada.
     LA TARJETA SE APRIETA PARA QUE SE VEA LA CIUDAD: todo lo que se recorta es
     aire, no se esconde ni un dato. La foto baja de alto porque en telefono es
     lo primero que se puede ceder sin perder que ES una foto de gondola. */
  .vuelta {
    /* `relative` Y NO `static`: lo que hace falta es que vuelva al FLUJO —en
       escritorio flota `absolute` arriba a la derecha— y `relative` ya lo hace.
       Con `static` el `z-index: 5` de la regla base queda sin efecto, y abajo de
       esta tarjeta hay un mapa con un panel de marcadores en `z-index: 600`:
       basta que algo cambie el contexto de apilado para que los pines se
       dibujen encima de la evidencia. Es un pixel de diferencia hoy y una clase
       de bug conocida. */
    position: relative; grid-row: 2;
    /* Y SE ANULAN `top`/`right`, QUE EN `relative` YA NO SE IGNORAN. La regla
       base los usa para colgar la tarjeta arriba a la derecha del mapa; con
       `static` no hacian nada, con `relative` DESPLAZAN. Medido: la tarjeta
       aparecia 32px corrida a la izquierda, pisando el margen. */
    top: auto; right: auto;
    width: auto; max-width: none;
  }
  /* v26: LA FRANJA DE MAPA MEDIA CERO, Y ESO ES LA ESCENA ENTERA.
     Medido a 390x844: `grid-template-rows` computaba `346px 321px 0px 87px`. La
     fila `minmax(0,1fr)` que existe justamente para abrir la ciudad valia CERO,
     y barriendo la escena de 4 en 4px solo 48 de 796 (6%) no estaban tapados por
     un panel — y eran dos astillas de 17 y 25px, no una franja.
     El comentario de arriba decia que la franja "sale de apretar la ficha".
     Era la idea correcta y no alcanzaba: la ficha media 321 y el titular 346, o
     sea 667 de 796 entre los dos, mas 87 de leyenda. No sobraba nada que apretar
     sin tocar los tres.
     Se recorta AIRE en los tres, ningun dato se esconde:
       - la foto de la ficha baja de 110 a ~76px. Sigue leyendose como foto de
         gondola, que es lo unico que tiene que hacer ahi;
       - el cuerpo de la ficha pierde relleno y separaciones;
       - la leyenda pasa de tres renglones a uno que se desliza (abajo).
     Con eso la fila del mapa pasa de 0 a ~160px. */
  .vuelta__img { height: clamp(68px, 9vh, 96px); }
  /* EL CUERPO DE LA FICHA PASA A DOS COLUMNAS. Apilado son cinco bloques en
     fila india —estado, comercio, direccion, cobertura y los tres numeros— y en
     un telefono eso son 209px de alto para 366 de ancho: la caja mas
     desaprovechada de la escena.
     La cobertura se para AL LADO del comercio, que ademas es donde tiene mas
     sentido: las dos cosas hablan de la misma tienda. Los tres numeros de la
     operacion se quedan abajo y a todo el ancho, porque su linea divisoria va A
     SANGRE de canto a canto —es lo que separa "que comercio" de "que costo"— y
     metida en una columna dejaria una raya flotando en el medio.
     Medido: el cuerpo baja de 209 a ~156px. */
  .vuelta__cuerpo {
    padding: 10px 12px 11px;
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(118px, .72fr);
    column-gap: clamp(10px, 3vw, 16px);
  }
  .vuelta__de     { grid-column: 1 / -1; }
  .vuelta__tienda { grid-column: 1; grid-row: 2; margin-top: 6px; }
  .vuelta__dir    { grid-column: 1; grid-row: 3; }
  .vuelta__cob    { grid-column: 2; grid-row: 2 / span 2; margin-top: 6px; align-self: start; }
  .vuelta__datos  { grid-column: 1 / -1; grid-row: 4; margin-top: 9px; padding-top: 8px; }

  /* EL TITULAR RESPIRABA MAS QUE EL MAPA. Las tres separaciones internas del
     bloque sumaban 47px sobre un panel de 346: aire entre piezas que ya se leen
     como distintas por tamaño. Bajan a 8 / 8 / 14. */
  .esc--red .esc__sub { margin-top: 8px; }
  .red__acc { margin-top: 14px; }

  /* LA LEYENDA SE DESLIZA EN VEZ DE ENVOLVER. Con `flex-wrap: wrap` los cuatro
     rotulos y la nota caian en tres renglones: 87px, o sea el 11% del alto de
     una escena cuyo protagonista tenia 0%. Es NIVEL 3 —contexto— y estaba
     gastando mas alto que el mapa.
     En una sola fila que se desliza mide 29px y devuelve 58 a la ciudad. Es el
     mismo patron que ya usa `.cov__list` con los seis canales, asi que no es un
     mecanismo nuevo: es el que la ruta ya tiene para "una fila que no entra".
     `-webkit-overflow-scrolling` no hace falta y `scrollbar-width: none` si: la
     barra de 15px sobre un velo de 29px de alto se lee como un error. */
  .red__ley {
    flex-wrap: nowrap; overflow-x: auto; max-width: 100%;
    scrollbar-width: none;
  }
  .red__ley::-webkit-scrollbar { display: none; }
  .red__ley > * { flex: none; }
  /* EL BOTON OCUPA EL ANCHO. Con la invitacion al lado, la fila medía mas que la
     pantalla y el banco la marcaba como texto cortado. Apilados, el boton es un
     blanco de toque de 44px de alto por todo el ancho, que es lo que un pulgar
     necesita. */
  .red__acc { flex-direction: column; align-items: stretch; gap: 10px; }
  .red__cta { width: 100%; justify-content: center; }
  .red__toca { justify-content: center; }

  /* ESCENA 2. El grafico va abajo del texto y los puntos se achican. */
  .hall { grid-template-columns: 1fr; gap: clamp(18px, 3vh, 28px); }
  .hall__grid { grid-template-columns: repeat(18, 1fr); gap: 4px; }

  /* ESCENA 4. Siete nodos no entran en una fila de 576px: el riel se desliza.
     Apilarlos convertiria la cadena en una lista, que es exactamente lo que el
     cliente mando borrar de la home. La forma tiene que seguir diciendo
     "encadenado". */
  /* `min-width: 0` EN TODA LA CADENA DE PADRES, Y NO ES OPCIONAL.
     Un elemento de grilla o de flex tiene `min-width: auto` por defecto, o sea
     que NO SE ENCOGE por debajo de su contenido. El riel mide 7x108px de ancho
     minimo, asi que sin esto empujaba a `.cad`, a la escena y hasta al
     titular: el desborde no lo causaba la tira —que hace scroll— sino sus
     padres, que crecian para contenerla. */
  .esc > *, .cad, .cad__tira { min-width: 0; }
  /* LA CADENA NO SE APILA, SE DESLIZA: apilarla la convierte en una lista, que
     es exactamente lo que el cliente mando borrar de la home. Y abajo de 1024
     el ancho de la activa se congela: con `overflow-x` puesto, una columna que
     se ensancha sola cada 3,4 s corre el contenido bajo el dedo de quien
     estaba leyendo otra. */
  .cad__tira {
    grid-template-columns: repeat(7, 122px) !important;
    min-width: 0; max-height: none; height: auto;
    overflow-x: auto; overscroll-behavior-x: contain;
    transition: none;
  }
  .cad__col { min-height: clamp(160px, 26vh, 230px); }
  /* EL ROTULO LARGO NO PUEDE VOLVER A APARECER EN LA ACTIVA. La regla de
     escritorio lo muestra y esconde el corto; esta la apagaba con
     `.cad__largo { display: none }`, que tiene MENOS especificidad que
     `.cad__col.is-active .cad__largo`, asi que en 390px se veian LOS DOS
     pegados: "ProcesamientoProces". Se apagan desde el mismo selector. */
  .cad__col .cad__largo, .cad__col.is-active .cad__largo { display: none; }
  .cad__col .cad__corto, .cad__col.is-active .cad__corto { display: block; }
  .cad__col.is-active .cad__rot { font-size: clamp(12px, 3.4vw, 15px); }
  .cad__col.is-active .cad__num { font-size: clamp(24px, 6.6vw, 34px); }
  /* ABAJO DE 1024 LAS TRES ESCENAS QUE SE ARMAN POR ALTO VUELVEN A CENTRAR SU
     BLOQUE. El reparto por filas elasticas existe para llenar una pantalla de
     escritorio, donde sobra alto; en 390x844, con todo apilado, lo que sobra
     es cero y una fila `1fr` de mas deja un hueco de 300px entre la lectura y
     la cadena. Medido en 390: pasaba exactamente eso. */
  .esc--cad, .esc--cov, .esc--pta { align-content: center; grid-template-rows: auto; }
  .cad {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: "cabe" "lee" "tira";
    grid-template-rows: auto auto auto;
  }
  .esc--cov .cov { grid-template-rows: auto auto auto; }
  .esc--cov .cov__ui { grid-template-rows: auto auto; }
  .esc--cov .cov__ph { min-height: 0; }
  .pta { grid-template-rows: auto auto; }
  .pta__caja { grid-template-rows: auto auto; }
  .pta__t { height: auto; }
  .cad__p { grid-template-columns: 1fr; gap: 12px; }

  /* ESCENA 5. La captura arriba y la anotacion abajo. Y la captura SE DESLIZA
     en vez de encogerse: es una pantalla de 1678px, y metida entera en 307px no
     se lee un solo numero. Achicada no prueba nada, que es todo lo que esta
     escena tiene que hacer. */
  .tab__ui { grid-template-columns: 1fr; }
  .tab__lienzo { overflow-x: auto; overscroll-behavior-x: contain; }
  .tab__vista { min-width: 620px; }

  /* LA COBERTURA, APRETADA PARA ENTRAR EN UNA PANTALLA DE TELEFONO. Medido:
     daba 1008px contra un viewport de 844. La foto y la lista de variables son
     lo que se puede achicar sin perder el argumento; el titular y los seis
     canales, no. */
  .esc--cov .cov__ph { height: clamp(112px, 16vh, 180px); }
  .esc--cov .cov__vars li { padding-block: 4px; font-size: 12.5px; }
  .esc--cov .cov__foot { font-size: 11px; margin-top: 10px; }
  .esc--cov .cov__hd { font-size: clamp(15px, 4.2vw, 19px); }
  .esc--cov .cov__ui { margin-top: 12px; }
  .esc--cov .cov__stage { margin-top: 12px; }

  /* LA MATRIZ DE DECISION NO SE APILA: SE DESLIZA.
     Apilar las tres columnas convierte una comparacion en tres folletos, que
     es exactamente lo que esta seccion dejo de ser. Una tabla de comparacion
     sin las columnas al lado no compara nada. Se desliza en horizontal, que es
     el patron que la gente ya conoce, y las dos partes (la tabla y la fila de
     botones) viven en el MISMO contenedor de scroll para que no se
     desincronicen al arrastrar. */
  .pta { overflow-x: auto; overscroll-behavior-x: contain; }
  .pta__t, .pta__pie { min-width: 640px; }
  /* LOS BOTONES DEJAN ENVOLVER SU TEXTO. En la tabla apaisada de telefono cada
     celda mide 181px y el boton 149: "Armar mi primera campaña" pide 175 y se
     cortaba al medio. `.btn` es una pildora de alto fijo y una linea, que esta
     bien en todos lados menos aca. Se le saca el alto duro y se le deja dos
     lineas: un boton mas alto se aprieta igual; uno con el texto cortado no se
     entiende. */
  .pta__b {
    height: auto; min-height: 44px; padding: 10px 14px;
    white-space: normal; text-align: center; line-height: 1.25;
  }
  /* LA LENTE, EN UNA COLUMNA. La lamina se pone arriba y las cinco variables
     debajo: apiladas al reves, el titular y la lista empujarian la foto abajo
     del pliegue y la escena arrancaria con un indice. */
  .lens { grid-template-columns: minmax(0, 1fr); gap: clamp(20px, 4vh, 32px); }
  .lens__foto { order: -1; }
  .lens__img { aspect-ratio: 16 / 11; }
  .lens__lista { margin-top: clamp(12px, 2vh, 20px); }
  .lens__stage { min-height: 3.6em; }

  /* ======================================================================
     /zlite EN PANTALLA ANGOSTA
     ======================================================================
     Las tres escenas se rearman, y las dos que llevan piezas cambian de forma,
     no solo de columnas.

     EL CICLO SE PARTE EN DOS RENGLONES. En escritorio el verbo y su
     complemento comparten linea de base, que es lo que lo hace una tabla. A
     576px el complemento entra en 18 caracteres y quedaria en cuatro renglones
     al lado de una palabra de 34px: se va abajo, en la misma columna, y el
     numeral se queda arriba con el verbo.

     EL MOSAICO SE DA VUELTA. Apilado, el orden del marcado pondria el telefono
     primero, y el protagonista de esa pantalla es el tablero: pasa a la fila 1.
     Y EL TELEFONO SE ACUESTA AL LADO DE SU TEXTO en vez de arriba: apilado y a
     37svh, la escena media 1.040px contra un viewport de 844 y las otras dos
     piezas caian abajo del pliegue. Chico y al costado ocupa el alto de su
     propia leyenda y no un pixel mas.
     ====================================================================== */
  .ciclo__f {
    grid-template-columns: clamp(26px, 7vw, 34px) minmax(0, 1fr);
    gap: 3px clamp(8px, 2.4vw, 14px);
  }
  /* y el complemento vuelve a la izquierda. Anclado a la derecha tenia
     sentido cuando compartia renglon con el verbo: en dos renglones, el verbo
     a la izquierda y su complemento a la derecha se leen como dos piezas
     distintas en vez de una frase. */
  .ciclo__g { grid-column: 2; justify-self: start; text-align: left; }
  .ciclo__sub { max-width: none; }

  .piez__grid { grid-template-columns: minmax(0, 1fr); }
  .piez__t--dash  { grid-column: 1; grid-row: 1; }
  .piez__t--app   { grid-column: 1; grid-row: 2;
                    grid-template-columns: auto minmax(0, 1fr);
                    align-items: center; gap: clamp(14px, 4vw, 22px); }
  .piez__t--adapt { grid-column: 1; grid-row: 3; }
  /* MEDIDO: apilada, la escena daba 900px contra un viewport de 844 y la nota
     al pie quedaba cortada. Los 56px salen de las dos piezas visuales y del
     cuerpo de la nota, no de sacar contenido: a este ancho la captura del
     tablero ya se lee como "una pantalla llena de datos" con 126px de alto. */
  .fono--zl { height: clamp(132px, 18svh, 190px); justify-self: start; }
  .piez__ph { height: clamp(104px, 15svh, 160px); }
  .piez__nota { font-size: 12px; }
  .piez__d, .piez__t--adapt .piez__d { max-width: none; }

  /* el selector pasa a una columna y el panel deja de reservar alto: apilado,
     lo que reservaba para que la lectura no saltara ahora es un hueco fijo
     entre los tres nombres y el pie. */
  .uso__ui { grid-template-columns: minmax(0, 1fr); gap: clamp(16px, 3.2vh, 26px); }
  .uso__stage { min-height: 0; }
  .uso__frase { max-width: none; }
}

/* A 560px el rotulo de la region no entra afuera de su caja sin salirse de la
   lamina: pasa adentro, arriba a la izquierda. Las cinco variables siguen en la
   lista, que a ese ancho es donde se leen. */
@media (max-width: 560px) {
  .lens__rot { bottom: auto; top: 5px; left: 5px; font-size: 9px; padding: 3px 7px; }
  .lens__nota { font-size: 10px; padding: 20px 10px 8px; }

  /* /zlite: A 360 CADA BLOQUE DE TEXTO ENVUELVE UN RENGLON MAS QUE A 390 y
     esos renglones se suman todos en la misma escena: medido, daba 902 contra
     un viewport de 844. Se recuperan los 58px de donde no cuesta legibilidad.

     Y EL CAMPO DE LA ADAPTABILIDAD SE CAMBIA POR UNA HAIRLINE, que es la mitad
     de esos pixeles. En escritorio el campo existe porque es la unica pieza sin
     imagen y sin el se leia como el hueco que dejaron las otras dos. Apiladas y
     a todo el ancho esa comparacion no existe: las tres ocupan lo mismo de
     ancho y lo que las separa es el borde de la pantalla, asi que una linea
     hace el mismo trabajo por 28px menos. */
  .piez__grid { margin-top: 16px; gap: 12px; }
  .piez__t { gap: 8px; }
  .piez__t--adapt { padding: 14px 0 0; background: none; border-top: 1px solid var(--line-2); border-radius: 0; }
  .fono--zl { height: clamp(124px, 17svh, 180px); }
  .piez__ph { height: clamp(96px, 14svh, 150px); }
  .piez__nota { margin-top: 10px; font-size: 11.5px; line-height: 1.4; }
}

/* ==========================================================================
   LA LAMINA PUEDE PASARSE DEL CONTENEDOR DE TEXTO. LA COLUMNA DE TEXTO NO.
   ==========================================================================
   `--wrap` es un maximo duro por decision del cliente y no se toca. Pero es el
   maximo de la MEDIDA DE LECTURA, y aplicarselo tambien a una fotografia es
   confundir dos cosas: una linea de texto deja de leerse arriba de ~75
   caracteres, y una foto de gondola no tiene ese limite — cuanto mas grande,
   mejor cumple su trabajo, que es que se vean las etiquetas.

   MEDIDO, Y POR ESO SE TOCA SOLO ESTA ESCENA: la lente llenaba el 56% de la
   pantalla en 1920 y el 60% en 1512, la mas vacia de la ruta despues del
   hallazgo, y la razon era una sola — la lamina esta limitada POR ANCHO
   mientras que las otras dos escenas de lamina se limitan por ALTO. El tablero
   ya calcula su ancho desde `100svh` (ver `.tab__lienzo`) y por eso llena el
   93-97%. La lente no lo hacia y se quedaba con 600px de pantalla al costado.

   Y SE ACOTA POR ALTO IGUAL QUE EL TABLERO, con la misma forma: el ancho no
   puede pasar de lo que el alto disponible permite con el recorte 4:3. Sin eso,
   en una ventana ancha y BAJA la lamina crece de ancho y la escena se pasa de
   alto — que es como se rompen estas dos escenas siempre.

   EL RECORTE 4:3 NO SE MUEVE, y eso no es estetica: las cinco regiones estan en
   porcentaje de la lamina, asi que cambiar la proporcion las despega de la
   gondola que señalan. Crece la lamina entera; el encuadre es el mismo.
   ========================================================================== */
@media (min-width: 1200px) {
  /* SE ANCLA AL MARGEN DE LA RUTA Y CRECE HACIA AFUERA. Centrado y mas ancho
     que `--wrap`, el bloque sobresalia por los DOS lados: en la tira de las
     siete escenas la lente arrancaba su texto 26px mas a la izquierda que las
     otras seis, y una ruta cuyo argumento es el ritmo vertical no puede tener
     una pantalla que se corre sola.
     El borde IZQUIERDO —el de lectura, el que el ojo usa para bajar— queda en
     el mismo lugar que en todas las demas; lo que se estira es el lado de la
     lamina. Es el gesto editorial de siempre: la columna de texto no se mueve y
     la imagen se va al margen. */
  .lens {
    /* `width: auto` Y NO SE PUEDE OLVIDAR: la regla base declara `width: 100%`,
       o sea el ancho de la ESCENA. Con un margen izquierdo encima, eso da un
       bloque que empieza en 360 y mide 1920: la lamina terminaba en 2231 sobre
       una pantalla de 1920. En `auto` el ancho lo resuelven los dos margenes,
       que es lo unico que puede quedar bien a cualquier tamaño. */
    width: auto; max-width: none;
    margin-left: max(24px, calc((100% - var(--wrap)) / 2));
    /* Y EL MARGEN DERECHO NO PUEDE SER `--gut`: en escritorio vale CERO. El
       inset lateral de esta ruta no lo da el gutter, lo da el centrado de
       `--wrap`, asi que `margin-right: var(--gut)` dejaba la lamina pegada al
       borde de la pantalla —medido: su canto derecho caia exactamente en 1920
       sobre una ventana de 1920, con la sombra y el redondeo cortados—. */
    margin-right: clamp(24px, 4vw, 72px);
  }
  .lens__foto {
    width: min(100%, calc((100svh - 290px) * 4 / 3));
    margin-inline: auto;
  }
}
/* UN ESCALON MAS ARRIBA DE 1700, Y VA DESPUES DEL DE 1200 A PROPOSITO: las
   media queries NO suman especificidad, asi que entre dos reglas que matchean,
   gana la que esta ULTIMA en el archivo. Escrito antes, a 1920 seguia pintando
   `min(1240px, 94vw)` y la lamina se quedaba en 731px con 1.100 de pantalla al
   costado. Es la misma trampa que esta hoja ya documenta arriba, y la pague
   igual. */


/* ==========================================================================
   EL DEDO PIDE 44px, Y NO ES UNA REGLA DE ESTILO: ES DE ENTRADA
   ==========================================================================
   Cuatro controles de la ruta quedaban abajo del minimo tactil, medidos en un
   telefono de 390 de ancho: los tres estados del hallazgo (34px), las cinco
   variables de la lente (43), los seis canales de la cobertura (40) y los tres
   iconos del pie (38x38). Son justo los que hay que APRETAR para que la pagina
   haga algo.

   VA EN `pointer: coarse` Y NO EN UNA MEDIA QUERY DE ANCHO, por dos razones: el
   problema es el dedo, no la pantalla —una tablet de 1024 con dedo lo tiene
   igual— y asi el escritorio no cambia ni un pixel. Se compra alto con relleno,
   no con `min-height`, porque `.lens__v` alinea por LINEA DE BASE: con
   `min-height` el sobrante se apila abajo y el numero se despega de su palabra.
   ========================================================================== */
@media (pointer: coarse) {
  .hall__op  { padding-block: 12px; }
  /* los chips del filtro del blog nacieron en 38px */
  .flt__b    { min-height: 44px; }
  .lens__v   { padding-block: 13px; }
  .cov__opt  { padding-top: 8px; }
  .finale__social a { width: 44px; height: 44px; }
  /* LOS ENLACES DEL PIE Y EL LOGO: relleno vertical, no `min-height`.
     Son elementos EN LINEA, y sobre un elemento en linea `min-height` no hace
     nada: la caja de linea la fija el interlineado. El relleno vertical sí
     agranda la caja de impacto sin mover una sola linea del texto, que es
     exactamente lo que hace falta acá. */
  .finale__big, .finale__quiet, .nav__logo,
  .finale__mini a, .finale__brand > a { padding-block: 12px; }
}

/* ==========================================================================
   DOS PIXELES QUE CON ENCAJE NO SON DOS PIXELES
   ==========================================================================
   Medido a 360x640: `#esc-tablero` daba `scrollHeight 666` contra
   `clientHeight 664`, y `#esc-cobertura` 645 contra 643. Los culpables son
   `.tab__nota` y `.cov__foot`, que sobresalen exactamente 2px por redondeo de
   sus `clamp()` contra el relleno de la escena.
   DOS PIXELES DE SCROLL INTERNO NO SE VEN, PERO SE SIENTEN: con
   `scroll-snap-type: y` puesto, el navegador tiene un punto de encaje que no
   puede alcanzar sin dejar 2px afuera, y al soltar el dedo la escena rebota.
   `overflow: clip` y no `hidden`: `hidden` crea un contenedor de scroll —o sea
   que la escena pasaria a poder scrollear por dentro, que es justo lo que hay
   que evitar— y ademas atrapa el `position: sticky` de la barra. `clip` recorta
   y no scrollea. `overflow-clip-margin` deja pasar las sombras, que si se
   recortan se ven como un canto duro alrededor de cada lamina. */
@media (max-height: 700px) {
  .esc { overflow: clip; overflow-clip-margin: 40px; }
}

/* ==========================================================================
   EL PUNTO DE CORTE QUE FALTABA: EL ALTO
   ==========================================================================
   Esta hoja tiene tres escalones por ANCHO y no tenia ni uno por ALTO, y ese
   era el hueco de fondo de toda la ruta: la pagina promete "una escena, una
   pantalla" mientras todo su sistema de espaciado esta calibrado en `vw` y en
   pixeles duros. En un monitor ancho y bajo eso da lo peor de los dos mundos,
   porque los rellenos valen su MAXIMO justo cuando menos alto hay.

   Medido: a 1440x700 la escena de la red medía 770px y no habia forma de que
   comprimiera, porque de sus ~376px de ficha ni uno estaba atado al alto.

   NO SE ESCONDE NADA. Todo lo que se recorta es aire: relleno de panel,
   margenes y el interlineado de las cajas de dato. Un dato que desaparece
   porque la ventana es baja es peor que un dato apretado, y ademas rompe la
   regla de la ruta de que la ficha cuenta lo que ese Znapper trajo.
   ========================================================================== */
@media (max-height: 780px) {
  :root { --pan-pad: 14px; }
  .red__ui { gap: 8px; }
  .esc__h1, .esc__h2 { margin-top: 8px; }
  .esc__sub { margin-top: 8px; }
  .vuelta__tienda { margin-top: 8px; }
  .vuelta__datos { margin-top: 9px; padding-top: 8px; }
  .red__ley { padding: 6px 10px; }
}

/* PANTALLAS MUY BAJAS (telefonos chicos y portatiles viejos). Segundo escalon
   del corte por alto: aca ya no alcanza con apretar la ficha, hay que sacarle
   aire a las siete escenas por igual. Sigue sin esconderse nada.
   Abajo de 1024 el encaje es `proximity` y no `mandatory`, asi que si una
   escena igual se pasa unos pixeles nadie queda atrapado: se scrollea y listo.
   Eso es lo que hace que este escalon sea una mejora y no una obligacion. */
@media (max-height: 700px) {
  /* LAS DOS ESCENAS A SANGRE QUEDAN AFUERA, y esto no es un detalle: la red y
     la lente anulan el relleno de `.esc` con `padding: 0` porque su contenido
     es el mapa y la foto, que van de borde a borde. Una regla `.esc` escrita
     DESPUES en el archivo le gana a `.esc--red` por orden, no por
     especificidad, asi que sin este `:not()` este bloque les devolvia 77px de
     relleno de barra y empeoraba justo la escena que venia a arreglar: la red
     paso de 675 a 764 en una ventana de 667. Medido. */
  .esc:not(.esc--red):not(.esc--lente) {
    padding-top: calc(var(--navh, 64px) + 8px); padding-bottom: 14px;
  }
  .tab__stage { min-height: 0; }
  /* EN PANTALLAS MUY BAJAS LA CAPTURA SE ACHICA. Abajo de 1024 el tablero vive
     en un contenedor que se scrollea de costado y la imagen se fuerza a 620px
     de ancho para que sus numeros se sigan leyendo al arrastrarla. Con 667px de
     alto eso son 340px solo de imagen y la escena se pasaba 45. Bajar el minimo
     a 520 devuelve 55px: se sigue arrastrando y se sigue leyendo, y la escena
     entra. Solo aplica cuando el alto aprieta; en tablet el minimo sigue en
     620. */
  .tab__vista { min-width: 520px; }
  /* v22: EL TABLERO ENTRA EN 375x667, Y NO ENTRABA DESDE ANTES DE ESTA RONDA.
     Medido sobre el commit anterior a la v22: 672 en una ventana de 667. O sea
     que `ESTADO.md` decia "queda desborde en 360x640 y menos" y ya no era
     cierto. El marco nuevo de la captura suma 6px mas, asi que se aprovecha
     para arreglar los dos de una vez: 4px de marco en vez de 11, y el aire de
     arriba y de abajo del lienzo al minimo. Se recorta AIRE, que es la regla
     de este bloque; la captura sigue midiendo lo mismo y arrastrandose igual. */
  .tab__lienzo { padding: 4px; }
  .tab__ui { margin-top: 8px; }
  .tab__nota { margin-top: 0; }
  .esc__h1, .esc__h2 { margin-top: 6px; }

  /* v22: LA TARJETA DE EVIDENCIA VUELVE A ENTRAR EN 375x667.
     La foto crecio un 35% en esta ronda (de 120px minimos a 158) y el titular
     de la primera escena subio de 38 a 50px de tope. Las dos cosas son
     mejoras y las dos se pagan en alto: medido, la escena de la red paso de
     667 a 751. Aca la foto vuelve a su minimo viejo y el titular a su escala
     vieja. NO SE ESCONDE NADA, que es la regla de este bloque: lo unico que se
     recorta es tamaño de pieza, y la tarjeta sigue contando lo mismo. */
  .vuelta__img { height: clamp(112px, 17vh, 150px); }
  .esc--red .esc__h1 { font-size: clamp(26px, 3vw, 34px); }
  .vuelta__cuerpo { padding: 12px 14px; }
  .vuelta__dir { font-size: 11.5px; }
}

/* ==========================================================================
   EL REMATE: LA CAMPAÑA COMPLETA, SOSTENIDA
   ==========================================================================
   `is-plano` la pone `cerrar()` sobre la escena cuando las seis misiones se
   cumplieron, y dura lo que dura el remate.

   LA TARJETA YA NO SE VA, Y SACAR ESA REGLA ES PARTE DE LA v23.
   Se iba por un motivo que era cierto y dejo de serlo: con la camara siguiendo
   a la mision, el remate ERA un plano general —la camara se abria— y con la
   tarjeta puesta habia que reservarle 700px al encuadre, con lo cual los seis
   comercios entraban recien en zoom 13. Abrir la camara y sacar la interfaz no
   eran dos ideas sino una sola.
   Desde que la camara no se mueve, no hay plano que abrir: lo unico que hacia
   esconder la tarjeta era dejar un hueco de 400x450 durante casi cuatro
   segundos, con el ultimo dato relevado desapareciendo justo cuando la campaña
   se completa. Se vio en el navegador del cliente y parecia un bug.

   LO QUE SI SE QUEDA es el velo del titular afinandose: la ciudad encendida es
   lo que hay que mirar en ese momento, y ese es el unico gesto que hace falta
   para decirlo. */
/* `.is-plano` atenuaba el velo del titular durante el remate, para que la
   campaña completa se viera sin nada encima. Con el velo propio retirado, lo que
   se atenua es el de la escena: mismo gesto, una capa menos. */
.js .esc--red.is-plano .red__mapa::after { opacity: .62; }
.red__mapa::after { transition: opacity .5s var(--ease); }
/* la tarjeta entra con un retardo corto respecto del resto de la escena: es la
   ultima pieza en llegar porque es la que trae el resultado */
.js .esc.is-en .vuelta[data-en] {
  transition: opacity .45s var(--ease-exp) .25s, transform .5s var(--ease-exp) .25s;
}
