/* ═══════════════════════════════════════════════════════════════════════════
   SISTEMA DE DISEÑO — Rapthor Fitness Festival
   ═══════════════════════════════════════════════════════════════════════════

   Fuente única de verdad de la apariencia. La consumen las DOS páginas del repo:
   `sistema-de-diseno.html` (el catálogo) y `chat.html` (el producto). Ninguna de
   las dos define un color ni un tamaño propio — si aparece un valor suelto en un
   HTML, es un error, no una excepción.

   Es un archivo separado y no CSS embebido como en biosalud-bot y en
   aguamadre-agente. Esos repos sirven UNA página, así que embeber no duplica
   nada. Acá hay dos, y el catálogo sólo sirve para decidir el producto si las
   dos leen exactamente los mismos valores.

   Los valores NO salen de una paleta inventada ni de mirar Instagram: salen del
   archivo de Illustrator del diseñador, que el cliente mandó el 10-sep-2026. Son
   los rellenos que él escribió. El detalle, con ratios de contraste, licencias de
   tipografía y proporciones medidas, está en docs/referencias-visuales.md. Ese
   documento manda sobre este archivo.

   SI ENCONTRÁS #ACF62F O #93F700 EN ALGÚN LADO, ES CÓDIGO VIEJO. La primera
   versión de esta hoja se dedujo de JPEG de Instagram a 640 px, que corre cada
   canal. #ACF62F es el verde real corrido un punto por canal, y #93F700 es el
   mismo verde corrido para el otro lado: NO es un segundo verde y no existe en
   la marca.

   LAS TRES REGLAS QUE ORDENAN TODO
     1. El verde ácido ocupa entre el 4 % y el 10 % de la superficie. Es acento,
        nunca fondo de lectura. Por encima del 10 % la interfaz deja de parecer
        de la marca y pasa a parecer una textura.
     2. Hay UN color secundario, el verde primavera, y HOY LA INTERFAZ NO LO USA:
        el cliente lo sacó del renglón de la fecha el 14-sep-2026 y pidió el
        ácido. Queda como ficha, sin un solo uso. Si alguna vez vuelve, vuelve
        como rastro —nunca por encima del 0,4 %, que es como la marca lo usa en
        cinco de sus seis páginas— y a UN elemento, no a dos.
     3. Tema oscuro, y no es una preferencia. La marca no tiene una sola pieza
        sobre fondo claro, y su verde sobre blanco da contraste 1,33. Un tema
        claro sería una decisión de diseño nueva, no una lectura de la marca.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ─── 0. Tipografías ────────────────────────────────────────────────────── */

/* LAS TRES SON DEL CLIENTE Y SE SIRVEN DESDE ACÁ, no desde Google Fonts.
   Antes esta hoja dependía de dos `preconnect` y un pedido bloqueante a
   fonts.googleapis.com. Quien entra desde Instagram con datos móviles pagaba
   ese viaje antes de ver la primera letra. Las tres subconjuntadas a Latin-1
   pesan 53 KB, que es menos que el viaje.

   SON LAS TRES QUE SE PUEDEN SERVIR. El cliente mandó ocho; las otras cinco
   —Akzidenz-Grotesk Next, American Typewriter, Andale Mono, Myriad Pro y
   Slaters Duct Tape— son comerciales, y dos de ellas traen el `fsType` en 4,
   que prohíbe incrustarlas. Si hace falta una de ésas para un titular, va como
   trazo SVG y no como fuente. El detalle, con la licencia de cada una, está en
   docs/referencias-visuales.md.

   HAY QUE REGISTRARLAS EN src/rapthor/estaticos.ts. Ese módulo es un ALLOWLIST,
   no un passthrough: una fuente que no esté listada da 404, el navegador cae a
   la del sistema y no queda nada en el log que lo explique.

   Se regeneran con tools/generar-fuentes.sh. La licencia OFL obliga a que el
   aviso de copyright acompañe a la fuente: está en public/fuentes/OFL.txt y no
   se borra. */

