:root {
  /* Couleurs "nebuleuse" d'origine (identite 2GETHER) restaurees a la
     demande de l'asso ; la refonte "Salle d'arcade" garde sa typo (Bungee),
     ses boutons "bouton-poussoir" et sa mise en page, mais reprend la
     palette violet/cyan/rose d'avant plutot que le rouge/jaune/bleu. */
  --bg-void: #06070f;
  --bg-deep: #0b0d20;
  --bg-panel: rgba(23, 25, 51, 0.6);
  --bg-panel-solid: #14162e;
  --bg-panel-2: #1c1f42;
  --text: #f5f5fc;
  --text-dim: #9a9cc4;
  --border: rgba(139, 92, 246, 0.22);
  --border-soft: rgba(255, 255, 255, 0.08);

  --accent: #8b5cf6;
  --accent-2: #22d3ee;
  --accent-3: #f472b6;
  --accent-dark: #6d28d9;

  --rarity-commune: #9aa0b4;
  --rarity-rare: #60a5fa;
  --rarity-epique: #a855f7;
  --rarity-legendaire: #f5a524;
  --rarity-mythique: #ef4444;
  --rarity-unique: #22c55e;

  --success: #4ade80;
  --danger: #fb7185;

  --font-display: "Bungee", "Segoe UI", system-ui, sans-serif;
  --font-body: "Inter", "Segoe UI", system-ui, sans-serif;

  --nav-bottom-h: 64px;
}

/* ========================================================================
   Themes de couleur (debloques par niveau, voir main.js cycleTheme()).
   Chaque theme redefinit les memes variables que :root - tout le reste de
   la feuille de style les utilise deja via var(...), rien d'autre a toucher.
   ======================================================================== */
:root[data-theme="monochrome"] {
  --bg-void: #060606; --bg-deep: #0d0d0d;
  --bg-panel: rgba(30,30,30,0.6); --bg-panel-solid: #161616; --bg-panel-2: #202020;
  --text: #f2f2f2; --text-dim: #9a9a9a;
  --border: rgba(255,255,255,0.2); --border-soft: rgba(255,255,255,0.08);
  --accent: #d4d4d4; --accent-2: #a3a3a3; --accent-3: #e5e5e5;
  --rarity-commune: #8a8a8a; --rarity-rare: #b0b0b0; --rarity-epique: #d9d9d9; --rarity-legendaire: #ffffff; --rarity-mythique: #ef4444; --rarity-unique: #e5e5e5;
}
:root[data-theme="sepia"] {
  --bg-void: #1c140d; --bg-deep: #241a11;
  --bg-panel: rgba(70,50,30,0.5); --bg-panel-solid: #2c2015; --bg-panel-2: #3a2a1a;
  --text: #f3e6d0; --text-dim: #c1a887;
  --border: rgba(212,163,89,0.32); --border-soft: rgba(255,235,200,0.1);
  --accent: #d4a359; --accent-2: #b5762f; --accent-3: #e8c078;
  --rarity-commune: #a89078; --rarity-rare: #8ba888; --rarity-epique: #b58ac0; --rarity-legendaire: #d4a359; --rarity-mythique: #c1440e; --rarity-unique: #7fa650;
}
:root[data-theme="cyberpunk"] {
  --bg-void: #04010a; --bg-deep: #0a0316;
  --bg-panel: rgba(20,6,40,0.6); --bg-panel-solid: #12051f; --bg-panel-2: #1a0a2e;
  --text: #f0f0ff; --text-dim: #b088e8;
  --border: rgba(255,0,200,0.32); --border-soft: rgba(0,255,213,0.14);
  --accent: #ff2ec4; --accent-2: #00ffd5; --accent-3: #ffe600;
  --rarity-commune: #7dd3fc; --rarity-rare: #00ffd5; --rarity-epique: #ff2ec4; --rarity-legendaire: #ffe600; --rarity-mythique: #ff1f3d; --rarity-unique: #39ff88;
}

* { box-sizing: border-box; }

html, body {
  height: 100%;
}

html { scroll-behavior: smooth; }

/* Pose sur <html> (pas <body>, qui porte deja overflow-x:hidden + son
   animation d'entree) pendant qu'une modale plein ecran est ouverte : evite
   que la page derriere continue de defiler sous la modale. */
html.scroll-locked, html.scroll-locked body { overflow: hidden; height: 100%; }

:focus-visible {
  outline: 2px solid var(--accent-2);
  outline-offset: 2px;
  border-radius: 4px;
}

/* Lien d'evitement : premier element focusable de la page, invisible tant
   qu'il n'a pas le focus clavier - permet de sauter directement au contenu
   sans devoir traverser le header/la nav a chaque page. */
.skip-link {
  position: absolute;
  top: -100px;
  left: 12px;
  z-index: 1000;
  background: var(--accent);
  color: #fff;
  padding: 12px 18px;
  border-radius: 8px;
  font-weight: 700;
  text-decoration: none;
  transition: top 0.15s ease;
}
.skip-link:focus {
  top: max(12px, env(safe-area-inset-top, 0px));
}

/* Texte present pour les lecteurs d'ecran mais retire visuellement - pour
   les cas ou un placeholder ou une icone suffit visuellement mais ne
   remplace pas un vrai label accessible. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

body {
  margin: 0;
  min-height: 100vh;
  background: var(--bg-void);
  color: var(--text);
  font-family: var(--font-body);
  position: relative;
  overflow-x: hidden;
  opacity: 0;
  animation: pageFadeIn 0.4s ease forwards;
}

/* Uniquement l'opacite : animer transform sur body creerait un containing
   block pour tous ses descendants position:fixed (modal plein ecran,
   bottom-nav, toasts, flashs), les empechant de rester colles a l'ecran. */
@keyframes pageFadeIn {
  from { opacity: 0; }
  to { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  body { animation: none; opacity: 1; }
  *, *::before, *::after { animation-duration: 0.001ms !important; animation-iteration-count: 1 !important; transition-duration: 0.001ms !important; }
}

/* --- Fond ambiant animé --- */
body::before,
body::after {
  content: "";
  position: fixed;
  inset: -20%;
  z-index: -2;
  pointer-events: none;
}

body::before {
  background:
    radial-gradient(circle at 15% 20%, rgba(139, 92, 246, 0.30), transparent 40%),
    radial-gradient(circle at 85% 15%, rgba(34, 211, 238, 0.22), transparent 42%),
    radial-gradient(circle at 50% 90%, rgba(244, 114, 182, 0.16), transparent 45%),
    var(--bg-void);
  animation: driftBg 24s ease-in-out infinite alternate;
}

body::after {
  background-image:
    radial-gradient(1.5px 1.5px at 20px 30px, rgba(255,255,255,0.5), transparent),
    radial-gradient(1.5px 1.5px at 140px 90px, rgba(255,255,255,0.35), transparent),
    radial-gradient(1px 1px at 90px 160px, rgba(255,255,255,0.4), transparent),
    radial-gradient(1.5px 1.5px at 220px 60px, rgba(255,255,255,0.3), transparent),
    radial-gradient(1px 1px at 260px 200px, rgba(255,255,255,0.4), transparent);
  background-repeat: repeat;
  background-size: 280px 240px;
  opacity: 0.5;
  animation: driftStars 90s linear infinite;
}

@keyframes driftBg {
  from { transform: translate3d(0, 0, 0) scale(1); }
  to { transform: translate3d(-3%, 2%, 0) scale(1.05); }
}

@keyframes driftStars {
  from { background-position: 0 0; }
  to { background-position: -280px 240px; }
}

a { color: var(--accent-2); text-decoration: none; }
a:hover { text-decoration: underline; }

h1, h2, h3 { font-family: var(--font-display); letter-spacing: -0.01em; }

/* --- Header --- */
/* position:fixed (pas sticky) : le header est retire du flux du document,
   donc quand initHeaderShrink() (main.js) change son padding interne au
   scroll, ca ne reflow plus jamais le reste de la page. C'etait la vraie
   cause du glitch au scroll (la version sticky forcait tout le contenu en
   dessous a se repositionner a chaque bascule shrunk/normal). L'espace
   reserve en haut de <main>/.bottom-nav-safe est calcule par JS
   (syncHeaderOffset, main.js) via la variable --header-h. */
header#site-header {
  border-bottom: 1px solid var(--border-soft);
  background: rgba(6, 7, 15, 0.7);
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  backdrop-filter: blur(14px) saturate(140%);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
  z-index: 10;
}

/* Banniere beta : ajoutee en JS comme premier enfant du header (voir
   renderHeader, main.js), pas un element separe - sa hauteur compte donc
   automatiquement dans --header-h sans code special. A retirer avec son
   appel JS au lancement de la V1. */
/* Bandeau d'avertissement discret plutot qu'un bloc jaune plein-opaque :
   meme langage "panneau de verre sombre" que le reste du site, l'accent
   or reste reserve au TEXTE/à la bordure (coherent avec --rarity-legendaire
   qui utilise deja ce ton pour signaler quelque chose de particulier). */
.beta-banner {
  background: rgba(245, 165, 36, 0.1);
  border-bottom: 1px solid rgba(245, 165, 36, 0.32);
  color: #fbbf24;
  text-align: center;
  font-size: 0.76rem;
  font-weight: 600;
  padding: 8px 14px;
  line-height: 1.4;
}
.beta-banner strong { color: #fde68a; }
/* .beta-banner tout court reste le style "maintenance" (ambre, deja utilise
   pour l'avertissement beta) - .banner-info est une variante plus neutre
   pour une simple annonce, pas une mise en garde. */
.beta-banner.banner-info {
  background: color-mix(in srgb, var(--accent-2) 12%, transparent);
  border-bottom: 1px solid color-mix(in srgb, var(--accent-2) 35%, transparent);
  color: var(--accent-2);
}
.beta-banner.banner-info strong { color: var(--text); }

.header-inner {
  max-width: 1300px;
  margin: 0 auto;
  padding: 14px 20px;
  display: flex;
  align-items: center;
  gap: 16px;
  transition: padding 0.2s ease;
}
/* Le logo et le bloc utilisateur (stats + pseudo) ne doivent jamais etre
   pousses hors ecran : avec 8-9 destinations possibles au total (Communaute
   et Jeux ajoutes cette session), c'est le NAV qui cede en premier - il
   defile horizontalement plutot que de faire deborder tout le header
   (c'etait le bug avant ce correctif : le pseudo se faisait couper a droite
   sur un ecran de 1400px). */
.brand, .user-box { flex-shrink: 0; }
/* Se tasse au scroll vers le bas (initHeaderShrink, main.js) : recupere de
   la hauteur d'ecran sur les longues pages sans jamais disparaitre. */
header#site-header.shrunk .header-inner { padding: 8px 20px; }
header#site-header.shrunk .brand { font-size: 1.1rem; }
header#site-header.shrunk .avatar { width: 22px; height: 22px; }

.brand {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 1.3rem;
  background: linear-gradient(90deg, var(--accent), var(--accent-2));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.nav {
  display: flex;
  gap: 2px;
  flex: 1;
  min-width: 0;
  /* PAS de overflow-x: auto ici (retire, 2026-09-30) : sans overflow-y
     assorti, la spec CSS force alors overflow-y a "auto" aussi (regle
     "si l'un vaut visible et l'autre non, visible devient auto") - ca
     rognait silencieusement tout .nav-group-dropdown positionne EN DESSOUS
     de .nav (bug reel trouve en testant les sous-menus : styles calcules
     corrects, dropdown pourtant invisible a l'ecran). Le repli "ca ne
     rentre pas" est desormais gere par le retour a la ligne (voir media
     query 641-1180px plus bas), pas par un defilement horizontal cache. */
  font-family: var(--font-display);
}
.nav a {
  position: relative;
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
  white-space: nowrap;
  color: var(--text-dim);
  font-weight: 600;
  font-size: 0.82rem;
  padding: 8px 8px;
  border-radius: 8px;
  transition: color 0.15s, background 0.15s;
}
.nav a:hover { color: var(--text); text-decoration: none; background: rgba(255,255,255,0.05); }
.nav a.active { color: var(--text); }
.nav a.active::after {
  content: "";
  position: absolute;
  left: 12px;
  right: 12px;
  bottom: 1px;
  height: 2px;
  border-radius: 2px;
  background: linear-gradient(90deg, var(--accent), var(--accent-2));
}

/* Sous-menus de nav (reorganisation demandee, 2026-09-30) : les pages qui
   appartiennent au meme geste (ouvrir un booster / reclamer un code ;
   consulter sa collection / la faire evoluer) sont regroupees sous un seul
   declencheur nomme plutot que d'etaler 9 destinations a plat (voir
   NAV_ITEMS/buildNavNodes, main.js). Classes generiques : plusieurs groupes
   peuvent coexister dans la nav, chacun avec son propre etat ouvert/ferme.
   Meme squelette ouverture/fermeture que .user-menu/.notif-bell. */
.nav-group { position: relative; flex-shrink: 0; }
.nav-group-trigger {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  flex-shrink: 0;
  white-space: nowrap;
  background: transparent;
  border: none;
  box-shadow: none;
  color: var(--text-dim);
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.82rem;
  padding: 8px 8px;
  border-radius: 8px;
  cursor: pointer;
  transition: color 0.15s, background 0.15s;
}
.nav-group-trigger:hover, .nav-group-trigger.active { color: var(--text); background: rgba(255,255,255,0.05); }
.nav-group-arrow { font-size: 0.65rem; transition: transform 0.15s; }
.nav-group.open .nav-group-arrow { transform: rotate(180deg); }
.nav-group-trigger.has-dot::after {
  content: "";
  position: absolute;
  top: 5px;
  right: 2px;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--danger);
  box-shadow: 0 0 0 2px var(--bg-panel-solid);
}
.nav-group-dropdown {
  position: absolute;
  top: calc(100% + 10px);
  left: 0;
  min-width: 190px;
  background: var(--bg-panel-solid);
  border: 1px solid var(--border-soft);
  border-radius: 12px;
  box-shadow: 0 20px 50px -16px rgba(0,0,0,0.7);
  padding: 8px;
  display: none;
  z-index: 20;
}
.nav-group.open .nav-group-dropdown { display: block; animation: modalPopIn 0.15s ease forwards; }
.nav-group-dropdown a {
  display: flex;
  width: 100%;
  align-items: center;
  gap: 8px;
  color: var(--text-dim);
  font-family: var(--font-body);
  font-weight: 500;
  font-size: 0.88rem;
  padding: 9px 10px;
  border-radius: 8px;
  white-space: nowrap;
}
.nav-group-dropdown a:hover { background: rgba(255,255,255,0.06); color: var(--text); text-decoration: none; }
.nav-group-dropdown a.active { color: var(--text); }

.user-box { display: flex; align-items: center; gap: 14px; font-size: 0.9rem; color: var(--text-dim); }

/* Ressources du joueur regroupees en une seule pastille (plutot que deux
   badges flottants cote a cote) : plus lisible, moins de "clutter" dans le
   header. */
.header-stats {
  display: flex;
  align-items: center;
  gap: 10px;
  background: var(--bg-panel-2);
  border: 1px solid var(--border-soft);
  padding: 6px 12px;
  border-radius: 999px;
}
.stat-chip {
  display: flex;
  align-items: center;
  gap: 6px;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.82rem;
  color: var(--text);
}
.stat-chip .icon { font-size: 0.92rem; }
.stat-divider { width: 1px; height: 14px; background: var(--border-soft); }

main {
  max-width: 1100px;
  margin: 0 auto;
  padding: calc(var(--header-h, 78px) + 40px) 20px 90px;
  position: relative;
  z-index: 1;
}

/* Grille du tableau de bord (accueil) : les panneaux secondaires (XP, roue,
   quetes) tiennent cote a cote sur ecran large au lieu d'empiler une longue
   colonne unique - le panel Hero et les panels pleine-largeur (Succes,
   Activite) restent en dehors de cette grille. Une seule colonne sur mobile,
   ordre de lecture inchange. */
.home-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 20px;
  margin-top: 24px;
}
.home-grid .panel { margin-top: 0; height: 100%; min-width: 0; }
@media (min-width: 760px) {
  .home-grid { grid-template-columns: 1fr 1fr; }
}

.panel {
  position: relative;
  background: var(--bg-panel);
  backdrop-filter: blur(10px);
  border: 2px solid var(--border-soft);
  border-radius: 20px;
  padding: 32px;
  box-shadow: 0 20px 60px -20px rgba(0,0,0,0.6);
}
/* Halo ambiant derriere le panel : --rarity-glow est pose par collection.js
   sur la couleur de la carte la plus rare possedee (accent par defaut
   ailleurs). */
.panel::before {
  content: "";
  position: absolute;
  inset: -40px;
  z-index: -1;
  border-radius: inherit;
  background: radial-gradient(ellipse at 50% 0%, var(--rarity-glow, var(--accent)), transparent 65%);
  opacity: 0.16;
  pointer-events: none;
  transition: background 0.6s ease;
}
/* Mobile : le halo deborde de 40px de chaque cote, ce qui faisait glisser
   toute la page de ~20px a l'horizontale (overflow-x du body ignore par
   certains navigateurs mobiles). Debord vertical seulement. */
@media (max-width: 640px) {
  .panel::before { left: 0; right: 0; }
}

h1 { font-size: 2rem; margin-top: 0; font-weight: 800; }
/* Liens dans un texte courant : soulignes, pas seulement colores. */
.lead a, .adb-help a { text-decoration: underline; text-underline-offset: 2px; }
/* Illustrations cliquables (vignette du classeur, booster) : focus clavier visible. */
.collection-card .card-art[role="button"]:focus-visible,
.extension-tile .ext-art[role="button"]:focus-visible { outline: 3px solid var(--accent-2); outline-offset: 2px; border-radius: 10px; }
/* Chip "ticket de jeton" : le seul flourish vraiment marque de la page,
   le reste reste sobre pour ne pas le concurrencer. */
.hero-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: var(--font-display);
  font-size: 0.68rem;
  font-weight: 400;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--accent-2);
  background: var(--bg-panel-2);
  border: 2px dashed var(--border);
  border-radius: 999px;
  padding: 5px 14px 5px 10px;
  margin-bottom: 16px;
}
.hero-eyebrow::before { content: "\1F3AB"; font-size: 0.9em; }
.hero-title { font-size: 2.6rem; text-wrap: balance; }
.hero-highlight {
  background: linear-gradient(90deg, var(--accent), var(--accent-2));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
h2 { font-size: 1.3rem; color: var(--text); font-weight: 700; }

/* Vitrine "derniere carte obtenue" dans le heros de l'accueil : rend la
   page d'accueil plus vivante (vraie illustration de carte, pas juste des
   stats/boutons) sans deplacer le focus des CTA principaux - une seule
   ligne discrete sous les boutons d'action. */
.last-card-showcase {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-top: 20px;
  padding: 10px 16px 10px 10px;
  background: var(--bg-panel-2);
  border: 1px solid var(--border-soft);
  border-radius: 14px;
  text-decoration: none;
  color: var(--text);
  transition: transform 0.15s ease, border-color 0.15s ease;
  max-width: 460px;
}
.last-card-showcase:hover {
  transform: translateY(-2px);
  border-color: var(--accent-2);
}
.last-card-showcase img {
  width: 46px;
  aspect-ratio: 3/4;
  object-fit: cover;
  border-radius: 8px;
  flex-shrink: 0;
}
.last-card-info { display: flex; flex-direction: column; gap: 4px; min-width: 0; flex: 1; }
.last-card-label { font-size: 0.66rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-dim); }
.last-card-name {
  font-family: var(--font-display);
  font-size: 0.9rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.last-card-showcase .rarity-badge { align-self: flex-start; font-size: 0.66rem; padding: 2px 8px; }
.last-card-arrow { color: var(--text-dim); font-size: 1.3rem; flex-shrink: 0; }

/* ========================================================================
   Menu deroulant stylise (enhanceSelect(), main.js) : remplace le <select>
   natif du navigateur par une interface a la charte du site, en gardant le
   <select> d'origine comme source de verite (valeur, evenement "change").
   ======================================================================== */
.fancy-select { position: relative; display: inline-block; }
.fancy-select-native {
  position: absolute;
  inset: 0;
  opacity: 0;
  pointer-events: none;
}
.fancy-select-trigger {
  display: flex;
  align-items: center;
  gap: 10px;
  background: var(--bg-panel-2);
  border: 1px solid var(--border-soft);
  color: var(--text);
  padding: 10px 14px;
  border-radius: 10px;
  font-size: 0.9rem;
  font-family: var(--font-body);
  cursor: pointer;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.fancy-select-trigger:hover { border-color: var(--border); }
.fancy-select.open .fancy-select-trigger,
.fancy-select-trigger:focus-visible {
  border-color: var(--accent-2);
  box-shadow: 0 0 0 2px rgba(34, 211, 238, 0.25);
  outline: none;
}
.fancy-select-label { flex: 1; text-align: left; white-space: nowrap; }
.fancy-select-arrow {
  color: var(--text-dim);
  font-size: 0.7rem;
  transition: transform 0.15s ease;
}
.fancy-select.open .fancy-select-arrow { transform: rotate(180deg); }
.fancy-select-menu {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  min-width: 100%;
  z-index: 20;
  background: var(--bg-panel-solid);
  border: 1px solid var(--border-soft);
  border-radius: 10px;
  padding: 6px;
  box-shadow: 0 16px 32px -12px rgba(0,0,0,0.6);
  animation: fancySelectOpen 0.15s ease both;
}
@keyframes fancySelectOpen {
  from { opacity: 0; transform: translateY(-4px); }
  to { opacity: 1; transform: translateY(0); }
}
/* Barre de recherche : hors de la zone qui defile (.fancy-select-options),
   reste toujours visible en haut du menu. */
.fancy-select-search {
  width: 100%;
  margin-bottom: 6px;
  padding: 8px 10px;
  border-radius: 7px;
  border: 1px solid var(--border-soft);
  background: var(--bg-panel-2);
  color: var(--text);
  font-size: 0.85rem;
  font-family: var(--font-body);
}
.fancy-select-search:focus { outline: 2px solid var(--accent-2); outline-offset: -1px; }
.fancy-select-options {
  max-height: 240px;
  overflow-y: auto;
}
.fancy-select-option {
  padding: 9px 12px;
  border-radius: 7px;
  font-size: 0.9rem;
  color: var(--text-dim);
  cursor: pointer;
  white-space: nowrap;
}
.fancy-select-option:hover,
.fancy-select-option.highlighted { background: rgba(255,255,255,0.06); color: var(--text); }
.fancy-select-option[aria-selected="true"] { color: var(--accent-2); font-weight: 600; }
.fancy-select-empty { padding: 9px 12px; font-size: 0.85rem; color: var(--text-dim); font-style: italic; }

/* ========================================================================
   Fil d'activite (accueil)
   ======================================================================== */
.activity-feed { display: flex; flex-direction: column; gap: 2px; margin-top: 12px; }
.activity-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 4px;
  border-bottom: 1px solid var(--border-soft);
  font-size: 0.88rem;
}
.activity-row:last-child { border-bottom: none; }
.activity-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
.activity-text { flex: 1; color: var(--text-dim); }
.activity-text strong { color: var(--text); }
.activity-time { color: var(--text-dim); font-size: 0.76rem; flex-shrink: 0; }
.activity-ext { color: var(--text-dim); font-weight: 500; }
.community-counter {
  font-size: 0.78rem;
  color: var(--text-dim);
  font-family: var(--font-display);
  font-weight: 700;
  white-space: nowrap;
}

/* ========================================================================
   Quetes du jour (accueil)
   ======================================================================== */
.quests-list { display: flex; flex-direction: column; gap: 8px; margin-top: 12px; }
.quest-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 14px;
  background: var(--bg-panel-2);
  border: 1px solid var(--border-soft);
  border-radius: 10px;
  font-size: 0.9rem;
  color: var(--text-dim);
}
.quest-row.done { color: var(--text); border-color: rgba(74,222,128,0.35); }
.quest-icon { font-size: 1.1rem; display: inline-flex; }
/* Coche qui se dessine (embellissement 2026-09-30) au lieu du remplacement
   instantane par un emoji - voir index.html QUEST_CHECK_SVG. */
.quest-check-svg path {
  fill: none;
  stroke: var(--success);
  stroke-width: 3;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-dasharray: 24;
  stroke-dashoffset: 24;
  animation: questCheckDraw 0.4s ease forwards;
}
@keyframes questCheckDraw { to { stroke-dashoffset: 0; } }
@media (prefers-reduced-motion: reduce) {
  .quest-check-svg path { animation: none; stroke-dashoffset: 0; }
}
/* Quetes hebdomadaires : compteur 0-5 (pas juste fait/pas fait), une mini
   barre de progression pleine largeur en dessous via flex-basis:100%. */
.quest-row.weekly { flex-wrap: wrap; }
.quest-row.weekly .quest-label { flex: 1; min-width: 110px; }
.quest-count { font-size: 0.78rem; font-variant-numeric: tabular-nums; color: var(--text-dim); flex-shrink: 0; }
.quest-row.weekly.done .quest-count { color: var(--success); font-weight: 700; }
.quest-mini-track {
  flex-basis: 100%;
  height: 5px;
  border-radius: 999px;
  background: rgba(255,255,255,0.08);
  overflow: hidden;
}
.quest-mini-fill {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: linear-gradient(90deg, var(--accent), var(--accent-2));
  transition: width 0.4s ease;
}
.quest-reward {
  margin-top: 4px;
  padding: 10px 14px;
  border-radius: 10px;
  background: linear-gradient(135deg, rgba(139, 92, 246, 0.18), rgba(34, 211, 238, 0.12));
  border: 1px solid var(--border);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.85rem;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  flex-wrap: wrap;
  text-align: center;
}
.quest-reward.claimed { background: linear-gradient(135deg, rgba(74,222,128,0.2), rgba(34, 211, 238, 0.12)); border-color: rgba(74,222,128,0.4); }
.quest-claim-btn { font-size: 0.78rem; padding: 8px 14px; flex-shrink: 0; }
.streak-badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 4px 10px;
  border-radius: 999px;
  background: linear-gradient(135deg, rgba(251,146,60,0.22), rgba(239,68,68,0.16));
  border: 1px solid rgba(251,146,60,0.4);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.78rem;
  color: #fdba74;
  white-space: nowrap;
}
/* Passage de niveau (embellissement 2026-09-30) : bandeau plein ecran cree
   dynamiquement par main.js showLevelUpBanner() - remplace le simple Toast
   d'avant, visible depuis n'importe quelle page (pas seulement redeem.html/
   ouverture.html qui ont leurs propres flashs dedies). */
.level-up-banner {
  position: fixed;
  top: 16%;
  left: 50%;
  transform: translate(-50%, -20px) scale(0.6);
  z-index: 450;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: 16px 44px;
  border-radius: 20px;
  background: linear-gradient(135deg, var(--accent), var(--accent-2));
  box-shadow: 0 30px 80px -20px rgba(0,0,0,0.7), 0 0 60px color-mix(in srgb, var(--accent) 50%, transparent);
  opacity: 0;
  pointer-events: none;
}
.level-up-banner.active { animation: levelUpBannerPop 2.6s cubic-bezier(.2,.9,.3,1) forwards; }
.level-up-banner-label {
  font-family: var(--font-body);
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: rgba(0,0,0,0.6);
  font-weight: 700;
}
.level-up-banner-number {
  font-family: var(--font-display);
  font-size: 2.8rem;
  font-weight: 800;
  color: var(--bg-void);
  line-height: 1;
}
@keyframes levelUpBannerPop {
  0% { opacity: 0; transform: translate(-50%, -20px) scale(0.6); }
  15% { opacity: 1; transform: translate(-50%, 0) scale(1.1); }
  25% { transform: translate(-50%, 0) scale(1); }
  85% { opacity: 1; transform: translate(-50%, 0) scale(1); }
  100% { opacity: 0; transform: translate(-50%, -10px) scale(0.95); }
}
@media (prefers-reduced-motion: reduce) {
  .level-up-banner.active { animation: levelUpBannerFade 2.6s ease forwards; }
}
@keyframes levelUpBannerFade {
  0% { opacity: 0; }
  15%, 85% { opacity: 1; }
  100% { opacity: 0; }
}
@media (max-width: 480px) {
  .level-up-banner { padding: 12px 28px; }
  .level-up-banner-number { font-size: 2.1rem; }
}

p.lead { color: var(--text-dim); font-size: 1.02rem; line-height: 1.5; }

/* Roue de la fortune quotidienne (accueil). */
.wheel-widget { display: flex; align-items: center; gap: 16px; margin-top: 12px; }
.wheel-icon { font-size: 2.4rem; line-height: 1; }
/* Coffre de guilde (embellissement 2026-09-30) : l'icone "a l'air" pleine ou
   vide selon poolSize (voir communaute.js loadChest) au lieu d'un simple
   cadeau fixe - un signal visuel immediat avant meme de lire le texte. */
.chest-icon { transition: filter 0.3s ease, transform 0.3s ease; }
.chest-icon.chest-empty { filter: grayscale(0.7) opacity(0.55); }
.chest-icon.chest-full { animation: chestFullPulse 1.6s ease-in-out infinite; filter: drop-shadow(0 0 10px rgba(212,175,55,0.7)); }
@keyframes chestFullPulse {
  0%, 100% { transform: scale(1) rotate(0deg); }
  50% { transform: scale(1.12) rotate(-4deg); }
}
@media (prefers-reduced-motion: reduce) {
  .chest-icon.chest-full { animation: none; }
}
.wheel-status { flex: 1; display: flex; flex-direction: column; gap: 8px; }
#wheel-status-text { color: var(--text-dim); font-size: 0.9rem; }
.chest-status-hint { color: var(--text-dim); font-size: 0.82rem; font-style: italic; }
.chest-status-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.chest-status-actions .btn-secondary { font-size: 0.82rem; padding: 8px 12px; }
.wheel-prizes-preview { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 16px; }
.wheel-prize-chip {
  font-size: 0.72rem;
  font-family: var(--font-display);
  padding: 5px 10px;
  border-radius: 999px;
  background: var(--bg-panel-solid);
  border: 1px solid var(--border-soft);
  color: var(--text-dim);
}
.wheel-prize-chip.wheel-prize-rare { border-color: var(--accent-2); color: var(--accent-2); }

/* Mini-jeu de fouille : meme squelette que .wheel-widget. */
.dig-widget { display: flex; align-items: center; gap: 16px; margin-top: 12px; }
/* Petit balancement au repos (embellissement, 2026-09-30) : une pioche
   parfaitement figee a cote d'une jauge qui vit (pips qui se remplissent)
   detonnait, comme un element mort au milieu d'une page par ailleurs animee
   (pack qui flotte, roue qui tourne, etc.). */
.dig-icon { font-size: 2.4rem; line-height: 1; display: inline-block; animation: digIconIdle 2.6s ease-in-out infinite; }
@keyframes digIconIdle {
  0%, 100% { transform: rotate(-6deg); }
  50% { transform: rotate(6deg); }
}
@media (prefers-reduced-motion: reduce) { .dig-icon { animation: none; } }
.dig-status { flex: 1; display: flex; flex-direction: column; gap: 8px; }
#dig-status-text { color: var(--text-dim); font-size: 0.9rem; }
.dig-energy-bar { display: flex; gap: 6px; }
.dig-pip { width: 22px; height: 14px; border-radius: 4px; background: var(--bg-panel-2); border: 1px solid var(--border-soft); }
.dig-pip.filled { background: linear-gradient(135deg, var(--accent), var(--accent-2)); border-color: var(--accent-2); }
.dig-result { margin-top: 16px; padding: 12px 16px; border-radius: 10px; background: var(--bg-panel-2); border: 1px solid var(--border-soft); font-weight: 700; font-family: var(--font-display); text-align: center; }

/* Site de fouille (2026-09-30) : cadre chaleureux (tons terre) autour du
   plateau plutot que des tuiles flottant directement sur le fond violet de
   la page - donne un vrai lieu ("un chantier de fouille") au lieu d'une
   grille abstraite. */
