/* =====================================================================
   CAEDES FEROX - Componentes especificos
   Tarjeta de jugador, medallas, publicaciones, comentarios y eventos.
   ===================================================================== */

/* --------------------------------------------------------------------
   Tarjeta de perfil gaming
   -------------------------------------------------------------------- */

.player-card {
    position: relative;
    border-radius: var(--r-xl);
    border: 1px solid var(--line-bright);
    background:
        linear-gradient(155deg, rgba(225, 29, 42, 0.14), transparent 46%),
        linear-gradient(180deg, var(--bg-raised), var(--bg-surface));
    overflow: hidden;
    box-shadow: var(--shadow-md);
}

/* Franja diagonal roja del encabezado */
.player-card::before {
    content: '';
    position: absolute;
    top: 0;
    right: 0;
    width: 62%;
    height: 150px;
    background: linear-gradient(115deg, transparent 42%, rgba(225, 29, 42, 0.18) 100%);
    clip-path: polygon(28% 0, 100% 0, 100% 100%, 0 100%);
    pointer-events: none;
}

/* Trama de rejilla sutil */
.player-card::after {
    content: '';
    position: absolute;
    inset: 0;
    pointer-events: none;
    opacity: .5;
    background-image:
        linear-gradient(rgba(255,255,255,.016) 1px, transparent 1px),
        linear-gradient(90deg, rgba(255,255,255,.016) 1px, transparent 1px);
    background-size: 26px 26px;
}

.player-card-inner {
    position: relative;
    z-index: 1;
    padding: 1.4rem 1.2rem 1.2rem;
}

.player-top {
    display: flex;
    align-items: center;
    gap: 1rem;
    margin-bottom: 1.1rem;
}

.player-avatar-wrap {
    position: relative;
    flex-shrink: 0;
}

.player-avatar-wrap .avatar {
    border: 3px solid var(--bg-base);
    box-shadow: 0 0 0 2px var(--red), 0 0 26px var(--red-glow);
}

.player-level {
    position: absolute;
    right: -4px;
    bottom: -4px;
    min-width: 30px;
    height: 26px;
    padding: 0 .35rem;
    display: grid;
    place-items: center;
    border-radius: var(--r-full);
    background: linear-gradient(135deg, var(--red-bright), var(--red-deep));
    color: #fff;
    font-size: .72rem;
    font-weight: 900;
    border: 2px solid var(--bg-base);
    box-shadow: var(--shadow-sm);
}

.player-id { min-width: 0; flex: 1; }

.player-name {
    margin: 0 0 .28rem;
    font-size: clamp(1.15rem, 4.5vw, 1.55rem);
    font-weight: 900;
    letter-spacing: -0.02em;
    line-height: 1.15;
    word-break: break-word;
}

.player-meta {
    display: flex;
    align-items: center;
    gap: .4rem;
    flex-wrap: wrap;
    margin-bottom: .3rem;
}

.player-since {
    font-size: .78rem;
    color: var(--text-dim);
}

.player-signature {
    margin-top: .5rem;
    padding: .5rem .7rem;
    border-left: 2px solid var(--red-line);
    background: var(--bg-void);
    border-radius: 0 var(--r-sm) var(--r-sm) 0;
    font-size: .82rem;
    font-style: italic;
    color: var(--text-soft);
}

/* Barra de estadisticas de la tarjeta */
.player-stats {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(76px, 1fr));
    gap: .45rem;
    padding-top: 1rem;
    border-top: 1px solid var(--line);
}

.player-stat {
    text-align: center;
    padding: .55rem .3rem;
    border-radius: var(--r-md);
    background: rgba(0, 0, 0, 0.32);
    border: 1px solid var(--line-soft);
    transition: border-color .2s var(--ease), transform .2s var(--ease);
}

.player-stat:hover {
    border-color: var(--red-line);
    transform: translateY(-2px);
}

.player-stat .icon {
    font-size: 1rem;
    line-height: 1;
    margin-bottom: .15rem;
}

.player-stat .n {
    display: block;
    font-size: 1.12rem;
    font-weight: 900;
    line-height: 1.15;
}

.player-stat .t {
    font-size: .6rem;
    font-weight: 800;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--text-dim);
}

/* --------------------------------------------------------------------
   Medallas
   -------------------------------------------------------------------- */

.medal-card {
    position: relative;
    padding: .9rem .7rem;
    text-align: center;
    background: var(--bg-surface);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    cursor: pointer;
    overflow: hidden;
    transition: transform .22s var(--ease), border-color .22s var(--ease), box-shadow .22s var(--ease);

    /* La coleccion completa son cientos de tarjetas y cada una enciende su
       halo de rareza. Fuera de pantalla no hay nada que mirar, asi que se
       deja de pintar hasta que asoma. El alto es parejo en toda la rejilla,
       de ahi que la estimacion sea fiable. */
    content-visibility: auto;
    contain-intrinsic-size: auto 168px;
}

.medal-card:hover {
    transform: translateY(-4px);
    border-color: var(--red-line);
    box-shadow: var(--shadow-md);
}

/* Halo segun rareza.

   El resplandor sale de un degradado radial y no de filter: blur() sobre una
   elipse solida. El desenfoque obliga al navegador a pintar la figura aparte
   y pasarle el filtro encima, una vez por tarjeta; el degradado lo resuelve
   de una sola pasada. A la vista es el mismo halo. */
.medal-card::before {
    content: '';
    position: absolute;
    top: -34%;
    left: 50%;
    transform: translateX(-50%);
    width: 120%;
    height: 96px;
    background: radial-gradient(closest-side, var(--halo, transparent), transparent);
    opacity: .75;
    pointer-events: none;
    transition: opacity .22s var(--ease);
}

.medal-card:hover::before { opacity: 1; }

/* El --halo de cada rareza lo inyecta el layout: los colores estan en la
   base de datos y aqui no se pueden conocer. */

.medal-img {
    position: relative;
    width: 72px;
    height: 72px;
    margin: 0 auto .55rem;
    display: grid;
    place-items: center;
    border-radius: var(--r-md);
    background: var(--bg-void);
    border: 1px solid var(--line);
    overflow: hidden;
}

.medal-img img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    padding: 4px;
}

.medal-img .fallback {
    font-size: 1.9rem;
    line-height: 1;
}

.medal-name {
    font-size: .8rem;
    font-weight: 800;
    line-height: 1.25;
    margin-bottom: .18rem;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.medal-date {
    font-size: .68rem;
    color: var(--text-dim);
    margin-top: .2rem;
}

/* Medalla bloqueada: apagada y con candado */
.medal-card.is-locked {
    cursor: default;
    opacity: .42;
    filter: grayscale(1);
}

.medal-card.is-locked:hover {
    transform: none;
    border-color: var(--line);
    box-shadow: none;
}

.medal-card.is-locked::before { opacity: 0; }

.medal-lock {
    position: absolute;
    top: .5rem;
    right: .5rem;
    width: 22px;
    height: 22px;
    display: grid;
    place-items: center;
    border-radius: var(--r-full);
    background: rgba(0, 0, 0, 0.7);
    color: var(--text-soft);
}

.medal-lock svg { width: 12px; height: 12px; }

/* Contador de repeticiones cuando la medalla se otorgo varias veces */
.medal-count {
    position: absolute;
    top: .5rem;
    left: .5rem;
    min-width: 21px;
    height: 21px;
    padding: 0 5px;
    display: grid;
    place-items: center;
    border-radius: var(--r-full);
    background: var(--red);
    color: #fff;
    font-size: .64rem;
    font-weight: 900;
}

/* --------------------------------------------------------------------
   Detalle de medalla: el acta de concesion

   No es una ficha con datos, es el documento que acredita el merito. La
   pieza aparece bajo un foco, sobre su peana y con los rayos girando
   detras; debajo va el acta propiamente dicha, y la cierra el sello.

   Todo lo tine la rareza. El color entra por --rare, que se resuelve
   segun el data-rarity que app deja puesto al abrir: asi una exclusiva se
   abre en rojo y una legendaria en oro sin duplicar una sola regla. El
   valor de cada rareza lo inyecta el layout desde la base de datos.
   -------------------------------------------------------------------- */

#medal-modal { --rare: var(--r-comun); }

/* La cabecera del modal se encabeza con el color de la pieza que hay dentro:
   antes de leer nada ya sabes de que rareza va. */
#medal-modal .modal { border-color: color-mix(in srgb, var(--rare) 32%, var(--line-bright)); }

#medal-modal .modal-head {
    position: relative;
    background: linear-gradient(180deg, color-mix(in srgb, var(--rare) 10%, transparent), transparent);
}

#medal-modal .modal-head::after {
    content: '';
    position: absolute;
    top: 0; left: 0; right: 0;
    height: 2px;
    background: linear-gradient(90deg, transparent, var(--rare), transparent);
}

.medal-detail { text-align: center; }

/* El foco. El alto lo fija la peana + la pieza, y todo lo de dentro se
   posiciona respecto a este recuadro. */
.medal-stage {
    position: relative;
    display: grid;
    place-items: center;
    padding: .6rem 0 1.5rem;
    margin-bottom: .9rem;
}

/* Rayos de luz saliendo por detras de la pieza. Es un degradado conico con
   franjas transparentes; gira con transform, que no obliga a repintar. */
.medal-rays {
    position: absolute;
    top: 50%;
    left: 50%;
    width: 260px;
    height: 260px;
    margin: -130px 0 0 -130px;
    border-radius: 50%;
    background: conic-gradient(
        from 0deg,
        color-mix(in srgb, var(--rare) 26%, transparent) 0deg 8deg,
        transparent 8deg 30deg
    );
    /* Se apagan hacia el borde para que no acaben en un corte circular. */
    mask-image: radial-gradient(closest-side, #000 26%, transparent 78%);
    -webkit-mask-image: radial-gradient(closest-side, #000 26%, transparent 78%);
    opacity: .8;
    animation: medal-rays 24s linear infinite;
    pointer-events: none;
}

@keyframes medal-rays { to { transform: rotate(360deg); } }

@media (prefers-reduced-motion: reduce) {
    .medal-rays { animation: none; }
}

.medal-detail .big-img {
    position: relative;
    z-index: 1;
    width: 148px;
    height: 148px;
    margin: 0;
    display: grid;
    place-items: center;
    border-radius: var(--r-lg);
    /* Resplandor de la rareza dentro del propio recuadro, sin filtros. */
    background:
        radial-gradient(closest-side, color-mix(in srgb, var(--rare) 22%, transparent), transparent 72%),
        var(--bg-void);
    border: 1px solid color-mix(in srgb, var(--rare) 45%, var(--line-bright));
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, .07),
        0 10px 30px -12px color-mix(in srgb, var(--rare) 55%, transparent);
    overflow: hidden;
}

.medal-detail .big-img img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    padding: 8px;
}

.medal-detail .big-img .fallback { font-size: 4rem; }

/* Peana: el filo donde se apoya y la sombra que proyecta encima. */
.medal-plinth {
    position: absolute;
    bottom: .55rem;
    left: 50%;
    width: 190px;
    margin-left: -95px;
    height: 26px;
    background:
        radial-gradient(closest-side, color-mix(in srgb, var(--rare) 34%, transparent), transparent)
            50% 100% / 100% 20px no-repeat,
        linear-gradient(90deg, transparent, color-mix(in srgb, var(--rare) 70%, transparent), transparent)
            50% 100% / 100% 1px no-repeat;
    pointer-events: none;
}

.medal-detail h3 {
    font-size: 1.3rem;
    margin-bottom: .5rem;
    overflow-wrap: anywhere;
}

/* Cinta de rareza: banda con los extremos en punta, como la de un galardon. */
.rarity-ribbon {
    display: inline-block;
    padding: .3rem 1.4rem;
    background: linear-gradient(180deg,
        color-mix(in srgb, var(--rare) 30%, transparent),
        color-mix(in srgb, var(--rare) 14%, transparent));
    border-top: 1px solid color-mix(in srgb, var(--rare) 55%, transparent);
    border-bottom: 1px solid color-mix(in srgb, var(--rare) 55%, transparent);
    clip-path: polygon(0 0, 100% 0, calc(100% - 11px) 50%, 100% 100%, 0 100%, 11px 50%);
}

.rarity-ribbon .rarity { font-size: .68rem; }

/* El acta. Cada dato en su fila, rotulo a la izquierda y valor a la derecha,
   con el filo de la rareza marcando el margen: se lee como un documento. */
.medal-facts {
    margin-top: 1.15rem;
    text-align: left;
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    background: var(--bg-void);
    overflow: hidden;
}

.medal-fact {
    display: flex;
    gap: .75rem;
    padding: .6rem .8rem;
    font-size: .85rem;
    border-bottom: 1px solid var(--line-soft);
}

.medal-fact:last-child { border-bottom: 0; }

.medal-fact dt {
    flex-shrink: 0;
    width: 92px;
    color: var(--text-dim);
    font-size: .68rem;
    font-weight: 800;
    letter-spacing: .08em;
    text-transform: uppercase;
    margin: 0;
    padding-top: .12rem;
}

.medal-fact dd { margin: 0; flex: 1; overflow-wrap: anywhere; }

/* El motivo es el dato que de verdad importa: es el que cuenta que hiciste
   para merecerla. Va destacado y con el filo de la rareza al costado. */
.medal-fact[data-fact="reason"] {
    background: linear-gradient(90deg, color-mix(in srgb, var(--rare) 9%, transparent), transparent 65%);
    border-left: 2px solid var(--rare);
    padding-left: calc(.8rem - 2px);
}

.medal-fact[data-fact="reason"] dd {
    color: var(--text);
    font-weight: 600;
}

/* La fecha de registro, con aire de asiento: cifras monoespaciadas para que
   parezca lo que es, una anotacion. */
