

.progression-avatar {
    width: 104px;
    height: 104px;
    object-fit: cover;
}

.progression-badge,
.progression-emblem {
    --pg-rarity: var(--secondary-rgb, 200, 121, 65);
    --pg-glow: .30;
    --pg-sweep: 3.4s;
}

.progression-badge[data-rarity="rare"],
.progression-emblem[data-rarity="rare"]             { --pg-rarity: var(--bs-info-rgb, 23, 162, 184);    --pg-glow: .45; --pg-sweep: 2.6s; }
.progression-badge[data-rarity="epique"],
.progression-emblem[data-rarity="epique"]           { --pg-rarity: var(--primary-rgb, 85, 107, 47);     --pg-glow: .60; --pg-sweep: 1.9s; }
.progression-badge[data-rarity="legendaire"],
.progression-emblem[data-rarity="legendaire"]       { --pg-rarity: var(--bs-warning-rgb, 255, 193, 7);  --pg-glow: .80; --pg-sweep: 1.3s; }

/* La bordure a sa propre variable, et ce n'est pas un détail : la couleur d'un commun est un gris:
 * à 35 % d'opacité sur un fond déjà clair, il ne dessine rien du tout — la carte paraissait sans
 * bordure. Un commun prend donc une valeur neutre franche, et les raretés colorées, qui se voient
 * beaucoup mieux, se contentent de leur propre teinte. */
.progression-badge                                  { --pg-border: rgba(var(--bs-dark-rgb, 42, 42, 42), .28); }
.progression-badge[data-rarity="rare"],
.progression-badge[data-rarity="epique"],
.progression-badge[data-rarity="legendaire"]        { --pg-border: rgba(var(--pg-rarity), .6); }

.progression-badge {
    --pg-ring: 0 0 0 0 rgba(var(--pg-rarity), 0);
    --pg-lift: 0 .125rem .35rem rgba(var(--bs-dark-rgb, 42, 42, 42), .09);

    /* La carte porte une vraie bordure, teintée par la rareté. Les fonds du projet sont des
     * teintes à 10 % d'opacité : posées sur une page blanche, elles ne délimitent rien, et une
     * grille de quarante cartes devient une bouillie. La bordure fait le travail que la teinte ne
     * peut pas faire, et redouble l'information de rareté déjà portée par le liseré. */
    border: 1px solid var(--pg-border);

    box-shadow: var(--pg-ring), var(--pg-lift) !important;
    transition: transform .22s cubic-bezier(.2, .7, .3, 1),
                box-shadow .22s ease,
                border-color .22s ease,
                background-color .22s ease;
}

/* Un badge verrouillé reste sobre — sa bordure ne doit pas crier plus fort que celle d'un badge
 * réellement obtenu — mais elle reste bien visible : une carte sans contour ne se lit pas. */
.progression-badge[data-state="locked"] {
    --pg-border: rgba(var(--bs-dark-rgb, 42, 42, 42), .22);
}

/* La pastille d'état, en tête de carte. Petites capitales espacées : elle doit se lire comme une
 * étiquette de classement, pas comme un bouton — c'est la seule chose de la carte qu'on parcourt
 * en diagonale. */
.progression-badge-state {
    font-size: .68rem;
    letter-spacing: .04em;
    text-transform: uppercase;
}

.progression-badge::before {
    content: "";
    position: absolute;
    inset: 0 0 auto 0;
    height: 3px;
    border-radius: .5rem .5rem 0 0;
    background: rgba(var(--pg-rarity), .55);
    opacity: .75;
    transition: opacity .22s ease, height .22s ease;
    pointer-events: none;
}

/* Un badge encore verrouillé ne s'annonce pas par sa rareté : le liseré reste discret. */
.progression-badge[data-state="locked"]::before {
    opacity: .2;
}

.progression-badge:hover,
.progression-badge:focus-visible {
    transform: translateY(-5px);
    --pg-border: rgba(var(--bs-dark-rgb, 42, 42, 42), .45);
    --pg-lift: 0 .75rem 1.6rem rgba(var(--bs-dark-rgb, 42, 42, 42), .16);
}

.progression-badge[data-rarity="rare"]:hover,
.progression-badge[data-rarity="epique"]:hover,
.progression-badge[data-rarity="legendaire"]:hover,
.progression-badge[data-rarity="rare"]:focus-visible,
.progression-badge[data-rarity="epique"]:focus-visible,
.progression-badge[data-rarity="legendaire"]:focus-visible {
    --pg-border: rgba(var(--pg-rarity), .9);
}

.progression-badge:hover::before,
.progression-badge:focus-visible::before {
    opacity: 1;
    height: 4px;
}

/* Une carte épinglable annonce qu'elle réagit au clic — le curseur est le seul indice que
 * Bootstrap ne donne pas à un `role="button"` qui n'est pas un `<button>`. */
.progression-badge--pinnable {
    cursor: pointer;
}

/* L'anneau de l'état « épinglé ». Une simple `.border-primary` se verrait mal sur une carte déjà
 * teintée : il faut un halo, que Bootstrap n'expose pas. */
.progression-badge[data-pinned="1"] {
    --pg-ring: 0 0 0 2px rgba(var(--primary-rgb, 85, 107, 47), .9);
}

/* Le clavier doit voir ce que la souris voit. `:focus-visible` seulement : un clic souris ne doit
 * pas laisser l'anneau accroché. */
.progression-badge:focus-visible {
    outline: none;
    --pg-ring: 0 0 0 3px rgba(var(--primary-rgb, 85, 107, 47), .45);
}

/* ───────────────────────────────────────────────────────────────────────────────────────────────
 * Punaise d'épinglage
 *
 * Révélée au survol, verrouillée en place quand la carte est épinglée. Aucun utilitaire ne place un
 * élément dans l'angle d'une carte avec un décalage fin : `top-0 end-0` colle au bord.
 */