.dig-site {
  margin-top: 18px;
  padding: 22px;
  border-radius: 18px;
  background:
    radial-gradient(circle at 50% 0%, rgba(245,165,36,0.08), transparent 60%),
    linear-gradient(180deg, #241a12, #1a130d);
  border: 1px solid rgba(245, 165, 36, 0.18);
  box-shadow: inset 0 2px 18px rgba(0,0,0,0.4);
}

/* Plateau de fouille : grille de tuiles a creuser (voir jeux.js/dig.json).
   Terre non creusee -> vide (rien) -> fissure (tresor partiel, discret,
   jamais localise) -> icone (tresor libere), chacun un etat visuel distinct
   pour rester lisible d'un coup d'oeil pendant la partie. */
.dig-board {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 10px;
  max-width: 420px;
  margin: 0 auto;
}
.dig-tile {
  position: relative;
  aspect-ratio: 1;
  border-radius: 12px;
  border: 1px solid rgba(0,0,0,0.3);
  background:
    repeating-linear-gradient(135deg, rgba(255,255,255,0.05) 0 6px, transparent 6px 14px),
    linear-gradient(160deg, #4a3728, #2c2013);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.6rem;
  transition: transform 0.15s ease, box-shadow 0.15s ease, border-color 0.15s ease;
  overflow: hidden;
}
/* Petits cailloux/mottes eparpilles, une disposition differente par variante
   (voir data-variant, jeux.js) - sans ca les 16 tuiles etaient un copier-
   coller visuel exact du meme motif, effet "papier peint" plat. */
.dig-tile::before {
  content: "";
  position: absolute;
  inset: 0;
  opacity: 0.5;
  background-image:
    radial-gradient(circle, rgba(0,0,0,0.35) 0 3px, transparent 3px),
    radial-gradient(circle, rgba(255,255,255,0.08) 0 2px, transparent 2px);
  pointer-events: none;
}
.dig-tile[data-variant="1"]::before { background-position: 15% 25%, 70% 65%; background-size: 30% 30%, 24% 24%; }
.dig-tile[data-variant="2"]::before { background-position: 75% 20%, 25% 70%; background-size: 26% 26%, 32% 32%; }
.dig-tile[data-variant="3"]::before { background-position: 40% 75%, 80% 30%; background-size: 34% 34%, 22% 22%; }
.dig-tile[data-variant="4"]::before { background-position: 20% 55%, 65% 15%; background-size: 24% 24%, 30% 30%; }
.dig-tile:hover:not([disabled]) {
  transform: translateY(-3px) scale(1.03);
  border-color: var(--accent-2);
  box-shadow: 0 10px 20px -12px color-mix(in srgb, var(--accent) 60%, transparent);
}
.dig-tile[disabled] { cursor: not-allowed; }
.dig-tile[disabled]:not(.dug) { opacity: 0.4; }
.dig-tile.dug {
  background: radial-gradient(circle at 50% 40%, #131418, #0a0b0e 75%);
  border-style: dashed;
  border-color: var(--border-soft);
  box-shadow: inset 0 6px 14px rgba(0,0,0,0.6);
}
.dig-tile.dug::before { display: none; }
.dig-tile.dug.empty { opacity: 0.6; }
/* Pulsation (embellissement 2026-09-30) : signale qu'un tresor est en cours
   sur CETTE tuile sans jamais reveler la position des autres tuiles du meme
   tresor (deliberement caches, voir jeux.js digTileContent). */
.dig-tile.dug.partial { border-color: #f5a524; animation: digPartialPulse 1.8s ease-in-out infinite; }
.dig-partial-icon { font-size: 1.3rem; filter: drop-shadow(0 0 6px rgba(245,165,36,0.6)) grayscale(0.35); opacity: 0.85; }
@keyframes digPartialPulse {
  0%, 100% { box-shadow: inset 0 6px 14px rgba(0,0,0,0.6), 0 0 0 0 rgba(245,165,36,0.4); }
  50% { box-shadow: inset 0 6px 14px rgba(0,0,0,0.6), 0 0 14px 2px rgba(245,165,36,0.55); }
}
@media (prefers-reduced-motion: reduce) {
  .dig-tile.dug.partial { animation: none; }
}
/* Pastille plutot qu'un texte minuscule dans un coin (trop discret pour un
   chiffre qui compte vraiment pour la strategie du joueur - combien de
   tuiles il reste a trouver pour ce tresor). */
.dig-remaining {
  position: absolute;
  top: -6px;
  right: -6px;
  min-width: 18px;
  height: 18px;
  padding: 0 4px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 999px;
  background: #f5a524;
  color: #1a130d;
  font-size: 0.66rem;
  font-weight: 800;
  font-family: var(--font-display);
  box-shadow: 0 0 0 2px #1a130d;
}
/* Halo colore par palier de recompense (voir data-reward, DIG_REWARD_ICON
   dans jeux.js) : une mythique fraichement liberee doit se voir de loin,
   une simple poussiere reste discrete - jusque-la la meme icone flottante
   pour tous les paliers ne rendait pas justice a la rarete du tresor. */
.dig-treasure-glow {
  position: absolute;
  inset: 8%;
  border-radius: 50%;
  background: radial-gradient(circle, var(--dig-glow-color, #9aa0b4) 0%, transparent 70%);
  opacity: 0.5;
  filter: blur(2px);
}
.dig-treasure-icon { position: relative; font-size: 1.7rem; filter: drop-shadow(0 2px 4px rgba(0,0,0,0.5)); }
.dig-tile[data-reward="smallDust"] { --dig-glow-color: #9aa0b4; }
.dig-tile[data-reward="bigDust"] { --dig-glow-color: #22d3ee; }
.dig-tile[data-reward="booster"] { --dig-glow-color: #8b5cf6; }
.dig-tile[data-reward="card"] { --dig-glow-color: #f5a524; }
.dig-tile[data-reward="rareCard"] { --dig-glow-color: #ef4444; }
.dig-tile.dug.revealed {
  border-color: var(--dig-glow-color, var(--accent-2));
  box-shadow: 0 0 16px -4px color-mix(in srgb, var(--dig-glow-color, var(--accent-2)) 70%, transparent);
  animation: digRevealPop 0.35s cubic-bezier(.2,.9,.3,1.3);
}
@keyframes digRevealPop { 0% { transform: scale(0.6); } 60% { transform: scale(1.12); } 100% { transform: scale(1); } }
@media (max-width: 480px) {
  .dig-site { padding: 14px; }
  .dig-board { gap: 8px; }
  .dig-tile { font-size: 1.3rem; border-radius: 10px; }
}

/* Volee de terre au coup de pioche (spawnDigDustBurst, jeux.js) : meme
   calque partage que les particules de rarete/eclats de foil. */
.dig-dust-speck {
  position: fixed;
  width: 5px;
  height: 5px;
  border-radius: 1px;
  pointer-events: none;
  z-index: 420;
  animation: digDustFly 0.6s ease-out forwards;
}
@keyframes digDustFly {
  0% { transform: translate(0, 0) rotate(0deg); opacity: 1; }
  100% { transform: translate(var(--dx), var(--dy)) rotate(180deg); opacity: 0; }
}

/* Grille de bingo 3x3. */
.bingo-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; margin-top: 16px; max-width: 480px; margin-left: auto; margin-right: auto; }
.bingo-cell { position: relative; border-radius: 10px; border: 2px solid transparent; background: var(--bg-panel-2); overflow: hidden; opacity: 0.55; }
.bingo-cell.owned { opacity: 1; }
.bingo-cell img { width: 100%; aspect-ratio: 3/4; object-fit: cover; display: block; }
.bingo-cell-name { padding: 6px 8px; font-size: 0.72rem; text-align: center; }
.bingo-cell-check { position: absolute; top: 6px; right: 6px; font-size: 1.1rem; filter: drop-shadow(0 1px 3px rgba(0,0,0,0.6)); }

/* Modale de la roue : une vraie roue SVG qui tourne et s'arrete pile sur le
   lot renvoye par le serveur (voir spinWheelTo(), index.html), fermeture
   uniquement par clic explicite du joueur (jamais automatique). */
.wheel-modal {
  background: var(--bg-panel-solid);
  border: 1px solid var(--border);
  border-radius: 20px;
  padding: 32px;
  width: min(360px, 92vw);
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 18px;
  box-shadow: 0 30px 80px -20px rgba(0,0,0,0.7);
}
.wheel-container { position: relative; width: min(280px, 70vw); aspect-ratio: 1; }
.wheel-pointer {
  position: absolute;
  top: -6px;
  left: 50%;
  transform: translateX(-50%);
  font-size: 1.8rem;
  color: var(--accent-2);
  z-index: 2;
  filter: drop-shadow(0 2px 6px rgba(0,0,0,0.6));
}
.wheel-svg { width: 100%; height: 100%; display: block; filter: drop-shadow(0 10px 30px rgba(0,0,0,0.5)); }
#wheel-group { transform-origin: 100px 100px; transition: transform 4s cubic-bezier(.17,.67,.12,1); }
.wheel-seg { stroke: var(--bg-void); stroke-width: 2; }
.wheel-seg-small { fill: var(--accent); }
.wheel-seg-big { fill: var(--accent-2); }
.wheel-seg-booster { fill: var(--rarity-legendaire); }
.wheel-seg-key { fill: var(--accent-3); }
.wheel-seg-jackpot { fill: url(#wheelJackpotGradient); }
.wheel-icon-label { text-anchor: middle; font-size: 15px; }
.wheel-icon-label-big { font-size: 18px; }
.wheel-icon-label-tiny { font-size: 9px; }
.wheel-icon-label-jackpot { font-size: 17px; }
.wheel-hub { fill: var(--bg-panel-solid); stroke: var(--border); stroke-width: 3; }
/* Traine lumineuse au pointeur pendant la rotation (embellissement
   2026-09-30) : un anneau conique qui tourne vite en continu tant que
   .spinning est present, pour donner une sensation d'energie qui monte
   sans avoir a synchroniser au ralentissement reel de la roue (CSS pur). */
.wheel-pointer-glow {
  position: absolute;
  top: -14px;
  left: 50%;
  width: 34px;
  height: 34px;
  margin-left: -17px;
  border-radius: 50%;
  background: conic-gradient(from 0deg, transparent, var(--accent-2), transparent 60%);
  opacity: 0;
  z-index: 1;
  transition: opacity 0.3s ease;
}
.wheel-pointer-glow.spinning { opacity: 0.9; animation: wheel-pointer-spin 0.6s linear infinite; }
@keyframes wheel-pointer-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .wheel-pointer-glow.spinning { animation: none; opacity: 0.5; }
}
.wheel-modal-result {
  min-height: 1.5em;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 1.1rem;
  background: linear-gradient(90deg, var(--accent), var(--accent-2));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

/* Succes / achievements (accueil). */
.achievements-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 12px;
  margin-top: 12px;
}
.achievement-tile {
  display: flex;
  align-items: center;
  gap: 12px;
  background: var(--bg-panel-2);
  border: 1px solid var(--border-soft);
  border-radius: 12px;
  padding: 12px 14px;
  opacity: 0.55;
  filter: grayscale(0.6);
}
.achievement-tile.unlocked { opacity: 1; filter: none; border-color: rgba(245,165,36,0.4); }
/* "Presque debloque" (QoL 2026-09-30) : il n'en manque qu'un - liseré discret
   qui pulse, sans crier aussi fort qu'un succes deja obtenu. */
.achievement-tile.almost-done { border-color: var(--accent-2); animation: achievementAlmostPulse 2.2s ease-in-out infinite; }
@keyframes achievementAlmostPulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(34,211,238,0.3); }
  50% { box-shadow: 0 0 0 5px rgba(34,211,238,0); }
}
@media (prefers-reduced-motion: reduce) {
  .achievement-tile.almost-done { animation: none; }
}

/* Boutique de badges cosmetiques (page profil, "Mes badges"). */
.badge-shop-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 12px;
  margin-top: 12px;
}
.badge-shop-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  text-align: center;
  background: var(--bg-panel-2);
  border: 1px solid var(--border-soft);
  border-radius: 12px;
  padding: 16px 12px;
}
.badge-shop-tile.selected { border-color: var(--accent-2); box-shadow: 0 0 0 2px var(--accent-2); }
.badge-shop-tile:not(.owned) { opacity: 0.7; }
.badge-shop-icon { font-size: 2rem; line-height: 1; }
/* Achat/equipement (embellissement 2026-09-30) : la vignette concernee joue
   une entree marquee au prochain rendu (voir profile.html loadMyBadges). */
.badge-shop-tile.just-buy { animation: badgeUnlockPop 0.6s cubic-bezier(.2,.9,.3,1.4); }
.badge-shop-tile.just-select { animation: badgeSelectPulse 0.5s ease; }
@keyframes badgeUnlockPop {
  0% { transform: scale(0.7); box-shadow: 0 0 0 0 rgba(139,92,246,0); }
  50% { transform: scale(1.08); box-shadow: 0 0 24px 4px rgba(139,92,246,0.55); }
  100% { transform: scale(1); box-shadow: 0 0 0 0 rgba(139,92,246,0); }
}
@keyframes badgeSelectPulse {
  0% { box-shadow: 0 0 0 0 rgba(34,211,238,0.6); }
  100% { box-shadow: 0 0 0 10px rgba(34,211,238,0); }
}
@media (prefers-reduced-motion: reduce) {
  .badge-shop-tile.just-buy, .badge-shop-tile.just-select { animation: none; }
}
.badge-icon-img { width: 28px; height: 28px; object-fit: contain; vertical-align: middle; border-radius: 6px; }

/* Selecteurs de cartes du bingo (page admin). */
.bingo-admin-selects {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: 10px;
  margin: 10px 0;
}
.bingo-admin-selects label { display: flex; flex-direction: column; gap: 4px; font-size: 0.78rem; }
.badge-shop-name { font-family: var(--font-display); font-weight: 700; font-size: 0.86rem; }
.badge-shop-desc { color: var(--text-dim); font-size: 0.72rem; }
.badge-shop-status { font-size: 0.76rem; font-weight: 700; color: var(--accent-2); }
.badge-shop-tile button { width: 100%; padding: 6px; font-size: 0.78rem; }
.achievement-icon { font-size: 1.8rem; flex-shrink: 0; }
.achievement-info { flex: 1; min-width: 0; }
.achievement-name { font-family: var(--font-display); font-weight: 700; font-size: 0.88rem; }
.achievement-desc { color: var(--text-dim); font-size: 0.74rem; margin-top: 2px; }
.achievement-progress-track {
  height: 5px;
  border-radius: 999px;
  background: var(--bg-panel-solid);
  overflow: hidden;
  margin-top: 6px;
}
.achievement-progress-fill { height: 100%; background: linear-gradient(90deg, var(--accent), var(--accent-2)); }
.achievement-progress-label { font-size: 0.7rem; color: var(--text-dim); margin-top: 3px; font-variant-numeric: tabular-nums; }
.achievement-tile.unlocked .achievement-progress-label { color: var(--rarity-legendaire); font-weight: 700; }

input[type="text"] {
  background: var(--bg-panel-2);
  border: 1px solid var(--border-soft);
  color: var(--text);
  padding: 12px 16px;
  border-radius: 10px;
  font-size: 1rem;
  font-family: var(--font-body);
  transition: border-color 0.15s;
}
input[type="text"]:focus { outline: none; border-color: var(--accent); }

/* Boutons "bouton-poussoir" de borne d'arcade : bordure epaisse + une
   vraie ombre 3D (pas juste un glow) qui s'ecrase au clic, comme un vrai
   bouton physique plutot qu'un bouton d'appli plat. */
button, .btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  vertical-align: middle;
  background: linear-gradient(135deg, var(--accent), var(--accent-2));
  color: white;
  text-shadow: 0 1px 2px rgba(0,0,0,0.35);
  border: 2px solid rgba(0,0,0,0.3);
  padding: 11px 24px;
  border-radius: 14px;
  font-size: 1rem;
  font-family: var(--font-display);
  font-weight: 400;
  letter-spacing: 0.01em;
  cursor: pointer;
  box-shadow: 0 4px 0 rgba(0,0,0,0.35), 0 10px 22px -10px rgba(139, 92, 246, 0.55);
  transition: transform 0.1s ease, box-shadow 0.1s ease, filter 0.15s;
  overflow: hidden;
  isolation: isolate;
  -webkit-tap-highlight-color: transparent;
}
button::before, .btn::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(120deg, transparent 30%, rgba(255,255,255,0.35) 50%, transparent 70%);
  transform: translateX(-120%);
  transition: transform 0.5s ease;
}
button:hover:not(:disabled)::before, .btn:hover::before { transform: translateX(120%); }
button:hover:not(:disabled) { transform: translateY(-1px); filter: brightness(1.08); }
button:active:not(:disabled) {
  transform: translateY(3px);
  box-shadow: 0 1px 0 rgba(0,0,0,0.35), 0 4px 10px -6px rgba(139, 92, 246, 0.5);
  filter: brightness(0.97);
}
button:disabled { opacity: 0.4; cursor: not-allowed; box-shadow: none; }

.btn-ghost {
  background: transparent;
  border: 2px solid var(--border-soft);
  color: var(--text-dim);
  text-shadow: none;
  padding: 6px 14px;
  border-radius: 10px;
  font-size: 0.82rem;
  box-shadow: none;
}
.btn-ghost:active:not(:disabled) { transform: translateY(1px); box-shadow: none; }

.btn-secondary {
  background: var(--bg-panel-2);
  border: 2px solid var(--border-soft);
  color: var(--text);
  text-shadow: none;
  box-shadow: 0 3px 0 rgba(0,0,0,0.25);
}
.btn-secondary:active:not(:disabled) { box-shadow: 0 1px 0 rgba(0,0,0,0.25); }

.actions { display: flex; gap: 12px; margin-top: 22px; flex-wrap: wrap; }

/* ========================================================================
   Ouverture de booster
   ======================================================================== */

/* Compteur "credits de borne d'arcade" (page boosters) : le nombre lui-meme
   ressort fort, le mot "disponible(s)" reste discret a cote. */
.credit-counter {
  display: flex;
  align-items: baseline;
  gap: 8px;
  font-family: var(--font-display);
  font-size: 0.92rem;
  color: var(--text-dim);
}
.credit-value {
  font-size: 1.8rem;
  background: linear-gradient(90deg, var(--accent), var(--accent-2));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  font-variant-numeric: tabular-nums;
}

/* Le booster lui-meme */
.booster-stage {
  position: relative;
  width: 220px;
  height: 300px;
  display: flex;
  align-items: center;
  justify-content: center;
  perspective: 800px;
}

.booster-pack {
  width: 200px;
  height: 280px;
  border-radius: 18px;
  position: relative;
  cursor: pointer;
  background:
    linear-gradient(135deg, rgba(255,255,255,0.18), transparent 40%),
    linear-gradient(160deg, var(--accent) 0%, var(--accent-dark) 45%, var(--accent-2) 100%);
  box-shadow:
    0 0 0 1.5px rgba(255,255,255,0.15) inset,
    0 25px 60px -18px rgba(139, 92, 246, 0.65);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  color: white;
  font-family: var(--font-display);
  text-align: center;
  padding: 20px;
  animation: boosterFloat 3.4s ease-in-out infinite;
  transition: transform 0.25s ease, filter 0.25s ease;
  overflow: hidden;
  user-select: none;
}

.booster-pack::after {
  content: "";
  position: absolute;
  top: -50%;
  left: -60%;
  width: 40%;
  height: 200%;
  background: linear-gradient(120deg, transparent, rgba(255,255,255,0.35), transparent);
  transform: rotate(20deg);
  animation: boosterSheen 3.2s ease-in-out infinite;
}

.booster-pack .booster-title {
  font-size: 1.15rem;
  font-weight: 800;
  text-shadow: 0 2px 10px rgba(0,0,0,0.3);
}

.booster-pack .booster-sub {
  font-size: 0.78rem;
  opacity: 0.85;
  margin-top: 6px;
  font-family: var(--font-body);
  font-weight: 500;
}

.booster-pack .booster-emoji {
  font-size: 2.4rem;
  margin-bottom: 8px;
  filter: drop-shadow(0 6px 10px rgba(0,0,0,0.35));
}

.booster-pack:hover:not(.locked):not(.charging):not(.bursting) {
  transform: translateY(-6px) scale(1.03);
  filter: brightness(1.08);
}

/* Jauge de charge visible autour du pack (#charge-ring, ouverture.html) :
   jusqu'ici la charge de 750ms avant dechirure n'etait qu'un tremblement CSS
   sans aucun repere de progression. L'anneau se remplit (stroke-dashoffset)
   exactement sur la duree du wait() correspondant (voir startOpening,
   opening.js), qui pose --charge-ring-color et bascule .active. */
.charge-ring {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  /* z-index necessaire : #booster-pack est un enfant flex de .booster-stage,
     et un flex item sans z-index peint comme un element positionne
     z-index:auto - au meme niveau que cet anneau (lui aussi position:absolute
     sans z-index), l'ordre DOM tranche alors et le pack (place APRES
     l'anneau en HTML) le recouvrait entierement avec son fond opaque. */
  z-index: 2;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.25s ease;
  transform: rotate(-90deg);
}
.charge-ring-track { fill: none; stroke: rgba(255,255,255,0.12); stroke-width: 4; }
.charge-ring-fill {
  fill: none;
  stroke: var(--charge-ring-color, #8b5cf6);
  stroke-width: 4;
  stroke-linecap: round;
  stroke-dasharray: 629;
  stroke-dashoffset: 629;
  filter: drop-shadow(0 0 6px var(--charge-ring-color, #8b5cf6));
}
.charge-ring.active { opacity: 1; }
.charge-ring.active .charge-ring-fill { transition: stroke-dashoffset 0.75s linear; stroke-dashoffset: 0; }

/* --pack-rx/--pack-ry sont poses en JS (attachPackTilt, opening.js) et
   restent a 0deg par defaut : le tilt s'ajoute a l'animation de flottement
   sans la remplacer (une propriete personnalisee reste lisible en direct
   dans des keyframes, contrairement a un transform fixe qu'une animation
   ecraserait). */
@keyframes boosterFloat {
  0%, 100% { transform: translateY(0) rotate(-1deg) rotateX(var(--pack-rx, 0deg)) rotateY(var(--pack-ry, 0deg)); }
  50% { transform: translateY(-10px) rotate(1deg) rotateX(var(--pack-rx, 0deg)) rotateY(var(--pack-ry, 0deg)); }
}

@keyframes boosterSheen {
  0% { left: -60%; }
  100% { left: 120%; }
}

.booster-pack.locked {
  cursor: not-allowed;
  filter: grayscale(0.85) brightness(0.55);
  animation: none;
}

.booster-pack.charging {
  animation: boosterCharge 0.5s ease-in-out infinite;
  /* --pity-ratio (0-1) pose en JS a l'ouverture de la modale (openModalFor) :
     plus la pity de cette extension approche du palier legendaire garanti,
     plus le pack rougeoie fort des la charge - avant meme de savoir ce qui va
     sortir. Ecrase par .charging-epique/-legendaire/-mythique ci-dessous des
     que le meilleur tirage du lot est connu (definis plus loin dans ce
     fichier, ils gagnent a specificite egale). */
  box-shadow: 0 0 calc(20px + 55px * var(--pity-ratio, 0)) rgba(245, 165, 36, calc(0.12 + 0.5 * var(--pity-ratio, 0)));
}

@keyframes boosterCharge {
  0%, 100% { transform: translate(0, 0) scale(1.02) rotate(0deg); }
  20% { transform: translate(-4px, 0) scale(1.04) rotate(-2deg); }
  40% { transform: translate(4px, 0) scale(1.04) rotate(2deg); }
  60% { transform: translate(-3px, 0) scale(1.05) rotate(-1deg); }
  80% { transform: translate(3px, 0) scale(1.05) rotate(1deg); }
}

/* Tremblement intensifie quand le meilleur tirage du lot est deja connu
   (spoiler discret cote gacha, avant la dechirure du pack). */
.booster-pack.charging-rare { box-shadow: 0 0 26px rgba(59,130,246,0.55); }
.booster-pack.charging-epique { animation: boosterChargeEpique 0.35s ease-in-out infinite; box-shadow: 0 0 40px rgba(168,85,247,0.6); }
.booster-pack.charging-legendaire { animation: boosterChargeLegendaire 0.22s ease-in-out infinite; box-shadow: 0 0 60px rgba(245,165,36,0.8); }
.booster-pack.charging-mythique { animation: boosterChargeMythique 0.16s ease-in-out infinite; box-shadow: 0 0 75px rgba(239,68,68,0.9); }
@keyframes boosterChargeEpique {
  0%, 100% { transform: translate(0, 0) scale(1.04) rotate(0deg); }
  25% { transform: translate(-6px, 0) scale(1.06) rotate(-3deg); }
  50% { transform: translate(6px, 0) scale(1.07) rotate(3deg); }
  75% { transform: translate(-5px, 0) scale(1.06) rotate(-2deg); }
}
@keyframes boosterChargeLegendaire {
  0%, 100% { transform: translate(0, 0) scale(1.06) rotate(0deg); }
  25% { transform: translate(-9px, 0) scale(1.1) rotate(-4deg); }
  50% { transform: translate(9px, 0) scale(1.12) rotate(4deg); }
  75% { transform: translate(-7px, 0) scale(1.1) rotate(-3deg); }
}
@keyframes boosterChargeMythique {
  0%, 100% { transform: translate(0, 0) scale(1.08) rotate(0deg); }
  25% { transform: translate(-11px, 0) scale(1.13) rotate(-5deg); }
  50% { transform: translate(11px, 0) scale(1.15) rotate(5deg); }
  75% { transform: translate(-9px, 0) scale(1.13) rotate(-4deg); }
}

/* Choix x1/x5 (modale d'ouverture) */
.quantity-picker { display: flex; gap: 10px; }
.quantity-picker button.active { outline: 2px solid var(--accent-2); }
.quantity-picker button:disabled { opacity: 0.4; cursor: not-allowed; }

.burst-flash {
  position: fixed;
  inset: 0;
  background: radial-gradient(circle at center, rgba(255,255,255,0.9), transparent 60%);
  pointer-events: none;
  opacity: 0;
  z-index: 50;
}
.burst-flash.flash-active {
  animation: flashPulse 0.9s ease-out forwards;
}
@keyframes flashPulse {
  0% { opacity: 0; }
  35% { opacity: 0.9; }
  60% { opacity: 0.75; }
  100% { opacity: 0; }
}

.booster-hint {
  font-size: 0.9rem;
  color: var(--text-dim);
  min-height: 1.3em;
}

/* --- Grille de reveal --- */
.reveal-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 22px;
  width: 100%;
  margin-top: 10px;
}

.card {
  position: relative;
  perspective: 1200px;
  width: 100%;
  /* Illustration entiere en 3:4 (comme dans la collection) + bandeau du nom. */
  aspect-ratio: 30 / 49;
  opacity: 0;
  transform: translateY(24px) scale(0.9);
  animation: cardAppear 0.5s ease forwards;
}

@keyframes cardAppear {
  to { opacity: 1; transform: translateY(0) scale(1); }
}

.card-inner {
  position: relative;
  width: 100%;
  height: 100%;
  transform-style: preserve-3d;
  transition: transform 0.8s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.card.revealed .card-inner { transform: rotateY(180deg); }

.card-face {
  position: absolute;
  inset: 0;
  border-radius: 14px;
  backface-visibility: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  border: 2px solid var(--border-soft);
}

.card-back {
  background:
    linear-gradient(135deg, rgba(255,255,255,0.08), transparent 50%),
    linear-gradient(160deg, var(--bg-panel-2), #0d0e24);
  font-size: 2.2rem;
  color: var(--accent-2);
  font-family: var(--font-display);
}
/* Lueur avant meme le flip, plus marquee selon la rarete (jamais sur les
   communes) - fait deviner l'intensite du tirage sans reveler la carte,
   a la maniere du "shine" de pack dans TCG Pocket. */
.card-back-shine { box-shadow: inset 0 0 0 2px var(--shine-color, transparent); }
.card-back-shine::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: radial-gradient(circle at 50% 40%, var(--shine-color, transparent), transparent 70%);
  mix-blend-mode: screen;
}

.card-front {
  transform: rotateY(180deg);
  background: var(--bg-panel-2);
  flex-direction: column;
  position: relative;
}

.card-front .card-art { width: 100%; aspect-ratio: 3 / 4; flex: none; }
.card-front img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* sweep holographique sur reveal */
.card-front::after {
  content: "";
  position: absolute;
  top: -60%;
  left: -80%;
  width: 55%;
  height: 220%;
  background: linear-gradient(120deg, transparent, rgba(255,255,255,0.55), transparent);
  transform: rotate(20deg);
  opacity: 0;
}
.card.revealed .card-front::after {
  animation: holoSweep 1.1s ease-out 0.55s;
}
@keyframes holoSweep {
  0% { left: -80%; opacity: 0; }
  15% { opacity: 0.9; }
  60% { opacity: 0.5; }
  100% { left: 130%; opacity: 0; }
}

.card-info {
  padding: 8px 8px 10px;
  text-align: center;
  width: 100%;
}

.card-name { font-size: 0.86rem; font-weight: 700; font-family: var(--font-display); }

/* Traitement typographique dedie aux raretes fortes : degrade rouge anime
   pour mythique (la plus haute, au-dessus de legendaire), dorure animee pour
   legendaire, neon pour epique - sur toute carte affichant data-rarity
   (grille, pile de reveal, modale, profil), pas de JS supplementaire. */
[data-rarity="mythique"] .card-name,
[data-rarity="mythique"] .card-modal-name,
[data-rarity="mythique"] .pull-log-name {
  background: linear-gradient(120deg, #ef4444, #ffb4b4, #ef4444);
  background-size: 200% auto;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  animation: legendaryNameShimmer 3s linear infinite;
}
[data-rarity="legendaire"] .card-name,
[data-rarity="legendaire"] .card-modal-name,
[data-rarity="legendaire"] .pull-log-name {
  background: linear-gradient(120deg, #f5a524, #ffe9b8, #f5a524);
  background-size: 200% auto;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  animation: legendaryNameShimmer 3s linear infinite;
}
@keyframes legendaryNameShimmer {
  to { background-position: -200% center; }
}
[data-rarity="epique"] .card-name,
[data-rarity="epique"] .card-modal-name,
[data-rarity="epique"] .pull-log-name {
  color: #d8b4fe;
  text-shadow: 0 0 10px rgba(168,85,247,0.7);
}
.card-artist { font-size: 0.7rem; color: var(--text-dim); margin-top: 2px; }

.rarity-badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 0.62rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  padding: 3px 9px 3px 6px;
  border-radius: 999px;
  margin-top: 6px;
}
/* Petit "gemme" avant le texte, plutot qu'une pastille plate uniforme. */
.rarity-badge::before {
  content: "";
  width: 7px;
  height: 7px;
  background: currentColor;
  clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
  flex-shrink: 0;
}

/* Le 2e calque (1.5px/3px, decale, opaque) simule une tranche de carte
   physique en plus du halo de rarete. */
.card[data-rarity="commune"] .card-face { box-shadow: 0 0 16px rgba(154,160,180,0.35), 1.5px 1.5px 0 rgba(0,0,0,0.35), 3px 3px 0 -1px rgba(0,0,0,0.22); }
.card[data-rarity="rare"] .card-face { box-shadow: 0 0 22px rgba(59,130,246,0.55), 1.5px 1.5px 0 rgba(0,0,0,0.35), 3px 3px 0 -1px rgba(0,0,0,0.22); }
.card[data-rarity="epique"] .card-face { box-shadow: 0 0 28px rgba(168,85,247,0.6), 1.5px 1.5px 0 rgba(0,0,0,0.35), 3px 3px 0 -1px rgba(0,0,0,0.22); }
.card[data-rarity="legendaire"] .card-face {
  box-shadow: 0 0 40px rgba(245,165,36,0.75), 1.5px 1.5px 0 rgba(0,0,0,0.35), 3px 3px 0 -1px rgba(0,0,0,0.22);
  animation: legendaryPulse 1.6s ease-in-out infinite;
}
.card[data-rarity="mythique"] .card-face {
  box-shadow: 0 0 50px rgba(239,68,68,0.85), 1.5px 1.5px 0 rgba(0,0,0,0.35), 3px 3px 0 -1px rgba(0,0,0,0.22);
  animation: mythiquePulse 1.4s ease-in-out infinite;
}

@keyframes legendaryPulse {
  0%, 100% { box-shadow: 0 0 40px rgba(245,165,36,0.75); }
  50% { box-shadow: 0 0 60px rgba(245,165,36,1); }
}
@keyframes mythiquePulse {
  0%, 100% { box-shadow: 0 0 50px rgba(239,68,68,0.85); }
  50% { box-shadow: 0 0 75px rgba(239,68,68,1); }
}

/* --- Collection --- */
.progress-bar {
  width: 100%;
  height: 10px;
  background: var(--bg-panel-2);
  border-radius: 999px;
  overflow: hidden;
  margin: 10px 0 4px;
}
.progress-bar-fill {
  height: 100%;
  background: linear-gradient(90deg, var(--accent), var(--accent-2));
  transition: width 0.6s ease;
}

.filters { display: flex; gap: 10px; margin: 20px 0; flex-wrap: wrap; }
.filters button.active { outline: 2px solid var(--accent-2); }
/* Liseré degrade sobre sur le filtre "Legendaire" (un contour tournant en
   continu attirerait l'oeil en permanence, meme sans interaction : trop
   voyant pour un simple bouton de filtre). */
.filters button[data-filter="legendaire"] {
  border: 1.5px solid transparent;
  background:
    linear-gradient(var(--bg-panel-2), var(--bg-panel-2)) padding-box,
    linear-gradient(135deg, var(--rarity-legendaire), #ffd166) border-box;
}
.filters button[data-filter="mythique"] {
  border: 1.5px solid transparent;
  background:
    linear-gradient(var(--bg-panel-2), var(--bg-panel-2)) padding-box,
    linear-gradient(135deg, var(--rarity-mythique), #ff9d9d) border-box;
}

.collection-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 18px;
}

.collection-card {
  border-radius: 14px;
  border: 2px solid var(--border-soft);
  overflow: hidden;
  background: var(--bg-panel-2);
  transition: transform 0.25s ease, box-shadow 0.25s ease, border-color 0.25s ease;
}

.collection-card:not(.locked):hover {
  transform: translateY(-6px) scale(1.03) rotateX(var(--rx, 3deg)) rotateY(var(--ry, 0deg));
  box-shadow: 0 18px 34px -14px color-mix(in srgb, var(--accent) 50%, transparent);
  border-color: var(--border);
}

.binder-accent-row { display: flex; align-items: center; gap: 8px; margin: 10px 0 4px; }
.binder-accent-label { font-size: 0.78rem; color: var(--text-dim); }
.binder-swatch {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  border: 2px solid var(--border-soft);
  background: var(--swatch-color, #8b5cf6);
  padding: 0;
  box-shadow: none;
  cursor: pointer;
}
.binder-swatch.active { border-color: var(--text); box-shadow: 0 0 0 2px var(--swatch-color, #8b5cf6); }

/* Pochettes de cartes (sleeves) : debloquees par niveau de profil. Chacune
   pose une bordure/texture bien visible - jamais juste un halo discret,
   c'est precisement ce qui rendait la v1 invisible (retour utilisateur). */
.sleeve-row { display: flex; align-items: center; gap: 8px; margin: 8px 0 4px; flex-wrap: wrap; }
.sleeve-swatch {
  font-size: 0.76rem;
  padding: 4px 12px;
  border-radius: 999px;
  border: 1px solid var(--border-soft);
  background: var(--bg-panel-2);
  color: var(--text-dim);
  cursor: pointer;
  box-shadow: none;
}
.sleeve-swatch.active { border-color: var(--accent-2); color: var(--text); }
.sleeve-swatch.locked { opacity: 0.5; cursor: not-allowed; }
.sleeve-swatch.locked::after { content: " \1F512"; }

body[data-sleeve] .collection-card:not(.locked) { position: relative; }
body[data-sleeve] .collection-card:not(.locked)::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  border-radius: inherit;
}
body[data-sleeve="neon"] .collection-card:not(.locked) {
  border-width: 2px;
  border-color: #22d3ee !important;
  box-shadow: 0 0 0 2px rgba(139, 92, 246, 0.55), 0 0 22px rgba(34, 211, 238, 0.65);
  animation: sleeve-neon-pulse 2.4s ease-in-out infinite;
}
@keyframes sleeve-neon-pulse {
  0%, 100% { box-shadow: 0 0 0 2px rgba(139, 92, 246, 0.55), 0 0 14px rgba(34, 211, 238, 0.5); }
  50% { box-shadow: 0 0 0 2px rgba(34, 211, 238, 0.7), 0 0 26px rgba(139, 92, 246, 0.75); }
}
body[data-sleeve="vintage"] .collection-card:not(.locked) {
  border-width: 2px;
  border-color: #c9a876 !important;
}
body[data-sleeve="vintage"] .collection-card:not(.locked) img { filter: sepia(0.65) contrast(1.05) brightness(0.96); }
body[data-sleeve="vintage"] .collection-card:not(.locked)::before {
  box-shadow: inset 0 0 26px 10px rgba(60,40,15,0.55);
  background-image: repeating-radial-gradient(circle at 30% 20%, rgba(255,235,200,0.08) 0 2px, transparent 2px 5px);
}
body[data-sleeve="carbone"] .collection-card:not(.locked) {
  border-width: 2px;
  border-color: #444 !important;
}
body[data-sleeve="carbone"] .collection-card:not(.locked)::before {
  background-image:
    repeating-linear-gradient(45deg, rgba(255,255,255,0.10) 0 3px, transparent 3px 9px),
    repeating-linear-gradient(-45deg, rgba(0,0,0,0.35) 0 3px, transparent 3px 9px);
  mix-blend-mode: overlay;
}
@media (prefers-reduced-motion: reduce) {
  body[data-sleeve="neon"] .collection-card:not(.locked) { animation: none; }
}

.collection-card img { width: 100%; aspect-ratio: 3/4; object-fit: cover; display: block; }

.collection-card .card-info { padding: 8px 8px; }

.count-badge {
  font-size: 0.7rem;
  color: var(--text-dim);
  font-weight: 600;
}

.card-quick-action {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 0.68rem;
  font-weight: 700;
  font-family: var(--font-body);
  background: var(--bg-panel-solid);
  border: 1px solid var(--border-soft);
  color: var(--text-dim);
  border-radius: 999px;
  cursor: pointer;
}
.quick-craft-btn {
  width: 100%;
  justify-content: center;
  margin-top: 6px;
  padding: 5px 10px;
  color: var(--accent-3);
  border-color: var(--accent-3);
}
.quick-craft-btn:hover { background: rgba(244, 114, 182, 0.12); }
.quick-actions-row { display: flex; justify-content: center; gap: 6px; margin-top: 6px; }
.quick-actions-row .card-quick-action {
  width: 26px;
  height: 26px;
  justify-content: center;
  font-size: 0.8rem;
  padding: 0;
}
.quick-actions-row .card-quick-action:hover { color: var(--text); border-color: var(--border); background: rgba(255,255,255,0.06); }
.quick-showcase-btn.active { color: var(--accent-2); border-color: var(--accent-2); }

/* ========================================================================
   Groupes d'extension repliables (collection) : le compteur N/X vit dans le
   titre plutot que dans une barre de progression separee.
   ======================================================================== */
.collection-ext-group + .collection-ext-group { margin-top: 10px; }
.collection-extension-heading {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 28px 0 14px;
  cursor: pointer;
  user-select: none;
}
/* Cible le premier groupe uniquement (pas juste le premier enfant de CHAQUE
   groupe, ce qui annulait la marge de toutes les extensions par erreur -
   voir la marge ajoutee ci-dessus entre groupes, qui compense desormais). */
.collection-ext-group:first-child .collection-extension-heading { margin-top: 0; }
.ext-fold-toggle {
  background: transparent;
  border: none;
  box-shadow: none;
  padding: 2px;
  color: var(--text-dim);
  font-size: 0.85rem;
  cursor: pointer;
  transition: transform 0.2s ease;
  transform: none !important;
}
.collection-ext-group.collapsed .ext-fold-toggle { transform: rotate(-90deg) !important; }
.ext-heading-name { flex: 1; }
.ext-heading-count {
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
  background: var(--bg-panel-2);
  border-radius: 999px;
  padding: 3px 10px;
}
.collection-ext-group.collapsed .collection-grid { display: none; }

.empty-state { color: var(--text-dim); text-align: center; padding: 40px 0; }
.empty-state::before {
  content: "\1F5C3";
  display: block;
  font-size: 2rem;
  margin-bottom: 8px;
  opacity: 0.7;
}
/* Message de fonctionnalite verrouillee (niveau insuffisant, voir
   renderFeatureLockedMessage dans main.js) : porte deja son propre
   pictogramme dans le texte, pas besoin de celui d'.empty-state. */
.empty-state.feature-locked { max-width: 480px; margin: 40px auto; line-height: 1.6; }
.empty-state.feature-locked::before { content: none; }

/* Squelette de chargement (2026-09-30) : remplace le texte statique
   "Chargement..." par une barre animee, plus doux pour l'oeil qu'un texte
   fige (et evite l'impression de page bloquee, voir le bug corrige sur le
   coffre-fort). S'applique directement sur l'element qui contient deja le
   texte "Chargement..." - le rend transparent et anime son propre fond ; le
   JS appelant retire cette classe des que le vrai contenu est pose. */
.skeleton-line {
  display: inline-block;
  min-width: 90px;
  max-width: 100%;
  height: 0.85em;
  border-radius: 4px;
  color: transparent !important;
  background: linear-gradient(90deg, var(--bg-panel-2) 25%, var(--bg-panel-solid) 50%, var(--bg-panel-2) 75%);
  background-size: 200% 100%;
  animation: skeletonShimmer 1.4s ease-in-out infinite;
}
@keyframes skeletonShimmer {
  0% { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}
@media (prefers-reduced-motion: reduce) {
  .skeleton-line { animation: none; opacity: 0.6; }
}

.error-box {
  background: #3b1220;
  border: 1px solid #7f1d3a;
  color: #fda4af;
  padding: 12px 16px;
  border-radius: 10px;
  margin-top: 16px;
}

/* --- Avatar Discord dans le header --- */
.avatar {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  border: 1.5px solid var(--border);
  object-fit: cover;
}

/* --- Formulaire de code (redeem.html) --- */
/* Le code d'evenement a l'allure d'un ticket/coupon a gratter, pas d'un
   simple champ de formulaire perdu dans la page. */
.redeem-card {
  display: flex;
  align-items: center;
  gap: 20px;
  background: linear-gradient(135deg, rgba(139, 92, 246, 0.16), rgba(34, 211, 238, 0.1));
  border: 2px dashed var(--border);
  border-radius: 16px;
  padding: 22px 24px;
  margin: 20px 0;
}
.redeem-icon { font-size: 2.6rem; flex-shrink: 0; line-height: 1; }
.redeem-card .code-form { flex: 1; margin: 0; }

/* Serrure cachee (easter egg cartes secretes) : 5 clics sur le cadeau le
   decalent sur le cote et revelent une serrure, consommee via une clef
   trouvee en fouillant (voir jeux.js/dig.json et unlock-secret.json) -
   remplace l'ancien Konami code. */
.redeem-icon-zone { position: relative; display: flex; align-items: center; flex-shrink: 0; }
.redeem-icon-zone .redeem-icon { transition: transform 0.4s ease; cursor: default; user-select: none; }
.redeem-icon-zone .redeem-icon.gift-shifted { transform: translateX(34px); }
.redeem-lock-icon {
  font-size: 2.2rem;
  line-height: 1;
  position: absolute;
  left: 0;
  opacity: 0;
  transform: scale(0.4) rotate(-15deg);
  pointer-events: none;
  transition: opacity 0.4s ease, transform 0.4s ease;
  filter: drop-shadow(0 0 8px rgba(139, 92, 246, 0.6));
}
.redeem-lock-icon.lock-revealed {
  opacity: 1;
  transform: scale(1) rotate(0deg);
  pointer-events: auto;
  cursor: pointer;
}
@media (prefers-reduced-motion: reduce) {
  .redeem-icon-zone .redeem-icon, .redeem-lock-icon { transition: none; }
}

.code-form {
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
  margin: 16px 0;
}
.code-form input[type="text"] {
  flex: 1;
  min-width: 200px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  font-family: var(--font-display);
  font-weight: 700;
}

.reward-banner {
  background: linear-gradient(135deg, rgba(139, 92, 246, 0.22), rgba(34, 211, 238, 0.16));
  border: 1px solid var(--border);
  color: var(--text);
  padding: 14px 18px;
  border-radius: 12px;
  font-family: var(--font-display);
  font-weight: 700;
  margin: 16px 0;
}

/* ========================================================================
   Blocs repliables de la page admin (<details>/<summary> natifs : clavier
   et lecteurs d'ecran geres gratuitement, pas de JS necessaire). Seul le
   bloc "Codes evenement" reste ouvert par defaut, en haut - c'est celui
   utilise en continu pendant un evenement, les autres sont consultes plus
   rarement.
   ======================================================================== */
.admin-section {
  background: var(--bg-panel-2);
  border: 1px solid var(--border-soft);
  border-radius: 14px;
  margin: 18px 0;
  overflow: hidden;
}
.admin-section.danger-zone { border-color: rgba(251,113,133,0.5); }
.admin-section.danger-zone summary h2 { color: var(--danger); }
.reset-scope-row { display: flex; flex-wrap: wrap; align-items: center; gap: 16px; margin: 14px 0; }
.reset-scope-row #reset-target-pseudo {
  background: var(--bg-panel-2);
  border: 1px solid var(--border-soft);
  color: var(--text);
  padding: 8px 12px;
  border-radius: 10px;
  font-size: 0.9rem;
  font-family: var(--font-body);
}
.admin-section summary {
  list-style: none;
  cursor: pointer;
  padding: 16px 20px;
  display: flex;
  align-items: center;
  user-select: none;
}
.admin-section summary::-webkit-details-marker { display: none; }
.admin-section summary h2 { margin: 0; flex: 1; font-size: 1.05rem; }
.admin-section summary::after {
  content: "\25BE";
  color: var(--text-dim);
  font-size: 0.8rem;
  transition: transform 0.2s ease;
  margin-left: 10px;
}
.admin-section[open] summary::after { transform: rotate(180deg); }
.admin-section:not([open]) summary { border-bottom: none; }
.admin-section[open] summary { border-bottom: 1px solid var(--border-soft); }
.admin-section-body { padding: 4px 20px 20px; }
.admin-section-body h3 { font-size: 0.95rem; margin: 18px 0 10px; }
.admin-section-body h3:first-child { margin-top: 4px; }

/* --- Formulaires generiques (trade / admin) --- */
.admin-form {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 16px;
  margin: 20px 0;
  align-items: end;
}
.admin-form label {
  display: flex;
  flex-direction: column;
  gap: 6px;
  font-size: 0.85rem;
  color: var(--text-dim);
}

.admin-form select,
.admin-form input[type="text"],
.admin-form input[type="number"],
.trade-compose-form select {
  background: var(--bg-panel-2);
  border: 1px solid var(--border-soft);
  color: var(--text);
  padding: 10px 12px;
  border-radius: 10px;
  font-size: 0.95rem;
  font-family: var(--font-body);
}
.admin-form button { grid-column: -2 / -1; }

/* Panneau imbrique (suggestions, formulaire de creation d'echange...) :
   version allegee de .panel pour un encart DANS un .panel deja existant -
   sans son flou/halo/gros padding, qui se cumulaient mal en imbrication. */
.panel-inset {
  background: var(--bg-panel-2);
  border: 1px solid var(--border-soft);
  border-radius: 16px;
  padding: 22px 24px;
}

/* Formulaire de creation d'echange (refonte visuelle, 2026-09-30) : remplace
   la grille generique .trade-form (ou la case a cocher "doublons" finissait
   perdue entre deux cellules, se chevauchant avec le label voisin) par une
   mise en scene "tu donnes <-> tu recois" a deux colonnes, chacune avec un
   vrai cadre de carte (comme .trade-card-thumb dans l'historique) plutot
   qu'une petite vignette 44px a cote d'un select. */
.trade-compose h2 { margin: 0 0 4px; font-size: 1.05rem; }
.trade-compose-form { display: flex; flex-direction: column; gap: 24px; margin-top: 18px; }
.trade-target-picker { display: flex; flex-direction: column; gap: 6px; max-width: 320px; }
.trade-field-label {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.76rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-dim);
}
.trade-compose-sides { display: flex; align-items: flex-start; justify-content: center; gap: 20px; flex-wrap: wrap; }
.trade-side { display: flex; flex-direction: column; align-items: center; gap: 10px; flex: 1; min-width: 210px; max-width: 280px; }
.trade-side-label {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.82rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--text-dim);
}
.trade-side-label em { font-style: normal; font-weight: 500; text-transform: none; letter-spacing: normal; opacity: 0.7; }
.trade-side select { width: 100%; }
/* --frame-color pose en JS (updateCardPreview, trade.js) depuis la vraie
   couleur de rarete de la carte choisie - un cadre neutre tant qu'aucune
   carte n'est selectionnee. */
.trade-side-card {
  position: relative;
  width: 130px;
  aspect-ratio: 3 / 4;
  border-radius: 12px;
  border: 2px solid var(--frame-color, var(--border-soft));
  background: var(--bg-panel-solid);
  overflow: hidden;
  box-shadow: 0 10px 24px -14px color-mix(in srgb, var(--frame-color, var(--accent)) 70%, transparent);
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
.trade-side-card.empty { border-style: dashed; opacity: 0.7; box-shadow: none; }
.trade-side-card.empty::before {
  content: "?";
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-display);
  font-size: 2.2rem;
  color: var(--text-dim);
}
.trade-side-thumb { width: 100%; height: 100%; object-fit: cover; display: block; }
.trade-compose-arrow {
  align-self: center;
  margin-top: 44px;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bg-panel-2);
  border: 1px solid var(--border-soft);
  color: var(--accent-2);
  font-size: 1.1rem;
  flex-shrink: 0;
}
.trade-side-extra { display: flex; flex-direction: column; align-items: flex-end; gap: 14px; flex-wrap: wrap; width: 100%; justify-content: center; }
.trade-side-sub { display: flex; flex-direction: column; gap: 4px; font-size: 0.78rem; color: var(--text-dim); flex: 1; min-width: 90px; }
.trade-side-extra .checkbox-inline { white-space: nowrap; padding-bottom: 10px; }
.trade-compose-submit { align-self: center; min-width: 240px; }
@media (max-width: 640px) {
  .trade-compose-sides { flex-direction: column; align-items: center; }
  .trade-compose-arrow { margin-top: 0; transform: rotate(90deg); }
}

/* Tableaux d'equilibrage (raretes/finitions) : chaque ligne editable en
   place, un bouton Enregistrer dedie (pas de formulaire global qui
   soumettrait tout d'un coup - chaque rangee est sa propre unite de
   sauvegarde, cote serveur comme cote UI). */
.balance-subheading { grid-column: 1 / -1; }
.balance-table { margin-bottom: 8px; border: 1px solid var(--border-soft); border-radius: 12px; overflow: hidden; }
.balance-row {
  display: grid;
  gap: 10px;
  align-items: center;
  padding: 10px 14px;
  border-bottom: 1px solid var(--border-soft);
}
.balance-row:last-child { border-bottom: none; }
#balance-rarities-table .balance-row { grid-template-columns: minmax(90px,1.3fr) repeat(3, minmax(70px,1fr)) minmax(90px,auto); }
#balance-finishes-table .balance-row,
#balance-qualities-table .balance-row { grid-template-columns: minmax(90px,1.3fr) repeat(2, minmax(80px,1fr)) minmax(90px,auto); }
.balance-row.balance-head { background: var(--bg-panel-2); font-size: 0.75rem; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.03em; }
.balance-row-label { font-weight: 700; font-family: var(--font-display); font-size: 0.85rem; }
.balance-row input[type="number"] {
  width: 100%;
  background: var(--bg-panel-2);
  border: 1px solid var(--border-soft);
  color: var(--text);
  padding: 7px 8px;
  border-radius: 8px;
  font-size: 0.88rem;
  font-family: var(--font-body);
}
.balance-save-btn { font-size: 0.78rem; padding: 7px 10px; }
@media (max-width: 640px) {
  #balance-rarities-table .balance-row,
  #balance-finishes-table .balance-row,
  #balance-qualities-table .balance-row { grid-template-columns: 1fr 1fr; }
  .balance-row.balance-head { display: none; }
  .balance-row-label { grid-column: 1 / -1; }
  .balance-save-btn { grid-column: 1 / -1; }
}

/* Recompenses de niveau (100 paliers) : longue liste, un seul bouton
   "Enregistrer" global plutot qu'un par ligne (contrairement aux tableaux
   d'equilibrage ci-dessus) - 100 requetes individuelles serait excessif. */
.level-rewards-table-wrap {
  max-height: 420px;
  overflow-y: auto;
  border: 1px solid var(--border-soft);
  border-radius: 12px;
  margin-bottom: 12px;
}
.level-rewards-table { width: 100%; border-collapse: collapse; font-size: 0.85rem; }
.level-rewards-table thead th {
  position: sticky;
  top: 0;
  background: var(--bg-panel-2);
  color: var(--text-dim);
  text-transform: uppercase;
  font-size: 0.72rem;
  letter-spacing: 0.03em;
  padding: 8px 10px;
  text-align: left;
  z-index: 1;
}
.level-rewards-table td { padding: 6px 10px; border-bottom: 1px solid var(--border-soft); }
.level-rewards-table tr:last-child td { border-bottom: none; }
.level-rewards-table td:first-child { font-family: var(--font-display); font-weight: 700; }
.level-rewards-table input[type="number"] {
  width: 84px;
  background: var(--bg-panel-2);
  border: 1px solid var(--border-soft);
  color: var(--text);
  padding: 6px 8px;
  border-radius: 8px;
  font-size: 0.85rem;
  font-family: var(--font-body);
}
.level-rewards-table select {
  width: 100%;
  min-width: 160px;
  background: var(--bg-panel-2);
  border: 1px solid var(--border-soft);
  color: var(--text);
  padding: 6px 8px;
  border-radius: 8px;
  font-size: 0.85rem;
  font-family: var(--font-body);
}

/* Coffre-fort (clefs secretes, jeux.html) : une carte arc-en-ciel vitrine,
   visible mais derriere des barreaux tant que le joueur n'a pas 6 clefs.
   Reprend la palette/anim shimmer de la finition rainbow (voir plus haut,
   finish-rainbow-shift) pour que la carte "derriere la vitre" ait deja
   l'air aussi speciale que la vraie. */
.vault-scene {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  padding: 28px 20px;
  background: radial-gradient(circle at 50% 20%, rgba(255,110,199,0.1), transparent 60%), var(--bg-panel-2);
  border: 2px solid var(--border);
  border-radius: 16px;
}
.vault-card-frame {
  position: relative;
  width: 160px;
  aspect-ratio: 5 / 7;
  border-radius: 12px;
  overflow: hidden;
  border: 3px solid #ff6ec7;
  animation: vault-glow-pulse 3s ease-in-out infinite;
}
/* Filtre adouci (2026-09-30) : saturate(1.6)+contrast(1.1) ecrasait les
   photos deja sombres (ex: un chat noir) en quasi-noir illisible - un leger
   coup de lumiere garde la carte reconnaissable quelle que soit l'image. */
.vault-card-img { width: 100%; height: 100%; object-fit: cover; display: block; filter: saturate(1.3) brightness(1.25) contrast(1.05); }
/* Barreaux metalliques (2026-09-30, corrige un vrai bug) : l'ancienne version
   etait un simple voile sombre semi-transparent, qui se fondait avec toute
   image deja sombre et rendait la carte totalement illisible (juste un
   rectangle noir). Un degrade clair-fonce-clair par barreau (comme un reflet
   sur une tige metallique) reste visible quelle que soit l'image en dessous,
   ET laisse largement plus de place transparente pour voir la carte. */
.vault-bars {
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(
    90deg,
    transparent 0px, transparent 22px,
    rgba(0,0,0,0.7) 22px, rgba(0,0,0,0.7) 24px,
    rgba(210,210,220,0.9) 25px,
    rgba(0,0,0,0.7) 26px, rgba(0,0,0,0.7) 28px,
    transparent 28px
  );
  box-shadow: inset 0 0 18px rgba(0,0,0,0.55);
  pointer-events: none;
}
.vault-lock-icon {
  position: absolute;
  bottom: 8px;
  right: 8px;
  font-size: 1.4rem;
  filter: drop-shadow(0 2px 6px rgba(0,0,0,0.8));
}
@keyframes vault-glow-pulse {
  0%, 100% { box-shadow: 0 0 30px rgba(255,110,199,0.5), 0 0 60px rgba(125,211,252,0.25); }
  50% { box-shadow: 0 0 42px rgba(255,215,110,0.6), 0 0 70px rgba(167,139,250,0.35); }
}
.vault-card-name { font-family: var(--font-display); font-weight: 700; font-size: 1rem; text-align: center; }
.vault-keys-track {
  width: min(240px, 100%);
  height: 10px;
  border-radius: 999px;
  background: var(--bg-panel-solid);
  border: 1px solid var(--border-soft);
  overflow: hidden;
}
.vault-keys-fill {
  height: 100%;
  background: linear-gradient(90deg, #ff6ec7, #ffd76e, #7dd3fc, #a78bfa);
  background-size: 300% 100%;
  animation: finish-rainbow-shift 4s linear infinite;
  transition: width 0.5s ease;
}
.vault-keys-label { font-size: 0.82rem; color: var(--text-dim); font-family: var(--font-display); font-weight: 700; }
.vault-card-frame.vault-unlocking { animation: vault-unlock-burst 0.9s ease forwards; }
@keyframes vault-unlock-burst {
  0% { transform: scale(1) rotate(0deg); opacity: 1; }
  30% { transform: scale(1.15) rotate(-4deg); }
  60% { transform: scale(0.9) rotate(4deg); }
  100% { transform: scale(1.3) rotate(0deg); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .vault-card-frame, .vault-keys-fill { animation: none; }
  .vault-card-frame.vault-unlocking { animation: none; opacity: 0; transition: opacity 0.4s ease; }
}

/* Coffre deja ouvert (2026-09-30, corrige un vrai bug) : reprenait la classe
   generique .empty-state (icone "boite d'archives" + gros espace vide),
   qui envoyait un signal "rien a voir ici" - a l'oppose d'un message de
   reussite. Reprend plutot le ton celebratoire de .reward-banner, avec la
   carte desormais entierement revelee (sans barreaux) a cote du texte. */
.vault-opened-banner {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 18px 20px;
  border-radius: 16px;
  background: linear-gradient(135deg, rgba(255,110,199,0.22), rgba(125,211,252,0.16));
  border: 1px solid var(--border);
}
.vault-opened-img {
  width: 70px;
  aspect-ratio: 5 / 7;
  object-fit: cover;
  border-radius: 10px;
  border: 2px solid #ff6ec7;
  box-shadow: 0 0 18px rgba(255,110,199,0.5);
  flex-shrink: 0;
}
.vault-opened-text { font-size: 0.92rem; line-height: 1.5; }
@media (max-width: 480px) {
  .vault-opened-banner { padding: 14px 16px; }
  .vault-opened-img { width: 54px; }
}

/* --- Liste d'echanges --- */
.trade-list { display: flex; flex-direction: column; gap: 12px; margin-bottom: 24px; }

.trade-card {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
  background: var(--bg-panel-2);
  border: 1px solid var(--border-soft);
  border-left: 3px solid var(--border-soft);
  border-radius: 12px;
  padding: 14px 18px;
}
/* Liseré colore selon le statut, pour un reperage plus rapide qu'en lisant
   le badge de texte. */
.trade-card[data-status="pending"] { border-left-color: var(--accent-2); }
.trade-card[data-status="accepted"] { border-left-color: #4ade80; }
.trade-card[data-status="declined"],
.trade-card[data-status="cancelled"] { border-left-color: #fb7185; }
.trade-card[data-status="countered"] { border-left-color: #fbbf24; }

.trade-info { font-size: 0.92rem; display: flex; flex-direction: column; gap: 6px; flex: 1; min-width: 200px; }
.trade-actions { display: flex; gap: 10px; flex-wrap: wrap; }

.trade-counter-tag {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  align-self: flex-start;
  background: rgba(251,191,36,0.16);
  border: 1px solid #fbbf24;
  color: var(--text);
  padding: 3px 9px;
  border-radius: 999px;
  font-size: 0.72rem;
}

/* Vignettes carte offerte -> carte demandee : remplace le mur de texte pur
   par un visuel rapide a scanner, dans l'esprit "carte a carte" du site. */
.trade-visual { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
.trade-card-block { display: flex; flex-direction: column; align-items: center; gap: 4px; }
.trade-card-thumb {
  width: 56px;
  height: 74px;
  border-radius: 6px;
  border: 1.5px solid var(--border-soft);
  overflow: hidden;
  flex-shrink: 0;
  background: var(--bg-panel-solid);
  transition: transform 0.15s ease;
}
.trade-card-thumb:hover { transform: scale(1.06); }
.trade-card-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.trade-card-thumb.gift {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.3rem;
  border-style: dashed;
}
.trade-arrow { color: var(--text-dim); font-size: 1rem; flex-shrink: 0; }
/* Detail rarete/etat/finition sous chaque vignette : voir a l'oeil si un
   echange est equitable sans avoir a deviner depuis la seule couleur de
   bordure (demande explicite : "plus de details... pour juger si l'echange
   est equitable"). */
.trade-card-tags {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  max-width: 74px;
}
.rarity-tag {
  font-size: 0.62rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  padding: 1px 6px;
  border-radius: 999px;
  border: 1px solid;
  white-space: nowrap;
}
.trade-card-tags .finish-tag,
.trade-card-tags .quality-tag { font-size: 0.6rem; padding: 1px 6px; }

.trade-status {
  display: inline-block;
  width: fit-content;
  font-size: 0.72rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  padding: 3px 10px;
  border-radius: 999px;
  background: var(--bg-panel-solid);
  color: var(--text-dim);
}

/* --- Journal d'ouvertures --- */
.pull-log-list { display: flex; flex-direction: column; gap: 8px; margin-top: 16px; }
.pull-log-row {
  display: flex;
  align-items: center;
  gap: 14px;
  background: var(--bg-panel-2);
  border: 1px solid var(--border-soft);
  border-radius: 10px;
  padding: 10px 14px;
}
.pull-log-row img { width: 40px; height: 53px; object-fit: cover; border-radius: 6px; border: 2px solid; flex-shrink: 0; }
.pull-log-info { flex: 1; min-width: 0; }
.pull-log-name { font-family: var(--font-display); font-weight: 700; font-size: 0.9rem; }
.pull-log-meta { display: flex; align-items: center; gap: 8px; margin-top: 4px; flex-wrap: wrap; }
.pull-log-source { font-size: 1.2rem; flex-shrink: 0; }
.pull-log-time { color: var(--text-dim); font-size: 0.76rem; flex-shrink: 0; white-space: nowrap; font-variant-numeric: tabular-nums; }

/* Graphique "tirages au fil du temps" : barres CSS simples (une seule
   serie, une seule teinte) plutot qu'une lib de charts pour un usage aussi
   modeste. */
.timeline-chart {
  display: flex;
  align-items: flex-end;
  gap: 3px;
  height: 120px;
  margin: 12px 0 20px;
  padding: 8px;
  background: var(--bg-panel-2);
  border-radius: 10px;
}
.timeline-bar-col { flex: 1; height: 100%; display: flex; align-items: flex-end; min-width: 0; }
.timeline-bar {
  width: 100%;
  min-height: 2px;
  border-radius: 3px 3px 0 0;
  background: linear-gradient(180deg, var(--accent-2), var(--accent));
}

/* --- Tableau admin --- */
.admin-table {
  width: 100%;
  border-collapse: collapse;
  margin-top: 16px;
  font-size: 0.88rem;
  scrollbar-width: thin;
  scrollbar-color: var(--accent-2) transparent;
}
.admin-table::-webkit-scrollbar { height: 8px; }
.admin-table::-webkit-scrollbar-track { background: transparent; }
.admin-table::-webkit-scrollbar-thumb { background: var(--accent-2); border-radius: 999px; }
.admin-table th, .admin-table td {
  text-align: left;
  padding: 10px 12px;
  border-bottom: 1px solid var(--border-soft);
}
.admin-table th {
  color: var(--text-dim);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}
/* Liseré colore par statut (border-collapse rend un border-left sur <tr>
   peu fiable entre navigateurs : on le pose sur la 1ere cellule). */
.admin-table tr[data-status] td:first-child { border-left: 3px solid var(--border-soft); }
.admin-table tr[data-status="active"] td:first-child { border-left-color: var(--success); }
.admin-table tr[data-status="expired"] td:first-child { border-left-color: var(--text-dim); }
.admin-table tr[data-status="exhausted"] td:first-child { border-left-color: var(--accent-3); }
.admin-table tr[data-status="disabled"] td:first-child { border-left-color: var(--danger); }
.admin-table code {
  background: var(--bg-panel-solid);
  padding: 2px 8px;
  border-radius: 6px;
  font-weight: 700;
}
.table-sub { font-size: 0.72rem; color: var(--text-dim); margin-top: 2px; }

/* ========================================================================
   Toasts
   ======================================================================== */
.toast-stack {
  position: fixed;
  top: 16px;
  right: 16px;
  z-index: 200;
  display: flex;
  flex-direction: column;
  gap: 10px;
  max-width: min(360px, calc(100vw - 32px));
  pointer-events: none;
}
.toast-clear-all {
  pointer-events: auto;
  align-self: flex-end;
  background: var(--bg-panel-solid);
  border: 1px solid var(--border-soft);
  color: var(--text-dim);
  font-size: 0.72rem;
  font-weight: 700;
  padding: 5px 12px;
  border-radius: 999px;
  box-shadow: 0 8px 20px -8px rgba(0,0,0,0.5);
}

.toast {
  pointer-events: auto;
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 13px 16px;
  border-radius: 12px;
  background: var(--bg-panel-solid);
  border: 1px solid var(--border-soft);
  box-shadow: 0 16px 40px -14px rgba(0,0,0,0.7);
  font-size: 0.88rem;
  font-weight: 600;
  color: var(--text);
  animation: toastIn 0.3s cubic-bezier(.2,.9,.3,1.3) forwards;
}
.toast.hide { animation: toastOut 0.25s ease forwards; }
.toast .toast-icon { font-size: 1.05rem; line-height: 1.2; flex-shrink: 0; }
.toast-success { border-color: rgba(74, 222, 128, 0.35); }
.toast-success .toast-icon { color: var(--success); }
.toast-error { border-color: rgba(251, 113, 133, 0.35); }
.toast-error .toast-icon { color: var(--danger); }
.toast-info .toast-icon { color: var(--accent-2); }

@keyframes toastIn {
  from { opacity: 0; transform: translateX(30px) scale(0.95); }
  to { opacity: 1; transform: translateX(0) scale(1); }
}
@keyframes toastOut {
  to { opacity: 0; transform: translateX(30px) scale(0.95); }
}

/* ========================================================================
   Navigation mobile (barre du bas)
   ======================================================================== */
.bottom-nav {
  display: none;
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 50;
  height: var(--nav-bottom-h);
  padding-bottom: env(safe-area-inset-bottom, 0px);
  background: rgba(10, 11, 24, 0.88);
  backdrop-filter: blur(16px) saturate(140%);
  -webkit-backdrop-filter: blur(16px) saturate(140%);
  border-top: 1px solid var(--border-soft);
  justify-content: space-around;
  align-items: center;
}

.bottom-nav a {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  min-width: 0;
  color: var(--text-dim);
  font-family: var(--font-display);
  font-size: 0.62rem;
  font-weight: 700;
  text-decoration: none;
  flex: 1;
  height: 100%;
  transition: color 0.15s, transform 0.15s;
}
/* Icone seule (le libelle passe en .sr-only, voir main.js) : 7-9
   destinations possibles ne tiennent pas avec un texte visible sous
   chaque icone sur un ecran de telephone - une icone plus grande, sans
   texte, scale correctement quel que soit le nombre d'entrees. */
.bottom-nav a .bn-icon { font-size: 1.5rem; transition: transform 0.15s; }
.bottom-nav a.active { color: var(--accent-2); }
.bottom-nav a.active .bn-icon { transform: translateY(-2px) scale(1.1); }
.bottom-nav a:active .bn-icon { transform: scale(0.85); }

/* ========================================================================
   Compteur anime (badge boosters, stats)
   ======================================================================== */
.count-bump {
  animation: countBump 0.4s cubic-bezier(.2,.9,.3,1.4);
}
/* "+N" qui s'envole depuis un badge de monnaie (embellissement 2026-09-30,
   voir main.js spawnFloatingGain) en plus du compteur qui defile. */
.count-gain-float {
  position: fixed;
  transform: translate(-50%, 0);
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 0.85rem;
  color: var(--success);
  pointer-events: none;
  z-index: 460;
  text-shadow: 0 1px 4px rgba(0,0,0,0.6);
  animation: countGainFloat 0.9s ease-out forwards;
}
@keyframes countGainFloat {
  0% { opacity: 0; transform: translate(-50%, 0) scale(0.8); }
  20% { opacity: 1; transform: translate(-50%, -6px) scale(1.05); }
  100% { opacity: 0; transform: translate(-50%, -28px) scale(1); }
}
@media (prefers-reduced-motion: reduce) {
  .count-gain-float { animation: none; opacity: 0; }
}
@keyframes countBump {
  0% { transform: scale(1); }
  40% { transform: scale(1.35); color: var(--success); }
  100% { transform: scale(1); }
}

/* ========================================================================
   Impact legendaire (flash + pulse) et tap-to-skip
   ======================================================================== */
/* Volontairement PAS de transform/shake sur un ancetre des cartes : les
   cartes de reveal utilisent transform-style:preserve-3d pour le flip, et
   animer le transform d'un parent (ex: document.body) au-dessus casse le
   rendu 3D dans certains navigateurs (plantage/ecran fige constate). On
   isole donc l'effet a un calque de flash independant + un filtre sur la
   carte elle-meme (jamais un ancetre commun a d'autres elements 3D). */
/* --flash-color/--flash-alpha : poses en JS depuis la vraie couleur de
   rarete (celebrateRarity, main.js) - jamais fige sur l'orange legendaire,
   pour que le flash reagisse aussi a epique (plus discret) et mythique (plus
   fort). Meme logique que --shine-color sur .card-back-shine. */
.legendary-flash {
  position: fixed;
  inset: 0;
  z-index: 410;
  pointer-events: none;
  background: radial-gradient(circle at center, var(--flash-color, rgba(245,165,36,0.55)), transparent 65%);
  opacity: 0;
}
.legendary-flash.active { animation: legendaryFlashPulse var(--flash-duration, 0.9s) ease-out; }
.legendary-flash.tier-epique { animation-name: legendaryFlashPulseSoft; }
.legendary-flash.tier-rare { animation-name: legendaryFlashPulseFaint; }
@keyframes legendaryFlashPulse {
  0% { opacity: 0; }
  20% { opacity: 1; }
  100% { opacity: 0; }
}
@keyframes legendaryFlashPulseSoft {
  0% { opacity: 0; }
  20% { opacity: 0.65; }
  100% { opacity: 0; }
}
@keyframes legendaryFlashPulseFaint {
  0% { opacity: 0; }
  20% { opacity: 0.4; }
  100% { opacity: 0; }
}

/* Le flash de luminosite s'applique aux ENFANTS de la face, jamais a
   .card-front elle-meme (bug Firefox reel, 2026-10-01) : un `filter` sur
   une face qui participe au contexte 3D du flip (preserve-3d de
   .card-inner) la sort de ce contexte sous Firefox - la face avant
   restait alors affichee en miroir (ou le flip semblait ne jamais se
   faire) des qu'une carte rare+ declenchait cet effet. Les enfants, eux,
   sont deja aplatis par l'overflow:hidden de .card-face : sans risque. */
.card.legendary-hit .card-front > * { animation: legendaryHit var(--flash-duration, 0.6s) ease-out; }
@keyframes legendaryHit {
  0% { filter: brightness(1); }
  30% { filter: brightness(var(--flash-brightness, 2)); }
  100% { filter: brightness(1); }
}

/* Anneau lumineux qui eclate autour de la carte des qu'elle est rare+
   (spawnCardAuraRing, opening.js - 2026-09-30, remplace l'idee des eclats de
   foil colores par extension) : nombre de pulsations et taille croissants
   avec la rarete (--aura-pulses/--aura-scale poses en JS par palier). Enfant
   direct de .card (desormais position:relative), jamais gene par le flip
   3D puisqu'il vit hors de .card-inner. */
.card-aura-ring {
  position: absolute;
  inset: -8%;
  border-radius: 18px;
  border: 3px solid var(--aura-color, #fff);
  box-shadow: 0 0 26px var(--aura-color, #fff);
  opacity: 0;
  pointer-events: none;
  z-index: 6;
  animation-name: cardAuraPulse;
  animation-duration: 0.6s;
  animation-timing-function: ease-out;
  animation-iteration-count: var(--aura-pulses, 1);
}
@keyframes cardAuraPulse {
  0% { transform: scale(0.92); opacity: 0.9; }
  100% { transform: scale(var(--aura-scale, 1.3)); opacity: 0; }
}

/* Zoom cinematique sur epique+ (heroZoom, RARITY_FLASH_TIERS) : la carte
   elle-meme grossit brievement au moment du flip, plutot que de rester a sa
   taille normale de pile pendant que seuls le flash et les particules
   bougent autour d'elle. L'animation porte sur .card-inner (jamais sur le
   wrap .card, dont le transform inline est pilote par layoutStack) et
   preserve rotateY(180deg) tout du long pour ne pas re-basculer la carte
   face cachee en cours de route. */
.card.hero-zoom { z-index: 5; }
.card.hero-zoom .card-inner { animation: cardHeroZoom var(--flash-duration, 1s) ease-out; }
@keyframes cardHeroZoom {
  0% { transform: rotateY(180deg) scale(1); }
  35% { transform: rotateY(180deg) scale(var(--hero-scale, 1.15)); }
  100% { transform: rotateY(180deg) scale(1); }
}

.skip-hint {
  position: absolute;
  bottom: -34px;
  left: 50%;
  transform: translateX(-50%);
  font-size: 0.76rem;
  color: var(--text-dim);
  opacity: 0;
  transition: opacity 0.2s;
  white-space: nowrap;
  pointer-events: none;
}
.skip-hint.visible { opacity: 1; animation: skipHintPulse 1.4s ease-in-out infinite; }
@keyframes skipHintPulse {
  0%, 100% { opacity: 0.55; }
  50% { opacity: 1; }
}

/* ========================================================================
   Holo shimmer permanent sur cartes rares+ (collection)
   ======================================================================== */
.collection-card {
  position: relative;
  transform-style: preserve-3d;
  /* Pas de will-change permanent ici : sur une grille de dizaines de
     cartes, ca force autant de calques de composition GPU en continu et
     provoque des saccades. attachTilt() (collection.js) l'ajoute/l'enleve
     lui-meme, seulement sur la carte survolee/touchee. */
}
.collection-card:not(.locked)[data-rarity="rare"] { box-shadow: 0 0 0 1px rgba(59,130,246,0.4), 0 0 10px rgba(59,130,246,0.22); }
.collection-card:not(.locked)[data-rarity="epique"] { box-shadow: 0 0 0 1px rgba(168,85,247,0.45), 0 0 14px rgba(168,85,247,0.3); }
.collection-card:not(.locked)[data-rarity="legendaire"] {
  box-shadow: 0 0 0 1px rgba(245,165,36,0.55), 0 0 22px rgba(245,165,36,0.32);
}
.collection-card:not(.locked)[data-rarity="mythique"] {
  box-shadow: 0 0 0 1px rgba(239,68,68,0.6), 0 0 26px rgba(239,68,68,0.4);
}
/* Cadre distinct par rarete, plutot que le meme rectangle pour toutes. */
.collection-card:not(.locked)[data-rarity="rare"] { border-color: rgba(59,130,246,0.55); }
.collection-card:not(.locked)[data-rarity="epique"] { border-width: 2px; border-style: double; border-color: rgba(168,85,247,0.7); }
.collection-card:not(.locked)[data-rarity="legendaire"] {
  border-style: solid;
  border-width: 2px;
  border-image: linear-gradient(135deg, var(--rarity-legendaire), #ffd166, var(--rarity-legendaire)) 1;
}
.collection-card:not(.locked)[data-rarity="mythique"] {
  border-style: solid;
  border-width: 2px;
  border-image: linear-gradient(135deg, var(--rarity-mythique), #ff9d9d, var(--rarity-mythique)) 1;
}
/* Epaisseur "carte physique" : une fine ombre en decalage simule une
   tranche, plutot qu'un visuel totalement plat (le halo de rarete est
   fusionne dans le meme box-shadow pour .card-face, voir plus haut). */
.collection-card {
  box-shadow: 1.5px 1.5px 0 rgba(0,0,0,0.35), 3px 3px 0 -1px rgba(0,0,0,0.22);
}
.collection-card:not(.locked)[data-rarity="rare"] { box-shadow: 0 0 0 1px rgba(59,130,246,0.4), 0 0 10px rgba(59,130,246,0.22), 1.5px 1.5px 0 rgba(0,0,0,0.35), 3px 3px 0 -1px rgba(0,0,0,0.22); }
.collection-card:not(.locked)[data-rarity="epique"] { box-shadow: 0 0 0 1px rgba(168,85,247,0.45), 0 0 14px rgba(168,85,247,0.3), 1.5px 1.5px 0 rgba(0,0,0,0.35), 3px 3px 0 -1px rgba(0,0,0,0.22); }
.collection-card:not(.locked)[data-rarity="legendaire"] {
  box-shadow: 0 0 0 1px rgba(245,165,36,0.55), 0 0 22px rgba(245,165,36,0.32), 1.5px 1.5px 0 rgba(0,0,0,0.35), 3px 3px 0 -1px rgba(0,0,0,0.22);
}
.collection-card:not(.locked)[data-rarity="mythique"] {
  box-shadow: 0 0 0 1px rgba(239,68,68,0.6), 0 0 26px rgba(239,68,68,0.4), 1.5px 1.5px 0 rgba(0,0,0,0.35), 3px 3px 0 -1px rgba(0,0,0,0.22);
}
.collection-card:not(.locked) {
  overflow: hidden;
}
.collection-card:not(.locked)::after {
  content: "";
  position: absolute;
  top: 0;
  left: -60%;
  width: 60%;
  height: 100%;
  background: linear-gradient(115deg, transparent 20%, rgba(255,255,255,0.28) 50%, transparent 80%);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.3s;
}
.collection-card:not(.locked)[data-rarity="epique"]::after,
.collection-card:not(.locked)[data-rarity="legendaire"]::after,
.collection-card:not(.locked)[data-rarity="mythique"]::after {
  opacity: 0.7;
  /* transform plutot que background-position : anime sur le compositeur
     GPU (pas de repaint a chaque frame), bien moins couteux repete sur
     plusieurs cartes en meme temps. */
  animation: cardHoloDrift 3.5s ease-in-out infinite;
}
/* Reflet foil au survol des legendaires/mythiques : plus vif et plus rapide
   que le shimmer ambiant permanent (qui reste sur ::after), sur ::before. */
.collection-card:not(.locked)[data-rarity="legendaire"]::before,
.collection-card:not(.locked)[data-rarity="mythique"]::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(120deg, transparent 30%, rgba(255,255,255,0.55) 50%, transparent 70%);
  transform: translateX(-120%);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s;
}
.collection-card:not(.locked)[data-rarity="legendaire"]:hover::before,
.collection-card:not(.locked)[data-rarity="mythique"]:hover::before {
  opacity: 1;
  animation: legendaryFoilSweep 0.7s ease-out;
}
/* Accent de bordure sur le reveal de pack (.card-front, seul element du flip
   a avoir une vraie bordure) : le halo/pulse existe deja par rarete sur
   .card-face (voir plus haut, legendaryPulse) - on ajoute ICI seulement la
   bordure colore/degradee, jamais le box-shadow (deja gere par .card-face,
   ne pas dupliquer/entrer en conflit avec son animation). */
.card[data-rarity="epique"] .card-front { border-width: 2px; border-style: double; border-color: rgba(168,85,247,0.75); }
.card[data-rarity="legendaire"] .card-front {
  border-style: solid;
  border-width: 2px;
  border-image: linear-gradient(135deg, var(--rarity-legendaire), #ffd166, var(--rarity-legendaire)) 1;
}
.card[data-rarity="mythique"] .card-front {
  border-style: solid;
  border-width: 2px;
  border-image: linear-gradient(135deg, var(--rarity-mythique), #ff9d9d, var(--rarity-mythique)) 1;
}
@keyframes legendaryFoilSweep {
  from { transform: translateX(-120%); }
  to { transform: translateX(120%); }
}
@keyframes cardHoloDrift {
  0% { transform: translateX(0); }
  100% { transform: translateX(280%); }
}

/* ========================================================================
   Spinner de chargement
   ======================================================================== */
.spinner {
  width: 34px;
  height: 34px;
  border-radius: 50%;
  border: 3px solid var(--border-soft);
  border-top-color: var(--accent-2);
  animation: spin 0.7s linear infinite;
  margin: 30px auto;
}
@keyframes spin { to { transform: rotate(360deg); } }

.loading-row {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 30px 0;
}

/* ========================================================================
   Transitions de page natives (navigation MPA), CDN Chrome/Edge recents ;
   ignore silencieusement ailleurs.
   ======================================================================== */
@view-transition {
  navigation: auto;
}

/* ========================================================================
   Pastille de notification sur la nav (echanges en attente)
   ======================================================================== */
.nav-dot {
  position: absolute;
  top: -4px;
  right: -8px;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: 999px;
  background: var(--danger);
  color: white;
  font-size: 0.6rem;
  font-weight: 800;
  line-height: 16px;
  text-align: center;
  box-shadow: 0 0 0 2px var(--bg-void);
  animation: navDotPop 0.3s cubic-bezier(.2,.9,.3,1.4);
}
.bottom-nav a { position: relative; }
.nav .nav-dot { position: static; display: inline-flex; align-items: center; justify-content: center; margin-left: 5px; box-shadow: none; }
@keyframes navDotPop {
  from { transform: scale(0); }
  to { transform: scale(1); }
}

/* ========================================================================
   Skeleton loaders (remplacent le spinner dans les listes)
   ======================================================================== */
.skeleton-row {
  height: 64px;
  border-radius: 12px;
  margin-bottom: 12px;
  background: linear-gradient(90deg, var(--bg-panel-2) 25%, var(--bg-panel-solid) 37%, var(--bg-panel-2) 63%);
  background-size: 400% 100%;
  animation: skeletonShine 1.4s ease infinite;
}
.skeleton-card {
  border-radius: 14px;
  aspect-ratio: 3/4;
  background: linear-gradient(90deg, var(--bg-panel-2) 25%, var(--bg-panel-solid) 37%, var(--bg-panel-2) 63%);
  background-size: 400% 100%;
  animation: skeletonShine 1.4s ease infinite;
}
@keyframes skeletonShine {
  0% { background-position: 100% 0; }
  100% { background-position: -100% 0; }
}
/* Meme effet directement en fond d'un <img> le temps qu'il charge (au lieu
   d'un rectangle plein qui donne l'impression que l'image a casse) - le
   PNG une fois charge est opaque et recouvre entierement ce fond, donc ca
   ne se voit plus une fois l'image arrivee. Utilise pour le pot commun du
   coffre de guilde, dont les images (souvent jamais affichees ailleurs
   dans la session) peuvent mettre plusieurs secondes a charger. */
#chest-card-grid img {
  background: linear-gradient(90deg, var(--bg-panel-2) 25%, var(--bg-panel-solid) 37%, var(--bg-panel-2) 63%);
  background-size: 400% 100%;
  animation: skeletonShine 1.4s ease infinite;
}

/* ========================================================================
   Badge "NEW" sur les cartes recemment obtenues (collection)
   ======================================================================== */
.new-badge {
  position: absolute;
  top: 8px;
  left: 8px;
  z-index: 2;
  background: linear-gradient(135deg, var(--accent-3), var(--accent));
  color: white;
  font-size: 0.6rem;
  font-weight: 800;
  letter-spacing: 0.04em;
  padding: 3px 8px;
  border-radius: 999px;
  box-shadow: 0 4px 12px -4px rgba(244, 114, 182, 0.7);
  animation: newBadgePulse 1.8s ease-in-out infinite;
}
/* Premiere obtention de cette carte sur tout le serveur (isFirstEver,
   open-pack.json/redeem-code.json) : variante doree du badge "Nouvelle !",
   plus large pour le texte complet. */
.first-ever-badge {
  left: 8px;
  right: 8px;
  width: auto;
  text-align: center;
  white-space: normal;
  background: linear-gradient(135deg, var(--rarity-legendaire), #ffd166);
  color: #1a1200;
}

/* Doublon detecte a la volee pendant le reveal (voir opening.js) : meme
   emplacement que .new-badge (jamais les deux en meme temps sur une carte),
   plus discret puisque ce n'est pas une bonne nouvelle a celebrer. */
.dupe-badge {
  position: absolute;
  top: 8px;
  left: 8px;
  z-index: 2;
  background: rgba(10, 11, 24, 0.75);
  border: 1px solid var(--border-soft);
  color: var(--text-dim);
  font-size: 0.68rem;
  font-weight: 700;
  padding: 3px 8px;
  border-radius: 999px;
  font-variant-numeric: tabular-nums;
}
@keyframes newBadgePulse {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.08); }
}

/* Silhouette floutee pour les cartes non decouvertes, plus evocatrice qu'un
   simple gris plat. */
.collection-card.locked img {
  filter: blur(9px) grayscale(1) brightness(0.5) saturate(0);
  transform: scale(1.1);
}
.collection-card.locked .card-info { filter: grayscale(1) brightness(0.5); }
/* Numero de catalogue (#001...), directement sur la silhouette floutee : sert
   de repere ("il me manque #014"), pas juste un placeholder "???". */
.locked-card-number {
  position: absolute;
  inset: 0;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 1.3rem;
  letter-spacing: 0.04em;
  color: rgba(255,255,255,0.6);
  text-shadow: 0 2px 8px rgba(0,0,0,0.7);
  pointer-events: none;
}

/* Barres de progression par rarete */
.rarity-progress-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 16px 0 22px;
}
.rarity-progress-row {
  display: grid;
  grid-template-columns: 90px 1fr 56px;
  align-items: center;
  gap: 10px;
  font-size: 0.78rem;
}
.rarity-progress-row .rp-label { color: var(--text-dim); font-weight: 700; font-family: var(--font-display); }
.rarity-progress-row .rp-track {
  position: relative;
  height: 7px;
  border-radius: 999px;
  background: var(--bg-panel-2);
  overflow: hidden;
}
/* Repere de reference (ex: taux theorique) superpose sur la barre, plutot
   qu'une deuxieme serie de couleur - voir renderPullLuckChart(). */
.rp-target-tick {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 2px;
  background: #fff;
  box-shadow: 0 0 0 1px rgba(0,0,0,0.4);
}
.rarity-progress-row .rp-fill {
  display: block;
  height: 100%;
  border-radius: 999px;
  transition: width 0.6s ease;
}
.rarity-progress-row .rp-count { text-align: right; font-variant-numeric: tabular-nums; color: var(--text-dim); }

/* ========================================================================
   Pass de combat (barre de progression XP, page d'accueil)
   ======================================================================== */
.pass-track { margin-top: 10px; }
.pass-track-bar {
  height: 10px;
  border-radius: 999px;
  background: var(--bg-panel-2);
  overflow: hidden;
}
.pass-track-fill {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: linear-gradient(90deg, var(--accent), var(--accent-2));
  transition: width 0.6s ease;
  width: 0%;
}
.pass-track-labels {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  margin-top: 6px;
  font-size: 0.78rem;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
}
/* Le compteur XP reste court et ne doit jamais se retrouver coupe mot par
   mot - c'est desormais le texte de recompense (variable, parfois une
   phrase complete avec un bonus de palier) qui absorbe l'espace restant et
   passe a la ligne si besoin (voir #pass-next-reward-label, index.html). */
#pass-xp-label { white-space: nowrap; flex-shrink: 0; }
#pass-next-reward-label { flex: 1; min-width: 0; text-align: right; }

/* Barre de PV du boss communautaire (meme squelette que .pass-track). */
.hp-track { margin-top: 12px; }
.hp-track-bar {
  height: 14px;
  border-radius: 999px;
  background: var(--bg-panel-2);
  overflow: hidden;
  border: 1px solid var(--border-soft);
}
.hp-track-fill {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: linear-gradient(90deg, #ef4444, #f59e0b);
  transition: width 0.6s ease;
  width: 0%;
}
.hp-track-labels {
  display: flex;
  justify-content: center;
  margin-top: 6px;
  font-size: 0.82rem;
  font-weight: 700;
  font-family: var(--font-display);
  color: var(--text);
  font-variant-numeric: tabular-nums;
}

/* ========================================================================
   Modal plein ecran (zoom carte)
   ======================================================================== */
.card-modal-overlay {
  position: fixed;
  inset: 0;
  z-index: 300;
  background: rgba(4, 5, 12, 0.85);
  backdrop-filter: blur(6px);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  opacity: 0;
  animation: modalFadeIn 0.2s ease forwards;
}
/* Les modales creees dynamiquement (collection, echanges...) sont retirees
   du DOM plutot que masquees, mais la modale de la roue est un element
   statique de la page, bascule via l'attribut hidden - sans cette regle,
   le display:flex ci-dessus l'emporterait sur le display:none par defaut
   de [hidden] (une regle d'auteur bat toujours la feuille de style navigateur). */
.card-modal-overlay[hidden] { display: none; }
@keyframes modalFadeIn { to { opacity: 1; } }
.card-modal {
  width: min(480px, 100%);
  max-height: calc(100vh - 48px);
  overflow-y: auto;
  background: var(--bg-panel-solid);
  border-radius: 20px;
  border: 1px solid var(--border);
  transform: scale(0.92) translateY(10px);
  animation: modalPopIn 0.28s cubic-bezier(.2,.9,.3,1.3) forwards;
  box-shadow: 0 40px 80px -20px rgba(0,0,0,0.8);
}
@keyframes modalPopIn { to { transform: scale(1) translateY(0); } }
/* Accent de rarete a l'echelle de la modale (meme logique que les vignettes
   de la grille, mais un anneau + halo plus genereux, adaptes a un format
   4x plus grand qu'une vignette). */
.card-modal[data-rarity="rare"] { border-color: rgba(59,130,246,0.6); box-shadow: 0 40px 80px -20px rgba(0,0,0,0.8), 0 0 0 1px rgba(59,130,246,0.4), 0 0 30px rgba(59,130,246,0.22); }
.card-modal[data-rarity="epique"] { border-color: rgba(168,85,247,0.65); box-shadow: 0 40px 80px -20px rgba(0,0,0,0.8), 0 0 0 1px rgba(168,85,247,0.45), 0 0 34px rgba(168,85,247,0.28); }
.card-modal[data-rarity="legendaire"] { border-color: var(--rarity-legendaire); box-shadow: 0 40px 80px -20px rgba(0,0,0,0.8), 0 0 0 1px rgba(245,165,36,0.5), 0 0 40px rgba(245,165,36,0.32); }
.card-modal[data-rarity="mythique"] { border-color: var(--rarity-mythique); box-shadow: 0 40px 80px -20px rgba(0,0,0,0.8), 0 0 0 1px rgba(239,68,68,0.55), 0 0 46px rgba(239,68,68,0.38); }
.card-modal .card-art { border-radius: 20px 20px 0 0; position: relative; }
.card-modal img {
  width: 100%;
  aspect-ratio: 3/4;
  object-fit: cover;
  display: block;
  /* Meme shimmer que le pot commun du coffre (#chest-card-grid img) : evite
     un flash de vide le temps du chargement, surtout au premier ouvre-modale
     d'une session (rien d'autre n'a encore mis cette image en cache). */
  background: linear-gradient(90deg, var(--bg-panel-2) 25%, var(--bg-panel-solid) 37%, var(--bg-panel-2) 63%);
  background-size: 400% 100%;
  animation: skeletonShine 1.4s ease infinite;
}
.card-modal .card-modal-body { padding: 18px 20px 22px; }
.card-modal .card-modal-name { font-family: var(--font-display); font-weight: 800; font-size: 1.25rem; }
.card-modal .card-modal-artist { color: var(--text-dim); font-size: 0.85rem; margin-top: 2px; }
.card-modal-badges { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 10px; }
.card-modal-stats {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 16px;
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--border-soft);
  font-size: 0.8rem;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
}
.card-modal-actions { display: flex; gap: 10px; margin-top: 14px; flex-wrap: wrap; }
.card-modal-description {
  color: var(--text-dim);
  font-size: 0.88rem;
  font-style: italic;
  line-height: 1.5;
  margin: 14px 0 0;
  padding: 8px 12px;
  border-left: 3px solid var(--border-soft);
  background: rgba(255,255,255,0.03);
  border-radius: 0 8px 8px 0;
  white-space: pre-line;
}
/* Fleches de navigation : sur l'image plutot que sur toute la modale, pour
   rester au bon endroit quelle que soit la hauteur du panneau d'info en
   dessous - meme cercle "verre depoli" que le bouton de fermeture, pas le
   style bouton primaire par defaut (voir "button, .btn" plus haut). */
.card-modal-nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 38px;
  height: 38px;
  padding: 0;
  border-radius: 50%;
  background: rgba(10,10,20,0.55);
  backdrop-filter: blur(4px);
  border: 1px solid rgba(255,255,255,0.22);
  color: white;
  font-size: 0.9rem;
  box-shadow: none;
  z-index: 2;
}
.card-modal-nav:hover { background: rgba(10,10,20,0.8); transform: translateY(-50%) scale(1.06); }
.card-modal-nav.prev { left: 10px; }
.card-modal-nav.next { right: 10px; }
.card-modal-nav::before { content: none; }
/* Repere de position dans la navigation (X / Y) : les fleches seules ne
   disent pas combien de cartes restent a parcourir. */
.card-modal-position {
  position: absolute;
  top: 14px;
  left: 50%;
  transform: translateX(-50%);
  padding: 4px 12px;
  border-radius: 999px;
  background: rgba(10,10,20,0.55);
  backdrop-filter: blur(4px);
  border: 1px solid rgba(255,255,255,0.22);
  color: white;
  font-size: 0.72rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  z-index: 2;
}
.first-obtainer-badge {
  display: flex;
  align-items: center;
  gap: 8px;
  background: linear-gradient(135deg, rgba(245,165,36,0.22), rgba(255,209,102,0.14));
  border: 1px solid var(--rarity-legendaire);
  color: var(--text);
  padding: 10px 14px;
  border-radius: 10px;
  font-size: 0.82rem;
  margin-top: 12px;
}
.card-modal-close {
  position: absolute;
  top: 14px; right: 14px;
  width: 34px; height: 34px;
  padding: 0;
  border-radius: 50%;
  background: rgba(10,10,20,0.55);
  backdrop-filter: blur(4px);
  color: white;
  border: 1px solid rgba(255,255,255,0.22);
  font-size: 1.1rem;
  box-shadow: none;
  cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  z-index: 2;
}
.card-modal-close:hover { background: rgba(10,10,20,0.8); }
.card-modal-close::before { content: none; }

/* ========================================================================
   Confirmation stylisee (remplace window.confirm)
   ======================================================================== */
.confirm-box {
  width: min(360px, 100%);
  background: var(--bg-panel-solid);
  border-radius: 18px;
  border: 1px solid var(--border);
  padding: 22px 24px;
  transform: scale(0.92) translateY(10px);
  animation: modalPopIn 0.28s cubic-bezier(.2,.9,.3,1.3) forwards;
  box-shadow: 0 40px 80px -20px rgba(0,0,0,0.8);
}
.confirm-title { font-family: var(--font-display); font-weight: 800; font-size: 1.15rem; margin-bottom: 8px; }
.confirm-message { color: var(--text-dim); font-size: 0.92rem; line-height: 1.5; }
.altar-result-img { width: 140px; aspect-ratio: 3/4; object-fit: cover; border-radius: 10px; margin: 12px auto; display: block; }
.confirm-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 20px; }

.btn-danger {
  background: linear-gradient(135deg, #ef4444, #b91c1c);
  box-shadow: 0 4px 0 rgba(0,0,0,0.35), 0 10px 22px -10px rgba(239,68,68,0.55);
}
.btn-danger:active:not(:disabled) { box-shadow: 0 1px 0 rgba(0,0,0,0.35), 0 4px 10px -6px rgba(239,68,68,0.5); }

/* ========================================================================
   Selection d'extension (page ouverture)
   ======================================================================== */
/* Banniere animee au-dessus du choix de booster : degrade qui derive
   lentement + quelques etincelles flottantes, purement decoratif (aria-hidden). */
.pack-selection-banner {
  position: relative;
  height: 56px;
  border-radius: 16px;
  overflow: hidden;
  margin-bottom: 4px;
  background: linear-gradient(120deg, rgba(139, 92, 246, 0.32), rgba(34, 211, 238, 0.22), rgba(244, 114, 182, 0.28));
  background-size: 300% 300%;
  animation: psb-shift 9s ease infinite;
}
.psb-spark {
  position: absolute;
  color: rgba(255,255,255,0.85);
  font-size: 13px;
  animation: psb-float 6s ease-in-out infinite;
}
.psb-spark-1 { left: 8%; top: 55%; }
.psb-spark-2 { left: 34%; top: 22%; font-size: 10px; animation-delay: 1.5s; }
.psb-spark-3 { left: 64%; top: 50%; animation-delay: 3s; }
.psb-spark-4 { left: 87%; top: 28%; font-size: 11px; animation-delay: 4.5s; }
@keyframes psb-shift {
  0%, 100% { background-position: 0% 50%; }
  50% { background-position: 100% 50%; }
}
@keyframes psb-float {
  0%, 100% { transform: translateY(0) scale(1); opacity: 0.45; }
  50% { transform: translateY(-14px) scale(1.3); opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .pack-selection-banner, .psb-spark { animation: none; }
}

.extension-picker {
  display: grid;
  /* minmax tune pour plafonner a 5 colonnes a cette largeur de conteneur
     (996px environ) sans jamais laisser de colonnes vides quand il y a
     moins de 5 extensions actives : auto-fit reduit alors le nombre de
     colonnes et etire les tuiles existantes, contrairement a un repeat(5,..)
     fixe qui aurait laisse un grand vide a droite avec 1-3 extensions. */
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 16px;
  width: 100%;
  margin-bottom: 8px;
}
.extension-tile {
  position: relative;
  border-radius: 16px;
  border: 2px solid var(--border-soft);
  background: var(--bg-panel-2);
  padding: 14px;
  text-align: center;
  cursor: pointer;
  transition: transform 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease;
}
.extension-tile:hover:not(.disabled) {
  transform: translateY(-4px);
  border-color: var(--border);
  box-shadow: 0 14px 28px -16px color-mix(in srgb, var(--accent) 55%, transparent);
}
.extension-tile.skeleton-tile { cursor: default; }
.extension-tile.selected { border-color: var(--accent-2); box-shadow: 0 0 0 2px var(--accent-2), 0 16px 32px -16px rgba(34, 211, 238, 0.5); }
.extension-tile.disabled { opacity: 0.45; cursor: not-allowed; }
/* Survoler la tuile devoile le dos de carte de l'extension (avant meme de
   l'ouvrir) : un aperçu discret, courant dans les vitrines de sets de TCG. */
.ext-art {
  position: relative;
  margin-bottom: 8px;
  aspect-ratio: 3/4;
  border-radius: 10px;
  background: radial-gradient(circle at 50% 40%, color-mix(in srgb, var(--accent) 20%, transparent), var(--bg-panel-solid) 70%);
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}
.ext-art .booster-emoji { font-size: 3.4rem !important; filter: drop-shadow(0 6px 14px rgba(0,0,0,0.4)); }
.ext-art-front, .ext-art-back {
  width: 100%;
  height: 100%;
  aspect-ratio: 3/4;
  object-fit: cover;
  border-radius: 10px;
  display: block;
  position: absolute;
  inset: 0;
}
.ext-art-back {
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity 0.3s ease;
}
.extension-tile:hover:not(.disabled) .ext-art-back { opacity: 1; }
.extension-tile .ext-name { font-family: var(--font-display); font-weight: 700; font-size: 0.88rem; }
.extension-tile .ext-count { color: var(--text-dim); font-size: 0.76rem; margin-top: 3px; font-variant-numeric: tabular-nums; }
.set-summary-link {
  display: block;
  width: 100%;
  margin-top: 8px;
  background: transparent;
  border: 1px solid var(--border-soft);
  color: var(--text-dim);
  font-size: 0.7rem;
  font-weight: 600;
  padding: 5px;
  border-radius: 8px;
  box-shadow: none;
}
.set-summary-modal { width: min(420px, 100%); }
.set-summary-list { display: flex; flex-direction: column; gap: 2px; max-height: 50vh; overflow-y: auto; margin-top: 10px; }
.set-summary-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 4px;
  border-bottom: 1px solid var(--border-soft);
  font-size: 0.86rem;
}
.set-summary-row:last-child { border-bottom: none; }
.rarity-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }

/* ========================================================================
   Ouverture "Hearthstone" : dechirure du pack, cartes face cachee tapees
   individuellement
   ======================================================================== */
/* Dechirure plus lente et marquee : le moment du pull merite un vrai
   "beat" plutot qu'un pack qui disparait en un clin d'oeil. */
.booster-pack.tearing {
  animation: packTear 0.75s cubic-bezier(.3,.1,.4,1) forwards;
}
@keyframes packTear {
  0% { transform: scale(1); filter: brightness(1); }
  30% { transform: scale(1.1) rotate(-1deg); filter: brightness(1.3); }
  60% { transform: scale(1.16) rotate(1deg); opacity: 1; filter: brightness(1.9); }
  100% { transform: scale(1.22); opacity: 0; filter: brightness(2.2); }
}

.reveal-grid.hearthstone .card { cursor: pointer; }
.reveal-grid.hearthstone .card:not(.revealed):hover .card-inner { transform: rotateY(-8deg) scale(1.03); }
.reveal-grid.hearthstone .card-back {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.reveal-grid.hearthstone .card-back .tap-hint {
  font-size: 0.62rem;
  color: var(--text-dim);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  animation: skipHintPulse 1.6s ease-in-out infinite;
}
/* Dos de carte dessine en 3:4, carte un peu plus haute (illustration +
   bandeau du nom) : image entiere, centree, sur un fond flou de la meme
   image qui comble le haut et le bas (jamais rognee sur les cotes). */
.card-back-image { width: 100%; height: 100%; object-fit: contain; position: absolute; inset: 0; }
.card-back-fill { position: absolute; inset: -16px; background-size: cover; background-position: center; filter: blur(12px) brightness(0.55); }

/* ========================================================================
   Pile de cartes (reveal one-by-one) : plus lisible qu'une grille dense,
   surtout pour un x5 (25 cartes). Une seule carte "active" (au-dessus,
   cliquable) ; les suivantes sont decalees en pile derriere ; les cartes
   deja revelees s'envolent sur le cote.
   ======================================================================== */
.reveal-grid.stacked {
  display: block;
  position: relative;
  width: min(240px, 78vw, 36vh);
  aspect-ratio: 30 / 49;
  margin: 6px auto 0;
}
.reveal-grid.stacked .card {
  position: absolute;
  inset: 0;
  width: 100%;
  opacity: 1;
  animation: none;
  transition: transform 0.45s cubic-bezier(.2,.9,.3,1), opacity 0.35s ease;
}
.reveal-grid.stacked .card[data-active="true"] { cursor: pointer; }
.reveal-grid.stacked .card[data-active="false"] { pointer-events: none; }
.reveal-grid.stacked .card.discarded { opacity: 0; pointer-events: none; }
.stack-progress {
  text-align: center;
  font-size: 0.78rem;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
  margin-top: 10px;
}

/* Pips de progression (renderStackPips, opening.js) : un point par carte du
   lot, colore avec sa vraie rarete des qu'elle est revelee - repere visuel
   immediat sur la pile en cours, la ou "Carte 2/5" (texte) ne donnait aucune
   indication avant d'y arriver. Vide (donc invisible) en mode "Tout
   révéler", ou toutes les cartes deviennent visibles d'un coup. */
.stack-pips { display: flex; justify-content: center; gap: 7px; margin-top: 14px; }
.stack-pip {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: rgba(255,255,255,0.18);
  transition: background 0.25s ease, transform 0.2s ease;
}
.stack-pip.revealed { background: var(--pip-color, #9aa0b4); box-shadow: 0 0 8px var(--pip-color, #9aa0b4); }
.stack-pip.current { transform: scale(1.35); }

#reveal-all-btn { margin-top: 6px; }
.post-reveal-actions {
  display: flex;
  justify-content: center;
  gap: 12px;
  flex-wrap: wrap;
  margin-top: 14px;
}

/* ========================================================================
   Modal plein ecran d'ouverture (par-dessus toute la page, comme Hearthstone)
   ======================================================================== */
.pack-modal-overlay {
  position: fixed;
  inset: 0;
  z-index: 400;
  background:
    radial-gradient(circle at 50% 30%, rgba(139, 92, 246, 0.25), transparent 55%),
    rgba(4, 5, 12, 0.94);
  /* Un peu plus de flou qu'avant : accentue la separation de profondeur
     entre la scene au premier plan et le reste de la page. */
  backdrop-filter: blur(20px) saturate(140%);
  -webkit-backdrop-filter: blur(20px) saturate(140%);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
}
.pack-modal-overlay[hidden] { display: none; }
.pack-modal-overlay:not([hidden]) { animation: modalFadeIn 0.25s ease forwards; }
/* Vignettage : assombrit les bords pour concentrer le regard sur la pile
   de cartes au centre. */
.pack-modal-overlay::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: radial-gradient(ellipse at center, transparent 45%, rgba(0,0,0,0.55) 100%);
}

/* Fond par extension, en fondu enchaine (voir crossfadeModalBackground). */
.pack-modal-bg-layer {
  position: absolute;
  inset: 0;
  opacity: 0;
  background-size: cover;
  background-position: center;
  transition: opacity 0.5s ease;
  pointer-events: none;
}
.pack-modal-bg-layer.visible { opacity: 1; }

/* Poussiere ambiante flottante (distincte de la trainee sous le curseur). */
.ambient-dust {
  position: absolute;
  bottom: -10px;
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: rgba(255,255,255,0.55);
  box-shadow: 0 0 6px 1px rgba(255,255,255,0.4);
  pointer-events: none;
  animation: ambientDustRise linear forwards;
}
@keyframes ambientDustRise {
  0% { transform: translateY(0) translateX(0); opacity: 0; }
  10% { opacity: 0.8; }
  100% { transform: translateY(-70vh) translateX(20px); opacity: 0; }
}

.pack-modal-stage {
  position: relative;
  width: min(760px, 100%);
  max-height: 92vh;
  overflow-y: auto;
  overflow-x: hidden;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 26px;
  padding: 10px 4px 30px;
  scrollbar-width: thin;
  scrollbar-color: var(--accent) transparent;
  transition: transform 0.2s ease, opacity 0.2s ease;
}
/* Animation de "rangement" a la fermeture (voir closeModal, opening.js). */
.pack-modal-stage.closing { transform: scale(0.9) translateY(16px); opacity: 0; }
.pack-modal-stage::-webkit-scrollbar { width: 8px; }
.pack-modal-stage::-webkit-scrollbar-track { background: transparent; }
.pack-modal-stage::-webkit-scrollbar-thumb { background: var(--accent); border-radius: 999px; }

.pack-modal-close {
  position: fixed;
  top: max(16px, env(safe-area-inset-top, 0px));
  right: 16px;
  z-index: 401;
}

/* Onglets Décrafter/Crafter : evite d'empiler deux grilles completes sur la
   meme page, ce qui rendait la page interminable a parcourir. */
/* Sur mobile, 5 onglets (craft.html) ou 4 (communaute.html) depassent
   souvent la largeur de l'ecran : defilement horizontal plutot qu'un
   retour a la ligne (casserait le soulignement actif) ou une coupe nette
   (rendrait un onglet inaccessible - c'etait le bug avant ce passage). */
.tabs {
  display: flex;
  gap: 8px;
  margin-bottom: 16px;
  border-bottom: 1px solid var(--border-soft);
  overflow-x: auto;
  scrollbar-width: none;
}
.tabs::-webkit-scrollbar { display: none; }
.tab {
  flex-shrink: 0;
  white-space: nowrap;
  background: transparent;
  border: none;
  box-shadow: none;
  color: var(--text-dim);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.95rem;
  padding: 10px 6px;
  cursor: pointer;
  position: relative;
  transform: none !important;
}
.tab.active { color: var(--text); }
.tab.locked { opacity: 0.5; }
.tab.locked::before { content: "\1F512 "; font-size: 0.8em; }
.tab.active::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: -1px;
  height: 2px;
  border-radius: 2px;
  background: linear-gradient(90deg, var(--accent), var(--accent-2));
}
.tab-count {
  display: inline-block;
  min-width: 18px;
  padding: 1px 6px;
  margin-left: 4px;
  border-radius: 999px;
  background: var(--bg-panel-2);
  color: var(--text-dim);
  font-size: 0.72rem;
  font-variant-numeric: tabular-nums;
}
.tab-count:empty { display: none; }
.tab-count.tab-count-alert { background: var(--accent-3); color: #fff; }
.craft-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
  margin-bottom: 16px;
}
.craft-toolbar input[type="text"] { min-width: 200px; flex: 1; }

/* Autel de sacrifice (onglet craft.html). */
.altar-tiers { display: flex; flex-direction: column; gap: 14px; }

/* ========================================================================
   Finitions (foil upgrade) : fusionner des doublons identiques en une
   version plus prestigieuse. Onglet "Finitions" (craft.html).
   ======================================================================== */
.finish-tiers { display: flex; flex-direction: column; gap: 12px; }
.finish-upgrade-row {
  display: flex;
  align-items: center;
  gap: 14px;
  background: var(--bg-panel-2);
  border: 1px solid var(--border-soft);
  border-radius: 14px;
  padding: 12px 16px;
}
.finish-upgrade-row img { width: 52px; height: 68px; object-fit: cover; border-radius: 8px; flex-shrink: 0; }
.finish-upgrade-info { flex: 1; min-width: 0; }
.finish-upgrade-name { font-weight: 700; font-family: var(--font-display); }
.finish-upgrade-path { display: flex; align-items: center; gap: 8px; margin: 6px 0; font-size: 0.85rem; }
.finish-upgrade-count { color: var(--text-dim); font-size: 0.78rem; }

/* Une pastille par finition : couleur/degrade identifiant chaque palier,
   reutilisee a la fois dans le picker de fusion et sur les vignettes de
   collection (.finish-indicator). */
.finish-tag {
  display: inline-flex;
  align-items: center;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 0.76rem;
  font-weight: 700;
  border: 1px solid var(--border-soft);
  color: var(--text-dim);
}
.finish-tag[data-finish="holo"] { border-color: #a78bfa; color: #c4b5fd; }
.finish-tag[data-finish="gold"] { border-color: #d4af37; color: #f5d76e; }
.finish-tag[data-finish="ghost"] { border-color: #94a3b8; color: #cbd5e1; }
.finish-tag[data-finish="diamond"] { border-color: #7dd3fc; color: #bae6fd; }
.finish-tag[data-finish="rainbow"] { border-color: #ff6ec7; color: #ff9ecf; }

/* Meme pastille, echelle de qualite (Abime -> Use -> Bon etat -> Parfait etat). */
.quality-tag {
  display: inline-flex;
  align-items: center;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 0.76rem;
  font-weight: 700;
  border: 1px solid var(--border-soft);
  color: var(--text-dim);
}
.quality-tag[data-quality="damaged"] { border-color: #b45309; color: #fbbf24; }
.quality-tag[data-quality="worn"] { border-color: #a16207; color: #eab308; }
.quality-tag[data-quality="good"] { border-color: #4ade80; color: #86efac; }
.quality-tag[data-quality="mint"] { border-color: #22d3ee; color: #a5f3fc; }

.finish-indicator {
  position: absolute;
  bottom: 8px;
  left: 8px;
  z-index: 2;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 0.66rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  background: rgba(0,0,0,0.6);
  border: 1px solid var(--border-soft);
  color: var(--text);
}
.finish-indicator[data-finish="holo"] { border-color: #a78bfa; color: #c4b5fd; }
.finish-indicator[data-finish="gold"] { border-color: #d4af37; color: #f5d76e; }
.finish-indicator[data-finish="ghost"] { border-color: #94a3b8; color: #e2e8f0; }
.finish-indicator[data-finish="diamond"] { border-color: #7dd3fc; color: #e0f7ff; }
.finish-indicator[data-finish="rainbow"] { border-color: #ff6ec7; color: #ffd6ec; }

/* Meme principe, echelle de qualite (Abime -> Use -> Bon etat -> Parfait
   etat) : coin oppose du finish-indicator pour ne jamais se chevaucher.
   Affiche des que la qualite n'est pas "mint", partout ou une carte
   possedee s'affiche (collection, modale) - demande explicite : une carte
   abimee doit se voir vraiment, pas juste dans l'onglet Restauration. */
.quality-indicator {
  position: absolute;
  bottom: 8px;
  right: 8px;
  z-index: 2;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 0.66rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  background: rgba(0,0,0,0.6);
  border: 1px solid var(--border-soft);
  color: var(--text);
}
.quality-indicator[data-quality="damaged"] { border-color: #fbbf24; color: #fde68a; }
.quality-indicator[data-quality="worn"] { border-color: #eab308; color: #fef08a; }
.quality-indicator[data-quality="good"] { border-color: #86efac; color: #d9f9e4; }

/* Traitement visuel de l'etat directement sur l'image (filtre reel, pas
   juste un badge), sur les 4 paliers - PAS seulement damaged/worn : "bon
   etat" est desormais neutre (le palier de reference, voir open-pack.json)
   et "parfait etat" recoit un traitement qui l'embellit (plus vif/lumineux +
   un reflet qui brille), pour qu'il se distingue vraiment de "bon etat" et
   pas seulement par l'absence d'un defaut. Applique partout ou une carte
   possedee s'affiche : collection, modale, decraft (craft.html), reveal de
   pack (.card). */
.card-art { position: relative; display: block; overflow: hidden; }
.collection-card img, .craft-card img, .card-modal img, .card-front img { position: relative; }
.collection-card[data-quality="damaged"] img,
.craft-card[data-quality="damaged"] img,
.card-modal[data-quality="damaged"] img,
.card[data-quality="damaged"] .card-front img {
  filter: sepia(0.15) saturate(0.25) contrast(1.25) brightness(0.78);
}
.collection-card[data-quality="worn"] img,
.craft-card[data-quality="worn"] img,
.card-modal[data-quality="worn"] img,
.card[data-quality="worn"] .card-front img {
  filter: saturate(0.55) contrast(1.08) brightness(0.9);
}
/* "Bon etat" : aucun filtre - c'est le palier neutre de reference. */
.collection-card[data-quality="mint"] img,
.craft-card[data-quality="mint"] img,
.card-modal[data-quality="mint"] img,
.card[data-quality="mint"] .card-front img {
  filter: saturate(1.2) contrast(1.07) brightness(1.05);
}
.collection-card[data-quality] .card-art::before,
.craft-card[data-quality] .card-art::before,
.card-modal[data-quality] .card-art::before,
.card[data-quality] .card-art::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  mix-blend-mode: screen;
  opacity: 0;
}
.collection-card[data-quality="damaged"] .card-art::before,
.craft-card[data-quality="damaged"] .card-art::before,
.card-modal[data-quality="damaged"] .card-art::before,
.card[data-quality="damaged"] .card-art::before {
  opacity: 1;
  background:
    repeating-linear-gradient(58deg, rgba(255,255,255,0.5) 0 1px, transparent 1px 26px),
    repeating-linear-gradient(-42deg, rgba(255,255,255,0.4) 0 1px, transparent 1px 20px),
    repeating-linear-gradient(150deg, rgba(255,255,255,0.28) 0 1px, transparent 1px 16px);
}
.collection-card[data-quality="worn"] .card-art::before,
.craft-card[data-quality="worn"] .card-art::before,
.card-modal[data-quality="worn"] .card-art::before,
.card[data-quality="worn"] .card-art::before {
  opacity: 1;
  background:
    repeating-linear-gradient(58deg, rgba(255,255,255,0.3) 0 1px, transparent 1px 34px),
    repeating-linear-gradient(-42deg, rgba(255,255,255,0.22) 0 1px, transparent 1px 28px);
}
/* "Parfait etat" : au lieu d'une texture d'usure, un reflet brillant qui
   balaie doucement l'image - le signal inverse de l'abime, jamais juste
   "rien". Egalement un fin liseret clair (outline, n'entre pas en conflit
   avec le border-color/box-shadow deja utilises par la finition/la rarete). */
.collection-card[data-quality="mint"] .card-art::before,
.craft-card[data-quality="mint"] .card-art::before,
.card-modal[data-quality="mint"] .card-art::before,
.card[data-quality="mint"] .card-art::before {
  opacity: 1;
  background: linear-gradient(115deg, transparent 35%, rgba(255,255,255,0.55) 50%, transparent 65%);
  background-size: 260% 260%;
  animation: quality-mint-sweep 3.2s ease-in-out infinite;
}
@keyframes quality-mint-sweep {
  0%, 100% { background-position: 90% 10%; }
  50% { background-position: 10% 90%; }
}
.collection-card[data-quality="mint"],
.craft-card[data-quality="mint"],
.card-modal[data-quality="mint"],
.card[data-quality="mint"] .card-front {
  outline: 2px solid rgba(224,247,255,0.65);
  outline-offset: 1px;
}
@media (prefers-reduced-motion: reduce) {
  .collection-card[data-quality="mint"] .card-art::before,
  .craft-card[data-quality="mint"] .card-art::before,
  .card-modal[data-quality="mint"] .card-art::before,
  .card[data-quality="mint"] .card-art::before { animation: none; }
}

/* Effet visuel applique directement sur la vignette/la modale, par-dessus
   l'image (voir .collection-card { position:relative; overflow:hidden } et
   .card-modal ci-dessous) : un vrai filtre de couleur SUR l'image (pas
   juste un calque par-dessus) + une texture/un halo marques par palier.
   Volontairement plus appuye que la v1 (retour utilisateur : "a peine
   visible") - l'oeuvre reste identifiable, mais la finition doit sauter
   aux yeux au premier coup d'oeil, pas seulement au survol. */
.card-modal { position: relative; }
.collection-card[data-finish]:not([data-finish="normal"]) .card-art::after,
.card-modal[data-finish]:not([data-finish="normal"]) .card-art::after,
.craft-card[data-finish]:not([data-finish="normal"]) .card-art::after,
.card[data-finish]:not([data-finish="normal"]) .card-art::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 1;
}
/* Le contour de finition vit sur le contenant possedant reellement une
   bordure visible : .card-front pour le reveal de pack (.card est juste le
   conteneur de flip, sans bordure propre). */
.card[data-finish]:not([data-finish="normal"]) .card-front { border-width: 2px; }

.collection-card[data-finish="holo"] img,
.card-modal[data-finish="holo"] img,
.craft-card[data-finish="holo"] img,
.card[data-finish="holo"] .card-front img { filter: saturate(1.65) contrast(1.1) brightness(1.04); }
.collection-card[data-finish="holo"],
.card-modal[data-finish="holo"],
.craft-card[data-finish="holo"] { border-color: #a78bfa !important; border-width: 2px; box-shadow: 0 0 18px rgba(167,139,250,0.6); }
.card[data-finish="holo"] .card-front { border-color: #a78bfa !important; box-shadow: 0 0 18px rgba(167,139,250,0.6); }
.collection-card[data-finish="holo"] .card-art::after,
.card-modal[data-finish="holo"] .card-art::after,
.craft-card[data-finish="holo"] .card-art::after,
.card[data-finish="holo"] .card-art::after {
  background:
    radial-gradient(circle at var(--shine-x, 50%) var(--shine-y, 50%), rgba(196,181,253,0.85), rgba(103,232,249,0.55) 40%, transparent 70%),
    repeating-linear-gradient(115deg, rgba(255,255,255,0.2) 0 4px, transparent 4px 14px, rgba(103,232,249,0.18) 14px 18px, transparent 18px 28px);
  mix-blend-mode: screen;
  animation: finish-holo-shift 3.5s ease-in-out infinite;
}
@keyframes finish-holo-shift {
  0%, 100% { background-position: 0 0, 0 0; }
  50% { background-position: 0 0, 40px 0; }
}

.collection-card[data-finish="gold"] img,
.card-modal[data-finish="gold"] img,
.craft-card[data-finish="gold"] img,
.card[data-finish="gold"] .card-front img { filter: sepia(0.7) saturate(2.4) brightness(1.06) hue-rotate(-8deg); }
.collection-card[data-finish="gold"],
.card-modal[data-finish="gold"],
.craft-card[data-finish="gold"] { border-color: #d4af37 !important; border-width: 2px; box-shadow: 0 0 22px rgba(212,175,55,0.65); }
.card[data-finish="gold"] .card-front { border-color: #d4af37 !important; box-shadow: 0 0 22px rgba(212,175,55,0.65); }
.collection-card[data-finish="gold"] .card-art::after,
.card-modal[data-finish="gold"] .card-art::after,
.craft-card[data-finish="gold"] .card-art::after,
.card[data-finish="gold"] .card-art::after {
  background:
    linear-gradient(135deg, rgba(255,223,120,0.6), transparent 55%),
    repeating-radial-gradient(circle at 20% 15%, rgba(255,235,180,0.28) 0 2px, transparent 2px 9px);
  mix-blend-mode: overlay;
}

.collection-card[data-finish="ghost"] img,
.card-modal[data-finish="ghost"] img,
.craft-card[data-finish="ghost"] img,
.card[data-finish="ghost"] .card-front img { filter: grayscale(0.65) brightness(1.3) contrast(0.78); opacity: 0.85; }
.collection-card[data-finish="ghost"],
.card-modal[data-finish="ghost"],
.craft-card[data-finish="ghost"] { border-color: #94a3b8 !important; border-width: 2px; box-shadow: 0 0 20px rgba(148,163,184,0.6); }
.card[data-finish="ghost"] .card-front { border-color: #94a3b8 !important; box-shadow: 0 0 20px rgba(148,163,184,0.6); }
.collection-card[data-finish="ghost"] .card-art::after,
.card-modal[data-finish="ghost"] .card-art::after,
.craft-card[data-finish="ghost"] .card-art::after,
.card[data-finish="ghost"] .card-art::after {
  background:
    linear-gradient(180deg, rgba(255,255,255,0.4), rgba(148,163,184,0.22)),
    repeating-radial-gradient(circle at 70% 60%, rgba(255,255,255,0.1) 0 3px, transparent 3px 10px);
  mix-blend-mode: screen;
  animation: finish-ghost-breathe 3s ease-in-out infinite;
}
@keyframes finish-ghost-breathe {
  0%, 100% { opacity: 0.75; }
  50% { opacity: 1; }
}

.collection-card[data-finish="diamond"] img,
.card-modal[data-finish="diamond"] img,
.craft-card[data-finish="diamond"] img,
.card[data-finish="diamond"] .card-front img { filter: contrast(1.3) brightness(1.16) saturate(0.8); }
.collection-card[data-finish="diamond"],
.card-modal[data-finish="diamond"],
.craft-card[data-finish="diamond"] { border-color: #7dd3fc !important; border-width: 2px; box-shadow: 0 0 24px rgba(125,211,252,0.7); }
.card[data-finish="diamond"] .card-front { border-color: #7dd3fc !important; box-shadow: 0 0 24px rgba(125,211,252,0.7); }
.collection-card[data-finish="diamond"] .card-art::after,
.card-modal[data-finish="diamond"] .card-art::after,
.craft-card[data-finish="diamond"] .card-art::after,
.card[data-finish="diamond"] .card-art::after {
  background:
    radial-gradient(circle at var(--shine-x, 50%) var(--shine-y, 50%), rgba(255,255,255,0.95), rgba(125,211,252,0.55) 30%, transparent 65%),
    repeating-linear-gradient(60deg, rgba(255,255,255,0.18) 0 2px, transparent 2px 12px),
    repeating-linear-gradient(-60deg, rgba(255,255,255,0.13) 0 2px, transparent 2px 12px);
  mix-blend-mode: screen;
}

.collection-card[data-finish="rainbow"] img,
.card-modal[data-finish="rainbow"] img,
.craft-card[data-finish="rainbow"] img,
.card[data-finish="rainbow"] .card-front img { filter: saturate(1.75) contrast(1.16); }
.collection-card[data-finish="rainbow"],
.card-modal[data-finish="rainbow"],
.craft-card[data-finish="rainbow"] { border-color: #ff6ec7 !important; border-width: 2px; box-shadow: 0 0 28px rgba(255,110,199,0.65); }
.card[data-finish="rainbow"] .card-front { border-color: #ff6ec7 !important; box-shadow: 0 0 28px rgba(255,110,199,0.65); }
.collection-card[data-finish="rainbow"] .card-art::after,
.card-modal[data-finish="rainbow"] .card-art::after,
.craft-card[data-finish="rainbow"] .card-art::after,
.card[data-finish="rainbow"] .card-art::after {
  background:
    repeating-linear-gradient(115deg, #ff6ec7 0 8%, #ffd76e 8% 16%, #7dd3fc 16% 24%, #a78bfa 24% 32%, #ff6ec7 32% 40%);
  background-size: 250% 250%;
  opacity: 0.6;
  mix-blend-mode: color-dodge;
  animation: finish-rainbow-shift 4s linear infinite;
}
@keyframes finish-rainbow-shift {
  0% { background-position: 0% 50%; }
  100% { background-position: 100% 50%; }
}

/* Degrade de fond qui reagit lui aussi a la finition (en plus du filtre sur
   l'image et du contour ci-dessus), directement SOUS l'image (sur le panneau
   d'info, pas sur tout le contenant) : reste visible quelle que soit la
   hauteur totale de la carte (l'image occuperait sinon la quasi-totalite
   d'un degrade calcule sur tout le contenant). Applique partout ou une carte
   finie s'affiche (collection, modale, onglet decraft de craft.html, reveal
   de pack). */
.collection-card[data-finish]:not([data-finish="normal"]) .card-info,
.card-modal[data-finish]:not([data-finish="normal"]) .card-modal-body,
.craft-card[data-finish]:not([data-finish="normal"]) .card-info,
.card[data-finish]:not([data-finish="normal"]) .card-info {
  background: linear-gradient(180deg, var(--finish-glow, transparent) 0%, transparent 85%);
}
.collection-card[data-finish="holo"], .card-modal[data-finish="holo"], .craft-card[data-finish="holo"], .card[data-finish="holo"] { --finish-glow: rgba(167, 139, 250, 0.45); }
.collection-card[data-finish="gold"], .card-modal[data-finish="gold"], .craft-card[data-finish="gold"], .card[data-finish="gold"] { --finish-glow: rgba(212, 175, 55, 0.45); }
.collection-card[data-finish="ghost"], .card-modal[data-finish="ghost"], .craft-card[data-finish="ghost"], .card[data-finish="ghost"] { --finish-glow: rgba(148, 163, 184, 0.45); }
.collection-card[data-finish="diamond"], .card-modal[data-finish="diamond"], .craft-card[data-finish="diamond"], .card[data-finish="diamond"] { --finish-glow: rgba(125, 211, 252, 0.45); }
.collection-card[data-finish="rainbow"] .card-info,
.card-modal[data-finish="rainbow"] .card-modal-body,
.craft-card[data-finish="rainbow"] .card-info,
.card[data-finish="rainbow"] .card-info {
  background: linear-gradient(90deg, rgba(255,110,199,0.35), rgba(255,215,110,0.3), rgba(125,211,252,0.3), rgba(167,139,250,0.35));
}
@media (prefers-reduced-motion: reduce) {
  .collection-card[data-finish="rainbow"] .card-art::after,
  .card-modal[data-finish="rainbow"] .card-art::after,
  .craft-card[data-finish="rainbow"] .card-art::after,
  .card[data-finish="rainbow"] .card-art::after,
  .collection-card[data-finish="holo"] .card-art::after,
  .card-modal[data-finish="holo"] .card-art::after,
  .craft-card[data-finish="holo"] .card-art::after,
  .card[data-finish="holo"] .card-art::after,
  .collection-card[data-finish="ghost"] .card-art::after,
  .card-modal[data-finish="ghost"] .card-art::after,
  .craft-card[data-finish="ghost"] .card-art::after,
  .card[data-finish="ghost"] .card-art::after { animation: none; }
}

/* Dissolution "poussiere d'etoile" a la destruction d'une carte (voir
   playDustDissolve, main.js) : la vignette se retrecit et se floute avant
   de disparaitre, en meme temps que les particules de spawnRarityBurst. */
.craft-card.dust-dissolve,
.collection-card.dust-dissolve {
  animation: dust-dissolve-out 0.38s ease forwards;
  pointer-events: none;
}
@keyframes dust-dissolve-out {
  0% { opacity: 1; filter: blur(0) brightness(1); transform: scale(1); }
  100% { opacity: 0; filter: blur(6px) brightness(1.6); transform: scale(0.75); }
}
@media (prefers-reduced-motion: reduce) {
  .craft-card.dust-dissolve,
  .collection-card.dust-dissolve { animation: none; opacity: 0.3; }
}

.craft-toolbar .filters { margin: 0; }
.checkbox-inline {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 0.85rem;
  color: var(--text-dim);
  white-space: nowrap;
}
.checkbox-inline input { width: auto; }

.craft-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: 18px;
}
.craft-card {
  border-radius: 14px;
  border: 2px solid var(--border-soft);
  background: var(--bg-panel-2);
  overflow: hidden;
  transition: transform 0.25s ease, border-color 0.3s ease, box-shadow 0.3s ease;
}
/* Meme bascule 3D + reflet suivant le curseur que .collection-card (voir
   craft.js attachTilt) - rend les finitions holo/diamant/arc-en-ciel du
   Décrafter aussi vivantes qu'en collection. */
.craft-card:hover {
  transform: translateY(-4px) scale(1.02) rotateX(var(--rx, 2deg)) rotateY(var(--ry, 0deg));
  box-shadow: 0 14px 28px -14px color-mix(in srgb, var(--accent) 50%, transparent);
}
/* Lien direct depuis une carte verrouillee de la collection (?cardId=...). */
.craft-card.highlighted {
  border-color: var(--accent-2);
  box-shadow: 0 0 0 3px rgba(34, 211, 238, 0.35), 0 0 24px rgba(34, 211, 238, 0.4);
}
.craft-card img { width: 100%; aspect-ratio: 3/4; object-fit: cover; display: block; }
.craft-card .card-info { padding: 8px 10px 12px; }
.craft-card .craft-cost { font-size: 0.78rem; color: var(--accent-3); font-weight: 700; margin: 4px 0 8px; }
/* Calculateur "il te manque X" (QoL 2026-09-30) : remplace le simple grise
   "indisponible" par un chiffre exploitable, pas juste "pas assez". */
.craft-card .craft-missing { font-size: 0.72rem; color: var(--text-dim); margin: -6px 0 8px; }
.craft-card button { width: 100%; padding: 8px; font-size: 0.82rem; }
.craft-card .owned-count { font-size: 0.7rem; color: var(--text-dim); }
.disenchant-btn { background: linear-gradient(135deg, var(--accent-dark), var(--accent-3)); }
.craft-card.unavailable { opacity: 0.5; filter: grayscale(0.6); }
.craft-card.unavailable img { filter: grayscale(0.4); }

/* Coffre de guilde : une ligne par variante (finish+qualite) REELLEMENT
   possedee sur une meme vignette carte, chacune avec son propre bouton
   "Déposer" - pour choisir VRAIMENT quel exemplaire part, pas un doublon
   au hasard parmi tous ceux possedes (voir communaute.js chestCardTile). */
.chest-variant-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 6px 0;
  border-top: 1px solid var(--border-soft);
}
.chest-variant-row:first-of-type { border-top: none; margin-top: 4px; }
.chest-variant-tags { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }
.chest-variant-count { font-size: 0.72rem; color: var(--text-dim); font-weight: 700; }
.chest-variant-row .chest-deposit-btn { width: auto; flex-shrink: 0; padding: 6px 12px; font-size: 0.74rem; }

/* Selecteur de quantite +/- (craft/decraft) */
.qty-stepper {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  margin: 0 0 8px;
}
.qty-stepper .qty-btn {
  width: 28px;
  height: 28px;
  padding: 0;
  flex-shrink: 0;
  border-radius: 8px;
  background: var(--bg-panel-2);
  border: 1px solid var(--border-soft);
  color: var(--text);
  font-size: 1rem;
  font-weight: 700;
  line-height: 1;
  cursor: pointer;
}
.qty-stepper .qty-btn:hover:not(:disabled) { border-color: var(--accent-2); color: var(--accent-2); }
.qty-stepper .qty-btn:disabled { opacity: 0.35; cursor: not-allowed; }
.qty-stepper .qty-value {
  min-width: 24px;
  text-align: center;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.9rem;
}

/* Decraft en masse */
.craft-card { position: relative; }
.craft-card.bulk-mode,
.collection-card.bulk-mode { cursor: pointer; }
.craft-card.selected,
.collection-card.selected { border-color: var(--accent-2); box-shadow: 0 0 0 2px var(--accent-2); }
.bulk-checkbox {
  position: absolute;
  top: 8px;
  right: 8px;
  z-index: 2;
  width: 22px;
  height: 22px;
  background: rgba(0,0,0,0.6);
  border-radius: 6px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.bulk-checkbox input { width: 16px; height: 16px; accent-color: var(--accent-2); }
.suggestions-list { display: flex; flex-direction: column; gap: 10px; margin-top: 10px; }
.suggestion-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  padding: 10px 14px;
  background: var(--bg-panel-2);
  border: 1px solid var(--border-soft);
  border-radius: 10px;
  font-size: 0.88rem;
}

.bulk-action-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  background: var(--bg-panel-2);
  border: 1px solid var(--border-soft);
  border-radius: 12px;
  padding: 12px 16px;
  margin: 12px 0;
  font-size: 0.88rem;
  color: var(--text-dim);
}

.promo-badge {
  display: inline-block;
  font-size: 0.6rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 2px 7px;
  border-radius: 999px;
  background: linear-gradient(135deg, var(--accent-3), var(--accent));
  color: white;
  margin-left: 6px;
}

/* ========================================================================
   Formatage visuel du champ code (redeem.html)
   ======================================================================== */
.code-form input[type="text"] { letter-spacing: 0.12em; }
.redeem-card { flex-direction: column; text-align: center; }

.card-preview-thumb {
  width: 44px;
  height: 58px;
  object-fit: cover;
  border-radius: 6px;
  border: 1.5px solid var(--border-soft);
  flex-shrink: 0;
  background: var(--bg-panel-2);
}

/* ========================================================================
   Mini bar chart (admin - repartition des raretes)
   ======================================================================== */
.rarity-chart {
  display: flex;
  align-items: flex-end;
  gap: 14px;
  height: 140px;
  padding: 10px 6px 0;
  margin: 16px 0 6px;
  border-bottom: 1px solid var(--border-soft);
}
.rarity-chart .bar-col {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  height: 100%;
  gap: 6px;
}
.rarity-chart .bar-value { font-size: 0.72rem; color: var(--text-dim); font-variant-numeric: tabular-nums; }
.rarity-chart .bar-fill {
  width: 60%;
  min-width: 18px;
  border-radius: 6px 6px 0 0;
  transition: height 0.6s cubic-bezier(.2,.9,.3,1);
}
.rarity-chart .bar-label { font-size: 0.68rem; color: var(--text-dim); text-align: center; }

/* ========================================================================
   Particules de rarete (revelation de carte) : DOIT rester au-dessus du
   modal plein ecran d'ouverture (z-index 400) et de son flash legendaire
   (z-index 410).
   ======================================================================== */
.rarity-particle-layer {
  position: fixed;
  inset: 0;
  z-index: 420;
  pointer-events: none;
  overflow: hidden;
}
.rarity-particle {
  position: absolute;
  transform: translate(-50%, -50%);
  animation-name: rarityParticleFly;
  animation-timing-function: ease-out;
  animation-fill-mode: forwards;
  text-shadow: 0 0 8px currentColor, 0 0 16px currentColor;
  line-height: 1;
}
@keyframes rarityParticleFly {
  0% { translate: 0 0; scale: 0.4; rotate: 0deg; opacity: 0; }
  15% { opacity: 1; }
  100% { translate: var(--dx) var(--dy); scale: 1; rotate: 180deg; opacity: 0; }
}

/* Eclats de foil dechire (ouverture de booster, voir opening.js
   spawnFoilShards) : de petits rectangles colores qui explosent puis
   retombent, contrairement aux etoiles de rarete qui s'envolent et
   s'estompent sur place - distinct visuellement du sparkle de rarete. */
.foil-shard {
  position: absolute;
  width: 7px;
  height: 11px;
  transform: translate(-50%, -50%);
  animation-name: foilShardFly;
  animation-timing-function: cubic-bezier(.25,.7,.4,1);
  animation-fill-mode: forwards;
  border-radius: 2px;
}
@keyframes foilShardFly {
  0% { translate: 0 0; rotate: 0deg; opacity: 1; }
  100% { translate: var(--dx) var(--dy); rotate: var(--rot); opacity: 0; }
}

/* ========================================================================
   Transitions de page (View Transitions API)
   ======================================================================== */
::view-transition-old(root) { animation: viewFadeOut 0.22s ease both; }
::view-transition-new(root) { animation: viewFadeIn 0.28s ease both; }
@keyframes viewFadeOut { to { opacity: 0; transform: scale(0.99); } }
@keyframes viewFadeIn { from { opacity: 0; transform: scale(1.01); } }

/* ========================================================================
   Mode cinema (collection) : masque le chrome, plein ecran sur la grille
   ======================================================================== */
body.cinema-mode header#site-header,
body.cinema-mode .bottom-nav,
body.cinema-mode #stats-block,
body.cinema-mode .coll-summary,
body.cinema-mode #unused-dust-reminder { display: none !important; }
body.cinema-mode main { max-width: none; padding: 24px; }
body.cinema-mode .panel { background: rgba(6,7,15,0.7); border: none; box-shadow: none; }

.back-to-top {
  position: fixed;
  right: 20px;
  bottom: 20px;
  z-index: 60;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  padding: 0;
  font-size: 1.1rem;
  opacity: 0;
  pointer-events: none;
  transform: translateY(10px);
  transition: opacity 0.25s ease, transform 0.25s ease;
}
.back-to-top.visible { opacity: 1; pointer-events: auto; transform: translateY(0); }
@media (max-width: 640px) { .back-to-top { bottom: calc(76px + env(safe-area-inset-bottom, 0px)); } }

/* ========================================================================
   Grille "classeur" dense (alternative a la grille de collection)
   ======================================================================== */
/* 8 colonnes fixes sur desktop (au lieu d'un auto-fill qui en case bien plus
   sur un grand ecran et rendait les vignettes minuscules) - reduit sur les
   ecrans plus etroits, jamais moins de 3, pour rester lisible. */
.collection-grid.dense {
  grid-template-columns: repeat(8, 1fr);
  gap: 8px;
}
@media (max-width: 900px) {
  .collection-grid.dense { grid-template-columns: repeat(5, 1fr); }
}
@media (max-width: 560px) {
  .collection-grid.dense { grid-template-columns: repeat(3, 1fr); }
}
.collection-grid.dense .card-info { padding: 4px; }
.collection-grid.dense .card-name { font-size: 0.68rem; }
.collection-grid.dense .rarity-badge { display: none; }
/* Les actions rapides (crafter/decrafter/echanger) ne rentrent pas dans une
   vignette compacte et vont a l'encontre du but de la vue dense (parcourir
   vite) : on les masque, l'ouverture de la modale carte reste le chemin pour
   agir depuis cette vue. */
.collection-grid.dense .quick-craft-btn,
.collection-grid.dense .quick-actions-row { display: none; }
/* Badges finition/qualite : le texte ("ARC-EN-CIEL", "ENDOMMAGE"...) deborde
   et se chevauche sur une vignette compacte - en vue dense, on les reduit a
   un simple point colore (garde la couleur/le sens via la bordure, jamais de
   texte a faire tenir). Meme logique pour le badge "New", reduit pour ne
   jamais toucher l'etoile favori au coin oppose. */
.collection-grid.dense .finish-indicator,
.collection-grid.dense .quality-indicator {
  width: 10px;
  height: 10px;
  padding: 0;
  border-radius: 50%;
  font-size: 0;
  overflow: hidden;
  bottom: 6px;
}
.collection-grid.dense .finish-indicator { left: 6px; }
.collection-grid.dense .quality-indicator { right: 6px; }
.collection-grid.dense .new-badge { font-size: 0.5rem; padding: 2px 5px; top: 4px; left: 4px; }
.collection-grid.dense .fav-btn,
.collection-grid.dense .wishlist-btn { top: 4px; right: 4px; width: 20px; height: 20px; font-size: 0.75rem; }

/* ========================================================================
   Cadre special pour les cartes promo dans la collection
   ======================================================================== */
.collection-card[data-promo="1"] {
  border-color: var(--accent-3);
  box-shadow: 0 0 0 1px var(--accent-3), 0 0 20px -4px rgba(244, 114, 182, 0.5);
}

/* ========================================================================
   Menu utilisateur (dropdown) dans le header
   ======================================================================== */
.user-menu { position: relative; }
.user-menu-trigger {
  display: flex;
  align-items: center;
  gap: 8px;
  background: transparent;
  border: 1px solid var(--border-soft);
  padding: 5px 12px 5px 5px;
  border-radius: 999px;
  color: var(--text);
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 0.88rem;
  box-shadow: none;
}
.user-menu-trigger .caret { font-size: 0.65rem; color: var(--text-dim); transition: transform 0.15s; }
.user-menu.open .user-menu-trigger .caret { transform: rotate(180deg); }
.user-menu-dropdown {
  position: absolute;
  top: calc(100% + 10px);
  right: 0;
  min-width: 200px;
  background: var(--bg-panel-solid);
  border: 1px solid var(--border-soft);
  border-radius: 12px;
  box-shadow: 0 20px 50px -16px rgba(0,0,0,0.7);
  padding: 8px;
  display: none;
  z-index: 20;
}
.user-menu.open .user-menu-dropdown { display: block; animation: modalPopIn 0.15s ease forwards; }
.user-menu-dropdown button, .user-menu-dropdown a {
  display: flex;
  width: 100%;
  align-items: center;
  gap: 8px;
  background: transparent;
  border: none;
  box-shadow: none;
  color: var(--text-dim);
  font-family: var(--font-body);
  font-weight: 500;
  font-size: 0.88rem;
  padding: 9px 10px;
  border-radius: 8px;
  text-align: left;
  cursor: pointer;
  transform: none !important;
}
.user-menu-dropdown button:hover, .user-menu-dropdown a:hover { background: rgba(255,255,255,0.06); color: var(--text); }
.user-menu-dropdown .menu-sep { height: 1px; background: var(--border-soft); margin: 6px 2px; }

/* Cloche de notifications (QoL 2026-09-30) : meme squelette que .user-menu,
   garde une trace des derniers Toasts (voir main.js NotificationHistory) -
   utile sur ce site multi-pages ou un Toast manque en changeant de page est
   perdu pour toujours sans ca. */
.notif-bell { position: relative; }
.notif-bell-trigger {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  background: transparent;
  border: 1px solid var(--border-soft);
  border-radius: 999px;
  font-size: 1rem;
  box-shadow: none;
}
.notif-bell-dot {
  position: absolute;
  top: 4px;
  right: 5px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--danger);
  box-shadow: 0 0 0 2px var(--bg-panel-solid);
}
.notif-bell-dropdown {
  position: absolute;
  top: calc(100% + 10px);
  right: 0;
  width: min(320px, 85vw);
  max-height: 380px;
  overflow-y: auto;
  background: var(--bg-panel-solid);
  border: 1px solid var(--border-soft);
  border-radius: 12px;
  box-shadow: 0 20px 50px -16px rgba(0,0,0,0.7);
  padding: 8px;
  display: none;
  z-index: 20;
}
.notif-bell.open .notif-bell-dropdown { display: block; animation: modalPopIn 0.15s ease forwards; }
.notif-bell-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 4px 10px 10px;
  margin-bottom: 4px;
  border-bottom: 1px solid var(--border-soft);
  font-family: var(--font-display);
  font-size: 0.78rem;
  color: var(--text-dim);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.notif-clear-btn {
  background: transparent;
  border: none;
  box-shadow: none;
  color: var(--accent-2);
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 0.78rem;
  text-transform: none;
  letter-spacing: normal;
  padding: 2px 4px;
  cursor: pointer;
}
.notif-clear-btn:hover { color: var(--text); text-decoration: underline; }
.notif-item {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  padding: 8px 10px;
  border-radius: 8px;
  font-size: 0.85rem;
}
.notif-item + .notif-item { border-top: 1px solid var(--border-soft); }
.notif-item-icon { flex-shrink: 0; margin-top: 1px; }
.notif-item-success .notif-item-icon { color: var(--success); }
.notif-item-error .notif-item-icon { color: var(--danger); }
.notif-item-info .notif-item-icon { color: var(--accent-2); }
.notif-item-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.notif-item-msg { color: var(--text); line-height: 1.35; word-wrap: break-word; }
.notif-item-time { color: var(--text-dim); font-size: 0.72rem; }
.notif-empty { color: var(--text-dim); text-align: center; padding: 20px 10px; font-size: 0.85rem; }

/* Recherche globale (QoL 2026-09-30, demande explicite : les profils sont
   peu accessibles) : meme squelette que .notif-bell, trouve un joueur, une
   carte ou une page depuis n'importe quelle page. */
.global-search { position: relative; }
.global-search-trigger {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  background: transparent;
  border: 1px solid var(--border-soft);
  border-radius: 999px;
  font-size: 1rem;
  box-shadow: none;
}
.global-search-panel {
  position: absolute;
  top: calc(100% + 10px);
  right: 0;
  width: min(320px, 85vw);
  background: var(--bg-panel-solid);
  border: 1px solid var(--border-soft);
  border-radius: 12px;
  box-shadow: 0 20px 50px -16px rgba(0,0,0,0.7);
  padding: 10px;
  display: none;
  z-index: 21;
}
.global-search.open .global-search-panel { display: block; animation: modalPopIn 0.15s ease forwards; }
.global-search-panel input {
  width: 100%;
  background: var(--bg-panel-2);
  border: 1px solid var(--border-soft);
  color: var(--text);
  padding: 8px 10px;
  border-radius: 8px;
  font-size: 0.9rem;
  font-family: var(--font-body);
  margin-bottom: 8px;
}
.global-search-results { max-height: 340px; overflow-y: auto; }
.global-search-group { font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-dim); padding: 6px 8px 2px; }
.global-search-item { display: block; padding: 8px; border-radius: 8px; color: var(--text); font-size: 0.88rem; text-decoration: none; }
.global-search-item:hover { background: rgba(255,255,255,0.06); }
.global-search-hint { color: var(--text-dim); font-size: 0.82rem; padding: 10px 8px; text-align: center; }

/* ========================================================================
   Barre de chargement (haut de page, pendant les appels API)
   ======================================================================== */
.top-loading-bar {
  position: fixed;
  top: 0; left: 0;
  height: 2.5px;
  width: 0%;
  background: linear-gradient(90deg, var(--accent), var(--accent-2));
  z-index: 999;
  transition: width 0.25s ease, opacity 0.2s ease;
  box-shadow: 0 0 8px var(--accent-2);
}
.top-loading-bar.done { width: 100%; opacity: 0; }

/* ========================================================================
   Bandeau "prochain jalon" / milestone
   ======================================================================== */
.milestone-banner {
  display: flex;
  align-items: center;
  gap: 10px;
  background: var(--bg-panel-2);
  border: 1px solid var(--border-soft);
  border-radius: 12px;
  padding: 10px 16px;
  font-size: 0.86rem;
  color: var(--text-dim);
  margin: 12px 0;
}
.milestone-banner strong { color: var(--text); }

/* ========================================================================
   En-tete de profil public
   ======================================================================== */
.profile-header {
  display: flex;
  align-items: center;
  gap: 20px;
  flex-wrap: wrap;
  margin-bottom: 20px;
}
.profile-avatar {
  width: 84px;
  height: 84px;
  border-radius: 50%;
  border: 3px solid var(--accent);
  box-shadow: 0 0 24px rgba(139, 92, 246, 0.4);
  object-fit: cover;
  flex-shrink: 0;
}
.profile-identity { flex: 1; min-width: 200px; }
.profile-name {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 2rem;
  background: linear-gradient(90deg, var(--accent), var(--accent-2));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  margin: 0;
}
.profile-meta { color: var(--text-dim); font-size: 0.85rem; margin: 4px 0 0; }

.profile-name-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }

/* Niveaux de profil : pastille discrete, jamais un chiffre isole sans
   contexte - toujours "Niv. X" pour rester lisible hors contexte (partage,
   capture d'ecran). */
.level-badge {
  display: inline-flex;
  align-items: center;
  background: linear-gradient(135deg, var(--accent), var(--accent-2));
  color: #0d0f1a;
  padding: 3px 11px;
  border-radius: 999px;
  font-size: 0.78rem;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* Mini-grille des cartes recherchees (wishlist) sur le profil public : plus
   petite que la grille de collection, juste de quoi reperer une carte. */
.wishlist-mini-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(80px, 1fr));
  gap: 10px;
  margin: 12px 0;
}
.wishlist-mini-card { display: flex; flex-direction: column; align-items: center; gap: 4px; position: relative; }
.wishlist-mini-card img {
  width: 100%;
  aspect-ratio: 3/4;
  object-fit: cover;
  border-radius: 8px;
  border: 2px solid var(--border-soft);
}
/* Indicateur "réalisable" (2026-09-30) : signale qu'un souhait est deja a
   portee de poussieres, pas besoin d'attendre un tirage. */
.wishlist-mini-card.craftable img { border-color: var(--success); box-shadow: 0 0 12px rgba(74,222,128,0.5); }
.wishlist-craftable-badge {
  position: absolute;
  top: -4px;
  right: 4px;
  z-index: 1;
  background: var(--success);
  color: #06210f;
  border-radius: 50%;
  width: 20px;
  height: 20px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.75rem;
  box-shadow: 0 2px 6px rgba(0,0,0,0.5);
}
.wishlist-mini-name {
  font-size: 0.68rem;
  color: var(--text-dim);
  text-align: center;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 100%;
}

/* Vitrine de profil : jusqu'a 5 cartes choisies par le joueur, mises en
   avant en plus grand que la mini-grille de wishlist - c'est la premiere
   chose qu'un visiteur doit remarquer sur le profil, avant la collection
   complete plus bas. */
.showcase-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  gap: 14px;
  margin: 12px 0 4px;
}
.showcase-card {
  border-radius: 12px;
  border: 2px solid var(--border-soft);
  overflow: hidden;
  background: var(--bg-panel-2);
  box-shadow: 0 10px 26px -14px rgba(139, 92, 246, 0.55);
}
.showcase-card img { width: 100%; aspect-ratio: 3/4; object-fit: cover; display: block; }
.showcase-card-name {
  padding: 6px 8px;
  font-size: 0.76rem;
  font-weight: 600;
  text-align: center;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Badges de sets complets (bonus de sets complets) : une pastille doree par
   extension entierement collectionnee, affichee sous l'en-tete de profil. */
.set-badge-row { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 16px; }
.set-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: linear-gradient(135deg, rgba(245,165,36,0.22), rgba(255,209,102,0.14));
  border: 1px solid var(--rarity-legendaire);
  color: var(--text);
  padding: 5px 12px;
  border-radius: 999px;
  font-size: 0.78rem;
  font-weight: 600;
}

/* ========================================================================
   Panneau de stats personnelles (collection)
   ======================================================================== */
.stats-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
  gap: 12px;
  margin: 16px 0 20px;
}
.stat-tile {
  background: var(--bg-panel-2);
  border: 1px solid var(--border-soft);
  border-radius: 12px;
  padding: 14px 16px;
  text-align: center;
  min-width: 0;
}
/* Bungee est une police d'affichage large ; une valeur textuelle longue
   ("LEGENDAIRE" dans "Meilleur pull") deborde sinon de la tuile sur mobile
   au lieu de passer a la ligne - overflow-wrap force la coupure au besoin. */
.stat-tile .stat-value { font-family: var(--font-display); font-weight: 800; font-size: 1.25rem; overflow-wrap: break-word; }
.stat-tile .stat-label { font-size: 0.72rem; color: var(--text-dim); margin-top: 2px; }

/* ========================================================================
   Barre de pity (ouverture de booster)
   ======================================================================== */
.pity-row { display: flex; align-items: center; gap: 8px; font-size: 0.72rem; color: var(--text-dim); margin-top: 8px; }

/* ========================================================================
   Liste "gains par niveau" (page d'accueil, panneau Progression)
   ======================================================================== */
.level-rewards-details { margin-top: 14px; }
.level-rewards-details summary {
  cursor: pointer;
  font-size: 0.82rem;
  color: var(--accent-2);
  font-weight: 600;
  list-style: none;
}
.level-rewards-details summary::-webkit-details-marker { display: none; }
.level-rewards-details summary::before { content: "▸ "; }
.level-rewards-details[open] summary::before { content: "▾ "; }
.level-rewards-details .quests-list { max-height: 260px; overflow-y: auto; }
.level-claim-banner {
  margin-top: 14px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  background: linear-gradient(135deg, rgba(139, 92, 246, 0.22), rgba(34, 211, 238, 0.16));
  border: 1px solid var(--border);
  color: var(--text);
  padding: 12px 16px;
  border-radius: 12px;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.85rem;
}
.pity-row .pity-icon { flex-shrink: 0; }
.pity-row .pity-track { flex: 1; height: 6px; border-radius: 999px; background: var(--bg-panel-2); overflow: hidden; }
.pity-row .pity-fill {
  display: block;
  height: 100%;
  background: linear-gradient(90deg, var(--accent), var(--rarity-legendaire));
  transition: width 0.6s ease;
}
.pity-row .pity-label { flex-shrink: 0; font-variant-numeric: tabular-nums; }

/* ========================================================================
   Favoris (collection)
   ======================================================================== */
.fav-btn,
.wishlist-btn {
  position: absolute;
  top: 8px;
  right: 8px;
  z-index: 2;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  border: none;
  background: rgba(0,0,0,0.5);
  color: rgba(255,255,255,0.55);
  font-size: 0.85rem;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: none;
  transform: none !important;
}
.fav-btn.active { color: var(--accent-3); }
/* Jamais affiches ensemble (favoris = cartes possedees, wishlist = cartes
   manquantes), donc meme position sans conflit. Couleur distincte de
   .fav-btn pour rester reconnaissable ("je veux" vs "je prefere"). */
.wishlist-btn.active { color: var(--rarity-legendaire); }
.fav-btn:hover { color: var(--accent-3); }

/* ========================================================================
   Recherche / tri / toggles (collection)
   ======================================================================== */
.collection-toolbar {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: center;
  margin: 14px 0;
}
/* Regroupe les ~10 controles en 3 clusters lisibles (rechercher/trier,
   filtrer ma collection, options d'affichage) plutot qu'une seule rangee
   indifferenciee - meme comportement de wrap, juste une hierarchie visuelle. */
.toolbar-group { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
.toolbar-group-primary { flex: 1; min-width: 260px; }
.toolbar-divider { width: 2px; align-self: stretch; min-height: 28px; background: var(--border-soft); border-radius: 1px; }
@media (max-width: 640px) {
  .toolbar-divider { display: none; }
}
.collection-toolbar input[type="text"] { min-width: 180px; flex: 1; }
.collection-toolbar button.active { outline: 2px solid var(--accent-2); }
.collection-toolbar select {
  background: var(--bg-panel-2);
  border: 1px solid var(--border-soft);
  color: var(--text);
  padding: 10px 12px;
  border-radius: 10px;
  font-size: 0.88rem;
  font-family: var(--font-body);
}
/* Bandeau de stats (progression, jalon, tuiles) : peut se masquer via le
   bouton dedie dans la barre d'outils, pour gagner de la place. */
body.hide-stats #stats-block { display: none; }

/* Formulaire d'echange rapide (openQuickTrade(), collection.js). */
.quick-trade-box { width: min(360px, 92vw); text-align: left; }
.quick-trade-form { display: flex; flex-direction: column; gap: 14px; margin: 16px 0; }
.quick-trade-form label { display: flex; flex-direction: column; gap: 6px; font-size: 0.85rem; color: var(--text-dim); }
.quick-trade-form .fancy-select,
.quick-trade-form .fancy-select-trigger { width: 100%; }

/* ========================================================================
   QR code (admin) et vue calendrier des codes
   ======================================================================== */
.qr-box {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  background: white;
  padding: 10px;
  border-radius: 12px;
  margin-top: 10px;
}
.qr-box canvas, .qr-box img { display: block; }
.qr-box .qr-caption { color: #111; font-size: 0.7rem; font-family: var(--font-display); font-weight: 700; }

.codes-calendar { display: flex; flex-direction: column; gap: 14px; margin-top: 10px; }
.codes-calendar-day { border: 1px solid var(--border-soft); border-radius: 12px; padding: 12px 16px; background: var(--bg-panel-2); }
.codes-calendar-day .day-label { font-family: var(--font-display); font-weight: 700; font-size: 0.85rem; color: var(--accent-2); margin-bottom: 8px; }
.codes-calendar-day .day-codes { display: flex; flex-direction: column; gap: 6px; }
.codes-calendar-day .day-code-row { display: flex; justify-content: space-between; gap: 10px; font-size: 0.85rem; color: var(--text-dim); }

/* ========================================================================
   Aperçu de carte (outil admin de previsualisation, card-preview-tool.html)
   ======================================================================== */
.preview-tool-layout { display: grid; grid-template-columns: minmax(260px, 340px) 1fr; gap: 32px; align-items: start; }
.preview-tool-form { display: flex; flex-direction: column; gap: 12px; }
.preview-tool-form label { display: flex; flex-direction: column; gap: 6px; font-size: 0.85rem; color: var(--text-dim); }
.preview-tool-stage { display: flex; justify-content: center; }
.preview-tool-stage .card { width: min(260px, 100%); }
@media (max-width: 720px) { .preview-tool-layout { grid-template-columns: 1fr; } }

/* ========================================================================
   Grands ecrans : le contenu ne doit pas rester ecrase au centre d'un vide,
   on lui donne plus de place et des grilles plus genereuses.
   ======================================================================== */
@media (min-width: 1300px) {
  .header-inner, main { max-width: 1280px; }
  .panel { padding: 40px 44px; }
  .collection-grid { grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 22px; }
  .reveal-grid { grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 26px; }
  .extension-picker { grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 20px; }
  .craft-grid { grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 22px; }
  .pack-modal-stage { width: min(920px, 92vw); }
}

/* Zone intermediaire (tablette / petit laptop, 2026-09-30) : le regroupement
   par sous-menus (voir .nav-group) ne suffit pas seul a rendre .nav lisible
   ici - le reste du header (pastille ressources + recherche + cloche + menu
   utilisateur) reste sur une seule ligne et ne laisse a .nav qu'une largeur
   trop etroite pour ses 6-7 destinations restantes, meme reduites. On passe
   .nav sur sa propre ligne pleine largeur (comme le fait deja le mode mobile
   plus bas), sans pour autant basculer sur la bottom-nav (reservee aux tres
   petits ecrans, ou l'espace horizontal manque vraiment). */
@media (max-width: 1180px) and (min-width: 641px) {
  .header-inner { flex-wrap: wrap; gap: 10px; }
  .nav { order: 3; width: 100%; overflow-x: visible; flex-wrap: wrap; gap: 4px; }
}

/* --- Responsive --- */
@media (max-width: 640px) {
  .header-inner { flex-wrap: wrap; gap: 10px; }
  .nav { order: 3; width: 100%; gap: 14px; }
  h1 { font-size: 1.6rem; }
  .booster-pack { width: 170px; height: 240px; }
  .admin-table { display: block; overflow-x: auto; }

  body { padding-bottom: var(--nav-bottom-h); }
  .bottom-nav { display: flex; }
  header#site-header .nav { display: none; }
  .toast-stack { left: 16px; right: 16px; max-width: none; top: max(16px, env(safe-area-inset-top, 0px)); }
  .rarity-progress-row { grid-template-columns: 70px 1fr 44px; }

  /* Le user-box contient les ressources groupees + le menu utilisateur :
     ca ne tient pas sur une ligne en dessous de 640px, il faut wrapper. */
  .user-box { flex-wrap: wrap; justify-content: center; gap: 8px 10px; width: 100%; }
  .header-stats { padding: 4px 10px; }
  .stat-chip { font-size: 0.76rem; }

  main { padding: calc(var(--header-h, 64px) + 24px) 16px 90px; }
  .panel { padding: 20px; }

  .extension-picker { grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 12px; }
  .craft-grid { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 12px; }
  .admin-form { grid-template-columns: 1fr; }
  .reveal-grid { gap: 14px; }
  .pack-modal-overlay { padding: 12px; }
  .pack-modal-stage { gap: 18px; padding: 6px 2px 20px; }
  .rarity-chart { gap: 8px; height: 110px; }
}

/* Le flou de fond (backdrop-filter) force le moteur a recomposer tout ce
   qu'il y a derriere a chaque frame ; sur des elements fixed/sticky comme le
   header et la barre du bas, ca tourne en continu pendant tout le scroll et
   c'est une cause connue de lag sur mobile. On le desactive sur tactile/petit
   ecran et on compense par un fond plus opaque pour rester lisible. */
@media (max-width: 768px), (pointer: coarse) {
  header#site-header,
  .bottom-nav,
  .panel,
  .card-modal-overlay,
  .pack-modal-overlay {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
  header#site-header { background: rgba(6, 7, 15, 0.97); }
  .bottom-nav { background: rgba(10, 11, 24, 0.98); }
  .panel { background: rgba(20, 22, 45, 0.92); }
  .card-modal-overlay { background: rgba(4, 5, 12, 0.95); }
  .pack-modal-overlay {
    background:
      radial-gradient(circle at 50% 30%, rgba(139, 92, 246, 0.25), transparent 55%),
      rgba(4, 5, 12, 0.97);
  }
}

/* Page de maintenance (maintenance.html) : ecran plein page indépendant du
   header/nav habituel - le site est volontairement injoignable, pas de sens
   a montrer une navigation vers des pages qui rediraient aussitot ici. */
.maintenance-screen {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 32px 20px;
  gap: 10px;
}
.maintenance-icon { font-size: 3.5rem; filter: drop-shadow(0 0 20px rgba(245, 165, 36, 0.5)); }
.maintenance-brand { font-size: 2rem; margin-top: 6px; }
.maintenance-title {
  font-family: var(--font-display);
  font-size: 1.6rem;
  color: var(--text-main);
  margin: 4px 0 0;
}
.maintenance-message { color: var(--text-dim); max-width: 440px; line-height: 1.6; }
.maintenance-sub { color: var(--accent-2); font-weight: 600; max-width: 440px; }

/* ========================================================================
   Firefox : aucun mix-blend-mode a l'interieur d'une carte qui se retourne
   (2026-10-01, bug reel "les cartes ne se retournent pas sous Firefox").
   Un descendant en mix-blend-mode force Firefox a isoler/aplatir le
   contexte preserve-3d de .card-inner : la face avant s'affichait alors en
   miroir, ou le dos restait visible - uniquement pour les cartes rare+ (dos
   .card-back-shine) et les finitions/etats speciaux, jamais pour une
   commune "nue", d'ou un bug qui semblait aleatoire. Places EN FIN de
   fichier pour l'emporter (a specificite egale) sur les regles de
   finition/qualite plus haut ; ne concerne que .card (reveal de booster /
   code), la collection et la modale gardent leurs fusions d'origine.
   ======================================================================== */
.card .card-back-shine::after,
.card[data-quality] .card-art::before,
.card[data-finish] .card-art::after { mix-blend-mode: normal; }
.card .card-back-shine::after {
  background: radial-gradient(circle at 50% 40%, color-mix(in srgb, var(--shine-color, transparent) 45%, transparent), transparent 70%);
}
.card[data-finish]:not([data-finish="normal"]) .card-art::after { opacity: 0.5; }
.card[data-quality] .card-art::before { opacity: 0.6; }

/* Echange en attente devenu impossible (exemplaire offert parti ailleurs). */
.trade-unavailable-tag {
  display: inline-flex;
  align-self: flex-start;
  gap: 6px;
  padding: 3px 9px;
  border-radius: 999px;
  font-size: 0.72rem;
  background: rgba(251,113,133,0.14);
  border: 1px solid #fb7185;
  color: var(--text);
}

/* Etiquette "#001" : premier exemplaire en circulation d'une carte (numero
   de serie 1). En haut au centre de l'illustration pour ne jamais heurter
   les badges deja places dans les coins (Nouvelle !, favori, finition,
   etat). */
.serial-one-badge {
  position: absolute;
  top: 8px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 2;
  padding: 2px 8px;
  border-radius: 999px;
  font-family: var(--font-display);
  font-size: 0.66rem;
  letter-spacing: 0.04em;
  color: #1a1206;
  background: linear-gradient(135deg, #ffe08a, #f5a524);
  box-shadow: 0 0 0 1px rgba(0,0,0,0.35), 0 0 10px rgba(245,165,36,0.55);
  white-space: nowrap;
  pointer-events: none;
}
.serial-one-badge[style*="position:static"] { transform: none; display: inline-flex; align-items: center; }

/* Finition (coin gauche) + etat (coin droit) sur la meme illustration : avec
   "Parfait état" desormais toujours affiche, un libelle long ("Arc-en-ciel")
   chevauchait le badge voisin sur les cartes etroites - chacun reste dans
   sa moitie et s'abrege proprement si besoin. */
.card-art .finish-indicator:not([style*="position:static"]),
.card-art .quality-indicator:not([style*="position:static"]) {
  max-width: calc(50% - 10px);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Choix des exemplaires d'une fusion (FusionPicker, main.js). */
.fusion-picker { width: min(460px, 94vw); }
.fusion-picker-list { display: flex; flex-direction: column; gap: 6px; max-height: 320px; overflow-y: auto; margin: 12px 0 8px; text-align: left; }
.fusion-row {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 8px 10px; border-radius: 10px;
  background: var(--bg-panel-2); border: 1px solid var(--border-soft);
  font-size: 0.85rem;
}
.fusion-row.selected { border-color: #fb7185; background: rgba(251,113,133,0.08); }
.fusion-pick, .fusion-keep { display: flex; align-items: center; gap: 6px; cursor: pointer; }
.fusion-pick input, .fusion-keep input { width: auto; }
.fusion-keep { font-size: 0.75rem; color: var(--text-dim); white-space: nowrap; }
.fusion-keep input:disabled + * , .fusion-keep:has(input:disabled) { opacity: 0.4; cursor: default; }
.fusion-precious {
  font-size: 0.65rem; padding: 1px 6px; border-radius: 999px;
  background: linear-gradient(135deg, #ffe08a, #f5a524); color: #1a1206; font-weight: 700;
}
.fusion-picker-count { font-size: 0.8rem; color: var(--text-dim); text-align: left; }
.fusion-warning { color: #fb7185; font-weight: 600; }

/* Pastille "nouveau" sur un onglet (Communaute) tant que son contenu actif
   n'a pas ete consulte - voir CommunauteSignals (main.js). */
.tab { position: relative; }
.tab-new-dot {
  position: absolute; top: 9px; right: 1px;
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--danger, #fb7185);
  box-shadow: 0 0 0 2px var(--bg-panel-solid), 0 0 8px rgba(251,113,133,0.7);
}

/* Coffre de guilde : pioche au choix contre une clef (zone visible seulement
   si le joueur possede une clef). */
.chest-key-zone {
  margin-top: 18px; padding: 16px 18px; border-radius: 14px;
  border: 1px solid rgba(245,165,36,0.35);
  background: radial-gradient(circle at 0% 0%, rgba(245,165,36,0.10), transparent 60%), var(--bg-panel-2);
}
.chest-key-title { margin: 0 0 4px; font-size: 1rem; }
.chest-key-hint { margin: 0 0 12px; font-size: 0.82rem; color: var(--text-dim); }
.chest-key-pick-btn { width: 100%; margin-top: 6px; }

/* Cartes etroites (grilles craft/coffre/collection) : etiquettes plus
   compactes pour que "Arc-en-ciel" + "Parfait état" tiennent sans etre
   tronques. */
.craft-card .card-art .finish-indicator,
.craft-card .card-art .quality-indicator,
.collection-card .card-art .finish-indicator,
.collection-card .card-art .quality-indicator {
  font-size: 0.56rem;
  padding: 2px 5px;
  letter-spacing: 0;
  bottom: 6px;
}
.craft-card .card-art .finish-indicator,
.collection-card .card-art .finish-indicator { left: 5px; }
.craft-card .card-art .quality-indicator,
.collection-card .card-art .quality-indicator { right: 5px; }

/* ========================================================================
   Autel (refonte 2026-10-02) : 3 emplacements remplis a la main avec des
   exemplaires precis + grille des exemplaires posables.
   ======================================================================== */
.altar-board {
  padding: 18px; border-radius: 18px; margin-bottom: 16px;
  background:
    radial-gradient(circle at 50% 0%, rgba(239,68,68,0.16), transparent 65%),
    linear-gradient(180deg, #1d1220, #120c16);
  border: 1px solid rgba(239,68,68,0.28);
  box-shadow: inset 0 2px 18px rgba(0,0,0,0.45);
}
.altar-slots { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; max-width: 520px; margin: 0 auto; }
.altar-slot {
  position: relative; aspect-ratio: 3 / 4; border-radius: 14px;
  display: flex; flex-direction: column; align-items: center; justify-content: flex-end;
  overflow: hidden; padding: 0; box-shadow: none;
}
.altar-slot.empty {
  border: 2px dashed rgba(239,68,68,0.45); color: var(--text-dim);
  font-size: 0.75rem; justify-content: center; background: rgba(0,0,0,0.25);
  animation: altarSlotBreath 2.4s ease-in-out infinite;
}
@keyframes altarSlotBreath { 0%, 100% { border-color: rgba(239,68,68,0.3); } 50% { border-color: rgba(239,68,68,0.7); } }
.altar-slot.filled { border: 2px solid var(--slot-color, #9aa0b4); background: var(--bg-panel-solid); cursor: pointer; box-shadow: 0 0 16px -4px var(--slot-color, #9aa0b4); }
.altar-slot.filled img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.altar-slot-name, .altar-slot-meta {
  position: relative; z-index: 1; width: 100%; text-align: center; padding: 0 4px;
  background: rgba(0,0,0,0.72); color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.altar-slot-name { font-family: var(--font-display); font-size: 0.72rem; padding-top: 4px; }
.altar-slot-meta { font-size: 0.62rem; color: var(--text-dim); padding-bottom: 5px; }
.altar-slot-remove {
  position: absolute; top: 6px; right: 6px; z-index: 2; width: 20px; height: 20px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: rgba(0,0,0,0.7); color: #fff; font-size: 0.9rem;
}
.altar-odds { text-align: center; margin-top: 14px; font-size: 0.85rem; color: var(--text-dim); }
.altar-warning { text-align: center; margin-top: 8px; font-size: 0.8rem; color: #fb7185; font-weight: 600; }
.altar-actions { display: flex; justify-content: center; gap: 10px; margin-top: 14px; flex-wrap: wrap; }
.altar-copy-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(118px, 1fr)); gap: 10px; }
.altar-copy {
  position: relative; display: flex; flex-direction: column; align-items: stretch; gap: 2px;
  padding: 6px; border-radius: 12px; text-align: left; box-shadow: none;
  background: var(--bg-panel-2); border: 1px solid var(--border-soft); cursor: pointer;
  transition: transform 0.15s ease, border-color 0.15s ease;
}
.altar-copy img { width: 100%; aspect-ratio: 3 / 4; object-fit: cover; border-radius: 8px; }
.altar-copy:hover:not(.disabled) { transform: translateY(-2px); border-color: #ef4444; }
.altar-copy.disabled { opacity: 0.35; cursor: not-allowed; }
.altar-copy-name { font-size: 0.75rem; font-weight: 700; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.altar-copy-meta { font-size: 0.65rem; color: var(--text-dim); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.altar-copy-precious {
  position: absolute; top: 10px; left: 10px; font-size: 0.6rem; font-weight: 800; padding: 1px 6px; border-radius: 999px;
  background: linear-gradient(135deg, #ffe08a, #f5a524); color: #1a1206;
}
.altar-bone-gain { color: #f5d9a8; }
@media (prefers-reduced-motion: reduce) { .altar-slot.empty { animation: none; } }

/* Chenil de la fouille (os + chien qui creuse 2 h). */
.dig-kennel {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  margin-top: 14px; padding: 12px 16px; border-radius: 14px;
  background: linear-gradient(135deg, rgba(245,217,168,0.08), transparent 70%), var(--bg-panel-2);
  border: 1px solid rgba(245,217,168,0.25);
}
.dig-kennel-dog { font-size: 2rem; line-height: 1; display: inline-block; }
.dig-kennel-dog.digging { animation: dogDig 0.9s ease-in-out infinite; }
@keyframes dogDig { 0%, 100% { transform: rotate(-8deg) translateY(0); } 50% { transform: rotate(8deg) translateY(3px); } }
.dig-kennel-body { flex: 1; min-width: 200px; }
.dig-kennel-title { font-family: var(--font-display); font-size: 0.9rem; }
.dig-kennel-bones { margin-left: 6px; font-family: var(--font-body); font-size: 0.75rem; padding: 1px 8px; border-radius: 999px; background: rgba(245,217,168,0.15); color: #f5d9a8; }
.dig-kennel-bones:empty { display: none; }
.dig-kennel-text { font-size: 0.82rem; color: var(--text-dim); margin-top: 2px; }
.dig-kennel-actions { display: flex; gap: 8px; flex-wrap: wrap; }
@media (prefers-reduced-motion: reduce) { .dig-kennel-dog.digging { animation: none; } }

/* Raretes de booster (2026-10-02). Indice subtil : un seul fremissement
   neutre quand le booster contient une epique ou mieux - on sent "quelque
   chose" sans savoir quoi (remplace les tremblements/couleurs par rarete). */
.booster-pack.charging-special { animation: boosterChargeSpecial 0.38s ease-in-out infinite; box-shadow: 0 0 34px rgba(255,255,255,0.28); }
@keyframes boosterChargeSpecial {
  0%, 100% { transform: translate(0, 0) scale(1.04) rotate(0deg); }
  25% { transform: translate(-5px, 0) scale(1.06) rotate(-2.5deg); }
  50% { transform: translate(5px, 0) scale(1.06) rotate(2.5deg); }
  75% { transform: translate(-3px, 0) scale(1.05) rotate(-1.5deg); }
}
/* Booster shiny (0.1%) : pack irise. */
.booster-pack.pack-shiny { box-shadow: 0 0 46px rgba(255,110,199,0.6), 0 0 90px rgba(125,211,252,0.35); }
.booster-pack.pack-shiny::before {
  content: ""; position: absolute; inset: 0; z-index: 1; border-radius: inherit; pointer-events: none;
  background: linear-gradient(120deg, rgba(255,110,199,0.45), rgba(255,215,110,0.4), rgba(125,211,252,0.45), rgba(167,139,250,0.45));
  background-size: 300% 300%;
  animation: shinyPackShift 1.6s linear infinite;
}
@keyframes shinyPackShift { 0% { background-position: 0% 50%; } 100% { background-position: 300% 50%; } }
@media (prefers-reduced-motion: reduce) { .booster-pack.pack-shiny::before, .booster-pack.charging-special { animation: none; } }

/* ===== Coffre-fort perso (coffre.html + tuiles "Protégé" de la collection, 2026-10-02) ===== */
.collection-card.protected-card { text-decoration: none; color: inherit; border-color: #22c55e; box-shadow: 0 0 0 2px #22c55e66, 0 0 18px #22c55e88; animation: protected-glow 2.6s ease-in-out infinite; }
.collection-card.protected-card .card-name { color: #4ade80; }
.collection-card.protected-card .card-art img { filter: saturate(0.85); }
.protected-badge { display: inline-block; padding: 2px 8px; border-radius: 999px; font-size: 0.75rem; font-weight: 700; color: #052e16; background: #4ade80; }
@keyframes protected-glow { 0%, 100% { box-shadow: 0 0 0 2px #22c55e55, 0 0 12px #22c55e66; } 50% { box-shadow: 0 0 0 2px #22c55eaa, 0 0 24px #22c55eaa; } }
@media (prefers-reduced-motion: reduce) { .collection-card.protected-card { animation: none; } }
.pv-locked { text-align: center; padding: 32px 16px; display: grid; gap: 12px; justify-items: center; }
.pv-locked-icon { font-size: 3rem; opacity: 0.7; }
.pv-summary { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; margin-bottom: 18px; }
.pv-reward-note { flex: 1 1 260px; font-size: 0.9rem; opacity: 0.85; }
.pv-rows { display: grid; gap: 14px; }
.pv-row { border: 1px solid rgba(255,255,255,0.08); border-radius: 14px; padding: 12px; background: rgba(0,0,0,0.18); }
.pv-row.complete { border-color: #22c55e; box-shadow: 0 0 16px #22c55e44; }
.pv-row-head { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-bottom: 10px; }
.pv-row-name { font-weight: 800; }
.pv-row.complete .pv-row-name { color: #4ade80; }
.pv-row-progress { margin-left: auto; font-size: 0.85rem; font-variant-numeric: tabular-nums; opacity: 0.85; }
.pv-slots { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 8px; }
@media (max-width: 640px) { .pv-slots { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.pv-slot { position: relative; min-height: 160px; max-width: 100%; border-radius: 10px; overflow: hidden; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px; padding: 6px; text-align: center; border: 2px dashed rgba(255,255,255,0.15); }
.pv-slot.available { border-color: #22c55e88; }
.pv-slot.filled { border: none; padding: 0; justify-content: flex-start; overflow: visible; gap: 4px; aspect-ratio: auto; }
.pv-mini { width: 100%; }
.pv-mini .card-art img { width: 100%; aspect-ratio: 3 / 4; object-fit: cover; display: block; }
.pv-slot-foot { display: flex; flex-direction: column; align-items: center; gap: 4px; }
.pv-slot-label { font-size: 0.72rem; font-weight: 700; }
.pv-slot-missing { opacity: 0.4; }
.pv-store, .pv-withdraw { font-size: 0.7rem; padding: 4px 6px; border-radius: 8px; border: none; cursor: pointer; font-weight: 700; }
.pv-store { background: #22c55e; color: #052e16; }
.pv-withdraw { background: rgba(255,255,255,0.12); color: #fff; opacity: 0; transition: opacity 0.15s; }
.pv-slot.filled:hover .pv-withdraw, .pv-slot.filled:focus-within .pv-withdraw { opacity: 1; }
@media (hover: none) { .pv-withdraw { opacity: 1; } }

/* ===== Devine la carte (jeux.html, 2026-10-04) ===== */
.guess-game { display: grid; grid-template-columns: minmax(0, 260px) minmax(0, 1fr); gap: 20px; align-items: start; }
.guess-frame { position: relative; width: 100%; aspect-ratio: 3 / 4; max-width: 100%; border-radius: 14px; overflow: hidden; border: 2px solid var(--border-soft); background: var(--bg-panel-2); box-shadow: inset 0 0 40px rgba(0,0,0,0.5); }
.guess-frame::after { content: "?"; position: absolute; top: 8px; right: 10px; font: 700 1.4rem/1 "Bungee", sans-serif; color: rgba(255,255,255,0.85); text-shadow: 0 2px 6px rgba(0,0,0,0.6); }
.guess-frame.revealed::after { content: none; }
.guess-img { width: 100%; height: 100%; object-fit: cover; display: block; transition: transform 1.2s cubic-bezier(.2,.8,.2,1); }
.guess-side { display: grid; gap: 14px; }
.guess-streak { display: flex; flex-direction: column; gap: 2px; font-size: 1rem; }
.guess-streak-next { font-size: 0.8rem; opacity: 0.7; }
.guess-choices { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
@media (max-width: 420px) { .guess-choices { grid-template-columns: minmax(0, 1fr); } }
.guess-choice { padding: 12px 10px; border-radius: 12px; border: 2px solid var(--border-soft); background: var(--bg-panel-2); color: var(--text); text-shadow: none; font-family: var(--font-body); font-size: 0.95rem; font-weight: 700; text-transform: none; cursor: pointer; transition: border-color .15s, transform .15s; }
.guess-choice:not(:disabled):hover { border-color: #8b5cf6; transform: translateY(-2px); }
.guess-choice:disabled { cursor: default; opacity: 0.55; }
.guess-choice.right { opacity: 1; border-color: #22c55e; background: rgba(34,197,94,0.15); color: #4ade80; }
.guess-choice.wrong { opacity: 1; border-color: #ef4444; background: rgba(239,68,68,0.12); color: #fca5a5; }
.guess-result { padding: 12px 14px; border-radius: 12px; border: 1px solid var(--border-soft); }
.guess-result.win { border-color: #22c55e88; background: rgba(34,197,94,0.1); }
.guess-result.lose { border-color: #f59e0b66; background: rgba(245,158,11,0.08); }
.guess-next { margin-top: 4px; font-size: 0.8rem; opacity: 0.7; }
@media (max-width: 640px) { .guess-game { grid-template-columns: minmax(0, 1fr); } .guess-frame { max-width: 220px; margin-inline: auto; } }

/* ===== Expedition (jeux.html, 2026-10-04) ===== */
.expe-setup { display: grid; gap: 10px; }
.expe-label { font-size: 0.75rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; opacity: 0.7; }
.expe-pick { display: flex; gap: 14px; align-items: center; flex-wrap: wrap; }
.expe-preview { width: 84px; aspect-ratio: 3 / 4; object-fit: cover; border-radius: 10px; border: 2px solid var(--border-soft); background: var(--bg-panel-2); }
.expe-pick-side { display: grid; gap: 6px; flex: 1 1 220px; min-width: 0; }
.expe-pick-side select { max-width: 100%; background: var(--bg-panel-2); border: 1px solid var(--border-soft); color: var(--text); padding: 10px 12px; border-radius: 10px; font-size: 0.95rem; font-family: var(--font-body); }
.expe-bonus { font-size: 0.85rem; color: #4ade80; }
.expe-durations { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; }
@media (max-width: 640px) { .expe-durations { grid-template-columns: minmax(0, 1fr); } }
.expe-duration { display: grid; justify-content: stretch; justify-items: start; align-content: start; gap: 2px; text-align: left; padding: 12px; font-family: var(--font-body); font-size: 0.95rem; text-transform: none; text-shadow: none; letter-spacing: 0; border-radius: 12px; border: 2px solid var(--border-soft); background: var(--bg-panel-2); color: inherit; cursor: pointer; transition: border-color .15s, transform .15s; }
.expe-duration:hover { border-color: #22d3ee; transform: translateY(-2px); }
.expe-duration-h { font: 700 1.3rem/1.1 "Bungee", sans-serif; }
.expe-duration-name { font-weight: 700; }
.expe-duration-loot { font-size: 0.78rem; opacity: 0.75; }
.expe-active { display: flex; gap: 18px; align-items: center; flex-wrap: wrap; }
.expe-explorer { width: 120px; aspect-ratio: 3 / 4; border-radius: 12px; overflow: hidden; border: 2px solid var(--expe-color); box-shadow: 0 0 18px color-mix(in srgb, var(--expe-color) 50%, transparent); animation: expe-bob 3s ease-in-out infinite; }
.expe-explorer img { width: 100%; height: 100%; object-fit: cover; display: block; }
.expe-active.ready .expe-explorer { animation: expe-ready 1.2s ease-in-out infinite; }
@keyframes expe-bob { 0%, 100% { transform: translateY(0) rotate(-1deg); } 50% { transform: translateY(-6px) rotate(1deg); } }
@keyframes expe-ready { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.05); } }
@media (prefers-reduced-motion: reduce) { .expe-explorer, .expe-active.ready .expe-explorer { animation: none; } }
.expe-info { display: grid; gap: 6px; flex: 1 1 240px; min-width: 0; }
.expe-title { font-weight: 800; font-size: 1.05rem; }
.expe-sub, .expe-countdown { font-size: 0.85rem; opacity: 0.8; font-variant-numeric: tabular-nums; }
.expe-progress { height: 10px; border-radius: 999px; background: rgba(255,255,255,0.08); overflow: hidden; }
.expe-progress-fill { height: 100%; width: 0; background: linear-gradient(90deg, #8b5cf6, #22d3ee); transition: width 1s linear; }
.expe-active.ready .expe-progress-fill { background: linear-gradient(90deg, #22c55e, #4ade80); }
.expe-info .btn { justify-self: start; }
.expe-report { margin-bottom: 16px; padding: 12px 14px; border-radius: 12px; border: 1px solid #22c55e88; background: rgba(34,197,94,0.1); display: grid; gap: 4px; }
.expe-report-title { font-weight: 700; }
.expe-report-bonus { opacity: 0.75; font-size: 0.85rem; }

/* ===== Flair du chien sur le plateau de fouille (2026-10-04) ===== */
.dig-tile.sniffed { box-shadow: 0 0 0 2px #fbbf24, 0 0 16px rgba(251,191,36,0.55); animation: dig-sniff 1.6s ease-in-out infinite; }
.dig-sniff-paw { position: absolute; top: 4px; right: 5px; font-size: 1rem; filter: drop-shadow(0 1px 2px rgba(0,0,0,0.6)); pointer-events: none; }
@keyframes dig-sniff { 0%, 100% { box-shadow: 0 0 0 2px #fbbf24, 0 0 10px rgba(251,191,36,0.4); } 50% { box-shadow: 0 0 0 2px #fbbf24, 0 0 22px rgba(251,191,36,0.75); } }
@media (prefers-reduced-motion: reduce) { .dig-tile.sniffed { animation: none; } }

/* ===== Admin - Base de donnees (admin-db.html, 2026-10-04) ===== */
.adb-login { display: grid; gap: 12px; max-width: 520px; }
.adb-login label, .adb-row label { display: grid; gap: 4px; font-size: 0.85rem; color: var(--text-dim); }
.adb-login input, .adb-row input[type="number"], .adb-row input[type="search"], .adb-row select, .adb-plan-ambiguous select { background: var(--bg-panel-2); border: 1px solid var(--border-soft); color: var(--text); padding: 9px 12px; border-radius: 10px; font-family: var(--font-body); font-size: 0.95rem; max-width: 100%; }
.adb-login .btn { justify-self: start; }
.adb-logout { margin-left: auto; align-self: center; padding: 6px 12px; font-size: 0.8rem; }
.adb-summary { display: flex; flex-wrap: wrap; gap: 12px; margin-bottom: 16px; }
.adb-summary .adb-warn .stat-value { color: #fbbf24; }
.adb-card { border: 1px solid var(--border-soft); border-radius: 14px; padding: 16px; margin-bottom: 16px; background: rgba(0,0,0,0.15); display: grid; gap: 12px; }
.adb-card h2 { margin: 0; font-size: 1.05rem; }
.adb-help { font-size: 0.82rem; color: var(--text-dim); margin: 0; }
.adb-row { display: flex; flex-wrap: wrap; gap: 10px 16px; align-items: end; }
.adb-row-between { justify-content: space-between; align-items: center; }
.adb-check { display: flex !important; align-items: center; gap: 6px !important; }
.adb-drop { display: grid; place-items: center; min-height: 110px; border: 2px dashed var(--border-soft); border-radius: 14px; cursor: pointer; text-align: center; padding: 16px; transition: border-color .15s, background .15s; }
.adb-drop input { display: none; }
.adb-drop.over, .adb-drop:hover { border-color: #22d3ee; background: rgba(34,211,238,0.06); }
.adb-table-wrap { overflow-x: auto; max-width: 100%; border: 1px solid var(--border-soft); border-radius: 10px; }
.adb-table { width: 100%; border-collapse: collapse; font-size: 0.82rem; }
.adb-table th, .adb-table td { padding: 6px 8px; border-bottom: 1px solid var(--border-soft); text-align: left; vertical-align: middle; white-space: nowrap; }
.adb-table th { position: sticky; top: 0; background: var(--bg-panel-2); font-weight: 700; z-index: 1; }
.adb-col-type { display: block; font-size: 0.68rem; font-weight: 400; color: var(--text-dim); }
.adb-table td.adb-cell { cursor: text; max-width: 280px; overflow: hidden; text-overflow: ellipsis; }
.adb-table td.adb-cell:hover { background: rgba(139,92,246,0.08); }
.adb-table td.adb-saved { background: rgba(34,197,94,0.18); transition: background .8s; }
.adb-id { color: var(--text-dim); font-variant-numeric: tabular-nums; }
.adb-null { color: var(--text-dim); }
.adb-cell-input { width: 100%; min-width: 120px; background: var(--bg-panel-2); color: var(--text); border: 1px solid #8b5cf6; border-radius: 6px; padding: 4px 6px; font: inherit; }
.adb-cell-img { width: 34px; height: 46px; object-fit: cover; border-radius: 4px; vertical-align: middle; margin-right: 4px; background: var(--bg-panel-2); }
.adb-cell-upload { cursor: pointer; padding: 2px 4px; }
.adb-del { background: none; border: none; box-shadow: none; padding: 2px 6px; cursor: pointer; font-size: 1rem; text-shadow: none; }
.adb-del::before { content: none; }
.adb-plan-ok td:first-child { color: #4ade80; }
.adb-plan-none td:first-child { color: #fca5a5; }
.adb-tag { display: inline-block; padding: 1px 6px; border-radius: 999px; font-size: 0.7rem; background: rgba(139,92,246,0.2); color: #c4b5fd; }
.adb-tag-warn { background: rgba(239,68,68,0.15); color: #fca5a5; }
/* Structure des tables (colonnes, types, renommage, suppression) */
.adb-card[hidden] { display: none; }
#adb-zone > .tabs { flex-wrap: wrap; }
.adb-table th.adb-col-head { cursor: pointer; }
.adb-table th.adb-col-head:hover { color: #c4b5fd; }
.adb-schema-table td { white-space: normal; }
.adb-schema-table input[type="text"], .adb-schema-table select, .adb-danger-zone input { background: var(--bg-panel-2); border: 1px solid var(--border-soft); color: var(--text); padding: 6px 10px; border-radius: 8px; font-family: var(--font-body); font-size: 0.85rem; max-width: 100%; }
.adb-schema-table input[type="text"] { width: 14rem; font-family: ui-monospace, SFMono-Regular, Consolas, monospace; }
.adb-type-cell { display: flex; flex-wrap: wrap; gap: 6px; }
.adb-schema-table tr.adb-dirty td { background: rgba(245,165,36,0.08); }
.adb-schema-table tfoot td { border-top: 2px solid var(--border-soft); border-bottom: none; }
.adb-schema-actions { display: flex; gap: 6px; align-items: center; justify-content: flex-end; }
.adb-mini { padding: 4px 10px; font-size: 0.78rem; }
.adb-tag-ok { background: rgba(34,197,94,0.15); color: #86efac; }
.adb-usage { margin-top: 10px; text-align: left; line-height: 1.7; }
.adb-danger-zone { border-top: 1px solid var(--border-soft); padding-top: 10px; display: grid; gap: 10px; }
.adb-danger-zone summary { cursor: pointer; color: #fca5a5; font-weight: 600; font-size: 0.9rem; }
.adb-danger-zone[open] summary { margin-bottom: 8px; }
.adb-danger-zone label { display: grid; gap: 4px; font-size: 0.85rem; color: var(--text-dim); }
#adb-new-table .adb-row input { background: var(--bg-panel-2); border: 1px solid var(--border-soft); color: var(--text); padding: 9px 12px; border-radius: 10px; font-family: var(--font-body); font-size: 0.95rem; }
@media (max-width: 640px) { .adb-schema-table input[type="text"] { width: 9rem; } }
.adb-img-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 10px; }
.adb-img { display: flex; gap: 10px; padding: 8px; border: 1px solid var(--border-soft); border-radius: 10px; background: var(--bg-panel-2); min-width: 0; }
.adb-img.missing { border-color: rgba(251,191,36,0.5); }
.adb-img-thumb { flex: 0 0 60px; height: 80px; border-radius: 6px; overflow: hidden; display: grid; place-items: center; background: rgba(0,0,0,0.3); font-size: 0.65rem; color: #fbbf24; text-align: center; }
.adb-img-thumb img { width: 100%; height: 100%; object-fit: cover; }
.adb-img-meta { display: grid; gap: 2px; min-width: 0; font-size: 0.78rem; align-content: start; }
.adb-img-name { font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.adb-img-size, .adb-img-usage { color: var(--text-dim); overflow: hidden; }
.adb-replace { justify-self: start; padding: 4px 10px; font-size: 0.75rem; margin-top: 4px; cursor: pointer; }

/* ========================================================================
   Page collection fusionnee (Classeur + Atelier, 2026-10-04)
   collection-core.js / collection-binder.js / collection-workshop.js
   ======================================================================== */
.coll-summary {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 14px 20px;
  margin-bottom: 8px;
}
.coll-summary-progress { flex: 1 1 280px; min-width: 0; }
.coll-summary-line { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px; }
.coll-summary-line #progress-label { font-weight: 700; font-variant-numeric: tabular-nums; }
.coll-stats-toggle { padding: 4px 10px; font-size: 0.78rem; }
/* Solde de poussieres : meme habillage que l'ancien .stardust-hero, en compact. */
.coll-dust {
  display: flex;
  align-items: center;
  gap: 10px;
  background: linear-gradient(135deg, rgba(244, 114, 182, 0.18), rgba(139, 92, 246, 0.14));
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 10px 16px;
  font-family: var(--font-display);
  font-weight: 700;
}
.coll-dust .sd-icon { font-size: 1.4rem; }
.coll-dust .sd-count { font-size: 1.25rem; font-variant-numeric: tabular-nums; }
.coll-dust .sd-label { color: var(--text-dim); font-weight: 500; font-size: 0.75rem; font-family: var(--font-body); }

/* Lignes de rarete cliquables (filtrent la grille). */
button.coll-rarity-row {
  width: 100%;
  background: none;
  border: 1px solid transparent;
  box-shadow: none;
  text-shadow: none;
  color: inherit;
  padding: 3px 6px;
  margin: 0 -6px;
  border-radius: 8px;
  cursor: pointer;
  text-align: left;
  font-family: var(--font-body);
  transform: none !important;
}
button.coll-rarity-row::before { content: none; }
button.coll-rarity-row:hover { background: rgba(255, 255, 255, 0.04); }
button.coll-rarity-row.active { border-color: var(--border); background: rgba(139, 92, 246, 0.1); }

#unused-dust-reminder { justify-content: space-between; flex-wrap: wrap; gap: 10px; }
.coll-reminder-btn { padding: 6px 12px; font-size: 0.8rem; }

.coll-modes { margin: 18px 0 12px; }
.coll-mode .tab-count { vertical-align: 1px; }
.coll-mode.active .tab-count { background: var(--accent); color: #fff; }

.coll-pane-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 10px;
  margin-bottom: 12px;
}
.coll-pane-actions .coll-pane-help { flex: 1 1 260px; }
.coll-pane-help { margin: 0 0 12px; font-size: 0.85rem; color: var(--text-dim); line-height: 1.5; }
.coll-pane-actions .coll-pane-help { margin: 0; }

/* Rechargement silencieux apres une action : on garde la grille visible. */
#collection-zone.coll-busy .coll-pane { opacity: 0.6; pointer-events: none; transition: opacity 0.15s; }

/* Mise en evidence via ?cardId=... (recherche globale). */
.collection-card.highlighted {
  outline: 3px solid var(--accent-2);
  outline-offset: 2px;
  box-shadow: 0 0 24px rgba(34, 211, 238, 0.45);
}

/* Atelier : carte encore jamais obtenue (craft), dernier exemplaire (decraft). */
.craft-card { position: relative; }
.craft-card.coll-missing .owned-count strong { color: var(--accent-2); }
.craft-card .coll-last-copies { color: var(--accent-3); font-weight: 600; }
.coll-wish-flag {
  position: absolute;
  top: 6px;
  right: 6px;
  z-index: 2;
  width: 24px;
  height: 24px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: rgba(6, 7, 15, 0.75);
  color: #facc15;
  font-size: 0.8rem;
}

@media (max-width: 640px) {
  .coll-dust { flex: 1 1 100%; }
  .coll-pane-actions > button { flex: 1 1 auto; }
}

/* ========================================================================
   Fonctionnalites du 2026-10-04 (api/src/native) : evenement, notifications,
   serie de connexion, sets complets, album, correspondances d'echange,
   classement du boss, economie, coffres, revelation en grille.
   ======================================================================== */
/* Bandeau d'evenement */
.beta-banner.banner-event {
  background: linear-gradient(90deg, color-mix(in srgb, var(--accent-3) 22%, transparent), color-mix(in srgb, var(--rarity-legendaire) 18%, transparent));
  border-bottom: 1px solid color-mix(in srgb, var(--rarity-legendaire) 40%, transparent);
  color: var(--text);
}

/* Notifications push (menu des notifications) */
.push-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 10px 12px; border-top: 1px solid var(--border-soft); font-size: 0.8rem; }
.push-row small { display: block; color: var(--text-dim); font-size: 0.7rem; margin-top: 2px; }
.push-row .push-toggle-btn { padding: 5px 10px; font-size: 0.75rem; flex-shrink: 0; }
.push-row-muted { color: var(--text-dim); display: block; }

/* Serie de connexion (accueil) */
.streak-days { list-style: none; padding: 0; margin: 12px 0; display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 6px; }
.streak-day { display: flex; flex-direction: column; align-items: center; gap: 2px; padding: 8px 2px; border-radius: 10px; background: var(--bg-panel-2); border: 1px solid var(--border-soft); font-size: 0.68rem; text-align: center; min-width: 0; }
.streak-day-num { font-family: var(--font-display); color: var(--text-dim); }
.streak-day-icon { font-size: 1.1rem; }
.streak-day-reward { color: var(--text-dim); line-height: 1.2; overflow-wrap: anywhere; }
.streak-day.streak-claimed { opacity: 0.55; }
.streak-day.streak-claimed .streak-day-icon { color: var(--success); }
.streak-day.streak-today { border-color: var(--accent-3); box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent-3) 35%, transparent); }
.streak-day.streak-today .streak-day-reward { color: var(--text); font-weight: 700; }
.streak-day.streak-big { background: linear-gradient(160deg, color-mix(in srgb, var(--rarity-legendaire) 18%, var(--bg-panel-2)), var(--bg-panel-2)); }
@media (max-width: 480px) { .streak-day-reward { font-size: 0.6rem; } }

/* Sets complets (collection) */
.set-reward-banner { display: grid; gap: 8px; margin: 12px 0; }
.set-reward-row { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 10px; padding: 12px 16px; border-radius: 12px; background: linear-gradient(120deg, color-mix(in srgb, var(--rarity-legendaire) 20%, var(--bg-panel-2)), var(--bg-panel-2)); border: 1px solid color-mix(in srgb, var(--rarity-legendaire) 45%, transparent); font-size: 0.88rem; }
.set-chip { display: inline-flex; align-items: center; gap: 4px; padding: 2px 10px; border-radius: 999px; font-size: 0.72rem; font-family: var(--font-body); font-weight: 700; margin-left: 8px; }
.set-chip-done { background: color-mix(in srgb, var(--rarity-legendaire) 18%, transparent); color: var(--rarity-legendaire); }
button.set-chip-claim { background: linear-gradient(135deg, var(--rarity-legendaire), #ffd166); color: #1a1200; border: none; box-shadow: none; cursor: pointer; text-shadow: none; }
button.set-chip-claim::before { content: none; }
.coll-value-trend { display: block; font-size: 0.68rem; margin-top: 2px; }
.coll-value-trend.up { color: var(--success); }
.coll-value-trend.down { color: var(--danger); }

/* Album par extension (classeur) */
.album { display: grid; gap: 14px; }
.album-head { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; }
.album-head select { background: var(--bg-panel-2); border: 1px solid var(--border-soft); color: var(--text); padding: 8px 12px; border-radius: 10px; font-family: var(--font-body); }
.album-progress { color: var(--text-dim); font-size: 0.85rem; font-variant-numeric: tabular-nums; }
.album-spread { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 340px), 1fr)); gap: 18px; }
.album-page { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; padding: 14px; border-radius: 14px; background: linear-gradient(160deg, rgba(255,255,255,0.04), rgba(0,0,0,0.25)); border: 1px solid var(--border-soft); box-shadow: inset 0 0 0 6px rgba(0,0,0,0.12); }
.album-slot { position: relative; aspect-ratio: 3 / 4; border-radius: 10px; overflow: hidden; min-width: 0; }
.album-empty { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px; border: 2px dashed color-mix(in srgb, var(--slot-color) 45%, transparent); background: color-mix(in srgb, var(--slot-color) 6%, transparent); color: var(--text-dim); }
.album-empty .album-q { font-family: var(--font-display); font-size: 1.4rem; opacity: 0.5; }
.album-empty .album-num { position: static; background: none; }
.album-owned { cursor: pointer; border: 2px solid color-mix(in srgb, var(--slot-color) 70%, transparent); box-shadow: 0 6px 18px -10px var(--slot-color); transition: transform 0.15s ease; }
.album-owned:hover, .album-owned:focus-visible { transform: translateY(-2px); outline: none; }
.album-owned img { width: 100%; height: 100%; object-fit: cover; display: block; }
.album-num { position: absolute; top: 4px; left: 4px; font-family: var(--font-display); font-size: 0.62rem; padding: 1px 6px; border-radius: 999px; background: rgba(6,7,15,0.75); color: var(--text); }
.album-name { position: absolute; left: 0; right: 0; bottom: 0; padding: 14px 6px 4px; font-size: 0.66rem; font-weight: 700; background: linear-gradient(transparent, rgba(6,7,15,0.9)); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.album-count { position: absolute; top: 4px; right: 4px; font-size: 0.62rem; font-weight: 800; padding: 1px 6px; border-radius: 999px; background: rgba(6,7,15,0.75); }
.album-owned .finish-indicator { top: auto !important; bottom: 22px; left: 4px; }
.album-vault { border-color: var(--success); }
.album-nav { display: flex; align-items: center; justify-content: center; gap: 14px; font-variant-numeric: tabular-nums; }
.album-nav button { padding: 6px 14px; }

/* Correspondances d'echange */
.match-row { display: grid; gap: 8px; padding: 12px 14px; border-radius: 12px; background: var(--bg-panel); border: 1px solid var(--border-soft); }
.match-row.match-mutual { border-color: color-mix(in srgb, var(--success) 50%, transparent); }
.match-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.match-tag { font-size: 0.72rem; padding: 2px 8px; border-radius: 999px; background: color-mix(in srgb, var(--success) 15%, transparent); color: var(--success); }
.match-line { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; font-size: 0.82rem; color: var(--text-dim); }
.match-card { display: inline-flex; align-items: center; gap: 6px; padding: 2px 8px 2px 2px; border-radius: 999px; background: var(--bg-panel-2); border: 1px solid color-mix(in srgb, var(--chip-color) 55%, transparent); color: var(--text); font-size: 0.78rem; }
.match-card img { width: 22px; height: 29px; object-fit: cover; border-radius: 4px; }
.match-actions { display: flex; gap: 8px; flex-wrap: wrap; }

/* Classement du boss */
.boss-leaderboard { margin-top: 24px; padding: 16px; border-radius: 14px; background: var(--bg-panel-2); border: 1px solid var(--border-soft); }
.boss-leaderboard h3 { margin: 0 0 8px; font-size: 1rem; }
.boss-finisher, .boss-me { font-size: 0.82rem; color: var(--text-dim); margin: 6px 0; }
.boss-ranking { list-style: none; padding: 0; margin: 8px 0 0; display: grid; gap: 4px; }
.boss-ranking li { display: grid; grid-template-columns: 34px 1fr auto auto; gap: 10px; align-items: center; padding: 6px 10px; border-radius: 8px; font-size: 0.85rem; font-variant-numeric: tabular-nums; }
.boss-ranking li:nth-child(odd) { background: rgba(255,255,255,0.03); }
.boss-ranking li.me { background: color-mix(in srgb, var(--accent) 18%, transparent); }
.boss-rank { text-align: center; font-weight: 800; }
.boss-dmg { font-weight: 700; }
.boss-hits { color: var(--text-dim); font-size: 0.75rem; }
@media (max-width: 480px) { .boss-ranking li { grid-template-columns: 28px 1fr auto; } .boss-hits { display: none; } }

/* Economie (admin) */
.econ-charts { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr)); gap: 16px; margin: 18px 0; }
.econ-chart { padding: 12px; border-radius: 12px; background: var(--bg-panel-2); border: 1px solid var(--border-soft); }
.econ-chart-title { font-size: 0.82rem; font-weight: 700; margin-bottom: 8px; }
.econ-chart-title span { color: var(--text-dim); font-weight: 400; }
.econ-bars { display: flex; align-items: flex-end; gap: 2px; height: 90px; }
.econ-bar { flex: 1; min-height: 2px; border-radius: 2px 2px 0 0; background: linear-gradient(var(--accent-2), var(--accent)); }
.econ-axis { display: flex; justify-content: space-between; font-size: 0.68rem; color: var(--text-dim); margin-top: 4px; }
.econ-columns { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr)); gap: 16px; }
.econ-columns h3 { font-size: 0.9rem; margin: 0 0 6px; }
.econ-list { list-style: none; padding: 0; margin: 0; display: grid; gap: 3px; font-size: 0.82rem; }
.econ-list li { display: flex; justify-content: space-between; gap: 8px; font-variant-numeric: tabular-nums; }
.econ-list small { color: var(--text-dim); }

/* Coffres (page d'ouverture) */
.chest-panel { display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: 18px; margin-top: 28px; padding: 18px 20px; border-radius: 16px; background: linear-gradient(135deg, color-mix(in srgb, var(--rarity-legendaire) 14%, var(--bg-panel-2)), var(--bg-panel-2)); border: 1px solid color-mix(in srgb, var(--rarity-legendaire) 35%, transparent); }
.chest-visual { font-size: 3rem; filter: drop-shadow(0 6px 14px rgba(245,165,36,0.35)); }
.chest-body h2 { margin: 0 0 4px; font-size: 1.15rem; }
.chest-desc, .chest-next { margin: 0; font-size: 0.82rem; color: var(--text-dim); }
.chest-stock { display: flex; gap: 8px; flex-wrap: wrap; margin: 8px 0; }
.chest-chip { padding: 3px 10px; border-radius: 999px; background: rgba(6,7,15,0.45); border: 1px solid var(--border-soft); font-weight: 700; font-size: 0.85rem; font-variant-numeric: tabular-nums; }
.chest-actions { display: grid; gap: 8px; }
@media (max-width: 640px) { .chest-panel { grid-template-columns: 1fr; text-align: center; } .chest-stock { justify-content: center; } }
.booster-pack.chest-pack { background: linear-gradient(160deg, #7c4a1e, #3b2410); border-color: #f5a524; }
.booster-pack.chest-pack .chest-emoji { font-size: 4rem; }

/* Etiquettes pendant la revelation (shiny, bonus, coffre) */
.pack-tags { display: flex; justify-content: center; flex-wrap: wrap; gap: 8px; }
.pack-tag { padding: 4px 12px; border-radius: 999px; font-size: 0.8rem; font-weight: 800; animation: modalPopIn 0.35s cubic-bezier(.2,.9,.3,1.3) both; }
.pack-tag-shiny { background: linear-gradient(90deg, #ff6ec7, #ffd76e, #7dd3fc, #a78bfa); color: #1a0f22; }
.pack-tag-bonus { background: color-mix(in srgb, var(--accent-2) 22%, transparent); color: var(--accent-2); border: 1px solid var(--accent-2); }
.pack-tag-dust { background: color-mix(in srgb, var(--accent-3) 20%, transparent); color: var(--accent-3); border: 1px solid var(--accent-3); }
.pack-tag-chest { background: color-mix(in srgb, var(--rarity-legendaire) 22%, transparent); color: var(--rarity-legendaire); border: 1px solid var(--rarity-legendaire); }
.bonus-card-ribbon { position: absolute; top: 34px; right: -2px; z-index: 3; padding: 2px 8px; border-radius: 6px 0 0 6px; background: var(--accent-2); color: #031018; font-size: 0.6rem; font-weight: 800; }
.loot-art { display: flex; align-items: center; justify-content: center; background: radial-gradient(circle, color-mix(in srgb, var(--accent-3) 30%, transparent), transparent 70%); }
.loot-booster .loot-art { background: radial-gradient(circle, color-mix(in srgb, var(--accent-2) 30%, transparent), transparent 70%); }
.loot-icon { font-size: 3.4rem; }

/* Face avant d'une carte revelee : exactement la taille de la carte.
   .card-front { position: relative } la laissait grandir avec son contenu
   (image + nom), environ 23 % plus haute que la carte : en grille ("Tout
   reveler"), chaque carte chevauchait celle du dessous. */
.reveal-grid .card-front { position: absolute; inset: 0; }
/* Une fois les cartes posees, le booster (deja masque) libere sa place :
   sinon ses 300 px repoussaient "Tout reveler" sous le bord de l'ecran. */
.pack-modal-stage:has(#reveal-grid .card) .booster-stage { display: none; }
.reveal-grid .card-front .card-info { flex: 1; min-height: 0; overflow: hidden; }

/* Badges d'une carte revelee : "Nouvelle !" / doublon en haut a gauche,
   finition et etat dans les coins du bas de l'illustration. Le bandeau
   "1ere obtention du serveur" prend toute la largeur : le #001 passe dessous. */
.reveal-grid .card-front > .first-ever-badge ~ .card-art .serial-one-badge { top: 40px; }

/* "Tout reveler" : grille compacte et lisible, meilleures cartes d'abord */
.pack-modal-stage.reveal-row .booster-stage,
.pack-modal-stage.reveal-row .quantity-picker { display: none !important; }
.reveal-grid.row-reveal { grid-template-columns: repeat(auto-fill, minmax(118px, 1fr)); gap: 12px; }
.reveal-grid.row-reveal .card.revealed { cursor: zoom-in; }
.reveal-grid.row-reveal .card-artist { display: none; }
.reveal-grid.row-reveal .card-name { font-size: 0.68rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.reveal-grid.row-reveal .rarity-badge { font-size: 0.56rem; padding: 1px 6px; }
.reveal-grid.row-reveal .card-info { padding: 4px 6px 6px; }
.reveal-grid.row-reveal .quality-indicator:not([data-quality="mint"]) { display: none; }
.reveal-grid.row-reveal .finish-indicator, .reveal-grid.row-reveal .quality-indicator { font-size: 0.52rem; }
.reveal-grid.row-reveal .new-badge.first-ever-badge { font-size: 0; left: 6px; right: auto; padding: 2px 6px; }
.reveal-grid.row-reveal .new-badge.first-ever-badge::after { content: "\1F3C6  1\E8 re"; font-size: 0.56rem; }
.reveal-grid.row-reveal .new-badge, .reveal-grid.row-reveal .dupe-badge { font-size: 0.56rem; padding: 1px 6px; }
.reveal-grid.row-reveal .card-front > .dupe-badge, .reveal-grid.row-reveal .card-front > .new-badge:not(.first-ever-badge) { left: auto; right: 6px; }
.reveal-grid.row-reveal .serial-one-badge { font-size: 0.56rem; padding: 1px 6px; top: 4px; }
.reveal-grid.row-reveal .bonus-card-ribbon { top: 22px; font-size: 0.5rem; }
.reveal-grid.row-reveal .card-aura-ring { display: none; }
.reveal-grid.row-reveal .card.hero-zoom .card-inner { animation: none; }
.reveal-grid.row-reveal .loot-icon { font-size: 2.2rem; }
@media (max-width: 480px) { .reveal-grid.row-reveal { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; } }

/* Resume du butin */
.reveal-summary { width: 100%; padding: 12px 14px; border-radius: 14px; background: rgba(6,7,15,0.55); border: 1px solid var(--border-soft); display: grid; gap: 8px; }
.reveal-summary[hidden] { display: none; }
.summary-head { font-size: 0.9rem; }
.summary-new { color: var(--accent-3); font-weight: 700; }
.summary-pills { display: flex; flex-wrap: wrap; gap: 6px; }
.summary-pill { padding: 2px 10px; border-radius: 999px; font-size: 0.78rem; font-weight: 700; background: color-mix(in srgb, var(--pill-color) 16%, transparent); border: 1px solid color-mix(in srgb, var(--pill-color) 60%, transparent); }
.summary-notable { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 210px), 1fr)); gap: 4px 12px; font-size: 0.8rem; }
.summary-notable li { border-left: 3px solid var(--pill-color); padding-left: 8px; }
.summary-notable span { color: var(--text-dim); }
.summary-hint { margin: 0; font-size: 0.72rem; color: var(--text-dim); }

/* Loupe sur une carte de la grille */
.reveal-zoom-overlay { position: fixed; inset: 0; z-index: 450; display: flex; align-items: center; justify-content: center; background: rgba(4,5,12,0.82); cursor: zoom-out; }
.reveal-zoom-card { width: min(300px, 80vw); opacity: 1; transform: none; animation: modalPopIn 0.25s cubic-bezier(.2,.9,.3,1.3) both; }
.reveal-zoom-card .card-inner { transform: none !important; }
.reveal-zoom-card .card-front { transform: none; position: absolute; inset: 0; }

/* Boss : attaque en salve (communaute.js, api/src/native/boss.js) */
.boss-rules { display: flex; flex-wrap: wrap; gap: 6px; margin: 4px 0 14px; }
.boss-rule { font-size: 0.74rem; padding: 3px 10px; border-radius: 999px; background: var(--bg-panel-2); border: 1px solid var(--border-soft); color: var(--text-dim); }
.boss-rule strong { color: var(--text); margin-right: 4px; }
.boss-attack-layout { display: grid; grid-template-columns: minmax(0, 1fr) 300px; gap: 18px; align-items: start; }
.boss-tray { position: sticky; top: calc(var(--header-h, 64px) + 12px); padding: 16px; border-radius: 14px; background: var(--bg-panel-2); border: 1px solid color-mix(in srgb, var(--danger) 35%, var(--border-soft)); display: grid; gap: 10px; }
.boss-tray h3 { margin: 0; font-size: 1rem; }
.boss-tray-empty { margin: 0; font-size: 0.82rem; color: var(--text-dim); }
.boss-tray-lines { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; max-height: 280px; overflow-y: auto; }
.boss-tray-lines li { display: flex; justify-content: space-between; gap: 8px; font-size: 0.82rem; }
.boss-tray-lines small { display: block; color: var(--text-dim); font-size: 0.7rem; }
.boss-tray-dmg { font-weight: 800; font-variant-numeric: tabular-nums; white-space: nowrap; }
.boss-tray-row { display: flex; justify-content: space-between; font-size: 0.85rem; font-variant-numeric: tabular-nums; }
.boss-tray-total { font-size: 1.05rem; padding-top: 6px; border-top: 1px solid var(--border-soft); }
.boss-tray-total strong { color: var(--danger); }
.boss-tray-hp { display: grid; gap: 4px; }
.boss-tray-hp small { font-size: 0.74rem; color: var(--text-dim); }
.boss-tray-hp-bar { position: relative; display: flex; height: 10px; border-radius: 999px; background: rgba(0,0,0,0.35); overflow: hidden; }
.boss-tray-hp-after { background: linear-gradient(90deg, #ef4444, #f97316); }
.boss-tray-hp-hit { background: repeating-linear-gradient(45deg, rgba(255,255,255,0.55) 0 4px, rgba(239,68,68,0.6) 4px 8px); animation: navDotPop 0.3s ease; }
.boss-tray-actions { display: flex; gap: 8px; justify-content: flex-end; }
.boss-warn { font-size: 0.72rem; color: var(--rarity-legendaire); }
.boss-dmg-chip { font-size: 0.74rem; font-weight: 800; color: var(--danger); margin: 2px 0 6px; }
.boss-variant.selected { outline: 2px solid var(--danger); outline-offset: 2px; }
.boss-variant .card-art { position: relative; }
@media (max-width: 860px) {
  .boss-attack-layout { grid-template-columns: 1fr; }
  .boss-tray { position: sticky; bottom: 70px; top: auto; z-index: 3; box-shadow: 0 -10px 30px -12px rgba(0,0,0,0.8); }
  .boss-tray-lines { max-height: 120px; }
}

/* Saison mensuelle (accueil) */
.season-panel { background: linear-gradient(150deg, color-mix(in srgb, var(--accent) 16%, var(--bg-panel)), var(--bg-panel)); }
.season-head { display: flex; align-items: baseline; justify-content: space-between; flex-wrap: wrap; gap: 6px; }
.season-head h2 { margin: 0; text-transform: none; }
.season-ends { font-size: 0.8rem; color: var(--text-dim); font-variant-numeric: tabular-nums; }
.season-progress { height: 10px; border-radius: 999px; background: rgba(0,0,0,0.35); overflow: hidden; margin: 10px 0 4px; }
.season-progress-fill { height: 100%; border-radius: 999px; background: linear-gradient(90deg, var(--accent), var(--accent-2)); transition: width 0.6s ease; }
.season-xp { font-size: 0.8rem; color: var(--text-dim); margin-bottom: 10px; }
.season-tiers { list-style: none; padding: 0; margin: 0 0 12px; display: grid; grid-auto-flow: column; grid-auto-columns: minmax(74px, 1fr); gap: 6px; overflow-x: auto; scrollbar-width: thin; padding-bottom: 4px; }
.season-tier { display: flex; flex-direction: column; align-items: center; gap: 3px; padding: 8px 4px; border-radius: 10px; background: var(--bg-panel-2); border: 1px solid var(--border-soft); font-size: 0.66rem; text-align: center; opacity: 0.6; min-width: 0; }
.season-tier.reached { opacity: 1; border-color: var(--accent-2); box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent-2) 30%, transparent); }
.season-tier.claimed { opacity: 0.85; }
.season-tier.claimed .season-tier-icon { color: var(--success); }
.season-tier-num { font-family: var(--font-display); color: var(--text-dim); }
.season-tier-icon { font-size: 1.1rem; }
.season-tier-reward { color: var(--text-dim); line-height: 1.2; overflow-wrap: anywhere; }
.season-tier-card { background: linear-gradient(160deg, color-mix(in srgb, var(--rarity-legendaire) 22%, var(--bg-panel-2)), var(--bg-panel-2)); }
.season-card-img { width: 40px; height: 53px; object-fit: cover; border-radius: 5px; box-shadow: 0 0 10px color-mix(in srgb, var(--rarity-legendaire) 60%, transparent); }

/* Peche (jeux) */
.fish-scene { position: relative; height: 220px; border-radius: 16px; overflow: hidden; border: 1px solid var(--border-soft); background: #0b1a33; margin: 12px 0; }
.fish-sky { position: absolute; inset: 0 0 45% 0; background: linear-gradient(#1b2a52, #284a7a); }
.fish-water { position: absolute; inset: 55% 0 0 0; background: linear-gradient(#1d5f8f, #0a2a4a); }
.fish-wave { position: absolute; left: -10%; width: 120%; height: 10px; border-radius: 50%; background: rgba(255,255,255,0.12); animation: fishWave 4s ease-in-out infinite; }
.fish-wave.w1 { top: 6px; } .fish-wave.w2 { top: 30px; animation-delay: -1.3s; opacity: 0.7; } .fish-wave.w3 { top: 58px; animation-delay: -2.6s; opacity: 0.5; }
@keyframes fishWave { 0%, 100% { transform: translateX(-3%); } 50% { transform: translateX(3%); } }
.fish-line { position: absolute; left: 50%; top: 0; width: 2px; height: 0; background: rgba(255,255,255,0.7); transform-origin: top; transition: height 0.6s ease; }
.fish-bobber { position: absolute; bottom: -10px; left: -7px; width: 16px; height: 16px; border-radius: 50%; background: linear-gradient(#ef4444 50%, #f5f5f5 50%); opacity: 0; }
.fish-scene.casting .fish-line, .fish-scene.waiting .fish-line, .fish-scene.bite .fish-line { height: 58%; }
.fish-scene.casting .fish-bobber, .fish-scene.waiting .fish-bobber, .fish-scene.bite .fish-bobber { opacity: 1; }
.fish-scene.waiting .fish-bobber { animation: fishBob 1.4s ease-in-out infinite; }
.fish-scene.bite .fish-bobber { animation: fishBite 0.18s linear infinite; }
@keyframes fishBob { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(4px); } }
@keyframes fishBite { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(12px); } }
.fish-status { position: absolute; left: 0; right: 0; top: 14px; text-align: center; font-family: var(--font-display); font-size: 1rem; color: #fff; text-shadow: 0 2px 6px rgba(0,0,0,0.6); }
.fish-catch { position: absolute; inset: 40px 4% 10px; display: flex; flex-wrap: wrap; justify-content: center; align-content: center; gap: 6px; pointer-events: none; }
.fish-catch-item { animation: fishPop 0.35s cubic-bezier(.2,.9,.3,1.3) both; }
@keyframes fishPop { from { opacity: 0; transform: scale(0.6); } to { opacity: 1; transform: scale(1); } }
.fish-catch[hidden] { display: none; }
.fish-catch-item { display: inline-flex; align-items: center; gap: 6px; padding: 6px 12px; border-radius: 999px; background: rgba(6,7,15,0.82); border: 1px solid var(--tier-color, var(--border-soft)); font-weight: 800; font-size: 0.85rem; color: #fff; }
.fish-catch-icon { font-size: 1.3rem; }
.tier-nothing { --tier-color: #6b7280; } .tier-commune { --tier-color: var(--rarity-commune); } .tier-rare { --tier-color: var(--rarity-rare); }
.tier-epique { --tier-color: var(--rarity-epique); } .tier-legendaire { --tier-color: var(--rarity-legendaire); } .tier-mythique { --tier-color: var(--rarity-mythique); } .tier-unique { --tier-color: var(--rarity-unique); }
.fish-catch-item.tier-legendaire, .fish-catch-item.tier-mythique { box-shadow: 0 0 18px var(--tier-color); }
.fish-controls { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; }
.fish-meta { font-size: 0.82rem; color: var(--text-dim); }
.fish-log { display: flex; flex-wrap: wrap; gap: 6px; margin: 12px 0; }
.fish-log-item { font-size: 0.74rem; padding: 2px 8px; border-radius: 999px; background: var(--bg-panel-2); border: 1px solid var(--tier-color, var(--border-soft)); }
.fish-table-wrap summary { cursor: pointer; font-size: 0.85rem; color: var(--text-dim); }
.fish-table { width: 100%; border-collapse: collapse; font-size: 0.82rem; margin-top: 8px; font-variant-numeric: tabular-nums; }
.fish-table th, .fish-table td { text-align: left; padding: 5px 8px; border-bottom: 1px solid var(--border-soft); }
.fish-table tr td:first-child { border-left: 3px solid var(--tier-color, transparent); }
.fish-row-nothing { --tier-color: #6b7280; } .fish-row-commune { --tier-color: var(--rarity-commune); } .fish-row-rare { --tier-color: var(--rarity-rare); }
.fish-row-epique { --tier-color: var(--rarity-epique); } .fish-row-legendaire { --tier-color: var(--rarity-legendaire); } .fish-row-mythique { --tier-color: var(--rarity-mythique); }

/* Reglages avances (admin) */
.settings-group { border-top: 1px solid var(--border-soft); padding-top: 12px; margin-top: 12px; }
.settings-group h3 { margin: 0 0 8px; font-size: 0.95rem; }
.settings-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr)); gap: 10px 16px; align-items: start; }
.setting-field { display: grid; gap: 4px; font-size: 0.82rem; }
.setting-field label { font-weight: 600; }
.setting-field small { color: var(--text-dim); font-size: 0.72rem; }
.setting-field input[type="number"], .setting-field textarea { background: var(--bg-panel-2); border: 1px solid var(--border-soft); color: var(--text); padding: 7px 10px; border-radius: 8px; font-family: var(--font-body); font-size: 0.85rem; width: 100%; }
.setting-field textarea { font-family: ui-monospace, Consolas, monospace; font-size: 0.76rem; min-height: 120px; }
.setting-field.custom label::after { content: " · modifié"; color: var(--accent-2); font-weight: 400; }
.setting-reset { justify-self: start; padding: 2px 8px; font-size: 0.7rem; }
.settings-actions { position: sticky; bottom: 8px; display: flex; gap: 10px; justify-content: flex-end; margin-top: 14px; padding: 10px; border-radius: 12px; background: var(--bg-panel-solid); border: 1px solid var(--border-soft); }
.settings-errors { color: var(--danger); font-size: 0.82rem; }

/* Rarete Unique (verte) : seulement en completant une ligne du coffre-fort
   perso (api/src/native/unique.js). Nom degrade vert anime, halo vert. */
[data-rarity="unique"] .card-name,
[data-rarity="unique"] .card-modal-name,
[data-rarity="unique"] .pull-log-name {
  background: linear-gradient(120deg, var(--rarity-unique), #d9ffe6, var(--rarity-unique));
  background-size: 200% auto;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  animation: legendaryNameShimmer 3s linear infinite;
}
.collection-card:not(.locked)[data-rarity="unique"] {
  border-color: var(--rarity-unique);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--rarity-unique) 60%, transparent), 0 0 22px color-mix(in srgb, var(--rarity-unique) 38%, transparent), 1.5px 1.5px 0 rgba(0,0,0,0.35);
}
.card[data-rarity="unique"] .card-front { border-width: 2px; border-color: var(--rarity-unique); box-shadow: 0 0 26px color-mix(in srgb, var(--rarity-unique) 55%, transparent); }
.card-modal[data-rarity="unique"] { border-color: var(--rarity-unique); box-shadow: 0 40px 80px -20px rgba(0,0,0,0.8), 0 0 0 1px color-mix(in srgb, var(--rarity-unique) 55%, transparent), 0 0 46px color-mix(in srgb, var(--rarity-unique) 38%, transparent); }

/* Coffre-fort perso : carte Unique gagnee en completant une ligne */
.pv-unique-note { color: var(--rarity-unique); font-weight: 700; }
.unique-reveal-overlay { position: fixed; inset: 0; z-index: 1200; display: grid; place-items: center; padding: 24px 16px; background: radial-gradient(circle at 50% 45%, color-mix(in srgb, var(--rarity-unique) 22%, rgba(4,6,12,0.92)), rgba(4,6,12,0.94) 70%); animation: uniqueFade 0.3s ease both; cursor: pointer; }
.unique-reveal { display: grid; justify-items: center; gap: 12px; text-align: center; max-width: 340px; }
.unique-reveal-eyebrow { font-family: var(--font-display); letter-spacing: 0.08em; text-transform: uppercase; color: var(--rarity-unique); font-size: 0.85rem; }
.unique-reveal-card { width: min(240px, 66vw); border-radius: 16px; overflow: hidden; border: 2px solid var(--rarity-unique); background: var(--bg-panel-2); box-shadow: 0 0 0 1px color-mix(in srgb, var(--rarity-unique) 50%, transparent), 0 0 60px color-mix(in srgb, var(--rarity-unique) 55%, transparent); animation: uniquePop 0.7s cubic-bezier(.2,.9,.3,1.3) both; }
.unique-reveal-card img { width: 100%; aspect-ratio: 3 / 4; object-fit: cover; display: block; }
.unique-reveal-card .card-info { padding: 10px; }
.unique-reveal-sub { color: var(--text-dim); font-size: 0.85rem; }
@keyframes uniqueFade { from { opacity: 0; } }
@keyframes uniquePop { 0% { transform: scale(0.4) rotateY(90deg); opacity: 0; } 60% { opacity: 1; } 100% { transform: none; } }
@media (prefers-reduced-motion: reduce) { .unique-reveal-card, .unique-reveal-overlay { animation: none; } }

/* Comptoir Unique (coffre.html) : tickets gagnes au coffre-fort, echanges
   contre la carte Unique de son choix. */
.pv-counter { margin: 4px 0 22px; padding: 16px; border-radius: 16px; border: 1px solid color-mix(in srgb, var(--rarity-unique) 45%, transparent); background: linear-gradient(160deg, color-mix(in srgb, var(--rarity-unique) 12%, transparent), rgba(0,0,0,0.18)); }
.pv-counter-head { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 10px; }
.pv-counter-head h2 { margin: 0; text-transform: none; color: var(--rarity-unique); }
.pv-counter-lead { font-size: 0.86rem; color: var(--text-dim); margin: 8px 0 14px; max-width: 65ch; }
.pv-ticket { position: relative; display: inline-flex; align-items: center; gap: 8px; padding: 7px 16px 7px 14px; border-radius: 6px; background: linear-gradient(135deg, #16a34a, #22c55e 60%, #4ade80); color: #04210f; font-weight: 800; box-shadow: 0 6px 18px -6px rgba(34,197,94,0.7); border-left: 3px dashed rgba(4,33,15,0.45); -webkit-mask: radial-gradient(circle 6px at 0 50%, transparent 98%, #000) left / 51% 100% no-repeat, radial-gradient(circle 6px at 100% 50%, transparent 98%, #000) right / 51% 100% no-repeat; mask: radial-gradient(circle 6px at 0 50%, transparent 98%, #000) left / 51% 100% no-repeat, radial-gradient(circle 6px at 100% 50%, transparent 98%, #000) right / 51% 100% no-repeat; }
.pv-ticket.empty { filter: grayscale(0.8) brightness(0.7); }
.pv-ticket-icon { font-size: 1.2rem; }
.pv-ticket-count { font-family: var(--font-display); font-size: 1.25rem; font-variant-numeric: tabular-nums; }
.pv-ticket-label { font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.06em; }
.pv-ticket.printing { animation: ticketPrint 0.9s cubic-bezier(.2,.9,.3,1.2) both; }
@keyframes ticketPrint { 0% { transform: translateY(-14px) scaleY(0.2); opacity: 0; clip-path: inset(0 0 100% 0); } 60% { opacity: 1; clip-path: inset(0 0 0 0); } 80% { transform: translateY(2px) rotate(-2deg); } 100% { transform: none; } }
.pv-counter-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 140px), 1fr)); gap: 14px; }
.pv-counter-card { display: grid; gap: 8px; justify-items: stretch; min-width: 0; }
.pv-counter-card .collection-card img { width: 100%; aspect-ratio: 3 / 4; object-fit: cover; display: block; }
.pv-counter-card.missing .collection-card img { filter: brightness(0.25) saturate(0) contrast(1.2); }
.pv-counter-card.missing .collection-card { border-style: dashed; }
.pv-counter-card .pv-redeem { width: 100%; padding: 8px 10px; font-size: 0.82rem; }
.pv-counter-owned { text-align: center; font-size: 0.78rem; color: var(--rarity-unique); font-weight: 700; }
.stat-chip-ticket { color: var(--rarity-unique); text-decoration: none; }
@media (prefers-reduced-motion: reduce) { .pv-ticket.printing { animation: none; } }


/* Niveaux de metier (jeux.html : peche, fouille) */
.skill-level { margin: 10px 0 14px; padding: 10px 12px; border-radius: 12px; background: var(--bg-panel-2); border: 1px solid var(--border-soft); display: grid; gap: 6px; }
.skill-level-head { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.skill-level-badge { font-size: 0.9rem; }
.skill-level-badge strong { font-family: var(--font-display); color: var(--accent-2); }
.skill-level-xp { font-size: 0.75rem; color: var(--text-dim); font-variant-numeric: tabular-nums; }
.skill-level-bar { height: 8px; border-radius: 999px; background: rgba(0,0,0,0.35); overflow: hidden; }
.skill-level-fill { height: 100%; border-radius: 999px; background: linear-gradient(90deg, var(--accent), var(--accent-2)); transition: width 0.5s ease; }
.skill-level-perks { display: flex; flex-wrap: wrap; gap: 6px; }
.skill-level-perks span { font-size: 0.72rem; padding: 2px 8px; border-radius: 999px; border: 1px solid var(--border-soft); color: var(--text-dim); }

/* Atelier : piece detachee dans le choix des exemplaires */
.fusion-part { display: flex; align-items: center; gap: 8px; margin: 10px 0 4px; padding: 8px 10px; border-radius: 10px; border: 1px dashed var(--accent-2); font-size: 0.85rem; cursor: pointer; }
.fusion-part input { accent-color: var(--accent-2); }

/* Halo vert anime des cartes Unique (collection, profil public, vitrine) */
@keyframes uniqueHalo {
  0%, 100% { box-shadow: 0 0 0 1px color-mix(in srgb, var(--rarity-unique) 60%, transparent), 0 0 14px color-mix(in srgb, var(--rarity-unique) 30%, transparent); }
  50% { box-shadow: 0 0 0 2px color-mix(in srgb, var(--rarity-unique) 85%, transparent), 0 0 30px color-mix(in srgb, var(--rarity-unique) 60%, transparent); }
}
.collection-card:not(.locked)[data-rarity="unique"] { animation: uniqueHalo 2.6s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce) { .collection-card:not(.locked)[data-rarity="unique"] { animation: none; } }

/* En-tete : pieces detachees */
.stat-chip-part { color: var(--accent-2); text-decoration: none; }

/* Passage de niveau */
.levelup-card { width: min(360px, 100%); padding: 26px 22px 20px; border-radius: 20px; text-align: center; background: radial-gradient(circle at 50% 0%, color-mix(in srgb, var(--accent-2) 30%, var(--bg-panel-solid)), var(--bg-panel-solid) 70%); border: 1px solid color-mix(in srgb, var(--accent-2) 55%, transparent); box-shadow: 0 30px 70px -20px rgba(0,0,0,0.8), 0 0 40px color-mix(in srgb, var(--accent-2) 30%, transparent); animation: modalPopIn 0.45s cubic-bezier(.2,.9,.3,1.3) both; display: grid; gap: 8px; justify-items: center; }
.levelup-icon { font-size: 3rem; animation: levelupBounce 1.2s ease-in-out infinite; }
@keyframes levelupBounce { 0%, 100% { transform: translateY(0) rotate(-4deg); } 50% { transform: translateY(-8px) rotate(4deg); } }
.levelup-eyebrow { text-transform: uppercase; letter-spacing: 0.1em; font-size: 0.78rem; color: var(--text-dim); }
.levelup-level { font-family: var(--font-display); font-size: 2rem; color: var(--accent-2); }
.levelup-tool { font-size: 0.85rem; }
.levelup-perks { list-style: none; padding: 0; margin: 6px 0 10px; display: grid; gap: 6px; width: 100%; }
.levelup-perks li { padding: 6px 10px; border-radius: 10px; background: var(--bg-panel-2); font-size: 0.85rem; }
.levelup-perks li::before { content: "\2714  "; color: var(--success); }
@media (prefers-reduced-motion: reduce) { .levelup-icon { animation: none; } }

/* Piece detachee qui se visse (atelier) */
.part-screw-overlay { position: fixed; inset: 0; z-index: 1300; display: grid; place-items: center; background: rgba(4,6,12,0.55); pointer-events: none; animation: uniqueFade 0.2s ease both; }
.part-screw-slot { width: 120px; height: 160px; border-radius: 14px; border: 2px dashed var(--accent-2); display: grid; place-items: center; background: rgba(0,0,0,0.35); }
.part-screw { font-size: 3.4rem; animation: partScrew 1s cubic-bezier(.3,.7,.3,1) both; }
@keyframes partScrew { 0% { transform: translateY(-140px) rotate(0deg) scale(1.4); opacity: 0; } 35% { opacity: 1; } 70% { transform: translateY(0) rotate(540deg) scale(1); } 85% { transform: scale(1.12) rotate(600deg); } 100% { transform: scale(1) rotate(630deg); } }

/* Peche : poissons, canne, meteo, carnet */
.fish-swim { position: absolute; font-size: 1.1rem; opacity: 0.55; filter: brightness(0.6) saturate(0.6); animation: fishSwim 14s linear infinite; }
.fish-swim.s1 { top: 30%; animation-duration: 16s; }
.fish-swim.s2 { top: 55%; animation-duration: 11s; animation-delay: -5s; transform: scaleX(-1); animation-name: fishSwimBack; }
.fish-swim.s3 { top: 75%; animation-duration: 19s; animation-delay: -9s; }
.fish-swim.gold { top: 45%; opacity: 0; filter: drop-shadow(0 0 6px #f5d76e) sepia(1) saturate(3) brightness(1.2); animation: fishGold 26s linear infinite; animation-delay: -3s; }
@keyframes fishSwim { from { left: -8%; } to { left: 108%; } }
@keyframes fishSwimBack { from { left: 108%; } to { left: -8%; } }
@keyframes fishGold { 0%, 70% { left: -8%; opacity: 0; } 72% { opacity: 0.9; } 98% { opacity: 0.9; } 100% { left: 108%; opacity: 0; } }
.fish-rod { position: absolute; right: 8%; top: 8%; width: 6px; height: 46%; border-radius: 3px; transform: rotate(-28deg); transform-origin: bottom; background: linear-gradient(#a3d977, #6b8e23); box-shadow: 0 0 0 1px rgba(0,0,0,0.3); }
.fish-rod[data-rod="carbone"] { background: linear-gradient(#4b5563, #111827); }
.fish-rod[data-rod="or"] { background: linear-gradient(#ffe9b8, #d4af37); box-shadow: 0 0 10px rgba(212,175,55,0.7); }
.fish-rod[data-rod="legende"] { background: linear-gradient(#bbf7d0, #22c55e, #a855f7); box-shadow: 0 0 14px rgba(34,197,94,0.8); }
.fish-weather { position: absolute; left: 10px; bottom: 10px; z-index: 2; padding: 4px 10px; border-radius: 999px; background: rgba(6,7,15,0.65); color: #fff; font-size: 0.78rem; }
.fish-weather small { opacity: 0.8; }
.fish-weather-fx { position: absolute; inset: 0; pointer-events: none; z-index: 1; }
.fish-scene[data-weather="soleil"] .fish-sky { background: radial-gradient(circle at 80% 20%, #ffe28a 0 8%, transparent 9%), linear-gradient(#3b6fb6, #7fb3e6); }
.fish-scene[data-weather="pluie"] .fish-weather-fx { background: repeating-linear-gradient(105deg, transparent 0 14px, rgba(200,220,255,0.35) 14px 15px); background-size: 40px 40px; animation: fishRain 0.6s linear infinite; }
@keyframes fishRain { to { background-position: -10px 40px; } }
.fish-scene[data-weather="brume"] .fish-weather-fx { background: linear-gradient(rgba(220,225,235,0.08), rgba(220,225,235,0.35) 60%, rgba(220,225,235,0.15)); animation: fishFog 8s ease-in-out infinite alternate; }
@keyframes fishFog { from { opacity: 0.6; } to { opacity: 1; } }
.fish-scene[data-weather="orage"] .fish-sky { background: linear-gradient(#141826, #2b3047); }
.fish-scene[data-weather="orage"] .fish-weather-fx { animation: fishStorm 7s infinite; }
@keyframes fishStorm { 0%, 92%, 96%, 100% { background: transparent; } 93%, 95% { background: rgba(255,255,255,0.35); } }
.fish-new { margin-left: 4px; padding: 0 6px; border-radius: 999px; background: var(--accent-2); color: #031018; font-size: 0.65rem; }
.fish-book { margin-top: 8px; }
.fish-book-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 200px), 1fr)); gap: 8px; }
.fish-book-entry { display: grid; grid-template-columns: auto 1fr; column-gap: 8px; align-items: center; padding: 8px 10px; border-radius: 10px; background: var(--bg-panel-2); border-left: 3px solid var(--tier-color, var(--border-soft)); }
.fish-book-entry.unseen { opacity: 0.55; border-left-color: var(--border-soft); }
.fish-book-icon { grid-row: span 2; font-size: 1.4rem; }
.fish-book-name { font-weight: 700; font-size: 0.85rem; }
.fish-book-meta { font-size: 0.72rem; color: var(--text-dim); }
@media (prefers-reduced-motion: reduce) { .fish-swim, .fish-weather-fx { animation: none !important; } }

/* Metiers : outil affiche */
.skill-tool { margin-left: 6px; padding: 1px 8px; border-radius: 999px; font-size: 0.7rem; border: 1px solid var(--border-soft); color: var(--text-dim); }
.skill-tool.tool-fer, .skill-tool.tool-carbone { color: #cbd5e1; border-color: #64748b; }
.skill-tool.tool-or { color: #f5d76e; border-color: #d4af37; }
.skill-tool.tool-legende { color: var(--rarity-unique); border-color: var(--rarity-unique); box-shadow: 0 0 10px color-mix(in srgb, var(--rarity-unique) 40%, transparent); }

/* Chenil : collier et cape */
.dig-kennel-dog-wrap { position: relative; display: inline-grid; place-items: center; }
.dig-dog-collar { position: absolute; left: 50%; bottom: 6px; width: 18px; height: 5px; border-radius: 3px; transform: translateX(-30%); background: #ef4444; box-shadow: 0 0 0 1px rgba(0,0,0,0.35); }
.dig-kennel-dog-wrap[data-tier="bleu"] .dig-dog-collar { background: #3b82f6; }
.dig-kennel-dog-wrap[data-tier="or"] .dig-dog-collar, .dig-kennel-dog-wrap[data-tier="cape"] .dig-dog-collar { background: #f5d76e; box-shadow: 0 0 6px #d4af37; }
.dig-dog-cape { display: none; position: absolute; right: 60%; top: 30%; width: 22px; height: 26px; background: linear-gradient(135deg, #dc2626, #7f1d1d); clip-path: polygon(100% 0, 0 30%, 30% 100%); animation: dogCape 1.2s ease-in-out infinite alternate; }
.dig-kennel-dog-wrap[data-tier="cape"] .dig-dog-cape { display: block; }
@keyframes dogCape { from { transform: rotate(-6deg); } to { transform: rotate(8deg); } }

/* Rapport du chien */
.dog-postcard { position: relative; width: min(420px, 100%); padding: 22px 20px 18px; border-radius: 12px; background: #fdf6e3; color: #3b2f22; box-shadow: 0 30px 70px -20px rgba(0,0,0,0.8); transform: rotate(-1.5deg); animation: modalPopIn 0.4s cubic-bezier(.2,.9,.3,1.3) both; display: grid; gap: 10px; }
.dog-postcard-stamp { position: absolute; top: 10px; right: 12px; width: 46px; height: 54px; display: grid; place-items: center; border: 2px dashed #b45309; border-radius: 4px; font-size: 1.4rem; transform: rotate(6deg); }
.dog-postcard-pic { font-size: 2.6rem; }
.dog-postcard-title { font-family: var(--font-display); font-size: 1.1rem; color: #92400e; }
.dog-postcard-text p { margin: 6px 0; font-size: 0.9rem; }
.dog-postcard-text ul { margin: 4px 0; padding-left: 18px; font-size: 0.88rem; }
.dog-postcard-sign { font-style: italic; text-align: right; font-size: 0.85rem; }
.dog-postcard-close { justify-self: end; }

/* Coffre-fort : tri, apercu, scellage */
.pv-toolbar { display: flex; flex-wrap: wrap; gap: 12px; margin: 0 0 12px; font-size: 0.82rem; }
.pv-toolbar[hidden] { display: none; }
.pv-toolbar label { display: flex; align-items: center; gap: 6px; color: var(--text-dim); }
.pv-toolbar select { background: var(--bg-panel-2); border: 1px solid var(--border-soft); color: var(--text); padding: 5px 8px; border-radius: 8px; }
.pv-row-preview { margin: -2px 0 8px; font-size: 0.78rem; color: var(--rarity-unique); }
.pv-row.sealing .pv-slot { animation: pvSeal 0.5s cubic-bezier(.2,.9,.3,1.3) both; animation-delay: var(--seal-delay, 0ms); }
@keyframes pvSeal { 0% { transform: scale(1); } 40% { transform: scale(0.92); box-shadow: inset 0 0 0 3px #f5d76e, 0 0 18px rgba(245,215,110,0.7); } 100% { transform: scale(1); box-shadow: inset 0 0 0 2px color-mix(in srgb, #f5d76e 60%, transparent); } }

/* Comptoir : vitrine de musee + registre */
.pv-dome { position: relative; padding: 10px 10px 0; border-radius: 60px 60px 12px 12px; background: radial-gradient(circle at 50% 0%, rgba(255,255,255,0.10), transparent 60%); }
.pv-dome-glass { position: absolute; inset: 0; border-radius: 60px 60px 12px 12px; border: 1px solid rgba(255,255,255,0.18); background: linear-gradient(115deg, rgba(255,255,255,0.18) 0 12%, transparent 22% 70%, rgba(255,255,255,0.08) 80%); pointer-events: none; }
.pv-counter-card.missing .pv-dome::after { content: ""; position: absolute; left: 50%; top: -30px; width: 160%; height: 140%; transform: translateX(-50%); background: radial-gradient(ellipse at 50% 0%, rgba(255,246,200,0.35), transparent 60%); opacity: 0; transition: opacity 0.35s ease; pointer-events: none; }
.pv-counter-card.missing:hover .pv-dome::after { opacity: 1; }
.pv-counter-card.missing:hover .collection-card img { filter: brightness(0.55) saturate(0.4) contrast(1.1); transition: filter 0.35s ease; }
.pv-owners { display: flex; flex-wrap: wrap; align-items: center; gap: 2px; font-size: 0.7rem; color: var(--text-dim); }
.pv-owners.empty { justify-content: center; font-style: italic; }
.pv-owner { width: 22px; height: 22px; border-radius: 50%; overflow: hidden; display: inline-grid; place-items: center; background: var(--bg-panel-2); border: 1px solid var(--border-soft); font-size: 0.65rem; margin-left: -6px; }
.pv-owner:first-child { margin-left: 0; }
.pv-owner.first { border-color: #f5d76e; box-shadow: 0 0 6px rgba(245,215,110,0.7); }
.pv-owner img { width: 100%; height: 100%; object-fit: cover; }
.pv-owners-label { width: 100%; margin-top: 2px; }

/* Ticket qui se dechire */
.ticket-tear-overlay { position: fixed; inset: 0; z-index: 1250; display: grid; place-items: center; background: rgba(4,6,12,0.6); pointer-events: none; }
.ticket-tear { display: flex; font-family: var(--font-display); font-size: 1.3rem; color: #04210f; }
.ticket-half { padding: 16px 22px; background: linear-gradient(135deg, #16a34a, #22c55e 60%, #4ade80); box-shadow: 0 10px 30px -10px rgba(34,197,94,0.8); }
.ticket-half.left { border-right: 3px dashed rgba(4,33,15,0.5); border-radius: 8px 0 0 8px; animation: tearLeft 0.9s ease-in both; }
.ticket-half.right { border-radius: 0 8px 8px 0; animation: tearRight 0.9s ease-in both; }
@keyframes tearLeft { 0%, 35% { transform: none; } 100% { transform: translate(-60px, 40px) rotate(-18deg); opacity: 0; } }
@keyframes tearRight { 0%, 35% { transform: none; } 100% { transform: translate(60px, 40px) rotate(18deg); opacity: 0; } }

/* Communaute : classement des metiers */
.skills-boards { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr)); gap: 16px; }
.skills-since { grid-column: 1 / -1; font-size: 0.78rem; color: var(--text-dim); }
.skills-board { padding: 14px; border-radius: 14px; background: var(--bg-panel-2); border: 1px solid var(--border-soft); }
.skills-board h3 { margin: 0 0 10px; font-size: 1rem; }
.skills-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
.skills-row { display: grid; grid-template-columns: 28px 28px 1fr auto auto; align-items: center; gap: 8px; padding: 6px 8px; border-radius: 10px; font-variant-numeric: tabular-nums; }
.skills-row.champion { background: linear-gradient(90deg, rgba(245,215,110,0.18), transparent); box-shadow: inset 0 0 0 1px rgba(245,215,110,0.45); }
.skills-row.me { outline: 1px solid var(--accent-2); }
.skills-rank { text-align: center; font-weight: 800; color: var(--text-dim); }
.skills-row.champion .skills-rank { font-size: 1.1rem; }
.skills-avatar { width: 28px; height: 28px; border-radius: 50%; overflow: hidden; display: grid; place-items: center; background: var(--bg-panel); font-size: 0.75rem; }
.skills-avatar img { width: 100%; height: 100%; object-fit: cover; }
.skills-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text); text-decoration: none; font-weight: 600; }
.skills-level { font-size: 0.72rem; color: var(--text-dim); }
.skills-xp { font-weight: 700; color: var(--accent-2); }

/* Vers de terre (fouille -> peche) */
.stat-chip-worm { color: #d9a58a; text-decoration: none; }
.dig-worms { margin-top: 6px; color: #d9a58a; font-weight: 700; animation: modalPopIn 0.35s cubic-bezier(.2,.9,.3,1.3) both; }