@font-face {
  font-family: "Anton";
  src: url("/fuentes/anton.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

/* Las dos variables declaran el RANGO de pesos en una sola cara. Escrito como
   un peso suelto, el navegador sintetiza los demás engordando el trazo.

   EL `format()` DICE `woff2` Y NO `woff2-variations`, a propósito. Aquél es la
   grafía previa a CSS Fonts 4 y un motor que no la reconozca DESCARTA EL `src`
   entero y cae a la letra del sistema, en silencio. No hay nada que ganar: no
   existe una cara no variable de reserva que haga falta distinguir. */
@font-face {
  font-family: "Antonio";
  src: url("/fuentes/antonio-variable.woff2") format("woff2");
  font-weight: 100 700;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Source Code Variable";
  src: url("/fuentes/source-code-variable.woff2") format("woff2");
  font-weight: 200 900;
  font-style: normal;
  font-display: swap;
}

/* ─── 1. Fichas (design tokens) ─────────────────────────────────────────── */

:root {
  /* Color. Los dos verdes son rellenos declarados del archivo del diseñador.

     EL VERDE ÁCIDO ES LO QUE SE TOCA — más la fecha. Botones, chips, foco del
     teclado, y el renglón `17 Y 18 DE OCTUBRE` del encabezado, que lo pidió el
     cliente el 14-sep-2026. Nada más. Cuando también marcaba el dato destacado
     dentro de una burbuja, quien barría la pantalla no podía saber qué era
     tocable; la fecha no reabre ese problema porque vive en el encabezado y los
     controles en la conversación, que nadie barre juntas. Un control en el
     encabezado sí lo reabriría.

     EL VERDE PRIMAVERA NO SE USA. Era el color de la fecha hasta el 14-sep-2026
     y el cliente lo sacó, así que el secundario de la marca no aparece en el
     producto. La ficha queda porque es un valor real de su archivo, no porque
     algo la lea. Si alguna vez vuelve: a UN elemento y como rastro —en sus seis
     páginas nunca pasa del 0,35 % del cuadro—. El porqué del lugar que tenía
     está en docs/referencias-visuales.md; no lo repongas sin preguntarle.

     SI CAMBIÁS EL VERDE ÁCIDO, EL ÍCONO DE LA PESTAÑA NO SE ENTERA. `favicon.ico`
     y `apple-touch-icon.png` son mapas de bits: el color está horneado adentro.
     Hay que cambiar `VERDE` en tools/generar-favicon.py y volver a correrlo.
     `tools/generar-favicon.py --check` avisa si quedaron desfasados. */
  --rp-verde: #abf52e;
  --rp-verde-primavera: #00de80;
  /* Los componentes del canal alfa, para las capas translúcidas. Sin esto, el
     verde se retipeaba como rgba(171, 245, 46, …) en cinco reglas y --rp-verde
     dejaba de ser la fuente única del color. */
  --rp-verde-rgb: 171 245 46;
  --rp-fondo-rgb: 18 18 18;

  /* Hover del primario. Se calcula A PARTIR del verde y no es una ficha suelta:
     antes era #93F700, un «verde vivo» que resultó ser el mismo verde corrido
     por la compresión de Instagram. La marca no tiene un segundo tono del
     ácido, así que el hover se saca aclarando el único que existe. */
  --rp-verde-hover: color-mix(in srgb, var(--rp-verde) 86%, #ffffff);

  --rp-fondo: #121212;      /* fondo de la aplicación */
  /* El telón detrás de la ventana. Es negro puro y NO `--rp-fondo`: son dos
     superficies distintas, y el contraste entre las dos es lo que hace que la
     ventana se lea como una ventana y no como la página entera. */
  --rp-telon: #000000;
  --rp-superficie: #1c1c1c; /* superficies elevadas: burbujas, tarjetas, campo */
  --rp-superficie-alta: #232323; /* hover y filas alternadas */
  --rp-linea: #2e2e2e;
  --rp-linea-fuerte: #3d3d3d;

  --rp-texto: #ffffff;       /* 18,73 sobre el fondo */
  --rp-texto-mudo: #8f8f8f;  /* 5,32 sobre el fondo: pasa AA en cuerpo normal */
  --rp-texto-tenue: #5f5f5f; /* sólo texto legal y marcas de agua */

  /* Tipografía. Las tres son del cliente, verificadas contra el archivo original
     del diseñador: cada una cumple acá el mismo papel que cumple en su carpeta.

     EL CUERPO ES MONOESPACIADO, Y YA NO ESTÁ A PRUEBA: LO ELIGIÓ EL CLIENTE EL
     14-sep-2026. La marca compone su texto corrido en `Source Code Variable`
     —tres páginas de párrafos—, así que la conversación habla con su letra.
     Cuesta 29 caracteres por renglón contra los 47 de una condensada, y eso se
     sabía al elegirla: entre el 11 y el 14 de septiembre las dos estuvieron
     servidas lado a lado, en `/mono` y `/antonio`, cada una llevada a su propio
     punto de interlineado y tracking. Ganó ésta. La comparación con sus números
     quedó en docs/referencias-visuales.md.

     NO LE BAJES EL CUERPO PARA RECUPERAR ANCHO. Su altura de x es 0,486 em
     contra 0,732 em de Antonio: se ve MÁS CHICA al mismo tamaño, así que
     achicarla para ganar caracteres empeora las dos cosas a la vez.

     Y no hay ficha `--rp-mono`. La había cuando el cuerpo era condensado y hacía
     falta una monoespaciada aparte para la tabla de kilos; ahora serían la misma
     familia escrita dos veces. Lo que alinea la columna es `tabular-nums`. */
  --rp-display: "Anton", "Arial Narrow", sans-serif;
  --rp-rotulo: "Antonio", "Arial Narrow", sans-serif;
  --rp-fuente: "Source Code Variable", ui-monospace, "SF Mono", Menlo, monospace;

  /* Escala tipográfica. La base es 16,5 y no 16 desde que el cuerpo era una
     condensada. Se mantiene con la monoespaciada por otro motivo: bajarla
     agrava el problema de ancho, y subirla también. */
  --rp-t-xs: 12px;
  --rp-t-sm: 14px;
  --rp-t-base: 16.5px;
  --rp-t-lg: 19px;
  --rp-t-xl: 24px;
  --rp-t-2xl: 34px;

  /* Interlineado y tracking del texto de conversación. SON FICHAS PORQUE
     DEPENDEN DE LA FAMILIA, no del gusto: cada letra se lleva a su propio punto
     y los dos puntos quedan en lados opuestos. La condensada pide MÁS aire —sus
     renglones entran 47 caracteres contra 29, así que el ojo viaja más para
     encontrar el siguiente, y sus letras angostas llegan con el espacio entre
     ellas ya reducido—. La monoespaciada pide MENOS: su avance fijo deja tanto
     aire lateral que el párrafo se ve desparramado, y es lo que el cliente
     señaló el 11-sep-2026.

     ESTOS SON LOS DE LA MONOESPACIADA, que es la letra que eligió el cliente el
     14-sep-2026. Los de Antonio se fueron con la prueba; quedaron anotados en
     docs/referencias-visuales.md por si alguna vez se reabre.

     EL TRACKING NEGATIVO NO ES UN RETOQUE DE GUSTO: A −0,015 em LA RESPUESTA
     PIERDE UN RENGLÓN ENTERO. Medido el 12-sep-2026 sobre la misma respuesta de
     232 caracteres, a 420 px de ancho: de 26 caracteres por renglón se pasa a
     29, la burbuja baja de 256 px a 212 px y la respuesta entra en 8 renglones
     en vez de 9.

     Y NO SIGAS BAJÁNDOLO. A −0,03 em la burbuja mide los mismos 220 px y los
     mismos 10 renglones que a −0,015 em: el ancho ya está todo ganado, así que
     de ahí en adelante sólo se paga rejilla. La rejilla pareja es lo que hace
     que al cliente le guste esta letra; gastarla sin comprar nada es el peor
     negocio posible. El banco que lo mide no necesita al modelo —inyecta el
     texto en la burbuja— así que rehacer la medición es gratis.

     1,22 ES EL PISO DEL INTERLINEADO, Y ES UN PISO DE COLISIÓN, NO DE GUSTO.
     Más abajo los acentos de un renglón empiezan a tocar las colas del de
     arriba: a 1,18 «septiembre» y «el público» del renglón siguiente ya se
     rozan. El cliente pidió apretarla más el 14-sep-2026 y se bajó hasta el
     último escalón que despeja. */
  --rp-interlineado: 1.22;
  --rp-tracking-cuerpo: -0.015em;

  /* Espaciado: escala de 4. */
  --rp-e-1: 4px;
  --rp-e-2: 8px;
  --rp-e-3: 12px;
  --rp-e-4: 16px;
  --rp-e-5: 22px;
  --rp-e-6: 32px;

  /* Radio: casi recto. Su marca no tiene una sola esquina blanda. */
  --rp-radio: 4px;
  --rp-radio-chico: 2px;

  /* Las dos formas de la malla del telón. Se usan como `clip-path` en el
     indicador de espera, que va de una a la otra.
     EL TRIÁNGULO LLEVA CUATRO VÉRTICES, dos pisados en el pico. Con la misma
     cantidad de vértices que el cuadrado el navegador interpola de uno al otro;
     escrito con tres, el `clip-path` no anima y las formas saltan. */
  --rp-cuadrado: polygon(0 0, 100% 0, 100% 100%, 0 100%);
  --rp-triangulo: polygon(50% 0, 50% 0, 100% 100%, 0 100%);

  /* Alto de la pintura. Vive acá y no en `.rp-pintura` porque el aire superior
     de los mensajes se calcula CONTRA él: escrito en dos lados, cambiar uno
     metía el primer mensaje debajo de la pintura. */
  --rp-pintura-alto: 48px;

  /* Cuánto sube el acento de esquina desde el piso de la ventana. Es la altura
     de la zona de escritura más un respiro: por debajo de eso el brochazo se
     mete detrás del campo de texto, que es opaco, y queda cortado en seco. */
  --rp-acento-piso: 76px;

  /* Cuánto SIGUE el velo después del borde inferior del brochazo. Es la
     distancia en la que un renglón que sube con el scroll se disuelve contra el
     fondo. El alto del velo y el aire de los mensajes salen los dos de acá: ver
     el bloque de `.rp-marca--pintura::after`, que explica qué se rompía sin
     esto. */
  --rp-velo-caida: 36px;
  --rp-velo-alto: calc(var(--rp-pintura-alto) + var(--rp-velo-caida));

  --rp-transicion: 0.12s ease;
}

/* ─── 2. Base ───────────────────────────────────────────────────────────── */

*, *::before, *::after { box-sizing: border-box; }

html, body {
  margin: 0;
  background: var(--rp-fondo);
  color: var(--rp-texto);
  font-family: var(--rp-fuente);
  font-size: var(--rp-t-base);
  line-height: var(--rp-interlineado);
  -webkit-font-smoothing: antialiased;
}

/* Foco visible en verde: es un indicador de estado, que es exactamente para lo
   que la marca usa el acento. No se quita nunca. */
:focus-visible {
  outline: 2px solid var(--rp-verde);
  outline-offset: 2px;
}

/* ─── 3. Textura de marca ───────────────────────────────────────────────── */

/* Evoca la trama de rejilla de sus placas. Las líneas van a 3,5 % de opacidad,
   que equivale a una capa de #121212 al 96 % — muy por encima del 85 % mínimo
   que exige la regla de textura. Su textura a fuerza plena detrás de un párrafo
   destruye la legibilidad; ésta es una insinuación, no la textura. */
.rp-textura::before {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  background-image:
    repeating-linear-gradient(115deg, rgb(var(--rp-verde-rgb) / 0.035) 0 1px, transparent 1px 84px),
    repeating-linear-gradient(25deg, rgba(255, 255, 255, 0.022) 0 1px, transparent 1px 84px);
}

/* ─── 4. Tipografía ─────────────────────────────────────────────────────── */

/* Titular de marca. Va SIEMPRE en mayúsculas: es como escribe la marca.
   Nunca se usa para texto de conversación — un asistente que responde en
   mayúsculas se lee como un grito. */
.rp-titular {
  font-family: var(--rp-display);
  font-size: var(--rp-t-2xl);
  line-height: 1.05;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  margin: 0;
}
.rp-titular--sm { font-size: var(--rp-t-xl); }
.rp-titular--verde { color: var(--rp-verde); }

/* Etiqueta: la línea corta en mayúsculas que rotula un bloque. */
.rp-etiqueta {
  font-family: var(--rp-display);
  font-size: var(--rp-t-sm);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--rp-verde);
  margin: 0;
}

.rp-mudo { color: var(--rp-texto-mudo); }
.rp-tenue { color: var(--rp-texto-tenue); font-size: var(--rp-t-xs); }

/* ─── 5. Marca ──────────────────────────────────────────────────────────── */

.rp-marca {
  display: flex;
  align-items: center;
  gap: var(--rp-e-3);
  padding: 13px var(--rp-e-4);
  /* Acá vivía un apaño: el wordmark viejo era un recorte con SU PROPIO fondo
     negro, así que la franja tenía que pintarse del mismo negro para tapar la
     costura. El logotipo del cliente tiene transparencia real y el apaño se
     cayó. No hace falta volver a igualar ningún negro. */
  background: var(--rp-fondo);
  border-bottom: 2px solid var(--rp-verde);
}
/* MÁS ALTO QUE EL WORDMARK VIEJO, y no es capricho. Aquél era un recorte
   ajustado de dos renglones de texto; el logotipo del cliente es un lockup
   apaisado de 3:1 donde la letra ocupa poco más de la mitad del alto, con el
   brochazo abajo. A 30 px la palabra «RAPTHOR» quedaba en 9 px y el numeral
   `26` no se leía. Los tres altos de esta hoja se subieron en la misma
   proporción. */
.rp-marca img { height: 40px; width: auto; display: block; }
.rp-marca-datos { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.rp-nueva-conversacion {
  align-self: flex-start;
  margin-top: var(--rp-e-1);
  min-height: 32px;
  padding: 4px 10px;
  border: 1px solid var(--rp-linea-fuerte);
  border-radius: var(--rp-radio);
  background: var(--rp-fondo);
  color: var(--rp-texto);
  font-family: var(--rp-rotulo);
  font-size: var(--rp-t-sm);
  line-height: 1.2;
  cursor: pointer;
}
.rp-nueva-conversacion:hover { border-color: var(--rp-verde); }

.rp-marca-fecha {
  font-family: var(--rp-display);
  font-size: 17px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  /* VERDE ÁCIDO, POR DECISIÓN DEL CLIENTE DEL 14-sep-2026. Lo vio andando y
     pidió «el verde de siempre».

     ACÁ HABÍA VERDE PRIMAVERA Y NO ERA UN DESCUIDO: es donde la marca pone su
     color secundario —el sello circular «17 y 18 DE OCTUBRE» de la página 4 de
     su carpeta— y la fecha es un dato, no un control. No lo repongas sin
     preguntarle; el razonamiento sigue escrito en docs/referencias-visuales.md,
     que es lo que lo hace fácil de «corregir» por error.

     LO QUE COSTÓ, PARA QUE ESTÉ DICHO. Éste era el ÚNICO verde primavera de la
     interfaz, así que el color secundario de la marca ya no aparece en el
     producto: queda como ficha y como muestra del catálogo, sin uso. Y el ácido
     vuelve a significar dos cosas —«se toca» y la fecha—, que es el defecto que
     se arregló el 10-sep-2026 sacándole el ácido al dato destacado. Acá pesa
     menos que allá porque la fecha vive en el encabezado y los chips en la
     conversación, que son dos zonas que nadie barre juntas, pero si algún día
     aparece un control en el encabezado la ambigüedad vuelve en serio. */
  color: var(--rp-verde);
  line-height: 1.1;
}
.rp-marca-sede {
  /* Rótulo, no texto de lectura: es un dato del encabezado. En monoespaciada
     «Cancha auxiliar del Estadio Kempes · Córdoba» no entra en 390 px y se
     cortaba con puntos suspensivos. */
  font-family: var(--rp-rotulo);
  font-size: var(--rp-t-sm);
  /* INTERLINEADO PROPIO, Y NO EL DEL CUERPO. Era el único rótulo que no lo
     declaraba, así que heredaba `--rp-interlineado` —la ficha de la
     CONVERSACIÓN— y el encabezado cambiaba de alto cada vez que se tocaba el
     interlineado de los mensajes. Son dos cosas sin relación: esto es un rótulo
     de una línea. Se descubrió con la prueba de letra, donde el encabezado
     quedaba 6 px más alto en una variante que en la otra.
     1,45 es el valor que tenía de hecho antes de que el interlineado fuera una
     ficha, así que esto no mueve nada; sólo deja de moverse solo. */
  line-height: 1.45;
  color: var(--rp-texto-mudo);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Isotipo: el cuadrado verde con la R. Es el único verde constante de una
   conversación, y alcanza para marcar quién habla sin teñir ninguna burbuja.
   El selector incluye `.cc-avatar`, que es el nodo vacío que antepone el runtime
   del núcleo a cada fila del bot: es el MISMO componente, y escrito dos veces se
   habría separado en cuanto alguien tocara uno de los dos. */
.rp-isotipo,
.cc-avatar {
  flex: 0 0 28px;
  width: 28px;
  height: 28px;
  background: var(--rp-verde);
  border-radius: var(--rp-radio);
  position: relative;
}
/* EL ISOTIPO VERDADERO, no una letra imitada. Hasta el 10-sep-2026 acá había
   una «R» de Anton inclinada 8°, porque el isotipo del cliente sólo existía
   pegado al numeral `26` y ningún recorte lo separaba. Su archivo de Illustrator
   lo trae en vector y son dos trazos: assets/marca/isotipo-r.svg.

   VA COMO MÁSCARA Y NO COMO IMAGEN a propósito. Así el color sale de la ficha
   del CSS y no del relleno del SVG — con un `background-image` habría un color
   de marca escrito adentro de un archivo que esta hoja no controla, que es
   justamente lo que el esquema de una-sola-fuente existe para evitar.

   El alto en porcentaje, y el ancho automático: el isotipo es más alto que
   ancho (66 × 108) y forzarlo a llenar el cuadrado lo deformaría. */
.rp-isotipo::after,
.cc-avatar::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--rp-fondo);
  -webkit-mask: url("/assets/marca/isotipo-r.svg") center / auto 74% no-repeat;
  mask: url("/assets/marca/isotipo-r.svg") center / auto 74% no-repeat;
}
/* Sin tamaño propio para la variante grande: la máscara escala con la caja. La
   versión de letra necesitaba un `font-size` aparte acá. */
.rp-isotipo--lg { flex-basis: 56px; width: 56px; height: 56px; }

/* ─── 6. Controles ──────────────────────────────────────────────────────── */

/* Botón primario: verde pleno con texto casi-negro. Es un CONTROL, no una
   superficie de lectura, así que el verde de fondo acá sí corresponde. */
.rp-boton {
  display: inline-flex;
  align-items: center;
  gap: var(--rp-e-2);
  padding: 9px var(--rp-e-4);
  border: 1px solid var(--rp-verde);
  border-radius: var(--rp-radio);
  background: var(--rp-verde);
  color: var(--rp-fondo);
  /* Antonio y no la del cuerpo: la etiqueta de un control es un RÓTULO, y en
     monoespaciada las mayúsculas con espaciado se estiran hasta desbordar el
     botón en pantalla chica. */
  font-family: var(--rp-rotulo);
  font-weight: 700;
  font-size: var(--rp-t-sm);
  letter-spacing: 0.05em;
  text-transform: uppercase;
  text-decoration: none;
  cursor: pointer;
  transition: background var(--rp-transicion);
}
.rp-boton:hover { background: var(--rp-verde-hover); border-color: var(--rp-verde-hover); }

.rp-boton--fantasma { background: transparent; color: var(--rp-verde); }
.rp-boton--fantasma:hover { background: var(--rp-verde); color: var(--rp-fondo); }

.rp-boton:disabled,
.rp-boton[aria-disabled="true"] { opacity: 0.35; cursor: default; pointer-events: none; }

/* ─── 7. Superficies ────────────────────────────────────────────────────── */

.rp-tarjeta {
  background: var(--rp-superficie);
  border: 1px solid var(--rp-linea);
  border-radius: var(--rp-radio);
  padding: var(--rp-e-4);
}

/* ─── 8. Componentes de dato ────────────────────────────────────────────── */
/* OJO — HOY SON DEL CATÁLOGO, NO DEL PRODUCTO. El runtime del núcleo renderiza
   lo que escribe el modelo con `formatearMarkdown`, que entiende sólo
   `**negrita**`, `[texto](https://…)` y saltos de línea: no hay tablas, no hay
   títulos, y una URL pelada no se convierte en enlace. Para que estas clases
   lleguen a una burbuja hace falta el gancho `renderTarjeta` de `montarChat`,
   que `chat.html` todavía no pasa.
   Mientras tanto el system prompt le prohíbe al modelo emitir tablas de markdown
   —se verían como barras y numerales crudos— y le pide una línea por valor. No
   le pidas al prompt que devuelva una tabla creyendo que este CSS la va a tomar. */
/* Éstos son los que el bot devuelve dentro de una burbuja. Son la parte del
   sistema que resuelve el producto y no la marca: la consulta más frecuente
   después de «cómo me inscribo» es una TABLA de diecisiete filas, y una tabla
   de diecisiete filas en un teléfono de 390 px es un problema de diseño real. */

/* 8.1 Tabla de parámetros de una categoría.
   Dos columnas y no una grilla completa: la tabla de la planilla tiene ocho
   columnas, pero una persona pregunta por UNA categoría. Mostrarle las ocho la
   obliga a encontrar su columna, que es exactamente el error que el asistente
   tiene que evitarle. */
.rp-parametros { width: 100%; border-collapse: collapse; font-size: var(--rp-t-sm); }
.rp-parametros caption {
  font-family: var(--rp-display);
  font-size: var(--rp-t-sm);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--rp-verde);
  text-align: left;
  padding-bottom: var(--rp-e-2);
}
.rp-parametros th,
.rp-parametros td { padding: 6px var(--rp-e-2); text-align: left; }
.rp-parametros th { color: var(--rp-texto-mudo); font-weight: 500; }
.rp-parametros td { font-variant-numeric: tabular-nums; }
.rp-parametros tbody tr:nth-child(odd) { background: rgba(255, 255, 255, 0.03); }
.rp-parametros td:last-child { text-align: right; white-space: nowrap; }

/* La salvedad del cliente. Va SIEMPRE con la tabla: los parámetros son topes, y
   una atleta que lo entienda al revés se anota en la categoría equivocada. */
.rp-salvedad {
  margin: var(--rp-e-3) 0 0;
  padding-left: var(--rp-e-3);
  border-left: 2px solid var(--rp-verde);
  font-size: var(--rp-t-sm);
  color: var(--rp-texto-mudo);
}

/* 8.2 Lista de categorías. */
.rp-categorias { display: flex; flex-direction: column; gap: 1px; margin: 0; padding: 0; list-style: none; }
.rp-categoria {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--rp-e-3);
  padding: 7px var(--rp-e-2);
  background: rgba(255, 255, 255, 0.03);
  font-size: var(--rp-t-sm);
}
.rp-categoria-nombre { font-weight: 600; letter-spacing: 0.01em; }
.rp-categoria-formato { color: var(--rp-texto-mudo); white-space: nowrap; }

