/* ===========================================================================
   MU NachitoDLG — hoja de estilos única.
   Los tokens son los de design/SPEC.md §1 ("Piedra y oro", tema oscuro único, dirección elegida
   en el gate del 2026-09-11). Se escriben una vez acá y no se repiten en ninguna plantilla: el
   panel hermano tenía el mismo CSS copiado tres veces adentro de un template de 36 KB
   (minecraft/docs/04-arquitectura.md §1.5, §4.2).
   =========================================================================== */

:root {
  /* §1.2 Paleta */
  --color-bg: #14110D;
  --color-surface: #1A1510;
  --color-surface-raised: #251E15;
  /* T-094: el borde de tarjeta era #4A3D24 y daba 1,71:1 sobre --color-surface, por debajo del
     3:1 que pide WCAG 1.4.11 para un componente. Con esto da 3,51:1 sobre surface, 3,64 sobre bg
     y 3,19 sobre surface-raised: las cajas del sitio se ven en los tres fondos, no en uno solo. */
  --color-border: #806A44;
  --color-border-strong: #D6B25E;
  /* Divisorio INTERNO de una caja que ya tiene borde (filas de tabla, separadores de una tarjeta).
     No es el contorno de ningún componente, así que no le aplica el 3:1 y a propósito es callado:
     una tabla de veinte filas con veinte líneas a 3,5:1 es una reja, no una tabla.

     T-095: era #3E3324 y fue el ÚNICO número que 0.39.0 empeoró. Se llevó cuatro piezas que antes
     usaban --color-border (#4A3D24) y las dejó más oscuras que antes: separador de fila de tabla y
     borde superior de ul.item__precios 1,71 -> 1,47:1 sobre --color-surface, article.drops__zona
     1,78 -> 1,53:1 sobre --color-bg. A 1,47:1 sobre piedra oscura la línea no se dibuja, y en una
     tabla de drops de 50 filas el renglón es la única guía que tiene el ojo.

     El número está medido (WCAG 2.x, color efectivo sobre el fondo real del ancestro) y no
     estimado. Con #6A5837:
       - .data-table td y .item__precios sobre --color-surface (#1A1510): 2,64:1 (antes 1,47; en 0.38.0 1,71)
       - .drops__zona sobre --color-bg (#14110D):                    2,75:1 (antes 1,53; en 0.38.0 1,78)
       - filete del pie sobre --color-surface-raised (#251E15):      2,40:1 (antes 1,34; en 0.38.0 1,56)
     Ninguno de los usos queda por debajo de lo que tenía antes de 0.39.0, que es lo que pedía el
     ticket, y los cuatro suben ~55 % sobre 0.38.0. Se queda por debajo de --color-border (3,51-3,64)
     a propósito: el divisorio interno tiene que leerse más callado que el contorno de la caja, que
     es la razón por la que este token existe. Si algún día se quiere cumplir 1.4.11 también acá, el
     valor es #806A44 y deja de haber dos tokens. */
  --color-rule: #6A5837;
  /* Ornamento (§1.2): esquinas del marco, separadores, filetes. El oro de ACCIÓN es
     --color-accent y no se usa para decorar; si todo es del color de la acción, la acción no se
     distingue de la moldura. El bronce es más oscuro y más apagado a propósito. */
  --color-ornamento: #8A6E33;
  --color-text: #E9E2D2;
  --color-text-strong: #F4E6C3;
  --color-text-muted: #A89F8C;
  --color-accent: #D6B25E;
  --color-accent-hover: #F0D080;
  --color-accent-active: #B98D2F;
  --color-on-accent: #1A1408;
  --color-ok: #4CC27A;
  --color-ok-bg: #12281B;
  --color-warn: #F0B14A;
  --color-warn-bg: #2E2410;
  --color-crit: #FF6B6B;
  --color-crit-bg: #301517;
  --color-neutral: #A3A89F;
  --color-neutral-bg: #1F1B16;
  --color-focus: #F0D080;

  /* §1.3 Tipografía */
  --font-display: "Cinzel", "Georgia", serif;
  --font-text: "Source Sans 3", "Segoe UI", system-ui, sans-serif;

  /* §1.4 Espaciado, base 4 px */
  --sp-1: 4px;  --sp-2: 8px;  --sp-3: 12px; --sp-4: 16px;
  --sp-5: 24px; --sp-6: 32px; --sp-7: 48px; --sp-8: 64px;

  /* §1.5 Radios */
  --r-sm: 2px;
  --r-md: 4px;
  --r-full: 9999px;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  background: var(--color-bg);
  /* Textura de §1.2: radial dorado arriba + líneas horizontales al 1 %, y desde T-099 el muro de
     piedra pintado DEBAJO de los dos. El orden importa dos veces: la piedra va última porque es
     opaca y arriba taparía el resplandor dorado, y porque así —si la variable no existe o si el
     bucket no contesta— lo que queda es exactamente el fondo de 0.41.0 y no un hueco.
     El tile se publica a 512 px y se dibuja a 256 px CSS: es @2x (METODO §3). */
  background-image:
    radial-gradient(ellipse 900px 500px at 50% -10%, rgba(214, 178, 94, .16), transparent 70%),
    repeating-linear-gradient(0deg, rgba(255, 255, 255, .012) 0 2px, transparent 2px 5px),
    var(--pieza-piedra-fondo, none);
  background-size: auto, auto, 256px 256px;
  background-attachment: fixed;
  color: var(--color-text);
  font-family: var(--font-text);
  font-size: 16px;
  line-height: 1.5;
}

h1, h2 { font-family: var(--font-display); font-weight: 700; letter-spacing: .04em; color: var(--color-text-strong); }
h1 { font-size: 36px; line-height: 44px; margin: 0; }

a { color: var(--color-accent); text-decoration: none; }
a:hover { color: var(--color-accent-hover); }

/* §1.5: el anillo de foco es visible en TODO lo que se puede enfocar. Sin esto, la web no se
   puede usar con teclado, y es la clase de cosa que no vuelve a aparecer una vez que se olvida. */
a:focus-visible, label:focus-visible, input:focus-visible, select:focus-visible,
textarea:focus-visible, button:focus-visible, summary:focus-visible, .action-button:focus-visible {
  outline: 2px solid var(--color-focus);
  outline-offset: 2px;
}

/* Las casillas y los radios nativos salen celestes sobre un tema de piedra y oro. Una línea. */
input[type="checkbox"], input[type="radio"] { accent-color: var(--color-accent); width: 18px; height: 18px; }

/* Nada de transiciones para quien pidió que no las haya. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .001ms !important; animation-delay: .001ms !important; transition-duration: .001ms !important; }
}

/* Números con ancho fijo (§1.3): jugadores, cuentas regresivas, versiones. */
.num { font-variant-numeric: tabular-nums; }

/* Contenedor de 1040 px en las vistas públicas y de cuenta (§1.4). */
.page-shell {
  flex: 1;
  width: 100%;
  max-width: 1040px;
  margin: 0 auto;
  padding: var(--sp-7) var(--sp-4);
}

/* --- Marca y cabecera (SPEC §1.6, §2.12) ---------------------------------- */

/* El escudo. Un solo dibujo para los cuatro tamaños en que aparece —34 px en la cabecera, 64 en el
   héroe de la portada (48 en teléfono, desde T-102) y 16 en la pestaña— porque es SVG: no hay una
   versión por tamaño que se pueda desfasar. */
.escudo { display: block; flex: none; color: var(--color-accent); }
.escudo .escudo__campo { fill: #120F0B; }
/* En la portada el escudo es ornamento a escala grande y ahí sí manda el bronce; en la cabecera es
   la marca, y la marca es lo único decorativo que se queda con el oro vivo. */
.heroe .escudo { color: var(--color-border-strong); filter: drop-shadow(0 4px 18px rgba(214, 178, 94, .28)); }

/* El logotipo: "MU" grande en la fuente de display y la casa abajo, chica y espaciada. Son dos
   palabras de peso distinto a propósito — el nombre del server es "MU" y el resto es de quién es. */
.logotipo { display: flex; flex-direction: column; line-height: 1; }
.logotipo__mu {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 24px;
  letter-spacing: .12em;
  color: var(--color-text-strong);
}
.logotipo__casa {
  margin-top: 3px;
  font-family: var(--font-text);
  font-weight: 600;
  font-size: 12px;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--color-accent);
}

.site-header {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  flex-wrap: wrap;
  /* SPEC §2.12: la cabecera mide 64 px. Con `box-sizing: border-box` eso incluye el borde, así que
     la altura se declara y no se deja salir de la suma del padding más el alto del botón (que daba
     69). El `min-` es para que el menú desplegado de <1024 pueda crecer. */
  min-height: 64px;
  padding: var(--sp-2) var(--sp-5);
  border-bottom: 1px solid var(--color-border);
  /* T-105 · la franja de piedra, en TRES piezas. La de T-099 era una sola, dibujada 8:1 y traída a
     22,5:1 con `border-image`: las esquineras salían aplastadas y el rombo del centro —el que el
     relleno estiraba— caía suelto abajo de la cabecera.

     Ahora cada extremo entra con su ornamento ENTERO y a tamaño fijo (67x64 px CSS, la mitad del
     archivo @2x), y el medio es un mosaico de 201x64 repetido en x. Las tres medidas son fijas y
     en píxeles: por eso no hay ancho de viewport —de 320 a 1920— en el que algo se deforme, que
     es exactamente lo que el nine-slice no podía prometer.

     El orden de las capas no es cosmético: los dos ornamentos van ARRIBA del riel, así que la
     repetición que queda cortada contra cada punta queda tapada y el corte no se ve nunca.

     Los extremos miden 67 y no 87 desde que se recortaron sin el escudo de la casa: el escudo se
     encimaba con el de la marca —dos escudos, uno arriba del otro— y lo que quedó es la cantonera
     de bronce sola, que es más angosta. El número sale del archivo publicado
     (`tools/export-identidad/kit/medidas.json`), no del ojo: dibujarla más ancha que su mitad @2x
     es estirarla.

     El alto va en píxeles y no en `100%` a propósito: con el menú de teléfono desplegado la
     cabecera crece, y con `100%` los ornamentos se estiraban con ella. La banda mide 64 y se
     queda arriba; lo que crece es el panel del menú, que sigue siendo el degradado.

     Última capa, el degradado de siempre: es el piso cuando el bucket no contesta. Sin las
     variables, las tres primeras capas son `none` y la cabecera es la de 0.41.0. */
  background-image:
    var(--pieza-franja-izq, none),
    var(--pieza-franja-der, none),
    var(--pieza-franja-centro, none),
    linear-gradient(180deg, rgba(37, 30, 21, .92), rgba(20, 17, 13, .92));
  background-repeat: no-repeat, no-repeat, repeat-x, no-repeat;
  background-position: left top, right top, left top, center;
  background-size: 67px 64px, 67px 64px, 201px 64px, auto;
}
/* El filete de oro del borde inferior: se desvanece hacia las puntas, así que marca el centro sin
   dibujar una raya de lado a lado. Es ornamento, no un separador de componente. */
.site-header::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  /* La caja crece de 1 px a 14 px para que el filete pintado quepa; la línea de degradado sigue
     dibujándose en el mismo píxel de antes (`bottom 6px` de una caja que baja 7), así que sin la
     pieza esto se ve idéntico a 0.41.0. No cambia el alto de la cabecera: está fuera de flujo. */
  bottom: -7px;
  height: 14px;
  /* T-099 · filete-ornamental. Va entero y centrado, NO como nine-slice: la pieza tiene un nodo
     ornamental justo en el medio y estirar la banda central lo deforma (medido en
     `tools/export-identidad/kit/medidas.json`). El degradado sigue corriendo de punta a punta por
     detrás, que es lo que marca el ancho; la pieza marca el centro. */
  background-image: var(--pieza-filete-ornamental, none),
    linear-gradient(90deg, transparent, var(--color-ornamento) 25%, var(--color-accent) 50%, var(--color-ornamento) 75%, transparent);
  background-repeat: no-repeat, no-repeat;
  background-size: auto 100%, 100% 1px;
  background-position: center, center bottom 6px;
  pointer-events: none;
}

.site-header__marca {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  min-height: 44px;
  color: var(--color-accent);
}
.site-header__marca:hover .logotipo__mu { color: var(--color-accent-hover); }

.site-header__nav {
  display: flex;
  align-items: center;
  gap: var(--sp-2) var(--sp-5);
  font-weight: 600;
  font-size: 14px;   /* t-sm */
  letter-spacing: .04em;
  text-transform: uppercase;
}
/* Objetivo táctil: los enlaces de la cabecera medían 21 px de alto y el mínimo de WCAG 2.5.8 es
   24×24. Con esto miden 44, que es el de los botones. */
.site-header__nav > a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  color: var(--color-text);
}
.site-header__nav > a:hover { color: var(--color-accent-hover); }

/* El plegado de <1024 es un checkbox con su label: esta web no tiene JavaScript. */
.site-header__interruptor { position: absolute; width: 1px; height: 1px; opacity: 0; }
.site-header__menu {
  display: none;
  align-items: center;
  gap: var(--sp-2);
  min-height: 44px;
  padding: 0 var(--sp-3);
  border: 1px solid var(--color-border);
  border-radius: var(--r-sm);
  cursor: pointer;
  color: var(--color-accent);
  font-weight: 600;
  font-size: 14px;
  letter-spacing: .04em;
  text-transform: uppercase;
}
.site-header__menu:hover { border-color: var(--color-border-strong); }
/* El checkbox está escondido, así que el anillo de foco lo muestra su label: sin esto, tabular
   hasta el menú no se ve en ninguna parte. */
.site-header__interruptor:focus-visible + .site-header__menu { outline: 2px solid var(--color-focus); outline-offset: 2px; }
.site-header__icono--cerrar { display: none; }
.site-header__interruptor:checked + .site-header__menu .site-header__icono--abrir { display: none; }
.site-header__interruptor:checked + .site-header__menu .site-header__icono--cerrar { display: block; }

/* El enlace a administración en la cabecera pública: es navegación, no acción, así que va con la
   forma de los otros ítems y no con la de los botones. */
.site-header__admin { white-space: nowrap; }
.site-header__entrar { flex: none; }

@media (max-width: 1023px) {
  /* Cerrada, la cabecera es UNA fila: marca · Menú · Entrar. Se pliega la navegación entera y no
     una parte, que es lo que la hacía medir 195 px en un teléfono. */
  .site-header { flex-wrap: wrap; padding: var(--sp-2) var(--sp-4); }
  .site-header__menu { display: inline-flex; order: 2; margin-left: auto; }
  .site-header__entrar { order: 3; }
  .site-header__nav { order: 4; display: none; }
  .site-header__interruptor:checked ~ .site-header__nav {
    display: flex;
    flex-basis: 100%;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    margin: var(--sp-2) 0;
    border-top: 1px solid var(--color-rule);
  }
  .site-header__interruptor:checked ~ .site-header__nav > a {
    padding: var(--sp-2) 0;
    border-bottom: 1px solid var(--color-rule);
  }
}

@media (max-width: 359px) {
  /* En el teléfono más angosto el botón de entrar y el de menú comparten fila con la marca, así
     que la marca se achica antes de que algo se parta en dos líneas. */
  .logotipo__mu { font-size: 20px; }
  .logotipo__casa { letter-spacing: .14em; }
  .site-header__menu span { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
  .site-header__menu { padding: 0 var(--sp-2); }
}

/* --- Pie (SPEC §2.12) ----------------------------------------------------- */

.pie {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3) var(--sp-5);
  justify-content: space-between;
  align-items: center;
  /* T-100: los enlaces del pie eran objetivos táctiles de 21 px, en las veinte vistas. Suben a 44
     y el padding baja de 24 a 12 para compensarlos: el pie mide lo mismo que antes (68 px contra
     69 a 1440, 134 contra 135 a 390) y el enlace pasa a ser tocable. */
  padding: var(--sp-3) var(--sp-6);
  /* El pie acompaña a la cabecera: la misma piedra y el mismo filete, invertidos. Antes era el
     mismo negro del cuerpo con una línea encima y el sitio terminaba sin terminar. */
  background: linear-gradient(0deg, rgba(37, 30, 21, .92), rgba(20, 17, 13, .92));
  border-top: 1px solid var(--color-rule);
  color: var(--color-text-muted);
  font-size: 14px;
}
.pie::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  /* Mismo criterio que `.site-header::after`: la caja crece para el filete pintado y la línea de
     degradado se queda en su píxel. */
  top: -7px;
  height: 14px;
  background-image: var(--pieza-filete-ornamental, none),
    linear-gradient(90deg, transparent, var(--color-ornamento) 30%, var(--color-ornamento) 70%, transparent);
  background-repeat: no-repeat, no-repeat;
  background-size: auto 100%, 100% 1px;
  background-position: center, center top 6px;
  pointer-events: none;
}
.pie__enlaces { display: flex; align-items: center; gap: var(--sp-4); }
.pie__enlaces a { display: inline-flex; align-items: center; min-height: 44px; }

/* --- Botones (SPEC §2.7) -------------------------------------------------- */

/* `.action-button` la usan un <a> y un <button>, y un <button> NO hereda ni la fuente ni el fondo del
   documento: sin `font-family` propia sale en Arial, y sin `background` propio sale con el gris
   del navegador. Los dos defectos se veían en producción (el "Comparar" del comparador, gris y en
   Arial en medio del tema oscuro; el "Buscar" de /drops, dorado pero en Arial). Todo lo que
   `.action-button` necesita para no depender del user-agent se declara acá, en la clase base. */
.action-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  min-height: 44px;
  padding: var(--sp-2) var(--sp-4);
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--color-accent);
  font-family: var(--font-text);
  font-weight: 600;
  font-size: 16px;   /* t-base: §1.3 lo nombra para los botones */
  line-height: 24px;
  letter-spacing: .01em;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
  transition: background-color 150ms ease, border-color 150ms ease, color 150ms ease;
}
.action-button:disabled { opacity: .55; cursor: not-allowed; }
/* Un `.action-button` sin modificador es un botón de tercer nivel: texto en acento y nada más. Antes esto
   era el botón del sistema operativo. */
.action-button:not([class*="boton--"]):hover { background: var(--color-surface-raised); }
.action-button--primario {
  background: linear-gradient(180deg, #E2C063, var(--color-accent-active));
  border-color: #6D5320;
  color: var(--color-on-accent);
  box-shadow: 0 0 0 1px #6D5320, 0 6px 20px rgba(214, 178, 94, .25);
  /* T-099 · boton-oro. Nine-slice en X —el eje que varía: todos los botones miden 44 px de alto y
     lo que cambia es el ancho—, con el inset MEDIDO sobre el archivo publicado de 486x94: 25 px de
     herraje.

     Entra SIN `fill`, y no es una preferencia: la placa dorada pintada tiene filigrana oscura de
     punta a punta y el texto del botón primario se queda en 2,13:1 encima (medido en
     `tools/export-identidad/kit/medidas.json`). Con el herraje afuera y el degradado de siempre
     adentro, el mismo texto da 7,45:1. Un `fill` acá sería cambiar un botón legible por uno lindo.

     El herraje se DIBUJA a 9 px y no a los 12,5 que salen de dividir el inset por dos. Con 12,5 la
     primera letra se apoyaba en el bronce —medido, "Leer las reglas" daba 1,58:1— y agrandarle el
     padding al botón para que entrara hacía envolver la cabecera en teléfono: 57 px de alto de más
     en TODA página. El inset es una medida de la pieza; a qué tamaño se dibuja es una decisión, y
     ésta entra en el padding que el sitio ya tenía. */
  border-image: var(--pieza-boton-oro, none) 0 25 / 0 9px / 0 stretch;
}
.action-button--primario:hover { background: linear-gradient(180deg, #F0D080, #C89A36); color: var(--color-on-accent); }
.action-button--secundario {
  border-color: rgba(214, 178, 94, .6);
  color: var(--color-accent-hover);
  /* El fondo opaco es el arreglo del defecto que T-097 dejó anotado y este ticket nombra:
     `.action-button` declara `background: transparent` (más arriba), así que sobre la portada el dibujo
     se veía ADENTRO del botón y el texto caía a 1,86:1. La placa de piedra lo tapa, pero la placa
     vive en el bucket: el color de superficie es el piso que no depende de que el bucket conteste. */
  background: var(--color-surface);
  /* T-099 · boton-piedra, con el recorte que le hizo T-118.

     EL RELLENO SOLO PUEDE DIBUJAR LA BANDA CUYA LEGIBILIDAD SE MIDIÓ, y acá es donde se rompía.
     `tools/export-identidad/preparar-kit.py` le mide el contraste a cada pieza sobre la ZONA
     CENTRAL que declara su plan —`zona=0.55` para ésta— y de ahí sale el 7,5:1 contra
     `--color-accent-hover` que publica `kit/medidas.json`. El corte, en cambio, era `0 24`:
     arriba y abajo en cero, o sea que el navegador estiraba adentro del botón el 100 % del alto
     de la pieza —los dos filos iluminados incluidos— y el herraje de bronce que va de x=24 a
     x=56 quedaba adentro del relleno y se estiraba con él. Por eso el contraste dependía del
     ANCHO: cuanto más ancho el botón, menos se comprime el relleno y más se abre esa vena
     debajo del renglón. Medido con `qa/sensor-anchos.js` contra la pieza de verdad:
     8,68:1 a 127 px · 5,10:1 a 162 · **4,09:1 a 203** · 2,19:1 a 270 · 1,82:1 a 320.

     Los dos números del corte no son de ojo: son el inset de esa misma zona medida, sobre el
     tamaño publicado de 484x94 — (1 − 0,55) / 2 × 94 = **21** y × 484 = **108**, que es
     exactamente el recorte que hace `peores_de_la_zona()`. Con eso lo que se estira es la
     piedra oscura que dio 7,5:1 en el archivo, y el herraje entero se queda en los 9 px de
     punta, que no cambian de tamaño con el ancho. Medido después: **8,3 a 8,6:1 en los cinco
     anchos del barrido**, y plano.

     No se acortó ninguna etiqueta: eso arregla una pantalla y deja el defecto para la próxima,
     que es lo que pasó en T-111. Lo que no lo deja volver es el barrido de anchos del sensor,
     que corre en el CI. */
  border-image: var(--pieza-boton-piedra, none) 21 108 fill / 0 9px / 0 stretch;
}
/* El hover no vuelve a transparente: el velo dorado va ENCIMA del fondo opaco, no en su lugar. */
.action-button--secundario:hover {
  border-color: var(--color-accent);
  background: linear-gradient(rgba(214, 178, 94, .08), rgba(214, 178, 94, .08)) var(--color-surface);
}

/* --- P-FRAME: marco de piedra (SPEC §2.21) -------------------------------- */

.marco {
  position: relative;
  padding: var(--sp-5);
  /* T-099 · piedra-panel encima del degradado, que queda de piso. La pieza se publica apoyada
     sobre --color-surface al 30 % (la opacidad más alta que deja pasar a todos sus textos, medida
     en `kit/medidas.json`), así que con o sin bucket el panel es del mismo color. */
  background: linear-gradient(180deg, #221C15, var(--color-surface));
  background-image: var(--pieza-piedra-panel, none), linear-gradient(180deg, #221C15, var(--color-surface));
  background-size: cover, auto;
  background-position: center, center;
  border: 1px solid var(--color-border);
  border-radius: var(--r-md);
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .6), inset 0 0 40px rgba(0, 0, 0, .4);
  /* Las esquinas heredan este color: ornamento en bronce, no en el oro de la acción (§1.1). */
  color: var(--color-ornamento);
}
/* Las cuatro esquinas del marco. Hasta 0.41.0 había SVG en DOS —arriba-izquierda y abajo-derecha—
   y las otras dos estaban vacías; ahora son cuatro, y la esquinera pintada de T-099 entra como
   fondo de cada una con el trazo vectorial de piso debajo. La pieza se dibuja una sola vez y las
   otras tres son rotaciones, que es lo que pide el manifiesto. */
.marco__esquina {
  position: absolute;
  width: 36px;
  height: 36px;
  background: var(--pieza-esquina-marco, none) no-repeat center / contain;
}
.marco__esquina--ai { top: 4px; left: 4px; }
.marco__esquina--ad { top: 4px; right: 4px; transform: rotate(90deg); }
.marco__esquina--bd { bottom: 4px; right: 4px; transform: rotate(180deg); }
.marco__esquina--bi { bottom: 4px; left: 4px; transform: rotate(270deg); }
.marco > *:not(.marco__esquina) { color: var(--color-text); }

/* T-099 · separador-seccion. El SVG de trazo queda de piso y la pieza pintada se apoya encima,
   a la medida exacta de la caja (220x25 CSS son las proporciones del archivo de 440x50). Entera y
   no como nine-slice: tiene el rombo en el medio y estirar la banda central lo deforma. */
.separador {
  color: var(--color-ornamento);
  background: var(--pieza-separador-seccion, none) no-repeat center / contain;
}


/* --- T-099: las quince cajas, de una sola vez -----------------------------
   El relevamiento de superficies encontró quince selectores repitiendo la misma receta —16 px de
   padding, borde de 1 px, radio de 4 px y `--color-surface` de fondo—. La textura entra por acá y
   se propaga sola a las quince, sin tocar una sola plantilla.

   **La regla que esta lista impone abajo:** cada una de las quince declara su color de fondo con
   `background-color` y NUNCA con la abreviatura `background`. La abreviatura apaga este
   `background-image` y la pieza deja de dibujarse **sin que se vea nada raro** —el color queda
   igual, solo falta la textura—, que es por lo que esto pasó sin que nadie lo viera desde 0.42.1.
   T-107 pasó a `background-color` las cuatro de la vista patrón (`.video`, `.set`, `.item`,
   `.kpi`) y T-114 las once que faltaban, así que **las quince dibujan la pieza** (medido con
   `getComputedStyle` contra producción, `qa/visual-0.47.0/`). Lo que lo vigila es
   `qa/sensor-piezas.js` de ESTE repo, que lee el `background-image` CALCULADO de cada selector de
   esta lista y falla si la URL de la pieza no está ahí — y lo corre el CI en cada push
   (`.github/workflows/ci.yml`, job `piezas`), no una persona que se acuerde: correrlo a mano es
   exactamente lo que dejó a once cajas sin textura durante tres versiones.

   Las tres variantes del `EmptyState` (`--collection`, `--data`, `--unavailable`) quedan **sin
   textura a propósito** y no es un olvido: T-100 les saca la caja entera —`background: none`, sin
   borde y sin alto— y una textura sin caja no tiene dónde apoyarse. El sensor las ve como
   "apagada a propósito", no como falta. La que se texturiza es la caja de `.empty-state` a secas,
   la de administración, del panel de cuenta y de los dos vacíos del comparador.

   `piedra-caja` se publica apoyada sobre `--color-surface` al 65 %: es la opacidad más alta con la
   que TODOS los textos que se apoyan en una caja siguen pasando (el peor es `--color-text-muted`,
   4,51:1; el enlace de acento 5,86:1; ver `tools/export-identidad/kit/medidas.json`). Por eso el
   color de la caja con bucket y sin bucket es prácticamente el mismo, y por eso el fondo de color
   se declara aparte de la imagen: si la imagen no llega, queda el token y no un hueco. */
/* T-106 sacó de esta lista `.comparador__personaje`, `.comparador__excelentes` y
   `.comparador__gemas`: dejaron de ser cajas —son grupos con un filete— y una textura sin caja no
   tiene dónde apoyarse. `.comparador__lado` queda, pero solo es caja de 720 px para arriba. */
.set, .item, .kpi, .video,
.comparador__lado, .comparador__costo, .comparador__afuera-lado,
.lightbox__barra, .data-table--comparacion,
.empty-state, .dialogo, .admin__tarjeta {
  background-image: var(--pieza-piedra-caja, none);
  background-size: cover;
  background-position: center;
}

/* La esquinera chica, en dos de las cuatro esquinas de cada caja: la misma diagonal que usa
   `.marco` desde siempre (arriba-izquierda y abajo-derecha), una sola pieza y una rotación.
   Es una pieza que SUMA y no reemplaza nada: sin bucket, la caja se ve como en 0.41.0.

   Quedan afuera a propósito `.empty-state` —su `::before` ya es el escudo de la casa— y
   `.data-table--comparacion`, que es una `<table>` y no una caja donde apoyar un absoluto. */
.set, .item, .kpi, .video, .data-card,
.comparador__lado, .comparador__costo, .comparador__afuera-lado,
.lightbox__barra, .dialogo, .admin__tarjeta {
  position: relative;
}
.set::before, .item::before, .kpi::before, .video::before, .data-card::before,
.comparador__lado::before,
.comparador__costo::before, .comparador__afuera-lado::before, .lightbox__barra::before,
.dialogo::before, .admin__tarjeta::before,
.set::after, .item::after, .kpi::after, .video::after, .data-card::after,
.comparador__lado::after,
.comparador__costo::after, .comparador__afuera-lado::after, .lightbox__barra::after,
.dialogo::after, .admin__tarjeta::after {
  content: "";
  position: absolute;
  width: 18px;
  height: 18px;
  background: var(--pieza-esquina-caja, none) no-repeat center / contain;
  pointer-events: none;
}
.set::before, .item::before, .kpi::before, .video::before, .data-card::before,
.comparador__lado::before,
.comparador__costo::before, .comparador__afuera-lado::before, .lightbox__barra::before,
.dialogo::before, .admin__tarjeta::before { top: 1px; left: 1px; }
.set::after, .item::after, .kpi::after, .video::after, .data-card::after,
.comparador__lado::after,
.comparador__costo::after, .comparador__afuera-lado::after, .lightbox__barra::after,
.dialogo::after, .admin__tarjeta::after { bottom: 1px; right: 1px; transform: rotate(180deg); }

/* La cabecera de administración lleva la misma franja de piedra que la pública y cuesta cero
   piezas extra, pero su regla NO vive acá: su fondo es un color declarado más abajo, en §Shell de
   administración, y un `background` que llega después apaga la imagen que se hubiera puesto antes.
   Con `border-image` (T-099) el orden daba igual; con capas de `background-image` no. */

/* --- Portada -------------------------------------------------------------- */

/* T-102 · EL ORDEN. La portada es, de arriba abajo, `Estado de los servers` → `Cómo entrar` →
   separador → héroe → `Próximo evento`, y ése es el orden del MARCADO: no hay una sola regla de
   `order` ni de `flex-direction: column-reverse` acá adentro. El orden visual y el orden de lectura
   con teclado o lector de pantalla son el mismo, que es la única manera de que un cambio de
   composición no le mienta a la mitad de la gente. */
.portada {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-5);
  text-align: center;
}
/* EL HÉROE, REENCUADRADO (T-102). Hasta 0.51.2 era el primer bloque de la página y se llevaba
   ~450 px en teléfono contra una pantalla de 844: la pregunta número uno —si el server está
   prendido— quedaba abajo del pliegue. Ahora es el ANTEÚLTIMO bloque y es una banda: 400 px de
   techo en escritorio y 340 en teléfono, medidos sobre el elemento renderizado y no sobre la hoja.
   Lo que NO cambió es lo que dice: el escudo, el antetítulo, "Dos servers. Una cuenta." y la
   bajada son los mismos cuatro, palabra por palabra. Cambió su lugar y su escala.

   El techo se cumple con la suma del contenido y no con un `max-height`: un `max-height` sobre un
   flex de columna no achica nada, recorta, y un título recortado a la mitad es peor que uno grande. */
.heroe {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-3);
  width: 100%;
  /* La franja de abajo es LA ZONA DE LA ESCENA: el espacio donde cae el horizonte encendido y las
     torres, y donde no se apoya ni una línea de texto. Es el padding, no un elemento: el texto
     termina donde termina y abajo queda dibujo. */
  padding: var(--sp-5) var(--sp-4) 132px;
  text-align: center;
}
/* El vectorial de respaldo (fragmentos/heroe-fondo.html). Ocupa SOLO la zona de la escena, pegado
   al pie del bloque y de punta a punta —de ahí los -16 px de los costados, que son justo el padding
   lateral—, así que en teléfono llega al borde de la pantalla.

   Por qué acá abajo y no detrás de todo el bloque, que es como estaba en escritorio hasta 0.51.2:
   su lienzo tiene la ciudad en el 45 % de abajo y `preserveAspectRatio="xMidYMax slice"` ancla el
   dibujo al pie, así que en una banda de 400 px y 1.040 de ancho lo único que entra son esas
   unidades de abajo — las torres le pasarían por detrás a la bajada. Con la franja, el dibujo cae
   entero en la zona que ya está reservada para él y el texto nunca lo tiene atrás. Es lo que la
   versión de teléfono ya hacía desde T-099; T-102 la vuelve la única.

   Alto y ancho van explícitos y NO en `auto` con los cuatro `inset`: un `<svg>` es un elemento
   reemplazado y con `height: auto` el navegador le da su alto intrínseco y descarta el `bottom`. */
.heroe__fondo {
  position: absolute;
  bottom: 0;
  left: calc(var(--sp-4) * -1);
  width: calc(100% + var(--sp-4) * 2);
  height: 132px;
  pointer-events: none;
  z-index: 0;
}
/* T-099. La escena pintada REEMPLAZA al vectorial cuando el bucket contesta, y lo hace sin tocar
   `inicio.html`: va en un pseudo-elemento que cubre la banda ENTERA y es opaco, así que tapa al SVG
   por completo. Si el bucket no contesta, esta capa queda transparente y abajo sigue estando la
   franja vectorial: la portada vuelve sola a su versión sin kit.

   EL ENCUADRE SALE DE UNA MEDICIÓN DEL LIENZO, no del ojo, y es lo que T-102 pedía medir de nuevo
   después de mover el texto. Recorriendo `portada-heroe.jpg` (2080x1040) por bandas del 2 % y
   tomando el percentil 99,5 de luminancia de cada una, el resplandor del horizonte arranca en el
   54 % del alto: hasta el 52-54 % el peor píxel deja `--color-text-muted` en 5,32:1, y en el
   54-56 % ya cae a 3,94:1 —debajo de AA—. O sea: **el texto vive arriba del 54 % del lienzo y
   abajo de esa línea no se apoya nada.**

   De ahí salen los dos números de abajo, y son un alto en px y no un ancho en porcentaje. El
   lienzo se ancla ARRIBA (`50% 0`) y se escala por su ALTO, así que la línea del 54 % cae siempre
   en el mismo píxel de la banda sin importar el ancho de la ventana: con 540 px de alto dibujado,
   el 54 % queda en y=292, dentro de los 132 px de zona de escena de una banda de ~395. Con un
   porcentaje de ANCHO —que es como estaba hasta 0.51.2— ese píxel se mueve con cada ventana, y es
   exactamente el error que T-099 pagó dos veces: la cuenta hecha sobre la imagen entera daba 9 %
   de velo donde hacían falta 88, porque el navegador recorta y la cuenta no lo sabía.

   Al dibujarse por alto, el ancho sale en 1.080 px (el lienzo es 2:1), que cubre los 1.040 de la
   banda más ancha; abajo de eso se recorta por los costados, que es donde el lienzo tiene ruinas
   y no ciudad. Por eso no hace falta velo: el texto no se apoya sobre nada encendido. */
.heroe::before {
  content: "";
  position: absolute;
  inset: 0 calc(var(--sp-4) * -1);
  background-color: transparent;
  background-image: var(--pieza-portada-heroe, none);
  background-repeat: no-repeat;
  background-position: 50% 0;
  background-size: auto 540px;
  pointer-events: none;
  /* Los tres z-index son el orden de las capas y están los tres escritos: el vectorial abajo, la
     escena pintada encima tapándolo, y el contenido arriba de las dos. Sin el del medio el SVG
     ganaría por venir después en el marcado y la escena no se vería nunca. */
  z-index: 1;
}
/* El contenido del héroe va por encima del fondo. Sin z-index negativo: el fondo está primero en
   el marcado y esto lo levanta a él, no lo hunde a aquél. */
.heroe > *:not(.heroe__fondo) { position: relative; z-index: 2; }
/* En teléfono la banda es más baja (340 de techo contra 400) y el lienzo se dibuja más chico para
   que la misma línea del 54 % siga cayendo adentro de la zona de escena: con 500 px de alto
   dibujado el resplandor queda en y=270 y la banda mide 337.

   El escudo es lo único que se achica por hoja: en escritorio son los 64 px del atributo del SVG
   —bajó de 96 en T-102, y es la mitad de lo que se le recortó al bloque—, y acá 48. No le saca
   identidad a nada: el mismo escudo está arriba de todo, en la cabecera de todas las páginas. */
@media (max-width: 719px) {
  .heroe { gap: var(--sp-2); padding: var(--sp-4) var(--sp-4) 80px; }
  .heroe .escudo { width: 48px; height: 48px; }
  .heroe__fondo { height: 80px; }
  .heroe::before { background-size: auto 500px; }
}
.heroe__antetitulo {
  margin: 0;
  font-size: 12px;
  line-height: 18px;
  font-weight: 600;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--color-text-muted);
}
/* A 390 px la línea entera no entra con .18em de espaciado y se parte en dos renglones, que son
   18 px de banda que no sobran. Con .06em entra en uno solo y sigue leyéndose como antetítulo. */
@media (max-width: 719px) { .heroe__antetitulo { letter-spacing: .06em; } }
.portada h1 {
  /* El techo sigue siendo el t-3xl de §1.3 (56 px) y el piso baja de 36 a 28 —el t-xl—, que es lo
     que hace entrar el título en dos renglones adentro de la banda de teléfono. Nunca se sale de la
     escala: no se inventa un tamaño nuevo, se usa el rango que la escala ya tiene. */
  font-size: clamp(28px, 6vw, 56px);
  line-height: 1.12;
  text-shadow: 0 2px 24px rgba(214, 178, 94, .25);
}
.portada__bajada {
  margin: 0;
  max-width: 72ch;
  font-size: 18px;   /* t-md */
  color: var(--color-text-muted);
}
/* En teléfono la bajada son cuatro renglones sí o sí —son 150 caracteres contra 358 px de ancho—,
   así que el t-base es lo que la hace entrar en la banda sin tocarle una palabra. */
@media (max-width: 719px) { .portada__bajada { font-size: 16px; line-height: 22px; } }

/* "Cómo entrar" y "Reglas", ahora arriba del héroe y abajo del estado (SPEC §3 V1, AC-47, AC-50). */
.portada__acciones { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: var(--sp-5); }

/* --- V1.a: estado de los servers (T-019) ---------------------------------- */

.servidores { width: 100%; text-align: left; }
.servidores h2 { font-size: 28px; line-height: 36px; margin: 0 0 var(--sp-4); }   /* t-xl */
/* Dos tarjetas lado a lado desde 720 px (SPEC §3 V1); una columna abajo. */
.servidores__grilla { display: grid; gap: var(--sp-5); grid-template-columns: 1fr; }
@media (min-width: 720px) { .servidores__grilla { grid-template-columns: 1fr 1fr; } }

.servidor__cuerpo { display: flex; flex-direction: column; gap: var(--sp-3); }
.servidor__cabecera { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--sp-3); }
/* Nombre de server en tarjeta: --font-display t-lg (§1.3). */
.servidor__nombre { margin: 0; font-family: var(--font-display); font-size: 22px; line-height: 28px; color: var(--color-text-strong); }
.servidor__jugadores { margin: 0; color: var(--color-text-muted); }
/* El número de jugadores: t-2xl, tabular (§1.3). */
.servidor__numero { font-size: 36px; line-height: 44px; font-weight: 600; color: var(--color-text-strong); }
.servidor__texto { margin: 0; }
.servidor__texto--apagado { color: var(--color-text-muted); }
.servidor__meta { margin: 0; font-size: 14px; line-height: 20px; color: var(--color-text-muted); }