.medal-fact[data-fact="date"] dd {
    font-family: ui-monospace, 'Cascadia Mono', 'Consolas', monospace;
    font-size: .8rem;
    color: var(--text-soft);
}

/* El sello cierra el acta, separado por un respiro. */
#medal-modal-seal:not(:empty) { margin-top: .9rem; }

/* --------------------------------------------------------------------
   Logros
   -------------------------------------------------------------------- */

.achievement {
    display: flex;
    align-items: center;
    gap: .75rem;
    padding: .75rem .85rem;
    background: var(--bg-surface);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    transition: border-color .2s var(--ease);

    /* La lista completa de logros es larga y los bloqueados van con
       grayscale, que es un filtro y se paga uno por fila. Fuera de pantalla
       no se pinta ninguno. */
    content-visibility: auto;
    contain-intrinsic-size: auto 74px;
}

.achievement:hover { border-color: var(--red-line); }

.achievement.is-locked { opacity: .45; filter: grayscale(1); }

.achievement-icon {
    width: 42px;
    height: 42px;
    flex-shrink: 0;
    display: grid;
    place-items: center;
    border-radius: var(--r-md);
    background: linear-gradient(135deg, var(--red-faint), transparent);
    border: 1px solid var(--red-line);
    font-size: 1.25rem;
}

.achievement-icon img { width: 100%; height: 100%; object-fit: contain; padding: 3px; }

.achievement-side {
    flex: none;
    display: flex;
    align-items: center;
    gap: .35rem;
}

.ach-done {
    display: grid;
    place-items: center;
    width: 22px;
    height: 22px;
    border-radius: var(--r-full);
    background: rgba(46, 204, 113, .14);
    color: var(--ok);
}

.ach-done svg { width: 13px; height: 13px; }

.achievement-body { min-width: 0; flex: 1; }
.achievement-body h4 { margin: 0 0 .1rem; font-size: .9rem; }
.achievement-body p { margin: 0; font-size: .78rem; color: var(--text-dim); }

/* --------------------------------------------------------------------
   Muro: rejilla de dos columnas
   -------------------------------------------------------------------- */

.feed-layout {
    display: grid;
    gap: 1.25rem;
    /* minmax(0,...) y no 1fr: una columna 1fr no baja de su contenido minimo,
       y la fila de pestanas -que ya se desplaza por dentro- estiraria la
       columna hasta caber entera, llevandose el documento entero a lo ancho.
       El guardado de .feed-layout > * en app.css cubre lo mismo por dentro. */
    grid-template-columns: minmax(0, 1fr);
}

.feed-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .6rem;
    /* En movil el titulo y el boton no caben en una linea: sin permitir el
       salto, el boton sobresale y arrastra la pagina a lo ancho. */
    flex-wrap: wrap;
    margin-bottom: .75rem;
}

.feed-bar h2 {
    display: flex;
    align-items: center;
    gap: .45rem;
    min-width: 0;
    margin: 0;
    font-size: 1.05rem;
}

.feed-bar h2 svg { width: 18px; height: 18px; flex: none; color: var(--red-bright); }

@media (min-width: 1024px) {
    .feed-layout {
        grid-template-columns: minmax(0, 1.7fr) minmax(300px, 1fr);
        align-items: start;
    }

    /* La columna lateral acompana al scroll. Sin esto, en cuanto pasas de
       su alto el lado derecho de la pantalla queda en negro y la pagina se
       ve medio vacia en un monitor grande. */
    .feed-side {
        position: sticky;
        top: calc(var(--header-h) + 1rem);
        max-height: calc(100vh - var(--header-h) - 2rem);
        overflow-y: auto;
        overscroll-behavior: contain;
        scrollbar-width: thin;
        scrollbar-color: var(--line-bright) transparent;
    }

    /* La columna es flex y tiene el alto limitado: sin esto sus tarjetas se
       aplastan para caber en el hueco (el sello, que es el mas bajo, quedaba
       a menos de la mitad de su alto) en lugar de dejar que la columna se
       desplace, que es lo que se pretende. */
    .feed-side > * { flex: none; }

    /* Un pelo de aire abajo para que el borde de la ultima tarjeta no quede
       rozando el corte del area que se desplaza. */
    .feed-side { padding-bottom: .25rem; }

    .feed-side::-webkit-scrollbar { width: 6px; }
    .feed-side::-webkit-scrollbar-thumb {
        border-radius: var(--r-full);
        background: var(--line-bright);
    }
}

/* En movil las tarjetas del lateral se reparten entre las publicaciones
   (feedSideInset en app.js) y la columna se queda vacia: sin esto su hueco
   sigue sumando el gap de la rejilla debajo del muro. No sirve el atributo
   hidden -.stack ya declara display:flex y le gana al del navegador-, de
   ahi la clase. */
.feed-side.is-split { display: none; }

@media (min-width: 1440px) {
    .feed-layout {
        gap: 1.6rem;
        grid-template-columns: minmax(0, 1.55fr) minmax(340px, 1fr);
    }
}

/* --------------------------------------------------------------------
   Publicaciones
   -------------------------------------------------------------------- */

.post {
    position: relative;
    background: var(--bg-surface);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    overflow: hidden;
    transition: border-color .2s var(--ease), transform .2s var(--ease), box-shadow .2s var(--ease);

    /* El muro no deja de crecer al desplazarse y cada publicacion con imagen
       arrastra un fondo desenfocado (.media-fill, blur de 22px), que es de lo
       mas caro que hay para pintar. Sin esto el navegador sigue atendiendo las
       cuarenta que ya han pasado de largo y el movil da tirones. Con
       content-visibility solo trabaja en las que se ven.

       El tamano va con la palabra auto: el navegador recuerda lo que midio la
       ultima vez, asi que al volver hacia arriba la barra de desplazamiento no
       pega saltos. Los 420px son solo la primera suposicion. */
    content-visibility: auto;
    contain-intrinsic-size: auto 420px;
}

.post:hover {
    border-color: var(--line-bright);
    box-shadow: var(--shadow-md);
}

/* Filo rojo que se enciende al pasar por encima: da relieve a la tarjeta
   sin cambiarla de sitio ni empujar lo que hay debajo. */
.post::before {
    content: '';
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    width: 3px;
    background: linear-gradient(180deg, var(--red-bright), var(--red-deep));
    opacity: 0;
    transition: opacity .2s var(--ease);
}

.post:hover::before { opacity: .85; }

.post.is-pinned {
    border-color: var(--red-line);
    background:
        linear-gradient(180deg, var(--red-faint), transparent 120px),
        var(--bg-surface);
}

.post.is-pinned::before { opacity: 1; }

.post-head {
    display: flex;
    align-items: center;
    gap: .65rem;
    padding: .9rem 1.1rem .5rem;
}

.post-author { min-width: 0; flex: 1; }

.post-author .name {
    display: flex;
    align-items: center;
    gap: .35rem;
    font-size: .875rem;
    font-weight: 800;
    line-height: 1.2;
}

.post-author .when {
    display: flex;
    align-items: center;
    gap: .35rem;
    font-size: .74rem;
    color: var(--text-dim);
}

/* Categoria como distintivo: en un muro mixto ayuda a saber de un vistazo
   si lo que viene es un torneo, un resultado o un aviso. Sin borde y con el
   fondo justo para separarse de la hora: se lee de un vistazo pero no compite
   con el titulo, que es lo que tiene que llevar el peso de la tarjeta. */
.post-cat {
    display: inline-flex;
    align-items: center;
    padding: .04rem .32rem;
    border-radius: var(--r-sm);
    background: var(--bg-raised);
    font-size: .6rem;
    font-weight: 800;
    letter-spacing: .05em;
    text-transform: uppercase;
    color: var(--text-dim);
}

.post-body { padding: 0 1.1rem .9rem; }

.post-body h3 {
    font-size: 1.14rem;
    line-height: 1.3;
    margin-bottom: .45rem;
}

.post-body h3 a { color: var(--text); }
.post-body h3 a:hover { color: var(--red-bright); }

.post-body p {
    font-size: .9rem;
    color: var(--text-soft);
    line-height: 1.65;
}

.post-media {
    display: block;
    position: relative;
    /* Proporcion fija en lugar de alto libre: en un muro que carga solo,
       una imagen que decide su alto al descargarse desplaza lo que estabas
       leyendo. Asi el hueco esta reservado desde el principio. */
    aspect-ratio: 16 / 10;
    border-top: 1px solid var(--line-soft);
    border-bottom: 1px solid var(--line-soft);
    background: var(--bg-void);
    overflow: hidden;
}

/* Al ensancharse la columna la imagen ganaria demasiado alto y empujaria el
   texto de la siguiente publicacion fuera de la pantalla. */
@media (min-width: 1024px) {
    .post-media { aspect-ratio: 16 / 9; }
}

.post-media .media-frame { transition: transform .45s var(--ease); }
a.post-media:hover .media-frame { transform: scale(1.02); }

.post-video {
    position: relative;
    padding-top: 56.25%;
    background: #000;
}

.post-video iframe,
.post-video video {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    border: 0;
}

.post-link {
    display: flex;
    align-items: center;
    gap: .5rem;
    margin: 0 1rem .85rem;
    padding: .6rem .75rem;
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    background: var(--bg-void);
    font-size: .82rem;
    color: var(--text-soft);
}
.post-link svg { width: 15px; height: 15px; color: var(--red-bright); flex-shrink: 0; }

.post-foot {
    display: flex;
    align-items: center;
    gap: .35rem;
    padding: .55rem .8rem;
    border-top: 1px solid var(--line-soft);
    background: var(--bg-void);
}

/* Enlace de cierre: se lee como accion, no como un texto mas del pie. */
.post-more {
    color: var(--text-soft);
}

.post-more:hover {
    background: var(--red-faint);
    color: var(--red-bright);
}

.post-more svg { width: 14px; height: 14px; }

.post-action {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    padding: .42rem .7rem;
    border: 0;
    border-radius: var(--r-sm);
    background: transparent;
    color: var(--text-dim);
    font: inherit;
    font-size: .8rem;
    font-weight: 700;
    cursor: pointer;
    transition: background-color .18s var(--ease), color .18s var(--ease);
}

.post-action svg {
    width: 17px;
    height: 17px;
    transition: transform .2s var(--ease);
}

.post-action:hover { background: var(--bg-raised); color: var(--text); }

.post-action.is-liked { color: var(--red-bright); }
.post-action.is-liked svg { fill: currentColor; }
.post-action.just-liked svg { animation: heart-pop .42s var(--ease); }

@keyframes heart-pop {
    0%   { transform: scale(1); }
    35%  { transform: scale(1.35); }
    70%  { transform: scale(.92); }
    100% { transform: scale(1); }
}

/* Ajuste fino para el movil: los margenes internos de escritorio dejan el
   texto en una columna estrecha con mucho aire a los lados. Aqui se aprietan
   para que la publicacion llene la tarjeta. */
@media (max-width: 767.98px) {
    .feed-layout { gap: 1rem; }

    .post { border-radius: var(--r-md); }
    .post-head { padding: .7rem .75rem .4rem; }
    .post-body { padding: 0 .75rem .7rem; }
    .post-body h3 { font-size: 1rem; margin-bottom: .3rem; }
    .post-body p { font-size: .85rem; line-height: 1.55; }
    .post-author .name { font-size: .82rem; }
    .post-author .when { font-size: .7rem; }
    .post-foot { padding: .4rem .45rem; }
    .post-action { padding: .38rem .5rem; font-size: .76rem; }
    .post-action svg { width: 16px; height: 16px; }
    .post-link { margin: 0 .75rem .7rem; }

    /* La barra del muro y su boton, a la escala del resto */
    .feed-bar { margin-bottom: .6rem; }
    .feed-bar h2 { font-size: 1rem; }
    .feed-bar h2 svg { width: 16px; height: 16px; }
}

/* --------------------------------------------------------------------
   Comentarios
   -------------------------------------------------------------------- */

.comments { padding: .85rem 1rem 1rem; border-top: 1px solid var(--line-soft); }

.comment {
    display: flex;
    gap: .6rem;
    padding: .6rem 0;
}

.comment-main { min-width: 0; flex: 1; }

.comment-bubble {
    padding: .55rem .75rem;
    background: var(--bg-raised);
    border: 1px solid var(--line-soft);
    border-radius: var(--r-md);
}

.comment-bubble .who {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: .35rem;
    margin-bottom: .15rem;
    font-size: .8rem;
    font-weight: 800;
    /* Los nicknames de Free Fire son largos: sin esto empujaban la hora
       fuera de la burbuja. */
    min-width: 0;
    overflow-wrap: anywhere;
}

.comment-bubble .text {
    font-size: .875rem;
    line-height: 1.5;
    color: var(--text-soft);
    white-space: pre-wrap;
    word-break: break-word;
}

/* Hora junto al nombre, no en la fila de acciones: alli la fecha larga
   empujaba los botones a una segunda linea. */
.comment-bubble .when {
    margin-left: .1rem;
    font-size: .68rem;
    font-weight: 500;
    color: var(--text-dim);
    white-space: nowrap;
}

.comment-bubble .when::before {
    content: '·';
    margin-right: .25rem;
}

/* Texto largo recortado con "Ver mas". El limite en lineas y no en
   caracteres respeta las palabras y funciona igual en cualquier ancho. */
