ENRE_WEB/README.md
SITO c3b51be992 La enredadera serpentea entre filas y los brotes cuelgan de un tallo
El dibujo del tallo de ordenador pasa a llevar dos filas: la pasada y su
espejo. Entra y sale vertical por las dos esquinas, así que empalma consigo
mismo por los dos lados y con repeat-y cada dos filas serpentea sin junta. En
colectivos, que tiene doce hojas, se veían dos ramas sueltas: la segunda
empezaba otra vez desde el margen izquierdo, debajo de la espiral con la que
acababa la primera.

El fondo va ahora en dos capas porque la última fila remata y las de arriba
siguen: .enredadera::before pinta todas menos la última y ::after pinta la
última con la espiral, en espejo si la fila es par (lo decide un :has()). Las
reglas por hoja pasan de tandas de seis a tandas de doce: las seis del espejo
son las mismas del revés y con el giro cambiado de signo.

Los tres brotes de REDES llevan su propio tallo, encadenado del agarre a la
primera hoja y de hoja en hoja, en vez de salir flotando. Para eso lo que se
coloca es la punta del rabillo -translate(-50%,0)- y no el centro de la hoja,
que va en vw y no dejaba que el ángulo fuera constante.

En ordenador el brote baja a la izquierda y ya no se sale por abajo de la
pantalla (se salía en seis de las siete medidas, hasta 41px a 1920x1080). En
móvil sale por la izquierda y trepa por el margen en vez de salir a la
derecha, donde asoman Eventos y Campañas: de 48 solapes en las nueve
pantallas a cero.

Banco: 0 fallos y 0 avisos en 150 mediciones.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FRjJnc4j5P7f5gCjkVmNY9
2026-09-14 20:38:40 +02:00

484 lines
22 KiB
Markdown

