/* =======================================================================
   Componentes compartidos
   -----------------------------------------------------------------------
   Cada componente de aquí existe porque alguna historia del Sprint 1 lo
   necesita. La historia está anotada encima. Si vas a añadir uno nuevo,
   anota también de dónde sale: este archivo lo comparten los tres equipos
   y sin esa nota nadie sabe si un componente sigue haciendo falta.

   Ningún color se escribe en hexadecimal aquí. Todos bajan de tokens.css.
   ======================================================================= */

/* =======================================================================
   Tarjeta — base de casi todo
   ======================================================================= */

/* =======================================================================
   Superficies claras — UX-GAME-1
   -----------------------------------------------------------------------
   Todo lo que pinta `--superficie` o `--superficie-2` devuelve la tinta a
   oscuro (ver «Tinta contextual» en base.css). Va ANTES que cualquier regla
   de componente, con la misma especificidad, para que un `color` propio del
   componente siga ganando. Si una superficie nueva no esta en esta lista, su
   texto sale claro sobre blanco: axe lo canta en el laboratorio visual.
   ======================================================================= */
.tarjeta,
.dialogo,
.estado-vista,
.tabla,
.menu,
.resultado,
.transmision,
.chatbot__tarjeta,
.chatbot-ventana,
.metrica,
.encuentro,
.panel-resultado,
.combate__controles,
.interrupcion__tarjeta,
.entrada__tarjeta,
.codigo-invitacion,
.modalidad__cara,
.accion-combate,
.zona-carga--con-archivos,
.zona-carga--cargando,
.lista-terminos__fila,
.campo__control,
.desplegable__control,
.campo-numerico__caja,
.boton--secundario,
.paginacion__pagina,
.tecla,
.elegir-objeto__opcion,
.casilla--caja,
.radio__entrada,
.distintivo--privada,
.sello-estado[data-estado='neutro'] {
  --tinta: var(--texto);
  --tinta-2: var(--texto-2);
  --tinta-3: var(--texto-3);
  --tinta-enlace: var(--primaria);
  --tinta-enlace-hover: var(--primaria-relleno);
  color: var(--texto);
}

/* La tarjeta: la superficie base del Nexo. Desde UX-GAME-1 lleva el filo de
   energia que coronaba la tarjeta de entrada —tres pixeles de `--energia` en
   el borde superior— y una sombra pensada para la atmosfera oscura; la de
   capa 1 no se veia sobre indigo. El filo va como capa de fondo y no como
   pseudoelemento para no reservar `::before` en un contenedor que lo usan
   muchas vistas. */
.tarjeta {
  background:
    var(--energia) top left / 100% 3px no-repeat,
    var(--superficie);
  border: 1px solid var(--borde);
  border-radius: var(--radio);
  padding: var(--e-4);
  box-shadow: var(--sombra-atmosfera);
}

.tarjeta--hundida {
  background: var(--fondo);
  box-shadow: none;
}

/* Tarjeta que además es un botón o un enlace (ej. una sala del listado) */
.tarjeta--pulsable {
  cursor: pointer;
  text-align: left;
  width: 100%;
  font: inherit;
  color: inherit;
  /* UX-R2.6 — faltaba, y se notaba: `base.css` subraya todo `<a>`, así que
     cada tarjeta-enlace salía con su título y su descripción subrayados, como
     un enlace de texto de los noventa. `color: inherit` ya estaba; lo que no
     estaba era apagar el subrayado. Afecta a las seis tarjetas de la home, al
     listado de salas y a cualquier tarjeta con `href`. */
  text-decoration: none;
  transition:
    border-color var(--transicion),
    box-shadow var(--transicion);
}

.tarjeta--pulsable {
  transition:
    border-color var(--transicion),
    box-shadow var(--transicion),
    transform var(--transicion);
}

.tarjeta--pulsable:hover {
  border-color: var(--primaria);
  box-shadow: var(--sombra-atmosfera), var(--brillo-primaria);
  transform: translateY(-2px);
}

.tarjeta--pulsable:active {
  transform: translateY(0);
  transition-duration: var(--duracion-instantaneo);
}

/* =======================================================================
   Botones
   Tres alturas, según la sección 2 del documento:
   48 para acciones principales, 40 para secundarias, 32 para barras
   compactas.
   ======================================================================= */

.boton {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--e-2);
  padding: 0 var(--e-4);
  min-height: var(--boton-mediano);
  border: 1px solid transparent;
  border-radius: var(--radio);
  font-family: var(--t-etiqueta-familia);
  font-size: var(--t-etiqueta-tam);
  font-weight: var(--t-etiqueta-peso);
  line-height: var(--t-etiqueta-alto);
  /* Un enlace con aspecto de botón («CREAR SALA» en Batallas, «Cancelar» en
     Crear sala) no debe heredar el subrayado ni el azul de `a` en base.css. */
  text-decoration: none;
  cursor: pointer;
  /* La luz del borde del primario es un pseudoelemento que vive dentro del
     boton; `isolation` evita que su capa se mezcle con lo de fuera. */
  position: relative;
  isolation: isolate;
  transition:
    background var(--transicion),
    border-color var(--transicion),
    color var(--transicion),
    transform var(--transicion),
    box-shadow var(--transicion);
}

/* Respuesta de videojuego: el boton se acerca un poco al pasar el cursor, se
   hunde al pulsarlo y vuelve. Son dos por ciento y un pixel: se sienten sin
   verse. Los deshabilitados no responden a nada, que es lo que significan.
   Bajo `prefers-reduced-motion` los desplazamientos se quitan mas abajo; el
   cambio de color y de sombra se queda, porque comunica estado y no se mueve. */
.boton:hover:not([disabled]):not([aria-disabled='true']) {
  transform: translateY(-1px) scale(1.02);
  box-shadow: var(--sombra-2);
}

.boton:active:not([disabled]):not([aria-disabled='true']) {
  transform: translateY(0) scale(0.98);
  box-shadow: var(--sombra-1);
  transition-duration: var(--duracion-instantaneo);
}

/* El anillo de foco global de base.css sigue mandando; aqui solo se separa un
   pixel mas para que no se pise con el brillo del hover. */
.boton:focus-visible {
  outline-offset: 3px;
}

/* Atacar, Confirmar, Pagar */
.boton--grande {
  min-height: var(--boton-grande);
  padding: 0 var(--e-5);
}
.boton--pequeno {
  min-height: var(--boton-pequeno);
  padding: 0 var(--e-3);
}

/* Cada variante repite su color en :hover a propósito: `a:hover` de base.css
   (0,1,1) le ganaría a la variante sola (0,1,0) y pintaría el texto de azul
   sobre un fondo azul o rojo. */
.boton--primario {
  background: var(--primaria-relleno);
  color: var(--texto-sobre-color);
}
.boton--primario:hover {
  background: var(--primaria);
  color: var(--texto-sobre-color);
}

.boton--primario:hover:not([disabled]):not([aria-disabled='true']) {
  box-shadow: var(--brillo-primaria);
}

/* La luz que recorre el borde del boton principal — una vez, al entrar el
   cursor, y se apaga. Es un anillo de un pixel pintado con un gradiente conico
   y recortado con una mascara para que solo quede el contorno; el angulo del
   gradiente es lo que se anima. Sin `@property` el angulo no interpola y el
   anillo simplemente no aparece: el boton sigue siendo el mismo boton. */
@property --angulo-luz {
  syntax: '<angle>';
  inherits: false;
  initial-value: 0deg;
}

.boton--primario::after {
  content: '';
  position: absolute;
  inset: -1px;
  border-radius: inherit;
  padding: 1px;
  background: conic-gradient(
    from var(--angulo-luz),
    transparent 0deg,
    transparent 250deg,
    color-mix(in srgb, var(--texto-sobre-color) 85%, var(--primaria)) 300deg,
    transparent 360deg
  );
  -webkit-mask:
    linear-gradient(var(--texto-sobre-color) 0 0) content-box,
    linear-gradient(var(--texto-sobre-color) 0 0);
  mask:
    linear-gradient(var(--texto-sobre-color) 0 0) content-box,
    linear-gradient(var(--texto-sobre-color) 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
  opacity: 0;
  pointer-events: none;
  z-index: -1;
}

@media (prefers-reduced-motion: no-preference) {
  .boton--primario:hover:not([disabled]):not([aria-disabled='true'])::after {
    animation: luz-del-borde 900ms var(--curva-salida) 1;
  }
}

@keyframes luz-del-borde {
  0% {
    opacity: 0;
    --angulo-luz: 0deg;
  }
  15% {
    opacity: 1;
  }
  85% {
    opacity: 1;
  }
  100% {
    opacity: 0;
    --angulo-luz: 360deg;
  }
}

.boton--acento {
  background: var(--acento-relleno);
  color: var(--texto-sobre-color);
}
.boton--acento:hover {
  color: var(--texto-sobre-color);
}

.boton--acento:hover:not([disabled]):not([aria-disabled='true']) {
  box-shadow: var(--brillo-acento);
}

.boton--secundario {
  background: var(--superficie);
  border-color: var(--borde-int);
  color: var(--texto);
}
.boton--secundario:hover {
  background: var(--fondo);
  color: var(--texto);
}

/* Acción secundaria SOBRE cromo — UX-R3.2.
   `--secundario` es blanco sobre superficie: puesto encima del cromo se
   convierte en un bloque blanco que pesa más que el propio CTA de acento y
   se lleva la mirada. Este hereda el color del contenedor y se dibuja solo
   con el contorno, que es lo que hace una acción secundaria: estar, sin
   competir. Solo tiene sentido dentro de una superficie oscura. */
.boton--contorno {
  background: transparent;
  border-color: currentcolor;
  color: inherit;
}
.boton--contorno:hover {
  background: color-mix(in srgb, currentcolor 14%, transparent);
  color: inherit;
}

.boton--peligro {
  background: var(--error);
  color: var(--texto-sobre-color);
}
.boton--peligro:hover {
  color: var(--texto-sobre-color);
}

.boton--exito {
  background: var(--exito);
  color: var(--texto-sobre-color);
}
.boton--exito:hover {
  color: var(--texto-sobre-color);
}

/* Boton de solo icono: cuadrado, con el nombre de la accion en `aria-label`
   o en un `.solo-lectores` dentro. El area tactil es la del boton mediano. */
.boton--icono {
  padding: 0;
  width: var(--boton-mediano);
  min-width: var(--boton-mediano);
  aspect-ratio: 1;
}

.boton[disabled],
.boton[aria-disabled='true'] {
  opacity: 0.5;
  cursor: not-allowed;
}

/* Cargando — UX-GAME-1. `aria-busy` es el estado, y lo pone el modulo que
   lanzo la peticion; esto solo lo pinta. El texto se queda donde estaba, con
   un indicador delante: cambiar la etiqueta por «Cargando…» hace que el boton
   salte de ancho y que quien mira pierda de vista que accion pulso. Mientras
   dure, el boton no responde al cursor. */
.boton[aria-busy='true'] {
  pointer-events: none;
  position: relative;
  color: transparent;
}

.boton[aria-busy='true']::before {
  content: '';
  position: absolute;
  inset: 0;
  margin: auto;
  width: 1.1em;
  height: 1.1em;
  border: 2px solid color-mix(in srgb, currentColor 30%, transparent);
  border-top-color: currentColor;
  border-radius: 50%;
  /* El color del indicador es el del texto que se oculto: lo toma del
     modificador (`--primario` blanco, `--secundario` oscuro). */
  color: var(--texto-sobre-color);
  animation: giro-de-carga 700ms linear infinite;
}

.boton--secundario[aria-busy='true']::before,
.boton--contorno[aria-busy='true']::before {
  color: var(--texto);
}

@keyframes giro-de-carga {
  to {
    transform: rotate(360deg);
  }
}

@media (prefers-reduced-motion: reduce) {
  /* Sin giro: el indicador se queda como un arco quieto, que sigue diciendo
     «esperando» sin moverse. */
  .boton[aria-busy='true']::before {
    animation: none;
  }
}

/* =======================================================================
   Campos de formulario
   HU-SAL-001 · crear sala con parámetros y recompensa
   ======================================================================= */

.campo {
  display: flex;
  flex-direction: column;
  gap: var(--e-1);
}

.campo__etiqueta {
  font-family: var(--t-etiqueta-familia);
  font-size: var(--t-etiqueta-tam);
  font-weight: var(--t-etiqueta-peso);
  /* Tinta y no paleta: los filtros de Batallas van directamente sobre la
     atmosfera y los de un formulario, dentro de una tarjeta. */
  color: var(--tinta);
}

.campo__control {
  padding: 0 var(--e-3);
  min-height: var(--campo-alto);
  background: var(--superficie);
  border: 1px solid var(--borde-int);
  border-radius: var(--radio);
  font-family: var(--t-cuerpo-familia);
  font-size: var(--t-cuerpo-tam);
  color: var(--texto);
  transition:
    border-color var(--transicion),
    box-shadow var(--transicion);
}

/* UX-GAME-1: el campo responde antes de recibir el foco, y al recibirlo el
   borde se enciende en la primaria ademas del anillo global. Invalido y
   deshabilitado se dicen con borde y fondo, no solo con color. */
.campo__control:hover:not(:disabled):not([aria-invalid='true']) {
  border-color: color-mix(in srgb, var(--primaria) 60%, var(--borde-int));
}

.campo__control:focus-visible {
  border-color: var(--primaria);
}

.campo__control[aria-invalid='true'] {
  border-color: var(--error);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--error) 22%, transparent);
}

.campo__control:disabled {
  background: var(--superficie-2);
  color: var(--texto-3);
  cursor: not-allowed;
}

.campo__pista {
  font-family: var(--t-meta-familia);
  font-size: var(--t-meta-tam);
  /* Tinta contextual (UX-GAME-1): una pista puede ir fuera de una tarjeta. */
  color: var(--tinta-3);
}

/* Estado inválido. El criterio de aceptación de HU-SAL-001 pide que se
   indique EL MOTIVO del rechazo, no solo que falló: de ahí campo__error. */
.campo--invalido .campo__control {
  border-color: var(--error);
}

.campo__error {
  display: flex;
  align-items: center;
  gap: var(--e-1);
  font-family: var(--t-meta-familia);
  font-size: var(--t-meta-tam);
  font-weight: 600;
  color: var(--error);
}

/* =======================================================================
   Selector de modalidad
   HU-SAL-004 · 1v1, contra IA, hasta seis participantes
   -----------------------------------------------------------------------
   Se construye con <input type="radio"> reales para que funcione con
   teclado y lectores de pantalla; el input se oculta y se estiliza la
   etiqueta. No usar <div> con onclick.
   ======================================================================= */

.modalidades {
  display: flex;
  gap: var(--e-2);
  flex-wrap: wrap;
}

.modalidad {
  position: relative;
  flex: 1 1 140px;
}

.modalidad__entrada {
  position: absolute;
  opacity: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  cursor: pointer;
}

.modalidad__cara {
  display: block;
  padding: var(--e-3);
  background: var(--superficie);
  border: 1px solid var(--borde-int);
  border-radius: var(--radio);
  text-align: center;
  transition:
    border-color var(--transicion),
    background var(--transicion);
}

.modalidad__entrada:hover + .modalidad__cara {
  border-color: var(--primaria);
}

.modalidad__entrada:checked + .modalidad__cara {
  background: var(--primaria-relleno);
  border-color: var(--primaria-relleno);
  color: var(--texto-sobre-color);
}

.modalidad__entrada:focus-visible + .modalidad__cara {
  outline: 2px solid var(--primaria);
  outline-offset: 2px;
}

.modalidad__titulo {
  display: block;
  font-family: var(--t-etiqueta-familia);
  font-size: var(--t-etiqueta-tam);
  font-weight: var(--t-etiqueta-peso);
}

.modalidad__detalle {
  display: block;
  margin-top: var(--e-1);
  font-family: var(--t-meta-familia);
  font-size: var(--t-meta-tam);
  opacity: 0.85;
}

/* =======================================================================
   Distintivo (insignia)
   HU-SAL-002 · estado de la sala en el listado
   -----------------------------------------------------------------------
   Regla del documento: color saturado como TEXTO sobre el fondo tenue del
   mismo color. Nunca fondo sólido con texto blanco.
   ======================================================================= */

.distintivo {
  display: inline-flex;
  align-items: center;
  gap: var(--e-1);
  padding: 2px var(--e-2);
  border: 1px solid transparent;
  border-radius: var(--radio-pildora);
  font-family: var(--t-meta-familia);
  font-size: var(--t-meta-tam);
  font-weight: 600;
  white-space: nowrap;
  /* Dentro de una rejilla, el distintivo mide lo que su texto: estirado a la
     celda parecia una barra (cola de moderacion, salas). UX-GAME-6. */
  justify-self: start;
  width: fit-content;
}

.distintivo--abierta {
  background: var(--exito-insignia);
  color: var(--exito);
}
.distintivo--llena {
  background: var(--advertencia-insignia);
  color: var(--advertencia);
}
.distintivo--en-juego {
  background: var(--info-insignia);
  color: var(--info);
}

/* El documento no define un fondo tenue para el acento, asi que la sala
   privada se marca con borde y texto en acento, sin inventar un color.
   Si el equipo aprueba un --acento-insignia, se cambia aqui.            */
.distintivo--privada {
  background: var(--superficie);
  border-color: var(--acento-relleno);
  color: var(--acento-relleno);
}

/* =======================================================================
   Aviso
   HU-SAL-003 · héroe no equipado o no disponible
   HU-SAL-001 · créditos insuficientes o parámetros inválidos
   RNF-USA-003 · estado de error de las vistas que consultan datos
   ======================================================================= */

.aviso {
  display: flex;
  gap: var(--e-3);
  padding: var(--e-3) var(--e-4);
  border: 1px solid;
  border-left-width: 4px;
  border-radius: var(--radio);
  font-size: var(--t-cuerpo-tam);
  color: var(--texto);
}

.aviso__titulo {
  font-family: var(--t-etiqueta-familia);
  font-weight: var(--t-etiqueta-peso);
  margin-bottom: var(--e-1);
}

/* El aviso puede llevar una acción a la derecha («Reintentar», «Ir»); el
   cuerpo ocupa lo que quede para que el botón no se aplaste. */
.aviso__cuerpo {
  flex: 1;
  min-width: 0;
}
.aviso__detalle {
  color: var(--texto-2);
}

.aviso--error {
  border-color: var(--error);
  background: var(--error-insignia);
}
.aviso--advertencia {
  border-color: var(--advertencia);
  background: var(--advertencia-insignia);
}
.aviso--exito {
  border-color: var(--exito);
  background: var(--exito-insignia);
}
.aviso--info {
  border-color: var(--info);
  background: var(--info-insignia);
}

/* =======================================================================
   Seccion degradada — HU-DIS-003 · RNF-DIS-003 · MAPEO-ERRORES §5.5
   Se pinta EN EL HUECO de la seccion que depende de un servicio caido;
   el resto de la vista queda como esta. No es un Aviso (§5.2) ni un
   Estado de vista (§5.1): el tono es de advertencia, no de error, porque
   la degradacion es temporal y lo demas sigue funcionando.
   Lo rellena frontend/app-web/src/comun/degradacion/aviso-degradacion.js.
   ======================================================================= */

.seccion-degradada {
  display: flex;
  flex-direction: column;
  gap: var(--e-2);
  padding: var(--e-3) var(--e-4);
  border: 1px solid var(--advertencia);
  border-left-width: 4px;
  border-radius: var(--radio);
  background: var(--advertencia-insignia);
  font-size: var(--t-cuerpo-tam);
  color: var(--texto);
}

.seccion-degradada__titulo {
  font-family: var(--t-etiqueta-familia);
  font-weight: var(--t-etiqueta-peso);
}

.seccion-degradada__espera {
  color: var(--texto-2);
}

/* El reintento es el boton secundario del kit: la degradacion no es la
   accion principal de la vista, es la salida de un estado temporal. */
.seccion-degradada__reintentar {
  align-self: flex-start;
  padding: var(--e-2) var(--e-4);
  border: 1px solid var(--primaria-relleno);
  border-radius: var(--radio);
  background: transparent;
  color: var(--primaria-relleno);
  font-family: var(--t-etiqueta-familia);
  font-weight: var(--t-etiqueta-peso);
  cursor: pointer;
}

.seccion-degradada__reintentar:hover {
  background: var(--fondo);
}

/* =======================================================================
   Barra de vida
   HU-SAL-005 · RF-JUE-009 y RN-COM-005
   -----------------------------------------------------------------------
   Reglas que NO se pueden romper:

   1. El color NUNCA es el único indicador. El valor numérico va siempre
      visible al lado. Un jugador con daltonismo tiene que poder jugar.
   2. Los umbrales viven en tokens.css (--vida-umbral-alto y -medio).
      El JavaScript los lee de ahí; no los escribe.
   3. La barra lleva role="progressbar" con sus aria-value*.

   El JS pone data-estado="alto|medio|bajo" y --vida (0-100).
   Ver shared/ui-kit/js/barra-vida.js
   ======================================================================= */

.barra-vida {
  display: flex;
  align-items: center;
  gap: var(--e-3);
}

.barra-vida__pista {
  flex: 1;
  height: 12px;
  background: var(--pista);
  border: 1px solid var(--borde);
  box-shadow: inset 0 1px 2px color-mix(in srgb, var(--cromo) 18%, transparent);
  border-radius: var(--radio-chico);
  overflow: hidden;
}

.barra-vida__relleno {
  height: 100%;
  width: calc(var(--vida, 100) * 1%);
  border-radius: inherit;
  transition:
    width var(--transicion),
    background var(--transicion);
}

.barra-vida[data-estado='alto'] .barra-vida__relleno {
  background: var(--exito);
}
.barra-vida[data-estado='medio'] .barra-vida__relleno {
  background: var(--advertencia);
}
.barra-vida[data-estado='bajo'] .barra-vida__relleno {
  background: var(--error);
}

.barra-vida__valor {
  min-width: 5ch;
  text-align: right;
  font-family: var(--t-etiqueta-familia);
  font-size: var(--t-etiqueta-tam);
  font-weight: var(--t-etiqueta-peso);
  font-variant-numeric: var(--cifras-tabulares);
  /* Tinta contextual: la barra vive en el HUD oscuro y en tarjetas claras. */
  color: var(--tinta-2);
}

.barra-vida__nombre {
  font-family: var(--t-subtitulo-familia);
  font-size: var(--t-subtitulo-tam);
  font-weight: var(--t-subtitulo-peso);
  color: var(--tinta);
}

/* =======================================================================
   Encabezado de aplicación
   El cromo oscuro está acotado a esto y a la cabecera del combate.
   ======================================================================= */

/* Medidas tomadas del componente «Cabecera» de Figma, variante
   Rol=Jugador / Ancho=1360: 64 px de alto, padding 0 24, separación 24
   entre los tres grupos. Antes eran 12/20 y 16, que era una aproximación. */
.cabecera {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--e-6);
  min-height: var(--cabecera-alto);
  padding: 0 var(--e-6);
  background: var(--cromo);
  color: var(--texto-sobre-color);
  /* UX-GAME-1: sobre la atmosfera, que arranca en el cromo elevado, la barra
     se fundia con la pagina. Una linea de energia al 45 % la separa sin
     anadir un borde solido. */
  box-shadow: inset 0 -1px 0 color-mix(in srgb, var(--primaria) 45%, transparent);
}

.cabecera__marca {
  gap: var(--e-2);
  font-family: var(--t-titulo-familia);
  font-size: var(--t-subtitulo-tam);
  font-weight: var(--t-titulo-peso);
  letter-spacing: 0.02em;
}

/* UX-GAME-2 — el emblema compacto del logotipo (ver `marca/`), 32 px de
   ancho. Lleva un halo azul tenue, el mismo gesto que el logotipo grande del
   portal con `--sombra-marca`, a la escala de una barra. */
.cabecera__emblema {
  width: 32px;
  height: auto;
  flex: 0 0 auto;
  filter: drop-shadow(0 0 6px color-mix(in srgb, var(--primaria) 60%, transparent));
}

/* =======================================================================
   Rejilla de salas
   HU-SAL-002 · listado de batallas, dieciséis por página
   ======================================================================= */

.rejilla-salas {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: var(--e-4);
}

/* A 1024 px la rejilla pasa de cuatro columnas a tres. RNF-USA-001. */
@media (max-width: 1024px) {
  .rejilla-salas {
    grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  }
}

/* =======================================================================
   Marco de héroe
   Retrato con anillo de rareza e insignia de nivel superpuesta.
   ======================================================================= */

.marco-heroe {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--e-2);
  width: 160px;
}

.marco-heroe__retrato {
  position: relative;
  width: 112px;
  height: 112px;
  border-radius: 50%;
  border: 3px solid var(--rareza-comun);
  background: var(--rareza-comun-insignia);
  display: grid;
  place-items: center;
}

.marco-heroe__imagen {
  width: 92px;
  height: 92px;
  border-radius: 50%;
  object-fit: cover;
  background: var(--rareza-comun);
}

/* La insignia de nivel se superpone al borde inferior derecho. */
.marco-heroe__nivel {
  position: absolute;
  right: -4px;
  bottom: 0;
  min-width: 34px;
  padding: 2px var(--e-2);
  border-radius: var(--radio-pildora);
  border: 2px solid var(--superficie);
  background: var(--cromo);
  color: var(--texto-sobre-color);
  font-family: var(--t-titulo-familia);
  font-weight: var(--t-titulo-peso);
  font-size: 1rem;
  text-align: center;
}

.marco-heroe--rara .marco-heroe__retrato {
  border-color: var(--rareza-rara);
  background: var(--rareza-rara-insignia);
}
.marco-heroe--rara .marco-heroe__imagen {
  background: var(--rareza-rara);
}
.marco-heroe--epica .marco-heroe__retrato {
  border-color: var(--rareza-epica);
  background: var(--rareza-epica-insignia);
}
.marco-heroe--epica .marco-heroe__imagen {
  background: var(--rareza-epica);
}
.marco-heroe--legendaria .marco-heroe__retrato {
  border-color: var(--rareza-legendaria);
  background: var(--rareza-legendaria-insignia);
}
.marco-heroe--legendaria .marco-heroe__imagen {
  background: var(--rareza-legendaria);
}

.marco-heroe__nombre {
  font-family: var(--t-subtitulo-familia);
  font-size: var(--t-subtitulo-tam);
  font-weight: var(--t-subtitulo-peso);
  text-align: center;
}

/* =======================================================================
   Estado de vista — RNF-USA-003
   Toda vista que consulte datos usa estos tres; el cuarto estado, con
   datos, es el contenido normal de la vista.
   ======================================================================= */

.estado-vista {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--e-3);
  padding: var(--e-6);
  min-height: 260px;
  text-align: center;
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-radius: var(--radio);
  /* UX-R4.4 — un estado de la vista entera ocupa la vista entera, tambien
     cuando la zona donde se pinta es una rejilla. En «Mis cofres» el fallo
     caia dentro de `.cofres-lista`, que reparte columnas de 240 px, y se
     colocaba como si fuera un cofre mas: una caja estrecha a la izquierda y
     el resto de la tarjeta en blanco. Fuera de una rejilla, esta linea no
     hace nada, asi que vale para las dos formas de pintarlo. */
  grid-column: 1 / -1;
}

/* UX-R4.4 — dentro de una tarjeta, el estado NO dibuja otra tarjeta.
   `historial-transacciones` y `mis-cofres` pintan su estado dentro de
   `<section class="tarjeta">`, que ya trae superficie blanca, borde y radio.
   El estado traia lo mismo, asi que salia un marco blanco dentro de otro
   marco blanco separados por doce pixeles: el doble borde que se ve en
   cuanto falla cualquiera de las dos. La tarjeta pone el papel; el estado
   solo pone lo que dice.

   Descendiente y no hijo directo: las dos vistas envuelven el estado en su
   propia zona (`<div id="historial-estado-vista">`, `.cofres-lista`), asi que
   `.tarjeta > .estado-vista` no llegaba a ninguna de las dos. Se probo, y el
   doble borde seguia ahi. */
.tarjeta .estado-vista {
  background: none;
  border: 0;
}

.estado-vista__icono {
  display: grid;
  place-items: center;
  width: 56px;
  height: 56px;
  border-radius: 50%;
  background: var(--fondo);
}

.estado-vista--error .estado-vista__icono {
  background: var(--error-insignia);
}
.estado-vista--vacio .estado-vista__icono {
  background: var(--info-insignia);
}

/* Un estado vacío o de error se lee, no se decora: el detalle en segundo
   nivel y con ancho de lectura cómodo. */
.estado-vista__detalle {
  color: var(--texto-2);
  max-width: 46ch;
}

.estado-vista__titulo {
  font-family: var(--t-subtitulo-familia);
  font-size: var(--t-subtitulo-tam);
  font-weight: var(--t-subtitulo-peso);
}

/* Estado de carga: esqueletos, no un girador. El usuario ve la forma
   de lo que viene, y eso hace la espera más corta. */
.estado-vista--cargando {
  align-items: stretch;
  justify-content: flex-start;
}

.esqueleto {
  height: 14px;
  border-radius: 4px;
  background: var(--pista);
}
.esqueleto--corto {
  width: 40%;
}
.esqueleto--largo {
  width: 75%;
  height: 10px;
}

/* =======================================================================
   Diálogo — bloquea la interacción, capa 3
   ======================================================================= */

.velo {
  position: fixed;
  inset: 0;
  /* Misma capa que la ficha de producto (tokens.css, --capa-modal): el orden
     del DOM decide quién queda encima. */
  z-index: var(--capa-modal);
  background: var(--interaccion-velo-modal);
  display: grid;
  place-items: center;
}

/* Con un modal abierto, la página de atrás no se desplaza (comun/ui/dialogo.js,
   bloquearDesplazamiento). */
html.con-modal {
  overflow: hidden;
}

/* La Pantalla 4 atenúa el contenido pero deja la cabecera legible y usable:
   en Figma el velo empieza en y=64, no en y=0. Variante y no cambio del velo
   base porque un diálogo destructivo sí debe taparlo todo. */
.velo--bajo-cabecera {
  top: var(--cabecera-alto);
}

.dialogo {
  /* `relative`: el botón de cerrar se ancla a la esquina del diálogo. */
  position: relative;
  width: min(480px, calc(100vw - var(--e-8)));
  display: flex;
  flex-direction: column;
  gap: var(--e-4);
  padding: var(--e-6);
  background:
    var(--energia) top left / 100% 3px no-repeat,
    var(--superficie);
  border-radius: var(--radio);
  box-shadow: var(--sombra-4);
}

/* UX-GAME-1: el dialogo entra, no aparece. Seis pixeles y un fundido en
   `--duracion-normal`; sin rebote ni zoom. Bajo movimiento reducido las
   duraciones ya valen cero por tokens.css y solo queda el fundido, que
   tambien vale cero. */
@media (prefers-reduced-motion: no-preference) {
  .dialogo {
    animation: entrar-dialogo var(--duracion-normal) var(--curva-salida) both;
  }
}

