/* =====================================================================
   CAEDES FEROX - Sistema visual
   Negro, rojo y grises oscuros. Estetica gaming competitiva.
   ===================================================================== */

:root {
    /* Superficies, de la mas profunda a la mas elevada */
    --bg-void:      #050506;
    --bg-base:      #0A0A0C;
    --bg-surface:   #111114;
    --bg-raised:    #17171B;
    --bg-overlay:   #1E1E24;
    --bg-hover:     #232329;

    /* Rojo de la marca */
    --red:          #E11D2A;
    --red-bright:   #FF2D3E;
    --red-deep:     #9E0E19;
    --red-glow:     rgba(225, 29, 42, 0.35);
    --red-faint:    rgba(225, 29, 42, 0.08);
    --red-line:     rgba(225, 29, 42, 0.25);

    /* Texto */
    --text:         #F2F3F5;
    --text-soft:    #A8ACB4;
    --text-dim:     #6E727B;
    --text-faint:   #4A4D55;

    /* Bordes */
    --line:         #24242B;
    --line-soft:    #1B1B21;
    --line-bright:  #33333C;

    /* Estados */
    --ok:           #2ECC71;
    --warn:         #F0A500;
    --danger:       #FF4757;
    --info:         #3A9BFF;

    /* Rareza por defecto. Las de verdad ya no viven aqui: el lider las
       crea y les elige el color en el panel, y el layout inyecta sus
       reglas. Esta se queda como suelo para lo que llegue sin rareza. */
    --r-comun: #8A8F98;

    /* ----------------------------------------------------------------
       Sistema de movimiento

       Cuatro duraciones y tres curvas. Todo lo que se mueve en la web
       usa solo estas: es la coherencia, y no la cantidad de efectos, lo
       que hace que una interfaz se lea como cuidada. Un catalogo de
       animaciones distintas se lee como plantilla.

       Nada pasa de 320 ms. Por encima de ahi el movimiento deja de
       percibirse como elegancia y empieza a percibirse como lentitud.
       ---------------------------------------------------------------- */
    --m-fast:  140ms;   /* microrespuesta: pulsar, marcar, resaltar */
    --m-base:  220ms;   /* entrada de un elemento */
    --m-slow:  320ms;   /* recorridos largos o piezas grandes */
    --m-step:   45ms;   /* separacion entre uno y el siguiente */

    /* Frena al final: la pieza llega y se asienta, no aterriza de golpe. */
    --m-out:   cubic-bezier(.22, 1, .36, 1);
    /* Arranca despacio y acelera: para lo que se va. */
    --m-in:    cubic-bezier(.55, 0, 1, .45);
    /* Sobrepasa y vuelve. Es la anticipacion: imperceptible a conciencia,
       y es lo que hace que el movimiento tenga peso en vez de parecer
       una interpolacion lineal. */
    --m-back:  cubic-bezier(.34, 1.4, .64, 1);

    /* Formas */
    --r-sm: 8px;
    --r-md: 12px;
    --r-lg: 18px;
    --r-xl: 24px;
    --r-full: 999px;

    /* Sombras */
    --shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.4);
    --shadow-md: 0 8px 24px rgba(0, 0, 0, 0.5);
    --shadow-lg: 0 18px 48px rgba(0, 0, 0, 0.6);
    --shadow-red: 0 6px 24px rgba(225, 29, 42, 0.28);

    /* Medidas del armazon */
    --header-h: 62px;
    --bottom-h: 68px;
    --sidebar-w: 264px;

    --ease: cubic-bezier(0.22, 1, 0.36, 1);
}

/* --------------------------------------------------------------------
   Base
   -------------------------------------------------------------------- */

*, *::before, *::after { box-sizing: border-box; }

/* El atributo hidden tiene que ganar siempre.
   -------------------------------------------------------------------
   El navegador lo aplica con la regla [hidden] { display: none }, que
   pierde en especificidad contra cualquier clase que declare su propio
   display. Un .medal-sentinel { display: grid } marcado como hidden se
   seguia viendo, y ahi se quedaba el spinner girando sin fin porque nada
   volvia a tocarlo. Con !important, esconder algo es esconderlo. */
[hidden] { display: none !important; }

/* Los navegadores moviles pintan un recuadro azul translucido encima de lo
   que tocas. Se quita aqui de una vez para todo lo pulsable; el aviso de
   "te he leido" lo da el hundido de :active que hay mas abajo. */
a, button, label, summary,
input, select, textarea,
[role="button"], [tabindex] {
    -webkit-tap-highlight-color: transparent;
}

html {
    -webkit-text-size-adjust: 100%;
    scroll-behavior: smooth;
}

body {
    margin: 0;
    min-height: 100vh;
    background: var(--bg-base);
    color: var(--text);
    font-family: 'Segoe UI', system-ui, -apple-system, 'Roboto', 'Helvetica Neue', sans-serif;
    font-size: 15px;
    line-height: 1.55;
    -webkit-font-smoothing: antialiased;
    overflow-x: hidden;
}

/* Resplandor rojo tenue de fondo, fijo tras todo el contenido */
body::before {
    content: '';
    position: fixed;
    inset: 0;
    pointer-events: none;
    z-index: 0;
    background:
        radial-gradient(1000px 520px at 12% -10%, rgba(225, 29, 42, 0.10), transparent 62%),
        radial-gradient(760px 420px at 100% 4%, rgba(225, 29, 42, 0.06), transparent 60%);

    /* Se lleva a su propia capa. Son dos degradados del tamano de la pantalla
       que ademas no se mueven nunca: sin esto el navegador los vuelve a pintar
       cada vez que la pagina se desplaza por encima. Asi se pintan una vez y
       despues solo se recomponen, que es gratis. Es la unica capa que se fuerza
       en toda la hoja; hacerlo a la ligera gasta memoria de video. */
    transform: translateZ(0);
}

h1, h2, h3, h4, h5 {
    margin: 0 0 .5rem;
    font-weight: 800;
    letter-spacing: -0.02em;
    line-height: 1.2;
    /* Los nicknames de Free Fire son palabras largas sin espacios: sin esto
       se salen del contenedor y arrastran la pagina a lo ancho. */
    overflow-wrap: anywhere;
}

/* Icon::render() emite los SVG sin width ni height. Si ninguna regla de
   componente les da tamano, el navegador usa el de un objeto sin dimensiones
   (300x150) y el icono sale gigante, ensanchando la pagina entera. Este es
   el tamano por defecto; al ser un selector de elemento, cualquier regla de
   componente (.btn svg, .icon-btn svg, .card-title svg...) sigue mandando. */
svg {
    width: 1.05em;
    height: 1.05em;
    flex: none;
    vertical-align: -0.15em;
}

h1 { font-size: clamp(1.5rem, 4vw, 2rem); }
h2 { font-size: clamp(1.2rem, 3vw, 1.5rem); }
h3 { font-size: 1.08rem; }
h4 { font-size: .96rem; }

p { margin: 0 0 .85rem; }
p:last-child { margin-bottom: 0; }

a {
    color: var(--red-bright);
    text-decoration: none;
    transition: color .18s var(--ease);
}
a:hover { color: #FF6070; }

img { max-width: 100%; display: block; }

hr {
    border: 0;
    border-top: 1px solid var(--line);
    margin: 1.25rem 0;
}

::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: var(--bg-base); }
::-webkit-scrollbar-thumb {
    background: var(--line-bright);
    border-radius: var(--r-full);
    border: 2px solid var(--bg-base);
}
::-webkit-scrollbar-thumb:hover { background: var(--red-deep); }

