/* Solitario — hoja de estilos única. Sin dependencias ni fuentes externas. */

:root {
  --cw: 90px;                    /* ancho de carta, lo fija el JS al medir la pantalla */
  --ch: 126px;
  --gap: 12px;
  --radius: 9px;
  --speed: 180ms;         /* transiciones de la interfaz */
  --card-speed: 324ms;    /* lo que tarda una carta como mucho en ir de un sitio a otro */
  --flip-speed: 280ms;    /* el volteo al destapar, un pelo más corto que el vuelo */
  --vuelo: cubic-bezier(.35, .12, .65, .88); /* acelera y frena sin saltar al salir */
  --volteo: cubic-bezier(.15, .45, .85, .55);  /* corre al salir y se frena en el canto */
  /* El cajón: la curva de las hojas de iOS, que sale con ganas y se posa sin
     rebote. Abrir dura más que una transición de botón porque recorre la
     pantalla entera; cerrar, menos, porque quien cierra ya ha terminado. */
  --cajon-curva: cubic-bezier(.32, .72, 0, 1);
  --cajon-abre: 480ms;
  --cajon-cierra: 300ms;

  --felt-1: #14663f;
  --felt-2: #0b3f28;
  --ink: #f2f7f4;
  --ink-soft: rgba(242, 247, 244, .8);
  --panel: #10261d;
  --panel-2: #16342a;
  --panel-ink: var(--ink);            /* tinta sobre los paneles de los diálogos */
  --panel-ink-soft: var(--ink-soft);
  --line: rgba(255, 255, 255, .14);
  --panel-line: rgba(255, 255, 255, .14);
  --fill: rgba(255, 255, 255, .09);   /* fondos suaves dentro de los paneles */
  --fill-strong: rgba(255, 255, 255, .17);
  --ok: #6ee7a8;
  --accent: #f0c453;
  --accent-ink: #2a2000;
  --warn: var(--accent);              /* avisos dentro de un panel */
  --danger: #e06a5c;
  /* Sobre el tapete y sobre la barra el fondo es oscuro en los dos temas, así
     que el acento y el verde de los paneles se apagan ahí: estas dos son sus
     versiones claras y no cambian con el tema. */
  --felt-accent: #ffd97a;
  /* El destino de la pista: más flojo que el origen, pero por encima del 3:1 que
     pide una marca no textual. Al 55 % de opacidad se quedaba en 2:1 sobre el
     tapete claro; opaco y un punto más apagado que el origen se distingue de él
     sin desaparecer. */
  --felt-accent-suave: #f2c66b;
  --felt-filo: rgba(24, 12, 0, .72);   /* el filo oscuro que envuelve al anillo */
  --felt-ok: #8ff0bd;
  /* Las cajas de las dos barras. Van aquí y no en `--fill` porque las barras son
     verde oscuro en los dos temas: el `--fill` del tema claro es tinta oscura
     sobre blanco y sobre este verde no se vería. Estaban escritas a mano en tres
     sitios con el mismo número. */
  --felt-fill: rgba(255, 255, 255, .07);
  --felt-fill-fuerte: rgba(255, 255, 255, .18);
  --hueco-marca: var(--ink);          /* la «K» del hueco vacío */
  --card-face: #fdfcf8;
  --card-ink: #1b1b1b;
  --card-red: #c0392b;
  /* Baraja de cuatro colores. La cara de la carta es la misma en los dos temas,
     así que estos dos tampoco cambian: 7,2:1 el azul y 5,3:1 el verde sobre la
     cara, los dos por encima del 4,5:1 que pide un texto pequeño.
     El verde es más claro de lo que pedía el ojo (antes #0a4520) porque a oscuras
     se confundía con el negro de las picas —1,5 veces de luminancia, ahora 3,2—,
     que es justo el par que hay que separar. Queda un límite conocido: con el
     rojo de corazones el verde comparte luminancia, así que quien no distinga
     rojo de verde sigue apoyándose en el símbolo del palo para ese par; bajar más
     el verde rompería el contraste sobre la cara. */
  --card-diamond: #1553a8;
  --card-club: #1a7a3c;
  --card-line: rgba(0, 0, 0, .16);
  --header-bg: #06452a;               /* el verde reforzado del header y barras */
  --shadow: 0 2px 5px rgba(0, 0, 0, .3);
  --shadow-lift: 0 6px 14px rgba(0, 0, 0, .26), 0 18px 36px rgba(0, 0, 0, .42);

  /* Las zonas seguras, con nombre. Van por variable y no por `env()` suelto en
     cada regla por dos motivos: se pueden simular para probarlas, y sobre todo
     se ven de un vistazo los cuatro sitios donde el sistema nos come pantalla. */
  --safe-top: env(safe-area-inset-top, 0px);
  --safe-right: env(safe-area-inset-right, 0px);
  --safe-bottom: env(safe-area-inset-bottom, 0px);
  --safe-left: env(safe-area-inset-left, 0px);

  /* Lo que pinta el sistema y no nosotros —la lista desplegable del idioma, las
     flechitas del campo numérico, las barras de desplazamiento— va con el tema
     que se ha elegido aquí. Sin esto, quien tiene el sistema en claro y el juego
     en oscuro abría el idioma y le salía una lista blanca. */
  color-scheme: dark;
}

/* En claro solo cambian el fieltro y los paneles: sobre el tapete la tinta sigue
   siendo blanca, dentro de los diálogos pasa a ser oscura. */
html[data-theme="light"] {
  color-scheme: light;
  --felt-1: #35a066;
  --felt-2: #1d7146;
  --ink: #ffffff;
  --ink-soft: rgba(255, 255, 255, .95);
  --panel: #ffffff;
  --panel-2: #eef2ef;
  --panel-ink: #15251d;
  --panel-ink-soft: rgba(21, 37, 29, .68);
  --line: rgba(255, 255, 255, .24);
  --panel-line: rgba(21, 37, 29, .16);
  --fill: rgba(21, 45, 33, .07);
  --fill-strong: rgba(21, 45, 33, .14);
  --accent: #9a6510;
  --accent-ink: #ffffff;
  --warn: #7d4f05;                    /* el acento se queda en 4,3:1 sobre panel blanco */
  --danger: #a4241c;
  --ok: #12734a;
  --header-bg: #0d5833;
  --shadow: 0 2px 5px rgba(0, 0, 0, .18);
  --shadow-lift: 0 6px 14px rgba(0, 0, 0, .14), 0 16px 36px rgba(0, 0, 0, .26);
}

/* Quietud a petición. La pide el sistema (prefers-reduced-motion) o la pide el
   jugador desde los ajustes, y motion.js la deja escrita en <html data-motion>;
   las dos quieren exactamente lo mismo. En vez de repetir el bloque entero dos
   veces, cada condición se limita a encender `--quieto`: todo lo que se mueve
   lleva su valor de siempre como respaldo del var(), así que basta con que la
   variable exista para que la declaración pase a valer `none`. */
@media (prefers-reduced-motion: reduce) { :root { --quieto: none; } }
html[data-motion="no"] { --quieto: none; }

/* Sin esto, cualquier `display` propio gana al atributo hidden. */
[hidden] { display: none !important; }

* { box-sizing: border-box; }

html {
  background-color: var(--header-bg);
}