@keyframes entrar-dialogo {
  from {
    opacity: 0;
    transform: translateY(6px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

.dialogo__encabezado {
  display: flex;
  align-items: center;
  gap: var(--e-3);
}

.dialogo__icono {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: var(--info-insignia);
  color: var(--info);
  flex: 0 0 auto;
}
.dialogo--peligro .dialogo__icono {
  background: var(--error-insignia);
  color: var(--error);
}

/* Retrato de héroe. El componente «Dialogo de validacion de heroe» lo pone a
   64 px, frente a los 40 del diálogo base. Es un modificador y no un cambio
   del icono base a propósito: el resto de diálogos siguen con su tamaño. */
.dialogo__icono--grande {
  width: 64px;
  height: 64px;
  background: var(--pista);
  color: var(--texto-3);
}

/* «Dialogo de validacion de heroe» (HU-SAL-003) mide 520 px y separa sus
   bloques 20 px, frente a los 480 y 16 del diálogo base. Son dos componentes
   distintos en Figma, no una corrección del base: `cuentas/registro.html`
   usa el base y no debe moverse. */
.dialogo--ancho {
  width: min(520px, calc(100vw - var(--e-8)));
  gap: var(--e-5);
}

/* Título y detalle van pegados —4 px— dentro del bloque del héroe. */
.pila--ajustada {
  gap: var(--e-1);
}

/* El botón destructivo va a la derecha; cancelar nunca es el prominente. */
.dialogo__acciones {
  display: flex;
  justify-content: flex-end;
  gap: var(--e-3);
}

/* UXC-7 — ConfirmCritical (§7.3.9): lo que va a pasar, en una lista, y la
   palabra que hay que escribir para poder confirmar. */
.confirmacion-critica {
  display: grid;
  gap: var(--e-3);
}

.confirmacion-critica p {
  margin: 0;
}

.confirmacion-critica__consecuencias {
  display: grid;
  gap: var(--e-1);
  margin: 0;
  padding: var(--e-3) var(--e-3) var(--e-3) var(--e-6);
  border-left: 3px solid var(--error);
  border-radius: var(--radio-chico);
  background: var(--error-insignia);
  color: var(--texto);
}

/* =======================================================================
   Paginación — RNF-USA-001, dieciséis por página
   ======================================================================= */

.paginacion {
  display: flex;
  align-items: center;
  justify-content: space-between;
  /* Sin `flex-wrap` el control desbordaba a lo ancho en cuanto habia muchas
     paginas. Venia de la copia de `comun/paginacion.css`, que se retira. */
  flex-wrap: wrap;
  gap: var(--e-3);
}

.paginacion__info {
  font-family: var(--t-meta-familia);
  font-size: var(--t-meta-tam);
  color: var(--tinta-3);
}

.paginacion__paginas {
  display: flex;
  flex-wrap: wrap;
  gap: var(--e-2);
}

.paginacion__pagina {
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  border: 1px solid var(--borde-int);
  border-radius: var(--radio);
  background: var(--superficie);
  color: var(--texto-2);
  font-family: var(--t-etiqueta-familia);
  font-size: var(--t-etiqueta-tam);
  font-weight: var(--t-etiqueta-peso);
  font-variant-numeric: var(--cifras-tabulares);
  cursor: pointer;
}

.paginacion__pagina:hover {
  border-color: var(--primaria);
  color: var(--primaria);
}

/* RNF-ACC-002: el foco siempre visible. El contorno va por fuera del borde
   para que no lo tape la casilla vecina. Se declara aqui y no solo en
   `base.css` porque hay vistas que cargan el componente sin la base. */
.paginacion__pagina:focus-visible {
  outline: 2px solid var(--primaria);
  outline-offset: 2px;
}

/* La pagina en curso lleva `aria-current="page"`, que es lo que anuncia el
   lector de pantalla; el color refuerza, no sustituye. */
.paginacion__pagina[aria-current='page'] {
  background: var(--primaria-relleno);
  border-color: var(--primaria-relleno);
  color: var(--texto-sobre-color);
  cursor: default;
}

.paginacion__pagina[aria-current='page']:hover {
  color: var(--texto-sobre-color);
}

/* Telefonos: el control se apila para no forzar desplazamiento lateral. */
@media (max-width: 599px) {
  .paginacion {
    justify-content: center;
  }
  .paginacion__info {
    width: 100%;
    text-align: center;
  }
}

/* =======================================================================
   Tabla
   Se compone apilando filas: no se dibuja la rejilla completa.
   ======================================================================= */

.tabla {
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-radius: var(--radio);
  overflow: hidden;
}

.tabla__fila {
  display: flex;
  align-items: center;
  gap: var(--e-4);
  padding: var(--e-3) var(--e-4);
  border-top: 1px solid var(--borde);
}

.tabla__fila:first-child {
  border-top: 0;
}

.tabla__fila--encabezado {
  background: var(--fondo);
  border-top: 0;
  font-family: var(--t-etiqueta-familia);
  font-size: var(--t-meta-tam);
  font-weight: var(--t-etiqueta-peso);
  color: var(--texto-3);
  text-transform: uppercase;
  letter-spacing: 0.03em;
}

.tabla__fila--alterna {
  background: var(--fondo);
}

.tabla__celda {
  flex: 0 0 auto;
  font-variant-numeric: var(--cifras-tabulares);
}
.tabla__celda--principal {
  flex: 1;
  font-weight: 600;
}
.tabla__celda--accion {
  text-align: right;
  color: var(--primaria);
}

/* --- Tabla de datos con <table> -------------------------------------------
   Mi cuenta pinta «Tus batallas» y los movimientos de créditos con una
   <table> semántica (encabezados con scope, una fila por partida), no con
   filas .tabla__fila. Sin reglas de celda, el navegador centraba los
   encabezados y dejaba las celdas a la izquierda y sin aire: las columnas no
   casaban (auditoría de DEV del 30-sep, «Tus batallas»). El encabezado y la
   celda de una columna llevan la misma clase de alineación. */

.tabla--datos {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
}

.tabla--datos th,
.tabla--datos td {
  padding: var(--e-3) var(--e-4);
  text-align: start;
  vertical-align: middle;
}

.tabla--datos thead th {
  background: var(--fondo);
  font-family: var(--t-etiqueta-familia);
  font-size: var(--t-meta-tam);
  font-weight: var(--t-etiqueta-peso);
  color: var(--texto-3);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  white-space: nowrap;
}

.tabla--datos tbody td {
  border-top: 1px solid var(--borde);
  font-variant-numeric: var(--cifras-tabulares);
}

/* Cifras y acciones, a la derecha: se comparan de un vistazo y el botón no
   baila con el ancho del texto de las otras columnas. */
.tabla--datos .tabla__numero,
.tabla--datos .tabla__accion {
  text-align: end;
}

.tabla--datos .tabla__accion {
  white-space: nowrap;
}

/* A 375 px la tabla de partidas se desplaza dentro de su tarjeta
   (.tabla-envoltorio); la de movimientos cabe con menos aire lateral. */
@media (max-width: 480px) {
  .tabla--datos th,
  .tabla--datos td {
    padding: var(--e-2) var(--e-3);
  }
}

/* =======================================================================
   Pestañas
   La activa se marca con color Y con la línea, nunca solo con color.
   Se recorren con las flechas, no con el tabulador.
   ======================================================================= */

.pestanas {
  display: flex;
  gap: var(--e-5);
  border-bottom: 1px solid var(--borde);
  /* UX-R2.9 — cuatro pestanas no caben en 375 px: en `perfil` el documento
     medía 401 px y la ultima se salia por la derecha. Se desplazan a lo
     ancho en vez de empujar la pagina entera; el resto de la vista deja de
     tener scroll horizontal, que es lo que de verdad molesta.

     `overscroll-behavior-x: contain` para que llegar al final de la tira no
     dispare el gesto de «atras» del navegador. */
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scrollbar-width: thin;
}

.pestanas > .pestana {
  /* Sin esto, `flex` las encoge hasta que el texto se parte en dos lineas en
     vez de desplazarse. */
  flex: 0 0 auto;
  white-space: nowrap;
}

/* Auditoria de DEV del 30-sep: a 375 px las cinco pestanas de «Mi cuenta»
   seguian sin caber y las ultimas quedaban fuera de la vista (habia que
   adivinar que la tira se desplazaba). En pantalla estrecha pasan a dos
   filas: todas a la vista y ninguna escondida. */
@media (max-width: 480px) {
  .pestanas {
    flex-wrap: wrap;
    column-gap: var(--e-2);
    row-gap: 0;
    overflow-x: visible;
  }
}

.pestana {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--e-2);
  padding: 0;
  border: 0;
  background: none;
  /* Tinta: en «Mi cuenta» las pestanas van directamente sobre la atmosfera. */
  color: var(--tinta-2);
  cursor: pointer;
  font: inherit;
  transition: color var(--transicion);
}

.pestana:hover {
  color: var(--tinta);
}

.pestana__cara {
  display: flex;
  align-items: center;
  gap: var(--e-2);
  padding: 0 var(--e-3);
  font-family: var(--t-etiqueta-familia);
  font-size: var(--t-etiqueta-tam);
  font-weight: var(--t-etiqueta-peso);
}

.pestana__linea {
  width: 100%;
  height: 2px;
  background: transparent;
}

.pestana[aria-selected='true'] {
  color: var(--tinta-enlace);
}
.pestana[aria-selected='true'] .pestana__linea {
  background: var(--primaria);
}

/* =======================================================================
   Menú desplegable
   ======================================================================= */

.menu {
  min-width: 280px;
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-radius: var(--radio);
  box-shadow: var(--sombra-3);
  overflow: hidden;
}

.menu__grupo {
  padding: var(--e-2) var(--e-4);
  background: var(--fondo);
  font-family: var(--t-etiqueta-familia);
  font-size: var(--t-meta-tam);
  font-weight: var(--t-etiqueta-peso);
  color: var(--texto-3);
  text-transform: uppercase;
  letter-spacing: 0.03em;
}

.menu__opcion {
  display: flex;
  align-items: center;
  gap: var(--e-3);
  width: 100%;
  padding: var(--e-3);
  border: 0;
  border-top: 1px solid var(--borde);
  background: none;
  color: var(--texto);
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.menu__opcion:hover {
  background: var(--interaccion-velo-hover);
}

/* Peligro siempre va la última del menú, separada del resto. */
.menu__opcion--peligro {
  color: var(--error);
}

/* =======================================================================
   Ayuda contextual
   Aparece al señalar Y al enfocar con teclado. Se cierra con Escape.
   Máximo dos líneas: si necesita más, es documentación.
   -----------------------------------------------------------------------
   OJO (UX-R3.3): esto es un BOCADILLO. Lleva fondo cromo, sombra de capa 3 y
   una flecha, y no sabe esconderse solo: quien lo pone en el marcado lo
   enseña siempre. Tres vistas —auditoría, historial y cofres— lo usaban como
   párrafo descriptivo bajo el `<h1>` y como indicador de página dentro de la
   paginación. El resultado eran dos cajas oscuras flotando fijas en medio de
   la pantalla, en la vista que consulta el registro inmutable del sistema.

   Para un párrafo descriptivo, `.t-meta`. Para el indicador de página,
   `.paginacion__info`. Este componente es para ayuda que aparece y se va.
   ======================================================================= */

.ayuda {
  position: relative;
  max-width: 260px;
  padding: var(--e-3);
  background: var(--cromo);
  color: var(--texto-sobre-color);
  border-radius: var(--radio);
  box-shadow: var(--sombra-3);
  font-size: var(--t-meta-tam);
}

.ayuda::after {
  content: '';
  position: absolute;
  left: 50%;
  translate: -50% 0;
  border: 6px solid transparent;
}

.ayuda--abajo::after {
  top: -12px;
  border-bottom-color: var(--cromo);
}
.ayuda--arriba::after {
  bottom: -12px;
  border-top-color: var(--cromo);
}

/* =======================================================================
   Tecla — indicador de atajo
   Si el atajo existe pero no se muestra, no existe para el jugador.
   ======================================================================= */

.tecla {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 26px;
  padding: 3px var(--e-2);
  background: var(--superficie);
  border: 1px solid var(--borde-int);
  border-radius: var(--radio-chico);
  font-family: var(--t-etiqueta-familia);
  font-size: var(--t-meta-tam);
  font-weight: var(--t-etiqueta-peso);
  color: var(--texto-2);
}

/* =======================================================================
   Estado de conexión — HU-REN-001 y HU-DIS-003
   Informar no es interrumpir: nunca bloquea la pantalla.
   ======================================================================= */

.conexion {
  display: inline-flex;
  align-items: center;
  gap: var(--e-1);
  padding: 4px var(--e-3);
  border-radius: var(--radio-pildora);
  font-family: var(--t-etiqueta-familia);
  font-size: var(--t-meta-tam);
  font-weight: var(--t-etiqueta-peso);
}

.conexion--estable {
  background: var(--exito-insignia);
  color: var(--exito);
}
.conexion--latencia {
  background: var(--advertencia-insignia);
  color: var(--advertencia);
}
.conexion--reconectando {
  background: var(--advertencia-insignia);
  color: var(--advertencia);
}
/* UX-R3.8 — esto iba en rojo, y no es un error: sin canal en tiempo real la
   vista sigue funcionando, sigue trayendo notificaciones y se pone al día
   sola; lo único que pasa es que los avisos tardan un poco más. El rojo es
   para lo que está roto. El ámbar dice «ojo, algo va distinto», que es
   exactamente lo que pasa. */
.conexion--sin-conexion {
  background: var(--advertencia-insignia);
  color: var(--advertencia);
}

/* =======================================================================
   Panel de resultado
   La derrota no castiga: muestra la experiencia ganada y ofrece revancha
   con la misma jerarquía que la victoria.
   ======================================================================= */

.resultado {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--e-5);
  width: min(660px, calc(100vw - var(--e-8)));
  padding: var(--e-8);
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-radius: var(--radio);
}

.resultado__titulo {
  font-family: var(--t-titulo-familia);
  font-size: var(--t-display-tam);
  line-height: var(--t-display-alto);
  font-weight: 700;
  letter-spacing: 0.1em;
  color: var(--exito);
}

.resultado--derrota .resultado__titulo {
  color: var(--error);
}
.resultado--empate .resultado__titulo {
  color: var(--info);
}

.resultado__marcadores {
  display: flex;
  gap: var(--e-4);
  width: 100%;
}

.resultado__marcador {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--e-1);
  padding: var(--e-4) 0;
  background: var(--fondo);
  border-radius: var(--radio);
  text-align: center;
}

.resultado__cifra {
  font-family: var(--t-titulo-familia);
  font-size: var(--t-titulo-xl-tam);
  line-height: var(--t-titulo-xl-alto);
  font-weight: 700;
  font-variant-numeric: var(--cifras-tabulares);
}

/* =======================================================================
   AMPLIACIÓN — componentes compartidos por los tres grupos
   -----------------------------------------------------------------------
   Todo lo de aquí abajo sale del barrido de los tres SRS:
   REQUISITOS GRUPO 2, GRUPO SANTIAGO y REQUISITOS GRUPO 6. Cada bloque
   lleva anotado el requisito que lo obliga. Igual que arriba: ningún
   hexadecimal, todo baja de tokens.css.
   ======================================================================= */

/* =======================================================================
   Controles de formulario
   RF-JUE-001 · RF-JUE-004 · RF-COM-006 · RF-COM-009 · RF-USR-009
   RF-CAR-006 · RF-INV-004 · RF-ADM-001 · RF-MIS-002
   -----------------------------------------------------------------------
   `.modalidad` (más arriba) sigue siendo la tarjeta de radio del selector
   de modalidad. Esto es el radio normal, para listas largas de opciones.
   ======================================================================= */

.casilla,
.radio {
  display: flex;
  align-items: flex-start;
  gap: var(--e-3);
  cursor: pointer;
}

.casilla__entrada,
.radio__entrada {
  appearance: none;
  flex: 0 0 auto;
  width: 20px;
  height: 20px;
  margin: 0;
  background: var(--superficie);
  border: 2px solid var(--borde-int);
  cursor: pointer;
  transition:
    background var(--transicion),
    border-color var(--transicion);
}

.casilla__entrada {
  border-radius: var(--radio-chico);
}
.radio__entrada {
  border-radius: 50%;
}

.casilla__entrada:checked,
.radio__entrada:checked {
  background: var(--primaria-relleno);
  border-color: var(--primaria-relleno);
}

/* La marca no es solo color: se dibuja un símbolo dentro. */
.casilla__entrada:checked::after {
  content: '';
  display: block;
  width: 6px;
  height: 10px;
  margin: 1px auto;
  border: solid var(--texto-sobre-color);
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}

.casilla__entrada:indeterminate {
  background: var(--primaria-relleno);
  border-color: var(--primaria-relleno);
}

.casilla__entrada:indeterminate::after {
  content: '';
  display: block;
  width: 10px;
  height: 2px;
  margin: 7px auto;
  background: var(--texto-sobre-color);
}

.radio__entrada:checked::after {
  content: '';
  display: block;
  width: 8px;
  height: 8px;
  margin: 4px auto;
  border-radius: 50%;
  background: var(--texto-sobre-color);
}

/* UX-R2.8d — estas siete reglas decian
     `outline: var(--foco-grosor) solid var(--anillo-foco)`
   y `--anillo-foco` NO es un color: vale `0 0 0 3px rgba(34,68,191,.35)`,
   pensado para `box-shadow`. Un `outline: 3px solid 0 0 0 3px rgba(…)` es
   invalido, asi que el navegador tiraba la declaracion entera y **el foco no
   se dibujaba** en casillas, radios, desplegables, estrellas, ranuras,
   acciones de combate, zona de carga y el selector de objeto.

   RNF-ACC-002 dice que el foco es siempre visible, asi que esto era un
   incumplimiento medible, no una preferencia. El color correcto ya existia:
   `--interaccion-foco-anillo`. `clases-del-kit.test.js` lo vigila. */
.casilla__entrada:focus-visible,
.radio__entrada:focus-visible {
  outline: var(--foco-grosor) solid var(--interaccion-foco-anillo);
  outline-offset: 2px;
}

/* UX-R2.9 — misma razon que en el panel de filtros: el objetivo tactil de
   una casilla es su etiqueta, porque `<label>` reenvia el clic. La casilla
   se queda en 20 px, que es su tamano de diseno y cumple los 24x24 de WCAG
   2.5.8 contando el relleno de la etiqueta a su alrededor. */
.casilla,
.radio {
  min-height: var(--objetivo-tactil);
  align-items: center;
}

.casilla__etiqueta,
.radio__etiqueta {
  font-family: var(--t-cuerpo-familia);
  font-size: var(--t-cuerpo-tam);
  line-height: var(--t-cuerpo-alto);
  color: var(--texto);
}

.radio__detalle {
  display: block;
  font-family: var(--t-meta-familia);
  font-size: var(--t-meta-tam);
  color: var(--texto-2);
}

/* Desplegable — RF-COM-009 (ordenar), RF-USR-009 (filtrar), RF-ADM-001 */
.desplegable {
  display: flex;
  flex-direction: column;
  gap: var(--e-2);
}

.desplegable__control {
  height: var(--campo-alto);
  padding: 0 var(--e-3);
  background: var(--superficie);
  border: 1px solid var(--borde-int);
  border-radius: var(--radio);
  color: var(--texto);
  font-family: var(--t-cuerpo-familia);
  font-size: var(--t-cuerpo-tam);
  cursor: pointer;
}

.desplegable__control:focus-visible {
  outline: var(--foco-grosor) solid var(--interaccion-foco-anillo);
  outline-offset: 1px;
}

.desplegable--invalido .desplegable__control {
  border-color: var(--error);
}

/* Campo — tipos añadidos. RF-COM-001 · RF-CAR-006 · RF-INV-004 · RF-SUB-004 */
.campo--area .campo__control {
  height: auto;
  min-height: 96px;
  padding: var(--e-3);
  resize: vertical;
}

.campo--busqueda .campo__control,
.campo--prefijo .campo__control {
  padding-left: calc(var(--e-3) + var(--icono) + var(--e-2));
}

.campo__adorno {
  position: absolute;
  left: var(--e-3);
  top: 50%;
  transform: translateY(-50%);
  width: var(--icono);
  height: var(--icono);
  color: var(--texto-3);
  pointer-events: none;
}

.campo--prefijo .campo__adorno {
  color: var(--rareza-legendaria);
}

.campo__envoltorio {
  position: relative;
}

/* Campo numérico — RF-SUB-004 (incremento mínimo), RF-JUE-001, RF-PRD-003 */
.campo-numerico__caja {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: var(--campo-alto);
  background: var(--superficie);
  border: 1px solid var(--borde-int);
  border-radius: var(--radio);
  overflow: hidden;
}

.campo-numerico__boton {
  width: 36px;
  height: 100%;
  border: 0;
  background: transparent;
  color: var(--primaria);
  font-family: var(--t-subtitulo-familia);
  font-size: var(--t-subtitulo-tam);
  cursor: pointer;
}

.campo-numerico__boton:hover {
  background: var(--interaccion-velo-hover);
}
.campo-numerico__boton:disabled {
  color: var(--texto-3);
  cursor: not-allowed;
}

.campo-numerico__valor {
  flex: 1 1 auto;
  border: 0;
  background: transparent;
  text-align: center;
  font-variant-numeric: var(--cifras-tabulares);
  color: var(--texto);
}

/* =======================================================================
   Calificación en estrellas
   RF-COM-002 (calificación única) · RF-COM-003 (promedio) · RF-INV-005
   -----------------------------------------------------------------------
   Las estrellas redondean; la precisión la da el número de al lado.
   Por eso el detalle textual no es opcional.
   ======================================================================= */

.estrellas {
  display: inline-flex;
  align-items: center;
  gap: var(--e-2);
}

.estrellas__lista {
  display: inline-flex;
  gap: 2px;
}

.estrellas__estrella {
  width: 18px;
  height: 18px;
  color: var(--pista);
}
.estrellas__estrella--llena {
  color: var(--rareza-legendaria);
}

.estrellas__detalle {
  font-family: var(--t-meta-familia);
  font-size: var(--t-meta-tam);
  color: var(--texto-2);
}

/* Editable: RF-COM-002 permite calificar una sola vez por producto. */
.estrellas--editable .estrellas__estrella {
  cursor: pointer;
}
.estrellas--editable .estrellas__entrada:focus-visible + .estrellas__estrella {
  outline: var(--foco-grosor) solid var(--interaccion-foco-anillo);
  outline-offset: 2px;
}

/* =======================================================================
   Barra de progreso genérica
   RF-HER-003 (nivel 1 a 8) · RF-MIS-007 (simulación) · RF-SUB-002
   -----------------------------------------------------------------------
   NO es .barra-vida. Aquella lleva los umbrales 60/40 y es del combate.
   ======================================================================= */

.progreso__cabecera {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: var(--e-2);
}

.progreso__etiqueta,
.progreso__valor {
  font-family: var(--t-etiqueta-familia);
  font-size: var(--t-etiqueta-tam);
  font-weight: var(--t-etiqueta-peso);
}

.progreso__valor {
  color: var(--texto-2);
  font-variant-numeric: var(--cifras-tabulares);
}

.progreso__riel {
  height: 10px;
  background: var(--pista);
  border-radius: var(--radio-chico);
  overflow: hidden;
}

.progreso__relleno {
  height: 100%;
  background: var(--primaria-relleno);
  border-radius: var(--radio-chico);
  transition: width var(--duracion-normal) var(--curva-estandar);
}

/* =======================================================================
   Contador / cuenta atrás
   RF-SUB-002 (duración) · RF-SUB-011 (subastas activas) · turno de combate
   ======================================================================= */

.contador {
  display: inline-flex;
  align-items: center;
  gap: var(--e-2);
  padding: var(--e-2) var(--e-3);
  border-radius: var(--radio);
  background: var(--info-insignia);
  color: var(--info);
  font-family: var(--t-etiqueta-familia);
  font-size: var(--t-etiqueta-tam);
  font-weight: var(--t-etiqueta-peso);
  font-variant-numeric: var(--cifras-tabulares);
}

.contador--urgente {
  background: var(--error-insignia);
  color: var(--error);
}

/* =======================================================================
   Tarjeta de contenido — tipos
   RF-INV-005 · RF-MIS-001 · RF-SUB-011 · RF-TOR-003 · RF-TOR-008
   -----------------------------------------------------------------------
   Un solo componente con cuatro tipos. La tarjeta de sala del juego en
   línea sigue aparte porque su anatomía y sus estados son propios.
   ======================================================================= */

.tarjeta__imagen {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 120px;
  margin: calc(var(--e-4) * -1) calc(var(--e-4) * -1) var(--e-3);
  background: var(--pista);
  border-radius: var(--radio) var(--radio) 0 0;
  color: var(--texto-3);
}

.tarjeta__titulo {
  font-family: var(--t-cuerpo-familia);
  font-size: var(--t-cuerpo-tam);
  font-weight: var(--t-cuerpo-peso-enfasis);
  color: var(--texto);
}

.tarjeta__meta {
  font-family: var(--t-meta-familia);
  font-size: var(--t-meta-tam);
  color: var(--texto-2);
}

.tarjeta__pie {
  display: flex;
  align-items: center;
  gap: var(--e-2);
  margin-top: var(--e-2);
  font-family: var(--t-etiqueta-familia);
  font-size: var(--t-etiqueta-tam);
  font-weight: var(--t-etiqueta-peso);
  color: var(--primaria);
}

.tarjeta--producto .tarjeta__pie {
  color: var(--rareza-legendaria);
}

/* RF-INV-011 · RF-MIS-012: producto bloqueado por subasta o por misión. */
.tarjeta--bloqueada {
  opacity: 0.6;
  pointer-events: none;
}

/* Tarjeta de partida/sala (UX-GAME-4): es un boton, asi que hereda estilos
   de agente de usuario que hay que apagar; el texto va a la izquierda y el
   titulo lleva la modalidad. La sala en la que se puede entrar lleva el
   acento lateral azul; la llena, ninguno. */
button.tarjeta--partida {
  width: 100%;
  font: inherit;
  color: var(--texto);
  text-align: left;
  cursor: pointer;
  border-left: 3px solid var(--primaria);
}

button.tarjeta--partida:disabled {
  cursor: not-allowed;
  border-left-color: var(--borde);
}

.tarjeta--partida[data-estado='PRIVADA'] {
  border-left-color: var(--acento-relleno);
}

.tarjeta--partida .tarjeta__cabecera {
  width: 100%;
  align-items: center;
}

.tarjeta--partida .tarjeta__titulo {
  font-size: var(--t-etiqueta-tam);
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

/* =======================================================================
   Panel de métrica
   RF-PRD-001 · RF-MET-001 · RF-MET-002 · RF-MET-003 · RF-MET-004 · RF-CHA-012
   -----------------------------------------------------------------------
   Contenedor y estados. La gráfica la dibuja Chart.js dentro de
   `.metrica__grafica`. Carga, vacío y error se resuelven con .estado-vista.
   ======================================================================= */

.metrica__titulo {
  font-family: var(--t-etiqueta-familia);
  font-size: var(--t-etiqueta-tam);
  font-weight: var(--t-etiqueta-peso);
  color: var(--texto-2);
}

.metrica__cifra {
  font-family: var(--t-titulo-familia);
  font-size: var(--t-titulo-tam);
  font-weight: var(--t-titulo-peso);
  color: var(--texto);
  font-variant-numeric: var(--cifras-tabulares);
}

.metrica__tendencia {
  font-family: var(--t-meta-familia);
  font-size: var(--t-meta-tam);
  color: var(--exito);
}

.metrica__tendencia--baja {
  color: var(--error);
}

.metrica__grafica {
  height: 88px;
  margin-top: var(--e-2);
  background: var(--fondo);
  border-radius: var(--radio-chico);
}

.metrica__pie {
  margin-top: var(--e-2);
  font-family: var(--t-meta-familia);
  font-size: var(--t-meta-tam);
  color: var(--texto-3);
}

/* =======================================================================
   Línea de tiempo
   RF-USR-010 · RF-PRD-008 · RF-MIS-011 · RF-AUD-001
   ======================================================================= */

.linea-tiempo {
  list-style: none;
  margin: 0;
  padding: 0;
}

.linea-tiempo__item {
  position: relative;
  display: grid;
  grid-template-columns: 12px 1fr;
  gap: var(--e-3);
  padding-bottom: var(--e-4);
}

/* El conector nace del punto y baja hasta el siguiente hito. */
.linea-tiempo__item::before {
  content: '';
  position: absolute;
  left: 5px;
  top: 12px;
  bottom: 0;
  width: 2px;
  background: var(--borde);
}

.linea-tiempo__item--ultima::before {
  display: none;
}

.linea-tiempo__punto {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--primaria-relleno);
}

.linea-tiempo__titulo {
  font-family: var(--t-cuerpo-familia);
  font-size: var(--t-cuerpo-tam);
  font-weight: var(--t-cuerpo-peso-enfasis);
}

.linea-tiempo__meta {
  font-family: var(--t-meta-familia);
  font-size: var(--t-meta-tam);
  color: var(--texto-3);
}

/* =======================================================================
   Indicador de paso
   RF-MIS-009 (Disponible, Bloqueada, En progreso, Completada)
   RF-MIS-004 · RF-MIS-005 · RF-CAR-010 · RF-USR-007
   ======================================================================= */

.pasos {
  display: flex;
  align-items: center;
  gap: var(--e-3);
  list-style: none;
  margin: 0;
  padding: 0;
}

.paso {
  display: flex;
  align-items: center;
  gap: var(--e-2);
}

.paso__marcador {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: var(--pista);
  color: var(--texto-3);
  font-family: var(--t-etiqueta-familia);
  font-size: var(--t-etiqueta-tam);
  font-weight: var(--t-etiqueta-peso);
}

.paso--actual .paso__marcador {
  background: var(--primaria-relleno);
  color: var(--texto-sobre-color);
}
.paso--completado .paso__marcador {
  background: var(--exito);
  color: var(--texto-sobre-color);
}

.paso__etiqueta {
  color: var(--texto-3);
}

.paso--actual .paso__etiqueta,
.paso--completado .paso__etiqueta {
  color: var(--texto);
  font-weight: var(--t-cuerpo-peso-enfasis);
}

.paso__conector {
  width: 32px;
  height: 2px;
  background: var(--pista);
}

.paso--completado .paso__conector {
  background: var(--exito);
}

/* =======================================================================
   Barra de acciones en lote
   RF-COM-008 · RF-USR-009 · RF-PRD-004 · RF-PRD-005
   -----------------------------------------------------------------------
   Se apoya en `.casilla__entrada:indeterminate` de la fila de encabezado
   para el «seleccionar todo».
   ======================================================================= */

.lote {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--e-4);
  padding: var(--e-3) var(--e-4);
  background: var(--cromo);
  color: var(--texto-sobre-color);
  border-radius: var(--radio);
  box-shadow: var(--sombra-3);
}

.lote__seleccion {
  font-family: var(--t-etiqueta-familia);
  font-size: var(--t-etiqueta-tam);
  font-weight: var(--t-etiqueta-peso);
}

.lote__acciones {
  display: flex;
  gap: var(--e-2);
}

.lote__accion {
  display: inline-flex;
  align-items: center;
  gap: var(--e-2);
  padding: var(--e-2) var(--e-3);
  background: transparent;
  border: 1px solid var(--texto-sobre-color);
  border-radius: var(--radio);
  color: var(--texto-sobre-color);
  font: inherit;
  cursor: pointer;
}

.lote__accion:hover {
  background: var(--interaccion-velo-hover);
}

/* =======================================================================
   Banner informativo rotativo
   RF-ADM-003 «Gestión de anuncios y banners» — DUEÑO: GRUPO 2
   RF-NOT-002 (fecha de publicación y vigencia) · RF-INV-003
   -----------------------------------------------------------------------
   Aquí solo vive la forma. La rotación, la programación y las reglas de
   vigencia las decide el Grupo 2.
   ======================================================================= */

.banner {
  display: flex;
  align-items: center;
  gap: var(--e-4);
  padding: var(--e-4) var(--e-5);
  background: var(--info-insignia);
  border-radius: var(--radio);
}

.banner__icono {
  flex: 0 0 auto;
  width: var(--icono-grande);
  height: var(--icono-grande);
  color: var(--info);
}
.banner__cuerpo {
  flex: 1 1 auto;
}

.banner__titulo {
  font-family: var(--t-cuerpo-familia);
  font-size: var(--t-cuerpo-tam);
  font-weight: var(--t-cuerpo-peso-enfasis);
}

.banner__texto {
  color: var(--texto-2);
}

/* RF-NOT-002 exige mostrar publicación y vigencia en cada mensaje. */
.banner__vigencia {
  font-family: var(--t-meta-familia);
  font-size: var(--t-meta-tam);
  color: var(--texto-3);
}

.banner__rotacion {
  display: flex;
  gap: var(--e-2);
}

.banner__punto {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--pista);
  border: 0;
  padding: 0;
  cursor: pointer;
}

.banner__punto[aria-current='true'] {
  background: var(--info);
}

/* =======================================================================
   Fila de resumen económico
   RF-CAR-007 · RF-CAR-010 · RF-PAG-002
   ======================================================================= */

.resumen__fila {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: var(--e-3) 0;
}

.resumen__fila--subtotal,
.resumen__fila--total {
  border-top: 1px solid var(--borde);
}

.resumen__fila--total {
  font-weight: var(--t-cuerpo-peso-enfasis);
}

.resumen__fila--total .resumen__importe {
  color: var(--primaria);
}

.resumen__importe {
  font-variant-numeric: var(--cifras-tabulares);
}

/* =======================================================================
   Insignia de logro
   RF-MIS-015 · RF-USR-012
   -----------------------------------------------------------------------
   Sin niveles: ningún documento define bronce, plata ni oro.
   ======================================================================= */

.logro {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--e-2);
  padding: var(--e-3);
  text-align: center;
}

.logro__medalla {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 56px;
  height: 56px;
  border-radius: 50%;
  background: var(--rareza-epica-insignia);
  color: var(--rareza-epica);
}

.logro--bloqueado .logro__medalla {
  background: var(--pista);
  color: var(--texto-3);
}
.logro--bloqueado .logro__titulo {
  color: var(--texto-3);
}

.logro__titulo {
  font-family: var(--t-etiqueta-familia);
  font-size: var(--t-etiqueta-tam);
  font-weight: var(--t-etiqueta-peso);
}

.logro__detalle {
  font-family: var(--t-meta-familia);
  font-size: var(--t-meta-tam);
  color: var(--texto-3);
}

/* =======================================================================
   Ranura de equipamiento y de habilidad
   RF-INV-009: 2 armas + 6 piezas de armadura + 2 ítems = 10 ranuras
   RF-MIS-005: 3 rotaciones con prioridad alta, media y baja
   Bloqueada: RF-JUE-018 · RF-MIS-012 · RF-INV-011
   ======================================================================= */

.ranura {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--e-2);
}

.ranura__caja {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 64px;
  height: 64px;
  border-radius: var(--radio);
  background: var(--rareza-epica-insignia);
  border: 1px solid var(--borde);
  color: var(--rareza-epica);
  cursor: pointer;
}

/* Vacía: borde discontinuo y un más. El estado no depende del color. */
.ranura--vacia .ranura__caja {
  background: var(--fondo);
  border: 2px dashed var(--borde);
  color: var(--texto-3);
}

.ranura--seleccionada .ranura__caja {
  border: var(--foco-grosor) solid var(--anillo-foco);
}

/* Bloqueada: candado visible, no solo atenuación. */
.ranura--bloqueada .ranura__caja {
  background: var(--pista);
  color: var(--texto-3);
  cursor: not-allowed;
}

.ranura__caja:focus-visible {
  outline: var(--foco-grosor) solid var(--interaccion-foco-anillo);
  outline-offset: 2px;
}

.ranura__etiqueta {
  font-family: var(--t-meta-familia);
  font-size: var(--t-meta-tam);
  color: var(--texto-2);
}

.ranura--bloqueada .ranura__etiqueta {
  color: var(--texto-3);
}

/* =======================================================================
   Encuentro del torneo
   RF-TOR-004: 8 equipos · ganadores 1-6, 11 y final · secundarios 7-10, 12, 13
   RF-TOR-005: equipo sustituido por la IA, marcado en el nombre
   ======================================================================= */

.encuentro {
  width: 220px;
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-radius: var(--radio);
  overflow: hidden;
  box-shadow: var(--sombra-1);
}

.encuentro--en-curso {
  border: 2px solid var(--primaria-relleno);
}

.encuentro__cabecera {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: var(--e-1) var(--e-3);
  padding: var(--e-2) var(--e-3);
  background: var(--fondo);
  font-family: var(--t-meta-familia);
  font-size: var(--t-meta-tam);
  color: var(--texto-3);
}

.encuentro--en-curso .encuentro__cabecera {
  background: var(--info-insignia);
  color: var(--info);
}
.encuentro--finalizado .encuentro__cabecera {
  background: var(--exito-insignia);
  color: var(--exito);
}

.encuentro__equipo {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: var(--e-2) var(--e-3);
}

.encuentro__equipo + .encuentro__equipo {
  border-top: 1px solid var(--borde);
}

/* El ganador no se distingue solo por color: cambia el peso del texto y lleva
   el acento lateral azul (UX-GAME-5), que se ve igual sin color. */
.encuentro__equipo--ganador {
  font-weight: var(--t-cuerpo-peso-enfasis);
  color: var(--texto);
  box-shadow: inset 3px 0 0 var(--primaria);
  background: color-mix(in srgb, var(--primaria) 6%, var(--superficie));
}

.encuentro--en-curso {
  box-shadow: var(--sombra-1), var(--brillo-primaria);
}

.encuentro__equipo--ganador .encuentro__marcador {
  color: var(--exito);
}

.encuentro__marcador {
  color: var(--texto-3);
  font-variant-numeric: var(--cifras-tabulares);
}

/* =======================================================================
   Reproductor de transmisión
   RF-TOR-006: directo, diferido y comentarios de los presentadores
   -----------------------------------------------------------------------
   El servicio de vídeo es externo. Aquí solo el marco y sus estados.
   ======================================================================= */

.transmision {
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-radius: var(--radio);
  overflow: hidden;
  box-shadow: var(--sombra-2);
}

.transmision__lienzo {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  aspect-ratio: 16 / 9;
  padding: var(--e-3);
  background: var(--cromo);
}

/* La etiqueta dice EN DIRECTO o GRABACIÓN: el color no basta. */
.transmision__etiqueta {
  align-self: flex-start;
  display: inline-flex;
  align-items: center;
  gap: var(--e-2);
  padding: var(--e-1) var(--e-3);
  border-radius: var(--radio-pildora);
  background: var(--pista);
  color: var(--texto-3);
  font-family: var(--t-meta-familia);
  font-size: var(--t-meta-tam);
}

.transmision--directo .transmision__etiqueta {
  background: var(--error);
  color: var(--texto-sobre-color);
}

.transmision__controles {
  display: flex;
  align-items: center;
  gap: var(--e-3);
  color: var(--texto-sobre-color);
}

.transmision__progreso {
  flex: 1 1 auto;
  height: 6px;
  background: var(--pista);
  border-radius: var(--radio-chico);
  overflow: hidden;
}

.transmision__avance {
  height: 100%;
  background: var(--primaria-relleno);
}
.transmision--directo .transmision__avance {
  background: var(--error);
  width: 100%;
}

.transmision__comentarios {
  padding: var(--e-3);
}

/* =======================================================================
   Mensaje del chatbot
   RF-CHA-006: respuestas directas, enriquecidas y paso a paso
   -----------------------------------------------------------------------
   DUEÑO DEL MÓDULO: GRUPO DE SANTIAGO (M14, Sprint 3). Aquí solo la forma
   visual compartida; el procesamiento de lenguaje y la base de conocimiento
   son decisiones suyas.
   ======================================================================= */

.chatbot__mensaje {
  padding: var(--e-3);
  background: var(--fondo);
  border-radius: var(--radio);
  box-shadow: var(--sombra-1);
}

.chatbot__tarjeta {
  display: flex;
  align-items: center;
  gap: var(--e-3);
  margin-top: var(--e-2);
  padding: var(--e-2) var(--e-3);
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-radius: var(--radio);
}

.chatbot__miniatura {
  flex: 0 0 auto;
  width: 36px;
  height: 36px;
  border-radius: var(--radio-chico);
  background: var(--pista);
}

.chatbot__enlace {
  display: inline-block;
  margin-top: var(--e-2);
  color: var(--primaria);
  font-weight: var(--t-etiqueta-peso);
}

.chatbot__pasos {
  list-style: none;
  margin: var(--e-2) 0 0;
  padding: 0;
}

.chatbot__paso {
  display: flex;
  align-items: center;
  gap: var(--e-2);
  padding: 2px 0;
}

.chatbot__paso-numero {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--info-insignia);
  color: var(--info);
  font-family: var(--t-meta-familia);
  font-size: var(--t-meta-tam);
}

/* -----------------------------------------------------------------------
   Ventana del asistente (comun/ui/ventana-chatbot.js)
   HU-CHA-001: chat para visitantes y jugadores · HU-CHA-011: calificar
   -----------------------------------------------------------------------
   Panel flotante NO modal, anclado sobre el botón `.chatbot-flotante`.
   Sin z-index a propósito: se cuelga al final del <body>, así que queda
   sobre el contenido de la vista, y el `.velo` de una confirmación (que se
   cuelga después) queda sobre él.
   ----------------------------------------------------------------------- */

.chatbot-ventana {
  position: fixed;
  right: var(--e-6);
  bottom: calc(56px + var(--e-6) + var(--e-3));
  width: min(380px, calc(100vw - var(--e-8)));
  max-height: min(600px, calc(100vh - 56px - var(--e-16)));
  display: flex;
  flex-direction: column;
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-radius: var(--radio);
  box-shadow: var(--sombra-3);
  overflow: hidden;
}

.chatbot-ventana__cabecera {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  column-gap: var(--e-3);
  padding: var(--e-3) var(--e-4);
  background: var(--superficie-2);
  border-bottom: 1px solid var(--borde-tenue);
}

.chatbot-ventana__titulo {
  margin: 0;
  font-family: var(--t-etiqueta-familia);
  font-size: var(--t-cuerpo-tam);
  font-weight: var(--t-etiqueta-peso);
}

.chatbot-ventana__estado {
  grid-column: 1 / -1;
  margin: 0;
  font-size: var(--t-meta-tam);
  color: var(--texto-2);
}

.chatbot-ventana__acciones {
  display: flex;
  align-items: center;
  gap: var(--e-2);
  flex: 0 0 auto;
}