.comment-bubble .text[data-clamp] {
    display: -webkit-box;
    -webkit-line-clamp: 6;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.comment-bubble .text.is-open {
    -webkit-line-clamp: unset;
    overflow: visible;
}

.see-more {
    margin-top: .25rem;
    padding: 0;
    border: 0;
    background: transparent;
    color: var(--red-bright);
    font: inherit;
    font-size: .74rem;
    font-weight: 800;
    cursor: pointer;
}

.see-more:hover { text-decoration: underline; }

/* --------------------------------------------------------------------
   Acciones del comentario

   Iconos en lugar de palabras: "Me gusta 0 · Responder · Eliminar" no
   cabe en una linea de movil y se partia en dos.
   -------------------------------------------------------------------- */

.comment-tools {
    display: flex;
    align-items: center;
    gap: .15rem;
    margin-top: .25rem;
    padding-left: .2rem;
}

.ctool {
    display: inline-flex;
    align-items: center;
    gap: .28rem;
    padding: .3rem .45rem;
    border: 0;
    border-radius: var(--r-sm);
    background: transparent;
    color: var(--text-dim);
    font: inherit;
    font-size: .74rem;
    font-weight: 800;
    line-height: 1;
    cursor: pointer;
    transition: color .18s var(--ease), background .18s var(--ease);
}

.ctool svg { width: 15px; height: 15px; }

.ctool .count { font-variant-numeric: tabular-nums; }
.ctool .count[hidden] { display: none; }

.ctool:hover { color: var(--text); background: var(--bg-raised); }

.ctool.is-static { cursor: default; }
.ctool.is-static:hover { background: transparent; color: var(--text-dim); }

.ctool.is-liked { color: var(--red-bright); }
.ctool.is-liked svg { fill: currentColor; }

.ctool.is-danger:hover { color: var(--danger); }

/* Late al marcar "me gusta", igual que en las publicaciones. */
.ctool.just-liked svg { animation: heart-pop .42s var(--ease); }

/* Respuestas anidadas, con guia vertical */
.comment-children {
    margin-top: .3rem;
    padding-left: .85rem;
    border-left: 2px solid var(--line);
}

.comment.is-deleted .comment-bubble {
    background: transparent;
    border-style: dashed;
}
.comment.is-deleted .text { font-style: italic; color: var(--text-faint); }

/* Formulario de comentario */
.comment-form {
    display: flex;
    gap: .55rem;
    align-items: flex-start;
    margin-top: .5rem;
}

.comment-form textarea {
    min-height: 42px;
    max-height: 180px;
    padding: .55rem .7rem;
    font-size: .875rem;
    border-radius: var(--r-lg);
    resize: none;
}

.comment-form.is-reply {
    margin-top: .45rem;
    margin-left: .2rem;
}

/* --------------------------------------------------------------------
   Miembros
   -------------------------------------------------------------------- */

.member-card {
    display: flex;
    align-items: center;
    gap: .8rem;
    padding: .85rem;
    background: var(--bg-surface);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    color: var(--text);
    transition: border-color .2s var(--ease), transform .2s var(--ease),
                color .2s var(--ease), box-shadow .2s var(--ease);

    /* La comunidad puede tener cientos de miembros en una sola lista. */
    content-visibility: auto;
    contain-intrinsic-size: auto 84px;
}

.member-card:hover {
    border-color: var(--red-line);
    transform: translateY(-2px);
    color: var(--text);
    box-shadow: var(--shadow-sm);
}

.member-card .info { min-width: 0; flex: 1; }

.member-card .name {
    font-size: .92rem;
    font-weight: 800;
    line-height: 1.25;
    margin-bottom: .2rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.member-card .meta {
    display: flex;
    align-items: center;
    gap: .4rem;
    flex-wrap: wrap;
    font-size: .72rem;
    color: var(--text-dim);
}

.member-card .points {
    flex-shrink: 0;
    text-align: right;
}

.member-card .points strong {
    display: block;
    font-size: 1rem;
    font-weight: 900;
    color: var(--red-bright);
    line-height: 1.1;
}

.member-card .points span {
    font-size: .62rem;
    font-weight: 800;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--text-dim);
}

/* --------------------------------------------------------------------
   Primitivas compartidas

   Estas reglas vivian solo en admin.css, que la zona de miembros no
   carga, asi que las vistas de miembros las usaban sin estilo alguno.
   -------------------------------------------------------------------- */

/* Encabezado de seccion con linea de continuacion.
   Lo usan comunidad, medallas y premios. */
.section-title {
    display: flex;
    align-items: center;
    gap: .5rem;
    margin-bottom: .8rem;
    font-size: .78rem;
    font-weight: 800;
    letter-spacing: .14em;
    text-transform: uppercase;
    color: var(--text-dim);
}

.section-title::after {
    content: '';
    flex: 1;
    height: 1px;
    background: var(--line);
}

/* Rejilla de dos columnas para pantallas anchas. Se mueve entera, con su
   punto de ruptura: dejar aqui la version de una columna y la de dos en
   admin.css era lo que obligaba a member/home.php a repetir la regla en un
   <style> propio. */
.split {
    display: grid;
    gap: 1rem;
    grid-template-columns: 1fr;
}

@media (min-width: 1024px) {
    .split { grid-template-columns: 1.6fr 1fr; }
    .split.even { grid-template-columns: 1fr 1fr; }
}

/* Contador junto a los botones de "me gusta". Cifras de ancho fijo para
   que el boton no se mueva al pasar de 9 a 10. */
.count {
    min-width: 1ch;
    font-weight: 800;
    font-variant-numeric: tabular-nums;
    opacity: .85;
}

/* Solo para lectores de pantalla. */
.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

/* --------------------------------------------------------------------
   Marco de imagen

   Las portadas las sube gente y vienen en cualquier proporcion. Recortarlas
   para llenar el marco (object-fit: cover) se comia medio cartel; dejarlas
   contenidas a secas dejaba dos franjas negras a los lados.

   La solucion: la imagen se ve entera y el hueco lo rellena una copia de
   ella misma, ampliada y desenfocada. Nunca se recorta nada y el marco
   siempre queda lleno. Es la misma URL, asi que el navegador la descarga
   una sola vez.
   -------------------------------------------------------------------- */

.media-fill,
.media-frame {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
}

.media-fill {
    object-fit: cover;
    filter: blur(22px) saturate(1.25);
    transform: scale(1.2);
    opacity: .42;
    pointer-events: none;
}

.media-frame { object-fit: contain; }

/* --------------------------------------------------------------------
   Espera de imagen

   Un JPEG normal se pinta de arriba abajo segun descarga, y se ve la
   franja avanzando. Aqui la imagen se mantiene invisible hasta estar
   entera y en su hueco late un esqueleto; al terminar, aparece fundida.
   La clase la pone y la quita app.js.
   -------------------------------------------------------------------- */

/* El fundido solo lo necesitan las imagenes que pasan por el esqueleto, que
   son las de estos marcos (la lista IMG_FRAMES de app.js). Antes la regla iba
   sobre img a secas y cargaba con una transicion a cada imagen del sitio,
   incluidas las que nunca cambian de opacidad. */
.medal-img img,
.achievement-icon img,
.claim-thumb img,
.avatar img,
.ach-pill img,
.post-media img,
.event-banner img,
.event-hero img,
.reward-img img { transition: opacity .3s var(--ease); }

.img-wait { position: relative; overflow: hidden; }

/* El hueco en reposo. Antes esta capa llevaba encima el degradado en
   movimiento, desplazado con background-position: eso obliga a repintar el
   recuadro entero en cada fotograma, y en el muro hay uno por publicacion
   pendiente. Ahora el color se queda quieto aqui y el brillo viaja aparte. */
.img-wait::after {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 1;
    background: var(--bg-raised);
    pointer-events: none;
}

/* El brillo, en su propia capa y movido con transform, que la tarjeta grafica
   resuelve sola sin volver a pintar nada. */
.img-wait::before {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 2;
    background: linear-gradient(90deg, transparent, var(--line), transparent);
    transform: translateX(-100%);
    animation: sk-sweep 1.4s ease infinite;
    pointer-events: none;
}

/* Gana en especificidad a .media-fill, que declara su propia opacidad. */
.img-wait > img { opacity: 0; }

@media (prefers-reduced-motion: reduce) {
    .img-wait::before { animation: none; opacity: 0; }
}

/* --------------------------------------------------------------------
   Portada del detalle de evento

   Alto explicito, NO aspect-ratio. Al combinar una proporcion con
   max-height, el navegador encoge el ANCHO para respetar la proporcion:
   el cartel salia mas estrecho que el resto de la pagina y, al no tener
   margenes automaticos, pegado a la izquierda. Con alto fijo el ancho se
   queda siempre al 100%.
   -------------------------------------------------------------------- */

.event-hero {
    position: relative;
    display: flex;
    align-items: flex-end;
    height: 230px;
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    background: var(--bg-void);
    overflow: hidden;
}

@media (min-width: 640px)  { .event-hero { height: 320px; } }
@media (min-width: 1024px) { .event-hero { height: 400px; } }

/* Velo inferior: el titulo tiene que leerse sobre cualquier cartel, sea
   claro u oscuro. */
.event-hero::after {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(180deg,
        rgba(0, 0, 0, .35) 0%,
        rgba(0, 0, 0, .05) 30%,
        rgba(0, 0, 0, .55) 68%,
        rgba(0, 0, 0, .92) 100%);
    pointer-events: none;
}

.event-hero-body {
    position: relative;
    z-index: 1;
    width: 100%;
    min-width: 0;
    padding: 1.1rem 1.2rem;
}

.event-hero-body .eyebrow { color: rgba(255, 255, 255, .72); }

.event-hero-body h1 {
    margin: 0;
    font-size: clamp(1.4rem, 3.2vw, 2.1rem);
    text-shadow: 0 2px 14px rgba(0, 0, 0, .7);
    overflow-wrap: anywhere;
}

/* Con portada, la cabecera solo lleva los botones: el titulo vive ya en el
   cartel y repetirlo sobraba. */
.page-head.is-compact {
    justify-content: flex-end;
    margin-bottom: .75rem;
}

@media (max-width: 639.98px) {
    .event-hero-body { padding: .85rem .9rem; }
}

/* Las dos columnas del detalle empiezan arriba en lugar de estirarse a la
   altura de la mas larga: con pocos inscritos, el panel de participantes
   quedaba con medio metro de hueco vacio debajo. */
.event-layout { align-items: start; }

/* Los detalles piden mas sitio que la lista de nombres. */
@media (min-width: 1024px) {
    .event-layout { grid-template-columns: 1.35fr 1fr; }
}

/* --------------------------------------------------------------------
   Progreso de coleccion y filtros
   -------------------------------------------------------------------- */

.collection-progress {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: .75rem;
}

.cp-item {
    padding: .9rem 1rem;
    background: var(--bg-surface);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
}

.cp-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .5rem;
    margin-bottom: .6rem;
}

.cp-label {
    display: flex;
    align-items: center;
    gap: .4rem;
    font-size: .72rem;
    font-weight: 800;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--text-dim);
}

.cp-count { font-size: .85rem; color: var(--text-dim); }
.cp-count strong { font-size: 1.15rem; color: var(--text); }

.cp-bar {
    height: 7px;
    border-radius: var(--r-full);
    background: var(--bg-void);
    overflow: hidden;
}

.cp-bar > span {
    display: block;
    height: 100%;
    border-radius: inherit;
    background: linear-gradient(90deg, var(--red-deep), var(--red-bright));
    transition: width .5s var(--ease);
}

