/* Estas se usaban en todo el lobby y la mesa sin estar definidas: una
     var(--x) inexistente no cae al valor anterior, invalida la declaracion
     entera. Por eso el borde del jugador en turno salia blanco (currentColor)
     en vez de ambar y las etiquetas KO/PKO salian sin color. */
  /* Issue #1106 — negro y oro, no verde.
     El cliente se veia igual que tatopoker.com, que es el producto de ESTUDIO.
     Este es el club donde se juega y tiene que verse como un club.

     La paleta no se invento: es la que TatoClub ya tenia, subida de excepcion a
     base. Y su decision de fondo se conserva — el oro va con un negro CALIDO,
     porque sobre un negro puro el dorado se ve amarillo barato y la diferencia
     esta en que el fondo tenga una pizca del mismo pigmento.

     Lo que MAS hacia ver verde no era el verde: eran los neutros. `--ink` era
     #edf4ef (un blanco verdoso) y `--muted` #9aa39f (un gris verdoso), usados
     89 veces entre los dos. Un acento dorado sobre neutros verdes no da negro y
     oro: da las dos cosas peleandose. */
  /* Issue #1701 — las cuatro áreas seguras, en un solo lugar.
     Se leen por variable y no con `env()` suelto en cada regla por dos razones:
     se escriben una vez, y un guion de e2e puede pisarlas para MEDIR qué queda
     tapado. `env()` en Chromium headless da 0, así que un test que lo use tal
     cual pasa por la razón equivocada — que es peor que no tenerlo. */
  :root { color-scheme: dark;
    --safe-top: env(safe-area-inset-top, 0px);
    --safe-bottom: env(safe-area-inset-bottom, 0px);
    --safe-left: env(safe-area-inset-left, 0px);
    --safe-right: env(safe-area-inset-right, 0px);
    --oro: #d4af37; --oro-rgb: 212,175,55;
    --oro-claro: #f2dc94; --oro-hondo: #6d5518;
    /* Blanco, sin sesgo. El crema (#f3efe4) se eligió para acompañar al oro,
       pero el oro ya quedó reducido a tres detalles y ese tinte se pagaba en
       TODO el texto de la app: se leía beige en vez de blanco. */
    --ink: #ffffff;
    --muted: #8d9192;          /* gris neutro para lo secundario */
    --panel: linear-gradient(180deg, #14120e 0%, #0b0a08 100%);
    /* Neutros, no dorados. El oro es para lo que se elige, lo que se gana y la
       marca; si además bordea cada caja deja de ser un detalle y pasa a ser el
       color de la pantalla. */
    --line: rgba(255,255,255,.10); --line-strong: rgba(255,255,255,.22);
    /* Verde y rojo se quedan, con DOS trabajos que no son la marca:
       - la plata: +100 contra -47 es significado, no decoracion;
       - lo que vive adentro del tapete, que sigue siendo verde y configurable. */
    --green: #49c58e; --green-rgb: 73,197,142;
    --red: #ff778b; --red-rgb: 255,119,139;
    --amber: #f1bd5e; --amber-rgb: 241,189,94; --amber-soft: rgba(241,189,94,.12);
    --felt: #12603f;
    /* Issue #1673 — los cinco colores de las notas, y NO tienen significado
       fijo: cada uno se arma el suyo. Cinco porque menos no separa las cuatro
       categorías que cualquiera arma igual, y más de seis nadie se acuerda.

       Falta el verde y falta el oro, por la misma razón y no por la paleta:
       `.pod.won` es un anillo verde y `.pod.free` una placa verde, `.pod.turn`
       es un anillo ámbar y `.entry` una pastilla ámbar. Un marco de esos colores
       diría "ganó" o "es su turno", que es peor que no decir nada.

       Valores propios y no `var(--red)` reciclada: `--red` es el rojo del signo
       de una pérdida, y atar el color de una nota a la variable semántica hace
       que retocar el rojo de la plata repinte las notas de todo el club. */
    --nota-1: #ff5fa2;  /* rosa */
    --nota-2: #ff6a3d;  /* naranja: corrido al rojo para no confundirse con el ámbar de "es su turno" */
    --nota-3: #5aa9f0;  /* azul */
    --nota-4: #b07cf0;  /* violeta */
    --nota-5: #9fb0bb;  /* gris */ }
  * { box-sizing: border-box; }
  body { margin: 0; background: #0b0a08; color: var(--ink);
         padding: calc(var(--safe-top) + 20px) calc(var(--safe-right) + 20px)
                  calc(var(--safe-bottom) + 20px) calc(var(--safe-left) + 20px);
         font: 14px/1.5 system-ui, -apple-system, "Segoe UI", sans-serif; }
  h1 { font-size: 16px; margin: 0 0 16px; }
  .bar { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-bottom: 16px; }
  input, button, select { font: inherit; padding: 7px 10px; border-radius: 8px;
         border: 1px solid var(--line); background: #050606; color: inherit; }
  button { cursor: pointer; }
  /* El boton principal en oro, con tinta oscura: el dorado es claro, y texto
     blanco encima se lee gris. */
  button.primary { background: var(--oro); border-color: var(--oro);
    color: #17130a; font-weight: 700; }
  button.primary:hover:not(:disabled) { background: var(--oro-claro); border-color: var(--oro-claro); }
  /* Rojo para lo que destruye algo: el color es parte del aviso. */
  button.danger { background: #b23b3b; border-color: #b23b3b; color: #fff; font-weight: 600; }
  button:disabled { opacity: .35; cursor: not-allowed; }
  /* .seat es la fila de una mesa de cash en el lobby. Las reglas de .card /
     .board / .pot que vivian aca las reemplaza el bloque de mesa de mas abajo:
     dejarlas rompia las cartas nuevas, porque `.card.h { background }` le gana
     por especificidad al `.card { background: blanco }` aunque venga despues. */
  .seat { display: flex; align-items: center; gap: 10px; padding: 8px 10px; border-radius: 8px;
          border: 1px solid transparent; }
  .seat .name { min-width: 120px; font-weight: 600; }
  .muted { color: var(--muted); }
  .err { color: var(--red); }

  .boot { display: grid; place-items: center; gap: 14px; }
  .boot p { margin: 0; font-size: 12px; }
  .boot-spin { width: 26px; height: 26px; border-radius: 50%;
    border: 3px solid rgba(255,255,255,.12); border-top-color: var(--oro);
    animation: girar .8s linear infinite; }
  /* --- login / registro --- */
  .auth { display: grid; place-items: center; min-height: 74vh; }
  .auth-card { width: min(380px, 100%); padding: 26px; border: 1px solid var(--line);
    border-radius: 16px; background: linear-gradient(180deg, #0d1110, #0a0c0c); }
  .auth-brand { display: flex; align-items: center; gap: 12px; margin-bottom: 22px; }
  .auth-brand strong { display: block; font-size: 17px; }
  .auth-brand small { color: var(--muted); font-size: 12px; }
  .auth-chip { width: 34px; height: 34px; border-radius: 50%;
    background: radial-gradient(circle at 30% 30%, var(--oro-claro), var(--oro-hondo));
    box-shadow: inset 0 0 0 4px rgba(255,255,255,.14), 0 3px 10px rgba(0,0,0,.4); }
  .segmented-tabs { display: grid; grid-template-columns: 1fr 1fr; gap: 3px; padding: 3px;
    border: 1px solid var(--line); border-radius: 10px; background: rgba(0,0,0,.35); margin-bottom: 18px; }
  .segmented-tabs button { border: 0; background: transparent; color: var(--muted); font-size: 13px; padding: 8px; }
  .segmented-tabs button.active { background: rgba(255,255,255,.07); color: var(--ink); font-weight: 600; }
  #authForm { display: flex; flex-direction: column; gap: 13px; }
  #authForm label { display: flex; flex-direction: column; gap: 5px; font-size: 12px; color: var(--muted); }
  /* Un `display` explícito en el autor gana sobre el `hidden` del navegador, asi
     que los campos ocultos hay que apagarlos a mano o el login muestra los de
     registro. Vale para cualquier elemento del cliente, no sólo estos labels. */
  [hidden] { display: none !important; }
  #authForm input { padding: 10px; }
  #authSubmit { margin-top: 6px; padding: 11px; }
  .auth-note { font-size: 11px; line-height: 1.5; margin: 2px 0 0; }
  /* Un boton que se lee como un link: "olvide mi contraseña" no es una accion
     del mismo peso que "Entrar" y no puede competir con el boton primario,
     pero tiene que seguir siendo un <button> — un <a href="#"> ahi navega. */
  .linkish { align-self: center; border: 0; padding: 4px; background: transparent;
    color: var(--muted); font-size: 12px; text-decoration: underline;
    text-underline-offset: 3px; cursor: pointer; }
  .linkish:hover { color: var(--ink); }
  #devForm { display: flex; flex-direction: column; gap: 13px; }
  #devForm label { display: flex; flex-direction: column; gap: 5px; font-size: 12px; color: var(--muted); }
  .dev-note { font-size: 11px; padding: 8px 10px; border-radius: 8px;
    border: 1px solid rgba(var(--amber-rgb),.3); background: var(--amber-soft); color: var(--amber); }

  /* ------------------------------------------------------------------ lobby */

  /* Torneos y cash: dos solapas grandes, con cuántos hay en cada una. Es lo
     primero que se elige al entrar, así que pesa más que un filtro. */
  .lobby-tabs { display: flex; gap: 4px; margin-bottom: 14px;
    border-bottom: 1px solid var(--line); }
  .lobby-tabs button { position: relative; border: 0; background: transparent;
    padding: 10px 4px 12px; margin-right: 20px; border-radius: 0;
    font-size: 15px; font-weight: 600; color: var(--muted); letter-spacing: -.01em; }
  .lobby-tabs button::after { content: ""; position: absolute; left: 0; right: 0; bottom: -1px;
    height: 2px; border-radius: 2px; background: transparent; }
  .lobby-tabs button.active { color: var(--ink); }
  .lobby-tabs button.active::after { background: var(--oro); }
  .lobby-tabs b { display: inline-grid; place-items: center; min-width: 20px; height: 20px;
    margin-left: 6px; padding: 0 6px; border-radius: 10px;
    font-size: 11px; font-weight: 700; font-variant-numeric: tabular-nums;
    background: rgba(255,255,255,.07); color: var(--muted); }
  .lobby-tabs button.active b { background: rgba(255,255,255,.12); color: var(--ink); }

  .lobby-bar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 14px; }

  /* --- filtros finos ---
     Flota anclado al botón: es un ajuste rápido sobre la lista que está justo
     abajo, y sacarlo a un cajón de pantalla completa lo convertiría en un
     trámite. Por eso tampoco lleva scrim. */
  .filter-wrap { position: relative; }
  #filterBtn.on { border-color: var(--line-strong); color: var(--ink); }
  #filterBtn b { font-variant-numeric: tabular-nums; }
  #filterBtn b:not(:empty) { display: inline-grid; place-items: center; min-width: 17px; height: 17px;
    margin-left: 4px; padding: 0 4px; border-radius: 9px; font-size: 10px;
    background: rgba(var(--oro-rgb),.2); color: var(--oro); }
  .filter-panel { position: absolute; z-index: 40; top: calc(100% + 8px); right: 0; width: 300px;
    display: flex; flex-direction: column; gap: 12px; padding: 14px;
    border: 1px solid var(--line-strong); border-radius: 14px;
    background: #0d1211; box-shadow: 0 18px 48px rgba(0,0,0,.6); }
  .filter-panel .fl { display: flex; flex-direction: column; gap: 6px;
    font-size: 11px; letter-spacing: .07em; text-transform: uppercase; color: var(--muted);
    font-weight: 700; }
  .filter-panel .seg { display: flex; }
  .filter-panel .seg button { padding: 7px 6px; font-size: 11px; }
  .frange { display: flex; align-items: center; gap: 6px; }
  .frange input { min-width: 0; flex: 1; text-align: right; font-variant-numeric: tabular-nums; }
  .frange em { font-style: normal; color: var(--muted); }
  .filter-panel .sw2 { align-items: flex-start; }
  .filter-panel .sw2 small { display: block; font-size: 10px; }
  .filter-foot { display: flex; justify-content: flex-end;
    padding-top: 4px; border-top: 1px solid var(--line); }
  /* En el teléfono el panel no puede colgar del botón: se sale de la pantalla. */
  @media (max-width: 560px) {
    .filter-panel { position: fixed; width: auto; top: auto;
      left: calc(var(--safe-left) + 12px); right: calc(var(--safe-right) + 12px);
      bottom: calc(var(--safe-bottom) + 12px); }
  }
  .chips { display: inline-flex; gap: 4px; padding: 3px; border-radius: 10px;
    border: 1px solid var(--line); background: rgba(0,0,0,.35); }
  .chips button { border: 0; background: transparent; color: var(--muted);
    font-size: 12px; padding: 6px 12px; border-radius: 8px; }
  /* Lo elegido se marca con CONTRASTE, no con color: en una pantalla hay hasta
     nueve selecciones a la vez —nav, solapa, estado, vista, cuatro filtros y el
     alcance— y en oro dejan de ser un detalle para ser el fondo. */
  .chips button.active { background: rgba(255,255,255,.10); color: var(--ink);
    font-weight: 600; }

  /* Issue #1339 — el banner de "qué se viene", arriba de la grilla.
     ALTO y con la ranura de la imagen ya reservada: un carrusel de un renglón
     no llama la atención de nadie, y meterle una imagen después habría sido
     rediseñarlo entero. La ranura hoy lleva un fondo propio según el tipo de
     torneo; cuando haya archivo, entra en esa misma caja sin mover nada. */
  .bannerbar { position: relative; display: flex; align-items: stretch; gap: 4px;
    margin-bottom: 16px; padding: 10px 6px; border: 1px solid var(--line);
    border-radius: 18px; overflow: hidden;
    background: radial-gradient(120% 140% at 12% 0%, rgba(var(--oro-rgb),.10), transparent 60%),
      var(--panel); }

  /* Dos elementos y no uno, y es la parte que hay que entender de un carrusel:
     la VENTANA recorta y la PISTA se mueve.
     Juntar las dos cosas en el mismo elemento no anda: `transform` mueve también
     el recorte, así que el contenido no se desplaza RESPECTO de su ventana y lo
     único que cambia es que la ventana se va para afuera de la barra. Se ve un
     resto de la diapositiva anterior y nada más — con el DOM perfecto, los
     rectángulos en su lugar y ni un error en la consola.
     Y el recorte tiene que existir: sin él las diapositivas de al lado se pintan
     encima de las flechas y se comen el clic. */
  .bn-ventana { flex: 1; min-width: 0; overflow: hidden; }
  .bn-pista { display: flex; transition: transform .35s ease; }

  .bn-slide { flex: 0 0 100%; min-width: 0; display: flex; gap: 16px;
    align-items: center; padding: 4px 10px 22px; cursor: pointer; }
  .bn-slide:hover .bn-arte { transform: scale(1.03); }

  /* La ranura de la imagen. 16:9 fijo: es la forma de una imagen rectangular, y
     fijarla ahora evita que el banner cambie de alto el día que entre una. */
  .bn-arte { position: relative; flex: none; width: 208px; aspect-ratio: 16 / 9;
    border-radius: 12px; overflow: hidden; transition: transform .25s ease;
    display: flex; align-items: center; justify-content: center; text-align: center; }
  .bn-arte span { font-size: 46px; line-height: 1; opacity: .30; }
  /* La lámina tapa el fondo de color cuando existe; cuando no, el <img> se saca
     solo y queda el degradé con el palo. `cover` porque la ranura es 16:9 igual
     que las láminas: no debería recortar nada, y si alguna viene con otra
     proporción, recorta en vez de deformar. */
  .bn-lamina { position: absolute; inset: 0; width: 100%; height: 100%;
    object-fit: cover; }
  /* El color dice el tipo antes de que leas nada: con cuatro diapositivas
     seguidas, es lo primero que distingue una de otra. */
  .bn-mtt { background: linear-gradient(140deg, rgba(var(--oro-rgb),.30), rgba(var(--oro-rgb),.05));
    color: var(--oro); }
  /* El KO/PKO con el MISMO degradé que su etiqueta en las tarjetas (ámbar a
     rojo): con ámbar solo quedaba a un pelo del oro del MTT y las dos
     diapositivas se veían iguales, que es justo lo contrario de para qué está
     el color acá. */
  .bn-ko { background: linear-gradient(140deg, rgba(var(--amber-rgb),.34), rgba(var(--red-rgb),.30));
    color: #f5c877; }
  /* El TatoClub con su propio tono: es lo único del carrusel que no es un
     torneo, y el color lo dice antes que el rótulo.
     Y la ranura no lleva un emoji decorativo sino AL PUNTERO: es el dato con
     cara y nombre, que es lo que hace que un ranking se mire. */
  .bn-club { background: linear-gradient(140deg, rgba(var(--oro-rgb),.42), rgba(var(--amber-rgb),.12));
    color: var(--oro-claro); flex-direction: column; gap: 1px; padding: 6px; }
  /* El fondo del ranking va atenuado y con un velo encima: la lámina está para
     dar clima, no para competirle al nombre del puntero, que es el dato. */
  .bn-club .bn-lamina { opacity: .38; }
  .bn-club::after { content: ""; position: absolute; inset: 0;
    background: radial-gradient(ellipse at center, rgba(0,0,0,.52), rgba(0,0,0,.72)); }
  /* Sobre una lámina posicionada, el texto en flujo queda DEBAJO. Posicionarlo
     lo devuelve arriba sin tocar el orden del marcado.
     Va SÓLO adentro del club: en una diapositiva de torneo el palo es el relleno
     de cuando no hay lámina, y subirlo lo dibujaba encima de la que sí cargó. */
  .bn-club span, .bn-lider-puesto, .bn-lider-nombre, .bn-lider-puntos {
    position: relative; z-index: 1; }
  .bn-lider-puesto { font-size: 12px; letter-spacing: .06em; opacity: .9; }
  .bn-lider-nombre { max-width: 100%; font-size: 17px; font-weight: 800; color: var(--ink);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .bn-lider-puntos { font-size: 11px; font-weight: 600; opacity: .85;
    font-variant-numeric: tabular-nums; }

  /* El pase no es una cifra: es un estado, y se lee como una etiqueta. */
  .bn-pase { max-width: 190px; padding: 7px 11px; border-radius: 999px;
    font-size: 12px; font-weight: 700; line-height: 1.25; text-align: center;
    color: var(--oro-claro); border: 1px solid rgba(var(--oro-rgb),.45);
    background: rgba(var(--oro-rgb),.14); }

  /* El freeroll en plateado y NO en verde: el verde de esta app tiene dos
     trabajos —la plata y el tapete— y usarlo de decoración lo gasta (#1106).
     Además le queda bien: "no cuesta nada" no es una ganancia. */
  .bn-free { background: linear-gradient(140deg, rgba(255,255,255,.17), rgba(255,255,255,.04));
    color: var(--ink); }

  .bn-datos { min-width: 0; flex: 1; display: flex; flex-direction: column; gap: 3px; }
  .bn-rotulo { font-size: 10px; letter-spacing: .10em; text-transform: uppercase;
    color: var(--muted); }
  .bn-slide h4 { margin: 0; font-size: 21px; font-weight: 800; letter-spacing: -.01em;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .bn-reloj-linea { margin: 0; display: flex; align-items: baseline; gap: 7px; }
  .bn-reloj { font-size: 25px; font-weight: 700; color: var(--oro);
    font-variant-numeric: tabular-nums; letter-spacing: -.02em; }
  .bn-reloj.bn-chico { font-size: 14px; color: var(--ink); }
  .bn-reloj-linea i { font-style: normal; font-size: 12px; color: var(--muted); }
  /* Los números contra el borde DERECHO y no pegados al nombre: el banner es
     ancho, y todo amontonado a la izquierda deja media franja vacía y se lee
     como un error de maquetación. Cada uno con su etiqueta arriba en chico, que
     es la misma forma que ya usan las tarjetas. */
  .bn-cifras { margin: 0 8px 0 24px; flex: none; align-self: center;
    display: flex; gap: 26px; font-size: 15px;
    font-weight: 700; font-variant-numeric: tabular-nums; text-align: right; }
  .bn-cifras i { display: block; font-style: normal; font-size: 9px; font-weight: 500;
    letter-spacing: .08em; text-transform: uppercase; color: var(--muted); }

  /* Las flechas centradas contra los bordes, como en cualquier carrusel. */
  .bn-flecha { flex: none; align-self: center; padding: 10px 6px; font-size: 13px;
    color: var(--muted); background: none; border: 0; border-radius: 8px; }
  .bn-flecha:hover { color: var(--ink); background: rgba(255,255,255,.06); }

  /* Los puntos abajo y centrados, encima del borde inferior: en la fila se
     comían ancho al nombre, que es lo que no se puede recortar. */
  .bn-puntos { position: absolute; left: 0; right: 0; bottom: 9px;
    display: flex; justify-content: center; gap: 6px; }
  .bn-punto { width: 6px; height: 6px; padding: 0; border: 0; border-radius: 50%;
    background: var(--line-strong); transition: width .2s ease, background .2s ease; }
  .bn-punto.aca { width: 18px; border-radius: 3px; background: var(--oro); }

  /* Angosto no es "pantalla chica": el panel de detalle se lleva media ventana,
     y en la app el bloque de torneos mide ~700px con el monitor entero. Por eso
     la medida sale del CONTENEDOR (`lista`, que ya existe para la vista en
     lista) y no de la ventana. Ahí se achica la ranura y se cae la cifra menos
     importante: el nombre del torneo es lo único que no se puede recortar. */
  @container lista (max-width: 900px) {
    .bn-arte { width: 150px; }
    .bn-arte span { font-size: 34px; }
    .bn-slide h4 { font-size: 17px; }
    .bn-reloj { font-size: 21px; }
    /* Queda sólo la cifra CLAVE de cada panel —el garantizado en un torneo, el
       puntero en el del club— y se marca con una clase en vez de deducirla por
       posición: el panel del TatoClub tiene dos columnas y no tres, así que
       "esconder la primera y la última" lo dejaba sin ninguna. */
    .bn-cifras { margin-left: 16px; gap: 16px; font-size: 13px; }
    .bn-cifras span:not(.bn-clave) { display: none; }
  }
  /* Teléfono: sin ranura y sin cifras. El nombre y el reloj solos, con todo el
     ancho — que es lo único que no se puede recortar, y con 369px de pantalla
     cualquier cosa al lado se lo come. */
  @container lista (max-width: 700px) {
    .bn-arte, .bn-cifras { display: none; }
  }

  /* Quien pidió menos animación no ve el deslizamiento tampoco: el módulo ya no
     rota solo, y esto apaga la transición de las flechas. */
  @media (prefers-reduced-motion: reduce) {
    .bn-pista, .bn-arte, .bn-punto { transition: none; }
  }

  .cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 14px; }
  .tcard { position: relative; overflow: hidden; padding: 16px 18px;
    display: flex; flex-direction: column;
    border: 1px solid var(--line); border-radius: 16px;
    background: var(--panel); transition: border-color .15s, transform .15s;
    /* Issue #1401 — la tarjeta arma su propio contexto de apilado para que el
       fondo pueda ir en z-index negativo. Sin esto, un hijo en -1 se va detrás
       del `background` de la tarjeta y no se ve nunca; con esto queda entre el
       fondo y el contenido, que es el único lugar donde sirve. */
    isolation: isolate; }
  /* La ilustración del torneo, de fondo. Va MUY tenue a propósito: la tarjeta
     existe para decir cuánto sale, cuánto paga y cuándo arranca, y eso se
     decide con plata real. La imagen es clima, no información — de ahí el
     z-index negativo (no necesita que ningún hijo se posicione) y el
     `pointer-events: none` (la tarjeta entera es cliqueable). */
  .tcard-fondo { position: absolute; inset: 0; width: 100%; height: 100%;
    /* `contain` y no `cover`: la tarjeta es más ANCHA que el recorte (434×363
       contra 306×360), así que `cover` agrandaba el dibujo y lo cortaba arriba y
       abajo — el cartel de una lámina quedaba gigante y casi legible, que es lo
       contrario de un fondo. Así entra entero, en su proporción, apoyado contra
       el borde derecho. */
    object-fit: contain; object-position: right center;
    /* Issue #1647 — REPORTADO: "se ve más orgánico, más con imágenes, no tan
       chato". El arte estaba al 5,5%, o sea invisible: se leía como una mancha
       y no como un dibujo, y la tarjeta quedaba plana.
       Sube, pero NO sólo el número: al 5% no importaba dónde caía el dibujo, y
       al 18% sí. Se apoya abajo a la derecha —el cuadrante que el texto no usa,
       porque el nombre va arriba y las cifras a la izquierda— y se escala al
       72%, que es lo que evita que el cartel de una lámina se vuelva legible y
       compita con el nombre del torneo. */
    object-position: right 82%;
    transform: scale(.72); transform-origin: right bottom;
    opacity: .18; z-index: -1; pointer-events: none;
    /* Un toque de desenfoque: es lo que deja pasar el color y la forma sin que
       el cartel de una lámina se vuelva legible. Sin esto, a esta opacidad el
       "SE BUSCA" de la lámina de KO competía con el nombre del torneo — que es
       la razón por la que el arte estaba al 5% en primer lugar. */
    filter: blur(1.5px);
    transition: opacity .18s;
    /* Se apaga hacia arriba y hacia la izquierda, que es donde vive el texto:
       el nombre del torneo, el contador y las cifras se leen sobre panel liso. */
    -webkit-mask-image: linear-gradient(115deg, transparent 22%, #000 78%);
    mask-image: linear-gradient(115deg, transparent 22%, #000 78%); }
  .tcard:hover .tcard-fondo { opacity: .3; }
  @media (prefers-reduced-motion: reduce) {
    .tcard:hover .tcard-fondo { opacity: .18; }
    .tcard-fondo { transition: none; }
  }
  /* El pie se pega abajo para que las tarjetas se vean parejas aunque el
     contenido del medio varie (un torneo terminado muestra podio, uno anunciado
     no muestra barra de llenado). */
  .tcard-foot { margin-top: auto; }
  .tcard:hover { border-color: var(--line-strong); transform: translateY(-1px); }
  /* Filo de color segun estado: reemplaza la banda vertical "MTT" de ClubGG,
     que ocupaba lugar sin decir nada que el resto de la tarjeta no dijera. */
  .tcard::before { content: ""; position: absolute; inset: 0 auto 0 0; width: 3px; background: var(--line-strong); }
  .tcard.is-open::before { background: rgba(255,255,255,.35); }
  .tcard.is-live::before { background: var(--amber); }
  .tcard.is-done::before { background: rgba(255,255,255,.16); }

  .tcard-head { display: flex; align-items: flex-start; gap: 10px; margin-bottom: 12px; }
  .tcard-title { font-size: 15px; font-weight: 650; line-height: 1.3; }
  /* Issue #1535 — cuántos lugares quedan. Sólo aparece cuando queda poco, así
     que cuando está tiene que leerse: ámbar como el resto de los avisos del
     club, y rojo cuando ya no entrás. Si estuviera siempre, este color sería
     ruido; justamente por eso habla poco. */
  .tcupo { font-size: 11px; font-weight: 650; color: var(--amber); margin-top: 2px; }
  .tcupo.lleno { color: var(--red); }
  /* Issue #1537 — la descripción que escribió el que creó el torneo. Dos
     líneas como mucho: es la única parte de la tarjeta con texto libre, y sin
     tope una descripción larga empuja los datos con los que se compara un
     torneo contra otro. */
  .tdesc { font-size: 11px; color: var(--muted); margin-top: 3px;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
    overflow: hidden; }
  .tcard-tags { display: flex; gap: 5px; margin-top: 6px; flex-wrap: wrap; }
  .tcard-title { min-height: 2.6em; }
  .tag { font-size: 10px; letter-spacing: .05em; text-transform: uppercase; font-weight: 700;
    padding: 3px 7px; border-radius: 5px; background: rgba(255,255,255,.07); color: var(--muted); }
  .tag.ko { background: rgba(var(--amber-rgb),.16); color: var(--amber); }
  .tag.pko { background: linear-gradient(90deg, rgba(var(--amber-rgb),.22), rgba(var(--red-rgb),.22)); color: #f5c877; }
  /* --- suma para el ranking ---
     El ORO es el mismo de la solapa TatoClub, y eso es la mitad del mensaje:
     este chip promete que lo que se juegue acá termina en ESA tabla. Un dorado
     distinto sería otra cosa distinta.
     Es el único chip del lobby con fondo lleno: los demás son translúcidos
     sobre el fondo de la tarjeta. Esa diferencia de material es lo que lo hace
     saltar sin necesidad de agrandarlo ni de ponerle un ícono. */
  .tag.ranking { color: #1c1509; font-weight: 800; letter-spacing: .08em;
    background: linear-gradient(135deg, #f2dc94 0%, #d4af37 55%, #b08d24 100%);
    box-shadow: 0 0 0 1px rgba(212,175,55,.4), 0 1px 7px rgba(212,175,55,.28); }
  /* En la tarjeta, un filo de oro arriba. El borde izquierdo ya es del estado
     (`.tcard::before`) y el color del borde es de "estás anotado", así que el
     canto de arriba es lo único libre — y alcanza: se ve de reojo al barrer la
     grilla, que es como se mira una lista de torneos. */
  .tcard.puntua::after { content: ""; position: absolute; left: 0; right: 0; top: 0; height: 2px;
    background: linear-gradient(90deg, transparent, #d4af37 22%, #f2dc94 50%, #d4af37 78%, transparent);
    pointer-events: none; }
  /* En la fila el borde izquierdo lo usa KO/PKO, así que va un lavado de oro
     que se apaga enseguida: la fila es densa y cualquier cosa más fuerte
     compite con los números, que son lo que se lee. */
  .rowcard.puntua { background-image: linear-gradient(90deg, rgba(212,175,55,.09), transparent 38%); }

  .pill { margin-left: auto; flex: none; display: inline-flex; align-items: center; gap: 6px;
    font-size: 11px; font-weight: 600; padding: 5px 10px; border-radius: 999px;
    background: rgba(255,255,255,.06); color: var(--muted); }
  .pill i { width: 6px; height: 6px; border-radius: 50%; background: currentColor; font-style: normal; }
  .pill.open { background: rgba(255,255,255,.09); color: var(--ink); }
  .pill.open i { animation: latir 1.8s ease-in-out infinite; }
  /* Registro tardío en naranja: verde ya es "inscripción abierta", y dos estados
     del mismo color son un solo estado para el que barre la lista. */
  .pill.tarde { background: rgba(232,145,54,.16); color: #e89136; }
  .pill.tarde i { animation: latir 1.8s ease-in-out infinite; }
  .pill.live { background: rgba(var(--amber-rgb),.16); color: var(--amber); }
  @keyframes latir { 0%,100% { opacity: 1 } 50% { opacity: .25 } }

  /* El countdown es lo unico que ClubGG hace bien: es lo que hace que un lobby
     se sienta vivo. Aca es el elemento principal de la tarjeta. */
  .count { display: flex; align-items: baseline; gap: 8px; margin: 2px 0 14px; }
  .count b { font-size: 24px; font-weight: 700; font-variant-numeric: tabular-nums;
    letter-spacing: -.02em; }
  .count small { font-size: 11px; color: var(--muted); }
  .count.urgent b { color: var(--amber); }

  .tgrid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; margin-bottom: 14px; }
  .tgrid div span { display: block; font-size: 10px; letter-spacing: .07em;
    text-transform: uppercase; color: var(--muted); margin-bottom: 2px; }
  .tgrid div strong { font-size: 14px; font-variant-numeric: tabular-nums; }
  .tgrid .prize strong { color: var(--amber); }

  .fill { height: 4px; border-radius: 999px; background: rgba(255,255,255,.07); overflow: hidden; margin-bottom: 4px; }
  .fill span { display: block; height: 100%; border-radius: inherit; background: rgba(255,255,255,.45); transition: width .3s; }
  /* #1647 — el rótulo en su propio renglón. Compitiendo con los botones por el
     mismo, cada tarjeta envolvía distinto según el largo del texto: una partía
     después de "Lobby", otra dejaba la ✕ sola abajo, y el conjunto se leía como
     desparejo aunque las tarjetas midieran igual. */
  .tcard-foot { display: flex; align-items: center; gap: 8px; padding-top: 14px;
    flex-wrap: wrap; }
  .tcard-foot > .muted { flex: 1 0 100%; font-size: 11px; }
  .tcard-foot .muted { font-size: 11px; }
  .tcard-foot button { margin-left: auto; }
  .tcard.mine { border-color: var(--line-strong); }
  .podio { font-size: 11px; color: var(--muted); line-height: 1.6; }
  .podio b { color: var(--ink); }
  .empty { grid-column: 1/-1; padding: 40px; text-align: center; color: var(--muted);
    border: 1px dashed var(--line); border-radius: 16px; }

  /* ------------------------------------------------------------------- mesa
     Una mesa de poker de verdad: tapete ovalado, jugadores sentados alrededor,
     apuestas empujadas hacia el centro. Igual que PokerStars/GG.

     Todo se dimensiona en cqmin sobre .stage, asi que la mesa ENTERA escala
     sola: un solo layout que anda igual en 390px que en un monitor, en vez de
     dos diseños distintos para mantener. Lo unico que cambia por breakpoint es
     la proporcion del ovalo (vertical en el telefono, horizontal en la compu),
     que es exactamente lo que hacen los clientes reales. */
  /* Alto EXACTO, no minimo: es lo que le deja al tapete un espacio con tope y
     por lo tanto lo que hace que pueda achicarse para entrar. Con min-height la
     mesa crecia hacia abajo y se iba de la ventana. */
  #tableView { position: relative; display: flex; flex-direction: column; gap: 10px;
    height: 100dvh; overflow: hidden;
    /* Los cuatro lados, no sólo abajo: de este padding-top cuelga `.mesabar`,
       y la barra de acción es un `absolute` de acá adentro, así que el padding
       derecho es lo que la salva del notch en apaisado. */
    padding: calc(var(--safe-top) + 10px) calc(var(--safe-right) + 10px)
             calc(var(--safe-bottom) + 10px) calc(var(--safe-left) + 10px); }
  /* La tira de mesas. Arriba de todo y siempre visible: si hay que buscarla, no
     sirve — el punto es enterarse SIN mirar. */
  .mesabar { display: flex; gap: 6px; flex-wrap: wrap; flex: none; }
  .mesabtn { display: flex; align-items: baseline; gap: 8px;
    padding: 7px 12px; border-radius: 10px; font-size: 12px;
    color: var(--muted); background: rgba(255,255,255,.03); }
  .mesabtn .mb-nombre { max-width: 22ch; overflow: hidden; text-overflow: ellipsis;
    white-space: nowrap; }
  .mesabtn b { font-variant-numeric: tabular-nums; }

  /* --- Issue #1428: qué tenés en cada mesa, sin entrar ---------------------
     PEDIDO con capturas de GGPoker: las cartitas en el header de cada mesa, y
     el stack cuando foldeaste y ya no hay cartas que mostrar.

     En el teléfono esta tira vive ARRIBA del tapete y le come alto a la mesa, así
     que las cartas van en px y no en cqmin: tienen que entrar en la línea que la
     tira ya ocupaba. Con 11px de fuente y 1px de padding la carta mide ~15px de
     alto, que es menos que los ~16px de la línea del nombre — la tira no crece.
     (El e2e lo mide: `dos-mesas-movil.js` compara el alto contra el de antes.)

     Los colores de palo son los de `.card` en el fieltro, misma clase, así que el
     mazo de cuatro colores (#1255) vale acá también. El fondo NO sigue al mazo
     oscuro (#1334): sobre el botón, una carta oscura se pierde contra el propio
     botón, y a 15px la cara clara es la única que se lee de reojo. */
  .mb-cartas { display: inline-flex; gap: 2px; flex: none; }
  .mb-carta { font-style: normal; font-weight: 800; font-size: 11px;
    line-height: 1.15; padding: 1px 3px; border-radius: 3px; letter-spacing: -.02em;
    background: linear-gradient(180deg, #fff, #e6eaee); color: #16181d; }
  .mb-carta.h, .mb-carta.d { color: #cf2337; }
  body.mazo-cuatro .mb-carta.d { color: #1668c4; }
  body.mazo-cuatro .mb-carta.c { color: #11804a; }
  /* Foldeado no hay cartas: lo único que queda por decir de esa mesa es cómo
     venís de fichas. Apagado a propósito — es un dato, no un reclamo. */
  .mesabtn .mb-stack { font-variant-numeric: tabular-nums; opacity: .75;
    flex: none; }
  /* La que estás mirando: marcada pero apagada, no compite con la que reclama. */
  .mesabtn.aca { color: var(--ink); border-color: var(--line-strong);
    background: rgba(255,255,255,.06); }
  /* La que te espera: ámbar y latiendo. Es lo único de la tira que tiene que
     ganarte el ojo estando en otra mesa. */
  .mesabtn.turno { color: var(--amber); border-color: rgba(var(--amber-rgb),.55);
    background: rgba(var(--amber-rgb),.12); animation: latir 1.1s ease-in-out infinite; }
  .mesabtn.turno b { color: var(--amber); font-weight: 700; }
  /* El botón entero ya late (`latir`, arriba). Las cartas no suman una segunda
     animación: se ANILLAN en ámbar, que es lo que las despega del resto de la
     tira sin que haya dos cosas moviéndose a distinto ritmo en la misma línea.
     Y el anillo se ve igual con `prefers-reduced-motion`, donde el latido no. */
  .mesabtn.turno .mb-carta { box-shadow: 0 0 0 1.5px var(--amber); }
  .mesabtn.turno .mb-stack { color: var(--amber); opacity: 1; }

  /* Issue #1261 — "no te alcanza para el refiche", dicho a tiempo.
     Ámbar y no rojo: no se rompió nada y el jugador no hizo nada mal. Es algo
     que todavía puede arreglar cargando, y un error rojo sobre algo que ni
     intentó se lee como una falla del sistema. */
  .saldo-corto { margin: 0 0 6px; font-size: 12px; color: var(--amber);
    border: 1px solid rgba(var(--amber-rgb),.4); border-radius: 8px;
    padding: 5px 9px; background: rgba(var(--amber-rgb),.10); }

  /* Issue #1628 - la cabecera no es una caja: son los controles y los datos
     flotando sobre el negro.
     REPORTADO con una captura: "un rectangulo gris arriba feo". Era una
     tarjeta con borde y fondo sobre el fondo de la pagina, y se leia como el
     header de una pagina web pegado encima de la mesa en vez de como parte de
     ella.
     No es un tratamiento nuevo: en telefono la cabecera ya era una linea sin
     caja desde #1298, y hay un e2e que lo exige (dos-mesas-movil). Lo que
     cambia es que ahora tambien lo es en escritorio. Sigue siendo `.thead`
     dentro de `#tableView` porque el preload de Electron inyecta ahi su boton
     de ordenar ventanas. */
  .thead { display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
    padding: 2px 2px; border: 0; background: none; position: relative; }
  .thead-info { min-width: 0; }
  /* El espacio entre la flecha y la palabra: son dos nodos porque en vertical la
     palabra se esconde, y el espacio en blanco del html entre dos spans pegados
     no existe. */
  #backTxt { margin-left: .35em; }

  /* Issue #1298 — la cabecera del torneo, chica arriba y completa al asomarse.
     Medida antes del cambio: 172px en un telefono de 393 de ancho, el 20% de la
     pantalla, con los bloques partidos en DOS filas. El comentario del bloque
     de telefono decia "en una sola linea: partida en dos se comia el alto que
     necesita el tapete" — y estaba partida en dos, porque no habia ninguna
     regla que lo impidiera.
     Arriba queda lo que hace falta para jugar LA MANO en curso: las ciegas,
     cuanto falta para que suban y en que puesto vas. Lo demas se consulta, y
     consultar es lo que hace el panel. */
  .tstats { margin-left: auto; min-width: 0; }
  .tcorta { display: flex; align-items: baseline; gap: 8px; flex-wrap: nowrap;
    overflow: hidden; font-variant-numeric: tabular-nums; }
  .tcorta b { font-size: 14px; font-weight: 700; white-space: nowrap; }
  .tcorta span { font-size: 11px; color: var(--muted); white-space: nowrap; }
  /* Separador dibujado y no escrito: un punto por nodo obliga a meterlo en el
     html de cada dato y despues sacarlo del primero. */
  .tcorta span::before { content: "·"; margin-right: 8px; opacity: .45; }
  .tcorta .urgente { color: var(--amber); }

  /* El panel CAE ENCIMA del tapete en vez de empujarlo: expandirse dentro del
     flujo le comeria al tapete el alto que este cambio le devuelve, que es
     justamente el problema que se esta arreglando.
     Semitransparente y con desenfoque por la misma razon que el panel #tinfo:
     tapa parte de la mesa, y mientras se lee la mano sigue. */
  .tpanel { position: absolute; top: calc(100% + 6px); right: 0; z-index: 40;
    display: grid; grid-template-columns: repeat(auto-fit, minmax(118px, 1fr));
    gap: 10px 18px; padding: 12px 14px; max-width: min(520px, calc(100vw - 24px));
    border: 1px solid var(--line); border-radius: 12px;
    background: rgba(12,16,20,.82); backdrop-filter: blur(14px) saturate(1.2);
    -webkit-backdrop-filter: blur(14px) saturate(1.2);
    box-shadow: 0 18px 40px rgba(0,0,0,.45);
    opacity: 0; visibility: hidden; pointer-events: none;
    transform: translateY(-6px);
    transition: opacity .14s ease, transform .14s ease, visibility .14s; }
  /* Con un retardo al abrir: el mouse que cruza la cabecera camino a otra cosa
     no tiene por que destapar el panel. Al cerrar no hay retardo — irse es una
     decision y se respeta enseguida. */
  .thead:hover .tpanel { transition-delay: .25s, .25s, .25s; }
  .thead:hover .tpanel, .thead.abierta .tpanel {
    opacity: 1; visibility: visible; pointer-events: auto; transform: none; }
  .thead.abierta .tpanel { transition-delay: 0s; }
  @media (prefers-reduced-motion: reduce) {
    .tpanel { transition: none; }
  }

  /* Bloques de dato. Cada uno con su rotulo chico arriba: sin el rotulo, cuatro
     numeros sueltos no se sabe que son. */
  .tstat { min-width: 0; }
  .tstat span { display: block; font-size: 9px; letter-spacing: .09em;
    text-transform: uppercase; color: var(--muted); white-space: nowrap; }
  .tstat strong { font-size: 14px; font-variant-numeric: tabular-nums; white-space: nowrap; }
  .tstat em { font-style: normal; display: block; font-size: 11px; color: var(--muted);
    font-variant-numeric: tabular-nums; white-space: nowrap; }
  .tstat.urgente strong, .tstat.urgente em { color: var(--amber); }
  .thead-info strong { display: block; font-size: 13px; }
  .thead-info span { font-size: 11px; }
  .thead-seat { margin-left: auto; display: flex; gap: 6px; align-items: center; }
  .thead-seat select, .thead-seat input { padding: 6px 8px; font-size: 12px; }
  .thead-seat input { width: 76px; }
  .buyin-field { display: flex; align-items: center; gap: 6px; font-size: 11px;
    color: var(--muted); }
  .buyin-field input { width: 82px; padding: 6px 8px; font-size: 12px; }
  button.ghost { border-color: transparent; background: transparent; color: var(--muted); }
  /* Issue #1628 - sin la caja que los contenia, los controles de la mesa
     necesitan leerse como controles: "Lobby" y "Ordenar" quedaban como dos
     palabras sueltas sobre el negro. Pastilla con el mismo material que
     `.potline` del tapete -- negro translucido con un filo de un pelo. */
  .thead button { padding: 6px 13px; border-radius: 999px; font-size: 12px;
    font-weight: 600; color: var(--muted); border: 0; background: rgba(0,0,0,.45);
    box-shadow: inset 0 0 0 1px rgba(255,255,255,.09);
    transition: color .15s ease, box-shadow .15s ease, transform .15s ease; }
  .thead button:hover { color: var(--ink);
    box-shadow: inset 0 0 0 1px var(--line-strong); transform: translateY(-1px); }
  @media (prefers-reduced-motion: reduce) { .thead button:hover { transform: none; } }

  /* --- el tapete ------------------------------------------------------- */
  /* La mesa mantiene SIEMPRE su proporcion y se escala entera para entrar en lo
     que sobra. Nada de adentro cambia de tamaño relativo: todo se mide en cqmin
     contra este mismo contenedor, asi que agrandar la ventana agranda la mesa y
     todo lo que tiene encima, en bloque.
     `min-height: 0` es lo que permite que un hijo de un flex se achique por
     debajo de su contenido; sin eso el tapete se desborda hacia abajo en vez de
     encogerse. */
  /* El hueco: toma todo el espacio libre y lo MIDE. `min-height: 0` es lo que
     permite que un hijo de un flex se achique por debajo de su contenido; sin
     eso el tapete se desborda hacia abajo en vez de encogerse. */
  /* Issue #1805 — el marco de alrededor de la mesa deja de ser negro liso.
     PEDIDO: "que el fondo no sea negro liso, que sea alguna imagen pero de
     alguna manera que con el estiramiento de las ventanas no se deforme ni se
     vea mal".
     Va GENERADO y sin archivo, y eso no es por ahorrar: es la respuesta al
     requisito. Un bitmap metido en esta caja se estira o se recorta, porque
     `.stage-wrap` toma la proporción que le deje la ventana —de 1900x620 a
     900x1300— mientras que `.stage` tiene `aspect-ratio` fijo y nunca se
     deformó. Un degradado no tiene proporción propia que romper.
     Los colores salen de `--felt-*`, que es el fieltro que cada uno eligió en
     ajustes: un fondo con color propio pisaría esa elección. */
  .stage-wrap { flex: 1 1 auto; min-height: 0; container-type: size;
    display: flex; align-items: center; justify-content: center;
    background:
      radial-gradient(120% 90% at 50% 42%,
        color-mix(in srgb, var(--felt-3) 55%, transparent) 0%,
        color-mix(in srgb, var(--felt-3) 22%, transparent) 42%,
        transparent 72%),
      radial-gradient(140% 120% at 50% 120%, rgba(0,0,0,.55) 0%, transparent 60%); }
  /* El Campeones: lo mismo, en oro. Es lo único que lo distingue de un martes a
     primera vista, y va por `--oro-rgb`, que ya existe — el verde está
     reservado para la plata y el fieltro (#1106). */
  body.es-campeones .stage-wrap {
    background:
      radial-gradient(118% 88% at 50% 40%,
        rgba(var(--oro-rgb),.17) 0%, rgba(var(--oro-rgb),.07) 38%, transparent 68%),
      radial-gradient(150% 130% at 50% 118%, rgba(0,0,0,.6) 0%, transparent 62%),
      radial-gradient(90% 70% at 50% 46%,
        color-mix(in srgb, var(--felt-3) 40%, transparent) 0%, transparent 70%); }
  /* Issue #1723 — el HUD tapaba el stack de abajo a la derecha y volvía (#1629 sólo
     mitigó): va en `vmin` y la mesa en `cqmin`, nada las ataba. Baja el `cqh` del
     hueco, así muerde sólo donde chocaba; 56 sale de medir. */
  @media (min-width: 720px) {
    .stage-wrap { align-items: flex-start; padding-bottom: max(56px, 4vmin); }
  }

  /* La mesa conserva SIEMPRE su proporción y se escala entera para entrar: lo
     más ancha que se pueda sin pasarse de alto. `min()` contra las dos medidas
     del hueco es lo único que lo resuelve en CSS — con `max-width: 100%` a
     secas el navegador respeta el ancho y ROMPE la proporción, que es lo que
     pasaba a 1000x900 (quedaba en 1.22 en vez de 1.55).
     Nada de adentro cambia de tamaño relativo: todo se mide en cqmin contra
     este mismo contenedor, así que agrandar la ventana agranda la mesa y todo
     lo que tiene encima, en bloque. */
  .stage { position: relative; margin: 0 auto;
    width: min(100cqw, calc(100cqh * 0.8));
    aspect-ratio: .8; container-type: size; }
  @media (min-width: 720px) {
    .stage { aspect-ratio: 1.55; width: min(100cqw, calc(100cqh * 1.55)); }
  }
  /* Angosta pero APAISADA: la mesa se comporta como en escritorio — proporción
     1.55 escalada para entrar. La regla base (0.8, vertical) dejaba una mesa
     flaca parada en el medio de una ventana ancha. */
  @media (max-width: 719px) and (orientation: landscape) {
    .stage { aspect-ratio: 1.55; width: min(100cqw, calc(100cqh * 1.55)); }
  }

  /* Canto de la mesa. El ovalo es un elipse puro (border-radius: 50%) y no un
     "stadium": los asientos se colocan sobre una elipse calculada, asi que la
     unica forma de que caigan EXACTO sobre el borde es que el borde tambien
     sea una elipse.

     Los radios del canto van en %, no en cqmin, porque tienen que coincidir
     exactamente con la elipse sobre la que render() coloca los asientos: canto
     13.5%/12.5% de inset = radios 36.5%/37.5%, o sea los mismos rx/ry de los
     pods. Asi las placas quedan montadas sobre el borde en vez de flotando. */
  /* El canto es el apoyabrazos: acolchado, no una franja plana. Lo que lo hace
     leer como mesa de verdad son tres luces, no el color — brillo arriba donde
     pega la luz, sombra abajo donde se hunde, y la mesa apoyada sobre algo. */
  .rail { position: absolute; inset: 13.5% 12.5%; border-radius: 50%;
    background: linear-gradient(170deg, #4a3626 0%, #2b1f18 46%, #1a120e 72%, #35271c 100%);
    box-shadow:
      0 2.2cqmin 6cqmin rgba(0,0,0,.62),
      inset 0 .7cqmin 1.4cqmin rgba(255,255,255,.16),
      inset 0 -.8cqmin 1.6cqmin rgba(0,0,0,.55); }
  /* El color del tapete es una preferencia de quien mira: no cambia nada del
     juego, así que vive entero en tres variables. */
  body { --felt-1: #19714c; --felt-2: #115239; --felt-3: #0a3626;
    /* Lo que se le reserva a la barra de acción en el teléfono, donde va pegada
       abajo. Constante a propósito: si dependiera del contenido, el tapete
       cambiaría de tamaño en cada turno. */
    --acts-movil: 150px; }
  body.tapete-rojo { --felt-1: #8e2733; --felt-2: #6a1b25; --felt-3: #430f16; }
  body.tapete-azul { --felt-1: #1d5688; --felt-2: #16405f; --felt-3: #0c2740; }
  body.tapete-morado { --felt-1: #553a86; --felt-2: #3d2a63; --felt-3: #251841; }
  body.tapete-negro { --felt-1: #23282b; --felt-2: #171a1c; --felt-3: #0c0e0f; }

  /* Más adentro que antes: con el canto de 3.2cqmin la madera era una línea y la
     mesa se leía como un óvalo verde pelado. Es lo único que da el ancho del
     apoyabrazos — el `inset` del canto NO se puede tocar, porque los asientos se
     colocan sobre esa misma elipse. */
  .felt { position: absolute; inset: 4.8cqmin; border-radius: 50%;
    background:
      radial-gradient(ellipse at 50% 34%, rgba(255,255,255,.11), transparent 60%),
      radial-gradient(ellipse at 50% 52%, var(--felt-1) 0%, var(--felt-2) 60%, var(--felt-3) 100%);
    /* Los dos anillos de afuera son el filo donde muere el paño: sin eso el
       verde y la madera se tocan a pleno y parece pegado, no encastrado. */
    box-shadow: inset 0 0 5cqmin rgba(0,0,0,.55), inset 0 .6cqmin .6cqmin rgba(0,0,0,.4),
      0 0 0 .3cqmin rgba(0,0,0,.5), 0 0 0 .5cqmin rgba(255,255,255,.07); }
  .felt::after { content: "TATOPOKER"; position: absolute; left: 50%; top: 63%;
    transform: translate(-50%,-50%); pointer-events: none;
    font-size: 5.4cqmin; font-weight: 800; letter-spacing: .34em;
    color: rgba(255,255,255,.055); }

  /* --- board y bote, en el centro del tapete --- */
  /* El centro es una franja ancha sobre el tapete y tapaba los asientos que
     quedan detras: con click-para-sentarse, esos asientos eran imposibles de
     tocar. No tiene nada interactivo, asi que deja pasar el puntero. */
  /* Anclado por ARRIBA, no por el medio. Con `translate(-50%,-50%)` el bloque
     crecía hacia los dos lados: en un showdown con botes laterales —board, bote,
     "lateral 1/2/3", el resultado— las cartas comunitarias terminaban ENCIMA de
     los jugadores de arriba. Anclado por su borde superior, todo lo que se
     agregue baja hacia el centro del fieltro, que está vacío. */
  .center { position: absolute; left: 50%; top: 29%; transform: translateX(-50%);
    display: grid; justify-items: center; gap: 2cqmin; width: 92%;
    pointer-events: none; }
  /* El bote es el ancla del cartel de resultado, que flota justo debajo. */
  .potline { position: relative; }
  .board { display: flex; gap: 1.1cqmin; justify-content: center;
    min-height: 14cqmin; align-items: center; }
  .board .muted { font-size: 2.8cqmin; }
  .potline { display: inline-flex; align-items: center; gap: 1.6cqmin;
    padding: .9cqmin 2.6cqmin; border-radius: 999px; background: rgba(0,0,0,.45);
    box-shadow: inset 0 0 0 .2cqmin rgba(255,255,255,.09); }
  .potline span { font-size: 2.3cqmin; letter-spacing: .13em; text-transform: uppercase;
    color: rgba(255,255,255,.5); }
  .potline b { font-size: 4.6cqmin; font-variant-numeric: tabular-nums; letter-spacing: -.02em; }
  .potline em { font-style: normal; font-size: 2.4cqmin; color: rgba(255,255,255,.5); }
  .sidepots { display: flex; gap: 1.4cqmin; font-size: 2.4cqmin; color: rgba(255,255,255,.55);
    /* Issue #1549 — UNA línea y nada más. Este renglón vive sobre el fieltro,
       arriba de los asientos de abajo, y en agosto los botes se sacaron de acá
       porque se encimaban con ellos. `nowrap` es lo que evita que con tres
       capas el bloque crezca hacia abajo y vuelva a pisarlos; lo mide
       scripts/e2e/pod-compacto.js, que ahora compara el centro contra cada
       placa. */
    flex-wrap: nowrap; white-space: nowrap; justify-content: center; }
  /* Un bote que NO jugás: se apaga en vez de marcarse. Los que jugás son más y
     no necesitan adorno; el que sobra es el que tiene que notarse menos. */
  .sidepots .afuera { opacity: .4; text-decoration: line-through; }
  /* Lo tuyo (#1518) queda al final y separado: es una frase, no un monto. */
  .sidepots .tuyo { color: rgba(255,255,255,.72); }

  /* Resultado de la mano. Sin esto foldeas y no te enteras de nada: ni quien
     gano, ni con que, ni cuanto se llevo. En una mesa presencial eso se ve
     solo. */
  /* FLOTA sobre el tapete, no ocupa lugar en la columna del centro. Adentro de
     la grilla empujaba el bote y el board hacia arriba justo al terminar la
     mano —el momento en que uno está mirando las cartas— y encima quedaba
     encimado con los asientos de abajo.
     Anclado bajo el bote y centrado: es el lugar donde la vista ya está. */
  .result { position: absolute; left: 50%; top: calc(100% + 1.5cqmin);
    transform: translateX(-50%); z-index: 6; white-space: nowrap;
    display: flex; flex-direction: column; align-items: center; gap: .4cqmin;
    padding: 1.2cqmin 3cqmin; border-radius: 2cqmin; text-align: center;
    background: rgba(6,12,10,.9); color: var(--oro);
    box-shadow: inset 0 0 0 .2cqmin rgba(var(--oro-rgb),.45),
      0 1.5cqmin 4cqmin rgba(0,0,0,.55); }
  .result b { font-size: 3.2cqmin; font-weight: 700; }
  .result span { font-size: 2.5cqmin; color: rgba(255,255,255,.7); }
  /* Issue #1257 — mostrar tus cartas al terminar la mano.
     Los botones LLEVAN LAS CARTAS dibujadas: con dos cartas en la mano, elegir
     entre "la primera" y "la segunda" obliga a mirar dos veces. Acá se toca la
     que se quiere mostrar.
     Van debajo del cartel de resultado y sobre el tapete: la ventana son cuatro
     segundos, asi que no pueden estar escondidos detras de nada. */
  /* Issue #1317 — FLOTA, no empuja.
     Estaba en el flujo entre el tapete y las acciones, así que al aparecer le
     comía su alto al tapete — que se escala solo para entrar en lo que sobra. La
     mesa daba un salto justo cuando la mano termina y uno mira quién ganó:
     medido en escritorio, 1260x813 pasaba a 1133x731. */
  /* Issue #1617 — Y VA DONDE VAN LOS BOTONES DE APUESTA.
     REPORTADO con captura: la franja le tapaba la placa de resultado —"marcos
     40 / los demás foldearon"— justo en el renglón de abajo.
     Estaba centrada sobre el paño a 27.5%. Medido a 1280x900: la franja ocupaba
     607..653 y el resultado 541..633, o sea 26px de solape (74px en `dev`, con
     la barra vieja). Y no se veía en los tests porque la franja sale a los
     ~200ms y el resultado se ESCALONA hasta los ~2000: al medir temprano,
     `#result` todavía estaba `hidden` y el solape se calculaba contra nada.
     El rincón de abajo a la derecha es el de `.actionwrap`, y está LIBRE en ese
     momento: la barra de acción se vacía al cerrarse la mano, y la franja
     desaparece cuando arranca la siguiente. Nunca conviven.
     Mismo ancla que `.actionwrap` (right/bottom 10px) para que las dos cosas
     que viven en ese rincón lo midan igual. */
  /* Issue #1617 — SIN CAJA.
     Era una losa de 403x74 en escritorio y 301x70 en el telefono —el 77% del
     ancho— con fondo casi negro y blur sobre el fieltro. Medido, tapaba el 7.6%
     del tapete en el telefono, y ahi el rectangulo era mas ancho que el ovalo a
     esa altura: sus esquinas se desbordaban sobre el fondo negro y la cosa se
     leia como una cinta pegada encima de la mesa.

     Y sobraba: adentro, CADA boton traia su propio marco dorado con fondo. Dos
     cajas anidadas diciendo lo mismo. La de afuera se va y el control pasa a ser
     la carta, que es lo que el jugador viene a tocar.

     `bottom` NO se toca: 27.5% y 44% estan medidos contra las placas y el bote
     (ver el bloque de #1317 abajo), y este grupo es mas angosto, asi que solo se
     aleja de lo que no podia tocar. */
  /* #1665 — sin `position: absolute`: la franja vive DENTRO de .actionwrap y la
     coloca el flex del padre. Absoluta en right/bottom 10px compartia el ancla
     exacta con .actionwrap y el mismo z-index, asi que el rincon era uno solo y
     ganaba la que iba despues en el DOM: el boton de add-on le tapaba 38px
     medidos. Apilada no hay solape posible, con cualquier boton adentro. */
  .showbar { display: flex; align-items: center; justify-content: flex-end;
    gap: 8px; flex-wrap: wrap; }
  /* El rotulo, de gris a eyebrow: nombra al grupo sin competirle. */
  .showbar-txt { font-size: 10px; letter-spacing: .16em; text-transform: uppercase;
    color: rgba(var(--oro-rgb),.7); }
  /* Issue #1617 — la chapita: "las dos" y el conejo, en el MISMO idioma.
     Antes eran dos lenguajes al lado — tres botones-carta y una pastilla de
     texto. El relleno oscuro es por legibilidad y no por caja: el pano es
     configurable en cinco colores (#1106) y el oro sobre el verde claro o el
     ambar se pierde. */
  .showchip { padding: 5px 11px; border-radius: 999px; font-size: 12px;
    border: 1px solid rgba(var(--oro-rgb),.45); background: rgba(6,10,9,.5);
    color: rgba(var(--oro-rgb),.92); cursor: pointer; white-space: nowrap;
    backdrop-filter: blur(3px); -webkit-backdrop-filter: blur(3px);
    transition: transform .12s ease, border-color .12s ease, color .12s ease; }
  .showchip:hover { transform: translateY(-2px);
    border-color: rgba(var(--oro-rgb),.9); color: var(--oro-claro); }
  .showchip:focus-visible { outline: 2px solid var(--oro); outline-offset: 2px; }

  /* Issue #1434 — las cartas que NO pasaron.
     Apagadas y con el borde cortado a propósito: leerse igual que el board de
     verdad sería mentir sobre la mano. Lo que se está mirando es una mano que no
     se jugó, y el tapete tiene que decirlo sin un cartel.

     Se apagan con `filter` y NO con `opacity`: `darVuelta` —la misma animación
     que las da vuelta— anima la opacidad hasta 1, así que con `opacity` cada
     carta se veía entera mientras giraba y recién se apagaba al terminar. Con
     cinco cartas escalonadas eso es un parpadeo, no una revelación. `filter` la
     animación no lo toca. */
  .board .card.conejo { filter: saturate(.45) brightness(.72); box-shadow: none; }
  /* Issue #1617 — el borde cortado, que hasta ahora no se veía.
     Era `rgba(255,255,255,.45)`: blanco al 45% DENTRO de una carta blanca. El
     `filter` de arriba apaga la carta y el borde por igual, así que el contraste
     entre los dos quedaba en cero y el punteado no aparecía en ningún paño.
     Va oscuro: es la carta la que tiene que verse cortada, y el fondo sobre el
     que se dibuja es el frente pálido, no el fieltro. */
  .board .card.conejo::after {
    content: ""; position: absolute; inset: 0; border-radius: inherit;
    border: 1px dashed rgba(22,24,29,.45); }
  /* Issue #1617 — el boton ES la carta: sin marco y sin fondo.
     La pista de que se puede tocar la da la carta misma (aro dorado de un pelo
     abajo) mas el salto del hover, que ya estaba. */
  .showbtn { display: flex; padding: 3px; border: 0; background: none;
    cursor: pointer; line-height: 0;
    transition: transform .12s ease, filter .12s ease; }
  .showbtn:hover { transform: translateY(-2px); filter: brightness(1.06); }
  .showbtn:focus-visible { outline: 2px solid var(--oro); outline-offset: 2px;
    border-radius: 7px; }
  /* Issue #1317 — en pantalla angosta la barra entra en UNA fila.
     Con el rótulo y las cartas a tamaño de escritorio se partía en dos y pasaba
     a medir 146px de alto: flotando ahí tapaba la placa del rival, que al
     terminar la mano es justo lo que uno mira. Se va el rótulo —los botones son
     las cartas, se entiende sin que se lo digan— y las cartas achican. */
  @media (max-width: 560px) {
    /* Y más arriba: el óvalo del teléfono es angosto y alto, así que las placas
       de los rivales caen más al centro.

       El 41% quedó viejo con #1429: esa cabecera pasó de tres filas a una y le
       devolvió 79px al tapete, así que el óvalo se estiró y las placas bajaron.
       Medido a 393x851 DESPUÉS de ese cambio: la barra terminaba en y=502 y la
       placa del rival empieza en 500 — se tocaban por 2px, y este mismo bloque
       existe porque taparla es justo lo que no puede pasar.

       El 44% la deja en 395..465, entre el bote (termina en 364) y el rival
       (empieza en 500): centrada en la banda libre y no pegada a ningún borde,
       que es lo que hace que aguante el próximo cambio de alto. */
    /* En el teléfono se queda SOBRE el paño, no abajo a la derecha: ahí
       `.actionwrap` es `position: static` —está en el flujo, no flotando— así
       que ese rincón no existe como hueco, y abajo además está la floatbar a
       todo lo ancho. Y acá no hace falta: medido a 393x851, la franja ocupa
       429..477 y el resultado 370..413, o sea 16px de aire. */
    /* #1665 — acá SÍ absoluta, y declarada: la franja ya no la hereda del
       bloque de escritorio. En el telefono .actionwrap es `position: static`,
       asi que no es bloque contenedor y esto se posiciona contra el mismo
       ancestro que siempre. */
    .showbar { position: absolute; z-index: 30;
      right: auto; bottom: 44%; left: 50%; transform: translateX(-50%);
      justify-content: center; flex-wrap: wrap; gap: 6px; max-width: 36vw; }
    .showbar-txt { display: none; }
    /* El padding se queda: es el blanco de toque alrededor de una carta de
       24x34, que con el dedo no alcanza sola. */
    .showbtn { padding: 4px; }
    /* Y las chapitas achican igual: acá el grupo crece a lo ANCHO y el óvalo del
       teléfono es angosto — cada píxel de más lo acerca a las placas de los
       costados. */
    .showchip { font-size: 11px; padding: 5px 9px; }
    .showbtn .card { width: 24px; height: 34px; }
    .showbtn .card b { font-size: 14px; }
    .showbtn .card i { font-size: 11px; }
    .showbtn .card.wide b { font-size: 11px; }
  }
  /* Las cartas del boton NO escalan con el tapete como las de la mesa: son un
     control, viven fuera del contenedor que define cqmin y tienen que poder
     tocarse con el dedo. Hay que redefinir TAMBIEN el tamano del rango y del
     palo: quedan en cqmin de la base y se desbordan de la caja. */
  .showbtn .card { width: 28px; height: 40px; border-radius: 5px;
    box-shadow: 0 2px 6px rgba(0,0,0,.5), 0 0 0 1px rgba(var(--oro-rgb),.55); }
  .showbtn .card b { font-size: 16px; }
  .showbtn .card i { font-size: 13px; }
  .showbtn .card.wide b { font-size: 15px; }
  @media (prefers-reduced-motion: reduce) {
    .showbtn { transition: none; }
    .showbtn:hover { transform: none; }
  }

  /* Por que no arranca la mano siguiente. Sin esto la ultima mano queda
     congelada en pantalla y parece que la mesa se tildo. */
  .tstatus { padding: 1cqmin 3cqmin; border-radius: 999px; text-align: center;
    font-size: 2.6cqmin; background: rgba(0,0,0,.4); color: rgba(255,255,255,.72); }
  /* "Arranca en 42s" no es un estado más: es LO que está pasando en una mesa
     que todavía no repartió una mano. Se ve como un cartel y no como una nota
     al pie. */
  .tstatus.grande { padding: 2cqmin 5cqmin; border-radius: 2.4cqmin;
    font-size: 4.4cqmin; font-weight: 700; letter-spacing: -.01em;
    font-variant-numeric: tabular-nums; color: #fff;
    background: rgba(0,0,0,.62); box-shadow: inset 0 0 0 .25cqmin rgba(var(--oro-rgb),.35); }
  /* --- cartas ----------------------------------------------------------
     Cara blanca con rango Y palo. Antes el palo iba SOLO por color, asi que un
     daltonico no podia leer el board — y en un color eso te cuesta el torneo. */
  .card { position: relative; display: flex; flex-direction: column;
    align-items: center; justify-content: center; line-height: .95;
    width: 9.6cqmin; height: 13.4cqmin; border-radius: 1.3cqmin; font-weight: 800;
    background: linear-gradient(180deg, #fff, #e6eaee); color: #16181d;
    box-shadow: 0 .6cqmin 1.4cqmin rgba(0,0,0,.45); }
  .card b { font-size: 5.4cqmin; letter-spacing: -.05em; }
  .card i { font-style: normal; font-size: 4.2cqmin; }
  .card.wide b { font-size: 4.2cqmin; }
  /* Baraja clasica: rojo corazon y diamante, negro trebol y pica. */
  .card.h, .card.d { color: #cf2337; }
  .card.c, .card.s { color: #16181d; }
  /* Issue #1255 — el mazo de cuatro colores, si el jugador lo eligio.
     Con dos, ♥/♦ y ♠/♣ se confunden de reojo, y en una mesa uno mira sus cartas
     medio segundo: confundir un palo arruina una mano entera.
     Corazon y pica NO se tocan —siguen rojo y negro— asi que a quien lo prenda
     le cambian dos de cuatro y no tiene que reaprender el mazo entero.
     Azul y verde oscuros a proposito: sobre el blanco de la carta tienen que
     pesar como el negro de la pica, no leerse como un resaltado. Y quedan
     distinguibles con daltonismo rojo-verde —el mas comun, y justo el que pone
     trebol y corazon enfrentados—: el azul del diamante es la referencia que
     desempata cuando el rojo y el verde se parecen. */
  body.mazo-cuatro .card.d { color: #1668c4; }
  body.mazo-cuatro .card.c { color: #11804a; }

  /* --- Issue #1334: la cara oscura ---------------------------------------
     La carta entera del color del palo, con el rango y la pinta en blanco. Es
     otro eje que el de cuántos colores: se combinan las dos clases, así que con
     `mazo-oscuro` solo van rojo y negro, y sumando `mazo-cuatro` van los cuatro.

     Los tonos NO son los mismos de arriba. Aquellos se eligieron para leerse
     SOBRE blanco y pesar como el negro de la pica; de fondo, con blanco encima,
     el que tiene que aguantar el contraste es el fondo. Están medidos: el e2e
     calcula el contraste real de cada palo contra el blanco y exige 4.5:1, que
     es el mínimo de WCAG AA para texto. Cambiar un tono a ojo lo hace fallar.

     Y siguen distinguiéndose con daltonismo rojo-verde: el azul del diamante es
     la referencia que desempata cuando el rojo y el verde se parecen, igual que
     en el mazo claro. */
  body.mazo-oscuro .card {
    background: linear-gradient(180deg, #23272e, #191c21);
    color: #fff; }
  body.mazo-oscuro .card.h, body.mazo-oscuro .card.d {
    background: linear-gradient(180deg, #b3182b, #911320); }
  body.mazo-oscuro.mazo-cuatro .card.d {
    background: linear-gradient(180deg, #14549e, #103f78); }
  body.mazo-oscuro.mazo-cuatro .card.c {
    background: linear-gradient(180deg, #0d6b3e, #09522f); }
  /* El color lo pone la cara, así que el rango y la pinta van SIEMPRE en blanco:
     sin esto ganaban las reglas de arriba y quedaba rojo sobre rojo. */
  body.mazo-oscuro .card.h, body.mazo-oscuro .card.d,
  body.mazo-oscuro .card.c, body.mazo-oscuro .card.s { color: #fff; }
  /* El dorso no es una carta de ningún palo: no se toca. */
  body.mazo-oscuro .card.back { color: transparent; }
  /* La ganadora se marcaba con un aro dorado sobre cara clara. Sobre cara oscura
     el aro solo no alcanza, así que además se le sube el brillo. */
  body.mazo-oscuro .card.win { filter: brightness(1.28); }
  /* Las cinco que forman la mano ganadora. Las demas se apagan en vez de que
     estas se enciendan: resaltar cinco de siete con brillo deja la mesa toda
     iluminada; bajarle a las otras dos hace que las cinco salten solas. */
  /* Se apagan SOLO si hay alguna resaltada. Sin el :has(), esta regla apagaba
     el board entero en cualquier lado donde no hubiera ganadora marcada — como
     el reproductor, donde la mano se ve completa y no hay nada que resaltar. */
  .board:has(.card.win) .card:not(.win),
  .pod.won .pod-cards:has(.card.win) .card:not(.win) { opacity: .38; }
  .card.win { box-shadow: 0 0 0 .35cqmin var(--amber), 0 .4cqmin 1.4cqmin rgba(230,180,70,.4); }
  .card.back { color: transparent;
    background:
      repeating-linear-gradient(45deg, rgba(255,255,255,.09) 0 .8cqmin, transparent .8cqmin 1.6cqmin),
      linear-gradient(160deg, #9d2333, #6a1320);
    box-shadow: inset 0 0 0 .45cqmin #eef1f4, 0 .6cqmin 1.4cqmin rgba(0,0,0,.45); }

  /* --- animaciones ---
     Sin esto el cliente redibuja instantaneo y no se ve NADA: se pasa de una
     calle a la siguiente sin que nadie alcance a leer quien hizo que. */
  .fx { position: absolute; inset: 0; pointer-events: none; z-index: 5; }
  .fx > * { position: absolute; transform: translate(-50%,-50%); }

  .say { padding: .9cqmin 2.4cqmin; border-radius: 999px; white-space: nowrap;
    font-size: 2.9cqmin; font-weight: 700;
    background: rgba(8,11,13,.92); color: var(--ink);
    box-shadow: 0 0 0 .22cqmin rgba(255,255,255,.18), 0 .6cqmin 1.6cqmin rgba(0,0,0,.5);
    animation: decir 1.6s ease-out forwards; }
  /* --- lo que alguien dice, sobre su asiento ---
     TAMAÑO FIJO: el ancho y la letra NO dependen del mensaje. Un globo que
     crece con el texto convierte el chat en un arma para taparle el tapete a
     los demás — con un párrafo se come media mesa. Se recorta a tres renglones
     y el resto se lee en el cajón, que para eso está.
     Se mide en `cqmin` como todo lo del tapete: así escala con la mesa y no con
     la ventana. */
  .chat-say { max-width: 26cqmin; padding: 1.1cqmin 1.8cqmin; border-radius: 2cqmin;
    font-size: 2.4cqmin; font-weight: 600; line-height: 1.25; text-align: center;
    background: rgba(8,11,13,.94); color: var(--ink);
    box-shadow: 0 0 0 .22cqmin rgba(255,255,255,.16), 0 .6cqmin 1.6cqmin rgba(0,0,0,.55);
    /* Anclado por la BASE: se coloca en el borde de arriba del pod y crece
       hacia arriba, así un mensaje de tres renglones no baja sobre las cartas.
       Y por encima de todo lo del tapete: un globo detrás de una carta no se
       lee, que es lo mismo que no estar. */
    z-index: 7;
    animation: decir-chat 5s ease-out forwards; }
  /* Tres renglones y puntos suspensivos: el que quiera el resto abre el chat.
     El recorte va en una caja PROPIA, sin el padding del globo: midiéndolo
     sobre el globo entero se asomaba un cuarto renglón por debajo de los
     puntos. */
  .chat-say .txt { display: -webkit-box; -webkit-line-clamp: 3;
    -webkit-box-orient: vertical; overflow: hidden; overflow-wrap: anywhere; }
  /* El sticker no va en caja de texto: la imagen ES el mensaje. */
  .chat-say.es-sticker { max-width: none; padding: 0; background: none; box-shadow: none;
    display: block; }
  .chat-say.es-sticker img { display: block; width: 14cqmin; aspect-ratio: 1;
    object-fit: contain; border-radius: 1.6cqmin; }
  @keyframes decir-chat {
    0%   { opacity: 0; transform: translate(-50%,-80%) scale(.9); }
    6%   { opacity: 1; transform: translate(-50%,-100%) scale(1); }
    88%  { opacity: 1; transform: translate(-50%,-100%) scale(1); }
    100% { opacity: 0; transform: translate(-50%,-112%) scale(.96); }
  }
  .say.fold { color: var(--red); }
  .say.check { color: rgba(255,255,255,.8); }
  .say.call { color: var(--green); }
  .say.raise, .say.bet { color: var(--amber); }
  .say.timebank { color: #7fd1ff; }
  @keyframes decir {
    0%   { opacity: 0; transform: translate(-50%,-30%) scale(.85); }
    12%  { opacity: 1; transform: translate(-50%,-50%) scale(1); }
    75%  { opacity: 1; transform: translate(-50%,-50%) scale(1); }
    100% { opacity: 0; transform: translate(-50%,-70%) scale(.95); }
  }

  /* Fichas viajando al bote al cerrarse la ronda de apuestas. */
  .fly { display: flex; align-items: center; gap: .8cqmin; white-space: nowrap;
    padding: .5cqmin 1.5cqmin .5cqmin .6cqmin; border-radius: 999px;
    background: rgba(0,0,0,.6); box-shadow: inset 0 0 0 .2cqmin rgba(255,255,255,.12);
    font-size: 2.7cqmin; font-weight: 600; font-variant-numeric: tabular-nums;
    animation: volar .75s cubic-bezier(.5,0,.6,1) forwards; }
  @keyframes volar {
    0%   { opacity: 1; }
    70%  { opacity: 1; }
    100% { opacity: 0; left: var(--tx); top: var(--ty); transform: translate(-50%,-50%) scale(.6); }
  }
  /* Sin translate: el -50%/-50% es para centrar los elementos ABSOLUTOS de la
     capa de efectos, y aplicado al numero del bote lo corria media caja arriba
     y a la izquierda en cada golpe. Y inline-block porque un <b> es inline y
     transform no le aplica. */
  .pot-bump { display: inline-block; animation: golpe .35s ease-out; }
  @keyframes golpe { 50% { transform: scale(1.14); } }

  /* --- vida en la mesa: reparto, board y showdown ------------------------
     Regla que vale para todas: se anima lo que ACABA DE APARECER, nunca "lo
     que hay". El cliente redibuja con cada mensaje del servidor, asi que
     animar lo que hay reiniciaria cada animacion varias veces por segundo y
     no se veria ninguna — que es exactamente por lo que la mesa se sentia
     instantanea. El delay de cada carta viaja en --tarda. */

  /* Las cartas salen del centro, como las reparte alguien. --dx/--dy son la
     distancia de ese asiento al centro del tapete, en unidades del tapete. */
  .pod-cards .card.repartida {
    animation: repartir .34s cubic-bezier(.2,.75,.35,1) backwards;
    animation-delay: var(--tarda, 0s); }
  @keyframes repartir {
    from { opacity: 0;
      transform: translate(var(--dx, 0cqw), var(--dy, 0cqh)) scale(.45) rotate(-12deg); }
  }

  /* El board no aparece de golpe: se da vuelta, una carta por vez. */
  .board .card.nueva, .pod-cards .card.revelada {
    animation: darVuelta .44s ease-out backwards;
    animation-delay: var(--tarda, 0s); }
  @keyframes darVuelta {
    0%   { opacity: .35; transform: rotateY(90deg) scale(.86) translateY(-1.2cqmin); }
    55%  { opacity: 1; }
    100% { opacity: 1; transform: none; }
  }

  /* El que gana se enciende: sin esto hay que leer el texto para saber quien
     fue, y para eso ya es tarde. */
  /* El porcentaje del all-in. Ambar y en negrita: es el numero que se mira
     mientras salen las cartas, y tiene que leerse de un vistazo. */
  .ptag.eq { background: rgba(var(--amber-rgb),.18); color: var(--amber);
    font-weight: 800; font-variant-numeric: tabular-nums; }
  /* El rodeo dorado va sobre la PLACA — el rectangulo negro con el nombre y las
     fichas — y no sobre .pod, que es una caja mucho mas grande e invisible que
     incluye las cartas y las etiquetas: el brillo salia como un cuadrado enorme
     alrededor de todo, en vez de rodear al jugador. */
  .pod.won .pod-body { animation: ganar 1s ease-out 2; }
  @keyframes ganar {
    50% { box-shadow: 0 0 0 .5cqmin var(--amber), 0 0 4cqmin rgba(230,180,70,.55); }
  }
  /* --- asientos --------------------------------------------------------- */
  /* 24cqmin y no mas: la placa mide 12% del ancho a cada lado del centro del
     asiento, y los de los costados estan a 37% del centro — 37+12 = 49 < 50,
     que es lo unico que evita que en 390px se salgan de la pantalla. */
  /* Los dos anchos los fija el cliente según cuánta gente hay (#1392); acá van
     los valores de mesa NO llena, que son los que valen si nadie los pisa. */
  .stage { --pod-ancho: 24cqmin; --carta-ancho: 6.6cqmin; --carta-solape: -0.13; }
  /* La excepción: una pantalla BAJA con la mesa llena. Ahí las placas pesan más
     de lo que les toca —el nombre tiene piso de 10px, que no escala con el
     resto— así que los pods quedan casi tocándose y las cartas necesitan
     volver a apretarse para no montarse sobre la placa del vecino. Medido: a
     375×667 con nueve asientos, el 13% pisa 12px² del pod de al lado; el 30%
     no pisa nada. En un teléfono más alto (393×851) el 13% entra bien. */
  @media (max-height: 700px) {
    .stage.llena { --carta-solape: -0.30; }
  }
  /* Issue #1722 — `grid-template-columns: 100%` no es cosmético: es LO QUE EVITA
     QUE LA PLACA SE ESTIRE.
     REPORTADO: "entre su racha, la etiqueta de UTG y los segundos de juego,
     estira el rectángulo del jugador". Y es literal. La columna de esta grilla
     era `auto`, o sea que crecía con el hijo más ancho — la fila de etiquetas,
     que es un flex sin wrap. Y `.pod-body` es `width: 100%` DE LA COLUMNA, no
     del pod: con las cinco etiquetas que el cliente puede emitir a la vez, la
     placa pasaba de 78px a 230 en un Android y de 164 a 395 en escritorio.
     Medido. El `.pod` seguía midiendo lo suyo, así que ningún guard que mirara
     la caja del pod se enteraba: se veían dos placas encimadas 7.800px².
     Con la columna fijada al ancho del pod, la fila puede desbordar —eso se
     tolera, entre placa y placa hay hueco— pero la placa no se mueve. */
  .pod { position: absolute; transform: translate(-50%,-50%);
    width: var(--pod-ancho); display: grid; grid-template-columns: 100%;
    justify-items: center; gap: .7cqmin; }
  /* Issue #1392 — las cartas MONTADAS sobre la placa, no en una fila propia.
     Referencia: GGPoker, donde las cartas van sobre el avatar y el pod entero
     ocupa poco más que ese círculo. Acá eran tres bloques apilados —cartas,
     placa, etiquetas— de 143px de alto en un teléfono, y con nueve de esos no
     quedaba fieltro: medido, la placa de un jugador ya tapaba 376px² de las
     cartas de otro, y no había ningún punto libre donde poner el disco de
     dealer (#1390).
     El margen negativo las solapa sin que el pod crezca; el z-index las manda
     atrás de la placa, así que lo que se tapa es el dorso y nunca el nombre. */
  .pod-cards { display: flex; gap: .7cqmin; height: 9.4cqmin; align-items: flex-end;
    margin-bottom: -3.4cqmin; position: relative; z-index: 0; }
  /* El valor ARRIBA Y A LA IZQUIERDA, como el índice de una carta de verdad.
     Centrado no servía para nada de esto: montar la carta sobre la placa le
     comía el número, y abanicarlas le tapaba el de la de atrás. */
  .pod-cards .card { width: var(--carta-ancho); height: 9.4cqmin; border-radius: .9cqmin;
    align-items: flex-start; justify-content: flex-start; padding: .7cqmin .6cqmin; }
  /* ABANICADAS, no lado a lado. Es lo que hace que el pod entre en una mesa de
     nueve: dos cartas enteras una al lado de la otra son casi 14cqmin, y la
     separación entre dos asientos vecinos no da para eso más la placa. Con el
     índice arriba a la izquierda, la de atrás se sigue leyendo.

     REPORTADO (#1410): "las cartas de los rivales se ven muy pegadas una a la
     otra". Se solapaban el 37% contra el 13% de las tuyas — medido, no a ojo.
     El solapado era un número fijo en cqmin, así que en una mesa de nueve, con
     la carta más chica, ese mismo número se comía una porción mucho mayor.

     Ahora es una FRACCIÓN del ancho de la carta: el `- .7cqmin` cancela el gap
     del flex, y lo que queda es exactamente el 13% de solapado, el mismo que el
     hero, sin importar cuánto mida la carta en esa mesa. */
  .pod-cards .card + .card {
    margin-left: calc(var(--carta-ancho) * var(--carta-solape, -0.13) - .7cqmin); }
  .pod.me .pod-cards .card + .card { margin-left: -2cqmin; }
  .pod-cards .card b { font-size: 3.7cqmin; }
  .pod-cards .card i { font-size: 2.7cqmin; }
  .pod-cards .card.wide b { font-size: 2.9cqmin; }
  /* Tus cartas mas grandes y por delante: es lo que mas se mira de la pantalla.
     Y se solapan MENOS que las ajenas: de las tuyas hay que leer las dos
     enteras, no el dorso. */
  /* Issue #1723 — detrás de la placa y 1.8cqmin arriba: tapaban stack y nombre. Se
     puede porque el índice va ARRIBA Y A LA IZQUIERDA; con -6.4 el palo quedaba 43%
     comido. `auto` y no `0`: un z-index numérico encierra al disco de dealer. */
  .pod.me .pod-cards { height: 14cqmin; margin-bottom: -4.6cqmin; z-index: auto; position: relative; }
  /* Tu pod por delante de las fichas apostadas. Al bajar las cartas sobre la
     placa, la ficha de una apuesta quedaba encima del valor de una carta tuya —
     medido en un iPhone SE. Entre leer tu carta y leer una ficha, gana la carta:
     el número de la ficha se repite en el bote. */
  .pod.me { z-index: 3; }
  .pod.me .pod-cards .card { width: 10cqmin; height: 14cqmin; border-radius: 1.3cqmin; }
  .pod.me .pod-cards .card b { font-size: 5.6cqmin; }
  .pod.me .pod-cards .card i { font-size: 4.2cqmin; }
  /* Issue #1723 — el "10" salía al 78% de una "J"; en las tuyas no desborda (medido). */
  .pod.me .pod-cards .card.wide b { font-size: 5.6cqmin; }

  /* Issue #1744 — el contenido va CENTRADO, no pegado a la izquierda.
     REPORTADO: "están estirados los jugadores". El `flex` no tenía
     `justify-content`, así que el avatar y el texto quedaban a la izquierda y
     TODO el sobrante caía de un solo lado: medido, 12px de aire a la izquierda y
     40-52px a la derecha — el 28% de la placa, igual en 1366, 1440, 1920 y 2560.
     No lo trajo la racha: `width: 100%` es viejo. La racha lo delató, porque
     ahora hay una línea encendida trazando ese borde que antes era placa oscura
     sobre fieltro oscuro. Centrar no cambia ningún tamaño, así que no mueve la
     geometría del marco ni los techos de alto.

     Y el fondo deja de ser translúcido: con .96 el 4% del velo del arte que va
     detrás se colaba igual. Medido, una diferencia de hasta 10 sobre 255 —
     invisible, pero obliga al guard a tener un umbral que defender en vez de
     exigir cero. El .96 venía de la mudanza del CSS de #1035, sin motivo escrito,
     y ningún `backdrop-filter` depende de él. */
  .pod-body { position: relative; z-index: 1; overflow: hidden; width: 100%;
    display: flex; align-items: center; justify-content: center; gap: 1.4cqmin;
    padding: 1cqmin 1.6cqmin;
    border-radius: 2.6cqmin;
    background: linear-gradient(180deg, rgb(19,24,28), rgb(8,11,13));
    box-shadow: 0 .6cqmin 1.6cqmin rgba(0,0,0,.5), inset 0 0 0 .22cqmin rgba(255,255,255,.11); }
  /* La foto compite por el mismo ancho que el stack, y el stack es el dato: con
     6.8cqmin la foto se llevaba más lugar que el número (#1690). */
  .avatar { flex: none; width: 5.2cqmin; height: 5.2cqmin; border-radius: 50%;
    display: grid; place-items: center; font-size: 2.5cqmin; font-weight: 800;
    color: #e4f5ec; background: linear-gradient(150deg, #2f6f57, #1c4737);
    box-shadow: inset 0 0 0 .2cqmin rgba(255,255,255,.16); }
  /* Nombre arriba, fichas abajo — pero las fichas en UNA SOLA LÍNEA. En ciegas
     el texto es más largo ("403,2 BB" contra "403"), se partía en dos renglones
     y estiraba la placa hacia abajo: cada jugador quedaba de un alto distinto
     según cuántas fichas tuviera. La fuente se achica lo necesario y listo. */
  .pod-txt { min-width: 0; display: grid; }
  /* Issue #1254 — el renglon del nombre: el nombre cede, el badge no.
     El badge vivia ADENTRO de `.n`, que recorta, asi que con un nombre largo se
     iba con los puntos suspensivos. Ahora son hermanos: `.n` se achica y recorta
     sola, y el badge tiene su lugar reservado. */
  .pod-txt .nl { display: flex; align-items: baseline; min-width: 0; }
  .pod-txt .nl .n { min-width: 0; }
  .pod-txt .nl .entry { flex: none; }
  /* Issue #1520 — el punto de "tengo algo escrito sobre éste".
     Va al lado del nombre, del mismo tamaño que el badge de entrada, pero
     apagado: es una ayuda memoria propia, no un dato del juego. Si compitiera
     con el stack o con la cabeza estaría gritando algo que sólo le importa a
     una persona de la mesa. */
  /* Issue #1673 — el marco de color, por `outline` y NO por `box-shadow`.
     Ese canal ya está ocupado por siete reglas que se PISAN entre sí (la base,
     .me, .turn, .won, .free, .free:hover, .vacant) más @keyframes ganar, que lo
     reemplaza ENTERO por dos segundos: el marco parpadearía justo cuando el
     jugador mira la placa.

     Y `outline` no está en el modelo de caja, así que el rect de .pod-body es
     idéntico con y sin marco — que es el número del que dependen el solape
     entre pods y los 12px de asomo de las cartas (pod-compacto.js). No hay que
     argumentar que no rompe: no puede.

     El offset NEGATIVO es lo que lo hace seguro: por afuera se metería en la
     caja del pod vecino y caería sobre sus cartas. Por adentro convive con el
     anillo ámbar de `turn` y el inset verdoso de `me`, que quedan por afuera. */
  .pod.nota-c1 .pod-body { outline: .45cqmin solid var(--nota-1); outline-offset: -.75cqmin; }
  .pod.nota-c2 .pod-body { outline: .45cqmin solid var(--nota-2); outline-offset: -.75cqmin; }
  .pod.nota-c3 .pod-body { outline: .45cqmin solid var(--nota-3); outline-offset: -.75cqmin; }
  .pod.nota-c4 .pod-body { outline: .45cqmin solid var(--nota-4); outline-offset: -.75cqmin; }
  .pod.nota-c5 .pod-body { outline: .45cqmin solid var(--nota-5); outline-offset: -.75cqmin; }
  /* Issue #1673 — el fuego y el hielo de la racha, alrededor de la placa.
     Absoluto sobre LA CELDA DE LA PLACA y no sobre el pod entero: el pod incluye
     las cartas y la fila de etiquetas, y ahí arriba las llamas quedaban sobre el
     abanico. Al ser absoluto no ocupa lugar ni mueve ningún rect, que es la
     condición de todo lo que se le agrega a esta placa.
     El arte son dos SVG (`/rachas/`): a 171px de ancho un dibujo hecho a mano no
     llega, y estos escalan sin perder nada y pesan 116KB contra 517KB del PNG. */
  /* El inset NO es a ojo: el arte tiene su marco interno en 8% del ancho y 12%
     del alto del lienzo, así que para que ese rectángulo caiga EXACTAMENTE sobre
     el borde de la placa la caja tiene que ser 1/(1-2*0.08) y 1/(1-2*0.12) más
     grande — o sea 9.524% y 15.789% de sangrado a cada lado. Con 8 y 14 los dos
     rectángulos quedaban desfasados y se veía doble en las esquinas. */
  /* Issue #1722 — el arte lame hacia adentro hasta la mitad del rectángulo.
     En escritorio eso es lo que lo hace ver bien: el avatar va al costado y las
     llamas pasan por encima del borde sin tapar nada. En el TELÉFONO la placa
     apila el avatar arriba (#1258) y los carámbanos le caían encima — medido,
     llegaban a 14.5px de una placa de 60 y el avatar arranca a 3.7px.
     Se probaron dos caminos antes de éste. Mandarlo detrás en TODAS las
     pantallas: el panel queda limpio pero la placa es opaca y se come el dibujo,
     y a tamaño real no se ve nada. Y recortarle el centro con una máscara de dos
     capas: el recorte es un rectángulo de esquinas rectas sobre un marco
     redondeado, así que deja agujeros a la vista. Lo que queda es partir por
     pantalla, que además es lo honesto: el arte está dibujado para un panel 2:1
     y la placa de un teléfono es 1.3:1. */
  /* Issue #1744 — el marco va DETRÁS de la placa.
     REPORTADO sobre producción: "las llamas no bordean correctamente el
     rectángulo del jugador, en los costados el fuego queda dentro del
     rectángulo". Y es peor que eso: medido con el guard diferencial, los píxeles
     que el arte cambia abarcan 184 de los 187px de ancho de la placa. No lame
     los bordes — composita un velo de turbulencia sobre TODO el interior, que es
     lo que hacía ver las placas lavadas.
     Agrandar el marco no servía: el velo crece con él. Y ponerle al arte de
     escritorio la máscara que usa el del teléfono recorta 2px de 15.
     Detrás, la placa tapa lo que entre y sólo se ve lo que asoma. La línea
     encendida sigue viéndose porque el `::after` la dibuja por FUERA del borde,
     donde nada la cubre.

     SIGUE DETRÁS aunque el arte ya no lame (#1754), y el motivo es de fondo: la
     placa cambia de proporción con la cantidad de asientos —187x77 en una mesa
     de 6, 164x77 en una de 9—, así que NINGÚN lienzo único calza en las dos.
     Medido con el marco adelante y el arte nuevo: mesa de 6 limpia en las tres
     pantallas de escritorio, mesa de 9 sucia en las nueve placas. Y el teléfono
     es el espejo, porque su arte se dibujó para la proporción de la de 9.
     Detrás vale para las cuatro combinaciones sin casos especiales, y con este
     arte casi no se paga nada: asoma 13.8 de los 15.6px de sangrado, así que
     adelante y detrás se ven igual. Con el arte viejo no era así, y por eso
     mandarlo detrás se había descartado en su momento (`b85cd5a`). */
  .pod-racha { position: absolute; grid-area: 2 / 1 / 3 / 2; inset: -15.789% -9.524%;
    pointer-events: none; z-index: 0;
    /* Issue #1722 — el arte se estira, PERO la línea nítida no sale de él.
       REPORTADO en un teléfono: "los rectángulos tienen otro tamaño y no
       coinciden". La alineación estaba bien —medido, 0.0px de desfase en los
       cuatro lados— pero el arte es 2:1 y la placa del teléfono es 1.3:1, así
       que al estirarlo las ESQUINAS quedaban elípticas: 9.6px de radio
       horizontal contra 16.3 vertical, sobre una placa con 9.7 parejo.
       La solución es repartir el trabajo: el SVG pone las llamas y la nieve
       —orgánicas, estirarlas no se nota— y el marco encendido lo dibuja el CSS
       con el MISMO `border-radius` que la placa, así coincide en cualquier
       proporción. Se probó `border-image` primero: no sirve, porque para que el
       marco interno caiga sobre la placa el corte tendría que ser exactamente el
       sangrado, y la esquina redondeada del arte vive adentro de ese borde. */
    background-repeat: no-repeat; background-size: 100% 100%; }
  .pod.racha-fuego .pod-racha { background-image: url("/rachas/marco-fuego.svg"); }
  .pod.racha-hielo .pod-racha { background-image: url("/rachas/marco-hielo.svg"); }
  /* El marco encendido, justo sobre el borde de la placa: `inset` en porcentaje
     del aura, que es la cuenta inversa del sangrado (8% y 12%). */
  .pod-racha::after { content: ""; position: absolute; inset: 12% 8%;
    border-radius: 2.6cqmin; }
  .pod.racha-fuego .pod-racha::after {
    box-shadow: 0 0 0 .22cqmin #ffe9a8, 0 0 1.4cqmin rgba(255,200,120,.75); }
  .pod.racha-hielo .pod-racha::after {
    box-shadow: 0 0 0 .22cqmin #d8f3ff, 0 0 1.4cqmin rgba(190,235,255,.7); }
  /* El nivel es la intensidad: uno solo apenas se insinúa, tres está encendido. */
  /* Issue #1744 — el piso sube. Con .34 y .62 la llama, que es textura suave,
     desaparecía y sólo sobrevivía la línea encendida: los niveles 1 y 2 se leían
     como un borde de "seleccionado" y no como fuego. */
  .pod.racha-n1 .pod-racha { opacity: .55; }
  .pod.racha-n2 .pod-racha { opacity: .8; }
  .pod.racha-n3 .pod-racha { opacity: 1; }
  /* Con la mesa llena la caja se achica y el FONDO se agranda para compensar, en
     vez de recortar con clip-path: `clip-path` recorta el pintado pero no la
     caja, y lo que mide el guard de solape es la caja. Al ir centrado, el marco
     interno del arte sigue cayendo exactamente sobre el borde de la placa —
     sólo se pierde parte de las llamas de afuera, que es lo que sobra cuando
     nueve placas se acercan. Medido: sin esto, una carta del vecino rozaba el
     marco 12px² en un iPhone SE de nueve.
     Las cuentas: ancho = 119.048 / (1 + 2*0.05), alto = 131.579 / (1 + 2*0.08). */
  .stage.llena .pod-racha { inset: -8% -5%;
    background-size: 108.22% 113.43%; background-position: center; }
  /* Con la caja más chica, la cuenta inversa del sangrado también cambia. */
  .stage.llena .pod-racha::after { inset: 6.9% 4.55%; }

  .pod-txt .nl .nota-marca { flex: none; }
  .nota-marca { font-style: normal; margin-left: .35em; font-size: 1.1em;
    line-height: 1; color: var(--muted); opacity: .85; }
  /* Por qué entrada va, al lado del nombre. Sólo aparece desde la segunda. */
  .entry { font-style: normal; margin-left: .5em; padding: 0 .4em; border-radius: 999px;
    font-size: .8em; font-weight: 700; font-variant-numeric: tabular-nums;
    background: rgba(var(--amber-rgb),.18); color: var(--amber); }
  /* Issue #1514 — cuánto vale la cabeza de cada uno, pegada a su stack.
     Va ANTES del stack y más chica: el stack sigue siendo lo que se mira para
     decidir, la cabeza es el dato que lo matiza. En oro como el resto del
     cliente (#1106) y en pastilla para que no se lea como parte del stack. */
  .stand-row .st .bty { font-style: normal; margin-right: .5em; padding: 0 .4em;
    border-radius: 999px; font-size: .78em; font-weight: 700;
    font-variant-numeric: tabular-nums;
    background: rgba(var(--oro-rgb),.16); color: var(--oro); }
  /* Más chico que las fichas: los nombres del club son largos ("bot-tincho",
     "MCMXCVIIIWido") y a 2.8cqmin se cortaban con puntos suspensivos. El stack
     es lo que se mira para decidir, así que si algo tiene que ceder tamaño es
     el nombre. */
  .pod-txt .n { font-size: 2.42cqmin; font-weight: 650;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  /* 2.8 y no más: a 3.2 un stack de 6 cifras entraba cortado en los 9 asientos,
     y los torneos de 100.000 de stack inicial llegan a 7 (#1690). */
  .pod-txt .s { font-size: 2.8cqmin; font-variant-numeric: tabular-nums; color: var(--oro);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  /* Con la unidad al lado (BB) el texto es más largo: entra igual, más chico. */
  body.en-bb .pod-txt .s { font-size: 2.6cqmin; }
  .pod.me .pod-body { box-shadow: 0 .6cqmin 1.6cqmin rgba(0,0,0,.5),
    inset 0 0 0 .28cqmin rgba(var(--green-rgb),.55); }
  .pod.turn .pod-body { box-shadow: 0 0 0 .45cqmin var(--amber), 0 0 3cqmin rgba(var(--amber-rgb),.4); }
  .pod.won .pod-body { box-shadow: 0 0 0 .45cqmin var(--green), 0 0 4cqmin rgba(var(--green-rgb),.6); }
  .pod.folded { opacity: .4; }
  /* El que se fundio: sigue en el tapete hasta que se termina de mostrar la
     mano, pero ya no esta en la mesa. Se nota sin taparlo — si se lo apagara
     como al que foldea no se le verian las cartas del showdown. */
  .pod.saliendo { filter: grayscale(.55); }
  .pod.saliendo .avatar { opacity: .6; }
  .pod.free .pod-body { cursor: pointer;
    background: rgba(var(--green-rgb),.10);
    box-shadow: inset 0 0 0 .25cqmin rgba(var(--green-rgb),.45); }
  .pod.free:hover .pod-body { background: rgba(var(--green-rgb),.2);
    box-shadow: inset 0 0 0 .3cqmin rgba(var(--green-rgb),.7); }
  .pod.free .pod-txt .n { color: var(--green); font-weight: 650; }
  .pod.free .pod-txt .s { color: rgba(255,255,255,.55); font-size: 2.6cqmin; }

  .pod.vacant .pod-body { background: rgba(255,255,255,.035);
    box-shadow: inset 0 0 0 .22cqmin rgba(255,255,255,.07); }
  .pod.vacant .pod-txt .n { color: rgba(255,255,255,.34); font-weight: 500; }
  /* El reloj vive dentro de la placa (overflow: hidden), asi que se ve como una
     barra que se vacia por debajo del nombre. Es la señal de que el tiempo
     corre — sin esto no sabes cuanto te queda antes de que te foldeen. */
  .pod-timer { position: absolute; left: 0; bottom: 0; height: .8cqmin; width: 100%;
    background: var(--amber); transform-origin: left; transform: scaleX(var(--t, 1)); }
  .pod-timer.low { background: var(--red); }
  .pod-secs { font-size: 2.4cqmin; font-weight: 700; font-variant-numeric: tabular-nums;
    padding: .3cqmin 1.2cqmin; border-radius: .9cqmin; letter-spacing: .02em;
    background: rgba(var(--amber-rgb),.2); color: var(--amber); }
  .pod-secs.low { background: rgba(var(--red-rgb),.24); color: #ffbcc6;
    animation: latir 1s ease-in-out infinite; }

  .pod-tags { display: flex; gap: .6cqmin; min-height: 3.2cqmin; }
  /* `white-space: nowrap` — "ALL-IN" se partía en dos renglones cuando la fila
     se apretaba, y una píldora de dos renglones agranda la placa, que es lo que
     el comentario de abajo dice que no puede pasar (#1722). */
  .ptag { white-space: nowrap; font-size: 2.2cqmin; font-weight: 700; letter-spacing: .04em;
    /* `line-height: 1` no es cosmético: sin él una etiqueta mide 3.24cqmin y la
       fila tiene 3.2cqmin reservados, así que la placa crecía 0.04cqmin y en un
       iPhone SE de nueve asientos eso alcanzaba para que las cartas pisaran la
       placa de al lado. Lo mide scripts/e2e/pod-compacto.js (#1392). */
    line-height: 1;
    padding: .3cqmin 1cqmin; border-radius: .9cqmin;
    background: rgba(255,255,255,.11); color: rgba(255,255,255,.75); }
  .ptag.allin { background: rgba(var(--red-rgb),.24); color: #ffbcc6; }
  /* Issue #1722 — "sin conexión" pasa a un ícono: doce caracteres eran 76px de
     una placa de 78 en un Android. Dibujado y no emoji, porque las métricas del
     emoji cambian por plataforma y acá `line-height: 1` está puesto porque
     0.04cqmin de más ya hizo que las cartas pisaran la placa vecina.
     Un círculo tachado, que es lo que se lee a 10px: cualquier dibujo de antena
     o de wifi a ese tamaño es una mancha. El texto sigue en el `title` y en el
     `aria-label`, así que para un lector de pantalla no cambia nada. */
  .ptag.sincon { position: relative; padding: .3cqmin .6cqmin; }
  .ptag.sincon::before { content: ""; display: block;
    width: 2cqmin; height: 2cqmin; min-width: 9px; min-height: 9px;
    border-radius: 50%; box-shadow: inset 0 0 0 .3cqmin currentColor; }
  .ptag.sincon::after { content: ""; position: absolute; left: 50%; top: 50%;
    width: 2.8cqmin; min-width: 12px; height: .3cqmin; min-height: 1.5px;
    background: currentColor; transform: translate(-50%, -50%) rotate(-45deg); }
  /* Y el chip de "hay N más", con el mismo criterio que los botes laterales:
     apagado, porque lo que importa es que exista, no que compita. */
  .ptag.mas { opacity: .72; }
  /* Issue #1538 — la posición no avisa nada, dice dónde estás sentado respecto
     del botón. Las otras etiquetas son eventos (te toca, te quedaste sin
     fichas) y por eso tienen fondo; ésta va con el contorno solo, para que
     esté cuando la buscás y no compita cuando pasa algo. */
  .ptag.pos { background: none; color: rgba(255,255,255,.58);
    box-shadow: inset 0 0 0 .2cqmin rgba(255,255,255,.16); }
  /* Issue #1299 — los numeros de un rival, al tocar su placa.
     Se ancla en el MISMO left/top que la placa (lo pone /stats.js) y se sube con
     el translate: asi sigue al jugador cuando el torneo lo cambia de asiento,
     sin recalcular ninguna posicion. */
  .podstats { position: absolute; z-index: 9;
    transform: translate(-50%, calc(-100% - 9cqmin));
    min-width: 30cqmin; padding: 1.1cqmin 1.8cqmin; border-radius: 2cqmin;
    background: rgba(10,14,18,.93); backdrop-filter: blur(10px) saturate(1.2);
    -webkit-backdrop-filter: blur(10px) saturate(1.2);
    box-shadow: 0 1.5cqmin 4cqmin rgba(0,0,0,.6),
      inset 0 0 0 .18cqmin rgba(255,255,255,.14); }
  /* La flecha que lo ata a la placa: sin esto, sobre una mesa de seis no se sabe
     de quien son los numeros. */
  .podstats::after { content: ""; position: absolute; left: 50%; top: 100%;
    transform: translateX(-50%);
    border: 1.2cqmin solid transparent; border-top-color: rgba(10,14,18,.93); }
  /* Dado vuelta, para los asientos de arriba: ahí el flotante se salía de la
     ventana. La flecha se va al techo y apunta para arriba, que es de donde
     ahora cuelga. */
  /* El campo de la nota, al final del panel (#1520). Separado por una línea de
     los números: arriba está lo que calcula el servidor y ve toda la mesa,
     abajo lo que escribí yo y no ve nadie. */
  .ps-nota { margin-top: .7cqmin; padding-top: .7cqmin; border-top: 1px solid var(--line); }
  /* Issue #1673 — la fila de colores, ARRIBA del campo: el textarea crece al
     enfocarse y una fila de botones que se mueve cuando el campo se despliega es
     una fila que se toca mal. Y "sin color" es un botón más, para que limpiar
     sea el mismo gesto que elegir. */
  .ps-colores { display: flex; gap: .6cqmin; margin-bottom: .7cqmin; }
  /* En cqmin y sin piso en px: el panel entero mide en unidades del tapete
     (#1592), y un piso en píxeles le rompe el alto justo en la pantalla chica
     que ese issue vino a arreglar. */
  .ps-color { width: 3.4cqmin; height: 3.4cqmin;
    border-radius: 50%; border: 1px solid var(--line-strong); cursor: pointer; padding: 0; }
  .ps-color.c1 { background: var(--nota-1); }
  .ps-color.c2 { background: var(--nota-2); }
  .ps-color.c3 { background: var(--nota-3); }
  .ps-color.c4 { background: var(--nota-4); }
  .ps-color.c5 { background: var(--nota-5); }
  /* El de "ninguno" se dibuja vacío y con una barra: un círculo transparente a
     secas se lee como un color que no cargó. */
  .ps-color.nada { background:
    linear-gradient(45deg, transparent 44%, var(--muted) 44%, var(--muted) 56%, transparent 56%); }
  .ps-color.puesto { outline: 2px solid var(--ink); outline-offset: 1px; }
  .ps-nota textarea { width: 100%; resize: none; border: 1px solid var(--line);
    border-radius: 1cqmin; padding: .35cqmin .7cqmin; font: inherit;
    font-size: 2.1cqmin; line-height: 1.3; height: 3.8cqmin; overflow: hidden;
    background: rgba(0,0,0,.25); color: var(--ink); }
  /* Al escribir sí se despliega: el panel se agranda cuando el jugador lo pidió,
     no mientras mira la mesa. */
  .ps-nota textarea:focus { height: 9cqmin; overflow: auto; }
  .ps-nota textarea::placeholder { color: var(--muted); opacity: .7; }

  .podstats.abajo { transform: translate(-50%, 9cqmin); }
  .podstats.abajo::after { top: auto; bottom: 100%;
    border-top-color: transparent; border-bottom-color: rgba(10,14,18,.93); }
  /* Y contra los bordes: en vez de centrarse sobre la placa se apoya en el
     costado, con la flecha quedándose donde está el jugador. */
  .podstats.al-borde-izq { transform: translate(-12%, calc(-100% - 9cqmin)); }
  .podstats.al-borde-izq.abajo { transform: translate(-12%, 9cqmin); }
  .podstats.al-borde-izq::after { left: 12%; }
  .podstats.al-borde-der { transform: translate(-88%, calc(-100% - 9cqmin)); }
  .podstats.al-borde-der.abajo { transform: translate(-88%, 9cqmin); }
  .podstats.al-borde-der::after { left: 88%; }
  .ps-cab { display: flex; align-items: baseline; gap: 1cqmin;
    margin-bottom: .7cqmin; font-size: 2.4cqmin; line-height: 1.25; font-weight: 700;
    color: #fff; white-space: nowrap; }
  .ps-cab small { font-size: 2.1cqmin; font-weight: 400; color: rgba(255,255,255,.55); }
  /* Issue #1341 — cuanto vale su cabeza, en un KO/PKO.
     Pesa mas que los porcentajes y va separada por una linea: es un numero de
     otra naturaleza —plata sobre esa persona, no como juega— y en un PKO es el
     que decide contra quien te jugas el stack. En ORO porque es plata, el mismo
     que usa el premio garantizado en el lobby. */
  .ps-ko { display: flex; align-items: baseline; gap: 1cqmin;
    margin-bottom: .7cqmin; padding-bottom: .7cqmin;
    border-bottom: 1px solid rgba(255,255,255,.12);
    font-size: 2.4cqmin; line-height: 1.25;
    font-variant-numeric: tabular-nums; white-space: nowrap; }
  .ps-ko span { flex: 1; color: rgba(255,255,255,.6); font-size: 2.3cqmin; }
  .ps-ko b { font-weight: 800; color: var(--oro); }
  .ps-row { display: flex; align-items: baseline; gap: 1cqmin;
    font-size: 2.1cqmin; line-height: 1.3;
    font-variant-numeric: tabular-nums; white-space: nowrap; }
  .ps-row span { flex: 1; color: rgba(255,255,255,.6); }
  .ps-row b { font-weight: 700; color: #fff; }
  /* La fraccion de 3bet y robo: son porcentajes sobre OPORTUNIDADES, y con ~70
     manos por torneo esas oportunidades son pocas. El denominador es el dato. */
  .ps-row em { font-style: normal; font-size: 2cqmin; color: rgba(255,255,255,.45); }
  .pod.statable .pod-body { cursor: pointer; }

  /* Fichas apostadas, entre el jugador y el bote — es como se lee una mesa de
     un vistazo: quien puso cuanto y de que lado esta. */
  .bet { position: absolute; transform: translate(-50%,-50%); z-index: 2;
    display: flex; align-items: center; gap: .8cqmin; white-space: nowrap;
    padding: .5cqmin 1.5cqmin .5cqmin .6cqmin; border-radius: 999px;
    background: rgba(0,0,0,.6); box-shadow: inset 0 0 0 .2cqmin rgba(255,255,255,.12);
    font-size: 2.7cqmin; font-weight: 600; font-variant-numeric: tabular-nums; }
  .chip { width: 3.4cqmin; height: 3.4cqmin; border-radius: 50%;
    background: radial-gradient(circle at 34% 30%, #ff8d9d, #c3243c 72%);
    box-shadow: inset 0 0 0 .45cqmin rgba(255,255,255,.85); }
  /* Issue #1390 — el disco va en la FILA DE LAS CARTAS del propio pod, al
     costado libre.
     Recién es posible con el pod compacto de #1392: ahí las cartas quedaron
     abanicadas y ocupan menos de la mitad del ancho del pod, así que al lado
     sobra lugar. Antes flotaba en la elipse a un radio elegido a mano contra un
     tamaño de carta, y al cambiar ese tamaño tapaba cartas — hasta 1194px².
     Se buscó otro punto del fieltro barriendo desplazamiento y radio en tres
     pantallas y mesas de 2, 6 y 9: el mejor dejaba 9px, y ninguno servía en
     todas. Adentro del pod no hay nada que buscar: el pod ya no pisa a otro
     (medido en scripts/e2e/pod-compacto.js) y las cartas están al lado, no
     debajo. */
  .dbtn { flex: none; align-self: flex-end; margin-bottom: -2.4cqmin; z-index: 4;
    width: 5.2cqmin; height: 5.2cqmin; border-radius: 50%; display: grid; place-items: center;
    font-size: 2.7cqmin; font-weight: 800; color: #15181b;
    background: linear-gradient(180deg, #fff, #c4ccd3);
    box-shadow: 0 .4cqmin 1cqmin rgba(0,0,0,.55); }
  /* Del lado que mira al centro de la mesa, como en cualquier sala. `order` y
     no `justify-self`: va PEGADO al abanico, no al borde del pod. En el borde se
     metía en la caja del pod vecino —que puede solaparse aunque sus partes no— y
     caía sobre las cartas del de al lado. */
  .dbtn.izq { order: -1; margin-right: -3.2cqmin; }
  .dbtn.der { order: 1; margin-left: -3.2cqmin; }
  /* Issue #1723 — el disco del hero estaba escondido detrás del AVATAR. ABSOLUTO: al
     lado con margen ensanchaba la fila 2.8cqmin y esa punta pisaba el marco del vecino. */
  .pod.me .dbtn { position: absolute; top: -1.4cqmin; margin: 0; }
  .pod.me .dbtn.izq { left: -5.8cqmin; }
  .pod.me .dbtn.der { right: -5.8cqmin; }

  /* --- barra de accion --------------------------------------------------
     BOTONES FLOTANTES, no un bloque. Antes era un panel opaco de una pieza y al
     achicar la ventana tapaba media mesa — incluidas tus propias cartas y tu
     stack, que es justo lo que estas mirando cuando decidis. Ahora cada boton
     flota por su cuenta sobre el fieltro, como en cualquier cliente de verdad.

     El orden es el de la mano: primero elegis CUANTO (los tamaños arriba) y
     despues QUE (los tres botones abajo). Al reves obligaria a volver hacia
     arriba despues de haber decidido. */
  /* ALTURA RESERVADA, siempre la misma. Es el arreglo del bug más molesto que
     tuvo la mesa: los botones viven en el flujo, y su alto va de 0 (sin turno) a
     153px (turno con tamaños de apuesta, deslizador y time bank). Como el tapete
     toma "lo que sobra", la mesa entera se achicaba y se agrandaba en CADA
     turno — justo cuando uno está mirando las cartas.
     Va `height` y no `min-height`: con min-height, un estado más alto que lo
     previsto vuelve a empujar. Si alguna vez pasa, desborda hacia arriba sobre
     el fieltro, que no mueve nada. */
  /* El contenedor es el que flota; las dos barras que cuelgan de el se apilan
     solas. Antes la posicion la tenia `.actionbar` y `.buybar` quedaba abajo en
     el flujo, o sea tapada. */
  /* Los insets van ACÁ y no le alcanza el padding de `#tableView` (#1701): un
     `absolute` se posiciona contra el PADDING BOX de su contenedor, o sea que el
     padding no lo corre ni un pixel. En apaisado —que es como se juega una mesa
     en el teléfono— el notch cae de un costado, justo sobre foldear/igualar. */
  .actionwrap { position: absolute; z-index: 30;
    right: calc(var(--safe-right) + 10px); bottom: calc(var(--safe-bottom) + 10px);
    display: flex; flex-direction: column; justify-content: flex-end; gap: 8px; }
  .actionbar { display: flex; flex-direction: column; justify-content: flex-end; gap: 6px; }

  /* La fila principal: foldear / pasar-igualar / apostar-subir, uno al lado del
     otro y del mismo tamaño, que es como se leen de un vistazo. */
  .actionbar .acts { display: flex; gap: 6px; }
  .actionbar .acts button { flex: 1 1 0; min-height: 48px; font-size: 15px;
    font-weight: 700; border-radius: 12px;
    background: linear-gradient(180deg, rgba(24,28,31,.97), rgba(12,15,17,.99));
    box-shadow: 0 6px 16px rgba(0,0,0,.5); }
  /* Foldear en rojo y SIEMPRE a la izquierda: es la unica de las tres que no se
     puede deshacer.

     Un color por accion, y uno solo verde. El verde es la accion agresiva
     (apostar/subir); pasar o igualar quedan neutros. Si los tres fueran del
     mismo color no habria nada que mirar, y con dos verdes se aprieta el que
     no era. La regla lleva `.acts` a proposito: `.actionbar .acts button` le
     gana por especificidad a `button.primary`, asi que sin esto el verde se
     perdia — un fondo declarado despues no "cae" al anterior, lo pisa. */
  .actionbar .acts button.fold { color: var(--red); border-color: rgba(var(--red-rgb),.35); }
  .actionbar .acts button.primary { background: #1e9a6c; border-color: #1e9a6c;
    color: #fff; }

  /* Los tamaños de apuesta, arriba: pastillas chicas que no compiten con la
     fila principal. */
  .actionbar .presets { display: flex; gap: 6px; }
  .actionbar .presets button { flex: 1 1 0; min-height: 32px; font-size: 12px;
    font-weight: 600; border-radius: 999px; color: var(--muted);
    background: rgba(12,15,17,.92); box-shadow: 0 4px 12px rgba(0,0,0,.4); }
  .actionbar .presets button:hover { color: var(--ink); }
  .actionbar .presets.pre button.armed { background: rgba(var(--amber-rgb),.18);
    border-color: rgba(var(--amber-rgb),.5); color: var(--amber); font-weight: 700; }
  /* Issue #1629 — EN EL TELEFONO EL RIEL SE LLEVA SU PROPIO RENGLON.
     Medido: con los dos pasos y el campo numerico en la misma fila, el riel de
     un 393px bajaba de 252px utiles a 137, y eso se comia la mitad de la mejora
     de sensibilidad que este issue vino a traer — 4px pasaban a mover 0,75 BB
     en vez de 0,39. En dos renglones el riel recupera el ancho completo y los
     pasos quedan igual de grandes. Escritorio lo vuelve a una sola fila. */
  .actionbar .raise { display: flex; gap: 6px; align-items: center; flex-wrap: wrap;
    padding: 5px 8px; border-radius: 999px; background: rgba(12,15,17,.92);
    box-shadow: 0 4px 12px rgba(0,0,0,.4); }
  /* El deslizador se dibuja ENTERO acá, en vez de dejárselo a `accent-color`.
     Con el nativo el relleno termina en el CENTRO del pulgar: medido, en el
     máximo quedaban 11px de riel sin llenar y en el mínimo 2px antes del
     pulgar. Se lee como si todavía quedara recorrido cuando ya no queda —
     justo cuando apretás All-in, que es cuando menos margen de duda hay.
     El relleno llega al BORDE DERECHO del pulgar y no a su centro: en el mínimo
     se ve sólo el pulgar pegado a la izquierda, y en el máximo la barra entera
     llena de punta a punta. `--pct` lo pone el cliente en cada cambio. */
  /* Issue #1629 — el pulgar de 15px a 30.
     Quince pixeles es la mitad de lo que piden Apple y Google para algo que se
     toca con el dedo (44). Con un pulgar chico hay que acertarle primero y
     despues cualquier temblor mueve el valor. El riel se dibuja mas fino que el
     pulgar para que el control no engorde toda la barra. */
  .actionbar .raise input[type=range] { flex: 1 1 100%; order: -1; min-width: 60px;
    --pulgar: 30px; --pct: 0;
    --relleno: calc(var(--pct) * (100% - var(--pulgar)) + var(--pulgar));
    -webkit-appearance: none; appearance: none; height: var(--pulgar);
    /* Sin relleno ni borde: los hereda del estilo general de los campos, y acá
       hacen doble daño. El degradado arranca en el borde del PADDING, así que
       los 10px de costado corrían el relleno 11px hacia adentro; y como la caja
       es `border-box`, esos 7px de arriba y abajo le comen el recorrido al
       pulgar dentro de una caja de 15px de alto. */
    padding: 0; border: 0; margin: 0;
    /* Verde como el pulgar y como el resto de la barra de apostar: es control
       de MESA, y la mesa se quedo en verde a proposito (#1106). Con el relleno
       en oro y el pulgar en verde, la barra tenia dos colores para una cosa. */
    /* #1629 — el riel se pinta en una banda de 10px centrada, no en los 30 de
       alto del control: el alto es el area de toque del pulgar, no el grosor de
       la barra. Las dos capas se cortan con la misma cuenta de `--relleno`. */
    border-radius: 999px; background:
      linear-gradient(to right, var(--green) var(--relleno), rgba(255,255,255,.16) var(--relleno))
      center / 100% 10px no-repeat; }
  .actionbar .raise input[type=range]::-webkit-slider-thumb {
    -webkit-appearance: none; width: var(--pulgar); height: var(--pulgar);
    border-radius: 50%; background: var(--green);
    /* El anillo oscuro es lo que lo despega del relleno: sin él, con la barra
       llena el pulgar desaparece adentro del verde y no se ve dónde está. */
    box-shadow: 0 0 0 2px rgba(8,11,13,.85); }
  .actionbar .raise input[type=range]::-moz-range-thumb {
    width: var(--pulgar); height: var(--pulgar); border: 0;
    border-radius: 50%; background: var(--green); }
  /* Issue #1629 — el campo se mide por su CONTENIDO, y sin el spinner nativo.
     REPORTADO con captura: "en escritorio el numero se ve cortado, en vez de
     1000 el cero de la derecha no se alcanza a ver entero". Medido a 1280x900:
     la caja quedaba en 63px y el contenido pedia 68.
     Dos causas. El ancho estaba en px y el bloque de escritorio lo bajaba a
     `7vmin`, que en una ventana de 900 de alto son 63. Y el `appearance: auto`
     de un `input[type=number]` reserva lugar para las flechitas del navegador,
     que desde que estan los pasos de una ciega no las usa nadie.
     El ancho va en `ch` para que siga a la tipografia: seis digitos es el monto
     mas largo que puede aparecer (un all-in de 100.000 fichas). */
  .actionbar .raise input[type=number] { width: clamp(72px, 19vmin, 104px);
    text-align: center; background: rgba(255,255,255,.06);
    font-variant-numeric: tabular-nums;
    -webkit-appearance: none; appearance: none; margin: 0; }
  .actionbar .raise input[type=number]::-webkit-outer-spin-button,
  .actionbar .raise input[type=number]::-webkit-inner-spin-button {
    -webkit-appearance: none; margin: 0; }
  /* Issue #1629 — los pasos de una ciega. Cuadrados de 44 para que se puedan
     tocar, y en verde apagado: son del control de apostar, no una jugada. */
  .actionbar .raise .raise-paso { flex: 0 0 auto; width: 44px; min-height: 44px;
    padding: 0; font-size: 20px; font-weight: 700; line-height: 1;
    border-radius: 12px; color: var(--green);
    border: 1px solid rgba(var(--green-rgb),.4); background: rgba(var(--green-rgb),.1);
    -webkit-user-select: none; user-select: none; touch-action: manipulation; }
  .actionbar .raise .raise-paso:hover { border-color: rgba(var(--green-rgb),.9); }
  .actionbar .raise .raise-paso:active { transform: translateY(1px); }
  /* Issue #1629 — en una VENTANA BAJA el riel vuelve a compartir renglon.
     El renglon propio le suma 34px de alto a la barra, y `scripts/e2e/
     mesa-quieta.js` lo caza: a 860x660 la barra pasaba a 146px de alto y tapaba
     la placa de un rival. Ahi el recurso escaso es el alto, no el ancho, y son
     ventanas de escritorio —no telefonos, que son altos— asi que los pasos se
     aprietan con el mouse igual. El riel queda mas angosto; el numero exacto
     sigue estando a un clic de los pasos, que es la mitad del arreglo. */
  /* Issue #1629 — EN UNA VENTANA BAJA los pasos se van y el riel vuelve a la
     fila del campo, o sea al layout de siempre.
     No es una preferencia: no entra. `scripts/e2e/mesa-quieta.js` mide que la
     barra no tape una placa ocupada, y ahi el alto es el recurso escaso. Con el
     riel en su propio renglon la fila `raise` medía 78px, y achicando las piezas
     todo lo razonable bajó a 70 y después a 66 — contra los 44 que tiene hoy.
     A 760x620 seguía tapando a un rival.
     Y meter los pasos en la misma fila que el riel y el campo tampoco sirve:
     medido, el riel quedaba en sus 60px de mínimo, inservible.
     Asi que en ventana baja el arreglo lo hace el MAPEO solo, que es la mitad
     que no cuesta pixeles. Medido a 860x660, con la barra en los mismos 112px
     de alto que tiene hoy: 4px de arrastre alrededor de 10 BB mueven 1,25 BB
     contra los 3,2 de antes, y 10 BB pasa de caer a 13px del minimo a caer a
     27. Es 2,6x, menos que el 6,4x del telefono, pero sin gastar un pixel de
     alto. Y el numero exacto sigue estando en el campo de al lado, que en una
     ventana de escritorio se escribe con el teclado que ya está ahí. */
  @media (max-height: 700px) {
    .actionbar .raise { flex-wrap: nowrap; }
    .actionbar .raise input[type=range] { flex: 1 1 auto; order: 0; }
    .actionbar .raise .raise-paso { display: none; }
    .actionbar .raise input[type=number] { width: clamp(68px, 10vmin, 84px);
      padding-left: 4px; padding-right: 4px; }
  }
  /* El boton de tiempo extra: celeste, distinto de todo lo demas de la barra.
     No es una jugada — no compite con foldear ni con apostar. */
  /* Chico y AZUL, primero de la fila. No se reparte el ancho con los botones de
     jugada: es un pedido de tiempo, no una jugada, y con el mismo tamaño se
     apretaba sin querer. */
  .actionbar .acts .timebank-btn { flex: 0 0 auto; min-width: 52px; min-height: 48px;
    padding: 0 10px; font-size: 13px; font-weight: 700; border-radius: 12px;
    color: #6fb4ff; border-color: rgba(111,180,255,.4); background: rgba(111,180,255,.12); }
  .actionbar .acts .timebank-btn:hover { background: rgba(111,180,255,.2); }
  .actionbar .timebank-btn { min-height: 30px; font-size: 12px; font-weight: 700;
    border-radius: 999px; color: #7fd1ff; background: rgba(12,15,17,.92);
    border-color: rgba(127,209,255,.35); box-shadow: 0 4px 12px rgba(0,0,0,.4); }
  .actionbar .sitout-inline { min-height: 32px; font-size: 12px; border-radius: 999px;
    color: var(--muted); background: rgba(12,15,17,.92); }
  .actionbar .waiting { text-align: center; color: var(--muted); font-size: 13px;
    padding: 10px 14px; border-radius: 999px; background: rgba(12,15,17,.86); }
  /* Issue #1682 — "se te desarmó el pagar". Ambar y no gris: es lo mismo que el
     aviso de turno, algo que pasó y hay que registrar antes de decidir de nuevo.
     Verde no, que acá es plata y paño (#1106). */
  .actionbar .waiting.roto { color: var(--amber); background: rgba(var(--amber-rgb),.12); }
  /* Cualquier boton suelto de la barra (el "Volver a jugar" de estar afuera):
     que flote igual que los demas y no quede como un boton pelado. */
  .actionbar > button { min-height: 42px; font-size: 14px; font-weight: 600;
    border-radius: 12px; background: linear-gradient(180deg, rgba(24,28,31,.97), rgba(12,15,17,.99));
    box-shadow: 0 6px 16px rgba(0,0,0,.5); }
  .actionbar > button.primary { background: #1e9a6c; }

  /* En escritorio no van a todo el ancho: 48px de alto y todo el ancho estan
     pensados para el pulgar, y con un mouse se ven gigantes. Se compactan abajo
     a la derecha, que es donde esta la mano que hace clic.

     El ancho se mide contra la VENTANA (no un maximo fijo): con 380px fijos, en
     una ventana angosta la barra llegaba hasta el medio y se comia al hero. */
  @media (min-width: 720px) {
    /* SE ACHICA CON LA VENTANA. Antes todo esto era px fijo y la barra quedaba
       congelada en 320x153 mientras el tapete bajaba de 1420 a 839 de ancho:
       medido, pasaba de comerse el 4% del fieltro al 12%, y desde los 1100px de
       ventana ya tapaba un asiento OCUPADO.
       Va en `vmin` y no en `cqmin` porque la barra cuelga de #tableView, que no
       es un contenedor de consulta — y además lo que se achica es la ventana,
       que es lo que `vmin` mide. Cada valor lleva su PISO en px: una barra que
       encoge sin límite deja de poder apretarse, que es peor que taparse. */
    .actionwrap {
      width: clamp(220px, 34vmin, 320px);
      margin-left: auto;
      align-self: flex-end;
      /* Despegada del borde, como del lado derecho. Pegada abajo se le corta la
         sombra y queda como si la pantalla la hubiera cortado. En el telefono
         sigue pegada abajo a proposito: ahi cae el pulgar.
         El inset va acá también (#1701): esta regla pisa la de más arriba, y un
         teléfono apaisado entra por este `@media` — el notch y la barra de
         gestos caen justo sobre foldear/igualar. */
      bottom: calc(var(--safe-bottom) + 10px);
    }
    /* El INTERLINEADO y el relleno también escalan, y son la mitad del asunto:
       al angostarse la barra el texto se parte en dos renglones —"Igualar 0,5
       BB", y el "All-in / 199,5 BB" de los tamaños ya viene partido a propósito—
       y con el interlineado de siempre esas dos líneas hacían crecer la barra
       justo cuando había menos lugar. Medido a 860x660: las tres filas eran
       46+47+49px y la barra terminaba 37px encima de un asiento ocupado.
       Se controla el alto de la línea en vez de prohibir el corte: cortar el
       monto del botón deja al que decide sin el número que está por pagar. */
    .actionbar { gap: clamp(4px, 0.6vmin, 6px); }
    .actionbar .acts button { min-height: clamp(32px, 4.2vmin, 44px);
      font-size: clamp(11px, 1.4vmin, 15px);
      line-height: 1.06; padding: clamp(3px, 0.5vmin, 6px) clamp(5px, 0.8vmin, 10px); }
    .actionbar .acts .timebank-btn { min-height: clamp(32px, 4.2vmin, 44px); min-width: clamp(42px, 5vmin, 52px); }
    .actionbar .presets button { min-height: clamp(22px, 2.8vmin, 30px);
      font-size: clamp(10px, 1.2vmin, 13px);
      line-height: 1.04; padding: clamp(2px, 0.4vmin, 5px) clamp(5px, 0.8vmin, 10px); }
    .actionbar .raise { padding: clamp(3px, 0.5vmin, 5px) clamp(6px, 1vmin, 8px); gap: clamp(4px, 0.6vmin, 6px); }
    /* Issue #1629 — el PISO sube de 52px a 72.
       REPORTADO con captura: "en escritorio el numero se ve cortado, en vez de
       1000 el cero de la derecha no se alcanza a ver entero". Medido a 1280x900,
       `clamp(52px, 7vmin, 76px)` daba 63px y el contenido pedia 68.
       El problema no era el `vmin` —la barra TIENE que ceder cuando la ventana
       se achica, y hay un test que lo cuida— sino que el piso estaba por debajo
       de lo que mide el monto mas largo que puede aparecer: seis digitos, o sea
       un all-in de 100.000 fichas, que con esta tipografia son 50px mas el
       relleno. Con 72 de piso entra en cualquier ventana. */
    .actionbar .raise input[type=number] { width: clamp(72px, 9vmin, 96px);
      padding-left: 6px; padding-right: 6px; }
    /* #1629 — los pasos no bajan de 40: son lo que se aprieta cuando el riel no
       alcanza, asi que encogerlos seria sacarle el arreglo justo al telefono,
       que es donde el reclamo era peor. */
    .actionbar .raise .raise-paso { width: clamp(40px, 6vmin, 44px); min-height: clamp(40px, 6vmin, 44px);
      font-size: clamp(16px, 2.4vmin, 20px); }
    /* En escritorio el riel TAMBIEN se lleva su renglon, y por el mismo motivo:
       la barra de accion es una columna angosta (`.actionwrap` la clampea en
       320px), asi que meter los dos pasos y el campo en la misma fila le dejaba
       143px de los 222 que tenia — medido, 4px pasaban a mover 1,25 BB en vez
       de 0,25. Lo que si cambia es el tamano de los pasos: con el mouse no hace
       falta un blanco de 44px. */
    .actionbar .raise .raise-paso { width: 34px; min-height: 34px; font-size: 17px; }
    .buybar button { min-height: 36px; font-size: 12px; }
    /* El cartel de "esperando tu turno" no necesita ocupar una barra entera. */
    .actionbar .waiting { padding: 8px 14px; font-size: 12px; }
  }

  /* --- compras (rebuy / add-on / re-entry) ---
     Van arriba de la barra de accion, no adentro: comprar no es una jugada, y
     mezclarlas con Foldear/Subir es como se aprieta la equivocada. */
  .buybar { display: flex; gap: 8px; flex-wrap: wrap; }
  .buybar:empty { display: none; }
  .buybar button { flex: 1 1 0; min-height: 42px; font-size: 13px; font-weight: 600;
    background: rgba(var(--amber-rgb),.12); border-color: rgba(var(--amber-rgb),.45); color: var(--amber); }
  .buybar button:disabled { background: rgba(255,255,255,.03);
    border-color: var(--line); color: var(--muted); }
  .buybar button b { display: block; font-size: 11px; font-weight: 500; opacity: .8; }

  /* --- chat ---
     Colapsado por defecto: en un telefono la mesa manda, y un panel siempre
     abierto le come el alto al tapete. */
  /* Botones chicos que flotan sobre el tapete, abajo a la izquierda, apilados
     sobre el chat para no taparlo. */
  /* Los controles que se usan una vez cada mucho: abajo a la izquierda, fuera
     del camino. Arriba competian con lo que se mira todo el tiempo. */
  /* Abajo del todo, rozando el borde: son los controles que se usan una vez
     cada mucho, así que cuanto más lejos del centro de la mesa, mejor.
     Y ESCALAN con la ventana. Con tamaño fijo, al achicar la ventana la fila se
     partía en dos y terminaba trepando sobre el tapete y sobre los botones de
     acción — justo lo que no puede pasar mientras se juega. */
  .floatbar { position: fixed; z-index: 31;
    left: calc(var(--safe-left) + 10px); bottom: calc(var(--safe-bottom) + 8px);
    display: flex; gap: .5vmin; flex-wrap: nowrap; max-width: 52vw; }
  .floatbtn { z-index: 31;
    /* El piso es 11px: abajo de eso no se lee en un teléfono, y `vmin` en una
       pantalla de 375px da 5. El techo sigue siendo 12 para que en escritorio
       se achiquen con la ventana, que es para lo que se puso el clamp. */
    font-size: clamp(11px, 1.35vmin, 12px);
    padding: clamp(6px, .75vmin, 7px) clamp(10px, 1.5vmin, 13px);
    border-radius: 999px;
    color: var(--muted); background: rgba(12,15,17,.92);
    box-shadow: 0 4px 12px rgba(0,0,0,.45); }
  .floatbtn:hover { color: var(--ink); }
  .floatbtn.primary { color: #fff; background: #1e9a6c; border-color: #1e9a6c; }
  /* --- reproductor de manos --- */
  .rp-bar { height: 4px; border-radius: 999px; background: rgba(255,255,255,.07);
    overflow: hidden; margin: 10px 0 12px; }
  .rp-bar i { display: block; height: 100%; width: calc(var(--t, 0) * 100%);
    background: rgba(255,255,255,.45); transition: width .15s linear; }
  .rp-controls { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
  .rp-controls button { min-width: 42px; font-size: 14px; }
  .rp-paso { font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums;
    min-width: 64px; }
  .rp-texto { font-size: 14px; font-weight: 600; }
  #replayView .stage { max-width: 720px; }

  /* --- chat: un cajon que entra desde la izquierda -----------------------
     Ocupa un cuarto de la ventana y deja ver la mesa entera al lado. */
  .chatdrawer { position: fixed; left: 0; top: 0; bottom: 0; z-index: 42;
    width: min(380px, 26vw); min-width: 280px;
    /* El inset va en el contenedor y no en cada hijo: la cabecera con el botón
       de cerrar es lo primero que tapa el notch. */
    padding-top: var(--safe-top); padding-left: var(--safe-left);
    display: flex; flex-direction: column;
    background: linear-gradient(180deg, #101514 0%, #0a0d0d 60%, #080a0a 100%);
    border-right: 1px solid var(--line-strong);
    box-shadow: 26px 0 70px rgba(0,0,0,.6), inset -1px 0 0 rgba(255,255,255,.03);
    animation: entrarIzq .22s cubic-bezier(.2,.8,.3,1); }
  .chatdrawer[hidden] { display: none; }
  @keyframes entrarIzq { from { transform: translateX(-100%); opacity: .6; } }

  /* Issue #1700 — en un teléfono el cajón de la izquierda no dejaba jugar.
     MEDIDO con Playwright: en 393x851 ocupaba 280px de ancho —el 71% de la
     pantalla, y los 113px que sobraban no alcanzan para ver el bote— y de paso
     tapaba el 72% de la barra de acción (76% en un iPhone SE). O sea que el
     comentario de index.html sobre este componente —"se sigue jugando con el
     chat abierto, que es para lo que sirve un chat en una mesa"— era cierto en
     escritorio y falso justo donde el club juega de noche.

     En vertical pasa a ser una hoja de ancho completo anclada ARRIBA de los
     controles. Los dos números salen de medir, no de elegirlos:

       - `bottom` lo pone chat.js con la posición real del primer control, así
         que la barra de acción queda siempre alcanzable — incluso cuando crece
         al abrir el riel de subir. El 30dvh del fallback es lo que miden hoy
         esos controles (252px de 851 y 209 de 667).
       - `height` es lo que queda entre el borde de abajo del bote y ese primer
         control: 295px en el Android y 199 en el SE. Un número fijo no sirve —
         con 30dvh el iPhone SE le tapaba el bote por 2px, y el bote es JUSTO el
         dato que se mira mientras se habla.

     No lleva scrim, por lo mismo que no lo llevaba el cajón. */
  @media (max-width: 700px) and (orientation: portrait) {
    .chatdrawer {
      left: 0; right: 0; top: auto; bottom: var(--hoja-abajo, 30dvh);
      width: auto; min-width: 0;
      /* La hoja no toca el borde de arriba ni el de la izquierda, así que los
         insets de área segura que el cajón de escritorio SÍ necesita (#1701)
         acá sobran: dejan una banda vacía del alto del notch ADENTRO de la
         hoja, medido en 60px en un iPhone con Dynamic Island. */
      padding-top: 0; padding-left: 0;
      height: min(var(--hoja-alto, 30dvh), 300px);
      /* Con el teclado arriba la hoja se apoya sobre él, así que el tope evita
         que se vaya por arriba de la pantalla. */
      max-height: calc(100dvh - var(--hoja-abajo, 0px) - 8px);
      border-right: 0; border-top: 1px solid var(--line-strong);
      border-radius: 16px 16px 0 0;
      box-shadow: 0 -18px 50px rgba(0,0,0,.55), inset 0 1px 0 rgba(255,255,255,.04);
      animation: entrarAbajo .22s cubic-bezier(.2,.8,.3,1); }

    /* La hoja flota sobre el tapete, no toca el borde de la pantalla: acá el
       `env()` de más abajo sumaría un hueco contra nada. */
    .chat-form { padding-bottom: 12px; }

    /* Elegir un sticker sí puede tapar el bote: dura dos segundos y mientras
       pasa nadie está leyendo el board. Pero la conversación no puede quedar en
       cero — con la hoja compacta, el alto es un recurso escaso. */
    .chatdrawer:has(#stickerPanel:not([hidden])) { height: min(56dvh, 480px); }
    .chatdrawer .stickers { max-height: 26dvh; overflow-y: auto; }
    .chat-log { min-height: 64px; }
  }
  @keyframes entrarAbajo { from { transform: translateY(100%); opacity: .6; } }

  .chat-head { display: flex; align-items: center; gap: 10px; flex: none;
    padding: 14px 14px 12px;
    border-bottom: 1px solid var(--line);
    background: linear-gradient(180deg, rgba(73,197,142,.07), transparent); }
  .chat-head strong { font-size: 15px; letter-spacing: .01em; }
  .chat-sub { flex: 1; font-size: 11px; color: var(--muted); }
  .chat-head button { font-size: 15px; line-height: 1; padding: 4px 8px; }

  /* El log crece y se lleva todo el alto que sobra: el campo de texto queda
     siempre abajo, sin importar cuantos mensajes haya. */
  .chat-log { flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain;
    display: flex; flex-direction: column; gap: 9px; padding: 14px 13px; }
  .chat-log .msg { display: flex; flex-direction: column; gap: 3px; max-width: 88%; }
  .chat-log .msg .who { font-size: 10px; letter-spacing: .06em; text-transform: uppercase;
    color: var(--muted); padding: 0 3px; }
  .chat-log .msg .bubble { padding: 8px 11px; border-radius: 13px; font-size: 13px;
    line-height: 1.35; word-break: break-word;
    background: rgba(255,255,255,.055); border: 1px solid rgba(255,255,255,.05);
    border-bottom-left-radius: 4px; }
  /* Lo tuyo va del otro lado y en verde: es como se lee un chat sin buscar el
     nombre en cada linea. */
  .chat-log .msg.mine { align-self: flex-end; align-items: flex-end; }
  .chat-log .msg.mine .bubble { background: rgba(30,154,108,.22);
    border-color: rgba(30,154,108,.35); border-bottom-left-radius: 13px;
    border-bottom-right-radius: 4px; }
  .chat-log .empty-chat { margin: auto; color: var(--muted); font-size: 12px;
    text-align: center; padding: 20px 10px; }

  /* --- stickers ---
     Una solapa por grupo (una carpeta = un grupo) y una grilla de 2x4, que es
     el tamaño de tanda que se carga por grupo. Va ENCIMA del formulario y no
     flotando sobre el log: abrirlo no tiene que tapar lo que se dijo. */
  /* Issue #1432 — el punto de "hay stickers nuevos".
     El cajón está detrás de un botón que no invita a nada, así que un sticker
     agregado no existe hasta que alguien lo abre de casualidad. El punto va
     PEGADO al botón y no en el chat entero: lo nuevo está adentro de ese cajón
     y de ningún otro lado. Ámbar, del tamaño de un acento: es un "mirá", no un
     reclamo — nadie pierde fichas por no abrirlo. */
  #stickerToggle { position: relative; }
  #stickerToggle.hay-nuevos::after {
    content: ""; position: absolute; top: 3px; right: 3px;
    width: 7px; height: 7px; border-radius: 50%;
    background: var(--amber); box-shadow: 0 0 0 2px var(--panel); }

  .stickers { flex: none; border-top: 1px solid var(--line); background: rgba(0,0,0,.3); }
  .sticker-tabs { display: flex; gap: 4px; padding: 8px 10px 0; overflow-x: auto;
    scrollbar-width: none; }
  .sticker-tabs::-webkit-scrollbar { display: none; }
  .sticker-tab { flex: none; border: 0; border-radius: 999px; padding: 5px 11px;
    font-size: 11px; letter-spacing: .02em; background: transparent; color: var(--muted); }
  .sticker-tab.activa { background: rgba(255,255,255,.12); color: var(--ink);
    font-weight: 600; }
  .sticker-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 6px;
    padding: 8px 10px 10px; }
  .sticker-btn { padding: 4px; border: 0; border-radius: 10px; background: transparent;
    aspect-ratio: 1; display: grid; place-items: center; }
  .sticker-btn:hover { background: rgba(255,255,255,.07); }
  /* `width`, no `max-width`: un tope deja que una imagen chica se dibuje a su
     tamaño intrínseco, así que una de 64px quedaba a 64 y una de 256 llenaba la
     celda — medido, la misma grilla mostraba cuadros de 32, 48 y 60px. El
     archivo lo carga el club como le sale del generador; lo que se ve no puede
     depender de eso. Y `contain` sobre celda cuadrada por si entra una que no
     lo es: se acomoda adentro en vez de deformarse. */
  .sticker-btn img { width: 100%; height: 100%; object-fit: contain; }
  /* El sticker en la conversación NO va en burbuja: la imagen es el mensaje. */
  /* Cuadrado fijo: el ancho ya no dependía del archivo, pero el alto sí, así
     que una imagen que no fuera cuadrada movía el renglón de la conversación. */
  .chat-log .msg .sticker { width: 130px; max-width: 100%; aspect-ratio: 1;
    object-fit: contain; border-radius: 10px; }
  /* Salvo que la imagen no haya llegado: ahí queda el nombre, y en itálica para
     que se note que es el reemplazo y no lo que el otro mandó. */
  .chat-log .msg .bubble.sticker-roto { font-style: italic; opacity: .7; }

  /* Issue #1441 — la solapa de gifs, adentro del mismo panel que los stickers.

     La grilla se rompe a UNA columna cuando está en modo gifs: los stickers son
     cuadrados de 256 y entran cuatro, pero un gif viene apaisado (medido: 220x110
     y 220x125 en respuestas reales) y en cuatro columnas quedaría de 58px de
     ancho dentro del cajón, que en un teléfono es ilegible. */
  .sticker-grid.es-gifs { grid-template-columns: 1fr; gap: 8px; }
  .gif-buscar { display: flex; }
  .gif-buscar input { width: 100%; border: 1px solid var(--line); border-radius: 999px;
    padding: 7px 12px; font-size: 13px; background: rgba(0,0,0,.35); color: var(--ink); }
  .gif-buscar input::placeholder { color: var(--muted); }
  /* Dos columnas para los resultados: es el punto donde un gif apaisado todavía
     se entiende y siguen entrando varios sin tener que scrollear tanto. */
  /* `grid-auto-rows` y `align-content` NO son decorativos acá: con la altura
     acotada, un grid reparte el alto sobrante entre sus filas, así que las de
     abajo se comprimían y los botones se APILABAN UNO ENCIMA DE OTRO — medido,
     botones de 89px de alto con las filas arrancando cada 18px. Se veía casi
     bien y el de arriba se comía el click del de abajo. */
  .gif-grilla { display: grid; grid-template-columns: repeat(2, 1fr); gap: 6px;
    grid-auto-rows: max-content; align-content: start;
    max-height: 210px; overflow-y: auto; }
  .gif-btn { padding: 0; border: 0; border-radius: 8px; background: rgba(255,255,255,.04);
    aspect-ratio: 16 / 10; overflow: hidden; }
  .gif-btn:hover { background: rgba(255,255,255,.1); }
  /* `cover` y no `contain`: acá la celda es el marco y lo que importa es que la
     grilla se vea pareja. En el mensaje sí se respeta la forma del gif. */
  .gif-btn img { width: 100%; height: 100%; object-fit: cover; display: block; }
  .gif-pie { font-size: 11px; color: var(--muted); text-align: center; padding-top: 2px; }
  .gif-mas { border: 0; border-radius: 999px; padding: 4px 12px; font-size: 11px;
    background: rgba(255,255,255,.1); color: var(--ink); }

  /* El gif en la conversación. A diferencia del sticker NO se fuerza cuadrado:
     un gif apaisado recortado a cuadrado pierde justo lo que lo hace gracioso.
     El alto se acota para que uno muy alto no empuje la conversación entera. */
  .chat-log .msg .gif { width: 100%; max-width: 200px; max-height: 160px;
    object-fit: contain; border-radius: 10px; display: block; }

  .chat-form { flex: none; display: flex; gap: 8px; padding: 12px 13px;
    padding-bottom: calc(var(--safe-bottom) + 12px);
    border-top: 1px solid var(--line); background: rgba(0,0,0,.25); }
  .chat-form input { flex: 1; border-radius: 999px; padding: 9px 14px;
    background: rgba(255,255,255,.05); }
  .chat-form button { border-radius: 999px; width: 40px; flex: none; font-size: 14px; }
  .floatbtn.unread { color: var(--amber); border-color: rgba(var(--amber-rgb),.5); }
  /* Un interruptor prendido tiene que verse prendido de lejos: el tilde solo se
     pierde entre los demás botones flotantes, y de este depende que te
     recompren sin preguntarte. */
  .floatbtn.active { color: var(--ink); border-color: var(--line-strong);
    background: rgba(255,255,255,.10); }
  .chat-form input { flex: 1; min-width: 0; }

  /* Panel del torneo: posiciones y escalera de premios. Jugando no habia forma
     de saber a cuantos se paga ni cuanto, que es JUSTO lo que decide como jugar
     en la burbuja. */
  /* Flota sobre el tapete y entra desde abajo. Metido en el flujo empujaba la
     mesa hacia arriba al abrirse — y con la mesa a proporcion fija, empujarla
     significa ACHICARLA: se abria el panel y la mano se hacia mas chica. */
  .tinfo { position: fixed; left: 50%; bottom: calc(var(--safe-bottom) + 12px);
    transform: translateX(-50%);
    z-index: 44; width: min(760px, calc(100vw - 24px)); max-height: 62dvh;
    overflow-y: auto;
    display: grid; gap: 12px; padding: 14px; border: 1px solid var(--line);
    border-radius: 14px;
    /* Semitransparente y con desenfoque: el panel tapa media mesa, y mientras
       se lee las posiciones la mano sigue. Ver el tapete por atrás es lo que
       permite consultarlo sin perderse lo que pasa. El desenfoque mantiene el
       texto legible sobre lo que sea que haya debajo. */
    background: linear-gradient(180deg, rgba(16,21,20,.82) 0%, rgba(10,13,13,.86) 100%);
    backdrop-filter: blur(14px) saturate(1.2);
    -webkit-backdrop-filter: blur(14px) saturate(1.2);
    box-shadow: 0 -10px 40px rgba(0,0,0,.55); }
  .tinfo h3 { margin: 0 0 6px; font-size: 11px; letter-spacing: .1em;
    text-transform: uppercase; color: var(--muted); font-weight: 700; }
  .tinfo-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 14px; }
  .stand-row { display: flex; align-items: center; gap: 8px; padding: 5px 8px;
    border-radius: 8px; font-size: 12px; }
  .stand-row.me { background: rgba(255,255,255,.08); }
  .stand-row.out { opacity: .45; }
  /* Menos apagado si el eliminado sos vos: tu propio puesto es justo lo que
     estas buscando en esa lista. */
  .stand-row.out.me { opacity: .8; }
  .stand-row .pos { width: 22px; color: var(--muted); font-variant-numeric: tabular-nums; }
  .stand-row .nm { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .stand-row .st { font-variant-numeric: tabular-nums; }
  .stand-row.paid .pos { color: var(--amber); font-weight: 700; }
  .prize-row { display: flex; justify-content: space-between; gap: 10px;
    padding: 4px 8px; font-size: 12px; border-radius: 8px; }
  .prize-row.me { background: rgba(255,255,255,.08); }
  .prize-row b { color: var(--amber); font-variant-numeric: tabular-nums; }
  /* Cartel de reconexion: sin esto la mesa se congela y no hay forma de saber
     si se corto la conexion o si simplemente no es tu turno. */
  .offline { display: flex; align-items: center; justify-content: center; gap: 10px;
    padding: 10px 14px; border-radius: 12px; font-size: 13px;
    background: rgba(var(--amber-rgb),.14); color: var(--amber);
    box-shadow: inset 0 0 0 1px rgba(var(--amber-rgb),.35); }
  .offline .spin { width: 13px; height: 13px; border-radius: 50%; flex: none;
    border: 2px solid rgba(var(--amber-rgb),.35); border-top-color: var(--amber);
    animation: girar .8s linear infinite; }
  @keyframes girar { to { transform: rotate(360deg); } }
  /* En la mesa la pantalla es la mesa: el titulo, la barra de sesion y el log
     de debug solo comen alto util. */
  /* Ventana emergente: se abrio para UNA cosa. El header del club ahi solo
     ocupa lugar y ofrece salidas que no vienen al caso. */
  body.popup > h1, body.popup > #topbar { display: none; }
  body.popup { padding-top: 14px; }
  body.at-table { padding: 0; }
  body.at-table > h1, body.at-table > #topbar { display: none; }
  /* El fin del torneo: es el final de dos horas, así que el número va grande. */
  .fin-card { max-width: 380px; }
  /* Issue #1805 — el cierre del Campeones.
     El mismo cartel, con el oro del club encima: es el torneo del mes y se
     juega una vez. No cambia la estructura —el podio, el premio y tu línea
     siguen donde estaban— porque lo que hace falta es que se note cuál torneo
     terminó, no volver a aprender dónde mirar. */
  .fin-campeones .fin-card {
    border-color: rgba(var(--oro-rgb),.45);
    background-image:
      radial-gradient(130% 80% at 50% 0%, rgba(var(--oro-rgb),.16) 0%, transparent 62%);
    box-shadow: 0 0 0 1px rgba(var(--oro-rgb),.22), 0 18px 48px rgba(0,0,0,.6); }
  .fin-campeones #finTitle { color: var(--oro); }
  .fin-campeones .fin-num { color: var(--oro); }
  .fin-num { margin: 2px 0 4px; font-size: 34px; font-weight: 800; letter-spacing: -.03em;
    color: var(--oro); font-variant-numeric: tabular-nums; text-align: center; }
  .fin-podio { display: grid; gap: 2px; margin-bottom: 4px; }
  .fin-podio .stand-row { padding: 7px 10px; border-radius: 9px; }

  .toast { position: fixed; left: 50%; bottom: 96px; transform: translateX(-50%);
    z-index: 40; padding: 9px 14px; border-radius: 10px; font-size: 13px;
    background: rgba(var(--red-rgb),.16); color: #ffbcc6;
    box-shadow: inset 0 0 0 1px rgba(var(--red-rgb),.4); }
  /* Issue #1515 — lo que pasó en la mano.
     ARRIBA y al medio: el cartel de error vive abajo, y abajo está la placa del
     héroe — el aviso le caía encima justo cuando está mirando su stack.
     OPACO y en oro: el de error es rojo y translúcido, que sobre el fieltro no
     se lee y además dice "algo salió mal". Cazar una cabeza no es un fallo. */
  .aviso-mesa { position: fixed; left: 50%; top: 84px; transform: translateX(-50%);
    z-index: 41; padding: 9px 16px; border-radius: 999px;
    font-size: 13px; font-weight: 700; letter-spacing: .01em;
    background: #0d0d0d; color: var(--oro); font-variant-numeric: tabular-nums;
    box-shadow: inset 0 0 0 1px rgba(var(--oro-rgb),.45), 0 6px 22px rgba(0,0,0,.55); }
  /* --- solapas del admin --- */
  /* --- capas flotantes ---
     Un panel que se mete en el flujo empuja todo lo de abajo y te hace perder
     el lugar donde estabas. Los paneles FLOTAN sobre la pagina y se cierran con
     Escape o tocando afuera. */
  /* El navegador bloquea window.open que no venga de un clic, asi que la
     apertura automatica al entrar puede fallar. En vez de no pasar nada, se
     ofrece el boton — que si es un clic. */
  /* Con cuanto sentarse. Antes salia del campo de arriba sin preguntar, asi que
     se entraba con el monto que hubiera quedado de la vez anterior. */
  .buyin-modal { position: fixed; inset: 0; z-index: 50; display: grid; place-items: center;
    background: rgba(0,0,0,.6); backdrop-filter: blur(3px); animation: aparecer .16s ease-out; }
  .buyin-card { width: min(360px, 92vw); display: grid; gap: 12px; padding: 22px;
    border-radius: 18px; border: 1px solid var(--line);
    background: linear-gradient(180deg, #101614, #0a0c0c);
    box-shadow: 0 30px 80px rgba(0,0,0,.7); }

  /* Issue #1540 — la mano recién jugada, encima del tapete.
     Misma capa que el cartel de fin de torneo, con una tarjeta mucho más ancha:
     adentro va un tapete, y en 360px las cartas no se leen. El panel que se
     dibuja acá adentro es el de "Mis manos", prestado (ver /reproductor.js).
     Mismo z-index que el cartel de fin de torneo y ANTES en el HTML: si el
     torneo termina mientras mirás una mano, el resultado va arriba. */
  .replay-modal { position: fixed; inset: 0; z-index: 50; display: grid; place-items: center;
    background: rgba(0,0,0,.66); backdrop-filter: blur(3px); animation: aparecer .16s ease-out; }
  .replay-card { width: min(760px, 94vw); max-height: 92vh; overflow-y: auto;
    padding: 18px; border-radius: 18px; border: 1px solid var(--line);
    background: linear-gradient(180deg, #101614, #0a0c0c);
    box-shadow: 0 30px 80px rgba(0,0,0,.7); }
  /* Más bajo que en "Mis manos": ahí el panel es la pantalla entera, acá abajo
     tienen que entrar los controles en un teléfono apaisado. */
  .replay-card .manos-rp .stage { max-height: 52vh; }
  .buyin-card h3 { margin: 0; font-size: 18px; }
  .buyin-card p { margin: 0; font-size: 12px; }
  .buyin-card label { display: flex; flex-direction: column; gap: 5px; font-size: 12px; color: var(--muted); }
  .buyin-card input { padding: 11px; font-size: 16px; text-align: center; }
  .buyin-card .chips { justify-content: space-between; }
  .buyin-card .chips button { flex: 1; }
  .buyin-acts { display: flex; gap: 8px; margin-top: 4px; }
  .buyin-acts button { flex: 1; min-height: 42px; }
  .popup-warn { position: fixed; left: 50%; bottom: calc(var(--safe-bottom) + 20px);
    transform: translateX(-50%);
    z-index: 45; display: flex; align-items: center; gap: 12px;
    padding: 12px 14px; border-radius: 14px; font-size: 13px;
    background: linear-gradient(180deg, #14201c, #0d1412); color: var(--ink);
    box-shadow: 0 0 0 1px var(--line-strong), 0 18px 50px rgba(0,0,0,.6); }
  /* Issue #1402 — el panel de editar un torneo.
     Flota centrado y no en un cajón lateral: son doce campos que se leen de
     corrido, y varios están bloqueados con su motivo al lado — eso se lee mal
     en una columna angosta. */
  .tedit { position: fixed; z-index: 46; top: 50%; left: 50%;
    transform: translate(-50%, -50%);
    width: min(680px, 94vw); max-height: 88vh; overflow-y: auto;
    display: flex; flex-direction: column; gap: 14px; padding: 20px;
    border-radius: 18px; background: var(--panel);
    border: 1px solid var(--line-strong);
    box-shadow: 0 30px 80px rgba(0,0,0,.7); }
  .tedit-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
  .tedit-head h3 { margin: 0; font-size: 17px; }
  .tedit-campos { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 12px; }
  .tedit-campos label { display: flex; flex-direction: column; gap: 4px; font-size: 12px; }
  .tedit-campos small { font-size: 11px; color: var(--muted); }
  /* Un campo bloqueado se ve apagado PERO su motivo se lee normal: el motivo es
     justamente lo que hay que poder leer. Sin esto, el "hay 3 anotados que ya
     pagaron" quedaba tan gris como el campo que explica. */
  /* Por `:disabled` y no por `input`: el % de premiados (#1630) es un
     <select>, y enumerando etiquetas quedaba bloqueado sin verse apagado.
     Lo que se pinta es la condición, así que el próximo control la hereda. */
  .tedit-campos label.bloqueado :disabled { opacity: .45; cursor: not-allowed; }
  .tedit-campos label.bloqueado small { color: var(--amber); opacity: 1; }
  .tedit-vaciar { padding: 12px 14px; border-radius: 12px;
    border: 1px solid rgba(var(--amber-rgb), .35);
    background: rgba(var(--amber-rgb), .07); }
  .tedit-vaciar p { margin: 0 0 10px; font-size: 12px; color: var(--muted); }
  .tedit-entradas { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; padding: 12px 14px;
    border-radius: 12px; border: 1px solid var(--line); }
  .tedit-entradas b, .tedit-entradas small { flex-basis: 100%; }
  .tedit-entradas small { font-size: 12px; color: var(--muted); }
  .tedit-entradas .tag button { min-height: 0; padding: 0 4px; border: 0; background: none; color: inherit; }
  .tedit-pie { display: flex; justify-content: flex-end; gap: 10px; }
  /* Issue #1414 — en el TELÉFONO este aviso no va.
     REPORTADO: "un jugador de celular que está en los 2 torneos no puede jugar
     en ambos". El cartel flota pegado al borde de abajo, que en vertical es
     exactamente donde vive la barra de acción. Medido a 375x667: el aviso
     ocupaba 506-647 y los botones 498-657 — o sea que enterarte de la OTRA mesa
     te tapaba Foldear, Igualar y Subir de ÉSTA.
     Y no se pierde nada: el aviso existe para el caso de escritorio ("abrila en
     una ventana"), y en el teléfono no se abren ventanas — `abrirMesas` navega
     en la misma página. La tira de arriba dice lo mismo, con el reloj de cada
     mesa, y no se va de pantalla: medido en el mismo teléfono, queda en y=129,
     visible y tocable, y la página no scrollea.
     El breakpoint es EL MISMO que usa `esTelefono()` en index.html
     (`max-width: 700px`), a propósito: si los dos no coincidieran habría un
     rango de anchos donde el cliente navega en la misma página —o sea, sin
     ventana que abrir— y el cartel igual se dibujaría. */
  @media (max-width: 700px) {
    .popup-warn { display: none; }
  }
  /* --- Issue #1472: el cajero -------------------------------------------- */
  /* El contador de pendientes cuelga del botón del header. Va en el botón y no
     en un cartel: el admin no está mirando el cajero cuando alguien carga. */
  #cajeroBtn { position: relative; }
  #cajeroPend { position: absolute; top: 0; right: 2px;
    min-width: 15px; padding: 0 3px; border-radius: 8px;
    background: var(--red); color: #1a0b0e;
    font-size: 10px; line-height: 15px; text-align: center; }
  /* El popup reusa `.scrim` de fondo pero con su propio z-index: se abre por
     encima del drawer de Ajustes, que también es una capa. */
  .cajero-fondo { z-index: 46; display: flex; align-items: center; justify-content: center; }
  /* #1478 — el diálogo de pedir un texto. Mismo molde que el del cajero, con un
     z-index más alto: se abre DESDE adentro de otros paneles (la cola del
     cajero, la vista de un torneo) y tiene que quedar encima. */
  .pedir-fondo { z-index: 48; display: flex; align-items: center; justify-content: center; }
  .pedir-card { width: min(380px, 92vw); display: flex; flex-direction: column; gap: 12px;
    padding: 20px; border-radius: 18px; background: var(--panel);
    border: 1px solid var(--line-strong); box-shadow: 0 30px 80px rgba(0,0,0,.7); }
  .pedir-card h3 { margin: 0; font-size: 17px; }
  .pedir-card label { display: flex; flex-direction: column; gap: 4px; font-size: 12px; }
  .pedir-acts { display: flex; justify-content: flex-end; gap: 10px; }
  .cajero-card { width: min(380px, 92vw); display: flex; flex-direction: column; gap: 12px;
    padding: 20px; border-radius: 18px; background: var(--panel);
    border: 1px solid var(--line-strong); box-shadow: 0 30px 80px rgba(0,0,0,.7); }
  .cajero-card h3 { margin: 0; font-size: 17px; }
  .cajero-card label { display: flex; flex-direction: column; gap: 4px; font-size: 12px; }
  .cajero-card p { margin: 0; font-size: 12px; }
  /* El alias es lo que el jugador va a COPIAR para transferir: se lee grande y
     se puede seleccionar aunque el resto de la app no. */
  .cajero-alias { display: flex; flex-direction: column; gap: 2px;
    padding: 10px 12px; border-radius: 12px;
    border: 1px solid rgba(var(--oro-rgb), .35); background: rgba(var(--oro-rgb), .07);
    color: var(--muted); }
  .cajero-alias b { font-size: 16px; color: var(--oro-claro); user-select: all; word-break: break-all; }
  .cajero-acts { display: flex; justify-content: flex-end; gap: 10px; }
  .cj { display: flex; flex-direction: column; gap: 12px; }
  .cj-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
  .cj-head h2 { margin: 0; }
  .cj h3 { margin: 6px 0 0; font-size: 13px; color: var(--muted); font-weight: 600; }
  .cj-fila { display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
    padding: 10px 12px; border-radius: 12px;
    border: 1px solid var(--line); background: rgba(255,255,255,.02); }
  .cj-cuando { font-size: 11px; color: var(--muted); min-width: 92px; }
  .cj-que { flex: 1 1 auto; }
  .cj-monto { font-variant-numeric: tabular-nums; font-weight: 600; }
  .cj-monto.up { color: var(--green); }
  .cj-monto.down { color: var(--red); }
  .cj-destino { font-size: 12px; color: var(--muted); word-break: break-all; }
  /* El estado es la columna que se barre con la vista, así que va como pastilla
     y con color: "pendiente" y "aprobado" en el mismo gris se leen igual. */
  .cj-estado { padding: 2px 9px; border-radius: 999px; font-size: 11px;
    border: 1px solid var(--line-strong); color: var(--muted); }
  .cj-estado.pendiente { border-color: rgba(var(--amber-rgb), .45);
    background: var(--amber-soft); color: var(--amber); }
  .cj-estado.aprobado { border-color: var(--line-strong); color: var(--ink); }
  .cj-estado.rechazado { border-color: rgba(var(--red-rgb), .45);
    background: rgba(var(--red-rgb), .10); color: var(--red); }
  /* El motivo del rechazo ocupa el renglón entero: es una frase, no un dato. */
  .cj-motivo { flex-basis: 100%; font-size: 12px; color: var(--muted); }
  .cj-cola { display: flex; flex-direction: column; gap: 8px;
    padding: 12px 14px; border-radius: 14px;
    border: 1px solid rgba(var(--amber-rgb), .35); background: rgba(var(--amber-rgb), .05); }
  .cj-cola h3 { margin: 0; }
  .cj-aliasrow { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; font-size: 12px; color: var(--muted); }
  .cj-aliasrow b { color: var(--ink); }
  .cj-pend { background: rgba(0,0,0,.25); }
  .scrim { position: fixed; inset: 0; z-index: 40; background: rgba(0,0,0,.55);
    backdrop-filter: blur(2px); animation: aparecer .16s ease-out; }
  @keyframes aparecer { from { opacity: 0 } }
  .drawer { position: fixed; top: 0; right: 0; bottom: 0; z-index: 41;
    width: min(400px, 92vw); display: flex; flex-direction: column; gap: 14px;
    padding: calc(var(--safe-top) + 18px) calc(var(--safe-right) + 18px)
             calc(var(--safe-bottom) + 18px) 18px;
    overflow-y: auto;
    background: var(--panel);
    border-left: 1px solid var(--line);
    box-shadow: -20px 0 60px rgba(0,0,0,.6);
    animation: entrar .2s cubic-bezier(.2,.8,.3,1); }
  @keyframes entrar { from { transform: translateX(100%) } }
  .drawer-head { display: flex; align-items: center; gap: 12px; }
  .drawer-head strong { font-size: 16px; flex: 1; }
  .drawer-head button { font-size: 16px; line-height: 1; padding: 4px 8px; }

  /* --- lobby en dos columnas ---
     La columna de detalle esta SIEMPRE reservada: al elegir un torneo la lista
     no se reacomoda ni cambia de ancho. */
  .lobby-shell { display: grid; grid-template-columns: minmax(0,1fr) 340px; gap: 18px; align-items: start; }
  .lobby-main { min-width: 0; }
  .lobby-side { position: sticky; top: 12px; border: 1px solid var(--line);
    border-radius: 16px; background: var(--panel);
    padding: 16px; max-height: calc(100dvh - 24px); overflow-y: auto; }
  .side-empty { display: grid; place-items: center; gap: 10px; padding: 40px 10px;
    text-align: center; color: var(--muted); font-size: 12px; }
  .side-mark { font-size: 26px; opacity: .35; }
  /* Issue #1647 — con el resumen del club adentro, el panel deja de estar
     centrado como un cartel y se lee como una ficha: alineado arriba y a la
     izquierda, igual que el detalle que lo reemplaza al elegir algo. */
  .side-empty.con-datos { display: block; place-items: initial; padding: 16px 18px;
    text-align: left; color: inherit; }
  .club-vistazo h3 { margin: 0 0 12px; font-size: 11px; letter-spacing: .1em;
    text-transform: uppercase; color: var(--muted); }
  .club-dato { padding: 10px 0; border-top: 1px solid var(--line); }
  .club-dato:first-of-type { border-top: 0; padding-top: 0; }
  .club-dato span { display: block; font-size: 10px; letter-spacing: .09em;
    text-transform: uppercase; color: var(--muted); }
  .club-dato strong { display: block; margin-top: 3px; font-size: 18px;
    letter-spacing: -.015em; font-variant-numeric: tabular-nums; }
  .club-dato:first-of-type strong { color: var(--amber); }
  .club-dato em { display: block; margin-top: 2px; font-style: normal;
    font-size: 11px; color: var(--muted); }
  .side-empty p { margin: 0; }

  /* En angosto la columna no entra: pasa a ser un panel flotante. */
  @media (max-width: 900px) {
    .lobby-shell { grid-template-columns: 1fr; }
    .lobby-side { position: fixed; top: 0; right: 0; bottom: 0; z-index: 41;
      width: min(400px, 92vw); border-radius: 0; border-left: 1px solid var(--line);
      padding: calc(var(--safe-top) + 16px) calc(var(--safe-right) + 16px)
               calc(var(--safe-bottom) + 16px) 16px;
      max-height: none; box-shadow: -20px 0 60px rgba(0,0,0,.6);
      animation: entrar .2s cubic-bezier(.2,.8,.3,1); }
    .lobby-side[hidden] { display: none; }
  }

  /* --- vista en lista ---
     Markup propio, no una tarjeta reacomodada con CSS: forzar una tarjeta a
     fila deja los botones desparramados y nunca se ve compacto. */
  .cards.rows { display: flex; flex-direction: column; gap: 6px; }

  /* Los anchos viven en el CONTENEDOR, no en cada fila: es lo único que hace
     que la cabecera y las filas alineen. Con cada fila definiendo lo suyo, el
     título termina arriba de otra columna. Los pone lobby.js, que los lee de
     localStorage — acá van los de fábrica por si todavía no se pintó. */
  .cards.rows { --c0: 220px; --c1: 150px; --c2: 130px; --c3: 92px; --c4: 92px; --c5: 92px; }
  .rowhead, .rowcard {
    display: grid; align-items: center; gap: 4px 16px;
    /* La última columna NUNCA por debajo de sus botones. Con `minmax(0, 1fr)` se
       encogía hasta 194px con 318 de botones adentro y, como están alineados a
       la derecha, se desbordaban HACIA LA IZQUIERDA encima de "Anotados" y
       "Premio": los números quedaban tapados por los botones. */
    grid-template-columns: var(--c0) var(--c1) var(--c2) var(--c3) var(--c4) var(--c5)
      minmax(min-content, 1fr);
  }
  .rowhead { padding: 2px 16px 6px; font-size: 9px; letter-spacing: .09em;
    text-transform: uppercase; color: var(--muted); font-weight: 700; }
  /* Sin `overflow` acá: recortaba la agarradera, que se apoya SOBRE el borde y
     por lo tanto cae fuera de la caja. El recorte del título va en el span de
     adentro. */
  .rowhead .rh { position: relative; min-width: 0; display: flex; }
  .rowhead .rht { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .rowhead .rh.num { justify-content: flex-end; }
  /* La agarradera se apoya sobre la mitad del hueco entre columnas: así se
     agarra el BORDE y no hay que apuntarle a una línea de un píxel. */
  .rowhead .grip { position: absolute; top: -4px; right: -12px; width: 13px; bottom: -6px;
    cursor: col-resize; touch-action: none; }
  .rowhead .grip::after { content: ""; position: absolute; top: 2px; bottom: 2px; left: 6px;
    width: 1px; background: var(--line); }
  .rowhead .grip:hover::after { background: rgba(255,255,255,.5); }
  /* Arrastrando, el cursor no cambia al pasar sobre otra cosa y nada se
     selecciona: sin esto, arrastrar pinta de azul media pantalla. */
  body.resizing { cursor: col-resize; user-select: none; }

  .rowcard {
    padding: 11px 16px; border: 1px solid var(--line); border-radius: 12px;
    background: var(--panel); cursor: pointer;
    transition: border-color .15s, background .15s; }
  .rowcard:hover { border-color: var(--line-strong); background: #101514; }
  .rowcard.picked { border-color: var(--line-strong); }
  /* El nombre recorta; las etiquetas NO. Antes compartían la celda y el
     `ellipsis` se comía media etiqueta: "RANKING" quedaba en "R…" y un chip
     cortado a la mitad dice menos que ninguno. Ahora el nombre tiene su propia
     caja que se achica, y las etiquetas van enteras o no van. */
  .rowcard .rname { min-width: 0; font-weight: 650; font-size: 13px;
    display: flex; flex-wrap: wrap; align-items: center; column-gap: 7px; }
  .rowcard .rname .rn { min-width: 0; overflow: hidden; text-overflow: ellipsis;
    white-space: nowrap; }
  /* Cada celda RECORTA lo que no entra. Sin esto, "Inscripción abierta" se salía
     de su columna y quedaba encima del contador de la de al lado: se leían los
     dos textos superpuestos y no se entendía ninguno. */
  .rowcard .pill { max-width: 100%; overflow: hidden; text-overflow: ellipsis;
    white-space: nowrap; }
  .rowcard .rwhen, .rowcard .rnum { min-width: 0; overflow: hidden;
    text-overflow: ellipsis; white-space: nowrap; }
  .rowcard .rname small { flex-basis: 100%; font-weight: 500; font-size: 10px;
    letter-spacing: .06em; text-transform: uppercase; color: var(--muted); }
  /* La etiqueta va al lado del nombre, en la misma línea: es parte de cómo se
     llama el torneo, no un dato aparte. */
  .rowcard .rname .tag { flex: none; }

  /* Un KO o un PKO se juegan distinto, así que se ven distinto. El acento va en
     el borde izquierdo de la fila —un solo trazo— en vez de teñir el nombre:
     con veinte filas, el color en el texto es lo que vuelve ilegible una lista.
     Un regular no lleva nada: la marca es para lo que se sale de lo normal. */
  .rowcard.es-ko { border-left-color: rgba(var(--amber-rgb),.55); }
  .rowcard.es-pko { border-left-color: rgba(var(--red-rgb),.5); }
  .rowcard.es-ko .rname, .rowcard.es-pko .rname { padding-left: 3px; }
  .rowcard.es-ko, .rowcard.es-pko { border-left-width: 3px; }

  /* "Garantizado" debajo del número: mientras no se cubra, ese monto es una
     promesa del club y no lo que juntó la gente. Al superarlo desaparece solo,
     porque ya no explica nada. */
  .rowcard .rnum .gtd { display: block; font-size: 9px; font-weight: 700;
    letter-spacing: .07em; text-transform: uppercase; color: var(--muted); }
  .rowcard .rwhen small { display: block; font-size: 9px; letter-spacing: .06em;
    text-transform: uppercase; color: var(--muted); font-variant-numeric: normal; }
  /* Issue #1337 — el rotulo de la columna, adentro de la celda. Con cabecera
     sobra: "ENTRADA 5.000" repetido en veinte filas es la misma palabra veinte
     veces, que es justo lo que la cabecera vino a evitar. Se enciende abajo,
     donde la cabecera no entra. */
  .rowcard .rot { display: none; }
  .rowcard .rnum { text-align: right; font-variant-numeric: tabular-nums; font-size: 13px; }
  .rowcard .rnum span { display: block; font-size: 9px; letter-spacing: .07em;
    text-transform: uppercase; color: var(--muted); }
  .rowcard .rnum.prize { color: var(--amber); }
  .rowcard .rwhen { font-variant-numeric: tabular-nums; font-size: 13px; color: var(--muted); }
  .rowcard .rwhen b { display: block; overflow: hidden; text-overflow: ellipsis; }
  .rowcard .rwhen b { color: var(--ink); font-weight: 650; }
  .rowcard .racts { display: flex; gap: 6px; justify-content: flex-end; }
  .rowcard .racts button { padding: 6px 12px; font-size: 12px; white-space: nowrap; }
  /* Issue #1443 — los dos de admin, en icono.
     MEDIDO en la ventana de la app (fila de 882px): la botonera se llevaba 315px,
     el 36% de la fila, y las tres celdas de números quedaban con 186px para 223
     de contenido. O sea "Entrada 5.400" recortado a "Entrada 5.40…": el buy-in,
     que es el dato por el que se mira una lista de torneos.

     Los dos son de gestión, no del jugador: el que juega toca "Inscribirme" y
     nada más, y ésos dos aparecen sólo siendo admin. Poner el ícono acá y el
     texto en el `title` le devuelve 75px a las columnas de datos.

     El ✕ no es un botón peligroso sin aviso: cancelar ya pide confirmación con
     el nombre del torneo y cuántos inscriptos pierden la entrada. */
  .rowcard .racts .radmin { padding: 5px 10px; font-size: 16px; line-height: 1.15;
    color: var(--muted); }
  .rowcard .racts .radmin:hover { color: var(--ink); }
  /* Debajo de ~980px una fila de 7 columnas no entra: se apila en dos lineas
     en vez de encogerse hasta ser ilegible. */
  /* La lista se apila según el ancho QUE TIENE, no el de la ventana.
     Era un media query contra la ventana, y el ancho real de la lista es otro:
     la columna de detalle se lleva 340px fijos, así que en una ventana de
     1330px la lista tiene 950 y las siete columnas ya no entran — se cortaban
     contra el panel de detalle y se perdía la mitad de la información con la
     regla de apilado sin disparar. */
  /* El contenedor son los PANELES de cada lista y no `.lobby-main`: poner la
     contención en el hijo del grid le cambia cómo resuelve su propio ancho y
     desalineaba la cabecera de las filas. */
  #tourPane, #cashPane { container-type: inline-size; container-name: lista; }
  /* El corte va donde la grilla deja de entrar de verdad, no en un número
     redondo: seis columnas fijas (776px) más los huecos (96) más los botones de
     admin (318) son 1.190. Por debajo de eso la fila se apila. */
  /* Issue #1337 — sin lugar para siete columnas, DOS LINEAS por fila.
     No es un caso de borde: en la ventana de la app el contenedor de la lista
     mide ~880px —el panel de detalle se lleva el resto— asi que por debajo de
     1190px esta SIEMPRE, y esto es lo unico que se ve en escritorio y en la app.

     Antes era una degradacion: se escondia la cabecera, los siete bloques se
     apilaban de a dos por linea y los botones se llevaban una linea entera
     propia. La fila terminaba midiendo 164px y entraban tres torneos y medio en
     pantalla — una "lista" que ocupa mas que las tarjetas. Y como los valores
     nunca tuvieron etiqueta propia (se la daba la cabecera que ese mismo bloque
     esconde) quedaban numeros sueltos: un "2" y un "5.000" en el aire.

     El nombre arriba, los datos abajo con su rotulo, y los botones a la derecha
     ocupando las dos lineas: asi no se comen una para ellos solos. */
  @container lista (max-width: 1190px) {
    .rowhead { display: none; }
    .rowcard {
      /* La columna del reloj NUNCA por debajo de su contenido. Con `minmax(0,1fr)`
         se encogia hasta casi cero cuando las otras cuatro se comian el ancho, y
         como el reloj va alineado a la derecha se desbordaba HACIA LA IZQUIERDA
         encima del nombre: se leian los dos textos superpuestos, "STAN01:59:56D".
         Es el mismo bug que ya habia aparecido en la vista ancha con la columna
         de los botones, y el mismo arreglo. */
      grid-template-columns: auto auto auto auto minmax(min-content,1fr) auto;
      gap: 1px 10px; padding: 7px 12px; align-items: center;
    }
    /* El reloj sube a la linea del NOMBRE, pegado a la derecha. Abajo no entraba:
       con el estado y los tres numeros, "00:59:56 para arrancar" quedaba cortado
       en "PARA ARRA…" en todas las filas. Arriba sobra lugar — un nombre y dos
       etiquetas no llenan 880px — y ademas se lee como lo que es: cuando pasa
       algo con ESE torneo. */
    /* `overflow: hidden` no es decorativo: el ancho de las columnas 1..4 lo fija
       lo que hay ABAJO (el estado y los tres numeros), y un nombre mas largo que
       eso se desbordaba ENCIMA del reloj — se leian los dos textos superpuestos,
       "STAN01:59:56D". Recortar con puntos suspensivos es lo que ya hace la vista
       ancha con los nombres largos. */
    .rowcard .rname  { grid-column: 1 / 5; grid-row: 1; overflow: hidden; }
    .rowcard .rwhen  { grid-column: 5; grid-row: 1; justify-self: end; }
    .rowcard .rstate { grid-column: 1; grid-row: 2; }
    /* Sin columna fija: caen solos en 2, 3 y 4, en el orden del markup. */
    .rowcard .rnum   { grid-row: 2; }
    /* Las dos lineas, para que los botones no se lleven una propia. */
    .rowcard .racts  { grid-column: 6; grid-row: 1 / 3; justify-content: flex-end; }

    .rowcard .rnum, .rowcard .rwhen { text-align: left; }
    /* El rotulo al lado del numero y no encima: encima suma una linea por celda,
       que es de donde salia la mitad del alto. */
    .rowcard .rot { display: inline; margin-right: 4px; font-size: 9px;
      letter-spacing: .06em; text-transform: uppercase; color: var(--muted);
      font-variant-numeric: normal; }
    /* PEDIDO: "en vez de 00:59:56 para arrancar, para arrancar 00:59:56, así los
       relojes quedan todos en el mismo lugar".
       Con el reloj primero, el que se alinea a la derecha es el final de la
       ETIQUETA, y como "para arrancar" y "para abrir inscripción" miden distinto,
       cada reloj arrancaba en una x diferente y la columna se leía escalonada.
       Dado vuelta, lo que queda pegado al borde es el reloj: todos terminan en la
       misma x y se leen como una columna.
       Se invierte con `row-reverse` y no cambiando el orden del markup porque en
       la vista ancha estos dos van uno ARRIBA del otro, con el reloj primero. */
    .rowcard .rwhen { display: flex; flex-direction: row-reverse;
      align-items: baseline; gap: 5px; }
    .rowcard .rwhen small { display: inline; }
    /* Sin cabecera el rotulo ya dice "GARANTIZADO", asi que repetirlo abajo es
       la misma palabra dos veces en la celda mas angosta de la fila — y lo que
       hacia que el numero se cortara. Con cabecera pasa al reves: el rotulo esta
       tapado y este marcador es lo unico que lo dice, y ahi se queda. */
    .rowcard .rnum .gtd { display: none; }
    /* Estos dos son los que empujaban el alto y no se ven: `.rname small` tiene
       `flex-basis: 100%` y `.rwhen b` es `display: block`, asi que cada uno se
       lleva una linea propia adentro de su celda. En la vista ancha eso esta
       bien —hay lugar de sobra a lo alto— pero aca son dos lineas de regalo. */
    .rowcard .rname small { flex-basis: auto; margin-left: 4px; }
    .rowcard .rwhen b { display: inline; }
  }

  /* Y mas angosto todavia, los botones aprietan.
     Medido en un contenedor de 702px: las columnas quedaban en 59/59/59 cuando
     los numeros necesitaban 87/67/69 — faltaban ~46px y se cortaba la plata. De
     esos 702, los botones se llevaban 250 (el 36%).
     Se les baja el padding en vez de esconder un dato: achicar un boton no le
     saca informacion a nadie, y el unico candidato a desaparecer era el chip de
     estado, que igual dice algo que el reloj no dice. */
  @container lista (max-width: 780px) {
    .rowcard .racts button { padding-left: 8px; padding-right: 8px; font-size: 11px; }
    /* Y aca los botones SI se llevan una linea propia.
       Medido en un contenedor de 702px: de esos 702 los botones se llevaban 250
       —el 36%— y a los tres numeros les quedaban 59px cada uno cuando necesitaban
       87, 67 y 69. Lo que se cortaba era la plata.
       Con una linea aparte la fila sube a ~88px, pero no se esconde ni se corta
       nada, y sigue siendo la mitad de los 164px de antes. Arriba de 780 no hace
       falta: ahi entran los dos en la misma altura. */
    .rowcard .racts { grid-column: 1 / -1; grid-row: 3; justify-content: flex-start; }
  }

  /* Y por las dudas: nada se corta en silencio. Si algún ancho intermedio deja
     la grilla más ancha que su caja, se desliza en vez de perder columnas.
     Son DOS defensas independientes y a propósito: sacar cualquiera de las dos
     por separado no rompe ningún test —la otra tapa el síntoma—, pero sin las
     dos la lista vuelve a cortarse contra el panel de detalle. */
  .cards.rows { overflow-x: auto; }

  /* --- detalle --- */
  /* --- lobby de UN torneo ---
     Una ventana dedicada: quien la abre quiere ver ESE torneo, no el club. */
  /* Issue #1756 — la banda NO envuelve mientras haya ancho de escritorio.
     REPORTADO: "con el agregado de la gente que suele venir se rompió el lobby
     del torneo, se puso mucho más alto el banner y se bajó el reloj para abajo
     ... es una cuestión del tamaño de la ventana".
     `.tour-id` no tiene `flex-grow` —a propósito, ver abajo— así que su base es
     su ancho MAX-CONTENT, que es la fila de insignias entera en una línea. Y
     `flex-wrap` ENVUELVE ANTES DE ENCOGER: cuando esa base más el anillo no
     entran, el anillo se va al renglón de abajo en vez de apretarse, y la banda
     pasa de 270 a 442px. O sea que el ancho de la fila de insignias decidía
     dónde iba el reloj.
     Ya pasaba con tres insignias abajo de ~900px; la cuarta (#1718) corrió el
     umbral a ~1170, o sea adentro de una ventana de escritorio normal.
     Con `nowrap`, `.tour-id` encoge —ya tiene `min-width: 0`— y la fila de
     insignias envuelve ADENTRO, que es de lo que ya sabe hacer.
     El corte de 700 no es nuevo: es el mismo con el que `.tour-fondo` se apaga
     más abajo, donde la banda se apila a propósito.
     Y VA DESPUES de la regla base, no antes: una media query no suma
     especificidad, así que puesta arriba el `flex-wrap: wrap` de abajo le ganaba
     y el arreglo no hacía nada — probado, el guard siguió rojo igual. */
  .tour-hero { position: relative; overflow: hidden;
    display: flex; align-items: flex-start; gap: 18px; flex-wrap: wrap;
    padding: 20px 22px; margin-bottom: 16px; border: 1px solid var(--line);
    border-radius: 18px;
    background: linear-gradient(135deg, rgba(255,255,255,.05), transparent 55%), #0b0d0d; }
  @media (min-width: 701px) { .tour-hero { flex-wrap: nowrap; } }

  /* Issue #1647 — la lámina del torneo, de fondo.
     Antes la banda eran ~180px de negro. El arte se apoya a la DERECHA y se
     recorta con `cover`: la banda es mucho más ancha que alta (1380×180 contra
     un dibujo casi cuadrado), así que `contain` lo dejaría chico y flotando en
     el medio. Apoyado al costado, el lado izquierdo —donde va el nombre— queda
     limpio para el texto.
     El degradado NO es decoración: es lo que garantiza que el nombre y el
     anillo se lean. Va de opaco a transparente cruzando la banda, así el arte
     aparece justo donde no hay texto. */
  /* Las láminas anchas (1920×200) están dibujadas para ESTA caja, así que van
     enteras y nítidas: sin desenfoque y sin recorte vertical. El `cover` sólo
     recorta a los costados cuando la ventana es más angosta que 9,6:1, y eso
     cae justo en los bordes, que el degradado apaga.
     Nada de `blur` acá: se lo puse cuando el arte era un recorte de un dibujo
     casi cuadrado y cualquier encuadre lo partía. Dibujado apaisado, el
     desenfoque sólo tiraría a la basura el detalle. */
  .tour-fondo { position: absolute; inset: 0; width: 100%; height: 100%;
    object-fit: cover; object-position: center center; opacity: .55;
    /* Se apaga en los DOS extremos: los primeros 400px son el nombre y las
       etiquetas, los últimos 200 el anillo del reloj. El arte entra de lleno en
       el medio, que es donde la lámina tiene el foco. */
    -webkit-mask-image: linear-gradient(90deg, transparent 2%, #000 22%, #000 82%, transparent 98%);
    mask-image: linear-gradient(90deg, transparent 2%, #000 22%, #000 82%, transparent 98%);
    pointer-events: none; }
  /* En teléfono la banda se apila y queda casi cuadrada (0,93:1): ahí una
     lámina de 9,6:1 se vería como una tira fina en el medio. Se apaga y queda
     el ícono, que es el que identifica al torneo. */
  @media (max-width: 700px) { .tour-fondo { display: none; } }
  .tour-hero > *:not(.tour-fondo) { position: relative; }
  .tour-hero h2 { margin: 0 0 8px; font-size: 26px; letter-spacing: -.02em; }
  .tour-id { min-width: 0; display: flex; flex-direction: column; }
  /* El ícono: la misma lámina, chica y cuadrada, al lado del nombre. Es lo que
     le da identidad a la banda cuando el fondo queda tapado por el degradado. */
  .tour-icono { width: 56px; height: 56px; margin-bottom: 12px; border-radius: 14px;
    border: 1px solid rgba(var(--oro-rgb),.28); overflow: hidden; flex: none;
    background: radial-gradient(circle at 50% 30%, rgba(var(--oro-rgb),.14), #0b0d0d 70%);
    display: grid; place-items: center; }
  /* `contain` y no `cover`: acá el dibujo ENTERO es el dato — es lo que
     distingue un torneo de otro de un vistazo. Recortado deja de identificar. */
  .tour-icono img { width: 84%; height: 84%; object-fit: contain; }
  /* #1624 — el anillo ocupa el lugar que tenia el reloj: empujado a la derecha.
     Sin esto quedaba pegado al titulo con la mitad del hero vacia al lado.
     Lo hace el margen y NO un `flex: 1` en `.tour-id`: ensanchar esa columna le
     daba espacio libre al `margin-left: auto` que `.pill` trae de la linea 398,
     y las tres etiquetas se iban al otro extremo del hero. */
  /* #1654 — el `auto` va en #tourAnillo, que es el hijo flex del hero.
     En #1653 el anillo se mudó adentro de ese nodo para que el repintado por
     segundo no recreara las imágenes de la banda, y con eso `.anillo-wrap` dejó
     de ser hijo directo: su `margin-left: auto` ya no tenía contra qué empujar y
     el reloj se fue al medio de la banda.
     Las dos reglas: la de abajo para el panel de detalle del lobby, donde el
     anillo sigue colgando derecho del hero. */
  .tour-hero > #tourAnillo { margin-left: auto; }
  .tour-hero .anillo-wrap { margin: 0 0 0 auto; }
  /* Y por si alguien vuelve a ensanchar la columna: aca las etiquetas van
     pegadas al titulo, no empujadas. El `auto` de `.pill` es para las filas
     donde la pastilla ES lo de la derecha; esta no es una de esas. */
  .tour-tags .pill { margin-left: 0; }
  .tour-tags { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
  /* La descripción es del que armó el torneo: se lee como una línea suya, no
     como un dato más de la ficha técnica. */
  .tour-desc { max-width: 52ch; margin: 10px 0 0; font-size: 13px; line-height: 1.55;
    color: var(--muted); }
  .tour-when { margin-left: auto; text-align: right; }
  .tour-when b { display: block; font-size: 28px; font-variant-numeric: tabular-nums;
    letter-spacing: -.02em; }
  .tour-when.urgent b { color: var(--amber); }
  .tour-when small { font-size: 11px; color: var(--muted); }
  /* Los tres números que deciden si te anotás, arriba y en grande: en la lista
     de pares de antes pesaban lo mismo que "minutos por nivel". */
  /* #1624 — con tope de ancho: estirados a 1400px cada numero quedaba en una
     isla y la fila se leia como cuatro cosas sueltas, no como un bloque. El
     tope es por celda y no de la fila entera, asi que con tres o con cuatro
     numeros la fila igual termina donde tiene que terminar. */
  /* #1647 — sin `max-width`: las fichas cortaban a 1000px mientras el contenido
     de abajo llegaba a 1420, y esos 400px de aire eran la mitad del "hay
     espacios vacíos por todos lados". Ahora acompañan el ancho del resto. */
  .tour-big { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: 1px; margin-bottom: 16px; border-radius: 16px; overflow: hidden;
    background: var(--line); }
  .tour-big > div { padding: 14px 18px; background: #0c100f; }
  .tour-big span { display: block; margin-bottom: 4px; font-size: 10px; letter-spacing: .09em;
    text-transform: uppercase; color: var(--muted); }
  .tour-big strong { font-size: 24px; letter-spacing: -.02em; font-variant-numeric: tabular-nums; }
  .tour-big .prize strong { color: var(--amber); }
  .tour-big em { display: block; margin-top: 3px; font-style: normal; font-size: 11px;
    color: var(--muted); }
  .tour-big .prize em { color: var(--amber); opacity: .8; }

  /* Issue #1646 — de qué está hecho el pozo, debajo del número grande.
     Sin esto el jugador ve un pozo de 108.000 y una escalera que paga 72.360, y
     no tiene dónde entender la diferencia. En `--muted` y no en oro a propósito:
     el que manda es el total; esto lo explica, no compite con él. */
  .pozo-part { display: block; margin-top: 3px; font-style: normal;
    font-size: 11px; font-weight: 400; color: var(--muted);
    font-variant-numeric: tabular-nums; }
  /* El pozo de cabezas cierra la caja de premios, así que va separado de la
     escalera: lo de arriba se paga por puesto, esto se cobra mano a mano. */
  .prize-row.cabezas { margin-top: 6px; padding-top: 8px;
    border-top: 1px solid rgba(255,255,255,.08); }
  .prize-row.cabezas span { color: var(--muted); }

  /* El desglose de lo que pagás: los números alineados a la derecha, uno debajo
     del otro, para que se lean como una suma. */
  .desglose { display: grid; gap: 2px; margin-bottom: 10px; }
  .desglose-row { display: flex; justify-content: space-between; gap: 14px;
    padding: 7px 10px; border-radius: 9px; background: rgba(255,255,255,.03);
    font-size: 13px; color: var(--muted); }
  .desglose-row b { color: var(--ink); font-variant-numeric: tabular-nums; }

  /* Jugadores y re-entries: dos números distintos, no uno sumado. */
  .tour-cuenta { display: flex; gap: 18px; margin-top: 12px; padding-top: 12px;
    border-top: 1px solid var(--line); }
  .tour-cuenta span { display: block; font-size: 9px; letter-spacing: .08em;
    text-transform: uppercase; color: var(--muted); }
  .tour-cuenta b { font-size: 19px; font-variant-numeric: tabular-nums; letter-spacing: -.01em; }

  /* Cada compra se explica entera: cuánto sale arriba, y abajo qué te da. */
  .compra { padding: 10px 12px; border-radius: 11px; margin-bottom: 8px;
    border: 1px solid var(--line); background: rgba(255,255,255,.02); }
  .compra-top { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; }
  .compra-top b { font-size: 13px; }
  .compra-top span { font-size: 15px; font-weight: 700; color: var(--amber);
    font-variant-numeric: tabular-nums; }
  .compra p { margin: 4px 0 0; font-size: 11px; line-height: 1.5; color: var(--muted); }

  /* La tabla de posiciones con techo y scroll propio: con cien anotados empujaba
     todo lo de abajo fuera de la pantalla. */
  .tour-stand { max-height: 340px; overflow-y: auto; display: grid; gap: 1px;
    margin: -2px -4px; padding: 2px 4px; }
  /* #1684 — colgaba de `.tour-box`, que #1651 se llevó: la mesa elegida quedó sin
     resalte desde entonces, con el `.picked` puesto y nadie pintándolo. El ancestro
     nuevo es la hoja de la solapa. */
  .tour-hoja .stand-row.picked { background: rgba(255,255,255,.08);
    box-shadow: inset 0 0 0 1px var(--line-strong); }

  /* ------------------------------------------------------- TatoClub
     El salón de trofeos del club, y la única pantalla que se sale de la paleta
     verde del resto — negro y oro, porque acá no se juega: se mira quién ganó.

     Todo bajo `.tc` y con nombres propios (`tc-`) para que ese cambio de tono
     no se filtre a ninguna mesa. El oro va con un negro CÁLIDO: sobre un negro
     puro el dorado se ve amarillo barato, y la diferencia está en que el fondo
     tenga una pizca del mismo pigmento. */
  .tc {
    --oro: #d4af37;
    --oro-claro: #f2dc94;
    --oro-hondo: #6d5518;
    --plata: #c9ccd2;
    --bronce: #b57a3c;
    --tc-tinta: #f3efe4;      /* blanco cálido, no blanco puro */
    --tc-gris: #8c8578;       /* gris con sesgo al oro: un gris neutro se lee
                                 como "no elegido" en vez de "secundario" */
    --tc-linea: rgba(212,175,55,.16);
    --tc-panel: linear-gradient(180deg, #14120e 0%, #0b0a08 100%);
    color: var(--tc-tinta);
  }

  /* --- cabecera --- */
  .tc-head { display: flex; align-items: flex-end; gap: 18px; flex-wrap: wrap;
    padding-bottom: 14px; margin-bottom: 18px;
    border-bottom: 1px solid var(--tc-linea); }
  .tc-marca { display: block; font-size: 10px; letter-spacing: .34em;
    text-transform: uppercase; color: var(--oro); margin-bottom: 4px; }
  .tc-head h2 { margin: 0; font-size: 30px; letter-spacing: -.02em; font-weight: 700;
    text-transform: capitalize; }
  .tc-nav { margin-left: auto; display: flex; align-items: center; gap: 6px; }
  .tc-flecha { width: 32px; height: 32px; padding: 0; border-radius: 9px;
    font-size: 17px; line-height: 1; color: var(--oro);
    border-color: var(--tc-linea); background: rgba(212,175,55,.06); }
  .tc-flecha:hover { background: rgba(212,175,55,.16); }
  .tc #tcMes { width: 148px; border-color: var(--tc-linea); color: var(--tc-tinta);
    background: rgba(0,0,0,.4); }

  /* --- las cifras del mes ---
     Una tira fina arriba de todo: son el contexto, no el contenido. */
  .tc-cifras { display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
    gap: 1px; margin-bottom: 18px; border-radius: 14px; overflow: hidden;
    background: var(--tc-linea); }
  .tc-cifras > div { padding: 12px 16px; background: #0b0a08; }
  .tc-cifras span { display: block; margin-bottom: 3px; font-size: 9px; letter-spacing: .14em;
    text-transform: uppercase; color: var(--tc-gris); }
  .tc-cifras b { font-size: 20px; font-weight: 700; letter-spacing: -.02em;
    font-variant-numeric: tabular-nums; }
  .tc-cifras .oro b { color: var(--oro); }
  .tc-cifras em { display: block; margin-top: 2px; font-style: normal; font-size: 10px;
    color: var(--tc-gris); }

  /* --- el podio ---
     Abre la pantalla porque los tres primeros SON la noticia del mes: una tabla
     de veinte filas iguales hay que leerla entera para saber quién va ganando. */
  .tc-podio { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
    gap: 12px; margin-bottom: 18px; }
  .tc-puesto { display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
    padding: 16px 18px; border-radius: 16px; text-align: left;
    border: 1px solid var(--tc-linea); background: var(--tc-panel); }
  .tc-puesto:hover { border-color: rgba(212,175,55,.4); }
  .tc-medalla { font-size: 10px; letter-spacing: .16em; text-transform: uppercase;
    color: var(--tc-gris); }
  .tc-puesto .tc-nombre { font-size: 15px; font-weight: 650;
    max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .tc-puntos { margin-top: 4px; font-size: 30px; font-weight: 800; letter-spacing: -.035em;
    font-variant-numeric: tabular-nums; line-height: 1; }
  .tc-sub { font-size: 11px; color: var(--tc-gris); }

  /* El campeón: más grande, con el borde encendido y un halo apenas. Es el
     único lugar de la pantalla que se permite brillar. */
  .tc-puesto.oro { border-color: rgba(212,175,55,.55);
    background: linear-gradient(155deg, rgba(212,175,55,.14), transparent 62%), var(--tc-panel);
    box-shadow: 0 0 34px rgba(212,175,55,.09); }
  .tc-puesto.oro .tc-medalla { color: var(--oro); }
  .tc-puesto.oro .tc-puntos { color: var(--oro-claro); font-size: 38px; }
  .tc-puesto.plata { border-color: rgba(201,204,210,.3); }
  .tc-puesto.plata .tc-medalla, .tc-puesto.plata .tc-puntos { color: var(--plata); }
  .tc-puesto.bronce { border-color: rgba(181,122,60,.3); }
  .tc-puesto.bronce .tc-medalla, .tc-puesto.bronce .tc-puntos { color: var(--bronce); }
  .tc-puesto.vos { box-shadow: inset 0 0 0 1px rgba(212,175,55,.45); }

  /* --- las tres columnas --- */
  .tc-cols { display: grid; gap: 14px;
    grid-template-columns: repeat(auto-fit, minmax(270px, 1fr)); }
  .tc-panel { padding: 14px 16px 16px; border-radius: 16px;
    border: 1px solid var(--tc-linea); background: var(--tc-panel); }
  .tc-panel h3 { display: flex; align-items: baseline; gap: 8px; margin: 0 0 10px;
    font-size: 10px; letter-spacing: .18em; text-transform: uppercase; color: var(--oro);
    font-weight: 700; }
  .tc-panel h3 em { font-style: normal; letter-spacing: 0; color: var(--tc-gris);
    font-size: 11px; font-variant-numeric: tabular-nums; }
  /* Techo y scroll propio: con veinte jugadores y veinticinco torneos, sin esto
     la página se vuelve un rollo de tres metros. */
  .tc-scroll { max-height: 380px; overflow-y: auto; margin: 0 -6px; padding: 0 6px; }

  /* --- la tabla ---
     La barra es lo que deja ver de un vistazo quién está cerca: con números
     solos hay que restar de a pares. */
  /* La columna de insignias va entre el nombre y la barra: se leen junto a
     quién es, que es lo que significan. Ancho fijo para que las filas sin
     insignias no desalineen la barra de puntos. */
  .tc-fila { display: grid; align-items: center; gap: 10px; width: 100%;
    /* Sin la barra proporcional: sus 64px fijos le salían al nombre y a la línea
       de abajo, que en una ventana angosta quedaban en puntos suspensivos. */
    grid-template-columns: 22px minmax(0, 1fr) 74px 46px 26px;
    padding: 7px 8px; border: 0; border-radius: 9px; background: transparent;
    text-align: left; }
  .tc-fila:hover { background: rgba(212,175,55,.07); }
  .tc-fila.elegido { background: rgba(212,175,55,.13); }
  .tc-fila.vos { box-shadow: inset 0 0 0 1px rgba(212,175,55,.35); }
  .tc-pos { font-size: 11px; color: var(--tc-gris); font-variant-numeric: tabular-nums;
    text-align: right; }
  /* Nombre arriba y, para los seis que se llevan algo, qué se llevan abajo.
     El nombre va en su propio hijo porque el recorte con puntos suspensivos
     necesita `nowrap`, y eso aplicado al contenedor se comería la sub-línea. */
  .tc-quien { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
  .tc-quien > b { font-size: 13px; font-weight: 400; overflow: hidden;
    text-overflow: ellipsis; white-space: nowrap; }
  .tc-premio { font-style: normal; font-size: 10px; color: var(--tc-gris);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .tc-premio em { font-style: normal; font-weight: 700; color: var(--oro);
    font-variant-numeric: tabular-nums; }
  .tc-num { font-size: 14px; font-weight: 700; text-align: right;
    font-variant-numeric: tabular-nums; letter-spacing: -.01em; }
  .tc-jugados { font-size: 11px; color: var(--tc-gris); text-align: right;
    font-variant-numeric: tabular-nums; }

  /* --- los torneos ---
     La fecha a la izquierda como en un calendario: el día grande y el mes
     debajo, para poder barrer la columna sin leer. */
  .tc-torneo { display: grid; align-items: center; gap: 10px; width: 100%;
    grid-template-columns: 30px minmax(0, 1fr) 26px;
    padding: 7px 8px; border: 0; border-radius: 9px; background: transparent;
    text-align: left; }
  .tc-torneo:hover { background: rgba(212,175,55,.07); }
  .tc-torneo.elegido { background: rgba(212,175,55,.13); }
  .tc-fecha { font-size: 15px; font-weight: 700; line-height: 1; text-align: center;
    font-variant-numeric: tabular-nums; }
  .tc-fecha em { display: block; font-style: normal; font-size: 9px; font-weight: 500;
    letter-spacing: .08em; text-transform: uppercase; color: var(--tc-gris); }
  .tc-tnombre { font-size: 13px; min-width: 0; }
  .tc-tjug { font-size: 11px; color: var(--tc-gris); text-align: right;
    font-variant-numeric: tabular-nums; }
  /* Las marcas de origen. Sólo la de "externo" tiene color: lo jugado en el
     cliente es lo normal, y marcar lo normal es ruido en todas las filas. */
  .tc-tnombre i { display: inline-block; margin-left: 6px; padding: 1px 6px;
    border-radius: 999px; font-style: normal; font-size: 9px; font-weight: 700;
    letter-spacing: .07em; text-transform: uppercase; vertical-align: 1px; }
  .tc-marca-propio { color: var(--oro); background: rgba(212,175,55,.12); }
  .tc-marca-externo { color: var(--tc-gris); background: rgba(255,255,255,.05); }
  .tc-marca-presencial { color: #6fc3a0; background: rgba(111,195,160,.12); }
  .tc-marca-abierta { color: #e8913a; background: rgba(232,145,58,.14); }

  /* --- el detalle ---
     Un panel, no dos: lo que un ranking no puede decir es si esos 125 puntos
     fueron dos victorias o veinte medias tablas. */
  .tc-panel-detalle { position: sticky; top: 12px; align-self: start; }
  .tc-detalle h3 { margin: 0 0 10px; font-size: 17px; letter-spacing: -.015em;
    color: var(--tc-tinta); text-transform: none; }
  .tc-mini { display: flex; gap: 16px; padding-bottom: 12px; margin-bottom: 10px;
    border-bottom: 1px solid var(--tc-linea); }
  .tc-mini span { display: block; font-size: 9px; letter-spacing: .12em;
    text-transform: uppercase; color: var(--tc-gris); }
  .tc-mini b { font-size: 17px; font-variant-numeric: tabular-nums; letter-spacing: -.02em; }
  .tc-mini .oro b { color: var(--oro); }
  .tc-linea { display: grid; align-items: center; gap: 10px;
    grid-template-columns: 30px minmax(0, 1fr) auto auto;
    padding: 6px 8px; border-radius: 8px; font-size: 12px; }
  .tc-linea:nth-child(odd) { background: rgba(255,255,255,.02); }
  .tc-linea.vos { box-shadow: inset 0 0 0 1px rgba(212,175,55,.35); }
  .tc-lpos { font-size: 11px; color: var(--tc-gris); font-variant-numeric: tabular-nums;
    text-align: right; }
  /* Oro, plata y bronce donde haya un puesto: en la tabla y en las líneas del
     detalle, con los mismos tres colores que el podio. Del 4º para abajo queda
     gris a propósito — si todos los puestos tuvieran color, ninguno destacaría. */
  .tc-lpos.oro, .tc-pos.oro { color: var(--oro); font-weight: 700; }
  .tc-lpos.plata, .tc-pos.plata { color: var(--plata); font-weight: 700; }
  .tc-lpos.bronce, .tc-pos.bronce { color: var(--bronce); font-weight: 700; }
  .tc-lnombre { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .tc-lnum { font-variant-numeric: tabular-nums; color: var(--tc-gris); }
  /* El bounty va en su propia columna, a la izquierda del total: así los totales
     quedan alineados entre sí y se pueden comparar de arriba abajo. */
  .tc-lko { font-size: 10px; letter-spacing: .04em; white-space: nowrap;
    color: var(--bronce); font-variant-numeric: tabular-nums; }
  /* El día se cede primero: si el nombre es largo, que se coma esta columna. */
  .tc-linea.con-dia { grid-template-columns: 30px minmax(0, 1fr) auto auto auto; }
  .tc-tipo { display: inline-block; padding: 1px 5px; border-radius: 4px;
    font-style: normal; font-size: 9px; font-weight: 700; letter-spacing: .1em;
    color: #1a1409; background: var(--bronce); vertical-align: 1px; }
  .tc-tipo.pko { background: var(--oro-claro); }
  .tc-ldia { font-size: 10px; letter-spacing: .04em; color: var(--tc-gris);
    font-variant-numeric: tabular-nums; white-space: nowrap; opacity: .8; }
  .tc-detalle h3 em { margin-left: 8px; font-style: normal; font-size: 11px;
    letter-spacing: .1em; text-transform: uppercase; color: var(--tc-gris); }
  .tc-vacio { margin: 0; padding: 18px 0; font-size: 12px; color: var(--tc-gris);
    text-align: center; }

  @media (max-width: 700px) {
    .tc-head h2 { font-size: 24px; }
    .tc-puesto.oro .tc-puntos { font-size: 30px; }
    .tc-panel-detalle { position: static; }
    .tc-scroll { max-height: 300px; }
    /* Piso de 10px en los rótulos: las versalitas con `letter-spacing` grande
       se leen peor que su tamaño, y a 9px en un teléfono "PREMIOS" y "RANKING
       POT" quedaban al borde de lo ilegible. En escritorio el 9px se sostiene
       porque la pantalla está más lejos de ser el limitante. */
    .tc-cifras span, .tc-mini span { font-size: 10px; letter-spacing: .1em; }
  }

  .tour-acts { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 16px; }
  /* Issue #1624 — el anillo del torneo.
     Un `conic-gradient` recortado con `mask`: el arco es una variable, asi que
     se actualiza seteando `--f` y no rehaciendo el nodo. Eso no es un detalle
     de implementacion — las dos pantallas rehacen su innerHTML una vez por
     segundo, asi que el nodo es nuevo en cada tick y una transicion arrancaria
     de cero sesenta veces por minuto (ver la cabecera de /anillo.js). El arco
     avanza de a un segundo, que es lo que hace un reloj.
     En oro: el verde esta reservado a la plata y al pano (#1106). */
  .anillo { --f: 1; position: relative; display: grid; place-items: center;
    width: 132px; height: 132px; flex: none; }
  .anillo::before {
    content: ""; position: absolute; inset: 0; border-radius: 50%;
    background: conic-gradient(from -90deg,
      var(--oro) calc(var(--f) * 360deg), rgba(var(--oro-rgb),.13) 0);
    mask: radial-gradient(farthest-side, #0000 calc(100% - 7px), #000 calc(100% - 6px));
    -webkit-mask: radial-gradient(farthest-side, #0000 calc(100% - 7px), #000 calc(100% - 6px)); }
  /* Sin total conocido no hay arco que dibujar: queda el aro apagado y el
     numero. Un arco sobre un total inventado miente sobre cuanto falta. */
  .anillo.sin-arco::before { background: rgba(var(--oro-rgb),.2); }
  .anillo-in { display: grid; place-items: center; gap: 1px; text-align: center; }
  .anillo-val { font-size: 25px; font-weight: 800; letter-spacing: -.03em;
    font-variant-numeric: tabular-nums; line-height: 1.05; }
  .anillo-lab { font-size: 9.5px; letter-spacing: .07em; text-transform: uppercase;
    color: var(--muted); max-width: 96px; line-height: 1.25; }
  /* El subtitulo va DEBAJO del aro y EN EL FLUJO: adentro de 132px no entra
     "Registro tardio: nivel 6 (=60 min)", y colgado con `position: absolute`
     se salia del resumen del lobby —que mide 340px— y se encimaba con la fila
     de numeros. */
  .anillo-wrap { display: flex; flex-direction: column; align-items: center;
    gap: 8px; margin: 2px 0 14px; }
  .anillo-sub { max-width: 100%; text-align: center; line-height: 1.35;
    font-size: 11px; color: rgba(var(--oro-rgb),.85); }
  .anillo.urgent .anillo-val { color: var(--amber); }

  /* Issue #1624 — las cajas FLUYEN en columnas, no en una grilla.
     Con `display: grid` las cinco se estiraban TODAS al alto de la mas alta
     —"Premios" con una fila media lo mismo que "Compras" con dos bloques— y
     abajo quedaban ~400px de negro. Poner `align-items: start` arreglaba el
     estirado pero dejaba a la quinta caja sola en una segunda fila con un
     hueco al lado.
     `columns` las acomoda como una pared: cada una mide lo suyo, se llenan de
     arriba abajo y no quedan huerfanas. El orden de lectura pasa a ser por
     columna, que para tarjetas independientes da igual. */
  /* Issue #1647 — DOS paneles, no multicol.
     `columns: 330px` repartía siete cajas en columnas de igual altura cortando
     donde CSS quisiera: medido a 1440px, 192px de diferencia entre la columna
     más larga y la más corta, y ~127.000 px² de hueco. Con dos regiones
     declaradas el desparejo deja de ser posible. */
  .tour-paneles { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 14px; align-items: stretch; }
  @media (max-width: 860px) { .tour-paneles { grid-template-columns: minmax(0, 1fr); } }
  .tour-panel { display: flex; flex-direction: column; min-width: 0;
    border: 1px solid var(--line); border-radius: 16px; background: var(--panel);
    overflow: hidden; }
  /* Alto mínimo para que el panel más corto no se desinfle al lado del otro:
     una solapa con tres renglones no puede dejar media pantalla en blanco. */
  .tour-hoja { flex: 1; min-height: 260px; padding: 16px 18px; overflow: auto; }

  /* Las solapas. En oro, como todo lo que no es plata (#1106). */
  .tour-solapas { display: flex; gap: 2px; padding: 6px 6px 0; flex-wrap: wrap;
    border-bottom: 1px solid var(--line); background: rgba(255,255,255,.02); }
  .tour-solapa { padding: 9px 14px; border: 0; border-radius: 10px 10px 0 0;
    background: transparent; color: var(--muted); cursor: pointer;
    font: inherit; font-size: 11px; font-weight: 700; letter-spacing: .08em;
    text-transform: uppercase; white-space: nowrap; }
  .tour-solapa:hover { color: var(--ink); background: rgba(255,255,255,.04); }
  .tour-solapa.activa { color: var(--oro); background: var(--panel);
    box-shadow: inset 0 -2px 0 var(--oro); }
  .tour-solapa:focus-visible { outline: 2px solid var(--oro); outline-offset: -2px; }

  /* El detalle de la mesa elegida, dentro de la solapa "Mesas": separado de la
     lista por una línea, no metido en otra caja. */
  .tour-sub { margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--line); }
  .tour-sub h4 { margin: 0 0 8px; font-size: 11px; letter-spacing: .1em;
    text-transform: uppercase; color: var(--muted); }

  /* El detalle es la pantalla donde alguien DECIDE si se anota, y la decisión
     se toma con tres números. En una lista de definiciones pesaban lo mismo que
     "Repite: semanal", así que suben arriba y en grande; la ficha técnica queda
     abajo para el que la busca. */
  .det-hero { padding-bottom: 12px; margin-bottom: 12px; border-bottom: 1px solid var(--line); }
  .det h3 { margin: 0 0 6px; font-size: 19px; letter-spacing: -.015em; }
  .det .sub { display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
    margin: 0; font-size: 11px; color: var(--muted);
    letter-spacing: .06em; text-transform: uppercase; }

  .det-count { display: flex; align-items: baseline; gap: 8px; margin-bottom: 12px; }
  .det-count b { font-size: 26px; font-weight: 700; letter-spacing: -.02em;
    font-variant-numeric: tabular-nums; }
  .det-count small { font-size: 11px; color: var(--muted); }

  .det-big { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1px; margin-bottom: 12px;
    border-radius: 12px; overflow: hidden; background: var(--line); }
  .det-big div { padding: 10px 12px; background: #0c100f; }
  .det-big span { display: block; margin-bottom: 3px; font-size: 9px; letter-spacing: .08em;
    text-transform: uppercase; color: var(--muted); }
  .det-big strong { font-size: 16px; font-variant-numeric: tabular-nums; letter-spacing: -.01em; }
  .det-big .prize strong { color: var(--amber); }

  .det-note { margin: 0 0 12px; padding: 9px 12px; border-radius: 10px; font-size: 12px;
    line-height: 1.45; color: var(--muted);
    border: 1px solid var(--line); background: rgba(255,255,255,.02); }
  .det-note b { color: var(--ink); font-variant-numeric: tabular-nums; }
  /* El aviso de "entrás con tantas BB" es el que decide una inscripción tardía:
     se distingue de la ficha técnica de al lado. */
  .det-note.live { border-color: rgba(var(--oro-rgb),.35); background: rgba(var(--oro-rgb),.06); }
  .det-note.live b { color: var(--ink); }

  .det dd em { font-style: normal; color: var(--muted); }
  .det dl { display: grid; grid-template-columns: auto 1fr; gap: 7px 14px; margin: 0 0 14px;
    font-size: 12px; }
  .det dt { color: var(--muted); }
  .det dd { margin: 0; text-align: right; font-variant-numeric: tabular-nums; }
  .det .acts { display: flex; gap: 8px; flex-wrap: wrap; }
  .det .acts button { flex: 1 1 auto; }
  .tcard.picked { border-color: var(--line-strong); }
  /* --- Issue #1338: qué hay de nuevo ---------------------------------------
     Un cartel en el medio, por encima de todo. Se abre una sola vez por novedad
     y nunca con una mano en juego — eso lo decide /novedades.js, no el CSS.

     Alto acotado con scroll adentro: si un release trae seis novedades, el
     cartel no puede pasarse de la pantalla y dejar el botón de cerrar afuera. */
  .news { position: fixed; z-index: 60; left: 50%; top: 50%;
    transform: translate(-50%, -50%);
    width: min(520px, calc(100vw - 32px)); max-height: min(78vh, 720px);
    display: flex; flex-direction: column;
    border: 1px solid var(--line-strong); border-radius: 18px;
    background: linear-gradient(180deg, #0f1413, #0a0c0c);
    box-shadow: 0 24px 70px rgba(0,0,0,.65); }
  .news header { display: flex; align-items: center; gap: 10px;
    padding: 16px 18px 12px; border-bottom: 1px solid var(--line); }
  .news header h2 { flex: 1; margin: 0; font-size: 17px; letter-spacing: -.01em; }
  .news header button { border: 0; background: transparent; color: var(--muted);
    font-size: 15px; padding: 4px 8px; }
  .news header button:hover { color: var(--ink); }
  .news-body { flex: 1; min-height: 0; overflow-y: auto; padding: 4px 18px 0; }
  .news footer { padding: 12px 18px 16px; }
  .news footer button { width: 100%; padding: 11px; }
  .news-item { padding: 14px 0; border-bottom: 1px solid var(--line); }
  .news-item:last-child { border-bottom: 0; }
  .news-item h3 { margin: 0 0 5px; font-size: 14px; }
  .news-item p { margin: 0; font-size: 13px; line-height: 1.55; color: var(--muted); }
  /* Dónde encontrarlo: una novedad que no dice dónde se toca es un anuncio. */
  .news-donde { display: inline-block; margin-top: 7px; padding: 3px 8px;
    border-radius: 999px; font-size: 10px; letter-spacing: .06em;
    text-transform: uppercase; color: var(--oro);
    background: rgba(var(--oro-rgb),.10); }
  /* #1680 — la unica imagen del cliente sin caja propia: `width` sin `height`
     deja que el alto lo decida el archivo cuando llega, y el texto de abajo
     salta. Las otras 18 ya tienen lado fijo, `inset: 0` o `aspect-ratio`. */
  .news-item img { display: block; width: 100%; aspect-ratio: 16 / 9;
    object-fit: cover; margin-top: 10px;
    border: 1px solid var(--line); border-radius: 10px; }

  .settings { display: grid; gap: 12px; max-width: 560px; margin: 0 auto 18px;
    padding: 16px 18px; border: 1px solid var(--line); border-radius: 16px;
    background: var(--panel); }
  /* Issue #1464 — el contenedor de las filas no existe para el layout.
     Las filas eran hijas DIRECTAS del drawer y de ahí sacaban su separación
     (`.drawer` es un flex column con gap 14). Al envolverlas en un div para que
     las arme ajustes.js, ese gap dejó de alcanzarlas y quedaban pegadas.
     `display: contents` hace que el div no pinte ni ocupe: sus hijas vuelven a
     ser hijas del flex del drawer, que es lo que eran. */
  #settingsBody { display: contents; }
  .settings-row { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; }
  .settings-row > div:first-child { flex: 1; min-width: 180px; }
  .settings-row strong { display: block; font-size: 13px; }
  .settings-foot { display: flex; justify-content: flex-end; padding: 14px 0 0;
    border-top: 1px solid var(--line); }
  .settings-foot button { padding: 10px 24px; }
  /* Cada opción es una muestra del color, no su nombre: leer "morado" para
     elegir morado es pedirle al ojo que lea en vez de mirar. */
  /* Los avatares se eligen mirándolos: en redondo y del tamaño en que después
     se van a ver. */
  .avatares { flex-wrap: wrap; max-width: 260px; }
  .avatares button { width: 40px; height: 40px; padding: 0; border-radius: 50%;
    overflow: hidden; display: grid; place-items: center;
    font-size: 15px; font-weight: 700; color: #e4f5ec;
    background: linear-gradient(150deg, #2f6f57, #1c4737); }
  .avatares button img { width: 100%; height: 100%; object-fit: cover; }
  .avatares button.active { box-shadow: 0 0 0 2px var(--ink); }

  /* En la placa la imagen ocupa el círculo entero. */
  .avatar img { width: 100%; height: 100%; object-fit: cover; border-radius: 50%; }

  .felts button { width: 30px; height: 26px; padding: 0; border-radius: 7px;
    box-shadow: inset 0 0 0 1px rgba(255,255,255,.14); }
  .felts button[data-felt="verde"] { background: linear-gradient(160deg, #19714c, #0a3626); }
  .felts button[data-felt="rojo"] { background: linear-gradient(160deg, #8e2733, #430f16); }
  .felts button[data-felt="azul"] { background: linear-gradient(160deg, #1d5688, #0c2740); }
  .felts button[data-felt="morado"] { background: linear-gradient(160deg, #553a86, #251841); }
  .felts button[data-felt="negro"] { background: linear-gradient(160deg, #23282b, #0c0e0f); }
  .felts button.active { box-shadow: 0 0 0 2px var(--ink), inset 0 0 0 1px rgba(255,255,255,.2); }
  /* Issue #1255 — el selector del mazo muestra los cuatro palos con sus colores,
     igual que el del tapete muestra colores y no nombres: elegir "cuatro
     colores" leyendo la frase es pedirle al ojo que lea en vez de mirar.
     Los de la muestra son mas claros que los de la carta porque el fondo del
     ajuste es oscuro y el de la carta es blanco. */
  .mazos button { display: flex; gap: 3px; padding: 6px 9px; line-height: 1; }
  .mazos button i { font-style: normal; font-size: 15px; }
  .mazos button .p-h, .mazos button .p-d { color: #e0566a; }
  .mazos button .p-s, .mazos button .p-c { color: var(--ink); }
  .mazos button[data-mazo="cuatro"] .p-d { color: #4a9eff; }
  .mazos button[data-mazo="cuatro"] .p-c { color: #2fbd77; }
  /* Issue #1334 — las dos de cara oscura se eligen VIENDOLAS, igual que las otras
     dos: cada palo va sobre su fondo, que es exactamente lo que vas a ver en la
     mesa. Un boton que dijera "oscuro" con letras obligaria a imaginarselo. */
  .mazos button[data-mazo$="-oscuro"] i { width: 1.35em; height: 1.35em;
    display: inline-flex; align-items: center; justify-content: center;
    border-radius: 3px; color: #fff; font-size: 12px; }
  .mazos button[data-mazo$="-oscuro"] .p-h,
  .mazos button[data-mazo$="-oscuro"] .p-d { background: #b3182b; }
  .mazos button[data-mazo$="-oscuro"] .p-s,
  .mazos button[data-mazo$="-oscuro"] .p-c { background: #23272e; }
  .mazos button[data-mazo="cuatro-oscuro"] .p-d { background: #14549e; }
  .mazos button[data-mazo="cuatro-oscuro"] .p-c { background: #0d6b3e; }
  /* Cuatro no entran en una línea en teléfono: que envuelvan en vez de apretarse. */
  .mazos { flex-wrap: wrap; }
  .settings-row p { margin: 2px 0 0; font-size: 11px; }
  /* Issue #1335 — los tres atajos. Los campos son angostos a propósito: lo que
     entra es un porcentaje de dos o tres dígitos, y un input ancho invita a
     escribir cualquier cosa. */
  .sizings { display: flex; align-items: center; gap: 6px; }
  .sizings input { width: 58px; padding: 7px 8px; text-align: center;
    font-variant-numeric: tabular-nums; }
  .sizings button { padding: 7px 10px; font-size: 12px; }
  #sizingError { margin-top: 6px; font-size: 11px; }
  /* --- el header ---
     UNA barra. Antes eran tres franjas para decir dos cosas: un <h1> con el
     nombre del club, la barra con el jugador y sus fichas, y una banda que
     repetía el nombre del club y las fichas otra vez. */
  .topbar { display: flex; align-items: center; gap: 10px; margin-bottom: 18px;
    padding: 10px 14px; border: 1px solid var(--line); border-radius: 14px;
    background: linear-gradient(135deg, rgba(73,197,142,.07), transparent 45%), #0b0d0d; }
  .brand { display: flex; align-items: center; gap: 9px; flex: none;
    font-size: 14px; font-weight: 500; color: var(--muted); letter-spacing: -.01em; }
  .brand b { color: var(--ink); font-weight: 700; }
  .brand-mark { display: grid; place-items: center; width: 28px; height: 28px; border-radius: 9px;
    background: linear-gradient(160deg, var(--oro), var(--oro-hondo)); font-size: 15px; color: #17130a;
    box-shadow: inset 0 1px 0 rgba(255,255,255,.25); }

  .navtabs { display: inline-flex; gap: 3px; padding: 3px; margin-left: 10px;
    border: 1px solid var(--line); border-radius: 10px; background: rgba(0,0,0,.35); }
  .navtabs button { border: 0; background: transparent; color: var(--muted);
    font-size: 12px; padding: 6px 14px; border-radius: 8px; }
  .navtabs button.active { background: rgba(255,255,255,.10); color: var(--ink);
    font-weight: 600; }

  /* El menú de admin: lo que se usa una vez cada mucho no ocupa la barra. */
  .navmenu { position: relative; display: flex; }
  .navmenu-pop { position: absolute; z-index: 50; top: calc(100% + 7px); left: 0; min-width: 170px;
    display: flex; flex-direction: column; padding: 5px;
    border: 1px solid var(--line-strong); border-radius: 12px; background: #0d1211;
    box-shadow: 0 18px 44px rgba(0,0,0,.6); }
  .navmenu-pop button { border: 0; background: transparent; color: var(--muted);
    padding: 8px 11px; border-radius: 8px; font-size: 12px; text-align: left; }
  .navmenu-pop button:hover { background: rgba(255,255,255,.06); color: var(--ink); }

  /* El jugador, a la derecha: sus fichas y su nombre, una vez cada uno.
     Va SCOPEADO al header: `.me` a secas pisa `.pod.me`, que es el asiento
     propio en el tapete — la placa quedaba de 20px de ancho y el contador se
     encimaba con el nombre. */
  .topbar .me { display: flex; align-items: center; gap: 14px; margin-left: auto; flex: none; }
  .topbar .me-chips { display: flex; align-items: baseline; gap: 5px; }
  .topbar .me-chips b { font-size: 15px; font-weight: 700; font-variant-numeric: tabular-nums;
    letter-spacing: -.01em; }
  .topbar .me-chips small { font-size: 10px; letter-spacing: .08em; text-transform: uppercase;
    color: var(--muted); }
  .topbar .me-who { display: flex; align-items: center; gap: 7px; font-size: 13px; }
  /* Un punto y no la palabra "conectado": ese es el estado el 99% del tiempo, y
     una etiqueta permanente diciendo que todo está bien es ruido. */
  .topbar .me-who .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--oro);
    box-shadow: 0 0 0 3px rgba(var(--oro-rgb),.15); }
  .topbar .me-who .dot.off { background: var(--amber); box-shadow: 0 0 0 3px rgba(var(--amber-rgb),.15);
    animation: latir 1.4s ease-in-out infinite; }
  @keyframes latir { 50% { opacity: .35; } }
  /* Issue #1361 — el atajo a tu mesa, en la única barra que se ve siempre.
     La tira de mesas de abajo sólo aparece con dos o más, así que el que juega
     UN torneo —el caso más común— no tenía ningún atajo: cerrada la ventana de
     la mesa, volver eran tres clics por la grilla y el lobby del torneo.
     En la ventana dedicada no aparece: `body.popup` esconde la barra entera. */
  .mimesa { padding: 6px 11px; border-radius: 9px; font-size: 12px; font-weight: 600;
    color: var(--ink); border: 1px solid var(--line-strong);
    background: rgba(var(--oro-rgb),.10); white-space: nowrap; }
  .mimesa:hover { background: rgba(var(--oro-rgb),.18); border-color: var(--oro); }
  .mimesa .mm-corto { display: none; }
  /* Te toca y no estás ahí: es lo único de la barra que tiene que ganarte el
     ojo, igual que la mesa que reclama en la tira. */
  .mimesa.turno { color: var(--amber); border-color: rgba(var(--amber-rgb),.55);
    background: rgba(var(--amber-rgb),.14); animation: latir 1.1s ease-in-out infinite; }

  .topbar .icon { padding: 7px 10px; font-size: 13px; line-height: 1; color: var(--muted); }
  .topbar .icon:hover { color: var(--ink); }

  /* Discretos a proposito: bajar la app es una comodidad, no una accion del
     juego, y esta barra la mira el jugador todo el tiempo. */
  .descargas { display: inline-flex; gap: 6px; margin-left: 10px; flex: none; }
  .descargas .descarga { padding: 6px 11px; border: 1px solid var(--line); border-radius: 9px;
    background: rgba(0,0,0,.35); color: var(--muted); font-size: 12px; line-height: 1;
    text-decoration: none; white-space: nowrap; }
  .descargas .descarga:hover { color: var(--ink); border-color: rgba(255,255,255,.22); }
  /* En un telefono las solapas no entran: siete botones miden mas que la
     pantalla y empujaban TODA la pagina a lo ancho, que es lo que rompe una
     vista movil — el lobby entero se corria de costado. Se deslizan solas. */
  @media (max-width: 700px) {
    .descargas { display: none; }
    .navtabs { display: flex; margin-left: 0; width: 100%;
      overflow-x: auto; scrollbar-width: none; }
    .navtabs::-webkit-scrollbar { display: none; }
    .navtabs button { flex: 0 0 auto; }

    /* Lo mismo con los filtros y el resto de las tiras de botones: envolverlos
       gastaba dos y tres renglones cada uno, y para llegar a las mesas — que es
       a lo que se entra — habia que bajar media pantalla. */
    .chips { display: flex; flex-wrap: nowrap; overflow-x: auto;
      scrollbar-width: none; max-width: 100%; }
    .chips::-webkit-scrollbar { display: none; }
    .chips button { flex: 0 0 auto; white-space: nowrap; }


    /* En telefono el banner SI va —es donde la grilla es una sola columna y
       encontrar el proximo torneo cuesta mas scroll— pero mas chico: la ranura
       de la imagen se achica y el reloj baja de tamano. */
    .bn-slide { gap: 11px; padding: 2px 4px 20px; }
    .bn-arte { width: 104px; }
    .bn-arte span { font-size: 30px; }
    .bn-slide h4 { font-size: 16px; }
    .bn-reloj { font-size: 19px; }
    .bn-cifras { margin: 0; gap: 12px; font-size: 12px; }
    .bannerbar { padding: 8px 2px; border-radius: 14px; }

    /* El margen del cuerpo y los titulos, mas chicos: en 390px cada 10px de
       padding son 5% del ancho. */
    body { padding: calc(var(--safe-top) + 12px) calc(var(--safe-right) + 12px)
                    calc(var(--safe-bottom) + 20px) calc(var(--safe-left) + 12px); }
    h1 { font-size: 19px; }
    .bar { gap: 6px; margin-bottom: 12px; }

    /* La tarjeta del club se partia en dos lineas y el bloque de numeros
       quedaba apretado contra el borde. Los numeros pasan abajo, en fila. */
    .brand-name { display: none; }
    .topbar { flex-wrap: wrap; padding: 10px 12px; }

    /* El atajo a tu mesa, corto: medido en un Android de 393px, el rótulo largo
       dejaba la fila en 368px justos y empujaba el botón de salir a un renglón
       propio — 42px de pantalla por una palabra. */
    .mimesa { padding: 5px 8px; font-size: 11px; }
    .mimesa .mm-largo { display: none; }
    .mimesa .mm-corto { display: inline; }
    .lobby-tabs button { font-size: 14px; margin-right: 14px; }

    /* Los botones sueltos de la barra no se van a la derecha solos: en angosto
       eso los dejaba en un renglon vacio propio. */
    .lobby-bar { gap: 6px; margin-bottom: 10px; }
    #layoutSeg { margin-left: 0 !important; }

    /* --- la mesa en un telefono ---
       EL TAPETE USA EL HUECO ENTERO, no una proporción propia: en vertical una
       mesa 1.55 deja media pantalla vacía arriba y abajo.
       Va `height: 100%` y no `flex: 1 1 auto`: el hueco centra a sus hijos, así
       que un item flexible sin contenido propio —y todo acá adentro está
       posicionado en absoluto— colapsa a CERO de alto. El tapete desaparecía
       entero del teléfono sin que nada fallara. */
    /* Estas cuatro son de TELÉFONO EN VERTICAL, no de "pantalla angosta".
       REPORTADO con una captura: una ventana chica pero APAISADA —un navegador
       a medio achicar en la compu— entraba acá sólo por el ancho y se llevaba
       el diseño vertical entero. El resultado era una elipse aplastada arriba
       y una franja negra muerta abajo, de 150px reservados para una barra que
       en apaisado no va ahí. */
    @media (orientation: portrait) {
      .stage { width: 100%; height: 100%; aspect-ratio: auto; min-height: 0; }
      /* Y el hueco le reserva abajo lo que ocupa la barra de acción, que en el
         teléfono va pegada al borde: sin esto las pastillas de tamaño caen
         justo encima de tus cartas y de tu placa, o sea que para apostar hay
         que tocar arriba de tu propia mano. */
      .stage-wrap { padding-bottom: var(--acts-movil, 150px); align-items: stretch; }

      /* Y los botones sueltos dejan de flotar: en escritorio flotan sobre el
         fieltro porque sobra lugar a los costados, pero acá la barra ocupa todo
         el ancho de abajo y se pisaban con ella. Van en fila, arriba de la
         barra. */
      .floatbar { position: static; max-width: none; }
      .floatbtn { position: static; }

      /* Issue #1258 — y la barra de ACCION tambien entra al flujo.
         REPORTADO con captura: los botones sueltos quedaban dibujados ENCIMA de
         "Pasar" y "Apostar", justo sobre lo unico que no puede fallar.
         Eran tres cosas juntas: `.actionbar` no tenia NINGUNA regla en vertical
         —se quedaba `absolute`, fuera del flujo—, la floatbar si pasaba a
         `static` y volvia al flujo, y `--acts-movil` reserva su alto DENTRO de
         `.stage-wrap`, o sea que achica el fieltro sin crear lugar debajo. Para
         colmo la floatbar tiene `z-index: 31` contra el 30 de la barra, asi que
         ademas ganaba el pintado.
         En el flujo las dos, el navegador las apila y no hay nada que apilar
         mal: el `padding-bottom` de `.stage-wrap` deja de hacer falta. */
      .actionwrap { position: static; z-index: auto; align-self: stretch; flex: none; }
      .stage-wrap { padding-bottom: 0; }

      /* Issue #1258 — la placa se apila, o el nombre no entra.
         REPORTADO con captura: los nombres cortados en TRES letras (`ttta…`,
         `pap…`) y sin saber quien era quien. Ensanchar la placa NO se puede: hay
         un test que verifica que los asientos de los costados no se salgan de la
         pantalla, y 24cqmin ya entra justo.
         Asi que el espacio sale de adentro. En fila, el avatar y el gap se comen
         casi la mitad del ancho util; apilados, el nombre lo toma entero.
         Medido con Playwright en 375x667, 393x851 y 430x932: los nombres entran
         completos en los tres, y ni el board ni las placas empiezan a chocar. */
      .pod-body { flex-direction: column; gap: .6cqmin; }
      .pod-txt { width: 100%; text-align: center; }

      /* PRUEBA: placa apilada */
      .pod-body { flex-direction: column; gap: .6cqmin; }
      .pod-txt { width: 100%; text-align: center; }

    }

    /* Nada sobre el tapete por debajo de 10px. Todo lo de adentro se mide en
       `cqmin` contra la mesa, que en un teléfono es chica: los rótulos que en
       escritorio quedan en 14px acá caen a 8. El `max()` les pone piso sin
       tocar el escritorio, donde el valor relativo sigue mandando. */
    .board .muted { font-size: max(10px, 2.8cqmin); }
    .potline span { font-size: max(10px, 2.3cqmin); }
    .potline em { font-size: max(10px, 2.4cqmin); }
    .sidepots { font-size: max(10px, 2.4cqmin); }
    /* Este quedó afuera de la lista de arriba: caía a 8.95px en un Android de
       393px. Se veía sólo mientras corre el reloj de alguien, así que el
       escenario lo agarraba una de cada seis corridas. */
    .pod-secs { font-size: max(10px, 2.4cqmin); }
    .chat-say { font-size: max(11px, 2.4cqmin); max-width: min(52vw, 26cqmin); }
    /* El sticker se mide en cqmin como todo el tapete, y en un teléfono el
       tapete es chico: medido, 14cqmin queda en 45-52px en pantalla. El sticker
       ES el mensaje, así que tiene que pesar como un globo de texto y no la
       mitad — el de al lado mide 83-96px en esos mismos teléfonos.
       El valor declarado no es el que se ve: el tapete va escalado ~0.9, así
       que 88px se dibujan como ~79. El escenario de teléfono mide lo VISIBLE. */
    .chat-say.es-sticker img { width: max(88px, 14cqmin); }
    .result span { font-size: max(10px, 2.5cqmin); }
    .tstatus { font-size: max(10px, 2.6cqmin); }
    .pod-txt .n { font-size: max(10px, 2.42cqmin); }
    .pod .bet, .ptag { font-size: max(10px, 2.6cqmin); }
    /* Issue #1722 — en el teléfono la etiqueta NO puede achicarse: el piso de
       10px de arriba está puesto porque debajo de eso no se lee sobre el paño.
       Lo que sí se puede achicar es el relleno, que no se lee. Medido en un
       iPhone SE de nueve —el caso más apretado que existe— la fila del peor caso
       rozaba 14px² la carta del vecino; con esto entra. */
    .pod-tags { gap: .4cqmin; }
    .ptag { padding: .3cqmin .7cqmin; }
    .ptag.mas { padding: .3cqmin .5cqmin; }
    /* El marco de la racha tiene su PROPIO arte acá.
       El de escritorio está dibujado para un panel 2:1 y lame hacia adentro
       hasta la mitad del rectángulo; en el teléfono la placa es 1.3:1 y apila el
       avatar arriba (#1258), así que eso le caía encima — medido, los carámbanos
       llegaban a 14.5px de una placa de 60 y el avatar arranca a 3.7px.
       Éste está dibujado para esa proporción y con TODO por fuera del
       rectángulo, así que puede ir adelante y verse la textura sin tapar nada.
       La geometría es la misma —marco interno al 8% y 12% del lienzo— así que el
       sangrado y el radio de arriba valen igual. */
    .pod.racha-fuego .pod-racha { background-image: url("/rachas/mobile/telefono-fuego.svg"); }
    .pod.racha-hielo .pod-racha { background-image: url("/rachas/mobile/telefono-hielo.svg"); }
    .pod-txt .s { font-size: max(10px, 2.8cqmin); }
    body.en-bb .pod-txt .s { font-size: max(10px, 2.6cqmin); }
    .pod.free .pod-txt .s { font-size: max(10px, 2.6cqmin); }

    /* Los rótulos chicos suben a 10px: 9 no se lee en un teléfono, y son
       justamente los que dicen QUÉ es cada número de la cabecera. */
    .tstat span, .rowhead, .rowcard .rnum span, .rowcard .rwhen small,
    .rowcard .rnum .gtd, .rowcard .rot { font-size: 10px; }

    /* La cabecera de la mesa, en una sola linea: partida en dos se comia el
       alto que necesita el tapete. El campo de fichas no hace falta — al
       sentarse lo pregunta el dialogo.
       Issue #1298: eso estaba escrito aca como intencion y no habia ninguna
       regla que lo cumpliera — medido, la cabecera ocupaba 172px de una
       pantalla de 851 y los bloques caian en dos filas. Ahora la linea corta no
       envuelve nunca: si un torneo con descanso y burbuja no entra, se desliza. */
    /* Issue #1429 — la cabecera deja de ser una tarjeta y pasa a ser una línea.
       PEDIDO mirando una captura: "el header ocupa mucho espacio sin sentido,
       podríamos sacarlo y que quede flotando en una sola línea ← Lobby / Turbo
       5K / 10-20 2/6, y una segunda línea con los botones de las mesas".

       MEDIDO antes, en un Android de 393: 109px en TRES filas. La de arriba con
       "← Lobby" y el nombre, la del medio con la línea corta —porque `.tstats`
       tenía `width: 100%` y se llevaba la fila entera— y "Salir" empujado a una
       tercera por el `margin-left: auto`. El tope del guard eran 96px, así que
       109 ya lo pasaba y nadie lo notó porque el guard corre con un torneo y
       esto se ve en cash.

       La caja se va entera: borde, fondo y padding. En vertical ese marco no
       agrupa nada —es la única cosa arriba del tapete— y cuesta 14px de alto más
       el radio. Lo que queda es la línea sola sobre el fondo de la página. */
    .thead { padding: 0; gap: 8px; border: 0; background: none; border-radius: 0;
      flex-wrap: nowrap; }
    .thead button { font-size: 12px; padding: 5px 9px; }
    /* La flecha sola. Escondida a la vista pero presente en el DOM: el botón se
       sigue anunciando "← Lobby" a un lector de pantalla, que con `display:none`
       se perdería. */
    .thead #backTxt { position: absolute; width: 1px; height: 1px;
      overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
    .thead #back { padding-left: 10px; padding-right: 10px; }
    .thead .buyin-field { display: none; }
    /* El nombre se recorta con puntos suspensivos antes que empujar a nadie,
       pero NO baja de 5em: reducido a dos letras deja de decir en qué mesa
       estás, que es todo lo que hace ahí. */
    /* Base 0 y crece: el nombre se queda con lo que SOBRE, no con lo que pida.
       Al revés —que era lo natural— se llevaba 108px y dejaba la línea de datos
       deslizándose. De los dos, el que se puede perder de vista es el nombre:
       ya sabés en qué torneo estás, no sabés en qué puesto vas. */
    .thead-info { flex: 1 1 0; min-width: 0; }
    .thead-info strong { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

    /* La línea corta deja de reclamar la fila entera y se pone al lado, con lo
       que quede libre. En cash son dos datos y sobra; en un torneo son las
       ciegas, cuánto falta para que suban y en qué puesto vas, y medido pide
       214px — que entran recién sacando la palabra "Lobby" de acá arriba.

       Y es lo que hay que cuidar al tocar esto: escondido no sirve. Esa línea se
       mira mientras se decide la mano, y nadie desliza una cabecera con la
       acción corriendo. El e2e mide que no se deslice. */
    .tstats { margin-left: 0; width: auto; flex: 0 1 max-content; min-width: 0; }
    .tcorta { overflow-x: auto; scrollbar-width: none; }
    .tcorta::-webkit-scrollbar { display: none; }
    /* El panel ocupa el ancho entero: anclarlo a la derecha en 393px lo dejaba
       pegado a un borde y con las columnas de 118px apretadas. */
    .tpanel { left: 0; right: 0; max-width: none;
      grid-template-columns: repeat(auto-fit, minmax(96px, 1fr)); }
  }