.progression-badge-pin {
    position: absolute;
    top: .5rem;
    right: .5rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.75rem;
    height: 1.75rem;
    border-radius: 50%;
    background: rgba(var(--primary-rgb, 85, 107, 47), .12);
    opacity: 0;
    transform: scale(.7);
    transition: opacity .2s ease, transform .2s cubic-bezier(.2, .7, .3, 1), background-color .2s ease;
    pointer-events: none;
}

.progression-badge:hover .progression-badge-pin,
.progression-badge:focus-visible .progression-badge-pin {
    opacity: .6;
    transform: scale(1) rotate(-20deg);
}

.progression-badge[data-pinned="1"] .progression-badge-pin {
    opacity: 1;
    transform: scale(1) rotate(-35deg);
    background: rgba(var(--primary-rgb, 85, 107, 47), .18);
}

/* ───────────────────────────────────────────────────────────────────────────────────────────────
 * .progression-emblem — l'emblème lui-même
 *
 * C'est le sujet de la carte : c'est lui qui doit réagir, davantage que la carte qui le porte.
 */
.progression-emblem {
    position: relative;
    transition: transform .28s cubic-bezier(.2, .7, .3, 1), filter .28s ease;
}

.progression-badge:hover .progression-emblem,
.progression-badge:focus-visible .progression-emblem,
.progression-pin-chip:hover .progression-emblem,
.progression-showcase-item:hover .progression-emblem,
.progression-showcase-item:focus-visible .progression-emblem {
    transform: scale(1.1);
    filter: drop-shadow(0 .35rem .75rem rgba(var(--pg-rarity), var(--pg-glow)));
}

/* Un badge verrouillé ne prend pas le halo coloré : il n'a rien à célébrer. */
.progression-badge[data-state="locked"]:hover .progression-emblem,
.progression-badge[data-state="locked"]:focus-visible .progression-emblem {
    filter: none;
    transform: scale(1.05);
}

/* ───────────────────────────────────────────────────────────────────────────────────────────────
 * Scintillement — plus le badge est rare, plus il brille
 *
 * Un balayage lumineux traverse le médaillon au survol ; sa vitesse et son intensité montent avec
 * la rareté (variables ci-dessus). Rien d'équivalent dans Bootstrap.
 *
 * Le balayage est un pseudo-élément : il ne touche pas au DOM et ne peut donc pas gêner le clic.
 */
.progression-shimmer {
    position: relative;
    display: inline-flex;
    overflow: hidden;
    /* Coins à peine adoucis, pas un cercle : le rognage s'applique aussi au SVG, et un rayon de
     * 50 % couperait les angles de l'écu et les pointes du cadre légendaire. */
    border-radius: 10%;
}

.progression-shimmer::after {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(115deg,
        transparent 38%,
        rgba(255, 255, 255, calc(var(--pg-glow) * .8)) 50%,
        transparent 62%);
    transform: translateX(-130%);
    pointer-events: none;
}

/* Le scintillement démarre au survol et tourne en boucle tant qu'on y reste. */
.progression-badge:hover .progression-shimmer::after,
.progression-badge:focus-visible .progression-shimmer::after,
.progression-pin-chip:hover .progression-shimmer::after,
.progression-showcase-item:hover .progression-shimmer::after,
.progression-showcase-item:focus-visible .progression-shimmer::after {
    animation: progression-sweep var(--pg-sweep, 3.4s) ease-in-out infinite;
}

/* ── Le légendaire brille en permanence ────────────────────────────────────────────────────────
 *
 * C'est le seul rang qui n'attend pas le curseur : dans une grille de quarante médaillons, il doit
 * se repérer sans qu'on ait à survoler quoi que ce soit.
 *
 * Les sélecteurs sont volontairement descendants (`.progression-badge .progression-shimmer[…]`) :
 * à spécificité égale avec les règles de survol ci-dessus, et déclarés après elles, ils gagnent —
 * l'animation ne s'interrompt donc pas quand on quitte la carte. */
.progression-badge .progression-shimmer[data-rarity="legendaire"]::after,
.progression-pin-chip .progression-shimmer[data-rarity="legendaire"]::after,
.progression-showcase-item .progression-shimmer[data-rarity="legendaire"]::after {
    animation: progression-sweep var(--pg-sweep) ease-in-out infinite;
}

/* Une aura qui respire, en plus du balayage. Le balayage traverse et disparaît ; l'aura reste, et
 * c'est elle qui attire l'oeil de loin.
 *
 * Elle est portée par un **disque** placé derrière le médaillon, sur l'élément extérieur — celui
 * qui ne rogne rien. La version précédente l'appliquait en `box-shadow` sur le médaillon lui-même :
 * une ombre de boîte épouse la boîte, et le médaillon est un carré à coins à peine adoucis
 * (`border-radius: 10%`, imposé par le balayage). Résultat, un halo carré très visible autour d'un
 * emblème rond.
 *
 * Pourquoi pas un `filter: drop-shadow`, qui épouserait la silhouette réelle du SVG : le balayage
 * est un descendant, sa bande blanche entre donc dans le canal alpha et ramènerait la forme carrée
 * pendant qu'elle traverse. Un disque frère du balayage ne peut pas être contaminé.
 *
 * Pourquoi pas simplement `border-radius: 50%` sur le médaillon : le cadre légendaire touche
 * exactement le cercle inscrit (rayon 32 sur 32 dans la boîte de 64), ses pointes seraient rasées. */