html, body {
  height: 100vh;
  height: 100dvh;
  margin: 0;
  overscroll-behavior: none;
  /* Nada de zoom: pan-x pan-y deja desplazarse pero quita el pellizco y el
     doble toque. El resto (los gestos de Safari) lo corta el JS. */
  touch-action: pan-x pan-y;
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

/* En una PWA, Safari puede descontar la barra de estado del alto dinámico
   aunque viewport-fit=cover permita dibujar detrás de ella. Recuperamos ese
   espacio sin superar el alto total ni sumar el inset cuando ya está incluido.
   Si iOS reserva la barra fuera de la página, --safe-top es cero. */
@media (display-mode: standalone) {
  html, body { height: min(100lvh, calc(100dvh + var(--safe-top))); }
}

body {
  font: 15px/1.45 system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
  color: var(--ink);
  background-color: var(--header-bg);
  display: flex;
  flex-direction: column;
  -webkit-user-select: none;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
}

/* ---------- barra superior ---------- */

/* Una rejilla con nombre para cada pieza: en el móvil la primera fila es Nueva,
   el chip de modalidad en el centro y Repetir y el menú a la derecha, con el
   marcador debajo; en pantalla ancha todo cabe en una sola fila. Así cada
   tamaño coloca las piezas sin ir reordenándolas una a una. */
.topbar {
  position: relative;      /* la barra de progreso se ancla abajo en pantallas bajas */
  flex-shrink: 0;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto minmax(0, auto) auto;
  grid-template-areas:
    "marca marcador izq chip der"
    "progreso progreso progreso progreso progreso";
  align-items: center;
  gap: 8px 10px;
  /* Instalada en iPhone la barra de estado se superpone (black-translucent +
     viewport-fit=cover), así que hay que dejarle su hueco. Va de margen y no de
     relleno para que ese hueco lo pinte el tapete y no el velo de la barra:
     arriba y abajo, las dos franjas del sistema son del color del fondo. */
  margin-top: var(--safe-top);
  padding: 8px max(12px, var(--safe-right)) 10px max(12px, var(--safe-left));
  background: var(--header-bg);
  border-bottom: 1px solid var(--line);
}

/* Las acciones que no son de la partida en curso: iconos sueltos, sin caja, con
   su nombre en el título y en el lector de pantalla. La barra es verde oscuro en
   los dos temas, así que la tinta y el resalte son los de la barra de abajo. */
.barra-izq { grid-area: izq; display: flex; align-items: center; gap: 6px; }
.barra-der { grid-area: der; display: flex; align-items: center; }
.bar-btn {
  display: grid;
  place-items: center;
  width: 44px;
  height: var(--bar-alto, 44px);
  padding: 0;
  color: var(--ink);
  background: none;
  border: 0;
  border-radius: 10px;
  cursor: pointer;
  transition: var(--quieto, background var(--speed) linear, transform var(--speed) linear);
}
.bar-btn:hover { background: var(--felt-fill-fuerte); }
.bar-btn:active { transform: translateY(1px); }
.bar-btn:focus-visible { outline: 2px solid var(--felt-accent); outline-offset: 2px; }

.brand { grid-area: marca; display: flex; align-items: center; gap: 8px; margin-right: 6px; }
.brand h1 { font-size: 17px; margin: 0; letter-spacing: .3px; font-weight: 650; }
.brand-mark {
  display: grid; place-items: center;
  width: 26px; height: 26px; border-radius: 7px;
  background: var(--accent); color: var(--accent-ink);
  font-size: 15px;
}

/* Las cuatro cifras se reparten la fila a partes iguales: con anchos de
   contenido salían de 60, 66, 67 y 70 px, cuatro datos sueltos en vez de un
   marcador. Las columnas pueden encogerse por debajo del contenido
   (`minmax(0, 1fr)`), que es lo que las deja caber en una pantalla de 280. */
.scoreboard {
  grid-area: marcador;
  justify-self: start;
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
  gap: 6px;
  margin: 0;
  min-width: 0;
}

/* Aviso de versión nueva: solo aparece cuando hay un worker esperando.
   Va del alto del chip de modalidad y con el rótulo corto porque tiene que
   caber en la fila que ya existe: con sus 44 px y su frase entera se llevaba una
   fila para ella sola, y en un móvil eso son 52 px menos de tapete que aparecen
   de repente, con la partida a medias. El objetivo de dedo lo repone la capa
   invisible de abajo, igual que en el chip. */
.update-pill {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 4px 11px;
  min-height: 30px;
  font: inherit;
  font-size: 12px;
  font-weight: 700;
  color: var(--accent-ink);
  background: var(--accent);
  border: 0;
  border-radius: 999px;
  cursor: pointer;
  /* Seis latidos y para: un pulso infinito en la barra no deja de moverse en
     toda la partida y acaba cansando (y gastando batería). */
  animation: var(--quieto, latido 2.4s linear 6);
}
/* La píldora mide 30 px y en el móvil se queda en el ancho de la flecha: el
   objetivo de dedo lo repone esta capa, que la rodea hasta los 44 x 44. */
.update-pill::after { content: ''; position: absolute; inset: -7px; }
.update-flecha { font-size: 14px; line-height: 1; }
.update-pill:hover { filter: brightness(1.08); }
.update-pill:disabled { opacity: .6; cursor: progress; animation: none; }

/* Sin sitio de sobra para el rótulo, la flecha y el nombre accesible bastan: así
   la píldora ocupa 30 px, cabe en la fila que ya hay y la cabecera no gana una
   línea entera. El corte está donde deja de caber con rótulo, medido: a 744 px
   —un iPad de pie— la cabecera crecía 44 px al aparecer el aviso. */
@media (max-width: 899px) {
  .update-texto { display: none; }
  .update-pill { padding: 4px 9px; }
}

/* Lo que solo tiene que oírse. El texto sigue en el árbol —un lector de
   pantalla lo canta— pero no ocupa ni un píxel. */
.solo-lectores {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Sin caja: cada cifra con su rótulo encima, directamente sobre la barra. La
   celda mide lo que su texto y se centra en su columna, así la marca de pausa
   cae junto al rótulo del reloj y no en el borde de la columna. */
.stat {
  position: relative;
  justify-self: center;
  min-width: 0;
  max-width: 100%;
  padding: 2px 4px;
  text-align: center;
}
/* Once píxeles es el suelo: por debajo un rótulo deja de leerse en una pantalla
   densa, y estos cuatro no son adorno, dicen qué cifra hay debajo. El espaciado
   entre letras se queda en .4 para que quepan en la rejilla. */
.stat dt {
  font-size: 11px;
  line-height: 1.2;
  text-transform: uppercase;
  letter-spacing: .4px;
  color: var(--ink-soft);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.stat dd { margin: 0; font-size: 16px; font-weight: 650; font-variant-numeric: tabular-nums; }
.stat-wide dd { font-size: 13px; padding-top: 2px; }
.stat.bump dd { animation: var(--quieto, bump 320ms linear); }
.stat.bump-down dd { animation: var(--quieto, bump-down 320ms linear); }
/* Los números que brincan (marcador y contador del mazo) están los dos sobre
   fondo oscuro en ambos temas, así que el resalte usa el acento claro: con el
   del tema claro el número se apagaba justo en el fotograma en que quería
   llamar la atención. Las penalizaciones usan el color de peligro. */
@keyframes bump { 40% { transform: scale(1.22); color: var(--felt-accent); } }
@keyframes bump-down { 40% { transform: scale(1.22); color: var(--danger); } }

/* El reloj parado. Antes era una pastilla con su rótulo debajo del número, y
   cada vez que el jugador cambiaba de pestaña la cabecera crecía 22 px y el
   tablero daba un salto. Ahora es una marca justo detrás del rótulo «Tiempo», a
   su altura y sin tapar la última letra: se sale del flujo, así que no mueve
   nada, y el número se apaga un poco, que es lo que de verdad dice «esto está
   parado». El rótulo sigue ahí para quien escucha. */
.paused-flag {
  position: absolute;
  top: 1px;
  right: -15px;
  display: grid;
  place-items: center;
  width: 15px;
  height: 15px;
  border-radius: 50%;
  /* El ámbar claro no cambia con el tema (vive sobre la barra, que es oscura en
     los dos), así que la tinta de encima tampoco: siempre oscura sobre él. */
  background: var(--felt-accent);
  color: #2a2000;
  font-size: 8px;
  line-height: 1;
}
.paused-mark { display: block; transform: translateY(.5px); }
/* El número apagado sigue siendo texto de 16 px: por debajo de esto se cae del
   4,5:1 que pide la AA y el crono se vuelve ilegible justo cuando hay que leerlo
   para entender que está parado. */
#stat-clock:has(.paused-flag:not([hidden])) dd { opacity: .78; }

/* La modalidad, de un vistazo y sin abrir nada; pulsarla lleva a los ajustes. */
.mode-chip {
  grid-area: chip;
  justify-self: center;
  position: relative;
  min-width: 0;
  max-width: 100%;
  padding: 6px 12px;
  min-height: 34px;
  font: inherit;
  font-size: 12px;
  font-weight: 650;
  color: var(--ink-soft);
  background: var(--fill);
  border: 1px solid var(--line);
  border-radius: 999px;
  cursor: pointer;
  transition: background var(--speed) linear, color var(--speed) linear;
}
/* Con las modalidades largas (el reto, las pistas penalizadas, otros idiomas) el
   chip no salta a una segunda línea, que haría crecer la cabecera: se corta con
   puntos suspensivos y la frase entera sigue en Ajustes, a un toque. */
.modo-texto { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mode-chip:hover { background: var(--fill-strong); color: var(--ink); }
.mode-chip:focus-visible { outline: 2px solid var(--felt-accent); outline-offset: 2px; }
/* La píldora es baja a propósito (si no, la barra crece), pero el dedo necesita
   sus 44 px: se los da una capa invisible que sobresale por arriba y por abajo. */
.mode-chip::after { content: ''; position: absolute; inset: -5px 0; }

/* El reparto se puede copiar para compartirlo, así que es un botón; parece
   texto porque en la barra un botón de verdad pesaría demasiado. */
.seed-chip {
  position: relative;
  padding: 0;
  font: inherit;
  font-size: 13px;
  font-weight: 650;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  background: none;
  border: 0;
  cursor: pointer;
  text-decoration: underline dotted;
  text-underline-offset: 3px;
  transition: color var(--speed) linear;
}
.seed-chip:hover { color: var(--felt-accent); }
.seed-chip:focus-visible { outline: 2px solid var(--felt-accent); outline-offset: 3px; border-radius: 5px; }
.seed-chip.copiado { color: var(--felt-ok); text-decoration: none; }
.seed-chip::after { content: ''; position: absolute; inset: -13px -10px; }

/* Cuántas cartas llevan colocadas las fundaciones. Va en su propia línea, de
   ancho completo, la última de la barra. */
.progress {
  grid-area: progreso;
  height: 3px;
  border-radius: 999px;
  background: var(--fill);
  overflow: hidden;
}
/* Se estira con `transform` y no con `width`: animar el ancho obliga al
   navegador a recalcular la disposición en cada fotograma, y esto se mueve cada
   vez que sube una carta —o cincuenta y dos seguidas en la cascada final—. El
   llenado lo escribe el JS en `--llenado`, de 0 a 1. */
.progress > i {
  display: block;
  width: 100%;
  height: 100%;
  background: var(--felt-accent);
  border-radius: inherit;
  transform: scaleX(var(--llenado, 0));
  transform-origin: left center;
  transition: var(--quieto, transform var(--speed) linear);
}

/* En el tema claro, la franja de la zona segura de arriba la pinta el tapete
   claro, y encima iOS dibuja el reloj y la batería en blanco (el estilo de la
   barra es `black-translucent`): blanco sobre verde claro se queda en 3,3:1. Un
   velo del propio verde oscuro del tapete, que se desvanece antes de llegar a la
   barra, lo sube por encima del 4,5:1 sin meter un color nuevo ni una costura.
   Con `--safe-top` a cero —o sea, en cualquier sitio que no sea un móvil con
   muesca— esto mide cero y no se ve. */
html[data-theme="light"] body::before {
  content: '';
  position: fixed;
  top: 0; left: 0; right: 0;
  height: var(--safe-top);
  background: linear-gradient(rgba(9, 58, 36, .42), rgba(9, 58, 36, 0));
  pointer-events: none;
}

/* ---------- barra de acciones ---------- */
/* Va al fondo de la pantalla a propósito: arriba, en un móvil grande, hay que
   cambiar la mano para llegar. Aquí cae el pulgar solo, y por eso aquí se
   quedan solo Deshacer y Pista, las dos que se usan a media partida. */

.sprite { position: absolute; width: 0; height: 0; overflow: hidden; }

.tools {
  display: flex;
  flex-shrink: 0;
  align-items: center;
  justify-content: center;
  gap: 8px;
  /* La barra pinta hasta el borde; los botones conservan su tamaño por encima
     del indicador de inicio. La zona segura se reserva una sola vez aquí, con
     el mínimo hueco necesario, sin alargar el relleno de cada botón. */
  padding: 8px max(10px, var(--safe-right)) max(8px, var(--safe-bottom)) max(10px, var(--safe-left));
  background: var(--header-bg);
  border-top: 1px solid var(--line);
}

/* El desenfoque de las dos barras, aparte: Safari lo pide con prefijo hasta muy
   tarde y sin la consulta se quedaba sin él. 4 px bastan para separar la barra
   del tapete; 6 obligaban a la GPU a trabajar de más en cada repintado. */
@supports (backdrop-filter: blur(4px)) or (-webkit-backdrop-filter: blur(4px)) {
  .topbar, .tools {
    -webkit-backdrop-filter: blur(4px);
    backdrop-filter: blur(4px);
  }
}

/* Dos botones anchos que se reparten la fila, con el icono y el rótulo en línea.
   En pantalla ancha no pasan de 240 px: más largos no se tocan mejor. */
.tool {
  flex: 1 1 0;
  min-width: 0;
  max-width: 240px;
  --tool-alto: 48px;             /* objetivo de dedo, no de ratón */
  min-height: var(--tool-alto, 48px);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 0 12px;
  font: inherit;
  color: var(--ink);
  background: var(--felt-fill);
  border: 1px solid var(--line);
  border-radius: 10px;
  cursor: pointer;
  transition: var(--quieto, background var(--speed) linear, transform var(--speed) linear, opacity var(--speed) linear);
}
.ico {
  flex-shrink: 0;
  width: 22px;
  height: 22px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
}
/* Al lado del icono y no debajo: con dos botones hay sitio, y el rótulo va a la
   medida del texto de los botones de los diálogos. */
.rotulo { font-size: 15px; line-height: 1.2; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tool:hover:not(:disabled) { background: var(--felt-fill-fuerte); }
.tool:active:not(:disabled) { transform: translateY(1px); }
.tool:disabled { opacity: .35; cursor: default; }
.tool:focus-visible, .btn:focus-visible, .seg-btn:focus-visible, .icon-btn:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px;
}
/* ---------- tablero ---------- */

.table {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 14px max(8px, var(--safe-right)) 10px max(8px, var(--safe-left));
  overflow-y: auto;
  /* Red de seguridad: el tablero se mide para que quepa con margen, pero si
     alguna animación creciera, antes se recorta que sacar barra horizontal.
     Un navegador que no entienda `clip` se queda con el `auto` de siempre. */
  overflow-x: clip;
  background: radial-gradient(120% 100% at 50% 0%, var(--felt-1), var(--felt-2) 70%);
}

.board {
  position: relative;
  width: calc(7 * var(--cw) + 6 * var(--gap));
  flex: 1;
  touch-action: none;
}

.slot {
  position: absolute;
  top: 0; left: 0;
  width: var(--cw);
  height: var(--ch);
  border-radius: var(--radius);
  border: 1.5px solid rgba(255, 255, 255, .22);
  background: rgba(0, 0, 0, .14);
  display: grid;
  place-items: center;
  transition: border-color var(--speed) linear, background var(--speed) linear, box-shadow var(--speed) linear;
}
.slot-mark { font-size: calc(var(--cw) * .34); opacity: .3; }
.slot-mark.red { color: #ffb3a7; }
/* La «K» del hueco de columna recuerda qué se puede poner ahí, pero es una
   marca de fondo: cualquier carta encima tiene que ganarle de calle. El color no
   se hereda de la tinta porque en el tema claro es blanca y sobre el tapete
   claro no quedaba nada; en negro al 22 % se lee en los dos. */
/* Blanca y no oscura: sobre el hueco (negro al 14 % encima del tapete) la tinta
   clara rinde más que cualquier tono oscuro —medido, 1,7:1 frente a 1,3:1—, y el
   .38 la deja a la altura de los glifos de las fundaciones, que es la misma clase
   de marca y se leen bien en los dos temas. */
.slot-tableau .slot-mark { opacity: .38; font-weight: 700; color: var(--hueco-marca); }
.slot-stock { cursor: pointer; }
.slot-stock.empty .slot-mark { opacity: .5; }
.slot-stock.dead { cursor: not-allowed; }
.slot-stock.dead .slot-mark { opacity: .12; }

.slot.drop-ok {
  border-color: var(--accent);
  background: rgba(240, 196, 83, .16);
  box-shadow: 0 0 0 3px rgba(240, 196, 83, .22);
}

.cards { position: absolute; inset: 0; pointer-events: none; }   /* si no, tapa los huecos */

/* ---------- cartas ---------- */

.card {
  position: absolute;
  pointer-events: auto;
  touch-action: none;
  top: 0; left: 0;
  width: var(--cw);
  height: var(--ch);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  cursor: default;
  perspective: 700px;      /* el volteo necesita fondo para no verse plano */
  transform-style: preserve-3d;
}
/* El relieve del vuelo va en una capa aparte, no en el box-shadow de la carta:
   animar una sombra obliga a redibujarla en cada fotograma, mientras que
   encender y apagar una capa ya pintada es solo opacidad. El `z-index: -1` la
   deja por detrás de las dos caras (van a inset 0 y con fondo propio, la
   taparían) y el radio lo copia de la carta para que no asome por las esquinas. */
.card::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: var(--shadow-lift);
  opacity: 0;
  z-index: -1;
}
/* La posición vuela a la medida de la distancia (el JS ajusta la duración) y
   entra y sale suave. El levantamiento ya no va por transición: ver más abajo. */
.anim .card { transition: var(--quieto, transform var(--card-speed) var(--vuelo), translate 140ms linear); }
.anim .card::after { transition: var(--quieto, opacity var(--speed) linear, translate var(--speed) linear, filter var(--speed) linear); }
.anim .card .face, .anim .card .back { transition: var(--quieto, transform var(--flip-speed) var(--volteo)); }

.card.playable { cursor: grab; }
@media (hover: hover) {
  .card.playable:not(.dragging):hover { translate: var(--quieto, 0 -3px); }
  .card.playable:not(.dragging):hover::after {
    opacity: var(--quieto, 0.42);
    translate: var(--quieto, 0 6px);
    filter: var(--quieto, blur(1.5px));
  }
}
/* El transform sigue al dedo sin interpolar; el alzado sí entra suave. */
.card.dragging { cursor: grabbing; transition: var(--quieto, translate 90ms var(--vuelo)); }

/* Una carta que va de un sitio a otro no se desliza por la mesa: alguien la
   coge, la lleva por el aire y la posa. Eso son cuatro cosas a la vez, y las
   cuatro duran exactamente lo que el vuelo —el JS escribe `--vuelo-ms` en la
   propia carta—, que es lo que faltaba: antes el levantamiento iba por su cuenta
   con una transición de 140 ms, así que la carta seguía subiendo cuando ya había
   llegado y bajaba después de posarse, como si se hundiera en la mesa.

   La curva es la del vuelo (`--vuelo`): si el alzado iba lineal, la carta
   frenaba en el plano y la altura se quedaba fija, como un raíl.

   1. La carta sube y baja (`translate`, que no toca la posición del transform)
      y se ladea un pelo hacia donde va (`--giro`).
   2. Las caras crecen un pelo: lo que está más cerca del ojo se ve más grande.
      Va en las caras y no en la carta porque `scale` se aplica ANTES que
      `transform` y le multiplicaría la posición, con saltos de 22 px.
   3. La sombra se queda en la mesa —se desplaza al revés que la carta— y se
      abre. Es el truco de toda la vida: lo que dice a qué altura está algo no es
      el objeto, es su sombra.
   4. Al llegar, un golpecito. Una carta que se posa hace ruido. */
.anim .card.volando {
  animation: var(--quieto, alzar-carta var(--vuelo-ms, 240ms) var(--vuelo) var(--vuelo-espera, 0ms) both);
}
.anim .card.volando .face, .anim .card.volando .back {
  animation: var(--quieto, alzar-cara var(--vuelo-ms, 240ms) var(--vuelo) var(--vuelo-espera, 0ms) both);
}
.anim .card.volando::after {
  animation: var(--quieto, alzar-sombra var(--vuelo-ms, 240ms) var(--vuelo) var(--vuelo-espera, 0ms) both);
}
@keyframes alzar-carta {
  0% { translate: 0 0; rotate: 0deg; }
  18% { translate: 0 calc(var(--alza, -10px) * 0.72); rotate: calc(var(--giro, 0deg) * 0.65); }
  48% { translate: 0 var(--alza, -10px); rotate: var(--giro, 0deg); }
  78% { translate: 0 calc(var(--alza, -10px) * 0.42); rotate: calc(var(--giro, 0deg) * 0.35); }
  90% { translate: 0 2px; rotate: 0deg; }
  100% { translate: 0 0; rotate: 0deg; }
}
@keyframes alzar-cara {
  0%, 100% { scale: 1; }
  48% { scale: 1.045; }
}
@keyframes alzar-sombra {
  0%, 100% { translate: 0 0; opacity: 0; filter: blur(0); scale: 1 1; }
  18% { translate: calc(var(--giro, 0deg) * 0.4px) calc(0.72 * (3px - var(--alza, -10px))); opacity: .78; filter: blur(1px); scale: 1.01 1.01; }
  48% { translate: calc(var(--giro, 0deg) * 0.9px) calc(3px - var(--alza, -10px) * 1.05); opacity: 1; filter: blur(2.5px); scale: 1.04 1.02; }
  78% { translate: calc(var(--giro, 0deg) * 0.4px) calc(0.42 * (3px - var(--alza, -10px))); opacity: .7; filter: blur(1.2px); scale: 1.01 1.01; }
  90% { translate: 0 1px; opacity: .3; filter: blur(0.4px); scale: 0.99 0.99; }
}

/* La que llevas en la mano se queda levantada mientras la lleves: ahí no hay
   trayecto que acompañar, hay una carta en el aire hasta que la sueltes. */
.card.dragging::after { opacity: 1; translate: var(--quieto, 0 16px); filter: blur(2.5px); }
.anim .card.dragging { translate: var(--quieto, 0 -10px); transition: var(--quieto, translate 90ms var(--vuelo)); }
.card.dragging .face, .card.dragging .back { scale: var(--quieto, 1.04); }
.card.volando, .card.dragging { will-change: transform, translate; }

/* El volteo tampoco es plano: la carta se levanta en Z al darse la vuelta,
   como cuando el pulgar la despega de la mesa. El giro (rotateY) va por
   transición, con una curva que se frena en el canto; el alzado es otra
   propiedad (`translate` en Z), así que puede convivir con el vuelo. */
.anim .card.volteando {
  animation: var(--quieto, voltear-suelo var(--flip-speed) ease-in-out both);
}
.anim .card.volteando .face, .anim .card.volteando .back {
  animation: var(--quieto, voltear-cara var(--flip-speed) ease-in-out both);
}
.anim .card.volteando::after { animation: var(--quieto, voltear-sombra var(--flip-speed) ease-in-out both); }
.anim .card.volando.volteando {
  animation: var(--quieto, alzar-carta var(--vuelo-ms, 240ms) var(--vuelo) var(--vuelo-espera, 0ms) both, voltear-suelo var(--flip-speed) ease-in-out both);
}
.anim .card.volando.volteando .face, .anim .card.volando.volteando .back {
  animation: var(--quieto, alzar-cara var(--vuelo-ms, 240ms) var(--vuelo) var(--vuelo-espera, 0ms) both, voltear-cara var(--flip-speed) ease-in-out both);
}
@keyframes voltear-cara {
  0%, 100% { translate: 0 0 0; filter: brightness(1); }
  50% { translate: 0 0 32px; filter: brightness(0.92); }
}
@keyframes voltear-sombra {
  0%, 100% { translate: 0 0; scale: 1 1; opacity: 0; filter: blur(0); }
  50% { translate: 0 10px; scale: .14 1.03; opacity: .95; filter: blur(2px); }
}
@keyframes voltear-suelo {
  0%, 100% { box-shadow: var(--shadow); }
  50% { box-shadow: 0 1px 3px rgba(0, 0, 0, .1); }
}

/* La pista: la carta que hay que tocar late fuerte; el sitio donde va, flojito.
   El crecer va en las dos caras y no en la carta, por lo mismo de antes: sobre
   la carta, `scale` le movería la posición. Sobre las caras, que están a inset 0
   y solo llevan el giro del volteo, únicamente la agranda.
   Debajo del latido hay un anillo fijo, y distinto para el origen y para el
   destino: mientras late no se ve (una animación gana a la declaración) y sin
   animación se queda puesto, que es la única pista que queda cuando no hay
   movimiento. La clase dura lo mismo que las tres pasadas, así que no se ve el
   relevo.
   El anillo va a dos tonos —ámbar claro por dentro, un filo oscuro por fuera— y
   no de un color solo. El tapete es un degradado que va de #35a066 a #1d7146 en
   claro y de #14663f a #0b3f28 en oscuro: contra ese recorrido ningún color único
   se despega de los dos extremos (el ámbar da 2,4:1 arriba y 4,4:1 abajo, y uno
   oscuro justo al revés). Con los dos filos, el que no contrasta con el fondo lo
   hace con su compañero, que es como se resuelve un indicador sobre fondo
   desconocido. Sin movimiento este anillo es la única pista que queda, así que
   tiene que verse sí o sí. */
.card.hint {
  box-shadow: 0 0 0 4px var(--felt-accent), 0 0 0 6px var(--felt-filo), var(--shadow);
  animation: var(--quieto, pista 800ms linear 3);
}
.card.hint .face, .card.hint .back { animation: var(--quieto, pista-cara 800ms linear 3); }
.card.hint-destino, .slot.hint-destino {
  box-shadow: 0 0 0 3px var(--felt-accent-suave), 0 0 0 5px var(--felt-filo);
  animation: var(--quieto, pista-destino 800ms linear 3);
}
.slot.hint {   /* la pista de robar: late el mazo */
  box-shadow: 0 0 0 4px var(--felt-accent), 0 0 0 6px var(--felt-filo);
  animation: var(--quieto, pista 800ms linear 3);
}
.card.nope { animation: var(--quieto, nope 380ms linear); }
@keyframes nope {
  0%, 100% { translate: 0; rotate: 0deg; box-shadow: var(--shadow); }
  15% { translate: -7px; rotate: -1.5deg; }
  35% { translate: 6px; rotate: 1.2deg; box-shadow: 0 0 0 3px var(--danger), 0 8px 20px rgba(0, 0, 0, .35); }
  55% { translate: -3px; rotate: -0.6deg; }
  75% { translate: 2px; rotate: 0.3deg; }
  90% { translate: -1px; rotate: 0deg; }
}
@keyframes pista {
  0%, 100% { box-shadow: var(--shadow); }
  50% { box-shadow: 0 0 0 4px var(--felt-accent), 0 0 0 6px var(--felt-filo), 0 14px 30px rgba(0, 0, 0, .45); }
}
@keyframes pista-cara {
  0%, 100% { scale: 1; }
  50% { scale: 1.07; }
}
@keyframes pista-destino {
  0%, 100% { box-shadow: var(--shadow); }
  50% { box-shadow: 0 0 0 3px var(--felt-accent-suave), 0 0 0 5px var(--felt-filo); }
}

.card .face, .card .back {
  position: absolute; inset: 0;
  border-radius: var(--radius);
  backface-visibility: hidden;   /* cada cara se esconde sola al darse la vuelta */
  transform: rotateY(0deg);
}
.card .back { transform: rotateY(180deg); }
.card.down .face { transform: rotateY(-180deg); }
.card.down .back { transform: rotateY(0deg); }

.card .face {
  background: var(--card-face);
  border: 1px solid var(--card-line);
  color: var(--card-ink);
  padding: calc(var(--cw) * .055);
  display: grid;
  grid-template-rows: auto 1fr auto;
}
.card.red .face { color: var(--card-red); }
/* Baraja de cuatro colores: los corazones se quedan con el rojo de siempre y
   rombos y tréboles pasan a azul y verde, que es la convención. Así el par de
   palos que en la baraja normal comparte color deja de confundirse. */
html[data-deck="4"] .card[data-suit="D"] .face { color: var(--card-diamond); }
html[data-deck="4"] .card[data-suit="C"] .face { color: var(--card-club); }

.card .back {
  background:
    repeating-linear-gradient(45deg, rgba(255, 255, 255, .11) 0 4px, transparent 4px 8px),
    linear-gradient(150deg, #2f5fa8, #1c3d75);
  border: 1px solid rgba(0, 0, 0, .3);
  box-shadow: inset 0 0 0 3px rgba(255, 255, 255, .14);
}

.corner {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-self: start;
  line-height: .95;
  font-weight: 700;
  font-size: calc(var(--cw) * .23);
  font-variant-numeric: tabular-nums;
}
.corner .suit { font-size: calc(var(--cw) * .2); }
.corner.br { transform: rotate(180deg); justify-self: end; }
.corner .suit, .corner .rank { display: block; }
.pip {
  place-self: center;
  font-size: calc(var(--cw) * .46);
  line-height: 1;
  opacity: .9;
}
.card.court .pip {
  font-size: calc(var(--cw) * .36);
  border: 1.5px solid currentColor;
  border-radius: 6px;
  padding: calc(var(--cw) * .04) calc(var(--cw) * .1);
  font-weight: 700;
  opacity: 1;
}

/* Cartas que quedan por robar: una chapa sobre el montón del mazo. */
.stock-count {
  position: absolute;
  top: 0; left: 0;
  width: var(--cw);
  height: var(--ch);
  display: grid;
  place-items: end center;
  padding-bottom: calc(var(--ch) * .07);
  pointer-events: none;              /* el toque tiene que llegar al mazo */
  z-index: 100;                      /* por encima del montón, por debajo de lo que vuela */
}
.stock-count span {
  padding: 1px 8px;
  border-radius: 999px;
  background: rgba(0, 0, 0, .62);
  border: 1px solid rgba(255, 255, 255, .3);
  color: #fff;
  font-size: calc(var(--cw) * .2);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  line-height: 1.5;
}
/* Al robar cambia un número pequeño en una esquina: sin el brinco nadie se
   entera de que el mazo ha bajado. */
.stock-count.bump span { animation: var(--quieto, bump 320ms linear); }

.banner {
  margin: 10px auto 0;
  width: fit-content;
  min-height: 22px;          /* reserva el hueco para que el tablero no dé saltos */
  padding: 4px 14px;
  border-radius: 999px;
  background: rgba(0, 0, 0, .34);      /* sobre el tapete hace falta fondo para leerlo */
  font-size: 14px;
  color: var(--ink-soft);
  text-align: center;
}
.banner:empty { padding: 0; background: none; }

/* Botón de rematar la partida: solo aparece cuando ya no queda nada que decidir. */
.finish {
  margin: 12px auto 0;
  padding: 11px 22px;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  gap: 9px;
  font: inherit;
  font-size: 15px;
  font-weight: 700;
  color: var(--accent-ink);
  background: var(--accent);
  border: 0;
  border-radius: 999px;
  cursor: pointer;
  box-shadow: 0 6px 20px rgba(0, 0, 0, .32);
  /* Seis latidos para llamar la atención y luego quieto: el botón se queda a la
     vista hasta que lo pulsen y no hace falta que siga moviéndose. */
  animation: var(--quieto, latido 1.9s linear 6);
}
.finish:hover { filter: brightness(1.08); }
.finish:active { transform: translateY(1px); }
.finish:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }
.finish[data-corriendo="si"] { animation: none; background: var(--fill-strong); color: var(--ink); }
@keyframes latido {
  50% { transform: scale(1.045); box-shadow: 0 10px 26px rgba(0, 0, 0, .38); }
}
.banner.warn { color: var(--felt-accent); font-weight: 600; }

/* ---------- diálogos ---------- */

/* Cómo entra y cómo se va un diálogo, en cinco variables. La tarjeta del centro
   aparece desde un pelo más pequeña y transparente; el cajón —el menú siempre, y
   en el móvil todos— sube entero desde el borde de abajo y se va por el mismo
   sitio, opaco de principio a fin, como un cajón de verdad.
   Cerrar es abrir al revés: `panels.js` pone `data-cerrando`, que lo devuelve a
   su sitio de reposo, y no lo cierra de verdad hasta que llega. Antes la salida
   se le dejaba al navegador con `overlay` y `display` en la transición, y eso
   solo lo sabe hacer Chrome: en Safari el menú se esfumaba en el sitio. */
.dlg {
  --dlg-fuera: scale(.96);             /* dónde está cuando no está */
  --dlg-fuera-opacidad: 0;
  --dlg-curva: cubic-bezier(0.16, 1, 0.3, 1);
  --dlg-abre: var(--speed);
  --dlg-cierra: var(--speed);
  --dlg-dura: var(--dlg-abre);
  border: 1px solid var(--panel-line);
  border-radius: 14px;
  background: var(--panel);
  color: var(--panel-ink);
  padding: 20px;
  width: min(94vw, 460px);
  /* Con las tres secciones en uno, el panel puede dar de sí: que se desplace
     por dentro en vez de salirse por abajo de la pantalla. */
  max-height: 88vh;
  overflow-y: auto;
  overscroll-behavior: contain;
  box-shadow: 0 24px 60px rgba(0, 0, 0, .5);
  transition: var(--quieto, opacity var(--dlg-dura) ease-out, transform var(--dlg-dura) var(--dlg-curva));
  opacity: var(--dlg-fuera-opacidad);
  transform: var(--dlg-fuera);
}
.dlg[open] {
  opacity: 1;
  transform: none;
}
@starting-style {
  .dlg[open] {
    opacity: var(--dlg-fuera-opacidad);
    transform: var(--dlg-fuera);
  }
}
.dlg[open][data-cerrando] {
  --dlg-dura: var(--dlg-cierra);
  opacity: var(--dlg-fuera-opacidad);
  transform: var(--dlg-fuera);
  pointer-events: none;                /* lo que se va ya no se toca */
}
.dlg-wide { width: min(94vw, 720px); }
/* El fondo se oscurece al paso del diálogo y se aclara con él. Mientras se tira
   del cajón, `--dlg-fondo` (lo pone panels.js) dice cuánto queda de él: se va
   aclarando según baja. */
.dlg::backdrop {
  background: rgba(0, 0, 0, .55);
  backdrop-filter: blur(2px);
  transition: var(--quieto, opacity var(--dlg-dura) var(--dlg-curva));
  opacity: 0;
}
.dlg[open]::backdrop {
  opacity: var(--dlg-fondo, 1);
}
@starting-style {
  .dlg[open]::backdrop {
    opacity: 0;
  }
}
.dlg[open][data-cerrando]::backdrop {
  opacity: 0;
}
/* El cajón también se cierra tirando de él hacia abajo. El tirador es la rayita
   de arriba que lo dice, y solo la llevan los cajones: el menú siempre, los
   carteles en el móvil (los enciende la regla del móvil, al final); en pantalla
   ancha los carteles son tarjetas en el centro y no hay de qué tirar.
   El dedo solo engancha arriba: el tirador y, en el menú, la cabecera y las
   pestañas. Ahí, y solo ahí, el navegador no se queda el gesto para desplazar
   (`touch-action: none`), porque el cuerpo del menú se tiene que seguir
   desplazando. Un toque en la ✕ o en una pestaña sigue siendo un toque: el
   arrastre no empieza hasta que el dedo baja unos píxeles. */
.dlg-tirador {
  display: none;
  position: absolute;
  top: 0; right: 0; left: 0;
  /* Por encima de lo que cubre: los palos apagados del cartel de bloqueo llevan
     opacidad, y eso los pinta en la capa de lo posicionado, encima del tirador. */
  z-index: 1;
  cursor: grab;
}
/* Ni desplazar ni seleccionar: Chrome vuelve a hacer seleccionable el texto de
   un diálogo modal abierto, aunque el body diga que no, y tirar con el ratón
   desde el título lo iba subrayando entero. */
.dlg-tirador,
#dlg-settings :is(.dlg-head, #panel-tabs) {
  touch-action: none;
  -webkit-user-select: none;
  user-select: none;
}
.dlg-tirador::before {
  content: '';
  position: absolute;
  top: 6px; left: 50%;
  width: 36px; height: 5px;
  margin-left: -18px;
  border-radius: 999px;
  background: var(--panel-ink-soft);
  opacity: .4;
}
/* En el menú es solo la rayita: se tira de la cabecera entera, que la tiene
   debajo. Que no reciba el dedo, porque taparía la ✕: con la pantalla tumbada
   la cabecera se aprieta y la ✕ sube hasta el borde. */
#dlg-settings .dlg-tirador { display: block; height: 0; pointer-events: none; }
/* Mientras se tira, el cajón va pegado al dedo: ninguna transición por medio,
   ni en él ni en el fondo. Al soltar se quita y la transición sale de ahí. */
.dlg[data-arrastrando] { transition: none; cursor: grabbing; }
.dlg[data-arrastrando]::backdrop { transition: none; }
.dlg h2 { margin: 0 0 4px; font-size: 19px; }
.dlg h3 { margin: 18px 0 8px; font-size: 14px; text-transform: uppercase; letter-spacing: .8px; color: var(--panel-ink-soft); }
.dlg p { margin: 6px 0; }
.dlg-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; }
.dlg-actions { display: flex; gap: 8px; flex-wrap: wrap; justify-content: center; margin-top: 18px; }
/* Los carteles que se cierran con la ✕ la llevan sobre el contenido, en la
   esquina: no tienen barra, y una fila entera para un solo botón desequilibraría
   el centrado. Cerrar ya no ocupa sitio entre los botones de verdad. Va por
   encima del tirador, que en el móvil ocupa toda esa franja. */
.dlg-cerrar { position: absolute; top: 8px; right: 8px; z-index: 2; }
/* Una salida manda: la principal a lo ancho y las otras dos debajo, a medias.
   Eran cuatro botones que se partían en dos filas desparejas. */
:is(.dlg-win, .dlg-stuck) .dlg-actions { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
:is(.dlg-win, .dlg-stuck) .dlg-actions .primary { grid-column: 1 / -1; }

/* Lo que pasa dentro del diálogo se contesta dentro del diálogo: el aviso del
   tablero queda detrás y no se lee. */
.dlg-status {
  margin: -4px 0 14px;
  padding: 9px 12px;
  border: 1px solid var(--panel-line);
  border-radius: 9px;
  font-size: 13px;
  color: var(--panel-ink);
}
/* Sin relleno de fondo a propósito: el rojo de `.err` sobre `--fill` se queda en
   3,7:1 en el tema oscuro y sobre el panel a secas llega a 4,9:1.
   Vacío no ocupa, pero sigue en el árbol: si se ocultara del todo, el lector de
   pantalla no cantaría el texto que llega después. */
.dlg-status:empty { margin: 0; padding: 0; border: 0; }
.dlg-status.warn { color: var(--warn); border-color: var(--warn); }
.dlg-status.err { color: var(--danger); border-color: var(--danger); }

.btn {
  padding: 9px 15px;
  min-height: 44px;              /* en un diálogo también se toca con el dedo */
  font: inherit; font-weight: 600;
  color: var(--panel-ink);
  background: var(--fill);
  border: 1px solid var(--panel-line);
  border-radius: 9px;
  cursor: pointer;
  transition: background var(--speed) linear;
}
.btn:hover:not(:disabled) { background: var(--fill-strong); }
/* Un botón apagado tiene que verse apagado: sin esto, «Ir a hoy» con hoy ya
   elegido parecía pulsable y no hacía nada. */
.btn:disabled { opacity: .4; cursor: default; filter: none; }
.btn.primary { background: var(--accent); color: var(--accent-ink); border-color: transparent; }
.btn.primary:hover:not(:disabled) { filter: brightness(1.08); }
.btn.danger { color: var(--danger); border-color: rgba(224, 106, 92, .5); }
.btn.ghost { background: transparent; }
.btn.small { padding: 6px 11px; font-size: 13px; }
/* Los iconos van dibujados, no con glifos del tipo de letra: un ✕ o un ‹ toman
   el grosor y el centrado que quiera la fuente de turno y nunca casan entre sí. */
.icon-btn {
  display: grid; place-items: center;
  background: none; border: 0; color: var(--panel-ink-soft);
  font-size: 17px; cursor: pointer; padding: 4px 8px; border-radius: 7px;
  min-width: 44px; min-height: 44px;
  transition: var(--quieto, background var(--speed) linear, color var(--speed) linear);
}
.icon-btn:hover:not(:disabled) { background: var(--fill); color: var(--panel-ink); }
.icon-btn:disabled { opacity: .35; cursor: default; }

.field { margin: 16px 0; }
.field-label { display: block; font-size: 13px; font-weight: 600; margin-bottom: 7px; }
.field-label em, .switch em { font-style: normal; font-weight: 400; color: var(--panel-ink-soft); }
.hint-text { font-size: 12.5px; color: var(--panel-ink-soft); margin: 6px 0 0; }
/* El error va pegado a su control, no en el aviso de arriba: así se ve a la vez
   qué está mal y dónde se arregla. */
.field-error { margin: 6px 0 0; font-size: 12.5px; font-weight: 600; color: var(--danger); }
.row { display: flex; gap: 8px; }

/* Envuelve: en francés «Automatique / Clair / Sombre» pide 275 px y en una
   pantalla de 320 el grupo se salía del diálogo y sacaba barra horizontal. */
.seg { display: inline-flex; flex-wrap: wrap; background: var(--fill); border: 1px solid var(--panel-line); border-radius: 9px; padding: 3px; gap: 3px; }
.seg-btn {
  padding: 6px 13px; min-height: 44px; font: inherit; font-size: 13px; font-weight: 600;
  color: var(--panel-ink-soft); background: none; border: 0; border-radius: 7px; cursor: pointer;
}
.seg-btn:hover { color: var(--panel-ink); }
.seg-btn[aria-checked="true"] { background: var(--accent); color: var(--accent-ink); }

/* El relleno de arriba y abajo es el que le da la altura de dedo a la fila
   entera, que es lo que se pulsa; la casilla sigue alineada con la primera
   línea, que estos rótulos se van a dos y a tres renglones. */
.switch {
  display: flex; align-items: flex-start; gap: 10px;
  margin: 2px 0; padding: 10px 0; min-height: 44px;
  cursor: pointer; font-size: 14px;
}
.switch input { width: 18px; height: 18px; margin: 1px 0 0; accent-color: var(--accent); cursor: pointer; }

.text-input {
  flex: 1;
  padding: 8px 11px;
  min-height: 44px;
  font: inherit;
  color: var(--panel-ink);
  background: var(--fill);
  border: 1px solid var(--panel-line);
  border-radius: 8px;
}
.text-input::placeholder { color: var(--panel-ink-soft); opacity: .8; }
.text-input:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

.panel { min-height: 0; }
.row.wrap { flex-wrap: wrap; }

/* Las pestañas de sección mandan sobre las de dentro (la modalidad de los
   récords): se dibujan como pestañas de verdad y no como píldoras, para que a
   nadie se le confundan dos filas de lo mismo. */
#panel-tabs {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  flex-shrink: 0;
  gap: 2px;
  margin: 0 var(--menu-pad, 20px);
  border-bottom: 1px solid var(--panel-line);
}
#panel-tabs .tab {
  position: relative;
  min-width: 0;
  padding: 10px 4px;
  font-size: 14px;
  background: none;
  border: 0;
  border-radius: 8px 8px 0 0;
  transition: var(--quieto, color var(--speed) ease-out, background var(--speed) ease-out);
}
/* El subrayado se abre desde el centro al elegir la pestaña, en vez de aparecer
   de golpe: es el único sitio del menú donde algo se mueve solo. */
#panel-tabs .tab::after {
  content: '';
  position: absolute;
  right: 6px; bottom: -1px; left: 6px;
  height: 2.5px;
  border-radius: 2px 2px 0 0;
  background: var(--accent);
  transform: scaleX(0);
  transition: var(--quieto, transform 220ms cubic-bezier(0.16, 1, 0.3, 1));
}
#panel-tabs .tab:hover { background: var(--fill); }
#panel-tabs .tab[aria-selected="true"] { background: none; color: var(--accent); }
#panel-tabs .tab[aria-selected="true"]::after { transform: scaleX(1); }