::selection { background: var(--red); color: #fff; }

:focus-visible {
    outline: 2px solid var(--red-bright);
    outline-offset: 2px;
}

/* --------------------------------------------------------------------
   Tipografia de apoyo
   -------------------------------------------------------------------- */

.eyebrow {
    font-size: .68rem;
    font-weight: 800;
    letter-spacing: .16em;
    text-transform: uppercase;
    color: var(--text-dim);
}

.muted   { color: var(--text-soft); }
.dim     { color: var(--text-dim); }
.tiny    { font-size: .78rem; }
.small   { font-size: .86rem; }
.strong  { font-weight: 700; }
.center  { text-align: center; }
.right   { text-align: right; }
.nowrap  { white-space: nowrap; }
.red     { color: var(--red-bright); }
.ok-text { color: var(--ok); }
.warn-text { color: var(--warn); }
.danger-text { color: var(--danger); }
.mono {
    font-family: 'Cascadia Mono', 'Consolas', ui-monospace, monospace;
    font-size: .88em;
}

.truncate {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* --------------------------------------------------------------------
   Armazon
   -------------------------------------------------------------------- */

.shell {
    position: relative;
    z-index: 1;
    min-height: 100vh;
    display: flex;
    flex-direction: column;
}

.page {
    flex: 1;
    width: 100%;
    max-width: 1220px;
    margin: 0 auto;
    padding: 1.1rem 1rem calc(var(--bottom-h) + 2rem);
}

/* Paginas a dos columnas (el muro). Con el ancho de lectura normal, en un
   monitor grande el contenido queda en una franja central y los lados se
   ven vacios: aqui se deja crecer sin pasar de lo comodo de leer. */
.page-wide { max-width: 1520px; }

@media (min-width: 1440px) {
    .page-wide { padding-left: 1.75rem; padding-right: 1.75rem; }
}

/* Escala compacta del movil.
   -------------------------------------------------------------------
   El limite va a la par del resto del armazon movil (la barra inferior y
   el menu "Mas" aparecen por debajo de 900px): un telefono grande que
   declare 700px de ancho debe recibir la misma escala apretada que uno de
   400px, no la de escritorio.

   Se recorta el margen lateral -cada pixel que no gasta el hueco lo gana la
   tarjeta- y se baja un punto la tipografia de cabecera, que a 24px se
   comia un tercio de la pantalla. */
@media (max-width: 767.98px) {
    .page { padding: .85rem .7rem calc(var(--bottom-h) + 1.5rem); }

    .card { padding: .85rem; }

    h1 { font-size: 1.28rem; }
    h2 { font-size: 1.1rem; }

    .eyebrow { font-size: .6rem; letter-spacing: .12em; }

    .avatar.lg { width: 46px; height: 46px; font-size: .92rem; }

    .tag { padding: .18rem .45rem; }
}

.page-head {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
    margin-bottom: 1.25rem;
}

.page-head h1 { margin: 0; }
.page-head .sub { color: var(--text-soft); font-size: .9rem; }

/* --------------------------------------------------------------------
   Cabecera
   -------------------------------------------------------------------- */

.header {
    position: sticky;
    top: 0;
    z-index: 60;
    height: var(--header-h);
    display: flex;
    align-items: center;
    gap: .85rem;
    padding: 0 1rem;
    /* En movil va casi opaca y sin desenfoque: la cabecera esta pegada arriba
       durante todo el recorrido, y desenfocar lo que pasa por detras obliga a
       rehacer la franja en cada fotograma. Es de las cosas que mas cuesta en
       un telefono de gama media. Al 96% el resultado es practicamente el
       mismo que el 86% con desenfoque. */
    background: rgba(10, 10, 12, 0.96);
    border-bottom: 1px solid var(--line);
}

/* En escritorio si se puede pagar, y ahi el cristal se aprecia mas porque hay
   mas pagina moviendose por detras. */
@media (min-width: 1024px) {
    .header {
        background: rgba(10, 10, 12, 0.86);
        backdrop-filter: blur(16px);
        -webkit-backdrop-filter: blur(16px);
    }
}

.header::after {
    content: '';
    position: absolute;
    left: 0; right: 0; bottom: -1px;
    height: 1px;
    background: linear-gradient(90deg, transparent, var(--red), transparent);
    opacity: .5;
}

.brand {
    display: flex;
    align-items: center;
    gap: .6rem;
    color: var(--text);
    font-weight: 900;
    letter-spacing: .04em;
    flex-shrink: 0;
}
.brand:hover { color: var(--text); }

.brand img {
    width: 36px;
    height: 36px;
    object-fit: contain;
    filter: drop-shadow(0 0 10px var(--red-glow));
}

.brand-name {
    font-size: .96rem;
    line-height: 1.05;
}
.brand-name span {
    display: block;
    font-size: .58rem;
    font-weight: 700;
    letter-spacing: .22em;
    color: var(--red-bright);
}

.header-spacer { flex: 1; }

.header-actions {
    display: flex;
    align-items: center;
    gap: .4rem;
}

/* Navegacion superior (escritorio) */
.top-nav {
    display: none;
    align-items: center;
    gap: .15rem;
    margin-left: .75rem;
}

.top-nav a {
    padding: .5rem .8rem;
    border-radius: var(--r-sm);
    color: var(--text-soft);
    font-size: .875rem;
    font-weight: 600;
    transition: all .18s var(--ease);
}

.top-nav a:hover {
    background: var(--bg-raised);
    color: var(--text);
}

.top-nav a.is-active {
    color: var(--text);
    background: var(--red-faint);
    box-shadow: inset 0 -2px 0 var(--red);
}

/* Boton icono de la cabecera */
.icon-btn {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    background: var(--bg-surface);
    color: var(--text-soft);
    cursor: pointer;
    transition: all .18s var(--ease);
}

.icon-btn:hover {
    border-color: var(--red-line);
    color: var(--red-bright);
    background: var(--bg-raised);
}

.icon-btn svg { width: 19px; height: 19px; }

.badge-dot {
    position: absolute;
    top: 5px;
    right: 5px;
    min-width: 18px;
    height: 18px;
    padding: 0 5px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--r-full);
    background: var(--red);
    color: #fff;
    font-size: .62rem;
    font-weight: 800;
    /* El body impone line-height 1.55: dentro de un circulo de 18px esa
       altura de linea no cabe y empuja la cifra hacia arriba. Con 1 el
       texto ocupa lo justo y el centrado del flex funciona de verdad. */
    line-height: 1;
    /* Cifras de ancho fijo: un "1" mide lo mismo que un "8", asi el numero
       no se descoloca al cambiar de digito. */
    font-variant-numeric: tabular-nums;
    /* El aro va por fuera con box-shadow en lugar de border: con
       box-sizing:border-box un borde de 2px se comia 4px de caja interior,
       que es lo que dejaba la cifra sin sitio. */
    box-shadow: 0 0 0 2px var(--bg-base);
    animation: pop .25s var(--ease);
}

@keyframes pop {
    from { transform: scale(0); }
    to   { transform: scale(1); }
}

/* Avatar de la cabecera */
.header-avatar {
    display: flex;
    align-items: center;
    gap: .55rem;
    padding: .3rem .3rem .3rem .7rem;
    border: 1px solid var(--line);
    border-radius: var(--r-full);
    background: var(--bg-surface);
    transition: all .18s var(--ease);
}
.header-avatar:hover { border-color: var(--red-line); }

.header-avatar .who {
    display: none;
    text-align: right;
    line-height: 1.15;
}
.header-avatar .who strong {
    display: block;
    font-size: .8rem;
    color: var(--text);
    max-width: 130px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.header-avatar .who span { font-size: .66rem; color: var(--text-dim); }

/* --------------------------------------------------------------------
   Avatares
   -------------------------------------------------------------------- */

.avatar {
    position: relative;
    display: grid;
    place-items: center;
    flex-shrink: 0;
    width: 40px;
    height: 40px;
    border-radius: var(--r-full);
    background: linear-gradient(135deg, var(--red-deep), #2A0A0E);
    color: #fff;
    font-weight: 800;
    font-size: .82rem;
    letter-spacing: .02em;
    overflow: hidden;
    border: 1px solid var(--line-bright);
}

.avatar img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.avatar.xs { width: 28px; height: 28px; font-size: .62rem; }
.avatar.sm { width: 34px; height: 34px; font-size: .7rem; }
.avatar.lg { width: 62px; height: 62px; font-size: 1.2rem; }
.avatar.xl { width: 96px; height: 96px; font-size: 1.9rem; border-width: 2px; }
.avatar.xxl { width: 128px; height: 128px; font-size: 2.6rem; border-width: 3px; }

.avatar.ring {
    box-shadow: 0 0 0 2px var(--bg-base), 0 0 0 4px var(--red);
}

/* --------------------------------------------------------------------
   Navegacion inferior (movil)
   -------------------------------------------------------------------- */

.bottom-nav {
    position: fixed;
    left: 0; right: 0; bottom: 0;
    z-index: 70;
    height: calc(var(--bottom-h) + env(safe-area-inset-bottom, 0px));
    padding-bottom: env(safe-area-inset-bottom, 0px);
    display: flex;
    /* Sin backdrop-filter a proposito. La barra estaba al 94% de opacidad: el
       desenfoque se veia por un 6% que no distingue nadie, pero el navegador
       tenia que volver a desenfocar la franja entera en cada fotograma del
       desplazamiento, y esta fija en pantalla todo el rato. Subiendo la
       opacidad al 97% se ve igual y el trabajo desaparece. */
    background: rgba(10, 10, 12, 0.97);
    border-top: 1px solid var(--line);
}

.bottom-nav a,
.bottom-nav button {
    position: relative;
    /* Base 0 y min-width 0: los huecos salen iguales aunque las etiquetas
       midan distinto. */
    flex: 1 1 0;
    min-width: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 3px;
    padding: 0 2px;
    border: 0;
    background: transparent;
    font-family: inherit;
    color: var(--text-dim);
    font-size: .62rem;
    font-weight: 700;
    letter-spacing: .01em;
    cursor: pointer;
    transition: color .18s var(--ease);
}

.bottom-nav a span,
.bottom-nav button > span:not(.nav-more-icon):not(.nav-badge) {
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.bottom-nav a svg { width: 21px; height: 21px; }

/* Boton "Mas": el + va dentro de un circulo para que se lea como accion y
   no como un apartado mas de la barra. */
.nav-more-icon {
    display: grid;
    place-items: center;
    width: 26px;
    height: 26px;
    border-radius: var(--r-full);
    background: linear-gradient(180deg, var(--red-bright), var(--red-deep));
    color: #fff;
    box-shadow: 0 2px 10px var(--red-glow);
}

.nav-more-icon svg { width: 17px; height: 17px; }

.bottom-nav .is-active { color: var(--red-bright); }

.bottom-nav .is-active::before {
    content: '';
    position: absolute;
    top: 0;
    width: 34px;
    height: 3px;
    border-radius: 0 0 var(--r-full) var(--r-full);
    background: var(--red);
    box-shadow: 0 0 12px var(--red-glow);
}

.bottom-nav .nav-badge {
    position: absolute;
    top: 8px;
    right: calc(50% - 20px);
    min-width: 15px;
    height: 15px;
    padding: 0 3px;
    display: grid;
    place-items: center;
    border-radius: var(--r-full);
    background: var(--red);
    color: #fff;
    font-size: .58rem;
    font-weight: 800;
}

/* Hoja que despliega el boton "Mas" de la barra inferior.
   Reutiliza la maquinaria de los modales, pero anclada abajo. */
.nav-sheet-backdrop {
    align-items: flex-end;
    padding: 0;
}

.nav-sheet {
    width: 100%;
    background: var(--bg-surface);
    border-top: 1px solid var(--line-bright);
    border-radius: var(--r-lg) var(--r-lg) 0 0;
    /* Deja libre el alto de la barra para no taparla con la hoja. */
    padding: .9rem 1rem calc(1rem + var(--bottom-h) + env(safe-area-inset-bottom, 0px));
    animation: sheet-in .26s var(--ease);
}

@keyframes sheet-in {
    from { transform: translateY(100%); }
    to   { transform: none; }
}

.nav-sheet-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: .8rem;
    font-size: .7rem;
    font-weight: 800;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--text-dim);
}

.nav-sheet-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: .5rem;
}

.nav-sheet-grid a {
    position: relative;
    min-width: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: .45rem;
    padding: .95rem .35rem;
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    background: var(--bg-raised);
    color: var(--text-soft);
    font-size: .68rem;
    font-weight: 700;
    line-height: 1.2;
    text-align: center;
}

.nav-sheet-grid a svg { width: 22px; height: 22px; }

.nav-sheet-grid a.is-active {
    border-color: var(--red-line);
    color: var(--red-bright);
}

/* --------------------------------------------------------------------
   Tarjetas
   -------------------------------------------------------------------- */

.card {
    position: relative;
    background: var(--bg-surface);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    padding: 1.1rem;
    transition: border-color .2s var(--ease), transform .2s var(--ease);
}

.card.hover:hover {
    border-color: var(--red-line);
    transform: translateY(-2px);
}

.card.flush { padding: 0; overflow: hidden; }

.card-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .75rem;
    margin-bottom: .9rem;
    padding-bottom: .75rem;
    border-bottom: 1px solid var(--line-soft);
}

