/* ============================================================
   Collection — feuille de style principale
   ============================================================ */

/* CSS officiel pokemon-cards-css (simeydotme, GPL-3.0) vendorisé tel quel dans cards/
   (seuls les chemins /img/ ont été adaptés). Voir la section « glue » plus bas.
   IMPORTANT : les @import DOIVENT rester les toutes premières règles du fichier —
   toute règle placée avant (même un @font-face) les ferait IGNORER par le navigateur
   (les cartes perdraient leur CSS de base et deviendraient invisibles). */
@import url('cards/base.css');
@import url('cards.css');
@import url('cards/basic.css');
@import url('cards/regular-holo.css');
@import url('cards/cosmos-holo.css');
@import url('cards/reverse-holo.css');
@import url('cards/amazing-rare.css');
@import url('cards/rainbow-holo.css');
@import url('cards/secret-rare.css');
@import url('cards/v-regular.css');
@import url('cards/v-max.css');
@import url('cards/v-star.css');
@import url('cards/radiant-holo.css');

/* Polices AUTO-HÉBERGÉES (la CSP — style-src/font-src 'self' — bloquait l'@import Google
   Fonts : le site tournait en police système). Fichiers variables woff2, sous-ensemble latin. */
@font-face {
  font-family: 'Baloo 2';
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url('../fonts/baloo2-latin.woff2') format('woff2');
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('../fonts/inter-latin.woff2') format('woff2');
}

