/* ==========================================================================
   FUNDACIÓN PUNTO 37 — palabras que se escriben solas
   --------------------------------------------------------------------------
   Pedido del cliente, en sus palabras: "para algunos textos, me gustaría que
   se escribieran solos, si es posible, como si los estuvieran escribiendo a
   mano".

   QUÉ SE ESCRIBE, Y POR QUÉ TAN POCO. Goodkids aparece en cinco sitios del
   arte, y además el remate de las cabeceras interiores repite el mismo patrón
   en cada página. Si se escribieran todos, el efecto dejaría de ser un remate
   y pasaría a ser un tic: APOYO y BOLETÍN salen en las CINCO páginas, así que
   alguien que recorre el sitio los vería escribirse diez veces. Y "Misión" es
   el rótulo de un bloque, no una frase: obligar a esperar a que se escriba un
   letrero es hacerle perder el tiempo a quien busca el contenido.

   Se escriben tres, y las tres cierran una frase que sólo tiene sentido cuando
   llega su última palabra:

     Inicio    "Llegamos hasta donde otros NO LLEGAN"   → NO LLEGAN
     Inicio    "Empezó con un gesto, se volvió un       → compromiso
                compromiso"
     Nosotros  "Más que un gesto, un compromiso"        → compromiso

   En las tres, la palabra manuscrita ES el remate de la frase: verla aparecer
   trazo a trazo cuenta lo mismo que dice el texto. Ese es el criterio; donde
   no se cumple, no se aplica.

   POR QUÉ LETRA A LETRA Y NO TRAZO DIBUJADO EN SVG. Convertir el texto a
   contornos SVG obligaría a: perder el texto real (el buscador del navegador y
   los lectores de pantalla se quedarían sin él), congelar el tamaño justo
   donde el sitio usa clamp() y depender de una librería para leer la fuente,
   que aquí está prohibido. Y hay una razón de dibujo, no sólo técnica:
   Goodkids Rough tiene los bordes rotos a propósito, así que un contorno
   dibujado con stroke-dasharray no traza la letra, traza su silueta. Se leería
   como un globo que se infla alrededor de la palabra, no como una mano.

   CÓMO SE CONSIGUE LA "MANO". Tres cosas, y ninguna es una máquina de
   escribir:
     1. el compás es IRREGULAR (lo reparte escritura.js), porque un intervalo
        constante suena a teletipo y no a pulso;
     2. cada letra entra desde un poco más abajo y desenfocada, y se asienta:
        es tinta que cae y se seca, no una letra que se enciende;
     3. una punta de pincel recorre la palabra por delante de la tinta.

   POR QUÉ LA ANIMACIÓN VIVE AQUÍ Y NO EN JAVASCRIPT. Es la lección del
   contador de cifras, que se quedó congelado en "5+" y en "539+" y estuvo
   enseñando datos falsos: lo que depende de que un bucle de JavaScript siga
   vivo, se queda a medias el día que ese bucle muere. Aquí JavaScript sólo
   MIDE y REPARTE los retardos; quien revela es CSS, con fill-mode:both, que
   termina solo aunque no quede nadie mirándolo.

   Y por si acaso, esta hoja trae un seguro de caducidad: toda letra armada
   lleva una animación de espera de 10 s que la deja visible al vencer. Si
   escritura.js muriera justo después de armar, el texto aparece igual.

   Ese seguro NO basta él solo, y conviene saber por qué: corre sobre la línea
   de tiempo del documento, y hay entornos donde esa línea se congela aunque la
   página se siga pintando. Medido en el panel de pruebas de este proyecto,
   document.timeline.currentTime seguía en 0 después de 74 segundos. Por eso
   escritura.js lleva ADEMÁS un techo por reloj de pared a los 12 s, que no
   depende de ninguna línea de tiempo. Los dos seguros cubren fallos distintos
   y por eso están los dos.

   SIN JAVASCRIPT NO PASA NADA. Todo lo de aquí cuelga de html.esc, y esa clase
   la pone escritura.js. Sin script no hay clase, no hay reglas y la página se
   ve entera desde el primer pintado. Es la misma regla de animaciones.css.

   ORDEN DE CARGA: va justo ANTES de profundidad.css, que sigue siendo la
   última. Comprobado hoja en mano: profundidad.css no escribe ni una regla
   sobre .mano, .brochazo ni ningún .esc-*, y su guarda `html.anim .rv{...}`
   apunta a los contenedores, no a las letras, así que las dos capas no se
   tocan.
   ========================================================================== */


/* --- 1. LA CAJA ----------------------------------------------------------
   El contenedor pasa a ser referencia de posición para la punta del pincel,
   que va en absoluto y por eso no ocupa sitio en la maqueta.

   Las tres palabras elegidas son display:block o inline-block, así que su
   bloque contenedor está bien definido y la punta no depende de dónde parta la
   línea. Si mañana se añadiera una palabra que fuese inline pura, habría que
   comprobar este punto antes. */
html.esc .esc{ position:relative }


/* --- 2. EL TEXTO DE VERDAD ------------------------------------------------
   Las letras sueltas van con aria-hidden, así que hace falta una copia intacta
   del texto para el buscador del navegador (Ctrl+F la encuentra) y para los
   lectores de pantalla. Se esconde con la misma receta que .oculto de
   estilo.css: sigue en el árbol de accesibilidad, que es justo lo que se
   quiere, a diferencia de display:none o visibility:hidden.

   OJO: escritura.js escribe estas mismas declaraciones EN LÍNEA sobre el
   elemento. No es duplicación por descuido. Si esta hoja no llegara a cargar,
   la copia accesible se vería y la palabra saldría dos veces; con el estilo en
   línea eso no puede pasar. Aquí se deja escrito para que se entienda el
   sistema leyendo la hoja. */