.card-head h2, .card-head h3 { margin: 0; }

.card-title {
    display: flex;
    align-items: center;
    gap: .5rem;
    font-size: .95rem;
    font-weight: 800;
}

.card-title svg {
    width: 17px;
    height: 17px;
    color: var(--red-bright);
}

/* Tarjeta con acento rojo lateral */
.card.accent::before {
    content: '';
    position: absolute;
    left: 0;
    top: 18%;
    bottom: 18%;
    width: 3px;
    border-radius: 0 var(--r-full) var(--r-full) 0;
    background: linear-gradient(180deg, var(--red-bright), var(--red-deep));
}

/* --------------------------------------------------------------------
   Rejillas
   -------------------------------------------------------------------- */

.grid { display: grid; gap: 1rem; }

/* Un item de rejilla no baja de su ancho de contenido minimo mientras no se
   le indique. Sin esto, la fila de pestanas -que ya trae su propio scroll
   horizontal- estira su columna hasta caber entera, y con ella el documento:
   la pagina acaba mas ancha que la pantalla y se puede arrastrar a los lados.
   Con min-width:0 la columna se ajusta al ancho disponible y las pestanas
   pasan a desplazarse por dentro, que es como estaban pensadas. */
.grid > *,
.split > *,
.feed-layout > * { min-width: 0; }

.grid-2 { grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
.grid-3 { grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); }
.grid-4 { grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); }
.grid-stats { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }
.grid-medals { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: .85rem; }

.stack { display: flex; flex-direction: column; gap: 1rem; }
.stack.tight { gap: .55rem; }
.row {
    display: flex;
    align-items: center;
    gap: .6rem;
    flex-wrap: wrap;
}
.row.between { justify-content: space-between; }
.row.tight { gap: .35rem; }
.row.nowrap-row { flex-wrap: nowrap; }

.spacer { flex: 1; }

/* --------------------------------------------------------------------
   Estadisticas
   -------------------------------------------------------------------- */

.stat {
    position: relative;
    padding: 1rem;
    background: var(--bg-surface);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    overflow: hidden;
    transition: border-color .2s var(--ease);
}

.stat:hover { border-color: var(--red-line); }