/* P-SKELETON (§2.4): bloque quieto del tamaño del dato que va a venir. Sin animación: la portada
   se recarga sola cada 3 s mientras está así, y un pulso que se corta en cada recarga distrae. */
.esqueleto { background: #2A2418; border-radius: var(--r-sm); }
.esqueleto--numero { height: 44px; width: 120px; }
.esqueleto__lento { margin: var(--sp-3) 0 0; font-size: 14px; line-height: 20px; color: var(--color-text-muted); }

/* --- V1.b: próximo evento del Leveo (T-020) ------------------------------- */

.eventos { width: 100%; text-align: left; display: flex; flex-direction: column; gap: var(--sp-4); }
.eventos h2 { font-size: 28px; line-height: 36px; margin: 0; }   /* t-xl */
.evento__cuerpo { display: flex; flex-direction: column; gap: var(--sp-3); }
.evento__hora { font-size: 18px; line-height: 28px; }   /* t-md */
/* La cuenta regresiva: t-2xl y no t-3xl (T-094). Era el texto más grande de todo el sitio, más
   que el título de la portada, y encima en la fuente de texto: el dato de una tarjeta le ganaba al
   titular de la página. Sigue siendo lo más grande DE SU TARJETA, que es lo que tiene que ser. */
.evento__cuenta { margin: 0; font-size: 36px; line-height: 44px; font-weight: 600; color: var(--color-text-strong); }
@media (max-width: 719px) { .evento__cuenta { font-size: 28px; line-height: 36px; } }
/* ST-11: anillo de espera. Quieto a propósito, como el esqueleto: la página se recarga cada 15 s. */
.evento__espera {
  display: inline-block; width: 14px; height: 14px; margin-right: var(--sp-2); vertical-align: -2px;
  border: 2px solid var(--color-border); border-top-color: var(--color-accent); border-radius: 50%;
}

/* --- Eventos del juego (2026-10-29) --------------------------------------- */

/* La tabla de Blood Castle, Chaos Castle, Devil Square, las invasiones y la happy hour. Mismo
   marco y mismos tokens que las tarjetas de arriba: es otra fila de datos de la portada, no una
   pieza nueva. */
.calendario { width: 100%; text-align: left; display: flex; flex-direction: column; gap: var(--sp-4); }
.calendario h2 { font-size: 28px; line-height: 36px; margin: 0; }   /* t-xl */
.calendario__cuerpo { display: flex; flex-direction: column; gap: var(--sp-3); }
.calendario__lista { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }

/* En teléfono cada evento es un bloque de tres renglones; desde 720 px es una grilla de tres
   columnas, que es donde las horas se leen en vertical una debajo de la otra. La línea separadora
   va arriba de cada fila menos la primera: con `border-bottom` quedaba una raya suelta al final. */
.calendario__fila {
  display: grid; grid-template-columns: 1fr; gap: var(--sp-1);
  padding: var(--sp-3) 0; align-items: baseline;
}
.calendario__fila + .calendario__fila { border-top: 1px solid var(--color-border); }
/* Cuatro columnas y no tres: la cuarta es el badge "abierto", que casi nunca está. Una columna
   `auto` vacía no ocupa nada, y así la hora de la fila con badge queda alineada con las otras. */
@media (min-width: 720px) {
  .calendario__fila { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) auto auto; gap: var(--sp-4); }
}
.calendario__nombre { font-family: var(--font-display); color: var(--color-text-strong); }
.calendario__cada { color: var(--color-text-muted); }
.calendario__cuando { font-size: 14px; line-height: 20px; color: var(--color-text-muted); }
@media (min-width: 720px) { .calendario__cuando { text-align: right; } }
.calendario__badge { justify-self: start; }
@media (min-width: 720px) { .calendario__badge { justify-self: end; } }

/* --- V2 instructivo y V3 reglas (T-023) ------------------------------------ */

.guia { display: flex; flex-direction: column; gap: var(--sp-5); }
/* TaskPage (T-103): el `PageShell` de 1024 con la navegación de pasos al costado y la
   `ReadingColumn` de 720 al lado. La columna del índice mide 176 px —el ticket pide 160-180— y
   bajó de los 200 que tenía: los siete rótulos más largos entran igual y la columna de lectura
   gana 24 px en el ancho donde el texto es lo que importa.

   Abajo de 1024 el índice NO desaparece y NO se pliega: pasa arriba del texto, que es donde tiene
   que estar para que sirva de índice, y es la primera cosa de la página después del H1. Un índice
   al pie de siete tramos no es un índice. */
.guia__cuerpo { display: grid; gap: var(--sp-6); grid-template-columns: 1fr; }
@media (min-width: 1024px) {
  .guia__cuerpo { grid-template-columns: 176px 1fr; align-items: start; }
  /* `sticky` sin una línea de JavaScript: el índice acompaña mientras se baja. El ticket lo dejaba
     caer si metía complejidad; son dos declaraciones y no mete ninguna, así que se queda. */
  .guia__indice { position: sticky; top: var(--sp-5); }
}
.guia__indice ol { margin: 0; padding-left: var(--sp-5); display: flex; flex-direction: column; gap: var(--sp-2); }
/* Los siete del índice son objetivos táctiles de una lista, no enlaces adentro de una oración: les
   aplica el mínimo de 44 px del sitio igual que a cualquier fila. Sin esto medían 24 px de alto,
   que es el alto de su renglón (medido con el sensor visual). El `inline-flex` es lo que los saca
   de "enlace en línea", donde un `min-height` no hace nada. */
.guia__indice a { display: inline-flex; align-items: center; min-height: 44px; }
/* EL NÚMERO DEL TRAMO, y por qué se ve así (T-103). Es el mismo tratamiento que el antetítulo del
   héroe —versalita chica, espaciada, en el gris atenuado— y a propósito NO tiene círculo, ni
   tilde, ni el oro de acción, ni fondo. Esta página no sigue el progreso de nadie: un número
   adentro de un círculo relleno dice "este paso ya está" y uno adentro de un círculo vacío dice
   "éste te falta", y las dos cosas son afirmaciones que la web no puede sostener. Lo que se lee es
   "Paso 3 de 7", que es una posición en un documento y no un estado. */
.guia__paso {
  margin: 0;
  font-size: 12px;
  line-height: 18px;
  font-weight: 600;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--color-text-muted);
}
/* El número queda pegado a su título y no flotando entre dos tramos: la separación que manda es la
   de la `ReadingColumn` entre secciones, no la que hay entre el rótulo y su encabezado. */
.reading-column .guia__paso + h2 { margin-top: calc(var(--sp-2) * -1); }

/* ReadingColumn (T-100, uno de los doce). Era `.prosa` a 72ch —unos 547 px con Source Sans 3 a
   16 px—; el sistema compartido la fija en 720 px, que es el segundo de los tres modos de
   composición. No es "más ancho porque sí": a 547 px el texto de reglas tiraba dos secciones y una
   lista a una columna más angosta que el propio selector de la página de al lado, y el ancho de
   lectura es lo único que las páginas editoriales comparten. */
.reading-column { max-width: 720px; display: flex; flex-direction: column; gap: var(--sp-6); }
.reading-column section { display: flex; flex-direction: column; gap: var(--sp-3); scroll-margin-top: var(--sp-5); }
.reading-column h2 { font-size: 28px; line-height: 36px; margin: 0; }   /* t-xl */
.reading-column p, .reading-column ul, .reading-column ol { margin: 0; }
/* La sangría de las listas numeradas: con `padding-left: sp-5` la segunda línea de un renglón
   largo quedaba pegada abajo del número (riesgo declarado de la vista 17). Con sp-6 de sangría y
   sp-3 entre renglones, el número queda afuera del bloque de texto y la lista se lee como lista. */
.reading-column ul, .reading-column ol { padding-left: var(--sp-6); display: flex; flex-direction: column; gap: var(--sp-3); }
.reading-column li { padding-left: var(--sp-1); }
/* Vista 17: "más separación entre las dos secciones". Crece de 32 a 48 SOLO de 720 para arriba: en
   teléfono el alto es el problema de todo el sitio y el instructivo son siete tramos. */
@media (min-width: 720px) { .reading-column { gap: var(--sp-7); } }
.reading-column code { font-size: 14px; background: var(--color-surface-raised); padding: 0 var(--sp-1); border-radius: var(--r-sm); }
.reading-column__fecha { font-size: 14px; line-height: 20px; color: var(--color-text-muted); }

/* Bloque de hash (§1.3): texto, t-sm, espaciado, cortable en cualquier punto. */
.hash {
  font-size: 14px;
  line-height: 20px;
  letter-spacing: .02em;
  word-break: break-all;
  font-variant-numeric: tabular-nums;
  background: var(--color-surface-raised);
  padding: var(--sp-2) var(--sp-3);
  border-radius: var(--r-sm);
}

/* La única caja del instructivo (T-103): archivo + versión + integridad + acción, que es una
   unidad y por eso conserva el marco. `stretch` y no `flex-start`: el botón de descarga ocupa el
   ancho entero de la caja, que es lo que lo vuelve la acción dominante de su sección en vez de un
   botón más apoyado contra el margen izquierdo. Los demás hijos son texto y no cambian de ancho. */
.descarga__tarjeta { display: flex; flex-direction: column; align-items: stretch; gap: var(--sp-3); }
.descarga__meta { margin: 0; font-size: 14px; line-height: 20px; color: var(--color-text-muted); }
.descarga__etiqueta { margin: 0; font-size: 14px; line-height: 20px; font-weight: 600; }
.descarga__tarjeta .hash { margin: 0; width: 100%; }

.banner__lista { margin: 0; padding-left: var(--sp-5); display: flex; flex-direction: column; gap: var(--sp-2); }

/* --- V4: rankings (T-022) ------------------------------------------------- */

.ranking { display: flex; flex-direction: column; gap: var(--sp-5); }
/* La franja funcional —métrica, actualización y ServerSwitch— se fue al PageHeader del sistema
   compartido (T-100): era el mismo objeto dibujado de dos maneras en ranking y en conectados. */
.ranking .data-table { margin-top: 0; }
.ranking .data-table td { height: 48px; }
.data-table__numero { text-align: right; }
/* Posiciones 1–3 en display sobre surface-raised, sin acento: no es una acción (SPEC §3 V4). */
.ranking__podio { background: var(--color-surface-raised); }
.ranking__podio .ranking__posicion { font-family: var(--font-display); font-weight: 700; color: var(--color-text-strong); }

/* T-100 (vistas 15 y 16): en teléfono la tabla no se apila en cuatro renglones con etiqueta —que
   es lo que hace P-TABLE por defecto y lo que convertía cada personaje en una tarjeta de 140 px—
   sino en una DataRow de posición · personaje · dato. El desempate no se pierde: lo explica la
   línea de métrica de la cabecera, que en el PVP va ANTES del resultado justamente para eso, y el
   `<thead>` sigue en el DOM para quien lee con lector de pantalla. */
@media (max-width: 719px) {
  .data-table--ranking tbody tr {
    display: flex;
    flex-wrap: nowrap;
    align-items: center;
    gap: var(--sp-3);
    min-height: 44px;
    margin-bottom: 0;
    padding: var(--sp-2) var(--sp-1);
    border: 0;
    border-bottom: 1px solid var(--color-rule);
    border-radius: 0;
    background: none;
  }
  .data-table--ranking tbody tr.ranking__podio { background: var(--color-surface-raised); }
  .data-table--ranking tbody tr:last-child { border-bottom: 0; }
  .data-table--ranking tbody td { display: block; height: auto; padding: 0; }
  .data-table--ranking tbody td::before { content: none; }
  /* La posición ocupa un ancho fijo para que los nombres arranquen todos en la misma columna. */
  .data-table--ranking .ranking__posicion { flex: none; width: 32px; text-align: right; color: var(--color-text-muted); }
  .data-table--ranking tbody td:nth-child(2) { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; color: var(--color-text-strong); font-weight: 600; }
  .data-table--ranking tbody td:nth-child(3) { flex: none; font-size: 12px; line-height: 18px; color: var(--color-text-muted); }
  .data-table--ranking tbody td:nth-child(4) { flex: none; text-align: right; font-weight: 600; color: var(--color-text-strong); }
}

/* Control segmentado: dos enlaces. El activo en acento, marcado con aria-current (SPEC §3 V4). */
.server-switch { display: inline-flex; border: 1px solid var(--color-border); border-radius: var(--r-md); overflow: hidden; background: var(--color-surface); }
/* Las mismas dos pastillas de T-099 que los chips: es el mismo objeto con otro nombre. El
   `border: 0 solid transparent` es para que `border-image` tenga un lado con estilo donde
   dibujarse; el contorno del grupo lo sigue poniendo `.server-switch`. */
.server-switch a { display: inline-flex; align-items: center; height: 44px; padding: 0 var(--sp-5); font-weight: 600; color: var(--color-text);
                border: 0 solid transparent;
                border-image: var(--pieza-pastilla, none) 0 27 fill / 0 8px / 0 stretch; }
.server-switch a:hover { background: var(--color-surface-raised); color: var(--color-text); }
.server-switch a[aria-current="page"] { background: var(--color-accent); color: var(--color-on-accent);
                                     border-image-source: var(--pieza-pastilla-activa, none); border-image-slice: 0 27; }

/* --- V25: conectados (T-021) ---------------------------------------------- */

.conectados { display: flex; flex-direction: column; gap: var(--sp-5); }
.conectados__total { margin: 0; font-size: 18px; line-height: 28px; }   /* t-md */
/* ST-210: el número de la lista vieja no va grande (SPEC §3 V25). */
.conectados__viejo { margin: 0; color: var(--color-text-muted); }
/* P-NAMES (§2.20): columnas de 160 px, tantas como quepan; separación sp-2 sp-5 (§1.4). */
.nombres { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: var(--sp-2) var(--sp-5); }
.nombres li { margin: 0; overflow-wrap: anywhere; }
.nombres--viejos li { color: var(--color-text-muted); }

/* --- V24: contacto (T-027) ------------------------------------------------ */

.canales { display: flex; flex-direction: column; gap: var(--sp-4); }
.canales h2 { font-size: 28px; line-height: 36px; margin: 0; }   /* t-xl */
/* Tarjetas b-1 r-md, grilla de 2 desde 720 px (SPEC §3 V24). */
.canales__grilla { display: grid; gap: var(--sp-4); grid-template-columns: 1fr; }
@media (min-width: 720px) { .canales__grilla { grid-template-columns: 1fr 1fr; } }
.canal__enlace { display: inline-flex; align-items: center; gap: var(--sp-1); min-height: 44px; font-weight: 600; }

/* --- V22: videos (T-026) -------------------------------------------------- */

.videos { display: flex; flex-direction: column; gap: var(--sp-5); }
.videos__intro { margin: 0; max-width: 72ch; font-size: 14px; line-height: 20px; color: var(--color-text-muted); }   /* t-sm */
.videos__lista { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--sp-2); }
.video { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--sp-3); padding: var(--sp-3) var(--sp-4); border: 1px solid var(--color-border); border-radius: var(--r-md); background-color: var(--color-surface); }
/* Fila elegida: --color-surface-raised (SPEC §3 V22, ST-192/193). */
.video--elegido { background: var(--color-surface-raised); border-left: 3px solid var(--color-accent); }
.video__titulo { margin: 0; font-size: 18px; line-height: 28px; font-weight: 600; color: var(--color-text-strong); }   /* t-md 600 */
.video__acciones { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-4); }
.video__acciones a:not(.action-button), .reproductor__respaldo a { display: inline-flex; align-items: center; gap: var(--sp-1); font-weight: 600; }

/* P-EMBED (§2.15): caja 16:9, fondo surface-raised, r-md. */
.reproductor { display: flex; flex-direction: column; gap: var(--sp-3); scroll-margin-top: var(--sp-5); }
.reproductor__titulo { margin: 0; font-size: 18px; line-height: 28px; font-weight: 600; color: var(--color-text-strong); }
.reproductor__caja { position: relative; aspect-ratio: 16 / 9; width: 100%; background: var(--color-surface-raised); border-radius: var(--r-md); overflow: hidden; }
.reproductor__iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
/* ST-192 sin JavaScript: la línea vive detrás del iframe y aparece a los 3 s; el reproductor, al pintar, la tapa. */
.reproductor__lento { position: absolute; left: var(--sp-4); right: var(--sp-4); bottom: var(--sp-4); margin: 0; font-size: 14px; line-height: 20px; color: var(--color-text-muted); opacity: 0; animation: reproductor-lento 0s linear 3s forwards; }
@keyframes reproductor-lento { to { opacity: 1; } }
.reproductor__respaldo { margin: 0; font-size: 14px; line-height: 20px; color: var(--color-text-muted); }

/* Texto que solo leen los lectores de pantalla. */
.solo-lectores { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }

/* --- P-SYSTEM: 404 / 403 / 500 (SPEC §2.10) ------------------------------- */

.sistema__aviso {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-4);
  padding: var(--sp-6) var(--sp-4);
  text-align: center;
}
.sistema__codigo {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 56px;
  line-height: 60px;
  font-variant-numeric: tabular-nums;
}
.sistema__cuerpo { margin: 0; max-width: 48ch; color: var(--color-text-muted); }
.sistema__botones { display: flex; flex-wrap: wrap; gap: var(--sp-3); justify-content: center; }

/* El token del estado (§1.1): ningún estado se comunica solo por color — el código y el título
   dicen lo mismo en texto. */
.estado--neutral { color: var(--color-neutral); }
.estado--warn { color: var(--color-warn); }
.estado--crit { color: var(--color-crit); }
.estado--ok { color: var(--color-ok); }

/* --- P-BANNER (SPEC §2.2) -------------------------------------------------- */

.banner {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  margin: 0 0 var(--sp-5);
  padding: var(--sp-4);
  border-radius: var(--r-md);
  border: 0 solid transparent;
  border-left: 3px solid var(--color-neutral);
  /* T-099 · banda-aviso-*. Nine-slice en Y —el eje que varía, porque el ancho del borde es fijo y
     el alto del banner no—, con el inset medido 13 px sobre el archivo de 31x241. El recorte pide
     los cuatro números: `13 0 13 31` deja el ancho ENTERO de la pieza en la región izquierda y
     cero en la derecha, que es la única forma de dibujar una banda vertical en un solo lado.
     Sin la pieza queda el `border-left` de 3 px de siempre. */
  border-image: var(--pieza-banda-aviso-bronce, none) 13 0 13 31 fill / 13px 0 13px 15px / 0 stretch;
  background: var(--color-neutral-bg);
  color: var(--color-text);
  text-align: left;
}

.banner__icono { flex: none; }
.banner__texto { margin: 0; font-size: 18px; line-height: 28px; font-weight: 600; }
/* Los 44 px del mínimo táctil: es el enlace que se toca para reintentar, no un enlace adentro de
   una oración, así que le aplica la regla y no la excepción de WCAG 2.5.8. Lo encontró el sensor de
   pantalla cuando T-115 lo puso en el CI: la corrida contra producción de 0.52.1 no podía verlo
   porque este banner solo se dibuja cuando un server no contesta, y en producción contestaban. */
.banner__accion { margin-left: auto; display: inline-flex; align-items: center; min-height: 44px; white-space: nowrap; text-decoration: underline; }

/* Cada tinte trae su propia banda. El color del `border-left` sigue siendo el piso: si la pieza
   no llega, el banner se ve como en 0.41.0, con su línea de 3 px del color del estado. */
.banner--ok       { border-left-color: var(--color-ok);       background: var(--color-ok-bg);
                    border-image-source: var(--pieza-banda-aviso-verde, none); }
.banner--ok .banner__icono       { color: var(--color-ok); }
.banner--warn     { border-left-color: var(--color-warn);     background: var(--color-warn-bg);
                    border-image-source: var(--pieza-banda-aviso-ambar, none); }
.banner--warn .banner__icono     { color: var(--color-warn); }
.banner--crit     { border-left-color: var(--color-crit);     background: var(--color-crit-bg);
                    border-image-source: var(--pieza-banda-aviso-rojo, none); }
.banner--crit .banner__icono     { color: var(--color-crit); }
.banner--neutral .banner__icono  { color: var(--color-neutral); }

/* Variante `ok+warn` (§1.1 regla 3): el título en ok y una segunda línea de advertencia en warn.
   Con dos líneas el ícono del título va arriba y no al medio del bloque. La advertencia lleva su
   propio ícono: el color nunca comunica solo (§1.1 regla 4). */
.banner--advertido { align-items: flex-start; }
.banner__cuerpo { display: flex; flex-direction: column; gap: var(--sp-2); }
.banner__advertencia {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  margin: 0;
  font-size: 14px;
  line-height: 20px;
  color: var(--color-text);
}
.banner__icono--advertencia { color: var(--color-warn); }

/* --- P-BADGE (SPEC §2.1) -------------------------------------------------- */

.badge {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-1) var(--sp-3);
  border-radius: var(--r-full);
  font-size: 14px;
  line-height: 20px;
  font-weight: 600;
  background: var(--color-neutral-bg);
  color: var(--color-neutral);
}

/* El punto es decorativo: el badge siempre lleva además su texto (§1.1 regla 4). */
.badge__punto { width: 8px; height: 8px; border-radius: var(--r-full); background: currentColor; }

.badge--ok   { background: var(--color-ok-bg);   color: var(--color-ok); }
.badge--ok .badge__punto { box-shadow: 0 0 8px var(--color-ok); }
.badge--warn { background: var(--color-warn-bg); color: var(--color-warn); }
.badge--crit { background: var(--color-crit-bg); color: var(--color-crit); }

/* --- Tarjeta centrada: entrada (V5) y registro (V6) ----------------------- */

.entrada { display: flex; justify-content: center; }
.entrada > .marco { width: 100%; max-width: 488px; }

.tarjeta {
  display: flex;
  flex-direction: column;
  gap: var(--sp-5);
  max-width: 440px;
  margin: 0 auto;
  text-align: center;
}

/* El registro es de 520 px (§3 V6); el marco lo acompaña (el padding del marco es sp-5 x2). */
.tarjeta--ancha { max-width: 520px; }
.entrada--ancha > .marco { max-width: 568px; }

.tarjeta__cuerpo { margin: 0; color: var(--color-text); }
.tarjeta__nota { margin: 0; font-size: 14px; line-height: 20px; color: var(--color-text-muted); }
.tarjeta__salida { text-decoration: underline; }
.entrada__accion { margin: 0; }
.entrada__accion .action-button { width: 100%; }
/* El banner ocupa el ancho de la tarjeta y no hereda el centrado del texto (§2.2). */
.tarjeta > .banner { margin: 0; }

/* --- V7: panel de cuenta -------------------------------------------------- */

.panel { display: flex; flex-direction: column; gap: var(--sp-5); align-items: flex-start; }

.panel__cabecera {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  align-items: flex-start;
}

.panel__usuario { margin: 0; font-size: 22px; line-height: 28px; font-weight: 600; color: var(--color-text-strong); }
.panel__email { margin: 0; color: var(--color-text-muted); }

/* Personajes por server (T-030): dos columnas desde 720 px (SPEC §3 V7). */
.personajes__grilla { display: grid; gap: var(--sp-6); grid-template-columns: 1fr; width: 100%; }
@media (min-width: 720px) { .personajes__grilla { grid-template-columns: 1fr 1fr; } }
.personajes { width: 100%; display: flex; flex-direction: column; gap: var(--sp-3); }
.personajes h2 { margin: 0; }
.personajes .data-table { margin-top: 0; }
.personajes .empty-state { margin-top: 0; }
.personajes__meta { margin: 0; font-size: 14px; line-height: 20px; color: var(--color-text-muted); }
.personajes__vacio { margin: 0; color: var(--color-text-muted); }

/* --- P-FIELD: campo de formulario (SPEC §2.6) ----------------------------- */

.formulario { display: flex; flex-direction: column; gap: var(--sp-5); margin: 0; text-align: left; }
.formulario .action-button { width: 100%; }

.campo { display: flex; flex-direction: column; gap: var(--sp-2); }

.campo__etiqueta { font-size: 14px; line-height: 20px; font-weight: 600; }

/* Las reglas y los rangos se ven SIEMPRE, antes de escribir, y no en un placeholder: un
   placeholder desaparece justo cuando la persona lo necesita (§2.6). */
.campo__ayuda { margin: 0; font-size: 14px; line-height: 20px; color: var(--color-text-muted); }