.progression-glint[data-rarity="legendaire"]::before {
    content: "";
    position: absolute;
    /* Un peu plus large que le médaillon : une aura qui s'arrête à son bord ne se voit pas. */
    inset: -12%;
    border-radius: 50%;
    background: radial-gradient(closest-side,
        rgba(var(--pg-rarity), .75),
        rgba(var(--pg-rarity), .28) 62%,
        rgba(var(--pg-rarity), 0) 100%);
    animation: progression-aura 2.6s ease-in-out infinite;
    pointer-events: none;
}

/* Le disque est déclaré avant le contenu et n'est pas positionné dans le flux : le médaillon,
 * lui, l'est (`position: relative` du balayage), il passe donc devant sans z-index. */
@keyframes progression-aura {
    0%, 100% { opacity: .35; transform: scale(.9); }
    50%      { opacity: 1;   transform: scale(1.08); }
}

@keyframes progression-sweep {
    0%   { transform: translateX(-130%); }
    55%  { transform: translateX(130%); }
    100% { transform: translateX(130%); }
}

/* ───────────────────────────────────────────────────────────────────────────────────────────────
 * .progression-tip — la description au survol
 *
 * Une infobulle purement CSS, portée par `data-tip`. Les infobulles MDB demandent une
 * initialisation JavaScript par élément et un conteneur injecté dans le `<body>` : beaucoup de
 * machinerie pour une phrase. Ici, ce qui n'est pas dans l'attribut n'existe pas — un badge dont le
 * serveur a masqué la description n'a tout simplement pas d'attribut, donc rien à révéler.
 */
.progression-tip {
    position: relative;
}

/* L'infobulle sort de la carte par le haut : celle-ci ne rogne donc plus (`overflow-hidden` retiré
 * du gabarit). Reste à la faire passer AU-DESSUS des cartes voisines — sans `z-index`, une carte
 * plus loin dans le DOM la recouvrirait, et l'élever au survol suffit : c'est le seul moment où
 * elle existe. */
.progression-badge:hover,
.progression-badge:focus-within,
.progression-tip:hover,
.progression-tip:focus-visible {
    z-index: 3;
}

.progression-tip[data-tip]::after {
    content: attr(data-tip);
    position: absolute;
    z-index: 4;
    left: 50%;
    bottom: calc(100% + .6rem);
    transform: translateX(-50%) translateY(.3rem) scale(.96);
    transform-origin: bottom center;
    width: max-content;
    max-width: 14rem;
    padding: .45rem .65rem;
    border-radius: .5rem;
    background: rgba(var(--bs-dark-rgb, 42, 42, 42), .96);
    color: #fff;
    font-size: .78rem;
    font-weight: 400;
    line-height: 1.35;
    text-align: center;
    white-space: normal;
    opacity: 0;
    visibility: hidden;
    transition: opacity .16s ease, transform .16s cubic-bezier(.2, .7, .3, 1);
    pointer-events: none;
    z-index: 10;
}

/* La pointe de l'infobulle. */
.progression-tip[data-tip]::before {
    content: "";
    position: absolute;
    z-index: 4;
    left: 50%;
    bottom: calc(100% + .3rem);
    width: .5rem;
    height: .5rem;
    transform: translateX(-50%) rotate(45deg);
    background: rgba(var(--bs-dark-rgb, 42, 42, 42), .96);
    opacity: 0;
    transition: opacity .16s ease;
    pointer-events: none;
    z-index: 10;
}

.progression-tip[data-tip]:hover::after,
.progression-tip[data-tip]:focus-visible::after {
    opacity: 1;
    visibility: visible;
    transform: translateX(-50%) translateY(0) scale(1);
}

.progression-tip[data-tip]:hover::before,
.progression-tip[data-tip]:focus-visible::before {
    opacity: 1;
}

/* ───────────────────────────────────────────────────────────────────────────────────────────────
 * .progression-showcase-item — un médaillon de la vitrine du profil
 */
.progression-showcase-item {
    display: inline-flex;
    border-radius: .75rem;
    transition: transform .22s cubic-bezier(.2, .7, .3, 1);
}

.progression-showcase-item:hover {
    transform: translateY(-3px);
}

.progression-showcase-item:focus-visible {
    outline: 2px solid rgba(var(--primary-rgb, 85, 107, 47), .55);
    outline-offset: 3px;
}

/* ───────────────────────────────────────────────────────────────────────────────────────────────
 * .progression-pin-chip — un badge épinglé du bandeau, cliquable pour le retirer
 */
.progression-pin-chip {
    position: relative;
    display: inline-flex;
    background: none;
    border: 0;
    padding: 0;
    line-height: 0;
    border-radius: .75rem;
    transition: transform .22s cubic-bezier(.2, .7, .3, 1);
}

.progression-pin-chip:hover {
    transform: translateY(-4px);
}

.progression-pin-chip:focus-visible {
    outline: 2px solid rgba(var(--primary-rgb, 85, 107, 47), .55);
    outline-offset: 3px;
}

/* La croix de retrait n'apparaît qu'au survol : la vitrine reste propre au repos. */
.progression-pin-chip-remove {
    position: absolute;
    top: -.3rem;
    right: -.3rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.2rem;
    height: 1.2rem;
    padding: 0;
    border-radius: 50%;
    font-size: .65rem;
    line-height: 1;
    opacity: 0;
    transform: scale(.6);
    transition: opacity .18s ease, transform .18s cubic-bezier(.2, .7, .3, 1);
}

.progression-pin-chip:hover .progression-pin-chip-remove,
.progression-pin-chip:focus-visible .progression-pin-chip-remove {
    opacity: 1;
    transform: scale(1);
}

/* ───────────────────────────────────────────────────────────────────────────────────────────────
 * Filtres
 *
 * Les trois filtres sont de simples `<select class="form-select form-select-sm">` : rien à styler,
 * MDB s'en charge, et ils se ressemblent tous les trois — c'est le but.
 */