/* 8.3 Tarjeta de derivación a una plataforma de terceros.
   Es el alcance central de lo vendido. El enlace es lo único que importa, así
   que es lo único con peso visual. */
.rp-derivacion {
  display: flex;
  flex-direction: column;
  gap: var(--rp-e-2);
  padding: var(--rp-e-3);
  background: rgba(255, 255, 255, 0.04);
  border-left: 2px solid var(--rp-verde);
  border-radius: var(--rp-radio-chico);
}
.rp-derivacion-destino {
  font-family: var(--rp-display);
  font-size: var(--rp-t-lg);
  letter-spacing: 0.03em;
  text-transform: uppercase;
}
.rp-derivacion-que { font-size: var(--rp-t-sm); color: var(--rp-texto-mudo); }

/* 8.4 WOD. Bloque monoespaciado: un WOD es una lista de repeticiones y pesos
   alineados, y proporcional los desalinea. */
.rp-wod {
  background: rgba(0, 0, 0, 0.35);
  border: 1px solid var(--rp-linea);
  border-radius: var(--rp-radio-chico);
  padding: var(--rp-e-3);
}
.rp-wod-cabecera {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--rp-e-2);
  margin-bottom: var(--rp-e-2);
}
.rp-wod-nombre {
  font-family: var(--rp-display);
  font-size: var(--rp-t-lg);
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--rp-verde);
}
.rp-wod-dia { font-size: var(--rp-t-xs); color: var(--rp-texto-mudo); text-transform: uppercase; letter-spacing: 0.06em; }
/* Sin `font-family`: hereda la del cuerpo, que YA es la monoespaciada. Lo
   propio del WOD es el `pre-wrap`, que respeta los saltos de renglón con que
   viene escrito.
   SI GANA ANTONIO EN LA PRUEBA DE LA SECCIÓN 12, esto deja de alcanzar: la
   columna de repeticiones y kilos se alinea porque la letra es de ancho fijo, y
   con una proporcional se desarma. Ahí hay que escribirle la familia acá. */