.tabs { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 14px; }
.tab {
  padding: 6px 12px; min-height: 44px; font: inherit; font-size: 13px; font-weight: 600;
  color: var(--panel-ink-soft); background: var(--fill);
  border: 1px solid var(--panel-line); border-radius: 999px; cursor: pointer;
}
.tab:hover { color: var(--panel-ink); }
.tab[aria-selected="true"] { background: var(--accent); color: var(--accent-ink); border-color: transparent; }

/* ---------- el menú ---------- */
/* Las cuatro secciones viven en un marco que no se mueve: solo el cuerpo se
   desplaza, así que las pestañas, el aviso y el cierre siguen al alcance por
   larga que sea la sección. Las medidas salen de una escala corta y no de un
   número distinto en cada regla: apretado dentro de un grupo, generoso entre
   grupos, y siempre más aire encima de un título que debajo.
   Y es un cajón en cualquier ancho: pegado al borde de abajo, sube entero al
   abrirse y baja por el mismo sitio al cerrarse. En pantalla ancha va centrado
   y con su ancho de siempre, con la mesa a la vista por encima. */
#dlg-settings {
  --menu-pad: 20px;    /* el aire lateral del marco */
  --menu-1: 8px;       /* entre un control y su explicación */
  --menu-2: 16px;      /* entre dos controles del mismo grupo */
  --menu-3: 28px;      /* entre dos grupos */
  --dlg-fuera: translateY(100%);
  --dlg-fuera-opacidad: 1;
  --dlg-curva: var(--cajon-curva);
  --dlg-abre: var(--cajon-abre);
  --dlg-cierra: var(--cajon-cierra);
  margin: auto auto 0;
  padding: 0;
  border-bottom: 0;
  border-radius: 14px 14px 0 0;
  height: min(88vh, 760px);
  height: min(88dvh, 760px);
  max-width: calc(100vw - 24px - var(--safe-left) - var(--safe-right));
  /* La zona segura de abajo va dentro, en el relleno del cuerpo. */
  max-height: calc(100dvh - 24px - var(--safe-top));
  overflow: hidden;
}
#dlg-settings[open] { display: flex; flex-direction: column; }

