/* Surprizio — design tokens
   Palette: cream canvas, deep ink, mint green, coral ribbon accent, amber twine
   Type: Nunito Sans (display + body/UI)
*/
:root{
  --canvas:#FDFCFC;
  --paper:#FFFFFF;
  --ink:#1E2B22;
  --ink-soft:#6B7568;
  --forest:#2F9E6E;
  --forest-dark:#237A56;
  --mint-soft:#ECF4EF;
  --berry:#EF5B50;
  --berry-dark:#D8483E;
  --berry-soft:#FDEAE7;
  --gold:#F2A93B;
  --gold-soft:#FDF1DC;
  /* Fond des boutons d'action principale : le vert profond de la charte, qui
     était DÉJÀ la couleur de base de .btn (jamais rendue, toutes les instances
     portant une variante). Le vert dit « valider / go », convient à Réserver,
     Ajouter, Confirmer, et laisse --gold entièrement aux statuts et au décor.
     Contraste du texte blanc : 5,26:1, au-dessus du seuil AA — --forest seul
     n'étant qu'à 3,37:1, ne pas l'utiliser comme fond de bouton. */
  --primary:var(--forest-dark);
  --primary-dark:#1C6144;
  /* Couleur propre au badge « Organisateur » (tag-moderator). Il partageait
     --gold avec tag-prete, ce qui rendait les deux indiscernables sur une
     tuile « Prête » gérée par un organisateur — cas fréquent. */
  --organizer:#3F6C82;
  /* Compagnon clair de --organizer, sur le modèle de --mint-soft / --berry-soft
     / --gold-soft : fond des pastilles décoratives dont l'icône est --organizer.
     Même teinte (H 0.55), luminosité alignée sur --mint-soft (L 0.94). */
  --organizer-soft:#EBF1F4;
  /* Fonds du bloc d'information d'une tuile de liste, UN PAR STATUT.
     ⚠️ Dérivés à faible saturation des couleurs déjà en place — aucune teinte
     nouvelle : --gold pour « prête », --organizer pour « brouillon », un gris
     neutre pour « archivée ». Ils reprennent donc les mêmes repères que les
     bordures de section et les badges, ce qui fait qu'on lit le statut d'un
     coup d'œil sans avoir à croiser deux codes visuels différents.
     ⚠️ Volontairement PLUS PÂLES que les -soft ci-dessus : ceux-là servent de
     fond à des pastilles d'icône de 40px, celles-ci à un bloc de texte de la
     taille d'une carte. La même intensité y deviendrait envahissante. */
  --bg-prete:#FBF3E2;
  --bg-brouillon:#EBF2F5;
  --bg-archivee:#F1F0ED;
  --beige: #F8F6F3;
  /* Variante plus soutenue de --beige, pour le survol des surfaces déjà
     beiges (boutons secondaires) : --canvas y serait plus CLAIR que le fond
     au repos, et le survol paraîtrait inversé. */
  --beige-dark: #EFECE6;
  --line:#EFE1D2;
  --danger:#D8483E;
  --radius:18px;
  --radius-lg:28px;
  --shadow: 0 10px 28px rgba(30,43,34,0.07), 0 2px 6px rgba(30,43,34,0.05);
  --shadow-sm: 0 2px 8px rgba(30,43,34,0.06);
}

*{box-sizing:border-box;}
/* 🔴 `[hidden]` DOIT ÊTRE REPOSÉ ICI, ET AVEC `!important`.
   Le `display:none` de l'attribut `hidden` vient de la feuille par DÉFAUT du
   navigateur : n'importe quelle règle auteur posant un `display` sur le même
   élément l'emporte, sans `!important` de son côté et sans le moindre
   avertissement. L'élément reste alors visible alors que le JS le croit
   masqué — panne parfaitement silencieuse, et le script n'a aucun moyen de
   s'en apercevoir.
   Le défaut s'est produit DEUX FOIS : sur le bouton « Partager » de la
   bannière (les deux états s'affichaient ensemble, « Copié ! » dès le
   chargement) et sur la modale d'idée cadeau, qui montrait un `<img src="">`
   pour une idée sans image. Deux corrections ponctuelles existaient déjà
   ailleurs pour la même raison (`.cover-field [hidden]`,
   `.modal-overlay[hidden]`) — le motif est donc systémique, pas accidentel.
   ⚠️ Tout composant qui bascule un `hidden` en JS dépend de cette ligne. Ne
   pas la retirer en la prenant pour une redondance avec le navigateur. */
[hidden]{display:none !important;}
html,body{margin:0;padding:0;}
body{
  background:var(--canvas);
  color:var(--ink);
  font-family:'Nunito Sans', system-ui, sans-serif;
  font-size:16px;
  line-height:1.5;
  min-height:100vh;
  display:flex;
  flex-direction:column;
}
h1,h2,h3,h4{
  font-family:'Nunito Sans', system-ui, sans-serif;
  font-weight:800;
  color:var(--ink);
  line-height:1.15;
  margin:0 0 .4em;
  letter-spacing:-.01em;
}
h1{font-size:2.2rem;}
h2{font-size:1.5rem;}
h3{font-size:1.15rem;}
p{margin:0 0 1em;}
a{color:var(--berry);text-decoration:none;}
a:hover{text-decoration:none;}
.wrap{max-width:1120px;margin:0 auto;padding:0 24px;}

/* ---- Icônes SVG inline (voir templates/_icons.html.twig) ---- */
.icon{display:inline-block;vertical-align:-0.15em;flex-shrink:0;}
.text-forest{color:var(--forest);}
.text-berry{color:var(--berry);}
.text-danger{color:var(--danger);}
.text-mint-soft{color:var(--mint-soft);}

/* ---- Import de cadeaux (liste à cocher) ---- */
.import-gift-list{display:flex;flex-direction:column;gap:8px;margin:14px 0 20px;}
.import-gift-row{display:flex;align-items:flex-start;gap:12px;padding:12px 14px;border:1px solid var(--line);border-radius:12px;cursor:pointer;transition:border-color .15s,background .15s;}
.import-gift-row:hover{border-color:var(--forest);background:var(--mint-soft);}
.import-gift-row input[type="checkbox"]{margin-top:3px;flex-shrink:0;width:auto;}
.import-gift-info{display:flex;flex-direction:column;gap:2px;}

/* ---- Topbar ---- */
.topbar{background:var(--paper);border-bottom:1px solid var(--line);}
.topbar-inner{display:flex;align-items:center;justify-content:space-between;padding:16px 24px;}
.brand{color:var(--ink);font-family:'Nunito Sans',sans-serif;font-size:1.3rem;font-weight:800;display:flex;align-items:center;gap:10px;}
.brand:hover{text-decoration:none;opacity:.9;}
.brand-badge{
  display:inline-flex;align-items:center;justify-content:center;
  width:52px;height:52px;border-radius:14px;
  flex-shrink:0;
}
.brand-mark{width:40px;height:40px;object-fit:contain;display:block;}
.nav{display:flex;align-items:center;gap:10px;flex-wrap:wrap;}
/* Groupe « Mes listes / Profil ». `display:contents` : le conteneur n'a
   aucune existence visuelle, ses liens restent des enfants directs de .nav.
   Il n'existe que pour pouvoir les faire basculer ENSEMBLE sur une seconde
   ligne en mobile (voir le @media 760px), sans les sortir du <nav>. */
.nav-links{display:contents;}
.nav a{color:var(--ink-soft);font-weight:600;font-size:.95rem;padding:8px 6px;}
.nav a:hover{color:var(--ink);text-decoration:none;}
/* Bouton secondaire de la nav. Mêmes surfaces que .btn-outline : sans fond
   déclaré il laissait apparaître le blanc de la topbar et détonnait avec les
   boutons secondaires du contenu. Classe distincte de .btn car les métriques
   diffèrent en contexte de nav. */
.nav-login{background:var(--beige);border:1px solid var(--line);border-radius:4px;padding:8px 18px !important;color:var(--ink) !important;}
.nav-login:hover{background:var(--beige-dark);border-color:var(--ink-soft);}
/* ⚠️ `.nav-cta` (« Créer un compte » de l'en-tête) a été SUPPRIMÉE avec son
   bouton — voir le commentaire dans base.html.twig. Ne pas la recréer pour
   remettre un second appel à l'action dans le header : c'est exactement le
   doublon qu'on a retiré. Une action principale de nav qui se justifierait
   un jour reprendrait les couleurs de .btn-primary, avec ses propres
   métriques et un `!important` sur la couleur pour l'emporter sur `.nav a`,
   plus spécifique. */
/* Sélecteur de langue (header) — pastille + menu déroulant, pour rester
   praticable au-delà de deux langues (voir CLAUDE.md). */
.lang-picker{position:relative;margin-left:4px;}
.lang-toggle{
  display:inline-flex;align-items:center;gap:4px;font:inherit;font-size:.78rem;
  font-weight:700;letter-spacing:.02em;color:var(--ink-soft);
  background:none;border:1px solid var(--line);border-radius:999px;
  padding:4px 10px;cursor:pointer;
}
.lang-toggle:hover{color:var(--ink);background:var(--beige);}
.lang-toggle svg{width:12px;height:12px;transition:transform .15s ease;}
.lang-toggle[aria-expanded="true"] svg{transform:rotate(180deg);}
/* ⚠️ `right:0`, pas `left:0` comme .cover-menu : ce composant vit tout à
   droite de la nav, un ancrage à gauche ferait déborder le panneau hors de
   l'écran sur les petites largeurs. */
.lang-menu{
  position:absolute;z-index:30;top:calc(100% + 6px);right:0;min-width:150px;
  background:var(--paper);border:1px solid var(--line);border-radius:12px;
  box-shadow:var(--shadow);padding:6px;
}
.lang-option{
  display:block;width:100%;text-align:left;padding:8px 10px;border-radius:8px;
  font-size:.9rem;color:var(--ink);background:none;border:none;font:inherit;
}
.lang-option:hover{background:var(--canvas);text-decoration:none;}
.lang-option-current{font-weight:700;background:var(--canvas);}

.main{flex:1;padding:20px;width:100%;}
.footer{color:var(--ink-soft);font-size:.85rem;padding:0;border-top:1px solid var(--line);background:var(--paper);margin-top:40px;}
.footer-top{display:flex;flex-wrap:wrap;justify-content:space-between;gap:32px;padding-top:44px;padding-bottom:44px;}
.footer-col{flex:1 1 200px;}
.footer-brand{display:flex;align-items:center;gap:10px;font-family:'Nunito Sans';font-weight:800;font-size:1.15rem;color:var(--ink);margin-bottom:10px;}
.footer-tagline{max-width:280px;}
.footer-col h4{font-size:.8rem;text-transform:uppercase;letter-spacing:.04em;color:var(--ink);margin-bottom:14px;}
.footer-col a{display:block;color:var(--ink-soft);font-size:.92rem;margin-bottom:10px;}
.footer-col a:hover{color:var(--berry);text-decoration:none;}
.footer-bottom{border-top:1px solid var(--line);padding:18px 0;text-align:center;}