.rp-wod-cuerpo { font-size: var(--rp-t-sm); line-height: 1.6; white-space: pre-wrap; margin: 0; }

/* 8.5 Aviso de dato pendiente.
   El estado más frecuente de este bot durante semanas: el festival cierra
   parámetros, horarios y precios sobre la fecha. Tiene componente propio para
   que «todavía no se publicó» se lea como una respuesta y no como una falla.
   No lleva verde: no es un estado bueno ni malo, y la marca no tiene color
   semántico con el que teñirlo. */
.rp-pendiente {
  display: flex;
  gap: var(--rp-e-3);
  padding: var(--rp-e-3);
  background: rgba(255, 255, 255, 0.03);
  border: 1px dashed var(--rp-linea-fuerte);
  border-radius: var(--rp-radio-chico);
  font-size: var(--rp-t-sm);
  color: var(--rp-texto-mudo);
}
.rp-pendiente::before { content: "—"; color: var(--rp-texto-tenue); }

/* 8.6 Aviso de derivación a una persona. */
.rp-handoff {
  padding: var(--rp-e-3);
  background: rgb(var(--rp-verde-rgb) / 0.07);
  border-left: 2px solid var(--rp-verde);
  border-radius: var(--rp-radio-chico);
  font-size: var(--rp-t-sm);
}
.rp-handoff-titulo {
  font-family: var(--rp-display);
  font-size: var(--rp-t-sm);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--rp-verde);
  display: block;
  margin-bottom: var(--rp-e-1);
}

