.card-grid {
    /* flex plutôt que CSS grid : une grille classique laisse les cases vides de la
       dernière ligne alignées à gauche (les pistes de colonnes existent même sans
       contenu) — avec flex-wrap, chaque ligne (y compris la dernière, incomplète) est
       centrée indépendamment, sans configuration supplémentaire */
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 1.75rem;
    padding: 2.5rem 0 1rem;
}

.card-reveal {
    animation: card-reveal 0.5s ease-out backwards;
    animation-delay: calc(var(--i) * 0.15s);
}

@keyframes card-reveal {
    from {
        opacity: 0;
        transform: translateY(30px) scale(0.8);
    }
    to {
        opacity: 1;
        transform: translateY(0) scale(1);
    }
}

.card {
    --card-color: #ffffff;

    width: 170px;
    cursor: pointer;
    transition: transform 0.15s ease-out, filter 0.15s ease-out;
    transform-style: preserve-3d;
    will-change: transform;
    /* colonne flex + .card__sacrifice-form en margin-top:auto (plus bas) : sur une même
       ligne de .card-grid, une carte avec un badge de variante (une ligne de texte en plus)
       ne doit pas décaler SON bloc "Sacrifier" vers le bas par rapport aux autres cartes de
       la ligne — .card-grid (flex, align-items: stretch par défaut) étire déjà chaque carte
       à la hauteur de la plus grande de sa ligne, il ne restait qu'à répartir cet espace en
       bas plutôt qu'en le laissant vide sous un contenu resté aligné en haut. */
    display: flex;
    flex-direction: column;
}

.card--large {
    width: 320px;
}

.card--missing {
    cursor: default;
    opacity: 0.55;
}

.card--missing .card__image {
    filter: grayscale(1) brightness(0.55) !important;
}

.card__image-wrap {
    position: relative;
}

