/* site.css — styles de la home (pages grand public).
   Style Material 3 adapté : surfaces tonales, radius 24px, sans bordures,
   accent violet, fond pointillé, halo hero. Variables dans tokens.css. */

* { box-sizing: border-box; margin: 0; padding: 0; }
html { scroll-behavior: smooth; }
body {
  font-family: 'Inter', sans-serif;
  background: var(--bg);
  color: var(--ink);
  line-height: 1.55;
  /* D) footer collé en bas : colonne flex pleine hauteur ; le footer (dernier
     enfant direct) est poussé en bas via margin-top:auto quand le contenu est court. */
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  transition: background .25s, color .25s;
  /*background-image: radial-gradient(var(--dot) 1.2px, transparent 1.7px);*/
  background-size: 30px 30px;
/* Fond constellations — pleine largeur, hauteur du hero */
 background-image:
    var(--bg-decor)
   /* radial-gradient(var(--dot) 1.2px, transparent 1.7px)*/;
  background-size: cover, 30px 30px;
  background-position: bottom center, 0 0;
  background-repeat: no-repeat, repeat;
  background-attachment: fixed, scroll;
}

/* Nouvel arrière-plan (public/img/background.png) superposé au fond actuel :
   couche fixe plein écran, au-dessus du fond décoratif mais SOUS le contenu
   (z-index:-1 → derrière les enfants du body, devant le background du body).
   Opacité 50 %, cadrage cover — valeurs à ajuster pour les tests. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  /* L2) 775 Ko en PNG pour un simple dégradé → 7 Ko en WebP (-99 %). image-set() sert le
     WebP aux navigateurs qui l'annoncent et retombe sur le PNG pour les autres ; la
     déclaration url() juste avant est le repli des navigateurs sans image-set(). */
  background-image: url("/img/background.png");
  background-image: -webkit-image-set(url("/img/background.webp") type("image/webp"), url("/img/background.png") type("image/png"));
  background-image: image-set(url("/img/background.webp") type("image/webp"), url("/img/background.png") type("image/png"));
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  opacity: 1;
}
.mono { font-family: 'JetBrains Mono', monospace; }
.wrap { max-width: 1240px; margin: 0 auto; padding: 0 24px; }
/* Point 7) Sur mobile, .wrap repasse en pleine largeur avec un padding réduit : les
   cartes/grilles qui en héritent (home, collection, mes-decks, favoris, source)
   disposent de tout l'espace. (La grille .grid collapse déjà en 1 colonne ≤620px.) */
@media (max-width: 767px) { .wrap { max-width: 100%; padding: 0 16px; } }
/* Point 1) Autorise l'overlay du KPI à déborder à droite du header sans jamais
   créer de scroll horizontal. `clip` préserve le position:sticky du header
   (contrairement à `hidden`). */
html { overflow-x: clip; }

/* ---------- header ---------- */
header {
  position: sticky; top: 0; z-index: 10;
  background: var(--surface); /* thème sombre : fond surface */
  border-bottom: none;
  box-shadow: 0 1px 0 rgba(30, 20, 50, .06);
}
html[data-theme="light"] header { background: #ffffff; } /* thème clair : blanc pur */
.nav { display: flex; align-items: center; justify-content: space-between; height: 64px; }
.brand {
  display: flex; align-items: baseline;
  font-weight: 800; font-size: 21px; letter-spacing: -0.5px;
  text-decoration: none; color: var(--ink);
}
/* Air à droite du logo : le sépare nettement de ce qui suit (bouton « Déposer » puis
   barre de recherche). Desktop uniquement — en mobile (≤720px) le logo est centré par
   `body.m-kiosk .brand`, une marge le décalerait. */
.brand .a { color: var(--amber); padding-right: 2px; }
.brand .fr { padding-left: 2px; color: var(--muted); font-weight: 700; }
.brand .bang { position: relative; top: -25px; width: 10px; margin-left: 3px; }
.brand .bang .bar { position: absolute; top: 2px; left: 1px; width: 5px; height: 13px; border-radius: 3px; background: var(--amber); }
.brand .bang .dot { position: absolute; top: 19px; left: 1px; width: 6px; height: 6px; border-radius: 50%; background: var(--green); }
.brand .bang .dot::after { content: ""; position: absolute; inset: -1px; border-radius: 50%; border: 1.5px solid var(--green); animation: ping 2s ease-out infinite; }
.nav-right { display: flex; gap: 40px; align-items: center; font-size: 14px; padding-left: 15px; }
.nav-right a { color: var(--muted); text-decoration: none; font-weight: 500; }
.nav-right a:hover { color: var(--ink); }
/* ===================== Icônes de .nav-right (cloche / cœur / compte) =====================
   Famille unique : AUCUNE couleur de base (trait --muted), le remplissage violet
   (--amber) monte progressivement DU BAS VERS LE HAUT au survol. Deux calques SVG
   superposés : `.hd-ic-base` (trait, toujours visible) et `.hd-ic-fill` (plein,
   révélé par un clip-path qui remonte). Chaque icône ajoute ensuite SON animation
   propre (cloche qui penche, écho de contour du cœur, retournement de l'avatar). */
.hd-link {
  display: inline-flex; align-items: center; justify-content: center;
  position: relative; color: var(--muted);
  /*padding: 0 5px;*/ /* léger air de part et d'autre de chaque icône */
  -webkit-tap-highlight-color: transparent;
}
/* Neutralise le `:hover { color: var(--ink) }` générique de .nav-right a :
   la couleur de ces icônes est pilotée par les calques, pas par le lien. */
.nav-right a.hd-link:hover { color: var(--muted); }

.hd-ic { position: relative; display: block; width: 22px; height: 22px; }
.hd-ic svg { position: absolute; inset: 0; display: block; width: 100%; height: 100%; }
.hd-ic-base { color: var(--muted); }
.hd-ic-fill {
  color: var(--amber);
  /* Remplissage masqué : le volet part du BAS (inset(top …)) et remonte au survol. */
  clip-path: inset(100% 0 0 0);
  transition: clip-path .42s cubic-bezier(.4, 0, .2, 1);
}
.hd-link:hover .hd-ic-fill,
.hd-link:focus-visible .hd-ic-fill { clip-path: inset(0 0 0 0); }

/* --- Cloche « Mes alertes » : penche de droite à gauche, assez vite. --- */
.hd-ic-bell { transform-origin: 50% 12%; }
.hd-link:hover .hd-ic-bell,
.hd-link:focus-visible .hd-ic-bell { animation: hd-bell-ring .62s ease-in-out infinite; }
@keyframes hd-bell-ring {
  0%, 100% { transform: rotate(0deg); }
  15%  { transform: rotate(13deg); }
  35%  { transform: rotate(-11deg); }
  55%  { transform: rotate(8deg); }
  75%  { transform: rotate(-5deg); }
  90%  { transform: rotate(2deg); }
}

/* --- Cœur « Mes favoris » : le contour déborde sur les côtés en s'estompant
   (même vocabulaire que le halo « ping » du KPI, mais en forme de cœur).
   Le contour est obtenu par un masque SVG *stroke only* : seule la silhouette
   du tracé s'agrandit, l'intérieur reste vide. Deux échos décalés. */
.hd-ic-heart {
  --hd-heart-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' d='M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z'/></svg>");
}
.hd-ic-heart::before,
.hd-ic-heart::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: var(--amber); opacity: 0;
  -webkit-mask: var(--hd-heart-mask) center / contain no-repeat;
  mask: var(--hd-heart-mask) center / contain no-repeat;
}
.hd-link:hover .hd-ic-heart::before,
.hd-link:focus-visible .hd-ic-heart::before { animation: hd-heart-echo 1.25s ease-out infinite; }
.hd-link:hover .hd-ic-heart::after,
.hd-link:focus-visible .hd-ic-heart::after { animation: hd-heart-echo 1.25s ease-out .62s infinite; }
@keyframes hd-heart-echo {
  0%   { transform: scale(1);    opacity: .6; }
  100% { transform: scale(1.85); opacity: 0; }
}

/* NB : plus d'état « cœur rempli en permanence » quand des favoris existent — les trois
   icônes du header sont STRICTEMENT sans couleur au repos, le violet n'apparaît qu'au
   survol. (L'ancienne règle .fav-link.has-fav a été retirée pour cette raison.) */

/* --- « Mon compte » : initiale du pseudo dans un cercle ; au survol le disque se
   remplit de violet (bas → haut) PUIS se retourne pour révéler la roue de
   paramètres. Deux faces en 3D, backface masquée. --- */
.hd-avatar-link { text-decoration: none; }
.hd-avatar {
  position: relative; display: block; width: 30px; height: 30px;
  perspective: 420px;
}
.hd-av-face {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  border-radius: 50%; overflow: hidden;
  -webkit-backface-visibility: hidden; backface-visibility: hidden;
  transition: transform .5s cubic-bezier(.4, 0, .2, 1);
}
.hd-av-front {
  border: 1.5px solid var(--muted); color: var(--muted);
  font-weight: 800; font-size: 13px; line-height: 1; transform: rotateY(0deg);
}
/* Le violet monte du bas (même grammaire que .hd-ic-fill). */
.hd-av-front::before {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 0;
  background: var(--amber); transition: height .42s cubic-bezier(.4, 0, .2, 1);
}
.hd-av-letter { position: relative; z-index: 1; transition: color .3s ease .12s; }
.hd-av-back {
  transform: rotateY(180deg);
  border: 1.5px solid var(--amber); background: var(--amber); color: #fff;
}
.hd-av-back svg { display: block; width: 16px; height: 16px; }
.hd-avatar-link:hover .hd-av-front::before,
.hd-avatar-link:focus-visible .hd-av-front::before { height: 100%; }
.hd-avatar-link:hover .hd-av-letter,
.hd-avatar-link:focus-visible .hd-av-letter { color: #fff; }
.hd-avatar-link:hover .hd-av-front,
.hd-avatar-link:focus-visible .hd-av-front { transform: rotateY(-180deg); transition-delay: .22s; }
.hd-avatar-link:hover .hd-av-back,
.hd-avatar-link:focus-visible .hd-av-back { transform: rotateY(0deg); transition-delay: .22s; }

@media (prefers-reduced-motion: reduce) {
  .hd-ic-fill { transition: none; }
  .hd-link:hover .hd-ic-bell, .hd-link:focus-visible .hd-ic-bell,
  .hd-link:hover .hd-ic-heart::before, .hd-link:hover .hd-ic-heart::after,
  .hd-link:focus-visible .hd-ic-heart::before, .hd-link:focus-visible .hd-ic-heart::after { animation: none; }
  .hd-av-face, .hd-av-front::before, .hd-av-letter { transition: none; }
}
/* .theme-btn : meme theme en background dans les deux thèmes. */
.theme-btn {
  border: 1px solid var(--line); background: #e7e7e7; color: #141218;
  border-radius: 8px; width: 34px; height: 34px; cursor: pointer; font-size: 15px;
}

/* Point 1) « Déposer une alerte » : pilule accent (équivalent « Déposer une annonce »
   de leboncoin). Placée juste après le logo dans le header. */
.btn-deposit {
  flex: none; display: inline-flex; align-items: center; gap: 6px;
  background: var(--amber); color: #fff; text-decoration: none;
  font-weight: 700; font-size: 13.5px; border-radius: 999px;
  padding: 9px 16px; white-space: nowrap; font-family: inherit;
  /* Pas de marge propre : l'écart avec la barre de recherche est piloté par le layout
     du header (voir le bloc ≥1024px), pas par une marge négative de compensation. */
  transition: background .15s;
  /* Point 3) Largeur fixe = le libellé le plus long (« Déposer une alerte ») : le
     changement de texte (rotation avec « Ajouter un deck ») ne décale plus les
     voisins du menu. Le « + » est ANCRÉ À GAUCHE (justify-content: flex-start) et
     ne bouge donc jamais d'un libellé à l'autre ; c'est le libellé qui se recentre
     tout seul dans la place restante (.bd-label flex:1 + text-align:center). */
  min-width: 185px; justify-content: flex-start;
}
.btn-deposit:hover { background: color-mix(in srgb, var(--amber) 88%, #000); color: #fff; }
.btn-deposit .plus { flex: none; font-size: 18px; line-height: 1; font-weight: 400; position: relative; top: -1px; }
.btn-deposit .bd-label { flex: 1 1 auto; text-align: center; }

/* Réduction au « + » seul entre 722 et 1299px : voir le bloc « rotation » en fin de
   feuille (il doit passer APRÈS `.btn-deposit .bd-label { display: inline-block }`). */

/* ---------- hero ---------- */

.hero { padding: 88px 0 48px; position: relative; }
/*
.hero::before {
  content: ""; position: absolute; top: -120px; left: -640px; transform: translate(-50%, 0) scale(1);
  width: 640px; height: 420px; pointer-events: none;
  background: radial-gradient(closest-side, color-mix(in srgb, var(--amber) 50%, transparent), transparent 70%);
  filter: blur(10px); /* halo flou/nébuleux, doux */
 /* animation: hero-halo 15s ease-in-out infinite;
  will-change: transform, opacity; 
}*/
/* Point 9) Mouvement nébuleux : la nappe grandit/rétrécit ET dérive lentement,
   comme un nuage. Multi-étapes non linéaires (translate + scale + opacité), boucle
   fermée (0% = 100%) pour un enchaînement continu. Reste discret, même teinte amber.
   Uniquement transform/opacity (propriétés compositor) → aucun scintillement. */
@keyframes hero-halo {
  0%   { transform: translate(-50%, 0px)    scale(.85);        opacity: .6; }
  25%  { transform: translate(-44%, 24px)   scale(1.06, 1.02); opacity: .9; }
  50%  { transform: translate(-56%, -18px)  scale(1.2,  1.12); opacity: 1;  }
  75%  { transform: translate(-47%, 20px)   scale(1.0,  1.08); opacity: .82; }
  100% { transform: translate(-50%, 0px)    scale(.85);        opacity: .6; }
}
@media (prefers-reduced-motion: reduce) { .hero::before { animation: none; } }
.hero > * { position: relative; }
/* 1) Titre de page commun (hero + /mes-decks…) : une seule source de vérité. */
.page-title { font-size: clamp(32px, 5.4vw, 5rem); font-weight: 800; letter-spacing: -0.03em; line-height: 1.08; font-family: 'Baloo 2',sans-serif; text-align:center; padding: 80px 0 80px;  /*text-shadow: 2px 1px 3px var(--muted);*/}
/* E3) Desktop : hauteur de la zone titre reduite d'environ 35% (80+80 -> 52+52) pour
   remonter la ligne de flottaison et montrer plus d'alertes. Typographie INCHANGEE
   (taille, graisse, famille) ; mobile non touche (media query ci-dessous). */
@media (min-width: 721px) { .page-title { padding: 52px 0 52px; } }
.hero h1 .hl { color: white; }
/* C) Bicoloration générale des H1 : 1er mot en --ink, le reste en violet (.hl).
   S'applique à toutes les pages (le .hl du hero n'était que local). */
h1 .hl { color: var(wheat); }
.hero p { color: var(--muted); font-size: 18px; margin: 18px auto 0; max-width: 520px; }
/* Point 1) Très petits smartphones (iPhone SE, ~375px et moins) : le .wrap ne laisse
   que 16px de marge — on renforce l'air autour du titre et du paragraphe pour qu'ils
   ne collent plus aux bords. */
@media (max-width: 400px) {
  .hero { padding-top: 64px; }
  .hero .page-title { padding-inline: 8px; }
  .hero p { padding-inline: 14px; font-size: 17px; }
}
.kpi {
  margin-top: 28px; display: inline-flex; gap: 8px; align-items: center;
  font-size: 13px; color: var(--muted);
  border: none; background: var(--surface);
  border-radius: 999px; padding: 7px 16px;
}
.kpi #kpi-num { font-weight: 700; color: var(--ink); }
.dot-live { position: relative; width: 11px; height: 11px; border-radius: 50%; background: var(--green); display: inline-block; }
.dot-live::after { content: ""; position: absolute; inset: -1px; border-radius: 50%; border: 1.5px solid var(--green); animation: ping 2s ease-out infinite; }

/* ---------- barre d'outils kiosque : recherche + catégories ---------- */
.toolbar { display: flex; gap: 12px; flex-wrap: wrap; align-items: center; margin: 6px 0 22px; }
.search {
  flex: 1; min-width: 220px; display: flex; align-items: center; gap: 10px;
  background: var(--surface); border-radius: 999px; padding: 11px 18px;
 /* box-shadow: 0 1px 3px rgba(30, 20, 50, .08);*/
}
/* En mode nuit, l'ombre du champ est invisible : on rend sa bordure permanente
   (pas seulement au focus) pour qu'il reste bien délimité. */
html[data-theme="dark"] .search { border: 1px solid color-mix(in srgb, var(--muted) 40%, transparent); }
.search input { flex: 1 1 auto; border: none; background: transparent; font: inherit; font-size: 14.5px; color: var(--ink); width: 100%; min-width: 0; outline: none; }
/* #q est un input[type=search] : Chromium y dessine SA PROPRE croix d'effacement
   (::-webkit-search-cancel-button) EN PLUS de notre .search-clear -> deux « x » cote a
   cote, l'un natif l'autre maison. On neutralise le natif ; .search-clear reste la seule
   croix, avec son handler, son focus clavier et le rejeu du filtre.
   Piege de reproduction : le bouton natif n'apparait QU'APRES UNE VRAIE FRAPPE CLAVIER —
   remplir #q par script (value = ... + dispatchEvent) ne le fait pas apparaitre, on croit
   alors le defaut absent. Se tester en tapant reellement.
   ::-webkit-search-decoration est neutralise avec lui (meme famille, memes surprises). */
.search input::-webkit-search-cancel-button,
.search input::-webkit-search-decoration {
  -webkit-appearance: none; appearance: none; display: none;
}
/* Loupe à gauche (défaut / mobile) ; bouton de recherche à droite masqué (desktop). */
.search-ic { flex: none; opacity: .5; }
.search-go { display: none; }
/* B5) Croix d'effacement : visible seulement quand le champ n'est pas vide (JS). */
.search-clear {
  flex: none; display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; margin-left: 4px; padding: 0; cursor: pointer;
  border: none; background: transparent; color: var(--muted); border-radius: 50%;
  -webkit-tap-highlight-color: transparent;
}
.search-clear[hidden] { display: none; }
.search-clear:hover { color: var(--ink); }
.chips { display: flex; flex-direction: column; gap: 8px; }
.chips-row { display: flex; gap: 8px; flex-wrap: wrap; }
.chip-f {
  border: none; background: none; color: var(--muted);
  font: inherit; font-size: 13px; font-weight: 600;
  border-radius: 999px; padding: 0px 16px; cursor: pointer;
}
/* I6) CIBLES TACTILES : les puces ne mesuraient que 21 px de haut. Sur mobile, on porte
   la ZONE CLIQUABLE à 32 px via le padding vertical — l'aspect desktop est inchangé. */
@media (max-width: 720px) {
  .chip-f { padding-top: 6px; padding-bottom: 6px; min-height: 32px; }
}

.chip-f:hover { color: var(--ink); }
.chip-f.on { color: var(--amber-ink); }
.chip-f .n { opacity: .6; font-size: 11.5px; margin-left: 4px; }
.chip-more-toggle { min-width: 38px; text-align: center; font-weight: 700; }
/* A6) Puces spéciales « Nouveautés » / « La sélection » : accent discret. */
.chip-f.chip-special { }
.chip-f.chip-special:hover { }
.chip-f.chip-special.on { }

/* Séparateur « · » entre les puces de catégories DU HEADER (rangées 2 et 3 de la home).
   Posé en `::before` ABSOLU sur chaque puce sauf la première : il vit dans le `gap` de
   .chips-row, donc il ne fait pas partie du bouton (pas de zone cliquable, pas de
   décalage du libellé, aucun impact sur la puce active). Ailleurs que dans le header
   (aucun usage aujourd'hui), les puces restent sans séparateur. */
header .chips-row .chip-f { position: relative; }
header .chips-row .chip-f + .chip-f::before {
  content: "·";
  position: absolute; left: -4px; top: 50%; transform: translate(-50%, -50%);
  color: var(--muted); opacity: .5; font-weight: 700; pointer-events: none;font-size: 25px;
}
/* Focus : uniquement sur le conteneur de recherche, jamais sur l'input. */
.search input:focus, .search input:focus-visible { outline: none; }

/* ---------- grid ---------- */
/* E1) Violet de TEXTE : l accent d aplat ne passait pas 4.5:1 sur ce petit corps gras. */
.section-label { font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .14em; color: var(--amber-ink); margin-bottom: 14px; }
/* G) padding-top ; rangées à hauteur auto alignées en haut (évite l'étirement
   d'une rangée unique en mode connecté). */
/* Bug « vie locale » : les colonnes DOIVENT rester strictement égales quel que
   soit le contenu. En `1fr` (= minmax(auto,1fr)), le minimum `auto` d'une carte au
   large min-content (ex. la source paramétrée « Vacances scolaires » dont le <select>
   liste des libellés très longs « Zone A (Besançon, Bordeaux…) ») gonflait sa colonne
   à ~644px et écrasait les autres — visible seulement au filtre car cette carte est
   paginée en vue « Toutes ». `minmax(0,1fr)` force des colonnes égales, le contenu
   pouvant rétrécir/déborder proprement (voir aussi `.card { min-width:0 }`). */
/* Format « carte a jouer » : la grille est ADAPTATIVE (auto-fill, le nombre de colonnes se
   recalcule selon la largeur), plutot que 3 colonnes fixes. Plancher de carte = 300px →
   3 colonnes sur tout le desktop (1024→1920), 2 en tablette, 1 en mobile. Les cartes
   REMPLISSENT (1fr) : largeur reelle mesuree = 313px (~1024) a 385px (>=1280), dans la
   fourchette voulue (280-420). minmax(min(300px,100%),1fr) preserve le garde-fou « vie
   locale » (colonnes qui ne gonflent pas au min-content d'un <select> long). */
/* BUG conteneur (corrige) : body est flex-column (footer colle en bas) et .wrap a
   margin:0 auto ; une marge auto sur l'axe transverse d'un flex item DESACTIVE le stretch
   → main#alertes retrecissait a son contenu (~675px) et auto-fill s'effondrait a 2 colonnes.
   width:100% redonne une largeur DEFINIE (cappee par max-width:1240 → grille 1192px) →
   etirement correct. 4 colonnes ne tiennent JAMAIS (4x300=1200 > 1192) : le gap n'a donc
   pas a etre gonfle pour bloquer la 4e colonne, il reste a 18px. Meme fix sur .decks-main. */
main#alertes { width: 100%; }
/* Quand scrollToGrid() amene la grille a l'ecran (filtre applique depuis le bas de
   la page), le header sticky (.nav, 64px) recouvrirait la premiere rangee :
   scroll-margin-top reserve sa hauteur. */
main#alertes { scroll-margin-top: 64px; }
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(300px, 100%), 1fr)); gap: 18px; padding-top: 2rem; padding-bottom: 26px; grid-auto-rows: auto; align-content: start; }
/* Plafond de largeur des cartes d'alerte : la piste reste en 1fr (ex. 385px desktop large),
   la carte y REMPLIT au plus 300px puis est CENTREE via margin auto (mesure : 300px, marges
   symetriques). On utilise margin-inline:auto et NON justify-items:center sur .grid, car
   justify-items:center repasserait aussi les tuiles-deck (.deck-card) en auto-width et
   ferait resurgir l'effondrement shrink-to-fit. Scope « .grid > .card » : cible les cartes
   d'alerte (enfants directs de la grille : kiosque #grid + collection/favoris/deck) et
   EXCLUT .deck-card (tuiles), .ds-face .card (apercus imbriques) et .acct-card (hors grille,
   /mes-decks non touche : ses items sont des .deck-card). */
.grid > .card { max-width: 300px; margin-inline: auto; }
/* Mobile : UNE carte par rangee (format « carte a jouer » pleine largeur, ratio conserve).
   minmax(0,1fr) → la carte se partage la largeur sans jamais deborder, meme sur les tres
   petits ecrans (~320px). */
@media (max-width: 620px) { .grid { grid-template-columns: repeat(1, minmax(0, 1fr)); gap: 12px; } .grid > .card { max-width: none; } }
.card {
  background: var(--surface); border: none; border-radius: 24px;
  padding: 22px; box-shadow: none;
  display: flex; flex-direction: column; gap: 12px;
  transition: box-shadow .2s, transform .15s;
  min-width: 0; /* Bug « vie locale » : autorise la carte à rétrécir dans sa colonne. */
}
.card:hover { box-shadow: var(--shadow); transform: none; }
/* Cartes masquées : pagination (hidden-more) et filtrage (filtered). */
.card.hidden-more, .card.filtered,
.deck-card.hidden-more, .deck-card.filtered { display: none; }
/* Vue « Mes alertes » : carte désabonnée EN SURSIS (~1,5 s) avant de sortir — fondu doux,
   annulation implicite en re-cliquant le switch (spécifique à cette vue, cf. scheduleMineRemoval). */
.card.mine-leaving { opacity: .5; transition: opacity .3s ease; }
@media (prefers-reduced-motion: reduce) { .card.mine-leaving { transition: none; } }

/* Animation FLIP du filtrage : disparition (fade + scale) / apparition (fade in). */
.card.card-leave { opacity: 0; transform: scale(.96); transition: opacity .2s ease, transform .2s ease; }
.card.card-enter { opacity: 0; transform: scale(.96); }
.card.card-enter.card-enter-active { opacity: 1; transform: scale(1); transition: opacity .25s ease, transform .25s ease; }
@media (prefers-reduced-motion: reduce) {
  .card.card-leave, .card.card-enter, .card.card-enter.card-enter-active { transition: none; transform: none; opacity: 1; }
}

/* ---------- carte recto/verso (flip 3D) ---------- */
/* La carte-conteneur devient transparente : l'apparence est portée par les faces.
   Verso en position absolue par-dessus le recto → le recto (donc le « ⓘ ») est
   masqué par backface-visibility quand la carte est retournée. */
/* align-self:start → la carte ne s'étire PAS à la hauteur de la plus grande de sa
   rangée ; elle garde la hauteur de son contenu (recto). Sinon le verso, en
   position:absolute inset:0, remplissait toute la cellule étirée et paraissait
   plus grand que le recto au retournement. */
.card.flip { padding: 0; background: transparent; box-shadow: none; perspective: 1400px; align-self: start; }
.card.flip:hover { box-shadow: none; }
.card-inner {
  position: relative; min-height: 100%;
  /* Fil #9bis (visuel retenu : mixte) — PARTIE 3, règle GLOBALE : rotation du flip
     ralentie (.5s → .85s) avec un easing doux, pour toutes les cartes et tous les
     contextes (kiosque, favoris, decks, page source). prefers-reduced-motion :
     transition:none (règle existante plus bas, inchangée). */
  transform-style: preserve-3d; transition: transform .85s cubic-bezier(.22, .8, .2, 1);
}
.card.flipped .card-inner { transform: rotateY(180deg);}
.card-face {
  display: flex; flex-direction: column; gap: 12px;
  background: var(--surface); border-radius: 24px; padding: 22px;
  -webkit-backface-visibility: hidden; backface-visibility: hidden;
  transition: box-shadow .35s ease, border-color .35s ease;
  border: 2px solid transparent;
  min-width: 0; /* Bug « vie locale » : le contenu (select à libellés longs) peut rétrécir. */
}
.card-front { position: relative; }
/* Format « carte a jouer » : la face recto porte le ratio 5/7 (equivalent simplifie
   du 2.5:3.5 des vraies cartes Magic/Pokemon). C'est le recto (en flux dans
   .card-inner) qui definit la hauteur ; le verso, en position:absolute inset:0,
   epouse cette hauteur → le flip 3D reste identique, juste recalibre.
   Scope .grid : le seul .card.flip HORS grille est la carte « Mon compte »
   (.acct-front, hauteur libre 460px) — elle ne doit PAS prendre ce ratio. */
/* .deck-card .card-front : les cartes d'apercu d'une pile-deck reutilisent EXACTEMENT
   le meme traitement que les cartes de la grille (ratio 5/7 + troncature en fondu), y
   compris hors .grid (en-tete de detail, apercu du formulaire) — sinon le recto s'y
   affiche sans ratio ni clamp (titre casse, description longue). */
.grid .card-front, .deck-card .card-front { aspect-ratio: 2 / 3; }
/* Verso : fond légèrement teinté (subtil), position absolue par-dessus le recto. */
.card-back {
  position: absolute; inset: 0; transform: rotateY(180deg); gap: 14px;
  background: color-mix(in srgb, var(--surface) 96%, var(--amber));
  overflow-y: auto; /* description longue (<=300) defilable si elle depasse le ratio 5/7 */
}
.card.flip:hover .card-face { box-shadow: var(--shadow); }
/* Bouton « ⓘ » masqué au verso (seule la flèche retour reste visible). */
.card.flipped .flip-btn { display: none; }
@media (prefers-reduced-motion: reduce) { .card-inner { transition: none; } }

/* Sous-titre discret sous le titre (recto). */
/* E2) 10,5 px / violet d'aplat mesurait 3,59:1. Corps porté à 11,5 px et violet de
   TEXTE (5,5:1 sur la surface des cartes). */
.card-subtitle { margin-top: 20px; font-size: 11.5px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .07em; color: var(--amber-ink); }

/* Bouton d'info « ⓘ » (recto) et retour « ↩ » (verso) : même coin, même taille. */
.flip-btn, .flip-back {
  position: absolute; top: 14px; right: 14px; z-index: 2;
  border: none; background: transparent; color: var(--muted);
  font-size: 16px; line-height: 1; cursor: pointer; padding: 4px; border-radius: 8px;
}
.flip-btn:hover, .flip-back:hover { color: var(--amber); }
/* C) Flèche retour ET bouton info en SVG inline (rendu identique tous OS) : même
   dimensionnement/alignement. Le « ⓘ » caractère a été remplacé par INFO_SVG. */
.flip-back, .flip-btn { display: inline-flex; align-items: center; justify-content: center; }
/* D) Fleche retour des faces : cible de 36x36 minimum (etait 26x26 = glyphe 18px + 4px de
   padding). Le glyphe reste a 18px ; top/right ajustes de 5px pour que la cible reste
   optiquement au meme endroit sans chevaucher le titre. */
.flip-back { min-width: 36px; min-height: 36px; top: 9px; right: 9px; }
.flip-back svg, .flip-btn svg { width: 18px; height: 18px; display: block; }

/* Verso : description longue (« i »), puis contenu dev. */
.card-long-desc { font-size: 12.5px; line-height: 1.45; color: var(--ink);padding-top: 25px; }
.back-tags { display: flex; flex-wrap: wrap; gap: 6px; }
.back-tags .tag {
  font-size: 12px; font-weight: 600; color: #000;
  background: var(--amber-soft); border-radius: 7px; padding: 7px 12px;
  border: none; border-left: 10px solid var(--tag-c, var(--amber));
}
.back-author { font-size: 12.5px; color: var(--muted); }
.back-author a { color: var(--amber); text-decoration: none; }
.back-author a:hover { text-decoration: underline; }
/* Auteur d'un deck (verso de tuile) : pseudo non cliquable, meme accent que le @handle
   github d'une carte (.back-author a). */
.back-author-name { color: var(--amber); }
.back-propose { margin-top: auto; color: var(--amber); font-weight: 600; font-size: 13px; text-decoration: none; }
.back-propose:hover { text-decoration: underline; }

/* ===== V3 · Zone « tâche à échéance » (bas du verso, cartes user-task) =========
   Zone PROPRE à ce type de carte : n'affecte aucune autre carte, ne modifie pas la
   rangée des 4 boutons ronds du recto. Fond plat (var(--amber-soft)) : AUCUN
   backdrop-filter, donc rien à retirer sous 620px (cf. bloc perf mobile plus bas).
   .card-back est deja overflow-y:auto → la zone defile si le verso deborde du ratio ;
   pas de plafond d'affichage au MVP (a reconsiderer si les tâches s'accumulent). --- */
.task-zone { margin-top: auto; display: flex; flex-direction: column; gap: 8px; }
.task-item {
  background: var(--amber-soft); border-radius: 12px; padding: 10px 12px;
  display: flex; flex-direction: column; gap: 4px;
}
.task-label { font-size: 13px; font-weight: 700; color: var(--ink); }
.task-due { font-size: 12px; color: var(--muted); }
.task-done {
  align-self: flex-start; margin-top: 4px; border: none; cursor: pointer;
  background: var(--amber); color: #fff; font-weight: 700; font-size: 12.5px;
  padding: 7px 14px; border-radius: 999px;
  transition: filter .15s ease, opacity .15s ease;
}
.task-done:hover { filter: brightness(1.08); }
.task-done:disabled { opacity: .55; cursor: default; }
.task-just-done { outline: 2px solid var(--amber); outline-offset: 1px; }
/* Croix de suppression d'une tâche : même style que le ✕ d'un chip de deck
   (.param-remove) — suppression immédiate, sans confirmation. */
.task-item { position: relative; }
.task-item .task-label { padding-right: 22px; }  /* réserve pour la croix */
.task-remove {
  position: absolute; top: 6px; right: 6px;
  border: none; background: transparent; color: var(--muted); cursor: pointer;
  font-size: 13px; line-height: 1; padding: 2px 4px; border-radius: 6px;
}
.task-remove:hover { color: var(--amber); }
.task-remove:disabled { opacity: .5; cursor: default; }
@media (prefers-reduced-motion: reduce) { .task-done { transition: none; } }

/* V3 · bouton d'appel du verso-decouverte + formulaire de creation (modale).
   La modale REUTILISE .share-modal-backdrop/.share-modal-card (share.js) : aucun
   overlay parallele, aucun backdrop-filter (rien a retirer sous 620px). */
.task-create {
  align-self: flex-start; border: none; cursor: pointer; text-decoration: none;
  background: var(--amber); color: #fff; font-weight: 700; font-size: 12.5px;
  padding: 8px 16px; border-radius: 999px; display: inline-block;
}
.task-create.secondary { background: var(--amber-soft); color: var(--amber); }
.task-create:hover { filter: brightness(1.08); }

/* Carte communautaire (« Chat perdu ») : 5e face « Signaler », calquée sur la 5e face
   'user-task' ci-dessus (.task-item/.task-label/.task-due/.task-zone/.tf-title
   réutilisés tels quels — voir cards.js). Classes DÉDIÉES seulement là où le nom
   .task-* est câblé à un comportement spécifique aux tâches (site.js/user-task-form.js)
   qu'on ne veut pas déclencher par erreur ; le style, lui, reste partagé par sélecteur
   groupé plutôt que dupliqué. */
.community-report-toggle {
  align-self: flex-start; border: none; cursor: pointer; text-decoration: none;
  font-weight: 700; font-size: 12.5px; padding: 8px 16px; border-radius: 999px; display: inline-block;
  background: var(--amber-soft); color: var(--amber); /* .secondary d'emblée : la liste prime */
}
.community-report-toggle:hover { filter: brightness(1.08); }
/* CORRECTIONS 4 (A/B) : même piège que .param-form/.community-form déjà connu — la
   règle display:inline-block ci-dessus (auteur) l'emportait sur [hidden] (UA), donc
   btn.hidden = true (site.js) restait sans effet visuel : le bouton restait affiché
   ET occupait sa hauteur dans le flex column .task-zone (l'« espace vide » constaté
   sous le formulaire n'était pas un min-height fantôme, juste ce bouton fantôme). */
.community-report-toggle[hidden] { display: none !important; }
/* Pas de croix de suppression sur un signalement (contrairement à .task-item d'une
   tâche personnelle) : annule la réserve de marge posée pour elle. */
.community-item.task-item .task-label { padding-right: 0; }
.community-item-desc { font-size: 12.5px; color: var(--ink); }
/* Vague 2 : « Je l'ai vu » (témoin, révèle Où/Quand) et « Je l'ai retrouvé » (auteur,
   clôture directe) partagent le même style d'action que l'ancien .community-spot. */
.community-spot-toggle, .community-found {
  align-self: flex-start; margin-top: 4px; border: none; cursor: pointer;
  background: var(--amber); color: #fff; font-weight: 700; font-size: 12.5px;
  padding: 7px 14px; border-radius: 999px; transition: filter .15s ease, opacity .15s ease;
}
.community-spot-toggle:hover, .community-found:hover { filter: brightness(1.08); }
.community-spot-toggle:disabled, .community-found:disabled { opacity: .55; cursor: default; }
/* [hidden] posé d'emblée cette fois (piège déjà rencontré 2 fois sur ce chantier pour
   .community-report-toggle/.community-form — display:inline-flex/block auteur bat
   [hidden] UA sans cet override explicite). */
.community-spot-toggle[hidden] { display: none !important; }
.community-spotted-tag { font-size: 12px; font-weight: 700; color: var(--violet, #a567e3); }
html[data-theme="dark"] .community-spotted-tag { color: var(--violet-l, #bd8ef0); }
.community-loading { font-size: 12.5px; color: var(--muted); }
@media (prefers-reduced-motion: reduce) { .community-spot-toggle, .community-found { transition: none; } }

/* Mini-formulaire Où/Quand (témoin) — même patron empilé que .community-form
   (un .param-row générique en ligne convient mal à 2 champs + bouton + message). */
.community-spot-form { flex-direction: column; align-items: stretch; width: 100%; margin-top: 4px; }
.community-spot-form[hidden] { display: none; }
.community-spot-where, .community-spot-when {
  width: 100%; min-width: 0; max-width: 100%; font: inherit; font-size: 13px;
  color: var(--ink); background: var(--surface); border: 1px solid var(--line);
  border-radius: 10px; padding: 8px 10px;
}
.community-spot-where:focus, .community-spot-when:focus { outline: none; border-color: var(--amber); }
.community-spot-submit {
  align-self: flex-start; border: none; cursor: pointer;
  background: var(--amber); color: #fff; font-weight: 700; font-size: 12.5px;
  padding: 7px 14px; border-radius: 999px;
}
.community-spot-submit:hover { filter: brightness(1.08); }
.community-spot-submit:disabled { opacity: .55; cursor: default; }

/* Recto dynamique (chat-perdu) : quand ≥1 instance est visible pour le profil, le
   sous-titre + la description statiques cèdent la place à .community-recto (contenu
   d'un .task-item.community-item, réutilisé tel quel — cf. cards.js/site.js). Rotation
   auto (≥2 instances) via le MÊME vocabulaire de transition que le H1 de /favoris
   (favoris-title.js : classes .bd-slide-out/.bd-slide-in, keyframe bd-slide-in déjà
   déclarée plus bas dans ce fichier), juste re-scopé à ce conteneur. [hidden] renforcé
   en !important : au moins un skin (École classique) pose display:flex sur
   .card-subtitle avec une spécificité supérieure à [hidden] seul (même piège que
   .community-form plus haut). */
.card-subtitle[hidden], .card-static-desc[hidden], .community-recto[hidden] { display: none !important; }
.community-recto { transition: transform .22s ease, opacity .22s ease; will-change: transform, opacity; }
.community-recto.bd-slide-out { transform: translateY(-70%); opacity: 0; }
.community-recto.bd-slide-in { animation: bd-slide-in .28s ease; }
@media (prefers-reduced-motion: reduce) {
  .community-recto, .community-recto.bd-slide-out, .community-recto.bd-slide-in { transition: none; animation: none; transform: none; opacity: 1; }
}

.utf-card { width: min(460px, 100%); }
.utf-form { display: flex; flex-direction: column; gap: 14px; }
.utf-field { display: flex; flex-direction: column; gap: 6px; border: none; padding: 0; margin: 0; }
.utf-lbl { font-size: 12.5px; font-weight: 700; color: var(--muted); padding: 0; }
.utf-hint { font-size: 11.5px; color: var(--muted); }
.utf-form input, .utf-form select {
  font: inherit; font-size: 14px; color: var(--ink); background: var(--surface);
  border: 1px solid var(--line); border-radius: 10px; padding: 9px 11px; width: 100%;
}
.utf-form input:focus, .utf-form select:focus { outline: 2px solid var(--amber); outline-offset: 1px; }
.utf-suggs { display: flex; flex-wrap: wrap; gap: 6px; }
.utf-sugg {
  border: none; cursor: pointer; font-size: 11.5px; font-weight: 600;
  color: var(--amber); background: var(--amber-soft); border-radius: 999px; padding: 5px 11px;
}
.utf-period { flex-direction: row; align-items: center; gap: 8px; flex-wrap: wrap; }
.utf-period .utf-lbl { flex: 1 0 100%; }
.utf-period .utf-value { flex: 0 0 88px; width: 88px; }
.utf-period .utf-unit { flex: 1; width: auto; }
.utf-error { font-size: 12.5px; color: var(--danger); font-weight: 600; }
.utf-submit {
  border: none; cursor: pointer; background: var(--amber); color: #fff;
  font-weight: 700; font-size: 14px; padding: 11px 18px; border-radius: 12px; margin-top: 4px;
}
.utf-submit:disabled { opacity: .55; cursor: default; }

/* En-tête : titre + badge-icône dans le flux (le badge suit le titre), coin réservé au ⓘ/↩. */
.card-top { display: flex; justify-content: flex-start; align-items: flex-start; gap: 7px; }
.card-front .card-top { padding-right: 20px; } /* réserve pour like + partager + ⓘ */
.card-back .card-top { padding-right: 30px; }  /* réserve pour ↩ */
/* Verso « i » : le titre (deja present sur le recto) est masque quand la carte est
   retournee, sur toutes tailles — le verso se concentre sur la description longue + tags. */
.card.flipped .card-back .card-top { display: none; }
.card h3 { font-size: 16.5px; font-weight: 700; letter-spacing: -0.01em; }
.card p { font-size: 13.5px; color: var(--muted); flex: 1; }
/* Troncature de la description dans le nouveau ratio : nombre de lignes fixe
   (-webkit-line-clamp) + fondu par masque vers le bas (plutot qu'une coupe nette
   avec « … »). Le clamp porte sur .card-desc (enfant du <p>, PAS un enfant flex) :
   sur le <p> lui-meme il serait inerte. flex:1 sur le <p> absorbe l'espace restant
   du recto ; min-height:0 l'autorise a se comprimer. Le texte complet reste
   accessible au verso (bouton « i »). */
/* overflow:hidden sur le <p> (pas seulement sur .card-desc) : quand la carte est
   tres chargee (titre + sous-titre longs sur une carte etroite), le <p> est
   comprime par flex ; sans ce clip, les 3 lignes de .card-desc deborderaient sur
   le bloc d'abonnement en dessous. Le clip garde le ratio et empeche tout
   chevauchement ; le texte complet reste au verso. */
/* Disposition A : la description vit désormais dans .card-content (ancré en bas). */
.grid .card-content p, .deck-card .card-content p { min-height: 0; overflow: hidden; }
/* La description courte est desormais bornee a 120 caracteres EN BASE (CHECK) : le fondu
   (mask) est retire — la description s'affiche en entier sur la carte. Le -webkit-line-clamp
   ne reste qu'en GARDE-FOU (5 lignes, large) : il ne devrait plus jamais se declencher pour
   un texte <=120. Le texte detaille vit au verso (« i ») et sur /source/:id. */
.grid .card-content p > .card-desc, .deck-card .card-content p > .card-desc {
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 5;
  overflow: hidden;
}
.badge {
  font-family: 'JetBrains Mono', monospace; font-size: 10px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .08em; border-radius: 8px; padding: 4px 10px;
  white-space: nowrap;
}
.badge.official { background: var(--amber-soft); color: var(--amber); }
.badge.community { background: var(--line); color: var(--muted); }
.badge.verified { background: var(--green-soft); color: var(--green); }
.state { display: flex; align-items: center; gap: 8px; font-size: 12.5px; font-weight: 600; }
.state.active { color: var(--green); }
.state.idle { color: var(--muted); }
.state.partner { color: var(--muted); }
/* V3 · état neutre des cartes « tâche à échéance » : ni active (pas de veille en
   cours), ni idle (« Rien à signaler » n'aurait aucun sens). Même traitement
   visuel que .state.partner — troisième vocabulaire assumé, pas une variante. */
.state.task { color: var(--muted); }
.dot-idle { width: 8px; height: 8px; border-radius: 50%; background: var(--muted); opacity: .4; }
/* Boutons d'abonnement : pilule violette, radius 10px. */
.sub-btn {
  border: none; background: var(--amber); color: #fff;
  font-weight: 600; font-size: 13.5px; border-radius: 10px; padding: 11px 0;
  cursor: pointer; font-family: inherit; transition: background .15s, box-shadow .15s;
  box-shadow: 0 1px 3px rgba(30, 20, 50, .2);
}
.sub-btn:hover { background: color-mix(in srgb, var(--amber) 88%, #000); box-shadow: 0 2px 6px rgba(30, 20, 50, .3); }
.sub-btn:disabled { opacity: .45; cursor: default; box-shadow: none; }
/* Lien-bouton des sources liées : style « ghost ambre » pour se distinguer. */
.link-btn {
  display: block; text-align: center; text-decoration: none;
  border: 1.5px solid var(--amber); background: transparent; color: var(--amber);
  font-weight: 600; font-size: 13.5px; border-radius: 10px; padding: 10px 0;
  font-family: inherit; transition: background .15s;
}
.link-btn:hover { background: var(--amber-soft); }
/* Recto d'une carte « tâche à échéance » : renvoi au verso, non actionnable.
   Conservé : plus utilisé par frontFace depuis l'étape 8 (remplacé par le bouton
   « Configurer »), gardé comme utilitaire de texte discret sur une face de carte. */
.task-hint { font-size: 12.5px; color: var(--muted); line-height: 1.45; }
.sub-form { display: none; gap: 8px; }
.card.open .sub-form { display: flex; }
.card.open .sub-btn { display: none; }
.sub-form input {
  flex: 1; border: 1.5px solid color-mix(in srgb, var(--ink) 30%, transparent);
  background: transparent; border-radius: 999px;
  padding: 9px 16px; font: inherit; font-size: 13.5px; color: var(--ink); min-width: 0;
}
.sub-form button {
  background: var(--amber); border: none; color: #fff; font-weight: 700;
  border-radius: 999px; padding: 0 18px; cursor: pointer; font-size: 13.5px;
}
/* Message de retour après soumission (remplace le formulaire). */
.sub-msg { font-size: 13px; font-weight: 600; }
.sub-msg.ok { color: var(--green); }
.sub-msg.dup { color: var(--amber); }
.sub-msg.err { color: var(--muted); }
.card.add {
  border: 2px dashed color-mix(in srgb, var(--amber) 45%, transparent);
  border-radius: 24px; background: transparent; box-shadow: none;
  align-items: center; justify-content: center; text-align: center; color: var(--muted);
  gap: 8px; cursor: pointer; text-decoration: none;
  font: inherit; /* variante <button> (ex. tuile « Ajouter une carte » /mes-decks) = même rendu que le <a> */
  /* Même EMPREINTE qu'une carte (300×450) : sans width:100%, le margin-inline:auto de
     .grid > .card fait rétrécir la tuile à la largeur de son texte (bug « tuile trop fine »).
     align-self:start + aspect-ratio 2/3 empêchent l'étirement vertical sur une ligne haute. */
  width: 100%; align-self: start; aspect-ratio: 2 / 3; min-height: 0;
}
.card.add .plus { font-size: 30px; font-weight: 300; color: var(--amber); }
.card.add strong { color: var(--ink); }
.card.add:hover { background: var(--amber-soft); border-color: var(--amber); }
/* Endpoint (verso) : cliquable, JetBrains Mono. */
.endpoint {
  font-family: 'JetBrains Mono', monospace; font-size: 11px; color: var(--muted);
  word-break: break-all; text-decoration: none; display: block;
}
a.endpoint:hover { color: var(--amber); text-decoration: underline; }

/* ---------- pagination ---------- */
.more-wrap { text-align: center; padding: 4px 0 72px; display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; }
/* « Toutes les alertes » (vue liste) : même pilule, en variante secondaire discrète. */
.more-btn-all { background: transparent; color: var(--muted); box-shadow: none; border: 1px solid color-mix(in srgb, var(--muted) 35%, transparent); }
.more-btn-all:hover { color: var(--ink); box-shadow: none; border-color: var(--muted); }
.more-btn {
  border: none; background: var(--surface); color: var(--ink);
  font: inherit; font-weight: 700; font-size: 14px;
  border-radius: 999px; padding: 13px 26px; cursor: pointer;
  box-shadow: 0 1px 3px rgba(30, 20, 50, .1);
}
.more-btn:hover { box-shadow: 0 3px 9px rgba(30, 20, 50, .18); }
.more-btn .n { color: var(--amber); }

/* ---------- footer strip devs ---------- */
.devs { border-top: none; background: var(--surface); padding: 56px 0; }
.devs .wrap { display: grid; grid-template-columns: 1.2fr 1fr; gap: 40px; align-items: center; }
@media (max-width: 760px) { .devs .wrap { grid-template-columns: 1fr; } }
.devs h2 { font-size: 26px; font-weight: 800; letter-spacing: -0.02em; }
.devs p { color: var(--muted); margin-top: 10px; font-size: 15px; }
.code {
  /* D1) Fond sombre PROPRE au bloc de code (--code-bg sur les pages dev), au lieu de
     var(--bg) qui suivait la palette de page : les exemples JSON restent lisibles en
     clair comme en sombre. */
  background: var(--code-bg, var(--bg)); color: var(--code-ink, var(--ink)); border: none; border-radius: 24px; padding: 18px;
  font-family: 'JetBrains Mono', monospace; font-size: 12px; line-height: 1.7; overflow-x: auto;
}
.code .k { color: #bd8ef0; }   /* violet clair : lu sur le fond nuit du bloc, pas sur la page */
.code .s { color: #22c55e; }

/* Bouton de copie sur les blocs de code (icône discrète, coin haut droit). */
.code-wrap { position: relative; max-width: 600px;}
.copy-btn {
  position: absolute; top: 10px; right: 10px; z-index: 2;
  border: none; border-radius: 8px; cursor: pointer;
  padding: 6px; line-height: 0; display: inline-flex;
  background: color-mix(in srgb, var(--surface) 82%, transparent); color: var(--muted);
  transition: color .15s, background .15s;
}
.copy-btn:hover { color: var(--amber); background: var(--surface); }
.copy-btn.copied { color: var(--green); }
.copy-btn svg { width: 15px; height: 15px; }
footer {
  margin-top: auto; /* D) pousse le footer en bas quand le contenu est court */
  padding: 36px 24px; text-align: center; font-size: 16px; color: var(--muted);
  position: relative; z-index: 0; /* contexte d'empilement pour le verre en ::after */
  /* Place reservee AU-DESSUS de la surface vitree pour le debord de la mascotte
     (voir plus bas). Une bordure transparente, et non une marge : elle augmente la
     hauteur du footer, donc elle repousse reellement le contenu de la page — ce
     qu'un margin-top ne pouvait pas faire (il est deja a `auto`), ni un padding-top
     (annule par le margin negatif du ::before). */
  border-top: 351px solid transparent;
  box-shadow: none;
  padding-bottom: 8rem;
}
/* D) Effet verre deplace du footer vers ::after : ainsi le fond semi-transparent et
   le flou ne couvrent QUE la boite de padding (l'absolu se cale sur elle), pas la
   bordure reservee au-dessus — sinon on flouterait 351px de page.
   z-index:-1 : derriere les liens du footer et derriere la mascotte (contenu en flux),
   mais devant le fond du body. */
footer::after {
  content: "";
  position: absolute; inset: 0; z-index: -1;
  background: color-mix(in srgb, var(--surface) 70%, transparent);
  -webkit-backdrop-filter: blur(14px) saturate(1.2);
  backdrop-filter: blur(14px) saturate(1.2);
  box-shadow: 0 -1px 0 rgba(30, 20, 50, .06); /* séparation inversée du header */
}
html[data-theme="light"] footer::after { background: color-mix(in srgb, #ffffff 66%, transparent); } /* thème clair : verre blanc */
footer a { color: var(--muted); }

/* Colonnes du footer. Enveloppees dans .footer-cols (et non footer en grid) : la
   mascotte est le ::before du footer, elle deviendrait sinon une cellule de la grille.
   1re colonne (marque) plus large ; les autres = listes de liens empilees. */
.footer-cols {
  max-width: 1240px; margin: 0 auto;
  display: grid; grid-template-columns: 1.6fr 1fr 1fr 1fr; gap: 32px;
  text-align: left; font-size: 16px; line-height: 1.5;
}
.footer-col { display: flex; flex-direction: column; gap: 8px; }
.footer-col p { margin: 0; }
.footer-name { font-family: 'Baloo 2', sans-serif; font-weight: 800; font-size: 3rem; color: var(--bg); }
.footer-col .footer-h {
  font-size: 13px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  color: var(--ink); margin-bottom: 4px; padding-top: 20px;
}
.footer-col a { text-decoration: none; width: fit-content; }
.footer-col a:hover { color: var(--amber); text-decoration: underline; }
/* Tablette : marque sur toute la largeur, 3 colonnes de liens dessous. */
@media (max-width: 900px) {
  .footer-cols { grid-template-columns: repeat(3, 1fr); }
  .footer-brand { grid-column: 1 / -1; }
}
/* Mobile : tout empile, centre (la mascotte est centree au-dessus). */
@media (max-width: 560px) {
  .footer-cols { grid-template-columns: 1fr; gap: 24px; text-align: center; }
  .footer-col { align-items: center; }
}

/* Mascotte « assise » sur le footer (public/img/image_footer.png, taille à déterminer, periode de test), facon
   Discord : le buste deborde AU-DESSUS de la surface vitree, seul le bas du
   personnage repose dessus.
   Rendue en ::before plutot qu'en <img> : decorative, et les 12 pages ont deja un
   <footer> — aucune retouche HTML page par page.
   Elle reste DANS LE FLUX : les ~200px du bas poussent le texte du footer sous les
   jambes, et le margin-top negatif la fait remonter dans la bordure transparente de
   351px reservee plus haut. Cette bordure faisant partie de la hauteur du footer, le
   contenu de la page est reellement repousse : plus de superposition avec le dernier
   bloc, quel qu'il soit et qu'il soit visible ou non.
   Geometrie : la boite de contenu commence a 351 (bordure) + 36 (padding) = 387px du
   haut du footer ; margin-top -387px ramene donc l'image pile sur ce haut, et la
   surface vitree lui coupe les 552 - 351 = 201px du bas. */
footer::before {
  content: "";
  display: block;
  width: 245px;
  height: 350px;
  max-width: 100%;
  margin: -355px auto 18px;
  background: url("/img/image_footer.png") center bottom / contain no-repeat;
  pointer-events: none;
}
/* Mobile : 260x287 ; bordure 183px + padding 36 = 219px de remontee, 104px poses. */
@media (max-width: 640px) {
  footer { border-top-width: 183px; }
  footer::before { width: 260px; height: 287px; margin: -219px auto 14px; }
}

/* ---------- bouton « Se connecter / déconnecter » ---------- */
.btn-login, .hero-cta {
  border: none; background: var(--amber); color: #fff;
  font-weight: 700; font-size: 13.5px; border-radius: 999px;
  padding: 8px 16px; text-decoration: none; cursor: pointer; font-family: inherit;
  transition: background .15s;
}
.nav-right a.btn-login { color: #fff; }
.btn-login:hover, .nav-right a.btn-login:hover, .hero-cta:hover { background: color-mix(in srgb, var(--amber) 88%, #000); color: #fff; }

/* ---------- menu mobile (hamburger) ---------- */
.nav-toggle {
  display: none; background: none; border: none; cursor: pointer;
  width: 42px; height: 42px; padding: 9px; color: var(--ink);
  flex-direction: column; justify-content: center; gap: 5px;
}
.nav-toggle span {
  display: block; height: 2px; width: 100%; background: currentColor;
  border-radius: 2px; transition: transform .2s, opacity .2s;
}
.nav-toggle[aria-expanded="true"] span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.nav-toggle[aria-expanded="true"] span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

@media (max-width: 720px) {
  .nav-toggle { display: inline-flex; }
  .nav-right {
    position: absolute; top: 64px; left: 0; right: 0;
    flex-direction: column; align-items: stretch; gap: 0;
    background: var(--surface); border-top: 1px solid var(--line);
    box-shadow: 0 10px 24px rgba(30, 20, 50, .14);
    padding: 8px 0; display: none;
  }
  .nav-right.open { display: flex; }
  .nav-right a { padding: 13px 24px; }
  .nav-right .btn-login { margin: 10px 24px; text-align: center; padding: 12px 0; }
  .nav-right .theme-btn { margin: 6px 24px 10px; }
  /* Page dev (Veille de nuit) : nav simple, pas de collapse. */
  body.dev .nav-toggle { display: none; }
  body.dev .nav-right {
    position: static; flex-direction: row; display: flex;
    background: none; box-shadow: none; border: none; padding: 0; gap: 20px;
  }
}

/* ---------- email connecté (header) ---------- */
.auth-email {
  font-size: 13px; color: var(--muted); font-weight: 600;
  max-width: 180px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* ---------- toggle switch (mode connecté) ---------- */
.switch-row { display: flex; align-items: center; gap: 12px; }
.switch { position: relative; display: inline-block; width: 46px; height: 26px; flex: none; }
.switch input { position: absolute; inset: 0; opacity: 0; width: 100%; height: 100%; margin: 0; cursor: pointer; }
.switch .track {
  position: absolute; inset: 0; border-radius: 999px; background: var(--line);
  border: 2px solid color-mix(in srgb, var(--muted) 45%, transparent); transition: background .2s, border-color .2s;
}
.switch .thumb {
  position: absolute; top: 4px; left: 4px; width: 18px; height: 18px; border-radius: 50%;
  background: var(--surface); box-shadow: 0 1px 3px rgba(0, 0, 0, .35); transition: transform .2s;
  border: 1px solid color-mix(in srgb, var(--muted) 45%, transparent);
}
.switch input:checked ~ .track { background: var(--green); border-color: transparent; }
.switch input:checked ~ .thumb { transform: translateX(20px); border-color: transparent; }
.switch input:focus-visible ~ .track { outline: 2px solid var(--amber); outline-offset: 2px; }
.switch-label { font-size: 13px; font-weight: 600; color: var(--muted); }
.switch-label.on { color: var(--green); }

/* Le curseur glisse avec un léger rebond (overshoot). */
.switch .thumb { transition: transform .28s cubic-bezier(.34, 1.56, .64, 1), background .2s, border-color .2s; }

/* Anneau vert émis depuis le curseur au passage ON — même « ping » que le point du logo. */
.switch .thumb::after {
  content: ""; position: absolute; inset: -2px; border-radius: 50%;
  border: 2px solid var(--green); opacity: 0; pointer-events: none;
}
.switch-row.celebrate .thumb::after { animation: ping .65s ease-out; }

/* État « en attente » (mode anonyme) : ambre, curseur à mi-course, pulsation. */
.switch-row.pending .track { background: var(--amber-soft); border-color: var(--amber); }
/* Curseur en attente : position mi-course + pulsation, mais couleur de base conservée. */
.switch-row.pending .thumb {
  transform: translateX(10px) !important;
  animation: pend-pulse 1s ease-in-out infinite;
}
.switch-row.pending .switch-label { color: var(--amber); }
@keyframes pend-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .55; } }

/* La carte s'illumine brièvement en vert quand l'abonnement « prend ». */
.card.flip.celebrate .card-face {
  border-color: var(--green);
  box-shadow: 0 0 0 1px var(--green), 0 6px 18px color-mix(in srgb, var(--green) 30%, transparent);
}

@media (prefers-reduced-motion: reduce) {
  .switch .thumb { transition: none; }
  .switch-row.celebrate .thumb::after { animation: none; }
  .switch-row.pending .thumb { animation: none; }
  .card.flip.celebrate .card-face { transition: none; }
}

/* ---------- squelettes de chargement ---------- */
.card.skeleton { pointer-events: none; gap: 14px; }
.sk { display: block; border-radius: 8px; background: color-mix(in srgb, var(--muted) 22%, transparent); }
.sk-title { height: 18px; width: 60%; }
.sk-line { height: 12px; width: 100%; }
.sk-line.short { width: 70%; }
.sk-btn { height: 40px; width: 100%; border-radius: 10px; margin-top: auto; }
@keyframes sk-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .45; } }
.card.skeleton .sk { animation: sk-pulse 1.4s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce) { .card.skeleton .sk { animation: none; } }

/* ===== Écran de chargement COMMUN à toutes les pages (body.loading) =====
   Pendant le chargement, seules restent visibles : la 1re ligne du header, le
   footer, et un halo centré (vertical + horizontal). Tout le reste (hero, toolbar,
   grille, sections, panneaux…) est masqué. Le flag est retiré dès que le contenu
   est prêt : site.js pour l'accueil, js/page-loading.js pour les pages à données. */
body.loading { display: flex; flex-direction: column; min-height: 100vh; }
body.loading > header,
body.loading > footer { flex: 0 0 auto; }
body.loading > *:not(header):not(footer):not(.page-loading) { display: none !important; }

.page-loading { display: none; } /* invisible hors chargement */
body.loading > .page-loading {
  display: flex; flex: 1 1 auto;
  align-items: center; justify-content: center;
  font-size: 0; /* « Chargement… » gardé pour l'a11y, remplacé par le halo */
}
/* Loader « barres vigilance » (egaliseur en cascade) — remplace le halo pulse sur les
   loaders STATIQUES (.page-loading, .src-loading, .cx-loading). La grille (.grid.grid-loading)
   garde son halo ::after (cohabitation avec le JS qui reecrit ses enfants). Le texte
   « Chargement… » reste present hors de .lba-bars (a11y, masque par font-size:0 de l'hote). */
.lba-bars { display: flex; align-items: flex-end; justify-content: center; gap: 6px; height: 100px; }
.lba-bars span {
  width: 8px; border-radius: 3px; background: var(--bg);
  transform-origin: bottom; animation: lba-bars 1s ease-in-out infinite;
}
.lba-bars span:nth-child(1) { height: 40%;  animation-delay: 0s; }
.lba-bars span:nth-child(2) { height: 70%;  animation-delay: .1s; background: color-mix(in srgb, var(--green) 60%, transparent); }
.lba-bars span:nth-child(3) { height: 100%; animation-delay: .2s; }
.lba-bars span:nth-child(4) { height: 60%;  animation-delay: .3s; background: color-mix(in srgb, var(--amber) 60%, transparent); }
.lba-bars span:nth-child(5) { height: 85%;  animation-delay: .4s; }
@keyframes lba-bars { 0%, 100% { transform: scaleY(.4); } 50% { transform: scaleY(1); } }
@media (prefers-reduced-motion: reduce) { .lba-bars span { animation: none; } }

/* ===== Accessibilité : contrastes renforcés (body.high-contrast) =====
   Activé depuis « Mon compte » (toggle) ou la préférence locale lba-contrast.
   Renforce le texte secondaire, matérialise les bordures, souligne les liens et
   accentue le focus — en clair comme en sombre. */
body.high-contrast { --muted: #2a2622; --line: #0f1419; }
:root[data-theme="dark"] body.high-contrast,
html[data-theme="dark"] body.high-contrast { --muted: #e8e8e8; --line: #f2efe9; }
/* Clair : accent violet assombri (#7d3fbf) pour que TOUT texte amber (liens, chips,
   section-labels, accents .hl, focus…) atteigne 4.5:1 avec ses fonds ; le sombre est
   déjà conforme (amber clair #bd8ef0 ≈ 7:1). Vérifié par calcul WCAG. */
html[data-theme="light"] body.high-contrast { --amber: #7d3fbf; }
/* Mêmes tons codés en dur (le-point) réalignés sur l'accent renforcé. */
html[data-theme="light"] body.high-contrast .lp-soon-when,
html[data-theme="light"] body.high-contrast .lp-soon-item a:hover .lp-soon-msg { color: #7d3fbf; }

body.high-contrast .card,
body.high-contrast .notif-card,
body.high-contrast .pack { border: 1px solid var(--line); }
/* Liens de contenu soulignés (on épargne les boutons/marque déjà identifiables). */
body.high-contrast a:not(.brand):not(.btn-login):not(.btn-deposit):not(.chip-f):not(.card):not(.pack):not(.notif-nav):not(.tag) {
  text-decoration: underline;
}
body.high-contrast :focus-visible { outline: 3px solid var(--amber); outline-offset: 2px; }

/* Header desktop (> 720px) : le lien « Le Point » est retiré dans TOUS les états
   (connecté ou non, écran de chargement ou non) — accessible via le footer. Sur
   mobile (≤ 720px) il reste dans le menu déroulant. Media query = indépendant du
   timing de body.m-kiosk (posé par JS). */
@media (min-width: 721px) { .lepoint-link { display: none; } }

/* ---------- erreur de chargement de la grille ---------- */
.grid-error {
  grid-column: 1 / -1; text-align: center; color: var(--muted);
  font-size: 14px; padding: 28px 0;
}
/* Le message porte desormais une action (« Reessayer », pilule .empty-btn du site). */
.grid-error p { margin: 0 0 14px; }
.grid-error[hidden] { display: none; }

/* ---------- accessibilité : focus clavier visible ---------- */
a:focus-visible,
button:focus-visible,
input:focus-visible,
.chip-f:focus-visible,
.nav-toggle:focus-visible,
.switch input:focus-visible ~ .track,
.search:focus-within {
  outline: 2px solid var(--amber);
  outline-offset: 2px;
}
/*---------------Ligature « la » du titre : courbe SVG (Bézier) + coupe du bas du « l »---------------*/
:root{
    --lig-left: 0.145em;
    --lig-y: 0.35em;
    --lig-width: 0.5em;
    --lig-height: 0.36em;
    --lig-rotate: 0deg;
    --l-cut-bottom: 0.52em;
    --lig-stroke: 34;
    --l-gap: 0.04em;
    --track: -0.05em;
}

.brandmark{font-family:'Baloo 2',sans-serif;font-weight:800;letter-spacing:var(--track)}

.brandmark .lig{position:relative;display:inline-block}

.brandmark .lig svg{
  position:absolute;pointer-events:none;overflow:visible;
  left:var(--lig-left); bottom:var(--lig-y);
  width:var(--lig-width); height:var(--lig-height);
  transform:rotate(var(--lig-rotate));
  transform-origin:center;
}
.brandmark .lig path{
  fill:none;
  stroke:currentColor;
  stroke-width:var(--lig-stroke);
  stroke-linecap:round;
}
.brandmark .l{
  letter-spacing:var(--l-gap);
  display:inline-block;
  clip-path:inset(0 0 var(--l-cut-bottom) 0);
}

/* ---------- Volet H : labels de section dans la marge gauche (grand écran) ---------- */
@media (min-width: 1280px) {
  body:not(.dev) main.wrap,
  body:not(.dev) .devs .wrap,
  body:not(.dev) .stats-section .wrap,
  body:not(.dev) .hist-section { position: relative; }
  body:not(.dev) .section-label {
    position: absolute; top: 2px; margin: 0; text-align: right;
    left: max(-220px, calc(-1 * ((100vw - 1060px) / 2 - 24px)));
    width: min(180px, calc((100vw - 1060px) / 2 - 36px));
  }
}
/* Fond constellations & montagnes (test) */
/*
.hero::after {
  content: "";
  position: absolute; inset: 0; z-index: -1;
  background: url('/img/fond-constellations.svg') bottom center / cover no-repeat;
  pointer-events: none;
}
*/

/* Fond constellations — pleine largeur, hauteur du hero */
/*
.hero::after {
  content: "";
  position: absolute;
  top: 0; bottom: 0;
  left: 50%;
  width: 100vw;
  margin-left: -50vw;
  z-index: -1;
  background: url('/img/fond-constellations.svg') bottom center / cover no-repeat;
  pointer-events: none;
}
*/
/*
body {


}
*/


/* ---------- Chantier confiance & transparence ---------- */
/* Bouton partager (recto des cartes + page statut). */
.share-btn { border: none; background: transparent; color: var(--muted); cursor: pointer; padding: 4px; border-radius: 8px; line-height: 0; display: inline-flex; }
.share-btn:hover { color: var(--amber); }
.card-share { position: absolute; top: 14px; right: 44px; z-index: 2; }
.card.flipped .card-share { display: none; }
.card-share.copied { color: var(--green); }

/* A1) Bouton « j'aime » (recto) : aligné avec partage/info en haut à droite, à leur
   gauche. Cœur contour → plein + accent quand .liked. Masqué au verso comme les autres. */
.card-like {
  position: absolute; top: 13px; right: 80px; z-index: 2;
  display: inline-flex; align-items: center; gap: 3px;
  border: none; background: transparent; cursor: pointer; padding: 4px;
  color: var(--muted); border-radius: 8px; line-height: 0;
  -webkit-tap-highlight-color: transparent;
}
.card-like:hover { color: var(--amber); }
.card-like.liked { color: var(--amber); }
.card-like.liked .ic-like { fill: currentColor; }
.card-like .ic-like { display: block; }
.card-like .like-n { font-size: 12px; font-weight: 600; line-height: 1; }
.card.flipped .card-like { display: none; }
.card-share.copied::after {
  content: 'Lien copié ✓'; position: absolute; top: 120%; right: 0; white-space: nowrap;
  font-size: 11px; font-weight: 600; color: var(--green); background: var(--surface);
  padding: 3px 8px; border-radius: 6px; box-shadow: var(--shadow);
}
.share-btn-lg {
  display: inline-flex; align-items: center; gap: 8px; border: 1.5px solid var(--amber);
  background: transparent; color: var(--amber); border-radius: 999px; padding: 9px 16px;
  font-weight: 600; font-size: 14px; cursor: pointer; font-family: inherit;
}
.share-btn-lg:hover { background: var(--amber-soft); }

/* Compteur d'abonnés discret (recto). */
.sub-count { font-size: 12px; color: var(--muted); font-weight: 600; margin-top: -2px; }

/* Lien statut au verso. */
.back-statut { color: var(--amber); font-weight: 600; font-size: 13px; text-decoration: none; }
.back-statut:hover { text-decoration: underline; }

/* ---------- page de statut ---------- */
.src-main { max-width: 760px; padding: 40px 0 80px; }
.src-loading, .src-error { text-align: center; color: var(--muted); padding: 60px 0; }

/* 7) Halo de chargement doux mais VISIBLE (esprit du halo .hero::before, couleurs plus
   soutenues) — REMPLACE le texte « Chargement… » (gardé pour l'a11y via font-size:0).
   Pages à données : mes-decks, favoris, collection, deck partagé, source (.src-loading),
   connexion (.cx-loading) : desormais les barres .lba-bars (voir plus haut). Seule la
   home (.grid.grid-loading) garde ce halo ::after. Respecte prefers-reduced-motion. */
@keyframes lba-halo-pulse {
  0%, 100% { opacity: .35; transform: translate(-50%, -50%) scale(.88); }
  50%      { opacity: .72; transform: translate(-50%, -50%) scale(1.12); }
}
.src-loading, .cx-loading { position: relative; font-size: 0; } /* texte -> a11y only, remplacé par les barres */
/* .src-loading / .cx-loading : le visuel est le conteneur .lba-bars (voir plus haut),
   injecte dans le markup ; plus de halo ::before ici. */
.grid.grid-loading { position: relative; }
.grid.grid-loading::after {
  content: ""; position: absolute; left: 50%; top: 140px; width: 170px; height: 170px;
  transform: translate(-50%, -50%); pointer-events: none; z-index: 0;
  background: radial-gradient(circle at center, color-mix(in srgb, var(--amber) 55%, transparent), transparent 68%);
  animation: lba-halo-pulse 1.35s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) {
  .grid.grid-loading::after { animation: none; opacity: .5; transform: translate(-50%, -50%); }
}
.src-head { position: relative; background: var(--surface); border-radius: 24px; padding: 28px; box-shadow: var(--shadow); }
/* I) Flèche « Mes alertes » : même flèche que .flip-back, en plus grand, alignée
   à droite à hauteur du titre. */
.src-back {
  position: absolute; top: 22px; right: 22px; z-index: 2;
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--muted); padding: 6px; border-radius: 10px; text-decoration: none;
  transition: color .15s;
}
.src-back:hover { color: var(--amber); }
.src-back svg { width: 30px; height: 30px; display: block; }
/* Le titre ne passe pas sous la flèche. */
.src-head-top { padding-right: 44px; }
.src-head-top { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.src-head h1 { font-size: clamp(22px, 4vw, 28px); font-weight: 800; letter-spacing: -0.02em; }
.src-subtitle { color: var(--muted); font-weight: 600; margin-top: 4px; }
.src-desc { color: var(--muted); margin-top: 10px; font-size: 14.5px; line-height: 1.5; }
/* B2) Liens de visibilité pro du déposant (page source). */
.src-prolinks { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.src-prolink {
  display: inline-flex; align-items: center; font-size: 13px; font-weight: 600;
  color: var(--amber); text-decoration: none; background: var(--amber-soft);
  border-radius: 999px; padding: 6px 14px;
}
.src-prolink:hover { text-decoration: underline; }
.src-head .state { margin-top: 16px; }
.src-count { color: var(--muted); font-size: 13px; font-weight: 600; margin-top: 8px; }
.src-action { margin-top: 16px; max-width: 320px; }
.src-action .sub-form { display: none; margin-top: 10px; gap: 8px; }
#src-share { margin-top: 18px; }
.src-section { margin-top: 34px; }
.src-section h2 { font-size: 17px; font-weight: 800; margin-bottom: 14px; }

/* Barres d'uptime 90 jours. */
.uptime { display: flex; gap: 2px; align-items: stretch; overflow-x: auto; }
.uptime-bar { flex: 1 0 3px; min-width: 3px; height: 36px; border-radius: 2px; background: color-mix(in srgb, var(--muted) 30%, transparent); }
.uptime-bar.u-calm { background: var(--green); }
.uptime-bar.u-active { background: var(--amber); }
.uptime-bar.u-failed { background: var(--danger); }
.uptime-bar.u-nodata { background: color-mix(in srgb, var(--muted) 25%, transparent); }
.uptime-legend { display: flex; gap: 16px; flex-wrap: wrap; margin-top: 12px; font-size: 12px; color: var(--muted); }
.uptime-legend .lg { display: inline-block; width: 10px; height: 10px; border-radius: 3px; vertical-align: middle; margin-right: 5px; }
.lg-calm { background: var(--green); } .lg-active { background: var(--amber); }
.lg-failed { background: var(--danger); } .lg-nodata { background: color-mix(in srgb, var(--muted) 25%, transparent); }

/* ---------- timeline verticale (réutilisable) ---------- */
.tl { list-style: none; position: relative; }
.tl-item { position: relative; padding: 0 0 22px 26px; }
.tl-item::before { content: ""; position: absolute; left: 5px; top: 15px; bottom: -5px; width: 2px; background: color-mix(in srgb, var(--muted) 30%, transparent); }
.tl-item:last-child::before { display: none; }
.tl-dot { position: absolute; left: 0; top: 3px; width: 12px; height: 12px; border-radius: 50%; background: var(--muted); border: 2px solid var(--surface); }
.tl-item.act .tl-dot { background: var(--green); }
.tl-item.calm .tl-dot { background: var(--muted); }
.tl-item.fail .tl-dot { background: var(--danger); }
.tl-head { font-size: 14px; font-weight: 600; }
.tl-when { color: var(--muted); font-weight: 500; font-size: 12.5px; margin-left: 6px; }
.tl-src { color: var(--ink); font-weight: 700; }
.tl-quote { color: var(--muted); font-style: italic; font-size: 13px; margin-top: 4px; }
.tl-empty { color: var(--muted); }

/* Point 2) Page de statut (/source/:id) responsive : rien ne doit déborder à droite
   sur mobile. Le manifeste JSON (<pre> injecté par source.js) ne wrappe pas → on le
   rend scrollable horizontalement plutôt que de le laisser pousser la page. */
#manifest-code pre {
  overflow-x: auto; -webkit-overflow-scrolling: touch; max-width: 100%;
  white-space: pre; margin: 0; font-size: 12.5px;
}
.src-desc, .src-subtitle, .src-author { overflow-wrap: anywhere; }
@media (max-width: 560px) {
  .src-main { padding: 24px 0 60px; }
  .src-head { padding: 20px 18px; }
  .src-head h1 { overflow-wrap: anywhere; }
  .src-back { top: 14px; right: 12px; }
  .src-action { max-width: none; }
  .uptime-legend { gap: 8px 14px; }
}

/* ---------- Chantier home #2 ---------- */
/* C) ancre sous header fixe */
html { scroll-padding-top: 84px; }

/* D) 2e ligne de chips : repli/déploiement réel (transition de hauteur) */
.chips-more { overflow: hidden; max-height: 0; opacity: 0; transition: max-height .25s ease, opacity .25s ease; }
.chips-more.open { max-height: 240px; opacity: 1; }

/* E) grille stable : au moins une rangée réservée */
.grid { }
/* G) Connecté : pas de hauteur minimale imposée (une seule rangée ne doit pas
   réserver 230px de vide) ; l'espace au-dessus des cartes suffit à loger le label
   « Recommandée » sans qu'il déborde sur la rangée du dessous. */
body[data-mode="connected"] .grid { min-height: 0; }
.card.card-reco { margin-top: 8px; }

/* I) rotation du bouton de thème */
.theme-btn { transition: transform .4s ease; }
@media (prefers-reduced-motion: reduce) { .theme-btn { transition: none; } }

/* A) bloc stats marketing */
.stats-section { padding-bottom: 44px; position: relative; }
.stats-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 26px; align-items: stretch; }
@media (max-width: 760px) { .stats-grid { grid-template-columns: 1fr; } }
.stats-tile {
  position: relative; overflow: hidden; background: var(--amber); color: #fff;
  border-radius: 28px; padding: 30px 30px 34px; box-shadow: var(--shadow);
}
/* motif radar en filigrane, coin bas droit */
.stats-tile::after {
  content: ""; position: absolute; right: -34px; bottom: -34px; width: 190px; height: 190px;
  border-radius: 50%; pointer-events: none;
  background: radial-gradient(circle,
    rgba(255,255,255,.22) 0 5px, transparent 5px 24px,
    rgba(255,255,255,.16) 24px 26px, transparent 26px 46px,
    rgba(255,255,255,.12) 46px 48px, transparent 48px 68px,
    rgba(255,255,255,.09) 68px 70px, transparent 70px);
}
.stats-eyebrow { font-size: 13px; font-weight: 700; letter-spacing: .03em; opacity: .92; position: relative; z-index: 1; }
.stats-lines { margin-top: 10px; position: relative; z-index: 1; }
.stat-big { font-size: clamp(44px, 9vw, 68px); font-weight: 800; line-height: 1; letter-spacing: -0.03em; }
.stat-cap { font-size: 15px; font-weight: 600; opacity: .92; margin-top: 2px; }
.stat-mid { font-size: 22px; font-weight: 800; margin-top: 18px; }
.stat-mid .stat-cap-inline { font-size: 14px; font-weight: 600; opacity: .92; }
.stat-spam { margin-top: 18px; font-size: 15px; font-weight: 700; }
.stat-spam .zero { color: #fff; }
.stats-copy { display: flex; flex-direction: column; justify-content: center; }
.stats-copy h2 { font-size: clamp(22px, 3.6vw, 30px); font-weight: 800; letter-spacing: -0.02em; line-height: 1.15; }
.stats-copy p { color: var(--muted); margin-top: 12px; line-height: 1.6; font-size: 15px; }
.stats-cta { margin-top: 16px; color: var(--amber); font-weight: 700; text-decoration: none; align-self: flex-start; }
.stats-cta:hover { text-decoration: underline; }

/* B) historique connecté + empty states */
.hist-section { padding: 8px 0 44px; }
.history-empty { color: var(--muted); background: var(--surface); border-radius: 16px; padding: 22px; font-size: 15px; }
/* E1) PAGES DE CONTENU : le corps de texte reposait directement sur le dégradé violet,
   où --muted tombe sous 2:1. Panneau opaque, comme /soutenir et la page de statut. */
main.wrap.page {
  background: var(--surface); border-radius: 18px; box-shadow: var(--shadow);
  padding: 40px 32px 56px; margin-top: 24px; margin-bottom: 48px;
}
@media (max-width: 620px) { main.wrap.page { padding: 28px 18px 40px; border-radius: 14px; } }

/* E3) Message d'état vide : il était en --muted sur le dégradé. Plaque opaque + --ink,
   et une seconde action « Proposer cette alerte » (l'utilisateur qui ne trouve rien est
   exactement celui qui peut nous signaler le manque). */
.grid-empty {
  text-align: center; padding: 28px 24px 32px; color: var(--ink);
  background: var(--surface); border-radius: 18px; box-shadow: var(--shadow);
  max-width: 560px; margin: 24px auto;
}
.grid-empty .empty-actions { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; }
.empty-btn.secondaire {
  background: transparent; color: var(--amber-ink); border: 1.5px solid var(--amber-ink);
  text-decoration: none; display: inline-flex; align-items: center;
}
.empty-btn.secondaire:hover { background: var(--amber-soft); }
/* Piege maison : une regle auteur `display:` l'emporterait sur [hidden] — les deux
   blocs ci-dessous sont masquables par JS, on garde l'override explicite. */
.grid-empty[hidden] { display: none; }
.empty-btn[hidden] { display: none; }
.grid-empty p { font-size: 16px; margin-bottom: 16px; }
.empty-btn { border: none; background: var(--amber); color: #fff; font-weight: 700; border-radius: 999px; padding: 11px 22px; cursor: pointer; font-family: inherit; font-size: 14px; }
.empty-btn:hover { background: color-mix(in srgb, var(--amber) 88%, #000); }

/* H) label « Le kiosque » centré verticalement avec la barre de recherche (desktop large).
   B) Le label se pin désormais dans .kiosk-head (relative), donc à la hauteur de la
   barre d'outils — plus jamais superposé à « Les decks » de l'étagère. */
@media (min-width: 1280px) {
  body:not(.dev) .kiosk-head > .section-label { top: 20px; }
}
/* Mobile : le label « Le kiosque » est superflu (la recherche/les chips suffisent). */
body.m-kiosk .kiosk-head > .section-label { display: none; }

/* ---------- Chantier retouches A-K ---------- */
/* A) popover de partage */
.share-pop {
  position: absolute; z-index: 1000; width: 300px; max-width: calc(100vw - 24px);
  background: var(--surface); color: var(--ink); border-radius: 24px; padding: 14px;
  box-shadow: 0 14px 44px rgba(30, 20, 50, .24), 0 2px 8px rgba(30, 20, 50, .12);
}
.share-pop-head { display: flex; align-items: center; justify-content: space-between; font-weight: 700; font-size: 14px; padding: 2px 4px 10px; }
.share-close { border: none; background: none; font-size: 22px; line-height: 1; color: var(--muted); cursor: pointer; padding: 0 4px; }
.share-close:hover { color: var(--ink); }
.share-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 6px; }
.share-opt {
  display: flex; flex-direction: column; align-items: center; gap: 6px; text-decoration: none;
  color: var(--ink); border: none; background: none; cursor: pointer; padding: 10px 4px; border-radius: 14px; font-family: inherit;
}
.share-opt:hover { background: var(--bg); }
.share-ic { color: var(--amber); display: inline-flex; }
.share-lbl { font-size: 11px; color: var(--muted); font-weight: 600; text-align: center; line-height: 1.2; }
.share-backdrop { position: fixed; inset: 0; background: rgba(0, 0, 0, .45); z-index: 999; }
.share-pop.sheet {
  position: fixed; left: 0; right: 0; bottom: 0; top: auto; width: 100%; max-width: none;
  border-radius: 24px 24px 0 0; padding: 18px 18px calc(18px + env(safe-area-inset-bottom, 0px));
}

/* 9) Grande carte de partage (modale plein écran) — style carte du site. */
.share-modal-backdrop {
  position: fixed; inset: 0; z-index: 1000; background: rgba(24, 16, 40, .5);
  display: flex; align-items: center; justify-content: center; padding: 20px;
  animation: share-modal-in .18s ease;
}
@keyframes share-modal-in { from { opacity: 0; } to { opacity: 1; } }
.share-modal-card {
  position: relative; width: min(430px, 100%); background: var(--surface); color: var(--ink);
  border-radius: 24px; box-shadow: 0 24px 70px rgba(20, 12, 40, .34), 0 3px 12px rgba(20, 12, 40, .16);
  padding: 26px 24px 24px;
}
.share-modal-close {
  position: absolute; top: 14px; right: 16px; border: none; background: none;
  font-size: 22px; line-height: 1; color: var(--muted); cursor: pointer; padding: 4px 6px; border-radius: 8px;
}
.share-modal-close:hover { color: var(--ink); }
.share-modal-title { font-weight: 800; font-size: 1.2rem; letter-spacing: -.01em; margin-bottom: 18px; }
.share-modal-grid { grid-template-columns: repeat(4, 1fr); gap: 6px; }
@media (max-width: 380px) { .share-modal-grid { grid-template-columns: repeat(3, 1fr); } }
@media (prefers-reduced-motion: reduce) { .share-modal-backdrop { animation: none; } }

/* F) badge → icône */
.badge-ic { display: inline-flex; flex: none; line-height: 0; }
.badge-ic.official, .badge-ic.verified { color: var(--amber); }
.badge-ic.community { color: var(--muted); }
.card-front .card-top, .card-back .card-top, .src-head-top { align-items: center; }

/* E) bandeau stats pleine largeur */
.stats-section { background: #edf; padding: 40px 0 48px; }
html[data-theme="dark"] .stats-section { background: color-mix(in srgb, var(--surface) 55%, var(--bg)); }

/* G) typographie du bloc stats */
.stats-eyebrow { font-family: 'Baloo 2', sans-serif; font-weight: 800; font-size: 16px; }
.stat-spam { display: flex; align-items: baseline; gap: 12px; margin-top: 14px; }
.stat-spam .zero { font-size: clamp(44px, 9vw, 68px); font-weight: 800; line-height: 1; background: none; padding: 0; border-radius: 0; }
.stat-spam .spam-cap { font-size: 15px; font-weight: 700; }

/* D) home connectée épurée : masque le titre + la tagline, garde le halo et le KPI */
/* On dissocie le halo (::before) du contenu : le conteneur .hero reste présent
   avec son halo violet animé ; seuls h1 et la tagline sont masqués. Le KPI
   personnalisé reste visible et centré. */
/* Connecté : le h1 reste affiché (« Bonjour <prénom> », prénom en accent via
   .hl-name) ; seule la tagline est masquée. */
body[data-mode="connected"] .hero p { display: none; }

/* Bloc d'amorce du hero (ANONYME uniquement) : promesse concrete + bouton vers
   /connexion. Masque par defaut et revele par [data-mode="anon"] : tant que le mode
   n'est pas connu (body.loading), rien ne s'affiche — donc pas de flash puis
   disparition chez un visiteur connecte, dont le hero reste strictement identique.
   Le bouton reprend le style de .btn-login par GROUPEMENT de selecteur (pas de
   classe ajoutee) : aucun handler du header ne peut le capter. */
.hero-start { display: none; }
body[data-mode="anon"] .hero-start { display: block; margin-top: 20px; }
.hero-promise {
  color: var(--ink); font-size: 16px; line-height: 1.5;
  margin: 0 auto 18px; max-width: 560px;
}
.hero-cta {
  display: inline-block; font-size: 15px; padding: 12px 26px;
}
@media (max-width: 640px) {
  .hero-promise { font-size: 15px; }
  .hero-cta { width: 100%; max-width: 320px; text-align: center; }
}
body[data-mode="connected"] .hero { padding: 0px 0 0px; }
body[data-mode="connected"] #stats { display: none; }
.hero h1 .hl-name { color: var(--amber); } /* prénom en accent, « Bonjour » en --ink */

/* H) carte Proposer : transition de hauteur symétrique */
.card.add { transition: min-height .3s ease, background .15s ease, border-color .15s ease; }

/* E4) THÈME SOMBRE — deux correctifs mesurés :
   1. Les tags de catégorie sont en texte noir (choix assumé en thème clair) : sur
      --amber-soft sombre (#332a44) cela tombait à 1,56:1. En sombre, le texte au repos
      passe donc en --ink ; au SURVOL le fond devient la variante claire de la couleur de
      famille, donc le texte y redevient noir.
   2. Le grand aplat violet clair (body::before, /img/background.png) était peint à
      l'identique dans les deux thèmes : des cartes sombres flottaient sur un fond
      lumineux. On l'assombrit pour qu'il suive le thème — même image, donc aucun
      téléchargement supplémentaire. */
html[data-theme="dark"] .back-tags .tag { color: var(--ink); }
html[data-theme="dark"] .back-tags .tag:hover { color: #000; }
html[data-theme="dark"] body::before { filter: brightness(.34) saturate(.9); }

/* J) tags : couleur par famille de catégorie (data-group = clé GROUPS de
   server/categories.js). Texte noir en permanence ; --tag-c = bordure gauche,
   --tag-c2 (variante ~35 % plus claire) = fond au survol. Posé aussi sur .card
   (famille de sa 1re catégorie) → liseré .card-edge. Sans famille connue →
   repli sur l'accent violet. */
.back-tags .tag { color: #000; cursor: pointer; transition: background .15s ease; }
.back-tags .tag:hover { background: var(--tag-c2, var(--amber)); color: #000; }
[data-group="alimentation"]                          { --tag-c: #e36767; --tag-c2: #ec9c9c; }
[data-group="securite-cyber"]                        { --tag-c: #c2456a; --tag-c2: #d7869e; }
[data-group="sante"]                                 { --tag-c: #e36fa5; --tag-c2: #eca1c4; }
[data-group="shopping"]                              { --tag-c: #d97fb0; --tag-c2: #e6abcb; }
[data-group="gaming"]                                { --tag-c: #d45fd9; --tag-c2: #e397e6; }
[data-group="billetterie-sorties"]                   { --tag-c: #b35fe3; --tag-c2: #cd97ec; }
[data-group="culture-medias"]                        { --tag-c: #8a6fe3; --tag-c2: #b2a1ec; }
[data-group="pro-marches"]                           { --tag-c: #6c63c9; --tag-c2: #9f99db; }
[data-group="tech-dev"]                              { --tag-c: #5b7de3; --tag-c2: #94aaec; }
[data-group="espace-science"]                        { --tag-c: #3f6fb8; --tag-c2: #82a1d0; }
[data-group="transports"]                            { --tag-c: #4f8fd1; --tag-c2: #8cb6e1; }
[data-group="voyages"]                               { --tag-c: #3fb0d9; --tag-c2: #82cbe6; }
[data-group="francophonie-international"]            { --tag-c: #4fa3a8; --tag-c2: #8cc3c6; }
[data-group="emploi-formation"]                      { --tag-c: #3fb3a3; --tag-c2: #82cdc3; }
[data-group="finance"]                               { --tag-c: #2f9e7a; --tag-c2: #77bfa8; }
[data-group="environnement-nature"]                  { --tag-c: #4fae5d; --tag-c2: #8cca95; }
[data-group="agriculture-jardin"]                    { --tag-c: #7fae3f; --tag-c2: #abca82; }
[data-group="energie"]                               { --tag-c: #c9b53a; --tag-c2: #dbce7e; }
[data-group="animaux"]                               { --tag-c: #c9973f; --tag-c2: #dbbb82; }
[data-group="bons-plans-conso"]                      { --tag-c: #e3a33f; --tag-c2: #ecc382; }
[data-group="meteo-risques"]                         { --tag-c: #e3823f; --tag-c2: #ecad82; }
[data-group="vie-locale"]                            { --tag-c: #d97a5f; --tag-c2: #e6a897; }
[data-group="solidarite"]                            { --tag-c: #e38f8f; --tag-c2: #ecb6b6; }
[data-group="immobilier"]                            { --tag-c: #b07a4f; --tag-c2: #cba88c; }
[data-group="auto-moto"]                             { --tag-c: #9a7d6a; --tag-c2: #bdaa9e; }
[data-group="administration"]                        { --tag-c: #6f86a8; --tag-c2: #a1b0c6; }
[data-group="autre"]                                 { --tag-c: #9a93a8; --tag-c2: #bdb8c6; }

/* I) conteneur de la 2e ligne de chips (hors toolbar) */
.chips-secondary-wrap .chips-more.open { margin-bottom: 6px; margin-top: 10px; }

/* D) carte de recommandation */
.card.add.reco-hidden { display: none; }
.card.card-reco { position: relative; }
.reco-label {
  position: absolute; top: -10px; left: 16px; z-index: 6;
  display: inline-flex; align-items: center; gap: 4px;
  background: var(--amber-soft); color: var(--amber);
  border-radius: 999px; padding: 3px 6px 3px 12px; font-size: 11px; font-weight: 700;
  box-shadow: 0 1px 3px rgba(30, 20, 50, .14);
}
/* H) Libelle visible d'un champ de parametre texte (ex. « Code commune (INSEE) ») : le
   champ n'affichait qu'un placeholder (« 05061 »), incomprehensible hors contexte. */
.param-field-label {
  display: block; font-size: 11px; font-weight: 600; color: var(--muted);
  margin-bottom: 3px; line-height: 1.2;
}
/* D) Croix du badge « Recommandee » : cible 24x24 (glyphe 15px conserve). */
.reco-x { border: none; background: none; color: var(--amber); cursor: pointer; font-size: 15px;
  line-height: 1; padding: 0; border-radius: 50%; min-width: 24px; min-height: 24px;
  display: inline-flex; align-items: center; justify-content: center; flex: none; }
.reco-x:hover { color: var(--ink); }

/* ---------- Volet C : face « Partager » (3e face de la carte) ---------- */
.card-share-face {
  position: absolute; inset: 0; transform: rotateY(180deg); gap: 12px;
  background: color-mix(in srgb, var(--surface) 96%, var(--amber));
  overflow-y: auto;
}
/* Face arrière visible selon .face-share (indépendant de .flipped → pas de flicker au retour). */
.card.face-share .card-back { display: none; }
.card:not(.face-share) .card-share-face { display: none; }
.card.flipped .card-share { display: none; } /* bouton partager masqué au verso */

/* F3) Face « Ajouter à un deck » (4e face) : même mécanique que la face partage. */
.card-deck-face {
  position: absolute; inset: 0; transform: rotateY(180deg); gap: 10px;
  background: color-mix(in srgb, var(--surface) 94%, var(--amber));
  overflow-y: auto;
}
.card.face-deck .card-back, .card.face-deck .card-share-face { display: none; }
.card:not(.face-deck) .card-deck-face { display: none; }
.cdf-title { font-size: 14px; font-weight: 700; padding-right: 30px; color: var(--ink); }
.cdf-body { display: flex; flex-direction: column; gap: 2px; }
.cdf-loading { color: var(--muted); font-size: .85rem; padding: 8px 2px; }
.card-deck-face .dam-item { width: 100%; text-align: left; }

/* V3 · 5e face « Configurer » (gestion des tâches). Même mécanique que les faces
   partage/deck : .face-task désigne la face arrière visible, indépendamment de
   .flipped (pas de flicker au retour). Nommage générique : cette structure est
   destinée à d'autres cartes à instances utilisateur. */
.card-task-face {
  position: absolute; inset: 0; transform: rotateY(180deg); gap: 10px;
  background: color-mix(in srgb, var(--surface) 94%, var(--amber));
  overflow-y: auto;
}
/* K2) La face des échéances est en position absolue : elle ne pouvait pas dépasser la
   hauteur du recto, si bien qu'à partir de ~5 tâches une seule et demie était visible et
   le bouton « C'est fait » tombait hors cadre. On autorise la CARTE à grandir quand cette
   face est ouverte (jusqu'à 460 px), au-delà la liste défile DANS la face. Le masonry est
   re-mesuré après l'ouverture (site.js appelle LBAViewCompare.remeasure). */
.card.face-task .card-inner { min-height: min(460px, 78vh); }
.card.face-task .card-task-face { overflow: hidden; display: flex; flex-direction: column; }
/* Seule la LISTE défile : le titre et « + une autre tâche » restent visibles sans défiler. */
.card.face-task .card-task-face .task-zone { overflow-y: auto; min-height: 0; flex: 1 1 auto; }
.card.face-task .card-back { display: none; }
.card:not(.face-task) .card-task-face { display: none; }
.tf-title { font-size: 14px; font-weight: 700; padding-right: 30px; color: var(--ink); }
.share-face-title { font-size: 14px; font-weight: 700; padding-right: 30px; }
/* Fil #9bis (visuel retenu : mixte) — PARTIE 4 : les 9 (ou 10 sur mobile, avec SMS)
   vignettes doivent tenir ENTIÈREMENT dans la hauteur de la carte, large (~232px) comme
   ultra (~150px) : 5 colonnes × 2 rangées, gaps/paddings/icônes/libellés resserrés, et
   overflow HIDDEN sur cette face (plus de barre de défilement). */
/* K1) La grille s'ÉTALE désormais sur toute la largeur de la face (plus de max-width qui
   la tassait à gauche), avec des icônes à 22 px et des libellés à 11 px — les 8,5 px
   précédents n'étaient pas lisibles. Toujours aucun défilement (overflow: hidden).
   · carte LARGE : une seule rangée de 9 (la largeur le permet largement) ;
   · carte ULTRA (320 px) : 5 colonnes sur 2 rangées, qui tiennent dans les 150 px. */
.card-share-face { gap: 6px; overflow: hidden; }
.card-share-face .share-face-title { font-size: 12.5px; }
.card-share-face .share-grid {
  grid-template-columns: repeat(9, minmax(0, 1fr)); gap: 4px;
  max-width: none; width: 100%;
}
.card-share-face .share-opt { padding: 6px 2px; border-radius: 10px; gap: 3px; }
.card-share-face .share-ic svg { width: 22px; height: 22px; }
.card-share-face .share-lbl { font-size: 11px; line-height: 1.15; }
/* K1) Carte ULTRA : 2 rangees d icones 22px + libelles 11px ne tiennent pas dans 150px.
   Plutot que de re-miniaturiser le texte (le defaut qu on corrige), la carte GRANDIT le
   temps que la face partage est ouverte - meme principe que la face des echeances. */
#grid[data-view-mode="mixte"] > .card.vc-side.face-share .card-inner,
#grid[data-view-mode="ultra"] > .card.face-share .card-inner { min-height: 215px; }
/* Cartes étroites (colonne ultra du mixte, mode ultra, mobile) : 5 colonnes, 2 rangées. */
#grid[data-view-mode="mixte"] > .card.vc-side .card-share-face .share-grid,
#grid[data-view-mode="ultra"] .card-share-face .share-grid {
  grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 3px;
}
#grid[data-view-mode="mixte"] > .card.vc-side .card-share-face .share-opt,
#grid[data-view-mode="ultra"] .card-share-face .share-opt { padding: 4px 1px; }
@media (max-width: 620px) {
  .card-share-face .share-grid { grid-template-columns: repeat(5, minmax(0, 1fr)); }
}

/* Volet B : le badge-icône suit le titre sans jamais le pousser hors flux.
   min-width:0 laisse le titre rétrécir dans sa colonne (garde-fou « vie locale ») ;
   overflow-wrap autorise un mot très long et insécable (ex. « Restrictions ») à se
   couper pour rester dans la boîte du h3 au lieu de déborder SOUS le badge inline
   sur carte étroite. Ne se déclenche que si un mot ne tient pas. */
.card-top h3 { flex: 0 1 auto; min-width: 0; overflow-wrap: anywhere; }
/* Format « carte a jouer » : quand le titre passe sur 2 lignes (fréquent sur carte
   étroite, ex. « Vigilance météo »), le badge doit rester aligné sur la PREMIÈRE
   ligne du titre — et non centré verticalement sur tout le bloc, où il tombait au
   milieu de la 2e ligne. On top-aligne le bloc titre et le badge (override des
   règles align-items:center / align-self:center ci-dessus, par ordre source),
   avec un léger décalage pour l'optique. src-head-top (page source) reste centré. */
.card-front .card-top, .card-back .card-top { align-items: flex-start; }
.card-top .badge-ic { align-self: flex-start; margin-top: 2px; }
/* Carte étroite (2 col mobile) : le titre est SOUS la bande d'icônes (voir .state
   repositionné) → plus besoin de la réserve padding-right, et le titre prend toute
   la largeur (flex:1) au lieu d'être comprimé au point de casser chaque mot. Le
   badge se pose alors en haut-droite du bloc titre (aligné 1re ligne). */
@media (max-width: 620px) {
  .card-front .card-top { padding-right: 0; }
  .card-front .card-top h3 { flex: 1 1 auto; }
}

/* Comptage des stats : petit pop du « 0 » (sans comptage) quand les chiffres finissent */
@keyframes zero-pop { 0% { transform: scale(.6); } 60% { transform: scale(1.12); } 100% { transform: scale(1); } }
.stat-spam .zero { display: inline-block; transform-origin: center bottom; }
.stat-spam .zero.pop { animation: zero-pop .45s cubic-bezier(.34, 1.56, .64, 1) both; }
@media (prefers-reduced-motion: reduce) { .stat-spam .zero.pop { animation: none; } }

/* ---------- panneau Notifications (mode connecté) ---------- */
.notif-panel { margin: 10px auto 0; }
.notif-panel .section-label { margin-bottom: 14px; }
.notif-card {
  background: var(--surface); border-radius: 20px; padding: 6px 22px;
  box-shadow: 0 1px 3px rgba(30, 20, 50, .06);
}
.notif-row {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding: 18px 0;
}
.notif-row + .notif-row { border-top: 1px solid color-mix(in srgb, var(--ink) 8%, transparent); }
/* Le display:flex ne doit pas défaire l'attribut [hidden] (ex. ligne Département). */
.notif-row[hidden] { display: none; }
.notif-txt strong { display: block; font-size: 15px; font-weight: 700; color: var(--ink); }
.notif-sub { display: block; margin-top: 3px; font-size: 13px; color: var(--muted); line-height: 1.5; max-width: 420px; }
/* Même style que .coll-adopt (bouton plein ambre, texte blanc, pastille arrondie, ombre). */
.notif-btn {
  flex: none; background: var(--amber); color: #fff; border: none; cursor: pointer;
  font-weight: 700; font-size: 1rem; padding: 12px 22px; border-radius: 999px;
  box-shadow: var(--shadow); transition: transform .14s ease, filter .14s ease;
}
.notif-btn:hover { filter: brightness(1.05); transform: translateY(-1px); }
.notif-btn.on { background: var(--amber); color: #fff; }
.notif-btn:disabled { opacity: .6; cursor: default; transform: none; }
/* Interrupteur email */
.notif-toggle {
  flex: none; position: relative; width: 46px; height: 27px; border: none; cursor: pointer;
  border-radius: 999px; background: color-mix(in srgb, var(--ink) 20%, transparent);
  transition: background .18s;
}
.notif-toggle::after {
  content: ""; position: absolute; top: 3px; left: 3px; width: 21px; height: 21px;
  border-radius: 50%; background: #fff; transition: transform .18s;
  box-shadow: 0 1px 2px rgba(0,0,0,.25);
}
.notif-toggle.on { background: var(--amber); }
.notif-toggle.on::after { transform: translateX(19px); }

/* ---------- C) Personnaliser les alertes (panneau Mon compte) ---------- */
.pref-select {
  flex: none; max-width: 60%; font: inherit; font-size: 14px; color: var(--ink);
  background: var(--bg); border: 1.5px solid var(--line); border-radius: 10px;
  padding: 8px 12px; cursor: pointer;
}
.pref-select:focus { outline: none; border-color: var(--amber); }
.pref-interests-row { flex-direction: column; align-items: stretch; gap: 12px; }
/* Point 5) La rangée des centres d'intérêt s'empile (label au-dessus) et les chips
   occupent TOUTE la largeur de la carte → la ligne se remplit (PC comme mobile). */
.pref-interests-row .pref-chips { width: 100%; }
.pref-chips { display: flex; flex-wrap: wrap; gap: 8px; }
.pref-chip {
  border: 1.5px solid var(--line); background: transparent; color: var(--muted);
  font: inherit; font-size: 13px; font-weight: 600; border-radius: 999px;
  padding: 7px 14px; cursor: pointer; transition: background .15s, color .15s, border-color .15s;
}
.pref-chip:hover { border-color: var(--amber); color: var(--ink); }
.pref-chip.on { background: var(--amber); border-color: var(--amber); color: #fff; }
/* Point 5) 2e ligne de centres d'intérêt repliable — réutilise .chips-more
   (overflow/max-height/opacity + transition) ; ouverture animée depuis scrollHeight. */
.pref-chips-more { display: flex; flex-wrap: wrap; gap: 8px; flex-basis: 100%; margin-top: 0; }
.pref-chip-more { min-width: 40px; text-align: center; font-weight: 700; }
.pref-chip-more.on { background: var(--amber); border-color: var(--amber); color: #fff; }
.pref-feedback {
  font-size: 11px; font-weight: 700; letter-spacing: 0; text-transform: none;
  color: var(--green); opacity: 0; transition: opacity .2s; margin-left: 8px;
}
.pref-feedback.show { opacity: 1; }
.pref-feedback.err { color: var(--amber); }

/* Heures de veille : les deux selects d'heures alignés. */
.quiet-range { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.quiet-range label { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; color: var(--muted); font-weight: 600; }
.quiet-range .pref-select { max-width: none; }
#quiet-hours-row[hidden] { display: none; }

/* ---------- panneau « Mon compte » (mode connecté) ---------- */
.account-panel { margin: 6px auto 4rem; position: relative; }
/* Mosaïque : plus de carte géante — chaque rubrique est une petite carte (.acct-tile)
   posée dans une grille auto-fill, alignée en haut (hauteurs libres). */
.acct-mosaic {
  /* Respiration verticale entre rubriques, plus large que la gouttière latérale.
     Variable car le calage maçonnerie (site.js) la relit : en .is-masonry le
     row-gap CSS passe à 0 et l'écart est reporté dans le span de chaque tuile. */
  --acct-row-gap: 50px;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(min(340px, 100%), 1fr));
  column-gap: 18px; row-gap: var(--acct-row-gap); align-items: start; padding-bottom: 6px;
}
.acct-tile {
  background: var(--surface); border-radius: 24px; padding: 22px;
  box-shadow: 0 1px 3px rgba(30, 20, 50, .06);
  transition: box-shadow .2s; min-width: 0;
}
.acct-tile:hover { box-shadow: var(--shadow); }
/* Une tuile peut occuper toute la largeur (historique). */
.acct-tile-wide { grid-column: 1 / -1; }

/* Vue compte : la grille reste une VRAIE grille (2-3 colonnes selon la largeur),
   mais calée en MAÇONNERIE — les rubriques ont des hauteurs très inégales
   (« Personnaliser les alertes » est longue, l'identité et « Notifications » sont
   courtes) et, en rangées classiques, les tuiles courtes laissaient un grand vide
   jusqu'à la rangée suivante. Technique : rangées fines (4px) + chaque tuile
   occupe le nombre de rangées correspondant à sa hauteur (span posé par
   layoutAcctMosaic() dans site.js). La classe .is-masonry n'est ajoutée que
   lorsque le JS a mesuré : sans JS, la grille normale reste valide.
   grid masonry natif n'étant pas encore supporté partout, on garde ce calage. */
#acct-view-main .acct-mosaic.is-masonry {
  grid-auto-rows: 4px; row-gap: 0;
}
#acct-view-main .acct-mosaic.is-masonry .acct-tile { align-self: start; }
/* Dans une tuile, le .notif-card interne n'est plus une carte dans la carte :
   il se fond (pas de fond ni d'ombre, padding latéral repris par la tuile). */
.acct-tile .notif-card { background: transparent; box-shadow: none; border-radius: 0; padding: 0; }
/* Identité : bloc centré, mise en avant douce. */
.acct-tile-id { text-align: center; }
.acct-tile-id .acct-avatar { margin-inline: auto; }
.acct-collection-count {
  display: inline-block; margin-top: 8px;
  font-size: 13px; font-weight: 700; color: var(--amber); text-decoration: none;
}
.acct-collection-count:hover { text-decoration: underline; }
.acct-collection-count[hidden] { display: none; }
.acct-head .acct-name { font-size: 22px; font-weight: 800; letter-spacing: -0.02em; }
.acct-email { color: var(--muted); font-size: 14px; margin-top: 4px; word-break: break-all; }
/* Avatar à initiale : teinte violette de la charte, propre en clair comme en sombre. */
.acct-avatar {
  width: 52px; height: 52px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-family: 'Baloo 2', system-ui, sans-serif;
  font-size: 24px; font-weight: 700; line-height: 1; color: #fff;
  background: linear-gradient(135deg, #a567e3, #bd8ef0);
  box-shadow: 0 4px 14px rgba(165, 103, 227, .28);
  margin-bottom: 12px; user-select: none;
}
/* Vues du panneau (compte / historique / nous soutenir) : empilées, une seule visible. */
.acct-view { position: relative; }
.acct-view[hidden] { display: none; }
/* Retour depuis une vue secondaire (même repère visuel que le ↩ des cartes). */
.acct-view-back {
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; margin-bottom: 12px;
  border: none; border-radius: 999px; cursor: pointer;
  background: var(--surface); color: var(--muted); box-shadow: 0 1px 3px rgba(30, 20, 50, .06);
}
.acct-view-back:hover { color: var(--amber); }
.acct-section .section-label { margin-bottom: 12px; }
/* Pied du panneau : dernières actions, sous la mosaïque. */
.acct-foot { margin-top: 22px; max-width: 340px; }
/* B) Entrées de navigation au style notif-row (Mon historique, Proposer, Se déconnecter,
   Toutes les façons d'aider) : la ligne entière est cliquable, chevron à droite. */
.notif-nav {
  width: 100%; border: none; background: transparent; cursor: pointer;
  font: inherit; text-align: left; text-decoration: none; color: inherit;
}
.notif-chevron { flex: none; color: var(--muted); font-size: 16px; transition: color .15s, transform .15s; }
.notif-nav:hover .notif-txt strong { color: var(--amber); }
.notif-nav:hover .notif-chevron { color: var(--amber); }
/* B) « Nous soutenir » : la rangée peut passer les boutons de don à la ligne sur mobile. */
.acct-support-row { flex-wrap: wrap; }
/* Historique : hauteur libre dans sa tuile, plafonnée pour rester lisible. */
.acct-history-scroll { margin-top: 12px; max-height: 62vh; overflow-y: auto; }
.acct-support-lead { color: var(--muted); font-size: 14px; line-height: 1.6; margin-bottom: 16px; }

/* B2) Label fixe du panneau (hors du conteneur qui tourne) : style section-label,
   mais jamais renvoyé dans la marge gauche (pas la classe .section-label). */
.acct-panel-label {
  font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .14em;
  color: var(--amber); margin-bottom: 12px;
}
/* Sous-titres internes du recto (Mes sources, Nous soutenir). */
.acct-subhead {
  font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .1em;
  color: var(--muted); margin-bottom: 12px;
}

/* C) Retour aux alertes : vrai bouton pleine largeur (design system). */
.acct-return-btn {
  display: block; width: 100%; margin-top: 8px;
  border: none; background: var(--amber); color: #fff;
  font: inherit; font-weight: 700; font-size: 14px; border-radius: 12px;
  padding: 13px 0; cursor: pointer; text-align: center;
  box-shadow: 0 1px 3px rgba(30, 20, 50, .2); transition: background .15s, box-shadow .15s;
}
.acct-return-btn:hover { background: color-mix(in srgb, var(--amber) 88%, #000); box-shadow: 0 2px 6px rgba(30, 20, 50, .3); }

/* E) Suppression de compte : lien discret tout en bas du recto. */
.acct-delete-link {
  display: block; margin-top: 14px; align-self: flex-start;
  border: none; background: transparent; cursor: pointer; font: inherit;
  font-size: 12.5px; color: var(--muted); text-decoration: none; padding: 4px 0;
}
.acct-delete-link:hover { color: var(--ink); }

/* G) Liste « Mes sources ». */
.acct-src-list { list-style: none; display: flex; flex-direction: column; gap: 8px; }
.acct-src-item, .acct-src-link {
  display: flex; align-items: center; gap: 8px;
  font-size: 14px; color: var(--ink); text-decoration: none;
}
.acct-src-link:hover .acct-src-name { color: var(--amber); }
.acct-src-name { font-weight: 600; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.acct-src-status { flex: none; font-size: 11.5px; font-weight: 700; border-radius: 999px; padding: 3px 10px; }
.acct-src-status.ok { background: var(--green-soft); color: var(--green); }
.acct-src-status.pending { background: var(--line); color: var(--muted); }

/* Sélecteur segmenté à 3 positions (Apparence › Thème : Clair / Auto / Sombre).
   Même vocabulaire visuel que .notif-toggle : piste discrète, position active en
   ambre. Le segment porte la PRÉFÉRENCE, « Auto » compris. */
.pref-seg {
  flex: none; display: inline-flex; align-items: center; gap: 2px;
  background: color-mix(in srgb, var(--ink) 8%, transparent);
  border-radius: 999px; padding: 3px;
}
.pref-seg button {
  border: none; background: transparent; cursor: pointer; font: inherit;
  font-size: 13px; font-weight: 700; color: var(--muted);
  border-radius: 999px; padding: 7px 14px; transition: background .15s, color .15s;
}
.pref-seg button:hover { color: var(--ink); }
.pref-seg button.on { background: var(--amber); color: #fff; }
@media (max-width: 620px) { .pref-seg button { padding: 7px 11px; font-size: 12.5px; } }

/* @pseudo public (rubrique « Mon pseudo ») : identifiant stable, cliquable vers
   la page profil publique /u/:pseudo. */
.pref-at {
  flex: none; font-weight: 700; font-size: 14px; color: var(--amber); text-decoration: none;
  background: var(--amber-soft); border-radius: 999px; padding: 6px 14px;
}
.pref-at:hover { text-decoration: underline; }

/* Composant boutons de don — partagé avec /soutenir (même style, désactivé). */
.sup-actions { display: flex; gap: 10px; flex-wrap: wrap; }
.sup-btn {
  display: inline-flex; align-items: center; gap: 8px; border-radius: 999px;
  padding: 11px 20px; font-weight: 700; font-size: 14px; text-decoration: none;
  border: none; cursor: pointer; font-family: inherit;
}
.sup-btn.primary { background: var(--amber); color: #fff; }
.sup-btn.primary:hover { background: color-mix(in srgb, var(--amber) 88%, #000); }
.sup-btn.ghost { border: 1.5px solid var(--amber); color: var(--amber); background: transparent; }
.sup-btn.ghost:hover { background: var(--amber-soft); }
.sup-btn[disabled] { opacity: .5; cursor: default; background: var(--line); color: var(--muted); border: none; }
.sup-btn .soon { font-size: 12px; color: var(--muted); font-weight: 600; margin-left: 4px; }

/* Icône café du bouton Ko-fi : SVG inline (currentColor → hérite la couleur du
   bouton), vapeur animée en CSS pur, sans dépendance externe ni widget tiers. */
.sup-coffee { flex: none; }
.sup-coffee .steam { transform-origin: center; animation: sup-steam 2.4s ease-in-out infinite; }
.sup-coffee .steam-2 { animation-delay: .4s; }
.sup-coffee .steam-3 { animation-delay: .8s; }
@keyframes sup-steam {
  0%, 100% { opacity: .35; transform: translateY(1px); }
  50%      { opacity: 1;   transform: translateY(-1.5px); }
}
@media (prefers-reduced-motion: reduce) {
  .sup-coffee .steam { animation: none; opacity: .8; }
}

@media (max-width: 620px) {
  .acct-mosaic { --acct-row-gap: 28px; column-gap: 12px; }
  .acct-tile { padding: 20px 18px; border-radius: 20px; }
  .acct-foot { max-width: none; }
  /* Anti-débordement du panneau compte sur mobile : aucun élément ne doit pousser
     la carte au-delà de l'écran. On laisse le texte rétrécir (min-width:0 → il passe
     à la ligne au lieu d'imposer sa largeur) et on empêche les selects d'imposer une
     largeur ; les contrôles restent à droite (pas de repli forcé). */
  .account-panel .notif-txt { min-width: 0; }
  .account-panel .notif-sub { max-width: 100%; }
  .account-panel .quiet-range { min-width: 0; }
  .account-panel .pref-select { max-width: 100%; }
}

/* ---------- Abonnement paramétré (OpenAlert v2) ---------- */
/* F1) Rangée inline : chips suivies + « + ajouter » + sélecteur coulent ensemble
   (wrap naturel). Le bloc s'ancre en bas de la carte (margin-top:auto). */
.param-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: auto; }
/* Point 2) « + ajouter » : le form prend la PLACE du bouton dans le flux inline
   (flex: 0 0 auto → largeur de son contenu, jamais étiré ni renvoyé à la ligne). */
.param-form { display: inline-flex; flex-wrap: wrap; gap: 8px; align-items: center; flex: 0 0 auto; }
.param-form[hidden] { display: none; }
/* Point B) Le JS met bien add.hidden = true à l'ouverture du form (togglePicker), mais
   .param-add définit display:inline-flex (règle auteur) qui l'emportait sur la règle UA
   [hidden]{display:none} → le bouton restait visible À CÔTÉ du form. Ce miroir de
   .param-form[hidden] rétablit l'exclusion mutuelle stricte bouton⇄form. */
.param-add[hidden] { display: none !important; }
/* 10) Cycle « + ajouter » ⇄ formulaire : apparitions en fondu doux (le bouton se
   masque, le form prend sa place et s'élargit ; puis l'inverse après l'ajout). */
@keyframes param-swap-in { from { opacity: 0; transform: translateY(2px); } to { opacity: 1; transform: none; } }
.param-form:not([hidden]), .param-add:not([hidden]) { animation: param-swap-in .16s ease; }
@media (prefers-reduced-motion: reduce) { .param-form:not([hidden]), .param-add:not([hidden]) { animation: none; } }
/* H) Indicateur d'abonnement permanent des cartes paramétrées (comme les broadcast). */
.param-status { display: flex; align-items: center; }
.param-status .switch-label { font-size: 13px; }
.param-select, .param-input {
  flex: 1 1 150px; min-width: 130px; font: inherit; font-size: 13.5px; color: var(--ink);
  background: var(--surface); border: 1px solid var(--line);
  border-radius: 10px; padding: 10px 12px;
}
.param-select { cursor: pointer; }
.param-input:focus { outline: none; border-color: var(--amber); }
/* Format « carte a jouer » : dans une carte (ratio 5/7, 2 colonnes sur mobile ~360px),
   aucun controle de parametre ne doit depasser la largeur de la carte, sinon il
   empiete sur la carte voisine. On leve le plancher min-width (130/180px, plus large
   qu'une carte etroite) et on borne chaque controle a 100%. box-sizing:border-box est
   deja pose globalement (reset). Scope .card : la page de statut (.param-statut-label)
   garde ses propres largeurs. */
.card .param-form { min-width: 0; max-width: 100%; }
.card .param-select, .card .param-input { min-width: 0; max-width: 100%; }
.card .dyn-enum { min-width: 0; max-width: 100%; }
/* Combobox autocomplete (champ dynamic-enum : ma-collectivite…). Un seul champ visuel
   (.dyn-search) + liste ARIA de propositions (.dyn-listbox) en surimpression. Tokens/rayons
   du design-system : surface, focus violet, rayon contrôle 10px / panneau 12px. */
.dyn-enum { position: relative; flex: 1 1 200px; min-width: 180px; }
.dyn-search {
  width: 100%; box-sizing: border-box; font: inherit; font-size: 13.5px; color: var(--ink);
  background: var(--surface); border: 1px solid var(--line); border-radius: 10px; padding: 10px 12px;
}
.dyn-search:focus { outline: none; border-color: var(--amber); }
.dyn-listbox {
  position: absolute; z-index: 30; left: 0; right: 0; top: calc(100% + 4px);
  margin: 0; padding: 4px; list-style: none; max-height: 240px; overflow-y: auto;
  background: var(--surface); border: 1px solid var(--line); border-radius: 12px; box-shadow: var(--shadow);
}
.dyn-listbox[hidden] { display: none; }
.dyn-option {
  padding: 9px 11px; border-radius: 8px; font-size: 13.5px; color: var(--ink); cursor: pointer;
}
.dyn-option:hover, .dyn-option.active { background: var(--amber-soft); }
.dyn-status { font-size: 12px; color: var(--muted); margin-top: 4px; }
.param-follow { flex: none; padding: 11px 16px; }
.param-subform { display: none; } /* révélé en JS (parcours anonyme) */
.param-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.param-chip {
  display: inline-flex; align-items: center; gap: 7px;
  background: var(--amber-soft); color: var(--ink);
  border-radius: 999px; padding: 6px 8px 6px 11px; font-size: 12.5px; font-weight: 600;
}
.param-chip .pc-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--muted); opacity: .5; }
.param-chip .pc-dot.on { background: var(--green); opacity: 1; }
.param-remove {
  border: none; background: transparent; color: var(--muted); cursor: pointer;
  font-size: 13px; line-height: 1; padding: 2px 4px; border-radius: 6px;
}
.param-remove:hover { color: var(--amber); }
/* C3) Même gabarit qu'une .param-chip (rayon, padding, typo) mais FOND VIOLET pour
   la distinguer comme action d'ajout (et non comme une instance existante). */
/* V3 · « Configurer » (recto des cartes user-task) : MÊME pastille que « + ajouter »
   des cartes paramétrées — les deux rectos ont désormais la même structure.
   Sélecteur GROUPÉ et non réutilisation de la classe .param-add : celle-ci est câblée
   à un handler qui ouvre le picker et masque le bouton (site.js) — l'emprunter ferait
   disparaître « Configurer » au premier clic. */
.param-add, .task-config, .task-config-anon, .community-submit {
  display: inline-flex; align-items: center; gap: 7px;
  background: var(--amber); color: #fff; border: none; cursor: pointer;
  border-radius: 999px; padding: 6px 11px; font: inherit; font-size: 12.5px; font-weight: 600;
}
.param-add:hover, .task-config:hover, .task-config-anon:hover, .community-submit:hover { filter: brightness(1.06); }
.task-config-anon { text-decoration: none; } /* variante <a> (anonyme → /connexion) */

/* Cœur « j'aime » pour un visiteur SANS COMPTE : le like est dédupliqué par compte en
   base (source_likes), il n'existe donc plus de vote anonyme. Le compteur reste lisible
   (donnée publique) ; le bouton s'affiche en retrait et son clic mène à /connexion
   (site.js) — jamais un clic sans effet. Le curseur reste `pointer` : le bouton FAIT
   quelque chose, il emmène se connecter. */
.like-btn.like-anon { opacity: .55; }
.like-btn.like-anon:hover { opacity: .8; }

/* Carte communautaire (« Chat perdu ») : formulaire empilé (commune, description,
   lien, rayon, bouton), contrairement au .param-row générique en ligne — une
   description tient mal sur une seule ligne. Le bouton .community-submit reprend
   le style des autres actions d'une carte (.param-add/.task-config, ci-dessus). */
.community-form { flex-direction: column; align-items: stretch; width: 100%; }
/* .param-row impose display:flex (règle auteur) qui l'emporterait sur [hidden] (même
   piège déjà résolu pour .param-form/.param-follow-row, cf. plus bas) : sans ce
   rétablissement explicite, le sous-formulaire resterait visible en permanence malgré
   l'attribut hidden posé par défaut (cards.js) et retiré/reposé par le bouton toggle. */
.community-form[hidden] { display: none; }
.community-ville, .community-desc, .community-link, .community-radius {
  flex: 1 1 auto; width: 100%; min-width: 0; max-width: 100%; font: inherit; font-size: 13.5px;
  color: var(--ink); background: var(--surface); border: 1px solid var(--line);
  border-radius: 10px; padding: 10px 12px; resize: vertical;
}
.community-desc { min-height: 90px; }
.community-radius { cursor: pointer; }
.community-ville:focus, .community-desc:focus, .community-link:focus, .community-radius:focus {
  outline: none; border-color: var(--amber);
}
.community-submit { align-self: flex-start; }
/* Switch « S'abonner » d'une carte paramétrée : à droite du contrôle dans le picker,
   passe à la ligne sur carte étroite. Réutilise .switch/.switch-row existants. */
.param-follow-row { flex: 0 0 auto; }
/* .switch-row impose display:flex (règle auteur) qui l'emporte sur l'attribut UA [hidden].
   On rétablit explicitement le masquage : le switch « S'abonner » du picker est caché dès
   qu'une instance est suivie (l'interrupteur pause/reprise du bas prend le relais), pour ne
   pas faire doublon à la réouverture via « + ajouter ». */
.param-follow-row[hidden] { display: none !important; }
/* Non-géo : switch DISABLED tant que non abonné (l'activation se fait à la saisie) →
   grisé et non cliquable, repère visuel cohérent sans être une action sur du vide. */
.param-follow-row .switch input:disabled { cursor: default; }
.param-follow-row .switch input:disabled ~ .track,
.param-follow-row .switch input:disabled ~ .thumb { opacity: .5; }
.param-follow-row:has(input:disabled) .switch-label { opacity: .55; }
.param-statut-label { display: flex; align-items: center; gap: 8px; font-size: 14px; font-weight: 600; color: var(--ink); }
.param-statut-label .param-select { flex: none; min-width: 180px; }
.param-statut-hint { font-size: 13px; color: var(--muted); margin-top: 10px; }
.param-statut-hint a { color: var(--amber); font-weight: 600; text-decoration: none; }
/* Onglets d'instances de l'utilisateur sur la page de statut (volet 1). */
.dept-tabs { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 14px; }
.dept-tab {
  border: 1px solid var(--line); background: var(--surface); color: var(--muted);
  font: inherit; font-size: 13px; font-weight: 600; border-radius: 999px;
  padding: 8px 14px; cursor: pointer;
}
.dept-tab:hover { color: var(--ink); }
.dept-tab.on { background: var(--amber); color: #fff; border-color: transparent; }
.dept-tab.dept-other { color: var(--amber); border-style: dashed; }
.dept-other-form { margin-bottom: 14px; }
.dept-other-form[hidden] { display: none; }
/* Sonde dynamique (validateur /proposer, sources paramétrées). */
.probe-block { margin: 12px 0; padding: 12px 14px; border-radius: 10px; font-size: 13.5px; line-height: 1.5; }
.probe-block.ok { background: var(--green-soft); color: var(--ink); }
.probe-block.err { background: var(--amber-soft); color: var(--ink); }
.probe-block code { font-size: 12.5px; }

/* =====================================================================
   Vue mobile « kiosque » (≤ 720px). js/mobile-header.js pose body.m-kiosk
   et relocalise UNIQUEMENT la barre de recherche dans le header (rangée dédiée
   sous le titre). Les puces de catégories RESTENT au-dessus de la grille.
   ===================================================================== */
@media (max-width: 720px) {

  /* Fix débordement horizontal : le halo .hero::before (640px, décalé vers la
     droite par son animation) dépassait le viewport → marge vide à droite +
     scroll horizontal. overflow-x: clip coupe le débord SANS créer de conteneur
     de défilement (préserve le position:sticky du header, contrairement à hidden). */
  html, body { overflow-x: clip; }

  /* En-tête mobile façon leboncoin, 3 rangées :
       Rangée 1 : hamburger (gauche) + titre centré (spacer à droite pour l'équilibre).
       Rangée 2 : barre de recherche pleine largeur.
       Rangée 3 : rail horizontal de catégories (carrousel scrollable au doigt).
     KPI, connexion/compte et « Déposer » migrent dans le menu plein écran (#m-menu). */
  body.m-kiosk header .nav {
    position: relative; height: auto; flex-wrap: wrap;
    row-gap: 12px; padding-top: 10px; padding-bottom: 12px;
  }

  /* Rangée 1 : hamburger rétabli à gauche, titre centré, spacer symétrique à droite. */
  body.m-kiosk .nav-toggle { display: inline-flex !important; order: 0; flex: 0 0 42px; margin: 0; }
  body.m-kiosk .brand { order: 1; flex: 1 1 auto; justify-content: center; font-size: 1.7rem; }
  body.m-kiosk .m-nav-spacer { display: block; order: 2; flex: 0 0 42px; }

  /* Éléments migrés dans le menu plein écran : retirés du header mobile. */
  body.m-kiosk .btn-deposit,
  body.m-kiosk .nav-right { display: none !important; }

  /* Point 5) Pages secondaires (hors home) : header sur UNE seule ligne =
     hamburger à gauche + barre de recherche. Le logo (accessible depuis le menu)
     et le spacer sont masqués pour tenir sur une ligne sans retour. */
  body.m-secondary header .nav { flex-wrap: nowrap; row-gap: 0; }
  body.m-secondary.m-kiosk header .brand,
  body.m-secondary.m-kiosk .m-nav-spacer { display: none; }
  body.m-secondary.m-kiosk header .nav > .search {
    order: 2; flex: 1 1 auto; margin-left: 10px;
  }
  /* Le rail de catégories injecté par header.js est un ajout DESKTOP : en mobile les
     pages secondaires gardent leur en-tête à une seule ligne (hamburger + recherche),
     les catégories restant accessibles par le menu plein écran.
     `.m-secondary.m-kiosk` (et non `.m-secondary` seul) : il faut repasser devant
     `body.m-kiosk header .nav .toolbar { display: block }` déclaré plus bas dans ce
     même bloc — à spécificité égale, c'est la dernière règle qui gagnerait. */
  body.m-secondary.m-kiosk header .nav .toolbar,
  body.m-secondary.m-kiosk header .nav .chips-secondary-wrap { display: none; }

  /* Rangée 2 : recherche (loupe à gauche, pas de bouton — inchangé). */
  body.m-kiosk header .nav > .search { order: 3; flex: 1 1 100%; min-width: 0; margin: 0; }

  /* Rangée 3 : rail de catégories — un seul groupe scrollable horizontalement.
     Toutes les puces (primaires + secondaires) sont fusionnées ici par renderChips
     (mobile) ; le « + » et .chips-secondary-wrap ne servent plus que sur desktop. */
  body.m-kiosk header .nav .toolbar { order: 4; flex: 1 1 100%; margin: 0; display: block; }
  body.m-kiosk header .nav .toolbar .chips { width: 100%; }
  body.m-kiosk header .nav .toolbar .chips .chips-row {
    flex-wrap: nowrap; overflow-x: auto; overflow-y: hidden;
    -webkit-overflow-scrolling: touch; scroll-snap-type: x proximity;
    scrollbar-width: none; padding-bottom: 2px;
    /* fondu de bord droit : signale qu'il reste des catégories à swiper */
    -webkit-mask-image: linear-gradient(to right, #000 88%, transparent 100%);
    mask-image: linear-gradient(to right, #000 88%, transparent 100%);
  }
  body.m-kiosk header .nav .toolbar .chips .chips-row::-webkit-scrollbar { display: none; }
  body.m-kiosk header .nav .toolbar .chip-f { flex: 0 0 auto; white-space: nowrap; scroll-snap-align: start; }
  body.m-kiosk header .nav .chips-secondary-wrap { display: none; }

  /* « Le kiosque » masqué. */
  body.m-kiosk main#alertes > .section-label { display: none; }

  /* Format « carte a jouer » sur mobile : le recto garde son ratio 5/7 (voir
     .grid .card-front), donc la carte NE se replie plus au tap. La description et
     l'etat restent affiches en permanence (description tronquee/fondue via
     -webkit-line-clamp) pour remplir la carte. L'ancien repli max-height:0 +
     .revealed devient sans objet ; le texte complet reste au verso (bouton « i »).
     Le handler .revealed (mobile-header.js) subsiste mais est inerte ici. */
  body.m-kiosk .grid .card .card-front > p,
  body.m-kiosk .grid .card .card-front > .state {
    max-height: none; opacity: 1;
  }

  /* E2) Header condensé au scroll : le titre et les catégories disparaissent, mais
     le HAMBURGER reste et la recherche (réduite) le rejoint sur UNE seule rangée
     compacte → header plus petit. Transition de taille fluide sur la recherche. */
  body.m-kiosk .brand,
  body.m-kiosk .m-nav-spacer,
  body.m-kiosk header .nav .toolbar {
    overflow: hidden;
    transition: max-height .3s ease, max-width .3s ease, opacity .25s ease, margin .3s ease, padding .3s ease;
    max-height: 60px; opacity: 1;
  }
  body.m-kiosk header .nav,
  body.m-kiosk header .nav > .search {
    transition: padding .25s ease, box-shadow .2s;
  }
  /* Titre + catégories : repliés à taille nulle (largeur ET hauteur) pour libérer la
     rangée ; le hamburger n'est PAS masqué. */
  body.m-kiosk.m-scrolled header .brand,
  body.m-kiosk.m-scrolled .m-nav-spacer,
  body.m-kiosk.m-scrolled header .nav .toolbar {
    max-height: 0; max-width: 0; opacity: 0; margin: 0; padding: 0;
    flex-basis: 0; flex-grow: 0;
  }
  /* Rangée compacte : hamburger + recherche. B6) la recherche garde EXACTEMENT sa
     hauteur de repos (aucune réduction de padding vertical) ; seules les rangées 1
     et 3 se replient. */
  body.m-kiosk.m-scrolled header .nav { padding-top: 6px; padding-bottom: 6px; row-gap: 0; }
  body.m-kiosk.m-scrolled header .nav > .search { flex: 1 1 auto; }
  @media (prefers-reduced-motion: reduce) {
    body.m-kiosk .brand, body.m-kiosk .m-nav-spacer,
    body.m-kiosk header .nav .toolbar,
    body.m-kiosk header .nav, body.m-kiosk header .nav > .search { transition: none; }
  }

  /* E3) iPhone SE (~≤375px) : police des catégories réduite. */
  @media (max-width: 375px) {
    body.m-kiosk .chip-f { font-size: 12px; padding: 7px 12px; }
  }
}

/* F) ============ En-tête façon leboncoin — DESKTOP (home uniquement) ============
   Sur la home (body.m-kiosk), au-dessus de 720px, l'en-tête est sur UNE seule
   rangée : [logo] [Déposer] [recherche flexible] [Mes alertes] [Se connecter]
   [thème] [KPI]. Les puces de catégories RESTENT au-dessus de la grille (.toolbar).
   Les autres pages n'ont pas body.m-kiosk → intactes. */
@media (min-width: 721px) {
  /* 721–1023px (tablette) : le header s'enroule, la recherche occupe sa propre
     rangée pleine largeur sous le logo/menu (évite tout débordement). */
  body.m-kiosk header .nav {
    height: auto; flex-wrap: wrap; align-items: center; gap: 12px;
    padding: 12px 0; row-gap: 10px;
  }
  body.m-kiosk .brand { font-size: 30px; flex: none; }
  body.m-kiosk .btn-deposit { order: 1; flex: none; }
  body.m-kiosk header .nav-right { order: 2; flex: none; }
  body.m-kiosk header .nav > .search {
    order: 5; flex: 1 1 100%; max-width: none; min-width: 0; margin: 2px 0 0;
    padding: 3px 3px 5px 18px; gap: 0;
  }
  /* Point 2) Rangée 2 : puces de catégories, centrées, DANS le header. */
  body.m-kiosk header .nav .toolbar {
    order: 6; flex: 1 1 100%; margin: 4px 0 0;
    display: flex; flex-direction: column; align-items: center; gap: 8px;
  }
  body.m-kiosk header .nav .toolbar .chips { width: 100%; }
  body.m-kiosk header .nav .toolbar .chips .chips-row { justify-content: center; }
  body.m-kiosk header .nav .chips-secondary-wrap { order: 7; flex: 1 1 100%; }
  body.m-kiosk header .nav .chips-secondary-wrap .chips-row { justify-content: center; }

  /* Point 6) Desktop : plus de loupe à gauche, bouton de recherche accolé à droite
     (fond accent, loupe blanche, coins épousant la pilule). */
  body.m-kiosk header .nav > .search .search-ic { display: none; }
  body.m-kiosk header .nav > .search .search-go {
    display: inline-flex; align-items: center; justify-content: center; flex: none;
    width: 34px; height: 34px; margin-left: 8px; border: none; cursor: pointer;
    background: var(--amber); color: #fff; border-radius: 999px;
    transition: background .15s;
  }
  body.m-kiosk header .nav > .search .search-go:hover { background: color-mix(in srgb, var(--amber) 88%, #000); }
  body.m-kiosk header .nav > .search .search-go svg { display: block; }

  /* Point 2) KPI compact dans le header : pastille + nombre uniquement. */
  body.m-kiosk .nav-right .kpi { margin-top: 0; position: relative; gap: 6px; padding: 6px 12px; }
  body.m-kiosk .nav-right .kpi #kpi-text { display: none; }
}

/* Point 1) ≥1024px : en-tête sur UNE seule rangée façon leboncoin.
   [logo] [Déposer] [recherche flex:1 max 420px] [nav-right (Mes alertes / Se
   connecter / thème / KPI)]. */
@media (min-width: 1024px) {
  /* Rangée 1 sur une seule ligne (recherche rejoint le logo/nav-right) ; les
     catégories restent en rangée 2 (flex-basis 100% → passent à la ligne). */
  /* La recherche étant plafonnée (300px), il reste de l'espace libre dans la rangée :
     avec le `justify-content: space-between` de .nav, cet excédent se répartissait
     dans TOUS les interstices — d'où des écarts imprévisibles qu'on ne pouvait
     rattraper qu'à coups de marges négatives. On passe donc en `flex-start` : les
     écarts valent alors exactement le `gap` (+ marges explicites), et c'est
     `.nav-right` qui absorbe tout le vide via `margin-left: auto` pour rester collé
     au bord droit.
     Écarts de la rangée 1, désormais déterministes :
       logo → « Déposer »      = 26px (marge du logo) + 10px (gap)
       « Déposer » → recherche = 10px (le gap seul)
     NB : ne PAS donner `margin-right: auto` au logo — quand la rangée est bien
     remplie il ne reste presque rien à distribuer et le logo se retrouve collé au
     bouton. Une marge explicite tient dans tous les cas. */
  body.m-kiosk header .nav {
    height: auto; flex-wrap: wrap; gap: 10px; padding: 10px 0;
    justify-content: flex-start;
  }
  body.m-kiosk .brand { font-size: 35px; margin-right: 60px; }
  body.m-kiosk header .nav > .search {
    order: 2; flex: 1 1 auto; max-width: 300px; min-width: 140px; margin: 0;
  }
  body.m-kiosk header .nav-right { order: 3; margin-left: auto; }
  body.m-kiosk header .nav .toolbar { margin-top: 10px; }
}

/* Point 1) Survol : le compteur s'étire vers la DROITE en OVERLAY depuis l'icône
   (point de départ fixe), révélant le texte complet SANS déplacer les voisins de
   gauche. L'espace de débordement à droite est garanti par header/.nav overflow
   visible + html/body overflow-x: clip (aucun scroll horizontal). Désactivé sur
   tactile et < 1024px. */
@media (hover: hover) and (min-width: 1024px) {
  /* Espace de débordement réservé À DROITE : la réserve vaut le déficit entre la
     largeur du texte étendu (~300px) et la place disponible jusqu'au bord droit
     ((100vw-1060)/2 + demi-KPI). Nulle sur très grands écrans (KPI alors flush à
     droite), plafonnée à 140px pour ne pas creuser un vide sur écran étroit. Le
     point de départ (icône + chiffre) reste ainsi fixe et le texte n'est pas coupé. */
  body.m-kiosk .nav-right .kpi {
    margin-right: max(0px, min(140px, calc(256px - (100vw - 1060px) / 2)));
  }
  body.m-kiosk .nav-right .kpi #kpi-text {
    display: flex; align-items: center; white-space: nowrap;
    position: absolute; top: 0; left: 0; height: 100%;
    max-width: 0; overflow: hidden; opacity: 0;
    background: var(--surface); border-radius: 999px; padding: 0; font-size: 12.5px;
    box-shadow: 0 2px 12px rgba(30, 20, 50, .18);
    transition: max-width .35s ease, opacity .25s ease, padding .3s ease;
    pointer-events: none; z-index: 1;
  }
  /* Le déploiement part de l'icône fixe (padding-left réserve la place du « ● N »)
     et pousse le texte vers la droite. */
  body.m-kiosk .nav-right .kpi:hover #kpi-text { max-width: 300px; opacity: 1; padding: 0 16px 0 46px; }
  /* Icône + chiffre restent visibles, au-dessus de l'overlay, ancrés à gauche. */
  body.m-kiosk .nav-right .kpi #kpi-dot,
  body.m-kiosk .nav-right .kpi #kpi-num { position: relative; z-index: 2; }
}

/* F3) titre en mobile (home). */
@media (max-width: 720px) {
  body.m-kiosk .brand { font-size: 1.7rem; }
}

/* Point 3) Header RIGOUREUSEMENT identique en tout contexte (home connecté, panneau
   compte, home non connecté) : aucun masquage conditionnel de la recherche/des
   catégories/du KPI quand le panneau « Mon compte » est ouvert. Les deux seules
   différences autorisées sont gérées ailleurs :
     · « Se connecter » ⇄ « Mon compte » (texte/lien du bouton, via session.js) ;
     · le KPI n'apparaît QUE connecté (règle ci-dessous) — sinon retiré du flux,
       les voisins se réajustent naturellement (flex/gap), aucun trou. */
body:not([data-mode="connected"]) .nav-right .kpi { display: none; }

/* ===================== KPI du header = pastille de notification sur la cloche =====================
   Dans le HEADER uniquement (le .kpi du hero garde sa forme de pilule), le compteur
   « alertes actives » est relocalisé DANS le lien « Mes alertes » (mobile-header.js)
   et devient une pastille verte chiffrée superposée à la cloche, façon notification.
   - le halo vert qui s'agrandit (`ping`, mutualisé avec .dot-live) est conservé ;
   - tout le reste (pastille ●, libellé au survol, overlay qui s'étire) disparaît ;
   - 0 alerte active (data-active != "true") → pas de pastille du tout.
   Sélecteurs préfixés `header` pour repasser devant les règles `body.m-kiosk .nav-right .kpi`
   des media queries ≥721px / ≥1024px déclarées plus haut. */
header .nav-right .mine-link .kpi {
  /* Ancrée sur la cloche, pas sur la boîte du lien : `right` compense le padding
     horizontal de 5px de .hd-link (sinon la pastille s'éloignerait de l'icône). */
  position: absolute; top: -7px; right: -4px; z-index: 3;
  margin: 0; padding: 0 4px; gap: 0;
  min-width: 18px; height: 18px; border-radius: 999px;
  background: var(--green); border: none; box-shadow: none;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 11px; line-height: 1; pointer-events: none;
}
header .nav-right .mine-link .kpi #kpi-num { color: #fff; font-weight: 700; }
header .nav-right .mine-link .kpi #kpi-dot,
header .nav-right .mine-link .kpi #kpi-text { display: none !important; }
/* Halo qui s'agrandit — même keyframes que .dot-live, à l'échelle de la pastille. */
/* E2) L'anneau vert flottant (ping) passait pour un artefact d'affichage : la pastille
   est desormais PLEINE et STATIQUE, sans anneau ni animation. */
/* Aucune alerte active → aucune pastille. */
header .nav-right .mine-link .kpi:not([data-active="true"]) { display: none; }
/* E1) Etat ACTIF des deux entrees de filtre du header (cloche = « Mes alertes actives »,
   coeur = « Mes favoris ») : surlignage quand le kiosque est filtre sur ce mode. La classe
   .is-active est posee par markActiveChip() (site.js), seule source de verite du filtre. */
.nav-right .hd-link.is-active {
  color: var(--amber); background: var(--amber-soft); border-radius: 10px;
}
.nav-right .hd-link.is-active .hd-ic-base { opacity: 0; }
.nav-right .hd-link.is-active .hd-ic-fill { opacity: 1; }

/* Connecté : « Mon compte » n'est plus une pilule accent mais un avatar rond — on
   neutralise le fond/padding de .btn-login (y compris ses variantes mobiles déclarées
   plus haut). Déclaré ici, en fin de feuille, pour passer après toutes les media queries. */
.btn-login.hd-avatar-link,
.nav-right a.btn-login.hd-avatar-link,
.btn-login.hd-avatar-link:hover,
.nav-right a.btn-login.hd-avatar-link:hover {
  background: none; border: none; padding: 0px; margin: 0;
  border-radius: 50%; color: var(--muted); line-height: 0;
}
/* `hidden` doit rester prioritaire sur le display:inline-flex de .hd-link
   (session.js masque « Mes alertes » aux visiteurs anonymes). */
.hd-link[hidden] { display: none; }

/* « Bonjour <prénom> » (hero) ne s'affiche que sur la page principale : masqué dès
   que le panneau « Mon compte » (et ses faces historique/soutenir) est ouvert — ce
   panneau porte déjà sa propre salutation. */
body.account-open .hero { display: none; }

/* ============ Menu mobile plein écran (home ≤720px, js/mobile-header.js) ============ */
/* Spacer de la rangée 1 : n'existe que sur mobile (règle plus haut le rend block). */
.m-nav-spacer { display: none; }

.m-menu[hidden] { display: none; }
.m-menu {
  position: fixed; inset: 0; z-index: 200; background: var(--bg);
  /* E) Le défilement se fait DANS la face (enfant), pas sur le conteneur retourné en 3D
     — évite le blocage de scroll tactile constaté quand les deux scrollaient. */
  overflow: hidden;
  opacity: 1; transform-origin: left center;
  transition: opacity .35s ease, transform .5s cubic-bezier(.2, .7, .2, 1);
}
/* Ouverture/fermeture : net retournement de carte (rotateY avec perspective, même
   vocabulaire que le flip des cartes du kiosque) + fondu, ~500ms. */
.m-menu.m-anim { opacity: 0; transform: perspective(1200px) rotateY(-82deg); }
.m-menu.m-anim.open { opacity: 1; transform: perspective(1200px) rotateY(0deg); }
.m-menu-inner {
  /* Point 8) Le conteneur de défilement est .m-menu-face (bornée au viewport grâce à
     .m-menu-flip{height:100%}). L'inner n'est QUE le contenu : min-height:100% pour que
     .m-menu-foot{margin-top:auto} reste en bas quand le menu est court, et il grandit
     au-delà quand le contenu est long → c'est la face qui défile (un seul conteneur de
     scroll, pas d'imbrication fragile). */
  min-height: 100%; display: flex; flex-direction: column;
  padding: 10px 22px calc(24px + env(safe-area-inset-bottom, 0px));
}
/* Titre centré et à la même hauteur que dans le header (pas de « saut » de titre) ;
   le ✕ est ancré à gauche, là où était le hamburger. */
.m-menu-head { position: relative; display: flex; align-items: center; justify-content: center; height: 42px; }
.m-menu-logo { font-size: 1.7rem; pointer-events: none; }
.m-menu-close {
  position: absolute; left: -6px; top: 50%; transform: translateY(-50%);
  border: none; background: transparent; color: var(--ink); cursor: pointer;
  font-size: 22px; line-height: 1; padding: 8px; border-radius: 8px;
}
.m-menu-close:hover { color: var(--amber); }
.m-menu-list { display: flex; flex-direction: column; margin-top: 8px; }
.m-menu-list a, .m-menu-list .m-link {
  display: block; width: 100%; text-align: left; text-decoration: none;
  font: inherit; font-size: 17px; font-weight: 600; color: var(--ink);
  background: none; border: none; cursor: pointer; padding: 14px 2px;
  -webkit-tap-highlight-color: transparent;
}
.m-menu-list a:hover, .m-menu-list .m-link:hover { color: var(--amber); }
.m-menu-list a.m-primary, .m-menu-list button.m-primary { color: var(--amber); }   /* entrée principale en accent */
/* B3) « Se déconnecter » : discret (muted), séparé du reste par un filet. */
.m-menu-list .m-logout { color: var(--muted); margin-top: 6px; border-top: 1px solid color-mix(in srgb, var(--muted) 28%, transparent); padding-top: 16px; }
.m-menu-list .m-logout:hover { color: var(--amber); }
/* Entrées « bientôt » : grisées, non cliquables, badge (réutilise le libellé .soon). */
.m-menu-list .m-soon, .m-menu-list .m-soon:hover {
  color: var(--muted); cursor: default; display: flex; align-items: center;
}
.m-menu-list .m-soon .soon {
  font-size: 11px; font-weight: 700; color: var(--muted); background: var(--line);
  border-radius: 999px; padding: 2px 8px; margin-left: 8px;
  text-transform: none; letter-spacing: 0;
}
/* Séparateurs visibles (—line est transparent) : trait fin adapté au thème. */
.m-menu-sep { border: none; border-top: 1px solid color-mix(in srgb, var(--muted) 28%, transparent); margin: 8px 0; }
.m-menu-foot { margin-top: auto; padding-top: 18px; display: flex; align-items: center; gap: 12px; }
.m-menu-foot .theme-btn { transition: transform .4s ease; }
.m-menu-foot .m-theme-label { font-size: 14px; color: var(--muted); font-weight: 600; }
@media (prefers-reduced-motion: reduce) { .m-menu { transition: none; } }
/* Jamais de menu plein écran sur desktop (le hamburger y est masqué de toute façon). */
@media (min-width: 721px) { .m-menu { display: none !important; } }
/* Body verrouillé pendant l'ouverture du menu (pas de scroll de l'arrière-plan). */
body.m-menu-open { overflow: hidden; }

/* ================================================================
   COLLECTIONS — étagère du kiosque + page /collection/:slug
   ================================================================ */
.section-hint { color: var(--muted); font-weight: 400; font-size: .82em; }

/* Étagère horizontale de packs, en tête du kiosque. */
/* E3) Clairance verticale : les feuilles inclinées de .pack-stack débordent d'~10px
   sous la tuile → padding-bas suffisant dans le rail (qui clippe en Y à cause de
   overflow-x) + marge-bas franche pour ne plus chevaucher le label du kiosque.
   E4) max-height + opacity animés → apparition/disparition fluides selon le filtre. */
.collections-shelf {
  position: relative; /* B) « Les decks » se pin dans l'étagère, pas dans le main */
  margin: 6px 0 30px; overflow: hidden;
  max-height: 360px; opacity: 1;
  transition: opacity .3s ease, max-height .38s ease, margin .38s ease;
}
.collections-shelf.shelf-hidden {
  max-height: 0; opacity: 0; margin-top: 0; margin-bottom: 0; pointer-events: none;
}
/* B) « Le kiosque » + toolbar groupés (le label se pin dans ce bloc). */
.kiosk-head { position: relative; }
.shelf-row {
  display: flex; gap: 16px; overflow-x: auto; padding: 12px 2px 26px;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none; /* E2 : aucune scrollbar visible (comme le rail catégories) */
  /* B) carrousel infini : plus de scroll-snap (défilement continu) + fondu des deux bords. */
  -webkit-mask-image: linear-gradient(to right, transparent 0, #000 26px, #000 calc(100% - 26px), transparent 100%);
  mask-image: linear-gradient(to right, transparent 0, #000 26px, #000 calc(100% - 26px), transparent 100%);
}
.shelf-row::-webkit-scrollbar { display: none; }

/* Un pack = « paquet de cartes » : tuile avec une petite pile derrière (éventail). */
.pack {
  position: relative; flex: 0 0 auto; width: 190px; min-height: 118px;
  display: block; text-decoration: none; scroll-snap-align: start;
  background: var(--surface); color: var(--ink);
  border-radius: 16px; padding: 16px 16px 14px;
  box-shadow: var(--shadow);
  transition: transform .16s ease, box-shadow .16s ease;
}
/* 3) Toutes les surfaces d'une tuile de deck montrent la main au survol. */
.pack, .pack *, .deck-tile, .deck-tile * { cursor: pointer; }
/* La pile derrière la tuile (deux feuilles décalées) = effet paquet. */
.pack-stack { position: absolute; inset: 0; z-index: -1; border-radius: 16px; }
.pack-stack::before, .pack-stack::after {
  content: ""; position: absolute; inset: 0; border-radius: 16px;
  background: var(--surface); box-shadow: var(--shadow);
}
.pack-stack::before { transform: translate(6px, 6px) rotate(2.2deg); opacity: .55; }
.pack-stack::after  { transform: translate(11px, 10px) rotate(4.2deg); opacity: .3; }
.pack:hover { transform: translateY(-3px); box-shadow: 0 6px 22px rgba(90, 60, 150, .22); }
.pack:focus-visible { outline: 2px solid var(--amber); outline-offset: 3px; }
.pack-body { display: flex; flex-direction: column; gap: 4px; }
.pack-name { font-weight: 700; font-size: 1.02rem; letter-spacing: -.01em; }
.pack-meta { color: var(--muted); font-size: .82rem; display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.pack-likes { color: var(--amber); font-weight: 600; }

/* Page collection. */
.coll-main { padding-top: 26px; }
/* Page « Mes favoris » : le body est un flex-column et .wrap (margin:0 auto) est
   un flex-item qui, sans contenu large pour l'ouvrir (contrairement au kiosque avec
   hero/recherche/stats), se replie en shrink-to-fit -> la grille tombe a 1 colonne.
   width:100% force le main a occuper la largeur dispo (plafonnee par .wrap max-width),
   ce qui rend a la grille son auto-fill 4-6 colonnes. Mobile inchange (.wrap = 100%). */
.favoris-main { width: 100%; }
/* Grille favoris : plafonnee a 4 colonnes MAX sur desktop (meme principe que le kiosque,
   dont le plancher de 300px le limite a 3). La grille utile plafonne a ~1192px (wrap 1240
   - padding) ; un plancher de 230px empeche l'auto-fill de placer une 5e colonne
   (5*230+4*18 = 1222 > 1192) tout en gardant 4 colonnes des ~1024px (4*230+3*18 = 974).
   La carte garde son max-width 300px (centree). Mobile <=620px : 1 colonne (regle .grid). */
@media (min-width: 621px) {
  .favoris-main .grid { grid-template-columns: repeat(auto-fill, minmax(min(230px, 100%), 1fr)); }
}
.coll-head { position: relative; margin-bottom: 22px; }
.coll-emoji { font-size: 2.2rem; line-height: 1; }
.coll-head h1 { margin: 0; font-size: 1.7rem; letter-spacing: -.02em; }
.coll-meta { display: flex; gap: 14px; align-items: center; color: var(--muted); font-size: .9rem; margin: 6px 0 16px; }
.coll-likes { color: var(--amber); font-weight: 600; }
.coll-actions { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; }
.coll-adopt {
  background: var(--amber); color: #fff; border: none; cursor: pointer;
  font-weight: 700; font-size: 1rem; padding: 12px 22px; border-radius: 999px;
  box-shadow: var(--shadow); transition: transform .14s ease, filter .14s ease;
}
.coll-adopt:hover { filter: brightness(1.05); transform: translateY(-1px); }
.coll-adopt:disabled { opacity: .6; cursor: default; transform: none; }
.coll-adopt-msg { margin-top: 14px; font-weight: 600; font-size: .92rem; }
.coll-adopt-msg.ok { color: var(--green); }
.coll-adopt-msg.info { color: var(--muted); }
.coll-adopt-msg.err { color: var(--danger); }
.coll-share-grid { margin-top: 14px; }

@media (max-width: 520px) {
  .pack { width: 168px; }
  .coll-head h1 { font-size: 1.4rem; }
  /* Point 4) La flèche retour est trop basse sur mobile (top:22px la décrochait du
     titre) : on la remonte pour l'aligner sur la 1re ligne du titre. */
  .coll-head .src-back { top: 12px; right: 8px; }
}

/* ===================== DECK PARTAGÉ (phase 2 UGC) ===================== */
.deck-byline { display: flex; flex-direction: column; gap: 2px; margin: 4px 0 6px; }
.deck-author { color: var(--ink); font-weight: 600; font-size: .95rem; }
.deck-forked { color: var(--muted); font-size: .85rem; }
.deck-report { display: flex; gap: 16px; align-items: center; flex-wrap: wrap; margin-top: 18px; }
.deck-report-link {
  background: none; border: none; padding: 0; cursor: pointer;
  color: var(--muted); font-size: .82rem; text-decoration: underline;
  text-underline-offset: 2px;
}
.deck-report-link:hover { color: var(--danger); }
.deck-report-sm { font-size: .78rem; opacity: .8; }

/* ===================== MES DECKS (phase 2 UGC) ===================== */
.decks-main { padding-top: 26px; width: 100%; } /* width:100% : meme fix shrink-to-fit que main#alertes */
.decks-main h1 { margin: 0 0 16px; } /* typographie via .page-title (1) */
.decks-anon { text-align: center; padding: 40px 0; }
.decks-anon .src-desc { margin: 8px auto 20px; max-width: 460px; }

/* Nom public (pseudo). */
.deck-pseudo { margin-bottom: 26px; }
.deck-pseudo-line { color: var(--muted); font-size: .92rem; }
.deck-pseudo-form label { display: block; font-size: .9rem; color: var(--muted); margin-bottom: 8px; }
.deck-pseudo-row { display: flex; gap: 10px; flex-wrap: wrap; }
.deck-pseudo-row input {
  flex: 1 1 220px; min-width: 0; font: inherit; font-size: 15px; color: var(--ink);
  background: var(--bg); border: 1.5px solid var(--line); border-radius: 10px; padding: 10px 14px;
}
.deck-pseudo-row input:focus { outline: none; border-color: var(--amber); }

/* Liste des decks. */
.deck-list { padding: 6px 22px; margin-bottom: 16px; }
.deck-row {
  width: 100%; border: none; background: transparent; cursor: pointer;
  font: inherit; text-align: left; color: inherit;
}
.deck-row:hover .notif-txt strong { color: var(--amber); }
.deck-row-emoji { flex: none; font-size: 1.5rem; line-height: 1; }
.deck-badge {
  flex: none; font-size: 12px; font-weight: 700; border-radius: 999px; padding: 4px 11px;
  color: var(--muted); background: color-mix(in srgb, var(--ink) 8%, transparent);
}
.deck-badge-shared { color: #fff; background: var(--amber); }
.deck-list-actions { margin-top: 18px; }
.deck-create-btn { font-size: .95rem; padding: 11px 20px; }
.deck-create-btn[disabled] { opacity: .5; cursor: not-allowed; pointer-events: none; }
/* En-tête de la liste : libellé « Vos decks » + compteur de quota aligné à droite. */
.deck-list-head { display: flex; align-items: baseline; justify-content: flex-start; gap: 12px; }
.deck-list-head .section-label { margin-bottom: 14px; }
.deck-count { font-size: .82rem; font-weight: 600; color: var(--muted); white-space: nowrap; }
.deck-count.full { color: var(--danger); }
/* Bloc « Cartes de ce deck » du formulaire de création depuis une carte. */
.deck-seed-grid { margin-top: 10px; }

/* G) Grille de tuiles de decks façon étagère de la home (motif + teinte). */
.deck-tiles { display: flex; flex-wrap: wrap; gap: 16px; margin-bottom: 8px; }
.deck-tile { width: 190px; min-height: 118px; text-align: left; }
.deck-tile .pack-meta { justify-content: flex-start; }
.deck-tile .deck-badge { margin-left: 2px; }
@media (max-width: 520px) { .deck-tile { width: 100%; } }
/* G) Sur cette page, le label de section reste dans le flux (pas renvoyé au gutter) :
   il accompagne la grille des tuiles, pas le haut de page. */
@media (min-width: 1280px) {
  body:not(.dev) main.decks-main .section-label {
    position: static; text-align: left; left: auto; width: auto; margin: 0 0 12px;
  }
}

/* Formulaire créer / éditer. */
.deck-back {
  border: none; background: transparent; cursor: pointer; font: inherit;
  color: var(--muted); font-size: .9rem; padding: 0 0 12px; display: inline-block;
}
.deck-back:hover { color: var(--amber); }
.deck-form label { display: block; font-size: .9rem; font-weight: 600; color: var(--ink); margin: 16px 0 6px; }
.deck-form input, .deck-form textarea {
  width: 100%; font: inherit; font-size: 15px; color: var(--ink);
  background: var(--bg); border: 1.5px solid var(--line); border-radius: 10px; padding: 10px 14px;
}
.deck-form textarea { resize: vertical; }
.deck-form input:focus, .deck-form textarea:focus { outline: none; border-color: var(--amber); }
.deck-hint { font-size: 12px; color: var(--muted); margin-top: 4px; text-align: right; }
.deck-emoji-picker { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 4px; }
.deck-emoji-opt {
  border: 1.5px solid var(--line); background: var(--surface); cursor: pointer;
  font-size: 1.3rem; line-height: 1; border-radius: 12px; padding: 7px 9px;
  transition: border-color .14s, background .14s, transform .1s;
}
.deck-emoji-opt:hover { border-color: var(--amber); }
.deck-emoji-opt.on { border-color: var(--amber); background: var(--amber-soft); transform: translateY(-1px); }
.deck-form-actions { margin-top: 20px; margin-bottom: 3rem; }
.deck-form-msg { margin-top: 12px; font-weight: 600; font-size: .9rem; color: var(--muted); }
.deck-form-msg.err { color: var(--danger); }
.deck-form-msg.ok { color: var(--green); }
.deck-form-msg[hidden] { display: none; }

/* Vue détail. */
.deck-detail-actions { margin: 16px 0; }
.deck-delete-btn {
  flex: none; border: 1.5px solid color-mix(in srgb, var(--danger) 55%, transparent);
  background: transparent; color: var(--danger); font: inherit; font-weight: 700; font-size: 14px;
  border-radius: 999px; padding: 9px 18px; cursor: pointer; transition: background .15s;
}
.deck-delete-btn:hover { background: color-mix(in srgb, var(--danger) 10%, transparent); }
.deck-card-wrap { display: flex; flex-direction: column; gap: 8px; }
/* Cartes composant un deck (grille détail /mes-decks + graines du formulaire) : plafonnées à
   300px et centrées comme les cartes du kiosque. Elles sont PETITES-ENFANTS de .grid (dans
   .deck-card-wrap) → la règle .grid > .card ne les atteint pas ; sinon elles remplissent la
   colonne (~385px, « cartes trop grosses »). */
.deck-card-wrap > .card { max-width: 300px; margin-inline: auto; }
.deck-remove {
  align-self: center; border: 1.5px solid var(--line); background: transparent; color: var(--muted);
  font: inherit; font-weight: 600; font-size: 13px; border-radius: 999px; padding: 7px 16px; cursor: pointer;
  transition: border-color .15s, color .15s;
}
.deck-remove:hover { border-color: var(--danger); color: var(--danger); }

/* Boîte de gestion du partage (ne contient plus que « Arrêter le partage » ; le lien de
   partage lui-même passe désormais par la modale unifiée LBAShare, comme collection). */
.deck-share-box { margin: 6px 0 18px; }
.deck-share-box[hidden] { display: none; }

/* ===================== AJOUTER À UN DECK (recto, UGC) ===================== */
/* Icône « + » du recto, même famille que .flip-btn/.share-btn, à gauche du « i ».
   En mode connecté seulement (classe has-add). Décale partage/❤ pour faire place. */
.card-add-deck {
  position: absolute; top: 14px; right: 44px; z-index: 2;
  border: none; background: transparent; color: var(--muted); cursor: pointer;
  padding: 4px; border-radius: 8px; line-height: 0;
  display: inline-flex; align-items: center; justify-content: center;
  -webkit-tap-highlight-color: transparent;
}
.card-add-deck:hover { color: var(--amber); }
.card-add-deck svg { width: 18px; height: 18px; display: block; }
.card.flipped .card-add-deck { display: none; }
/* Quand le « + » est présent, partage et ❤ glissent d'un cran vers la gauche. */
.card-front.has-add .card-share { right: 74px; }
.card-front.has-add .card-like { right: 110px; }

/* iPhone SE & co : resserrer la rangée pour rester lisible avec le « + » en plus. */
@media (max-width: 400px) {
  .card-front.has-add .flip-btn { right: 10px; }
  .card-front.has-add .card-add-deck { right: 38px; }
  .card-front.has-add .card-share { right: 66px; }
  .card-front.has-add .card-like { right: 96px; }
  .card-front.has-add .flip-btn svg,
  .card-front.has-add .card-add-deck svg,
  .card-front.has-add .card-share svg { width: 16px; height: 16px; }
  .card-front.has-add .flip-btn,
  .card-front.has-add .card-add-deck,
  .card-front.has-add .card-share { padding: 3px; }
}

/* Petit menu flottant listant les decks (ancré sous la rangée d'icônes du recto). */
.deck-add-menu {
  position: absolute; z-index: 40; top: 46px; right: 12px; min-width: 210px; max-width: 260px;
  background: var(--bg); border: 1px solid var(--amber-soft); border-radius: 12px;
  box-shadow: 0 8px 26px rgba(60, 40, 110, .22); padding: 6px; overflow: hidden;
  text-align: left;
}
.dam-item {
  display: flex; align-items: center; gap: 8px; width: 100%; text-align: left;
  background: none; border: none; cursor: pointer; padding: 8px 10px; border-radius: 8px;
  color: var(--ink); font-size: .9rem;
}
.dam-item:hover { background: var(--surface); }
.dam-emoji { font-size: 1.1rem; }
.dam-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dam-count { color: var(--muted); font-size: .78rem; }
.dam-create { display: block; padding: 8px 10px; color: var(--amber); font-weight: 600; font-size: .85rem; text-decoration: none; border-top: 1px solid var(--amber-soft); margin-top: 4px; }
.dam-empty { padding: 8px 10px; color: var(--muted); font-size: .85rem; }

/* Toast de confirmation d'ajout. */
.deck-toast {
  position: fixed; left: 50%; bottom: 24px; transform: translateX(-50%) translateY(10px);
  z-index: 9999; background: var(--ink); color: var(--bg); padding: 11px 20px;
  border-radius: 999px; font-weight: 600; font-size: 14px; opacity: 0;
  transition: opacity .2s ease, transform .2s ease; box-shadow: 0 6px 24px rgba(0,0,0,.24);
}
.deck-toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }
.deck-toast.err { background: var(--danger); color: #fff; }

/* ===== /mes-decks : dashboard de decks (dépliage en place) ===== */
/* Sortie animée des tuiles-deck voisines quand on en déplie une (calqué sur apply()). */
.deck-leaving { transition: opacity .2s ease, transform .2s ease; opacity: 0; transform: scale(.97); pointer-events: none; }
@media (prefers-reduced-motion: reduce) { .deck-leaving { transition: none; } }
/* Tuile voisine retirée pendant le dépliage (l'attribut [hidden] est battu par .deck-card
   /.card.add qui fixent display) → classe explicite. */
.deck-gone { display: none !important; }
/* En-tête : « ← Retour » (remplace le compteur en mode déplié) — MÊME style que .deck-count. */
.deck-back-inline { background: none; border: none; cursor: pointer; padding: 0; font-size: .82rem; font-weight: 600; color: var(--muted); white-space: nowrap; }
.deck-back-inline:hover { text-decoration: underline; }
/* Tuile « Ajouter un deck » désactivée (quota atteint). */
.deck-create-tile.is-full { opacity: .5; cursor: not-allowed; }
/* Verso propriétaire (actions) dans le .card-back d'une tuile-deck : rangée d'actions
   empilée pleine largeur (le card-back est étroit et défilable). Réutilise .coll-actions. */
.deck-card .card-back { overflow-y: auto; }
.deck-card .card-back .coll-actions { display: flex; flex-direction: column; gap: 6px; width: 100%; margin-top: 8px; }
.deck-card .card-back .coll-actions > button { width: 100%; margin: 0; font-size: .9rem; padding: 8px 10px; }
/* « Apparence » : liste des skins inline sous le bouton, façon paramètres (.param-row). */
.deck-skin-inline { margin-top: 8px; }
.deck-skin-inline .deck-skin-opt.on { outline: 2px solid var(--amber); outline-offset: 1px; }
.deck-skin-inline .deck-skin-link { color: var(--amber); font-weight: 600; font-size: 13px; text-decoration: none; }

/* ===================== PROPOSITIONS DE CARTES (deck) ===================== */
.deck-suggest-block { margin-top: 28px; }
.deck-suggest-toggle {
  background: none; border: none; cursor: pointer; color: var(--amber);
  font-weight: 700; font-size: 1rem; padding: 6px 0; display: inline-flex; align-items: center; gap: 6px;
}
.deck-suggest-toggle .dst-caret { font-size: .8em; }
.deck-suggest-body { margin-top: 12px; }
.deck-suggest-more-wrap { text-align: center; margin-top: 14px;margin-bottom: 2rem; }
/* En-tête des suggestions : label « Cartes suggérées » + champ de recherche à droite. */
.deck-suggest-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin-bottom: 4px; }
.deck-suggest-head .section-label { margin: 0; }
.deck-suggest-search {
  flex: 0 1 260px; min-width: 160px; font: inherit; font-size: .92rem; color: var(--ink);
  background: var(--surface); border: 1.5px solid var(--line); border-radius: 999px; padding: 8px 14px;
}
.deck-suggest-search:focus { outline: 2px solid var(--amber); outline-offset: 1px; border-color: var(--amber); }

/* Carte proposée : on masque les contrôles d'abonnement (on compose, on ne suit pas). Le
   bouton « Ajouter au deck » est INCLUS DANS la carte, juste sous la description (.card-desc). */
.deck-suggest-add { display: block; width: 100%; margin-top: 8px; font-size: .85rem; padding: 8px 10px; }
.deck-suggest-wrap .switch-row,
.deck-suggest-wrap .sub-form,
.deck-suggest-wrap .param-status,
.deck-suggest-wrap .param-chips,
.deck-suggest-wrap .param-add,
.deck-suggest-wrap .param-form,
.deck-suggest-wrap .param-subform,
.deck-suggest-wrap .sub-count { display: none !important; }

/* ===================== Bouton « Déposer une alerte » =====================
   Libellé fixe : l'ancienne rotation « Ajouter un deck » (et ses animations
   .bd-slide-in / .bd-slide-out) a été retirée. */
.btn-deposit .bd-label { display: inline-block; }
/* PC étroit (722–1299px) : la place manque pour logo + bouton + recherche + nav-right.
   Le bouton se réduit au « + » seul (pastille ronde) ; le libellé passe dans l'infobulle
   (attribut `title`, statique). Hors de cette plage : ≤721px =
   header mobile (bouton hors flux), ≥1300px = libellé complet.
   Déclaré ICI, après `.bd-label { display: inline-block }` ci-dessus, pour l'emporter. */
@media (min-width: 722px) and (max-width: 1299px) {
  .btn-deposit { min-width: 0; width: 36px; height: 36px; padding: 0; justify-content: center; }
  .btn-deposit .bd-label { display: none; }
  .btn-deposit .plus { top: 0; font-size: 20px; }
}

/* Rotation du H1 de /favoris (« Ma collection » / « Mes favoris » / … / « Mes favorites »).
   ⚠️ Les keyframes `bd-slide-in` ci-dessous portent le nom hérité du bouton « Déposer »,
   dont la rotation a été retirée : elles ne servent PLUS qu'ici (et à favoris-title.js,
   qui pose les classes .bd-slide-out/.bd-slide-in). Ne pas les supprimer avec le reste. */
@keyframes bd-slide-in { from { transform: translateY(70%); opacity: 0; } to { transform: translateY(0); opacity: 1; } }
.fav-title-label { display: inline-block; transition: transform .22s ease, opacity .22s ease; will-change: transform, opacity; }
.fav-title-label.bd-slide-out { transform: translateY(-70%); opacity: 0; }
.fav-title-label.bd-slide-in { animation: bd-slide-in .28s ease; }
@media (prefers-reduced-motion: reduce) {
  .fav-title-label, .fav-title-label.bd-slide-out, .fav-title-label.bd-slide-in { transition: none; animation: none; transform: none; opacity: 1; }
}


/* ===================== A1/A3) Menu mobile : face « Catégories » (flip) + icônes ===================== */
.m-menu { perspective: 1400px; }
/* Point 8 (SUITE — cause du « scrollbar bouge mais contenu figé ») : l'ancienne bascule
   Menu⇄Catégories était un flip 3D (transform-style:preserve-3d + rotateY + backface-
   visibility) avec les DEUX faces en position:absolute. Le conteneur de défilement
   (.m-menu-face, overflow-y:auto) se retrouvait donc rendu comme une COUCHE 3D composée :
   son scrollTop changeait (l'ascenseur avançait) mais la texture 3D n'était pas
   re-rasterisée → le contenu paraissait figé. Un conteneur scrollable sous preserve-3d
   est intrinsèquement incompatible avec un défilement fiable.
   CORRECTIF : on remplace le flip 3D par un GLISSEMENT 2D (translateX). Une transform 2D
   sur un conteneur scrollable se repeint correctement → le défilement fonctionne. La
   révélation « Catégories » reste animée (slide horizontal), sans aucun contexte 3D. */
.m-menu-flip {
  position: relative; width: 100%; height: 100%; overflow: hidden; /* clippe la face hors-champ */
  background: var(--surface);
}
.m-menu-face {
  position: absolute; inset: 0; height: 100%; /* flip a une hauteur définie → borne la face au viewport */
  overflow-y: auto; -webkit-overflow-scrolling: touch; overscroll-behavior: contain;
  transition: transform .4s cubic-bezier(.2, .7, .2, 1);
}
/* Face avant visible, face Catégories parquée à droite. Au flip, elles glissent. */
.m-face-front { transform: translateX(0); }
.m-face-back  { transform: translateX(100%); }
.m-menu-flip.flipped .m-face-front { transform: translateX(-100%); }
.m-menu-flip.flipped .m-face-back  { transform: translateX(0); }
@media (prefers-reduced-motion: reduce) { .m-menu-face { transition: none; } }
.m-menu-cat-title {
  font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .14em;
  color: var(--amber); margin: 6px 0 8px;
}
.m-menu-cats .m-cat-item { display: flex; align-items: center; justify-content: space-between; }
.m-cat-n { color: var(--muted); font-weight: 600; font-size: .85em; }
.m-cat-empty { color: var(--muted); padding: 12px 0; }
/* Entrées de menu avec icône SVG (Catégories / Les nouvelles / La sélection). */
.m-menu-list .m-has-ic { display: inline-flex; align-items: center; gap: 10px; }
.m-menu-list .m-ic { flex: 0 0 auto; }
@media (prefers-reduced-motion: reduce) { .m-menu-flip { transition: none; } }

/* A3) Puces spéciales avec icône SVG (alignement). */
.chip-f.chip-special { display: inline-flex; align-items: center; gap: 5px; }
.chip-f.chip-special .chip-ic { flex: 0 0 auto; }

/* ===================== B1) Panneau « Mon compte » : rangées empilées sur mobile ===================== */
/* Cause du débordement : .notif-row reste en flex row (texte + switch/bouton côte à
   côte) → largeur mini qui fait déborder le panneau. Sur mobile, on empile : le
   texte prend toute la largeur, le contrôle passe dessous, aligné à gauche.
   Les rangées de NAVIGATION (.notif-nav, texte + chevron) restent en ligne. */
@media (max-width: 600px) {
  .notif-row:not(.notif-nav) {
    flex-direction: column; align-items: stretch; gap: 10px;
  }
  .notif-row:not(.notif-nav) .notif-sub { max-width: none; }
  .notif-row:not(.notif-nav) > .notif-btn,
  .notif-row:not(.notif-nav) .sup-actions,
  .notif-row:not(.notif-nav) .switch,
  .notif-row:not(.notif-nav) .pref-select,
  .notif-row:not(.notif-nav) select,
  .notif-row:not(.notif-nav) .pref-dn-input,
  .notif-row:not(.notif-nav) .pref-dn-save { align-self: flex-start; }
  /* Sélecteurs de profil : pleine largeur pour un tap confortable. */
  .notif-row:not(.notif-nav) .pref-select,
  .notif-row:not(.notif-nav) select { width: 100%; }
}

/* J) Nom public dans « Mon compte » : saisie + bouton alignés au style du site. */
.pref-dn-input-row { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.pref-dn-input {
  /*flex: 1 1 200px;*/ min-width: 0; font: inherit; font-size: 15px; color: var(--ink);
  background: var(--bg); border: 1.5px solid var(--line); border-radius: 10px; padding: 10px 14px;
}
.pref-dn-input:focus { outline: none; border-color: var(--amber); }
.pref-dn-save { flex: none; white-space: nowrap; }
/* Solde de points (phase 1) : affichage minimal, chiffre seul aligne a droite. */
.pref-points { flex: none; font-size: 20px; font-weight: 700; color: var(--ink); font-variant-numeric: tabular-nums; }
/* Rang prive (phase 2), aligne a droite comme le solde. Prive : visible de vous seul. */
.pref-rank { flex: none; font-size: 14px; font-weight: 600; color: var(--ink); }
/* Badge Top 20 (phase 2) : violet charte, sur tuiles kiosque + detail deck, a cote
   du pseudo auteur. Cohabite avec .ds-ribbon-likes dans le ruban du deck-stack. */
.deck-badge-top20 {
  display: inline-block; vertical-align: middle; white-space: nowrap;
  font-size: 11px; font-weight: 700; line-height: 1; border-radius: 999px;
  padding: 3px 9px; color: #fff; background: #a567e3;
}
@media (max-width: 600px) { .pref-dn-input-row { width: 100%; } }

/* ===================== C1) Recto : état en haut-gauche, icônes en haut-droite ===================== */
/* .state est le 1er enfant en flux (rangée du haut) ; les icônes restent absolues
   à droite. On réserve la place des icônes sur cette rangée pour éviter le
   chevauchement. Le titre (.card-top) passe en 2e rangée, sous cet en-tête.
   NB mobile : .state est replié (tap pour révéler) → c'est le titre qui remonte
   dans la bande des icônes ; sa réserve (padding-right:136px) reste donc utile. */
.card-front > .state:first-of-type { padding-right: 130px; margin-bottom: 6px; min-height: 20px; }
/* Format « carte a jouer » etroit (mobile, 2 colonnes) : la carte est trop etroite
   pour aligner l'etat ET les 3-4 icones sur la meme rangee (« Rien a signaler »
   chevauchait les icones). L'etat passe SOUS la bande d'icones (pleine largeur),
   les icones gardent leur coin haut-droit. Aucune refonte du contenu, juste un
   repositionnement pour le format resserre. */
@media (max-width: 620px) {
  .grid .card-front > .state:first-of-type { padding-right: 0; margin-top: 30px; min-height: 0; }
}

/* C4) Statut d'abonnement des cartes paramétrées : en bas, détaché du sélecteur. */
.param-status { margin-top: 10px; }

/* ===================== D) Titre dashboard : lettres qui se retournent ===================== */
.hero-letter { display: inline-block; transform-origin: 50% 50%; white-space: pre; }
@media (prefers-reduced-motion: no-preference) {
  .hero-letter { animation: hero-letter-in .5s cubic-bezier(.2, .7, .2, 1) both; will-change: transform, opacity; }
}
@keyframes hero-letter-in {
  from { transform: perspective(500px) rotateX(-90deg); opacity: 0; }
  to   { transform: perspective(500px) rotateX(0deg);   opacity: 1; }
}

/* ===================== E6) Célébration : pack/deck entièrement adopté ===================== */
.celebrate-burst { position: absolute; left: 50%; top: 50%; width: 0; height: 0; pointer-events: none; z-index: 5; }
.cb-star {
  position: absolute; left: 0; top: 0; width: 7px; height: 7px; margin: -3.5px;
  border-radius: 50%; background: var(--amber); opacity: 0;
  animation: cb-pop 1.1s ease-out forwards;
}
.cb-star:nth-child(3n)   { background: var(--green); }
.cb-star:nth-child(3n+1) { background: #d9a54a; } /* ambre doux, dans l'ambiance */
@keyframes cb-pop {
  0%   { transform: translate(0, 0) scale(.2); opacity: 0; }
  25%  { opacity: 1; }
  60%  { transform: translate(var(--tx), var(--ty)) scale(1); opacity: 1; }
  100% { transform: translate(calc(var(--tx) * 1.25), calc(var(--ty) * 1.25)) scale(.4); opacity: 0; }
}
.celebrated { animation: cb-glow .9s ease; }
@keyframes cb-glow {
  0%, 100% { box-shadow: var(--shadow); }
  50% { box-shadow: 0 0 0 4px var(--amber-soft), 0 6px 22px rgba(120, 90, 180, .4); }
}
@media (prefers-reduced-motion: reduce) { .cb-star { animation: none; display: none; } }

/* ===================== E5) Motif × teinte des decks ===================== */
/* 8 teintes douces. Chaque classe pose --hue ; deux règles en dérivent le fond de
   carte (--deck-bg, pâle) et la couleur du motif (--deck-motif, soutenu + translucide),
   avec déclinaison thème sombre. Motif = SVG monochrome injecté (currentColor). */
.tint-1 { --hue: #a567e3; } /* violet maison */
.tint-2 { --hue: #5b7db8; } /* bleu nuit doux */
.tint-3 { --hue: #6fa287; } /* vert sauge */
.tint-4 { --hue: #d9a54a; } /* ambre doux */
.tint-5 { --hue: #c97b5f; } /* terracotta doux */
.tint-6 { --hue: #c58ab0; } /* rose poudré */
.tint-7 { --hue: #8a86a3; } /* gris-violet (ardoise) */
.tint-8 { --hue: #5fa8a4; } /* turquoise doux */
.tint-9 { --hue: #d05a5a; } /* rouge doux */
.tint-10 { --hue: #3a3a44; } /* noir ardoise (fond dédié plus bas) */
.tint-11 { --hue: #a567e3; } /* arc-en-ciel (fond dédié plus bas) */
[class*="tint-"] {
  --deck-bg: color-mix(in srgb, var(--hue) 50%, #ffffff);
  --deck-motif: color-mix(in srgb, var(--hue) 100%, transparent);
}
html[data-theme="dark"] [class*="tint-"] {
  --deck-bg: color-mix(in srgb, var(--hue) 50%, #16131c);
  --deck-motif: color-mix(in srgb, var(--hue) 100%, transparent);
}
/* Teinte 10 — noir ardoise : fond sombre lisible dans les deux thèmes, motif clair.
   (placé APRÈS le bloc générique pour gagner sur --deck-bg / --deck-motif) */
.tint-10 { --deck-bg: #2b2833; --deck-motif: rgba(255, 255, 255, .5); }
html[data-theme="dark"] .tint-10 { --deck-bg: #1b1922; --deck-motif: rgba(255, 255, 255, .42); }
/* Teinte 11 — arc-en-ciel : dégradé doux + voile de lisibilité renforcé, motif neutre. */
.tint-11 { --deck-motif: rgba(58, 48, 78, .45); }
html[data-theme="dark"] .tint-11 { --deck-motif: rgba(255, 255, 255, .42); }
.pack.tint-11, .deck-thumb.tint-11, .deck-preview.tint-11, .deck-tint-opt.tint-11 {
  background: linear-gradient(120deg,
    #f6c6c6 0%, #f7dcb0 22%, #f3f0a8 42%, #bfe6bf 62%, #b9d4f0 80%, #d8c2ee 100%);
}
html[data-theme="dark"] .pack.tint-11, html[data-theme="dark"] .deck-thumb.tint-11,
html[data-theme="dark"] .deck-preview.tint-11 {
  background: linear-gradient(120deg,
    #6b3f4a 0%, #6b563a 22%, #5f5f38 42%, #3f5f45 62%, #3a4f6b 80%, #4f3f6b 100%);
}
.tint-11 .deck-motif-bg::after { background: color-mix(in srgb, #ffffff 52%, transparent); }
html[data-theme="dark"] .tint-11 .deck-motif-bg::after { background: color-mix(in srgb, #16131c 55%, transparent); }

/* Calque motif en fond de carte + voile de lisibilité par-dessus le motif. */
.deck-motif-bg {
  position: absolute; inset: 0; z-index: 0; color: var(--deck-motif);
  overflow: hidden; border-radius: inherit; pointer-events: none;
}
.deck-motif-bg svg { width: 100%; height: 100%; display: block; }
.deck-motif-bg::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(160deg, color-mix(in srgb, var(--deck-bg) 30%, transparent), color-mix(in srgb, var(--deck-bg) 72%, transparent));
}

/* Packs de l'étagère : fond teinté + motif, plus d'emoji. */
.pack[class*="tint-"] { background: var(--deck-bg); }
.pack[class*="tint-"] .pack-stack::before,
.pack[class*="tint-"] .pack-stack::after { background: var(--deck-bg); }
.pack .pack-body { position: relative; z-index: 1; }

/* Vignette carrée (lignes « Mes decks », en-têtes) : mini-carte teintée + motif. */
.deck-thumb {
  position: relative; flex: 0 0 auto; width: 44px; height: 44px; border-radius: 11px;
  overflow: hidden; background: var(--deck-bg);
}
.deck-thumb.deck-thumb-lg { width: 60px; height: 60px; border-radius: 14px; }

/* Carte d'aperçu du formulaire de deck. */
.deck-preview {
  position: relative; overflow: hidden; border-radius: 16px; padding: 18px;
  min-height: 165px;    width: 275px; background: var(--deck-bg); box-shadow: var(--shadow);
}
.deck-preview .deck-preview-body { position: relative; z-index: 1; }
.deck-preview .deck-preview-name { font-weight: 700; font-size: 1.05rem; color: var(--ink); }

/* Formulaire de deck : champs (nom + description) à gauche, aperçu à droite. */
.deck-form-top { display: flex; gap: 18px; align-items: flex-start; margin-bottom: 6px; }
.deck-form-fields { flex: 1 1 auto; min-width: 0; }
/* Aperçu « Votre deck » : deck à taille NORMALE (largeur d'une carte = 300px, ratio 2/3). */
.deck-form-top .deck-preview-wrap { flex: 0 0 auto; align-self: flex-start; width: 300px; max-width: 100%; }
.deck-preview-wrap .deck-card { width: 100%; }
@media (max-width: 560px) {
  .deck-form-top { flex-direction: column; }
  .deck-form-top .deck-preview-wrap { width: min(300px, 90%); }
}

/* Sélecteur de teinte (8 pastilles). */
.deck-tint-picker { display: flex; flex-wrap: wrap; gap: 8px; margin: 4px 0 4px; }
.deck-tint-opt {
  width: 26px; height: 26px; border-radius: 50%; cursor: pointer; padding: 0;
  border: 2px solid transparent; background: var(--hue);
}
.deck-tint-opt.on { border-color: var(--ink); outline: 2px solid var(--hue); outline-offset: 1px; }

/* Selecteur de visibilite du deck (Public par defaut / Prive) dans le formulaire. */
.deck-vis-picker { display: flex; flex-wrap: wrap; gap: 10px; margin: 4px 0 8px; }
.deck-vis-opt {
  flex: 1 1 200px; text-align: left; cursor: pointer; font: inherit; color: var(--ink);
  background: var(--surface); border: 1.5px solid transparent; border-radius: 12px; padding: 10px 14px;
}
.deck-vis-opt.on { border-color: var(--amber); background: var(--amber-soft); }
.deck-vis-title { display: block; font-weight: 700; font-size: .95rem; }
.deck-vis-sub { display: block; font-size: .78rem; color: var(--muted); margin-top: 2px; }

/* Vignette motif dans le menu « Ajouter à un deck » (petite). INCHANGÉE (exception). */
.dam-thumb { width: 24px; height: 24px; border-radius: 7px; }
.deck-row .deck-thumb { margin-right: 2px; }

/* ===================== Deck en pile de cartes PLEINE TAILLE + ruban (LBADeckStack) =========
   ITERATION 2 : une tuile-deck occupe UNE cellule de grille comme une carte normale
   (meme ratio 5/7, meme largeur de colonne). A l'interieur, jusqu'a 3 VRAIES cartes du
   kiosque (LBACards.cardHTML) empilees + ruban teinte centre. Un seul composant, aucune
   variante de taille selon l'emplacement (kiosque, Mes decks, apercu form, detail). */
/* overflow:hidden = CONTENANCE STRICTE : la pile (cartes du fond + ruban) ne peut plus
   deborder de la tuile ni recouvrir une tuile/carte voisine (correctif regression :
   plus de surplomb non maitrise entre tuiles de la grille dense du kiosque). */
.deck-card { display: block; text-decoration: none; color: inherit; cursor: pointer; min-width: 0; align-self: start; overflow: visible; border-radius: 24px; }
/* Tuile-deck recommandee : contexte de positionnement pour l'etiquette « Recommandee »
   (.reco-label est en position:absolute ; overflow:visible laisse deborder le top:-10px). */
.deck-card.card-reco { position: relative; }
.deck-card, .deck-card * { cursor: pointer; }
.deck-card:focus-visible { outline: 2px solid var(--amber); outline-offset: 3px; border-radius: 24px; }
.deck-stack { display: block; position: relative; }
.ds-stack { display: block; position: relative; }
/* La carte de DEVANT est EN FLUX : elle donne sa largeur ET sa hauteur a la tuile.
   CAUSE RACINE du bug « une lettre par ligne » : quand TOUTES les faces etaient en
   position:absolute, la tuile avait une largeur min-content de 0 ; dans une grille ne
   contenant QUE des tuiles-deck (page Mes decks), le <main> en shrink-to-fit s'effondrait
   → colonnes ~78px → titre casse caractere par caractere. Une face en flux redonne une
   largeur intrinseque normale (comme une vraie carte). Une bande haute (margin-top) laisse
   les cartes du fond depasser vers le haut, contenue par overflow:hidden. */
.ds-face { transform-origin: 50% 50%; }
@media (prefers-reduced-motion: no-preference) { .ds-face { transition: transform .2s ease; } }
.ds-face .card, .ds-face .card.flip { width: 100%; margin: 0; }
/* --ds-fan : decalage horizontal d'une carte du fond (fraction de la largeur de tuile) ;
   --ds-front : reduction de la carte de devant, qui LIBERE la place laterale ou l'eventail
   se voit. La carte de devant reste EN FLUX (elle donne largeur+hauteur a la tuile → pas
   d'effondrement « une lettre par ligne ») ; le scale est purement visuel (n'affecte pas la
   largeur min-content). La contenance de l'eventail ne repose PAS sur un overflow:hidden
   (la tuile est en overflow:visible pour l'etiquette « Recommandee ») mais sur la geometrie :
   cf. --ds-fan et le plafond de largeur juste en dessous. */
/* --ds-fan : decalage lateral d'une carte du fond, en fraction de la LARGEUR DE FACE.
   Valeur bornee (correctif 4b) : une carte du fond au ratio 2/3 tournee de 5deg deborde
   deja de ~5,6 % de sa largeur de chaque cote (demi-diagonale projetee). Avec des cartes
   plafonnees a 80 % de la face (regle ci-dessous), l'excursion laterale maximale vaut
   0,8 x 0,5635 + --ds-fan = 45,1 % + 4,5 % = 49,6 % < 50 % : l'eventail tient DANS la
   tuile dans tous les contextes, sans dependre d'un overflow:hidden. */
.deck-stack {
  --ds-fan: 4.5%; /* --ds-front supprime : la taille vient de max-width, plus du scale */
  /* Bande du ruban, en pourcentage de la hauteur de pile, mesuree depuis le BAS. Declaree
     ICI (et non sur .ds-ribbon) parce que deux regles la lisent : le ruban s'y place, et le
     bloc de texte des cartes s'y plafonne. Les skins structures la redefinissent plus bas,
     sur .deck-stack egalement, pour que le plafond suive automatiquement. */
  --ds-ribbon-bottom: 56%;
  /* Degagement minimal garanti entre le bord bas du ruban et le haut du bloc de texte. */
  --ds-ribbon-gap: 10px;
}
/* INVARIANT PAR CONSTRUCTION : « le ruban ne recouvre jamais le titre ni le sous-titre ».
   .card-content est ancre EN BAS (margin-top:auto) et grandit vers le HAUT, donc il finit
   par entrer sous le ruban des que la carte est riche (cf. l'incident « Indice UV » decrit
   sur .ds-ribbon). On lui interdit purement et simplement la bande du ruban : sa hauteur
   est plafonnee a --ds-ribbon-bottom moins le degagement. Le titre ne PEUT donc plus y
   passer, quels que soient le contenu de la carte, la taille de la pile et le skin.
   C'est un NO-OP tant que le contenu tient dans la bande (verifie : les 5 packs qui
   passaient deja sont inchanges au pixel) — il ne se declenche que sur le cas limite.
   Ce qui cede alors est la DESCRIPTION, seul element concu pour se couper (elle a deja
   min-height:0 + overflow:hidden plus haut) : le titre, le sous-titre et la ligne
   d'abonnement restent entiers. D'ou le flex : sans lui, le plafond rognerait le BAS du
   bloc, c'est-a-dire la ligne d'abonnement, au lieu de la description.
   Aucune incidence sur la hauteur de la carte : l'aspect-ratio 2/3 est intact. */
.deck-stack .ds-face .card-content {
  display: flex; flex-direction: column;
  max-height: calc(var(--ds-ribbon-bottom) - var(--ds-ribbon-gap));
  overflow: hidden;
}
.deck-stack .ds-face .card-content .card-static-desc { flex: 0 1 auto; min-height: 0; overflow: hidden; }
.ds-i0 { position: relative; z-index: 3; /* Plus de tansformation transform: scale(var(--ds-front));*/ }
/* Parite avec .grid > .card : la carte de devant fait la MEME TAILLE REELLE (300px), a
   taille native (contenu non reduit). Le scale(--ds-front) a ete retire : on plafonne donc
   la largeur REELLE a 300px directement (avant, on plafonnait a 300/--ds-front car le scale
   reduisait ensuite la carte). margin-inline:auto la recentre dans sa face. */
/* Le plafond s'applique a TOUTES les faces (devant + eventail) et dans TOUS les contextes
   — plus seulement dans .grid. C'etait la cause du debordement de l'eventail (4b) : hors
   grille (en-tete de detail, apercu de formulaire) et en 1 colonne sur mobile, la carte
   remplissait toute la face, il ne restait aucune marge laterale pour l'offset + la
   rotation, et .ds-i1/.ds-i2 sortaient de la tuile sur la gouttiere voisine. Le second
   plafond (80 % de la face) reserve cette marge partout ; le 300px garde la parite de
   taille avec les cartes classiques de la grille (ou 80 % vaut plus de 300px). */
.deck-card .ds-face .card { max-width: min(300px, 80%); margin-inline: auto; }
.ds-i1, .ds-i2 { position: absolute; inset: 0; }
.ds-i1 .card, .ds-i2 .card { height: 100%; }
/* Cartes de FOND : MEME base de taille que le front (max-width 300px + centrage), au lieu
   de dependre du scale pour la TAILLE. Avant, scale(.74-.76) depuis le centre reduisait la
   carte vers son milieu → retrait vertical (bas « coupe ») qui exigeait une compensation
   empirique. Ici la taille de base est deja bonne via max-width ; le transform ne garde que
   l'offset + la rotation (+ un scale tres subtil pour la profondeur, cf. plus bas). Parite
   avec .ds-i0 .card ; scope grille (kiosque + mes-decks), comme le front. */
/* (Le plafond de largeur des cartes du fond est desormais celui de .ds-face .card
   ci-dessus : meme base de taille que le devant, dans tous les contextes.) */
/* Cartes d'APERCU : non interactives (la tuile entiere navigue). Le switch « S'abonner »
   reste visible mais grise (desactive). */
.deck-card .ds-face { pointer-events: none; }
.deck-card .ds-face .switch-row { opacity: .5; }
/* Exceptions sur la carte de DEVANT : elements ACTIFS (switch = abonne tout le deck,
   cœur = favori deck, partager = retourne le deck) → on leur rend les evenements pointeur
   et l'opacite pleine. Les autres cartes de la pile restent inertes. */
.deck-card .ds-i0 .switch-row { pointer-events: auto; opacity: 1; }
.deck-card .ds-i0 .switch-row .switch input:disabled ~ .track,
.deck-card .ds-i0 .switch-row .switch input:disabled ~ .thumb { opacity: 1; }
/* Le switch n'est pas un lien : curseur normal (fleche), pas la main de la tuile-lien. */
.deck-card .ds-i0 .switch-row, .deck-card .ds-i0 .switch-row * { cursor: default; }
/* Cœur, partager, retour (face partage) et la face de partage elle-meme : cliquables. */
.deck-card .ds-i0 .card-like,
.deck-card .ds-i0 .card-share,
.deck-card .ds-i0 .flip-back,
.deck-card .ds-i0 .card-share-face { pointer-events: auto; }
/* « i » (En savoir plus) + verso : la carte de devant se retourne sur le DOS DU DECK
   (description + categories + « Plus d'infos »), meme flip/apparence qu'un verso de carte.
   Scope aux TUILES du kiosque (data-deck-tile) : les apercus de detail/formulaire gardent
   un « i » inerte (leur verso resterait celui de la source d'apercu). */
.deck-card[data-deck-tile] .ds-i0 .flip-btn,
.deck-card[data-deck-tile] .ds-i0 .card-back { pointer-events: auto; }
/* /mes-decks : la tuile-deck se retourne sur son VERSO PROPRIETAIRE (actions) → verso
   cliquable. La face partage / flip-back sont deja actives (regles .deck-card .ds-i0 …). */
.deck-card[data-deck] .ds-i0 .card-back,
.deck-card[data-deck] .ds-i0 .flip-btn { pointer-events: auto; }
/* Le compteur de likes de la source n'a pas de sens pour un favori de deck → masque. */
.deck-card .ds-i0 .like-n { display: none; }
/* Survol du deck : ombre portee sur la carte de devant, comme .card.flip:hover. */
.deck-card:hover .ds-i0 .card-front { box-shadow: var(--shadow); }
/* Pendant le partage (retournement de la carte de devant) : on masque le ruban et les
   cartes du fond pour un retournement propre facon carte. */
.deck-card.ds-sharing .ds-ribbon,
.deck-card.ds-sharing .ds-i1,
.deck-card.ds-sharing .ds-i2 { opacity: 0; pointer-events: none; }
/* Eventail VISIBLE : la TAILLE des fonds vient deja de max-width (ci-dessus). Le transform
   ne fait plus que l'effet de pile : offset horizontal (--ds-fan) de chaque cote + rotation
   ±5°, plus un scale TRES SUBTIL (.98/.97) pour un leger retrait de profondeur — pas assez
   pour recreer le decalage vertical de l'ancien gros scale. Le ruban pleine largeur englobe
   l'ensemble. transform-origin reste au centre (50% 50%, cf. .ds-face). */
.ds-i1 { z-index: 2; transform: translateX(calc(-1 * var(--ds-fan))) rotate(-5deg) scale(.98); }
.ds-i2 { z-index: 1; transform: translateX(var(--ds-fan)) rotate(5deg) scale(.97); }
/* Deck vide (0 carte) : visuel generique (carte teintee + motif « paquet »). Le contenu
   (motif) etant en absolu, on donne explicitement la hauteur via aspect-ratio (sinon la
   carte de devant en flux serait de hauteur nulle → seul le ruban s'afficherait). */
.ds-empty { aspect-ratio: 5 / 7; background: var(--deck-bg); border-radius: 24px; box-shadow: var(--shadow); overflow: hidden; }
.ds-empty .deck-motif-bg { border-radius: inherit; }

/* Ruban en forme de VAGUE (patron Hugo) : bords arrondis + encoche laterale qui epouse
   le paquet, plutot qu'un rectangle a coins inclines. Centre sur la pile (positionnement
   deja valide), contenu dans la tuile (overflow:hidden → aucun debordement).
   - Fond = teinte du deck via var(--deck-bg) (11 teintes .tint-1..11, tint-10/11 inclus) —
     jamais de couleur en dur (le #8FBE00 du patron est remplace).
   - Ombres de repli = ombre neutre semi-transparente (--ds-ribbon-shadow, meme teinte que
     --shadow) a la place du #0005 du patron.
   - padding haut = bande de l'encoche (2r) ; padding bas = 2e ligne de texte (compteur). */
.ds-ribbon {
  --r: 9px;
  --ds-ribbon-shadow: rgba(30, 20, 50, .33);
  /* POSITION (correctif 4a) : le ruban etait centre verticalement (top:54% + translate),
     donc il retombait pile sur le bloc de titre de la carte d'apercu, qui est ancre EN BAS
     (.card-content { margin-top:auto }) et remonte d'autant que son contenu est riche
     (titre sur 2 lignes, chips de parametres...). Le titre/sous-titre etaient masques.
     On ancre le ruban par son BORD BAS, a --ds-ribbon-bottom du bas de la pile ; il grandit
     alors vers le HAUT (nom long, 3 categories), c'est-a-dire vers la fenetre d'art.
     La variable est declaree sur .deck-stack (et non ici) pour que les DEUX cotes de la
     regle la lisent : le ruban qui se place, et le bloc de texte qui se plafonne.

     REGLE : le ruban ne recouvre JAMAIS .card-top (titre) ni .card-subtitle.

     CE QUE CETTE REGLE VALAIT AU LOT 4, ET POURQUOI ELLE ETAIT FAUSSE (verifie au
     navigateur le 09/09/2026) : elle reposait sur l'idee que le bloc de texte occupe une
     fraction a peu pres stable de la carte, donc qu'un pourcentage suffisait. Faux. Le
     pourcentage mesure la hauteur de PILE, qui ne sait rien de la hauteur reelle du
     contenu. Contexte qui l'a mise en defaut : la PILE REDUITE DE L'EN-TETE DE DETAIL
     (/collection/<pack>), plus etroite que les tuiles de grille — le sous-titre
     « LE DEPARTEMENT DE VOTRE CHOIX » de la carte « Indice UV » y passe sur DEUX lignes au
     lieu d'une, le bloc monte a 58,6 % de la carte pour un ruban ancre a 56 %, et le titre
     etait coupe en deux sur 9 px (packs ciel et montagne, clair et sombre, 1440 et 390 px).
     N'importe quelle future carte au sous-titre aussi long aurait refait le meme defaut,
     sur n'importe quel pack : le lot 4 n'avait valide que les tuiles de grille.

     LA REGLE EST DESORMAIS VRAIE PAR CONSTRUCTION, pas par reglage : le bloc de texte est
     PLAFONNE a la bande libre sous le ruban (voir .deck-stack .ds-face .card-content plus
     bas). Ne pas « ajuster » les pourcentages ci-dessous pour rattraper un chevauchement :
     s'il y en a un, c'est le plafond qui n'est pas applique. */
  position: absolute; left: 8px; right: 8px; top: auto; bottom: var(--ds-ribbon-bottom); z-index: 4;
  text-align: center; line-height: 1.25; color: var(--ink);
  padding: calc(3 * var(--r)) 14px 9px;
  border-radius: var(--r);
  --_g: 0 / var(--r) calc(2 * var(--r)) no-repeat;
  background:
    radial-gradient(100% 50% at right, var(--ds-ribbon-shadow) 98%, transparent 101%) 0 var(--_g),
    radial-gradient(100% 50% at left, var(--ds-ribbon-shadow) 98%, transparent 101%) 100% var(--_g),
    conic-gradient(at var(--r) calc(2 * var(--r)), transparent 25%, var(--deck-bg) 0) 0 0 / calc(100% - var(--r)) 100%;
}
.ds-ribbon-name { display: block; font-weight: 700; font-size: 1rem; line-height: 1.15; letter-spacing: -.01em; overflow-wrap: anywhere; }
.ds-ribbon-meta { display: block; font-size: .8rem; margin-top: 2px; color: color-mix(in srgb, var(--ink) 60%, transparent); }
/* MOTIF choisi, discret, DERRIERE le texte du ruban (couleur --deck-motif de la teinte).
   Le dessin ne s'applique QUE sur le ruban ; inséré sous l'encoche haute (2r) pour rester
   dans la vague. Le contenu du ruban passe au-dessus (z-index:1). */
.ds-ribbon-motif {
  position: absolute; top: calc(2 * var(--r)); left: 3px; right: 3px; bottom: 3px; z-index: 0;
  overflow: hidden; border-radius: 0 0 var(--r) var(--r);
  color: var(--deck-motif); opacity: .3; pointer-events: none;
}
.ds-ribbon-motif svg { width: 100%; height: 100%; display: block; }
.ds-ribbon-name, .ds-ribbon-meta, .ds-ribbon-cats { position: relative; z-index: 1; }
/* Categories auto (top-3) sur le ruban : pastilles au style des tags de carte, LECTURE
   SEULE (jamais cliquables ici — elles decrivent le deck, elles ne filtrent pas). */
.ds-ribbon-cats { display: flex; flex-wrap: wrap; gap: 4px; justify-content: center; margin-top: 5px; }
/* ANCRAGE DU RUBAN PAR SKIN STRUCTURE (correctif 4a). Les 4 skins structures rangent le
   titre et le sous-titre ailleurs que le rendu par defaut (plaque de nom en haut, ligne de
   type au milieu...) : le creux libre n'est pas au meme endroit. Chaque skin declare donc
   OU passe le ruban, toujours dans une bande ou ne vit ni .card-top ni .card-subtitle.
   Le selecteur est un simple ancetre : il marche que la classe soit posee sur #grid (skin
   de carte equipe) ou sur la tuile .deck-card (apercu de boutique, skin de deck).
   L'Ecole classique : SOUS la ligne de type, sur la boite de texte, au-dessus du switch.
   L'Arcane          : AU-DESSUS de la ligne de type, sur le medaillon.
   L'Assemblee / Le Dresseur : dans la fenetre d'art, au-dessus de la ligne de type (leur
   plaque de nom est tout en haut, le ruban passe donc entre les deux).
   Valeurs verifiees au navigateur (aucun recouvrement, marge >= 14px) sur les tuiles de
   grille en 1440 et 390, themes clair et sombre.
   Redeclarees sur .deck-stack et non sur .ds-ribbon : c'est ce qui permet au plafond de
   .card-content (cf. plus haut) de suivre automatiquement la bande de chaque skin. */
.skin-ecole-classique .deck-stack { --ds-ribbon-bottom: 24%; }
.skin-arcane .deck-stack { --ds-ribbon-bottom: 66%; }
.skin-assemblee .deck-stack { --ds-ribbon-bottom: 56%; }
.skin-dresseur .deck-stack { --ds-ribbon-bottom: 56%; }
.ds-cat-tag {
  font-size: 10px; font-weight: 600; color: var(--amber); line-height: 1.3;
  background: var(--amber-soft); border-radius: 999px; padding: 3px 9px; border: none;
}
/* Chips categories dans l'en-tete de detail d'un deck (un cran plus grand). */
.deck-detail-cats { display: flex; flex-wrap: wrap; gap: 6px; margin: 6px 0 4px; }
.deck-detail-cats .ds-cat-tag { font-size: 12px; padding: 5px 12px; }
/* tint-10 (ardoise) : le fond du ruban est SOMBRE dans les deux themes (--deck-bg fixe) →
   le texte --ink (fonce en clair) devient illisible. On force un texte clair (#fff = meme
   convention que le texte sur bouton accent). Les autres teintes restent en --ink (fond pale
   en clair / fonce en sombre, contraste OK). */
.tint-10 .ds-ribbon { color: #fff; }
.tint-10 .ds-ribbon-meta { color: color-mix(in srgb, #fff 72%, transparent); }
/* tint-11 (arc-en-ciel) : le ruban adopte le MEME degrade que .pack.tint-11 /
   .deck-thumb.tint-11 / .deck-preview.tint-11 (repris a l'identique — degrade documente,
   seule exception hex de la charte) au lieu du fond uni --deck-bg. Ombres de repli et
   variables (--_g, --ds-ribbon-shadow) conservees. Texte --ink lisible (degrade pastel en
   clair, sombre en sombre), pas de surcharge de couleur necessaire. */
.tint-11 .ds-ribbon {
  background:
    radial-gradient(100% 50% at right, var(--ds-ribbon-shadow) 98%, transparent 101%) 0 var(--_g),
    radial-gradient(100% 50% at left, var(--ds-ribbon-shadow) 98%, transparent 101%) 100% var(--_g),
    linear-gradient(120deg, #f6c6c6 0%, #f7dcb0 22%, #f3f0a8 42%, #bfe6bf 62%, #b9d4f0 80%, #d8c2ee 100%)
      0 0 / calc(100% - var(--r)) 100% no-repeat;
}
html[data-theme="dark"] .tint-11 .ds-ribbon {
  background:
    radial-gradient(100% 50% at right, var(--ds-ribbon-shadow) 98%, transparent 101%) 0 var(--_g),
    radial-gradient(100% 50% at left, var(--ds-ribbon-shadow) 98%, transparent 101%) 100% var(--_g),
    linear-gradient(120deg, #6b3f4a 0%, #6b563a 22%, #5f5f38 42%, #3f5f45 62%, #3a4f6b 80%, #4f3f6b 100%)
      0 0 / calc(100% - var(--r)) 100% no-repeat;
}

/* En-tete de detail : la pile pleine taille remplace la vignette .deck-thumb-lg. On la
   borne a une largeur de carte pour ne pas ecraser le titre a cote. Largeur revue avec le
   correctif 4b : la carte d'apercu ne fait plus que 80 % de la largeur de la face (la marge
   laterale est reservee a l'eventail, qui debordait sur la colonne voisine). Sans cette
   compensation la carte devenait trop etroite ici, son bloc de texte — de hauteur presque
   fixe — mangeait toute la carte, et il ne restait plus de bande libre pour le ruban. */
.deck-detail-layout .deck-card { width: min(310px, 88vw); flex: 0 0 auto; padding: 10px;}

/* Detail d'un deck (/mes-decks) : titre .page-title EN PLEINE LARGEUR, puis rangee
   [pile deck-card] | [corps]. Le corps (description, categories, actions, partage)
   est empile verticalement, A DROITE de la pile et SOUS le titre. */
.deck-detail-layout { display: flex; align-items: flex-start; gap: 20px; margin-bottom: 8px; }
.deck-detail-body { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; align-items: flex-start; }
/* Mobile-first : sous ~640px, le corps repasse SOUS la pile (colonne unique). */
@media (max-width: 640px) {
  .deck-detail-layout { flex-direction: column; gap: 14px; }
  .deck-detail-body { width: 100%; }
}
/* Le corps du detail de collection REUTILISE .deck-detail-body : on n'ajoute que
   l'ecart vertical entre ses blocs et la pile en element flex non etirable. */
.deck-detail-body { gap: 6px; }
.deck-detail-layout > .coll-emoji { flex: 0 0 auto; line-height: 1; }
.coll-head .page-title { text-align: left; margin-bottom: 12px; }

/* Cartes d'apercu HORS grille (en-tete de detail, apercu du formulaire) : elles sont
   toujours etroites, mais ne recoivent pas les regles « carte etroite » de .grid (limitees
   au mobile). On leur applique donc le MEME traitement, quelle que soit la largeur d'ecran :
   etat SOUS la bande d'icones (fini le chevauchement coeur/partage/i avec « Rien a signaler »)
   et titre pleine largeur (fini la coupure en plein milieu d'un mot). */
.deck-detail-layout .deck-card .card-front > .state:first-of-type,
.deck-preview-wrap .deck-card .card-front > .state:first-of-type { padding-right: 0; margin-top: 30px; min-height: 0; }
.deck-detail-layout .deck-card .card-front .card-top,
.deck-preview-wrap .deck-card .card-front .card-top { padding-right: 0; }
.deck-detail-layout .deck-card .card-front .card-top h3,
.deck-preview-wrap .deck-card .card-front .card-top h3 { flex: 1 1 auto; }
/* Meme motif pour le format « toprow » (pastille d'etat + icones sur UNE ligne) : a 250px
   de large la pastille etait rognee (« Rien a signal... »). Hors grille, on autorise le
   retour a la ligne : icones en haut, pastille entiere en dessous. */
.deck-detail-layout .deck-card .card-toprow,
.deck-preview-wrap .deck-card .card-toprow { flex-wrap: wrap; justify-content: flex-end; row-gap: 6px; }
.deck-detail-layout .deck-card .card-toprow .state,
.deck-preview-wrap .deck-card .card-toprow .state { order: 2; flex: 0 0 auto; }

/* ---------------- Page « Le Point » ---------------- */
.lepoint-main { max-width: 760px; padding-bottom: 60px; }
.lepoint-head { margin: 8px 0 22px; text-align: center; }
.lepoint-head .page-title { margin-bottom: 4px; }
.lepoint-head .src-desc { margin-left: auto; margin-right: auto; }
.lp-share { margin-top: 12px; }
/* Point 6) Les deux labels (« En ce moment » / « À venir ») étaient tous deux
   renvoyés dans la marge gauche à top:2px en ≥1280px → ils se chevauchaient.
   Comme sur .decks-main, on les remet dans le flux ici. */
@media (min-width: 1280px) {
  body:not(.dev) main.lepoint-main .section-label {
    position: static; text-align: left; left: auto; width: auto; margin: 0 0 14px;
  }
}
#lp-active { display: flex; flex-direction: column; gap: 10px; margin-bottom: 30px; }
.lp-card {
  display: block; text-decoration: none; color: inherit;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: 14px; padding: 14px 16px; transition: border-color .15s ease, transform .15s ease;
}
.lp-card:hover { border-color: #a567e3; transform: translateY(-1px); }
.lp-card-msg { font-size: 15px; line-height: 1.45; }
.lp-calm { text-align: center; padding: 34px 16px; font-size: 20px; font-weight: 700; color: var(--ink); }
.lp-calm-sub { display: inline-block; margin-top: 8px; font-size: 14px; font-weight: 400; color: var(--muted); }
.lp-soon-label { margin-top: 8px; }
.lp-upcoming { list-style: none; margin: 0; padding: 0; }
.lp-soon-item a {
  display: flex; gap: 14px; align-items: baseline; text-decoration: none; color: inherit;
  padding: 11px 4px; border-bottom: 1px solid var(--line);
}
.lp-soon-item a:hover .lp-soon-msg { color: #a567e3; }
.lp-soon-when { flex: 0 0 auto; min-width: 92px; font-weight: 700; font-size: 13px; color: #a567e3; }
.lp-soon-msg { font-size: 14px; line-height: 1.4; }
.lp-soon-empty { color: var(--muted); font-size: 14px; padding: 12px 4px; list-style: none; }
@media (max-width: 560px) {
  .lp-soon-item a { flex-direction: column; gap: 2px; }
  .lp-soon-when { min-width: 0; }
}

/* ======================================================================
   CARTES DE SOURCE — REFONTE VISUELLE « disposition A » (full-art)
   Reproduit le modele valide (design-reference-cartes.html) : fond degrade
   + motif SVG plein-carte + voile de lisibilite + double lisere ; rangee
   haute (etat a gauche, controles ronds a droite) ; contenu ancre en bas.
   PERIMETRE STRICTEMENT VISUEL : aucune classe interactive ni handler
   modifie (card-share/card-like/card-add-deck/flip-btn/state/switch/param-*
   conserves). Le theme nuit de la carte suit le dark mode du site.
   Scope .grid + .deck-card : couvre home, favoris, collection, deck, mes-decks
   et les apercus de deck (LBADeckStack reutilise LBACards.cardHTML). La carte
   « Mon compte » (.acct-*, hors grille) et l'apercu dev (.preview) sont
   volontairement hors scope.
   Toutes les valeurs (degrades, opacites, voile, rayons 18/14, tailles) sont
   reprises telles quelles de la reference, centralisees en variables --tc-*
   pour rester swappables (motif par categorie a venir).
   ====================================================================== */
   
:root, html[data-theme="light"] {
  --tc-grad: linear-gradient(180deg, #f6effd 0%, #efe4fb 100%);
  --tc-edge: rgba(165, 103, 227, .45);
  --tc-edge-in: rgba(165, 103, 227, .18);
  --tc-veil: linear-gradient(180deg, rgba(250,245,255,0) 26%, rgba(250,245,255,.86) 54%, rgba(250,245,255,.98) 78%);
  --tc-glass: rgba(255, 255, 255, .72);
  --tc-glass-fg: var(--muted);
  --tc-glass-shadow: 0 1px 3px rgba(30, 20, 50, .10);
  --tc-motif-stroke: #a567e3;
  --tc-rings-op: .38;
  --tc-line-op: .26;
  --tc-stars-op: .35;
  --tc-stars-color: #a567e3;
  --tc-dot-idle: #c9c2b8;
  --tc-desc: var(--muted);
  --tc-chip-bg: #fff;
  --tc-chip-fg: var(--ink);
  --tc-chip-shadow: 0 1px 2px rgba(30, 20, 50, .12);
  /* Animation « radar calme » (etat actif) : violet de repos / vert de balayage. */
  --fa-ring: #a567e3;
  --fa-green: #16a34a;
}

html[data-theme="dark"] {
  --tc-grad: linear-gradient(180deg, #241c38 0%, #171126 100%);
  --tc-edge: rgba(189, 142, 240, .5);
  --tc-edge-in: rgba(189, 142, 240, .2);
  --tc-veil: linear-gradient(180deg, rgba(23,17,38,0) 26%, rgba(23,17,38,.86) 54%, rgba(23,17,38,.97) 78%);
  --tc-glass: rgba(255, 255, 255, .12);
  --tc-glass-fg: #cfc5e4;
  --tc-glass-shadow: none;
  --tc-motif-stroke: #bd8ef0;
  --tc-rings-op: .5;
  --tc-line-op: .35;
  --tc-stars-op: .65;
  --tc-stars-color: #e9dff8;
  --tc-dot-idle: #5c5470;
  --tc-desc: #cfc5e4;
  --tc-chip-bg: rgba(255, 255, 255, .12);
  --tc-chip-fg: #f0eaf8;
  --tc-chip-shadow: none;
  --fa-ring: #bd8ef0;
  --fa-green: #22c55e;
}

/* ----- Recto : conteneur full-art (degrade + couches en fond) ----- */
.grid .card-front, .deck-card .card-front {
  display: flex; flex-direction: column; gap: 0;
  padding: 0; position: relative;
  border-radius: 18px; background: var(--tc-grad); color: var(--ink);
  /*box-shadow: 0 2px 6px rgba(30, 20, 50, .10), 0 14px 34px rgba(30, 20, 50, .13);*/
  border: none;
}
.grid .card-front{
overflow: hidden;
}
/* Motif SVG plein-carte (couche du fond). Opacite par element (rings/line/stars)
   pour garder le point vert a pleine intensite, comme la reference. */
.grid .card-front .fullart, .deck-card .card-front .fullart {
  position: absolute; inset: 0; z-index: 0; pointer-events: none;
  color: var(--tc-motif-stroke);
}
.card-front .fullart svg { width: 100%; height: 100%; display: block; }
.card-front .fullart .tc-rings { opacity: var(--tc-rings-op); }
.card-front .fullart .tc-line { opacity: var(--tc-line-op); }
.card-front .fullart .tc-stars { opacity: var(--tc-stars-op); color: var(--tc-stars-color); }
.card-front .fullart .tc-dot { fill: var(--green); }

/* ----- Animation d'etat actif « radar calme » (skin full-art par defaut) -----
   PRINCIPE POUR LA SUITE (a respecter pour tout futur skin de carte) : chaque skin
   definit SA PROPRE animation d'etat actif, adaptee a SON illustration — minime,
   calme, et JAMAIS deux elements animes en meme temps hors du vocabulaire du skin.
   Celle-ci est PROPRE au skin full-art par defaut : ne pas la dupliquer telle quelle
   sur les futurs skins « ecole classique » / « Arcane » (chacun aura son propre geste).
   Geste : le point vert bat UNE fois, puis chaque cercle concentrique passe au vert A
   TOUR DE ROLE (du plus proche au plus lointain, jamais deux a la fois), puis long repos.
   Valeurs (reference) : cycle 5,6 s ; fenetre verte 3->11 % (~0,62 s) par cercle, fondu
   ease-in-out ; delais 0,62 / 1,34 / 2,06 / 2,78 s ; point : blip a 0-8 %.
   Scope = etat actif REEL (.state.active via :has()) → une carte inactive n'anime
   RIEN. Uniquement stroke/fill/opacity (pas de transform/layout). --fa-op = opacite de
   repos propre a chaque cercle (retour exact a l'etat de base). */
.fa-c1 { --fa-op: 1; }
.fa-c2 { --fa-op: .6; }
.fa-c3 { --fa-op: .35; }
.fa-c4 { --fa-op: .2; }
@keyframes fa-blip {
  0%       { opacity: 1; }
  4%       { opacity: .3; }
  8%, 100% { opacity: 1; }
}
@keyframes fa-ring {
  0%, 3%    { stroke: var(--fa-ring); opacity: var(--fa-op); }
  7%        { stroke: var(--fa-green); opacity: 1; }
  11%, 100% { stroke: var(--fa-ring); opacity: var(--fa-op); }
}
/* Desync entre cartes actives voisines : decalage de phase par paquet (nth-child) pour
   eviter l'effet « toutes en choeur » trop mecanique sur une grille dense. --fa-shift
   s'ajoute au delai de chaque cercle (le stagger interne 0,62 s reste intact) et pilote
   le point. Neutre (0s) par defaut → une carte isolee reste fidele a la reference. */
.grid .card, .deck-card .card { --fa-shift: 0s; }
.grid .card:nth-child(5n+2) { --fa-shift: -1.1s; }
.grid .card:nth-child(5n+3) { --fa-shift: -2.3s; }
.grid .card:nth-child(5n+4) { --fa-shift: -3.4s; }
.grid .card:nth-child(5n+5) { --fa-shift: -4.5s; }
.card-front:has(.state.active) .fullart .fa-dot {
  animation: fa-blip 5.6s ease-in-out infinite;
  animation-delay: var(--fa-shift, 0s);
}
.card-front:has(.state.active) .fullart .fa-c1,
.card-front:has(.state.active) .fullart .fa-c2,
.card-front:has(.state.active) .fullart .fa-c3,
.card-front:has(.state.active) .fullart .fa-c4 {
  animation: fa-ring 5.6s ease-in-out infinite;
}
.card-front:has(.state.active) .fullart .fa-c1 { animation-delay: calc(var(--fa-shift, 0s) + .62s); }
.card-front:has(.state.active) .fullart .fa-c2 { animation-delay: calc(var(--fa-shift, 0s) + 1.34s); }
.card-front:has(.state.active) .fullart .fa-c3 { animation-delay: calc(var(--fa-shift, 0s) + 2.06s); }
.card-front:has(.state.active) .fullart .fa-c4 { animation-delay: calc(var(--fa-shift, 0s) + 2.78s); }
@media (prefers-reduced-motion: reduce) {
  .card-front:has(.state.active) .fullart .fa-dot,
  .card-front:has(.state.active) .fullart .fa-c1,
  .card-front:has(.state.active) .fullart .fa-c2,
  .card-front:has(.state.active) .fullart .fa-c3,
  .card-front:has(.state.active) .fullart .fa-c4 { animation: none; }
}
/* Voile de lisibilite (transparent en haut -> quasi opaque en bas). */
.grid .card-front .card-veil, .deck-card .card-front .card-veil {
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: var(--tc-veil);
}
/* Double lisere : bordure externe 1.5px + filet interne 1px (pseudo). */
.grid .card-front .card-edge, .deck-card .card-front .card-edge {
  position: absolute; inset: 0; z-index: 5; pointer-events: none;
  border-radius: 18px; border: 2px solid var(--tag-c, var(--tc-edge));
}
.card-front .card-edge::after {
  content: ""; position: absolute; inset: 4px; border-radius: 14px;
  border: 1px solid var(--tc-edge-in);
}
/* Liseré catégorie ADOUCI (option 3 retenue après essai comparatif du 13/09) : couleur de
   la famille atténuée à 60 % + lueur intérieure. Seulement sur les cartes qui ont une
   famille (data-group) ; les autres gardent le liseré --tc-edge ci-dessus. */
.grid .card[data-group] .card-front .card-edge {
  border: 1.5px solid color-mix(in srgb, var(--tag-c) 60%, transparent);
  box-shadow: inset 0 0 14px color-mix(in srgb, var(--tag-c) 18%, transparent);
}

/* ----- Rangee haute : etat a gauche, controles a droite ----- */
.card-toprow {
  position: absolute; top: 11px; left: 11px; right: 11px; z-index: 3;
  display: flex; justify-content: space-between; align-items: center; gap: 8px;
}
/* Pastille d'etat (verre depoli). .active reste vert (lu par cardIsActive). */
.card-toprow .state {
  flex: 0 1 auto; min-width: 0;
  border-radius: 999px; padding: 9px 10px; gap: 6px;
  font-size: 11px; font-weight: 700; white-space: nowrap; overflow: hidden;
  background: var(--tc-glass); color: var(--tc-glass-fg);
  box-shadow: var(--tc-glass-shadow);
  -webkit-backdrop-filter: blur(5px); backdrop-filter: blur(5px);
}
.card-toprow .state .state-txt, .card-toprow .state { text-overflow: ellipsis; }
.card-toprow .state.active { color: var(--green); }
.card-toprow .dot-idle { background: var(--tc-dot-idle); opacity: 1; }
/* Groupe de controles ronds (26px, verre depoli). Ordre DOM = ♥ · partage · +deck · i. */
.card-toprow .card-icons { display: flex; align-items: center; gap: 5px; flex: none; }
.card-toprow .card-icons .card-like,
.card-toprow .card-icons .card-share,
.card-toprow .card-icons .card-add-deck,
.card-toprow .card-icons .flip-btn {
  position: static; top: auto; right: auto; margin: 0; padding: 0;
  width: 35px; height: 35px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--tc-glass); color: var(--tc-glass-fg);
  box-shadow: var(--tc-glass-shadow);
  -webkit-backdrop-filter: blur(5px); backdrop-filter: blur(5px);
}
.card-toprow .card-icons svg { width: 17px; height: 17px; }
/* Le « + deck » (action collection) ressort en violet ; les 3 autres restent neutres. */
.card-toprow .card-icons .card-add-deck { color: var(--amber); }
.card-toprow .card-icons .card-like:hover,
.card-toprow .card-icons .card-share:hover,
.card-toprow .card-icons .flip-btn:hover { color: var(--amber); }
.card-toprow .card-icons .card-like.liked { color: var(--amber); }
/* Compteur numerique masque dans la pastille ronde (comme la reference). */
.card-toprow .card-icons .like-n { display: none; }

/* ----- Contenu bas (ancre en bas, au-dessus du voile) ----- */
.card-content {
  position: relative; z-index: 3; margin-top: auto;
  padding: 0 14px 13px; min-width: 0;
}
.card-content .card-top { padding-right: 0; align-items: center; gap: 7px; }
.grid .card-content h3, .deck-card .card-content h3 {
  font-family: 'Baloo 2', sans-serif; font-size: 20px; font-weight: 800;
  line-height: 1.1; letter-spacing: -0.02em; color: var(--ink);
}
.grid .card-content .card-subtitle, .deck-card .card-content .card-subtitle {
  margin-top: 3px; font-size: 10.5px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .07em; color: var(--amber);
}
.grid .card-content p, .deck-card .card-content p {
  flex: none; margin-top: 7px; font-size: 12px; color: var(--tc-desc);
}
.card-content .card-desc { font-size: 16px; color: var(--tc-desc); }
.card-content .sub-count { color: var(--tc-desc); }
/* Chips de parametres : pilules verre/blanc (la reference). « + ajouter » reste la
   pilule violette pleine (.param-add) → les deux « + » ne se confondent pas. */
.card-content .param-chip {
  background: var(--tc-chip-bg); color: var(--tc-chip-fg); box-shadow: var(--tc-chip-shadow);
}
.card-content .switch-row, .card-content .param-row { margin-top: 10px; }

/* ----- Versos (info / partage / deck) : meme langage (degrade + lisere) ----- */
.grid .card-back, .grid .card-share-face, .grid .card-deck-face, .grid .card-task-face,
.deck-card .card-back, .deck-card .card-share-face, .deck-card .card-deck-face, .deck-card .card-task-face {
  background: var(--tc-grad); color: var(--ink);
  border: 1.5px solid var(--tc-edge); border-radius: 18px;
}
.grid .card-back::after, .grid .card-share-face::after, .grid .card-deck-face::after, .grid .card-task-face::after,
.deck-card .card-back::after, .deck-card .card-share-face::after, .deck-card .card-deck-face::after,
.deck-card .card-task-face::after {
  content: ""; position: absolute; inset: 4px; border-radius: 14px;
  border: 1px solid var(--tc-edge-in); pointer-events: none; z-index: 5;
}
.grid .card-back h3, .grid .card-share-face .share-face-title, .grid .card-deck-face .cdf-title,
.grid .card-task-face .tf-title,
.deck-card .card-back h3, .deck-card .card-share-face .share-face-title, .deck-card .card-deck-face .cdf-title,
.deck-card .card-task-face .tf-title {
  color: var(--ink);
}

/* ----- Survol : ombre renforcee (remplace le .card.flip:hover generique) ----- */
.grid .card.flip:hover .card-front, .deck-card:hover .ds-i0 .card-front {
  box-shadow: 0 4px 12px rgba(30, 20, 50, .16), 0 18px 44px rgba(30, 20, 50, .20);
}

/* ----- Perf mobile : le blur generalise (voile + ~178 pastilles verre) peut ramer.
   Sous 620px on retire backdrop-filter ; les fonds translucides restent (rendu tres
   proche, sans le cout du flou). Documente dans le rapport. ----- */
@media (max-width: 620px) {
  .card-toprow .state,
  .card-toprow .card-icons .card-like,
  .card-toprow .card-icons .card-share,
  .card-toprow .card-icons .card-add-deck,
  .card-toprow .card-icons .flip-btn {
    -webkit-backdrop-filter: none; backdrop-filter: none;
  }
}

/* ===== Fix retournement : le RECTO transparaissait « en miroir » par-dessus le VERSO ==
   Depuis la refonte full-art, backface-visibility etait pris en defaut (overflow:hidden
   + preserve-3d sur les faces) : la face non tournee vers l'utilisateur restait visible.
   On masque donc EXPLICITEMENT la face inactive du couple recto/verso (les faces partage
   et deck sont deja gerees en display:none, l.1048/1058). Le basculement se fait a
   MI-PARCOURS du flip (delai = moitie de la transition .5s) pour garder l'animation
   fluide. Scope grille + tuiles-deck (meme perimetre que la refonte). =================== */
.grid .card-front, .grid .card-back,
.deck-card .card-front, .deck-card .card-back {
  transition: box-shadow .35s ease, border-color .35s ease, visibility 0s linear .25s;
}
.grid .card.flipped .card-front,
.deck-card .card.flipped .card-front { visibility: hidden; }
.grid .card:not(.flipped) .card-back,
.deck-card .card:not(.flipped) .card-back { visibility: hidden; }
@media (prefers-reduced-motion: reduce) {
  .grid .card-front, .grid .card-back,
  .deck-card .card-front, .deck-card .card-back { transition: none; }
}

/* ===================== Phase 3 : boutique de skins (squelette) ===================== */
/* Mise en page de /boutique. */
/* width:100% : MEME fix shrink-to-fit que main#alertes / .decks-main / .favoris-main.
   Sans lui, body flex-column + .wrap{margin:auto} desactive le stretch -> le main
   retrecit a son contenu et auto-fill s'effondre (grille trop fine, cartes rabotees). */
.shop-main { padding-bottom: 40px; width: 100%; }
.shop-balance { margin: 6px 0 16px; color: var(--muted); }
.shop-balance strong { color: var(--ink); font-variant-numeric: tabular-nums; }
/* GRILLE : strictement les memes colonnes que le kiosque (.grid) -> meme largeur de
   carte. Chaque tuile est elle-meme un .grid (le CSS carte est integralement scope
   « .grid .card-* ») portant la classe de skin ; on neutralise donc sa mise en page
   de grille pour qu'elle se comporte comme une simple cellule. */
.shop-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(300px, 100%), 1fr)); gap: 18px; align-content: start; }
.shop-grid > .shop-item { padding: 0; width: 100%; }
/* Tuile de skin de DECK : le wrapper porte .grid (pour la cascade « .grid .deck-card »),
   on neutralise donc sa mise en page de grille. Les tuiles de skin de CARTE ont leur
   propre mise en page (colonne apercu + CTA, plus bas) et ne sont pas concernees. */
.shop-grid > .shop-item:not(.shop-card-item) { display: block; }
@media (max-width: 620px) {
  .shop-grid { grid-template-columns: repeat(1, minmax(0, 1fr)); gap: 12px; }
}

/* ---- APERCU D'UN SKIN DE CARTE : BOITE DE DIMENSIONS FIXES + CTA SOUS LA BOITE ----
   Correctif du 06/09. Avant : la carte etait rendue « taille reelle » directement dans
   la cellule de grille et le CTA vivait DANS le recto (.card-action). Les 4 skins
   structures posent .card-front en `display:grid` avec des zones nommees, dont `foot`
   pour .switch-row : le CTA se retrouvait donc AVALE par la carte (L'Assemblee), les
   hauteurs partaient (Le Dresseur debordait la cellule, L'Arcane laissait ~150px de
   vide) et le titre chevauchait la rangee d'icones (L'Ecole classique).
   Desormais : (1) la carte vit dans .shop-preview, boite de taille FIXE et IDENTIQUE
   pour les 10 skins ; (2) le CTA est un frere de cette boite, donc hors d'atteinte de
   toute grid-area de skin -> meme hauteur de tuile et meme emplacement de CTA partout.
   Ratio 2/3 conserve : la boite EST au ratio 2/3, et .grid .card-front aussi. */
.shop-item.shop-card-item { --shop-prev-w: 240px; display: flex; flex-direction: column; align-items: center; gap: 12px; }
.shop-preview {
  position: relative; width: var(--shop-prev-w, 240px); height: calc(var(--shop-prev-w, 240px) * 3 / 2);
  max-width: 100%; overflow: hidden; border-radius: 16px;
}
/* La scene est le .grid qui porte la classe de skin. En absolu top/left : c'est le seul
   ancrage qui rende `transform: scale()` previsible (un centrage laissait la boite
   pleine taille deborder puis se faire clipper de travers). */
.shop-preview > .shop-stage { position: absolute; top: 0; left: 0; display: block; padding: 0; margin: 0; }
/* TEINTES (base + fullart-*) : recolorations pures -> la carte est rendue directement A
   LA TAILLE de la boite. Mini carte-front au ratio 2/3, radar reel anime. */
.shop-preview.has-card > .shop-stage { width: var(--shop-prev-w); }
/* STRUCTURES : leur CSS est calibre sur la geometrie NATURELLE de la carte (cadres,
   sceaux, encarts en px). On rend donc a 300px puis on reduit par transform:scale
   depuis transform-origin:top-left, dans une boite DE LA TAILLE SCALEE (240 = 300 x .8,
   360 = 450 x .8) : rien a centrer, donc rien a deborder. */
.shop-preview.has-card-full > .shop-stage { width: 300px; transform: scale(.8); transform-origin: top left; }
.shop-preview .card { width: 100%; max-width: none; margin: 0; }
/* Apercu inerte : aucun switch, coeur ou « i » n'est cliquable dans la boutique. */
.shop-preview, .shop-preview * { pointer-events: none; }
/* Desynchronise les radars d'une tuile a l'autre (chaque carte est seule dans sa scene :
   les :nth-child(5n+k) du kiosque, qui visent des cartes soeurs, ne peuvent pas jouer). */
.shop-grid > .shop-item:nth-child(5n+2) .shop-stage .card { --fa-shift: -1.1s; }
.shop-grid > .shop-item:nth-child(5n+3) .shop-stage .card { --fa-shift: -2.3s; }
.shop-grid > .shop-item:nth-child(5n+4) .shop-stage .card { --fa-shift: -3.4s; }
.shop-grid > .shop-item:nth-child(5n+5) .shop-stage .card { --fa-shift: -4.5s; }

/* Skin equipe : liseré ambre autour de la carte (etat, pas decoration de skin). */
.shop-item.is-equipped > .shop-preview,
.shop-item.is-equipped > .deck-card { outline: 2px solid var(--amber); outline-offset: 3px; border-radius: 16px; }
/* Le bouton d'achat/equipement prend la place du switch d'abonnement. */
.shop-act { display: flex; }
.shop-act .sup-btn { width: 100%; justify-content: center; }
/* CTA toujours au meme endroit, et de la meme largeur que la boite d'apercu. */
.shop-item.shop-card-item > .shop-act { width: var(--shop-prev-w, 240px); max-width: 100%; }
/* Deux rayons : « Skins de cartes » / « Skins de decks ». */
.shop-h2 { font-family: 'Baloo 2', sans-serif; font-size: 22px; margin: 26px 0 2px; }
/* Skins de DECK : la tuile est celle du kiosque (.grid > .deck-card + LBADeckStack),
   AUCUN CSS specifique — le wrapper .shop-item porte deja .grid, donc la geometrie
   (carte de devant, eventail, ancrage du ruban) est exactement celle des tuiles-deck.
   Seule exception : la tuile n'est pas un lien ici, et le bouton injecte a la place du
   switch est un vrai bouton -> curseur normal plutot que la main de la tuile-lien. */
.shop-item > .deck-card, .shop-item > .deck-card * { cursor: default; }
.shop-item .ds-i0 .shop-act .sup-btn { cursor: pointer; }
.shop-item .ds-i0 .shop-act .sup-btn[disabled] { cursor: default; }

/* --- Teintes des skins du seed de phase 3. --skin-accent EST le rendu d'un skin de
   DECK (lisere autour de la tuile, cf. plus bas) -> menthe / or-royal sont fonctionnels
   et restent au catalogue. aurore / nuit-etoilee etaient de type 'dashboard', ou ce
   lisere n'a jamais rien habille : retires le 04/08/2026 (active=false, cf. init.sql) ;
   leur teinte est conservee ici pour un compte qui les equiperait encore. --- */
.skin-aurore { --skin-accent: #d9b8ff; }
.skin-nuit-etoilee { --skin-accent: #3a4a80; }
.skin-menthe { --skin-accent: #7fe3c0; }
.skin-or-royal { --skin-accent: #e3c567; }

/* --- Branchement du rendu : le skin equipe est une classe additionnelle qui porte
   --skin-accent ; on materialise juste un liseré (placeholder visible) le temps que les
   vrais visuels arrivent. Dashboard = classe sur #grid (cascade sur .card). Deck =
   classe sur la tuile kiosque et sur la vue detail. --- */
#grid[class*="skin-"] .card,
.shop-stage[class*="skin-"] .card { box-shadow: inset 0 0 0 2px var(--skin-accent, transparent); }
/* Skin de DECK : le lisere entourait la TUILE (rectangle englobant invisible derriere
   la pile) -> quasiment pas vu. Il ceinture desormais CHACUNE des 3 cartes de la pile,
   eventail compris : c'est la seule surface reellement visible d'une tuile-deck.
   L'anneau est ajoute EN TETE des ombres d'elevation de .grid .card-front, qui sont
   reprises telles quelles (une seule propriete box-shadow : les redeclarer est
   obligatoire, sinon l'elevation saute). */
.deck-card[class*="skin-"] .ds-face .card-front {
  box-shadow:
    0 0 0 3px var(--skin-accent, transparent),
    0 2px 6px rgba(30, 20, 50, .10),
    0 14px 34px rgba(30, 20, 50, .13);
}
#deck-view[class*="skin-"] .card { box-shadow: inset 0 0 0 2px var(--skin-accent, transparent); }

/* --- Skins de deck « RUBAN » (04/08/2026). Deuxieme famille de skins de deck : au lieu
   d'un lisere autour des cartes, ils recolorent LE RUBAN — la piece la plus visible d'une
   tuile-deck. Ils ne surchargent que le fond du ruban ; la teinte tint-N choisie par
   l'auteur du deck continue de piloter tout le reste (motif, ruban des decks sans skin).
   Ils ne definissent PAS --skin-accent : l'effet EST le ruban, pas un lisere de plus.
   SPECIFICITE : le fond du ruban est reecrit en entier par .tint-11 (degrade arc-en-ciel,
   0,2,0) et par html[data-theme="dark"] .tint-11 (0,3,1). Pour rester prioritaire sur
   N'IMPORTE QUELLE teinte, on redeclare ici la recette de fond de .ds-ribbon a l'identique
   (seule --deck-bg change) sous deux selecteurs qui couvrent les deux cas, ce bloc etant
   place APRES eux dans le fichier. Toute evolution de .ds-ribbon doit etre repercutee ici.
   Fond volontairement CLAIR dans les deux themes (c'est le produit vendu) -> la couleur du
   texte est fixee en dur, sinon --ink la rendrait illisible en sombre. --- */
html .skin-ruban-blanc .ds-ribbon,
html[data-theme="dark"] .skin-ruban-blanc .ds-ribbon { --deck-bg: #f4edfb; --ds-ribbon-ink: #2a2140; }
html .skin-ruban-bleu .ds-ribbon,
html[data-theme="dark"] .skin-ruban-bleu .ds-ribbon { --deck-bg: #dbe9fb; --ds-ribbon-ink: #1c2b45; }

html .skin-ruban-blanc .ds-ribbon, html[data-theme="dark"] .skin-ruban-blanc .ds-ribbon,
html .skin-ruban-bleu .ds-ribbon, html[data-theme="dark"] .skin-ruban-bleu .ds-ribbon {
  color: var(--ds-ribbon-ink);
  background:
    radial-gradient(100% 50% at right, var(--ds-ribbon-shadow) 98%, transparent 101%) 0 var(--_g),
    radial-gradient(100% 50% at left, var(--ds-ribbon-shadow) 98%, transparent 101%) 100% var(--_g),
    conic-gradient(at var(--r) calc(2 * var(--r)), transparent 25%, var(--deck-bg) 0) 0 0 / calc(100% - var(--r)) 100%;
}
html .skin-ruban-blanc .ds-ribbon-meta, html[data-theme="dark"] .skin-ruban-blanc .ds-ribbon-meta,
html .skin-ruban-bleu .ds-ribbon-meta, html[data-theme="dark"] .skin-ruban-bleu .ds-ribbon-meta {
  color: color-mix(in srgb, var(--ds-ribbon-ink) 62%, transparent);
}

/* =====================================================================
   STRUCTURE NEUTRE (socle des skins « en cadre »). cards.js emet UNE structure
   unique enrichie ; par defaut ces ajouts sont INERTES pour ne RIEN changer au
   full-art ni aux 5 teintes (contrainte n°1) :
   - .card-art (wrapper motif+voile) = display:contents -> fullart/veil restent
     positionnes exactement comme avant (containing block = .card-front).
   - .card-seal (x2, marques generiques) et .card-attacks (bloc Dresseur) =
     display:none -> hors flux, aucun impact visuel/layout.
   Les skins structures (plus bas) rebasculent ces elements a l'affichage. --- */
.card-front > .card-art { display: contents; }
.card-front .card-seal { display: none; }
.card-front .card-attacks { display: none; }
.card-front .card-foot { display: none; }
/* Elements neutres reutilises par L'Assemblee (anatomie carte a collectionner) :
   badge d'edition (ligne de type), encart stat chevauchant (compteur de likes) et
   fine ligne d'info technique -> hors flux par defaut (aucun impact full-art/teintes). */
.card-front .card-edition,
.card-front .card-stat,
.card-front .card-fineprint { display: none; }
/* Zone d'action : transparente par defaut (les enfants restent des flex-items de
   .card-content -> full-art/teintes INCHANGES). Les skins structures la rendent visible. */
.card-front .card-action { display: contents; }
/* Boite de texte (description + action regroupees) : transparente par defaut (meme
   principe que .card-action) -> p et card-action restent des enfants effectifs de
   .card-content, full-art/teintes INCHANGES. Les skins structures (L'Assemblee =
   .frame-text-box) la rebasculent en boite reelle. NB : les selecteurs .card-content p
   sont en DESCENDANT (pas « > p ») pour rester valides a travers ce wrapper. */
.card-front .card-textbox { display: contents; }

/* =====================================================================
   TEINTES DU FULL-ART (5 skins boutique : Or / Prisme / Braise / Ocean /
   Aurore). Recolorations PURES du skin par defaut « disposition A » : meme
   DOM .card-front, meme animation « radar calme » (.fa-c1..4 / .fa-dot),
   ZERO markup, ZERO keyframe dediee. Un skin = une classe qui REDEFINIT les
   variables full-art (--tc-grad / --tc-motif-stroke / --fa-ring / --tc-edge
   / --tc-veil) heritees jusqu'a .card-front. Le VERT d'alerte ne change
   JAMAIS (--green / --fa-green / .tc-dot intacts). --skin-accent:transparent
   neutralise le lisere placeholder generique (le vrai lisere = .card-edge
   recolore via --tc-edge). Scope-agnostique : la classe portee sur #grid
   (dashboard), .deck-card (tuile) ou #deck-view cascade pareil sur .card-front,
   donc ces skins se rendent aussi a l'echelle reduite d'un deck-stack (recolor
   seul, aucun risque de mise en page). prefers-reduced-motion : deja couvert
   par le bloc radar par defaut (memes classes .fa-*), rien a ajouter. --- */

/* — Or — */
.skin-fullart-or {
  --skin-accent: transparent;
  --tc-grad: linear-gradient(180deg, #fdf6e8, #f6e8c8);
  --tc-motif-stroke: #c9973f; --fa-ring: #c9973f;
  --tc-edge: rgba(201,151,63,.55); --tc-edge-in: rgba(201,151,63,.22);
  --tc-veil: linear-gradient(180deg, rgba(253,246,232,0) 26%, rgba(253,246,232,.86) 54%, rgba(253,246,232,.98) 78%);
}
.skin-fullart-or .card-content .card-subtitle { color: #b07f2c; }
html[data-theme="dark"] .skin-fullart-or {
  --tc-grad: linear-gradient(180deg, #332a16, #241d0e);
  --tc-motif-stroke: #e2b45f; --fa-ring: #e2b45f;
  --tc-edge: rgba(226,180,95,.55); --tc-edge-in: rgba(226,180,95,.22);
  --tc-veil: linear-gradient(180deg, rgba(36,29,14,0) 26%, rgba(36,29,14,.86) 54%, rgba(36,29,14,.97) 78%);
}
html[data-theme="dark"] .skin-fullart-or .card-front { color: #f5edda; }
html[data-theme="dark"] .skin-fullart-or .card-content .card-subtitle { color: #e2b45f; }

/* — Braise — */
.skin-fullart-braise {
  --skin-accent: transparent;
  --tc-grad: linear-gradient(180deg, #fdeeea, #f8d9d0);
  --tc-motif-stroke: #c96a55; --fa-ring: #c96a55;
  --tc-edge: rgba(201,106,85,.5); --tc-edge-in: rgba(201,106,85,.2);
  --tc-veil: linear-gradient(180deg, rgba(253,238,234,0) 26%, rgba(253,238,234,.86) 54%, rgba(253,238,234,.98) 78%);
}
.skin-fullart-braise .card-content .card-subtitle { color: #b25642; }
html[data-theme="dark"] .skin-fullart-braise {
  --tc-grad: linear-gradient(180deg, #3a2018, #26140f);
  --tc-motif-stroke: #e08a72; --fa-ring: #e08a72;
  --tc-edge: rgba(224,138,114,.5); --tc-edge-in: rgba(224,138,114,.2);
  --tc-veil: linear-gradient(180deg, rgba(38,20,16,0) 26%, rgba(38,20,16,.86) 54%, rgba(38,20,16,.97) 78%);
}
html[data-theme="dark"] .skin-fullart-braise .card-content .card-subtitle { color: #e08a72; }

/* — Ocean — */
.skin-fullart-ocean {
  --skin-accent: transparent;
  --tc-grad: linear-gradient(180deg, #eaf2fb, #d8e6f6);
  --tc-motif-stroke: #5b7db8; --fa-ring: #5b7db8;
  --tc-edge: rgba(91,125,184,.5); --tc-edge-in: rgba(91,125,184,.2);
  --tc-veil: linear-gradient(180deg, rgba(234,242,251,0) 26%, rgba(234,242,251,.86) 54%, rgba(234,242,251,.98) 78%);
}
.skin-fullart-ocean .card-content .card-subtitle { color: #48679c; }
html[data-theme="dark"] .skin-fullart-ocean {
  --tc-grad: linear-gradient(180deg, #18243a, #0f1726);
  --tc-motif-stroke: #7fa3d8; --fa-ring: #7fa3d8;
  --tc-edge: rgba(127,163,216,.5); --tc-edge-in: rgba(127,163,216,.2);
  --tc-veil: linear-gradient(180deg, rgba(14,22,38,0) 26%, rgba(14,22,38,.86) 54%, rgba(14,22,38,.97) 78%);
}
html[data-theme="dark"] .skin-fullart-ocean .card-content .card-subtitle { color: #7fa3d8; }

/* — Aurore (distinct du skin legacy « skin-aurore ») — */
.skin-fullart-aurore {
  --skin-accent: transparent;
  --tc-grad: linear-gradient(180deg, #fdeef7, #f6d9ec);
  --tc-motif-stroke: #c58ab0; --fa-ring: #c58ab0;
  --tc-edge: rgba(197,138,176,.5); --tc-edge-in: rgba(197,138,176,.2);
  --tc-veil: linear-gradient(180deg, rgba(253,238,247,0) 26%, rgba(253,238,247,.86) 54%, rgba(253,238,247,.98) 78%);
}
.skin-fullart-aurore .card-content .card-subtitle { color: #a86c93; }
html[data-theme="dark"] .skin-fullart-aurore {
  --tc-grad: linear-gradient(180deg, #3a1830, #26101f);
  --tc-motif-stroke: #d8a6c4; --fa-ring: #d8a6c4;
  --tc-edge: rgba(216,166,196,.5); --tc-edge-in: rgba(216,166,196,.2);
  --tc-veil: linear-gradient(180deg, rgba(38,16,30,0) 26%, rgba(38,16,30,.86) 54%, rgba(38,16,30,.97) 78%);
}
html[data-theme="dark"] .skin-fullart-aurore .card-content .card-subtitle { color: #d8a6c4; }

/* — Prisme — motif multicolore (anneaux de teintes differentes) + lisere
   irise conic. Le vert d'alerte reste le vert ; seules les couleurs de REPOS
   des anneaux different (le radar ramene chacun au vert a tour de role). */
.skin-fullart-prisme {
  --skin-accent: transparent;
  --tc-grad: linear-gradient(150deg, #fbeef6, #eef2fb 45%, #eefaf3);
  --tc-motif-stroke: #a567e3; --fa-ring: #a567e3;
  --tc-edge: rgba(165,103,227,.55); --tc-edge-in: rgba(165,103,227,.22);
  --tc-veil: linear-gradient(180deg, rgba(252,247,253,0) 26%, rgba(252,247,253,.86) 54%, rgba(252,247,253,.98) 78%);
}
.skin-fullart-prisme .card-content .card-subtitle { color: #a567e3; }
.skin-fullart-prisme .fullart .fa-c2 { stroke: #c58ab0; --fa-ring: #c58ab0; }
.skin-fullart-prisme .fullart .fa-c3 { stroke: #5b7db8; --fa-ring: #5b7db8; }
.skin-fullart-prisme .fullart .fa-c4 { stroke: #5fa8a4; --fa-ring: #5fa8a4; }
html[data-theme="dark"] .skin-fullart-prisme {
  --tc-grad: linear-gradient(150deg, #241c38, #1a2238 45%, #182e28);
  --tc-motif-stroke: #bd8ef0; --fa-ring: #bd8ef0;
  --tc-edge: rgba(189,142,240,.55); --tc-edge-in: rgba(189,142,240,.22);
  --tc-veil: linear-gradient(180deg, rgba(23,17,38,0) 26%, rgba(23,17,38,.86) 54%, rgba(23,17,38,.97) 78%);
}
html[data-theme="dark"] .skin-fullart-prisme .fullart .fa-c2 { stroke: #d8a6c4; --fa-ring: #d8a6c4; }
html[data-theme="dark"] .skin-fullart-prisme .fullart .fa-c3 { stroke: #7fa3d8; --fa-ring: #7fa3d8; }
html[data-theme="dark"] .skin-fullart-prisme .fullart .fa-c4 { stroke: #7fd8c0; --fa-ring: #7fd8c0; }
/* Lisere irise : conic + mask-composite (ring seul). Guarde par @supports —
   sans mask-composite, on retombe sur le double-lisere violet standard
   (--tc-edge/-in ci-dessus), jamais un rectangle plein qui masquerait la carte. */
@supports ((mask-composite: exclude) or (-webkit-mask-composite: xor)) {
  /* Le prefixe `html` (0,3,1) doit battre la bordure de base .grid/.deck-card
     .card-front .card-edge (0,3,0) ET la regle d'apercu (0,3,0) — sinon lisere
     double. Hors @supports (fallback), rien de tout ca : le lisere violet
     standard --tc-edge s'affiche normalement. */
  html .skin-fullart-prisme .card-front .card-edge {
    border: none;
    background: conic-gradient(from 40deg, #e8b4c8, #c4a8e8, #a8cbe8, #a8e8d0, #e8e0a8, #e8b4c8);
    padding: 1.6px;
    -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
    -webkit-mask-composite: xor;
    mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
    mask-composite: exclude;
  }
  html .skin-fullart-prisme .card-front .card-edge::after { display: none; }
}

/* =====================================================================
   SKINS STRUCTURES (4) — « structure unique ». S'appuient sur la structure
   neutre enrichie (.card-art / .card-top+.card-seal / .card-attacks) : cards.js
   emet TOUJOURS le meme markup, ces skins ne font que du CSS scope par la classe
   posee sur #grid ou .deck-card (aucune branche de rendu, aucun re-render).
   INVARIANTS : les 4 icones (like/share/+deck/i) restent en absolu haut-droite
   (deja le cas, decision actee) ; l'etat .state reste en overlay haut-gauche
   (coherent avec le full-art). Chaque skin a SA propre animation d'etat actif,
   jamais partagee ; neutralisee par prefers-reduced-motion (bloc en fin). ---- */
.skin-ecole-classique .card-front,
.skin-arcane .card-front,
.skin-assemblee .card-front,
.skin-dresseur .card-front { display: grid; overflow: hidden; }
/* REGLE 2/3 PARTOUT : les structures gardent l'aspect-ratio:2/3 herite de la base
   (comme full-art/teintes), dans TOUS les contextes (grille, decks, boutique) -> geometrie
   homogene. Le debordement (cartes paramétrées multi-chips, descriptions longues) est gere
   par DEGRADATION en CSS pur : les regions FLEXIBLES (fenetre d'art, boite de texte) sont
   en minmax(0,...) et cedent la place ; la zone d'action (chips/select/switch/etat) est en
   `auto` -> jamais tronquee. La description/ambiance se clippe par le bas (overflow:hidden
   des boites de texte : l'ambiance ::after part avant la description). Voir chaque skin. */
.skin-ecole-classique .card-content,
.skin-arcane .card-content,
.skin-assemblee .card-content,
.skin-dresseur .card-content { display: contents; }
/* Etat en overlay haut-gauche (les icones sont deja en absolu haut-droite). */
.skin-ecole-classique .card-toprow,
.skin-arcane .card-toprow,
.skin-assemblee .card-toprow,
.skin-dresseur .card-toprow { position: absolute; top: 10px; left: 12px; right: 12px; z-index: 6; display: flex; align-items: center; }
/* Icones poussees a DROITE (margin-left:auto tient meme quand l'etat est masque, ex. Dresseur). */
.skin-ecole-classique .card-icons,
.skin-arcane .card-icons,
.skin-assemblee .card-icons,
.skin-dresseur .card-icons { margin-left: auto; }
/* Le voile plein-carte n'a de sens qu'en pleine image : masque sur les cadres. */
.skin-ecole-classique .card-front .card-veil,
.skin-arcane .card-front .card-veil,
.skin-assemblee .card-front .card-veil,
.skin-dresseur .card-front .card-veil { display: none; }
/* .card-art redevient une boite : le motif s'y confine (fenetre d'art / medaillon). */
.skin-ecole-classique .card-art,
.skin-arcane .card-art,
.skin-assemblee .card-art,
.skin-dresseur .card-art { display: block; position: relative; overflow: hidden; }
/* Motif CLAIR dans les fenetres d'art (fond violet) — sinon violet-sur-violet invisible. */
.skin-ecole-classique .card-front .card-art .fullart,
.skin-assemblee .card-front .card-art .fullart,
.skin-dresseur .card-front .card-art .fullart { color: #f0e8fb; }
/* Le SVG plein-carte est portrait : dans une fenetre large il faut l'afficher a sa
   hauteur naturelle, ancre en haut -> le radar + la ligne sismique deviennent visibles. */
.skin-ecole-classique .card-front .card-art .fullart svg,
.skin-assemblee .card-front .card-art .fullart svg,
.skin-dresseur .card-front .card-art .fullart svg { height: auto; }
/* Chips + boutons des paramétrées : flux qui passe a la ligne, jamais empile. */
.skin-ecole-classique .card-action .param-row, .skin-arcane .card-action .param-row,
.skin-assemblee .card-action .param-row, .skin-dresseur .card-action .param-row {
  display: flex; flex-wrap: wrap; gap: 5px; align-items: center; width: 100%;
}
/* Compteur d'abonnes masque sur les skins structures (absent de la reference). */
.skin-ecole-classique .sub-count, .skin-arcane .sub-count,
.skin-assemblee .sub-count, .skin-dresseur .sub-count { display: none; }

/* --- L'Ecole classique --- */
.skin-ecole-classique .card-front {
  grid-template-areas: "name" "art" "type" "text" "chips" "foot";
  grid-template-rows: auto minmax(0, 88px) auto minmax(0, 1fr) auto auto; gap: 6px;
  padding: 36px 16px 12px; background: linear-gradient(160deg,#efe4fb,#e2d2f5); color: var(--ink);
}
/* Chips a TAILLE CLASSIQUE dans la boite parcheminee : c'est la zone de texte (flexible,
   minmax(0,1fr)) qui cede quand les chips s'accumulent, jamais le switch (grid-area foot,
   auto) -> ordre de sacrifice respecte (fonctionnel > description). */
.skin-ecole-classique .card-front .card-edge { inset: 9px; border: 2.5px solid #b493dd; box-shadow: inset 0 0 0 1.5px #fff; border-radius: 12px; }
.skin-ecole-classique .card-front .card-edge::after { display: none; }
.skin-ecole-classique .card-top {
  grid-area: name; background: #fff; border: 1.5px solid #cbb3e8; border-radius: 9px;
  padding: 6px 10px; display: flex; align-items: center; gap: 6px; box-shadow: 0 1px 2px rgba(30,20,50,.12); z-index: 2;
}
.skin-ecole-classique .card-top h3 { font-family: 'Baloo 2', sans-serif; font-size: 14px; font-weight: 800; line-height: 1.05; flex: 1 1 auto; }
/* Sceau « ! » masque : place a droite de la plaque, il tomberait sous les icones
   haut-droite -> artefact « a moitie couvert ». Mieux vaut l'absence nette. */
.skin-ecole-classique .card-front .card-seal { display: none; }
.skin-ecole-classique .card-art {
  grid-area: art; border: 1.5px solid #cbb3e8; border-radius: 9px;
  background: linear-gradient(180deg,#d9c4f2,#b795e0); color: #f3ecfc; z-index: 2;
}
.skin-ecole-classique .card-art::after {
  content: ""; position: absolute; inset: -20%; z-index: 1;
  background: linear-gradient(115deg,transparent 34%,rgba(255,255,255,.55) 50%,transparent 66%); transform: translateX(-130%);
}
.skin-ecole-classique .card-front .card-subtitle {
  grid-area: type; background: #fff; border: 1.5px solid #cbb3e8; border-radius: 8px; padding: 4px 9px;
  font-size: 10.5px; font-weight: 700; color: var(--muted); text-transform: none; letter-spacing: 0;
  display: flex; align-items: center; z-index: 2;
}
.skin-ecole-classique .card-front .card-subtitle::after { content: ""; width: 11px; height: 11px; background: var(--amber); border-radius: 2px; transform: rotate(45deg); margin-left: auto; flex: none; }
.skin-ecole-classique .card-content p {
  grid-area: text; background: #fdfaf4; border: 1.5px solid #cbb3e8; border-radius: 9px; padding: 8px 10px;
  font-size: 11.3px; color: #4a4440; line-height: 1.38; z-index: 2; margin: 0;
  overflow: hidden; min-height: 0;
}
.skin-ecole-classique .card-desc { -webkit-line-clamp: 3; }
.skin-ecole-classique .card-content p::after {
  content: "« La terre parle avant les journaux. »"; display: block; margin-top: 6px; font-size: 10.3px; font-style: italic; color: var(--muted);
}
/* Chips DANS la boite parcheminee (cf. maquette brouillon.png), switch en bas HORS boite.
   .card-action redevient transparent (display:contents) pour placer param-row (chips+ajouter+
   select) et switch-row dans des grid-areas distinctes. */
.skin-ecole-classique .card-action { display: contents; }
.skin-ecole-classique .param-row {
  grid-area: chips; margin: -6px 0 0; z-index: 2;
  background: #fdfaf4; border: 1.5px solid #cbb3e8; border-top: none; border-radius: 0 0 9px 9px;
  padding: 2px 10px 8px; display: flex; flex-wrap: wrap; gap: 5px; align-items: center;
}
.skin-ecole-classique .switch-row, .skin-ecole-classique .sub-form { grid-area: foot; z-index: 2; }
/* Boite jointe : des qu'un param existe, le p perd son bord/radius bas pour se souder aux chips. */
.skin-ecole-classique .card-front:has(.param-row) .card-content p { border-bottom: none; border-radius: 9px 9px 0 0; }
/* FONDU (P1 Arcane + P3 Ecole, factorise) : quand la boite de texte deborde (overflow:hidden),
   la derniere ligne s'estompe au lieu d'etre tranchee net. Le mask est permanent mais invisible
   quand tout tient (la zone de fondu recouvre le padding-bas). */
/*.skin-ecole-classique .card-content p,*/
.skin-arcane .card-content p {
  -webkit-mask-image: linear-gradient(180deg, #000 calc(100% - 1.4em), transparent 100%);
          mask-image: linear-gradient(180deg, #000 calc(100% - 1.4em), transparent 100%);
}
/* animation « le sceau & le reflet » */
@keyframes sk-ec-seal { 0%,100%{ box-shadow: 0 0 0 0 rgba(165,103,227,0);} 4%{ box-shadow: 0 0 0 6px rgba(165,103,227,.30);} 9%{ box-shadow: 0 0 0 0 rgba(165,103,227,0);} }
@keyframes sk-ec-sweep { 0%,9%{ transform: translateX(-130%);} 22%{ transform: translateX(130%);} 100%{ transform: translateX(130%);} }
.skin-ecole-classique .card-front:has(.state.active) .card-seal:first-of-type { animation: sk-ec-seal 6.2s ease-in-out infinite; }
.skin-ecole-classique .card-front:has(.state.active) .card-art::after { animation: sk-ec-sweep 6.2s ease-in-out infinite; }
/* nuit */
html[data-theme="dark"] .skin-ecole-classique .card-front { background: linear-gradient(160deg,#2c2148,#1d1633); color: #f0eaf8; }
html[data-theme="dark"] .skin-ecole-classique .card-front .card-edge { border-color: #6d51a0; box-shadow: inset 0 0 0 1.5px rgba(255,255,255,.14); }
html[data-theme="dark"] .skin-ecole-classique .card-top,
html[data-theme="dark"] .skin-ecole-classique .card-front .card-subtitle { background: #352a52; border-color: #6d51a0; color: #e8dff7; }
html[data-theme="dark"] .skin-ecole-classique .card-content p { background: #262038; border-color: #6d51a0; color: #d8cfeb; }
html[data-theme="dark"] .skin-ecole-classique .param-row { background: #262038; border-color: #6d51a0; }
html[data-theme="dark"] .skin-ecole-classique .card-content p::after { color: #b3a6cf; }
html[data-theme="dark"] .skin-ecole-classique .card-seal:first-of-type { background: var(--violet-l, #bd8ef0); color: #1c1428; }
html[data-theme="dark"] .skin-ecole-classique .card-art { border-color: #6d51a0; background: linear-gradient(180deg,#4d3a78,#33255c); }

/* --- L'Arcane (pleine image, medaillon centre) --- */
.skin-arcane .card-front {
  grid-template-areas: "med" "num" "title" "desc" "foot"; grid-template-rows: minmax(0, 120px) auto auto minmax(0, 1fr) auto;
  justify-items: center; text-align: center; gap: 4px; padding: 40px 26px 22px; background: #fbf7ef; color: var(--ink);
}
.skin-arcane .card-front .card-veil { display: none; }
.skin-arcane .card-front .card-edge { inset: 8px; border: 1.5px solid var(--violet); border-radius: 12px; }
.skin-arcane .card-front .card-edge::after { content: ""; position: absolute; inset: 3px; border: 1px solid rgba(165,103,227,.4); border-radius: 9px; }
.skin-arcane .card-toprow { top: 14px; left: 16px; }
/* medaillon = .card-art rond ; anneau pointille = ::before (rotation) ; motif = fullart ; point = .fa-dot */
.skin-arcane .card-art {
  /* Medaillon responsive : remplit la hauteur de sa ligne (minmax 0-120) en restant
     circulaire (aspect-ratio 1) -> se retrecit sans deborder quand les chips serrent. */
  grid-area: med; height: 100%; width: auto; aspect-ratio: 1 / 1; max-height: 116px;
  border-radius: 50%; align-self: center; justify-self: center;
  border: 2px solid var(--violet); background: radial-gradient(circle at 50% 38%,#f3ecfc,#e6d8f6); z-index: 2;
}
.skin-arcane .card-art .fullart { color: var(--violet); transform: scale(.7); }
.skin-arcane .card-art::before { content: ""; position: absolute; inset: 5px; border-radius: 50%; border: 1px dashed rgba(165,103,227,.55); z-index: 3; }
/* Titre + badge-ic en flux INLINE centre (comme le skin de base : le badge suit le
   titre) -> le badge reste colle a la fin du titre, meme quand il passe sur 2 lignes ;
   le filet ::after (display:block) passe dessous. */
.skin-arcane .card-top { grid-area: title; display: block; text-align: center; }
.skin-arcane .card-top h3 { display: inline; font-family: 'Baloo 2', sans-serif; font-size: 18px; font-weight: 800; line-height: 1.15; }
.skin-arcane .card-top .badge-ic { display: inline-flex; vertical-align: middle; margin: 0 0 2px 5px; }
.skin-arcane .card-seal { display: none; }
.skin-arcane .card-front .card-subtitle {
  grid-area: num; font-family: 'Baloo 2', sans-serif; font-weight: 800; font-size: 12px; color: var(--muted);
  letter-spacing: .22em; text-transform: uppercase;
}
/* Filet sous le titre : force un retour a la ligne (flex-basis:100%) puis peint une
   barre de 54px CENTREE via un background centre -> reste 54px sans casser le wrap. */
.skin-arcane .card-top::after { content: ""; display: block; width: 54px; height: 2px; margin: 7px auto 0; background: var(--violet); border-radius: 2px; }
.skin-arcane .card-content p { grid-area: desc; font-size: 11.6px; color: var(--muted); margin: 0; overflow: hidden; min-height: 0; }
.skin-arcane .card-content p::after { content: "« Que du signal »"; display: block; margin-top: 8px; font-size: 10px; letter-spacing: .14em; text-transform: uppercase; color: var(--violet); font-weight: 700; }
.skin-arcane .card-action { grid-area: foot; justify-self: stretch; display: flex; flex-direction: column; gap: 6px; align-items: center; }
/* animation « la boussole » */
@keyframes sk-ar-turn { to { transform: rotate(360deg); } }
.skin-arcane .card-front:has(.state.active) .card-art::before { animation: sk-ar-turn 26s linear infinite; }
.skin-arcane .card-front:has(.state.active) .card-art .fa-dot { animation: fa-blip 5.6s ease-in-out infinite; }
.skin-arcane .card-front:has(.state.active) .card-art .fa-c1,
.skin-arcane .card-front:has(.state.active) .card-art .fa-c2,
.skin-arcane .card-front:has(.state.active) .card-art .fa-c3,
.skin-arcane .card-front:has(.state.active) .card-art .fa-c4 { animation: none; } /* la boussole ne fait pas de radar */
/* nuit */
html[data-theme="dark"] .skin-arcane .card-front { background: #1c1730; color: #f0eaf8; }
html[data-theme="dark"] .skin-arcane .card-front .card-edge { border-color: var(--violet-l, #bd8ef0); }
html[data-theme="dark"] .skin-arcane .card-front .card-edge::after { border-color: rgba(189,142,240,.4); }
html[data-theme="dark"] .skin-arcane .card-art { border-color: var(--violet-l, #bd8ef0); background: radial-gradient(circle at 50% 38%,#332a52,#241c40); }
html[data-theme="dark"] .skin-arcane .card-art .fullart { color: var(--violet-l, #bd8ef0); }
html[data-theme="dark"] .skin-arcane .card-art::before { border-color: rgba(189,142,240,.55); }
html[data-theme="dark"] .skin-arcane .card-front .card-subtitle, html[data-theme="dark"] .skin-arcane .card-content p { color: #b9addb; }

/* --- L'Assemblee — anatomie « carte a collectionner » (d'apres reference/magic-card-css :
   bandeau de nom + ligne de type en plaques dorees biseautees, fenetre d'art a filet
   metallique, boite de texte claire a 2 paragraphes, encart chevauchant force/endurance,
   fine ligne d'info technique). AUCUN asset/police/symbole/texte du jeu d'origine : seules
   l'anatomie generique du genre et les techniques CSS (degrades, ombres decalees, formes)
   sont reprises, avec la palette et le vocabulaire de LaBonneAlerte. Bord de carte NOIR. --- */
.skin-assemblee .card-front {
  grid-template-areas: "name" "art" "type" "text" "info";
  /* L'ART est la rangee FLEXIBLE (minmax 64px..1fr) : il absorbe l'espace libre quand la
     boite de texte est courte (image plus grande) et cede quand elle s'allonge (multi-chips).
     La rangee "text" est en `auto` -> la boite epouse son contenu, plus d'espace mort. */
  grid-template-rows: auto minmax(64px, 1fr) auto auto auto; gap: 6px;
  padding: 22px 15px 13px; background: linear-gradient(170deg,#efe8d6,#ddd2bb); color: #2e2a24;
}
/* Cadre exterieur NOIR conserve + filet parchemin interne. */
.skin-assemblee .card-front .card-edge { inset: 0px; border: 12px solid #000; box-shadow: inset 0 0 0 1.5px #cfc09a, inset 0 0 0 3px rgba(0,0,0,.12); border-radius: 10px; }
.skin-assemblee .card-front .card-edge::after { display: none; }
/* Bandeau de nom : plaque doree biseautee (spec ref), titre a gauche, pastilles a l'extreme
   droite. border-radius:25px/50px -> forme de plaque ovale caracteristique. */
.skin-assemblee .card-top {
  grid-area: name; background: linear-gradient(25deg, rgb(177,162,110), rgb(238,221,169), rgb(165,151,102));
  border-radius: 25px/50px; padding: 3px 12px; padding-right: 104px; margin: 0 3px;
  display: flex; align-items: center; box-shadow: -1px 2px 0 rgba(0,0,0,.32), inset 0 1px 1px rgba(255,255,255,.5); z-index: 2;
}
.skin-assemblee .card-top h3 { font-family: 'Baloo 2', sans-serif; font-size: 13.5px; font-weight: 800; color: #2b2410; flex: 1 1 auto; text-shadow: 0 1px 0 rgba(255,255,255,.35); }
.skin-assemblee .card-seal { display: none; }
.skin-assemblee .card-top .badge-ic { display: none; } /* le badge migre sur la ligne de type */
/* Pastilles fonctionnelles a l'EXTREME DROITE du bandeau (position du « cout de mana ») :
   ♥ · partage · +deck · i · etat. Traitement « pion de jeu de plateau » : fond clair mat +
   ombre decalee noire SANS blur (effet relief), d'apres la spec ref (.mana-cost-two). */
.skin-assemblee .card-toprow { top: 24px; left: 18px; right: 24px; height: 18px; justify-content: flex-end; gap: 2px; pointer-events: none; }
.skin-assemblee .card-icons { margin-left: 0; gap: 2px; pointer-events: auto; }
.skin-assemblee .card-toprow .state { order: 5; pointer-events: auto; }
.skin-assemblee .card-toprow .card-icons .card-like,
.skin-assemblee .card-toprow .card-icons .card-share,
.skin-assemblee .card-toprow .card-icons .card-add-deck,
.skin-assemblee .card-toprow .card-icons .flip-btn,
.skin-assemblee .card-toprow .state {
  width: 17px; height: 17px; min-width: 17px; border-radius: 50%; padding: 0; flex: none; margin: 0;
  display: inline-flex; align-items: center; justify-content: center;
  background: #f8f8e2; /* violet clair mat = pastille d'action */
  box-shadow: -1.5px 2.5px 0 0 #000; /* jeton en relief (pas de blur) */
  color: #3a2c5c; -webkit-backdrop-filter: none; backdrop-filter: none;
}
.skin-assemblee .card-toprow .card-icons svg { width: 11px; height: 11px; }
.skin-assemblee .card-toprow .card-like .like-n { display: none; } /* juste le coeur */
/* Pastille d'ETAT : jeton gris neutre au repos (point de depart ref #d5d1d1), VERT si actif. */
.skin-assemblee .card-toprow .state { font-size: 0; background: #d5d1d1; }
.skin-assemblee .card-toprow .state .dot-idle, .skin-assemblee .card-toprow .state .dot-live { display: none; }
.skin-assemblee .card-toprow .state.active { background: var(--green); }
/* Fenetre d'art : bande franche, filet metallique (border-image blanc->noir, spec ref). */
.skin-assemblee .card-art { grid-area: art; margin: 0 3px; border: 3px solid; border-image-source: linear-gradient(85deg,#fff,#000); border-image-slice: 1; box-shadow: 0 0 0 2px #1c1e18; background: linear-gradient(165deg,#8e5fcb,#5b3a94); color: #efe6fb; z-index: 2; }
/* Ligne de type : meme plaque doree que le bandeau de nom ; type a gauche, BADGE d'edition
   (.card-edition, officiel/verifie/communaute) a l'EXTREME DROITE (symbole d'edition/rarete). */
.skin-assemblee .card-front .card-subtitle {
  grid-area: type; background: linear-gradient(25deg, rgb(177,162,110), rgb(238,221,169), rgb(165,151,102));
  border-radius: 25px/50px; margin: 0 3px; padding: 2px 11px;
  font-size: 10px; font-weight: 700; color: #2b2410; text-transform: none; letter-spacing: 0;
  display: flex; align-items: center; z-index: 2; box-shadow: -1px 2px 0 rgba(0,0,0,.28);
}
.skin-assemblee .card-edition { display: inline-flex; align-items: center; justify-content: center; margin-left: auto; width: 15px; height: 15px; flex: none; color: #2b2410; }
.skin-assemblee .card-edition svg { width: 13px; height: 13px; }
.skin-assemblee .card-edition.official, .skin-assemblee .card-edition.verified { color: #6b4c00; }
/* Boite de texte claire texturee (spec ref) : elle englobe DESCRIPTION + params/switch,
   d'un seul bloc (le decor de conteneur vit sur .card-textbox, plus sur le <p>). Colonne
   flex : la description (flexible) cede/clippe d'abord, la zone d'action (params/switch) est
   preservee (ordre de sacrifice). La citation d'ambiance ::after ferme la boite en bas.
   position:relative -> ancre l'encart chevauchant (.card-stat, enfant du <p>). */
.skin-assemblee .card-textbox {
  grid-area: text; position: relative; display: flex; flex-direction: column; gap: 5px;
  margin: 0 3px;   background: linear-gradient(0.25turn, #f7f7f0, #d5d2c7);
  border: 1px solid #b6a678; box-shadow: 0 0 0 2px #1c1e18; border-radius: 3px;
  padding: 7px 10px 9px; /*overflow: hidden;*/ min-height: 0; z-index: 2;
}
/* Le <p> ne porte plus que le texte (couleur/typo), sans fond ni bordure propres. */
.skin-assemblee .card-textbox p { flex: 0 1 auto; min-height: 0; overflow: hidden; margin: 0; font-size: 11px; color: #40382a; line-height: 1.4; }
.skin-assemblee .card-desc { -webkit-line-clamp: 3; }
.skin-assemblee .card-textbox::after { content: "« Le sol murmure a qui sait poser l'oreille. »"; flex: none; margin-top: 2px; padding-top: 6px; border-top: 1px solid rgba(150,120,80,.4); font-size: 10.3px; font-style: italic; color: #7a6f58; }
/* Ordre de sacrifice (decorum en premier) : sur les cartes parametrees (chips/ajouter), la
   citation d'ambiance cede la place pour que la zone fonctionnelle (params + switch) tienne
   entierement dans la boite au ratio 2/3. La description clippe ensuite si besoin. */
.skin-assemblee .card-front:has(.param-row) .card-textbox::after { display: none; }
/* Encart CHEVAUCHANT (force/endurance de la ref) : 2e affichage decoratif du compteur de
   likes, a cheval sur le coin bas-droit de la boite de texte, coins nets, cadre dore. */
.skin-assemblee .card-stat {
  display: block; position: absolute; right: -7px; bottom: -11px; z-index: 5;
  min-width: 26px; text-align: center; padding: 0 7px;
  font-family: 'Baloo 2', sans-serif; font-weight: 800; font-size: 12px; line-height: 18px; color: #2b2410;
  background: rgb(194,175,114); border: 3px solid rgb(210,193,140); border-radius: 2px;
  box-shadow: -2px 2px 4px rgba(0,0,0,.5), inset -1px 1px 2px rgba(0,0,0,.35), inset 1px -1px 2px rgba(255,255,255,.6);
}
.skin-assemblee .card-stat::before { content: "\2665\00a0"; font-size: 9px; }
/* Fine ligne d'info technique tout en bas (spec ref .frame-bottom-info). */
.skin-assemblee .card-fineprint { display: block; grid-area: info; margin: 0 6px; font-size: 8px; letter-spacing: .04em; color: rgba(60,50,30,.6); text-align: left; }
/* Chips/select : petite pastille de « cout » decorative accolee devant « + ajouter »
   (facon symbole de cout en debut de ligne de capacite activee). Purement decoratif. */
/* Chips + « + ajouter » packes sur les memes lignes (les chips remontent dans le flex de
   .param-row) -> economise des rangees dans la boite au ratio 2/3, le switch reste visible.
   N'affecte que la mise en page, pas l'apparence des chips. */
.skin-assemblee .param-chips { display: contents; }
/* Le « + » de « + ajouter » vient se loger DANS une pastille (jeton) : le cercle est
   positionne en absolu sous le premier glyphe, le « + » (texte du bouton) se peint par-dessus
   -> impression d'un « + » serti dans la pastille de cout. */
.skin-assemblee .param-add { position: relative; z-index: 0; padding-left: 13px; }
/* z-index:-1 -> la pastille se peint AU-DESSUS du fond du bouton mais SOUS le texte
   (le « + » reste visible, serti dans le jeton). */
.skin-assemblee .param-add::before {
  content: ""; position: absolute; left: 6px; top: 50%; transform: translateY(-50%);
  width: 15px; height: 15px; border-radius: 50%; background: #c9b8ec;
  box-shadow: -1px 1.5px 0 0 #000; z-index: -1; pointer-events: none;
}
/* .card-action : posee A L'INTERIEUR de la boite (plus de grid-area:foot propre). Apparence
   des chips/boutons/switch INCHANGEE, seul leur emplacement (dans la boite) change. */
.skin-assemblee .card-action { z-index: 2; display: flex; flex-direction: column; gap: 4px; align-items: flex-start; }
/* animation « l'invocation » : la pastille d'ETAT (verte) s'allume, puis le badge d'edition scintille. */
@keyframes sk-as-cost { 0%,100%{ box-shadow: -1.5px 2.5px 0 0 #000;} 5%{ box-shadow: -1.5px 2.5px 0 0 #000, 0 0 9px 2px rgba(22,163,74,.8);} 10%{ box-shadow: -1.5px 2.5px 0 0 #000;} }
@keyframes sk-as-sym { 0%,100%{ filter: none;} 5%{ filter: drop-shadow(0 0 5px rgba(165,103,227,.9));} 10%{ filter: none;} }
.skin-assemblee .card-front:has(.state.active) .card-toprow .state { animation: sk-as-cost 6s ease-in-out infinite .3s; }
.skin-assemblee .card-front:has(.state.active) .card-edition { animation: sk-as-sym 6s ease-in-out infinite 2.1s; }
/* nuit : le cadre et le fond suivent le theme ; les plaques dorees et la boite de texte
   restent claires (comme une vraie carte -> lisibilite conservee dans les deux themes). */
html[data-theme="dark"] .skin-assemblee .card-front { background: linear-gradient(170deg,#2a2438,#1b1626); color: #efe9f8; }
html[data-theme="dark"] .skin-assemblee .card-front .card-edge { border-color: #000; box-shadow: inset 0 0 0 1.5px #cfc09a, inset 0 0 0 3px rgba(0,0,0,.4); }
html[data-theme="dark"] .skin-assemblee .card-fineprint { color: rgba(240,234,220,.55); }
/* La boite englobe desormais les params/switch : en theme sombre elle passe en fond SOMBRE
   pour que les chips/select/switch (styles clair-sur-sombre du theme) restent lisibles
   (une boite parcheminee claire les ecraserait). Description/citation repassent en clair. */
html[data-theme="dark"] .skin-assemblee .card-textbox { background: linear-gradient(0.25turn,#2a2340,#211b33); border-color: #5e4f80; box-shadow: 0 0 0 2px #000; }
html[data-theme="dark"] .skin-assemblee .card-textbox p { color: #d5cbe8; }
html[data-theme="dark"] .skin-assemblee .card-textbox::after { color: #a89bc4; border-top-color: rgba(200,180,255,.22); }

/* --- Le Dresseur (v2 : jaune plein texture, plus de plaque de nom ni de PV) ---
   Refonte complete d'apres dresseur-v2-reference.html. Les valeurs de la reference
   (320px de large) sont transposees au gabarit du site (264px, ratio 2/3) par un
   facteur ~0.82 sur la geometrie ; la typo suit un facteur plus doux (~0.87) pour
   rester lisible dans une carte deux fois plus dense en texte. Toujours 100% CSS
   sur le DOM neutre : aucune branche de rendu, aucun element ajoute. */
.skin-dresseur .card-front {
  grid-template-columns: auto minmax(0, 1fr) auto;
  grid-template-areas:
    "state title icons"
    "art   art   art"
    "dept  dept  dept"
    "atk   atk   atk"
    "act   act   act";
  /* Fenetre d'art en POURCENTAGE de la hauteur de carte (36% ≈ 175/480 de la reference)
     plutot qu'en px : la geometrie reste fidele quelle que soit la largeur reelle
     (grille 230→300px, decks, apercu boutique) au meme ratio 2/3. */
  /* La rangee « Signal » HUGE son contenu (minmax(0,auto) : elle grandit avec l'espace
     disponible jusqu'a la hauteur du texte, et clippe/fond au-dela) ; tout le mou restant
     va a la zone d'action, ou le switch est ancre en bas. Resultat : chips collees sous la
     description (comme la reference) et switch en pied de carte, sans espace mort au milieu.
     Ordre de sacrifice : la zone d'action (auto/1fr, min auto) n'est jamais tronquee.
     Fenetre d'art : 36% de la hauteur de carte (≈ 175/480 de la reference) + 50px demandes
     en plus. Les rangees suivantes se decalent d'elles-memes, le switch reste ancre en bas. */
  grid-template-rows: auto minmax(0, calc(36% + 50px)) auto auto 1fr;
  gap: 5px 7px; align-items: center;
  padding: 9px 9px 10px;
  color: #241a05;
  /* fond : 5 radial-gradient tachetes superposes au degrade diagonal (ref .tc) */
  background:
    radial-gradient(ellipse 116px 82px at 15% 12%, rgba(255,255,255,.35), transparent 60%),
    radial-gradient(ellipse 132px 99px at 85% 30%, rgba(140,95,0,.22), transparent 60%),
    radial-gradient(ellipse 148px 116px at 25% 78%, rgba(140,95,0,.18), transparent 62%),
    radial-gradient(ellipse 124px 91px at 90% 88%, rgba(255,255,255,.22), transparent 60%),
    radial-gradient(ellipse 165px 132px at 60% 55%, rgba(255,240,180,.25), transparent 65%),
    linear-gradient(155deg,#ffd94d 0%,#ffc72c 45%,#f0af00 100%);
}
/* Le header n'est plus un overlay : .card-toprow devient transparent (display:contents)
   pour que ses deux enfants (etat, groupe d'icones) deviennent des items de la grille et
   encadrent le titre — pastille | titre | icones sur une seule ligne, sans toucher au DOM. */
.skin-dresseur .card-toprow { display: contents; position: static; }
/* Seule delimitation restante : filet gris fin en retrait (plus de bord noir epais). */
.skin-dresseur .card-front .card-edge {
  inset: 8px; border: 1px solid rgba(90,90,90,.55); background: none; box-shadow: none;
  border-radius: 10px; z-index: 6; pointer-events: none;
}
.skin-dresseur .card-front .card-edge::after { display: none; }
/* Pastille d'etat metallisee (meme degrade brosse que la ligne de departement), au-dessus
   du filet. Le libelle du site (« Rien à signaler » / « Active ») est remplace visuellement
   par « Inactif » / « Actif » (ref) — le texte reel reste dans le DOM pour l'a11y. */
.skin-dresseur .card-toprow .state {
  grid-area: state; position: relative; z-index: 7; justify-self: start;
  margin-left: -3px; padding: 3px 8px; border-radius: 999px; flex: none; gap: 0;
  font-size: 7.6px; font-weight: 800; font-style: italic; text-transform: uppercase;
  letter-spacing: .02em; white-space: nowrap; color: #3a3a3a;
  background: linear-gradient(115deg,#e8e8ea 0%,#c7c8cc 30%,#f4f4f6 50%,#b9bac0 72%,#e2e3e6 100%);
  box-shadow: inset 0 1px 1px rgba(255,255,255,.6), inset 0 -1px 1px rgba(0,0,0,.15);
  -webkit-backdrop-filter: none; backdrop-filter: none;
}
.skin-dresseur .card-toprow .state.idle, .skin-dresseur .card-toprow .state.active { font-size: 0; }
.skin-dresseur .card-toprow .state.idle::after { content: "Inactif"; }
.skin-dresseur .card-toprow .state.active::after { content: "Actif"; }
.skin-dresseur .card-toprow .state.idle::after, .skin-dresseur .card-toprow .state.active::after {
  font-size: 7.6px; letter-spacing: .02em;
}
.skin-dresseur .card-toprow .state .dot-idle, .skin-dresseur .card-toprow .state .dot-live { display: none; }
.skin-dresseur .card-toprow .state.active { color: #0f5c26; background: linear-gradient(115deg,#bfe8c9,#8fd7a3,#bfe8c9); }
/* Titre nu, pose directement sur le jaune (plus de plaque, plus de « PV »). */
.skin-dresseur .card-top { grid-area: title; display: flex; align-items: center; gap: 5px; min-width: 0; }
.skin-dresseur .card-top h3 {
  font-family: 'Baloo 2', sans-serif; font-size: 13.5px; font-weight: 800; line-height: 1.08;
  color: #241a05; min-width: 0;
  /* La ligne d'en-tete est etroite (pastille + 4 jetons) : un titre long se limite a
     2 lignes au lieu de repousser la fenetre d'art vers le bas. */
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden;
}
.skin-dresseur .card-top .badge { display: none; }
.skin-dresseur .card-top .badge-ic { flex: none; }
.skin-dresseur .card-top .badge-ic svg { width: 13px; height: 13px; }
.skin-dresseur .card-seal { display: none; }
/* Les 4 boutons fonctionnels en jetons ronds jaunes biseautes (triple ombre : interne
   claire haute, interne sombre basse, portee externe). */
.skin-dresseur .card-icons { grid-area: icons; margin-left: 0; gap: 2px; }
.skin-dresseur .card-toprow .card-icons .card-like,
.skin-dresseur .card-toprow .card-icons .card-share,
.skin-dresseur .card-toprow .card-icons .card-add-deck,
.skin-dresseur .card-toprow .card-icons .flip-btn {
  width: 21px; height: 21px; color: #5a4408;
  background: linear-gradient(160deg,#ffe27a,#f0b400);
  box-shadow: inset 0 1.5px 1.5px rgba(255,255,255,.75),
              inset 0 -2px 3px rgba(120,70,0,.5),
              0 1px 2px rgba(0,0,0,.3);
  -webkit-backdrop-filter: none; backdrop-filter: none;
}
.skin-dresseur .card-toprow .card-icons svg { width: 12px; height: 12px; }
/* Etat « liké » : coeur plein violet (le binding existant .card-like.liked est conserve,
   seule la couleur du jeton change ; .ic-like passe deja en fill:currentColor). */
.skin-dresseur .card-toprow .card-icons .card-add-deck { color: #5a4408; }
.skin-dresseur .card-toprow .card-icons .card-like.liked { color: var(--violet, #a567e3); }
/* Fenetre d'art agrandie, liseré creme. Le motif full-art est inchange. */
.skin-dresseur .card-art {
  grid-area: art; align-self: stretch; height: 100%;
  border: 3px solid #fffdf5; border-radius: 6px; box-shadow: 0 1px 3px rgba(0,0,0,.25);
  background: linear-gradient(180deg,#e2d0f7,#b795e0); color: #f3ecfc; z-index: 2;
}
/* Ligne de departement : languette metal brosse qui CHEVAUCHE le bas de la fenetre d'art. */
.skin-dresseur .card-front .card-subtitle {
  grid-area: dept; position: relative; z-index: 3;
  margin: -17px 12px 0; border-radius: 6px; padding: 3px 10px;
  font-size: 9px; font-weight: 700; font-style: italic; text-align: center; color: #3a3a3a;
  text-transform: none; letter-spacing: 0; display: block;
  background: linear-gradient(115deg,#e8e8ea 0%,#c7c8cc 30%,#f4f4f6 50%,#b9bac0 72%,#e2e3e6 100%);
  box-shadow: inset 0 1px 1px rgba(255,255,255,.6), inset 0 -1px 1px rgba(0,0,0,.15);
  overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
}
/* La description passe dans l'effet d'attaque (visuel). On la garde dans le DOM en
   sr-only pour l'accessibilite (l'effet d'attaque est aria-hidden = echo visuel). */
.skin-dresseur .card-content p {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
/* Rangee « Signal » : pips A GAUCHE du nom sur la meme ligne, degats a droite, effet
   en dessous sur toute la largeur. .card-atkbody devient transparent pour que ses deux
   enfants rejoignent la grille de la rangee (aucun element DOM ajoute). */
.skin-dresseur .card-attacks {
  display: block; grid-area: atk; align-self: start; overflow: hidden; min-height: 0; padding: 0 4px;
}
.skin-dresseur .card-atk {
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto;
  grid-template-areas: "en nom dmg" "eff eff eff";
  align-items: center; gap: 3px 7px; padding: 0; border: none;
}
.skin-dresseur .card-atkbody { display: contents; }
.skin-dresseur .card-en { grid-area: en; display: flex; gap: 3px; flex: none; }
/* Pips : disques translucides bombes portant une etoile pleine noire (image CSS, pas de
   SVG ajoute au DOM). Jamais verts hors etat actif. */
.skin-dresseur .card-pip {
  width: 14px; height: 14px; border-radius: 50%; flex: none;
  background: rgba(255,255,255,.6);
  box-shadow: inset 0 -2px 2px rgba(0,0,0,.18), 0 1px 1px rgba(255,255,255,.4);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%231a1408' d='M12 2l2.9 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l7.1-1.01z'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: center; background-size: 9px 9px;
}
.skin-dresseur .card-atkname { grid-area: nom; font-family: 'Baloo 2', sans-serif; font-weight: 800; font-size: 12.5px; color: #241a05; }
.skin-dresseur .card-dmg { grid-area: dmg; font-family: 'Baloo 2', sans-serif; font-weight: 800; font-size: 12.5px; color: #241a05; flex: none; margin: 0; }
/* Effet = vraie description de la source (aucun texte invente). ORDRE DE SACRIFICE : c'est
   la description qui cede, jamais la zone fonctionnelle. Le nombre de lignes est borne par
   -webkit-line-clamp (meme mecanisme de troncature que .card-desc ailleurs sur le site) et
   se resserre des que des chips occupent le bas de la carte. */
.skin-dresseur .card-atkeff {
  grid-area: eff; font-size:12px; color: #4a3c10; line-height: 1.4; margin-top: 1px; min-width: 0;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 6; overflow: hidden;
}
.skin-dresseur .card-front:has(.param-row) .card-atkeff { -webkit-line-clamp: 4; }
.skin-dresseur .card-front:has(.param-chip) .card-atkeff { -webkit-line-clamp: 3; }
/* 3 chips ou plus : la description se resserre encore d'un cran (la fenetre d'art, flexible,
   rend le reste de l'espace). */
.skin-dresseur .card-front:has(.param-chips .param-chip:nth-child(3)) .card-atkeff { -webkit-line-clamp: 2; }
/* Le pied « faiblesse / resistance / retraite » disparait : absent de la reference v2
   (l'espace revient a la fenetre d'art). Reste inerte dans le DOM. */
.skin-dresseur .card-foot { display: none; }
/* Zone d'action pleine largeur (chips + « + ajouter » + select + switch), meme padding
   lateral que la rangee d'attaque pour respirer par rapport aux bords. */
.skin-dresseur .card-action {
  grid-area: act; align-self: stretch; height: 100%;
  display: flex; flex-direction: column; gap: 5px; align-items: flex-start; padding: 0 4px 1px;
}
/* L'interrupteur (abonnement / pause) est ancre en BAS de la carte ; ce qui le precede
   (chips, « + ajouter », select) reste colle sous la description. Deux cas : carte simple
   (le switch ouvre la zone -> on le pousse) et carte paramétrée (la rangee de chips ouvre
   la zone -> c'est elle qui repousse tout le reste vers le bas). */
.skin-dresseur .card-action > .switch-row:first-child { margin-top: auto; }
.skin-dresseur .card-action > .param-row:first-child { margin-bottom: auto; }
/* Chips / « + ajouter » : pilules translucides posees directement sur le jaune. */
.skin-dresseur .param-chip {
  background: rgba(255,253,245,.85); color: #3a2e08; font-size: 10.5px; font-weight: 700;
  padding: 3px 6px 3px 9px; gap: 5px; box-shadow: 0 1px 2px rgba(0,0,0,.12);
}
.skin-dresseur .param-chip .pc-dot { width: 6px; height: 6px; }
.skin-dresseur .param-remove { color: #6b5a1c; font-size: 12px; }
.skin-dresseur .param-add, .skin-dresseur .task-config, .skin-dresseur .task-config-anon {
  background: rgba(255,253,245,.6); color: #5a4a12; font-style: italic;
  font-size: 10.5px; font-weight: 700; padding: 3px 9px; box-shadow: 0 1px 2px rgba(0,0,0,.1);
}
.skin-dresseur .param-select, .skin-dresseur .param-input, .skin-dresseur .dyn-search {
  font-size: 11.5px; padding: 6px 9px; background: rgba(255,253,245,.9); border-color: rgba(120,90,10,.3); color: #3a2e08;
}
.skin-dresseur .switch-row { gap: 8px; }
/* VOCABULAIRE DU SKIN : « Abonné » → « Attrapé », « Non abonné » / « S'abonner » →
   « En liberté », « En pause » → « Se repose ». Substitution 100% CSS pilotee par la classe
   .switch-label.on (que TOUS les repeints JS basculent deja : setLabel/toggleMute/
   setParamStatus/tuiles-deck) -> aucun handler touche, et ca vaut pour les cartes comme
   pour les tuiles-deck (la classe de skin vit sur #grid ET .deck-card). Le libelle reel
   reste dans le DOM (font-size:0) pour les lecteurs d'ecran, comme la pastille d'etat. */
.skin-dresseur .switch-label { font-size: 0; font-weight: 800; color: #4a3c10; }
.skin-dresseur .switch-label::after { font-size: 11.5px; }
.skin-dresseur .switch-label:not(.on)::after { content: "En liberté"; }
.skin-dresseur .switch-label.on::after { content: "Attrapé"; }
.skin-dresseur .param-mute-row .switch-label:not(.on)::after { content: "Se repose"; }
.skin-dresseur .switch-label.on { color: var(--green); }
/* :not(:checked) — sinon la piste repeinte ecraserait le vert de l'etat abonne. */
.skin-dresseur .switch input:not(:checked) ~ .track { background: rgba(255,255,255,.4); border-color: rgba(120,90,10,.25); }
.skin-dresseur .switch .thumb { background: #fffdf5; }
/* Etat actif : le point central + les cercles du motif full-art gardent le radar de base
   (.fa-dot / .fa-c1..4, deja pilote par .card-front:has(.state.active)) — aucune animation
   nouvelle. Seul s'y ajoute le premier pip, qui s'allume en vert (keyframes historique de
   ce skin, valeurs mises a jour pour le nouveau pip). */
@keyframes sk-dr-en {
  0%,100% { background-color: rgba(255,255,255,.6); box-shadow: inset 0 -2px 2px rgba(0,0,0,.18), 0 1px 1px rgba(255,255,255,.4); }
  5%      { background-color: var(--green); box-shadow: 0 0 7px rgba(22,163,74,.6); }
  11%     { background-color: rgba(255,255,255,.6); box-shadow: inset 0 -2px 2px rgba(0,0,0,.18), 0 1px 1px rgba(255,255,255,.4); }
}
.skin-dresseur .card-front:has(.state.active) .card-pip.p1 { animation: sk-dr-en 5.8s ease-in-out infinite .5s; }
.skin-dresseur .card-front:has(.state.active) .card-pip.p2 { animation: sk-dr-en 5.8s ease-in-out infinite 1.3s; }
.skin-dresseur .card-front:has(.state.active) .card-pip.p3 { animation: sk-dr-en 5.8s ease-in-out infinite 2.1s; }
/* nuit : meme texture tachetee, jaune assombri ; pastilles/chips en anthracite translucide. */
html[data-theme="dark"] .skin-dresseur .card-front {
  color: #f3e6b8;
  background:
    radial-gradient(ellipse 116px 82px at 15% 12%, rgba(255,255,255,.18), transparent 60%),
    radial-gradient(ellipse 132px 99px at 85% 30%, rgba(0,0,0,.30), transparent 60%),
    radial-gradient(ellipse 148px 116px at 25% 78%, rgba(0,0,0,.28), transparent 62%),
    radial-gradient(ellipse 124px 91px at 90% 88%, rgba(255,255,255,.12), transparent 60%),
    radial-gradient(ellipse 165px 132px at 60% 55%, rgba(255,225,140,.14), transparent 65%),
    linear-gradient(155deg,#a97c12 0%,#8a640d 45%,#6b4d09 100%);
}
html[data-theme="dark"] .skin-dresseur .card-front .card-edge { border-color: rgba(230,225,210,.35); }
html[data-theme="dark"] .skin-dresseur .card-top h3 { color: #f6ecc8; }
html[data-theme="dark"] .skin-dresseur .card-toprow .card-icons .card-like,
html[data-theme="dark"] .skin-dresseur .card-toprow .card-icons .card-share,
html[data-theme="dark"] .skin-dresseur .card-toprow .card-icons .card-add-deck,
html[data-theme="dark"] .skin-dresseur .card-toprow .card-icons .flip-btn {
  background: linear-gradient(160deg,#c99a1e,#8a640d); color: #f3e6b8;
  box-shadow: inset 0 1.5px 1.5px rgba(255,255,255,.35), inset 0 -2px 3px rgba(0,0,0,.55), 0 1px 2px rgba(0,0,0,.4);
}
html[data-theme="dark"] .skin-dresseur .card-toprow .card-icons .card-add-deck { color: #f3e6b8; }
html[data-theme="dark"] .skin-dresseur .card-toprow .card-icons .card-like.liked { color: var(--violet-l, #bd8ef0); }
html[data-theme="dark"] .skin-dresseur .card-toprow .state,
html[data-theme="dark"] .skin-dresseur .card-front .card-subtitle {
  color: #e7e7ea;
  background: linear-gradient(115deg,#4a4a50 0%,#2f2f34 30%,#5c5c62 50%,#26262a 72%,#45454b 100%);
  box-shadow: inset 0 1px 1px rgba(255,255,255,.15), inset 0 -1px 1px rgba(0,0,0,.35);
}
html[data-theme="dark"] .skin-dresseur .card-toprow .state.active { color: #eafff1; background: linear-gradient(115deg,#1c6b37,#2f8f4d,#1c6b37); }
html[data-theme="dark"] .skin-dresseur .card-art { border-color: #3a2e12; }
html[data-theme="dark"] .skin-dresseur .card-atkname, html[data-theme="dark"] .skin-dresseur .card-dmg { color: #f6ecc8; }
html[data-theme="dark"] .skin-dresseur .card-atkeff { color: #e2d3a0; }
/* Etoile creme sur pip sombre (la reference garde l'etoile noire en nuit : invisible sur
   fond anthracite — on l'eclaircit, seul ecart assume par rapport a la ref). */
html[data-theme="dark"] .skin-dresseur .card-pip {
  background-color: rgba(0,0,0,.28);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23f6ecc8' d='M12 2l2.9 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l7.1-1.01z'/%3E%3C/svg%3E");
}
html[data-theme="dark"] .skin-dresseur .param-chip { background: rgba(0,0,0,.28); color: #f3e6b8; }
html[data-theme="dark"] .skin-dresseur .param-remove { color: #e2d3a0; }
html[data-theme="dark"] .skin-dresseur .param-add,
html[data-theme="dark"] .skin-dresseur .task-config, html[data-theme="dark"] .skin-dresseur .task-config-anon { background: rgba(0,0,0,.18); color: #e2d3a0; }
html[data-theme="dark"] .skin-dresseur .param-select, html[data-theme="dark"] .skin-dresseur .param-input,
html[data-theme="dark"] .skin-dresseur .dyn-search { background: rgba(0,0,0,.3); border-color: rgba(255,255,255,.2); color: #f3e6b8; }
html[data-theme="dark"] .skin-dresseur .switch-label { color: #e2d3a0; }
html[data-theme="dark"] .skin-dresseur .switch input:not(:checked) ~ .track { background: rgba(0,0,0,.35); border-color: rgba(255,255,255,.18); }
html[data-theme="dark"] .skin-dresseur .switch .thumb { background: #f6ecc8; }
html[data-theme="dark"] .skin-dresseur .switch-row::before { color: #a89bc4; }
@keyframes sk-dr-en-dk {
  0%,100% { background-color: rgba(0,0,0,.28); box-shadow: inset 0 -2px 2px rgba(0,0,0,.18), 0 1px 1px rgba(255,255,255,.2); }
  5%      { background-color: var(--green); box-shadow: 0 0 7px rgba(22,163,74,.6); }
  11%     { background-color: rgba(0,0,0,.28); box-shadow: inset 0 -2px 2px rgba(0,0,0,.18), 0 1px 1px rgba(255,255,255,.2); }
}
html[data-theme="dark"] .skin-dresseur .card-front:has(.state.active) .card-pip.p1 { animation-name: sk-dr-en-dk; }
html[data-theme="dark"] .skin-dresseur .card-front:has(.state.active) .card-pip.p2 { animation-name: sk-dr-en-dk; }
html[data-theme="dark"] .skin-dresseur .card-front:has(.state.active) .card-pip.p3 { animation-name: sk-dr-en-dk; }

/* --- FACES ARRIERE accordees au skin, cartes ET decks (la classe skin vit sur #grid/
   .deck-card, donc .skin-x .card-back couvre les deux contextes). On ne retouche que la
   surface + le cadre (box-shadow inset) + la couleur de texte : le contenu reste identique.

   LES QUATRE FACES, PAS SEULEMENT LE « i ». Une carte a jusqu'a 4 faces arriere — verso
   d'info (.card-back, bouton « i »), partage (.card-share-face), ajout a un deck
   (.card-deck-face) et gestion des taches des cartes v3 (.card-task-face, « configurer »).
   Elles partagent toutes le meme habillage neutre (bloc « Versos » plus haut : --tc-grad
   + double lisere) ; un skin qui ne repeignait que .card-back laissait donc les 3 autres
   en tenue generique. Chaque skin liste desormais les 4 faces. --- */
.skin-ecole-classique .card-back, .skin-ecole-classique .card-share-face,
.skin-ecole-classique .card-deck-face, .skin-ecole-classique .card-task-face { background: linear-gradient(160deg,#efe4fb,#e2d2f5); color: var(--ink); box-shadow: inset 0 0 0 2.5px #b493dd, inset 0 0 0 4px #fff; }
.skin-arcane .card-back, .skin-arcane .card-share-face,
.skin-arcane .card-deck-face, .skin-arcane .card-task-face { background: #fbf7ef; color: var(--ink); box-shadow: inset 0 0 0 1.5px var(--violet); }
.skin-assemblee .card-back, .skin-assemblee .card-share-face,
.skin-assemblee .card-deck-face, .skin-assemblee .card-task-face { background: linear-gradient(170deg,#efe8d6,#ddd2bb); color: #2e2a24; box-shadow: inset 0 0 0 2px #2c2822, inset 0 0 0 3.5px #cfc09a; }
/* Dresseur : les faces arriere reprennent le jaune TEXTURE du recto v2 (le jaune plat de
   l'ancienne version detonnait a cote du nouveau recto) et son unique filet gris fin. */
.skin-dresseur .card-back, .skin-dresseur .card-share-face,
.skin-dresseur .card-deck-face, .skin-dresseur .card-task-face {
  color: #241a05; box-shadow: none; border-color: transparent;
  background:
    radial-gradient(ellipse 116px 82px at 15% 12%, rgba(255,255,255,.35), transparent 60%),
    radial-gradient(ellipse 132px 99px at 85% 30%, rgba(140,95,0,.22), transparent 60%),
    radial-gradient(ellipse 148px 116px at 25% 78%, rgba(140,95,0,.18), transparent 62%),
    radial-gradient(ellipse 124px 91px at 90% 88%, rgba(255,255,255,.22), transparent 60%),
    radial-gradient(ellipse 165px 132px at 60% 55%, rgba(255,240,180,.25), transparent 65%),
    linear-gradient(155deg,#ffd94d 0%,#ffc72c 45%,#f0af00 100%);
}
/* Meme filet que le recto (1px gris, inset 8px) : on recycle le ::after des versos. */
.skin-dresseur .card-back::after, .skin-dresseur .card-share-face::after,
.skin-dresseur .card-deck-face::after, .skin-dresseur .card-task-face::after {
  inset: 8px; border-radius: 10px; border-color: rgba(90,90,90,.55);
}
/* Titres des 4 faces : la regle neutre les force a var(--ink) — on rend la main au skin. */
.skin-assemblee .card-back h3, .skin-dresseur .card-back h3,
.skin-assemblee .card-share-face .share-face-title, .skin-dresseur .card-share-face .share-face-title,
.skin-assemblee .card-deck-face .cdf-title, .skin-dresseur .card-deck-face .cdf-title,
.skin-assemblee .card-task-face .tf-title, .skin-dresseur .card-task-face .tf-title { color: inherit; }
/* nuit : les faces arriere suivent les variantes sombres de chaque skin (pas de tache claire). */
html[data-theme="dark"] .skin-ecole-classique .card-back, html[data-theme="dark"] .skin-ecole-classique .card-share-face,
html[data-theme="dark"] .skin-ecole-classique .card-deck-face, html[data-theme="dark"] .skin-ecole-classique .card-task-face { background: linear-gradient(160deg,#2c2148,#1d1633); color: #f0eaf8; box-shadow: inset 0 0 0 2.5px #6d51a0; }
html[data-theme="dark"] .skin-arcane .card-back, html[data-theme="dark"] .skin-arcane .card-share-face,
html[data-theme="dark"] .skin-arcane .card-deck-face, html[data-theme="dark"] .skin-arcane .card-task-face { background: #1c1730; color: #f0eaf8; box-shadow: inset 0 0 0 1.5px var(--violet-l,#bd8ef0); }
html[data-theme="dark"] .skin-assemblee .card-back, html[data-theme="dark"] .skin-assemblee .card-share-face,
html[data-theme="dark"] .skin-assemblee .card-deck-face, html[data-theme="dark"] .skin-assemblee .card-task-face { background: linear-gradient(170deg,#2a2438,#1b1626); color: #efe9f8; box-shadow: inset 0 0 0 2px #54476e; }
html[data-theme="dark"] .skin-dresseur .card-back, html[data-theme="dark"] .skin-dresseur .card-share-face,
html[data-theme="dark"] .skin-dresseur .card-deck-face, html[data-theme="dark"] .skin-dresseur .card-task-face {
  color: #f3e6b8; box-shadow: none; border-color: transparent;
  background:
    radial-gradient(ellipse 116px 82px at 15% 12%, rgba(255,255,255,.18), transparent 60%),
    radial-gradient(ellipse 132px 99px at 85% 30%, rgba(0,0,0,.30), transparent 60%),
    radial-gradient(ellipse 148px 116px at 25% 78%, rgba(0,0,0,.28), transparent 62%),
    radial-gradient(ellipse 124px 91px at 90% 88%, rgba(255,255,255,.12), transparent 60%),
    radial-gradient(ellipse 165px 132px at 60% 55%, rgba(255,225,140,.14), transparent 65%),
    linear-gradient(155deg,#a97c12 0%,#8a640d 45%,#6b4d09 100%);
}
html[data-theme="dark"] .skin-dresseur .card-back::after, html[data-theme="dark"] .skin-dresseur .card-share-face::after,
html[data-theme="dark"] .skin-dresseur .card-deck-face::after, html[data-theme="dark"] .skin-dresseur .card-task-face::after {
  border-color: rgba(230,225,210,.35);
}
/* Contenu des faces arriere du Dresseur : les libelles neutres (--muted, gris-violet) sont
   calibres pour les surfaces du theme, pas pour le jaune/brun de ce skin -> ils suivent la
   couleur de la face (contraste correct dans les deux themes). Les liens/pastilles gardent
   leur accent (repere d'action), seul le texte de service est repris. */
.skin-dresseur .card-share-face .share-lbl, .skin-dresseur .card-share-face .share-ic,
.skin-dresseur .card-deck-face .dam-item, .skin-dresseur .card-long-desc { color: inherit; }

/* ============================================================================
   EFFET HOLOGRAPHIQUE « Dresseur » — TECHNIQUE inspiree de reference/pokemon-cards-css
   (couches de degrades + mix-blend-mode pilotees par la position du curseur, cf. leur
   .card__shine/.card__glare). Re-implemente en vanilla (aucun asset/JS de leur repo repris).
   SCOPE STRICT : #grid.skin-dresseur .card-art uniquement -> jamais les decks (.deck-card),
   la boutique (.shop-item), le full-art, les 5 teintes, ni les 3 autres skins.
   Reflet nacre MOBILE au survol (desktop, pilote par --mx/--my/--holo depuis holo.js, UN
   seul listener delegue), leger sheen STATIQUE au repos et sur mobile/tactile. Palette
   jaune/violet/blanc (aucun vert : reserve a l'etat actif). ------------------------------- */
#grid.skin-dresseur .card-front { --mx: 50%; --my: 50%; --holo: 0; }
#grid.skin-dresseur .card-art::before,
#grid.skin-dresseur .card-art::after {
  content: ""; position: absolute; inset: 0; pointer-events: none; border-radius: inherit; z-index: 4;
}
/* Reflet diagonal nacre (bandes jaune/violet/blanc) : leger au repos (.1), renforce au survol.
   La position du degrade suit le curseur (--mx/--my) -> le reflet « bouge ». */
#grid.skin-dresseur .card-art::after {
  z-index: 5;
  background: repeating-linear-gradient(110deg,
    rgba(255,255,255,0) 0%, rgba(255,241,186,.55) 7%, rgba(201,151,236,.42) 13%,
    rgba(255,255,255,.6) 19%, rgba(255,241,186,0) 27%);
  background-size: 220% 220%;
  background-position: var(--mx) var(--my);
  mix-blend-mode: color-dodge;
  filter: brightness(.92) contrast(1.5) saturate(.85);
  opacity: calc(.1 + var(--holo) * .5);
  transition: opacity .3s ease;
}
/* Glare radial ponctuel qui suit le curseur : invisible au repos (--holo:0), apparait au survol. */
#grid.skin-dresseur .card-art::before {
  background: radial-gradient(circle at var(--mx) var(--my),
    rgba(255,255,255,.5), rgba(255,255,255,.12) 38%, rgba(0,0,0,.22) 78%);
  mix-blend-mode: overlay;
  opacity: var(--holo);
  transition: opacity .3s ease;
}

/* prefers-reduced-motion : neutralise toutes les animations des skins structures */
@media (prefers-reduced-motion: reduce) {
  .skin-ecole-classique .card-seal, .skin-ecole-classique .card-art::after,
  .skin-arcane .card-art::before, .skin-arcane .card-art .fa-dot,
  .skin-assemblee .card-toprow .state, .skin-assemblee .card-edition,
  .skin-dresseur .card-pip { animation: none !important; }
  /* Holo Dresseur fige au sheen statique (le glare mobile est desactive, holo.js ne tourne pas). */
  #grid.skin-dresseur .card-art::before { opacity: 0 !important; }
  #grid.skin-dresseur .card-art::after { transition: none !important; }
}

/* Selecteur « Skin » dans la gestion d'un deck (/mes-decks). */
.deck-skin-row { margin-top: 10px; }
.deck-skin-ctrl { display: inline-flex; align-items: center; gap: 8px; font-size: 14px; color: var(--muted); }
.deck-skin-ctrl select {
  font: inherit; font-size: 14px; color: var(--ink); background: var(--bg);
  border: 1.5px solid var(--line); border-radius: 10px; padding: 7px 10px;
}
.deck-skin-ctrl select:focus { outline: none; border-color: var(--amber); }
.deck-skin-link { font-size: 14px; color: var(--muted); text-decoration: underline; }

/* ================================================================
   VUE LISTE DENSE (list-view.js) — alternative aux cartes.
   Bascule pilotée par <html data-view="cards|list"> (view-mode.js).
   Aucune règle ne touche la vue cartes : #grid n'est masqué QUE en data-view=list.
   ================================================================ */

/* (Le bouton .view-toggle de la toolbar a été supprimé — le mode d'affichage se règle
   depuis « Mon compte › Apparence ». Ses styles sont retirés avec lui.) */

/* Bascule d'affichage : une seule vue visible à la fois. */
.list { display: none; }
:root[data-view="list"] #grid { display: none; }
:root[data-view="list"] .list { display: block; }

/* Transition de vue (approche c) — crossfade + slide directionnel, joué UNIQUEMENT au
   clic (classes .view-leaving/.view-entering posées par view-mode.js). Séquentiel :
   la vue sortante glisse vers le HAUT en disparaissant, puis (après swap) la nouvelle
   apparaît depuis le BAS. Un seul #grid/.list en flux à la fois → pas de saut de hauteur
   nouveau. Durée/easing alignés sur l'entrée des cartes/lignes (.2–.25s ease). */
:root.view-leaving[data-view="cards"] #grid,
:root.view-leaving[data-view="list"] .list {
  opacity: 0; transform: translateY(-14px);
  transition: opacity .15s ease, transform .15s ease;
}
:root.view-entering[data-view="cards"] #grid,
:root.view-entering[data-view="list"] .list {
  animation: view-enter .22s ease;
}
@keyframes view-enter { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }
/* Fallback instantané : aucune animation si l'utilisateur réduit les animations. */
@media (prefers-reduced-motion: reduce) {
  :root.view-leaving[data-view="cards"] #grid,
  :root.view-leaving[data-view="list"] .list,
  :root.view-entering[data-view="cards"] #grid,
  :root.view-entering[data-view="list"] .list { transition: none; animation: none; opacity: 1; transform: none; }
}

/* Conteneur liste : pleine largeur du wrap, lignes séparées par un filet. */
.list {
  width: 100%; margin: 2rem auto 26px; border: 1px solid var(--line);
  border-radius: 14px; overflow: hidden; background: var(--bg);
}
.lrow { border-bottom: 1px solid var(--line); }
.lrow:last-child { border-bottom: 0; }
.lrow[hidden] { display: none; }

/* Ligne : lecture gauche→droite (titre/sous-titre), actions à droite. */
.lrow-head { display: flex; align-items: center; gap: 12px; padding: 11px 16px; border-bottom: 1px solid var(--dot);}
.lrow-main {
  flex: 1 1 auto; min-width: 0; display: flex; align-items: baseline; gap: 10px;
  background: none; border: 0; padding: 0; cursor: pointer; text-align: left; color: inherit; font: inherit;
}
.lrow-title { font-weight: 600; color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.lrow-sub { font-size: .84em; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; flex: 1 1 auto; min-width: 0; }
.lrow-cat {
  flex: 0 0 auto; font: inherit; font-size: .78em; color: var(--muted);
  background: none; border: 1px solid var(--line); border-radius: 999px;
  padding: 2px 9px; white-space: nowrap; cursor: pointer; transition: color .15s, border-color .15s;
}
.lrow-cat:hover { color: var(--amber); border-color: var(--amber); }
.lrow-actions { flex: 0 0 auto; display: flex; align-items: center; gap: 10px; }

/* Icône d'état (seule, sans texte). */
.lrow-state { display: inline-flex; }
.lrow-state .dot { width: 9px; height: 9px; border-radius: 50%; background: var(--muted); opacity: .45; }
.lrow-state.active .dot { background: var(--green); opacity: 1; box-shadow: 0 0 0 3px color-mix(in srgb, var(--green) 25%, transparent); }

/* Boutons d'action (cœur / partage / i) : MÊMES classes et SVG que les cartes
   (like-btn/card-like, card-share, flip-btn). En contexte liste (flux, pas de coin
   de carte), on neutralise le positionnement absolu hérité de leurs règles de base. */
.lrow-actions .like-btn, .lrow-actions .card-share, .lrow-actions .flip-btn {
  position: static; top: auto; right: auto; z-index: auto;
  background: none; border: 0; cursor: pointer; color: var(--muted);
  padding: 4px; border-radius: 8px; line-height: 0; transition: color .15s;
}
.lrow-actions .like-btn:hover, .lrow-actions .card-share:hover, .lrow-actions .flip-btn:hover { color: var(--ink); }
.lrow-actions .card-like.liked { color: #ef5a78; }
.lrow-actions svg { width: 16px; height: 16px; display: block; }
.lrow-manage, .lrow-link {
  font-size: .82em; color: var(--amber); background: none; border: 1px solid var(--line);
  border-radius: 8px; padding: 4px 10px; cursor: pointer; text-decoration: none; white-space: nowrap;
}
.lrow-manage:hover, .lrow-link:hover { border-color: var(--amber); }

/* Accordéon (une seule ligne ouverte). */
.lrow-exp { padding: 16px 16px 16px 16px; font-size: .92em; color: var(--ink); }
.lrow-exp[hidden] { display: none; }
.lrow-desc { margin: 0; color: var(--muted); }
.lrow-loading, .lrow-muted { color: var(--muted); font-size: .9em; }
.lrow-status-uptime { display: flex; gap: 2px; flex-wrap: wrap; margin-bottom: 10px; }
.lrow-status .uptime-bar { width: 6px; height: 22px; border-radius: 2px; background: var(--line); }
.lrow-status .uptime-bar.u-active { background: var(--green); }
.lrow-status .uptime-bar.u-failed { background: var(--amber); }
.lrow-status .uptime-bar.u-nodata { background: var(--line); opacity: .5; }
.lrow-status-more { display: inline-block; margin-top: 8px; font-size: .85em; color: var(--amber); }
.lrow-anon { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
.lrow-anon input { flex: 1 1 200px; padding: 8px 10px; border: 1px solid var(--line); border-radius: 8px; background: var(--bg); color: var(--ink); }
.lrow-anon button { padding: 8px 14px; border: 0; border-radius: 8px; background: var(--amber); color: #fff; cursor: pointer; }
.lrow-anon-note { font-size: .85em; color: var(--muted); }

/* (d) Sous-titre déplacé : en tête du volet déplié, avant tout autre contenu. */
.lrow-exp-sub { color: var(--muted); font-size: .9em; margin: 0 0 8px; }
/* (e) Description du statut (« i »), au-dessus des barres d'uptime. */
.lrow-status-desc { margin: 0 0 10px; color: var(--muted); }

/* (f/g) Carte réelle déplacée dans le volet (B1) : rendu compact et sobre — on ne
   garde que la zone d'abonnement/paramètres, sans le décor de la carte pleine. */
.lrow-exp .card.in-list-exp { margin: 0; width: 100%; }
.lrow-exp .card.in-list-exp .card-inner { transform: none; transition: none; min-height: 0; }
.lrow-exp .card.in-list-exp .card-face { box-shadow: none; border-color: transparent; background: none; padding: 0; gap: 10px; }
.lrow-exp .card.in-list-exp .card-back,
.lrow-exp .card.in-list-exp .card-share-face,
.lrow-exp .card.in-list-exp .card-deck-face,
.lrow-exp .card.in-list-exp .card-task-face { display: none; }
/* EXCEPTION user-task : la 5e face porte TOUTE la gestion des tâches (liste,
   « C'est fait ✓ », croix, « + une autre tâche »). La masquer laissait la vue liste
   sans aucun chemin vers les échéances (dette n°1 du fil V3). Comme .card-inner est
   figé (transform:none) dans le volet, le flip ne tourne pas : on rejoue la bascule
   en display, pilotée par la MÊME classe .face-task que la vue cartes — donc par le
   même handler .task-config / .flip-back de site.js, sans une ligne de JS en plus.
   Ciblé sur .card-usertask-face : la face des cartes 'community' reste masquée. */
.lrow-exp .card.in-list-exp.face-task .card-usertask-face {
  display: flex; position: relative; inset: auto; transform: none; overflow: visible;
  background: none; /* le volet fournit déjà son fond */
}
.lrow-exp .card.in-list-exp.face-task .card-front { display: none; }
/* Pas de flèche « Retour » dans le volet : le recto d'une carte user-task ne porte que
   « + configurer » (dont le seul rôle est d'ouvrir cette face) et l'interrupteur de
   pause, déjà représenté par le switch de la ligne — y revenir n'offre rien. On ferme
   la ligne pour sortir, et unparkCard() rend la carte à la grille sur son recto. */
.lrow-exp .card.in-list-exp .card-usertask-face .flip-back { display: none; }
/* Le titre « Mes échéances » fait doublon avec le nom de la source, déjà en tête de ligne. */
.lrow-exp .card.in-list-exp .card-usertask-face .tf-title { display: none; }
/* EXCEPTION 'community' (ex. chat-perdu) : même mécanique que ci-dessus pour
   .card-usertask-face — la 5e face partagée .card-task-face porte ici la liste des
   signalements (« Je l'ai vu »/« Je l'ai retrouvé »/formulaire de création), sans
   laquelle la vue liste n'aurait aucun chemin vers ces actions. */
.lrow-exp .card.in-list-exp.face-task .card-community-face {
  display: flex; position: relative; inset: auto; transform: none; overflow: visible;
  background: none; /* le volet fournit déjà son fond */
}
.lrow-exp .card.in-list-exp .card-community-face .flip-back { display: none; }
/* « Signalements en cours » fait doublon avec le nom de la source, déjà en tête de ligne. */
.lrow-exp .card.in-list-exp .card-community-face .tf-title { display: none; }
.lrow-exp .card.in-list-exp .fullart,
.lrow-exp .card.in-list-exp .card-veil,
.lrow-exp .card.in-list-exp .card-edge,
.lrow-exp .card.in-list-exp .card-toprow,
.lrow-exp .card.in-list-exp .card-content > .card-top,
.lrow-exp .card.in-list-exp .card-content > .card-subtitle,
.lrow-exp .card.in-list-exp .card-content p,
.lrow-exp .card.in-list-exp .card-content .sub-count { display: none; }
.lrow-exp .card.in-list-exp .card-content { padding: 0; }
/* Abonné : l'interrupteur pause/reprise du volet est représenté par le switch de la
   ligne fermée → on le masque ici pour éviter le doublon. */
.lrow-exp .card.in-list-exp .param-mute-row { display: none; }
/* Non abonné : le switch « S'abonner » du volet est masqué pour TOUTES les familles
   (géo comme non-géo). Depuis que le choix actif d'une valeur abonne directement
   (change/input/dyn-enum câblés sur followParamConnected, sans blocage géo), plus aucune
   source n'a besoin de ce switch pour souscrire : il ferait doublon avec le switch de la
   ligne fermée (seul contrôle visible en liste, reflète l'état via sync()). */
.lrow-exp .card.in-list-exp .param-follow-row { display: none; }

/* (h) Apparition d'une ligne (filtrage catégorie, adoption, dismiss reco) : même
   durée/easing que l'entrée des cartes (.25s ease). Le réordonnancement complet type
   FLIP est laissé hors périmètre (voir rapport) pour rester simple et robuste. */
@keyframes lrow-in { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }
.lrow.lrow-in { animation: lrow-in .25s ease; }
@media (prefers-reduced-motion: reduce) { .lrow.lrow-in { animation: none; } }

/* Surbrillance brève d'une carte ciblée depuis « Gérer » (vue cartes). */
.card-flash { outline: 3px solid var(--amber); outline-offset: 3px; transition: outline-color .4s; }

/* Catégorie masquée sur écrans étroits (avant le breakpoint mobile). */
@media (max-width: 900px) { .lrow-cat { display: none; } }

/* Mobile : la ligne s'étale (wrap), un bloc = une alerte, pas de colonnes fixes. */
@media (max-width: 620px) {
  .list { margin-top: 12px; }
  .lrow-head { flex-wrap: wrap; gap: 8px 12px; }
  .lrow-main { flex: 1 1 100%; }
  .lrow-actions { flex: 1 1 100%; justify-content: flex-end; }
}

/* ================================================================== */
/* FORUM — habillage des pages /forum (server-rendu par                 */
/* server/routes/forum.js). Réutilise STRICTEMENT les tokens du site    */
/* (var(--amber), --surface, --muted, --shadow…) et la classe .card :   */
/* aucune couleur/police codée en dur, thèmes clair/sombre automatiques,*/
/* body.high-contrast et prefers-reduced-motion hérités du site.        */
/* ================================================================== */
.forum-main { max-width: 820px; margin: 0 auto; padding: 28px 24px 80px; width: 100%; }
@media (max-width: 767px) { .forum-main { padding: 20px 16px 64px; } }

.forum-breadcrumb { font-size: 13px; color: var(--muted); margin-bottom: 14px; }
/* F) Fil d'Ariane : il reste, lui, sur le dégradé violet (hors plaque) où le gris
   --muted tombait à 2.04:1 et l'accent violet à ~1.3:1. L'encre du thème y mesure
   6.46:1. Le lien garde son soulignement au survol pour rester identifiable. */
.forum-breadcrumb, .forum-breadcrumb a { color: var(--ink); }
.forum-breadcrumb a { text-decoration: none; font-weight: 600; }
.forum-breadcrumb a:hover { text-decoration: underline; }
.forum-breadcrumb .bc-sep { color: var(--muted); margin: 0 2px; }

/* F) LISIBILITE DE L'EN-TETE DU FORUM — mesure a l'appui (echantillonnage des pixels du
   rendu, pas a l'oeil) : le degrade violet de fond passe de #c384dd a #9d5bd6 derriere ce
   bloc, ou le mot « forum » (--amber) tombait a 1.22:1 et le sous-titre (--muted) a 1.36:1.
   Aucune couleur de texte ne peut garantir 4.5:1 sur un degrade qui va du rose pale au
   violet moyen ET qui se deplace avec la largeur du viewport : on pose donc l'en-tete sur
   la MEME surface que les cartes du forum juste en dessous (--surface, opaque). Mesures
   obtenues : titre 16.18:1, violet du titre 5.5:1, sous-titre 5.11:1 (clair) ; 10.88:1,
   4.93:1 et 4.89:1 (sombre). */
.forum-head, .forum-intro { background: var(--surface); }
.forum-head { padding: 16px 20px 10px; border-radius: 18px 18px 0 0; margin-bottom: 0; }
.forum-intro { padding: 0 20px 16px; border-radius: 0 0 18px 18px; }
/* En-tete SANS sous-titre (categorie, sujet, profil) : le bloc se referme tout seul. */
.forum-head:not(:has(+ .forum-intro)) { border-radius: 18px; padding-bottom: 16px; }
.forum-head { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
.forum-title { font-family: 'Baloo 2', sans-serif; font-weight: 800; letter-spacing: -0.02em;
  font-size: clamp(26px, 4.4vw, 40px); line-height: 1.1; margin: 0; }
/* F) violet de TEXTE (5.5:1 sur la surface) et non l'accent d'aplat (2.9:1). */
.forum-title .hl { color: var(--amber-ink); }
.forum-title-topic { font-size: clamp(22px, 3.6vw, 30px); }
.forum-intro { color: var(--muted); font-size: 15px; margin: 0 0 22px; }
.forum-h2 { font-family: 'Baloo 2', sans-serif; font-weight: 800; font-size: 20px; margin: 30px 0 14px; }

/* CTA « Créer un sujet » : réutilise .empty-btn (pilule violette du site) en inline-flex. */
.forum-create-btn { display: inline-flex; align-items: center; gap: 6px; text-decoration: none; }

/* Cartes de catégorie (accueil) — objets .card cliquables. */
.forum-cats-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; margin-bottom: 8px; }
@media (max-width: 620px) { .forum-cats-grid { grid-template-columns: 1fr; } }
.forum-cat-card { text-decoration: none; color: inherit; flex-direction: row; align-items: center; gap: 14px; padding: 18px; }
.fcc-emoji { font-size: 26px; line-height: 1; flex: none; }
.fcc-body { flex: 1 1 auto; min-width: 0; }
.fcc-name { font-family: 'Baloo 2', sans-serif; font-weight: 800; font-size: 17px; }
.fcc-desc { color: var(--muted); font-size: 13px; margin-top: 2px; }
.fcc-count { flex: none; font-size: 12px; font-weight: 700; color: var(--amber-ink); }

/* Liste + carte de sujet. */
.forum-list { display: flex; flex-direction: column; gap: 12px; }
.topic-card { gap: 8px; position: relative; }
.topic-card-title { font-weight: 700; font-size: 16px; color: var(--ink); text-decoration: none; line-height: 1.35; }
/* Zone cliquable étendue à toute la carte SANS ancre imbriquée (le badge @slug,
   lien frère, passe au-dessus via z-index). */
.topic-card-title::after { content: ''; position: absolute; inset: 0; border-radius: 24px; }
.topic-card:hover .topic-card-title { color: var(--amber); }
.topic-card-meta { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; font-size: 12.5px; color: var(--muted); }
.tc-author { font-weight: 600; color: var(--ink); }
.tc-dot { color: var(--muted); }
.tc-replies { display: inline-flex; align-items: center; gap: 4px; margin-left: auto; }
.tc-replies svg { opacity: .8; }

/* Badge catégorie (réutilise l'esprit des .tag du site) + badge @forum_slug violet. */
/* F) pastilles de categorie : 2.87:1 avant (accent sur --amber-soft), 4.86:1 desormais. */
.forum-tag { display: inline-block; background: var(--amber-soft); color: var(--amber-ink);
  border-radius: 999px; padding: 2px 10px; font-size: 12px; font-weight: 700; text-decoration: none; }
a.forum-tag:hover { text-decoration: underline; }
.forum-slug-badge { position: relative; z-index: 1; display: inline-block; background: var(--amber);
  color: #fff; border-radius: 999px; padding: 2px 10px; font-size: 12px; font-weight: 700; text-decoration: none; }
.forum-slug-badge:hover { background: color-mix(in srgb, var(--amber) 88%, #000); }
.forum-lock { display: inline-flex; align-items: center; gap: 3px; color: var(--muted); }

/* Page sujet : carte du 1er message (OP) distinguée, réponses empilées. */
.topic-op-card { gap: 10px; margin-bottom: 18px; }
.forum-posts { display: flex; flex-direction: column; gap: 12px; }
.post-card { gap: 10px; }
.post-card-op { border: 2px solid var(--amber-soft); }
.post-meta { display: flex; align-items: center; gap: 8px; font-size: 12.5px; color: var(--muted); }
.post-author { font-weight: 700; color: var(--ink); }
.post-body { line-height: 1.6; word-break: break-word; overflow-wrap: anywhere; }
.forum-report { margin-left: auto; background: none; border: none; cursor: pointer; color: var(--muted);
  padding: 4px; border-radius: 8px; display: inline-flex; line-height: 0; transition: color .15s ease, background .15s ease; }
.forum-report:hover { color: var(--danger); background: var(--danger-soft); }
.forum-report.reported { color: var(--green); cursor: default; }

/* Formulaires (création / réponse). */
.forum-reply { margin-top: 26px; }
.forum-form { display: flex; flex-direction: column; gap: 8px; }
.forum-create { max-width: 640px; }
.forum-label { font-weight: 700; font-size: 14px; margin-top: 8px; }
.forum-optional { font-weight: 500; color: var(--muted); font-size: 12px; }
.forum-input, .forum-select, .forum-textarea {
  font: inherit; color: var(--ink); background: var(--surface);
  border: 2px solid transparent; border-radius: 14px; padding: 12px 14px; width: 100%;
  transition: border-color .15s ease, box-shadow .15s ease; }
.forum-textarea { min-height: 120px; resize: vertical; line-height: 1.55; }
.forum-input:focus, .forum-select:focus, .forum-textarea:focus {
  outline: none; border-color: var(--amber); box-shadow: 0 0 0 3px var(--amber-soft); }
/* Combobox filtrable du tag source/deck (/forum/nouveau). Classes .tag-* distinctes
   de .dyn-* (collision de handlers délégués, cf. commentaire dans js/forum.js) : le
   STYLE est repris en groupant les sélecteurs plus haut (.dyn-listbox/.dyn-option),
   seul le positionnement propre au formulaire vit ici. Le champ visible réutilise
   .forum-input, donc rien à redéclarer pour lui. */
.tag-combo { position: relative; }
.tag-listbox {
  position: absolute; z-index: 30; left: 0; right: 0; top: calc(100% + 4px);
  margin: 0; padding: 4px; list-style: none; max-height: 280px; overflow-y: auto;
  background: var(--surface); border: 1px solid var(--line); border-radius: 12px; box-shadow: var(--shadow);
}
.tag-listbox[hidden] { display: none; }
.tag-option { padding: 9px 11px; border-radius: 8px; font-size: 13.5px; color: var(--ink); cursor: pointer; }
.tag-option:hover, .tag-option.active { background: var(--amber-soft); }
/* En-tête de groupe : non cliquable, role="presentation" côté DOM. */
.tag-group {
  padding: 8px 11px 4px; font-size: 11px; font-weight: 800; letter-spacing: .06em;
  text-transform: uppercase; color: var(--muted); cursor: default;
}
.tag-status { font-size: 12px; color: var(--muted); margin-top: 4px; }

/* Autocomplétion de mention dans un textarea : même coquille .tag-combo (ancrage
   absolu de la listbox), la liste s'ouvre SOUS le champ. Le textarea porte lui-même
   role="combobox" (c'est lui qui a le focus). */
.mention-combo { display: flex; flex-direction: column; }
.mention-combo .tag-listbox { top: calc(100% - 22px); }

/* Mention RÉSOLUE dans un message publié. Aucune palette nouvelle : même traitement
   que l'auteur cliquable (.forum-author) — accent violet, souligné au survol. Le
   badge-pastille .forum-slug-badge reste réservé à l'IDENTIFICATION d'un objet
   (en-tête de sujet, verso de carte) ; en plein texte, une pastille couperait la
   lecture. */
.forum-mention { color: var(--amber); text-decoration: none; font-weight: 600; }
.forum-mention:hover { text-decoration: underline; }

.forum-counter { font-size: 12px; color: var(--muted); }
.forum-form-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-top: 4px; }
.forum-form-msg { color: var(--danger); font-size: 13px; font-weight: 600; }

.forum-login-invite { background: var(--surface); border-radius: 18px; padding: 20px; text-align: center; margin-top: 20px; }
.forum-login-invite p { color: var(--muted); margin-bottom: 12px; }
.forum-locked-banner { display: flex; align-items: center; gap: 8px; justify-content: center;
  background: var(--surface); color: var(--muted); border-radius: 14px; padding: 14px; font-size: 14px; margin-top: 26px; }

.forum-empty { color: var(--muted); padding: 28px 0; text-align: center; }

/* Pagination habillée au style du site. */
.forum-pager { display: flex; align-items: center; justify-content: center; gap: 14px; margin-top: 24px; }
.forum-pager-btn { color: var(--amber); font-weight: 700; text-decoration: none; font-size: 14px; }
.forum-pager-btn:hover { text-decoration: underline; }
.forum-pager-page { color: var(--muted); font-size: 13px; }

/* Apparition des cartes forum — même esprit que .card-enter (fade + scale léger). */
@keyframes forum-in { from { opacity: 0; transform: translateY(6px) scale(.98); } to { opacity: 1; transform: none; } }
.forum-in { animation: forum-in .25s ease both; }
@media (prefers-reduced-motion: reduce) { .forum-in { animation: none; } }

/* Focus clavier visible partout (aligné sur body.high-contrast :focus-visible du site). */
.forum-main a:focus-visible, .forum-main button:focus-visible,
.forum-input:focus-visible, .forum-select:focus-visible, .forum-textarea:focus-visible {
  outline: 3px solid var(--amber); outline-offset: 2px; }

/* Auteur cliquable (@pseudo) → couleur accent, sur-clic souligné. S'applique aux bylines
   forum (.tc-author/.post-author + .forum-author) et au verso deck (a.back-author-name). */
.forum-author, a.back-author-name { color: var(--amber); text-decoration: none; }
.forum-author:hover, a.back-author-name:hover { text-decoration: underline; }

/* Liens de la méta d'une carte-sujet (tag catégorie, badge @slug, auteur) : au-DESSUS de
   l'overlay .topic-card-title::after (qui rend toute la carte cliquable) → chacun reste
   cliquable vers sa propre cible. Sans ce z-index, seul le titre captait le clic. */
.topic-card-meta a { position: relative; z-index: 1; }

/* Fil #9bis (visuel retenu : mixte) — PARTIE 5 : badge @forum_slug au verso d'une carte.
   Il était en position:absolute (top/left 14px) et se posait PAR-DESSUS le bloc titre /
   sous-titre du verso (depuis que le sous-titre a été déplacé au verso, fil #9). Correction
   par la MISE EN PAGE, pas par z-index/décalage : le badge est désormais le premier élément
   DU FLUX de .card-back (il occupe sa propre rangée, le reste descend naturellement) ; la
   réserve de 25px en tête de la description longue n'a alors plus de raison d'être (classe
   .has-forum-badge posée par cards.js). Comportement du lien inchangé. */
.card-forum-badge { position: relative; align-self: flex-start; z-index: 2;
  max-width: calc(100% - 44px); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.card-back.has-forum-badge { gap: 8px; }
.card-back.has-forum-badge .card-long-desc { padding-top: 0; }

/* Decks du profil : grille RÉUTILISÉE de /favoris (.grid) + tuiles .deck-card identiques.
   Seul ajout propre au profil : espacement/centrage du bouton « Afficher plus de decks »
   (réutilise .more-btn pour le style). */
.profile-decks-more { display: block; margin: 20px auto 0; }

/* Perf mobile : aucun backdrop-filter utilisé dans le forum → rien à retirer < 620px. */

/* ══════════════════════════════════════════════════════════════════════════════
   GABARIT « CARTES LARGES ET COURTES » (fil #9 — vraie demande d'Hugo)
   Cartes paysage (larges, courtes) sur 1 à 2 colonnes, au lieu du portrait 5/7 dense.
   Le MÉCANISME FLIP EST INCHANGÉ : .card.flip / .card-inner / rotateY / .card-face /
   .card-back (absolute inset:0) restent tels quels. On ne touche QUE :
     · la grille du kiosque (#grid) → 1-2 colonnes larges ;
     · l'agencement interne du RECTO (#grid .card-front) → horizontal.
   Scopé #grid : favoris.html (#fav-grid) et toute autre .grid restent inchangées.
   Deux variables pilotent la cible (Hugo : ~800×250, variante ~1024×200). ══════════ */
:root { --card-w: 820px; --card-min-h: 232px; }
#grid {
  grid-template-columns: repeat(auto-fill, minmax(min(var(--card-w), 100%), 1fr));
  max-width: calc(var(--card-w) * 2 + 18px); margin-inline: auto;
}
/* La carte est bornée à --card-w et CENTRÉE dans sa piste : en 1 colonne (desktop
   standard) elle reste ~820px et ne s'étire pas à toute la largeur ; en 2 colonnes
   (écran large) chaque piste vaut ~820px. */
#grid > .card { width: min(var(--card-w), 100%); max-width: none; justify-self: center; margin-inline: 0; }

/* Recto : on quitte le portrait 2/3 pour un format large et court, contenu horizontal. */
#grid .card.flip { align-self: start; }
#grid .card-front { aspect-ratio: auto; min-height: var(--card-min-h); }
/* CORRECTIF CLIC : .card-content passe en flex:1 (remplit la hauteur) et RECOUVRAIT le
   .card-toprow absolu (état + icônes ♥/partage/ⓘ), au même z-index → les icônes étaient
   VISIBLES mais leurs clics captés par card-content (« boutons qui ne marchent pas »). On
   remonte le toprow au-dessus : ses contrôles reçoivent de nouveau les clics. Les éléments
   interactifs de card-content (switch, action) sont plus bas → non affectés. */
#grid .card-toprow { z-index: 5; }
/* .card-content n'est plus ancré en bas : il occupe la hauteur et centre son contenu.
   .card-toprow (état + icônes) est en position:absolute en haut → on RÉSERVE sa hauteur
   via padding-top pour que .card-top (le titre) soit TOUJOURS rendu EN DESSOUS du toprow,
   jamais en chevauchement, quelle que soit la carte. */
#grid .card-front .card-content {
  margin-top: 0; flex: 1 1 auto;
  display: flex; flex-direction: column; justify-content: center;
  padding: 50px 22px 14px; gap: 2px;
}
/* CORRECTION : le titre (.card-top) est rendu DANS le toprow (déplacé en JS), À DROITE de
   la pastille d'état et avant les icônes. En flex (flex:1) il se cale à droite de l'état
   quelle que soit la largeur de celui-ci (état-point ou état à libellé). */
#grid .card-toprow .card-top {
  flex: 1 1 auto; min-width: 0; margin: 0;
  display: flex; align-items: center; gap: 6px;
}
#grid .card-toprow .card-top h3 {
  font-family: 'Baloo 2', sans-serif; font-size: 20px; font-weight: 800;
  line-height: 1.1; letter-spacing: -0.02em; color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0;
}
#grid .card-front .card-desc { -webkit-line-clamp: 2; }
/* Tags de catégories AU RECTO (déplacés depuis le verso, à la place du sous-titre). */
/* Collés juste sous .card-toprow : margin-bottom:auto les plaque en haut du contenu ;
   le margin-bottom:auto de .card-textbox partage l'espace restant → description/action
   restent centrées entre les tags et le bas de la carte. */
#grid .card-front .back-tags { margin-top: 0; margin-bottom: auto; }
#grid .card-front .back-tags ~ .card-textbox { margin-bottom: auto; }
/* Sous-titre AU VERSO (déplacé depuis le recto) : garde ses paramètres de texte d'origine
   (.card-subtitle de base : 12px/600/muted). Seule la marge est ajustée pour le verso :
   margin-top d'origine (-6px) augmenté de 10px = 4px ; margin-bottom à 0. */
#grid .card-back .card-subtitle { margin-top: 4px; margin-bottom: 0; }
/* Badge d'édition dupliqué (.card-edition) masqué quand le sous-titre est au verso. */
#grid .card-back .card-subtitle .card-edition { display: none; }
/* La « boîte texte » passe en RANGÉE : description à gauche (extensible), zone d'action
   à droite (largeur fixe) → remplit le format large. (.card-textbox/.card-action étaient
   en display:contents ; on les matérialise en flex, sans effet sur le flip.) */
#grid .card-front .card-textbox {
  display: flex; flex-direction: row; align-items: center; justify-content: space-between;
  gap: 28px; margin-top: 8px;
}
#grid .card-front .card-static-desc { flex: 1 1 auto; margin: 0; min-width: 0; }
#grid .card-front .card-action {
  flex: 0 0 auto; width: 280px; max-width: 42%;
  display: flex; flex-direction: column; gap: 8px; align-items: flex-end; margin-top: 0;
  margin-left: auto; text-align: right;
}
/* DURCISSEMENT FLIP (fil #9) : on RÉ-AFFIRME explicitement, dans le scope #grid, les
   propriétés structurelles que le flip EXIGE — pour garantir que ce bloc de gabarit ne
   puisse jamais les affaiblir (perspective sur .card, preserve-3d + relatif sur .card-inner,
   faces en absolute/inset:0/backface-hidden). Valeurs identiques au CSS flip d'origine. */
#grid .card.flip { perspective: 1400px; }
#grid .card-inner { position: relative; transform-style: preserve-3d; }
#grid .card-face { -webkit-backface-visibility: hidden; backface-visibility: hidden; }
/* Toutes les faces ARRIÈRE restent en absolu inset:0 (jamais dans le flux → pas d'empilement,
   la hauteur du conteneur ne dépend QUE du recto). Défilement interne si le contenu déborde
   du format court. Le recto (.card-front) reste, lui, en position:relative (dans le flux). */
#grid .card-back, #grid .card-share-face, #grid .card-task-face, #grid .card-community-face,
#grid .card-deck-face {
  position: absolute; inset: 0; overflow-y: auto;
}
/* PARTIE 4 (fil #9bis, visuel retenu : mixte) : la face PARTAGE est la seule à ne jamais
   défiler — ses 9/10 vignettes tiennent désormais dans la hauteur (5 colonnes resserrées),
   sur carte large comme ultra. On annule donc l'overflow-y:auto hérité ci-dessus pour
   cette face (les autres faces, à texte libre, le conservent). */
#grid .card-share-face { overflow: hidden; }

/* Mobile : 1 colonne pleine largeur, recto re-empilé (la rangée desc|action ne tient pas). */
@media (max-width: 620px) {
  #grid { max-width: none; }
  #grid .card-front .card-textbox { flex-direction: column; align-items: stretch; gap: 10px; }
  #grid .card-front .card-action { width: auto; max-width: none; }
}

/* ══════════════════════════════════════════════════════════════════════════════
   TEMPORAIRE — fil #9bis, COMPARATEUR DE VISUELS DU KIOSQUE — À RETIRER une fois
   le visuel choisi (avec la barre .view-compare de index.html et js/view-compare.js
   et le bump sw.js associé). Rien ici ne touche le flip : .card.flip / .card-inner /
   rotateY / .card-face restent pilotés par les règles #grid ci-dessus (scope #grid),
   qui s'appliquent quel que soit data-view-mode. On ne fait varier que les DIMENSIONS
   du conteneur/des cartes et la DISPOSITION du recto.
   Modes (attribut data-view-mode sur #grid) :
     · large  (défaut / absent) → gabarit large existant, aucune règle ici.
     · liste  → géré hors CSS de ce bloc (root[data-view="list"], view-mode.js/list-view.js).
     · compact→ 350×200, 3 colonnes.
     · ultra  → 300×150, sans description, grille auto-fill dense.
     · mixte  → colonne principale large + colonne secondaire ultra-compacte.
   ══════════════════════════════════════════════════════════════════════════════ */

/* La barre de boutons temporaire (index.html, dans .kiosk-head). */
.view-compare {
  display: flex; flex-wrap: wrap; gap: 6px; align-items: center;
  margin: 10px 0 2px; padding: 6px 8px;
  border: 1px dashed var(--amber); border-radius: 12px;
  background: color-mix(in srgb, var(--amber) 8%, transparent);
}
.view-compare .vc-label { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); margin-right: 4px; }
.view-compare .vc-btn {
  font: inherit; font-size: 12.5px; font-weight: 700; cursor: pointer;
  padding: 6px 11px; border-radius: 999px; border: 1px solid var(--line, #d9d4cc);
  background: var(--surface); color: var(--ink);
}
.view-compare .vc-btn:hover { border-color: var(--amber); }
.view-compare .vc-btn.on { background: var(--amber); color: #fff; border-color: var(--amber); }

/* ---- Visuel 3 : BLOCS COMPACTS (350×200, 3 colonnes) ---- */
#grid[data-view-mode="compact"] {
  grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
  max-width: calc(350px * 3 + 2 * 18px); gap: 18px; margin-inline: auto;
}
#grid[data-view-mode="compact"] > .card { width: 100%; max-width: 350px; height: 200px; justify-self: center; }
/* ---- Visuel 4 : BLOCS ULTRA-COMPACTS (320×150, auto-fill dense) ---- */
#grid[data-view-mode="ultra"] {
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  max-width: none; gap: 14px; margin-inline: auto;
}
#grid[data-view-mode="ultra"] > .card { width: 320px; max-width: 320px; min-height: 150px; justify-self: center; }

/* Recto compact ET ultra ET colonne secondaire du mixte : hauteur bornée, recto empilé,
   paddings/typo réduits. min-height ANNULE le var(--card-min-h) du gabarit large (sinon la
   carte ne pourrait pas descendre sous 232px). Le verso conserve son défilement interne
   (règle #grid .card-back { overflow-y:auto } du gabarit). */
#grid[data-view-mode="compact"] .card-front { min-height: 0; height: 100%; }
/* A) ultra / colonne ultra du mixte : hauteur AUTO bornee par le bas (150px), jamais 100%
   d'une boite a hauteur fixe - sinon le contenu debordait au lieu d'agrandir la carte. */
#grid[data-view-mode="ultra"] .card-front,
#grid[data-view-mode="mixte"] > .card.vc-side .card-front { min-height: 150px; height: auto; }
#grid[data-view-mode="compact"] .card-face,
#grid[data-view-mode="ultra"] .card-face,
#grid[data-view-mode="mixte"] > .card.vc-side .card-face { padding: 14px; border-radius: 18px; }
#grid[data-view-mode="compact"] .card-toprow,
#grid[data-view-mode="ultra"] .card-toprow,
#grid[data-view-mode="mixte"] > .card.vc-side .card-toprow { top: 9px; left: 10px; right: 10px; gap: 6px; }
#grid[data-view-mode="compact"] .card-toprow .card-icons,
#grid[data-view-mode="ultra"] .card-toprow .card-icons,
#grid[data-view-mode="mixte"] > .card.vc-side .card-toprow .card-icons { gap: 4px; }
#grid[data-view-mode="compact"] .card-toprow .card-icons .card-like,
#grid[data-view-mode="compact"] .card-toprow .card-icons .card-share,
#grid[data-view-mode="compact"] .card-toprow .card-icons .flip-btn,
#grid[data-view-mode="ultra"] .card-toprow .card-icons .card-like,
#grid[data-view-mode="ultra"] .card-toprow .card-icons .card-share,
#grid[data-view-mode="ultra"] .card-toprow .card-icons .flip-btn,
#grid[data-view-mode="mixte"] > .card.vc-side .card-toprow .card-icons .card-like,
#grid[data-view-mode="mixte"] > .card.vc-side .card-toprow .card-icons .card-share,
#grid[data-view-mode="mixte"] > .card.vc-side .card-toprow .card-icons .flip-btn { width: 30px; height: 30px; }
#grid[data-view-mode="compact"] .card-front .card-content,
#grid[data-view-mode="ultra"] .card-front .card-content,
#grid[data-view-mode="mixte"] > .card.vc-side .card-front .card-content {
  padding: 34px 12px 10px; justify-content: flex-start; gap: 2px;
}
#grid[data-view-mode="compact"] .card-toprow .card-top h3,
#grid[data-view-mode="mixte"] > .card.vc-side .card-toprow .card-top h3 { font-size: 18px; }
#grid[data-view-mode="ultra"] .card-toprow .card-top h3 { font-size: 18px; }
/* Recto empilé (la rangée desc|action large ne tient pas dans ces largeurs). */
#grid[data-view-mode="compact"] .card-front .card-textbox,
#grid[data-view-mode="ultra"] .card-front .card-textbox,
#grid[data-view-mode="mixte"] > .card.vc-side .card-front .card-textbox {
  flex-direction: column; align-items: stretch; justify-content: flex-start;
  gap: 6px; margin-top: 4px;
}
#grid[data-view-mode="compact"] .card-front .card-action,
#grid[data-view-mode="ultra"] .card-front .card-action,
#grid[data-view-mode="mixte"] > .card.vc-side .card-front .card-action {
  width: auto; max-width: none; align-items: stretch; text-align: left; margin-left: 0;
}
#grid[data-view-mode="compact"] .card-front .card-desc { -webkit-line-clamp: 2; font-size: 12px; }
/* ULTRA (et colonne secondaire du mixte) : PAS de description — masquée en CSS, jamais
   retirée du DOM (la logique de site.js/recherche reste intacte). */
#grid[data-view-mode="ultra"] .card-static-desc,
#grid[data-view-mode="mixte"] > .card.vc-side .card-static-desc { display: none; }
/* Verso des vignettes mini (colonne ultra « .vc-side ») : description COURTE au lieu de
   la longue (trop dense). La courte est rendue en double masqué par défaut dans backFace. */
.card-back-short { display: none; }
#grid[data-view-mode="ultra"] .card-long-desc:not(.card-back-short),
#grid[data-view-mode="mixte"] > .card.vc-side .card-long-desc:not(.card-back-short) { display: none; }
#grid[data-view-mode="ultra"] .card-back-short,
#grid[data-view-mode="mixte"] > .card.vc-side .card-back-short { display: block; }
#grid[data-view-mode="ultra"] .card-front .card-content,
#grid[data-view-mode="mixte"] > .card.vc-side .card-front .card-content { padding-top: 30px; }
/* ULTRA uniquement (pas la colonne mixte) : on retire aussi les tags du recto et les icônes
   d'action (♥/partage/ⓘ) — rendu épuré demandé par Hugo. Masqué en CSS, jamais retiré du
   DOM. Sans les icônes, la rangée haute ne porte plus que l'état + le titre. */
#grid[data-view-mode="ultra"] .back-tags,
#grid[data-view-mode="mixte"] > .card.vc-side .back-tags { display: none; }
/* C) Les icones d'action RESTENT sur l'ultra (il fallait agrandir la carte pour y acceder) :
   rangee compacte 28px a droite du titre. L'ellipsis reste sur le titre (h3), les icones ne
   se compriment pas (flex: none). Le clic dessus ne bascule PAS la colonne : onDocClickMixte
   ignore les cibles interactives (isInteractive). */
#grid[data-view-mode="ultra"] .card-toprow .card-icons,
#grid[data-view-mode="mixte"] > .card.vc-side .card-toprow .card-icons { flex: none; gap: 2px; }
#grid[data-view-mode="ultra"] .card-toprow .card-icons .card-like,
#grid[data-view-mode="ultra"] .card-toprow .card-icons .card-share,
#grid[data-view-mode="ultra"] .card-toprow .card-icons .flip-btn,
#grid[data-view-mode="mixte"] > .card.vc-side .card-toprow .card-icons .card-like,
#grid[data-view-mode="mixte"] > .card.vc-side .card-toprow .card-icons .card-share,
#grid[data-view-mode="mixte"] > .card.vc-side .card-toprow .card-icons .flip-btn {
  width: 28px; height: 28px; flex: none;
}

/* ---- Visuel 5 : MIXTE — 2 colonnes CSS Grid (principale LARGE + secondaire ULTRA) ----
   Astuce « masonry » : lignes implicites fines (8px) + span calculé en JS (view-compare.js
   measureMixte) → chaque colonne se tasse indépendamment, pas de trous liés au partage de
   rangée entre une carte large (haute) et une carte ultra (basse). Répartition des cartes
   entre les deux colonnes : voir view-compare.js (classe .vc-side posée en JS). */
#grid[data-view-mode="mixte"] {
  grid-template-columns: minmax(0, var(--card-w)) 320px;
  grid-auto-rows: 8px; grid-auto-flow: row dense; align-items: start; justify-content: center;
  max-width: calc(var(--card-w) + 320px + 18px); gap: 0 18px; margin-inline: auto;
}
/* PARTIE 2 (visuel retenu : mixte) — la bascule de colonne n'est PLUS animée par une
   transition CSS de max-width : elle est animée en JS par FLIP (Web Animations API,
   view-compare.js) — le changement de piste est appliqué d'un coup, puis la carte et ses
   voisines sont animées en transform depuis leur ancien rectangle. Aucune transition CSS
   ici, sinon elle entrerait en concurrence avec le transform du FLIP. width:100% remplit
   la piste ; max-width explicite conservé (gabarit large). */
#grid[data-view-mode="mixte"] > .card {
  grid-column: 1; width: 100%; max-width: var(--card-w); justify-self: stretch;
}
/* Pendant l'animation FLIP, la carte en mouvement passe AU-DESSUS de ses voisines (elle
   traverse la grille). Pas de pointer-events:none : les boutons d'action restent cliquables
   pendant le déplacement (hit-test elementFromPoint vérifié). */
#grid[data-view-mode="mixte"] > .card.vc-moving { z-index: 6; }
/* A) HAUTEUR AUTO (plus de 150px fixes) : sur les alertes parametrees, chips + « + ajouter »
   + interrupteur + champ depassaient la hauteur fixe et etaient coupes par le bord bas.
   min-height garde le gabarit court comme plancher ; measureMixte() lit offsetHeight et
   calcule le span en consequence, donc la colonne reste tassee. */
#grid[data-view-mode="mixte"] > .card.vc-side { grid-column: 2; min-height: 150px; height: auto; max-width: 320px; }
/* Carte statique « Proposer » (.card.add, sans .card-front) : hauteur fixe pour qu'elle ne
   s'étire pas dans la piste (sinon offsetHeight mesuré ≈ hauteur de colonne → span géant). */
#grid[data-view-mode="mixte"] > .card.add { height: var(--card-min-h); align-self: start; }
/* LEGACY (fil #9bis, visuel retenu : mixte) : ancienne phase transitoire du retour
   large vers ultra (animation par max-width en deux phases). Remplacée par le FLIP ;
   plus aucune carte ne reçoit .vc-collapsing. Conservée sans effet. */
#grid[data-view-mode="mixte"] > .card.vc-side.vc-collapsing {
  grid-column: 1; max-width: 320px; min-height: 150px; height: auto; justify-self: center;
}

/* B) MOBILE (<= 640px) : le masonry 2 colonnes laissait des vides de ~200px (les rangees
   reservees aux cartes larges subsistaient dans la colonne ultra). On sort donc du grid :
   flux FLEX vertical, une carte par ligne, pleine largeur, espacement regulier (18px) et
   AUCUN span de rangee (measureMixte() purge aussi gridRowEnd en mobile, view-compare.js).
   L'ordre DOM est conserve : .vc-side ne deplace plus rien, il ne reste qu'un format de recto. */
@media (max-width: 640px) {
  #grid[data-view-mode="mixte"] {
    display: flex; flex-direction: column; gap: 18px;
    grid-template-columns: none; grid-auto-rows: auto; max-width: none;
  }
  #grid[data-view-mode="mixte"] > .card,
  #grid[data-view-mode="mixte"] > .card.vc-side,
  #grid[data-view-mode="mixte"] > .card.add {
    grid-column: auto; width: 100%; max-width: none; height: auto; min-height: 0;
  }
  #grid[data-view-mode="mixte"] > .card.vc-side .card-front { min-height: 0; }
}

/* ---- Visuel 6 : COMPACT-LARGE (expansion au clic) ----
   Défaut = compact ÉPURÉ (350×200, 3 colonnes, SANS description : reprend le compact du
   visuel 3 + le masquage de desc du visuel 4). Au clic (classe .cl-expanded posée par
   view-compare.js), la carte prend le format LARGE (visuel 1) en OCCUPANT UNE RANGÉE PLEINE
   LARGEUR (grid-column: 1/-1) → les voisines se réorganisent proprement, sans chevauchement.
   Transition animée sur width/max-width/min-height. Le flip reste piloté par les règles
   #grid (scope commun), donc fonctionnel en compact ET en agrandi. */
#grid[data-view-mode="clarge"] {
  grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
  max-width: calc(350px * 3 + 2 * 18px); gap: 18px; margin-inline: auto; align-items: start;
}
#grid[data-view-mode="clarge"] > .card {
  width: 350px; max-width: 350px; height: 200px; justify-self: center;
  transition: width .32s ease, max-width .32s ease, min-height .32s ease;
}
/* Recto compact épuré (état NON agrandi) — mêmes réglages que compact/ultra + desc masquée. */
#grid[data-view-mode="clarge"] > .card:not(.cl-expanded) .card-front { min-height: 0; height: 100%; }
#grid[data-view-mode="clarge"] > .card:not(.cl-expanded) .card-face { padding: 14px; border-radius: 18px; }
#grid[data-view-mode="clarge"] > .card:not(.cl-expanded) .card-toprow { top: 9px; left: 10px; right: 10px; gap: 6px; }
#grid[data-view-mode="clarge"] > .card:not(.cl-expanded) .card-toprow .card-icons { gap: 4px; }
#grid[data-view-mode="clarge"] > .card:not(.cl-expanded) .card-toprow .card-icons .card-like,
#grid[data-view-mode="clarge"] > .card:not(.cl-expanded) .card-toprow .card-icons .card-share,
#grid[data-view-mode="clarge"] > .card:not(.cl-expanded) .card-toprow .card-icons .flip-btn { width: 30px; height: 30px; }
#grid[data-view-mode="clarge"] > .card:not(.cl-expanded) .card-front .card-content {
  padding: 30px 12px 10px; justify-content: flex-start; gap: 2px;
}
#grid[data-view-mode="clarge"] > .card:not(.cl-expanded) .card-toprow .card-top h3 { font-size: 18px; }
#grid[data-view-mode="clarge"] > .card:not(.cl-expanded) .card-front .card-textbox {
  flex-direction: column; align-items: stretch; justify-content: flex-start; gap: 6px; margin-top: 4px;
}
#grid[data-view-mode="clarge"] > .card:not(.cl-expanded) .card-front .card-action {
  width: auto; max-width: none; align-items: stretch; text-align: left; margin-left: 0;
}
#grid[data-view-mode="clarge"] > .card:not(.cl-expanded) .card-static-desc { display: none; }
/* État AGRANDI : format large (visuel 1), pleine largeur. Le recto retrouve la disposition
   large via les règles #grid .card-front (non surchargées ici, car scopées :not(.cl-expanded)). */
#grid[data-view-mode="clarge"] > .card.cl-expanded {
  grid-column: 1 / -1; width: min(var(--card-w), 100%); max-width: var(--card-w);
  height: auto; min-height: var(--card-min-h); justify-self: center;
}
/* Retour ANIMÉ (large → compact) — symétrique de l'agrandissement. On GARDE la rangée
   pleine largeur (grid-column: 1/-1) pendant que la largeur redescend à 350px : sans ça,
   le retrait immédiat de grid-column réduit la piste à 350px AVANT le tween → la largeur
   SAUTE au lieu d'être animée (l'agrandissement, lui, élargit la piste d'abord, d'où son
   animation correcte). view-compare.js pose .cl-collapsing puis la retire à la fin de la
   transition (transitionend sur width), rendant la carte à sa cellule normale une fois
   déjà revenue à 350px (donc sans saut final). */
#grid[data-view-mode="clarge"] > .card.cl-collapsing {
  grid-column: 1 / -1; width: 350px; max-width: 350px; min-height: 200px; justify-self: center;
}

/* Mobile : ces modes de test retombent tous en 1 colonne pleine largeur, sans hauteur
   forcée (le contenu prime sur la maquette de comparaison sur petit écran). */
@media (max-width: 620px) {
  #grid[data-view-mode="compact"], #grid[data-view-mode="ultra"], #grid[data-view-mode="mixte"],
  #grid[data-view-mode="clarge"] {
    grid-template-columns: 1fr; grid-auto-rows: auto; max-width: none; gap: 14px;
  }
  #grid[data-view-mode="compact"] > .card,
  #grid[data-view-mode="ultra"] > .card,
  #grid[data-view-mode="mixte"] > .card,
  #grid[data-view-mode="mixte"] > .card.vc-side,
  #grid[data-view-mode="clarge"] > .card { width: 100%; max-width: none; height: auto; grid-column: 1; }
  #grid[data-view-mode="compact"] .card-front,
  #grid[data-view-mode="ultra"] .card-front,
  #grid[data-view-mode="mixte"] > .card.vc-side .card-front,
  #grid[data-view-mode="clarge"] > .card:not(.cl-expanded) .card-front { height: auto; min-height: 0; }
}
/* FIN TEMPORAIRE — fil #9bis */

/* I1) LIEN D'ÉVITEMENT — premier élément focusable de la page : hors écran tant qu'il
   n'a pas le focus, puis visible. Permet d'atteindre la grille sans parcourir tout
   l'en-tête au clavier. */
.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--amber); color: #fff; font-weight: 700;
  padding: 10px 18px; border-radius: 0 0 10px 0; text-decoration: none;
}
.skip-link:focus {
  left: 0; outline: 3px solid var(--ink); outline-offset: 2px;
}

/* I3) Titre de section masqué visuellement mais lu par les lecteurs d'écran : la grille
   enchaînait des H3 (titres de cartes) directement sous le H1 de la page. */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* I5) FOCUS CLAVIER homogène : les <select> et les entrées d'en-tête n'avaient pas de
   contour visible (ou seulement un effet d'animation). Contour violet, même règle pour
   tous, sans toucher au survol souris (:focus-visible uniquement). */
select:focus-visible,
.param-select:focus-visible,
.pref-select:focus-visible,
.hd-link:focus-visible,
.btn-deposit:focus-visible,
.btn-login:focus-visible,
.lepoint-link:focus-visible,
.chip-f:focus-visible,
.more-btn:focus-visible,
.to-top:focus-visible {
  outline: 3px solid var(--amber-ink); outline-offset: 2px; border-radius: 8px;
}



/* H3) Compteur de pagination : repere discret au-dessus de « Afficher plus ». */
.grid-count { text-align: center; color: var(--ink); font-size: 13px; font-weight: 600; margin: 18px 0 0; }
.grid-count[hidden] { display: none; }
/* H3) Retour en haut : apparait apres un defilement significatif (bouton pose par site.js). */
.to-top {
  position: fixed; right: 18px; bottom: 18px; z-index: 40;
  width: 44px; height: 44px; border-radius: 50%; border: none; cursor: pointer;
  background: var(--amber); color: #fff; font-size: 18px; line-height: 1;
  box-shadow: var(--shadow); display: none; align-items: center; justify-content: center;
}
.to-top.on { display: inline-flex; }
.to-top:hover { background: color-mix(in srgb, var(--amber) 88%, #000); }

/* J) Combobox DEPARTEMENT du kiosque : meme coquille que le combobox du forum
   (.tag-listbox), sur un champ au gabarit des autres controles de parametre. */
.dept-combo { position: relative; }
.dept-combo .dept-search {
  width: 100%; border: 1px solid var(--line); background: var(--bg); color: var(--ink);
  border-radius: 9px; padding: 10px 12px; font: inherit; font-size: 13.5px;
}
.dept-combo .dept-search:focus-visible { outline: 3px solid var(--amber-ink); outline-offset: 1px; }
.dept-combo .dept-listbox { max-height: 240px; }
.dept-combo .dyn-status { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }

/* Interrupteur « Publier une alerte » (LOT 5 interim) : les CTA .publish-cta sont
   masqués tant que <html> ne porte pas data-lba-publish="true" (attribut posé côté
   serveur par pages.js — pas de script inline, pour respecter la CSP script-src 'self'). */
html:not([data-lba-publish="true"]) .publish-cta { display: none !important; }