:root {
  --color-bg:          #F2F4F8;
  --color-card:        #FFFFFF;
  --color-text:        #1F2937;
  --color-heading:     #15233D;
  --color-muted:       #6B7280;
  --color-border:      #E2E4E9;
  --color-error:       #B3261E;
  --color-error-bg:    #FBEAE9;
  --color-success:     #1E7B34;
  --color-success-bg:  #EAF6EC;
  --radius:            14px;
  --font-heading:      'Baloo 2', sans-serif;
  --font-body:         'Inter', sans-serif;
  --topbar-h:          60px;
  --accent:            #2563EB;
  --accent-dark:       #1D4ED8;
  /* Échelle d'ombres (du plus discret au plus marqué) */
  --ombre-1: 0 1px 2px rgba(21, 35, 61, .05);
  --ombre-2: 0 1px 3px rgba(21, 35, 61, .06), 0 8px 24px -12px rgba(21, 35, 61, .18);
  --ombre-3: 0 12px 32px -12px rgba(21, 35, 61, .28);
  /* Teintes dérivées de l'accent (calculées, suivent la couleur du profil) */
  --accent-doux: color-mix(in srgb, var(--accent) 9%, #fff);
  --accent-bord: color-mix(in srgb, var(--accent) 35%, var(--color-border));
}

[data-couleur="bleu"]   { --accent: #2563EB; --accent-dark: #1D4ED8; }
[data-couleur="vert"]   { --accent: #059669; --accent-dark: #047857; }
[data-couleur="rouge"]  { --accent: #DC2626; --accent-dark: #B91C1C; }
[data-couleur="violet"] { --accent: #9333EA; --accent-dark: #7E22CE; }
[data-couleur="orange"] { --accent: #EA580C; --accent-dark: #C2410C; }
[data-couleur="rose"]   { --accent: #DB2777; --accent-dark: #BE185D; }
[data-couleur="jaune"]  { --accent: #CA8A04; --accent-dark: #A16207; }
[data-couleur="cyan"]   { --accent: #0891B2; --accent-dark: #0E7490; }

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  font-family: var(--font-body);
  background: var(--color-bg);
  color: var(--color-text);
  min-height: 100vh;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}
h1, h2, h3, .titre { font-family: var(--font-heading); color: var(--color-heading); line-height: 1.2; }
h1.titre { font-size: 1.9rem; margin: .4rem 0 1.1rem; }
a { color: var(--accent); }
button { font-family: inherit; }
/* Accessibilité : anneau de focus visible au clavier, partout. */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 4px;
}

.conteneur {
  max-width: 1100px;
  margin: 0 auto;
  padding: 1.5rem 1rem 3rem;
}
.conteneur-centre { text-align: center; padding-top: 3rem; }
.msg-etroit { max-width: 400px; margin: 1rem auto; }
.masque { display: none !important; }
.form-nouveau-profil { max-width: 420px; margin: 0 auto; text-align: left; }

/* ── Boutons ─────────────────────────────────────────────── */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .4rem;
  padding: .65rem 1.2rem;
  border-radius: 999px;
  border: none;
  font-weight: 600;
  font-size: .95rem;
  cursor: pointer;
  text-decoration: none;
  transition: transform .1s ease, box-shadow .15s ease;
}
.btn:active { transform: scale(.97); }
.btn-primaire { background: var(--accent); color: #fff; box-shadow: 0 4px 14px -4px var(--accent); }
.btn-primaire:hover { background: var(--accent-dark); }
.btn-secondaire { background: #fff; color: var(--color-text); border: 1px solid var(--color-border); }
.btn-secondaire:hover { border-color: var(--accent-bord); background: var(--accent-doux); }
.btn-danger { background: var(--color-error-bg); color: var(--color-error); }
.btn-danger:hover { background: var(--color-error); color: #fff; }
.btn-petit { padding: .4rem .8rem; font-size: .82rem; }
.btn[disabled] { opacity: .5; cursor: not-allowed; }

/* ── Formulaires ─────────────────────────────────────────── */
.champ { margin-bottom: 1rem; display: flex; flex-direction: column; gap: .35rem; }
.champ label { font-weight: 600; font-size: .88rem; }
.champ input, .champ select, .champ textarea {
  padding: .6rem .7rem;
  border: 1px solid var(--color-border);
  border-radius: 10px;
  font-family: inherit;
  font-size: .95rem;
  background: #fff;
  transition: border-color .12s ease, box-shadow .12s ease;
}
.champ input:hover, .champ select:hover, .champ textarea:hover { border-color: var(--accent-bord); }
.champ input:focus, .champ select:focus, .champ textarea:focus {
  outline: none; border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 18%, transparent);
}
.champ small { color: var(--color-muted); font-size: .8rem; }
/* Deux champs côte à côte (min/max des fiches). */
.champ-paire { display: flex; gap: .5rem; }
.champ-paire input { width: 100%; min-width: 0; }
/* Formulaire de carte : champs de fiche dynamiques. */
.fiche-champs { margin-top: .4rem; }
.fiche-unite { color: var(--color-muted); font-weight: 400; font-size: .85em; }
/* Éditeur de modèle de fiche : rangées de champs. */
.modele-champs { display: flex; flex-direction: column; gap: .5rem; margin: .4rem 0 .8rem; }
.modele-champ { display: flex; gap: .5rem; align-items: center; }
.modele-champ input[name="champ_label[]"] { flex: 2; min-width: 0; }
.modele-champ select { flex: 1.4; min-width: 0; }
.modele-champ input[name="champ_unite[]"] { flex: 1; min-width: 0; }
.champ-label-bloc { display: block; font-weight: 600; font-size: .88rem; margin-top: .5rem; }
.pastille-couleur { display: inline-block; width: 14px; height: 14px; border-radius: 4px; vertical-align: middle; border: 1px solid rgba(0,0,0,.15); }
.fil-ariane { color: var(--color-muted); font-size: .9rem; margin: 0 0 .6rem; }
.fil-ariane a { color: var(--accent); }
.table-admin { width: 100%; border-collapse: collapse; margin-bottom: 1.2rem; }
.table-admin th, .table-admin td { text-align: left; padding: .5rem .6rem; border-bottom: 1px solid var(--color-border); vertical-align: middle; }
.table-admin tbody tr:hover { background: var(--accent-doux); }
.msg { padding: .7rem 1rem; border-radius: 10px; font-size: .9rem; margin-bottom: 1rem; }
.msg-erreur { background: var(--color-error-bg); color: var(--color-error); }
.msg-succes { background: var(--color-success-bg); color: var(--color-success); }

/* Formulaire en grille : 2 colonnes dès que la place le permet ; un .champ--large
   occupe toute la largeur (textarea, upload avec aperçu…). */
.form-grille {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  column-gap: 1.4rem;
}
.form-grille .champ--large { grid-column: 1 / -1; }
/* Section de formulaire : regroupe les champs liés sous un intertitre discret. */
.form-section {
  border: 1px solid var(--color-border);
  border-radius: 12px;
  padding: 1rem 1.1rem .35rem;
  margin-bottom: 1.1rem;
  background: color-mix(in srgb, var(--color-bg) 45%, #fff);
}
.form-section > legend, .form-section-titre {
  font-family: var(--font-heading);
  font-weight: 700; font-size: .95rem; color: var(--color-heading);
  padding: 0 .45rem; margin: 0 0 .5rem;
}
.form-actions { display: flex; gap: .6rem; align-items: center; margin-top: 1.1rem; flex-wrap: wrap; }

.carte-panneau {
  background: var(--color-card);
  border-radius: var(--radius);
  border: 1px solid var(--color-border);
  padding: 1.4rem;
  margin-bottom: 1.25rem;
  box-shadow: var(--ombre-1);
}
.carte-panneau > h2:first-child { margin-top: 0; }

table.table-liste { width: 100%; border-collapse: collapse; }
table.table-liste th, table.table-liste td {
  text-align: left; padding: .65rem .6rem; border-bottom: 1px solid var(--color-border); font-size: .9rem;
  vertical-align: middle;
}
table.table-liste th { color: var(--color-muted); font-weight: 600; font-size: .76rem; text-transform: uppercase; letter-spacing: .04em; }
table.table-liste tbody tr { transition: background .1s ease; }
table.table-liste tbody tr:hover { background: var(--accent-doux); }
table.table-liste tbody tr:last-child td { border-bottom: none; }
.cellule-actions { display: flex; gap: .4rem; flex-wrap: wrap; }
.cellule-actions form { display: inline; }

/* ── Topbar ──────────────────────────────────────────────── */
.topbar {
  height: var(--topbar-h);
  display: flex; align-items: center; gap: 1.25rem;
  padding: 0 1.25rem;
  background: linear-gradient(180deg, color-mix(in srgb, var(--color-heading) 92%, var(--accent)), var(--color-heading));
  color: #fff;
  position: sticky; top: 0; z-index: 50;
  box-shadow: 0 1px 0 rgba(255,255,255,.06) inset, var(--ombre-2);
}
@media (max-width: 720px) {
  .topbar { gap: .6rem; padding: 0 .75rem; }
  .topbar-nav { flex: none; }
}
.topbar-titre {
  display: flex; align-items: center; gap: .5rem;
  color: #fff; text-decoration: none;
  font-family: var(--font-heading); font-weight: 800; font-size: 1.15rem;
}
.topbar-nav { display: flex; gap: .25rem; margin-left: auto; }
.topbar-lien {
  color: rgba(255,255,255,.8); text-decoration: none; padding: .5rem .8rem; border-radius: 8px; font-size: .9rem; font-weight: 500;
  white-space: nowrap;
  transition: background .12s ease, color .12s ease;
}
.topbar-lien:hover { background: rgba(255,255,255,.12); color: #fff; }
.topbar-lien.actif { background: rgba(255,255,255,.16); color: #fff; box-shadow: 0 -2px 0 var(--accent) inset; }
.topbar-prefs { padding: .5rem .55rem; font-size: 1.05rem; }
.topbar-depense { font-size: .85rem; font-weight: 600; color: #ffd76a; white-space: nowrap; }
.topbar-profil {
  display: flex; align-items: center; gap: .5rem;
  color: #fff; text-decoration: none;
  padding: .3rem .7rem .3rem .3rem;
  border-radius: 999px;
  background: rgba(255,255,255,.1);
  font-size: .85rem;
}
.topbar-avatar {
  width: 28px; height: 28px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: var(--accent); font-size: 1rem;
}

/* ── Écran de profils ────────────────────────────────────── */
.profils-grille {
  display: flex; flex-wrap: wrap; gap: 1.5rem; justify-content: center; margin: 2rem 0;
}
.profil-tuile {
  background: none; border: none; cursor: pointer;
  display: flex; flex-direction: column; align-items: center; gap: .6rem;
  color: var(--color-text);
  padding: .5rem;
  border-radius: 12px;
}
.profil-tuile:hover .profil-avatar { transform: scale(1.06); box-shadow: 0 0 0 4px #fff, 0 0 0 6px var(--accent); }
.profil-avatar {
  width: 96px; height: 96px; border-radius: 16px;
  display: flex; align-items: center; justify-content: center;
  font-size: 2.6rem;
  background: var(--accent);
  transition: transform .15s ease, box-shadow .15s ease;
}
.profil-nom { font-weight: 600; }
.profil-tuile-ajout .profil-avatar { background: #fff; border: 2px dashed var(--color-border); color: var(--color-muted); }

/* ── Accueil / choix de collection ──────────────────────────*/
.collections-grille {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 1.5rem;
}
.collection-tuile {
  background: var(--color-card); border-radius: var(--radius); border: 1px solid var(--color-border);
  padding: 1.25rem; text-decoration: none; color: var(--color-text);
  display: flex; flex-direction: column; gap: .5rem;
  transition: transform .15s ease, box-shadow .15s ease;
}
.collection-tuile:hover { transform: translateY(-3px); box-shadow: 0 10px 24px -12px rgba(0,0,0,.25); }
.collection-tuile h3 { margin: 0; }
.collection-progres { height: 8px; border-radius: 999px; background: var(--color-border); overflow: hidden; }
.collection-progres > span { display: block; height: 100%; width: 0; background: var(--accent); transition: width .4s ease; }
.collection-actions { display: flex; gap: .5rem; margin-top: .4rem; flex-wrap: wrap; }
.collection-theme { color: var(--color-muted); font-size: .85rem; margin: -.3rem 0 0; }

/* ── Tirage / pack opening ──────────────────────────────────*/
.tirage-zone { text-align: center; padding: 2rem 0; }

/* ── Ouverture d'un paquet tiré : on TIRE la languette du haut de gauche à droite pour
   déchirer le paquet (app.js pilote la progression via --dechir), puis il éclate et les
   cartes se révèlent. ── */
.paquet-ouverture {
  display: flex; flex-direction: column; align-items: center; gap: 1.1rem;
  margin: 1.6rem auto; width: fit-content; user-select: none;
}
.paquet-ouverture-scene {
  position: relative; display: inline-block;
  touch-action: none; cursor: grab;
  --dechir: 0;
}
.paquet-ouverture-scene:active { cursor: grabbing; }
.paquet-ouverture-corps { position: relative; }
/* La réaction au tirage porte sur la SCÈNE ENTIÈRE, pas sur le seul corps du paquet :
   sinon le paquet s'inclinait tout seul et la languette, restée d'aplomb, se retrouvait
   décalée et non parallèle — elle semblait flotter à côté du paquet au lieu d'être dessus. */
.paquet-ouverture-scene { transition: transform .2s ease; transform-origin: 50% 90%; }
.paquet-tire .paquet-ouverture-scene { transform: rotate(-1deg) scale(1.01); }
.paquet-ouverture-img {
  display: block; max-height: 45vh; max-width: min(90vw, 420px);
  filter: drop-shadow(0 18px 30px rgba(0,0,0,.35));
  pointer-events: none;
}
.paquet-ouverture-defaut {
  width: min(64vw, 270px); aspect-ratio: 3 / 4; border-radius: 14px;
  background: linear-gradient(150deg, var(--accent), var(--accent-dark));
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: .8rem;
  color: #fff; padding: 1rem; text-align: center;
  box-shadow: 0 18px 30px rgba(0,0,0,.35);
}
.paquet-ouverture-defaut span { font-size: 4rem; }

/* ── ON ARRACHE LE HAUT DU PAQUET ───────────────────────────────────────────────────────
   Le morceau déchiré n'est pas dessiné : il est RETIRÉ. Un polygone creuse dans le visuel
   du paquet une encoche en haut à gauche, large de --dechir et haute de --haut ; ce qui
   est arraché disparaît purement et simplement, et on voit la page derrière — comme un
   vrai bout de papier qu'on emporte. Pas de lueur, pas de lambeau qui vole : ce sont eux
   qui trahissaient l'illusion.
   Le rognage porte sur .paquet-ouverture-corps, donc sur les DEUX moitiés du paquet à la
   fois (elles gardent leur propre découpe gauche/droite pour l'éclatement final). ── */
.paquet-ouverture-scene { --haut: 12%; }   /* hauteur de la bande arrachée, en % du paquet */

/* Le polygone de l'encoche est construit par app.js (setDechir), parce qu'il porte des
   DENTS le long de la ligne de déchirure : leur nombre dépend de la largeur arrachée, ce
   que CSS ne sait pas calculer. Tant que JS n'a pas parlé, aucun rognage → paquet entier. */
/* À l'éclatement, les moitiés s'envolent : plus aucun rognage, sinon elles seraient
   tranchées par un cadre resté immobile. */
.paquet-ouverture--ouvre .paquet-ouverture-corps { clip-path: none !important; }
.paquet-ouverture--ouvre .paquet-bande { display: none; }

/* Bande perforée : le repère « ligne à déchirer ». Elle est mangée par la gauche
   exactement comme le paquet, et disparaît donc avec lui. */
.paquet-bande {
  position: absolute; top: 0; left: 0; right: 0; height: var(--haut);
  z-index: 3; pointer-events: none; opacity: .8;
  border-radius: 14px 14px 4px 4px;
  background: repeating-linear-gradient(45deg, rgba(255,255,255,.28) 0 10px, rgba(255,255,255,.12) 10px 20px),
              linear-gradient(180deg, rgba(255,255,255,.35), rgba(0,0,0,.12));
  -webkit-mask: linear-gradient(#000 0 0) top/100% calc(100% - 7px) no-repeat,
                radial-gradient(circle at 7px 3px, transparent 3.5px, #000 4px) bottom left/14px 8px repeat-x;
  mask: linear-gradient(#000 0 0) top/100% calc(100% - 7px) no-repeat,
        radial-gradient(circle at 7px 3px, transparent 3.5px, #000 4px) bottom left/14px 8px repeat-x;
}

/* La main reste AU front de déchirure et avance avec lui. */
.paquet-prise {
  position: absolute; top: calc(var(--haut) / 2); left: calc(var(--dechir, 0) * 100%);
  transform: translate(-50%, -50%); z-index: 6; pointer-events: none;
  display: flex; align-items: center; gap: .45rem; white-space: nowrap;
}
.paquet-bande-poignee { font-size: 1.3rem; filter: drop-shadow(0 1px 2px rgba(0,0,0,.5)); }
.paquet-bande-fleches {
  font-weight: 800; color: #fff; letter-spacing: .1em; font-size: .9rem;
  text-shadow: 0 1px 2px rgba(0,0,0,.6);
  animation: bande-appel 1.3s ease-in-out infinite;
  transition: opacity .25s ease;
}
.paquet-tire .paquet-bande-fleches { opacity: 0; }
@keyframes bande-appel { 0%,100% { opacity: .5; } 50% { opacity: 1; } }

/* Le retour élastique et la fin de déchirure sont animés par app.js (il fait varier
   --dechir image par image) : un polygone à dents change de nombre de sommets, une
   transition CSS ne saurait pas l'interpoler. */

/* Au bout, la main s'efface : il ne reste que le paquet ouvert. */
.paquet-dechire--fini .paquet-prise { opacity: 0; transition: opacity .2s ease; }

.paquet-ouverture-hint { margin: 0; font-weight: 600; }
.paquet-ouverture--ouvre { pointer-events: none; }
.paquet-ouverture--ouvre .paquet-ouverture-hint { opacity: 0; transition: opacity .3s ease; }
/* Le paquet tressaille, puis s'ouvre EN DEUX (voir « Ouverture de paquet v2 » plus bas). */
.paquet-ouverture--ouvre .paquet-ouverture-corps {
  animation: paquet-secousse .24s ease-in-out 2;
}
@keyframes paquet-secousse {
  0%, 100% { transform: translateX(0) rotate(0); }
  25%      { transform: translateX(-4px) rotate(-1.2deg); }
  75%      { transform: translateX(4px)  rotate(1.2deg); }
}
@media (prefers-reduced-motion: reduce) {
  .paquet-bande-fleches { animation: none; }
}

/* ── Fenêtre de choix des paquets (accueil) ── */
.paquets-modal { position: fixed; inset: 0; z-index: 200; display: flex; align-items: center; justify-content: center; padding: 1rem; }
.paquets-modal-fond { position: absolute; inset: 0; background: rgba(10, 14, 25, .55); backdrop-filter: blur(3px); }
.paquets-modal-boite {
  position: relative; background: #fff; border-radius: 16px;
  width: min(92vw, 760px); max-height: 86vh; overflow-y: auto;
  padding: 1.2rem 1.4rem 1.5rem;
  box-shadow: 0 30px 80px -20px rgba(0, 0, 0, .5);
}
.paquets-modal-entete { display: flex; justify-content: space-between; align-items: center; gap: 1rem; margin-bottom: 1rem; }
.paquets-modal-entete h2 { margin: 0; font-size: 1.15rem; }
.paquets-modal-fermer {
  border: none; background: transparent; font: inherit; font-size: 1.25rem;
  cursor: pointer; line-height: 1; padding: .35rem .55rem; border-radius: 8px;
}
.paquets-modal-fermer:hover { background: rgba(0, 0, 0, .08); }
.paquets-liste { display: flex; flex-wrap: wrap; gap: 1rem; justify-content: center; }
.paquet-tuile {
  display: flex; flex-direction: column; align-items: center; gap: .5rem;
  width: 205px; padding: 1rem; border-radius: 14px;
  border: 2px solid var(--color-border); background: #fff;
  cursor: pointer; font: inherit; text-align: center;
  transition: transform .15s ease, box-shadow .15s ease, border-color .15s ease;
}
.paquet-tuile:hover:not(:disabled) {
  transform: translateY(-4px); border-color: var(--accent);
  box-shadow: 0 14px 30px -12px rgba(0, 0, 0, .35);
}
.paquet-tuile:disabled { opacity: .55; cursor: not-allowed; filter: grayscale(.6); }
.paquet-visuel { width: 125px; height: 150px; display: flex; align-items: center; justify-content: center; }
.paquet-visuel img { max-width: 100%; max-height: 100%; object-fit: contain; filter: drop-shadow(0 8px 14px rgba(0, 0, 0, .25)); }
.paquet-visuel-defaut {
  width: 100%; height: 100%; border-radius: 12px; font-size: 3.2rem;
  display: flex; align-items: center; justify-content: center;
  background: linear-gradient(150deg, var(--accent), var(--accent-dark));
}
.paquet-libelle { font-weight: 700; }
.paquet-prix { font-weight: 700; border-radius: 999px; padding: .15rem .75rem; font-size: .85rem; }
.paquet-prix--gratuit { background: var(--color-success-bg); color: var(--color-success); }
.paquet-prix--payant { background: #fff3cd; color: #7a5b00; }
.paquet-note { font-size: .78rem; opacity: .75; }
.paquet-note--epuise { color: var(--color-error); opacity: 1; font-weight: 600; }
/* Garantie de rareté : c'est l'argument de vente du paquet, il ne doit pas s'effacer. */
.paquet-note--garantie { opacity: 1; font-weight: 600; color: var(--color-heading); }
/* Garantie que l'album ne peut pas honorer (admin) : pas assez de cartes de cette rareté. */
.badge-alerte { color: var(--color-error); font-weight: 700; cursor: help; }
.paquet-mini { max-width: 46px; max-height: 60px; }
.tirage-actions { margin-top: 2rem; display: flex; gap: .75rem; justify-content: center; }
.tirage-overlay {
  position: fixed; inset: 0; background: rgba(15,17,23,.85);
  display: flex; align-items: center; justify-content: center; flex-wrap: wrap; gap: 1.5rem;
  z-index: 200; padding: 2rem;
}
.tirage-overlay[hidden] { display: none; }
.tirage-fermer {
  position: absolute; top: 1.25rem; right: 1.25rem;
  background: rgba(255,255,255,.12); color: #fff; border: none; border-radius: 999px;
  width: 40px; height: 40px; font-size: 1.2rem; cursor: pointer;
}
.paquet-carte-slot { width: 240px; height: 180px; margin: 0 auto; }   /* horizontal 1024×768 */
.paquet-carte-slot.paquet-carte-slot--vertical   { width: 180px; height: 240px; }
.paquet-carte-slot.paquet-carte-slot--carre      { width: 200px; height: 200px; }
.paquet-carte-slot.paquet-carte-slot--rond       { width: 200px; height: 200px; }

/* ============================================================
   Cartes à collectionner — GLUE d'intégration pokemon-cards-css.
   La 3D, les effets et les variables sont gérés par le CSS vendorisé (cards/) sur la
   structure .card > .card__translater > .card__rotator > .card__front. Ici : formats du
   site (via --card-aspect), calage dans les conteneurs, couches internes, interface.
   ============================================================ */
.card.carte {
  /* valeurs par défaut des variables attendues par cards/base.css (app.js les anime) */
  --pointer-x: 50%; --pointer-y: 50%;
  --pointer-from-center: 0; --pointer-from-top: .5; --pointer-from-left: .5;
  --card-opacity: 0;
  --rotate-x: 0deg; --rotate-y: 0deg;
  --background-x: 50%; --background-y: 50%;
  --card-scale: 1; --translate-x: 0px; --translate-y: 0px;
  /* legacy (parallaxe du calque) */
  --mx: 50%; --my: 50%;
  /* nos cartes n'ont pas le gabarit Pokémon : les découpes (fenêtre d'illustration…)
     couvrent toute la carte */
  --clip: inset(0); --clip-invert: inset(0);
  --clip-stage: inset(0); --clip-stage-invert: inset(0);
  --clip-trainer: inset(0); --clip-borders: inset(0);
  width: 100%;
  position: relative;
  cursor: grab;
}
/* Formats du site : on pilote la variable d'aspect du projet d'origine. */
.card.carte--horizontal { --card-aspect: 1024 / 768; }
.card.carte--vertical   { --card-aspect: 768 / 1024; }
.card.carte--carre      { --card-aspect: 1 / 1; }
.card.carte--rond       { --card-aspect: 1 / 1; --card-radius: 50%; }
/* Coins HOMOGÈNES (quart de cercle) : le CSS d'origine utilise un rayon en % à deux valeurs
   (4.55% / 3.5%) → sur une carte non carrée les coins deviennent des quarts d'ELLIPSE. On
   impose une LONGUEUR unique (rayon horizontal = vertical), bornée, qui suit la taille de la page. */
.card.carte:not(.carte--rond) { --card-radius: clamp(6px, 2.2cqw, 20px); }
/* Texture --foil PAR DÉFAUT (black.png, uni), en CSS et non en inline : chaque effet qui définit
   sa propre texture la surcharge sur .card__shine (radiant→trainerbg, rainbow→illusion-mask,
   secret→geometric, vmax→vmaxbg, vstar→ancient, reverse→none…). Le défaut noir uni évite toute
   trame parasite (auparavant wave.png ondulé → des « vagues » sur les effets sans foil propre). */
.card.carte, .groupe-effet { --foil: url("../img/black.png"); }
/* La carte remplit la hauteur donnée par son conteneur (zones, pioche…). */
.card.carte .card__translater, .card.carte .card__rotator { width: 100%; height: 100%; }
/* Transitions douces hors interaction (le JS pose les variables sans spring). */
.card.interactive:not(.interacting) .card__rotator { transition: transform .5s ease, box-shadow .4s ease; }
.card.interactive:not(.interacting) .card__shine,
.card.interactive:not(.interacting) .card__glare { transition: opacity .4s ease; }
/* Face avant utilisable aussi hors rotator (recto du tirage) */
.card__front { display: grid; width: 100%; height: 100%; position: relative; }
.card__front > * { grid-area: 1 / 1; }
/* Couches internes de la face avant */
.carte-fond { position: absolute; inset: 0; }
/* Image de fond propre à la carte (cartes.fond_image) : décor derrière l'image principale,
   visible à travers les zones transparentes d'un PNG détouré. */
.carte-fond-img {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
}
.carte-image {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
}
/* Les morceaux de puzzle sont des images pleines : pas de fond de carte. */
.zone-piece .carte-fond-img { display: none; }
/* Interface (numéro, légende) au-dessus des effets, réinitialisée des styles génériques
   que cards/base.css applique à tous les descendants du rotator (.card__rotator *). */
.card.carte .carte-ui {
  position: absolute; inset: 0; pointer-events: none;
  transform: translateZ(1.5px);
  overflow: hidden; border-radius: var(--card-radius);
}
.card.carte .carte-ui * {
  width: auto; height: auto; display: revert; grid-area: auto;
  aspect-ratio: auto; border-radius: revert; overflow: revert;
}
.carte-legende {
  position: absolute; left: 0; right: 0; bottom: 0;
  padding: .5rem .6rem;
  background: linear-gradient(0deg, rgba(0,0,0,.72), transparent);
  color: #fff;
  display: flex; flex-direction: column; gap: .15rem;
}
/* Nom de carte : taille RELATIVE à la page (cqw) → suit le zoom d'affichage comme le texte
   Markdown (identique sur tous les formats ; le zoom plein écran garde ses tailles fixes). */
.carte-legende-nom { font-size: max(7.2px, 1.6cqw); font-weight: 600; }
.carte-legende-desc {
  font-size: .68rem; font-weight: 400; line-height: 1.3;
  color: rgba(255,255,255,.88);
  max-height: 4.5em; overflow-y: auto;
}
.carte-numero {
  position: absolute; top: .4rem; right: .5rem;
  background: rgba(0,0,0,.55); color: #fff; font-size: .7rem; font-weight: 700;
  border-radius: 999px; padding: .1rem .5rem;
}
/* ── Fiche technique normalisée (style Panini), tailles proportionnelles à la carte (cqw).
   Sélecteurs scopés .card.carte .carte-ui … pour battre le reset « .carte-ui * ». La couleur
   du cadre vient de --cadre (posée par app.js depuis data-cadre), défaut doré. ── */
/* La couleur (--cadre) est le FOND de la fiche ; le texte (--cadre-texte, posé par app.js selon
   la luminance) contraste automatiquement. Pas de cadre coloré autour de la photo. */
.card.carte .carte-ui .carte-fiche {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 6;
  container-type: inline-size;          /* cqw des ENFANTS = % de la largeur de la CARTE (pas la grille) */
  display: flex; flex-direction: column; text-align: left;
  padding: 2% 3% 2.4%;                  /* % = proportionnel à la carte, à toute taille */
  max-height: 25%; overflow: hidden;    /* JAMAIS plus de 25 % de la hauteur de la carte */
  background: var(--cadre, rgba(255,255,255,.96));
  color: var(--cadre-texte, #1a2330);
  box-shadow: 0 -2px 10px rgba(0,0,0,.16);   /* légère séparation d'avec la photo */
}
.card.carte .carte-ui .carte-fiche-nom   { display: block; font-weight: 800; font-size: 4.8cqw; line-height: 1; color: inherit; }
/* Titre « riche » (champ de fiche de type « titre ») : corps en graisse normale pour que le
   **gras** ressorte — ex. « Prénom NOM » d'une carte joueur, dont seul le nom est en gras. */
.card.carte .carte-ui .carte-fiche-nom--riche   { font-weight: 500; }
.card.carte .carte-ui .carte-fiche-nom--riche b { font-weight: 900; }
.card.carte .carte-ui .carte-fiche-latin { display: block; font-style: italic; font-size: 3.3cqw; line-height: 1.05; color: inherit; opacity: .82; margin-top: .3cqw; }
.card.carte .carte-ui .carte-fiche-stats { display: flex; flex-wrap: wrap; gap: .3cqw 2.4cqw; margin-top: .8cqw; font-size: 3.3cqw; line-height: 1.12; }
.card.carte .carte-ui .fiche-stat        { display: inline; white-space: nowrap; color: inherit; }
.card.carte .carte-ui .fiche-stat b      { font-weight: 700; color: inherit; margin-right: .2em; }
/* La description déportée reste cachée sur la face (lue par app.js au zoom). */
.card.carte .carte-ui .carte-desc-zoom { display: none; }
/* ── Zoom : carte + description SOUS la carte, sur fond sombre. ── */
.zoom-avec-desc { display: flex; flex-direction: column; align-items: center; gap: 1rem; max-width: 94vw; max-height: 96vh; }
.zoom-avec-desc > .carte--horizontal { width: min(1024px, 92vw, calc(72vh * 1024 / 768)); }
.zoom-avec-desc > .carte--vertical   { width: auto; height: min(760px, 72vh, calc(92vw * 1024 / 768)); }
.zoom-avec-desc > .carte--carre      { width: min(760px, 88vw, 72vh); }
.zoom-avec-desc > .carte { box-shadow: 0 30px 90px -20px rgba(0,0,0,.85); }
.zoom-desc {
  max-width: min(560px, 90vw); color: #fff; text-align: left;
  background: rgba(9, 14, 22, .86); border-radius: 12px;
  padding: .9rem 1.15rem; font-size: 1rem; line-height: 1.5;
  max-height: 26vh; overflow-y: auto;
}
.zoom-desc > :first-child { margin-top: 0; } .zoom-desc > :last-child { margin-bottom: 0; }
.zoom-desc em { font-style: italic; } .zoom-desc strong { font-weight: 700; }
.zoom-desc code { background: rgba(255,255,255,.16); padding: .05em .35em; border-radius: 4px; }
.zoom-desc a { color: #9fd0ff; }
/* Fonds unis.
   'std' = AUCUN fond : les zones transparentes du PNG laissent voir la page / l'arrière-plan
   (un dégradé ne serait de toute façon jamais visible derrière une image opaque en object-fit:cover
   — il ne servait qu'à remplir de gris les parties transparentes, ce qui n'est pas voulu). */
.fond-std       { background: none; }
.fond-alt1      { background: linear-gradient(160deg, #FDE9D9, #F7C99B); }
.fond-alt2      { background: linear-gradient(160deg, #D9F0E6, #9FD9BF); }

/* ============================================================
   Effets holographiques : code OFFICIEL pokemon-cards-css (importé en tête de feuille).
   data-rarity sur .card sélectionne l'effet (voir fondsCatalogue → fondRarity). app.js pose
   les variables --pointer-*, --rotate-*, --background-*, --card-opacity, --mask, --foil,
   --seedx/--seedy/--cosmosbg. Ci-dessous : fonds de base + halo paramétrable par carte.
   ============================================================ */

/* Fonds de base sous l'image. AUCUN fond pour la famille holo : ce dégradé n'était visible que
   dans les zones TRANSPARENTES du PNG (une image opaque le couvre entièrement en object-fit:cover)
   — il y peignait donc une boîte bleu-gris au repos. Transparent → la page se voit au travers.
   L'effet holographique vient des couches shine/glare (data-rarity), pas de .carte-fond. */
.fond-holo, .fond-reverse, .fond-amazing, .fond-rainbow,
.fond-v, .fond-vmax, .fond-vstar, .fond-radiant {
  background: none;
}
/* Gold et cosmos gardent un fond coloré VOLONTAIRE (métal doré / ciel étoilé) qui fait partie
   de l'effet ; mettre `background: none` ici si l'on veut aussi les rendre transparents. */
.fond-gold   { background: linear-gradient(160deg, #6a5326, #b8942f 45%, #f6e29a 60%, #8a6d2b); }
.fond-cosmos { background: #0b1030; }

/* Halo (glare) paramétrable par carte : même formule que cards/base.css, avec
   --halo-k (contraste, 1 = réglage 60 %) et --halo-r (taille, 150 % = réglage 45 %),
   posés par app.js depuis data-halo-contraste / data-halo-taille. Aux valeurs par
   défaut, le rendu est identique à l'original. */
.card.carte .card__glare {
  background-image: radial-gradient(
    var(--halo-r, 150%) var(--halo-r, 150%) at var(--pointer-x) var(--pointer-y),
    hsla(0, 0%, 100%, calc(0.8 * var(--halo-k, 1))) 10%,
    hsla(0, 0%, 100%, calc(0.65 * var(--halo-k, 1))) 20%,
    hsla(0, 0%, 0%, calc(0.5 * var(--halo-k, 1))) 90%
  );
}

/* ── Animation « veille » des cartes holo : au repos (sans survol), un léger balayage
   lumineux périodique signale qu'une carte est holographique — indispensable quand
   l'animation au survol est désactivée (préférence par profil), utile aussi sinon.
   Elle anime les variables du modèle pokemon-cards-css, ce qui exige @property
   (interpolation typée) : app.js pose la classe holo-anim sur <html> quand le navigateur
   le gère. Les animations CSS priment sur les styles inline → le balayage l'emporte au
   repos et cesse dès que .interacting rend la main aux variables posées par app.js. ── */
@property --card-opacity { syntax: '<number>'; inherits: true; initial-value: 0; }
@property --pointer-x { syntax: '<percentage>'; inherits: true; initial-value: 50%; }
@property --pointer-y { syntax: '<percentage>'; inherits: true; initial-value: 50%; }
@property --background-x { syntax: '<percentage>'; inherits: true; initial-value: 50%; }
@property --background-y { syntax: '<percentage>'; inherits: true; initial-value: 50%; }
@property --pointer-from-left { syntax: '<number>'; inherits: true; initial-value: 0.5; }
@property --pointer-from-top { syntax: '<number>'; inherits: true; initial-value: 0.5; }
@property --pointer-from-center { syntax: '<number>'; inherits: true; initial-value: 0; }

@media (prefers-reduced-motion: no-preference) {
  html.holo-anim .card.carte--brillant:not(.interacting),
  html.holo-anim .zone-puzzle-grille[data-groupe-init]:not(.interacting):not([data-effet="aucun"]) {
    /* --seedx (aléa par carte posé par app.js) désynchronise les cartes entre elles */
    animation: holo-veille 4.5s ease-in-out infinite;
    animation-delay: calc(var(--seedx, .5) * -4.5s);
  }
}
@keyframes holo-veille {
  0% {
    --card-opacity: 0;
    --pointer-x: 18%; --pointer-y: 28%;
    --background-x: 40%; --background-y: 40%;
    --pointer-from-left: .18; --pointer-from-top: .28; --pointer-from-center: .65;
  }
  15%, 55% { --card-opacity: .65; }
  75%, 100% {
    --card-opacity: 0;
    --pointer-x: 82%; --pointer-y: 72%;
    --background-x: 60%; --background-y: 60%;
    --pointer-from-left: .82; --pointer-from-top: .72; --pointer-from-center: .65;
  }
}

/* MASQUE = DÉCOUPE PURE. La carte reste en rendu COMPLET : on N'UTILISE PAS la classe vendored
   « .masked », qui ferait basculer CHAQUE effet vers son rendu simplifié « :not(.masked) » (foil,
   blend, params différents → « KO »). On garde donc l'effet strictement identique au non-masqué,
   et on se contente de le ROGNER avec notre propre variable --carte-mask, que le pack ne remet
   jamais à « none » (contrairement à --mask, que radiant & co réinitialisent).
   Mode LUMINANCE : noir du masque = alpha 0 (aucun effet), blanc = alpha 1 (effet identique).
   On rogne shine ET glare (sinon le halo quasi-blanc du glare déborderait sur les zones
   transparentes des PNG détourés → boîte claire par-dessus la page). */
.card.carte-masquee .card__shine,
.card.carte-masquee .card__shine:before,
.card.carte-masquee .card__shine:after,
.card.carte-masquee .card__glare,
.card.carte-masquee .card__glare:after {
  -webkit-mask-image: var(--carte-mask);
  mask-image: var(--carte-mask);
  -webkit-mask-size: cover;
  mask-size: cover;
  -webkit-mask-position: center center;
  mask-position: center center;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-source-type: luminance;
  mask-mode: luminance;
}

/* Calque PNG optionnel : texture de reflet ajoutée au-dessus de l'image (ex : foil holographique
   personnalisé). Se déplace légèrement avec le pointeur (parallax) via --mx/--my. */
.carte-calque {
  position: absolute; inset: -12%; width: 124%; height: 124%; object-fit: cover;
  pointer-events: none; opacity: .4;
  transform: translate(calc((var(--mx, 50%) - 50%) * .5), calc((var(--my, 50%) - 50%) * .5));
  transition: opacity .3s ease;
}
.card.interacting .carte-calque { opacity: .6; }

/* ── Animation de révélation (tirage de paquet) ─────────────*/
.carte-flip-wrap { width: 100%; height: 100%; perspective: 900px; }
.carte-flip-inner {
  position: relative; width: 100%; height: 100%;
  transform-style: preserve-3d;
  transition: transform .8s cubic-bezier(.3,.1,.2,1);
}
.carte-flip-wrap.carte-flip-wrap--revele .carte-flip-inner { transform: rotateY(180deg); }
.carte-face {
  position: absolute; inset: 0; border-radius: 12px; overflow: hidden;
  backface-visibility: hidden;
  box-shadow: 0 6px 16px -8px rgba(0,0,0,.4);
}
.carte-face--recto { transform: rotateY(180deg); }

.carte-verso {
  position: absolute; inset: 0;
  background: linear-gradient(135deg, var(--accent), var(--accent-dark));
  display: flex; align-items: center; justify-content: center;
  color: #fff; font-family: var(--font-heading); font-weight: 800; font-size: 1.4rem;
  backface-visibility: hidden;
}

/* ============================================================
   Album : livre avec pages tournables
   ============================================================ */
/* Le livre occupe tout l'espace disponible : la double page a un ratio 1.5 (2 faces 3/4) ;
   sa largeur = hauteur restante de la fenêtre (100vh − topbar − barre − navigation −
   crédit ≈ 215px) × 1.5, plafonnée à la largeur de l'écran. */
.album-livre {
  perspective: 2600px;
  max-width: none;
  /* Gouttières latérales (--bord-l) réservées aux bandes de navigation : la largeur est
     augmentée d'autant pour que la taille des pages reste inchangée sur grand écran ;
     box-sizing:border-box → le padding rogne le contenu (la reliure), pas le total. */
  --bord-l: clamp(26px, 4%, 50px);
  width: min(100%, calc((100vh - 215px) * 1.5 + 2 * var(--bord-l)));
  padding: 0 var(--bord-l);
  margin: 0 auto; position: relative;
}
/* Niveaux d'affichage (sélecteur « Affichage » de la barre de l'album) — data-zoom /
   data-mode / data-face posés par app.js et mémorisés par appareil (localStorage).
   « Pleine largeur » : le livre prend toute la largeur et dépasse la hauteur de l'écran
   → on défile verticalement. « Une page » : une seule face affichée à la fois (page plus
   grande, confort des petites résolutions) ; la navigation avance face par face. */
.album-livre[data-zoom="double-large"],
.album-livre[data-zoom="simple-large"] { width: 100%; }
.album-livre[data-zoom="simple"] { width: min(100%, calc((100vh - 215px) * .75 + 2 * var(--bord-l))); }
.album-livre[data-mode="simple"][data-face="gauche"] .album-face[data-cote="droite"],
.album-livre[data-mode="simple"][data-face="droite"] .album-face[data-cote="gauche"] { display: none; }
.album-livre[data-mode="simple"] .album-face { border-left: none; }
.album-zoom { display: inline-flex; align-items: center; gap: .45rem; font-size: .85rem; font-weight: 500; }
.album-zoom select {
  padding: .3rem .5rem; border-radius: 8px; border: 1px solid var(--color-border);
  background: #fff; font: inherit; font-size: .85rem;
}

/* Vue album : conteneur pleine largeur, barre compacte à la place du titre. */
.conteneur--album { max-width: none; padding-top: .6rem; padding-bottom: 1rem; }
.album-barre { display: flex; justify-content: space-between; align-items: center; margin-bottom: .5rem; flex-wrap: wrap; gap: .4rem .8rem; }
/* Dans l'album, une carte n'affiche que son nom (la description reste visible au zoom). */
/* (spécificité > .card.carte .carte-ui * qui fait un display: revert) */
.album-face .card.carte .carte-ui .carte-legende-desc { display: none; }
/* Nom et description sur des lignes séparées (le revert ci-dessus les remettait en inline). */
.card.carte .carte-ui .carte-legende { display: flex; flex-direction: column; }
.card.carte .carte-ui .carte-legende-nom,
.card.carte .carte-ui .carte-legende-desc { display: block; }
/* Pas d'ombre portée au repos (elle casse la jointure des morceaux conjoints) ;
   sur les morceaux de puzzle, jamais d'ombre, même au survol. */
.card.carte:not(.interacting) .card__rotator { box-shadow: none; }
.zone-piece .card__rotator { box-shadow: none; }
.album-reliure {
  display: flex; align-items: stretch; background: #fff; border-radius: 10px;
  box-shadow: 0 20px 50px -20px rgba(0,0,0,.4); overflow: hidden;
}
/* Format de page fixe façon papier (portrait, ratio 3/4) : chaque face du livre a les
   mêmes proportions, quel que soit le nombre de vignettes qu'elle contient. */
/* Page au format papier portrait 3/4 (cf. maquettes des modèles). */
.album-face {
  flex: 1; aspect-ratio: 3 / 4; min-width: 0;
  padding: 0; position: relative; overflow: hidden;
  border-left: 1px solid var(--color-border);
  display: block;
}
.album-face:first-child { border-left: none; }
/* Ombre de reliure : dégradé sur le bord INTÉRIEUR de chaque page (droite de la page de
   gauche, gauche de la page de droite) pour l'effet « livre ouvert ». Surimpression douce
   qui laisse passer les clics. Uniquement dans le vrai album (pas l'aperçu du concepteur). */
.album-reliure .album-face::after {
  content: ""; position: absolute; inset: 0; z-index: 4; pointer-events: none;
}
.album-reliure .album-face[data-cote="gauche"]::after {
  background: linear-gradient(to left, rgba(15,11,6,.5), rgba(15,11,6,.28) 2.5%, rgba(15,11,6,.08) 7%, transparent 14%);
}
.album-reliure .album-face[data-cote="droite"]::after {
  background: linear-gradient(to right, rgba(15,11,6,.5), rgba(15,11,6,.28) 2.5%, rgba(15,11,6,.08) 7%, transparent 14%);
}
/* En mode « une seule page », la reliure est sur le bord affiché (l'autre page est cachée) :
   on garde une ombre plus légère du côté du pli. */
.album-livre[data-mode="simple"] .album-reliure .album-face::after {
  background: linear-gradient(to left, rgba(15,11,6,.32), rgba(15,11,6,.1) 4%, transparent 11%);
}
.album-livre[data-mode="simple"][data-face="droite"] .album-reliure .album-face::after {
  background: linear-gradient(to right, rgba(15,11,6,.32), rgba(15,11,6,.1) 4%, transparent 11%);
}
/* Canevas à COORDONNÉES FIXES : .album-face-corps remplit toute la face (portrait 3/4).
   Le fond, la grille des cartes et les surimpressions (entête + textes) partagent
   EXACTEMENT ce même repère → les cartes tombent au même endroit du fond quelle que soit
   la résolution, et éditer titre/textes ne déplace jamais la grille. (Ce corps est aussi
   le nœud remplacé par app.js au tourne-page.) */
.album-face-corps { position: absolute; inset: 0; container-type: inline-size; }
.album-page-entete {
  position: absolute; top: 0; left: 0; right: 0; z-index: 2;
  display: flex; justify-content: space-between; align-items: baseline;
  padding: 1.6cqw 2cqw 3.5cqw; pointer-events: none;
  background: linear-gradient(180deg, rgba(255,255,255,.6), transparent);
}
/* Titre et numéro proportionnels à la largeur de la page (comme le reste du canevas). */
.album-page-entete h3 { font-size: max(10px, 2.9cqw); }
.album-page-numero { color: var(--color-muted); font-size: max(8px, 2.1cqw); }

/* Fond personnalisé de page : image 3/4 mappée EXACTEMENT sur la face 3/4 (object-fit:fill
   → un point à (x %, y %) de l'image tombe toujours à (x %, y %) de la page, à toute
   résolution). Le voile de lisibilité le recouvre. Tout est en absolu, inset:0. */
.album-page-fond-img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: fill; z-index: 0;
}
.album-page-fond-voile {
  position: absolute; inset: 0; z-index: 0;
  background: rgba(255,255,255,0);
}

/* Enveloppe transparente (aucun impact sur la mise en page) portant la couleur de texte de la
   page. « texte-blanc » = tout le texte de la page en blanc, pour un fond sombre / voile sombre. */
.album-page-inner { display: contents; }
.album-page-inner.texte-blanc .zone-texte,
.album-page-inner.texte-blanc .album-page-texte,
.album-page-inner.texte-blanc .page-bande-texte {
  color: #fff; text-shadow: 0 1px 3px rgba(0,0,0,.9);
}
.album-page-inner.texte-blanc .album-page-entete h3 { color: #fff; text-shadow: 0 1px 3px rgba(0,0,0,.9); }
.album-page-inner.texte-blanc .album-page-numero { color: rgba(255,255,255,.85); }
.album-page-inner.texte-blanc .album-page-entete { background: linear-gradient(180deg, rgba(0,0,0,.55), transparent); }
.album-page-inner.texte-blanc .album-page-texte--bas { background: linear-gradient(0deg, rgba(0,0,0,.55), transparent); }
.album-page-inner.texte-blanc .album-page-texte a,
.album-page-inner.texte-blanc .zone-texte a,
.album-page-inner.texte-blanc .page-bande-texte a { color: #9fd0ff; }
.album-page-inner.texte-blanc code { background: rgba(255,255,255,.18); }
/* Couleurs Markdown ajoutées : blanc (fonds sombres) et noir. */
.md-c-blanc { color: #fff; }
.md-c-noir  { color: #141414; }

/* Panneau de LISIBILITÉ par bloc de texte (directive [fond-clair] / [fond-sombre], PAR bloc).
   Ne teinte QUE le bloc Markdown (zone libre, texte de page haut/bas, bande), coins légèrement
   arrondis. Clair = voile clair + texte foncé ; sombre = voile foncé + texte blanc. Placé après
   le bloc .texte-blanc pour gagner en cas d'égalité de spécificité (un panneau clair reste lisible
   même sur une page réglée « texte blanc »). */
.album-face .zone-fond--clair,
.album-face .zone-fond--sombre { border-radius: .7em; }
.album-face .zone-texte.zone-fond--clair,
.album-face .album-page-texte.zone-fond--clair,
.album-face .page-bande-texte.zone-fond--clair {
  background: rgba(255, 255, 255, .82); color: #16202b; text-shadow: none;
}
.album-face .zone-texte.zone-fond--sombre,
.album-face .album-page-texte.zone-fond--sombre,
.album-face .page-bande-texte.zone-fond--sombre {
  background: rgba(14, 22, 32, .60); color: #fff; text-shadow: 0 1px 2px rgba(0, 0, 0, .55);
}
.album-face .zone-fond--sombre a { color: #9fd0ff; }
.album-face .zone-fond--sombre code { background: rgba(255, 255, 255, .18); }
.album-face .zone-fond--clair code { background: rgba(0, 0, 0, .06); }
/* Le voile de page (entête/bas) fait double emploi avec le panneau : on le neutralise dessus. */
.album-face .album-page-texte.zone-fond--clair.album-page-texte--bas,
.album-face .album-page-texte.zone-fond--sombre.album-page-texte--bas { padding-bottom: 2cqw; }
/* Le panneau teinté ÉPOUSE le texte (au lieu de remplir toute la case) : boîte dimensionnée
   au contenu, centrée dans la case, padding resserré, hauteur = contenu → rectangle plus petit. */
.album-face .zone--libre > .zone-texte.zone-fond--clair,
.album-face .zone--libre > .zone-texte.zone-fond--sombre {
  width: fit-content; height: auto; max-width: 94%; max-height: 100%;
  padding: .5em .85em;
  align-self: center;   /* DÉFAUT = centré ; [haut]/[bas] le surchargent ci-dessous */
}
/* L'alignement vertical passe du justify-content interne (sans effet quand la boîte épouse le
   contenu) à align-self dans la case : défaut = centré ci-dessus ; [haut]/[bas] le surchargent. */
.album-face .zone--libre > .zone-texte.zone-fond--clair.zone-v-haut,
.album-face .zone--libre > .zone-texte.zone-fond--sombre.zone-v-haut { align-self: flex-start; }
.album-face .zone--libre > .zone-texte.zone-fond--clair.zone-v-bas,
.album-face .zone--libre > .zone-texte.zone-fond--sombre.zone-v-bas { align-self: flex-end; }
/* Textes de niveau PAGE (haut/bas) et BANDES : panneau resserré et centré, avec de BONNES marges
   gauche/droite (le rectangle ne prend pas toute la largeur). fit-content = BORNÉ (jamais de
   débordement, contrairement à max-content). */
.album-face .album-page-texte.zone-fond--clair,
.album-face .album-page-texte.zone-fond--sombre {
  left: 50%; right: auto; transform: translateX(-50%);
  width: fit-content; max-width: 68%; padding: 1cqw 2cqw;
}
.album-face .page-bande-texte.zone-fond--clair,
.album-face .page-bande-texte.zone-fond--sombre {
  width: fit-content; max-width: 70%; margin-inline: auto; padding: .4em 1em;
}
/* Textes de page (haut/bas) : SURIMPRESSION — n'affectent jamais la grille. Léger fond de
   lisibilité car ils flottent au-dessus de la page. */
/* TAILLE DE TEXTE PROPORTIONNELLE À LA PAGE : les faces/grilles de l'album sont des
   containers (inline-size) → 1cqw = 1 % de la largeur de la page. Les tailles de base
   sont en cqw (calées sur le rendu historique : page ≈ 610px ↔ 1rem ≈ 2.6cqw) et les
   tailles internes (titres, marges) en em → tout le bloc suit la taille de la page,
   quel que soit l'écran ou le niveau de zoom choisi. max(8px, …) = plancher de secours
   sur très petit écran (le débordement éventuel est géré par la pastille [suite]). */
.album-page-texte {
  position: absolute; left: 0; right: 0; z-index: 2;
  max-height: 42%; overflow-y: auto;
  padding: 2cqw 4cqw; font-size: max(7px, 1.75cqw); line-height: 1.45; color: var(--color-text, #1a1a1a);
  text-shadow: 0 1px 2px rgba(255,255,255,.75);
}
.album-page-texte:not(.album-page-texte--bas) { top: 7cqw; }
.album-page-texte--bas {
  bottom: 0;
  background: linear-gradient(0deg, rgba(255,255,255,.6), transparent);
}
.album-page-texte > :first-child { margin-top: 0; }
.album-page-texte > :last-child { margin-bottom: 0; }
.album-page-texte h1, .album-page-texte h2, .album-page-texte h3 { margin: .45em 0 .35em; line-height: 1.2; }
.album-page-texte h1 { font-size: 1.4em; }
.album-page-texte h2 { font-size: 1.28em; }
.album-page-texte h3 { font-size: 1.16em; }
.album-page-texte p, .album-page-texte ul, .album-page-texte ol { margin: .4em 0; }
.album-page-texte ul, .album-page-texte ol { padding-left: 1.45em; }
.album-page-texte a { color: var(--accent); }
.album-page-texte img { max-width: 100%; height: auto; border-radius: 6px; }
.album-page-texte code { background: rgba(0,0,0,.06); padding: .06em .35em; border-radius: 4px; }

/* ============================================================
   Zones de page (cf. maquettes) : la page (portrait 3/4) contient une grille cols×rows.
   TOUTES les cartes d'une grille ont LA MÊME TAILLE, dérivée d'une unité commune :
     --carte-l = grand côté (1024)  ·  --carte-c = petit côté (768) = l × 768/1024
   horizontal = l × c   ·   vertical = c × l   ·   carré / rond = c × c
   L'unité est relative à la largeur de la grille (container query). */
.album-grille {
  flex: 1; min-height: 0;
  display: grid;
  grid-template-columns: repeat(var(--cols, 3), 1fr);
  grid-template-rows: repeat(var(--rows, 3), 1fr);
  gap: .55rem;
  container-type: inline-size;
  --carte-l: 29cqw;
  --carte-c: calc(29cqw * 768 / 1024);
}
/* Grille de la VUE ALBUM : verrouillée sur tout le canevas de la page (mêmes bords que le
   fond). SANS marge ni écart → les cases sont des fractions EXACTES de la page (largeur/cols
   × hauteur/rows), et l'unité --carte-l = 29cqw = 29 % pile de la largeur de page (le
   content-box de la grille = la page entière) : position/taille des cartes exactement
   calculables et identiques à toute résolution, indépendantes de l'entête/textes (en
   surimpression). Les cartes sont centrées dans leur case. */
.album-grille--page {
  position: absolute; inset: 0; z-index: 1;
  /* Marges haut/bas : les cases ne recouvrent plus le haut/bas du fond de page (titre,
     décor). 5,5 % de la hauteur de page de chaque côté. */
  top: 5.5%; bottom: 5.5%;
  flex: none;
  gap: 0; padding: 0;
}
.album-emplacement { position: relative; min-width: 0; min-height: 0; }

/* --- Tailles uniformes des cartes (et cadres) dans la grille --- */
.album-grille .carte--horizontal { width: var(--carte-l); height: var(--carte-c); }
.album-grille .carte--vertical   { width: var(--carte-c); height: var(--carte-l); }
.album-grille .carte--carre,
.album-grille .carte--rond       { width: var(--carte-c); height: var(--carte-c); }

/* Modèles « ronds + texte » (zt_rond_texte*) : grille à 6 colonnes, 2 unités [2 ronds empilés
   + carte texte] par ligne. On réduit l'unité de carte pour que les ronds (768×768) tiennent
   dans une colonne (1/6 de page) avec un peu de marge ; les zones texte (2×2) remplissent leur
   case. Doit suivre .album-grille pour surcharger --carte-l/--carte-c (même spécificité). */
.album-grille--rt { --carte-l: 19cqw; --carte-c: calc(19cqw * 768 / 1024); }
/* Ces modèles (ronds + texte) démarrent leurs cases dès la 1re rangée : on décale la grille
   vers le bas pour laisser une bande en haut où le titre (surimpression .album-page-entete)
   ne chevauche plus les cases. Le décalage réduit seulement la hauteur (les cartes sont en
   cqw = largeur), donc leur taille est inchangée. Sans effet en admin (grille non positionnée). */
.album-grille--rt { top: 8%; }

/* Modèles thématiques denses : on réduit --carte-l pour que les cartes (et pièces de puzzle,
   dimensionnées par les mêmes variables) tiennent dans des cases plus courtes.
   --serre  : 6 rangées (Modèle 12, équipe) → case ≈ 22 % de hauteur, carte verticale ≤ 21cqw.
   --serre5 : 5 rangées (Modèle 13, vedette + fiches) → case ≈ 27 % de hauteur.
   Doit suivre .album-grille pour surcharger --carte-l/--carte-c (même spécificité). */
/* (calibrés pour la hauteur utile de page : 100 % − 2×5,5 % de marges ≈ 118,7 cqw) */
.album-grille--serre  { --carte-l: 19.3cqw; --carte-c: calc(19.3cqw * 768 / 1024); }
.album-grille--serre5 { --carte-l: 23cqw;   --carte-c: calc(23cqw * 768 / 1024); }
.album-grille--serre4 { --carte-l: 28.8cqw; --carte-c: calc(28.8cqw * 768 / 1024); }   /* Modèle 16 : 4 col., rangée de cartes = 6/24 de la hauteur utile */
/* En admin la grille garde un écart entre cases : avec 24 rangées il doit rester ténu,
   sinon les 23 gouttières mangent toute la hauteur (la vue album, elle, reste à gap 0). */
.album-grille--admin.album-grille--serre4 { gap: .12rem; }
/* Modèle 18 (planche « identité » d'une sélection) : 3 colonnes, rangées de 8/36 de la hauteur
   utile ≈ 26,4 cqw → carte verticale 25 cqw, qui laisse un peu d'air dans chaque case. */
.album-grille--selection { --carte-l: 25cqw; --carte-c: calc(25cqw * 768 / 1024); }
.album-grille--admin.album-grille--selection { gap: .08rem; }

/* Dispositions PERSONNALISÉES (concepteur) : la grille peut avoir un nombre libre de
   colonnes/lignes → l'unité de carte est calculée pour tenir dans UNE case, quel que soit
   le découpage (page portrait 3/4 : hauteur = 133.33 cqw ; --cols/--rows posés par app.js). */
.album-grille--perso {
  /* hauteur utile de page = 133,33 cqw − marges haut/bas → 118,6 cqw */
  --carte-l: calc(min(100cqw / var(--cols, 3), 118.6cqw / var(--rows, 3)) * .92);
  --carte-c: calc(var(--carte-l) * 768 / 1024);
}

/* --- Zone libre : contenu centré --- */
.zone--libre { display: flex; align-items: center; justify-content: center; overflow: hidden; }

/* --- Cartes rondes : cercle EN HAUT, légende SOUS le cercle. Le cercle est borné en hauteur
   pour toujours laisser la place au nom/description (sinon, quand la carte remplit la case,
   la légende débordait sous la case et était rognée par overflow:hidden). --- */
.zone-rond { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: .3rem; width: 100%; height: 100%; }
.zone-rond:has(.carte-sous-legende) { justify-content: flex-start; }
/* Cercle dimensionné par la HAUTEUR (définie) → toujours de la place pour la légende dessous ;
   width:auto + aspect-ratio garde un cercle parfait. Plafonné à la taille de carte standard
   (--carte-c) et à 72 % de la case pour réserver ~28 % au nom/description. */
.zone-rond > .card.carte--rond {
  flex: 0 0 auto;
  height: min(var(--carte-c, 21.75cqw), 72%);
  width: auto; max-width: 100%;
  aspect-ratio: 1 / 1;
}
/* Rond : RIEN sur l'image (ni nom, ni description, ni numéro) — tout est affiché SOUS le
   cercle (.carte-sous-legende). !important + spécificité pour battre les règles génériques
   qui remettent la légende en display:flex. Vaut aussi en zoom. */
.card.carte--rond .carte-ui .carte-legende,
.card.carte--rond .carte-ui .carte-numero { display: none !important; }
.carte-sous-legende {
  display: flex; flex-direction: column; gap: .1rem; text-align: center;
  max-width: 100%; padding: 0 .25rem; min-height: 0; overflow: hidden;
}
/* Nom sous le cercle : MÊME taille (cqw, relative à la page) que le nom des autres formats. */
.carte-sous-nom  { font-weight: 600; font-size: max(7.2px, 1.6cqw); color: var(--color-heading); }
.carte-sous-desc { font-size: max(7px, 1.85cqw); line-height: 1.25; color: var(--color-muted); overflow: hidden; }
/* Dans l'album (non zoomé), on n'affiche QUE le nom sous le rond — la description n'apparaît
   qu'au zoom, comme pour les autres formats. */
.zone-rond .carte-sous-desc { display: none; }

/* Cadre au format de la carte pour les états non collés (placeholder / bouton coller) */
.zone-cadre { position: relative; display: flex; }
.zone-cadre.fmt-horizontal { width: var(--carte-l); height: var(--carte-c); }
.zone-cadre.fmt-vertical   { width: var(--carte-c); height: var(--carte-l); }
.zone-cadre.fmt-carre,
.zone-cadre.fmt-rond       { width: var(--carte-c); height: var(--carte-c); }
.zone-cadre > .emplacement-vide, .zone-cadre > .form-coller { width: 100%; height: 100%; }
.zone-cadre.fmt-rond .emplacement-vide { border-radius: 50%; }

/* Texte Markdown d'une zone libre : aucun fond propre (fond de page visible). */
.zone-texte {
  width: 100%; height: 100%; overflow-y: auto;
  /* Marges généreuses à gauche/droite : le texte ne colle pas aux bords de la case
     ni aux cases voisines. Padding (pas margin) car le bloc fait width:100% —
     une marge horizontale le ferait déborder puis clipper par la case. */
  padding: .35em 3cqw; margin: 5px 0;
  /* proportionnel à la page (cf. .album-page-texte) */
  font-size: max(7px, 1.7cqw); line-height: 1.4; text-align: left;
  display: flex; flex-direction: column; justify-content: flex-start; /* défaut : haut */
}
/* Alignement vertical du texte dans sa case (directive [haut]/[centre]/[bas]). */
.zone-texte.zone-v-haut   { justify-content: flex-start; }
.zone-texte.zone-v-centre { justify-content: center; }
.zone-texte.zone-v-bas    { justify-content: flex-end; }
/* Espaceur : une ligne vide « supplémentaire » du Markdown = un saut vertical. */
.md-espace { height: 1.15em; flex: 0 0 auto; }
.zone-texte > :first-child { margin-top: 0; }
.zone-texte > :last-child { margin-bottom: 0; }

/* Débordement d'une boîte de texte/média dans l'album — zones libres ET blocs de texte de niveau
   page (fond_texte haut/milieu/bas) : on clippe (pas de défilement), on pose une pastille
   « [suite] » en bas à droite ; un clic ouvre le texte complet en zoom (SANS rotation,
   contrairement aux cartes-images). Hors album (admin), le scroll reste possible. */
.zone-texte, .page-bande-texte { position: relative; }
.album-face .zone-texte,
.album-face .album-page-texte,
.album-face .page-bande-texte { overflow: hidden; }
.boite-deborde { cursor: pointer; }
/* Un contenu qui déborde se lit depuis le DÉBUT : on force l'alignement en haut même si la case
   est paramétrée centrée / en bas (sinon le texte « commence trop haut », coupé par le haut). */
.zone-texte.boite-deborde, .page-bande-texte.boite-deborde { justify-content: flex-start; }
.boite-deborde::after {
  content: "[suite]";
  position: absolute; right: 3px; bottom: 2px;
  font-family: var(--font-body);
  /* proportionnelle à la page, comme le texte qu'elle complète */
  font-size: max(8px, 1.65cqw); font-weight: 700; line-height: 1.35;
  color: #fff; background: var(--accent);
  padding: 0 .35em; border-radius: 6px;
  pointer-events: auto; cursor: pointer;
  box-shadow: 0 1px 4px rgba(0, 0, 0, .35);
}
/* Panneau de zoom d'une boîte de texte/média : plein texte, défilable, aucune inclinaison. */
.zoom-texte {
  max-width: min(760px, 92vw); max-height: 86vh; overflow-y: auto;
  background: var(--color-card); color: var(--color-text);
  border-radius: var(--radius); padding: 1.6rem 1.9rem;
  box-shadow: 0 30px 90px -20px rgba(0, 0, 0, .85);
  font-family: var(--font-body); font-size: 1.02rem; line-height: 1.6;
  text-align: left; cursor: auto;
}
.zoom-texte > :first-child { margin-top: 0; }
.zoom-texte > :last-child { margin-bottom: 0; }
.zoom-texte img, .zoom-texte video { max-width: 100%; height: auto; }
.zoom-texte .zone-media { width: 100%; }
.zone-texte h1, .zone-texte h2, .zone-texte h3 { margin: .4em 0 .25em; font-size: 1.15em; }
.zone-texte p, .zone-texte ul, .zone-texte ol { margin: .4em 0; }
.zone-texte ul, .zone-texte ol { padding-left: 1.4em; }
.zone-texte img { max-width: 100%; height: auto; border-radius: 6px; }

/* --- Zone puzzle : morceaux conjoints (aucun espace), coins arrondis sur l'ensemble.
   La grille interne fait EXACTEMENT pcols × prows morceaux au format imposé par le
   modèle (pfmt-vertical : morceaux 768×1024 ; pfmt-horizontal : 1024×768). --- */
.zone--puzzle { overflow: visible; display: flex; align-items: center; justify-content: center; }
.zone-puzzle-grille {
  display: grid;
  grid-template-columns: repeat(var(--pcols, 1), 1fr);
  grid-template-rows: repeat(var(--prows, 1), 1fr);
  gap: 0;
  /* Bloc de cartes JOINTES (puzzle) : morceaux internes à angle DROIT (jonctions nettes),
     mais léger arrondi du CONTOUR extérieur (overflow:hidden clippe seulement les 4 coins
     de l'ensemble). Même rayon que les cartes individuelles, pour la cohérence. */
  border-radius: clamp(6px, 2.2cqw, 20px); overflow: hidden;
}
.zone-puzzle-grille.pfmt-vertical {
  width: calc(var(--pcols, 1) * var(--carte-c));
  height: calc(var(--prows, 1) * var(--carte-l));
}
.zone-puzzle-grille.pfmt-horizontal {
  width: calc(var(--pcols, 1) * var(--carte-l));
  height: calc(var(--prows, 1) * var(--carte-c));
}
.zone-piece { position: relative; min-width: 0; min-height: 0; }
/* .card.carte pour battre en spécificité la règle générale des coins (.card.carte:not(.carte--rond)). */
.zone-piece .card.carte { width: 100%; height: 100%; aspect-ratio: auto; --card-radius: 0; }
.zone-piece .emplacement-vide { width: 100%; height: 100%; border-radius: 0; }
.zone-piece .form-coller { width: 100%; height: 100%; }
/* Photo continue, sans trace d'assemblage :
   - pas de fond dégradé sous les morceaux (aux échelles non entières, l'antialiasing du
     bord de chaque photo laissait transparaître ~1px du dégradé clair → lignes/ombres
     « haut-gauche / bas-droite » qui trahissaient les coutures) ;
   - la photo déborde de 1px pour souder les bords entre morceaux ;
   - pas de calque parallaxe par morceau (il bougerait indépendamment). */
.zone-piece .carte-fond { background: none; }
/* Image d'un morceau : remplit EXACTEMENT sa case (object-fit: fill, inset 0) — comme le
   masque du groupe (composite en « fill » sur les mêmes cases). Ainsi image et masque se
   superposent au pixel près, quels que soient leurs ratios natifs (avec « cover » ils se
   décalent dès que le ratio d'un morceau ≠ celui de sa case). */
.zone-piece .carte-image { inset: 0; width: 100%; height: 100%; object-fit: fill; }
.zone-piece .carte-calque { display: none; }

/* --- Un groupe conjoint se comporte comme UNE SEULE carte : la rotation 3D est portée
   par la grille entière, et une couche d'effet unique (.groupe-effet, shine/glare du CSS
   officiel, rareté du 1er morceau) recouvre tout l'assemblage. Les rotations et effets
   individuels des morceaux sont désactivés. Variables posées par app.js sur la grille. --- */
.zone--puzzle { perspective: 900px; }
.zoom-overlay { perspective: 1400px; }
.zone-puzzle-grille {
  position: relative;
  isolation: isolate;
  transform: rotateY(var(--rotate-x, 0deg)) rotateX(var(--rotate-y, 0deg));
  will-change: transform;
}
.zone-puzzle-grille:not(.interacting) { transition: transform .5s ease; }
.zone-puzzle-grille:not(.interacting) .groupe-effet .card__shine,
.zone-puzzle-grille:not(.interacting) .groupe-effet .card__glare { transition: opacity .4s ease; }

/* Morceaux : AUCUN effet individuel (opacité d'effet forcée à 0, shine/glare masqués,
   rotation et ombre supprimées) — sinon on voit les coutures entre les images. */
.zone-puzzle-grille .zone-piece .card { --card-opacity: 0 !important; }
.zone-puzzle-grille .zone-piece .card__shine,
.zone-puzzle-grille .zone-piece .card__glare { display: none !important; }
.zone-puzzle-grille .zone-piece .card__rotator { transform: none !important; box-shadow: none !important; }

/* Couche d'effet UNIQUE du groupe : holo + halo INDÉPENDANTS DU RATIO (le CSS officiel est
   calibré pour une carte portrait et produit une bande décalée sur un panorama). Pilotée par
   les variables du pointeur posées sur la grille (--pointer-*, --background-*, --card-opacity). */
.groupe-effet {
  position: absolute; inset: 0; z-index: 5;
  pointer-events: none; overflow: hidden; border-radius: inherit;
  /* base.css applique aspect-ratio: var(--card-aspect) (0.718) à TOUT .card → la couche
     d'effet serait contrainte à un ratio portrait au lieu de remplir la boîte du groupe,
     ce qui décale le shine/masque par rapport aux images. On neutralise ici (cascade à tout
     le sous-arbre : shine, glare, cases masquées). */
  --card-aspect: auto;
  aspect-ratio: auto;
  /* raretés officielles sur un groupe : découpes portrait neutralisées, coins droits */
  --card-radius: 0;
  --clip: inset(0); --clip-invert: inset(0);
  --clip-stage: inset(0); --clip-stage-invert: inset(0);
  --clip-trainer: inset(0); --clip-borders: inset(0);
}
/* Filet de sécurité : aucune contrainte de ratio sur les couches d'effet du groupe. */
.groupe-effet .card__shine, .groupe-effet .card__glare { aspect-ratio: auto; }
.groupe-effet .card__shine::before,
.groupe-effet .card__shine::after,
.groupe-effet .card__glare::after { width: 100%; height: 100%; }
.groupe-effet .card__shine,
.groupe-effet .card__glare {
  position: absolute; inset: 0;
  border-radius: 0;
}
/* Shine tempéré : à pleine opacité, le color-dodge crame les photos claires et masque
   l'image ; on plafonne pour que l'image reste toujours lisible sous l'effet. */
.groupe-effet .card__shine { opacity: calc(var(--card-opacity, 0) * .5); }
.groupe-effet .card__glare { opacity: calc(var(--card-opacity, 0) * .8); }
/* Holo du groupe : recette « rare holo » OFFICIELLE (bandes + scanlines fusionnées en
   overlay, puis le filtre assombrissant de base.css), indépendante du ratio — sans le
   clip-path portrait-only. Couleurs = variables --red/--violet… de cards.css (héritées
   via la classe .card de la couche). */
.groupe-effet:not([data-rarity]) .card__shine {
  background-image:
    repeating-linear-gradient(110deg,
      var(--violet), var(--blue), var(--green), var(--yellow), var(--red),
      var(--violet), var(--blue), var(--green), var(--yellow), var(--red),
      var(--violet), var(--blue), var(--green), var(--yellow), var(--red)),
    repeating-linear-gradient(90deg, black 0px, black 2px, #666 2px, #666 4px);
  background-position:
    calc(((50% - var(--background-x, 50%)) * 2.6) + 50%) calc(((50% - var(--background-y, 50%)) * 3.5) + 50%),
    center center;
  background-size: 400% 400%, cover;
  background-blend-mode: overlay;
  filter: brightness(.85) contrast(2.75) saturate(.65);
  mix-blend-mode: color-dodge;
}
/* Halo du groupe : formule officielle, paramétrée comme les cartes (--halo-k / --halo-r).
   Uniquement en repli : une rareté configurée fournit son propre glare. */
.groupe-effet:not([data-rarity]) .card__glare {
  background-image: radial-gradient(
    var(--halo-r, 150%) var(--halo-r, 150%) at var(--pointer-x, 50%) var(--pointer-y, 50%),
    hsla(0, 0%, 100%, calc(.8 * var(--halo-k, 1))) 10%,
    hsla(0, 0%, 100%, calc(.65 * var(--halo-k, 1))) 20%,
    hsla(0, 0%, 0%, calc(.5 * var(--halo-k, 1))) 90%);
  mix-blend-mode: overlay;
}

/* Interface des morceaux (numéro, nom, description) : masquée dans l'album et le zoom —
   les badges « #31 #32 … » et bandeaux par morceau trahissent l'assemblage. Le nom
   s'affiche au niveau du GROUPE (pastille compacte). En ADMIN, on garde les numéros
   pour identifier les emplacements. */
.zone-piece .carte-ui { display: none !important; }
.album-grille--admin .zone-piece .carte-ui { display: block !important; }
.zone-piece .carte-legende { display: none !important; }
.groupe-legende {
  position: absolute; left: .6rem; bottom: .6rem; z-index: 6;
  max-width: 75%;
  padding: .3rem .75rem; border-radius: 14px;
  background: rgba(0, 0, 0, .6); color: #fff;
  /* même taille relative à la page (cqw) que le nom des cartes */
  font-size: max(7.2px, 1.6cqw); font-weight: 600;
  pointer-events: none;
  display: flex; flex-direction: column; gap: .15rem;
}
.groupe-legende-desc { display: none; font-weight: 400; font-size: .78rem; color: rgba(255,255,255,.92); }

/* Fiche technique d'un groupe conjoint : recopiée par app.js depuis le morceau qui la
   porte (la .carte-ui des morceaux est masquée, cf. plus haut). Mêmes règles que la fiche
   d'une carte, à ceci près que les cqw se mesurent ici sur la largeur du GROUPE : on les
   divise par --pcols pour retrouver la taille qu'aurait la fiche sur UN morceau. */
.groupe-fiche {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 7;
  container-type: inline-size;
  display: flex; flex-direction: column; text-align: left;
  padding: calc(2cqw / var(--pcols, 1)) calc(3cqw / var(--pcols, 1)) calc(2.4cqw / var(--pcols, 1));
  max-height: calc(25% / var(--prows, 1)); overflow: hidden;
  background: var(--cadre, rgba(255, 255, 255, .96));
  color: var(--cadre-texte, #1a2330);
  box-shadow: 0 -2px 10px rgba(0, 0, 0, .16);
  pointer-events: none;
}
.groupe-fiche .carte-fiche-nom   { display: block; font-weight: 800; font-size: calc(4.8cqw / var(--pcols, 1)); line-height: 1; color: inherit; }
.groupe-fiche .carte-fiche-nom--riche   { font-weight: 500; }
.groupe-fiche .carte-fiche-nom--riche b { font-weight: 900; }
.groupe-fiche .carte-fiche-latin { display: block; font-style: italic; font-size: calc(3.3cqw / var(--pcols, 1)); line-height: 1.05; color: inherit; opacity: .82; margin-top: calc(.3cqw / var(--pcols, 1)); }
.groupe-fiche .carte-fiche-stats { display: flex; flex-wrap: wrap; gap: calc(.3cqw / var(--pcols, 1)) calc(2.4cqw / var(--pcols, 1)); margin-top: calc(.8cqw / var(--pcols, 1)); font-size: calc(3.3cqw / var(--pcols, 1)); line-height: 1.12; }
.groupe-fiche .fiche-stat        { display: inline; white-space: nowrap; color: inherit; }
.groupe-fiche .fiche-stat b      { font-weight: 700; color: inherit; margin-right: .2em; }
.groupe-fiche .carte-desc-zoom   { display: none; }
/* Quand un groupe a les DEUX, la pastille de nom remonte au-dessus de la fiche. */
.groupe-legende:has(~ .groupe-fiche) { bottom: calc(0.6rem + 25% / var(--prows, 1)); }
.zoom-overlay .groupe-legende-desc { display: block; font-size: .95rem; line-height: 1.4; }
.zoom-overlay .groupe-legende { font-size: 1.15rem; padding: .5rem 1.1rem; left: 1rem; bottom: 1rem; }

.emplacement-vide {
  border-radius: 12px;
  border: 2px dashed var(--color-border);
  display: flex; align-items: center; justify-content: center;
  color: var(--color-border); font-size: 1.6rem;
  background: repeating-linear-gradient(45deg, #ffffff40 0 8px, #FAFBFD85 8px 16px);
}
.emplacement-vide.emplacement-vide--dispo {
  border-color: var(--accent); color: var(--accent); cursor: pointer; font-family: inherit;
  position: relative; overflow: hidden; flex-direction: column; padding: 0;
}
/* Glisser-déposer des cartes (grille admin) : carte en cours de glisse + case cible. */
.carte-en-glisse { opacity: .35; }
.album-grille--admin .drop-cible {
  outline: 3px dashed var(--accent); outline-offset: 3px; border-radius: 10px;
  background: color-mix(in srgb, var(--accent) 10%, transparent);
}

/* Case non collée « riche » : numéro, puis nom/description selon le réglage de la carte. */
.emplacement-vide--infos {
  flex-direction: column; gap: .1em; padding: 6%; overflow: hidden; text-align: center;
  font-size: max(7px, 1.7cqw); line-height: 1.25;
}
.emplacement-vide--infos .empl-numero { font-size: 1.5em; font-weight: 800; }
.emplacement-vide--infos .empl-nom    { font-weight: 700; color: var(--color-muted); }
.emplacement-vide--infos .empl-desc   { font-size: .88em; color: var(--color-muted); opacity: .85; }
.emplacement-apercu { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; padding: 10%; opacity: .35; }
.emplacement-coller-texte {
  position: relative; z-index: 1; background: var(--accent); color: #fff;
  font-size: .75rem; font-weight: 700; padding: .3rem .6rem; border-radius: 999px;
}
.form-coller { width: 100%; height: 100%; }
.form-coller button { width: 100%; height: 100%; }
.album-page-vide { display: flex; align-items: center; justify-content: center; height: 100%; color: var(--color-muted); font-style: italic; }
.emplacement-actions { display: flex; gap: .35rem; margin-top: .4rem; justify-content: center; flex-wrap: wrap; }
.emplacement-actions form { display: inline; }
.form-ajout-carte { scroll-margin-top: 80px; }
.modifier-carte-mise-en-page { display: flex; gap: 2rem; flex-wrap: wrap; }
.modifier-carte-apercu { width: 180px; flex-shrink: 0; }
.modifier-carte-form { flex: 1; min-width: 260px; }
.calques-grille { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 1.25rem; }
.calque-tuile { text-align: center; }
.calque-apercu {
  width: 100%; aspect-ratio: 5/7; object-fit: cover; border-radius: 10px;
  background: repeating-conic-gradient(#e5e7eb 0% 25%, #fff 0% 50%) 0 0 / 16px 16px;
  margin-bottom: .4rem;
}

/* Barre du bas : navigation + « Aller à » centrés, icônes d'affichage à droite. Les trois
   colonnes de largeur égale gardent le centre VRAIMENT centré quel que soit le groupe de
   droite ; sous 900 px la barre s'empile et le groupe d'affichage repasse au centre. */
.album-nav {
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: center;
  gap: .6rem 1rem; margin-top: 1rem;
}
.album-nav-centre { display: flex; align-items: center; justify-content: center; gap: 1rem; flex-wrap: wrap; }
.album-nav-cote { display: flex; align-items: center; }
.album-nav-cote--fin { justify-content: flex-end; }
.album-nav-info { color: var(--color-muted); font-size: .9rem; }

/* « Aller à » : le libellé s'efface, la valeur sélectionnée fait office d'indicateur de page. */
.album-saut select { max-width: 22rem; }
.album-saut-libelle { color: var(--color-muted); }

/* Choix du mode d'affichage : segments compacts, l'actif en foncé, les autres en gris. */
.album-affichage {
  display: inline-flex; align-items: center; gap: .15rem; padding: .15rem;
  background: var(--color-card); border: 1px solid var(--color-border); border-radius: 10px;
}
.album-affichage-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 2rem; height: 1.9rem; padding: 0; border: none; border-radius: 8px;
  background: transparent; color: var(--color-muted); cursor: pointer;
  transition: background .15s ease, color .15s ease;
}
.album-affichage-btn:hover { background: var(--accent-doux); color: var(--color-text); }
.album-affichage-btn.actif { background: var(--accent-doux); color: var(--color-heading); }
.album-affichage-btn.actif svg { stroke-width: 2.1; }

@media (max-width: 900px) {
  .album-nav { grid-template-columns: 1fr; justify-items: center; }
  .album-nav-cote { display: none; }
  .album-nav-cote--fin { display: flex; justify-content: center; }
}

/* ── Barre du haut de l'album ───────────────────────────────*/
.album-barre-groupe { display: flex; align-items: center; gap: .5rem .8rem; flex-wrap: wrap; }
.album-retour { font-size: .9rem; white-space: nowrap; }

/* Sélecteur d'album : présenté comme un bouton, pour se lire comme une action. */
.album-choix {
  display: inline-flex; align-items: center; gap: .35rem;
  background: #fff; border: 1px solid var(--color-border); border-radius: 999px;
  padding: .15rem .6rem .15rem .7rem;
}
.album-choix:hover { border-color: var(--accent-bord); }
.album-choix-icone { font-size: .95rem; }
.album-choix select {
  border: none; background: transparent; font: inherit; font-size: .9rem; font-weight: 600;
  color: var(--color-heading); padding: .25rem 0; max-width: 16rem; cursor: pointer;
}

/* « Ma pioche » : action principale de la barre, avec le nombre de cartes en attente. */
.btn-pioche { display: inline-flex; align-items: center; gap: .45rem; white-space: nowrap; }
.btn-pioche-badge {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 1.4rem; height: 1.4rem; padding: 0 .35rem; border-radius: 999px;
  background: rgba(255,255,255,.28); font-size: .8rem; font-weight: 700;
}

/* En-tête de la pioche : le titre et « ouvrir des paquets » sur la même ligne. */
.pioche-entete { display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap; }
.pioche-entete .titre { margin-bottom: 0; }

/* Animation de tourne-page : une FEUILLE à deux faces pivote autour de la reliure. Le RECTO
   montre la page qui s'en va, le VERSO la nouvelle page du côté opposé qui arrive → on voit
   bien l'ancienne page disparaître ET la nouvelle apparaître de l'autre côté (app.js échange
   le contenu sous la feuille à mi-parcours). */
.album-page-tournante {
  position: absolute; inset: 0; z-index: 10;
  transform-origin: left center;
  transform-style: preserve-3d;
  transition: transform .8s cubic-bezier(.36,.1,.2,1);
  will-change: transform;
}
.album-page-tournante.tourne-arriere { transform-origin: right center; }
.album-page-tournante.en-cours { transform: rotateY(-180deg); }
.album-page-tournante.tourne-arriere.en-cours { transform: rotateY(180deg); }
.album-page-tournante .tourne-recto,
.album-page-tournante .tourne-verso {
  position: absolute; inset: 0;
  backface-visibility: hidden;
  background: var(--color-card); border-radius: 6px; overflow: hidden;
  box-shadow: 0 0 40px -6px rgba(0,0,0,.45);
}
.album-page-tournante .tourne-verso { transform: rotateY(180deg); }
/* léger dégradé d'ombre du papier qui se courbe */
.album-page-tournante .tourne-recto::after,
.album-page-tournante .tourne-verso::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(90deg, rgba(0,0,0,.22), rgba(0,0,0,0) 55%, rgba(255,255,255,.18));
}

/* ── Pioche (cartes non collées) ────────────────────────────*/
.album-entete-actions { display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap; }
.pioche-liste { display: grid; grid-template-columns: repeat(auto-fill, minmax(130px, 1fr)); gap: 1rem; }
.pioche-item { display: flex; flex-direction: column; gap: .4rem; align-items: center; }
.pioche-quantite { font-size: .78rem; color: var(--color-muted); }

@media (max-width: 720px) {
  .album-reliure { flex-direction: column; }
  .album-face { border-left: none; border-top: 1px solid var(--color-border); }
  .album-face:first-child { border-top: none; }
}

/* Aperçu de l'image de fond dans l'éditeur de page (admin) */
.apercu-fond-page { display: block; max-width: 260px; width: 100%; border-radius: 8px; margin-bottom: .5rem; border: 1px solid var(--color-border); }
.case-inline { display: flex; align-items: center; gap: .4rem; font-weight: 400; margin-bottom: .5rem; }
.case-inline input { width: auto; }

/* Sécurité admin (MFA / TOTP) + connexion */
.profil-tuile-statique { cursor: default; }
.profil-tuile-statique:hover .profil-avatar { transform: none; box-shadow: none; }
.liste-statut { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: .4rem; }
.qr-zone { display: inline-block; padding: .6rem; background: #fff; border: 1px solid var(--color-border); border-radius: 10px; margin: .5rem 0; }
.qr-zone img, .qr-zone canvas { display: block; }
.totp-cle { display: inline-block; font-size: 1.05rem; letter-spacing: .12em; background: rgba(0,0,0,.06); padding: .35rem .6rem; border-radius: 6px; margin-top: .3rem; }

/* ── Laboratoire d'effets (admin) ─────────────────────────── */
.labo { display: grid; grid-template-columns: 320px 1fr; gap: 1.5rem; align-items: start; }
@media (max-width: 760px) { .labo { grid-template-columns: 1fr; } }
.labo-apercu { position: sticky; top: calc(var(--topbar-h) + 1rem); }
.labo-apercu .carte { max-width: 320px; margin: 0 auto; }
.labo-astuce { text-align: center; color: var(--color-muted); font-size: .82rem; margin-top: .6rem; }
.labo-grille-reglages { display: grid; grid-template-columns: 1fr 1fr; gap: .6rem 1rem; margin: .5rem 0 1rem; }
@media (max-width: 520px) { .labo-grille-reglages { grid-template-columns: 1fr; } }
.labo-grille-reglages label { display: flex; flex-direction: column; gap: .2rem; font-size: .8rem; font-weight: 600; }
.labo-grille-reglages output { color: var(--accent); font-weight: 700; font-variant-numeric: tabular-nums; }
.labo-grille-reglages input[type="range"] { width: 100%; }
.labo-palette { display: flex; gap: .4rem; flex-wrap: wrap; }
.labo-palette input[type="color"] { width: 40px; height: 34px; padding: 0; border: 1px solid var(--color-border); border-radius: 6px; background: none; cursor: pointer; }
#sortie { font-family: ui-monospace, monospace; font-size: .78rem; }

/* ── Galerie d'effets (admin) ─────────────────────────────── */
.galerie-effets { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 1.6rem; margin-top: 1rem; }
.galerie-item { display: flex; flex-direction: column; align-items: center; gap: .5rem; }
.galerie-item .carte { width: 100%; max-width: 210px; }
.galerie-cle { font-size: .78rem; color: var(--color-muted); }

/* Vignettes sources (image / masque / foil) dans la galerie d'effets */
.galerie-source { width: 100%; max-width: 210px; aspect-ratio: 3/4; object-fit: cover; border-radius: 12px; border: 1px solid var(--color-border); }
.galerie-source--damier { background: repeating-conic-gradient(#e9edf3 0 25%, #fff 0 50%) 0 0 / 20px 20px; }

/* ── Grille d'admin des zones (page non contrainte : rangées automatiques) ── */
.album-grille--admin { flex: none; grid-template-rows: none; grid-auto-rows: auto; gap: .9rem .6rem; }
.zone-boite { width: 100%; }
/* Le ratio réel de CHAQUE zone (libre, puzzle, multi) est posé via data-ratio par le JS,
   calculé par ratioZoneAdmin() sur la géométrie du modèle : le 1/1 ci-dessous n'est qu'un
   repli avant l'exécution du JS (la CSP interdit un style inline côté serveur). */
.zone-boite.zone--libre {
  aspect-ratio: 1 / 1;
  background: rgba(37,99,235,.06); border: 1px dashed var(--color-border); border-radius: 12px;
}
.zone-boite.zone--puzzle { /* ratio posé via data-ratio (JS) */ }
.zone-vide-actions { display: flex; gap: .6rem; }
.zone-btn-rond {
  width: 52px; height: 52px; border-radius: 50% !important;
  font-size: 1.2rem; text-decoration: none;
  display: flex; align-items: center; justify-content: center;
}

/* ── Aperçus des modèles de page (formulaire « Ajouter une page ») ── */
.dispo-choix { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 1rem; }
.dispo-option { display: flex; flex-direction: column; gap: .4rem; align-items: center; cursor: pointer; position: relative; }
.dispo-option input { position: absolute; opacity: 0; pointer-events: none; }
.dispo-apercu {
  width: 100%; aspect-ratio: 3 / 4;
  background: #F6D84A; border-radius: 10px; padding: 7%;
  display: flex; flex-direction: column; gap: 4%;
  border: 3px solid transparent; transition: border-color .15s ease, box-shadow .15s ease;
}
.dispo-option input:checked ~ .dispo-apercu { border-color: var(--accent); box-shadow: 0 6px 18px -8px var(--accent); }
.dispo-option input:focus-visible ~ .dispo-apercu { outline: 2px solid var(--accent-dark); }
.dispo-barre { height: 6.5%; background: #1a1a1a; border-radius: 2px; flex-shrink: 0; }
.dispo-mini {
  flex: 1; min-height: 0;
  display: grid;
  grid-template-columns: repeat(var(--cols, 3), 1fr);
  grid-template-rows: repeat(var(--rows, 3), 1fr);
  gap: 4%;
  container-type: inline-size;
  --carte-l: 31cqw;
  --carte-c: calc(31cqw * 768 / 1024);
}
.dispo-zone { border-radius: 4px; min-width: 0; min-height: 0; }
.dispo-zone--libre { background: #45AEDE; }
.dispo-zone--puzzle { display: flex; align-items: center; justify-content: center; background: transparent; }
.dispo-zone--puzzle .zone-puzzle-grille { border-radius: 4px; gap: 2px; background: #7CC74F; }
.dispo-piece { background: #7CC74F; min-width: 0; min-height: 0; }
.dispo-nom { font-size: .78rem; text-align: center; line-height: 1.25; }

/* ── Zoom sur une carte (album) : tilt + reflets dans l'overlay ── */
.zoom-overlay {
  position: fixed; inset: 0; z-index: 300;
  background: rgba(10, 12, 20, .82);
  display: flex; align-items: center; justify-content: center;
}
/* Ombre portée UNIQUEMENT sur l'élément zoomé lui-même (carte seule ou grille de groupe) —
   surtout pas sur les morceaux : chaque pièce projetterait son ombre sur ses voisines et
   dessinerait des bandes sombres le long des jointures. */
.zoom-overlay > .carte,
.zoom-overlay > .zone-puzzle-grille { box-shadow: 0 30px 90px -20px rgba(0,0,0,.85); }
.zoom-overlay .zone-piece .carte { box-shadow: none !important; }
/* Zoom : la carte s'affiche à sa taille native (1024 × 768 / 768 × 1024 / 768 × 768),
   réduite seulement si la fenêtre est plus petite. */
.zoom-overlay > .carte--horizontal { width: min(1024px, 92vw, calc(92vh * 1024 / 768)); }
.zoom-overlay > .carte--vertical   { width: auto; height: min(1024px, 92vh, calc(92vw * 1024 / 768)); }
.zoom-overlay > .carte--carre,
.zoom-overlay > .carte--rond       { width: min(768px, 85vw, 85vh); }
/* Carte ronde agrandie AVEC sa légende dessous (nom/description hors du cercle). */
.zoom-rond { display: flex; flex-direction: column; align-items: center; gap: 1rem; max-width: 92vw; }
.zoom-rond > .card.carte--rond { width: min(620px, 84vw, 74vh); box-shadow: 0 30px 90px -20px rgba(0,0,0,.85); }
.zoom-rond-legende { color: #fff; text-align: center; max-width: min(620px, 84vw); }
.zoom-rond-legende .carte-sous-nom  { font-size: 1.5rem; font-weight: 700; color: #fff; }
.zoom-rond-legende .carte-sous-desc { font-size: 1rem; line-height: 1.4; color: rgba(255,255,255,.9); }
/* Textes lisibles à la taille native */
/* ── Zoom : la légende passe SOUS la carte (cf. app.js) ────────────────────────────────
   · le bloc dessiné sur l'image est masqué, sinon le texte apparaîtrait deux fois ;
   · une carte MASQUÉE perd son ombre portée : l'ombre suit le rectangle de l'image et
     trahissait les bords (coins arrondis d'un logo, par exemple) alors que le masque, lui,
     ne laisse voir que la forme utile. */
/* Le sélecteur doit battre « .card.carte .carte-ui .carte-legende » (4 classes) qui affiche
   le bloc sur l'image : on repasse donc par la même chaîne, préfixée du zoom. */
.zoom-overlay .card.carte.carte--zoom-legende-dessous .carte-ui .carte-legende { display: none; }
/* Une carte MASQUÉE n'a PLUS D'OMBRE PORTÉE, ni dans l'album ni au zoom, et même au survol :
   l'ombre suit le rectangle de l'image alors que le masque ne laisse voir que la forme utile
   (un écusson, un logo…) — elle dessinait donc un carré fantôme autour du blason. Elle vient
   de .card__rotator (pack pokemon-cards-css), qui la remet au survol via .interacting, autant
   que de nos propres règles de zoom : on la coupe partout. */
.card.carte-masquee,
.card.carte-masquee .card__translater,
.card.carte-masquee .card__rotator,
.card.carte-masquee.interacting .card__rotator,
.zoom-avec-desc > .carte.carte-masquee,
.zoom-rond > .card.carte-masquee { box-shadow: none; }
/* Le panneau de légende reprend la largeur de la carte pour les formats non ronds. */
.zoom-avec-desc > .zoom-rond-legende { max-width: min(760px, 90vw); text-align: center; }

.zoom-overlay .carte-legende { padding: .9rem 1rem; gap: .35rem; }
.zoom-overlay .carte-legende-nom  { font-size: 1.25rem; }
.zoom-overlay .carte-legende-desc { font-size: 1rem; line-height: 1.45; max-height: 9em; }
.zoom-overlay .carte-numero { font-size: 1rem; padding: .2rem .7rem; }
.zoom-fermer {
  position: absolute; top: 1rem; right: 1.2rem;
  width: 44px; height: 44px; border-radius: 50%; border: none;
  background: rgba(255,255,255,.18); color: #fff; font-size: 1.3rem; cursor: pointer;
}
.zoom-fermer:hover { background: rgba(255,255,255,.32); }

/* Flèches de navigation du zoom : image précédente / suivante de la même double page (ou de
   la même grille de vignettes). Elles doublent les touches ←/→ et se placent AU-DESSUS du
   contenu zoomé, collées aux bords — jamais par-dessus l'image, qui reste centrée. */
.zoom-nav {
  position: absolute; top: 50%; transform: translateY(-50%); z-index: 2;
  width: 48px; height: 72px; border: none; border-radius: 10px;
  background: rgba(255,255,255,.14); color: #fff;
  font-size: 2rem; line-height: 1; cursor: pointer;
}
.zoom-nav:hover  { background: rgba(255,255,255,.32); }
.zoom-nav--prec  { left: .6rem; }
.zoom-nav--suiv  { right: .6rem; }
@media (max-width: 640px) {
  .zoom-nav { width: 38px; height: 56px; font-size: 1.5rem; }
}

/* Alignement des blocs Markdown (syntaxe -> texte <- / -> texte ->) */
.md-centre { text-align: center; }
.md-droite { text-align: right; }

/* Bande de séparation (--- / *** / ___ sur une ligne) → <hr class="md-hr"> */
.md-hr {
  border: 0; height: max(2px, .35cqw); margin: .9em 0;
  background: linear-gradient(90deg, transparent, currentColor, transparent);
  opacity: .45;
  flex: 0 0 auto;   /* .zone-texte est un flex column : sans ça la barre (3px) est écrasée par flex-shrink et devient invisible hors zoom */
}
/* Surlignage <mark> : fond jaune autour du texte */
mark {
  background: #fff3a0; color: inherit;
  padding: 0 .18em; border-radius: .18em;
  box-decoration-break: clone; -webkit-box-decoration-break: clone;
}
/* Texte coloré : <rouge>…</rouge>, <bleu>…</bleu>, etc. (classes, pas de style inline / CSP) */
.md-c-rouge  { color: #e03131; }
.md-c-orange { color: #e8590c; }
.md-c-jaune  { color: #f08c00; }
.md-c-vert   { color: #2f9e44; }
.md-c-bleu   { color: #1971c2; }
.md-c-violet { color: #7048e8; }
.md-c-rose   { color: #e64980; }
.md-c-gris   { color: #868e96; }

/* ── Banque de cartes (admin) ─────────────────────────────── */
.banque-grille { display: grid; grid-template-columns: repeat(auto-fill, minmax(170px, 1fr)); gap: 1.2rem; }
.banque-item { display: flex; flex-direction: column; gap: .45rem; }
.banque-item .carte { max-width: 100%; }
.banque-statut { font-size: .74rem; color: var(--color-muted); }
.banque-statut--place { color: var(--color-success); font-weight: 600; }

/* Sélection multiple dans la banque : barre d'actions au-dessus de la grille, une case à
   cocher par vignette. Les cases sont rattachées au formulaire de la barre par form=, un
   <form> ne pouvant pas en contenir d'autres. */
.banque-barre {
  display: flex; flex-wrap: wrap; align-items: center; gap: .8rem;
  margin-bottom: 1rem; padding: .55rem .8rem;
  background: var(--accent-doux); border: 1px solid var(--accent-bord); border-radius: var(--radius);
}
.banque-tout { display: inline-flex; align-items: center; gap: .4rem; font-size: .85rem; cursor: pointer; }
.banque-compte { font-size: .8rem; color: var(--color-muted); margin-right: auto; }
.banque-choix { display: inline-flex; align-items: center; gap: .35rem; font-size: .74rem; color: var(--color-muted); cursor: pointer; }
.banque-choix input { cursor: pointer; }
/* La vignette cochée se distingue nettement dans une grille dense. */
.banque-item:has(.banque-choix input:checked) {
  outline: 2px solid var(--color-error); outline-offset: 4px; border-radius: 8px;
}

/* Panneau d'assignation d'une carte à un emplacement */
.assigner-liste { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 1rem; }
.assigner-item { display: flex; flex-direction: column; gap: .4rem; align-items: stretch; }
.assigner-item button { width: 100%; }

/* Crédit effets holographiques */
.credit-holo { font-size: .75rem; color: var(--color-muted); text-align: center; margin-top: 1rem; }
.credit-holo a { color: var(--color-muted); }

/* ── Bords cliquables du livre : page précédente / suivante ── */
.album-bord {
  position: absolute; top: 0; bottom: 0; width: var(--bord-l);
  border: none; z-index: 20; cursor: pointer;
  background: transparent; color: transparent;
  font-size: 3rem; line-height: 1; font-family: inherit;
  display: flex; align-items: center; justify-content: center;
  transition: background .2s ease, color .2s ease;
}
.album-bord--gauche { left: 0; border-radius: 10px 0 0 10px; }
.album-bord--droite { right: 0; border-radius: 0 10px 10px 0; }
.album-bord--gauche:hover { color: #fff; background: linear-gradient(to right, rgba(0,0,0,.35), transparent); }
.album-bord--droite:hover { color: #fff; background: linear-gradient(to left, rgba(0,0,0,.35), transparent); }
.album-bord[disabled] { display: none; }

/* Sélecteur d'effet d'un groupe conjoint (admin) */
.groupe-effet-form { display: flex; align-items: center; gap: .4rem; width: 100%; margin: .4rem 0 .2rem; }
.groupe-effet-form label { font-size: .78rem; font-weight: 600; white-space: nowrap; }
.groupe-effet-form select { flex: 1; padding: .3rem .5rem; border: 1px solid var(--color-border); border-radius: 8px; font-size: .82rem; }

/* ── Édition : barre d'actions + navigation entre pages ── */
.edition-actions { display: flex; gap: .5rem; flex-wrap: wrap; }
.edition-nav-pages {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  margin: .2rem 0 1rem; flex-wrap: wrap;
}
.edition-nav-pages .btn[aria-disabled="true"] { opacity: .4; pointer-events: none; }

/* ── Liste des pages : réordonnancement par glisser-déposer ── */
.table-pages tbody tr { cursor: default; }
.poignee-glisser { cursor: grab; color: var(--color-muted); font-size: 1.2rem; user-select: none; width: 1.5rem; text-align: center; }
.table-pages tr.en-glisse { opacity: .45; background: var(--color-success-bg); }
.page-numero-cell { font-weight: 700; color: var(--color-heading); }

/* ── Lecteurs média dans les zones/textes Markdown ── */
.zone-media { display: block; width: 100%; margin: .5rem 0; border-radius: 10px; }
.zone-audio { height: 40px; }
.zone-video { max-height: 60vh; background: #000; }

/* ============================================================
   Modèles à zones sur 3 rangées (z1…z6) — layout en BANDES (page 1200×1600) :
   grille de 3 colonnes × 6 lignes (bande 133 / rangée 400 / bande / rangée / bande /
   rangée). Les zones et les bandes de texte sont posées par grid-column/grid-row (data-gcol
   / data-grow appliqués en JS, CSP oblige). Un puzzle qui s'étend sur 2 rangées traverse la
   bande du milieu (span 3 lignes). Les blocs de texte ne chevauchent plus les cases.
   ============================================================ */
.page-bandes {
  position: absolute; inset: 0; z-index: 1;
  /* Mêmes marges haut/bas que la grille générique : le fond de page reste dégagé. */
  top: 5.5%; bottom: 5.5%;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: 133fr 400fr 133fr 400fr 133fr 400fr;   /* = 1200×1600 : 133/400/… */
  container-type: inline-size;                                /* pour cqw des cartes */
  --carte-l: 29cqw;                                           /* grand côté = 348px @1200 */
  --carte-c: calc(29cqw * 768 / 1024);                        /* petit côté = 261px @1200 */
}
.page-bande-texte {
  overflow: hidden; min-width: 0; min-height: 0;
  display: flex; flex-direction: column; justify-content: center;
  padding: 0 4cqw;
  /* proportionnel à la page (cf. .album-page-texte) */
  font-size: max(7px, 1.75cqw); line-height: 1.4;
  text-shadow: 0 1px 2px rgba(255,255,255,.75);
}
.page-bande-texte > :first-child { margin-top: 0; }
.page-bande-texte > :last-child  { margin-bottom: 0; }
.page-bande-texte h1, .page-bande-texte h2, .page-bande-texte h3 { margin: .25em 0; line-height: 1.15; }
.page-bandes .album-emplacement { position: relative; min-width: 0; min-height: 0; }
.page-bandes .zone--libre { display: flex; align-items: center; justify-content: center; overflow: hidden; }
/* Cartes centrées dans une case 400×400 : carré/rond utilisent le GRAND côté (348). */
.page-bandes .carte--horizontal,
.page-bandes .zone-cadre.fmt-horizontal { width: var(--carte-l); height: var(--carte-c); }
.page-bandes .carte--vertical,
.page-bandes .zone-cadre.fmt-vertical   { width: var(--carte-c); height: var(--carte-l); }
.page-bandes .carte--carre,
.page-bandes .carte--rond,
.page-bandes .zone-cadre.fmt-carre,
.page-bandes .zone-cadre.fmt-rond       { width: var(--carte-l); height: var(--carte-l); }

/* ============================================================
   Couverture / dos — l'album démarre FERMÉ et s'ouvre au clic
   ============================================================ */
.album-couverture {
  position: absolute; top: 0; bottom: 0; left: 50%;
  transform: translateX(-50%);
  aspect-ratio: 3 / 4;
  border-radius: 6px 12px 12px 6px; overflow: hidden;
  display: flex; align-items: flex-end;
  background: linear-gradient(135deg, var(--accent), var(--accent-dark));
  color: #fff; z-index: 30;
  box-shadow: 0 24px 60px -20px rgba(0,0,0,.6), inset 10px 0 18px -10px rgba(0,0,0,.5);
  transform-origin: left center;
  transition: transform .65s cubic-bezier(.4,.05,.2,1), opacity .5s ease, visibility .5s;
  backface-visibility: hidden;
  container-type: inline-size;
}
.album-couverture--avant { cursor: pointer; }
.album-couv-img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.album-couv-voile { position: absolute; inset: 0; background: linear-gradient(0deg, rgba(0,0,0,.1), rgba(0,0,0,.05) 55%, rgba(0,0,0,.1)); }
.album-couverture:not(.a-image) .album-couv-voile { background: none; }
.album-couv-texte { position: relative; z-index: 1; width: 100%; padding: 2rem 1.5rem; text-align: center; }
.album-couv-titre { color: #fff; font-size: clamp(1.3rem, 4cqw, 2.4rem); margin: 0 0 .3rem; text-shadow: 0 2px 12px rgba(0,0,0,.55); }
.album-couv-theme, .album-couv-desc { margin: 0 0 1rem; opacity: .92; }
.album-couv-hint { display: inline-block; padding: .5rem 1.1rem; border-radius: 999px; background: rgba(255,255,255,.22); font-weight: 600; font-size: .9rem; }
.album-couverture--avant:hover .album-couv-hint { background: rgba(255,255,255,.36); }
.album-couv-hint--fin { background: rgba(255,255,255,.14); cursor: default; }

/* États : on bascule en opacité/visibilité (transition possible) plutôt qu'en display. */
.album-livre .album-couverture { opacity: 0; visibility: hidden; pointer-events: none; }
.album-livre[data-etat="ferme-avant"]   .album-couverture--avant,
.album-livre[data-etat="ferme-arriere"] .album-couverture--arriere { opacity: 1; visibility: visible; pointer-events: auto; transform: translateX(-50%); }
/* Couverture masquée = pivotée « ouverte » (effet livre qui s'ouvre) */
.album-livre[data-etat="ouvert"] .album-couverture--avant,
.album-livre[data-etat="ferme-arriere"] .album-couverture--avant { transform: translateX(-50%) perspective(1800px) rotateY(-105deg); }
.album-livre[data-etat="ouvert"] .album-couverture--arriere,
.album-livre[data-etat="ferme-avant"] .album-couverture--arriere { transform: translateX(-50%) perspective(1800px) rotateY(105deg); transform-origin: right center; }
/* Reliure : cachée (mais garde sa place → hauteur du livre stable) quand l'album est fermé. */
.album-livre[data-etat="ferme-avant"]   .album-reliure,
.album-livre[data-etat="ferme-arriere"] .album-reliure { opacity: 0; visibility: hidden; pointer-events: none; }
.album-livre[data-etat="ferme-avant"]   .album-bord,
.album-livre[data-etat="ferme-arriere"] .album-bord { display: none; }
.album-reliure { transition: opacity .5s ease .1s; }

/* ============================================================
   Administration — navigation contextuelle et hub
   ============================================================ */

/* Fil d'ariane : Administration › Collection › Page courante */
.fil-ariane {
  display: flex; align-items: center; gap: .45rem; flex-wrap: wrap;
  font-size: .84rem; color: var(--color-muted);
  margin: .2rem 0 .4rem;
}
.fil-ariane a { color: var(--color-muted); text-decoration: none; }
.fil-ariane a:hover { color: var(--accent); text-decoration: underline; }
.fil-ariane .fil-sep { opacity: .55; user-select: none; }
.fil-ariane .fil-courant { color: var(--color-heading); font-weight: 600; }

/* Entête d'écran admin : titre + actions à droite */
.admin-entete {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 1rem; flex-wrap: wrap; margin-bottom: .35rem;
}
.admin-entete h1 { margin: 0; }
.admin-entete-actions { display: flex; gap: .5rem; flex-wrap: wrap; }

/* Onglets d'une collection : Pages | Cartes | Paquets | Paramètres */
.onglets {
  display: flex; gap: .25rem; flex-wrap: wrap;
  border-bottom: 2px solid var(--color-border);
  margin: .8rem 0 1.25rem;
}
.onglet {
  padding: .55rem 1rem .5rem;
  border-radius: 10px 10px 0 0;
  text-decoration: none; color: var(--color-muted);
  font-weight: 600; font-size: .92rem;
  border-bottom: 2px solid transparent; margin-bottom: -2px;
  transition: color .12s ease, background .12s ease;
}
.onglet:hover { color: var(--color-heading); background: var(--accent-doux); }
.onglet.actif { color: var(--accent); border-bottom-color: var(--accent); }

/* Hub : grille de tuiles de collections */
.tuiles-admin {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: 1.1rem; margin-top: 1rem;
}
.tuile-collection {
  background: var(--color-card); border: 1px solid var(--color-border);
  border-radius: var(--radius); padding: 1.1rem 1.2rem 1rem;
  display: flex; flex-direction: column; gap: .55rem;
  box-shadow: var(--ombre-1);
  transition: box-shadow .15s ease, transform .15s ease, border-color .15s ease;
}
.tuile-collection:hover { box-shadow: var(--ombre-2); transform: translateY(-2px); border-color: var(--accent-bord); }
.tuile-collection-titre { display: flex; align-items: center; gap: .6rem; }
.tuile-collection-titre h3 { margin: 0; font-size: 1.12rem; }
.tuile-collection-titre a { text-decoration: none; color: var(--color-heading); }
.tuile-collection-titre a:hover { color: var(--accent); }
.pastille-couleur { width: 12px; height: 12px; border-radius: 50%; background: var(--accent); flex: none; }
.tuile-collection-theme { color: var(--color-muted); font-size: .85rem; margin: -.35rem 0 0; }
.tuile-stats { display: flex; gap: .4rem; flex-wrap: wrap; }
.stat-puce {
  display: inline-flex; align-items: center; gap: .3rem;
  background: color-mix(in srgb, var(--color-bg) 70%, #fff);
  border: 1px solid var(--color-border);
  border-radius: 999px; padding: .18rem .65rem;
  font-size: .78rem; color: var(--color-muted); font-weight: 600;
}
.stat-puce b { color: var(--color-heading); font-weight: 700; }
.tuile-collection-actions {
  display: flex; gap: .4rem; flex-wrap: wrap; align-items: center;
  margin-top: .25rem; padding-top: .7rem; border-top: 1px solid var(--color-border);
}
.tuile-collection-actions .a-droite { margin-left: auto; }

/* Panneau repliable natif (création, options avancées…) — sans JavaScript. */
details.panneau-pliable {
  background: var(--color-card); border: 1px solid var(--color-border);
  border-radius: var(--radius); margin-bottom: 1.25rem;
  box-shadow: var(--ombre-1);
}
details.panneau-pliable > summary {
  cursor: pointer; list-style: none;
  padding: 1rem 1.4rem;
  font-family: var(--font-heading); font-weight: 700; font-size: 1.05rem;
  color: var(--color-heading);
  display: flex; align-items: center; gap: .6rem;
  border-radius: var(--radius);
}
details.panneau-pliable > summary::-webkit-details-marker { display: none; }
details.panneau-pliable > summary::before {
  content: "▸"; color: var(--accent); transition: transform .15s ease;
}
details.panneau-pliable[open] > summary::before { transform: rotate(90deg); }
details.panneau-pliable > summary:hover { background: var(--accent-doux); }
details.panneau-pliable[open] > summary { border-radius: var(--radius) var(--radius) 0 0; border-bottom: 1px solid var(--color-border); }
details.panneau-pliable > .panneau-corps { padding: 1.25rem 1.4rem; }

/* ============================================================
   Concepteur de page (admin/pages/concepteur.php)
   ============================================================ */
.conteneur--large { max-width: 1400px; }

.concepteur {
  display: grid;
  grid-template-columns: 250px minmax(0, 1fr) 280px;
  gap: 1.1rem;
  align-items: start;
}
@media (max-width: 980px) { .concepteur { grid-template-columns: 1fr; } }

/* Palette de blocs */
.concept-palette h2, .concept-props h2 { margin: 0 0 .7rem; font-size: 1.05rem; }
.concept-palette h2 + .concept-palette-note { margin-top: -.4rem; }
.concept-palette-note { font-size: .78rem; color: var(--color-muted); margin: .5rem 0; }
.concept-palette-bloc {
  display: flex; align-items: center; gap: .6rem;
  padding: .5rem .6rem; margin-bottom: .45rem;
  border: 1px solid var(--color-border); border-radius: 10px;
  background: color-mix(in srgb, var(--color-bg) 45%, #fff);
  font-size: .84rem; font-weight: 600; cursor: grab;
  transition: border-color .12s ease, box-shadow .12s ease;
}
.concept-palette-bloc:hover { border-color: var(--accent-bord); box-shadow: var(--ombre-1); }
.concept-palette-bloc:active { cursor: grabbing; }
.concept-mini { width: 26px; height: 26px; border-radius: 5px; flex: none; }
.concept-mini--libre  { background: #45AEDE; }
.concept-mini--puzzle { background: #7CC74F; }
.concept-mini--large      { width: 40px; }
.concept-mini--tres-large { width: 54px; }
.concept-mini--haut       { height: 38px; }
.concept-mini--carre      { width: 34px; height: 34px; }
.concept-grille-reglages { display: flex; gap: .8rem; }
.concept-grille-reglages label {
  display: flex; flex-direction: column; gap: .25rem;
  font-size: .8rem; font-weight: 600;
}
.concept-grille-reglages input { width: 70px; padding: .4rem .5rem; border: 1px solid var(--color-border); border-radius: 8px; }

/* Scène : la page portrait 3/4 */
.concept-scene { display: flex; justify-content: center; }
.concept-page {
  width: min(100%, 560px); aspect-ratio: 3 / 4;
  background: #fff; border: 1px solid var(--color-border);
  border-radius: 10px; box-shadow: var(--ombre-2);
  padding: 10px;
}
#conceptZone { position: relative; width: 100%; height: 100%; }
.concept-cellule {
  position: absolute; box-sizing: border-box;
  border: 1px dashed color-mix(in srgb, var(--color-border) 80%, #9aa);
}
.concept-cellule.apercu-ok { background: color-mix(in srgb, var(--accent) 16%, #fff); }
.concept-cellule.apercu-ko { background: var(--color-error-bg); }

/* Blocs posés */
.concept-bloc {
  position: absolute; box-sizing: border-box; z-index: 2;
  border-radius: 8px; cursor: grab;
  display: flex; align-items: center; justify-content: center;
  border: 2px solid rgba(0,0,0,.18);
  padding: 4px; overflow: hidden;
}
.concept-bloc:active { cursor: grabbing; }
.concept-bloc--libre  { background: #45AEDE; }
.concept-bloc--puzzle { background: #eef5ea; }
.concept-bloc.selection { outline: 3px solid var(--color-heading); outline-offset: 2px; z-index: 3; }
.concept-bloc-label {
  position: absolute; left: 4px; right: 4px; bottom: 3px;
  font-size: .66rem; font-weight: 700; text-align: center; color: #fff;
  text-shadow: 0 1px 2px rgba(0,0,0,.45);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  pointer-events: none;
}
.concept-bloc--puzzle .concept-bloc-label { color: #2f5d1f; text-shadow: none; }
.concept-bloc-pgrille {
  display: grid; gap: 3px; width: 88%; height: 78%;
  pointer-events: none;
}
.concept-bloc-pgrille span { background: #7CC74F; border-radius: 4px; }

/* Panneau de propriétés */
.concept-panneau-vide { color: var(--color-muted); font-size: .85rem; }
.concept-prop-titre { font-weight: 700; margin: 0 0 .6rem; font-size: .9rem; }
.concept-prop-ligne {
  display: flex; align-items: center; gap: .45rem;
  margin-bottom: .55rem; font-size: .84rem;
}
.concept-prop-ligne span:first-child { flex: 1; }
.concept-prop-ligne select { padding: .3rem .4rem; border: 1px solid var(--color-border); border-radius: 8px; }
.concept-props .form-actions { margin-top: 1rem; }

/* ============================================================
   Raretés : halo autour des cartes révélées lors d'un tirage
   ============================================================ */
.carte-rarete--peu_commune .carte-face--recto { box-shadow: 0 0 18px 2px rgba(69, 174, 222, .55); }
.carte-rarete--rare        .carte-face--recto { box-shadow: 0 0 22px 4px rgba(147, 51, 234, .6); }
.carte-rarete--legendaire  .carte-face--recto {
  box-shadow: 0 0 26px 6px rgba(255, 190, 60, .75);
  animation: halo-legendaire 1.6s ease-in-out infinite alternate;
}
@keyframes halo-legendaire {
  from { box-shadow: 0 0 18px 4px rgba(255, 190, 60, .55); }
  to   { box-shadow: 0 0 34px 10px rgba(255, 190, 60, .9); }
}
@media (prefers-reduced-motion: reduce) {
  .carte-rarete--legendaire .carte-face--recto { animation: none; }
}

/* ── Découpeur d'image-puzzle (éditeur de page admin) ─────── */
.decouper-form { display: flex; align-items: center; gap: .5rem; margin-top: .4rem; flex-wrap: wrap; }
.decouper-form small { color: var(--color-muted); font-size: .74rem; }
.decouper-label { position: relative; overflow: hidden; cursor: pointer; }
.decouper-fichier { position: absolute; inset: 0; opacity: 0; cursor: pointer; }

/* ── Aperçu Markdown en direct (éditeurs admin) ───────────── */
.apercu-md {
  margin-top: .45rem;
  border: 1px dashed var(--color-border); border-radius: 10px;
  background: #fff; padding: .6rem .75rem;
  min-height: 2.4rem; max-height: 280px; overflow-y: auto;
  container-type: inline-size;
}
.apercu-md .zone-texte { height: auto; overflow: visible; margin: 0; padding: 0; }
.apercu-md-vide { color: var(--color-muted); font-size: .8rem; font-style: italic; margin: 0; }

/* ── Aperçu réel du concepteur ────────────────────────────── */
.concept-scene { flex-direction: column; align-items: center; gap: .6rem; }
.concept-bascule { display: flex; gap: .4rem; }
.concept-bascule .btn.actif { border-color: var(--accent); color: var(--accent); background: var(--accent-doux); }
.concept-apercu { padding: 0; overflow: hidden; }
.concept-apercu .album-face { width: 100%; border: none; }

/* Mini-aperçu de disposition dans un tableau (page des modèles) */
.dispo-mini--table { width: 64px; aspect-ratio: 3 / 4; display: grid; }

/* ============================================================
   Complétion : badges page/album + félicitations
   ============================================================ */
/* Trophée sur la couverture (album complet). */
.album-couv-trophee {
  position: absolute; top: 4cqw; right: 4cqw; z-index: 2;
  font-size: 9cqw; filter: drop-shadow(0 3px 6px rgba(0,0,0,.45));
  animation: trophee-pulse 2.2s ease-in-out infinite alternate;
}
@keyframes trophee-pulse { from { transform: scale(1); } to { transform: scale(1.12) rotate(-4deg); } }
@media (prefers-reduced-motion: reduce) { .album-couv-trophee { animation: none; } }

/* Accueil : tuile de collection complète. */
.collection-trophee { font-size: 1rem; }
.collection-progres--complet > span { background: linear-gradient(90deg, #f0b428, #ffd45e, #f0b428); }

/* Overlay de félicitation one-shot (page/album qui vient d'être complété). */
.felicitation-overlay {
  position: fixed; inset: 0; z-index: 120;
  display: flex; align-items: center; justify-content: center;
  background: rgba(13, 20, 40, .55);
  opacity: 0; transition: opacity .3s ease;
}
.felicitation-overlay.visible { opacity: 1; }
.felicitation-boite {
  background: var(--color-card); border-radius: 18px;
  padding: 2rem 2.4rem; max-width: min(420px, 88vw); text-align: center;
  box-shadow: var(--ombre-3);
  transform: translateY(14px) scale(.96); transition: transform .3s ease;
}
.felicitation-overlay.visible .felicitation-boite { transform: none; }
.felicitation-emoji { font-size: 3.4rem; line-height: 1; animation: felicitation-saut .7s ease; }
@keyframes felicitation-saut {
  0% { transform: scale(.3) rotate(-15deg); } 60% { transform: scale(1.25); } 100% { transform: scale(1); }
}
@media (prefers-reduced-motion: reduce) { .felicitation-emoji { animation: none; } }
.felicitation-boite h2 { margin: .5rem 0 .3rem; }
.felicitation-boite p { color: var(--color-muted); margin: 0 0 1.2rem; }

/* ============================================================
   Zones MULTI : plusieurs cartes indépendantes dans une tuile
   (motif « pile » = empilées ; « cercle » = anneau + centre)
   ============================================================ */
.zone--multi { display: flex; align-items: center; justify-content: center; overflow: hidden; }
.zone-multi { position: relative; width: 100%; height: 100%; }

/* — pile : n cartes empilées verticalement, chacune dans sa bande — */
.zone-multi--pile { display: flex; flex-direction: column; gap: 2%; padding: 2% 0; }
.zone-multi--pile .multi-slot {
  flex: 1 1 0; min-height: 0;
  display: flex; align-items: center; justify-content: center;
}
/* Les cartes d'un slot multi sont dimensionnées par leur bande (plus par --carte-l). */
.zone-multi--pile .multi-slot .card.carte {
  width: auto; height: 100%; max-width: 100%;
  aspect-ratio: var(--card-aspect, 1024 / 768);
}
.zone-multi--pile .multi-slot > .emplacement-vide,
.zone-multi--pile .multi-slot > .form-coller {
  height: 100%; width: auto; aspect-ratio: 1024 / 768; max-width: 100%;
}

/* — cercle : slots positionnés en px par app.js (positionnerZonesMulti) — */
.zone-multi--cercle .multi-slot { position: absolute; }
.zone-multi--cercle .multi-slot .card.carte { width: 100%; height: 100%; aspect-ratio: auto; }
.zone-multi--cercle .multi-slot > .emplacement-vide,
.zone-multi--cercle .multi-slot > .form-coller { width: 100%; height: 100%; }
/* Ronds de l'anneau : carte découpée en cercle, aucune légende sur l'image. */
.multi-slot--rond .card.carte { --card-radius: 50%; }
.multi-slot--rond .card.carte .carte-ui .carte-legende,
.multi-slot--rond .card.carte .carte-ui .carte-numero { display: none !important; }
.multi-slot--rond > .emplacement-vide,
.multi-slot--rond > .form-coller > .emplacement-vide { border-radius: 50%; }

/* ── Concepteur : tuiles multi (pile / cercle) ────────────── */
.concept-mini--pile {
  background: repeating-linear-gradient(180deg, #45AEDE 0 6px, #fff 6px 8px);
  border: 1px solid #2b83ab; border-radius: 5px;
}
.concept-mini--cercle {
  width: 32px; height: 32px; border-radius: 50%;
  border: 3px dotted #45AEDE; background: #45AEDE content-box;
  padding: 7px;
}
.concept-bloc--multi { background: #dcebf7; }
.concept-bloc-pile {
  display: flex; flex-direction: column; gap: 4%;
  width: 86%; height: 74%; pointer-events: none;
}
.concept-bloc-pile span { flex: 1 1 0; background: #45AEDE; border-radius: 4px; }
.concept-bloc-cercle { position: absolute; inset: 6%; pointer-events: none; }
.concept-cercle-centre {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: 34%; height: 34%; background: #45AEDE; border-radius: 6px;
}
.concept-cercle-point {
  position: absolute; width: 13%; height: 13%;
  transform: translate(-50%, -50%);
  background: #45AEDE; border-radius: 50%;
}
/* Texte Markdown dans une case de pile : occupe toute la bande. */
.zone-multi--pile .multi-slot .zone-texte { margin: 0; width: 100%; height: 100%; }
/* Texte Markdown au centre d'un cercle : remplit la carte centrale. */
.zone-multi--cercle .multi-slot--centre .zone-texte { margin: 0; width: 100%; height: 100%; }
/* Mini de palette : bloc colonne (1×3) */
.concept-mini--tres-haut { height: 52px; }

/* ============================================================
   Page Succès du profil (pages/profils/succes.php)
   ============================================================ */
.succes-album { padding-bottom: 1.1rem; }
.succes-entete { display: flex; align-items: center; gap: .6rem; margin-bottom: .6rem; flex-wrap: wrap; }
.succes-entete h2 { margin: 0; }
.succes-entete .a-droite { margin-left: auto; }

.succes-medailles {
  display: flex; gap: .6rem; flex-wrap: wrap;
  margin: .9rem 0 .4rem;
}
.succes-medaille {
  display: flex; flex-direction: column; align-items: center; gap: .25rem;
  width: 108px; padding: .65rem .4rem .5rem;
  border: 1px solid var(--color-border); border-radius: 12px;
  background: color-mix(in srgb, var(--color-bg) 55%, #fff);
  text-align: center;
  filter: grayscale(1); opacity: .45;
}
.succes-medaille.obtenue {
  filter: none; opacity: 1;
  border-color: color-mix(in srgb, #f0b428 55%, var(--color-border));
  background: linear-gradient(180deg, #fff8e2, #fff);
  box-shadow: var(--ombre-1);
}
.succes-medaille-emoji { font-size: 1.7rem; line-height: 1; }
.succes-medaille-libelle { font-size: .68rem; font-weight: 600; color: var(--color-muted); line-height: 1.2; }
.succes-medaille.obtenue .succes-medaille-libelle { color: var(--color-heading); }

.succes-pages-titre { font-weight: 600; font-size: .88rem; margin: .8rem 0 .35rem; }
.succes-pages { display: flex; gap: .35rem; flex-wrap: wrap; }
.succes-page {
  font-size: .78rem; padding: .2rem .6rem; border-radius: 999px;
  border: 1px solid var(--color-border); color: var(--color-muted);
  background: #fff;
}
.succes-page.obtenue {
  color: #6b4a00; border-color: #e8c66a; font-weight: 600;
  background: linear-gradient(180deg, #ffe9a8, #ffd45e);
}
.felicitation-actions { display: flex; gap: .5rem; justify-content: center; flex-wrap: wrap; }

/* ============================================================
   Textes d'album : taille (petit/moyen/grand) et polices
   ============================================================ */
/* Polices supplémentaires auto-hébergées (font-src 'self'). */
@font-face {
  font-family: 'Caveat';
  font-style: normal; font-weight: 400 700; font-display: swap;
  src: url('../fonts/caveat-latin.woff2') format('woff2');
}
@font-face {
  font-family: 'Lora';
  font-style: normal; font-weight: 400 700; font-display: swap;
  src: url('../fonts/lora-latin.woff2') format('woff2');
}
@font-face {
  font-family: 'Courier Prime';
  font-style: normal; font-weight: 400; font-display: swap;
  src: url('../fonts/courierprime-latin.woff2') format('woff2');
}
@font-face {
  font-family: 'Courier Prime';
  font-style: normal; font-weight: 700; font-display: swap;
  src: url('../fonts/courierprime-gras-latin.woff2') format('woff2');
}

/* Défauts de l'ALBUM (classes posées sur .album-livre et la grille admin) :
   --texte-echelle multiplie la taille de base des textes, --police-zone choisit la famille. */
.texte-taille-petit  { --texte-echelle: .78; }
.texte-taille-grand  { --texte-echelle: 1.35; }
.police-texte-ronde      { --police-zone: 'Baloo 2', sans-serif; }
.police-texte-manuscrite { --police-zone: 'Caveat', cursive; }
.police-texte-elegante   { --police-zone: 'Lora', serif; }
.police-texte-machine    { --police-zone: 'Courier Prime', monospace; }

/* Application aux textes (zones libres/pile/centre, textes de page, bandes). */
.zone-texte, .album-page-texte, .page-bande-texte {
  font-family: var(--police-zone, var(--font-body));
}
.zone-texte        { font-size: max(6px, calc(1.55cqw * var(--texte-echelle, 1))); }
.album-page-texte  { font-size: max(6px, calc(1.6cqw  * var(--texte-echelle, 1))); }
.page-bande-texte  { font-size: max(6px, calc(1.6cqw  * var(--texte-echelle, 1))); }

/* Surcharges PAR TEXTE via les directives en tête de texte ([petit], [manuscrite]…). */
.zone-texte.texte-echelle-petit { --texte-echelle: .78; }
.zone-texte.texte-echelle-grand { --texte-echelle: 1.35; }
.zone-texte.police-ronde      { --police-zone: 'Baloo 2', sans-serif; }
.zone-texte.police-manuscrite { --police-zone: 'Caveat', cursive; }
.zone-texte.police-elegante   { --police-zone: 'Lora', serif; }
.zone-texte.police-machine    { --police-zone: 'Courier Prime', monospace; }
/* Caveat est visuellement petite : léger coup de pouce. */
.zone-texte.police-manuscrite, .police-texte-manuscrite .zone-texte { font-size: max(7px, calc(1.9cqw * var(--texte-echelle, 1))); }

/* ── Renommage rapide dans la banque ─────────────────────── */
.renommer-form { display: flex; gap: .3rem; margin: .35rem 0; }
.renommer-form input[type="text"] {
  flex: 1; min-width: 0;
  padding: .35rem .5rem; font-size: .82rem;
  border: 1px solid var(--color-border); border-radius: 8px;
}
.renommer-form input[type="text"]:focus { outline: none; border-color: var(--accent); }

/* ============================================================
   Cases « à coller » en SILHOUETTE (fonds transparents std/holo) :
   les hachures épousent le contour visible du PNG de la carte
   (masque alpha = l'image elle-même, posée par app.js en --empl-mask).
   Pour une image opaque, le masque couvre toute la case → rendu inchangé.
   ============================================================ */
.emplacement-vide--silhouette {
  position: relative;
  border: none;
  background: none;
  color: var(--color-heading);
}
.emplacement-vide--silhouette .empl-silhouette {
  position: absolute; inset: 0; pointer-events: none;
  background: repeating-linear-gradient(45deg, rgba(255,255,255,.5) 0 8px, rgba(250,251,253,.78) 8px 16px);
  -webkit-mask-image: var(--empl-mask);
  mask-image: var(--empl-mask);
  -webkit-mask-size: 100% 100%;
  mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
}
/* Textes lisibles sans boîte : halo blanc. */
.emplacement-vide--silhouette .empl-numero,
.emplacement-vide--silhouette .empl-nom,
.emplacement-vide--silhouette .empl-desc {
  position: relative; z-index: 1;
  text-shadow: 0 1px 3px rgba(255,255,255,.95), 0 0 10px rgba(255,255,255,.8);
}

/* ── Album masqué (hub admin) ─────────────────────────────── */
.tuile-collection--masquee { opacity: .6; }
.tuile-collection--masquee:hover { opacity: 1; }
.badge-masque {
  font-size: .7rem; font-weight: 700; color: #8a5a00;
  background: #ffe9a8; border-radius: 999px; padding: .12rem .55rem;
  white-space: nowrap;
}

/* ── Grille de cartes réutilisable hors album (améliorations 2026-08) ───────── */
.cartes-grille { display: flex; flex-wrap: wrap; gap: 1.1rem; --carte-l: 150px; --carte-c: calc(150px * 768 / 1024); }
.cartes-grille .carte--horizontal { width: var(--carte-l); height: var(--carte-c); }
.cartes-grille .carte--vertical   { width: var(--carte-c); height: var(--carte-l); }
.cartes-grille .carte--carre,
.cartes-grille .carte--rond       { width: var(--carte-c); height: var(--carte-c); }
.carte-vignette { display: flex; flex-direction: column; align-items: center; gap: .35rem; width: max-content; }
.carte-vignette--manquante .card { filter: grayscale(.7) brightness(.8); opacity: .82; }
.carte-vignette-badge { font-size: .8rem; font-weight: 700; padding: .1rem .5rem; border-radius: 999px;
    background: var(--accent, #2563eb); color: #fff; }
.carte-vignette-legende { font-size: .8rem; color: var(--muted, #64748b); text-align: center; max-width: 160px; }
.cartes-groupe-titre { margin: 1.4rem 0 .6rem; font-size: 1.05rem; }
.sous-onglets { display: flex; gap: .5rem; flex-wrap: wrap; margin-bottom: 1rem; }

/* ── Tableau de bord / classement / raretés (améliorations 2026-08) ────────── */
.stats-tuiles { display:grid; grid-template-columns:repeat(auto-fit,minmax(130px,1fr)); gap:1rem; margin:1rem 0 1.5rem; }
.stat-tuile { background:#fff; border:1px solid var(--color-border); border-radius:14px; padding:1rem; text-align:center; }
.stat-nombre { font-size:1.8rem; font-weight:800; color:var(--color-heading); }
.stat-label { font-size:.8rem; color:var(--color-muted); }
.rarete-chips { display:flex; flex-wrap:wrap; gap:.5rem; margin:.4rem 0 1rem; }
.rarete-chip { display:inline-flex; align-items:center; gap:.35rem; padding:.25rem .7rem; border-radius:999px; font-size:.82rem; font-weight:600; border:1px solid var(--color-border); }
.rarete-pastille { width:12px; height:12px; border-radius:50%; }
.rar-commune{--rc:#9ca3af;} .rar-peu_commune{--rc:#22c55e;} .rar-rare{--rc:#3b82f6;} .rar-legendaire{--rc:#f59e0b;}
.rarete-pastille { background:var(--rc,#9ca3af); }
.classement { width:100%; border-collapse:collapse; background:#fff; border:1px solid var(--color-border); border-radius:14px; overflow:hidden; }
.classement th, .classement td { padding:.6rem .8rem; text-align:left; border-bottom:1px solid var(--color-border); }
.classement th { font-size:.76rem; text-transform:uppercase; letter-spacing:.04em; color:var(--color-muted); }
.classement tr:last-child td { border-bottom:none; }
.classement .rang { font-weight:800; font-size:1.1rem; width:2.5rem; }
.classement .moi { background:var(--accent-doux); }
.classement-barre-wrap { background:var(--color-bg); border-radius:6px; overflow:hidden; height:8px; min-width:90px; }
.classement-barre { height:8px; background:var(--accent); }
/* Filtre rareté album : cartes non concernées estompées */
.carte--estompee { opacity:.22; filter:grayscale(1); transition:opacity .2s, filter .2s; }
/* Solde de pièces + cloche notifications dans le topbar */
.topbar-solde { font-weight:700; font-size:.92rem; padding:.2rem .6rem; background:var(--accent-doux); border-radius:999px; white-space:nowrap; }
.topbar-notif { position:relative; text-decoration:none; }
.topbar-notif-badge { position:absolute; top:-6px; right:-8px; background:#dc2626; color:#fff; font-size:.66rem; font-weight:700; min-width:15px; height:15px; line-height:15px; text-align:center; border-radius:999px; padding:0 3px; }
/* Récompense quotidienne (accueil) */
.recompense-carte { display:flex; align-items:center; gap:1rem; flex-wrap:wrap; background:linear-gradient(135deg,var(--accent-doux),#fff); border:1px solid var(--accent-bord); border-radius:16px; padding:1rem 1.3rem; margin-bottom:1.4rem; }
.recompense-emoji { font-size:2rem; }
.recompense-txt { flex:1; min-width:180px; }
.recompense-streak { font-weight:700; color:var(--accent); }
/* Carte du jour (accueil) */
.carte-du-jour { display:flex; align-items:center; gap:1.5rem; flex-wrap:wrap; background:#fff; border:1px solid var(--color-border); border-radius:16px; padding:1rem 1.3rem; margin-bottom:1.6rem; }
.carte-du-jour-titre { margin:0 0 .2rem; }
.carte-du-jour .cartes-grille { --carte-l:170px; --carte-c:calc(170px*768/1024); margin:0; }

/* ── Accessibilité : mouvement réduit ─────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .001ms !important; animation-iteration-count: 1 !important; transition-duration: .001ms !important; scroll-behavior: auto !important; }
  .card__translater, .card__rotator { transform: none !important; transition: none !important; }
  .card__shine, .card__glare { opacity: 0 !important; }
}

/* Bouton favori sur une vignette */
.favori-btn { background:none; border:none; cursor:pointer; font-size:1.15rem; line-height:1; padding:.1rem; filter:grayscale(1) opacity(.55); }
.favori-btn.actif { filter:none; }
.badge-pct { display:inline-block; font-size:.72rem; font-weight:800; color:var(--accent); }

/* Vignettes hors album : le texte des cartes (en cqw) doit se référer à la carte, pas au viewport */
.cartes-grille .card__front { container-type: inline-size; }
.cartes-grille .card { cursor: zoom-in; }
.carte-du-jour .card { cursor: zoom-in; }

/* Couverture d'album sur les tuiles de l'accueil */
.collection-couv { display:block; position:relative; aspect-ratio:3/4; max-height:340px; margin:-.2rem auto .7rem; border-radius:12px; overflow:hidden;
    background:linear-gradient(140deg, var(--accent), var(--accent-dark)); box-shadow:0 6px 18px -8px rgba(0,0,0,.4); width:auto; }
.collection-couv img { width:100%; height:100%; object-fit:cover; display:block; }
.collection-couv-defaut { position:absolute; inset:0; display:flex; align-items:center; justify-content:center; font-family:var(--font-heading); font-size:3rem; font-weight:800; color:rgba(255,255,255,.9); }
.collection-couv-trophee { position:absolute; top:.4rem; right:.4rem; font-size:1.4rem; filter:drop-shadow(0 1px 2px rgba(0,0,0,.5)); }

/* Sélection multiple de doubles (Mes cartes) */
.sel-label { font-size:.8rem; color:var(--color-muted); display:flex; align-items:center; gap:.3rem; cursor:pointer; }
.barre-selection { position:fixed; left:50%; transform:translateX(-50%); bottom:1.2rem; z-index:500;
    display:flex; align-items:center; gap:1rem; background:var(--color-heading); color:#fff; padding:.7rem 1.2rem; border-radius:999px; box-shadow:0 8px 30px -8px rgba(0,0,0,.5); }
.barre-selection.masquee { display:none; }
/* Lot d'échange : options radio sur les cartes offertes */
.lot-choix { display:flex; flex-wrap:wrap; gap:.6rem; margin:.4rem 0; }
.lot-option { display:flex; flex-direction:column; align-items:center; gap:.2rem; padding:.3rem; border:2px solid transparent; border-radius:10px; cursor:pointer; }
.lot-option input { margin:0; }
.lot-option:has(input:checked) { border-color:var(--accent); background:var(--accent-doux); }

/* ── Menu hamburger + navigation responsive (2026-08-20) ───────────────────── */
.topbar-burger { display: none; }
.topbar-separateur { width: 1px; align-self: center; height: 22px; background: rgba(255,255,255,.18); margin: 0 .15rem; }

@media (max-width: 1024px) {
  .topbar-burger {
    display: inline-flex; flex-direction: column; justify-content: center; gap: 4px;
    width: 40px; height: 40px; padding: 0 9px; margin-left: .1rem;
    background: rgba(255,255,255,.1); border: 0; border-radius: 9px; cursor: pointer;
  }
  .topbar-burger span { display: block; height: 2px; border-radius: 2px; background: #fff; transition: transform .2s ease, opacity .2s ease; }
  .topbar.menu-ouvert .topbar-burger span:nth-child(1) { transform: translateY(6px) rotate(45deg); }
  .topbar.menu-ouvert .topbar-burger span:nth-child(2) { opacity: 0; }
  .topbar.menu-ouvert .topbar-burger span:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }
  .topbar-notif { margin-left: auto; }
  .topbar-nav {
    position: absolute; top: 100%; left: 0; right: 0;
    flex-direction: column; align-items: stretch; gap: .1rem;
    margin: 0; padding: .5rem;
    background: var(--color-heading); box-shadow: var(--ombre-3);
    border-top: 1px solid rgba(255,255,255,.1);
    display: none; max-height: calc(100vh - var(--topbar-h)); overflow-y: auto;
  }
  .topbar.menu-ouvert .topbar-nav { display: flex; }
  .topbar-nav .topbar-lien { padding: .7rem .9rem; font-size: 1rem; border-radius: 8px; }
  .topbar-separateur { width: auto; height: 1px; margin: .3rem .4rem; }
}
@media (max-width: 560px) {
  .topbar-profil-nom { display: none; }
  .topbar-prefs { display: none; }
}

/* ── Administration des utilisateurs ───────────────────────────────────────── */
.radios-inline { display: flex; flex-wrap: wrap; gap: .4rem 1.2rem; margin: .2rem 0 .4rem; }
.msg-info { background: var(--accent-doux); color: var(--color-text); }
.utilisateurs-liste { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 1rem; margin-top: 1rem; }
.utilisateur-carte { margin: 0; }
.utilisateur-tete { display: flex; align-items: center; gap: .8rem; }
.utilisateur-avatar { width: 44px; height: 44px; border-radius: 50%; display: flex; align-items: center; justify-content: center; background: var(--accent); font-size: 1.4rem; flex: none; }
.utilisateur-nom { font-weight: 700; font-size: 1.05rem; display: flex; align-items: center; gap: .4rem; flex-wrap: wrap; }
.utilisateur-meta { font-size: .8rem; color: var(--color-muted); margin-top: .15rem; }
.badge-admin { font-size: .7rem; font-weight: 700; color: #fff; background: var(--accent); padding: .1rem .45rem; border-radius: 999px; }
.badge-moi { font-size: .7rem; font-weight: 700; color: var(--accent); background: var(--accent-doux); padding: .1rem .45rem; border-radius: 999px; }
.utilisateur-actions { margin-top: .8rem; }
.utilisateur-actions > summary { cursor: pointer; font-weight: 600; color: var(--accent); padding: .3rem 0; }
.bloc-action { padding: .8rem 0; border-top: 1px solid var(--color-border); margin-top: .6rem; }
.bloc-action:first-of-type { border-top: 0; margin-top: .3rem; }
.bloc-boutons { display: flex; flex-wrap: wrap; gap: .5rem; }
.form-retirer { margin-top: 1rem; padding-top: 1rem; border-top: 1px solid var(--color-border); }

/* ── Galerie : badge de rareté + Mes cartes : placeholder « manquante » (2026-08-20) ───────── */
.carte-vignette-infos { display: flex; align-items: center; gap: .4rem; justify-content: center; flex-wrap: wrap; }
.carte-vignette-rarete { font-size: .72rem; font-weight: 700; padding: .1rem .55rem; border-radius: 999px; white-space: nowrap;
  background: var(--rar-bg, #eef2f7); color: var(--rar-fg, #64748b); }
.rarete--commune     { --rar-bg:#eef2f7; --rar-fg:#64748b; }
.rarete--peu_commune { --rar-bg:#dcfce7; --rar-fg:#15803d; }
.rarete--rare        { --rar-bg:#dbeafe; --rar-fg:#1d4ed8; }
.rarete--legendaire  { --rar-bg:#fef3c7; --rar-fg:#b45309; }

.carte-manque {
  width: var(--carte-l, 150px); height: var(--carte-c, 112px);
  border: 2px dashed var(--color-border); border-radius: 10px;
  background: repeating-linear-gradient(45deg, transparent, transparent 8px, rgba(0,0,0,.035) 8px, rgba(0,0,0,.035) 16px), var(--accent-doux);
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: .15rem;
  text-align: center; padding: .3rem;
}
.carte-manque-num { font-size: 1.5rem; font-weight: 800; color: var(--color-muted); line-height: 1; }
.carte-manque-nom { font-size: .74rem; font-weight: 600; color: var(--color-text); line-height: 1.1; }

/* ── Échanges : carte « mystère » floutée + Mes doubles : sélection par album (2026-08-20) ─── */
.carte-floue { position:relative; overflow:hidden; border-radius:10px; background:#0b1220;
  width: var(--carte-l,104px); height: var(--carte-c, calc(104px * 768 / 1024)); box-shadow: var(--ombre-1); }
.carte-floue--vertical { width: var(--carte-c); height: var(--carte-l); }
.carte-floue--carre, .carte-floue--rond { width: var(--carte-c); height: var(--carte-c); }
.carte-floue--rond { border-radius:50%; }
.carte-floue-img { width:100%; height:100%; object-fit:cover; filter: blur(9px) brightness(.8) saturate(1.1); transform: scale(1.18); }
.carte-floue-info { position:absolute; inset:0; display:flex; flex-direction:column; align-items:center; justify-content:center;
  gap:.15rem; color:#fff; text-align:center; padding:.3rem; background:rgba(6,12,26,.30); }
.carte-floue-num { font-size:1.35rem; font-weight:800; text-shadow:0 1px 4px rgba(0,0,0,.75); line-height:1; }
.carte-floue-nom { font-size:.74rem; font-weight:700; text-shadow:0 1px 4px rgba(0,0,0,.75); line-height:1.12; }

.sel-groupe-label { font-size:.8rem; font-weight:600; color:var(--accent); margin-left:.6rem; cursor:pointer; white-space:nowrap; }
.sel-groupe-label input { vertical-align:middle; }

/* ── Ma pioche : carte cliquable → planche pour coller (2026-08-20) ─────────────────────────── */
a.pioche-item { text-decoration:none; color:inherit; cursor:pointer; border-radius:12px; padding:.45rem; transition: transform .12s ease, box-shadow .12s ease; }
a.pioche-item:hover { transform: translateY(-3px); box-shadow: var(--ombre-2); }
a.pioche-item:hover .pioche-quantite { text-decoration: underline; }
a.pioche-item .pioche-quantite { color: var(--accent); font-weight:700; }

/* ── Badge « NEW » sur une carte nouvelle au tirage (2026-08-20) ────────────────────────────── */
.badge-new {
  position:absolute; top:8px; left:8px; z-index:6;
  background:#dc2626; color:#fff; font-weight:800; font-size:.72rem; letter-spacing:.06em;
  padding:.15rem .55rem; border-radius:999px; box-shadow:0 2px 6px rgba(0,0,0,.4);
  transform: rotate(-8deg); pointer-events:none;
}

/* ══════════════════════════════════════════════════════════════════════════════════════
   OUVERTURE DE PAQUET v2 (2026-08-25) — trois temps
   1) on déchire la languette (--dechir, inchangé) ; la lumière qui fuit prend déjà la
      couleur de la MEILLEURE carte du paquet (data-tease) → pré-teasing ;
   2) le paquet tressaille puis s'ouvre en deux moitiés qui tombent, flash de lumière ;
   3) scène plein écran : les cartes sont révélées UNE PAR UNE, un halo annonce la rareté
      AVANT le retournement, et la réaction (onde, étincelles, rayons, secousse) est
      proportionnelle à la rareté. Le final ramène tout dans la grille.
   Couleur de rareté portée par --rc / --rc2 ; app.js pose data-rarete sur la scène.
   ══════════════════════════════════════════════════════════════════════════════════════ */

.tirage-scene[data-rarete="commune"],
.paquet-carte-slot[data-rarete="commune"],
.paquet-ouverture[data-tease="commune"]     { --rc: #e2e8f0; --rc2: #94a3b8; }
.tirage-scene[data-rarete="peu_commune"],
.paquet-carte-slot[data-rarete="peu_commune"],
.paquet-ouverture[data-tease="peu_commune"] { --rc: #5ec4f0; --rc2: #1d6fa5; }
.tirage-scene[data-rarete="rare"],
.paquet-carte-slot[data-rarete="rare"],
.paquet-ouverture[data-tease="rare"]        { --rc: #c084fc; --rc2: #6d28d9; }
.tirage-scene[data-rarete="legendaire"],
.paquet-carte-slot[data-rarete="legendaire"],
.paquet-ouverture[data-tease="legendaire"]  { --rc: #ffd257; --rc2: #f59e0b; }

/* Pré-teasing : la déchirure ne laisse plus fuir de lumière (le morceau arraché disparaît
   sans rien laisser derrière), --rc ne sert donc plus qu'au flash de l'éclatement. */

/* ── 2) Le paquet s'ouvre en deux ── */
.paquet-moitie { backface-visibility: hidden; will-change: transform, opacity; }
.paquet-moitie--g { position: relative; clip-path: inset(0 49.8% 0 -20%); }   /* léger recouvrement : pas de liseré au milieu */
.paquet-moitie--d { position: absolute; top: 0; left: 0; clip-path: inset(0 -20% 0 49.8%); }
.paquet-ouverture--ouvre .paquet-moitie--g { animation: paquet-moitie-g .85s cubic-bezier(.36,.06,.4,1) .38s forwards; }
.paquet-ouverture--ouvre .paquet-moitie--d { animation: paquet-moitie-d .85s cubic-bezier(.36,.06,.4,1) .38s forwards; }
@keyframes paquet-moitie-g {
  0%   { transform: none; opacity: 1; }
  18%  { transform: perspective(700px) translateX(-7%) rotateY(22deg); opacity: 1; }
  100% { transform: perspective(700px) translate(-90%, 60%) rotate(-34deg) rotateY(38deg) scale(.8); opacity: 0; }
}
@keyframes paquet-moitie-d {
  0%   { transform: none; opacity: 1; }
  18%  { transform: perspective(700px) translateX(7%) rotateY(-22deg); opacity: 1; }
  100% { transform: perspective(700px) translate(90%, 60%) rotate(34deg) rotateY(-38deg) scale(.8); opacity: 0; }
}
.paquet-flash {
  position: absolute; left: 50%; top: 50%; width: 48px; height: 48px; margin: -24px 0 0 -24px;
  border-radius: 50%; z-index: 4; opacity: 0; pointer-events: none;
  background: radial-gradient(circle, #fff 0%, var(--rc, #fff7cf) 32%, transparent 70%);
}
.paquet-ouverture--ouvre .paquet-flash { animation: paquet-flash .9s ease-out .42s forwards; }
@keyframes paquet-flash {
  0%   { opacity: 0; transform: scale(.2); }
  16%  { opacity: 1; transform: scale(5); }
  100% { opacity: 0; transform: scale(15); }
}

/* ── 3) Scène de révélation carte par carte ── */
body.tirage-actif { overflow: hidden; }
.tirage-scene {
  position: fixed; inset: 0; z-index: 400;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: .8rem; padding: 1rem; overflow: hidden; user-select: none;
  --rc: #e2e8f0; --rc2: #94a3b8;
  background: radial-gradient(ellipse at 50% 46%, rgba(30, 41, 59, .78), rgba(5, 8, 17, .97) 68%);
  backdrop-filter: blur(4px);
  animation: scene-entre .35s ease both;
}
.tirage-scene[hidden] { display: none; }
@keyframes scene-entre { from { opacity: 0; } }
.tirage-scene.tirage-scene--sortie { animation: scene-sort .45s ease forwards; }
@keyframes scene-sort { to { opacity: 0; } }
/* Une fois entrée, la scène ne doit plus rejouer son fondu (sinon la secousse le relance). */
.tirage-scene--prete { animation: none; }

/* Halo de rareté : il pulse DERRIÈRE la carte encore face cachée — c'est lui qui vend
   le suspense (« la lumière est dorée ! ») avant même le retournement. */
.tirage-halo {
  position: absolute; left: 50%; top: 47%; width: min(90vw, 720px); aspect-ratio: 1;
  transform: translate(-50%, -50%); border-radius: 50%; pointer-events: none; z-index: 1;
  background: radial-gradient(circle, var(--rc) 0%, var(--rc2) 34%, transparent 66%);                 /* repli sans color-mix() */
  background: radial-gradient(circle, color-mix(in srgb, var(--rc) 55%, transparent) 0%, color-mix(in srgb, var(--rc2) 26%, transparent) 38%, transparent 68%);
  opacity: 0; transition: opacity .5s ease;
}
.tirage-scene--attente .tirage-halo { opacity: .55; animation: halo-pulse 1.7s ease-in-out infinite; }
.tirage-scene--eclat .tirage-halo   { opacity: 1; animation: none; }
@keyframes halo-pulse {
  0%, 100% { transform: translate(-50%, -50%) scale(.86); opacity: .4; }
  50%      { transform: translate(-50%, -50%) scale(1.04); opacity: .75; }
}

/* Rayons tournants : réservés aux belles cartes (classe posée par app.js). */
.tirage-rayons {
  position: absolute; left: 50%; top: 47%; width: 150vmax; aspect-ratio: 1;
  transform: translate(-50%, -50%); pointer-events: none; z-index: 0; opacity: 0;
  background: repeating-conic-gradient(from 0deg, var(--rc) 0deg 5deg, transparent 5deg 16deg);       /* repli sans color-mix() */
  background: repeating-conic-gradient(from 0deg, color-mix(in srgb, var(--rc) 34%, transparent) 0deg 5deg, transparent 5deg 16deg);
  -webkit-mask: radial-gradient(circle, #000 8%, rgba(0,0,0,.55) 34%, transparent 62%);
  mask: radial-gradient(circle, #000 8%, rgba(0,0,0,.55) 34%, transparent 62%);
  transition: opacity .5s ease;
}
.tirage-scene--rayons .tirage-rayons { opacity: 1; animation: rayons-tournent 14s linear infinite; }
@keyframes rayons-tournent { to { transform: translate(-50%, -50%) rotate(360deg); } }

/* Onde de choc au moment exact où la carte se retourne. */
.tirage-onde {
  position: absolute; left: 50%; top: 47%; width: 220px; aspect-ratio: 1;
  transform: translate(-50%, -50%) scale(0); border-radius: 50%;
  border: 4px solid var(--rc); pointer-events: none; z-index: 2; opacity: 0;
}
.tirage-onde--joue { animation: onde .75s cubic-bezier(.15,.7,.3,1) forwards; }
@keyframes onde {
  0%   { opacity: .95; transform: translate(-50%, -50%) scale(.15); border-width: 10px; }
  100% { opacity: 0;   transform: translate(-50%, -50%) scale(3.4); border-width: 1px; }
}

/* Étincelles : app.js en crée N (selon la rareté) avec --dx/--dy/--w/--dur. */
.tirage-etincelles { position: absolute; left: 50%; top: 47%; width: 0; height: 0; pointer-events: none; z-index: 60; }
.tirage-etincelle {
  position: absolute; left: 0; top: 0; width: var(--w, 9px); height: var(--w, 9px);
  border-radius: 50%; background: var(--rc); box-shadow: 0 0 10px 2px var(--rc);
  animation: etincelle var(--dur, 1.1s) cubic-bezier(.1,.7,.3,1) forwards;
}
@keyframes etincelle {
  0%   { transform: translate(-50%, -50%) scale(.3); opacity: 1; }
  70%  { opacity: 1; }
  100% { transform: translate(-50%, -50%) translate(var(--dx, 0), var(--dy, 0)) scale(0); opacity: 0; }
}

/* La pile de cartes : la carte active est devant, les suivantes décalées derrière. */
.tirage-pile {
  position: relative; z-index: 10;
  width: min(92vw, 620px); height: min(58vh, 480px);
}
/* La TAILLE de la carte sur la pile est posée par app.js en width/height, pas par un
   scale() : un scale() étirerait une image rastérisée au gabarit de 240 px et rendrait
   photo et texte flous. Ne restent ici que le décalage et l'inclinaison de profondeur. */
.tirage-pile .paquet-carte-slot {
  position: absolute; left: 50%; top: 50%; margin: 0;
  --d: 0;
  transform: translate(-50%, -50%) translate(calc(var(--d) * 9px), calc(var(--d) * 11px))
             rotate(calc(var(--d) * 1.6deg));
  transition: transform .45s cubic-bezier(.2,.85,.3,1), opacity .4s ease,
              width .45s cubic-bezier(.2,.85,.3,1), height .45s cubic-bezier(.2,.85,.3,1);
  z-index: calc(40 - var(--d));
  /* PAS de filter ici pour assombrir les cartes du fond : un filter (même brightness(1))
     force la case en groupe de rendu aplati, ce qui casse la DÉCOUPE des cartes masquées
     (la zone noire du masque se met à réagir à la luminosité) et le backface-visibility du
     retournement. La profondeur est déjà portée par le décalage, la rotation et l'échelle. */
}
.tirage-pile .paquet-carte-slot--loin { opacity: 0; }
.tirage-pile .paquet-carte-slot--actif { cursor: pointer; }
.tirage-pile .paquet-carte-slot--actif .carte-flip-wrap { animation: carte-flotte 3.2s ease-in-out infinite; }
.tirage-pile .paquet-carte-slot--revele .carte-flip-wrap { animation: none; }
@keyframes carte-flotte {
  0%, 100% { transform: translateY(0) rotate(0); }
  50%      { transform: translateY(-9px) rotate(.5deg); }
}
/* Sortie de la carte révélée : elle s'envole vers la pile « déjà vues ». */
.tirage-pile .paquet-carte-slot--sortie { animation: carte-sort .5s ease-in forwards; }
@keyframes carte-sort {
  to { transform: translate(-50%, -50%) translate(-28vw, -26vh) rotate(-22deg) scale(.5); opacity: 0; }
}
/* Halo de rareté plus franc sur la scène que dans la grille. */
.tirage-pile .paquet-carte-slot--revele .carte-face--recto {
  box-shadow: 0 0 34px 8px var(--rc), 0 18px 40px -12px rgba(0, 0, 0, .8);                            /* repli sans color-mix() */
  box-shadow: 0 0 34px 8px color-mix(in srgb, var(--rc) 70%, transparent),
              0 18px 40px -12px rgba(0, 0, 0, .8);
}

/* ── Textes et arrondis d'une carte hors de l'album ─────────────────────────────────────
   Deux tailles de la carte sont exprimées en `cqw` : le nom de la légende (1.6cqw) et le
   rayon des coins (--card-radius: clamp(6px, 2.2cqw, 20px)). Or les cqw se mesurent sur le
   plus proche ANCÊTRE conteneur de requête — .album-face-corps dans l'album, .card__front
   dans la galerie. Sur l'écran de tirage il n'y en a AUCUN : les cqw retombaient alors sur
   le VIEWPORT, soit 1,6 % de 1920 px = 30 px pour le nom, puis × --s (jusqu'à 2,6) une fois
   la carte agrandie → 80 px à l'écran. Même dérive pour l'arrondi, plaqué à son maximum de
   20 px alors que .carte-face arrondit à 12 px : la fiche technique dépassait donc des coins
   de la carte.
   Correctif : .card__front devient conteneur de requête (comme le fait déjà
   `.cartes-grille .card__front`), tout est exprimé en cqw relatifs à la CARTE, et
   --card-radius est figé sur la valeur qu'utilise .carte-face pour que toutes les couches
   s'arrondissent pareil. Vaut pour la scène ET pour la grille finale. ── */
.paquet-carte-slot .card.carte .card__front { container-type: inline-size; }
.paquet-carte-slot .card.carte:not(.carte--rond) { --card-radius: 12px; }
.paquet-carte-slot .carte-legende { padding: 2.5cqw 3cqw; gap: .8cqw; }
.paquet-carte-slot .card.carte .carte-ui .carte-legende-nom  { font-size: 4.8cqw; }
.paquet-carte-slot .card.carte .carte-ui .carte-legende-desc { font-size: 3.3cqw; line-height: 1.4; max-height: 7em; }
.paquet-carte-slot .card.carte .carte-ui .carte-numero {
  font-size: 3cqw; top: 2cqw; right: 2.4cqw; padding: .5cqw 2cqw;
}

/* Bandeau de rareté (« LÉGENDAIRE ! »), consigne, compteur. */
.tirage-bandeau {
  margin: 0; z-index: 70; min-height: 2.2rem;
  font-family: var(--font-heading); font-weight: 800; letter-spacing: .16em;
  font-size: clamp(1.1rem, 4.5vw, 2rem); text-transform: uppercase;
  color: var(--rc); text-shadow: 0 0 18px var(--rc), 0 2px 4px rgba(0,0,0,.7);                        /* repli sans color-mix() */
  text-shadow: 0 0 18px color-mix(in srgb, var(--rc) 80%, transparent), 0 2px 4px rgba(0,0,0,.7);
  opacity: 0;
}
.tirage-bandeau--joue { animation: bandeau .9s cubic-bezier(.2,1.5,.35,1) forwards; }
@keyframes bandeau {
  0%   { opacity: 0; transform: scale(2.1); filter: blur(6px); }
  35%  { opacity: 1; transform: scale(1); filter: blur(0); }
  85%  { opacity: 1; transform: scale(1); }
  100% { opacity: .85; transform: scale(1); }
}
.tirage-consigne {
  margin: 0; z-index: 70; color: #f1f5f9; font-weight: 700;
  font-size: clamp(.9rem, 3.4vw, 1.05rem); text-shadow: 0 2px 6px rgba(0,0,0,.8);
  animation: consigne-appel 1.6s ease-in-out infinite;
}
@keyframes consigne-appel { 0%, 100% { opacity: .55; } 50% { opacity: 1; } }
.tirage-compteur { margin: 0; z-index: 70; color: #cbd5e1; font-weight: 700; font-size: .9rem; letter-spacing: .1em; }
.tirage-skip, .tirage-son {
  position: absolute; z-index: 80; cursor: pointer; font: inherit;
  border: 1px solid rgba(255, 255, 255, .28); background: rgba(255, 255, 255, .1);
  color: #fff; border-radius: 999px; backdrop-filter: blur(3px);
}
.tirage-skip { right: 1rem; bottom: 1rem; padding: .5rem .95rem; font-size: .85rem; font-weight: 700; }
.tirage-son  { left: 1rem;  bottom: 1rem; width: 42px; height: 42px; font-size: 1.05rem; line-height: 1; }
.tirage-skip:hover, .tirage-son:hover { background: rgba(255, 255, 255, .22); }
.tirage-son[aria-pressed="false"] { opacity: .55; }

/* Secousse d'écran sur une légendaire. */
.tirage-scene--shake { animation: scene-shake .55s cubic-bezier(.36,.07,.19,.97) both; }
@keyframes scene-shake {
  10%, 90% { transform: translate(-2px, 1px); }
  20%, 80% { transform: translate(4px, -2px); }
  30%, 50%, 70% { transform: translate(-7px, 3px); }
  40%, 60% { transform: translate(7px, -3px); }
}

/* ── Final : les cartes reviennent dans la grille, badges NEW en cascade ── */
.pioche-liste--arrivee .paquet-carte-slot {
  animation: slot-arrive .55s cubic-bezier(.2,.9,.3,1) backwards;
  animation-delay: calc(var(--i, 0) * 70ms);
}
@keyframes slot-arrive {
  from { opacity: 0; transform: translateY(30px) scale(.88); }
}
.pioche-liste--arrivee .badge-new {
  animation: badge-pop .5s cubic-bezier(.2,1.7,.4,1) backwards;
  animation-delay: calc(var(--i, 0) * 70ms + .35s);
}
@keyframes badge-pop {
  from { opacity: 0; transform: rotate(-8deg) scale(0); }
  60%  { opacity: 1; transform: rotate(-8deg) scale(1.35); }
  to   { opacity: 1; transform: rotate(-8deg) scale(1); }
}

@media (max-width: 560px) {
  .tirage-pile { height: min(52vh, 400px); }
  .tirage-skip { right: .6rem; bottom: .6rem; }
  .tirage-son  { left: .6rem;  bottom: .6rem; }
}

/* Accessibilité : sans mouvement, app.js saute directement au final ; on neutralise en
   plus toute animation résiduelle de la séquence. */
@media (prefers-reduced-motion: reduce) {
  .paquet-ouverture--ouvre .paquet-ouverture-corps,
  .paquet-ouverture--ouvre .paquet-moitie--g,
  .paquet-ouverture--ouvre .paquet-moitie--d,
  .paquet-ouverture--ouvre .paquet-flash,
  .tirage-scene, .tirage-scene--attente .tirage-halo, .tirage-scene--rayons .tirage-rayons,
  .tirage-onde--joue, .tirage-etincelle, .tirage-bandeau--joue, .tirage-consigne,
  .tirage-scene--shake, .tirage-pile .paquet-carte-slot--actif .carte-flip-wrap,
  .pioche-liste--arrivee .paquet-carte-slot, .pioche-liste--arrivee .badge-new {
    animation: none !important;
  }
  .tirage-pile .paquet-carte-slot { transition: none; }
}

/* ── Aperçu admin de l'ouverture de paquet (pages/admin/tirage_apercu.php) ───────────────── */
.apercu-tirage-form {
  display: flex; flex-wrap: wrap; align-items: flex-end; gap: .8rem;
  margin: 1rem 0 .4rem;
}
.apercu-tirage-form label { display: flex; flex-direction: column; gap: .25rem; font-weight: 600; font-size: .85rem; }
.apercu-tirage-form select,
.apercu-tirage-form input[type="number"] {
  padding: .4rem .5rem; border: 1px solid var(--color-border); border-radius: 8px; font: inherit;
}
.apercu-tirage-form input[type="number"] { width: 5rem; }
.apercu-tirage-form .apercu-tirage-case { flex-direction: row; align-items: center; gap: .4rem; padding-bottom: .5rem; }
.apercu-tirage-note { margin: 0 0 1rem; color: var(--color-muted); font-size: .82rem; }

/* ══════════════════════════════════════════════════════════════════════════════════════
   COSMOS / GALAXIE À TEXTURE PERSONNALISÉE (2026-08-25)
   L'effet « Cosmos / galaxie » du pack vendorisé empile trois calques en parallaxe :
   une texture de fond (cosmos-bottom.png) et deux voiles d'étoiles transparents qui lui
   donnent sa profondeur. On rend ICI la texture de fond remplaçable par le FOIL de la
   carte — le slot d'image personnalisée qui existe déjà (uploads/foils/, data-foil).
   On passe par --carte-shine et NON par --foil : plusieurs raretés du pack définissent
   --foil pour leur propre compte (trainerbg, geometric, illusion…), et le repli
   « pas d'image → cosmos-bottom.png » serait alors détourné. --carte-shine n'est posée
   que par app.js, et uniquement quand la carte a bien un fichier : la texture par DÉFAUT
   reste donc toujours assets/img/cosmos-bottom.png.
   On redéclare uniquement background-image (mêmes 3 calques, dans le même ordre) : les
   background-position / -size / -blend-mode du pack restent valables tels quels. La règle
   vient après les @import, elle l'emporte donc à spécificité égale. Vaut aussi pour un
   groupe conjoint, .groupe-effet portant lui aussi la classe .card.
   ══════════════════════════════════════════════════════════════════════════════════════ */
.card[data-rarity="rare holo cosmos"] .card__shine {
  background-image:
    var(--carte-shine, url("../img/cosmos-bottom.png")),
    repeating-linear-gradient(82deg,
      hsl(53, 65%, 60%)  calc(var(--space) * 1),  hsl(93, 56%, 50%)  calc(var(--space) * 2),
      hsl(176, 54%, 49%) calc(var(--space) * 3),  hsl(228, 59%, 55%) calc(var(--space) * 4),
      hsl(283, 60%, 55%) calc(var(--space) * 5),  hsl(326, 59%, 51%) calc(var(--space) * 6),
      hsl(326, 59%, 51%) calc(var(--space) * 7),  hsl(283, 60%, 55%) calc(var(--space) * 8),
      hsl(228, 59%, 55%) calc(var(--space) * 9),  hsl(176, 54%, 49%) calc(var(--space) * 10),
      hsl(93, 56%, 50%)  calc(var(--space) * 11), hsl(53, 65%, 60%)  calc(var(--space) * 12)),
    radial-gradient(farthest-corner circle at var(--pointer-x) var(--pointer-y),
      hsla(180, 100%, 89%, .5) 5%, hsla(180, 14%, 57%, .3) 40%, hsl(0, 0%, 0%) 130%);
}

/* Bouton admin « jouer l'animation » sur une tuile de la boutique : discret, il ne doit pas
   concurrencer le bouton « Ouvrir » qui, lui, coûte des pièces. */
.boutique-apercu { margin-top: .35rem; font-size: .78rem; opacity: .85; }
.boutique-apercu:hover { opacity: 1; }

/* ── Pastille de rareté (repère d'administration) ───────────────────────────────────────
   Rendue sur CHAQUE carte par cartes_render.php, à l'intérieur de .carte-ui — le seul
   endroit où les styles génériques du pack vendorisé sont neutralisés (.card__front est
   une grille dont chaque enfant direct reçoit grid-area: 1/1 et s'étire à toute la carte).
   Masquée partout par défaut : elle n'apparaît qu'en vue page d'administration, en banque
   de cartes et dans l'aperçu complet de l'album.
   Les couleurs viennent des variables --rar-bg / --rar-fg déjà définies par .rarete--*. */
.card.carte .carte-ui .carte-badge-rarete { display: none; }
.album-grille--admin .card.carte .carte-ui .carte-badge-rarete,
.banque-grille .card.carte .carte-ui .carte-badge-rarete,
.album-livre--apercu .card.carte .carte-ui .carte-badge-rarete {
  display: inline-flex; align-items: center; gap: .3em;
  position: absolute; right: .4rem; bottom: .4rem; z-index: 8;
  width: max-content; max-width: calc(100% - .8rem);
  background: var(--rar-bg, #eef2f7); color: var(--rar-fg, #64748b);
  font-size: .68rem; font-weight: 700; line-height: 1; white-space: nowrap;
  padding: .22rem .5rem; border-radius: 999px;
  box-shadow: 0 1px 5px rgba(0, 0, 0, .45);
  pointer-events: none;
}
/* ── Badge de mercato (arrivée) ─────────────────────────────
   Contenu de la carte, pas repère d'admin : visible partout, en haut à GAUCHE pour ne pas
   heurter le numéro (haut-droite) ni la rareté (bas-droite). Taille en rem comme ces deux
   pastilles : .carte-ui n'est PAS un conteneur de requête, un cqw y vaudrait un pourcentage
   de la PAGE et non de la carte. */
.card.carte .carte-ui .carte-badge-mercato {
  display: inline-flex; align-items: center; gap: .35em;
  position: absolute; left: .4rem; top: .4rem; z-index: 8;
  width: max-content; max-width: calc(100% - 2.6rem);
  overflow: hidden; text-overflow: ellipsis;   /* filet de sécurité sur les petites vignettes */
  background: var(--mercato-bg, #1e293b); color: var(--mercato-fg, #f8fafc);
  font-size: .66rem; font-weight: 700; line-height: 1; white-space: nowrap;
  padding: .24rem .5rem; border-radius: 999px;
  box-shadow: 0 1px 5px rgba(0, 0, 0, .45);
  pointer-events: none;
}
.card.carte .carte-ui .carte-badge-mercato b { font-weight: 700; }
.carte-badge-mercato.badge--transfert { --mercato-bg: #0f7b4f; --mercato-fg: #ecfdf5; }
.carte-badge-mercato.badge--pret      { --mercato-bg: #1d4ed8; --mercato-fg: #eff6ff; }
.carte-badge-mercato.badge--libre     { --mercato-bg: #7c3aed; --mercato-fg: #f5f3ff; }
.carte-badge-mercato.badge--retour    { --mercato-bg: #475569; --mercato-fg: #f8fafc; }
/* Sur une carte ronde, le coin haut-gauche est hors du disque. */
.card.carte--rond .carte-ui .carte-badge-mercato { left: 8%; top: 6%; }
/* Au zoom, la carte est bien plus grande : on repasse en unités absolues. */
.zoom-overlay .carte-badge-mercato { font-size: .9rem; }

/* Sur une carte ronde, le coin bas-droite est hors du disque : on rentre la pastille. */
.card.carte--rond .carte-ui .carte-badge-rarete { right: 8%; bottom: 6%; }

/* Morceaux de puzzle : leur interface entière est masquée dans l'album pour ne pas trahir
   l'assemblage. En APERÇU on rouvre .carte-ui juste assez pour laisser passer la pastille,
   et rien d'autre — numéro, légende et fiche restent cachés. (En vue page d'administration,
   .carte-ui est déjà rétablie en entier, numéros compris : c'est voulu.) */
.album-livre--apercu .zone-piece .carte-ui { display: block !important; }
.album-livre--apercu .zone-piece .carte-ui > *:not(.carte-badge-rarete) { display: none !important; }

/* ── Tutoriel guidé ─────────────────────────────────────────
   Trois couches au-dessus de tout le reste (le tirage plein écran monte à 400) :
   un voile transparent qui neutralise les clics, un projecteur dont l'ÉNORME ombre portée
   assombrit la page tout en laissant la cible en clair, et la bulle d'explication. */
.tuto-voile { position: fixed; inset: 0; z-index: 1400; background: transparent; }
.tuto-projecteur {
  position: fixed; z-index: 1401; pointer-events: none;
  border-radius: 12px; border: 2px solid var(--accent);
  box-shadow: 0 0 0 9999px rgba(10, 12, 20, .62), 0 0 0 4px color-mix(in srgb, var(--accent) 35%, transparent);
  transition: top .18s ease, left .18s ease, width .18s ease, height .18s ease;
}
/* Étape sans cible : plus de trou, le voile couvre tout l'écran. */
.tuto-projecteur--absent { top: 50%; left: 50%; width: 0; height: 0; border: none; border-radius: 0; }

.tuto-bulle {
  position: fixed; z-index: 1402; width: min(24rem, calc(100vw - 2rem));
  background: var(--color-card); color: var(--color-text);
  border-radius: var(--radius); border: 1px solid var(--color-border);
  box-shadow: 0 18px 50px -12px rgba(0, 0, 0, .55);
  padding: 1rem 1.1rem;
}
.tuto-bulle--centree { top: 50%; left: 50%; transform: translate(-50%, -50%); }
.tuto-bulle-titre { margin: 0 0 .4rem; font-family: var(--font-heading); font-weight: 800; color: var(--color-heading); }
.tuto-bulle-texte { font-size: .92rem; line-height: 1.45; }
.tuto-bulle-pied { display: flex; align-items: center; gap: .5rem; margin-top: .9rem; }
.tuto-bulle-compte { margin-right: auto; color: var(--color-muted); font-size: .8rem; font-variant-numeric: tabular-nums; }