/* ─── 8bis. La pintura ─────────────────────────────────────────────────── */
/* El filo de la franja de marca es un BROCHAZO RASGADO, no una línea recta.
   `assets/marca/brochazo.svg` es el trazo que va debajo del logotipo en el
   archivo del diseñador: un solo `<path>`, con el borde de abajo desgarrado
   como pintura de brocha ancha.

   ACÁ HUBO UN GOTEO Y SE SACÓ EL 10-sep-2026. Era verde chorreando en hilos con
   gotas que se desprendían, dibujado por JS con un filtro gooey. Estaba bien
   hecho y salía del material de la marca, pero **al cliente no le gustó**. No
   volver a proponerlo: la decisión no es técnica. El dibujo, con su generador y
   sus tres invariantes, está en el historial —`git log -- public/goteo.js`— por
   si alguna vez cambia de opinión.

   POR QUÉ ESTIRARLO NO LO ROMPE, cosa que al goteo sí. Aquél eran gotas, y una
   gota achatada deja de leerse como líquido: su alto y su viewBox tenían que
   guardar la misma relación. Un brochazo es una mancha irregular sin forma que
   reconocer, así que aplastarlo de 645x200 —su viewBox— a lo que mida la franja
   lo deja como lo que es, un filo de pintura. Por eso la máscara va con `100% 100%`.

   REGRESIÓN CONOCIDA — `mask-size: 100% 100%` NO ALCANZA PARA ESTIRAR UN SVG.
   El SVG conserva su propia proporción por dentro: `mask-size` le da la caja y
   él encaja su viewBox adentro, centrado y con aire a los costados. El síntoma
   es mudo — el estilo computado dice `100% 100%` y todo parece bien — y se ve
   como un brochazo chico en el medio de una franja vacía. Lo que lo arregla es
   `preserveAspectRatio="none"` ADENTRO de brochazo.svg. Si alguna vez se
   regenera ese archivo desde el `.ai`, hay que volver a ponérselo.
   Los otros dos SVG de `assets/marca/` NO lo llevan y no deben llevarlo: el
   isotipo y el acento de esquina se escalan con `contain` y deformarlos los
   arruinaría. */

.rp-pintura {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  height: var(--rp-pintura-alto);
  pointer-events: none;
  background: var(--rp-verde);
  /* Como máscara y no como imagen, igual que el isotipo y el acento de esquina:
     el color sale de la ficha del CSS y no del relleno del SVG. */
  -webkit-mask: url("/assets/marca/brochazo.svg") center bottom / 100% 100% no-repeat;
  mask: url("/assets/marca/brochazo.svg") center bottom / 100% 100% no-repeat;
  /* Se pinta de izquierda a derecha, como una pasada de brocha. Es corto a
     propósito: la pintura ya está puesta cuando alguien termina de leer el
     encabezado. */
  animation: rp-pincelada 0.5s cubic-bezier(0.32, 0.72, 0.4, 1) both;
}

@keyframes rp-pincelada {
  from { clip-path: inset(0 100% 0 0); }
  to   { clip-path: inset(0 0 0 0); }
}

/* La pintura cuelga POR FUERA de la franja, sobre el área de mensajes, así que
   la franja tiene que ganarle en apilado a `#chat`, que viene después en el DOM.
   El selector lleva `.rp-ventana >` a propósito: `.rp-ventana > *` de más abajo
   le pone z-index 1 a todos los hijos y, a igual especificidad, gana el último
   — con un `.rp-marca` pelado la pintura quedaba tapada y no se veía nada. */
.rp-marca { position: relative; }
/* `.muestra .rp-marca` está acá y no en el catálogo a propósito. El velo va con
   `z-index: -1` y necesita que la franja abra contexto de apilado propio; en el
   producto se lo da `.rp-ventana >`, pero la muestra suelta del catálogo no vive
   adentro de una ventana. Sin esto el catálogo apila distinto que el producto,
   que es exactamente la deriva que el esquema de una-hoja-dos-páginas evita. */
.rp-ventana > .rp-marca,
.muestra .rp-marca { z-index: 3; }
/* Sin borde: el brochazo hace de filo. Si alguna vez se apaga la pintura, hay
   que devolverle el borde o el encabezado queda flotando sin cierre — es lo que
   hace el bloque de `data-compacto`. */
.rp-marca--pintura { border-bottom: none; }

/* Velo bajo la pintura. Sin esto, un renglón que sube con el scroll CRUZA el
   brochazo y se lee como un error de dibujo — un chip con borde verde atravesado
   por pintura verde es exactamente eso. Con el velo, el contenido se disuelve
   contra el fondo antes de llegar a la pintura, que es lo que hace su material
   sobre las fotos: la pintura nunca compite con algo legible.
   z-index -1 lo deja DETRÁS de la pintura y DELANTE de los mensajes: la franja
   abre contexto de apilado propio (z-index 3), así que el negativo no se le
   escapa.

   REGRESIÓN CONOCIDA — el alto y los topes salen del alto de la pintura, nunca
   de un número suelto ni de porcentajes. El velo medía 104 px fijos mientras el
   alto de la pintura bajaba en el teléfono. Como el aire de los mensajes sí lo
   seguía, en el teléfono el primer mensaje nacía 44 px ADENTRO del velo: el
   primer renglón del saludo salía gris contra el resto blanco, que es lo primero
   que ve alguien que entra desde Instagram. Y al revés en escritorio, donde el
   velo se disolvía antes que la pintura y un renglón la cruzaba. Los dos huecos
   son el mismo error —dos alturas escritas aparte— y ninguno da error en
   consola. */