.stat::after {
    content: '';
    position: absolute;
    right: -26px;
    top: -26px;
    width: 76px;
    height: 76px;
    border-radius: 50%;
    background: var(--red-faint);
}

.stat .label {
    display: flex;
    align-items: center;
    gap: .35rem;
    font-size: .68rem;
    font-weight: 800;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: var(--text-dim);
    margin-bottom: .3rem;
}

.stat .label svg { width: 13px; height: 13px; }

.stat .value {
    font-size: 1.65rem;
    font-weight: 900;
    letter-spacing: -0.03em;
    line-height: 1.05;
}

.stat .foot {
    margin-top: .2rem;
    font-size: .74rem;
    color: var(--text-dim);
}

.stat.is-red .value { color: var(--red-bright); }
.stat.is-ok .value { color: var(--ok); }
.stat.is-warn .value { color: var(--warn); }

/* Fila compacta de estadisticas del perfil */
.stat-row {
    display: grid;
    /* min() evita que el minimo de 84px sea mayor que el propio contenedor
       en pantallas estrechas, que es lo que provocaba el desbordamiento. */
    grid-template-columns: repeat(auto-fit, minmax(min(84px, 100%), 1fr));
    gap: .5rem;
}

/* En el movil el reparto automatico daba cuatro columnas y dejaba la quinta
   cifra sola en una fila, con un hueco raro al lado. Con tres fijas, las
   cinco del inicio quedan 3 + 2 y las tres de la ficha del clan, en linea.
   A cambio cada casilla es mas ancha, asi que se rebaja su alto y su cifra:
   el bloque queda igual de apretado que con cuatro columnas, pero sin la
   casilla huerfana. */
@media (max-width: 767.98px) {
    /* Tres fijas daba 3 + 2 con las cinco del perfil, que es lo que se
       buscaba, pero apretaba a un tercio del ancho a los grupos de dos: la
       etiqueta no cabia y se salia. Con auto-fit y un minimo de 90px salen
       igualmente tres cuando hay cinco, y las que sobran se reparten el
       hueco cuando son menos. */
    .stat-row {
        grid-template-columns: repeat(auto-fit, minmax(min(90px, 100%), 1fr));
        gap: .4rem;
    }

    .stat-chip { padding: .42rem .35rem; }
    .stat-chip .n { font-size: 1.02rem; }
    .stat-chip .t { font-size: .58rem; letter-spacing: .06em; }
}

.stat-chip {
    padding: .65rem .5rem;
    text-align: center;
    background: var(--bg-raised);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    /* Sin esto una cifra larga de puntos ensancha su columna. */
    min-width: 0;
}

.stat-chip .n {
    display: block;
    font-size: 1.2rem;
    font-weight: 900;
    line-height: 1.1;
    overflow-wrap: anywhere;
}

.stat-chip .t {
    display: block;
    font-size: .64rem;
    font-weight: 700;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--text-dim);
    /* La etiqueta no puede salirse de su recuadro cuando la casilla queda
       estrecha: antes "MEDALLAS" se desbordaba y se leia "MEDALLA". Que se
       recorte con puntos suspensivos avisa de que falta texto; salirse, no. */
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* --------------------------------------------------------------------
   Botones
   -------------------------------------------------------------------- */

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: .45rem;
    padding: .62rem 1.05rem;
    border: 1px solid transparent;
    border-radius: var(--r-sm);
    background: var(--bg-raised);
    color: var(--text);
    font: inherit;
    font-size: .875rem;
    font-weight: 700;
    line-height: 1.2;
    cursor: pointer;
    white-space: nowrap;
    transition: all .18s var(--ease);
}

.btn svg { width: 16px; height: 16px; flex-shrink: 0; }

.btn:hover { background: var(--bg-hover); }
/* El hundido de :active se define junto al resto en "Respuesta al toque". */

.btn:disabled,
.btn[aria-disabled="true"] {
    opacity: .5;
    cursor: not-allowed;
    transform: none;
}

.btn-primary {
    background: linear-gradient(135deg, var(--red-bright), var(--red-deep));
    color: #fff;
    box-shadow: var(--shadow-red);
}
.btn-primary:hover {
    background: linear-gradient(135deg, #FF4453, var(--red));
    box-shadow: 0 8px 30px rgba(225, 29, 42, 0.42);
}

.btn-outline {
    background: transparent;
    border-color: var(--line-bright);
    color: var(--text-soft);
}
.btn-outline:hover {
    border-color: var(--red);
    color: var(--red-bright);
    background: var(--red-faint);
}

.btn-ghost {
    background: transparent;
    color: var(--text-soft);
}
.btn-ghost:hover { background: var(--bg-raised); color: var(--text); }

.btn-danger {
    background: transparent;
    border-color: rgba(255, 71, 87, 0.4);
    color: var(--danger);
}
.btn-danger:hover { background: rgba(255, 71, 87, 0.12); }

.btn-ok {
    background: transparent;
    border-color: rgba(46, 204, 113, 0.4);
    color: var(--ok);
}
.btn-ok:hover { background: rgba(46, 204, 113, 0.12); }

.btn-sm { padding: .4rem .7rem; font-size: .78rem; }
.btn-sm svg { width: 14px; height: 14px; }
.btn-lg { padding: .82rem 1.5rem; font-size: .95rem; }
.btn-block { width: 100%; }

.btn-icon {
    width: 34px;
    height: 34px;
    padding: 0;
}

.btn.is-loading {
    color: transparent;
    pointer-events: none;
    position: relative;
}
.btn.is-loading::after {
    content: '';
    position: absolute;
    width: 15px;
    height: 15px;
    border: 2px solid rgba(255,255,255,.3);
    border-top-color: #fff;
    border-radius: 50%;
    animation: spin .7s linear infinite;
}

@keyframes spin { to { transform: rotate(360deg); } }

/* --------------------------------------------------------------------
   Etiquetas y distintivos
   -------------------------------------------------------------------- */

.tag {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    padding: .22rem .55rem;
    border-radius: var(--r-full);
    background: var(--bg-overlay);
    border: 1px solid var(--line);
    font-size: .7rem;
    font-weight: 800;
    letter-spacing: .04em;
    color: var(--text-soft);
    white-space: nowrap;
}

.tag svg { width: 12px; height: 12px; }

.tag-red    { background: var(--red-faint); border-color: var(--red-line); color: var(--red-bright); }
.tag-ok     { background: rgba(46,204,113,.1); border-color: rgba(46,204,113,.3); color: var(--ok); }
.tag-warn   { background: rgba(240,165,0,.1); border-color: rgba(240,165,0,.3); color: var(--warn); }
.tag-danger { background: rgba(255,71,87,.1); border-color: rgba(255,71,87,.3); color: var(--danger); }
.tag-info   { background: rgba(58,155,255,.1); border-color: rgba(58,155,255,.3); color: var(--info); }

/* Distintivo de jerarquia; el color lo pone el lider */
.role-badge {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    padding: .2rem .6rem;
    border-radius: var(--r-full);
    font-size: .68rem;
    font-weight: 800;
    letter-spacing: .06em;
    text-transform: uppercase;
    border: 1px solid currentColor;
    background: color-mix(in srgb, currentColor 12%, transparent);
}

.role-badge svg { width: 12px; height: 12px; }

/* Rareza de medallas.

   Solo la forma del rotulo. El color lo pone la regla .rarity-<slug> que
   el layout genera a partir de la tabla de rarezas, porque un fichero
   estatico no puede saber que colores eligio el lider. */
.rarity {
    font-size: .64rem;
    font-weight: 900;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: var(--r-comun);
}

/* --------------------------------------------------------------------
   Formularios
   -------------------------------------------------------------------- */

.field { margin-bottom: .95rem; }

.field label,
.label {
    display: block;
    margin-bottom: .35rem;
    font-size: .78rem;
    font-weight: 700;
    letter-spacing: .04em;
    color: var(--text-soft);
}

.field .hint {
    margin-top: .3rem;
    font-size: .74rem;
    color: var(--text-dim);
    line-height: 1.4;
}

.input,
input[type="text"],
input[type="password"],
input[type="email"],
input[type="number"],
input[type="date"],
input[type="datetime-local"],
input[type="url"],
input[type="search"],
select,
textarea {
    width: 100%;
    padding: .65rem .8rem;
    background: var(--bg-void);
    border: 1px solid var(--line-bright);
    border-radius: var(--r-sm);
    color: var(--text);
    font: inherit;
    font-size: .9rem;
    transition: border-color .18s var(--ease), box-shadow .18s var(--ease);
}

textarea { resize: vertical; min-height: 96px; line-height: 1.55; }

.input:focus,
input:focus, select:focus, textarea:focus {
    outline: none;
    border-color: var(--red);
    box-shadow: 0 0 0 3px var(--red-faint);
}

input::placeholder, textarea::placeholder { color: var(--text-faint); }

input[type="color"] {
    width: 52px;
    height: 38px;
    padding: 3px;
    cursor: pointer;
}

select {
    appearance: none;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%236E727B' stroke-width='2.5' stroke-linecap='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right .65rem center;
    background-size: 15px;
    padding-right: 2.2rem;
}

input:disabled, select:disabled, textarea:disabled {
    opacity: .55;
    cursor: not-allowed;
}

.field.has-error input,
.field.has-error select,
.field.has-error textarea { border-color: var(--danger); }

.field-error {
    margin-top: .28rem;
    font-size: .76rem;
    color: var(--danger);
}

/* Casillas e interruptores */
.check {
    display: flex;
    align-items: flex-start;
    gap: .55rem;
    padding: .5rem .65rem;
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    background: var(--bg-void);
    cursor: pointer;
    transition: all .18s var(--ease);
}

.check:hover { border-color: var(--line-bright); }

.check input[type="checkbox"],
.check input[type="radio"] {
    width: 17px;
    height: 17px;
    margin: 1px 0 0;
    flex-shrink: 0;
    accent-color: var(--red);
    cursor: pointer;
}

.check span { font-size: .85rem; line-height: 1.4; }
.check .check-desc {
    display: block;
    font-size: .74rem;
    color: var(--text-dim);
    margin-top: .1rem;
}

.check:has(input:checked) {
    border-color: var(--red-line);
    background: var(--red-faint);
}

.field-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: .85rem;
}