html.esc .esc-real{
  position:absolute; width:1px; height:1px;
  overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap;
}


/* --- 3. LAS LETRAS -------------------------------------------------------
   Van en <span> y no en <i> a propósito: si esta hoja fallara, un <i> saldría
   en cursiva y la palabra quedaría torcida. Un <span> sin estilo no se nota.

   position:relative, y NO transform. No es un capricho: transform no aplica a
   cajas inline, así que para usarlo habría que pasar cada letra a
   inline-block, y eso rompe el kerning. Medido en este sitio: "compromiso" con
   letras inline-block mide 321,3 px en vez de 316,9, o sea 4,4 px de más que
   al restituir el texto darían un tirón. Con letras inline la diferencia es de
   0,09 px, que es ruido de subpíxel. Por eso el desplazamiento se hace con
   `top`, que en posición relativa mueve lo pintado sin mover a nadie. */
html.esc .esc-c{
  position:relative;
  opacity:0;
  will-change:opacity;
}

/* La palabra entera no se puede partir por la mitad al final de un renglón.
   Sin esto, cada letra sería una caja independiente y el navegador podría
   cortar "compromiso" en "compro" y "miso". */
html.esc .esc-pal{ white-space:nowrap }


/* --- 4. LA TINTA QUE CAE --------------------------------------------------
   Los valores van en em para que la caída sea proporcional al tamaño de la
   palabra: la misma regla sirve para los 96 px de "compromiso" en Inicio y
   para los 30 px que mide en un móvil estrecho.

   El desenfoque es de 0,022em (unos 2 px sobre 96, medio píxel sobre 30). Más
   que eso se lee como una mancha; menos, no se nota. Y en el 55% la letra pasa
   un pelo por encima de su sitio antes de asentarse: es lo que hace un trazo
   que se apoya. */
@keyframes esc-traza{
  0%  { opacity:0; top:.038em; filter:blur(.022em) }
  55% { opacity:1; top:-.008em; filter:blur(.004em) }
  100%{ opacity:1; top:0; filter:blur(0) }
}

/* Seguro de caducidad. Vive mientras la palabra está armada y esperando su
   turno: si nadie la dispara, al vencer los 10 s la deja visible ella sola.
   fill-mode:both es lo que clava el estado final; una transición no serviría,
   porque se reinicia con cualquier recálculo de estilo. */
@keyframes esc-espera{
  0%, 94%{ opacity:0 }
  100%   { opacity:1 }
}

html.esc .esc-armado .esc-c{
  animation:esc-espera 10s linear both;
}

/* Va DESPUÉS de la de espera a propósito: misma especificidad, gana el orden.
   Aun así escritura.js quita .esc-armado al disparar, así que en la práctica
   nunca conviven; esto es la red por si alguna vez lo hicieran. */
html.esc .esc-escribiendo .esc-c{
  animation:esc-traza var(--esc-dur,210ms) var(--esc-d,0ms)
            cubic-bezier(.22,.68,.3,1) both;
}


/* --- 5. LA PUNTA DEL PINCEL ----------------------------------------------
   Un cursor de terminal que parpadea diría "máquina", que es justo lo
   contrario de lo que pidió el cliente. Esto es una punta cargada de tinta:
   una mancha alargada, inclinada, del color del propio texto, que va por
   DELANTE de las letras. La tinta aparece detrás de ella, como cuando el
   pincel ya levantó.

   Su recorrido no se puede escribir aquí: depende de dónde caiga cada letra,
   que sólo se sabe midiendo con la fuente ya cargada. Así que escritura.js
   mide una vez y fabrica un @keyframes con las coordenadas reales. Si esa
   parte fallara, la punta sencillamente no aparece y las letras se escriben
   igual: es adorno, no estructura. */
html.esc .esc-punta{
  position:absolute; left:0; top:0;
  width:.085em; height:.46em;
  border-radius:.05em .05em .04em .04em;
  background:currentColor;
  opacity:0;
  pointer-events:none;
  /* Se pinta por encima de la palabra, que es donde va una punta que escribe. */
  z-index:2;
}


/* --- 6. LA SALIDA --------------------------------------------------------
   .esc-fin es el estado "esto ya terminó, pase lo que pase". Lo pone
   escritura.js en TODAS sus salidas: al acabar la animación, al vencer el
   reloj de respaldo y al cambiar la visibilidad de la pestaña. Justo después
   el script devuelve el marcado original, así que este bloque sólo tiene que
   sobrevivir unos milisegundos; existe porque si esa restitución fallara, la
   palabra ya estaría completa y visible de todas formas.

   Los !important son deliberados: tienen que ganarle a una animación a medio
   camino, y una animación en curso pesa más que cualquier declaración normal
   de autor. Es lo mismo que hace animaciones.css al pasar al reposo. */
html.esc .esc-fin .esc-c{
  animation:none !important;
  opacity:1 !important;
  top:0 !important;
  filter:none !important;
}
html.esc .esc-fin .esc-punta{
  animation:none !important;
  opacity:0 !important;
}


/* --- 7. MOVIMIENTO REDUCIDO ----------------------------------------------
   escritura.js ni siquiera arma nada si la preferencia está activa, así que
   esto no debería llegar a usarse nunca. Se deja por un caso real: la
   preferencia puede cambiar con la página ya abierta (el sistema pasa a
   ahorro de energía, alguien la activa en accesibilidad), y entonces las
   reglas de CSS sí se reevalúan aunque el script ya haya pasado. */
@media (prefers-reduced-motion: reduce){
  html.esc .esc-c{
    animation:none !important;
    opacity:1 !important;
    top:0 !important;
    filter:none !important;
  }
  html.esc .esc-punta{ display:none !important }
}