.rp-marca--pintura::after {
  content: "";
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  height: var(--rp-velo-alto);
  z-index: -1;
  pointer-events: none;
  /* Opaco hasta donde llega la pintura, y recién ahí se disuelve. */
  background: linear-gradient(
    180deg,
    var(--rp-fondo) 0,
    var(--rp-fondo) var(--rp-pintura-alto),
    rgb(var(--rp-fondo-rgb) / 0.55) calc(var(--rp-pintura-alto) + var(--rp-velo-caida) * 0.45),
    rgb(var(--rp-fondo-rgb) / 0) 100%
  );
}

@media (prefers-reduced-motion: reduce) {
  .rp-pintura { animation: none; }
}

/* ─── 8ter. La ventana ─────────────────────────────────────────────────── */
/* El asistente vive en una VENTANA sobre un telón, no a sangre. Es el patrón de
   biosalud-bot y de aguamadre-agente, y no es cosmético: a sangre, la interfaz
   se lee como un sitio, y esto no es un sitio — es un asistente. El marco dice
   «esto es una cosa que te contesta», que es exactamente lo que se vendió.

   Y resuelve la textura. La regla dice que ninguna textura va sobre una
   superficie de lectura, así que a sangre no había dónde ponerla. Con ventana
   aparece el telón, que no tiene texto, y ahí su malla puede ir casi a fuerza
   plena — donde la marca la usa de verdad. */

.rp-escena {
  position: fixed;
  inset: 0 auto auto 0;
  width: 100%;
  /* El teclado en pantalla no achica 100dvh en todos los navegadores. El script
     de la página setea --rp-vp-*; sin soporte cae a 100dvh, que es escritorio. */
  height: var(--rp-vp-alto, 100dvh);
  transform: translateY(var(--rp-vp-top, 0px));
  display: grid;
  place-items: center;
  padding: clamp(0px, 3vw, 40px);
}

/* El telón: su malla, más la luz verde detrás de la ventana. La malla acá va al
   7 % y no al 36 % de su póster — a fuerza plena, alrededor de un panel, se lee
   como una planilla de cálculo.
   `.rp-telon` existe para que el catálogo enmarque su maqueta de teléfono con el
   MISMO telón sin copiarse el gradiente: la copia era exactamente la deriva que
   el esquema de una-hoja-dos-páginas existe para evitar. */
.rp-escena,
.rp-telon { background: var(--rp-telon); position: relative; }

.rp-escena::before,
.rp-telon::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background-image:
    radial-gradient(115% 80% at 50% 0%, rgb(var(--rp-verde-rgb) / 0.10), transparent 62%),
    repeating-linear-gradient(0deg, rgba(255, 255, 255, 0.07) 0 1px, transparent 1px 26px),
    repeating-linear-gradient(90deg, rgba(255, 255, 255, 0.07) 0 1px, transparent 1px 26px);
}

.rp-ventana {
  position: relative;
  z-index: 1;
  width: min(560px, 100%);
  height: 100%;
  max-height: 820px;
  display: flex;
  flex-direction: column;
  background: var(--rp-fondo);
  border: 1px solid var(--rp-linea-fuerte);
  border-radius: var(--rp-radio);
  /* La sombra ancla la ventana sobre el telón; el filo verde al 8 % es lo único
     que la separa del negro sin gastar acento. */
  box-shadow:
    0 0 0 1px rgb(var(--rp-verde-rgb) / 0.08),
    0 30px 90px -30px rgba(0, 0, 0, 0.95);
  overflow: hidden;
}

/* La trama diagonal fina, adentro del panel. Acá SÍ hay texto encima, así que va
   al 3 % — el equivalente a una capa de #121212 al 97 %. */
.rp-ventana::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  background-image:
    repeating-linear-gradient(115deg, rgb(var(--rp-verde-rgb) / 0.03) 0 1px, transparent 1px 84px),
    repeating-linear-gradient(25deg, rgba(255, 255, 255, 0.02) 0 1px, transparent 1px 84px);
}
/* EL ACENTO DE ESQUINA. Su composición lo describe así: «un brochazo verde
   suelto en la esquina inferior derecha, sin texto». Acá cae en el hueco que
   queda debajo de la conversación, que era la única zona muerta de la ventana.

   CUATRO COSAS QUE LO MANTIENEN DEL LADO CORRECTO DE LAS REGLAS:
     · `z-index: 0` lo deja detrás de todo lo que se lee. `.rp-ventana > *`
       lleva z-index 1, así que ningún mensaje le pasa por debajo.
     · Va anclado ABAJO. Una conversación larga lo cubre con burbujas y el
       brochazo desaparece bajo ellas, que es lo que tiene que pasar: es un
       acento de pantalla vacía, no un fondo de lectura.
     · A opacidad PLENA y chico, no grande y tenue. Se probaron las dos: al
       55 % sobre el fondo el verde se compone en oliva y se lee como una
       mancha sucia, no como pintura. Lo que lo mantiene abajo del techo del
       10 % es el tamaño —36 % del ancho—, no la transparencia.
     · `--rp-acento-piso` lo despega de la zona de escritura, que es opaca y le
       pasa por encima. Más abajo, el brochazo queda cortado en seco contra el
       borde del campo de texto en vez de terminar en punta.

   Como máscara y no como imagen, por lo mismo que el isotipo: el color sale de
   la ficha del CSS. */
.rp-ventana::after {
  content: "";
  position: absolute;
  right: -6%;
  bottom: var(--rp-acento-piso);
  width: 36%;
  aspect-ratio: 367 / 291;
  pointer-events: none;
  z-index: 0;
  background: var(--rp-verde);
  -webkit-mask: url("/assets/marca/acento-esquina.svg") right bottom / contain no-repeat;
  mask: url("/assets/marca/acento-esquina.svg") right bottom / contain no-repeat;
}
/* Con el teclado abierto la decoración le cede el lugar al texto, igual que la
   pintura. Ver el bloque de `data-compacto`. */
html[data-compacto] .rp-ventana::after { display: none; }

.rp-ventana > * { position: relative; z-index: 1; }
/* El punto de montaje del chat ocupa lo que queda. `min-height: 0` es lo que deja
   que `.cc-mensajes` scrollee adentro de un flex item en vez de estirarlo. */
.rp-ventana > #chat { flex: 1; min-height: 0; }

/* ═══ 9. El chat ════════════════════════════════════════════════════════════
   Las clases `cc-*` las emite el runtime headless de chatbot-core. Acá va sólo
   su apariencia; el comportamiento, la estructura del DOM y el estado son del
   núcleo. Ver chatbot-core/docs/web-runtime.md. */

.cc-chat { display: flex; flex-direction: column; height: 100%; }

.cc-mensajes {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: var(--rp-e-5) var(--rp-e-4) var(--rp-e-2);
  display: flex;
  flex-direction: column;
  gap: var(--rp-e-3);
  scrollbar-width: thin;
  scrollbar-color: var(--rp-linea) transparent;
}