.campo__control {
  min-height: 44px;
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--color-border);
  border-radius: var(--r-sm);
  background: var(--color-surface);
  color: var(--color-text);
  font: inherit;
  font-size: 16px;
}
.campo__control--error { border: 2px solid var(--color-crit); }

/* El error nunca se comunica solo por color (§1.1 regla 4): lleva el ícono y el texto. */
.campo__error {
  display: flex;
  /* El ícono y el texto van en línea, y en un campo angosto el texto tiene que poder bajar de
     línea en vez de desbordar. */
  flex-wrap: wrap;
  gap: var(--sp-2);
  margin: 0;
  font-size: 14px;
  line-height: 20px;
  color: var(--color-crit);
}
.campo__error::before { content: "⚠"; }

/* --- Shell de administración (SPEC §2.12, §1.4) ---------------------------- */

/* Cabecera propia, en `--color-surface-raised`: quien está en la administración tiene que verlo
   sin leer la URL. */
.admin__cabecera {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--sp-3);
  min-height: 64px;
  padding: var(--sp-3) var(--sp-4);
  border-bottom: 1px solid var(--color-border);
  background-color: var(--color-surface-raised);
  /* T-105 · la misma franja de tres piezas que `.site-header`, con el porqué de cada línea escrito
     allá. Acá la capa de piso no es un degradado sino el color de arriba: quien está en la
     administración tiene que verlo sin leer la URL, con bucket y sin bucket. */
  background-image:
    var(--pieza-franja-izq, none),
    var(--pieza-franja-der, none),
    var(--pieza-franja-centro, none);
  background-repeat: no-repeat, no-repeat, repeat-x;
  background-position: left top, right top, left top;
  background-size: 67px 64px, 67px 64px, 201px 64px;
}

.admin__marca {
  font-family: var(--font-display);
  font-weight: 700;
  letter-spacing: .04em;
  color: var(--color-text-strong);
}

/* "Actuando como: <cuenta>" (§2.12): t-sm y siempre visible, porque todo queda auditado. */
.admin__chip {
  font-size: 14px;
  line-height: 20px;
  color: var(--color-text-muted);
}

/* Contenedor de 1200 px en administración (§1.4), con la navegación lateral de 240 px desde
   1024 px. Abajo de eso la navegación va arriba, en una sola fila que se desplaza. */
.admin {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: var(--sp-5);
  width: 100%;
  max-width: 1200px;
  margin: 0 auto;
  padding: var(--sp-6) var(--sp-4);
}

.admin__nav {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2) var(--sp-4);
}

.admin__cuerpo { flex: 1; }

@media (min-width: 1024px) {
  .admin { flex-direction: row; gap: var(--sp-6); padding: var(--sp-7) var(--sp-6); }
  .admin__nav { flex: none; width: 240px; flex-direction: column; gap: var(--sp-3); }
}

/* La grilla del índice (ST-70): tres columnas desde 1024, dos entre 720 y 1023, una abajo. */
.admin__grilla {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--sp-4);
  margin-top: var(--sp-5);
}

@media (min-width: 720px) { .admin__grilla { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1024px) { .admin__grilla { grid-template-columns: repeat(3, 1fr); } }

.admin__tarjeta {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  padding: var(--sp-5);
  border: 1px solid var(--color-border);
  border-radius: var(--r-md);
  background-color: var(--color-surface);
}

.admin__tarjeta:hover { border-color: var(--color-border-strong); }

/* El título es el enlace (§2.7: los enlaces van en acento); la línea de descripción no lo es. */
.admin__tarjeta-titulo { font-size: 18px; line-height: 28px; font-weight: 600; }
.admin__tarjeta-linea { font-size: 14px; line-height: 20px; color: var(--color-text-muted); }

/* --- P-TABLE (§2.9), P-EMPTY (§2.5) y el diálogo de confirmación (§2.8) ----- */

/* Encabezado de una sección de administración: el H1 y su acción primaria en la misma línea. */
.admin__encabezado {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  margin-bottom: var(--sp-5);
}

/* Las dos salidas del shell admin (§2.12, enmienda 2026-09-15). "Cerrar sesión" es un botón de
   formulario y no un enlace, así que se lo despinta para que se vea como el enlace que es. */
.admin__salidas { display: flex; align-items: center; gap: var(--sp-4); }
.admin__salidas form { margin: 0; }
.admin__salir {
  padding: 0;
  border: 0;
  background: none;
  color: var(--color-accent);
  font: inherit;
  cursor: pointer;
}
.admin__salir:hover { color: var(--color-accent-hover); }
.admin__salir:focus-visible { outline: 2px solid var(--color-focus); outline-offset: 2px; }

/* El enlace a administración en la cabecera pública: es navegación, no acción, así que va con la
   forma de los otros ítems y no con la de los botones. */
.site-header__admin { white-space: nowrap; }

.data-table {
  width: 100%;
  margin-top: var(--sp-5);
  border-collapse: collapse;
  text-align: left;
}

.data-table th {
  padding: var(--sp-2) var(--sp-3);
  border-bottom: 1px solid var(--color-border);
  white-space: nowrap;
  font-size: 14px;
  line-height: 20px;
  font-weight: 600;
  color: var(--color-text-muted);
}

.data-table td {
  height: 40px; /* filas de 40 px en administración (§2.9) */
  padding: var(--sp-2) var(--sp-3);
  border-bottom: 1px solid var(--color-rule);
  vertical-align: middle;
}

.data-table tbody tr:hover { background: var(--color-surface-raised); }

/* El `<caption>`: por defecto sale centrado arriba de la tabla y se lee como un título mal puesto.
   Es una aclaración de la tabla, así que se ve como una aclaración. */
.data-table caption {
  caption-side: top;
  margin-bottom: var(--sp-3);
  font-size: 14px;
  line-height: 20px;
  color: var(--color-text-muted);
  text-align: left;
}

/* Una tabla que es el resultado de la página —y no una lista de filas de administración— se
   encuadra como la caja que es: el resto del sitio tiene bordes y ésta también. */
.data-table--comparacion {
  border: 1px solid var(--color-border);
  border-radius: var(--r-md);
  background-color: var(--color-surface);
  overflow: hidden;
}
.data-table--comparacion { max-width: 840px; }
.data-table--comparacion th, .data-table--comparacion td { padding: var(--sp-3) var(--sp-4); }
/* La cifra va alineada a la derecha porque es una cifra, y su encabezado también: si no, el
   número queda a 200 px del nombre de su columna y no se sabe de cuál de los dos items es. */
.data-table--comparacion thead th:nth-child(2), .data-table--comparacion thead th:nth-child(3) { text-align: right; }
.data-table--comparacion col { width: auto; }
.data-table--comparacion td { height: 48px; }
.data-table--comparacion tbody th[scope="row"] { color: var(--color-text); font-weight: 600; }
.data-table--comparacion tbody tr:last-child th, .data-table--comparacion tbody tr:last-child td { border-bottom: 0; }
/* La columna del veredicto es la conclusión del renglón: es la que se lee, no las dos cifras. */
.data-table--comparacion tbody td:last-child { color: var(--color-text-strong); font-weight: 600; }
.data-table--comparacion .comparador__fila--incompleta td:last-child { color: var(--color-text-muted); font-weight: 400; }

.data-table__acciones { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-3); }
.data-table__acciones form { margin: 0; }
.data-table__acciones .action-button { width: auto; }
.data-table__accion { text-decoration: underline; }

/* En <720 las filas se apilan en tarjetas con la etiqueta de columna adelante (§2.9). La etiqueta
   sale del atributo `data-columna` de cada celda: sin eso, una tabla apilada es una lista de
   valores sueltos sin nombre. */
@media (max-width: 719px) {
  .data-table thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
  .data-table tbody tr {
    display: block;
    margin-bottom: var(--sp-4);
    border: 1px solid var(--color-border);
    border-radius: var(--r-md);
    background: var(--color-surface);
  }
  .data-table tbody th[scope="row"] {
    display: block;
    padding: var(--sp-3) var(--sp-3) 0;
    border-bottom: 0;
    color: var(--color-text-strong);
    font-size: 16px;
    line-height: 24px;
    white-space: normal;
  }
  .data-table td { display: flex; gap: var(--sp-3); height: auto; border-bottom: 0; }
  .data-table--comparacion th, .data-table--comparacion td { padding: var(--sp-2) var(--sp-3); }
  .data-table--comparacion tbody th[scope="row"] { padding: var(--sp-3) var(--sp-3) 0; }
  .data-table--comparacion tbody td { height: auto; }
  /* Apilada, la cifra va al lado de su etiqueta y no alineada a la derecha de la nada. */
  .data-table__numero { text-align: left; }
  .data-table td::before {
    content: attr(data-columna);
    flex: none;
    width: 40%;
    font-size: 12px;
    line-height: 20px;
    color: var(--color-text-muted);
  }
}

.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-7);
  margin-top: var(--sp-5);
  border: 1px solid var(--color-border);
  border-radius: var(--r-md);
  background-color: var(--color-surface);
  text-align: center;
}

/* Las seis pantallas vacías de la superficie pública —galería, videos, contacto, los dos rankings
   y conectados— eran la misma caja de texto en el medio de la nada (relevamiento 2026-09-24). El
   escudo de la casa las convierte en algo mirable sin agregar un solo archivo: es el mismo dibujo
   de `fragmentos/marca`, escrito acá como SVG en la propia hoja de estilos.
   No va en administración: ahí "no hay nada" es un dato de trabajo, no una pantalla que impresionar. */
.page-shell .empty-state::before {
  content: "";
  width: 64px;
  height: 64px;
  background: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32' fill='none'><path d='M16 1.5L28.5 5.3L28.5 16.2L27.2 20.5L24.2 24.2L20.6 27.2L16 29.8L11.4 27.2L7.8 24.2L4.8 20.5L3.5 16.2L3.5 5.3Z' stroke='%238A6E33' stroke-width='1.7' stroke-linejoin='round'/><path d='M16 7.4L20.2 12.6L16 17.8L11.8 12.6Z' fill='%238A6E33'/><path d='M9.4 17.4L16 21.8L22.6 17.4' stroke='%238A6E33' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'/><path d='M11.2 22.4L16 25.6L20.8 22.4' stroke='%238A6E33' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'/></svg>") no-repeat center / contain;
  opacity: .6;
}
.page-shell .empty-state { padding: var(--sp-8) var(--sp-5); max-width: 640px; margin-left: auto; margin-right: auto; }
.empty-state__titulo { margin: 0; font-size: 18px; line-height: 28px; font-weight: 600; }
.empty-state__cuerpo { margin: 0; color: var(--color-text-muted); }
.empty-state .action-button { width: auto; }

/* --- EmptyState, las tres variantes (T-100) --------------------------------
   El `.empty-state` de arriba es el rectángulo de 0.43.0 y se queda como está: lo usan
   administración y el panel de cuenta, donde "no hay nada" es un dato de trabajo. Lo que este
   ticket cambia son las SEIS pantallas vacías de la superficie pública, y las cambia por variante
   y no en el tronco, para que una pantalla que todavía no eligió su variante siga viéndose como
   ayer en vez de cambiar sola.

   Las tres comparten lo mismo: el vacío deja de ser una caja. Sin contorno, sin fondo propio, sin
   los 250 px de alto artificial y **sin centrado**: arranca donde arranca el contenido. El texto
   queda apoyado en la textura de piedra del `body`, así que su contraste se vuelve a medir sobre
   ese fondo y no sobre `--color-surface` (T-100, medición en qa/visual-0.44.0). */
.empty-state--collection, .empty-state--data, .empty-state--unavailable {
  align-items: flex-start;
  text-align: left;
  gap: var(--sp-2);
  padding: 0;
  margin: 0;
  max-width: 720px;
  border: 0;
  border-radius: 0;
  background: none;
}
/* La misma anulación para cuando están adentro del shell público, que es el selector que hoy le
   pone al vacío el escudo de 64 px, los 640 de ancho y el centrado horizontal. */
.page-shell .empty-state--collection,
.page-shell .empty-state--data,
.page-shell .empty-state--unavailable {
  padding: 0;
  max-width: 720px;
  margin-left: 0;
  margin-right: 0;
}
/* `collection` (galería, videos): el título de la página manda arriba y el vacío es chico debajo.
   Conserva el escudo, pero a 32 px: es el único de los tres donde el ícono aporta, porque lo que
   falta es una colección de imágenes y la pantalla no tiene ningún otro dato que mostrar. */
.page-shell .empty-state--collection::before { width: 32px; height: 32px; opacity: .5; }
/* `data` (ranking, conectados) y `unavailable` (contacto): sin ícono. En los dos el vacío ocupa el
   lugar del dato —la tabla, la lista, el canal— y un escudo ahí adentro lo disfrazaría de adorno. */
.page-shell .empty-state--data::before,
.page-shell .empty-state--unavailable::before { content: none; }
/* El riesgo que el diseñador declaró: que "cero jugadores" parezca información secundaria. Sigue
   siendo el resultado principal de la página, así que su título sube a t-lg sobre el texto fuerte
   —lo mismo que el nombre de un server en la portada—; lo que se fue son los 250 px de caja. */
.empty-state--data .empty-state__titulo { font-size: 22px; line-height: 28px; color: var(--color-text-strong); }

/* El diálogo de confirmación, servido como página (ver desactivar.html). */
.dialogo {
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
  max-width: 520px;
  padding: var(--sp-6);
  border: 1px solid var(--color-border);
  border-radius: var(--r-md);
  background-color: var(--color-surface);
}
.dialogo__botones { display: flex; flex-wrap: wrap; gap: var(--sp-3); }
.dialogo__botones form { margin: 0; }
.dialogo__botones .action-button { width: auto; }

/* Botón de peligro (§2.7): solo en confirmaciones de acciones consecuentes. */
.action-button--peligro {
  border: 1px solid var(--color-crit);
  background: var(--color-crit);
  color: #12141A;
  font-weight: 600;
}
.action-button--peligro:hover { background: var(--color-crit); color: #12141A; filter: brightness(1.08); }

/* Formulario ancho: el de la definición tiene muchos campos y no entra en la columna angosta de
   una tarjeta de entrada. */
.formulario--ancho { max-width: 560px; }
.formulario--ancho .action-button { width: auto; }

/* Radios y casillas: la etiqueta entera es el objetivo del clic. */
.campo__opcion { display: flex; align-items: center; gap: var(--sp-2); }
.campo__casillas { display: flex; flex-wrap: wrap; gap: var(--sp-2) var(--sp-4); }
.campo__control--lista { height: auto; padding: var(--sp-2); }

/* La lista de campos inválidos adentro del banner de ST-103. */
.banner__lista { margin: 0; padding-left: var(--sp-5); }
.banner__lista a { text-decoration: underline; }

/* El badge neutral: la definición inactiva (los otros tres ya estaban en §2.1). */
.badge--neutral { background: var(--color-neutral-bg); color: var(--color-neutral); }

/* --- Galería (T-028 admin, T-029 pública: V26, V23) ------------------------ */

.action-button--peligro-contorno { border: 1px solid var(--color-crit); color: var(--color-crit); width: auto; }
.action-button--peligro-contorno:hover { color: var(--color-crit); background: var(--color-crit-bg); }
.data-table__fila--nueva { box-shadow: inset 3px 0 0 var(--color-ok); }   /* b-state ok, ST-219 */
.galeria-admin__miniatura { display: block; width: 48px; height: 36px; object-fit: cover; border-radius: var(--r-sm); background: var(--color-surface-raised); }
.galeria-admin__confirmacion { display: block; width: 96px; height: 72px; object-fit: cover; border-radius: var(--r-sm); background: var(--color-surface-raised); }
.galeria-admin__falta { margin: var(--sp-1) 0 0; font-size: 14px; line-height: 20px; color: var(--color-warn); }
.galeria-admin__enlace { margin: 0; }

/* P-UPLOAD (§2.19) sin JavaScript: la zona punteada es el propio campo de archivo. */
.subida__control {
  min-height: 120px;
  padding: var(--sp-5) var(--sp-4);
  border: 1px dashed var(--color-border);
  border-radius: var(--r-md);
  background: var(--color-surface);
  color: var(--color-text);
}
.subida--error .subida__control { border: 2px solid var(--color-crit); }

.galeria { display: flex; flex-direction: column; gap: var(--sp-5); }
.galeria__intro { margin: 0; max-width: 72ch; font-size: 14px; line-height: 20px; color: var(--color-text-muted); }
.galeria__cargando { opacity: 0; animation: reproductor-lento 0s linear 3s forwards; }
/* P-MEDIA-GRID 4:3. La tarjeta entera es clicable: el enlace del título se estira por encima. */
.galeria__grilla { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--sp-4); grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); }
.galeria__tarjeta { position: relative; display: flex; flex-direction: column; gap: var(--sp-2); }
.galeria__marco { position: relative; aspect-ratio: 4 / 3; border-radius: var(--r-md); overflow: hidden; background: var(--color-surface-raised); border: 1px solid var(--color-border); }
.galeria__imagen { position: relative; z-index: 1; display: block; width: 100%; height: 100%; object-fit: cover; pointer-events: none; }
/* MENOR-5: detrás de la imagen (z-index 0 contra 1). Sin JavaScript no se sabe si una petición colgada va a
   terminar; lo que se ve pasa de nada a "Cargando…" a los 3 s y a "No disponible" a los 15 s. La imagen, al
   pintar, lo tapa; su respaldo de error, también. */
.galeria__espera { position: absolute; inset: 0; z-index: 0; display: flex; align-items: center; justify-content: center; font-size: 14px; line-height: 20px; pointer-events: none; }
.galeria__lento, .galeria__colgada { position: absolute; opacity: 0; }
.galeria__lento { color: var(--color-text-muted); animation: reproductor-lento 0s linear 3s forwards, galeria-ocultar 0s linear 15s forwards; }
.galeria__colgada { color: var(--color-warn); font-weight: 600; animation: reproductor-lento 0s linear 15s forwards; }
@keyframes galeria-ocultar { to { opacity: 0; } }
.galeria__titulo { font-size: 14px; line-height: 20px; color: var(--color-text); }
.galeria__titulo::after { content: ""; position: absolute; inset: 0; }
.galeria__titulo:hover { color: var(--color-accent-hover); }

/* ST-199: el respaldo del <object>. "Reintentar" queda por encima del enlace estirado. */
.no-disponible {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--sp-2);
  width: 100%; height: 100%; min-height: 120px;
  background: var(--color-warn-bg); color: var(--color-warn); pointer-events: auto;
}
.no-disponible__texto { font-size: 14px; line-height: 20px; font-weight: 600; }
.no-disponible--grande .no-disponible__texto { font-size: 18px; line-height: 28px; }
.no-disponible__reintentar { position: relative; z-index: 1; text-decoration: underline; }

/* P-LIGHTBOX como página. */
.lightbox { display: flex; flex-direction: column; gap: var(--sp-4); }
.lightbox__caja { position: relative; width: 100%; max-height: 80vh; margin: 0 auto; background: var(--color-surface-raised); border-radius: var(--r-md); overflow: hidden; min-height: 200px; }
.lightbox__imagen { position: relative; display: block; width: 100%; height: 100%; object-fit: contain; }
/* ST-197 sin JavaScript: detrás de la imagen, a los 3 s; la imagen, al pintar, la tapa. */
.lightbox__lento { position: absolute; left: var(--sp-4); right: var(--sp-4); top: 50%; margin: 0; text-align: center; font-size: 14px; line-height: 20px; color: var(--color-text-muted); opacity: 0; animation: reproductor-lento 0s linear 3s forwards; }
.lightbox__barra { display: flex; flex-direction: column; gap: var(--sp-2); padding: var(--sp-4); background-color: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--r-md); }
.lightbox__titulo { font-size: 18px; line-height: 28px; font-family: var(--font-text); letter-spacing: 0; font-weight: 600; }
.lightbox__tamano, .lightbox__nota { margin: 0; font-size: 14px; line-height: 20px; color: var(--color-text-muted); }
.lightbox__botones { display: flex; flex-wrap: wrap; gap: var(--sp-3); }
.lightbox__botones .action-button { width: auto; }

/* --- Cuentas (T-036 admin: V10) -------------------------------------------- */

/* SearchForm: etiqueta + ayuda + campo + botón, dentro de los 720 de lectura y formularios, que
   es el segundo de los tres modos de composición. */
.search-form { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--sp-3); margin: 0 0 var(--sp-5); max-width: 720px; }
.search-form__campo { flex: 1 1 320px; max-width: 560px; }
.search-form .action-button { width: auto; }
/* SearchForm (T-100): a 720 es horizontal; abajo de eso el campo ocupa todo el ancho y el botón va
   debajo, también entero. Antes el botón quedaba suelto y chico contra el margen izquierdo, que es
   el ancho en el que menos se acierta un objetivo angosto. */
@media (max-width: 719px) {
  .search-form { flex-direction: column; align-items: stretch; }
  /* `flex: 1 1 320px` en una columna son 320 px de ALTO, no de ancho: el campo medía 376 px de
     alto y el buscador ocupaba media pantalla. En columna la base la pone el contenido. */
  .search-form__campo { flex: 0 0 auto; max-width: none; }
  .search-form .action-button { width: 100%; }
}
.search-form__indicacion, .search-form__vacio { margin: 0; font-size: 14px; line-height: 20px; color: var(--color-text-muted); }
.search-form__chips { display: flex; flex-wrap: wrap; gap: var(--sp-1); }
/* Chip `t-xs` (§2.1): en qué campo coincidió. Neutro: informa, no comunica estado. */
.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
  padding: 0 var(--sp-2);
  border: 1px solid var(--color-border);
  border-radius: var(--r-full);
  font-size: 12px;
  line-height: 18px;
  color: var(--color-text-muted);
}

/* --- Sigilos de linaje (T-097) --------------------------------------------- */

/* El emblema de clase adentro de un chip. Hereda currentColor, y con eso el chip activo del filtro
   —que invierte a --color-on-accent— invierte también su sigilo: no hay una segunda regla de color
   que se pueda olvidar.

   No hay un color por clase, aunque el comparativo visual lo pedía. Siete acentos saturados nuevos
   es exactamente lo que la paleta de T-094 prohíbe, y el emblema ya distingue por FORMA con el
   nombre escrito al lado. */
.sigilo { display: block; flex: none; }
/* El sigilero son ocho <symbol> y no dibuja nada por sí mismo; sin esto igual reserva su caja. */
.sigilero { display: none; }
/* En un chip de 18 px de renglón, el lienzo de 16 lo estira. */
.chip .sigilo { width: 14px; height: 14px; }
/* El rombo de "Todas" es la marca del sitio, no un octavo linaje: va más callado que los siete. */
.sigilo--todas { opacity: .75; }

/* --- Mensaje global (T-039 admin: V14) ------------------------------------- */

.campo__control--area { height: auto; min-height: 88px; padding: var(--sp-2) var(--sp-3); resize: vertical; }
/* "N / límite" (t-sm num); en --color-crit cuando se pasa (ST-122). */
.campo__contador { margin: 0; font-size: 14px; line-height: 20px; color: var(--color-text-muted); text-align: right; }
.campo__contador--error { color: var(--color-crit); font-weight: 600; }

/* --- Ocurrencias (T-040 admin: V13) ---------------------------------------- */

.ocurrencias__nota, .ocurrencias__consultado { margin: 0 0 var(--sp-4); font-size: 14px; line-height: 20px; color: var(--color-text-muted); }
.ocurrencias__vivo { margin: 0 0 var(--sp-6); }
.ocurrencias__vivo h2 { margin: 0 0 var(--sp-3); font-size: 18px; line-height: 28px; }
.ocurrencias__sin-efecto, .ocurrencias__efecto-titulo, .ocurrencias__zonas { margin: 0 0 var(--sp-2); }
.ocurrencias__zonas { font-size: 14px; line-height: 20px; }
.ocurrencias__efecto { margin: 0 0 var(--sp-3); }
.ocurrencias__linea { margin: var(--sp-1) 0 0; font-size: 14px; line-height: 20px; color: var(--color-text-muted); }
.ocurrencias__linea--warn { color: var(--color-warn); }
.ocurrencias__detalle { margin: var(--sp-1) 0 0; font-size: 14px; line-height: 20px; }
.ocurrencias__reintento { margin: 0 0 var(--sp-4); }
.ocurrencias__reintento .action-button { width: auto; }

/* --- Catálogo (T-025: V21; recompuesto por T-101 con las vistas 01 y 02) ----
   Tres URL y una hoja: la puerta (`/catalogo`, selector y sets), el catálogo de una clase
   (`?clase=`) y la vista completa (`/catalogo/items`). Lo que la puerta ya NO tiene son los 245
   items, y con ellos se fueron los 141.853 px de alto en teléfono (AC-169 enmendado).

   Las cajas de esta vista ya no son recetas propias: el set y el item son `DataCard` en escritorio
   y `DataRow` en teléfono, que es exactamente "tarjeta arriba de 720, fila abajo" y lo que el
   diseñador pidió para las dos. Acá quedan solo los interiores —el marco del dibujo, los precios—
   y las dos geometrías que el sistema no puede saber: el ancho reservado de la imagen y la grilla
   densa. */

.catalogo { display: flex; flex-direction: column; gap: var(--sp-5); }
/* El encabezado del selector y el de cada sección no llevan margen propio: la columna ya separa
   con su `gap`, y el margen del componente sumaba una segunda separación de 16 px encima. */
.catalogo > .section-header { margin-bottom: 0; }
/* El enlace de vuelta de la vista completa es un objetivo táctil, no un renglón de metadata: dentro
   de un contenedor flex un `<a>` se blockifica y deja de estar exento del mínimo de 44 px que WCAG
   2.5.8 le perdona a un enlace en línea. Medido con el sensor: 20 px de alto sin esta línea. */
.catalogo .page-header__meta a { display: inline-flex; align-items: center; min-height: 44px; }
.catalogo__sin-sets { margin: 0; font-size: 14px; line-height: 20px; color: var(--color-neutral); }
/* "De qué server es cada dato" (T-126): el mismo P-SCOPE que `/drops` lleva al pie, con la misma
   geometría y el mismo token informativo —sin `b-state` y sin ícono de alerta, porque no avisa de
   nada: declara de dónde sale cada número—. Comparte maquetado con `.drops__no-cubre` a propósito: en
   las dos páginas es la misma pieza de la spec (§2.24) y verse distinto sería decir que son cosas
   distintas. El enlace queda EN LÍNEA adentro de su renglón, que es lo que lo exime del mínimo de
   44 px (WCAG 2.5.8): el `<li>` es el item flex y no un contenedor flex, así que el `<a>` no se
   blockifica — el gotcha de T-116/T-123 al derecho. */
.catalogo__servers { margin: 0; max-width: 74ch; padding-left: var(--sp-5); display: flex; flex-direction: column; gap: var(--sp-2); font-size: 14px; line-height: 20px; color: var(--color-text-muted); }
.catalogo__servers strong { color: var(--color-text); }
/* LAS DOS PROFUNDIZACIONES DEL CATÁLOGO (T-129): drops y comparador, desde que salieron de la barra
   de navegación. Son `DataRow` del sistema y no una pieza nueva —la geometría, el mínimo táctil y la
   señal de enlace ya están resueltos ahí—; lo único propio es la grilla, que es la misma receta de
   `.ficha__lugares`: una columna en teléfono y dos de 720 para arriba, donde son dos tarjetas
   hermanas del mismo tamaño. */
.catalogo__profundizaciones { display: grid; gap: var(--sp-2); grid-template-columns: 1fr; }
@media (min-width: 720px) {
  .catalogo__profundizaciones { gap: var(--sp-3); grid-template-columns: repeat(2, 1fr); }
}

/* FilterPill (T-100). El selector de clase es el bloque principal de la vista 01 y va SIN CAJA:
   son siete enlaces a esta misma página, que es lo que deja la página sin una línea de JS. */
.filter-pills { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2); }
.filter-pill {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  /* T-100: la pastilla medía 30 px de alto (20 de renglón + 4+4 de padding + 2 de borde) y es un
     objetivo táctil. El mínimo de WCAG 2.5.8, que el resto del sitio ya cumple, son 44. Sube por
     `min-height` y NO por padding: con padding crecía también a lo ancho y los chips de /catalogo
     pasaban a dos filas —eso es lo que T-099 midió en 38 px de alto extra—. */
  min-height: 44px;
  padding: var(--sp-1) var(--sp-3);
  border: 1px solid var(--color-border);
  border-radius: var(--r-full);
  font-size: 14px;
  line-height: 20px;
  font-weight: 500;
  color: var(--color-text);
  /* T-099 · pastilla. Nine-slice en X, inset medido 27 px sobre el archivo de 298x80, dibujado a
     8 px por lo mismo que el herraje de los botones: con 13,5 el chip activo tenía la última letra
     encima del bronce, y agrandarle el padding pasaba los chips a dos filas y le sumaba 38 px de
     alto a `/catalogo`. Con `fill`: la piedra del medio es la pastilla, y el texto da 8,18:1. */
  border-image: var(--pieza-pastilla, none) 0 27 fill / 0 8px / 0 stretch;
}
.filter-pill:hover { border-color: var(--color-border-strong); }
/* El chip activo, SIN `fill` por lo mismo que el botón de oro: sobre la pastilla dorada pintada el
   texto oscuro se queda en 2,46:1, y sobre --color-accent da 8,98:1. Entra el herraje, el oro lo
   sigue poniendo el token. */
.filter-pill--activo { background: var(--color-accent); border-color: var(--color-accent); color: var(--color-on-accent);
                       border-image-source: var(--pieza-pastilla-activa, none); border-image-slice: 0 27; }
.filter-pill--activo:hover { color: var(--color-on-accent); }
/* Los dos enlaces del selector: van en la misma fila que las pastillas y son igual de tocables que
   ellas (T-100). "Ver todos los items" es un enlace de texto y no una octava pastilla a propósito:
   el riesgo declarado de la vista 01 es que compita con elegir clase. */
.filter-pill__quitar, .filter-pill__todos {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  min-height: 44px; font-size: 14px; line-height: 20px; text-decoration: underline;
}
/* En teléfono el selector es una grilla de dos columnas y no una fila que se desplaza a lo ancho.
   La fila con `overflow-x` dejaba cuatro de las siete clases fuera de la pantalla, y la puerta del
   catálogo es justo la vista donde elegir la clase es la acción principal. Los dos enlaces ocupan
   el ancho entero: no son una clase más. */
@media (max-width: 719px) {
  .filter-pills { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* Con el descendiente y no con `.filter-pill` a secas: los chips de resultado del buscador de
     /drops son la misma pastilla y viven en otro contenedor, y no se los centra de rebote. */
  .filter-pills .filter-pill { justify-content: center; text-align: center; }
  .filter-pill__quitar, .filter-pill__todos { grid-column: 1 / -1; }
}

/* Sets: dos columnas desde 720 (§3 V21), fila en teléfono. La caja y la fila las pone el sistema
   (`data-card data-row`); acá va el interior. Los dos hijos ocupan el ancho entero para que en la
   geometría de fila el nombre quede arriba y las piezas debajo, y no uno al lado del otro. */
.sets { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: 1fr; }
@media (min-width: 720px) { .sets { grid-template-columns: repeat(2, 1fr); gap: var(--sp-4); } }
.set__nombre { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2); margin: 0; font-size: 18px; line-height: 28px; font-weight: 600; color: var(--color-text-strong); }
/* Los enlaces de una pieza quedaban a 4-8 px entre sí en filas de tres o cuatro: errar el toque
   era lo esperable. Con la separación de sp-2/sp-4 y el alto mínimo de abajo, no.

   T-116 · 32 → 44 px, y el número que lo decidió. Estos 185 enlaces eran la única excepción
   silenciosa a la regla de 44 px del sitio: pasaban WCAG 2.5.8 (que pide 24) pero no la norma que
   este proyecto se puso, aplicada en los otros siete componentes del bloque de abajo de esta hoja.
   La regla del ticket era medir antes de elegir, porque los 5.379 px de `/catalogo` en teléfono son
   el logro de T-101 y no se tiran por una regla interna. Medido con el sensor visual a 390 px:
   con 32 px, 5.339 px de documento (5.056 de sección) y 185 objetivos por debajo del mínimo; con
   44 px, 6.191 px (5.908) y ninguno. El costo es 852 px, un 16 %, y la puerta del catálogo queda
   bien por debajo de los 8.000 px que el ticket fijó como techo — así que se suben. El +12 px no es
   por enlace sino por RENGLÓN de la envoltura: 185 × 12 habrían sido 2.220. */