# Web del CSO La Enredadera de Tetuán
Sitio estático: HTML y CSS. **No hay build, ni framework, ni dependencias.**
Se despliega copiando la carpeta y sirviéndola con cualquier servidor web.
Todo el JavaScript que hay son los dos calendarios, que venían de antes y
funcionan. La navegación, el menú, las fichas de colectivo y el brote de REDES
son CSS puro: funcionan con JavaScript desactivado. **Comprobado**, no
supuesto: se desactiva el JS por CDP y se mira. Los dos calendarios, que sí lo
necesitan, avisan de ello con un `<noscript>` y dicen dónde mirar.
---
## Ver la web en local
```sh
python3 -m http.server 8099 --bind 127.0.0.1
```
y abrir <http://127.0.0.1:8099/>. Tiene que servirse por HTTP: si abres el
fichero con `file://` fallan las rutas absolutas y el calendario de convoca.la.
---
## Los ficheros
| Fichero | Qué es |
|---|---|
| `index.html` | Portada: la enredadera con seis hojas |
| `colectivos.html` | Las doce hojas de colectivos: once con ficha y Tattoo Circus, que lleva a su micrositio |
| `calendario.html` | Horarios semanales de cada colectivo |
| `eventos.html` | Agenda de convoca.la |
| `subdir/campa.html` | Campañas |
| `subdir/sobre_nosotrxs.html` | Quiénes somos y la historia del espacio |
| `subdir/contacto.html` | Dónde estamos y cómo proponer algo |
| `tatus.html`, `panel.html` | Tattoo Circus: micrositio aparte, con su propio CSS |
| `404.html` | Página de error, con el mismo armazón que el resto |
| `index.css` | **Todo el estilo del sitio**, menos los dos del Tattoo Circus |
| `INFO/` | Imágenes. **12 de las 18 no se usan** (3,2 MB): ver pendientes |
| `favicon.svg`, `favicon.ico` | Icono: una hoja verde con los nervios en rosa |
| `Code462WPink-Regular.ttf` | La fuente de los títulos, servida desde aquí |
---
## Añadir una hoja a la enredadera
Copia un `<li>` entero y cambia el enlace y el texto. **Nada más.** La
posición, el ángulo y el tamaño salen solos de `:nth-child`, así que la hoja
nueva se coloca en el punto que le toca sin tocar el CSS.
```html
<li>
<a class="hoja" href="/donde-va.html">
<svg class="hoja-svg" viewBox="0 0 100 114" aria-hidden="true"><use href="#hoja-forma"></use></svg>
<span class="hoja-txt">Como se llama</span>
</a>
</li>
```
Variantes:
- `class="hoja brota"` — hoja más tenue, para un colectivo que existe pero
todavía no ha escrito su ficha.
- Para que abra una ficha en vez de ir a otra página, el enlace apunta a
`#ficha-loquesea` y añades el `<article class="ficha" id="ficha-loquesea">`
al final del documento, copiando uno de los que ya hay.
**Aviso:** los ciclos son de 6. Si añades una séptima hoja a la portada, la
raíz seguirá pasando por su sitio, pero repasa a ojo que no se pise con la
vecina — y pasa el banco de pruebas (más abajo).
---
## Los colores
Están todos en `:root`, al principio de `index.css`. **Para recolorear el
sitio entero solo hay que tocar ahí.**
| Variable | Para qué |
|---|---|
| `--verde`, `--verde-hondo`, `--verde-humo` | fondos |
| `--rosa` | contorno de las hojas y acentos. **Nunca para texto normal:** da 3.1:1 |
| `--rosa-legible` | el rosa para textos y enlaces: 6.3:1 |
| `--verde-vivo` | nervios y zarcillos. Es adorno: 1.7:1, no vale para texto |
| `--verde-hoja` | el verde legible, para subtítulos |
| `--tinta`, `--tinta-suave` | textos |
| `--marron`, `--negro` | las dos hebras de la raíz |
Los contrastes están medidos contra WCAG AA. Si cambias un color, mídelo: hay
un cálculo hecho en `../BITACORA.md` que puedes reutilizar.
---
## Las tres medidas de la enredadera
Están en `.enredadera`, y **son tres cosas distintas a propósito**. Antes eran
una sola variable y se estorbaban entre sí.
| Variable | Qué manda |
|---|---|
| `--fila` | la **separación**: alto de la franja. Más alta = hojas más apartadas y menos hueco abajo |
| `--hoja-base` | el **tamaño** de la hoja |
| `--col` | el ancho **real** de columna, que es el tope de la hoja |
Por qué separadas: estirar la franja para que llene la pantalla agrandaba
también las hojas y volvía a juntarlas. Ese fue el callejón de «`--fila` a
63vh», anotado en `../BITACORA.md`.
Y `--col` no es `15vw`: coinciden solo por debajo de 1680px. A partir de ahí
la rejilla deja de crecer (`max-width:1680px`) pero `15vw` sigue subiendo, así
que a 1920 las hojas se salían de su columna y se pisaban.
En móvil mandan otras dos, en `.enredadera-wrap`:
- **`--ancho-hoja`**, de la que salen a la vez el ancho de la hoja y el hueco
bajo la cabecera, porque las hojas de móvil giran hacia arriba y sobresalen
~0.96 de su ancho por encima de donde se agarran. Si se separan esas dos
medidas, la primera hoja se come el subtítulo. Va contra las **dos**
dimensiones —`min(28vw, 13.6vh)`—: sólo con `vw`, en una pantalla baja las
seis hojas no entran sin deslizar.
- **`--celda`**, el alto de cada `<li>`. Es **más corto que la hoja a
propósito**: las hojas alternan lado, así que se entrelazan como en una
enredadera de verdad. Reparte entre las seis lo que queda tras la cabecera,
el hueco de entrada y lo que la última asoma por abajo, así que la portada
entra sin deslizar de 320x568 para arriba.
El tallo de móvil son **tres** dibujos, no uno: la onda que empieza a la
derecha, su espejo y el remate con espiral de la última celda. La onda es
`x = 50 ± 28·cos(pi·y/100)` en un recuadro de 100x100 que se pinta cuadrado
—`background-size:var(--celda) 100%`—, así que la escala sale uniforme. En los
dos bordes la tangente es vertical: por eso dos celdas seguidas empalman sin
junta, y por eso los extremos caen justo donde se agarra la hoja. Si tocas la
amplitud, toca también el `--centrar` de las hojas, que vale `AM/100`.
---
## Cuidado con `--tam` dentro de una hoja
Las reglas por hoja son de la forma:
```css
.enredadera > li:nth-child(12n+4) .hoja{ --giro:154deg; --tam:0.92; }
```
Ese `.hoja` es un **descendiente**, así que alcanza también a las hojas que
haya dentro del `<li>` —los brotes de REDES— y con especificidad (0,3,1), que
le gana a cualquier `.una-clase`. Poner `--tam` en `.hoja-brote` no hacía
nada: los brotes salían con el `--tam` de REDES, al doble de tamaño, y se
comían a las hojas vecinas.
**Regla:** dentro de un `<li>` de la enredadera, no confíes en `--tam`. Si
necesitas un tamaño propio, escríbelo en la medida, sin `var(--tam)`.
---
## La raíz (los dibujos de la enredadera)
Los dibujos —la bajada desde el título, la franja de ordenador, su remate y el
tallo de móvil— **no están escritos a mano**: los genera un script, porque el
trenzado hay que calcularlo (la perpendicular a lo largo del recorrido).
```sh
cd ../prueba && python3 raiz2.py # regenera raiz2.json
```
Después hay que **reinyectar los SVG en el CSS**. Y lo más importante:
> Los puntos por donde pasa la raíz y las alturas de las que cuelgan las hojas
> (`--y` en las reglas `nth-child(12n+N)`) **tienen que coincidir**. Si mueves
> uno sin mover el otro, las hojas quedan flotando sin tocar la raíz.
Para eso está `../coherencia.py`: compara el CSS contra el JSON y avisa.
**Pásalo siempre después de tocar la raíz.**
### La franja de ordenador: dos filas, no una
La franja es la única parte que se **repite**, porque una página puede tener
más hojas de las que caben en una fila: colectivos tiene doce, o sea dos.
El dibujo lleva **dos filas**: la pasada de izquierda a derecha y, debajo, esa
misma pasada en **espejo** —un `<use>` del mismo grupo, así que no pesa el
doble—. Entra y sale **vertical** por las dos esquinas, de modo que empalma
con su espejo por la derecha y con la copia siguiente por la izquierda. Puesto
con `repeat-y` y `background-size:100% calc(var(--fila) * 2)`, la enredadera
serpentea por las filas que haga falta **sin una sola junta**. Antes el dibujo
era de una fila y remataba en espiral: en colectivos se veían dos ramas
sueltas, la segunda empezando otra vez desde el margen izquierdo.
De ahí salen tres cosas encadenadas:
- La tanda de las reglas por hoja es de **doce** (`nth-child(12n+N)`), no de
seis: las seis de la pasada y las seis del espejo, que son **las mismas
leídas del revés y con el giro cambiado de signo**. `../coherencia.py` lo
comprueba.
- El tallo va en **dos capas**, no en el fondo del `<ul>`: `.enredadera::before`
pinta todas las filas menos la última (`top:0; bottom:var(--fila)`) y
`.enredadera::after` pinta la última con el remate en espiral. Las dos con
`z-index:-1`, que es donde estaba el fondo al que sustituyen.
- Si la última fila es **par**, la pasada va de derecha a izquierda y el remate
se pinta en espejo. Lo decide un `:has()` sobre dónde cae el último `<li>`
dentro de la tanda de doce, así que añadir una hoja sigue sin tocar nada.
### La bajada del título
Es el dibujo más delicado de los tres, porque empalma con dos cosas a la vez:
el título por arriba y la franja por la esquina de abajo a la izquierda.
- Su viewBox es **1000x72** y la caja que lo pinta mide `--bajada`, o sea el
**7.2% de `--rejilla`**. Los dos números van juntos: si cambias uno, cambia
el otro, o el dibujo se deforma —`preserveAspectRatio` es `none`—.
- Las hebras no son `stroke` sino **cintas rellenas** de grosor variable
(`../prueba/cinta.py`), afiladas en la punta y del grosor de la franja —9 y
5.5— en el empalme. Si tocas el grosor de la franja, toca también ése.
- El recorrido llega **vertical** al margen izquierdo: es lo que evita el pico
contra la franja. No lo hagas terminar en diagonal.
- `--fila` descuenta `--bajada` por su nombre. No vuelvas a meterla en una
constante fija: la bajada mide en proporción al ancho, no en `vh`.
---
## El brote de REDES
Al pulsar REDES salen tres hojas pequeñas, una por red. **Cuelgan de un tallo
de verdad**, no flotan: cada `<li>` del brote lleva un `::before` que es su
trozo de tallo, y los tres se encadenan —el primero va hasta el agarre en la
enredadera, el segundo hasta el primero y el tercero hasta el segundo—, así que
lo que se dibuja es **un solo brote con tres hojas**, no tres hojas sueltas.
Dos cifras por hoja bastan, y son las mismas que la colocan leídas del revés:
```css
.brotes > li:nth-child(2){ --largo:0.605; --ang:262.4deg; }
```
- `--largo` va en unidades de `--r` y `--ang` es **absoluto**, porque el
`<li>` no gira (gira la hoja que lleva dentro).
- Por eso lo que se coloca es la **punta del rabillo** —`translate(-50%,0)`,
no `(-50%,-50%)`—: si se colocara el centro de la hoja, el punto donde
empieza el tallo dependería de lo que mida la hoja, que va en `vw`, y el
ángulo no podría ser una constante.
- Si mueves un nudo, recalcula las dos: `--largo` es la distancia al nudo
anterior y `--ang` = `atan2(dx, -dy)`.
En **ordenador** el brote sale hacia abajo y a la izquierda, al hueco que
dejan Eventos y Campañas. En **móvil** no cabe un abanico: el tallo es
vertical y las hojas ocupan los ~130px del centro, así que lo único libre son
las franjas de los lados —62px a 390, 86 a 360, 110 a 320—. Ahí el brote sale
por la **izquierda**, pasa por debajo de la hoja de REDES y **trepa** por el
margen. Antes salía a la derecha y se comía Eventos y Campañas.
---
## El banco de pruebas
```sh
cd ..
python3 coherencia.py # raíz y hojas cuadran
python3 qa.py --breve # las 10 páginas en 15 pantallas
python3 qa.py index.html # una sola, con detalle
```
Puntuación **0 = perfecto**. Mide, en cada página y cada tamaño de pantalla:
- desborde horizontal
- si la portada entra sin deslizar en ordenador
- hojas cortadas por los bordes
- hojas que se pisan entre sí
- etiquetas que se solapan
- **los brotes de REDES**: que no se salgan, no pisen a las hojas, no se
toquen entre ellos y no partan su etiqueta en dos líneas
- **contraste** de todos los textos contra su fondo real, con el umbral de
WCAG AA (4.5, o 3.0 si la letra es grande). Ojo: las fuentes usan `clamp()`
con `vw`, así que un titular puede ser «grande» en ordenador y dejar de
serlo en móvil — por eso se mide en las quince pantallas y no en una
- **contenido perdido** dentro de elementos con `overflow` oculto
- **foco fantasma**: enlaces que se tabulan pero no se ven
- espacio muerto en los bordes
- el menú desplegado: que se alcancen todos los enlaces
### El banco va por CDP, y no es un capricho
`chromium --dump-dom` y `--screenshot` **sujetan la ventana a un mínimo de
~500px**: si pides 320, maqueta a 500. La captura encima engaña, porque
reescala la imagen al ancho pedido y parece buena. Da igual `--headless`,
`--headless=new` o `--headless=old`.
Durante un tiempo el banco midió así, y **las cuatro pantallas de móvil (430,
390, 360 y 320) daban todas la medida de 500**: el 28% de cada barrido era
falso. Por eso `cdp.py` levanta chromium con `--remote-debugging-port` y fija
el viewport con `Emulation.setDeviceMetricsOverride`, que sí respeta cualquier
ancho. Hace falta `--remote-allow-origins=*` o el websocket devuelve 403.
**No vuelvas a medir con `--dump-dom`.** Si un día das con números idénticos
en varios anchos distintos, eso es la señal: el ancho no está cambiando.
Al medir cosas que se animan (los brotes, el menú) hay que **quitar la
transición antes**, o se mide a medio camino. Y `--r` y las demás variables
con `calc()` no se pueden leer con `getPropertyValue`: devuelven el texto. Se
miden creando un testigo con `width:var(--r)`.
### Recorrer el foco
```sh
python3 teclado.py # se usa desde otro script: ver el ejemplo al final
```
Pulsa Tab de verdad por CDP y anota cada parada. Mover `activeElement` con
`.focus()` no vale: no prueba el orden de tabulación ni lo que el navegador se
salta. Va aparte del banco porque cada parada es una ida y vuelta al
navegador y en las 98 mediciones sería eterno.
### Cuidado: la sonda se recorta
`SONDA_JS` no es el texto entero de la sonda: se **recorta** desde la línea
`var d=document.documentElement` hasta donde escribía su resultado. Todo lo
que se ponga fuera de ese trozo **desaparece sin avisar**. Pasó con el apagado
de animaciones: estuvo seis vueltas escrito y sin aplicarse. Al tocar la sonda,
comprobar que lo nuevo está de verdad:
```python
python3 -c "import qa; print('sinAnim' in qa.SONDA_JS)"
```
### La sonda apaga las animaciones
Lo primero que hace es meter `*{animation:none!important}`. La portada tiene
una entrada animada que acaba a los 0.8s y la sonda mide a los 1.1s: medir tan
cerca del final es pedir problemas. Como la animación termina en el estado
normal, apagarla deja justo lo que hay que medir.
### Cuidado al medir cosas que se abren
La barra lateral cierra con `transition: visibility 0s .32s`. Si un bloque de
la sonda la abre y la cierra, **todo lo que se mida después la ve todavía
abierta**. Ha mordido tres veces: al foco, a los brotes y al contraste. Por
eso el bloque del menú y el de los brotes quitan la transición mientras miden
y la reponen al salir. **Si añades una comprobación nueva, no la pongas
detrás de una que abra algo sin ese cuidado.**
Lo que el banco **no** ve, y hay que mirar a ojo:
- Las fichas de colectivo y los dos calendarios.
- **Los avisos de reserva.** El `.gancio-fallback` de `eventos.html` y el
`<noscript>` de `calendario.html` sólo se dibujan cuando el componente
externo no carga o no hay JavaScript. Cuando el banco mide, no están, así
que sus colores y tamaños **no se comprueban solos**. Ahí se coló un
contraste de 3.4:1. Para revisarlos hay que bloquear el script a mano:
```python
nav._envia("Network.enable")
nav._envia("Network.setBlockedURLs", {"urls": ["*rosa.frama.io*"]})
```
## Qué NO tocar
**El IIFE del calendario semanal** (`calendario.html`, dentro de `<script>`).
Lleva los horarios escritos a mano en `WEEKLY` y `COLORS`, y reglas especiales
—todos los viernes Hacklab salvo el segundo que es Art-Lab; Fridays el segundo
y el último viernes—. Para cambiar un horario se editan esos arrays, no el
resto del código.
Y hay un **contrato entre ese script y el CSS**: el script escribe
`style="--bg:...; --fg:..."` en cada píldora de color, y `.cal-pill` del CSS
las lee con `color-mix()`. Esas dos variables no se declaran en ninguna parte
del CSS —sólo se usan—, así que parecen sobrar y no sobran. Si se quitan las
reglas que las usan, las píldoras se quedan sin color.
**El web component de gancio** (`eventos.html`). Se carga desde
`rosa.frama.io` y trae la agenda de `mad.convoca.la`. El identificador del
lugar es `places="42"`, comprobado contra la API.
**No se puede recolorear.** Durante un tiempo el CSS le pasaba once variables
`--calendar-*` y aquí ponía que se tematizaba con ellas. Es falso: el
componente **las redefine dentro de su propio shadow DOM** —una tanda para
`.light` y otra para `.dark`— y esa definición gana a la que herede del host.
Comprobado: se le pasaba `--calendar-surface:#f5faef` y su `.calendar` seguía
calculando `#ffffff`. Tampoco expone ningún `::part`. Las once variables se
quitaron: no hacían nada.
Lo único que se puede hacer es **enmarcarlo desde fuera** (borde, radio,
sombra, fondo), que es lo que hay en la regla `gancio-calendar`, para que la
tarjeta blanca se lea como un panel puesto a propósito.
---
## Cosas pendientes de la asamblea
- **Orden de los colectivos.** Los cinco con la ficha sin escribir (`brota`)
van seguidos al final, así que la segunda fila entera sale desvaída.
Repartidos entre las dos filas se leería como "esta hoja está brotando"
y no como "la mitad de abajo está apagada". El orden lo decide la asamblea.
- **Hacklab**: el párrafo de su ficha lo escribí yo de forma provisional
porque la sección original no tenía ni una línea de texto. Conviene que lo
revise la gente del hacklab y lo ponga en sus palabras.
- **Bicilab**: el texto dice «domingos a las 17:00» y el calendario lo pinta a
las 19:00. Hay que decidir cuál es la hora buena.
- **«Asamblea ext.»** aparece en el calendario semanal pero **no tiene color
asignado en `COLORS`**, así que su píldora sale en blanco y no figura en la
leyenda. Las otras once etiquetas sí. O se le pone color, o se decide que va
neutra a propósito y entonces conviene explicarlo en la leyenda.
- En el calendario la etiqueta pone **«Fridays 🌱»** y en la leyenda
**«Fridays for Future»**. El color sí casa, pero los dos nombres deberían
ser el mismo para que la leyenda se entienda.
- **Ficha de Feminismos**: pone «el derecho a la libre **intervención** del
embarazo». La expresión habitual es «interrupción», y aquí el cambio de
palabra cambia el sentido. No lo he tocado porque el texto es suyo, pero
conviene que lo miren.
- **Ficha de Bicilab**: «Bocata de espaguetis todos los domingos a las 17:00
nos engrasamos las manos y recuperamos tu bicicleta!» son dos frases pegadas
sin puntuación, y a la exclamación le falta el «¡» de apertura. Tampoco lo
he tocado: es su voz.
- Dos botones apuntaban a PDF que no existen (`/archivos/biblioteca.pdf` y
`/archivos/asamblea.pdf`). Los quité y dejé el comentario en el HTML: cuando
subáis los ficheros, se reponen.
- Las campañas tenían seis botones que no llevaban a ninguna parte. Quitados,
con nota en el HTML de lo que prometía cada uno.
---
## La página de error
`404.html` existe, pero **el servidor tiene que saber usarla**. Con nginx:
```nginx
error_page 404 /404.html;
```
Sin esa línea, un enlace roto enseña el error por defecto del servidor y la
página no se usa nunca.
---
## Al compartir el enlace
Todas las páginas llevan `og:title`, `og:description` y `og:image` para que
Mastodon, Telegram o WhatsApp enseñen una vista previa decente. La imagen es
`INFO/cso_laenre.jpeg`, la foto de la fachada con el cartel pintado a mano.
**Una pega conocida:** la `og:image` va en **ruta relativa**, porque el
dominio propio del sitio no aparece escrito en ningún fichero —todos los
enlaces internos son relativos y las únicas URLs absolutas son de subdominios
(`hacklab.laenre.net`, `okupanel.laenre.net`)—. La mayoría de plataformas la
resuelven, pero **lo seguro es ponerla absoluta**. Cuando confirméis el
dominio, cambiad las ocho líneas `og:image` por la URL completa.
---
## Accesibilidad
Se ha cuidado y medido:
- Contrastes AA en todos los textos.
- Anillo de foco visible sobre cualquier fondo (en rosa era invisible sobre
las propias hojas).
- Nada oculto se puede tabular.
- Áreas de toque medidas en las ocho páginas: **todo lo pulsable llega a los
24px** que pide WCAG 2.5.8, y casi todo a 44. Lo que queda por debajo son
enlaces dentro de una frase, exentos por la norma.
Ojo con la píldora del calendario en móvil: se **ve** de 10px pero **mide**
24, gracias a un relleno vertical con `background-clip:content-box`. Necesita
`box-sizing:content-box`, o el `border-box` global se come la altura y la
barra desaparece.
- `:active` en todo lo pulsable: en un móvil no hay `:hover` y sin él el toque
no responde. `.tarjeta` queda fuera a propósito: es un `div` que no lleva a
ninguna parte.
- `color-scheme` declarado: `light` en el sitio, `dark` en el Tattoo Circus.
Sin eso, un navegador en tema oscuro pinta en oscuro lo que el CSS no
controla (barras de desplazamiento, controles, el lienzo inicial).
- Las imágenes de contenido llevan texto alternativo que **describe la
imagen**, no que repite el nombre del colectivo. Si un enlace es sólo una
imagen, el nombre del enlace va en un `aria-label` que dice **a dónde va**:
el `alt` describe el dibujo, y para un enlace eso no sirve.
- El logo de la barra lateral va con `alt=""`, decorativo a propósito: justo
debajo hay un `<h2>` que ya dice «La Enredadera», y con `alt` puesto, cuando
la imagen no cargaba, el texto se dibujaba **recortado** dentro de la caja de
48px.
- Se respeta `prefers-reduced-motion`, **y también el retardo**: la entrada
de la enredadera va escalonada, y anular sólo la duración dejaba las hojas
invisibles hasta 0.3s para luego aparecer de golpe.
Límite conocido: al ser CSS puro, **las fichas no se cierran con Escape** y el
contenido de detrás sigue siendo tabulable. Se cierran con la ✕, pulsando
fuera o con el botón atrás del navegador. Arreglarlo exigiría JavaScript.