/* Acciones secundarias (borrar, adjuntar): texto, no botón sólido, para
   no competir con «Enviar». Mantienen el alto táctil mínimo. */
.chatbot-ventana__accion {
  min-height: var(--objetivo-tactil);
  padding: 0 var(--e-1);
  border: 0;
  background: transparent;
  color: var(--primaria);
  font-size: var(--t-meta-tam);
  font-weight: var(--t-etiqueta-peso);
  text-decoration: underline;
  cursor: pointer;
}

.chatbot-ventana__accion:disabled {
  color: var(--texto-3);
  cursor: not-allowed;
}

.chatbot-ventana__cerrar {
  width: var(--objetivo-tactil);
  height: var(--objetivo-tactil);
  border: 0;
  border-radius: var(--radio);
  background: transparent;
  color: var(--texto-2);
  font-size: 1.5rem;
  line-height: 1;
  cursor: pointer;
}

.chatbot-ventana__cerrar:hover {
  background: var(--superficie-2);
  color: var(--texto);
}

.chatbot-ventana__registro {
  flex: 1 1 auto;
  min-height: 160px;
  margin: 0;
  padding: var(--e-4);
  list-style: none;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: var(--e-3);
  background: var(--fondo);
}

.chatbot-ventana__mensaje {
  max-width: 85%;
  padding: var(--e-2) var(--e-3);
  border-radius: var(--radio);
  box-shadow: var(--sombra-1);
}

.chatbot-ventana__mensaje--bot {
  align-self: flex-start;
  background: var(--superficie);
  border: 1px solid var(--borde-tenue);
}

.chatbot-ventana__mensaje--usuario {
  align-self: flex-end;
  background: var(--primaria-relleno);
  color: var(--texto-sobre-color);
}

.chatbot-ventana__texto {
  margin: 0;
  white-space: pre-line;
  overflow-wrap: anywhere;
}

.chatbot-ventana__meta {
  margin: var(--e-1) 0 0;
  font-size: var(--t-meta-tam);
  color: var(--texto-3);
}

.chatbot-ventana__mensaje--usuario .chatbot-ventana__meta,
.chatbot-ventana__mensaje--usuario .chatbot-ventana__adjunto {
  color: var(--texto-sobre-color);
}

.chatbot-ventana__adjunto {
  display: inline-block;
  margin-top: var(--e-1);
  font-size: var(--t-meta-tam);
  color: var(--primaria);
}

.chatbot-ventana__escribiendo,
.chatbot-ventana__cargando {
  align-self: flex-start;
  font-size: var(--t-meta-tam);
  color: var(--texto-2);
}


/* 7.4.6 — historial por páginas: «Ver mensajes anteriores» encabeza la lista
   mientras puede haber más hacia atrás. */
.chatbot-ventana__anteriores {
  align-self: center;
}

.chatbot-ventana__aviso {
  padding: 0 var(--e-4) var(--e-3);
  background: var(--fondo);
}

.chatbot-ventana__formulario {
  display: flex;
  flex-direction: column;
  gap: var(--e-2);
  padding: var(--e-3) var(--e-4);
  border-top: 1px solid var(--borde-tenue);
  background: var(--superficie);
}

.chatbot-ventana__fila {
  display: flex;
  align-items: flex-end;
  gap: var(--e-2);
}

.chatbot-ventana__entrada {
  flex: 1 1 auto;
  min-height: 2.75rem;
  max-height: 8rem;
  resize: vertical;
}

/* Respuestas enriquecidas del asistente: pasos, tarjetas, enlaces a
   secciones del sitio y botones (los botones reusan .chatbot-rapidas). */

.chatbot-enriquecido {
  display: flex;
  flex-direction: column;
  gap: var(--e-2);
  margin-top: var(--e-2);
}

.chatbot-enriquecido__pasos {
  margin: 0;
  padding-left: var(--e-5);
  display: flex;
  flex-direction: column;
  gap: var(--e-1);
}

.chatbot-enriquecido__tarjeta {
  padding: var(--e-2) var(--e-3);
  border: 1px solid var(--borde-tenue);
  border-left: 3px solid var(--primaria);
  border-radius: var(--radio);
  background: var(--superficie);
}

.chatbot-enriquecido__tarjeta-titulo {
  margin: 0;
  font-weight: var(--t-etiqueta-peso);
}

.chatbot-enriquecido__tarjeta-texto {
  margin: var(--e-1) 0 0;
  font-size: var(--t-meta-tam);
  color: var(--texto-2);
}

.chatbot-enriquecido__enlaces {
  display: flex;
  flex-wrap: wrap;
  gap: var(--e-2);
}

.chatbot-enriquecido__enlace {
  color: var(--primaria);
  font-weight: var(--t-etiqueta-peso);
}

.chatbot-enriquecido__soporte {
  border-color: var(--primaria);
}

/* Preguntas rápidas y autocompletado. Los botones son de texto, como las
   acciones secundarias, para no competir con «Enviar». La lista del
   autocompletado sale ENCIMA de la caja: la ventana está pegada abajo. */

.chatbot-rapidas {
  display: flex;
  flex-direction: column;
  gap: var(--e-1);
}

.chatbot-rapidas__cabecera {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--e-2);
}

.chatbot-rapidas__titulo {
  margin: 0;
  font-size: var(--t-meta-tam);
  color: var(--texto-2);
}

.chatbot-rapidas__tema {
  max-width: 55%;
  min-height: var(--objetivo-tactil);
  border: 1px solid var(--borde);
  border-radius: var(--radio-chico);
  background: var(--superficie);
  color: var(--texto);
  font-size: var(--t-meta-tam);
}

.chatbot-rapidas__lista {
  display: flex;
  flex-wrap: wrap;
  gap: var(--e-1);
}

.chatbot-rapidas__opcion {
  min-height: var(--objetivo-tactil);
  padding: 0 var(--e-2);
  border: 1px solid var(--borde);
  border-radius: 999px;
  background: var(--superficie-2);
  color: var(--primaria);
  font-size: var(--t-meta-tam);
  cursor: pointer;
}

.chatbot-rapidas__opcion:hover:not(:disabled) {
  border-color: var(--primaria);
}

.chatbot-rapidas__opcion:disabled {
  color: var(--texto-3);
  cursor: not-allowed;
}

.chatbot-ventana__fila--entrada {
  position: relative;
}

.chatbot-autocompletar {
  position: absolute;
  left: 0;
  right: 0;
  bottom: calc(100% + var(--e-1));
  margin: 0;
  padding: var(--e-1) 0;
  list-style: none;
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-radius: var(--radio);
  box-shadow: var(--sombra-2);
}

.chatbot-autocompletar__opcion {
  padding: var(--e-2) var(--e-3);
  cursor: pointer;
}

.chatbot-autocompletar__opcion:hover,
.chatbot-autocompletar__opcion[aria-selected='true'] {
  background: var(--superficie-2);
  color: var(--primaria);
}

/* «Soporte»: solicitud de soporte humano dentro de la ventana. Ocupa el
   sitio de la conversación mientras está abierto. */

.chatbot-soporte {
  flex: 1 1 auto;
  min-height: 160px;
  overflow-y: auto;
  padding: var(--e-3) var(--e-4);
}

.chatbot-soporte__cabecera {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--e-2);
}

.chatbot-soporte__titulo,
.chatbot-soporte__subtitulo {
  margin: 0;
  font-family: var(--t-etiqueta-familia);
  font-size: var(--t-cuerpo-tam);
  font-weight: var(--t-etiqueta-peso);
}

.chatbot-soporte__subtitulo {
  margin-top: var(--e-4);
}

.chatbot-soporte__texto {
  margin: var(--e-2) 0;
  color: var(--texto-2);
}

.chatbot-soporte__aviso {
  margin: var(--e-2) 0;
}

.chatbot-soporte__acciones {
  display: flex;
  flex-wrap: wrap;
  gap: var(--e-2);
}

.chatbot-soporte__formulario {
  display: flex;
  flex-direction: column;
  gap: var(--e-2);
}

.chatbot-soporte__lista {
  margin: var(--e-2) 0 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: var(--e-2);
}

.chatbot-soporte__ticket {
  padding: var(--e-2) var(--e-3);
  border: 1px solid var(--borde-tenue);
  border-radius: var(--radio);
  background: var(--superficie-2);
}

.chatbot-soporte__fila {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--e-2);
}

.chatbot-soporte__asunto,
.chatbot-soporte__respuesta {
  margin: 0;
  overflow-wrap: anywhere;
}

.chatbot-soporte__meta,
.chatbot-soporte__vacio {
  margin: var(--e-1) 0 0;
  font-size: var(--t-meta-tam);
  color: var(--texto-2);
}

.chatbot-soporte__respuesta {
  margin-top: var(--e-2);
}


/* 7.4.5 — «Preferencias» del asistente: ocupa el sitio de la conversación,
   como «Soporte», y se oculta con la ventana minimizada. */

.chatbot-preferencias {
  flex: 1 1 auto;
  min-height: 160px;
  overflow-y: auto;
  padding: var(--e-3) var(--e-4);
}

.chatbot-preferencias__cabecera {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--e-2);
}

.chatbot-preferencias__titulo {
  margin: 0;
  font-family: var(--t-etiqueta-familia);
  font-size: var(--t-cuerpo-tam);
  font-weight: var(--t-etiqueta-peso);
}

.chatbot-preferencias__texto {
  margin: var(--e-2) 0;
  color: var(--texto-2);
}

.chatbot-preferencias__aviso {
  margin: var(--e-2) 0;
}

.chatbot-preferencias__formulario {
  display: flex;
  flex-direction: column;
  gap: var(--e-2);
}

.chatbot-ventana--minimizada .chatbot-preferencias {
  display: none;
}

/* Calificación de cada respuesta del bot (HU-CHA-011) */

.chatbot-calificacion {
  margin-top: var(--e-2);
  padding-top: var(--e-2);
  border-top: 1px solid var(--borde-tenue);
}

.chatbot-calificacion__pregunta {
  margin: 0 0 var(--e-1);
  font-size: var(--t-meta-tam);
  color: var(--texto-2);
}

.chatbot-calificacion__botones {
  display: flex;
  gap: var(--e-2);
}

.chatbot-calificacion__boton {
  min-height: 2rem;
  padding: 0 var(--e-3);
  border: 1px solid var(--borde-int);
  border-radius: var(--radio-pildora);
  background: var(--superficie);
  color: var(--texto);
  font-size: var(--t-meta-tam);
  cursor: pointer;
}

.chatbot-calificacion__boton:hover {
  background: var(--superficie-2);
}

.chatbot-calificacion__boton:disabled {
  cursor: progress;
  opacity: 0.7;
}

.chatbot-calificacion__comentario {
  margin-top: var(--e-2);
}

.chatbot-calificacion__nota,
.chatbot-calificacion__gracias {
  margin: var(--e-1) 0 0;
  font-size: var(--t-meta-tam);
  color: var(--texto-2);
}

.chatbot-calificacion__nota:empty {
  display: none;
}

/* UXC-9 — minimizada: queda la barra de título; la conversación sigue ahí.
   `!important` gana al alto que puso el asa (estilo en línea). */
.chatbot-ventana.chatbot-ventana--minimizada {
  height: auto !important;
}

.chatbot-ventana--minimizada .chatbot-ventana__registro,
.chatbot-ventana--minimizada .chatbot-soporte,
.chatbot-ventana--minimizada .chatbot-ventana__aviso,
.chatbot-ventana--minimizada .chatbot-ventana__formulario,
.chatbot-ventana--minimizada .chatbot-ventana__estado,
.chatbot-ventana--minimizada .chatbot-ventana__asa {
  display: none;
}

/* UXC-9 — el asa del tamaño. La ventana cuelga de la esquina inferior
   derecha, así que se agarra por la superior izquierda. Tiene objetivo táctil
   de 24 px y foco visible: con las flechas también cambia el tamaño. */
.chatbot-ventana__asa {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 1;
  width: 24px;
  height: 24px;
  padding: 0;
  border: 0;
  border-radius: var(--radio) 0 var(--radio-chico) 0;
  background: transparent;
  cursor: nwse-resize;
  touch-action: none;
}

.chatbot-ventana__asa::before {
  content: '';
  position: absolute;
  top: 6px;
  left: 6px;
  width: 9px;
  height: 9px;
  border-top: 2px solid var(--borde-int);
  border-left: 2px solid var(--borde-int);
  border-top-left-radius: 2px;
}

.chatbot-ventana__asa:hover {
  background: var(--superficie-2);
}

/* El título empieza donde acaba el asa, para que agarrarla no sea pulsar
   sobre «Asistente». */
.chatbot-ventana__cabecera {
  padding-left: var(--e-6);
}

/* En el teléfono ocupa el ancho, pero sigue por encima del botón flotante:
   si lo tapara, no habría cómo cerrarla con un toque sobre el mismo botón. */
@media (max-width: 480px) {
  .chatbot-ventana__asa {
    display: none;
  }

  .chatbot-ventana__cabecera {
    padding-left: var(--e-4);
  }

  .chatbot-ventana {
    right: var(--e-2);
    left: var(--e-2);
    /* UXC-9 — el tamaño elegido con el asa es de pantalla ancha (estilo en
       línea): en el teléfono manda la pantalla. */
    width: auto !important;
    height: auto !important;
    max-height: min(600px, calc(100vh - 56px - var(--e-16))) !important;
  }
}

/* =======================================================================
   Cabecera — navegación completa
   RF-INV-008: seis destinos y búsqueda de productos
   RF-INV-004: la búsqueda arranca a los cuatro caracteres
   -----------------------------------------------------------------------
   PENDIENTE DE SANTIAGO: ningún documento define cómo llega un visitante
   con cuenta a la pantalla de inicio de sesión. La variante de visitante
   solo ofrece registro, que es lo único que el requisito especifica.
   ======================================================================= */

/* «Marca y navegacion» separa el logotipo de los destinos con 32, no con 24:
   son dos separaciones distintas en Figma y compartían clase por descuido. */
.cabecera__grupo-marca {
  display: flex;
  align-items: center;
  gap: var(--e-8);
}

.cabecera__nav {
  display: flex;
  gap: var(--e-6);
  align-items: center;
}

.cabecera__destino {
  color: var(--texto-sobre-color);
  font-family: var(--t-etiqueta-familia);
  font-size: var(--t-etiqueta-tam);
  font-weight: var(--t-etiqueta-peso);
  text-decoration: none;
  padding: var(--e-1) 0;
  border-bottom: 2px solid transparent;
}

/* El destino en curso se marca aquí, no con una variante de Figma. */
.cabecera__destino.activo,
.cabecera__destino[aria-current='page'] {
  /* El destino activo lleva el filo de energia, no una linea blanca: es el
     mismo gesto que corona las tarjetas. */
  border-bottom-color: transparent;
  border-image: var(--energia) 1;
  border-image-width: 0 0 2px 0;
}

/* Destino cuya pantalla todavía no existe. Se conserva —RF-INV-008 exige los
   seis— pero no finge llevar a ninguna parte. `data-pendiente` dice de quién
   es. Cuando su dueño la publique, se le pone `href` y se quita esto. */
.cabecera__destino--pendiente {
  opacity: 0.55;
  cursor: not-allowed;
}

.cabecera__buscador {
  flex: 1 1 auto;
  /* Ni una barra de 1200 px en una pantalla ancha ni un campo que empuje los
     destinos a otra línea: crece hasta aquí y encoge lo que haga falta. */
  max-width: 22rem;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: var(--e-2);
  height: 32px;
  padding: 0 var(--e-3);
  /* El relleno del buscador es un color propio del diseño (#2A3358), no el
     cromo aclarado con un filtro: `filter` teñía también el icono y el texto. */
  background: var(--cromo-elevado);
  border-radius: var(--radio);
}

.cabecera__buscador-texto {
  flex: 1 1 auto;
  min-width: 0;
  background: none;
  border: 0;
  color: var(--texto-sobre-color);
  font-family: var(--t-cuerpo-familia);
  font-size: var(--t-meta-tam);
}

.cabecera__buscador-texto::placeholder {
  color: var(--pista);
  opacity: 1;
}

/* El campo recibe el foco sin contorno propio (no tiene borde): el anillo va
   en la caja entera, que es lo que se ve. */
.cabecera__buscador:focus-within {
  outline: 2px solid var(--acento-relleno);
  outline-offset: 2px;
}

.cabecera__buscador-texto:focus-visible {
  outline: none;
  box-shadow: none;
}

/* RF-INV-008 — la lupa que lleva a la búsqueda de la tienda cuando la barra
   no tiene sitio para el campo (ver la regla de 1100 px). */
.cabecera__buscador-atajo {
  display: none;
  align-items: center;
  justify-content: center;
  min-width: 32px;
  min-height: 32px;
  border-radius: var(--radio);
  color: inherit;
  transition: background-color var(--transicion);
}

.cabecera__buscador-atajo:hover {
  background: var(--cromo-elevado);
}

.cabecera__buscador-atajo:focus-visible {
  outline: 2px solid var(--acento-relleno);
  outline-offset: 2px;
}

.cabecera__acciones {
  display: flex;
  align-items: center;
  gap: var(--e-5);
}

/* Créditos: icono más cifra, separados 6 px —el valor del frame «Creditos» de
   Figma, que no cae en la escala de 4—. La cifra va en el oro de legendaria.
   Hasta ahora viajaba suelta porque el ui-kit no tenía iconos. */
.cabecera__dato {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: var(--t-etiqueta-familia);
  font-size: var(--t-etiqueta-tam);
  font-weight: var(--t-etiqueta-peso);
  /* Sobre el cromo, el oro claro (ver tokens.css). */
  color: var(--credito-oro-sobre-cromo);
}

/* -----------------------------------------------------------------------
   Icono — los 30 componentes `Icono/*` de Figma, en iconos/sprite.svg
   -----------------------------------------------------------------------
   Uso: <svg class="icono" aria-hidden="true"><use href=".../sprite.svg#buscar"/></svg>
   Heredan el color del texto que los acompaña, igual que en el diseño.
   ----------------------------------------------------------------------- */
.icono {
  width: var(--icono-chico);
  height: var(--icono-chico);
  flex: 0 0 auto;
  color: inherit;
}

/* 16 px: es el tamaño real de `Icono/buscar` y `Icono/moneda` en la cabecera.
   La campana se queda en los 20 de `.icono`. */
.icono--menudo {
  width: var(--icono-menudo);
  height: var(--icono-menudo);
}

/* El distintivo va AL LADO de la campana, separado 6 px, no superpuesto:
   en Figma «Notificaciones» es una fila de campana + distintivo. */
.cabecera__campana {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.cabecera__contador {
  display: inline-block;
  min-width: 20px;
  height: 17px;
  padding: 0 var(--e-1);
  border-radius: var(--radio-pildora);
  background: var(--acento-relleno);
  color: var(--texto-sobre-color);
  font-family: var(--t-meta-familia);
  font-size: var(--t-meta-tam);
  line-height: 17px;
  text-align: center;
}

/* «Mi Cuenta» es el grupo del avatar: retrato, apodo y rango en dos líneas. */
.cabecera__cuenta {
  display: inline-flex;
  align-items: center;
  gap: var(--e-2);
}

/* En Figma el avatar es una elipse morada lisa de 32 px. No lleva icono de
   usuario dentro: poner uno sería añadir algo que el diseño no tiene. */
.cabecera__avatar {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: var(--acento-relleno);
  flex: 0 0 auto;
}

/* UX-R3.3 — el distintivo de rol se pegaba al apodo y su borde superior se
   leía como un subrayado: parecía que «qa_ux» fuera un enlace. Dos píxeles de
   separación y el distintivo ajustado a su propio texto (`align-items`, no
   estirado a lo ancho del apodo) lo resuelven. */
.cabecera__identidad {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--e-1);
  line-height: 1.2;
}

.cabecera__apodo {
  font-family: var(--t-etiqueta-familia);
  font-size: var(--t-etiqueta-tam);
  font-weight: var(--t-etiqueta-peso);
}

/* El rango va en `--pista`, que es el color exacto del diseño, no un blanco
   con opacidad: la opacidad tiñe distinto sobre cada fondo. */
.cabecera__rango {
  font-family: var(--t-meta-familia);
  font-size: var(--t-meta-tam);
  color: var(--pista);
}

/* A 1024 px la marca pasa a la corta (el buscador, ver la regla de 1300 px).
   Nunca se quita un destino. */
@media (max-width: 1100px) {
  .cabecera__nav {
    gap: var(--e-4);
  }

  /* UX-GAME-2: con el emblema, la marca larga ya no cabe junto a los seis
     destinos y las acciones a 1024; va la corta (el nombre accesible es el
     mismo). Con `.cabecera` delante para ganar a la regla base, que va mas
     abajo en esta hoja y la dejaba sin texto. */
  .cabecera .cabecera__marca-larga {
    display: none;
  }

  .cabecera .cabecera__marca-corta {
    display: inline;
  }

  /* Revisión del modo jugador del 6-oct (responsive a 1024): con la marca
     corta todavía no cabían los seis destinos, la lupa, el saldo, la campana
     y el apodo, y «Mi Cuenta» bajaba a una segunda línea. El apodo se queda
     en el avatar (su nombre accesible lo lleva entero), como ya pasaba por
     debajo de 900 px. Nunca se quita un destino. */
  .cabecera--jugador .cabecera__identidad {
    display: none;
  }
}

/* UXC-9 — el campo de búsqueda cabe junto a la marca, los seis destinos y la
   cuenta desde unos 1300 px (1366 y 1440 lo muestran). Por debajo, la lupa
   lleva a la búsqueda de la tienda. Antes el campo se plegaba a 1100 px a una
   lupa de 32 px que no hacía nada, y entre 1100 y 1300 empujaba «Mi Cuenta» a
   una tercera línea de la barra. */
@media (max-width: 1300px) {
  .cabecera .cabecera__buscador {
    display: none;
  }
  .cabecera .cabecera__buscador-atajo {
    display: inline-flex;
  }
}

/* Con el campo a la vista, lo que encoge es el campo, no los destinos: sin
   esto, a 1366 px el reparto de espacio encogía también la navegación y
   «Mi Cuenta» saltaba de línea. */
@media (min-width: 1301px) {
  .cabecera--jugador .cabecera__grupo-marca {
    flex-shrink: 0;
  }
}

/* =======================================================================
   Distintivo — familias añadidas
   Inventario RF-INV-010 · Productos RF-PRD-005 · Misiones RF-MIS-009
   Comercio RF-CAR-003 · Subastas RF-SUB-010 · Cuentas RF-USR-008 · RF-COM-006
   -----------------------------------------------------------------------
   Color saturado como TEXTO sobre fondo tenue del mismo color.
   Nunca fondo sólido con texto blanco. La etiqueta siempre dice el estado.
   ======================================================================= */

.distintivo--en-mision {
  background: var(--info-insignia);
  color: var(--info);
}
/* UXC-5 — estos dos pintaban `--texto-3` sobre `--pista`: 4,11:1, por debajo
   del 4,5:1 que pide un texto de 13 px. Nadie los usaba hasta las misiones
   (Bloqueada, Abandonada) y axe lo cantó en su primer escenario. Con
   `--texto-2`, 5,18:1. */
.distintivo--no-disponible {
  background: var(--pista);
  color: var(--texto-2);
}
.distintivo--activo {
  background: var(--exito-insignia);
  color: var(--exito);
}
.distintivo--suspendido {
  background: var(--error-insignia);
  color: var(--error);
}
.distintivo--bloqueada {
  background: var(--pista);
  color: var(--texto-2);
}
.distintivo--en-progreso {
  background: var(--info-insignia);
  color: var(--info);
}
.distintivo--completada {
  background: var(--exito-insignia);
  color: var(--exito);
}
.distintivo--epica {
  background: var(--rareza-epica-insignia);
  color: var(--rareza-epica);
}
.distintivo--administrador {
  background: var(--rareza-epica-insignia);
  color: var(--acento-relleno);
}
.distintivo--moderador {
  background: var(--info-insignia);
  color: var(--info);
}
.distintivo--sancionado {
  background: var(--error-insignia);
  color: var(--error);
}
.distintivo--reportado {
  background: var(--advertencia-insignia);
  color: var(--advertencia);
}
.distintivo--promocion {
  background: var(--exito-insignia);
  color: var(--exito);
}
.distintivo--maestro-juego {
  background: var(--rareza-epica-insignia);
  color: var(--rareza-epica);
}
/* UXC-9 — el resultado de una partida en «Mi cuenta». La palabra dice el
   resultado; el color solo lo refuerza. El empate va neutro (`--texto-2`
   sobre `--pista`, 5,18:1): no es ni logro ni fallo. */
.distintivo--victoria {
  background: var(--exito-insignia);
  color: var(--exito);
}
.distintivo--derrota {
  background: var(--error-insignia);
  color: var(--error);
}
.distintivo--empate {
  background: var(--pista);
  color: var(--texto-2);
}

/* =======================================================================
   Tabla — selección múltiple y encabezado ordenable
   RF-USR-009 (ordenar y exportar) · RF-COM-008 (lote) · RF-AUD-004
   ======================================================================= */

.tabla__fila--seleccionada {
  background: var(--info-insignia);
}

/* UX-GAME-1: la fila responde al cursor. Solo las de datos: el encabezado no
   es una fila que se pueda elegir. */
.tabla__fila:not(.tabla__fila--encabezado) {
  transition: background var(--transicion);
}

.tabla__fila:not(.tabla__fila--encabezado):not(.tabla__fila--seleccionada):hover {
  background: var(--interaccion-velo-hover);
}

.tabla__encabezado--ordenable {
  cursor: pointer;
  user-select: none;
}

.tabla__encabezado--ordenable .tabla__orden {
  display: inline-block;
  margin-left: var(--e-2);
  color: var(--primaria);
}

.tabla__encabezado--ordenable[aria-sort='descending'] .tabla__orden {
  transform: rotate(180deg);
}

.tabla__seleccion {
  width: 40px;
  text-align: center;
}

/* =======================================================================
   Bloques base que faltaban
   -----------------------------------------------------------------------
   Estas clases estaban documentadas en Figma pero no existían aquí.
   `.accion-combate` venía sin escribir desde el Sprint 1: el componente
   existía en Figma y el CSS nunca se creó.
   ======================================================================= */

/* Barra de progreso genérica — contenedor */
.progreso {
  display: block;
  width: 100%;
}

/* Campo numérico — contenedor. RF-SUB-004 · RF-JUE-001 · RF-PRD-003 */
.campo-numerico {
  display: flex;
  flex-direction: column;
  gap: var(--e-2);
}

.campo-numerico__ayuda {
  font-family: var(--t-meta-familia);
  font-size: var(--t-meta-tam);
  color: var(--texto-3);
}

.campo-numerico--invalido .campo-numerico__caja {
  border-color: var(--error);
}
.campo-numerico--invalido .campo-numerico__ayuda {
  color: var(--error);
}

/* Panel de métrica — contenedor. RF-PRD-001 · RF-MET-001..004 · RF-CHA-012 */
.metrica {
  display: flex;
  flex-direction: column;
  gap: var(--e-2);
  padding: var(--e-4);
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-radius: var(--radio);
  box-shadow: var(--sombra-2);
}

.metrica--cifra {
  min-width: 200px;
}
.metrica--grafica {
  min-width: 260px;
}

/* Tarjeta de contenido — tipos restantes. RF-MIS-001 · RF-SUB-011 · RF-TOR-003 */
.tarjeta--mision .tarjeta__pie {
  color: var(--primaria);
}
.tarjeta--subasta .tarjeta__pie {
  color: var(--error);
}
.tarjeta--equipo .tarjeta__pie {
  color: var(--exito);
}

/* Acción de combate — RF-JUE-006, RF-JUE-017 (todo control con icono) */
.accion-combate {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--e-1);
  /* UX-GAME-4: una accion de combate es un objetivo de juego, no un boton
     de formulario. Minimo 96 px de ancho, y la etiqueta cabe en dos lineas
     sin salirse de la caja. */
  min-width: 96px;
  max-width: 160px;
  min-height: var(--objetivo-tactil);
  padding: var(--e-2) var(--e-3);
  background: var(--superficie);
  border: 1px solid var(--borde-int);
  border-radius: var(--radio);
  color: var(--texto);
  font: inherit;
  cursor: pointer;
  transition:
    border-color var(--transicion),
    background var(--transicion),
    box-shadow var(--transicion),
    transform var(--transicion);
}

.accion-combate:not(:disabled):not(.accion-combate--fuera-de-turno):not(.accion-combate--sin-poder) {
  border-color: color-mix(in srgb, var(--primaria) 55%, var(--borde-int));
  box-shadow: var(--sombra-1);
}

.accion-combate:hover:not(:disabled) {
  background: var(--interaccion-velo-hover);
  box-shadow: var(--sombra-2), var(--brillo-primaria);
  transform: translateY(-1px);
}

.accion-combate:active:not(:disabled) {
  transform: translateY(0) scale(0.98);
  transition-duration: var(--duracion-instantaneo);
}

.accion-combate:focus-visible {
  outline: var(--foco-grosor) solid var(--interaccion-foco-anillo);
  outline-offset: 2px;
}

.accion-combate__icono {
  width: var(--icono);
  height: var(--icono);
}

.accion-combate__etiqueta {
  font-family: var(--t-etiqueta-familia);
  font-size: var(--t-meta-tam);
  font-weight: var(--t-etiqueta-peso);
  line-height: 1.25;
  text-align: center;
  max-width: 100%;
  overflow-wrap: anywhere;
}

.accion-combate--secundaria {
  background: var(--fondo);
}

/* Sin poder y fuera de turno: no basta con atenuar, el motivo va en el texto. */
.accion-combate--sin-poder,
.accion-combate--fuera-de-turno {
  color: var(--texto-3);
  border-color: var(--borde);
  cursor: not-allowed;
}

.accion-combate--sin-poder .accion-combate__icono,
.accion-combate--fuera-de-turno .accion-combate__icono {
  opacity: 0.5;
}

/* =======================================================================
   Zona de carga de archivos
   RF-COM-001 (comentarios con imágenes) · RF-PRD-007 (recursos gráficos)
   RF-AUT-001 (avatar propio) · RF-CHA-006 (adjuntar capturas)
   -----------------------------------------------------------------------
   Arrastrar y soltar nunca es la única vía: la zona es también un botón
   que abre el selector de archivos del sistema. El error dice siempre
   qué formatos y qué tamaño se admiten, no solo que falló.
   ======================================================================= */

.zona-carga {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--e-3);
  min-height: 140px;
  padding: var(--e-5);
  background: var(--fondo);
  border: 2px dashed var(--borde);
  border-radius: var(--radio);
  text-align: center;
  cursor: pointer;
}

.zona-carga:focus-visible,
.zona-carga--arrastrando {
  outline: var(--foco-grosor) solid var(--interaccion-foco-anillo);
  outline-offset: 2px;
  border-color: var(--primaria);
}

.zona-carga__entrada {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

.zona-carga__icono {
  width: var(--icono-grande);
  height: var(--icono-grande);
  color: var(--texto-3);
}

.zona-carga__titulo {
  font-family: var(--t-cuerpo-familia);
  font-size: var(--t-cuerpo-tam);
  color: var(--texto);
}

.zona-carga__ayuda {
  font-family: var(--t-meta-familia);
  font-size: var(--t-meta-tam);
  color: var(--texto-3);
}

/* Con archivos: deja de ser una invitación y pasa a ser una lista. */
.zona-carga--con-archivos {
  background: var(--superficie);
  border: 1px solid var(--borde);
}

.zona-carga__miniaturas {
  display: flex;
  flex-wrap: wrap;
  gap: var(--e-2);
  justify-content: center;
}

.zona-carga__miniatura {
  position: relative;
  width: 56px;
  height: 56px;
  border-radius: var(--radio-chico);
  background: var(--pista);
  overflow: hidden;
}

.zona-carga__quitar {
  position: absolute;
  top: 2px;
  right: 2px;
  width: 20px;
  height: 20px;
  border: 0;
  border-radius: 50%;
  background: var(--interaccion-velo-modal);
  color: var(--texto-sobre-color);
  cursor: pointer;
}

.zona-carga--cargando {
  background: var(--superficie);
  border: 1px solid var(--borde);
  cursor: progress;
}

.zona-carga__progreso {
  width: 240px;
  height: 8px;
  background: var(--pista);
  border-radius: var(--radio-chico);
  overflow: hidden;
}

.zona-carga__avance {
  height: 100%;
  background: var(--primaria-relleno);
  transition: width var(--duracion-normal) var(--curva-estandar);
}

/* Error: el motivo va escrito, no solo el borde en rojo. */
.zona-carga--error {
  background: var(--error-insignia);
  border-color: var(--error);
}

.zona-carga--error .zona-carga__titulo,
.zona-carga--error .zona-carga__ayuda,
.zona-carga--error .zona-carga__icono {
  color: var(--error);
}

/* =======================================================================
   Casilla en caja
   -----------------------------------------------------------------------
   Variante de Pantalla 1: la casilla vive dentro de una caja con borde que
   ocupa el ancho, no suelta sobre el fondo. Toda la caja es el area
   pulsable, que es mas facil de acertar que un cuadrado de 20 px.
   ======================================================================= */

.casilla--caja {
  align-items: center;
  padding: var(--e-3);
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-radius: var(--radio);
}

.casilla--caja:hover {
  border-color: var(--borde-int);
}

.casilla--caja:has(.casilla__entrada:focus-visible) {
  border-color: var(--primaria);
}

/* =======================================================================
   Chatbot — boton flotante
   RNF-DIS-002: presente en todas las vistas, sin excepcion.
   -----------------------------------------------------------------------
   El componente existia en el sistema de diseno pero su clase nunca se
   escribio; lo destapo la primera vista construida de verdad.
   ======================================================================= */

/* Revisión del modo jugador del 6-oct, punto 4 — «que no se vea tan
   genérico»: el emblema del Nexo sobre el cromo, con un halo de energía que
   gira despacio, una señal y el rótulo «Asistente Nexus» al pasar o al llegar
   con el teclado. Mismo tamaño que antes (56 px): la ventana y los huecos que
   las vistas le reservan siguen midiendo lo mismo. */
.chatbot-flotante {
  position: fixed;
  right: var(--e-6);
  bottom: var(--e-6);
  width: 56px;
  height: 56px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  border-radius: 50%;
  isolation: isolate;
  background: radial-gradient(
    circle at 50% 38%,
    var(--cromo-elevado),
    var(--cromo) 72%
  );
  color: var(--texto-sobre-color);
  font-family: var(--t-etiqueta-familia);
  font-weight: var(--t-etiqueta-peso);
  box-shadow:
    var(--sombra-3),
    0 0 18px color-mix(in srgb, var(--primaria) 55%, transparent);
  cursor: pointer;
  transition:
    transform var(--duracion-normal) var(--curva-estandar),
    box-shadow var(--duracion-normal) var(--curva-estandar);
}

/* El halo: un anillo con el degradado de energía, por detrás del cromo. */
.chatbot-flotante__halo {
  position: absolute;
  inset: -3px;
  z-index: -1;
  border-radius: 50%;
  background: conic-gradient(
    from 0deg,
    var(--primaria),
    var(--acento-relleno),
    var(--enlace-sobre-cromo),
    var(--primaria)
  );
  /* Solo el anillo: el centro deja ver el cromo del botón. */
  -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 3px), #000 calc(100% - 3px));
  mask: radial-gradient(farthest-side, transparent calc(100% - 3px), #000 calc(100% - 3px));
  pointer-events: none;
}

.chatbot-flotante__emblema {
  width: 34px;
  height: auto;
  pointer-events: none;
  user-select: none;
  filter: drop-shadow(0 0 6px color-mix(in srgb, var(--enlace-sobre-cromo) 70%, transparent));
}

/* La señal: un punto de energía en el borde, como el de un HUD. */
.chatbot-flotante__senal {
  position: absolute;
  top: 3px;
  right: 3px;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--enlace-sobre-cromo);
  box-shadow:
    0 0 0 2px var(--cromo),
    0 0 8px var(--enlace-sobre-cromo);
  pointer-events: none;
}

/* El rótulo, a la izquierda del botón: solo al pasar o con el foco del
   teclado. Está dentro del nombre accesible, así que no se anuncia dos veces. */
.chatbot-flotante__rotulo {
  position: absolute;
  top: 50%;
  right: calc(100% + var(--e-3));
  padding: var(--e-1) var(--e-3);
  border-radius: var(--radio-pildora);
  background: var(--cromo);
  box-shadow: var(--sombra-2);
  color: var(--texto-sobre-color);
  font-size: var(--t-meta-tam);
  font-weight: 600;
  white-space: nowrap;
  opacity: 0;
  transform: translate(6px, -50%);
  pointer-events: none;
  transition:
    opacity var(--duracion-normal) var(--curva-salida),
    transform var(--duracion-normal) var(--curva-salida);
}