.set__piezas { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: var(--sp-2) var(--sp-4); font-size: 14px; line-height: 20px; }
.set__piezas a { display: inline-flex; align-items: center; min-height: 44px; }
/* `width` y no `flex-basis`: en la geometría de fila el contenedor es `row wrap` y el ancho entero
   es lo que manda cada hijo a su propio renglón —nombre arriba, piezas debajo—; en la de tarjeta el
   contenedor es una columna y ahí `flex-basis: 100%` sería el 100 % del ALTO, que es otra cosa. */
.set__nombre, .set__piezas { width: 100%; }

/* Items. En escritorio, grilla densa con el dibujo mandando; en teléfono, filas con la imagen de
   ancho reservado fijo. El marco bajó de 220 px a 184 px de columna mínima (T-094): con los PNG
   encuadrados el dibujo ya llena su lienzo, y un marco más chico es menos agrandado del asset y
   más items por pantalla.

   DOS VISTAS, UN JUEGO DE REGLAS (T-120). Esta grilla y esta tarjeta las usan el catálogo (V21) y la
   grilla de items del mapa acotado de /drops (V28, ST-310). No hay una segunda receta y no hace falta
   ninguna clase nueva: el marcado es el mismo fragmento (`templates/fragmentos/item.html`) con las
   mismas dos clases, así que todo lo que está medido acá —la hornacina cuadrada, los 184 px de
   columna, el ancho reservado del teléfono— vale igual en las dos. En drops la tarjeta es un `<a>` y
   en el catálogo un `<li>`: lo único que eso cambia es el `a.data-card:hover` que el sistema ya
   declara y la señal de `.data-row__ir`, que el sistema esconde de 720 para arriba.

   El hueco central lo sacaron los precios: ya no se empujan al pie con `margin: auto`. Lo que NO
   lo saca es `align-items: start` en la grilla, y se probó: medido con `getComputedStyle`, sin el
   estirado la hornacina deja de ser cuadrada —158x122, 158x149, 158x158 según el sprite— y cada
   dibujo sale de un tamaño distinto, porque el `aspect-ratio` del marco necesita que la tarjeta
   tenga altura para apoyarse. Y no ahorraba nada: el alto de la fila de la grilla lo fija la
   tarjeta más alta igual. Así que la grilla estira, como en 0.47.0, y el dibujo manda. */
.items { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: 1fr; }
@media (min-width: 720px) {
  .items { grid-template-columns: repeat(auto-fill, minmax(184px, 1fr)); gap: var(--sp-3); }
}
/* T-099 · nicho-item: la hornacina iluminada, la pieza que más cambia la vista patrón. El radial
   de siempre queda debajo de piso, así que sin bucket el marco es el de 0.41.0. */
.item__marco { display: flex; align-items: center; justify-content: center; aspect-ratio: 1 / 1; padding: var(--sp-2); border-radius: var(--r-md);
               background-color: var(--color-surface-raised);
               background-image: var(--pieza-nicho-item, none), radial-gradient(circle at 50% 42%, #2E2619, var(--color-surface-raised) 70%);
               background-size: cover, auto; background-position: center, center; overflow: hidden; }
/* El PNG viene cuadrado y con su margen adentro (ImagenesDeItems.encuadrar), así que llenar el
   marco es exactamente mostrarlo a su encuadre: `contain` no recorta nada. */
.item__imagen { width: 100%; height: 100%; object-fit: contain; }
/* ST-185. La hornacina va a plena luz —es lo que hace que el sprite se lea— y este marcador es el
   único texto que cae adentro: por eso lleva su propia placa oscura. Sin ella, sobre el punto más
   claro de la pieza, el texto medía 1,13:1. */
.item__sin-imagen { display: flex; flex-direction: column; align-items: center; gap: var(--sp-1); color: var(--color-neutral);
                    padding: var(--sp-2) var(--sp-3); border-radius: var(--r-sm); background: rgba(12, 10, 7, .82); }
.item__sin-imagen-texto { font-size: 12px; line-height: 18px; }
/* La columna de texto de la ficha. Va aparte del marco porque en teléfono el marco es un ancho
   reservado fijo al costado y todo lo demás es la columna de al lado: así, si la imagen no llega,
   el texto sigue alineado con el de las otras filas (vista 02). */
.item__datos { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: var(--sp-2); }
.item__nombre { margin: 0; font-size: 16px; line-height: 24px; font-weight: 600; color: var(--color-text-strong); }
.item__clases { display: flex; flex-wrap: wrap; gap: var(--sp-1); margin: 0; }
.item__linea { margin: 0; font-size: 14px; line-height: 20px; color: var(--color-text-muted); }
/* Sin `margin: auto 0 0`: ese `auto` era el hueco central de la vista 02 —empujaba los precios al
   pie de una tarjeta estirada— y el diseñador lo pidió afuera. */
.item__precios { list-style: none; margin: 0; padding: var(--sp-2) 0 0; display: flex; flex-direction: column; gap: var(--sp-1); border-top: 1px solid var(--color-rule); }
.item__precio { font-size: 16px; line-height: 24px; font-weight: 600; color: var(--color-text-strong); }
.item__variante { font-size: 12px; line-height: 18px; color: var(--color-text-muted); }
.item__requisito-variante { display: block; font-size: 12px; line-height: 18px; color: var(--color-text-strong); }
.item__sin-precio { margin: 0; padding: var(--sp-2) 0 0; border-top: 1px solid var(--color-rule); font-size: 14px; line-height: 20px; color: var(--color-text-muted); }

/* La fila del teléfono. Los selectores llevan las dos clases porque el sistema declara `.data-row`
   después de esta sección y en un empate de especificidad gana el que está más abajo.
   72 px de ancho reservado: es lo que hace que el texto de todas las filas empiece en la misma
   columna con imagen y sin imagen. El ícono del marcador "Sin imagen" se va —es decorativo y el
   texto es el que tiene que quedar—, y el texto envuelve en dos renglones adentro del marco. */
/* La tarjeta del escritorio es una columna que NO envuelve. `DataRow` deja `flex-wrap: wrap`
   puesto y el bloque de 720 para arriba no lo revierte, y con envoltura el `aspect-ratio` de la
   hornacina no se resuelve: medido con `getComputedStyle`, el marco salía 158x122, 158x149 o
   158x158 según el sprite y cada dibujo de un tamaño distinto, en vez de los 158x158 iguales de
   0.47.0. Con `nowrap` vuelven a ser todos iguales, medido igual. Va con las dos clases porque
   `.data-row` se declara después de esta sección y en un empate gana la de abajo. */
@media (min-width: 720px) {
  .data-row.item, .data-row.set { flex-wrap: nowrap; }
}

/* `nowrap` EN LA FILA DEL TELÉFONO, Y LO PIDIÓ UNA MEDICIÓN (T-127). Desde que la tarjeta del
   catálogo tiene destino lleva la señal de `DataRow`, y con la envoltura que `.data-row` deja puesta
   ese chevron se iba a un renglón propio en casi todas las filas: medido a 390 px, `/catalogo/items`
   pasaba de 121.569 a 126.297 px —8 px por item, 4.728 en total— y `?clase=dark-knight` de 50.291 a
   52.223. Con `nowrap` la señal se queda al lado del texto (`item__datos` ya es `min-width: 0`, así
   que encoge en vez de empujar) y la vista vuelve a 121.897 y 50.251: la flecha cuesta 328 px en la
   página más larga del sitio y devuelve 40 en la filtrada. Es el mismo remedio que el `order` que el
   sistema le puso al chevron de `/drops`, por el mismo motivo y con el mismo número detrás.
   No cambia la grilla sin ficha: `?mapa=atlans&clase=dark-knight` mide 6.939 con y sin la regla. */
@media (max-width: 719px) {
  .data-row.item { align-items: flex-start; flex-wrap: nowrap; padding: var(--sp-3) var(--sp-1); }
  .data-row.item .item__marco { flex: none; width: 72px; padding: var(--sp-1); }
  .data-row.item .item__sin-imagen { padding: var(--sp-1); }
  .data-row.item .item__sin-imagen svg { display: none; }
  .data-row.set { align-items: flex-start; padding: var(--sp-3) var(--sp-1); }
}

/* LA GRILLA SIN FICHA: la misma tarjeta, y en teléfono la fila que `DataCard` promete (T-122).

   Qué la pide. La grilla de items del mapa acotado de /drops dibuja la tarjeta compartida con
   `conFicha=false`: sin clases, requisitos, valor base ni precios, la fila no lleva más que el
   dibujo y el nombre. En el catálogo el alto de la fila lo fijan esas cuatro líneas —medido,
   `?clase=dark-knight` da 24.017 px de sección para 97 items, o sea 247 px por fila— y los 72 px del
   marco no deciden nada; acá el nombre mide 24 px y el marco decide TODO. Medido a
   390 px sobre 0.55.0: cada fila 97 px (72 del marco + 24 de padding + el divisorio) y
   `?mapa=atlans&clase=dark-knight` —el peor caso del sitio: el mapa con más items y la clase con más
   compatibles, 100 items— 12.422 px de documento y 12.139 de sección. Sin filtrar, ese mapa mide
   2.831 / 2.548: filtrar por la propia clase, que es el uso más obvio del filtro, hacía la página
   4,4 veces más larga que no filtrar.

   Qué se hace. El dibujo baja a 44 px y la fila se queda en el mínimo de `DataRow`, que es el mismo
   44: con el marco cuadrado ocupando el alto entero del renglón, el dibujo es el más grande que cabe
   en la fila más corta que la norma permite, y los dos números coinciden en el mismo. Por eso el
   padding vertical es 0 y no `--sp-2`: 8 px arriba y abajo dejarían la fila en 61 px sin agrandar el
   dibujo ni un píxel, y la separación entre filas ya la da el `border-bottom` de `--color-rule`, que
   es el divisorio del sistema. Medido después: fila 45 px justos (44 del marco cuadrado + el
   divisorio), `?mapa=atlans&clase=dark-knight` 7.222 / 6.939 —un 42 % menos— y `?mapa=lorencia&
   clase=dark-knight` 6.796 → 4.612. El dibujo sigue estando y sigue siendo cuadrado: 44 x 44,
   medido con `getComputedStyle`, que es como se descubrió el defecto del `aspect-ratio` en T-101.

   VA EN LA GRILLA Y NO EN LA TARJETA, a propósito. La tarjeta es un solo fragmento y su marcado es
   byte por byte el mismo en las dos páginas (`DropsGrillaDeItemsTest.laTarjetaEsElMismoComponente`);
   lo que difiere de verdad es el contenedor, que lo pone cada página. Bajar el marco en
   `.data-row.item` habría encogido también el dibujo del catálogo, donde no ahorra un píxel —el alto
   lo fijan las cuatro líneas— y donde el dibujo es lo único que permite recorrer 245 items sin leer.
   Tres clases contra dos, así que gana sin depender del orden (el gotcha de T-109).

   LO QUE ESTE BLOQUE NO ALCANZA, con su número. El ticket pedía que el peor caso quedara POR DEBAJO
   del mapa sin filtrar (2.548 px de sección) y eso no se puede: el armazón de la vista filtrada
   —cabecera, banner, buscador, selector y el P-SCOPE del pie— ya mide 2.440 px sin un solo item, y
   los 100 items obligan a 100 objetivos táctiles de 44 px, que son 4.500 px con el divisorio. El
   piso medido es 6.939 y la meta 2.548: sobran 1,7 páginas enteras, y los 4.500 px de los objetivos
   táctiles solos ya son 1,8 veces el mapa entero sin filtrar. La meta salió de suponer que la
   geometría de fila alcanzaba, sin contar que 100 filas de 44 px no caben en el alto de una página
   que tiene siete renglones plegados — el mismo error de escala que el ticket le señala al informe
   de T-120, una vuelta más arriba. Bajar de ahí pide esconder items o paginar, y las dos cosas están
   descartadas en el ticket. */
@media (max-width: 719px) {
  .items--sin-ficha .data-row.item { align-items: center; padding: 0 var(--sp-1); }
  .items--sin-ficha .data-row.item .item__marco { width: 44px; padding: 0; }
  /* Sin el padding del marco, el marcador de ST-185 se apoyaría en el filo de la hornacina. */
  .items--sin-ficha .data-row.item .item__sin-imagen { padding: 0 var(--sp-1); }
}

/* --- Detalle del personaje y sus operaciones (T-031/T-032/T-033: V18, V19, V20) ------------- */

.personaje { display: flex; flex-direction: column; gap: var(--sp-4); }
.personaje > h1 { margin: 0; }
.migas { margin: 0; font-size: 14px; line-height: 20px; color: var(--color-text-muted); }
.personaje__cabecera { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-3); }
.personaje__cabecera h1 { margin: 0; }
.personaje__meta { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--sp-3); }
.personaje__meta p { margin: 0; font-size: 14px; line-height: 20px; color: var(--color-text-muted); }
.personaje__meta .action-button { width: auto; }

/* La zona de acciones de ST-147. Los botones no se apilan a ancho completo: son tres cosas
   distintas y una de ellas cobra moneda. */
.acciones { display: flex; flex-wrap: wrap; gap: var(--sp-3); }
.acciones .action-button { width: auto; }

/* ST-148: el botón que no se puede apretar. Es un <span> y no un <button disabled> porque lo que
   había en su lugar era un enlace, y un enlace deshabilitado no existe en HTML. El estado se
   comunica con `aria-disabled`, con el color y —sobre todo— con la línea de causa de abajo: el
   color nunca comunica solo (§1.1 regla 4). */
.action-button--inerte { opacity: 0.55; cursor: not-allowed; }
.causa { display: flex; align-items: center; gap: var(--sp-2); margin: 0; font-size: 14px; line-height: 20px; color: var(--color-warn); }

/* P-KPI (§2.16): dos por fila en angosto, cuatro en ancho. */
.kpis { display: grid; gap: var(--sp-3); grid-template-columns: repeat(2, 1fr); }
@media (min-width: 720px) { .kpis { grid-template-columns: repeat(4, 1fr); } }
.kpi { padding: var(--sp-4); border: 1px solid var(--color-border); border-radius: var(--r-md); background-color: var(--color-surface); }
.kpi__etiqueta { margin: 0; font-size: 14px; line-height: 20px; color: var(--color-text-muted); }
.kpi__valor { margin: var(--sp-1) 0 0; font-size: 36px; line-height: 44px; font-weight: 600; color: var(--color-text-strong); }
.kpi__sufijo { margin: var(--sp-1) 0 0; font-size: 14px; line-height: 20px; color: var(--color-text-muted); }

.personaje__stats h2, .personaje__reset h2 { margin: 0 0 var(--sp-3); }
.personaje__costo { margin: 0; font-size: 18px; line-height: 28px; font-weight: 600; color: var(--color-text-strong); }
.personaje__nota { margin: var(--sp-2) 0 0; font-size: 14px; line-height: 20px; color: var(--color-text-muted); }
.personaje__disponibles { margin: 0; font-size: 18px; line-height: 28px; }

/* La nota del KPI "Puntos sin asignar" y el chip "actualizado" (§2.16, T-034). El segundo número
   —el 65.535 del cliente— vive en `.kpi__sufijo`, o sea chico y etiquetado: nunca puede leerse
   como si fuera el saldo real. */
.kpi__chip {
  margin-left: var(--sp-2);
  padding: 1px var(--sp-2);
  border-radius: var(--r-sm);
  background: var(--color-ok);
  color: #12141A;
  font-size: 12px;
  line-height: 18px;
  font-weight: 600;
}

/* La cifra con la que se decide el reset de stats (V18-X): mismo P-KPI del detalle, adentro del
   diálogo. Etiqueta arriba, número desnudo abajo; nunca una oración en 36 px. */
.dialogo__kpi { padding: var(--sp-4); border: 1px solid var(--color-border); border-radius: var(--r-md); }

/* La fila de P-STATS que ya está en el mínimo de su clase: no libera nada (§2.17). */
.data-table__fila--apagada { color: var(--color-text-muted); }

/* ST-233: la causa de una acción deshabilitada por condición, que no es un error. */
.causa--neutral { color: var(--color-text-muted); }

/* El resumen del diálogo (V19, V20): qué va a pasar, en una lista, antes de confirmar. */
.dialogo__resumen { margin: 0; padding-left: var(--sp-5); display: flex; flex-direction: column; gap: var(--sp-1); }
.dialogo__irreversible { margin: 0; font-weight: 600; color: var(--color-text-strong); }
.dialogo { max-width: none; }

/* El totalizador de P-STATS editable (§2.17). */
.totalizador { margin: 0; font-size: 16px; line-height: 24px; color: var(--color-text-muted); }
.totalizador--error { color: var(--color-crit); font-weight: 600; }

/* --- Drops por mapa (T-087) ------------------------------------------------- */

.drops { display: flex; flex-direction: column; gap: var(--sp-5); }
.drops__seccion h2 { margin: 0 0 var(--sp-4); font-size: 24px; line-height: 32px; }
.drops__nota { margin: 0 0 var(--sp-3); max-width: 72ch; font-size: 14px; line-height: 20px; }
.drops__cuantos, .mejora__detalle { font-size: 12px; line-height: 18px; color: var(--color-text-muted); }

/* Los resultados del buscador (T-109). Son EL resultado de la búsqueda y no un filtro secundario,
   así que dejan de ser una fila de pastillas y pasan a ser una grilla de tarjetas con el dibujo
   grande: la idea es reconocer el item sin leer el nombre.

   `DataCard` sola —caja en los dos anchos— porque el diseñador pidió dos columnas en teléfono y no
   una fila de ancho completo, que era la alternativa. Los números salen de la grilla y no de un
   `width` a mano: con 190 de mínimo y 12 de hueco entran cinco columnas de 192 px en los 1008 de
   contenido (seis pedirían 1.050) y dos de 173 en los 358 del teléfono. Los 192 caen dentro de los
   180-210 pedidos. */
.drops__resultados { display: grid; gap: var(--sp-3); grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); }
.data-card.drops__resultado {
  flex-direction: row;
  /* La `DataCard` es una columna que NO envuelve, y acá se declara igual: en un flex que envuelve
     Chromium le calcula la altura al hijo por su contenido y un `aspect-ratio` deja de resolverse
     (T-101). Acá no hay `aspect-ratio`, pero el dibujo es de alto fijo y no tiene por qué caer a
     un segundo renglón cuando el nombre es largo. */
  flex-wrap: nowrap;
  align-items: center;
  gap: var(--sp-3);
  min-height: 96px;
  padding: var(--sp-3);
}
/* El sprite a 56 px: adentro de los 48-64 que pidió el diseñador. `flex: none` para que el nombre
   largo no se lo coma, y ST-185 sin hueco — el item sin asset no reserva la caja ni escribe "Sin
   imagen", que es del marco de la ficha (§2.25). */
.drops__resultado-imagen { flex: none; width: 56px; height: 56px; object-fit: contain; }
.data-card__nombre.drops__resultado-nombre { font-size: 16px; line-height: 22px; overflow-wrap: anywhere; }
/* En teléfono, dos columnas de 175 px con el dibujo a 48 a la izquierda y sin alto mínimo propio:
   la tarjeta mide lo que mide su contenido (64 px), que sigue estando por encima de los 44 del
   objetivo táctil. Así cada coincidencia cuesta 36 px de alto contra los 52 que costaba como
   pastilla —una por renglón—, y la vista de búsqueda se acorta a partir de la segunda. */
@media (max-width: 719px) {
  .drops__resultados { gap: var(--sp-2); grid-template-columns: repeat(2, 1fr); }
  .data-card.drops__resultado { min-height: 0; gap: var(--sp-2); padding: var(--sp-2); }
  .drops__resultado-imagen { width: 48px; height: 48px; }
  .data-card__nombre.drops__resultado-nombre { font-size: 14px; line-height: 20px; }
}

/* La lista de mapas, que es la entrada principal (el usuario eligió "mapa primero"). Cada entrada
   es una DataCard que en teléfono es una DataRow: la caja y la geometría de la fila las pone el
   sistema compartido, acá queda solo la grilla. Cuatro columnas a 1024 (240 de mínimo dentro de
   los 1008 de contenido) y una sola columna abajo de 720. */
/* T-111 · El selector de los dos filtros del mapa (AC-179). No trae geometría nueva: son las
   `FilterPill` del sistema en dos filas, cada una con su etiqueta escrita arriba. Sin caja a
   propósito (regla de T-100): son controles sobre el fondo, no una unidad comparable. En teléfono
   las pastillas pasan a la grilla de dos columnas que ya define el componente. */
.drops__filtros { display: flex; flex-direction: column; gap: var(--sp-4); margin: 0 0 var(--sp-5); }
/* La etiqueta de una fila de pastillas. La comparten `/drops` —sus dos filas, la del mapa y la del
   ancla— y la ficha del item desde T-130: es la MISMA pieza, así que es la misma regla y no una
   segunda declaración con los mismos cinco valores (si se desincronizan, el selector se ve distinto
   en dos páginas que contestan la misma pregunta). */
.drops__filtro-etiqueta, .ficha__filtro-etiqueta { margin: 0 0 var(--sp-2); font-size: 14px; line-height: 20px; font-weight: 600; color: var(--color-text-strong); }
.drops__filtros .drops__nota { margin: 0; }

.drops__mapas { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--sp-3); grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); }
/* En teléfono las filas van pegadas y separadas por el divisorio interno: el `gap` de la grilla
   dejaba 12 px entre filas sin caja y la lista se leía como veinte cosas sueltas. */
@media (max-width: 719px) {
  .drops__mapas { gap: 0; grid-template-columns: 1fr; }
  /* Vista 09: la frecuencia debajo del nombre y del porcentaje, no al lado. Es la condición del
     número grande y tiene que quedar pegada a él (riesgo declarado). Son las filas de 64-72 px
     que pidió el diseñador. En el índice de mapas (vista 07) el dato entra en el mismo renglón y
     la fila se queda en 44, que es lo que hace bajar el alto de /drops. */
  .drops__mapas--item .data-row__dato { order: 3; flex: 1 0 100%; margin-left: 0; text-align: left; }
}

/* Un monstruo por desplegable. Es un <details> y no un acordeón con JavaScript: en un mapa con 27
   monstruos de 100 items cada uno, la tabla entera abierta es una pared de 2.700 filas.

   T-100 (vista 10): el desplegable deja de ser una tarjeta y su `summary` pasa a ser una DataRow,
   el mismo componente en los dos anchos. "Señal de enlace" se resuelve con el chevron de
   `.data-row__ir`, que acá apunta hacia abajo cuando el desplegable está abierto: no hay una URL
   por monstruo y este ticket no inventa una. */
.drops__monstruo { margin-bottom: 0; }
.drops__monstruo > summary { cursor: pointer; font-size: 16px; line-height: 24px; }
.drops__monstruo > summary::-webkit-details-marker { display: none; }
.drops__monstruo > summary::marker { content: ""; }
.drops__monstruo > summary:focus-visible { outline: 2px solid var(--color-focus); outline-offset: -2px; }
.drops__monstruo[open] > summary { border-bottom-color: var(--color-border); }
.drops__monstruo[open] > summary .data-row__ir { transform: rotate(135deg); }
.drops__monstruo > .data-table { margin-top: 0; margin-bottom: var(--sp-5); }

/* LOS 605 ENLACES DE ITEM DE LA TABLA DE CAÍDAS, a 44 px (T-123).

   El hallazgo, y vale más que el arreglo: en `/drops?mapa=atlans` a 390 px estos enlaces son 605 y
   medían 24 px —por debajo de los 44 que este proyecto se puso y aplicó en todo el resto (T-116)—, y
   el sensor de táctiles reportaba CERO, porque los siete `<details>` de la página vienen cerrados y
   un barrido que respeta la visibilidad pasa de largo. El contenido existe y se toca: basta que
   alguien abra el desplegable. Desde T-123 `qa/sensor-pantalla.js` abre los `<details>` en una
   segunda pasada y los mide; sin eso, esto seguiría sin verse.

   Por qué son 44 y no una excepción declarada, con el número: en la página como se sirve el costo es
   CERO —los siete desplegables están cerrados y el alto no se mueve: 2.831 px antes y 2.831 después—
   y aparece solo adentro de un desplegable abierto, a 20 px por renglón. Abrir el monstruo más
   grande de Atlans (Bahamut, 99 caídas) cuesta +1.780 px sobre 20.717, y los siete abiertos a la vez
   —una página que nadie mira— +12.100 sobre 126.041, un 9,6 %. Con un costo de cero en la página que
   se sirve, declarar la excepción habría sido pagar la regla para no medir nada.

   Solo abajo de 720: a 1440 la tabla es una tabla de verdad, el enlace es texto en línea adentro de
   su celda de 41 px y WCAG 2.5.8 lo exime. Apilada, en cambio, la celda es un `display: flex` y el
   enlace es un bloque de ancho propio: ahí el mínimo le aplica. */
@media (max-width: 719px) {
  .drops__monstruo > .data-table td a {
    display: flex;
    align-items: center;
    min-height: 44px;
  }
  /* Con el enlace de 44 px, la etiqueta de columna del `::before` quedaría pegada arriba. */
  .drops__monstruo > .data-table td { align-items: center; }
}

/* --- El camino del item (T-088) --------------------------------------------- */

/* ItemHeader (T-109): el dibujo del item a 80 × 80 al lado del título y la bajada, para reconocer
   de un vistazo qué se está consultando. Eran 96 y el marco quedaba como un cuadradito separado. */
.drops__item { display: flex; align-items: flex-start; gap: var(--sp-4); margin-bottom: var(--sp-4); }
.drops__item h2 { margin-bottom: var(--sp-2); }
.drops__item-marco { flex: 0 0 80px; width: 80px; }

/* La probabilidad más alta del mapa, que es lo que ordena la lista, es `.data-row__destacado` del
   sistema compartido desde T-100: el mismo objeto que el dato destacado de cualquier otra fila. */

/* --- El ancla: algo mejor que este item (T-125, AC-180) ---------------------- */

/* LA TARJETA DE MEJORA ES UN COMPONENTE Y NO UNA PIEZA DE ESTA VISTA (T-127). Desde que la ficha
   central del item dibuja la misma respuesta —"esto lo supera, en esto y por tanto"— en su tercera
   sección, el marcado vive una sola vez en `templates/fragmentos/mejora.html` y las dos páginas lo
   llaman, igual que la tarjeta de item de T-120. Por eso las clases perdieron el prefijo de la
   vista: `.mejora` y `.mejoras`, no `.drops__ancla-*`. Lo que cambia entre las dos páginas es el
   destino del enlace —en `/drops` va a dónde cae y en la ficha va a la ficha del que gana— y eso lo
   pone quien la llama, que es el mismo reparto que el elemento de afuera en T-120. */

/* El selector de ejes son `DataRow` y no `FilterPill`, y lo decidió el sensor de pantalla: con las
   pastillas, "Velocidad de movimiento bajo el agua" mide 250 px y el nine-slice `0 27 fill` de la
   pieza estira su centro, así que la banda clara se abría debajo del renglón y el contraste caía a
   1,12:1 —el defecto de T-118 en otra pieza—. Acortar la etiqueta no valía: el nombre del atributo es
   el del comparador (AC-180 a). La fila también es el componente correcto de por sí: es el de las
   etiquetas largas, el mismo con el que se elige un mapa.
   El elegido no se marca con un fondo pintado sino con el `chip` de siempre y `aria-current`. */
.drops__ancla-ejes, .ficha__ejes {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--sp-2);
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
}

/* Los campos del requisito. La grilla es la misma receta ya medida del comparador —150 px de columna
   mínima, no 190: con 190 el teléfono de 390 daba UNA columna—. La clase es de esta vista y no la del
   comparador a propósito (`comparador__*` es el bloque de esa pantalla), pero el número es el mismo
   porque el ancho de pantalla es el mismo. */
.drops__ancla-requisito { display: flex; flex-direction: column; gap: var(--sp-3); align-items: flex-start; }
.drops__ancla-campos {
  width: 100%;
  display: grid;
  gap: var(--sp-3);
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
}

/* La cuenta es el veredicto de esta vista y por eso pesa: no es una nota al pie de los controles. */
.drops__ancla-cuenta, .ficha__cuenta {
  margin: 0 0 var(--sp-3);
  max-width: 74ch;
  font-size: 16px;
  line-height: 24px;
}
.drops__ancla-cuenta .num, .ficha__cuenta .num { font-weight: 600; color: var(--color-text-strong); }
.drops__ancla-cuenta .drops__cuantos { display: block; margin-top: var(--sp-1); }

/* DOS COLUMNAS DESDE 720 Y NO UNA, y el número que lo decide: el peor caso son 108 resultados —el
   Small Axe por daño físico mínimo, el arma más floja que cae— y en una sola columna medía 22.090 px
   de sección a 1440. En dos mide la mitad, sin esconder un solo resultado, que es lo único que no se
   puede hacer acá (el filtro acota y no recorta, la misma regla de ST-307).
   En dos columnas ese peor caso mide 14.334 px a 1440 y la vista acotada 8.540.
   En teléfono es una sola columna y ahí el alto es el que es: 35.344 px con esos 108, y bajarlo pide
   esconder resultados o paginar. La respuesta es acotar por requisito —que es lo que pide AC-180 y
   baja a 19.366 con Fuerza 150 · Agilidad 150—, no recortar la lista en silencio. Los cuatro números
   los mide `qa/sensor-pantalla.js` en cada push, con el peor caso en su lista, y no una persona que
   se acuerde: es la lección de T-122. */
.mejoras {
  list-style: none;
  margin: var(--sp-4) 0 0;
  padding: 0;
  display: grid;
  gap: var(--sp-3);
  grid-template-columns: 1fr;
}
/* Las dos columnas van adentro del media query y NO en un `minmax(420px, 1fr)` de base: con el
   mínimo declarado siempre, la grilla no baja de 420 px y a 390 la página desbordaba a lo ancho —436
   de scroll contra 390 de viewport, medido por el sensor—. Una columna que no entra no se acomoda
   sola. */
@media (min-width: 720px) {
  .mejoras { grid-template-columns: repeat(auto-fill, minmax(420px, 1fr)); }
}

/* DOS CLASES Y NO UNA (el gotcha de T-109): la sección del sistema va última en `mu.css`, así que
   `.mejora` a secas pierde contra `.data-card` con la misma especificidad. Es caja en
   los dos anchos —cuatro o cinco líneas que forman una unidad comparable, que es cuándo va una caja
   (T-100)— y la hornacina queda arriba a la izquierda con el texto al lado. */
.data-card.mejora {
  flex-direction: row;
  flex-wrap: nowrap;
  align-items: flex-start;
  gap: var(--sp-3);
  text-decoration: none;
  color: inherit;
}
.mejora__marco { flex: 0 0 56px; width: 56px; }
.mejora__datos { display: flex; flex-direction: column; gap: var(--sp-1); min-width: 0; flex: 1 1 auto; }

/* El "en qué y cuánto" es lo que se vino a leer: el atributo, la diferencia y los dos números que la
   sostienen. Envuelve en teléfono en vez de recortarse. */
.mejora__gana { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--sp-1) var(--sp-2); margin: 0; }
.mejora__atributo { font-size: 14px; line-height: 20px; }
.mejora__cuanto { font-size: 16px; line-height: 24px; font-weight: 600; color: var(--color-text-strong); }

.mejora__linea { margin: 0; font-size: 14px; line-height: 20px; color: var(--color-text-muted); }
/* "Pero pierde en" NO lleva `--color-warn`: no es una advertencia del sistema, es la otra mitad de la
   misma medición, y pintarla de ámbar diría que algo salió mal. Lo que la distingue es el rótulo. */