fieldset {
    margin: 0 0 1.1rem;
    padding: 1rem;
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    background: var(--bg-void);
}

legend {
    padding: 0 .5rem;
    font-size: .72rem;
    font-weight: 800;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: var(--red-bright);
}

/* Selector de archivo */
input[type="file"] {
    width: 100%;
    padding: .5rem;
    background: var(--bg-void);
    border: 1px dashed var(--line-bright);
    border-radius: var(--r-sm);
    color: var(--text-soft);
    font-size: .82rem;
    cursor: pointer;
}

input[type="file"]::file-selector-button {
    margin-right: .7rem;
    padding: .35rem .75rem;
    border: 0;
    border-radius: var(--r-sm);
    background: var(--bg-hover);
    color: var(--text);
    font: inherit;
    font-size: .8rem;
    font-weight: 700;
    cursor: pointer;
}

input[type="file"]:hover { border-color: var(--red-line); }

/* Barra de filtros */
.filters {
    display: flex;
    gap: .5rem;
    flex-wrap: wrap;
    align-items: flex-end;
    margin-bottom: 1.1rem;
    padding: .85rem;
    background: var(--bg-surface);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
}

.filters .field { margin: 0; flex: 1 1 160px; }
.filters .field label { font-size: .68rem; }

/* --------------------------------------------------------------------
   Avisos
   -------------------------------------------------------------------- */

.alert {
    display: flex;
    align-items: flex-start;
    gap: .65rem;
    padding: .8rem 1rem;
    border: 1px solid var(--line);
    border-left-width: 3px;
    border-radius: var(--r-sm);
    background: var(--bg-surface);
    font-size: .875rem;
    line-height: 1.5;
    margin-bottom: .75rem;
}

.alert svg { width: 18px; height: 18px; flex-shrink: 0; margin-top: 1px; }