.cp-bar.is-gold > span { background: linear-gradient(90deg, #C57D12, #FFB020); }

.cp-foot { margin-top: .4rem; font-size: .72rem; color: var(--text-dim); }

/* Barra de busqueda y filtros */
/* Recuento del resultado. Con el catalogo cargado por tandas es el unico
   sitio donde el miembro ve cuantas hay de verdad: contar las tarjetas en
   pantalla daria solo las de la tanda. */
.filter-summary {
    display: flex;
    align-items: center;
    gap: .5rem;
    margin: 0 0 .9rem;
    font-size: .8rem;
    color: var(--text-dim);
}

.filter-summary [data-medal-total] {
    font-weight: 800;
    color: var(--text);
}

/* Mientras llega la busqueda, lo viejo se atenua en lugar de desaparecer:
   la pagina no da un salto y se ve que hay algo en marcha. */
[data-medal-catalog].is-loading,
[data-medal-feed].is-loading,
[data-ach-feed].is-loading {
    opacity: .45;
    pointer-events: none;
    transition: opacity .15s var(--ease);
}

.filter-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .6rem;
    margin-bottom: 1.25rem;
    padding: .75rem;
    background: var(--bg-surface);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
}

.filter-search {
    position: relative;
    display: flex;
    align-items: center;
    gap: .45rem;
    flex: 1 1 200px;
    min-width: 0;
    padding: 0 .7rem;
    background: var(--bg-void);
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    color: var(--text-dim);
}

.filter-search:focus-within { border-color: var(--red-line); }
.filter-search svg { flex: none; width: 15px; height: 15px; }

.filter-search input {
    flex: 1;
    min-width: 0;
    height: 36px;
    border: 0;
    background: transparent;
    color: var(--text);
    font: inherit;
    font-size: .85rem;
}

.filter-search input:focus { outline: none; }

.filter-chips {
    display: flex;
    flex-wrap: wrap;
    gap: .3rem;
}

.chip {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    height: 36px;
    padding: 0 .7rem;
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    background: var(--bg-raised);
    color: var(--text-soft);
    font: inherit;
    font-size: .78rem;
    font-weight: 700;
    cursor: pointer;
    transition: border-color .16s var(--ease), background-color .16s var(--ease),
                color .16s var(--ease);
}

.chip:hover { border-color: var(--red-line); color: var(--text); }

.chip.is-active {
    border-color: var(--red);
    background: var(--red-faint);
    color: var(--red-bright);
}

.chip-n {
    padding: 0 .3rem;
    border-radius: var(--r-sm);
    background: var(--bg-void);
    font-size: .68rem;
    color: var(--text-dim);
}

/* Punto de color para identificar la rareza sin leer */
.rarity-dot {
    width: 8px;
    height: 8px;
    border-radius: var(--r-full);
    background: currentColor;
}

/* --------------------------------------------------------------------
   Sello de validacion automatica

   Firma visual de los reconocimientos. Se lee como un asiento emitido por
   la plataforma, no como la firma de una persona.
   -------------------------------------------------------------------- */

.sys-seal {
    position: relative;
    display: flex;
    align-items: center;
    gap: .7rem;
    margin-top: .9rem;
    padding: .7rem .85rem;
    border: 1px solid var(--red-line);
    border-radius: var(--r-md);
    background:
        linear-gradient(135deg, var(--red-faint), transparent 55%),
        var(--bg-void);
    overflow: hidden;
    text-align: left;
}

/* Barrido de luz: sugiere una comprobacion en marcha. */
.sys-seal::after {
    content: '';
    position: absolute;
    top: 0;
    bottom: 0;
    width: 45%;
    pointer-events: none;
    background: linear-gradient(100deg, transparent, rgba(255, 45, 62, 0.10), transparent);
    animation: sys-sweep 3.6s var(--ease) infinite;
}

@keyframes sys-sweep {
    0%        { transform: translateX(-160%); }
    55%, 100% { transform: translateX(320%); }
}

.sys-seal-glyph {
    display: grid;
    place-items: center;
    flex: none;
    width: 34px;
    height: 34px;
    border: 1px solid var(--red-line);
    border-radius: var(--r-sm);
    background: rgba(225, 29, 42, 0.10);
    color: var(--red-bright);
}

.sys-seal-glyph svg { width: 17px; height: 17px; }

.sys-seal-body { min-width: 0; flex: 1; }

.sys-seal-t {
    display: flex;
    align-items: center;
    gap: .35rem;
    font-size: .8rem;
    font-weight: 800;
    letter-spacing: .02em;
    color: var(--text);
}

/* Punto latiendo: el sistema esta vivo. */
.sys-seal-t::before {
    content: '';
    width: 6px;
    height: 6px;
    flex: none;
    border-radius: var(--r-full);
    background: var(--ok);
    box-shadow: 0 0 0 3px rgba(46, 204, 113, 0.16);
    animation: sys-pulse 2.4s ease-in-out infinite;
}

@keyframes sys-pulse {
    0%, 100% { opacity: 1; }
    50%      { opacity: .35; }
}

.sys-seal-s {
    font-size: .7rem;
    color: var(--text-dim);
    line-height: 1.45;
}

.sys-seal-folio {
    flex: none;
    text-align: right;
}

.sys-seal-folio .k {
    display: block;
    font-size: .58rem;
    font-weight: 800;
    letter-spacing: .14em;
    text-transform: uppercase;
    color: var(--text-faint);
}

.sys-seal-folio code,
.sys-mark code {
    font-family: 'Consolas', 'SF Mono', ui-monospace, monospace;
    font-size: .72rem;
    font-weight: 700;
    letter-spacing: .04em;
    color: var(--red-bright);
}

/* Version de una linea, para tarjetas y listados. */
.sys-mark {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    font-size: .68rem;
    font-weight: 700;
    color: var(--text-dim);
}

.sys-mark-dot {
    width: 5px;
    height: 5px;
    flex: none;
    border-radius: var(--r-full);
    background: var(--ok);
    box-shadow: 0 0 0 2px rgba(46, 204, 113, 0.16);
}

@media (max-width: 479.98px) {
    .sys-seal { flex-wrap: wrap; }
    .sys-seal-folio { text-align: left; padding-left: 2.6rem; }
}

@media (prefers-reduced-motion: reduce) {
    .sys-seal::after,
    .sys-seal-t::before { animation: none; }
}

/* --------------------------------------------------------------------
   Muro con carga progresiva
   -------------------------------------------------------------------- */

.feed-sentinel { margin-top: 1rem; }
.feed-sentinel.is-error { display: grid; place-items: center; padding: 1rem 0; }

/* Esqueleto: ocupa el hueco de lo que viene, para que el scroll no pegue
   un tiron cuando llegan las publicaciones. */
.post-skeleton {
    padding: 1.1rem;
    background: var(--bg-surface);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
}

.post-skeleton .sk-head {
    display: flex;
    align-items: center;
    gap: .7rem;
    margin-bottom: .9rem;
}

/* Mismo cambio que en .img-wait: el color se queda quieto y lo unico que se
   mueve es el brillo, con transform. El centinela del muro esta en pantalla
   justo mientras se desplaza, que es el peor momento para estar repintando. */
.sk {
    display: block;
    position: relative;
    overflow: hidden;
    border-radius: var(--r-sm);
    background: var(--bg-raised);
}

.sk::after {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(90deg, transparent, var(--line), transparent);
    transform: translateX(-100%);
    animation: sk-sweep 1.4s ease infinite;
}

.sk-avatar { width: 38px; height: 38px; border-radius: var(--r-full); flex: none; }
.sk-line { height: 11px; margin-bottom: .55rem; }
.sk-line:last-child { margin-bottom: 0; }

@keyframes sk-sweep {
    to { transform: translateX(100%); }
}

@media (prefers-reduced-motion: reduce) {
    .sk::after { animation: none; opacity: 0; }
}

.feed-end {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: .4rem;
    margin-top: 1.1rem;
    padding: .9rem;
    border-top: 1px solid var(--line);
    font-size: .78rem;
    font-weight: 700;
    color: var(--text-dim);
}

.feed-end svg { width: 15px; height: 15px; color: var(--ok); }

/* Esqueleto del listado de eventos, con la misma forma que las tarjetas
   que va a sustituir para que el hueco no cambie de alto al llegar. */
.event-sentinel { margin-top: 1.1rem; }
.event-sentinel.is-error { display: grid; place-items: center; padding: 1rem 0; }

.event-skeleton-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(270px, 1fr));
    gap: 1rem;
}

.event-skeleton {
    background: var(--bg-surface);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    overflow: hidden;
}

.event-skeleton .sk-banner {
    aspect-ratio: 16 / 10;
    border-radius: 0;
}

.event-skeleton .sk-lines { padding: .9rem 1rem 1.1rem; }

/* Rueda de carga para las listas que no usan esqueleto. */
.medal-sentinel {
    display: grid;
    place-items: center;
    padding: 1.25rem 0;
}

.feed-loading {
    width: 22px;
    height: 22px;
    border-radius: var(--r-full);
    border: 2px solid var(--line);
    border-top-color: var(--red-bright);
    animation: spin .7s linear infinite;
}

@keyframes spin { to { transform: rotate(360deg); } }

@media (prefers-reduced-motion: reduce) {
    .feed-loading { animation-duration: 2s; }
}

/* --------------------------------------------------------------------
   Vitrina del perfil
   -------------------------------------------------------------------- */

/* --------------------------------------------------------------------
   Vitrina

   No es una tarjeta mas con medallas dentro: es un mueble expositor. Marco
   de laton, cristal, luz cenital cayendo desde arriba y las piezas apoyadas
   sobre una balda iluminada. La idea es que asomarse al perfil de alguien se
   parezca a asomarse a un escaparate, porque de eso va la vitrina: de que
   apetezca presumir de lo que hay dentro.

   Todo el relieve sale de degradados y sombras interiores. Ni un filter:
   blur() ni una capa de mas: esto vive en el perfil, que en movil se
   desplaza, y lo que se pinta caro se nota.
   -------------------------------------------------------------------- */

.showcase {
    --oro:       #FFB020;
    --oro-linea: rgba(255, 176, 32, .30);
    --oro-tenue: rgba(255, 176, 32, .10);

    position: relative;
    padding: 1.1rem;
    border-radius: var(--r-lg);
    border: 1px solid var(--oro-linea);
    background:
        linear-gradient(160deg, rgba(255, 176, 32, .09), transparent 55%),
        var(--bg-surface);
    /* Relieve del marco: una luz fina por dentro del borde superior y un poso
       oscuro abajo. Es lo que separa un mueble de un rectangulo. */
    box-shadow:
        inset 0 1px 0 rgba(255, 214, 140, .16),
        inset 0 -20px 40px -28px #000;
    overflow: hidden;
}

/* Filo de laton encendido en el canto de arriba */
.showcase::before {
    content: '';
    position: absolute;
    top: 0; left: 0; right: 0;
    height: 1px;
    background: linear-gradient(90deg, transparent, var(--oro), transparent);
    opacity: .75;
    pointer-events: none;
}

.showcase-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: .75rem;
    margin-bottom: .9rem;
}

.showcase-head .eyebrow { color: var(--oro); }
.showcase-head .eyebrow svg { width: 13px; height: 13px; }

.showcase-head-side {
    display: flex;
    align-items: center;
    gap: .6rem;
    flex: none;
}

/* Recuento de piezas, con aire de placa grabada en el marco */
.showcase-tally {
    padding: .22rem .55rem;
    border: 1px solid var(--oro-linea);
    border-radius: var(--r-full);
    background: rgba(255, 176, 32, .07);
    color: var(--oro);
    font-size: .7rem;
    font-weight: 800;
    letter-spacing: .02em;
    white-space: nowrap;
}

.showcase-title {
    margin: 0;
    font-size: 1.05rem;
    overflow-wrap: anywhere;
}

/* El interior del mueble. Fondo mas profundo que la tarjeta que lo contiene
   -por dentro esta mas oscuro que por fuera, como en un expositor de verdad-
   con un cono de luz cayendo desde el techo. */
.showcase-case {
    position: relative;
    padding: 1.15rem .9rem 1rem;
    border-radius: var(--r-md);
    border: 1px solid rgba(255, 176, 32, .14);
    background:
        radial-gradient(125% 75% at 50% -12%, rgba(255, 200, 110, .13), transparent 68%),
        linear-gradient(180deg, #0B0B0F, var(--bg-void));
    box-shadow: inset 0 2px 14px rgba(0, 0, 0, .65);
    overflow: hidden;
}

/* Reflejo del cristal: una banda diagonal que cruza muy despacio. Se mueve
   con transform, asi que no obliga a repintar nada. */
.showcase-case::before {
    content: '';
    position: absolute;
    top: -60%;
    bottom: -60%;
    width: 42%;
    left: -55%;
    /* Por delante de las piezas: el reflejo esta en el cristal, y el cristal
       esta entre ellas y quien mira. Por eso el blanco es tan flojo. */
    z-index: 2;
    background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .05), transparent);
    transform: rotate(14deg);
    animation: vitrina-reflejo 7s var(--ease) infinite;
    pointer-events: none;
}

@keyframes vitrina-reflejo {
    0%, 62%  { transform: translateX(0) rotate(14deg); }
    100%     { transform: translateX(420%) rotate(14deg); }
}

@media (prefers-reduced-motion: reduce) {
    .showcase-case::before { animation: none; opacity: 0; }
}

/* Las piezas destacadas se ven algo mas grandes que en la coleccion: son las
   que el miembro quiere que mires. */
.showcase-medals {
    position: relative;
    z-index: 1;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(118px, 1fr));
    gap: .75rem;
}

/* Dentro del mueble la tarjeta desaparece y queda solo la pieza: el fondo y
   el borde los pone el expositor, no cada medalla. */
.showcase-medals .medal-card {
    padding: .2rem .4rem 1.15rem;
    background: transparent;
    border-color: transparent;
    /* Son cuatro o cinco y estan siempre a la vista: aqui saltarse el pintado
       no ahorra nada y complica la balda. */
    content-visibility: visible;
}

.showcase-medals .medal-card:hover {
    transform: translateY(-3px);
    border-color: transparent;
    box-shadow: none;
}

/* El halo de rareza manda mas aqui: es la pieza iluminada dentro del mueble. */
.showcase-medals .medal-card::before { opacity: .95; }
.showcase-medals .medal-card:hover::before { opacity: 1; }

/* La balda: el filo donde se apoya la pieza y el charco de luz que su rareza
   derrama encima. Van los dos en la misma capa, como dos fondos. */
.showcase-medals .medal-card::after {
    content: '';
    position: absolute;
    left: 8%;
    right: 8%;
    bottom: 0;
    height: 22px;
    background:
        radial-gradient(closest-side, var(--halo, transparent), transparent)
            50% 100% / 76% 15px no-repeat,
        linear-gradient(90deg, transparent, rgba(255, 200, 120, .5), transparent)
            50% 100% / 100% 1px no-repeat;
    pointer-events: none;
}

.showcase-medals .medal-img {
    width: 78px;
    height: 78px;
    background: rgba(0, 0, 0, .45);
    border-color: rgba(255, 176, 32, .18);
}

.showcase-ach {
    display: flex;
    flex-wrap: wrap;
    gap: .4rem;
    margin-top: .9rem;
    padding-top: .9rem;
    border-top: 1px solid var(--line-soft);
}

/* Placa de logro. Reborde de laton, brillo fino arriba y poso oscuro abajo:
   con eso sola ya parece una chapa y no una etiqueta. */
.ach-pill {
    display: inline-flex;
    align-items: center;
    gap: .45rem;
    max-width: 100%;
    padding: .32rem .7rem .32rem .35rem;
    border-radius: var(--r-full);
    border: 1px solid rgba(255, 176, 32, .32);
    background: linear-gradient(180deg, rgba(255, 176, 32, .10), transparent 60%), var(--bg-raised);
    box-shadow:
        inset 0 1px 0 rgba(255, 214, 140, .18),
        0 1px 3px rgba(0, 0, 0, .45);
    font-size: .78rem;
    font-weight: 700;
    overflow-wrap: anywhere;
    transition: border-color .18s var(--ease), transform .18s var(--ease);
}

.ach-pill:hover {
    border-color: rgba(255, 176, 32, .6);
    transform: translateY(-1px);
}

.ach-pill img {
    width: 24px;
    height: 24px;
    border-radius: var(--r-full);
    object-fit: contain;
    background: var(--bg-void);
    border: 1px solid rgba(255, 176, 32, .22);
    flex: none;
}

.ach-pill-icon { font-size: 1rem; line-height: 1; }