.mejora__rotulo { font-weight: 600; color: var(--color-text); }

/* Dónde está cada monstruo (T-110). El monstruo es la unidad y por eso es una caja: monstruo,
   nivel, el drop con su probabilidad y, plegada, su ubicación. Las coordenadas dejan de ser
   contenido principal —tenían más peso visual que el monstruo, cuando son auxiliares— y pasan a
   ser detalle bajo demanda. */
.drops__zonas { margin-top: var(--sp-5); }
.drops__zonas h3 { margin: 0 0 var(--sp-2); font-size: 18px; line-height: 28px; }
.data-card.drops__monstruo-caja { gap: var(--sp-1); margin-bottom: var(--sp-3); }
.drops__monstruo-caja:last-child { margin-bottom: 0; }
/* T-117 · El dibujo del monstruo a la izquierda de su nombre y de sus drops, a 96 px —adentro de
   los 72-96 que pidió el diseñador— y a 72 en teléfono. Es una fila flex y no una grilla de dos
   columnas a propósito: cuando el monstruo no tiene dibujo no se escribe el elemento, la fila queda
   con un solo hijo y el texto arranca pegado al borde. Un monstruo sin imagen no deja hueco. */
.drops__monstruo-cabecera { display: flex; align-items: flex-start; gap: var(--sp-3); }
.drops__monstruo-datos { display: flex; flex-direction: column; gap: var(--sp-1); min-width: 0; flex: 1 1 auto; }
/* `pointer-events: none` porque el dibujo no es un enlace ni un control: el <object> por defecto se
   comporta como un documento embebido y se lo puede enfocar y arrastrar. */
.drops__monstruo-dibujo { flex: none; width: 96px; height: 96px; pointer-events: none; }
.drops__monstruo-nombre { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2); margin: 0; font-size: 18px; line-height: 28px; font-weight: 600; color: var(--color-text-strong); }
.drops__monstruo-drop { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--sp-1) var(--sp-3); }
.drops__monstruo-p { font-size: 16px; line-height: 24px; font-weight: 600; color: var(--color-text-strong); }

/* La ubicación, plegada. El <details> va cerrado y el resumen dice cuántas posiciones hay: si no,
   plegar esconde que la información existe. El `summary` es una DataRow, el mismo componente que
   el desplegable por monstruo de la vista por mapa, y de ahí saca los 44 px y el foco visible. */
.drops__ubicacion > summary { cursor: pointer; font-size: 16px; line-height: 24px; }
.drops__ubicacion > summary::-webkit-details-marker { display: none; }
.drops__ubicacion > summary::marker { content: ""; }
.drops__ubicacion > summary:focus-visible { outline: 2px solid var(--color-focus); outline-offset: -2px; }
/* Cerrado no hay nada debajo, así que el divisorio sería una línea que no divide nada. */
.drops__ubicacion:not([open]) > summary { border-bottom: 0; }
.drops__ubicacion[open] > summary .data-row__ir { transform: rotate(135deg); }

/* Las coordenadas en grilla y nunca en columna: son pares X/Y, no una narración. Cinco columnas de
   ~176 px en los 944 de adentro de la caja a 1024, y dos en teléfono. Seis posiciones pasan de seis
   renglones a dos. */
.drops__coordenadas { list-style: none; margin: var(--sp-3) 0; padding: 0; display: grid; gap: var(--sp-2); grid-template-columns: repeat(auto-fill, minmax(170px, 1fr)); font-size: 14px; line-height: 20px; }
.drops__coordenadas li { display: flex; flex-direction: column; }
@media (max-width: 719px) {
  .drops__coordenadas { grid-template-columns: repeat(2, 1fr); }
  /* 72 px en teléfono, que es el piso que pidió el diseñador: a 96 el dibujo le come el ancho al
     nombre y al porcentaje, que son los que contestan la pregunta. */
  .drops__monstruo-dibujo { width: 72px; height: 72px; }
  .drops__monstruo-cabecera { gap: var(--sp-2); }
}

/* El croquis: un cuadrado de 256x256 con las zonas encima y, desde T-091, el terreno del mapa
   atrás. El fondo del cuadro se queda igual que antes a propósito: es lo que se ve si el dibujo
   —que lo sirve el almacenamiento de objetos, no el jar— no llega. */
.croquis {
  position: relative;
  width: 100%;
  max-width: 240px;
  aspect-ratio: 1 / 1;
  border: 1px solid var(--color-border);
  border-radius: var(--r-sm);
  background: var(--color-surface-raised);
  overflow: hidden;
}
/* El mismo trato que la vista previa de la galería (T-028): <object> y no <img>, para que un fallo
   no deje el ícono de imagen rota, y `object-fit` para que el píxel del dibujo y el casillero del
   mapa sigan siendo lo mismo. */
/* scaleY(-1): el minimapa del juego tiene la Y al revés (crece hacia arriba), así que la imagen de
   terreno se da vuelta en vertical para coincidir con lo que ve el jugador. Las zonas van encima con
   `top: 255 - y2` (ZonaDeAparicion), que es la misma inversión: imagen y zonas quedan coherentes. La
   X no se toca. El dato del PNG sigue siendo origen arriba-izquierda; esto es solo el display. */
.croquis__terreno { position: absolute; inset: 0; z-index: 0; display: block; width: 100%; height: 100%; object-fit: contain; pointer-events: none; transform: scaleY(-1); }
/* Rojo y no dorado: sobre el mapa el dorado es el color de la zona segura, y una zona de monstruos
   que se confunde con la ciudad manda a alguien al lugar equivocado.
   Y RELLENO TRANSPARENTE CON BORDE: en Lorencia una sola zona mide 8,11 → 94,244 y, pintada
   maciza, tapa un tercio del mapa — que es exactamente el problema que este ticket vino a
   resolver. Con el borde adentro, una zona de un casillero sigue leyéndose como un punto lleno.
   El elemento es la zona de verdad —lo que dicen las coordenadas— y no lleva pintura encima: lo
   que se ve es `::before`, que se centra en él y crece hasta un mínimo visible sin correr el
   centro. Con `min-width` sobre el elemento, una zona de un casillero crecía hacia la derecha y
   hacia abajo y la marca terminaba al lado del punto, no sobre él. */
.croquis__zona { position: absolute; z-index: 1; }
/* La marca: tres bordes de luminancia distinta, no un color. El terreno tiene una mitad clara
   (lo caminable, #8A7A55) y una oscura (lo bloqueado, #3A3124), y un rojo solo se ve bien contra
   una de las dos; con el halo oscuro por fuera y el filo claro por dentro, el contorno se recorta
   contra las dos. El mínimo de 12 px es lo que hacía falta: en LaCleon cada zona es un casillero
   —0,94 px de los 240 del croquis— y los 3 px de antes se perdían adentro del dibujo. */
.croquis__zona::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: 100%;
  height: 100%;
  min-width: 12px;
  min-height: 12px;
  transform: translate(-50%, -50%);
  border-radius: var(--r-sm);
  background: rgba(255, 45, 45, .3);
  box-shadow:
    inset 0 0 0 2px #FF2D2D,
    inset 0 0 0 3px rgba(255, 240, 224, .9),
    0 0 0 2px rgba(12, 9, 6, .92);
}
.croquis__pie { margin: var(--sp-1) 0 0; font-size: 12px; line-height: 18px; color: var(--color-text-muted); }

/* Qué significa cada color del terreno. Desde T-110 va adentro del desplegable de la ubicación y
   debajo del croquis que explica: afuera quedaba una leyenda de seis colores a la vista mientras
   no había ningún dibujo abierto. Sigue apareciendo y desapareciendo con el terreno. */
.croquis__leyenda { list-style: none; margin: var(--sp-3) 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: var(--sp-2) var(--sp-4); font-size: 13px; line-height: 20px; color: var(--color-text-muted); }
.croquis__leyenda li { display: flex; align-items: center; gap: var(--sp-2); }
.croquis__color { width: 12px; height: 12px; border-radius: var(--r-sm); border: 1px solid var(--color-border); }
/* Los cinco colores son los de `ExportadorDeMapas.Terreno`: si se cambian allá se cambian acá,
   porque una leyenda que no coincide con el dibujo es peor que no tener leyenda. */
.croquis__color--segura { background: #D6B25E; }
.croquis__color--construccion { background: #7A5E28; }
.croquis__color--caminable { background: #8A7A55; }
.croquis__color--bloqueado { background: #3A3124; }
.croquis__color--agua { background: #2F5D7C; }
.croquis__color--zona { background: #FF2D2D; }

/* "Qué no cubre esta página": es parte de la respuesta, así que se lee como texto y no como nota. */
.drops__no-cubre { margin: 0; max-width: 74ch; padding-left: var(--sp-5); display: flex; flex-direction: column; gap: var(--sp-2); font-size: 14px; line-height: 20px; color: var(--color-text-muted); }
.drops__no-cubre strong { color: var(--color-text); }

/* --- La ficha central del item (T-127, ADR-002, V30) ------------------------ */

/* ESTA VISTA ABSORBE CUATRO PÁGINAS Y NO PUEDE VERSE COMO UN TABLERO (ADR-002 §7). Lo que la hoja
   aporta a eso es poco y concreto: una sola columna, las secciones separadas por aire y no por
   cajas anidadas, y CERO controles propios más allá del selector de ejes —que es el de `/drops` y
   no uno nuevo—. Todo lo demás lo deciden la plantilla (vertical, sin pestañas, sin un `<details>`
   que pliegue una sección) y el backend (tres lugares y tres mejoras, con su "ver todos").

   LO QUE NO SE VUELVE A ESCRIBIR ACÁ. Los chips de clase, las dos líneas de requisito y valor, la
   lista de precios y la frase de "no se vende" son las MISMAS piezas de la tarjeta del catálogo
   (`.item__clases`, `.item__linea`, `.item__precios`, `.item__sin-precio`): la ficha las usa tal
   cual, así que un cambio en la tarjeta se ve acá y no quedan dos maneras de escribir un requisito.
   El selector de ejes, la cuenta y la tarjeta de mejora los comparte con el ancla de T-125. */
.ficha { display: flex; flex-direction: column; gap: var(--sp-5); }

/* LA PISTA DE LA CADENA DE REFERENCIA (T-128). `Kris → Bone Blade → Sword of Destruction`, en una
   línea de texto chico debajo de las migas y con la misma escala: es una pista y no un breadcrumb
   gigante, que es literalmente la condición del ticket ("se lee sin ocupar media pantalla").
   Envuelve en vez de recortarse —los nombres son contenido del juego y hay uno de catorce letras—,
   y con cuatro pasos y el actual mide una línea a 1440 y dos a 390: medido, no estimado.
   Los enlaces quedan EN LÍNEA y por eso están exentos del mínimo táctil de 44 px (WCAG 2.5.8): el
   contenedor fuera flex, un `<a>` hijo directo SE BLOCKIFICARÍA —el gotcha de T-116— y cada paso
   costaría 44 px de alto. Por eso esto NO es flex: es texto corrido, igual que `.migas`, y la
   separación la ponen las flechas. */
.ficha__cadena {
  margin: calc(var(--sp-2) * -1) 0 0;
  font-size: 14px;
  line-height: 20px;
  color: var(--color-text-muted);
}
.ficha__cadena-flecha, .ficha__cadena-mas { color: var(--color-text-muted); padding: 0 var(--sp-1); }
/* El item actual no es enlace —ya estás acá— y por eso es lo único que va en el color fuerte: sin
   eso, el último nombre se lee como un paso más y no como dónde estás parado. */
.ficha__cadena-actual { color: var(--color-text-strong); font-weight: 600; }

/* "COMPARAR CON <ancla>" (T-128), debajo de cada tarjeta de mejora. Va afuera de la tarjeta porque
   la tarjeta entera ya es un `<a>` (§2.27) y un enlace adentro de otro no es marcado válido. Es un
   `ActionButton` del sistema y no un enlace de texto: es la acción que abre la comparación con los
   dos items puestos, y un objetivo táctil de 44 px lo da el componente. */
.ficha__comparar { display: flex; width: 100%; margin-top: var(--sp-2); }

/* 1 · QUÉ ES. El dibujo a 160 px y la identidad al lado; en teléfono el dibujo baja a 112 y sigue
   al costado —apilarlo dejaba el nombre debajo del pliegue en las fichas con muchos chips—. */
.ficha__cabecera { display: flex; align-items: flex-start; gap: var(--sp-4); }
.ficha__cabecera h1 { margin: 0 0 var(--sp-3); }
.ficha__marco { flex: 0 0 160px; width: 160px; }
.ficha__identidad { min-width: 0; flex: 1 1 auto; }
/* EL H1 BAJA A `t-xl` EN TELÉFONO, Y NO ES UN TAMAÑO NUEVO: es el que la escala ya tiene (§1.3), el
   mismo movimiento que la portada hace al revés. Lo pidió una medición y no el gusto: el nombre del
   item es contenido del juego y hay nueve que traen la palabra "Transformation", catorce letras en
   una sola palabra. A 36 px esa palabra mide 353 px contra los 234 que deja la columna al lado del
   dibujo, así que la página DESBORDABA A LO ANCHO —432 de scroll contra 390 de viewport, medido, el
   mismo defecto que T-125 encontró en la grilla del ancla y por el mismo motivo: un mínimo que no
   entra no se acomoda solo—. A 28 px entra a 390.
   `overflow-wrap` es la garantía y no el arreglo: abajo de 390 ni 28 px alcanzan, y una palabra que
   no entra tiene que cortarse antes que empujar la página. Se verificó a 320 px. */
@media (max-width: 719px) {
  .ficha__marco { flex-basis: 112px; width: 112px; }
  .ficha__cabecera { gap: var(--sp-3); }
  .ficha__cabecera h1 { font-size: 28px; line-height: 36px; overflow-wrap: break-word; }
}

/* Los datos del catálogo. Caja en los dos anchos: son tres o cuatro renglones que forman la unidad
   "qué pide y qué da", que es cuándo va una caja (T-100). Dos clases por el gotcha de T-109. */
.data-card.ficha__datos { gap: var(--sp-2); }
/* El precio no puede quedar sin dueño en LA ÚNICA página del sitio que muestra un precio del PVP y
   unos mapas del Leveo juntos. Va pegado a la lista y no al pie de la página. */
.ficha__precio-server { margin: 0; font-size: 12px; line-height: 18px; color: var(--color-text-muted); }
.ficha__detalle { margin: 0; font-size: 12px; line-height: 18px; color: var(--color-text-muted); }
.ficha__nota { margin: 0 0 var(--sp-3); max-width: 74ch; font-size: 14px; line-height: 20px; }
.ficha__seccion > .ficha__nota:last-child { margin-bottom: 0; }

/* 2 · DÓNDE CONSEGUIRLO. Tres cajas en fila desde 720 y apiladas en teléfono. El mínimo de columna
   va ADENTRO del media query y no en la regla de base: con el mínimo declarado siempre, a 390 px la
   grilla no baja de su mínimo y la página desborda a lo ancho — es el defecto que T-125 midió en la
   lista del ancla (436 de scroll contra 390 de viewport) y no se repite. */
.ficha__lugares {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--sp-3);
  grid-template-columns: 1fr;
}
@media (min-width: 720px) {
  .ficha__lugares { grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
}
/* La señal de enlace va ARRIBA A LA DERECHA y absoluta, y no como un hijo más del flex. La caja es
   una columna —mapa arriba, monstruo y cifras abajo— así que un chevron en el flujo cae al pie y a
   la izquierda, que es donde no lo busca nadie y donde además suma un renglón. Absoluto no cuesta
   alto y queda donde la maqueta lo dibuja. El nombre del mapa sigue siendo el texto del enlace: el
   chevron es decorativo (`aria-hidden`). */
.data-card.ficha__lugar { position: relative; gap: var(--sp-2); text-decoration: none; color: inherit; }
.ficha__lugar .data-row__ir { position: absolute; top: var(--sp-4); right: var(--sp-4); margin: 0; }
.ficha__lugar-cuerpo { display: flex; align-items: flex-start; gap: var(--sp-3); }
.ficha__lugar-datos { display: flex; flex-direction: column; gap: var(--sp-1); min-width: 0; flex: 1 1 auto; }
/* Mismo `<object>` y mismo trato que la caja de monstruo de T-110: sin dibujo no se escribe el
   elemento y la caja queda con un solo hijo, así que un monstruo sin dibujo no deja hueco. */
.ficha__lugar-dibujo { flex: none; width: 72px; height: 72px; pointer-events: none; }
.ficha__lugar-monstruo { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-1) var(--sp-2); margin: 0; font-size: 14px; line-height: 20px; }
/* La probabilidad es lo que ordena estas tres cajas, así que es lo que pesa adentro de cada una. */
.ficha__lugar-p { margin: 0; font-size: 18px; line-height: 28px; font-weight: 600; color: var(--color-text-strong); }

/* EL "VER TODOS" ES UNA FILA EN LOS DOS ANCHOS y no una caja: es una salida, no una unidad
   comparable. `data-card data-row` con las dos clases más la de la vista, que es lo que hace que de
   720 para arriba tampoco se vuelva tarjeta (el gotcha de T-109 al derecho). */
.data-card.data-row.ficha__ver-todos { flex-direction: row; flex-wrap: nowrap; align-items: center; margin-top: var(--sp-3); }

/* El pie de "de qué server es cada dato": el mismo maquetado que el de `/catalogo`, porque es la
   misma declaración dicha en la página donde los dos datos aparecen juntos. */
.ficha__servers { margin: 0; max-width: 74ch; padding-left: var(--sp-5); display: flex; flex-direction: column; gap: var(--sp-2); font-size: 14px; line-height: 20px; color: var(--color-text-muted); }
.ficha__servers strong { color: var(--color-text); }

/* --- Comparador de items (T-089) -------------------------------------------- */

/* LO QUE RECOMPUSO T-106, en una línea: el defecto no era el resultado sino la DISTANCIA entre
   los dos formularios en teléfono, y lo que la acorta es que los dos se lean igual. De acá salen
   las cinco reglas de abajo:
     1. dos pasos numerados (`__paso`) con su SectionHeader, porque "Tu personaje" es una SEGUNDA
        decisión y no un bloque más del mismo montón;
     2. los dos lados con la estructura exacta repetida —los escribe UN fragmento, `lado(...)`, así
        que no hay forma de agregarle algo a uno solo— y sin nada decorativo entre medio;
     3. menos caja adentro de caja: los grupos de excelentes y de gemas y el bloque del personaje
        son grupos con un filete y no cajas. En teléfono los lados tampoco son caja;
     4. el resultado con peso propio, separado por el filete ornamental;
     5. el vacío sin caja grande para un solo selector.
   Medido en teléfono (390): el alto de la vista con dos items bajó de 4.300 a los números del
   `informe.md` de este release, contra el baseline que el ticket nombra (`qa/visual-0.42.1/`). */

.comparador { display: flex; flex-direction: column; gap: var(--sp-5); }
.comparador__intro { margin: var(--sp-3) 0 0; max-width: 72ch; font-size: 14px; line-height: 20px; }
.comparador__intro--menor { color: var(--color-text-muted); }

/* LA CADENA DE REFERENCIA (T-128), y las tres piezas aparecen SOLO con `?desde=` válido: sin eso
   esta página es la de 0.59.1, regla por regla.
   La vuelta a la referencia va en el hueco que `.page-header` ya deja a la derecha (es un flex con
   `space-between`), así que no hace falta ninguna geometría nueva: en teléfono envuelve y queda
   debajo del título, que es donde el botón de volver se busca. */
.comparador__volver { margin: 0; }
/* "Usar <item> como referencia" y "Referencia actual" son el MISMO renglón del mismo fragmento con
   dos estados, no un control que tiene un lado y el otro no: los dos lados se leen igual (T-106) y
   lo que cambia es cuál de los dos es la referencia. */
.comparador__usar { display: flex; width: 100%; margin-top: var(--sp-3); }
.comparador__referencia {
  margin: var(--sp-3) 0 0;
  padding-top: var(--sp-3);
  border-top: 1px solid var(--color-rule);
  font-size: 14px;
  line-height: 20px;
  color: var(--color-text-muted);
}
.comparador__referencia strong { color: var(--color-text-strong); }

/* Un paso del formulario: su SectionHeader y su contenido, sin caja. Las dos decisiones se leen
   como dos y el filete del encabezado es la única línea entre ellas. */
.comparador__form { display: flex; flex-direction: column; gap: var(--sp-5); margin: 0; }
.comparador__paso { display: flex; flex-direction: column; }
.comparador__opcional { font-family: var(--font-text); font-size: 16px; line-height: 24px; font-weight: 400; color: var(--color-text-muted); }

/* La grilla de dos lados, que la usan el formulario y dos secciones del resultado. */
.comparador__lados { display: grid; gap: var(--sp-5); grid-template-columns: 1fr; }
@media (min-width: 720px) { .comparador__lados { grid-template-columns: 1fr 1fr; } }

/* UN LADO. Las DOS clases en el selector no son un adorno: la sección del sistema va última en
   esta hoja, así que `.comparador__lado { padding: 0 }` a secas PIERDE contra `.data-card`
   por orden y con la misma especificidad — es el gotcha que T-109 midió y el mismo que ya había
   mordido con `.data-row.item` del catálogo.
   En teléfono el lado no es caja: un formulario largo adentro de una caja, dos veces, es
   justamente la caja adentro de la caja que este ticket saca, y son 34 px por lado. De 720 para
   arriba sí, porque ahí las dos cajas hermanas son lo que hace legible la comparación lateral
   (el pedido del diseñador para la vista 03). */
.data-card.comparador__lado {
  margin: 0;
  padding: 0;
  gap: var(--sp-2);
  border: 0;
  border-radius: 0;
  background: none;
  background-image: none;
}
.data-card.comparador__lado::before, .data-card.comparador__lado::after { content: none; }
@media (min-width: 720px) {
  .data-card.comparador__lado {
    padding: var(--sp-4);
    border: 1px solid var(--color-border);
    border-radius: var(--r-md);
    background-color: var(--color-surface);
    background-image: var(--pieza-piedra-caja, none);
    background-size: cover;
    background-position: center;
  }
  .data-card.comparador__lado::before { content: ""; top: 1px; left: 1px; }
  .data-card.comparador__lado::after { content: ""; bottom: 1px; right: 1px; }
}
/* Una caja grande para un solo selector es lo que el ticket saca del estado vacío: mientras no hay
   item elegido el lado no es caja en ningún ancho. */
@media (min-width: 720px) {
  .data-card.comparador__lado--vacio {
    padding: 0;
    border: 0;
    background: none;
    background-image: none;
  }
  .data-card.comparador__lado--vacio::before, .data-card.comparador__lado--vacio::after { content: none; }
}
/* El encabezado numerado fuerte de cada lado. Es un `<legend>` —sigue siendo el nombre del grupo
   de controles para un lector de pantalla— con el peso de un título. */
.comparador__lado-titulo {
  padding: 0;
  font-family: var(--font-display);
  font-size: 20px;
  line-height: 28px;
  font-weight: 700;
  color: var(--color-text-strong);
}

/* Los grupos de adentro de un lado: un filete y su título, sin caja. */
.comparador__mejoras { display: flex; flex-direction: column; gap: var(--sp-2); }
.comparador__grupo {
  margin: var(--sp-2) 0 0;
  padding: var(--sp-2) 0 0;
  border: 0;
  border-top: 1px solid var(--color-rule);
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
}
.comparador__grupo legend, .comparador__personaje legend { padding: 0; font-weight: 600; color: var(--color-text-strong); }
.comparador__personaje { margin: 0; padding: 0; border: 0; display: flex; flex-direction: column; gap: var(--sp-3); }
/* El `<legend>` del grupo del personaje no se dibuja: el título ya lo puso el SectionHeader del
   paso 2, y repetirlo era el encabezado de más. Sigue en el marcado porque un `<fieldset>` sin
   nombre no le dice nada a un lector de pantalla. */
.comparador__oculto { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; }

/* LAS CASILLAS Y LOS 44 PX. El objetivo táctil de una casilla es su `<label>` —tocar el texto
   marca la casilla—, no el cuadradito nativo de 18 px: agrandar el cuadradito daría un checkbox de
   44 px, que no existe en ninguna interfaz. Así que el label lleva el mínimo.
   Y van en grilla para que subirlas no cueste alto: siete casillas en una columna de 24 px son
   168 px; en dos columnas de 44, 176. Medido en 0.52.1 había 14 casillas de 18 px en esta vista, y
   eran la excepción táctil más grande que quedaba en el sitio. */
/* 150 px de columna mínima y no 190: con 190 el teléfono de 390 daba UNA columna y las siete
   casillas costaban 308 px por lado; con 150 entran dos (150×2 + 12 de gap = 312 sobre los 358 de
   ancho útil) y son 176. Una etiqueta larga envuelve a dos renglones de 20 px, que siguen entrando
   en los 44 del mínimo táctil: envolver ahí no cuesta un píxel. */
.comparador__casillas { display: grid; gap: 0 var(--sp-3); grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }
.comparador__casilla {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  min-height: 44px;
  font-size: 14px;
  line-height: 20px;
}
.comparador__casilla input { flex: 0 0 auto; }

/* Seis números cortos y un selector: la columna mínima la fija el NÚMERO y no la clase, y a la
   clase se le dan dos columnas. Los 110 px están medidos y no elegidos: con ellos la columna de
   lectura de 1008 px da ocho columnas, que son exactamente las que piden la clase (dos) más los
   seis números, así que los siete campos entran en un renglón donde antes entraban en dos. Con 120
   daba siete columnas y volvían los dos renglones. */
.comparador__stats { display: grid; gap: var(--sp-3); grid-template-columns: repeat(auto-fill, minmax(110px, 1fr)); }
.comparador__stats span { display: flex; flex-direction: column; gap: var(--sp-2); }
.comparador__stats span:first-child { grid-column: span 2; }
.comparador__ayuda { margin: 0; max-width: 72ch; font-size: 14px; line-height: 20px; color: var(--color-text-muted); }
.comparador__form .action-button { width: auto; align-self: flex-start; }

/* EL RESULTADO, con peso propio (T-106). Lo separa del formulario el filete ornamental —la misma
   pieza que separa secciones en el resto del sitio— y no un encabezado de más: enviado el
   formulario, lo que se vino a leer es esto. */
.comparador__resultado { display: flex; flex-direction: column; gap: var(--sp-5); padding-top: var(--sp-5); border-top: 1px solid var(--color-ornamento); }
.comparador__veredicto, .comparador__tabla, .comparador__costos, .comparador__afuera {
  display: flex; flex-direction: column; gap: var(--sp-3);
}
.comparador__veredicto h2, .comparador__tabla h2, .comparador__costos h2, .comparador__afuera h2 {
  margin: 0; font-size: 24px; line-height: 32px;
}
/* El veredicto es el bloque más pesado: es la respuesta. */
.comparador__veredicto {
  padding: var(--sp-4);
  border: 1px solid var(--color-border);
  border-radius: var(--r-md);
  background-color: var(--color-surface);
  background-image: var(--pieza-piedra-caja, none);
  background-size: cover;
  background-position: center;
}
.comparador__ganador { margin: 0; font-size: 20px; line-height: 28px; color: var(--color-text-strong); }
.comparador__porque { margin: 0; padding-left: var(--sp-5); display: flex; flex-direction: column; gap: var(--sp-2); font-size: 14px; line-height: 20px; }

/* Un renglón que no se puede comparar entero se ve distinto: el número está, el veredicto no. */
.comparador__fila--incompleta { color: var(--color-text-muted); }

.comparador__costo, .comparador__afuera-lado {
  border: 1px solid var(--color-border);
  border-radius: var(--r-md);
  background-color: var(--color-surface);
  padding: var(--sp-4);
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
}
.comparador__costo h3, .comparador__afuera-lado h3 { margin: 0; font-size: 18px; line-height: 26px; }
.comparador__costo p, .comparador__afuera-lado p { margin: 0; font-size: 14px; line-height: 20px; }
.comparador__etiqueta { color: var(--color-text-muted); }
.comparador__no-usa { color: var(--color-crit); font-weight: 600; }
.comparador__afuera-lado ul { margin: 0; padding-left: var(--sp-5); display: flex; flex-direction: column; gap: var(--sp-2); font-size: 14px; line-height: 20px; color: var(--color-text-muted); }
.comparador__afuera-lado strong { color: var(--color-text); }

/* ===========================================================================
   A dónde ir a levear — V-SPOTS (B3 · T-194 · T-196)
   ---------------------------------------------------------------------------
   T-196 (`design/spots-mapa-2026-11-06/PROPUESTA-v2.md`): EL MAPA ES EL PROTAGONISTA.
   Jerarquía, de más a menos: (1) el mapa elegido —nombre en Cinzel, terreno grande y
   centrado, marcadores—; (2) el bicho elegido, lo único con anillo de oro; (3) los
   recomendados como PESTAÑAS en una fila; (4) la barra de nivel y resets, 44 px; (5) el
   resto de los mapas como FICHAS de texto en flujo. Un solo borde fuerte: el del terreno.
   Reusa el lienzo del mapa en vivo (`.mapa-vivo__lienzo/__terreno`, más abajo).
   =========================================================================== */
.spots { display: flex; flex-direction: column; gap: var(--sp-5); }
.spots__seccion { display: flex; flex-direction: column; gap: var(--sp-3); }

/* El título de sección chico: versalitas en bronce, nunca compite con el nombre del mapa. */
.spots__titulo {
  margin: 0;
  font-family: var(--font-text);
  font-size: 12px;
  line-height: 16px;
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--color-text-muted);
}
.spots__titulo-nota { font-weight: 400; letter-spacing: 0; text-transform: none; }
.spots__vacio { margin: 0; font-size: 14px; line-height: 20px; color: var(--color-text-muted); }
.spots__vacio strong { color: var(--color-text); }

/* ── LA BARRA DE CONTROLES: etiqueta + input de 96 px, dos veces, el botón y la nota del nivel
   efectivo, todo en una línea de 44 px. En teléfono los dos inputs reparten el ancho y el botón
   cierra la fila; la nota baja sola. */
.spots__barra {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2) var(--sp-4);
  padding: var(--sp-3) 0;
  border-top: 1px solid var(--color-rule);
  border-bottom: 1px solid var(--color-rule);
}
.spots__control { display: flex; align-items: center; gap: var(--sp-2); }
.spots__control-etiqueta { font-size: 14px; line-height: 20px; font-weight: 600; color: var(--color-text); white-space: nowrap; }
.spots__input { width: 96px; min-height: 40px; padding: var(--sp-1) var(--sp-2); text-align: center; }
.spots__ver { min-height: 40px; }
.spots__efectivo { margin: 0 0 0 auto; font-size: 14px; line-height: 20px; color: var(--color-text-muted); }

/* ── LAS PESTAÑAS (`.spots__banda`): una fila que se desliza si no entra, sin cajas. La activa
   lleva el subrayado de oro y el nombre en fuerte; las atenuadas (difícil/bloqueado) al 65 %. */
.spots__banda {
  display: flex;
  gap: var(--sp-1);
  overflow-x: auto;
  scroll-snap-type: x proximity;
  border-bottom: 1px solid var(--color-rule);
  -webkit-overflow-scrolling: touch;
}
.spots__tarjeta { text-decoration: none; color: var(--color-text); }
.spots__tarjeta--pestania {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 120px;
  max-width: 220px;
  padding: var(--sp-2) var(--sp-3) var(--sp-3);
  margin-bottom: -1px;
  border-bottom: 2px solid transparent;
  scroll-snap-align: start;
}
.spots__tarjeta--pestania:hover { border-bottom-color: var(--color-border); }
.spots__tarjeta--pestania.spots__tarjeta--activa,
.spots__tarjeta--pestania.spots__tarjeta--activa:hover { border-bottom-color: var(--color-accent); }
.spots__tarjeta--pestania.spots__tarjeta--activa .spots__tarjeta-nombre { color: var(--color-accent); }
.spots__tarjeta-nombre { font-size: 15px; line-height: 20px; font-weight: 600; color: var(--color-text-strong); overflow-wrap: anywhere; }
.spots__tarjeta-rango { font-size: 12px; line-height: 16px; color: var(--color-text-muted); white-space: nowrap; }
.spots__tarjeta-rango--bloqueo { color: var(--color-crit); }
.spots__tarjeta--atenuada { opacity: .65; }
.spots__tarjeta--atenuada.spots__tarjeta--activa { opacity: 1; }

/* ── LAS FICHAS (`.spots__fichas`): `Nombre rango` en flujo, con borde apenas visible; la dificultad
   es el color del rango (fácil gris, difícil ámbar, bloqueado rojo) y además un texto para lectores.
   La activa: fondo elevado y borde de oro, igual que la ficha de bicho activa. Sin nivel la misma
   nav lleva también `.spots__banda` (lo mira el sensor) pero se comporta como fichas. */
.spots__fichas,
.spots__fichas.spots__banda {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-1) var(--sp-2);
  overflow: visible;
  border-bottom: 0;
}
.spots__tarjeta--ficha {
  display: inline-flex;
  align-items: baseline;
  gap: var(--sp-2);
  padding: var(--sp-1) var(--sp-2);
  border: 1px solid transparent;
  border-radius: var(--r-md);
}
.spots__tarjeta--ficha .spots__tarjeta-nombre { font-size: 14px; line-height: 20px; font-weight: 500; }
.spots__tarjeta--ficha:hover { border-color: var(--color-border); }
.spots__tarjeta--ficha.spots__tarjeta--activa,
.spots__tarjeta--ficha.spots__tarjeta--activa:hover { border-color: var(--color-accent); background: var(--color-surface-raised); }
.spots__tarjeta--ficha.spots__tarjeta--activa .spots__tarjeta-nombre { color: var(--color-accent); }
.spots__tarjeta-rango--entrada { color: var(--color-text-muted); opacity: .8; }
.spots__tarjeta-rango.spots__dificultad--recomendado { color: var(--color-ok); }
.spots__tarjeta-rango.spots__dificultad--dificil { color: var(--color-warn); }
.spots__tarjeta-rango.spots__dificultad--bloqueado { color: var(--color-crit); }

/* Sin nivel: tres tramos por nivel absoluto, cada uno con su título chico (T-196, ajuste 3). */
.spots__tramos { display: flex; flex-direction: column; gap: var(--sp-3); }
.spots__tramo { display: flex; flex-direction: column; gap: var(--sp-1); }
.spots__tramo-titulo { margin: 0; font-family: var(--font-text); font-size: 13px; line-height: 18px; font-weight: 600; color: var(--color-text); }

/* El resto, PLEGABLE y separado del detalle con un filete (T-196, ajuste 3). El summary es el título
   de sección con una flecha; sin JS queda abierto, el JS lo cierra en teléfono. */
.spots__resto { border-top: 1px solid var(--color-rule); padding-top: var(--sp-4); }
.spots__resto-resumen { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); cursor: pointer; list-style: none; }
.spots__resto-resumen::-webkit-details-marker { display: none; }
.spots__resto-resumen::marker { content: ""; }
.spots__resto-resumen:focus-visible { outline: 2px solid var(--color-focus); outline-offset: 4px; }
.spots__resto-resumen .spots__titulo { flex: 1 1 auto; }
.spots__resto-flecha { flex: none; width: 8px; height: 8px; margin-right: 4px; border-right: 2px solid var(--color-text-muted); border-bottom: 2px solid var(--color-text-muted); transform: rotate(-45deg); transition: transform .15s ease; }
.spots__resto[open] > .spots__resto-resumen .spots__resto-flecha { transform: rotate(45deg); }
.spots__resto > .spots__fichas { margin-top: var(--sp-3); }

/* La pastilla de dificultad del detalle hereda `.chip` y le pinta el borde y el texto por estado. */
.spots__dificultad { border-color: currentColor; font-weight: 600; }
.chip.spots__dificultad--recomendado { color: var(--color-ok); }
.chip.spots__dificultad--facil { color: var(--color-text-muted); }
.chip.spots__dificultad--dificil { color: var(--color-warn); }
.chip.spots__dificultad--bloqueado { color: var(--color-crit); }

/* ── EL DETALLE, sin caja. `scroll-margin-top` para que `scrollIntoView` no lo deje debajo de la
   cabecera fija. */
.spots__detalle { display: flex; flex-direction: column; gap: var(--sp-4); scroll-margin-top: 80px; }
.spots__detalle:focus { outline: none; }
.spots__mapa-cabecera { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--sp-2) var(--sp-3); }
.spots__mapa-nombre {
  margin: 0;
  font-size: 28px;
  line-height: 36px;
  font-weight: 700;
  color: var(--color-text-strong);
  overflow-wrap: anywhere;
}
.spots__mapa-datos { margin: 0; display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-1) var(--sp-2); }
.spots__drops { margin-left: auto; font-size: 14px; line-height: 20px; color: var(--color-accent); text-decoration: underline; text-underline-offset: 3px; white-space: nowrap; }
.spots__drops:hover { color: var(--color-accent-hover); }
.spots__drops::after { content: " \2192"; text-decoration: none; }
/* El aviso de "todavía no podés entrar" del mapa bloqueado. */
.spots__bloqueo { margin: 0; font-size: 14px; line-height: 20px; color: var(--color-crit); }