/* ---- Flash ---- */
.flash{padding:15px;border-radius:var(--radius);margin:20px 0 20px 0;font-weight:500;}
.flash-success{background:var(--mint-soft);color:var(--forest-dark);border:1px solid #C6E9D5;}
.flash-error{background:var(--berry-soft);color:var(--danger);border:1px solid #F4C7C1;}
.flash-info{background:var(--gold-soft);color:#7A5313;border:1px solid #F3DCA8;}
/* ---- Messages de SESSION uniquement ----
   ⚠️ `.flash` sert aussi à des bandeaux permanents (avertissement de
   confidentialité, encart de lecture seule de la discussion, états vides) : une
   vingtaine dans le projet. Ce modificateur n'est posé que sur les messages
   rendus par `app.flashes()` — voir base.html.twig. Ne pas le généraliser à
   `.flash`, ce serait donner un bouton de fermeture à des explications qui
   doivent rester à l'écran. */
.flash-dismissible{position:relative;padding-right:48px;}
.flash-close{
  position:absolute;top:10px;right:10px;
  display:flex;align-items:center;justify-content:center;
  width:26px;height:26px;border-radius:999px;border:none;
  background:transparent;color:inherit;opacity:.55;cursor:pointer;
  transition:opacity .15s ease,background .15s ease;
}
.flash-close:hover,.flash-close:focus-visible{opacity:1;background:rgba(30,43,34,.08);}
.flash-close svg{width:16px;height:16px;}
/* La disparition elle-même. `max-height` et `margin` sont animés en plus de
   l'opacité : sans eux, le message s'efface mais laisse son espace, et la page
   garde un trou de la taille du bandeau jusqu'à son retrait du DOM. */
.flash--leaving{
  opacity:0;transform:translateY(-4px);
  max-height:0;margin-top:0;margin-bottom:0;padding-top:0;padding-bottom:0;
  overflow:hidden;
  transition:opacity .3s ease,transform .3s ease,max-height .3s ease,margin .3s ease,padding .3s ease;
}
/* ⚠️ Un contenu qui s'efface tout seul est exactement ce que ce réglage
   système vise. On le retire d'un coup plutôt que de l'animer — le message
   disparaît toujours, sans mouvement. */
@media (prefers-reduced-motion: reduce){
  .flash--leaving{transition:none;}
}

/* ---- Cards / layout blocks ---- */
.card{
  background:var(--paper);
  border-radius:var(--radius);
  box-shadow:var(--shadow);
  padding:28px;
  border:1px solid var(--line);
}
.card + .card{margin-top:20px;}
.narrow{max-width:580px;margin:0 auto;}
/* Pages légales : plus larges que `.narrow`, volontairement.

   ⚠️ `.narrow` (580px) est calibrée pour des FORMULAIRES — connexion,
   inscription, création de liste — où des champs de 850px seraient désagréables
   à remplir. Ne pas l'élargir globalement pour régler ce cas : les 24 autres
   écrans qui la portent y perdraient.

   ⚠️ 720px et non davantage : ces pages sont des paragraphes denses qu'on lit
   en continu, et cette largeur donne ~83 caractères par ligne. La fourchette de
   confort typographique va de 45 à 75 ; au-delà de ~90, l'œil perd la ligne en
   revenant à la suivante. 900px en donnerait ~105.

   ⚠️ Aucune largeur en dessous de 720px : `max-width` ne contraint rien tant
   que la fenêtre est plus étroite, la carte suit alors `.wrap` et ses marges. */
.legal-page{max-width:720px;}

.page-head{display:flex;justify-content:space-between;align-items:flex-end;flex-wrap:wrap;gap:16px;margin-bottom:16px;}
.page-head p{color:var(--ink-soft);margin:4px 0 0;}
/* Colonne de gauche de l'en-tête (titre, description, méta, badges). `flex:1`
   pour qu'elle occupe la place laissée par les actions ; `min-width:0` pour
   qu'un mot très long de la description puisse se couper au lieu de pousser
   les boutons hors de l'écran. */
.page-head-main{flex:1 1 auto;min-width:0;}
/* En-tête de la section « Cadeaux » : même patron que celui d'une page, à deux
   ajustements près.
   ⚠️ `align-items:center` et non `flex-end` : ici la colonne de gauche n'est
   qu'un titre, sans description ni métadonnées dessous. Aligner sur la ligne de
   base laisserait les boutons flotter au-dessus de lui.
   ⚠️ `margin-top:4px` reprend celui que portait le titre seul, pour que la
   section ne remonte pas contre le bandeau qui la précède. */
.gifts-head{align-items:center;margin-top:4px;margin-bottom:20px;}
.gifts-head h2{margin:0;}

/* Description d'une liste, sous la bannière et avant les métadonnées :
   volontairement PAS un .card (28px de padding et l'ombre d'un panneau
   interactif, disproportionnés pour un paragraphe). Bloc léger, juste assez
   délimité pour se détacher du fond. Marge basse resserrée : la ligne de
   métadonnées la suit immédiatement.
   ⚠️ Doit rester un enfant direct de .list-main, pas de .page-head : ce dernier
   est un conteneur flex partagé avec les boutons d'action, la carte y perdait
   leur largeur et se retrouvait plus étroite que tous les blocs voisins.
   Pas de white-space:pre-line : les sauts de ligne sont déjà convertis en <br>
   par le filtre |nl2br côté template, les cumuler les doublerait. */
.list-description{
  background:var(--paper);border:1px solid var(--line);border-radius:var(--radius);
  padding:16px 18px;margin-bottom:12px;
  color:var(--ink-soft);
}

/* ---- Buttons ---- */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:8px;
  font-family:'Nunito Sans',sans-serif;font-weight:700;font-size:.95rem;
  padding:12px 22px;border-radius:4px;border:1px solid transparent;
  cursor:pointer;background:var(--primary);color:#fff;transition:.15s ease;
}
.btn:hover{background:var(--primary-dark);text-decoration:none;}
/* Action PRINCIPALE. Vert et non rouge : --berry était trop proche de
   --danger pour que l'œil distingue « action à encourager » de « action
   destructive ». Reprend les mêmes couleurs que la base .btn ci-dessus —
   la classe reste explicite dans les templates pour marquer l'intention. */
.btn-primary{background:var(--primary);}
.btn-primary:hover{background:var(--primary-dark);}
/* Conservée : --berry reste la couleur des liens, de tag-participant et des
   accents décoratifs ; son usage « bouton d'action principale » a seul migré
   vers .btn-primary. */
.btn-berry{background:var(--berry);}
.btn-berry:hover{background:var(--berry-dark);}
.btn-outline{background:var(--beige);border-color:var(--line);color:var(--ink);}
.btn-outline:hover{background:var(--beige-dark);color:var(--ink);border-color:var(--ink-soft);}
/* Conservée bien qu'aucun template ne la référence plus : tous les boutons
   « Annuler »/« Retour » sont passés en .btn-outline, qui les fait lire comme
   le second choix offert plutôt que comme du texte. Même statut que
   .btn-berry — gardée pour un besoin de bouton vraiment discret. */
.btn-ghost{background:transparent;color:var(--ink-soft);border-color:transparent;}
.btn-ghost:hover{background:var(--beige);color:var(--ink);}
/* Action destructive, style DISCRET par défaut (contour) : ces boutons
   cohabitent avec des actions courantes, un aplat rouge y attire l'œil autant
   qu'une action à encourager. Le rouge plein n'apparaît qu'au survol, quand
   l'intention est déjà là. */
.btn-danger{background:transparent;border-color:var(--danger);color:var(--danger);}
.btn-danger:hover{background:var(--danger);border-color:var(--danger);color:#fff;}
/* Rouge PLEIN, réservé au moment de confirmation finale d'une action
   destructive — c'est-à-dire une vraie page de confirmation dédiée (voir
   reservation/cancel_confirm.html.twig). Ne PAS l'utiliser sur un bouton qui
   ouvre une simple boîte confirm() JS : voir CLAUDE.md. */
.btn-danger-confirm{background:var(--danger);color:#fff;}
.btn-danger-confirm:hover{background:#B93A31;color:#fff;}
/* Bouton DÉSACTIVÉ, générique à toutes les variantes. Sert au cas « le droit
   existait et vient d'être retiré » : le bouton reste visible pour que
   l'absence ne passe pas pour un bug, mais il est inerte et son title explique
   pourquoi (voir _gifts_section.html.twig et AccessResolver::giftEditLockedByActivity).
   Le `cursor:not-allowed` est ce qui distingue à la souris un bouton désactivé
   d'un bouton simplement pâle. Déclaré APRÈS toutes les variantes pour les
   neutraliser toutes — y compris leur :hover, sans quoi .btn-danger:hover
   repeindrait en rouge plein un bouton pourtant inactif. */
.btn:disabled,.btn[disabled],.btn:disabled:hover,.btn[disabled]:hover{
  background:var(--beige);border-color:var(--line);color:var(--ink-soft);
  opacity:.65;cursor:not-allowed;
}
.btn-sm{padding:8px 16px;font-size:.85rem;}
.btn-block{width:100%;}
button.link-btn{background:none;border:none;padding:0;color:var(--berry);cursor:pointer;font:inherit;text-decoration:underline;}

/* ---- Forms ---- */
label{display:block;font-weight:700;font-size:.9rem;margin-bottom:6px;color:var(--ink);}
.field{margin-bottom:18px;}
input[type=text],input[type=email],input[type=password],input[type=number],input[type=date],input[type=url],input[type=file],textarea,select{
  width:100%;padding:11px 13px;border-radius:12px;border:1px solid var(--line);
  background:#fff;font-family:inherit;font-size:.95rem;color:var(--ink);
}
textarea{min-height:100px;resize:vertical;}
input:focus,textarea:focus,select:focus{outline:2px solid var(--gold);outline-offset:1px;}
.field-hint{font-size:.8rem;color:var(--ink-soft);margin-top:4px;}
/* ---- Champ enrichi : en-tête, aide, icône dans le champ, compteur ----
   Utilisé par le formulaire de liste ; réutilisable tel quel ailleurs. */
.field-head{display:flex;align-items:center;gap:8px;flex-wrap:wrap;}
.field-head label{margin:0;}
.field-required{color:var(--danger);font-weight:700;}
.field-help{margin:2px 0 8px;font-size:.82rem;color:var(--ink-soft);}
/* Icône DANS le champ. `position:relative` sur le conteneur, et un padding
   gauche sur l'input pour lui laisser la place — sans ce padding, le texte
   saisi passe SOUS l'icône. */
.field-input{position:relative;}
.field-input-icon{
  position:absolute;left:14px;top:50%;transform:translateY(-50%);
  display:flex;color:var(--forest-dark);pointer-events:none;
}
.field-input-icon svg{width:20px;height:20px;}
.field-input input{padding-left:44px;}
.field-counter{margin:6px 0 0;text-align:right;font-size:.78rem;color:var(--ink-soft);}
/* Un dépassement reste possible : `maxlength` ne tronque pas une valeur déjà
   en place. On le signale plutôt que de le taire — le serveur refusera. */
.field-counter--over{color:var(--danger);font-weight:700;}
/* Chevron à DROITE d'un champ, pour signaler une liste de suggestions
   (`<datalist>`). ⚠️ Purement visuel : le navigateur n'expose aucun moyen de
   styler l'indicateur natif, qui n'apparaît d'ailleurs pas partout. */
.field-input-chevron{
  position:absolute;right:14px;top:50%;transform:translateY(-50%);
  display:flex;color:var(--ink-soft);pointer-events:none;
}
.field-input-chevron svg{width:18px;height:18px;}
.field-input:has(.field-input-chevron) input{padding-right:42px;}

/* ---- Sélecteur de quantité en pas à pas ----
   Les flèches natives d'un `input[type=number]` sont minuscules et quasiment
   invisibles sur mobile. ⚠️ Le champ reste éditable au clavier : on ajoute
   deux boutons, on ne remplace pas la saisie. */
.qty-stepper{display:flex;align-items:stretch;border:1px solid var(--line);border-radius:12px;overflow:hidden;}
.qty-stepper input{
  border:none;border-radius:0;text-align:center;
  /* Les flèches natives feraient doublon avec les deux boutons. */
  -moz-appearance:textfield;
}
.qty-stepper input::-webkit-outer-spin-button,
.qty-stepper input::-webkit-inner-spin-button{-webkit-appearance:none;margin:0;}
.qty-stepper input:focus{outline-offset:-2px;}
.qty-step{
  flex:none;display:flex;align-items:center;justify-content:center;
  width:48px;border:none;background:var(--beige);color:var(--ink);cursor:pointer;
  transition:background .15s ease;
}
.qty-step:hover{background:var(--beige-dark);}
.qty-step svg{width:18px;height:18px;}
/* Quantité verrouillée : le champ est `disabled`, les boutons ne sont pas
   rendus. Le cadre garde la même silhouette pour que la mise en page ne bouge
   pas d'un formulaire à l'autre. */
.qty-stepper--locked input{background:var(--beige);}

/* ---- Image : téléversement OU lien, côte à côte ----
   ⚠️ Les deux options sur la MÊME ligne, pour que le « ou » se lise d'un coup
   d'œil. L'une sous l'autre, on croit à deux champs à remplir. */
.image-choice{display:grid;grid-template-columns:1fr 1fr;gap:16px;align-items:start;}
.dropzone{
  display:flex;align-items:center;gap:14px;
  padding:16px;border:2px dashed var(--line);border-radius:12px;
  background:var(--canvas);cursor:pointer;transition:.15s ease;
}
.dropzone:hover{border-color:var(--forest);background:var(--mint-soft);}
/* 🔴 Le vrai `input[type=file]` est MASQUÉ mais reste dans le label : c'est le
   label qui le déclenche, sans une ligne de JavaScript. Le retirer du DOM ou le
   mettre en `display:none` sur certains navigateurs le rendrait inatteignable
   au clavier. */
.dropzone input[type=file]{position:absolute;width:1px;height:1px;opacity:0;pointer-events:none;}
.dropzone-icon{
  flex:none;display:flex;align-items:center;justify-content:center;
  width:44px;height:44px;border-radius:50%;
  background:var(--mint-soft);color:var(--forest-dark);
}
.dropzone-icon svg{width:22px;height:22px;}
.dropzone-text{display:flex;flex-direction:column;gap:2px;min-width:0;}
.image-choice-url label{display:block;font-weight:700;font-size:.85rem;margin-bottom:6px;}
.gift-form-preview{max-width:140px;border-radius:12px;display:block;margin-bottom:12px;}

@media (max-width:560px){
  /* Les deux options s'empilent : à cette largeur, une zone de dépôt et un
     champ d'URL côte à côte deviennent illisibles tous les deux. */
  .image-choice{grid-template-columns:1fr;}
}
.radio-group{display:flex;flex-direction:column;gap:10px;}
.radio-option{border:1px solid var(--line);border-radius:12px;padding:12px 14px;display:flex;gap:10px;align-items:flex-start;cursor:pointer;}
.radio-option:has(input:checked){border-color:var(--forest);background:var(--mint-soft);}
.radio-option input[type=radio],.radio-option input[type=checkbox]{width:auto;flex-shrink:0;margin:2px 0 0;}
.radio-option label{flex:1;margin:0;font-weight:600;font-size:.92rem;color:var(--ink);line-height:1.4;cursor:pointer;}
.radio-option div strong{display:block;font-size:.92rem;}
.radio-option div span{font-size:.82rem;color:var(--ink-soft);}
/* Variante CARTES : deux choix côte à côte, chacun avec sa pastille d'icône et
   sa phrase d'explication. ⚠️ OPT-IN, posée par le thème de formulaire quand le
   champ déclare des icônes (voir templates/form/fields.html.twig) : sans ça,
   tous les choix développés du projet basculeraient en colonnes, y compris le
   mode de participation d'un cadeau, qu'aucune demande ne concernait.
   `auto-fit` + `minmax` : côte à côte quand la largeur le permet, empilés
   sinon — aucune media query à maintenir. */
.radio-group--cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(230px,1fr));gap:12px;}
.radio-option--card{align-items:center;padding:14px;}
.radio-icon{
  flex:none;display:flex;align-items:center;justify-content:center;
  width:38px;height:38px;border-radius:50%;
  background:var(--mint-soft);color:var(--forest-dark);
}
.radio-icon svg{width:20px;height:20px;}
/* Ton AMBRÉ pour le second choix de chaque paire. Il ne dit pas « attention » :
   il distingue deux options également valables, là où deux pastilles vertes
   se seraient confondues au premier coup d'œil. */
.radio-icon--warm{background:var(--gold-soft);color:#B87A14;}
.radio-body{display:flex;flex-direction:column;gap:2px;min-width:0;}
.radio-hint{font-size:.8rem;color:var(--ink-soft);line-height:1.4;}
.form-actions{display:flex;gap:12px;margin-top:26px;flex-wrap:wrap;}

/* Bouton « Continuer avec Google » — voir templates/_google_button.html.twig.
   ⚠️ Ces valeurs viennent des Google Sign-In Branding Guidelines, pas de notre
   charte : fond blanc, bordure #747775, texte #1F1F1F, Roboto. Ne pas les
   aligner sur --paper / --line / --ink, qui en sont proches mais différents —
   la conformité de marque prime ici sur l'homogénéité visuelle. */
.btn-google{
  display:inline-flex;align-items:center;justify-content:center;gap:10px;
  width:100%;box-sizing:border-box;
  background:#FFFFFF;border:1px solid #747775;border-radius:4px;
  padding:11px 18px;
  font-family:'Roboto','Nunito Sans',system-ui,sans-serif;
  font-weight:500;font-size:.95rem;color:#1F1F1F;
  cursor:pointer;transition:.15s ease;
}
.btn-google:hover{background:#F7F8F8;border-color:#5F6368;text-decoration:none;color:#1F1F1F;}
.btn-google:focus-visible{outline:2px solid var(--gold);outline-offset:2px;}
.btn-google-icon{display:inline-flex;align-items:center;}

/* Séparateur « ou » entre le bouton Google et le formulaire classique. */
.auth-separator{
  display:flex;align-items:center;gap:12px;
  margin:18px 0;color:var(--ink-soft);font-size:.85rem;
}
.auth-separator::before,.auth-separator::after{
  content:"";flex:1;height:1px;background:var(--line);
}

/* ---- Tag / badge (nest-tag signature element) ---- */
.tag{
  position:relative;display:inline-flex;align-items:center;gap:6px;
  font-size:.75rem;font-weight:700;letter-spacing:.03em;text-transform:uppercase;
  padding:5px 12px 5px 16px;border-radius:2px 10px 10px 2px;color:#fff;
}
.tag::before{
  content:"";position:absolute;left:6px;top:50%;transform:translateY(-50%);
  width:5px;height:5px;border-radius:50%;background:rgba(255,255,255,.85);
}
.tag-brouillon{background:var(--ink-soft);}
.tag-prete{background:var(--gold);}
.tag-archivee{background:#8A8375;}
.tag-privee{background:var(--forest);}
.tag-publique{background:var(--berry);}
.tag-owner{background:var(--forest);}
.tag-moderator{background:var(--organizer);}
.tag-participant{background:var(--berry);}
/* Liste système « Mes envies ». Volontairement NEUTRE (gris ardoise) et non
   coloré comme les statuts : ce badge ne décrit pas une étape du cycle de vie
   d'une liste, il signale qu'elle n'en a pas. Le distinguer visuellement des
   tag-brouillon/prete/archivee évite de le lire comme un quatrième statut. */
.tag-default{background:#E7E9EC;color:#3F4A57;}
.tag-white{background:#FFFFFF;color:#3F4A57;}
/* Conteneur de badges, valable PARTOUT (vue de liste, cartes du tableau de
   bord…) : le `gap` s'applique aussi entre les lignes, sans quoi les badges se
   touchent verticalement dès qu'ils passent à la ligne (cas mobile à 3 badges).
   `.list-card .tags-row` ci-dessous n'en surcharge que les marges. */
.tags-row{display:flex;flex-wrap:wrap;gap:6px;margin-top:8px;}

/* ---- Grid of list cards ---- */
.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:20px;}
.list-card{
  background:var(--paper);border:1px solid var(--line);border-radius:var(--radius);
  overflow:hidden;box-shadow:var(--shadow);display:flex;flex-direction:column;
  transition:transform .15s ease;
}
.list-card:hover{transform:translateY(-3px);}
.list-card a.cover-link{display:block;}
.list-card .cover{height:140px;background:linear-gradient(135deg,var(--forest),var(--forest-dark));background-size:cover;background-position:center;position:relative;}
/* Indicateur « je vois / je ne vois pas les réservations » : pastille blanche
   opaque à icône sombre, pour rester lisible par-dessus n'importe quelle image
   de couverture. L'ombre portée évite qu'elle se fonde dans une couverture
   claire, où un simple rond blanc disparaîtrait. */
/* Badge « Liste permanente », posé SUR la couverture, en haut à gauche.
   ⚠️ Fond blanc opaque et ombre portée, comme `.cover-visibility` juste en
   dessous : il repose sur une photo dont on ne peut rien présumer. Un badge
   translucide ou coloré s'y perdrait sur une image claire — c'est exactement le
   problème réglé sur la bannière de `wishlist/view.html.twig`, traité ici par
   l'opacité plutôt que par un double liseré, la surface étant plus petite. */
.list-card .cover .cover-badge{
  position:absolute;top:15px;left:15px;
  display:inline-flex;align-items:center;gap:6px;
  padding:5px 10px;border-radius:999px;
  background:#fff;color:var(--ink);
  font-size:.72rem;font-weight:700;letter-spacing:.02em;text-transform:uppercase;
  box-shadow:0 1px 4px rgba(30,43,34,.35);
}
.list-card .cover .cover-badge svg{width:13px;height:13px;stroke-width:2.2;}

.list-card .cover .cover-visibility{
  position:absolute;top:15px;right:15px;
  display:flex;align-items:center;justify-content:center;
  width:28px;height:28px;border-radius:50%;
  background:#fff;color:var(--ink);
  box-shadow:0 1px 4px rgba(30,43,34,.35);
}
.list-card .cover .cover-visibility svg{width:15px;height:15px;stroke-width:2.2;}
.list-card .body{padding:15px;display:flex;flex-direction:column;gap:8px;flex:1;}
/* ⚠️ La teinte porte le STATUT, et uniquement sur le bloc d'information : la
   couverture reste intacte au-dessus. Une teinte appliquée à toute la carte
   aurait débordé sur la photo, qu'on ne maîtrise pas — un voile coloré sur une
   image téléversée donne un résultat sale.

   ⚠️ Elle DOUBLE le badge de statut plutôt que de le remplacer : on identifie
   la liste en survolant la page des yeux, et on confirme en lisant. Retirer le
   badge en croyant la teinte suffisante laisserait l'information portée par la
   seule couleur — invisible à qui ne la distingue pas. */
.list-card--prete .body{background:var(--bg-prete);}
.list-card--brouillon .body{background:var(--bg-brouillon);}
.list-card--archivee .body{background:var(--bg-archivee);}

/* ⚠️ Le titre passe de --berry à --berry-dark SUR CES FONDS. En --berry il
   tombait à 2,93:1 sur le beige archivé — sous le seuil AA « grand texte »
   (3:1), alors que ce h3 de 18,4px relève en réalité du texte NORMAL, dont le
   seuil est 4,5:1.
   ⚠️ --berry-dark remonte à 3,75:1 : c'est mieux, ce n'est PAS conforme. La
   correction complète demande de changer la couleur des titres de carte
   (--forest-dark passerait à 4,62:1), ce qui touche l'identité visuelle et
   dépasse ce chantier. Défaut PRÉEXISTANT : sur le fond de page, --berry était
   déjà à 3,26:1 avant l'ajout de ces teintes. */
.list-card h3 a{color:var(--berry-dark);}
.list-card h3{margin:0;}
.list-card .meta{font-size:.82rem;color:var(--ink-soft);}
/* Hérite de .tags-row (flex + gap) ; ne surcharge que le positionnement :
   margin-top:auto pousse la rangée en bas de la carte. */
.list-card .tags-row{margin-top:auto;padding-top:10px;}

.empty-state{text-align:center;padding:60px 20px;color:var(--ink-soft);}
.empty-state h3{color:var(--ink);}

/* ---- Dashboard : titres de section collants + section archivée repliable ---- */
/* Titres de section du tableau de bord, rendus « collants » : les cartes ne
   portent plus de tag de statut (cf. dashboard/_list_card.html.twig), c'est ce
   titre qui garde le repère visible pendant le défilement d'une grille longue.
   Le fond opaque est indispensable — sans lui les cartes défileraient sous le
   texte. Chaque titre est borné par sa <section>/<details> parente, sinon celui
   d'une section resterait figé au-dessus de la suivante. */
.list-section{margin-top:20px;}
.list-section > h2,
.archived-toggle > summary{
  position:sticky;top:0;z-index:5;
  background:var(--canvas);
  margin:0 0 16px;padding:10px 0;
}
/* ---- Sections du tableau de bord, en conteneurs étiquetés ----

   ⚠️ Classe DÉDIÉE (`.status-section`) et non une extension de `.list-section` :
   cette dernière sert aussi le catalogue et les guides, où les titres sont
   `sticky` et n'ont aucune bordure. Les fusionner aurait repeint trois écrans
   sans rapport avec ce chantier.

   ⚠️ Corollaire assumé : les titres de section du tableau de bord ne sont PLUS
   collants. Une étiquette en chevauchement est `position:absolute` dans un
   parent `relative` — les deux comportements s'excluent, il faut choisir. Ici
   la bordure identifie la section en continu pendant le défilement, ce que le
   titre collant faisait avant elle. */
.status-section{
  position:relative;
  margin-top:36px;padding:28px 18px 18px;
  border:2px solid var(--line);border-radius:16px;
}
.status-section:first-of-type{margin-top:24px;}

/* ⚠️ L'étiquette chevauche le bord : remontée d'une demi-hauteur, avec un fond
   OPAQUE (`--canvas`, celui de la page) qui masque la portion de bordure
   passant dessous. Un fond translucide laisserait le trait traverser le texte.

   ⚠️ `width:max-content` : sans lui, l'étiquette absolue prend la largeur de
   son contenu au pixel près et le padding déborde du fond opaque, laissant
   réapparaître deux fragments de bordure de part et d'autre du mot. */
.status-section-tab{
  position:absolute;top:-13px;left:16px;
  width:max-content;max-width:calc(100% - 32px);
  padding:3px 12px;background:var(--canvas);
  font-size:.78rem;font-weight:800;letter-spacing:.06em;text-transform:uppercase;
  color:var(--ink-soft);
}

/* Une variante de couleur par statut — jamais trois blocs de règles recopiés.
   ⚠️ La couleur porte l'information : les sections doivent se distinguer SANS
   qu'on lise leur étiquette. D'où le pointillé sur les brouillons, qui ajoute
   une différence de FORME et pas seulement de teinte — utile à qui distingue
   mal les couleurs. */
.status-section--drafts{border-style:dashed;border-color:var(--organizer);}
.status-section--drafts .status-section-tab{color:var(--organizer);}
.status-section--ready{border-color:var(--gold);}
.status-section--ready .status-section-tab{color:#9A6612;}
.status-section--permanent{border-color:var(--forest-dark);}
.status-section--permanent .status-section-tab{color:var(--forest-dark);}
.status-section--archived{border-color:var(--line);}

/* Les archivées gardent leur repli natif : l'étiquette EST le bouton. */
.archived-toggle > summary{cursor:pointer;list-style:none;display:flex;align-items:center;gap:8px;}
.archived-toggle > summary::-webkit-details-marker{display:none;}
.archived-toggle > summary::before{content:"▸";font-size:.9em;transition:transform .15s ease;}
.archived-toggle[open] > summary::before{transform:rotate(90deg);}
/* ⚠️ Replié, le conteneur ne doit pas garder la hauteur d'une grille vide :
   le padding bas ne s'applique qu'une fois ouvert. */
.archived-toggle:not([open]){padding-bottom:6px;}
.archived-toggle .grid{margin-top:8px;}

/* ---- Carte de la liste permanente ----

   ⚠️ Deux blocs côte à côte, le texte À GAUCHE sur aplat vert, l'image à
   droite. La proportion 5/7 laisse à l'image de quoi rester lisible tout en
   donnant au texte la place d'une accroche sur deux lignes. */
.permanent-card{
  display:grid;grid-template-columns:5fr 7fr;
  min-height:210px;border-radius:14px;overflow:hidden;
  color:#fff;text-decoration:none;
}
.permanent-card:hover{text-decoration:none;}

/* Le bloc de texte : un aplat vert, sans découpe.

   ⚠️ Il déborde sous l'image (`margin-right` négatif) et passe DESSOUS
   (`z-index` inférieur). C'est le bloc image qui porte la vague et vient
   mordre sur lui — voir `.permanent-card-media`. Ne pas remettre de
   `clip-path` ici : les deux découpes se soustrairaient et laisseraient une
   bande vide entre les blocs. */
.permanent-card-body{
  position:relative;z-index:2;
  display:flex;flex-direction:column;justify-content:center;gap:10px;
  padding:26px 30px;margin-right:-44px;
  background:var(--forest-dark);
}
.permanent-card-title{font-size:1.9rem;font-weight:800;letter-spacing:-.01em;line-height:1.1;}
.permanent-card-lead{font-size:.95rem;line-height:1.5;opacity:.92;max-width:34ch;}

/* ⚠️ LA VAGUE VIT ICI. Le bloc image est découpé et posé AU-DESSUS du bloc de
   texte (`z-index:3` contre `2`), dont il mord le bord droit.

   ⚠️ Le tracé mêle POURCENTAGES et PIXELS (`1.17% 173px`) : les abscisses
   suivent la largeur, les ordonnées NON. Conséquence à connaître — si la carte
   grandit (titre sur deux lignes, accroche plus longue, traduction plus
   verbeuse), la vague reste calée à 173px du haut au lieu de s'étirer, et son
   dessin se décale vers le haut de la carte. Sans conséquence tant que la
   hauteur reste proche de `min-height`, à revoir si le contenu s'allonge. */
.permanent-card-media{
  background-size:cover;background-position:center;
  /* Repli si l'image venait à manquer : un aplat de la charte, jamais un vide
     blanc qui passerait pour une image cassée. */
  background-color:var(--forest);
    clip-path: polygon(1.17% 173px, 1.1% 141px, 1.6% 105px, 2.44% 73px, 3.08% 49px, 4.23% 24px, 5.39% 0px, 5.39% 0px, 100% 0px, 100% 100%, 1.32% 99.52%);
    z-index: 3;
}

/* ⚠️ En mobile, les deux blocs s'EMPILENT et la vague passe à l'horizontale :
   à 380px de large, une découpe verticale laissait au texte moins de la moitié
   de l'écran — le titre y tenait sur trois lignes. C'est toujours le bloc IMAGE
   qui porte le tracé, repris sur l'axe des ordonnées. */
@media (max-width:640px){
  .permanent-card{grid-template-columns:1fr;min-height:0;}
  .permanent-card-body{order:2;margin-right:0;margin-top:-28px;padding:34px 22px 24px;}
  .permanent-card-media{order:1;min-height:150px;
      clip-path: polygon(64.41% 150px, 51.52% 146px, 41.27% 143px, 28.99% 138px, 18.59% 134px, 8.4% 131px, 0.3% 129px, 0% 0px, 100% 0px, 100% 100%, 82.17% 102.19%);
      z-index: 3;}
  .permanent-card-title{font-size:1.5rem;}
  .status-section{padding:26px 12px 14px;}
}

/* ---- Gift list ---- */
.gift-category-heading{
  margin:28px 0 12px;font-size:.8rem;font-weight:800;letter-spacing:.06em;text-transform:uppercase;
  color:var(--ink-soft);padding-bottom:8px;border-bottom:1px solid var(--line);
}
.gift-category-heading:first-of-type{margin-top:16px;}
/* Carte d'un cadeau : visuel à gauche, informations à droite.

   ⚠️ `.gift-row` n'avait AUCUNE règle avant ce chantier — les cadeaux
   s'empilaient sans cadre et sans mise en colonnes, alors que `.gift-main`
   (`flex:1`) et `.gift-thumb` (`flex-shrink:0`) présupposaient déjà un parent
   flex. Ces deux règles ne servaient donc à rien ; elles servent maintenant. */
/* ⚠️ GRILLE et non flex, depuis que la rangée d'actions doit changer de place
   selon la largeur. Un flex ne sait pas « mettre cet élément sur toute la
   largeur en mobile mais dans la seconde colonne en desktop » sans réordonner
   le DOM ; une grille le fait avec deux jeux de zones nommées.

   Desktop : le visuel occupe les DEUX lignes de la première colonne, les infos
   et les boutons se partagent la seconde — les boutons démarrent donc sous le
   titre, comme la maquette. */
.gift-row{
  display:grid;
  grid-template-columns:180px 1fr;
  grid-template-areas:
    "thumb main"
    "thumb actions";
  gap:16px 20px;
  background:var(--paper);border:1px solid var(--line);border-radius:var(--radius);
  padding:18px;margin-bottom:16px;
}

/* ⚠️ Largeur FIXE et carré : c'est ce qui aligne verticalement toutes les
   colonnes de texte d'une liste. Une image qui dicterait sa propre largeur
   ferait démarrer chaque titre à un endroit différent. */
/* ⚠️ Hauteur EXPLICITE (180px, la même que `.idea-card-media` du catalogue),
   et non une hauteur déduite d'un `aspect-ratio`. La carte est un conteneur
   flex : sans hauteur fixe, le visuel s'étire pour épouser celle de la colonne
   de texte à côté, et la boîte cadeau d'un cadeau très commenté sort deux fois
   plus haute que celle du cadeau voisin. `align-self:flex-start` empêche
   l'étirement, mais poser la hauteur ne dépend d'aucun autre réglage — c'est
   plus robuste, et ça aligne les vignettes sur celles du catalogue. */
.gift-thumb-wrap{
  grid-area:thumb;align-self:start;width:180px;height:180px;
  display:flex;align-items:center;justify-content:center;
  background:var(--canvas);border-radius:12px;overflow:hidden;
}
/* ⚠️ `contain` et jamais `cover` : les photos de produit ont des proportions
   très variables, et `cover` en rognerait le sujet — même raisonnement que sur
   les cartes d'idées cadeaux du catalogue. Le fond clair comble le reste. */
.gift-thumb{max-width:100%;max-height:100%;object-fit:contain;}
.gift-thumb-placeholder{
  width:100%;height:100%;background:var(--mint-soft);
  display:flex;align-items:center;justify-content:center;font-size:1.6rem;
}

/* `min-width:0` : sans lui, un mot long (une URL collée en description) impose
   sa largeur au conteneur flex et fait déborder la carte. */
.gift-main{grid-area:main;min-width:0;}
.gift-main > h3{font-size:1.35rem;}
.gift-main h3{margin-bottom:2px;}
.gift-desc{color:var(--ink-soft);font-size:.92rem;margin:4px 0;}
.gift-meta{display:flex;gap:14px;flex-wrap:wrap;font-size:.85rem;color:var(--ink-soft);margin-top:6px;}
.gift-meta span{white-space:nowrap;}
/* « Cadeau en participation » : doit se distinguer du reste de la ligne de
   méta, volontairement grise et discrète — c'est une information de NATURE du
   cadeau, pas un détail. Pas un .tag : la ligne resterait lisible, sans le
   poids visuel d'un badge coloré à côté des badges de statut. */
.gift-meta-funding{font-weight:700;color:var(--primary);}

/* Commentaire du propriétaire : un encart, pas une ligne de méta de plus. */
.gift-comment{
  display:flex;align-items:flex-start;gap:8px;
  background:var(--beige);border-radius:10px;padding:10px 12px;
  margin:12px 0 0;font-size:.9rem;color:var(--ink);
}
.gift-comment svg{flex:none;margin-top:2px;color:var(--ink-soft);}

/* ⚠️ `margin-top:auto` : colle les boutons en bas de la carte, quelle que soit
   la longueur du texte au-dessus. Sans lui, chaque rangée flotte à la fin de
   son propre paragraphe et deux cartes voisines n'alignent plus rien. */
/* ⚠️ `align-self:end` remplace l'ancien `margin-top:auto` : le visuel occupant
   les deux lignes, la seconde s'étire quand le texte est court, et les boutons
   flotteraient en haut d'un vide. Collés en bas, ils s'alignent sur le pied du
   visuel — et sur ceux de la carte voisine. */
.gift-actions{grid-area:actions;align-self:end;display:flex;flex-wrap:wrap;gap:8px;align-items:stretch;}
/* ⚠️ « Supprimer » est enveloppé dans un `<form>` (POST + jeton CSRF) : c'est
   donc le FORMULAIRE qui est l'enfant flex, pas le bouton. Une règle posée sur
   `.btn` seul laissait ce troisième élément à sa largeur naturelle pendant que
   les deux autres s'étiraient — les boutons n'étaient jamais de largeur égale.
   D'où la mise à plat du formulaire et la règle posée sur `> *`. */
.gift-actions form{display:flex;margin:0;}
.gift-actions form > .btn{width:100%;}
.avail-bar{width:100%;height:6px;background:var(--line);border-radius:3px;overflow:hidden;margin-top:4px;}
.avail-bar span{display:block;height:100%;background:var(--gold);}
.reserved-list{font-size:.8rem;color:var(--ink-soft);margin-top:4px;}

/* ---- Share box ---- */
.share-box{display:flex;gap:10px;align-items:center;background:var(--canvas);border-radius:12px;padding:12px 14px;margin-top:8px;}
.share-box input{background:#fff;font-size:.82rem;}

/* ---- Cover image picker ---- */
.cover-field{display:flex;flex-direction:column;gap:12px;align-items:flex-start;}
/* L'attribut `hidden` (basculé en JS) doit l'emporter sur les `display:flex`
   ci-dessous : sans ça, .cover-empty / .cover-remove resteraient visibles une
   fois masqués. Spécificité 0,2,0 > les règles à une seule classe. */
.cover-field [hidden]{display:none;}
.cover-preview{
  position:relative;width:100%;height:150px;border:1px solid var(--line);border-radius:12px;
  background:var(--canvas);overflow:hidden;display:flex;align-items:center;justify-content:center;
}
.cover-preview img[data-cover-img]{width:100%;height:100%;object-fit:cover;display:block;}
.cover-empty{display:flex;flex-direction:column;align-items:center;gap:8px;color:var(--ink-soft);pointer-events:none;}
.cover-empty svg{width:40px;height:40px;stroke-width:1.6;opacity:.55;}
.cover-empty span{font-size:.9rem;font-weight:600;}
.cover-remove{
  position:absolute;top:8px;right:8px;width:30px;height:30px;border-radius:50%;border:none;
  background:rgba(30,43,34,.55);color:#fff;cursor:pointer;font-size:.8rem;line-height:1;
  display:flex;align-items:center;justify-content:center;transition:.15s ease;
}
.cover-remove:hover{background:var(--danger);}
.cover-picker{position:relative;display:flex;align-items:center;gap:12px;flex-wrap:wrap;}
.cover-formats{font-size:.78rem;color:var(--ink-soft);}
.cover-toggle{display:inline-flex;align-items:center;gap:8px;}
.cover-menu{
  position:absolute;z-index:30;top:calc(100% + 6px);left:0;width:340px;max-width:80vw;max-height:400px;
  overflow-y:auto;background:var(--paper);border:1px solid var(--line);border-radius:12px;box-shadow:var(--shadow);padding:6px;
}
.cover-option{
  display:flex;align-items:center;gap:10px;width:100%;text-align:left;background:none;border:none;
  padding:8px 10px;border-radius:8px;cursor:pointer;font:inherit;font-size:.9rem;color:var(--ink);
}
.cover-option:hover{background:var(--canvas);}
.cover-option-device{font-weight:600;border-bottom:1px solid var(--line);border-radius:0;margin-bottom:6px;padding-bottom:12px;}
/* Presets : uniquement l'image, en pleine largeur du menu. */
.cover-preset-option{display:block;padding:4px;}
.cover-preset-option + .cover-preset-option{margin-top:4px;}
.cover-preset-option .cover-thumb{
  display:block;width:100%;height:auto;aspect-ratio:8 / 3;object-fit:cover;
  border-radius:8px;background:var(--canvas);border:1px solid var(--line);
}
.cover-preset-option:hover .cover-thumb{border-color:var(--forest);}

/* ---- Misc ---- */
.divider{height:1px;background:var(--line);margin:24px 0;}
.two-col{display:grid;grid-template-columns:1fr 1fr;gap:24px;}
.center{text-align:center;}
.muted{color:var(--ink-soft);}
.small{font-size:.85rem;}
.avatar{width:40px;height:40px;border-radius:50%;object-fit:cover;background:var(--canvas);}

/* ================================================================
   Landing page components
   ================================================================ */

/* Inline line-icons (Lucide-style) : couleur pilotée par `color` du parent */
.ic{fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;display:block;}

.hero{display:grid;grid-template-columns:1fr 1fr;gap:48px;align-items:center}
.hero-badge{
  display:inline-flex;align-items:center;gap:6px;background:var(--mint-soft);color:var(--forest-dark);
  font-weight:700;font-size:.82rem;padding:7px 16px;border-radius:999px;margin-bottom:18px;
}
.hero h1{font-size:2.7rem;margin-bottom:.3em;}
.hero .accent{color:var(--forest);}
.hero-lead{font-size:1.05rem;color:var(--ink-soft);max-width:460px;}
.hero-actions{display:flex;gap:14px;flex-wrap:wrap;margin:26px 0 22px;}
.hero-actions .btn{padding:14px 26px;}
.hero-proof{display:flex;align-items:center;gap:12px;color:var(--ink-soft);font-size:.88rem;}
.avatar-stack{display:flex;}
.avatar-stack .avatar{width:34px;height:34px;border:2px solid var(--paper);margin-left:-10px;}
.avatar-stack .avatar:first-child{margin-left:0;}

.hero-visual{position:relative;}
/* ⚠️ `max-width:100%` + `height:auto` : l'illustration s'adapte à la colonne
   sans jamais déborder, et garde ses proportions. `display:block` supprime
   l'espace sous la ligne de base que laisse une image en `inline`. */
.hero-visual img{max-width:100%;height:auto;display:block;}

/* ---- Stats bar ---- */
.stats-bar{
  background:var(--paper);border:1px solid var(--line);border-radius:var(--radius-lg);
  box-shadow:var(--shadow);display:flex;margin:44px 0;overflow:hidden;
}
.stat{flex:1;display:flex;align-items:center;gap:14px;padding:26px 30px;}
.stat + .stat{border-left:1px solid var(--line);}
.stat-icon{
  width:48px;height:48px;border-radius:50%;display:flex;align-items:center;justify-content:center;
  font-size:1.3rem;flex-shrink:0;
}
.stat-icon svg{width:24px;height:24px;}
.stat-icon.mint{background:var(--mint-soft);color:var(--forest);}
.stat-icon.coral{background:var(--berry-soft);color:var(--berry);}
.stat-icon.gold{background:var(--gold-soft);color:var(--gold);}
.stat strong{display:block;font-size:1.4rem;color:var(--ink);}
.stat span{display:block;font-size:.85rem;color:var(--ink-soft);}

/* ---- Section headings ---- */
.section-head{text-align:center;max-width:560px;margin:0 auto 36px;}
.section-eyebrow{display:block;color:var(--berry);font-weight:800;font-size:.8rem;letter-spacing:.08em;text-transform:uppercase;margin-bottom:10px;}
.section-head h2{font-size:1.9rem;}

/* ---- « Explorer Surprizio » (accueil) ---- */
.explore-section{margin:44px 0;}
/* Titre encadré de deux traits, avec un picto au-dessus — repris de la
   maquette. Les traits sont des pseudo-éléments en flex : pas d'image, pas de
   balise vide, et ils s'adaptent seuls à la longueur du titre. */
.explore-head{text-align:center;margin-bottom:24px;}
.explore-head-icon{
  display:inline-flex;align-items:center;justify-content:center;
  width:40px;height:40px;border-radius:50%;background:var(--mint-soft);color:var(--forest);margin-bottom:6px;
}
.explore-head-icon svg{width:22px;height:22px;}
.explore-head h2{display:flex;align-items:center;justify-content:center;gap:16px;font-size:1.6rem;margin:0;}
.explore-head h2::before,.explore-head h2::after{
  content:"";height:1px;background:var(--line);flex:1;max-width:90px;
}

/* ⚠️ `auto-fit` + `justify-content:center` + largeur plafonnée : quand
   PUBLIC_GIFT_IDEAS_ENABLED est à false, il ne reste qu'UNE carte. Sans ces
   trois réglages, elle s'étirerait sur toute la largeur ou laisserait un vide
   asymétrique à droite. Avec, elle se centre proprement. */
.explore-grid{
  display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));
  gap:18px;justify-content:center;
}
.explore-card{
  display:flex;flex-direction:column;gap:8px;padding:20px;
  border:1px solid var(--line);border-radius:16px;color:var(--ink);transition:.15s ease;
}
.explore-card:hover{text-decoration:none;transform:translateY(-3px);border-color:var(--ink-soft);}
.explore-card-head{display:flex;align-items:center;gap:12px;}
/* La pastille perd sa marge basse ici : dans cette carte elle est À CÔTÉ du
   titre, pas au-dessus comme dans .feature-card. */
.explore-card .icon-badge{margin-bottom:0;flex:none;}
.explore-card-title{font-weight:800;font-size:1.05rem;}
.explore-card-text{font-size:.9rem;line-height:1.55;color:var(--ink-soft);flex:1;}
.explore-card-link{font-weight:700;font-size:.88rem;margin-top:4px;}

/* Fond très légèrement teinté, accordé à la pastille — toutes les teintes
   viennent de la palette existante, aucune n'est inventée pour l'occasion. */
.explore-card--mint{background:#F2FAF6;}
.explore-card--mint .explore-card-link{color:var(--forest-dark);}
.explore-card--coral{background:#FEF6F5;}
.explore-card--coral .explore-card-link{color:var(--berry-dark);}
.explore-card--slate{background:#F4F6F8;}
.explore-card--slate .explore-card-link{color:var(--organizer);}

@media (max-width:640px){
  .explore-head h2{font-size:1.35rem;gap:10px;}
  .explore-head h2::before,.explore-head h2::after{max-width:40px;}
}

/* ---- Feature grid ---- */
.feature-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:20px;align-items:stretch;}
/* Neutralise la marge générique `.card + .card` qui, dans cette grille,
   décalait les tuiles 2-4 et rendait la 1re plus haute que les autres. */
.feature-grid .card + .card{margin-top:0;}
.feature-card{text-align:left;display:flex;flex-direction:column;height:100%;}
.icon-badge{
  width:52px;height:52px;border-radius:16px;display:flex;align-items:center;justify-content:center;
  font-size:1.4rem;margin-bottom:16px;
}
.icon-badge svg{width:26px;height:26px;}
.icon-badge.mint{background:var(--mint-soft);color:var(--forest);}
.icon-badge.coral{background:var(--berry-soft);color:var(--berry);}
.icon-badge.gold{background:var(--gold-soft);color:var(--gold);}
/* Quatrième variante, pour les séquences à 4 items (grille « Pourquoi » et
   parcours « 4 étapes ») : sans elle, le 4ᵉ item rebouclait sur .mint et se
   confondait avec le 1er. Nommée d'après la teinte, pas d'après --organizer :
   le badge « Organisateur » et ces pastilles n'ont aucun lien sémantique, ils
   partagent seulement la couleur. */
.icon-badge.slate{background:var(--organizer-soft);color:var(--organizer);}
.feature-card p{font-size:.9rem;}
.feature-card .learn-more{margin-top:auto;font-weight:700;font-size:.85rem;color:var(--forest-dark);display:inline-flex;align-items:center;gap:6px;}
.feature-card .learn-more svg{width:16px;height:16px;}
.feature-card .learn-more:hover{text-decoration:none;color:var(--forest);}

/* ---- Promotion de l'extension navigateur ---- */
/* Deux variantes d'un même composant : bandeau horizontal sur l'accueil,
   carte verticale dans la colonne du tableau de bord. Markup unique, voir
   templates/_extension_promo.html.twig. */
.extension-promo{
  background:var(--mint-soft);border:1px solid var(--line);border-radius:16px;
  padding:22px 24px;margin:28px 0;
}
.extension-promo-title{font-size:1.15rem;line-height:1.3;margin:0 0 10px;}
.extension-promo-list{list-style:none;margin:0;padding:0;display:grid;gap:6px;}
/* Coche verte devant chaque puce, posée en `::before` : un caractère décoratif
   dans le HTML serait lu par les lecteurs d'écran devant chaque ligne. */
.extension-promo-list li{
  position:relative;padding-left:22px;font-size:.9rem;color:var(--ink-soft);
}
.extension-promo-list li::before{
  content:"";position:absolute;left:2px;top:.45em;width:9px;height:5px;
  border-left:2px solid var(--forest);border-bottom:2px solid var(--forest);
  transform:rotate(-45deg);
}
.extension-promo-stores{display:flex;align-items:center;gap:10px;}
/* ⚠️ Les logos de navigateur ne suivent PAS `currentColor`, contrairement au
   jeu d'icônes maison : ce sont des marques déposées, voir le partiel. */
.browser-logo{display:block;}
.extension-promo-cta{white-space:nowrap;}
.extension-promo-note{margin:0;font-size:.8rem;color:var(--ink-soft);}

/* Bandeau (accueil) : icône | texte | actions, sur une rangée. */
.extension-promo--banner{display:flex;align-items:center;gap:24px;}
.extension-promo--banner .extension-promo-body{flex:1;min-width:0;}
.extension-promo--banner .extension-promo-actions{
  display:flex;flex-direction:column;align-items:center;gap:10px;flex-shrink:0;
}

/* Carte (tableau de bord) : tout empilé, aligné à gauche. */
.extension-promo--card{
  display:flex;flex-direction:column;gap:14px;margin:0;padding:20px;
}
.extension-promo--card .extension-promo-title{font-size:1.05rem;}
.extension-promo--card .extension-promo-actions{display:grid;gap:10px;justify-items:center;}
.extension-promo--card .extension-promo-cta{width:100%;}

/* Tableau de bord : listes à gauche, colonne latérale à droite.
   ⚠️ `auto` et non une largeur fixe sur la seconde colonne : quand
   l'extension n'est pas publiée, le partiel n'émet rien, l'aside est vide et
   la colonne se réduit à zéro — la grille redevient donc une seule colonne
   sans qu'aucune condition ne soit écrite dans le gabarit. */
/* ⚠️ UNE colonne par défaut, deux seulement avec `--with-side`. L'inverse a
   été essayé — deux colonnes en permanence, la seconde en `auto` censée se
   réduire à zéro quand rien ne l'occupe — et ça ne marche pas : la largeur
   fixe de `.dashboard-side` et la gouttière de la grille laissaient les
   listes tassées à gauche d'un vide, extension dépubliée. Le gabarit ne pose
   le modificateur que s'il a réellement un panneau à placer. */
.dashboard-layout{display:grid;grid-template-columns:1fr;gap:24px;align-items:start;}
.dashboard-layout--with-side{grid-template-columns:minmax(0,1fr) auto;}
.dashboard-main{min-width:0;}
/* ⚠️ Le décalage aligne le haut de la carte de promotion sur celui de la
   PREMIÈRE CARTE de la colonne de gauche, pas sur le bord de sa section : la
   section porte une étiquette en chevauchement, donc un rembourrage avant son
   contenu. L'arithmétique est laissée en clair — 2px de bordure + 28px de
   `padding-top` de `.status-section`.
   ⚠️ La `margin-top` de la section n'entre PAS dans le calcul : elle décale
   la section ET la colonne latérale de la même façon, les deux colonnes
   étant des éléments de la même grille. L'y ajouter faisait descendre la
   carte 24px trop bas — erreur commise puis corrigée à l'œil, faute de
   pouvoir mesurer autrement.
   🔴 COUPLAGE : si la bordure ou le rembourrage de `.status-section` change,
   ce décalage doit suivre, sinon les colonnes se désalignent sans que rien
   ne casse. */
.dashboard-side{width:280px;position:sticky;top:16px;margin-top:24px;}

/* ---- Barre de retour fixée ---- */
/* ⚠️ `z-index:20` : au-dessus des titres de section, qui sont eux-mêmes
   `sticky top:0 z-index:5` (voir .list-section > h2). Sans ça, la barre
   passerait DERRIÈRE eux au défilement. En dessous du menu déroulant du
   sélecteur de langue (z-index:30) et des modales (1000), qui doivent rester
   au-dessus de tout. */
.sticky-back{
  position:sticky;top:0;z-index:20;
  /* Fond OPAQUE : le contenu défile dessous, un fond translucide rendrait le
     libellé illisible dès qu'une image ou un titre passe derrière. */
  background:var(--beige);border-bottom:1px solid var(--line);
}
.sticky-back a{
  /* Cliquable sur toute la largeur, pas seulement sur le texte. */
  display:flex;align-items:center;gap:10px;
  padding:11px 24px;font-weight:700;font-size:.92rem;color:var(--ink);
}
.sticky-back a:hover{text-decoration:none;color:var(--forest-dark);}
.sticky-back-arrow{font-size:1.1rem;line-height:1;}

/* ⚠️ Décale les titres de section collants pour qu'ils s'arrêtent SOUS la
   barre au lieu de disparaître dessous. Le combinateur `~` fait que la règle
   ne s'applique QUE sur les pages qui incluent réellement la barre : rien à
   activer à la main, rien à désactiver ailleurs. */
.sticky-back ~ main .list-section > h2,
.sticky-back ~ main .archived-toggle > summary{top:45px;}

/* ---- Catalogue d'idées cadeaux ---- */
/* Grille de catégories. Tuiles carrées, teinte douce tournant sur quatre
   valeurs — toutes tirées de la palette existante (--berry-soft, --gold-soft,
   --beige-dark et un vert dérivé), jamais de couleur inventée pour l'occasion.
   La rotation se fait sur l'index d'affichage : stable d'une visite à l'autre,
   et rien à stocker en base. */
.category-grid{
  display:grid;grid-template-columns:repeat(auto-fill,minmax(140px,1fr));gap:14px;margin-bottom:8px;
}
.cat-tile{
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;
  padding:20px 14px;background:#fff;border:1px solid var(--line);border-radius:14px;
  text-align:center;color:var(--ink);transition:.15s ease;
}
.cat-tile:hover{text-decoration:none;transform:translateY(-2px);border-color:var(--ink-soft);}
.cat-tile-media{
  display:flex;align-items:center;justify-content:center;
  width:64px;height:64px;border-radius:16px;flex:none;overflow:hidden;
}
/* Une vignette téléversée n'est pas forcément carrée : `cover` recadre au
   centre plutôt que de déformer. */
.cat-tile-media img{width:100%;height:100%;object-fit:cover;}

/* ⚠️ Vignette SVG inlinée. C'est TOUT l'intérêt de l'inliner : un SVG chargé en
   `<img>` serait hors d'atteinte d'ici, et garderait la taille et la couleur de
   son fichier d'origine. La couleur vient de `currentColor`, posé sur la
   racine du SVG par SvgSanitizer — et non par une règle `fill:` ici, qui
   l'emporterait sur `fill="none"` et transformerait une icône « contour » en
   aplat plein. */
.tile-svg{display:flex;align-items:center;justify-content:center;width:56%;height:56%;}
.tile-svg svg{width:100%;height:100%;display:block;}
.guide-row .tile-svg,.cat-tile .tile-svg{width:60%;height:60%;}
.cat-tile-media svg{width:28px;height:28px;}
.cat-tile-title{font-weight:700;font-size:.92rem;line-height:1.3;}

.cat-tile--0 .cat-tile-media{background:var(--berry-soft);color:var(--berry-dark);}
.cat-tile--1 .cat-tile-media{background:var(--gold-soft);color:#9A6612;}
.cat-tile--2 .cat-tile-media{background:#E4F3EC;color:var(--forest-dark);}
.cat-tile--3 .cat-tile-media{background:var(--beige-dark);color:var(--ink-soft);}

/* Libellé réservé aux lecteurs d'écran.
   ⚠️ Ni `display:none` ni `visibility:hidden` : les deux retirent l'élément de
   l'arbre d'accessibilité, donc du texte qu'on cherche précisément à lui
   donner. On le sort de l'écran en gardant une boîte de 1px, seule technique
   qui le laisse lisible par une synthèse vocale. */
.sr-only{
  position:absolute;width:1px;height:1px;padding:0;margin:-1px;
  overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0;
}

/* Indicateur de chargement du panneau de résultats.

   ⚠️ On se greffe sur `aria-busy`, que le script pose déjà pour les lecteurs
   d'écran : un seul état, deux publics servis. Ajouter une classe `.is-loading`
   en parallèle aurait créé deux sources de vérité pour la même chose.

   ⚠️ Le `transition-delay` ne s'applique QUE dans le sens « devient occupé ».
   Une réponse rapide — le cas normal en local comme sur un petit catalogue —
   ne produit donc aucun clignotement : la requête est finie avant que
   l'atténuation ne commence. Au retour, pas de délai : le contenu redevient net
   immédiatement. Sans cette asymétrie, chaque frappe ferait palpiter la page.

   ⚠️ Une atténuation plutôt qu'un remplacement du panneau par un « chargement
   en cours » : les résultats précédents restent lisibles pendant la requête, ce
   qui est exactement ce qu'on veut en tapant — on affine, on ne repart pas de
   zéro. */
#results-panel{transition:opacity .12s ease;}
#results-panel[aria-busy="true"]{opacity:.45;transition-delay:.18s;}

/* Champ de recherche du catalogue. La saisie prend toute la place restante,
   le bouton garde la sienne — d'où `flex:1` sur l'un et rien sur l'autre. */
.idea-search{margin:0 0 28px;}
.idea-search input{
  /* Plus de bouton à côté : le champ occupe toute la largeur. */
  width:100%;padding:11px 14px;font:inherit;color:var(--ink);
  background:#fff;border:1px solid var(--line);border-radius:10px;
}
.idea-search input:focus-visible{outline:2px solid var(--gold);outline-offset:1px;}

/* Cartes d'idées cadeaux — composant PARTAGÉ par l'index, la page d'une
   rubrique et la sélection d'un guide. Le markup vit dans
   gift_idea/_idea_card.html.twig ; ces règles vont par paires avec lui.
   ⚠️ Pas de note ni d'étoiles : aucun champ de notation n'existe sur
   PublicGiftIdea (voir CLAUDE.md). */
.idea-grid{
  display:grid;grid-template-columns:repeat(auto-fill,minmax(230px,1fr));gap:16px;
  /* `stretch` : toutes les cartes d'une rangée prennent la hauteur de la plus
     haute. C'est la condition pour que `margin-top:auto` puisse ensuite
     plaquer les boutons en bas de CHACUNE. */
  align-items:stretch;
}
.idea-card{
  display:flex;flex-direction:column;padding:14px;
  background:#fff;border:1px solid var(--line);border-radius:14px;
  color:var(--ink);transition:.15s ease;height:100%;
}
.idea-card:hover{border-color:var(--ink-soft);transform:translateY(-2px);}

/* ⚠️ Hauteur FIXE et `contain`, jamais `cover` : les photos produit ont des
   proportions très variables et `cover` en rognerait le sujet. Le fond clair
   comble ce que l'image ne remplit pas. */
.idea-card-media{
  display:flex;align-items:center;justify-content:center;
  height:180px;flex:none;margin-bottom:12px;
  /* ⚠️ Fond TRANSPARENT, pas le beige de la charte : les photos produit sont
     très majoritairement détourées sur blanc, et un fond crème derrière faisait
     ressortir un rectangle blanc au milieu de la carte. Transparent plutôt que
     `#fff` en dur : la zone suit alors le fond de la carte, quel qu'il devienne. */
  background:transparent;border-radius:10px;overflow:hidden;
}
.idea-card-media img{max-width:100%;max-height:100%;object-fit:contain;}
.idea-card-media svg{width:32px;height:32px;color:var(--ink-soft);}

/* `flex:1` : le corps absorbe l'espace disponible, ce qui pousse la rangée de
   boutons vers le bas quelle que soit la longueur du texte. */
.idea-card-body{display:flex;flex-direction:column;gap:4px;flex:1;}

/* ⚠️ La troncature à 2 lignes a besoin des QUATRE propriétés ensemble :
   `-webkit-line-clamp` seul ne fait rien sans `display:-webkit-box`,
   `-webkit-box-orient` et `overflow:hidden`. `line-clamp` (sans préfixe) est
   ajouté pour les moteurs qui l'implémentent désormais sans. */
.idea-card-title,.idea-card-desc{
  display:-webkit-box;-webkit-box-orient:vertical;overflow:hidden;
  -webkit-line-clamp:2;line-clamp:2;
}
.idea-card-title{margin:0;font-size:.98rem;line-height:1.35;font-weight:800;}
.idea-card-rubrique{margin:0;font-size:.8rem;color:var(--ink-soft);}
.idea-card-desc{margin:4px 0 0;font-size:.88rem;line-height:1.5;color:var(--ink-soft);}

/* Lien « en savoir plus » : un bouton pour l'accessibilité et le clavier, mais
   présenté comme un lien — c'est une action de lecture, pas une action sur le
   produit, et il ne doit pas concurrencer les deux boutons du bas. */
.idea-card-more{
  align-self:flex-start;margin-top:2px;padding:0;border:none;background:none;
  color:var(--berry-dark);font:inherit;font-size:.85rem;font-weight:700;cursor:pointer;
}
.idea-card-more:hover{text-decoration:underline;}

.idea-card-price{margin:10px 0 0;font-weight:800;font-size:1.05rem;color:var(--forest-dark);}

/* ⚠️ `margin-top:auto` : c'est LUI qui aligne les boutons de toutes les cartes
   sur la même ligne horizontale, qu'une carte porte ou non un lien « en savoir
   plus ». Sans lui, chaque rangée flotterait à la fin de son propre texte. */
.idea-card-actions{
  display:flex;flex-direction:column;gap:8px;margin-top:auto;padding-top:14px;
}
.idea-card-actions .btn{width:100%;}

/* Modale de description : réutilise .modal-overlay / .modal-panel du composant
   générique, on n'ajoute ici que la mise en forme de son contenu. */
#idea-modal .modal-panel img{
  display:block;max-width:100%;max-height:260px;margin:0 auto 16px;object-fit:contain;
}
#idea-modal .modal-panel p{white-space:pre-line;line-height:1.6;}

/* Guides d'achat. Une ligne par guide : vignette teintée, titre + accroche,
   flèche. Reprend la palette et les teintes de .cat-tile--n ci-dessus plutôt
   que d'en introduire de nouvelles. */
/* ⚠️ DEUX colonnes en bureau, pas trois, et le minimum de 420px le décide
   seul : le conteneur fait 1120px, donc trois cartes n'y tiennent pas
   (3×420 > 1120) mais deux oui. Pas de media query — la grille retombe
   d'elle-même à une colonne dès que la largeur ne suffit plus, ce qui couvre
   aussi bien le mobile que la fenêtre à moitié réduite.

   Trois colonnes donneraient ~360px par carte : la carte étant HORIZONTALE
   (icône, puis titre et accroche à côté), l'accroche passerait sur deux ou
   trois lignes et l'icône se retrouverait à l'étroit. C'est la forme de la
   carte qui impose le nombre de colonnes, pas l'inverse. */
/* ⚠️ `min(420px,100%)` et NON `420px` seul. Le minimum d'un `minmax()` est une
   valeur DURE : `auto-fit` sait retomber à une colonne, il ne sait pas la
   rétrécir en dessous de ce minimum. Sur un téléphone de 412px, `.wrap` en
   retire 48px de marges — la colonne réclamait 420px pour 364px disponibles et
   dépassait de la fenêtre, emportant la flèche hors de l'écran. Le `min()`
   plafonne le minimum à la largeur réellement offerte.

   ⚠️ Piège à reconnaître : tout `minmax()` dont le minimum dépasse la largeur
   utile d'un mobile produit ce défilement horizontal. Les autres grilles du
   projet restent en dessous (140 à 260px), d'où ce correctif ici seulement. */
.guide-list{
  display:grid;grid-template-columns:repeat(auto-fit,minmax(min(420px,100%),1fr));
  gap:12px;align-items:stretch;
}
.guide-row{
  /* `height:100%` : sans lui, deux cartes voisines dont les accroches
     n'occupent pas le même nombre de lignes n'auraient pas la même hauteur. */
  display:flex;align-items:center;gap:16px;padding:16px;height:100%;
  background:#fff;border:1px solid var(--line);border-radius:14px;color:var(--ink);transition:.15s ease;
}
.guide-row:hover{text-decoration:none;transform:translateY(-2px);border-color:var(--ink-soft);}
.guide-row-body{display:flex;flex-direction:column;gap:4px;flex:1;min-width:0;}
.guide-row-title{font-weight:800;font-size:1rem;}
.guide-row-teaser{font-size:.9rem;line-height:1.5;color:var(--ink-soft);}
.guide-row-arrow{flex:none;color:var(--forest-dark);display:flex;}
.guide-row-arrow svg{width:20px;height:20px;}

/* Corps d'un guide. ⚠️ Seules ces balises peuvent apparaître : le contenu est
   filtré par RichTextSanitizer, qui n'en laisse pas passer d'autres. */
.guide-content{line-height:1.7;}
/* ⚠️ `div` autant que `p` : Trix, l'éditeur du back-office, enveloppe chaque
   paragraphe dans un <div>. Sans cette règle, tout le texte se collait en un
   seul bloc sans respiration. */
.guide-content div{margin:0 0 14px;}
.guide-content div:last-child{margin-bottom:0;}
/* ⚠️ Le bouton « titre » de Trix ne produit qu'un h1. La page en a déjà un
   (le titre du guide) : celui-ci est donc ramené visuellement au rang d'un
   sous-titre, faute de pouvoir changer la balise au nettoyage. */
.guide-content h1{font-size:1.2rem;margin:24px 0 10px;}
.guide-content h2{font-size:1.2rem;margin:24px 0 10px;}
.guide-content h3{font-size:1.05rem;margin:20px 0 8px;}
.guide-content p{margin:0 0 14px;}
.guide-content ul,.guide-content ol{margin:0 0 14px;padding-left:22px;}
.guide-content li{margin-bottom:6px;}
.guide-content blockquote{
  margin:0 0 14px;padding:12px 16px;background:var(--beige);
  border-left:3px solid var(--line);border-radius:8px;color:var(--ink-soft);
}
.guide-content hr{border:none;border-top:1px solid var(--line);margin:24px 0;}
.guide-content pre{
  margin:0 0 14px;padding:12px 16px;background:var(--beige);border-radius:8px;
  overflow-x:auto;font-size:.88rem;
}

/* ---- FAQ ---- */
/* Renvoi vers /faq depuis l'accueil. Reprend l'aspect de .learn-more des
   cartes, qui est scopée à .feature-card et ne s'appliquait donc pas ici. */
.faq-teaser{margin:20px 0 0;text-align:center;}
.faq-teaser .learn-more{font-weight:700;font-size:.9rem;color:var(--forest-dark);display:inline-flex;align-items:center;gap:6px;}
.faq-teaser .learn-more svg{width:16px;height:16px;}
.faq-teaser .learn-more:hover{text-decoration:none;color:var(--forest);}

.faq-section{margin-bottom:32px;}
.faq-section > h2{font-size:1.15rem;margin:0 0 12px;padding-bottom:8px;border-bottom:1px solid var(--line);}
/* Accordéon 100% natif : <details>/<summary>, aucune ligne de JS. Le navigateur
   gère l'ouverture, le clavier et l'exposition aux lecteurs d'écran. */
.faq-item{background:#fff;border:1px solid var(--line);border-radius:4px;margin-bottom:8px;}
.faq-item > summary{
  cursor:pointer;padding:14px 16px;font-weight:700;font-size:.95rem;color:var(--ink);
  display:flex;align-items:center;justify-content:space-between;gap:12px;list-style:none;
}
/* Le marqueur par défaut diffère d'un navigateur à l'autre (triangle, flèche,
   position). On le retire des deux façons nécessaires — ::-webkit-details-marker
   pour les moteurs WebKit, `list-style:none` ci-dessus pour les autres — et on
   pose notre propre chevron, identique partout. */
.faq-item > summary::-webkit-details-marker{display:none;}
.faq-item > summary::after{
  content:"";flex:none;width:9px;height:9px;
  border-right:2px solid var(--ink-soft);border-bottom:2px solid var(--ink-soft);
  transform:rotate(45deg);transform-origin:center;transition:transform .15s ease;
}
.faq-item[open] > summary::after{transform:rotate(-135deg);}
.faq-item > summary:hover{background:var(--beige);}
/* ⚠️ Le focus doit rester visible : c'est le seul repère au clavier, et
   l'accordéon n'est utilisable que comme ça sans souris. */
.faq-item > summary:focus-visible{outline:2px solid var(--gold);outline-offset:-2px;}
.faq-item > p{margin:0;padding:0 16px 16px;color:var(--ink-soft);font-size:.95rem;line-height:1.6;}
.faq-footnote{margin-top:24px;}

/* ---- Steps ---- */
.steps{display:grid;grid-template-columns:repeat(4,1fr);gap:0;margin:44px 0;position:relative;}
.step{text-align:center;padding:0 16px;position:relative;}
.step-connector{position:absolute;top:36px;left:calc(50% + 50px);right:calc(-50% + 50px);height:0;border-top:2px dashed var(--line);z-index:0;}
.step:last-child .step-connector{display:none;}
.step-icon{
  position:relative;width:72px;height:72px;border-radius:50%;margin:0 auto 16px;
  display:flex;align-items:center;justify-content:center;z-index:1;
}
.step-icon svg{width:30px;height:30px;}
.step-icon.mint{background:var(--mint-soft);color:var(--forest);}
.step-icon.coral{background:var(--berry-soft);color:var(--berry);}
.step-icon.gold{background:var(--gold-soft);color:var(--gold);}
.step-icon.slate{background:var(--organizer-soft);color:var(--organizer);}
.step-badge{
  position:absolute;top:-2px;right:2px;width:24px;height:24px;border-radius:50%;
  color:#fff;font-size:.78rem;font-weight:800;display:flex;align-items:center;justify-content:center;
  border:3px solid var(--canvas);
}
.step-badge.mint{background:var(--forest);}
.step-badge.coral{background:var(--berry);}
.step-badge.gold{background:var(--gold);}
.step-badge.slate{background:var(--organizer);}
.step h3{font-size:1.02rem;}
.step p{font-size:.88rem;color:var(--ink-soft);}

/* ---- CTA banner ---- */
.cta-banner{
  background:linear-gradient(120deg, var(--mint-soft), var(--gold-soft));
  border-radius:var(--radius-lg);padding:36px 44px;margin:20px 0 8px;
  display:flex;align-items:center;justify-content:space-between;gap:24px;flex-wrap:wrap;
}
.cta-inner{display:flex;align-items:center;gap:22px;}
.cta-gift{width:84px;height:84px;flex-shrink:0;}
.cta-gift svg{width:100%;height:100%;display:block;}
.cta-banner h2{font-size:1.5rem;margin-bottom:.2em;}
.cta-banner p{margin:0;color:var(--ink-soft);}

@media (max-width:900px){
  .hero{grid-template-columns:1fr;}
  /* ⚠️ Le CONTENEUR est masqué, pas seulement l'image : `display:none` sur la
     seule `<img>` laisserait une ligne de grille vide et son `gap` de 48px —
     un blanc résiduel sous le texte, sans rien dedans. La colonne de gauche
     occupe ainsi naturellement toute la largeur.
     ⚠️ L'ancien `order:-1` disparaît avec elle : il remontait la carte de démo
     au-dessus du texte, ce qui n'a plus d'objet. */
  .hero-visual{display:none;}
  .feature-grid{grid-template-columns:repeat(2,1fr);}
  .steps{grid-template-columns:repeat(2,1fr);row-gap:32px;}
  .step-connector{display:none;}
  .stats-bar{flex-direction:column;}
  .stat + .stat{border-left:none;border-top:1px solid var(--line);}
  .footer-top{grid-template-columns:1fr 1fr;}
}

@media (max-width:760px){
  .two-col{grid-template-columns:1fr;}
  /* ---- Header mobile : MÊME première ligne dans les deux états ----
     marque à gauche, bouton d'action + sélecteur de langue à droite. Le
     header connecté ajoute simplement une SECONDE ligne pour « Mes listes »
     et « Profil ». Cette symétrie est voulue : le repère visuel ne bouge pas
     entre visiteur et connecté.

     ⚠️ Ces réductions ne sont PAS cosmétiques — sans elles la ligne déborde
     sur un téléphone étroit (~360px). Ne pas les retirer sans remesurer. */
  .topbar-inner{padding:10px 16px;gap:8px;}
  .brand{font-size:1.05rem;gap:8px;}
  .brand-badge{width:36px;height:36px;border-radius:10px;}
  .brand-mark{width:28px;height:28px;}
  .nav{gap:8px;flex-wrap:nowrap;}
  .nav-login{padding:7px 12px !important;}

  /* Header CONNECTÉ : deux lignes.
     ⚠️ `display:contents` sur la nav — ses enfants deviennent des items flex
     de `.topbar-inner`, ce qui permet de les répartir sur deux lignes SANS
     sortir les liens du `<nav>` (ils doivent rester dans le repère de
     navigation). La nav garde donc son rôle sémantique tout en cessant
     d'être une boîte de mise en page. */
  .topbar-authenticated .topbar-inner{flex-wrap:wrap;}
  .topbar-authenticated .nav{display:contents;}
  /* Pousse « Déconnexion » + le sélecteur contre le bord droit, la marque
     restant à gauche. Une marge auto absorbe l'espace libre AVANT que
     `justify-content:space-between` n'ait quelque chose à distribuer — sans
     elle, les trois éléments de la ligne 1 s'écarteraient également et le
     bouton flotterait au milieu. */
  .topbar-authenticated .nav-login{margin-left:auto;}
  /* `flex-basis:100%` force le passage à la ligne suivante ; `order:1` la
     place APRÈS le bouton et le sélecteur, malgré sa position antérieure
     dans le DOM (voulue : ces liens restent les premiers pour la navigation
     au clavier et les lecteurs d'écran). */
  .topbar-authenticated .nav-links{display:flex;gap:14px;order:1;flex-basis:100%;}
  /* ⚠️ AUCUNE surcharge de `.lang-menu`, dans AUCUN des deux états, et c'est
     délibéré. Une règle `right:auto;left:0` a existé ici deux fois, chaque
     fois rendue nécessaire par un `flex-direction:column` qui collait la
     pastille au bord GAUCHE — un menu ancré à droite y débordait hors de
     l'écran. Les deux headers gardant maintenant leur pastille à DROITE de
     la première ligne, le `right:0` du desktop est le bon ancrage partout.
     La réintroduire recréerait le défaut en miroir. */
  .page-head{flex-direction:column;align-items:flex-start;}
  .gift-actions{align-items:flex-start;width:100%;}
  /* Boutons pleine largeur en mobile. `width:100%` sur le conteneur est
     indispensable : .page-head passe en colonne avec align-items:flex-start
     juste au-dessus, sans quoi .form-actions se réduirait à son contenu.
     `flex:1 1 auto` (plutôt que width:100% sur chaque bouton) laisse deux
     boutons courts se partager une même ligne, et étire tout seul celui qui
     se retrouve seul sur la sienne. */
  .form-actions{width:100%;}
  .form-actions .btn{flex:1 1 auto;}
  /* Appels à l'action du hero, pleine largeur et empilés. ⚠️ `column` et non
     le `flex:1 1 auto` de .form-actions juste au-dessus : ces deux boutons-là
     ne doivent JAMAIS se partager une ligne. Ce sont les deux entrées
     principales du site (créer un compte / se connecter), pas deux boutons de
     formulaire — côte à côte sur un écran de téléphone, ils tomberaient à une
     cinquantaine de pixels chacun. Le libellé reste centré grâce au
     `justify-content:center` de .btn. */
  .hero-actions{flex-direction:column;}
  /* 🔴 Promotion de l'extension masquée en mobile — mais SEULEMENT le bandeau
     de l'accueil et la carte du tableau de bord (celle-ci via
     `.dashboard-side` juste en dessous). Ce sont des blocs PROMOTIONNELS
     posés devant quelqu'un qui ne demandait rien : sur un téléphone, ils
     invitent à faire quelque chose d'impossible sur l'appareil qu'on a en
     main.
     ⚠️ La carte du PROFIL, elle, reste visible : on y arrive volontairement,
     pour gérer ses jetons d'accès — qui ne servent qu'à l'extension. Y masquer
     l'endroit où la récupérer laisserait la question sans réponse, sur le seul
     écran où elle se pose vraiment. On peut très bien préparer depuis son
     téléphone une extension qu'on installera sur son ordinateur.
     ⚠️ `display:none` et non une détection serveur : c'est une décision
     d'affichage, et un test d'agent utilisateur se tromperait au premier
     navigateur inhabituel.
     ⚠️ Ne concerne QUE ces blocs : la question de FAQ correspondante reste du
     texte normal, lisible à toutes les tailles d'écran. */
  .extension-promo--banner{display:none;}
  /* La colonne latérale disparaît avec elle : la grille repasse à une colonne
     plutôt que de réserver une gouttière pour un conteneur vide. */
  .dashboard-layout--with-side{grid-template-columns:1fr;}
  .dashboard-side{display:none;}
  .feature-grid{grid-template-columns:1fr;}
  .steps{grid-template-columns:1fr;}
  .footer-top{grid-template-columns:1fr;}
}

/* ---- Vue liste en 2 colonnes : cadeaux (grande) + config/discussion (petite) ---- */
/* ---- Bannière de couverture d'une liste (wishlist/view.html.twig) ---- */
/* Pleine largeur : émise par le bloc Twig `hero` de base.html.twig, donc HORS
   de <main class="wrap"> qui est plafonné à 1120px. N'existe que si la liste a
   une image — sans image aucun élément n'est produit, donc aucune hauteur
   réservée et aucune régression sur l'ancien rendu.
   `background-size:cover` plutôt qu'un <img object-fit:cover> : cadrage
   identique, et cohérent avec .cover (tuiles du tableau de bord), qui pose
   déjà l'image de liste en fond. */
.list-hero{position:relative;height:240px;background-size:cover;background-position:center;}
/* Dégradé NON décoratif : le titre est blanc et l'image est choisie par
   l'utilisateur — claire, sombre ou saturée, on ne peut rien en présumer. Le
   dégradé garantit un fond sombre sous le texte quelle que soit l'image ; le
   text-shadow est la seconde ligne de défense.
   PALIER plutôt qu'une rampe linéaire jusqu'en bas : la rampe atteint son
   maximum à 75%, donc AVANT le titre, qui repose ainsi sur un fond d'opacité
   constante. Une rampe simple (transparent -> .65) laissait le HAUT du titre
   sur un voile deux fois plus clair que son bas — 3,25:1 sur image blanche en
   mobile, tout juste au seuil AA du grand texte. Ici : 6,13:1 dans ce même
   pire cas, et le sommet de l'image est même PLUS dégagé (transparent
   jusqu'à 25% au lieu de 0%). */
.list-hero::after{content:'';position:absolute;inset:0;background:linear-gradient(to bottom,transparent 25%,rgba(0,0,0,.72) 75%);}
/* z-index indispensable : sans lui le ::after, postérieur dans l'ordre de
   peinture, recouvrirait le titre. Padding réécrit en entier (et non un simple
   padding-bottom) pour ne pas dépendre de l'ordre des règles avec .wrap. */
.list-hero-inner{
  position:relative;z-index:1;height:100%;
  display:flex;flex-direction:column;justify-content:space-between;
  padding:18px 24px 22px;
  /* `gap` et non une marge : avec justify-content:space-between il agit comme
     un PLANCHER. Tant que la bannière a de la hauteur en trop (desktop), c'est
     space-between qui écarte les blocs et le gap ne se voit pas ; dès que la
     hauteur devient juste (mobile, badges sur deux lignes), il garantit que le
     titre ne vienne pas se coller sous les badges. */
  gap:14px;
}
/* Statut à gauche, rôle(s) à droite : deux natures d'information opposées dans
   l'espace (la liste vs la relation du visiteur à la liste). Le libellé
   « Vous êtes : » a disparu avec ce placement — la position le dit. */
.list-hero-badges{display:flex;justify-content:space-between;align-items:flex-start;gap:12px;flex-wrap:wrap;}
.list-hero-roles{display:flex;gap:6px;flex-wrap:wrap;justify-content:flex-end;}
/* Colonne droite de la bannière : les badges de rôle et le bouton « Modifier ».
   `flex-wrap` fait tout le travail responsive — côte à côte tant que la largeur
   le permet, bouton sous les badges sinon, et dans les deux cas à l'intérieur de
   la bannière. Pas de media query. */
.list-hero-actions{display:flex;flex-wrap:wrap;justify-content:flex-end;align-items:center;gap:8px 10px;}
/* Les trois actions de pilotage (Personnaliser / Gérer / Partager). Groupe
   propre plutôt que trois enfants directs de .list-hero-actions : elles doivent
   passer à la ligne ENSEMBLE sous les badges de rôle, jamais se disperser de
   part et d'autre d'eux. */
.list-hero-tools{display:flex;flex-wrap:wrap;gap:8px;}
/* Le bouton « Partager » porte ses deux états (repos / « Copié ! ») en enfants,
   ce qui interpose un <span> entre .btn et son icône : sans ces trois
   propriétés, l'icône et le libellé perdent l'alignement et l'écart que .btn
   applique à ses propres enfants. */
.list-hero-tool-state{display:inline-flex;align-items:center;gap:8px;}
/* Les .tag sont conçus pour un fond CLAIR ; ici ils reposent sur une photo
   quelconque ou sur le vert du fond de secours. On ne touche PAS à leurs
   couleurs de fond (elles doivent rester celles du tableau de bord) : on ajoute
   seulement un double liseré, chacun couvrant un cas.
   - anneau blanc translucide : détache le badge d'un fond SOMBRE. Indispensable,
     tag-owner (--forest) sur le fond de secours vert atteint 1,00:1 — la même
     couleur exactement, le badge disparaîtrait purement et simplement.
   - ombre portée noire : détache le badge d'un fond CLAIR, où l'anneau blanc
     ne peut rien. */
.list-hero .tag{box-shadow:0 0 0 1px rgba(255,255,255,.7), 0 2px 6px rgba(0,0,0,.35);}
/* Même problème que les .tag pour le bouton de la bannière, mais à moitié
   seulement : son fond --beige est déjà très clair, il se détache tout seul
   d'une photo sombre ou du fond de secours vert — inutile d'y ajouter l'anneau
   blanc. Il ne lui manque que l'ombre portée, pour ne pas se fondre dans une
   photo claire (--beige sur du blanc, c'est 1,08:1). */
.list-hero .btn{box-shadow:0 2px 6px rgba(0,0,0,.35);}
.list-hero h1{color:#fff;margin:0;text-shadow:0 2px 6px rgba(0,0,0,.45);}
/* Fond de secours quand la liste n'a pas d'image : la bannière garde exactement
   les mêmes dimensions et la même disposition, seul le fond change. Pas d'image
   générique — l'image de couverture ne doit jamais devenir obligatoire.
   Moitié SOMBRE de la palette (--forest-dark -> --primary-dark) plutôt que
   --forest-dark -> --forest : sur la variante claire, l'anneau des badges ne
   se détachait qu'à 2,3:1 du fond, contre 3,9:1 ici (seuil de 3:1 des
   contrastes non textuels). Le titre blanc y gagne aussi. */
.list-hero-fallback{background-image:linear-gradient(135deg,var(--forest-dark),var(--primary-dark));}
/* Surcharges responsives déclarées ICI, après la règle de base : les media
   queries n'ajoutent aucune spécificité, une @media placée plus haut dans le
   fichier serait donc écrasée par le .list-hero{height:240px} ci-dessus.
   Mêmes points de rupture que le reste de la feuille (900 / 760px). */
@media (max-width:900px){
  /* min-height, même raison qu'au palier suivant : les badges peuvent déjà se
     replier à cette largeur, la bannière doit pouvoir suivre. Elle ne descend
     jamais sous l'ancienne hauteur fixe. */
  .list-hero{height:auto;min-height:200px;}
}
@media (max-width:760px){
  /* min-height et non height : les deux badges de rôle d'un propriétaire
     passent à la ligne sur un écran étroit, la bannière doit pouvoir grandir
     plutôt que de rogner son contenu. */
  .list-hero{height:auto;min-height:160px;}
  .list-hero h1{font-size:1.6rem;}
  /* Les trois boutons quittent le flux et deviennent des pastilles rondes dans
     le coin. Ils étaient jusqu'ici sur la même ligne que les badges de rôle,
     or ceux-ci passent à la ligne quand ils sont deux : la zone devenait
     confuse. Hors flux, ils ne peuvent plus entrer en collision avec eux quel
     que soit leur nombre.
     Fond blanc semi-opaque : lisible aussi bien sur une photo claire que
     sombre ou sur le vert du fond de secours, sans dépendre de l'image. */
  .list-hero-tools{position:absolute;top:14px;right:24px;flex-wrap:nowrap;gap:8px;}
  .list-hero-tool{
    width:40px;height:40px;padding:0;border-radius:50%;
    background:rgba(255,255,255,.92);border-color:transparent;
    color:var(--forest-dark);
  }
  .list-hero-tool:hover{background:#fff;border-color:transparent;}
  /* Les icônes restent seules : le nom accessible passe par aria-label/title.
     ⚠️ 40px de côté, soit au-dessus du minimum de 24px de WCAG 2.5.8 —
     trois cibles voisines sur un écran tactile ne pardonnent pas l'à-peu-près. */
  .list-hero-tool-label{display:none;}
  .list-hero-tool svg{width:18px;height:18px;}
  /* Statut hors flux lui aussi, dans le coin opposé : les deux forment une
     ligne d'en-tête claire, statut à gauche / pastille à droite, et les badges
     de rôle restent seuls juste au-dessus du titre.
     top:21px et non 14px : le badge fait ~26px de haut contre 40px pour la
     pastille, ce décalage les centre l'un sur l'autre (14 + (40-26)/2).
     max-width : garde-fou pour que le badge ne puisse jamais atteindre les
     pastilles, même avec un libellé long sur un écran très étroit — 196px
     couvrent les trois pastilles (3 × 40 + 2 × 8 d'écart), leurs marges
     (24 + 24) et un écart de 12. ⚠️ À revoir si un quatrième bouton
     apparaissait : le badge de statut passerait sous les pastilles. */
  .list-hero-status{position:absolute;top:21px;left:24px;max-width:calc(100% - 196px);}
  /* Rôles alignés à GAUCHE, sur le même bord que le statut (left:24px = les
     24px de padding de .list-hero-inner) : en colonne étroite, un statut à
     gauche et des rôles à droite se lisaient comme deux fragments sans rapport.
     Les trois règles sont nécessaires — .list-hero-badges est en
     space-between, .list-hero-actions et .list-hero-roles en flex-end. */
  .list-hero-badges{justify-content:flex-start;}
  .list-hero-actions{justify-content:flex-start;}
  .list-hero-roles{justify-content:flex-start;}
  /* ⚠️ Alignés mais PAS accolés : statut et rôles décrivent deux choses
     différentes (l'état de la liste vs la relation du visiteur à elle), un
     empilement serré les ferait lire comme un seul groupe. Le statut hors flux
     s'arrête à 47px, la pastille à 54px ; démarrer les rôles à 78px laisse
     24px de vide franc sous la pastille. C'est ce padding qui porte la
     séparation — pas une marge sur les badges, qui suivrait mal le décalage
     vertical entre le badge (26px) et la pastille (40px). */
  .list-hero-inner{padding-top:78px;}
}

.list-view{display:block;}
.list-view.has-sidebar{
  display:grid;
  grid-template-columns:minmax(0,1fr) 360px;
  gap:28px;
  align-items:start;
}
.list-main{min-width:0;}
.list-side{display:flex;flex-direction:column;gap:20px;min-width:0;}
/* La colonne latérale gère son espacement via `gap` : on neutralise les marges
   propres des enfants (notamment la marge haute des .flash). */
.list-side > *{margin-top:0;}
.list-side .card + .card{margin-top:0;}

.discussion-panel{
  background:var(--paper);border:1px solid var(--line);border-radius:var(--radius);
  box-shadow:var(--shadow);
  display:flex;flex-direction:column;
  overflow:hidden;
}
.discussion-header{padding:16px 18px 12px;border-bottom:1px solid var(--line);}
.discussion-header h3{margin:0;font-size:1.05rem;}

.discussion-messages{
  flex:1;overflow-y:auto;padding:14px 16px;
  display:flex;flex-direction:column;gap:10px;
  min-height:160px;max-height:420px;
}
.msg-bubble{
  align-self:flex-start;max-width:88%;
  background:var(--canvas);border-radius:12px 12px 12px 3px;
  padding:8px 12px;font-size:.88rem;
}
.msg-bubble.mine{
  align-self:flex-end;
  background:var(--berry);color:#fff;
  border-radius:12px 12px 3px 12px;
}
.msg-author{font-weight:700;font-size:.75rem;opacity:.75;margin-bottom:2px;}
.msg-bubble.mine .msg-author{opacity:.85;}
.msg-content{white-space:pre-wrap;word-break:break-word;}
.msg-time{font-size:.7rem;opacity:.6;margin-top:3px;text-align:right;}

.discussion-form{
  display:flex;flex-direction:column;gap:8px;
  padding:12px 16px 16px;border-top:1px solid var(--line);
}
.discussion-form input[type=text],.discussion-form textarea{font-size:.88rem;padding:9px 11px;}
.discussion-form textarea{min-height:auto;resize:vertical;}
/* Remplace le formulaire quand la liste est repassée en brouillon : mêmes
   marges et même bordure haute, pour que le panneau garde exactement sa
   silhouette. Un pied de discussion qui change de forme selon le statut
   attirerait l'œil sur le contenant plutôt que sur l'explication. */
.discussion-readonly{
  display:flex;align-items:flex-start;gap:8px;margin:0;
  padding:12px 16px 16px;border-top:1px solid var(--line);
  font-size:.8rem;color:var(--ink-soft);
}
.discussion-readonly .icon{flex:none;margin-top:2px;}

@media (max-width:900px){
  .list-view.has-sidebar{grid-template-columns:1fr;}
  .discussion-messages{max-height:320px;}
}

/* ---- Tri des cadeaux façon Kanban (SortableJS) ---- */
.kanban-board{
  display:flex;gap:16px;align-items:flex-start;
  overflow-x:auto;padding-bottom:12px;margin-top:16px;
}
/* ⚠️ Le panneau s'élargit POUR CE CONTENU seulement. Un Kanban dans 760px ne
   montre que deux colonnes et demie, et le défilement horizontal devient le
   geste principal — alors qu'on vient y faire du glisser-déposer.
   `:has()` plutôt qu'un modificateur posé par le JS : la largeur est une
   propriété du CONTENU, pas un état à synchroniser. Même mécanisme que
   `.radio-option:has(input:checked)`, déjà en place. */
.modal-panel:has(.kanban-board){max-width:1100px;}
/* Dans la modale, la hauteur de référence n'est plus la fenêtre mais le
   panneau : `calc(100vh - 220px)` y déborderait, et on obtiendrait DEUX
   ascenseurs verticaux imbriqués — celui de la colonne et celui du panneau. */
#panel-modal-body .kanban-column{max-height:min(52vh, 420px);}
.kanban-column{
  flex:0 0 270px;width:270px;
  background:var(--paper);border:1px solid var(--line);border-radius:var(--radius);
  box-shadow:var(--shadow-sm);
  display:flex;flex-direction:column;
  max-height:calc(100vh - 220px);
}
.kanban-column-header{
  padding:14px 16px 10px;border-bottom:1px solid var(--line);
  font-weight:800;font-size:.95rem;color:var(--ink);
  display:flex;align-items:center;justify-content:space-between;gap:8px;
}
.kanban-column-count{font-weight:600;font-size:.78rem;color:var(--ink-soft);}
.kanban-column-list{
  flex:1;overflow-y:auto;padding:10px;
  display:flex;flex-direction:column;gap:8px;
  min-height:80px;
}
.kanban-card{
  display:flex;align-items:center;gap:10px;
  background:var(--canvas);border:1px solid var(--line);border-radius:12px;
  padding:9px 10px;
}
.kanban-drag-handle{
  display:flex;align-items:center;justify-content:center;flex-shrink:0;
  width:26px;height:26px;border-radius:8px;color:var(--ink-soft);
  cursor:grab;touch-action:none;
}
.kanban-drag-handle:hover{background:var(--mint-soft);color:var(--ink);}
.kanban-card-body{min-width:0;flex:1;}
.kanban-card-name{font-weight:700;font-size:.9rem;color:var(--ink);word-break:break-word;}
.kanban-card-meta{font-size:.78rem;color:var(--ink-soft);}
.kanban-column-list .sortable-ghost{opacity:.4;}
.kanban-column-list .sortable-chosen .kanban-card{border-color:var(--forest);}
.kanban-column-list .sortable-drag{opacity:.95;}

/* ---- Modale générique (overlay + panneau centré) ---- */
.modal-overlay{
  position:fixed;inset:0;z-index:1000;
  background:rgba(30,43,34,.55);
  display:flex;align-items:center;justify-content:center;
  padding:20px;
}
.modal-overlay[hidden]{display:none;}
/* Verrou de défilement : posé sur <body> par l'observateur de base.html.twig
   dès qu'une .modal-overlay est visible. Sans lui, la molette faisait défiler
   la PAGE derrière la modale — le panneau restait immobile au milieu d'un
   contenu qui glissait, et on ressortait de la modale sur un tout autre
   endroit de la page. La compensation de la barre de défilement (padding-right)
   est calculée en JS : sa largeur dépend du système. */
body.modal-open{overflow:hidden;}
/* 🔴 LA CONFIRMATION PASSE TOUJOURS DEVANT. Elle est déclenchée DEPUIS une
   autre modale — supprimer une liste se fait depuis « Gérer » — et les deux
   panneaux sont alors visibles en même temps. À `z-index` égal, c'est l'ordre
   du DOCUMENT qui tranche : la confirmation, incluse dans le corps de la page,
   se retrouvait DERRIÈRE la modale d'écran, désormais rendue par
   `base.html.twig` juste avant `</body>`. On voyait donc le fond s'assombrir
   sans jamais voir la question.
   ⚠️ Ne pas compter sur l'ordre d'inclusion pour rétablir ça : il a déjà changé
   une fois, et rien ne le signalerait. */
#confirm-overlay{z-index:1010;}
.modal-panel{
  position:relative;width:100%;max-width:760px;max-height:90vh;overflow-y:auto;
  background:var(--paper);border-radius:var(--radius-lg);box-shadow:var(--shadow);
  padding:30px 28px 28px;
}
.modal-close{
  position:absolute;top:14px;right:14px;
  display:flex;align-items:center;justify-content:center;
  width:32px;height:32px;border-radius:999px;border:1px solid var(--line);
  background:var(--paper);color:var(--ink-soft);cursor:pointer;
}
.modal-close:hover{background:var(--canvas);color:var(--ink);}
.modal-body h1{font-size:1.4rem;padding-right:36px;}

/* ---- Panneau d'écran (modale « Gérer », et tout écran de même facture) ----
   Sections empilées dans un cadre unique, chacune introduite par une pastille
   d'icône et un titre.

   ⚠️ Les icônes sont celles du jeu maison (_icons.html.twig), en
   `currentColor` : elles prennent donc la couleur de leur pastille sans une
   règle de plus. Aucun emoji — ils changent d'aspect d'une plateforme à
   l'autre et ne suivent ni la couleur ni la taille du texte. Même règle que le
   badge « Liste permanente ». */
.panel-head{display:flex;gap:14px;align-items:flex-start;margin-bottom:20px;padding-right:36px;}
.panel-head h1{margin:0 0 2px;font-size:1.35rem;}
.panel-head p{margin:0;}
/* Pastille ronde. `flex:none` : sans lui elle s'écrase quand le titre est long,
   et l'icône déborde de son cercle devenu ovale. */
.panel-icon{
  flex:none;display:flex;align-items:center;justify-content:center;
  width:44px;height:44px;border-radius:50%;
  background:var(--mint-soft);color:var(--forest-dark);
}
.panel-icon svg{width:22px;height:22px;}
.panel-icon--sm{width:34px;height:34px;}
.panel-icon--sm svg{width:18px;height:18px;}
/* ⚠️ Réutilise --berry-soft plutôt qu'un token neuf : --danger vaut exactement
   --berry-dark, son compagnon clair existe déjà. Un --danger-soft ferait deux
   noms pour la même couleur. */
.panel-icon--danger{background:var(--berry-soft);color:var(--danger);}

/* ⚠️ Chaque section de « Gérer » est une CARTE À PART ENTIÈRE (`card
   panel-section`), plus une tranche d'un cadre unique découpé par des
   séparateurs. Deux règles de la forme précédente ont donc été retirées :
   `.panel-section + .panel-section{border-top}`, qui reposait la bordure haute
   que `.card` pose déjà à l'identique, et `.panel-card`, dont le nom désignait
   ce cadre unique — ses trois déclarations étaient soit identiques à celles de
   `.card` (bordure, rayon), soit sans effet (`overflow:hidden`, sur des cartes
   dont aucun enfant ne déborde). Inertes toutes les deux, mais elles
   décrivaient une structure qui n'existe plus.

   ⚠️ Le `padding` doit rester APRÈS `.card` dans le fichier : à spécificité
   égale, c'est l'ordre qui tranche, et `.card` en pose 28. */
.panel-section{padding:20px;}
.panel-section-head{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-bottom:8px;}
.panel-section-title{margin:0;font-size:1.05rem;}
.panel-section-text{margin:0 0 14px;font-size:.88rem;color:var(--ink-soft);}
/* Pastille d'information à côté du titre : dit ce que la section ACCORDE
   (« Consultation et réservation »), là où le titre ne dit que ce qu'elle est. */
.panel-badge{
  display:inline-block;padding:4px 10px;border-radius:999px;
  background:var(--mint-soft);color:var(--forest-dark);
  font-size:.75rem;font-weight:700;
}

/* Champ + bouton sur une ligne. `min-width:0` sur l'input : sans lui, une URL
   longue impose sa largeur intrinsèque et pousse le bouton hors du cadre. */
.panel-input-row{display:flex;gap:10px;align-items:center;margin-bottom:12px;}
.panel-input-row input{min-width:0;flex:1;}
.panel-input-row .btn{flex:none;white-space:nowrap;}
.panel-inline-form{display:flex;gap:10px;flex-wrap:wrap;align-items:center;}
.panel-inline-form select{max-width:240px;}
.panel-field-label{display:block;font-weight:700;font-size:.85rem;margin-bottom:6px;}

/* Ligne d'aide sous une action. Trois tons, une seule structure. */
.panel-note{display:flex;gap:8px;align-items:flex-start;margin:10px 0 0;font-size:.8rem;line-height:1.45;}
.panel-note svg{flex:none;margin-top:2px;}
.panel-note--warning{color:var(--ink-soft);}
.panel-note--warning svg{color:var(--gold);}
.panel-note--info{color:var(--ink-soft);}
.panel-note--info svg{color:var(--forest-dark);}
/* Variante encadrée, pour un texte qui explique un POUVOIR accordé — il pèse
   plus lourd qu'une simple note et mérite d'être détaché du flux. */
.panel-note--soft{
  margin-top:12px;padding:12px 14px;border-radius:12px;
  background:var(--mint-soft);color:var(--ink);
}
.panel-note--soft svg{color:var(--forest-dark);}

/* État vide : un encart, jamais une ligne grise perdue au milieu du reste —
   c'est une réponse à la question « qui a accès ? », elle doit se voir. */
.panel-empty{
  display:flex;gap:8px;align-items:center;margin:0 0 14px;
  padding:14px;border-radius:12px;background:var(--beige);
  color:var(--ink-soft);font-size:.88rem;
}
.panel-empty svg{flex:none;}

.panel-list{list-style:none;padding:0;margin:0 0 14px;display:grid;gap:8px;}
.panel-list li{
  display:flex;align-items:center;gap:10px;flex-wrap:wrap;
  padding:10px 12px;border:1px solid var(--line);border-radius:12px;
}
.panel-list-label{display:inline-flex;align-items:center;gap:6px;font-size:.9rem;word-break:break-all;}
/* Le formulaire de retrait est un enfant flex : sans `margin-left:auto` il se
   colle au badge au lieu de tenir le bord droit de la ligne. */
.panel-list form{margin-left:auto;}

/* Badge neutre — « Facultatif » — à côté d'un libellé de champ. Gris et non
   vert : il ne dit pas ce qu'on gagne, seulement qu'on peut passer. */
.panel-badge--muted{background:var(--beige-dark);color:var(--ink-soft);}

/* Barre d'actions d'un panneau. En flux normal par défaut (page complète). */
.panel-actions{
  display:flex;justify-content:flex-end;gap:12px;flex-wrap:wrap;
  margin-top:24px;padding-top:18px;border-top:1px solid var(--line);
}
/* ⚠️ COLLANTE en modale UNIQUEMENT. Le formulaire est long : devoir le faire
   défiler jusqu'en bas pour trouver « Enregistrer » est exactement ce qu'on
   évite. Les marges négatives la font déborder dans le padding du panneau, pour
   que sa bordure aille d'un bord à l'autre — elles valent `-28px` parce que
   c'est le padding latéral de `.modal-panel`. Hors modale, la page entière
   défile et une barre collée au bas de la fenêtre suivrait le visiteur sur
   toute la page, d'où la portée restreinte.

   🔴 PORTÉE SUR LA CLASSE `.modal-body`, ET SURTOUT PAS SUR UN `id`. La règle
   visait `#panel-modal-body`, donc la seule modale des écrans de liste : celle
   des CADEAUX (`#gift-modal-body`) porte le même partiel d'actions et n'en
   bénéficiait pas — sa barre restait en bas du formulaire, à faire défiler.
   Défaut signalé à l'usage. Les deux modales partagent `.modal-panel`, donc le
   même padding : une seule règle les couvre, et couvrira la prochaine. La
   modale de confirmation porte aussi `.modal-body` mais aucune
   `.panel-actions` — elle n'est pas concernée. */
.modal-body .panel-actions{
  position:sticky;bottom:-28px;z-index:1;
  margin:24px -28px -28px;padding:18px 28px;
  background:var(--paper);
}
@media (max-width:520px){
  /* Boutons pleine largeur, et l'action principale EN PREMIER visuellement :
     sur un écran étroit, c'est celle que le pouce doit atteindre. */
  .panel-actions{flex-direction:column-reverse;}
  .panel-actions .btn{width:100%;}
}

@media (max-width:520px){
  /* Le bouton passe SOUS le champ : à cette largeur, « Copier » et une URL
     complète ne tiennent pas sur une ligne sans que l'un des deux devienne
     illisible. */
  .panel-input-row{flex-wrap:wrap;}
  .panel-input-row input{flex:1 1 100%;}
  .panel-input-row .btn{flex:1 1 auto;}
  .panel-inline-form select{max-width:none;flex:1 1 100%;}
}

/* ---- Jetons d'accès API (profil) ---- */
/* ⚠️ `word-break` indispensable : un jeton fait 64 caractères sans espace et
   déborderait de la carte sur mobile, empêchant justement de le copier. */
.api-token-value{
  display:block;padding:10px 12px;background:#fff;border:1px solid var(--line);
  border-radius:8px;font-family:ui-monospace,monospace;font-size:.85rem;word-break:break-all;
}
.api-token-heading{margin:0 0 10px;font-size:.95rem;}
.api-token-list{list-style:none;padding:0;margin:0 0 16px;display:grid;gap:12px;}
/* Une CARTE par jeton, en colonne : nom, création, dernière utilisation,
   action. ⚠️ La rangée compacte précédente (flex, tout sur une ligne) tassait
   les deux dates dans un même bloc gris où l'on ne distinguait plus laquelle
   était laquelle. */
.api-token-card{
  display:flex;flex-direction:column;gap:4px;
  padding:14px 16px;background:var(--beige);
  border:1px solid var(--line);border-radius:12px;
}
.api-token-name{font-size:.95rem;}
/* ⚠️ `flex-end` sur le FORMULAIRE, pas sur le bouton : c'est le formulaire
   qui est l'enfant flex de la carte (le bouton est dedans). Une règle posée
   sur `.btn` ne déplacerait rien. */
.api-token-revoke{align-self:flex-end;margin-top:6px;}
/* ⚠️ Le `input[name=label]` que cette règle accompagnait a été RETIRÉ : la
   création d'un jeton est un simple bouton (voir profile/edit.html.twig).
   Le bouton prend toute la largeur — c'est l'action principale de ce bloc,
   et rien ne l'accompagne plus sur sa ligne. */
.api-token-create{display:flex;gap:10px;flex-wrap:wrap;}
.api-token-create .btn{width:100%;}

/* Cartes de cadeau en mobile.
   ⚠️ Le visuel RÉTRÉCIT au lieu de passer pleine largeur : une photo de 100%
   de large pousserait les informations si loin sous la ligne de flottaison que
   la liste cesserait d'être parcourable d'un coup d'œil — or c'est exactement
   ce qu'on vient y faire. */
@media (max-width:720px){
  /* ⚠️ Les boutons passent sur une LIGNE À EUX, sur toute la largeur de la
     carte — ils démarrent donc sous le visuel. Coincés dans la seconde colonne,
     trois boutons devaient tenir dans les deux tiers d'un écran de téléphone :
     « Supprimer » débordait de sa propre bordure. */
  .gift-row{
    grid-template-columns:104px 1fr;
    grid-template-areas:
      "thumb main"
      "actions actions";
    gap:14px;padding:14px;
  }
  /* ⚠️ La HAUTEUR suit la largeur. Ne changer que la largeur laissait un
     visuel de 104×180 : un rectangle étiré, exactement le défaut que la
     hauteur fixe était censée corriger. */
  .gift-thumb-wrap{width:104px;height:104px;}
  .gift-main > h3{font-size:1.1rem;}
  /* ⚠️ UNE SEULE LIGNE, trois parts égales. `flex:1 1 0` (base zéro) et non
     `1 1 auto` : avec `auto`, chaque bouton part de la largeur de son texte, et
     « Supprimer » ressort plus large que « Copier ». Base zéro = tiers exacts.
     `min-width:0` autorise la réduction sous la largeur du contenu, sans quoi
     `nowrap` ferait déborder la rangée. */
  .gift-actions{flex-wrap:nowrap;}
  .gift-actions > *{flex:1 1 0;min-width:0;}
  .gift-actions .btn{
    justify-content:center;white-space:nowrap;
    font-size:.78rem;padding-left:8px;padding-right:8px;gap:5px;
  }
  .gift-actions .btn .icon{flex:none;}
}

/* ⚠️ Sous 400px, l'icône disparaît et le LIBELLÉ reste. Trois boutons dans
   ~76px chacun ne laissent pas la place aux deux : entre une icône décorative
   et le mot qui porte le sens, on garde le mot. Le `title` du bouton « Copier »
   continue d'ailleurs de porter l'intitulé complet. */
@media (max-width:400px){
  .gift-actions .btn{font-size:.72rem;padding-left:6px;padding-right:6px;}
  .gift-actions .btn .icon{display:none;}
}

/* ---- Repli de langue du catalogue ----

   ⚠️ Discret mais LISIBLE : ce badge est la seule chose qui distingue « fiche
   pas encore traduite » de « traduction bâclée ». L'effacer au point qu'on ne
   le voie plus reviendrait à servir le repli en silence, ce que tout ce
   mécanisme existe pour éviter. */
.fallback-badge{
  display:inline-block;margin-top:4px;padding:2px 8px;border-radius:999px;
  background:var(--beige-dark);color:var(--ink-soft);
  font-size:.68rem;font-weight:700;letter-spacing:.02em;text-transform:uppercase;
  white-space:nowrap;
}
.fallback-notice{
  display:flex;align-items:center;gap:10px;
  margin:0 0 20px;padding:12px 16px;border-radius:12px;
  background:var(--gold-soft);border:1px solid var(--gold);
  color:var(--ink);font-size:.92rem;
}