.cc-fila { display: flex; gap: var(--rp-e-2); align-items: flex-end; }
.cc-fila--user { justify-content: flex-end; }

/* El runtime antepone un .cc-avatar vacío a cada fila del bot; su apariencia es
   la del isotipo y se declara arriba, en la sección 5, con un selector
   compartido. Acá queda sólo lo que es propio del chat. */
.cc-fila--user .cc-avatar { display: none; }

/* Las dos burbujas comparten superficie a propósito.
   REGRESIÓN CONOCIDA — no volver a teñir la burbuja del usuario. La primera
   versión la pintaba de verde pleno con texto casi-negro. El contraste está bien
   (14,25) y esa combinación es válida en el manual, pero en una conversación de
   diez turnos el verde pasaba del 30 % de la pantalla, muy por encima del 4-10 %
   que mide su material. La marca se lee mal cuando su acento se vuelve fondo.
   Quién habla lo marcan la alineación, el isotipo y el filo verde del usuario. */
.cc-burbuja {
  max-width: 78%;
  padding: 11px var(--rp-e-3);
  background: var(--rp-superficie);
  border-radius: var(--rp-radio);
  font-size: var(--rp-t-base);
  /* El tracking va ACÁ y no en `body`. Puesto en el cuerpo lo hereda toda la
     hoja, y los chips, los rótulos y la tabla de parámetros ya traen el suyo
     medido: se pisarían entre sí según qué regla gane. Esto es texto corrido de
     conversación, que es lo único que lo necesita. */
  letter-spacing: var(--rp-tracking-cuerpo);
  line-height: var(--rp-interlineado);
  word-wrap: break-word;
}
.cc-burbuja--bot { border: 1px solid var(--rp-linea); }
/* El filo va a la DERECHA y no a la izquierda como pide docs/referencias-visuales.md:
   la burbuja del usuario está alineada a la derecha, y un filo del lado opuesto
   al borde de la pantalla se lee como un error de alineación. */
.cc-burbuja--user { border-right: 2px solid var(--rp-verde); }
/* NEGRITA BLANCA, NO VERDE, Y ES A PROPÓSITO. Los chips de sugerencia son verde
   ácido porque se tocan; cuando el dato destacado también lo era, el verde
   significaba dos cosas y quien barría la pantalla no podía saber qué era
   tocable — «45 kg» y «¿CÓMO ME INSCRIBO?» se veían igual.
   Tampoco va en verde primavera: en una respuesta de parámetros habría medio
   párrafo de secundario, y ese color es de rastro. */
.cc-burbuja strong { font-weight: 700; color: var(--rp-texto); }
.cc-burbuja ul, .cc-burbuja ol { margin: var(--rp-e-2) 0; padding-left: 20px; }
.cc-burbuja li { margin: 3px 0; }
.cc-burbuja > *:first-child { margin-top: 0; }
.cc-burbuja > *:last-child { margin-bottom: 0; }
.cc-enlace { color: var(--rp-verde); text-underline-offset: 3px; }

/* «escribiendo…» — tres formas que van del cuadrado al triángulo, en cadena.
   Es la geometría de la malla del telón, que es lo único que la marca dibuja
   además de la pintura.

   TRES COSAS QUE NO SE VEN EN EL SELECTOR, y las tres eran defectos medidos el
   7-sep-2026 contra el chat en vivo:

   1. EL SELECTOR LLEVA `.cc-typing` ADELANTE PARA GANARLE AL NÚCLEO. El runtime
      inyecta su hoja funcional DESPUÉS de ésta, y a igual especificidad gana la
      última. Con `.cc-typing-punto` pelado (0,1,0) corría `cc-parpadeo 1.2s` del
      núcleo y estos `@keyframes` no se ejecutaban nunca. Nada avisaba.
   2. LOS PUNTOS SON LOS HIJOS 2, 3 y 4. El primer hijo de la burbuja es el
      `<span>` de la frase. Numerados desde 1, el tercero se quedaba sin retardo
      y arrancaba junto con el primero: la onda salía rota.
   3. VENÍAN `display: inline`, así que el alto y el ancho se ignoraban. El fondo
      verde pintaba la caja de línea entera y los tres se tocaban: en pantalla no
      había tres cuadraditos sino un bloque verde macizo con tres puntitos grises
      impresos encima.

   El catálogo (`/sistema`) replica el marcado EXACTO del runtime a propósito. Con
   los puntos antes de la frase, como estaban, la numeración daba bien allá y mal
   en el producto — que es cómo el defecto 2 pasó desapercibido. */
.cc-typing { display: flex; align-items: center; gap: 7px; color: var(--rp-texto-mudo); font-size: var(--rp-t-sm); }

/* La burbuja de la espera es una FILA. El runtime cuelga la frase y las tres
   formas como hermanos sueltos adentro de un `div` de bloque, así que sin esto
   las formas nacen pegadas a la última letra. */
.cc-typing .cc-burbuja { display: flex; align-items: center; gap: 7px; }
.cc-typing .cc-typing-texto { font-size: var(--rp-t-sm); color: var(--rp-texto-mudo); }

.cc-typing .cc-typing-punto {
  display: block;
  width: 9px;
  height: 9px;
  background: var(--rp-verde);
  clip-path: var(--rp-cuadrado);
  /* Cada span del runtime trae un "." adentro. Es su contenido y no lo podemos
     sacar; se apaga acá o queda dibujado sobre el verde. */
  font-size: 0;
  color: transparent;
  animation: rp-forma 1.6s infinite ease-in-out;
}

/* EL RITMO ES DE IDA Y VUELTA: izquierda, medio, derecha, medio, y de nuevo.
   La vuelta anterior era un solo pulso largo con 0,2 s de diferencia entre una
   forma y la siguiente; contra 1,8 s de duración, esa diferencia era el 11 % del
   ciclo y las tres parecían moverse juntas. Ahora cada forma late CORTO y las
   cuatro pulsaciones se reparten la vuelta en cuartos, así se lee la escalera.

   La del medio late DOS veces por vuelta —en la ida y en la vuelta—, así que es
   la única que necesita su propia línea de tiempo. Las de las puntas comparten
   `rp-forma` y se separan medio ciclo con el retardo.

   La primera lleva un cero EXPLÍCITO. Sin ese renglón queda mandando el
   `:nth-child(2){animation-delay:.2s}` del núcleo, que tiene la misma
   especificidad que la regla de arriba y viene después: la primera forma
   arrancaba tarde y la cadena salía corrida. Verificado midiendo el chat. */
.cc-typing .cc-typing-punto:nth-child(2) { animation-delay: 0s; }
.cc-typing .cc-typing-punto:nth-child(3) {
  animation-name: rp-forma-medio;
  animation-delay: 0.4s;
}
.cc-typing .cc-typing-punto:nth-child(4) { animation-delay: 0.8s; }

/* Un pulso por vuelta, centrado en el primer cuarto. */
@keyframes rp-forma {
  0%, 25%, 100% { clip-path: var(--rp-cuadrado); }
  12.5% { clip-path: var(--rp-triangulo); }
}

/* Dos pulsos por vuelta, a medio ciclo uno del otro. */
@keyframes rp-forma-medio {
  0%, 25%, 50%, 75%, 100% { clip-path: var(--rp-cuadrado); }
  12.5%, 62.5% { clip-path: var(--rp-triangulo); }
}