/* El mapa, grande y centrado: 680 px en escritorio (180 de aire a cada lado a 1040); en teléfono,
   el ancho entero. La leyenda, chica y centrada debajo. */
.spots__mapa { display: flex; flex-direction: column; align-items: center; gap: var(--sp-2); }
.spots__lienzo { max-width: 680px; }
.spots__leyenda { margin: 0; font-size: 12px; line-height: 16px; color: var(--color-text-muted); text-align: center; }

/* EL MARCADOR: un círculo con el NÚMERO del bicho adentro y su color de identidad de fondo
   (`--marcador`, inline desde la plantilla). Número en oscuro: la paleta es toda clara. Centrado en la
   coordenada con el margen negativo, como el punto del mapa en vivo. Decorativo (aria-hidden): el
   control es la grilla. */
.spots__marcador {
  position: absolute;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  margin: -10px 0 0 -10px;
  border-radius: 50%;
  background: var(--marcador, var(--color-accent));
  color: #1A1510;
  font-size: 11px;
  line-height: 1;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  box-shadow: 0 0 0 1.5px rgba(0, 0, 0, .7);
  cursor: pointer;
  transition: transform .15s ease, opacity .15s ease;
}
/* Con un bicho elegido: los suyos grandes y con anillo de oro, el resto atenuado (NO oculto: el mapa
   sigue contando cuánto hay alrededor). */
.spots__detalle--seleccion .spots__marcador { opacity: .3; }
.spots__detalle--seleccion .spots__marcador--activo {
  opacity: 1;
  z-index: 1;
  transform: scale(1.6);
  box-shadow: 0 0 0 2px var(--color-accent), 0 0 0 4px rgba(0, 0, 0, .7), 0 0 14px 4px rgba(214, 178, 94, .55);
  animation: spots-pulso 1s ease-in-out 2;
}
/* Un par de latidos al elegir, para encontrar el marcador en un mapa lleno; sin movimiento si el
   sistema lo pide. */
@keyframes spots-pulso {
  0%, 100% { transform: scale(1.6); }
  50% { transform: scale(2); }
}
@media (prefers-reduced-motion: reduce) {
  .spots__detalle--seleccion .spots__marcador--activo { animation: none; }
}

/* ── LOS BICHOS: título chico con «Todos los spots» a la derecha, y la GRILLA de fichas: 4 columnas a
   1008 px, 3 a ~760, 2 a ~480, 1 en teléfono. Sin alto máximo ni scroll interno: la página desliza. */
.spots__bichos { display: flex; flex-direction: column; gap: var(--sp-3); }
.spots__bichos-cabecera { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); }
.spots__lista { display: grid; gap: var(--sp-2); grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); }
.spots__bicho {
  display: flex; align-items: flex-start; gap: var(--sp-2);
  min-width: 0; min-height: 44px; text-align: left;
  padding: var(--sp-2);
  border: 1px solid var(--color-rule);
  border-radius: var(--r-md);
  background: transparent;
  color: var(--color-text);
  font: inherit; cursor: pointer;
  -webkit-appearance: none; appearance: none;
}
.spots__bicho:hover { border-color: var(--color-border); background: var(--color-surface); }
.spots__bicho--activo,
.spots__bicho--activo:hover {
  border-color: var(--color-accent);
  background: var(--color-surface-raised);
  box-shadow: inset 0 0 0 1px var(--color-accent), 0 0 12px rgba(214, 178, 94, .25);
}
.spots__bicho--activo .spots__bicho-nombre { color: var(--color-accent); }
.spots__bicho--activo .spots__retrato { box-shadow: 0 0 0 2px var(--color-accent); border-radius: 50%; }
/* «Todos los spots»: un botón chico de texto; activo = oro, como las pestañas. */
.spots__bicho--todos {
  min-height: 32px; padding: var(--sp-1) var(--sp-3);
  font-size: 13px; line-height: 18px; font-weight: 600;
  border-radius: var(--r-full);
}
.spots__bicho--todos.spots__bicho--activo { color: var(--color-accent); }
/* En un recomendado, los bichos fuera de la banda se atenúan pero se leen. */
.spots__bicho--fuera { opacity: .65; }
.spots__marca {
  flex: none; display: flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; border-radius: 50%;
  background: var(--marcador, var(--color-accent)); color: #1A1510;
  font-size: 12px; font-weight: 700; line-height: 1;
  font-variant-numeric: tabular-nums;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, .5);
}
/* EL RETRATO (T-197): 44 px con el dibujo del cliente y el número como insignia abajo a la derecha;
   sin dibujo, el círculo numerado de siempre (24 px) y nada más. El <object> que no carga muestra su
   contenido: el círculo vacío del color del bicho, y la insignia sigue dando el número. */
.spots__retrato { position: relative; flex: none; display: flex; align-items: center; justify-content: center; }
/* 56 px sobre un disco elevado: los renders del cliente son oscuros y sobre la piedra se perdían. */
/* Disco con un resplandor radial de bronce detrás del render (pedido del dueño, 2026-11-09): la
   imagen no se toca, es solo el fondo; el anillo exterior es el bronce del ornamento. */
.spots__retrato--con-dibujo {
  width: 56px; height: 56px; border-radius: 50%;
  background: radial-gradient(circle at 50% 42%, rgba(214, 178, 94, .5) 0%, rgba(138, 110, 51, .3) 40%, rgba(37, 30, 21, .9) 70%, var(--color-surface-raised) 100%);
  box-shadow: inset 0 0 0 1px rgba(138, 110, 51, .75), 0 0 8px rgba(214, 178, 94, .2);
}
/* El WebP de 192 px trae margen transparente alrededor de la figura: el <object> se dibuja más
   grande que el disco (72 en 56) para que el bicho ocupe el disco, y la insignia queda por encima. */
.spots__retrato-dibujo { width: 72px; height: 72px; margin: -8px; pointer-events: none; display: block; position: relative; }
.spots__retrato--con-dibujo .spots__marca { z-index: 1; }
.spots__retrato-vacio { display: block; width: 52px; height: 52px; border-radius: 50%; background: var(--marcador, var(--color-accent)); opacity: .35; }
.spots__retrato--con-dibujo .spots__marca { position: absolute; right: -4px; bottom: -4px; width: 18px; height: 18px; font-size: 10px; }
.spots__bicho-texto { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.spots__bicho-nombre { font-size: 14px; line-height: 20px; font-weight: 600; color: var(--color-text-strong); overflow-wrap: anywhere; }
.spots__bicho-datos { font-size: 12px; line-height: 16px; color: var(--color-text-muted); }
.spots__coords { display: flex; flex-wrap: wrap; gap: var(--sp-1) var(--sp-3); font-size: 12px; line-height: 16px; color: var(--color-text); }

/* Teléfono: la barra en dos filas (los dos inputs y el botón, la nota debajo), la grilla en una
   columna con la ficha de bicho como FILA (nombre y datos en una línea, coordenadas en la segunda). */
@media (max-width: 767px) {
  /* grilla: los dos controles reparten el ancho y el botón cierra la fila; la nota, debajo */
  .spots__barra { display: grid; grid-template-columns: 1fr 1fr auto; gap: var(--sp-2); }
  .spots__control { min-width: 0; }
  .spots__input { width: 100%; min-width: 0; }
  .spots__ver { padding-left: var(--sp-3); padding-right: var(--sp-3); }
  .spots__efectivo { grid-column: 1 / -1; margin-left: 0; }
  .spots__lienzo { max-width: none; }
  /* el enlace a los drops cierra la cabecera en su propia línea, alineado a la derecha */
  .spots__drops { flex: 1 0 100%; margin-left: 0; text-align: right; white-space: normal; }
}
/* Teléfono (T-196, ajuste 1): DOS columnas de fichas compactas; se saca «· N spots» (las coordenadas
   ya lo dicen) y el retrato baja a 36 px. */
@media (max-width: 479px) {
  .spots__lista { grid-template-columns: 1fr 1fr; gap: var(--sp-1); }
  .spots__bicho { padding: var(--sp-1) var(--sp-2); gap: var(--sp-1); }
  .spots__bicho-nombre { font-size: 13px; line-height: 17px; }
  .spots__bicho-datos { font-size: 11px; line-height: 14px; }
  .spots__bicho-spots { display: none; }
  .spots__coords { gap: 2px var(--sp-2); font-size: 11px; line-height: 14px; }
  .spots__marca { width: 20px; height: 20px; font-size: 11px; }
  .spots__retrato--con-dibujo { width: 40px; height: 40px; }
  .spots__retrato-dibujo { width: 54px; height: 54px; margin: -7px; }
  .spots__retrato-vacio { width: 38px; height: 38px; }
  .spots__retrato--con-dibujo .spots__marca { width: 16px; height: 16px; font-size: 9px; right: -3px; bottom: -3px; }
}

/* ===========================================================================
   T-100 · EL SISTEMA COMPARTIDO — los doce componentes y las tres reglas
   ---------------------------------------------------------------------------
   Los nombres son los del diseñador (work/c2-entrega-2-disenador.md §"Los doce componentes
   compartidos") y van al marcado tal cual, en minúsculas con guion, que es como se escribe una
   clase de CSS. Los modificadores siguen en castellano, como todo el resto del repo.

   Dónde vive cada uno:
     SiteHeader     .site-header      §Marca y cabecera
     PageShell      .page-shell       arriba de todo, con el contenedor de 1040
     ReadingColumn  .reading-column   §V2 instructivo y V3 reglas — 720 px
     PageHeader     .page-header      acá
     SectionHeader  .section-header   acá
     ActionButton   .action-button    §Botones
     FilterPill     .filter-pill      §Catálogo
     DataCard       .data-card        acá
     DataRow        .data-row         acá  (la geometría de DataCard en teléfono)
     EmptyState     .empty-state      §P-EMPTY, con las tres variantes
     ServerSwitch   .server-switch    §V4 rankings — el mismo en ranking y en conectados
     SearchForm     .search-form      §Cuentas
     DataTable      .data-table       §P-TABLE

   Esta sección va ÚLTIMA a propósito. Muchas reglas de vista de antes de este ticket declaran lo
   mismo que un componente con la misma especificidad, y lo que decide un empate de especificidad
   es el orden. Puesta última, el sistema manda; a medida que T-101..T-106 absorban cada vista, las
   reglas de arriba se van y esto deja de importar.

   Las TRES anchuras son tres modos de composición y no tres quiebres decorativos:
     359  lectura lineal y filas compactas
     720  lectura y formularios          -> .reading-column, .page-header__titulo
     1024 datos comparativos y grillas   -> .page-shell (1040 con sus 16 de lado)

   La regla de la caja: "una caja significa que sus contenidos forman una unidad manipulable,
   comparable o de estado". Texto editorial, reglas, instrucciones y listas normales viven directo
   sobre el fondo; sets, servidores, resultados y formularios pueden necesitarla.
   =========================================================================== */

/* --- PageHeader ------------------------------------------------------------
   H1 + introducción + metadata/estado, SIN caja alrededor. Cuando la vista tiene un control que
   manda sobre toda la página —el ServerSwitch de ranking y de conectados— ese control va acá
   mismo, alineado con la cabecera, y no en una franja aparte debajo: así los dos quedan en el
   mismo lugar relativo en las dos vistas, que es lo que ServerSwitch pide. */
.page-header {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--sp-4);
  /* Sin margen propio: las vistas que lo usan son columnas flex con su `gap`, y un margen encima
     del gap deja dos separaciones distintas entre el H1 y el contenido según la página. */
  margin: 0;
}
.page-header__titulo { display: flex; flex-direction: column; gap: var(--sp-2); max-width: 720px; }
.page-header__intro { margin: 0; max-width: 720px; font-size: 16px; line-height: 24px; color: var(--color-text-muted); }
/* El estado y el timestamp van inmediatamente debajo del H1 (vista 05): son parte de la cabecera y
   no una línea suelta que aparece después de la lista. */
.page-header__meta { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2) var(--sp-3); margin: 0; font-size: 14px; line-height: 20px; color: var(--color-text-muted); }

/* --- SectionHeader ---------------------------------------------------------
   Cinzel más chico que el H1 y un separador OPCIONAL. El separador nunca es necesario para
   entender la sección: es un filete de bronce y si la pieza no llega queda el token. */
.section-header { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: var(--sp-2) var(--sp-4); margin: 0 0 var(--sp-4); }
.section-header h2, .section-header h3 { margin: 0; font-size: 24px; line-height: 32px; }
.section-header__nota { margin: 0; font-size: 14px; line-height: 20px; color: var(--color-text-muted); }
.section-header--con-filete { border-bottom: 1px solid var(--color-rule); padding-bottom: var(--sp-2); }

/* --- DataCard --------------------------------------------------------------
   La caja de siempre —16 de padding, borde de 1, radio de 4, `--color-surface`— convertida en un
   solo nombre. Las quince recetas repetidas que encontró el relevamiento entran acá de a una, a
   medida que su vista pasa por su ticket; hoy la usan contacto, videos y la lista de mapas de
   drops. El ornamento es el mínimo: la textura de piedra y las dos esquineras que ya reparte
   T-099, nada propio. */
.data-card {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  padding: var(--sp-4);
  border: 1px solid var(--color-border);
  border-radius: var(--r-md);
  /* El color primero y la textura después, en este orden y en la misma regla. Al revés no se ve:
     `background` es una abreviatura y apaga el `background-image` que se haya puesto antes, que es
     el motivo por el que la piedra de T-099 no llegaba a NINGUNA de las quince cajas (medido con
     `getComputedStyle` contra producción en 0.45.1, `qa/visual-0.46.0/`). T-107 corrigió las
     cuatro de la vista patrón y T-114 las once restantes, todas pasándolas a `background-color`;
     acá se deja el orden como está porque también funciona, y el sensor de piezas mira el
     resultado y no la forma de escribirlo. */
  background: var(--color-surface);
  background-image: var(--pieza-piedra-caja, none);
  background-size: cover;
  background-position: center;
  color: var(--color-text);
}
a.data-card:hover { border-color: var(--color-border-strong); }
.data-card__nombre { margin: 0; font-size: 18px; line-height: 28px; font-weight: 600; color: var(--color-text-strong); }
.data-card__linea { margin: 0; font-size: 14px; line-height: 20px; color: var(--color-text-muted); }
.data-card__dato { margin: 0; font-size: 20px; line-height: 28px; font-weight: 600; color: var(--color-text-strong); }

/* --- DataRow ---------------------------------------------------------------
   No es "la tarjeta con menos información": es la misma información con otra geometría. Se va la
   caja —contorno, fondo, esquineras— y queda una fila de 44 px de alto mínimo separada por el
   divisorio interno. Eso es lo que hace bajar el alto de /drops, de los rankings y de conectados
   en teléfono, sin esconder un solo dato.

   El oro señala el enlace y no pinta la fila entera (riesgo declarado de la vista 10): una fila
   que es un <a> hereda el color de acento en TODO su texto, así que acá se le devuelve el color de
   texto y el oro queda solo en `.data-row__ir`. */
.data-row {
  display: flex;
  /* Explícito: cuando la fila comparte elemento con `.data-card`, la tarjeta declara `column` y
     sin esta línea la fila de teléfono salía apilada y centrada. */
  flex-direction: row;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-1) var(--sp-3);
  min-height: 44px;
  padding: var(--sp-2) var(--sp-1);
  border: 0;
  border-bottom: 1px solid var(--color-rule);
  border-radius: 0;
  background: none;
  color: var(--color-text);
}
.data-row:last-child { border-bottom: 0; }
/* Sin caja no hay dónde apoyar las esquineras de T-099, y dibujarlas sueltas sobre la piedra deja
   dos marcas de bronce flotando. */
.data-row::before, .data-row::after { content: none; }
a.data-row:hover { background: var(--color-surface); }
.data-row__nombre { flex: 1 1 auto; min-width: 0; font-weight: 600; color: var(--color-text-strong); overflow-wrap: anywhere; }
/* El dato secundario va a la derecha de la fila. El `order` es lo que deja la señal de enlace
   pegada al nombre cuando el dato se va a un segundo renglón: sin él, el chevron caía a un tercer
   renglón propio y cada fila de /drops medía 76 px en vez de 44. */
.data-row__dato { order: 1; margin-left: auto; font-size: 14px; line-height: 20px; color: var(--color-text-muted); text-align: right; }
/* La cifra que ordena la lista (la probabilidad de la vista 09). Va pegada a su frecuencia y no
   suelta: el riesgo declarado es que un porcentaje grande parezca una promesa independiente. */
.data-row__destacado { font-size: 18px; line-height: 28px; font-weight: 600; color: var(--color-text-strong); }
/* La señal de enlace: el chevron de bordes, que es el único oro de la fila. Decorativo, así que el
   marcado lo lleva con aria-hidden y el nombre sigue siendo el texto del enlace. */
.data-row__ir {
  order: 2;
  flex: none;
  width: 8px;
  height: 8px;
  margin-left: var(--sp-2);
  border: 2px solid var(--color-accent);
  border-left: 0;
  border-bottom: 0;
  transform: rotate(45deg);
}
a.data-row:hover .data-row__ir { border-color: var(--color-accent-hover); }

/* DataCard -> DataRow en teléfono. El marcado lleva las dos clases y no un modificador: es
   literalmente "tarjeta en escritorio, fila en teléfono", y así se lee. `.data-row` está declarada
   después de `.data-card`, con la misma especificidad, así que abajo de 720 gana la fila; de 720
   para arriba este bloque —dos clases, más específico— devuelve la tarjeta.

   Una `.data-card` sola (la tarjeta de canal de contacto) es caja en los dos anchos, y una
   `.data-row` sola (los monstruos de la vista 10) es fila en los dos: el diseñador pidió el mismo
   componente en los dos anchos ahí. */
@media (min-width: 720px) {
  .data-card.data-row {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    /* Compacta: el diseñador pidió "DataCard compactas en cuatro columnas" para la vista 07, y con
       el padding y el gap de la DataCard normal cada fila de /drops crecía 12 px. Sobre doce filas
       eran 140 px más que antes del ticket, medidos a 1440. */
    gap: var(--sp-1);
    min-height: 0;
    padding: var(--sp-3) var(--sp-4);
    border: 1px solid var(--color-border);
    border-radius: var(--r-md);
    background: var(--color-surface);
    background-image: var(--pieza-piedra-caja, none);
    background-size: cover;
    background-position: center;
  }
  .data-card.data-row::before, .data-card.data-row::after { content: ""; }
  .data-card.data-row .data-row__ir { display: none; }
  .data-card.data-row .data-row__destacado { font-size: 20px; line-height: 28px; }
}

/* --- La regla que no se negocia: 44 px de objetivo táctil --------------------
   Escrita una vez, sobre los componentes del sistema que se tocan. No es un recordatorio: es la
   regla, y lo que la mide es el sensor visual, no el ojo.

   T-116 se llevó la excepción más grande: los 185 enlaces de pieza de set del catálogo medían 32 px
   y subieron a 44 en su propia regla, más arriba en esta hoja, con el número medido al lado.

   LO QUE TODAVÍA QUEDA ABAJO DEL MÍNIMO, medido contra producción en 0.52.0 y declarado acá porque
   una excepción callada deja de ser una regla. No son regresiones: estaban desde antes y lo que
   hizo esta tanda fue medirlas por primera vez en esas dos páginas.
     - `/comparador` con dos items: 14 casillas nativas de 18 px, con su `<label>` de 24. El
       objetivo real es el label, así que el arreglo es darle alto al label y no agrandar el
       cuadradito. Es superficie de T-106, que es el ticket de los formularios del comparador.
     - `/entrar`: `a.tarjeta__salida` ("Volver al inicio") mide 24 px de alto. Es un enlace de
       bloque al pie de la tarjeta, no un enlace adentro de una oración, así que le aplica.
   Los que NO entran en la cuenta son los `<a>` de texto corrido adentro de una oración, que WCAG
   2.5.8 exime y el sensor cuenta aparte. Una excepción nueva se declara con su medición en
   design/SPEC.md y acá. */
.data-row, .filter-pill, .action-button, .server-switch a, .site-header__nav > a { min-height: 44px; }

/* ── Mapa en vivo ────────────────────────────────────────────────────────────
   El punto se posiciona con porcentajes que calcula el servidor (PosicionDePersonaje):
   cero JavaScript, como el resto de la web. La imagen de terreno es 256x256 y se
   escala con la caja, así que el porcentaje vale a cualquier tamaño — que es lo que
   la hace servir en un teléfono.                                              */
.mapa-vivo__ayuda { color: var(--color-text-muted); margin-bottom: 1.5rem; }
.mapa-vivo__server { margin-bottom: 2.5rem; }
.mapa-vivo__personaje { margin-bottom: 2rem; }
.mapa-vivo__personaje h3 small { color: var(--color-text-muted); font-weight: normal; margin-left: .5rem; }
.mapa-vivo__datos { color: var(--color-text-muted); margin: .25rem 0 .75rem; }

.mapa-vivo__lienzo {
  position: relative;
  width: 100%;
  max-width: 512px;
  aspect-ratio: 1 / 1;
  border: 1px solid var(--color-border);
  border-radius: var(--r-md);
  overflow: hidden;
}

.mapa-vivo__terreno {
  width: 100%;
  height: 100%;
  display: block;
  /* El terreno es un mapa de bits de 256x256: sin esto el navegador lo difumina
     y se pierde el dibujo de las calles del pueblo. */
  image-rendering: pixelated;
  /* scaleY(-1): el minimapa del juego tiene la Y al revés (crece hacia arriba), así que la imagen se
     da vuelta en vertical para coincidir con lo que ve el jugador. El marcador y los spots van encima
     con `(255 - y)` (PosicionDePersonaje/Spot), la misma inversión: todo queda coherente. La X no se
     toca. El dato del PNG sigue siendo origen arriba-izquierda; esto es solo el display. */
  transform: scaleY(-1);
}

.mapa-vivo__punto {
  position: absolute;
  width: 10px;
  height: 10px;
  margin: -5px 0 0 -5px;      /* centra el punto en la coordenada, no lo cuelga de su esquina */
  border-radius: 50%;
  background: var(--color-accent);
  box-shadow: 0 0 0 2px rgba(0, 0, 0, .6);
}

/* AC-C8: el marcador de un compañero de party en el mismo mapa. Distinguible del pj —más chico y en
   verde, no en el oro del propio— para ver de un vistazo si están agrupados o dispersos. */
.mapa-vivo__punto--companero {
  width: 9px;
  height: 9px;
  margin: -4.5px 0 0 -4.5px;
  background: var(--color-ok);
  /* Anillo claro: distingue a un compañero (círculo con aro) de un spot verde de nivel bajo (cuadrado). */
  box-shadow: 0 0 0 1.5px var(--color-text-strong), 0 0 0 3px rgba(0, 0, 0, .6);
}

/* AC-A6: el terreno del fork tiene más mapas que imágenes en el jar (30). Cuando falta, un
   placeholder honesto adentro del mismo lienzo, nunca un recuadro roto ni un 500 (Riesgo #4). */
.mapa-vivo__placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  padding: var(--sp-4);
  text-align: center;
  background-color: var(--color-surface-raised);
  color: var(--color-text-muted);
  font-size: 14px;
  line-height: 20px;
}

/* ── Dashboard de «Tu cuenta» (T-150) ────────────────────────────────────────
   Dos columnas 30/70 desde 720 px; una sola columna apilada abajo (AC-A5). Reusa los bloques de
   adentro (.panel__cabecera, .personajes, .kpis, .personaje__*, .mapa-vivo__*): acá solo vive el
   armazón y el selector. */
.cuenta-dashboard { display: flex; flex-direction: column; gap: var(--sp-5); align-items: flex-start; }
.cuenta-dashboard > h1 { margin: 0; }
.cuenta-dashboard > .banner { width: 100%; }
.cuenta-dashboard__cols { display: grid; grid-template-columns: 1fr; gap: var(--sp-6); width: 100%; }
@media (min-width: 720px) {
  .cuenta-dashboard__cols { grid-template-columns: minmax(240px, 30%) 1fr; align-items: start; }
}
.cuenta-dashboard__lateral { display: flex; flex-direction: column; gap: var(--sp-5); min-width: 0; }
.cuenta-dashboard__detalle { display: flex; flex-direction: column; gap: var(--sp-5); min-width: 0; }
.cuenta-dashboard__acciones { display: flex; flex-direction: column; gap: var(--sp-3); align-items: stretch; }
.cuenta-dashboard__acciones form { margin: 0; }
.cuenta-dashboard__acciones .action-button { width: 100%; }
.cuenta-dashboard__mapa { display: flex; flex-direction: column; gap: var(--sp-3); }
.cuenta-dashboard__mapa h2 { margin: 0; }
.cuenta-dashboard__puntos { margin: 0; }

/* El chip de "tenés X puntos para repartir" (AC-A7b): se ve como un chip pero es un objetivo
   táctil, así que mide 44 px de alto (la regla del sistema). */
.cuenta-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  min-height: 44px;
  padding: var(--sp-1) var(--sp-4);
  border: 1px solid var(--color-accent);
  border-radius: var(--r-full);
  background-color: var(--color-warn-bg);
  color: var(--color-text-strong);
  font-weight: 600;
  text-decoration: none;
}
.cuenta-chip:hover { border-color: var(--color-accent-hover); }

/* El selector: una lista por server, cada entrada un enlace-tarjeta con el estado en vivo. */
.cuenta-selector { gap: var(--sp-4); }
.cuenta-selector__grupo { display: flex; flex-direction: column; gap: var(--sp-2); }
.cuenta-selector__grupo h3 { margin: 0; font-size: 16px; line-height: 24px; color: var(--color-text-muted); }
.cuenta-pj {
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
  min-height: 44px;
  padding: var(--sp-3) var(--sp-4);
  text-decoration: none;
  color: var(--color-text);
}
.cuenta-pj:hover { border-color: var(--color-border-strong); }
.cuenta-pj--activa { border-color: var(--color-accent); background-color: var(--color-surface-raised); }
.cuenta-pj__nombre { font-weight: 600; color: var(--color-text-strong); }
.cuenta-pj__datos { font-size: 14px; line-height: 20px; color: var(--color-text-muted); }

/* AC-C7: el bloque de party del detalle. Cada miembro es una DataCard con nombre, nivel · mapa y el
   badge de "mismo mapa / otro mapa". */
.cuenta-party { display: flex; flex-direction: column; gap: var(--sp-3); }
.cuenta-party h2 { margin: 0; }
.cuenta-party__meta { margin: 0; font-size: 14px; line-height: 20px; color: var(--color-text-muted); }
.cuenta-party__vacio { margin: 0; color: var(--color-text-muted); }
.cuenta-party__lista { display: flex; flex-direction: column; gap: var(--sp-2); }
.cuenta-party__miembro {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2) var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
}
.cuenta-party__nombre { font-weight: 600; color: var(--color-text-strong); }
.cuenta-party__datos { font-size: 14px; line-height: 20px; color: var(--color-text-muted); }