/* El texto que se selecciona dentro del menú también es del menú: sin esto se
   pinta con el azul del sistema, que no es de nadie. */
#dlg-settings ::selection { background: var(--accent); color: var(--accent-ink); }

#dlg-settings .dlg-head {
  flex-shrink: 0;
  gap: 12px;
  margin: 0;
  padding: 14px var(--menu-pad) 10px;
}
#dlg-settings .dlg-head h2 {
  min-width: 0;
  margin: 0;
  font-size: 22px;
  line-height: 1.25;
  letter-spacing: -.01em;
  overflow-wrap: anywhere;
}
#dlg-settings .dlg-head .icon-btn { display: grid; place-items: center; flex: 0 0 44px; }
#dlg-settings > .dlg-status {
  flex-shrink: 0;
  max-height: 96px;
  overflow-y: auto;
  margin: 12px var(--menu-pad) 0;
}
#dlg-settings > .dlg-status:empty { margin: 0; }

.menu-body {
  flex: 1;
  min-height: 0;
  /* El último renglón queda por encima de la zona segura: el cajón llega al
     borde de la pantalla. */
  padding: var(--menu-3) var(--menu-pad) calc(var(--menu-3) + var(--safe-bottom));
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-gutter: stable;
  scroll-padding-block: 16px;
}

