/* ============================================================================
   BOUTIQUE — feuille de style autonome
   ----------------------------------------------------------------------------
   Volontairement INDÉPENDANTE de style.css : cette base est destinée à être
   déployée chez des clients, et hériter des tokens du site vitrine aurait
   ramené sa charte par la porte de derrière. Tout ce dont la boutique a besoin
   est défini ici, et rien d'autre.

   Parti pris : sobre mais habillé. Une seule couleur d'accent, une échelle
   typographique courte, des surfaces qui se distinguent par un filet plutôt
   que par une ombre. Ce qui reste à faire pour habiller un client tient dans
   le bloc de tokens ci-dessous : teinte d'accent, rayon, police.

   Thème sombre inclus, et il suit trois états — le choix explicite du visiteur
   (data-theme sur <html>), et à défaut sa préférence système. Une couleur
   n'est JAMAIS définie uniquement dans le bloc sombre : le clair est la base,
   le sombre ne fait que redéfinir.
   ============================================================================ */

:root {
  --fond:            var(--marque-fond);
  --fond-doux:       var(--marque-fond-doux);
  --surface:         var(--marque-surface);
  --encre:           var(--marque-encre);
  --encre-douce:     var(--marque-encre-douce);
  /* ASSOMBRI LE 9 AOÛT 2026, POUR UNE RAISON MESURÉE ET NON DE GOÛT.
     À #8A8A96 ce gris donnait 3,41 sur blanc et 3,19 sur le fond doux, quand
     le texte courant demande 4,5 (WCAG AA). Or il ne sert PAS à de la
     décoration : accroches de carte produit, prix barrés, légendes de
     carrousel, états vides, promesse de la newsletter, intro du chat. Sept
     endroits de texte à lire, sur toutes les pages.
     #6D6D77 monte à 5,12 sur blanc et 4,78 sur le fond doux. La teinte est
     conservée au canal près — seule la clarté change — et la hiérarchie reste
     lisible : 18 pour l'encre, 6,7 pour l'encre douce, 4,8 pour celle-ci. */
  --encre-tenue:     var(--marque-encre-tenue);
  --filet:           var(--marque-filet);
  --filet-fort:      var(--marque-filet-fort);
  --accent:          var(--marque-accent);
  --accent-encre:    var(--marque-accent-encre);
  --accent-doux:     rgba(var(--marque-accent-rgb), 0.08);
  /* L'ACCENT QUAND IL SERT DE TEXTE, et non de fond. Les deux emplois n'ont pas
     les mêmes exigences : un aplat de couleur n'a qu'à porter son libellé,
     tandis qu'un lien doit lui-même se détacher de la page.

     Une marque claire — un or, un jaune, un pastel — passe très bien en fond
     (avec une encre foncée dessus) et échoue en texte : l'or #C29A60 donne
     6,99:1 sous une encre foncée, et 2,49:1 posé sur la page. Tant qu'un seul
     jeton servait aux deux, il fallait choisir entre une marque juste et des
     liens lisibles.

     PAR DÉFAUT, C'EST LE MÊME : une boutique dont l'accent est déjà sombre ne
     déclare rien et ne voit aucune différence. */
  --accent-texte:    var(--marque-accent-texte, var(--accent));
  /* L'ACCENT ÉCLAIRCI — L'OR QUI SE POSE SUR UNE IMAGE.
     C'est la troisième déclinaison de l'accent, et elle a une règle d'emploi
     précise : elle sert PAR-DESSUS une photo ou un fond sombre, jamais sur le
     fond de la page.

     Pourquoi elle est nécessaire. Un accent de marque est choisi pour porter du
     texte sur fond clair ; posé sur une vidéo d'atelier ou une photo de bijou,
     l'or de cette boutique devient un ocre éteint qui se confond avec les
     parties dorées de l'image. Éclairci de 28 %, il se détache — et contre son
     encre foncée il monte de 7,0 à 9,6:1.

     ELLE EST CALCULÉE, PAS ÉCRITE. Une valeur en dur serait juste pour cette
     boutique et fausse pour la suivante : le mélange suit la charte quelle
     qu'elle soit. Une marque qui veut trancher déclare `--marque-accent-clair`. */
  --accent-clair:    var(--marque-accent-clair, color-mix(in srgb, var(--accent) 72%, #FFFFFF));
  /* LE BORDEAUX DES SIGNAUX PERSONNELS — compte du panier, cœur des favoris.
     Il ne dérive PAS de l'accent, et c'est le point : sur cette boutique
     l'accent est l'or du panier lui-même, et un compteur doré sur une icône
     dorée ne se remarque pas. Ces deux marqueurs disent « il y a quelque chose
     à vous » ; ils doivent trancher avec la charte, pas la prolonger — et
     trancher DE LA MÊME FAÇON tous les deux, sinon deux signaux de même nature
     se lisent comme deux choses différentes. */
  --bordeaux:        var(--marque-bordeaux, #B01B3F);
  --succes:          #1B7F4B;
  /* Le point « en ligne » du chat : un vert franc, identique en clair et en
     sombre — c'est un signal d'état, pas une couleur de marque. Les variantes
     -rgb existent parce que les rgba() de l'animation ont besoin des
     composantes, qu'une couleur hexadécimale ne sait pas fournir. */
  --status-live:           #22C55E;
  --status-live-rgb:       34, 197, 94;
  --status-live-deep-rgb:  21, 128, 61;
  --alerte:          #B4442A;
  /* L'AVERTISSEMENT N'EST PAS UNE ERREUR. « Paiement abandonné, votre panier
     est intact » et « adresse non vérifiée » demandent une action sans rien
     signaler de cassé : en rouge, ils alarment pour rien ; en gris, ils
     passent inaperçus — ce qu'ils faisaient jusqu'ici, faute de règle. */
  --attention:       #9A6413;
  /* L'AMBRE DES ÉTOILES EST UN SIGNAL, PAS UNE COULEUR DE MARQUE — au même titre
     que le vert « en ligne » du chat plus haut. Une note se lit en ambre partout
     dans le commerce en ligne ; la reteindre selon la palette de la boutique
     ferait perdre le repère. Elle était écrite en dur à trois endroits, ce qui
     garantissait qu'un jour l'un des trois divergerait. */
  --etoile:          #F5A623;
  --etoile-encre:    #C67C00;
  --notification:    #FF453A;
  --rayon:           var(--marque-rayon);
  --rayon-large:     var(--marque-rayon-large);
  --ombre:           0 1px 2px rgba(16, 16, 24, .05);
  /* CONTAINER HOMOGÈNE (25/08) : UNE SEULE largeur pour toutes les pages —
     accueil, catégories, fiches, compte, panier — pour que la mise en page ne
     « saute » pas d'une largeur à l'autre en navigant (une taille différente par
     type de page se remarque et déstabilise). Élargi de 1180 → 1440px pour mieux
     exploiter les écrans actuels ; le texte long reste borné par `.prose`
     (68-85ch), donc la lisibilité n'est jamais dégradée. Les grilles produits y
     gagnent une colonne (3 → 4) sans réglage : `repeat(auto-fill, minmax(230px,1fr))`
     fait suivre le nombre de colonnes à la largeur disponible, et le réduit tout
     seul sur petit écran. RÉGLABLE AU BACK-OFFICE (Apparence → largeur des pages) :
     la valeur vient de `--marque-largeur`, 1440 par défaut. */
  --largeur:         var(--marque-largeur, 1440px);
  /* L'ÉCHELLE D'ESPACEMENT, et pourquoi elle est écrite ici plutôt que subie.
     La feuille comptait VINGT ET UNE valeurs distinctes au-dessus de 20 px —
     22 et 24 employées vingt-six et vingt fois, 26, 28 et 30 treize fois
     chacune. Aucune ne se distinguait des voisines à l'œil : deux pixels
     d'écart ne se voient pas, mais ils signent l'ajustement à la main, une
     règle après l'autre, sans référence commune. Le résultat se voit, lui :
     deux blocs de même nature respirent différemment selon la page.

     Six paliers suffisent, chacun avec un emploi :
       20px  entre deux éléments d'un même bloc
       24px  entre deux blocs d'une même section
       32px  entre deux sections
       40px  ouverture et fermeture d'une page
       48px  séparation forte, avec filet
       64px  pleine respiration (bannière, tunnel)
     Les valeurs sous 20 px ne sont pas concernées : ce sont des rembourrages
     de composant (bouton, champ, badge), réglés par rapport à leur propre
     texte et non par rapport à la page.

     Les 106 valeurs hors échelle ont été ramenées au palier INFÉRIEUR : à
     défaut d'être toujours le bon choix, c'est celui qui va dans le sens
     demandé — moins d'espace, et le même partout. */
  --police:          system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  /* LA CHASSE FIXE NE SERT PLUS QU'À CE QUI SE RECOPIE — référence de commande,
     IBAN, numéro de suivi, extrait de code. Elle habillait aussi des libellés
     d'interface du widget de chat ; Jesse l'a fait retirer partout le 10 août
     2026, sur les trois sites et le back-office compris, pour le motif qui
     valait déjà pour les surtitres en capitales monospace : ça se reconnaît.
     La pile reste celle du SYSTÈME, jamais une police chargée : en
     proportionnelle, un 1 et un l se confondent sur une référence qu'on dicte. */
  --police-mono:     var(--marque-police-code);
  /* LA POLICE DES TITRES, séparée de celle du texte — et par défaut la MÊME.
     Une base neutre ne doit rien changer d'elle-même : tant qu'un profil de
     marque ne déclare pas `--marque-police-titre`, ce jeton retombe sur la
     pile courante et pas un pixel ne bouge.

     Pourquoi la séparer alors. Une charte de bijouterie, d'hôtellerie ou de
     vin se joue en grande partie sur ce couple : une sérif pour les titres,
     une linéale pour le texte courant. Sans deux jetons, il fallait choisir
     entre habiller les titres et garder un corps de texte lisible — ou
     modifier la feuille chez chaque client, ce que ce fichier existe
     précisément pour éviter. */
  --police-titre:    var(--marque-police-titre, var(--police));
}

/* LA GRAISSE DES TITRES SE RÈGLE PAR MARQUE, ET C'EST INDISSOCIABLE DE LA
   POLICE DES TITRES. Beaucoup de belles sérifs de titrage n'existent qu'en UN
   seul poids — Sorts Mill Goudy n'a que du 400. Demander 700 à une fonte qui ne
   l'a pas ne rend pas un gras : le navigateur en FABRIQUE un, en épaississant
   les tracés dans tous les sens. Sur une linéale cela passe ; sur une sérif à
   fort contraste les déliés s'empâtent et les empattements bavent — la police
   choisie pour son élégance rend exactement l'inverse.

   `--marque-poids-titre` N'A PAS DE VALEUR PAR DÉFAUT COMMUNE, et c'est
   volontaire : chaque règle garde SA valeur actuelle en repli. Un jeton avec un
   défaut unique aurait aligné tous les titres du modèle sur un même poids et
   modifié le rendu de toutes les boutiques déjà en ligne — pour un réglage que
   personne n'avait demandé. */

/* ═══ SORTS MILL GOUDY ════════════════════════════════════════════════════════
   Auto-hébergée, jamais appelée sur un service tiers : un lien vers un CDN de
   polices fait dépendre l'affichage d'un domaine qu'on ne maîtrise pas, et
   communique l'adresse IP de chaque visiteur à ce tiers.

   ELLE N'EST TÉLÉCHARGÉE QUE SI ELLE SERT. Une déclaration `@font-face` ne
   déclenche aucune requête par elle-même : le fichier ne part que lorsqu'un
   élément réellement affiché la demande. La laisser dans la feuille partagée ne
   coûte donc rien aux boutiques qui ne l'emploient pas — c'est une police
   disponible, pas une police imposée.

   `font-display: swap` : le texte s'affiche tout de suite dans la pile de repli
   et bascule à l'arrivée de la fonte. L'alternative (`block`) laisse un blanc de
   trois secondes à la place des titres — le pire endroit où en laisser un.

   Deux sous-ensembles plutôt qu'un fichier complet : le latin suffit au
   français (é, è, à, ç, œ y sont), le latin étendu ne part que si la page
   contient un caractère qui l'exige. 22 Ko au lieu de 33. */
@font-face {
  font-family: 'Sorts Mill Goudy';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/assets/fonts/sorts-mill-goudy-v16-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                 U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
                 U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Sorts Mill Goudy';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/assets/fonts/sorts-mill-goudy-v16-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
                 U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
                 U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --fond:         var(--marque-fond-nuit);
    --fond-doux:    var(--marque-fond-doux-nuit);
    --surface:      var(--marque-surface-nuit);
    --encre:        var(--marque-encre-nuit);
    --encre-douce:  var(--marque-encre-douce-nuit);
    /* Même correction en sens inverse : 4,23 sur les surfaces sombres. */
    --encre-tenue:  var(--marque-encre-tenue-nuit);
    --filet:        var(--marque-filet-nuit);
    --filet-fort:   var(--marque-filet-fort-nuit);
    --accent:       var(--marque-accent-nuit);
    --accent-encre: var(--marque-accent-encre-nuit);
    --accent-doux:  rgba(var(--marque-accent-rgb-nuit), 0.12);
    /* Sur fond sombre, la variante nuit EST déjà celle qui porte le texte : la
       distinction fond/texte du thème clair n'a plus lieu d'être, puisque la
       palette a éclairci l'accent exprès pour ce fond. On réaligne donc le
       jeton, sans quoi les liens garderaient la teinte sombre du thème clair
       et disparaîtraient. */
    --accent-texte: var(--marque-accent-nuit);
    --succes:       #4CC98A;
    --alerte:       #FF8A6B;
    --attention:    #E5A83C;
  --etoile:       #F5B942;
  --etoile-encre: #F5B942;
  --notification: #FF6B5E;
    --etoile:       #F5B942;
    --etoile-encre: #F5B942;
    --notification: #FF6B5E;
    --ombre:        0 1px 2px rgba(0, 0, 0, .4);
  }
}
/* LE SOMBRE CHOISI À LA MAIN, par opposition au sombre hérité du système.
   Ce bloc portait ses couleurs ÉCRITES EN DUR — le gris bleuté et l'indigo
   #7D9BFF de la base neutre — là où son jumeau au-dessus (`prefers-color-scheme`)
   lisait les jetons de marque. Conséquence : une boutique habillée aux couleurs
   d'un client rendait sa charte au visiteur dont le système est en sombre, et
   la charte d'ORIGINE à celui qui avait cliqué sur la lune. Deux thèmes sombres
   différents sur le même site, selon un réglage que le marchand ne voit pas.

   Le défaut ne pouvait pas se remarquer sur la base neutre : les valeurs en dur
   y étaient précisément celles des jetons. Il n'apparaît qu'à la première
   déclinaison — c'est-à-dire chez le premier client. */
:root[data-theme="dark"] {
  --fond:         var(--marque-fond-nuit);
  --fond-doux:    var(--marque-fond-doux-nuit);
  --surface:      var(--marque-surface-nuit);
  --encre:        var(--marque-encre-nuit);
  --encre-douce:  var(--marque-encre-douce-nuit);
  /* Même correction en sens inverse : 4,23 sur les surfaces sombres. */
  --encre-tenue:  var(--marque-encre-tenue-nuit);
  --filet:        var(--marque-filet-nuit);
  --filet-fort:   var(--marque-filet-fort-nuit);
  --accent:       var(--marque-accent-nuit);
  --accent-encre: var(--marque-accent-encre-nuit);
  --accent-doux:  rgba(var(--marque-accent-rgb-nuit), 0.12);
  --accent-texte: var(--marque-accent-nuit);
  --succes:       #4CC98A;
  --alerte:       #FF8A6B;
  --attention:    #E5A83C;
  --ombre:        0 1px 2px rgba(0, 0, 0, .4);
}

*, *::before, *::after { box-sizing: border-box; }

body {
  margin:0;
  background: var(--fond);
  color: var(--encre);
  font-family: var(--police);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

img { max-width: 100%; height: auto; display: block; }
a { color: inherit; }

/* ANTI-SCROLL HORIZONTAL. Les sections plein cadre (hero, carrousels débordants,
   bandeau d'avis) s'étendent en `width: 100vw` — or `100vw` INCLUT la barre de
   défilement verticale, alors que la zone de contenu ne fait que `clientWidth`
   (100vw − scrollbar). Elles dépassaient donc de la largeur de la scrollbar,
   créant un scroll horizontal parasite. On CLIPPE ce débordement sur `main` :
   `overflow-x: clip` coupe pile aux bords de la fenêtre (le plein cadre reste
   plein), NE crée PAS de conteneur de défilement (donc le header sticky — hors de
   `main` — et la colonne d'achat sticky ne sont pas affectés) et laisse l'axe
   vertical `visible`. Le header vit dans <body>, pas ici. */
main { overflow-x: clip; }
.contenu { max-width: var(--largeur); margin:0 auto; padding:0 24px; }

/* ------------------------------------------------------------- en-tête */
.bandeau {
  position: sticky; top: 0; z-index: 20;
  /* Repli opaque : c'est ce que voient les navigateurs sans `backdrop-filter`,
     et c'est aussi ce qui garantit qu'un en-tête collant ne laisse JAMAIS lire
     le texte de la page au travers. */
  background: var(--fond);
  border-bottom: 1px solid var(--filet);
}

/* ═══ LE FLOU DE L'EN-TÊTE COLLANT ═══════════════════════════════════════════
   IL EST PORTÉ PAR UN PSEUDO-ÉLÉMENT, ET C'EST TOUT L'ENJEU.

   `backdrop-filter` fait de l'élément qui le porte le BLOC CONTENEUR de ses
   descendants en `position: fixed`. Posé sur `.bandeau`, il aurait enfermé dans
   la boîte de l'en-tête trois choses qui doivent précisément en sortir : le
   champ de recherche ouvert, le tiroir de navigation mobile et son fond
   sombre — tous trois `fixed` et tous trois enfants du bandeau. Le tiroir
   n'aurait plus couvert l'écran mais une bande de cent trente pixels de haut.

   La feuille porte déjà deux commentaires sur ce piège, payés chacun d'un essai
   raté avec `transform`. Un pseudo-élément n'a pas de descendants : il peut
   flouter sans rien piéger.

   L'arrière-plan opaque de `.bandeau` est retiré dans ce cas seulement — sinon
   il n'y aurait rien à flouter derrière. */
@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .bandeau { background: transparent; }
  .bandeau::before {
    content: ''; position: absolute; inset: 0; z-index: -1;
    /* Assez opaque pour que le texte de l'en-tête reste lisible par-dessus
       n'importe quel contenu — une photo produit sombre comme une grille
       claire — et assez transparent pour qu'on voie défiler dessous. */
    background: color-mix(in srgb, var(--fond) 72%, transparent);
    /* 16 px effaçaient ce qui passait dessous : on ne devinait plus un titre de
       produit d'un prix, et la barre paraissait dépolie plutôt que translucide.
       À 9 px on lit encore les formes qui défilent — c'est ce qui dit que la
       page continue sous l'en-tête, et c'est toute la raison de le flouter. */
    -webkit-backdrop-filter: blur(9px) saturate(1.4);
    backdrop-filter: blur(9px) saturate(1.4);
  }
  /* ⚠️ SEUL L'EN-TÊTE RÉDUIT AU DÉFILEMENT CHANGE DE COULEUR (20/08/2026,
     « seul le header sticky doit changer de couleur, et plutôt couleur or »).
     La passe précédente assombrissait `.bandeau::before` SANS le conditionner
     à `.est-defile` — la teinte plus sombre s'appliquait donc aussi à l'en-
     tête haut de page, jamais visé par la demande. Elle mélangeait en plus
     de l'ENCRE, ce qui donnait un gris-brun plutôt qu'un or — corrigé en
     mélangeant l'ACCENT (la teinte or de la marque) à la place. */
  .bandeau.est-defile::before {
    background: color-mix(in srgb, color-mix(in srgb, var(--fond) 55%, var(--accent) 45%) 80%, transparent);
    -webkit-backdrop-filter: blur(6px) saturate(1.4);
    backdrop-filter: blur(6px) saturate(1.4);
  }
}
/* Deux rangées : le logo centré, le menu dessous.

   LES ACTIONS SONT EN ABSOLU, et c'est tout l'intérêt. Placées dans le flux à
   côté du logo, leur largeur pousserait celui-ci hors du centre — et pas d'une
   quantité fixe : la pastille du panier apparaît, le libellé passe de
   « Connexion » à « Mon compte », et le logo se décale au fil de la visite. Un
   élément censé être centré qui bouge d'une page à l'autre se remarque
   immédiatement, sans qu'on sache dire pourquoi. Sorti du flux, il est centré
   sur la PAGE et n'a plus rien à négocier avec ses voisins. */
.bandeau-haut {
  position: relative;
  max-width: var(--largeur); margin:0 auto; padding:20px 24px 20px;
  display: flex; justify-content: center;
}
.marque {
  /* Le nom de la boutique suit la police des TITRES, pas celle du texte : c'est
     le premier mot de la page et il porte l'identité. Sans logo déposé, c'est
     même la seule chose qui la porte. Sans réglage de marque, ce jeton vaut la
     police courante — rien ne change. */
  font-family: var(--police-titre);
  /* PLUS GRAND (17/08/2026) : 22 px, c'était la taille d'un intertitre. Le nom
     de la boutique est la seule chose qui porte l'identité tant qu'il n'y a pas
     de logo déposé — il doit dominer sa ligne, pas s'y fondre. Les actions de
     droite sont positionnées en absolu : l'agrandir ne les déplace pas. */
  /* LE NOM DE LA BOUTIQUE RESTE GRAS même quand la marque demande des titres
     légers : c'est une signature, pas un titre courant. Il ne suit donc pas
     `--marque-poids-titre` — sur une charte qui déclare 400 pour ses titres,
     le nom se serait affiché du même poids que le texte de la navigation. */
  font-weight: 700; font-size: 27px; letter-spacing: -0.02em;
  text-decoration: none; white-space: nowrap;
  display: flex; align-items: center;
  /* Point d'ancrage de l'éclat, et repère de la mise à l'échelle au
     défilement : sans origine explicite, le nom rapetisse vers son coin haut
     gauche et se décentre de la barre. */
  position: relative; transform-origin: center center;

  /* ENTRÉE À L'OUVERTURE DE LA PAGE.
     `both` retient l'état INITIAL avant le premier plan : sans lui, le nom
     s'affiche net, puis saute à l'état de départ au moment où l'animation
     démarre — un clignotement d'autant plus visible qu'il touche le premier
     mot de la page.

     TROIS PROPRIÉTÉS SEULEMENT, et c'est un choix : `opacity`, `transform` et
     `filter` se composent sur le GPU et ne provoquent aucun recalcul de mise
     en page. Une entrée sur `letter-spacing` ou `font-size` aurait été plus
     spectaculaire et aurait décalé la barre de navigation pendant huit
     dixièmes de seconde, juste au-dessus de la ligne de flottaison — c'est
     exactement ce que mesure le CLS. */
  /* `backwards` ET NON `both`, et c'est la clé de tout ce qui suit. Une
     animation qui RETIENT son dernier état (`forwards`/`both`) continue de
     piloter `transform` une fois terminée, et une animation l'emporte sur les
     déclarations ordinaires : la mise à l'échelle au défilement, écrite plus
     bas, aurait été ignorée sans erreur ni message. Ici l'état final de
     l'animation est exactement l'état naturel de l'élément — il n'y a donc rien
     à retenir, et `transform` redevient disponible dès la fin. */
  animation: marque-entree .9s cubic-bezier(.16, .84, .44, 1) backwards;
  /* La transition ne sert QUE au défilement ; elle ne gêne pas l'animation
     d'entrée, qui a la priorité tant qu'elle joue. */
  transition: transform .28s cubic-bezier(.22, .61, .36, 1);
}
/* Un logo déposé depuis l'écran Identité arrive dans sa taille d'origine, qui
   peut être n'importe laquelle : la hauteur est donc imposée ici et la largeur
   suit. Sans cela, une image de 2000 px pousse le bandeau hors de l'écran. */
.marque img {
  /* Le logo suit l'agrandissement du nom : les deux sont la même chose selon
     que la boutique en a déposé un ou non.

     LA HAUTEUR EST UN JETON (25/08/2026). 44 px conviennent à un LOGOTYPE — un
     mot posé sur une ligne, large et bas. Ils ne conviennent pas à un BLOC
     EMPILÉ, symbole au-dessus du nom sur deux lignes : à 44 px de haut, le nom
     tombe sous 10 px et ne se lit plus. Une boutique dont le logo est carré peut
     donc le remonter depuis son profil de marque, sans toucher cette feuille —
     la même mécanique que `--marque-ratio-visuel` pour les visuels produit. */
  height: var(--marque-logo-hauteur, 44px);
  width: auto; max-width: 300px; display: block;
}

/* LE LOGO EN THÈME SOMBRE (18/09/2026, en posant celui du bijoutier).
   Le thème sombre retourne les FONDS, pas les FICHIERS : un logotype tracé en
   noir — le cas de presque toutes les chartes reprises d'un ancien site —
   disparaît purement et simplement sur le fond de nuit, et le bandeau paraît
   vide. La correction ne peut pas être automatique : la même inversion
   ravagerait un logo en couleur. C'est donc un JETON DE MARQUE, que seules les
   boutiques au logo monochrome déclarent dans leur profil :
   `--marque-logo-filtre-nuit: invert(1)`. Les autres ne voient rien changer.
   Les deux écritures sont nécessaires : l'attribut couvre le visiteur qui a
   basculé le thème à la main, la requête média celui dont le navigateur
   n'exécute pas le script d'amorçage et qui hérite du réglage du système. */
:root[data-theme="dark"] .marque img,
:root[data-theme="dark"] .pied-logo,
:root[data-theme="dark"] .contact-logo {
  filter: var(--marque-logo-filtre-nuit, none);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .marque img,
  :root:not([data-theme="light"]) .pied-logo,
  :root:not([data-theme="light"]) .contact-logo {
    filter: var(--marque-logo-filtre-nuit, none);
  }
}

@keyframes marque-entree {
  /* Trois gestes plutôt qu'un : le nom monte, se dépose (il dépasse légèrement
     sa taille avant de s'y ranger) et se net. Le dépassement est ce qui
     distingue une apparition d'un simple fondu — c'est lui qui donne
     l'impression d'un objet qu'on pose, et non d'une image qu'on allume. */
  /* ⚠️ NI `letter-spacing` NI `filter` (18/09/2026). Ces deux propriétés sortent
     l'animation du compositeur : l'interlettrage relance une MISE EN PAGE du
     texte à chaque image, le flou une PEINTURE. Sur la première seconde de
     chaque page — celle où le navigateur a le plus à faire — c'était neuf cents
     millisecondes de travail rendu au thread principal pour un effet de
     signature. `opacity` et `transform` font le même geste sans rien coûter :
     le nom monte, dépasse légèrement sa taille, puis s'y range. Lighthouse le
     signalait comme « animation non composée », et c'était le seul élément
     animé de la page. */
  0%   { opacity: 0; transform: translateY(12px) scale(.94); }
  60%  { opacity: 1; transform: translateY(0)    scale(1.03); }
  100% { opacity: 1; transform: none; }
}

/* ⚠️ L'ÉCLAT BALAYANT A ÉTÉ RETIRÉ (25/08/2026, « le reflet s'applique à tout
   le bloc en thème sombre »). Il était porté par un pseudo-élément rectangulaire
   (`.marque::after`, `inset: -6px -14px`) en `mix-blend-mode: overlay` avec du
   blanc à 95 %. Le piège : `overlay` sur un fond CLAIR reste discret, mais sur
   le fond SOMBRE de l'en-tête il éclaircit brutalement toute la surface du
   rectangle — le reflet flashait donc le bloc entier, pas le nom. Un balayage
   rectangulaire touche forcément le bloc ; le limiter au texte imposerait
   `background-clip: text` (donc `color: transparent`), qui ferait disparaître
   le nom là où l'animation ne joue pas — refusé à raison. L'entrée `marque-
   entree` (le nom qui monte, se pose et se net) reste, seule : elle est
   theme-safe et vaut pour un logo texte comme pour un logo image. */

/* LE NOM RAPETISSE QUAND LA PAGE DÉFILE. L'en-tête est collant : il accompagne
   le visiteur sur toute la page, et il n'a pas à y occuper la même place qu'à
   l'arrivée. On met à l'ÉCHELLE plutôt que de changer la taille de police :
   `transform` ne provoque aucun recalcul de mise en page, donc ni saut de la
   barre de navigation, ni invalidation de `--collant`, la hauteur mesurée dont
   dépendent les colonnes épinglées de la fiche produit et des rubriques. */
.bandeau.est-defile .marque { transform: scale(.78); }

/* UNE ANIMATION D'ENTRÉE EST DÉCORATIVE : elle n'apporte rien à qui a demandé
   moins de mouvement, et un flou animé est précisément ce qui déclenche des
   nausées chez les personnes sensibles au mouvement. On ne la ralentit pas, on
   la supprime — et l'état final doit être écrit explicitement, sinon `both`
   laisserait le nom flou et transparent pour toujours. */
@media (prefers-reduced-motion: reduce) {
  .marque { animation: none; opacity: 1; transform: none; filter: none; transition: none; }
  /* La réduction de taille au défilement reste : ce n'est pas une animation
     décorative mais un changement d'état, et sans transition il est instantané.
     La supprimer priverait ces visiteurs du gain de place, sans rien leur
     épargner. */
}
.bandeau-actions {
  position: absolute; right: 24px; top: 50%; transform: translateY(-50%);
  display: flex; align-items: center; gap:8px;
  /* CE z-index N'EST PAS DÉCORATIF. Le `transform` ci-dessus — un simple
     centrage vertical — crée un contexte d'empilement, et y ENFERME le z-index
     du panneau de notifications : celui-ci avait beau demander 40, il ne se
     comparait plus qu'à ses frères, et le menu, plus bas dans le document,
     passait par-dessus. On voyait les rubriques à travers le panneau ouvert.
     Le remède se pose ici, sur l'élément transformé, jamais sur l'enfant. */
  z-index: 30;
}
/* Pendant gauche des actions : la bascule de thème, seule. Même positionnement
   absolu, pour la même raison — le logo doit rester centré sur la page, quelle
   que soit la largeur des deux groupes. */
.bandeau-outils {
  position: absolute; left: 24px; top: 50%; transform: translateY(-50%);
  display: flex; align-items: center; gap:8px;
}
/* 34 px entre deux entrées, contre 26. Une barre de navigation serrée se lit
   comme un bloc de texte : l'œil doit découper les mots avant de les choisir.
   L'espace est ce qui transforme une suite de mots en une suite de boutons.

   `--menu-ecart` EST POSÉ PAR LE SCRIPT, ET SEULEMENT S'IL RESTE DE LA PLACE
   (22/09/2026, « un peu plus d'écart, à condition que la place soit
   disponible »). Le CSS ne sait pas si la barre tient sur une ligne : une
   valeur plus large écrite ici serait confortable sur une boutique à cinq
   rubriques et ferait passer sur deux lignes celle qui en a onze — gb en a
   exactement onze. Le script mesure la somme des entrées, en déduit l'écart
   le plus large qui tient encore, et ne touche à rien quand ça ne tient pas :
   la valeur de repli ci-dessous reste alors celle qui s'applique. */
.menu {
  max-width: var(--largeur); margin:0 auto; padding:6px 24px 14px;
  display: flex; justify-content: center; flex-wrap: wrap; gap:8px var(--menu-ecart, 32px);
}
.menu a {
  text-decoration: none; color: var(--encre-douce); font-size: 15px;
  padding:4px 0; border-bottom: 2px solid transparent;
  /* En ligne SIMPLE, l'emoji venait se coller au libellé (« 👕Hommes ») : un
     espace insécable dans le gabarit aurait fait l'affaire à l'affichage, mais
     serait parti dans les copiés-collés et les lecteurs d'écran. C'est une
     affaire d'espacement, donc de feuille de style. */
  display: inline-flex; align-items: center; gap:6px;
}
/* AU SURVOL, DANS LA BARRE COMME DANS LE PANNEAU : le gras et l'encre d'accent
   (18/08/2026, « pareil pour le menu »). Pas de soulignement — dans une barre où
   tout est lien, souligner au survol n'apprend rien ; le trait de deux pixels
   reste réservé à la PAGE COURANTE, où il dit quelque chose. */
.menu > a:hover,
.menu-groupe > summary:hover { color: var(--accent-texte); text-decoration: none; }

/* ═══ AU SURVOL : LA COULEUR, ET RIEN QUE LA COULEUR ════════════════════════
   (18/08/2026, deuxième passe. « Change juste la couleur en rapport avec la
   charte sans gras. »)

   CE QUI A ÉTÉ ESSAYÉ ET RETIRÉ. Le survol a d'abord fait passer le libellé au
   gras, puis en fondu — un jeu de trois calques superposés qui croisait un
   maigre et un gras pour contourner le fait que `font-weight` ne s'anime pas de
   façon fiable sur une pile de polices système. Le mécanisme fonctionnait ; il
   n'aurait pas dû exister. Un menu n'a pas besoin de deux variables pour dire
   « c'est celui-ci » : la couleur d'accent le dit seule, elle se lit d'aussi
   loin, elle ne déplace rien, et elle s'anime nativement.

   Ce qui disparaît avec le gras : la copie fantôme qui réservait la largeur, la
   copie visible qui se superposait, l'attribut `data-nom` qui les alimentait —
   et le risque qu'une entrée se réorganise sous le curseur. Une transition qui
   demande trois exemplaires du même mot est une transition qu'on ne garde pas.

   LE GRAS RESTE À LA PAGE COURANTE, où il ne bouge pas et ne clignote pas :
   c'est un état, pas un survol. */
.menu > a,
.menu-groupe > summary,
.menu .menu-sous-item { transition: color .15s ease; }

.menu > a:hover,
.menu > a:focus-visible,
.menu-groupe > summary:hover,
.menu-groupe > summary:focus-visible { color: var(--accent-texte); text-decoration: none; }

@media (prefers-reduced-motion: reduce) {
  .menu > a, .menu-groupe > summary, .menu .menu-sous-item { transition: none; }
}

/* LA PAGE COURANTE SE VOIT AU GRAS ET À LA COULEUR, ET À RIEN D'AUTRE.
   Elle a d'abord gagné le gras et l'or EN PLUS du filet de deux pixels ; le
   filet est retiré à son tour (18/08/2026, « pas de sous-lignage »). Trois
   signaux pour une seule information, c'est deux de trop — et le trait est
   celui des trois qui se remarque le moins et pèse le plus : il ajoute une
   ligne graphique sous une barre qui n'en demandait pas.
   La règle `.menu a` garde son `border-bottom: 2px solid transparent` : il ne
   se voit jamais, mais il réserve sa place et empêche la barre de sursauter le
   jour où une variante le rallume. */
.menu a[aria-current="page"] {
  color: var(--accent-texte); font-weight: 600;
}

.bouton-icone {
  display: inline-flex; align-items: center; gap:7px;
  padding:7px 12px; border: 1px solid var(--filet); border-radius: var(--rayon);
  background: var(--surface); color: var(--encre); text-decoration: none;
  font-size: 14px; cursor: pointer; font-family: inherit;
}
/* `display: inline-flex` ci-dessus l'emporte sur le `[hidden] { display: none }`
   de la feuille du navigateur : sans cette règle, l'attribut `hidden` posé sur
   l'icône du comparateur serait ignoré sans un mot, et l'icône resterait
   affichée en permanence, vide. Même convention que .carrousel-fleches. */
.bouton-icone[hidden] { display: none; }
.bouton-icone:hover { border-color: var(--filet-fort); }
.bouton-icone svg { width: 17px; height: 17px; }

/* LE BADGE DE LA SOCIÉTÉ DES AVIS GARANTIS, DANS L'EN-TÊTE (20/08/2026) — voir
   shop-header.php pour le pourquoi (« fixe au défilement, mais sans script »)
   et pour les deux instances (normale / défilée).
   ⚠️ TROISIÈME PASSE (20/08/2026, « je n'aime pas le badge ») : la version
   précédente posait l'icône et le texte nus, sans cadre — et un `<a>` sans
   `text-decoration: none` explicite garde le SOULIGNEMENT PAR DÉFAUT du
   navigateur sur son contenu, ici le texte « 36 avis ». Ni voulu ni vu à
   temps : rien dans cette feuille ne réinitialise le style de lien par défaut
   au niveau du site, chaque composant le fait lui-même — celui-ci ne l'avait
   pas fait. Redevenu une VRAIE PASTILLE, du même vocabulaire que les autres
   éléments cliquables de l'en-tête (`.bouton-icone` : fond, bordure, coins
   arrondis) plutôt qu'une icône et un mot livrés à eux-mêmes. */
/* ⚠️ QUATRIÈME PASSE (20/08/2026) : plus de marges, le fond reprend celui du
   bandeau de congés (`.bandeau-vacances`, même recette `color-mix`) plutôt
   qu'un blanc plat — les deux sont des bandeaux d'information posés sur la
   page, ils gagnent à se reconnaître comme de la même famille. */
.bandeau-badge-avis {
  display: inline-flex; align-items: center; gap: 7px; flex: none;
  padding: 6px 14px 6px 8px; border-radius: 999px;
  /* PLUS PREMIUM : la teinte suit l'OR DES ÉTOILES (`--etoile`), pas l'accent —
     c'est un repère de NOTE, et l'or est son code universel. Léger dégradé chaud
     + filet doré + un soupçon de relief (ombre douce + reflet interne) pour que
     le badge « accroche » sans crier. */
  border: 1px solid color-mix(in srgb, var(--etoile) 32%, transparent);
  background:
    linear-gradient(180deg,
      color-mix(in srgb, var(--etoile) 10%, var(--fond)),
      color-mix(in srgb, var(--etoile) 20%, var(--fond)));
  box-shadow: 0 1px 2px rgba(16, 16, 24, .06), inset 0 1px 0 rgba(255, 255, 255, .35);
  text-decoration: none; color: inherit;
  transition: border-color .15s ease, transform .15s ease, box-shadow .15s ease;
}
.bandeau-badge-avis:hover,
.bandeau-badge-avis:focus-visible {
  border-color: color-mix(in srgb, var(--etoile) 55%, transparent);
  transform: translateY(-1px);
  box-shadow: 0 4px 12px -4px color-mix(in srgb, var(--etoile) 55%, transparent), inset 0 1px 0 rgba(255, 255, 255, .4);
}
/* HAUTEUR ALIGNÉE SUR `.bouton-icone` VOISIN (20/08/2026 — « badge trop large
   en hauteur par rapport aux icônes d'à côté ») : ce dernier mesure ~33px
   (7px de rembourrage + 17px de trait + bordure). La cocarde, plus haute que
   large dans son SVG d'origine, est réduite en conséquence — le rembourrage
   du badge suit à l'identique pour retomber sur le même total. */
.bandeau-badge-avis img { display: block; height: 20px; width: auto; flex: none; transform: translateY(1px); }
/* Sans certification, le badge perd la cocarde : il reste la note de la
   boutique, et le rembourrage se rééquilibre — sans image à gauche, les 8 px
   réservés à la cocarde laissaient l'étoile décentrée dans sa pastille. */
.bandeau-badge-avis.est-maison { padding-left: 13px; }
/* LE NOMBRE D'AVIS ET LA NOTE EN ÉTOILE, POUR UN BADGE « COMPLET » plutôt
   qu'une simple icône — demande de Jesse. N'existe que sur l'instance
   normale (voir shop-header.php pour pourquoi l'instance défilée reste
   icône seule). */
.bandeau-badge-avis-texte {
  display: inline-flex; align-items: baseline; gap: 5px;
  font-size: 12.5px; font-weight: 500; color: var(--encre-tenue); white-space: nowrap;
}
/* UNE seule étoile, dorée et légèrement présente (repère de note, pas décor). */
.bandeau-badge-avis-etoile { color: var(--etoile); font-size: 13px; line-height: 1; }
/* La NOTE ressort (gras, encre pleine) ; le « · 36 avis » reste discret. */
.bandeau-badge-avis-texte strong { font-weight: 700; font-size: 13px; color: var(--encre); letter-spacing: -0.01em; }
.bandeau-badge-avis-sep { color: var(--etoile); opacity: .7; margin: 0 1px; }
@media (max-width: 640px) {
  /* `.bandeau-outils` disparaît déjà sous 640 px (voir plus bas) : le badge
     s'efface avec lui, il n'a pas de traitement mobile à part. */
  .bandeau-badge-avis { display: none; }
}
.pastille {
  min-width: 19px; height: 19px; padding:0 5px; border-radius: 10px;
  background: var(--accent); color: var(--accent-encre);
  font-size: 11px; font-weight: 650; line-height: 19px; text-align: center;
}

/* LE COMPTE DU PANIER SE POSE SUR L'ICÔNE, il n'est plus à côté d'elle.
   En ligne, il élargissait le bouton et poussait toute la barre d'outils dès
   qu'un article entrait dans le panier — la rangée d'icônes se décalait à
   chaque ajout. En pastille superposée, le bouton garde sa largeur.

   LA COULEUR EST UN JETON, avec un bordeaux par défaut. Elle ne peut pas être
   dérivée de l'accent : sur cette charte l'accent EST l'or du panier, et un
   compte doré sur une icône dorée ne se remarque pas. Une marque qui veut une
   autre couleur d'alerte déclare `--marque-pastille-panier`. */
.bouton-icone { position: relative; }
[data-pastille="panier"] .pastille {
  position: absolute; top: -7px; right: -7px;
  /* UN BORDEAUX PLUS VIF. Le premier (#7C2233) était juste de teinte mais trop
     éteint : sur un bandeau ivoire, à dix-huit pixels, il se lisait comme une
     tache brune plutôt que comme un compteur. Celui-ci garde le bordeaux et
     gagne en saturation — c'est le seul élément de l'en-tête qui doive attirer
     l'œil sans qu'on le cherche. */
  background: var(--bordeaux);
  color: #FFFFFF;
  /* CENTRAGE PAR FLEX, PAS PAR INTERLIGNAGE. `line-height` égal à la hauteur
     centre la BOÎTE de ligne, pas le chiffre : selon les métriques de la police
     — et elles changent d'une charte à l'autre — le chiffre se pose un ou deux
     pixels trop bas. Ici il est centré quelle que soit la fonte. */
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 19px; height: 19px; line-height: 1; padding:0 5px;
  font-size: 11px; font-weight: 700;
  /* Un liseré de la couleur du bandeau détache la pastille du tracé de l'icône
     qu'elle chevauche : sans lui, les deux se touchent et forment une tache. */
  box-shadow: 0 0 0 2px var(--fond);
}

/* ═══ CONFIRMATION D'AJOUT AU PANIER ═════════════════════════════════════════
   En bas au centre, par-dessus la page. Elle remplace la redirection vers le
   panier : on confirme, on ne déplace pas.

   `position: fixed` et un z-index élevé : elle doit passer au-dessus de la
   barre d'achat collante du mobile, qui vit à 46, et de la bulle de chat.
   Elle reste sous les fenêtres modales (10000), qu'elle n'a aucune raison de
   recouvrir. */
.notice-panier {
  position: fixed; z-index: 9990;
  left: 50%; bottom: 24px; transform: translateX(-50%);
  display: flex; align-items: center; gap: 12px;
  max-width: min(560px, calc(100vw - 32px));
  padding: 12px 14px 12px 16px;
  border-radius: var(--rayon-large);
  background: var(--encre); color: var(--fond);
  box-shadow: 0 18px 44px -12px rgba(10, 10, 20, .5);
  font-size: 14.5px; line-height: 1.35;
  /* Elle entre par le bas puis s'efface d'elle-même : une confirmation qu'il
     faut fermer à la main est une corvée de plus après chaque ajout. Le retard
     de six secondes laisse le temps de lire ET de cliquer « Voir le panier ». */
  animation: notice-panier-entree .28s cubic-bezier(.16,.84,.44,1) backwards,
             notice-panier-sortie .4s ease 6s forwards;
}
@keyframes notice-panier-entree {
  from { opacity: 0; transform: translate(-50%, 14px); }
  to   { opacity: 1; transform: translate(-50%, 0); }
}
@keyframes notice-panier-sortie {
  to { opacity: 0; transform: translate(-50%, 10px); visibility: hidden; }
}
.notice-panier-icone {
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; flex: none; border-radius: 50%;
  background: var(--succes); color: #FFFFFF;
}
.notice-panier-texte { flex: 1 1 auto; min-width: 0; }
.notice-panier-texte strong { font-weight: 600; }
.notice-panier-lien {
  flex: none; color: var(--fond); text-decoration: underline;
  text-underline-offset: 3px; white-space: nowrap;
}
.notice-panier-fermer {
  flex: none; width: 26px; height: 26px; padding: 0;
  border: 0; border-radius: 50%; cursor: pointer;
  background: transparent; color: inherit; opacity: .6;
  font: inherit; font-size: 19px; line-height: 1;
}
.notice-panier-fermer:hover { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  /* On garde la disparition — c'est un changement d'état, pas une décoration —
     mais sans glissement. */
  .notice-panier { animation: notice-panier-sortie .01s ease 6s forwards; }
}
@media (max-width: 640px) {
  /* Au-dessus de la barre d'achat collante, qui occupe déjà le bas. */
  .notice-panier { bottom: 84px; }
}

/* ------------------------------------------------------------- boutons */
.bouton {
  display: inline-flex; align-items: center; justify-content: center; gap:8px;
  padding:11px 20px; border-radius: var(--rayon); border: 1px solid transparent;
  /* L'OR DE LA MARQUE, ET SON ENCRE FONCÉE.
     Deux demandes se sont croisées ici : « texte en blanc » et « utilise
     #C29A60 ». Elles ne tiennent pas ensemble — du blanc sur cet or donne
     2,6:1, très en dessous du minimum de 4,5 exigé pour du texte, et le
     libellé s'y dissout à la lecture comme à la photo. J'avais d'abord gardé
     le blanc en assombrissant le fond ; c'était sacrifier la couleur de la
     marque à l'encre, alors que c'est la couleur qui porte l'identité.

     C'est donc l'or qui reste, et l'encre qui cède : `--accent-encre` sur
     `--accent` donne 7,0:1. Si le blanc est indispensable, il faut assombrir
     le fond — les deux ne peuvent pas coexister. */
  background: var(--accent); color: var(--accent-encre);
  /* JAMAIS DE GRAS SUR UN BOUTON. À 550, le libellé épaississait un aplat déjà
     très présent ; c'est la couleur qui doit porter l'appel, pas la graisse. */
  font: inherit; font-size: 15px; font-weight: 400; text-decoration: none;
  cursor: pointer;
}
.bouton:hover { filter: brightness(1.06); }
.bouton:disabled { opacity: .5; cursor: not-allowed; filter: none; }
.bouton-secondaire { background: var(--surface); color: var(--encre); border-color: var(--filet-fort); font-weight: 400; }
.bouton-secondaire:hover { filter: none; border-color: var(--encre-tenue); }
.bouton-large { width: 100%; }
/* LE BOUTON D'ACHAT PREND LA MESURE DES DEUX ACTIONS QU'IL SURMONTE. Il était
   plus épais qu'elles (11/15 px contre 9/14), ce qui ajoutait un troisième
   rythme vertical à un bloc qui n'en demandait pas. Sa force ne vient pas de sa
   taille mais de son aplat de couleur — c'est le seul des trois qui en a un.

   LA HAUTEUR EST ÉCRITE, PAS DÉDUITE DU REMBOURRAGE. À rembourrage égal, le
   bouton mesurait encore quatre pixels de plus que ses voisins : il contient un
   EMOJI, dont la boîte de ligne est plus haute que celle d'un tracé SVG à la
   même taille de police. Quatre pixels ne se remarquent pas isolément, mais ils
   décalent le seul bord que l'œil suit — celui qui court le long des trois
   boutons. Une hauteur explicite rend l'alignement indépendant de ce que
   contiennent les boutons. */
.fiche-achat form .bouton-large {
  /* PLUS HAUT QUE LES DEUX ACTIONS QU'IL SURMONTE. Il avait été aligné sur
     elles pour que le bloc forme un rectangle net ; c'est la LARGEUR qui devait
     s'aligner, pas la hauteur. À 40 px il pesait exactement autant que
     « Ajouter à ma liste », alors qu'il porte l'achat. 52 px lui rendent son
     rang sans toucher à l'alignement des bords, qui reste acquis. */
  height: 52px; padding: 0 18px; font-size: 15px;
}

/* ---------------------------------------------------------------------------
   UN SEUL ANNEAU DE FOCUS, POUR TOUT CE QUI SE PARCOURT AU CLAVIER.

   Ce n'est PAS la correction d'un bug : rien dans cette feuille ne supprimait
   le contour, et le navigateur en dessine un par défaut. Mais ce défaut-là
   varie — épaisseur, couleur, forme selon Chrome, Safari ou Firefox — et sur
   un fond sombre comme le pied de page, l'anneau noir de Chrome disparaît.
   Un site qui se navigue au clavier ne peut pas dépendre de ce que le
   navigateur a décidé ce mois-ci.

   `:where()` maintient la spécificité à ZÉRO : les composants qui ont déjà
   leur propre traitement de focus — champs de formulaire, piste de carrousel,
   sélecteur de quantité, étoiles d'avis — continuent de gagner sans avoir à
   surenchérir. C'est un plancher, pas une surcharge.

   `:focus-visible` et non `:focus` : un bouton cliqué à la souris ne doit pas
   garder un anneau, sinon chaque clic laisse une trace que l'utilisateur lit
   comme un état actif.

   L'offset de 2 px décolle l'anneau du bord : posé dessus, il se confond avec
   la bordure des cartes et des boutons secondaires. */
:where(a, button, summary, [role="button"], [tabindex]):focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
/* Sur une surface qui porte déjà l'accent en fond, l'anneau d'accent serait
   invisible : on repasse à l'encre du bouton. */
:where(.bouton, .auth-ouverture a):focus-visible {
  outline-color: var(--accent-encre);
  outline-offset: -4px;
}

/* --------------------------------------------------------------- titres
   LES COUPURES DE LIGNE SONT TRAITÉES ICI, UNE FOIS, ET PAS TITRE PAR TITRE.
   Un titre qui laisse un mot seul sur sa dernière ligne, un chapô qui rejette
   un point tout seul : ce ne sont pas des fautes de contenu, ce sont des
   décisions du moteur de rendu, qui coupe au dernier mot qui entre. Les
   corriger dans le texte — en ajoutant un mot, en raccourcissant — ne tient
   que jusqu'au prochain changement de largeur.

   `text-wrap: balance` répartit les lignes d'un titre à peu près également au
   lieu de remplir la première ; `pretty` interdit la dernière ligne d'un seul
   mot dans un paragraphe. Les deux sont des améliorations progressives : un
   navigateur qui ne les connaît pas rend exactement ce qu'il rendait avant. */
/* ⚠️ ÉCHELLE RELEVÉE ET UNIFORMISÉE (20/08/2026, « les titres du site sont
   globalement trop petits, il faudrait uniformiser tout ça »). La base ne
   suffisait pas à elle seule à expliquer la plainte : DES DIZAINES de
   sections avaient chacune leur propre `font-size` de titre — 15, 17, 19,
   20, 21, 23px — posé au moment où chacune a été écrite, jamais réaccordé
   depuis. Trois jetons ci-dessous donnent un SEUL endroit où lire et changer
   l'échelle ; les composants qui avaient leur propre taille SANS bonne
   raison (un h2 de section qui ne faisait que reprendre approximativement la
   base d'alors) la perdent au profit de ces jetons. Restent volontairement
   À PART les libellés compacts d'interface — en-tête de panneau de filtres,
   sous-titres de formulaire — qui ne sont pas des titres de page ou de
   section mais des étiquettes, et n'ont jamais prétendu suivre cette échelle. */
:root {
  --titre-h1: clamp(30px, 3.6vw, 40px);
  --titre-h2: 27px;
  --titre-h3: 20px;
}
h1 {
  font-family: var(--police-titre);
  font-size: var(--titre-h1); line-height: 1.16; letter-spacing: -0.02em;
  font-weight: var(--marque-poids-titre, 700);
  margin:0 0 12px; text-wrap: balance;
}
h2 {
  font-family: var(--police-titre);
  font-size: var(--titre-h2); line-height: 1.22; letter-spacing: -0.015em;
  font-weight: var(--marque-poids-titre, 700);
  margin:0 0 14px; text-wrap: balance;
}
h3 {
  font-family: var(--police-titre); text-wrap: balance;
  font-size: var(--titre-h3); line-height: 1.3;
  font-weight: var(--marque-poids-titre, 700);
}
/* ═══ L'INTRODUCTION D'UNE RUBRIQUE OU D'UNE PAGE DE FACETTE ════════════════
   Ce bloc portait une seule déclaration de couleur et une mesure. Le résultat :
   trois à cinq paragraphes du même gris, au même corps, sans point d'entrée —
   un pavé qu'on saute pour aller aux produits. Or c'est le texte qui doit
   convaincre d'y descendre, et sur une page de facette c'est même le premier
   contenu que lisent les moteurs.

   TROIS CHOSES SEULEMENT, parce qu'un texte de rubrique n'est pas un article :
   un chapô qui donne un point d'entrée, une interligne de lecture, et un gras
   qui redevient un gras. Ni encadré, ni fond, ni filet — la page en porte déjà
   assez, et [[la charte]] proscrit l'ornement pour l'ornement. */
.sous-titre {
  color: var(--encre-douce);
  margin: 0 0 22px;
  /* PLEINE LARGEUR (23/09/2026, « il faudrait que les hauts de page soient
     en pleine largeur »). Deux étapes avant : 68ch, puis 85ch le 20/08 (« pas
     assez large ») — le bloc restait une colonne posée à gauche d'une page
     dont le titre, les pastilles de sous-rubriques et la grille de produits
     courent sur tout le conteneur. Il suit désormais le conteneur.
     SEULE EXCEPTION, voulue par Jesse : l'encart « baguier » garde ses 85ch
     (voir `.boc-baguier-wrapper`) — deux tuiles étirées sur toute la page
     ne se lisent plus comme deux boutons. */
  max-width: none;
  font-size: 15.5px; line-height: 1.68;
  text-wrap: pretty;
}
.sous-titre p { margin: 0 0 13px; }
.sous-titre > p:last-child { margin-bottom: 0; }
/* LE PREMIER PARAGRAPHE EST UN CHAPÔ. Un demi-point de plus et l'encre pleine
   suffisent à donner à l'œil un endroit où se poser ; sans lui, les cinq
   paragraphes se valent et aucun n'invite à commencer. */
.sous-titre > p:first-child {
  font-size: 17px; line-height: 1.6;
  color: var(--encre); margin-bottom: 16px;
}
/* LE GRAS REDEVIENT UN GRAS. Il était écrit dans la même encre atténuée que le
   reste : « bracelets en or d'occasion » ressortait à peine du paragraphe qui
   le porte, alors que c'est exactement le mot que l'auteur a voulu montrer. */
.sous-titre strong, .sous-titre b { color: var(--encre); font-weight: 600; }
.sous-titre a { color: var(--accent-texte); text-underline-offset: 3px; }
.sous-titre :is(h2, h3) {
  color: var(--encre); font-size: 19px; line-height: 1.3;
  margin: 26px 0 10px;
}
.sous-titre :is(h2, h3):first-child { margin-top: 0; }
.sous-titre :is(ul, ol) { margin: 0 0 13px; padding-left: 20px; }
.sous-titre li { margin-bottom: 5px; }
.sous-titre li::marker { color: var(--accent); }

/* CE QUE `text-wrap` NE PEUT PAS FAIRE, ET QUI DEMANDE LE BALISAGE.
   Un navigateur a le droit de couper après un trait d'union : « Écrivez-nous »
   devenait « Écrivez- » en fin de ligne et « nous » au début de la suivante,
   avec le soulignement du lien coupé en deux. Aucune propriété d'habillage ne
   l'en empêche — `hyphens: none` ne concerne que la césure AJOUTÉE, pas un
   trait d'union déjà écrit. Il faut donc le dire, sur l'élément concerné.

   La classe sert aussi à retenir la ponctuation qui suit un lien court : dans
   « Vous ne trouvez pas ? Écrivez-nous. », le point est un nœud de texte
   distinct, et il partait seul à la ligne. Enveloppé avec le lien, il ne peut
   plus s'en détacher. À réserver aux fragments courts : posée sur une longue
   phrase, la règle la ferait déborder de sa colonne. */
.insecable { white-space: nowrap; }

/* Les autres paragraphes courts de la boutique — mention sous un formulaire,
   invitation à se connecter, renvoi vers le contact au bas de la FAQ. Ils font
   deux ou trois lignes : c'est exactement la longueur où une dernière ligne
   d'un seul mot se voit. Le corps des descriptions produit est inclus, il est
   rédigé au même endroit et lu de la même façon.
   Le commentaire est AVANT la liste de sélecteurs, jamais entre elle et son
   accolade : glissé au milieu, il annule la règle entière sans rien signaler. */
.mention,
.faq-contact,
.avis-invite,
.avis-vide,
.fiche-description p {
  text-wrap: pretty;
}

/* LE RYTHME DE TÊTE DE PAGE, resserré d'un cran sous l'échelle.
   Quarante-quatre pixels au-dessus du titre et vingt-huit en dessous
   donnaient, une fois le chapô et sa propre marge ajoutés, cent
   pixels de blanc entre le titre d'une rubrique et son premier
   produit — sur une page dont c'est le seul contenu. Le titre est
   déjà séparé de l'en-tête par le bandeau : il n'a pas besoin d'être
   séparé deux fois. */
/* LE HAUT RESPIRE PLUS QUE LE BAS (22/09/2026, « ça paraît bien collé en
   haut, sur les pages CMS etc. »). Les 32 px ci-dessus venaient du resserrage
   décrit juste avant, qui visait le blanc SOUS le titre — il avait emporté
   celui du dessus avec lui, et le fil d'Ariane se retrouvait à trente pixels
   d'un bandeau qui porte déjà deux rangées. La valeur suit la largeur de la
   fenêtre : sur un téléphone, 32 px sous une barre étroite suffisent, et en
   prendre 52 repousserait le titre sous la ligne de flottaison. Le bas, lui,
   ne bouge pas : c'est le rythme titre → contenu, il a été réglé pour ça. */
.entete-page { padding: clamp(32px, 3.6vw, 52px) 0 20px; }
/* UN CADRE POUR LES PAGES LIBRES (20/08/2026, « pages annexes, légales...
   étrangement intégrées ») : même traitement que les autres blocs de contenu
   du site — surface, bordure, rayon — pour que les CGV, mentions légales,
   etc. cessent d'avoir l'air de texte posé nu sur le fond de la page. Les
   sous-titres numérotés du contenu importé (« 1. Définition »…) gagnent une
   ligne de séparation au-dessus : ce sont les seules coupures visuelles du
   document, et rien ne les distinguait jusqu'ici du reste du texte courant. */
.page-libre-corps {
  background: var(--surface); border: 1px solid var(--filet);
  border-radius: var(--rayon-large);
  padding: 28px 32px 32px;
  margin-bottom: 40px;
  /* ⚠️ UNE COLONNE DE LECTURE, ET NON TOUTE LA LARGEUR DE LA PAGE (4/09/2026,
     Jesse : « trop large, illisible »). Le cadre posé le 20/08 s'étirait sur
     les 1 392 px du conteneur : les CGV se lisaient sur cent soixante-cinq
     caractères par ligne, plus du double de ce qu'un œil suit sans perdre le
     début de la ligne suivante. Le gabarit demandait pourtant la bonne chose —
     `class="prose mesure-texte"` — mais `.page-libre-corps .prose` (0,2,0)
     annulait le `max-width` de `.mesure-texte` (0,1,0) juste en dessous : la
     mesure était écrite, appliquée, puis effacée trois lignes plus loin.

     C'est le CADRE qui se borne, pas le texte à l'intérieur : un texte étroit
     centré dans une carte large laisse deux marges vides qui n'appartiennent à
     rien. `72ch` est la mesure du site (voir `.mesure-texte`), plus les 2×32 px
     de rembourrage — ce qui reste dedans est bien la ligne de 72 caractères. */
  max-width: calc(72ch + 64px);
  margin-inline: auto;
}
/* L'en-tête suit la carte, sinon le titre et le fil d'Ariane restent au bord
   gauche d'un conteneur de 1 400 px pendant que le texte est au milieu. */
.contenu:has(.page-libre-corps) .entete-page {
  max-width: calc(72ch + 64px);
  margin-inline: auto;
}
/* UNE PAGE COMPOSÉE EN RÉCIT REPREND LA PLEINE LARGEUR : ses images d'ouverture
   et ses bandes de vues sortent du cadre par des marges négatives, et une
   colonne de lecture les réduirait à des vignettes. Le sélecteur porte sur
   `.recit`, qui n'est écrit que par ce type de page. */
.page-libre-corps:has(.recit),
.contenu:has(.recit) .entete-page { max-width: none; }
/* L'EN-TÊTE DES PAGES DE LECTURE RESPIRE (25/09/2026, Jesse : « les marges
   supérieures des pages CMS sont trop collées, c'est bizarre »). Mesuré : le
   haut était IDENTIQUE à celui d'une rubrique (fil à 51 px du menu, titre
   10 px dessous, date 12 px plus bas). C'est le contraste qui gênait : sous
   ce haut serré, une colonne étroite où chaque article est précédé de 70 à
   80 px d'air — le titre avait l'air collé au menu. On donne à l'en-tête le
   même souffle que le corps, sur les pages libres comme sur les gabarits
   légaux ; les rubriques et fiches ne changent pas. */
.contenu:has(.page-libre-corps) .entete-page,
.contenu:has(> .prose.mesure-texte) .entete-page {
  padding-top: clamp(48px, 6vw, 92px);
  padding-bottom: 32px;
}
.contenu:has(.page-libre-corps) .entete-page .fil,
.contenu:has(> .prose.mesure-texte) .entete-page .fil { margin-bottom: 18px; }
.contenu:has(.page-libre-corps) .entete-page h1,
.contenu:has(> .prose.mesure-texte) .entete-page h1 { margin-bottom: 20px; }
/* ET LE BAS AUSSI (25/09/2026, même signalement de Jesse, en bas de page).
   Mesuré : sur un gabarit légal, le dernier paragraphe tombait à 18 px du
   filet du bandeau d'avis (rien sous la colonne `.prose`) ; sur une page libre,
   la carte n'avait que 40 px, contre 64 sous une rubrique. Même souffle qu'en
   haut de page. */
.contenu:has(> .prose.mesure-texte) { padding-bottom: clamp(64px, 7vw, 96px); }
.page-libre-corps { margin-bottom: clamp(64px, 7vw, 96px); }
.page-libre-corps .prose { max-width: none; }

/* ─── LA COMPOSITION DU DOCUMENT (4/09/2026, Jesse : « pas convaincu par la
   présentation des titres, marges »).

   ⚠️ LA RÈGLE PRÉCÉDENTE NE S'APPLIQUAIT À RIEN. Elle visait `h2` ; le contenu
   repris de PrestaShop n'écrit que des `h3` — dix-sept sur cette page, zéro
   `h2`. Les intertitres numérotés (« 1. Définition », « 2. Nos produits »)
   tombaient donc dans la règle générique `.prose :is(h2,h3)` : même corps ou
   presque que le texte courant, vingt-quatre pixels au-dessus, dix en dessous.
   Rien ne les distinguait d'un paragraphe en gras, et un document de dix-sept
   articles se lisait comme une coulée continue. Les DEUX niveaux sont donc
   traités : quel que soit celui qu'emploie le contenu importé, il se voit.

   TROIS RÉGLAGES, ET AUCUN N'EST DÉCORATIF :
   · le TIRET D'ACCENT au-dessus plutôt qu'un filet pleine largeur — dix-sept
     filets dans une carte déjà bordée donnaient une grille de tableau, là où un
     tiret court ouvre l'article sans le cloisonner ;
   · l'ÉCART EST ASYMÉTRIQUE (42 px au-dessus, 14 en dessous) : un intertitre
     appartient au texte qui le SUIT. À écart égal, il flotte entre les deux et
     l'œil doit décider à qui il se rattache ;
   · l'INTERLIGNE DU CORPS passe à 1,75 et le blanc inter-paragraphes à 18 px :
     sur un texte juridique qu'on parcourt en cherchant une clause, c'est le
     blanc qui donne les points d'accroche. */
/* ⚠️ LA MÊME COMPOSITION POUR LES DEUX FAMILLES DE PAGES LÉGALES, et il en
   existe bien deux : celle que le marchand a posée depuis le back-office
   (`.page-libre-corps`, dans une carte) et celle que la boutique génère
   elle-même (`.contenu > .prose.mesure-texte`, sans carte — mentions légales,
   CGV, confidentialité, cookies). Elles se suivent dans le pied de page : les
   composer différemment ferait croire à deux sites. */
.page-libre-corps { padding: 36px 40px 40px; }
:is(.page-libre-corps .prose, .contenu > .prose.mesure-texte) p { margin: 0 0 18px; line-height: 1.75; }
:is(.page-libre-corps .prose, .contenu > .prose.mesure-texte) :is(h2, h3) {
  font-family: var(--police-titre);
  color: var(--encre);
  line-height: 1.3;
  margin: 42px 0 14px;
  /* Remise à zéro de l'ancien filet, au cas où une règle plus haut le repose. */
  padding-top: 0; border-top: 0;
}
/* ⚠️ LE NUMÉRO SORT DU TITRE ET DEVIENT UN REPÈRE (4/09/2026, choix de Jesse
   parmi quatre présentations). Dans un contrat de dix-sept articles, ce qu'on
   cherche n'est pas « Prix et disponibilité » mais « l'article 7 » : rendu en
   pastille, le numéro se balaie du regard le long de la colonne, alors que
   noyé en tête de phrase il se lit à la même vitesse que le reste.

   La pastille est posée par `numeros_en_pastille()` au rendu, pas écrite dans
   le contenu : le marchand continue de saisir « 7. Prix et disponibilité »
   comme il l'a toujours fait, et le texte reste exportable tel quel. */
:is(.page-libre-corps .prose, .contenu > .prose.mesure-texte) :is(h2, h3) .num {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px;
  margin-right: 12px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--accent) 18%, transparent);
  color: var(--accent-texte);
  font-family: var(--police); font-size: 13px; font-weight: 650;
  vertical-align: 2px;
}
/* LE TIRET RESTE POUR LES INTERTITRES SANS NUMÉRO — et il y en a : les pages
   reprises mélangent « 1. Définition » et « Identification de l'entreprise ».
   Sans ce repli, un titre non numéroté n'aurait plus aucune ouverture et
   flotterait entre deux paragraphes. */
:is(.page-libre-corps .prose, .contenu > .prose.mesure-texte) :is(h2, h3):not(:has(.num))::before {
  content: '';
  display: block; width: 26px; height: 2px;
  margin-bottom: 16px; border-radius: 2px;
  background: var(--accent);
}
:is(.page-libre-corps .prose, .contenu > .prose.mesure-texte) h2 { font-size: 24px; }
:is(.page-libre-corps .prose, .contenu > .prose.mesure-texte) h3 { font-size: 22px; }
/* Un intertitre en tête de document n'ouvre rien : il EST le début. */
:is(.page-libre-corps .prose, .contenu > .prose.mesure-texte) > :first-child { margin-top: 0; }
:is(.page-libre-corps .prose, .contenu > .prose.mesure-texte) > :is(h2, h3):first-child::before { display: none; }
/* Le récit a sa propre composition (titres sans tiret, sortis de la carte) :
   il reprend la main sur ces trois réglages. */
.page-libre-corps .prose .recit :is(h2, h3)::before { display: none; }
@media (max-width: 640px) { .page-libre-corps { padding: 20px 18px 24px; } }

/* --- En-tête d'article illustré (12/08/2026) ---
   La bannière est passée EN FOND DU TITRE, comme sur les guides de
   boostmypresta.com. Trois précautions, dans l'ordre où elles se sont
   imposées là-bas :
   · le voile n'est pas décoratif — les visuels sont clairs (le remplacement
     est un aplat gris) et un titre clair posé dessus disparaîtrait. Il impose
     donc un fond sombre franc, et l'en-tête illustré bascule en encre claire
     quel que soit le thème du site, contrairement à tout le reste ;
   · le fond est un ÉLÉMENT et non un `background-image` : c'est ce qui permet
     à l'image de garder son `fetchpriority` et ses dimensions natives ;
   · le contenu est remonté au-dessus par `position: relative`, sans quoi le
     voile passerait par-dessus le titre. */
.entete-illustree {
  position: relative;
  isolation: isolate;
  padding: 64px 28px 44px;
  margin-bottom: 32px;
  border-radius: var(--rayon-large);
  overflow: hidden;
  color: #F2F6FC;
}
/* Le hero d'article sort de la colonne (16/08/2026, « reprendre la structure
   de BMP et PSF ») : bandeau bord à bord comme les guides des vitrines, texte
   dans un conteneur aligné sur la colonne de lecture (760, celle de .article).
   La carte arrondie qui flottait au milieu de la page disparaît — le rayon et
   le padding horizontal n'ont plus de sens sur un bloc pleine largeur. */
.article-entete.entete-illustree {
  border-radius: 0;
  padding: 76px 0 52px;
  margin-bottom: 40px;
}
.article-entete .entete-contenu { max-width: 760px; margin: 0 auto; padding: 0 24px; }
@media (max-width: 700px) {
  .article-entete.entete-illustree { padding: 48px 0 36px; }
}
.entete-illustree > *:not(.entete-fond) { position: relative; z-index: 1; }
.entete-illustree h1,
.entete-illustree .article-chapo { color: #F2F6FC; }
.entete-illustree .fil,
.entete-illustree .fil a,
.entete-illustree .article-meta,
.entete-illustree .article-meta a { color: rgba(242, 246, 252, 0.78); }
.entete-illustree .fil a:hover,
.entete-illustree .article-meta a:hover { color: #FFFFFF; }
.entete-fond { position: absolute; inset: 0; z-index: 0; line-height: 0; pointer-events: none; }
.entete-fond img { width: 100%; height: 100%; object-fit: cover; display: block; }
.entete-fond::after {
  content: '';
  position: absolute;
  inset: 0;
  /* VOILE ALLÉGÉ LE 12/08/2026, et c'est le visuel qui a changé, pas le goût.
     Il était à 0,80–0,93 pour enterrer un aplat gris clair frappé d'un
     pictogramme de document, qui sinon traversait juste au-dessus du titre.
     La bannière est maintenant un indigo profond (tools/visuels-articles.php) :
     à 0,80 elle disparaissait sous le voile et l'en-tête n'était plus qu'une
     bande noire. Le voile n'a plus à cacher quoi que ce soit : l'image est déjà
     sombre, il ne sert qu'à égaliser le fond sous le texte. D'où 0,28–0,48.
     Le contraste tient largement — mesuré sur le point le plus clair de
     l'image, la lueur en haut à droite, là où le voile est le plus mince :
     hsl(226 52% 34%) sous 28 % de noir donne encore 12:1 avec le blanc du
     titre, pour un seuil AA de 4,5. */
  /* VOILE RENFORCÉ (17/08/2026) — ET C'EST L'IMAGE QUI A CHANGÉ, PAS LE GOÛT.
     Les valeurs précédentes (0,28–0,48) avaient été calibrées sur les
     bannières INDIGO que le projet générait lui-même : sombres, unies, sans
     texte. La reprise de la boutique a apporté de vraies photographies — fond
     clair, forte matière, et surtout un TITRE déjà imprimé dessus. Le titre de
     la page se superposait alors à celui de l'image, et l'accroche se lisait
     par-dessus des lettres.

     Un voile calibré pour une image précise n'est pas un voile : c'est un
     réglage qui tient tant que l'image ne change pas. Celui-ci est calibré pour
     le PIRE cas — une photo claire et chargée — parce que c'est la seule
     hypothèse qui reste vraie quand le marchand change ses visuels. */
  background: linear-gradient(180deg, rgba(8, 9, 13, .52) 0%, rgba(8, 9, 13, .62) 50%, rgba(8, 9, 13, .74) 100%);
}
/* LA PHOTO EST LÉGÈREMENT FLOUTÉE, et pas seulement assombrie. Un voile plus
   opaque suffirait au contraste, mais pas à la LISIBILITÉ : le texte imprimé
   dans l'image reste net et continue de concurrencer le titre. Deux pixels de
   flou l'effacent en tant que texte tout en gardant la matière et la couleur
   de la photo. L'agrandissement de 3 % évite la frange claire que le flou
   laisse sur les bords. */
.entete-fond img {
  filter: blur(2px) saturate(.92);
  transform: scale(1.03);
}
@media (max-width: 700px) {
  .entete-illustree { padding: 40px 20px 32px; }
}
/* ⚠️ REDESSINÉ (20/08/2026, « largement améliorable ») : les liens du fil
   d'Ariane ne se distinguaient en rien du texte autour — même gris, aucun
   soulignement avant le survol. Rien ne disait « ceci mène ailleurs » avant
   d'y passer la souris. Colorés dans l'accent, comme tous les autres liens
   de contenu du site (`.sous-titre a`, `.prose a`…) — pas un traitement à
   part, juste celui qui manquait ici. */
.fil { font-size: 13.5px; color: var(--encre-tenue); margin-bottom:10px; letter-spacing: .01em; }
/* SOULIGNÉS, ET PAS SEULEMENT AU SURVOL (18/09/2026, audit d'accessibilité
   « Links rely on color to be distinguishable »). Dans une ligne de texte, un
   lien qui ne se signale que par sa couleur n'existe pas pour qui ne distingue
   pas cette couleur — et le fil d'Ariane est précisément une ligne de texte où
   deux mots sur trois sont cliquables. Le soulignement est décalé et fin pour
   ne pas alourdir une ligne de 13,5 px. */
.fil a {
  color: var(--accent-texte);
  text-decoration: underline; text-underline-offset: 2px;
  text-decoration-thickness: 1px; text-decoration-color: color-mix(in srgb, var(--accent-texte) 45%, transparent);
}
.fil a:hover { text-decoration-color: currentColor; }

/* ------------------------------------------------------------ catalogue */
.grille-produits {
  display: grid; gap:20px;
  grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  padding-bottom:48px;
}
.carte-produit {
  border: 1px solid var(--filet); border-radius: var(--rayon-large);
  background: var(--surface); overflow: hidden;
  text-decoration: none; color: inherit;
  display: flex; flex-direction: column;
  transition: border-color .15s ease, transform .15s ease;
}
/* AU SURVOL, LE VISUEL AVANCE. Un simple changement de teinte de filet se voit
   à peine sur une grille claire ; l'ombre portée et le très léger agrandissement
   de l'image disent « ceci s'ouvre » à la vitesse où l'œil parcourt une grille.
   L'agrandissement reste sous 3 % : au-delà, l'image se met à respirer et le
   catalogue devient fatigant. */
.carte-produit:hover {
  border-color: var(--filet-fort);
  transform: translateY(-3px);
  box-shadow: 0 10px 26px rgba(16, 16, 24, .09);
}
.carte-visuel {
  /* PORTRAIT, ET NON PAYSAGE. Le 4/3 vient des catalogues de mode, où l'on
     photographie un vêtement porté, en largeur. Un objet — bijou, flacon,
     montre, luminaire — se photographie DEBOUT : les visuels arrivent en 3/4
     ou en carré, et un cadre en paysage les rognait par le haut et par le bas.
     Sur un bracelet posé à plat, on perdait le fermoir ; sur un collier, la
     moitié de la chaîne.

     Le 4/5 est le compromis : assez haut pour montrer la pièce entière, assez
     court pour qu'une grille de trois colonnes tienne dans un écran. Le carré
     aurait fait des cartes plus hautes encore sans rien montrer de plus, la
     plupart des sources étant en 3/4.

     LE RÉGLAGE EST UN JETON pour qu'une boutique dont les visuels sont en
     paysage — du mobilier, des tableaux — puisse revenir au 4/3 depuis son
     profil de marque, sans toucher à cette feuille. */
  aspect-ratio: var(--marque-ratio-visuel, 4 / 5);
  /* LE FOND DU CADRE EST UN JETON (25/08/2026). En `cover`, on ne le voit
     jamais : l'image couvre tout. En `contain`, il occupe ce que l'image ne
     remplit pas — et le fond doux de la charte, teinté, encadrait alors chaque
     packshot d'un liseré coloré. Une boutique dont les visuels sont détourés
     sur blanc met `#fff` et les images se fondent dans leur cadre. */
  background: var(--marque-fond-visuel, var(--fond-doux));
  display: flex; align-items: center; justify-content: center;
  color: var(--encre-tenue); font-size: 13px;
  overflow: hidden;
}
/* `<picture>` EST TRANSPARENT À LA MISE EN PAGE (20/08/2026, premier gabarit
   avec source AVIF — voir Vignette::avif()). `display: contents` le retire de
   l'arbre de boîtes : l'`<img>` qu'il contient se comporte comme s'il était
   directement l'enfant de `.carte-visuel`, exactement comme avant l'ajout du
   `<picture>` — sans ça, `width/height: 100%` sur l'image se serait résolu
   contre une boîte `<picture>` sans taille propre, pas contre `.carte-visuel`. */
.carte-visuel picture { display: contents; }
/* ⚠️ LE RECADRAGE EST UN JETON DE MARQUE (25/08/2026, « il y a toujours des
   produits trop zoomés sur les miniatures »).

   `cover` remplit le cadre et rogne ce qui dépasse : c'est ce qu'on veut quand
   les visuels sont photographiés dans le même format que le cadre — une
   bijouterie qui shoote ses pièces debout, en 3/4. C'est exactement ce qu'on ne
   veut PAS quand le catalogue vient d'un fournisseur : des packshots carrés,
   des photos paysage, des visuels rognés d'usine. Dans un cadre 4/5, une image
   panoramique perd la moitié de sa largeur — le bonbon occupe tout le cadre et
   sort des deux côtés, et le client voit un gros plan là où il attend un
   produit.

   `contain` ne rogne rien : l'image entière tient dans le cadre, le reste est
   du fond. Cela laisse des marges sur les visuels qui ne sont pas au bon
   format — c'est le prix, et il est plus honnête qu'un cadrage arbitraire.

   Le choix se fait donc PAR BOUTIQUE, dans le profil de marque, à côté du ratio
   du cadre (`--marque-ratio-visuel`) qui existait déjà pour la même raison.
   Défaut inchangé : `cover`, pour ne rien bouger là où c'est juste. */
.carte-visuel img {
  width: 100%; height: 100%;
  object-fit: var(--marque-ajustement-visuel, cover);
  transition: transform .35s cubic-bezier(.2, .7, .3, 1);
}

.carte-produit:hover .carte-visuel img { transform: scale(1.03); }
/* MODULE carte_survol_image : fondu simple de la 2e image.
   Spécificité `img.carte-visuel-survol` (0,2,1) nécessaire pour écraser
   la `transition: transform` générale de `.carte-visuel img` (0,1,1). */
.carte-visuel { position: relative; }
.carte-visuel img.carte-visuel-survol {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: var(--marque-ajustement-visuel, cover);
  opacity: 0;
  transform: none;
  transition: opacity .3s cubic-bezier(.4, 0, .2, 1);
}
.carte-produit:hover .carte-visuel img.carte-visuel-survol {
  opacity: 1;
  transform: none;
}
@media (prefers-reduced-motion: reduce) {
  .carte-visuel img.carte-visuel-survol { transition: none; opacity: 0; }
  .carte-produit:hover .carte-visuel img.carte-visuel-survol { opacity: 1; }
}

.carte-corps { padding:14px 16px 16px; display: flex; flex-direction: column; gap:5px; flex: 1; }

/* DEUX LIGNES AU PLUS, POUR LE NOM COMME POUR L'ACCROCHE. Sans limite, une
   description de trois lignes repoussait le prix vers le bas et donnait à
   chaque carte une hauteur différente : la grille perdait sa ligne de base, et
   le prix — l'information qu'on vient chercher — se retrouvait à un endroit
   différent sur chaque produit. */
.carte-nom {
  font-weight: 560; font-size: 15px; line-height: 1.35;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
.carte-accroche {
  font-size: 13.5px; line-height: 1.45; color: var(--encre-tenue); margin:0;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
.carte-prix { margin-top:auto; padding-top:10px; display: flex; align-items: baseline; gap:8px; flex-wrap: wrap; }
/* LE PRIX EST L'ANCRE DE LA CARTE. À 17 px et 650, il pesait le même poids que
   le nom juste au-dessus : rien ne guidait l'œil. Deux points de plus et un
   resserrement suffisent à le détacher sans crier. */
.prix { font-weight: 680; font-size: 19px; letter-spacing: -0.015em; }
.prix-barre { color: var(--encre-tenue); text-decoration: line-through; font-size: 14px; }

/* ------------------------------------------------- moyens de paiement ---
   Repris tel quel du site vitrine (src/Templates/partials/payment-badges.php),
   couleurs de marque comprises : un badge Visa n'est pas un élément de charte
   du site, c'est le logo d'un tiers, et le teinter aux jetons de la boutique
   le rendrait méconnaissable — donc inutile, puisque tout son intérêt est
   d'être reconnu d'un coup d'œil.
   Le conteneur reste à la page appelante ; ce bloc n'habille que les badges. */
/* 6 px d'écart et non 8 : avec six moyens de paiement, l'écart cumulé décidait
   à lui seul du passage à la ligne dans la colonne de commande. */
.payment-icons { display: inline-flex; align-items: center; flex-wrap: wrap; gap:6px; }
/* ⚠️ RÉÉCRIT LE 26/08/2026, EN MÊME TEMPS QUE payment-badges.php.
   Chaque badge est maintenant un FICHIER qui porte sa propre carte — fond,
   angles et couleurs comprises. Tout ce que faisait cette section a donc
   disparu avec les formes qu'elle habillait : le fond bleu nuit de Visa,
   l'italique de son mot, les deux cercles en recouvrement de Mastercard, la
   case élargie de PayPal et celle de Stripe.

   CE QUI RESTE TIENT EN DEUX RÈGLES, et c'est le but. Les dimensions internes
   écrites badge par badge étaient un piège : elles supposaient une case de
   42 × 26, et la première tentative de réduire cette case (bande de
   réassurance, 26/08/2026) a fait déborder Mastercard et coupé PayPal. Une
   image en `object-fit: contain` suit sa case à toutes les échelles ; il n'y a
   plus de cote à recalculer ailleurs.

   PAS DE FOND NI DE BORDURE ICI : les cartes en portent déjà, et en ajouter
   reviendrait à encadrer un badge encadré. C'est la leçon d'Apple Pay, dont la
   case avait justement dû être vidée pour cette raison. */
.payment-icon {
  display: inline-flex; align-items: center; justify-content: center;
  box-sizing: border-box;
  /* 42 × 27 : le rapport exact des fichiers (208 × 134), pour qu'aucune carte
     ne laisse de filet vide au-dessus et en dessous d'elle. */
  width: 42px; height: 27px; flex: none;
}
.payment-icon img {
  /* Bornes ABSOLUES, jamais en pourcentage : dans un conteneur flex, un
     pourcentage se résout contre zéro et le logo disparaît sans qu'aucune
     erreur ne le signale. */
  width: 42px; height: 27px; display: block; object-fit: contain;
}
/* --- Réassurance ---
   Seule .reassurance avait une règle : .reassurance-point, -icone et -corps
   n'en avaient AUCUNE. D'où l'alignement en escalier et l'impression de bloc
   rapporté — quatrième occurrence du même défaut dans ce projet, après le
   formulaire de contact, les pages légales et l'espace client.

   L'ICÔNE EST DANS SA PROPRE COLONNE DE GRILLE, de largeur fixe : tous les
   textes commencent alors à la même abscisse, ce qu'un simple flex avec un
   emoji ne donne jamais — chaque emoji ayant sa propre chasse. */
.reassurance {
  list-style: none;
  margin:16px 0 0;
  padding:16px 0 0;
  border-top: 1px solid var(--filet);
  font-size: 13px;
  color: var(--encre-tenue);
}
.reassurance-point {
  display: grid;
  /* 34 px : la largeur de la pastille, posée le 16/08/2026 à la place des
     20 px de l'icône nue. */
  grid-template-columns: 34px minmax(0, 1fr);
  gap:12px;
  align-items: start;
}
.reassurance-icone {
  /* L'ENCRE VIENT DE LA PASTILLE, elle n'est plus écrite ici. Le blanc était
     posé en dur « parce que le fond est coloré » — vrai d'un bleu soutenu, faux
     de l'or d'une charte de bijouterie, où il tombe à 2,6:1 et disparaît. La
     pastille sait, elle, sur quelle couleur elle repose : c'est à elle de dire
     quelle encre s'y lit. */
  color: inherit;
  display: block;
  flex: 0 0 auto;
}
/* LA PASTILLE. Un aplat dégradé plutôt qu'un aplat plat : sur 34 px, une seule
   teinte paraît imprimée, deux tons proches donnent le relief d'un objet. Les
   couleurs sont FIXES et non prises dans la palette de marque — ce sont des
   repères de nature (livrer, rendre, payer, demander), pas des éléments de
   charte ; elles doivent rester reconnaissables même si la boutique change
   d'accent. Icône blanche : le contraste tient dans les deux thèmes sans
   variante nuit. */
.reassurance-pastille {
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px;
  border-radius: 11px;
  /* Optiquement calée sur la première ligne de texte, comme l'icône avant elle. */
  margin-top: 1px;
}
/* LES PASTILLES SONT AUX COULEURS DE LA MARQUE, ET TOUTES LA MÊME.
   Elles étaient bleue, violette, verte et turquoise — des « repères de nature »
   choisis pour rester reconnaissables quelle que soit la boutique. Sur une
   charte or, quatre couleurs franches qui n'appartiennent à aucune palette du
   site ressemblent moins à un système qu'à quatre icônes empruntées ailleurs.

   UNE SEULE TEINTE POUR LES QUATRE, ET C'EST L'ICÔNE QUI DISTINGUE. Décliner
   l'accent en quatre ors arbitraires aurait rendu les pastilles moins lisibles,
   pas plus : entre deux ors voisins, personne ne lit une différence de sens.
   Le camion, la flèche de retour, le cadenas et la bulle, eux, se reconnaissent
   d'un coup d'œil — c'est là que doit vivre la distinction.

   PAS D'OMBRE NI DE FLOU : sur 34 px, un dégradé prononcé et une ombre portée
   ne produisent pas du relief mais du flou, et la pastille paraît hors du plan
   de la page. Le dégradé conservé est de deux tons voisins du même or, ce qui
   donne juste assez de matière pour que la pastille ne soit pas un aplat mort.

   L'ENCRE DE L'ICÔNE PASSE AU FONCÉ, et ce n'était pas un choix esthétique :
   une icône BLANCHE sur l'or de la marque tient un contraste de 2,6:1, sous le
   minimum de 3:1 exigé pour un élément graphique porteur de sens. Sur l'encre
   d'accent, elle atteint 7:1. */
.reassurance-pastille {
  /* UN APLAT, PLUS DE DÉGRADÉ DU TOUT.
     Deux tentatives ont échoué avant celle-ci, et pour la même raison : sur
     34 px, un dégradé ne se lit pas comme du relief mais comme une salissure —
     la pastille paraît mal imprimée. Le premier allait du bleu au violet, hors
     charte ; le second, d'un or très clair à l'or de la marque, faisait
     disparaître l'icône dans sa moitié claire. Un carré de couleur de trente
     pixels n'a pas besoin de modelé : il a besoin d'être net.

     L'encre est celle que la charte associe à son accent, et c'est ce qui rend
     la règle valable pour la boutique suivante quelle que soit sa couleur. */
  background: var(--accent);
  color: var(--accent-encre);
}
.reassurance-corps { display: block; min-width: 0; }
.reassurance-corps strong {
  display: block;
  color: var(--encre);
  font-size: 13.5px;
  font-weight: 600;
  line-height: 1.35;
}
.reassurance-corps span { display: block; line-height: 1.5; }

/* LA SECONDE LIGNE D'UN ARGUMENT — l'horaire sous le numéro, le délai sous le
   mode de livraison. Elle doit se lire APRÈS, pas en même temps : un ton plus
   clair et un cran plus petit suffisent, sans quoi les deux lignes se disputent
   le même poids et l'argument principal disparaît dans le détail. */
.reassurance-detail {
  color: var(--encre-attenuee, var(--fg-muted, #6b6b73));
  font-size: .92em;
  margin-top: 1px;
}

/* Le numéro reste du texte : même graisse, même couleur d'accent, souligné au
   survol seulement. Un lien bleu souligné dans une bande de réassurance
   ressemblerait à une pièce rapportée. */
.reassurance-tel { color: inherit; text-decoration: none; }
.reassurance-tel:hover, .reassurance-tel:focus-visible { text-decoration: underline; }

/* En colonne — panier, tunnel, fiche produit. */
.reassurance-colonne { display: flex; flex-direction: column; gap:14px; }

/* En ligne — sous un bouton d'achat. Les points se répartissent, et repassent
   en colonne dès que la place manque : trois arguments écrasés sur deux lignes
   chacun sont moins lisibles qu'empilés. */
.reassurance-ligne {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap:16px 24px;
}

/* ─── EN BANDE, AU BAS DE TOUTES LES PAGES (25/08/2026) ──────────────────────
   L'encadré de l'accueil ne convenait pas ici : posé sur chaque page, un gros
   bloc à fond coloré et bord arrondi devient un second pied de page qui se
   dispute la fin de la lecture avec le vrai. Et il portait DEUX cadres pour un
   seul contenu — la carte, plus le filet supérieur de `.reassurance` à
   l'intérieur — ce qui est précisément ce qui se lisait comme « mal intégré ».

   La bande, elle, ne s'encadre pas : elle se sépare de ce qui précède par un
   filet et se pose sur un fond à peine distinct, comme une bordure de page.
   Elle appartient au bas de page plutôt qu'elle ne s'y ajoute. */
.bande-reassurance {
  border-top: 1px solid var(--filet);
  background: var(--fond-doux);
}
.bande-reassurance-interieur {
  /* `--largeur` et le padding de 24 px : les mêmes que `.contenu` et
     `.pied-interieur`, pour que la bande s'aligne sur les colonnes du pied et
     non sur une largeur à elle. */
  max-width: var(--largeur); margin: 0 auto;
  padding: 22px 24px;   /* 24 px : EXACTEMENT le rembourrage de .pied-interieur */
}
/* Le filet et les marges de `.reassurance` sont NEUTRALISÉS : c'est la bande
   qui porte la séparation, pas la liste. */
/* UN FLUX SOUPLE, PAS UNE GRILLE. `auto-fit` découpe la largeur en autant de
   colonnes de 210 px qu'elle peut, puis y range les points : à 1 700 px cela
   faisait cinq pistes pour quatre arguments, donc une rangée qui s'arrête aux
   trois quarts avec un vide à droite. En flux, les quatre points se PARTAGENT
   la largeur — et la bande reste juste avec trois arguments, ce qui arrive dès
   qu'une boutique ne renseigne pas son délai de livraison. */
/* LA MÊME GRILLE QUE LE PIED DE PAGE, et c'est tout le sujet.
   Deux versions ont échoué avant celle-ci, chacune en corrigeant le défaut de
   l'autre :
     - étirés sur des pistes égales (`auto-fit`), les arguments gardaient leur
       texte calé à gauche dans des colonnes bien plus larges qu'eux : trois
       blocs isolés et un grand vide à droite ;
     - centrés en groupe, ils formaient un amas flottant au-dessus d'un pied de
       page à quatre colonnes calées sur les bords du conteneur. Rien n'était
       « faux », et pourtant l'ensemble ne se lisait pas comme structuré : deux
       bandes voisines suivaient deux rythmes horizontaux différents.

   Elles partagent donc les MÊMES pistes (1.5fr 1fr 1fr 1fr) et le même
   gouttière : chaque argument se pose exactement au-dessus d'une colonne du
   pied, et les deux bandes deviennent une seule structure. Le premier argument
   est plus large que les trois autres, comme la colonne d'identité — c'est
   celui qui porte la livraison, la phrase la plus longue. */
/* ⚠️ QUATRIÈME FORME (25/08/2026, demande de Jesse). La troisième — les mêmes
   pistes que le pied de page, 1.5fr 1fr 1fr 1fr, texte calé à gauche — corrigeait
   le rythme horizontal mais laissait le défaut qu'elle voulait résoudre : quand
   une boutique ne renseigne pas son délai de livraison, il reste TROIS arguments
   dans QUATRE pistes. Le premier flotte alors seul dans une piste une fois et
   demie trop large, et la rangée s'arrête aux trois quarts. C'est ce vide que
   Jesse a vu, pas le dessin des pastilles.

   DES BLOCS SÉPARÉS, ET C'EST LE FILET QUI STRUCTURE. Les arguments se
   PARTAGENT la largeur (`flex: 1 1 0`) : trois arguments donnent trois blocs
   égaux, quatre en donnent quatre, et il n'y a plus jamais de piste vide.
   Ce que les pistes fixes apportaient — un rythme lisible — est repris par les
   filets verticaux, qui le rendent visible au lieu de le suggérer.

   LE CONTENU EST CENTRÉ DANS SON BLOC. Il faut l'assumer : les icônes ne sont
   plus à la même abscisse d'un bloc à l'autre, puisque chaque groupe se centre
   sur un texte de longueur différente. C'est précisément ce qui fait que les
   blocs se lisent comme quatre objets distincts plutôt que comme une grille mal
   remplie — et le filet, lui, tient l'alignement vertical que l'icône ne tient
   plus. Le TEXTE, en revanche, reste fer à gauche : centrer deux lignes de
   longueurs inégales (« Aucune donnée bancaire n'est conservée par la
   boutique ») les rendrait déchiquetées des deux côtés. */
.reassurance-pied {
  margin: 0; padding: 0; border-top: 0;
  display: flex;
  align-items: stretch;
  gap: 0;
}
.reassurance-pied .reassurance-point {
  flex: 1 1 0;
  min-width: 0;
  /* LE GROUPE SE CENTRE, L'ICÔNE RESTE SUR LA PREMIÈRE LIGNE (4/09/2026).
     Deux exigences qu'un simple `align-items` ne peut pas tenir ensemble :
     `align-items: center` centrait le groupe dans sa colonne étirée (il le
     fallait — sans quoi les quatre arguments courts flottaient en haut d'une
     case laissée haute par la rangée de badges), mais il centrait AUSSI la
     pastille contre son texte : sur « Service client » et ses trois lignes,
     l'icône se retrouvait en face de l'horaire, à mi-hauteur, au lieu du
     titre qu'elle annonce. Signalé par Jesse.

     La grille sait faire la différence, le flex non : `align-content` place
     la RANGÉE dans la case (donc le groupe se centre), `align-items` place
     chaque cellule dans la rangée (donc la pastille se cale en haut, contre
     le titre). La colonne fixe de 28 px aligne au passage toutes les
     pastilles d'une colonne à l'autre, ce qu'un flex ne garantissait pas. */
  display: grid;
  grid-template-columns: 28px minmax(0, 1fr);
  align-content: center;
  align-items: start;
  /* FER À GAUCHE DANS SA COLONNE, et non groupe centré (3/09/2026). Centrer
     chaque groupe icône+texte donnait cinq blocs de largeurs différentes
     posés au milieu de leur colonne : les pastilles ne s'alignaient pas d'une
     colonne à l'autre, et surtout le texte ne prenait que la largeur de son
     contenu — « Du mardi au samedi · 9h30–12h30 et 14h–18h » tombait sur
     QUATRE lignes, dont une pour le seul mot « et », alors qu'il restait
     quatre-vingts pixels libres à droite. */
  justify-content: start;
  gap: 10px;
  padding: 6px 16px;
  border-left: 1px solid var(--filet);
}
/* `align-items: stretch` sur le conteneur donne à chaque bloc la hauteur de la
   rangée : le filet court alors sur toute la bande, quel que soit le nombre de
   lignes du bloc voisin. */
.reassurance-pied .reassurance-point:first-child { border-left: 0; }
/* Des pastilles plus petites qu'ailleurs : sous un bouton d'achat elles
   soutiennent une décision, au bas d'une page elles rappellent. Le rapport
   entre les deux tailles est celui de l'attention qu'on leur demande. */
.reassurance-pied .reassurance-pastille { width: 28px; height: 28px; border-radius: 8px; flex: 0 0 auto; }
.reassurance-pied .reassurance-pastille svg { width: 15px; height: 15px; }
/* Le corps prend la largeur qui reste : c'est elle qui décide où le texte se
   coupe, et une colonne de bande vaut deux cent quarante pixels — de quoi tenir
   un horaire sur une ligne. */
.reassurance-pied .reassurance-corps { flex: 1 1 auto; }
.reassurance-pied .reassurance-corps strong { font-size: 13px; }
.reassurance-pied .reassurance-corps span { font-size: 12.5px; }
/* LA RANGÉE DE MOYENS DE PAIEMENT, sous l'argument de paiement.
   `display: flex` écrase le `display: block` que `.reassurance-corps span`
   pose sur tous les span du corps — sans quoi les six badges s'empileraient
   verticalement, chacun sur sa ligne.

   UNE SEULE RANGÉE, QUELLE QUE SOIT LA LARGEUR (3/09/2026, demande de Jesse).
   À taille fixe, six badges de 38 px demandent 250 px : dans une colonne de
   bande, ils passaient à deux rangées — quatre logos puis deux, ce qui se lit
   comme deux groupes distincts. Ils ne s'enroulent plus : ils RÉTRÉCISSENT
   ensemble jusqu'à tenir, chacun gardant son rapport (208 × 134, la famille de
   fichiers décrite dans payment-badges.php). Un septième badge — Klarna —
   trouve donc sa place sans qu'on touche à quoi que ce soit. */
.reassurance-pied .reassurance-paiement {
  display: flex; flex-wrap: nowrap; align-items: center;
  gap: 4px;
  margin-top: 8px;
}
/* ⚠️ CORRIGÉ LE 26/08/2026. La première version rétrécissait la case à
   36 × 22 en laissant intactes les dimensions écrites À L'INTÉRIEUR de chaque
   badge — les cercles de Mastercard, la largeur du logotype PayPal, celle de
   Stripe. Résultat à l'écran : Mastercard débordait par le bas, « PayPal »
   était coupé en « PayPa », Stripe sortait de son cadre blanc. La leçon n'est
   pas « ne pas redimensionner », c'est que des cotes internes écrites en dur
   rendent une case NON redimensionnable — d'où les cartes en fichier.

   MAINTENANT QUE CHAQUE BADGE EST UNE IMAGE `contain` dans sa case, la seule
   chose à écrire est la case, et le dessin suit. 5 badges de 38 px et leurs
   4 écarts font 190 px, très en deçà des ~275 px utiles d'un bloc sur une
   boutique à quatre arguments : la rangée tient sur une ligne, et elle y
   tiendrait encore avec un sixième moyen de paiement. */
/* `min-width: 0` est ce qui autorise le rétrécissement : sans lui, un élément
   flex ne descend jamais sous la largeur de son contenu, et la rangée
   déborderait de sa colonne au lieu de s'ajuster. */
.reassurance-pied .reassurance-paiement .payment-icon {
  flex: 0 1 auto;
  min-width: 0;
  display: block;
  width: 38px;
}
.reassurance-pied .reassurance-paiement .payment-icon img {
  width: 100%;
  height: auto;
  aspect-ratio: 208 / 134;
}

/* LA MARGE DU PIED DISPARAÎT QUAND LA BANDE LE PRÉCÈDE. Ses 48 px séparaient le
   pied du CONTENU de la page — utile tant qu'une grille de produits s'arrêtait
   juste au-dessus. Depuis que la bande de réassurance ferme chaque page, ces
   deux bandes se suivent : la marge n'articule plus rien, elle creuse un trou
   blanc entre deux blocs colorés. Le sélecteur d'adjacence est volontaire — sur
   une boutique dont l'écran Identité ne renseigne rien, la bande n'est pas
   rendue et le pied retrouve sa marge. */
.bande-reassurance + .pied { margin-top: 0; }

/* ENTRE 640 ET 1100 PX, LA RANGÉE DE CINQ N'EN EST PLUS UNE (3/09/2026,
   signalé par Jesse sur « Service client »). À 780 px de fenêtre — un
   navigateur en demi-écran, cas très courant sur portable —, cinq colonnes font
   120 px chacune : l'horaire tombait sur quatre lignes, dont une pour le seul
   point médian. La bande passe donc en grille qui se remplit d'elle-même, à 220
   px minimum par argument : trois colonnes à 780 px, quatre à 1000. Les filets
   verticaux disparaissent au profit d'un espacement — un filet n'a de sens
   qu'entre deux blocs de même hauteur, et sur deux rangées il séparerait des
   choses qui ne se suivent pas. */
@media (min-width: 641px) and (max-width: 1099px) {
  .reassurance-pied {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: 18px 28px;
    align-items: start;
  }
  .reassurance-pied .reassurance-point {
    border-left: 0;
    padding: 0;
    align-items: flex-start;
  }
}

@media (max-width: 640px) {
  .bande-reassurance-interieur { padding: 6px 20px; }
  /* Sous 640 px, chaque argument prend la ligne entière : quatre blocs de 90 px
     couperaient « Aucune donnée bancaire n'est conservée » en six lignes. Le
     filet bascule donc de la verticale à l'horizontale — il sépare toujours,
     mais dans l'autre sens. */
  .reassurance-pied { flex-direction: column; }
  .reassurance-pied .reassurance-point {
    border-left: 0;
    border-top: 1px solid var(--filet);
    /* PAS CENTRÉ ICI, et ce n'est pas un oubli : sur un bloc large de tout
       l'écran, un groupe centré flotte au milieu d'un vide des deux côtés. Le
       centrage n'a de sens que face à un voisin, c'est-à-dire en rangée. */
    justify-content: flex-start;
    padding: 14px 0;
  }
  .reassurance-pied .reassurance-point:first-child { border-top: 0; padding-top: 8px; }
  .reassurance-pied .reassurance-point:last-child { padding-bottom: 8px; }
}

/* ─── PALIERS DE REMISE ──────────────────────────────────────────────────────
   ⚠️ TROISIÈME FORME (25/08/2026). Voir sections-accueil.php pour l'historique
   des deux refus. Ce qui manquait aux deux premières n'était pas un dessin plus
   joli : c'était une DÉLIMITATION. Un titre à gauche, une échelle sur 1180 px et
   une note à gauche ne forment pas un bloc — ils forment trois éléments posés
   au même endroit. Ici, le texte tient sa colonne et les paliers deviennent un
   encart encadré à sa droite : le même vocabulaire que les autres blocs du site
   (bordure, rayon, fond de surface), et le vide de droite est occupé au lieu
   d'être meublé.
--------------------------------------------------------------------------- */
.section-paliers { margin: 8px 0 44px; }
.paliers-bande {
  display: grid;
  /* Le texte respire, l'encart reste tenu : au-delà de ~420px, une liste de
     quatre lignes s'étire sans rien gagner. */
  grid-template-columns: minmax(0, 1fr) minmax(0, 420px);
  gap: 0 clamp(28px, 4vw, 64px);
  align-items: start;
}
.section-paliers h2 {
  font-family: var(--police-titre); font-weight: var(--marque-poids-titre, 700);
  font-size: clamp(24px, 3.4vw, 30px); margin: 0 0 10px; letter-spacing: -.015em;
}
.section-paliers-intro, .section-paliers-note {
  max-width: 60ch; color: var(--encre-douce); font-size: 15.5px; line-height: 1.6;
}
.section-paliers-intro { margin: 0 0 12px; }
.section-paliers-note  { margin: 0; font-size: 14.5px; }
.section-paliers-intro p:last-child, .section-paliers-note p:last-child { margin-bottom: 0; }

.paliers {
  list-style: none; margin: 0; padding: 0;
  border: 1px solid var(--filet); border-radius: var(--rayon-large);
  background: var(--surface); overflow: hidden;
}
/* UNE LIGNE PAR PALIER : l'intitulé au-dessus, le seuil et la remise sur la
   même ligne de part et d'autre. Les remises s'alignent donc toutes sur le bord
   droit — c'est la colonne qu'on vient lire, elle doit se balayer d'un trait. */
.palier {
  display: grid; grid-template-columns: minmax(0, 1fr) auto;
  align-items: baseline; gap: 2px 16px;
  padding: 13px 18px;
}
.palier + .palier { border-top: 1px solid var(--filet); }
.palier-titre {
  grid-column: 1 / -1; margin: 0;
  font-size: 11.5px; font-weight: 600; letter-spacing: .04em;
  text-transform: uppercase; color: var(--encre-tenue);
}
.palier-seuil { margin: 0; font-size: 15px; font-weight: 500; color: var(--encre-douce); }
.palier-remise {
  margin: 0; justify-self: end;
  font-family: var(--police-titre); font-weight: var(--marque-poids-titre, 700);
  font-size: 22px; line-height: 1.1; letter-spacing: -.02em;
  color: var(--accent-texte); font-variant-numeric: tabular-nums;
}
/* LE DERNIER PALIER EST LE PLUS FORT, et c'est la seule hiérarchie qu'on pose :
   un fond très léger d'accent, pas une quatrième couleur. */
.palier:last-child { background: color-mix(in srgb, var(--accent) 6%, transparent); }

@media (max-width: 860px) {
  /* L'encart passe SOUS le texte : deux colonnes de 300px couperaient
     « dès 1 000 € » et la remise sur deux lignes. */
  .paliers-bande { grid-template-columns: minmax(0, 1fr); gap: 22px 0; }
  .section-paliers-intro, .section-paliers-note { max-width: none; }
}

/* ----------------------------------------------------------- le journal --
   Les cartes d'article reprennent la structure des cartes produit — même
   bordure, même rayon, même relèvement au survol — mais avec un visuel plus
   large et pas de prix. Les habiller autrement aurait donné deux grammaires
   dans le même site pour la même chose : un bloc cliquable qui mène ailleurs. */
.grille-articles {
  display: grid; gap:20px;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  padding-bottom:48px;
}
.carte-article {
  border: 1px solid var(--filet); border-radius: var(--rayon-large);
  background: var(--surface); overflow: hidden;
  text-decoration: none; color: inherit;
  display: flex; flex-direction: column;
  transition: border-color .15s ease, transform .15s ease;
}
/* ⚠️ RELIEF AU SURVOL (20/08/2026, « améliore la présentation globale des
   articles ») : la carte ne faisait jusqu'ici que changer de bordure et se
   soulever de 2px — le même langage que les vignettes de carrousel voisines
   (`.carrousel-vignette:hover .carrousel-visuel`) lui manquait. Même recette
   d'ombre qu'ailleurs sur le site pour rester cohérent. `.grille-articles`
   n'a pas d'`overflow` posé : rien ne vient la rogner. */
.carte-article:hover {
  border-color: var(--filet-fort); transform: translateY(-3px);
  box-shadow: 0 10px 26px rgba(16, 16, 24, .09);
}
[data-theme="dark"] .carte-article:hover { box-shadow: 0 10px 26px rgba(0, 0, 0, .4); }
@media (prefers-reduced-motion: reduce) { .carte-article:hover { transform: none; } }
.carte-article-visuel { display: block; aspect-ratio: 16 / 9; background: var(--fond-doux); overflow: hidden; }
.carte-article-visuel img { width: 100%; height: 100%; object-fit: cover; display: block; transition: transform .3s ease; }
.carte-article:hover .carte-article-visuel img { transform: scale(1.04); }
@media (prefers-reduced-motion: reduce) { .carte-article:hover .carte-article-visuel img { transform: none; } }
.carte-article-corps { padding:16px 18px 18px; display: flex; flex-direction: column; gap:8px; flex: 1; }
/* ⚠️ PLUS DE `display: flex` (20/08/2026, « pas de séparateur, trop
   d'espace entre émojis et intitulés ») : posé pour DEUX enfants d'origine
   (l'étiquette de catégorie et la date), `gap: 10px` s'appliquait à CHAQUE
   enfant une fois le contenu enrichi d'émojis et de « · » littéraux — un
   conteneur flex traite un nœud de texte entre deux éléments comme un enfant
   de plus, avec le même écart forcé de chaque côté. Résultat : le "·" gonflé
   d'espace ET, dès que l'étiquette passait à la ligne, un "·" livré à
   lui-même sans rien après. Un flux de texte normal, comme `.article-meta`
   qui n'a jamais eu ce défaut, laisse les espaces être ceux du gabarit — ni
   plus, ni moins. */
.carte-article-meta { font-size: 13px; color: var(--encre-tenue); }
.carte-article-meta .etiquette { display: block; width: fit-content; margin-top: 6px; }
/* 17 → 15.5px (24/08/2026, « baisse un peu la taille des titres ») : à 17px,
   un titre long passait sur trois lignes dans la carte et écrasait le chapo. */
.carte-article-titre { font-weight: 600; font-size: 15.5px; line-height: 1.35; }
/* ⚠️ BORNÉ, MÊME PRINCIPE QUE `.carrousel-entete-description` (20/08/2026,
   « les descriptions doivent toutes avoir la même taille ») : à longueur de
   caractères égale (`RichText::excerpt(...,140)`), deux chapôs peuvent
   occuper un nombre de lignes différent — les mots ne coupent pas au même
   endroit — et les cartes d'une même rangée se retrouvaient de hauteurs
   inégales. Le clamp force le même nombre de lignes partout, quel que soit
   le texte réellement déposé. */
.carte-article-chapo {
  font-size: 14px; color: var(--encre-douce); line-height: 1.6;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3;
  overflow: hidden;
  /* ⚠️ HAUTEUR RÉSERVÉE, PAS SEULEMENT PLAFONNÉE (24/08/2026, « les
     descriptions courtes doivent faire la même taille ») : le clamp borne à
     3 lignes mais laisse un chapo court n'en occuper que 2 — l'extrait coupé
     à 140 caractères tient parfois en deux lignes, et cette carte-là restait
     plus basse que ses voisines. `min-height` de 3 lignes (3 × 1.6em) :
     toutes les descriptions occupent le même bloc, plein ou non. */
  min-height: 4.8em;
}
/* CTA EN PIED DE CARTE : la carte entière est déjà cliquable (c'est un `<a>`),
   ce lien ne double donc rien fonctionnellement — il DIT ce que le clic fait,
   là où la carte seule ne le disait qu'implicitement. `margin-top: auto`
   l'ancre en bas même quand les chapôs voisins font des hauteurs inégales. */
.carte-article-lien {
  margin-top: auto; padding-top: 4px;
  font-size: 13.5px; font-weight: 600; color: var(--accent-texte);
}
.carte-article:hover .carte-article-lien { text-decoration: underline; }

/* L'article lui-même : la colonne de lecture est centrée et étroite. Le corps
   passe par .prose, qui porte déjà la mesure et les styles de texte long. */
.article { max-width: 760px; margin:0 auto; }
.article-meta { font-size: 14px; color: var(--encre-tenue); margin:0 0 14px; }
.article-meta a { color: inherit; }
/* CHAQUE ÉMOJI DE MÉTA OCCUPE LA MÊME BOÎTE, et c'est la seule façon d'obtenir
   le même écart pour tous. Les dessins n'ont pas la même chasse : posé tel
   quel, 📅 collait à la date là où ⏱️ laissait un blanc devant le temps de
   lecture. Largeur fixe, glyphe centré dedans, marge unique à droite — le
   gabarit n'écrit plus aucun espace autour d'eux. */
.meta-emoji {
  display: inline-block; width: 1.15em; margin-right:.32em;
  text-align: center; font-style: normal; line-height: 1;
}
.article-visuel { margin:0 0 32px; }
.article-visuel img { width: 100%; height: auto; border-radius: var(--rayon-large); display: block; }
.article-corps { max-width: none; padding-bottom:40px; }
/* « À lire aussi » revient dans la colonne de l'article. Il occupait toute la
   largeur du contenu quand tout ce qui le précède tient dans 760 px : la page
   se terminait sur un bloc dont le bord gauche ne correspondait à aucun autre.
   Trois cartes tiennent encore dans cette largeur — c'est la grille qui s'y
   adapte, pas la colonne qui s'élargit. */
.article-voisins {
  max-width: 760px; margin:0 auto;
  border-top: 1px solid var(--filet); padding-top:32px;
}
.article-voisins h2 { margin:0 0 20px; }
.article-voisins .grille-articles { grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap:16px; }

/* ---------------------------------------------------------------------------
   LE PIED D'ARTICLE — le vote, puis le partage

   UNE SEULE CARTE À DEUX RANGÉES, séparées par un filet, et non deux blocs qui
   se suivent. Les deux répondent à la même question posée au même instant : on
   vient de finir de lire, et il n'y a que deux choses à faire de cette
   lecture — dire ce qu'on en a pensé, ou la passer à quelqu'un. En deux blocs
   distincts, chacun devenait une bande de plus entre la fin du texte et les
   commentaires, et la fin de l'article se mettait à ressembler à un pied de
   page.
--------------------------------------------------------------------------- */
.article-pied {
  margin:32px 0 0;
  border: 1px solid var(--filet); border-radius: var(--rayon-large);
  background: var(--surface);
}
.article-pied-ligne {
  display: flex; align-items: center; justify-content: space-between;
  gap:16px; flex-wrap: wrap; padding:14px 18px;
}
.article-pied-ligne + .article-pied-ligne { border-top: 1px solid var(--filet); }
.article-pied-intitule { margin:0; font-size: 14px; font-weight: 550; }

/* --- les deux pouces --- */
.article-votes { display: flex; align-items: center; gap:10px; flex-wrap: wrap; }
.article-vote { margin:0; }
/* L'émoji plutôt que le pouce tracé : il porte sa propre couleur, là où le SVG
   en `currentColor` donnait un pouce gris qui n'appelait pas le clic. Il est
   posé en `aria-hidden` — c'est `aria-pressed` et le libellé qui portent le
   sens, l'émoji n'ajoute rien à dire à voix haute. Sa marge remplace le `gap`
   du bouton, trop large pour un glyphe qui porte déjà son propre blanc. */
.vote-emoji { font-size: 16px; line-height: 1; margin-right: 7px; }
.vote-bouton {
  display: inline-flex; align-items: center; gap:8px;
  /* 38 px de haut, en pastille : c'est un bouton d'humeur, pas une validation
     de commande. Un bouton plein lui donnerait le poids d'un achat. */
  min-height: 38px; padding:0 14px;
  border: 1px solid var(--filet-fort); border-radius: 999px;
  background: var(--surface); color: var(--encre-douce);
  font: inherit; font-size: 13.5px; cursor: pointer;
  transition: border-color .15s ease, color .15s ease, background-color .15s ease;
}
.vote-bouton:hover, .vote-bouton:focus-visible { border-color: var(--encre-tenue); color: var(--encre); outline: none; }
/* Le chiffre en chasse fixe : sans lui, passer de 9 à 10 décale le bouton
   entier, et le pouce voisin avec. */
.vote-compte { font-weight: 650; color: var(--encre); font-variant-numeric: tabular-nums; }
.vote-bouton.est-choisi {
  border-color: var(--accent); background: var(--accent-doux); color: var(--accent-texte);
}
.vote-bouton.est-choisi .vote-compte { color: var(--accent-texte); }
.article-votes-defaire { font-size: 12.5px; color: var(--encre-tenue); }

/* --- le partage ---
   DES PASTILLES NOMMÉES, ET AUCUN LOGO. Les marques y sont écrites en toutes
   lettres : un jeu de pictogrammes de marque se démode au rythme des marques
   elles-mêmes — Twitter est devenu X en gardant son oiseau partout — et rend
   la rangée illisible pour qui ne les reconnaît pas. La couleur n'apparaît
   qu'au survol, sur celle qu'on vise : dix pastilles colorées au repos
   feraient une barre de confettis sous un article. */
.partage-liste { display: flex; flex-wrap: wrap; gap:8px; list-style: none; margin:0; padding:0; }
.partage-lien {
  display: inline-flex; align-items: center; min-height: 32px; padding:0 12px;
  border: 1px solid var(--filet); border-radius: 999px;
  background: var(--surface); color: var(--encre-douce);
  font: inherit; font-size: 13px; text-decoration: none; cursor: pointer;
  transition: border-color .15s ease, color .15s ease;
}
.partage-lien:hover, .partage-lien:focus-visible { border-color: currentColor; outline: none; }
/* La marque devant le libellé (12/08/2026, demande de Jesse « icônes officielles
   des plateformes »). Elle est peinte en `currentColor` : la teinte de marque
   n'apparaît toujours qu'au survol, sur la pastille visée — dix marques en
   couleur au repos feraient la barre de confettis que le commentaire ci-dessus
   écarte, et l'icône suit désormais le texte au lieu de la contredire. */
.icone-reseau { width: 15px; height: 15px; flex: none; margin-right: 7px; display: block; }
/* LES TEINTES DE MARQUE SONT ASSOMBRIES POUR LE THÈME CLAIR, et c'est une
   mesure, pas un goût. Les valeurs officielles sont conçues pour des aplats,
   pas pour du texte : posé sur le blanc de `--surface`, le bleu Facebook
   #1877F2 ne donne que 4,24:1, le Reddit #FF4500 3,44 et le Telegram #229ED9
   3,01 — sous le seuil de 4,5 de WCAG AA pour du texte courant. Le survol
   rendait donc la pastille MOINS lisible qu'au repos, ce qui est exactement
   l'inverse de ce qu'on lui demande. La teinte est conservée, seule la clarté
   change. Mesuré : 5,42 à 7,84:1. */
.partage-x:hover, .partage-x:focus-visible { color: var(--encre); }
.partage-facebook:hover,  .partage-facebook:focus-visible  { color: #0B57C9; }
.partage-linkedin:hover,  .partage-linkedin:focus-visible  { color: #04569C; }
.partage-bluesky:hover,   .partage-bluesky:focus-visible   { color: #0257C4; }
.partage-reddit:hover,    .partage-reddit:focus-visible    { color: #B03400; }
.partage-whatsapp:hover,  .partage-whatsapp:focus-visible  { color: #0E7A3E; }
.partage-telegram:hover,  .partage-telegram:focus-visible  { color: #10658C; }
.partage-pinterest:hover, .partage-pinterest:focus-visible { color: #A8001A; }

/* … ET ÉCLAIRCIES POUR LE THÈME SOMBRE, symétriquement. Les mêmes valeurs
   assombries tombent à 1,5-2:1 sur le #16171C des surfaces sombres. Les deux
   blocs sont écrits parce que la feuille suit TROIS états : choix explicite du
   visiteur dans les deux sens, et préférence système à défaut. Mesuré :
   5,72 à 9,22:1. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .partage-facebook:hover,
  :root:not([data-theme="light"]) .partage-facebook:focus-visible  { color: #4E93F7; }
  :root:not([data-theme="light"]) .partage-linkedin:hover,
  :root:not([data-theme="light"]) .partage-linkedin:focus-visible  { color: #3E96E6; }
  :root:not([data-theme="light"]) .partage-bluesky:hover,
  :root:not([data-theme="light"]) .partage-bluesky:focus-visible   { color: #4FA5FF; }
  :root:not([data-theme="light"]) .partage-reddit:hover,
  :root:not([data-theme="light"]) .partage-reddit:focus-visible    { color: #FF6A33; }
  :root:not([data-theme="light"]) .partage-whatsapp:hover,
  :root:not([data-theme="light"]) .partage-whatsapp:focus-visible  { color: #3ED37E; }
  :root:not([data-theme="light"]) .partage-telegram:hover,
  :root:not([data-theme="light"]) .partage-telegram:focus-visible  { color: #4FB9E8; }
  :root:not([data-theme="light"]) .partage-pinterest:hover,
  :root:not([data-theme="light"]) .partage-pinterest:focus-visible { color: #FF5B72; }
}
:root[data-theme="dark"] .partage-facebook:hover,
:root[data-theme="dark"] .partage-facebook:focus-visible  { color: #4E93F7; }
:root[data-theme="dark"] .partage-linkedin:hover,
:root[data-theme="dark"] .partage-linkedin:focus-visible  { color: #3E96E6; }
:root[data-theme="dark"] .partage-bluesky:hover,
:root[data-theme="dark"] .partage-bluesky:focus-visible   { color: #4FA5FF; }
:root[data-theme="dark"] .partage-reddit:hover,
:root[data-theme="dark"] .partage-reddit:focus-visible    { color: #FF6A33; }
:root[data-theme="dark"] .partage-whatsapp:hover,
:root[data-theme="dark"] .partage-whatsapp:focus-visible  { color: #3ED37E; }
:root[data-theme="dark"] .partage-telegram:hover,
:root[data-theme="dark"] .partage-telegram:focus-visible  { color: #4FB9E8; }
:root[data-theme="dark"] .partage-pinterest:hover,
:root[data-theme="dark"] .partage-pinterest:focus-visible { color: #FF5B72; }
/* La confirmation de copie se lit sur le bouton lui-même : une notification
   ailleurs dans la page demande de la chercher pour apprendre que le geste
   qu'on vient de faire a marché. */
.partage-copier.est-copie { color: var(--succes); border-color: var(--succes); }

@media (max-width: 640px) {
  /* Sur téléphone, l'intitulé et sa rangée ne tiennent pas côte à côte : la
     rangée se retrouverait à trois boutons par ligne sur quatre lignes. */
  .article-pied-ligne { flex-direction: column; align-items: flex-start; gap:12px; }
}

.etiquette {
  display: inline-block; padding:2px 8px; border-radius: 999px;
  font-size: 12px; font-weight: 550; border: 1px solid var(--filet-fort);
  color: var(--encre-douce);
}
.etiquette-promo { border-color: transparent; background: var(--accent-doux); color: var(--accent-texte); }
.etiquette-rupture { border-color: transparent; background: var(--accent-doux); color: var(--alerte); }

/* ------------------------------------------------------------- rubriques */
.rubriques { display: flex; flex-wrap: wrap; gap:8px; padding-bottom:24px; }
.rubriques a {
  padding:7px 14px; border: 1px solid var(--filet); border-radius: 999px;
  text-decoration: none; font-size: 14px; color: var(--encre-douce); background: var(--surface);
}
.rubriques a:hover { border-color: var(--filet-fort); color: var(--encre); }
.rubriques a.actif { background: var(--encre); color: var(--fond); border-color: var(--encre); }

/* Le code de réduction n'est plus un déroulant : son intitulé devient un simple
   libellé de champ. */
.coupon-titre {
  margin: 0 0 8px; font-size: 13.5px; font-weight: 600; color: var(--encre-douce);
}

/* ------------------------------------------------------- pagination --
   Elle empruntait l'habillage du filtre de rubriques, ce qui était juste tant
   qu'elle n'était qu'une rangée de pastilles. Elle porte désormais des flèches
   et un compte : elle a sa propre règle, et une seule (voir le partial
   Templates/partials/pagination.php, qui a remplacé cinq copies). */
/* LE BLOC EST CENTRÉ depuis que les flèches mortes ne sont plus rendues
   (18/08/2026). Elles tenaient les deux bords et donnaient au bloc une largeur
   constante ; sans elles, un bloc aligné à gauche verrait sa rangée de numéros
   glisser de la largeur de « ‹ Précédent » entre la page 1 et la page 2 — et le
   bouton « Suivant » se déroberait sous le curseur de qui enchaîne les pages.
   Centré, il reste sur son axe quelle que soit la page : la largeur bouge, le
   milieu ne bouge pas.
   Le compte, lui, reste poussé à droite par sa marge automatique. */
.pagination-boutique {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: 8px;
  padding: 8px 0 24px;
}
.pagination-numeros { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.pagination-boutique a,
.pagination-boutique .actif {
  display: inline-flex; align-items: center; justify-content: center;
  /* UNE LARGEUR MINIMALE, PAS UNE LARGEUR FIXE. Les pastilles étaient rondes et
     calibrées sur un chiffre : à trois chiffres, le nombre débordait de son
     cercle. `min-width` garde l'alignement des unités et laisse la centaine
     s'installer. */
  min-width: 36px; height: 36px; padding: 0 10px;
  border: 1px solid var(--filet); border-radius: 999px;
  text-decoration: none; font-size: 14px;
  color: var(--encre-douce); background: var(--surface);
}
.pagination-boutique a:hover { border-color: var(--filet-fort); color: var(--encre); }
.pagination-boutique .actif {
  background: var(--encre); color: var(--fond); border-color: var(--encre);
  font-weight: 600;
}
.pagination-fleche { gap: 6px; font-weight: 500; }
/* `.est-inerte` a été RETIRÉE avec le repère qu'elle habillait : une flèche qui
   ne mène nulle part n'est plus rendue du tout. Voir pagination.php. */
.pagination-ellipse {
  padding: 0 2px; color: var(--encre-tenue); font-size: 14px;
  user-select: none;
}
.pagination-compte {
  /* `margin-left: auto` pousse le compte au bord droit ET, sur un conteneur
     centré, repousse d'autant le groupe des numéros. On le sort donc du flux de
     centrage en lui donnant sa propre ligne dès qu'il y a la place : il est une
     LÉGENDE de la barre, pas un de ses contrôles. */
  flex: 1 0 100%; text-align: center;
  margin-top: 4px; font-size: 13.5px; color: var(--encre-tenue);
}

/* ----------------------------------------------------------- fiche produit */
/* ⚠️ LA FICHE PRODUIT GARDE LA LARGEUR D'AVANT L'ÉLARGISSEMENT (25/08/2026,
   demande de Jesse : « remets le container en l'état pour les fiches produit »).
   Le container commun est passé de 1180 à 1440px pour les pages de LISTE, où la
   largeur gagnée se remplit de cartes. Sur une fiche, elle ne remplit rien :
   elle étire la colonne d'achat et la fiche technique, et éloigne le prix de la
   photo — on lit une fiche en allant de l'image au bouton, pas en balayant
   l'écran. La règle vise le container QUI CONTIENT la fiche (`.fiche` n'existe
   que sur cette page), donc toute la page produit, pas seulement le haut : deux
   largeurs dans une même page se verraient plus encore que le changement. */
main:has(.fiche) .contenu { max-width: 1180px; }
.fiche { display: grid; grid-template-columns: 1.1fr 1fr; gap:48px; padding:32px 0 48px; }
.fiche-visuels { display: flex; flex-direction: column; gap:12px; }
/* ⚠️ MÊME CORRECTION QUE LA VUE EN VEDETTE, ET POUR LA MÊME RAISON (8/09,
   voir le commentaire long plus bas) : ce cadre-ci n'est pas une disposition
   parmi d'autres sur les boutiques en service — toutes les trois sont réglées
   en « grille-vedette », qui le masque —, c'est le cadre de repli des fiches
   À UNE SEULE PHOTO, soit 194 produits sur 349 dans une reprise ordinaire.
   Les laisser en 4/3 aurait corrigé la moitié visible du catalogue et laissé
   l'autre moitié décapitée. */
.fiche-visuel-principal {
  aspect-ratio: var(--marque-ratio-visuel, 4 / 5); border: 1px solid var(--filet); border-radius: var(--rayon-large);
  /* Même jeton que les vignettes : deux fonds différents pour le même produit
     se remarquent en passant de la liste à la fiche. */
  background: var(--marque-fond-visuel, var(--fond-doux)); overflow: hidden;
  display: flex; align-items: center; justify-content: center; color: var(--encre-tenue);
  position: relative;
}
.fiche-visuel-principal img { width: 100%; height: 100%; object-fit: contain; }

/* ─── VENDU / DISPONIBLE (26/09/2026) ────────────────────────────────────────
   « Pas assez clair sur la fiche quand un produit est déjà vendu » et « ce doit
   être clair qu'un produit est disponible ». Trois signaux, un par endroit où
   l'œil passe : la photo, le prix, la colonne d'achat. La galerie est déjà
   positionnée (`.fiche-visuels { position: relative }`, plus bas) : la
   pastille s'y accroche. */
/* La pastille se lit avant tout le reste : encre pleine sur la photo, en
   capitales espacées comme l'étiquette « Occasion » qu'elle remplace dans le
   regard. La photo passe légèrement en retrait, sans disparaître — on doit
   encore voir la pièce, pas croire que l'image est cassée. */
.fiche-pastille-etat {
  position: absolute; top: 16px; left: 16px; z-index: 2;
  padding: 7px 14px; border-radius: 999px;
  background: var(--encre); color: var(--fond);
  font-size: 13px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase;
  pointer-events: none;
}
/* ESTOMPÉE, JAMAIS DÉSATURÉE : un premier essai en `grayscale()` changeait
   l'or jaune en argent sur la photo — une fausse information sur la matière,
   sur une boutique qui vend de l'or. */
.fiche-visuels.est-indisponible img { opacity: .6; }
.fiche-prix.est-indisponible .prix { color: var(--encre-tenue); text-decoration: line-through; text-decoration-thickness: 1.5px; }

/* Disponible : un point vert et un mot en gras, juste sous le prix. Le vert
   est le jeton d'état `--succes`, pas une couleur de charte — il signifie la
   même chose sur toutes les boutiques. */
.fiche-dispo {
  display: flex; align-items: center; gap: 8px;
  margin: -4px 0 16px; font-size: 15px; color: var(--encre-tenue);
}
.fiche-dispo strong { color: var(--succes); font-weight: 650; }
.fiche-dispo-point {
  width: 9px; height: 9px; border-radius: 50%; flex: none;
  background: var(--succes);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--succes) 18%, transparent);
}

/* Vendu : un encadré qui se voit et qui donne une suite. */
.fiche-vendu {
  margin: 0 0 16px; padding: 18px 20px;
  border: 1px solid var(--filet-fort); border-left: 4px solid var(--encre);
  border-radius: var(--rayon); background: var(--fond-doux);
}
.fiche-vendu-titre { margin: 0 0 4px; font-size: 18px; font-weight: 650; color: var(--encre); }
.fiche-vendu-texte { margin: 0 0 14px; font-size: 15px; color: var(--encre-tenue); }
.fiche-vendu-lien { display: inline-flex; }

/* --- La loupe au survol ---
   Un carré posé sur le document, qui suit le curseur et montre la zone visée
   agrandie. Il ne vit dans aucun conteneur de la galerie : c'est ce qui lui
   permet de fonctionner avec les trois présentations, y compris celles où le
   visuel principal est masqué.

   `pointer-events: none` est indispensable : sans lui, la loupe se glisse sous
   le curseur, intercepte le survol de l'image, et se met à clignoter. */
.loupe-visuel {
  position: fixed; z-index: 9997;
  /* LE CÔTÉ DE LA LOUPE SE RÈGLE ICI, ET NULLE PART AILLEURS. shop.js le lit
     sur l'élément (`offsetWidth`) : la valeur était auparavant recopiée dans le
     script, où elle pouvait diverger sans que rien ne le signale.
     380 px plutôt que 260 : sur un bijou, la loupe sert à compter des griffes
     et à lire un poinçon — un hublot de la taille d'une vignette ne montrait
     pas assez de l'objet pour qu'on s'y repère. */
  width: 380px; height: 380px;
  border-radius: var(--rayon);
  border: 1px solid var(--filet-fort);
  box-shadow: 0 18px 50px rgba(10, 10, 20, .35);
  background-repeat: no-repeat;
  background-color: var(--surface);
  pointer-events: none;
  opacity: 0;
  transition: opacity 120ms ease;
}
.loupe-visuel.est-visible { opacity: 1; }
.fiche-vignettes img, .fiche-visuel-principal img { cursor: zoom-in; }

/* La pastille qui annonce l'agrandissement, ancrée sur LA GALERIE : le visuel
   principal est masqué dans trois présentations sur quatre, s'y accrocher la
   faisait disparaître avec lui. */
.fiche-visuels { position: relative; }
.fiche-zoom-indice {
  position: absolute; right: 12px; top: 12px;
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; border-radius: 50%;
  background: rgba(12, 14, 20, .62);
  backdrop-filter: blur(4px);
  /* L'INDICE EST UN EMOJI : il se dimensionne par `font-size`, pas par les
     attributs d'un SVG, et il porte ses propres couleurs — `color` n'aurait
     aucun effet sur lui. La pastille sombre reste, elle : c'est elle qui
     détache la loupe d'un visuel clair comme d'un visuel foncé. */
  font-size: 17px; line-height: 1;
  pointer-events: none;      /* le clic appartient à l'image */
  opacity: .85;
  transition: opacity .15s ease, transform .15s ease;
}
.fiche-visuels:hover .fiche-zoom-indice { opacity: 1; transform: scale(1.06); }
@media (prefers-reduced-motion: reduce) {
  .fiche-zoom-indice, .fiche-visuels:hover .fiche-zoom-indice { transition: none; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .loupe-visuel { transition: none; }
}
/* NOTE PHOTOS — bandeau superposé en bas du visuel principal */
.fiche-note-photos {
  position: absolute; bottom: 0; left: 0; right: 0;
  padding: 7rem 1rem .65rem;
  background: linear-gradient(to top, rgba(0,0,0,.26) 0%, rgba(0,0,0,.12) 40%, rgba(0,0,0,.03) 70%, transparent 100%);
  color: #fff;
  font-size: .67rem; line-height: 1.5;
  pointer-events: none;
  border-radius: 0 0 var(--rayon-large) var(--rayon-large);
}
.fiche-note-photos p { margin: 0; text-shadow: 0 1px 3px rgba(0,0,0,.5); }
.fiche-note-photos p:first-child::before {
  content: '';
  display: inline-block;
  width: 13px; height: 13px;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='white'%3E%3Cpath d='M9 3L7.17 5H4C2.9 5 2 5.9 2 7v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2h-3.17L15 3H9zm3 15c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z'/%3E%3C/svg%3E") no-repeat center / contain;
  vertical-align: middle;
  margin-right: 5px;
  margin-bottom: 2px;
  filter: drop-shadow(0 1px 3px rgba(0,0,0,.5));
  opacity: .9;
}
.fiche-note-photos p + p { margin-top: .2rem; font-style: italic; opacity: .82; }
.fiche-vignettes { display: flex; gap:10px; flex-wrap: wrap; }
.fiche-vignettes img {
  width: 74px; height: 74px; object-fit: cover;
  border: 1px solid var(--filet); border-radius: var(--rayon);
}
.fiche-prix { display: flex; align-items: baseline; gap:12px; margin:6px 0 18px; }
.fiche-prix .prix { font-size: 27px; }
/* LA DESCRIPTION EST DU TEXTE COURANT, ET C'EST TOUT CE QU'ELLE AVAIT.
   Deux règles l'habillaient : une couleur, et une taille pour ses intertitres.
   Tout le reste — interligne, mesure, listes, mise en valeur — tombait sur les
   valeurs par défaut du navigateur. Un texte rédigé depuis l'éditeur riche du
   back-office peut porter des sous-titres, des listes et des tableaux : ils
   sortaient sans marge, à l'interligne d'origine, sur toute la largeur.

   LA MESURE EST BORNÉE À 68 CARACTÈRES, pas à une largeur en pixels : au-delà
   d'une soixantaine de signes, l'œil rate le début de la ligne suivante. En
   `ch`, la borne suit la taille de police au lieu de la contredire. */
.fiche-description {
  color: var(--encre-douce);
  max-width: 68ch;
  font-size: 15.5px;
  line-height: 1.75;
}
.fiche-description > :first-child { margin-top:0; }
.fiche-description > :last-child { margin-bottom:0; }
.fiche-description p { margin:0 0 1.1em; }
/* La première ligne fait office de chapô : c'est celle qu'on lit avant de
   décider si on lit la suite. */
.fiche-description > p:first-child { font-size: 16.5px; color: var(--encre); }
.fiche-description :is(h2, h3, h4) {
  color: var(--encre); font-size: 16.5px; font-weight: 650;
  margin:24px 0 8px; line-height: 1.35;
}
.fiche-description :is(ul, ol) { margin:0 0 1.1em; padding-left:1.3em; }
.fiche-description li { margin-bottom:6px; }
.fiche-description li::marker { color: var(--encre-tenue); }
.fiche-description strong { color: var(--encre); font-weight: 600; }
.fiche-description a { color: var(--accent-texte); }
.fiche-description img { max-width: 100%; height: auto; border-radius: var(--rayon); }
/* Un tableau collé dans la description ne doit pas élargir la page : c'est lui
   qui défile, pas le corps du document. */
.fiche-description table { width: 100%; border-collapse: collapse; margin:0 0 1.1em; font-size: 14.5px; }
.fiche-description :is(th, td) { padding:8px 10px; border: 1px solid var(--filet); text-align: left; }
.fiche-description blockquote {
  margin:0 0 1.1em; padding:2px 0 2px 16px;
  border-left: 2px solid var(--filet-fort); color: var(--encre-tenue);
}

.champ { display: flex; flex-direction: column; gap:6px; margin-bottom:16px; }
.champ label { font-size: 14px; font-weight: 550; }
/* LES CASES ET LES BOUTONS RADIO SONT EXCLUS, et c'est ce qui manquait : la
   règle leur appliquait `width: 100%`, une bordure et un rembourrage. Dans le
   choix du transporteur, chaque bouton radio occupait donc toute la largeur de
   la ligne, poussait le nom du transporteur dans une colonne de trois mots à
   droite, et laissait le point du radio flotter au milieu d'un grand vide. Le
   symptôme se lit comme un défaut de mise en page ; la cause est ici. */
.champ input:not([type="radio"]):not([type="checkbox"]),
.champ select, .champ textarea {
  font: inherit; font-size: 15px; padding:10px 12px;
  border: 1px solid var(--filet-fort); border-radius: var(--rayon);
  background: var(--surface); color: var(--encre); width: 100%;
}
.champ input[type="radio"], .champ input[type="checkbox"] { flex: none; width: auto; margin:0; }
.champ input:focus, .champ select:focus, .champ textarea:focus {
  outline: 2px solid var(--accent-doux); outline-offset: 1px; border-color: var(--accent);
}
.champ small { color: var(--encre-tenue); font-size: 13px; }
.champs-2 { display: grid; grid-template-columns: 1fr 1fr; gap:0 16px; }

/* ---------------------------------------------------------------- panier */
.panier { display: grid; grid-template-columns: 1fr 340px; gap:40px; padding:24px 0 64px; }
.ligne-panier {
  display: grid; grid-template-columns: 88px 1fr auto; gap:16px; align-items: center;
  padding:16px 0; border-bottom: 1px solid var(--filet);
}
.ligne-panier:first-child { border-top: 1px solid var(--filet); }
.ligne-visuel {
  width: 88px; height: 88px; border-radius: var(--rayon); overflow: hidden;
  background: var(--fond-doux); border: 1px solid var(--filet);
}
.ligne-visuel img { width: 100%; height: 100%; object-fit: cover; }
.ligne-nom { font-weight: 550; text-decoration: none; }
.ligne-meta { font-size: 14px; color: var(--encre-tenue); }
.ligne-actions { display: flex; align-items: center; gap:12px; }
.quantite { width: 68px; text-align: center; }
.lien-discret {
  background: none; border: 0; padding:0; font: inherit; font-size: 14px;
  color: var(--encre-tenue); text-decoration: underline; cursor: pointer;
}
.lien-discret:hover { color: var(--alerte); }

.recap {
  border: 1px solid var(--filet); border-radius: var(--rayon-large);
  background: var(--surface); padding:20px; align-self: start;
  position: sticky; top: 88px;
  /* GARDE-FOU DE HAUTEUR (16/08/2026) : un élément collant PLUS HAUT que la
     fenêtre ne défile pas de lui-même — il se fige et sa partie basse devient
     inatteignable tant que son conteneur est à l'écran. Le récapitulatif du
     tunnel atteint 760 px une fois ses lignes, ses badges et sa réassurance
     empilés : sur un portable, le total et le bouton passaient sous le bord.
     Il défile donc en interne quand il déborde, et pas une ligne avant. Même
     leçon que la colonne d'achat de la fiche produit, apprise deux fois. */
  max-height: calc(100vh - 108px);
  overflow-y: auto;
  overscroll-behavior: contain;
}
.recap-ligne { display: flex; justify-content: space-between; gap:16px; padding:7px 0; font-size: 15px; }
.recap-ligne span:first-child { color: var(--encre-douce); }
.recap-total {
  border-top: 1px solid var(--filet); margin-top:12px; padding-top:14px;
  font-size: 19px; font-weight: 650;
}
.recap .mention { font-size: 13px; color: var(--encre-tenue); margin-top:14px; }

/* ------------------------------------------------------------- messages */
.message {
  padding:13px 16px; border-radius: var(--rayon); margin:0 0 20px;
  border: 1px solid var(--filet-fort); background: var(--fond-doux); font-size: 15px;
}
.message-succes { border-color: var(--succes); color: var(--succes); background: transparent; }
.message-erreur { border-color: var(--alerte); color: var(--alerte); background: transparent; }
.message-alerte { border-color: var(--attention); color: var(--attention); background: transparent; }

/* L'état du paiement sur la confirmation : une carte DANS la colonne, à la
   largeur du contenu qu'elle précède — pas un bandeau pleine page. La teinte
   vient du jeton d'état (succès / attention / alerte) fondu dans la surface :
   la carte appartient à la page quelle que soit la charte de la boutique. */
.paiement-etat {
  display: flex; align-items: flex-start; gap: 14px; text-align: left;
  max-width: 460px; margin: 26px auto 0; padding: 15px 18px;
  border-radius: var(--rayon-large);
  background: color-mix(in srgb, var(--succes) 6%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--succes) 30%, transparent);
}
.paiement-etat-icone {
  width: 34px; height: 34px; border-radius: 50%; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  background: var(--succes); color: #FFFFFF;
}
.paiement-etat-icone svg { width: 19px; height: 19px; }
.paiement-etat strong { display: block; font-size: 15px; color: var(--encre); }
.paiement-etat p { margin: 3px 0 0; font-size: 13.5px; line-height: 1.55; color: var(--encre-tenue); }
.paiement-etat.est-attente {
  background: color-mix(in srgb, var(--attention) 6%, var(--surface));
  border-color: color-mix(in srgb, var(--attention) 30%, transparent);
}
.paiement-etat.est-attente .paiement-etat-icone { background: var(--attention); }
/* L'horloge de l'attente respire : c'est le seul signe que la page travaille. */
.paiement-etat.est-attente .paiement-etat-icone svg { animation: paiement-attente 1.6s ease-in-out infinite; }
@keyframes paiement-attente { 50% { opacity: .45; } }
@media (prefers-reduced-motion: reduce) {
  .paiement-etat.est-attente .paiement-etat-icone svg { animation: none; }
}
.paiement-etat.est-echec {
  background: color-mix(in srgb, var(--alerte) 6%, var(--surface));
  border-color: color-mix(in srgb, var(--alerte) 30%, transparent);
}
.paiement-etat.est-echec .paiement-etat-icone { background: var(--alerte); }
.paiement-etat-action { margin-top: 10px; padding: 8px 16px; font-size: 13.5px; display: inline-block; }

/* ÉTAT VIDE : soixante-douze pixels de rembourrage vertical faisaient,
   sur une rubrique sans produit, un écran presque entier de blanc entre
   le chapô et le message. Un écran vide n'a pas besoin d'être aéré, il
   est déjà vide — le rembourrage n'y sépare rien de rien. */
.vide { text-align: center; padding: 40px 24px; color: var(--encre-douce); }
.vide h2 { color: var(--encre); }

/* --------------------------------------------------------------- confirmation */
.confirmation { max-width: 640px; margin:0 auto; padding:48px 0 64px; text-align: center; }
.confirmation .reference {
  font-family: var(--police-mono); font-size: 21px; font-weight: 650;
  padding:10px 18px; border: 1px dashed var(--filet-fort); border-radius: var(--rayon);
  display: inline-block; margin:14px 0 20px;
}
.recap-commande { text-align: left; border: 1px solid var(--filet); border-radius: var(--rayon-large); padding:20px; }

/* ------------------------------------------------------------ pied de page */
.pied {
  /* PLUS D'AIR EN HAUT ET EN BAS. Le pied de page ouvrait à 32 px de son filet
     et fermait sur son bloc d'inscription, si bien que la première ligne et la
     dernière touchaient presque les bords : le bloc paraissait comprimé alors
     qu'il porte, sur ce site, quatre colonnes et un formulaire. Un pied de page
     est la seule zone qu'on lit APRÈS avoir décidé de rester — lui donner de
     l'espace ne coûte rien à personne. */
  border-top: 1px solid var(--filet); background: var(--fond-doux);
  margin-top:48px; padding:56px 0; font-size: 14px; color: var(--encre-douce);
}
.pied { padding-bottom:0; }
/* UNE GRILLE À COLONNES, ET NON UN FLEX « ESPACÉ ». En flex, les trois blocs se
   répartissaient l'espace restant : leurs largeurs changeaient selon la
   longueur des liens, et rien ne s'alignait d'une colonne à l'autre. La grille
   fixe des gouttières, ce qui fait qu'un pied de page se lit en colonnes plutôt
   qu'en zones. La colonne d'identité est la plus large : elle porte une adresse
   sur plusieurs lignes. */
.pied-interieur {
  max-width: var(--largeur); margin:0 auto; padding:0 24px 48px;
  display: grid; gap:32px 40px;
  /* `minmax(0, 1.5fr)` ET NON `1.5fr` : sans le minimum à zéro, une piste `fr`
     ne descend jamais sous la contribution max-content de son contenu — et
     depuis que la colonne d'identité demande à se dimensionner sur son texte
     (voir `.pied-identite`), c'est ELLE qui décidait de la largeur de la
     piste. La carte s'élargissait alors au lieu de se resserrer, jusqu'à
     mordre sur la colonne voisine. */
  grid-template-columns: minmax(0, 1.5fr) 1fr 1fr 1fr;
  align-items: start;
}
.pied a { text-decoration: none; }
.pied a:hover { text-decoration: underline; }

.pied-titre {
  font-size: 12px; font-weight: 650; letter-spacing: .05em; text-transform: uppercase;
  color: var(--encre); margin:0 0 12px;
}
/* LES LIENS DU PIED SONT LA ZONE LA PLUS DENSE DU SITE AU DOIGT, et ils
   faisaient 22 px de haut — sous le minimum de 24 fixé par WCAG 2.2 (critère
   2.5.8). Larges mais plats : sur un téléphone, deux entrées voisines se
   touchent presque, et la faute de frappe se paie d'un aller-retour.

   La correction ne passe PAS par un interligne plus grand, qui écarterait les
   textes sans agrandir la cible. Chaque lien devient un bloc avec sa propre
   hauteur minimale : la zone cliquable grandit, l'écart visuel entre deux
   libellés reste celui qu'il était. La gouttière descend donc de 9 à 4 px pour
   compenser — le pied garde à peu près sa hauteur. */
.pied-colonne { display: flex; flex-direction: column; gap: 4px; }
.pied-colonne a {
  color: var(--encre-douce);
  display: flex; align-items: center;
  min-height: 32px;
  transition: color 150ms var(--ease);
}
.pied-colonne a:hover { color: var(--accent-texte); text-decoration: none; }

/* ⚠️ L'EFFET D'ESTOMPE DE LA COLONNE AU SURVOL A ÉTÉ RETIRÉ LE 18/09/2026, à la
   demande de Jesse (« retire l'effet de survol sur tous les liens, tous sites
   confondus »). Il avait été posé le 9 septembre d'après le pied de Desktronic :
   au survol d'un lien, ses voisins de colonne passaient à `opacity: .5`.
   Ne pas le remettre sans le redemander. Le lien visé garde, lui, son
   changement d'encre — c'est le seul retour qui dit qu'un mot est cliquable. */

/* Même raison pour les coordonnées du pied : un numéro de téléphone est le
   lien qu'on vise le plus mal, et c'est celui qui ouvre une application.
   `inline-flex` et non `flex` sur les nouvelles coordonnées : la cible doit
   faire 32 px de haut, pas toute la largeur de la colonne — un lien large
   comme la colonne se déclenche quand on vise le vide à côté du texte. */
.pied-contact a { display: flex; align-items: center; min-height: 32px; }
.pied-coord-corps a { display: inline-flex; align-items: center; min-height: 32px; }
/* Et pour les liens de fin de carrousel, qui font 68 px de large sur 22. */
.carrousel-lien { display: inline-flex; align-items: center; min-height: 32px; }

/* La newsletter occupe toute la largeur sous les colonnes : posée dans une
   gouttière, son champ tombait à cent quarante pixels, où une adresse
   électronique ne tient pas. */
.pied-newsletter { grid-column: 1 / -1; }

/* La barre basse se détache par un filet, pas par un fond : deux aplats
   superposés en bas de page donnent l'impression que le site s'arrête deux
   fois. */
.pied-bas { border-top: 1px solid var(--filet); }
.pied-bas-interieur {
  max-width: var(--largeur); margin:0 auto; padding:26px 24px;
  display: flex; align-items: center; justify-content: space-between;
  gap:18px; flex-wrap: wrap;
}
.pied-copyright { font-size: 13px; color: var(--encre-tenue); }

/* La signature de l'atelier, accueil seulement (voir shop-footer.php).
   Elle occupe la place laissée vide à droite de la barre basse : même taille
   que le copyright, même encre tenue, et ne devient nette qu'au survol. Une
   signature qui crie sur la boutique de quelqu'un d'autre est une faute de
   goût — et une ligne de plus sous le copyright en serait une de mise en page. */
.pied-signature {
  display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
  font-size: 13px; color: var(--encre-tenue);
}
/* ⚠️ PAS DE `border-bottom` ICI. La première version en posait un au survol
   pour souligner le nom — mais `.pied a:hover` (plus haut) rétablit déjà
   `text-decoration: underline`, et les deux traits se cumulaient : un filet
   sous le texte, un second sous la boîte. Le survol du pied de page est déjà
   défini pour tout le monde ; la signature s'y conforme et ne garde en propre
   que l'assombrissement de l'encre. */
.pied-signature a { color: inherit; text-decoration: none; transition: color .18s ease; }
.pied-signature strong { font-weight: 600; }
.pied-signature a:hover,
.pied-signature a:focus-visible { color: var(--encre); }
.pied-signature-baseline { font-size: 12px; opacity: .72; }
@media (max-width: 560px) {
  /* Sous le copyright plutôt qu'à côté : deux textes de 13 px côte à côte sur
     375 px se lisent comme une seule phrase bancale. */
  .pied-signature { width: 100%; }
}

@media (max-width: 900px) {
  .pied-interieur { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap:24px 24px; }
}
@media (max-width: 560px) {
  /* `minmax(0, 1fr)` comme sur la grille large : la carte d'identité se
     dimensionne sur son texte, et une piste `1fr` sans minimum à zéro suit
     cette demande jusqu'à sortir de l'écran — 694 px de piste dans 375 px de
     téléphone, et toute la page défilait de côté. */
  .pied-interieur { grid-template-columns: minmax(0, 1fr); }
  .pied-bas-interieur { justify-content: flex-start; }
}

/* ------------------------------------------------------------ adaptatif */
@media (max-width: 900px) {
  /* ⚠️ `minmax(0, 1fr)` ET NON `1fr` — mesuré le 25/08/2026 sur un écran de
     343px : une piste `1fr` a pour taille minimale AUTOMATIQUE le min-content de
     son contenu, si bien qu'un seul élément large (ici la colonne d'achat)
     élargit la colonne au-delà de la page. La fiche mesurait 390px dans 343
     disponibles, et le débordement ne se voyait même pas : `main { overflow-x:
     clip }` le coupe. `minmax(0, …)` autorise la piste à descendre sous le
     min-content, et le contenu passe à la ligne comme prévu. `min-width: 0` sur
     les enfants ferme le même piège d'un cran plus bas. */
  .fiche { grid-template-columns: minmax(0, 1fr); gap:24px; }
  .fiche > * { min-width: 0; }
  .panier { grid-template-columns: 1fr; }
  .recap { position: static; }
}
@media (max-width: 640px) {
  /* Sous cette largeur, les actions n'ont plus la place de tenir à droite du
     logo sans le chevaucher : elles reviennent dans le flux, sur leur propre
     rangée. Le menu passe en défilement horizontal — le replier sur trois
     lignes mangerait l'écran avant même le premier produit. */
  .bandeau-haut { flex-direction: column; align-items: center; gap:12px; padding-bottom:10px; }
  .bandeau-actions { position: static; transform: none; }
  /* La bascule de thème rejoint la même rangée que les autres actions : gardée
     en absolu à gauche, elle chevaucherait le logo revenu dans le flux. */
  .bandeau-outils { position: static; transform: none; order: 3; }
  /* SUR MOBILE, LA BARRE DEVIENT UNE BANDE QU'ON FAIT GLISSER. Elle l'était
     déjà, mais rien ne le disait : pas de barre de défilement (masquée par le
     système sur téléphone), pas de coupure au bord — la dernière entrée
     visible semblait être la dernière tout court. Le fondu à droite montre
     qu'il y a une suite ; il disparaît quand on atteint le bout, puisqu'il est
     posé sur le conteneur et non sur un enfant.

     On garde le glissement plutôt qu'un menu en tiroir : sur une boutique, les
     rubriques sont le contenu principal, et les cacher derrière un bouton coûte
     un geste à chaque visite. */
  .menu {
    flex-wrap: nowrap; justify-content: flex-start; gap:24px;
    overflow-x: auto; scrollbar-width: none;
    padding-left:20px; padding-right:20px;
    scroll-snap-type: x proximity;
    -webkit-mask-image: linear-gradient(to right, #000 0, #000 calc(100% - 34px), transparent 100%);
            mask-image: linear-gradient(to right, #000 0, #000 calc(100% - 34px), transparent 100%);
  }
  .menu::-webkit-scrollbar { display: none; }
  .menu > * { scroll-snap-align: start; flex: none; }
  .champs-2 { grid-template-columns: 1fr; }
  .ligne-panier { grid-template-columns: 64px 1fr; }
  .ligne-visuel { width: 64px; height: 64px; }
  .ligne-actions { grid-column: 1 / -1; justify-content: space-between; }
}

/* Texte de fond d'une rubrique, sous la liste des produits. Mesure de lecture
   bornée : au-delà d'environ 70 caractères l'œil perd la ligne en revenant à
   la marge, et ce bloc est le seul de la boutique à être vraiment long. */
/* ---------------------------------------------------------------------------
   LE BAS D'UNE RUBRIQUE : le texte de fond, et les questions fréquentes.

   DEUX COLONNES QUAND IL Y A DEUX BLOCS. Chacun tient une mesure de lecture —
   72ch, un peu plus de sept cents pixels — dans un conteneur qui en fait mille
   cent quatre-vingts. Empilés, ils faisaient deux écrans de défilement avec un
   fleuve de blanc à droite, alors qu'ils s'adressent au même lecteur au même
   instant : celui qui a fini de regarder les produits et hésite encore.

   LE FILET DU HAUT EST PORTÉ PAR L'ENVELOPPE, pas par les deux blocs. Chacun
   avait le sien, avec sa propre respiration au-dessus — 24 px pour le texte,
   36 px pour la FAQ : côte à côte, cela donnait deux traits à deux hauteurs
   différentes et deux titres qui ne commençaient pas à la même ligne.

   UNE SEULE COLONNE S'IL N'Y A QU'UN BLOC. Un texte seul étiré sur la moitié
   gauche d'une grille laisserait un vide plus visible que celui qu'on corrige :
   sans son voisin, il reprend simplement sa mesure d'avant.
--------------------------------------------------------------------------- */
.rubrique-bas {
  /* ⚠️ RESPIRATION RELEVÉE (24/08/2026, « pas assez d'espace, manque de
     marges ») : 26px sous le filet collait les deux blocs à la pagination —
     l'écart entre le filet et les titres doit être du même ordre que celui
     qui sépare deux sections de l'accueil, pas celui de deux paragraphes. */
  margin:16px 0 64px;
  padding-top:44px;
  border-top: 1px solid var(--filet);
}
.rubrique-bas.est-double {
  display: grid;
  /* Le texte un peu plus large que la FAQ : l'un est de la prose, qui réclame
     sa mesure, l'autre une liste d'intitulés qui se contente de moins. */
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
  gap:0 clamp(32px, 4vw, 64px);
  /* Les deux colonnes ne font pas la même hauteur, et déplier une question
     change celle de droite : sans `start`, la plus courte s'étirerait et son
     contenu se retrouverait centré verticalement à chaque ouverture. */
  align-items: start;
}
.rubrique-bas-colonne { min-width: 0; }

/* LES SÉLECTIONS VOISINES, sous une page de facette. C'est le maillage interne :
   une page de facette qui ne renvoie qu'au rayon entier est un cul-de-sac pour
   le visiteur comme pour les moteurs — celui qui cherchait « bagues rubis » et
   n'en trouve aucune aujourd'hui est bien plus près de « bagues saphir » que
   de la page d'accueil.

   DES PASTILLES ET NON UNE LISTE À PUCES : elles se lisent en diagonale, elles
   se touchent au doigt, et elles ne prétendent pas être une navigation
   principale. */
/* ⚠️ MARGE BASSE AJOUTÉE (20/08/2026, « problèmes de marges ») : cette liste
   n'avait AUCUNE marge sous elle, seulement 34px au-dessus. Sur une page de
   facette (le bandeau d'avis s'affiche ensuite, `padding-top` de 8px
   seulement), les pastilles touchaient quasiment la section suivante — à
   peine plus d'espace qu'entre deux pastilles de la même ligne. */
.landing-voisines { margin:34px 0 40px; }
.landing-voisines h2 {
  font-size: 15px; font-weight: 600; margin:0 0 12px; color: var(--encre-tenue);
}
/* ⚠️ `.cat_facettes` PARTAGE CES RÈGLES, ET CE N'EST PAS UNE COMMODITÉ
   (4/09/2026, signalé par Jesse : « aligne ces liens sur une ligne »).
   La reprise PrestaShop a laissé, dans la description de vingt-deux rubriques
   de bagues, un `<div class="cat_facettes">` contenant une liste de liens vers
   les pages de facette. Cette classe venait de l'ancien thème : rien ne la
   stylait ici, et elle sortait donc en liste à puces soulignée, sur seize
   lignes, juste sous le titre de la rubrique — de quoi faire croire au client
   que « les styles ne chargent pas », alors que la feuille était bien servie et
   que tout le reste de la page était en place.

   Elle dit exactement la même chose que les sélections voisines — voici les
   rayons d'à côté — donc elle prend le même dessin, aux mêmes déclarations.
   Deux blocs de pastilles qui divergeraient d'un pixel sur la même page
   seraient pires que pas de pastilles du tout. */
.landing-voisines ul,
.cat_facettes ul {
  list-style: none; margin:0; padding:0;
  display: flex; flex-wrap: wrap; gap:8px;
}
.cat_facettes { margin: 14px 0 4px; }
.cat_facettes li { margin: 0; }
.landing-voisines a,
.cat_facettes a {
  display: inline-block; padding:7px 13px;
  border: 1px solid var(--filet); border-radius: 999px;
  font-size: 13.5px; border-bottom: 1px solid var(--filet);
  background: var(--surface);
  /* Le contenu importé arrive sans classe sur ses liens : ils héritent donc du
     soulignement et de l'encre d'accent des liens de prose. Une pastille ne se
     souligne pas. */
  text-decoration: none; color: var(--encre-douce);
}
.landing-voisines a:hover, .landing-voisines a:focus-visible,
.cat_facettes a:hover, .cat_facettes a:focus-visible {
  background: var(--fond-doux); border-color: var(--encre-tenue);
  color: var(--encre); text-decoration: none; outline: none;
}

/* ═══ LE TEXTE DE BAS DE RUBRIQUE ET DE PAGE DE FACETTE ═════════════════════
   C'est le plus long texte du site — jusqu'à quatre mille signes — et c'était le
   moins habillé : une couleur, une mesure, cinq déclarations. Il se lit à côté
   d'une FAQ en accordéon, dont les intitulés sont nets et espacés ; à côté
   d'elle, un bloc de prose sans hiérarchie paraissait brouillon.

   La passe est typographique, pas décorative : de l'interligne, des intertitres
   qui se distinguent vraiment des paragraphes, des listes qui respirent, et un
   gras qui se voit. Rien qui ajoute un cadre ou un fond — la colonne de droite
   n'en a pas non plus, et deux traitements différents côte à côte se liraient
   comme deux blocs sans rapport. */
.texte-rubrique {
  max-width: 72ch;
  margin:0;
  color: var(--encre-douce);
  font-size: 15.5px; line-height: 1.68;
}
/* LES INTERTITRES PORTENT LA POLICE DE TITRE. Ils étaient dans la police
   courante, au même gris que le texte à deux points près : sur quatre mille
   signes, on ne voyait plus l'articulation, et le texte paraissait plus long
   qu'il ne l'est. La famille de titre les distingue d'un coup d'œil, sans
   qu'il ait fallu les grossir. */
.texte-rubrique :is(h2, h3) {
  color: var(--encre);
  font-family: var(--police-titre);
  font-weight: var(--marque-poids-titre, 600);
  line-height: 1.3;
}
/* ⚠️ TROIS NIVEAUX, TROIS TAILLES (25/08/2026, « plus de cohésion sur la taille
   des titres, sous-titres »). Les `h2` et les `h3` étaient au MÊME corps (19px) :
   un texte de quatre mille signes structuré en deux niveaux les rendait donc
   indiscernables — « Notre atelier & expertise » (une partie) pesait autant que
   « Une collection complète… » (une sous-partie), et l'articulation qu'on avait
   pris soin d'écrire ne se lisait pas.

   L'ÉCHELLE EST CELLE DU SITE, pas une de plus : le titre qui OUVRE la colonne
   reprend `--titre-h2`, exactement le corps du « Questions fréquentes » d'en
   face — les deux colonnes de la bande commencent alors sur la même note, ce
   qui est le premier signe de cohésion qu'on voit. À l'intérieur, les parties
   descendent à `--titre-h3` et les sous-parties à 17px, soit un rapport franc
   (27 / 20 / 17) au lieu de trois valeurs voisines. */
.texte-rubrique h2 { font-size: var(--titre-h3); margin:34px 0 12px; }
.texte-rubrique h3 { font-size: 17px; margin:24px 0 10px; }
.texte-rubrique :is(h2, h3):first-child {
  margin-top:0;
  font-size: var(--titre-h2); letter-spacing: -0.015em;
}
.texte-rubrique p { margin:0 0 14px; }
.texte-rubrique strong, .texte-rubrique b { color: var(--encre); font-weight: 600; }
.texte-rubrique a { color: var(--accent-texte); text-underline-offset: 3px; }
/* Les puces prennent la couleur d'accent, comme celles de la description
   produit : c'est le même geste de lecture, il doit avoir le même repère. */
.texte-rubrique :is(ul, ol) { padding-left:20px; margin:0 0 14px; }
.texte-rubrique li { margin-bottom:6px; }
.texte-rubrique li::marker { color: var(--accent); }
/* Un tableau importé n'avait aucune règle : colonnes collées, aucun filet. */
.texte-rubrique table { width: 100%; border-collapse: collapse; margin:0 0 16px; font-size: 14.5px; }
.texte-rubrique :is(th, td) { padding:8px 10px; border: 1px solid var(--filet); text-align: left; }
.texte-rubrique th { background: var(--fond-doux); color: var(--encre); font-weight: 600; }
.texte-rubrique img { max-width: 100%; height: auto; border-radius: var(--rayon); }
.texte-rubrique blockquote {
  margin: 0 0 16px; padding: 2px 0 2px 16px;
  border-left: 2px solid var(--accent);
  color: var(--encre); font-style: normal;
}

/* ============================================================================
   ÉLÉMENTS REPRIS DU SITE VITRINE, RHABILLÉS EN SOBRE
   ----------------------------------------------------------------------------
   Le balisage du bandeau cookies et du widget de chat vient du site existant,
   éprouvé en production : rôles ARIA corrects, comportement piloté par main.js
   et chat.js. On ne réécrit donc que l'APPARENCE, en réutilisant les mêmes
   noms de classes — c'est tout l'intérêt de la reprise.

   Deux règles pour rester sobre : aucune ombre portée colorée, aucun dégradé,
   et les mêmes tokens que le reste de la boutique. Un bandeau cookies qui
   détonne du site est un bandeau qu'on prend pour une publicité.
   ============================================================================ */

/* ---------------------------------------------- bandeau de maintenance */
.bandeau-maintenance {
  display: flex; align-items: center; justify-content: center; gap:10px;
  flex-wrap: wrap; padding:9px 16px; font-size: 14px;
  background: var(--accent-doux); color: var(--encre);
  border-bottom: 1px solid var(--filet);
}
.bandeau-maintenance a { color: var(--accent-texte); }

/* ------------------------------------------------------ bandeau cookies */
.cookie-banner {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 60;
  background: var(--surface); border-top: 1px solid var(--filet-fort);
}
.cookie-banner[hidden] { display: none; }
.cookie-banner-inner {
  max-width: var(--largeur); margin:0 auto; padding:18px 24px;
  display: flex; align-items: center; justify-content: space-between;
  gap:20px; flex-wrap: wrap;
}
.cookie-banner-content { flex: 1; min-width: 260px; }
.cookie-banner-title { font-size: 15px; font-weight: 600; margin:0 0 4px; }
/* L'emoji du gabarit d'origine est masqué : un pictogramme décoratif dans un
   titre est exactement le genre de marqueur qu'on nous a demandé d'éviter. */
.cookie-banner-emoji { display: none; }
.cookie-banner-desc { margin:0; font-size: 14px; color: var(--encre-douce); }
.cookie-banner-link { color: var(--accent-texte); }
.cookie-banner-actions-row { display: flex; gap:8px; flex-wrap: wrap; }

.cookie-btn {
  font: inherit; font-size: 14px; padding:9px 16px; cursor: pointer;
  border-radius: var(--rayon); border: 1px solid var(--filet-fort);
  background: var(--surface); color: var(--encre);
}
.cookie-btn:hover { border-color: var(--encre-tenue); }
.cookie-btn-primary { background: var(--accent); color: var(--accent-encre); border-color: transparent; }
.cookie-btn-primary:hover { filter: brightness(1.06); }
.cookie-btn-tertiary { border-color: transparent; text-decoration: underline; }

.cookie-modal { position: fixed; inset: 0; z-index: 70; display: flex; align-items: center; justify-content: center; padding:20px; }
.cookie-modal[hidden] { display: none; }
.cookie-modal-backdrop { position: absolute; inset: 0; background: rgba(0, 0, 0, .5); }
.cookie-modal-panel {
  position: relative; width: min(560px, 100%); max-height: 84vh; overflow: auto;
  background: var(--surface); border: 1px solid var(--filet-fort);
  border-radius: var(--rayon-large); padding:24px;
}
.cookie-modal-head { display: flex; align-items: flex-start; justify-content: space-between; gap:16px; }
.cookie-modal-close { background: none; border: 0; font-size: 22px; line-height: 1; cursor: pointer; color: var(--encre-tenue); }
.cookie-modal-intro { color: var(--encre-douce); font-size: 14px; }
.cookie-cat { border-top: 1px solid var(--filet); padding:14px 0; }
.cookie-cat-head { display: flex; align-items: center; justify-content: space-between; gap:16px; }
.cookie-modal-foot { display: flex; justify-content: flex-end; gap:8px; padding-top:16px; border-top: 1px solid var(--filet); }

.cookie-toggle { position: relative; display: inline-block; width: 42px; height: 24px; flex: none; }
.cookie-toggle input { position: absolute; opacity: 0; width: 100%; height: 100%; margin:0; cursor: pointer; }
.cookie-toggle-slider {
  position: absolute; inset: 0; border-radius: 999px;
  background: var(--filet-fort); transition: background .15s ease; pointer-events: none;
}
.cookie-toggle-slider::before {
  content: ''; position: absolute; top: 3px; left: 3px; width: 18px; height: 18px;
  border-radius: 50%; background: var(--surface); transition: transform .15s ease;
}
.cookie-toggle input:checked + .cookie-toggle-slider { background: var(--accent); }
.cookie-toggle input:checked + .cookie-toggle-slider::before { transform: translateX(18px); }
/* Une catégorie obligatoire doit se voir comme telle : grisée ET non
   cliquable, sinon on croit à un réglage refusé par erreur. */
.cookie-toggle.is-disabled { opacity: .5; }
.cookie-toggle.is-disabled input { cursor: not-allowed; }

/* ---------------------------------------------------- chat en direct -----
   BLOC REPRIS EN ENTIER de style.css, jetons remappés — et c'est la troisième
   fois que la leçon se vérifie. Mon premier habillage, écrit à vue d'après les
   noms de classes, laissait le panneau invisible : chat.js pose `is-open` sur
   le PANNEAU et sur le BOUTON, pas sur le conteneur. Pire, le panneau est une
   machine à états (`[data-state]` / `[data-shown-when]`) qui décide quelle
   étape s'affiche — sans ces règles, aucune ne s'affiche.
   Réécrire l'habillage d'un composant piloté par du JavaScript revient à
   réécrire une partie de son comportement. On reprend, on remappe.

   Le CONTENEUR faisait partie de l'oubli, et c'est lui qui ancre tout le reste :
   sans `position: fixed`, il repart dans le flux en bas de page, le bouton se
   retrouve collé à la marge gauche et le panneau, positionné en absolu, va se
   caler sur le bloc de la page. On voyait alors une bulle à gauche ouvrir un
   panneau à droite. La seule règle qui survivait était la variante mobile — un
   `@media` ne remplace jamais la règle de base. */
/* LA FAÇADE DE TAWK (partials/tawk.php) : un rond de 60 px à 20 px des bords,
   les mesures de la bulle de Tawk elle-même — quand le vrai script arrive et
   remplace la façade, rien ne saute à l'écran. */
.tawk-facade {
  position: fixed; right: 20px; bottom: 20px; z-index: 9999;
  width: 60px; height: 60px; border-radius: 50%; border: 0; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--accent); color: var(--accent-encre); cursor: pointer;
  box-shadow: 0 4px 14px rgba(0, 0, 0, .22);
  transition: transform .15s ease;
}
.tawk-facade[hidden] { display: none; }
.tawk-facade:hover, .tawk-facade:focus-visible { transform: scale(1.06); }
.tawk-facade.est-en-chargement { opacity: .7; cursor: progress; }
@media (prefers-reduced-motion: reduce) { .tawk-facade { transition: none; } }

#bmp-chat-widget {
  position: fixed;
  bottom: 24px;
  right: 24px;
  z-index: 9999;
}
.bmp-chat-toggle {
  display: inline-flex;
  align-items: center;
  gap:8px;
  padding:14px 20px 14px 16px;
  border-radius: 999px;
  background: var(--accent);
  color: var(--accent-encre);
  border: none;
  cursor: pointer;
  font-family: inherit;
  font-size: 14px;
  font-weight: 600;
  letter-spacing: -0.005em;
  box-shadow: 0 10px 32px -8px rgba(125, 155, 255, 0.55), 0 4px 14px rgba(0, 0, 0, 0.25);
  transition: all 200ms ease;
  position: relative;
}
.bmp-chat-toggle::before {
  /* Point "online" — anneau clair sur fond plein vert, visible dans les deux thèmes */
  content: '';
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--accent-encre);
  flex-shrink: 0;
  margin-right:2px;
  box-shadow: 0 0 0 0 rgba(255, 255, 255, 0.6);
  animation: bmp-chat-toggle-ping 2.4s ease-in-out infinite;
}
@keyframes bmp-chat-toggle-ping {
  0%, 100% { box-shadow: 0 0 0 0 rgba(255, 255, 255, 0.55); }
  50% { box-shadow: 0 0 0 6px rgba(255, 255, 255, 0); }
}
.bmp-chat-toggle:hover {
  transform: translateY(-2px) scale(1.03);
  box-shadow: 0 14px 38px -8px rgba(125, 155, 255, 0.65), 0 6px 18px rgba(26, 22, 16, 0.3);
}
.bmp-chat-toggle .bmp-chat-icon-open,
.bmp-chat-toggle .bmp-chat-icon-close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.bmp-chat-toggle .bmp-chat-icon-close { display: none; }
.bmp-chat-toggle.is-open::before { display: none; }
.bmp-chat-toggle.is-open .bmp-chat-icon-open { display: none; }
.bmp-chat-toggle.is-open .bmp-chat-icon-close { display: inline-flex; }
.bmp-chat-toggle.is-open .bmp-chat-toggle-label { display: none; }
.bmp-chat-toggle.is-open {
  padding:10px 12px;
}

/* Badge */
.bmp-chat-badge {
  position: absolute;
  top: -4px;
  right: -4px;
  min-width: 20px;
  height: 20px;
  padding:0 5px;
  background: var(--notification);
  color: #FFFFFF;
  font-size: 11px;
  font-weight: 700;
  border-radius: 10px;
  display: none;
  align-items: center;
  justify-content: center;
  border: 2px solid var(--fond);
}

/* Panel */
.bmp-chat-panel {
  position: absolute;
  bottom: 72px;
  right: 0;
  width: 380px;
  max-width: calc(100vw - 32px);
  height: 560px;
  max-height: calc(100vh - 120px);
  background: var(--surface);
  border: 1px solid var(--filet-fort);
  border-radius: 16px;
  box-shadow: 0 24px 48px -12px rgba(0, 0, 0, 0.5), 0 8px 24px rgba(0, 0, 0, 0.3);
  display: flex;
  flex-direction: column;
  opacity: 0;
  pointer-events: none;
  transform: translateY(12px) scale(0.96);
  transform-origin: bottom right;
  transition: opacity 220ms ease, transform 220ms ease;
  overflow: hidden;
}
.bmp-chat-panel.is-open {
  opacity: 1;
  pointer-events: auto;
  transform: translateY(0) scale(1);
}

/* Header */
.bmp-chat-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding:18px 20px;
  background: linear-gradient(135deg, rgba(125, 155, 255, 0.06), transparent);
  border-bottom: 1px solid var(--filet);
}
.bmp-chat-title {
  font-size: 15px;
  font-weight: 600;
  color: var(--encre);
  letter-spacing: -0.01em;
}
.bmp-chat-status {
  display: flex;
  align-items: center;
  gap:6px;
  font-size: 10px;
  color: var(--encre-douce);
  margin-top:3px;
  letter-spacing: 0.02em;
  text-transform: uppercase;
}
.bmp-chat-dot {
  /* Statut "en ligne" — reste un vrai vert dans les deux thèmes */
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--status-live);
  box-shadow: 0 0 0 0 rgba(var(--status-live-rgb), 0.5), 0 0 0 0 rgba(var(--status-live-deep-rgb), 0.35);
  animation: status-pulse-dual 2s ease-in-out infinite;
}
.bmp-chat-close {
  background: transparent;
  border: none;
  color: var(--encre-douce);
  cursor: pointer;
  padding:6px;
  border-radius: 6px;
  transition: background 150ms ease;
  display: inline-flex;
}
.bmp-chat-close:hover {
  background: var(--fond-doux);
  color: var(--encre);
}

/* Form de démarrage */
.bmp-chat-start {
  padding:20px;
  flex-direction: column;
  gap:14px;
  overflow-y: auto;
}
.bmp-chat-intro {
  font-size: 13px;
  line-height: 1.55;
  color: var(--encre-tenue);
  margin:0 0 4px;
}
.bmp-chat-field {
  display: flex;
  flex-direction: column;
  gap:5px;
}
.bmp-chat-field span {
  font-size: 10px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--encre-douce);
}
.bmp-chat-field input,
.bmp-chat-field textarea {
  padding:10px 12px;
  background: var(--fond-doux);
  border: 1px solid var(--filet);
  border-radius: 8px;
  color: var(--encre);
  font-family: inherit;
  font-size: 13.5px;
  transition: border-color 150ms ease;
}
.bmp-chat-field input:focus,
.bmp-chat-field textarea:focus {
  outline: none;
  border-color: var(--accent);
}
.bmp-chat-field textarea {
  resize: vertical;
  min-height: 76px;
  font-family: inherit;
  line-height: 1.5;
}
.bmp-chat-send-btn {
  margin-top:4px;
  padding:11px 18px;
  background: var(--accent);
  color: var(--surface);
  border: none;
  border-radius: 8px;
  font-size: 13.5px;
  font-weight: 600;
  cursor: pointer;
  transition: transform 150ms ease;
}
.bmp-chat-send-btn:hover { transform: translateY(-1px); }
.bmp-chat-send-btn:disabled { opacity: 0.5; cursor: wait; }
.bmp-chat-rgpd {
  font-size: 10.5px;
  color: var(--encre-douce);
  margin:4px 0 0;
  line-height: 1.45;
}

/* Thread */
.bmp-chat-thread {
  flex: 1;
  overflow-y: auto;
  padding:18px 18px 12px;
  flex-direction: column;
  gap:8px;
}
.bmp-chat-msg {
  display: flex;
  flex-direction: column;
  max-width: 80%;
}
.bmp-chat-msg.is-visitor {
  align-self: flex-end;
  align-items: flex-end;
}
.bmp-chat-msg.is-admin {
  align-self: flex-start;
  align-items: flex-start;
}
.bmp-chat-bubble {
  padding:8px 12px;
  border-radius: 14px;
  font-size: 13.5px;
  line-height: 1.5;
  word-wrap: break-word;
  white-space: pre-wrap;
}
.bmp-chat-msg.is-visitor .bmp-chat-bubble {
  background: var(--accent);
  color: var(--surface);
  border-bottom-right-radius: 4px;
}
.bmp-chat-msg.is-admin .bmp-chat-bubble {
  background: var(--fond-doux);
  color: var(--encre);
  border: 1px solid var(--filet);
  border-bottom-left-radius: 4px;
}
/* Bulle contenant une réponse mise en forme depuis le back-office. Le
   `white-space: pre-wrap` de la bulle ordinaire doit sauter : appliqué à du
   HTML, il rend visibles les retours à la ligne et l'indentation du balisage,
   et chaque paragraphe se retrouve précédé d'une ligne vide. Les marges sont
   resserrées — une bulle de chat n'est pas un article — et la dernière tombe à
   zéro pour ne pas creuser le bas de la bulle. */
.bmp-chat-rich { white-space: normal; }
.bmp-chat-rich > :last-child { margin-bottom:0; }
.bmp-chat-rich p { margin:0 0 8px; }
.bmp-chat-rich h2,
.bmp-chat-rich h3,
.bmp-chat-rich h4 { font-size: 14.5px; font-weight: 600; margin:10px 0 5px; line-height: 1.3; }
.bmp-chat-rich ul { list-style: disc; margin:0 0 8px; padding-left:18px; }
.bmp-chat-rich ol { list-style: decimal; margin:0 0 8px; padding-left:18px; }
.bmp-chat-rich li { margin-bottom:3px; }
.bmp-chat-rich a { color: var(--accent-texte); text-decoration: underline; }
.bmp-chat-rich strong { font-weight: 600; }
.bmp-chat-rich blockquote {
  border-left: 2px solid var(--filet);
  padding-left:10px;
  margin:8px 0;
  color: var(--encre-douce);
  font-style: italic;
}
.bmp-chat-rich pre {
  background: var(--surface);
  border-radius: 6px;
  padding:8px 10px;
  overflow-x: auto;
  font-family: var(--police-mono);
  font-size: 12px;
}
.bmp-chat-rich code { font-family: var(--police-mono); font-size: .92em; }
.bmp-chat-rich img { max-width: 100%; height: auto; border-radius: 6px; margin:6px 0; }
.bmp-chat-rich hr { border: 0; border-top: 1px solid var(--filet); margin:10px 0; }

.bmp-chat-meta {
  font-size: 9px;
  color: var(--encre-douce);
  margin-top:3px;
  letter-spacing: 0.02em;
  padding:0 4px;
}

/* Form envoi */
.bmp-chat-send {
  gap:8px;
  padding:12px 14px 14px;
  border-top: 1px solid var(--filet);
  align-items: flex-end;
}

/* ============================================================================
   STATE MACHINE — un seul état affiché à la fois dans le panel
   data-state="idle"   → seul le formulaire de démarrage est visible
   data-state="active" → thread + form d'envoi (conversation en cours)
   data-state="closed" → thread seul (conversation closée par l'admin)
   ============================================================================ */
.bmp-chat-panel [data-shown-when] {
  display: none;
}
.bmp-chat-panel[data-state="idle"]   [data-shown-when="idle"]   { display: flex; flex-direction: column; }
.bmp-chat-panel[data-state="active"] [data-shown-when="active"] { display: flex; }
.bmp-chat-panel[data-state="closed"] .bmp-chat-thread { display: flex; flex-direction: column; }
.bmp-chat-panel[data-state="active"] .bmp-chat-thread { flex-direction: column; }
.bmp-chat-send textarea {
  flex: 1;
  padding:10px 12px;
  background: var(--fond-doux);
  border: 1px solid var(--filet);
  border-radius: 8px;
  color: var(--encre);
  font-family: inherit;
  font-size: 13.5px;
  resize: none;
  line-height: 1.5;
  min-height: 40px;
  max-height: 100px;
}
.bmp-chat-send textarea:focus {
  outline: none;
  border-color: var(--accent);
}
.bmp-chat-send button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: 8px;
  background: var(--accent);
  color: var(--surface);
  border: none;
  cursor: pointer;
  flex-shrink: 0;
  transition: transform 150ms ease;
}
.bmp-chat-send button:hover { transform: translateY(-1px); }

/* Mobile : panel quasi plein écran */
@media (max-width: 480px) {
  #bmp-chat-widget {
    bottom: 16px;
    right: 16px;
  }
  .bmp-chat-panel {
    width: calc(100vw - 24px);
    right: -8px;
    height: calc(100vh - 100px);
    max-height: calc(100vh - 100px);
  }
  .bmp-chat-toggle {
    padding:10px 14px 10px 14px;
    font-size: 13px;
  }
  .bmp-chat-toggle-label {
    display: none;
  }
  .bmp-chat-toggle::before {
    margin-right:0;
  }
}

/* ============================================================================
   COUCHE DE COMPATIBILITÉ — pages reprises du site vitrine
   ----------------------------------------------------------------------------
   Contact, espace client, pages légales et 404 sont repris tels quels : 2 131
   lignes de gabarits déjà éprouvés, avec leur CSRF, leur limitation de débit,
   leurs pièces jointes validées et leurs messages d'erreur. Les réécrire pour
   changer des noms de classes aurait été absurde.
   Ces pages n'emploient qu'une vingtaine de classes, très répétées : il suffit
   de leur donner ici une définition SOBRE, avec les tokens de la boutique.
   C'est la couche la moins chère possible — et le jour où une page est refaite,
   elle cesse simplement de dépendre de ce bloc.
   ============================================================================ */

.container { max-width: var(--largeur); margin:0 auto; padding:0 24px; }
.page-hero { padding:48px 0 20px; }
.page-hero h1 { margin-bottom:10px; }
.sec-after-hero { padding-bottom:48px; }
/* Même traitement de coupure que `.sous-titre` : c'est le second nom du même
   objet — le chapô qui suit un titre de page. Sans lui, la page contact
   rejetait « temps. » seul sur une quatrième ligne sous 600 px. */
.lead { color: var(--encre-douce); font-size: 17px; max-width: 62ch; text-wrap: pretty; }
/* Colonne de lecture, CENTRÉE. Elle ne l'était pas : la largeur était bien
   limitée à 72 caractères, mais le bloc restait collé à gauche d'une colonne de
   1180 px — le texte partait du bord et laissait un vide de 500 px à droite, ce
   qui se lit comme un défaut de mise en page et non comme un parti pris. */
.mesure-texte { max-width: 72ch; margin-inline: auto; }
/* LE TITRE SUIT LA COLONNE DE LECTURE (4/09/2026). Sur les pages de texte pur —
   CGV, mentions légales, confidentialité, cookies — le contenu est une colonne
   de 72 caractères centrée dans un conteneur de 1 400 px, mais le fil d'Ariane
   et le titre restaient calés au bord gauche : le titre commençait trois cents
   pixels avant son propre texte, et la page paraissait mal assemblée. Même
   mesure, même centrage, et les deux blocs se lisent comme un seul document.
   `.entete-page` a la même taille de police que `.prose` (16 px), donc `72ch`
   y vaut exactement la même largeur — ce qui ne serait pas vrai sur un bloc
   au corps différent. */
.contenu:has(> .prose.mesure-texte) .entete-page { max-width: 72ch; margin-inline: auto; }

/* Corps de texte long : pages légales, articles. Même mesure de lecture que le
   texte de rubrique — au-delà l'œil perd la ligne en revenant à la marge. */
.prose { max-width: 72ch; color: var(--encre-douce); }
.prose :is(h2, h3) { color: var(--encre); margin:24px 0 10px; }
.prose h2 { font-size: var(--titre-h2); }
.prose h3 { font-size: var(--titre-h3); }
.prose p { margin:0 0 14px; }
.prose :is(ul, ol) { padding-left:20px; margin:0 0 14px; }
.prose a { color: var(--accent-texte); }

/* ═══ LE CORPS D'ARTICLE ═══════════════════════════════════════════════════
   `.prose` sert partout — pages légales, descriptions de rubrique, FAQ — et
   son rythme est celui d'un bloc court inséré dans une page. Un article fait
   deux mille mots : il demande un rythme de LECTURE, pas de consultation.
   D'où un jeu de règles propre au corps d'article, sans toucher au reste.

   Trois choses seulement, et chacune se voit : le texte respire (interlignage
   et taille), les intertitres reprennent la police des titres et s'espacent
   franchement de ce qui les précède, et tout ce qui n'est pas du paragraphe —
   images, citations, tableaux, listes — cesse d'être collé au texte. */
.article-corps {
  font-size: 17px; line-height: 1.72; color: var(--encre-douce);
}
.article-corps p { margin: 0 0 1.15em; }
.article-corps :is(h2, h3, h4) {
  font-family: var(--police-titre);
  font-weight: var(--marque-poids-titre, 700);
  color: var(--encre);
  line-height: 1.25;
  /* Un intertitre appartient à ce qui SUIT : trois fois plus d'air au-dessus
     qu'en dessous, sinon on le lit comme la fin du paragraphe précédent. */
  margin: 2em 0 .6em;
  scroll-margin-top: calc(var(--collant, 106px) + 12px);
}
.article-corps h2 { font-size: var(--titre-h2); letter-spacing: -.015em; }
.article-corps h3 { font-size: var(--titre-h3); }
.article-corps h4 { font-size: 18px; }
.article-corps :is(ul, ol) { margin: 0 0 1.15em; padding-left: 1.4em; }
.article-corps li { margin: 0 0 .45em; }
.article-corps li::marker { color: var(--accent-texte); }
.article-corps :is(strong, b) { color: var(--encre); font-weight: 600; }
/* Le lien se souligne : dans un texte long, la seule couleur ne suffit pas à
   distinguer un lien d'un mot mis en valeur. */
.article-corps a {
  color: var(--accent-texte);
  text-decoration: underline; text-underline-offset: 3px;
  text-decoration-thickness: 1px; text-decoration-color: var(--filet-fort);
}
.article-corps a:hover { text-decoration-color: currentColor; }
.article-corps img {
  max-width: 100%; height: auto; display: block;
  margin: 1.8em auto; border-radius: var(--rayon-large);
}
.article-corps blockquote {
  margin: 1.8em 0; padding: 2px 0 2px 22px;
  border-left: 3px solid var(--accent);
  color: var(--encre); font-style: italic; font-size: 18px;
}
.article-corps blockquote p:last-child { margin-bottom: 0; }
/* UN TABLEAU LARGE DÉFILE DANS SON CADRE, jamais la page : les guides de
   tailles comparent quatre systèmes de mesure, ce qui déborde d'une colonne
   de lecture de 760 px. Sans cela, c'est le document entier qui se décale et
   le texte sort de l'écran à gauche. */
.article-corps table {
  display: block; overflow-x: auto; width: 100%;
  border-collapse: collapse; margin: 1.8em 0; font-size: 15px;
}
.article-corps :is(th, td) {
  border: 1px solid var(--filet); padding: 9px 12px; text-align: left;
  white-space: nowrap;
}
.article-corps th { background: var(--fond-doux); font-weight: 600; color: var(--encre); }
.article-corps hr {
  border: 0; border-top: 1px solid var(--filet); margin: 2.4em 0;
}
@media (max-width: 700px) {
  .article-corps { font-size: 16.5px; }
  .article-corps h2 { font-size: 23px; }
  .article-corps h3 { font-size: 19px; }
}
.prose table { width: 100%; border-collapse: collapse; margin:0 0 18px; }
.prose :is(th, td) { border: 1px solid var(--filet); padding:8px 10px; text-align: left; font-size: 14px; }

/* ------------------------------------------------------------ formulaires */
.form-group { display: flex; flex-direction: column; gap:6px; margin-bottom:16px; }
.form-label { font-size: 14px; font-weight: 550; }
.form-input, .form-select, .form-textarea, .form-group textarea, .form-group select {
  font: inherit; font-size: 15px; padding:10px 12px; width: 100%;
  border: 1px solid var(--filet-fort); border-radius: var(--rayon);
  background: var(--surface); color: var(--encre);
}
.form-input:focus, .form-select:focus, .form-textarea:focus,
.form-group textarea:focus, .form-group select:focus {
  outline: 2px solid var(--accent-doux); outline-offset: 1px; border-color: var(--accent);
}
.form-grid-2, .form-row-2 { display: grid; grid-template-columns: 1fr 1fr; gap:0 16px; }
.form-checkbox { display: flex; align-items: flex-start; gap:9px; font-size: 14px; color: var(--encre-douce); }
.form-checkbox input { margin-top:3px; flex: none; }
.form-hint, .form-help { font-size: 13px; color: var(--encre-tenue); }
.form-secondary-link { font-size: 14px; color: var(--encre-douce); }
.form-secondary-link a { color: var(--accent-texte); }

/* Les messages d'erreur et de succès NE dépendent pas de la couleur seule :
   un liseré et un texte explicite les distinguent, sinon un daltonien lit deux
   fois le même bloc. */
.form-errors, .alert-error, .alert-danger {
  border: 1px solid var(--alerte); color: var(--alerte);
  padding:12px 15px; border-radius: var(--rayon); margin:0 0 18px; font-size: 14px;
}
.form-errors ul { margin:6px 0 0; padding-left:18px; }
.form-success, .alert-success {
  border: 1px solid var(--succes); color: var(--succes);
  padding:12px 15px; border-radius: var(--rayon); margin:0 0 18px; font-size: 14px;
}
.alert { border: 1px solid var(--filet-fort); background: var(--fond-doux);
  padding:12px 15px; border-radius: var(--rayon); margin:0 0 18px; font-size: 14px; }

/* ---------------------------------------------------------------- boutons */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap:8px;
  padding:11px 20px; border-radius: var(--rayon); border: 1px solid var(--filet-fort);
  background: var(--surface); color: var(--encre);
  font: inherit; font-size: 15px; font-weight: 550; text-decoration: none; cursor: pointer;
}
.btn:hover { border-color: var(--encre-tenue); }
.btn-primary { background: var(--accent); color: var(--accent-encre); border-color: transparent; }
.btn-primary:hover { filter: brightness(1.06); border-color: transparent; }
.btn-lg { padding:13px 24px; font-size: 16px; }
.btn-block, .btn-full { width: 100%; }
.btn:disabled { opacity: .5; cursor: not-allowed; filter: none; }

/* ---------------------------------------------------------- espace client */
.account-form {
  max-width: 460px; margin:0 auto 64px;
  border: 1px solid var(--filet); border-radius: var(--rayon-large);
  background: var(--surface); padding:24px;
}
.account-layout { display: grid; grid-template-columns: 220px 1fr; gap:32px; padding-bottom:64px; }
.account-nav { display: flex; flex-direction: column; gap:2px; }
.account-nav-link {
  padding:9px 12px; border-radius: var(--rayon); text-decoration: none;
  color: var(--encre-douce); font-size: 15px;
}
.account-nav-link:hover { background: var(--fond-doux); color: var(--encre); }
.account-nav-link.is-active { background: var(--accent-doux); color: var(--accent-texte); }
.account-card {
  border: 1px solid var(--filet); border-radius: var(--rayon-large);
  background: var(--surface); padding:20px; margin-bottom:18px;
}
.account-card-header { display: flex; align-items: baseline; justify-content: space-between; gap:16px; margin-bottom:14px; }
.account-card-header h2 { margin:0; font-size: 17px; }
.account-card-link { font-size: 14px; color: var(--accent-texte); text-decoration: none; }
.account-stat-label { font-size: 13px; color: var(--encre-tenue); margin:0; }
.account-stat-value { font-size: 19px; font-weight: 650; margin:2px 0; }
.account-stat-meta { font-size: 13px; color: var(--encre-tenue); margin:0; }
.account-recent-list { list-style: none; margin:0; padding:0; }
.account-recent-item {
  display: flex; align-items: center; justify-content: space-between; gap:14px;
  padding:10px 0; border-bottom: 1px solid var(--filet);
}
.account-recent-item:last-child { border-bottom: 0; }
.account-recent-primary { font-weight: 550; font-size: 15px; }
.account-recent-meta { font-size: 13px; color: var(--encre-tenue); }

.status-pill {
  display: inline-block; padding:2px 9px; border-radius: 999px;
  font-size: 12px; font-weight: 550;
  border: 1px solid var(--filet-fort); color: var(--encre-douce);
}
.status-pill-open, .status-pill-answered, .status-pill-active { border-color: var(--succes); color: var(--succes); }
.status-pill-closed, .status-pill-cancelled { border-color: var(--filet-fort); color: var(--encre-tenue); }

/* Le logo textuel du gabarit vitrine, quand une page le rend elle-même. */
.logo-text { font-weight: 650; letter-spacing: -0.02em; }
.logo-mark { display: none; }

@media (max-width: 760px) {
  .account-layout { grid-template-columns: 1fr; gap:20px; }
  .form-grid-2, .form-row-2 { grid-template-columns: 1fr; }
}

/* ------------------------ éditeur riche du formulaire de contact ---------
   La palette d'emoji DOIT être `display:none` par défaut : sans cette règle
   elle se déroule en pleine page, ce qui n'est pas un défaut d'esthétique mais
   de fonctionnement — le champ de saisie disparaît sous cinq rangées d'emoji.
   C'est le premier symptôme visible d'une reprise de balisage sans sa feuille
   de style, et un bon rappel : reprendre du HTML oblige à reprendre AUSSI les
   règles dont dépend son comportement, pas seulement celles qui l'habillent. */
/* ⚠ LE SÉLECTEUR PORTE LE NOM DE LA BALISE, et ce n'est pas de la coquetterie :
   `.rte-shadow-field` seul (0,1,0) perdait contre `.form-group textarea`
   (0,1,1), qui impose `width: 100%`. Le miroir caché du champ riche mesurait
   donc toute la largeur du formulaire et, décalé par sa marge négative,
   débordait la page de 48 px — un défilement horizontal sur la seule page de
   contact, invisible tant qu'on ne mesure pas. */
textarea.rte-shadow-field {
  position: absolute; width: 1px; height: 1px; padding:0; margin:-1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); clip-path: inset(50%);
  white-space: nowrap; border: 0;
}
.rte {
  border: 1px solid var(--filet-fort); border-radius: var(--rayon);
  background: var(--surface); overflow: visible;
}
.rte:focus-within { border-color: var(--accent); }
.rte-toolbar {
  display: flex; align-items: center; gap:2px; flex-wrap: wrap; position: relative;
  padding:6px; border-bottom: 1px solid var(--filet);
}
.rte-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; border-radius: 6px; border: 0; cursor: pointer;
  background: none; color: var(--encre-douce); font: inherit; font-size: 13px;
}
.rte-btn:hover { background: var(--fond-doux); color: var(--encre); }
.rte-btn.is-active { background: var(--accent-doux); color: var(--accent-texte); }
.rte-toolbar-sep { width: 1px; height: 20px; background: var(--filet); margin:0 4px; }
.rte-emoji-wrap { position: relative; }
.rte-emoji-btn { font-size: 16px; }
.rte-emoji-panel {
  display: none; position: absolute; top: calc(100% + 6px); left: 0; z-index: 20;
  width: 260px; max-height: 220px; overflow-y: auto; padding:8px;
  background: var(--surface); border: 1px solid var(--filet-fort);
  border-radius: var(--rayon);
  grid-template-columns: repeat(8, 1fr); gap:2px;
}
.rte-emoji-panel.is-open { display: grid; }
.rte-emoji-item {
  display: flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; border-radius: 6px; font-size: 17px;
  border: 0; background: none; cursor: pointer;
}
.rte-emoji-item:hover { background: var(--fond-doux); }
.rte-editor {
  min-height: 120px; max-height: 360px; overflow-y: auto;
  padding:11px 14px; font-size: 15px; line-height: 1.55; outline: none;
}
.rte-editor:empty::before { content: attr(data-placeholder); color: var(--encre-tenue); }

/* Le surtitre à icône du gabarit vitrine : on garde le texte, on masque la
   pastille — c'est le motif que Jesse fait retirer partout. */
.kicker-icon, .kicker-dot { display: none; }
.kicker { font-size: 14px; color: var(--encre-tenue); margin-bottom:8px; display: block; }

/* -------------------- témoin de force du mot de passe --------------------
   REPRIS TEL QUEL de style.css : ses couleurs sont déjà des variables locales
   au bloc, précisément pour être réutilisable ailleurs. Seuls deux jetons
   hérités sont remappés sur ceux de la boutique.
   Il alimente trois pages — inscription, profil, réinitialisation — dont le
   script avait disparu avec le changement de gabarit : une page qui ne charge
   pas un script n'est pas une page cassée, elle fonctionne juste moins bien,
   et rien ne le signale. */
.pw-meter {
  --pw-reject: #E5533D;
  --pw-weak:   #E5533D;
  --pw-fair:   #D89A2B;
  --pw-good:   #4FA85E;
  --pw-strong: #2F8F52;
  margin-top:8px;
}
.pw-meter[hidden] { display: none; }
.pw-meter-track {
  height: 4px;
  border-radius: 999px;
  background: var(--filet);
  overflow: hidden;
}
.pw-meter-fill {
  display: block;
  height: 100%;
  width: 0;
  border-radius: 999px;
  background: var(--pw-weak);
  transition: width .18s ease, background-color .18s ease;
}
.pw-meter-verdict {
  margin:6px 0 0;
  font-size: 12.5px;
  line-height: 1.45;
  color: var(--encre-douce);
  min-height: 1.45em;
}
.pw-meter-rules {
  list-style: none;
  margin:6px 0 0;
  padding:0;
  display: flex;
  flex-wrap: wrap;
  gap:4px 14px;
  font-size: 12px;
  color: var(--encre-tenue);
}
.pw-meter-rules li {
  display: flex;
  align-items: center;
  gap:5px;
}
/* La pastille sert de repère visuel ; le texte de la règle reste lisible seul,
   pour ne pas faire porter l'information par la seule couleur. */
.pw-meter-tick {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
  opacity: .35;
  flex: 0 0 auto;
  transition: opacity .18s ease, background-color .18s ease;
}
.pw-meter-rules li[data-pw-ok] {
  color: var(--pw-good);
}
.pw-meter-rules li[data-pw-ok] .pw-meter-tick { opacity: 1; }

.pw-meter[data-pw-level="reject"] .pw-meter-fill { background: var(--pw-reject); }
.pw-meter[data-pw-level="0"] .pw-meter-fill,
.pw-meter[data-pw-level="1"] .pw-meter-fill { background: var(--pw-weak); }
.pw-meter[data-pw-level="2"] .pw-meter-fill { background: var(--pw-fair); }
.pw-meter[data-pw-level="3"] .pw-meter-fill { background: var(--pw-good); }
.pw-meter[data-pw-level="4"] .pw-meter-fill { background: var(--pw-strong); }

/* Le verdict prend la couleur du niveau seulement quand il alerte : en vert il
   ferait doublon avec la barre et attirerait l'œil sans rien apprendre. */
.pw-meter[data-pw-level="reject"] .pw-meter-verdict { color: var(--pw-reject); }
.pw-meter[data-pw-level="0"] .pw-meter-verdict,
.pw-meter[data-pw-level="1"] .pw-meter-verdict { color: var(--pw-weak); }
.pw-meter[data-pw-level="2"] .pw-meter-verdict { color: var(--pw-fair); }

/* ---------------------- pages d'authentification (espace client) ---------
   Ces pages portent leur propre mise en page — une carte centrée avec son
   logo — qui dépendait d'un conteneur du gabarit vitrine. Sans lui, l'en-tête
   collait au bord de l'écran. On lui redonne son cadre plutôt que de réécrire
   les pages : c'est la même logique que le reste de la couche. */
.auth-section {
  max-width: var(--largeur); margin:0 auto; padding:40px 24px 64px;
  display: flex; justify-content: center; position: relative;
}
/* Le décor de fond des pages d'authentification est masqué : il portait la
   trame du site vitrine, et une base sobre n'en a pas besoin. */
.auth-decor { display: none; }
.auth-card {
  width: 100%; max-width: 440px; position: relative;
  border: 1px solid var(--filet); border-radius: var(--rayon-large);
  background: var(--surface); padding:24px;
}
.auth-card-wide { max-width: 620px; }
.auth-card-logo {
  display: inline-flex; align-items: center; gap:8px;
  text-decoration: none; font-weight: 650; font-size: 18px; margin-bottom:20px;
}
.auth-card-logo .accent { color: var(--accent-texte); }
.auth-card-head { margin-bottom:20px; }
.auth-card-head h1 { font-size: 27px; margin:4px 0 8px; }
.auth-card-lead { color: var(--encre-douce); margin:0; font-size: 15px; }
.auth-card-foot { margin-top:20px; padding-top:18px; border-top: 1px solid var(--filet); font-size: 14px; color: var(--encre-douce); }
.auth-card-foot a { color: var(--accent-texte); }

/* ------------------- bandeaux d'information et newsletter ----------------- */
/* -------------------------------------------------------- notifications --
   Cloche et panneau, repris du site d'origine. Le bandeau pleine largeur a été
   essayé d'abord : deux annonces empilées mangeaient 130 px avant le premier
   produit, et placées entre la navigation et le contenu, elles se lisaient
   comme du contenu. La cloche coûte un clic, et rend la page au catalogue.
   Le point rouge est ce qui reste visible sans rien ouvrir : c'est lui qui
   porte l'information « il y a quelque chose », le reste est à la demande. */
.notif-wrap { position: relative; }
.notif-dot {
  box-sizing: content-box;
  position: absolute; top: -6px; right: -6px;
  width: 15px; height: 15px; border-radius: 50%;
  background: var(--accent); color: var(--accent-encre);
  border: 2px solid var(--fond);
  font-size: 9px; font-weight: 700; line-height: 15px; text-align: center;
}
.notif-menu {
  position: absolute; top: calc(100% + 10px); right: 0; z-index: 40;
  min-width: 310px; max-width: 360px; max-height: 70vh; overflow-y: auto;
  background: var(--surface); border: 1px solid var(--filet);
  border-radius: var(--rayon); box-shadow: 0 12px 40px rgba(16, 16, 24, .16);
  padding:8px;
  /* Masqué par l'opacité et non par display:none : la transition n'aurait rien
     à animer, et le panneau apparaîtrait d'un bloc. */
  opacity: 0; pointer-events: none; transform: translateY(4px);
  transition: opacity 160ms ease, transform 160ms ease;
}
.notif-menu.est-ouvert { opacity: 1; pointer-events: auto; transform: translateY(0); }
.notif-item { padding:12px; border-radius: 8px; text-align: left; }
.notif-item + .notif-item { border-top: 1px solid var(--filet); margin-top:4px; }
.notif-item-head { display: flex; align-items: center; gap:8px; margin-bottom:6px; }
.notif-item-icon { font-size: 16px; flex-shrink: 0; line-height: 1; }
.notif-item-title { flex: 1; font-size: 14px; font-weight: 600; }
.notif-item-dismiss {
  flex-shrink: 0; width: 20px; height: 20px;
  display: flex; align-items: center; justify-content: center;
  border: 0; background: transparent; border-radius: 50%;
  color: var(--encre-tenue); font-size: 16px; line-height: 1; cursor: pointer;
}
.notif-item-dismiss:hover { background: var(--fond-doux); color: var(--encre); }
.notif-item-message { font-size: 13.5px; color: var(--encre-douce); line-height: 1.55; margin:0 0 10px; }
.notif-empty { font-size: 13.5px; color: var(--encre-tenue); text-align: center; padding:20px 4px; margin:0; }
.notif-item-form { display: flex; gap:8px; flex-wrap: wrap; }
.notif-item-input {
  flex: 1; min-width: 0;
  font: inherit; font-size: 13.5px; padding:7px 11px;
  border: 1px solid var(--filet-fort); border-radius: var(--rayon);
  background: var(--fond); color: var(--encre);
}
.notif-item-form .bouton, .notif-item-lien { padding:7px 14px; font-size: 13.5px; }
/* Le retour d'envoi occupe la ligne entière sous les champs, sinon il se glisse
   entre eux et pousse le bouton hors du panneau. */
.notif-item-form .form-alert { flex-basis: 100%; font-size: 12.5px; }

@media (max-width: 720px) {
  .notif-menu { max-width: calc(100vw - 32px); }
}

.pied-newsletter { display: flex; flex-direction: column; gap:7px; }
.pied-newsletter label {
  font-size: 12px; font-weight: 650; letter-spacing: .05em; text-transform: uppercase;
  color: var(--encre);
}
.pied-newsletter-promesse { margin:0; font-size: 13.5px; color: var(--encre-tenue); }
.pied-newsletter-champs { max-width: 420px; }
.pied-newsletter-champs { display: flex; gap:8px; flex-wrap: wrap; }
.pied-newsletter input {
  font: inherit; font-size: 14px; padding:8px 12px; min-width: 220px;
  border: 1px solid var(--filet-fort); border-radius: var(--rayon);
  background: var(--surface); color: var(--encre);
}
.pied-newsletter .bouton { padding:8px 16px; font-size: 14px; }

/* La bascule de thème ne montre qu'une icône : celle de l'état vers lequel on
   va, pas celui où l'on est — c'est la convention que tout le monde attend. */
#bascule-theme .icone-sombre { display: none; }
:root[data-theme="dark"] #bascule-theme .icone-clair { display: none; }
:root[data-theme="dark"] #bascule-theme .icone-sombre { display: inline; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) #bascule-theme .icone-clair { display: none; }
  :root:not([data-theme="light"]) #bascule-theme .icone-sombre { display: inline; }
}

/* ------------------------------------------------------- menu à niveaux --
   Le sous-menu est un <details> : le navigateur gère l'ouverture, la fermeture
   au clic extérieur et le clavier, et surtout il fonctionne au doigt — un
   sous-menu qui ne s'ouvre qu'au survol n'existe pas sur la moitié des visites.
   Le panneau est en absolu SOUS le résumé, et le <details> lui-même reste dans
   le flux du menu : sans cela, l'ouverture pousserait les entrées suivantes. */
/* STATIC, ET C'EST VOULU. `relative` servait au petit panneau accroché sous son
   entrée ; le panneau prend maintenant la largeur de la page et doit s'ancrer
   sur `.bandeau`, qui est `sticky` donc positionné. Laisser `relative` ici le
   rattacherait à une boîte de quelques dizaines de pixels. */
.menu-groupe { position: static; }
.menu-groupe > summary {
  list-style: none; cursor: pointer;
  color: var(--encre-douce); font-size: 15px;
  padding:4px 0; border-bottom: 2px solid transparent;
  display: inline-flex; align-items: center; gap:6px;
}
.menu-groupe > summary::-webkit-details-marker { display: none; }
/* Le chevron dit qu'il y a quelque chose dessous : sans lui, l'entrée ressemble
   à un lien ordinaire qui ne mène nulle part au clic. */
.menu-groupe > summary::after {
  content: ''; width: 6px; height: 6px; margin-left:2px;
  border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor;
  /* LE CHEVRON FLOTTAIT TROP HAUT, et la cause tenait à l'ordre des fonctions :
     `rotate(45deg) translate(-2px,-2px)` applique le déplacement DANS LE REPÈRE
     DÉJÀ TOURNÉ. Un vecteur (−2, −2) dans un repère à 45° vaut (0, −2,83) à
     l'écran : le chevron remontait de près de trois pixels, et se retrouvait
     aligné sur le haut des capitales au lieu du centre du mot.

     En plaçant la translation AVANT la rotation, elle s'exprime en pixels
     d'écran et devient lisible. Il ne reste qu'une correction optique d'un
     pixel : la pointe du chevron occupe le bas de sa boîte, son centre visuel
     n'est donc pas son centre géométrique. */
  transform: translateY(-1px) rotate(45deg);
}
.menu-groupe[open] > summary { color: var(--encre); }
/* (le survol du résumé est traité avec celui des liens, plus haut) */
.menu-groupe > summary[aria-current="page"] {
  color: var(--accent-texte); font-weight: 600;
}
/* --- LE PANNEAU DÉROULANT ---
   Reprend la présentation de la recherche : le fond de la page passe au flou,
   seul le panneau reste net. Une colonne de 190 px accrochée sous son entrée
   affichait cinq rubriques en file indienne avec tout l'écran vide à côté ;
   le panneau prend maintenant la largeur de la page, comme le champ de
   recherche ouvert prend celle du bandeau.

   `fixed` et non `absolute` : le parent <details> fait 80 px de large, un
   panneau `absolute` s'y accrocherait au lieu de traverser l'écran. Rien à
   craindre du côté du bloc conteneur ici — `.menu-groupe` est simplement
   `relative`, et seuls `transform`, `filter` et `backdrop-filter` piègent un
   descendant `fixed`. C'est ce piège-là qui a coûté deux essais à la
   recherche, avec le `transform` de la barre d'outils. */
.menu-sous {
  /* LA LARGEUR DU CONTENU, PAS CELLE DE L'ÉCRAN. Une bande d'un bord à l'autre
     ne se rattache visuellement à rien : elle se lit comme une seconde barre de
     site plutôt que comme le déroulé d'une entrée. Alignée sur la largeur du
     contenu, la carte reste au-dessus de ce qu'elle prolonge.
     ⚠️ UNE SEULE DÉCLARATION `transform` par règle : le recentrage et l'amorce
     d'animation dans deux déclarations, la seconde écrase la première en
     silence et la carte part du milieu de l'écran pour déborder à droite. */
  position: absolute; top: calc(100% + 6px); left: 50%;
  transform: translateX(-50%);
  width: min(var(--largeur), calc(100vw - 48px));
  z-index: 61;
  max-height: 74vh; overflow-y: auto;
  background: var(--surface);
  border: 1px solid var(--filet); border-radius: var(--rayon-large);
  box-shadow: 0 24px 60px -20px rgba(16, 16, 24, .28);
  /* MARGES INTERNES PLUS GÉNÉREUSES. À 18 px, les libellés touchaient presque
     le bord d'une carte qui peut faire mille cent quatre-vingts pixels de large
     et porter cinquante entrées : le panneau paraissait rempli à ras bord. Sur
     un panneau de cette taille, c'est le blanc du pourtour qui dit qu'on
     regarde une carte posée sur la page et non un second bandeau. */
  padding:28px 28px 24px;
}
/* ═══ UNE PRÉSENTATION EN COLONNES, PAS EN BANDES (18/08/2026) ═══════════════
   La grille rangeait les liens de gauche à droite, ligne après ligne, et chaque
   intitulé de section réclamait sa ligne entière. Une section de trois entrées
   occupait donc trois cases d'une rangée de cinq et laissait les deux dernières
   vides ; huit sections empilaient huit bandes trouées, et la carte dépassait
   les deux mille pixels pour quarante liens — c'est-à-dire qu'elle ne se lisait
   qu'en la faisant défiler.

   Le gabarit groupe désormais chaque intitulé avec ses liens (voir
   shop-header.php). Ici, ces groupes sont COULÉS EN COLONNES : le navigateur
   remplit la première de haut en bas, passe à la suivante, et équilibre les
   hauteurs. C'est la disposition d'un mégamenu — on lit une section d'un trait,
   du titre à sa dernière entrée — et elle absorbe sans réglage des sections de
   trois comme de onze entrées, là où une grille de colonnes égales aurait
   creusé un blanc sous chaque section courte.

   `columns: 230px 4` donne une LARGEUR IDÉALE et un PLAFOND, pas un nombre fixe :
   sur une carte étroite le navigateur en met moins, jamais des colonnes de
   quatre-vingts pixels où les libellés se casseraient en trois lignes. */
.menu-sous-lignes {
  columns: 230px 4;
  column-gap: 30px;
}
/* Le groupe ne se coupe JAMAIS entre deux colonnes : un intitulé resté seul en
   bas d'une colonne, ses liens repris en haut de la suivante, et le titre
   annonce une section qu'il ne coiffe plus.
   `flow-root` est indispensable : sans lui, la marge basse du groupe fusionne
   avec celle de ses liens et l'écart entre sections se perd. */
.menu-sous-groupe {
  break-inside: avoid; -webkit-column-break-inside: avoid;
  display: flow-root;
  margin:0 0 20px;
}
.menu-sous-groupe:last-child { margin-bottom:0; }
/* ⚠️ LE SÉLECTEUR PORTE `.menu` EN PLUS DE SA CLASSE, ET C'EST OBLIGATOIRE.
   `.menu a` (une classe + un type) l'emporte sur `.menu-sous-item` (une classe
   seule), quel que soit l'ordre dans le fichier : c'est la barre de rubriques
   qui imposait ici son `display: inline-flex` et son `padding: 4px 0`.
   Tant que les liens étaient des éléments de GRILLE, cela ne se voyait pas —
   une grille « blockifie » ses enfants, `inline-flex` y devient `flex`. Le jour
   où les groupes ont coulé en colonnes, les liens sont revenus dans le flux
   normal : `inline-flex` a repris ses droits et les libellés se sont mis bout à
   bout sur la même ligne (« Bagues or Bracelets or »).
   Une mise en page ne doit pas dépendre d'un effet de bord du mode de
   disposition parent : la règle gagne donc franchement. */
.menu .menu-sous-item {
  display: flex; align-items: center; gap:10px;
  /* Aligné sur le rembourrage horizontal de l'intitulé (12 px) : sans cela le
     titre de section et les liens qu'il coiffe ne commencent pas au même
     endroit, ce qui se voit d'autant plus que le titre a maintenant un fond. */
  padding:8px 12px; border-radius: 8px; border-bottom: 0;
  white-space: normal; text-align: left;
}
/* L'INTITULÉ DE COLONNE d'un menu repris d'un mégamenu. Il occupe la LARGEUR
   ENTIÈRE de la grille : posé dans une seule cellule, il se retrouverait à
   côté des liens qu'il est censé coiffer, et l'on ne saurait plus lequel
   annonce lesquels. Sur toute la ligne, il ouvre franchement une section et
   les liens qui suivent lui appartiennent jusqu'au suivant.

   Le premier n'a pas de marge haute : elle creuserait un blanc entre le bord
   du panneau et le premier mot. */
.menu-sous-entete {
  /* UN FOND, ET UN FOND DE LA CHARTE. L'intitulé n'était qu'un texte gris en
     petites capitales : dans une carte de cinquante liens répartis sur huit
     sections, rien ne distinguait franchement un titre de section d'un lien un
     peu plus pâle, et l'œil devait relire pour retrouver où commence quoi.

     La teinte est dérivée de l'accent et non écrite en dur : sur cette boutique
     elle donne un or très dilué, sur la suivante elle suivra sa couleur. À 10 %
     l'aplat se voit sans jamais concurrencer les liens — c'est un repère, pas
     un bouton. */
  /* L'APLAT ÉPOUSE LE TEXTE, il ne traverse pas la colonne. Un bloc s'étire par
     défaut sur toute la largeur disponible : le fond devenait une barre pleine
     largeur pour trois mots, plus proche d'un séparateur de tableau que d'une
     étiquette. `fit-content` réduit la BOÎTE à son contenu sans la sortir du
     flux — l'intitulé reste le premier bloc de son groupe, les liens dessous.
     (`justify-self: start` faisait ce travail du temps de la grille ; il n'a
     plus d'effet depuis que les groupes coulent en colonnes.) */
  width: fit-content; max-width: 100%;
  margin:0 0 8px; padding:5px 11px;
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  border-radius: 999px;
  font-size: 11.5px; font-weight: 700; letter-spacing: .08em;
  text-transform: uppercase; color: var(--accent-texte);
}
/* LE SURVOL NE REMPLIT PLUS LE LIEN. Un pavé de couleur derrière chaque entrée
   fait sauter un rectangle d'un bout à l'autre de la carte au moindre mouvement
   de souris — sur cinquante entrées, la carte clignote. L'entrée survolée passe
   simplement à l'encre pleine et se souligne : c'est ce que fait un lien.

   LE FOCUS CLAVIER, LUI, GARDE UN CONTOUR. L'ancienne règle mettait `outline:
   none` en s'appuyant sur le fond coloré pour marquer le focus ; en retirant ce
   fond sans rien mettre à la place, on aurait rendu la carte impossible à
   parcourir au clavier. */
/* AU SURVOL : LE GRAS ET LA COULEUR DE LA CHARTE, PAS DE SOULIGNEMENT
   (18/08/2026, demande de Jesse). Le soulignement disait « c'est un lien » —
   information que personne n'attend dans un menu, où tout est lien. Le gras et
   l'encre d'accent disent « c'est CELUI-CI », ce qui est la seule chose utile
   quand on parcourt cinquante entrées.
   `--accent-texte` et non `--accent` : c'est la déclinaison de l'accent
   calculée pour rester lisible sur un fond clair. Sur une charte or, l'accent
   brut tombe sous le contraste minimum dès qu'il sert à du texte. */
.menu .menu-sous-item:hover,
.menu .menu-sous-item:focus-visible,
.menu .menu-sous-item[aria-current="page"] {
  color: var(--accent-texte);
  text-decoration: none;
}
/* PAS DE GRAS ICI NON PLUS, pas même pour la page courante. Dans une colonne de
   cinquante entrées, une seule en gras attire l'œil plus que le lien qu'on est
   en train de viser ; et sur la page qu'elle désigne, on sait déjà où l'on est.
   La couleur suffit, et elle ne change pas la largeur du libellé. */
.menu .menu-sous-item:focus-visible {
  outline: 2px solid var(--accent); outline-offset: -2px; border-radius: 8px;
}
/* La pastille tient la colonne : une rangée sur deux sans elle décalerait tous
   les libellés. À défaut d'emoji, l'initiale — un carré vide se lit comme une
   image qui n'a pas chargé. */
.menu-sous-puce {
  flex: none; width: 28px; height: 28px; border-radius: 7px;
  background: var(--fond-doux);
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 15px; line-height: 1;
}
.menu-sous-puce.est-monogramme {
  font-size: 12px; font-weight: 650; color: var(--encre-tenue);
}
/* `min-width: 0` et rien d'autre : l'entrée vit dans une colonne étroite et doit
   pouvoir se replier sur deux lignes sans forcer la largeur de sa grille. */
.menu-sous-nom { min-width: 0; }

/* Le pied est COLLANT : sur un panneau qui défile, un pied posé tout en bas ne
   se voit qu'après avoir tout parcouru, c'est-à-dire jamais. */
.menu-sous-pied {
  position: sticky; bottom: 0;
  display: flex; align-items: center; justify-content: space-between; gap:16px;
  margin-top:14px; padding:11px 2px 0;
  border-top: 1px solid var(--filet); background: var(--surface);
}
/* Même piège de spécificité que `.menu-sous-item`, et il se voyait à l'écran :
   le lien du pied s'affichait à l'encre atténuée de la barre de rubriques au
   lieu de la couleur d'accent, avec le rembourrage et le filet de celle-ci. */
.menu .menu-sous-tout {
  font-size: 13.5px; font-weight: 550; color: var(--accent-texte);
  border-bottom: 0; padding:0;
}
.menu .menu-sous-tout:hover { text-decoration: underline; color: var(--accent-texte); }

/* --- Le fond flouté pendant qu'un sous-menu est ouvert ---
   Le voile recouvre ce qui vit HORS du bandeau ; le bandeau passe DEVANT lui,
   et ses enfants sont floutés séparément — le sous-menu ouvert excepté. Un
   flou posé sur le bandeau entier emporterait le panneau : un filtre s'hérite
   par le rendu et non par la cascade, aucune règle sur l'enfant ne le défait.

   Le fond du bandeau devient transparent, sans quoi la bande opaque sur
   laquelle reposent ses enfants resterait parfaitement nette au-dessus du
   voile — un rectangle net en travers de l'écran, plus visible que ce qu'on
   cherchait à masquer. */
.voile-menu {
  position: fixed; inset: 0; z-index: 50;
  /* AUCUN VOILE SOMBRE : le flou seul. Une teinte par-dessus assombrit la page
     et fait basculer tout le fond dans un gris uniforme, ce qui écrase les
     repères au lieu de les reculer. Déclaré à transparent plutôt qu'omis : la
     déclaration dit que l'absence de teinte est un choix, pas un oubli.
     (La recherche, elle, garde la sienne — son panneau couvre moins d'écran et
     s'ouvre sur une intention différente.) */
  background: transparent;
  /* ⚠️ RÉDUIT (20/08/2026, « ça bug au survol du header, tout est flou ») :
     11px sur toute la page derrière une carte de menu qui n'en couvre
     qu'une partie donnait l'impression d'un rendu cassé plutôt que d'un
     arrière-plan reculé exprès — le bandeau de promo, les bannières, tout
     devenait illisible d'un coup. Le principe (reculer ce qui n'est pas le
     menu) reste ; l'intensité, elle, était largement excessive. */
  opacity: 0; pointer-events: none;
  transition: opacity 180ms ease;
}
/* Le flou n'est posé QUE quand le voile est là (26/08/2026) : à opacité 0 le
   calque existait quand même et se recalculait à chaque image derrière lui. */
body.menu-ouvert .voile-menu { -webkit-backdrop-filter: blur(5px); backdrop-filter: blur(5px); }
/* PLUS AUCUN `body:has()` DANS CETTE FEUILLE (26/08/2026, audit de performance).
   `body:has(.menu-groupe[open])` se lisait bien, mais un `:has()` ancré sur
   <body> est réévalué à chaque mutation du DOM de toute la page — 0,8 à 1,1 s
   de « Style & Layout » mesurés sur chaque page, sliders et pastilles compris.
   Le script pose `menu-ouvert` / `menu-tiroir-ouvert` / `comparateur-visible`
   sur <body> (shop.js, refleter) ; les `:has()` qui restent sont ancrés sur un
   conteneur local (fiche, échéancier, moyen de paiement, carte) et ne coûtent
   que ce qu'ils regardent. Les POIDS ont changé avec la réécriture :
   `body.menu-ouvert .bandeau` pèse (0,2,1) et non plus (0,3,1) — les règles du
   tiroir plus bas, écrites après et avec une classe de plus, gardent la main. */
body.menu-ouvert .voile-menu { opacity: 1; pointer-events: auto; }
body.menu-ouvert .bandeau {
  z-index: 60; background: transparent; border-bottom-color: transparent;
}
/* ⚠️ PAS DE `> *:not(…)` DERRIÈRE UNE CLASSE D'ÉTAT DU <body> (25/09/2026,
   chantier INP). Quand la partie droite d'un sélecteur ne porte ni classe ni
   balise, Chrome ne sait pas QUI invalider et recalcule le style de tout le
   <body> : 1 830 éléments à chaque ouverture de menu, de tiroir ou de
   recherche, au lieu de 20 à 450. Les cibles sont donc NOMMÉES — et un nouvel
   enfant de `.bandeau`, `.bandeau-haut`, `.bandeau-outils` ou `.menu` doit
   être ajouté ici s'il doit se flouter avec les autres. */
body.menu-ouvert .bandeau > .bandeau-haut,
body.menu-ouvert .menu > :is(.menu-entete, .menu-groupe:not([open]), a, .menu-secondaire) {
  filter: blur(5px); pointer-events: none;
  transition: filter 180ms ease;
}
/* LE VOILE ET LE FLOU NE S'APPLIQUENT JAMAIS DANS LE TIROIR. Ce mécanisme sert
   le mégamenu horizontal : une carte flottante au-dessus d'une page nette, que
   le voile recule pour qu'on distingue la carte du reste. Dans le tiroir, le
   panneau EST déjà toute la navigation, posé sur son propre fond flouté
   (`.tiroir-fond`) ; lui superposer ce second voile flouterait deux fois, et la
   règle `.menu > *:not(.menu-groupe[open])` prendrait pour cible les AUTRES
   rubriques du même tiroir — la liste se floutait sous les yeux dès qu'on
   dépliait l'une d'elles.
   Non gaté par une largeur d'écran, contrairement à avant : c'est `est-tiroir`
   qui sait, dans les deux cas où le menu se replie. */
body.menu-tiroir-ouvert .voile-menu { opacity: 0; pointer-events: none; }
body.menu-tiroir-ouvert .bandeau > .bandeau-haut,
body.menu-tiroir-ouvert .menu > :is(.menu-entete, .menu-groupe:not([open]), a, .menu-secondaire) {
  filter: none; pointer-events: auto;
}
/* Les bandeaux défilants s'arrêtent : un `backdrop-filter` recalcule son flou à
   chaque image de ce qui bouge derrière lui, et le voile scintillait — c'est le
   même constat que pour la recherche, fait à l'écran avant d'être compris. */
body.menu-ouvert .bandeau-annonces.defile .bandeau-annonces-piste,
body.menu-ouvert .marques-bandeau.defile .marques-piste {
  animation-play-state: paused;
}
@media (prefers-reduced-motion: reduce) {
  .voile-menu { transition: none; }
  body.menu-ouvert .bandeau > .bandeau-haut,
  body.menu-ouvert .menu > :is(.menu-entete, .menu-groupe:not([open]), a, .menu-secondaire) { transition: none; }
}

/* Dans le tiroir, l'apparence du sous-panneau est déjà écrite une fois sous
   `.menu.est-tiroir .menu-sous` (voir ce bloc). Ne reste ici qu'un mécanisme
   sans rapport avec la largeur d'écran : voir la note juste au-dessus. */
.menu-icone { font-size: 15px; line-height: 1; }

/* --------------------------------------------------- bandeau d'annonces --
   Une bande fine tout en haut, dont le contenu défile de droite à gauche.

   LE DÉFILEMENT EST EN CSS, pas en JavaScript : une animation de transformation
   est composée par le navigateur sans repeindre, là où un déplacement piloté
   image par image fait travailler le processeur pendant toute la visite.

   LA PISTE EST DUPLIQUÉE dans le gabarit et translatée de -50 % : au terme de
   l'animation, la copie occupe exactement la place du premier groupe, et la
   boucle est invisible. Sans elle, on verrait un vide traverser la bande à
   chaque tour. */
.bandeau-annonces {
  background: var(--encre);
  color: var(--fond);
  font-size: 13.5px;
  overflow: hidden;
}
.bandeau-annonces-piste { display: flex; width: max-content; }
.bandeau-annonces-groupe { display: flex; align-items: center; }
.bandeau-annonce {
  display: inline-flex; align-items: center; gap:8px;
  padding:8px 0; margin:0 24px; white-space: nowrap;
  color: inherit; text-decoration: none;
}
/* AUCUN SÉPARATEUR — décision de Jesse (8 août 2026). Le point « · » puis le
   filet vertical ont été essayés et écartés : ils ajoutaient un objet graphique
   dans une bande qui doit se lire d'un trait. Ce qui sépare deux annonces est
   l'espace : 26 px de marge de chaque côté, soit 52 px entre deux messages —
   assez pour qu'ils ne se lisent pas comme une seule phrase. */
a.bandeau-annonce:hover { text-decoration: underline; }
.bandeau-annonce strong { font-weight: 650; }
.bandeau-annonce a { color: inherit; text-decoration: underline; }

/* Une seule annonce : rien ne défile, elle se pose au centre. Faire tourner un
   message unique n'apporte aucune information et fatigue l'œil. */
.bandeau-annonces:not(.defile) .bandeau-annonces-piste { width: 100%; justify-content: center; }

.bandeau-annonces.defile .bandeau-annonces-piste {
  /* La durée est posée par shop.js d'après la LONGUEUR réelle du contenu : à
     durée fixe, deux fois plus de texte défile deux fois plus vite, donc
     illisible. */
  animation: bandeau-defile var(--bandeau-duree, 40s) linear infinite;
}
/* Au survol, on s'arrête : c'est le seul moyen de lire tranquillement une
   annonce longue, et de viser un lien sans qu'il se dérobe. */
.bandeau-annonces.defile:hover .bandeau-annonces-piste { animation-play-state: paused; }

/* On avance d'exactement UNE largeur de groupe, en pixels, et non de -50 % :
   le pourcentage supposait deux groupes et un seul, ce qui ne tient plus dès
   que le script en ajoute pour couvrir un écran large. La distance est posée
   par shop.js ; le repli de 50 % garde un défilement correct si le script ne
   s'exécute pas. */
@keyframes bandeau-defile {
  from { transform: translateX(0); }
  to   { transform: translateX(calc(-1 * var(--bandeau-distance, 50%))); }
}

/* Un visiteur qui a demandé moins d'animations ne doit pas subir la seule qui
   tourne en permanence sur la page. La bande devient alors une liste que l'on
   fait glisser au doigt, et la copie est masquée pour ne pas lire deux fois. */
@media (prefers-reduced-motion: reduce) {
  .bandeau-annonces.defile .bandeau-annonces-piste {
    animation: none; width: 100%; overflow-x: auto; scrollbar-width: none;
  }
  .bandeau-annonces.defile .bandeau-annonces-groupe + .bandeau-annonces-groupe { display: none; }
}

/* ------------------------------------------------------------ carrousel --
   Une piste défilante native : `overflow-x: auto` + `scroll-snap`. Le glisser
   au doigt, l'inertie, la molette horizontale et le clavier viennent du
   navigateur — les réimplémenter en JavaScript revient à les réimplémenter
   moins bien. Le script n'ajoute que les flèches et l'état des points.

   AUCUN DÉFILEMENT AUTOMATIQUE : une vignette qui se dérobe au moment où on la
   vise est le défaut du marquee, et c'est précisément ce qu'on ne veut pas. */
.carrousel { padding:8px 0 40px; }
/* Sur la FICHE PRODUIT (16/08/2026, « manque d'espaces ») : le carrousel des
   associés suit directement la prose de la description — les 8 px du réglage
   d'accueil collaient « Dans la même rubrique » au paragraphe précédent. On
   lui donne le rythme d'une vraie section, et un peu d'air sous le titre.
   ⚠ FRÈRE de .fiche et non descendant : le partial est requis après la
   fermeture du bloc fiche, d'où le combinateur ~. */
.fiche ~ .carrousel { padding:44px 0 52px; }
.fiche ~ .carrousel .carrousel-entete { margin-bottom:22px; }
/* LES INTERTITRES DE LA FICHE SONT TOUS AU MÊME CORPS (18/08/2026, demande de
   Jesse : « les 3 titres devraient faire la même taille »).
   « Description », « Avis clients » et « Questions fréquentes » étaient à 20 px,
   posés section par section ; « Dans la même rubrique » retombait sur le `h2`
   général du site, à 22 px. Quatre sections de même rang dont une seule est
   plus grosse se lit comme une hiérarchie — on cherche pourquoi celle-là compte
   davantage, et il n'y a pas de raison.
   C'est le carrousel qu'on aligne, et non les trois autres qu'on grossit : sur
   l'accueil, où le carrousel EST le titre principal de sa bande, 22 px reste
   juste. Le sélecteur reste donc borné à la fiche. */
.fiche ~ .carrousel .carrousel-entete h2 { font-size: 20px; }
/* ─── TEXTE SEO ET ILLUSTRATION D'EN-TÊTE (20/08/2026) ────────────────────────
   Facultatifs, voir carrousel.php pour le pourquoi. L'illustration s'étire
   sur la hauteur du titre empilé sur sa description — même principe que la
   cocarde du bandeau d'avis (`.avis-bandeau-badge`), qui a validé cette
   mécanique : un `min-height` fixe plutôt qu'un simple `align-self: stretch`,
   pour qu'une description courte (voire absente, titre seul) n'écrase pas
   l'illustration à quelques pixels de haut. */
.carrousel-entete-groupe {
  display: flex; align-items: stretch; gap: 20px;
  margin-bottom: 16px;
}
.carrousel-entete-visuel {
  flex: none; align-self: stretch;
  width: 128px; min-height: 96px; border-radius: var(--rayon-large);
  object-fit: cover; background: var(--fond-doux);
}
/* L'ICÔNE DESSINÉE, QUAND AUCUNE IMAGE N'EST DÉPOSÉE (20/08/2026, voir
   Illustration::icone()). ⚠️ TROISIÈME PASSE, même jour, Jesse à l'écran :
   « les 3 icônes doivent avoir une taille similaire et un fond crème
   circulaire » — retour sur la passe précédente (sans fond du tout), qui
   avait elle-même corrigé une boîte carrée trop pâle. Pastille ronde, taille
   FIXE (pas `align-self: stretch` comme la photo voisine) : c'est justement
   ce qui garantit que les trois se valent, quelle que soit la hauteur du
   titre+texte à côté. */
/* ⚠️ RÉDUITE (20/08/2026, « cercles trop larges ») : 84px de pastille pour un
   trait de 40px laissait une couronne de vide disproportionnée autour de
   l'icône — la pastille cherchait à égaler la hauteur de la photo voisine
   (96px), pas à s'ajuster à ce qu'elle contient réellement. */
.carrousel-entete-visuel--icone {
  flex: none; align-self: center;
  display: flex; align-items: center; justify-content: center;
  width: 64px; height: 64px; min-height: 0; border-radius: 50%;
  background: var(--fond-doux);
  color: color-mix(in srgb, var(--accent) 75%, var(--encre));
}
.carrousel-entete-visuel--icone svg { width: 30px; height: 30px; flex: none; }
.carrousel-entete-contenu {
  flex: 1 1 auto; min-width: 0;
  display: flex; flex-direction: column; justify-content: center; gap: 8px;
}
.carrousel-entete-groupe .carrousel-entete { margin-bottom: 0; }
/* MESURE DE LECTURE BORNÉE, comme toute prose du site (voir `.rubrique-bas`) :
   un paragraphe SEO étiré sur toute la largeur de la bande se lit mal, et il
   n'a de toute façon pas vocation à occuper l'espace des vignettes. */
.carrousel-entete-description {
  max-width: 62ch; font-size: 14px; line-height: 1.55; color: var(--encre-douce);
}
.carrousel-entete-description p { margin: 0 0 8px; }
.carrousel-entete-description p:last-child { margin-bottom: 0; }
/* ⚠️ BORNÉ À 2 LIGNES (20/08/2026, « les descriptions doivent tenir sur 2
   lignes ») : posé sur chaque paragraphe plutôt que sur le conteneur — un
   texte en plusieurs `<p>` (RichText peut en rendre plusieurs) resterait
   bien clampé PAR paragraphe, pas cassé par un `-webkit-box` qui ne sait
   traiter qu'UN seul flux de texte à la fois. Vaut pour tout texte déposé
   au constructeur de page d'accueil, pas seulement ceux écrits aujourd'hui —
   la contrainte ne dépend donc pas de la longueur de ce qui est tapé. */
.carrousel-entete-description p {
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
  overflow: hidden;
}
@media (max-width: 640px) {
  /* Une illustration de 128 px prend le tiers d'un écran de téléphone à côté
     d'un titre déjà serré : elle passe au-dessus plutôt qu'à côté. */
  .carrousel-entete-groupe { flex-direction: column; gap: 12px; }
  .carrousel-entete-visuel { width: 100%; height: 140px; min-height: 0; }
  /* LA PASTILLE RESTE RONDE : la règle ci-dessus l'étirerait sur toute la
     largeur (100%) sinon, un cercle devenant une ellipse. */
  .carrousel-entete-visuel--icone { width: 64px; height: 64px; }
}
.carrousel-entete {
  display: flex; align-items: baseline; justify-content: space-between;
  gap:16px; margin-bottom:16px;
}
.carrousel-entete h2 { margin:0; }
.carrousel-outils { display: flex; align-items: center; gap:14px; }
.carrousel-lien { color: var(--accent-texte); text-decoration: none; font-size: 14px; white-space: nowrap; }
.carrousel-lien:hover { text-decoration: underline; }
/* Le lien porte le nom de la section depuis le 30/09/2026 (« Voir les sachets
   individuels ») : sur un téléphone, titre + lien + flèches ne tiennent plus
   toujours sur une ligne. Le lien passe alors SOUS le titre, au lieu de
   pousser la page hors de l'écran. */
@media (max-width: 560px) {
  .carrousel-entete { flex-wrap: wrap; row-gap: 8px; }
}
.carrousel-fleches { display: flex; gap:6px; }
.carrousel-fleches[hidden] { display: none; }
.carrousel-fleche {
  /* Cercle gris clair, SANS CONTOUR : le liseré doublait le cercle et rendait
     le bouton plus lourd que ce qu'il commande. */
  width: 34px; height: 34px; border-radius: 50%;
  border: 0; background: var(--fond-doux); color: var(--encre);
  cursor: pointer;
  /* Le chevron était décentré : posé comme du texte, il suivait la ligne de
     base de la police et retombait un ou deux pixels trop bas, décalé à droite
     par son propre chasse. Centré en flex avec line-height à 1, il ne dépend
     plus des métriques de la police. */
  display: inline-flex; align-items: center; justify-content: center;
  font: inherit; font-size: 19px; line-height: 1; padding:0 1px 2px 0;
}
.carrousel-fleche:hover:not(:disabled) { background: var(--filet); }
/* Une flèche en bout de course est DÉSACTIVÉE, pas masquée : la faire
   disparaître décale l'autre, et on clique alors à côté. */
.carrousel-fleche:disabled { opacity: .35; cursor: default; }

.carrousel-piste {
  display: flex; gap:18px;
  /* `proximity` et non `mandatory` : avec l'accrochage obligatoire, un
     défilement animé lancé par les flèches est ramené au point d'accrochage le
     plus proche dès que la mise en page bouge — les vignettes ne bougeaient
     pas d'un pixel. En proximité, le navigateur aligne quand on relâche près
     d'un point et laisse le reste tranquille.
     `scroll-padding-inline` fait démarrer l'accrochage APRÈS le rembourrage,
     sinon la première vignette se cale sous le bord gauche. */
  overflow-x: auto; scroll-snap-type: x proximity; scroll-padding-inline: 24px;
  /* Le glisser à la SOURIS est ajouté par shop.js ; le curseur l'annonce. */
  cursor: grab;
  /* PAS de `scroll-behavior: smooth` ici, et c'est mesuré, pas supposé : sur le
     navigateur de test, tout défilement ANIMÉ de ce conteneur est resté sans
     effet — `scrollBy({behavior:'smooth'})` comme une affectation de scrollLeft
     sous `scroll-behavior: smooth` laissaient la piste à 0 px, à 150 ms comme à
     1,2 s, là où la même affectation en `auto` déplaçait immédiatement de
     516 px. Un carrousel dont les flèches ne font rien est pire qu'un carrousel
     qui saute. Le défilement au doigt et à la molette, lui, garde son inertie
     native — elle ne dépend pas de cette propriété. */
  /* La marge négative + le rembourrage laissent les vignettes toucher le bord
     de l'écran en défilant, sans sortir de la colonne de contenu au repos.

     ⚠️ LES VALEURS VERTICALES SONT DICTÉES PAR L'OMBRE DES CARTES, pas par le
     rythme voulu — celui-ci est rendu par les marges négatives qui les
     compensent exactement (24 − 20 = 4 en haut, 46 − 34 = 12 en bas, soit les
     mêmes 4 et 12 qu'avant).

     Pourquoi il le faut : `overflow-x: auto` force l'axe vertical à `auto` lui
     aussi — dès qu'un axe n'est plus `visible`, l'autre cesse de l'être. La
     piste DÉCOUPE donc tout ce qui dépasse en haut et en bas, et ce qui dépasse
     ici, c'est l'ombre portée des cartes au survol : `0 14px 34px` descend à
     48 px sous la carte, remontée de 3 px par le soulèvement, soit 45 px ; vers
     le haut, 34 − 14 + 3 = 23 px. À 12 px de rembourrage bas, l'ombre était
     tranchée net au ras de la carte — un trait horizontal sous la rangée, que
     Jesse a vu avant moi (18/08/2026).

     La règle générale : **un conteneur défilant doit réserver, en rembourrage,
     l'emprise de l'ombre de ses enfants au survol**, et la reprendre en marge
     négative. Les autres rangées glissantes du site (`.avis-rangee`, la galerie
     de la fiche, la barre de rubriques sur mobile) ont été vérifiées : leurs
     enfants n'ont ni ombre ni soulèvement, elles n'ont rien à réserver. */
  margin:-20px -24px -34px; padding:24px 24px 46px;
  scrollbar-width: none;
}
.carrousel-piste::-webkit-scrollbar { display: none; }
.carrousel-piste:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }

/* LE FONDU QUI ANNONCE QU'ON PEUT FAIRE DÉFILER.
   Il est obtenu par un MASQUE et non par un dégradé posé par-dessus : un
   dégradé n'est opaque que sur une couleur connue, et il faudrait alors le
   redéclarer pour chaque fond de section — puis le refaire à chaque charte
   cliente, puis le corriger en thème sombre. Le masque, lui, rend les pixels
   transparents : il fonctionne sur n'importe quel fond sans rien savoir de lui.

   Les classes viennent de shop.js, qui suit déjà la position de défilement pour
   activer ses flèches — on ne rajoute donc aucun écouteur. Le fondu n'apparaît
   que du côté où il RESTE quelque chose : au bout de la course, un fondu qui
   subsisterait ferait croire qu'il y a encore des articles à voir. */
.carrousel-piste {
  --fondu-gauche: #000;
  --fondu-droite: #000;
  -webkit-mask-image: linear-gradient(90deg,
    var(--fondu-gauche) 0, #000 72px, #000 calc(100% - 72px), var(--fondu-droite) 100%);
  mask-image: linear-gradient(90deg,
    var(--fondu-gauche) 0, #000 72px, #000 calc(100% - 72px), var(--fondu-droite) 100%);
}
.carrousel.deborde-gauche .carrousel-piste { --fondu-gauche: transparent; }
.carrousel.deborde-droite .carrousel-piste { --fondu-droite: transparent; }
.carrousel-piste.est-tiree,
.fiche-vignettes.est-tiree { cursor: grabbing; scroll-snap-type: none; }
/* UNE PISTE EN DÉFILEMENT AUTOMATIQUE N'ACCROCHE PAS (24/08/2026) : le snap
   `proximity` peut recadrer la piste après une écriture programmatique de
   scrollLeft — l'avance d'un demi-pixel par image se ferait alors ramener en
   arrière à chaque pas, un tremblement plutôt qu'un défilement. */
.carrousel-piste.est-defilante,
.carrousel-piste.est-animee { scroll-snap-type: none; }
/* Pendant un glisser, les vignettes ne doivent ni se sélectionner ni partir en
   glisser d'image — les deux interrompent le mouvement. */
.carrousel-piste.est-tiree * { user-select: none; pointer-events: none; }

/* ═══ LA CARTE PRODUIT DANS UNE PISTE DÉFILANTE (18/08/2026) ════════════════
   Les listes de produits de l'accueil, les suggestions du panier et les
   produits associés rendent désormais LA carte de rubrique et non plus une
   vignette appauvrie (voir carrousel.php). Il n'y a donc rien à styler de la
   carte elle-même : seulement à lui dire de ne pas s'étirer dans un conteneur
   flex, et à lui donner sa largeur de piste.

   230 px, comme la colonne minimale de `.grille-produits` : une carte qui
   changerait de largeur selon la page ferait mentir la photo — le même bijou y
   paraîtrait plus grand sur l'accueil que dans son rayon.

   LE RESPIRE VERTICAL EST NÉCESSAIRE, pas cosmétique — mais il est réglé une
   fois pour toutes sur `.carrousel-piste` (voir la note d'ombre qui y est
   écrite), et non ici : les vignettes légères se soulèvent et s'ombrent elles
   aussi, elles avaient donc le même défaut. */
.carrousel-piste.est-cartes > .carte-enveloppe {
  flex: 0 0 auto; width: 230px;
  scroll-snap-align: start;
}
@media (max-width: 560px) {
  .carrousel-piste.est-cartes > .carte-enveloppe { width: 62vw; }
}

.carrousel-vignette {
  flex: 0 0 auto; width: 220px;
  scroll-snap-align: start;
  text-decoration: none; color: inherit;
  display: flex; flex-direction: column; gap:8px;
}
.carrousel-visuel {
  /* LE MÊME JETON QUE `.carte-visuel`, et c'est le point : deux rangées de
     vignettes qui se comporteraient différemment seraient un défaut, pas une
     nuance — c'est déjà ce que dit le commentaire du gabarit. Le carrousel
     était pourtant resté en 4/3 quand la grille est passée en portrait, si
     bien que la page d'accueil montrait les mêmes bijoux dans deux cadres
     différents selon la section. */
  display: block; aspect-ratio: var(--marque-ratio-visuel, 4 / 5); overflow: hidden;
  border-radius: var(--rayon-large); background: var(--fond-doux);
  border: 1px solid var(--filet);
  position: relative;
}
.carrousel-visuel img {
  width: 100%; height: 100%; object-fit: cover; display: block;
  transition: transform .3s ease;
}
.carrousel-vignette:hover .carrousel-visuel img { transform: scale(1.04); }
/* ⚠️ QUATRIÈME PASSE (24/08/2026, « remets les images en fond crème, les
   miniatures doivent se fondre dans le fond ») : les trois passes ci-dessous
   avaient accordé ce fond au badge d'avis (un or dosé sur `--accent`) — Jesse
   revient dessus : la miniature doit redevenir INVISIBLE en tant que cadre,
   `var(--fond)` étant exactement la couleur du `body` (voir shop.css:268).
   Historique des passes précédentes, gardé pour comprendre pourquoi
   `object-fit: contain` + rembourrage restent nécessaires même sans teinte :
   - (20/08, « même couleur que badges avis ») : même recette `color-mix`
     que `.bandeau-badge-avis`, pour que les deux teintes suivent le même
     calcul sur `--accent`.
   - (20/08, deuxième passe, « tu n'as pas changé le fond de ces
     miniatures ») : le fond ÉTAIT bien posé, mais invisible — la photo
     produit occupe toute la boîte en `object-fit: cover`, elle ne laisse
     RIEN dépasser du fond derrière elle. Il fallait rétrécir l'image
     (`contain` + rembourrage) pour que la teinte se voie tout autour.
   - (20/08, troisième passe, « le fond des miniatures aussi ») : le dosage
     relevé à 40% pour se lire comme une couleur choisie. */
/* ⚠️ CINQUIÈME PASSE (24/08/2026, « passe les fonds en blanc ») : après l'or
   (20/08) puis le crème de la page (24/08 matin), Jesse tranche pour le BLANC
   PUR — celui du fond studio cuit dans les photos elles-mêmes : la photo et
   sa boîte ne font plus qu'un, seul le filet dessine la carte. En dur et non
   `var(--surface)` : c'est la couleur des pixels des photos qu'on égalise,
   pas un jeton de thème — elle ne doit pas bouger en thème sombre non plus,
   sinon la photo redevient un rectangle blanc dans une boîte sombre. */
.section-epoques .carrousel-visuel { background: #fff; }
/* ⚠️ LE FOND BLANC DE LA PHOTO ELLE-MÊME, PAS SEULEMENT LA CARTE (20/08/2026,
   « des photos j'entends ? ») : `.carrousel-visuel` a bien changé de couleur,
   mais chaque photo de catalogue est prise sur un fond studio BLANC — ce
   blanc-là est cuit dans les pixels, aucune règle CSS ne repeint le contenu
   d'une image. `mix-blend-mode: multiply` est le contournement qui marche
   sans repasser chaque photo en détourage : sous ce mode, un pixel blanc pur
   devient exactement la couleur posée dessous (blanc × couleur = couleur),
   tandis qu'un pixel plus sombre — le bijou — reste visible, assombri en
   proportion de sa propre teinte. Le fond studio se fond dans la carte ; le
   bijou, lui, résiste au traitement précisément parce qu'il n'est pas blanc. */
/* ⚠️ CE QUI A ÉTÉ ABANDONNÉ N'EST PAS LE CADRE, C'EST LA FUSION (20/08/2026,
   « tu as enlevé tous les fonds ») — deux choses différentes que la passe
   précédente a confondues. `mix-blend-mode: multiply` cherchait à rendre le
   fond de CHAQUE PHOTO invisible pour qu'il se fonde dans l'or : ça ne
   marche jamais parfaitement (voir plus haut) et Jesse l'a montré à l'écran.
   Mais un cadre — la photo en retrait, un mat or clairement visible tout
   autour, comme un cadre de photo ordinaire — n'a JAMAIS eu ce problème :
   personne n'attend qu'un cadre se fonde dans sa photo, la limite entre les
   deux est le principe même du cadre, pas un défaut à cacher. `object-fit:
   contain` + un rembourrage suffisent, SANS fusion : le mat or reste visible
   partout, sans jamais chercher à effacer le bord de la photo. */
.section-epoques .carrousel-visuel img {
  object-fit: contain; padding: 20px; box-sizing: border-box;
}
/* ⚠️ MINIATURES RÉDUITES, CES DEUX SECTIONS SEULEMENT (20/08/2026, « diminue
   la taille des miniatures... pour ces slides seulement ») : `.carrousel-
   vignette`/`.carrousel-visuel` valent pour tout le site (rubriques,
   suggestions…) — les rétrécir globalement aurait changé des bandes que
   Jesse n'a pas visées. */
/* ⚠️ TROISIÈME PASSE (20/08/2026, « époques et styles doivent faire la même
   taille ») : les deux tours précédents ajustaient une section à la fois
   (styles pour égaler la largeur d'époque, puis l'inverse) — Jesse veut au
   final UN SEUL gabarit pour les deux, celui qui remplit le container à six
   cartes (182px, calculé sur un container mesuré à 1180px). Styles, à quatre
   cartes, ne le remplit plus tout à fait à ce gabarit — c'est le compromis
   qu'implique une taille commune avec un nombre de cartes différent. */
.section-epoques .carrousel-vignette { width: 182px; }
.section-epoques .carrousel-visuel { min-height: 78px; }
.carrousel-initiale {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  font-size: 40px; font-weight: 650; color: var(--encre-tenue);
}
.carrousel-nom { font-weight: 550; }
.carrousel-legende { font-size: 13.5px; color: var(--encre-tenue); }

@media (max-width: 640px) {
  .carrousel-vignette { width: 62vw; }
}

/* Qui a demandé moins d'animations n'a pas demandé un défilement animé. */
@media (prefers-reduced-motion: reduce) {
  .carrousel-visuel img { transition: none; }
}

/* -------------------------------------------------- sections d'accueil --
   Composées dans /admin/accueil.php. Chaque section porte son propre
   espacement vertical : c'est elle qui décide de sa respiration, sinon
   l'ordre dans lequel on les empile changerait les écarts. */
.hero {
  position: relative;
  /* PLEINE LARGEUR depuis l'intérieur de la colonne de contenu. Un simple
     `margin: 0 -24px` n'annulait que le rembourrage : la bannière s'arrêtait à
     1180 px et laissait deux bandes vides sous un en-tête qui, lui, traverse la
     page — on lisait ça comme un défaut d'affichage, pas comme un parti pris.
     `50% - 50vw` ramène le bord de l'élément au bord de la fenêtre, quelle que
     soit la largeur de la colonne. */
  margin:0 calc(50% - 50vw) 40px;
  width: 100vw;
  min-height: 380px; padding:64px 0;
  display: flex; align-items: center;
  background-size: cover; background-position: center;
  color: #FFFFFF;
  /* PLUS DE `background-image` ICI : le visuel est une <img class="hero-fond">
     posée par le gabarit, pour qu'un téléphone puisse recevoir une autre taille
     que celle d'un grand écran. Voir sections-accueil.php pour le détail, et
     surtout pour la raison d'avoir renoncé aux deux variables CSS. */
}
/* L'IMAGE DE FOND, DANS LE MÊME PLAN QUE LA VIDÉO (z-index: 0, sous le voile
   qui est à 1 et le texte à 2). Elle est écrite AVANT la vidéo dans le gabarit :
   quand les deux existent, la vidéo la recouvre et l'image lui sert d'affiche
   le temps du téléchargement. */
.hero-fond {
  position: absolute; inset: 0; z-index: 0;
  width: 100%; height: 100%; object-fit: cover;
  display: block;
}
/* Un voile sous le texte : une photographie claire rendrait illisible n'importe
   quel texte posé dessus, et on ne choisit pas la photographie du client.
   Il s'éteint AVANT la moitié droite : étalé sur toute la largeur, il ternissait
   le visuel là où il n'y a rien à protéger — un dégradé franc y perdait tout ce
   qui le rendait franc. Le texte, lui, tient dans le tiers gauche. */
.hero::before {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(90deg, rgba(0,0,0,.58) 0%, rgba(0,0,0,.30) 36%, rgba(0,0,0,0) 68%);
}
.hero-sans-visuel { background: var(--fond-doux); color: var(--encre); }
.hero-sans-visuel::before { display: none; }
/* LE RÉGLAGE QUI ALIGNE LE HÉROS SUR LE RESTE DE LA PAGE.
   Il portait la largeur du conteneur (`--largeur`) mais PAS son rembourrage,
   alors que `.contenu` a les deux : `max-width: var(--largeur)` ET
   `padding: 0 24px`. Le titre du héros commençait donc 24 px à gauche de tous
   les autres titres de la page — assez pour se voir, trop peu pour qu'on
   sache dire ce qui cloche.

   Le rembourrage horizontal a quitté `.hero` pour venir ici : sur `.hero`, il
   s'ajoutait au centrage et décalait le bloc au lieu de le border. Les trois
   déclinaisons responsives suivent la même règle. */
.hero-corps {
  position: relative; width: 100%;
  max-width: var(--largeur); margin:0 auto; padding:0 24px;
}
.hero-titre { font-family: var(--police-titre); font-size: clamp(30px, 5vw, 46px); margin:0 0 12px; max-width: 18ch; }
/* `text-wrap: pretty` empêche les orphelins de fin de paragraphe. Sans lui,
   « Livraison offerte dès 49 €. » renvoyait « €. » seul sur la dernière ligne :
   deux caractères sous une phrase pleine, ce qui saute aux yeux dans un titrage
   de bannière alors que personne ne le remarquerait dans un paragraphe long. */
.hero-accroche {
  font-size: 18px; margin:0 0 24px; max-width: 46ch; opacity: .92;
  text-wrap: pretty;
}
.hero-titre { text-wrap: balance; }

.section-texte { padding:8px 0 40px; }
.section-texte h2 { margin-bottom:14px; }
.section-produits { padding:8px 0 40px; }
.section-produits h2 { margin-bottom:20px; }
/* ⚠️ PLUS D'AIR SOUS LES ARTICLES (25/08/2026, demande de Jesse). Les 40px du
   rythme commun laissaient 52px entre le bas des cartes et le bloc suivant :
   assez pour deux paragraphes, pas pour deux SECTIONS dont l'une est une bande
   à deux colonnes. Les cartes d'article sont hautes, encadrées et cliquables —
   elles se lisent comme un ensemble fermé, et ce qui suit doit commencer
   franchement ailleurs. Seul le bas change : le haut reste calé sur le rythme
   des autres sections, sinon l'écart avec ce qui précède doublerait aussi. */
.section-journal { padding:8px 0 72px; }
/* EN PISTE DÉFILANTE (24/08/2026, « 4 articles de blog sur une ligne, slider
   défilant ») : la carte d'article dans la piste du carrousel — QUATRE PAR
   VUE, calculées sur la largeur du conteneur moins les trois écarts de 18px
   de la piste. C'est la même carte que la grille du journal, seule la
   mécanique d'empilement change : dans une piste flex, `flex: 0 0 <base>`
   remplace les colonnes de la grille, et l'accrochage au défilement vient
   de `.carrousel-piste` sans rien redéclarer. */
.section-journal .carte-article {
  flex: 0 0 calc((100% - 54px) / 4);
  scroll-snap-align: start;
}
@media (max-width: 900px) {
  /* Sous 900px, un quart de conteneur est une carte de timbre-poste : même
     gabarit mobile que les vignettes de carrousel (62vw), la piste défile. */
  .section-journal .carte-article { flex-basis: 62vw; }
}

@media (max-width: 640px) {
  .hero { min-height: 300px; padding:40px 0; }
  .hero-corps { padding:0 20px; }
}

/* Mention légale non renseignée : volontairement voyante. Une page légale
   incomplète qui a l'air complète est pire qu'une page qui signale ses trous —
   l'exploitant croit être en règle et ne l'est pas. */
.mention-manquante {
  background: #FFE9A8; color: #6B4E00;
  padding:1px 7px; border-radius: 4px;
  font-size: .92em; font-weight: 600; white-space: nowrap;
}

/* ------------------------------------------------------------- WhatsApp --
   Bouton flottant, repris du site d'origine. Il se pose AU-DESSUS de la bulle
   de chat quand les deux modules sont allumés : côte à côte, ils se
   recouvriraient, et c'est toujours celui du dessous qu'on voulait viser. */
.whatsapp-flottant {
  position: fixed; right: 24px; bottom: 24px; z-index: 9998;
  display: inline-flex; align-items: center; gap:8px;
  padding:10px 16px; border-radius: 999px;
  background: #25D366; color: #fff; text-decoration: none;
  font-size: 14px; font-weight: 600;
  box-shadow: 0 6px 20px rgba(37, 211, 102, .35);
}
.whatsapp-flottant.avec-chat { bottom: 84px; }
.whatsapp-flottant:hover { background: #20BD5A; }
.whatsapp-flottant svg { width: 18px; height: 18px; flex: none; }
@media (max-width: 560px) {
  /* Sur un écran étroit, deux boutons à libellé mangent la moitié de la
     largeur : on garde l'icône seule. */
  .whatsapp-flottant-texte { display: none; }
  .whatsapp-flottant { padding:12px; }
}

/* ------------------------------------------- présentations de galerie --
   Quatre dispositions pour le même balisage : la classe change, rien d'autre.
   Dupliquer le gabarit par disposition aurait garanti qu'une correction
   n'arrive un jour que dans trois d'entre elles. */

/* Vignettes DESSOUS — la disposition par défaut. */
.galerie-dessous .fiche-vignettes { display: flex; gap:10px; flex-wrap: wrap; }

/* Vignettes À GAUCHE, en colonne : elle rend le visuel principal plus grand à
   largeur égale, ce qui compte pour du vêtement ou de l'affiche. */
.galerie-gauche { display: grid; grid-template-columns: 76px 1fr; gap:12px; align-items: start; }
.galerie-gauche .fiche-visuel-principal { order: 2; }
.galerie-gauche .fiche-vignettes {
  order: 1; display: flex; flex-direction: column; gap:10px;
  max-height: 520px; overflow-y: auto; scrollbar-width: none;
}
.galerie-gauche .fiche-vignettes::-webkit-scrollbar { display: none; }

/* GRILLE — toutes les vues d'un coup, sans visuel principal. Pour un produit
   qu'on juge sur l'ensemble de ses angles plutôt que sur une image. */
.galerie-grille .fiche-visuel-principal { display: none; }
.galerie-grille .fiche-vignettes { display: grid; grid-template-columns: repeat(2, 1fr); gap:10px; }
.galerie-grille .fiche-vignettes img { width: 100%; height: auto; aspect-ratio: 1; }

/* CARROUSEL — une seule vue à la fois, qu'on fait défiler au doigt. C'est la
   disposition des petits écrans, proposée aussi en grand pour les catalogues
   qui ont beaucoup de vues par produit. */
.galerie-carrousel .fiche-visuel-principal { display: none; }
.galerie-carrousel .fiche-vignettes {
  display: flex; gap:10px; overflow-x: auto;
  scroll-snap-type: x mandatory; scrollbar-width: none;
}
.galerie-carrousel .fiche-vignettes::-webkit-scrollbar { display: none; }
.galerie-carrousel .fiche-vignettes img {
  flex: 0 0 100%; width: 100%; height: auto; aspect-ratio: 1; scroll-snap-align: start;
}

.fiche-vignettes img { cursor: pointer; transition: border-color .15s ease; }
/* LA VIGNETTE ACTIVE NE SE SIGNALE QUE LÀ OÙ « ACTIVE » VEUT DIRE QUELQUE
   CHOSE : dans les présentations qui ont un visuel principal, où la vignette
   indique laquelle est affichée en grand. En grille et en carrousel il n'y a
   pas de visuel principal — le filet bleu sur la première image se lisait alors
   comme une sélection dont on ne comprenait ni la cause ni l'effet. */
.fiche-vignettes img.est-active { border-color: var(--accent); }
.galerie-grille .fiche-vignettes img.est-active,
.galerie-grille-vedette .fiche-vignettes img.est-active,
.galerie-carrousel .fiche-vignettes img.est-active { border-color: var(--filet); }
.fiche-visuel-principal img { cursor: zoom-in; }

/* ------------------------------------------------------------------ zoom --
   Une visionneuse plein écran, ouverte au clic sur le visuel principal ou une
   vignette. Sans elle, on commande sur une image de 400 px de côté. */
.zoom {
  position: fixed; inset: 0; z-index: 10000;
  display: none; align-items: center; justify-content: center;
  background: rgba(10, 10, 14, .92);
}
.zoom.est-ouvert { display: flex; }
.zoom img { max-width: 92vw; max-height: 88vh; object-fit: contain; }
.zoom-fermer, .zoom-fleche {
  position: absolute; border: 0; cursor: pointer;
  background: rgba(255, 255, 255, .12); color: #fff;
  width: 44px; height: 44px; border-radius: 50%;
  font: inherit; font-size: 20px; line-height: 1;
  display: flex; align-items: center; justify-content: center;
}
.zoom-fermer:hover, .zoom-fleche:hover { background: rgba(255, 255, 255, .22); }
/* LES CHEVRONS ET LA CROIX SONT DES CARACTÈRES, pas des tracés : « ‹ », « › »
   et « × » n'occupent pas le même creux dans leur boîte de ligne que des
   chiffres, et l'interlignage par défaut les posait deux à trois pixels trop
   bas dans leur pastille. Le centrage par flex ne suffit pas à lui seul — il
   centre la BOÎTE DE LIGNE, dont la hauteur dépend de la police. Un
   interlignage de 1 réduit cette boîte au glyphe. */
.zoom-fermer, .zoom-fleche { line-height: 1; padding: 0; }
/* La croix est légèrement plus haute que large dans la plupart des polices :
   une correction optique d'un pixel la recale sur l'axe du bouton. */
.zoom-fermer { font-size: 22px; }
.zoom-fleche { font-size: 24px; }
.zoom-fermer { top: 20px; right: 20px; width: 48px; height: 48px; font-size: 26px; }
/* LES FLÈCHES SUIVENT L'IMAGE, ELLES NE SONT PLUS COLLÉES AUX BORDS.
   Elles étaient épinglées à vingt pixels des bords de la FENÊTRE : sur un
   grand écran, le visuel d'un bijou fait sept cents pixels de large au milieu
   de deux mille, et les commandes se retrouvaient à six cents pixels de ce
   qu'elles commandent — on ne les reliait plus à l'image.

   Elles sont déjà sœurs de l'image dans un conteneur flex : il suffit de les
   remettre dans le flux pour qu'elles se placent d'elles-mêmes de part et
   d'autre, quelle que soit la largeur du visuel. Rien à calculer, rien à
   mesurer au chargement. */
.zoom-fleche {
  position: static; transform: none;
  width: 56px; height: 56px; font-size: 30px;
  margin: 0 14px; flex: none;
}
/* L'image cède la place aux commandes plutôt que de passer dessous : deux
   boutons de 56 px et leurs marges font 168 px à réserver. */
.zoom img { max-width: calc(100vw - 200px); }

@media (max-width: 640px) {
  /* Au doigt, deux boutons de 56 px pris sur la largeur ne laisseraient plus
     rien au bijou : ils repassent aux bords, par-dessus l'image. */
  .zoom-fleche {
    position: absolute; top: 50%; transform: translateY(-50%);
    width: 46px; height: 46px; font-size: 26px; margin: 0;
  }
  .zoom-fleche-prec { left: 10px; }
  .zoom-fleche-suiv { right: 10px; }
  .zoom img { max-width: 92vw; }
}
.zoom-compte {
  position: absolute; bottom: 22px; left: 50%; transform: translateX(-50%);
  color: rgba(255, 255, 255, .7); font-size: 13px;
}
@media (max-width: 640px) {
  /* ⚠️ LA LIGNE QUI RENDAIT LE VISUEL PRINCIPAL A ÉTÉ RETIRÉE (08/09/2026), et
     avec elle une promesse que le code ne tenait pas.

     Elle disait « toutes les dispositions reviennent au carrousel sur mobile ».
     Rien de tel n'arrivait. Elle ne faisait qu'une chose : REMONTRER le visuel
     principal que `grille` et `carrousel` avaient masqué — sans rien changer
     aux vignettes, qui contiennent DÉJÀ la première image. Sur un téléphone,
     ces deux présentations affichaient donc la même photo deux fois de suite,
     en grand puis en vignette (mesuré à 414 px : `carrousel` donnait deux vues
     pleine largeur superposées).

     `grille-vedette` y échappait par accident de position : sa règle de
     masquage est écrite plus loin dans la feuille, donc elle l'emportait à
     poids égal. Trois présentations, trois comportements différents, tirés au
     sort par l'ordre des lignes — les trois boutiques en service tournent en
     `grille-vedette`, la seule où l'on ne voyait rien.

     Le mobile garde donc la disposition choisie, ce que les attributs `sizes`
     du gabarit supposaient déjà (`grille` : 50vw par vignette ; `carrousel` et
     la vue en vedette : 100vw). Les fiches à une seule image retombent sur
     `dessous` côté PHP et gardent leur visuel principal. */
  .galerie-gauche { grid-template-columns: 1fr; }
  .galerie-gauche .fiche-vignettes { flex-direction: row; max-height: none; }
}

/* -------------------------------------------------------- déclinaisons --
   Un sélecteur par attribut. Les valeurs indisponibles restent VISIBLES et
   barrées : masquer une taille absente du coloris choisi ferait croire qu'elle
   n'existe pas, au lieu de dire qu'elle n'est pas disponible ainsi. */
.declinaisons { display: flex; flex-direction: column; gap:4px; }
.declinaison-choisi { color: var(--encre-tenue); font-weight: 400; }
.declinaison-choix { display: flex; flex-wrap: wrap; gap:8px; }
.declinaison-valeur {
  min-width: 44px; padding:8px 14px;
  border: 1px solid var(--filet-fort); border-radius: var(--rayon);
  background: var(--surface); color: var(--encre);
  font: inherit; font-size: 14px; cursor: pointer;
}
.declinaison-valeur:hover { border-color: var(--encre-tenue); }
.declinaison-valeur.est-choisie { border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }
.declinaison-valeur.est-indisponible {
  opacity: .45;
  /* Barré en diagonale : le gris seul se confond avec un simple survol. */
  background-image: linear-gradient(to top right, transparent 47%, var(--filet-fort) 47%, var(--filet-fort) 53%, transparent 53%);
}
.declinaison-valeur.est-couleur { min-width: 0; padding:5px; }
.declinaison-pastille {
  display: block; width: 26px; height: 26px; border-radius: 50%;
  /* Un liseré, sinon une pastille blanche disparaît sur fond blanc. */
  border: 1px solid var(--filet-fort);
}
.declinaison-etat { color: var(--alerte); font-size: 14px; margin:4px 0 0; }
.sr-only {
  position: absolute; width: 1px; height: 1px; padding:0; margin:-1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.pied-identite { display: flex; flex-direction: column; gap:10px; font-size: 14px; }
/* ⚠ `align-self: start` N'EST PAS DÉCORATIF, IL EMPÊCHE LA DÉFORMATION.
   `.pied-identite` est un flex en COLONNE : ses enfants sont étirés sur toute
   la largeur par le `align-items: stretch` implicite, et `width: auto` ne
   protège pas — dans l'axe transversal, l'étirement l'emporte. Le logo sortait
   donc écrasé sur 380 px de large pour 34 px de haut. Le défaut a attendu la
   PREMIÈRE boutique à déposer un logo pour se voir : sans logo, le pied affiche
   le nom en texte et la règle ne servait jamais.

   LA HAUTEUR SUIT CELLE DE L'EN-TÊTE, d'un cran en dessous. Un seul réglage
   (`--marque-logo-hauteur`) commande les deux : une boutique au logo empilé qui
   remonte celui du haut ne doit pas avoir à se souvenir qu'il en existe un
   second en bas. Le facteur redonne exactement 34 px sur la valeur livrée. */
.pied-logo {
  height: calc(var(--marque-logo-hauteur, 44px) * 0.78);
  width: auto; align-self: start;
}
/* MÊME POLICE QUE LE LOGO DE L'EN-TÊTE (20/08/2026, « mauvaise police pour le
   logo ») : `.marque` (en-tête) pose `font-family: var(--police-titre)`,
   cette règle-ci l'avait oublié et retombait donc sur la police de corps par
   défaut — la même identité de marque se lisait dans deux polices selon
   qu'on regardait en haut ou en bas de page. `700` remplace `650` : un poids
   hors des crans standard (400/700) peut retomber sur un gras SYNTHÉTIQUE
   flou selon la police système du visiteur — déjà vu et corrigé une fois
   ailleurs sur ce projet, pas de raison de le réintroduire ici. */
.pied-marque {
  font-family: var(--police-titre);
  font-weight: 700; font-size: 17px; letter-spacing: -0.02em;
}
.pied-adresse { font-style: normal; color: var(--encre-douce); line-height: 1.6; }
.pied-contact { display: flex; flex-direction: column; gap:2px; }
.pied-contact a { color: var(--encre-douce); text-decoration: none; }
.pied-contact a:hover { color: var(--encre); text-decoration: underline; }
.pied-horaires { display: flex; flex-direction: column; gap:2px; color: var(--encre-douce); }
.pied-horaires strong { color: var(--encre); font-size: 13px; }
.pied-copyright { color: var(--encre-tenue); font-size: 13px; margin-top:6px; }

/* La réassurance était habillée DEUX FOIS dans cette feuille, et c'est le bloc
   d'ici qui gagnait — il vient après. Il datait des icônes en emoji :
   `display: flex` sur le point, une taille de police sur l'icône, un
   `flex-direction: column` sur le corps. Il annulait donc en silence la grille
   à colonne fixe écrite plus haut, celle qui aligne tous les textes sur la
   même abscisse, et rendait inopérant le passage en rangée `auto-fit`. Les
   points revenaient en escalier sans qu'aucune des deux règles ne soit fausse
   prise isolément.
   La leçon est la même que pour les 138 classes sans règle, prise par l'autre
   bout : avant d'écrire un habillage, chercher si la classe en a déjà un.
   L'habillage vivant est celui de la section « Réassurance », plus haut. */
.paiement-logos { padding-top:16px; margin-top:16px; border-top: 1px solid var(--filet); }

/* --------------------------------------------------------- page contact --
   Formulaire à gauche, coordonnées à droite. Ces classes venaient du gabarit
   vitrine et n'avaient AUCUNE règle dans cette feuille : la page s'affichait
   donc en une seule colonne, le formulaire au-dessus des coordonnées, sur toute
   la largeur. Rien ne le signalait — un bloc sans style reste un bloc. */
/* LES COORDONNÉES SONT UNE CARTE, comme le formulaire (28/09/2026, « c'est
   étrangement centré »). Nues, elles ne tenaient qu'une étroite colonne de
   texte au début de leur piste : le bord droit de la page restait vide sous
   les icônes de l'en-tête, et le formulaire semblait flotter au milieu. En
   carte, la colonne occupe toute sa piste et s'aligne sur le bord droit du
   conteneur.
   LARGEUR FIXE POUR LES COORDONNÉES, le reste au formulaire (même soir, « il y
   a du vide pour rien dans la colonne de droite ») : un tiers de 1 440 px,
   c'était 460 px pour une adresse dont la plus longue ligne — les horaires —
   en fait 260. 340 px = cette ligne + les marges de la carte. */
.contact-grid {
  display: grid; grid-template-columns: minmax(0, 1fr) 340px;
  gap:32px; align-items: start; padding-bottom:48px;
}
.contact-form {
  background: var(--surface); border: 1px solid var(--filet);
  border-radius: var(--rayon-large); padding:24px;
}
.contact-sidebar {
  display: flex; flex-direction: column; gap:18px;
  background: var(--surface); border: 1px solid var(--filet);
  border-radius: var(--rayon-large); padding:24px;
}
.contact-sidebar > :last-child { margin-bottom:0; }
.contact-row { display: flex; flex-direction: column; gap:3px; font-size: 14px; }
.contact-row-label {
  font-size: 12px; text-transform: uppercase; letter-spacing: .04em;
  color: var(--encre-tenue);
}
.contact-divider { height: 1px; background: var(--filet); }
.contact-avatar-wrap { display: flex; align-items: center; gap:12px; }
.contact-avatar { width: 46px; height: 46px; border-radius: 50%; object-fit: cover; }

/* Dépôt de pièces jointes : le pointillé dit qu'on peut y lâcher un fichier,
   ce qu'aucun libellé ne fait comprendre aussi vite. */
.attach-dropzone {
  border: 2px dashed var(--filet-fort); border-radius: var(--rayon);
  padding:20px; text-align: center; cursor: pointer;
  color: var(--encre-douce); font-size: 14px;
}
.attach-dropzone:hover, .attach-dropzone.est-survolee { border-color: var(--accent); color: var(--accent-texte); }
.attach-dropzone-text { display: block; font-weight: 550; }
.attach-dropzone-hint { display: block; font-size: 12.5px; color: var(--encre-tenue); margin-top:4px; }
.attach-filelist { display: flex; flex-direction: column; gap:6px; margin-top:10px; font-size: 13.5px; }

@media (max-width: 900px) {
  .contact-grid { grid-template-columns: 1fr; gap:32px; }
}

/* ------------------------------------------------------------ recherche --
   Dans l'en-tête, à gauche : le champ est replié derrière son icône et s'ouvre
   au survol ou au focus. Déplié en permanence, il prendrait la place du logo,
   qui est centré sur la page. */
/* L'ESPACEMENT EST PORTÉ PAR LE CHAMP, PAS PAR LE FORMULAIRE. Un `gap` s'applique
   entre deux éléments frères même quand le second est réduit à zéro pixel : le
   champ replié est invisible mais reste un élément de flex, et le formulaire
   mesurait donc 41 px pour un bouton de 35. La pastille de thème était repoussée
   de 6 px de plus que toutes les autres, alors que la barre est réglée à 8 px
   partout. Déplacé sur la marge du champ, l'écart n'existe que lorsque le champ
   existe — et il s'anime avec lui. */
.recherche-entete { display: flex; align-items: center; }
.recherche-entete input {
  width: 0; padding:0; border: 0; opacity: 0; margin-left:0;
  font: inherit; font-size: 14px; background: var(--surface); color: var(--encre);
  border-radius: var(--rayon);
  transition: width 180ms ease, opacity 160ms ease, padding 180ms ease, margin-left 180ms ease;
}
/* :focus-within et non :hover seul — au clavier, on n'a pas de survol. */
.recherche-entete:hover input,
.recherche-entete:focus-within input,
.recherche-entete input:not(:placeholder-shown) {
  width: 190px; padding:7px 11px; opacity: 1; margin-left:6px;
  border: 1px solid var(--filet-fort);
}
/* Le formulaire de la PAGE de résultats. Il reprend les proportions de la
   barre ouverte du bandeau — même hauteur, même corps de texte — pour qu'on
   reconnaisse le même outil des deux côtés. */
.recherche-formulaire { display: flex; gap:8px; margin:4px 0 20px; max-width: 640px; }
.recherche-formulaire input {
  flex: 1; font: inherit; font-size: 16px; padding:12px 15px;
  border: 1px solid var(--filet-fort); border-radius: var(--rayon);
  background: var(--surface); color: var(--encre);
}
.resultats-liens { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap:10px; padding-bottom:40px; }
.resultat-lien {
  display: flex; flex-direction: column; gap:2px;
  padding:14px 16px; border: 1px solid var(--filet); border-radius: var(--rayon);
  text-decoration: none; color: inherit; background: var(--surface);
}
.resultat-lien:hover { border-color: var(--filet-fort); }
.resultat-lien span { font-size: 12.5px; color: var(--encre-tenue); }
/* L'EXTRAIT EST BORNÉ À DEUX LIGNES (22/09/2026, avec l'index de recherche).
   Une page de contenu rend trois cents caractères de chapô : sans cette
   borne, une carte de page fait trois fois la hauteur d'une carte de
   rubrique, et la grille se troue. */
.resultat-extrait {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; line-height: 1.45; margin-top:2px;
}
/* ── LES FAMILLES DE RÉSULTATS ───────────────────────────────────────────────
   Des filtres, pas des onglets : « Tout » reste une option, et chacun porte
   son compte. Ils défilent latéralement plutôt que de se replier sur trois
   lignes — sept familles tiennent rarement sur une ligne de téléphone. */
.recherche-familles {
  display: flex; flex-wrap: wrap; gap:8px; margin-top:14px;
}
.recherche-famille {
  display: inline-flex; align-items: center; gap:7px;
  padding:7px 14px; border:1px solid var(--filet); border-radius: 999px;
  text-decoration: none; color: var(--encre-douce); font-size: 14px;
  background: var(--surface); transition: color .15s ease, border-color .15s ease;
}
.recherche-famille span { font-size: 12.5px; color: var(--encre-tenue); }
.recherche-famille:hover { color: var(--accent-texte); border-color: var(--filet-fort); }
/* L'ACTIF SE LIT AU REMPLISSAGE, pas à la couleur du texte seule : sur une
   rangée de pastilles toutes semblables, une nuance d'encre ne se voit pas. */
.recherche-famille.est-active {
  background: var(--accent-doux); border-color: transparent; color: var(--accent-texte); font-weight: 500;
}
.recherche-famille.est-active span { color: inherit; opacity: .75; }
/* Le compte d'une section, à côté de son titre : il dit combien il y en a en
   tout, quand la section n'en montre que les premiers. */
.section-compte {
  font-size: 13px; font-weight: 400; color: var(--encre-tenue);
  vertical-align: middle; margin-left:6px;
}
.recherche-suite { padding-bottom:40px; margin-top:-24px; }
@media (max-width: 640px) {
  /* Sur petit écran, le champ replié n'a pas la place de se déplier dans le
     bandeau : seule la loupe reste. La toucher ouvre le MÊME panneau que sur
     grand écran — champ centré en pleine largeur, suggestions dessous, page
     floutée (voir `.recherche-ouverte` plus bas et `shop.js`). */
  .recherche-entete input { display: none; }
  .recherche-ouverte .recherche-entete input { display: block; }

  /* LA LOUPE REVIENT DANS L'EN-TÊTE MOBILE (26/09/2026, « il faut intégrer la
     recherche en mobile sur tous les shops »). Le 20/08, le champ avait quitté
     le tiroir — un formulaire n'a rien à faire dans un menu — et, le groupe
     d'outils étant masqué sous 640 px, la recherche n'existait plus du tout
     sur téléphone. Elle se range à côté du panier, dans le coin des actions :
     le groupe d'outils garde sa seule loupe, la bascule de thème et le badge
     d'avis restent réservés au grand écran. */
  /* Tout le positionnement est REDIT ici : un bloc mobile plus ancien
     (« .bandeau-outils { position: static; order: 3 } ») le remettait dans le
     flux, où il poussait le logo hors du centre. */
  .bandeau-outils {
    display: flex; position: absolute; left: auto; right: 56px;
    top: 50%; transform: translateY(-50%);
  }
  .bandeau-outils > :not(.recherche-entete) { display: none; }
  /* Le logo reste centré sur la PAGE : l'en-tête réserve la même place des
     deux côtés — menu à gauche, loupe et panier à droite. `object-fit` garde
     ses proportions quand un petit écran le rétrécit. */
  .bandeau-haut { padding-left: 100px; padding-right: 100px; }
  .marque img { max-width: 100%; object-fit: contain; }
}

/* ------------------------------------------------- bulles des icônes --
   Le libellé s'affiche AU SURVOL et au focus clavier, et non à côté de l'icône.
   Écrits en toutes lettres, les libellés faisaient une barre d'actions plus
   large que le logo qu'elle encadre.

   `title` n'aurait pas suffi : la bulle native met une seconde à apparaître,
   ne se style pas, et ne s'affiche jamais au clavier. L'attribut ARIA reste sur
   l'élément — un lecteur d'écran ne survole rien. */
.bouton-icone, .bandeau-badge-avis { position: relative; }
.bouton-icone[data-bulle]::after,
.bandeau-badge-avis[data-bulle]::after {
  content: attr(data-bulle);
  position: absolute; top: calc(100% + 8px); left: 50%; transform: translateX(-50%);
  padding:5px 9px; border-radius: 6px;
  background: var(--encre); color: var(--fond);
  font-size: 12.5px; font-weight: 500; white-space: nowrap;
  opacity: 0; pointer-events: none; transition: opacity 120ms ease;
  z-index: 40;
}
.bouton-icone[data-bulle]:hover::after,
.bouton-icone[data-bulle]:focus-visible::after,
.bandeau-badge-avis[data-bulle]:hover::after,
.bandeau-badge-avis[data-bulle]:focus-visible::after { opacity: 1; }
/* Sans libellé écrit, l'icône n'a plus besoin de la largeur d'un bouton texte. */
.bandeau-actions .bouton-icone, .bandeau-outils .bouton-icone { padding:8px; }
@media (hover: none) {
  /* Au doigt, il n'y a pas de survol : la bulle resterait affichée après le
     tap, par-dessus la page vers laquelle on vient de naviguer. */
  .bouton-icone[data-bulle]::after,
  .bandeau-badge-avis[data-bulle]::after { display: none; }
}

/* ------------------------------------------- suggestions de recherche --
   Le panneau sous le champ de l'en-tête. Au repos il est ancré sous le champ
   replié ; DÈS QU'IL S'OUVRE, le champ et lui quittent le coin gauche pour le
   centre de la page, en pleine largeur (voir plus bas). */
.recherche-entete { position: relative; }
.recherche-suggestions {
  position: absolute; top: calc(100% + 8px); left: 0; z-index: 45;
  width: 340px; max-height: 70vh; overflow-y: auto;
  background: var(--surface); border: 1px solid var(--filet);
  border-radius: var(--rayon); box-shadow: 0 12px 40px rgba(16, 16, 24, .16);
  padding:6px;
}
.recherche-suggestions[hidden] { display: none; }

/* --- Le fond flouté pendant la recherche ---
   SEULS LE CHAMP ET SES RÉSULTATS RESTENT NETS. Tout le reste — le contenu,
   les bandeaux, le logo, le menu, les icônes d'action — passe au flou.

   Il a fallu DEUX MÉCANISMES, et c'est une contrainte du navigateur, pas un
   choix. Le bandeau est `sticky` avec un z-index : il forme son propre
   contexte d'empilement, et aucun de ses enfants ne peut en sortir. Un simple
   voile ne peut donc pas passer devant le menu tout en laissant le champ de
   recherche devant lui — les deux sont dans la même boîte.

   Alors :
     1. le voile (z-index 50) recouvre et floute tout ce qui vit HORS du
        bandeau : le contenu de la page, le pied, les deux bandes du haut ;
     2. le bandeau est remonté au-dessus du voile pendant l'ouverture, et ses
        propres enfants sont floutés un à un — sauf le formulaire.

   Le résultat est celui demandé, et il tient sans toucher au balisage. */
.voile-recherche {
  position: fixed; inset: 0; z-index: 50;
  background: rgba(10, 10, 16, .28);
  opacity: 0; pointer-events: none;
  transition: opacity 180ms ease;
}
/* Flou posé seulement quand la recherche est ouverte — même raison que
   `.voile-menu` (26/08/2026) : un calque de flou à opacité 0 coûte encore. */
.recherche-ouverte .voile-recherche { opacity: 1; pointer-events: auto; -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px); }

/* TOUT CE QUI DÉFILE S'ARRÊTE PENDANT L'OUVERTURE, et ce n'est pas une
   coquetterie : un `backdrop-filter` doit RECALCULER son flou à chaque image de
   ce qui bouge derrière lui. Les deux bandeaux défilants tournent en continu,
   le voile les re-floutait donc soixante fois par seconde — et le résultat
   scintillait franchement, le compositeur n'arrivant pas à suivre.
   Signalé à l'écran avant d'être compris ici.

   Le motif existait déjà dans ce fichier, pour le survol : on s'arrête pour
   qu'on puisse lire. La raison diffère, la règle est la même — et un fond qui
   bouge derrière un panneau ouvert n'avait de toute façon rien à y faire. */
.recherche-ouverte .bandeau-annonces.defile .bandeau-annonces-piste,
.recherche-ouverte .marques-bandeau.defile .marques-piste {
  animation-play-state: paused;
}

/* Le bandeau passe DEVANT le voile — sans quoi le champ, qui vit dedans,
   serait flouté avec lui.

   SON FOND ET SON FILET DEVIENNENT TRANSPARENTS, et c'est le complément
   indispensable. Remonté au-dessus du voile, le bandeau emmène AVEC LUI sa
   propre surface : ses enfants étaient bien floutés un à un, mais la bande
   opaque sur laquelle ils reposent restait parfaitement nette — un grand
   rectangle net en travers de l'écran, plus visible que ce qu'on cherchait à
   masquer. Transparent, il laisse le voile agir derrière lui, et seuls le
   champ et son panneau — qui portent leur propre fond — restent lisibles. */
.recherche-ouverte .bandeau {
  z-index: 60;
  background: transparent;
  border-bottom-color: transparent;
}

/* … et ses enfants sont floutés séparément, le formulaire excepté. Le flou est
   posé sur les conteneurs plutôt que sur `.bandeau` lui-même : appliqué au
   parent, il emporterait le formulaire, un filtre s'héritant par le rendu et
   non par la cascade — aucune règle sur l'enfant ne peut le défaire. */
/* Cibles NOMMÉES, pas `> *:not(…)` : voir le mégamenu (chantier INP du 25/09/2026). */
.recherche-ouverte .bandeau-haut > :is(.bandeau-menu, .marque, .bandeau-actions),
.recherche-ouverte .bandeau-outils > :is(.bouton-icone, .bandeau-badge-avis),
.recherche-ouverte .bandeau > .menu {
  filter: blur(12px);
  transition: filter 180ms ease;
  pointer-events: none;
}

/* Un fondu reste supportable, mais on l'abrège pour qui demande moins de
   mouvement — le flou, lui, n'est pas du mouvement et peut rester. */
@media (prefers-reduced-motion: reduce) {
  .voile-recherche { transition: none; }
  .recherche-ouverte .bandeau-haut > :is(.bandeau-menu, .marque, .bandeau-actions),
  .recherche-ouverte .bandeau-outils > :is(.bouton-icone, .bandeau-badge-avis),
  .recherche-ouverte .bandeau > .menu { transition: none; }
}

/* Le défilement du fond pendant que le panneau est ouvert donne l'impression
   que la page échappe au contrôle. */
.recherche-ouverte { overflow: hidden; }
/* ===========================================================================
   LE PANNEAU OUVERT : LE CHAMP AU CENTRE, LES RÉSULTATS EN LARGEUR
   ---------------------------------------------------------------------------
   Le champ replié vit dans le coin gauche du bandeau, où il ne gêne pas. Dès
   qu'il s'ouvre, il n'y a plus de raison qu'il y reste : c'est le seul endroit
   de la page où l'on regarde, et une colonne de 340 px accrochée au bord
   gauche affichait cinq produits en file indienne avec les trois quarts de
   l'écran vides à côté. Le champ passe donc AU CENTRE, en pleine largeur, et
   le panneau prend la largeur du champ.

   TROIS CONTRAINTES DE POSITIONNEMENT, chacune payée par un essai raté :

   1. `position: fixed` sur le formulaire, pour l'arracher au coin du bandeau.
   2. MAIS `.bandeau-outils` porte `transform: translateY(-50%)`, et un
      `transform` fait de l'élément le BLOC CONTENEUR de ses descendants
      `fixed` — le formulaire se recentrait alors dans la petite boîte des
      outils, à trente pixels du bord gauche. La transformation est donc
      annulée pendant l'ouverture ; ses autres enfants sont invisibles à ce
      moment-là, le décalage vertical ne se voit pas.
   3. Les frères du formulaire sont mis à `opacity: 0` et pas seulement floutés :
      le formulaire quittant le flux, la bascule de thème se recalait contre le
      bord gauche — un mouvement net, en pleine ouverture, sur le seul élément
      resté lisible du bandeau.
=========================================================================== */
.recherche-ouverte .bandeau-outils { transform: none; }
.recherche-ouverte .bandeau-outils > :is(.bouton-icone, .bandeau-badge-avis) { opacity: 0; }

.recherche-ouverte .recherche-entete {
  position: fixed; z-index: 61;
  top: clamp(64px, 11vh, 108px);
  left: 50%; transform: translateX(-50%);
  width: min(1040px, calc(100vw - 40px));
  padding:5px 6px 5px 12px;
  background: var(--surface);
  border: 1px solid var(--filet-fort); border-radius: var(--rayon-large);
  box-shadow: 0 20px 60px rgba(16, 16, 24, .22);
}
/* La loupe redevient un pictogramme dans la barre : son cadre de bouton, utile
   quand elle est seule dans le bandeau, ferait un bouton dans un bouton. */
.recherche-ouverte .recherche-declencheur {
  border-color: transparent; background: transparent; color: var(--encre-tenue); padding:6px;
}
.recherche-ouverte .recherche-declencheur svg { width: 19px; height: 19px; }
/* Les trois sélecteurs du champ replié sont repris ici, sinon celui de plus
   forte spécificité (`:hover input`) le ramènerait à 190 px au survol. */
.recherche-ouverte .recherche-entete input,
.recherche-ouverte .recherche-entete:hover input,
.recherche-ouverte .recherche-entete:focus-within input {
  flex: 1; width: auto; min-width: 0; margin-left:0;
  padding:11px 8px; font-size: 17px; opacity: 1;
  border: 0; background: transparent; outline: none;
}

.recherche-ouverte .recherche-suggestions {
  left: 0; right: 0; width: auto;
  top: calc(100% + 10px);
  max-height: min(64vh, 580px);
  padding:0; overflow: hidden auto;
  border-radius: var(--rayon-large);
  box-shadow: 0 20px 60px rgba(16, 16, 24, .22);
}

/* DEUX COLONNES : les produits, puis tout le reste. La colonne annexe est de
   largeur FIXE — en `1fr`, elle enflait à la moitié du panneau dès qu'aucun
   produit ne sortait, et la grille des produits sautait d'une largeur à
   l'autre au fil de la frappe. */
.recherche-corps { display: grid; grid-template-columns: minmax(0, 1fr) 292px; }
.recherche-corps.est-sans-produits,
.recherche-corps.est-sans-annexe { grid-template-columns: minmax(0, 1fr); }
.recherche-colonne { min-width: 0; padding:16px 18px 18px; }
.recherche-colonne-annexe { border-left: 1px solid var(--filet); background: var(--fond-doux); }
.recherche-corps.est-sans-produits .recherche-colonne-annexe {
  border-left: 0; background: transparent;
}

.recherche-groupe + .recherche-groupe { margin-top:18px; }
.recherche-groupe-titre {
  display: flex; align-items: center; gap:8px; margin-bottom:10px;
  font-size: 11.5px; text-transform: uppercase; letter-spacing: .05em;
  font-weight: 600; color: var(--encre-tenue);
}
/* Le compte par famille répond à la question qu'on se pose devant une liste
   tronquée : « est-ce tout ? ». */
.recherche-groupe-compte {
  padding:0 6px; border-radius: 999px;
  border: 1px solid var(--filet); background: var(--surface);
  font-size: 11px; letter-spacing: 0; color: var(--encre-tenue);
}

/* --- les produits, en vignettes --- */
.recherche-vignettes {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(148px, 1fr)); gap:8px;
}
.recherche-vignette {
  display: flex; flex-direction: column; gap:7px;
  padding:8px; border-radius: var(--rayon);
  border: 1px solid transparent; text-decoration: none; color: inherit;
}
.recherche-vignette:hover, .recherche-vignette:focus-visible {
  background: var(--fond-doux); border-color: var(--filet); outline: none;
}
/* 4/3 ET NON UN CARRÉ, et c'est une mesure et non un goût : les six vignettes
   se répartissent en deux rangées de quatre, et en carré la seconde rangée
   passait sous le bord du panneau — ses prix n'étaient visibles qu'en faisant
   défiler, alors que le prix est précisément ce qu'on vient chercher. C'est
   aussi la proportion des cartes produit du reste du site. */
.recherche-vignette-image {
  display: block; aspect-ratio: 4 / 3; border-radius: 8px;
  overflow: hidden; background: var(--fond-doux);
}
.recherche-vignette-image img { width: 100%; height: 100%; object-fit: cover; display: block; }
.recherche-vignette-vide { display: block; width: 100%; height: 100%; }
.recherche-vignette-texte { display: flex; flex-direction: column; gap:2px; min-width: 0; }
/* Deux lignes pour le nom, une pour l'accroche, et la coupure est franche :
   une vignette qui grandit avec la longueur du libellé désaligne la rangée. */
.recherche-vignette-titre {
  font-size: 13.5px; font-weight: 550; line-height: 1.35;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.recherche-vignette-note {
  font-size: 12px; line-height: 1.4; color: var(--encre-tenue);
  display: -webkit-box; -webkit-line-clamp: 1; -webkit-box-orient: vertical; overflow: hidden;
}
.recherche-vignette-prix { font-size: 13.5px; font-weight: 650; margin-top:auto; }

/* --- rubriques, journal et pages, en lignes --- */
.recherche-lignes { display: flex; flex-direction: column; gap:2px; }
.recherche-suggestion {
  display: flex; align-items: center; gap:10px;
  padding:7px 8px; border-radius: 8px;
  text-decoration: none; color: inherit; font-size: 14px;
}
.recherche-suggestion:hover, .recherche-suggestion:focus-visible {
  background: var(--surface); outline: none;
}
.recherche-corps.est-sans-produits .recherche-suggestion:hover,
.recherche-corps.est-sans-produits .recherche-suggestion:focus-visible {
  background: var(--fond-doux);
}
.recherche-suggestion img { width: 34px; height: 34px; object-fit: cover; border-radius: 6px; flex: none; }
.recherche-puce { width: 34px; height: 34px; border-radius: 6px; background: var(--filet); flex: none; }
.recherche-suggestion-texte { display: flex; flex-direction: column; gap:1px; min-width: 0; flex: 1; }
.recherche-suggestion-titre { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.recherche-suggestion-detail { color: var(--encre-tenue); font-size: 12px; }

/* --- le pied du panneau --- */
.recherche-pied {
  position: sticky; bottom: 0;
  display: flex; align-items: center; justify-content: space-between; gap:16px;
  padding:10px 18px; border-top: 1px solid var(--filet); background: var(--surface);
}
.recherche-tout { font-size: 13.5px; font-weight: 550; color: var(--accent-texte); text-decoration: none; }
.recherche-tout:hover, .recherche-tout:focus-visible { text-decoration: underline; }
.recherche-astuce { font-size: 12px; color: var(--encre-tenue); }
.recherche-astuce kbd {
  font: inherit; font-size: 11px; padding:1px 5px; margin-right:3px;
  border: 1px solid var(--filet-fort); border-bottom-width: 2px; border-radius: 5px;
  background: var(--fond-doux); color: var(--encre-douce);
}

/* --- rien trouvé --- */
.recherche-vide { padding:32px 20px; text-align: center; }
.recherche-vide-titre { margin:0 0 4px; font-weight: 550; }
.recherche-vide-piste { margin:0 0 16px; font-size: 13.5px; color: var(--encre-tenue); }
.recherche-vide-liens { display: flex; justify-content: center; gap:20px; margin:0; font-size: 13.5px; }
.recherche-vide-liens a { color: var(--accent-texte); }

/* Sous 900 px le panneau n'a plus la place de deux colonnes : l'annexe passe
   sous les produits, avec un filet horizontal au lieu du filet vertical. */
@media (max-width: 900px) {
  .recherche-corps { grid-template-columns: minmax(0, 1fr); }
  .recherche-colonne-annexe { border-left: 0; border-top: 1px solid var(--filet); }
  .recherche-astuce { display: none; }
}
/* Le panneau ouvert sur téléphone : le champ monte en haut de l'écran, là où
   le pouce l'attend et où le clavier ne le recouvre pas, et les suggestions
   prennent toute la hauteur restante. Écrit APRÈS les règles générales du
   panneau ouvert, qui fixent `top` et `width` au même poids. */
@media (max-width: 640px) {
  .recherche-ouverte .recherche-entete { top: 12px; width: calc(100vw - 24px); }
  .recherche-ouverte .recherche-suggestions { max-height: calc(100dvh - 96px); }
}

/* ---------------------------------------------------------------------------
   CARACTÉRISTIQUES DE LA FICHE PRODUIT
   Un tableau, et non une liste : on lit une fiche technique en balayant la
   colonne des libellés jusqu'à celui qu'on cherche. Une liste « Matière :
   coton » fait commencer chaque valeur à une abscisse différente, et oblige à
   lire les lignes une par une.
   La colonne de gauche est donc de largeur FIXE, et les libellés y sont plus
   pâles que les valeurs : c'est la valeur qu'on vient chercher.
--------------------------------------------------------------------------- */
/* ═══ LA FICHE TECHNIQUE, EN VRAI TABLEAU ═══════════════════════════════════
   Elle était une liste de paires posées sur des filets, en largeur
   automatique. Deux défauts en découlaient, et le second était un vrai bogue :
   la largeur auto se réglait sur le contenu, si bien que le tableau changeait
   de largeur d'un produit à l'autre ; et comme la colonne des intitulés était
   fixée en POURCENTAGE d'une largeur elle-même variable, un libellé long
   venait toucher sa valeur — « Famille de braceletManchette », sans espace.

   Un pourcentage n'a de sens que sur une largeur connue : le tableau prend
   donc toute sa colonne, et les deux réglages redeviennent cohérents.

   L'habillage suit : fond doux, rangées alternées, cadre arrondi. Une fiche
   technique de bijou se PARCOURT — on y cherche le carat, le poids, l'époque —
   et l'alternance guide l'œil d'une ligne à l'autre bien mieux qu'un filet. */
/* ⚠️ LE CADRE EST SUR LE CONTENEUR, PAS SUR LE <table>, et ce n'est pas un
   caprice de structure. Un `<caption>` est rendu HORS de la boîte de bordure du
   tableau : le navigateur fabrique une boîte enveloppe qui porte la légende et
   la grille, et `border` / `border-radius` / `overflow` écrits sur `table` ne
   s'appliquent qu'à la grille. La légende gardait donc ses angles droits et sa
   propre largeur au-dessus d'un cadre arrondi qui commençait sous elle : deux
   rectangles empilés, exactement ce qu'on voulait éviter en la rendant visible.
   Aucun sélecteur n'atteint la boîte enveloppe — d'où la <div>. */
.fiche-caracteristiques-bloc {
  margin: 24px 0 0;
  border: 1px solid var(--filet);
  border-radius: var(--rayon-large);
  overflow: hidden;
  background: var(--surface);
}
.fiche-caracteristiques {
  width: 100%;
  border-collapse: separate; border-spacing: 0;
  font-size: 14.5px;
  /* ⚠️ `table-layout: fixed` EST CE QUI TIENT LA LARGEUR — mesuré sur un
     téléphone de 343px le 25/08/2026, après avoir retiré le `width: 46%` des
     intitulés : en disposition AUTOMATIQUE, un tableau dont le contenu réclame
     plus de place que son conteneur DÉBORDE, `width: 100%` ou pas. Le bloc
     mesurait 388px dans 343 disponibles, et son cadre (`overflow: hidden`)
     coupait la fin des valeurs. En disposition fixe, les colonnes se règlent sur
     la largeur annoncée et le texte passe à la ligne — ce que faisait
     l'ancienne mise en page, pour cette raison exactement. */
  table-layout: fixed;
}
/* LA LÉGENDE EST VISIBLE DEPUIS QUE LE TABLEAU EST DANS LA COLONNE D'ACHAT.
   Serré contre le prix et la réassurance, il devenait un troisième empilement
   de paires libellé/valeur sans rien qui le nomme. Elle reste dans le
   <caption> plutôt que dans un <h3> voisin : elle appartient au tableau, et un
   lecteur d'écran l'annonce à l'entrée. */
.fiche-caracteristiques caption {
  text-align: left;
  font-family: var(--police-titre);
  /* 16 px ET NON 14 (18/08/2026, « titre trop petit »). Un titre de bloc écrit
     PLUS PETIT que les lignes qu'il annonce (14,5 px) ne se lit plus comme un
     titre : il passe pour une première ligne du tableau, un peu plus foncée.
     L'écart était encore aggravé par la police de titre, à l'œil plus petite
     qu'une linéale au même corps — une même valeur numérique ne donne pas la
     même taille apparente d'une famille à l'autre. */
  font-size: 16px; font-weight: var(--marque-poids-titre, 600);
  color: var(--encre);
  /* Rembourrage aligné sur celui des cellules (16px) : la légende et les
     libellés commençaient à deux abscisses différentes, à deux pixels près —
     un décalage qu'on ne nomme pas mais qu'on voit. */
  padding: 13px 16px 12px;
  background: var(--fond-doux);
  border-bottom: 1px solid var(--filet);
}
/* ═══ REVUE DU 25/08/2026 (« je n'aime pas trop cette présentation ») ════════
   Deux choses faisaient l'effet d'un tableau de tableur plutôt que d'une fiche
   technique de bijoutier :

   • L'ALTERNANCE DE FONDS. Dix bandes beiges et blanches sur toute la largeur
     de la colonne d'achat pèsent bien plus que l'information qu'elles portent —
     elles avaient été posées pour guider l'œil d'un libellé à sa valeur, mais
     l'œil n'a pas ce trajet à faire ici : la valeur est alignée à droite, donc
     toujours au même endroit. Un filet d'un pixel suffit à séparer, et ne
     colore rien.

   • LE VIDE AU MILIEU. Les libellés collés à gauche, les valeurs plantées à
     46 % : entre les deux, un couloir vide qui grandissait avec la colonne
     (jusqu'à trois cents pixels sur grand écran) et cassait le lien entre une
     ligne et sa valeur. Les valeurs sont désormais alignées sur le bord DROIT,
     le libellé sur le gauche : deux colonnes qui se lisent chacune d'un trait,
     et un écart qui ne dépend plus de la largeur disponible.

   Le libellé se met en retrait (plus petit, encre tenue), la valeur porte le
   poids : sur une fiche de bijou d'occasion, c'est « 750/00 (18K) » et « 9,9 gr »
   qu'on vient chercher, pas le mot « Carat ». */
.fiche-caracteristiques th,
.fiche-caracteristiques td {
  padding: 11px 16px;
  vertical-align: baseline;
  line-height: 1.45;
}
.fiche-caracteristiques th {
  /* 42 % : la part réservée aux intitulés. Elle n'a plus le rôle qu'elle avait
     avant (poser l'abscisse des valeurs — celles-ci sont désormais alignées sur
     le bord droit) ; elle sert seulement à borner la colonne de gauche pour que
     les deux textes ne se touchent jamais, y compris sur un écran de 320px. */
  width: 42%;
  padding-right: 20px;
  text-align: left;
  font-weight: 400; font-size: 13.5px;
  color: var(--encre-tenue);
}
.fiche-caracteristiques td {
  text-align: right;
  color: var(--encre); font-weight: 600;
  font-variant-numeric: tabular-nums;
  /* Une référence sans espace (« X1363-AB-750/000 ») ne peut pas passer à la
     ligne toute seule : sans ce repli, elle rendrait sa colonne plus large que
     la place disponible et rouvrirait le débordement que `table-layout` vient
     de fermer. */
  overflow-wrap: anywhere;
}
/* UN FILET ENTRE LES LIGNES, pas sous la dernière : un trait au ras du cadre
   arrondi doublerait sa bordure d'un second trait à un pixel. */
.fiche-caracteristiques tbody tr + tr th,
.fiche-caracteristiques tbody tr + tr td { border-top: 1px solid var(--filet); }

/* ===========================================================================
   ESPACE CLIENT
   Une bonne moitié des classes de ces écrans n'avait AUCUNE règle : le HTML
   était juste, et le rendu tombait en une seule colonne sans hiérarchie. C'est
   le troisième écran du projet à souffrir du même défaut (contact, pages
   légales, puis celui-ci) — d'où la règle : une classe écrite dans un gabarit
   se vérifie dans la feuille de style, pas de tête.
=========================================================================== */
.account-layout {
  display: grid;
  grid-template-columns: 232px minmax(0, 1fr);
  gap:40px;
  align-items: start;
}
@media (max-width: 860px) {
  .account-layout { grid-template-columns: 1fr; gap:24px; }
}
.account-main { min-width: 0; }

/* --- Barre latérale --- */
.account-sidebar { position: sticky; top: 92px; }
@media (max-width: 860px) { .account-sidebar { position: static; } }
.account-nav { display: flex; flex-direction: column; gap:2px; }
@media (max-width: 860px) {
  /* En bande horizontale défilante sur mobile : une colonne de cinq liens
     repousserait le contenu sous la ligne de flottaison. */
  .account-nav { flex-direction: row; overflow-x: auto; gap:6px; padding-bottom:6px; }
  .account-nav-link { white-space: nowrap; }
}
.account-nav-link {
  display: flex; align-items: center; gap:10px;
  padding:10px 12px; border-radius: 9px;
  color: var(--encre-tenue); text-decoration: none;
  font-size: 14.5px; font-weight: 500;
  transition: background .15s, color .15s;
}
.account-nav-link:hover { background: var(--fond-doux); color: var(--encre); }
.account-nav-link.is-active { background: var(--fond-doux); color: var(--encre); }
.account-nav-link svg { flex: 0 0 18px; opacity: .75; }
/* La déconnexion est séparée du reste : c'est la seule action irréversible du
   menu, elle ne doit pas se cliquer par ricochet. */
.account-nav-logout { margin-top:12px; border-top: 1px solid var(--filet); padding-top:16px; }

/* --- Compteurs du tableau de bord --- */
.account-compteurs {
  display: grid; grid-template-columns: repeat(3, 1fr); gap:14px; margin-bottom:32px;
}
@media (max-width: 620px) { .account-compteurs { grid-template-columns: 1fr; } }
.account-compteur {
  background: var(--surface); border: 1px solid var(--filet);
  border-radius: 12px; padding:18px 20px;
}
.account-compteur-valeur {
  display: block; font-size: 25px; font-weight: 600; color: var(--encre); line-height: 1.2;
}
.account-compteur-libelle {
  display: block; margin-top:4px; font-size: 13px; color: var(--encre-tenue);
}

.account-titre {
  font-size: 17px; font-weight: 600; margin:32px 0 14px;
}
.account-main > .account-titre:first-child { margin-top:0; }

/* --- Écran vide --- */
.account-vide {
  border: 1px dashed var(--filet); border-radius: 14px;
  padding:40px 24px; text-align: center; color: var(--encre-tenue);
}
.account-vide-titre { font-size: 17px; font-weight: 600; color: var(--encre); margin-bottom:8px; }
.account-vide .bouton { margin-top:18px; }

/* --- Carte de commande --- */
.commande-carte {
  border: 1px solid var(--filet); border-radius: 13px;
  padding:18px 20px; margin-bottom:14px; background: var(--surface);
}
.commande-entete {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap:14px; flex-wrap: wrap;
}
.commande-reference {
  font-weight: 600; font-size: 15.5px; color: var(--encre); text-decoration: none;
  font-variant-numeric: tabular-nums;
}
.commande-reference:hover { text-decoration: underline; }
.commande-date { font-size: 13px; color: var(--encre-tenue); margin-top:2px; }
.commande-chiffres {
  display: flex; gap:32px; flex-wrap: wrap;
  margin-top:16px; padding-top:14px; border-top: 1px solid var(--filet);
}
.commande-etiquette {
  display: block; font-size: 12px; color: var(--encre-tenue); margin-bottom:2px;
}
.commande-chiffres strong { font-size: 15px; font-weight: 600; }
.commande-pied {
  display: flex; align-items: center; justify-content: space-between;
  gap:12px; flex-wrap: wrap; margin-top:14px;
}
.commande-suivi { font-size: 13px; color: var(--encre-tenue); }
.commande-suivi code {
  background: var(--fond-doux); padding:2px 7px; border-radius: 5px;
  font-size: 12.5px; letter-spacing: .03em;
}

/* --- Pastille de statut ---
   La teinte arrive de la base via --teinte : le fond en est une version très
   diluée, le texte la version pleine. Un fond plein rendrait illisibles les
   statuts définis en couleur claire par le marchand. */
.statut-pastille {
  display: inline-flex; align-items: center; gap:7px;
  padding:5px 12px; border-radius: 999px;
  font-size: 12.5px; font-weight: 600; white-space: nowrap;
  color: var(--teinte, #6b7280);
  background: color-mix(in srgb, var(--teinte, #6b7280) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--teinte, #6b7280) 26%, transparent);
}
.statut-point {
  width: 7px; height: 7px; border-radius: 50%; background: currentColor; flex: 0 0 7px;
}

/* --- Détail d'une commande --- */
.commande-bandeau {
  display: flex; align-items: center; justify-content: space-between;
  gap:14px; flex-wrap: wrap; margin-bottom:24px;
}
/* LA FRISE EST DANS UNE CARTE, comme le suivi et le retour qui la suivent.
   Posée nue sur le fond de la page, entre deux encarts gris, elle flottait :
   trois points verts et trois mots, sans rien qui dise où le bloc commence ni
   où il finit. */
.commande-etapes {
  display: flex; list-style: none; margin:0 0 24px; padding:24px 18px 18px; gap:0;
  border: 1px solid var(--filet); border-radius: var(--rayon-large);
  background: var(--surface);
}
.commande-etape {
  flex: 1; position: relative; padding-top:24px;
  font-size: 13.5px; color: var(--encre-tenue); text-align: center;
}
/* Le trait qui relie deux étapes est porté par l'étape SUIVANTE et s'arrête à
   son point : posé sur l'étape courante, il dépasserait après la dernière. */
.commande-etape + .commande-etape::after {
  content: ''; position: absolute; top: 6px; right: 50%; width: 100%;
  height: 2px; background: var(--filet);
}
.commande-etape.est-faite + .commande-etape.est-faite::after { background: var(--succes); }
.commande-etape-point {
  position: absolute; top: 0; left: 50%; transform: translateX(-50%);
  width: 14px; height: 14px; border-radius: 50%;
  background: var(--surface); border: 2px solid var(--filet); z-index: 1;
}
.commande-etape.est-faite { color: var(--encre); font-weight: 500; }
.commande-etape.est-faite .commande-etape-point { background: var(--succes); border-color: var(--succes); }

/* Même dessin que la frise et le bloc de retour : c'est la troisième boîte de
   la même colonne, elle n'a aucune raison d'avoir son propre rayon (11 px, qui
   ne correspondait à aucun jeton) ni d'être la seule sans filet. */
.commande-encart {
  display: flex; align-items: center; gap:12px; flex-wrap: wrap;
  background: var(--fond-doux);
  border: 1px solid var(--filet); border-radius: var(--rayon-large);
  padding:14px 18px; margin-bottom:24px; font-size: 14px;
}
.commande-encart code { letter-spacing: .05em; font-size: 13.5px; }

.commande-lignes { border: 1px solid var(--filet); border-radius: 13px; overflow: hidden; }
.commande-ligne {
  display: flex; align-items: center; justify-content: space-between;
  gap:16px; padding:15px 18px; border-bottom: 1px solid var(--filet);
  text-decoration: none; color: inherit;
}
.commande-ligne:last-child { border-bottom: 0; }
a.commande-ligne:hover { background: var(--fond-doux); }
.commande-ligne-nom { font-weight: 500; }

.commande-colonnes {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap:32px; margin-top:8px;
}
@media (max-width: 700px) { .commande-colonnes { grid-template-columns: 1fr; gap:8px; } }
.commande-adresse { font-style: normal; line-height: 1.7; font-size: 14.5px; }

/* ===========================================================================
   FILTRES DE RUBRIQUE
   La colonne remplace le rappel des rubriques, qui n'apprenait rien à qui
   venait d'entrer dans l'une d'elles.
=========================================================================== */
.rubrique-corps {
  display: grid;
  grid-template-columns: 248px minmax(0, 1fr);
  gap:40px;
  align-items: start;
}
@media (max-width: 900px) {
  /* Sur mobile la colonne passe AU-DESSUS des produits, dans un bloc repliable :
     posée en dessous, elle serait invisible ; laissée dépliée, elle repousserait
     le premier produit hors de l'écran. */
  .rubrique-corps { grid-template-columns: 1fr; gap:20px; }
}
.rubrique-resultats { min-width: 0; }

.rubrique-barre {
  display: flex; align-items: center; justify-content: space-between;
  gap:16px; flex-wrap: wrap; margin-bottom:20px;
}
.rubrique-compte { margin:0; font-size: 14.5px; }
.rubrique-compte .ligne-meta { font-weight: 400; }
.rubrique-tri { display: flex; align-items: center; gap:9px; margin:0; }
.rubrique-tri label { font-size: 13.5px; color: var(--encre-tenue); }
.rubrique-tri select {
  padding:7px 24px 7px 11px; border-radius: 9px;
  border: 1px solid var(--filet); background: var(--surface);
  color: var(--encre); font-size: 14px; font-family: inherit;
}

/* LE DÉCALAGE VIENT DE LA HAUTEUR RÉELLE DU BANDEAU, mesurée par shop.js et
   posée dans `--collant`. Il était écrit 92 px en dur alors que le bandeau
   collant en fait 130 : le titre de la fiche produit passait dessous, et rien
   ne le disait puisque le reste de la colonne, lui, tombait juste. La valeur de
   repli couvre le cas où le script ne s'exécute pas. */
/* ⚠️ HAUTEUR BORNÉE ET DÉROULEMENT INTERNE (20/08/2026, « si les filtres
   dépassent la hauteur des listes de produit, elle doit être fixée et
   déroulable ») : une colonne de filtres plus haute que la grille de
   produits imposait jusqu'ici la longueur de LA PAGE ENTIÈRE — un bijou aux
   critères nombreux (matière, pierre, genre, famille…) pouvait pousser un
   visiteur à faire défiler des centaines de pixels de vide à droite pour
   atteindre le bas du filtre. `max-height` + `overflow-y: auto` la fait
   défiler SUR PLACE, dans la hauteur disponible sous l'en-tête collant —
   sans effet quand elle tient déjà dans cette hauteur.
   `--collant` sert deux fois dans ce calcul : une fois pour poser le haut de
   la colonne, une fois pour connaître ce qu'il reste de fenêtre en dessous. */
.filtres {
  position: sticky; top: var(--collant, 106px);
  max-height: calc(100vh - var(--collant, 106px) - 24px);
  overflow-y: auto;
  /* ⚠️ BARRE MASQUÉE (20/08/2026, « c'est pas dingue la barre de scroll ») :
     `scrollbar-width: thin` restait un rail de navigateur brut, jamais
     habillé par la charte. Le flou en bas de colonne (juste en dessous) dit
     déjà « il y a plus à dérouler » — la barre n'apportait plus qu'un trait
     gris de plus. Le défilement à la molette, au trackpad et au clavier reste
     intact ; seul le rail visuel disparaît, même recette que `.avis-rangee`
     un peu plus haut dans cette feuille. */
  scrollbar-width: none;
}
.filtres::-webkit-scrollbar { display: none; }
/* LE FLOU EN BAS DE COLONNE, SEULEMENT QUAND IL Y A VRAIMENT DE QUOI DÉROULER
   — `shop.js` pose cette classe après avoir comparé la hauteur réelle du
   contenu à celle de la boîte. Sans cette condition, une courte liste de
   filtres (peu de caractéristiques actives) afficherait un bord flouté qui
   annoncerait du contenu inexistant. */
/* ⚠️ ZONE DE FLOU AGRANDIE (20/08/2026, « effet de flou plus intense ») :
   28px de transition se voyait à peine sur une colonne de plusieurs
   centaines de pixels de haut — presque un bord net. Portée à 70px pour un
   dégradé qui se remarque vraiment. */
.filtres.filtres--deroulable {
  -webkit-mask-image: linear-gradient(180deg, #000 0, #000 calc(100% - 70px), transparent 100%);
          mask-image: linear-gradient(180deg, #000 0, #000 calc(100% - 70px), transparent 100%);
}
/* … ET IL S'EFFACE UNE FOIS ARRIVÉ EN BAS (posé/retiré par le même script au
   défilement) : passé ce point il n'y a plus rien sous le flou, le garder
   promettrait du contenu qui n'existe plus. */
.filtres.filtres--deroulable.filtres--en-bas { mask-image: none; -webkit-mask-image: none; }
@media (max-width: 900px) { .filtres { position: static; max-height: none; overflow: visible; mask-image: none; -webkit-mask-image: none; } }
.filtres-entete {
  display: flex; align-items: baseline; justify-content: space-between;
  gap:12px; margin-bottom:6px;
}
.filtres-entete h2 { font-size: 15px; font-weight: 600; margin:0; }
.filtres-effacer { font-size: 13px; color: var(--encre-tenue); }
.filtres-effacer:hover { color: var(--accent-texte); }

/* ⚠️ LE REMBOURRAGE HORIZONTAL EST SUR LE BLOC, PAS SEULEMENT SUR LA LIGNE.
   Une bande de survol se construit toujours à deux : un rembourrage sur le
   CONTENEUR, une marge négative de même valeur sur la ligne. Avec la marge
   négative seule — ce qu'on avait — la bande sort du bloc : sur la colonne de
   filtres elle allait de 84 à 354 px pendant que le titre de section, le filet
   et les valeurs tenaient de 95 à 343. La bande ne s'alignait sur rien, et la
   case à cocher touchait le bord de sa propre colonne.
   Avec les deux, la bande épouse exactement le bloc et le contenu respire À
   L'INTÉRIEUR. Le titre de section suit le même retrait : il est dans le bloc. */
.filtre-bloc { padding:18px var(--filtre-retrait, 11px); border-bottom: 1px solid var(--filet); }
.filtre-bloc:last-child { border-bottom: 0; }
.filtre-bloc h3 {
  font-size: 12px; font-weight: 600; letter-spacing: .04em;
  text-transform: uppercase; color: var(--encre-tenue); margin:0 0 11px;
}
.filtre-valeurs { display: flex; flex-direction: column; gap:1px; }

/* Une valeur de filtre : case, libellé, compte. Le compte est à droite et
   aligné, parce qu'on le compare d'une valeur à l'autre en descendant la
   colonne — désaligné, il oblige à relire chaque ligne. */
.filtre-bascule {
  display: flex; align-items: center; gap:10px;
  /* La marge négative REND au bloc le rembourrage qu'il vient de poser : la
     bande couvre le bloc d'un bord à l'autre, le texte reste en retrait. */
  padding:7px var(--filtre-retrait, 11px); margin:0 calc(-1 * var(--filtre-retrait, 11px));
  border-radius: 8px;
  color: var(--encre); text-decoration: none; font-size: 14px;
  transition: background .13s;
}
.filtre-bascule:hover { background: var(--fond-doux); }

/* ⚠️ LA RESPIRATION DE FIN DE FICHE NE DOIT PAS DÉPENDRE DU CARROUSEL.
   Sur une fiche produit, c'est le carrousel « À compléter avec » qui ferme le
   contenu, et ses 52 px de rembourrage bas séparaient la fiche de la bande
   d'avis. Un produit SANS suggestion — le baguier, faute d'association et de
   voisins de rubrique — perdait ce bloc, donc cette respiration : la carte
   « Avant de commander » venait buter contre la bande d'avis, bord contre
   bord. L'espace appartient à la fiche, pas à un bloc qui peut manquer. */
.contenu > .aide-achat:last-child,
.contenu > .fiche-description-bloc:last-child { padding-bottom: 52px; }
/* LE LIBELLÉ EST DÉSIGNÉ PAR SON NOM, plus par sa position. `nth-child(2)`
   visait le libellé tant qu'il était le deuxième enfant ; en insérant la case à
   cocher avant l'échantillon de couleur, c'est l'échantillon qui a reçu le
   `flex: 1` et s'est étiré en barre de treize pixels de haut sur toute la
   largeur. Un sélecteur positionnel casse au premier élément ajouté. */
.filtre-libelle { flex: 1; min-width: 0; }
.filtre-compte {
  font-size: 12.5px; color: var(--encre-tenue);
  font-variant-numeric: tabular-nums; flex: 0 0 auto;
}
.filtre-bascule.est-actif { font-weight: 500; }

.filtre-case {
  width: 16px; height: 16px; flex: 0 0 16px; border-radius: 5px;
  border: 1.5px solid var(--filet-fort); background: var(--surface);
  position: relative;
}
.filtre-bascule.est-actif .filtre-case {
  background: var(--accent); border-color: var(--accent);
}
.filtre-bascule.est-actif .filtre-case::after {
  content: ''; position: absolute; left: 4.5px; top: 1px;
  width: 4px; height: 8px; border: solid var(--accent-encre);
  border-width: 0 2px 2px 0; transform: rotate(45deg);
}
/* Une pastille de couleur remplace la case quand l'attribut EN EST une : on
   choisit un bleu en le voyant, pas en lisant le mot « bleu ». */
/* L'échantillon de couleur est CARRÉ, la case est ronde-carrée : les deux ne se
   confondent plus. Il porte un filet, sans quoi un échantillon blanc disparaît
   sur fond blanc. */
.filtre-pastille {
  width: 13px; height: 13px; flex: none; border-radius: 3px;
  border: 1px solid var(--filet-fort);
  margin-right:2px;
}
.filtre-bascule.est-actif .filtre-pastille {
  box-shadow: inset 0 0 0 2px var(--surface), 0 0 0 2px var(--accent);
}

.filtre-prix-champs { display: flex; align-items: center; gap:7px; }
.filtre-prix-champs input {
  width: 100%; min-width: 0; padding:8px 9px;
  border: 1px solid var(--filet); border-radius: 8px;
  background: var(--surface); color: var(--encre);
  font-size: 14px; font-family: inherit;
}
.filtre-prix-ok {
  flex: 0 0 auto; padding:8px 11px; border-radius: 8px;
  border: 1px solid var(--filet); background: var(--fond-doux);
  color: var(--encre); font-size: 13px; font-weight: 600; cursor: pointer;
}
.filtre-prix-ok:hover { background: var(--accent); color: var(--accent-encre); border-color: var(--accent); }
.filtre-note { margin:8px 0 0; font-size: 12.5px; color: var(--encre-tenue); }

/* --- Filtres en bandeau (disposition « horizontal ») ---
   Même HTML que la colonne : seule la classe du conteneur change. Les blocs
   passent en ligne, et chaque groupe de valeurs devient une liste déroulante
   native — c'est le seul moyen de tenir six facettes sur une ligne sans
   inventer un menu maison qu'il faudrait rendre accessible au clavier. */
.rubrique-corps--horizontal { display: block; }
/* UN BANDEAU DE DÉCLENCHEURS, ET UN PANNEAU PLEINE LARGEUR.
   Les six dimensions tenaient d'abord dans six boîtes ouvertes côte à côte :
   chacune faisait cent cinquante pixels, les valeurs longues passaient à la
   ligne, « Marque » se dotait d'une barre de défilement interne — on lisait mal
   ce qu'on venait justement lire. Une rangée de déclencheurs occupe la même
   hauteur qu'une seule ligne, et le panneau qui s'ouvre dispose de TOUTE la
   largeur : les valeurs s'y étalent sur quatre ou cinq colonnes, sans coupure. */
.filtres--horizontal {
  /* `top: auto` EST INDISPENSABLE. La règle générale `.filtres` est collante
     avec `top: 92px` ; repasser en `relative` ne suffit pas — le décalage
     s'applique aussi aux éléments relatifs, et le bandeau descendait de 92 px
     par-dessus la grille de produits, qui, elle, n'avait pas bougé. */
  position: relative; top: auto;
  margin-bottom:24px;
  display: flex; flex-wrap: wrap; gap:9px;
  align-items: center;
}
.filtres--horizontal .filtres-entete { flex: 1 0 100%; margin-bottom:2px; }

/* Le déclencheur. Le marqueur natif est retiré : il place un triangle avant le
   texte, alors que la flèche doit être à la fin, du côté où le panneau s'ouvre. */
/* Le dépliant annule l'habillage de colonne de `.filtre-bloc` : dix-huit pixels
   de marge haute et basse plus un filet, qui étaient justes dans une colonne
   empilée et donnaient ici une pastille de 79 px de haut pour un déclencheur qui
   en fait 42. */
.filtre-depliant { position: static; padding:0; border-bottom: 0; }
.filtre-depliant > summary {
  display: inline-flex; align-items: center; gap:8px;
  padding:9px 14px; cursor: pointer; list-style: none;
  border: 1px solid var(--filet); border-radius: 999px;
  background: var(--surface); color: var(--encre-douce);
  font-size: 14px; white-space: nowrap;
  transition: border-color .14s ease, color .14s ease;
}
.filtre-depliant > summary::-webkit-details-marker { display: none; }
.filtre-depliant > summary:hover { border-color: var(--filet-fort); color: var(--encre); }
.filtre-depliant[open] > summary { border-color: var(--accent); color: var(--encre); }

.filtre-depliant-titre { font-weight: 550; }
/* Le compteur de filtres actifs sur CE déclencheur. Sans lui, un panneau fermé
   cache ses choix : on ne sait plus ce qui filtre la liste qu'on regarde. */
.filtre-depliant-nb {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 18px; height: 18px; padding:0 5px;
  border-radius: 999px; background: var(--accent); color: var(--accent-encre);
  font-size: 11.5px; font-weight: 650;
}
.filtre-depliant-fleche {
  width: 7px; height: 7px; flex: none; margin-left:1px;
  border: solid currentColor; border-width: 0 1.6px 1.6px 0;
  transform: translateY(-2px) rotate(45deg);
  transition: transform .16s ease;
}
.filtre-depliant[open] > summary .filtre-depliant-fleche { transform: translateY(1px) rotate(-135deg); }

/* Le panneau couvre la largeur du bandeau, sous la rangée. En absolu, il ne
   pousse pas la grille de produits vers le bas : ouvrir un filtre ne doit pas
   déplacer ce qu'on est en train de regarder. */
/* LE PANNEAU FERMÉ EST MASQUÉ EXPLICITEMENT. Un <details> fermé cache ses
   enfants — sauf ceux en position absolue, qui échappent au masquage natif du
   navigateur. Les six panneaux étaient donc rendus en permanence, empilés
   par-dessus la grille de produits, sans qu'aucun ne soit « ouvert ». */
.filtre-depliant:not([open]) .filtre-depliant-panneau { display: none; }
.filtre-depliant-panneau {
  position: absolute; left: 0; right: 0; top: calc(100% + 9px); z-index: 30;
  padding:18px 20px;
  border: 1px solid var(--filet); border-radius: var(--rayon-large);
  background: var(--surface); box-shadow: 0 12px 32px rgba(16, 16, 24, .10);
}
.filtre-depliant-panneau .filtre-valeurs,
.filtre-depliant-panneau {
  --colonnes-filtre: repeat(auto-fill, minmax(190px, 1fr));
}
.filtre-depliant-panneau .filtre-valeurs {
  display: grid; grid-template-columns: var(--colonnes-filtre);
  gap:2px 18px;
}
/* Le bloc de disponibilité n'a pas de .filtre-valeurs : ses bascules sont
   posées directement dans le panneau. */
.filtre-depliant-panneau > .filtre-bascule { display: flex; }
.filtre-depliant-panneau .filtre-bascule { padding:7px 9px; }
/* LE COMPTEUR RESTE COLLÉ À SON LIBELLÉ. En colonne, le libellé pousse le
   compteur contre le bord droit : les chiffres s'alignent verticalement, ce qui
   se lit bien sur une liste étroite. Dans une case de deux cents pixels, le même
   réglage laissait cent pixels de vide entre « Atelier Nord » et son « 1 », et
   on ne savait plus quel chiffre allait avec quel nom. */
.filtre-depliant-panneau .filtre-libelle { flex: 0 1 auto; }
.filtre-depliant-panneau .filtre-bascule .filtre-compte { margin-left:6px; }
.filtre-depliant-panneau .filtre-prix { max-width: 340px; }
.filtre-depliant-panneau .filtre-note { margin-top:9px; }

@media (max-width: 620px) {
  .filtre-depliant-panneau { padding:14px; }
  .filtre-depliant-panneau .filtre-valeurs { grid-template-columns: 1fr; }
}

/* --- Mini-carte de la page de contact --- */
.carte-bloc { margin-bottom:20px; }
.carte-cadre {
  position: relative; height: 220px; border-radius: var(--rayon);
  overflow: hidden; border: 1px solid var(--filet); background: var(--fond-doux);
}
/* Le repli porte l'adresse en clair. Il reste visible tant que la carte n'est
   pas chargée — et POUR TOUJOURS si le service tiers ne répond pas, ce qui est
   exactement le comportement voulu sur une page de contact. */
.carte-repli {
  position: absolute; inset: 0; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap:5px;
  padding:18px; text-align: center; font-size: 14px; color: var(--encre-tenue);
}
.carte-repli strong { color: var(--encre); }
.carte-cadre.est-chargee .carte-repli { display: none; }
.carte-point {
  width: 15px; height: 15px; border-radius: 50%;
  border: 2.5px solid #fff; box-shadow: 0 1px 5px rgba(0,0,0,.35);
}
.carte-mention { margin:7px 0 0; font-size: 11.5px; color: var(--encre-tenue); }
.carte-mention a { color: inherit; text-decoration: underline; }

.contact-bloc { margin-bottom:20px; }
.contact-bloc-titre { font-weight: 600; margin-bottom:6px; }
/* Logo de la carte de coordonnées : la hauteur du pied de page, même jeton.
   `max-width` borne un logotype très large à la carte (340 px moins ses marges). */
.contact-logo {
  display: block; height: calc(var(--marque-logo-hauteur, 44px) * 0.78);
  width: auto; max-width: 100%; object-fit: contain; object-position: left center;
  margin-bottom:12px;
}
.contact-adresse { font-style: normal; line-height: 1.65; color: var(--encre-tenue); font-size: 14.5px; }
.contact-horaires { line-height: 1.65; color: var(--encre-tenue); font-size: 14.5px; }

.contact-suite { padding-bottom:64px; }
.contact-suite-grille {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap:14px;
}
.contact-suite-lien {
  display: flex; flex-direction: column; gap:5px;
  padding:18px 20px; border: 1px solid var(--filet); border-radius: 12px;
  text-decoration: none; color: inherit; transition: border-color .15s, background .15s;
}
.contact-suite-lien:hover { border-color: var(--filet-fort); background: var(--fond-doux); }
.contact-suite-lien span { font-size: 13.5px; color: var(--encre-tenue); }

/* --- Bannière d'article ---
   Elle remplace .article-visuel, qui vivait sous le chapô. Le rapport est
   imposé plutôt que laissé à l'image : une bannière dont la hauteur dépend du
   fichier déposé fait sauter toute la page au chargement, et le marchand ne
   cadre pas deux visuels de la même façon. 3/1 sur grand écran, plus trapu sur
   téléphone — une bande de 3/1 y mesurerait 120 px de haut et ne montrerait
   plus rien. */
.article-banniere {
  margin:0 0 32px;
  border-radius: var(--rayon-large);
  overflow: hidden;
  background: var(--fond-doux);
  aspect-ratio: 3 / 1;
}
.article-banniere img {
  width: 100%; height: 100%; object-fit: cover; display: block;
}
@media (max-width: 700px) {
  .article-banniere { aspect-ratio: 16 / 9; margin-bottom:24px; }
}

/* --- Sommaire d'article ---
   Encadré discret : il précède le texte, il ne doit pas lui disputer
   l'attention. Un sommaire plus voyant que le premier paragraphe fait hésiter
   sur ce qu'on est censé lire en premier. */
/* ═══ LE SOMMAIRE ÉCOURTÉ (au-delà de douze entrées — voir article.php) ═════
   Il reste OUVERT et se coupe : un sommaire fermé ne sert plus à rien, puisque
   son intérêt est de se laisser parcourir du regard. Le fondu posé sur les
   dernières lignes dit que la liste continue — c'est le même geste que
   « Lire la suite » sur les textes de rubrique, et volontairement le même
   mécanisme : une case à cocher masquée, aucun script. */
.article-sommaire.est-ecourte { position: relative; }
.est-ecourte .sommaire-corps {
  position: relative;
  max-height: 190px; overflow: hidden;
  transition: max-height .35s ease;
}
/* Le fondu est posé SUR la liste, pas derrière : il masque la dernière ligne
   pour dire qu'elle est coupée, et disparaît net une fois déplié. */
.est-ecourte .sommaire-corps::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 76px;
  background: linear-gradient(to bottom, transparent, var(--fond-doux));
  pointer-events: none;
}
.sommaire-bouton {
  display: inline-flex; align-items: center; gap: 7px;
  margin-top: 12px; padding: 7px 16px; cursor: pointer;
  border: 1px solid var(--filet-fort); border-radius: 999px;
  background: var(--surface); color: var(--encre);
  font-size: 13.5px;
}
.sommaire-bouton:hover { border-color: var(--accent); color: var(--accent-texte); }
.sommaire-bouton::after {
  content: ''; width: 7px; height: 7px;
  border: solid currentColor; border-width: 0 1.6px 1.6px 0;
  transform: translateY(-2px) rotate(45deg);
}
.sommaire-moins { display: none; }
.sommaire-compte {
  margin-left: 8px;
  font-family: var(--police); font-size: 12.5px; font-weight: 400;
  color: var(--encre-tenue);
}

.sommaire-bascule:checked ~ .article-sommaire .sommaire-corps { max-height: 4000px; }
.sommaire-bascule:checked ~ .article-sommaire .sommaire-corps::after { display: none; }
.sommaire-bascule:checked ~ .article-sommaire .sommaire-plus { display: none; }
.sommaire-bascule:checked ~ .article-sommaire .sommaire-moins { display: inline; }
.sommaire-bascule:checked ~ .article-sommaire .sommaire-bouton::after { transform: translateY(1px) rotate(-135deg); }
/* La case est masquée : son contour de focus se reporte sur l'étiquette, sinon
   le dépliage devient invisible au clavier. */
.sommaire-bascule:focus-visible ~ .article-sommaire .sommaire-bouton {
  outline: 2px solid var(--accent); outline-offset: 2px;
}
@media (prefers-reduced-motion: reduce) {
  .est-ecourte .sommaire-corps { transition: none; }
}

.article-sommaire {
  margin: 0 0 32px;
  padding: 18px 22px;
  border: 1px solid var(--filet);
  border-left: 3px solid var(--accent);
  border-radius: var(--rayon);
  background: var(--fond-doux);
}
.article-sommaire h2 {
  margin: 0 0 10px;
  font-size: 14px; font-weight: 600;
  letter-spacing: .02em;
  color: var(--encre-tenue);
}
.article-sommaire ol {
  margin: 0; padding: 0;
  list-style: none;
  counter-reset: sommaire;
}
/* SEULES LES SECTIONS DE PREMIER NIVEAU AVANCENT LE COMPTEUR. En l'incrémentant
   sur tous les `li`, une sous-section consommait un numéro qu'elle n'affichait
   pas : la liste comptait 1, 2, —, 4. Le trou ne se voit qu'à l'écran, jamais
   dans le HTML. */
.article-sommaire li:not(.est-sous-section) { counter-increment: sommaire; }
/* La puce numérotée est dessinée par le compteur plutôt que par la liste : les
   sous-sections ne doivent PAS relancer la numérotation, et une liste native
   imbriquée le ferait. */
.article-sommaire li > a::before {
  content: counter(sommaire) ".";
  margin-right: 8px;
  color: var(--encre-tenue);
  font-variant-numeric: tabular-nums;
}
.article-sommaire li.est-sous-section { padding-left: 22px; }
.article-sommaire li.est-sous-section > a::before { content: "—"; }
.article-sommaire a {
  display: block;
  /* 40 px de haut : une cible tactile confortable, et deux entrées voisines
     qu'on ne confond pas au pouce (WCAG 2.2 §2.5.8 exige 24 px au minimum). */
  min-height: 40px;
  display: flex; align-items: center;
  color: var(--encre);
  text-decoration: none;
  font-size: 15px;
  line-height: 1.35;
}
.article-sommaire a:hover { color: var(--accent-texte); text-decoration: underline; }

/* LE DÉFILEMENT DOUX EST NEUTRALISÉ POUR QUI LE DEMANDE : un saut animé sur
   plusieurs écrans provoque des nausées chez les personnes sensibles au
   mouvement, et c'est exactement ce que signale prefers-reduced-motion. */
@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}
/* L'en-tête est collant : sans cette marge, une section visée par une ancre
   arrive DERRIÈRE lui, et le lecteur atterrit sur un titre invisible. */
.article-corps :where(h2, h3)[id] { scroll-margin-top: 90px; }

/* --- Bandeau de congés ---
   Deux tons : ambre quand la boutique prévient seulement, plus soutenu quand
   elle a fermé ses commandes. La différence doit se voir sans être lue — un
   client qui a déjà rempli son panier doit comprendre au premier coup d'œil
   que le bouton manquant n'est pas une panne. */
.bandeau-vacances {
  background: color-mix(in srgb, var(--accent) 14%, var(--fond));
  border-bottom: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
  padding:13px 0;
}
.bandeau-vacances.est-bloquant {
  background: color-mix(in srgb, var(--alerte) 13%, var(--fond));
  border-bottom-color: color-mix(in srgb, var(--alerte) 34%, transparent);
}
.bandeau-vacances .contenu {
  display: flex; align-items: center; gap:11px;
  padding-top:0; padding-bottom:0;
}
.bandeau-vacances p { margin:0; font-size: 14.5px; color: var(--encre); }
/* L'icône s'aligne optiquement sur la ligne de texte, pas sur sa boîte : un
   pictogramme carré posé sur une ligne de 15 px paraît toujours un peu haut. */
.bandeau-vacances-icone {
  flex: 0 0 auto; display: inline-flex; align-items: center;
  line-height: 1;
  /* Emoji 🌴 : taille via font-size (l'opacité .75 qui atténuait l'icône au trait
     est retirée — un emoji en couleur ne doit pas être terni). */
  font-size: 15px;
}
.bandeau-vacances-icone svg { display: block; }

/* --- Prix remisé ---
   Le prix courant passe en couleur d'alerte quand il est remisé. C'est le seul
   endroit du site où cette couleur ne signale pas un problème, et c'est
   assumé : sur une fiche produit, un chiffre en rouge à côté d'un prix barré se
   lit comme une bonne nouvelle, jamais comme une erreur. */
/* UN PRIX REMISÉ N'EST PAS UNE ALERTE. Il portait `--alerte` — le rouge des
   erreurs, celui qui dit « quelque chose ne va pas ». Deux torts : un signal
   d'erreur employé pour une bonne nouvelle, et un rouge codé en dur qui entre
   en conflit avec toute charte qui n'est pas rouge (sur une charte or, il fait
   déstockage sur une fiche de joaillerie). La doctrine de ce fichier est
   pourtant claire : les couleurs d'ÉTAT sont des signaux, elles ne s'emploient
   pas comme des couleurs de mise en valeur.

   C'est donc l'accent de la marque qui met en valeur — et rien n'est perdu du
   message : l'ancien prix reste barré à côté, et la pastille « −16 % » dit
   l'écart. */
.prix.est-remise { color: var(--accent-texte); }
.prix-echeance {
  display: inline-block; margin-left:8px;
  font-size: 12.5px; color: var(--encre-tenue);
}

/* --- Tableaux et encarts des pages légales ---
   Un tableau plutôt qu'une liste à puces pour les traitements de données : la
   colonne « base légale » se compare d'une ligne à l'autre, ce qu'une suite de
   paragraphes ne permet pas. Il DOIT défiler dans son propre cadre — cinq
   colonnes sur un téléphone déborderaient la page entière, et c'est le corps du
   document qui se mettrait à glisser latéralement. */
.prose .tableau-legal {
  display: block;
  width: 100%;
  overflow-x: auto;
  border-collapse: collapse;
  margin:20px 0;
  font-size: 14px;
}
.prose .tableau-legal thead { background: var(--fond-doux); }
.prose .tableau-legal th,
.prose .tableau-legal td {
  padding:10px 13px;
  text-align: left;
  vertical-align: top;
  border: 1px solid var(--filet);
  min-width: 120px;
}
.prose .tableau-legal th { font-weight: 600; font-size: 13px; }
.prose .tableau-legal code { font-size: 12.5px; white-space: nowrap; }

/* L'encart qui porte la conclusion de la page — « aucun traceur publicitaire »,
   ou l'inverse. C'est la phrase que le visiteur cherche ; elle ne doit pas se
   confondre avec les paragraphes autour. */
.prose .encart-legal {
  background: var(--fond-doux);
  border-left: 3px solid var(--accent);
  border-radius: 0 var(--rayon) var(--rayon) 0;
  padding:15px 18px;
  margin:20px 0;
}

/* --- Suivi de commande sans compte ---
   Formulaire court et centré : deux champs, un bouton. Toute la page ne sert
   qu'à ça, et l'étaler sur la largeur d'un écran donnerait deux champs perdus
   au milieu du vide. */
.suivi-bloc { max-width: 460px; margin:0 auto 64px; }
.suivi-formulaire { margin-bottom:20px; }
.suivi-formulaire .bouton { width: 100%; margin-top:6px; }
.suivi-bloc .mention { text-align: center; margin:8px 0 0; }

/* ===========================================================================
   AVIS CLIENTS
=========================================================================== */
.avis-bloc { margin-top:48px; padding-top:32px; border-top: 1px solid var(--filet); }
.avis-bloc h2 { margin:0 0 20px; }
.avis-vide, .avis-invite { color: var(--encre-tenue); font-size: 14.5px; }

.avis-synthese {
  display: grid; grid-template-columns: auto minmax(0, 260px);
  gap:32px; align-items: center; margin-bottom:24px;
}
@media (max-width: 620px) { .avis-synthese { grid-template-columns: 1fr; gap:16px; } }
.avis-note { display: flex; flex-direction: column; gap:4px; }
.avis-note strong { font-size: 38px; font-weight: 600; line-height: 1; }
.avis-note strong + span { font-size: 15px; color: var(--encre-tenue); }
.avis-compte { font-size: 13px; color: var(--encre-tenue); }
.avis-trop-peu { font-size: 14px; color: var(--encre-tenue); }

/* Les étoiles sont du texte : elles se copient, se lisent à la synthèse vocale
   et ne coûtent aucune requête. Les vides restent VISIBLES en gris — sans
   elles, on ne sait pas sur quelle échelle lire trois étoiles. */
.avis-etoiles { display: inline-flex; gap:2px; font-size: 17px; line-height: 1; color: var(--filet-fort); }
/* LES ÉTOILES SONT JAUNE-ORANGÉ, PAS À LA COULEUR D'ACCENT. Une note se lit
   avant d'être lue : l'or est le code universel de la notation, et le reprendre
   évite au visiteur de se demander si ces étoiles bleues sont cliquables. C'est
   aussi le seul endroit du site où une couleur ne vient pas de la charte, et
   c'est assumé — elle ne désigne pas la boutique, elle désigne une note. */
.avis-etoiles .est-pleine { color: var(--etoile); }
.avis-etoiles.est-petite { font-size: 14px; }

.avis-repartition { display: flex; flex-direction: column; gap:5px; }
.avis-barre-ligne {
  display: grid; grid-template-columns: 34px 1fr 26px;
  align-items: center; gap:9px; font-size: 12.5px; color: var(--encre-tenue);
}
.avis-barre { height: 7px; border-radius: 4px; background: var(--fond-doux); overflow: hidden; }
.avis-barre > span { display: block; height: 100%; background: var(--etoile); }
.avis-barre-n { text-align: right; font-variant-numeric: tabular-nums; }

.avis-liste { list-style: none; margin:0; padding:0; }
.avis-item { padding:18px 0; border-top: 1px solid var(--filet); }
.avis-item-entete {
  display: flex; align-items: center; gap:10px; flex-wrap: wrap;
  font-size: 13.5px; color: var(--encre-tenue); margin-bottom:7px;
}
.avis-item-entete strong { color: var(--encre); font-size: 14.5px; }
.avis-verifie { color: var(--succes); font-size: 12.5px; font-weight: 500; }
.avis-titre { font-weight: 600; margin:0 0 4px; }
.avis-corps { margin:0; line-height: 1.65; }
/* La réponse du marchand est décalée et teintée : on doit voir d'un coup d'œil
   qu'elle ne vient pas du client. */
.avis-reponse {
  margin:12px 0 0 20px; padding:12px 15px;
  background: var(--fond-doux); border-radius: var(--rayon); font-size: 14px;
}
.avis-reponse p { margin:4px 0 0; }

.avis-formulaire { margin-top:24px; padding-top:24px; border-top: 1px solid var(--filet); max-width: 560px; }
.avis-formulaire h3 { font-size: 16px; margin:0 0 16px; }
.avis-notes { border: 0; padding:0; margin:0 0 18px; display: flex; gap:8px; flex-wrap: wrap; }
.avis-notes legend { font-size: 13.5px; font-weight: 500; margin-bottom:8px; padding:0; }
.avis-note-choix { position: relative; }
.avis-note-choix input {
  position: absolute; opacity: 0; width: 0; height: 0;
}
.avis-note-choix span {
  display: inline-flex; align-items: center; gap:5px;
  padding:7px 11px; border: 1px solid var(--filet); border-radius: 999px;
  color: var(--filet-fort); font-size: 13px; cursor: pointer;
}
.avis-note-choix input:checked + span { border-color: var(--etoile); color: var(--etoile-encre); }
/* Le focus doit rester visible : la case est masquée, pas l'interaction. */
.avis-note-choix input:focus-visible + span { outline: 2px solid var(--accent); outline-offset: 2px; }
.avis-note-chiffre { color: var(--encre-tenue); font-variant-numeric: tabular-nums; }

/* --- Sélecteur de quantité ---
   Les flèches natives de input[type=number] sont minuscules, n'apparaissent
   qu'au survol sur la plupart des navigateurs et sont inatteignables au doigt.
   On les masque et on pose deux vrais boutons, à la taille d'une cible tactile. */
.selecteur-quantite {
  /* max-content : sans lui, le conteneur s'étirait à la largeur du champ
     parent — 150 px pour 124 px de contenu — et laissait un rectangle vide
     bordé après le bouton « + ». */
  display: inline-flex; width: max-content; align-items: stretch;
  border: 1px solid var(--filet); border-radius: 9px; overflow: hidden;
  background: var(--surface);
}
/* LA SPÉCIFICITÉ EST DÉLIBÉRÉMENT ÉLEVÉE, et les deux :not() sont recopiés
   exprès. La règle générique des formulaires est
   `.champ input:not([type="radio"]):not([type="checkbox"])`, qui vaut (0,3,1) :
   un simple `.selecteur-quantite .quantite` (0,2,0) perdait, et le champ
   gardait sa bordure, son rayon et son fond blanc — d'où un cadre blanc
   flottant à l'intérieur du sélecteur. On la bat franchement plutôt que de
   compter sur l'ordre du fichier, qui se réorganise un jour sans qu'on y pense. */
.selecteur-quantite input.quantite:not([type="radio"]):not([type="checkbox"]) {
  width: 52px; min-width: 0; text-align: center;
  border: 0; border-radius: 0; box-shadow: none;
  background: transparent; color: var(--encre);
  font-size: 15px; font-family: inherit; padding:0;
  -moz-appearance: textfield;
}
.selecteur-quantite input.quantite::-webkit-outer-spin-button,
.selecteur-quantite input.quantite::-webkit-inner-spin-button {
  -webkit-appearance: none; margin:0;
}
.selecteur-quantite input.quantite:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.quantite-pas {
  width: 36px; border: 0; background: var(--fond-doux);
  color: var(--encre); font-size: 16px; line-height: 1; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  transition: background .13s;
}
.quantite-pas:hover:not(:disabled) { background: var(--filet); }
.quantite-pas:disabled { opacity: .4; cursor: default; }

/* --- Récapitulatif de commande avec miniatures ---
   Trois colonnes : vignette, désignation, montant. Le montant reste collé à
   droite quelle que soit la longueur du nom — c'est la colonne qu'on additionne
   du regard. */
.recap-ligne-produit {
  display: grid; grid-template-columns: 44px minmax(0, 1fr) auto;
  gap:11px; align-items: center;
}
/* PAS D'overflow: hidden ICI : il servait à arrondir les coins de l'image, mais
   il rognait aussi la pastille de quantité, posée volontairement à cheval sur
   le coin — on n'en voyait qu'un quartier. C'est l'image qui porte désormais son
   propre arrondi (8 px moins l'épaisseur du filet). */
.recap-vignette {
  position: relative; width: 44px; height: 44px; flex: 0 0 44px;
  border: 1px solid var(--filet); border-radius: 8px;
  background: var(--fond-doux);
}
.recap-vignette img {
  width: 100%; height: 100%; object-fit: cover; display: block;
  border-radius: 7px;
}
/* La quantité se pose SUR la vignette plutôt qu'en préfixe du nom : elle reste
   lisible et ne pousse pas le texte, qui est déjà à l'étroit. */
.recap-quantite {
  position: absolute; top: -6px; right: -6px;
  min-width: 19px; height: 19px; padding:0 5px;
  border-radius: 999px; background: var(--encre); color: var(--fond);
  font-size: 11px; font-weight: 700; line-height: 19px; text-align: center;
  /* Le liseré à la couleur du fond détache la pastille de la vignette : sans
     lui, l'encre noire sur une vignette sombre se lit comme une tache. */
  box-shadow: 0 0 0 2px var(--fond);
}
.recap-nom { min-width: 0; line-height: 1.35; }
.recap-nom small { display: block; color: var(--encre-tenue); font-size: 12.5px; }
.recap-montant { white-space: nowrap; font-variant-numeric: tabular-nums; }

/* Le bandeau de congés : contenu centré, dates en gras. */
.bandeau-vacances .contenu { justify-content: center; text-align: center; }
.bandeau-vacances p strong { font-weight: 600; color: var(--encre); }

/* ===========================================================================
   PAIEMENT EN PLUSIEURS FOIS (simulateur)
   La bascule entre échéanciers est en CSS pur : des boutons radio masqués et
   le sélecteur :checked. Elle fonctionne donc sans JavaScript et sans attendre
   son chargement — un bloc qui apparaît en retard sous un prix fait sauter la
   page au moment précis où on la lit.
=========================================================================== */
.echelonnement {
  border: 1px solid var(--filet); border-radius: var(--rayon);
  padding:14px 16px; margin:16px 0;
  background: var(--surface);
  /* L'ENCADRÉ NE PREND QUE LA PLACE DE SON CONTENU. Il s'étirait sur toute la
     colonne d'achat pour trois lignes courtes — un montant, une mention, un
     lien — et le vide à droite le faisait paraître incomplet, comme un tableau
     auquel il manquerait une colonne. La règle est posée sur le bloc et vaut
     donc pour TOUS les organismes, présents et à venir. */
  width: fit-content; max-width: 100%;
}
.echelonnement-organisme + .echelonnement-organisme {
  margin-top:14px; padding-top:14px; border-top: 1px solid var(--filet);
}
.echelonnement-entete {
  display: flex; align-items: center; justify-content: space-between;
  gap:12px; flex-wrap: wrap; margin-bottom:9px;
}
.echelonnement-nom { font-weight: 600; font-size: 14px; }

.echelonnement-onglets { display: flex; gap:5px; }
.echelonnement-radio { position: absolute; opacity: 0; width: 0; height: 0; }
.echelonnement-onglet {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 38px; padding:5px 10px;
  border: 1px solid var(--filet); border-radius: 8px;
  font-size: 13px; font-weight: 600; color: var(--encre-tenue);
  cursor: pointer; transition: background .13s, color .13s, border-color .13s;
}
.echelonnement-onglet:hover { border-color: var(--filet-fort); color: var(--encre); }

/* DEUX MÉCANISMES DISTINCTS, chacun pour ce qu'il sait faire.
   L'onglet coché : `input:checked + label`, le motif le plus simple, qui
   marche parce que chaque radio est collé à son libellé.
   Le panneau : `:has()` posé sur le conteneur, qui affranchit de la position
   des radios dans l'arbre. La tentative précédente pilotait les deux depuis
   `:nth-of-type` sur les radios, et a produit deux défauts d'affilée — rien
   d'affiché au chargement, puis un surlignage figé sur le dernier onglet.
   Les règles vont jusqu'à six échéanciers ; au-delà, le premier reste affiché,
   ce qui est inerte plutôt que cassé. */
/* L'ONGLET ACTIF PASSE PAR :has(), COMME LES PANNEAUX. Le motif classique
   `input:checked + label` était pourtant en place et correct — le navigateur
   confirmait que le sélecteur ciblait le bon libellé — sans que le style suive.
   Plutôt que d'insister sur un mécanisme qui se comportait de façon
   inexplicable ici, on emploie celui qui est vérifié à l'écran juste à côté :
   un seul mécanisme pour les deux moitiés du composant se relit aussi mieux. */
.echelonnement-organisme:has(.echelonnement-radio[value="0"]:checked) .echelonnement-onglet[data-rang="0"],
.echelonnement-organisme:has(.echelonnement-radio[value="1"]:checked) .echelonnement-onglet[data-rang="1"],
.echelonnement-organisme:has(.echelonnement-radio[value="2"]:checked) .echelonnement-onglet[data-rang="2"],
.echelonnement-organisme:has(.echelonnement-radio[value="3"]:checked) .echelonnement-onglet[data-rang="3"],
.echelonnement-organisme:has(.echelonnement-radio[value="4"]:checked) .echelonnement-onglet[data-rang="4"],
.echelonnement-organisme:has(.echelonnement-radio[value="5"]:checked) .echelonnement-onglet[data-rang="5"] {
  background: var(--accent); border-color: var(--accent); color: var(--accent-encre);
}
.echelonnement-radio:focus-visible + .echelonnement-onglet {
  outline: 2px solid var(--accent); outline-offset: 2px;
}

.echelonnement-detail { display: none; }
.echelonnement-organisme:has(.echelonnement-radio[value="0"]:checked) .echelonnement-detail[data-rang="0"],
.echelonnement-organisme:has(.echelonnement-radio[value="1"]:checked) .echelonnement-detail[data-rang="1"],
.echelonnement-organisme:has(.echelonnement-radio[value="2"]:checked) .echelonnement-detail[data-rang="2"],
.echelonnement-organisme:has(.echelonnement-radio[value="3"]:checked) .echelonnement-detail[data-rang="3"],
.echelonnement-organisme:has(.echelonnement-radio[value="4"]:checked) .echelonnement-detail[data-rang="4"],
.echelonnement-organisme:has(.echelonnement-radio[value="5"]:checked) .echelonnement-detail[data-rang="5"] {
  display: block;
}
/* Repli pour un navigateur sans :has() — le premier échéancier reste lisible
   plutôt que de laisser un bloc vide sous le prix. */
@supports not selector(:has(*)) {
  .echelonnement-detail[data-rang="0"] { display: block; }
}

.echelonnement-montants { margin:0; font-size: 15px; line-height: 1.4; }
.echelonnement-montants strong { font-size: 19px; color: var(--accent-texte); }
.echelonnement-montants span { color: var(--encre-tenue); }
.echelonnement-frais { margin:3px 0 0; font-size: 12.5px; color: var(--encre-tenue); }
/* LE LIEN D'INFORMATION PASSE À LA LIGNE, ET CE N'EST PAS QUE DE LA MISE EN
   PAGE. « Offre soumise à conditions. Plus d'informations » sur une seule ligne
   était la ligne la plus longue de l'encadré : c'est donc ELLE qui en fixait la
   largeur, pour une mention légale que personne ne lit avant d'avoir décidé.
   En la coupant, l'encadré se règle sur le montant — l'information qu'on vient
   y chercher. */
.echelonnement-mention a { display: block; margin-top: 3px; }
/* Un encadré de paiement échelonné n'a pas à faire la largeur d'une colonne
   d'achat : il annonce trois chiffres. */
.echelonnement { max-width: 22rem; }
.echelonnement-mention {
  margin:9px 0 0; font-size: 11.5px; color: var(--encre-tenue); line-height: 1.45;
}
.echelonnement-mention a { color: inherit; text-decoration: underline; }

/* --- Code de réduction ---
   Le formulaire est REPLIÉ par défaut. Un champ « code promo » affiché en grand
   dans un tunnel envoie le client en chercher un ailleurs, et il ne revient pas
   toujours : il doit rester accessible sans réclamer l'attention. */
.coupon { margin-top:16px; padding-top:14px; border-top: 1px solid var(--filet); }
.coupon-repli > summary {
  cursor: pointer; font-size: 13.5px; color: var(--encre-tenue);
  list-style: none; display: inline-flex; align-items: center; gap:6px;
}
.coupon-repli > summary::-webkit-details-marker { display: none; }
.coupon-repli > summary::before { content: '+'; font-weight: 700; }
.coupon-repli[open] > summary::before { content: '−'; }
.coupon-repli > summary:hover { color: var(--encre); }
.coupon-saisie { display: flex; gap:8px; margin-top:10px; }
.coupon-saisie input {
  flex: 1; min-width: 0; padding:9px 11px; text-transform: uppercase;
  border: 1px solid var(--filet); border-radius: 9px;
  background: var(--surface); color: var(--encre); font-family: inherit; font-size: 14px;
}
.coupon-saisie .bouton { white-space: nowrap; }
.coupon-refus { margin:0 0 8px; font-size: 13px; color: var(--alerte); }

.coupon-actif { display: flex; align-items: center; gap:10px; flex-wrap: wrap; }
.coupon-badge {
  display: inline-flex; align-items: center; padding:4px 10px;
  border-radius: 7px; border: 1px dashed var(--succes);
  background: color-mix(in srgb, var(--succes) 10%, transparent);
  color: var(--succes); font-weight: 700; font-size: 13px; letter-spacing: .04em;
}
.coupon-effet { flex: 1; min-width: 0; font-size: 13.5px; }
.coupon-effet small { display: block; color: var(--encre-tenue); font-size: 12.5px; }
.coupon-retirer { margin:0; }

/* La ligne de remise, en vert : c'est la seule du récapitulatif qui soustrait,
   et elle doit se distinguer sans qu'on ait à lire le signe. */
.recap-remise span:last-child { color: var(--succes); font-weight: 600; }

/* ===========================================================================
   FOIRE AUX QUESTIONS
   La page n'avait AUCUNE règle : ni .faq, ni .faq-theme, ni .faq-question. Elle
   s'affichait donc avec les triangles bruts de <details>, sans grille et sans
   marge — d'où « mal intégrée ». Cinquième page du projet dans ce cas, après
   contact, pages légales, espace client et réassurance.
=========================================================================== */
.faq-grille {
  display: grid; gap:16px;
  grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
  margin-bottom:48px;
}
.faq-carte {
  display: flex; flex-direction: column; gap:8px;
  padding:20px 20px 18px;
  border: 1px solid var(--filet); border-radius: var(--rayon-large);
  background: var(--surface); text-decoration: none; color: inherit;
  transition: border-color .15s ease, transform .15s ease;
}
.faq-carte:hover { border-color: var(--filet-fort); transform: translateY(-2px); }
.faq-carte-icone {
  display: inline-flex; align-items: center; justify-content: center;
  width: 42px; height: 42px; border-radius: 11px;
  background: var(--fond-doux); color: var(--accent-texte); margin-bottom:4px;
}
.faq-carte-titre { font-size: 16px; font-weight: 600; line-height: 1.3; }
.faq-carte-intro { font-size: 13.5px; color: var(--encre-tenue); line-height: 1.5; }
/* Le pied est poussé en bas : sans cela, des cartes d'introductions inégales
   alignent leurs compteurs à des hauteurs différentes, ce qui se voit
   immédiatement sur une grille. */
.faq-carte-pied {
  display: flex; align-items: center; justify-content: space-between;
  margin-top:auto; padding-top:12px;
  font-size: 12.5px; color: var(--encre-tenue);
}

.faq-liste { margin-bottom:48px; }
.faq-liste > h2 { font-size: 18px; margin:0 0 16px; }
/* ⚠️ LA LISTE NE SE CENTRE PAS, ELLE S'ALIGNE (18/09/2026).
   `.mesure-texte` borne la lecture à 72 caractères ET centre la colonne — ce
   qui convient à une page de texte pur, où le titre suit le même centrage
   (règle `.contenu:has(> .prose.mesure-texte)`). Une page de FAQ, elle, n'est
   pas que du texte : son titre, son introduction, ses autres thèmes et sa bande
   de contact sont calés au bord gauche. La liste centrée était donc le SEUL
   bloc décalé de la page, de trois cents pixels, et l'ensemble paraissait
   désassemblé. On garde la mesure, on rend l'alignement. */
.faq-liste.mesure-texte { margin-inline: 0; }

/* La question : le marqueur natif est remplacé par un chevron qui pivote. Le
   triangle par défaut n'a ni la taille ni la couleur du reste, et c'est lui
   qu'on voyait sur la page nue. */
/* UNE QUESTION EST UNE COMMANDE, PAS UNE PUCE (18/09/2026, « il faut améliorer
   les FAQ déroulantes »). Le chevron ouvrait la ligne, collé au premier mot :
   à l'œil il se lisait comme une puce de liste, et rien ne disait que la ligne
   entière était cliquable. Trois corrections, toutes déjà éprouvées sur les
   volets « Avant de commander » de la fiche produit :
   le chevron part À DROITE, là où l'on cherche un déplié ; la bande de survol
   VA D'UN BORD À L'AUTRE de la colonne (marges négatives annulées par le
   rembourrage) au lieu de s'arrêter au mot ; et un filet ferme la liste par le
   HAUT, sans quoi un thème à une seule question n'affichait aucun trait et sa
   question flottait seule au milieu du blanc. */
.faq-question {
  border-bottom: 1px solid var(--filet);
}
.faq-question:first-of-type { border-top: 1px solid var(--filet); }
/* LA DERNIÈRE QUESTION NE PORTE PAS DE FILET. Il fermait la liste par un trait
   suspendu, sans rien en dessous à séparer — le même trait de trop que celui
   retiré en haut du bloc. */
.faq-question:last-of-type { border-bottom: 0; }
.faq-question > summary {
  display: flex; align-items: flex-start; gap:14px;
  margin:0 calc(-1 * var(--faq-debord, 16px)); padding:18px var(--faq-debord, 16px);
  border-radius: var(--rayon);
  cursor: pointer;
  font-size: 16px; font-weight: 550; line-height: 1.45;
  list-style: none;
  transition: color .15s ease, background-color .15s ease;
}
.faq-question > summary:hover { background: color-mix(in srgb, var(--accent) 6%, transparent); }
/* L'ANNEAU EST SUR LE SOMMAIRE, PAS SUR LE <details>. Au clavier c'est le
   sommaire qui reçoit le focus ; l'anneau par défaut du navigateur épousait la
   ligne sans ses marges négatives et débordait du filet. */
.faq-question > summary:focus-visible {
  outline: 2px solid var(--accent-texte); outline-offset: -2px;
  background: color-mix(in srgb, var(--accent) 6%, transparent);
}
/* La question ouverte se distingue de celles qui ne le sont pas : sans cela,
   dans une liste de dix, on perd de vue laquelle on vient de déplier. */
.faq-question[open] > summary { color: var(--accent-texte); }
.faq-question > summary::-webkit-details-marker { display: none; }
.faq-question > summary::before {
  content: ''; flex: 0 0 9px; width: 9px; height: 9px; margin-top:7px;
  border-right: 1.5px solid var(--encre-tenue); border-bottom: 1.5px solid var(--encre-tenue);
  /* Le chevron pivote AUTOUR DE SON CENTRE. Sans origine explicite il tourne
     autour du coin haut-gauche et se déplace en même temps qu'il tourne, ce qui
     donne un petit sursaut à chaque ouverture. */
  transform-origin: center;
  transform: rotate(-45deg); transition: transform .18s ease, border-color .15s ease;
  /* À DROITE, ET POUSSÉ PAR LA MARGE AUTOMATIQUE. `order` seul ne suffit pas :
     sans `margin-left: auto`, le chevron se range contre le dernier mot de la
     question au lieu de tenir le bord de la colonne, et les chevrons d'une
     liste ne s'alignent plus entre eux. */
  order: 3; margin-left: auto;
}
.faq-question > summary:hover::before,
.faq-question[open] > summary::before { border-color: var(--accent-texte); }
.faq-question[open] > summary::before { transform: rotate(45deg); }
.faq-question > summary:hover { color: var(--accent-texte); }
.faq-reponse {
  /* PLUS D'INDENTATION depuis que le chevron est passé à droite (18/09/2026) :
     les 23 px rendaient la question au chevron qui l'ouvrait, il n'y a plus
     rien à sa gauche à contourner. La réponse commence donc exactement sous le
     mot qu'elle prolonge. */
  padding:2px 0 22px;
  color: var(--encre-douce); line-height: 1.7; font-size: 15.5px;
  max-width: 68ch;
}
.faq-reponse > *:first-child { margin-top:0; }
.faq-reponse > *:last-child { margin-bottom:0; }

.faq-autres { margin-bottom:32px; }
.faq-autres h2 { font-size: 15px; margin:0 0 12px; }
.faq-autres-liens { display: flex; flex-wrap: wrap; gap:8px; }
.faq-autres-liens a {
  display: inline-flex; align-items: center; gap:7px;
  padding:8px 14px; border: 1px solid var(--filet); border-radius: 999px;
  font-size: 13.5px; text-decoration: none; color: var(--encre);
  transition: border-color .15s, background .15s;
}
.faq-autres-liens a:hover { border-color: var(--filet-fort); background: var(--fond-doux); }
.faq-autres-liens a span {
  font-size: 12px; color: var(--encre-tenue); font-variant-numeric: tabular-nums;
}
.faq-contact {
  padding:18px 20px; margin-bottom:64px;
  background: var(--fond-doux); border-radius: var(--rayon);
  font-size: 14.5px;
}

/* ===========================================================================
   MARQUES
=========================================================================== */
.bloc-marques { padding:40px 0 48px; }
.bloc-marques h2 { margin:0 0 20px; }
/* LE BANDEAU DÉFILE, ET SES BORDS S'ESTOMPENT. Le fondu est un masque, pas un
   dégradé blanc posé par-dessus : un calque opaque suppose de connaître la
   couleur du fond, et se verrait comme un rectangle dès que le thème sombre est
   choisi ou qu'une section change de fond. Le masque, lui, rend le contenu
   transparent — il marche sur n'importe quel fond, y compris une image.

   Sans ce fondu, un logo apparaît et disparaît net au bord de l'écran, ce qui
   fait ressembler la bande à un contenu tronqué plutôt qu'en mouvement. */
/* LA MARGE VERTICALE EST INDISPENSABLE. Le bandeau masque ce qui déborde pour
   cacher la piste qui défile ; au survol, une carte se soulève de deux pixels et
   son bord supérieur passait sous la coupe — on voyait un filet tronqué, ce qui
   ressemblait à un défaut d'affichage. Six pixels de marge haute et basse
   laissent la place au mouvement sans rien montrer de la piste. */
.marques-bandeau {
  overflow: hidden;
  padding:6px 0;
  -webkit-mask-image: linear-gradient(to right, transparent, #000 9%, #000 91%, transparent);
          mask-image: linear-gradient(to right, transparent, #000 9%, #000 91%, transparent);
}
.marques-piste { display: flex; width: max-content; }
.marques-groupe { display: flex; align-items: center; gap:14px; padding-right:14px; }

/* Trop peu de marques pour tourner : la bande redevient une grille, et chaque
   logo garde une case de même largeur — sans quoi un logo large écrase
   optiquement ses voisins. */
.marques-bandeau:not(.defile) .marques-piste { width: 100%; }
.marques-bandeau:not(.defile) .marques-groupe {
  display: grid; gap:14px; width: 100%; padding-right:0;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
}

/* On avance d'exactement la largeur d'un groupe : `-50 %` de la piste revient
   au même tant qu'il y a deux groupes, mais se casse au premier changement. */
.marques-bandeau.defile .marques-piste {
  animation: marques-defile 38s linear infinite;
}
@keyframes marques-defile {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}
/* Au survol on s'arrête : c'est le seul moyen de viser un logo sans qu'il se
   dérobe. */
.marques-bandeau.defile:hover .marques-piste,
.marques-bandeau.defile:focus-within .marques-piste { animation-play-state: paused; }
/* La bande se TIRE aussi (règle générale du 16/08/2026) : le curseur
   l'annonce, et pendant le tirage on coupe sélection et pointer-events des
   logos — mêmes précautions que la piste de carrousel. */
.marques-bandeau.defile { cursor: grab; }
.marques-bandeau.defile.est-tiree { cursor: grabbing; }
.marques-bandeau.defile.est-tiree * { user-select: none; pointer-events: none; }

.marque-logo {
  display: flex; align-items: center; justify-content: center;
  flex: none;
  height: 72px; padding:14px 20px;
  border: 1px solid var(--filet); border-radius: var(--rayon);
  background: var(--surface);
  transition: border-color .15s ease, transform .15s ease;
}
.marques-bandeau:not(.defile) .marque-logo { flex: initial; }
.marque-logo:hover { border-color: var(--filet-fort); transform: translateY(-2px); }
.marque-logo img {
  /* Hauteur absolue : un pourcentage dans un conteneur qui se dimensionne sur
     son contenu se résout contre zéro, et le logo disparaît sans rien dire. */
  height: 30px; width: auto; max-width: 168px;
  object-fit: contain; display: block;
}

/* Un visiteur qui a demandé moins d'animations ne subit pas la seule qui tourne
   en permanence : la bande devient une liste qu'on fait glisser au doigt, et la
   copie disparaît pour ne pas se lire deux fois. */
@media (prefers-reduced-motion: reduce) {
  .marques-bandeau.defile { -webkit-mask-image: none; mask-image: none; }
  .marques-bandeau.defile .marques-piste {
    animation: none; width: 100%; overflow-x: auto; scrollbar-width: none;
  }
  .marques-bandeau.defile .marques-groupe[aria-hidden="true"] { display: none; }
}
.marque-logo:hover img { filter: none; opacity: 1; }

.marque-entete { display: flex; align-items: center; gap:18px; flex-wrap: wrap; }
.marque-entete h1 { margin:0; }
.marque-entete-logo {
  display: flex; align-items: center; justify-content: center;
  width: 88px; height: 88px; padding:12px; flex: 0 0 auto;
  border: 1px solid var(--filet); border-radius: var(--rayon);
  background: var(--surface);
}
.marque-entete-logo img { max-width: 100%; max-height: 100%; object-fit: contain; }
.marque-description { max-width: 68ch; margin-top:14px; }

/* ===========================================================================
   ÉCRANS DE COMPTE — connexion, inscription, mot de passe oublié
   Aucune de leurs classes n'avait de règle : elles venaient de la feuille du
   site vitrine, restée en arrière. Sixième page du projet dans ce cas.
=========================================================================== */
/* L'ÉCRAN ENTIER PARTAGÉ EN DEUX, et non une carte posée sur une page.
   Elle a d'abord été une carte de 980 px, puis une carte à la laisse du site
   (1180 px). Les deux versions avaient le même défaut, seulement moins marqué
   à chaque fois : sur un écran large il restait du vide autour, et le
   formulaire de connexion — le seul contenu de la page — avait l'air d'un
   encart posé sur autre chose.

   Elle occupe désormais la largeur et la hauteur de l'écran, coupée par le
   milieu : le panneau de marque à gauche, le formulaire à droite. Il n'y a plus
   ni bordure ni rayon, parce qu'il n'y a plus de carte — un bord arrondi au ras
   de la fenêtre se lit comme un défaut d'affichage.

   `main` occupe déjà toute la largeur : il suffit de retirer le `max-width`.
   Pas besoin de la ruse `50% - 50vw` du bandeau d'accueil, qui ne sert qu'aux
   blocs enfermés dans la colonne de contenu.

   La hauteur retranche `--collant`, posé par shop.js et qui vaut la hauteur de
   l'en-tête : sans lui, le partage dépasserait d'un en-tête sous la ligne de
   flottaison et l'on verrait une bande de pied de page au chargement. `svh` et
   non `vh` — sur mobile, `vh` compte la barre d'adresse rétractée, donc trop
   haut au premier rendu. */
.auth {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap:0;
  max-width: none;
  margin:0;
  border: 0;
  border-radius: 0;
  min-height: calc(100svh - var(--collant, 106px));
  background: var(--surface);
}
/* Sans panneau : une seule colonne, resserrée à la largeur de lecture d'un
   formulaire. C'est le cas de l'inscription, dont le formulaire fait une
   quinzaine de champs. Étalée sur toute la largeur, une suite de champs devient
   une ligne d'horizon que l'œil doit balayer de gauche à droite à chaque
   saisie — et le plein écran, ici, desservirait.
   Elle reprend donc la carte : bordure, rayon, marges. */
.auth--simple {
  grid-template-columns: 1fr;
  max-width: 620px;
  margin:40px auto 64px;
  min-height: 0;
  border: 1px solid var(--filet);
  border-radius: var(--rayon-large);
  overflow: hidden;
}
@media (max-width: 860px) {
  /* Le panneau passe AU-DESSUS et se réduit à son texte : le masquer ferait
     perdre la seule phrase qui dit à quoi sert le compte, et le garder à sa
     hauteur repousserait le formulaire sous la ligne de flottaison. Le partage
     en deux n'a plus de sens à cette largeur, et la hauteur imposée non plus —
     empilées, les deux moitiés feraient deux écrans. */
  .auth { grid-template-columns: 1fr; min-height: 0; }
  .auth--simple { margin:20px auto 48px; }
}

.auth-ouverture {
  position: relative; display: flex; align-items: center;
  /* Le rembourrage suit la largeur : à 36 px sur une demi-fenêtre de 720, le
     texte se colle au bord de l'écran. */
  padding:48px clamp(24px, 5vw, 64px);
}
.auth-ouverture-fond {
  position: absolute; inset: 0;
  /* Le dégradé part de l'accent et s'assombrit : deux teintes de la même
     famille plutôt que deux couleurs, sinon le panneau devient une image et
     non un fond. */
  /* LE REFLET NE MONTE PLUS QU'À 8 % DE BLANC, contre 35 auparavant, et c'est
     une mesure et non un goût : à 65 % d'accent le point le plus clair du
     dégradé donnait 2,86 de contraste pour le texte blanc posé dessus — sous
     le seuil de 3 réservé au GRAND texte, alors que le chapô fait 15 px et la
     liste 14, qui en demandent 4,5. Le reflet était donc joli exactement là où
     le texte devenait illisible.
     À 92 %, le point le plus clair remonte à 4,76, et le dégradé garde son
     modelé — c'est la même famille de teintes, simplement moins délavée. */
  background:
    radial-gradient(120% 90% at 15% 10%, color-mix(in srgb, var(--accent) 92%, #ffffff) 0%, transparent 60%),
    linear-gradient(150deg, var(--accent) 0%, color-mix(in srgb, var(--accent) 55%, #101014) 100%);
}
/* LES DEUX MOITIÉS COMMENCENT À LA MÊME HAUTEUR.
   Le panneau centrait son texte verticalement pendant que le formulaire
   partait du haut : les deux blocs de texte démarraient à trois cents pixels
   d'écart, sur un écran coupé en deux parts égales. L'œil lit ça comme un
   défaut d'alignement, parce que c'en est un — deux colonnes côte à côte
   partagent leur ligne de départ.

   Elles descendent aussi d'un cran. Collé sous l'en-tête, « Connexion » se
   confondait avec le bandeau d'annonces ; à quatre-vingts pixels, le bloc
   respire sans tomber sous la ligne de flottaison.

   La règle exclut `.auth--simple` (l'inscription), qui n'a pas de panneau et
   reste une carte : quatre-vingts pixels au-dessus d'un formulaire de quinze
   champs le pousseraient hors de l'écran. */
.auth:not(.auth--simple) .auth-ouverture,
.auth:not(.auth--simple) .auth-carte {
  padding-top: 64px;
  padding-bottom: 64px;
}
/* LES DEUX BLOCS SONT CENTRÉS SUR LE MÊME AXE, et c'est la seule façon de les
   aligner vraiment. Les caler tous les deux en haut ne suffisait pas : le
   panneau est aussi haut que l'écran, le formulaire fait la moitié, et deux
   blocs de hauteurs différentes partant de la même ligne ne se répondent pas.

   Aucune règle ne sait « centrer A sur le contenu de B ». Mais les deux
   colonnes ont la même hauteur — elles partagent une ligne de grille — donc
   centrer chacune DANS SA COLONNE leur donne le même milieu. Le résultat est
   celui demandé, obtenu par une propriété que le navigateur sait tenir quand
   le contenu change. */
.auth:not(.auth--simple) .auth-ouverture {
  align-items: center;
  justify-content: center;
}
.auth:not(.auth--simple) .auth-carte {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}
/* LE FORMULAIRE EST BORNÉ ET CENTRÉ DANS SA MOITIÉ, il ne l'occupe pas.
   Étalé sur toute la demi-fenêtre, un champ de saisie mesurait sept cents
   pixels pour recevoir une adresse e-mail : le curseur part à gauche, le
   texte s'arrête au tiers, et l'œil doit traverser un champ vide à chaque
   ligne. La largeur d'un champ est une promesse sur ce qu'on y écrit — un
   champ très large annonce une saisie longue, et ment.

   `width: 100%` reste nécessaire malgré le `max-width` : en flex, un enfant
   se réduit à son contenu, et le formulaire se serait rétréci à la largeur
   de son plus long libellé. */
.auth:not(.auth--simple) .auth-carte > * { width: 100%; max-width: 400px; }

/* Le panneau, centré horizontalement lui aussi. Sa mesure est bornée : étalé
   sur une demi-fenêtre de 900 px, un texte centré devient illisible — l'œil ne
   retrouve pas le début de la ligne suivante. */
.auth:not(.auth--simple) .auth-ouverture-texte {
  text-align: center;
  max-width: 420px;
}
/* La liste, elle, reste alignée à gauche À L'INTÉRIEUR d'un bloc centré : des
   puces centrées font commencer chaque ligne à une abscisse différente, ce qui
   est exactement ce qu'une liste sert à éviter. */
.auth:not(.auth--simple) .auth-ouverture-points {
  display: inline-grid;
  text-align: left;
  justify-self: center;
}

/* LE PARTAGE DESCEND JUSQU'AU PIED DE PAGE.
   `.pied` porte 48 px de marge haute — juste, partout ailleurs, où le contenu
   se termine sur du texte. Ici le panneau de couleur s'arrêtait net et laissait
   une bande blanche avant le pied : on lisait un bloc qui s'arrête trop tôt,
   pas une respiration. La marge est retirée quand la page se termine par le
   partage plein écran, et seulement là. */
main:has(> .auth:not(.auth--simple)) + .pied { margin-top: 0; }

.auth-ouverture-texte { position: relative; color: var(--accent-encre); }
.auth-ouverture-titre { margin:0 0 10px; font-size: 25px; font-weight: 600; line-height: 1.2; }
.auth-ouverture-lead { margin:0; font-size: 15px; line-height: 1.55; opacity: .88; }
.auth-ouverture-points { list-style: none; margin:20px 0 0; padding:0; display: grid; gap:9px; }
.auth-ouverture-points li {
  position: relative; padding-left:24px; font-size: 14px; opacity: .92; line-height: 1.45;
}
.auth-ouverture-points li::before {
  content: ''; position: absolute; left: 3px; top: 5px;
  width: 5px; height: 9px; border: solid currentColor;
  border-width: 0 2px 2px 0; transform: rotate(45deg);
}
@media (max-width: 860px) {
  .auth-ouverture { padding:24px 24px; }
  .auth-ouverture-titre { font-size: 21px; }
  .auth-ouverture-points { display: none; }
}

/* DANS UNE CARTE, LE FORMULAIRE N'A PLUS DE CADRE À LUI.
   `.account-form` porte une bordure, un fond, un rembourrage et une mesure de
   460 px parce qu'il sert aussi SEUL. Posé dans une carte qui l'entoure déjà,
   il donne deux cadres emboîtés — un bloc collé dans un autre, avec un liseré
   à quinze pixels du précédent et un formulaire deux fois plus étroit que la
   carte qui le contient. C'était le cas de la coquille de connexion ; c'est
   aussi celui de Profil et de Support, où la carte s'appelle `.account-card`.
   La règle vaut donc pour les deux, plutôt que d'être recopiée. */
.auth .account-form,
.account-card .account-form {
  max-width: none; margin:0; padding:0;
  border: 0; background: transparent;
}
.auth-carte { padding:40px 32px; }
@media (max-width: 620px) { .auth-carte { padding:24px 20px; } }
.auth-carte-entete { margin-bottom:24px; }
.auth-carte-entete h1 { font-size: 24px; margin:0 0 8px; }
.auth-carte-lead { margin:0; color: var(--encre-tenue); font-size: 14.5px; line-height: 1.55; }

.account-form .form-group { margin-bottom:16px; }
.account-form .form-label {
  display: flex; align-items: baseline; justify-content: space-between; gap:10px;
  margin-bottom:6px; font-size: 13.5px; font-weight: 500;
}
.account-form .form-label-link { font-size: 12.5px; font-weight: 400; color: var(--encre-tenue); }
.account-form .form-input {
  width: 100%; padding:11px 13px;
  border: 1px solid var(--filet); border-radius: 10px;
  background: var(--surface); color: var(--encre);
  font-size: 15px; font-family: inherit;
}
.account-form .form-input:focus-visible { outline: 2px solid var(--accent); outline-offset: -1px; }
.form-checkbox-group { margin:4px 0 18px; }
.form-checkbox { display: flex; align-items: center; gap:9px; font-size: 13.5px; }
.form-secondary-link { margin:18px 0 0; text-align: center; font-size: 14px; color: var(--encre-tenue); }
.form-errors, .form-success {
  padding:11px 14px; border-radius: 10px; margin-bottom:18px; font-size: 14px;
}
.form-errors { background: color-mix(in srgb, var(--alerte) 10%, transparent); color: var(--alerte); }
.form-success { background: color-mix(in srgb, var(--succes) 12%, transparent); color: var(--succes); }
.form-errors p, .form-success p { margin:0; }

/* --- Badges de paiement en fichier ---
   Les logos déposés (Stripe, PayPal) sont servis en SVG plutôt que redessinés :
   leur graisse et leur chasse font partie de la marque, et un client les
   reconnaît avant de les lire. La case est de hauteur fixe et le logo s'y
   inscrit sans se déformer — les proportions varient beaucoup d'une marque à
   l'autre. */
/* ⚠️ SUPPRIMÉ LE 26/08/2026 — `.payment-icon-marque`, `.payment-icon-stripe`
   et `.payment-icon-paypal` n'existent plus dans le balisage. Depuis que les
   cinq badges sont des cartes complètes en fichier (voir payment-badges.php),
   il n'y a plus de « badge de marque » à distinguer d'un badge dessiné : ils
   sont tous de la même nature, et une seule paire de règles les habille, bien
   plus haut dans cette feuille.

   CE QUI DISPARAÎT AVEC ELLES, ET C'EST TANT MIEUX : le piège de cascade
   documenté ici le 24/08/2026, où `.payment-icon-stripe img` posée plus haut
   dans le fichier perdait contre `.payment-icon-marque img` à spécificité
   égale, et rendait 18px/40px au lieu des 14px/34px écrits. Deux réglages de
   taille pour un même logo, arbitrés par l'ordre des lignes : c'est
   exactement le genre de dette que les cartes en fichier suppriment. */

/* Le logo de l'organisme, dans le simulateur. Hauteur bornée et largeur libre :
   Klarna est un bandeau très large, PayPal un monogramme presque carré, Alma un
   carré plein. Une largeur fixe écraserait l'un ou étirerait l'autre. */
.echelonnement-logo {
  display: inline-flex; align-items: center;
}
/* UNE BOÎTE IDENTIQUE POUR CHAQUE ORGANISME, et l'image s'y inscrit sans se
   déformer. Sans elle, chaque logo garde ses proportions d'origine : Alma est
   trois fois et demie plus large que haut, Klarna deux fois et demie, et à
   hauteur égale Alma paraissait écraser Klarna. À 60 × 26 les deux sont bornés
   par la largeur, donc rendus exactement à la même largeur ; le cadrage à
   gauche aligne les bords sur toute la colonne.

   Bornes EN PIXELS, jamais en pourcentage : un `max-width: 100 %` sur un enfant
   flex dont le conteneur se dimensionne sur son contenu se résout contre zéro,
   et le logo disparaît sans rien signaler — ni erreur, ni 404. */
.echelonnement-logo img {
  width: 60px; height: 26px;
  object-fit: contain; object-position: left center;
  display: block;
}

/* ==========================================================================
   RATTRAPAGE DES CLASSES SANS RÈGLE — écrans de compte, contact, 404
   Un audit de tout le front (src/Pages et src/Templates confrontés à cette
   feuille) a trouvé 138 classes utilisées dans le balisage sans une seule règle
   en face. Les deux tiers venaient de gabarits hérités du site vitrine qui ne
   sont plus inclus nulle part ; le reste habillait des écrans bien vivants —
   support client, profil, fil de discussion — qui s'affichaient donc en HTML
   nu, sans que rien ne le signale.
   ========================================================================== */

/* ------------------------------------------------------- variantes de bouton
   `.btn` et `.btn-primary` existaient ; les quatre variantes utilisées à côté
   n'avaient rien. Un bouton « Supprimer mon compte » qui ressemble trait pour
   trait à « Enregistrer » n'est pas un défaut d'esthétique : c'est une action
   irréversible qui ne s'annonce pas. */
.btn-secondary { background: var(--fond-doux); color: var(--encre); border-color: var(--filet-fort); }
.btn-secondary:hover { background: var(--surface); border-color: var(--encre-tenue); }
.btn-danger { background: transparent; color: var(--alerte); border-color: var(--alerte); }
.btn-danger:hover { background: var(--alerte); color: var(--surface); }
.btn-sm { padding:7px 13px; font-size: 14px; }
/* La flèche avance au survol : le mouvement dit « la suite est par là » mieux
   qu'un changement de couleur, et il ne coûte rien à qui ne le remarque pas. */
.btn-arrow { display: inline-block; transition: transform .16s ease; }
.btn:hover .btn-arrow { transform: translateX(3px); }

/* ------------------------------------------------------------ espace client */
/* Le rembourrage de cette section était écrit dans l'attribut `style` de
   CHAQUE gabarit de compte — 88/56 pour le titre, 48/96 pour le corps. Quatre
   fichiers portaient les mêmes nombres, hors de portée de l'échelle
   d'espacement : c'est ainsi qu'on se retrouve avec deux cents pixels entre un
   titre et son formulaire pendant que le reste du site en a quarante. */
.account-section { background: var(--fond); padding: 0 0 64px; }
/* La zone dangereuse se distingue par son FILET, pas par un aplat rouge : un
   bloc rouge dans une page de profil alarme en permanence, alors que le danger
   n'existe qu'au moment du clic. */
.account-card-danger { border-color: color-mix(in srgb, var(--alerte) 45%, var(--filet)); }
.account-card-danger h2 { color: var(--alerte); }

.account-tickets-list { list-style: none; margin:0; padding:0; display: grid; gap:12px; }
.account-ticket-item {
  border: 1px solid var(--filet); border-radius: var(--rayon);
  padding:15px 17px; background: var(--fond-doux);
}
.account-ticket-header {
  display: flex; align-items: baseline; justify-content: space-between;
  gap:12px; flex-wrap: wrap; margin-bottom:3px;
}
.account-ticket-meta { font-size: 12.5px; color: var(--encre-tenue); margin-bottom:9px; }
.account-ticket-body { font-size: 14.5px; color: var(--encre-douce); }
.account-ticket-body p { margin:0; }
/* La réponse est décalée et bordée à gauche : c'est ce qui la rattache au
   message sans qu'on ait à écrire « réponse de la boutique » deux fois. */
.account-ticket-response {
  margin-top:12px; padding:11px 14px;
  border-left: 3px solid var(--accent);
  background: var(--surface); border-radius: 0 var(--rayon) var(--rayon) 0;
  font-size: 14.5px;
}
.account-ticket-response-header {
  font-size: 12.5px; font-weight: 550; color: var(--accent-texte); margin-bottom:5px;
}

/* --------------------------------------------------- fil d'une conversation */
.breadcrumb { font-size: 13.5px; color: var(--encre-tenue); margin-bottom:14px; }
.breadcrumb a { color: var(--encre-douce); }
.breadcrumb a:hover { color: var(--accent-texte); }
.alert-info {
  border: 1px solid color-mix(in srgb, var(--accent) 35%, var(--filet));
  background: var(--accent-doux); color: var(--encre);
  padding:12px 15px; border-radius: var(--rayon); margin:0 0 18px; font-size: 14px;
}

.ticket-thread { display: grid; gap:14px; margin-bottom:24px; }
.ticket-message {
  border: 1px solid var(--filet); border-radius: var(--rayon-large);
  padding:15px 17px; background: var(--surface);
}
/* LE MESSAGE DU CLIENT EST DÉCALÉ, celui de la boutique reste à gauche. Deux
   bulles identiques obligent à lire l'en-tête pour savoir qui parle ; le
   décalage se voit avant d'être lu. On garde une marge modeste plutôt qu'un
   alignement à droite complet : au-delà, les lignes deviennent trop courtes. */
.ticket-message-user { margin-left:8%; background: var(--fond-doux); }
.ticket-message-header { display: flex; align-items: center; gap:11px; margin-bottom:9px; }
.ticket-message-author { display: flex; align-items: center; gap:11px; }
.ticket-message-avatar {
  width: 34px; height: 34px; flex: none; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: var(--filet); color: var(--encre-douce);
  font-size: 14px; font-weight: 650; text-transform: uppercase;
}
.ticket-message-avatar.is-admin { background: var(--accent); color: var(--accent-encre); }
.ticket-message-avatar svg { width: 17px; height: 17px; }
.ticket-message-name { font-size: 14.5px; font-weight: 550; }
.ticket-message-date { font-size: 12.5px; color: var(--encre-tenue); }
.ticket-message-body { font-size: 15px; line-height: 1.6; }
.ticket-reply-form { border-top: 1px solid var(--filet); padding-top:18px; }

/* ------------------------------------------------------------ formulaires */
.form-grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap:0 16px; }
@media (max-width: 620px) { .form-grid-3 { grid-template-columns: 1fr; } }

/* Le repli d'adresse facultative. Il s'affichait en marqueur natif du
   navigateur — un triangle noir collé au champ suivant, sans marge — ce qui le
   faisait passer pour un défaut d'affichage plutôt que pour un bouton. */
.form-collapse { margin:0 0 18px; }
.form-collapse > summary {
  display: flex; align-items: center; gap:8px;
  padding:11px 14px; cursor: pointer; list-style: none;
  border: 1px dashed var(--filet-fort); border-radius: var(--rayon);
  font-size: 14.5px; color: var(--encre-douce);
}
.form-collapse > summary::-webkit-details-marker { display: none; }
.form-collapse > summary::before {
  content: '+'; font-size: 16px; line-height: 1; color: var(--accent-texte); font-weight: 600;
}
.form-collapse[open] > summary::before { content: '−'; }
.form-collapse > summary:hover { border-color: var(--accent); color: var(--encre); }
.form-collapse[open] > summary { margin-bottom:16px; }

.form-checkbox-label {
  display: flex; align-items: flex-start; gap:9px;
  font-size: 14px; color: var(--encre-douce); line-height: 1.5; cursor: pointer;
}
.form-checkbox-label input { margin-top:3px; flex: none; }

/* --------------------------------------------------------------- divers */
.whatsapp-badge {
  display: inline-flex; align-items: center; gap:8px;
  padding:9px 15px; border-radius: 999px;
  border: 1px solid var(--filet-fort); background: var(--surface);
  color: var(--encre); text-decoration: none; font-size: 14.5px; font-weight: 550;
}
.whatsapp-badge:hover { border-color: var(--succes); color: var(--succes); }
/* L'icône a une taille (28/08/2026, gb : « gros bug ici ») : le SVG du partiel
   icones-reseaux n'a pas d'attributs width/height, il prenait donc toute la
   largeur de la colonne — un pictogramme WhatsApp de 900 px sur la page
   contact. Même taille que sur le bouton flottant. */
.whatsapp-badge svg { width: 18px; height: 18px; flex: none; }

.commande-encart-bloc { padding:14px 0; border-bottom: 1px solid var(--filet); }
.commande-encart-bloc:last-child { border-bottom: 0; padding-bottom:0; }

.sec { padding:64px 0; }
.formulaire-retractation {
  border: 1px solid var(--filet-fort); border-radius: var(--rayon);
  padding:20px 20px; margin:20px 0; background: var(--fond-doux);
  font-size: 15px; line-height: 1.7;
}

.cookie-banner-actions { display: flex; gap:10px; flex-wrap: wrap; align-items: center; }
.cookie-btn-secondary {
  background: transparent; color: inherit;
  border: 1px solid currentColor; border-radius: var(--rayon);
  padding:9px 16px; font: inherit; font-size: 14px; cursor: pointer;
}
.cookie-btn-secondary:hover { background: color-mix(in srgb, currentColor 12%, transparent); }
.cookie-modal-body { display: grid; gap:16px; }

/* La mention compacte de paiement fractionné, sous le prix. Discrète par
   construction : elle informe sans revendiquer la place du prix juste au-dessus. */
.echelonnement-ligne {
  margin:6px 0 0; font-size: 14px; color: var(--encre-douce);
}
.echelonnement-ligne strong { color: var(--encre); font-weight: 600; }
.echelonnement-ligne a { color: var(--accent-texte); margin-left:6px; }

/* Le retrait d'une ligne de panier. Discret au repos, rouge au survol : c'est
   une suppression, elle doit s'annoncer au moment où le curseur l'atteint — mais
   un bouton rouge en permanence sur chaque ligne ferait d'un panier une liste
   d'avertissements. */
.ligne-retirer {
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; padding:0;
  border: 1px solid transparent; border-radius: var(--rayon);
  background: none; color: var(--encre-tenue); cursor: pointer;
  transition: color .14s ease, border-color .14s ease, background-color .14s ease;
}
.ligne-retirer:hover {
  color: var(--alerte); border-color: var(--filet); background: var(--fond-doux);
}
.ligne-retirer svg { display: block; }

/* ------------------------------------------------------ rubrique vide ---
   Un état vide doit proposer une suite, pas constater un échec. L'ancien se
   réduisait à un titre et un lien au milieu d'une page blanche : le visiteur
   n'avait qu'une porte, celle de la sortie. */
.vide-icone {
  display: inline-flex; align-items: center; justify-content: center;
  width: 62px; height: 62px; margin-bottom:18px;
  border-radius: 50%; background: var(--fond-doux); color: var(--encre-tenue);
}
.vide h2 { margin:0 0 8px; font-size: 21px; }
.vide-texte {
  margin:0 auto 20px; max-width: 46ch;
  color: var(--encre-douce); font-size: 15px; line-height: 1.6;
}
.vide-suggestions { margin-top:40px; text-align: left; }
.vide-suggestions h3 {
  font-size: 12px; font-weight: 650; letter-spacing: .05em; text-transform: uppercase;
  color: var(--encre-tenue); margin:0 0 12px; text-align: center;
}
.vide-suggestions ul {
  list-style: none; margin:0 auto; padding:0; max-width: 560px;
  display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap:10px;
}
.vide-suggestions a {
  display: flex; align-items: baseline; justify-content: space-between; gap:10px;
  padding:13px 16px; text-decoration: none;
  border: 1px solid var(--filet); border-radius: var(--rayon);
  background: var(--surface); color: var(--encre); font-size: 15px;
  transition: border-color .14s ease, transform .14s ease;
}
.vide-suggestions a:hover { border-color: var(--accent); transform: translateY(-2px); }
.vide-suggestions small { color: var(--encre-tenue); font-size: 12.5px; white-space: nowrap; }

/* -------------------------------------------- texte de fond repliable ---
   Ces textes sont longs par vocation : ils s'adressent autant aux moteurs
   qu'aux lecteurs. Ils sont donc ENTIÈREMENT dans la page — un contenu chargé
   au clic ne s'indexe pas de la même façon — mais écrêtés à une hauteur qui
   n'enterre pas le pied de page.

   Le repli est en CSS pur : une case masquée et son étiquette. Sans JavaScript
   le texte s'ouvre quand même. */
.texte-deplie { position: relative; }
.texte-deplie-corps {
  /* 520 px ET NON 268 (18/08/2026, demande de Jesse : « plus longues avant
     d'être floutées, équilibre avec une FAQ typique »).

     LA MESURE VIENT DE LA COLONNE D'À CÔTÉ. Le bas de rubrique et de page de
     facette est en deux colonnes : le texte à gauche, la FAQ à droite. Une FAQ
     de neuf questions repliées mesure un peu plus de cinq cents pixels ; à
     268 px, la colonne de gauche s'arrêtait donc à mi-hauteur de sa voisine, et
     le bloc entier paraissait bancal — on lisait deux paragraphes et demi avant
     de tomber sur un bouton, à côté d'une liste qui, elle, se déroulait.

     Ce n'est pas « plus de texte pour plus de texte » : c'est la hauteur à
     laquelle les deux colonnes se terminent ensemble, ce qui est la seule
     raison d'écrêter à une valeur plutôt qu'à une autre.

     ⚠️ LA HAUTEUR EST MESURÉE, ET 520 PX N'EST PLUS QUE LE REPLI (25/08/2026,
     « le flou du texte SEO doit commencer à la fin de la FAQ »). Une valeur en
     dur ne peut pas tenir cette promesse : la FAQ fait 438 px sur une boutique,
     560 sur une autre, et change dès qu'on ajoute une question. `shop.js` mesure
     la colonne voisine et pose `--hauteur-repli` sur la bande ; sans script, ou
     quand il n'y a pas de seconde colonne, on retombe sur les 520 px d'avant. */
  max-height: var(--hauteur-repli, 520px); overflow: hidden;
  transition: max-height .35s ease;
}
/* ─── LE FONDU ET LE BOUTON DISPARAISSENT QUAND LE TEXTE N'EST PAS COUPÉ.
   Ils étaient rendus dans tous les cas : une description de trois lignes se
   voyait donc voilée sur sa moitié basse et surmontée d'un « Lire la suite » qui
   ne dépliait rien. Le défaut restait discret tant que l'écrêtage était à
   268 px — presque tous les textes le dépassaient — mais à 520 px la plupart des
   pages de facette passent en dessous, et il serait devenu la règle.

   ⚠️ LA CLASSE MARQUE LE CAS RARE, PAS LE CAS COURANT, et l'inverse serait un
   défaut visible. Avec un `est-ecrete` posé sur les textes longs, l'état PAR
   DÉFAUT du HTML serait « déplié » : le temps que le script s'exécute, tout
   visiteur verrait le texte entier s'afficher puis se replier d'un coup. En
   marquant l'exception — `est-entier` sur les textes qui tiennent — le rendu
   initial est déjà le bon, et un navigateur sans script garde exactement le
   comportement d'avant : replié, avec un bouton qui fonctionne en CSS pur. */
.texte-deplie.est-entier .texte-deplie-corps { max-height: none; }
.texte-deplie.est-entier .texte-deplie-corps::after,
.texte-deplie.est-entier .texte-deplie-bouton { display: none; }
/* Le fondu est posé SUR le texte, pas derrière : il masque la dernière ligne
   pour dire qu'elle est coupée. Il s'arrête net quand le texte est déplié,
   sinon il resterait un voile au milieu du paragraphe. */
.texte-deplie-corps::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: 44px; height: 92px;
  background: linear-gradient(to bottom, transparent, var(--fond));
  pointer-events: none;
}
.texte-deplie-bouton {
  display: inline-flex; align-items: center; gap:7px;
  margin-top:14px; padding:9px 18px; cursor: pointer;
  border: 1px solid var(--filet-fort); border-radius: 999px;
  background: var(--surface); color: var(--encre);
  font-size: 14px; font-weight: 550;
}
.texte-deplie-bouton:hover { border-color: var(--accent); color: var(--accent-texte); }
.texte-deplie-bouton::after {
  content: ''; width: 7px; height: 7px;
  border: solid currentColor; border-width: 0 1.6px 1.6px 0;
  transform: translateY(-2px) rotate(45deg);
}
.texte-deplie-moins { display: none; }

.texte-deplie-bascule:checked ~ .texte-deplie .texte-deplie-corps { max-height: 4000px; }
.texte-deplie-bascule:checked ~ .texte-deplie .texte-deplie-corps::after { display: none; }
.texte-deplie-bascule:checked ~ .texte-deplie .texte-deplie-plus { display: none; }
.texte-deplie-bascule:checked ~ .texte-deplie .texte-deplie-moins { display: inline; }
.texte-deplie-bascule:checked ~ .texte-deplie .texte-deplie-bouton::after { transform: translateY(1px) rotate(-135deg); }
/* Le contour de focus se reporte sur l'étiquette : la case est masquée, mais
   elle reste atteignable au clavier et doit le montrer. */
.texte-deplie-bascule:focus-visible ~ .texte-deplie .texte-deplie-bouton {
  outline: 2px solid var(--accent); outline-offset: 2px;
}

/* Rubrique sans aucun produit : une seule colonne, le message au centre de la
   page et non au centre de la zone de résultats. */
.rubrique-corps--deserte { display: block; }

/* LA FLÈCHE DES LISTES DÉROULANTES EST DESSINÉE PAR NOUS, PLUS PAR LE
   NAVIGATEUR.

   Ce bloc réservait déjà sa place (`padding-right`) et lui donnait une position
   (`background-position: right 14px`) — mais il n'y avait AUCUNE image de fond,
   et `appearance` n'était nulle part : le chevron affiché restait celui du
   navigateur, posé par lui contre le bord droit. La règle de position ne
   s'appliquait donc à rien, et rien ne le signalait : une déclaration sans
   effet ne produit pas d'erreur. Le résultat se voyait pourtant — le chevron
   touchait presque l'angle arrondi du champ, avec deux fois moins d'air à
   droite qu'à gauche.

   Le dessiner nous-mêmes règle le cadrage ET l'uniformité : Chrome, Firefox et
   Safari ne dessinent pas le même chevron ni au même endroit, si bien que la
   barre de tri n'avait pas la même allure d'un poste à l'autre.

   La teinte est un gris moyen plutôt que `currentColor`, qu'une URL de données
   ne sait pas lire : à 55 % de gris, le chevron tient sur la surface claire
   comme sur la surface sombre du thème de nuit. */
select,
.admin-select,
.rubrique-tri select,
.filtre-bloc select {
  appearance: none; -webkit-appearance: none; -moz-appearance: none;
  padding-right: 36px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8' fill='none' stroke='%238C8C93' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1 1.5 6 6.5 11 1.5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 13px center;
  background-size: 11px 8px;
}
/* `appearance: none` efface aussi la flèche que le navigateur ajoutait aux
   listes MULTIPLES et à celles qui affichent plusieurs lignes — elles n'en ont
   pas, et l'image y serait posée sur la première option. */
select[multiple],
select[size]:not([size="1"]) { background-image: none; padding-right: 11px; }

/* --------------------------------------------------------- article ---
   `.article-banniere` n'avait aucune règle : dixième classe du projet dans ce
   cas. L'image s'affichait donc à sa proportion naturelle, sans arrondi ni
   marge, et un visuel de remplacement occupait un écran de gris. */
.article-banniere {
  margin:0 0 32px;
  border-radius: var(--rayon-large);
  overflow: hidden;
  background: var(--fond-doux);
}
.article-banniere img {
  display: block; width: 100%;
  /* Hauteur bornée : une bannière est un bandeau, pas une affiche. Au-delà de
     280 px elle repousse le premier paragraphe sous la ligne de flottaison, ce
     qui est exactement ce qu'une illustration ne doit pas faire. */
  height: clamp(180px, 26vw, 280px);
  object-fit: cover;
}

/* Le chapô est plus gros que le corps et plus clair que le titre : c'est ce qui
   le fait lire en premier et permet de décider si l'on continue. Il était rendu
   avec `.sous-titre`, la classe des sous-titres de rubrique, donc au même corps
   que le texte courant. */
.article-chapo {
  font-size: 19px; line-height: 1.6; color: var(--encre-douce);
  margin:0 0 4px; text-wrap: pretty;
}

.article-retour { margin:8px 0 40px; font-size: 15px; }
.article-retour a { color: var(--accent-texte); }

/* Le simulateur de financement, sur la fiche produit, se cale sur la largeur de
   lecture des blocs qui l'entourent. Étendu à toute la colonne, il devenait
   l'élément le plus large de la page pour un contenu de trois lignes. */
/* 340 px : l'encadré tient trois lignes et deux onglets, il n'a aucune raison
   d'occuper la largeur d'un bouton d'achat. */
.fiche #paiement-plusieurs-fois .echelonnement { max-width: 340px; }

/* GRILLE AVEC COUVERTURE EN VEDETTE — la grille ordinaire, sauf que la première
   vue occupe toute la première ligne. C'est la présentation qui convient aux
   catalogues dont une image porte le produit à elle seule : l'affiche, le
   tableau, la pièce de mobilier. Les vues suivantes gardent leur carré. */
.galerie-grille-vedette .fiche-visuel-principal { display: none; }
.galerie-grille-vedette .fiche-vignettes {
  display: grid; grid-template-columns: repeat(2, 1fr); gap:10px;
}
/* LES VUES SECONDAIRES SONT EN PORTRAIT, PAS EN CARRÉ. Un carré est le format
   le plus défavorable à un bijou : une bague, une paire de boucles ou un
   pendentif s'inscrivent dans un rectangle haut, et le carré remplit le reste
   de fond blanc — sur la capture d'une paire de dormeuses, l'objet occupait
   moins du tiers de sa case. Le rapport est celui de la marque, le même que
   les cartes de rubrique et le carrousel : trois grilles de vignettes qui ne
   cadreraient pas pareil seraient un défaut, pas une nuance. */
.galerie-grille-vedette .fiche-vignettes img {
  width: 100%; height: auto; aspect-ratio: var(--marque-ratio-visuel, 4 / 5);
}
/* ⚠️ LA VUE EN VEDETTE ÉTAIT EN PAYSAGE ALORS QU'AUCUNE PHOTO NE L'EST
   (8/09/2026, « la première image doit être plus haute, on doit voir
   l'intégralité du produit »). Le 4/3 venait de l'idée d'affiche ou de meuble
   qui a présidé à cette disposition ; les catalogues repris, eux, sont
   photographiés en portrait — 1 100 × 1 448 sur les 4 200 photos de la
   boutique de bijoux, soit un rapport de 0,76. Une photo de 0,76 forcée en
   1,33 par `object-fit: cover` PERD 43 % DE SA HAUTEUR : le bijou était
   décapité en haut et coupé en bas, sur la seule image qui décide de l'achat.

   Deux corrections, et il fallait les deux. Le rapport passe à celui de la
   marque — le même que les vues secondaires juste en dessous, donc une grille
   qui ne cadre plus de deux façons — ce qui suffit à rendre la vue 66 % plus
   haute. Et le cadrage passe en `contain` : `cover` ne peut PAS tenir la
   promesse « l'intégralité du produit », quel que soit le rapport choisi, tant
   que le fichier n'a pas exactement celui de la boîte. Le fond de la boîte est
   celui de la marque, comme partout ailleurs, si bien qu'un écart de rapport
   se lit comme une marge et non comme un défaut.

   La loupe au survol refait ce cadrage de son côté (shop.js) : elle a été
   passée en `contain` en même temps, sinon elle montrerait un cadre plus
   serré que celui qu'on a sous les yeux. */
.galerie-grille-vedette .fiche-vignette-wrap {
  grid-column: 1 / -1;
  position: relative; overflow: hidden;
  border-radius: var(--rayon);
}
.galerie-grille-vedette .fiche-vignette-wrap img {
  width: 100%; display: block;
  aspect-ratio: var(--marque-ratio-visuel, 4 / 5);
  object-fit: contain;
  background: var(--marque-fond-visuel, var(--fond-doux));
  border-radius: 0;
}

/* LA COLONNE D'ACHAT N'EST PLUS ÉPINGLÉE, ET C'EST UNE CONSÉQUENCE DIRECTE
   DE CE QU'ELLE PORTE. Elle l'a été tant qu'elle se réduisait au prix, aux
   déclinaisons et au bouton ; elle porte désormais la fiche technique et la
   réassurance, à la demande, et les deux choses ne peuvent pas coexister.

   La raison est dans la définition de `sticky`, et elle est contre-intuitive :
   un élément épinglé plus HAUT que la fenêtre ne défile pas de lui-même. Il se
   fige dès que son sommet atteint l'offset, et il reste figé tant que son bloc
   conteneur est à l'écran — la galerie faisant deux mille pixels, cela dure
   presque toute la page. Sa partie basse, celle qui dépasse de la fenêtre,
   n'est alors JAMAIS atteignable : la réassurance devenait invisible pendant
   tout le défilement. Un blanc à droite est un défaut de mise en page ; du
   contenu inaccessible est un défaut tout court.

   `align-self: start` reste utile sans l'épinglage : il empêche la colonne de
   s'étirer sur la hauteur de la galerie, ce qui étalerait ses marges. */
/* LA COLONNE D'ACHAT EST ÉPINGLÉE, MAIS À UNE HAUTEUR CALCULÉE.
   Elle avait été dépinglée quand les caractéristiques et la réassurance y ont
   été déplacées : la colonne mesure alors 1243 px, elle ne tient dans aucune
   fenêtre courante, et un élément collant plus haut que l'écran a son bas
   INATTEIGNABLE — on ne peut plus lire la fin de la fiche technique.

   `--achat-haut` est posé par shop.js et vaut :
     — 90 px quand la colonne tient dans la fenêtre : épinglage classique ;
     — une valeur NÉGATIVE quand elle est plus haute, calculée pour que son BAS
       vienne se caler en bas de l'écran. La colonne défile alors avec la page
       jusqu'à ce qu'on en voie la fin, puis se fige. Tout reste lisible, et
       elle accompagne quand même les visuels.

   Le repli à 90 px vaut si le script ne tourne pas : l'épinglage classique est
   le comportement le moins surprenant, et sur une colonne courte il est juste.

   Sous 1000 px les colonnes s'empilent : épingler n'a plus de sens. */
.fiche-achat { align-self: start; }
@media (min-width: 1000px) {
  .fiche-achat { position: sticky; top: var(--achat-haut, 90px); }
}

/* La marque, au-dessus du titre de la fiche. */
.fiche-marque {
  display: inline-flex; align-items: center;
  margin-bottom:10px; padding:5px 10px;
  border: 1px solid var(--filet); border-radius: var(--rayon);
  background: var(--surface); text-decoration: none;
  transition: border-color .14s ease;
}
.fiche-marque:hover { border-color: var(--accent); }
.fiche-marque img { height: 20px; width: auto; max-width: 132px; object-fit: contain; display: block; }
.fiche-marque span { font-size: 13.5px; font-weight: 550; color: var(--encre-douce); }

/* LA DESCRIPTION LONGUE, EN PLEINE LARGEUR SOUS LA GALERIE.
   Elle est le seul bloc à y rester : la fiche technique et la réassurance sont
   remontées dans la colonne d'achat, à côté de la décision qu'elles servent.

   Elle est habillée comme les avis et les produits associés — filet, même
   retrait, intertitre de 20 px — parce que c'est la même chose : une section
   de la page, pas un appendice. Auparavant elle commençait par un `margin-top`
   écrit dans l'attribut `style` du gabarit et rien d'autre ; sans titre ni
   filet, le texte se posait sous la galerie sans qu'on sache ce qu'on lisait. */
/* ⚠️ LE FILET RESPIRE DES DEUX CÔTÉS (18/09/2026, « améliore les marges »).
   Ces sections montaient à `margin-top: 8px` pour 32 px de rembourrage : le
   trait était collé au bloc du dessus — huit pixels — et flottait à trente-deux
   du titre qu'il annonce. Un séparateur si près de ce qu'il quitte se lit comme
   un SOULIGNEMENT du bloc précédent, pas comme une coupure, et toute la page
   paraît mal calée. 48 au-dessus, 32 en dessous : c'est le rythme que
   `.avis-bloc` tenait déjà dans cette même feuille, et le trait appartient
   enfin à ce qui le suit. */
.fiche-description-bloc {
  margin-top:48px;
  padding-top:32px;
  border-top: 1px solid var(--filet);
}
.fiche-description-bloc h2 { margin:0 0 18px; }

/* L'INVITATION À SE CONNECTER, EN TÊTE DU TUNNEL. Discrète par construction :
   c'est une commodité, pas un péage — la commande sans compte reste le chemin
   normal, et une bannière insistante ferait croire le contraire. Lavis d'accent
   comme la carte d'appel de la fiche produit, même grammaire. */
.tunnel-connexion {
  /* LE BOUTON SUIT LA PHRASE, il ne part pas au bout de la bande. Avec
     `space-between`, sur un écran large, « Se connecter » se retrouvait à mille
     pixels de la question qu'il répond : l'œil lit l'invitation, puis doit
     traverser un vide pour trouver quoi cliquer. Groupés, les deux se lisent
     d'un seul regard. */
  display: flex; align-items: center; justify-content: flex-start;
  flex-wrap: wrap; gap: 10px 18px;
  margin: 0 0 22px; padding: 12px 14px 12px 18px;
  border-radius: var(--rayon);
  background: color-mix(in srgb, var(--accent) 9%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--accent) 20%, transparent);
  font-size: 14.5px; line-height: 1.5; color: var(--encre);
}
.tunnel-connexion-texte { display: inline-flex; align-items: center; gap: 8px; min-width: 0; }
/* L'ACTION EST UN BOUTON, PAS UN MOT SOULIGNÉ DANS UNE PHRASE. Un lien en
   ligne se lit comme une note de bas de page ; ici c'est un geste qu'on
   propose, et il doit se viser sans lire — 40 px de haut, la cible au pouce. */
.tunnel-connexion-action {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 40px; padding: 0 18px; flex: none;
  border: 1px solid var(--accent); border-radius: 999px;
  background: var(--surface); color: var(--accent-texte);
  font-size: 14px; font-weight: 600; text-decoration: none; white-space: nowrap;
  transition: background-color .15s ease, color .15s ease;
}
.tunnel-connexion-action:hover { background: var(--accent); color: var(--accent-encre, #fff); }
/* Connecté, ce n'est plus une invitation mais une confirmation : pas d'accent,
   pas de bouton — juste une ligne qui rassure sur l'identité utilisée. */
.tunnel-connexion-connecte {
  background: var(--fond-doux); border-color: var(--filet);
  color: var(--encre-douce);
}
.tunnel-connexion-connecte svg { flex: none; color: var(--accent-texte); }
@media (max-width: 560px) {
  /* Le bouton passe sous la phrase et prend la largeur : à cette taille, une
     cible collée au bord droit se rate une fois sur trois. */
  .tunnel-connexion { align-items: stretch; }
  .tunnel-connexion-action { width: 100%; }
}

/* ============================================================================
   CE QUI EST SOUS LA LIGNE DE FLOTTAISON N'EST PAS MIS EN PAGE AU CHARGEMENT
   (18/09/2026, « optimise les performances bureau »).

   Mesuré sur l'accueil du bijoutier : 2 284 éléments, dont 1 264 dans les seuls
   carrousels de produits, pour une page de 5 568 px de haut — quatre cinquièmes
   du document sont hors de l'écran au premier rendu, et pourtant entièrement
   mis en page. C'est ce qui faisait de « Style & Layout » le premier poste du
   thread principal : 814 ms sur un processeur bridé comme celui de PageSpeed,
   devant le script, le rendu et l'analyse réunis.

   `content-visibility: auto` dit au navigateur de sauter la mise en page d'un
   bloc tant qu'il n'approche pas de l'écran. `contain-intrinsic-size: auto <h>`
   lui donne une hauteur présumée en attendant — et le mot-clé `auto` lui fait
   RETENIR la vraie hauteur une fois mesurée, ce qui évite que la barre de
   défilement saute quand on remonte.

   ⚠️ CE N'EST PAS APPLIQUÉ À LA BANDE D'AVIS, qui défile toute seule : un bloc
   dont la mise en page est suspendue rend des largeurs nulles, et le défilement
   repartirait de zéro à chaque fois qu'il revient à l'écran.

   ⚠️ LES CARROUSELS DEMANDENT UNE LIGNE DE JAVASCRIPT EN PLUS (shop.js) : leurs
   flèches n'apparaissent que si la piste déborde, ce qui se mesure — et un bloc
   sauté mesure zéro. Un IntersectionObserver relance la mesure à l'entrée dans
   l'écran. Sans lui, les flèches restaient cachées sur les carrousels du bas.

   ⚠️ `content-visibility: auto` EMPORTE UN CONFINEMENT DE PEINTURE (21/09/2026,
   « les ombres au hover sont cassées ») : tout ce qui déborde du bloc est
   COUPÉ NET à son bord, comme sous `overflow: clip`. Or la piste déborde de
   24 px de chaque côté (sa marge négative), et l'ombre des cartes avec elle :
   au survol, l'ombre s'arrêtait au bord gauche de la première carte, dessinant
   un rectangle clair. D'où le rembourrage latéral, rendu par une marge
   négative, qui remet le bord du bloc là où finit la piste. `.fiche ~` est
   répété parce que sa règle de rembourrage pose un horizontal à zéro et gagne
   en spécificité.
   `.rubrique-bas` n'en a PLUS : la bande de survol de sa FAQ déborde de 16 px
   à droite et y était tranchée, et l'y réserver aurait élargi son filet du
   haut. Un texte et une dizaine de questions ne pesaient rien dans la mesure
   ci-dessus — ce sont les carrousels qui comptaient.
   ========================================================================== */
.carrousel      { content-visibility: auto; contain-intrinsic-size: auto 520px; }
.carrousel,
.fiche ~ .carrousel { margin-inline: -24px; padding-inline: 24px; }
.pied           { content-visibility: auto; contain-intrinsic-size: auto 700px; }

/* ============================================================================
   « AVANT DE COMMANDER » — le pavé d'aide à la décision, bas de fiche produit
   (18/09/2026, repris du PrestaShop source de la reprise bijoux ; recomposé le
   soir même, « marges, design, ergonomie : c'est pas top »).

   UN SEUL PANNEAU, ET C'EST TOUT LE SUJET. La première version posait les
   volets À NU sur le fond de la page et la carte d'appel dans un cadre : deux
   objets de poids visuel inégal, l'un fini, l'autre flottant, séparés par
   quarante-quatre pixels de blanc. Ici un seul cadre les contient — les volets
   sur la surface, la colonne d'appel en lavis d'accent JUSQU'AU BORD (c'est
   l'`overflow: hidden` du panneau qui lui arrondit les coins), séparées par le
   même filet qui borde l'ensemble.

   Les volets reprennent `.faq-question` — même chevron, même filet, même rythme
   que la FAQ du site. Trois choses leur sont propres : l'icône de tête, le
   chevron passé à droite, et la ligne d'en-tête qui déborde jusqu'aux bords du
   panneau pour que le survol couvre toute la bande cliquable.
   ========================================================================== */
.aide-achat {
  /* Même rythme que la description et que les avis : voir la note de
     `.fiche-description-bloc`. */
  margin-top: 48px;
  padding-top: 32px;
  border-top: 1px solid var(--filet);
}
/* ⚠️ LE TITRE N'EST PLUS DANS LA GRILLE. Il l'était, et la `gap` de 44 px valait
   aussi entre les RANGÉES : il y avait donc quarante-quatre pixels entre
   « Avant de commander » et ce qu'il annonce, contre vingt partout ailleurs sur
   la fiche. Une gouttière de colonnes ne doit jamais servir de marge de titre. */
.aide-achat-titre { margin: 0 0 18px; }

.aide-achat-panneau {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 340px;
  align-items: stretch;
  background: var(--surface);
  border: 1px solid var(--filet);
  border-radius: var(--rayon-large);
  /* Ce qui permet au lavis de la colonne d'appel d'aller jusqu'au bord sans
     avoir à lui redonner les rayons du panneau, coin par coin. */
  overflow: hidden;
}
/* Sans carte d'appel — une boutique qui ne publie ni numéro ni courriel — les
   volets prennent toute la largeur plutôt que de laisser une colonne vide. */
.aide-achat.est-seule .aide-achat-panneau { grid-template-columns: minmax(0, 1fr); }

/* --- les volets ---------------------------------------------------------- */
.aide-volets { min-width: 0; padding: 6px 26px 10px; }
/* ⚠️ PAS DE FILET D'OUVERTURE DANS UN PANNEAU QUI EN A DÉJÀ UN (18/09/2026).
   La FAQ a reçu ce jour-là un filet au-dessus de sa première question, pour
   qu'un thème à une seule question ne flotte pas sans cadre. Ici les volets
   vivent DANS un panneau bordé : ce filet venait doubler sa bordure haute à
   sept pixels d'écart, et comme il s'arrête au bord de la colonne et non du
   panneau, il pendait dans le vide devant la carte d'appel. */
.aide-volets .aide-volet:first-of-type { border-top: 0; }
.aide-volet > summary {
  font-family: var(--police-titre);
  font-weight: var(--marque-poids-titre, 600);
  font-size: 17px;
  align-items: center;
  gap: 12px;
  /* LA BANDE CLIQUABLE VA D'UN BORD À L'AUTRE du panneau : les marges négatives
     annulent le rembourrage des volets, le rembourrage les rend au texte. Sans
     cela, le survol s'arrêtait au mot et laissait deux bandes mortes de 26 px
     de part et d'autre, sur une ligne qui est pourtant cliquable entière. */
  margin: 0 -26px;
  padding: 17px 26px;
  transition: background-color .15s ease, color .15s ease;
}
.aide-volet > summary:hover { background: color-mix(in srgb, var(--accent) 6%, transparent); }
.aide-volet[open] > summary { background: transparent; }
/* LE CHEVRON PASSE À DROITE, l'icône prend sa place à gauche. Dans
   `.faq-question` il ouvre la ligne, ce qui convient à une question ; ici la
   tête de volet est un intitulé court accompagné d'un pictogramme, et deux
   marqueurs collés l'un à l'autre en entrée de ligne se lisent comme un seul
   objet cassé. `order` suffit — la boîte est déjà en flex. */
.aide-volet > summary::before { order: 3; margin-left: auto; margin-top: 0; }
.aide-volet-icone { display: inline-flex; flex: none; color: var(--accent-texte); }
/* Le corps s'aligne sur le TEXTE de l'intitulé : 18 px d'icône plus 12 px
   d'écart font 30. Et il est BORNÉ : sur un grand écran la colonne fait sept
   cent cinquante pixels, où une ligne de texte court sur cent-dix signes —
   deux fois la mesure de lecture confortable. */
.aide-volet-corps { padding: 2px 0 20px 30px; max-width: 64ch; }

.aide-liste { list-style: none; margin: 0; padding: 0; display: grid; gap: 9px; }
.aide-liste li {
  display: flex; flex-wrap: wrap; align-items: baseline;
  gap: 2px 10px;
  font-size: 15px; line-height: 1.5;
}
/* Une pastille d'accent plutôt qu'une puce de navigateur : la puce est posée
   dans l'encre du texte et s'aligne sur la première ligne d'un paragraphe qui
   en fait deux. Celle-ci tient sa place quelle que soit la casse du texte. */
.aide-liste li::before {
  content: ''; flex: 0 0 5px; width: 5px; height: 5px;
  margin-top: .58em; align-self: flex-start;
  border-radius: 50%; background: var(--accent);
}
.aide-liste strong { font-weight: 600; color: var(--encre); }
.aide-liste li > span { color: var(--encre-douce); font-size: 14.5px; }
.aide-note { margin: 12px 0 0 15px; font-size: 13px; color: var(--encre-tenue); }
.aide-badges { display: flex; flex-wrap: wrap; gap: 6px; margin: 14px 0 0 15px; }
.aide-volet-liens {
  display: flex; flex-wrap: wrap; gap: 4px 20px;
  margin: 10px 0 0 15px; font-size: 14px;
}
.aide-volet-liens a {
  color: var(--accent-texte);
  text-decoration: underline; text-underline-offset: 3px;
  text-decoration-thickness: 1px;
  /* Une cible de 40 px de haut : ces liens sont en bas d'un volet déplié, donc
     presque toujours atteints au pouce. */
  display: inline-flex; align-items: center; min-height: 40px;
}

/* --- La colonne d'appel -------------------------------------------------- */
/* UN LAVIS D'ACCENT, PAS UN GRIS. Teinter avec un pourcentage d'encre paraît
   prudent — ça marche sur toutes les chartes — mais pose un gris que la
   boutique n'emploie nulle part ailleurs. Un lavis d'accent donne le même écart
   de clarté DANS la teinte de la maison, et suit la déclinaison suivante sans
   qu'on y revienne. Même raisonnement que `.pied-identite`. */
.aide-contact {
  /* 11 %, et non 8 : à huit pour cent d'un or clair sur une surface blanche,
     l'écart de clarté ne se voyait plus qu'au filet — la colonne ne se
     détachait pas, elle avait juste l'air moins nette. */
  background: color-mix(in srgb, var(--accent) 11%, var(--surface));
  border-left: 1px solid var(--filet);
  padding: 24px 24px 26px;
}
.aide-contact-titre {
  margin: 0 0 14px;
  font-family: var(--police-titre);
  font-weight: var(--marque-poids-titre, 600);
  font-size: 18px; line-height: 1.3;
  text-wrap: balance;
}
/* LE NUMÉRO EST LE SEUL ÉLÉMENT EN ACCENT ET LE PLUS GROS DE LA COLONNE : c'est
   l'action qu'elle propose, tout le reste sert à la réussir. */
.aide-contact-tel {
  display: inline-flex; align-items: center; gap: 10px;
  min-height: 44px;
  font-size: 21px; font-weight: 600; letter-spacing: .01em;
  color: var(--accent-texte); text-decoration: none;
}
.aide-contact-tel:hover { text-decoration: underline; text-underline-offset: 4px; }
.aide-contact-tel svg { flex: none; }
/* LA RÉFÉRENCE, SOUS LE NUMÉRO ET AU MÊME CORPS : c'est la seconde chose qu'on
   dit en appelant. Même icône à gauche, même taille ; l'encre et non l'accent,
   pour que le numéro reste le seul élément cliquable en couleur. */
.aide-contact-ref {
  display: flex; align-items: center; flex-wrap: wrap; gap: 4px 10px;
  margin: 2px 0 6px; min-height: 36px;
  font-size: 21px; line-height: 1.25; color: var(--encre);
}
.aide-contact-ref svg { flex: none; color: var(--encre-tenue); }
.aide-contact-ref .aide-reference { font-weight: 600; }
.aide-contact-ref .aide-copier { margin-left: 0; }

.aide-contact-liste {
  list-style: none; margin: 8px 0 0; padding: 0;
  display: grid; gap: 9px;
  font-size: 14px; line-height: 1.5; color: var(--encre-douce);
}
.aide-contact-liste li { display: flex; gap: 10px; align-items: flex-start; }
.aide-contact-icone { flex: 0 0 18px; color: var(--encre-tenue); margin-top: 1px; }
.aide-contact-liste a { color: var(--encre-douce); }
.aide-reference { color: var(--encre); letter-spacing: .04em; }
/* LE BOUTON DE COPIE N'EST PLUS UNE PASTILLE BORDÉE : dans une colonne qui ne
   contient aucun autre contrôle, elle se lisait comme un champ de formulaire
   oublié là. Un libellé en accent, souligné au survol — la même grammaire que
   les autres liens de la colonne — et une cible de 32 px qui déborde sous le
   texte sans écarter les lignes. */
.aide-copier {
  margin-left: 8px; padding: 0 2px;
  border: 0; background: none;
  color: var(--accent-texte);
  font: inherit; font-size: 13px; line-height: inherit;
  cursor: pointer;
  text-decoration: underline; text-underline-offset: 2px;
  text-decoration-color: color-mix(in srgb, var(--accent-texte) 40%, transparent);
}
.aide-copier:hover { text-decoration-color: currentColor; }
.aide-copier.est-copie { text-decoration: none; opacity: .75; }

/* La consigne de visite est DÉTACHÉE : ce n'est pas une coordonnée de plus mais
   une condition — venir sans rendez-vous, c'est venir pour rien. Sur le lavis
   de la colonne, c'est la SURFACE qui la détache, et non un second lavis plus
   dense : deux teintes de la même couleur à quinze pixels d'écart se lisent
   comme une erreur d'impression. */
.aide-contact-note {
  display: flex; gap: 10px;
  margin: 16px 0 0; padding: 12px 14px;
  border-radius: var(--rayon);
  background: var(--surface);
  border: 1px solid color-mix(in srgb, var(--accent) 22%, transparent);
  font-size: 13.5px; line-height: 1.5; color: var(--encre);
}
/* ⚠️ `display: block` ET NON `inline-flex` : la flèche était un élément de
   flex, donc placée APRÈS le bloc de texte et centrée verticalement — sur un
   intitulé qui passe à la ligne (« Voir tout le rayon Bijoux or de seconde
   main »), elle se retrouvait en bout de PREMIÈRE ligne, à trente pixels du
   dernier mot. En contenu généré d'un bloc, elle suit le dernier mot, toujours,
   et l'espace insécable l'empêche de tomber seule à la ligne suivante. */
.aide-contact-catalogue {
  display: block;
  margin-top: 18px;
  font-size: 14.5px; line-height: 1.45; color: var(--accent-texte);
  text-decoration: underline; text-underline-offset: 3px;
  text-decoration-thickness: 1px;
}
.aide-contact-catalogue::after { content: "\00a0\2192"; }

@media (max-width: 900px) {
  /* La colonne d'appel passe SOUS les volets, et son lavis prend toute la
     largeur du panneau : le filet vertical devient horizontal, rien d'autre ne
     change. Le numéro reste à portée de pouce, en fin de bloc. */
  .aide-achat { padding-top: 26px; }
  .aide-achat-panneau { grid-template-columns: minmax(0, 1fr); }
  .aide-contact { border-left: 0; border-top: 1px solid var(--filet); }
  .aide-volets { padding: 4px 18px 8px; }
  .aide-volet > summary { margin: 0 -18px; padding: 16px 18px; }
  .aide-volet-corps { padding-bottom: 18px; }
}

/* Les intertitres d'un formulaire long. Un formulaire de compte marchand porte
   une quinzaine de champs : sans découpage, on le lit comme une liste et l'on
   abandonne au milieu. Trois sections nommées transforment la même liste en
   trois efforts courts, dont on voit la fin. */
.form-section {
  font-size: 12px; font-weight: 650; letter-spacing: .05em; text-transform: uppercase;
  color: var(--encre-tenue);
  margin:24px 0 4px; padding-top:20px;
  border-top: 1px solid var(--filet);
}
.form-section:first-of-type { margin-top:6px; padding-top:0; border-top: 0; }
.form-section-aide {
  margin:0 0 16px; font-size: 13.5px; color: var(--encre-tenue); line-height: 1.55;
}
.form-collapse .form-section-aide { margin:0 0 12px; }

/* ============================================================================
   LE TIROIR DE NAVIGATION — écrit UNE FOIS pour ses deux apparitions
   ----------------------------------------------------------------------------
   Le menu se replie en tiroir dans deux cas qui ne se combinent pas en CSS :
   sous 640 px (une requête de média) et dès que la page est défilée sur grand
   écran (une classe sur le bandeau). La feuille portait donc les mêmes règles
   en DEUX EXEMPLAIRES, avec un commentaire qui assumait la duplication — et,
   comme toute duplication assumée, ils avaient commencé à diverger.

   `shop.js` pose maintenant `est-tiroir` sur le menu dans les deux cas. Tout ce
   qui suit s'écrit donc une fois, et le comportement (clic et non survol) se lit
   sur le même état que l'apparence : les deux ne peuvent plus se contredire.

   ─── IL S'OUVRE SOUS LE BOUTON, PLUS AU CENTRE (18/08/2026, demande de Jesse)
   Le panneau centré était un choix défendable — il ouvre là où l'œil se trouve
   — mais il coupe le lien avec ce qui l'a ouvert : rien ne rattache une carte
   posée au milieu de l'écran au bouton à trois traits du coin. Accroché sous
   l'en-tête, il se lit comme le déroulé de ce bouton, et l'animation part de
   son bord haut au lieu de grandir depuis nulle part.
   `--bandeau-bas` est MESURÉ par le script : une valeur en dur serait fausse
   dès qu'un bandeau d'annonces ou de congés s'ajoute au-dessus.
   ========================================================================== */
.menu.est-tiroir {
  position: fixed;
  top: calc(var(--bandeau-bas, 72px) + 8px);
  /* `--tiroir-ancrage` : LA MÊME VALEUR QUE LE BOUTON QUI OUVRE CE PANNEAU,
     un seul jeton pour les deux — voir le commentaire dans le bloc
     `@media (min-width: 641px)` plus bas, qui explique pourquoi deux valeurs
     écrites en dur séparément avaient fini par diverger. Le repli à 16px
     couvre le cas où ce jeton n'est posé nulle part (`.bandeau` absent du
     DOM, gabarit qui n'inclut pas l'en-tête). */
  left: var(--tiroir-ancrage, 16px); right: auto; bottom: auto;
  z-index: 90;
  width: min(calc(100vw - 32px), 360px);
  /* `100dvh` (hauteur de viewport DYNAMIQUE) et non `100vh` : sur mobile, `100vh`
     compte la barre d'adresse du navigateur, si bien que le bas du tiroir (et ses
     derniers liens) passait sous cette barre, hors de l'écran. `dvh` suit la
     hauteur réellement visible. Repli en `vh` pour les navigateurs sans `dvh`. */
  max-height: calc(100vh - var(--bandeau-bas, 72px) - 24px);
  max-height: calc(100dvh - var(--bandeau-bas, 72px) - 24px);
  flex-direction: column; flex-wrap: nowrap; justify-content: flex-start;
  align-items: stretch; gap: 0;
  padding: 8px;
  background: var(--surface);
  border: 1px solid var(--filet);
  border-radius: calc(var(--rayon) * 1.6);
  box-shadow: 0 18px 48px -12px rgba(16, 16, 24, .28);
  overflow-y: auto; overscroll-behavior: contain;
  /* L'animation part du COIN HAUT GAUCHE, sous le bouton : un panneau qui
     grandit depuis son centre paraît surgir, un panneau qui se déploie depuis
     son point d'ancrage paraît sortir de là où on a cliqué. */
  transform-origin: top left;
  transform: translateY(-6px) scale(.97);
  opacity: 0; visibility: hidden;
  transition: opacity .16s ease, transform .18s cubic-bezier(.2, .7, .3, 1), visibility 0s linear .18s;
  -webkit-mask-image: none; mask-image: none;
}
.menu.est-tiroir.est-ouvert {
  transform: none;
  opacity: 1; visibility: visible;
  transition: opacity .16s ease, transform .18s cubic-bezier(.2, .7, .3, 1), visibility 0s;
}
@media (prefers-reduced-motion: reduce) {
  .menu.est-tiroir, .menu.est-tiroir.est-ouvert { transition: opacity .01s linear; transform: none; }
}

/* ─── LES RANGÉES ────────────────────────────────────────────────────────────
   Elles faisaient 16 px de texte dans 11 px de rembourrage, sans séparation ni
   repère : une colonne de trente libellés tous pareils, que Jesse a trouvée
   « grossière » — le mot est juste. On resserre le corps, on donne à chaque
   rangée sa hauteur de cible tactile (44 px, la règle d'Apple comme de Google),
   et on sépare les entrées par un filet plutôt que par du vide. */
.menu.est-tiroir > a,
.menu.est-tiroir .menu-groupe > summary {
  display: flex; align-items: center; gap: 10px;
  min-height: 44px; padding: 0 12px;
  border-radius: var(--rayon); border-bottom: 0;
  font-size: 15px; color: var(--encre);
}
.menu.est-tiroir > a:hover,
.menu.est-tiroir .menu-groupe > summary:hover { background: var(--fond-doux); color: var(--accent-texte); }
.menu.est-tiroir > a[aria-current="page"],
.menu.est-tiroir .menu-groupe > summary[aria-current="page"] {
  color: var(--accent-texte); font-weight: 600;
}
/* Le filet sépare les rangées de PREMIER niveau seulement, et jamais la
   dernière : un trait sous le dernier élément d'une liste ne sépare rien. */
.menu.est-tiroir > a + a,
.menu.est-tiroir > a + .menu-groupe,
.menu.est-tiroir > .menu-groupe + a,
.menu.est-tiroir > .menu-groupe + .menu-groupe { border-top: 1px solid var(--filet); }

/* ─── LE CHEVRON PASSE À DROITE ET TOURNE ────────────────────────────────────
   Collé au libellé, il désignait un mot ; posé au bout de la rangée, il désigne
   la rangée — et c'est la rangée entière qu'on touche. Il pivote à l'ouverture,
   ce qui dit l'état sans un mot de plus. */
.menu.est-tiroir .menu-groupe > summary::after {
  margin-left: auto; margin-right: 2px;
  transition: transform .18s ease;
}
.menu.est-tiroir .menu-groupe[open] > summary::after {
  transform: translateY(-3px) rotate(-135deg);
}
.menu.est-tiroir .menu-groupe[open] > summary { color: var(--accent-texte); }

/* ─── LE SOUS-PANNEAU ────────────────────────────────────────────────────────
   (20/08/2026, deuxième passe — « mauvaise intégration des sous-menus ».) La
   première version rattachait les enfants par un simple filet vertical et
   dépouillait l'intitulé de section de sa pastille, en jugeant celle du
   grand panneau — un aplat de l'accent à 12 %, en pilule — trop lourde dans
   340 px. Le résultat se lisait comme une liste par défaut, sans rapport avec
   le reste du site : le tiroir a ses rangées pleines et arrondies, la barre
   horizontale a ses pastilles de couleur, et le sous-panneau seul n'avait ni
   l'une ni l'autre.

   IL REPREND DONC LA MÊME PASTILLE QUE LE GRAND PANNEAU, réduite, plutôt
   qu'un habillage propre au tiroir : deux vocabulaires visuels pour la même
   information sont ce qui fait « mal intégré ». Le fond tinté remplace le
   filet vertical — un bloc se reconnaît comme un groupe sans qu'il faille lui
   dessiner une bordure. */
.menu.est-tiroir .menu-sous {
  position: static; left: auto; top: auto; transform: none;
  width: auto; max-height: none; overflow: visible;
  border: 0; border-radius: var(--rayon);
  box-shadow: none; background: var(--fond-doux);
  margin: 4px 0 8px; padding: 10px 10px 8px;
}
.menu.est-tiroir .menu-sous-lignes { columns: 1; column-gap: 0; }
.menu.est-tiroir .menu-sous-groupe { margin-bottom: 10px; }
.menu.est-tiroir .menu-sous-groupe:last-child { margin-bottom: 0; }
.menu.est-tiroir .menu-sous-item {
  display: flex; align-items: center;
  min-height: 38px; padding: 0 8px; border-radius: var(--rayon);
  font-size: 14.5px; color: var(--encre-douce);
}
.menu.est-tiroir .menu-sous-item:hover { background: var(--surface); color: var(--encre); }
.menu.est-tiroir .menu-sous-entete {
  width: fit-content; max-width: 100%;
  margin: 0 0 6px; padding: 4px 10px;
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  border-radius: 999px;
  font-size: 10.5px; font-weight: 700; letter-spacing: .08em;
  text-transform: uppercase; color: var(--accent-texte);
}
.menu.est-tiroir .menu-sous-groupe:first-child .menu-sous-entete { margin-top: 0; }
/* ⚠️ LE PIED RESTE, ET C'EST LA SEULE FAÇON D'ATTEINDRE LA RUBRIQUE ELLE-MÊME.
   Une première version le masquait, en supposant que « la rangée de la
   rubrique est déjà ce lien » — c'est faux : `<summary>` n'est pas un `<a>`, il
   ne fait que déplier la liste. Sans ce lien, « Ouvrir la page Bijoux
   d'occasion » devenait injoignable depuis le tiroir. Il reprend juste
   l'habillage compact du panneau plutôt que celui, collant et rembourré, du
   grand panneau. */
.menu.est-tiroir .menu-sous-pied {
  position: static;
  margin: 6px 0 2px; padding: 8px 10px 2px;
  border-top: 1px solid var(--filet); background: none;
}
.menu.est-tiroir .menu-sous-tout {
  display: flex; align-items: center; min-height: 36px;
  font-size: 14px;
}

/* ─── LA CROIX ET LES LIENS SECONDAIRES ──────────────────────────────────────
   La croix était un carré bordé de 34 px posé en haut à droite d'un panneau qui
   n'a que 8 px de rembourrage : elle mangeait une rangée entière. Elle devient
   une cible discrète, alignée sur la première ligne. */
/* L'ENTÊTE PORTE LES DEUX : L'INTITULÉ ET LA CROIX, sur la même ligne. La
   croix seule flottait déjà en haut à droite ; lui donner un intitulé à
   gauche coûte une ligne de flex, pas un second bloc. */
.menu.est-tiroir .menu-entete {
  display: flex; align-items: center; justify-content: space-between;
  margin: 0 0 6px; padding: 0 4px;
}
.menu.est-tiroir .menu-entete-titre {
  margin: 0; font-size: 12px; font-weight: 650; letter-spacing: .04em;
  text-transform: uppercase; color: var(--encre-tenue);
}
.menu.est-tiroir .menu-fermer {
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; padding: 0;
  border: 0; border-radius: var(--rayon);
  background: transparent; color: var(--encre-tenue); cursor: pointer;
}
.menu.est-tiroir .menu-fermer:hover { background: var(--fond-doux); color: var(--encre); }
.menu.est-tiroir .menu-secondaire {
  display: flex; flex-direction: column; gap: 0;
  margin-top: 8px; padding-top: 8px;
  border-top: 1px solid var(--filet);
}
.menu.est-tiroir .menu-secondaire a {
  display: flex; align-items: center; gap: 10px; min-height: 40px; padding: 0 12px;
  border-radius: var(--rayon);
  color: var(--encre-douce); font-size: 14.5px;
}
.menu.est-tiroir .menu-secondaire a:hover { background: var(--fond-doux); color: var(--encre); }
/* La page courante s'allume ici comme dans les rubriques au-dessus : sans
   cette règle, la couleur de `.menu-secondaire a` l'emportait sur celle de
   `.menu a[aria-current]`. */
.menu.est-tiroir .menu-secondaire a[aria-current="page"] { color: var(--accent-texte); font-weight: 600; }
/* ICÔNE DES DEUX NOUVEAUX LIENS (favoris, comparateur) — même trait, même
   gabarit que `.bouton-icone svg` dans la barre d'actions au-dessus de
   640px, pour que le tiroir ne parle pas un langage graphique différent. */
.menu.est-tiroir .menu-secondaire-lien svg { width: 18px; height: 18px; flex: none; color: var(--encre-tenue); }
.menu.est-tiroir .menu-groupe { width: 100%; }

/* ─── LE FOND DU TIROIR ──────────────────────────────────────────────────────
   Écrit une seule fois, non gaté par une largeur : `fond.hidden` (posé par
   `shop.js`) décide seul de sa présence, dans les deux cas où le tiroir existe.
   Il était dupliqué sous deux media queries, avec les mêmes valeurs au chiffre
   près — la définition même d'une règle qui n'avait aucune raison d'être
   écrite deux fois.

   LE FOND FLOUTE AU LIEU D'ASSOMBRIR. Une teinte sombre bascule toute la page
   dans un gris uniforme et écrase les repères au lieu de les reculer ; le flou
   garde les masses et les couleurs, on reconnaît sa page derrière le panneau. */
.tiroir-fond {
  position: fixed; inset: 0; z-index: 80;
  /* `100dvh` EN PLUS DE `inset: 0` (20/08/2026) : sur mobile, la hauteur de
     la fenêtre change quand la barre d'adresse se rétracte au défilement —
     `dvh` (dynamic viewport height) suit ce changement, `vh` seul peut rester
     calé sur une valeur figée au premier rendu selon le navigateur. Sans
     effet sur un navigateur qui ne connaît pas `dvh` : la règle est juste
     ignorée, `inset: 0` continue de porter seul. */
  height: 100vh; height: 100dvh; width: 100vw;
  /* VOILE + FLOU FRANC. Le flou avait été ramené à 2px le matin du 25/08 pour
     calmer un « tout est flouté » dont la vraie cause était ailleurs (voir la
     règle d'empilement juste en dessous) : le fond floutait le tiroir lui-même.
     Cause corrigée, le fond ne touche plus que la page — il peut donc reculer
     franchement ce qu'il y a derrière, ce que Jesse demandait le même jour
     (« seul le fond doit être flou, et plus flou »). */
  background: rgba(16, 16, 24, .34);
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
  border: 0; padding: 0;
}

/* ⚠️ LE TIROIR PASSAIT SOUS SON PROPRE FOND — c'est ça, « tout est flou »
   (25/08/2026). Diagnostic fait à l'écran, parce qu'il ne se lit dans aucune
   des deux règles prises séparément :

   `.tiroir-fond` est un enfant de <body> (z-index 80). Le tiroir, lui, est un
   enfant du bandeau — et `.bandeau` porte `position: sticky; z-index: 20`, donc
   il OUVRE UN CONTEXTE D'EMPILEMENT : le `z-index: 90` du tiroir ne le compare
   qu'à ses frères DANS le bandeau, jamais au fond. Tout le bandeau, tiroir
   compris, se peint donc au niveau 20 — sous le fond, qui le floute et
   l'assombrit avec la page. Le flou avait été baissé de 11 à 2px la veille pour
   rendre le symptôme supportable ; la cause, elle, était là.

   On remonte le BANDEAU (donc le tiroir avec lui) au-dessus du fond quand le
   tiroir est ouvert — et seulement là, pour ne rien changer au reste du temps.
   Le fond peut alors flouter franchement : il ne floute plus que la page. */
/* ⚠️ LE SECOND SÉLECTEUR N'EST PAS UNE REDONDANCE — c'est une question de
   POIDS, et elle se paie à l'écran (25/08/2026, deuxième passe : « tout est
   flou » revenait dès qu'on dépliait une rubrique du tiroir). Le mégamenu
   déclare `body.menu-ouvert .bandeau { z-index: 60 }` : la
   spécificité de `:has()` est celle de son argument le plus fort, donc cette
   règle pèse (0,3,1) contre (0,2,1) ici — elle gagnait, le bandeau retombait à
   60, sous le fond (80), et le tiroir se refloutait avec son propre voile dès
   qu'un sous-menu s'ouvrait. La variante ci-dessous porte le même argument
   `:has()` et passe donc devant. */
body.tiroir-ouvert .bandeau,
body.tiroir-ouvert.menu-tiroir-ouvert .bandeau { z-index: 90; }
/* L'EN-TÊTE, LUI, DOIT RECULER COMME LE RESTE : il est derrière le tiroir, pas
   dedans. Le fond ne l'atteint plus (il est passé au-dessus) — on le floute
   donc par son propre `filter`, exactement comme le mégamenu le fait avec ce
   qui n'est pas lui. `:not(.menu)` épargne le tiroir, qui EST le contenu au
   premier plan ; sans cette exclusion on aurait refait le bogue d'en face. */
/* Même question de poids que ci-dessus : sans la seconde ligne, l'annulation
   écrite pour le mégamenu (`filter: none` sur `.bandeau > *:not(.menu)` quand
   un groupe du tiroir est ouvert) rendait l'en-tête net au milieu d'une page
   floutée dès qu'on dépliait une rubrique. */
body.tiroir-ouvert .bandeau > .bandeau-haut,
body.tiroir-ouvert.menu-tiroir-ouvert .bandeau > .bandeau-haut {
  filter: blur(6px);
  /* Modal : tant que le tiroir est ouvert, rien d'autre ne se clique. Le bouton
     à trois traits est déjà masqué (voir `body.tiroir-ouvert .bandeau-menu`), la
     croix du tiroir et le fond restent les deux sorties. */
  pointer-events: none;
}
/* LA BULLE DE CHAT S'EFFACE PENDANT QUE LE TIROIR EST OUVERT. Elle est posée
   très haut dans l'empilement (c'est ce qu'on attend d'un widget de chat) : le
   fond ne l'atteint pas, et elle restait donc parfaitement nette au milieu
   d'une page floutée — le seul élément à donner l'impression d'un défaut. Elle
   revient à la fermeture, rien n'est perdu. */
body.tiroir-ouvert #bmp-chat-widget,
body.tiroir-ouvert .tawk-facade { display: none; }
/* Même chose pendant la recherche : sur téléphone, la bulle se posait sur le
   bas des suggestions et cachait le lien « Voir les N résultats » (26/09/2026). */
.recherche-ouverte #bmp-chat-widget,
.recherche-ouverte .tawk-facade { display: none; }
body.tiroir-ouvert { overflow: hidden; }

/* ==========================================================================
   MENU MOBILE — un tiroir, à partir du bouton à trois traits
   Sous 640 px, l'en-tête empilait cinq rangées avant le premier contenu :
   maintenance, annonces, logo, cinq icônes d'action, recherche et thème, puis
   la barre de rubriques. Le tiroir rassemble la navigation et rend cette place
   au catalogue.
   ========================================================================== */

/* Le bouton n'existe qu'en petit écran. Il est dans le balisage plutôt qu'ajouté
   par script : un bouton de navigation qui attend le JavaScript laisse, sur une
   connexion lente, une page sans issue. */
.bandeau-menu { display: none; }
/* Les liens secondaires du tiroir n'existent qu'en petit écran : au-dessus, la
   barre d'actions les porte déjà, et les afficher deux fois ferait deux chemins
   pour la même page. */
.menu-secondaire { display: none; }

/* La croix du panneau n'existe QUE lorsque le menu est un panneau. Sur la barre
   de rubriques horizontale, elle serait une croix posée au milieu d'une
   navigation qui n'a rien à fermer. */
.menu-fermer { display: none; }
/* Son intitulé non plus : sur la barre horizontale, un mot « Navigation »
   au-dessus des rubriques n'annoncerait rien qu'on ne voie déjà. */
.menu-entete { display: none; }

@media (max-width: 640px) {
  .bandeau-menu {
    position: absolute; left: var(--tiroir-ancrage, 16px); top: 50%; transform: translateY(-50%);
    display: inline-flex; align-items: center; justify-content: center;
    width: 38px; height: 38px; padding:0;
    border: 1px solid var(--filet); border-radius: var(--rayon);
    background: var(--surface); color: var(--encre); cursor: pointer;
  }
  /* LES DEUX ÉTATS SONT PORTÉS PAR LA MÊME SPÉCIFICITÉ. Une règle
     `.bandeau-menu svg { display: block }` vaut (0,1,1) et l'emportait sur
     `.bandeau-menu-croix { display: none }`, qui ne vaut que (0,1,0) : les deux
     icônes s'affichaient côte à côte. Deux sélecteurs de même poids ne
     s'annulent plus l'un l'autre. */
  .bandeau-menu .bandeau-menu-barres { display: block; }
  .bandeau-menu .bandeau-menu-croix { display: none; }
  .bandeau-menu[aria-expanded="true"] .bandeau-menu-barres { display: none; }
  .bandeau-menu[aria-expanded="true"] .bandeau-menu-croix { display: block; }

  /* Le logo reste centré : le bouton et les actions sont en absolu de part et
     d'autre, comme sur grand écran. */
  .bandeau-haut { position: relative; flex-direction: row; padding-top:12px; padding-bottom:12px; }
  .bandeau-actions { position: absolute; right: 12px; top: 50%; transform: translateY(-50%); gap:4px; }
  /* Seul le panier reste dans l'en-tête : c'est la seule action qu'on veut
     atteindre sans réfléchir. Le reste rejoint le tiroir. On le désigne par son
     lien, `a[href="/panier"]`, et non par une classe : il n'en portait pas, et
     ma première règle les masquait tous les six — panier compris. */
  .bandeau-actions > *:not([href="/panier"]) { display: none; }
  /* `.bandeau-outils` N'EST PLUS MASQUÉ ICI (26/09/2026) : il porte la loupe
     de la recherche mobile — voir le bloc « recherche » plus haut. */

  /* L'APPARENCE DU TIROIR EST ÉCRITE AILLEURS, une seule fois, sous
     `.menu.est-tiroir` — voir le bloc qui porte ce nom. Ne restent ici que les
     réglages propres au petit écran : le bouton, l'en-tête, les actions
     masquées et le voile. */

  /* UNE SEULE CROIX, ET C'EST CELLE DU PANNEAU. Le bouton de l'en-tête se
     changeait lui aussi en croix à l'ouverture : deux croix à l'écran pour une
     seule chose à fermer, et le regard doit choisir. Celle du panneau est la
     seule qui vaille à toutes les largeurs — sous 640 px le panneau centré
     recouvre l'en-tête, et la croix qui s'y trouve devient inatteignable.
     Le bouton s'efface donc le temps de l'ouverture. */
  body.tiroir-ouvert .bandeau-menu { display: none; }

  /* ⚠️ LE TIROIR EST CENTRÉ SUR LE TÉLÉPHONE (25/08/2026, « le sous-menu est
     mal centré »). Il est ancré à gauche sous son bouton (16 px), mais sa
     largeur était PLAFONNÉE à 360 px : passé 392 px de large, la marge de
     droite grandissait toute seule pendant que celle de gauche restait à 16.
     Sur un écran de 430 px — les grands téléphones d'aujourd'hui — cela faisait
     16 px d'un côté contre 54 de l'autre, et le panneau paraissait tomber vers
     la gauche sans qu'on sache pourquoi.

     On pose les DEUX bords au lieu d'une largeur : `left` et `right` valent le
     même ancrage, la largeur se déduit, et les marges restent égales quelle que
     soit la taille de l'écran. `right` plutôt qu'un `calc(100vw - 32px)` : la
     largeur de fenêtre en `vw` inclut la barre de défilement (invisible sur
     téléphone, bien réelle dans un navigateur de bureau réduit), ce qui
     redonnait un décalage de la largeur de cette barre.

     Le plafond de 360 px reste pour l'autre apparition du tiroir — sur grand
     écran défilé — où il n'est pas question qu'il traverse la page. */
  .menu.est-tiroir {
    right: var(--tiroir-ancrage, 16px);
    width: auto; max-width: none;
  }
}

@media (max-width: 640px) and (prefers-reduced-motion: reduce) {
  .menu { transition: none; }

}

/* ==========================================================================
   LE MÊME TIROIR, SUR GRAND ÉCRAN, UNE FOIS LA PAGE DÉFILÉE

   L'en-tête est collant : il accompagne le visiteur sur toute la page, et il y
   occupe deux rangées — le nom de la boutique, puis la barre de rubriques. Une
   fois qu'on lit des produits, cette seconde rangée coûte trente pixels de
   hauteur en permanence pour une navigation dont on ne se sert plus. Elle se
   replie donc derrière le bouton à trois traits, à gauche, exactement comme sur
   téléphone.

   LA DUPLICATION QUI VIVAIT ICI A ÉTÉ RETIRÉE (18/08/2026). Le tiroir mobile
   dépend d'une LARGEUR (`@media`), celui-ci d'un ÉTAT (`.est-defile`) : les deux
   ne se combinent pas dans un sélecteur, et la feuille portait donc les mêmes
   règles en deux exemplaires — qui avaient commencé à diverger, comme toujours.
   C'est `shop.js` qui tranche désormais : il pose `est-tiroir` sur le menu dans
   les deux cas, et l'apparence s'écrit une fois sous ce nom.

   Ne restent séparés que les réglages VRAIMENT propres à chaque contexte : la
   place du bouton, ce que l'en-tête masque, et le voile.
   ========================================================================== */
@media (min-width: 641px) {
  /* `--tiroir-ancrage` REMPLACE DEUX VALEURS ÉCRITES EN DUR (20/08/2026). Le
     bouton et le panneau étaient chacun posés à `left: 24px`, en toutes
     lettres, à deux endroits différents de cette feuille — une coïncidence de
     valeurs, pas un lien. Ajouter la cocarde à côté du bouton (voir plus bas)
     a demandé de décaler le bouton vers la droite ; SANS ce jeton, il aurait
     fallu penser à répéter le même changement sur le panneau, et l'oublier
     est précisément ce qui a fait dériver les deux valeurs par le passé —
     Jesse a vu le panneau décroché du bouton à plusieurs reprises. Avec le
     jeton, une seule ligne pilote les deux : ils ne peuvent plus se
     désynchroniser. */
  .bandeau { --tiroir-ancrage: 24px; }

  .bandeau.est-defile .bandeau-menu {
    /* `top: 50%` + `translateY(-50%)` REMPLACE UN `top: 22px` ÉCRIT EN DUR
       (20/08/2026, « header au scroll : moins de marges ») : la même astuce
       de centrage que `.bandeau-outils` juste en dessous, pour la même
       raison — un bouton calé à une hauteur fixe se recentrerait mal (ou
       déborderait) au premier changement du padding vertical de l'en-tête,
       ce qui est exactement ce que cette demande vient de faire. Centré, le
       bouton suit tout seul. */
    position: absolute; left: var(--tiroir-ancrage); top: 50%; transform: translateY(-50%);
    display: inline-flex; align-items: center; justify-content: center;
    width: 38px; height: 38px; padding: 0;
    border: 1px solid var(--filet); border-radius: var(--rayon);
    background: var(--surface); color: var(--encre); cursor: pointer;
    z-index: 2;
  }
  .bandeau.est-defile .bandeau-menu .bandeau-menu-barres { display: block; }
  .bandeau.est-defile .bandeau-menu .bandeau-menu-croix { display: none; }
  .bandeau.est-defile .bandeau-menu[aria-expanded="true"] .bandeau-menu-barres { display: none; }
  .bandeau.est-defile .bandeau-menu[aria-expanded="true"] .bandeau-menu-croix { display: block; }

  /* ─── LES BOUTONS S'ACCORDENT À LA BARRE ÉPINGLÉE ────────────────────────
     (25/08/2026, « le blanc dénote trop du fond crème ».)

     En haut de page, les pastilles d'action sont posées sur le fond du site :
     un fond `--surface` opaque y est exactement ce qu'il faut. Épinglée, la
     barre change de nature — elle devient un verre teinté d'accent
     (`.bandeau.est-defile::before`) posé sur la page qui défile dessous. Des
     pastilles opaques y flottent comme des vignettes blanches collées sur du
     crème : elles ne partagent plus rien avec ce qu'elles habitent.

     On garde la forme (même taille, même rayon, même cible tactile) et on rend
     la MATIÈRE translucide : le verre teinté transparaît, le bouton s'y pose au
     lieu de s'y superposer. Au survol il redevient franc — c'est le retour
     d'information qui dit qu'on le vise.

     Tout est en `color-mix` de jetons, jamais en valeurs figées : sur une
     boutique dont l'accent est bleu, la même règle donne un verre bleuté. */
  /* ⚠️ DOSAGE REPRIS À L'ŒIL, DEUX FOIS (25/08/2026). Premier essai à 38 % de
     surface : « ça ne dénote pas assez » — le bouton avait disparu dans le
     verre, on ne voyait plus qu'un pictogramme flottant. Le bon réglage n'est
     ni le blanc plein (« dénote trop ») ni la transparence : c'est une pastille
     FRANCHE mais TEINTÉE — assez opaque pour se lire comme un bouton, assez
     translucide pour prendre la couleur du verre qui est derrière. */
  .bandeau.est-defile .bouton-icone,
  .bandeau.est-defile .bandeau-menu {
    background: color-mix(in srgb, var(--surface) 72%, transparent);
    border-color: color-mix(in srgb, var(--encre) 20%, transparent);
    transition: background-color .15s ease, border-color .15s ease;
  }
  .bandeau.est-defile .bouton-icone:hover,
  .bandeau.est-defile .bouton-icone:focus-visible,
  .bandeau.est-defile .bandeau-menu:hover,
  .bandeau.est-defile .bandeau-menu:focus-visible {
    background: color-mix(in srgb, var(--surface) 95%, transparent);
    border-color: color-mix(in srgb, var(--encre) 32%, transparent);
  }
  /* LE BADGE D'AVIS SUIT LE MÊME PRINCIPE, sans quoi il resterait la seule
     pastille pleine de la barre — et le seul rectangle clair du côté gauche.
     Son dégradé doré est conservé, simplement dilué de la même façon. */
  .bandeau.est-defile .bandeau-badge-avis {
    background:
      linear-gradient(180deg,
        color-mix(in srgb, var(--etoile) 9%, color-mix(in srgb, var(--surface) 70%, transparent)),
        color-mix(in srgb, var(--etoile) 18%, color-mix(in srgb, var(--surface) 70%, transparent)));
    border-color: color-mix(in srgb, var(--etoile) 34%, transparent);
    box-shadow: none;
  }
  .bandeau.est-defile .bandeau-badge-avis:hover,
  .bandeau.est-defile .bandeau-badge-avis:focus-visible {
    background:
      linear-gradient(180deg,
        color-mix(in srgb, var(--etoile) 10%, color-mix(in srgb, var(--surface) 92%, transparent)),
        color-mix(in srgb, var(--etoile) 20%, color-mix(in srgb, var(--surface) 92%, transparent)));
  }

  /* L'APPARENCE DU TIROIR EST ÉCRITE AILLEURS, une seule fois, sous
     `.menu.est-tiroir` : c'est `shop.js` qui pose cette classe dans les DEUX
     cas où le menu se replie — sous 640 px et ici, page défilée. Ce bloc ne
     garde donc que ce qui est propre au grand écran défilé : le bouton, la
     place du logo, le voile et le blocage du défilement. */

  /* Le nom de la boutique cède la place au bouton : il est centré, le bouton
     est en absolu à gauche, rien ne se chevauche. */
  /* MOINS DE MARGES QU'EN HAUT DE PAGE (20/08/2026, « trop de marges top et
     bottom navbar fixe au scroll ») : `.bandeau-haut` garde son padding
     20px/20px de base, pensé pour le grand logo au repos — une fois réduit
     et l'en-tête épinglé, cet espace ne sert plus qu'à agrandir une barre
     qui doit au contraire se faire discrète. Le bouton de menu et le groupe
     d'outils se recentrent tout seuls (`top: 50%` + `translateY(-50%)`, voir
     plus haut) : rien à recalculer côté positionnement en réduisant ceci. */
  .bandeau.est-defile .bandeau-haut { position: relative; padding-top: 10px; padding-bottom: 10px; }

  /* LE SÉLECTEUR PORTE `.bandeau.est-defile` PAR NÉCESSITÉ, PAS PAR HABITUDE.
     Écrit `body.tiroir-ouvert .bandeau-menu`, il pèse deux classes contre les
     trois de `.bandeau.est-defile .bandeau-menu` quelques lignes plus haut : la
     règle serait perdue en silence, et le bouton resterait affiché. */
  body.tiroir-ouvert .bandeau.est-defile .bandeau-menu { display: none; }

  /* LA RECHERCHE NE DISPARAISSAIT PAS : ELLE ÉTAIT RECOUVERTE. Le bouton de
     menu et le groupe d'outils étaient tous deux posés en absolu à `left: 24px`.
     Le bouton, ajouté plus tard avec un fond opaque et un `z-index`, se posait
     donc exactement sur la loupe — au défilement, la barre de recherche semblait
     s'évanouir alors qu'elle était intacte, dessous. Le groupe commence après
     le bouton : 24px de marge + 38px de bouton + 12px d'écart. */
  .bandeau.est-defile .bandeau-outils { left: 74px; }

  /* PLUS DE SECONDE COPIE DU BADGE ICI (20/08/2026, cinquième passe — « le
     badge ne doit pas changer au scroll »). Il vivait auparavant à gauche du
     bouton de menu, icône seule, ce qui poussait `--tiroir-ancrage` à 82px
     pour lui faire de la place. Le badge reste maintenant dans le groupe
     d'outils dans les deux états, `--tiroir-ancrage` retombe donc sur la
     valeur de base (24px, définie plus haut) : le bouton et le panneau
     qu'il ouvre n'ont plus jamais à composer avec sa largeur. */
}
@media (min-width: 641px) and (prefers-reduced-motion: reduce) {
  .bandeau.est-defile .menu { transition: none; }
}

/* ─── SUR TRÈS GRAND ÉCRAN, LE TIROIR REVIENT AU CENTRE (20/08/2026) ─────────
   Jesse, en le voyant sur son iMac : « ça doit s'adapter en fonction de la
   résolution... sur mon iMac là ça fait bizarre ». Le 18/08 avait ancré le
   panneau sous le bouton pour le rattacher visuellement à ce qui l'ouvre —
   pertinent sur un téléphone, où bouton et panneau se touchent presque. Sur
   un très grand écran, ce même ancrage laisse un petit panneau collé dans un
   coin d'un espace bien plus large : il paraît perdu, pas rattaché. Centré,
   il redevient un point focal — le compromis qu'« adapter selon la
   résolution » demande, plutôt qu'un choix unique pour toutes les largeurs.

   SEUL LE MODE DÉFILÉ DE GRAND ÉCRAN EST CONCERNÉ (`.bandeau.est-defile`,
   posé par shop.js) : sous 1024 px — téléphone, tablette, fenêtre de travail
   réduite — le panneau reste sous le bouton, ancrage qui a du sens à cette
   échelle. Les deux sélecteurs pèsent plus que les règles qu'ils corrigent
   (quatre classes contre deux) : ils gagnent par spécificité, pas par ordre
   d'écriture — les déplacer plus haut dans la feuille ne changerait rien. */
@media (min-width: 1024px) {
  .bandeau.est-defile .menu.est-tiroir {
    left: 50%; right: auto;
    transform-origin: top center;
    transform: translateX(-50%) translateY(-6px) scale(.97);
  }
  .bandeau.est-defile .menu.est-tiroir.est-ouvert {
    transform: translateX(-50%);
  }
}

/* ==========================================================================
   FILTRES EN TIROIR SOUS 900 PX
   La colonne passait en pleine largeur au-dessus des produits : sur une
   rubrique à six dimensions, une trentaine de lignes de cases à cocher avant
   le premier article. Même procédé que la navigation.
   ========================================================================== */
.rubrique-filtrer { display: none; }

@media (max-width: 900px) {
  .rubrique-filtrer {
    display: inline-flex; align-items: center; gap:8px;
    padding:9px 15px; cursor: pointer;
    border: 1px solid var(--filet-fort); border-radius: 999px;
    background: var(--surface); color: var(--encre);
    font: inherit; font-size: 14.5px; font-weight: 550;
  }
  .rubrique-filtrer:hover { border-color: var(--accent); }
  /* Le compte de filtres actifs : un panneau fermé cache ses choix, et sans ce
     chiffre on ne sait plus ce qui filtre la liste qu'on regarde. */
  .rubrique-filtrer-nb {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 19px; height: 19px; padding:0 6px;
    border-radius: 999px; background: var(--accent); color: var(--accent-encre);
    font-size: 12px; font-weight: 650;
  }
  .rubrique-barre { justify-content: space-between; gap:12px; }

  .filtres {
    position: fixed; inset: 0 auto 0 0; z-index: 90;
    width: min(86vw, 340px);
    padding:18px 18px 32px;
    background: var(--surface);
    border-right: 1px solid var(--filet);
    box-shadow: 6px 0 28px rgba(16, 16, 24, .14);
    overflow-y: auto;
    transform: translateX(-102%);
    transition: transform .26s cubic-bezier(.2, .7, .3, 1);
  }
  .filtres.est-ouvert { transform: translateX(0); }
  /* En bandeau horizontal, les déclencheurs dépliants n'ont plus de sens dans
     un tiroir étroit : les blocs reprennent leur forme de colonne. */
  .filtres--horizontal { display: block; }
  .filtres--horizontal .filtre-depliant > summary { display: none; }
  .filtres--horizontal .filtre-depliant .filtre-depliant-panneau {
    position: static; display: block; padding:0; border: 0; box-shadow: none;
  }
  .filtres--horizontal .filtre-depliant-panneau .filtre-valeurs { grid-template-columns: 1fr; }
}

@media (max-width: 900px) and (prefers-reduced-motion: reduce) {
  .filtres { transition: none; }
}

/* ===========================================================================
   LISTE D'ENVIES ET COMPARATEUR
   Deux actions qui ne sont pas « acheter », et qui doivent donc se voir sans
   concurrencer le prix ni le bouton d'achat.
=========================================================================== */

/* L'enveloppe existe pour porter les actions HORS du lien de la carte, tout en
   les superposant dessus. `position: relative` ici et absolue sur les actions :
   sans l'enveloppe, elles se caleraient sur le premier ancêtre positionné,
   c'est-à-dire n'importe où dans la grille. */
.carte-enveloppe { position: relative; display: flex; }
.carte-enveloppe > .carte-produit { flex: 1; min-width: 0; }

.produit-actions {
  position: absolute; top: 10px; right: 10px;
  display: flex; flex-direction: column; gap: 6px;
  /* La grille de cartes reçoit le survol sur toute sa surface : sans cela, le
     conteneur des boutons masquerait le lien sur une bande de 40 px. */
  pointer-events: none;
}
.produit-actions .produit-action { pointer-events: auto; margin: 0; }

.produit-favori,
.produit-comparer {
  display: grid; place-items: center;
  width: 34px; height: 34px; padding: 0;
  border: 1px solid var(--filet); border-radius: 999px;
  /* PLUS DE `backdrop-filter` ICI (26/08/2026, audit de performance). Deux
     boutons par carte, seize cartes par page : trente-deux calques de flou
     posés sur des visuels qui bougent au survol et à côté des bandeaux
     défilants — le navigateur refait ces trente-deux flous à chaque image.
     Un fond à 94 % fait le même travail à l'œil, pour rien. */
  background: color-mix(in srgb, var(--surface) 94%, transparent);
  color: var(--encre-douce); cursor: pointer;
  transition: color .14s ease, border-color .14s ease, transform .14s ease;
}
.produit-favori:hover,
.produit-comparer:hover { color: var(--encre); border-color: var(--filet-fort); transform: scale(1.06); }
/* L'ACTIF SE VOIT À LA COULEUR ET AU REMPLISSAGE, pas à la couleur seule : un
   cœur qui ne change que de teinte est indistinguable pour qui ne perçoit pas
   le rouge. Le tracé se remplit, ce qui reste lisible en niveaux de gris. */
/* Le cœur actif prend le MÊME bordeaux que le compte du panier : les deux
   disent la même chose — « vous avez mis quelque chose de côté ». */
.produit-favori.est-actif { color: var(--bordeaux); border-color: currentColor; }
.produit-comparer.est-actif { color: var(--accent-texte); border-color: currentColor; }

/* Sur la fiche produit, les deux actions passent sous le bouton d'achat et
   deviennent des boutons à libellé : il y a la place, et un cœur seul au
   milieu d'une colonne ne dit pas ce qu'il fait. */
/* LES TROIS BOUTONS D'ACHAT FORMENT UN BLOC, PAS TROIS ÉLÉMENTS EMPILÉS.
   « Ajouter au panier » occupait toute la colonne pendant que les deux actions
   du dessous s'arrêtaient à la longueur de leur texte : trois bords droits à
   trois abscisses différentes, ce qui se lit comme un défaut de mise en page
   plutôt que comme une hiérarchie.

   C'EST LE BOUTON QUI SE RÈGLE SUR LES ACTIONS, et non l'inverse. La première
   version étirait les deux actions à parts égales sur toute la colonne : les
   bords s'alignaient, mais en donnant à « Ajouter à ma liste » la même
   importance visuelle qu'au bouton d'achat. Le conteneur `.fiche-achat-bloc`
   prend la largeur de son contenu le plus large — la rangée d'actions — et le
   bouton, en largeur 100 %, s'y ajuste. */
.fiche-achat-bloc { width: fit-content; max-width: 100%; }
.fiche-achat .produit-actions {
  position: static; margin-top: 12px; pointer-events: auto;
  display: flex; flex-direction: row; gap: 10px;
}
/* LA HAUTEUR EST ÉCRITE ICI, ET C'EST LE SEUL ENDROIT OÙ ELLE TIENT. Elle
   avait d'abord été posée à côté de `.bouton-large`, plus haut dans le
   fichier : ce bloc-ci, qui vient après et déclare `height: auto` à
   spécificité égale, l'annulait — sans erreur, et sans que rien ne le montre
   ailleurs que sur deux pixels d'écart. */
.fiche-achat .produit-favori,
.fiche-achat .produit-comparer {
  width: auto; height: 40px; gap: 8px;
  grid-auto-flow: column; padding: 0 14px;
  border-radius: var(--rayon); font-size: 14px;
}
.fiche-achat .produit-actions .sr-only {
  position: static; width: auto; height: auto; margin: 0;
  overflow: visible; clip: auto; white-space: nowrap;
}

/* ------------------------------------------------------------- comparateur */
.comparer-outils { display: flex; justify-content: flex-end; margin-bottom: 16px; }
/* Le tableau défile DANS son cadre, jamais la page : trois colonnes de produit
   dépassent la largeur d'un téléphone, et faire défiler le document entier
   ferait perdre la colonne des libellés. */
.comparer-cadre { overflow-x: auto; padding-bottom: 8px; }
.comparer {
  width: 100%; border-collapse: collapse;
  min-width: 560px; font-size: 14.5px;
}
.comparer :is(th, td) {
  padding: 14px 16px; text-align: left; vertical-align: top;
  border-bottom: 1px solid var(--filet);
}
.comparer tbody th[scope="row"] {
  width: 190px; font-weight: 500; color: var(--encre-tenue);
  /* La colonne des libellés reste visible pendant le défilement latéral :
     c'est elle qui donne son sens à chaque ligne. */
  position: sticky; left: 0; background: var(--fond);
}
.comparer thead th { border-bottom: 1px solid var(--filet-fort); }
.comparer-produit { display: flex; flex-direction: column; gap: 8px; width: 180px; }
.comparer-visuel {
  display: block; aspect-ratio: 1; border-radius: var(--rayon);
  border: 1px solid var(--filet); background: var(--fond-doux); overflow: hidden;
}
.comparer-visuel img { width: 100%; height: 100%; object-fit: cover; }
.comparer-visuel span { display: grid; place-items: center; height: 100%; font-size: 13px; color: var(--encre-tenue); }
.comparer-nom { color: inherit; text-decoration: none; font-weight: 600; line-height: 1.35; }
.comparer-nom:hover { text-decoration: underline; }
.comparer-retirer button {
  padding: 0; border: 0; background: none; cursor: pointer;
  color: var(--encre-tenue); font: inherit; font-size: 13px; text-decoration: underline;
}
.comparer-retirer button:hover { color: var(--encre); }
.comparer-absent { color: var(--encre-tenue); }
.comparer-ligne-prix .prix { font-size: 18px; }
.comparer-communes td { background: var(--fond-doux); }
.comparer-communes summary { cursor: pointer; color: var(--encre-douce); font-size: 14px; }
.comparer-communes dl {
  display: grid; grid-template-columns: 190px 1fr; gap: 6px 16px;
  margin: 12px 0 0; font-size: 14px;
}
.comparer-communes dt { color: var(--encre-tenue); }
.comparer-communes dd { margin: 0; }
.comparer-note { margin-top: 20px; color: var(--encre-tenue); font-size: 14px; }

/* ------------------------------------------------------------- retours --
   Le bloc de retour sur le détail d'une commande, et le formulaire de demande.
   Les classes sont posées en même temps que le gabarit : c'est la règle du
   projet depuis les 138 classes trouvées sans règle. */
/* LE BLOC EST UN ENCART DE MÊME FAMILLE QUE LES AUTRES DE LA PAGE — même
   rayon, même filet, même fond que la frise et le numéro de suivi. Il en
   différait sur les trois, ce qui suffit à le faire lire comme une pièce
   rapportée : trois boîtes qui se suivent et dont aucune n'a le même dessin.

   Sa structure est ÉCRITE, elle aussi : un intitulé, la liste des retours déjà
   faits, puis l'action. En simple `flex` avec un enfant à `100 %`, l'état du
   retour occupait une première ligne pleine largeur et le bouton se retrouvait
   dessous sans rien pour dire lequel commande l'autre. */
.commande-retour {
  margin: 0 0 24px; padding: 16px 18px;
  border: 1px solid var(--filet); border-radius: var(--rayon-large);
  background: var(--fond-doux);
}
.commande-retour-titre {
  display: block; margin-bottom: 10px;
  font-size: 11.5px; text-transform: uppercase; letter-spacing: .05em;
  font-weight: 600; color: var(--encre-tenue);
}
.commande-retour-liste {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-direction: column; gap: 8px;
}
.commande-retour-liste li {
  display: flex; align-items: center; flex-wrap: wrap; gap: 10px; font-size: 14px;
}
.commande-retour-ref { font-weight: 600; letter-spacing: .02em; }
.commande-retour-rembourse { font-size: 13.5px; color: var(--encre-tenue); }
.commande-retour-action { display: flex; align-items: center; flex-wrap: wrap; gap: 14px; }
/* Le filet ne sépare que s'il y a deux choses à séparer. */
.commande-retour-liste + .commande-retour-action {
  margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--filet);
}
.commande-retour-delai { font-size: 13.5px; color: var(--encre-tenue); }

.retour-lignes { display: flex; flex-direction: column; gap: 12px; margin-bottom: 24px; }
.retour-ligne {
  display: flex; align-items: center; justify-content: space-between;
  flex-wrap: wrap; gap: 12px;
  padding: 14px 16px; border: 1px solid var(--filet); border-radius: var(--rayon);
}
/* Une ligne déjà retournée reste VISIBLE mais en retrait : la masquer ferait
   croire que l'article n'a jamais été commandé. */
.retour-ligne.est-epuisee { opacity: .6; }
.retour-ligne-nom { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.retour-ligne-variante { font-size: 13px; color: var(--encre-tenue); }
.retour-ligne-prix { font-size: 13px; color: var(--encre-tenue); }
.retour-ligne-etat { font-size: 13.5px; color: var(--encre-tenue); }
.retour-ligne-qte { display: flex; align-items: center; gap: 10px; font-size: 13.5px; }
.retour-ligne-qte select {
  font: inherit; font-size: 14px; padding: 7px 10px;
  border: 1px solid var(--filet-fort); border-radius: var(--rayon);
  background: var(--surface); color: var(--encre);
}
.retour-rappel {
  margin: 4px 0 20px; padding: 14px 16px;
  border-left: 2px solid var(--filet-fort); border-radius: var(--rayon);
  background: var(--fond-doux);
  font-size: 13.5px; line-height: 1.6; color: var(--encre-douce);
}
.retour-rappel p { margin: 0 0 8px; }
.retour-rappel p:last-child { margin-bottom: 0; }

/* Alerte de retour en stock : le bloc prend la place du bouton d'achat sur un
   article épuisé. Il est encadré pour se distinguer du message « épuisé » qui
   le précède — sans cadre, les deux se lisent comme un seul paragraphe. */
.alerte-stock {
  margin-top: 16px; padding: 16px;
  border: 1px solid var(--filet); border-radius: var(--rayon);
  background: var(--fond-doux);
}
.alerte-stock-titre { margin: 0 0 4px; font-weight: 600; font-size: 15px; }
.alerte-stock-aide { margin: 0 0 12px; font-size: 13px; color: var(--encre-tenue); line-height: 1.5; }
.alerte-stock-champ { display: flex; gap: 8px; flex-wrap: wrap; }
.alerte-stock-champ input {
  flex: 1 1 200px; min-width: 0;
  font: inherit; font-size: 15px; padding: 10px 12px;
  border: 1px solid var(--filet-fort); border-radius: var(--rayon);
  background: var(--surface); color: var(--encre);
}
.alerte-stock-champ .bouton { flex: 0 0 auto; }

/* Les documents téléchargeables d'une commande, côté client. Deux boutons de
   rang différent : la facture est ce qu'on vient chercher, le bon de livraison
   ne sert qu'en cas de litige avec le transporteur. */
.commande-documents { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 20px; }

/* LE LIEN D'ÉVITEMENT — invisible jusqu'à la tabulation.
   Il est décalé hors écran plutôt que masqué : `display: none` et
   `visibility: hidden` retirent l'élément de l'ordre de tabulation, donc le
   rendent inatteignable — un lien d'évitement qu'on ne peut pas atteindre est
   la définition d'un ornement. */
.lien-evitement {
  /* `fixed` ET NON `absolute`, et la différence n'est pas théorique : en
     absolu, le lien se place par rapport au HAUT DU DOCUMENT. Un visiteur qui
     tabule après avoir défilé le verrait apparaître plusieurs écrans plus
     haut — c'est-à-dire nulle part. Mesuré : après un défilement, le lien
     « révélé » restait à une ordonnée négative. En fixe, il se montre toujours
     dans la fenêtre, ce qui est la seule chose qu'on lui demande. */
  position: fixed; left: 8px; top: -100px; z-index: 100;
  padding: 10px 16px; border-radius: var(--rayon);
  background: var(--accent); color: var(--accent-encre);
  font-size: 14px; font-weight: 550; text-decoration: none;
  transition: top .12s ease;
}
.lien-evitement:focus { top: 8px; }
/* La cible reçoit le focus par programme (tabindex="-1") : sans cela, certains
   navigateurs déplacent le défilement sans déplacer le focus, et la tabulation
   suivante repart du haut de la page — le lien semble alors ne rien faire. */
#contenu:focus { outline: none; }

/* ---------------------------------------------------------------------------
   TRANSITION D'OUVERTURE DE PAGE

   Par l'API View Transitions du navigateur, pas par une animation maison. La
   différence n'est pas cosmétique : un fondu appliqué au corps ferait démarrer
   la page à une opacité nulle, et un élément d'opacité nulle N'EST PAS
   CONSIDÉRÉ COMME PEINT — le LCP se décalerait de toute la durée de
   l'animation, sur chaque page, y compris la première.

   `@view-transition` n'agit qu'ENTRE deux navigations : le premier affichage
   n'est pas touché, donc le LCP non plus. Les navigateurs qui ne connaissent
   pas la règle l'ignorent.

   AUCUN MOUVEMENT, UN FONDU SEUL. La première version faisait entrer la page
   par un `translateY(8px)` — un déplacement, donc, et il se voyait : à chaque
   navigation le contenu se recalait de huit pixels sous les yeux du lecteur.
   Un glissement de cette amplitude n'apporte rien qu'un fondu ne dise déjà,
   et il coûte le seul défaut qu'une transition ne doit jamais avoir : donner
   l'impression que la page n'est pas encore posée.

   Le fondu croisé est raccourci dans la foulée : à 200 ms l'entrée se
   remarquait, à 140 elle se sent sans se voir.
--------------------------------------------------------------------------- */
@media (prefers-reduced-motion: no-preference) {
  @view-transition { navigation: auto; }

  ::view-transition-old(root) {
    animation: bmp-sortie 90ms linear both;
  }
  ::view-transition-new(root) {
    animation: bmp-entree 140ms linear both;
  }
}

@keyframes bmp-sortie { to { opacity: 0; } }
@keyframes bmp-entree { from { opacity: 0; } }

/* ---------------------------------------------------------------------------
   L'ÉCHELLE DE LA PAGE DE CONNEXION SUR GRAND ÉCRAN

   La page occupe toute la fenêtre depuis qu'elle est coupée en deux, mais son
   contenu était resté dimensionné pour une carte centrée : titre de 24 px,
   formulaire borné à 400. Sur un écran de 2000 px, deux blocs de cette taille
   flottent au milieu de deux grandes surfaces vides — chaque élément est
   correct, l'ensemble paraît minuscule.

   TOUT PASSE PAR clamp(), jamais par une requête de média. Une bascule à un
   seuil donné produit un saut visible au redimensionnement, et il faudrait
   autant de seuils que de tailles d'écran.

   La borne basse est exactement la valeur d'origine, donc rien de ce qui a été
   réglé pour mobile et tablette n'est touché. La croissance commence là où le
   terme en `vw` dépasse cette borne — autour de 960 px pour les titres, plus
   haut pour les textes courants — et non à un seuil unique : chaque élément
   grandit quand SA taille d'origine commence à paraître petite, ce qui n'est
   pas au même moment pour un titre de 24 px et un libellé de 14.

   Le tout reste dans `.auth:not(.auth--simple)` : l'inscription est une carte
   ordinaire, pas un partage plein écran, et l'agrandir la ferait déborder.
--------------------------------------------------------------------------- */
.auth:not(.auth--simple) .auth-carte > * {
  max-width: clamp(400px, 27vw, 540px);
}
.auth:not(.auth--simple) .auth-carte-entete { margin-bottom: clamp(24px, 2.4vw, 34px); }
.auth:not(.auth--simple) .auth-carte-entete h1 {
  font-size: clamp(24px, 2.5vw, 36px);
  margin-bottom: clamp(8px, .8vw, 12px);
}
.auth:not(.auth--simple) .auth-carte-lead { font-size: clamp(14.5px, 1.05vw, 17.5px); }

.auth:not(.auth--simple) .auth-ouverture-texte { max-width: clamp(420px, 32vw, 600px); }
.auth:not(.auth--simple) .auth-ouverture-titre { font-size: clamp(25px, 2.7vw, 40px); }
.auth:not(.auth--simple) .auth-ouverture-lead { font-size: clamp(15px, 1.2vw, 19px); }
.auth:not(.auth--simple) .auth-ouverture-points { margin-top: clamp(20px, 2vw, 30px); gap: clamp(9px, .9vw, 14px); }
.auth:not(.auth--simple) .auth-ouverture-points li {
  font-size: clamp(14px, 1.05vw, 17px);
  padding-left: clamp(24px, 1.8vw, 30px);
}
/* La coche suit le texte : figée à 5×9, elle devient un détail sous une ligne
   de 17 px, et la liste perd son alignement optique. */
.auth:not(.auth--simple) .auth-ouverture-points li::before {
  width: clamp(5px, .38vw, 7px);
  height: clamp(9px, .68vw, 12px);
  top: clamp(5px, .45vw, 7px);
}

/* LES CHAMPS GRANDISSENT AVEC LE RESTE, sinon ce sont eux qui paraissent
   rapetisser. Un formulaire dont les libellés grossissent mais pas les zones
   de saisie donne l'impression d'un gabarit cassé. */
.auth:not(.auth--simple) .account-form .form-label { font-size: clamp(14px, 1.05vw, 16.5px); }
.auth:not(.auth--simple) .account-form .form-input {
  font-size: clamp(15px, 1.1vw, 17.5px);
  padding-top: clamp(11px, .95vw, 15px);
  padding-bottom: clamp(11px, .95vw, 15px);
}
.auth:not(.auth--simple) .account-form .form-checkbox { font-size: clamp(13.5px, 1vw, 16px); }
.auth:not(.auth--simple) .account-form .btn-lg {
  font-size: clamp(15px, 1.15vw, 18px);
  padding-top: clamp(13px, 1.1vw, 18px);
  padding-bottom: clamp(13px, 1.1vw, 18px);
}
.auth:not(.auth--simple) .form-secondary-link { font-size: clamp(14px, 1.05vw, 16.5px); }

/* --- Retirer un article de la liste d'envies ---
   Un bouton ÉCRIT sous chaque carte. Le cœur en surimpression suffit partout
   ailleurs — sur une rubrique il met de côté — mais ici il fait l'inverse, et
   une icône qui change de sens selon la page est une icône qu'on n'ose pas
   cliquer. Le mot lève l'ambiguïté.

   IL EST DANS LA CARTE, PAS SOUS ELLE. Détaché, avec son propre cadre et sa
   propre hauteur de bouton plein, il pesait autant que le produit qu'il
   accompagne : une rangée de trois cartes se lisait comme trois produits et
   trois boutons, alors qu'on vient regarder des produits. C'est le BLOC qui
   porte désormais le cadre, la carte y perd le sien, et le retrait devient un
   pied de carte séparé par un simple filet — présent, jamais premier.

   Conséquence à ne pas oublier : le survol change de propriétaire. La carte
   soulevait sa propre boîte ; si elle continuait, elle glisserait toute seule
   en laissant son pied sur place. C'est donc `.favori-bloc` qui se soulève, et
   la carte qui ne bouge plus. */
.favori-bloc {
  display: flex; flex-direction: column;
  border: 1px solid var(--filet); border-radius: var(--rayon-large);
  background: var(--surface); overflow: hidden;
  transition: border-color .15s ease, transform .15s ease, box-shadow .15s ease;
}
.favori-bloc:hover {
  border-color: var(--filet-fort);
  transform: translateY(-3px);
  box-shadow: 0 10px 26px rgba(16, 16, 24, .09);
}
.favori-bloc > .carte-enveloppe { flex: 1; }
.favori-bloc .carte-produit { border: 0; border-radius: 0; }
.favori-bloc .carte-produit:hover { transform: none; box-shadow: none; }
@media (prefers-reduced-motion: reduce) {
  .favori-bloc, .favori-bloc:hover { transition: none; transform: none; }
}

.favori-retirer { margin: 0; border-top: 1px solid var(--filet); }
.favori-retirer button {
  display: flex; align-items: center; justify-content: center; gap: 6px;
  width: 100%;
  /* 38 px : la cible tactile reste bien au-delà des 24 px exigés par WCAG 2.2
     §2.5.8. Ce qui a maigri, c'est le dessin — pas la zone de clic. */
  min-height: 38px;
  padding: 0 12px;
  border: 0; background: transparent;
  color: var(--encre-tenue);
  font: inherit; font-size: 13px;
  cursor: pointer;
  transition: background-color .15s ease, color .15s ease;
}
.favori-retirer button svg { opacity: .7; }
/* Le rouge n'apparaît qu'à l'intention, pas au repos : une rangée de pieds
   rouges sous chaque carte ferait d'une liste d'envies un tableau de bord
   d'alertes. */
.favori-retirer button:hover,
.favori-retirer button:focus-visible {
  /* `--alerte` est le jeton du projet ; `--danger` n'existe pas dans cette
     feuille, et l'écrire aurait donné une couleur de repli codée en dur qui
     aurait cessé de suivre le thème au premier changement de charte. */
  background: var(--fond-doux);
  color: var(--alerte);
  outline: none;
}
.favori-retirer button:hover svg, .favori-retirer button:focus-visible svg { opacity: 1; }

/* --- L'écran de connexion du back-office ---
   Il reprend `.auth` de la boutique : même partage en deux, même dégradé, mêmes
   champs. Deux différences seulement, et chacune a sa raison.

   `--collant: 0` : la règle de base réserve la hauteur de l'en-tête collant du
   site. Cette page n'en a pas, et sans cette remise à zéro le partage
   s'arrêterait cent pixels avant le bas de l'écran — une bande vide sous une
   mise en page qui se veut pleine fenêtre. */
.auth--pleine { --collant: 0px; }

/* Le logo est DANS la carte, contrairement à l'écran client : celui-là est
   précédé de l'en-tête du site, qui le porte déjà. Ici rien ne dit de quelle
   boutique on ouvre l'administration — ce qui compte quand on en gère
   plusieurs. Même verrou qu'ailleurs : pastille et nom sur une ligne. */
.auth-logo {
  display: flex; align-items: center; gap: 10px;
  margin-bottom: clamp(16px, 1.6vw, 24px);
}
/* UNE IMAGE, pas une lettre habillée (25/08/2026) : la marque générée, la même
   que l'onglet et que la barre latérale. Le fond, le rayon et la fonte vivent
   dans le dessin — une seule définition de la silhouette. */
.auth-logo-pastille {
  width: 38px; height: 38px; flex: none; display: block;
}
.auth-logo-nom {
  font-size: clamp(19px, 1.5vw, 23px); font-weight: 600;
  letter-spacing: -0.02em; color: var(--encre);
}

/* ---------------------------------------------------------------------------
   LES COMMENTAIRES DU JOURNAL
--------------------------------------------------------------------------- */
/* LA MÊME COLONNE QUE L'ARTICLE — 760 px, exactement la valeur de `.article`
   et non « à peu près la même mesure ».

   C'était `72ch`, choisi pour la lisibilité, et c'était le bon raisonnement
   appliqué au mauvais endroit : `ch` dépend de la police et du corps, il
   tombait vers 630 px, et la discussion se retrouvait CENTRÉE dans une colonne
   plus étroite que l'article — donc décalée d'une soixantaine de pixels vers
   la droite. Sur une page où tout le reste partage un bord gauche, ce décalage
   se voit avant qu'on sache le nommer. La mesure de lecture reste assurée par
   la largeur des blocs à l'intérieur, pas par celle de la section. */
.commentaires {
  max-width: 760px;
  margin: clamp(40px, 5vw, 72px) auto 0;
  padding-top: clamp(28px, 3vw, 44px);
  border-top: 1px solid var(--filet);
}
.commentaires h2 { font-size: clamp(22px, 2vw, 27px); margin: 0 0 20px; }
.commentaires-vide { color: var(--encre-tenue); margin: 0 0 28px; }

.commentaires-liste, .commentaires-reponses { list-style: none; margin: 0; padding: 0; }
.commentaires-liste > li + li { margin-top: 22px; }
.commentaire {
  padding: 16px 18px;
  border: 1px solid var(--filet);
  border-radius: var(--rayon);
  background: var(--fond-doux);
}
.commentaire-entete {
  display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
  margin-bottom: 6px;
}
.commentaire-auteur { font-weight: 600; }
.commentaire-date { font-size: 13px; color: var(--encre-tenue); }
.commentaire-texte { margin: 0; line-height: 1.6; }

/* La réponse de la boutique, rattachée par un filet à gauche : elle appartient
   au commentaire, elle ne lui succède pas. L'accent la distingue d'un autre
   visiteur — sans quoi le mot de l'exploitant perd son autorité. */
.commentaire-reponse {
  margin-top: 14px; padding-left: 14px;
  border-left: 3px solid var(--accent);
}
.commentaire-reponse-qui {
  display: block; font-weight: 600; font-size: 13.5px;
  color: var(--accent-texte); margin-bottom: 4px;
}
.commentaire-reponse p { margin: 0; line-height: 1.6; }

/* Les réponses sont décalées d'un cran, et d'UN SEUL : au-delà, une discussion
   s'indente jusqu'à devenir illisible sur téléphone. Le modèle aplatit déjà
   toute réponse de troisième niveau, la feuille n'a donc qu'un cas à traiter. */
.commentaires-reponses { margin-top: 14px; padding-left: clamp(14px, 2vw, 28px); }
.commentaires-reponses > li + li { margin-top: 12px; }
.commentaires-reponses .commentaire { background: var(--surface); }

/* LE FORMULAIRE EST UNE CARTE, pas une suite de champs posés sur le fond de la
   page. Nu, il se confondait avec la discussion qui le précède : même fond,
   même largeur, aucune frontière — on ne voyait pas où finissait la lecture et
   où commençait l'écriture. Le cadre le dit sans un mot de plus. */
.commentaire-formulaire {
  margin-top: clamp(32px, 4vw, 48px);
  padding: clamp(18px, 2.4vw, 26px);
  border: 1px solid var(--filet); border-radius: var(--rayon-large);
  background: var(--surface);
}
.commentaire-formulaire h3 { font-size: 17px; margin: 0 0 8px; }
.commentaire-note { color: var(--encre-tenue); font-size: 14px; margin: 0 0 20px; line-height: 1.55; }
/* « Vous commentez en tant que … » n'est pas une phrase de plus : c'est l'état
   du formulaire. Il est donc posé comme tel, sur le fond doux, et non perdu
   entre deux paragraphes de la même couleur. */
.commentaire-identite {
  margin: 0 0 18px; padding: 9px 12px;
  border-radius: var(--rayon); background: var(--fond-doux);
  color: var(--encre-douce); font-size: 14px;
}
.commentaire-formulaire textarea.form-input { resize: vertical; min-height: 120px; }
/* Le dernier champ ne garde pas la marge basse qui le séparait du suivant :
   sous elle, la barre d'envoi flottait à vingt pixels du cadre. */
.commentaire-formulaire .form-group:last-of-type { margin-bottom: 0; }
.commentaire-envoi {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px; flex-wrap: wrap; margin-top: 18px;
}
.commentaire-envoi-note { font-size: 13px; color: var(--encre-tenue); margin: 0; }

/* LE PIÈGE À ROBOTS. `display: none` suffirait à le cacher, mais certains
   robots ignorent précisément les champs ainsi masqués — c'est le premier test
   qu'ils font. On le sort de l'écran plutôt que de le supprimer du rendu : il
   reste un champ ordinaire pour qui lit le HTML, et invisible pour qui regarde
   la page. `aria-hidden` et `tabindex="-1"` le retirent du chemin d'un lecteur
   d'écran et du clavier, sans quoi on piégerait des visiteurs légitimes. */
.piege-robots {
  position: absolute;
  left: -9999px;
  width: 1px; height: 1px;
  overflow: hidden;
}

/* ---------------------------------------------------------------------------
   LA BARRE DE COMPARAISON

   Elle répond à un défaut précis : ajouter un produit à la comparaison ne
   produisait rien à l'écran. Le bouton de la carte passait au bleu, la session
   enregistrait bien le produit, et c'était tout — aucun chemin vers l'écran de
   comparaison, aucun compteur qui bouge. On ajoutait deux produits et l'on
   concluait, très logiquement, que ça ne marchait pas.

   EN BAS ET NON EN HAUT. Le haut de l'écran est déjà pris par un bandeau
   `sticky` qui porte le logo, le menu et six actions ; y ajouter une septième
   bande aurait mangé un tiers de la hauteur utile sur un portable. Le bas est
   libre, et c'est là que se posent les barres d'action de tous les tunnels
   d'achat — y compris le nôtre.
--------------------------------------------------------------------------- */
.comparateur-barre {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 46;
  background: var(--surface);
  border-top: 1px solid var(--filet);
  box-shadow: 0 -8px 30px rgba(16, 16, 24, .10);
}
.comparateur-barre[hidden] { display: none; }
.comparateur-barre-corps {
  max-width: var(--largeur); margin:0 auto; padding:12px 24px;
  display: flex; align-items: center; justify-content: space-between; gap:16px; flex-wrap: wrap;
}
.comparateur-barre-etat { margin:0; font-size: 14px; }
.comparateur-barre-etat [data-comparateur-compte] { font-weight: 650; }
.comparateur-barre-aide { color: var(--encre-tenue); }
.comparateur-barre-aide[hidden] { display: none; }
.comparateur-barre-actions { display: flex; align-items: center; gap:12px; }
.comparateur-barre-vider { margin:0; }
.comparateur-barre-vider button {
  border: 0; background: none; padding:6px 4px;
  font: inherit; font-size: 13.5px; color: var(--encre-tenue);
  text-decoration: underline; text-underline-offset: 3px; cursor: pointer;
}
.comparateur-barre-vider button:hover, .comparateur-barre-vider button:focus-visible {
  color: var(--alerte); outline: none;
}
.comparateur-barre-ouvrir { padding:9px 18px; font-size: 14px; }
/* UN SEUL PRODUIT : le lien reste affiché mais inerte. `aria-disabled` ne
   désactive rien tout seul — c'est une annonce faite au lecteur d'écran, et
   sans `pointer-events` on cliquerait quand même vers un écran qui n'a rien à
   comparer. Le masquer aurait été pire : le bouton apparaîtrait d'un coup au
   second ajout, sans qu'on ait compris qu'il attendait. */
.comparateur-barre-ouvrir[aria-disabled="true"] {
  opacity: .45; pointer-events: none;
}

/* LE PIED DE PAGE DOIT POUVOIR SE LIRE EN ENTIER. Une barre fixe recouvre la
   fin du document : sur la dernière rubrique du pied, les liens passaient
   dessous et devenaient inatteignables. On rend au corps la hauteur qu'elle
   occupe, et seulement quand elle est là. */
body.comparateur-visible { padding-bottom: 68px; }

/* LA BULLE DE CHAT REMONTE AU-DESSUS DE LA BARRE. Les deux sont fixées en bas
   à droite, et la bulle a un z-index de 9999 : elle se posait donc PAR-DESSUS
   la barre, à cheval sur « Vider ». Aucune des deux n'est en tort — c'est la
   dernière arrivée qui cède, et elle a de la place au-dessus. Le décalage
   n'existe que quand la barre est là : `:has` le lit sur le document, sans une
   ligne de script. */
body.comparateur-visible #bmp-chat-widget,
body.comparateur-visible .tawk-facade { bottom: 92px; }
@media (max-width: 480px) {
  body.comparateur-visible #bmp-chat-widget,
  body.comparateur-visible .tawk-facade { bottom: 84px; }
}

@media (max-width: 640px) {
  .comparateur-barre-corps { padding:10px 16px; gap:10px; }
  .comparateur-barre-etat { flex: 1 1 100%; font-size: 13.5px; }
  .comparateur-barre-actions { flex: 1 1 100%; justify-content: space-between; }
}

/* ---------------------------------------------------------------------------
   LE CHOIX DU MOYEN DE PAIEMENT, ET LE RIB D'UN VIREMENT
--------------------------------------------------------------------------- */

/* LA LISTE N'APPARAÎT QUE S'IL Y A UN CHOIX À FAIRE — le gabarit s'en charge.
   Un seul moyen disponible, et une liste à un élément coché d'office ne demande
   rien : elle occupe l'écran juste avant le bouton de paiement, à l'endroit où
   toute hésitation coûte le plus cher. */
/* LE BLOC RESPIRE DES DEUX CÔTÉS. Il n'avait de marge qu'EN HAUT : la dernière
   option touchait donc le bouton « Valider », les deux formant un pavé continu
   où l'on ne distinguait plus ce qu'on choisit de ce qu'on déclenche. C'est
   l'écart le plus important de la page — le dernier geste avant de payer. */
.moyens-paiement {
  margin:26px 0 22px; padding:0; border: 0;
  display: flex; flex-direction: column; gap:8px;
}
.moyens-paiement legend {
  padding:0; margin-bottom:12px;
  font-size: 15px; font-weight: 600;
}
/* `align-items: center` et non `flex-start` : l'icône, le rond du bouton radio
   et le nom sont sur la même ligne optique. Alignés en haut, l'icône flottait
   au-dessus du texte quand l'accroche passait à deux lignes. */
.moyen-paiement {
  display: flex; align-items: center; gap:12px;
  padding:12px 14px;
  border: 1px solid var(--filet); border-radius: var(--rayon);
  background: var(--surface); cursor: pointer;
  transition: border-color .15s ease, background-color .15s ease;
}
/* Les réseaux de cartes d'un moyen, en badges officiels au bout de la ligne.
   22px de haut : les fichiers sont des CARTES dessinées (fond et coins
   compris), pas des logos nus — plus grands, ils écrasent la ligne. */
.moyen-paiement-cartes { display: flex; gap: 6px; margin-left: auto; flex-shrink: 0; }
.moyen-paiement-cartes img { height: 22px; width: auto; display: block; }
.moyen-paiement:hover { border-color: var(--filet-fort); }
/* LE CHOIX RETENU SE VOIT SUR TOUTE LA CARTE, pas seulement dans le rond du
   bouton radio. Sur un écran où l'on vient de saisir six champs, un point de
   trois pixels ne suffit pas à répondre à « qu'est-ce que j'ai coché, déjà ? ».
   `:has()` double la classe posée par le serveur : celle-ci rend le bon état au
   chargement, celui-là suit le clic sans recharger. */
.moyen-paiement.est-choisi,
.moyen-paiement:has(input:checked) {
  border-color: var(--accent); background: var(--accent-doux);
}
.moyen-paiement input { accent-color: var(--accent); flex: none; margin:0; }

/* L'ICÔNE DANS SA PASTILLE. Une boîte de taille fixe, quel que soit le dessin
   qu'elle reçoit : un tracé au trait et un logo de marque n'ont ni les mêmes
   proportions ni les mêmes marges internes, et sans cadre commun la rangée
   d'options se met à onduler d'une ligne à l'autre. */
.moyen-paiement-icone {
  flex: none; width: 38px; height: 38px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 9px; background: var(--fond-doux);
  color: var(--encre-douce);
}
.moyen-paiement-icone svg { width: 21px; height: 21px; }
/* Le logo de marque est CONTENU, jamais recadré : `contain` garde ses
   proportions, et la marge l'empêche de toucher les bords de la pastille. */
.moyen-paiement-icone img { width: 26px; height: 26px; object-fit: contain; display: block; }
.moyen-paiement.est-choisi .moyen-paiement-icone,
.moyen-paiement:has(input:checked) .moyen-paiement-icone {
  background: var(--surface); color: var(--accent-texte);
}
.moyen-paiement-texte { display: flex; flex-direction: column; gap:2px; min-width: 0; }
.moyen-paiement-nom { font-weight: 550; font-size: 14.5px; }
.moyen-paiement-detail { font-size: 13px; color: var(--encre-tenue); }

/* --- Le RIB, sur la page de commande ---
   IL EST TRAITÉ COMME UN DOCUMENT À RECOPIER, pas comme un encart d'ambiance :
   fond distinct, montant et référence en évidence, IBAN en chasse fixe. C'est
   la seule chose que le client vient chercher sur cette page. */
.virement-encart {
  /* Même mesure que le récapitulatif qui le suit, et de l'air entre les deux
     (16/08/2026, « intégration étrange ») : à 560 centré contre 640, les deux
     cartes se décalaient, et le zéro de marge basse les faisait se toucher. */
  margin:28px 0 18px; padding:20px 22px;
  border: 1px solid var(--filet-fort); border-radius: var(--rayon-large);
  background: var(--fond-doux); text-align: left;
}
.virement-titre { margin:0 0 8px; font-size: 17px; }
.virement-intro { margin:0 0 16px; font-size: 14px; color: var(--encre-douce); line-height: 1.55; }
.virement-lignes { margin:0; display: flex; flex-direction: column; gap:8px; }
.virement-ligne { display: flex; align-items: baseline; gap:14px; }
.virement-ligne dt { flex: 0 0 150px; font-size: 13px; color: var(--encre-tenue); }
.virement-ligne dd { margin:0; font-size: 14.5px; }
/* Le montant et la référence portent tout : ce sont les deux seules valeurs
   qu'un virement de travers rend inutilisables. */
.virement-ligne-forte dd { font-size: 16px; }
.virement-encart code {
  font-size: 14px; letter-spacing: .03em;
  padding:2px 6px; border-radius: 5px;
  background: var(--surface); border: 1px solid var(--filet);
}
.virement-rappel {
  margin:16px 0 0; padding-top:14px; border-top: 1px solid var(--filet);
  font-size: 13.5px; color: var(--encre-douce); line-height: 1.55;
}
.virement-mot { margin:12px 0 0; font-size: 13.5px; color: var(--encre-tenue); line-height: 1.55; }

@media (max-width: 520px) {
  /* En dessous, l'intitulé et la valeur ne tiennent plus sur une ligne : un
     IBAN passerait à la ligne au milieu d'un groupe de quatre. */
  .virement-ligne { flex-direction: column; gap:2px; }
  .virement-ligne dt { flex: none; }
}

/* ---------------------------------------------------------------------------
   LE BLOC DE FAQ DANS UNE PAGE QUI N'EST PAS LA FAQ

   Il reprend `.faq-question` et `.faq-reponse` de la page de FAQ — mêmes
   chevrons, mêmes filets, même rythme. Un second accordéon dessiné autrement
   sur le même site se remarque, et le jour où l'on corrige l'un on oublie
   l'autre. Seule l'enveloppe est propre à ce bloc.

   LA MESURE DE LECTURE EST TENUE ICI, et non par `.mesure-texte` comme sur la
   page de FAQ : ce bloc vit sous une grille de produits ou sous une galerie,
   c'est-à-dire dans un conteneur beaucoup plus large. Sans largeur maximale,
   une réponse de trois lignes s'étalait sur 1 180 px — illisible, et surtout
   incohérent avec le texte de bas de rubrique juste au-dessus.
--------------------------------------------------------------------------- */
/* ALIGNÉ À GAUCHE, PAS CENTRÉ, et sur la MÊME MESURE que ses voisins. Centré
   sur 760 px, le bloc ne partageait son bord gauche avec rien : sur une page de
   rubrique, le texte de bas de rayon commence au bord du conteneur et fait
   72ch, la FAQ démarrait trente pixels plus loin. C'est exactement le décalage
   déjà corrigé sur les commentaires du journal — même cause, même remède, et
   `72ch` suffit ici parce que ces blocs partagent la taille de police du corps
   de page, ce qui n'était pas le cas là-bas. */
.faq-bloc {
  max-width: 72ch; margin:0;
  /* PLUS DE FILET AU-DESSUS. Le bloc en portait un, et la section qui le
     précède en pose déjà un : deux traits à quelques pixels d'écart, dont on
     cherche ce qu'ils séparent. C'est l'espace qui sépare, pas le trait. */
  padding:44px 0 48px;
}
/* Dans le bas de rubrique, c'est l'enveloppe qui porte le filet et la marge :
   la FAQ n'y est plus un bloc de fin de page mais une colonne parmi deux. */
/* ⚠️ LE DÉBORD EST RÉTABLI (20/09/2026). Il avait été mis à ZÉRO ici de peur
   qu'il ne morde sur la gouttière séparant les deux colonnes. Mesuré depuis :
   **la gouttière fait 64 px**, un débord de 16 px en laisse donc 48 — la
   crainte était excessive, et le remède pire que le mal : `--faq-debord: 0`
   annulait AUSSI le rembourrage (la même variable sert aux deux), si bien que
   la bande de survol épousait le texte au pixel près. La question semblait
   collée contre le bord de sa propre surbrillance.
   Une bande de survol doit entourer son texte, pas l'épouser. */
/* Le rembourrage horizontal RESTE (seul le vertical tombe à zéro) : c'est lui
   que la marge négative du sommaire rend, et sans lui la bande de survol sort
   de la colonne — 853→1502 px pour un bloc qui tient de 869 à 1486. */
.rubrique-bas .faq-bloc { padding:0 var(--faq-debord, 16px); max-width: none; }
.rubrique-bas .faq-bloc-titre { margin-top:0; }
/* LE TITRE RESPIRE. Il était à 8 px de la première question — moins que
   l'écart entre deux questions : le titre semblait appartenir à la première
   plutôt que de coiffer la liste. */
/* ⚠️ REPORTÉ SUR LE GROUPE (24/08/2026) : depuis l'icône + sous-titre ajoutés
   au-dessus (voir faq-bloc.php), le 22px qui séparait le titre de la liste
   vit sur `.carrousel-entete-groupe` — le mettre encore ici doublerait l'écart
   avec le gap déjà posé par `.carrousel-entete-contenu`. */
.faq-bloc-titre { font-size: var(--titre-h2); margin:0; }
/* 22 → 30px (24/08/2026, « manque de marges ») : l'en-tête à pastille pèse
   plus lourd qu'un titre nu, la liste doit s'en détacher davantage. */
.faq-bloc .carrousel-entete-groupe { margin-bottom: 30px; }
/* Le bloc porte déjà sa marge basse : celle de `.faq-liste`, prévue pour une
   page entière de thèmes, ajoutait 48 px de vide avant le pied de page. */
.faq-bloc .faq-liste { margin-bottom:0; }
/* Le rognage du premier rembourrage n'a plus lieu d'être : c'était une
   compensation du filet d'enveloppe, qui a disparu. */

@media (max-width: 820px) {
  .faq-bloc { padding:28px 0 36px; }
}

/* Sous 900 px, deux colonnes de trois cents pixels ne sont plus une mise en
   page : elles se remettent l'une sous l'autre, séparées par un filet — sans
   quoi le titre de la FAQ arriverait collé à la dernière ligne du texte. */
@media (max-width: 900px) {
  .rubrique-bas.est-double { grid-template-columns: minmax(0, 1fr); gap:0; }
  .rubrique-bas.est-double .rubrique-bas-colonne + .rubrique-bas-colonne {
    margin-top:32px; padding-top:28px; border-top: 1px solid var(--filet);
  }
}

/* Le captcha du formulaire de commentaires. Une marge au-dessus du bouton
   d'envoi, et rien d'autre : le widget est un iframe dessiné par Cloudflare,
   dont on ne contrôle ni les couleurs ni les dimensions. `data-theme="auto"` le
   fait suivre le thème du visiteur — c'est la seule prise qu'on ait. */
.commentaire-captcha { margin-top:18px; }

/* LES MARQUES DE PARTAGE PRENNENT LEURS COULEURS (13 août 2026).
   Même règle que sur les deux autres sites, et même raisonnement : c'est
   l'ICÔNE qui porte la couleur de marque, le LIBELLÉ garde l'encre du thème.
   La rangée reste sobre — le commentaire plus haut expliquait pourquoi dix
   pastilles colorées feraient des confettis, et il reste vrai du texte — mais
   chaque plateforme redevient identifiable sans lire.

   X garde `currentColor` : sa marque est noire sur clair et blanche sur
   sombre, lui fixer une valeur la casserait dans l'un des deux thèmes.

   Ces valeurs sont les officielles, NON assombries, contrairement aux teintes
   de survol du bloc ci-dessus. La raison est écrite là-haut : celles-là
   peignent du TEXTE, et un texte doit tenir 4,5:1. Une icône accompagnée de
   son libellé en toutes lettres est décorative au sens de WCAG 1.4.11. */
.partage-linkedin  .icone-reseau { color: #0A66C2; }
.partage-facebook  .icone-reseau { color: #1877F2; }
.partage-bluesky   .icone-reseau { color: #0285FF; }
.partage-reddit    .icone-reseau { color: #FF4500; }
.partage-whatsapp  .icone-reseau { color: #25D366; }
.partage-telegram  .icone-reseau { color: #26A5E4; }
.partage-pinterest .icone-reseau { color: #E60023; }

/* LE FONDU AUX BORDS DE LA RÉASSURANCE EST RETIRÉ (18/08/2026), et il faisait
   des dégâts très loin de ce qu'il visait.

   Il avait été écrit pour un bandeau de réassurance DÉFILANT : sur une piste
   qui tourne, des bords fondus laissent les entrées apparaître au lieu de
   surgir. Ce bandeau n'existe plus — la réassurance ne se rend aujourd'hui
   qu'en COLONNE, dans la fiche produit, le panier et le tunnel. Le masque, lui,
   est resté posé sur `.reassurance` tout court, donc sur le seul cas restant.

   Ce qu'on voyait : un masque `to right` efface les 6 % de gauche du bloc. Sur
   une colonne d'achat, ces 6 % tombent exactement sur la bande des pastilles —
   les textes commencent plus loin et y échappent. Chaque pastille se retrouvait
   donc traversée d'un fondu horizontal, que l'œil lit comme un DÉGRADÉ : trois
   carrés d'or qui pâlissent vers la gauche, alors que la règle qui les peint
   déclare un aplat net et le dit noir sur blanc.

   La leçon : un masque décoratif se pose sur la classe de LA PRÉSENTATION à
   laquelle il appartient (`.reassurance-ligne`, une piste animée…), jamais sur
   le bloc générique — sinon il survit à la présentation qui le justifiait et
   ressort ailleurs sous la forme d'un bogue qu'on ne relie plus à sa cause. */

/* ============================================================================
   LES BLOCS HÉRITÉS DE LA BOUTIQUE REPRISE (18/08/2026)

   POURQUOI CES RÈGLES EXISTENT. Un import PrestaShop apporte le HTML des
   textes, jamais la feuille de style qui l'habillait. Le contenu de
   bijoux-or-occasion est arrivé avec une petite bibliothèque de blocs —
   un encart « baguier », des paires image + texte, des listes d'avantages,
   des intertitres — dont AUCUNE classe n'avait de règle ici. Ils s'affichaient
   donc en HTML nu : une image en pleine largeur suivie de son texte au lieu de
   deux colonnes, une liste à puces là où il y avait des tuiles, un intertitre
   au corps du paragraphe.

   Un audit a comparé toutes les classes écrites dans le contenu à celles que
   cette feuille connaît : cent vingt étaient orphelines. C'est le même défaut
   que les accordéons de FAQ importés — invisible, parce que la page s'affiche.
   On habille ici ce qui a un sens éditorial ; le reste (enveloppes de
   copier-coller, classes utilitaires d'un autre framework) est RETIRÉ du
   contenu par tools/nettoyer-contenu-importe.php, parce qu'habiller du bruit
   revient à le garder.

   ⚠️ CES RÈGLES SONT PORTÉES PAR `.prose` ET SES VOISINES : elles ne doivent
   jamais s'appliquer hors d'un texte rédigé. Une classe aussi générique que
   `.texte` posée à la racine finirait par attraper autre chose.
   ========================================================================== */

/* ---- L'encart « baguier » : deux tuiles côte à côte -----------------------
   Il vit en bas de vingt-deux rubriques et pages de facette de bagues, et c'est
   un vrai outil de vente : mesurer sa taille avant d'acheter est LE frein d'un
   achat de bague en ligne. Il méritait mieux qu'une pile de textes nus. */
/* Les trois enveloppes dans lesquelles un texte rédigé peut vivre : la prose
   d'un article, le texte de bas de rubrique, et le CHAPÔ de haut de page de
   facette (`.sous-titre`). C'est dans le dernier que l'encart se trouve le plus
   souvent, et l'oublier laissait le bloc nu sur la moitié des pages qui le
   portent — vérifié à l'écran, pas supposé. */
:is(.prose, .sous-titre, .texte-rubrique, .fiche-encart) .boc-baguier-wrapper {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 12px; margin: 22px 0;
}
/* L'encart de rubrique sur la fiche produit (migration 081) : dans la colonne
   d'achat, les deux tuiles s'empilent dès que la colonne se resserre, et
   perdent la marge haute du texte courant. */
.fiche-encart { margin: 18px 0 4px; max-width: 560px; }
.fiche-encart .boc-baguier-wrapper { margin: 0; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }
.fiche-encart > :last-child { margin-bottom: 0; }
/* Deux tuiles de même hauteur : dans une colonne étroite, « Trouver votre
   taille de bague » passe sur deux lignes et sa voisine restait plus courte. */
.fiche-encart .boc-baguier-wrapper > div > a { height: 100%; box-sizing: border-box; }
/* UN TITRE, UNE LIGNE (24/09/2026, « ça doit tenir sur une ligne ») : dans la
   colonne d'achat, chaque tuile n'a que ~270 px. Au corps des pages de rubrique
   (14,5 px, icône 32 px, marges 16 px), « Trouver votre taille de bague »
   passait à la ligne. Tout est resserré d'un cran, ICI seulement : les pages
   de rubrique ont la place et gardent leur corps. */
.fiche-encart .boc-baguier-wrapper { gap: 10px; }
.fiche-encart .boc-baguier-wrapper > div > a { gap: 10px; padding: 11px 12px; }
.fiche-encart :is(.boc-baguier-link__icon, .boc-baguier-acheter__icon) { width: 24px; height: 24px; }
.fiche-encart :is(.boc-baguier-link__title, .boc-baguier-acheter__title) { font-size: 13.5px; white-space: nowrap; }
.fiche-encart :is(.boc-baguier-link__sub, .boc-baguier-acheter__sub) { font-size: 12.5px; line-height: 1.4; }
.fiche-encart .boc-baguier-acheter__price { font-size: 12.5px; }
/* Le chapô de haut de page court sur toute la largeur ; les deux tuiles, non. */
.sous-titre .boc-baguier-wrapper { max-width: 85ch; }
.boc-baguier-link > a,
.boc-baguier-acheter > a {
  display: flex; align-items: center; gap: 14px;
  padding: 14px 16px;
  border: 1px solid var(--filet); border-radius: var(--rayon-large);
  background: var(--surface); color: inherit; text-decoration: none;
  transition: border-color .15s ease, transform .15s ease;
}
.boc-baguier-link > a:hover,
.boc-baguier-acheter > a:hover {
  border-color: var(--accent); transform: translateY(-2px);
}
/* L'icône prend la couleur de l'accent PAR LE TEXTE : les tracés importés
   portaient un `#c29b5f` écrit en dur — l'or de la boutique d'origine. Sur une
   autre charte il aurait détonné, et personne ne serait allé le chercher dans
   une description de rubrique. L'outil de nettoyage les repasse en
   `currentColor`, et c'est cette règle qui décide alors de la teinte. */
.boc-baguier-link__icon,
.boc-baguier-acheter__icon { flex: none; color: var(--accent-texte); }
.boc-baguier-link__texts,
.boc-baguier-acheter__texts { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.boc-baguier-link__title,
.boc-baguier-acheter__title { font-weight: 600; font-size: 14.5px; color: var(--encre); }
.boc-baguier-link__sub,
.boc-baguier-acheter__sub { font-size: 13px; color: var(--encre-tenue); }
.boc-baguier-acheter__price { font-weight: 600; color: var(--accent-texte); font-size: 13.5px; }

/* ---- Image et texte côte à côte ------------------------------------------
   Huit articles s'en servent pour illustrer un point précis — un poinçon, une
   forme de serti. Sans règle, l'image passait en pleine largeur au-dessus de
   son texte : elle cessait d'illustrer le paragraphe pour devenir une coupure.
   `align-items: start` garde l'image en haut quand le texte est plus long ;
   sinon elle flotte au milieu d'un vide. */
:is(.prose, .sous-titre, .texte-rubrique) .image-texte {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr);
  gap: 20px; align-items: start; margin: 22px 0;
}
/* ⚠️ DEUX PIÈGES DU BALISAGE IMPORTÉ, vus seulement à l'écran.

   1. UN `<br>` TRAÎNE ENTRE L'IMAGE ET LE TEXTE. Invisible dans un flux normal,
      il devient un ÉLÉMENT DE GRILLE à part entière : il occupait la deuxième
      case, le bloc de texte était repoussé en deuxième ligne, et se retrouvait
      donc SOUS l'image dans une colonne de 308 px au lieu d'être à côté d'elle
      sur 430. On ne le masque pas par principe — il n'a aucun rôle ici.

   2. L'IMAGE N'EST PAS TOUJOURS UN ENFANT DIRECT. Selon les articles elle est
      posée nue, ou enveloppée dans un `<strong>` hérité de l'éditeur d'origine.
      `> img` n'attrapait que le premier cas, et l'image gardait alors sa
      largeur d'attribut — 210 px dans une colonne de 308. On vise donc l'image
      où qu'elle soit dans la colonne de gauche, en excluant `.texte`, la
      colonne de droite, où une illustration inline garde sa taille. */
:is(.prose, .sous-titre, .texte-rubrique) .image-texte > br { display: none; }
:is(.prose, .sous-titre, .texte-rubrique) .image-texte > img,
:is(.prose, .sous-titre, .texte-rubrique) .image-texte > *:not(.texte) img {
  display: block;
  width: 100%; height: auto; border-radius: var(--rayon);
}
:is(.prose, .sous-titre, .texte-rubrique) .image-texte .texte > :first-child { margin-top: 0; }
:is(.prose, .sous-titre, .texte-rubrique) .image-texte .texte > :last-child { margin-bottom: 0; }
/* Les largeurs d'origine (« un tiers », « une moitié ») ne valent plus rien :
   l'image occupe sa colonne, la colonne fait la proportion. On borne seulement
   le débordement, qu'une image de 1 200 px produirait sans cela. */
:is(.prose, .sous-titre, .texte-rubrique) :is(.img-1-3, .img-1-2) { max-width: 100%; height: auto; }
@media (max-width: 640px) {
  :is(.prose, .sous-titre, .texte-rubrique) .image-texte { grid-template-columns: 1fr; gap: 14px; }
}

/* ---- La liste d'avantages ------------------------------------------------
   « ✔ Paiement sécurisé », « ✔ Livraison gratuite » : une liste de réassurance
   rédigée dans le texte, sur huit rubriques. La puce du navigateur DOUBLAIT la
   coche déjà écrite dans le contenu — deux marqueurs par ligne. */
:is(.prose, .sous-titre, .texte-rubrique) .benefits-list { list-style: none; padding-left: 0; }
:is(.prose, .sous-titre, .texte-rubrique) .benefits-list li { margin-bottom: 6px; }

/* ---- Intertitre et paragraphe de pied des pages libres -------------------- */
:is(.prose, .sous-titre, .texte-rubrique) .page-subheading { font-size: 19px; margin: 28px 0 12px; }
:is(.prose, .sous-titre, .texte-rubrique) .bottom-indent { margin-bottom: 1.1em; }

/* ============================================================================
   SECTION TEXTE DE L'ACCUEIL (16/08/2026) — « une structure stylée, optimisée
   SEO » : le bloc de texte nu devient une bande éditoriale. Colonne de lecture
   bornée, titre qui pèse, et — quand l'éditeur a posé un visuel — deux
   colonnes qui se replient en une sur mobile.
   ========================================================================== */
/* Même rythme vertical que les autres sections d'accueil (8px haut / 40px bas) :
   le 44px de haut d'origine, ajouté aux 40px de bas de la section précédente,
   ouvrait un trou de 84px au-dessus du bloc — deux fois l'écart courant, d'où la
   marge « bizarre » signalée. La bande garde son identité éditoriale par son
   titre (26px) et sa colonne de lecture bornée, pas par un sur-espacement. */
.section-texte { padding: 8px 0 40px; }
.section-texte h2 { font-size: 26px; margin: 0 0 16px; letter-spacing: -0.015em; }
.section-texte .prose { max-width: 68ch; }
.section-texte-illustree {
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) minmax(240px, 1fr);
  gap: 40px;
  align-items: center;
}
/* LE FOND DE L'ILLUSTRATION EST POSÉ ICI, pas dans le fichier : un SVG servi
   par <img> est un document isolé, aucune variable de thème ne le traverse.
   Avec son propre fond clair, il devenait une tache lumineuse sur une page
   sombre. Le jeton, lui, suit les deux thèmes. */
.section-texte-illustree .section-texte-visuel {
  background: var(--fond-doux);
  border-radius: var(--rayon-large);
  overflow: hidden;
}
.section-texte-illustree .section-texte-visuel img {
  width: 100%; height: auto; display: block;
}
/* VISUEL À GAUCHE : on inverse l'ORDRE, pas les colonnes — le texte reste
   premier dans le document, donc premier à la lecture d'écran et premier sur
   mobile. Deux blocs éditoriaux qui se suivent alternent ainsi sans que la
   page devienne un escalier. */
.section-texte-inversee .section-texte-corps { order: 2; }
.section-texte-inversee .section-texte-visuel { order: 1; }
/* UNE PHOTO N'EST PAS UNE ILLUSTRATION (30/09/2026). Pleine colonne, elle
   écrasait le texte (deux fois sa hauteur) ; bornée à côté, elle laissait un
   grand vide entre la prose et elle. Désormais la prose court jusqu'à la photo,
   et la photo prend la HAUTEUR DU TEXTE (object-fit) — elle ne peut plus être
   disproportionnée, quelle que soit la longueur de la prose. Ni cadre ni fond
   (essayés et refusés) : son bord côté texte se fond dans la page, la photo
   sort de la prose au lieu d'être posée à côté. */
.section-texte-photo {
  grid-template-columns: minmax(0, 1fr) minmax(0, 44%);
  gap: 32px;
  align-items: stretch;
}
.section-texte-photo .section-texte-corps { align-self: center; }
.section-texte-photo .prose { max-width: none; }
.section-texte-photo .section-texte-visuel {
  position: relative;
  min-height: 260px;
  background: none;
  -webkit-mask-image: linear-gradient(to right, transparent 0, #000 30%);
          mask-image: linear-gradient(to right, transparent 0, #000 30%);
}
.section-texte-photo .section-texte-visuel img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  object-position: 60% 55%;
}
.section-texte-photo.section-texte-inversee { grid-template-columns: minmax(0, 44%) minmax(0, 1fr); }
.section-texte-photo.section-texte-inversee .section-texte-visuel {
  -webkit-mask-image: linear-gradient(to left, transparent 0, #000 30%);
          mask-image: linear-gradient(to left, transparent 0, #000 30%);
}
@media (max-width: 760px) {
  .section-texte { padding: 32px 0 24px; }
  .section-texte-illustree { grid-template-columns: 1fr; gap: 20px; }
  /* Le visuel APRÈS le texte : sur mobile il illustre, il n'introduit pas. */
  .section-texte-illustree .section-texte-visuel { order: 2; }
  .section-texte-inversee .section-texte-corps { order: 1; }
  .section-texte-photo, .section-texte-photo.section-texte-inversee { grid-template-columns: 1fr; gap: 20px; }
  /* Sous le texte, le fondu passe au bord HAUT : c'est lui qui touche la prose. */
  .section-texte-photo .section-texte-visuel,
  .section-texte-photo.section-texte-inversee .section-texte-visuel {
    min-height: 0; aspect-ratio: 3 / 2;
    -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 28%);
            mask-image: linear-gradient(to bottom, transparent 0, #000 28%);
  }
}

/* La FAQ de fin d'accueil : le même accordéon que partout (faq-bloc), avec la
   respiration d'une section de page et la porte vers la FAQ complète. */
/* AUCUN REMBOURRAGE EN HAUT. Deux sections qui se suivent additionnent leur
   rembourrage — celui du bas de la précédente et celui du haut de celle-ci — et
   l'écart obtenu n'est écrit nulle part : ni l'une ni l'autre des deux règles
   ne le montre. La section qui PRÉCÈDE porte déjà quarante pixels, ce qui est
   la respiration voulue entre deux blocs ; la FAQ n'a donc rien à ajouter
   au-dessus d'elle. */
.section-faq { padding: 0 0 44px; }
/* LE REMBOURRAGE NE SE CUMULE PAS. La section en pose, et le bloc de FAQ
   qu'elle contient en posait à son tour : 36 + 44 px en haut, à quoi s'ajoutait
   le bas de la section précédente — près de cent trente pixels de vide entre
   deux titres. Chacun des deux réglages était défendable isolément ; c'est leur
   addition qui ne l'était pas, et elle ne se voit sur aucune des deux règles.
   La section porte l'espacement, le bloc porte le contenu. */
.section-faq .faq-bloc { margin: 0; padding: 0; }
/* ─── LA BANDE « TEXTE SEO + FAQ » DE L'ACCUEIL (25/08/2026) ─────────────────
   Elle réutilise `.rubrique-bas.est-double`, écrit pour le bas des rubriques.
   Deux réglages de cette enveloppe n'ont pas de sens ici et sont neutralisés :
   le filet supérieur (l'accueil sépare déjà ses sections par leur espacement,
   pas par des traits — un trait de plus ferait une ligne isolée au milieu de la
   page) et les marges verticales, que `.section-faq` porte déjà (voir la note
   sur le cumul des rembourrages juste au-dessus). */
.section-faq .rubrique-bas.section-faq-double {
  margin: 0; padding-top: 0; border-top: 0;
}
.section-faq-lien { margin: 18px 0 0; }
.section-faq-lien a { color: var(--accent-texte); text-decoration: none; font-size: 14.5px; }
.section-faq-lien a:hover { text-decoration: underline; }

/* ============================================================================
   PASSE DESIGN DU FRONT (16/08/2026, « plus design, plus optimisé e-commerce »)
   — la carte produit s'équipe (badge sur le visuel, étoiles, urgence de stock,
   ajout rapide au survol), le hero prend de l'ampleur, les vignettes et les
   boutons gagnent la même profondeur. Tout est en jetons : les deux thèmes et
   les palettes du back-office suivent sans une règle de plus.
   ========================================================================== */

/* ----- Badge unique posé sur le visuel de la carte ----- */
.carte-visuel { position: relative; }
.carte-badge {
  position: absolute; top: 10px; left: 10px;
  padding: 4px 9px; border-radius: 999px;
  font-size: 12px; font-weight: 650; letter-spacing: .01em;
  line-height: 1.2; pointer-events: none;
}
/* Le badge de promotion est lui aussi POSÉ SUR LE VISUEL : même déclinaison. */
.carte-badge-promo { background: var(--accent-clair); color: var(--accent-encre); }
/* La nouveauté en encre pleine : elle se remarque sans concurrencer l'accent,
   réservé à ce qui se paie moins cher. */
.carte-badge-nouveau { background: var(--encre); color: var(--fond); }
.carte-badge-rupture { background: var(--surface); color: var(--encre-tenue); border: 1px solid var(--filet-fort); }
/* L'ÉTAT VA À DROITE, seul de son côté. Les trois badges précédents partagent
   le coin gauche parce qu'ils s'excluent — promo, rupture ou nouveauté, jamais
   deux à la fois. « Occasion » n'est pas de cette famille : il peut accompagner
   n'importe lequel des trois, et le poser au même endroit les ferait se
   chevaucher. Discret, en surface et non en aplat : il informe, il n'appelle
   pas au clic. */
.carte-badge-etat {
  /* EN BAS À GAUCHE, et c'est le seul emplacement libre de la vignette. Le coin
     haut-gauche appartient aux badges qui s'excluent (promo, rupture,
     nouveauté) ; le coin haut-DROIT est occupé par les boutons favori et
     comparateur, qui sont posés en frère de la carte et superposés par la
     feuille de style. Placé à droite, « Occasion » passait dessous et sortait
     tronqué en « Occa… » — un défaut invisible tant qu'on ne regarde pas une
     vraie grille, puisque rien ne déborde et que le texte est bien là. */
  top: auto; bottom: 10px; left: 10px;
  background: var(--surface); color: var(--encre-tenue);
  border: 1px solid var(--filet-fort);
  font-weight: 600;
}

/* La même mention sur la fiche, au-dessus du titre. Elle reprend la mesure des
   surtitres du site plutôt que la forme d'une pastille : sur une page, un
   badge posé au-dessus d'un H1 fait publicité, une ligne de surtitre fait
   information. */
/* L'ÉTAT EST UN BADGE, comme sur les vignettes de liste. Il était rendu en
   surtitre — petites capitales grises au-dessus du titre — et ne ressemblait
   donc à rien de ce que le visiteur venait de voir dans la grille, alors que
   c'est la MÊME information sur le MÊME produit. Reprendre la forme du badge
   fait le lien entre la liste et la fiche sans qu'on ait à l'expliquer. */
/* LA RÉFÉRENCE APPARTIENT AU TITRE. Elle héritait de la marge basse du h1,
   calibrée pour séparer un titre d'un PARAGRAPHE — pas d'une ligne technique de
   quatre mots qui le prolonge. Vingt-quatre pixels plus loin, elle semblait
   appartenir au prix qui suit. */
.fiche-achat h1 { margin-bottom: 6px; }
.fiche-achat h1 + .ligne-meta { margin-top: 0; }

.fiche-etat {
  display: inline-block;
  margin: 0 0 .6rem;
  padding: 4px 10px; border-radius: 999px;
  background: var(--surface); border: 1px solid var(--filet-fort);
  font-size: 11.5px; font-weight: 600; letter-spacing: .04em;
  text-transform: uppercase; color: var(--encre-douce);
  line-height: 1.2;
}

/* Une carte épuisée s'éteint un peu : l'œil file vers ce qui s'achète. */
.carte-enveloppe:has(.carte-badge-rupture) .carte-visuel img:not(.carte-visuel-survol) { opacity: .55; filter: grayscale(.35); }

/* ----- Étoiles de la carte ----- */
.carte-avis { display: inline-flex; align-items: center; gap: 6px; margin-top: -1px; }
.carte-avis .avis-etoiles.est-petite { font-size: 12.5px; gap: 1px; }
.carte-avis-nombre { font-size: 12px; color: var(--encre-tenue); }

/* ----- Urgence douce sur le stock ----- */
.carte-stock-faible {
  flex-basis: 100%;
  font-size: 12.5px; font-weight: 550;
  color: var(--etoile-encre, #C67C00);
}

/* ----- Ajout rapide : surgit au bas du visuel, au survol ----- */
.carte-ajout {
  position: absolute; top: 0; left: 0; right: 0;
  /* LA MÊME BOÎTE QUE `.carte-visuel`, et il faut que ça le reste : posé en
     absolu par-dessus le visuel, ce calque doit s'arrêter exactement où
     l'image s'arrête. Les deux valeurs ont divergé une fois — le bouton
     d'ajout rapide flottait alors au milieu du titre. D'où le jeton commun. */
  aspect-ratio: var(--marque-ratio-visuel, 4 / 5);
  /* La pastille se pose en BAS À DROITE : le bas gauche appartient au badge
     d'état (« Occasion »), qu'elle recouvrait sinon. */
  display: flex; align-items: flex-end; justify-content: flex-end;
  padding: 10px; margin: 0;
  pointer-events: none;                /* seul le bouton se clique */
  z-index: 2;
}
.carte-ajout-bouton {
  pointer-events: auto;
  /* PASTILLE RONDE DE 40 px. Le bouton faisait toute la largeur de la carte et
     pesait plus que le produit ; il se contente désormais de se proposer. */
  width: 40px; height: 40px; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  border: 0; border-radius: 50%;
  /* MÊME RÈGLE QUE LE HÉROS : cette pastille est posée sur la photo du produit,
     pas sur le fond de la page. Sur un bijou doré photographié sur fond clair,
     l'accent plein se fondait dans l'image ; l'éclairci s'en détache. */
  background: var(--accent-clair); color: var(--accent-encre);
  font: inherit;
  cursor: pointer;
  box-shadow: 0 6px 18px rgba(var(--marque-accent-rgb, 47, 91, 234), .35);
  opacity: 0; transform: translateY(8px);
  transition: opacity .18s ease, transform .18s ease, filter .15s ease;
}
.carte-enveloppe:hover .carte-ajout-bouton,
.carte-ajout-bouton:focus-visible { opacity: 1; transform: translateY(0); }
.carte-ajout-bouton:hover { filter: brightness(1.07); }
/* Sans pointeur qui survole (tactile), pas de bouton fantôme sur le visuel :
   la carte s'ouvre, la fiche fait le reste — c'est déjà son travail. */
@media (hover: none) {
  .carte-ajout { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .carte-ajout-bouton { transition: none; transform: none; }
}

/* ----- La carte elle-même : un cran de profondeur en plus ----- */
.carte-produit:hover { box-shadow: 0 14px 34px rgba(16, 16, 24, .12); }
[data-theme="dark"] .carte-produit:hover { box-shadow: 0 14px 34px rgba(0, 0, 0, .45); }

/* ----- Hero : l'échelle d'une vitrine ----- */
.hero { min-height: 460px; padding: 84px 0; }
.hero::before {
  background:
    linear-gradient(90deg, rgba(6, 8, 16, .64) 0%, rgba(6, 8, 16, .34) 40%, rgba(6, 8, 16, 0) 70%),
    linear-gradient(0deg, rgba(6, 8, 16, .38) 0%, rgba(6, 8, 16, 0) 30%);
}
.hero-titre {
  /* RAMENÉ DE 58 À 44 px, même raison que le h1 de page : l'échelle avait été
     calée sur une linéale. À 58 px, une sérif à fort contraste occupait la
     moitié de la hauteur de la bannière et écrasait tout ce qui la suit —
     l'accroche et le bouton, c'est-à-dire ce sur quoi on veut cliquer. */
  font-size: clamp(30px, 4.2vw, 44px);
  letter-spacing: -0.015em; line-height: 1.1;
  text-shadow: 0 2px 24px rgba(0, 0, 0, .25);
}
.hero-accroche { font-size: clamp(16px, 2vw, 19px); margin-bottom: 28px; }
.hero .bouton {
  /* LE HÉROS EST TOUJOURS SOMBRE — le gabarit y écrit `color: #FFFFFF` sans
     condition — et l'accent d'une marque est choisi pour porter du texte sur
     fond CLAIR. Sur une charte foncée (un or profond, un bordeaux, un vert
     bouteille), le bouton d'appel se fondait donc dans sa bannière : le libellé
     restait lisible, mais la forme ne se détachait plus, et c'est le seul
     bouton de la page d'accueil.

     On y emploie la variante NUIT, celle-là même que la palette calcule pour
     les fonds sombres, avec son encre associée. Sur une charte claire elle
     vaut à peu de chose près l'accent : rien ne change visiblement. */
  background: var(--marque-accent-nuit);
  /* ENCRE TOUJOURS LISIBLE sur ce grand CTA. La marque peut choisir le blanc
     partout ailleurs sur son accent (boo l'a fait sur son or le 20/08), mais
     ici — grand bouton posé sur une photo — le blanc sur un or clair se dissout.
     `--accent-encre-auto` est l'encre dérivée de l'accent (foncée sur clair,
     blanche sur sombre) ; on retombe sur l'encre de marque si elle n'est pas
     émise. Déclinable, et sans toucher aux autres boutons d'accent. */
  color: var(--accent-encre-auto, var(--marque-accent-encre-nuit));
  padding: 13px 26px; font-size: 15.5px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, .28);
}
.hero .bouton:hover, .hero .bouton:focus-visible {
  background: var(--marque-accent-nuit); filter: brightness(1.08);
}
.hero .bouton::after {
  content: '→'; font-weight: 500;
  transition: transform .15s ease;
}
.hero .bouton:hover::after { transform: translateX(3px); }
@media (max-width: 640px) {
  .hero { min-height: 340px; padding: 48px 0; }
  .hero-corps { padding:0 20px; }
}

/* CE QUI SUIT DOIT RESTER APRÈS LE BLOC CI-DESSUS. Les dispositions de bannière
   redéfinissent `min-height` et `padding` de `.hero` à spécificité ÉGALE : à
   cette égalité, c'est l'ordre du fichier qui tranche. Placées plus haut — là
   où vivent les autres règles du hero — elles étaient silencieusement annulées
   par la passe de design, et le bandeau sortait à 460 px au lieu de 440 avec
   84 px de rembourrage qu'il ne demandait pas. */

/* ═══ BANNIÈRE VIDÉO ET DISPOSITIONS (17/08/2026) ═════════════════════════
   La vidéo se glisse ENTRE le fond et le voile : le fond reste l'affiche —
   c'est lui qu'on voit tant que la vidéo n'a pas sa première image, et c'est
   lui que mesure le LCP — et le voile continue de garantir la lisibilité du
   texte par-dessus les deux. L'ordre d'empilement est écrit explicitement :
   sans lui, il dépend de l'ordre du document, qu'une retouche de gabarit
   déplacerait sans que personne ne fasse le lien. */
.hero-video {
  position: absolute; inset: 0; z-index: 0;
  width: 100%; height: 100%; object-fit: cover;
  /* Une vidéo est un élément remplacé : sans cela elle garde une ligne de base
     et laisse quelques pixels sous elle. */
  display: block;
}
.hero::before { z-index: 1; }
.hero-corps   { z-index: 2; }

.hero-surtitre {
  font-size: 12px; letter-spacing: .16em; text-transform: uppercase;
  color: var(--accent); margin: 0 0 12px; font-weight: 650;
  /* MÊME OMBRE QUE LE TITRE, et pour la même raison en pire : le surtitre est
     doré, donc clair, et il est posé plus HAUT que le titre — c'est-à-dire là
     où le voile vertical est le plus faible. Sur un plan d'atelier éclairé, il
     s'y confondait. */
  text-shadow: 0 2px 18px rgba(0, 0, 0, .55);
}
.hero-sans-visuel .hero-surtitre { color: var(--accent-texte); }

/* ── Bandeau : la disposition par défaut ──────────────────────────────────
   440 px au lieu d'un plein écran, et le texte en bas. Le premier écran n'est
   pas mangé par la bannière : les produits apparaissent dès le premier
   défilement, ce qui est le point d'une page d'accueil de BOUTIQUE. */
.hero-bandeau {
  min-height: 0; height: 440px; padding: 0;
  /* CENTRÉ VERTICALEMENT, ALIGNÉ À GAUCHE. Le bloc était calé en bas à gauche :
     sur un bandeau de 440 px, un texte d'angle se lit comme une légende de
     photo plutôt que comme l'accroche du site. Il remonte donc au milieu — mais
     il reste à gauche, sur la même verticale que tous les titres de la page
     (« Par univers », « Nouveautés »…). Un bloc centré horizontalement aurait
     ouvert une seconde colonne de lecture pour une seule section. */
  align-items: center;
}
/* LE VOILE SUIT LE TEXTE, ET C'EST TOUTE SA RAISON D'ÊTRE.
   Il a changé deux fois avec la mise en page, parce qu'un voile n'est pas une
   décoration : c'est ce qui garantit le contraste SOUS le texte, et il n'a donc
   de sens qu'à l'endroit où le texte se trouve. Vertical quand le texte était
   en bas, uniforme quand il est passé au milieu ; le texte étant maintenant au
   milieu À GAUCHE, le voile redevient horizontal — il assombrit la moitié
   gauche et s'efface sur la droite, où l'on veut voir l'atelier.

   Le fond uniforme très léger qui subsiste ne sert pas au texte mais à la
   PAGE : il détache le bandeau de l'en-tête clair posé juste au-dessus. */
.hero-bandeau::before {
  background:
    linear-gradient(95deg, rgba(0,0,0,.80) 0%, rgba(0,0,0,.62) 34%,
                           rgba(0,0,0,.24) 62%, rgba(0,0,0,.06) 82%),
    rgba(0, 0, 0, .14);
}
/* PLUS PETIT, et le titre d'abord : à 40 px il occupait un tiers de la hauteur
   du bandeau et écrasait l'accroche, qui porte pourtant ce que fait la
   boutique. */
.hero-bandeau .hero-surtitre { font-size: 11.5px; margin-bottom: 10px; }
.hero-bandeau .hero-titre {
  font-size: clamp(22px, 2.5vw, 31px); line-height: 1.14;
  margin: 0 0 10px; max-width: 26ch;
}
.hero-bandeau .hero-accroche {
  font-size: 15px; line-height: 1.55;
  /* 52ch et non 58 : à gauche, l'accroche n'est plus équilibrée par du blanc
     des deux côtés, et une ligne trop longue y devient pénible à suivre. */
  margin: 0; max-width: 52ch;
}
/* Un bouton dans un bandeau de 440 px pousse le texte contre le bord haut :
   il reprend sa marge seulement s'il existe. */
.hero-bandeau .hero-corps .bouton { margin-top: 20px; }

/* ═══ L'OR S'ÉCLAIRCIT SUR UN FOND SOMBRE ════════════════════════════════════
   L'accent de la marque — #C29A60 — est choisi pour porter du texte sur fond
   CLAIR. Posé sur une vidéo d'atelier ou une photo sombre, il devient un ocre
   éteint : le surtitre s'y confond avec les parties dorées de l'image, et le
   bouton, qui est le seul de tout le haut de page, cesse de se détacher.

   ON MÉLANGE AVEC DU BLANC PLUTÔT QUE D'ÉCRIRE UNE COULEUR. Une valeur en dur
   serait juste pour cette boutique-ci et fausse pour la suivante : c'est
   exactement le défaut qu'on a déjà payé ailleurs dans cette feuille. Le
   mélange, lui, suit la charte quelle qu'elle soit.

   LE GAIN EST MESURÉ, pas supposé. Le fond du bouton passe de #C29A60 à
   #D5BA96 ; contre son encre (#191510) le contraste monte de 7,0 à 9,6:1. Le
   surtitre gagne dans les mêmes proportions contre le voile.

   LA RÈGLE NE S'APPLIQUE QU'AUX BANNIÈRES QUI ONT UN VISUEL. Sans image ni
   vidéo, la bannière garde le fond doux du site et son encre foncée : y
   éclaircir l'or reviendrait à l'effacer. C'est aussi ce qui rend la règle
   utile d'avance sur une boutique qui n'a pas encore de fond — elle
   s'appliquera le jour où on lui en pose un, sans rien avoir à retoucher. */
.hero:not(.hero-sans-visuel) .hero-surtitre { color: var(--accent-clair); }
/* LE BOUTON DU HÉROS FAIT EXCEPTION, ET C'EST MESURÉ. Il est posé sur une
   photo, ce qui impose l'or ÉCLAIRCI pour qu'il s'en détache — or le blanc sur
   cet or-là ne tient que 1,9:1, soit un libellé illisible. Il garde donc son
   encre foncée, qui y atteint 9,6:1. Le blanc partout ailleurs, l'encre foncée
   ici : la règle n'est pas « telle couleur de texte » mais « le texte doit se
   lire sur son fond ». */
.hero:not(.hero-sans-visuel) .bouton,
.hero:not(.hero-sans-visuel) .bouton:hover,
.hero:not(.hero-sans-visuel) .bouton:focus-visible {
  /* ENCRE DÉRIVÉE LISIBLE, pas `--accent-encre`. Le commentaire ci-dessus vise
     une encre FONCÉE (9,6:1 sur l'or éclairci), mais `--accent-encre` vaut le
     blanc quand la marque a choisi le blanc sur son accent (boo, 20/08) → on
     retombait à 1,9:1, illisible. `--accent-encre-auto` est l'encre calculée sur
     l'accent (foncée sur clair, blanche sur sombre) : elle réalise l'intention et
     reste déclinable. */
  background: var(--accent-clair); color: var(--accent-encre-auto, var(--accent-encre));
}

/* ── Grand : 76 % de l'écran, texte à gauche ───────────────────────────── */
.hero-grand { min-height: 76vh; }

/* ── Plein écran, texte centré ─────────────────────────────────────────── */
.hero-centre { min-height: 84vh; text-align: center; }
.hero-centre::before {
  /* Voile UNIFORME et non radial : un vignettage éclaircit le centre, c'est-à-
     dire exactement là où se trouve le texte dans cette disposition. */
  background: rgba(0, 0, 0, .56);
}
.hero-centre .hero-titre,
.hero-centre .hero-accroche { margin-left: auto; margin-right: auto; }
.hero-centre .hero-titre { max-width: 22ch; }

@media (max-width: 640px) {
  /* Sur un téléphone, 440 px de bandeau occupent déjà la moitié de l'écran. */
  .hero-bandeau { height: 340px; }
  .hero-grand   { min-height: 62vh; }
  .hero-centre  { min-height: 70vh; }
}
/* Moins de mouvement : la vidéo disparaît et l'affiche — qui est le fond du
   bloc — reste. C'est la seule façon de retirer l'animation SANS retirer
   l'image, une vidéo masquée laissant autrement un cadre vide. */
@media (prefers-reduced-motion: reduce) {
  .hero-video { display: none; }
}

/* ----- Vignettes de carrousel : même langage de survol que les cartes ----- */
.carrousel-vignette { transition: transform .15s ease; }
.carrousel-vignette:hover { transform: translateY(-3px); }
.carrousel-vignette .carrousel-visuel {
  transition: box-shadow .15s ease;
}
.carrousel-vignette:hover .carrousel-visuel { box-shadow: 0 12px 28px rgba(16, 16, 24, .12); }
[data-theme="dark"] .carrousel-vignette:hover .carrousel-visuel { box-shadow: 0 12px 28px rgba(0, 0, 0, .45); }
@media (prefers-reduced-motion: reduce) {
  .carrousel-vignette, .carrousel-vignette:hover { transition: none; transform: none; }
}

/* ----- Boutons principaux : une ombre à la mesure de l'accent ----- */
.bouton:not(.bouton-secondaire):not(.btn-ghost) {
  box-shadow: 0 4px 14px rgba(var(--marque-accent-rgb, 47, 91, 234), .22);
  transition: filter .15s ease, box-shadow .15s ease, transform .12s ease;
}
.bouton:not(.bouton-secondaire):hover { transform: translateY(-1px); box-shadow: 0 7px 20px rgba(var(--marque-accent-rgb, 47, 91, 234), .3); }
.bouton:not(.bouton-secondaire):active { transform: translateY(0); }
@media (prefers-reduced-motion: reduce) {
  .bouton, .bouton:hover { transition: none; transform: none; }
}

/* ----- La barre du franco, dans le récapitulatif du panier ----- */
.franco { margin: 4px 0 14px; }
.franco-texte {
  margin: 0 0 8px; font-size: 13.5px; color: var(--encre-douce);
  display: flex; align-items: center; gap: 6px;
}
.franco-texte strong { color: var(--encre); }
.franco-barre {
  height: 6px; border-radius: 999px; background: var(--fond-doux);
  overflow: hidden;
}
.franco-barre > span {
  display: block; height: 100%;
  background: var(--accent);
  border-radius: inherit;
  transition: width .4s ease;
}
.franco.est-atteint .franco-texte { color: var(--succes, #1B7F4B); font-weight: 550; }
.franco.est-atteint .franco-barre > span { background: var(--succes, #1B7F4B); }

/* ----- Les étoiles sous le titre de la fiche ----- */
.fiche-avis-lien {
  display: inline-flex; align-items: center; gap: 8px;
  margin: 2px 0 10px;
  font-size: 13.5px; color: var(--encre-douce);
  text-decoration: none;
}
.fiche-avis-lien:hover span:last-child { text-decoration: underline; }

/* ============================================================================
   LE FIL DES ÉTAPES D'ACHAT (16/08/2026) — panier, commande, confirmation.
   Il répond à « combien en reste-t-il ? », la question qui décide d'un
   abandon. Discret par construction : ce n'est pas lui qu'on vient lire.
   ========================================================================== */
.tunnel-etapes { margin: 0 0 28px; }
.tunnel-etapes ol {
  display: flex; align-items: center; flex-wrap: wrap;
  gap: 10px 18px;
  list-style: none; margin: 0; padding: 0;
}
.tunnel-etapes li {
  display: inline-flex; align-items: center; gap: 9px;
  font-size: 14px; color: var(--encre-tenue);
}
/* Le trait de liaison est porté par l'étape, pas par un élément de plus : un
   séparateur écrit dans le balisage se ferait lire par les lecteurs d'écran. */
.tunnel-etapes li + li::before {
  content: ''; width: 26px; height: 1px;
  background: var(--filet-fort); margin-right: 9px;
}
.tunnel-etape-puce {
  display: inline-flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; border-radius: 50%;
  border: 1px solid var(--filet-fort);
  font-size: 12.5px; font-weight: 700; line-height: 1;
}
.tunnel-etapes a { color: inherit; text-decoration: none; display: inline-flex; align-items: center; gap: 9px; }
.tunnel-etapes a:hover .tunnel-etape-nom { text-decoration: underline; }
/* Franchie : l'accent, mais en second plan — c'est du chemin parcouru. */
.tunnel-etapes .est-faite { color: var(--encre-douce); }
.tunnel-etapes .est-faite .tunnel-etape-puce {
  background: var(--accent-doux); border-color: transparent; color: var(--accent-texte);
}
/* Courante : la seule qui pèse.
   ⚠️ 700, PAS UN POIDS INTERMÉDIAIRE (20/08/2026 — « ça a l'air flou »).
   `550`/`650` sont la convention du site pour une emphase moyenne, et
   passent inaperçus sur les polices variables (San Francisco, Segoe UI) qui
   savent interpoler n'importe quel poids. Les polices système NON variables
   (Roboto, entre autres, sur Android) n'existent qu'en paliers fixes — 400,
   500, 700 — et un poids qui tombe entre deux force le moteur à FABRIQUER un
   gras en épaississant algorithmiquement les traits du poids le plus proche.
   C'est ce gras synthétique, pas la police elle-même, qui se lit flou : un
   700 réel n'a pas ce défaut sur aucun moteur. */
.tunnel-etapes .est-active { color: var(--encre); font-weight: 700; }
.tunnel-etapes .est-active .tunnel-etape-puce {
  background: var(--accent); border-color: transparent; color: var(--accent-encre);
}
.tunnel-etapes .est-a-venir { opacity: .72; }
/* Sur la confirmation, le fil est centré comme le reste de la page. */
.confirmation .tunnel-etapes ol { justify-content: center; }
.confirmation .tunnel-etapes { margin-bottom: 24px; }
@media (max-width: 560px) {
  .tunnel-etapes li + li::before { width: 14px; margin-right: 6px; }
  .tunnel-etapes li { font-size: 13px; }
}

/* ----- Pied du formulaire de contact : vérification et envoi côte à côte ----- */
.contact-envoi {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px; flex-wrap: wrap;
  padding-top: 18px; border-top: 1px solid var(--filet);
}
/* Le widget garde sa taille propre (Cloudflare la fixe) ; c'est le bouton qui
   s'aligne à droite de ce qui reste. Sur un écran étroit, le captcha occupe
   déjà toute la largeur : la ligne se replie et le bouton s'étale dessous. */
.contact-envoi .cf-turnstile { flex: 0 0 auto; margin: 0; }
.contact-envoi > .btn { margin-left: auto; }
@media (max-width: 560px) {
  .contact-envoi > .btn { margin-left: 0; width: 100%; justify-content: center; }
}

/* ============================================================================
   AVIS CLIENTS SUR L'ACCUEIL (16/08/2026) — la preuve sociale manquait à la
   page : le catalogue, le journal et les marques y étaient, la parole des
   acheteurs non. Une rangée qui se tire, comme partout ailleurs.
   ========================================================================== */
.section-avis { padding: 8px 0 44px; }
.section-avis > h2 { margin: 0 0 20px; }
/* La rangée hérite du comportement générique de glisser : la classe
   `.rangee-glissante` porte le défilement et le curseur.
   ═══ REVUE DU 20/08/2026 : DÉFILEMENT AUTOMATIQUE EN BOUCLE, « comme sur
   bmp » (demande de Jesse — boostmypresta.com fait déjà exactement ceci pour
   ses témoignages). Le CONTENEUR (`.avis-rangee`) porte le défilement et le
   flou des bords ; la PISTE (`.avis-rangee-piste`, ajoutée à cette occasion)
   porte la mise en page flex — c'est elle que le gabarit remplit deux fois
   (la seconde passe en `aria-hidden`), pour que `shop.js` ait de la matière
   à faire défiler sans jamais montrer de trou ni de couture. Voir l'IIFE
   « avis : défilement automatique en boucle » dans `shop.js`.
   PAS DE `scroll-snap` ICI : un point d'ancrage qui rattrape la rangée à
   chaque image entre en conflit avec le défilement automatique — la rangée
   saccade au lieu de glisser. Les rangées qui ne défilent pas seules
   gardent le leur ailleurs dans cette feuille. */
.avis-rangee {
  position: relative;
  overflow-x: auto;
  /* ⚠️ EXPLICITE, PAS LAISSÉ À `auto` (20/08/2026, « ombres coupées »).
     `overflow-x: auto` sans `overflow-y` déclaré fait que le navigateur
     calcule aussi l'axe vertical en `auto` — un conteneur qui défile devient
     un conteneur qui ROGNE, sur les deux axes à la fois. L'ombre et le léger
     soulèvement d'une carte au survol débordent de sa propre boîte : sans
     cette ligne (et sans la marge ci-dessous, qui leur donne de la place à
     l'intérieur du cadre qui rogne), ils se faisaient couper net. */
  overflow-y: hidden;
  /* ⚠️ MARGE RECALCULÉE SUR LA VRAIE OMBRE, PAS AU JUGÉ (20/08/2026, deuxième
     passe — « ombres coupées » persistait malgré la première correction).
     16px de marge basse contre une ombre au survol de `0 10px 26px` — décalage
     + flou, soit ~36px de portée réelle sous la carte, 3px de moins une fois
     le survol qui la soulève déduits — ne pouvait que la trancher net. Même
     calcul en haut, sans le décalage qui pousse l'essentiel de l'ombre vers
     le bas. Le chiffre juste, pas un arrondi optimiste, pour ne pas avoir à y
     revenir une troisième fois. */
  padding: 20px 4px 34px; cursor: grab; scrollbar-width: none;
  /* LE FLOU AUX DEUX BORDS, SYMÉTRIQUE — même recette que le carrousel de
     témoignages de boostmypresta.com. N'a de sens QUE parce que la rangée
     boucle sans fin : sur une rangée qu'on fait défiler à la main jusqu'au
     bout, le même flou couperait le DERNIER avis sans qu'il y ait plus rien
     derrière. */
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 40px, #000 calc(100% - 40px), transparent 100%);
          mask-image: linear-gradient(90deg, transparent 0, #000 40px, #000 calc(100% - 40px), transparent 100%);
}
.avis-rangee::-webkit-scrollbar { display: none; }
.avis-rangee-piste { display: flex; gap: 16px; width: max-content; }
.avis-rangee-piste > * { flex: 0 0 auto; }
.avis-rangee.est-tiree { cursor: grabbing; }
.avis-rangee.est-tiree * { user-select: none; pointer-events: none; }

/* ═══ PASSE D'APPARENCE DU 20/08/2026, TROISIÈME TOUR ════════════════════════
   Le deuxième tour (fond teinté, corps en italique dans la police des titres)
   a été rejeté par Jesse à l'écran : « je n'aime pas la couleur, ni la police »,
   et « les blocs sont trop en hauteur ». On revient donc à un fond PLEIN
   (`--surface`, blanc — celui de `.carte-produit`, jamais remis en cause) et à
   un corps en police courante, DROITE — l'italique et le fond teinté
   partaient d'une bonne intention (distinguer la carte d'avis d'une carte
   produit) mais l'un et l'autre étaient un choix esthétique de plus, pas une
   demande. Le guillemet en filigrane, lui, n'a pas été visé : il reste, mais
   plus petit — une carte plus basse ne peut plus en porter un de 76 px sans
   qu'il déborde du texte. */
.avis-carte {
  /* `z-index: 0` OUVRE UN CONTEXTE D'EMPILEMENT PROPRE (20/08/2026, guillemet
     « plus grosse et visible ») : sans lui, `position: relative` seul ne
     borne rien, et un enfant en `z-index: -1` (voir `.avis-carte-guillemet`)
     irait chercher un plan D'EMPILEMENT PARTAGÉ avec les cartes voisines —
     capable de passer DERRIÈRE la carte elle-même selon l'ordre du DOM. Avec
     ce contexte posé ici, `-1` ne peut aller nulle part sous CETTE carte. */
  position: relative; z-index: 0;
  width: 320px;
  display: flex; flex-direction: column; gap: 8px;
  padding: 16px 18px 14px;
  border: 1px solid var(--filet); border-radius: var(--rayon-large);
  background: var(--surface);
  overflow: hidden;
  transition: border-color .15s ease, transform .15s ease, box-shadow .15s ease;
}
/* `<a>` ET `<article>` PARTAGENT LA MÊME CLASSE : la première remet à zéro ce
   qu'un lien ajoute par défaut (soulignement, encre bleue), pour que les deux
   balises rendent EXACTEMENT la même carte. */
a.avis-carte { text-decoration: none; color: inherit; }
/* LE RELIEF AU SURVOL EST RÉSERVÉ AUX CARTES QUI MÈNENT QUELQUE PART
   (20/08/2026, « avis pas cliquables ») : le proposer sur une carte SAG, qui
   n'a nulle part où mener, promettrait un clic qu'elle ne peut pas tenir. */
.avis-carte.est-cliquable:hover {
  border-color: var(--filet-fort);
  transform: translateY(-3px);
  box-shadow: 0 10px 26px rgba(16, 16, 24, .09);
}
@media (prefers-reduced-motion: reduce) {
  .avis-carte.est-cliquable:hover { transition: none; transform: none; }
}
/* ⚠️ TROISIÈME PASSE (20/08/2026, « plus grosse et visible ») : les deux
   premières l'agrandissaient sans jamais corriger ce qui la rendait
   discrète — `overflow: hidden` sur la carte (nécessaire par ailleurs, voir
   plus haut) rognait presque tout signe posé à cheval sur le bord, si bien
   qu'agrandir ne faisait qu'agrandir la part invisible. Repositionné pour
   tenir dans le cadre, `z-index: -1` (voir le contexte ouvert sur
   `.avis-carte`) pour passer DERRIÈRE le texte plutôt que de risquer de le
   traverser, opacité relevée pour se voir vraiment sur le fond clair de la
   carte. */
/* ⚠️ ANCRÉ SUR L'ENCRE, PAS SUR L'ACCENT SEUL (20/08/2026, « pourquoi
   l'apparence n'est pas la même que ecommerce ? ») : la même règle,
   `color-mix(accent 38%, transparent)`, rendait bien sur ecommerce (accent
   bleu roi, contraste fort sur du blanc) et presque invisible sur boo
   (accent or, un ton clair qui se distingue à peine du blanc à 38%
   d'opacité) — LA RÈGLE ÉTAIT IDENTIQUE, c'est la teinte de marque qui
   changeait le résultat du tout au tout. Mélanger l'accent à de l'encre
   AVANT de dolser la transparence donne un contraste qui ne dépend plus de
   la clarté de l'accent choisi par la marque — la couleur garde son identité
   (accent majoritaire) mais un ton toujours assez sombre pour se voir. */
/* ⚠️ TROISIÈME PASSE (20/08/2026, « les illustrations quote doivent être les
   MÊMES que ecommerce ») : mélanger de l'accent, même minoritairement,
   garantit que le résultat varie d'une marque à l'autre — c'était TOUJOURS
   vrai avec la passe précédente (65% accent), juste moins visible tant que
   les deux teintes restaient dans le même registre de clarté. Retiré tout à
   fait : l'encre (le texte, sombre et de contraste comparable d'une marque à
   l'autre) porte seule la couleur. Ce n'est plus « accordé à chaque
   charte » — c'est délibérément IDENTIQUE partout, comme demandé. */
/* ⚠️ QUATRIÈME PASSE (20/08/2026) : les deux précédentes cherchaient une
   égalité de FORMULE (même pourcentage d'accent, puis un mélange neutre) —
   Jesse demandait une égalité de RÉSULTAT : un signe clairement coloré et
   visible, comme sur ecommerce, pas une nuance de gris. Retour à l'accent
   SEUL, sans mélange, à une opacité franchement relevée — le même principe
   des deux côtés (l'accent de chaque marque, bien visible), qui ne peut
   donner un hex identique puisque les marques n'ont pas le même accent, mais
   donne le même EFFET. */
/* ⚠️ LE GUILLEMET EST DEVENU UN SVG (24/08/2026, « les mêmes illustrations
   pour les avis pour boo ») : le glyphe « ” » composé en
   `var(--marque-police-titre)` donnait deux DESSINS selon la marque — rond en
   linéale sur ecommerce, anguleux en sérif sur boo — et c'est le dessin que
   Jesse comparait, pas la couleur. La forme vit dans avis-bandeau.php, la
   taille dans ses attributs width/height ; ne restent ici que la position et
   la teinte. */
/* ⚠️ LA COULEUR RESTE L'ACCENT DE CHAQUE MARQUE (20/08/2026, « tu n'y arrives
   pas, remets au moins la bonne couleur ») : une passe avait fixé un bleu en
   dur pour coller littéralement à ecommerce — au prix d'un bleu sans rapport
   avec la charte or de boo. Un seul dosage, deux teintes légitimes. */
.avis-carte-guillemet {
  position: absolute; z-index: -1; top: 14px; right: 16px;
  line-height: 0;
  /* 62 % → 40 % (24/08/2026, « trop foncé ») : le guillemet est un décor de
     fond, il doit se lire derrière le texte sans lui disputer l'encre. */
  color: color-mix(in oklch, var(--accent) 40%, transparent);
  pointer-events: none; user-select: none;
}
.avis-carte .avis-etoiles.est-petite { font-size: 13px; gap: 1px; }
.avis-carte-titre { margin: 0; font-weight: 600; font-size: 15px; line-height: 1.35; }
.avis-carte-corps {
  position: relative; margin: 0;
  font-size: 14px; line-height: 1.5; color: var(--encre-douce);
  /* Trois lignes au plus, contre quatre : la carte a moins de rembourrage à
     donner à un texte long désormais qu'à sa hauteur d'ensemble. */
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
  overflow: hidden;
}
/* LE PIED : UNE LIGNE D'IDENTITÉ, avatar compris — voir plus bas.
   `align-items: center` et non `baseline` : un disque et un nom se répondent
   par leur milieu, pas par le pied de leurs lettres. */
.avis-carte-pied {
  margin: auto 0 0; padding-top: 8px; border-top: 1px solid var(--filet);
  display: flex; align-items: center; gap: 9px;
}
/* L'INITIALE, DANS UN DISQUE TEINTÉ DE L'ACCENT — pas l'accent plein : une
   boutique à l'accent clair (l'or de bijoux-or-occasion) y perdrait son
   contraste de texte, et un aplat à pleine force sur douze avatars d'affilée
   pèserait plus que les avis eux-mêmes. `--accent-texte` est la même encre
   que le nom du produit sous le prénom : le disque et le lien parlent la
   même couleur. */
.avis-carte-avatar {
  flex: none; width: 30px; height: 30px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: color-mix(in srgb, var(--accent) 18%, var(--surface));
  color: var(--accent-texte);
  font-size: 12px; font-weight: 700;
}
.avis-carte-auteur { margin: 0; min-width: 0; font-size: 13px; color: var(--encre-tenue); }
.avis-carte-auteur strong { display: block; color: var(--encre); font-weight: 600; font-size: 13.5px; }
/* Le nom du produit N'EST PLUS UN LIEN À LUI SEUL (20/08/2026) : la carte
   entière l'est désormais quand elle a une destination — voir `.est-cliquable`
   plus haut et le commentaire du gabarit. Il reprend juste l'encre d'accent
   pour rester repérable au premier coup d'œil. */
.avis-carte-auteur span { display: block; color: var(--accent-texte); }
@media (max-width: 560px) {
  .avis-carte { width: 78vw; }
}

/* ----- Le bandeau d'avis, en bas de toutes les pages ----- */
.avis-bandeau {
  /* Séparé du pied par un filet, et non par une couleur : le pied a déjà son
     fond, empiler deux zones teintées ferait deux pieds. */
  border-top: 1px solid var(--filet);
  padding: 40px 0 8px;
}
.avis-bandeau > .contenu { padding-top: 0; }
/* ─── L'EN-TÊTE PORTE LE TITRE ET, S'IL Y A LIEU, LE REPÈRE CHIFFRÉ ──────────
   (20/08/2026 — le repère ne nomme plus le tiers qui le fournit, voir
   avis-bandeau.php. `.grc-site-iframe`, le sélecteur de leur ancien widget
   encapsulé, est retiré depuis le 18/08 : plus rien ne l'émet.)

   LA COCARDE À GAUCHE, SUR TOUTE LA HAUTEUR DU BLOC (20/08/2026, deuxième
   demande de Jesse — casée en icône de 14 px devant « Certifiés par », elle
   passait inaperçue la première fois). `align-items: stretch` sur la rangée
   et `align-self: stretch` sur l'image lui donnent la hauteur du titre
   empilé sur la note, quelle que soit cette hauteur — pas un chiffre écrit en
   dur qui se déréglerait au moindre changement de texte. */
.avis-bandeau-entete {
  display: flex; align-items: stretch; gap: 14px;
  margin: 0 0 20px;
}
/* « Voir tous les avis → » ANCRÉ EN HAUT À DROITE de l'en-tête. Sans règle, le
   lien (dernier enfant flex) se calait au centre vertical à côté de la note et
   paraissait collé au badge ; `margin-left:auto` le repousse à droite,
   `align-self:flex-start` le remonte au niveau du titre. */
.avis-bandeau-lien { margin-left: auto; align-self: flex-start; }
/* ⚠️ AGRANDIE UNE DEUXIÈME FOIS (20/08/2026, « plus grande la cocarde ») :
   `align-self: stretch` la limitait à la hauteur du bloc titre+note+lien
   empilés à côté, qui ne dépasse pas 70-75 px sur une seule ligne — un
   plafond bien plus bas que ce que Jesse avait en tête. `min-height` lui
   donne un plancher qui ne dépend plus du texte voisin ; `max-height` monte
   avec, pour ne pas la fixer plus haut que ce plancher quand le texte est
   court. */
.avis-bandeau-badge {
  flex: none; align-self: stretch;
  width: auto; height: auto; min-height: 96px; max-height: 130px;
  object-fit: contain; object-position: left center;
}
.avis-bandeau-entete-texte {
  display: flex; flex-direction: column; justify-content: center; gap: 6px;
  min-width: 0;
}
.avis-bandeau h2 { margin: 0; }
/* LE REPÈRE CHIFFRÉ, DANS UNE PASTILLE plutôt qu'en simple ligne de texte
   (20/08/2026, passe d'apparence) : une note qui flotte au fil du texte se
   lit comme une précision, une note dans un aplat se lit comme un repère —
   c'est la différence entre la mentionner et la mettre en avant, et une
   preuve sociale mérite la seconde. */
.avis-bandeau-note {
  display: inline-flex; align-items: center; gap: 5px; width: fit-content;
  margin: 0; padding: 5px 12px 5px 10px; border-radius: 999px;
  background: color-mix(in srgb, var(--accent) 10%, transparent);
  font-size: 13.5px; color: var(--encre-tenue);
}
/* Taille seule : la couleur des étoiles (orange remplies / gris vides) vient du
   composant partagé `.avis-etoiles`, pour qu'une note < 5 montre bien des étoiles
   grises et non toutes orange. */
.avis-bandeau-note-etoiles.est-petite { font-size: 13px; }
.avis-bandeau-note strong { color: var(--encre); font-weight: 600; }
.avis-bandeau-note span { color: var(--encre-tenue); }
/* Paragraphe SEO sous le bloc d'avis : colonne de lecture bornée, ton discret. */
.avis-bandeau-seo {
  max-width: 68ch; margin: 2px 0 24px;
  font-size: 14.5px; line-height: 1.6; color: var(--encre-douce);
}
.avis-bandeau-seo strong { color: var(--encre); font-weight: 600; }
/* LA MENTION DU TIERS, SOUS LA PASTILLE (20/08/2026, revenue après un retrait
   puis une demande inverse de Jesse le même jour — voir le gabarit). En LIEN,
   vers leur domaine réel : un badge de confiance qui ne mène nulle part n'en
   est pas un. */
.avis-bandeau-cert { margin: 0; font-size: 12.5px; color: var(--encre-tenue); }
.avis-bandeau-cert a { color: var(--accent-texte); text-decoration: none; }
.avis-bandeau-cert a:hover { text-decoration: underline; }

/* ── LE BANDEAU D'AVIS SUR TÉLÉPHONE ─────────────────────────────────────────
   En ligne unique, les quatre éléments de l'en-tête — cocarde, titre, pastille
   de note, lien « Voir tous les avis » — se disputent 390 px. Constaté à
   l'écran : le titre tombait sur deux lignes coincé entre la cocarde et le
   lien, et la pastille se brisait en trois (« sur » / « 36 » / « avis »), ce
   qui est le contraire d'un repère qu'on lit d'un coup d'œil.

   On empile donc : la cocarde et le texte restent côte à côte — c'est leur
   rapport qui fait la preuve — et le lien passe DESSOUS, sur sa propre ligne.
   Il ne perd rien à descendre : on le cherche après avoir lu la note, jamais
   avant. */
@media (max-width: 640px) {
  .avis-bandeau-entete { flex-wrap: wrap; gap: 12px; align-items: center; }
  /* La cocarde garde sa présence sans manger la largeur du titre : elle est
     décorative, le texte ne l'est pas. */
  .avis-bandeau-badge { min-height: 68px; max-height: 84px; align-self: center; }
  .avis-bandeau-entete-texte { flex: 1 1 0; }
  .avis-bandeau h2 { font-size: clamp(20px, 5.6vw, 26px); line-height: 1.2; }
  /* ⚠️ LA PASTILLE NE SE BRISE PAS. `white-space: nowrap` sur le conteneur ne
     suffit pas : ce sont les `span` intérieurs qui retournent à la ligne. */
  .avis-bandeau-note { flex-wrap: nowrap; white-space: nowrap; max-width: 100%; }
  .avis-bandeau-note > * { white-space: nowrap; }
  /* Le lien prend sa propre ligne, à gauche comme le reste : aligné à droite
     sous un texte aligné à gauche, il paraîtrait détaché du bloc. */
  .avis-bandeau-lien {
    order: 3; width: 100%; margin-left: 0; align-self: flex-start;
  }
}
/* Sous 380 px, la cocarde elle-même devient un luxe : le titre passerait à
   trois lignes pour la garder à côté. */
@media (max-width: 380px) {
  .avis-bandeau-badge { min-height: 56px; max-height: 64px; }
  .avis-bandeau-note { font-size: 12.5px; padding: 4px 10px 4px 8px; }
}

/* ---------------------------------------------------------------------------
   Afficher le mot de passe saisi (public/assets/js/mot-de-passe-visible.js)
   L'enveloppe est posée par le script AUTOUR du champ : elle doit se comporter
   comme le champ qu'elle remplace dans la mise en page — bloc, pleine largeur,
   sans marge propre. Le bouton se superpose, et le champ lui réserve sa place
   par un padding à droite, sinon un mot de passe long passe dessous.
   --------------------------------------------------------------------------- */
.champ-mdp { position: relative; display: block; width: 100%; }
/* `!important` sur la SEULE réserve de place à droite : le champ hérite de
   règles plus spécifiques selon l'écran (`.account-form .form-input`,
   `.auth … .form-input`), qui reposent toutes le padding. Sans cela, un mot de
   passe long passe sous le bouton — constaté sur /compte/profil, où le padding
   retombait à 13 px. Le reste de l'habillage du champ n'est pas touché. */
.champ-mdp > input { width: 100%; padding-right: 42px !important; }
.champ-mdp-oeil {
  position: absolute; top: 50%; right: 4px; transform: translateY(-50%);
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; padding: 0;
  border: 0; border-radius: var(--rayon); background: none;
  color: var(--encre-tenue); cursor: pointer; line-height: 0;
}
.champ-mdp-oeil:hover { color: var(--encre); }
.champ-mdp-oeil:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.champ-mdp-oeil svg { width: 19px; height: 19px; }

/* Mise en valeur dans un argument de réassurance : `**mot**` dans le profil de
   marque. L'accent porte sur ce qui décide — « gratuite », « certificat » — et
   le reste du texte garde son encre atténuée, sinon plus rien ne ressort. */
.reassurance-accent { color: var(--accent-texte); font-weight: 600; }

/* ---------------------------------------------------------------------------
   Colonne « Nous trouver » du pied de page
   Le titre reprend celui des colonnes de liens — même graisse, même casse, même
   marge basse — pour que la colonne d'identité fasse partie de la même rangée
   et non d'un bloc à part. La note de visite et le lien associé ferment la
   colonne, dans une encre plus retenue : ce sont des précisions, pas des
   coordonnées.
   --------------------------------------------------------------------------- */
/* DE L'AIR ENTRE LE LOGOTYPE ET LE TITRE. Collés, ils formaient une pile de
   deux en-têtes : le nom de la boutique en lettres dessinées, puis « NOUS
   TROUVER » en petites capitales, à quatre pixels l'un de l'autre. Le logotype
   appartient à la marque, le titre à la colonne — l'écart doit le dire. */
/* LA COLONNE D'IDENTITÉ EST UNE CARTE (3/09/2026, demande de Jesse). Posée à
   plat sur le fond du pied, elle se lisait comme la suite des trois colonnes de
   liens alors qu'elle dit autre chose : qui vend, où, et quand. Un fond à peine
   plus soutenu — quatre pour cent d'encre, donc valable sur toutes les chartes
   et dans les deux thèmes — suffit à la détacher sans la faire crier. */
/* ⚠️ PAS D'APLAT DU TOUT : UN FILET ET UNE ILLUSTRATION (4/09/2026, Jesse :
   « oublie le fond coloré, je préfère une illustration de fond stylée »).

   DEUX APLATS ONT ÉTÉ REFUSÉS AVANT CELUI-CI, et les deux refus disent quelque
   chose de différent. 4 % d'encre : un lavis de NOIR, donc un gris tiède
   étranger à une charte crème et or — « couleur hors charte ». 10 % d'accent :
   juste sur l'identité, mais toujours un APLAT, c'est-à-dire une zone colorée
   qui pèse dans un pied de page déjà chargé.

   Ce qui reste : un filet, et des ARCS CONCENTRIQUES qui naissent du coin
   bas-droit — la taille d'une pierre chez le bijoutier, une onde chez le
   confiseur. Tracés à l'accent, estompés par un masque radial : denses au
   coin, éteints avant d'atteindre le texte. C'est le masque qui fait la
   différence entre une illustration et une trame de papier peint.

   ⚠️ EN CSS ET NON EN IMAGE, et ce n'est pas une préférence de méthode : un
   fichier servi depuis /assets est figé un an par Cloudflare. Un motif
   redessinable en CSS suit l'accent de la boutique sans qu'aucun fichier ne
   soit à régénérer ni à purger. */
.pied-identite {
  /* LA CARTE ÉPOUSE SON CONTENU (4/09/2026, Jesse : « trop large le fond »).
     Étirée sur toute sa piste de grille, elle mesurait 424 px chez un marchand
     dont la plus longue ligne — un courriel — en fait 209 : un aplat coloré
     dont la moitié droite est vide ne se lit plus comme une carte, mais comme
     une zone de fond mal détourée. Le contenu de cette colonne varie beaucoup
     d'une boutique à l'autre (avec ou sans horaires, consigne de visite, lien
     d'enseigne) : une largeur écrite en dur serait juste pour l'une et fausse
     pour la suivante.

     `max-content` borné par la piste : la carte fait la largeur de sa plus
     longue ligne tant que la place existe, et retombe sur la largeur de la
     colonne quand le texte la dépasse — c'est le cas du bijoutier, dont la
     consigne de visite tient deux lignes pleines. */
  width: max-content;
  max-width: 100%;
  position: relative;
  overflow: hidden;
  background: transparent;
  border: 1px solid var(--filet);
  border-radius: var(--rayon-large, var(--rayon));
  padding: 22px 24px 24px;
}
/* L'illustration vit dans un PSEUDO-ÉLÉMENT et non dans `background-image` :
   `mask-image` ne s'applique pas à une couche de fond, et sans masque le motif
   se répète mécaniquement d'un bord à l'autre. `inset: 0`, aucun `z-index` —
   un pseudo-élément positionné est peint sous les enfants en flux du même
   contexte — et `pointer-events: none` pour ne rien attraper au passage. */
/* ⚠️ L'ILLUSTRATION EST UN JETON DE MARQUE, PAS UNE VALEUR DE FEUILLE
   (4/09/2026). Jesse a retenu les facettes pour les boutiques en général et la
   « lumière rasante » pour le bijoutier : deux motifs différents pour le même
   composant, sur trois instances qui partagent CETTE feuille au bit près.
   Écrire le motif ici l'aurait figé pour tout le monde ; le poser dans le
   profil de chaque boutique (`theme.root`) le rend déclinable comme une
   couleur, et le repli de `var()` porte le motif par défaut — de sorte qu'une
   boutique qui ne déclare rien reçoit les facettes sans une ligne de config. */
.pied-identite::after {
  content: '';
  position: absolute; inset: 0;
  pointer-events: none;
  background-image: var(--marque-pied-illustration,
    repeating-radial-gradient(circle at 100% 100%,
      transparent 0 30px,
      color-mix(in srgb, var(--accent) 34%, transparent) 30px 31px));
  /* Le masque suit le motif : les facettes ont besoin d'être éteintes avant le
     texte, un halo posé en coin s'éteint déjà tout seul et demande `none`. */
  -webkit-mask-image: var(--marque-pied-illustration-masque,
    radial-gradient(150% 130% at 100% 100%, #000, transparent 70%));
          mask-image: var(--marque-pied-illustration-masque,
    radial-gradient(150% 130% at 100% 100%, #000, transparent 70%));
}
/* ⚠️ LA SURCHARGE DOIT VIVRE ICI, ET NON DANS LE BLOC ADAPTATIF DU HAUT DU
   FICHIER : à spécificité égale, c'est la DERNIÈRE règle écrite qui gagne, et
   les media queries du pied de page sont déclarées sept mille lignes plus
   haut. Posée là-bas, `width: auto` était écrasé par le `width: max-content`
   ci-dessus et la carte gardait sa largeur de contenu sur téléphone — sans
   qu'aucune règle ne paraisse fautive.

   LA CARTE REPREND TOUTE SA COLONNE SOUS 900 PX. Épouser son contenu n'a de
   sens que face à des colonnes voisines : c'est là qu'une carte à moitié vide
   se remarque. Dès que le pied se replie sur une ou deux colonnes, la carte
   EST la rangée — s'arrêter à la longueur de son courriel laisserait un bord
   droit ragé au milieu de l'écran, sans rien à quoi le comparer. */
@media (max-width: 900px) { .pied-identite { width: auto; } }
.pied-identite .pied-logo, .pied-identite .pied-marque { margin-bottom: 4px; }
.pied-titre-identite { margin-top: 2px; margin-bottom: 4px; }

/* LES COORDONNÉES : une gouttière d'icônes, deux niveaux d'espacement.
   La gouttière est FIXE (22 px) et l'icône y est centrée : tous les textes
   s'alignent alors sur un même axe vertical, quelle que soit la longueur des
   lignes. C'est ce qui distingue une liste d'un empilement. */
.pied-coordonnees {
  list-style: none; margin: 0; padding: 0;
  /* DIX PIXELS ET NON QUATORZE (3/09/2026) : dans une carte, l'espace du bord
     tient déjà le bloc, et quatorze pixels entre cinq lignes faisaient une
     colonne aérée au point de paraître dépeuplée. */
  display: flex; flex-direction: column; gap: 10px;
  font-size: 14px; color: var(--encre-douce);
}
/* L'ICÔNE EST CENTRÉE SUR SA LIGNE, pas calée sur la première majuscule. Elle
   l'était pour coller au haut d'un groupe d'une seule ligne ; sur l'adresse et
   les horaires, qui en font deux, elle se retrouvait en haut d'un bloc dont
   elle désigne l'ENSEMBLE — un point d'ancrage flottant au-dessus de son texte.
   Centrée, elle vaut pour une ligne comme pour trois. */
.pied-coord-item { display: grid; grid-template-columns: 22px 1fr; align-items: center; gap: 2px; }
.pied-coord-icone { color: var(--encre-tenue); flex: 0 0 auto; }
.pied-coord-corps { line-height: 1.55; min-width: 0; }
.pied-coord-corps address { font-style: normal; }
.pied-coord-corps a { color: var(--encre-douce); text-decoration: none; }
.pied-coord-corps a:hover { color: var(--encre); text-decoration: underline; }
/* La consigne de visite : même colonne, encre plus retenue et corps plus petit.
   C'est une précision sur ce qui précède, elle ne doit pas peser autant que
   l'adresse ou l'heure. */
.pied-coord-note { display: block; font-size: 13px; color: var(--encre-tenue); }

.pied-lien-associe {
  align-self: flex-start; margin-top: 2px;
  display: inline-flex; align-items: center; gap: 6px;
  color: var(--encre-douce); font-size: 13.5px; text-decoration: none;
}
/* La flèche est POSÉE EN CSS et non dans le gabarit : elle dit « ce lien sort
   du site », ce qui est une information d'interface, pas de contenu. */
.pied-lien-associe::after { content: "↗"; font-size: 12px; color: var(--encre-tenue); }
.pied-lien-associe:hover { color: var(--encre); text-decoration: underline; text-underline-offset: 3px; }
.pied-lien-associe:hover::after { color: var(--encre); }

/* ===========================================================================
   RÉCIT — la mise en page d'une page rédigée illustrée (« Mon histoire »).
   ===========================================================================
   Ces classes s'écrivent dans le CONTENU de la page, depuis le back-office :
   le gabarit des pages libres ne change pas, et n'importe quelle boutique peut
   composer un récit de la même façon. Elles ne décrivent que des rapports —
   une image pleine largeur, un texte à côté d'une photo, une bande de vues,
   un encadré — jamais un contenu particulier.

   Le tout vit DANS la carte `.page-libre-corps` : les sélecteurs y sont donc
   ancrés, sinon la règle qui pose un filet au-dessus de chaque `h2` d'une page
   libre couperait le récit tous les deux paragraphes. */
.recit { display: flex; flex-direction: column; gap: 38px; }
.page-libre-corps .prose .recit :is(h2, h3) {
  border-top: 0; padding-top: 0; margin: 0 0 12px;
  font-size: clamp(20px, 2.1vw, 27px); line-height: 1.25;
}
.recit p { margin: 0 0 12px; line-height: 1.7; }
.recit p:last-child { margin-bottom: 0; }

/* L'image d'ouverture sort de la carte pour en occuper toute la largeur : une
   photo à pleine page derrière un titre pose le sujet avant la première ligne.
   Les marges négatives valent exactement le rembourrage de `.page-libre-corps`
   — les changer là-bas sans les changer ici laisserait un liseré. */
.recit-hero {
  position: relative; margin: -28px -32px 0; overflow: hidden;
  border-radius: var(--rayon-large) var(--rayon-large) 0 0;
}
/* LA HAUTEUR DE L'IMAGE D'OUVERTURE EST BORNÉE. Servie à sa proportion, une
   photo de 1200 × 800 occupait 926 px de haut sur un écran large : le titre de
   la page et la première phrase passaient sous la ligne de flottaison, et le
   visiteur devait faire défiler pour savoir de quoi on lui parle. Le recadrage
   privilégie le haut du cadre (`center 42%`), où se trouve le sujet. */
.recit-hero img { display: block; width: 100%; height: 460px; object-fit: cover; object-position: center 42%; }
.recit-hero-legende {
  position: absolute; inset: auto 0 0 0;
  display: flex; flex-direction: column; gap: 6px;
  padding: 56px 32px 26px; color: #fff;
  /* Un voile, pas un aplat : le dégradé n'assombrit que le bas de l'image,
     là où le texte se pose. Sans lui, un titre blanc sur une photo claire
     descend sous le contraste lisible — et les photos d'atelier le sont. */
  background: linear-gradient(to top, rgba(0,0,0,.72), rgba(0,0,0,.42) 45%, rgba(0,0,0,0));
}
.recit-hero-surtitre {
  font-size: 12.5px; letter-spacing: .12em; text-transform: uppercase;
  opacity: .92;
}
.recit-hero-titre {
  font-family: var(--police-titre, inherit);
  font-size: clamp(21px, 2.6vw, 32px); line-height: 1.2;
}

/* L'introduction est plus large et plus aérée que le corps : c'est la voix du
   marchand, elle a droit à un peu d'air avant que la démonstration commence. */
.recit-intro { font-size: 16.5px; }
.recit-intro p { margin-bottom: 14px; }
.recit-cle {
  margin-top: 18px; padding: 16px 18px;
  background: var(--fond); border-left: 3px solid var(--accent);
  border-radius: 0 var(--rayon) var(--rayon) 0;
  color: var(--encre); font-size: 16px;
}

/* Texte et photo côte à côte, alternés. La photo est en portrait : elle occupe
   une colonne plus étroite, sinon elle écrase le texte qu'elle illustre. */
.recit-duo { display: grid; grid-template-columns: 1.25fr .75fr; gap: 28px; align-items: center; }
.recit-duo-inverse { grid-template-columns: .75fr 1.25fr; }
.recit-duo-image { margin: 0; }
.recit-duo-image img,
.recit-bande img {
  display: block; width: 100%;
  border-radius: var(--rayon); border: 1px solid var(--filet);
  object-fit: cover;
}
/* TOUTES LES PHOTOS SONT RECADRÉES À UNE HAUTEUR FIXE, et c'est ce qui fait
   tenir la page. Les originaux sont des portraits de 800 × 1200 : à leur
   proportion, chacun occupait plus de 700 px de haut à côté de trois lignes de
   texte, et le bloc s'ouvrait sur un grand vide. Une hauteur commune donne
   aussi des rangées alignées, ce qu'une bande de photos aux formats mêlés ne
   fait jamais d'elle-même. */
.recit-duo-image img { height: 420px; }
.recit-bande-trio img { height: 300px; }
.recit-bande-duo img { height: 360px; }
.recit-duo-image figcaption,
.recit-bande figcaption {
  margin-top: 8px; color: var(--encre-tenue);
  font-size: 12.5px; letter-spacing: .04em; text-transform: uppercase;
}
.recit-precision { color: var(--encre-tenue); font-size: 14px; }

/* Une bande de vues : deux ou trois photos de même hauteur. Elles ne racontent
   rien seules — c'est leur suite qui montre un geste répété, une méthode. */
.recit-bande { display: grid; gap: 18px; }
.recit-bande-trio { grid-template-columns: repeat(3, 1fr); }
.recit-bande-duo { grid-template-columns: repeat(2, 1fr); }
.recit-bande figure { margin: 0; }


.recit-encadre {
  padding: 24px 26px; border-radius: var(--rayon-large);
  background: var(--fond); border: 1px solid var(--filet);
}
.page-libre-corps .prose .recit-encadre h2 { margin-bottom: 10px; font-size: clamp(19px, 1.9vw, 23px); }

@media (max-width: 860px) {
  .recit { gap: 30px; }
  .recit-duo, .recit-duo-inverse { grid-template-columns: 1fr; gap: 18px; }
  /* SUR TÉLÉPHONE, LA PHOTO PASSE APRÈS SON TEXTE, toujours : en colonne, une
     photo placée avant repousse sous la ligne de flottaison le titre qui dit
     de quoi il s'agit. L'alternance n'a de sens qu'à deux colonnes. */
  .recit-duo-inverse .recit-duo-texte { order: -1; }
  .recit-bande-trio { grid-template-columns: repeat(2, 1fr); }
  .recit-bande-trio figure:first-child { grid-column: span 2; }
  .recit-bande-trio figure:first-child img { height: 220px; }
  .recit-duo-image img { height: 300px; }
  .recit-bande-trio img { height: 200px; }
  .recit-bande-duo img { height: 240px; }
  .recit-hero { margin: -20px -18px 0; }
  .recit-hero img { height: 300px; }
  .recit-hero-legende { padding: 40px 18px 18px; }
}

/* Case d'acceptation des conditions de vente, au-dessus du bouton de paiement.
   ELLE DOIT SE VOIR SANS PESER : encadrée comme un bloc à part, elle
   ressemblerait à une option commerciale ; noyée dans le texte, on la coche
   sans la lire. Un fond très légèrement teinté et une case de taille normale
   suffisent — c'est la dernière chose lue avant de payer. */
.champ-cgv { margin: 4px 0 14px; }
.case-cgv {
  display: flex; align-items: flex-start; gap:10px;
  font-size: .92rem; line-height: 1.45; cursor: pointer;
}
.case-cgv input { margin-top:3px; flex: 0 0 auto; width:17px; height:17px; }
.case-cgv a { text-decoration: underline; }
/* Le navigateur signale lui-même une case obligatoire non cochée ; on ne
   colore rien tant que le client n'a pas essayé de valider. */
.case-cgv input:invalid:not(:placeholder-shown) { outline: 2px solid var(--danger, #B00020); }