.alert-success { border-left-color: var(--ok);     color: #B7F5D0; background: rgba(46,204,113,.07); }
.alert-error   { border-left-color: var(--danger); color: #FFC9CE; background: rgba(255,71,87,.07); }
.alert-warning { border-left-color: var(--warn);   color: #FFE2AC; background: rgba(240,165,0,.07); }
.alert-info    { border-left-color: var(--info);   color: #C4E0FF; background: rgba(58,155,255,.07); }

/* Contenedor de notificaciones emergentes */
.toasts {
    position: fixed;
    top: calc(var(--header-h) + 12px);
    right: 12px;
    left: 12px;
    z-index: 200;
    display: flex;
    flex-direction: column;
    gap: .5rem;
    pointer-events: none;
}

.toast {
    display: flex;
    align-items: flex-start;
    gap: .6rem;
    padding: .75rem .9rem;
    background: var(--bg-overlay);
    border: 1px solid var(--line-bright);
    border-left: 3px solid var(--red);
    border-radius: var(--r-sm);
    box-shadow: var(--shadow-md);
    font-size: .86rem;
    pointer-events: auto;
    animation: toast-in .3s var(--ease);
}

.toast.is-out { animation: toast-out .25s var(--ease) forwards; }
.toast svg { width: 17px; height: 17px; flex-shrink: 0; margin-top: 1px; }
.toast-success { border-left-color: var(--ok); }
.toast-success svg { color: var(--ok); }
.toast-error { border-left-color: var(--danger); }
.toast-error svg { color: var(--danger); }
.toast-info svg { color: var(--info); }

@keyframes toast-in {
    from { opacity: 0; transform: translateY(-12px); }
    to   { opacity: 1; transform: none; }
}
@keyframes toast-out {
    to { opacity: 0; transform: translateX(20px); }
}

/* --------------------------------------------------------------------
   Estado vacio
   -------------------------------------------------------------------- */

.empty {
    padding: 2.5rem 1.25rem;
    text-align: center;
    color: var(--text-dim);
}

.empty svg {
    width: 44px;
    height: 44px;
    margin-bottom: .7rem;
    opacity: .35;
}

.empty h3 {
    color: var(--text-soft);
    font-size: 1rem;
    margin-bottom: .3rem;
}

.empty p { font-size: .86rem; max-width: 380px; margin: 0 auto .85rem; }

/* --------------------------------------------------------------------
   Tablas
   -------------------------------------------------------------------- */

.table-wrap {
    overflow-x: auto;
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    background: var(--bg-surface);
    -webkit-overflow-scrolling: touch;
}

table {
    width: 100%;
    border-collapse: collapse;
    font-size: .875rem;
    min-width: 560px;
}

thead th {
    position: sticky;
    top: 0;
    padding: .7rem .85rem;
    text-align: left;
    background: var(--bg-raised);
    border-bottom: 1px solid var(--line);
    font-size: .68rem;
    font-weight: 800;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--text-dim);
    white-space: nowrap;
}

tbody td {
    padding: .7rem .85rem;
    border-bottom: 1px solid var(--line-soft);
    vertical-align: middle;
}

tbody tr:last-child td { border-bottom: 0; }
tbody tr { transition: background .15s var(--ease); }
tbody tr:hover { background: var(--bg-raised); }

td.actions {
    text-align: right;
    white-space: nowrap;
}

/* --------------------------------------------------------------------
   Paginacion
   -------------------------------------------------------------------- */

.pagination {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: .3rem;
    margin-top: 1.25rem;
    flex-wrap: wrap;
}

.pagination a,
.pagination span {
    min-width: 36px;
    height: 36px;
    padding: 0 .55rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    background: var(--bg-surface);
    color: var(--text-soft);
    font-size: .84rem;
    font-weight: 700;
}

.pagination a:hover { border-color: var(--red-line); color: var(--red-bright); }
.pagination .is-current {
    background: var(--red);
    border-color: var(--red);
    color: #fff;
}
.pagination .is-disabled { opacity: .4; }

/* --------------------------------------------------------------------
   Modales
   -------------------------------------------------------------------- */

.modal-backdrop {
    position: fixed;
    inset: 0;
    z-index: 300;
    display: none;
    align-items: center;
    justify-content: center;
    padding: 1rem;
    background: rgba(0, 0, 0, 0.78);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
}

.modal-backdrop.is-open {
    display: flex;
    animation: fade-in .2s var(--ease);
}

@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }

.modal {
    width: 100%;
    max-width: 520px;
    max-height: 88vh;
    display: flex;
    flex-direction: column;
    background: var(--bg-surface);
    border: 1px solid var(--line-bright);
    border-radius: var(--r-lg);
    box-shadow: var(--shadow-lg);
    animation: modal-in .28s var(--ease);
    overflow: hidden;
}

.modal.wide { max-width: 760px; }

@keyframes modal-in {
    from { opacity: 0; transform: translateY(18px) scale(.97); }
    to   { opacity: 1; transform: none; }
}

.modal-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .75rem;
    padding: 1rem 1.15rem;
    border-bottom: 1px solid var(--line);
}

.modal-head h3 { margin: 0; font-size: 1.02rem; }

.modal-body {
    padding: 1.15rem;
    overflow-y: auto;
    flex: 1;
}

.modal-foot {
    display: flex;
    justify-content: flex-end;
    gap: .5rem;
    padding: .9rem 1.15rem;
    border-top: 1px solid var(--line);
    background: var(--bg-void);
}

.modal-close {
    width: 32px;
    height: 32px;
    display: grid;
    place-items: center;
    border: 0;
    border-radius: var(--r-sm);
    background: transparent;
    color: var(--text-dim);
    cursor: pointer;
    transition: all .18s var(--ease);
}
.modal-close:hover { background: var(--bg-hover); color: var(--text); }
.modal-close svg { width: 18px; height: 18px; }

/* --------------------------------------------------------------------
   Utilidades
   -------------------------------------------------------------------- */

.mt-0 { margin-top: 0; }
.mt-1 { margin-top: .5rem; }
.mt-2 { margin-top: 1rem; }
.mt-3 { margin-top: 1.5rem; }
.mb-0 { margin-bottom: 0; }
.mb-1 { margin-bottom: .5rem; }
.mb-2 { margin-bottom: 1rem; }
.mb-3 { margin-bottom: 1.5rem; }

.hide { display: none !important; }

/* Se oculta en movil y no se declara 'display' en escritorio: asi el
   elemento conserva el suyo propio (.icon-btn es inline-flex y necesita el
   centrado del flex para que el icono no se pegue a la esquina). */
@media (max-width: 899.98px) {
    .only-desktop { display: none !important; }
}

.divider {
    height: 1px;
    background: var(--line);
    margin: 1rem 0;
}

.scroll-x {
    display: flex;
    gap: .6rem;
    overflow-x: auto;
    padding-bottom: .4rem;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
}
.scroll-x > * { scroll-snap-align: start; flex-shrink: 0; }
.scroll-x::-webkit-scrollbar { height: 4px; }

/* Pestanas */
.tabs {
    display: flex;
    gap: .25rem;
    overflow-x: auto;
    border-bottom: 1px solid var(--line);
    margin-bottom: 1.1rem;
    /* La barra de desplazamiento horizontal robaba alto y ensuciaba el filo
       inferior. Se arrastra con el dedo, que es como se usa en el movil. */
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
}

.tabs::-webkit-scrollbar { display: none; }

@media (max-width: 767.98px) {
    .tabs { margin-bottom: .85rem; }
    .tabs a, .tabs button { padding: .5rem .65rem; font-size: .8rem; }
}

.tabs a, .tabs button {
    padding: .6rem .9rem;
    border: 0;
    background: transparent;
    color: var(--text-dim);
    font: inherit;
    font-size: .85rem;
    font-weight: 700;
    white-space: nowrap;
    cursor: pointer;
    border-bottom: 2px solid transparent;
    transition: all .18s var(--ease);
}

.tabs a:hover, .tabs button:hover { color: var(--text); }

.tabs .is-active {
    color: var(--red-bright);
    border-bottom-color: var(--red);
}

/* Pestanas con icono y recuento (catalogo de medallas y logros) */
.tabs [role="tab"] {
    display: inline-flex;
    align-items: center;
    gap: .4rem;
}

.tabs [role="tab"] svg { width: 16px; height: 16px; }

.tabs [role="tab"] .chip-n {
    padding: .05rem .35rem;
    border-radius: var(--r-full);
    background: var(--bg-raised);
    font-size: .68rem;
    font-weight: 800;
    color: var(--text-dim);
}

.tabs [role="tab"].is-active .chip-n {
    background: var(--red-faint);
    color: var(--red-bright);
}

/* --------------------------------------------------------------------
   Respuesta al toque

   Sustituye al recuadro azul del navegador: al pulsar, el elemento se
   hunde un instante. Va al final de la hoja a proposito, para ganar a las
   transformaciones de :hover que declaran antes las tarjetas.
   -------------------------------------------------------------------- */

/* Controles pequenos: el hundido se nota mas porque la pieza es pequena. */
.btn:active,
.icon-btn:active,
.chip:active,
.modal-close:active,
.post-action:active,
.tabs a:active,
.pagination a:active,
.bottom-nav a:active,
.bottom-nav button:active,
.nav-sheet-grid a:active {
    transform: scale(.93);
    /* La ida es inmediata y la vuelta usa la transicion propia del
       elemento: asi el gesto se siente firme, no elastico. */
    transition-duration: .06s;
}

/* Piezas grandes: el mismo gesto, mucho mas contenido. Una tarjeta ancha
   encogiendo un 7% pareceria de goma.

   Solo las que son pulsables ellas mismas (un enlace, una etiqueta o algo
   con role="button"). Las que unicamente contienen un boton se quedan
   fuera: :active alcanza tambien a los ancestros, y si no se filtran, al
   pulsar "Solicitar" se encogerian el boton y su tarjeta a la vez. */
.medal-card:active,
.member-card:active,
.event-card:active,
.claims-link:active,
.pick:active {
    transform: scale(.985);
    transition-duration: .06s;
}

/* Lo que no se puede pulsar no debe responder como si se pudiera. */
.btn:disabled:active,
.btn[aria-disabled="true"]:active,
.medal-card.is-locked:active,
.reward-card.is-out:active,
.pick.is-disabled:active {
    transform: none;
}

/* Al mantener pulsado, el movil intenta seleccionar el texto del control y
   pinta otra capa azul. En un boton ese texto nunca hace falta copiarlo. */
.btn, .icon-btn, .chip, .modal-close, .post-action,
.tabs a, .pagination a, .bottom-nav a, .bottom-nav button,
.nav-sheet-grid a, .medal-card, .pick, .locked-block > summary {
    -webkit-user-select: none;
    user-select: none;
}

/* --------------------------------------------------------------------
   Escritorio
   -------------------------------------------------------------------- */

@media (min-width: 900px) {
    body { font-size: 15px; }

    .top-nav { display: flex; }
    .bottom-nav { display: none; }
    .only-mobile { display: none !important; }

    .header-avatar .who { display: block; }

    .page {
        padding: 1.6rem 1.5rem 3rem;
    }

    .toasts {
        left: auto;
        width: 360px;
    }

    .grid-medals { grid-template-columns: repeat(auto-fill, minmax(158px, 1fr)); }
}

@media (min-width: 1280px) {
    .page { padding-left: 2rem; padding-right: 2rem; }
}

/* Menos movimiento si el sistema lo pide */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: .01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: .01ms !important;
        scroll-behavior: auto !important;
    }
}