/* La barra de desplazamiento venía con los colores del navegador y se veía. */
.menu-body {
  scrollbar-width: thin;
  scrollbar-color: var(--fill-strong) transparent;
}
.menu-body::-webkit-scrollbar { width: 10px; }
.menu-body::-webkit-scrollbar-track { background: transparent; }
.menu-body::-webkit-scrollbar-thumb {
  background: var(--fill-strong);
  border: 3px solid transparent;
  border-radius: 999px;
  background-clip: content-box;
}

/* Al cambiar de sección el cuerpo se posa; el estado de reposo es el visible,
   así que si se pide quietud no se mueve nada y se ve igual. Solo al cambiar
   (la clase la pone panels.js): al abrir, lo de dentro sube con el cajón y no
   aparece aparte, a medio camino y transparente. */
@keyframes menu-entra {
  from { opacity: 0; transform: translateY(7px); }
}
#dlg-settings .panel.entra:not([hidden]) {
  animation: var(--quieto, menu-entra 240ms cubic-bezier(0.16, 1, 0.3, 1));
}

/* Jerarquía: el título del grupo manda, el rótulo del campo nombra el control y
   la nota explica. Tres tamaños y tres pesos, no cinco. */
#dlg-settings :is(.settings-group > h3, .menu-body h3) {
  margin: var(--menu-3) 0 var(--menu-1);
  font-size: 15px;
  font-weight: 650;
  text-transform: none;
  letter-spacing: 0;
  color: var(--panel-ink);
}
/* Solo pierde el aire de encima el título que abre una sección; el de un
   bloque que viene detrás de otro contenido lo necesita entero. */
