/* ═══════════════════════════════════════════════════════════════════════════
   GIFT SHOP — LA VITRINE                                    05.09.2026
   ───────────────────────────────────────────────────────────────────────────
   Phil : « ce ne sont que des emojis débiles, j'en ai caché parce que c'est
   moche — il faut une vraie animation par article, indexée sur la dépense :
   le moins cher a moins d'effet. Modeste pour l'instant, je n'ai pas encore
   les objets à 1 000 💎. » Puis : « on classe les articles comme les cartes —
   common uncommon rare super rare epic legendary enchanted iconic. »

   CE QUI FAIT L'ÉCHELLE — DE LA MACHINERIE, PAS DU VOLUME
   ──────────────────────────────────────────────────────
   Un palier ne monte pas « la même lueur en plus fort » : il AJOUTE une pièce.
   L'œil apprend une lueur en deux secondes ; il n'apprend pas une pièce qui
   n'était pas là.
     palier 0  common      le sigle, et un souffle (respiration lente)
     palier 1  uncommon    + la LUEUR DE SOL — l'objet est posé quelque part
     palier 2  rare        + le BALAYAGE conique derrière le sigle
     palier 3  super rare  + l'ANNEAU contra-rotatif, arête chromatique,
                             et le CHAMP DE POUSSIÈRE (ORBITE · Poussière) à
                             80 % de densité — descendu du palier 4 le 06.09
                             quand l'échelle de prix a ramené tout le
                             catalogue sous epic (voir shop_sigils.php)
     palier 4  epic        + poussière à pleine densité
     palier 5  legendary   + poussière plus dense encore, éclat porté
     palier 6  enchanted   + les RUNES en arc, teinte spectrale qui dérive
     palier 7  iconic      + tout, à pleine amplitude
   Les paliers 6 et 7 n'ont aucun article aujourd'hui. Ils sont écrits quand
   même : le jour où un article à 1 000 💎 arrive, il n'y a rien à coder.

   ⚠ LE ROBINET EST `--fx-master`, ET IL EST BAS (.55).
   C'est la réponse littérale à « ne pousse pas trop pour l'instant ». Tout ce
   qui est visible passe par lui. Le monter à 1 rend la vitrine spectaculaire
   sans toucher à une seule règle — c'est le geste à faire quand la boutique
   aura sa gamme haute, pas avant.

   ⚠ CE QUI N'EST PAS TOUCHÉ : les cadres d'avatar (frames) et les badges de
   rareté. Phil : « eux sont déjà bien. » Ils reçoivent l'alcôve et l'échelle
   d'ambiance, leur ART reste celui d'avant.

   ORBITE : le champ de particules est `assets/js/orbite-poussiere.js`, copie
   conforme de /Users/phil/Desktop/ORBITE/poussiere.js. Toute correction de
   loi se fait dans ORBITE puis se recopie — jamais l'inverse.
   ═══════════════════════════════════════════════════════════════════════════ */

/* Le robinet doit être DÉCLARÉ pour pouvoir être animé : une custom property
   non enregistrée n'a pas de type, donc pas d'interpolation — le survol
   sauterait d'un cran au lieu de monter la lumière. */
@property --fx-master {
    syntax: '<number>';
    inherits: true;
    initial-value: 0.55;
}

.shop-page {
    /* Le robinet unique. Modeste tant que la gamme haute n'existe pas. */
    --fx-master: .55;
}

/* ── L'ÉCHELLE ────────────────────────────────────────────────────────────
   `--fx-t` est le palier (lu aussi par shop-fx.js pour doser la poussière),
   `--fx-a` l'amplitude de base, `--fx-1/--fx-2` la teinte de rareté,
   `--fx-rgb` la même teinte en composantes (les canvas en ont besoin),
   `--fx-spin` la période du balayage — elle ACCÉLÈRE avec le palier, mais
   reste toujours lente : sous 8 s on lit un chargement, pas une présence. */
