/**
 * XoXo Cards - Collection Effects CSS
 *
 * v5.9.0 [TACHE 128] Effets visuels appliques aux cartes possedees dans le
 * Front Office (Ma Collection, Mes Cartes, Albums, Echanges, popup detail).
 *
 * REGLE STRICTE D'APPLICATION (mirror Q11.iii) :
 *   La classe `.xoxo-card-fx` n'est ajoutee par le JS QUE SI la carte est
 *   possedee par le visiteur courant ET que `xoxoCards.collectionFx` definit
 *   un effet pour la rarete avec `type !== 'none'`. Les cartes non possedees
 *   (`xoxo-card-missing` ou wishlist) n'ont JAMAIS la classe fx.
 *
 * PRIORITE ABSOLUE : 0 REGRESSION sur les avatars et overlays existants.
 *   - z-index strict : tous les pseudo-elements d'effet en z-index <= 1
 *   - pointer-events: none systematique sur les pseudo-elements
 *   - Aucune modification de .xoxo-card-image / .xoxo-card-avatar-zone /
 *     .xoxo-card-quantity / .xoxo-card-exhausted-badge / .xoxo-card-lock-badge
 *   - Aucun reflow ou repositionnement des elements internes
 *   - Effets de bordure : box-shadow EXTERIEUR uniquement (pas inset, pas border)
 *   - Effets de surface : pseudo-elements en absolute INTERNES a .xoxo-card-image
 *     (overflow:hidden deja present sur cette zone)
 *
 * SCOPING STRICT : aucun selecteur ne croise avec
 * `.xoxo-anim-rendered .preview-card.fx-X` (CSS du builder/pack opening) ->
 * 0 collision possible avec les TACHES 113-126.
 *
 * @package XoXo_Cards
 * @since 5.9.0
 */

/* ============================================================================
 * RACINE FX : Variables CSS injectees inline par le JS (frontend.js)
 *   --fx-color         : couleur principale de l'effet (ex: #FFC0CB)
 *   --fx-intensity     : intensite en pixels pour box-shadow blur (ex: 20px)
 *   --fx-intensity-norm: intensite normalisee 0..1 (ex: 0.4) pour opacity/scale
 *
 * NB : `.xoxo-card-fx` est la classe-marker (ajoutee par le JS uniquement si
 * la carte est possedee et qu'un effet est configure). Les classes fx-X
 * exactes (fx-glow, fx-rainbow...) sont ajoutees en COMPAGNIE de fx-marker.
 *
 * Regle de specificite : tous les selecteurs sont prefixes par
 * `.xoxo-card-item.xoxo-card-fx.fx-X` pour neutraliser les regles legacy
 * `.xoxo-card-item` du theme SocialV ou autres.
 * ============================================================================ */

/* Position relative requise pour positionner les pseudo-elements INTERNES.
 * NB : .xoxo-card-item a deja position:relative implicite dans la collection
 * (cf cards.css), on confirme defensive. */
.xoxo-card-item.xoxo-card-fx {
    position: relative;
    /* Performance hint : prepare le compositor pour les effets animes.
     * `contain: layout style paint` reduit le coût des reflows globaux quand
     * 100+ cartes sont animees simultanement (Q3a effets permanents). */
    contain: layout style paint;
}

/* ============================================================================
 * 1. GLOW / Aura - effet de bordure exterieure douce (halo)
 *
 * v5.20.0 [TACHE 140 BUG-I + Q3=a] : SCOPE recadre sur `.xoxo-card-image`
 * uniquement (au lieu de `.xoxo-card-item` qui englobait carte entiere
 * = image + zone info blanche en dessous avec badge + titre). Le user a
 * explicitement demande que TOUS les effets de bordure tournent autour
 * de l'IMAGE seule pour une UX coherente avec les effets glissant deja
 * scopes image (shine, light_sweep, foil, aurora, twinkle).
 * ============================================================================ */