/* Carte écartée par un filtre : retirée du flux, sans toucher aux classes de grille Bootstrap. */
.progression-hidden {
    display: none !important;
}

/* ───────────────────────────────────────────────────────────────────────────────────────────────
 * Accessibilité — rien ne bouge pour qui ne le souhaite pas.
 */
@media (prefers-reduced-motion: reduce) {
    .progression-badge,
    .progression-badge::before,
    .progression-emblem,
    .progression-pin-chip,
    .progression-pin-chip-remove,
    .progression-showcase-item,
    .progression-badge-pin,
    .progression-tip[data-tip]::after,
    .progression-tip[data-tip]::before {
        transition: none;
    }

    .progression-badge:hover,
    .progression-badge:focus-visible,
    .progression-pin-chip:hover,
    .progression-showcase-item:hover {
        transform: none;
    }

    .progression-badge:hover .progression-emblem,
    .progression-badge:focus-visible .progression-emblem,
    .progression-showcase-item:hover .progression-emblem,
    .progression-pin-chip:hover .progression-emblem {
        transform: none;
    }

    /* L'aura a changé de porteur (`.progression-glint::before`) : sans cette ligne, elle aurait
       continué de pulser malgré la préférence système. */
    .progression-shimmer,
    .progression-shimmer::after,
    .progression-glint[data-rarity="legendaire"]::before {
        animation: none !important;
    }
}


/* ═══════════════════════════════════════════════════════════════════════════
 * PAGES FRONT DU MODULE — huitième groupe de règles
 * (bandeau commun, classement, boutique)
 *
 * Le tableau Bootstrap d'origine était juste, et parfaitement muet : il ressemblait à un export
 * de tableur posé sur une page de site de jeu de rôle. Un classement est un objet de fierté, il
 * doit se regarder. Ce qui suit dessine un panthéon — bandeau, podium, stèles — avec la même
 * discipline que le reste du fichier : **aucune couleur en dur**, tout passe par `--bs-*-rgb` et
 * par les jetons de thème, donc tout suit les thèmes clair, sombre et ROSE.
 *
 * Chaque règle existe parce qu'aucun utilitaire ne la fournit :
 *
 *  - hauteurs et rognage d'avatar : `object-fit` n'a pas d'utilitaire dans la version de `mdb.css`
 *    embarquée (même raison que `.progression-avatar`, plus haut) ;
 *  - médaillons : une taille en pixels, que les utilitaires de largeur ne savent pas exprimer ;
 *  - stèles du podium : des hauteurs relatives entre elles, qui *sont* le podium ;
 *  - barre d'écart : `.progress` fait 1rem de haut, il en faut quatre pixels ;
 *  - lueur de médaille et survol de ligne : Bootstrap n'a que deux ombres figées, sans interpolation.
 *
 * Tout ce qui bouge est coupé sous `prefers-reduced-motion`, en fin de fichier avec le reste.
 * ═══════════════════════════════════════════════════════════════════════════ */

/* Le bandeau, **commun aux cinq pages front du module** (`_partials/_hero.html.twig`). Une teinte
 * primaire très diluée, traversée d'un dégradé : sur une page blanche, un aplat à 10 % ne délimite
 * rien (c'est le constat déjà fait pour les cartes de badge). Le dégradé lui donne une profondeur
 * sans peser, et reste lisible en thème sombre parce qu'il n'est fait que d'opacités de la couleur
 * primaire, jamais d'un blanc ou d'un noir posé en dur. */
.pg-hero {
    background:
        radial-gradient(120% 90% at 50% -20%, rgba(var(--bs-primary-rgb), .22), transparent 62%),
        linear-gradient(180deg, rgba(var(--bs-primary-rgb), .10), rgba(var(--bs-primary-rgb), .02));
    border-bottom: 1px solid rgba(var(--bs-primary-rgb), .18);
}

/* Le liseré doré qui court sous le bandeau : il annonce la couleur du podium avant qu'on l'ait vu,
 * et sert de signature visuelle aux autres pages du module. Trois pixels, en dégradé, sans quoi ce
 * serait un trait de tableur de plus. */
.pg-hero::after {
    content: "";
    display: block;
    height: 3px;
    margin-top: -1px;
    background: linear-gradient(90deg,
        transparent,
        rgba(var(--bs-warning-rgb), .55) 30%,
        rgba(var(--bs-warning-rgb), .9) 50%,
        rgba(var(--bs-warning-rgb), .55) 70%,
        transparent);
}

/* ── Médailles ──────────────────────────────────────────────────────────────
 * Une seule mécanique pour les trois places, différenciée par une variable posée en `data-medal`.
 * Or, argent, bronze : ce sont des jetons de thème (`warning`, `secondary`, `danger`), pas des
 * couleurs choisies — le podium doit rester lisible sur le thème ROSE comme sur le sombre. */
.pg-lb-medal {
    --pg-medal: var(--bs-secondary-rgb, 108, 117, 125);
    --pg-medal-glow: .35;
}

.pg-lb-medal[data-medal="1"] { --pg-medal: var(--bs-warning-rgb, 255, 193, 7);   --pg-medal-glow: .75; }
.pg-lb-medal[data-medal="2"] { --pg-medal: var(--bs-secondary-rgb, 108, 117, 125); --pg-medal-glow: .45; }
.pg-lb-medal[data-medal="3"] { --pg-medal: var(--bs-danger-rgb, 220, 53, 69);    --pg-medal-glow: .45; }

/* Le portrait du podium : un disque rogné, cerclé de la couleur de sa médaille, posé sur un halo
 * qui respire. Le halo est un `box-shadow` et non un pseudo-élément — il doit suivre le disque
 * exactement, y compris quand GSAP le met à l'échelle à l'entrée. */