.chatbot-flotante:hover,
.chatbot-flotante:focus-visible {
  transform: translateY(-2px);
  box-shadow:
    var(--sombra-3),
    0 0 26px color-mix(in srgb, var(--acento-relleno) 70%, transparent);
}

.chatbot-flotante:hover .chatbot-flotante__rotulo,
.chatbot-flotante:focus-visible .chatbot-flotante__rotulo {
  opacity: 1;
  transform: translate(0, -50%);
}

.chatbot-flotante:focus-visible {
  outline: 3px solid var(--enlace-sobre-cromo);
  outline-offset: 5px;
}

.chatbot-flotante:active {
  transform: scale(0.95);
}

/* Con la ventana abierta, el halo queda quieto y lleno: el asistente escucha. */
.chatbot-flotante[aria-expanded='true'] .chatbot-flotante__halo {
  inset: -4px;
  animation: none;
}

@media (prefers-reduced-motion: no-preference) {
  .chatbot-flotante__halo {
    animation: halo-del-nexo 8s linear infinite;
  }
}

@keyframes halo-del-nexo {
  to {
    transform: rotate(1turn);
  }
}

/* En el teléfono el rótulo no cabe a la izquierda y no hay puntero: fuera. */
@media (max-width: 599px) {
  .chatbot-flotante {
    right: var(--e-4);
    bottom: var(--e-4);
  }

  .chatbot-flotante__rotulo {
    display: none;
  }
}

/* Que no tape acciones: la sala de espera en el teléfono es una columna que
   termina en sus botones; se le deja el hueco del botón debajo. */
@media (max-width: 767px) {
  body:has(.chatbot-flotante) .combate[data-sin-partida='si'] {
    padding-bottom: calc(var(--e-4) + 56px + var(--e-4));
  }
}

/* =======================================================================
   Cabecera de la aplicación — HU-UX-001 (src/comun/cabecera-app.js)
   Lo que la barra única añade sobre `.cabecera` de Figma: la marca como
   enlace, el estado sin sesión, el botón de cuenta con su menú y el
   comportamiento en pantallas estrechas. Ninguna vista escribe su propia
   barra: todas montan esta.
   ======================================================================= */

/* Vistas que todavía no cargan base.css (cuentas, inventario, subastas):
   sin esto el contador de la campana y el menú cerrado se pintan igual. */
.cabecera [hidden] {
  display: none !important;
}

/* La cabecera fija su propia tipografía y ancho: no hereda la de la vista
   (vitrina.css, tema-cuentas.css), que la desalineaba y partía la navegación. */
.cabecera {
  box-sizing: border-box;
  width: 100%;
  font-family: var(--fuente-texto, inherit);
  font-size: 1rem;
}
.cabecera *,
.cabecera *::before,
.cabecera *::after {
  box-sizing: border-box;
}
.cabecera__nav {
  flex-wrap: wrap;
  min-width: 0;
}

.cabecera__marca,
.cabecera__marca:hover,
.cabecera__marca:visited {
  color: inherit;
  text-decoration: none;
  white-space: nowrap;
}

.cabecera__marca:focus-visible,
.cabecera__destino:focus-visible,
.cabecera__campana:focus-visible,
.cabecera__cuenta-boton:focus-visible {
  outline: 2px solid var(--acento-relleno);
  outline-offset: 2px;
  border-radius: var(--radio);
}

.cabecera__destino {
  transition:
    color var(--transicion),
    border-color var(--transicion);
}

.cabecera__destino:hover:not(.cabecera__destino--pendiente) {
  border-bottom-color: var(--enlace-sobre-cromo);
  color: var(--enlace-sobre-cromo);
}

/* Sin sesión: dos botones y, si la sesión caducó, la razón. */
.cabecera__sesion {
  display: inline-flex;
  align-items: center;
  gap: var(--e-2);
}

.cabecera__aviso-sesion {
  font-family: var(--t-meta-familia);
  font-size: var(--t-meta-tam);
  color: var(--pista);
  margin-right: var(--e-2);
}

/* Con sesión: el grupo de cuenta es un botón que despliega el menú. */
.cabecera__cuenta {
  position: relative;
}

.cabecera__cuenta-boton {
  display: inline-flex;
  align-items: center;
  gap: var(--e-2);
  padding: var(--e-1) var(--e-2);
  border: 0;
  border-radius: var(--radio);
  background: none;
  color: inherit;
  font: inherit;
  cursor: pointer;
}

.cabecera__cuenta-boton {
  transition: background-color var(--transicion);
}

.cabecera__cuenta-boton:hover,
.cabecera__cuenta-boton[aria-expanded='true'] {
  background: var(--cromo-elevado);
}

/* El avatar sin imagen muestra la inicial del apodo. UX-GAME-2: relleno de
   energia (azul a violeta) y un anillo fino separado del cromo, que es lo
   que lo hace leerse como un retrato y no como un punto morado. */
.cabecera__avatar {
  display: inline-grid;
  place-items: center;
  color: var(--texto-sobre-color);
  font-family: var(--t-etiqueta-familia);
  font-weight: var(--t-etiqueta-peso);
  font-size: var(--t-etiqueta-tam);
  background: var(--energia);
  box-shadow:
    0 0 0 2px var(--cromo),
    0 0 0 3px color-mix(in srgb, var(--primaria) 60%, transparent);
  transition: box-shadow var(--transicion);
}

.cabecera__cuenta-boton:hover .cabecera__avatar,
.cabecera__cuenta-boton[aria-expanded='true'] .cabecera__avatar {
  box-shadow:
    0 0 0 2px var(--cromo-elevado),
    0 0 0 3px var(--enlace-sobre-cromo);
}

/* La campana es un objetivo con estado propio: relleno al pasar, y el
   contador con un borde de cromo para que no se funda con el icono. */
.cabecera__campana {
  border-radius: var(--radio);
  transition: background-color var(--transicion);
}

.cabecera__campana:hover {
  background: var(--cromo-elevado);
}

/* Revisión del modo jugador del 6-oct, punto 7 — la tienda y el chat como
   atajos del HUD, junto a la campana y con su misma forma: icono, objetivo
   táctil completo y nombre accesible. El de la vista en curso lleva el filo
   de energía debajo, como el destino activo. */
.cabecera__atajo {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: var(--objetivo-tactil);
  min-height: var(--objetivo-tactil);
  border-radius: var(--radio);
  color: inherit;
  text-decoration: none;
  transition: background-color var(--transicion);
}

.cabecera__atajo:hover {
  background: var(--cromo-elevado);
  color: inherit;
}

.cabecera__atajo:focus-visible {
  outline: 2px solid var(--acento-relleno);
  outline-offset: 2px;
}

.cabecera__atajo.activo {
  background: var(--cromo-elevado);
  box-shadow: inset 0 -2px 0 var(--enlace-sobre-cromo);
}

/* La barra está medida para que los seis destinos de RF-INV-008 no salten de
   línea (ver el buscador, a 1300 px). Dos iconos más solo caben con holgura
   —sin recortar el campo de búsqueda— desde 1440 px; por debajo, la tienda y
   el chat siguen arriba, en el menú de la cuenta, y la barra queda como
   estaba. */
@media (max-width: 1439px) {
  .cabecera__atajo {
    display: none;
  }
}

/* Con los atajos a la vista, las acciones se juntan un poco: el campo de
   búsqueda conserva su ancho. */
@media (min-width: 1440px) {
  .cabecera--jugador .cabecera__acciones {
    gap: var(--e-3);
  }
}

.cabecera__contador {
  box-shadow: 0 0 0 2px var(--cromo);
}

.cabecera__menu {
  position: absolute;
  right: 0;
  top: calc(100% + var(--e-2));
  z-index: 30;
  color: var(--texto);
  text-transform: none;
  box-shadow: var(--sombra-atmosfera);
}

@media (prefers-reduced-motion: no-preference) {
  /* El menu entra como el dialogo: 6 px y opacidad, una sola vez. `hidden`
     lo saca del arbol, asi que la animacion arranca cada vez que se abre. */
  .cabecera__menu {
    animation: entrar-dialogo var(--duracion-rapido) var(--curva-salida) both;
  }
}

/* Separador entre las acciones de sesion y la cuenta, solo en escritorio:
   en la fila de movil no hay sitio y el avatar ya se distingue solo. */
@media (min-width: 901px) {
  .cabecera--jugador .cabecera__cuenta,
  .cabecera--consola .cabecera__cuenta {
    margin-left: var(--e-1);
    padding-left: var(--e-4);
    border-left: 1px solid color-mix(in srgb, var(--pista) 28%, transparent);
  }
}

.cabecera__menu .menu__grupo {
  padding: 0;
  text-transform: none;
  letter-spacing: 0;
}
.cabecera__menu .menu__opcion {
  text-decoration: none;
  font-size: var(--t-cuerpo-tam);
}
.cabecera__menu .menu__opcion:first-child {
  border-top: 0;
}

/* =======================================================================
   La cabecera en pantallas estrechas — UX-R2.9
   -----------------------------------------------------------------------
   Antes esto decia «la navegación baja a una segunda fila». En la practica
   bajaba a TRES: el laboratorio visual (#600) media **180 px de cabecera a
   375 px de ancho**, casi el triple de los 64 px que fija el diseno, y lo
   encontraba en 30 de las 31 vistas en movil y en 18 en tablet — 48 de las
   155 combinaciones medidas. La pagina empezaba media pantalla mas abajo.

   Ahora la cabecera es UNA fila a cualquier anchura:

     [marca corta] [campana] [avatar] [☰]

   y los seis destinos se pliegan detras del boton. **No se pierde ninguno**:
   HU-INV-004 CA-01 exige los seis accesos siempre, y siguen estando todos,
   a un toque. Plegar no es esconder — lo que se esconde es lo que no se
   puede alcanzar.

   El panel se abre con `data-nav-abierto` en la cabecera, no con `hidden` en
   el `<nav>`: quien decide si hay que plegar es el ancho, y eso solo lo sabe
   el CSS. Asi el escritorio ignora el atributo y ensena los seis siempre.

   Los objetivos tactiles suben a 44x44 (WCAG 2.5.8 AA). Eran 84x31, 48x31,
   55x31…: 327 hallazgos del laboratorio, casi todos en esta barra.
   ======================================================================= */

/* El disparador no existe en escritorio: ahi caben los seis. */
.cabecera__alternar {
  display: none;
  align-items: center;
  justify-content: center;
  min-width: var(--objetivo-tactil);
  min-height: var(--objetivo-tactil);
  border: 0;
  border-radius: var(--radio);
  background: none;
  color: inherit;
  cursor: pointer;
}

.cabecera__alternar:hover,
.cabecera__alternar[aria-expanded='true'] {
  background: var(--cromo-elevado);
}

.cabecera__alternar:focus-visible {
  outline: 2px solid var(--acento-relleno);
  outline-offset: 2px;
}

/* La marca corta solo aparece cuando la larga no cabe. */
.cabecera__marca-corta {
  display: none;
}

/* Objetivos tactiles del tamano minimo, en las tres anchuras. Un enlace de
   texto de 31 px de alto se falla igual en escritorio con un raton impreciso
   que en un movil con el pulgar. */
.cabecera__marca,
.cabecera__destino,
.cabecera__campana,
.cabecera__cuenta-boton {
  display: inline-flex;
  align-items: center;
  min-height: var(--objetivo-tactil);
}

/* La campana es un enlace de solo icono: alto ya tenia, ancho no. Medía 24 px
   y salia en 23 de las 31 vistas. */
.cabecera__campana {
  justify-content: center;
  min-width: var(--objetivo-tactil);
}

/* Las pestanas son botones sin relleno vertical (`padding: 0`), asi que su
   alto lo daba el texto: 31 px. */
.pestana {
  min-height: var(--objetivo-tactil);
  justify-content: center;
}

@media (max-width: 900px) {
  .cabecera {
    /* Sin `wrap`: es justo lo que producia las tres filas. */
    flex-wrap: nowrap;
    gap: var(--e-2);
    padding: 0 var(--e-3);
    /* El panel de navegacion cuelga de aqui. */
    position: relative;
  }

  .cabecera__grupo-marca {
    display: flex;
    align-items: center;
    gap: var(--e-2);
    min-width: 0;
    flex: 1 1 auto;
  }

  .cabecera__marca-larga {
    display: none;
  }

  .cabecera__marca-corta {
    display: inline;
  }

  .cabecera__alternar {
    display: inline-flex;
    /* Ultimo de la fila, pegado a las acciones. */
    order: 5;
    margin-left: auto;
  }

  /* El panel: cae debajo de la barra, ocupa el ancho y se cierra solo al
     navegar porque la pagina cambia. */
  .cabecera__nav {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    z-index: 30;
    flex-direction: column;
    gap: 0;
    padding: var(--e-2);
    /* UX-GAME-2: el panel remata con el filo de energia, como la consola. */
    background: var(--energia) bottom / 100% 2px no-repeat, var(--cromo);
    box-shadow: var(--sombra-3);
  }

  .cabecera[data-nav-abierto='si'] .cabecera__nav {
    display: flex;
  }

  .cabecera__destino {
    /* A lo ancho: un destino por fila, sin tener que apuntar a 55 px. */
    width: 100%;
    padding: var(--e-2) var(--e-3);
    border-bottom: 0;
    border-radius: var(--radio-chico);
  }

  .cabecera__destino.activo {
    background: var(--cromo-elevado);
  }

  .cabecera__buscador {
    display: none;
  }

  .cabecera__acciones {
    display: flex;
    align-items: center;
    gap: var(--e-1);
  }

  /* El apodo y el rango no caben; el avatar y el menu de cuenta si, y de ahi
     se llega al perfil. El nombre accesible del boton no cambia. */
  .cabecera__identidad {
    display: none;
  }

  /* El saldo en cifras tampoco cabe junto a todo lo demas. Esta en la home,
     que es donde el jugador lo mira (jerarquia de UX-R2.6). */
  .cabecera__dato {
    display: none;
  }
}

/* =======================================================================
   Encabezados de página y de sección
   -----------------------------------------------------------------------
   Antes cada vista colocaba el título, la descripción y el botón principal
   donde le parecía. Dos pantallas se sienten la misma aplicación cuando lo
   primero que ve el ojo está siempre en el mismo sitio.
   ======================================================================= */

.encabezado-pagina {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--e-4);
  margin-bottom: var(--e-5);
}

.encabezado-pagina h1 {
  font-family: var(--t-titulo-familia);
  font-size: var(--t-titulo-xl-tam);
  line-height: var(--t-titulo-xl-alto);
}

/* El bloque del titulo y su descripcion, cuando la cabecera lleva tambien
   acciones a la derecha. `historial-transacciones` y `mis-cofres` lo
   escribian como `.encabezado-contenido`, una clase que no existia en ningun
   sitio: el bloque no tenia ancho maximo y la descripcion cruzaba la pantalla
   entera (UX-R2.4). */
.encabezado-pagina__contenido {
  display: grid;
  gap: var(--e-2);
  max-width: var(--ancho-estrecho);
}

.encabezado-pagina__acciones,
.encabezado-seccion__acciones {
  display: flex;
  flex-wrap: wrap;
  gap: var(--e-2);
}

.encabezado-seccion {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--e-3);
  margin-bottom: var(--e-3);
  padding-bottom: var(--e-2);
  border-bottom: 1px solid var(--borde);
}

/* =======================================================================
   Tarjeta — partes internas
   -----------------------------------------------------------------------
   `.tarjeta` ya existía; lo que faltaba era la estructura de dentro, que
   cada listado resolvía a mano y por eso la tarjeta de subasta y la de sala
   no se parecían.
   ======================================================================= */

.tarjeta__cabecera {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--e-3);
}

.tarjeta__titulo {
  font-family: var(--t-subtitulo-familia);
  font-size: var(--t-subtitulo-tam);
  line-height: var(--t-subtitulo-alto);
  font-weight: var(--t-subtitulo-peso);
}

.tarjeta__distintivos {
  display: flex;
  flex-wrap: wrap;
  gap: var(--e-1);
}

.tarjeta__medios {
  margin: calc(var(--e-4) * -1) calc(var(--e-4) * -1) 0;
  border-radius: var(--radio) var(--radio) 0 0;
  overflow: hidden;
  background: var(--fondo);
}

.tarjeta__medios img {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 10;
  object-fit: cover;
}

/* Pares etiqueta/valor: la etiqueta pequeña encima y el valor con cifras
   tabulares, para que una columna de créditos quede alineada. */
.tarjeta__datos {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(96px, 1fr));
  gap: var(--e-2) var(--e-3);
  margin: 0;
}

/* Un par etiqueta/valor es UNA celda de la rejilla (UX-GAME-3). */
.tarjeta__dato {
  display: grid;
  gap: 2px;
  min-width: 0;
}

.tarjeta__datos dt {
  font-family: var(--t-meta-familia);
  font-size: var(--t-meta-tam);
  color: var(--texto-2);
}

.tarjeta__datos dd {
  margin: 0;
  font-variant-numeric: var(--cifras-tabulares);
  font-weight: var(--t-cuerpo-peso-enfasis);
}

.tarjeta__acciones {
  display: flex;
  flex-wrap: wrap;
  gap: var(--e-2);
  margin-top: auto;
  padding-top: var(--e-2);
}

/* Distintivo de rareza: el color lo pone quien lo crea, con la variable de
   rareza del catálogo de tokens. */
.distintivo--rareza {
  border-color: var(--rareza, var(--borde));
  color: var(--rareza, var(--texto));
}

.metrica__valor {
  font-family: var(--t-titulo-familia);
  font-size: var(--t-titulo-tam);
  line-height: var(--t-titulo-alto);
  font-variant-numeric: var(--cifras-tabulares);
}

/* UX-R3.11 — estaba declarado dos veces en esta misma hoja, con dos escalas
   tipograficas distintas (titulo y subtitulo). Como el selector es identico,
   ganaba la segunda para lo que declaraba y sobrevivia el `flex: 1` de la
   primera: el resultado en pantalla es esta union. Se deja escrita una sola
   vez para que lo que se lee sea lo que pasa. */
.dialogo__titulo {
  flex: 1;
  font-family: var(--t-subtitulo-familia);
  font-size: var(--t-subtitulo-tam);
  font-weight: var(--t-subtitulo-peso);
  padding-right: var(--e-6);
}

.dialogo__acciones {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: var(--e-2);
  margin-top: var(--e-4);
}

.dialogo__cerrar {
  position: absolute;
  top: var(--e-2);
  right: var(--e-2);
  width: var(--icono);
  height: var(--icono);
  display: grid;
  place-items: center;
  border: 0;
  border-radius: var(--radio-chico);
  background: transparent;
  color: var(--texto-2);
  font-size: 1.25rem;
  line-height: 1;
  cursor: pointer;
}

.dialogo__cerrar:hover {
  background: var(--interaccion-velo-hover);
  color: var(--texto);
}

/* Menos movimiento para quien lo pidió en su sistema operativo. */
@media (prefers-reduced-motion: reduce) {
  .esqueleto {
    animation: none;
  }

  /* Los botones no se desplazan ni se escalan; conservan el cambio de color y
     de sombra, que es lo que dice «esto responde». */
  .boton:hover:not([disabled]):not([aria-disabled='true']),
  .boton:active:not([disabled]):not([aria-disabled='true']),
  .tarjeta--pulsable:hover,
  .tarjeta--pulsable:active,
  .accion-combate:hover:not(:disabled),
  .accion-combate:active:not(:disabled) {
    transform: none;
  }
}

/* =======================================================================
   Campo con acción a la derecha (ver/ocultar contraseña)
   ======================================================================= */

.campo__con-accion {
  position: relative;
  display: flex;
  align-items: stretch;
}

.campo__con-accion .campo__control {
  flex: 1;
  /* Sitio para el botón, para que el texto no pase por debajo. */
  padding-right: calc(var(--e-8) + var(--e-2));
}

.campo__accion {
  position: absolute;
  top: 50%;
  right: var(--e-1);
  transform: translateY(-50%);
  /* UX-R2.9 — «Ver» medía 38x24. Es el control que decide si alguien puede
     comprobar la contrasena que acaba de escribir, y en un movil se falla.
     El relleno vertical sube al minimo tactil; el texto sigue igual de
     pequeno, asi que no se come el campo. */
  display: inline-flex;
  align-items: center;
  min-height: var(--objetivo-tactil);
  min-width: var(--objetivo-tactil);
  justify-content: center;
  padding: var(--e-1) var(--e-2);
  border: 0;
  border-radius: var(--radio-chico);
  background: transparent;
  color: var(--texto-2);
  font-family: var(--t-meta-familia);
  font-size: var(--t-meta-tam);
  font-weight: 600;
  cursor: pointer;
}

.campo__accion:hover {
  color: var(--texto);
  background: var(--interaccion-velo-hover);
}

/* =======================================================================
   Portal de entrada — login, registro y recuperación (UX-R3.1)
   -----------------------------------------------------------------------
   Antes esto era una pantalla de aplicación: fondo gris claro, tarjeta
   blanca, marca en texto oscuro. Funcionaba, y no se parecía a un juego —
   se parecía a la pantalla de acceso de una intranet. Era también la única
   vista del producto que un visitante ve antes de decidir si entra.

   El cambio no introduce ni un color nuevo. Usa los que ya están, donde
   tienen sentido: `--cromo` es el color del juego (la barra, la cabecera del
   combate), así que el portal se pinta EN cromo en vez de flotar sobre gris,
   y la tarjeta del formulario pasa a ser la única superficie clara de la
   pantalla. Eso hace dos cosas a la vez: el portal se lee como juego, y el
   sitio donde hay que escribir es el punto más luminoso de la composición.

   Sin fondo animado (§22) ni ilustración inventada: la profundidad sale de
   dos degradados de cromo y de la sombra de la tarjeta.
   ======================================================================= */

.portal {
  /* Columna, para que el `main` se quede con lo que sobra de la fila de marca
     y el centrado vertical sea exacto. Con `min-height: 100vh` en los dos, la
     escena medía una pantalla MÁS la cabecera y se iba media fila abajo. */
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  /* El fondo lo pone `body` desde UX-GAME-1: la atmosfera del portal paso a
     ser la de toda la aplicacion. Aqui queda solo la maquetacion. */
  color: var(--texto-sobre-color);
}

/* La composición ocupa lo que quede bajo la fila de marca, sea cual sea su
   alto: `flex: 1` en vez de restar `--cabecera-alto` a mano. */
.entrada {
  flex: 1;
  display: grid;
  grid-template-columns: 1fr;
  /* La fila se centra en el hueco que sobra; dentro de ella, las dos columnas
     se alinean por ARRIBA. Con `align-items: center` la identidad se centraba
     contra la altura de la tarjeta, y en el registro —una tarjeta de ocho
     campos, más alta que la ventana— eso dejaba la marca a media página, por
     debajo del pliegue, en la pantalla donde más falta hace. */
  align-items: start;
  align-content: center;
  gap: var(--e-8);
  padding: var(--e-8) var(--e-4);
}

@media (min-width: 900px) {
  .entrada {
    grid-template-columns: 1.05fr 1fr;
    max-width: 1180px;
    margin: 0 auto;
    padding: var(--e-8) var(--e-6);
    gap: var(--e-16);
  }
}

/* En móvil manda el formulario. Nadie se registra para leer la presentación,
   y en 375 px la identidad empujaba la tarjeta fuera de la primera pantalla.
   El orden del DOM se mantiene (la identidad primero) porque es el orden en
   que tiene sentido oírlo con un lector de pantalla. */
/* En pantalla ancha la identidad acompaña al formulario mientras se rellena:
   en el registro hay que desplazarse, y sin esto la marca se iba arriba y la
   columna izquierda quedaba vacía durante todo el resto del formulario. */
@media (min-width: 900px) {
  .entrada__identidad {
    position: sticky;
    top: var(--e-8);
  }
}

@media (max-width: 899px) {
  .entrada__identidad {
    order: 2;
  }

  .entrada__tarjeta {
    order: 1;
  }
}

.entrada__identidad {
  display: flex;
  flex-direction: column;
  gap: var(--e-5);
}

/* «Return of the Warriors» — el subtítulo del producto, que es lo que este
   renglón siempre debió decir. Antes decía «Proyecto Integrador II · UPB
   Bucaramanga»: información verdadera, y de la entrega, no del juego. */
.entrada__sobretitulo {
  display: flex;
  align-items: center;
  gap: var(--e-3);
  font-family: var(--t-etiqueta-familia);
  font-size: var(--t-meta-tam);
  font-weight: var(--t-etiqueta-peso);
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--pista);
}

/* La línea corta que lo precede: el gesto del filo de la tarjeta, horizontal
   y a escala de renglón. Dos usos del mismo recurso, no dos recursos. */
.entrada__sobretitulo::before {
  content: '';
  width: var(--e-8);
  height: 2px;
  background: linear-gradient(90deg, var(--texto-sobre-color), transparent);
}

.entrada__marca {
  margin: 0;
  font-family: var(--t-titulo-familia);
  font-size: clamp(2.5rem, 6vw, 4.25rem);
  font-weight: var(--t-titulo-peso);
  line-height: 1.02;
  letter-spacing: 0.01em;
  color: var(--texto-sobre-color);
}

/* El logotipo oficial dentro del h1 de la entrada. Sustituye al texto grande
   solo donde la vista lo pone; las vistas que siguen con `NEXUS <em>BATTLES</em>
   VI` no cambian. Ancho acotado para que no compita con el formulario: en
   escritorio ocupa la columna de identidad hasta 520 px; en movil, donde la
   identidad va debajo del formulario, se queda en 320. `height: auto` con el
   `width`/`height` del marcado evita el salto de maqueta mientras carga. */
.entrada__logo {
  display: block;
  width: min(100%, 520px);
  height: auto;
  filter: drop-shadow(var(--sombra-marca));
}

@media (max-width: 899px) {
  .entrada__logo {
    width: min(100%, 320px);
  }
}

/* El acento es del Nexo: una sola palabra, no toda la marca.
   Sobre cromo, `--primaria` es casi invisible —es un azul pensado para leerse
   sobre fondo claro—, y `--pista` se lee como gris, no como acento. El acento
   del portal es la MISMA primaria aclarada hacia el blanco del kit: sigue
   siendo el azul de la marca, con el contraste que este fondo pide. No es un
   color nuevo; es el de siempre, mezclado con otro de la paleta. */
.entrada__marca em {
  font-style: normal;
  color: var(--pista);
  color: color-mix(in srgb, var(--primaria) 35%, var(--texto-sobre-color));
}

.entrada__lema {
  max-width: 44ch;
  margin: 0;
  color: var(--pista);
  font-size: var(--t-subtitulo-tam);
  line-height: 1.45;
}

.entrada__puntos {
  display: grid;
  gap: var(--e-3);
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Cada capacidad con su icono del sprite compartido. No son viñetas
   decorativas: dicen de qué va cada línea antes de leerla (§23). El icono va
   `aria-hidden` y el texto lo dice entero, así que no aporta ni quita nada a
   quien usa lector de pantalla. */
.entrada__punto {
  display: flex;
  align-items: center;
  gap: var(--e-4);
  color: var(--texto-sobre-color);
  font-size: var(--t-cuerpo-tam);
}

/* Sin pastilla detrás. Se probó con una —un cuadrado tenue por icono— y tres
   cuadrados oscuros sobre fondo oscuro no separan nada: ensucian la columna y
   obligan al glifo a competir con su propia caja. El icono solo, en el acento
   de la marca, hace el trabajo con menos.

   `overflow: visible` porque estos glifos dibujan hasta el borde del viewBox
   (la espada va de 3,3 a 21,21 con trazo de 2) y el SVG recorta por defecto:
   sin esto, la punta de la espada y el asa del trofeo salían cortadas. */
.entrada__punto .icono {
  flex: 0 0 auto;
  width: var(--icono);
  height: var(--icono);
  overflow: visible;
  color: var(--pista);
  color: color-mix(in srgb, var(--primaria) 35%, var(--texto-sobre-color));
}

/* --- La tarjeta: la única superficie clara de la pantalla -------------- */

.entrada__tarjeta {
  width: min(452px, 100%);
  justify-self: center;
  display: flex;
  flex-direction: column;
  gap: var(--e-4);
  padding: var(--e-8) var(--e-6) var(--e-6);
  background: var(--superficie);
  border-radius: var(--radio);
  /* Sobre cromo, un borde de 1 px no separa nada: lo que separa es la
     sombra, y aquí puede ser la de capa 4 sin competir con ningún modal. */
  box-shadow: var(--sombra-4);
  color: var(--texto);
}

/* Filo superior. Lo único decorativo de la pantalla, y el mismo gesto que
   marca la consola en su barra. */
.entrada__tarjeta::before {
  content: '';
  height: 4px;
  margin: calc(var(--e-8) * -1) calc(var(--e-6) * -1) 0;
  border-radius: var(--radio) var(--radio) 0 0;
  background: linear-gradient(90deg, var(--primaria), var(--acento-relleno));
}

.entrada__titulo {
  margin: 0;
  font-family: var(--t-titulo-familia);
  font-size: var(--t-titulo-tam);
  line-height: var(--t-titulo-alto);
}

.entrada__pie {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: var(--e-2);
  padding-top: var(--e-3);
  border-top: 1px solid var(--borde);
  font-size: var(--t-meta-tam);
}

/* UX-R2.9 — «¿Olvidaste tu contraseña?» y «Crear una cuenta» no son texto
   dentro de una frase: son las dos salidas de la pantalla de entrada, y son
   justo las que pulsa quien NO consigue entrar. Medían 20 px de alto.

   La excepcion «inline» de WCAG 2.5.8 no aplica aqui porque no estan dentro
   de un parrafo: estan solos, como acciones. Asi que les toca el minimo. */
.entrada__pie a {
  display: inline-flex;
  align-items: center;
  min-height: var(--objetivo-tactil);
}

/* Formularios largos (registro) dentro de la tarjeta: el ancho de la tarjeta
   sube, porque apretar ocho campos en 452 px produce filas de un solo campo
   y una columna de dos metros. */
.entrada__tarjeta--ancha {
  width: min(620px, 100%);
}

/* =======================================================================
   Home del jugador
   ======================================================================= */

.home {
  display: grid;
  gap: var(--e-5);
}

/* La portada del jugador — UX-R3.2.

   Una sola pieza con lo que se mira al volver: quién eres y qué vas a hacer a
   la izquierda, con qué a la derecha. Antes eran dos bloques uno debajo del
   otro, y el orden decía «hola, juega» y solo después «ah, y este es tu
   héroe» — al revés de como se lee un juego.

   La profundidad sale del mismo recurso que el portal: cromo con dos
   degradados. Es deliberado que se parezcan: la portada del jugador es el
   portal una vez dentro. */
.home__portada {
  display: grid;
  gap: var(--e-6);
  align-items: center;
  padding: var(--e-8) var(--e-6);
  border-radius: var(--radio);
  background: var(--cromo-elevado);
  background-image:
    radial-gradient(
      100% 120% at 0% 0%,
      color-mix(in srgb, var(--acento-relleno) 40%, transparent),
      transparent 62%
    ),
    linear-gradient(100deg, var(--cromo-elevado), var(--cromo) 70%);
  color: var(--texto-sobre-color);
  box-shadow: var(--sombra-2);
}

@media (min-width: 900px) {
  .home__portada {
    grid-template-columns: minmax(0, 1.2fr) minmax(280px, 0.8fr);
    gap: var(--e-16);
  }
}

/* El fondo de la portada es oscuro, asi que su texto NO puede heredar
   `--texto` del body. Y no basta con poner el color en el contenedor: `base.css`
   declara `color: var(--texto)` DIRECTAMENTE sobre `h1, h2, .t-titulo...`, y una
   declaracion propia gana siempre a la herencia. Por eso van nombrados.

   Sin esto, el saludo salia a 1,46:1 sobre `--cromo-elevado`, muy por debajo
   del 4,5:1 de WCAG AA (1.4.3): el panel mas visible de la aplicacion y
   practicamente ilegible. Lo midio el laboratorio visual (UX-R2.1); leyendo el
   CSS no se ve, porque el color venia de tres niveles mas arriba.

   La excepcion es `.home__heroe`: ahi dentro vive una tarjeta blanca del kit,
   con su propio texto oscuro, y forzarle el blanco la dejaria ilegible. */
.home__presentacion :is(h1, h2, h3, .t-titulo, .t-subtitulo, .t-etiqueta, .t-cuerpo) {
  color: var(--texto-sobre-color);
}

.home__presentacion {
  display: flex;
  flex-direction: column;
  gap: var(--e-4);
}

.home__sobretitulo {
  margin: 0;
  font-family: var(--t-etiqueta-familia);
  font-size: var(--t-meta-tam);
  font-weight: var(--t-etiqueta-peso);
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--pista);
}

.home__saludo {
  margin: 0;
  font-family: var(--t-titulo-familia);
  font-size: var(--t-titulo-xl-tam);
  line-height: var(--t-titulo-xl-alto);
  /* Un apodo puede ser largo y no lleva espacios: sin esto empujaba la
     portada a lo ancho y sacaba el botón fuera en móvil. */
  overflow-wrap: anywhere;
}

.home__lema {
  max-width: 46ch;
  margin: 0;
  color: var(--pista);
}

.home__acciones {
  display: flex;
  flex-wrap: wrap;
  gap: var(--e-3);
  padding-top: var(--e-2);
}

/* El héroe equipado dentro de la portada. La tarjeta que pinta `home.js` es
   del kit —blanca, con su retrato y su anillo de rareza— y aquí solo se le
   dice cuánto sitio tiene.

   `color: var(--texto)` NO es redundante: la portada declara
   `--texto-sobre-color` para su propio texto, y eso se hereda hacia dentro.
   Los títulos y encabezados del kit traen su color declarado y se salvan,
   pero los estados de vista pintan su título con `<p class="…__titulo">`, que
   hereda — y sobre la tarjeta blanca salía blanco sobre blanco. El
   laboratorio no lo habría marcado: el texto estaba, medía bien y no
   desbordaba. Simplemente no se veía. */
.home__heroe {
  min-width: 0;
  color: var(--texto);
}

.home__heroe > * {
  height: 100%;
}

.home__rejilla {
  display: grid;
  gap: var(--e-4);
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
}

/* --- Mi cuenta · Estadísticas (UXC-9) ---------------------------------------
   Las cifras de la cuenta conservan su ancho aunque haya una sola: con
   `auto-fit`, «Partidas jugadas» sola se estiraba a lo ancho de la tarjeta. */
.cuenta-estadisticas__cifras {
  display: grid;
  gap: var(--e-4);
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
}

/* Notas de una o dos frases con su título (torneos y logros; suscripciones y
   métodos de pago). `.tarjeta__datos` es para datos cortos en negrita: con
   frases largas partía el texto en columnas de 96 px a 375. */
.cuenta-notas {
  display: grid;
  gap: var(--e-4);
  grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
  margin: 0;
}

.cuenta-notas__nota {
  display: grid;
  gap: var(--e-1);
  min-width: 0;
}

.cuenta-notas dt {
  font-weight: var(--t-cuerpo-peso-enfasis);
}

.cuenta-notas dd {
  margin: 0;
  color: var(--texto-2);
}

/* Revisión del modo jugador del 6-oct, puntos 6 y 7 — el escaparate de la
   tienda ocupa el sitio de «A dónde ir» (que repetía la barra). Las tarjetas
   son las de producto de la tienda (`tienda-producto.css`): superficie clara
   sobre la atmósfera, como en la portada pública. */
.home__tienda {
  display: grid;
  gap: var(--e-4);
}

.home__tienda-cabecera {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--e-3);
}

.home__tienda-titulos {
  display: grid;
  gap: var(--e-1);
}

.home__tienda-titulo {
  font-family: var(--t-titulo-familia);
  font-size: var(--t-titulo-tam);
  line-height: var(--t-titulo-alto);
}

.home__tienda-rejilla {
  display: grid;
  gap: var(--e-4);
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
}

@media (max-width: 599px) {
  .home__tienda-rejilla {
    grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
    gap: var(--e-3);
  }
}

/* =======================================================================
   Avatar e importes — Mi Cuenta
   -----------------------------------------------------------------------
   Estaban en `cuentas/registro.css` y `cuentas/perfil.css`, cada uno con su
   propia versión. Viven aquí porque los usan el registro, el perfil y el
   resumen de la cuenta.
   ======================================================================= */

.avatar-vista-previa {
  width: 72px;
  height: 72px;
  border-radius: 50%;
  background: var(--fondo);
  border: 2px dashed var(--borde);
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  object-fit: cover;
  flex-shrink: 0;
}

.avatar-vista-previa img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.avatar-placeholder {
  font-size: var(--t-meta-tam);
  color: var(--texto-3);
}