#dlg-settings .menu-body > *:first-child,
#dlg-settings .settings-group > h3:first-child { margin-top: 0; }
#dlg-settings .field-label { font-size: 14px; font-weight: 600; margin-bottom: var(--menu-1); }
#dlg-settings .hint-text { font-size: 13px; line-height: 1.55; }

/* Cada grupo es una tarjeta de filas, como en las hojas de ajustes de Peloton,
   Quizlet o Tinder: el título encima, el rótulo a la izquierda y el control o la
   cifra a la derecha, y las notas debajo de la tarjeta. Antes eran rótulos
   sueltos separados por la misma raya que separaba los grupos, y un grupo no se
   distinguía de un campo. La tarjeta es la de la casa —la de la fila de instalar
   y la de las mejores partidas— y la raya entre filas empieza donde empieza el
   texto, no en el borde. */
#dlg-settings .settings-group { margin: var(--menu-3) 0 0; padding: 0; }
#dlg-settings .tarjeta {
  margin: 0;
  padding: 0;
  list-style: none;
  background: var(--panel-2);
  border: 1px solid var(--panel-line);
  border-radius: 10px;
}
#dlg-settings .tarjeta + .tarjeta { margin-top: var(--menu-2); }
#dlg-settings .fila {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  min-height: 48px;
  margin: 0 0 0 14px;
  padding: 12px 14px 12px 0;
  border-top: 1px solid var(--panel-line);
}
#dlg-settings .tarjeta > .fila:first-child,
#dlg-settings .tarjeta > .filas:first-child > .fila:first-child { border-top: 0; }
#dlg-settings .filas { margin: 0; }
#dlg-settings .fila.campo { display: block; }
#dlg-settings .fila.campo .hint-text { margin-top: var(--menu-1); }
#dlg-settings .fila.cabeza { font-weight: 650; }
/* La nota de una tarjeta va debajo y entra a la altura de su texto. */
#dlg-settings .nota-tarjeta { margin: var(--menu-1) 14px 0; }

/* Cifras: el rótulo a la izquierda y la cifra al final de su fila, como las
   estadísticas de (Not Boring) Camera o los valores de Forest y Tiimo. Los dos
   pueden partirse —en Ayuda el rótulo es largo y en el Reto lo es el modo—, pero
   nunca por debajo de su palabra más larga: con `min-width: 0` la versión se
   salía de su fila. */
#dlg-settings .fila.valor > :is(dt, span) { flex: 1 1 auto; font-size: 14px; }
#dlg-settings .fila.valor > :is(dd, strong) {
  flex: 0 1 auto;
  margin: 0;
  font-size: 15px;
  font-weight: 650;
  text-align: right;
  font-variant-numeric: tabular-nums;
}
#dlg-settings .fila.valor dd.win { color: var(--ok); }
/* «sin jugar» viene en minúscula porque antes iba a media frase. */
#dlg-settings .fila.valor dd::first-letter { text-transform: uppercase; }
#dlg-settings .fila-texto { display: grid; gap: 2px; }
#dlg-settings .fila-texto .hint-text { margin: 0; }

#dlg-settings .fila-nombre > span { flex: 0 0 auto; }
#dlg-settings .fila-nombre .text-input { flex: 0 1 58%; max-width: 320px; }

/* Acciones: la fila entera se pulsa y lleva la punta de la casa, como las filas
   de soporte de ClickUp o de Forest. */
#dlg-settings button.fila {
  width: calc(100% - 14px);
  font: inherit;
  color: var(--panel-ink);
  text-align: left;
  background: none;
  border-width: 1px 0 0;
  border-style: solid;
  border-color: var(--panel-line);
  border-radius: 0;
  cursor: pointer;
  transition: var(--quieto, color var(--speed) linear);
}
#dlg-settings .tarjeta > button.fila:first-child { border-top-width: 0; }
#dlg-settings button.fila::after {
  content: '';
  flex: 0 0 8px;
  width: 8px;
  height: 8px;
  margin-right: 4px;
  border-right: 1.5px solid var(--panel-ink-soft);
  border-bottom: 1.5px solid var(--panel-ink-soft);
  transform: rotate(-45deg);
}
#dlg-settings button.fila:hover:not(:disabled) { color: var(--accent); }
#dlg-settings button.fila:disabled { opacity: .4; cursor: default; }
/* Borrar todo va fuera de la tarjeta: el rojo sobre su relleno no llega al 4,5:1
   en el tema oscuro y sobre el panel sí. */
#dlg-settings .borrar-datos { margin-top: var(--menu-2); }

/* La letra de la marca de apolar, la de apolar.co: Space Grotesk fijada en 700 y
   recortada a lo que escribe, «a», «l», «o», «p», «r» y el punto (•). Pesa 1 KB y
   va con su licencia al lado (SIL OFL 1.1). La ruta es relativa a esta hoja, así
   que vale igual en la raíz del dominio que en /solitario/, y el worker la
   precarga: sin conexión el pie se ve igual. Mientras llega, la de reserva. */
@font-face {
  font-family: "apolar wordmark";
  src: url("src/fonts/apolar-wordmark.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

/* Quién lo hace, al pie de Ajustes: la frase con la letra de las notas y la marca
   con la suya, como en apolar.co —negrita, apretada y con su punto— y en la tinta
   del texto y no en la suave, como su logo de tinta sobre papel. Va sobre el panel
   y no en una tarjeta porque al pasar por encima la marca toma el acento, como
   las filas de acción, y en el tema claro el acento llega a 4,9:1 sobre el panel
   pero se queda en 4,4 sobre el relleno de la tarjeta. Centrado, se lee como el
   pie de la sección y no como otra nota de lo de encima. Que es un enlace lo dice
   su letra, no un subrayado ni un color más, y el foco lleva el anillo de la casa.
   Con el interlineado a 1 la marca, más grande, no le da altura a la línea. Su
   objetivo de dedo crece por fuera de la letra, como el del reparto de la
   cabecera: a este tamaño la palabra sola no da para un dedo. */
#dlg-settings .creditos { margin: var(--menu-3) 0 0; text-align: center; }
#dlg-settings .creditos a {
  position: relative;
  font-family: "apolar wordmark", "Space Grotesk", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
  font-size: 1.15em;
  font-weight: 700;
  letter-spacing: -0.085em;
  line-height: 1;
  color: var(--panel-ink);
  text-decoration: none;
  transition: var(--quieto, color var(--speed) linear);
}
#dlg-settings .creditos-punto { font-size: 0.74em; }
#dlg-settings .creditos a::after { content: ''; position: absolute; inset: -14px -10px; }
#dlg-settings .creditos a:hover { color: var(--accent); }
#dlg-settings .creditos a:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }

/* Interruptores dibujados. La casilla del navegador no es de esta interfaz: se
   apaga con appearance y se dibuja el carril y la bola con los mismos tokens
   que el resto, que además funcionan en los dos temas. La fila la pone `.fila`. */
#dlg-settings .switch input {
  appearance: none;
  -webkit-appearance: none;
  position: relative;
  order: 1;
  flex: 0 0 46px;
  width: 46px;
  height: 27px;
  margin: 0;
  background: var(--fill-strong);
  border: 1px solid var(--panel-line);
  border-radius: 999px;
  cursor: pointer;
  transition: var(--quieto, background var(--speed) ease-out, border-color var(--speed) ease-out);
}
#dlg-settings .switch input::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 3px;
  width: 19px;
  height: 19px;
  margin-top: -9.5px;
  background: var(--panel-ink-soft);
  border-radius: 50%;
  box-shadow: 0 1px 2px rgba(0, 0, 0, .3);
  transition: var(--quieto, transform var(--speed) cubic-bezier(0.16, 1, 0.3, 1), background var(--speed) ease-out);
}
#dlg-settings .switch input:checked { background: var(--accent); border-color: transparent; }
#dlg-settings .switch input:checked::after {
  transform: translateX(19px);
  background: var(--accent-ink);
}
#dlg-settings .switch:hover input:not(:checked) { background: var(--fill-strong); border-color: var(--panel-ink-soft); }

/* El grupo de opciones ocupa su columna entera y reparte los botones a partes
   iguales. Nada de partir «Equilibrado» por la mitad: se envuelve por palabras
   o no se envuelve. */
#dlg-settings .seg {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
  width: 100%;
  max-width: 100%;
}
#dlg-settings .seg-btn {
  min-width: 0;
  padding: 8px 6px;
  line-height: 1.3;
  overflow-wrap: normal;
  word-break: normal;
  hyphens: none;
  transition: var(--quieto, background var(--speed) ease-out, color var(--speed) ease-out);
}

#dlg-settings .text-input { min-width: 0; max-width: 100%; caret-color: var(--accent); }
/* La flecha del desplegable la pintaba el navegador, con su gris y su forma. Va
   trazada, no en una imagen: la política de contenido no admite `data:` en
   `img-src` y no se va a abrir por una flecha. Con bordes sale gratis, hereda la
   tinta del tema y es la misma punta que llevan las filas de acción. */
.select-wrap { position: relative; display: flex; min-width: 0; }
.select-wrap::after {
  content: '';
  position: absolute;
  top: 50%;
  right: 14px;
  width: 7px;
  height: 7px;
  margin-top: -6px;
  border-right: 1.7px solid var(--panel-ink-soft);
  border-bottom: 1.7px solid var(--panel-ink-soft);
  transform: rotate(45deg);
  pointer-events: none;
}
#dlg-settings select.text-input {
  appearance: none;
  -webkit-appearance: none;
  padding-right: 36px;
}
#dlg-settings .settings-group .fila.campo :is(.text-input, .select-wrap) { width: 100%; }
#dlg-settings .text-input::placeholder { opacity: 1; }

#dlg-settings :is(.tab, .switch input, .cal-dia, .seg-btn):focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
/* La fila de acción toca los bordes de su tarjeta: el anillo va por dentro. */
#dlg-settings button.fila:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
#dlg-settings .app-row, #dlg-settings .bank-row { flex-wrap: wrap; }
#dlg-settings .app-row .btn { flex: 0 1 auto; max-width: 100%; }
/* El texto corrido se lee a una anchura, no a la que dé el diálogo: a lo ancho
   del panel las reglas y la explicación de la puntuación pasaban de cien
   caracteres por línea y el ojo perdía el renglón al volver. Las tarjetas y sus
   filas quedan fuera: una raya entre filas que se corta antes del borde parece
   un fallo. */
#dlg-settings :is(#panel-ayuda, #panel-reto) :is(p, ul):not(.empty, .tarjeta, .fila),
#dlg-settings .keys { max-width: 68ch; }
/* Las reglas, una por fila de su tarjeta, como las de Rush en Vocabulary. */
#dlg-settings .rules.tarjeta { margin-top: var(--menu-2); line-height: 1.5; }
#dlg-settings .rules.tarjeta .fila { display: block; }