.pg-lb-portrait {
    width: 96px;
    height: 96px;
    object-fit: cover;
    border: 3px solid rgba(var(--pg-medal), .85);
    box-shadow:
        0 0 0 6px rgba(var(--pg-medal), .12),
        0 .5rem 1.4rem rgba(var(--pg-medal), calc(var(--pg-medal-glow) * .40));
}

/* Première place : un peu plus grande, et son halo pulse lentement. C'est la seule chose animée en
 * continu sur la page — au-delà, ce serait une vitrine de magasin. */
.pg-lb-medal[data-medal="1"] .pg-lb-portrait {
    width: 120px;
    height: 120px;
    animation: pg-lb-crown 3.2s ease-in-out infinite;
}

@keyframes pg-lb-crown {
    0%, 100% { box-shadow: 0 0 0 6px rgba(var(--pg-medal), .12), 0 .5rem 1.4rem rgba(var(--pg-medal), .30); }
    50%      { box-shadow: 0 0 0 10px rgba(var(--pg-medal), .18), 0 .6rem 2rem rgba(var(--pg-medal), .48); }
}

/* Le portrait de remplacement, quand il n'y a pas d'avatar — ou quand le profil est discret et
 * qu'on n'a pas le droit d'en montrer un. Une initiale, pas une silhouette générique : elle
 * distingue au moins les lignes les unes des autres. */
.pg-lb-portrait-fallback {
    background: rgba(var(--pg-medal), .12);
    color: rgba(var(--pg-medal), 1);
    font-family: 'Marcellus', serif;
    font-size: 2.2rem;
    line-height: 1;
}

.pg-lb-medal[data-medal="1"] .pg-lb-portrait-fallback { font-size: 2.8rem; }

/* ── Stèles du podium ───────────────────────────────────────────────────────
 * Les hauteurs *sont* le podium : sans elles, trois cartes alignées ne racontent rien. Elles ne
 * s'appliquent qu'à partir de `md` — empilées sur un téléphone, 1 · 2 · 3 se lit de haut en bas et
 * un décalage vertical n'aurait aucun sens. */
.pg-lb-plinth {
    border: 1px solid rgba(var(--pg-medal), .28);
    background: linear-gradient(180deg, rgba(var(--pg-medal), .10), rgba(var(--pg-medal), .02));
    transition: transform .25s cubic-bezier(.2, .7, .3, 1), box-shadow .25s ease;
}

.pg-lb-plinth:hover {
    transform: translateY(-4px);
    box-shadow: 0 .75rem 1.75rem rgba(var(--pg-medal), .22) !important;
}

@media (min-width: 768px) {
    .pg-lb-plinth[data-medal="1"] { min-height: 21rem; }
    .pg-lb-plinth[data-medal="2"],
    .pg-lb-plinth[data-medal="3"] { min-height: 17rem; }
}

/* ── Lignes du classement ───────────────────────────────────────────────────
 * Des lignes, pas des cellules de tableau : chaque membre est une carte basse, avec sa place, son
 * portrait, son rang et sa barre d'écart. Le liseré de gauche prend la couleur du rang, ce qui
 * donne au tableau une trame lisible en diagonale — on repère « les Vétérans » d'un coup d'œil. */
.pg-lb-row {
    border: 1px solid rgba(var(--bs-dark-rgb, 42, 42, 42), .10);
    border-left: 3px solid rgba(var(--bs-primary-rgb), .35);
    transition: transform .2s cubic-bezier(.2, .7, .3, 1),
                box-shadow .2s ease,
                border-color .2s ease;
}

.pg-lb-row:hover {
    transform: translateX(3px);
    border-left-color: rgba(var(--bs-primary-rgb), .9);
    box-shadow: 0 .4rem 1rem rgba(var(--bs-dark-rgb, 42, 42, 42), .10) !important;
}

/* La ligne du visiteur. Elle doit se trouver sans être cherchée : fond teinté, liseré plein, et
 * elle ne bouge pas au survol — c'est un repère, pas une cible. */
.pg-lb-row[data-me="1"] {
    border-left-color: rgb(var(--bs-primary-rgb));
    background: rgba(var(--bs-primary-rgb), .07) !important;
}

.pg-lb-row[data-me="1"]:hover { transform: none; }

/* Le numéro de place. Une taille fixe, sinon la colonne danse d'une ligne à l'autre selon qu'on
 * affiche 4 ou 47. */
.pg-lb-rankno {
    width: 2.5rem;
    height: 2.5rem;
    font-family: 'Marcellus', serif;
    font-size: 1.05rem;
}

/* Le portrait de ligne : même rognage que le podium, en plus petit et sans halo. */
.pg-lb-thumb {
    width: 44px;
    height: 44px;
    object-fit: cover;
}

.pg-lb-thumb-fallback {
    background: rgba(var(--bs-primary-rgb), .12);
    color: rgba(var(--bs-primary-rgb), 1);
    font-family: 'Marcellus', serif;
}

/* La barre d'écart au meneur. `.progress` fait 1rem de haut, ce qui en ferait l'élément le plus
 * lourd de la ligne : ici c'est une indication, pas une jauge à lire. */
.pg-lb-bar {
    height: 4px;
    background: rgba(var(--bs-dark-rgb, 42, 42, 42), .08);
}