/* Importe de un movimiento de créditos. El color acompaña al signo que ya
   está escrito (+ / − / «apartados»): nunca es la única señal. */
.movimiento__importe {
  font-variant-numeric: var(--cifras-tabulares);
  font-weight: var(--t-cuerpo-peso-enfasis);
  white-space: nowrap;
}

.movimiento__importe[data-tono='suma'] {
  color: var(--exito);
}
.movimiento__importe[data-tono='resta'] {
  color: var(--error);
}
.movimiento__importe[data-tono='aparta'] {
  color: var(--advertencia);
}
.movimiento__importe[data-tono='neutro'] {
  color: var(--texto-2);
}

/* =======================================================================
   Rejilla de formulario
   -----------------------------------------------------------------------
   Formularios de varios campos que reparten el ancho. `.fila` no sirve
   aqui: reparte a partes iguales y un formulario de cinco campos necesita
   que cada uno conserve un ancho minimo legible y baje de linea solo
   cuando no cabe. `__ancho` es el campo que ocupa la fila entera (un area
   de texto, normalmente).
   ======================================================================= */

.rejilla-formulario {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: var(--e-4);
}

.rejilla-formulario__ancho {
  grid-column: 1 / -1;
}

/* Fila cuyo ultimo elemento es un boton que no debe estirarse: el boton
   se alinea con la base de los campos, no con su etiqueta. */
.fila--fin {
  align-items: flex-end;
}
.fila--fin > .boton {
  flex: 0 0 auto;
}

/* Fila que envuelve sin repartir el ancho: distintivos, etiquetas, chips.
   `.fila` estira cada hijo a partes iguales, que es lo contrario de lo que
   necesita una lista de permisos o de estados. */
.fila--envuelta > * {
  flex: 0 0 auto;
}

/* =======================================================================
   Codigo de invitacion — FI-R4 · RF-JUE-002
   -----------------------------------------------------------------------
   Ocho caracteres que alguien va a leer en voz alta, copiar de una captura o
   teclear de memoria. Se pinta como una llave, no como texto corrido: fondo
   propio, cifras tabulares para que los grupos de cuatro no bailen, y espacio
   entre letras para que no se confundan al dictarlas. El alfabeto del servicio
   ya excluye O, 0, I y 1 (`Sala.ALFABETO_DEL_CODIGO`); esto es la otra mitad
   del mismo cuidado.
   ======================================================================= */
.fila--invitacion {
  align-items: center;
  flex-wrap: wrap;
}
.fila--invitacion > * {
  flex: 0 0 auto;
}

.codigo-invitacion {
  padding: var(--e-1) var(--e-3);
  border: 1px solid var(--borde-int);
  border-radius: var(--radio-chico);
  background: var(--superficie);
  color: var(--texto);
  font-variant-numeric: var(--cifras-tabulares);
  font-weight: var(--t-cuerpo-peso-enfasis);
  letter-spacing: 0.08em;
  /* Seleccionable de una pasada: quien no tenga portapapeles lo copia a mano,
     y arrastrar sobre ocho caracteres con un guion en medio falla mas de lo
     que parece. */
  user-select: all;
}

/* =======================================================================
   Turno en curso — HU-SAL-005 · RF-JUE-017
   -----------------------------------------------------------------------
   En un juego por turnos, de quien es el turno es el dato mas importante de
   la pantalla, y hasta ahora solo se deducia de que los botones de atacar
   estuvieran grises. Se dice con texto (que es lo que lee un lector de
   pantalla) y se marca sobre la barra de quien juega; el color acompana al
   texto, nunca lo sustituye.
   ======================================================================= */

.turno-actual {
  display: inline-flex;
  align-items: center;
  gap: var(--e-2);
  padding: var(--e-1) var(--e-3);
  border: 1px solid color-mix(in srgb, var(--pista) 40%, transparent);
  border-radius: var(--radio-pildora);
  font-family: var(--t-etiqueta-familia);
  font-size: var(--t-etiqueta-tam);
  color: var(--tinta-2);
  white-space: nowrap;
}

/* Cuando toca jugar, el indicador deja de ser informativo y pasa a ser una
   llamada a la accion. Relleno solido con texto claro: el mismo par que usa
   `.boton--acento`, para no inventar una combinacion sin contraste medido. */
.turno-actual[data-mio='true'] {
  border-color: transparent;
  background: var(--energia);
  color: var(--texto-sobre-color);
  box-shadow: var(--brillo-primaria);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

/* Combate finalizado: el tercer estado. Sobrio, sin brillo, sin punto. */
.turno-actual[data-fin='si'] {
  border-color: color-mix(in srgb, var(--pista) 40%, transparent);
  color: var(--tinta);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.turno-actual[data-fin='si']::before {
  background: var(--tinta-3);
}

/* Cambio de turno: el indicador entra con un pulso corto, una sola vez. */
@media (prefers-reduced-motion: no-preference) {
  .turno-actual[data-mio='true'] {
    animation: entrar-dialogo var(--duracion-normal) var(--curva-salida) both;
  }
}

.turno-actual::before {
  content: '';
  width: var(--icono-menudo);
  height: var(--icono-menudo);
  border-radius: 50%;
  background: var(--tinta-3);
  flex-shrink: 0;
}

.turno-actual[data-mio='true']::before {
  background: var(--texto-sobre-color);
}

/* La barra de quien juega, destacada. El borde y el desplazamiento no tapan
   el color de vida, que sigue diciendo lo suyo. */
.barra-vida[data-turno='si'] {
  border-left: 3px solid var(--enlace-sobre-cromo);
  padding-left: var(--e-2);
}

@media (prefers-reduced-motion: no-preference) {
  .turno-actual[data-mio='true']::before {
    animation: latido-turno var(--duracion-turno, 1.2s) ease-in-out infinite;
  }
}

@keyframes latido-turno {
  0%,
  100% {
    opacity: 1;
  }
  50% {
    opacity: 0.35;
  }
}

/* =======================================================================
   Arbol del torneo — HU-TOR-004 · RF-TOR-004
   -----------------------------------------------------------------------
   El componente `Encuentro` (mas arriba, linea ~1667) ya venia del Figma con
   su cabecera, sus dos filas de equipo y el ganador en negrita. Lo que
   faltaba era el contenedor: la vista pintaba catorce encuentros en una
   lista plana, que no se parece a un cuadro de doble eliminacion. `ronda`
   viene en el contrato; aqui se convierte en columnas.
   ======================================================================= */

.arbol-torneo {
  /* UXC-9 — contenedor de lo absoluto de dentro: el «(gana este encuentro)»
     para lectores de pantalla se posicionaba contra la página y, en la
     última ronda, la ensanchaba 2 px a 768. */
  position: relative;
  display: flex;
  gap: var(--e-5);
  align-items: flex-start;
  overflow-x: auto;
  padding-bottom: var(--e-2);
}

/* Los equipos del torneo, en rejilla (UX-GAME-5). */
.torneo__equipos {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: var(--e-3);
}

.torneo__equipos .tarjeta {
  padding: var(--e-3) var(--e-4);
}

.torneo__equipos .tarjeta[data-ia='true'] {
  border-style: dashed;
}

.arbol-torneo__ronda {
  display: flex;
  flex-direction: column;
  gap: var(--e-3);
  flex: 0 0 auto;
}

/* En pantalla estrecha el cuadro se lee hacia abajo: un scroll horizontal
   de catorce encuentros en un movil no lo usa nadie. */
@media (max-width: 720px) {
  .arbol-torneo {
    flex-direction: column;
    overflow-x: visible;
  }
  .encuentro {
    width: 100%;
  }
}

/* =======================================================================
   Aviso flotante — HU-RBAC-004
   -----------------------------------------------------------------------
   El rechazo de permiso que levanta el interceptor compartido. Se construia
   a mano con veinte lineas de `elemento.style.*` en linea; ahora es un
   `.aviso` normal que flota. El color y el borde salen del modificador
   (`.aviso--error`), asi que acompanan al modo de alto contraste.
   ======================================================================= */

.aviso-flotante {
  position: fixed;
  top: var(--e-5);
  right: var(--e-5);
  z-index: 9999;
  max-width: min(420px, calc(100vw - var(--e-8, 2rem)));
  box-shadow: var(--sombra-3);
  opacity: 0;
  transform: translateY(-10px);
  transition:
    opacity var(--duracion-normal) var(--curva-salida),
    transform var(--duracion-normal) var(--curva-salida);
  /* Mientras esta invisible no debe atrapar clics de lo que hay debajo. */
  pointer-events: none;
}

.aviso-flotante[data-visible='si'] {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}

/* En pantalla estrecha ocupa el ancho: un aviso de 420 px anclado a la
   derecha en un movil se sale o tapa el contenido. */
@media (max-width: 599px) {
  .aviso-flotante {
    left: var(--e-3);
    right: var(--e-3);
    max-width: none;
  }
}

/* Destino de la barra que exige sesión — HU-INV-004 CA-01 · UX-R2.0
   -----------------------------------------------------------------------
   El requisito manda los seis accesos siempre, tambien a quien no ha
   entrado. Lo que faltaba era que se notara cual va a pedir sesion: se veian
   identicos y cuatro devolvian al login. La opacidad acompana al `title`,
   que es lo que lee un lector de pantalla; el color no es la unica senal. */
.cabecera__destino--con-sesion {
  opacity: 0.72;
}

.cabecera__destino--con-sesion:hover,
.cabecera__destino--con-sesion:focus-visible {
  opacity: 1;
}

/* =======================================================================
   Componentes de juego — UX-R2.2
   -----------------------------------------------------------------------
   Lo que el kit ya traia de Figma (`marco-heroe`, `ranura`, `accion-combate`,
   `barra-vida`, `turno-actual`) no se toca: esto es solo lo que le faltaba
   para que esas piezas se puedan usar de verdad en una pantalla.

   Ni un color nuevo. Todo sale de fichas existentes.
   ======================================================================= */

/* --- Heroe sin retrato ------------------------------------------------- */

/* Antes de que llegue la imagen, o cuando el heroe no tiene ninguna, la
   inicial dice de quien es el hueco. Un circulo vacio no distingue seis
   participantes; una «A» y una «M», si. */
.marco-heroe__inicial {
  font-family: var(--t-titulo-familia);
  font-size: var(--t-titulo-tam);
  font-weight: var(--t-titulo-peso);
  color: var(--texto-2);
}

/* --- Carta de heroe ---------------------------------------------------- */

.tarjeta-heroe {
  display: grid;
  gap: var(--e-3);
  justify-items: center;
  text-align: center;
}

/* Cuando es pulsable es un `<button>`, y un boton trae estilos de agente de
   usuario que hay que apagar para que siga pareciendo una carta. */
button.tarjeta-heroe {
  width: 100%;
  font: inherit;
  color: inherit;
  cursor: pointer;
}

.tarjeta-heroe__identidad {
  display: grid;
  gap: var(--e-1);
  justify-items: center;
}

/* Elegida: no basta con el borde de foco, que se va al pulsar en otro sitio.
   `aria-pressed` es lo que lee el lector; esto es su equivalente visual. */
.tarjeta-heroe--seleccionada,
button.tarjeta-heroe[aria-pressed='true'] {
  border-color: var(--primaria);
  box-shadow: var(--anillo-foco);
}

.tarjeta-heroe__estadisticas {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(72px, 1fr));
  gap: var(--e-2) var(--e-3);
  margin: 0;
  width: 100%;
}

.tarjeta-heroe__estadisticas dt {
  color: var(--texto-2);
}

.tarjeta-heroe__cifra {
  margin: 0;
  font-family: var(--t-titulo-familia);
  font-size: var(--t-subtitulo-tam);
  font-weight: var(--t-titulo-peso);
}

/* --- Ranuras de equipamiento ------------------------------------------- */

.ranura__icono {
  width: var(--icono);
  height: var(--icono);
}

.grupo-ranuras {
  display: grid;
  gap: var(--e-3);
}

.grupo-ranuras__rejilla {
  display: flex;
  flex-wrap: wrap;
  gap: var(--e-4);
}

/* --- Accion de combate: el coste --------------------------------------- */

.accion-combate__coste {
  color: var(--texto-2);
}

/* --- Indicador de turno: ronda y texto --------------------------------- */

.turno-actual__ronda {
  font-weight: var(--t-titulo-peso);
}

/* Separador entre la ronda y de quien es el turno, sin meter un nodo mas. */
.turno-actual__ronda + .turno-actual__texto::before {
  content: '·';
  margin: 0 var(--e-2);
  color: currentcolor;
}

/* --- Creditos ---------------------------------------------------------- */

.distintivo-credito {
  display: inline-flex;
  align-items: center;
  gap: var(--e-1);
  padding: var(--e-1) var(--e-3);
  border: 1px solid var(--credito-oro);
  border-radius: var(--radio-pildora);
  background: var(--rareza-legendaria-insignia);
  font-family: var(--t-titulo-familia);
  font-weight: var(--t-titulo-peso);
  line-height: 1;
  white-space: nowrap;
}

.distintivo-credito__icono {
  width: var(--icono-menudo);
  height: var(--icono-menudo);
  color: var(--credito-oro);
}

.distintivo-credito--grande {
  padding: var(--e-2) var(--e-4);
  font-size: var(--t-subtitulo-tam);
}

.distintivo-credito--grande .distintivo-credito__icono {
  width: var(--icono);
  height: var(--icono);
}

.distintivo-credito__contexto {
  color: var(--texto-2);
  font-family: var(--t-meta-familia);
  font-weight: var(--t-cuerpo-peso);
}

/* --- Desenlace de partida ---------------------------------------------- */

/* `--t-display-tam` llevaba en tokens.css desde el principio, reservada para
   esto, y nadie la usaba: el tamano mas grande de toda la aplicacion era el de
   un titulo de seccion. Una partida que termina igual de discreta que un
   formulario no se siente como el final de nada. */
.panel-resultado {
  display: grid;
  justify-items: center;
  gap: var(--e-3);
  padding: var(--e-8) var(--e-6);
  border-radius: var(--radio);
  background: var(--superficie);
  border: 1px solid var(--borde);
  box-shadow: var(--sombra-3);
  text-align: center;
}

@media (prefers-reduced-motion: no-preference) {
  /* El desenlace entra una vez, en 320 ms: es el unico momento de la
     pantalla que se merece algo mas que un hover. */
  .panel-resultado {
    animation: entrar-desenlace var(--duracion-pausado) var(--curva-salida) both;
  }

  @keyframes entrar-desenlace {
    from {
      opacity: 0;
      transform: translateY(12px) scale(0.98);
    }
    to {
      opacity: 1;
      transform: none;
    }
  }
}

.panel-resultado__icono {
  width: 48px;
  height: 48px;
}

.panel-resultado__palabra {
  margin: 0;
  font-family: var(--t-titulo-familia);
  font-size: var(--t-display-tam);
  line-height: var(--t-display-alto);
  font-weight: var(--t-titulo-peso);
  letter-spacing: 0.04em;
}

/* Ganar y perder no son el mismo panel con otro color: cambian la palabra, el
   icono y el borde. Quien no distinga verde de rojo lee «VICTORIA» igual. */
/* UX-GAME-4 — la victoria es azul/violeta luminosa, la identidad del Nexo, no
   el verde de «guardado». El filo superior lleva el gradiente de energia y el
   panel un brillo; la palabra va en la primaria (8,5:1 sobre blanco) y el
   detalle de creditos ganados sigue en exito, que es el color del saldo. */
.panel-resultado--victoria {
  border-top: 4px solid transparent;
  border-image: var(--energia) 1;
  border-image-width: 4px 0 0 0;
  box-shadow: var(--sombra-3), var(--brillo-primaria);
}

.panel-resultado--victoria .panel-resultado__palabra {
  background: var(--energia);
  -webkit-background-clip: text;
  background-clip: text;
  color: var(--primaria-relleno);
  -webkit-text-fill-color: transparent;
}

.panel-resultado--victoria .panel-resultado__icono {
  color: var(--primaria);
}

@media (prefers-contrast: more) {
  .panel-resultado--victoria .panel-resultado__palabra {
    background: none;
    -webkit-text-fill-color: currentcolor;
  }
}

.panel-resultado--derrota {
  border-top: 4px solid var(--texto-3);
}

.panel-resultado--derrota .panel-resultado__palabra,
.panel-resultado--derrota .panel-resultado__icono {
  color: var(--texto-2);
}

/* El empate es su propio desenlace y no una derrota con otra frase (R10).
   Antes solo habia dos modificadores, asi que una partida en la que nadie
   quedaba en pie se pintaba de derrota mientras el texto decia «empate». El
   borde va en el color de advertencia y no en el de error: no se perdio nada. */
.panel-resultado--empate {
  border-top: 4px solid var(--advertencia);
}

.panel-resultado--empate .panel-resultado__palabra,
.panel-resultado--empate .panel-resultado__icono {
  color: var(--texto-2);
}

.panel-resultado__detalle {
  margin: 0;
  color: var(--texto-2);
}

.panel-resultado__creditos {
  margin: 0;
  font-family: var(--t-titulo-familia);
  font-size: var(--t-subtitulo-tam);
  font-weight: var(--t-titulo-peso);
}

.panel-resultado__creditos[data-signo='positivo'] {
  color: var(--exito);
}

.panel-resultado__creditos[data-signo='negativo'] {
  color: var(--texto-2);
}

.panel-resultado__acciones {
  display: flex;
  flex-wrap: wrap;
  gap: var(--e-3);
  justify-content: center;
  margin-top: var(--e-2);
}

/* En movil el display de 56px ocupa media pantalla. Se reduce, pero sigue
   siendo con diferencia lo mas grande de la vista, que es su trabajo. */
@media (max-width: 480px) {
  .panel-resultado {
    padding: var(--e-6) var(--e-4);
  }

  .panel-resultado__palabra {
    font-size: var(--t-titulo-xl-tam);
  }
}

/* La entrada del panel solo para quien no ha pedido lo contrario. El panel se
   ve igual de bien sin ella; la animacion es adorno, no informacion. */
@media (prefers-reduced-motion: no-preference) {
  .panel-resultado {
    animation: aparecer-resultado var(--duracion-pausado) var(--curva-estandar);
  }
}

@keyframes aparecer-resultado {
  from {
    opacity: 0;
    transform: translateY(8px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

/* =======================================================================
   Pantalla de combate — HU-JUE-017 · RF-JUE-017 (UX-R2.3)
   -----------------------------------------------------------------------
   CA-01 pide que el area de juego ocupe mas del 80 % del alto y del ancho
   utiles, con los menus abajo o al lado y NUNCA sobre el campo. Eso no se
   consigue con `.pagina`, que es una columna centrada con margenes: se
   consigue con una reja de tres franjas que ocupa la ventana entera menos la
   cabecera de la aplicacion.

   Las medidas: la cabecera son 64 px (`--cabecera-alto`). El HUD y los
   controles se dimensionan por su contenido, con un maximo; lo que sobra es
   campo. En 1360×768 —el minimo de la ficha— eso deja el campo por encima del
   80 % del alto util y el 100 % del ancho.
   ======================================================================= */

.combate {
  display: grid;
  grid-template-rows: auto 1fr auto;
  gap: var(--e-3);
  /* La ventana menos la cabecera. `dvh` y no `vh`: en un movil la barra del
     navegador se esconde al desplazar y con `vh` el campo quedaba cortado. */
  min-height: calc(100dvh - var(--cabecera-alto));
  padding: var(--e-3) var(--e-4);
  box-sizing: border-box;
}

/* --- HUD superior (CA-02) ---------------------------------------------- */

.combate__hud {
  display: grid;
  gap: var(--e-2);
  padding: var(--e-3) var(--e-4);
  /* UX-GAME-4 — el HUD es la «cabecera de combate», el otro sitio donde la
     paleta permite el cromo (frontend-web.md). Oscuro, con filo de energia,
     y las barras de vida encima: verde, ambar y rojo se ven mejor sobre el
     cromo que sobre blanco. */
  background:
    var(--energia) top left / 100% 2px no-repeat,
    linear-gradient(180deg, var(--cromo-elevado), color-mix(in srgb, var(--cromo-elevado) 70%, var(--cromo)));
  border: 1px solid color-mix(in srgb, var(--primaria) 35%, transparent);
  border-radius: var(--radio);
  box-shadow: var(--sombra-atmosfera);
  --tinta: var(--texto-sobre-color);
  --tinta-2: var(--pista);
  --tinta-3: var(--pista);
  color: var(--tinta);
  /* Tope duro: el HUD no puede comerse el campo por muchos participantes que
     haya. A partir de ahi, las barras se desplazan dentro. */
  max-height: 22vh;
  overflow-y: auto;
}

.combate__hud-titulo {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--e-3);
}

/* Las seis barras de una partida de seis, en dos columnas cuando caben: en
   una sola fila cada una quedaria de 20 px y el color no se distinguiria. */
.combate__vidas {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: var(--e-2) var(--e-5);
}

/* --- El campo (CA-01, CA-03) ------------------------------------------- */

.combate__campo {
  position: relative;
  /* `min-height: 0` en una reja permite que la fila `1fr` encoja de verdad;
     sin ella el contenido impone su alto y el campo desborda la ventana. */
  min-height: 0;
  border-radius: var(--radio);
  border: 1px solid color-mix(in srgb, var(--primaria) 35%, transparent);
  /* UX-GAME-4 — la arena es OSCURA: el lado propio con halo azul, el rival
     con halo violeta, y una linea de energia en el centro que separa los dos
     bandos. Solo degradados: nada de imagenes ni de canvas. Antes era un
     gris claro que, entre el HUD y los controles blancos, se leia como una
     pantalla vacia. */
  background:
    radial-gradient(
      55% 70% at 18% 62%,
      color-mix(in srgb, var(--primaria) 42%, transparent),
      transparent 70%
    ),
    radial-gradient(
      55% 70% at 82% 38%,
      color-mix(in srgb, var(--acento-relleno) 42%, transparent),
      transparent 70%
    ),
    linear-gradient(
      90deg,
      transparent calc(50% - 1px),
      color-mix(in srgb, var(--enlace-sobre-cromo) 25%, transparent) calc(50% - 1px),
      color-mix(in srgb, var(--enlace-sobre-cromo) 25%, transparent) calc(50% + 1px),
      transparent calc(50% + 1px)
    ),
    linear-gradient(180deg, var(--cromo-elevado), var(--cromo));
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--texto-sobre-color) 6%, transparent);
  overflow: hidden;
  --tinta: var(--texto-sobre-color);
  --tinta-2: var(--pista);
  --tinta-3: var(--pista);
}

.campo__puesto {
  position: absolute;
  display: grid;
  justify-items: center;
  gap: var(--e-1);
  /* La posicion que calcula `campo.js` es la del centro del heroe. */
  transform: translate(-50%, -50%);
  transition: transform var(--duracion-normal) var(--curva-estandar);
}

.campo__nombre {
  font-family: var(--t-etiqueta-familia);
  font-size: var(--t-etiqueta-tam);
  font-weight: var(--t-etiqueta-peso);
  color: var(--tinta);
  text-align: center;
  max-width: 120px;
  overflow-wrap: break-word;
  text-shadow: 0 1px 2px color-mix(in srgb, var(--cromo) 80%, transparent);
}

/* Sobre la arena el retrato lleva su brillo: azul el propio, violeta el
   rival. El anillo de rareza sigue siendo el borde. */
.combate__campo .marco-heroe__retrato {
  box-shadow: var(--brillo-primaria);
}

.campo__puesto--rival .marco-heroe__retrato {
  box-shadow: var(--brillo-acento);
}

/* Los rivales miran hacia el jugador. Es el truco de siempre en un juego 2D y
   cuesta una linea. UXC-2 — solo la IMAGEN: volteando el retrato entero, la
   inicial salia en espejo («Ɔ» por «C») y el simbolo del prototipo tambien. */
.campo__puesto--rival .marco-heroe__imagen {
  transform: scaleX(-1);
}

/* A quien le toca, destacado. Igual que en la barra de vida: el anillo
   acompana al texto del indicador de turno, no lo sustituye. */
.campo__puesto[data-turno='si'] .marco-heroe__retrato {
  box-shadow:
    0 0 0 4px var(--enlace-sobre-cromo),
    var(--brillo-primaria);
}

.campo__puesto--caido {
  opacity: 0.45;
  filter: grayscale(1);
}

/* --- Capa de sala: lo de antes del combate ----------------------------- */

.combate__sala {
  position: absolute;
  inset: auto 0 0 0;
  display: grid;
  gap: var(--e-3);
  justify-items: center;
  padding: var(--e-4);
  pointer-events: none;
}

.combate__sala > * {
  pointer-events: auto;
}

/* UX-GAME-4 — el lobby (arranque, invitacion, ocupacion) va sobre un velo
   oscuro al pie de la arena, no suelto sobre el degradado: es un panel de
   sala, y sus botones y el codigo de invitacion necesitan un fondo comun. */
.combate[data-sin-partida='si'] .combate__sala {
  padding-top: var(--e-5);
  background: linear-gradient(
    180deg,
    transparent,
    color-mix(in srgb, var(--cromo) 78%, transparent) 30%,
    color-mix(in srgb, var(--cromo) 92%, transparent)
  );
}

/* --- Controles, abajo (CA-01) ------------------------------------------ */

.combate__controles {
  display: grid;
  gap: var(--e-2);
  padding: var(--e-3) var(--e-4);
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-radius: var(--radio);
  box-shadow: var(--sombra-2);
}

.combate__acciones {
  display: flex;
  flex-wrap: wrap;
  gap: var(--e-3);
  justify-content: center;
}

/* --- Desenlace (CA-04) -------------------------------------------------- */

/* Encima de todo, centrado. Cuando aparece, la partida acabo: no hay nada
   debajo que pulsar, asi que taparlo no incumple CA-01. */
.combate__desenlace {
  position: fixed;
  inset: 0;
  display: grid;
  place-items: center;
  padding: var(--e-4);
  background: rgb(17 23 38 / 55%);
  z-index: 40;
}

.combate__desenlace[hidden] {
  display: none;
}

/* --- Degradacion por tamano (CA-05) ------------------------------------- */

/* Por encima del minimo, el aviso no existe. */
.combate__aviso-tamano {
  display: none;
  margin: 0;
  padding: var(--e-3);
  border-radius: var(--radio);
  background: var(--advertencia-insignia);
  border: 1px solid var(--advertencia);
  color: var(--texto);
  text-align: center;
}

/* Por debajo, el campo absoluto no cabe: en vez de dejar a los heroes
   amontonados en una franja de 90 px, se retira el campo y se dice por que.
   La vida y los controles siguen enteros — degradar no es apagar. */
@media (max-width: 767px), (max-height: 520px) {
  .combate {
    min-height: auto;
    grid-template-rows: auto auto auto;
  }

  .combate__campo {
    display: none;
  }

  .combate__aviso-tamano {
    display: block;
  }

  .combate__hud {
    max-height: none;
  }

  .combate__sala {
    position: static;
    padding: 0;
  }
}

/* =======================================================================
   Elegir que va en una ranura — HU-INV-005 (UX-R2.5)
   -----------------------------------------------------------------------
   El contenido del dialogo que se abre al pulsar una ranura vacia. Vive en
   el kit y no en la vista porque cualquier pantalla con ranuras —el
   equipamiento de hoy, la configuracion de un equipo de torneo manana—
   necesita exactamente esta lista.
   ======================================================================= */

.elegir-objeto {
  display: grid;
  gap: var(--e-2);
  margin: 0;
  padding: 0;
  list-style: none;
  max-height: 50vh;
  overflow-y: auto;
}

.elegir-objeto__opcion {
  width: 100%;
  padding: var(--e-3);
  text-align: left;
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-radius: var(--radio);
  color: var(--texto);
  font: inherit;
  cursor: pointer;
}

.elegir-objeto__opcion:hover {
  background: var(--interaccion-velo-hover);
}

.elegir-objeto__opcion:focus-visible {
  outline: var(--foco-grosor) solid var(--interaccion-foco-anillo);
  outline-offset: 2px;
}

/* Publicado en una subasta (HU-INV-010): se ofrece igual, apagado y con su
   motivo en el texto. Esconderlo dejaria al jugador buscando una pieza que
   tiene y no entiende por que no aparece. */
.elegir-objeto__opcion--bloqueada {
  color: var(--texto-3);
  cursor: not-allowed;
}

.equipamiento__resumen {
  margin: 0;
  grid-column: 1 / -1;
}

/* --- Home como hub del jugador (UX-R2.6) -------------------------------- */

/* El CTA por el que uno entra al juego. `--boton-grande` son 48px, que es la
   altura de una acción principal cualquiera; esta no es cualquiera. */
.home__jugar {
  min-height: 56px;
  padding-inline: var(--e-8);
  font-size: var(--t-subtitulo-tam);
  box-shadow: var(--sombra-2);
}

/* El héroe equipado ya no es «lo segundo que se mira»: desde UX-R3.2 vive
   dentro de la portada, al lado de la acción de jugar. En la ranura, el
   retrato manda y los datos se apilan debajo — la columna mide 280 px como
   mínimo, no el ancho de la página, así que el retrato a la izquierda y el
   texto a la derecha ya no caben cómodos. */
.home__heroe .tarjeta {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--e-3);
  text-align: center;
}

/* UX-R3.2 — aquí había `display: block` sobre `.estado-vista`.

   La intención era que el estado ocupara la tarjeta entera, y lo conseguía;
   el efecto secundario era que deshacía el `display: flex` del propio
   componente y con él su `align-items: center`. El resultado: el icono
   circular de 56 px pegado al borde izquierdo, y el título y el detalle
   centrados por el `text-align` heredado. Una pieza descuadrada, en el bloque
   más visible de la home, en los dos estados en que aparece (sin héroe, y con
   el inventario caído — que es el estado por omisión en dev).

   `.estado-vista` ya sabe ocupar y centrar: lo que necesitaba era altura, no
   otro `display`. */
.home__heroe .estado-vista {
  height: 100%;
}

/* UX-GAME-3 — el retrato del héroe equipado va sobre un escenario oscuro con
   un halo azul, no sobre el gris de `--fondo`: es la pieza de juego de la
   portada y se lee como tal. El anillo del retrato recibe el brillo de la
   primaria; la rareza, cuando la hay, sigue mandando en el color del borde. */
.home__heroe .tarjeta__medios {
  align-self: stretch;
  display: grid;
  place-items: center;
  padding: var(--e-5) var(--e-4);
  background:
    radial-gradient(
      60% 75% at 50% 45%,
      color-mix(in srgb, var(--primaria) 45%, transparent),
      transparent 72%
    ),
    var(--cromo);
}

.home__heroe .marco-heroe__retrato {
  box-shadow: var(--brillo-primaria);
}

@media (max-width: 767px) {
  /* Las dos acciones de la portada ocupan el ancho y se apilan: en 375 px,
     «Jugar ahora» y «Ver salas abiertas» en la misma fila dejaban la segunda
     en dos renglones. */
  .home__acciones > .boton {
    width: 100%;
    justify-content: center;
  }

  .home__jugar {
    width: 100%;
    justify-content: center;
  }
}

/* =====================================================================
   Cuenta atras — UX-R2.8b
   =====================================================================

   El tiempo que le queda a una subasta es el dato con el que alguien
   decide si puja ahora o espera. Se lee como una cifra, no como una
   frase: `--cifras-tabulares` para que «04:31» no baile al pasar a
   «04:30», que es exactamente lo que hace un contador con la fuente
   proporcional.

   `--urgente` es color y peso. NO es un parpadeo: la urgencia nunca se
   codifica solo en movimiento, porque entonces `prefers-reduced-motion`
   la borra (regla de UX-R2.10). */
.cuenta-atras {
  font-variant-numeric: var(--cifras-tabulares);
  font-weight: 600;
  color: var(--texto-2);
}

.cuenta-atras--urgente {
  color: var(--error);
}

.cuenta-atras--finalizada {
  color: var(--texto-3);
  font-weight: 400;
}

/* UXC-6 — «Termina en 2d»: el rótulo a la vista, en el tono de la meta. */
.cuenta-atras-rotulada {
  color: var(--texto-2);
}

/* =====================================================================
   Mercado — encabezado y rejilla de subastas (UX-R2.8b)
   ===================================================================== */

/* La barra de busqueda, el orden y el filtro son UN control compuesto, no
   tres bloques sueltos apilados con el titulo en medio. */
.mercado__controles {
  display: flex;
  flex-wrap: wrap;
  gap: var(--e-3);
  align-items: center;
  margin-bottom: var(--e-5);
}

.mercado__controles > :first-child {
  flex: 1 1 260px;
  min-width: 0;
}

.mercado__resumen {
  color: var(--texto-3);
  font-size: var(--t-meta-tam);
}

/* =====================================================================
   Acuse de un cambio de estado — UX-R2.10
   =====================================================================

   Movimiento con proposito: turno, dano, curacion, equipar, puja,
   notificacion, dialogo y desenlace. Nada mas. Sin fondos animados, sin
   entradas escalonadas de listas, sin transiciones de pagina.

   Las duraciones salen de las fichas del kit, que valen `0ms` bajo
   `prefers-reduced-motion`. Por eso aqui NO hay un bloque
   `@media (prefers-reduced-motion)` que apague nada: con la preferencia
   puesta, la animacion dura cero y el cambio se ve igual, de golpe. Lo que
   no puede desaparecer es la informacion, y la informacion vive en
   `.acuse__cifra`, que es texto con `aria-live`. */

.acuse--dano {
  animation: acuse-sacudida var(--duracion-pausado) var(--curva-estandar);
}

.acuse--curacion {
  animation: acuse-brillo var(--duracion-pausado) var(--curva-salida);
}

.acuse--equipar {
  animation: acuse-encaje var(--duracion-normal) var(--curva-impulso);
}

.acuse--puja {
  animation: acuse-brillo var(--duracion-normal) var(--curva-salida);
}

/* Dos pixeles, no una convulsion: comunica el golpe sin marear a nadie ni
   empujar el resto de la maqueta (`translate`, no `margin`). */
@keyframes acuse-sacudida {
  0%,
  100% {
    transform: translateX(0);
  }
  25% {
    transform: translateX(-2px);
  }
  75% {
    transform: translateX(2px);
  }
}

@keyframes acuse-brillo {
  0% {
    filter: brightness(1);
  }
  40% {
    filter: brightness(1.35);
  }
  100% {
    filter: brightness(1);
  }
}

/* La ranura «encaja» lo que se acaba de poner: un rebote corto con la curva
   de impulso, que es justo para lo que esta esa ficha. */
@keyframes acuse-encaje {
  0% {
    transform: scale(0.94);
  }
  100% {
    transform: scale(1);
  }
}

/* La cifra: el dato, que es lo que sobrevive a `prefers-reduced-motion`.
   `pointer-events: none` para que no se coma un clic al aparecer encima de
   un boton. */
.acuse__cifra {
  position: absolute;
  top: 0;
  right: var(--e-2);
  z-index: 5;
  font-family: var(--t-etiqueta-familia);
  font-weight: 700;
  font-size: var(--t-cuerpo-tam);
  font-variant-numeric: var(--cifras-tabulares);
  pointer-events: none;
  animation: acuse-subir calc(var(--duracion-pausado) * 2) var(--curva-salida) forwards;
}

.acuse__cifra--dano {
  color: var(--error);
}

.acuse__cifra--curacion {
  color: var(--exito);
}

.acuse__cifra--puja {
  color: var(--acento-relleno);
}

@keyframes acuse-subir {
  0% {
    opacity: 0;
    transform: translateY(4px);
  }
  20% {
    opacity: 1;
    transform: translateY(-2px);
  }
  100% {
    opacity: 0;
    transform: translateY(-14px);
  }
}

/* El acuse se posiciona dentro de lo acusado. Solo las clases del kit: una
   vista que quiera acusar sobre un bloque propio declara su `position` en su
   propia hoja — que es lo que hace `subastas-vitrina.css`. */
.barra-vida,
.ranura {
  position: relative;
}

/* =====================================================================
   Presentacion de los heroes — HU-JUE-017 CA-04 (UX-R2.10)
   =====================================================================

   La otra mitad de «vistas de alto impacto al inicio y al final». El final
   es `.panel-resultado`; esto es el inicio.

   Es la SEGUNDA pantalla que usa `--t-display-tam`, y la unica junto al
   desenlace: el tamano display esta reservado para los dos momentos de mayor
   intensidad de la partida. Si aparece en una tercera, deja de significar
   nada.

   Cubre el campo, pero `aria-modal="false"` a proposito: el combate ya esta
   montado debajo y sigue vivo; esto no es un dialogo que bloquee, es una
   presentacion que se puede cerrar y a la que no se vuelve. */