/* =====================================================================
   Los cinco gestos

   Capa puramente aditiva: solo movimiento de entrada. No cambia un color,
   ni un borde, ni un radio. Si se borra este bloque entero, la web queda
   exactamente como estaba antes, solo que sin coreografia.

   Todo se anima con transform y opacity. Los dos los resuelve
   la GPU sin recalcular la maqueta, que es la condicion para que esto siga
   siendo fluido en un movil de gama media con cien medallas en pantalla.
   Nada de animar width, top, box-shadow ni filter: blur.
   ===================================================================== */

/* ---------------------------------------------------------------------
   Gesto 1 - Nada aparece: todo llega

   Fundido puro es lo que hace una pagina web. Un revelado con direccion
   y mascara es lo que hace una aplicacion. La mascara sube desde abajo
   mientras la pieza se desplaza, asi que se descubre en vez de encenderse.
   --------------------------------------------------------------------- */

@keyframes m-llega {
    from {
        /* La pieza se posa: llega inclinada hacia atras, como una carta
           que se deja sobre la mesa, y se endereza frenando.

           El giro es sobre el eje horizontal, asi que la anchura no
           cambia en ningun fotograma. Eso no es un detalle: cualquier
           cosa que se salga por los lados dentro de .page ensancha el
           viewport de maqueta y descoloca la barra inferior, que es fija.
           Ya paso una vez con el barrido del filo.

           Antes de esto habia una mascara que bajaba (clip-path) y su
           borde duro recorriendo la tarjeta se leia como un barrido. */
        opacity: 0;
        transform: perspective(900px) translate3d(0, 24px, 0) rotateX(8deg) scale(.97);
    }
    to {
        opacity: 1;
        transform: perspective(900px) translate3d(0, 0, 0) rotateX(0deg) scale(1);
    }
}

/* ---------------------------------------------------------------------
   Gesto 3 - Jerarquia en cascada

   La seccion no llega de golpe: llega por rango. Primero el encabezado,
   con autoridad; despues el contenido, y dentro de una rejilla cada pieza
   45 ms detras de la anterior.

   Se escalona solo lo que cabe en pantalla. Mas alla de la fila doce ya
   nadie esta mirando, y encadenar retardos sin fin significa que la
   ultima tarjeta de una coleccion de mil entraria un minuto tarde.
   --------------------------------------------------------------------- */

.page > * {
    animation: m-llega var(--m-base) var(--m-out) both;
}

.page > .page-head { animation-delay: 0ms; }
.page > * + *      { animation-delay: 60ms; }

/* Dentro de una rejilla o una lista, pieza a pieza. */
.page .grid > *,
.page .mini-list > *,
.page .card-list > * {
    animation: m-llega var(--m-base) var(--m-out) both;
}

.page .grid > :nth-child(1),  .page .mini-list > :nth-child(1)  { animation-delay: calc(var(--m-step) * 1); }
.page .grid > :nth-child(2),  .page .mini-list > :nth-child(2)  { animation-delay: calc(var(--m-step) * 2); }
.page .grid > :nth-child(3),  .page .mini-list > :nth-child(3)  { animation-delay: calc(var(--m-step) * 3); }
.page .grid > :nth-child(4),  .page .mini-list > :nth-child(4)  { animation-delay: calc(var(--m-step) * 4); }
.page .grid > :nth-child(5),  .page .mini-list > :nth-child(5)  { animation-delay: calc(var(--m-step) * 5); }
.page .grid > :nth-child(6),  .page .mini-list > :nth-child(6)  { animation-delay: calc(var(--m-step) * 6); }
.page .grid > :nth-child(7),  .page .mini-list > :nth-child(7)  { animation-delay: calc(var(--m-step) * 7); }
.page .grid > :nth-child(8),  .page .mini-list > :nth-child(8)  { animation-delay: calc(var(--m-step) * 8); }
.page .grid > :nth-child(9),  .page .mini-list > :nth-child(9)  { animation-delay: calc(var(--m-step) * 9); }
.page .grid > :nth-child(10), .page .mini-list > :nth-child(10) { animation-delay: calc(var(--m-step) * 10); }
.page .grid > :nth-child(11), .page .mini-list > :nth-child(11) { animation-delay: calc(var(--m-step) * 11); }
.page .grid > :nth-child(12), .page .mini-list > :nth-child(12) { animation-delay: calc(var(--m-step) * 12); }

/* De la trece en adelante entra sin retardo: ya esta fuera de pantalla,
   y esperar su turno solo la haria llegar tarde al hacer scroll. */
.page .grid > :nth-child(n+13),
.page .mini-list > :nth-child(n+13) {
    animation-delay: calc(var(--m-step) * 12);
}

/* Lo que llega por tandas al bajar no se re-anima: ya se pinto una vez y
   volver a escalonarlo daria un tiron en mitad del scroll. */
.page .grid > .is-appended,
.page .mini-list > .is-appended {
    animation: none;
}

/* ---------------------------------------------------------------------
   Gesto 4 - El filo se enciende

   Cuando un panel termina de llegar, su borde superior se enciende una
   vez y se apaga. Marca el momento en que la pieza se asienta.

   OJO CON LO QUE SE HIZO ANTES AQUI, porque costo caro de encontrar.

   Esto era un destello que recorria el borde: un pseudo-elemento de
   ancho completo que se desplazaba de -100% a +100%. Con fill-mode both
   se quedaba clavado en el estado final, es decir, sobresaliendo un
   ancho de tarjeta por la derecha para siempre. Y .card no recorta.

   Eso ensancha el viewport de maqueta. Como position: fixed se dimensiona
   contra ese viewport y no contra la pantalla, la barra inferior pasaba a
   medir casi el doble de ancho: en el movil solo se veian sus dos
   primeros enlaces, repartidos al 25% y al 75% de lo visible, y volvia a
   su sitio al reevaluarse el viewport. Parecia que la barra se rompia al
   hacer scroll, y el problema estaba aqui.

   La leccion: nada dentro de .page puede desplazarse fuera de sus limites
   en horizontal. Ahora el filo no se mueve, solo cambia de opacidad, y
   ademas se recorta contra la tarjeta.
   --------------------------------------------------------------------- */

@keyframes m-filo {
    0%   { opacity: 0; }
    45%  { opacity: 1; }
    100% { opacity: 0; }
}

.page > .card,
.page > section.card {
    position: relative;
}

.page > .card::after {
    content: '';
    position: absolute;
    /* inset en vez de width: no puede sobresalir aunque se toque luego. */
    top: 0;
    left: 0;
    right: 0;
    height: 1px;
    pointer-events: none;
    background: linear-gradient(90deg, transparent, var(--red-bright), transparent);
    animation: m-filo var(--m-slow) var(--m-out) both;
    animation-delay: var(--m-base);
}

/* ---------------------------------------------------------------------
   Gesto 2 - Anticipacion

   Lo pulsable se comprime un instante antes de responder. Son 140 ms y
   no se percibe conscientemente; lo que se percibe es que la interfaz
   tiene peso en vez de conmutar entre dos estados.
   --------------------------------------------------------------------- */