/* ── Boutique (/boutique) ───────────────────────────────────────────────────
 * Les cartes d'article reprennent la mécanique des cartes de badge — variable de teinte, bordure
 * qui en découle, survol animé — mais la teinte vient ici du **type d'article** et non de la rareté :
 * un rôle, un boost, un rôle temporaire et un badge ne se ressemblent pas, et le catalogue doit se
 * parcourir en diagonale. Aucune couleur en dur, tout en jetons de thème.
 *
 * Ce que Bootstrap ne fournit pas, et qui justifie chaque règle :
 *  - un disque de pictogramme à taille fixe (les utilitaires de largeur ne s'expriment pas en px) ;
 *  - un survol interpolé (Bootstrap n'a que deux ombres figées) ;
 *  - un état « verrouillé » qui grise sans effacer — un article hors de portée doit rester une
 *    envie, pas un trou dans la grille ;
 *  - une étiquette de prix en angle de carte, que rien n'approche dans les utilitaires. */

.pg-shop-card {
    --pg-kind: var(--bs-primary-rgb, 85, 107, 47);

    border: 1px solid rgba(var(--pg-kind), .28);
    box-shadow: 0 .125rem .35rem rgba(var(--bs-dark-rgb, 42, 42, 42), .09) !important;
    transition: transform .22s cubic-bezier(.2, .7, .3, 1),
                box-shadow .22s ease,
                border-color .22s ease;
}

/* Un jeton par nature d'article. `role` garde la primaire : c'est l'article le plus courant, il ne
 * doit pas se distinguer, ce sont les autres qui se signalent. */
.pg-shop-card[data-kind="badge"]      { --pg-kind: var(--bs-warning-rgb, 255, 193, 7); }
.pg-shop-card[data-kind="temp_role"]  { --pg-kind: var(--bs-info-rgb, 23, 162, 184); }
.pg-shop-card[data-kind="boost"]      { --pg-kind: var(--bs-success-rgb, 25, 135, 84); }
.pg-shop-card[data-kind="channel"]    { --pg-kind: var(--bs-secondary-rgb, 108, 117, 125); }

.pg-shop-card:hover,
.pg-shop-card:focus-within {
    transform: translateY(-4px);
    border-color: rgba(var(--pg-kind), .65);
    box-shadow: 0 .75rem 1.6rem rgba(var(--pg-kind), .22) !important;
}

/* Article hors de portée : niveau insuffisant, épuisé, ou réservé à Discord. Il reste lisible —
 * on doit pouvoir lire ce qu'on n'a pas encore — mais il cesse de réclamer l'attention, et il ne
 * se soulève plus au survol : il n'y a rien à y faire. */
.pg-shop-card[data-locked="1"] {
    --pg-kind: var(--bs-dark-rgb, 42, 42, 42);
    opacity: .72;
}

.pg-shop-card[data-locked="1"]:hover,
.pg-shop-card[data-locked="1"]:focus-within {
    transform: none;
    box-shadow: 0 .125rem .35rem rgba(var(--bs-dark-rgb, 42, 42, 42), .09) !important;
}

/* Le disque de pictogramme. Taille fixe, teintée par la nature de l'article : c'est le repère qu'on
 * suit du regard quand on parcourt une grille de vingt cartes. */
.pg-shop-glyph {
    width: 60px;
    height: 60px;
    background: rgba(var(--pg-kind), .12);
    color: rgb(var(--pg-kind));
}

.pg-shop-glyph--lg {
    width: 96px;
    height: 96px;
}

/* L'étiquette de prix, posée dans l'angle. Elle doit se lire avant le nom de l'article : en
 * boutique, on regarde le prix d'abord. */
.pg-shop-price {
    background: rgba(var(--bs-warning-rgb), .15);
    color: rgba(var(--bs-dark-rgb, 42, 42, 42), .92);
    border: 1px solid rgba(var(--bs-warning-rgb), .45);
    font-variant-numeric: tabular-nums;
}

/* Un prix hors budget se barre d'un liseré : la carte dit « pas encore », pas « pas pour toi ». */
.pg-shop-card[data-afford="0"] .pg-shop-price {
    background: rgba(var(--bs-dark-rgb, 42, 42, 42), .07);
    border-color: rgba(var(--bs-dark-rgb, 42, 42, 42), .18);
}

/* ── Mes achats ─────────────────────────────────────────────────────────────
 * Une frise verticale plutôt qu'un tableau : un historique se lit dans le temps. Le trait qui relie
 * les points est un pseudo-élément, faute d'utilitaire pour tracer une ligne entre des éléments. */
.pg-shop-entry {
    border-left: 2px solid rgba(var(--bs-primary-rgb), .18);
}

.pg-shop-entry:last-child {
    border-left-color: transparent;
}

.pg-shop-dot {
    width: 2.25rem;
    height: 2.25rem;
    margin-left: -1.125rem;
    background: rgba(var(--bs-primary-rgb), .12);
    color: rgb(var(--bs-primary-rgb));
    border: 2px solid rgba(var(--bs-primary-rgb), .35);
}

.pg-shop-entry[data-delivery="failed"] .pg-shop-dot {
    background: rgba(var(--bs-danger-rgb), .12);
    color: rgb(var(--bs-danger-rgb));
    border-color: rgba(var(--bs-danger-rgb), .45);
}

.pg-shop-entry[data-delivery="pending"] .pg-shop-dot {
    background: rgba(var(--bs-warning-rgb), .14);
    color: rgba(var(--bs-dark-rgb, 42, 42, 42), .8);
    border-color: rgba(var(--bs-warning-rgb), .5);
}

/* ── Ma progression : bandeau ───────────────────────────────────────────────
 * Le médaillon d'avatar de la page de progression, cerclé par son rang. La couleur vient d'un jeton
 * posé en `data-rank`, comme les médailles du podium : une seule mécanique, six teintes possibles. */
.pg-me-portrait {
    width: 132px;
    height: 132px;
    object-fit: cover;
}