.card__new-badge {
    /* position absolue (ancrée sur .card__image-wrap, déjà en position: relative) : sinon
       ce badge s'insère dans le flux normal et décale l'image vers le bas, désalignant les
       cartes "nouvelles" par rapport aux autres dans la même grille */
    position: absolute;
    top: -12px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 1;
    width: fit-content;
    white-space: nowrap;
    padding: 0.25rem 0.8rem;
    border-radius: 999px;
    background: linear-gradient(90deg, #ffd23f, #ff8c00);
    color: #10131a;
    font-size: 0.7rem;
    font-weight: 800;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    box-shadow: 0 0 12px rgba(255, 180, 0, 0.7);
    /* synchronisé avec l'apparition différée de la carte (.card-reveal) : sinon, sur une
       grille de plusieurs cartes, ce badge apparaît dès le chargement de la page, avant
       même que la carte (encore masquée par son propre délai) ne soit visible */
    animation: new-badge-pop 0.4s ease-out backwards;
    animation-delay: calc(var(--i, 0) * 0.15s);
}

@keyframes new-badge-pop {
    from {
        opacity: 0;
        transform: translateX(-50%) translateY(6px) scale(0.7);
    }
    to {
        opacity: 1;
        transform: translateX(-50%) translateY(0) scale(1);
    }
}

/* petite gerbe d'étincelles à la couleur de la rareté (--card-color, héritée de .card),
   jouée une seule fois à l'apparition de la carte — pas un effet continu comme les
   variantes (rainbow/stars), juste un "pop" pour célébrer la découverte */
.card__new-spark-burst {
    position: absolute;
    inset: 0;
    overflow: visible;
    pointer-events: none;
    z-index: 2;
}

.card__spark {
    position: absolute;
    top: 50%;
    left: 50%;
    width: 4px;
    height: 26px;
    margin: -13px -2px;
    border-radius: 999px;
    background: var(--card-color);
    box-shadow: 0 0 8px var(--card-color), 0 0 16px var(--card-color);
    transform: rotate(var(--spark-angle)) translateY(0) scaleY(0.2);
    opacity: 0;
    animation: new-spark-burst 1s ease-out backwards;
    /* attend que la carte ait fini son propre effet d'apparition (délai .card-reveal
       + sa durée de 0.5s) avant de se déclencher, plus le décalage propre à chaque étincelle */
    animation-delay: calc(var(--i, 0) * 0.15s + 0.5s + var(--spark-delay));
}

@keyframes new-spark-burst {
    0% {
        transform: rotate(var(--spark-angle)) translateY(0) scaleY(0.2);
        opacity: 1;
    }
    60% {
        transform: rotate(var(--spark-angle)) translateY(-90px) scaleY(1);
        opacity: 1;
    }
    100% {
        transform: rotate(var(--spark-angle)) translateY(-140px) scaleY(0.3);
        opacity: 0;
    }
}

.card__quantity-badge {
    position: absolute;
    top: -8px;
    right: -8px;
    z-index: 1;
    padding: 0.2rem 0.55rem;
    border-radius: 999px;
    background: #d6006e;
    color: #fff;
    font-size: 0.75rem;
    font-weight: 700;
}

.card__image {
    display: block;
    width: 100%;
    aspect-ratio: 3 / 4;
    object-fit: contain;
    border-radius: 10px;
    filter:
        drop-shadow(0 0 8px var(--card-color))
        drop-shadow(0 0 18px var(--card-color));
}

/* effet holo : dégradé arc-en-ciel, masqué par la transparence du PNG,
   déplacé par card_tilt_controller.js selon la position de la souris. Paramétrable par
   variante via --holo-hue-rotate/--holo-saturation (Variant.effectHueRotateDeg/
   effectSaturationPercent) : décale/module le dégradé fixe plutôt que de le remplacer par
   des couleurs éditables une à une, pour garder un rendu harmonieux. Défauts (0deg, 1)
   = rendu inchangé. */
.card__holo {
    position: absolute;
    inset: 0;
    pointer-events: none;
    background: linear-gradient(
        115deg,
        transparent 0%,
        #ff008c 12%,
        #ffee00 26%,
        #00ff88 40%,
        #00d4ff 54%,
        #a200ff 68%,
        transparent 82%
    );
    background-size: 250% 250%;
    background-position: var(--holo-x, 50%) var(--holo-y, 50%);
    filter: hue-rotate(var(--holo-hue-rotate, 0deg)) saturate(var(--holo-saturation, 1));
    mix-blend-mode: color-dodge;
    opacity: 0.7;
    -webkit-mask-image: var(--card-image);
    mask-image: var(--card-image);
    /* voir le commentaire sur .card__sparkle-mask : sans ça, le dégradé ne s'affiche que
       sur les zones claires d'une carte sans vraie transparence (fond sombre = masqué). */
    -webkit-mask-mode: alpha;
    mask-mode: alpha;
    -webkit-mask-size: contain;
    mask-size: contain;
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-position: center;
    mask-position: center;
    animation: holo-idle 5s ease-in-out infinite;
}

/* pendant l'interaction souris, le JS pilote --holo-x/--holo-y : on coupe l'animation au repos */
.card--holo-active .card__holo {
    animation: none;
}

@keyframes holo-idle {
    0%, 100% {
        background-position: 20% 30%;
    }
    50% {
        background-position: 80% 70%;
    }
}

/* calque étoiles/scintillement : masque fixe (silhouette) + calque intérieur qui tourne
   librement à l'intérieur, sans déformer le contour de la carte. "mask-mode: alpha" est
   essentiel : par défaut un masque-image utilise la LUMINANCE (clair/sombre), pas la
   transparence — sur une carte sans vrai canal alpha (fond uni sombre, cas fréquent), les
   étoiles ne s'affichaient alors que sur les zones claires du dessin (cheveux, cadre doré...)
   et disparaissaient sur les zones sombres, au lieu de couvrir toute la carte (repéré en prod
   le 2026-09-19 — pris pour un bug de répartition à tort, avant d'identifier la vraie cause). */
.card__sparkle-mask {
    position: absolute;
    inset: 0;
    overflow: hidden;
    pointer-events: none;
    -webkit-mask-image: var(--card-image);
    mask-image: var(--card-image);
    -webkit-mask-mode: alpha;
    mask-mode: alpha;
    -webkit-mask-size: contain;
    mask-size: contain;
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-position: center;
    mask-position: center;
}

.card__star {
    position: absolute;
    top: var(--star-top);
    left: var(--star-left);
    width: var(--star-size);
    height: var(--star-size);
    background-image: var(--star-image);
    background-size: contain;
    background-repeat: no-repeat;
    background-position: center;
    opacity: var(--star-opacity, 1);
    /* 2 drop-shadow au lieu de 3 (chacun = une passe de flou complète, coûteuse à répéter sur
       15-40 éléments animés en continu — repéré comme un facteur de lenteur en prod le
       2026-09-19) — rendu quasi identique visuellement, moins cher à calculer. */
    filter:
        drop-shadow(0 0 4px var(--star-glow-color, #fff))
        drop-shadow(0 0 10px var(--star-glow-color, #fff))
        brightness(1.8)
        contrast(1.3)
        saturate(2);
    animation: star-drift var(--star-duration) ease-in-out infinite;
    animation-delay: var(--star-delay);
}

/* halo lumineux derrière l'étoile : un dégradé radial se voit bien plus qu'un simple contour
   fin (le dessin d'origine est un trait très fin, peu visible une fois réduit à sa taille
   normale) — et un dégradé se peint directement, sans passe de flou comme un drop-shadow
   supplémentaire l'exigerait, donc moins cher malgré le halo plus marqué. S'applique aussi
   à .card__star--tinted (même sélecteur de base, la classe s'ajoute, ne le remplace pas). */
.card__star::before {
    content: '';
    position: absolute;
    inset: -70%;
    border-radius: 50%;
    background: radial-gradient(circle, var(--star-glow-color, #fff) 0%, transparent 70%);
    opacity: var(--star-glow-intensity, 0.85);
    mix-blend-mode: screen;
    pointer-events: none;
}

/* quand une "couleur du dessin" est configurée sur la variante : l'image sert de masque
   (sa forme, peu importe sa couleur d'origine) et --star-image-color la remplit — sinon
   l'image s'affiche telle quelle via background-image ci-dessus */
.card__star--tinted {
    background-image: none;
    background-color: var(--star-image-color);
    -webkit-mask-image: var(--star-image);
    mask-image: var(--star-image);
    -webkit-mask-size: contain;
    mask-size: contain;
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-position: center;
    mask-position: center;
}

/* effet "reverse holo" : étincelles à positions FIXES (voir AppExtension::angleSparklePositions),
   éparpillées partout + rotation aléatoire chacune (rendu organique, pas un motif géométrique),
   masquées par un dégradé radial qui suit --holo-x/--holo-y (déjà calculées par
   card_tilt_controller.js pour l'effet holographique — réutilisées ici tel quel) : seules
   les étincelles proches de "l'angle courant" de la souris deviennent visibles, avec un halo
   néon prononcé pour un effet spectaculaire au mouvement. */
.card__angle-sparkle-layer {
    position: absolute;
    inset: 0;
    overflow: hidden;
    pointer-events: none;
    -webkit-mask-image: radial-gradient(circle at var(--holo-x, 50%) var(--holo-y, 50%), black 0%, black 22%, transparent 60%);
    mask-image: radial-gradient(circle at var(--holo-x, 50%) var(--holo-y, 50%), black 0%, black 22%, transparent 60%);
}

/* au moment où la carte apparaît (isNew), on élargit brièvement le masque pour montrer
   TOUTES les étincelles une fois — histoire que le joueur comprenne qu'il y a un effet à
   découvrir en bougeant la souris, avant de revenir au comportement normal (spot étroit) */
.card__angle-sparkle-layer--hint {
    animation: angle-sparkle-hint 1.6s ease-in-out 1;
}

@keyframes angle-sparkle-hint {
    0% {
        -webkit-mask-size: 100% 100%;
        mask-size: 100% 100%;
    }
    35%, 55% {
        -webkit-mask-size: 300% 300%;
        mask-size: 300% 300%;
    }
    100% {
        -webkit-mask-size: 100% 100%;
        mask-size: 100% 100%;
    }
}

.card__angle-sparkle {
    position: absolute;
    top: var(--sparkle-top);
    left: var(--sparkle-left);
    width: var(--sparkle-size);
    height: var(--sparkle-size);
    transform: translate(-50%, -50%) rotate(var(--sparkle-rotation, 0deg));
    background-image: var(--sparkle-image);
    background-size: contain;
    background-repeat: no-repeat;
    background-position: center;
    opacity: var(--sparkle-opacity, 1);
    filter:
        drop-shadow(0 0 4px var(--sparkle-glow-color, #fff))
        drop-shadow(0 0 9px var(--sparkle-glow-color, #fff))
        drop-shadow(0 0 18px var(--sparkle-glow-color, #fff))
        drop-shadow(0 0 32px var(--sparkle-glow-color, #fff))
        brightness(2.2)
        saturate(2.2);
}

/* même technique de recoloration que .card__star--tinted */
.card__angle-sparkle--tinted {
    background-image: none;
    background-color: var(--sparkle-image-color);
    -webkit-mask-image: var(--sparkle-image);
    mask-image: var(--sparkle-image);
    -webkit-mask-size: contain;
    mask-size: contain;
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-position: center;
    mask-position: center;
}

@keyframes star-drift {
    0%, 100% {
        transform: translate(0, 0) rotate(0deg);
    }
    50% {
        transform: translate(var(--star-dx), var(--star-dy)) rotate(180deg);
    }
}

.card__info {
    margin-top: 0.6rem;
    text-align: center;
    color: #f2f2f2;
}

.card__name {
    margin: 0;
    font-weight: 600;
    font-size: 0.95rem;
}

.card__rarity-label {
    display: block;
    margin-top: 0.2rem;
    font-size: 0.75rem;
    font-weight: 700;
    letter-spacing: 0.05em;
    color: var(--card-color);
}

.card__variant-badge {
    display: inline-block;
    margin-top: 0.3rem;
    padding: 0.15rem 0.6rem;
    border-radius: 999px;
    font-size: 0.65rem;
    font-weight: 700;
    letter-spacing: 0.05em;
    color: #10131a;
    background: var(--variant-color, #ffffff);
}

.card--rare {
    --card-color: #4aa8ff;
}

.card--epique {
    --card-color: #b04aff;
}

.card--legendaire {
    --card-color: #ffd23f;
}

.card--legendaire .card__image {
    animation: legendary-glow 2s ease-in-out infinite;
}

@keyframes legendary-glow {
    0%, 100% {
        filter:
            drop-shadow(0 0 8px var(--card-color))
            drop-shadow(0 0 18px var(--card-color));
    }
    50% {
        filter:
            drop-shadow(0 0 14px var(--card-color))
            drop-shadow(0 0 30px var(--card-color));
    }
}

.card-dialog {
    border: none;
    border-radius: 16px;
    padding: 2rem;
    background: transparent;
    /* le <dialog> a "overflow: auto" par défaut dans les navigateurs : l'inclinaison 3D de
       la carte (card_tilt_controller.js, rotateX/rotateY + scale) dépasse transitoirement sa
       boîte à certains angles, ce qui faisait apparaître une scrollbar parasite. */
    overflow: hidden;
}

.card-dialog::backdrop {
    background: rgba(0, 0, 0, 0.65);
    backdrop-filter: blur(4px);
}

.card-dialog__close {
    display: block;
    margin: 0 0 1rem auto;
    background: none;
    border: none;
    color: #f2f2f2;
    font-size: 1.5rem;
    cursor: pointer;
    line-height: 1;
}

.card__sacrifice-form {
    /* auto = collé au bas de .card (voir son commentaire) plutôt qu'un espacement fixe */
    margin-top: auto;
    padding-top: 0.5rem;
    text-align: center;
    font-size: 0.7rem;
    color: #f2f2f2;
}

.card__sacrifice-amount {
    width: 2.5rem;
    text-align: center;
    border-radius: 6px;
    border: 1px solid rgba(255, 255, 255, 0.3);
    background: rgba(0, 0, 0, 0.3);
    color: #fff;
}

.card__sacrifice-btn {
    padding: 0.3rem 0.7rem;
    border-radius: 999px;
    border: none;
    background: #d6006e;
    color: #fff;
    font-size: 0.7rem;
    font-weight: 700;
    cursor: pointer;
}

.card__sacrifice-btn:hover {
    background: #a90057;
}