.presentacion {
  position: absolute;
  inset: 0;
  z-index: 20;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--e-6);
  padding: var(--e-6);
  /* UX-GAME-4: la misma arena que el campo, con los dos halos. */
  background:
    radial-gradient(
      50% 70% at 20% 55%,
      color-mix(in srgb, var(--primaria) 45%, transparent),
      transparent 70%
    ),
    radial-gradient(
      50% 70% at 80% 45%,
      color-mix(in srgb, var(--acento-relleno) 45%, transparent),
      transparent 70%
    ),
    var(--cromo);
  color: var(--texto-sobre-color);
  text-align: center;
  animation: presentacion-entrar var(--duracion-pausado) var(--curva-salida);
}

@keyframes presentacion-entrar {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

.presentacion__rotulo {
  margin: 0;
  font-family: var(--fuente-titulo);
  font-size: var(--t-display-tam);
  font-weight: 700;
  line-height: 1;
  letter-spacing: 0.02em;
}

.presentacion__bandos {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--e-6);
}

.presentacion__bando {
  display: flex;
  flex-wrap: wrap;
  gap: var(--e-5);
  justify-content: center;
}

.presentacion__versus {
  font-family: var(--fuente-titulo);
  font-size: var(--t-display-tam);
  font-weight: 700;
  /* Sobre el cromo el acento oscuro no se veia; el «VS» es energia. */
  background: var(--energia);
  -webkit-background-clip: text;
  background-clip: text;
  color: var(--enlace-sobre-cromo);
  -webkit-text-fill-color: transparent;
  filter: drop-shadow(0 0 10px color-mix(in srgb, var(--primaria) 60%, transparent));
}

@media (prefers-contrast: more) {
  .presentacion__versus {
    background: none;
    -webkit-text-fill-color: currentcolor;
  }
}

.presentacion__heroe {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--e-2);
  min-width: 120px;
}

/* Quien abre el combate se marca con un anillo, no solo con el texto de
   abajo: es lo primero que alguien quiere saber al entrar. */
.presentacion__heroe--abre .marco-heroe {
  border-radius: var(--radio);
  box-shadow: 0 0 0 3px var(--enlace-sobre-cromo);
}

.presentacion__nombre {
  margin: 0;
  font-family: var(--fuente-titulo);
  font-size: var(--t-subtitulo-tam);
  font-weight: 700;
}

.presentacion__jugador {
  margin: 0;
  font-size: var(--t-meta-tam);
  color: var(--pista);
}

.presentacion__turno {
  margin: 0;
  font-size: var(--t-cuerpo-tam);
  color: var(--pista);
}

/* En pantallas cortas el rotulo display no cabe junto a los heroes: baja al
   tamano de titulo antes que empujar a los heroes fuera. */
@media (max-height: 600px), (max-width: 599px) {
  .presentacion__rotulo {
    font-size: var(--t-titulo-tam);
  }

  .presentacion {
    gap: var(--e-4);
  }
}

/* =======================================================================
   Los tres armazones — UX-R3.0 (src/comun/shell.js)
   -----------------------------------------------------------------------
   RF-INV-008 («adapta las opciones visibles al estado de autenticación y al
   rol del usuario») · RF-RBAC-001 · RF-RBAC-002.

   Un solo sistema de diseño, tres densidades. Lo que cambia entre ellos no
   es la paleta ni la tipografía —son las mismas— sino cuánta información
   cabe por fila y qué se pone en primer plano:

     portal   marca y una salida. Nada más: quien mira todavía no tiene
              aplicación que navegar.
     jugador  seis destinos (RF-INV-008), buscador y HUD.
     consola  ocho herramientas, sin HUD de juego y con el rol a la vista.

   Ninguna regla de aquí introduce un color nuevo: todo sale de tokens.css.
   ======================================================================= */

/* --- 1. Portal de entrada ---------------------------------------------- */

/* En el portal, la barra no es una barra: es la primera línea de la escena.
   Pintar cromo sobre cromo dibujaría un rectángulo que no separa nada, así
   que se queda transparente y la marca flota sobre el degradado. */
/* En el portal la barra no es una barra: es la primera línea de la escena.
   Pintar cromo sobre cromo dibujaría un rectángulo que no separa nada. Fuera
   del portal —la pantalla de «sin acceso» usa esta misma variante— el fondo
   es claro, y ahí el cromo sí separa, así que solo se hace transparente
   dentro de `.portal`. */
.portal .cabecera--portal {
  background: transparent;
  padding-top: var(--e-3);
}

/* Sin navegación, la marca deja de competir por el espacio: se le da el peso
   que tiene en la pantalla de un juego, no el de un logotipo de barra. */
.cabecera--portal .cabecera__grupo-marca {
  gap: var(--e-4);
}

.cabecera--portal .cabecera__marca {
  font-family: var(--t-titulo-familia);
  font-size: var(--t-subtitulo-tam);
  font-weight: var(--t-titulo-peso);
  letter-spacing: 0.04em;
}

/* «¿Primera vez en el Nexo?» acompaña al botón y explica para qué es. En
   móvil desaparece: el botón se basta y la fila no tiene sitio. */
.cabecera__invitacion {
  font-family: var(--t-meta-familia);
  font-size: var(--t-meta-tam);
  color: var(--pista);
}

@media (max-width: 640px) {
  .cabecera__invitacion {
    display: none;
  }
}

/* --- 2. Distintivo de rol ---------------------------------------------- */

/* Quién eres cuando no eres solo jugador. Sigue la regla de las insignias de
   tokens.css: color saturado como TEXTO sobre el fondo tenue del mismo
   color, nunca relleno sólido con texto blanco. */
.distintivo-rol {
  display: inline-flex;
  align-items: center;
  padding: 0 var(--e-2);
  border-radius: var(--radio-pildora);
  font-family: var(--t-meta-familia);
  font-size: var(--t-meta-tam);
  font-weight: var(--t-cuerpo-peso-enfasis);
  line-height: 1.6;
  white-space: nowrap;
  background: var(--info-insignia);
  color: var(--info);
}

.distintivo-rol--moderador {
  background: var(--info-insignia);
  color: var(--info);
}

.distintivo-rol--administrador {
  background: var(--rareza-epica-insignia);
  color: var(--rareza-epica);
}

/* El super administrador se distingue del administrador a propósito: es el
   único rol que puede crear otros administradores (RF-RBAC-003) y el único
   cuya sesión exige doble factor. Verlo de un vistazo importa. */
.distintivo-rol--super-administrador {
  background: var(--advertencia-insignia);
  color: var(--advertencia);
}

/* Dentro del cromo el fondo tenue de la insignia no contrasta: ahí el
   distintivo se dibuja en negativo, con el borde marcando la forma. */
.cabecera .distintivo-rol {
  background: transparent;
  border: 1px solid currentcolor;
  color: var(--pista);
}

/* --- 3. Consola de operación ------------------------------------------- */

/* «Control» junto a la marca. No es un adorno: es la única señal permanente
   de que lo que se pulsa aquí afecta a otras personas. */
.cabecera__marca-sufijo {
  margin-left: var(--e-2);
  padding: 0 var(--e-2);
  border-radius: var(--radio-chico);
  background: var(--acento-relleno);
  color: var(--texto-sobre-color);
  font-family: var(--t-etiqueta-familia);
  font-size: var(--t-meta-tam);
  font-weight: var(--t-etiqueta-peso);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

/* Nueve destinos no caben con la separación de seis. La consola comprime la
   navegación —es su diferencia de densidad respecto al jugador— sin tocar el
   objetivo táctil, que sigue en 44 px de alto.

   Y se pliega antes. El armazón de jugador se pliega a los 900 px (860 hasta UX-GAME-6) porque sus
   seis destinos caben de sobra en 1024; los nueve de la consola no: el
   laboratorio los midió en **tres filas, 166 px de cabecera**, en las siete
   vistas de trastienda a 1024×768 — que es la resolución de una pantalla de
   portátil corriente, no un caso raro. Plegar no es esconder: los nueve
   siguen estando, a un toque.

   UX-R3.11 — el corte en 1200 se fijó midiendo a 1024 y no se volvió a mirar
   más arriba. La captura del barrido, con persona de super administración a
   1440, enseñó «AUDITORÍA» sola en una segunda fila. Medido en las cuatro
   combinaciones (altura real de `.cabecera`, 64 px = una fila):

     nueve destinos, MAYÚSCULAS + espaciado   684 px de texto
     nueve destinos, caja normal              548 px de texto

     ancho   sitio para la nav   MAYÚSCULAS   caja normal
     1600    —                   64 px        64 px
     1540    —                   64 px        64 px
     1520    —                   106 px       —
     1440    676 px              106 px       64 px
     1366    644 px              106 px       64 px
     1280    570 px              106 px       102 px

   Las mayúsculas cuestan 136 px, que es justo la diferencia entre caber y no
   caber en un portátil. Así que la consola se queda en mayúsculas donde hay
   sitio, baja a caja normal en la banda de portátil —donde la densidad importa
   más que el gesto— y se pliega cuando ya no cabe de ninguna manera. Ninguna
   de las tres opciones esconde un destino: la tercera lo deja a un toque. */

@media (max-width: 1365px) {
  .cabecera--consola .cabecera__nav--consola {
    display: none;
  }

  .cabecera--consola[data-nav-abierto='si'] .cabecera__nav--consola {
    display: flex;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    z-index: 25;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    padding: var(--e-2) var(--e-4) var(--e-4);
    background: var(--energia) bottom / 100% 2px no-repeat, var(--cromo);
    box-shadow: var(--sombra-3);
  }

  .cabecera--consola {
    position: relative;
  }

  .cabecera--consola .cabecera__alternar {
    display: inline-flex;
  }
}

.cabecera--consola .cabecera__grupo-marca {
  gap: var(--e-5);
}

.cabecera--consola .cabecera__nav--consola {
  gap: var(--e-4);
}

.cabecera--consola .cabecera__destino {
  font-size: var(--t-meta-tam);
  letter-spacing: 0.02em;
  text-transform: uppercase;
}

/* Banda de portátil: caben los nueve en una fila si se les quita el coste de
   las mayúsculas. Verificado a 1440 y a 1366.

   Va DESPUES de la regla de arriba a proposito: una consulta de medios no
   suma especificidad, asi que escrita antes perdia contra ella y no hacia
   nada — el laboratorio lo midio: seguia en 106 px. */
@media (max-width: 1540px) and (min-width: 1366px) {
  .cabecera--consola .cabecera__nav--consola {
    gap: var(--e-3);
  }

  /* UX-GAME-2 sumo el emblema a la marca (32 px + separacion) y los diez
     destinos del super administrador volvian a partirse en dos filas a
     1440. En esta banda la consola lleva la marca corta: emblema, «NB VI»
     y «Control». El nombre accesible no cambia (`aria-label`). */
  .cabecera--consola .cabecera__marca-larga {
    display: none;
  }

  .cabecera--consola .cabecera__marca-corta {
    display: inline;
  }

  /* Y las separaciones bajan un escalon: con los diez destinos del super
     administrador (Auditoria incluida) la fila sobraba por 16 px a 1440. */
  .cabecera--consola .cabecera__grupo-marca {
    gap: var(--e-4);
  }

  .cabecera--consola .cabecera__nav--consola {
    gap: var(--e-2);
  }

  .cabecera--consola .cabecera__destino {
    text-transform: none;
    letter-spacing: normal;
  }
}

/* B3 — «Comentarios» (la cola de comentarios reportados) entra en la consola:
   el administrador pasa a diez destinos y el super administrador a once. Por
   encima de la banda de portátil ya no cabían en una fila en mayúsculas: con
   diez la barra se partía en dos entre 1541 y ~1740 px (medido antes de este
   cambio), y con once hasta ~1900. En esa franja, las consolas con muchos
   destinos (nueve o más) usan la caja normal y las separaciones cortas de la
   banda de portátil; las que tienen pocos —el moderador ve cuatro— siguen en
   mayúsculas, porque les sobra sitio. Medido con el laboratorio (super
   administrador, administrador y moderador) a 1366, 1440, 1540, 1541, 1560,
   1580, 1599, 1600, 1620, 1700, 1800, 1919 y 1920 px: una fila de 64 px en
   todos. */
@media (min-width: 1541px) and (max-width: 1919px) {
  .cabecera--consola:has(.cabecera__destino:nth-child(9)) .cabecera__grupo-marca {
    gap: var(--e-4);
  }

  .cabecera--consola:has(.cabecera__destino:nth-child(9)) .cabecera__nav--consola {
    gap: var(--e-2);
  }

  .cabecera--consola:has(.cabecera__destino:nth-child(9)) .cabecera__destino {
    text-transform: none;
    letter-spacing: normal;
  }
}

/* Y justo por encima de la banda de portátil (1541–1599) los once destinos
   del super administrador todavía no caben con la marca larga: ahí llevan la
   corta, como en la banda de portátil. El nombre accesible no cambia. */
@media (min-width: 1541px) and (max-width: 1599px) {
  .cabecera--consola:has(.cabecera__destino:nth-child(9)) .cabecera__marca-larga {
    display: none;
  }

  .cabecera--consola:has(.cabecera__destino:nth-child(9)) .cabecera__marca-corta {
    display: inline;
  }
}

/* El filo de acento recorre toda la barra. Es la versión a lo ancho del filo
   superior de la tarjeta de entrada: mismo gesto, mismo gradiente, distinto
   sitio. Marca el modo sin gastar un color nuevo. */
.cabecera--consola {
  /* UX-GAME-2: el filo es el gradiente de energia, el mismo de las tarjetas
     y del destino activo; antes era el acento plano. */
  background: var(--energia) bottom / 100% 2px no-repeat, var(--cromo);
}

/* La salida al juego es secundaria, pero tiene que existir: un administrador
   también juega, y sin esto volvía escribiendo la URL a mano. */
.cabecera__salida {
  white-space: nowrap;
}

@media (max-width: 1440px) {
  /* Hasta la banda de portatil la salida va en el menu de cuenta, no en la
     fila: con ella, los diez destinos del super administrador se partian en
     dos filas a 1440 y 1366 (UX-GAME-6). */
  .cabecera__salida {
    display: none;
  }
}

/* --- 4. Aterrizaje de la consola --------------------------------------- */

/* §19: la consola es más compacta que la aplicación del jugador. La densidad
   no se consigue apretando la tipografía —que sería la misma peor leída— sino
   ensanchando el área útil y acortando el ritmo vertical: quien opera compara
   tablas y filtros, no contempla una portada. `.pagina` sigue poniendo el
   centrado y el relleno; esto solo ajusta las dos medidas que cambian. */
.consola {
  max-width: 1600px;
  display: flex;
  flex-direction: column;
  gap: var(--e-6);
}

.consola__rejilla {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: var(--e-4);
}

.consola__herramienta {
  display: flex;
  flex-direction: column;
  gap: var(--e-2);
  text-decoration: none;
  color: inherit;
}

/* El alcance del rol, al pie. Texto de lectura, no de interfaz: ancho medido
   en caracteres para que no se convierta en una línea de 140. */
.consola__alcance {
  max-width: 78ch;
  color: var(--tinta-2);
  line-height: var(--t-cuerpo-alto);
}

/* --- 5. Interrupción: sin permiso, sin sesión, servicio caído ----------- */

/* §17: un 403 no es contenido. Esta pantalla sustituye a la vista entera
   —y no se superpone— para que debajo no siga cargando datos que el
   servidor va a negar de todas formas. */
/* La pantalla de interrupción trae su propia barra de marca, así que aquí se
   descuenta su alto igual que en cualquier otra vista. Sobre `--fondo`, no
   sobre cromo: quien llega aquí no está en el portal, está dentro y se ha
   topado con una puerta. */
.interrupcion {
  min-height: calc(100vh - var(--cabecera-alto));
  display: grid;
  place-items: center;
  padding: var(--e-8) var(--e-4);
}

.interrupcion__tarjeta {
  width: min(520px, 100%);
  display: flex;
  flex-direction: column;
  gap: var(--e-3);
  padding: var(--e-8) var(--e-6);
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-radius: var(--radio);
  box-shadow: var(--sombra-2);
  text-align: center;
}

.interrupcion__titulo {
  font-family: var(--t-titulo-familia);
  font-size: var(--t-titulo-tam);
  line-height: var(--t-titulo-alto);
  color: var(--texto);
}

.interrupcion__detalle {
  max-width: 46ch;
  margin: 0 auto;
  color: var(--texto-2);
  line-height: var(--t-cuerpo-alto);
}

.interrupcion__salidas {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--e-3);
  padding-top: var(--e-3);
}

/* =======================================================================
   Lista negra de términos — UX-R3.3
   -----------------------------------------------------------------------
   RF-USR-002 / HU-ADM-002: los términos que el registro rechaza en un apodo.

   La vista existía y funcionaba, pero no había adoptado el sistema: un
   `<header>` con texto suelto, un `<input>` sin clase y dos botones que se
   pintaban con el estilo por omisión del navegador, porque sus clases
   (`acciones-termino`, `boton-eliminar`) no estaban definidas en ningún CSS
   del repositorio. Una lista de filas con acciones es un patrón que el kit ya
   sabe resolver; lo único que faltaba era la fila.
   ======================================================================= */

.lista-terminos {
  display: flex;
  flex-direction: column;
  gap: var(--e-2);
  margin: 0;
  padding: 0;
  list-style: none;
}

.lista-terminos__fila {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--e-3);
  padding: var(--e-2) var(--e-3);
  border: 1px solid var(--borde);
  border-radius: var(--radio);
  background: var(--superficie);
}

.lista-terminos__termino {
  min-width: 0;
  overflow-wrap: anywhere;
  font-family: var(--t-cuerpo-familia);
  font-weight: var(--t-cuerpo-peso-enfasis);
}

.lista-terminos__acciones {
  display: flex;
  gap: var(--e-2);
}

/* Formulario de una línea: un campo y su acción — UX-R3.3.

   `.fila` existe y reparte el ancho a partes iguales entre sus hijos
   (`.fila > * { flex: 1 1 280px }`), que es lo correcto para dos campos del
   mismo peso. Para «un campo y un botón» no lo es: el botón crecía hasta
   ocupar media pantalla. Aquí el campo se lleva el espacio y el botón mide lo
   que mide su texto, alineado con el control y no con la etiqueta. */
.formulario-en-linea {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--e-3);
}

.formulario-en-linea > .campo {
  flex: 1 1 260px;
  min-width: 0;
}

.formulario-en-linea > .boton {
  flex: 0 0 auto;
}

/* El campo de combate solo existe cuando hay combate — UX-R3.4.

   `.combate__campo` se lleva la franja `1fr` de la reja, que es la mayor
   parte de la ventana: CA-01 de HU-JUE-017 pide más del 80 % del alto útil
   para el área de juego, y eso está bien mientras haya área de juego. Sin
   partida cargada dejaba media pantalla de degradado vacío con la tarjeta del
   estado vacío huérfana debajo, casi en el borde inferior. La pantalla más
   importante del producto parecía rota justo en el estado en el que más gente
   la abre por error.

   Sin partida: el campo desaparece y el estado vacío ocupa su sitio, centrado.
   Con partida, nada de esto aplica y la reja vuelve a ser la de CA-01. */
.combate[data-sin-partida='si'] .combate__campo {
  display: none;
}

/* Y el hueco que dejaba pasa al estado vacío. Sin esto, la reja sigue
   reservando su franja `1fr` —las filas las fija la plantilla, no el
   contenido— y la tarjeta se quedaba pegada al borde inferior de la ventana,
   que es justo lo que se venía a arreglar. */
.combate[data-sin-partida='si'] {
  grid-template-rows: auto 1fr;
}

/* `.combate__sala` es una CAPA: `position: absolute; inset: auto 0 0 0`, es
   decir, pegada al borde inferior del campo, que es donde tiene que estar
   mientras se espera a que la sala se llene y el campo ya se ve detrás.

   Sin campo detrás no es una capa, es el contenido: vuelve al flujo de la
   reja, ocupa la franja `1fr` que dejó el campo y se centra en ella. Esto era
   lo que faltaba: ocultar el campo no bastaba, porque la tarjeta seguía
   anclada abajo y quedaba flotando sobre medio lienzo vacío. */
.combate[data-sin-partida='si'] .combate__sala {
  position: static;
  display: grid;
  place-items: center;
  align-content: center;
  min-height: 0;
}

.combate[data-sin-partida='si'] .combate__sala > .estado-vista {
  width: min(560px, 100%);
}

/* Fila de acciones que NO reparte el ancho — UX-R3.8.

   `.fila` existe y da `flex: 1 1 280px` a cada hijo, que es lo correcto para
   campos del mismo peso. Para una fila de botones no lo es: un solo botón
   crecía de lado a lado del contenedor. Esta variante los deja medir lo que
   mide su texto, que es lo que hace un botón. */
.fila--acciones > * {
  flex: 0 0 auto;
}

/* =========================================================================
   Consola de control integral
   =========================================================================
   Ocho secciones, cada una con sus paneles. El sello de estado de cada panel
   es la pieza clave del componente: dice, de un vistazo, si lo que hay
   debajo es un dato real, un hueco o un servicio caido. Sin el, un panel
   vacio y un panel que no pudo preguntar se ven igual -- que es justo la
   confusion que esta pantalla existe para evitar.
   ========================================================================= */

.consola-integral {
  display: flex;
  flex-direction: column;
  gap: var(--e-3);
}

.consola-integral__contrato {
  border-left: 3px solid var(--primaria);
  padding-left: var(--e-3);
  max-width: 78ch;
}

.consola-integral__seccion {
  margin-block-start: var(--e-6, 1.5rem);
}

.consola-integral__rejilla {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 22rem), 1fr));
  gap: var(--e-3);
  margin-block-start: var(--e-2);
}

/* Un panel con tabla necesita el ancho entero; uno con una cifra, no. */
.panel-admin {
  display: flex;
  flex-direction: column;
  gap: var(--e-2);
  min-width: 0;
}

.panel-admin:has(.tabla-envoltorio) {
  grid-column: 1 / -1;
}

.panel-admin__encabezado {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--e-2);
}

.panel-admin__textos {
  min-width: 0;
}

.panel-admin__cuerpo {
  min-width: 0;
}

.panel-admin__fuente {
  color: var(--texto-3);
  font-variant-numeric: var(--cifras-tabulares);
  margin-block-start: auto;
  overflow-wrap: anywhere;
}

/* --- El sello de estado -------------------------------------------------- */

.sello-estado {
  flex: 0 0 auto;
  border-radius: var(--radio-pildora);
  padding: 0.125rem var(--e-2);
  font-family: var(--t-etiqueta-familia);
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  white-space: nowrap;
  /* El color NO es la unica senal: el texto dice el estado con palabras.
     Quien no distingue rojo de verde lee EN LINEA o SERVICIO DEGRADADO. */
  background: var(--pista);
  color: var(--texto-2);
}

.sello-estado[data-estado='ok'] {
  background: var(--exito-insignia);
  color: var(--exito);
}

.sello-estado[data-estado='malo'] {
  background: var(--error-insignia);
  color: var(--error);
}

.sello-estado[data-estado='aviso'] {
  background: var(--advertencia-insignia);
  color: var(--advertencia);
}

.sello-estado[data-estado='neutro'] {
  background: var(--superficie-2);
  color: var(--texto-3);
}

/* --- Indicadores --------------------------------------------------------- */

.indicador {
  display: flex;
  flex-direction: column;
  gap: var(--e-1);
}

.indicador__valor {
  font-family: var(--fuente-titulo);
  font-size: 2.25rem;
  font-weight: 700;
  line-height: 1.1;
  font-variant-numeric: var(--cifras-tabulares);
  color: var(--texto);
}

.indicador__etiqueta {
  font-family: var(--t-etiqueta-familia);
  font-size: var(--t-etiqueta-tam);
  font-weight: var(--t-etiqueta-peso);
  color: var(--texto-2);
}

.indicador__nota {
  color: var(--texto-3);
}

/* --- Tablas dentro de un panel ------------------------------------------- */

.tabla-envoltorio {
  /* Una tabla de seis columnas no cabe en un telefono. Se desplaza dentro de
     su panel en vez de estirar la pagina entera. */
  overflow-x: auto;
  max-width: 100%;
}

.tabla-envoltorio .tabla,
.tabla-envoltorio table {
  width: 100%;
  /* Tambien las tablas `<table>` de las vistas (historial, auditoria): sin
     ancho minimo se apretaban a 375 px hasta partir cada palabra en una
     linea, en vez de desplazarse (UX-GAME-7). */
  min-width: 38rem;
}

/* --- Buscador y paginacion del directorio -------------------------------- */

.consola-integral__buscador {
  display: flex;
  gap: var(--e-2);
  flex-wrap: wrap;
}

.consola-integral__buscador .campo__entrada {
  flex: 1 1 14rem;
  min-width: 0;
}

.consola-integral__paginacion {
  display: flex;
  gap: var(--e-2);
  margin-block-start: var(--e-2);
}

/* --- Usuarios: filtros, estados y registros por dia (HU-USR-008) ---------
   Los filtros del directorio y el periodo de los indicadores van en una fila
   que se parte en varias cuando no cabe; cada campo con su etiqueta visible.
   El grafico es un SVG sin libreria: barras en la primaria de relleno, el
   valor de cada una en su <title> y la misma serie en una tabla debajo. */

.consola-integral__filtros {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--e-2) var(--e-3);
}

.consola-integral__filtros > .campo,
.consola-integral__filtros > .desplegable {
  flex: 1 1 10rem;
  min-width: 0;
}

/* El desplegable separa su etiqueta mas que el campo: en la misma fila, las
   etiquetas tienen que quedar a la misma altura. */
.consola-integral__filtros > .desplegable {
  gap: var(--e-1);
}

/* Dentro del buscador del directorio, los filtros van en su propia linea. */
.consola-integral__buscador > .consola-integral__filtros {
  flex: 1 1 100%;
}

.consola-integral__estados {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 9rem), 1fr));
  gap: var(--e-3);
}

.grafico-registros {
  display: block;
  width: 100%;
  height: 160px;
}

.grafico-registros__barra {
  fill: var(--primaria-relleno);
}

.grafico-registros__barra:hover {
  fill: var(--acento-relleno);
}

.grafico-registros__base {
  stroke: var(--borde);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}

/* Primer y ultimo dia de la serie, debajo de las barras. */
.grafico-registros__eje {
  display: flex;
  justify-content: space-between;
  gap: var(--e-2);
}

.consola-integral__datos-grafico > summary {
  cursor: pointer;
  color: var(--primaria);
}

/* =======================================================================
   UXC-1 — Núcleo del héroe: prototipo, StatBlock, HeroCard y su estado
   -----------------------------------------------------------------------
   §6.1.1 fija ocho prototipos (Tabla 6) y hasta aqui todos eran el mismo
   circulo con una inicial. El simbolo del prototipo (sprite) sustituye a la
   inicial cuando el prototipo se conoce, coloreado por familia; el nombre va
   SIEMPRE escrito al lado, asi que el color solo refuerza.
   ======================================================================= */

.marco-heroe__simbolo {
  width: 46%;
  height: 46%;
  color: var(--texto-2);
}

.marco-heroe__retrato[data-prototipo^='guerrero'] .marco-heroe__simbolo {
  color: var(--primaria);
}
.marco-heroe__retrato[data-prototipo^='mago'] .marco-heroe__simbolo {
  color: var(--acento-relleno);
}
.marco-heroe__retrato[data-prototipo^='picaro'] .marco-heroe__simbolo {
  color: var(--advertencia);
}
.marco-heroe__retrato[data-prototipo='chaman'] .marco-heroe__simbolo,
.marco-heroe__retrato[data-prototipo='medico'] .marco-heroe__simbolo {
  color: var(--exito);
}

/* --- StatBlock: las seis cifras de la Tabla 6 --------------------------- */

.stat-block {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
  gap: var(--e-2) var(--e-3);
  margin: 0;
}

.stat-block--compacto {
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--e-1) var(--e-3);
}

.stat-block__cifra {
  display: grid;
  gap: 2px;
  min-width: 0;
}

.stat-block__etiqueta {
  display: inline-flex;
  align-items: center;
  gap: var(--e-1);
  font-family: var(--t-meta-familia);
  font-size: var(--t-meta-tam);
  color: var(--tinta-3);
}

.stat-block__icono {
  width: 14px;
  height: 14px;
  flex: none;
}

.stat-block__valor {
  margin: 0;
  font-family: var(--fuente-titulo);
  font-size: 1.125rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--tinta);
}

.stat-block--compacto .stat-block__valor {
  font-size: 1rem;
}

.stat-block__seccion {
  display: grid;
  gap: var(--e-2);
}

.stat-block__titulo {
  margin: 0;
  font-family: var(--t-subtitulo-familia);
  font-size: 1rem;
  font-weight: 700;
  color: var(--tinta);
}

.stat-block__nota {
  margin: 0;
}

/* --- Estado del heroe o del objeto --------------------------------------- */

.sello-estado-heroe {
  justify-self: start;
  width: fit-content;
  font-weight: 600;
}

.sello-estado-heroe__icono {
  width: 14px;
  height: 14px;
  flex: none;
}

/* Equipado: tono informativo, no de exito — no es un logro, es donde esta. */
.distintivo--equipado {
  background: var(--info-insignia);
  color: var(--primaria-relleno);
}

/* Bloqueado: neutro y legible. Las variantes `--bloqueada` y
   `--no-disponible` del kit ponen `--texto-3` sobre `--pista`, que no llega a
   4,5:1 (axe con datos, UXC-1); estas dos si. */
.distintivo--bloqueado {
  background: var(--superficie-2);
  border-color: var(--borde);
  color: var(--texto-2);
}

/* No puede combatir: aviso con solucion, tono de advertencia. */
.distintivo--aviso {
  background: var(--advertencia-insignia);
  color: var(--advertencia);
}

/* --- HeroCard ------------------------------------------------------------ */

.hero-grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: var(--e-4);
}

.hero-grid__celda {
  min-width: 0;
  display: grid;
}

.hero-card {
  display: grid;
  gap: var(--e-3);
  align-content: start;
  min-width: 0;
}

.hero-card__cabecera {
  display: flex;
  align-items: center;
  gap: var(--e-3);
  min-width: 0;
}

.hero-card .marco-heroe {
  flex: none;
  width: auto;
}

.hero-card .marco-heroe__retrato {
  width: 64px;
  height: 64px;
}

.hero-card .marco-heroe__imagen {
  width: 52px;
  height: 52px;
}

.hero-card__identidad {
  display: grid;
  gap: 2px;
  min-width: 0;
}

.hero-card__nombre {
  margin: 0;
  font-family: var(--t-subtitulo-familia);
  font-size: var(--t-subtitulo-tam);
  font-weight: 700;
  line-height: 1.2;
  color: var(--tinta);
  overflow-wrap: anywhere;
}

.hero-card__prototipo {
  margin: 0;
  display: inline-flex;
  align-items: center;
  gap: var(--e-1);
  font-family: var(--t-etiqueta-familia);
  font-size: var(--t-etiqueta-tam);
  font-weight: 600;
  color: var(--tinta-2);
}

.hero-card__icono-prototipo {
  width: 16px;
  height: 16px;
  flex: none;
}

.hero-card__equipo {
  margin: 0;
}

/* UXC-9 — nivel y experiencia (inventario 1.5.0): el texto dice cuánto hay y
   cuánto falta; la barra solo lo acompaña. */
.hero-card__progreso {
  display: grid;
  gap: 4px;
}

.hero-card__nivel {
  margin: 0;
}

.hero-card__experiencia {
  display: block;
  height: 6px;
  overflow: hidden;
  border-radius: var(--radio-pildora);
  background: var(--pista);
}

.hero-card__experiencia-relleno {
  display: block;
  height: 100%;
  background: var(--acento-relleno);
}

.hero-card__acciones {
  display: flex;
  flex-wrap: wrap;
  gap: var(--e-2);
}

/* Bloqueado (en subasta): el retrato se apaga; el texto no, para que siga
   cumpliendo contraste. El sello dice por que. */
.hero-card[data-estado='BLOQUEADO'] .marco-heroe__retrato {
  filter: grayscale(1);
  opacity: 0.7;
}

/* =======================================================================
   UXC-2 — Combate a pantalla completa, barra de mando y lo que pasa
   -----------------------------------------------------------------------
   §7.6: «el area de juego debe ocupar mas del 80 % de la pantalla. Los
   menus pueden estar en la parte inferior o lateral. La barra de
   informacion debe estar en la parte superior, con la vida de los
   participantes y el turno». Con la barra de la aplicacion (64 px), un HUD
   de dos filas y los controles en tarjeta, el campo medía el 56 % en
   1366x768. En modo combate (la vista pone `data-modo` en el body cuando hay
   partida) la barra de la aplicacion se retira —la salida vive en el HUD—, el
   HUD es una sola franja y los controles otra: el campo pasa del 80 %. Lo
   mide `tests/visual/campo-de-combate.spec.js`.
   ======================================================================= */

.combate__salir {
  display: none;
}

body[data-modo='combate'] [data-cabecera-app] {
  display: none;
}

body[data-modo='combate'] .combate {
  height: 100dvh;
  min-height: 0;
  padding: 0;
  gap: 0;
  grid-template-rows: auto minmax(0, 1fr) auto;
}

body[data-modo='combate'] .combate__hud {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--e-1) var(--e-4);
  padding: 4px var(--e-4);
  border-width: 0 0 1px;
  border-radius: 0;
  box-shadow: none;
  max-height: 30vh;
}

body[data-modo='combate'] .combate__salir {
  display: inline-flex;
  align-items: center;
  gap: var(--e-1);
  min-height: 36px;
  padding: 0 var(--e-3);
  border: 1px solid color-mix(in srgb, var(--pista) 40%, transparent);
  border-radius: var(--radio-pildora);
  color: var(--tinta);
  font-family: var(--t-etiqueta-familia);
  font-size: var(--t-meta-tam);
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
}

body[data-modo='combate'] .combate__salir:hover {
  border-color: var(--enlace-sobre-cromo);
  color: var(--tinta);
}

body[data-modo='combate'] .combate__hud-titulo {
  flex-wrap: nowrap;
  gap: var(--e-2);
}

/* El titulo se sigue leyendo (h1), pero no ocupa ancho del HUD: el turno y
   las barras dicen donde se esta. Mismo patron que `.solo-lectores`. */
body[data-modo='combate'] .combate__titulo,
body[data-modo='combate'] .conexion__prefijo {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

.combate__hud-estado {
  display: flex;
  align-items: center;
  gap: var(--e-2);
  margin-left: auto;
}

/* En la sala de espera (sin partida) el HUD es una sola franja: titulo a un
   lado, canal y chat al otro. Las barras aun no existen. */
body:not([data-modo='combate']) .combate__hud {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--e-2) var(--e-4);
}

.combate__vidas:empty {
  display: none;
}

/* Las barras, compactas: nombre y valor arriba, la pista debajo. */
body[data-modo='combate'] .combate__vidas {
  display: flex;
  flex: 1 1 360px;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--e-1) var(--e-4);
  min-width: 0;
}

body[data-modo='combate'] .combate__vidas .barra-vida {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto auto;
  grid-template-areas:
    'nombre efectos etiqueta valor'
    'pista pista pista pista';
  align-items: center;
  gap: 2px var(--e-2);
  flex: 1 1 110px;
  max-width: 360px;
  min-width: 0;
}

/* Con cuatro barras o mas (partidas de hasta seis), la etiqueta «IA · Equipo
   2» se sigue leyendo pero deja de ocupar ancho: el campo ya separa los dos
   bandos y los botones de ataque solo ofrecen rivales. */
body[data-modo='combate'] .combate__vidas:has(> .barra-vida:nth-child(4)) .barra-vida__etiqueta {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

body[data-modo='combate'] .combate__vidas .barra-vida__nombre {
  grid-area: nombre;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--t-etiqueta-tam);
}

body[data-modo='combate'] .combate__vidas .barra-vida__etiqueta {
  grid-area: etiqueta;
}

body[data-modo='combate'] .combate__vidas .barra-vida .efectos {
  grid-area: efectos;
}

body[data-modo='combate'] .combate__vidas .barra-vida__pista {
  grid-area: pista;
  height: 8px;
}

body[data-modo='combate'] .combate__vidas .barra-vida__valor {
  grid-area: valor;
  min-width: 0;
}

body[data-modo='combate'] .combate__campo {
  border-width: 0;
  border-radius: 0;
}

body[data-modo='combate'] .combate__controles {
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  gap: var(--e-2) var(--e-4);
  /* A la derecha queda el hueco del asistente flotante, que esta en todas
     las vistas (RNF-DIS-002) y no debe tapar el registro. */
  padding: 6px 88px 6px var(--e-4);
  border-width: 1px 0 0;
  border-radius: 0;
  box-shadow: none;
}

/* --- La barra de mando -------------------------------------------------- */

.combate__mando {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--e-2) var(--e-4);
}

.combate__grupo {
  display: grid;
  gap: 2px;
  align-content: end;
}

.combate__grupo-cabeza {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0 var(--e-2);
}