/* ── Mouvement ────────────────────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
    .pg-shop-card { transition: none; }
    .pg-shop-card:hover,
    .pg-shop-card:focus-within { transform: none; }
}

/* ── Mouvement ──────────────────────────────────────────────────────────────
 * Les blocs animés par GSAP sont masqués tant que `gsap-pending` est posée, comme sur les autres
 * pages du site. Sans JavaScript, la classe n'est jamais retirée : elle est donc posée par un
 * script inline, jamais par le gabarit — si le script ne tourne pas, rien n'est masqué. */
@media (prefers-reduced-motion: reduce) {
    .pg-lb-medal[data-medal="1"] .pg-lb-portrait { animation: none; }
    .pg-lb-plinth,
    .pg-lb-row { transition: none; }
    .pg-lb-plinth:hover,
    .pg-lb-row:hover { transform: none; }
}


/* ═══════════════════════════════════════════════════════════════════════════
 * ARTEFACTS DE VAELORIA — neuvième groupe de règles
 * (cadres d'avatar, bannières de profil, bordures de niveau, cartes d'inventaire)
 *
 * **La seule exception assumée à « aucune couleur en dur » de tout le module.**
 *
 * Partout ailleurs, une couleur vient d'un jeton de thème (`--bs-primary-rgb`, `--bs-warning-rgb`)
 * parce qu'elle porte un *sens d'interface* : succès, danger, rareté. Ici, non. Une bannière
 * « crépuscule » est une **illustration** : la faire passer par les six jetons du thème donnerait
 * six bannières identiques à la teinte près, c'est-à-dire aucune raison de les collectionner.
 *
 * L'exception est donc bornée, et elle tient à trois conditions :
 *
 *  1. **elle ne concerne que le dessin**, jamais le texte ni les commandes posés dessus, qui
 *     continuent d'utiliser les classes de thème ;
 *  2. **aucune de ces valeurs ne vient de la base** : `ArtifactArtwork` tient la liste fermée, la
 *     base ne stocke qu'un slug, et c'est ce qui empêche une couleur saisie en back-office
 *     d'atteindre une feuille de style (CDC §16.1) ;
 *  3. **chaque dessin est lisible dans les deux thèmes**, parce qu'il pose son propre fond et que
 *     le contenu qu'on met dessus porte sa propre couleur.
 *
 * Ajouter un dessin = une entrée dans `ArtifactArtwork::ARTS` **et** une règle ici. Rien d'autre.
 * ═══════════════════════════════════════════════════════════════════════════ */

/* ── Cadres d'avatar ────────────────────────────────────────────────────────
 * Un anneau conique posé derrière le portrait. Le dégradé tourne, donc le cadre a un « haut » et
 * un « bas » : c'est ce qui le distingue d'une simple bordure de couleur, que le module accorde
 * déjà gratuitement par palier (`SitePerkService`). Un objet payant doit se voir. */
.pg-avatar-frame {
    padding: 4px;
    background: var(--pg-frame, transparent);
    box-shadow: var(--pg-frame-glow, none);
}