@media (min-width: 641px) {
  #dlg-settings .settings-group .fila.campo {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 340px);
    gap: 4px 24px;
    align-items: center;
  }
  #dlg-settings .settings-group .fila.campo .field-label { grid-column: 1; margin: 0; }
  #dlg-settings .settings-group .fila.campo .hint-text { grid-column: 1; grid-row: 2; margin: 0; }
  #dlg-settings .settings-group .fila.campo :is(.seg, .select-wrap) { grid-column: 2; grid-row: 1 / 3; }
}

/* La evolución de las últimas partidas: una línea sin ejes ni números, que aquí
   solo interesa la forma. */
.spark {
  display: block;
  width: 100%;
  height: 64px;
  margin-top: 4px;
  background: var(--panel-2);
  border: 1px solid var(--panel-line);
  border-radius: 10px;
}
/* El viewBox se estira a lo ancho del panel (preserveAspectRatio="none"), así
   que sin esto el trazo engordaría con él y saldría deforme. */
.spark path, .spark polyline {
  fill: none;
  stroke: var(--accent);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
}
/* El punto de la última partida NO puede ser un <circle>: con
   preserveAspectRatio="none" el viewBox se estira a lo ancho del panel y el
   círculo sale con forma de huevo. Es un trazo de longitud cero con la punta
   redonda y `non-scaling-stroke`, que se pinta en píxeles de pantalla y por eso
   sale redondo mida lo que mida el panel. */
.spark .punto {
  fill: none;
  stroke: var(--accent);
  stroke-width: 7;
  stroke-linecap: round;
  vector-effect: non-scaling-stroke;
}

.app-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-top: 10px;
  padding: 10px 12px;
  background: var(--panel-2);
  border: 1px solid var(--panel-line);
  border-radius: 10px;
}
.app-row-info { min-width: 0; }
.app-row-info strong { font-size: 14px; font-variant-numeric: tabular-nums; }
.app-row .hint-text { margin-top: 2px; }
.app-row .btn { flex: 0 0 auto; }
.settings-install { margin: 0 0 18px; }

.bank-row {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  margin-top: 12px; padding: 9px 12px;
  background: var(--panel-2); border: 1px solid var(--panel-line); border-radius: 10px; font-size: 14px;
}

/* Las mejores partidas, una por bloque. Era una tabla de nueve columnas que en
   un móvil de 390 px medía 871 de ancho: se veían el puesto, los puntos y el
   principio del modo, y al desplazarla para leer el resultado, el tiempo o la
   fecha se perdía de qué partida eran. Ahora cada partida cabe entera: el
   puesto, los puntos y el resultado arriba; la fecha y cómo se jugó debajo; y
   al pie los cuatro valores medidos, con el rótulo encima como en las casillas.
   La lista no lleva caja con scroll propio: corre con el del menú, y así no hay
   dos desplazamientos verticales uno dentro de otro.

   Las columnas son de la lista entera, no de cada partida (subgrid): el tiempo
   de la segunda cae justo debajo del de la primera, como en la tabla, y cada
   columna mide al menos su rótulo más largo, que en portugués es
   «DISTRIBUIÇÃO». Sin subgrid, cada bloque reparte sus cuatro columnas. */
.scores {
  display: grid;
  grid-template-columns: auto repeat(4, minmax(min-content, 1fr));
  column-gap: 12px;
  margin: 0;
  padding: 0;
  list-style: none;
  border: 1px solid var(--panel-line);
  border-radius: 10px;
  overflow: hidden;
  font-size: 13px;
  font-variant-numeric: tabular-nums;
}
.partida {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: auto repeat(4, minmax(0, 1fr));
  grid-template-columns: subgrid;
  align-items: baseline;
  padding: 10px 12px;
  line-height: 1.35;
}
.partida + .partida { border-top: 1px solid var(--panel-line); }
.partida-pos { grid-column: 1; color: var(--panel-ink-soft); }
.partida-puntos { grid-column: 2 / 4; font-size: 18px; font-weight: 650; line-height: 1.2; }
.partida-res { grid-column: 4 / -1; justify-self: end; }
/* Con la clase delante: si no, el margen de `.dlg p` separaba la fecha de sus
   puntos tanto como de la tira de abajo. */
.scores .partida-meta { grid-column: 2 / -1; margin: 2px 0 0; color: var(--panel-ink-soft); }
.partida-datos {
  grid-column: 2 / -1;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  grid-template-columns: subgrid;
  margin: var(--menu-1, 8px) 0 0;
}
.partida-datos dt { font-size: 11px; color: var(--panel-ink-soft); text-transform: uppercase; letter-spacing: .5px; }
.partida-datos dd { margin: 0; white-space: nowrap; }
.scores .win { color: var(--ok); font-weight: 600; }
.scores .loss { color: var(--panel-ink-soft); }
/* La que se acaba de jugar. Con el relleno fuerte, el verde de «Ganada» se
   quedaba en 4,49:1 en el tema claro; sin el rayado de la tabla, el suave ya la
   distingue y deja el verde en 5,1:1 y el texto secundario en 5,2:1. */
.scores .me { background: var(--fill); font-weight: 650; }
.scores .me dt { font-weight: 400; }
.empty { color: var(--panel-ink-soft); font-size: 13.5px; text-align: center; padding: 14px; }

/* ---------- reto diario ---------- */

.cal-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin: 16px 0 8px;
}
/* El navegador escribe «agosto de 2026» en minúscula en español y «August» en
   inglés: la mayúscula la pone el CSS y así los cinco idiomas van igual. Va en
   la primera letra y no en `capitalize`, que pondría «Agosto De 2026». */
.cal-titulo { font-size: 15px; }
.cal-titulo::first-letter { text-transform: uppercase; }

.cal { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; }
/* Las filas existen para el lector de pantalla, no para la vista: `contents` las
   saca del dibujo y deja los días como hijos directos de la cuadrícula. */
.cal-fila { display: contents; }
.cal-cab {
  text-align: center;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  color: var(--panel-ink-soft);
  padding-bottom: 2px;
}
.cal-hueco { visibility: hidden; }

/* Cada día es un botón: se elige, se ve cómo fue y se juega. Un calendario es
   denso por definición, así que aquí el objetivo no llega a los 44 px de la
   barra; a cambio, la casilla entera es pulsable y hay dos botones grandes
   debajo para lo que se hace de verdad. */
.cal-dia {
  position: relative;
  min-height: 40px;
  display: grid;
  place-items: center;
  padding: 6px 0 10px;
  font: inherit;
  font-size: 13.5px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--panel-ink);
  background: var(--fill);
  border: 1.5px solid transparent;
  border-radius: 9px;
  cursor: pointer;
  transition: background var(--speed) linear, color var(--speed) linear;
}
.cal-dia:hover:not(:disabled) { background: var(--fill-strong); }
/* Apagado, no borrado: quitarle el fondo dejaba el mes sin cuadrícula de la
   segunda fila para abajo y las filas descuadradas entre sí. */
.cal-dia:disabled { color: var(--panel-ink-soft); opacity: .45; cursor: default; }
.cal-dia.hoy { border-color: var(--accent); }
.cal-dia.elegido { background: var(--accent); color: var(--accent-ink); border-color: transparent; }
/* Cómo fue ese día: un punto bajo el número. Apagado si se jugó y se perdió,
   verde si se ganó. Un punto no es un idioma, se entiende en los cinco. */
.cal-dia.jugado::after, .cal-dia.ganado::after {
  content: '';
  position: absolute;
  bottom: 5px;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: currentColor;
  opacity: .45;
}
.cal-dia.ganado::after { background: var(--ok); opacity: 1; }
.cal-dia.elegido.ganado::after { background: var(--accent-ink); }

/* El mes, en su tarjeta, y al pie la leyenda de los puntos, como el calendario
   de Centr. Los puntos de la leyenda son los mismos de las casillas. */
#dlg-settings .cal-tarjeta { margin-top: var(--menu-2); padding: 4px 12px 12px; }
#dlg-settings .cal-tarjeta .cal-nav { margin: 0 0 6px; }
.cal-leyenda {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 18px;
  padding: 10px 2px 0;
  border-top: 1px solid var(--panel-line);
  font-size: 13px;
  color: var(--panel-ink-soft);
}
.dlg .cal-leyenda { margin: 12px 0 0; }   /* `.dlg p` le pondría el suyo */
.cal-leyenda span { display: inline-flex; align-items: center; gap: 7px; }
.cal-leyenda span::before {
  content: '';
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--panel-ink);
  opacity: .45;
}
.cal-leyenda .ganado::before { background: var(--ok); opacity: 1; }

/* El día elegido: la fecha encima de su tarjeta. «sábado, 29 de agosto» empieza
   en minúscula en español y en mayúscula en inglés: se iguala por la primera
   letra, no por palabra. */
.reto-fecha::first-letter { text-transform: uppercase; }
#dlg-settings #panel-reto > .row.wrap { margin-top: var(--menu-2); }
#dlg-settings .tarjeta-reparto { margin-top: var(--menu-3); }

.dlg-win { text-align: center; width: min(94vw, 400px); }
.win-art { font-size: 34px; letter-spacing: 8px; margin-bottom: 6px; }
/* Cuatro cajas en fila se quedan en 80 px y «Puntuación» ya no cabe: van en dos
   y dos, que además es la misma retícula en el móvil más estrecho y en el panel
   ancho. */
.win-stats { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; margin: 16px 0 0; }
.win-stats > div { background: var(--panel-2); border: 1px solid var(--panel-line); border-radius: 10px; padding: 9px; }
.win-stats dt { font-size: 11px; color: var(--panel-ink-soft); text-transform: uppercase; }
.win-stats dd { margin: 2px 0 0; font-size: 20px; font-weight: 700; font-variant-numeric: tabular-nums; }
.win-note { color: var(--accent); font-weight: 700; min-height: 20px; }

/* El reto del día se anuncia arriba del todo y en dorado. Ganar el reto no es
   ganar una partida cualquiera: es la mano que hoy juega todo el mundo, y es lo
   primero que tiene que verse, tanto aquí como en la tarjeta que se comparte. */
.win-reto {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 8px;
  margin: 0 0 10px;
  /* Va a la altura de la ✕: deja libre su hueco (8 + 44 − 20 de relleno) a los
     dos lados, y así la chapa y la fecha siguen centradas sin meterse debajo. */
  padding-inline: 32px;
}
.win-reto-chapa {
  padding: 4px 12px;
  border-radius: 999px;
  background: var(--accent);
  color: var(--accent-ink);
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .06em;
}
.win-reto-fecha { color: var(--accent); font-weight: 700; font-size: 13px; }
.win-reto-fecha::first-letter { text-transform: uppercase; }
/* Los palos se tiñen del mismo dorado: el cartel entero cambia de tono. */
.dlg-win[data-reto] .win-art { color: var(--accent); }

/* Lo que pasa al compartir se cuenta aquí mismo: el banner del tablero queda
   detrás del diálogo y no se lee. Sin texto no ocupa nada. */
.win-share-nota { margin: 12px 0 0; font-size: 13px; color: var(--panel-ink-soft); }
.win-share-nota:empty { display: none; }

/* ---------- invitación al reto del día ---------- */
/* Mismo aire que el cartel de victoria: centrado, con su palo arriba y estrecho,
   para que se lea de un vistazo y se cierre sin pensarlo. */
