/* CityMyst - Sistema de Diseño - Estilo Glassmorphism */

/* Tipografías: se cargan desde el layout con vendor_url() (local si existe,
Google como respaldo) */

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

:root {
    /* Paleta CityMyst */
    --color-primary: #005080;
    --color-secondary: #aaacad;
    --color-accent: #003d5f;
    --color-light: #e8f4f8;
    --color-success: #28a745;
    --color-danger: #dc3545;
    --color-warning: #ffc107;
    --color-info: #17a2b8;
    
    /* Texto que va ENCIMA del color primario (botones llenos, celdas de la barra
       del juego, cabeceras de tarjeta). Era `#fff` a pelo en cada regla, y eso
       da por hecho que el primario siempre es oscuro. Deja de serlo en cuanto un
       tema de aventura usa un primario claro —el tema «negro» lo tiene BLANCO—,
       y entonces salía blanco sobre blanco: botones en blanco. */
    --color-on-primary: #ffffff;

    /* Superficies internas de la pantalla de juego. Estaban puestas a un azul
       fijo bajo `[data-theme="dark"]`, que en el juego NO se aplica nunca (el
       layout del juego no pone ese atributo) y que además no tiene sentido en un
       tema negro. Como variables, cada tema decide. */
    --cm-item-bg: rgba(255, 255, 255, 0.35);
    --cm-etapa-bg: rgba(255, 255, 255, 0.18);

    /* Glassmorphism */
    --glass-bg: rgba(255, 255, 255, 0.25);
    --glass-border: rgba(255, 255, 255, 0.4);
    --glass-shadow: 0 8px 32px rgba(0, 80, 128, 0.15);
    --blur: blur(20px);
    
    --bg-gradient: linear-gradient(135deg, #e8f4f8 0%, #d0e8f2 30%, #a8d4e6 60%, #80c0d8 100%);
    --text-primary: #1a1a1a;
    --text-secondary: #005080;

    /* Modales: fondo SOLIDO. El glass translucido sobre el velo oscuro del
       overlay dejaba el texto negro casi ilegible (correccion 2026-08-10). */
    --pop-bg: #f2fafe;
    --pop-borde: rgba(0, 80, 128, 0.22);

    /* «City» del logo. En tema claro la cabecera es casi blanca, así que
       «casi blanco» sería invisible: se usa el tono más claro que sigue
       leyéndose sobre ella. En oscuro sí va casi blanco (ver más abajo). */
    --logo-city: #3894cb;
    
    /* Tipografía */
    --font-display: 'Poppins', sans-serif;
    --font-body: 'DM Sans', sans-serif;
    
    /* Espaciado */
    --spacing-xs: 0.5rem;
    --spacing-sm: 1rem;
    --spacing-md: 1.5rem;
    --spacing-lg: 2rem;
    --spacing-xl: 3rem;
    
    --radius: 24px;
    --radius-sm: 16px;
    --radius-lg: 32px;
    
    /* Sidebar */
    --sidebar-width: 280px;
}

[data-theme="dark"] {
    --color-primary: #0099cc;
    --color-secondary: #c0c2c3;
    --color-accent: #006699;
    --color-light: #1a2a35;
    --color-success: #00ff88;
    --color-danger: #ff3366;
    --color-warning: #ffcc00;
    --color-info: #00ddff;
    
    --glass-bg: rgba(10, 30, 50, 0.6);
    --glass-border: rgba(0, 153, 204, 0.3);
    --glass-shadow: 0 8px 32px rgba(0, 187, 255, 0.2);
    
    --bg-gradient: linear-gradient(135deg, #0a0a0a 0%, #0a1525 30%, #0a2035 60%, #0a2545 100%);
    --text-primary: #e8e8e8;
    --text-secondary: #00bbff;

    --pop-bg: #0c2131;
    --pop-borde: rgba(0, 187, 255, 0.3);
    --logo-city: #f2fafe;
}

body {
    font-family: var(--font-body);
    line-height: 1.7;
    color: var(--text-primary);
    background: var(--bg-gradient);
    background-attachment: fixed;
    min-height: 100vh;
    transition: all 0.5s ease;
}

/* Shell con sidebar (zona admin) */
body.app-shell {
    overflow: hidden;
    height: 100vh;
}

/* Decorative blobs */
body::before,
body::after {
    content: '';
    position: fixed;
    border-radius: 50%;
    filter: blur(80px);
    opacity: 0.6;
    z-index: 0;
    pointer-events: none;
    transition: all 0.5s ease;
}

body::before {
    width: 500px;
    height: 500px;
    background: var(--color-primary);
    top: -150px;
    right: -100px;
    opacity: 0.3;
}

body::after {
    width: 400px;
    height: 400px;
    background: var(--color-info);
    bottom: -100px;
    left: 20%;
    opacity: 0.25;
}

/* Sidebar */
.sidebar {
    position: fixed;
    left: 0;
    top: 0;
    width: var(--sidebar-width);
    height: 100vh;
    background: var(--glass-bg);
    backdrop-filter: var(--blur);
    -webkit-backdrop-filter: var(--blur);
    border-right: 1px solid var(--glass-border);
    padding: var(--spacing-md);
    transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
    z-index: 1000;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
}

.sidebar.collapsed {
    left: calc(-1 * var(--sidebar-width));
}

.sidebar-logo {
    display: block;
    width: 70%;
    height: auto;
    margin: 0 auto var(--spacing-lg) auto;
    filter: drop-shadow(0 10px 30px rgba(0, 80, 128, 0.3));
    animation: float 6s ease-in-out infinite;
    transition: transform 0.4s ease, filter 0.4s ease;
}

.sidebar-logo:hover {
    filter: drop-shadow(0 10px 30px rgba(0, 80, 128, 0.3));
}

@keyframes float {
    0%, 100% {
        transform: translateY(0);
    }
    50% {
        transform: translateY(-10px);
    }
}

.sidebar-nav {
    list-style: none;
}

.sidebar-nav li {
    margin-bottom: var(--spacing-xs);
}

.sidebar-nav a {
    display: flex;
    align-items: center;
    color: var(--text-primary);
    text-decoration: none;
    padding: var(--spacing-sm) var(--spacing-md);
    font-family: var(--font-display);
    font-size: 0.95rem;
    font-weight: 500;
    background: transparent;
    border-radius: var(--radius-sm);
    transition: all 0.3s ease;
    position: relative;
    overflow: hidden;
}

/* Los ítems del menú admin siempre en color de texto normal (blanco en oscuro),
   estén visitados o no: anula la regla global a:visited (que los ponía en cian). */
.sidebar-nav a:visited { color: var(--text-primary); }

.sidebar-nav a::before {
    content: '';
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    background: linear-gradient(90deg, var(--color-primary), var(--color-info));
    opacity: 0;
    transition: opacity 0.3s ease;
    z-index: -1;
    border-radius: var(--radius-sm);
}

.sidebar-nav a:hover::before {
    opacity: 0.15;
}

.sidebar-nav a:hover {
    transform: translateX(8px);
    color: var(--color-primary);
}

.sidebar-nav a.active {
    background: linear-gradient(135deg, var(--color-primary), var(--color-accent));
    color: white;
    box-shadow: 0 4px 20px rgba(0, 80, 128, 0.4);
}

.sidebar-nav a.active::before {
    opacity: 0;
}

/* Toggle Button */
.sidebar-toggle {
    position: fixed;
    left: var(--spacing-md);
    top: var(--spacing-md);
    z-index: 9999;
    background: var(--glass-bg);
    backdrop-filter: var(--blur);
    -webkit-backdrop-filter: var(--blur);
    color: var(--color-primary);
    width: 50px;
    height: 50px;
    border: 1px solid var(--glass-border);
    border-radius: 50%;
    cursor: pointer;
    box-shadow: var(--glass-shadow);
    transition: all 0.3s ease;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1.3rem;
}

.sidebar-toggle:hover {
    transform: rotate(180deg);
    background: var(--color-primary);
    color: white;
    box-shadow: 0 8px 30px rgba(0, 80, 128, 0.4);
}

.sidebar:not(.collapsed) ~ .sidebar-toggle {
    left: calc(var(--sidebar-width) + var(--spacing-md));
}

/* Theme Toggle */
.theme-toggle-sidebar {
    width: 100%;
    margin-top: auto;
    padding: var(--spacing-sm) var(--spacing-md);
    background: var(--glass-bg);
    backdrop-filter: var(--blur);
    border: 1px solid var(--glass-border);
    border-radius: var(--radius-sm);
    cursor: pointer;
    transition: all 0.3s ease;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--spacing-sm);
    font-size: 1.3rem;
    color: var(--text-primary);
    box-shadow: var(--glass-shadow);
}

.theme-toggle-sidebar:hover {
    background: linear-gradient(135deg, var(--color-primary), var(--color-info));
    color: white;
    box-shadow: 0 8px 30px rgba(0, 80, 128, 0.4);
}

.theme-icon { transition: all 0.3s ease; }

.theme-icon.sun { display: block; }

.theme-icon.moon { display: none; }

[data-theme="dark"] .theme-icon.sun { display: none; }

[data-theme="dark"] .theme-icon.moon { display: block; }

.sidebar.collapsed .theme-toggle-sidebar { display: none; }

/* Main Content (dentro del shell admin) */
body.app-shell .main-content {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    left: var(--sidebar-width);
    padding: var(--spacing-lg);
    transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
    overflow-y: auto;
    z-index: 1;
}

body.app-shell .sidebar.collapsed ~ .main-content {
    left: 0;
}

/* Container */
.container {
    max-width: 1100px;
    margin: 0 auto;
    padding: 0 var(--spacing-md); /* aire lateral: sin esto las tarjetas tocan el borde en móvil */
}

/* Section */
.section {
    background: var(--glass-bg);
    backdrop-filter: var(--blur);
    -webkit-backdrop-filter: var(--blur);
    padding: var(--spacing-lg);
    margin-bottom: var(--spacing-lg);
    border: 1px solid var(--glass-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--glass-shadow);
    transition: all 0.4s ease;
}

/* SIN LEVANTARSE NI CRECER AL PASAR EL RATÓN (2026-08-20, pedido de Indy).
   Antes: `translateY(-4px)` y una sombra el doble de grande. Se quitan las DOS
   cosas: sin el desplazamiento pero con la sombra crecida, la tarjeta sigue
   pareciendo que se despega del fondo. Esto es exactamente lo que el bloque
   `@media (hover: none)` del final ya hacía en el móvil desde siempre; ahora el
   escritorio se comporta igual. La regla se queda (vacía de efecto) porque el
   `:hover` sigue siendo el sitio donde mirar si algún día se quiere devolver
   una señal, y para que no se reintroduzca por descuido. */
.section:hover {
    box-shadow: var(--glass-shadow);
}

.section-title {
    font-family: var(--font-display);
    font-size: 2.2rem;
    font-weight: 700;
    background: linear-gradient(135deg, var(--color-primary), var(--color-info));
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    background-clip: text;
    margin-bottom: var(--spacing-md);
    position: relative;
    display: inline-block;
}

.section-title::after {
    content: '';
    position: absolute;
    bottom: -8px;
    left: 0;
    width: 60px;
    height: 4px;
    background: linear-gradient(90deg, var(--color-primary), var(--color-info));
    border-radius: 2px;
}

.section-subtitle {
    font-family: var(--font-display);
    font-size: 1.4rem;
    font-weight: 600;
    color: var(--color-primary);
    margin-top: var(--spacing-lg);
    margin-bottom: var(--spacing-md);
}

/* Typography */
h1,
h2,
h3,
h4,
h5,
h6 {
    font-family: var(--font-display);
    font-weight: 600;
    line-height: 1.3;
}

.text-sample h1 { font-size: 2.8rem; margin: var(--spacing-sm) 0; background: linear-gradient(135deg, var(--color-primary), var(--color-accent)); -webkit-background-clip: text; -webkit-text-fill-color: transparent; }

.text-sample h2 { font-size: 2.3rem; margin: var(--spacing-sm) 0; color: var(--color-primary); }

.text-sample h3 { font-size: 1.9rem; margin: var(--spacing-sm) 0; color: var(--color-accent); }

.text-sample h4 { font-size: 1.5rem; margin: var(--spacing-sm) 0; }

.text-sample h5 { font-size: 1.2rem; margin: var(--spacing-sm) 0; }

.text-sample h6 { font-size: 1rem; margin: var(--spacing-sm) 0; }

.btn {
    font-family: var(--font-display);
    padding: var(--spacing-sm) var(--spacing-lg);
    border: none;
    border-radius: 50px;
    cursor: pointer;
    font-size: 0.95rem;
    font-weight: 600;
    transition: all 0.3s ease;
    text-decoration: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    position: relative;
    overflow: hidden;
}

.btn::before {
    content: '';
    position: absolute;
    top: 0;
    left: -100%;
    width: 100%;
    height: 100%;
    background: linear-gradient(90deg, transparent, rgba(255,255,255,0.3), transparent);
    transition: left 0.5s ease;
}

.btn:hover::before {
    left: 100%;
}

.btn:hover {
    box-shadow: 0 10px 30px rgba(0, 80, 128, 0.3);
}

/* El `:active` valía `translateY(-1px)` porque el botón estaba 3 px ARRIBA al
   pasar el ratón y al pulsarlo bajaba un poco. Sin el levantamiento del hover,
   ese mismo valor haría que el botón SALTE HACIA ARRIBA al pulsarlo, que es lo
   contrario de lo que significa pulsar. */
.btn:active {
    transform: none;
}

.btn-primary {
    background: linear-gradient(135deg, var(--color-primary), var(--color-accent));
    color: var(--color-on-primary);
    box-shadow: 0 4px 15px rgba(0, 80, 128, 0.3);
}

.btn-outline {
    background: var(--glass-bg);
    backdrop-filter: var(--blur);
    border: 2px solid var(--color-primary);
    color: var(--color-primary);
}

.btn-outline:hover {
    background: var(--color-primary);
    color: var(--color-on-primary);
}

.btn-success {
    background: linear-gradient(135deg, var(--color-success), #1e8e3e);
    color: white;
}

.btn-danger {
    background: linear-gradient(135deg, var(--color-danger), #b02a37);
    color: white;
}

.btn-sm { padding: var(--spacing-xs) var(--spacing-md); font-size: 0.85rem; }

.btn-lg { padding: var(--spacing-md) var(--spacing-xl); font-size: 1.05rem; }

.btn:disabled { opacity: 0.5; cursor: not-allowed; transform: none !important; }

.form-control,
.form-select {
    width: 100%;
    padding: var(--spacing-sm) var(--spacing-md);
    background: var(--glass-bg);
    backdrop-filter: var(--blur);
    border: 1px solid var(--glass-border);
    border-radius: var(--radius-sm);
    font-family: var(--font-body);
    font-size: 1rem;
    color: var(--text-primary);
    transition: all 0.3s ease;
    box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.05);
}

.form-control:focus,
.form-select:focus {
    outline: none;
    border-color: var(--color-primary);
    box-shadow: 0 0 0 4px rgba(0, 80, 128, 0.15), 
                inset 0 2px 4px rgba(0, 0, 0, 0.05);
}

.form-control::placeholder {
    color: var(--color-secondary);
}

textarea.form-control {
    resize: vertical;
    min-height: 120px;
}

/* Cards */
.card-grid {
    display: grid;
    /* auto-fill (no auto-fit): con pocas tarjetas mantienen su tamaño en vez de
       estirarse a lo ancho — misma distribución 1/2/3 columnas en todos los
       listados (Aventuras, Mis aventuras, home) sin depender del nº de fichas. */
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: var(--spacing-md);
    margin: var(--spacing-md) 0;
}

.card {
    background: var(--glass-bg);
    backdrop-filter: var(--blur);
    -webkit-backdrop-filter: var(--blur);
    border: 1px solid var(--glass-border);
    border-radius: var(--radius);
    overflow: hidden;
    box-shadow: var(--glass-shadow);
    transition: all 0.4s ease;
}

/* Ídem que `.section:hover`: ni sube ni crece. Era el caso más llamativo
   —12 px hacia arriba y un 2 % más grande— y en una rejilla de tarjetas hacía
   que la de al lado pareciera moverse también. */
.card:hover {
    box-shadow: var(--glass-shadow);
}

.card-header {
    background: linear-gradient(135deg, var(--color-primary), var(--color-accent));
    color: white;
    padding: var(--spacing-md);
    font-family: var(--font-display);
    font-weight: 600;
    font-size: 1.1rem;
}

.card-body {
    padding: var(--spacing-md);
}

.card-image {
    width: 100%;
    overflow: hidden;
    border-radius: var(--radius) var(--radius) 0 0;
}

.card-image svg {
    width: 100%;
    height: 180px;
    display: block;
}

.card-title {
    font-family: var(--font-display);
    font-size: 1.2rem;
    font-weight: 600;
    margin-bottom: var(--spacing-sm);
    background: linear-gradient(135deg, var(--color-primary), var(--color-accent));
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
}

.card-text {
    color: var(--text-primary);
    margin-bottom: var(--spacing-md);
    opacity: 0.9;
}

.card-footer {
    padding: var(--spacing-md);
    background: rgba(0, 80, 128, 0.05);
    border-top: 1px solid var(--glass-border);
}

/* Tabs */
.tabs {
    margin: var(--spacing-md) 0;
}

.tab-list {
    display: flex;
    list-style: none;
    gap: var(--spacing-xs);
    margin-bottom: var(--spacing-md);
    flex-wrap: wrap;
    background: var(--glass-bg);
    backdrop-filter: var(--blur);
    padding: var(--spacing-xs);
    border-radius: var(--radius);
    border: 1px solid var(--glass-border);
}

.tab-button {
    padding: var(--spacing-sm) var(--spacing-md);
    background: transparent;
    border: none;
    border-radius: var(--radius-sm);
    cursor: pointer;
    font-family: var(--font-display);
    font-weight: 500;
    font-size: 0.9rem;
    color: var(--text-primary);
    transition: all 0.3s ease;
}

.tab-button:hover {
    background: rgba(0, 80, 128, 0.1);
    color: var(--color-primary);
}

.tab-button.active {
    background: linear-gradient(135deg, var(--color-primary), var(--color-accent));
    color: white;
    box-shadow: 0 4px 15px rgba(0, 80, 128, 0.3);
}

.tab-content {
    display: none;
    padding: var(--spacing-md);
    background: var(--glass-bg);
    backdrop-filter: var(--blur);
    border: 1px solid var(--glass-border);
    border-radius: var(--radius);
    box-shadow: var(--glass-shadow);
    animation: fadeIn 0.3s ease;
}

@keyframes fadeIn {
    from { opacity: 0; transform: translateY(10px); }
    to { opacity: 1; transform: translateY(0); }
}

.tab-content.active {
    display: block;
}

/* Alerts */
.alert {
    padding: var(--spacing-md);
    background: var(--glass-bg);
    backdrop-filter: var(--blur);
    border: 1px solid var(--glass-border);
    border-radius: var(--radius);
    margin: var(--spacing-sm) 0;
    box-shadow: var(--glass-shadow);
    border-left: 4px solid;
}

.alert-info { border-left-color: var(--color-info); }

.alert-success { border-left-color: var(--color-success); }

.alert-warning { border-left-color: var(--color-warning); }

.alert-danger { border-left-color: var(--color-danger); }

/* Badges */
.badge {
    display: inline-block;
    padding: 0.4rem 1rem;
    font-size: 0.8rem;
    font-family: var(--font-display);
    font-weight: 500;
    border-radius: 50px;
    margin: 0.2rem;
    color: white;
}

/* VIS-18/W9: sólido, no degradado. Los otros seis badges se pasaron a sólido
   accesible en esta misma tanda; este se quedó con el degradado y en tema oscuro su
   extremo claro (--color-primary = #0099cc) da 3,27:1 con texto blanco, por debajo
   del 4,5:1 que exige WCAG AA para texto normal (.badge es 0,8 rem/500, no «texto
   grande»). En tema claro el degradado sí pasaba, pero un sólido evita que la mitad
   del badge cumpla y la otra no. #005f87 da 7,03:1 y mantiene el azul de marca. */
.badge-primary { background: #005f87; }

.badge-secondary { background: #55595c; }

.badge-success { background: #176b32; }

.badge-danger { background: #a21d2f; }

.badge-warning { background: linear-gradient(135deg, var(--color-warning), #e0a800); color: #1a1a1a; }

.badge-info { background: #075e6b; }

/* Packs multiciudad: distingue de un vistazo a los miembros de un pack en el
   listado de admin. La estrella marca la maestra. Va aquí, entre las reglas base,
   y NUNCA dentro del bloque @media del final. */
.badge-pack { background: #59359a; }

/* La fila del pack que corresponde a la aventura que se está gestionando. */
.table tr.is-current > td { background: rgba(111, 66, 193, 0.10); font-weight: 600; }

/* Selector «elige tu ciudad» de la ficha de pack. Misma rejilla fluida que el
   listado de aventuras, para que las dos páginas se sientan iguales. */
.pack-ciudades { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
                 gap: var(--spacing-md); }

/* Campo que un miembro de pack no puede tocar: se ve, pero se ve apagado. */
label.campo-pack { opacity: 0.55; }

/* Tables */
.table-responsive {
    overflow-x: auto;
    margin: var(--spacing-md) 0;
}

table {
    width: 100%;
    border-collapse: collapse;
    background: var(--glass-bg);
    backdrop-filter: var(--blur);
    border-radius: var(--radius);
    overflow: hidden;
    box-shadow: var(--glass-shadow);
    border: 1px solid var(--glass-border);
}

thead {
    background: linear-gradient(135deg, var(--color-primary), var(--color-accent));
    color: white;
}

th,
td {
    padding: var(--spacing-md);
    text-align: left;
}

th {
    font-family: var(--font-display);
    font-weight: 600;
    font-size: 0.9rem;
    text-transform: uppercase;
    letter-spacing: 0.5px;
}

tbody tr {
    border-bottom: 1px solid var(--glass-border);
    transition: all 0.3s ease;
}

tbody tr:last-child {
    border-bottom: none;
}

tbody tr:hover {
    background: rgba(0, 80, 128, 0.05);
}

/* Scrollbar */
::-webkit-scrollbar {
    width: 10px;
    height: 10px;
}

::-webkit-scrollbar-track {
    background: transparent;
}

::-webkit-scrollbar-thumb {
    background: linear-gradient(135deg, var(--color-primary), var(--color-accent));
    border-radius: 10px;
}

::-webkit-scrollbar-thumb:hover {
    background: var(--color-accent);
}

/* Loader - Floating Orbs */
.loader {
    width: 120px;
    height: 80px;
    margin: var(--spacing-lg) auto;
    position: relative;
    display: flex;
    justify-content: center;
    align-items: center;
}

.hexagon {
    width: 18px;
    height: 18px;
    background: linear-gradient(135deg, var(--color-primary), var(--color-info));
    border-radius: 50%;
    position: absolute;
    animation: orbit 2s ease-in-out infinite;
    box-shadow: 0 0 20px var(--color-primary);
}

.hexagon:nth-child(1) { animation-delay: 0s; }

.hexagon:nth-child(2) { animation-delay: 0.15s; }

.hexagon:nth-child(3) { animation-delay: 0.3s; }

.hexagon:nth-child(4) { animation-delay: 0.45s; }

.hexagon:nth-child(5) { animation-delay: 0.6s; }

.hexagon:nth-child(6) { animation-delay: 0.75s; }

.hexagon:nth-child(7) { animation-delay: 0.9s; }

@keyframes orbit {
    0%, 100% {
        transform: translateX(0) translateY(0) scale(0.5);
        opacity: 0.3;
    }
    25% {
        transform: translateX(40px) translateY(-20px) scale(1);
        opacity: 1;
    }
    50% {
        transform: translateX(0) translateY(-30px) scale(0.8);
        opacity: 0.7;
    }
    75% {
        transform: translateX(-40px) translateY(-20px) scale(1);
        opacity: 1;
    }
}

/* Progress Bar */
.progress {
    background: var(--glass-bg);
    backdrop-filter: var(--blur);
    border: 1px solid var(--glass-border);
    border-radius: 50px;
    height: 28px;
    overflow: hidden;
    margin: var(--spacing-sm) 0;
    box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.1);
}

.progress-bar {
    background: linear-gradient(90deg, var(--color-primary), var(--color-info), var(--color-primary));
    background-size: 200% 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    color: white;
    font-family: var(--font-display);
    font-weight: 600;
    font-size: 0.8rem;
    border-radius: 50px;
    transition: width 0.5s ease;
    animation: shimmer 2s linear infinite;
}

@keyframes shimmer {
    0% { background-position: 200% 0; }
    100% { background-position: -200% 0; }
}

/* ============================================================
   Adaptaciones CityMyst (sobre la guía de diseño glassmorphism)
   ============================================================ */

/* ---------- Cabecera pública (layout main) ---------- */

.site-header {
    position: sticky;
    top: 0;
    z-index: 100;
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: var(--spacing-sm);
    flex-wrap: wrap;
    padding: var(--spacing-sm) var(--spacing-lg);
    background: var(--glass-bg);
    backdrop-filter: var(--blur);
    -webkit-backdrop-filter: var(--blur);
    border-bottom: 1px solid var(--glass-border);
    box-shadow: var(--glass-shadow);
}

.site-header .logo {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    font-family: var(--font-display);
    font-weight: 700;
    font-size: 1.25rem;
    text-decoration: none;
    background: linear-gradient(135deg, var(--color-primary), var(--color-info));
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    background-clip: text;
}

/* «City» en un tono más claro que «Myst». El logo se pinta con un degradado
   recortado sobre el texto (`background-clip:text` + `text-fill-color:
   transparent`), así que a la primera palabra hay que darle su propio relleno:
   con `color` a secas no se vería, la ganaría el `-webkit-text-fill-color`
   transparente que hereda del enlace. */
.site-header .logo .logo-city {
    -webkit-text-fill-color: var(--logo-city);
    color: var(--logo-city);
}

.site-header .logo img { height: 40px; width: auto; display: block; }

.site-header nav { display: flex; gap: var(--spacing-sm); align-items: center; flex-wrap: wrap; }

.site-header nav a,
.site-header button.link {
    font-family: var(--font-display);
    font-weight: 500;
    font-size: 0.95rem;
    color: var(--text-primary);
    text-decoration: none;
    background: transparent;
    border: none;
    cursor: pointer;
    padding: 0.45rem 1rem;
    border-radius: 50px;
    transition: all 0.3s ease;
}

.site-header nav a:hover,
.site-header button.link:hover {
    background: linear-gradient(135deg, var(--color-primary), var(--color-accent));
    color: #fff;
    box-shadow: 0 4px 15px rgba(0, 80, 128, 0.3);
}

.inline { display: inline; }

.nav-toggle {
    display: none;
    background: var(--glass-bg);
    border: 1px solid var(--glass-border);
    border-radius: 10px;
    font-size: 1.35rem;
    line-height: 1;
    color: var(--text-primary);
    cursor: pointer;
    padding: 0.35rem 0.65rem;
}

/* ---------- Página pública ---------- */

.public-main {
    position: relative;
    z-index: 1;
    max-width: 1100px;
    margin: 0 auto;
    padding: var(--spacing-lg);
}

.site-footer {
    position: relative;
    z-index: 1;
    text-align: center;
    padding: var(--spacing-md);
    color: var(--text-primary);
    opacity: 0.75;
}

/* ---------- Hero (home) ---------- */

.hero {
    text-align: center;
    padding: var(--spacing-xl) var(--spacing-lg);
}

.hero .hero-logo {
    width: 110px;
    height: auto;
    filter: drop-shadow(0 10px 30px rgba(0, 80, 128, 0.3));
    animation: float 6s ease-in-out infinite;
}

.hero h1 {
    font-size: 2.4rem;
    margin: var(--spacing-sm) 0;
    background: linear-gradient(135deg, var(--color-primary), var(--color-info));
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    background-clip: text;
}

.hero p { font-size: 1.1rem; opacity: 0.85; }

/* ---------- Formularios de la app ---------- */

/* Los inputs sin clase heredan el estilo .form-control de la guía */
input[type="text"],
input[type="email"],
input[type="password"],
input[type="number"],
input[type="date"],
input[type="tel"],
input[type="datetime-local"],
input[type="time"],
input[type="search"],
input[type="url"],
select,
textarea {
    width: 100%;
    padding: var(--spacing-sm) var(--spacing-md);
    background: var(--glass-bg);
    backdrop-filter: var(--blur);
    border: 1px solid var(--glass-border);
    border-radius: var(--radius-sm);
    font-family: var(--font-body);
    font-size: 1rem;
    color: var(--text-primary);
    transition: all 0.3s ease;
    box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.05);
}

/* Campo apagado por una casilla que lo sustituye —hoy los dos mínimos de etapa
   con su casilla «Todas»—. El `[hidden]` del navegador ya lo esconde, pero la
   regla de arriba da estilo a `input[type="number"]` y el día que alguien le
   añada un `display` a esa lista ganaría al del user agent y el campo
   reaparecería a medias. Se fija aquí para que no dependa de eso. */
input[hidden] { display: none !important; }

input[type="text"]:focus,
input[type="email"]:focus,
input[type="password"]:focus,
input[type="number"]:focus,
input[type="date"]:focus,
input[type="tel"]:focus,
input[type="datetime-local"]:focus,
input[type="time"]:focus,
input[type="search"]:focus,
input[type="url"]:focus,
select:focus,
textarea:focus {
    outline: none;
    border-color: var(--color-primary);
    box-shadow: 0 0 0 4px rgba(0, 80, 128, 0.15), inset 0 2px 4px rgba(0, 0, 0, 0.05);
}

input[type="color"] {
    width: 100%;
    height: 48px;
    padding: 4px;
    background: var(--glass-bg);
    border: 1px solid var(--glass-border);
    border-radius: var(--radius-sm);
    cursor: pointer;
}

label {
    display: block;
    margin: var(--spacing-sm) 0 var(--spacing-xs);
    font-family: var(--font-display);
    font-weight: 500;
    color: var(--text-secondary);
}

small { opacity: 0.8; }

/* Contenedores de formulario como secciones de cristal */
.form-narrow,
.form-wide {
    background: var(--glass-bg);
    backdrop-filter: var(--blur);
    -webkit-backdrop-filter: var(--blur);
    border: 1px solid var(--glass-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--glass-shadow);
    padding: var(--spacing-lg);
    margin: var(--spacing-md) 0;
}

.form-narrow { max-width: 460px; }

.form-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: var(--spacing-sm);
}

fieldset {
    border: 1px solid var(--glass-border);
    border-radius: var(--radius);
    padding: var(--spacing-sm) var(--spacing-md) var(--spacing-md);
    margin: var(--spacing-md) 0;
    background: rgba(255, 255, 255, 0.08);
}

legend {
    font-family: var(--font-display);
    font-weight: 600;
    color: var(--color-primary);
    padding: 0 var(--spacing-xs);
}

/* ---------- Secciones y utilidades ---------- */

.page-head {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: var(--spacing-sm);
    flex-wrap: wrap;
    margin-bottom: var(--spacing-md);
}

.page-head h1 {
    font-family: var(--font-display);
    font-size: 1.9rem;
    background: linear-gradient(135deg, var(--color-primary), var(--color-info));
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    background-clip: text;
    margin: 0;
}

.muted { opacity: 0.75; }

.mt-1 { margin-top: var(--spacing-sm); }

.mt-2 { margin-top: var(--spacing-lg); }

.actions { display: flex; gap: 0.4rem; flex-wrap: wrap; align-items: center; }

.actions form.inline { display: inline-flex; align-items: center; }

.actions .btn,
.actions form.inline .btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    vertical-align: middle;
}

.btn-ico.btn-sm {
    font-size: 1.02rem;
    line-height: 1;
    padding: var(--spacing-xs) var(--spacing-sm);
}

.btn-ico.btn-sm .ico-svg {
    width: 1.02rem;
    height: 1.02rem;
    display: block;
    flex-shrink: 0;
}

/* ---------- Mapa (editor de áreas) ---------- */

#map {
    height: 480px;
    border-radius: var(--radius);
    border: 1px solid var(--glass-border);
    box-shadow: var(--glass-shadow);
    position: relative;
    z-index: 1;
}

.map-help {
    padding: var(--spacing-sm) var(--spacing-md);
    background: var(--glass-bg);
    backdrop-filter: var(--blur);
    border: 1px solid var(--glass-border);
    border-left: 4px solid var(--color-info);
    border-radius: var(--radius);
    margin: var(--spacing-sm) 0;
}

/* ---------- Motivo hexagonal del logo (se conserva como detalle de marca) ---------- */

.hex-bullet {
    display: inline-block;
    width: 12px; height: 13px;
    clip-path: polygon(50% 0%, 100% 12%, 100% 72%, 50% 100%, 0% 72%, 0% 12%);
    background: linear-gradient(135deg, var(--color-primary), var(--color-info));
    margin-right: 0.5rem;
}

/* ---------- Portada de sub-admin (branding dinámico) ---------- */

.portada-header {
    text-align: center;
    padding: var(--spacing-xl) var(--spacing-lg);
    color: #fff;
    background: linear-gradient(135deg, var(--brand-primary, var(--color-primary)), var(--brand-secondary, var(--color-accent)));
    border-radius: 0 0 var(--radius-lg) var(--radius-lg);
    box-shadow: var(--glass-shadow);
    position: relative;
    z-index: 1;
}

.portada-header h1 { color: #fff; }

.portada-header img.brand-logo {
    max-height: 96px;
    width: auto;
    background: rgba(255, 255, 255, 0.9);
    padding: 8px;
    border-radius: var(--radius-sm);
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.25);
}

/* ---------- Sidebar admin: enlaces de sección ---------- */

.sidebar-user {
    font-size: 0.85rem;
    text-align: center;
    margin-bottom: var(--spacing-sm);
    opacity: 0.8;
    word-break: break-all;
}

/* ---------- Autenticación centrada ---------- */
/* Icono del calendario visible en tema oscuro */
[data-theme="dark"] input[type="date"]::-webkit-calendar-picker-indicator { filter: invert(1); }

/* Enlaces de contenido acordes al tema (incluido :visited,
nada de morado) */
a { color: var(--color-info); }

a:visited { color: var(--color-info); }

a:hover { color: var(--color-primary); }

/* Los enlaces-botón conservan su color aunque estén visitados
   (a:visited gana a .btn por especificidad si no se redeclara) */
a.btn:visited { color: #fff; }

a.btn-outline:visited,
a.btn-outline { color: var(--color-primary); }

/* Pulsado/hover de enlaces-botón VISITADOS: la regla de arriba (0,2,1) ganaba a
   .btn-outline:hover (0,2,0) y al tocar (hover pegajoso en móvil) el texto quedaba
   del mismo color que el fondo. Detectado en la maqueta de portada («¿Cómo funciona?»). */
a.btn-outline:visited:hover,
a.btn-outline:visited:active,
.btn-outline:active {
  background: var(--color-primary);
  color: var(--color-on-primary);
}

/* ---------- Autenticación centrada (horizontal Y vertical) ---------- */
.auth-center {
    max-width: 560px;
    margin: 0 auto;
    text-align: center;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    min-height: calc(100dvh - 240px); /* header + footer aprox */
}

.auth-center h1 { margin-top: var(--spacing-md); }

.auth-center .form-narrow { margin: 0 auto; text-align: left; width: 100%; }

.auth-center .form-narrow input { width: 100%; }

.auth-center > p { margin-top: var(--spacing-sm); }

.check-line { display: flex; gap: 0.55rem; align-items: flex-start; font-weight: normal;
  margin: 0.7rem 0; cursor: pointer; }

.check-line input[type="checkbox"] { width: auto; margin-top: 0.25rem; flex-shrink: 0; }

/* ---------- Botones: aire respecto al elemento anterior ---------- */
.form-narrow .btn,
.form-narrow button[type="submit"],
form > .btn,
form > button[type="submit"] { margin-top: var(--spacing-sm); }

.btn + .btn,
.btn + form.inline,
form.inline + .btn { margin-top: var(--spacing-xs); }

/* Filas de acciones horizontales: sin márgenes verticales (gana a las reglas de arriba) */
.actions .btn + .btn,
.actions .btn + form.inline,
.actions form.inline + .btn,
.actions form.inline + form.inline,
.actions form > .btn,
.actions form > button[type="submit"] {
    margin-top: 0;
}

.actions .btn.btn-sm {
    box-sizing: border-box;
    min-height: calc(2 * var(--spacing-xs) + 1.02rem + 4px);
}

/* Pila de acciones de tarjeta: cada botón en su línea,
a ancho completo */
.card-actions-stack { display: flex; flex-direction: column; gap: 0.5rem; }

.card-actions-stack .btn { width: 100%; text-align: center; margin: 0; }

.card-actions-stack details.inline { width: 100%; }

.card-actions-stack details.inline > summary.btn { width: 100%; text-align: center; box-sizing: border-box; }
.card-actions-stack form.inline { width: 100%; }

/* Interruptor de tema en la barra pública */
.theme-toggle-nav { display: inline-flex; align-items: center; gap: 0.4rem;
    background: none; border: none; cursor: pointer; color: inherit; font: inherit;
    font-size: 0.9rem; padding: 0.3rem 0.6rem; border-radius: 12px; }
.theme-toggle-nav:hover { color: var(--color-primary); }

/* ---------- Medallas de logros: formato premio ---------- */
/* `.pop-card.logro-medalla` (0,2,0) por el mismo motivo que .pop-card-video: este
   bloque va antes que .pop-card y su fondo dorado quedaba pisado. */
.logro-medalla,
.pop-card.logro-medalla {
    position: relative;
    text-align: center;
    padding: var(--spacing-md);
    border-radius: var(--radius);
    background: linear-gradient(160deg, rgba(255,200,60,0.22), rgba(255,150,0,0.10) 55%, var(--pop-bg));
    border: 1px solid rgba(255,190,60,0.55);
    /* La sombra de elevacion del modal va encadenada: al ganar este bloque a
       .pop-card, su glow dorado la sustituia y el popup quedaba plano. */
    box-shadow: 0 24px 70px rgba(0, 0, 0, 0.5),
                0 4px 18px rgba(255,170,0,0.12), inset 0 1px 0 rgba(255,255,255,0.15);
}

.logro-medalla .logro-icono {
    font-size: 2.6rem;
    line-height: 1;
    display: block;
    margin-bottom: 0.35rem;
    filter: drop-shadow(0 3px 6px rgba(255,160,0,0.45));
}

/* #ffc107 sobre el fondo claro del premio da 1,5:1. Ambar oscuro en claro,
   el amarillo original en oscuro. */
.logro-medalla h3 { margin: 0 0 0.2rem; color: #8a5a00; }

[data-theme="dark"] .logro-medalla h3 { color: var(--color-warning); }

.logro-medalla .logro-aventura { display: block; margin-top: 0.3rem; font-style: italic; }
.logro-medalla .logro-aventura[hidden] { display: none; }

/* Responsive */
/* Panel de preferencias de cookies */
.cm-cat { padding: 0.55rem 0; border-bottom: 1px solid var(--glass-border); }

.cm-cat:last-of-type { border-bottom: none; }

.cm-cat small { display: block; margin: 0.15rem 0 0 1.9rem; }

/* Popup de vídeo teaser: marco VERTICAL 9:16 (vídeos grabados con el móvil),
dimensionado para caber siempre en el alto de la pantalla */
/* Este modal es «cine»: fondo oscuro SIEMPRE, tambien en tema claro. Ademas de
   quedar mejor, garantiza que no se transparente nada detras del video. */
/* Doble clase A PROPOSITO: este bloque se declara ANTES que .pop-card y, a igual
   especificidad, ganaria .pop-card (fondo claro) — la ✕ quedaba invisible en tema
   claro. `.pop-card.pop-card-video` sube a (0,2,0) y manda aqui. */
.pop-card.pop-card-video { max-width: none; width: auto; padding: var(--spacing-md); text-align: left;
    background: #0b2233; border: 1px solid rgba(0, 187, 255, 0.3);
    /* Redefinir la variable, no solo `color`: .pop-cerrar fija
       color: var(--text-primary) y en tema claro la ✕ quedaria negra sobre
       negro. Asi la hereda bien todo el contenido del modal. */
    --text-primary: #eaf4fb;
    --glass-border: rgba(0, 187, 255, 0.3);
    /* Tambien el azul de marca: el claro (#005080) sobre este fondo oscuro
       daria 1,6:1 en cuanto haya un enlace dentro. */
    --color-primary: #4fc3f7;
    color: var(--text-primary); }

.video-marco { position: relative; aspect-ratio: 9 / 16;
    width: min(85vw, calc((100vh - 8rem) * 0.5625));
    max-width: 100%;
    margin-inline: auto;
    border-radius: var(--radius); overflow: hidden; background: #000; }

/* 100vh en movil INCLUYE la barra de direcciones: el marco se salia por abajo y
   el modal aparecia descentrado. svh (viewport pequeño, barra visible) y no dvh:
   el dinamico recalcula el ancho mientras se hace scroll y el reproductor
   reflowea en caliente (2026-08-10). */
@supports (height: 100svh) {
    .video-marco { width: min(85vw, calc((100svh - 8rem) * 0.5625)); }
}

.video-marco iframe,
.video-marco video { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }

/* Características de la ficha de aventura: cuadrícula fija de 2 columnas */
.ficha-carac { display: grid; grid-template-columns: 1fr 1fr; gap: var(--spacing-md) var(--spacing-sm); }

.ficha-carac > div { min-width: 0; }

/* Dificultad como barras (no confundir con las ⭐ de valoración) */
.dif-nivel { display: inline-flex; align-items: flex-end; gap: 2px; vertical-align: middle; }

.dif-nivel i { display: inline-block; width: 5px; border-radius: 2px 2px 0 0;
    background: var(--glass-border); }

.dif-nivel i:nth-child(1) { height: 6px; }

.dif-nivel i:nth-child(2) { height: 9px; }

.dif-nivel i:nth-child(3) { height: 12px; }

.dif-nivel i:nth-child(4) { height: 15px; }

.dif-nivel i:nth-child(5) { height: 18px; }

.dif-nivel i.on { background: linear-gradient(180deg, var(--color-info), var(--color-primary)); }

.dif-nivel small { margin-left: 0.35rem; align-self: center; }

/* Medallero compacto: icono + título; la descripción va en el popup */
.logros-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(130px, 1fr));
    gap: var(--spacing-sm); margin: var(--spacing-md) 0; }

.logro-mini { cursor: pointer; font: inherit; color: inherit; aspect-ratio: 1 / 1;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    padding: 0.5rem; }

.logro-mini .logro-icono { font-size: 2.2rem; margin-bottom: 0.3rem; }

.logro-mini h3 { font-size: 0.78rem; line-height: 1.2; margin: 0; overflow: hidden;
    display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; }

/* Popup con el tema del proyecto (sin alert()) */
.pop-overlay { position: fixed; inset: 0; z-index: 3000; display: flex; align-items: center;
    justify-content: center; padding: 1rem; background: rgba(4, 20, 35, 0.72);
    backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); }

/* imprescindible: un display propio pisa el display:none del atributo hidden */
.pop-overlay[hidden],
.btn[hidden],
/* El contador de partidas en juego se esconde con `hidden` cuando vale 0, y
   `.badge` trae display:inline-block, que gana al display:none del atributo. */
.badge[hidden] { display: none; }

/* Fondo SOLIDO, no glass: el modal va sobre un velo oscuro con blur, y con un
   fondo translucido el texto (#1a1a1a en claro) quedaba sobre gris oscuro y no
   se leia — igual que los titulos y los botones (correccion 2026-08-10).
   Redefinir aqui --glass-border hace visibles las separaciones de los hijos
   (.cm-cat y demas), que en claro son blanco al 40 % e invisibles sobre #f2fafe. */
/* Los enlaces del sitio usan --color-info (#17a2b8), que sobre el fondo claro
   del modal da 2,9:1. Dentro de un modal van en el azul de marca.
   `:not(.btn)` es imprescindible: (0,1,1) le ganaba a .btn-primary (0,1,0) y un
   <a class="btn btn-primary"> perdia su texto blanco — y encima solo cuando NO
   estaba visitado, porque a.btn:visited es (0,2,1) y si ganaba.
   El subrayado en hover devuelve el feedback que el color ya no puede dar. */
.pop-card a:not(.btn) { color: var(--color-primary); }

.pop-card a:not(.btn):hover { text-decoration: underline; }

.pop-card { position: relative; max-width: 340px; width: 100%; text-align: center;
    padding: var(--spacing-lg); border-radius: var(--radius-lg);
    background: var(--pop-bg); color: var(--text-primary);
    border: 1px solid var(--pop-borde);
    --glass-border: var(--pop-borde);
    box-shadow: 0 24px 70px rgba(0, 0, 0, 0.5); }

.pop-cerrar { position: absolute; top: 0.25rem; right: 0.25rem; z-index: 2;
    display: inline-flex; align-items: center; justify-content: center;
    width: 44px; height: 44px; min-width: 44px; padding: 0;
    background: none; border: none; color: var(--text-primary); font-size: 1.4rem;
    line-height: 1; cursor: pointer; -webkit-tap-highlight-color: transparent; }

/* Historial de aventuras en dos subfilas (sin scroll horizontal en móvil) */
.hist-item { padding: 0.65rem 0; border-bottom: 1px solid var(--glass-border); }

.hist-item:last-child { border-bottom: none; }

/* `flex-wrap` y `min-width: 0` desde que la fila lleva DOS botones («Ver
   resultado» + «Ver aventura», 2026-08-28): sin ellos, con los rótulos largos del
   alemán la fila no baja de unos 300 px solo de botones y ensancha la página
   entera en un móvil de 360 — justo el scroll horizontal que dice evitar el
   comentario de esta sección. `.hist-f2` ya lo hacía. */
.hist-f1 { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 0.8rem; }

.hist-f1 > strong, .hist-f1 > span { min-width: 0; overflow-wrap: anywhere; }

.hist-f1 .btn { flex-shrink: 0; width: auto; }

.hist-f2 { display: flex; align-items: center; flex-wrap: wrap; gap: 0.6rem; margin-top: 0.4rem; }

/* Listados admin en subfilas (aventuras, jugadores, actividad…) */
.adv-list,
.ply-list,
.aud-list,
.hue-list,
.sub-list {
    display: flex;
    flex-direction: column;
    background: var(--glass-bg);
    backdrop-filter: var(--blur);
    border: 1px solid var(--glass-border);
    border-radius: var(--radius);
    box-shadow: var(--glass-shadow);
    overflow: hidden;
}

.adv-item,
.ply-item,
.aud-item,
.hue-item,
.sub-item {
    padding: 0.85rem 1rem;
    border-bottom: 1px solid var(--glass-border);
}

.adv-item:last-child,
.ply-item:last-child,
.aud-item:last-child,
.hue-item:last-child,
.sub-item:last-child { border-bottom: none; }

.adv-f1,
.ply-f1 {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 0.75rem;
    min-width: 0;
}

.aud-f1 {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.35rem 0.65rem;
    min-width: 0;
}

.adv-head {
    display: flex;
    align-items: center;
    gap: 0.65rem;
    min-width: 0;
    flex: 1 1 auto;
}

.adv-cover {
    width: 72px;
    height: 48px;
    object-fit: cover;
    border-radius: 8px;
    flex-shrink: 0;
    border: 1px solid var(--glass-border);
}

.adv-cover-ph {
    width: 72px;
    height: 48px;
    border-radius: 8px;
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(0, 80, 128, 0.12);
    border: 1px solid var(--glass-border);
    font-size: 1.35rem;
    opacity: 0.75;
}

.adv-ident,
.ply-ident { min-width: 0; flex: 1 1 auto; }

.adv-title,
.ply-title {
    display: block;
    font-size: 1rem;
    line-height: 1.25;
    word-break: break-word;
}

.adv-slug,
.ply-email { display: block; margin-top: 0.15rem; font-size: 0.78rem; word-break: break-all; }

.adv-owner {
    width: 40px;
    height: 40px;
    object-fit: contain;
    border-radius: 8px;
    flex-shrink: 0;
    background: rgba(255, 255, 255, 0.06);
    border: 1px solid var(--glass-border);
    padding: 0.15rem;
}

.adv-badges,
.ply-badges {
    display: flex;
    flex-wrap: nowrap;
    gap: 0.35rem;
    align-items: center;
    justify-content: flex-end;
    flex-shrink: 0;
    max-width: 100%;
}

.adv-badges .btn.btn-ico.btn-sm {
    box-sizing: border-box;
    width: 2.125rem;
    height: 2.125rem;
    min-width: 2.125rem;
    max-width: 2.125rem;
    min-height: 2.125rem;
    max-height: 2.125rem;
    padding: 0;
    flex: 0 0 auto;
}

.adv-badges .badge,
.ply-badges .badge { flex-shrink: 0; }

.adv-f2,
.ply-f2 {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.35rem 1rem;
    margin-top: 0.45rem;
    font-size: 0.88rem;
}

.aud-f2 {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.25rem 0.45rem;
    margin-top: 0.4rem;
    font-size: 0.84rem;
    word-break: break-all;
    line-height: 1.35;
}

.adv-meta,
.ply-meta { min-width: 0; word-break: break-word; }

.adv-meta-k,
.ply-meta-k {
    opacity: 0.72;
    font-size: 0.78rem;
    margin-right: 0.2rem;
}

.adv-f3,
.ply-f3,
.sub-f2 { margin-top: 0.55rem; }

.ply-f3.actions .badge,
.sub-f2.actions .badge { flex-shrink: 0; }

.sub-f1 {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.35rem 0.75rem;
    min-width: 0;
}

.sub-name {
    font-size: 1rem;
    line-height: 1.25;
    word-break: break-word;
}

.sub-email {
    font-size: 0.85rem;
    word-break: break-all;
}

.sub-portada {
    font-size: 0.85rem;
    word-break: break-all;
}

.sub-meta {
    font-size: 0.88rem;
    white-space: nowrap;
}

.sub-meta-k {
    opacity: 0.72;
    font-size: 0.78rem;
    margin-right: 0.2rem;
}

.sub-f1 .badge { flex-shrink: 0; }

.aud-fecha {
    font-size: 0.78rem;
    opacity: 0.8;
    white-space: nowrap;
}

.aud-user {
    font-size: 0.9rem;
    word-break: break-word;
}

.aud-f1 .badge { flex-shrink: 0; }

.aud-ip {
    font-size: 0.78rem;
    opacity: 0.75;
    white-space: nowrap;
}

.aud-f2 code {
    font-size: 0.78rem;
    opacity: 0.85;
}

.aud-desc {
    opacity: 0.88;
    word-break: break-word;
}

.hue-f1 {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.35rem 0.65rem;
    min-width: 0;
}

.hue-file {
    font-size: 0.82rem;
    word-break: break-all;
}

.hue-folder {
    font-size: 0.78rem;
    opacity: 0.72;
}

.hue-size {
    font-size: 0.85rem;
    white-space: nowrap;
}

.hue-f1 .badge { flex-shrink: 0; }

.hue-f2 {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.25rem 0.45rem;
    margin-top: 0.4rem;
    font-size: 0.84rem;
    line-height: 1.35;
    word-break: break-word;
}

.hue-meta {
    font-size: 0.82rem;
    white-space: nowrap;
}

.hue-origen {
    font-size: 0.82rem;
    word-break: break-word;
}

/* Táctil (móvil/tablet): sin zoom ni desplazamiento en hover — el toque dispara
   un hover "fantasma" que deja las fichas movidas. En escritorio se mantiene. */
/* Modales .game-modal (juego y panel GM): hidden gana SIEMPRE, incluso a un
   display:flex en línea (la trampa del «popup siempre visible» de agents.md) */
.game-modal[hidden] { display: none !important; }

/* Marcador de vídeo bloqueado por consentimiento (RGPD: nada de terceros sin permiso) */
.video-consent {
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 1rem;
  /* Este marcador NO es un video: su contenido va en flujo y con el 9/16
     heredado se recortaba sin scroll en pantallas bajas — y lo primero en
     desaparecer era el aviso de cookies, que es justo lo que no puede faltar. */
  aspect-ratio: auto;
  height: auto;
  min-height: 11rem;
  /* Sin esto heredaba el ancho calculado para un video 9:16: en escritorio salia
     un bloque ancho y bajo, y al pulsar «Cargar video» la pagina daba un salto
     de casi 600 px. */
  width: min(85vw, 22rem);
  overflow: auto;
  /* OPACO a proposito: con rgba se transparentaba el marco y se veia el pie de
     pagina «dentro» del video (correccion 2026-08-10). */
  background: #0b2233;
  color: #eaf4fb;
}
.video-consent p { margin: 0 0 0.6rem; font-size: 0.9rem; }
.video-consent-aviso { margin: 0.7rem 0 0; font-size: 0.78rem; opacity: 0.75; }

/* Miembros de un equipo en la tarjeta de «Mis aventuras» */
.equipo-lista { list-style: none; margin: 0.35rem 0 0.6rem; padding: 0; }

/* `usuario_nombre` admite 120 caracteres sin espacios: sin esto se recorta contra
   el borde de la tarjeta sin dar ninguna pista de que sigue. */
.equipo-lista li { padding: 0.15rem 0; font-size: 0.92rem; overflow-wrap: anywhere; }

.equipo-lista .badge { font-size: 0.68rem; padding: 0.15rem 0.5rem; }

/* Reloj de cada partida en el panel de juego en vivo */
.gm-reloj { font-variant-numeric: tabular-nums; white-space: nowrap; }

.gm-reloj-poco { color: var(--color-danger); font-weight: 700; }

/* ---------- Parpadeo de aviso (badge del chat, partidas en juego) ----------
   Se apaga con «reducir movimiento» del sistema: son avisos que el jugador va a
   tener delante durante horas. */
@keyframes cm-parpadeo {
  0%, 45%  { opacity: 1; }
  55%, 95% { opacity: 0.28; }
  100%     { opacity: 1; }
}

.parpadea { animation: cm-parpadeo 1.25s ease-in-out infinite; }

@media (prefers-reduced-motion: reduce) {
  .parpadea { animation: none; }
}

/* ---------- Portada pública (secciones nuevas 2026-07-21) ---------- */
.home-cifras { display: flex; gap: 0.8rem; justify-content: center; flex-wrap: wrap; margin-top: 1.2rem; }
.home-cifra {
  background: var(--glass-bg);
  backdrop-filter: var(--blur);
  border: 1px solid var(--glass-border);
  border-radius: var(--radius);
  padding: 0.9rem 1.6rem;
  text-align: center;
  min-width: 130px;
}
.home-cifra strong { display: block; font-family: var(--font-display); font-size: 1.6rem; color: var(--color-primary); }
.home-cifra small { color: var(--text-secondary); }

.home-video {
  position: relative;
  border-radius: var(--radius-lg);
  overflow: hidden;
  cursor: pointer;
  aspect-ratio: 16 / 9;
  max-width: 720px;
  margin: 0 auto;
  background: linear-gradient(135deg, #04263f 0%, #005080 55%, #0a7ab8 100%);
  border: 1px solid var(--glass-border);
  box-shadow: var(--glass-shadow);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  gap: 0.6rem;
}
.home-video .play {
  width: 72px;
  height: 72px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.92);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.7rem;
  color: #005080;
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.35);
}
.home-video p { color: #eaf4fb; margin: 0; font-weight: 500; }

.home-pasos { display: grid; grid-template-columns: repeat(4, 1fr); gap: 0.8rem; }
.home-paso {
  background: var(--glass-bg);
  backdrop-filter: var(--blur);
  border: 1px solid var(--glass-border);
  border-radius: var(--radius);
  padding: 1.1rem;
  text-align: center;
}
.home-paso .ico { font-size: 2rem; display: block; margin-bottom: 0.4rem; }
.home-paso .num {
  display: inline-flex;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--color-primary);
  color: #fff;
  align-items: center;
  justify-content: center;
  font-size: 0.85rem;
  font-weight: 700;
  margin-bottom: 0.5rem;
}
.home-paso h3 { font-size: 1rem; margin: 0.2rem 0 0.4rem; }
.home-paso p { font-size: 0.88rem; color: var(--text-secondary); margin: 0; }

.home-tipo .ico { font-size: 1.8rem; }

.home-testi { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.8rem; }
.home-testi blockquote {
  background: var(--glass-bg);
  backdrop-filter: var(--blur);
  border: 1px solid var(--glass-border);
  border-radius: var(--radius);
  padding: 1rem 1.1rem;
  margin: 0;
  font-size: 0.92rem;
}
.home-testi footer { margin-top: 0.6rem; font-size: 0.85rem; color: var(--text-secondary); }
.home-estrellas { color: #f4a825; letter-spacing: 2px; }

.home-cta-final { text-align: center; padding: 2.2rem 1rem; }

/* Honeypot anti-bots del registro: fuera de pantalla, no display:none
   (los bots más listos saltan los display:none) */
.hp-caja {
  position: absolute !important;
  left: -9999px;
  top: auto;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* Posiciones de compañeros (mapa del jugador) y miembros (panel GM) */
.comp-wrap { background: none; border: none; }
.comp-pin {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: #1d8a4a;
  border: 2px solid #fff;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
}
.comp-pin.comp-viejo { background: #9aa3ab; }
.gm-eq-label { font-weight: 600; }

/* Reasignar aventura (admin) — reglas BASE: van antes del bloque @media final */
.reasig-accion { align-self: end; }
.reasig-check { font-weight: normal; display: block; margin-top: 0.4rem; }
.reasig-lista { margin: 0.35rem 0 0; padding-left: 1.1rem; }

/* ---------- Ficha del minijuego (editor de pruebas) ----------
   Reglas BASE: van antes del bloque @media final (regla dura 5).
   La ficha cuelga del fieldset «Minijuego» a todo su ancho, fuera del
   .form-row, y sustituye a los dos <small> de ayuda que había allí.
   Rejilla de auto-ajuste, no una columna: en el panel a 1280 px una sola
   columna daría renglones de 1000 px que no hay quien lea, y al estrecharse
   cae sola a una columna sin necesidad de una media query propia. */
.mj-ficha {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    /* El hueco de FILAS sube de --spacing-sm a --spacing-md (queda igual que el
       de columnas) para que cada sección respire. Va en el `gap` y no en un
       margin-bottom de `.mj-ficha-sec`: el hueco solo aparece ENTRE filas, así
       que la última no deja un colchón muerto contra el borde de la caja, y no
       descuadra nada — un margen en el ítem alarga su celda y arrastra al
       vecino de la otra columna. */
    gap: var(--spacing-md);
    align-items: start;
    margin-top: var(--spacing-sm);
    padding: var(--spacing-sm) var(--spacing-md);
    border: 1px solid var(--glass-border);
    border-left: 4px solid var(--color-primary);
    border-radius: var(--radius-sm);
    background: var(--glass-bg);
}

/* El `hidden` lo pone el JS cuando el juego elegido no tiene ficha. Sin esta
   regla el display:grid de arriba lo pisaría —una regla de autor gana a la del
   navegador— y la ficha «oculta» se seguiría viendo, vacía. */
.mj-ficha[hidden] { display: none; }

.mj-ficha-nombre,
.mj-ficha-vacia { grid-column: 1 / -1; }

/* El azul de TEXTO es `--text-secondary`, no `--color-primary`: en claro son el
   mismo #005080, pero en oscuro el primary (#0099cc) se queda en 4,7:1 sobre el
   fondo de la caja —AA raspado— y este panel se mira durante horas. El
   secundario (#00bbff) sube a 7,1:1 con el mismo papel de acento. El primary se
   queda donde es fondo o filete grueso (el borde izquierdo de la caja), que no
   se lee. */
.mj-ficha-nombre {
    font-family: var(--font-display);
    font-size: 1rem;
    color: var(--text-secondary);
}

.mj-ficha-vacia { opacity: 0.75; font-size: 0.9rem; }

/* Las cabeceras iban en el color del párrafo rebajado con `opacity: 0.8`, que
   es justo lo contrario de lo que necesitan: en el tema oscuro quedaban a un
   paso del texto y había que buscar dónde empezaba cada sección. Van en el azul
   de acento —el mismo de `.mj-ficha-nombre`, ver allí por qué `--text-secondary`
   y no `--color-primary`— y sin opacity, que apagaba lo que tiene que destacar.
   Filete inferior en vez de subrayar el texto: `text-decoration: underline` se
   pega a las letras y a 0.78rem con letter-spacing ensucia la palabra, mientras
   que el borde llega al ancho de la columna, se despega con su propio
   padding-bottom y hace de línea de salida del bloque. */
.mj-ficha-sec h5 {
    font-family: var(--font-display);
    font-size: 0.78rem;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--text-secondary);
    padding-bottom: 0.25rem;
    border-bottom: 1px solid currentColor;   /* el filete sigue al título sin repetir el color */
    margin-bottom: 0.45rem;
}

.mj-ficha-sec p {
    font-size: 0.88rem;
    line-height: 1.45;
    overflow-wrap: anywhere;   /* un dato de ejemplo sin espacios no desborda la caja */
}

.mj-ficha-chips { display: flex; flex-wrap: wrap; gap: 0.35rem; }

/* INTENTOS Y FALLOS (2026-08-23) ocupa la FILA ENTERA, al revés que sus cuatro
   hermanas, y por dos motivos que se suman:
   · lleva dentro un `.alert` con el aviso de la penalización, y una caja de
     aviso metida en una columna de 260 px se lee a cuatro palabras por renglón,
     que es justo cuando la gente deja de leerla;
   · es la única sección que habla de campos que están MÁS ARRIBA en este mismo
     formulario («Máx. intentos», «Penalización por fallo»), así que tiene que
     poder mirarse de un vistazo sin buscarla entre dos columnas.
   El `hidden` que le pone el JS sigue funcionando: `grid-column` no es una
   propiedad de display y no pisa la regla del navegador — al revés que en
   `.mj-ficha`, donde el `display: grid` de autor sí había que desactivar. */
#mjSecIntentos { grid-column: 1 / -1; }

/* El tope sugerido va bajo la frase, en el azul de acento y algo más pequeño:
   es un DATO accionable —el número que el editor acaba de escribir en el campo—
   y no debe leerse como una línea más del párrafo. Mismo `--text-secondary` que
   las cabeceras, por lo mismo (ver `.mj-ficha-sec h5`).

   SELECTOR CON EL ELEMENTO: `.mj-ficha-sec p` (0,1,1) le gana a `.mj-ficha-sug`
   (0,1,0) por especificidad y le clavaba su `font-size: 0.88rem` — el «algo más
   pequeño» de aquí era letra muerta. Con `p.mj-ficha-sug` (0,2,1) manda esta. */
.mj-ficha-sec p.mj-ficha-sug {
    margin-top: 0.35rem;
    font-size: 0.82rem;
    font-weight: 600;
    color: var(--text-secondary);
}

/* ---- Cuenta atrás por prueba (2026-08-24) ----
   El reloj de la ficha se lee de reojo mientras se juega, con el móvil en la
   mano y a veces al sol: números grandes, tabulares y sin nada alrededor que
   compita. `tabular-nums` es lo que evita que la línea entera baile cada vez que
   un 1 sustituye a un 8 — con dígitos proporcionales, un reloj que cambia cada
   segundo parece que vibra. */
.cm-cuenta-reloj {
    margin: 0.4rem 0 0.6rem;
    padding: 0.35rem 0.7rem;
    display: inline-block;
    border-radius: 8px;
    background: #a21d2f;
    color: #fff;
    font-size: 1.35rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.02em;
}

/* El mismo número en la tarjeta de la lista, donde solo tiene que decir «esta
   prueba tiene un reloj corriendo». Ahí manda no romper la fila. */
.cm-cuenta-tag {
    font-variant-numeric: tabular-nums;
}

/* El aviso previo va sobre el botón «Comenzar», y son una sola cosa: pegarlos
   evita que el botón quede huérfano al final de una ficha larga. */
.cm-cuenta-aviso {
    margin-bottom: 0.5rem;
}

.mj-chip {
    padding: 0.15rem 0.6rem;
    border: 1px solid var(--color-primary);
    border-radius: 999px;
    background: rgba(255, 255, 255, 0.25);
    font-size: 0.78rem;
    line-height: 1.6;
}

/* ==========================================================================
   MINIJUEGOS — reglas BASE (regla dura 5: antes del bloque @media del final)
   --------------------------------------------------------------------------
   El resto del CSS de los packs vive en el `<style>` de
   `app/views/player/juego.php`. Lo de aquí es SOLO lo nuevo, y con clases
   NUEVAS a propósito: aquel `<style>` se sirve DESPUÉS de este archivo, así
   que a igual especificidad gana él y redefinir una clase suya (`.cm-lock`,
   `.cm-dial`, `.cm-mm-pt`…) desde aquí no tendría ningún efecto.
   Todo en variables del tema (`--glass-*`, `--text-*`) para que valga en claro
   y en oscuro, y todo lo pulsable a 44 px o más (regla dura 4).
   ========================================================================== */

/* ---------- Código de colores: historial y feedback del servidor ---------- */

/* Una fila por intento: las 4 fichas + las marcas de resultado. */
.cm-mm-h {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.3rem;
}

.cm-mm-hp {
    width: 22px;
    height: 22px;
    border-radius: 50%;
    border: 1px solid var(--glass-border);
    background: transparent;
}

/* Intento gastado en una sesión anterior (el jugador recargó la página): el
   servidor devuelve cuántos van, nunca las combinaciones probadas. */
.cm-mm-h.gastada .cm-mm-hp { border-style: dashed; opacity: 0.45; }

.cm-mm-h.ganada .cm-mm-hp { box-shadow: 0 0 0 2px var(--color-success); }

/* ● negras (color en su sitio) y ○ blancas (color en otra posición), en la
   rejilla 2×2 de toda la vida. Las que sobran quedan apagadas. */
.cm-mm-claves {
    display: grid;
    grid-template-columns: repeat(2, 10px);
    gap: 2px;
    margin-left: 0.25rem;
}

.cm-mm-k {
    display: block;
    width: 10px;
    height: 10px;
    border-radius: 50%;
    border: 1px solid var(--text-primary);
    opacity: 0.22;
}

.cm-mm-k.n { background: var(--text-primary); opacity: 1; }

.cm-mm-k.b { background: transparent; opacity: 1; }

/* Y en cifra, que en un móvil al sol los puntos de 10 px se cuentan mal. */
.cm-mm-cif {
    font-family: monospace;
    font-size: 0.8rem;
    min-width: 4.2em;
    text-align: left;
}

.cm-mm-cuenta { font-family: monospace; font-weight: 700; font-size: 1rem; }

.cm-mm-fin { color: var(--color-danger); }

/* Tablero bloqueado (ganado o sin intentos): se ve, pero se ve muerto. */
.cm-mm-off { opacity: 0.45; filter: grayscale(0.6); pointer-events: none; }

/* El envío no llegó (sin cobertura): no gasta intento, solo avisa. */
.cm-mm-ko { animation: cm-mm-shake 0.5s; }

@keyframes cm-mm-shake {
    20%, 60% { transform: translateX(-6px); }
    40%, 80% { transform: translateX(6px); }
}

/* ---------- Cerradura de combinación: candado de 4 ruedas ----------
   Cuerpo metálico + cilindros con el número anterior y el siguiente asomando.
   El «metal» son capas translúcidas de blanco y negro SOBRE las variables del
   tema, no colores fijos: así el mismo relieve vale en claro y en oscuro. */
.cm-cerr {
    display: flex;
    flex-direction: column;
    align-items: center;
    width: 100%;
    max-width: 320px;
    margin: 0.5rem auto 0.6rem;
}

/* El ARCO de acero se retiró el 2026-08-16 (pedido tras jugarlo): en un móvil de
   360 px se comía 40 px de altura por encima de la combinación, empujaba las
   ruedas hacia abajo y no aportaba ninguna pista sobre el reto. El candado se
   reconoce igual por el cuerpo y la ventana de cifras. Sus reglas se han
   borrado en vez de dejarlas muertas: CSS que no pinta nada es CSS que alguien
   vuelve a activar por error. */

.cm-cerr-cuerpo {
    position: relative;
    width: 100%;
    padding: 0.6rem 0.5rem 0.45rem;
    border: 1px solid var(--glass-border);
    border-radius: 18px;
    background:
        linear-gradient(180deg, rgba(255, 255, 255, 0.25) 0%, rgba(255, 255, 255, 0) 45%, rgba(0, 0, 0, 0.18) 100%),
        var(--glass-bg);
    box-shadow: var(--glass-shadow);
}

/* La ventana que enmarca la combinación: hundida, con sombra interior. */
.cm-cerr-ventana {
    display: flex;
    justify-content: center;
    gap: 0.35rem;
    padding: 0.3rem;
    border: 1px solid var(--glass-border);
    border-radius: 12px;
    background: rgba(0, 0, 0, 0.16);
    box-shadow: inset 0 2px 7px rgba(0, 0, 0, 0.35);
}

.cm-cerr-rueda {
    flex: 1 1 0;
    min-width: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
}

/* Zona de toque: 44 px de alto y todo el ancho de la rueda. Se juega de pie y
   con el pulgar, así que el tamaño de la flecha manda sobre la estética. */
.cm-cerr-fl {
    width: 100%;
    min-width: 44px;
    min-height: 44px;
    padding: 0;
    border: 0;
    border-radius: 8px;
    background: transparent;
    color: var(--text-primary);
    font-size: 1.2rem;
    line-height: 1;
    cursor: pointer;
    opacity: 0.75;
    -webkit-tap-highlight-color: transparent;
}

.cm-cerr-fl:active { opacity: 1; background: rgba(255, 255, 255, 0.12); }

/* El cilindro. `overflow:hidden` recorta los números de al lado, que es lo que
   lo hace leerse como una rueda y no como tres cifras sueltas. */
.cm-cerr-cil {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.1rem;
    width: 100%;
    height: 82px;
    overflow: hidden;
    border: 1px solid var(--glass-border);
    border-radius: 9px;
    background:
        linear-gradient(180deg,
            rgba(0, 0, 0, 0.34) 0%, rgba(0, 0, 0, 0.05) 22%,
            rgba(255, 255, 255, 0.16) 50%,
            rgba(0, 0, 0, 0.05) 78%, rgba(0, 0, 0, 0.34) 100%),
        var(--glass-bg);
}

.cm-cerr-cil b {
    display: block;
    line-height: 1;
    font-family: var(--font-display), monospace;
    color: var(--text-primary);
}

.cm-cerr-num {
    font-size: 2.2rem;
    font-weight: 800;
}

.cm-cerr-ant,
.cm-cerr-sig { font-size: 0.95rem; opacity: 0.3; }

/* El «clac» del giro: los números entran desde donde venían. */
.cm-cerr-cil.gira-up b { animation: cm-cerr-sube 0.14s ease-out; }

.cm-cerr-cil.gira-down b { animation: cm-cerr-baja 0.14s ease-out; }

@keyframes cm-cerr-sube { from { transform: translateY(10px); opacity: 0.4; } }

@keyframes cm-cerr-baja { from { transform: translateY(-10px); opacity: 0.4; } }

.cm-cerr-remaches {
    display: flex;
    justify-content: space-between;
    padding: 0 0.15rem;
    margin-top: 0.4rem;
}

.cm-cerr-remaches i {
    display: block;
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: rgba(0, 0, 0, 0.28);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.45);
}

.cm-cerr-go { min-height: 48px; min-width: 104px; }

/* ---------- Braille: abecedario de referencia plegable ----------
   Va `open`: se ve de entrada, porque es donde hay que buscar la letra. Se
   pliega de un toque cuando el pad se queda sin sitio en una pantalla corta.
   El resumen es un glifo (⠿), no texto: nada que traducir. */
.cm-bab { margin: 0.5rem 0; }

.cm-bab-t {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 44px;
    padding: 0.35rem;
    border: 1px solid var(--glass-border);
    border-radius: 12px;
    background: var(--glass-bg);
    font-size: 1.4rem;
    line-height: 1;
    cursor: pointer;
    list-style: none;
}

.cm-bab-t::-webkit-details-marker { display: none; }

/* ============================================================
   MORSE (cipher_morse) — 2026-08-15
   ------------------------------------------------------------
   OJO CON EL ORDEN: las reglas base de este minijuego viven en el
   `<style>` de `app/views/player/juego.php`, que se imprime DESPUÉS
   de este archivo y por tanto gana a igual especificidad. Estas de
   aquí llevan un selector MÁS ESPECÍFICO a propósito (`.cm-mo .cm-mo-luz`
   frente a `.cm-mo-luz`, `.cm-mo-ab .cm-leyenda` frente a `.cm-leyenda`);
   no es adorno, sin eso no pisan nada. Nada de `!important`.
   ============================================================ */

/* --- La luz: ahora dura LO MISMO que el sonido -------------------------
   El destello ya no es un parpadeo de duración fija e igual para los dos
   símbolos: dura 0,090 s con el punto
   y 0,270 s con la raya, arrancando en el mismo instante que el oscilador
   (lo programa `seguirLaLuz()` con `AudioContext.currentTime`). Eso permite
   descifrar la palabra MIRANDO, sin audio — móvil en silencio, calle ruidosa
   o pérdida auditiva.
   Dos condiciones para que se distingan 90 ms de 270 ms a simple vista:
   (a) CERO transición. La de 0,08 s que había emborronaba justo la magnitud
       que hay que medir: añadía 80 ms de subida y 80 de bajada a un destello
       de 90, y el punto pasaba a parecer una raya corta.
   (b) Luz grande y de mucho contraste, porque lo que se mide es un intervalo
       de tiempo y el ojo necesita el borde bien marcado. */
.cm-mo .cm-mo-luz {
    width: 40px;
    height: 40px;
    flex: none;
    border-width: 3px;
    background: rgba(0, 0, 0, 0.45);
    transition: none;
}

.cm-mo .cm-mo-luz.on {
    background: #fff6c2;
    border-color: #fffdf2;
    box-shadow:
        0 0 0 4px rgba(255, 200, 40, 0.35),
        0 0 22px 6px var(--color-warning, #ffc107);
}

/* Sin `@media (prefers-reduced-motion)` porque no hace falta: el destello no
   lleva transición NUNCA. No es una animación decorativa que se pueda suavizar
   ni apagar — su duración ES el dato que hay que leer. */

/* --- Tabla de referencia: las 37 entradas, SIEMPRE ---------------------
   Se pintan las 27 letras (con Ñ) y los 10 dígitos, y eso nunca falló. Lo que
   fallaba era verlas: `.cm-leyenda` lleva `max-height: 40vh; overflow-y: auto`,
   y 40vh en apaisado son ~144 px para una rejilla de ~164 px. La lista se
   cortaba por la mitad de la tercera fila y el jugador veía un abecedario que
   se acababa cerca de la P — con la barra de scroll invisible en móvil, nada
   decía que hubiera más. Con el texto del sistema agrandado se cortaba antes
   todavía, y las 7 columnas fijas desbordaban a lo ancho contra el
   `overflow-x: hidden`, comiéndose además la última columna.
   Se quita el recorte —la ficha del modal ya tiene su propio scroll, uno solo
   y natural en vez de dos anidados que en iOS se pelean por el dedo— y las
   columnas pasan a ser fluidas: 7 en un móvil de 360 px, menos si el texto es
   grande (nunca desborda) y más en apaisado (menos filas). */
.cm-mo-ab .cm-leyenda {
    max-height: none;
    overflow: visible;
    grid-template-columns: repeat(auto-fill, minmax(2.6rem, 1fr));
}

/* ==========================================================================
   MINIJUEGOS — correcciones de campo del 2026-08-16
   --------------------------------------------------------------------------
   Todas estas reglas son BASE y van ANTES del bloque `@media` del final
   (regla dura 5). Clases NUEVAS salvo donde se dice: el `<style>` de
   `juego.php` se sirve DESPUÉS de este archivo, así que para pisar una clase
   suya hace falta un selector MÁS ESPECÍFICO, nunca el mismo nombre a secas.
   ========================================================================== */

/* ---------- Botón de envío único de todo el motor (`CMGames.btnGo`) ----------
   Antes cada juego llevaba su glifo («✓», «✔», «OK», «🔓») y ninguno decía que
   ese botón manda la respuesta —y que en varias pruebas gasta un intento—. Ahora
   es la misma palabra en los seis packs. Como es una palabra y no un símbolo,
   necesita ancho propio: `min-height` de 44 px (regla dura 4) y que no lo
   estruje ningún contenedor flexible. */
.cm-comprobar {
    min-height: 44px;
    padding: 0 1rem;
    font-size: 1rem;
    line-height: 1.2;
    white-space: nowrap;
    flex: 0 0 auto;
}

/* El candado y la caja del cifrado de equipo traen tamaños pensados para un
   glifo grande; con texto se leerían enormes. Selector de dos clases para ganar
   al `<style>` de `juego.php`, que va después. */
.cm-cerr .cm-comprobar,
.cm-cerr-go.cm-comprobar { font-size: 1.05rem; }

.cm-eq-linea .cm-comprobar {
    /* SIN `width:auto` (2026-08-16). Lo llevaba porque el botón compartía fila
       con la caja de texto; ahora van apilados como en los otros seis cifrados
       (ver `.cm-eq-linea` en `juego.php`), así que se le deja seguir la regla
       general del juego: ancho automático en escritorio y al 100 % en móvil,
       igual que el «Comprobar» de todos los demás minijuegos. Lo que sí es
       propio de este juego es el color: el verde de equipo. */
    min-width: 0;
    font-size: 0.95rem;
    background: var(--color-success);
    color: #fff;
    border-color: transparent;
}

/* Los juegos de sensores tienen los controles en botones REDONDOS de 56 px
   (`.cm-tri button`, especificidad 0,1,1). El de enviar deja de ser redondo:
   una palabra no cabe en un círculo. */
.cm-tri .cm-comprobar {
    width: auto;
    height: 44px;
    border-radius: var(--radius-sm);
    font-size: 1rem;
}

/* ---------- Rótulo «prueba en equipo» ----------
   Sustituye al 🗣️ de la cabecera de rol. `.cm-eq-rol .cm-eq-hab` (0,2,0) pisa
   al `.cm-eq-hab` de `juego.php` (0,1,0), que lo dejaba a 1,1rem porque era un
   emoji suelto. */
.cm-eq-rol .cm-eq-hab {
    font-size: 0.85rem;
    font-weight: 600;
    opacity: 0.85;
    line-height: 1.2;
}

/* ---------- Texto que tiene que caber en UNA línea (`CMGames.encajaTexto`) ----
   La palabra del ahorcado y la que se va componiendo en braille. El tamaño lo
   pone el JS midiendo el ancho real; aquí solo se impide que envuelva y que se
   desborde mientras se mide. */
.cm-fit {
    white-space: nowrap;
    overflow: hidden;
}

/* ---------- Simon: botón de reproducir la secuencia ----------
   La secuencia ya no arranca sola, así que este botón es el que empieza la
   prueba y el que la repite. Grande y separado de los cuatro colores para que
   no se pulse uno por otro. */
.cm-si-play {
    min-width: 96px;
    min-height: 48px;
    padding: 0 1rem;
    border: 1px solid var(--glass-border);
    border-radius: var(--radius-sm);
    background: var(--glass-bg);
    color: var(--text-primary);
    font-size: 1.6rem;
    line-height: 1;
    cursor: pointer;
    touch-action: manipulation;
}

.cm-si-play:active { opacity: 0.6; }
.cm-si-play:disabled { opacity: 0.35; cursor: default; }

/* ---------- César: disco de desplazamiento ----------
   Anillo exterior fijo (el alfabeto del mensaje) y anillo interior que gira. El
   disco es una TABLA DE CONSULTA —como el abecedario braille o la tabla morse—:
   no descifra nada por su cuenta. Arriba el criptograma, en medio la rueda y
   debajo la caja de texto de siempre (2026-08-16). */
.cm-cae { text-align: center; }

/* EL CRIPTOGRAMA, Y AHORA ES EL PROTAGONISTA (2026-08-16). Estaba a 1 rem y al
   60 % de opacidad porque debajo de la rueda se pintaba el texto ya descifrado:
   era un dato de partida que nadie llegaba a leer letra a letra. Retirado el
   descifrado en vivo, esto es LO QUE HAY QUE TRADUCIR, así que va en negrita,
   con cuerpo mayor y sin apagar. `overflow-wrap` para que un criptograma largo
   envuelva en vez de desbordar la ficha en una pantalla de 320 px. */
.cm-cae-cif {
    font-family: monospace;
    font-size: 1.35rem;
    font-weight: 700;
    letter-spacing: 0.18em;
    margin: 0.2rem 0 0.5rem;
    color: var(--text-primary);
    overflow-wrap: anywhere;
}

.cm-cae-disco {
    display: block;
    width: 100%;
    max-width: 230px;
    margin: 0 auto;
    color: var(--text-primary);
}

.cm-cae-ar { fill: var(--glass-bg); stroke: var(--glass-border); stroke-width: 1.5; }
/* El disco interior va oscuro EN LOS DOS TEMAS a propósito: las letras que giran
   son del color de aviso (amarillo) y sobre el fondo claro del tema día se
   quedaban sin contraste justo cuando hay que leerlas al sol. */
.cm-cae-in { fill: rgba(0, 0, 0, 0.42); stroke: var(--glass-border); stroke-width: 1; }
.cm-cae-t  { stroke: currentColor; stroke-width: 1; opacity: 0.28; }

.cm-cae-l {
    fill: currentColor;
    font-size: 13px;
    font-weight: 700;
    text-anchor: middle;
    dominant-baseline: middle;
}

/* El anillo que gira, en el color de aviso: se ve de un vistazo cuál de los dos
   se mueve, que es justo lo que hay que entender en dos segundos. */
.cm-cae-l2 { fill: var(--color-warning, #ffc107); font-size: 12px; }
.cm-cae-mk { fill: var(--color-warning, #ffc107); }

/* Fila de control: dos botones grandes y, en medio, la correspondencia actual
   («D→A»). Se juega con una mano: los dos botones son de 64 px. */
.cm-cae-ctrl {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    margin: 0.5rem 0 0.3rem;
}

.cm-cae-b {
    width: 64px;
    height: 52px;
    border: 1px solid var(--glass-border);
    border-radius: var(--radius-sm);
    background: var(--glass-bg);
    color: var(--text-primary);
    font-size: 1.5rem;
    line-height: 1;
    cursor: pointer;
    touch-action: manipulation;
}

.cm-cae-b:active { opacity: 0.6; }

/* INDICADOR DE LA POSICIÓN DEL DISCO: «J→A», y cada letra del color de SU
   anillo (2026-08-16, pedido tras jugarlo). Iba entero en amarillo y no decía
   nada: las dos letras son de anillos distintos y sólo una de ellas se mueve.
   Ahora la de la izquierda —la del anillo de fuera, el mensaje cifrado— va en el
   color de texto del tema y la de la derecha —la del anillo de dentro, el
   claro— en el amarillo del anillo que gira, exactamente los mismos colores que
   se ven en la rueda. */
.cm-cae-par {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.1rem;
    min-width: 4.5rem;
    font-family: monospace;
    font-size: 1.5rem;
    font-weight: 700;
}

/* NEGRO EN CLARO Y CLARO EN OSCURO, y por eso es `--text-primary` y no `#000`:
   un negro literal se pierde sobre el fondo del tema noche. Es el mismo color
   con el que se pintan las letras del anillo exterior (`currentColor` sobre
   `.cm-cae-disco`), así que la letra del indicador y la de la rueda son la
   misma tinta. */
.cm-cae-p1 { color: var(--text-primary); }

/* La flecha no es ninguna de las dos letras: en tinta normal y rebajada, para
   que no compita con ellas. */
.cm-cae-fl { font-style: normal; color: var(--text-primary); opacity: 0.55; }

/* La letra del anillo que gira, en el amarillo de aviso: el mismo con el que se
   pintan las letras del anillo interior y la marca del disco.

   PASTILLA OSCURA SOLO DEBAJO DE ESTA LETRA, y es una CORRECCIÓN (2026-08-17).
   Aquí decía que el amarillo no la necesitaba porque la ficha compone un gris
   azulado medio —rgb(89,107,119)— donde daba 3,40:1. Ese fondo no es el que se
   pinta: medido en Chromium sobre la pantalla de verdad (fondo del tema + velo
   del modal + cristal de la tarjeta) sale rgb(111,135,147) en tema día, y ahí
   el amarillo se queda en 2,32:1 — por debajo del 3:1 que pide la AA para texto
   grande, que es lo que son estos 24 px en negrita. En tema noche no había
   problema (11,3:1).
   La pastilla NO puede ir bajo las dos letras: sobre ese mismo fondo oscuro la
   letra de texto se hunde a 2,0:1, y sobre un fondo medio no hay ningún color de
   fondo que favorezca a la vez al negro y al amarillo. Así que va solo bajo la
   amarilla, y con la MISMA tinta que `.cm-cae-in` (negro al 42 %), que es
   exactamente como resuelve este caso el propio disco: las letras que giran ya
   se leen sobre ese fondo oscuro. Además dice la verdad de un vistazo —esta
   letra es la del anillo interior, y el anillo interior tiene ese fondo—.
   Medido con la pastilla: 5,37:1 en tema día y 12,70:1 en noche. La letra de al
   lado se queda con el fondo del tema, donde da 4,88:1 y 14,07:1. Ver
   `.cm-cae-p1`. */
.cm-cae-p2 {
    color: var(--color-warning, #ffc107);
    background: rgba(0, 0, 0, 0.42);
    border-radius: var(--radius-sm);
    padding: 0 0.28rem;
}

/* ---------- Conectar cables: bornes y cables que cuelgan ---------- */
.cm-cab { margin: 0.4rem 0; }

.cm-cab-svg {
    display: block;
    width: 100%;
    max-width: 320px;
    margin: 0 auto;
    touch-action: manipulation;
}

/* El cable: grueso, con la punta redondeada y sin relleno (es una curva, no una
   figura). El `stroke` lo pone el JS con el color del borne del que sale. */
.cm-cab-h {
    fill: none;
    stroke-width: 7;
    stroke-linecap: round;
}

/* Halo bajo cada cable: sin él, el cable negro se pierde sobre el fondo oscuro
   (y el blanco sobre el claro) y en un cruce no se ve cuál pasa por delante. */
.cm-cab-halo {
    fill: none;
    /* `--text-primary` y no un blanco fijo: en el tema oscuro es casi blanco (y
       rescata el cable negro) y en el claro es casi negro (y rescata el blanco).
       Con un halo blanco fijo, en tema día el cable blanco desaparecía. */
    stroke: var(--text-primary);
    opacity: 0.4;
    stroke-width: 11;
    stroke-linecap: round;
}

/* La punta que aún no está enchufada: más fina y apagada, para que se distinga
   de un cable ya puesto sin tener que seguirlo con la vista. */
.cm-cab-h.cm-cab-suelto { stroke-width: 5; opacity: 0.55; }
.cm-cab-halo.cm-cab-suelto { stroke-width: 8; opacity: 0.25; }

.cm-cab-b { cursor: pointer; }

.cm-cab-caja {
    stroke: rgba(0, 0, 0, 0.45);
    stroke-width: 1.5;
}

.cm-cab-g {
    font-size: 20px;
    text-anchor: middle;
    pointer-events: none;
}

/* Borne elegido, esperando a su pareja: aro de aviso bien gordo. Es la única
   señal de que el primer toque ha entrado, así que no puede ser sutil. */
.cm-cab-b.sel .cm-cab-caja {
    stroke: var(--color-warning, #ffc107);
    stroke-width: 4;
}

.cm-cab-b.on .cm-cab-caja { stroke: rgba(255, 255, 255, 0.75); }

.cm-cab-msg {
    text-align: center;
    font-size: 1.5rem;
    min-height: 1.6rem;
    margin: 0.2rem 0;
    color: var(--color-danger);
}

/* ---------- Laberinto: puntos perdidos en las trampas ----------
   Las trampas se dibujan dentro del canvas (aro rojo con su número); esto es el
   marcador de lo que se lleva perdido, que tiene que leerse sin mirar el
   laberinto. */
.cm-lab-pen {
    text-align: center;
    font-size: 1.6rem;
    font-weight: 800;
    line-height: 1.2;
    margin: 0.3rem 0 0;
    color: var(--color-danger, #dc3545);
}

.cm-lab-pen[hidden] { display: none !important; }

/* ---------- Objetos de inventario: icono en imagen o en emoji ----------
   Reglas BASE: van antes del bloque @media del final.

   `.obj-ico` la pintan las DOS ramas de `objeto_icono_html()` —un `<img>` con
   la imagen subida o un `<span>` con el emoji— para que ocupen lo mismo y el
   nombre no baile según lo que tenga cada objeto. Al `<span>` hay que
   devolverle el tamaño automático: un emoji dentro de una caja de 1.4em se
   recorta por abajo en Android. */
.obj-ico {
    display: inline-block;
    width: 1.4em;
    height: 1.4em;
    object-fit: cover;
    border-radius: 4px;
    vertical-align: -0.35em;
}

span.obj-ico {
    width: auto;
    height: auto;
    border-radius: 0;
    vertical-align: baseline;
}

/* La ficha de un objeto en el panel se ve COMO EN LA MOCHILA del jugador
   (2026-08-20): misma cápsula, mismo icono grande, mismo orden. El creador ve
   al editar lo mismo que va a ver el jugador, que es la única forma de saber si
   una imagen funciona a ese tamaño. Los valores están duplicados a mano en
   `app/views/player/juego.php` (`.mochila-item`) porque el juego lleva su CSS
   embebido; si se cambia uno, el otro también. */
.obj-fila {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.7rem;
    margin: 0.4rem 0;
    padding: 0.45rem 0.7rem;
    border: 1px solid var(--glass-border);
    border-radius: var(--radius-sm);
    background: var(--glass-bg);
}

/* La caja mide lo mismo lleve imagen o emoji, o la columna del nombre baila de
   una fila a otra. `flex` y no `text-align`: un emoji y un `<img>` no se
   centran igual en vertical. */
.obj-ico-caja {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: 3rem;
    height: 3rem;
    font-size: 2.1rem;
    line-height: 1;
}

.obj-ico-caja .obj-ico {
    width: 3rem;
    height: 3rem;
    border-radius: 10px;
    vertical-align: middle;
}

.obj-nom { flex: 1; min-width: 6rem; }

/* Los dos botones de la fila son de UN carácter, y con el relleno de `.btn-sm`
   (1,5 rem a cada lado) quedan dos óvalos anchos que en un móvil se comen el
   sitio del nombre. Aquí se ajustan al icono. */
/* `line-height` explícito para que los dos midan lo mismo: el ✎ se dibuja con
   fuente de emoji y el ✕ con la del texto, y con `line-height:normal` cada uno
   sacaba una altura distinta —dos círculos de distinto tamaño uno al lado del
   otro—. */
.obj-fila .btn-sm { padding: 0.4rem 0.75rem; font-size: 1rem; line-height: 1.3; }

/* Dos ajustes para que el ✕ case exactamente con el ✎ de al lado:
   · `margin: 0` — `.btn` arrastra un `margin-top` del formulario general que lo
     empujaba hacia abajo.
   · el borde transparente — el ✎ es `.btn-outline` y lleva 2 px de borde
     arriba y abajo; sin esto el ✕ salía 4 px más bajo. */
.obj-fila > form.inline .btn { margin: 0; border: 2px solid transparent; }

/* El formulario de ALTA va en su propia cápsula, igual que las filas: sin ella
   se leía como una continuación de la lista y no como «añadir uno nuevo». */
.obj-nuevo {
    margin-top: 0.6rem;
    padding: 0.6rem 0.7rem;
    border: 1px solid var(--glass-border);
    border-radius: var(--radius-sm);
    background: var(--glass-bg);
}

/* UNA COSA POR FILA. Con la rejilla de `.form-row` (`auto-fit minmax(200px)`),
   el nombre, el selector de archivo y el botón se repartían la anchura según
   cupieran, y el orden en que hay que rellenarlos dejaba de leerse: en una
   pantalla ancha el botón «Crear» acababa al lado del campo del nombre y
   parecía que lo enviaba. Apilado se lee de arriba abajo, que es como se
   rellena. */
.obj-form {
    display: grid;
    gap: var(--spacing-sm);
}

/* La excepción: el icono va PEGADO al nombre, porque son el mismo dato visto de
   dos formas. Columnas fijas y no `auto-fit`: con la rejilla automática, por
   debajo de unos 400 px el icono se iba a su propia fila y quedaba un campo
   para un emoji ocupando el ancho entero de la pantalla. */
.obj-form .obj-form-par {
    display: grid;
    grid-template-columns: 5.5rem 1fr;
    gap: var(--spacing-sm);
    align-items: end;
}

/* `.btn` arrastra un `margin-top` del formulario general que, con el botón ya
   en su propia fila de la rejilla, se sumaba al `gap` y abría un hueco doble. */
.obj-form .btn { margin-top: 0; }

/* El avatar de un personaje es redondo, aquí y en la ficha del juego. Gana a
   `.obj-ico-caja .obj-ico` por ir después con la misma especificidad. */
.obj-ico-caja .obj-ico-redondo { border-radius: 50%; }

/* El ✎ es un `<summary>` disfrazado de botón: sin `list-style:none` sale con
   el triangulito del navegador dentro del botón. */
/* Columna flexible, y no un bloque normal, POR ALINEACIÓN. Con el `<summary>`
   como `inline-block` dentro de un bloque, la caja del `<details>` se quedaba
   8 px más alta que el propio botón —el hueco que el descendente de la línea
   deja debajo— y el ✎ salía más arriba que el ✕ de al lado, que no tiene ese
   hueco. En una columna flexible no hay línea, así que el `<details>` mide
   exactamente lo que mide el botón y los dos círculos quedan a la misma
   altura. */
.obj-editar {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
}

.obj-editar > summary {
    display: inline-block;
    list-style: none;
    cursor: pointer;
    /* `width:auto` NO es redundante: el bloque responsive del final pone
       `.btn { width: 100% }` por debajo de 768 px. Mientras el ✎ es un hijo
       flexible de la fila no se nota, pero al abrirse el desplegable ocupa su
       propia línea (`flex-basis:100%`) y el botón se estiraba de lado a lado
       como una barra. Gana por especificidad (0,1,1 contra 0,1,0), no por
       orden. */
    width: auto;
}

/* El formulario sí ocupa el ancho entero: `align-items:flex-start` del padre lo
   encogería al contenido y los campos quedarían amontonados a la izquierda. */
.obj-editar > form { align-self: stretch; width: 100%; }

.obj-editar > summary::-webkit-details-marker { display: none; }

/* Abierto, el formulario ocupa su propia línea en vez de estrujar la fila. */
.obj-editar[open] { flex-basis: 100%; }

/* Y mientras se edita, el ✕ de borrar desaparece: está a un centímetro del
   botón de guardar y borrar un objeto se lleva por delante sus asignaciones a
   las pruebas y el inventario de las partidas vivas. Donde no haya `:has` el
   botón simplemente se queda: se pierde la protección, no la pantalla. */
.obj-fila:has(.obj-editar[open]) > form.inline { display: none; }

/* ============================================================
   RESPONSIVE — SIEMPRE AL FINAL DEL ARCHIVO.
   A igual especificidad gana la última regla declarada: si se
   añaden estilos base después de este bloque,
pisarán las
   adaptaciones móviles (bug del menú del 2026-07-08).
   ============================================================ */
/* Este bloque anulaba en el móvil los zooms y levantamientos del hover, que en
   una pantalla táctil se quedan PEGADOS después de tocar (no hay «salir» del
   hover). Desde el 2026-08-20 esos efectos ya no existen en ninguna parte —se
   quitaron de las reglas base—, así que aquí solo quedan los dos que SÍ siguen
   vivos porque no son ni zoom ni levantar: el deslizamiento del menú lateral y
   el giro del botón de plegarlo. */
@media (hover: none), (pointer: coarse) {
.sidebar-nav a:hover,
.sidebar-toggle:hover {
        transform: none;
    }
}

/* Atribución de los mapas (© OSM,
obligatoria): mínima y translúcida */
.leaflet-control-attribution {
    font-size: 0.6rem !important;
    background: rgba(0, 0, 0, 0.35) !important;
    color: rgba(255, 255, 255, 0.55) !important;
    padding: 0 4px !important;
    border-radius: 4px 0 0 0;
}

.leaflet-control-attribution a { color: rgba(255, 255, 255, 0.55) !important; text-decoration: none; }

@media (max-width: 768px) {/* Espaciado compacto: más contenido en pantalla sin pegarlo a los bordes */
    :root {
        --spacing-xs: 0.3rem;
        --spacing-sm: 0.55rem;
        --spacing-md: 0.85rem;
        --spacing-lg: 1.1rem;
    }

.card-grid { gap: 0.6rem; }

.card { margin-bottom: 0.6rem; }

.section { padding: var(--spacing-md); }

.sidebar {
        width: 100%;
        left: -100%;
    }

.sidebar.collapsed { left: -100%; }

.sidebar:not(.collapsed) { left: 0; }

.sidebar-logo { width: 50%; }

.sidebar-toggle {
        left: var(--spacing-sm);
    }

.sidebar:not(.collapsed) ~ .sidebar-toggle {
        left: auto;
        right: var(--spacing-sm);
    }

body.app-shell .main-content {
        left: 0;
        padding: var(--spacing-sm);
        /* Hueco para el botón ☰: es `position: fixed` con z-index 9999 (50px de
           alto + 0.85rem desde arriba), así que FLOTA SOBRE el contenido y tapaba
           las primeras letras del título. Sangrar solo la primera línea no basta:
           los títulos largos ocupan dos y la segunda seguía cayendo debajo del
           botón. Reservar el alto es independiente del texto y de si la barra
           lateral está abierta (con ella abierta el botón salta a la derecha). */
        padding-top: 4.25rem;
    }

    /* El aire lateral lo da SOLO .main-content. Antes se sumaba también el de
       .container (0.85 + 0.85 = 1.7rem) y el panel de administración quedaba con
       el TRIPLE de margen que la vista de jugador, que usa un único .public-main
       con 0.55rem. Ahora ambas vistas dan el mismo aire. */
    body.app-shell .main-content > .container {
        padding-left: 0;
        padding-right: 0;
    }

.section-title { font-size: 1.45rem; }

.btn { width: 100%; }

.card-grid { grid-template-columns: 1fr; }

/* ---- Cabecera pública: menú colapsado con hamburguesa ---- */
    .nav-toggle { display: block; }

.site-header { padding: var(--spacing-sm); }

.site-header nav {
        display: none;
        width: 100%;
        flex-direction: column;
        align-items: stretch;
        gap: 0.15rem;
        margin-top: var(--spacing-xs);
        padding-top: var(--spacing-xs);
        border-top: 1px solid var(--glass-border);
    }

.site-header nav.open { display: flex; }

.site-header nav a,
.site-header button.link {
        display: block;
        width: 100%;
        text-align: left;
        padding: 0.7rem 0.9rem;
        border-radius: 12px;
    }

.site-header nav form.inline { display: block; width: 100%; }

.site-header nav select { align-self: flex-start; margin: 0.3rem 0; }

/* ---- Distribución general en pantallas pequeñas ---- */
    .public-main { padding: var(--spacing-sm); }

.page-head { flex-direction: column; align-items: flex-start; gap: 0.5rem; }

.page-head h1 { font-size: 1.4rem; }

.page-head .actions { width: 100%; }

fieldset { padding: var(--spacing-sm); }

fieldset legend { font-size: 0.95rem; }

.tab-list {
        flex-wrap: nowrap;
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
        scrollbar-width: none;
    }

.tab-list::-webkit-scrollbar { display: none; }

.tab-button { white-space: nowrap; padding: var(--spacing-xs) var(--spacing-sm); }

table { font-size: 0.85rem; }

th,
td { padding: 0.45rem 0.5rem; }

.card-body { padding: var(--spacing-sm); }

.hero h1 { font-size: 1.9rem; }

.hero p { font-size: 1rem; }

.site-footer { text-align: center; padding: var(--spacing-sm); }

/* Los botones dentro de .actions y formularios inline no ocupan todo el ancho */
    .actions .btn,
.inline .btn,
td .btn { width: auto; }

    .adv-f1,
    .ply-f1 { flex-direction: column; align-items: stretch; }

    .adv-badges,
    .ply-badges { justify-content: flex-start; }

    .adv-badges .btn { width: auto; }

    .adv-badges .btn.btn-ico.btn-sm {
        width: 2.125rem;
        min-width: 2.125rem;
        max-width: 2.125rem;
    }

    .adv-cover,
    .adv-cover-ph { width: 64px; height: 44px; }

    .adv-owner { width: 36px; height: 36px; }

    .adv-f2,
    .ply-f2 { gap: 0.3rem 0.75rem; font-size: 0.84rem; }

    .aud-f2 { font-size: 0.82rem; }

    .hue-f2 { font-size: 0.82rem; }

/* La pantalla de juego gestiona sus propios tamaños (correccion_01) */
    #game .btn { width: auto; }

/* CTA a ancho completo y apiladas en móvil (petición de campo): botones del
   modal de prueba, pantalla final, ficha de pausa y acciones Empezar/Continuar. */
    #gModalBody .btn,
    #gFinalBody .btn,
    #gPausaFicha .btn { width: 100%; }
    .card-actions-stack form.inline,
    .card-actions-stack form.inline .btn,
    .card-actions-stack .btn { width: 100%; }
    .site-header nav .theme-toggle-nav {
        width: 100%; justify-content: flex-start; text-align: left; padding: 0.7rem 0.9rem;
    }
}

/* Medallero en móvil: 4 por fila (parte del bloque responsive) */
@media (max-width: 768px) {.logros-grid { grid-template-columns: repeat(4, 1fr); gap: 0.45rem; }

.logro-mini { padding: 0.35rem; }

.logro-mini .logro-icono { font-size: 1.7rem; margin-bottom: 0.2rem; }

.logro-mini h3 { font-size: 0.62rem; }
}

/* CTA del juego a ancho completo en móvil (parte del bloque responsive).
   AQUÍ y no en juego.php: su <style> está dentro de un if de tema y en las
   aventuras SIN tema no se imprimía (regresión detectada en auditoría).
   Doble id para ganar al «#game .btn{width:auto}» de la vista. */
@media (max-width: 768px) {
#game #gModalBody .btn,
#game #gFinalBody .btn,
#game #gPausaFicha .btn,
#game #gIntro .btn { width: 100%; }
}

/* Portada pública en móvil (parte del bloque responsive) */
@media (max-width: 768px) {
.home-pasos { grid-template-columns: repeat(2, 1fr); }

.home-testi { grid-template-columns: 1fr; }

.home-cifra { flex: 1; min-width: 100px; padding: 0.7rem 0.8rem; }

.home-cifra strong { font-size: 1.3rem; }
}

/* Accesibilidad: respetar «reducir movimiento» del sistema (parte del responsive) */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
  animation-duration: 0.001ms !important;
  animation-iteration-count: 1 !important;
  transition-duration: 0.001ms !important;
  scroll-behavior: auto !important;
}

.btn:hover { transform: none; }
}

/* ---------- Selector de idioma con bandera (cabecera pública) ---------- */
/* Va después de `.site-header nav a` a propósito: los enlaces de la lista
   heredan de ahí el color y el tamaño, y aquí solo se corrige la caja. */
.lang-pick { position: relative; }

.lang-pick > summary {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    list-style: none;               /* quita el triángulo nativo (Firefox) */
    padding: 0.3rem 0.7rem;
    border: 1px solid var(--glass-border);
    border-radius: 50px;
    background: var(--glass-bg);
    color: var(--text-primary);
    font-family: var(--font-display);
    font-weight: 500;
    font-size: 0.85rem;
    line-height: 1;
    cursor: pointer;
    user-select: none;
}

.lang-pick > summary::-webkit-details-marker { display: none; }  /* …y en Safari */
.lang-pick > summary::after { content: '▾'; font-size: 0.7rem; opacity: 0.65; }
.lang-pick[open] > summary::after { content: '▴'; }

/* El filo gris despega la bandera del fondo cuando comparten color (la franja
   blanca de Francia sobre la cabecera clara, por ejemplo). */
.bandera {
    display: block;
    flex: none;
    width: 21px;
    height: 14px;
    border-radius: 2px;
    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.18);
}

.lang-pick-lista {
    position: absolute;
    top: calc(100% + 0.35rem);
    right: 0;
    z-index: 120;                   /* la cabecera es sticky con z-index 100 */
    min-width: 100%;
    margin: 0;
    padding: 0.3rem;
    list-style: none;
    background: var(--pop-bg);
    border: 1px solid var(--pop-borde);
    border-radius: 14px;
    box-shadow: var(--glass-shadow);
}

.site-header nav .lang-pick-lista a {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.4rem 0.7rem;
    border-radius: 10px;
    font-size: 0.85rem;
    white-space: nowrap;
}

.lang-pick-lista a[aria-current] { background: var(--glass-bg); font-weight: 700; }

@media (max-width: 768px) {
    /* Con el menú en columna, un desplegable flotante se saldría de la pantalla:
       aquí la lista va en el flujo, debajo del botón. */
    .site-header nav .lang-pick { width: 100%; }

    .site-header nav .lang-pick > summary {
        width: 100%;
        padding: 0.7rem 0.9rem;
        border-radius: 12px;
    }

    .site-header nav .lang-pick > summary::after { margin-left: auto; }

    .site-header nav .lang-pick-lista {
        position: static;
        margin-top: 0.25rem;
        display: grid;
        grid-template-columns: repeat(auto-fill, minmax(5.5rem, 1fr));
        gap: 0.15rem;
    }
}

@media (max-width: 768px) {
    /* Ficha del minijuego: una sola columna y caja más estrecha. La rejilla ya
       cae sola, pero el padding lateral del fieldset más el suyo dejaban los
       renglones en unos 200 px. */
    .mj-ficha {
        grid-template-columns: 1fr;
        /* En una sola columna TODAS las secciones son vecinas de arriba abajo,
           así que el hueco de filas es la única separación que queda: se aprieta
           el lateral (que ya no separa nada) pero se mantiene el vertical. */
        gap: var(--spacing-sm) var(--spacing-xs);
        padding: var(--spacing-xs) var(--spacing-sm);
        border-left-width: 3px;
    }

    .mj-ficha-sec p { font-size: 0.85rem; }
}

/* ---------- Minijuegos: adaptación a pantallas estrechas ----------
   Va DENTRO del bloque responsive del final (regla dura 5). El candado se
   aprieta para que en 360 px siga entrando entero, sin scroll horizontal. */
@media (max-width: 380px) {
    .cm-cerr-ventana { gap: 0.2rem; padding: 0.22rem; }

    .cm-cerr-num { font-size: 2rem; }

    .cm-cerr-cil { height: 76px; }

    .cm-mm-cif { font-size: 0.72rem; min-width: 3.8em; }

    .cm-mm-hp { width: 20px; height: 20px; }
}

/* Sin animación para quien la ha desactivado en el sistema: el giro de la
   rueda y el aviso de «no se ha enviado» son decorativos, la información
   (el número, el intento no gastado) está igualmente en pantalla. */
@media (prefers-reduced-motion: reduce) {
    .cm-cerr-cil.gira-up b,
    .cm-cerr-cil.gira-down b,
    .cm-mm-ko { animation: none; }
}