.pg-art-avatar_frame-laurier  { --pg-frame: conic-gradient(from 210deg, #7d8c4e, #c8d99a, #6f7d43, #a8bd76, #7d8c4e); }
.pg-art-avatar_frame-runes    { --pg-frame: conic-gradient(from 0deg, #3d5a80, #98c1d9, #2b3a55, #7fa8c9, #3d5a80);
                                --pg-frame-glow: 0 0 14px rgba(61, 90, 128, .45); }
.pg-art-avatar_frame-epines   { --pg-frame: conic-gradient(from 40deg, #4a3728, #8c6849, #2f231a, #7a5c3f, #4a3728); }
.pg-art-avatar_frame-flammes  { --pg-frame: conic-gradient(from 90deg, #b23a1a, #f2a03d, #7c1f0c, #e2652a, #b23a1a);
                                --pg-frame-glow: 0 0 18px rgba(226, 101, 42, .55); }
.pg-art-avatar_frame-cristal  { --pg-frame: conic-gradient(from 150deg, #6a5acd, #b8a9ff, #3f3480, #9a8cf0, #6a5acd);
                                --pg-frame-glow: 0 0 18px rgba(154, 140, 240, .55); }
.pg-art-avatar_frame-abysses  { --pg-frame: conic-gradient(from 270deg, #0b132b, #3a506b, #05070f, #1c2541, #0b132b);
                                --pg-frame-glow: 0 0 22px rgba(58, 80, 107, .7); }

/* ── Bannières de profil ────────────────────────────────────────────────────
 * Un fond posé derrière l'en-tête. Il doit rester **calme** : c'est un arrière-plan, pas une
 * affiche. D'où des dégradés doux plutôt que des motifs contrastés — le pseudo et la barre d'XP
 * passent par-dessus et doivent rester lisibles. */
.pg-banner {
    background: var(--pg-banner, transparent);
    background-size: cover;
    background-position: center;
}

.pg-art-profile_banner-parchemin  { --pg-banner: linear-gradient(135deg, #e8dcc0 0%, #d9c8a0 45%, #c9b287 100%); }
.pg-art-profile_banner-foret      { --pg-banner: linear-gradient(160deg, #2f4a34 0%, #5b7f52 55%, #94b06d 100%); }
.pg-art-profile_banner-crepuscule { --pg-banner: linear-gradient(160deg, #2b2545 0%, #7d4f6d 50%, #e0906b 100%); }
.pg-art-profile_banner-grimoire   { --pg-banner: linear-gradient(140deg, #3b2c22 0%, #6b5136 50%, #a68a5b 100%); }
.pg-art-profile_banner-aurore     { --pg-banner: linear-gradient(160deg, #1b3b5a 0%, #4d8fa8 45%, #f0c76e 100%); }
.pg-art-profile_banner-nuit       { --pg-banner: radial-gradient(120% 90% at 30% 15%, #2a3a63 0%, #131a33 55%, #060912 100%); }

/* ── Bordures de niveau ─────────────────────────────────────────────────────
 * Le plus petit des trois cosmétiques : il cercle le médaillon de niveau. Un anneau plein plutôt
 * qu'un dégradé conique — à cette taille, un dégradé tournant devient une bouillie. */
.pg-level-frame {
    border: 3px solid var(--pg-level, transparent);
    box-shadow: var(--pg-level-glow, none);
}

.pg-art-level_frame-bronze     { --pg-level: #a9714b; }
.pg-art-level_frame-argent     { --pg-level: #b9c2c9; }
.pg-art-level_frame-or         { --pg-level: #d4a72c; --pg-level-glow: 0 0 10px rgba(212, 167, 44, .5); }
.pg-art-level_frame-emeraude   { --pg-level: #3f9b6d; --pg-level-glow: 0 0 10px rgba(63, 155, 109, .5); }
.pg-art-level_frame-obsidienne { --pg-level: #1b1b22; --pg-level-glow: 0 0 12px rgba(120, 120, 160, .55); }

/* Les deux consommables n'ont pas de dessin porté : leur carte affiche un pictogramme. Les classes
 * existent quand même, pour que `cssClass()` ne renvoie jamais un nom sans règle. */
.pg-art-call_gm-cor,
.pg-art-call_players-banniere { --pg-frame: none; }

/* ── Cartes d'inventaire ────────────────────────────────────────────────────
 * Même mécanique que les cartes de badge : une variable de rareté, une bordure qui en découle, un
 * survol interpolé. Bootstrap n'a que deux ombres figées, sans interpolation. */
.pg-inv-card {
    --pg-rar: var(--bs-secondary-rgb, 108, 117, 125);

    border: 1px solid rgba(var(--pg-rar), .35);
    transition: transform .22s cubic-bezier(.2, .7, .3, 1), box-shadow .22s ease, border-color .22s ease;
}

.pg-inv-card[data-rarity="rare"]       { --pg-rar: var(--bs-info-rgb, 23, 162, 184); }
.pg-inv-card[data-rarity="epique"]     { --pg-rar: var(--bs-primary-rgb, 85, 107, 47); }
.pg-inv-card[data-rarity="legendaire"] { --pg-rar: var(--bs-warning-rgb, 255, 193, 7); }

.pg-inv-card:hover,
.pg-inv-card:focus-within {
    transform: translateY(-4px);
    border-color: rgba(var(--pg-rar), .7);
    box-shadow: 0 .75rem 1.6rem rgba(var(--pg-rar), .22) !important;
}

/* Artefact équipé : il ne se soulève plus au survol — il est déjà en place, il n'y a rien à y
 * faire — et son liseré est plein, pour qu'on le repère sans lire les boutons. */
.pg-inv-card[data-equipped="1"] {
    border-color: rgb(var(--bs-primary-rgb));
    border-width: 2px;
}

.pg-inv-card[data-equipped="1"]:hover { transform: none; }

/* Un consommable déjà utilisé reste dans l'inventaire — c'est l'historique des appels — mais il
 * s'efface : ce n'est plus un objet, c'est un souvenir. */
.pg-inv-card[data-consumed="1"] { opacity: .6; }
.pg-inv-card[data-consumed="1"]:hover { transform: none; }

/* L'aperçu au dos de la carte : un carré qui montre le dessin réel plutôt qu'une icône. Acheter
 * une bannière sans l'avoir vue serait acheter un nom. */
.pg-inv-preview {
    height: 84px;
    border-radius: .5rem;
}

.pg-inv-preview-round {
    width: 84px;
    height: 84px;
}

@media (prefers-reduced-motion: reduce) {
    .pg-inv-card { transition: none; }
    .pg-inv-card:hover,
    .pg-inv-card:focus-within { transform: none; }
}

/* Vignette d'aperçu du back-office. Une taille fixe, que les utilitaires de largeur ne savent pas
 * exprimer en pixels — et l'écran de tarification doit montrer le dessin réel, pas un nom. */
.pg-art-thumb {
    width: 42px;
    height: 42px;
}

.pg-art-thumb-wide {
    height: 42px;
}

/* ── Le porté ───────────────────────────────────────────────────────────────
 * Ce que les artefacts équipés ajoutent aux pages qui dessinent un membre.
 *
 * L'anneau est un disque **derrière** l'avatar, légèrement plus grand : à travers la bordure blanche
 * de la photo, il se lit exactement comme un cadre. C'est ce qui permet de l'ajouter au profil sans
 * toucher au balisage de l'avatar lui-même, qui appartient à UserProfile. */
.pg-avatar-ring {
    position: absolute;
    inset: -6px;
    border-radius: 50%;
    background: var(--pg-frame, transparent);
    box-shadow: var(--pg-frame-glow, none);
    z-index: 0;
}

/* L'avatar doit passer au-dessus de l'anneau. Sans cela, un dégradé conique recouvrirait la photo. */
.pg-avatar-ring + img,
.pg-avatar-ring ~ img,
.pg-avatar-ring ~ div {
    position: relative;
    z-index: 1;
}

/* La bannière est une **bande en haut de la carte**, pas un fond derrière le texte. Un fond
 * intégral rendrait le pseudo illisible sur les dessins sombres — et sur une carte de profil, la
 * lisibilité passe avant la décoration. */
.pg-profile-banner {
    height: 110px;
    margin: -1rem -1rem 0;
}

/* Le médaillon de niveau, quand un artefact l'encadre. Taille fixe : les utilitaires de largeur ne
 * s'expriment pas en pixels, et le chiffre doit rester centré quel que soit son nombre de digits. */
.pg-level-medal {
    width: 3rem;
    height: 3rem;
}

@media (prefers-reduced-motion: reduce) {
    .pg-avatar-ring { box-shadow: none; }
}
