Consolida los tokens visuales y armoniza la tipografía de Juegoland

main
dev 4 days ago
parent c66accfd40
commit c9c396187d

@ -46,6 +46,7 @@ La estructura relacional incluye usuarios, sesiones, salas/partidas, eventos, me
```sh
npm run check
npm run design:check
npm test
npm run build
```
@ -74,4 +75,4 @@ Modalidades casuales, sin reloj, ranking, torneos, bots, espectadores ni penaliz
Antes de abrir registro público quedan verificación y recuperación por correo (requieren proveedor SMTP), moderación/bloqueo/reportes, gestión de cuentas y políticas de conservación de datos. La versión está instalada en la VM con PostgreSQL y systemd. El acceso público requiere conectar DNS y el proxy HTTPS a juegoland.imaginacion.com; consulta deploy/server-status.md.
La guía de instalación está en [deploy/README.md](deploy/README.md). La dirección visual y el prompt para otra herramienta están en [docs/design.md](docs/design.md).
La guía de instalación está en [deploy/README.md](deploy/README.md). La dirección visual y el prompt para otra herramienta están en [docs/design.md](docs/design.md). Los tokens, sus roles y la política de estilos están en [docs/design-system.md](docs/design-system.md). La auditoría `design:check` detecta valores de tipografía, espaciado y radios escritos a mano y variables CSS sin definir; también se ejecuta antes de activar un despliegue.