.shop-card {
    --fx-t: 0;
    --fx-a: .10;
    --fx-1: #9ca3af;
    --fx-2: #6b7280;
    --fx-rgb: 156, 163, 175;
    --fx-spin: 30s;
    --fx-breathe: 7s;
}
.shop-card.rarity-common     { --fx-t: 0; --fx-a: .10; --fx-1: #b8c0cc; --fx-2: #6b7280; --fx-rgb: 156,163,175; --fx-spin: 30s; --fx-breathe: 7.5s; }
.shop-card.rarity-uncommon   { --fx-t: 1; --fx-a: .17; --fx-1: #8ede93; --fx-2: #2d9e4f; --fx-rgb: 102,187,106; --fx-spin: 26s; --fx-breathe: 7s;   }
.shop-card.rarity-rare       { --fx-t: 2; --fx-a: .24; --fx-1: #93c5fd; --fx-2: #2563eb; --fx-rgb: 96,165,250;  --fx-spin: 22s; --fx-breathe: 6.5s; }
.shop-card.rarity-super_rare { --fx-t: 3; --fx-a: .31; --fx-1: #a5b4fc; --fx-2: #3f51b5; --fx-rgb: 121,134,203; --fx-spin: 19s; --fx-breathe: 6s;   }
.shop-card.rarity-epic       { --fx-t: 4; --fx-a: .38; --fx-1: #c4b5fd; --fx-2: #7c3aed; --fx-rgb: 167,139,250; --fx-spin: 16s; --fx-breathe: 5.5s; }
.shop-card.rarity-legendary  { --fx-t: 5; --fx-a: .46; --fx-1: #fde68a; --fx-2: #d97706; --fx-rgb: 251,191,36;  --fx-spin: 13s; --fx-breathe: 5s;   }
.shop-card.rarity-enchanted  { --fx-t: 6; --fx-a: .55; --fx-1: #f9a8d4; --fx-2: #be185d; --fx-rgb: 240,98,146;  --fx-spin: 11s; --fx-breathe: 4.5s; }
.shop-card.rarity-iconic     { --fx-t: 7; --fx-a: .66; --fx-1: #ffd166; --fx-2: #f97316; --fx-rgb: 255,183,77;  --fx-spin: 9s;  --fx-breathe: 4s;   }

/* ⚠ Les multiplicateurs des couches (1.05 pour la lueur et le faisceau,
   1.4 pour l'anneau) ont été BAISSÉS après lecture à l'écran : à 1.9, la
   Couronne Illuminaire (legendary, or) noyait son propre sigle dans sa
   propre lumière. Une ambiance qui efface l'article vendu est une ambiance
   ratée, quelle que soit sa beauté hors contexte. */
/* Ce qui est réellement peint. TOUT passe par le robinet. */
.shop-card { --fx: calc(var(--fx-a) * var(--fx-master)); }

/* La carte prend la couleur de sa rareté — discrètement, sur l'arête basse
   seulement : une bordure entière teintée fait un damier de couleurs à la
   première grille de vingt articles. */
.shop-card {
    position: relative;
    isolation: isolate;
    overflow: hidden;
}
.shop-card::after {
    content: '';
    position: absolute;
    left: 12%; right: 12%; bottom: 0;
    height: 1px;
    background: linear-gradient(90deg, transparent, var(--fx-1), transparent);
    opacity: calc(var(--fx) * 1.6);
    pointer-events: none;
}
.shop-card:hover {
    border-color: color-mix(in srgb, var(--fx-1) 26%, transparent);
    box-shadow: 0 6px 22px rgba(0,0,0,.28),
                0 0 0 1px color-mix(in srgb, var(--fx-1) 12%, transparent) inset;
}

/* ── L'ALCÔVE ─────────────────────────────────────────────────────────────
   Un renfoncement, pas un cadre : le fond descend SOUS la carte (dégradé
   plus sombre au centre) pour que l'objet ait de la profondeur derrière lui.
   Sans ça un sigle vectoriel flotte sur un aplat et se lit comme une icône
   de barre d'outils. */
.sg-stage {
    position: relative;
    width: 100%;
    height: var(--sg-h, 92px);
    margin-bottom: 2px;
    border-radius: 10px;
    display: grid;
    place-items: center;
    overflow: hidden;
    background:
        radial-gradient(120% 90% at 50% 118%, color-mix(in srgb, var(--fx-1) 9%, transparent), transparent 62%),
        radial-gradient(85% 70% at 50% 40%, rgba(0,0,0,.34), rgba(0,0,0,.10) 70%, transparent);
    box-shadow: inset 0 1px 0 rgba(255,255,255,.045),
                inset 0 -14px 22px -14px rgba(0,0,0,.55);
}

/* Chaque couche d'ambiance est un <i> vide, empilée sous l'art. */
.sg-stage > i {
    position: absolute;
    inset: 0;
    pointer-events: none;
    border-radius: inherit;
}

/* palier ≥1 — LA LUEUR DE SOL. L'objet cesse de flotter. */
.sg-halo {
    background: radial-gradient(58% 42% at 50% 92%,
                rgba(var(--fx-rgb), calc(var(--fx) * 1.05)), transparent 74%);
    animation: sgBreathe var(--fx-breathe) ease-in-out infinite;
}

/* palier ≥2 — LE FAISCEAU. Un cône de lumière qui monte du SOCLE et bascule
   très lentement, comme un projecteur de vitrine.
   ⚠ Première version : un secteur conique qui tournait autour du centre. Vu
   à l'écran, ça ne se lisait pas comme une lumière mais comme une TACHE
   grise en haut à droite — parce qu'un secteur large et mou n'a pas de
   direction, et que sans source visible l'œil n'a rien à quoi le rattacher.
   Ici l'apex est à 50% 118%, c'est-à-dire SOUS le socle : le faisceau part
   du même endroit que la lueur de sol du palier 1. Une seule source, deux
   manifestations — c'est ce qui les fait tenir ensemble. */
.sg-sheen {
    /* ⚠ LES RAMPES ANGULAIRES SONT LARGES, ET C'EST LA CORRECTION.
       La version d'origine montait de `transparent` à pleine intensité en
       12°, avec un `blur(1.5px)` : sur les cartes claires ça ne se voyait
       pas, mais sur le champ presque noir du Void Walker le faisceau se
       lisait comme une BANDE DIAGONALE À BORDS NETS — une forme géométrique
       posée sur une matière organique. La cause n'est pas l'opacité (la
       baisser aurait juste donné un slash plus pâle), c'est la DÉCOUPE : un
       dégradé conique dont l'apex est hors cadre produit des bords qui sont
       de longues droites, et 1,5 px de flou ne les casse pas sur 168 px.
       Trois rampes de 10–14° de chaque côté + un flou proportionnel à
       l'alcôve : le faisceau garde sa direction, il perd son arête. */
    background: conic-gradient(from -26deg at 50% 118%,
                transparent 0deg,
                rgba(var(--fx-rgb), calc(var(--fx) * .16)) 10deg,
                rgba(var(--fx-rgb), calc(var(--fx) * 1.0)) 24deg,
                rgba(var(--fx-rgb), calc(var(--fx) * .22)) 38deg,
                transparent 52deg);
    /* le faisceau s'éteint en montant : une lumière qui garde son intensité
       jusqu'au bord haut se lit comme un triangle peint. */
    -webkit-mask: linear-gradient(to top, #000 8%, rgba(0,0,0,.45) 58%, transparent 94%);
            mask: linear-gradient(to top, #000 8%, rgba(0,0,0,.45) 58%, transparent 94%);
    transform-origin: 50% 118%;
    animation: sgRock var(--fx-spin) ease-in-out infinite alternate;
    mix-blend-mode: screen;
    filter: blur(5px);
}

@keyframes sgRock {
    from { transform: rotate(-8deg); }
    to   { transform: rotate(8deg); }
}

/* palier ≥3 — L'ANNEAU CONTRA-ROTATIF. Il tourne dans l'autre sens et deux
   fois plus lentement : c'est le décalage entre les deux qui crée le
   scintillement, pas une animation d'opacité de plus. */
.sg-ring {
    /* ⚠ CERCLE, PAS `inset:`. Un `inset: 12%` dans une alcôve de 168×92
       donne une ELLIPSE très aplatie : à l'écran on ne lit pas un anneau
       mais deux traits diagonaux dans les coins hauts, comme des rayures
       sur l'image. La géométrie est prise sur la HAUTEUR, jamais sur la
       boîte, et l'anneau est centré à la main. */
    inset: auto;
    top: 50%;
    left: 50%;
    height: 78%;
    aspect-ratio: 1;
    border-radius: 50%;
    border: 1px solid transparent;
    background:
        conic-gradient(from 180deg,
            transparent 0deg,
            rgba(var(--fx-rgb), calc(var(--fx) * 1.4)) 40deg,
            transparent 96deg,
            transparent 180deg,
            rgba(var(--fx-rgb), calc(var(--fx) * .6)) 220deg,
            transparent 280deg) border-box;
    -webkit-mask: linear-gradient(#000 0 0) padding-box, linear-gradient(#000 0 0);
            mask: linear-gradient(#000 0 0) padding-box, linear-gradient(#000 0 0);
    -webkit-mask-composite: xor;
            mask-composite: exclude;
    animation: sgRingSpin calc(var(--fx-spin) * 2) linear infinite;
}

@keyframes sgRingSpin {
    from { transform: translate(-50%, -50%) rotate(0deg); }
    to   { transform: translate(-50%, -50%) rotate(-360deg); }
}

/* palier ≥6 — LES RUNES. Un arc de tirets qui se remplit puis se vide ; la
   teinte dérive en plus (hue-rotate) pour dire « ceci n'a pas de couleur
   fixe » — c'est le registre enchanted/iconic, invisible aujourd'hui. */
.sg-runes {
    /* Même correction que l'anneau : cercle centré, jamais un `inset`. */
    inset: auto;
    top: 50%;
    left: 50%;
    height: 94%;
    aspect-ratio: 1;
    border-radius: 50%;
    border: 1px dashed rgba(var(--fx-rgb), calc(var(--fx) * 1.3));
    animation: sgRunesSpin calc(var(--fx-spin) * 3) linear infinite,
               sgSpectre 11s linear infinite;
}

@keyframes sgRunesSpin {
    from { transform: translate(-50%, -50%) rotate(0deg); }
    to   { transform: translate(-50%, -50%) rotate(360deg); }
}

/* palier ≥3 — LE CHAMP DE POUSSIÈRE. L'hôte du canvas ORBITE ; shop-fx.js
   y monte la couche à l'entrée dans le viewport et la retire à la sortie.
   Le seuil est décidé côté PHP (shopDustHost) : sans hôte, pas de canvas. */
.sg-dust { z-index: 1; }
.sg-dust canvas { border-radius: inherit; }

/* L'art, toujours au-dessus de l'ambiance. */
.sg-art {
    position: relative;
    z-index: 2;
    display: grid;
    place-items: center;
    width: 100%;
    height: 100%;
}

@keyframes sgSpin     { to { transform: rotate(360deg); } }
@keyframes sgSpinBack { to { transform: rotate(-360deg); } }
@keyframes sgSpectre  { to { filter: hue-rotate(360deg); } }
@keyframes sgBreathe {
    0%, 100% { opacity: .68; transform: scale(1); }
    50%      { opacity: 1;   transform: scale(1.04); }
}

/* ═══ LES CADEAUX — le sigle vectoriel ════════════════════════════════════
   Trois parts animées séparément (cœur / orbite / éclats). Un SVG d'un seul
   bloc ne sait que tourner ou pulser en entier, et ça se voit. */
.shop-card[data-cat="gift"] .sg-stage { --sg-h: 92px; }

.sg-svg {
    width: 54px;
    height: 54px;
    overflow: visible;
    filter: drop-shadow(0 2px 5px rgba(0,0,0,.45))
            drop-shadow(0 0 calc(4px + var(--fx) * 16px) rgba(var(--fx-rgb), calc(var(--fx) * 1.3)));
    animation: sgFloat var(--fx-breathe) ease-in-out infinite;
}
.sg-svg .sg-core  { transform-origin: 50% 50%; animation: sgCore calc(var(--fx-breathe) * 1.3) ease-in-out infinite; }
.sg-svg .sg-orbit { transform-origin: 50% 50%; animation: sgSpin calc(var(--fx-spin) * .8) linear infinite; }
.sg-svg .sg-glint { transform-origin: 50% 50%; animation: sgGlint calc(var(--fx-breathe) * .85) ease-in-out infinite; }

@keyframes sgFloat {
    0%, 100% { transform: translateY(calc(var(--fx) * 2px)); }
    50%      { transform: translateY(calc(var(--fx) * -4px)); }
}
@keyframes sgCore {
    0%, 100% { transform: scale(1) rotate(0deg); }
    50%      { transform: scale(calc(1 + var(--fx) * .07)) rotate(calc(var(--fx) * 3deg)); }
}
@keyframes sgGlint {
    0%, 100% { opacity: calc(.30 + var(--fx) * .5); transform: scale(.9); }
    50%      { opacity: calc(.75 + var(--fx) * .5); transform: scale(1.12); }
}

/* ═══ LES THÈMES — le vrai symbole d'encre, dans son encre ════════════════
   Avant : un rond de couleur emoji (🟡🟣🟢…) au-dessus d'un dégradé fixe.
   Maintenant : le SYMBOLE D'ENCRE LORCANA (/svg/amber.svg…) posé dans un
   champ d'encre qui coule. C'est le même objet que sur les cartes du jeu —
   le joueur n'a rien à apprendre. */
.shop-card[data-cat="profile_theme"] .sg-stage { --sg-h: 92px; }

.sg-inkfield {
    position: absolute;
    inset: 0;
    z-index: 1;
    border-radius: inherit;
    background:
        radial-gradient(60% 70% at 22% 28%, var(--th-1), transparent 68%),
        radial-gradient(70% 80% at 78% 74%, var(--th-2), transparent 70%),
        linear-gradient(135deg, var(--th-2), transparent 55%, var(--th-1));
    background-size: 190% 190%, 200% 200%, 220% 220%;
    animation: sgInkDrift calc(16s + var(--fx-spin)) ease-in-out infinite;
    opacity: calc(.45 + var(--fx));
}
@keyframes sgInkDrift {
    0%   { background-position:   0% 30%,  100% 70%,   0% 50%; }
    33%  { background-position:  70% 70%,   30% 20%, 100% 20%; }
    66%  { background-position:  25%  8%,   72% 96%,  40% 90%; }
    100% { background-position:   0% 30%,  100% 70%,   0% 50%; }
}

.sg-ink {
    width: 46px;
    height: 46px;
    object-fit: contain;
    filter: drop-shadow(0 2px 6px rgba(0,0,0,.5))
            drop-shadow(0 0 calc(3px + var(--fx) * 14px) rgba(var(--fx-rgb), calc(var(--fx) * 1.2)));
    animation: sgFloat var(--fx-breathe) ease-in-out infinite;
}

/* Les encres. Deux teintes par thème — celles des previews d'avant, pour ne
   pas déplacer ce que Phil a déjà validé sur la couleur. */
.shop-card[data-fx="theme-amber"]    { --th-1: rgba(251,191,36,.55); --th-2: rgba(180,83,9,.45);   }
.shop-card[data-fx="theme-amethyst"] { --th-1: rgba(155,93,229,.55); --th-2: rgba(76,29,149,.48);  }
.shop-card[data-fx="theme-emerald"]  { --th-1: rgba(45,158,79,.55);  --th-2: rgba(6,95,70,.48);    }
.shop-card[data-fx="theme-ruby"]     { --th-1: rgba(230,57,70,.55);  --th-2: rgba(127,29,29,.48);  }
.shop-card[data-fx="theme-sapphire"] { --th-1: rgba(0,150,199,.55);  --th-2: rgba(30,58,138,.48);  }
.shop-card[data-fx="theme-steel"]    { --th-1: rgba(148,163,184,.5); --th-2: rgba(51,65,85,.5);    }
/* Aurora : les six encres, en boucle — un thème « toutes couleurs » ne peut
   pas être dit avec deux teintes. Le champ tourne en plus de dériver. */
.shop-card[data-fx="theme-aurora"] .sg-inkfield {
    background:
        conic-gradient(from 0deg, rgba(251,191,36,.42), rgba(230,57,70,.42), rgba(155,93,229,.42),
                       rgba(0,150,199,.42), rgba(45,158,79,.42), rgba(148,163,184,.42), rgba(251,191,36,.42));
    background-size: 160% 160%;
    animation: sgSpin 26s linear infinite, sgBreathe 9s ease-in-out infinite;
    filter: blur(9px) saturate(1.15);
}
/* Void : l'absence. Le champ est presque noir et AVALE la lumière au centre
   au lieu d'en émettre — c'est l'inverse exact des sept autres. */
.shop-card[data-fx="theme-void"] .sg-inkfield {
    background:
        radial-gradient(48% 60% at 50% 50%, #000 30%, rgba(20,5,45,.85) 62%, transparent 82%),
        conic-gradient(from 210deg, rgba(120,50,220,.35), rgba(10,0,25,.9), rgba(76,29,149,.4), rgba(10,0,25,.9), rgba(120,50,220,.35));
    background-size: 100% 100%, 200% 200%;
    animation: sgSpin 34s linear infinite;
    opacity: calc(.6 + var(--fx));
    /* ⚠ LE FLOU N'EST PAS COSMÉTIQUE, IL CORRIGE UNE DÉCOUPE.
       En `background-size: 200%` et position 0 0, l'apex du conique tombe sur
       le COIN de l'alcôve : ses secteurs deviennent alors de longues droites
       qui traversent tout le cadre. À l'écran on ne lisait pas une nébuleuse
       mais une BANDE DIAGONALE à bords nets — une géométrie dans ce qui doit
       être de la matière. Aurora ne l'avait pas parce qu'elle est floutée à
       9 px ; le Void portait la même construction sans le flou. Même remède
       pour les deux, et un peu plus fort ici parce que le contraste du champ
       presque noir rend chaque arête plus lisible. */
    filter: blur(11px) saturate(1.1);
}
/* ⚠ `Lore.svg` EST LE SEUL SIGLE DESSINÉ EN NOIR (#000000 + #5E5E5E) — les
   six encres sont des illustrations en couleur, l'encrier d'Aurora est en
   #D6C081. Le `brightness(.75)` d'origine ASSOMBRISSAIT donc un glyphe déjà
   noir, posé sur le champ le plus sombre de la boutique : à l'écran on ne
   lisait pas un symbole mais un TROU. La correction n'est pas d'éclaircir au
   jugé, c'est d'INVERSER — `invert(1)` envoie #000 sur du blanc et #5E5E5E
   sur un gris clair, ce qui rend au Lore son dessin au lieu de sa silhouette.
   La lueur violette par-dessus dit le Void ; la teinte vient du halo, pas du
   fichier, donc remplacer l'asset ne casse rien ici. */
.shop-card[data-fx="theme-void"] .sg-ink {
    filter: invert(1) brightness(1.35) saturate(0)
            drop-shadow(0 0 6px rgba(167,139,250,.75))
            drop-shadow(0 0 16px rgba(124,58,237,.45));
}

/* ═══ LES CADRES — inchangés (Phil : « déjà bien ») ═══════════════════════
   Ils gagnent l'alcôve et l'ambiance de palier, leur anneau reste celui
   d'avant. Le disque intérieur reçoit juste une matière au lieu d'un aplat,
   pour qu'on voie que c'est un AVATAR qui est encadré. */
.shop-card[data-cat="profile_frame"] .sg-stage { --sg-h: 92px; }
.shop-card[data-cat="profile_frame"] .preview-frame { height: auto; }
.shop-card[data-cat="profile_frame"] .preview-circle {
    width: 44px;
    height: 44px;
    background:
        radial-gradient(70% 70% at 35% 28%, rgba(255,255,255,.16), transparent 70%),
        linear-gradient(160deg, rgba(255,255,255,.10), rgba(255,255,255,.03));
}

/* ═══ LES EFFETS — l'aperçu EST le produit ════════════════════════════════
   `.sg-avatar` est inscrit dans les mêmes listes de sélecteurs que
   `.post-avatar` et `.user-avatar` (style.css, section Profile Effects).
   Il n'y a donc pas d'aperçu à maintenir : réparer l'effet répare la
   vitrine. C'est aussi ce qui interdit de vendre un effet mort — un carré
   vide dans la boutique se voit tout de suite. */
.shop-card[data-cat="profile_effect"] .sg-stage { --sg-h: 96px; }

.sg-wearer { position: relative; display: block; line-height: 0; }
.sg-avatar {
    position: relative;
    display: grid;
    place-items: center;
    width: 46px;
    height: 46px;
    border-radius: 50%;
    background: linear-gradient(150deg, #2b3444, #171d27);
    box-shadow: inset 0 1px 0 rgba(255,255,255,.10), 0 3px 10px rgba(0,0,0,.45);
    overflow: visible;
}
.sg-avatar-letter {
    font-size: .95rem;
    font-weight: 800;
    line-height: 1;
    color: rgba(226,232,240,.72);
    letter-spacing: .02em;
}

/* ═══ LES TITRES — le badge, gravé ════════════════════════════════════════
   Le badge de titre existait déjà ; c'est l'emoji au-dessus qui parasitait.
   Il part, le badge grandit et reçoit un socle. */
.shop-card[data-cat="title"] .sg-stage { --sg-h: 54px; }
.shop-card[data-cat="title"] .preview-title { height: auto; }
.shop-card[data-cat="title"] .profile-title-badge {
    font-size: .74rem;
    padding: 4px 12px;
    box-shadow: 0 2px 8px rgba(0,0,0,.35);
}

/* ═══ LES BADGES — inchangés (Phil : « déjà bien ») ═══════════════════════ */
.shop-card[data-cat="badge"] .sg-stage { --sg-h: 78px; }
.shop-card[data-cat="badge"] .preview-badge { height: auto; }
.shop-card[data-cat="badge"] .badge-preview-img {
    width: 42px;
    height: 42px;
    filter: drop-shadow(0 2px 6px rgba(0,0,0,.5))
            drop-shadow(0 0 calc(3px + var(--fx) * 14px) rgba(var(--fx-rgb), calc(var(--fx) * 1.4)));
    animation: sgFloat var(--fx-breathe) ease-in-out infinite;
}

/* ── L'ancienne bande d'aperçu et l'emoji sous le nom ─────────────────────
   Les deux disparaissent : le premier est remplacé par l'alcôve, le second
   était la doublure emoji de l'article. On ne les masque pas, shop.php ne
   les écrit plus — ces règles ne restent que pour un cache HTML tardif. */
.shop-card .sg-stage + .shop-preview,
.shop-card .sg-stage ~ .shop-card-icon:empty { display: none; }

/* ── SURVOL : la carte SE RAPPROCHE de la lumière ─────────────────────────
   Pas un `translateY(-2px)` de plus : ce qui change au survol, c'est le
   RÉGLAGE de l'ambiance — le robinet local s'ouvre, la scène s'éclaire.
   Un même geste, une réponse différente selon la rareté. */
.shop-card { transition: --fx-master .45s ease, border-color .25s, box-shadow .25s, transform .25s; }
.shop-card:hover { --fx-master: .95; }
.shop-card:hover .sg-stage { box-shadow: inset 0 1px 0 rgba(255,255,255,.08),
                                         inset 0 -16px 26px -14px rgba(0,0,0,.6); }

/* ── THÈME CLAIR ──────────────────────────────────────────────────────────
   Ce n'est pas le sombre inversé : sur papier, une lueur additive disparaît
   et l'alcôve doit devenir une OMBRE portée, pas un trou noir. */
body.light-theme .sg-stage,
[data-theme="light"] .sg-stage {
    background:
        radial-gradient(120% 90% at 50% 118%, color-mix(in srgb, var(--fx-1) 18%, transparent), transparent 62%),
        linear-gradient(180deg, rgba(15,23,42,.05), rgba(15,23,42,.10));
    box-shadow: inset 0 1px 0 rgba(255,255,255,.6),
                inset 0 -12px 20px -14px rgba(15,23,42,.30);
}
body.light-theme .sg-halo,
[data-theme="light"] .sg-halo { mix-blend-mode: multiply; opacity: .8; }
body.light-theme .sg-sheen,
[data-theme="light"] .sg-sheen { mix-blend-mode: multiply; }
body.light-theme .sg-svg,
[data-theme="light"] .sg-svg,
body.light-theme .sg-ink,
[data-theme="light"] .sg-ink {
    filter: drop-shadow(0 2px 4px rgba(15,23,42,.28))
            drop-shadow(0 0 calc(2px + var(--fx) * 9px) rgba(var(--fx-rgb), calc(var(--fx) * .9)));
}
body.light-theme .sg-avatar,
[data-theme="light"] .sg-avatar {
    background: linear-gradient(150deg, #e2e8f0, #cbd5e1);
    box-shadow: inset 0 1px 0 rgba(255,255,255,.9), 0 2px 8px rgba(15,23,42,.18);
}
body.light-theme .sg-avatar-letter,
[data-theme="light"] .sg-avatar-letter { color: rgba(51,65,85,.8); }

/* ── MOUVEMENT RÉDUIT ─────────────────────────────────────────────────────
   Tout s'arrête, rien ne disparaît : l'article reste lisible, éclairé à
   l'amplitude de sa rareté, simplement figé. */
@media (prefers-reduced-motion: reduce) {
    .sg-halo, .sg-sheen, .sg-ring, .sg-runes,
    .sg-svg, .sg-svg .sg-core, .sg-svg .sg-orbit, .sg-svg .sg-glint,
    .sg-ink, .sg-inkfield,
    .shop-card[data-cat="badge"] .badge-preview-img {
        animation: none !important;
    }
    .shop-card { transition: none; }
}

/* ── ÉCRANS ÉTROITS ───────────────────────────────────────────────────────
   Sur téléphone la grille passe à deux colonnes : l'alcôve se tasse, mais
   le sigle garde sa taille — c'est lui l'article. */
@media (max-width: 560px) {
    .sg-stage { --sg-h: 78px; }
    .shop-card[data-cat="title"] .sg-stage { --sg-h: 48px; }
    .sg-svg, .sg-ink { width: 44px; height: 44px; }
}

/* ── Le repli ─────────────────────────────────────────────────────────────
   Un article ajouté en base sans sigle connu retombe sur son icône de la
   base de données. Ce n'est pas la solution, c'est le FILET : un trou muet
   dans la grille se lit comme une panne de la boutique, pas comme un article
   à dessiner. Il porte quand même l'ambiance de son palier. */
.sg-fallback {
    font-size: 2rem;
    line-height: 1;
    filter: drop-shadow(0 2px 5px rgba(0,0,0,.45))
            drop-shadow(0 0 calc(3px + var(--fx) * 12px) rgba(var(--fx-rgb), calc(var(--fx) * 1.1)));
    animation: sgFloat var(--fx-breathe) ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) { .sg-fallback { animation: none !important; } }

/* ═══ L'IDENTITÉ DE COULEUR — elle appartient à L'ARTICLE ═════════════════
   Trouvé à l'œil sur le banc (05.09.2026) : la carte Void Walker — « dark
   matter cosmic theme » — peignait son halo, son balayage et son anneau en
   DORÉ. Mesuré, pas supposé : `--fx-rgb` valait `251,191,36` sur cette
   carte. Cause : la teinte était posée par le palier de rareté, et Void
   Walker est legendary — donc or, comme la Couronne Illuminaire.

   Le partage est maintenant net, et c'est la même doctrine que la table des
   canaux des cosmétiques (style.css) :
       LE PALIER décide COMBIEN — `--fx`, `--fx-a`, `--fx-spin`, quelles
                 couches sont allumées, si la carte gagne un canvas.
       L'ARTICLE décide QUOI    — `--fx-rgb`, sa seule teinte.
   Les deux ne se marchent plus dessus : une couronne legendary reste dorée
   parce qu'elle EST dorée, pas parce qu'elle est legendary.

   ⚠ CES VALEURS SONT LA SOURCE UNIQUE. shop-fx.js lit `--fx-rgb` sur la
   carte pour teinter son canvas de poussière ; avant, il portait sa propre
   table de teintes et les deux avaient déjà divergé (le canvas du Void
   était violet, son ambiance CSS dorée, sur la même carte).

   Non listés volontairement : les TITRES et les BADGES DE RARETÉ. Un titre
   est un RANG et un badge de rareté EST la rareté — pour eux, la teinte du
   palier n'est pas un défaut, c'est le sujet. */
.shop-card[data-fx="gift-star"]      { --fx-rgb: 253,230,138; }
.shop-card[data-fx="gift-rose"]      { --fx-rgb: 248,113,113; }
.shop-card[data-fx="gift-pixie"]     { --fx-rgb: 196,181,253; }
.shop-card[data-fx="gift-mirror"]    { --fx-rgb: 165,180,252; }
.shop-card[data-fx="gift-slipper"]   { --fx-rgb: 190,235,255; }
.shop-card[data-fx="gift-crown"]     { --fx-rgb: 253,230,138; }

.shop-card[data-fx="theme-amber"]    { --fx-rgb: 251,191,36;  }
.shop-card[data-fx="theme-amethyst"] { --fx-rgb: 167,139,250; }
.shop-card[data-fx="theme-emerald"]  { --fx-rgb: 74,222,128;  }
.shop-card[data-fx="theme-ruby"]     { --fx-rgb: 248,113,113; }
.shop-card[data-fx="theme-sapphire"] { --fx-rgb: 56,189,248;  }
.shop-card[data-fx="theme-steel"]    { --fx-rgb: 148,163,184; }
.shop-card[data-fx="theme-aurora"]   { --fx-rgb: 186,230,253; }
.shop-card[data-fx="theme-void"]     { --fx-rgb: 196,181,253; }

.shop-card[data-fx="frame-silver"]   { --fx-rgb: 203,213,225; }
.shop-card[data-fx="frame-gold"]     { --fx-rgb: 251,191,36;  }
.shop-card[data-fx="frame-enchanted"]{ --fx-rgb: 196,181,253; }
.shop-card[data-fx="frame-iconic"]   { --fx-rgb: 253,230,138; }

.shop-card[data-fx="effect-sparkle"] { --fx-rgb: 248,250,252; }
.shop-card[data-fx="effect-frost"]   { --fx-rgb: 147,197,253; }
.shop-card[data-fx="effect-phoenix"] { --fx-rgb: 251,146,60;  }
.shop-card[data-fx="effect-halo"]    { --fx-rgb: 253,230,138; }