/* ── T-150 · densidad del dashboard (feedback del dueño 2026-10-11) ───────────────────────────────
   El dashboard reusa el fragmento `.personaje` de la ficha standalone (AC-A8), que trae el espaciado
   y el orden de una página completa y, adentro del dashboard, queda disperso y obliga a scrollear.
   Todo lo de acá densifica scopeado a `.cuenta-dashboard`: la ficha /cuenta/personaje/{id} NO cambia.
   Objetivo (maqueta del diseñador): entrar en ~un viewport, cada bloque como card bronce, y en
   pantalla ancha el detalle y el mapa/party lado a lado para aprovechar el ancho. */

/* 1 · armazón más apretado */
.cuenta-dashboard { gap: var(--sp-4); }
.cuenta-dashboard__cols { gap: var(--sp-4); }
.cuenta-dashboard__lateral,
.cuenta-dashboard__detalle { gap: var(--sp-4); }
.cuenta-dashboard__acciones { gap: var(--sp-2); }

/* 2 · cada bloque como card bronce (como la maqueta) */
.cuenta-dashboard .panel__cabecera,
.cuenta-dashboard .cuenta-selector,
.cuenta-dashboard__detalle > .personaje,
.cuenta-dashboard__mapa,
.cuenta-dashboard .cuenta-party {
  padding: var(--sp-4);
  border: 1px solid var(--color-border);
  border-radius: var(--r-md);
  background-color: var(--color-surface);
}
.cuenta-dashboard .panel__cabecera { background-color: var(--color-surface-raised); }

/* Títulos de panel como la maqueta: chicos, en mayúsculas y con filete abajo. Solo los títulos de
   sección (nunca el nombre del pj —es h1— ni el h2 de los diálogos de reset, que llevan el nombre). */
.cuenta-dashboard .cuenta-selector > h2,
.cuenta-dashboard .cuenta-selector__grupo > h3,
.cuenta-dashboard__mapa > h2,
.cuenta-dashboard .cuenta-party > h2,
.cuenta-dashboard__detalle .personaje__stats > h2,
.cuenta-dashboard__detalle .personaje__reset > h2,
.cuenta-dashboard__detalle .personaje__offline > h2 {
  margin: 0 0 var(--sp-2);
  padding-bottom: var(--sp-2);
  border-bottom: 1px solid var(--color-filete, var(--color-border));
  font-size: 13px;
  line-height: 18px;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--color-text-muted);
}

/* 3 · pantalla ancha: el detalle a la izquierda, el mapa+party al lado (no abajo) */
@media (min-width: 1024px) {
  .cuenta-dashboard__detalle {
    display: grid;
    grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr);
    gap: var(--sp-4);
    align-items: start;
  }
  .cuenta-dashboard__puntos { grid-column: 1 / -1; }
  .cuenta-dashboard__detalle > .personaje { grid-column: 1; }
  .cuenta-dashboard__aside { grid-column: 2; }
  /* sin detalle (pj ajeno / sin elegir): el estado vacío ocupa todo el ancho, no media columna */
  .cuenta-dashboard__detalle > .empty-state { grid-column: 1 / -1; }
}
.cuenta-dashboard__aside { display: flex; flex-direction: column; gap: var(--sp-4); min-width: 0; }

/* 4 · detalle compacto adentro del dashboard */
.cuenta-dashboard__detalle > .personaje { gap: var(--sp-3); }
.cuenta-dashboard__detalle .migas { display: none; }            /* el selector ya es la navegación */
.cuenta-dashboard__detalle .personaje__cabecera h1 { font-size: 24px; line-height: 30px; }
.cuenta-dashboard__detalle .personaje__meta p { font-size: 13px; line-height: 18px; }
.cuenta-dashboard__detalle .kpis { grid-template-columns: repeat(2, 1fr); gap: var(--sp-2); }
.cuenta-dashboard__detalle .kpi { padding: var(--sp-3); }
.cuenta-dashboard__detalle .kpi__valor { font-size: 26px; line-height: 32px; }
.cuenta-dashboard__detalle .data-table td,
.cuenta-dashboard__detalle .data-table th { padding-top: var(--sp-2); padding-bottom: var(--sp-2); }

/* 5 · el mapa llena su columna del aside (antes quedaba un cuadrito con mucho aire al lado) */
/* El mapa llena su columna en 3-col (desktop), pero NO crece sin fin: a ancho completo en tablet un
   cuadrado de ~960px era enorme. Cap a 560 (en 3-col la columna es ≤~620, así que lo llena igual). */
.cuenta-dashboard__aside .mapa-vivo__lienzo { max-width: 560px; }
.cuenta-dashboard__mapa .mapa-vivo__datos { margin-top: 0; }

/* ── T-150 · iteración 2 (dirección del diseñador 2026-10-11) ─────────────────────────────────────
   Prioridad del diseñador: compactar las stats y separar el leveo offline ANTES de cards nuevas. */

/* 6 · stats como grilla compacta de pares atributo/valor (no la tabla alta) */
.cuenta-dashboard__detalle .stats-grid {
  margin: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(72px, 1fr));
  gap: var(--sp-2);
}
.cuenta-dashboard__detalle .stats-grid__par {
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--color-border);
  border-radius: var(--r-md);
  background-color: var(--color-surface-raised);
}
.cuenta-dashboard__detalle .stats-grid dt {
  margin: 0; font-size: 12px; line-height: 16px; color: var(--color-text-muted);
}
.cuenta-dashboard__detalle .stats-grid dd {
  margin: var(--sp-1) 0 0; font-size: 18px; line-height: 22px; font-weight: 600; color: var(--color-text-strong);
}

/* 7 · leveo offline como card aparte, empujada al final del detalle (flex order) */
.cuenta-dashboard__detalle .personaje__offline {
  order: 10;
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--color-filete, var(--color-border));
  border-radius: var(--r-md);
  background-color: var(--color-surface-raised);
}

/* 8 · comprimir el bloque de cuenta (arriba izquierda) y la lista de personajes */
.cuenta-dashboard .panel__cabecera { padding: var(--sp-3) var(--sp-4); gap: var(--sp-1); }
.cuenta-dashboard .panel__usuario { font-size: 18px; line-height: 24px; }
.cuenta-dashboard .cuenta-selector .personajes__meta { font-size: 12px; line-height: 16px; }
.cuenta-dashboard .cuenta-pj { padding: var(--sp-2) var(--sp-3); }

/* 9 · spots de spawn sobre el mapa + card de control "Spots y bichos" (it.2, dirección del
   diseñador: reusar el MISMO mapa, marcador de forma distinta, selección resalta en oro y atenúa el
   resto, leyenda, SIN un color por bicho). */
/* El spot es un CUADRADO (forma distinta del círculo del jugador/party) con el COLOR DE DIFICULTAD
   inline (gradiente por nivel del mapa). Elegir un bicho oculta los demás (display:none vía JS). */
.mapa-vivo__spot {
  position: absolute;
  width: 8px; height: 8px;
  margin: -4px 0 0 -4px;
  border-radius: 1px;                        /* cuadrado, no círculo */
  box-shadow: 0 0 0 1px rgba(0, 0, 0, .65);  /* contorno oscuro para que resalte sobre el terreno */
}

/* Leyenda del mapa (el color/forma nunca comunica solo, SPEC §1.1). El gradiente es RELATIVO a este
   mapa: verde = lo más bajo del mapa, rojo = lo más alto. */
.mapa-vivo__leyenda {
  list-style: none; margin: var(--sp-2) 0 0; padding: 0;
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-1) var(--sp-3);
  font-size: 12px; line-height: 16px; color: var(--color-text-muted);
}
.mapa-vivo__leyenda li { display: inline-flex; align-items: center; gap: var(--sp-1); }
.leyenda-marca { display: inline-block; width: 9px; height: 9px; box-shadow: 0 0 0 1px rgba(0, 0, 0, .5); }
.leyenda-marca--pj { border-radius: 50%; background: var(--color-accent); }
.leyenda-marca--party { border-radius: 50%; background: var(--color-ok); box-shadow: 0 0 0 2px var(--color-text-strong); }
/* La escala de dificultad: barra verde→rojo con sus extremos rotulados (no depende solo del color). */
.leyenda-gradiente { gap: var(--sp-1); }
.leyenda-gradiente__barra {
  display: inline-block; width: 56px; height: 8px; border-radius: 2px;
  background: linear-gradient(90deg, hsl(130, 55%, 52%), hsl(65, 55%, 52%), hsl(0, 55%, 52%));
  box-shadow: 0 0 0 1px rgba(0, 0, 0, .4);
}
.leyenda-gradiente__min, .leyenda-gradiente__max { font-size: 11px; }

/* Card de control: lista de bichos del mapa; al elegir uno se resaltan sus spots arriba. */
.cuenta-spots { display: flex; flex-direction: column; gap: var(--sp-2); }
.cuenta-spots h2 { margin: 0; }
.cuenta-spots__meta { margin: 0; font-size: 12px; line-height: 16px; color: var(--color-text-muted); }
.cuenta-spots__lista { display: flex; flex-direction: column; gap: var(--sp-1); max-height: 260px; overflow-y: auto; }
.cuenta-spots__bicho {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2);
  width: 100%; min-height: 44px; text-align: left;
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--color-border);
  border-radius: var(--r-md);
  background: var(--color-surface);
  color: var(--color-text);
  font: inherit; cursor: pointer;
}
.cuenta-spots__bicho:hover { border-color: var(--color-border-strong); }
.cuenta-spots__bicho--activo { border-color: var(--color-accent); background: var(--color-surface-raised); }
.cuenta-spots__bicho--todos { justify-content: center; font-weight: 600; }
.cuenta-spots__nombre { font-weight: 600; color: var(--color-text-strong); }
.cuenta-spots__datos { font-size: 12px; line-height: 16px; color: var(--color-text-muted); white-space: nowrap; }

/* ── T-150 · secundarias (it.2): encabezado compacto, ranking, eventos ───────────────────────────── */

/* Las cards nuevas también como card bronce (la de eventos NO: adentro va la tarjeta de la portada,
   que ya trae su propio marco; doble marco quedaría pesado). */
.cuenta-dashboard .cuenta-spots,
.cuenta-dashboard .cuenta-ranking {
  padding: var(--sp-4);
  border: 1px solid var(--color-border);
  border-radius: var(--r-md);
  background-color: var(--color-surface);
}
/* Título de panel (mayúsculas + filete) también para las cards nuevas. */
.cuenta-dashboard .cuenta-spots > h2,
.cuenta-dashboard .cuenta-ranking > h2,
.cuenta-dashboard .cuenta-eventos > h2 {
  margin: 0 0 var(--sp-2);
  padding-bottom: var(--sp-2);
  border-bottom: 1px solid var(--color-filete, var(--color-border));
  font-size: 13px; line-height: 18px; letter-spacing: .06em; text-transform: uppercase;
  color: var(--color-text-muted);
}

/* 10 · encabezado: título a la izquierda, acciones de cuenta compactas a la derecha */
.cuenta-dashboard__encabezado {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: var(--sp-2) var(--sp-3); width: 100%;
}
.cuenta-dashboard__encabezado > h1 { margin: 0; }
.cuenta-dashboard__encabezado .cuenta-dashboard__acciones {
  flex-direction: row; flex-wrap: wrap; align-items: center; gap: var(--sp-2);
}
.cuenta-dashboard__encabezado .cuenta-dashboard__acciones form { margin: 0; }
.cuenta-dashboard__encabezado .cuenta-dashboard__acciones .action-button { width: auto; }

/* 11 · ranking del pj (card chica): la posición grande + link al ranking completo */
.cuenta-ranking { display: flex; flex-direction: column; gap: var(--sp-2); align-items: flex-start; }
.cuenta-ranking__pos { margin: 0; display: flex; align-items: baseline; gap: var(--sp-2); }
.cuenta-ranking__num { font-size: 28px; line-height: 32px; font-weight: 600; color: var(--color-text-strong); }
.cuenta-ranking__de { font-size: 14px; color: var(--color-text-muted); }
.cuenta-ranking .action-button { width: auto; }

/* 12 · eventos: la tarjeta de la portada reusada, apenas espaciada en la columna de cuenta */
.cuenta-eventos { display: flex; flex-direction: column; gap: var(--sp-2); }

/* ── T-150 · it.3 (dirección del diseñador): mapa+spots unificado + ancho de /cuenta ──────────────── */

/* (El ancho de /cuenta y la grilla que estaban acá —it.3— quedaron SUPERADOS por las reglas de it.4A
   / it.4A.1 más abajo: page-shell hasta 1480, y grilla de 3 columnas desde 1280. Se removieron para no
   dejar reglas contradictorias.) */

/* «Mapa y spots» como UN componente: cabecera (título + nombre del mapa) y el selector pegado. */
.cuenta-mapaspots__cabecera {
  display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: var(--sp-1) var(--sp-3);
}
.cuenta-mapaspots__mapa { font-size: 12px; color: var(--color-text-muted); }
.cuenta-mapaspots .cuenta-spots__ayuda {
  margin: var(--sp-3) 0 0; font-size: 12px; line-height: 16px; color: var(--color-text-muted);
}
/* El título de «Mapa y spots» usa el mismo estilo de panel que las otras cards del dashboard. */
.cuenta-dashboard .cuenta-mapaspots__cabecera > h2 {
  margin: 0; padding-bottom: 0; border: 0;
  font-size: 13px; line-height: 18px; letter-spacing: .06em; text-transform: uppercase; color: var(--color-text-muted);
}

/* La fila del selector lleva el swatch con el color de dificultad del bicho (el mismo del mapa). */
.cuenta-spots__bicho { justify-content: flex-start; }
.cuenta-spots__bicho--todos { justify-content: center; }
.cuenta-spots__datos { margin-left: auto; }
.cuenta-spots__marca {
  flex: none; width: 10px; height: 10px; border-radius: 1px; box-shadow: 0 0 0 1px rgba(0, 0, 0, .5);
}

/* ══ T-150 · it.4A (diseñador): arquitectura de 3 columnas + fila secundaria + ancho de /cuenta ══════
   Reemplaza la estructura 2-col de it.3 (esas reglas quedan superadas por éstas, que van después). El
   aside (mapa+spots+party) y el detalle ahora son HERMANOS —no anidados—, por eso el detalle vuelve a
   ser una columna simple y aparece una fila secundaria (ranking+eventos) que no estira las columnas. */

/* Ancho EXCEPCIONAL de /cuenta (pantalla de gestión), solo cuando el shell contiene el dashboard
   (`:has`), sin tocar el shell global de 1040 de las demás páginas. Hasta 767 ocupa el ancho normal;
   768+ usa el disponible; a partir de 1100 se acota a 1180; desde 1500, a 1480 (aprovecha 1920 sin
   hacer líneas larguísimas). Documentado: es una regla de UNA sola página; el `:has` la mantiene
   scopeada sin un parámetro de clase en el layout compartido. */
@media (min-width: 768px)  { .page-shell:has(.cuenta-dashboard) { max-width: none; } }
/* Desde 1200 (3 columnas): crece con un margen consistente de ~96px hasta toparse en 1480. Así a 1440
   el mapa tiene lugar (un cap fijo de 1180 lo ahogaba) y a 1920 no se ocupa todo el monitor. */
@media (min-width: 1200px) { .page-shell:has(.cuenta-dashboard) { max-width: min(1480px, calc(100vw - 96px)); } }

/* Mobile (<768, it.4C): UNA columna como FLEX, con orden explícito para la IA móvil:
   selector → pestañas → vista activa (personaje/mapa/party) → eventos (al fondo, secundario). */
.cuenta-dashboard__cols {
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
  width: 100%;
}
.cuenta-dashboard__lateral { order: 1; }
.cuenta-tabs              { order: 2; }
.cuenta-dashboard__detalle { order: 3; }
.cuenta-dashboard__aside   { order: 4; }
.cuenta-eventos            { order: 5; }
.cuenta-dashboard__detalle { display: flex; flex-direction: column; gap: var(--sp-3); min-width: 0; }
.cuenta-dashboard__aside { display: flex; flex-direction: column; gap: var(--sp-4); min-width: 0; }

/* Tablet (768–1279): personajes | detalle; eventos bajo el selector (col izq); mapa+party a ancho
   completo debajo. it.4C: eventos es su propia fila en la columna izquierda (misma ubicación visual
   que tenía dentro de la lateral); el detalle abarca las dos filas de arriba. */
@media (min-width: 768px) {
  .cuenta-dashboard__cols {
    display: grid;
    grid-template-columns: minmax(240px, 320px) minmax(0, 1fr);
    grid-template-areas:
      "lista   detalle"
      "eventos detalle"
      "mapa    mapa";
    align-items: start;
  }
  .cuenta-dashboard__lateral { grid-area: lista; }
  .cuenta-dashboard__detalle { grid-area: detalle; }
  .cuenta-dashboard__aside { grid-area: mapa; }
  .cuenta-eventos { grid-area: eventos; }
}

/* Escritorio (1280+): tres columnas — personajes | detalle | mapa+spots+party. Eventos bajo el
   selector en la col izquierda (misma ubicación de escritorio aprobada en 4B); detalle y mapa abarcan
   las dos filas. El MAPA es la columna que más crece. */
@media (min-width: 1280px) {
  .cuenta-dashboard__cols {
    grid-template-columns: minmax(240px, 270px) minmax(410px, 470px) minmax(320px, 1fr);
    grid-template-areas:
      "lista   detalle mapa"
      "eventos detalle mapa";
  }
}

/* Header: título + datos de cuenta compactos (usuario · email · estado) a la izquierda, acciones a la
   derecha. Los datos NO son una card alta: una línea. */
.cuenta-dashboard__identidad { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--sp-1) var(--sp-3); min-width: 0; }
.cuenta-dashboard__identidad > h1 { margin: 0; }
.cuenta-dashboard__datos {
  margin: 0; display: inline-flex; flex-wrap: wrap; align-items: center; gap: var(--sp-1) var(--sp-3);
  font-size: 14px; line-height: 20px; color: var(--color-text-muted);
}
.cuenta-dashboard__usuario { font-weight: 600; color: var(--color-text-strong); }
.cuenta-dashboard__email { overflow-wrap: anywhere; }

/* Party compacta (it.4A): filas finas separadas por filete, no una card por compañero. */
.cuenta-party__cabecera { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: var(--sp-1) var(--sp-3); }
.cuenta-party__lista { list-style: none; margin: var(--sp-2) 0 0; padding: 0; display: flex; flex-direction: column; }
.cuenta-party__fila {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-1) var(--sp-3);
  padding: var(--sp-2) 0; border-top: 1px solid var(--color-filete, var(--color-border));
}
.cuenta-party__fila:first-child { border-top: 0; }
.cuenta-party__fila .badge { margin-left: auto; }

/* Fila secundaria: la card de ranking no necesita ser alta; eventos reusa la tarjeta de la portada. */
.cuenta-dashboard .cuenta-ranking { padding: var(--sp-3) var(--sp-4); }

/* Botones del selector de spots: `appearance:none` para que iOS Safari no les ponga chrome nativo
   azul/blanco (el resto de los botones usa .action-button, que ya lo tiene). Se conservan focus,
   hover, disabled y el objetivo táctil de 44px definidos arriba. */
.cuenta-spots__bicho { -webkit-appearance: none; appearance: none; }

/* ══ T-150 · it.4A.1 (diseñador): mapa+selector lado a lado en tablet · Ranking en la col del detalle ══ */

/* El componente «Mapa y spots» se parte en mapa | selector LADO A LADO cuando ES ancho (tablet a
   ancho completo), y apilado cuando es angosto (la columna del 3-col). Decide por el ancho del PROPIO
   componente (container query), no del viewport: así el mismo componente se adapta en los dos lugares. */
.cuenta-mapaspots { container-type: inline-size; }
.cuenta-mapaspots__cuerpo { display: flex; flex-direction: column; gap: var(--sp-3); }
.cuenta-mapaspots__mapa-col { min-width: 0; }
.cuenta-mapaspots__sel-col { min-width: 0; }
@container (min-width: 680px) {
  .cuenta-mapaspots__cuerpo { flex-direction: row; align-items: flex-start; gap: var(--sp-4); }
  .cuenta-mapaspots__mapa-col { flex: 1 1 auto; }
  .cuenta-mapaspots__sel-col { flex: 0 0 248px; }
  .cuenta-mapaspots__sel-col .cuenta-spots__ayuda { margin-top: 0; }
  /* al lado del mapa hay alto de sobra: la lista completa, sin el scroll interno del modo apilado */
  .cuenta-mapaspots__sel-col .cuenta-spots__lista { max-height: none; }
}

/* Ranking compacto en la columna del detalle (it.4A.1: cerca del personaje, no en una fila aislada).
   Una sola fila: título + posición + link; nada de card alta. */
.cuenta-ranking__fila { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2) var(--sp-3); }
.cuenta-ranking__fila > h2 {
  margin: 0; padding: 0; border: 0;
  font-size: 13px; line-height: 18px; letter-spacing: .06em; text-transform: uppercase; color: var(--color-text-muted);
}
.cuenta-ranking__fila .cuenta-ranking__pos { margin: 0; }
.cuenta-ranking__fila .cuenta-ranking__num { font-size: 22px; line-height: 26px; }
.cuenta-ranking__fila .cuenta-ranking__de { font-size: 13px; }
.cuenta-ranking__fila .action-button { width: auto; margin-left: auto; }

/* ══ T-150 · it.4B (diseñador): densidad y jerarquía interna ══════════════════════════════════════════
   Aprobada 4A.1. Esto NO cambia columnas, breakpoints ni el ancho de /cuenta (reglas de arriba intactas);
   solo comprime y jerarquiza el contenido para que /cuenta se lea como un panel de juego y no como una
   pila de cards administrativas. Todo scopeado a `.cuenta-dashboard`: la ficha /cuenta/personaje/{id} no
   cambia. Las alturas bajan por padding/escala, nunca forzando alturas fijas (restricción del diseñador). */

/* 1 · Encabezado del personaje más apretado. Las tres acciones entran en UNA fila cuando hay ancho
   (no dejar «Resetear» solo en un renglón): botones con padding horizontal menor y gap chico dentro del
   detalle. El objetivo táctil de 44px de alto NO se toca. Repartir sigue primario; Resetear, contorno rojo. */
.cuenta-dashboard__detalle .personaje__cabecera { gap: var(--sp-1) var(--sp-3); }
.cuenta-dashboard__detalle .personaje__meta { margin: 0; gap: var(--sp-1) var(--sp-3); }
.cuenta-dashboard__detalle .acciones { gap: var(--sp-1); }
.cuenta-dashboard__detalle .acciones .action-button { padding-left: var(--sp-2); padding-right: var(--sp-2); font-size: 15px; }
/* los chips server/clase, un punto más chicos para que la fila 1 no empuje el estado abajo */
.cuenta-dashboard__detalle .personaje__cabecera .chip { font-size: 12px; }

/* 2 · KPIs compactos y jerarquizados. Siguen 2×2 (Saldo legible sin partir), pero con menos aire.
   Nivel y Resets (kpi--clave) destacados: fondo elevado + número en oro; Saldo y Puntos secundarios
   (número más chico, en texto fuerte). Los cuatro valores siguen visibles. */
.cuenta-dashboard__detalle .kpis { gap: var(--sp-2); }
.cuenta-dashboard__detalle .kpi { padding: var(--sp-2) var(--sp-3); }
.cuenta-dashboard__detalle .kpi__etiqueta { font-size: 11px; line-height: 15px; }
.cuenta-dashboard__detalle .kpi__valor { font-size: 22px; line-height: 26px; }
.cuenta-dashboard__detalle .kpi__sufijo { margin-top: 0; font-size: 11px; line-height: 14px; }
.cuenta-dashboard__detalle .kpi--clave { background-color: var(--color-surface-raised); border-color: var(--color-accent); }
.cuenta-dashboard__detalle .kpi--clave .kpi__valor { color: var(--color-accent); font-size: 26px; line-height: 30px; }

/* 3 · Próximo reset: el costo manda; la regla y la nota, breves y muted en una línea cada una. */
.cuenta-dashboard__detalle .personaje__reset .tarjeta__cuerpo { display: flex; flex-direction: column; gap: var(--sp-1); }
.cuenta-dashboard__detalle .personaje__reset .tarjeta__cuerpo > p { margin: 0; }
.cuenta-dashboard__detalle .personaje__costo { font-size: 17px; line-height: 24px; }
.cuenta-dashboard__detalle .personaje__reset .tarjeta__cuerpo > p:not(.personaje__costo) { font-size: 13px; line-height: 18px; color: var(--color-text-muted); }

/* 3b · Leveo offline: NO una card dentro de la card del detalle. Se aplana a una sección separada por un
   filete (como Stats / Próximo reset), sin su propio recuadro ni fondo. Mantiene título, estado y botón;
   la lógica del servidor no se toca. */
.cuenta-dashboard__detalle .personaje__offline {
  order: 10;
  padding: var(--sp-3) 0 0;
  margin-top: var(--sp-1);
  border: 0;
  border-top: 1px solid var(--color-filete, var(--color-border));
  border-radius: 0;
  background-color: transparent;
}
.cuenta-dashboard__detalle .personaje__offline .causa { font-size: 13px; line-height: 18px; margin: var(--sp-1) 0 0; }

/* 4 · Lista de personajes: filas compactas de 2 renglones (nombre + estado arriba, clase·nivel·resets
   abajo), no una card alta de 3 renglones apilados. Selección activa marcada con una barra de acento a
   la izquierda además del borde. Zona táctil de 44px conservada. */
.cuenta-dashboard .cuenta-pj {
  display: flex;
  flex-flow: row wrap;                 /* la base es column; acá queremos fila que envuelve */
  align-items: center;
  gap: var(--sp-1) var(--sp-2);
  padding: var(--sp-2) var(--sp-3);
  border-left-width: 3px;              /* el borde izquierdo es donde se pinta la selección */
}
/* El nombre NUNCA se corta: va primero y, si el estado es largo («Leveando offline», 124px) y no entra
   al lado en la lateral angosta de 270px, el badge baja a su propia línea en vez de recortar el nombre.
   Los datos (clase·nivel·resets) fuerzan su propio renglón (flex-basis 100%). Orden visual nombre→badge
   →datos vía `order`. Casi todas las filas quedan en 2 renglones; solo la de «Leveando offline» en 3. */
.cuenta-dashboard .cuenta-pj__nombre { order: 1; flex: 0 1 auto; min-width: 0; }
.cuenta-dashboard .cuenta-pj > .badge { order: 2; margin-left: auto; white-space: nowrap; font-size: 11px; line-height: 16px; padding: 2px var(--sp-2); }
.cuenta-dashboard .cuenta-pj__datos { order: 3; flex: 1 1 100%; font-size: 13px; line-height: 17px; }
.cuenta-dashboard .cuenta-pj--activa { border-left-color: var(--color-accent); }

/* 4b · Filtros de la lista: packean en 1–2 renglones (flex, no la grilla de 2 columnas que los forzaba a
   dos filas). 44px de alto conservados. */
.cuenta-dashboard .filter-pills { display: flex; flex-wrap: wrap; gap: var(--sp-1) var(--sp-2); }
.cuenta-dashboard .filter-pills .filter-pill,
.cuenta-dashboard .filter-pills .filter-pill__quitar { justify-content: center; }

/* 5 · Ranking y Eventos: se quedan donde están (Ranking en el detalle, Eventos en la lateral); solo
   densidad. La fila de ranking ya es compacta; a Eventos le saco aire sobrante. */
.cuenta-dashboard .cuenta-eventos { gap: var(--sp-1); }

/* 6 · Selector de bichos: el nombre arriba y «Nv X · N spots» debajo (wrapper .cuenta-spots__texto), así
   el nombre nunca se parte por competir con los datos. Fila más apretada (44px de alto igual). Selección
   con barra de acento a la izquierda además del borde. El color del swatch = el del marcador en el mapa. */