.dlg-invita { text-align: center; width: min(94vw, 400px); position: relative; }
.invita-art { font-size: 34px; letter-spacing: 8px; margin-bottom: 6px; color: var(--accent); }
.invita-fecha { margin: 0; font-size: 12.5px; color: var(--panel-ink-soft); }
.invita-fecha::first-letter { text-transform: uppercase; }
.invita-texto { color: var(--panel-ink-soft); }

.dlg-stuck { text-align: center; width: min(94vw, 420px); }
.stuck-art { font-size: 34px; letter-spacing: 8px; margin-bottom: 6px; opacity: .4; filter: grayscale(1); }
.stuck-note { color: var(--panel-ink-soft); }

.rules { margin: 8px 0; padding-left: 20px; }
.rules li { margin: 5px 0; }
/* Un atajo se lee mejor si parece una tecla. El monoespaciado sobraba: esto no
   es código, son los nombres de las teclas y van en la letra de la interfaz. */
.keys { display: grid; grid-template-columns: auto 1fr; gap: 8px 14px; margin: 0; font-size: 13.5px; align-items: center; }
.keys > div { display: contents; }
.keys dt {
  justify-self: start;
  padding: 4px 9px;
  min-width: 32px;
  text-align: center;
  font-size: 12.5px;
  font-weight: 650;
  font-variant-numeric: tabular-nums;
  color: var(--panel-ink);
  background: var(--fill);
  border: 1px solid var(--panel-line);
  border-bottom-width: 2px;
  border-radius: 6px;
}
.keys dd { margin: 0; color: var(--panel-ink-soft); }

/* ---------- confeti y celebración ---------- */

.confetti {
  display: var(--quieto, block);
  position: fixed; inset: 0;
  pointer-events: none;
  overflow: hidden;
  z-index: 130;
  transition: opacity 400ms ease-out;
}
.confetti.fading { opacity: 0; }
.win-canvas {
  display: var(--quieto, block);
  position: fixed; inset: 0;
  width: 100vw; height: 100vh;
  pointer-events: none;
  z-index: 120;
}
.win-canvas.activa {
  pointer-events: auto;
  cursor: pointer;
}
/* Botón de omitir la fiesta de la cascada, en la esquina de abajo a la derecha:
   quien ya vio la animación mil veces quiere llegar a la ventana de victoria.
   Solo vive mientras la cascada corre; panels.js lo enciende y lo apaga. */
.btn-skip-win {
  position: fixed; right: max(14px, var(--safe-right)); bottom: calc(14px + var(--safe-bottom));
  z-index: 125;
  padding: 8px 16px;
  border: 1px solid rgba(0, 0, 0, .25);
  border-radius: 999px;
  background: rgba(0, 0, 0, .5);
  color: #fff;
  font: inherit;
  font-size: 13px;
  font-weight: 700;
  cursor: pointer;
  box-shadow: 0 2px 8px rgba(0, 0, 0, .3);
}
.btn-skip-win:hover { background: rgba(0, 0, 0, .68); }
.btn-skip-win[hidden] { display: none; }
.confetti i {
  position: absolute; top: -14px;
  width: 9px; height: 14px; border-radius: 2px;
  box-shadow: 0 2px 5px rgba(0, 0, 0, .25);
  animation: var(--quieto, fall cubic-bezier(.25, .46, .45, .94) forwards);
}
/* La deriva y el giro los reparte panels.js pieza a pieza: si todas cayeran en
   vertical y con la misma vuelta, setenta papelitos parecerían una persiana.
   El aleteo en 3D (rotateY) simula la resistencia del aire sobre el papel. */
@keyframes fall {
  0% { transform: translate(0, -10px) rotate(0deg) rotateY(0deg); opacity: 1; }
  35% { transform: translate(calc(var(--deriva, 0) * 0.45), 34vh) rotate(calc(var(--giro, 760deg) * 0.35)) rotateY(180deg); }
  70% { transform: translate(calc(var(--deriva, 0) * 0.8), 72vh) rotate(calc(var(--giro, 760deg) * 0.7)) rotateY(360deg); }
  100% { transform: translate(var(--deriva, 0), 102vh) rotate(var(--giro, 760deg)) rotateY(540deg); opacity: .1; }
}

/* Móvil apaisado: 844x390 es ancho, así que la compactación de arriba no entra,
   pero de alto va justísimo. Con la cabecera de la 1.5 —chip de modalidad, caja
   de fundaciones y barra de progreso— el tablero se quedaba 69 px corto y seis
   cartas caían por debajo del borde, fuera del alcance del dedo (el tapete no se
   desplaza a propósito). Aquí la cabecera se aprieta y la barra de progreso deja
   de gastar una línea entera: se ancla al borde de abajo. */
@media (max-height: 520px) {
  /* Con la pantalla tumbada el marco se aprieta por la escala, no regla a
     regla: así el ritmo de dentro se encoge entero y no a trozos. */
  #dlg-settings { --menu-2: 12px; --menu-3: 18px; }
  #dlg-settings .dlg-head { padding-top: 4px; padding-bottom: 0; }
  #dlg-settings .menu-body { padding-top: 12px; padding-bottom: calc(12px + var(--safe-bottom)); }
  #dlg-settings .fila { min-height: 42px; padding-top: 8px; padding-bottom: 8px; }
  /* Una sola fila, con la barra de progreso pegada al borde de abajo. */
  .topbar {
    grid-template-areas: "marca marcador izq chip der";
    gap: 6px 10px;
    padding-top: 4px;
    padding-bottom: 4px;
    --bar-alto: 40px;
  }
  .brand h1 { display: none; }
  .mode-chip { min-height: 28px; padding: 4px 10px; }
  .update-pill { min-height: 26px; padding: 3px 9px; }
  .update-texto { display: none; }
  .stat { padding: 0 6px; }
  .stat dd { font-size: 14px; }
  .progress { grid-area: auto; position: absolute; left: 0; right: 0; bottom: 0; border-radius: 0; }
  .table { padding-top: 6px; padding-bottom: 4px; }
  .banner { margin-top: 6px; }
  .tool { --tool-alto: 40px; }
  .tools { padding-top: 5px; }
}

@media (max-width: 640px) {
  /* En el móvil el menú y los carteles son una hoja pegada al borde de abajo, a
     todo lo ancho, y la mesa se sigue viendo encima: detrás de «La partida está
     cerrada» quedan a la vista las cartas que ya no tienen jugada. El modal va
     con inset 0; el margen de arriba en auto lo deja caer. Todos son cajones:
     suben al abrirse en vez de crecer desde el centro, y bajan al cerrarse. */
  .dlg {
    --dlg-fuera: translateY(100%);
    --dlg-fuera-opacidad: 1;
    --dlg-curva: var(--cajon-curva);
    --dlg-abre: var(--cajon-abre);
    --dlg-cierra: var(--cajon-cierra);
    width: 100%;
    max-width: none;
    margin: auto 0 0;
    padding-bottom: calc(20px + var(--safe-bottom));
    border-bottom: 0;
    border-radius: 14px 14px 0 0;
  }
  /* Los carteles no tienen cabecera: su tirador es una franja a lo ancho que
     cubre el aire de arriba y la fila de palos de adorno (20 px de relleno y
     una línea de casi 50), y acaba justo antes del título. La ✕ de la esquina
     queda por encima, así que se sigue pulsando. */
  .dlg-tirador { display: block; height: 70px; }
  /* El menú conserva su alto, así que arriba queda una franja de mesa. Sin pie
     ni «Listo»: se cierra con la ✕ de la cabecera. */
  #dlg-settings {
    --menu-pad: 16px;
    --menu-3: 24px;
    height: min(92dvh, 760px);
    max-width: none;
  }
  #dlg-settings .dlg-head h2 { font-size: 20px; }
  #panel-tabs { margin-right: var(--menu-pad); margin-left: var(--menu-pad); }
  #panel-tabs .tab { font-size: 12px; }
  #dlg-settings .app-row .btn { width: 100%; }
  #dlg-settings #stats-tabs { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
  #dlg-settings #stats-tabs .tab { padding: 8px; line-height: 1.3; }
  /* Ojo: sin repetir aquí la zona segura, el `padding: 8px` se comía la del
     iPhone y la barra se metía debajo del reloj y la muesca. */
  .topbar {
    /* Tres filas: Nueva, el chip y Repetir con el menú; el marcador; el
       progreso. Las dos columnas de los lados miden lo mismo (al menos los dos
       iconos de la derecha), así el chip queda centrado en la pantalla. */
    grid-template-columns: minmax(88px, 1fr) minmax(0, auto) minmax(88px, 1fr);
    grid-template-areas:
      "izq chip der"
      "marcador marcador marcador"
      "progreso progreso progreso";
    gap: 4px 0;
    padding: 2px max(4px, var(--safe-right)) 8px max(4px, var(--safe-left));
  }
  /* En iOS un fondo fijo obliga a repintar el degradado entero cada vez que algo
     se desplaza, y en una pantalla de móvil no se gana nada: no hay recorrido
     donde notar que el tapete se queda quieto. */
  body { background-attachment: scroll; }
  /* Y por lo mismo, fuera el desenfoque: es el efecto más caro de la pantalla y
     el que peor lleva un móvil justo de fuelle. Las dos barras se pintan con el
     verde opaco de la cabecera, así que quitarlo no cambia lo que se ve. */
  .topbar, .tools { backdrop-filter: none; -webkit-backdrop-filter: none; }
  /* La marca no entra en el móvil: la primera fila es para Nueva, el chip y los
     dos iconos de la derecha. */
  .brand { display: none; }
  .barra-der { justify-self: end; }
  .mode-chip { padding-inline: 10px; }
  .scoreboard { justify-self: stretch; gap: 0; }
  .stat dt { letter-spacing: .3px; }
  .progress { margin: 0 4px; }
  .table { padding: 8px max(4px, var(--safe-right)) 6px max(4px, var(--safe-left)); }
  .tool { max-width: none; }
  /* En pantalla estrecha los dos botones de la invitación se apilan a lo ancho:
     el primario arriba, que es el que se busca. */
  .dlg-invita .dlg-actions .btn { flex: 1 1 100%; }
}

/* Por debajo de 360 no caben dos columnas iguales de 88 px y el chip entero: los
   lados miden lo que su contenido y el chip se queda con el resto, aunque se
   desplace un poco del centro. */
@media (max-width: 359px) {
  .topbar { grid-template-columns: auto minmax(0, 1fr) auto; }
}

@media (max-width: 340px) {
  /* En las pantallas más estrechas el rótulo del marcador no baja de 11 px: el
     sitio sale del relleno lateral y del espaciado entre letras, que es adorno.
     Cortar «JUGADAS» con puntos suspensivos sería peor que tenerlo pequeño. */
  .stat { padding-inline: 3px; }
  .stat dt { letter-spacing: 0; }
  #panel-tabs .tab { font-size: 11px; padding-inline: 2px; }
  #dlg-settings .seg:has([data-pref="dealBias"]) { grid-auto-flow: row; }
  /* Con 320 px, en portugués «DISTRIBUIÇÃO» no cabía en la tira de cada partida.
     Lo mismo que en el marcador: el sitio sale del relleno, del espacio entre
     columnas y del espaciado entre letras, no del tamaño del rótulo. */
  .scores { column-gap: 8px; }
  .partida { padding-inline: 8px; }
  .partida-datos dt { letter-spacing: 0; }
}