@ -26,6 +26,7 @@ local_address=$(ip -4 -o addr show scope global | awk '{split($4,a,"/");print a[
test -n "$local_address"
runuser -u mesa -- env HOME=/var/lib/mesa-abierta npm config set local-address "$local_address" --location=user
runuser -u mesa -- env HOME=/var/lib/mesa-abierta NODE_OPTIONS=--dns-result-order=ipv4first npm ci --local-address="$local_address" --no-audit --no-fund
runuser -u mesa -- npm run design:check
runuser -u mesa -- npm run check
runuser -u mesa -- npm test -- --maxWorkers=1
runuser -u mesa -- npm run build

@ -1,6 +1,6 @@
# Servidor de Juegoland
Versión 20261004-08. Web, API y WebSockets en mesa-game, 0.0.0.0:3000.
Versión 20261004-10. Web, API y WebSockets en mesa-game, 0.0.0.0:3000.
El proxy Nginx existente de la red apunta a http://192.168.18.155:3000.
Nginx y el antiguo servicio mesa-web están deshabilitados en la VM.
@ -12,4 +12,6 @@ El popup breve llega abierto en el HTML del servidor y pasa a modal nativo al in
El despliegue conserva los archivos inmutables del cliente anterior para que las pestañas abiertas puedan seguir cargando sus componentes.
Damas mantiene la ficha seleccionada durante capturas encadenadas y permite recorrido diagonal largo de las damas. Las partidas existentes siguen disponibles.
La interfaz consume `src/tokens.css`: tipografía proporcional de 16 a 18 px, espaciados, radios, sombras y colores de claro/oscuro y de las tres paletas. Los estilos globales se concentran en `src/app.css`; la geometría de los tableros permanece en sus componentes. `npm run design:check` se ejecuta durante el despliegue. Se revisaron catálogo, perfil, menús, invitaciones, avatar y salas en el navegador integrado, con anchos de 390, 565, 1440 y 3818 px. Los nombres y datos de partidas comparten escala en catálogo, perfil y panel lateral; la navegación inferior se ajustó a su altura real.
PostgreSQL permanece en loopback, con base y rol dedicados. Credenciales y datos existentes se conservan. APT y npm operan por IPv4. Copias diarias activas a las 03:15 Europe/Madrid.

@ -0,0 +1,33 @@
# Sistema visual de Juegoland
La identidad la llevan el logo multicolor y los tableros. La interfaz acompaña con una sola familia, controles legibles y alineaciones compartidas. Esta consolidación mantiene las paletas existentes; concentra sus valores y retira las correcciones CSS duplicadas.
## Tokens
`src/tokens.css` define los valores compartidos. La base clara conserva fondo #f5f7fc, superficie #ffffff, texto #202c41, texto secundario #58677e, acento #0759d9 y borde #dde4f0. Los modos claro, oscuro y sistema y las paletas azul, bosque y violeta consumen los mismos nombres semánticos.
Manrope se usa para títulos y texto. El cuerpo aumenta gradualmente de 16 a 18 px con el ancho disponible; los demás roles derivan de él. Un título de tarjeta usa `--type-subtitle`, una sección `--type-section`, el nombre de una partida `--type-body` y sus datos `--type-secondary`. El texto auxiliar usa `--type-detail`. El cambio de ancho no cambia estos roles.
Los espaciados siguen una base de 4 px: 4, 8, 12, 16, 20, 24, 32, 40, 48 y 64. Hay 2 px para separaciones pequeñas. Los radios distinguen controles, tarjetas, paneles y diálogos; las sombras distinguen controles, tarjetas y capas flotantes.
## Composición
El catálogo alinea título, descripción, datos y acción dentro de cada tarjeta. Las partidas pendientes y las salas comparten tamaños de nombre y metadatos. El texto de lectura se limita a 70 caracteres de ancho. En una sala, el tablero ocupa el espacio disponible y los controles y el chat mantienen una escala de interfaz propia.
```text
Escritorio Móvil
menú | contenido alineado cabecera
| tarjetas y partidas tarjeta
| tarjeta
diálogo centrado diálogo con márgenes laterales
```
La marca y los tableros concentran el color y el detalle. Los controles comparten alineación, foco visible y tamaño táctil. Las animaciones de juego conservan sus reglas y respetan movimiento reducido.
## Mantenimiento
`src/app.css` contiene los estilos globales consolidados en el orden necesario para preservar la cascada. Los estilos locales de Svelte consumen los mismos tokens. No se crean hojas de correcciones superpuestas.
Las coordenadas SVG, el recorrido de los tableros, los puntos y caras del dado y la geometría de las ilustraciones son valores de dibujo: se mantienen en sus componentes. Los puntos de ruptura permanecen como condiciones CSS explícitas, porque las variables CSS no se pueden usar directamente en las condiciones de `@media`.
Antes de publicar: ejecutar `npm run design:check`, las comprobaciones del proyecto y revisar catálogo, perfil, diálogos y sala en móvil y escritorio, tanto en claro como en oscuro. La auditoría exige tokens para tamaños, familia, peso, interlineado y espaciado tipográfico; también revisa márgenes, rellenos, separaciones, radios y referencias a variables CSS.

1
package-lock.json generated

@ -21,6 +21,7 @@
"@sveltejs/vite-plugin-svelte": "^7.3.1",
"@types/node": "^24.0.0",
"@types/pg": "^8.15.5",
"postcss": "8.5.28",
"prettier": "^3.9.9",
"prettier-plugin-svelte": "^4.1.1",
"svelte": "^5.57.1",

@ -14,6 +14,7 @@
"build": "vite build",
"preview": "vite preview --host 127.0.0.1",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json && tsc --project tsconfig.server.json --noEmit",
"design:check": "node scripts/check-design-tokens.mjs",
"test": "vitest run",
"start": "node --env-file-if-exists=.env server/production.mjs",
"db:migrate": "node --env-file-if-exists=.env scripts/migrate.mjs"
@ -32,6 +33,7 @@
"@sveltejs/vite-plugin-svelte": "^7.3.1",
"@types/node": "^24.0.0",
"@types/pg": "^8.15.5",
"postcss": "8.5.28",
"prettier": "^3.9.9",
"prettier-plugin-svelte": "^4.1.1",
"svelte": "^5.57.1",

@ -0,0 +1,98 @@
import { readdir, readFile } from 'node:fs/promises';
import { join } from 'node:path';
import postcss from 'postcss';
const problems = [];
let checked = 0;
const definitions = new Set();
const references = [];
const drawingFiles = new Set(['GameArt.svelte', 'ParchisMap.svelte']);
const drawingSelector =
/\.(?:chess-piece|checker-piece|coordinate|parchis-token|dice-face|dice-cube|dice-stage|dice-shadow|face-\d|dice-cup|cup-shadow|throw-scene|go-target)\b/;
async function inspect(path, css) {
const file = path.split(/[\\/]/).at(-1);
if (file === 'tokens.css' || drawingFiles.has(file)) return;
const parsed = postcss.parse(css, { from: path });
parsed.walkDecls((d) => {
let inKeyframes = false;
for (let parent = d.parent; parent; parent = parent.parent)
if (parent.type === 'atrule' && /keyframes$/.test(parent.name))
inKeyframes = true;
const selector = d.parent.type === 'rule' ? d.parent.selector : '';
if (
inKeyframes ||
drawingSelector.test(selector) ||
(file === 'Dice.svelte' && /^i(?:[.:]|$)/.test(selector)) ||
d.prop.startsWith('--')
)
return;
checked++;
const value = d.value.replace(/\b0(?:px|rem)\b/g, '0');
const absoluteSize = /\b\d+(?:\.\d+)?(?:px|rem)\b/;
if (
(d.prop === 'font-size' || d.prop === 'font') &&
!value.includes('var(') &&
!/^(?:0|inherit|initial|unset)$/.test(value)
)
problems.push(
`${path}:${d.source.start.line} — ${selector}: use a type token for ${d.prop}`,
);
if (
/^(?:font-weight|line-height|letter-spacing|font-family)$/.test(d.prop) &&
!value.includes('var(') &&
!/^(?:inherit|initial|unset|normal)$/.test(value)
)
problems.push(
`${path}:${d.source.start.line} — ${selector}: use a typography token for ${d.prop}`,
);
if (
/^(?:padding|margin)(?:-.+)?$|^(?:gap|row-gap|column-gap)$/.test(
d.prop,
) &&
absoluteSize.test(value)
)
problems.push(
`${path}:${d.source.start.line} — ${selector}: use a spacing token for ${d.prop}`,
);
if (d.prop === 'border-radius' && absoluteSize.test(value))
problems.push(
`${path}:${d.source.start.line} — ${selector}: use a radius token`,
);
});
}
function collect(path, source) {
for (const match of source.matchAll(/(--[\w-]+)\s*:/g))
definitions.add(match[1]);
for (const match of source.matchAll(/style:(--[\w-]+)/g))
definitions.add(match[1]);
for (const match of source.matchAll(/var\((--[\w-]+)\s*\)/g))
references.push({ path, name: match[1] });
}
async function scan(directory) {
for (const entry of await readdir(directory, { withFileTypes: true })) {
const path = join(directory, entry.name);
if (entry.isDirectory()) await scan(path);
else if (entry.name.endsWith('.css')) {
const source = await readFile(path, 'utf8');
collect(path, source);
await inspect(path, source);
} else if (entry.name.endsWith('.svelte')) {
const source = await readFile(path, 'utf8');
collect(path, source);
const style = source.match(/<style[^>]*>([\s\S]*?)<\/style>/);
if (style) await inspect(path, style[1]);
}
}
}
await scan('src');
for (const { path, name } of references)
if (!definitions.has(name))
problems.push(`${path} — undefined CSS variable ${name}`);
if (problems.length) {
console.error(problems.join('\n'));
process.exitCode = 1;
} else
console.log(
`Design tokens: ${checked} UI declarations checked; no hardcoded type, spacing or radius values.`,
);

File diff suppressed because it is too large Load Diff

@ -1,570 +0,0 @@
.brand {
flex-direction: column;
align-items: flex-start;
gap: 9px;
margin: 0 0 38px;
letter-spacing: 0;
}
.brand-logo {
display: block;
width: 100%;
height: auto;
aspect-ratio: 897 / 214;
object-fit: cover;
background: transparent;
border-radius: 0;
}
.brand small {
color: var(--secondary);
font:
500 10px 'DM Sans',
sans-serif;
letter-spacing: 0.3px;
margin-left: 6px;
}
.mobile-logo {
display: none;
}
.sidebar,
.game-card,
.hero-note,
.join-code,
.lobby-table,
.continue-card,
.modal,
.auth-layout,
.stat-card,
.help-card,
.room-panel,
.parchis-board,
.promotion,
.room-code {
background: var(--surface);
border-color: var(--border);
}
.topbar {
background: var(--background);
}
.sidebar-invite,
.filter-tabs,
.difficulty,
.lobby-table-head,
.chat-form,
.variant-note,
.parchis-home,
.promotion button {
background: var(--soft);
border-color: var(--border);
}
.hero,
.auth-art,
.profile-header,
.help-intro,
.waiting-card,
.result-banner {
background: var(--hero);
border-color: var(--border);
}
.sidebar nav a.active,
.sidebar nav a:hover,
.turn-indicator {
background: var(--soft);
color: var(--accent-text);
}
.hero h1 {
color: var(--text);
}
.hero h1 em,
.hero-note > span,
.game-play,
.lobby-join,
.bottom-note > a,
.sidebar-invite a,
.waiting-symbol,
.room-code,
.hero-star {
color: var(--accent-text);
}
.hero-loop {
border-color: var(--border);
}
.sidebar-label,
.eyebrow,
.sidebar nav a:not(.active),
.sidebar-invite p,
.sidebar-bottom > p,
.user-card small,
.logout,
.header-search,
.header-search input::placeholder,
.connection,
.hero-eyebrow,
.hero-copy > p,
.hero-caption,
.hero-note small,
.welcome-strip,
.live-count,
.filter-tabs button,
.game-card-body > p,
.difficulty,
.game-meta,
.card-bottom > a,
.join-code,
.join-code input::placeholder,
.lobby-table-head,
.lobby-game small,
.player-stack small,
.status-badge,
.empty-lobby p,
.empty-panel,
.bottom-note > span,
.bottom-note strong,
.bottom-note p,
.continue-card small,
.site-footer,
.muted,
.modal label,
.auth-art p,
.auth-form > p,
.auth-tabs button,
.auth-note,
.page-heading p,
.profile-header p,
.stat-card span,
.stat-card small,
.history-item small,
.help-card ol,
.help-intro p,
.room-page-heading p,
.breadcrumb,
.player-banner small,
.board-note,
.chat-empty,
.chat-message time,
.room-rules,
.event-list,
.event-list span,
.event-list small,
.waiting-card > p,
.waiting-player small,
.result-banner p,
.parchis-controls p,
.parchis-home strong,
.home-pieces small {
color: var(--secondary);
}
.header-search input,
.modal input,
.modal select,
.modal textarea,
.auth-form input,
.chat-form input {
color: var(--text);
}
input:not([type='range']),
select,
textarea {
background: var(--surface);
border-color: var(--border);
color: var(--text);
}
input::placeholder,
textarea::placeholder {
color: var(--secondary);
opacity: 0.8;
}
.button.secondary,
.icon-button:hover,
.filter-tabs button.chosen {
background: var(--surface);
border-color: var(--border);
color: var(--accent-text);
}
.button.primary,
.chat-form button {
background: var(--accent);
color: #fff;
}
.button.primary:hover:not(:disabled) {
background: var(--accent-hover);
}
.button.danger,
.form-error {
color: var(--danger-text);
background: var(--danger-bg);
border-color: var(--danger-text);
}
.card-bottom,
.lobby-table-head,
.lobby-row,
.event-list > div {
border-color: var(--border);
}
.stat-card strong {
color: var(--text);
}
.stat-card:nth-child(2) strong,
.result-tag.win {
color: var(--accent-text);
}
.result-tag {
color: var(--secondary);
background: var(--soft);
}
.result-tag.loss {
color: var(--danger-text);
background: var(--danger-bg);
}
.avatar,
.mini-avatar {
background: var(--soft);
color: var(--accent-text);
border-color: var(--surface);
}
.settings-trigger {
display: grid;
place-items: center;
width: 38px;
height: 38px;
padding: 0;
border: 1px solid var(--border);
border-radius: 12px;
background: var(--surface);
color: var(--secondary);
transition:
background 0.15s,
transform 0.15s,
color 0.15s;
}
.settings-trigger:hover {
color: var(--accent-text);
background: var(--soft);
transform: translateY(-1px);
}
.settings-popover {
position: fixed;
inset: 68px 28px auto auto;
margin: 0;
width: 310px;
max-width: calc(100vw - 24px);
padding: 22px;
border: 1px solid var(--border);
border-radius: 18px;
color: var(--text);
background: var(--surface);
box-shadow: 0 16px 54px var(--shadow);
}
.settings-popover:popover-open {
animation: panel-enter 0.18s ease-out;
}
.settings-popover > strong {
display: block;
font:
750 18px 'Manrope',
sans-serif;
margin-bottom: 20px;
}
.settings-label {
display: block;
font-size: 12px;
font-weight: 600;
color: var(--secondary);
margin: 18px 0 9px;
}
.theme-options,
.palette-options {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 7px;
}
.theme-options button,
.palette-options button {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 8px;
padding: 12px 4px;
min-width: 0;
border: 1px solid var(--border);
border-radius: 11px;
background: var(--surface);
color: var(--secondary);
font-size: 11px;
}
.theme-options button.chosen,
.palette-options button.chosen {
background: var(--soft);
border-color: var(--accent-text);
color: var(--accent-text);
box-shadow: 0 0 0 1px var(--accent-text);
}
.palette-options i {
width: 22px;
height: 22px;
border-radius: 50%;
box-shadow: inset 0 0 0 2px #fff6;
}
.sound-toggle {
width: 100%;
display: flex;
align-items: center;
gap: 10px;
padding: 12px;
border: 1px solid var(--border);
border-radius: 10px;
color: var(--accent-text);
background: var(--soft);
font-size: 13px;
margin-bottom: 20px;
}
.sound-popover label {
display: flex;
justify-content: space-between;
color: var(--secondary);
font-size: 12px;
font-weight: 500;
}
.sound-popover input[type='range'] {
width: 100%;
padding: 0;
margin: 16px 0 22px;
accent-color: var(--accent);
}
.sound-popover p {
font-size: 12px;
margin-top: 12px;
color: var(--secondary);
}
.sound-popover .speech-option {
justify-content: flex-start;
align-items: center;
gap: 9px;
margin: 0 0 20px;
line-height: 1.5;
}
.speech-option input {
width: 16px;
height: 16px;
margin: 0;
accent-color: var(--accent);
}
.modal.dice-overlay {
width: 560px;
max-width: calc(100vw - 24px);
border: 1px solid var(--border);
border-radius: 28px;
padding: 38px 30px 28px;
text-align: center;
background: radial-gradient(
ellipse at 50% 36%,
var(--hero),
var(--surface) 70%
);
}
.dice-overlay::backdrop {
background: #081126b8;
backdrop-filter: blur(7px);
}
.dice-overlay h2 {
font-size: 24px;
max-width: 360px;
margin: auto;
}
.text-button {
background: transparent;
color: var(--accent-text);
border: 0;
font-size: 12px;
padding: 10px 0;
}
.pin-input {
font-size: 23px;
letter-spacing: 12px;
}
.guest-notice {
padding: 12px;
background: var(--soft);
border-radius: 10px;
margin-bottom: 20px;
font-size: 12px;
}
.modal .button.full + .button.full {
margin-top: 10px;
}
.player-color-0 {
--player-ink: #775800;
--player-soft: #fff3ce;
--player-solid: #b88925;
}
.player-color-1 {
--player-ink: #165ba1;
--player-soft: #e6f1ff;
--player-solid: #397bba;
}
.player-color-2 {
--player-ink: #aa3938;
--player-soft: #ffecea;
--player-solid: #b95f56;
}
.player-color-3 {
--player-ink: #217146;
--player-soft: #e6f5eb;
--player-solid: #568264;
}
.player-color-4 {
--player-ink: #30343b;
--player-soft: #e8eaed;
--player-solid: #30343b;
}
.player-color-5 {
--player-ink: #985023;
--player-soft: #fff0e1;
--player-solid: #ad702f;
}
.player-color-6 {
--player-ink: #703cad;
--player-soft: #f2eaff;
--player-solid: #8654b8;
}
.player-color-7 {
--player-ink: #536070;
--player-soft: #eaf0f4;
--player-solid: #7d8794;
}
[data-theme='dark'] .player-color-0 {
--player-ink: #f3cd67;
--player-soft: #393326;
}
[data-theme='dark'] .player-color-1 {
--player-ink: #95c6ff;
--player-soft: #243650;
}
[data-theme='dark'] .player-color-2 {
--player-ink: #ffb4ad;
--player-soft: #402d36;
}
[data-theme='dark'] .player-color-3 {
--player-ink: #92ddb1;
--player-soft: #203d35;
}
[data-theme='dark'] .player-color-4 {
--player-ink: #e1e4e9;
--player-soft: #30343b;
}
[data-theme='dark'] .player-color-5 {
--player-ink: #ffc290;
--player-soft: #3b3029;
}
[data-theme='dark'] .player-color-6 {
--player-ink: #d9b8ff;
--player-soft: #352a49;
}
[data-theme='dark'] .player-color-7 {
--player-ink: #c1ccd6;
--player-soft: #303c48;
}
.avatar[class*='player-color'],
.mini-avatar[class*='player-color'] {
color: var(--player-ink);
background: var(--player-soft);
}
.chat-message strong {
display: flex;
align-items: center;
gap: 7px;
color: var(--player-ink);
font-size: 11px;
}
.chat-avatar {
display: grid;
place-items: center;
height: 23px;
width: 23px;
border-radius: 8px;
background: var(--player-soft);
color: var(--player-ink);
font-size: 11px;
}
.chat-message p,
.chat-message.mine p {
color: var(--text);
background: var(--player-soft);
border-inline-start: 3px solid var(--player-solid);
font-size: 13px;
}
.chat-message time {
font-size: 10px;
}
.chat-message {
animation: panel-enter 0.2s ease-out;
}
.parchis-controls {
background: var(--soft);
border-radius: 16px;
padding: 18px;
margin: 16px 0;
}
.parchis-home {
border-color: var(--border);
}
.home-pieces .parchis-token:not(:disabled) {
outline: 2px solid var(--accent-text);
outline-offset: 3px;
}
@keyframes panel-enter {
from {
opacity: 0;
transform: translateY(6px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@media (max-width: 1000px) and (min-width: 501px) {
.brand {
width: 64px;
align-items: center;
margin: 0 auto 38px;
}
.brand-logo {
min-width: 64px;
}
.brand small {
display: none;
}
}
@media (max-width: 500px) {
.sidebar {
background: var(--surface);
}
.mobile-logo {
display: block;
width: 155px;
flex-shrink: 0;
}
.topbar .header-search {
display: none;
}
.topbar {
justify-content: space-between;
gap: 10px;
padding: 0 16px;
}
.header-right {
gap: 8px;
}
.settings-popover {
inset: 65px 12px auto auto;
}
.header-login {
font-size: 11px;
gap: 4px;
}
.settings-trigger {
width: 34px;
height: 34px;
border-radius: 10px;
}
}

File diff suppressed because it is too large Load Diff

@ -150,27 +150,27 @@
.avatar-copy {
font-size: var(--type-secondary);
color: var(--secondary);
margin: 12px 0 20px;
margin: var(--space-3) 0 var(--space-5);
}
.avatar-preview {
width: 80px;
height: 80px;
margin: 0 auto 20px;
font-size: 28px;
margin: 0 auto var(--space-5);
font-size: var(--type-heading);
}
.avatar-options {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 12px;
margin-bottom: 20px;
gap: var(--space-3);
margin-bottom: var(--space-5);
}
.avatar-options button {
border: 2px solid transparent;
display: grid;
place-items: center;
background: transparent;
border-radius: 16px;
padding: 4px;
border-radius: var(--radius-card);
padding: var(--space-1);
min-height: 64px;
}
.avatar-options button.selected {
@ -181,28 +181,28 @@
width: 64px;
height: 64px;
max-width: 100%;
border-radius: 50%;
border-radius: var(--radius-round);
}
.avatar-tools {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 8px;
gap: var(--space-2);
}
.avatar-tools .button {
font-size: var(--type-secondary);
padding: 10px;
padding: var(--space-2);
}
.avatar-hint {
font-size: var(--type-detail);
color: var(--secondary);
margin: 12px 0 20px;
margin: var(--space-3) 0 var(--space-5);
}
input[type='file'] {
display: none;
}
@media (max-width: 380px) {
.avatar-options {
gap: 8px;
gap: var(--space-2);
}
.avatar-options img {
width: 52px;

@ -51,21 +51,21 @@
color: var(--text);
border: 1px solid var(--border);
border-top: 3px solid var(--accent);
border-radius: 14px;
box-shadow: 0 8px 30px var(--shadow);
padding: 16px;
border-radius: var(--radius-card);
box-shadow: var(--shadow-popover);
padding: var(--space-4);
z-index: 5;
display: flex;
flex-wrap: wrap;
align-items: flex-start;
gap: 8px;
gap: var(--space-2);
pointer-events: auto;
}
.cell-info.danger {
border-top-color: #c3403a;
border-top-color: var(--danger-text);
}
.cell-info.advantage {
border-top-color: #308052;
border-top-color: var(--success);
}
.cell-info > div {
flex: 1;
@ -76,25 +76,25 @@
color: var(--secondary);
}
.cell-info.danger span {
color: var(--danger);
color: var(--danger-text);
}
.cell-info.advantage span {
color: var(--player-ink, #308052);
color: var(--success);
}
h3 {
font-size: var(--type-body);
line-height: 1.4;
margin: 4px 0 0;
line-height: var(--leading-compact);
margin: var(--space-1) 0 0;
}
p {
width: 100%;
font-size: var(--type-secondary);
line-height: 1.5;
line-height: var(--leading-body);
margin: 0;
color: var(--secondary);
}
.icon-button {
flex-shrink: 0;
margin: -8px -8px 0 0;
margin: calc(-1 * var(--space-2)) calc(-1 * var(--space-2)) 0 0;
}
</style>

@ -49,8 +49,8 @@
.dialog-fallback-backdrop {
position: fixed;
inset: 0;
z-index: 1000;
background: #263a2d55;
z-index: var(--layer-backdrop);
background: var(--overlay);
backdrop-filter: blur(4px);
}
</style>

@ -276,7 +276,7 @@
.dice-widget {
display: flex;
align-items: center;
gap: 24px;
gap: var(--space-6);
}
.dice-result {
display: flex;
@ -286,8 +286,8 @@
}
.dice-result small {
color: var(--secondary);
font-size: var(--type-secondary, 14px);
line-height: 1.4;
font-size: var(--type-secondary);
line-height: var(--leading-compact);
max-width: 160px;
text-align: center;
overflow-wrap: anywhere;
@ -474,21 +474,19 @@
alternate;
}
.dropping .large {
animation: drop-die var(--roll-duration, 800ms) ease-out;
animation: drop-die var(--roll-duration, 800ms) var(--ease-ui);
}
.dice-announcement {
min-height: 95px;
}
.dice-announcement > strong {
display: block;
font:
800 52px 'Manrope',
sans-serif;
font: 800 var(--type-display) var(--font-ui);
color: var(--accent-text);
line-height: 1.2;
line-height: var(--leading-heading);
}
.dice-announcement p {
font-size: 14px;
font-size: var(--type-secondary);
color: var(--secondary);
}
.shake-button {
@ -508,28 +506,28 @@
z-index: 30;
display: flex;
align-items: center;
gap: 14px;
gap: var(--space-3);
width: 390px;
max-width: calc(100vw - 24px);
padding: 12px 18px;
padding: var(--space-3) var(--space-4);
border: 1px solid var(--border);
border-radius: 18px;
border-radius: var(--radius-panel);
background: var(--surface);
color: var(--text);
box-shadow: 0 12px 50px var(--shadow);
animation: notice-enter 0.2s ease-out;
box-shadow: var(--shadow-popover);
animation: notice-enter 0.2s var(--ease-ui);
pointer-events: none;
}
.dice-notice > div {
min-width: 0;
}
.dice-notice strong {
font-size: var(--type-body, 16px);
font-size: var(--type-body);
}
.dice-notice p {
font-size: var(--type-secondary, 14px);
line-height: 1.5;
margin-top: 5px;
font-size: var(--type-secondary);
line-height: var(--leading-body);
margin-top: var(--space-1);
color: var(--secondary);
}
.dice-notice button {
@ -582,7 +580,7 @@
}
@media (max-width: 500px) {
.dice-widget {
gap: 12px;
gap: var(--space-3);
}
.dice-result small {
max-width: 90px;

@ -198,47 +198,55 @@
cursor: pointer;
outline: none;
}
.go-map g[role='button'] { outline:none; }
.go-map g[role='button'] {
outline: none;
}
.allowed:hover .intersection,
.allowed:focus-visible .intersection {
fill: #ffffff33;
stroke: #0759d9;
stroke: var(--accent);
stroke-width: 3;
}
.go-scores {
display: flex;
flex-direction: column;
gap: 14px;
gap: var(--space-3);
}
.go-scores > div {
display: grid;
grid-template-columns: 20px minmax(0, 1fr);
gap: 3px 8px;
gap: var(--space-half) var(--space-2);
align-items: center;
}
.go-scores i {
width: 20px;
height: 20px;
border-radius: 50%;
background: #20252e;
border: 1px solid #858b96;
border-radius: var(--radius-round);
background: var(--stone-dark);
border: 1px solid var(--stone-border);
grid-row: span 2;
}
.go-scores i.white {
background: #f5f6f8;
background: var(--stone-light);
}
.go-scores strong {
font-size: var(--type-secondary,14px);
white-space:nowrap;
overflow:hidden;
text-overflow:ellipsis;
font-size: var(--type-secondary);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.go-scores small {
font-size: var(--type-detail,12px);
font-size: var(--type-detail);
color: var(--secondary);
}
@media(max-width:800px) and (orientation:portrait) {
.go-scores { flex-direction:row; gap:12px; }
.go-scores>div { flex:1; min-width:0; }
@media (max-width: 800px) and (orientation: portrait) {
.go-scores {
flex-direction: row;
gap: var(--space-3);
}
.go-scores > div {
flex: 1;
min-width: 0;
}
}
</style>

@ -162,26 +162,26 @@
display: grid;
grid-template-columns: minmax(0, 1fr) 44px;
align-items: start;
gap: 16px;
margin: 0 0 24px;
gap: var(--space-4);
margin: 0 0 var(--space-6);
}
h2 {
margin: 6px 0 0;
font-size: 24px;
line-height: 1.25;
letter-spacing: -0.5px;
margin: var(--space-1) 0 0;
font-size: var(--type-section);
line-height: var(--leading-heading);
letter-spacing: var(--tracking-heading);
}
.invitation-game {
display: grid;
grid-template-columns: 96px minmax(0, 1fr);
gap: 16px;
gap: var(--space-4);
align-items: center;
margin: 0 0 24px;
margin: 0 0 var(--space-6);
}
.invitation-art {
width: 96px;
height: 96px;
border-radius: 16px;
border-radius: var(--radius-card);
overflow: hidden;
}
.invitation-art :global(.game-art) {
@ -192,35 +192,35 @@
}
.invitation-details {
display: grid;
gap: 4px;
gap: var(--space-1);
min-width: 0;
}
.invitation-details strong {
font-size: 18px;
line-height: 1.4;
font-size: var(--type-subtitle);
line-height: var(--leading-compact);
overflow-wrap: anywhere;
}
.invitation-details span {
font-size: 14px;
line-height: 1.5;
font-size: var(--type-secondary);
line-height: var(--leading-body);
color: var(--secondary);
}
.invitation-message {
margin: 0 0 24px;
padding: 16px;
margin: 0 0 var(--space-6);
padding: var(--space-4);
border-left: 3px solid var(--accent);
border-radius: 0 12px 12px 0;
border-radius: 0 var(--radius-control) var(--radius-control) 0;
background: var(--soft);
color: var(--text);
font-size: 16px;
line-height: 1.5;
font-size: var(--type-body);
line-height: var(--leading-body);
white-space: pre-wrap;
overflow-wrap: anywhere;
}
.invitation-note {
margin: 0 0 16px;
font-size: 16px;
line-height: 1.5;
margin: 0 0 var(--space-4);
font-size: var(--type-body);
line-height: var(--leading-body);
color: var(--secondary);
}
@media (max-width: 600px) {
@ -230,7 +230,7 @@
.invitation-art {
width: 80px;
height: 80px;
border-radius: 12px;
border-radius: var(--radius-control);
}
.invitation-art :global(.game-art) {
transform: scale(0.421053);

@ -265,7 +265,7 @@
}
.cell-enlargement {
pointer-events: none;
filter: drop-shadow(0 8px 10px #27365250);
filter: drop-shadow(var(--shadow-board));
}
.cell-enlargement > rect {
stroke: var(--accent);
@ -290,10 +290,10 @@
}
.bonus,
.goal {
animation: arrive-jump var(--arrival-duration) ease-out;
animation: arrive-jump var(--arrival-duration) var(--ease-ui);
}
.penalty {
animation: arrive-shake var(--arrival-duration) ease-out;
animation: arrive-shake var(--arrival-duration) var(--ease-ui);
}
@keyframes arrive-jump {
30% {

@ -46,17 +46,17 @@
}
.pending-games-list {
display: grid;
gap: 8px;
gap: var(--space-2);
}
.pending-game {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
padding: 16px;
gap: var(--space-3);
padding: var(--space-4);
background: var(--surface);
border: 1px solid var(--border);
border-radius: 12px;
border-radius: var(--radius-control);
color: var(--text);
text-decoration: none;
}
@ -71,24 +71,24 @@
.pending-game strong {
display: block;
font-size: var(--type-body);
line-height: 1.4;
line-height: var(--leading-compact);
overflow-wrap: anywhere;
}
.pending-game small {
display: block;
font-size: var(--type-secondary);
color: var(--secondary);
margin-top: 6px;
margin-top: var(--space-1);
}
.pending-status {
display: block;
font-size: var(--type-detail);
font-size: var(--type-secondary);
color: var(--secondary);
margin-top: 6px;
margin-top: var(--space-1);
}
.pending-status.your-turn {
color: var(--accent-text);
font-weight: 700;
font-weight: var(--weight-bold);
}
.pending-empty {
font-size: var(--type-secondary);

@ -55,34 +55,34 @@
<style>
.color-chooser {
margin: 24px 0;
margin: var(--space-6) 0;
text-align: left;
}
h3 {
font-size: var(--type-body);
margin-bottom: 12px;
margin-bottom: var(--space-3);
}
.color-options {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 8px;
gap: var(--space-2);
}
.color-options button {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
padding: 12px;
min-height: 48px;
gap: var(--space-2);
padding: var(--space-3);
min-height: var(--size-field);
border: 1px solid var(--border);
border-radius: 12px;
border-radius: var(--radius-control);
background: var(--surface);
color: var(--text);
font-size: var(--type-secondary);
}
.color-options button.chosen {
border-color: var(--accent);
box-shadow: inset 0 0 0 1px var(--accent);
box-shadow: var(--shadow-selection);
}
.color-options button:disabled {
opacity: 0.35;
@ -92,25 +92,25 @@
display: inline-block;
width: 20px;
height: 20px;
border-radius: 50%;
border-radius: var(--radius-round);
flex-shrink: 0;
box-shadow: inset 0 0 0 2px #ffffff70;
box-shadow: var(--shadow-selection);
}
.color-hint,
.automatic-color p {
font-size: var(--type-secondary);
color: var(--secondary);
margin-top: 12px;
margin-top: var(--space-3);
}
.automatic-color {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 8px;
gap: var(--space-2);
}
.automatic-color p {
width: 100%;
margin-top: 4px;
margin-top: var(--space-1);
}
@media (max-width: 650px) {
.color-options {

@ -145,50 +145,50 @@
.modal-head {
display: flex;
align-items: flex-start;
gap: 16px;
gap: var(--space-4);
justify-content: space-between;
margin: 0 0 24px;
margin: 0 0 var(--space-6);
}
h2 {
margin: 6px 0 0;
font-size: 24px;
line-height: 1.25;
margin: var(--space-1) 0 0;
font-size: var(--type-section);
line-height: var(--leading-heading);
}
h3 {
margin: 0;
font-size: 18px;
line-height: 1.4;
font-size: var(--type-subtitle);
line-height: var(--leading-compact);
}
.exit-choice {
display: grid;
gap: 12px;
gap: var(--space-3);
}
.exit-choice p {
margin: 0;
font-size: 16px;
line-height: 1.5;
font-size: var(--type-body);
line-height: var(--leading-body);
color: var(--secondary);
}
.inactivity-choice {
margin-top: 24px;
padding-top: 24px;
margin-top: var(--space-6);
padding-top: var(--space-6);
border-top: 1px solid var(--border);
}
.exit-choice .exit-status {
font-size: 14px;
font-size: var(--type-secondary);
color: var(--accent-text);
}
.expiry-note {
display: flex;
align-items: flex-start;
gap: 8px;
margin: 24px 0 0;
gap: var(--space-2);
margin: var(--space-6) 0 0;
color: var(--secondary);
font-size: 14px;
line-height: 1.5;
font-size: var(--type-secondary);
line-height: var(--leading-body);
}
.expiry-note :global(svg) {
flex: 0 0 16px;
margin-top: 3px;
margin-top: var(--space-half);
}
</style>

@ -2,7 +2,7 @@
import { appearance, saveAppearance, type Mode, type Palette } from '../theme.svelte';
import Icon from './Icon.svelte';
const modes:{id:Mode;label:string;icon:string}[]=[{id:'light',label:'Claro',icon:'sun'},{id:'dark',label:'Oscuro',icon:'moon'},{id:'system',label:'Sistema',icon:'monitor'}];
const palettes:{id:Palette;label:string;color:string}[]=[{id:'blue',label:'Azul',color:'#4263df'},{id:'forest',label:'Bosque',color:'#247761'},{id:'violet',label:'Violeta',color:'#8756ca'}];
const palettes:{id:Palette;label:string;color:string}[]=[{id:'blue',label:'Azul',color:'var(--palette-blue)'},{id:'forest',label:'Bosque',color:'var(--palette-forest)'},{id:'violet',label:'Violeta',color:'var(--palette-violet)'}];
</script>
<div class="theme-control">
<button class="settings-trigger" popovertarget="appearance-settings" aria-label="Cambiar apariencia" title="Apariencia"><Icon name={appearance.mode==='dark'?'moon':'sun'} size={19}/></button>

@ -1,601 +0,0 @@
/* The table has its own viewport; platform navigation opens on demand. */
.match-shell {
height: 100dvh;
overflow: hidden;
}
.match-shell .main-shell {
margin: 0;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
}
.match-shell .topbar {
height: 52px;
flex: 0 0 52px;
padding: 0 18px;
gap: 14px;
background: var(--surface);
}
.match-shell .mobile-logo {
display: block;
width: 128px;
margin-right: auto;
}
.match-shell .header-right {
gap: 12px;
}
.match-shell .header-right .small-avatar {
display: none;
}
.match-shell main {
flex: 1;
min-height: 0;
width: 100%;
max-width: none;
margin: 0;
padding: 0;
overflow: hidden;
}
.match-shell .site-footer {
display: none;
}
.match-shell .sidebar:not(:popover-open) {
display: none;
}
.match-shell .sidebar:popover-open {
display: flex;
position: fixed;
inset: 60px auto 12px 12px;
margin: 0;
height: auto;
width: 260px;
padding: 24px;
border: 1px solid var(--border);
border-radius: 18px;
box-shadow: 0 16px 54px var(--shadow);
z-index: 50;
overflow: auto;
animation: panel-enter 0.18s ease-out;
}
.match-shell .sidebar .brand {
display: flex;
width: 100%;
margin: 0 0 24px;
align-items: flex-start;
}
.match-shell .sidebar .brand-logo {
width: 100%;
}
.match-shell .sidebar .brand small,
.match-shell .sidebar-label,
.match-shell .sidebar nav a span,
.match-shell .sidebar-invite,
.match-shell .sidebar-bottom {
display: block;
}
.match-shell .sidebar nav {
display: block;
width: 100%;
}
.match-shell .sidebar nav a {
display: flex;
align-items: center;
justify-content: flex-start;
flex-direction: row;
gap: 10px;
padding: 14px 12px;
font-size: 13px;
}
.match-shell .sidebar-bottom {
margin-top: auto;
padding-top: 18px;
}
.match-shell .sidebar-invite {
margin-top: 18px;
}
.match-shell .settings-popover {
top: 58px;
}
.match-room {
height: 100%;
display: flex;
flex-direction: column;
min-height: 0;
background: radial-gradient(
ellipse at 50% 40%,
var(--soft),
var(--background) 75%
);
}
.match-room .room-page-heading {
display: flex;
align-items: center;
gap: 16px;
margin: 0;
padding: 10px 20px;
flex-shrink: 0;
border-bottom: 1px solid var(--border);
}
.room-page-heading > div:first-child {
min-width: 0;
}
.match-room .room-page-heading .eyebrow {
font-size: 9px;
line-height: 1;
letter-spacing: 1px;
}
.match-room .room-page-heading h1 {
font-size: 17px;
letter-spacing: -0.4px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 36vw;
}
.match-turn {
display: flex;
align-items: center;
gap: 7px;
padding: 8px 12px;
background: var(--player-soft);
color: var(--player-ink);
border-radius: 20px;
font-size: 12px;
font-weight: 650;
white-space: nowrap;
}
.match-turn i,
.panel-players i {
display: block;
width: 7px;
height: 7px;
border-radius: 50%;
background: var(--player-solid);
flex-shrink: 0;
}
.room-tools {
display: flex;
align-items: center;
gap: 10px;
margin-left: auto;
}
.match-room .room-tools .button {
padding: 9px 12px;
margin: 0;
font-size: 12px;
}
.room-tools .icon-button {
width: 34px;
height: 34px;
display: grid;
place-items: center;
padding: 0;
}
.panel-trigger b {
background: var(--accent);
color: white;
border-radius: 20px;
font-size: 10px;
padding: 2px 6px;
}
.match-room .room-layout {
flex: 1;
min-height: 0;
display: block;
position: relative;
padding: 12px 18px 8px;
transition: padding-right 0.18s ease-out;
}
.match-room .play-area {
height: 100%;
min-height: 0;
display: flex;
flex-direction: column;
container-type: size;
}
.match-room .parchis-board {
flex: 1;
min-height: 0;
padding: 0;
border: 0;
background: transparent;
display: grid;
grid-template-columns: minmax(0, 1fr) 210px;
gap: 24px;
}
.board-surface {
min-width: 0;
min-height: 0;
display: grid;
place-items: center;
container-type: size;
}
.match-room .parchis-map {
width: min(100cqw, 100cqh);
height: min(100cqw, 100cqh);
max-width: 100%;
max-height: 100%;
filter: drop-shadow(0 10px 18px var(--shadow));
}
.match-room .parchis-controls {
margin: 0;
padding: 20px 16px;
border: 1px solid var(--border);
border-radius: 20px;
align-self: center;
display: flex;
align-items: stretch;
flex-direction: column;
gap: 14px;
box-shadow: 0 4px 24px var(--shadow);
}
.match-room .parchis-controls .dice-widget {
flex-direction: column;
gap: 10px;
}
.match-room .parchis-controls p {
font-size: 12px;
flex: none;
}
.table-players {
display: flex;
flex-direction: column;
gap: 10px;
border-top: 1px solid var(--border);
padding-top: 14px;
}
.table-player {
display: grid;
grid-template-columns: 25px minmax(0, 1fr);
align-items: center;
column-gap: 8px;
opacity: 0.7;
}
.table-player.playing {
opacity: 1;
}
.table-player .mini-avatar {
grid-row: span 2;
width: 25px;
height: 25px;
margin: 0;
border: 0;
font-size: 10px;
background: var(--player-soft);
color: var(--player-ink);
}
.table-player strong {
font-size: 11px;
color: var(--player-ink);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.table-player small {
font-size: 10px;
color: var(--secondary);
}
.piece-shortcuts {
display: flex;
gap: 8px;
align-items: center;
flex-wrap: wrap;
}
.piece-shortcuts > span {
width: 100%;
font-size: 10px;
color: var(--secondary);
}
.piece-shortcuts .parchis-token {
width: 32px;
height: 32px;
font-size: 13px;
}
.match-room .board-note {
font-size: 11px;
margin: 8px 0 0;
height: 20px;
flex-shrink: 0;
justify-content: center;
}
.match-room .board-note > span:last-child {
display: none;
}
.match-room .parchis-board + .board-note {
display: none;
}
.match-room .board-container {
padding: 8px;
margin: auto;
max-width: none;
width: min(100cqw, calc(100cqh - 34px));
aspect-ratio: 1;
flex-shrink: 0;
}
.match-room .board-grid {
height: 100%;
grid-template-rows: repeat(8, minmax(0, 1fr));
}
.match-room .chess-piece {
font-size: clamp(16px, 8cqmin, 80px);
}
.match-room .board-square {
aspect-ratio: auto;
}
.match-room .promotion {
position: absolute;
bottom: 15px;
left: 50%;
transform: translateX(-50%);
z-index: 10;
}
.match-room .room-panel:not(:popover-open) {
display: none;
}
.match-room .room-panel:popover-open {
display: flex;
flex-direction: column;
width: 310px;
max-width: calc(100vw - 24px);
position: fixed;
inset: 124px 18px 14px auto;
height: auto;
margin: 0;
z-index: 20;
background: var(--surface);
border-radius: 18px;
box-shadow: 0 6px 30px var(--shadow);
animation: panel-enter 0.18s ease-out;
}
.panel-heading {
display: flex;
align-items: center;
justify-content: space-between;
padding: 14px 16px 8px;
}
.panel-heading strong {
font-size: 14px;
}
.panel-players {
display: flex;
gap: 8px 12px;
flex-wrap: wrap;
padding: 0 16px 10px;
}
.panel-players span {
display: flex;
align-items: center;
gap: 5px;
color: var(--player-ink);
font-size: 10px;
}
.match-room .room-tabs {
flex-shrink: 0;
}
.match-room .chat-messages,
.match-room .room-rules,
.match-room .event-list {
height: auto;
min-height: 0;
flex: 1;
overflow: auto;
}
.match-room .chat-form {
flex-shrink: 0;
}
.match-room .waiting-card {
height: 100%;
max-width: 800px;
margin: auto;
overflow: auto;
padding: 24px;
}
.match-room .result-banner {
padding: 12px 20px;
margin: 0;
display: flex;
gap: 12px;
align-items: center;
flex-wrap: wrap;
}
.match-room .result-banner .empty-symbol {
display: none;
}
.match-room .result-banner h2 {
font-size: 15px;
}
.match-room .result-banner p {
font-size: 12px;
}
.match-room .result-banner .button {
padding: 8px 12px;
margin: 0;
}
.match-room .form-error {
margin: 8px 18px 0;
flex-shrink: 0;
}
@media (min-width: 850px) {
.match-room .room-layout:has(.room-panel:popover-open) {
padding-right: 346px;
}
.match-room .room-layout:has(.room-panel:popover-open) .parchis-board {
grid-template-columns: minmax(0, 1fr) 180px;
gap: 12px;
}
}
@media (min-width: 850px) and (max-width: 1199px) {
.match-room .room-layout:has(.room-panel:popover-open) .parchis-board {
grid-template-columns: minmax(0, 1fr);
grid-template-rows: minmax(0, 1fr) auto;
}
.match-room .room-layout:has(.room-panel:popover-open) .parchis-controls {
width: 100%;
padding: 10px 14px;
display: grid;
grid-template-columns: 180px minmax(0, 1fr);
gap: 6px 14px;
}
.match-room
.room-layout:has(.room-panel:popover-open)
.parchis-controls
> .eyebrow,
.match-room .room-layout:has(.room-panel:popover-open) .table-players {
display: none;
}
.match-room .room-layout:has(.room-panel:popover-open) .dice-widget {
flex-direction: row;
grid-row: span 2;
}
}
@media (max-width: 800px) and (orientation: portrait) {
.match-room .parchis-board {
grid-template-columns: minmax(0, 1fr);
grid-template-rows: minmax(0, 1fr) auto;
gap: 10px;
}
.match-room .parchis-controls {
width: 100%;
padding: 10px 12px;
display: grid;
grid-template-columns: auto minmax(0, 1fr);
gap: 6px 12px;
align-self: stretch;
}
.match-room .parchis-controls > .eyebrow {
display: none;
}
.match-room .parchis-controls .dice-widget {
flex-direction: row;
gap: 8px;
grid-row: span 2;
}
.match-room .parchis-controls .dice-widget .dice-stage {
width: 54px;
height: 65px;
--edge: 36px;
--depth: 18px;
}
.match-room .parchis-controls .dice-widget .dice-face {
padding: 6px;
gap: 2px;
border-radius: 6px;
}
.match-room .parchis-controls .button {
font-size: 11px;
padding: 10px;
}
.match-room .parchis-controls .dice-instruction {
font-size: 11px;
align-self: center;
}
.match-room .table-players {
display: none;
}
.match-room .piece-shortcuts {
gap: 6px;
}
.match-room .piece-shortcuts > span {
width: auto;
}
}
@media (max-width: 650px) {
.match-shell .topbar {
padding: 0 12px;
gap: 10px;
}
.match-shell .header-right {
gap: 8px;
}
.match-shell .connection {
font-size: 0;
gap: 0;
}
.match-shell .mobile-logo {
width: 114px;
}
.match-room .room-page-heading {
padding: 8px 12px;
gap: 6px 8px;
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
}
.match-room .room-page-heading > div:first-child {
grid-row: 1;
grid-column: 1;
}
.match-room .room-tools {
grid-row: 1;
grid-column: 2;
}
.match-room .room-page-heading h1 {
font-size: 14px;
max-width: 45vw;
}
.match-turn {
padding: 5px 0;
font-size: 11px;
grid-row: 2;
grid-column: 1 / -1;
width: 100%;
background: transparent;
}
.room-tools {
gap: 4px;
}
.room-tools .fullscreen-trigger {
display: none;
}
.match-room .panel-trigger {
padding: 8px;
}
.panel-trigger > span {
display: none;
}
.match-room .room-layout {
padding: 10px 8px 8px;
}
.match-room .room-panel:popover-open {
inset: 142px 8px 8px auto;
width: 340px;
}
.match-room .board-note {
font-size: 10px;
height: auto;
}
}
@media (orientation: landscape) and (max-height: 520px) {
.match-shell .topbar {
height: 44px;
flex-basis: 44px;
}
.match-room .room-page-heading {
padding: 5px 12px;
}
.match-room .parchis-board {
gap: 12px;
grid-template-columns: minmax(0, 1fr) 180px;
}
.match-room .parchis-controls {
padding: 10px 12px;
gap: 8px;
}
.match-room .table-players {
display: none;
}
.match-room .parchis-controls .dice-widget {
flex-direction: row;
gap: 8px;
}
.match-room .parchis-controls .dice-widget .dice-stage {
--edge: 36px;
--depth: 18px;
width: 48px;
height: 60px;
}
.match-room .parchis-controls .button {
padding: 8px;
font-size: 10px;
}
}

@ -11,11 +11,8 @@
import BrandMark from '#lib/components/BrandMark.svelte';
import AvatarImage from '#lib/components/AvatarImage.svelte';
import { brand } from '../../shared/brand.mjs';
import '../tokens.css';
import '../app.css';
import '../appearance.css';
import '../match.css';
import '../design.css';
import '../terms.css';
import TermsConsent from '#lib/components/TermsConsent.svelte';
import { terms } from '../../shared/terms.mjs';
let { children, data } = $props();

@ -1,114 +0,0 @@
.entry-notice {
position: fixed;
inset: 0;
margin: auto;
z-index: 1001;
width: min(600px, calc(100vw - 32px));
max-width: 600px;
padding: 32px;
}
.entry-notice h2 {
margin: 0 0 20px;
font-size: 24px;
}
.entry-notice p {
font-size: 16px;
line-height: 1.55;
margin: 0 0 16px;
color: var(--muted);
}
.entry-notice .terms-author {
color: var(--text);
}
.terms-read {
display: inline-block;
font-size: 16px;
font-weight: 650;
color: var(--green);
margin: 0 0 20px;
text-decoration: underline;
text-underline-offset: 3px;
}
.entry-notice .terms-check {
display: flex;
flex-direction: row;
align-items: flex-start;
gap: 12px;
margin: 0 0 16px;
color: var(--text);
font-size: 16px;
line-height: 1.5;
font-weight: 600;
}
.terms-check input {
flex: 0 0 22px;
width: 22px;
height: 22px;
margin: 2px 0 0;
padding: 0;
accent-color: var(--green);
}
.entry-notice .terms-cookie {
font-size: 14px;
line-height: 1.5;
}
.terms-actions {
display: flex;
gap: 12px;
justify-content: flex-end;
margin-top: 24px;
}
.terms-actions .button {
font-size: 16px;
}
.terms-actions a[aria-disabled='true'] {
pointer-events: none;
opacity: 0.5;
}
.terms-page {
width: min(100%, 760px);
margin: 0 auto;
padding: 40px 32px;
}
.terms-page h1 {
font-size: 28px;
margin: 0 0 24px;
}
.terms-page h2 {
font-size: 20px;
margin: 32px 0 12px;
}
.terms-page p {
font-size: 16px;
line-height: 1.65;
margin-bottom: 16px;
color: var(--muted);
}
.terms-page .terms-introduction {
color: var(--text);
}
.terms-page .terms-version {
font-size: 14px;
margin-top: 32px;
}
.terms-page .button {
margin: 0 8px 12px 0;
font-size: 16px;
}
.site-footer .footer-author {
max-width: 42ch;
}
@media (max-width: 600px) {
.entry-notice {
padding: 24px;
}
.terms-page {
padding: 24px 20px;
}
.terms-actions {
flex-direction: column-reverse;
}
.terms-actions .button {
width: 100%;
}
}

@ -0,0 +1,247 @@
/* The only source of shared UI values. Board artwork keeps its own geometry. */
@import url('https://fonts.googleapis.com/css2?family=Manrope:wght@400;500;600;700;750;800&display=swap');
:root {
color-scheme: light;
font-size: 100%;
--font-ui: 'Manrope', system-ui, sans-serif;
--font-title: var(--font-ui);
--type-body: clamp(1rem, 0.95rem + 0.1vw, 1.125rem);
--type-detail: calc(var(--type-body) * 0.8125);
--type-secondary: calc(var(--type-body) * 0.875);
--type-subtitle: calc(var(--type-body) * 1.25);
--type-section: calc(var(--type-body) * 1.5);
--type-heading: calc(var(--type-body) * 1.75);
--type-display: clamp(2rem, 1.4rem + 2vw, 3rem);
--leading-none: 1;
--leading-heading: 1.25;
--leading-compact: 1.4;
--leading-body: 1.5;
--leading-relaxed: 1.6;
--tracking-body: 0;
--tracking-heading: -0.025em;
--tracking-label: 0.06em;
--tracking-pin: 0.65em;
--weight-regular: 400;
--weight-medium: 500;
--weight-semibold: 600;
--weight-bold: 700;
--weight-display: 750;
--weight-heavy: 800;
--space-half: 0.125rem;
--space-1: 0.25rem;
--space-2: 0.5rem;
--space-3: 0.75rem;
--space-4: 1rem;
--space-5: 1.25rem;
--space-6: 1.5rem;
--space-8: 2rem;
--space-10: 2.5rem;
--space-12: 3rem;
--space-14: 3.5rem;
--space-16: 4rem;
--space-20: 5rem;
--space-24: 6rem;
--radius-xs: 0.25rem;
--radius-sm: 0.5rem;
--radius-control: 0.75rem;
--radius-card: 1rem;
--radius-panel: 1.25rem;
--radius-dialog: 1.5rem;
--radius-round: 50%;
--radius-pill: 999px;
--stroke-1: 1px;
--stroke-2: 2px;
--stroke-3: 3px;
--size-control: 2.75rem;
--size-field: 3rem;
--size-avatar: 2.5rem;
--size-avatar-small: 2rem;
--size-avatar-large: 5rem;
--size-modal: 32.5rem;
--size-notice: 37.5rem;
--size-popover: 22.5rem;
--size-menu: 20rem;
--size-navigation-mobile: 5.5rem;
--layout-sidebar: 15rem;
--layout-sidebar-medium: 12.8125rem;
--layout-sidebar-compact: 5.125rem;
--layout-content: 90rem;
--layout-gutter: clamp(var(--space-4), 3vw, var(--space-10));
--measure-copy: 70ch;
--measure-note: 48ch;
--duration-fast: 150ms;
--duration-normal: 200ms;
--duration-slow: 300ms;
--ease-ui: ease-out;
--layer-menu: 50;
--layer-notice: 35;
--layer-backdrop: 1000;
--layer-dialog: 1001;
--symbol-small: 2rem;
--symbol-medium: 2.5rem;
--symbol-large: 3rem;
--symbol-hero: 8rem;
--text: #202c41;
--background: #f5f7fc;
--surface: #ffffff;
--soft: #edf2fa;
--hero: #e8efff;
--accent: #0759d9;
--accent-hover: #0046b8;
--accent-text: #0759c4;
--on-accent: #ffffff;
--secondary: #58677e;
--border: #dde4f0;
--danger-bg: #fff0ec;
--danger-text: #a32a27;
--success: #217146;
--success-bg: #e6f5eb;
--glass-surface: #ffffffb8;
--glass-border: #ffffff90;
--overlay: #10274a55;
--overlay-strong: #081126b8;
--shadow-color: #10274a1c;
--palette-blue: #0759d9;
--palette-forest: #14744f;
--palette-violet: #7742bf;
--art-terracotta: #bc7255;
--board-light: #f1eddf;
--board-dark: #7f9871;
--board-selected: #d4bb68;
--board-target: #294b35a0;
--board-target-border: #234732aa;
--stone-dark: #20252e;
--stone-light: #f5f6f8;
--stone-border: #858b96;
--shadow-control: 0 var(--space-1) var(--space-2) var(--shadow-color);
--shadow-card: 0 var(--space-3) var(--space-8) var(--shadow-color);
--shadow-popover: 0 var(--space-4) var(--space-14) var(--shadow-color);
--shadow-dialog: 0 var(--space-6) var(--space-20) var(--shadow-color);
--shadow-board: 0 var(--space-2) var(--space-5) var(--shadow-color);
--shadow-selection: inset 0 0 0 var(--stroke-2) var(--glass-border);
}
:root[data-palette='forest'] {
--accent: #14744f;
--accent-hover: #0c5c3d;
--accent-text: #116942;
--hero: #e4f2e9;
}
:root[data-palette='violet'] {
--accent: #7742bf;
--accent-hover: #62329f;
--accent-text: #6836af;
--hero: #f0e9fc;
}
:root[data-theme='dark'] {
color-scheme: dark;
--text: #e8edf7;
--background: #101827;
--surface: #192438;
--soft: #223149;
--hero: #1c3052;
--accent: #397aee;
--accent-hover: #2b69d3;
--accent-text: #9bbeff;
--secondary: #adbbd0;
--border: #34445d;
--danger-bg: #442b32;
--danger-text: #ffb5a9;
--success: #92ddb1;
--success-bg: #203d35;
--glass-surface: #192438df;
--glass-border: #34445daa;
--overlay: #0008;
--shadow-color: #0006;
}
:root[data-theme='dark'][data-palette='forest'] {
--accent: #177f5a;
--accent-hover: #106e4b;
--accent-text: #7adab1;
--hero: #183a30;
}
:root[data-theme='dark'][data-palette='violet'] {
--accent: #8554d1;
--accent-hover: #7744c5;
--accent-text: #d2b4ff;
--hero: #30244c;
}
/* Player identities remain distinct in either theme. */
.player-color-0 {
--player-ink: #775800;
--player-soft: #fff3ce;
--player-solid: #b88925;
}
.player-color-1 {
--player-ink: #165ba1;
--player-soft: #e6f1ff;
--player-solid: #397bba;
}
.player-color-2 {
--player-ink: #aa3938;
--player-soft: #ffecea;
--player-solid: #b95f56;
}
.player-color-3 {
--player-ink: #217146;
--player-soft: #e6f5eb;
--player-solid: #568264;
}
.player-color-4 {
--player-ink: #30343b;
--player-soft: #e8eaed;
--player-solid: #30343b;
}
.player-color-5 {
--player-ink: #985023;
--player-soft: #fff0e1;
--player-solid: #ad702f;
}
.player-color-6 {
--player-ink: #703cad;
--player-soft: #f2eaff;
--player-solid: #8654b8;
}
.player-color-7 {
--player-ink: #536070;
--player-soft: #eaf0f4;
--player-solid: #7d8794;
}
[data-theme='dark'] .player-color-0 {
--player-ink: #f3cd67;
--player-soft: #393326;
}
[data-theme='dark'] .player-color-1 {
--player-ink: #95c6ff;
--player-soft: #243650;
}
[data-theme='dark'] .player-color-2 {
--player-ink: #ffb4ad;
--player-soft: #402d36;
}
[data-theme='dark'] .player-color-3 {
--player-ink: #92ddb1;
--player-soft: #203d35;
}
[data-theme='dark'] .player-color-4 {
--player-ink: #e1e4e9;
--player-soft: #30343b;
}
[data-theme='dark'] .player-color-5 {
--player-ink: #ffc290;
--player-soft: #3b3029;
}
[data-theme='dark'] .player-color-6 {
--player-ink: #d9b8ff;
--player-soft: #352a49;
}
[data-theme='dark'] .player-color-7 {
--player-ink: #c1ccd6;
--player-soft: #303c48;
}
Loading…
Cancel
Save

Powered by TurnKey Linux.