.showcase-empty {
    display: flex;
    align-items: center;
    gap: .8rem;
    padding: .9rem;
    border: 1px dashed var(--line-bright);
    border-radius: var(--r-md);
    color: var(--text-dim);
    font-size: .82rem;
}

.showcase-empty > svg { width: 24px; height: 24px; flex: none; color: #FFB020; }
.showcase-empty p { font-size: .78rem; margin-top: .15rem; }

/* Bloque plegable de lo que falta por conseguir */
.locked-block {
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    padding: .7rem .9rem;
    background: var(--bg-surface);
}

.locked-block > summary {
    display: flex;
    align-items: center;
    gap: .45rem;
    cursor: pointer;
    font-size: .82rem;
    font-weight: 800;
    color: var(--text-soft);
    list-style: none;
}

.locked-block > summary::-webkit-details-marker { display: none; }
.locked-block > summary svg { width: 14px; height: 14px; color: var(--text-dim); }
.locked-block[open] > summary { margin-bottom: .3rem; }

/* --------------------------------------------------------------------
   Selector de la vitrina
   -------------------------------------------------------------------- */

/* Pestanas: mando de dos posiciones, no dos enlaces sueltos. En movil se
   pulsan con el pulgar y tienen que ser anchas. */
.picker-tabs {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: .35rem;
    margin-bottom: 1rem;
    padding: .3rem;
    border-radius: var(--r-md);
    background: var(--bg-void);
    border: 1px solid var(--line);
}

.picker-tabs button {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: .45rem;
    min-width: 0;
    padding: .6rem .5rem;
    border: 1px solid transparent;
    border-radius: var(--r-sm);
    background: transparent;
    color: var(--text-dim);
    font: inherit;
    font-size: .85rem;
    font-weight: 800;
    cursor: pointer;
    transition: background-color .18s var(--ease), color .18s var(--ease),
                border-color .18s var(--ease);
}

.picker-tabs button svg { width: 15px; height: 15px; flex: none; }

.picker-tabs button .t {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.picker-tabs button:hover { color: var(--text-soft); }

.picker-tabs button.is-active {
    background: var(--red-faint);
    border-color: var(--red-line);
    color: var(--text);
}

.picker-tabs button.is-active svg { color: var(--red-bright); }

/* Cuantas llevas puestas de las que caben */
.picker-tabs .n {
    flex: none;
    padding: .1rem .4rem;
    border-radius: var(--r-full);
    background: var(--bg-raised);
    border: 1px solid var(--line);
    font-size: .68rem;
    font-variant-numeric: tabular-nums;
}

.picker-tabs button.is-active .n {
    background: rgba(225, 29, 42, .16);
    border-color: var(--red-line);
    color: var(--red-bright);
}

.picker-panel:not(.is-active) { display: none; }

/* Buscador. Va encima de la rejilla: con mil medallas, buscar no puede
   pedir que bajes primero hasta el final del monton. */
.picker-search {
    position: relative;
    display: flex;
    align-items: center;
    gap: .5rem;
    margin-bottom: .9rem;
    padding: 0 .5rem 0 .75rem;
    background: var(--bg-void);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    overflow: hidden;
}

.picker-search:focus-within { border-color: var(--red-line); }

.picker-search-icon {
    display: grid;
    place-items: center;
    flex: none;
    color: var(--text-dim);
}

.picker-search-icon svg { width: 16px; height: 16px; }

.picker-search input {
    flex: 1;
    min-width: 0;
    height: 44px;
    border: 0;
    background: transparent;
    color: var(--text);
    font: inherit;
    font-size: .88rem;
}

.picker-search input::placeholder { color: var(--text-faint); }
.picker-search input:focus { outline: 0; }

/* El aspa nativa del type=search estorba: hay boton propio. */
.picker-search input::-webkit-search-cancel-button { display: none; }

.picker-search-clear {
    display: grid;
    place-items: center;
    flex: none;
    width: 30px;
    height: 30px;
    border: 0;
    border-radius: var(--r-sm);
    background: transparent;
    color: var(--text-dim);
    cursor: pointer;
    transition: background-color .16s var(--ease), color .16s var(--ease);
}

.picker-search-clear:hover { background: var(--bg-raised); color: var(--text); }
.picker-search-clear svg { width: 14px; height: 14px; }

/* Boton de buscar. La busqueda se lanza a mano: escribiendo, cada tanda que
   vuelve tira treinta tarjetas y monta otras treinta, y eso repetido mientras
   tecleas se nota en un telefono. */
.picker-search-go {
    flex: none;
    height: 34px;
    margin-left: .1rem;
    padding: 0 .85rem;
    border: 1px solid var(--line-bright);
    border-radius: var(--r-sm);
    background: var(--bg-raised);
    color: var(--text-soft);
    font: inherit;
    font-size: .8rem;
    font-weight: 800;
    cursor: pointer;
    white-space: nowrap;
    transition: background-color .16s var(--ease), border-color .16s var(--ease),
                color .16s var(--ease);
}

.picker-search-go:hover { border-color: var(--red-line); color: var(--text); }

/* Hay algo escrito que todavia no se ha buscado: el boton se enciende para
   que se vea que falta pulsarlo. Sin esto, con resultados viejos en pantalla
   parece que la busqueda no funciona. */
.picker-search-go.is-pending {
    background: var(--red);
    border-color: var(--red);
    color: #fff;
}

.picker-search-go.is-pending:hover { background: var(--red-bright); }

/* -----------------------------------------------------------------
   Animacion de busqueda

   Mientras el servidor busca pasan tres cosas: la lupa late, una linea
   recorre el borde de abajo y el monton se atenua. Con una coleccion
   grande la respuesta tarda lo suyo, y sin senal parece que no pasa
   nada y se vuelve a escribir encima.
   ----------------------------------------------------------------- */

.picker-search.is-busy .picker-search-icon {
    color: var(--red-bright);
    animation: picker-lupa .9s ease-in-out infinite;
}

@keyframes picker-lupa {
    0%, 100% { transform: scale(1) rotate(0); }
    50%      { transform: scale(1.18) rotate(-14deg); }
}

/* La linea que recorre. Se mueve con transform, asi que no repinta. */
.picker-search::after {
    content: '';
    position: absolute;
    left: 0;
    bottom: 0;
    width: 45%;
    height: 2px;
    background: linear-gradient(90deg, transparent, var(--red-bright), transparent);
    transform: translateX(-100%);
    opacity: 0;
    pointer-events: none;
}

.picker-search.is-busy::after {
    opacity: 1;
    animation: picker-scan 1.05s var(--ease) infinite;
}

@keyframes picker-scan {
    to { transform: translateX(322%); }
}

/* El monton se apaga mientras llega lo nuevo: deja claro que lo que hay en
   pantalla ya no es la respuesta. Hermano general y no :has(), que aqui no
   hace falta y no todos los navegadores en juego lo tienen. */
.picker-search.is-busy ~ .grid {
    opacity: .4;
    pointer-events: none;
}

.picker-panel .grid { transition: opacity .18s var(--ease); }

@media (prefers-reduced-motion: reduce) {
    .picker-search.is-busy .picker-search-icon,
    .picker-search.is-busy::after { animation: none; }
}

/* En pantallas estrechas el boton se aprieta para no comerse el hueco de
   escribir. El texto se queda: un icono solo no dice que hay que pulsarlo. */
@media (max-width: 419.98px) {
    .picker-search { padding-left: .6rem; gap: .35rem; }
    .picker-search-go { padding: 0 .6rem; font-size: .75rem; }
    .picker-search input { font-size: .85rem; }
}

/* Buscando, lo elegido baja detras de los resultados (lo coloca reorder() en
   app.js) y se marca en un tono aparte: es lo que ya llevas puesto, no una
   coincidencia de lo que buscas. Sigue pulsable, asi que estando al tope se
   puede soltar una y poner otra sin vaciar la busqueda. */
.grid.is-searching .pick.is-picked::after {
    /* Neutro a proposito: el mismo rotulo vale para una medalla y para un
       logro, que no comparten genero. */
    content: 'En tu vitrina';
    position: absolute;
    left: 50%;
    bottom: -.15rem;
    transform: translateX(-50%);
    z-index: 3;
    padding: .1rem .4rem;
    border-radius: var(--r-full);
    background: var(--bg-overlay);
    border: 1px solid var(--line-bright);
    color: var(--text-dim);
    font-size: .6rem;
    font-weight: 800;
    letter-spacing: .04em;
    white-space: nowrap;
    pointer-events: none;
}

/* Busqueda sin resultados */
.picker-none {
    display: flex;
    align-items: center;
    gap: .7rem;
    margin-top: .8rem;
    padding: 1rem;
    border: 1px dashed var(--line-bright);
    border-radius: var(--r-md);
    color: var(--text-dim);
    font-size: .85rem;
}

.picker-none svg { width: 20px; height: 20px; flex: none; color: var(--text-faint); }

.pick {
    position: relative;
    display: block;
    cursor: pointer;
    border-radius: var(--r-lg);
    transition: transform .16s var(--ease);
}

/* La casilla real se oculta pero sigue existiendo: asi el teclado y los
   lectores de pantalla la encuentran. */
.pick input {
    position: absolute;
    opacity: 0;
    width: 1px;
    height: 1px;
}

.pick .medal-card,
.pick .achievement { pointer-events: none; }

.pick:hover { transform: translateY(-2px); }

.pick.is-picked .medal-card,
.pick.is-picked .achievement {
    border-color: #FFB020;
    box-shadow: 0 0 0 2px rgba(255, 176, 32, .3);
}

.pick.is-disabled { opacity: .38; cursor: not-allowed; }
.pick.is-disabled:hover { transform: none; }

/* Numero de orden: el 1 es el primero que se vera en el perfil. */
.pick-order {
    position: absolute;
    top: -7px;
    left: -7px;
    z-index: 2;
    display: grid;
    place-items: center;
    width: 24px;
    height: 24px;
    border-radius: var(--r-full);
    background: #FFB020;
    color: #17130A;
    font-size: .74rem;
    font-weight: 900;
    line-height: 1;
    font-variant-numeric: tabular-nums;
    box-shadow: 0 0 0 2px var(--bg-base);
    transform: scale(0);
    transition: transform .18s var(--ease);
}

.pick.is-picked .pick-order { transform: scale(1); }

.pick:focus-within .medal-card,
.pick:focus-within .achievement { border-color: var(--red-bright); }

.showcase-actions {
    position: sticky;
    bottom: calc(var(--bottom-h) + .5rem);
    z-index: 20;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: .6rem;
    /* Aire por encima. Al llegar al final del monton la barra deja de flotar y
       se planta en su sitio del documento, justo detras de la ultima fila de
       tarjetas: sin este margen las dos ultimas quedan pegadas a ella. El
       centinela no lo cubre porque al terminar de cargar se esconde. */
    margin-top: 1.6rem;
    padding: .75rem .9rem;
    border: 1px solid var(--line-bright);
    border-radius: var(--r-lg);
    background: var(--bg-surface);
    box-shadow: var(--shadow-lg);
}

@media (min-width: 900px) {
    .showcase-actions { bottom: 1rem; }
}

/* --------------------------------------------------------------------
   Listas compactas
   -------------------------------------------------------------------- */

.mini-list { display: flex; flex-direction: column; }

.mini-item {
    display: flex;
    align-items: center;
    gap: .6rem;
    padding: .6rem 0;
    border-bottom: 1px solid var(--line-soft);
    font-size: .85rem;
}

.mini-item:last-child { border-bottom: 0; }
.mini-item .body { min-width: 0; flex: 1; }

.mini-item .body .t {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-weight: 600;
}

.mini-item .body .s { font-size: .74rem; color: var(--text-dim); }

/* --------------------------------------------------------------------
   Ranking
   -------------------------------------------------------------------- */

.podium {
    display: grid;
    grid-template-columns: 1fr 1.15fr 1fr;
    gap: .55rem;
    align-items: end;
    margin-bottom: 1.35rem;
}

.podium-slot {
    position: relative;
    padding: 1rem .5rem .85rem;
    text-align: center;
    background: var(--bg-surface);
    border: 1px solid var(--line);
    border-radius: var(--r-lg) var(--r-lg) var(--r-sm) var(--r-sm);
    overflow: hidden;
}

.podium-slot .rank {
    position: absolute;
    top: .45rem;
    left: .45rem;
    width: 24px;
    height: 24px;
    display: grid;
    place-items: center;
    border-radius: var(--r-full);
    font-size: .74rem;
    font-weight: 900;
    background: var(--bg-overlay);
    color: var(--text-soft);
}

.podium-slot .avatar { margin: 0 auto .5rem; }

.podium-slot .pname {
    font-size: .8rem;
    font-weight: 800;
    line-height: 1.2;
    margin-bottom: .2rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.podium-slot .pscore {
    font-size: 1.02rem;
    font-weight: 900;
    color: var(--red-bright);
}

/* Primer puesto destacado */
.podium-slot.gold {
    border-color: rgba(255, 176, 32, .45);
    background: linear-gradient(180deg, rgba(255, 176, 32, .1), var(--bg-surface));
    padding-top: 1.35rem;
}
.podium-slot.gold .rank { background: #FFB020; color: #17130A; }
.podium-slot.gold .avatar { box-shadow: 0 0 0 2px var(--bg-base), 0 0 0 4px #FFB020; }

.podium-slot.silver { border-color: rgba(190, 195, 205, .34); }
.podium-slot.silver .rank { background: #BEC3CD; color: #14151A; }

.podium-slot.bronze { border-color: rgba(205, 127, 50, .34); }
.podium-slot.bronze .rank { background: #CD7F32; color: #1A1109; }

/* --------------------------------------------------------------------
   Ganadores y participantes de un evento
   -------------------------------------------------------------------- */

/* El podio del ranking asume tres puestos. En un evento puede haber uno o
   dos ganadores, y estirarlos a tres columnas queda desangelado. */
.podium.has-1 { grid-template-columns: minmax(0, 260px); justify-content: center; }
.podium.has-2 { grid-template-columns: repeat(2, 1fr); }

.winners-card .podium { margin-bottom: 0; }

/* Peana bajo cada puesto: da la altura escalonada que se espera de un
   podio sin necesidad de imagenes. */
.podium-base {
    display: grid;
    place-items: center;
    margin: .7rem -.5rem -.85rem;
    border-top: 1px solid var(--line-soft);
    color: var(--text-dim);
}

.podium-slot.gold   .podium-base { height: 52px; background: linear-gradient(180deg, rgba(255,176,32,.16), transparent); color: #FFB020; }
.podium-slot.silver .podium-base { height: 38px; background: linear-gradient(180deg, rgba(190,195,205,.12), transparent); }
.podium-slot.bronze .podium-base { height: 30px; background: linear-gradient(180deg, rgba(205,127,50,.12), transparent); }

.podium-base svg { width: 16px; height: 16px; }

.winners-rest { margin-top: 1.1rem; }
.winners-rest .label { margin-bottom: .2rem; }

.winners-rest .mini-item .pos {
    flex: none;
    width: 22px;
    text-align: center;
    font-size: .8rem;
    font-weight: 900;
    color: var(--text-dim);
}

.winners-rest .mini-item > svg { width: 15px; height: 15px; color: #FFB020; }

/* Plantilla de participantes: rejilla de fichas en vez de una lista
   estrecha, que con pocos inscritos se veia vacia. */
.roster {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(168px, 1fr));
    gap: .55rem;
}

.roster-card {
    position: relative;
    display: flex;
    align-items: center;
    gap: .6rem;
    padding: .6rem .7rem;
    background: var(--bg-raised);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    transition: border-color .18s var(--ease), transform .18s var(--ease);
}

.roster-card:hover { border-color: var(--red-line); transform: translateY(-1px); }

.roster-card .info { flex: 1; min-width: 0; }

.roster-name {
    font-size: .82rem;
    font-weight: 800;
    line-height: 1.25;
    overflow-wrap: anywhere;
}

.roster-name a { color: inherit; }
.roster-name a:hover { color: var(--red-bright); }

.roster-role {
    display: flex;
    align-items: center;
    gap: .3rem;
    margin-top: .1rem;
    font-size: .7rem;
    color: var(--text-dim);
}

.roster-you {
    padding: 0 .28rem;
    border-radius: var(--r-sm);
    background: var(--red-faint);
    color: var(--red-bright);
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: .06em;
    font-size: .58rem;
}

.roster-check {
    flex: none;
    display: grid;
    place-items: center;
    width: 20px;
    height: 20px;
    border-radius: var(--r-full);
    background: var(--ok-faint, rgba(46, 204, 113, .14));
    color: var(--ok);
}

.roster-check svg { width: 12px; height: 12px; }

/* Quien gano se distingue sin depender solo del color. */
.roster-card.is-winner {
    border-color: rgba(255, 176, 32, .45);
    background: linear-gradient(180deg, rgba(255, 176, 32, .1), var(--bg-raised));
}

.roster-card.is-winner .avatar {
    box-shadow: 0 0 0 2px var(--bg-base), 0 0 0 3px #FFB020;
}

.roster-card.is-me { box-shadow: inset 3px 0 0 var(--red); }

.roster-flag {
    position: absolute;
    top: -7px;
    right: -7px;
    display: grid;
    place-items: center;
    width: 22px;
    height: 22px;
    border-radius: var(--r-full);
    background: #FFB020;
    color: #17130A;
    border: 2px solid var(--bg-surface);
}

.roster-flag svg { width: 11px; height: 11px; }

/* En pantallas estrechas, dos fichas por fila siguen siendo legibles. */
@media (max-width: 419.98px) {
    .roster { grid-template-columns: repeat(2, 1fr); gap: .45rem; }
    .roster-card { flex-direction: column; align-items: flex-start; gap: .4rem; }
    .roster-card .info { width: 100%; }
    .roster-check { position: absolute; top: .5rem; right: .5rem; }
}

.rank-row {
    display: flex;
    align-items: center;
    gap: .7rem;
    padding: .65rem .85rem;
    border-bottom: 1px solid var(--line-soft);
    transition: background .15s var(--ease);
}

.rank-row:last-child { border-bottom: 0; }
.rank-row:hover { background: var(--bg-raised); }

.rank-row.is-me {
    background: var(--red-faint);
    box-shadow: inset 3px 0 0 var(--red);
}

.rank-row .pos {
    width: 30px;
    flex-shrink: 0;
    text-align: center;
    font-size: .9rem;
    font-weight: 900;
    color: var(--text-dim);
}

.rank-row .who { min-width: 0; flex: 1; }
.rank-row .who .n {
    font-size: .875rem;
    font-weight: 700;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.rank-row .score {
    flex-shrink: 0;
    font-size: .95rem;
    font-weight: 900;
    color: var(--red-bright);
}

/* --------------------------------------------------------------------
   Eventos
   -------------------------------------------------------------------- */

/* auto-fill, no auto-fit: con uno o dos eventos, auto-fit colapsa las
   columnas sobrantes y la tarjeta se estira de lado a lado. */
.event-grid { grid-template-columns: repeat(auto-fill, minmax(270px, 1fr)); }

.event-card {
    /* Columna flexible para que, en una misma fila, todas las tarjetas
       midan igual y sus datos queden alineados abajo. */
    display: flex;
    flex-direction: column;
    background: var(--bg-surface);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    overflow: hidden;
    color: var(--text);
    /* Solo lo que cambia de verdad: 'all' obliga al navegador a vigilar
       cada propiedad de la tarjeta, y aqui hay muchas en pantalla. */
    transition: border-color .2s var(--ease), transform .2s var(--ease), box-shadow .2s var(--ease);

    /* Las tarjetas que quedan fuera de la pantalla no se maquetan ni se
       pintan hasta que hacen falta; el tamano declarado evita que la barra
       de desplazamiento salte al entrar cada una. Es lo que quita el tiron
       al desplazarse por un listado largo. */
    content-visibility: auto;
    contain-intrinsic-size: auto 340px;
}

.event-card:hover {
    border-color: var(--red-line);
    transform: translateY(-3px);
    color: var(--text);
    box-shadow: var(--shadow-md);
}

.event-banner {
    position: relative;
    /* Proporcion fija en lugar de alto fijo: la portada se adapta al ancho
       de la tarjeta y en pantallas grandes deja de verse achatada. */
    aspect-ratio: 16 / 10;
    background: linear-gradient(135deg, var(--red-deep), #1A0508);
    display: grid;
    place-items: center;
    overflow: hidden;
}

/* La portada llena su hueco por completo, recortando lo que sobra por el
   lado mas largo. Asi todas las tarjetas de una fila se ven iguales sea
   cual sea la proporcion de la imagen que se haya subido. */
.event-cover {
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center;
}

.event-banner .placeholder {
    font-size: 2.4rem;
    opacity: .35;
}

/* Velo sobre la portada: las etiquetas van encima de una foto cualquiera,
   que puede ser clara, oscura o con mucho detalle. Sin este degradado no
   hay tamano de letra que se lea siempre. */
.event-banner::after {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 1;
    background: linear-gradient(180deg,
        rgba(0, 0, 0, .5) 0%,
        rgba(0, 0, 0, .05) 34%,
        rgba(0, 0, 0, .1) 56%,
        rgba(0, 0, 0, .78) 100%);
    pointer-events: none;
}

/* La portada se acerca un poco al pasar por encima. */
.event-cover { transition: transform .45s var(--ease), opacity .3s var(--ease); }

.event-card:hover .event-cover { transform: scale(1.05); }

/* En pantalla tactil no hay "pasar por encima": el acercamiento solo se
   declara donde de verdad puede ocurrir, para no dejar la tarjeta con una
   capa preparada para animarse que nunca se usa. */
@media (hover: none) {
    .event-cover { transition: none; }
    .event-card:hover { transform: none; box-shadow: none; }
}

/* --------------------------------------------------------------------
   Estado del evento sobre la portada

   Las variantes .tag-* pintan el fondo al 10% de opacidad: sobre la
   tarjeta oscura se leen, pero sobre una foto no. Aqui la pastilla es
   opaca y el color pasa al punto, que se distingue contra cualquier
   fondo y no depende de leer texto de color.
   -------------------------------------------------------------------- */

.event-banner .status-pill,
.event-banner .event-date {
    position: absolute;
    z-index: 2;
}

.event-banner .status-pill { top: .6rem; left: .6rem; }

.tag.status-pill {
    --dot: var(--text-dim);
    padding: .28rem .6rem;
    /* Fondo casi opaco en lugar de un desenfoque del fondo. backdrop-filter
       obliga al navegador a releer lo que hay detras y volver a desenfocarlo
       mientras la pastilla se mueve; con una pastilla por tarjeta, en un
       listado eso se paga en cada fotograma del desplazamiento. Subiendo la
       opacidad se lee igual de bien sobre cualquier portada. */
    background: rgba(8, 8, 10, .92);
    border-color: rgba(255, 255, 255, .18);
    color: #fff;
    box-shadow: 0 2px 12px rgba(0, 0, 0, .5);
}

.tag.status-pill::before {
    content: '';
    flex: none;
    width: 7px;
    height: 7px;
    border-radius: var(--r-full);
    background: var(--dot);
    box-shadow: 0 0 8px var(--dot);
}

.status-pill.tag-ok     { --dot: var(--ok); }
.status-pill.tag-red    { --dot: var(--red-bright); }
.status-pill.tag-warn   { --dot: var(--warn); }
.status-pill.tag-danger { --dot: var(--danger); }

/* Un evento en curso late: se distingue de un vistazo entre varios. */
.status-pill.tag-red::before { animation: live-pulse 1.8s ease-in-out infinite; }

@keyframes live-pulse {
    0%, 100% { opacity: 1; transform: scale(1); }
    50%      { opacity: .45; transform: scale(.82); }
}

@media (prefers-reduced-motion: reduce) {
    .status-pill.tag-red::before { animation: none; }
}

.event-date {
    right: .6rem;
    bottom: .6rem;
    padding: .3rem .6rem;
    border-radius: var(--r-full);
    /* Igual que la pastilla de estado: opacidad en lugar de desenfoque del
       fondo, que es la segunda capa cara que llevaba cada tarjeta. */
    background: rgba(8, 8, 10, .92);
    border: 1px solid rgba(255, 255, 255, .18);
    color: #fff;
    font-size: .71rem;
    font-weight: 800;
    box-shadow: 0 2px 12px rgba(0, 0, 0, .5);
}

.event-body {
    display: flex;
    flex-direction: column;
    flex: 1;
    padding: .85rem 1rem 1rem;
}

/* Titulo y resumen recortados a dos lineas: un titulo largo no puede
   descolocar la altura de toda la fila. */
.event-body h3 {
    font-size: .98rem;
    margin-bottom: .3rem;
    line-height: 1.3;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.event-body > p {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

/* El premio, destacado: es lo que decide si te apuntas. */
.event-prize {
    display: flex;
    align-items: center;
    gap: .4rem;
    margin-top: .6rem;
    padding: .4rem .6rem;
    border-radius: var(--r-sm);
    border-left: 2px solid #FFB020;
    background: rgba(255, 176, 32, .09);
    font-size: .78rem;
    font-weight: 700;
    color: var(--text-soft);
}

.event-prize svg { flex: none; width: 14px; height: 14px; color: #FFB020; }

.event-prize span {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Los datos del pie quedan pegados abajo, alineados entre tarjetas. */
.event-card .event-meta { margin-top: auto; }

.event-meta span { display: inline-flex; align-items: center; gap: .3rem; }

/* El tipo de evento como pastilla discreta, sin icono que lo confunda con
   la fecha. */
.event-type {
    padding: .1rem .45rem;
    border-radius: var(--r-sm);
    background: var(--bg-raised);
    border: 1px solid var(--line);
    font-size: .68rem;
    font-weight: 800;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--text-dim);
}

.event-meta {
    display: flex;
    align-items: center;
    gap: .75rem;
    flex-wrap: wrap;
    margin-top: .6rem;
    padding-top: .6rem;
    border-top: 1px solid var(--line-soft);
    font-size: .76rem;
    color: var(--text-dim);
}

.event-meta span {
    display: inline-flex;
    align-items: center;
    gap: .28rem;
}

.event-meta svg { width: 13px; height: 13px; }

/* Barra de cupos */
/* Los cupos con su cifra encima: una barra suelta no dice cuanta gente
   falta, y el numero estaba abajo del todo, lejos de la barra. */
.capacity { margin-top: .7rem; }

.capacity-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: .5rem;
    margin-bottom: .3rem;
    font-size: .7rem;
    font-weight: 800;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--text-dim);
}

.capacity-head strong {
    font-size: .8rem;
    letter-spacing: 0;
    text-transform: none;
    color: var(--text);
    font-variant-numeric: tabular-nums;
}

.capacity.is-full .capacity-head strong { color: var(--warn); }

.capacity-bar {
    height: 5px;
    border-radius: var(--r-full);
    background: var(--bg-void);
    overflow: hidden;
}

.capacity-bar i {
    display: block;
    height: 100%;
    border-radius: var(--r-full);
    background: linear-gradient(90deg, var(--red-bright), var(--red-deep));
    transition: width .4s var(--ease);
}

.capacity.is-full .capacity-bar i { background: linear-gradient(90deg, var(--warn), #C97F00); }

/* --------------------------------------------------------------------
   Premios
   -------------------------------------------------------------------- */

/* --------------------------------------------------------------------
   Premios

   El catalogo es una tienda de canje, no un listado. Cada premio se
   presenta como un articulo: el arte bajo un foco, el precio en puntos y
   -lo que de verdad mueve- cuanto te falta para llevartelo. Los que ya
   puedes canjear se encienden para que salten a la vista.
   -------------------------------------------------------------------- */

.reward-card {
    position: relative;
    display: flex;
    flex-direction: column;
    background: var(--bg-surface);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    overflow: hidden;
    /* Solo lo que cambia: el catalogo puede tener muchas tarjetas a la vez. */
    transition: border-color .2s var(--ease), transform .2s var(--ease),
                box-shadow .2s var(--ease);

    content-visibility: auto;
    contain-intrinsic-size: auto 340px;
}

.reward-card:hover {
    border-color: var(--red-line);
    transform: translateY(-2px);
    box-shadow: var(--shadow-md);
}

/* Alcanzable ahora mismo: filo rojo encendido y un resplandor bajo la
   tarjeta. Es la diferencia entre mirar el catalogo y querer canjear. */
.reward-card.is-ready {
    border-color: color-mix(in srgb, var(--red) 45%, var(--line));
    box-shadow: 0 0 0 1px rgba(225, 29, 42, .12), 0 10px 30px -18px var(--red);
}

.reward-card.is-ready::before {
    content: '';
    position: absolute;
    top: 0; left: 0; right: 0;
    height: 2px;
    z-index: 3;
    background: linear-gradient(90deg, transparent, var(--red-bright), transparent);
}

.reward-img {
    /* Imprescindible: las dos capas de imagen van posicionadas y sin esto
       se cuelgan del primer ancestro con posicion, saliendose de la
       tarjeta. */
    position: relative;
    aspect-ratio: 4 / 3;
    display: grid;
    place-items: center;
    padding: .7rem;
    /* Foco cenital sobre el arte, como en un expositor de tienda. */
    background:
        radial-gradient(105% 70% at 50% 0%, rgba(225, 29, 42, .18), transparent 70%),
        linear-gradient(135deg, rgba(225, 29, 42, .10), var(--bg-void));
    border-bottom: 1px solid var(--line-soft);
    overflow: hidden;
}

/* El marcador comparte celda con la imagen: si esta no carga basta con
   ocultarla y el 🎁 queda debajo, en lugar del icono de imagen rota. */
.reward-img > .placeholder { grid-area: 1 / 1; }

/* Una capa posicionada ignora el padding del contenedor, asi que el aire
   alrededor del arte se le da aqui. El fondo desenfocado si llega al borde. */
.reward-img .media-frame {
    inset: .7rem;
    width: auto;
    height: auto;
}

.reward-img .placeholder { font-size: 2.3rem; opacity: .5; }

.reward-body {
    padding: .85rem;
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
}

/* El tipo va sobre el arte y no en el cuerpo: ahi no le roba sitio al
   nombre ni a la descripcion, que es lo que se lee. */
.reward-type {
    position: absolute;
    top: .5rem;
    left: .5rem;
    z-index: 2;
    padding: .16rem .45rem;
    border-radius: var(--r-sm);
    background: rgba(8, 8, 10, .82);
    border: 1px solid rgba(255, 255, 255, .14);
    color: var(--text-soft);
    font-size: .62rem;
    font-weight: 800;
    letter-spacing: .06em;
    text-transform: uppercase;
}

.reward-name {
    margin: 0 0 .25rem;
    font-size: 1rem;
    overflow-wrap: anywhere;
}

/* Existencias: en rojo cuando quedan pocas, que es cuando corre prisa. */
.reward-stock {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    margin-top: .45rem;
    font-size: .72rem;
    font-weight: 700;
    color: var(--text-dim);
}

.reward-stock svg { width: 13px; height: 13px; flex: none; }
.reward-stock.is-low { color: var(--warn); }

/* Camino hacia el premio. La barra hace mas por las ganas de conseguirlo
   que cualquier texto: se ve de un vistazo lo cerca que estas. */
.reward-progress {
    margin-top: .7rem;
    padding-top: .65rem;
    border-top: 1px dashed var(--line);
}

.reward-progress-bar {
    height: 6px;
    border-radius: var(--r-full);
    background: var(--bg-void);
    border: 1px solid var(--line-soft);
    overflow: hidden;
}

.reward-progress-bar > span {
    display: block;
    height: 100%;
    border-radius: var(--r-full);
    background: linear-gradient(90deg, var(--red-deep), var(--red-bright));
    /* Un punto de luz en la punta, para que el avance se lea como avance. */
    box-shadow: 0 0 8px rgba(255, 45, 62, .55);
}

.reward-progress-txt {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .5rem;
    margin-top: .35rem;
    font-size: .72rem;
    color: var(--text-dim);
}

.reward-progress-txt strong { color: var(--warn); }

.reward-progress-txt .pct {
    font-weight: 900;
    color: var(--text-soft);
    font-variant-numeric: tabular-nums;
}

.reward-desc {
    margin: 0;
    font-size: .8rem;
    line-height: 1.45;
    color: var(--text-dim);
}

.reward-foot {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: .5rem;
    margin-top: .8rem;
}

/* Cinta sobre la imagen: agotado, o listo para canjear. */
.reward-flag {
    position: absolute;
    top: .5rem;
    right: .5rem;
    z-index: 2;
    display: inline-flex;
    align-items: center;
    gap: .25rem;
    padding: .2rem .5rem;
    border-radius: var(--r-sm);
    background: var(--danger);
    color: #fff;
    font-size: .6rem;
    font-weight: 800;
    letter-spacing: .06em;
    text-transform: uppercase;
    box-shadow: 0 2px 10px rgba(0, 0, 0, .5);
}

.reward-flag svg { width: 11px; height: 11px; flex: none; }

.reward-flag.is-ready {
    background: linear-gradient(180deg, var(--red-bright), var(--red));
}

.reward-card.is-out .reward-img img { opacity: .35; }
.reward-card.is-out .reward-img { filter: saturate(.35); }
.reward-card.is-out:hover { border-color: var(--line); transform: none; box-shadow: none; }

/* --------------------------------------------------------------------
   Saldo de puntos en la cabecera del catalogo
   -------------------------------------------------------------------- */

.points-wallet {
    display: flex;
    align-items: center;
    gap: .7rem;
    flex: none;
    padding: .6rem .95rem;
    border-radius: var(--r-lg);
    border: 1px solid var(--red-line);
    background:
        radial-gradient(120% 130% at 0% 0%, rgba(225, 29, 42, .16), transparent 65%),
        var(--bg-surface);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, .05);
}

.points-wallet-icon {
    display: grid;
    place-items: center;
    width: 38px;
    height: 38px;
    flex: none;
    border-radius: var(--r-md);
    background: rgba(225, 29, 42, .12);
    border: 1px solid var(--red-line);
    color: var(--red-bright);
}

.points-wallet-icon svg { width: 19px; height: 19px; }

.points-wallet-body { display: flex; flex-direction: column; line-height: 1.15; }

.points-wallet .k {
    font-size: .66rem;
    font-weight: 800;
    letter-spacing: .09em;
    text-transform: uppercase;
    color: var(--text-dim);
}

.points-wallet .v {
    font-size: 1.35rem;
    font-weight: 900;
    color: var(--red-bright);
    font-variant-numeric: tabular-nums;
}

.points-wallet .v small {
    font-size: .68rem;
    font-weight: 800;
    color: var(--text-dim);
}

/* auto-fill, no auto-fit: auto-fit colapsa las columnas que sobran, asi que
   con un solo premio la tarjeta se estiraba de lado a lado de la pantalla.
   Con auto-fill conserva su ancho y la rejilla se ve como tal. */
.reward-grid { grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); }

/* En movil deja de ser una rejilla de tarjetas y pasa a ser una lista: una
   fila por premio, miniatura cuadrada a la izquierda y los datos a la
   derecha. Ocupa mucho menos alto y encaja con arte 1:1 sin franjas vacias. */
@media (max-width: 639.98px) {
    .reward-grid { grid-template-columns: 1fr; gap: .6rem; }

    .reward-card {
        flex-direction: row;
        align-items: stretch;
        border-radius: var(--r-md);
        /* En fila la tarjeta es mucho mas baja que en rejilla; con la
           estimacion de escritorio la barra de desplazamiento pegaria un
           salto al pintarse la primera pantalla. */
        contain-intrinsic-size: auto 116px;
    }

    /* En una lista el desplazamiento al pasar el dedo estorba. */
    .reward-card:hover { transform: none; }

    .reward-img {
        flex: none;
        width: 96px;
        aspect-ratio: 1;
        padding: .45rem;
        border-bottom: 0;
        border-right: 1px solid var(--line-soft);
    }

    .reward-img .placeholder { font-size: 1.8rem; }

    /* En 96px no cabe una etiqueta encima del arte. El tipo se cae -el
       nombre ya lo dice- y el aviso de canjeable se queda en su marca. */
    .reward-type { display: none; }

    .reward-flag {
        top: .3rem;
        right: .3rem;
        padding: .16rem .35rem;
        font-size: .55rem;
    }

    .reward-flag.is-ready {
        gap: 0;
        padding: .25rem;
        font-size: 0;
    }

    .reward-body { padding: .6rem .75rem; }
    .reward-desc { display: none; }
    .reward-stock { margin-top: .3rem; }
    .reward-foot { margin-top: .5rem; }

    .reward-progress { margin-top: .45rem; padding-top: .45rem; }
    .reward-progress-txt { margin-top: .3rem; font-size: .68rem; }
}

/* --------------------------------------------------------------------
   Solicitudes de premios
   -------------------------------------------------------------------- */

/* Miniatura del premio. Tamano fijo: el contenido no manda aqui. */
.claim-thumb {
    flex: none;
    width: 44px;
    height: 44px;
    display: grid;
    place-items: center;
    overflow: hidden;
    border-radius: var(--r-sm);
    border: 1px solid var(--line-soft);
    background: linear-gradient(135deg, rgba(225,29,42,.12), var(--bg-void));
    font-size: 1.1rem;
}

/* Imagen y emoji comparten celda, para que el 🎁 quede debajo si la imagen
   no carga. Sin esto caerian en dos filas y la miniatura se descuadra. */
.claim-thumb > * { grid-area: 1 / 1; }
.claim-thumb img { width: 100%; height: 100%; object-fit: contain; }

/* Enlace al historial desde el catalogo */
.claims-link {
    display: flex;
    align-items: center;
    gap: .8rem;
    padding: .9rem 1rem;
    background: var(--bg-surface);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    color: inherit;
    transition: border-color .18s var(--ease);
}

.claims-link:hover { border-color: var(--red-line); }

.claims-link-icon {
    flex: none;
    display: grid;
    place-items: center;
    width: 38px;
    height: 38px;
    border-radius: var(--r-md);
    background: var(--bg-raised);
    border: 1px solid var(--line-soft);
    color: var(--red-bright);
}

.claims-link-body { flex: 1; min-width: 0; }
.claims-link-body .t { display: block; font-weight: 800; font-size: .95rem; }
.claims-link-body .s { display: block; font-size: .78rem; color: var(--text-dim); }
.claims-link-body .warn { color: var(--warn); }

/* Lista del historial */
.claim-list {
    display: flex;
    flex-direction: column;
    gap: .6rem;
    margin-bottom: 1.25rem;
}

.claim-row {
    display: flex;
    align-items: flex-start;
    gap: .8rem;
    padding: .85rem .95rem;
    background: var(--bg-surface);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
}

.claim-main { flex: 1; min-width: 0; }

.claim-title {
    font-weight: 800;
    font-size: .95rem;
    overflow-wrap: anywhere;
}

.claim-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .3rem;
    margin-top: .15rem;
    font-size: .76rem;
    color: var(--text-dim);
}

.claim-meta svg { width: 12px; height: 12px; }

.claim-note {
    margin: .5rem 0 0;
    padding: .5rem .65rem;
    border-left: 2px solid var(--line-bright);
    background: var(--bg-raised);
    border-radius: 0 var(--r-sm) var(--r-sm) 0;
    font-size: .8rem;
    color: var(--text-soft);
    overflow-wrap: anywhere;
}

.claim-note.is-admin { border-left-color: var(--red); }

.claim-side {
    flex: none;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: .45rem;
}

/* En movil la columna de estado se pone debajo, en fila, para no estrangular
   el texto del premio. */
@media (max-width: 519.98px) {
    .claim-row { flex-wrap: wrap; }

    .claim-side {
        flex-direction: row;
        align-items: center;
        justify-content: space-between;
        width: 100%;
        padding-top: .1rem;
    }
}

.reward-cost {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    font-size: .95rem;
    font-weight: 900;
    color: var(--red-bright);
}

/* --------------------------------------------------------------------
   Notificaciones
   -------------------------------------------------------------------- */

.notif {
    display: flex;
    gap: .7rem;
    padding: .8rem .9rem;
    background: var(--bg-surface);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    transition: border-color .2s var(--ease);
}

.notif:hover { border-color: var(--line-bright); }

.notif.is-unread {
    background: var(--red-faint);
    border-color: var(--red-line);
}

.notif-icon {
    width: 36px;
    height: 36px;
    flex-shrink: 0;
    display: grid;
    place-items: center;
    border-radius: var(--r-md);
    background: var(--bg-overlay);
    color: var(--red-bright);
}

.notif-icon svg { width: 17px; height: 17px; }

.notif-body { min-width: 0; flex: 1; }
.notif-body h4 { margin: 0 0 .12rem; font-size: .875rem; }
.notif-body p { margin: 0; font-size: .8rem; color: var(--text-soft); line-height: 1.45; }
.notif-body .when { font-size: .7rem; color: var(--text-dim); margin-top: .25rem; display: block; }

/* --------------------------------------------------------------------
   Linea de tiempo (historial)
   -------------------------------------------------------------------- */

.timeline { position: relative; padding-left: 1.4rem; }

.timeline::before {
    content: '';
    position: absolute;
    left: 5px;
    top: .4rem;
    bottom: .4rem;
    width: 2px;
    background: var(--line);
}

.timeline-item {
    position: relative;
    padding-bottom: .95rem;
}

.timeline-item::before {
    content: '';
    position: absolute;
    left: -1.4rem;
    top: .38rem;
    width: 12px;
    height: 12px;
    border-radius: 50%;
    background: var(--bg-base);
    border: 2px solid var(--red);
}

.timeline-item:last-child { padding-bottom: 0; }

.timeline-item .what { font-size: .86rem; line-height: 1.45; }
.timeline-item .when { font-size: .72rem; color: var(--text-dim); }

/* --------------------------------------------------------------------
   Vista previa del jugador (registro de miembros)
   -------------------------------------------------------------------- */

.player-preview {
    display: none;
    margin-top: 1rem;
    padding: 1rem;
    border: 1px solid var(--line-bright);
    border-radius: var(--r-lg);
    background: linear-gradient(160deg, rgba(225,29,42,.1), var(--bg-void));
    animation: modal-in .3s var(--ease);
}

.player-preview.is-visible { display: block; }

.preview-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(128px, 1fr));
    gap: .55rem;
    margin-top: .85rem;
}

.preview-fact {
    padding: .5rem .65rem;
    background: rgba(0, 0, 0, .3);
    border: 1px solid var(--line-soft);
    border-radius: var(--r-sm);
}

.preview-fact dt {
    margin: 0 0 .1rem;
    font-size: .64rem;
    font-weight: 800;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--text-dim);
}

.preview-fact dd {
    margin: 0;
    font-size: .88rem;
    font-weight: 700;
    word-break: break-word;
}

/* Verificacion de pertenencia al clan */
.guild-check {
    display: flex;
    align-items: flex-start;
    gap: .55rem;
    margin-top: .85rem;
    padding: .65rem .8rem;
    border-radius: var(--r-sm);
    font-size: .84rem;
    line-height: 1.45;
}

.guild-check svg { width: 17px; height: 17px; flex-shrink: 0; margin-top: 1px; }
.guild-check.is-ok   { background: rgba(46,204,113,.1); border: 1px solid rgba(46,204,113,.3); color: #B7F5D0; }
.guild-check.is-bad  { background: rgba(255,71,87,.1); border: 1px solid rgba(255,71,87,.32); color: #FFC9CE; }
.guild-check.is-warn { background: rgba(240,165,0,.1); border: 1px solid rgba(240,165,0,.3); color: #FFE2AC; }

/* Medidor de cuota de la API */
.quota-meter {
    display: flex;
    align-items: center;
    gap: .7rem;
    padding: .7rem .85rem;
    background: var(--bg-surface);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
}

.quota-ring {
    position: relative;
    width: 46px;
    height: 46px;
    flex-shrink: 0;
    border-radius: 50%;
    display: grid;
    place-items: center;
    background: conic-gradient(var(--red) calc(var(--pct, 0) * 1%), var(--bg-overlay) 0);
}

.quota-ring::before {
    content: '';
    position: absolute;
    inset: 4px;
    border-radius: 50%;
    background: var(--bg-surface);
}

.quota-ring span {
    position: relative;
    font-size: .8rem;
    font-weight: 900;
}

.quota-meter.is-low .quota-ring { background: conic-gradient(var(--warn) calc(var(--pct, 0) * 1%), var(--bg-overlay) 0); }
.quota-meter.is-out .quota-ring { background: conic-gradient(var(--danger) calc(var(--pct, 0) * 1%), var(--bg-overlay) 0); }

/* Credenciales recien generadas */
.credentials-box {
    padding: 1rem;
    border: 1px dashed var(--red-line);
    border-radius: var(--r-md);
    background: var(--red-faint);
}

.credential-line {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .75rem;
    padding: .5rem .7rem;
    margin-bottom: .45rem;
    background: var(--bg-void);
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
}

.credential-line:last-child { margin-bottom: 0; }

.credential-line .v {
    font-family: 'Cascadia Mono', 'Consolas', ui-monospace, monospace;
    font-size: .95rem;
    font-weight: 700;
    color: var(--red-bright);
    word-break: break-all;
}

/* --------------------------------------------------------------------
   Senal de navegacion entre secciones

   Aqui habia una barra de progreso propia, arriba del todo. Se quito.

   Sobraba: en una navegacion normal el navegador ya pinta la suya justo
   debajo del cuadro de direcciones, y la suya conoce el progreso real de
   la descarga mientras que la nuestra se lo inventaba. Se veian dos
   lineas casi pegadas y quedaba sucio.

   Queda solo lo que el navegador no hace: marcar el elemento pulsado.
   Responde en cero milisegundos, que es lo que de verdad quita la duda
   de "no me ha leido el toque" y evita que se vuelva a pulsar.
   -------------------------------------------------------------------- */

/* Lo pulsado acusa recibo. Muy contenido a proposito: no es un estado
   nuevo de la interfaz, solo la confirmacion de que se registro. */
.is-navigating {
    opacity: .55;
    transition: opacity .12s var(--ease);
}

/* Un boton que envia un formulario no debe poder pulsarse dos veces
   mientras se va: son dos peticiones y, en un alta, dos registros. */
button.is-navigating,
.btn.is-navigating {
    pointer-events: none;
}

@media (prefers-reduced-motion: reduce) {
    .is-navigating { transition: none; }
}

/* --------------------------------------------------------------------
   Buscador que rellena un desplegable

   El .picker-search generico se hizo para la vitrina, que vive suelto en
   la pagina. Dentro de un .field de formulario hace falta afinarlo: alli
   la regla .field label { display: block } gana en especificidad y el
   componente se partia en tres lineas.
   -------------------------------------------------------------------- */

.picker-field .picker-search {
    display: flex;
    margin-bottom: .4rem;
}

/* La caja de busqueda y el desplegable son una sola pieza, no dos campos
   sueltos: se pegan y solo redondean por fuera. */
.picker-field .picker-search {
    border-bottom-left-radius: 0;
    border-bottom-right-radius: 0;
    border-bottom-color: transparent;
}

.picker-field select[data-remote-target] {
    border-top-left-radius: 0;
    border-top-right-radius: 0;
}

.picker-field .picker-search input { height: 40px; }

/* Cuando la lista se despliega (size > 1) deja de ser un desplegable y
   pasa a ser una lista: se le da aire para que se pueda tocar comodo. */
.picker-field select[data-remote-target][size] option {
    padding: .5rem .7rem;
}

.picker-field select[data-remote-target][size]:not([size="1"]) {
    padding: 0;
    overflow-y: auto;
}

/* El recuento. Es lo que hace visible que la busqueda respondio: sin el,
   escribir no producia ningun cambio a la vista porque los resultados
   caen dentro de un desplegable cerrado. */
.picker-count {
    margin: .35rem 0 0;
    font-size: .72rem;
    color: var(--text-dim);
}

.picker-count.is-empty { color: var(--warn); }

/* Mientras se busca, el barrido que ya usaba la vitrina. */
.picker-field .picker-search.is-busy { border-color: var(--red-line); }

/* ====================================================================
   Apertura del detalle de una medalla

   No es un modal que aparece: es una pieza que se presenta. La secuencia
   va por rangos, como una pantalla de recompensa, y dura 620 ms de punta
   a punta -por encima de eso deja de sentirse contundente y empieza a
   sentirse lento-.

     0 ms   el fondo se cierra sobre la pagina
    60 ms   el panel sube y se asienta
   140 ms   la pieza cae desde arriba y rebota una vez sobre la peana
   180 ms   la peana se abre bajo ella
   240 ms   los rayos arrancan
   300 ms   el nombre y la cinta de rareza entran
   380 ms   los puntos golpean con sobrepaso
   460 ms   el acta y el sello cierran

   Todo con transform y opacity. El foco y el halo ya existian; aqui solo
   se coreografia su entrada.
   ==================================================================== */

@keyframes mm-panel {
    from { opacity: 0; transform: translate3d(0, 22px, 0) scale(.96); }
    to   { opacity: 1; transform: none; }
}

/* La pieza cae y se asienta. El sobrepaso de la curva --m-back hace el
   rebote sin necesidad de fotogramas intermedios. */
@keyframes mm-pieza {
    from { opacity: 0; transform: translate3d(0, -38px, 0) scale(.8); }
    to   { opacity: 1; transform: none; }
}

/* La peana se abre bajo la pieza, del centro hacia los lados. */
@keyframes mm-peana {
    from { opacity: 0; transform: scaleX(.2); }
    to   { opacity: 1; transform: scaleX(1); }
}

/* Solo opacidad, a proposito. Los rayos ya giran con su propia animacion
   sobre transform, y dos animaciones peleando por la misma propiedad se
   resuelven a favor de la ultima declarada: la escala no llegaria a verse
   y el giro se cortaria durante la entrada. */
@keyframes mm-rayos {
    from { opacity: 0; }
    to   { opacity: 1; }
}

@keyframes mm-sube {
    from { opacity: 0; transform: translate3d(0, 12px, 0); }
    to   { opacity: 1; transform: none; }
}

/* El golpe de los puntos: entra grande y se encaja. */
@keyframes mm-golpe {
    from { opacity: 0; transform: scale(1.5); }
    to   { opacity: 1; transform: scale(1); }
}

#medal-modal.is-open .modal {
    animation: mm-panel 300ms var(--m-out) both 60ms;
}

#medal-modal.is-open .big-img {
    animation: mm-pieza 420ms var(--m-back) both 140ms;
}

#medal-modal.is-open .medal-plinth {
    animation: mm-peana 320ms var(--m-out) both 180ms;
}

/* El impacto: cuando la pieza toca la peana, un anillo de luz se abre
   desde ella y se desvanece. Es lo que convierte la entrada en un
   aterrizaje con peso en vez de una simple caida.

   Se dibuja sobre el escenario, que ya recorta, asi que no puede
   ensanchar nada. */
@keyframes mm-onda {
    from { opacity: .85; transform: translate(-50%, -50%) scale(.25); }
    to   { opacity: 0;   transform: translate(-50%, -50%) scale(2.2); }
}

.medal-stage { position: relative; }

#medal-modal.is-open .medal-stage::after {
    content: '';
    position: absolute;
    left: 50%;
    top: 62%;
    width: 130px;
    height: 130px;
    pointer-events: none;
    border-radius: 50%;
    border: 2px solid var(--rare);
    /* Arranca justo cuando la pieza termina de caer: 140 + 420 = 560 ms,
       menos un pelo para que el destello y el golpe coincidan. */
    animation: mm-onda 520ms var(--m-out) both 520ms;
}

#medal-modal.is-open .medal-rays {
    animation: mm-rayos 420ms var(--m-out) both 240ms,
               medal-rays 24s linear infinite;
}

#medal-modal.is-open #medal-modal-name   { animation: mm-sube 280ms var(--m-out) both 300ms; }
#medal-modal.is-open .rarity-ribbon      { animation: mm-sube 280ms var(--m-out) both 340ms; }
#medal-modal.is-open .medal-reward       { animation: mm-golpe 340ms var(--m-back) both 380ms; }
#medal-modal.is-open #medal-modal-desc   { animation: mm-sube 280ms var(--m-out) both 420ms; }
#medal-modal.is-open .medal-facts        { animation: mm-sube 280ms var(--m-out) both 460ms; }
#medal-modal.is-open .sys-seal           { animation: mm-sube 320ms var(--m-out) both 520ms; }

/* --------------------------------------------------------------------
   Los puntos que otorga la medalla
   -------------------------------------------------------------------- */

/* Prendido en la esquina del escenario, como el galon de una insignia.
   Debajo del nombre competia con el por la atencion y partia la lectura;
   aqui acompana a la pieza sin quitarle el sitio. */
.medal-reward {
    position: absolute;
    top: 0;
    right: 0;
    z-index: 2;
    display: inline-flex;
    align-items: center;
    gap: .28rem;
    padding: .2rem .5rem;
    border-radius: var(--r-full);
    /* Se tine con el color de la rareza, que el modal ya resuelve en
       --rare: una legendaria anuncia sus puntos en oro. */
    color: var(--rare);
    background: color-mix(in srgb, var(--rare) 14%, var(--bg-void));
    border: 1px solid color-mix(in srgb, var(--rare) 42%, transparent);
    backdrop-filter: none;
}

.medal-reward-icon { display: grid; place-items: center; }
.medal-reward-icon svg { width: 12px; height: 12px; }

.medal-reward-n {
    font-size: .82rem;
    font-weight: 900;
    letter-spacing: -.01em;
    font-variant-numeric: tabular-nums;
    line-height: 1;
}

@media (prefers-reduced-motion: reduce) {
    #medal-modal.is-open .modal,
    #medal-modal.is-open .big-img,
    #medal-modal.is-open .medal-plinth,
    #medal-modal.is-open #medal-modal-name,
    #medal-modal.is-open .rarity-ribbon,
    #medal-modal.is-open .medal-reward,
    #medal-modal.is-open #medal-modal-desc,
    #medal-modal.is-open .medal-facts,
    #medal-modal.is-open .sys-seal {
        animation: none;
    }

    #medal-modal.is-open .medal-rays { animation: none; }
}

/* Puntos que otorga la medalla, en la esquina de la tarjeta. Se lee en la
   vitrina y en la coleccion sin abrir nada. Comparte esquina con el
   candado, pero nunca coinciden: una bloqueada no lo muestra. */
.medal-pts {
    position: absolute;
    top: .5rem;
    right: .5rem;
    z-index: 2;
    padding: .1rem .38rem;
    border-radius: var(--r-full);
    font-size: .62rem;
    font-weight: 900;
    letter-spacing: .02em;
    line-height: 1.5;
    font-variant-numeric: tabular-nums;
    color: var(--warn);
    background: color-mix(in srgb, var(--warn) 14%, var(--bg-void));
    border: 1px solid color-mix(in srgb, var(--warn) 36%, transparent);
}
