/* ============================================================
   EL GRANO · hoja compartida por todo el máster

   Va aquí y no dentro de cada página por una razón concreta: el
   grano es lo único capaz de unificar ocho tratamientos de origen
   distinto. Si cada tira lleva su copia, a la tercera ya no son el
   mismo grano y deja de coser nada.

   Las capas las mete grano.js — aquí solo está su aspecto.

   DOS CAPAS desde el 14 de septiembre, y cada una hace un trabajo que
   la otra no puede:

   .grano     overlay     el pique Y el cuerpo. Las dos frecuencias
                          —la fina y la gruesa— van fundidas dentro del
                          mismo mosaico. Hasta hoy eran dos capas
                          superpuestas, `.grano` en overlay y `.grano.b`
                          en soft-light; se fundieron porque el coste
                          del hervido es el ÁREA que se repinta, y esto
                          la deja a la mitad. El porqué de los valores
                          está en comun/grano.js.
   .grano-negros  screen  el polvillo del fondo. NO se funde con la
                          anterior, y no es un olvido: una capa tiene un
                          solo modo de mezcla. Hace falta porque en
                          overlay el negro se queda negro: sin esta,
                          el fondo de la web queda liso y muerto al
                          lado de las celdas. Screen solo puede sumar
                          luz, así que levanta los negros y en los
                          medios tonos no se nota.
============================================================ */

/*  ————— DE DÓNDE NACEN LAS CAPAS · VALORES POR DEFECTO —————
    Las capas de grano existen en el DOM antes de que grano.js escriba
    una sola variable: `--grano-img` no valía nada hasta el primer
    `pintar(0)`, así que entre el primer fotograma pintado y ese
    `pintar(0)` había fotogramas sin grano. Ese es el apagón doble del
    arranque, y es el mismo error que el flash de la frase del 10 sep,
    al revés.

    REGLA DEL PROYECTO: lo que el JS enciende al arrancar no puede
    depender de que el JS haya corrido.

    Estos valores NO son una aproximación: son exactamente los que
    escribe `pintar(0)` —FUNDIDA[0], NEGROS[3] y el primer paso del
    temblor—, así que cuando grano.js arranca no hay salto:
    lo siguiente que se ve ya es el hervido. grano.js escribe en el
    style inline de <html>, que gana a esta regla — en cuanto corre,
    manda él, y el calibrador y la tecla N siguen funcionando igual.

    LAS OPACIDADES VAN AQUÍ POR OBLIGACIÓN, NO POR SIMETRÍA. Sin ellas,
    `calc(var(--grano-op) * var(--g,1))` es inválido en tiempo de
    cómputo y `opacity` cae a su valor inicial, que es 1: dar imagen por
    defecto sin dar opacidad por defecto no arregla el apagón, lo
    cambia por un fogonazo de grano a plomo. Hoy no se nota justamente
    porque no hay imagen.

    El tamaño es el mismo fallback que usa grano.js cuando nadie le
    pasa medidor: alto de banda (40vh) por el aspect-ratio de la celda
    (2.3706) por TAM. Si la celda real mide otra cosa, la primera
    `medir()` lo corrige en el fotograma en que grano.js arranca.   */
:root{
  --grano-op:.38;
  --grano-op-n:.085;
  --grano-pos:-3.5px -3.5px;
  --grano-tam:calc(100vh * .4 * 2.3706 * .17);
  --grano-tam-b:calc(100vh * .4 * 2.3706 * .33);
  --grano-img:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='n' color-interpolation-filters='sRGB'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.62' numOctaves='4' seed='0' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0' result='f'/%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.216' numOctaves='4' seed='37' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0' result='g'/%3E%3CfeComposite in='f' in2='g' operator='arithmetic' k1='0' k2='0.800' k3='0.200' k4='0'/%3E%3CfeComponentTransfer%3E%3CfeFuncR type='linear' slope='2.2' intercept='-0.600'/%3E%3CfeFuncG type='linear' slope='2.2' intercept='-0.600'/%3E%3CfeFuncB type='linear' slope='2.2' intercept='-0.600'/%3E%3C/feComponentTransfer%3E%3C/filter%3E%3Crect width='220' height='220' filter='url(%23n)'/%3E%3C/svg%3E");
  --grano-img-n:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='n' color-interpolation-filters='sRGB'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.48' numOctaves='4' seed='74' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3CfeComponentTransfer%3E%3CfeFuncR type='linear' slope='1.5' intercept='-0.35'/%3E%3CfeFuncG type='linear' slope='1.5' intercept='-0.35'/%3E%3CfeFuncB type='linear' slope='1.5' intercept='-0.35'/%3E%3C/feComponentTransfer%3E%3C/filter%3E%3Crect width='220' height='220' filter='url(%23n)'/%3E%3C/svg%3E");
}