.combate__grupo-titulo {
  margin: 0;
  font-family: var(--t-etiqueta-familia);
  font-size: var(--t-meta-tam);
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--tinta-2);
}

.combate__motivo {
  margin: 0;
  font-size: var(--t-meta-tam);
  line-height: 1.3;
  color: var(--tinta-3);
}

body[data-modo='combate'] .combate__acciones {
  flex-wrap: nowrap;
  justify-content: flex-start;
  gap: var(--e-2);
}

/* La carta de accion, compacta en la barra de mando: icono a la izquierda,
   nombre y coste a la derecha. 56 px de alto. */
body[data-modo='combate'] .accion-combate {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  grid-template-rows: auto auto;
  align-items: center;
  column-gap: var(--e-2);
  row-gap: 2px;
  min-width: 112px;
  max-width: 176px;
  min-height: 52px;
  padding: var(--e-1) var(--e-3);
  text-align: left;
}

body[data-modo='combate'] .accion-combate__insignias,
body[data-modo='combate'] .accion-combate__detalle,
body[data-modo='combate'] .accion-combate__coste {
  grid-column: 2;
}

body[data-modo='combate'] .accion-combate__icono {
  grid-row: 1 / span 2;
}

body[data-modo='combate'] .accion-combate__etiqueta {
  text-align: left;
}

.accion-combate__detalle {
  color: var(--texto-2);
  line-height: 1.2;
}

/* Coste y carga con su icono: «⚡2 ⟳1», como en las cartas de un juego. */
.accion-combate__insignias {
  display: inline-flex;
  gap: var(--e-2);
}

.accion-combate__insignia {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  font-family: var(--t-etiqueta-familia);
  font-size: var(--t-meta-tam);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--texto-2);
}

.accion-combate__insignia-icono {
  width: 12px;
  height: 12px;
}

/* UXC-9 — la carga que falta: «faltan N» con el reloj, en ámbar (el texto
   ya lo dice; el color acompaña). */
.accion-combate__insignia--en-carga {
  color: var(--advertencia);
}

/* La especial deshabilitada sigue legible: se atenua el icono, no el texto
   (contraste); el motivo esta escrito al lado del titulo del grupo. */
.accion-combate--especial.accion-combate--fuera-de-turno {
  border-style: dashed;
}

/* --- Poder (PowerMeter) ------------------------------------------------ */

.combate__poder {
  display: grid;
  align-content: end;
}

.combate__poder[hidden] {
  display: none;
}

.medidor-poder {
  display: grid;
  gap: 2px;
  min-width: 112px;
}

.medidor-poder__cabeza {
  display: flex;
  align-items: center;
  gap: var(--e-1);
  margin: 0;
  font-family: var(--t-etiqueta-familia);
  font-size: var(--t-meta-tam);
  font-weight: 700;
  color: var(--tinta);
}

.medidor-poder__icono {
  width: 16px;
  height: 16px;
  color: var(--primaria);
}

.medidor-poder__valor {
  margin-left: auto;
  font-variant-numeric: tabular-nums;
}

/* Revision del modo jugador (6-oct) — «Poder 10/10 no suma ni resta»: el
   movimiento que mando el servidor, junto a la cifra. El texto lleva el signo,
   asi que el color solo acompana. Sin movimiento con `prefers-reduced-motion`. */
.medidor-poder__cambio {
  padding: 0 var(--e-1);
  border-radius: var(--radio);
  font-variant-numeric: tabular-nums;
  line-height: 1.4;
}

.medidor-poder__cambio[data-signo='negativo'] {
  color: var(--advertencia);
  background: color-mix(in srgb, var(--advertencia) 14%, transparent);
}

.medidor-poder__cambio[data-signo='positivo'] {
  color: var(--exito);
  background: color-mix(in srgb, var(--exito) 14%, transparent);
}

@media (prefers-reduced-motion: no-preference) {
  .medidor-poder__cambio {
    animation: poder-cambia 1.8s var(--curva-salida) both;
  }
}

@keyframes poder-cambia {
  0% {
    opacity: 0;
    transform: translateY(4px);
  }

  15%,
  70% {
    opacity: 1;
    transform: none;
  }

  100% {
    opacity: 0.35;
  }
}

/* La barra propia lleva «Tu» en su etiqueta y un borde que la distingue de la
   del rival (revision del 6-oct: el contragolpe parecia dano propio). */
.barra-vida[data-propia='true'] .barra-vida__nombre {
  font-weight: 700;
}

.barra-vida[data-propia='true'] .barra-vida__etiqueta {
  color: var(--enlace-sobre-cromo);
}

.medidor-poder__pista {
  display: flex;
  gap: 2px;
}

.medidor-poder__segmento {
  flex: 1 1 0;
  height: 8px;
  border-radius: 2px;
  background: var(--pista);
  border: 1px solid var(--borde);
}

.medidor-poder__segmento--lleno {
  background: var(--primaria);
  border-color: var(--primaria);
}

/* Sin seguimiento: la capacidad, rayada, para que no se lea como «lleno». */
.medidor-poder--sin-seguimiento .medidor-poder__segmento {
  background: repeating-linear-gradient(
    45deg,
    var(--pista),
    var(--pista) 2px,
    var(--superficie) 2px,
    var(--superficie) 4px
  );
}

.medidor-poder__nota {
  margin: 0;
}

/* --- Registro del combate (CombatLog) ------------------------------------ */

/* Revisión del modo jugador del 6-oct, punto 19: el registro ya no ocupa una
   columna de la barra de mando (en ese hueco no se leía). Queda un botón
   «Historial» al final de la barra que lo abre encima; ver «La vivencia del
   combate» más abajo. */
.combate__registro {
  position: relative;
  display: flex;
  align-items: flex-end;
  align-self: flex-end;
  flex: 0 0 auto;
  min-width: 0;
  margin-left: auto;
}

/* Con tres rivales o mas, las cartas de ataque se estrechan: el nombre del
   rival cabe en dos lineas y la barra de mando sigue en una sola franja. */
body[data-modo='combate'] .combate__acciones:has(> .accion-combate:nth-child(3)) .accion-combate {
  min-width: 96px;
  max-width: 132px;
}

body[data-modo='combate'] .combate__acciones--especiales .accion-combate {
  max-width: 164px;
}

.registro-combate {
  height: 50px;
  overflow-y: auto;
  border: 1px solid var(--borde);
  border-radius: var(--radio-chico);
  background: var(--superficie-2);
  padding: 2px var(--e-2);
}

body:not([data-modo='combate']) .registro-combate {
  height: 96px;
}

.registro-combate__lista {
  display: grid;
  gap: 2px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.registro-combate__linea {
  display: flex;
  align-items: flex-start;
  gap: var(--e-1);
  font-size: var(--t-meta-tam);
  line-height: 1.25;
  color: var(--texto);
}

.registro-combate__icono {
  width: 14px;
  height: 14px;
  margin-top: 2px;
  flex: none;
  color: var(--texto-2);
}

.registro-combate__linea--critico .registro-combate__icono,
.registro-combate__linea--dano .registro-combate__icono,
.registro-combate__linea--caida .registro-combate__icono {
  color: var(--error);
}

.registro-combate__linea--curacion .registro-combate__icono {
  color: var(--exito);
}

.registro-combate__linea--mitigado .registro-combate__icono {
  color: var(--info);
}

.registro-combate__linea--critico {
  font-weight: 700;
}

/* --- Efectos activos (EffectChip) --------------------------------------- */

.efectos {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 2px;
}

.efecto {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  padding: 1px var(--e-1);
  border-radius: var(--radio-pildora);
  background: var(--info-insignia);
  color: var(--info);
  font-size: 0.75rem;
  font-weight: 600;
  line-height: 1.3;
}

.efecto__icono {
  width: 12px;
  height: 12px;
}

.efecto--compacto {
  padding: 1px 3px;
}

.campo__puesto .efectos {
  justify-content: center;
  max-width: 140px;
}

/* --- La cifra del golpe (DamageCallout) --------------------------------- */

.campo__puesto {
  /* El impacto se coloca respecto al heroe. */
  isolation: isolate;
}

.impacto {
  position: absolute;
  left: 50%;
  bottom: 100%;
  display: grid;
  justify-items: center;
  transform: translate(-50%, -4px);
  pointer-events: none;
  font-family: var(--fuente-titulo);
  font-weight: 700;
  line-height: 1;
  white-space: nowrap;
  text-shadow: 0 2px 6px color-mix(in srgb, var(--cromo) 90%, transparent);
  z-index: 2;
}

.impacto__cifra {
  font-size: 2rem;
  color: var(--texto-sobre-color);
}

.impacto__etiqueta {
  margin-top: 2px;
  padding: 2px var(--e-2);
  border-radius: var(--radio-pildora);
  font-family: var(--t-etiqueta-familia);
  font-size: 0.75rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  background: var(--cromo);
  color: var(--texto-sobre-color);
}

.impacto--critico .impacto__cifra {
  font-size: 2.5rem;
}

.impacto--critico .impacto__etiqueta {
  background: var(--error);
}

.impacto--curacion .impacto__etiqueta {
  background: var(--exito);
}

.impacto--mitigado .impacto__etiqueta {
  background: var(--info);
}

@media (prefers-reduced-motion: no-preference) {
  .impacto {
    animation: impacto-sube 1.6s var(--curva-salida) both;
  }
}

@keyframes impacto-sube {
  0% {
    opacity: 0;
    transform: translate(-50%, 8px) scale(0.9);
  }
  12% {
    opacity: 1;
    transform: translate(-50%, -4px) scale(1.08);
  }
  80% {
    opacity: 1;
    transform: translate(-50%, -18px) scale(1);
  }
  100% {
    opacity: 0;
    transform: translate(-50%, -26px) scale(1);
  }
}

/* --- Por debajo del minimo ------------------------------------------------ */

@media (max-width: 767px), (max-height: 520px) {
  body[data-modo='combate'] .combate {
    height: auto;
    min-height: 100dvh;
    grid-template-rows: auto auto auto;
  }

  body[data-modo='combate'] .combate__controles {
    padding-right: var(--e-4);
    padding-bottom: 88px;
  }

  body[data-modo='combate'] .combate__acciones {
    flex-wrap: wrap;
  }

  body[data-modo='combate'] .combate__vidas .barra-vida {
    max-width: none;
  }
}

/* La pildora del canal con su «Reintentar» al lado. */
.conexion__reintentar {
  flex: none;
}

/* El enlace al chat de la sala, en el HUD, no crece. */
.combate__chat {
  flex: none;
  white-space: nowrap;
}

/* Las especiales: su cabecera lleva el motivo de por que no se pueden usar. */
.combate__grupo--especiales {
  min-width: 0;
}

.combate__registro-cuerpo {
  min-width: 0;
}

/* =========================================================================
   La vivencia del combate — revisión del modo jugador del 6-oct
   (puntos 15, 17, 19 y 20)
   -------------------------------------------------------------------------
   15 · el chat grupal en la misma página: panel lateral en escritorio y hoja
        inferior en móvil. Nunca una pestaña nueva.
   17 · la cuenta atrás del comienzo: 5, 4, 3, 2, 1, ¡COMBATE!
   19 · lo que pasa, grande en el centro del campo, y la cifra con su unidad
        («−4 VIDA», «−2 PODER»); el historial detrás de un botón.
   20 · el desenlace con los créditos fila a fila.
   Toda animación es adorno: con `prefers-reduced-motion` se ve lo mismo,
   quieto.
   ========================================================================= */

/* --- 15 · Chat grupal ---------------------------------------------------- */

.chat-grupal {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  z-index: 35;
  display: flex;
  flex-direction: column;
  width: min(380px, 100vw);
  background: var(--superficie);
  color: var(--texto);
  border-left: 1px solid color-mix(in srgb, var(--primaria) 35%, transparent);
  box-shadow: var(--sombra-3);
}

/* En la sala de espera la barra de la aplicación sigue arriba: el panel
   empieza debajo y sus menús (cuenta, campana) se abren por encima de él. */
body:not([data-modo='combate']) .chat-grupal {
  top: var(--cabecera-alto);
  z-index: 25;
}

.chat-grupal__cabecera {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--e-2) var(--e-3);
  padding: var(--e-3) var(--e-4);
  background:
    var(--energia) bottom left / 100% 2px no-repeat,
    var(--cromo);
  --tinta: var(--texto-sobre-color);
  --tinta-2: var(--pista);
  --tinta-3: var(--pista);
  color: var(--tinta);
}

.chat-grupal__titulo {
  flex: 1 1 auto;
  margin: 0;
  font-family: var(--fuente-titulo);
  font-size: var(--t-subtitulo-tam);
  font-weight: 700;
  letter-spacing: 0.02em;
  color: var(--texto-sobre-color);
}

.chat-grupal__conversacion {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
}

/* El hilo llena el panel: la altura fija del chat de página no aplica aquí. */
.chat-grupal__mensajes .conversacion__mensajes {
  flex: 1 1 0;
  height: auto;
  min-height: 0;
}

.chat-grupal__mensajes .conversacion__estado > .estado-vista {
  min-height: 160px;
}

/* Cuántos mensajes nuevos hay con el panel cerrado. Oculto al lector: el
   nombre accesible del botón ya lo dice («Chat grupal, 2 mensajes nuevos»). */
.combate__chat-nuevos {
  display: inline-grid;
  place-items: center;
  min-width: 1.25rem;
  height: 1.25rem;
  margin-left: var(--e-2);
  padding: 0 var(--e-1);
  border-radius: var(--radio-pildora);
  background: var(--error);
  color: var(--texto-sobre-color);
  font-size: 0.75rem;
  font-weight: 700;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

/* Escritorio: el combate se estrecha y le deja sitio al panel, así no tapa el
   campo ni las barras y se sigue jugando con él abierto. El asistente
   flotante se corre a su izquierda, al hueco que la barra de mando ya le
   reserva: ni tapa el redactor ni cae encima de una acción. */
@media (min-width: 769px) {
  body[data-chat-grupal='abierto'] .combate {
    margin-right: min(380px, 100vw);
  }

  body[data-chat-grupal='abierto'] .chatbot-flotante,
  body[data-chat-grupal='abierto'] .chatbot-ventana {
    right: calc(min(380px, 100vw) + var(--e-6));
  }
}

/* Móvil: hoja inferior casi a pantalla completa, por encima del asistente. */
@media (max-width: 768px) {
  .chat-grupal,
  body:not([data-modo='combate']) .chat-grupal {
    top: auto;
    left: 0;
    z-index: 35;
    width: 100%;
    height: min(88dvh, 680px);
    border-left: 0;
    border-top: 1px solid color-mix(in srgb, var(--primaria) 35%, transparent);
    border-radius: var(--radio) var(--radio) 0 0;
  }
}

/* --- 17 · Cuenta atrás --------------------------------------------------- */

.presentacion__cuenta {
  margin: 0;
  font-family: var(--fuente-titulo);
  font-size: clamp(4.5rem, 12vw, 8rem);
  font-weight: 700;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  color: var(--texto-sobre-color);
  text-shadow: 0 0 28px color-mix(in srgb, var(--primaria) 75%, transparent);
}

.presentacion__cuenta[data-final='si'] {
  font-size: clamp(3rem, 9vw, 6rem);
  letter-spacing: 0.06em;
  color: var(--enlace-sobre-cromo);
  text-shadow: 0 0 32px color-mix(in srgb, var(--acento-relleno) 80%, transparent);
}

@media (prefers-reduced-motion: no-preference) {
  /* Dos nombres de animación que se alternan: así cada número la empieza de
     cero (el texto cambia en el mismo nodo). */
  .presentacion__cuenta[data-tic='a'] {
    animation: cuenta-atras-a 1s var(--curva-salida) both;
  }

  .presentacion__cuenta[data-tic='b'] {
    animation: cuenta-atras-b 1s var(--curva-salida) both;
  }
}

@keyframes cuenta-atras-a {
  0% {
    opacity: 0;
    transform: scale(1.5);
  }
  22% {
    opacity: 1;
    transform: scale(1);
  }
  100% {
    opacity: 0.9;
    transform: scale(0.94);
  }
}

@keyframes cuenta-atras-b {
  0% {
    opacity: 0;
    transform: scale(1.5);
  }
  22% {
    opacity: 1;
    transform: scale(1);
  }
  100% {
    opacity: 0.9;
    transform: scale(0.94);
  }
}

@media (max-height: 600px), (max-width: 599px) {
  .presentacion__cuenta {
    font-size: 3.5rem;
  }

  .presentacion__cuenta[data-final='si'] {
    font-size: 2.75rem;
  }
}

/* --- 19 · Lo que pasa, en grande ----------------------------------------- */

/* Como un subtítulo: centrado, justo encima de la barra de mando (la vista
   anota su alto en `--hueco-del-mando`). El centro del campo es de los héroes
   y de sus cifras; ahí no se tapa nada. Por debajo de la presentación y del
   desenlace, y sin recibir clics: dura dos segundos. */
.combate__anuncio {
  position: fixed;
  bottom: calc(var(--hueco-del-mando, 112px) + var(--e-3));
  left: 50%;
  z-index: 15;
  width: max-content;
  max-width: min(640px, calc(100vw - 2 * var(--e-6)));
  transform: translateX(-50%);
  pointer-events: none;
  text-align: center;
}

@media (min-width: 769px) {
  /* Con el chat abierto, el centro es el del campo que queda a la vista. */
  body[data-chat-grupal='abierto'] .combate__anuncio {
    left: calc((100vw - min(380px, 100vw)) / 2);
  }
}

.combate__anuncio-texto {
  margin: 0;
  padding: var(--e-3) var(--e-5);
  border: 1px solid color-mix(in srgb, var(--primaria) 55%, transparent);
  border-left-width: 4px;
  border-radius: var(--radio);
  background: color-mix(in srgb, var(--cromo) 90%, transparent);
  box-shadow: var(--sombra-3);
  color: var(--texto-sobre-color);
  font-family: var(--fuente-titulo);
  font-size: var(--t-subtitulo-tam);
  font-weight: 700;
  line-height: 1.25;
  text-wrap: balance;
}

/* El tono va en el filo y en el brillo, nunca solo en el color del texto: la
   frase ya dice qué pasó («crítico», «esquiva», «cura»). */
.combate__anuncio-texto--critico {
  border-color: var(--error);
  box-shadow:
    var(--sombra-3),
    0 0 24px color-mix(in srgb, var(--error) 45%, transparent);
}

.combate__anuncio-texto--curacion {
  border-color: var(--exito);
}

.combate__anuncio-texto--mitigado {
  border-color: var(--info);
}

.combate__anuncio-texto--fallo {
  border-color: var(--pista);
}

.combate__anuncio-texto--caida {
  border-color: var(--advertencia);
}

.combate__anuncio-texto--turno {
  border-color: var(--enlace-sobre-cromo);
  font-size: var(--t-titulo-tam);
  letter-spacing: 0.04em;
}

@media (prefers-reduced-motion: no-preference) {
  .combate__anuncio-texto {
    animation: anuncio-del-combate 2s var(--curva-salida) both;
  }
}

@keyframes anuncio-del-combate {
  0% {
    opacity: 0;
    transform: translateY(10px) scale(0.96);
  }
  12% {
    opacity: 1;
    transform: none;
  }
  82% {
    opacity: 1;
  }
  100% {
    opacity: 0;
  }
}

/* Sin campo (móvil) la barra de mando no está abajo de la ventana: el anuncio
   va a media altura y en letra de cuerpo, para no tapar las barras. */
@media (max-width: 767px), (max-height: 520px) {
  .combate__anuncio {
    top: 38%;
    bottom: auto;
    transform: translate(-50%, -50%);
  }

  .combate__anuncio-texto,
  .combate__anuncio-texto--turno {
    font-size: var(--t-cuerpo-tam);
  }
}

/* La cifra del golpe lleva su unidad: «−4 VIDA», «−2 PODER», «+3 VIDA». */
.impacto__unidad {
  margin-left: 0.15em;
  font-size: 0.5em;
  letter-spacing: 0.06em;
  vertical-align: 0.35em;
}

/* El gasto de poder sobre el propio héroe: azul de energía, no rojo de daño. */
.impacto--poder .impacto__cifra {
  font-size: 1.6rem;
  color: var(--enlace-sobre-cromo);
}

/* --- 19 · El historial, encima de la barra -------------------------------- */

/* Cerrado se oculta a la vista, no al lector de pantalla: el registro sigue
   siendo una región viva y lo que pasa se anuncia una vez. Sin desplazamiento
   propio mientras está cerrado (no hay nada que desplazar que se vea). */
.combate__registro-cuerpo[data-abierto='no'] {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

.combate__registro-cuerpo[data-abierto='no'] .registro-combate {
  height: auto;
  overflow: visible;
}

.combate__registro-cuerpo[data-abierto='si'] {
  position: absolute;
  right: 0;
  bottom: calc(100% + var(--e-2));
  z-index: 12;
  width: min(440px, calc(100vw - 2 * var(--e-4)));
  padding: var(--e-2);
  border: 1px solid var(--borde);
  border-radius: var(--radio);
  background: var(--superficie);
  box-shadow: var(--sombra-3);
}

.combate__registro-cuerpo[data-abierto='si'] .registro-combate {
  height: min(320px, 45vh);
  border: 0;
  background: transparent;
}

.combate__historial {
  flex: none;
  white-space: nowrap;
}

/* --- 20 · El desenlace, con los créditos fila a fila ---------------------- */

.panel-resultado__desglose {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  /* Sin hueco entre columnas: así el filo de la fila del total es una sola
     línea de lado a lado. La separación la pone el valor. */
  gap: var(--e-1) 0;
  width: min(360px, 100%);
  margin: 0;
  padding: var(--e-3) var(--e-4);
  border-radius: var(--radio-chico);
  background: var(--superficie-2);
  text-align: left;
}

.panel-resultado__concepto {
  margin: 0;
  color: var(--texto-2);
}

.panel-resultado__valor {
  margin: 0;
  padding-left: var(--e-6);
  text-align: right;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--texto);
}

.panel-resultado__valor[data-signo='positivo'] {
  color: var(--exito);
}

/* La fila del total, separada y en la tipografía de los títulos. */
.panel-resultado__concepto--total,
.panel-resultado__valor--total {
  padding-top: var(--e-2);
  border-top: 1px solid var(--borde);
  font-family: var(--fuente-titulo);
  font-size: var(--t-subtitulo-tam);
  font-weight: 700;
  color: var(--texto);
}

.panel-resultado__valor--total[data-signo='positivo'] {
  color: var(--exito);
}

/* =======================================================================
   UXC-3 — Comunidad: estrellas, resumen, comentarios y opiniones
   §7.1 (calificación promedio en cinco estrellas e hilo de comentarios en
   el detalle del producto) · RF-COM-002 · RF-COM-004 · RF-COM-006
   -----------------------------------------------------------------------
   Los componentes de `comun/ui/comunidad/` y `plataforma/comentarios/`
   (hilo, redactor, reporte). Se usan dentro de la ficha del inventario, del
   detalle de la tienda y del de la portada pública: superficies claras, así
   que la tinta es la oscura del kit.
   ======================================================================= */

/* Superficies claras: la tinta vuelve a oscuro (ver «Tinta contextual» en
   base.css). El hilo puede caer dentro de una ficha, de un dialogo o de una
   vista sobre la atmosfera; asi sale legible en los tres sitios. */
.hilo-comentarios,
.comentario,
.redactor-comentario {
  --tinta: var(--texto);
  --tinta-2: var(--texto-2);
  --tinta-3: var(--texto-3);
  --tinta-enlace: var(--primaria);
  --tinta-enlace-hover: var(--primaria-relleno);
}

/* --- Selector de estrellas (calificar) --------------------------------- */

.selector-estrellas {
  display: grid;
  gap: var(--e-1);
  min-width: 0;
  margin: 0;
  padding: 0;
  border: 0;
}

.selector-estrellas__fila {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--e-2) var(--e-3);
}

.selector-estrellas__opcion {
  display: inline-grid;
  place-items: center;
  width: 36px;
  height: 36px;
  border-radius: var(--radio-chico);
  cursor: pointer;
}

.selector-estrellas__opcion .estrellas__estrella {
  width: 24px;
  height: 24px;
}

.selector-estrellas__opcion:hover .estrellas__estrella {
  color: var(--rareza-legendaria);
}

.selector-estrellas__cifra {
  font-variant-numeric: var(--cifras-tabulares);
}

.selector-estrellas:disabled .selector-estrellas__opcion {
  cursor: not-allowed;
}

.selector-estrellas:disabled .estrellas__estrella {
  opacity: 0.45;
}

.selector-estrellas:disabled .selector-estrellas__opcion:hover .estrellas__estrella {
  color: var(--pista);
}

/* --- Resumen de la calificación ---------------------------------------- */

.resumen-calificacion {
  display: flex;
  align-items: center;
  gap: var(--e-3);
  min-width: 0;
}

.resumen-calificacion--compacto {
  gap: var(--e-2);
}

.resumen-calificacion__cifra {
  margin: 0;
  font-family: var(--t-titulo-familia);
  font-size: 2rem;
  font-weight: 700;
  line-height: 1;
  font-variant-numeric: var(--cifras-tabulares);
  color: var(--texto);
}

.resumen-calificacion__cuerpo {
  display: grid;
  gap: 2px;
}

.resumen-calificacion__detalle {
  margin: 0;
  font-size: var(--t-meta-tam);
  color: var(--texto-2);
}

/* --- Un comentario ------------------------------------------------------ */

.comentario {
  display: grid;
  gap: var(--e-2);
  padding: var(--e-3) var(--e-4);
  border: 1px solid var(--borde);
  border-radius: var(--radio);
  background: var(--superficie);
  color: var(--texto);
}

.comentario:focus-visible {
  outline: var(--foco-grosor) solid var(--interaccion-foco-anillo);
  outline-offset: 2px;
}

/* Lo propio se reconoce sin leer el apodo: filo en la primaria, como la
   sala en la que se puede entrar. El «Tú» escrito es el canal principal. */
.comentario--propio {
  border-left: 3px solid var(--primaria);
}

.comentario[data-estado='REPORTADO'] {
  background: var(--superficie-2);
}

.comentario__cabecera {
  display: flex;
  align-items: flex-start;
  gap: var(--e-3);
}

.comentario__avatar {
  flex: none;
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: var(--cromo);
  color: var(--texto-sobre-color);
  font-family: var(--t-titulo-familia);
  font-weight: 700;
}

.comentario__firma {
  display: grid;
  gap: 2px;
  min-width: 0;
}

.comentario__autor {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--e-2);
  margin: 0;
  font-weight: var(--t-cuerpo-peso-enfasis);
}

.comentario__apodo {
  overflow-wrap: anywhere;
}

.comentario__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--e-1) var(--e-3);
  font-size: var(--t-meta-tam);
  color: var(--texto-2);
}

.comentario__estrellas .estrellas__estrella {
  width: 14px;
  height: 14px;
}

.comentario__texto {
  margin: 0;
  line-height: 1.5;
  white-space: pre-line;
  overflow-wrap: anywhere;
}

.comentario__adjuntos {
  display: grid;
  gap: var(--e-1);
}

.comentario__adjuntos-lista {
  display: flex;
  flex-wrap: wrap;
  gap: var(--e-2);
  margin: 0;
  padding: 0;
  list-style: none;
}

.comentario__adjunto {
  display: inline-flex;
  align-items: center;
  gap: var(--e-2);
  max-width: 100%;
  padding: var(--e-1) var(--e-2);
  border: 1px dashed var(--borde-int);
  border-radius: var(--radio-chico);
  background: var(--superficie-2);
  font-size: var(--t-meta-tam);
  color: var(--texto-2);
}

.comentario__adjunto-icono {
  flex: none;
  width: 16px;
  height: 16px;
  color: var(--primaria);
}

.comentario__adjunto-nombre {
  max-width: 22ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.comentario__adjuntos-nota {
  margin: 0;
  font-size: var(--t-meta-tam);
  color: var(--texto-2);
}

/* B3 — imágenes guardadas (comentarios.yaml 1.5.0): la miniatura real, del
   lado fijo que marca `LADO_DE_MINIATURA` (96 px) para que el hilo no salte
   al cargar, recortada sin deformar y con enlace a su tamaño completo. El
   hueco del adjunto por nombre (borde discontinuo) no aplica: aquí hay imagen. */
.comentario__adjunto--imagen {
  padding: 0;
  border: 0;
  background: none;
}

.comentario__imagen-enlace {
  display: block;
  border-radius: var(--radio-chico);
  line-height: 0;
}

.comentario__imagen-enlace:focus-visible {
  outline: var(--foco-grosor) solid var(--interaccion-foco-anillo);
  outline-offset: 2px;
}

.comentario__imagen {
  display: block;
  width: 96px;
  height: 96px;
  border: 1px solid var(--borde);
  border-radius: var(--radio-chico);
  background: var(--superficie-2);
  object-fit: cover;
}

.comentario__estado {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--e-2);
  margin: 0;
  font-size: var(--t-meta-tam);
  color: var(--texto-2);
}

.comentario__estado-icono,
.comentario__accion-icono {
  flex: none;
  width: 14px;
  height: 14px;
}

.comentario__acciones {
  display: flex;
  justify-content: flex-end;
  gap: var(--e-2);
}

.comentario__accion {
  display: inline-flex;
  align-items: center;
  gap: var(--e-1);
}

/* --- El hilo (RatingThread) --------------------------------------------- */

.hilo-comentarios {
  display: grid;
  gap: var(--e-4);
  margin-top: var(--e-5);
  padding-top: var(--e-5);
  border-top: 1px solid var(--borde);
  color: var(--texto);
}

.hilo-comentarios__cabecera {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--e-3);
}

.hilo-comentarios__titulo {
  margin: 0;
  font-family: var(--t-titulo-familia);
  font-size: 1.15rem;
  font-weight: 700;
}

.hilo-comentarios__cuerpo {
  display: grid;
  gap: var(--e-3);
}

.hilo-comentarios__lista {
  display: grid;
  gap: var(--e-3);
  margin: 0;
  padding: 0;
  list-style: none;
}

.hilo-comentarios__mas {
  justify-self: start;
}

.hilo-comentarios__entrada {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--e-3);
  padding: var(--e-3) var(--e-4);
  border: 1px solid var(--borde);
  border-radius: var(--radio);
  background: var(--superficie-2);
}

.hilo-comentarios__entrada p {
  margin: 0;
  max-width: 46ch;
}

/* B3 — cuántas hay en el servidor y en qué orden: el `total` del hilo. */
.hilo-comentarios__conteo {
  margin: 0;
  font-size: var(--t-meta-tam);
  color: var(--texto-2);
}

/* --- Calificar sin comentar (RatingControl, B3) ---------------------------
   La calificación va aparte de la opinión (7.1: una sola vez). Mismo panel
   anidado que el redactor, para que se lea como un acto propio. */

.calificar-producto {
  --tinta: var(--texto);
  --tinta-2: var(--texto-2);
  --tinta-3: var(--texto-3);
  display: grid;
  gap: var(--e-3);
  padding: var(--e-3) var(--e-4);
  border: 1px solid var(--borde);
  border-radius: var(--radio);
  background: var(--superficie-2);
  color: var(--texto);
}

.calificar-producto__formulario {
  display: grid;
  gap: var(--e-2);
}

.calificar-producto__acciones {
  display: flex;
  justify-content: flex-start;
}

.calificar-producto__propia {
  display: grid;
  gap: var(--e-1);
}

.calificar-producto__propia:focus-visible {
  outline: var(--foco-grosor) solid var(--interaccion-foco-anillo);
  outline-offset: 2px;
}

.calificar-producto__titulo {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--e-2);
  margin: 0;
  font-weight: var(--t-cuerpo-peso-enfasis);
}

.calificar-producto__propia .campo__pista,
.calificar-producto__estado {
  margin: 0;
}

.calificar-producto__estado {
  font-size: var(--t-meta-tam);
  color: var(--texto-2);
}

/* --- El redactor (CommentComposer) --------------------------------------- */

.redactor-comentario {
  display: grid;
  gap: var(--e-3);
  padding: var(--e-4);
  border: 1px solid var(--borde);
  border-radius: var(--radio);
  background: var(--superficie-2);
}

.redactor-comentario__imagenes {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--e-2);
}

.redactor-comentario__imagenes .campo__pista {
  flex-basis: 100%;
  margin: 0;
}

.redactor-comentario__adjuntar {
  display: inline-flex;
  flex-direction: row;
  align-items: center;
  gap: var(--e-2);
  cursor: pointer;
}

.redactor-comentario__archivo:focus-visible + .redactor-comentario__adjuntar {
  outline: var(--foco-grosor) solid var(--interaccion-foco-anillo);
  outline-offset: 2px;
}

.redactor-comentario__miniaturas {
  display: flex;
  flex-basis: 100%;
  flex-wrap: wrap;
  gap: var(--e-2);
  margin: 0;
  padding: 0;
  list-style: none;
}

.redactor-comentario__miniatura {
  display: inline-flex;
  align-items: center;
  gap: var(--e-2);
  max-width: 100%;
  padding: var(--e-1) var(--e-2);
  border: 1px solid var(--borde);
  border-radius: var(--radio-chico);
  background: var(--superficie);
}

.redactor-comentario__miniatura img {
  width: 48px;
  height: 48px;
  border-radius: var(--radio-chico);
  object-fit: cover;
}

.redactor-comentario__nombre {
  max-width: 18ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--t-meta-tam);
}

.redactor-comentario__pie {
  display: flex;
  justify-content: flex-end;
}

/* B3 — cada imagen se sube al elegirla y su miniatura dice cómo va: subiendo
   (barra indeterminada), lista o el motivo del rechazo. El estado va escrito,
   no solo en color. */
.redactor-comentario__detalle {
  display: grid;
  gap: 2px;
  min-width: 0;
}

.redactor-comentario__estado {
  display: inline-flex;
  align-items: center;
  gap: var(--e-1);
  font-size: var(--t-meta-tam);
  color: var(--texto-2);
}

.redactor-comentario__estado .icono {
  width: 14px;
  height: 14px;
}

.redactor-comentario__estado--lista {
  color: var(--exito);
}

.redactor-comentario__estado--error {
  max-width: 32ch;
  color: var(--error);
}

.redactor-comentario__miniatura[data-estado='error'] {
  border-color: var(--error);
}

.redactor-comentario__progreso {
  width: 120px;
  height: 6px;
  accent-color: var(--primaria);
}

/* Con tres imágenes el selector se apaga: su etiqueta lo enseña. */
.redactor-comentario__archivo:disabled + .redactor-comentario__adjuntar {
  opacity: 0.55;
  cursor: not-allowed;
}

/* --- Reportar un comentario (ReportComment) ------------------------------- */

.reporte-comentario__categorias {
  display: grid;
  gap: var(--e-1);
  margin: 0;
  padding: 0;
  border: 0;
}

.reporte-comentario__opcion {
  display: flex;
  /* Explícito: alguna hoja de vista pone `label` en columna (tema-cuentas). */
  flex-direction: row;
  align-items: center;
  gap: var(--e-2);
  min-height: 36px;
  cursor: pointer;
}

/* --- Imágenes de un comentario en moderación (B3) -------------------------
   Las de un comentario en revisión no son públicas: se piden con la sesión
   del moderador. Más grandes que en el hilo, enteras (sin recorte), porque
   aquí se juzga lo que enseñan. */

.moderacion-imagenes {
  display: flex;
  flex-wrap: wrap;
  gap: var(--e-3);
  margin: 0;
  padding: 0;
  list-style: none;
}

.moderacion-imagenes__imagen {
  display: block;
  max-width: 240px;
  max-height: 240px;
  border: 1px solid var(--borde);
  border-radius: var(--radio-chico);
  background: var(--superficie-2);
  object-fit: contain;
}

@media (max-width: 767px) {
  /* 44×44 al dedo (WCAG 2.5.5): lo que se pulsa es la etiqueta. */
  .selector-estrellas__opcion {
    width: var(--objetivo-tactil);
    height: var(--objetivo-tactil);
  }

  .reporte-comentario__opcion {
    min-height: var(--objetivo-tactil);
  }

  .comentario {
    padding: var(--e-3);
  }
}

/* =======================================================================
   UXC-5 — Banner de misiones (MissionBanner)
   RF-MIS-001 (banner rotativo del tablón) · RF-INV-003 (banner del inventario)
   -----------------------------------------------------------------------
   Vive en el kit porque lo pintan dos vistas: el tablón de misiones y Mi
   inventario. Es un panel oscuro como la portada de la home: la tinta es
   clara dentro y se declara aquí, no se hereda por casualidad de la
   atmósfera. Sin servicio de misiones el inventario no lo pinta (RF-INV-003,
   Excepciones), así que en producción hoy solo se ve en el laboratorio.
   ======================================================================= */