.xoxo-card-item.xoxo-card-fx.fx-glow .xoxo-card-image {
    box-shadow: 0 0 var(--fx-intensity, 20px) var(--fx-color, #fff);
    border-radius: inherit;
}

/* ============================================================================
 * 2. PULSE / Pulsation respirante
 *
 * v5.20.0 [TACHE 140 BUG-I + Q3=a] : SCOPE recadre sur `.xoxo-card-image`.
 * `.xoxo-card-item` conserve son `animation: fadeInUp 0.4s forwards`
 * defaut (cf cards.css L.287-289) -> opacite 0->1 preservee.
 * `.xoxo-card-image` recoit `xoxoCollPulse` (box-shadow + transform scale
 * limite a +1.2% pour ne pas decaler les overlays absolute internes a
 * l'image, ex: pastille avatar coin haut-droit). Multi-animation valide :
 * fadeInUp anime opacity sur le parent .xoxo-card-item, xoxoCollPulse
 * anime box-shadow+transform sur l'enfant .xoxo-card-image -> 0 collision.
 * ============================================================================ */
.xoxo-card-item.xoxo-card-fx.fx-pulse .xoxo-card-image {
    animation: xoxoCollPulse 1.8s ease-in-out infinite;
    will-change: box-shadow, transform;
    border-radius: inherit;
}
@keyframes xoxoCollPulse {
    0%, 100% {
        box-shadow: 0 0 calc(8px + 12px * var(--fx-intensity-norm, 0.5)) 0 var(--fx-color, #FF00AA);
        transform: scale(1);
    }
    50% {
        box-shadow: 0 0 calc(18px + 26px * var(--fx-intensity-norm, 0.5)) calc(2px + 4px * var(--fx-intensity-norm, 0.5)) var(--fx-color, #FF00AA);
        transform: scale(calc(1 + 0.012 * var(--fx-intensity-norm, 0.5)));
    }
}

/* ============================================================================
 * 3. ANIMATED_BORDER / Bordure animee
 *
 * v5.20.0 [TACHE 140 BUG-I + Q3=a] : SCOPE recadre sur `.xoxo-card-image`
 * uniquement (au lieu de `.xoxo-card-item` qui englobait carte + info).
 * outline ne participe pas au layout -> 0 decalage des elements internes
 * a l'image. fadeInUp reste sur le parent .xoxo-card-item via la regle
 * default (cards.css L.287-289).
 * ============================================================================ */
.xoxo-card-item.xoxo-card-fx.fx-animated_border .xoxo-card-image {
    outline: 3px solid var(--fx-color, #FFD700);
    outline-offset: 0;
    animation: xoxoCollBorder 1.5s ease infinite;
    will-change: box-shadow;
    border-radius: inherit;
}
@keyframes xoxoCollBorder {
    0%, 100% { box-shadow: 0 0 5px var(--fx-color, #FFD700); }
    50%      { box-shadow: 0 0 25px var(--fx-color, #FFD700); }
}

/* ============================================================================
 * 4. RAINBOW / Bordure conique tournante 7 couleurs
 *
 * v5.20.0 [TACHE 140 BUG-H + BUG-I + Q3=a] : SCOPE recadre sur
 * `.xoxo-card-image` (au lieu de `.xoxo-card-item`). Le pseudo-element
 * ::before passe en `inset:-3px` SUR L'IMAGE -> bordure rainbow conique
 * tournante autour de l'IMAGE seule + halo box-shadow exterieur sur
 * l'image. La regle default `.xoxo-card-item` conserve son fadeInUp
 * (cards.css L.287-289) pour gerer l'opacite 0->1 -> aucune regression
 * d'invisibilite (cf TACHE 130 BUG B1).
 *
 * BUG-H specifique : le user signalait que Rainbow ne tournait pas
 * autour de la carte. La cause RCA etait le scope sur `.xoxo-card-item`
 * qui rendait visuellement la rotation moins lisible (le contour s'etalait
 * sur la zone info blanche en dessous). En recentrant sur l'image seule,
 * la rotation est BIEN visible et perceptible (image carre/rectangle vs
 * carte allongee + info).
 * ============================================================================ */
@property --xoxo-coll-rainbow-angle {
    syntax: '<angle>';
    initial-value: 0deg;
    inherits: false;
}
.xoxo-card-item.xoxo-card-fx.fx-rainbow .xoxo-card-image {
    box-shadow:
        0 0 14px 1px rgba(255, 0, 200, 0.45),
        0 0 26px 4px rgba(0, 200, 255, 0.30);
    animation: xoxoCollRainbowAngle 3s linear infinite;
    isolation: isolate;
    border-radius: inherit;
    /* assure que le pseudo ::before puisse se positionner par rapport a image */
    position: relative;
}
.xoxo-card-item.xoxo-card-fx.fx-rainbow .xoxo-card-image::before {
    content: '';
    position: absolute;
    inset: -3px;
    border-radius: inherit;
    background: conic-gradient(from var(--xoxo-coll-rainbow-angle), red, orange, yellow, green, blue, indigo, violet, red);
    z-index: -1;
    pointer-events: none;
    -webkit-mask:
        linear-gradient(#000, #000) content-box,
        linear-gradient(#000, #000);
    -webkit-mask-composite: xor;
            mask-composite: exclude;
    padding: 3px;
    will-change: --xoxo-coll-rainbow-angle;
}
@keyframes xoxoCollRainbowAngle {
    to { --xoxo-coll-rainbow-angle: 360deg; }
}
@supports not (background: paint(angle)) {
    .xoxo-card-item.xoxo-card-fx.fx-rainbow .xoxo-card-image::before {
        animation: xoxoCollRainbowFallback 3s linear infinite;
    }
    @keyframes xoxoCollRainbowFallback {
        0%   { background: conic-gradient(from 0deg,   red, orange, yellow, green, blue, indigo, violet, red); }
        25%  { background: conic-gradient(from 90deg,  red, orange, yellow, green, blue, indigo, violet, red); }
        50%  { background: conic-gradient(from 180deg, red, orange, yellow, green, blue, indigo, violet, red); }
        75%  { background: conic-gradient(from 270deg, red, orange, yellow, green, blue, indigo, violet, red); }
        100% { background: conic-gradient(from 360deg, red, orange, yellow, green, blue, indigo, violet, red); }
    }
}

/* ============================================================================
 * 5. SHINE / Lueur diagonale balayage
 * Mirror : .preview-card.fx-shine .preview-card-image-zone::after.
 * Application : pseudo-element ::after sur .xoxo-card-image (overflow:hidden
 * deja present -> le balayage ne deborde pas).
 * z-index 1 : DERRIERE l'avatar (z-index 2+ habituel) + DERRIERE les badges
 * absolute (toujours au-dessus). pointer-events:none -> 0 blocage de click.
 * ============================================================================ */
.xoxo-card-item.xoxo-card-fx.fx-shine .xoxo-card-image::after {
    content: '';
    position: absolute;
    top: 0; left: -100%;
    width: 50%;
    height: 100%;
    background: linear-gradient(90deg, transparent 0%, rgba(255,255,255,0.6) 50%, transparent 100%);
    animation: xoxoCollShine 2.5s ease infinite;
    z-index: 1;
    pointer-events: none;
    will-change: left;
}
@keyframes xoxoCollShine { to { left: 150%; } }

/* ============================================================================
 * 6. LIGHT_SWEEP / Reflet diagonal
 * Mirror : .preview-card.fx-light_sweep ::after avec mix-blend-mode screen.
 * ============================================================================ */
.xoxo-card-item.xoxo-card-fx.fx-light_sweep .xoxo-card-image::after {
    content: '';
    position: absolute;
    top: 0; bottom: 0; left: -50%;
    width: 200%;
    background: linear-gradient(110deg, transparent 35%, var(--fx-color, #ffffff) 50%, transparent 65%);
    transform: translateX(-65%) skewX(-12deg);
    animation: xoxoCollLightSweep 3.5s ease-in-out infinite;
    pointer-events: none;
    mix-blend-mode: screen;
    opacity: var(--fx-intensity-norm, 0.6);
    z-index: 1;
    will-change: transform;
}
@keyframes xoxoCollLightSweep {
    0%, 25%   { transform: translateX(-65%) skewX(-12deg); }
    55%, 100% { transform: translateX(35%)  skewX(-12deg); }
}

/* ============================================================================
 * 7. FOIL / Feuille metallique conique
 * Mirror : .preview-card.fx-foil ::before avec mix-blend overlay + blur(2px).
 *
 * v5.10.0 [TACHE 130 BUG B3] : opacite plafonnee de 0.85 -> 0.65 pour eviter
 * l'effet "image lavee" sur fond clair (mix-blend overlay pousse l'image vers
 * le blanc quand la couleur foil est claire). 0.65 preserve le reflet sans
 * masquer l'image sous-jacente. 7 cas mesures (FORENSIC TEST).
 * ============================================================================ */
.xoxo-card-item.xoxo-card-fx.fx-foil .xoxo-card-image::before {
    content: '';
    position: absolute;
    inset: 0;
    background: conic-gradient(from 0deg at 50% 50%,
        transparent 0deg, var(--fx-color, #FFD700) 60deg, transparent 120deg,
        transparent 240deg, var(--fx-color, #FFD700) 300deg, transparent 360deg);
    animation: xoxoCollFoil 6s linear infinite;
    pointer-events: none;
    mix-blend-mode: overlay;
    opacity: calc(var(--fx-intensity-norm, 0.5) * 0.65);
    z-index: 1;
    filter: blur(2px);
    will-change: transform;
}
@keyframes xoxoCollFoil { to { transform: rotate(360deg); } }

/* ============================================================================
 * 8. AURORA / Voile aurore
 * Mirror : .preview-card.fx-aurora ::before avec radial gradients + soft-light.
 *
 * v5.10.0 [TACHE 130 BUG B5] : voile aurore retravaille pour preserver la
 * lisibilite de l'image sous-jacente.
 *   - inset -8% -> 0 : le pseudo ne deborde plus de la zone image (evite les
 *     fuites colorees au-dela des bords arrondis du cadre)
 *   - filter blur(10px)->blur(6px) + saturate(1.2)->saturate(1.1) : voile
 *     moins diffus, image plus lisible
 *   - opacity * 0.9 -> * 0.65 : reduction d'intensite pour preserver le
 *     contenu sous-jacent meme a intensity=100%
 * ============================================================================ */
.xoxo-card-item.xoxo-card-fx.fx-aurora .xoxo-card-image::before {
    content: '';
    position: absolute;
    inset: 0;
    background:
        radial-gradient(ellipse 80% 55% at 30% 30%, var(--fx-color, #00CED1) 0%, transparent 60%),
        radial-gradient(ellipse 60% 70% at 70% 60%, var(--fx-color, #00CED1) 0%, transparent 55%);
    animation: xoxoCollAurora 7s ease-in-out infinite alternate;
    pointer-events: none;
    mix-blend-mode: soft-light;
    opacity: calc(var(--fx-intensity-norm, 0.5) * 0.65);
    z-index: 1;
    filter: blur(6px) saturate(1.1);
    will-change: transform;
}
@keyframes xoxoCollAurora {
    0%   { transform: translate(-4%, -4%) rotate(0deg);   }
    50%  { transform: translate( 4%,  3%) rotate(2.5deg); }
    100% { transform: translate(-2%,  4%) rotate(-2deg);  }
}

/* ============================================================================
 * 9. TWINKLE / N etoiles dorees scintillantes desynchronisees
 * Mirror : .preview-card.fx-twinkle .preview-card-image-zone::before/after.
 *
 * v5.10.0 [TACHE 130 BUG B6] PARAMETRE DENSITE :
 * Le precedent fx-twinkle utilisait UNIQUEMENT 2 pseudo-elements ::before /
 * ::after avec position HARDCODED (top:18%/22% et top:65%/75%) sans aucune
 * possibilite de configurer le nombre d'etoiles. Le slider intensity ne
 * controlait que la TAILLE des etoiles (font-size).
 *
 * NOUVELLE APPROCHE :
 *   - Backend : ajoute un champ `density` dans la config
 *     xoxo_cards_collection_fx_config[twinkle].density = 3..16
 *   - Frontend.js (renderCard) : si type='twinkle', injecte N spans
 *     `<span class="xoxo-twinkle-star" style="--star-x:..%;--star-y:..%;
 *     --star-delay:..s;--star-dur:..s">` dans .xoxo-card-image (positions
 *     deterministes seedees sur card_id pour eviter le scintillement aleatoire
 *     lors du re-render)
 *   - CSS (ce bloc) : nouvelles regles pour `.xoxo-twinkle-star` avec
 *     l'animation xoxoCollTwinkleStar partagee.
 *   - Retro-compat : les pseudo-elements ::before / ::after sont conserves
 *     comme FALLBACK actif uniquement si .xoxo-card-image ne contient AUCUN
 *     enfant .xoxo-twinkle-star (legacy DOM avant migration).
 * ============================================================================ */

/* Conteneur du twinkle : prepare le compositor pour les N etoiles animees */
.xoxo-card-item.xoxo-card-fx.fx-twinkle .xoxo-card-image {
    position: relative;
    /* permet aux etoiles d'utiliser % en position (left/top relatifs au parent) */
}

/* Etoile twinkle dynamique (1 element par etoile, N=density) */
.xoxo-card-item.xoxo-card-fx.fx-twinkle .xoxo-twinkle-star {
    position: absolute;
    left: var(--star-x, 50%);
    top: var(--star-y, 50%);
    transform: translate(-50%, -50%) scale(0.3) rotate(0deg);
    color: var(--fx-color, #FFD700);
    font-size: calc(8px + 14px * var(--fx-intensity-norm, 0.5));
    line-height: 1;
    pointer-events: none;
    text-shadow: 0 0 6px var(--fx-color, #FFD700), 0 0 2px rgba(255,255,255,0.7);
    z-index: 1;
    opacity: 0;
    will-change: opacity, transform;
    animation: xoxoCollTwinkleStar var(--star-dur, 2.6s) ease-in-out infinite;
    animation-delay: var(--star-delay, 0s);
}
@keyframes xoxoCollTwinkleStar {
    0%, 80%, 100% { opacity: 0; transform: translate(-50%, -50%) scale(0.3) rotate(0deg);   }
    40%           { opacity: 1; transform: translate(-50%, -50%) scale(1.2) rotate(180deg); }
    60%           { opacity: 0.6; transform: translate(-50%, -50%) scale(0.9) rotate(270deg); }
}

/* Fallback legacy : 2 etoiles via pseudo-elements UNIQUEMENT si AUCUN enfant
 * .xoxo-twinkle-star n'a ete injecte par le JS (vue ancienne, AJAX legacy
 * avant migration density, etc.). On utilise :not() + :empty pour garantir
 * que le fallback ne fait pas double emploi avec les nouveaux spans. */
.xoxo-card-item.xoxo-card-fx.fx-twinkle .xoxo-card-image:not(:has(.xoxo-twinkle-star))::before,
.xoxo-card-item.xoxo-card-fx.fx-twinkle .xoxo-card-image:not(:has(.xoxo-twinkle-star))::after {
    content: '\2605';
    position: absolute;
    color: var(--fx-color, #FFD700);
    font-size: calc(8px + 14px * var(--fx-intensity-norm, 0.5));
    line-height: 1;
    pointer-events: none;
    text-shadow: 0 0 6px var(--fx-color, #FFD700), 0 0 2px rgba(255,255,255,0.7);
    z-index: 1;
    opacity: 0;
    will-change: opacity, transform;
}
.xoxo-card-item.xoxo-card-fx.fx-twinkle .xoxo-card-image:not(:has(.xoxo-twinkle-star))::before {
    top: 18%; left: 22%;
    animation: xoxoCollTwinkle1 2.4s ease-in-out infinite;
}
.xoxo-card-item.xoxo-card-fx.fx-twinkle .xoxo-card-image:not(:has(.xoxo-twinkle-star))::after {
    top: 65%; left: 75%;
    animation: xoxoCollTwinkle2 3.1s ease-in-out infinite 0.6s;
}
@keyframes xoxoCollTwinkle1 {
    0%, 80%, 100% { opacity: 0; transform: scale(0.3) rotate(0deg);   }
    40%           { opacity: 1; transform: scale(1.2) rotate(180deg); }
    60%           { opacity: 0.6; transform: scale(0.9) rotate(270deg); }
}
@keyframes xoxoCollTwinkle2 {
    0%, 75%, 100% { opacity: 0; transform: scale(0.3) rotate(0deg);    }
    35%           { opacity: 1; transform: scale(1.3) rotate(-180deg); }
    55%           { opacity: 0.5; transform: scale(0.8) rotate(-260deg); }
}

/* ============================================================================
 * 10. HOLOGRAPHIC / 5 couleurs en gradient anime
 * Mirror : .preview-card.fx-holographic .preview-card-image-zone::before
 * avec mix-blend overlay sur image existante.
 *
 * v5.10.0 [TACHE 130 BUG B4] : opacite plafonnee a 0.55 (au lieu de 1.0
 * brut quand intensity=100%). Le mix-blend overlay avec 5 couleurs vives
 * masquait l'image sous-jacente a haut intensity. 0.55 preserve l'effet
 * holographique tout en gardant l'image lisible.
 * ============================================================================ */
.xoxo-card-item.xoxo-card-fx.fx-holographic .xoxo-card-image::before {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(135deg, #ff006e 0%, #8338ec 25%, #3a86ff 50%, #06ffa5 75%, #ffbe0b 100%);
    background-size: 300% 300%;
    animation: xoxoCollHolo 4s ease infinite;
    mix-blend-mode: overlay;
    pointer-events: none;
    z-index: 1;
    opacity: calc(var(--fx-intensity-norm, 0.7) * 0.55);
    will-change: background-position;
}
@keyframes xoxoCollHolo {
    0%, 100% { background-position: 0% 50%; }
    50%      { background-position: 100% 50%; }
}

/* ============================================================================
 * POPUP DETAIL CARTE - Application des effets dans la modale .xoxo-detail-image
 *
 * Q11 = (c) Adaptatif mirror BO :
 *   - Effets de bordure (glow, pulse, rainbow, animated_border) -> sur le
 *     wrapper de carte detail entiere (.xoxo-detail-card-wrapper)
 *   - Effets de surface (shine, light_sweep, foil, twinkle, aurora,
 *     holographic) -> sur le meme wrapper (qui contient l'image grand format)
 *
 * Q11.iii : effet UNIQUEMENT si la carte est possedee par le visiteur courant
 * (regle ajoutee par card-detail.js avant ouverture).
 *
 * Selecteurs cibles CONFIRMES par lecture forensique de card-detail.js :
 *   - `.xoxo-detail-image` : colonne gauche complete (badge type + owned + image)
 *   - `.xoxo-detail-card-wrapper` : wrapper INTERNE qui contient l'image grand
 *     format + l'avatar pastille. C'est ICI qu'on applique les effets pour
 *     que l'effet entoure l'image sans deborder sur les badges externes
 *     (badge type bas + owned/missing badge).
 *
 * NB : ce wrapper a deja overflow:hidden (defensive border-radius des cards
 * detail) -> les pseudo-elements de surface ne debordent pas.
 * ============================================================================ */

/* Marker generic sur le wrapper image de la modale */
.xoxo-detail-card-wrapper.xoxo-card-fx {
    position: relative;
    isolation: isolate;
    /* defensive : confine les pseudo-elements de surface */
    overflow: hidden;
}

/* Effets de bordure sur le wrapper de la modale detail */
/* v5.23.3 [TACHE 4] BUG-T4 fix : overflow:visible !important pour FORCER l'override
 * du `.xoxo-detail-card-wrapper { overflow:hidden }` (card-detail.css L.92, qui charge
 * APRES collection-effects.css selon class-xoxo-cards-public.php L.381 / L.422). Sans
 * !important, le cascade pouvait masquer le halo box-shadow colore (--fx-color, ex.
 * #FF00AA pour Sexy avec Glow) -> rendu visuel d'un "fond solide" au lieu d'une aura
 * douce qui deborde du wrapper. card-detail.css v5.23.3 a ete restructure pour scoper
 * son box-shadow et overflow:hidden via :not(.xoxo-card-fx), mais on conserve cette
 * defense !important pour garantir 100% de robustesse face aux themes tiers ou aux
 * styles inline injectes par BuddyPress. Effet symetrique applique aux 4 effets de
 * bordure : fx-glow, fx-pulse, fx-animated_border, fx-rainbow. */
.xoxo-detail-card-wrapper.xoxo-card-fx.fx-glow {
    box-shadow: 0 0 var(--fx-intensity, 30px) var(--fx-color, #fff);
    overflow: visible !important; /* le halo doit pouvoir deborder du wrapper */
    background: transparent !important; /* defense anti-theme : pas de fond opaque */
}
.xoxo-detail-card-wrapper.xoxo-card-fx.fx-pulse {
    animation: xoxoCollPulse 1.8s ease-in-out infinite;
    overflow: visible !important; /* le halo doit pouvoir deborder du wrapper */
    background: transparent !important; /* defense anti-theme */
}
.xoxo-detail-card-wrapper.xoxo-card-fx.fx-animated_border {
    outline: 3px solid var(--fx-color, #FFD700);
    outline-offset: 0;
    animation: xoxoCollBorder 1.5s ease infinite;
    overflow: visible !important;
    background: transparent !important; /* defense anti-theme */
}
.xoxo-detail-card-wrapper.xoxo-card-fx.fx-rainbow {
    box-shadow:
        0 0 14px 1px rgba(255, 0, 200, 0.45),
        0 0 26px 4px rgba(0, 200, 255, 0.30);
    animation: xoxoCollRainbowAngle 3s linear infinite;
    overflow: visible !important;
    background: transparent !important; /* defense anti-theme */
}
.xoxo-detail-card-wrapper.xoxo-card-fx.fx-rainbow::before {
    content: '';
    position: absolute;
    inset: -3px;
    border-radius: inherit;
    background: conic-gradient(from var(--xoxo-coll-rainbow-angle), red, orange, yellow, green, blue, indigo, violet, red);
    z-index: -1;
    pointer-events: none;
    -webkit-mask:
        linear-gradient(#000, #000) content-box,
        linear-gradient(#000, #000);
    -webkit-mask-composite: xor;
            mask-composite: exclude;
    padding: 3px;
    will-change: --xoxo-coll-rainbow-angle;
}

/* Effets de surface sur la zone image de la modale (les pseudo-elements
 * sont enfants direct du wrapper qui contient deja img + avatar) */
.xoxo-detail-card-wrapper.xoxo-card-fx.fx-shine::after {
    content: '';
    position: absolute;
    top: 0; left: -100%;
    width: 50%; height: 100%;
    background: linear-gradient(90deg, transparent 0%, rgba(255,255,255,0.6) 50%, transparent 100%);
    animation: xoxoCollShine 2.5s ease infinite;
    z-index: 1;
    pointer-events: none;
}
.xoxo-detail-card-wrapper.xoxo-card-fx.fx-light_sweep::after {
    content: '';
    position: absolute;
    top: 0; bottom: 0; left: -50%;
    width: 200%;
    background: linear-gradient(110deg, transparent 35%, var(--fx-color, #ffffff) 50%, transparent 65%);
    transform: translateX(-65%) skewX(-12deg);
    animation: xoxoCollLightSweep 3.5s ease-in-out infinite;
    pointer-events: none;
    mix-blend-mode: screen;
    opacity: var(--fx-intensity-norm, 0.6);
    z-index: 1;
}
.xoxo-detail-card-wrapper.xoxo-card-fx.fx-foil::before {
    content: '';
    position: absolute;
    inset: 0;
    background: conic-gradient(from 0deg at 50% 50%,
        transparent 0deg, var(--fx-color, #FFD700) 60deg, transparent 120deg,
        transparent 240deg, var(--fx-color, #FFD700) 300deg, transparent 360deg);
    animation: xoxoCollFoil 6s linear infinite;
    pointer-events: none;
    mix-blend-mode: overlay;
    /* v5.10.0 [TACHE 130 BUG B3] : opacite 0.85 -> 0.65 pour preserver l'image */
    opacity: calc(var(--fx-intensity-norm, 0.5) * 0.65);
    z-index: 1;
    filter: blur(2px);
}
.xoxo-detail-card-wrapper.xoxo-card-fx.fx-aurora::before {
    content: '';
    position: absolute;
    /* v5.10.0 [TACHE 130 BUG B5] : inset -8% -> 0 (pas de fuite hors cadre) */
    inset: 0;
    background:
        radial-gradient(ellipse 80% 55% at 30% 30%, var(--fx-color, #00CED1) 0%, transparent 60%),
        radial-gradient(ellipse 60% 70% at 70% 60%, var(--fx-color, #00CED1) 0%, transparent 55%);
    animation: xoxoCollAurora 7s ease-in-out infinite alternate;
    pointer-events: none;
    mix-blend-mode: soft-light;
    /* v5.10.0 [TACHE 130 BUG B5] : opacite 0.9 -> 0.65 + blur 10px -> 6px + saturate 1.2 -> 1.1 */
    opacity: calc(var(--fx-intensity-norm, 0.5) * 0.65);
    z-index: 1;
    filter: blur(6px) saturate(1.1);
}
.xoxo-detail-card-wrapper.xoxo-card-fx.fx-twinkle:not(:has(.xoxo-twinkle-star))::before,
.xoxo-detail-card-wrapper.xoxo-card-fx.fx-twinkle:not(:has(.xoxo-twinkle-star))::after {
    /* v5.10.0 [TACHE 130 BUG B6] Fallback legacy 2 etoiles via pseudo-elements
     * UNIQUEMENT si AUCUN enfant .xoxo-twinkle-star n'est present (markup
     * pre-migration density). Quand le JS injecte des spans, le selecteur
     * :not(:has) desactive automatiquement ces pseudo. */
    content: '\2605';
    position: absolute;
    color: var(--fx-color, #FFD700);
    font-size: calc(16px + 24px * var(--fx-intensity-norm, 0.5));
    line-height: 1;
    pointer-events: none;
    text-shadow: 0 0 10px var(--fx-color, #FFD700), 0 0 4px rgba(255,255,255,0.7);
    z-index: 1;
    opacity: 0;
}
.xoxo-detail-card-wrapper.xoxo-card-fx.fx-twinkle:not(:has(.xoxo-twinkle-star))::before {
    top: 18%; left: 22%;
    animation: xoxoCollTwinkle1 2.4s ease-in-out infinite;
}
.xoxo-detail-card-wrapper.xoxo-card-fx.fx-twinkle:not(:has(.xoxo-twinkle-star))::after {
    top: 65%; left: 75%;
    animation: xoxoCollTwinkle2 3.1s ease-in-out infinite 0.6s;
}
/* v5.10.0 [TACHE 130 BUG B6] Etoiles twinkle dynamiques (N=density) dans la
 * popup detail, mirror exact de la grille collection. Tailles legerement plus
 * grandes (16-40px vs 8-22px en grille) pour rester proportionnees au format
 * popup expanded. */
.xoxo-detail-card-wrapper.xoxo-card-fx.fx-twinkle .xoxo-twinkle-star {
    position: absolute;
    left: var(--star-x, 50%);
    top: var(--star-y, 50%);
    transform: translate(-50%, -50%) scale(0.3) rotate(0deg);
    color: var(--fx-color, #FFD700);
    font-size: calc(16px + 24px * var(--fx-intensity-norm, 0.5));
    line-height: 1;
    pointer-events: none;
    text-shadow: 0 0 10px var(--fx-color, #FFD700), 0 0 4px rgba(255,255,255,0.7);
    z-index: 1;
    opacity: 0;
    will-change: opacity, transform;
    animation: xoxoCollTwinkleStar var(--star-dur, 2.6s) ease-in-out infinite;
    animation-delay: var(--star-delay, 0s);
}
.xoxo-detail-card-wrapper.xoxo-card-fx.fx-holographic::before {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(135deg, #ff006e 0%, #8338ec 25%, #3a86ff 50%, #06ffa5 75%, #ffbe0b 100%);
    background-size: 300% 300%;
    animation: xoxoCollHolo 4s ease infinite;
    mix-blend-mode: overlay;
    pointer-events: none;
    z-index: 1;
    /* v5.10.0 [TACHE 130 BUG B4] : opacite plafonnee a 0.55 (au lieu de 1.0 brut) */
    opacity: calc(var(--fx-intensity-norm, 0.7) * 0.55);
}

/* ============================================================================
 * GUARD : si la carte porte ces classes, on N'APPLIQUE PAS l'effet meme si
 * `.xoxo-card-fx` est present (defensive en cas de mismatch JS/CSS).
 * Cette regle est purement DEFENSIVE - le JS frontend.js est cense ne pas
 * ajouter `.xoxo-card-fx` sur ces cartes en premier lieu.
 *
 * v5.10.0 [TACHE 130 BUG B2] CRITICAL FIX :
 * La regle precedente declarait `opacity: 0 !important` sur le SELECTEUR PARENT
 * `.xoxo-card-item.xoxo-card-missing.xoxo-card-fx`. Si une carte non-possedee
 * recevait erronement la classe `xoxo-card-fx` (regression future, AJAX
 * malformed, custom code), elle devenait ENTIEREMENT INVISIBLE (cadre + badge
 * + titre + image), faisant croire a une "carte fantome" dans la grille.
 *
 * FIX : on retire la cible `.xoxo-card-item.xoxo-card-missing.xoxo-card-fx`
 * elle-meme. On reset UNIQUEMENT les pseudo-elements (::before, ::after, et
 * les pseudo de .xoxo-card-image) pour neutraliser visuellement l'effet.
 * La carte parente garde son opacity normale (0.85 cf cards.css L.394 pour
 * .xoxo-card-missing) et reste visible avec son contenu.
 * ============================================================================ */
.xoxo-card-item.xoxo-card-missing.xoxo-card-fx::before,
.xoxo-card-item.xoxo-card-missing.xoxo-card-fx::after,
.xoxo-card-item.xoxo-card-missing.xoxo-card-fx .xoxo-card-image::before,
.xoxo-card-item.xoxo-card-missing.xoxo-card-fx .xoxo-card-image::after,
.xoxo-card-item.xoxo-card-missing.xoxo-card-fx .xoxo-twinkle-star {
    /* Reset visuel des pseudo-elements et etoiles twinkle dynamiques.
     * On NE TOUCHE PAS au selecteur parent, ce qui preserve la visibilite
     * de la carte (cadre, badge, titre, image grayscale). */
    animation: none !important;
    background: none !important;
    content: none !important;
    opacity: 0 !important;
}
.xoxo-card-item.xoxo-card-missing.xoxo-card-fx {
    /* Neutralisation des effets sur le selecteur parent uniquement (box-shadow
     * et outline, pas opacity/animation pour laisser fadeInUp et l'opacity
     * .xoxo-card-missing standard fonctionner). */
    box-shadow: none !important;
    outline: none !important;
}