/*  La franja: el hueco de una tira. La capa de grano va DENTRO de
    ella pero FUERA de la banda que se desplaza — si fuera dentro, el
    grano viajaría con la tira, y el grano no viaja: se vuelve a
    sortear en el sitio.

    isolation:isolate acota la mezcla a esta tira y no a media
    página. Pero aislar deja al grano sin fondo contra el que
    mezclarse, y overlay contra nada no puede hacer nada: en vez de
    desaparecer, el ruido se pinta tal cual y sale una banda gris a
    lo ancho de la tira. Por eso el negro va DENTRO del grupo
    aislado. Con negro debajo, overlay da negro y el grano solo
    aparece donde hay celda — que es todo lo que se le pide.       */
.franja{ position:relative; isolation:isolate; background:#000; }

.grano{
  position:absolute; inset:0; z-index:5; pointer-events:none;
  mix-blend-mode:overlay;
  /*  --g es el multiplicador de ESTA tira (data-grano). Las ocho son
      materiales distintos: la que lleve archivo de los 70 pide más
      grano que la que lleve render limpio.                         */
  opacity:calc(var(--grano-op) * var(--g,1));
  background-image:var(--grano-img);
  background-position:var(--grano-pos);
  background-size:var(--grano-tam) auto;
}
/*  ————— DORMIDA · la capa cuya franja no está en pantalla —————
    La pone y la quita el IntersectionObserver de grano.js. Se queda el
    elemento y se queda su mezcla: lo que se va es el único trabajo que
    de verdad cuesta, pintar el ruido. Medido el 16 en el iMac: quitar
    las doce capas del todo da ×4.71 contra ×1.64 de sólo congelarlas,
    o sea que el área se paga aunque el grano esté quieto. Y en pantalla
    hay dos o tres franjas de nueve. Ver la nota del JS.            */
.grano.dormida{ background-image:none; }

/*  ————— APAGANDO · el grano se va con un fundido *(16 sep)* —————
    Lo preguntó Valeria: «¿no hay un salto entre versiones?». Sí lo hay,
    y está en el paso del peldaño 1 al 2 del modo suave, dentro de la
    primera franja. Los vídeos no dan tirón —arrancan desde el fotograma
    donde el scrub los dejó— pero la textura desaparecía de golpe.

    Medio segundo de fundido convierte el corte en una disolvencia. No
    cuesta nada: es una opacidad animada por el compositor, y ocurre UNA
    vez en la visita.                                                */
.grano, .grano-negros, .polvo{ transition:opacity .5s linear; }
.apagando .grano, .apagando .grano-negros, .apagando .polvo{ opacity:0; }

.grano-negros{
  position:fixed; inset:0; z-index:29; pointer-events:none;
  mix-blend-mode:screen;
  opacity:var(--grano-op-n);
  background-image:var(--grano-img-n);
  background-position:var(--grano-pos);
  /*  ————— ⬛ EL MOSAICO NUNCA BAJA DE SU ORIGEN · 18 sep —————
      El grano es un `feTurbulence` dibujado a 220 px en grano.js, y
      esto lo estira o lo encoge. `--grano-tam-b` sale del ancho de
      celda de una tira, así que en un teléfono encoge con ella:

          escritorio   celda ~910 px → 225 px   ×1.02   limpio
          el teléfono  celda ~597 px → 148 px   ×0.67   MOIRÉ

      **Reducir ruido aliasea.** Meter 220 px de turbulencia en 148
      tira uno de cada tres píxeles y lo que queda es un patrón
      regular — la «rejilla de cuadrados» que Valeria vio en el móvil
      el 18 de septiembre. Sólo se veía ahí porque en el monitor el
      mosaico ya caía en su tamaño natural.

      El `max()` es la regla entera: agrandar ruido es inofensivo,
      encogerlo no. Se escribe aquí y no en JS porque en JS depende
      del orden en que corran los manejadores de `resize` —se probó, y
      al girar el teléfono volvía el moiré—; en CSS no hay carrera.  */
  background-size:max(220px, calc(var(--grano-tam-b) * var(--grano-dens, 1))) auto;
}

/*  Una página suelta: el grano va dentro del cuadro y se mide contra
    él, no contra la pantalla.                                      */
.cuadro > .grano{ position:absolute; z-index:7; }

@media (prefers-reduced-motion:reduce){
  /*  Con el hervido continuo, un grano que se sortea doce veces por
      segundo a pantalla completa es exactamente lo que esta consulta
      existe para evitar. Se queda quieto, no se quita: sin grano la
      página es otra cosa.                                          */
  .grano, .grano-negros{ animation:none; }
}