@media (prefers-reduced-motion: reduce) {
  .cc-typing .cc-typing-punto { animation: none; opacity: 0.7; }
}

/* Chips de sugerencia. */
.cc-sugerencias { display: flex; flex-wrap: wrap; gap: var(--rp-e-2); padding: var(--rp-e-1) 0 2px 38px; }
.cc-sugerencia {
  background: transparent;
  border: 1px solid var(--rp-verde);
  color: var(--rp-verde);
  padding: 7px var(--rp-e-3);
  border-radius: var(--rp-radio);
  /* Rótulo, igual que .rp-boton. En monoespaciada los cuatro chips crecían
     tanto que dejaban de entrar de a uno por renglón en 390 px. */
  font-family: var(--rp-rotulo);
  font-weight: 700;
  font-size: 15px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  cursor: pointer;
  transition: background var(--rp-transicion), color var(--rp-transicion);
}
.cc-sugerencia:hover { background: var(--rp-verde); color: var(--rp-fondo); }

/* Zona de escritura. */
.cc-input-zona {
  display: flex;
  align-items: flex-end;
  gap: var(--rp-e-2);
  padding: var(--rp-e-3) var(--rp-e-4) calc(var(--rp-e-3) + env(safe-area-inset-bottom));
  background: var(--rp-fondo);
  border-top: 1px solid var(--rp-linea);
}
.cc-textarea {
  flex: 1;
  resize: none;
  max-height: 132px;
  padding: 11px 13px;
  background: var(--rp-superficie);
  border: 1px solid var(--rp-linea);
  border-radius: var(--rp-radio);
  color: var(--rp-texto);
  font-family: inherit;
  /* NUNCA POR DEBAJO DE 16 px, aunque la escala del cuerpo baje. Safari en iOS
     hace zoom sobre cualquier campo de texto con menos de 16 px cuando recibe el
     foco, y el zoom no se deshace solo: la persona termina de escribir y se
     queda con la conversación agrandada y corrida. `--rp-t-base` vale hoy
     16,5 px, así que el tope no hace nada: queda de red para cualquier retoque
     futuro de la escala. Ya salvó una vez —la variante de Antonio de la prueba
     de letra bajaba la base a 14 px— y esa variante ya no existe, pero el tope
     sí. Deja el campo apenas más grande que las burbujas, que es el precio
     correcto. */
  font-size: max(16px, var(--rp-t-base));
  line-height: 1.4;
}
/* El texto de ayuda del campo va con la ficha de texto mudo y no con un hex
   suelto. El valor anterior (#6b6b6b) daba 3,20 contra la superficie del campo,
   por debajo del 4,5 que pide AA para texto — y este texto dice qué hacer.
   La ficha da 5,26 sobre la misma superficie. */
.cc-textarea::placeholder { color: var(--rp-texto-mudo); }
.cc-textarea:focus { outline: none; border-color: var(--rp-verde); }

.cc-enviar {
  flex: 0 0 42px;
  height: 42px;
  border: none;
  border-radius: var(--rp-radio);
  background: var(--rp-verde);
  cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23121212'%3E%3Cpath d='M2.5 21 23 12 2.5 3 2.5 10l14 2-14 2z'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: center;
  background-size: 19px;
  transition: background-color var(--rp-transicion);
}
.cc-enviar:hover { background-color: var(--rp-verde-hover); }
.cc-enviar:disabled { opacity: 0.35; cursor: default; }

/* ─── 10. Adaptación a pantalla chica ───────────────────────────────────── */
/* El tráfico viene de Instagram, o sea del teléfono. El teléfono no es el caso
   degradado: es el caso principal. */

/* La ventana con pintura necesita aire arriba, y el aire se mide contra el VELO,
   no contra la pintura. En reposo el primer mensaje tiene que nacer por debajo
   del velo entero: un renglón adentro del velo sale gris, y ahí adentro cae
   justo el saludo. En movimiento, en cambio, un renglón pasa por atrás de un
   hilo al hacer scroll, y eso es a propósito — es lo que hace su material sobre
   las fotos. */
.rp-ventana .cc-mensajes { padding-top: calc(var(--rp-velo-alto) + var(--rp-e-2)); }

@media (max-width: 560px) {
  /* A sangre en el teléfono. Un marco de 8 px alrededor de una pantalla de 390 px
     no se lee como ventana: se lee como un error de márgenes, y roba el ancho que
     necesita una tabla de parámetros. Mismo criterio que aguamadre-agente. */
  .rp-escena { padding: 0; }
  .rp-ventana { max-height: none; border: none; border-radius: 0; box-shadow: none; }
}

@media (max-width: 480px) {
  /* Se redefine la ficha, no la regla: el padding de los mensajes la consume y
     así los dos bajan juntos. */
  :root { --rp-pintura-alto: 38px; }
  .rp-marca { padding: 9px var(--rp-e-3); gap: 10px; }
  .rp-marca img { height: 29px; }
  .rp-marca-fecha { font-size: 15px; }
  .rp-marca-sede { font-size: 12.5px; }
  .cc-mensajes { padding: var(--rp-e-4) var(--rp-e-3) 6px; }
  /* El aire de arriba NO se redefine acá: sale de `--rp-velo-alto`, que ya baja
     con `--rp-pintura-alto`. Repetirlo era lo que dejaba las dos alturas sueltas. */
  .cc-burbuja { max-width: 86%; }
  .cc-sugerencias { padding-left: 0; }
  .rp-titular { font-size: var(--rp-t-xl); }
}

/* ─── 11. Pantalla corta: cuando se abre el teclado ─────────────────────── */
/* El teclado de un teléfono se come más de la mitad de la pantalla. Medido en
   390x380, que es lo que queda: la franja de marca, el aire de la pintura y la
   zona de escritura se comían más de la mitad, y a la conversación le quedaban
   cuatro renglones.

   Acá la decoración le cede el lugar al texto. Vuelve entera al cerrar el
   teclado, que es cuando hay pantalla para mirarla.

   NO ES UNA CONSULTA DE MEDIOS. El teclado no achica el viewport de layout en
   todos los navegadores, así que `@media (max-height:…)` no dispara donde más
   falta. El atributo lo pone el script de `chat.html`, que ya mide
   `visualViewport` para el alto de la escena — la misma medida que sí baja. */
html[data-compacto] {
  /* En cero: con el teclado abierto la decoración le cede el lugar al texto. El
     velo y el aire de los mensajes lo siguen solos porque derivan de esta ficha,
     así que no hay que tocar ningún otro número. */
  --rp-pintura-alto: 0px;
}
html[data-compacto] .rp-pintura { display: none; }

/* La pintura era el filo de la franja: sin ella hay que devolver el borde, o el
   encabezado queda flotando sin cierre contra los mensajes. */
html[data-compacto] .rp-marca--pintura { border-bottom: 2px solid var(--rp-verde); }
html[data-compacto] .rp-marca--pintura::after { display: none; }

html[data-compacto] .rp-marca { padding: 6px var(--rp-e-3); gap: 9px; }
html[data-compacto] .rp-marca img { height: 23px; }
html[data-compacto] .rp-marca-fecha { font-size: 13.5px; }
/* La sede es el dato que se puede releer después: con el teclado abierto la
   persona está escribiendo, no ubicando el estadio. */
html[data-compacto] .rp-marca-sede { display: none; }