.cuenta-spots__bicho { gap: var(--sp-2); padding: var(--sp-1) var(--sp-3); border-left-width: 3px; }
.cuenta-spots__texto { display: flex; flex-direction: column; gap: 1px; min-width: 0; text-align: left; }
.cuenta-spots__nombre { line-height: 18px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cuenta-spots__bicho .cuenta-spots__datos { margin-left: 0; white-space: nowrap; }
.cuenta-spots__bicho--activo { border-left-color: var(--color-accent); }
.cuenta-spots__bicho--todos { padding: var(--sp-2) var(--sp-3); }

/* ══ T-150 · it.4C (diseñador): experiencia mobile-first ═══════════════════════════════════════════════
   Navegación interna Personaje · Mapa · Party en móvil (<768) para que el mapa quede a un toque, sin
   recorrer toda la página y SIN JavaScript de estado: pestañas = anclas a #v-personaje/#v-mapa/#v-party
   y el CSS (:target + :has) muestra solo la vista activa. El estado vive en el fragment de la URL (el
   back del navegador funciona); sin CSS, todas las vistas quedan visibles (nada inaccesible). En ≥768 se
   ocultan las pestañas y las tres vistas se ven lado a lado, como se aprobó en 4A.1/4B (sin cambios). */

/* ---- Pestañas (solo móvil) ---- */
.cuenta-tabs { display: none; }                 /* base: ocultas; se muestran solo en <768 */
@media (max-width: 767px) {
  .cuenta-tabs {
    display: flex; gap: var(--sp-1);
    padding: var(--sp-1);
    border: 1px solid var(--color-border); border-radius: var(--r-md);
    background-color: var(--color-surface);
  }
  .cuenta-tabs__link {
    flex: 1 1 0; min-height: 44px;
    display: inline-flex; align-items: center; justify-content: center;
    padding: var(--sp-1) var(--sp-2); border-radius: var(--r-sm);
    color: var(--color-text); font-weight: 600; text-decoration: none; text-align: center;
  }
  .cuenta-tabs__link:hover { background-color: var(--color-surface-raised); }
  /* Pestaña activa (resaltada en oro). La activa por defecto, sin fragment, es Personaje. */
  .cuenta-dashboard:not(:has(.cuenta-vista:target)) .cuenta-tabs__link--personaje,
  .cuenta-dashboard:has(#v-personaje:target) .cuenta-tabs__link--personaje,
  .cuenta-dashboard:has(#v-mapa:target)      .cuenta-tabs__link--mapa,
  .cuenta-dashboard:has(#v-party:target)     .cuenta-tabs__link--party {
    background-color: var(--color-accent); color: var(--color-on-accent);
  }

  /* ---- Mostrar SOLO la vista activa (móvil) ---- */
  .cuenta-vista { display: none; }
  /* Personaje es el default cuando ninguna vista es :target */
  .cuenta-dashboard:not(:has(.cuenta-vista:target)) #v-personaje { display: flex; }
  #v-personaje:target { display: flex; }
  /* El aside contiene mapa y party: se muestra solo si una de sus vistas es la activa, y adentro se ve
     solo esa (la otra queda en display:none por .cuenta-vista). Así no deja un hueco cuando no es la
     sección activa. */
  .cuenta-dashboard__aside { display: none; }
  .cuenta-dashboard__aside:has(.cuenta-vista:target) { display: flex; }
  #v-mapa:target  { display: flex; }
  #v-party:target { display: flex; }
  /* al saltar a una vista, que no quede tapada por las pestañas pegajosas */
  .cuenta-vista { scroll-margin-top: 64px; }

  /* El mapa aprovecha casi todo el ancho: menos padding de card en móvil (el cap de 560 no molesta
     porque la columna es angosta). */
  .cuenta-dashboard .cuenta-dashboard__mapa { padding: var(--sp-3); }
  .cuenta-dashboard__aside .mapa-vivo__lienzo { max-width: none; }
}

/* ---- Acciones de cuenta: compactas en móvil para que no dominen el inicio (it.4C) ----
   Quedan como el <div> probado de 4B (en el header, en línea a la derecha en ≥768). En <768 se hacen
   más chicas (menos alto/tipografía) para ceder protagonismo al personaje y al mapa. */
@media (max-width: 767px) {
  .cuenta-dashboard__encabezado .cuenta-dashboard__acciones { gap: var(--sp-1); }
  /* más chicas en tipografía/padding, pero el objetivo táctil se mantiene en 44px (regla del sitio). */
  .cuenta-dashboard__encabezado .cuenta-dashboard__acciones .action-button {
    padding: var(--sp-1) var(--sp-2); font-size: 13px;
  }
}

/* ---- Selector de personajes (it.4C) ----
   Dos presentaciones de la MISMA lista (enlaces GET, sin forms → se puede duplicar sin efectos):
   · --desktop: la sección de siempre, visible solo en ≥768.
   · --movil: un <details> colapsable visible solo en <768; colapsado muestra la tarjeta compacta del
     pj seleccionado (summary) y se despliega con un toque.
   Así el escritorio aprobado NO depende de ningún "force-open" de <details>. */
.cuenta-dashboard .cuenta-selector--desktop { display: none; }
@media (min-width: 768px) { .cuenta-dashboard .cuenta-selector--desktop { display: flex; flex-direction: column; gap: var(--sp-3); } }

.cuenta-selector--movil { display: flex; flex-direction: column; gap: var(--sp-3); }
@media (min-width: 768px) { .cuenta-selector--movil { display: none; } }
.cuenta-selector--movil > summary { list-style: none; cursor: pointer; }
.cuenta-selector--movil > summary::-webkit-details-marker { display: none; }
/* Colapsado: solo el summary (tarjeta del pj seleccionado). */
.cuenta-selector--movil:not([open]) > :not(summary) { display: none; }
/* La fila compacta del pj seleccionado (el summary). NO lleva marco propio: la card es el <details>
   (evita card-dentro-de-card). Al abrir, queda como cabecera con un filete abajo separando la lista. */
.cuenta-selector__actual {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-1) var(--sp-2);
}
.cuenta-selector--movil[open] .cuenta-selector__actual {
  padding-bottom: var(--sp-2); border-bottom: 1px solid var(--color-filete, var(--color-border));
}
.cuenta-selector__actual-nombre { font-weight: 600; color: var(--color-text-strong); }
/* El título "Tus personajes" dentro del <details> móvil (mismo estilo de panel que los otros h2). */
.cuenta-selector__titulo-movil {
  margin: 0; padding-bottom: var(--sp-1);
  font-size: 13px; line-height: 18px; letter-spacing: .06em; text-transform: uppercase; color: var(--color-text-muted);
}
.cuenta-selector__actual-datos { flex: 1 1 100%; font-size: 13px; line-height: 17px; color: var(--color-text-muted); }
.cuenta-selector__actual .badge { margin-left: auto; font-size: 11px; line-height: 16px; padding: 2px var(--sp-2); white-space: nowrap; }
.cuenta-selector__cambiar {
  margin-left: auto; align-self: center;
  font-size: 12px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; color: var(--color-accent);
}
.cuenta-selector__cambiar::after { content: " ▾"; }
.cuenta-selector--movil[open] .cuenta-selector__cambiar::after { content: " ▴"; }

/* ══ T-150 · it.4D (diseñador): acciones en overlay <dialog> sobre /cuenta ════════════════════════════
   reset / repartir puntos / reset-stats abren un <dialog> nativo ARRIBA de /cuenta en vez de navegar al
   view viejo. El <dialog> ES la card; adentro reusa .dialogo / .personaje / .formulario (se aplanan para
   no hacer card-dentro-de-card). Sin JS el <dialog> no se abre y los enlaces navegan como siempre. */
/* it.4D.1 (diseñador): el <dialog> es una COLUMNA FLEX con tres zonas — barra de cierre FIJA arriba,
   cuerpo SCROLLABLE en el medio, y footer de acciones PEGAJOSO abajo. Antes scrolleaba el <dialog>
   entero, así que en diálogos largos (reset-stats, puntos) el cierre y los botones se iban con el
   scroll. `dvh` acota al viewport REAL (se achica con el teclado móvil). */
.cuenta-overlay {
  display: flex;
  flex-direction: column;
  width: min(560px, calc(100vw - 2 * var(--sp-4)));
  max-width: 560px;
  max-height: calc(100dvh - 2 * var(--sp-4));
  padding: 0;
  border: 1px solid var(--color-border-strong);
  border-radius: var(--r-md);
  background-color: var(--color-surface);
  color: var(--color-text);
  overflow: hidden;                 /* el que scrollea es el cuerpo, no el <dialog> */
}
/* it.4D.5 (BUG-1): el <dialog> nativo cerrado es display:none, pero el display:flex de arriba lo
   pisaba y la caja vacía (con la ×) se veía en CADA carga de /cuenta, solapando la fila de identidad.
   Se restaura el oculto en estado cerrado; el display:flex solo aplica cuando está [open] (showModal). */
.cuenta-overlay:not([open]) { display: none; }
.cuenta-overlay::backdrop { background: rgba(0, 0, 0, .62); }
.cuenta-overlay__barra {
  flex: 0 0 auto;                   /* barra de cierre FIJA arriba */
  display: flex; justify-content: flex-end; margin: 0; padding: var(--sp-2) var(--sp-2) 0;
}
.cuenta-overlay__cerrar {
  min-width: 44px; min-height: 44px;
  display: inline-flex; align-items: center; justify-content: center;
  border: 0; border-radius: var(--r-sm); background: transparent;
  color: var(--color-text-muted); font-size: 22px; line-height: 1; cursor: pointer;
}
.cuenta-overlay__cerrar:hover { background: var(--color-surface-raised); color: var(--color-text-strong); }
.cuenta-overlay__cuerpo {
  flex: 1 1 auto;                   /* ocupa el resto y SCROLLEA */
  overflow-y: auto; overflow-x: hidden;
  overscroll-behavior: contain;     /* no encadena el scroll al documento de atrás */
  padding: 0 var(--sp-4);
}

/* Footer de acciones PEGAJOSO: confirmación/cancelación siempre alcanzables sin tener que llegar al
   fondo. Se queda pegado abajo del cuerpo (con su filete y su fondo, para que el contenido pase por
   detrás sin mezclarse). Vale para los dos contenedores de botones (reset/reset-stats y puntos). */
.cuenta-overlay__cuerpo .dialogo__botones {
  position: sticky;
  bottom: 0;
  margin-top: var(--sp-3);
  padding: var(--sp-3) 0;
  background-color: var(--color-surface);
  box-shadow: 0 -1px 0 var(--color-filete, var(--color-border));
}

/* El contenido reusa los estilos existentes, aplanado para que la card sea el <dialog>: */
.cuenta-overlay__cuerpo .migas { display: none; }               /* en un modal, las migas sobran */
.cuenta-overlay__cuerpo > .personaje { gap: var(--sp-3); }
.cuenta-overlay__cuerpo h1 { font-size: 20px; line-height: 26px; margin: 0; }
.cuenta-overlay__cuerpo h2 { font-size: 18px; line-height: 24px; margin: 0; }
.cuenta-overlay__cuerpo .dialogo {
  margin: 0; padding: 0; max-width: none; border: 0; background: transparent; gap: var(--sp-3);
}
.cuenta-overlay__cuerpo .dialogo::before,
.cuenta-overlay__cuerpo .dialogo::after { display: none; }       /* sin las esquinas decorativas adentro */
.cuenta-overlay__cuerpo .formulario--ancho { max-width: none; }

/* T-193: el modal es EL modal de la cuenta. Lo que entra adentro se aplana para que la card sea el
   <dialog>: la tarjeta de la contraseña (antes página con marco) y el mapa de todos. */
.cuenta-overlay__cuerpo .tarjeta {
  margin: 0; padding: 0; max-width: none; border: 0; background: transparent; box-shadow: none;
}
.cuenta-overlay__cuerpo .tarjeta::before, .cuenta-overlay__cuerpo .tarjeta::after { display: none; }
.cuenta-overlay__cuerpo .cuenta-paso { padding-bottom: var(--sp-3); }
.cuenta-overlay__cuerpo .cuenta-paso > .action-button { margin-top: var(--sp-3); }
.cuenta-overlay__cuerpo .mapa-vivo__server h2 { font-size: 16px; line-height: 22px; margin: var(--sp-3) 0 var(--sp-2); }
/* El mapa de todos muestra varios personajes: va más ancho que un diálogo de confirmación. */
.cuenta-overlay--mapa {
  width: min(920px, calc(100vw - 2 * var(--sp-4)));
  max-width: 920px;
}
.cuenta-overlay--mapa .mapa-vivo__server {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: var(--sp-4);
}
.cuenta-overlay--mapa .mapa-vivo__server > h2,
.cuenta-overlay--mapa .mapa-vivo__server > p { grid-column: 1 / -1; }
/* AC-CU-16: en el teléfono el modal ocupa la pantalla y los botones quedan al pie (sticky). */
@media (max-width: 599px) {
  .cuenta-overlay, .cuenta-overlay--mapa {
    width: 100vw; max-width: none; max-height: 100dvh; margin: 0;
    border: 0; border-radius: 0;
  }
}
/* El aviso de un fetch que no llegó (cuenta.js), al lado del formulario que lo disparó. */
.cuenta-falla-de-red { color: var(--color-crit, var(--color-text-strong)); }

/* it.4D: en el form de puntos dentro del overlay (angosto), el input "Agregar" se desbordaba. Que
   ocupe su propio renglón a ancho completo, con box-sizing correcto, para no cortarse a 390. */
.cuenta-overlay__cuerpo .data-table td { flex-wrap: wrap; }
.cuenta-overlay__cuerpo .campo__control { flex: 1 1 100%; min-width: 0; box-sizing: border-box; width: 100%; }

/* it.4D.2 (diseñador): el banner de error con enlace (p. ej. "Server fuera de línea" + "Ver el estado
   de los servers") se comprimía en el overlay angosto: el enlace (nowrap + margin-left:auto) se comía
   un ancho fijo y el texto quedaba en una columna de 1-2 palabras por línea. Scopeado al overlay (la
   banda es angosta siempre acá): el texto ocupa el ancho y el ENLACE baja a su propia fila. La banda
   global del resto del sitio no se toca (misma identidad). */
.cuenta-overlay__cuerpo .banner { flex-wrap: wrap; }
.cuenta-overlay__cuerpo .banner__texto { flex: 1 1 auto; min-width: 0; }
.cuenta-overlay__cuerpo .banner__cuerpo { flex: 1 1 auto; min-width: 0; }
.cuenta-overlay__cuerpo .banner__accion { flex: 1 0 100%; margin-left: 0; margin-top: var(--sp-2); }

/* ===== Novedades (changelog) — T-153 (UI v1.1) ======================================
   Página pública: lista de novedades y ficha por slug. Todo con tokens del design system. */
.novedades__lista {
  list-style: none;
  margin: var(--sp-5) 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
}
.novedad-card {
  position: relative;                          /* ancla del enlace estirado del título */
  border: 1px solid var(--color-border);
  border-left: 3px solid var(--color-border-strong);
  border-radius: var(--r-md);
  background-color: var(--color-surface);
  padding: var(--sp-4) var(--sp-5);
  transition: border-color .12s ease, background-color .12s ease;
}
.novedad-card:hover { border-color: var(--color-border-strong); background-color: var(--color-surface-raised); }
/* El color de la categoría tiñe el filo izquierdo: da ritmo sin ruido (los tokens del badge). */
.novedad-card--ok { border-left-color: var(--color-ok); }
.novedad-card--warn { border-left-color: var(--color-warn); }
.novedad-card--crit { border-left-color: var(--color-crit); }
.novedad-card--neutral { border-left-color: var(--color-neutral); }
/* El foco se pinta sobre la card entera (el enlace estirado la cubre). */
.novedad-card:focus-within { outline: 2px solid var(--color-focus); outline-offset: 2px; }

.novedad-card__meta,
.novedad__meta {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  margin-bottom: var(--sp-2);
}
.novedad-card__fecha,
.novedad__fecha { color: var(--color-text-muted); font-size: 0.85rem; font-variant-numeric: tabular-nums; }
.novedad-card__titulo {
  margin: 0 0 var(--sp-1);
  font-family: var(--font-display);
  font-size: 1.2rem;
  line-height: 1.25;
}
.novedad-card__titulo a { color: var(--color-text-strong); text-decoration: none; }
.novedad-card__titulo a:hover { color: var(--color-accent); }
.novedad-card__titulo a::after { content: ""; position: absolute; inset: 0; }  /* card entera clickeable */
.novedad-card__titulo a:focus-visible { outline: none; }                         /* lo pinta la card */
.novedad-card__resumen { margin: 0; color: var(--color-text-muted); line-height: 1.5; }

.paginacion {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  margin-top: var(--sp-6);
  padding-top: var(--sp-4);
  border-top: 1px solid var(--color-rule);
  flex-wrap: wrap;
}
.paginacion__estado { color: var(--color-text-muted); font-size: 0.85rem; font-variant-numeric: tabular-nums; }
.paginacion__enlace { color: var(--color-accent); text-decoration: none; font-weight: 600; }
.paginacion__enlace:hover { color: var(--color-accent-hover); text-decoration: underline; }

.novedad { max-width: 68ch; margin: 0 auto; }
.novedad__volver { margin: 0 0 var(--sp-4); }
.novedad__volver a { color: var(--color-text-muted); text-decoration: none; font-size: 0.9rem; }
.novedad__volver a:hover { color: var(--color-accent); text-decoration: underline; }
.novedad__cabecera {
  padding-bottom: var(--sp-4);
  border-bottom: 1px solid var(--color-rule);
  margin-bottom: var(--sp-5);
}
.novedad__cabecera h1 { font-family: var(--font-display); margin: var(--sp-2) 0 0; }
.novedad__resumen { color: var(--color-text-muted); font-size: 1.1rem; line-height: 1.5; margin: var(--sp-3) 0 0; }
.novedad__cuerpo {
  color: var(--color-text);
  line-height: 1.7;
  white-space: pre-wrap;   /* el cuerpo es texto plano: respeta los saltos de línea sin th:utext */
  overflow-wrap: break-word;
}

/* ===========================================================================
   EXPLORADOR DE MAPAS Y DROPS (T-198) · `/spots`
   Maqueta aprobada: projectos/mu/design/explorador/maqueta.html. Reusa el lienzo del mapa en vivo
   (`.mapa-vivo__lienzo`), el marcador, el retrato y la marca de `/spots` (`.spots__marcador`,
   `.spots__retrato`, `.spots__marca`) y las pestañas (`.spots__tarjeta--pestania`).
   Escritorio: contenedor de 1.280 (no el de 1.040 del resto), mapa fijo a la izquierda (560) y monstruos
   y drops a la derecha EN EL FLUJO de la página (sin scroll interno). Teléfono: una columna, un panel a la
   vez (el segmento lo enciende `spots.js`; sin JS se ven los dos). Los estados van en atributos
   (`aria-current`, `data-on`, `data-apagado`) porque Thymeleaf no deja dos `__` en un atributo th:.
   =========================================================================== */
.page-shell:has(> .explorador) { max-width: 1280px; padding-top: var(--sp-5); }
.explorador { display: flex; flex-direction: column; gap: var(--sp-4); }
.explorador__titulo { margin-bottom: 0; }

/* ── FILTROS: qué busco. */
.explorador__filtros { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 20px; padding: 10px 0; border-top: 1px solid var(--color-rule); border-bottom: 1px solid var(--color-rule); }
.explorador__grupo { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2); }
.explorador__rotulo { font-size: 11px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; color: var(--color-text-muted); }
.explorador__campo { display: flex; align-items: center; gap: 6px; font-size: 14px; }
.explorador__campo .campo__control { width: auto; min-height: 36px; height: 36px; padding: 4px 8px; }
.explorador__campo input[type=number] { width: 76px; text-align: center; }
.explorador__q { width: 220px !important; }
.explorador__aplicar { min-height: 36px; }
.explorador__cats { display: flex; flex-wrap: wrap; gap: 4px; flex-basis: 100%; }
.explorador__chip { display: inline-flex; align-items: center; gap: 6px; min-height: 30px; padding: 2px 10px; border: 1px solid var(--color-rule); border-radius: var(--r-full); font-size: 13px; color: var(--color-text); text-decoration: none; }
.explorador__chip:hover { border-color: var(--color-border); }
.explorador__chip[aria-current=true] { background: var(--color-accent); border-color: var(--color-accent); color: var(--color-on-accent); font-weight: 600; }
.explorador__chip-n { font-size: 11px; opacity: .75; }
.explorador__limpiar, .explorador__accion, .explorador__aviso a, .explorador__estado-filtro a, .explorador__vacio a,
.explorador__ver-mapa, .explorador__ficha-meta a { color: var(--color-accent); text-decoration: underline; text-underline-offset: 3px; font-size: 14px; }
.explorador__limpiar:hover, .explorador__accion:hover, .explorador__aviso a:hover { color: var(--color-accent-hover); }
.explorador__buscador { position: relative; }

/* ── SELECCIÓN: dónde estoy. Fija arriba al bajar. */
.explorador__paneles { display: flex; flex-direction: column; gap: var(--sp-4); }
.explorador__seleccion { position: sticky; top: 0; z-index: 5; display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2); padding: var(--sp-2) 0; background: var(--color-bg); }
.explorador__sel { display: inline-flex; align-items: center; gap: 6px; min-height: 32px; padding: 2px 4px 2px 10px; border: 1px solid var(--color-accent); border-radius: var(--r-full); background: var(--color-surface-raised); color: var(--color-text-strong); font-size: 14px; }
.explorador__sel--mapa, .explorador__sel--vacia { padding-right: 10px; }
.explorador__sel--vacia { border-style: dashed; border-color: var(--color-rule); background: transparent; color: var(--color-text-muted); }
.explorador__sel img { width: 22px; height: 22px; object-fit: contain; }
.explorador__sel-tipo { font-size: 11px; color: var(--color-text-muted); text-transform: uppercase; letter-spacing: .08em; }
.explorador__sel-x { display: grid; place-items: center; width: 26px; height: 26px; border-radius: 50%; color: var(--color-text-muted); text-decoration: none; font-size: 16px; line-height: 1; }
.explorador__sel-x:hover { background: var(--color-surface); color: var(--color-text-strong); }
.explorador__ver-mapa { display: none; margin-left: auto; }

/* ── MAPAS: pestañas y "todos". */
.explorador__mapas { display: flex; align-items: flex-end; gap: 4px 12px; box-shadow: inset 0 -1px 0 var(--color-rule); }
.explorador__tabs { flex: 1 1 auto; min-width: 0; }
/* El texto solo para lectores es absoluto: sin esto escapa del recorte de la banda y estira la página. */
.explorador__tabs .spots__tarjeta { position: relative; }
.spots__tarjeta--pestania[aria-current=true] { border-bottom-color: var(--color-accent); }
.spots__tarjeta--pestania[aria-current=true] .spots__tarjeta-nombre { color: var(--color-accent); }
.spots__tarjeta--pestania[data-atenuada]:not([aria-current]) { opacity: .6; }
.explorador__tab-cae { font-size: 11px; line-height: 16px; color: var(--color-ok); white-space: nowrap; }
.explorador__todos { flex: none; display: flex; gap: 6px; align-items: center; padding-bottom: 6px; }
.explorador__todos .campo__control { width: auto; max-width: 240px; height: 36px; min-height: 36px; padding: 4px 8px; }
.explorador__ir { min-height: 36px; }

/* ── CUERPO: mapa fijo | monstruos + drops. */
.explorador__cuerpo { display: grid; grid-template-columns: 560px minmax(0, 1fr); gap: var(--sp-6); align-items: start; }
.explorador__col-mapa { position: sticky; top: 56px; display: flex; flex-direction: column; gap: 10px; scroll-margin-top: 56px; }
.explorador__col-info { display: flex; flex-direction: column; gap: 28px; min-width: 0; }
.explorador__mapa-cab { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 10px; }
.explorador__mapa-cab h2 { margin: 0; font-size: 26px; line-height: 32px; }
.explorador__lienzo { max-width: none; background: var(--color-surface); }
.explorador__vista { position: absolute; left: 0; top: 0; width: 100%; height: 100%; }
.explorador__marcador { text-decoration: none; }
.explorador__lienzo[data-foco] .explorador__marcador { opacity: .25; }
.explorador__lienzo[data-foco] .explorador__marcador[data-on] {
  opacity: 1; z-index: 2; transform: scale(1.6);
  box-shadow: 0 0 0 2px var(--color-accent), 0 0 0 4px rgba(0, 0, 0, .7), 0 0 14px 4px rgba(214, 178, 94, .55);
}
.explorador__lienzo[data-foco] .explorador__marcador[data-on=item] { box-shadow: 0 0 0 2px var(--color-ok), 0 0 0 4px rgba(0, 0, 0, .7), 0 0 14px 4px rgba(76, 194, 122, .5); }
@media (prefers-reduced-motion: no-preference) {
  .explorador__lienzo[data-foco] .explorador__marcador[data-on] { animation: spots-pulso 1s ease-in-out 2; }
}
.explorador__zona { position: absolute; border: 2px dashed var(--color-accent); background: rgba(214, 178, 94, .12); border-radius: 2px; pointer-events: none; }
.explorador__zona[data-on=item] { border-color: var(--color-ok); background: rgba(76, 194, 122, .12); }
.explorador__zona-n { position: absolute; left: 4px; top: 2px; font-size: 11px; line-height: 1; font-weight: 700; color: var(--color-text-strong); text-shadow: 0 0 3px #000; white-space: nowrap; }
.explorador__leyenda { text-align: left; }
.explorador__otros { display: flex; flex-direction: column; gap: 6px; padding-top: 4px; }
.explorador__otros-lista { display: flex; flex-wrap: wrap; gap: 6px; }
.explorador__otro { display: inline-flex; flex-direction: column; padding: 4px 10px; border: 1px solid var(--color-rule); border-radius: var(--r-md); background: var(--color-surface); text-decoration: none; }
.explorador__otro:hover { border-color: var(--color-ok); }
.explorador__otro b { font-weight: 600; color: var(--color-text-strong); font-size: 13px; }
.explorador__otro-r { color: var(--color-text-muted); font-weight: 400; }
.explorador__otro > span { font-size: 12px; color: var(--color-ok); }

/* secciones de la columna derecha */
.explorador__sec-cab { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 4px 12px; margin-bottom: 10px; }
.explorador__sec-cab h3 { margin: 0; font-family: var(--font-text); font-size: 12px; line-height: 16px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; color: var(--color-text-muted); }
.explorador__sec-cab p { margin: 0; font-size: 13px; color: var(--color-text-muted); }

/* monstruos: grilla compacta */
.explorador__bichos { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 6px; }
.explorador__bicho { display: flex; align-items: center; gap: var(--sp-2); min-height: 52px; padding: 4px 8px 4px 4px; border: 1px solid var(--color-rule); border-radius: var(--r-md); color: var(--color-text); text-decoration: none; }
.explorador__bicho:hover { border-color: var(--color-border); background: var(--color-surface); }
.explorador__bicho[aria-current=true] { border-color: var(--color-accent); background: var(--color-surface-raised); box-shadow: inset 0 0 0 1px var(--color-accent); }
.explorador__bicho[data-apagado] { opacity: .72; }
.explorador__bicho[data-apagado] .spots__retrato { filter: grayscale(.7); }
.explorador__bicho[data-tira] { border-color: var(--color-ok); }
.explorador__bicho .spots__retrato--con-dibujo { width: 46px; height: 46px; }
.explorador__bicho .spots__retrato-dibujo { width: 60px; height: 60px; margin: -7px; }
.explorador__bicho-t { display: flex; flex-direction: column; min-width: 0; }
.explorador__bicho-n { font-size: 14px; font-weight: 600; color: var(--color-text-strong); line-height: 1.2; overflow-wrap: anywhere; }
.explorador__bicho-d { font-size: 12px; color: var(--color-text-muted); }
.explorador__bicho-p { font-size: 12px; color: var(--color-ok); font-weight: 600; }
.explorador__bicho-no { font-size: 12px; color: var(--color-text-muted); font-style: italic; }
.explorador__bichos-sub { margin: 14px 0 8px; font-size: 13px; color: var(--color-text-muted); }
.explorador__bichos-sub b { color: var(--color-text); }

/* avisos */
.explorador__avisos { display: flex; flex-direction: column; gap: var(--sp-2); }
.explorador__avisos:empty { display: none; }
.explorador__aviso { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px; padding: 10px 12px; border-left: 3px solid var(--color-warn); background: rgba(240, 177, 74, .08); border-radius: 0 var(--r-md) var(--r-md) 0; font-size: 14px; }
.explorador__aviso--info { border-left-color: var(--color-accent); background: rgba(214, 178, 94, .06); }
.explorador__aviso strong { color: var(--color-text-strong); }
.explorador__aviso-acciones { display: flex; flex-wrap: wrap; gap: 6px 12px; }

/* ficha del ítem elegido */
.explorador__ficha { display: grid; grid-template-columns: 64px minmax(0, 1fr); gap: var(--sp-3); padding: var(--sp-3); margin-bottom: 14px; border: 1px solid var(--color-ok); border-radius: var(--r-md); background: var(--color-surface); }
.explorador__ficha-img { width: 64px; height: 64px; display: grid; place-items: center; background: var(--color-bg); border-radius: var(--r-md); }
.explorador__ficha-img img { max-width: 56px; max-height: 56px; object-fit: contain; }
.explorador__ficha h4 { margin: 0; font-size: 18px; color: var(--color-text-strong); }
.explorador__ficha-meta { margin: 0; font-size: 13px; color: var(--color-text-muted); }
.explorador__ficha-quien { display: flex; flex-wrap: wrap; gap: 6px; margin-top: var(--sp-2); }
.explorador__quien { display: inline-flex; align-items: center; gap: 6px; padding: 2px 10px 2px 2px; border: 1px solid var(--color-rule); border-radius: var(--r-full); color: var(--color-text); text-decoration: none; font-size: 13px; }
.explorador__quien:hover { border-color: var(--color-ok); }
.explorador__quien .spots__marca { width: 22px; height: 22px; font-size: 11px; }
.explorador__quien b { color: var(--color-ok); font-weight: 600; }

/* drops por categoría */
.explorador__drops { display: flex; flex-direction: column; gap: 18px; }
.explorador__cab-bicho { display: flex; align-items: center; gap: var(--sp-3); padding: 8px 10px; margin-bottom: 14px; border: 1px solid var(--color-accent); border-radius: var(--r-md); background: var(--color-surface); }
.explorador__cab-bicho-t { display: flex; flex-direction: column; min-width: 0; flex: 1 1 auto; }
.explorador__cab-bicho-n { font-size: 17px; font-weight: 700; color: var(--color-text-strong); }
.explorador__cab-bicho-d { font-size: 13px; color: var(--color-text-muted); }
.explorador__cat h4 { margin: 0 0 6px; display: flex; align-items: baseline; gap: var(--sp-2); font-family: var(--font-text); font-size: 14px; font-weight: 700; color: var(--color-text-strong); }
.explorador__cat h4 span { font-size: 12px; font-weight: 400; color: var(--color-text-muted); }
.explorador__items { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 4px 8px; }
.explorador__it { display: grid; grid-template-columns: 34px minmax(0, 1fr); align-items: center; gap: var(--sp-2); min-height: 44px; padding: 3px 6px; border: 1px solid transparent; border-radius: var(--r-md); color: var(--color-text); text-decoration: none; }
.explorador__it:hover { border-color: var(--color-rule); background: var(--color-surface); }
.explorador__it[aria-current=true] { border-color: var(--color-ok); background: var(--color-surface); }
.explorador__it-img { width: 34px; height: 34px; display: grid; place-items: center; background: var(--color-bg); border-radius: 3px; }
.explorador__it-img img { max-width: 30px; max-height: 30px; object-fit: contain; }
.explorador__it-t { display: flex; flex-direction: column; min-width: 0; }
.explorador__it-n { font-size: 14px; line-height: 1.2; overflow-wrap: anywhere; }
.explorador__it-d { font-size: 12px; color: var(--color-text-muted); }
.explorador__it-d b { color: var(--color-ok); font-weight: 600; }
.explorador__exc { color: #7FD3FF; font-size: 11px; font-weight: 700; }
.explorador__mas { margin-top: 4px; }
.explorador__mas > summary { color: var(--color-accent); text-decoration: underline; text-underline-offset: 3px; font-size: 14px; cursor: pointer; list-style: none; }
.explorador__mas > summary::-webkit-details-marker { display: none; }
.explorador__mas[open] > summary { display: none; }
.explorador__vacio { padding: var(--sp-4) 0; margin: 0; color: var(--color-text-muted); }
.explorador__estado-filtro { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 10px; margin: -2px 0 14px; padding: 6px 10px; border-left: 3px solid var(--color-accent); background: rgba(214, 178, 94, .07); font-size: 14px; }
.explorador__estado-filtro b { color: var(--color-text-strong); }
.explorador__estado-filtro span { color: var(--color-text-muted); }

/* teléfono */
.explorador__segmento { display: none; }
@media (max-width: 960px) {
  .explorador__cuerpo { grid-template-columns: minmax(0, 1fr); gap: var(--sp-4); }
  .explorador__col-mapa { position: static; }
  .explorador__segmento:not([hidden]) { display: flex; border: 1px solid var(--color-border); border-radius: var(--r-md); overflow: hidden; }
  .explorador__segmento button { flex: 1 1 0; min-height: 44px; border: 0; background: transparent; color: var(--color-text); cursor: pointer; font: inherit; font-weight: 600; }
  .explorador__segmento button[aria-pressed=true] { background: var(--color-accent); color: var(--color-on-accent); }
  .explorador__col-info[data-panel=bichos][data-js] .explorador__sec-drops { display: none; }
  .explorador__col-info[data-panel=drops][data-js] .explorador__sec-bichos { display: none; }
  .explorador__ver-mapa { display: inline-flex; }
}
@media (max-width: 560px) {
  .explorador__filtros { gap: var(--sp-2); }
  .explorador__grupo--busqueda, .explorador__buscador, .explorador__buscador .explorador__campo { width: 100%; }
  .explorador__q { width: 100% !important; }
  .explorador__bichos { grid-template-columns: 1fr 1fr; }
  .explorador__bicho .spots__retrato--con-dibujo { width: 38px; height: 38px; }
  .explorador__bicho .spots__retrato-dibujo { width: 50px; height: 50px; margin: -6px; }
  .explorador__items { grid-template-columns: minmax(0, 1fr); }
  .explorador__mapas { flex-wrap: wrap; }
  .explorador__tabs { flex-basis: 100%; }
  .explorador__todos .campo__control { max-width: 180px; }
}
/* buscador: sugerencias del combobox (spots.js) */
.explorador__sugerencias { position: absolute; z-index: 20; top: calc(100% + 4px); left: 0; width: min(420px, calc(100vw - 32px)); max-height: 360px; overflow-y: auto; margin: 0; padding: 4px; list-style: none;
  background: var(--color-surface-raised); border: 1px solid var(--color-border); border-radius: var(--r-md); box-shadow: 0 8px 24px rgba(0, 0, 0, .6); }
.explorador__sug { display: grid; grid-template-columns: 30px minmax(0, 1fr); align-items: center; gap: var(--sp-2); padding: 6px 8px; border-radius: 3px; cursor: pointer; }
.explorador__sug[aria-selected=true], .explorador__sug:hover { background: var(--color-surface); outline: 1px solid var(--color-accent); }
.explorador__sug img { max-width: 28px; max-height: 28px; object-fit: contain; }
.explorador__sug b { display: block; font-weight: 600; color: var(--color-text-strong); }
.explorador__sug small { color: var(--color-text-muted); font-size: 12px; }
.explorador__sug small.explorador__sug-cae { color: var(--color-ok); }
.explorador__sug--filtro { grid-template-columns: 1fr; color: var(--color-accent); font-size: 14px; }
.explorador__sug--mas { grid-template-columns: 1fr; color: var(--color-text-muted); font-size: 13px; cursor: default; }
.explorador__sug--mas:hover { background: none; outline: none; }
.explorador [hidden] { display: none !important; }