.btn,
.chip,
.icon-btn,
.medal-card {
    transition: transform var(--m-fast) var(--m-back),
                background-color var(--m-fast) var(--m-out),
                border-color var(--m-fast) var(--m-out),
                color var(--m-fast) var(--m-out);
}

.btn:active,
.chip:active,
.icon-btn:active { transform: scale(.96); }

.medal-card:active { transform: scale(.98); }

/* ---------------------------------------------------------------------
   Gesto 5 - Los numeros se ganan

   Los contadores suben rodando al entrar. El JavaScript escribe el valor;
   aqui solo se reserva el ancho con cifras de paso fijo, porque si cada
   digito mide distinto el numero baila mientras sube y eso es justo el
   salto de maqueta que estamos evitando.
   --------------------------------------------------------------------- */

[data-count-to] {
    font-variant-numeric: tabular-nums;
    font-feature-settings: 'tnum';
}

/* ---------------------------------------------------------------------
   Quien pide menos movimiento, recibe menos movimiento.

   No es un detalle de accesibilidad opcional: para una parte real de la
   gente el movimiento provoca mareo. Se conserva el fundido, que informa
   sin desplazar nada.
   --------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
    .page > *,
    .page .grid > *,
    .page .mini-list > *,
    .page .card-list > * {
        animation: none;
        opacity: 1;
        transform: none;
        clip-path: none;
    }

    .page > .card::after { animation: none; opacity: 0; }

    .btn, .chip, .icon-btn, .medal-card { transition: none; }
    .btn:active, .chip:active, .icon-btn:active, .medal-card:active { transform: none; }
}

/* ---------------------------------------------------------------------
   Red de seguridad

   Los gestos de entrada arrancan desde opacity: 0. Eso significa que el
   contenido esta oculto hasta que la animacion lo descubre, y si por lo
   que sea la animacion no llega a correr, la seccion se queda en blanco
   para siempre. Un adorno nunca puede dejar la web inservible.

   El JavaScript marca la pagina pasado el tiempo de la coreografia. En
   uso normal esto no se nota: para entonces todo termino hace rato.
   --------------------------------------------------------------------- */

.page.m-revelada > *,
.page.m-revelada .grid > *,
.page.m-revelada .mini-list > *,
.page.m-revelada .card-list > *,
/* La coreografia del perfil tambien parte de opacity: 0, asi que tiene
   que entrar en el seguro. Si no, un fallo la dejaria invisible justo
   igual, y ese es el error que este bloque existe para impedir. */
.page.m-revelada .player-card-inner > *,
.page.m-revelada .player-stat,
.page.m-revelada .rank-strip,
.page.m-revelada .showcase-medals > *,
.page.m-revelada .filter-chips > *,
.page.m-revelada [data-feed] > * {
    animation: none;
    opacity: 1;
    transform: none;
    clip-path: none;
}

.page.m-revelada > .card::after { animation: none; opacity: 0; }

/* ---------------------------------------------------------------------
   Entrada del perfil

   La tarjeta de jugador es un unico hijo de .page, asi que con la regla
   general entraba entera de golpe: identidad, cinco estadisticas y
   rangos aparecian en el mismo fotograma. Se lee como un pantallazo, no
   como una presentacion.

   Aqui se abre por dentro y por rangos, como la ficha de un jugador al
   entrar a una partida:

      0 ms   la tarjeta llega (regla general)
    120 ms   la identidad: avatar, nombre y jerarquia
    200 ms   las estadisticas, una detras de otra cada 55 ms
    440 ms   la tira de rangos
    500 ms   la vitrina, y sus piezas escalonadas

   Solo transform vertical y opacity: nada puede salirse por los lados.
   --------------------------------------------------------------------- */

.player-card .player-card-inner > *,
.player-card .player-stat,
.player-card .rank-strip,
.showcase .showcase-medals > * {
    animation: m-perfil 320ms var(--m-out) both;
}

@keyframes m-perfil {
    from { opacity: 0; transform: translate3d(0, 14px, 0); }
    to   { opacity: 1; transform: none; }
}

.player-card .player-card-inner > * { animation-delay: 120ms; }

/* Las estadisticas se ganan una a una: es el gesto de las pantallas de
   recompensa, y aqui refuerza que cada numero cuesta conseguirlo. */
.player-card .player-stat:nth-child(1) { animation-delay: 200ms; }
.player-card .player-stat:nth-child(2) { animation-delay: 255ms; }
.player-card .player-stat:nth-child(3) { animation-delay: 310ms; }
.player-card .player-stat:nth-child(4) { animation-delay: 365ms; }
.player-card .player-stat:nth-child(5) { animation-delay: 420ms; }

.player-card .rank-strip { animation-delay: 440ms; }

/* La vitrina cierra la presentacion, pieza a pieza. */
.showcase .showcase-medals > :nth-child(1) { animation-delay: 500ms; }
.showcase .showcase-medals > :nth-child(2) { animation-delay: 555ms; }
.showcase .showcase-medals > :nth-child(3) { animation-delay: 610ms; }
.showcase .showcase-medals > :nth-child(4) { animation-delay: 665ms; }
.showcase .showcase-medals > :nth-child(5) { animation-delay: 720ms; }
.showcase .showcase-medals > :nth-child(n+6) { animation-delay: 775ms; }

@media (prefers-reduced-motion: reduce) {
    .player-card .player-card-inner > *,
    .player-card .player-stat,
    .player-card .rank-strip,
    .showcase .showcase-medals > * {
        animation: none;
        opacity: 1;
        transform: none;
    }
}

/* ---------------------------------------------------------------------
   Entrada de las secciones con listas

   El perfil ya se abre por rangos. Aqui va lo mismo para las tres
   pantallas donde vive el uso diario: el muro, el catalogo y los
   filtros que los acompanan.

   Se apoya en el mismo fotograma (m-perfil) a proposito. Un gesto
   repetido en todas partes se lee como sistema; uno distinto en cada
   sitio se lee como parches sueltos.
   --------------------------------------------------------------------- */

/* Los chips de filtro entran en fila, deprisa: son el mando de la
   pantalla y tienen que estar disponibles cuanto antes. */
.page .filter-chips > * {
    animation: m-perfil 240ms var(--m-out) both;
}

.page .filter-chips > :nth-child(1) { animation-delay: 90ms; }
.page .filter-chips > :nth-child(2) { animation-delay: 118ms; }
.page .filter-chips > :nth-child(3) { animation-delay: 146ms; }
.page .filter-chips > :nth-child(4) { animation-delay: 174ms; }
.page .filter-chips > :nth-child(5) { animation-delay: 202ms; }
.page .filter-chips > :nth-child(n+6) { animation-delay: 230ms; }

/* El muro. Las publicaciones llevan imagen grande, asi que entran algo
   mas despacio y mas separadas: con 45 ms se pisaban unas a otras. */
.page [data-feed] > * {
    animation: m-perfil 340ms var(--m-out) both;
}

.page [data-feed] > :nth-child(1) { animation-delay: 140ms; }
.page [data-feed] > :nth-child(2) { animation-delay: 215ms; }
.page [data-feed] > :nth-child(3) { animation-delay: 290ms; }
.page [data-feed] > :nth-child(4) { animation-delay: 365ms; }
.page [data-feed] > :nth-child(n+5) { animation-delay: 440ms; }

/* Lo que llega al bajar no se re-anima: la coreografia es para cuando la
   seccion llega, no para cada tanda. Sin esto, seguir bajando daria un
   tiron a mitad de scroll. */
.page [data-feed] > .is-appended,
.page .filter-chips > .is-appended {
    animation: none;
}

@media (prefers-reduced-motion: reduce) {
    .page .filter-chips > *,
    .page [data-feed] > * {
        animation: none;
        opacity: 1;
        transform: none;
    }
}