.banner-misiones {
  --tinta: var(--texto-sobre-color);
  --tinta-2: var(--pista);
  --tinta-3: var(--pista);
  --tinta-enlace: var(--enlace-sobre-cromo);
  --tinta-enlace-hover: var(--texto-sobre-color);
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--e-4);
  padding: var(--e-6);
  border-radius: var(--radio);
  background: var(--cromo-elevado);
  background-image:
    radial-gradient(
      90% 120% at 100% 0%,
      color-mix(in srgb, var(--acento-relleno) 42%, transparent),
      transparent 60%
    ),
    linear-gradient(110deg, var(--cromo), var(--cromo-elevado) 65%);
  box-shadow: var(--sombra-atmosfera);
  color: var(--tinta);
  overflow: hidden;
}

/* El filo de energía de las tarjetas del Nexo, arriba. */
.banner-misiones::before {
  content: '';
  position: absolute;
  inset: 0 0 auto;
  height: 3px;
  background: var(--energia);
}

.banner-misiones__pista {
  min-width: 0;
}

.banner-misiones__diapositiva,
.banner-misiones--promocion {
  display: grid;
  grid-template-columns: 96px minmax(0, 1fr);
  gap: var(--e-5);
  align-items: center;
}

.banner-misiones__arte {
  display: grid;
  place-items: center;
  width: 96px;
  height: 96px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--primaria) 35%, var(--cromo));
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--enlace-sobre-cromo) 45%, transparent);
  overflow: hidden;
}

.banner-misiones__imagen {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.banner-misiones__emblema {
  width: 48px;
  height: 48px;
  color: var(--enlace-sobre-cromo);
}

.banner-misiones__cuerpo {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--e-2);
  min-width: 0;
}

.banner-misiones__sello {
  margin: 0;
  font-family: var(--t-etiqueta-familia);
  font-size: var(--t-meta-tam);
  font-weight: var(--t-etiqueta-peso);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--credito-oro-sobre-cromo);
}

.banner-misiones .banner-misiones__nombre {
  margin: 0;
  font-family: var(--t-titulo-familia);
  font-size: var(--t-titulo-tam);
  line-height: var(--t-titulo-alto);
  color: var(--texto-sobre-color);
  overflow-wrap: anywhere;
}

.banner-misiones__texto {
  max-width: 62ch;
  margin: 0;
  color: var(--pista);
}

.banner-misiones__datos {
  margin: 0;
  font-family: var(--t-etiqueta-familia);
  font-size: var(--t-etiqueta-tam);
  font-weight: var(--t-etiqueta-peso);
  color: var(--texto-sobre-color);
}

.banner-misiones__accion {
  margin-top: var(--e-2);
}

.banner-misiones__pie {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--e-3);
}

.banner-misiones__controles {
  display: flex;
  align-items: center;
  gap: var(--e-2);
}

/* Anterior apunta a la izquierda: es el mismo chevron, girado. */
.banner-misiones__anterior .icono {
  transform: rotate(180deg);
}

.banner-misiones__puntos {
  display: flex;
  align-items: center;
}

/* El punto se ve de 10 px, pero el objetivo es el botón entero (24×44). */
.banner-misiones__punto {
  display: grid;
  place-items: center;
  width: 24px;
  height: var(--objetivo-tactil);
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
}

.banner-misiones__punto::before {
  content: '';
  width: 10px;
  height: 10px;
  border-radius: 50%;
  border: 2px solid var(--pista);
  background: transparent;
}

/* La actual va rellena Y más ancha: no se distingue solo por el color. */
.banner-misiones__punto[aria-current='true']::before {
  width: 20px;
  border-radius: var(--radio-pildora);
  border-color: var(--texto-sobre-color);
  background: var(--texto-sobre-color);
}

.banner-misiones__punto:focus-visible {
  outline: var(--foco-grosor) solid var(--enlace-sobre-cromo);
  outline-offset: -2px;
  border-radius: var(--radio-chico);
}

.banner-misiones__tablon {
  font-family: var(--t-etiqueta-familia);
  font-size: var(--t-etiqueta-tam);
  font-weight: var(--t-etiqueta-peso);
}

@media (max-width: 599px) {
  .banner-misiones {
    padding: var(--e-5) var(--e-4);
  }

  .banner-misiones__diapositiva,
  .banner-misiones--promocion {
    grid-template-columns: 1fr;
    gap: var(--e-3);
  }

  .banner-misiones__arte {
    width: 64px;
    height: 64px;
  }

  .banner-misiones__emblema {
    width: 32px;
    height: 32px;
  }

  .banner-misiones .banner-misiones__nombre {
    font-size: var(--t-subtitulo-tam);
  }
}

/* =======================================================================
   UXC-6 — Conversación: globos, hilo, redactor y mensajes privados
   RF-JUE-015 (chat de la sala y de la vista general) · retroalimentación
   del docente (mensajes privados)
   -----------------------------------------------------------------------
   Vive en el kit porque la pintan tres sitios: el chat general, el de una
   sala y los mensajes privados (`comun/ui/conversacion/`).

   Quién habla nunca es solo color (WCAG 1.4.1): el globo lleva la palabra
   («Tú», el apodo o «Sistema»), la posición (derecha, izquierda, centro) y
   la forma (la inicial del otro jugador; el borde discontinuo del sistema).
   ======================================================================= */

.tarjeta.conversacion {
  display: flex;
  flex-direction: column;
  padding: 0;
  overflow: hidden;
}

.conversacion__barra {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--e-2) var(--e-3);
  padding: var(--e-3) var(--e-4);
  border-bottom: 1px solid var(--borde);
}

.conversacion__titulo {
  margin: 0;
  font-family: var(--t-subtitulo-familia);
  font-size: var(--t-subtitulo-tam);
  font-weight: var(--t-subtitulo-peso);
  color: var(--texto);
}

/* El hilo y su estado comparten el hueco: posicion relativa para el aviso de
   «Hay mensajes nuevos», que flota sobre el final de la lista. */
.conversacion__cuerpo {
  position: relative;
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
}

.conversacion__mensajes {
  display: flex;
  flex-direction: column;
  gap: var(--e-3);
  margin: 0;
  padding: var(--e-4);
  list-style: none;
  height: clamp(300px, 52vh, 560px);
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* Sin mensajes, el hueco es del estado (cargando, en silencio, sin canal). */
.conversacion__mensajes:empty {
  display: none;
}

.conversacion__estado > .estado-vista {
  min-height: 300px;
  border: 0;
  border-radius: 0;
}

.conversacion__nuevos {
  position: absolute;
  bottom: var(--e-3);
  left: 50%;
  transform: translateX(-50%);
  box-shadow: var(--sombra-2);
  z-index: 1;
}

/* Separador de día: «Hoy», «Ayer», «Lunes 21 de septiembre». */
.conversacion__dia {
  display: flex;
  align-items: center;
  gap: var(--e-3);
  font-family: var(--t-meta-familia);
  font-size: var(--t-meta-tam);
  color: var(--texto-3);
}

.conversacion__dia::before,
.conversacion__dia::after {
  content: '';
  flex: 1 1 auto;
  height: 1px;
  background: var(--borde);
}

/* --- El globo ---------------------------------------------------------- */

.mensaje {
  display: flex;
  flex-direction: column;
  gap: var(--e-1);
  max-width: min(72ch, 86%);
  align-self: flex-start;
}

.mensaje--yo {
  align-self: flex-end;
  align-items: flex-end;
}

.mensaje--sistema {
  align-self: center;
  align-items: center;
  max-width: min(64ch, 94%);
  text-align: center;
}

.mensaje__cabecera {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--e-1) var(--e-2);
  margin: 0;
  font-family: var(--t-meta-familia);
  font-size: var(--t-meta-tam);
  color: var(--texto-2);
}

.mensaje__autor {
  font-weight: 600;
  color: var(--texto);
}

.mensaje__hora {
  color: var(--texto-3);
  font-variant-numeric: tabular-nums;
}

.mensaje__inicial {
  display: inline-grid;
  place-items: center;
  flex: none;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: var(--primaria-relleno);
  color: var(--texto-sobre-color);
  font-family: var(--t-etiqueta-familia);
  font-size: 12px;
  font-weight: 700;
}

.mensaje__icono {
  flex: none;
  width: var(--icono-menudo);
  height: var(--icono-menudo);
}

.mensaje__globo {
  display: grid;
  gap: var(--e-2);
  padding: var(--e-2) var(--e-3);
  border: 1px solid var(--borde-tenue);
  border-radius: 4px 14px 14px 14px;
  background: var(--superficie-2);
  color: var(--texto);
}

.mensaje--yo .mensaje__globo {
  border-color: color-mix(in srgb, var(--primaria) 38%, transparent);
  border-radius: 14px 4px 14px 14px;
  background: color-mix(in srgb, var(--primaria) 9%, var(--superficie));
}

.mensaje--sistema .mensaje__globo {
  border: 1px dashed var(--info);
  border-radius: var(--radio);
  background: var(--info-insignia);
}

.mensaje--sistema .mensaje__cabecera {
  justify-content: center;
  color: var(--info);
}

.mensaje--advertencia .mensaje__globo {
  border-color: var(--advertencia);
  background: var(--advertencia-insignia);
}

.mensaje--advertencia .mensaje__cabecera {
  color: var(--advertencia);
}

.mensaje--exito .mensaje__globo {
  border-color: var(--exito);
  background: var(--exito-insignia);
}

.mensaje--exito .mensaje__cabecera {
  color: var(--exito);
}

.mensaje__texto {
  margin: 0;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

.mensaje__logro {
  display: flex;
  align-items: center;
  gap: var(--e-2);
  margin: 0;
  padding: var(--e-1) var(--e-2);
  border-radius: var(--radio-chico);
  background: var(--rareza-legendaria-insignia);
  font-family: var(--t-meta-familia);
  font-size: var(--t-meta-tam);
  font-weight: 600;
}

.mensaje__logro .icono {
  color: var(--rareza-legendaria);
}

/* Entrega de lo tuyo: con palabra e icono; «No se envió» en rojo y con su
   «Reintentar» al lado. */
.mensaje__entrega {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--e-1) var(--e-2);
  margin: 0;
  font-family: var(--t-meta-familia);
  font-size: var(--t-meta-tam);
  color: var(--texto-3);
}

.mensaje__entrega--leido {
  color: var(--info);
}

.mensaje__entrega--fallido {
  color: var(--error);
  font-weight: 600;
}

.mensaje__reintentar {
  min-height: 32px;
}

/* --- El redactor ------------------------------------------------------- */

.redactor-mensaje {
  display: grid;
  gap: var(--e-2);
  padding: var(--e-3) var(--e-4) var(--e-4);
  border-top: 1px solid var(--borde);
  background: var(--superficie);
  color: var(--texto);
}

.redactor-mensaje__fila {
  display: flex;
  align-items: flex-end;
  gap: var(--e-2);
}

.redactor-mensaje__campo {
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
}

.redactor-mensaje__texto {
  display: block;
  width: 100%;
  min-height: var(--boton-grande);
  max-height: 160px;
  padding: var(--e-2) var(--e-3);
  line-height: 1.45;
  resize: vertical;
}

.redactor-mensaje__enviar {
  flex: none;
  min-height: var(--boton-grande);
}

.redactor-mensaje__pista {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: var(--e-1) var(--e-3);
  margin: 0;
  color: var(--texto-3);
}

.redactor-mensaje__cuenta {
  font-variant-numeric: tabular-nums;
}

.redactor-mensaje__cuenta--cerca {
  color: var(--advertencia);
  font-weight: 600;
}

.redactor-mensaje__espera {
  flex-basis: 100%;
  color: var(--advertencia);
  font-weight: 600;
}

/* `list-item` (el de siempre) para no perder el triángulo que dice que se
   despliega; el alto mínimo lo da el relleno. */
.redactor-mensaje__logro summary {
  width: fit-content;
  padding: var(--e-1) 0;
  cursor: pointer;
}

/* El asistente flotante (RNF-DIS-002) vive abajo a la derecha: el final de la
   conversación no puede quedar debajo de él. */
.pagina[data-vista='chat'] {
  padding-bottom: calc(var(--e-6) + 56px + var(--e-4));
}

.redactor-mensaje__logro .fila {
  margin-top: var(--e-2);
}

/* Bloqueado (silencio, conversación bloqueada, sin sesión): el campo se
   sustituye por el motivo y lo que se puede hacer. */
.redactor-mensaje__bloqueo {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: var(--e-3);
  padding: var(--e-3) var(--e-4);
  border: 1px solid var(--borde-int);
  border-radius: var(--radio);
  background: var(--superficie-2);
}

.redactor-mensaje--bloqueado .redactor-mensaje__fila,
.redactor-mensaje--bloqueado .redactor-mensaje__pista,
.redactor-mensaje--bloqueado .redactor-mensaje__logro {
  display: none;
}

.redactor-mensaje__bloqueo-icono {
  flex: none;
  width: var(--icono);
  height: var(--icono);
  color: var(--texto-2);
}

.redactor-mensaje__bloqueo-cuerpo {
  display: grid;
  gap: var(--e-1);
  flex: 1 1 220px;
  min-width: 0;
}

.redactor-mensaje__bloqueo-titulo {
  margin: 0;
  font-weight: 600;
  color: var(--texto);
}

.redactor-mensaje__bloqueo-detalle {
  margin: 0;
  color: var(--texto-2);
}

.redactor-mensaje__bloqueo-acciones {
  display: flex;
  flex-wrap: wrap;
  gap: var(--e-2);
  align-self: center;
}

/* --- La página del chat ------------------------------------------------ */

.chat__titulo-fila {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--e-2) var(--e-3);
}

.chat__titulo-fila h1 {
  margin: 0;
}

.chat__pestanas {
  display: grid;
  gap: var(--e-4);
}

.chat__pestanas:empty {
  display: none;
}

.chat__general {
  min-width: 0;
}

.chat__privados {
  min-width: 0;
}

/* --- Mensajes privados -------------------------------------------------- */

.mensajes-privados {
  display: grid;
  grid-template-columns: minmax(260px, 340px) minmax(0, 1fr);
  gap: var(--e-4);
  align-items: start;
}

.mensajes-privados__lateral {
  display: grid;
  gap: var(--e-3);
  align-content: start;
  min-width: 0;
}

.mensajes-privados__titulo {
  margin: 0;
  font-family: var(--t-subtitulo-familia);
  font-size: var(--t-subtitulo-tam);
  font-weight: var(--t-subtitulo-peso);
}

.mensajes-privados__restriccion .aviso {
  flex-wrap: wrap;
}

.tarjeta.mensajes-privados__hilo {
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 480px;
  padding: 0;
  overflow: hidden;
}

.mensajes-privados__hilo > .estado-vista {
  flex: 1 1 auto;
  border: 0;
  border-radius: 0;
}

.mensajes-privados__cabecera {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--e-2) var(--e-3);
  padding: var(--e-3) var(--e-4);
  border-bottom: 1px solid var(--borde);
}

.mensajes-privados__con {
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
  font-family: var(--t-subtitulo-familia);
  font-size: var(--t-subtitulo-tam);
  font-weight: var(--t-subtitulo-peso);
  overflow-wrap: anywhere;
}

/* Solo en pantalla estrecha, donde la lista y la conversación se turnan. */
.mensajes-privados__volver {
  display: none;
}

/* El mismo chevron que apunta a la derecha, girado: «atrás». */
.mensajes-privados__volver-icono {
  transform: rotate(180deg);
}

.mensajes-privados__conversacion {
  flex: 1 1 auto;
  border: 0;
}

.mensajes-privados__sin-abrir {
  display: grid;
  gap: var(--e-3);
  justify-items: start;
  max-width: 68ch;
}

.mensajes-privados__sin-abrir h2,
.mensajes-privados__sin-abrir p {
  margin: 0;
}

.mensajes-privados__sin-abrir-icono {
  width: 40px;
  height: 40px;
  color: var(--primaria);
}

/* Lista de conversaciones: la activa se marca con fondo, borde y la barra
   de la izquierda, no solo con color. */
.conversaciones {
  display: grid;
  gap: var(--e-1);
  margin: 0;
  padding: 0;
  list-style: none;
}

.conversaciones__item {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--e-3);
  width: 100%;
  min-height: 56px;
  padding: var(--e-2) var(--e-3);
  border: 1px solid transparent;
  border-radius: var(--radio);
  background: none;
  color: var(--texto);
  font: inherit;
  text-align: left;
  cursor: pointer;
  transition: background var(--transicion);
}

.conversaciones__item:hover {
  background: var(--superficie-2);
}

.conversaciones__item[aria-current='true'] {
  border-color: color-mix(in srgb, var(--primaria) 40%, transparent);
  background: color-mix(in srgb, var(--primaria) 9%, var(--superficie));
  box-shadow: inset 3px 0 0 var(--primaria);
}

.conversaciones__inicial {
  display: inline-grid;
  place-items: center;
  flex: none;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: var(--primaria-relleno);
  color: var(--texto-sobre-color);
  font-family: var(--t-etiqueta-familia);
  font-weight: 700;
}

.conversaciones__cuerpo {
  display: grid;
  gap: 2px;
  min-width: 0;
  justify-items: start;
}

.conversaciones__apodo {
  max-width: 100%;
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.conversaciones__item--sin-leer .conversaciones__apodo {
  font-weight: 700;
}

.conversaciones__vista {
  max-width: 100%;
  font-family: var(--t-meta-familia);
  font-size: var(--t-meta-tam);
  color: var(--texto-2);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.conversaciones__lado {
  display: grid;
  justify-items: end;
  gap: var(--e-1);
}

.conversaciones__cuando {
  font-family: var(--t-meta-familia);
  font-size: var(--t-meta-tam);
  color: var(--texto-3);
  white-space: nowrap;
}

.conversaciones__no-leidos {
  display: inline-grid;
  place-items: center;
  min-width: 22px;
  height: 22px;
  padding: 0 6px;
  border-radius: var(--radio-pildora);
  background: var(--primaria-relleno);
  color: var(--texto-sobre-color);
  font-family: var(--t-meta-familia);
  font-size: var(--t-meta-tam);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* Buscar jugador */
.buscador-jugador {
  display: grid;
  gap: var(--e-2);
}

.buscador-jugador .campo {
  margin: 0;
}

.buscador-jugador__resultados {
  display: grid;
  gap: var(--e-1);
}

.buscador-jugador__lista {
  display: grid;
  gap: var(--e-1);
  margin: 0;
  padding: var(--e-1);
  list-style: none;
  border: 1px solid var(--borde);
  border-radius: var(--radio);
}

.buscador-jugador__resultado {
  display: flex;
  align-items: center;
  gap: var(--e-3);
  width: 100%;
  min-height: var(--objetivo-tactil);
  padding: var(--e-1) var(--e-2);
  border: 0;
  border-radius: var(--radio-chico);
  background: none;
  color: var(--texto);
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.buscador-jugador__resultado:hover {
  background: var(--superficie-2);
}

.buscador-jugador__apodo {
  flex: 1 1 auto;
  min-width: 0;
  font-weight: 600;
  overflow-wrap: anywhere;
}

.buscador-jugador__accion {
  font-family: var(--t-etiqueta-familia);
  font-weight: var(--t-etiqueta-peso);
  color: var(--primaria);
}

.buscador-jugador__estado {
  margin: 0;
  color: var(--texto-2);
}

.buscador-jugador__estado--error {
  color: var(--error);
}

@media (max-width: 899px) {
  .mensajes-privados {
    grid-template-columns: minmax(0, 1fr);
  }

  .mensajes-privados[data-vista-movil='lista'] .mensajes-privados__hilo,
  .mensajes-privados[data-vista-movil='hilo'] .mensajes-privados__lateral {
    display: none;
  }

  .mensajes-privados__volver {
    display: inline-flex;
  }
}

@media (max-width: 599px) {
  /* En la cabecera estrecha de una conversación privada, la píldora dice solo
     el estado; «Canal en tiempo real:» se sigue leyendo. Mismo patrón que el
     combate a pantalla completa. */
  .mensajes-privados__cabecera .conexion__prefijo {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
  }

  .conversacion__mensajes {
    padding: var(--e-3);
    height: clamp(280px, 58vh, 520px);
  }

  .mensaje {
    max-width: 92%;
  }

  .redactor-mensaje {
    padding: var(--e-3);
  }
}

/* =======================================================================
   UXC-7 — Estado de la cuenta (SanctionCountdown) y línea de tiempo
   (AdminTimeline)
   §7.3.8 (línea de tiempo de actividad del usuario) · RF-USR-004/005
   -----------------------------------------------------------------------
   El estado se dice con palabra e icono además del color del borde; en la
   línea de tiempo el tono va también en la forma del marcador (círculo,
   rombo, cuadrado) para que no dependa del color.
   ======================================================================= */

.estado-cuenta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--e-3) var(--e-4);
  border-left: 4px solid var(--exito);
}

/* Dentro de otra tarjeta (la consola de sanciones) no se levanta: sin sombra. */
.tarjeta .estado-cuenta {
  box-shadow: none;
}

.estado-cuenta--suspendida {
  border-left-color: var(--advertencia);
}

.estado-cuenta--baneada {
  border-left-color: var(--error);
}

.estado-cuenta__icono {
  flex: none;
  width: 28px;
  height: 28px;
  color: var(--exito);
}

.estado-cuenta--suspendida .estado-cuenta__icono {
  color: var(--advertencia);
}

.estado-cuenta--baneada .estado-cuenta__icono {
  color: var(--error);
}

.estado-cuenta__cuerpo {
  display: grid;
  gap: var(--e-1);
  flex: 1 1 240px;
  min-width: 0;
}

.estado-cuenta__titulo {
  margin: 0;
  font-family: var(--t-etiqueta-familia);
  font-size: var(--t-etiqueta-tam);
  font-weight: 700;
  color: var(--texto);
}

.estado-cuenta__detalle {
  margin: 0;
  color: var(--texto-2);
}

.estado-cuenta__cuenta {
  font-weight: 600;
  color: var(--texto);
}

.linea-tiempo {
  display: grid;
  margin: 0;
  padding: 0;
  list-style: none;
}

.linea-tiempo__hecho {
  position: relative;
  display: grid;
  grid-template-columns: 20px minmax(0, 1fr);
  gap: var(--e-3);
  padding-bottom: var(--e-4);
}

/* El hilo que une los hechos; el último no lo lleva. */
.linea-tiempo__hecho::before {
  content: '';
  position: absolute;
  top: 22px;
  bottom: 0;
  left: 9px;
  width: 2px;
  background: var(--borde);
}

.linea-tiempo__hecho:last-child {
  padding-bottom: 0;
}

.linea-tiempo__hecho:last-child::before {
  display: none;
}

.linea-tiempo__marca {
  width: 14px;
  height: 14px;
  margin: 4px 3px;
  border: 2px solid var(--texto-3);
  border-radius: 50%;
  background: var(--superficie);
}

.linea-tiempo__hecho--info .linea-tiempo__marca {
  border-color: var(--info);
  background: var(--info-insignia);
}

/* Advertencia: rombo. */
.linea-tiempo__hecho--advertencia .linea-tiempo__marca {
  border-color: var(--advertencia);
  border-radius: 2px;
  background: var(--advertencia-insignia);
  transform: rotate(45deg);
}

/* Error (un baneo): cuadrado relleno. */
.linea-tiempo__hecho--error .linea-tiempo__marca {
  border-color: var(--error);
  border-radius: 2px;
  background: var(--error);
}

/* Éxito (revertida): círculo relleno. */
.linea-tiempo__hecho--exito .linea-tiempo__marca {
  border-color: var(--exito);
  background: var(--exito);
}

/* Lo que todavía no ha pasado (termina el…): contorno discontinuo. */
.linea-tiempo__hecho--futuro .linea-tiempo__marca {
  border-style: dashed;
  background: var(--superficie);
}

.linea-tiempo__cuerpo {
  display: grid;
  gap: 2px;
  min-width: 0;
}

.linea-tiempo__cuando {
  margin: 0;
  font-family: var(--t-meta-familia);
  font-size: var(--t-meta-tam);
  color: var(--texto-3);
}

.linea-tiempo__titulo {
  margin: 0;
  font-weight: 600;
  color: var(--texto);
}

.linea-tiempo__detalle,
.linea-tiempo__actor {
  margin: 0;
  color: var(--texto-2);
  overflow-wrap: anywhere;
}

.linea-tiempo__actor {
  font-family: var(--t-meta-familia);
  font-size: var(--t-meta-tam);
}

/* =======================================================================
   UXC-8 — El torneo del jugador
   -----------------------------------------------------------------------
   «Tu torneo» (Mi equipo, Próximo encuentro, Mi camino), el encuentro de tu
   equipo marcado en el árbol —con texto, «Tu equipo», además del borde— y la
   transmisión en su variante sin señal: el marco del kit, sin prometer vídeo.
   ======================================================================= */

.torneo__tarjeta-cabecera {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--e-2) var(--e-3);
}

.torneo-mio {
  border-left: 4px solid var(--acento-relleno);
}

.torneo-mio__rejilla {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--e-4);
}

.torneo-mio__bloque {
  padding: var(--e-3) var(--e-4);
  border: 1px solid var(--borde);
  border-radius: var(--radio);
  background: var(--superficie);
}

.torneo-mio__equipo {
  display: flex;
  align-items: center;
  gap: var(--e-2);
  margin: 0;
}

.torneo-mio__inicial {
  display: inline-grid;
  place-items: center;
  flex: 0 0 auto;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: var(--acento-relleno);
  color: var(--texto-sobre-color);
  font-family: var(--t-titulo-familia);
  font-weight: 700;
}

.torneo-mio__camino {
  display: grid;
  gap: var(--e-2);
  margin: 0;
  padding: 0;
  list-style: none;
}

.torneo-mio__paso {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--e-1) var(--e-2);
}

.torneo-mio__paso--victoria .icono {
  color: var(--exito);
}

.torneo-mio__paso--derrota .icono {
  color: var(--error);
}

.encuentro--mio {
  border-color: var(--acento-relleno);
  box-shadow: var(--sombra-1), inset 0 0 0 1px var(--acento-relleno);
}

.encuentro__tuyo {
  margin-left: auto;
  padding: 0 var(--e-2);
  border-radius: var(--radio-pildora);
  background: var(--rareza-epica-insignia);
  color: var(--acento-relleno);
  font-family: var(--t-meta-familia);
  font-size: var(--t-meta-tam);
  font-weight: 600;
}

.transmision--sin-senal .transmision__lienzo {
  aspect-ratio: auto;
  min-height: 140px;
  align-items: center;
  justify-content: center;
  gap: var(--e-3);
  color: var(--texto-sobre-color);
}

.transmision--sin-senal .transmision__etiqueta {
  align-self: center;
  /* La etiqueta base (pista sobre pista) no llega a 4,5:1; sin señal se lee
     en superficie clara, con el texto de la paleta. */
  background: var(--superficie);
  color: var(--texto);
}

.transmision__simbolo {
  width: 40px;
  height: 40px;
}

/* UXC-8 — el premio del torneo (RF-TOR-007, torneos.yaml 1.2.0): la frase
   para todos va sobre la tinta del lugar; tu parte del premio, en «Mi
   equipo», con el fondo tenue de su estado. Icono y texto, nunca solo color. */
.torneo__premio {
  display: flex;
  align-items: center;
  gap: var(--e-2);
  margin: 0;
  font-weight: 600;
  color: var(--tinta);
}

.torneo-mio__premio {
  display: flex;
  align-items: flex-start;
  gap: var(--e-2);
  margin: 0;
  padding: var(--e-2) var(--e-3);
  border-radius: var(--radio);
  font-weight: 600;
}

.torneo-mio__premio .icono {
  flex: 0 0 auto;
  margin-top: 2px;
}

.torneo-mio__premio--exito {
  background: var(--exito-insignia);
  color: var(--exito);
}

.torneo-mio__premio--info {
  background: var(--info-insignia);
  color: var(--info);
}

.torneo-mio__premio--advertencia {
  background: var(--advertencia-insignia);
  color: var(--advertencia);
}

/* =======================================================================
   UXC-9 — Aviso de red transversal y hueco del asistente
   -----------------------------------------------------------------------
   `.aviso-red`: «Sin conexión» / «Conexión recuperada», arriba y centrado,
   sin tapar la cabecera; icono y texto además del color.
   El botón flotante del asistente (56 px, a 24 px del borde) tapaba lo
   último de cada página: con el botón montado, el contenido principal deja
   ese hueco al final. El combate queda fuera: es pantalla completa.
   ======================================================================= */

.aviso-red {
  position: fixed;
  /* Debajo de la cabecera, para no taparla; centrado sin `transform`, que
     con `left: 50%` dejaba al aviso media pantalla de ancho en el móvil. */
  top: calc(64px + var(--e-2));
  left: 0;
  right: 0;
  z-index: 10000;
  display: flex;
  align-items: center;
  gap: var(--e-2);
  width: fit-content;
  max-width: min(560px, calc(100vw - 2 * var(--e-4)));
  margin-inline: auto;
  padding: var(--e-2) var(--e-4);
  border: 1px solid currentColor;
  border-radius: var(--radio);
  box-shadow: var(--sombra-2);
  font-family: var(--t-cuerpo-familia, inherit);
  font-size: var(--t-meta-tam);
}

.aviso-red[hidden] {
  display: none;
}

.aviso-red--sin-red {
  background: var(--advertencia-insignia);
  color: var(--advertencia);
}

.aviso-red--recuperada {
  background: var(--exito-insignia);
  color: var(--exito);
}

.aviso-red__icono {
  flex: 0 0 auto;
  width: 20px;
  height: 20px;
}

.aviso-red__detalle {
  color: var(--texto);
}

body:has(.chatbot-flotante) main:not(.combate) {
  padding-bottom: calc(var(--e-6) + 56px + var(--e-4));
}

/* =========================================================================
   Sala de espera — revisión del modo jugador del 6-oct (puntos 13, 14, 16)
   -------------------------------------------------------------------------
   Una sola pieza en tres zonas: A · estado (modalidad, pública o privada,
   apuesta, anfitrión y el contador «1 / 2»), B · plazas (ocupada, de la IA o
   libre) y C · acciones. Antes eran una tarjeta blanca genérica y botones
   sueltos debajo. Mismo lenguaje que el resto del kit: superficie oscura de
   la arena, borde tenue, el acento solo donde hay que mirar.
   ========================================================================= */

.sala-espera {
  width: min(760px, 100%);
  display: grid;
  gap: var(--e-4);
  padding: var(--e-5);
  background: color-mix(in srgb, var(--cromo-elevado) 92%, transparent);
  border: 1px solid color-mix(in srgb, var(--enlace-sobre-cromo) 30%, transparent);
  color: var(--texto-sobre-color);
}

.sala-espera[hidden] {
  display: none;
}

.sala-espera__cabecera {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--e-4);
}

.sala-espera__identidad {
  display: grid;
  gap: var(--e-2);
  min-width: 0;
}

.sala-espera__sobretitulo {
  margin: 0;
  font-family: var(--t-etiqueta-familia);
  font-size: var(--t-etiqueta-tam);
  font-weight: var(--t-etiqueta-peso);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--enlace-sobre-cromo);
}

.sala-espera__titulo {
  margin: 0;
  font-family: var(--t-titulo-familia);
  font-size: var(--t-titulo-tam);
  line-height: var(--t-titulo-alto);
  font-weight: var(--t-titulo-peso);
  color: var(--texto-sobre-color);
}

.sala-espera__datos {
  display: flex;
  flex-wrap: wrap;
  gap: var(--e-2);
  margin: 0;
  padding: 0;
  list-style: none;
}

/* El contador de plazas: la cifra que se mira primero. */
.sala-espera__contador {
  flex: 0 0 auto;
  margin: 0;
  padding: var(--e-2) var(--e-4);
  border: 1px solid color-mix(in srgb, var(--enlace-sobre-cromo) 45%, transparent);
  border-radius: var(--radio-pildora);
  font-family: var(--t-titulo-familia);
  font-size: var(--t-subtitulo-tam);
  font-weight: var(--t-titulo-peso);
  font-variant-numeric: var(--cifras-tabulares);
  color: var(--texto-sobre-color);
}

.sala-espera__plazas {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: var(--e-3);
  margin: 0;
  padding: 0;
  list-style: none;
}

.plaza {
  display: flex;
  align-items: center;
  gap: var(--e-3);
  min-height: var(--objetivo-tactil);
  padding: var(--e-3);
  border: 1px solid color-mix(in srgb, var(--enlace-sobre-cromo) 30%, transparent);
  border-radius: var(--radio);
  background: color-mix(in srgb, var(--cromo) 70%, transparent);
}

.plaza--ocupada {
  border-color: color-mix(in srgb, var(--enlace-sobre-cromo) 60%, transparent);
}

.plaza[data-tu='si'] {
  border-color: var(--enlace-sobre-cromo);
  border-width: 2px;
}

.plaza--ia {
  border-color: color-mix(in srgb, var(--enlace-sobre-cromo) 45%, transparent);
}

/* La libre se lee como un hueco: borde discontinuo y texto apagado. */
.plaza--libre {
  border-style: dashed;
  background: transparent;
}

.plaza__avatar {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border-radius: var(--radio-pildora);
  background: var(--primaria-relleno);
  color: var(--texto-sobre-color);
  font-family: var(--t-titulo-familia);
  font-weight: var(--t-titulo-peso);
}

.plaza--ia .plaza__avatar {
  background: var(--acento-relleno);
}

.plaza--libre .plaza__avatar {
  background: transparent;
  border: 1px dashed var(--enlace-sobre-cromo);
  color: var(--enlace-sobre-cromo);
}

.plaza__cuerpo {
  display: grid;
  gap: 2px;
  min-width: 0;
  flex: 1 1 auto;
}

.plaza__nombre {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-weight: var(--t-cuerpo-peso-enfasis);
  color: var(--texto-sobre-color);
}

.plaza__detalle {
  font-size: var(--t-meta-tam);
  color: var(--enlace-sobre-cromo);
}

.plaza--libre .plaza__nombre {
  color: var(--enlace-sobre-cromo);
  font-weight: var(--t-cuerpo-peso);
}

.sala-espera__estado {
  margin: 0;
  font-weight: var(--t-cuerpo-peso-enfasis);
  color: var(--enlace-sobre-cromo);
}

/* Listo: el texto lo dice; el punto solo lo refuerza (no solo color). */
.sala-espera__estado[data-listo='si'] {
  color: var(--texto-sobre-color);
}

.sala-espera__estado[data-listo='si']::before {
  content: '';
  display: inline-block;
  width: 10px;
  height: 10px;
  margin-right: var(--e-2);
  border-radius: var(--radio-pildora);
  background: var(--exito-insignia);
  vertical-align: middle;
}

.sala-espera__acciones {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--e-3);
}

.sala-espera__acciones > .fila {
  margin: 0;
}

/* Sobre la arena oscura, los metadatos van en el azul claro del cromo: el
   gris de las superficies claras no se leería (contraste). */
.sala-espera .t-meta {
  color: var(--enlace-sobre-cromo);
}

.sala-espera__invitar {
  display: grid;
  gap: var(--e-3);
  padding: var(--e-4);
  border: 1px solid var(--borde-tenue);
  border-radius: var(--radio);
  background: var(--superficie);
  color: var(--texto);
}

.sala-espera__invitar[hidden] {
  display: none;
}

/* El panel de invitar es una superficie clara: vuelve a los colores de lectura. */
.sala-espera__invitar .t-meta {
  color: var(--texto-3);
}

.sala-espera__invitar-cabecera {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--e-3);
}

.sala-espera__acuse:empty {
  display: none;
}

.sala-espera__acuse[data-tono='error'] {
  color: var(--error);
}

/* Los datos sin variante (apuesta, anfitrión) como fichas discretas. */
.sala-espera__datos .distintivo:not([class*='distintivo--']) {
  border: 1px solid color-mix(in srgb, var(--enlace-sobre-cromo) 45%, transparent);
  color: var(--texto-sobre-color);
}

/* En la sala de espera no hay campo de combate: el aviso de «pantalla
   demasiado estrecha para el campo» no aplica y solo asustaba. */
.combate[data-sin-partida='si'] .combate__aviso-tamano {
  display: none;
}

@media (max-width: 600px) {
  .sala-espera {
    padding: var(--e-4);
  }

  .sala-espera__cabecera {
    flex-direction: column;
  }

  /* Acciones apiladas a lo ancho: el pulgar las encuentra todas igual. */
  .sala-espera__acciones,
  .sala-espera__acciones > .fila {
    flex-direction: column;
    align-items: stretch;
    width: 100%;
  }

  .sala-espera__acciones .boton {
    width: 100%;
  }
}

/* --- La sala privada en el listado (punto 10) ---------------------------- */

.sala-privada {
  width: min(560px, 100%);
}

.sala-privada .tarjeta__cabecera {
  align-items: center;
}
