/* =====================================================================
   DOLCE COFFEE — direction « CrumbleMagic »
   ---------------------------------------------------------------------
   Feuille unique pour les trois pages du site public : accueil, fiche
   produit, commande. Les applications de service (/console, /admin,
   /commande) gardent leur propre style, on n'y touche pas.

   LES REGLES DE FORME, POSEES UNE FOIS ET TENUES PARTOUT
     - Rayons : boutons = pilule, cartes et visuels = 20px, champs =
       12px, vignettes produit = cercle. Aucun autre rayon.
     - Aucune ombre portee, aucun flou. La hierarchie passe par les
       aplats : chaque bande a sa couleur, chaque carte est un aplat
       plus clair (ou plus sombre) que sa bande.
     - Une bande = une couleur = un role. On ne melange pas deux fonds
       dans une meme section, sauf le hero et le contact qui sont
       volontairement coupes en deux.
   ===================================================================== */

/* =====================================================================
   1. JETONS
   ---------------------------------------------------------------------
   Les jetons portent le ROLE de la couleur, pas son nom : --moyen est
   la bande qui porte le hero et les incontournables, --sombre celle du
   pied et des signatures, --pale les bandes claires. C'est ce qui rend
   le changement de palette gratuit — et ca evite d'avoir un jeton
   nomme --caramel qui contient du vert.

   La palette par defaut est celle de la marque : vert foret + creme,
   reprise du menu papier. La palette caramel/cacao de la reference
   « CrumbleMagic » reste disponible en posant data-palette="caramel"
   sur <html> : la mise en page ne bouge pas d'un pixel.

   CONTRASTES VERIFIES (texte fonce sur la bande moyenne) :
     --sombre #2d3f33 sur --moyen #b8c6a4 ....... 6,3:1  (min 4,5)
     --encre-douce sur --moyen .................. 4,5:1
     --accent #cbd8a8 sur --sombre .............. 7,5:1
   La reference posait du creme sur son caramel (2,2:1) : c'est le seul
   endroit ou l'on s'en ecarte, et c'est deliberé.
   ===================================================================== */
:root {
  --moyen: #b8c6a4;            /* sauge claire : la bande qui porte le hero */
  --moyen-clair: #c9d5b8;      /* cartes posees sur la bande moyenne */
  --moyen-fonce: #6d8163;      /* accent typographique sur fond clair */
  --sombre: #2d3f33;           /* vert foret de la marque */
  --sombre-clair: #3e5344cc;   /* cartes posees sur le vert foret */
  --pale: #efe6cf;             /* creme de la marque */
  --neutre: #ded8c8;           /* bande neutre, un cran sous la creme */
  --accent: #cbd8a8;           /* pistache claire, sur fond sombre uniquement */

  --encre: #2d3f33;
  --encre-douce: rgba(45, 63, 51, .78);
  --encre-tenue: rgba(45, 63, 51, .56);
  --clair: #f5efe0;
  --clair-doux: rgba(245, 239, 224, .76);
  --clair-tenu: rgba(245, 239, 224, .48);

  /* Jetons DERIVES : jamais ecrits en dur ailleurs dans la feuille.
     Ils sont calcules a partir des jetons de palette, donc ils suivent
     automatiquement le changement de palette. C'est la raison d'etre du
     color-mix ici : sans lui il faudrait redeclarer huit valeurs de
     survol dans chaque variante. */
  --sombre-survol: color-mix(in srgb, var(--sombre) 86%, var(--clair));
  --moyen-survol: color-mix(in srgb, var(--moyen-clair) 90%, var(--sombre));
  --voile: color-mix(in srgb, var(--sombre) 55%, transparent);
  --trait-clair: color-mix(in srgb, var(--clair) 15%, transparent);
  --trait-sombre: color-mix(in srgb, var(--sombre) 13%, transparent);
  --pose-clair: color-mix(in srgb, var(--clair) 58%, transparent);
  --pose-tenue: color-mix(in srgb, var(--clair) 32%, transparent);
  --alerte: #9a2f18;
  /* Fond de la boite de navigation. Le vert foret de la marque : il se
     detache aussi bien de la sauge du hero que de la creme de l'en-tete
     defile, et il repond au pied de page, lui aussi vert foret. */
  --boite: var(--sombre);

  /* TYPOGRAPHIE — deux voix, une seule origine
     `--titrage` est choisie pour ressembler au LOGOTYPE : le logo de la
     maison est un didone gras, tres contraste, a terminaisons en boule
     et interlettrage serre. Les cinq candidates de Google Fonts ont ete
     composees cote a cote sous le logo et comparees a l'oeil ; Playfair
     Display est la seule qui reprenne cette structure (Bodoni Moda a des
     empattements trop mecaniques, Prata / DM Serif / Instrument Serif
     sont trop legeres pour la graisse du logo).
     Elle porte donc TOUT ce qui parle au nom de la maison : titres, noms
     de plats, prix, chiffres, citations, navigation.
     `--texte` reste une geometrique neutre pour tout ce qui doit se lire
     vite et petit : paragraphes, etiquettes, boutons, formulaires. Un
     didone a 15px en corps de texte serait illisible — et ne ressemble
     de toute facon au logo qu'en grande taille. */
  --titrage: "Playfair Display", "Iowan Old Style", "Palatino Linotype", Georgia, serif;
  --texte: "Plus Jakarta Sans", system-ui, -apple-system, "Segoe UI", sans-serif;

  --r-carte: 20px;
  --r-champ: 12px;

  --page: 1320px;
  --marge: clamp(20px, 5vw, 64px);

  /* Rythme vertical des bandes. */
  --bande: clamp(64px, 9vw, 112px);

  --sortie: cubic-bezier(.22, 1, .36, 1);
  /* Deceleration plus longue que --sortie, pour les mouvements lents :
     une courbe raide sur un geste lent donne un freinage brutal. */
  --pose: cubic-bezier(.16, 1, .3, 1);

  /* Attente avant l'entree du hero. Vaut zero par defaut : seules les
     pages qui portent l'ouverture (`.intro`) l'arment, via la regle
     ci-dessous. Sans ce jeton, l'entree du hero se jouerait DERRIERE le
     panneau d'ouverture et le visiteur ne verrait qu'une page deja
     posee au moment du balayage. */
  --intro: 0s;
}
/* LE JETON VAUT EXACTEMENT LA FIN DU BALAYAGE, ET RIEN D'AUTRE.
   Le panneau d'ouverture est en `position: fixed; inset: 0` et sort en
   `translateY(-100%)` : il monte, donc il decouvre la page PAR LE BAS et
   l'en-tete est la DERNIERE chose degagee. Toute cascade lancee avant la
   fin du balayage se joue derriere le rideau et n'est jamais vue — c'est
   le defaut signale par le client, quand le jeton valait 3,1s pour un
   balayage qui finissait a 3,55s.
   2,85s depuis le resserrement du generique (voir `.intro`). */
:root:has(.intro) { --intro: 2.85s; }
@media (prefers-reduced-motion: reduce) { :root:has(.intro) { --intro: 0s; } }

/* La palette de la reference, conservee telle quelle. */
[data-palette="caramel"] {
  --moyen: #c39a6b;
  --moyen-clair: #d6b489;
  --moyen-fonce: #8a6534;
  --sombre: #2f1408;
  --sombre-clair: #45231480;
  --pale: #e9e1d4;
  --neutre: #d8d5d0;
  --accent: #d9b183;
  --encre: #2f1408;
  --encre-douce: rgba(47, 20, 8, .78);
  --encre-tenue: rgba(47, 20, 8, .56);
  --clair: #f6efe4;
  --clair-doux: rgba(246, 239, 228, .76);
  --clair-tenu: rgba(246, 239, 228, .48);
}

/* =====================================================================
   2. SOCLE
   ===================================================================== */
*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: 90px;

}

/* BARRE DE DEFILEMENT MASQUEE
   Demande explicite. Le defilement lui-meme est intact : molette, geste
   tactile, barre d'espace, Page haut/bas, Origine/Fin et les fleches
   fonctionnent comme avant — seul le rail visible disparait.
   Il en faut DEUX declarations : `scrollbar-width` est la propriete
   standard (Firefox), `::-webkit-scrollbar` couvre Chrome et Safari, qui
   ne l'implementent pas encore sur la fenetre.
   Pose sur `*` et non sur la seule racine : sinon les rails des panneaux
   internes (tiroir du panier, bandeau de categories) resteraient
   visibles. En contrepartie, tout panneau qui defile sans rail doit
   annoncer sa suite autrement — d'ou les fondus en `mask-image` sur les
   bandeaux, qui laissent voir qu'il reste du contenu au-dela du bord. */
html, * { scrollbar-width: none; }
html::-webkit-scrollbar, *::-webkit-scrollbar { width: 0; height: 0; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  font-family: var(--texte);
  font-size: clamp(15px, .35vw + 14px, 16.5px);
  line-height: 1.6;
  color: var(--encre);
  background: var(--pale);
  -webkit-font-smoothing: antialiased;
}

img, svg { display: block; max-width: 100%; }
img { height: auto; }

/* Les titres portent la voix du logotype.
   Trois reglages viennent avec le changement de fonte, et ils ne sont
   pas cosmetiques :
   - `font-weight: 800` et non 700 : la graisse du logo est proche du
     noir, un 700 de Playfair a cote du logo parait maigre ;
   - `line-height: 1.02` : Playfair a de longues hampes montantes et
     descendantes, l'interligne visuel est plus lache que celui de la
     geometrique a valeur egale ;
   - `letter-spacing: -.02em` seulement : le -.025em qui allait a Plus
     Jakarta Sans fait s'accrocher les empattements entre eux. */
h1, h2, h3, h4, .titrage {
  margin: 0;
  font-family: var(--titrage);
  font-weight: 800;
  line-height: 1.02;
  letter-spacing: -.02em;
  text-wrap: balance;
}
/* CE QUI PORTE ENCORE LA VOIX DU LOGOTYPE, hors titres.
   Regle de partage, tenue partout : la fonte du logo prend les NOMS
   (plats, categories, navigation), les CHIFFRES qu'on lit un par un
   (prix, note Google, total) et les CITATIONS. La geometrique garde les
   phrases, les etiquettes en capitales et les commandes.
   Un prix est un nom autant qu'un nombre — c'est ce qu'on cherche des
   yeux sur une carte — d'ou sa place ici. */
.nav a, .tiroir__panneau a,
.hero__mot, .hero__etiquette__prix,
.carte__nom, .carte__prix,
.signature__nom, .signature__prix,
.mur__note, .mur__note small,
.fiche__nom, .fiche__prix,
.tarif, .base__nom,
.totaux .total {
  font-family: var(--titrage);
  letter-spacing: -.015em;
}
/* Les capitales d'etiquette restent a la geometrique : un didone en
   petites capitales tres espacees perd ses contrastes et devient sale. */
.carte__prix--vide, .fiche__prix--vide { font-family: var(--texte); letter-spacing: .04em; }

p { margin: 0; text-wrap: pretty; }
/* `figure` et `blockquote` arrivent avec `margin: 1em 40px` du
   navigateur. Sans cette remise a zero, la carte d'avis est decalee de
   40px vers l'interieur et la citation s'indente une seconde fois. */
figure, blockquote { margin: 0; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }

:focus-visible {
  outline: 3px solid currentColor;
  outline-offset: 3px;
  border-radius: 4px;
}

.horsecran {
  position: absolute; width: 1px; height: 1px;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}

.enveloppe {
  width: min(100% - var(--marge) * 2, var(--page));
  margin-inline: auto;
}

/* GARDE-FOU CONTRE LE DEFILEMENT HORIZONTAL
   Aucune page de ce site n'a de raison de defiler lateralement. Mesure
   faite au navigateur : l'accueil defilait de 9px vers la droite autour
   de 768px de large — defaut deja present dans la version archivee,
   donc anterieur a cette refonte. Une dizaine de tentatives d'isolement
   (masquage section par section, puis descente recursive) n'ont pas
   designe de coupable unique : plusieurs blocs y contribuent d'un ou
   deux pixels chacun.

   La coupe est posee sur des BLOCS REELS, pas sur `html` : sur la
   racine, la valeur est propagee a la fenetre par le moteur et ne coupe
   rien du tout — essaye, sans effet.

   `clip` et non `hidden` : `hidden` ferait de ces elements des
   conteneurs de defilement, ce qui casserait `position: sticky` (le
   bandeau de la page carte) et figerait les animations liees au
   defilement posees a l'interieur. */
main, .tete, .pied { overflow-x: clip; }

/* Titres de section : une seule echelle pour tout le site. */
.titre-bande {
  font-size: clamp(28px, 3.6vw, 44px);
  text-align: center;
}
.titre-bande--gauche { text-align: left; }

/* =====================================================================
   3. BOUTONS
   ---------------------------------------------------------------------
   Trois variantes seulement. Toutes en pilule, toutes sur une ligne :
   un libelle qui passe a la ligne veut dire que le libelle est trop
   long, pas que le bouton est trop etroit.
   ===================================================================== */
/* LE SURVOL SE LIT A DEUX SIGNES, PAS TROIS
   Une premiere version cumulait un effet magnetique en JavaScript — le
   bouton venait vers le curseur sur seize pixels — et un balayage de
   degrade qui traversait le bouton de gauche a droite. Les deux ont ete
   retires a la demande du client, et les deux etaient discutables :
     - une cible qui se deplace pendant qu'on la vise est plus dure a
       atteindre ; le mouvement travaillait contre le clic ;
     - le balayage donnait un reflet glissant, etranger a un site sans
       ombre ni flou, qui ne joue que sur des aplats.
   Reste un remplissage franc et un agrandissement de 3 % SUR PLACE. Le
   centre du bouton ne bouge pas d'un pixel. */
.bouton {
  display: inline-flex; align-items: center; justify-content: center; gap: 10px;
  min-height: 46px;
  padding: 12px 28px;
  font-size: 15px; font-weight: 600; white-space: nowrap;
  border-radius: 999px;
  transition: background-color .28s var(--sortie), color .28s var(--sortie),
              box-shadow .28s var(--sortie), scale .22s var(--sortie),
              transform .18s var(--sortie);
}
/* `scale` en propriete independante, et non dans `transform` : l'etat
   presse ecrit `transform: scale(.97)`, et les deux doivent pouvoir
   coexister sans s'ecraser. */
.bouton:hover { scale: 1.03; }
.bouton:active { transform: scale(.97); }

.bouton--plein {
  color: var(--clair);
  background-color: var(--sombre);
}
.bouton--plein:hover { background-color: var(--sombre-survol); }

.bouton--trait {
  color: var(--encre);
  box-shadow: inset 0 0 0 1.5px currentColor;
  background-color: transparent;
}
.bouton--trait:hover {
  color: var(--clair);
  background-color: var(--sombre);
  box-shadow: inset 0 0 0 1.5px var(--sombre);
}

.bouton--accent {
  color: var(--sombre);
  background-color: var(--accent);
}
.bouton--accent:hover { background-color: var(--clair); }

.bouton--large { width: 100%; }

/* Le petit rond « ajouter » des cartes produit. */
.rond {
  display: inline-grid; place-items: center;
  width: 42px; height: 42px; flex: 0 0 auto;
  border-radius: 999px;
  background: var(--sombre); color: var(--clair);
  transition: background-color .25s var(--sortie), transform .18s var(--sortie),
              scale .22s var(--sortie);
}
.rond:hover { background: var(--sombre-survol); scale: 1.08; }
.rond:active { transform: scale(.92); }
.rond svg { width: 19px; height: 19px; }

/* =====================================================================
   3 bis. L'OUVERTURE
   ---------------------------------------------------------------------
   Deroule, en secondes :
     0,10 → 1,60   LA FEVE DE CAFE s'assemble LENTEMENT : ses deux
                   moities viennent de loin, en tournant, grandissent et
                   se referment l'une sur l'autre. C'est le temps fort du
                   generique, il prend donc a lui seul presque la moitie
                   de sa duree ;
     1,55 → 2,40   la decoupe s'ouvre depuis la feve vers l'exterieur,
                   « Dolce » et « Coffee » poussent de part et d'autre ;
     2,30 → 2,75   la ligne d'adresse monte ;
     2,85 → 3,55   le panneau balaye vers le haut.

   L'assemblage utilise `--pose`, une deceleration plus longue que le
   `--sortie` du reste du site : sur un mouvement lent, une courbe trop
   raide donne l'impression que l'objet freine d'un coup.

   Tout est en CSS et se termine sur `visibility: hidden` : sans
   JavaScript, le panneau s'efface quand meme. Le JS ne fait que rendre le
   defilement et marquer le panneau comme fini.

   LE PANNEAU N'EST PAS RETIRE DU DOCUMENT, et c'est indispensable.
   Le hero et l'en-tete attendent la fin du balayage pour entrer en scene :
   c'est le role du jeton `--intro` (section 1), arme par
   `:root:has(.intro)`. Retirer le noeud faisait cesser la correspondance,
   `--intro` retombait a `0s` et TOUTE la cascade sautait a son etat final
   derriere le rideau. Voir `ouverture()` dans js/site.js.
   ===================================================================== */
.intro {
  position: fixed; inset: 0; z-index: 200;
  display: grid; place-items: center;
  background: var(--sombre);
  /* 2,30s et 0,55s de course — le balayage se termine donc a 2,85s au lieu
     de 3,55s. Seule la FIN du generique a ete resserree ; l'assemblage de
     la feve garde ses 1,5 secondes, le client les a demandees lentes. */
  animation: introSort .55s var(--sortie) 2.3s forwards;
}
.intro__centre { display: grid; justify-items: center; gap: clamp(20px, 2.6vw, 34px); }

/* Les deux couches se superposent au pixel : meme viewBox, meme
   largeur, memes coordonnees de trace. La feve de la couche 2 tombe
   donc exactement sur celle de la couche 1, et le passage de l'une a
   l'autre ne se voit pas. */
/* UNIQUEMENT une fois le balayage fini, jamais avant.
   Le panneau reste dans le document (voir plus haut) : il ne doit plus
   intercepter de clic. Mais poser `pointer-events: none` des le depart
   serait pire que le mal — les clics traverseraient le rideau et
   atteindraient la navigation cachee dessous pendant les 3,5s de
   l'ouverture. `visibility: hidden` suffirait ; ceci le double. */
.intro[data-fini] { pointer-events: none; }

.intro__marque {
  --l: min(64vw, 520px);
  /* Bords de la feve dans le logotype, mesures au navigateur sur la
     boite englobante des traces #3 et #5. */
  --feve-g: 43.71%;
  --feve-d: 45.11%;
  position: relative; display: block;
  width: var(--l); aspect-ratio: 1258 / 227;
}
.intro__feve, .intro__logo {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  color: var(--clair);
}

/* ETAPE 1 — les deux moities de la feve se referment l'une sur l'autre.
   `transform-box: fill-box` fait porter la rotation sur le centre de
   chaque moitie et non sur l'origine du viewBox, sinon elles partiraient
   a l'autre bout du dessin. */
.feve { transform-box: fill-box; transform-origin: center; }
.intro__feve .feve--g { animation: feveGauche 1.5s var(--pose) .1s both; }
.intro__feve .feve--d { animation: feveDroite 1.5s var(--pose) .1s both; }

/* ETAPE 2 — la decoupe part de la seule feve et s'ouvre vers les deux
   cotes : les mots poussent depuis le centre. */
/* CACHE JUSQU'A 1,55 s. Sans ce `opacity: 0`, cette couche affichait
   deja la feve — decoupee sur sa seule zone — pendant que les deux
   moities volaient encore vers le centre : on voyait donc DEUX feves,
   une immobile et une en train de s'assembler.
   `forwards` ne remplit que l'APRES : pendant le delai, c'est le style
   de base qui s'applique, donc invisible. Et comme la feve de cette
   couche tombe au pixel sur celle de l'etape 1, la bascule ne se voit
   pas. */
.intro__logo {
  opacity: 0;
  clip-path: inset(0 var(--feve-d) 0 var(--feve-g));
  animation: introOuvre .6s var(--sortie) 1.55s forwards;
}
/* La feve seule s'efface a l'instant ou le logotype prend le relais. */
.intro__feve { animation: feveSort 0s linear 1.55s forwards; }

.intro__ligne {
  font-size: clamp(11px, 1vw, 13px); font-weight: 600;
  letter-spacing: .24em; text-transform: uppercase;
  color: var(--clair-tenu);
  opacity: 0;
  /* Elle apparait pendant que la decoupe du logotype finit de s'ouvrir,
     au lieu d'attendre son tour : c'est ce chevauchement qui fait gagner
     du temps sans donner l'impression de precipitation. */
  animation: introLigne .35s var(--sortie) 1.95s forwards;
}

/* Course allongee en meme temps que la duree : un mouvement lent sur
   une courte distance ne parait pas lent, il parait mou. Les moities
   viennent de plus loin, tournent davantage et grandissent en route.
   L'opacite est acquise tot (35 %) pour qu'on VOIE le trajet, au lieu
   de deux formes qui apparaissent deja presque en place. */
@keyframes feveGauche {
  from { transform: translate(-210px, 120px) rotate(-58deg) scale(.62); opacity: 0; }
  35% { opacity: 1; }
  to { transform: none; opacity: 1; }
}
@keyframes feveDroite {
  from { transform: translate(210px, -120px) rotate(58deg) scale(.62); opacity: 0; }
  35% { opacity: 1; }
  to { transform: none; opacity: 1; }
}
@keyframes introOuvre {
  from { opacity: 1; clip-path: inset(0 var(--feve-d) 0 var(--feve-g)); }
  to { opacity: 1; clip-path: inset(0 0 0 0); }
}
@keyframes feveSort { to { opacity: 0; } }
@keyframes introLigne { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }
@keyframes introSort { to { transform: translateY(-100%); visibility: hidden; } }

/* Mouvement reduit : pas d'ouverture du tout. Le panneau est retire
   immediatement et le hero demarre sans attendre. */
@media (prefers-reduced-motion: reduce) {
  .intro { display: none; }
}

/* =====================================================================
   4. EN-TETE
   ---------------------------------------------------------------------
   Sur l'accueil, l'en-tete est transparent : le hero est deja coupe en
   sauge / vert foret et cette coupe doit monter jusqu'en haut de page,
   comme sur la reference. Des qu'on defile, il devient un aplat
   sauge plein.
   ===================================================================== */
.tete {
  position: fixed; inset: 0 0 auto; z-index: 60;
  transition: background-color .3s var(--sortie);
}
/* Pose une fois le hero passe (voir entete() dans js/site.js). Tant que
   le hero est a l'ecran, l'en-tete reste transparent pour laisser la
   coupe bicolore monter jusqu'en haut.
   Le fond est le CREME de la marque, pas la sauge : l'en-tete se pose
   sur des bandes de toutes les couleurs du site, il lui faut donc la
   teinte la plus neutre de la palette. */
.tete--pleine { background: var(--pale); }
/* Les pages interieures n'ont pas de hero bicolore : fond des le depart. */
.tete--opaque { background: var(--pale); }

/* Trois colonnes SYMETRIQUES (1fr auto 1fr) et non `auto 1fr auto` :
   avec des colonnes laterales de largeurs differentes — le logo est plus
   large que les deux icones — la navigation se retrouvait centree sur
   l'espace restant, donc decalee de 39px vers la droite par rapport au
   milieu de la page. Ici les deux cotes pesent pareil, la navigation
   tombe pile au centre. */
.tete__in {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 24px;
  height: 74px;
}

/* CONTRASTE : tout l'en-tete est en --sombre, jamais en clair.
   La reference pose un logo creme sur son caramel ; mesure faite, ce
   couple plafonne a 2,2:1 alors que le minimum lisible est 4,5:1. Le
   vert foret sur la sauge donne 6,3:1. Comme l'en-tete survole toujours
   la bande moyenne (le hero n'est sombre qu'a droite, sous le mot
   geant), une seule couleur suffit et rien ne clignote au defilement. */
/* LE RATIO DU LOGOTYPE, ET POURQUOI IL EST INDISPENSABLE
   Le <svg> qui porte le <use> n'a ni `viewBox` ni dimensions : le
   `viewBox` vit sur le <symbol>, pas sur l'element qui l'instancie.
   Un SVG sans ratio intrinseque et en `height: auto` retombe sur la
   hauteur par defaut des elements remplaces : 150px. Le logo mesurait
   donc 150px dans un en-tete de 74, la ligne de grille etait etiree a
   sa taille, et `align-items: center` centrait tout le monde sur 150 —
   navigation et icones se retrouvaient 38px trop bas, a cheval sur le
   bord de l'en-tete. Poser le ratio suffit a tout remettre d'aplomb. */
.marque svg, .pied__marque svg { aspect-ratio: 1258 / 227; }

.marque { display: inline-flex; align-items: center; justify-self: start; }
.marque svg { width: clamp(126px, 13vw, 168px); height: auto; color: var(--sombre); }

/* LA BOITE DE NAVIGATION
   Les liens sont poses sur une boite blanche, de « La carte » a
   « Contact ». Elle se detache aussi bien de l'aplat sauge du hero
   (en-tete transparent) que de la creme de l'en-tete defile.
   Rayon en PILULE : c'est le rayon de tous les elements interactifs du
   site (voir la regle de forme en tete de feuille), donc la barre de
   navigation parle enfin le meme langage que les boutons. Une premiere
   version etait a 12px, trop rectangulaire pour une barre de 46px. */
.nav {
  display: flex; justify-content: center; align-items: center;
  gap: clamp(4px, .6vw, 10px);
  padding: 5px;
  background: var(--boite);
  border-radius: 999px;
}
/* La boite etant sombre, les liens passent en clair. */
/* 16.5px la ou la geometrique tenait a 15 : la hauteur d'x de Playfair
   est nettement plus basse, a corps egal le libelle parait plus petit. */
.nav a {
  padding: 8px clamp(14px, 1.4vw, 22px);
  font-size: 16.5px; font-weight: 600; white-space: nowrap;
  color: var(--clair-doux);
  border-radius: 999px;
  transition: color .22s var(--sortie), background-color .25s var(--sortie);
}
/* A l'interieur d'une boite, un souligne se lit mal : l'element survole
   prend un voile clair, l'element actif un aplat pistache plein. */
.nav a:hover { color: var(--clair); background: color-mix(in srgb, var(--clair) 13%, transparent); }
.nav a[aria-current="page"] { color: var(--sombre); background: var(--accent); }

.outils { display: flex; align-items: center; justify-self: end; gap: 6px; }
.outils button, .outils a {
  position: relative;
  display: grid; place-items: center;
  width: 42px; height: 42px; border-radius: 999px;
  color: var(--sombre);
  transition: background-color .22s var(--sortie);
}
/* Survol : un voile clair tant que l'en-tete est transparent (les outils
   sont alors sur le panneau fonce), un voile sombre des qu'il est creme. */
.outils button:hover, .outils a:hover { background: var(--pose-tenue); }
.tete--pleine .outils button:hover, .tete--pleine .outils a:hover,
.tete--opaque .outils button:hover, .tete--opaque .outils a:hover { background: var(--trait-sombre); }
.outils svg { width: 21px; height: 21px; }

.pastille {
  position: absolute; top: 4px; right: 3px;
  min-width: 18px; height: 18px; padding: 0 5px;
  display: grid; place-items: center;
  font-size: 11px; font-weight: 700; line-height: 1;
  background: var(--sombre); color: var(--clair);
  border-radius: 999px;
}
.pastille[hidden] { display: none; }

/* EXCEPTION, et la seule : sur l'accueil non defile, les outils sont a
   l'extreme droite, donc AU-DESSUS du panneau sombre du hero. Du vert
   foret sur du vert foret ne se voit pas. Ils passent en clair tant que
   l'en-tete est transparent, et reviennent au sombre des qu'il prend
   son fond sauge. En dessous de 900px le hero est d'un seul aplat
   clair : la regle ne s'applique plus. */
@media (min-width: 901px) {
  .tete:not(.tete--pleine):not(.tete--opaque) .outils button,
  .tete:not(.tete--pleine):not(.tete--opaque) .outils a { color: var(--clair); }
  .tete:not(.tete--pleine):not(.tete--opaque) .pastille {
    background: var(--clair); color: var(--sombre);
  }
}

/* `.outils button` pose deja `display: grid` et pese plus lourd qu'un
   simple `.burger` : ecrit sans le parent, le `display: none` etait
   ignore et le burger restait visible en grand ecran. */
.outils .burger { display: none; }

/* --- Tiroir mobile ---------------------------------------------------- */
/* Les deux panneaux hors-ecran (ce tiroir et le panier) attendent a
   DROITE de la fenetre, en `translateX(100%)`. Sans clipping, ils
   allongent la zone defilable du document et le site gagne une barre de
   defilement horizontale en mobile. Chacun depassant de son cote,
   masquer l'un ne suffit pas a le prouver — d'ou un diagnostic qui ne
   trouvait aucun coupable unique. On coupe donc au bord de la fenetre :
   le panneau glisse depuis l'exterieur, il n'y a rien a voir dehors. */
.tiroir {
  position: fixed; inset: 0; z-index: 80;
  display: grid; grid-template-columns: 1fr min(84vw, 340px);
  visibility: hidden;
  overflow: clip;
}
.tiroir[data-ouvert="1"] { visibility: visible; }
.tiroir__voile {
  background: var(--voile); opacity: 0;
  transition: opacity .3s var(--sortie);
}
.tiroir[data-ouvert="1"] .tiroir__voile { opacity: 1; }
.tiroir__panneau {
  display: flex; flex-direction: column; gap: 8px;
  padding: 22px var(--marge) 40px;
  background: var(--sombre); color: var(--clair);
  transform: translateX(100%);
  transition: transform .38s var(--sortie);
  overflow-y: auto;
}
.tiroir[data-ouvert="1"] .tiroir__panneau { transform: none; }
.tiroir__haut { display: flex; justify-content: flex-end; margin-bottom: 18px; }
.tiroir__x { width: 42px; height: 42px; display: grid; place-items: center; border-radius: 999px; }
.tiroir__x svg { width: 22px; height: 22px; }
.tiroir__panneau a {
  padding: 14px 0;
  font-size: 21px; font-weight: 600;
  border-bottom: 1px solid var(--trait-clair);
}
.tiroir__contact { margin-top: 26px; font-size: 14.5px; color: var(--clair-doux); }
.tiroir__contact a { border: 0; font-size: 15px; }

/* =====================================================================
   5. HERO
   ---------------------------------------------------------------------
   Le fond est coupe en deux verticalement, comme sur la reference : le
   la sauge porte le discours, le vert foret porte le mot geant et sert de
   fond au visuel detoure. Le decoupage est fait en degrade a arret
   franc plutot qu'en deux blocs : le visuel peut alors chevaucher la
   frontiere sans se faire couper.
   ===================================================================== */
/* La coupe est a 72 %, pas aux deux tiers de la reference. Raison
   mesuree : la navigation est centree dans le conteneur, et a 1000-1200px
   de large son dernier element (« Contact ») depasse les 66 % et se
   retrouve en vert foret sur vert foret. A 72 % elle reste dans la sauge
   sur toute la plage bureau, et les outils, eux, restent bien sur le
   panneau sombre. */
.hero {
  position: relative;
  background: linear-gradient(90deg, var(--moyen) 0 72%, var(--sombre) 72% 100%);
  /* `clip` et NON `hidden`. Les deux coupent pareil, mais `hidden` fait
     de l'element un CONTENEUR DE DEFILEMENT : une animation en
     `animation-timeline: view()` posee a l'interieur s'accroche alors a
     ce conteneur — qui ne defile jamais — et reste figee a son etat de
     depart. C'est ce qui immobilisait la parallaxe du mot geant.
     `clip` coupe sans creer de conteneur de defilement. */
  overflow: clip;
}

.hero__in {
  display: grid;
  /* La colonne de texte est la plus large : c'est elle qui contraint la
     mise en page, parce que le titre doit tenir sur deux lignes. Le
     visuel, lui, est plafonne a 480px et se contente de ce qui reste. */
  grid-template-columns: minmax(0, 1.12fr) minmax(0, 1fr);
  align-items: center;
  gap: clamp(16px, 3vw, 48px);
  min-height: min(760px, 100svh);
  padding-block: 116px 64px;
}

/* Le titre est bicolore comme sur la reference, mais dans l'autre sens.
   La reference eclaircit les mots accentues (creme sur caramel : 2,2:1,
   illisible). Ici on garde le meme geste en fonçant : le corps du titre
   est a 78 % de vert foret (4,4:1) et le mot accentue passe a 100 % (6,3:1).
   Meme rythme visuel, deux niveaux qui se lisent tous les deux. */
/* Le <br> prevoit DEUX lignes : la taille doit donc etre calee sur la
   plus longue, « La maison s'en charge. » (22 caracteres). Mesure faite
   dans le navigateur : cette police tient environ 0,48 em par caractere
   a cette graisse, soit ~10,5 em pour la ligne. La colonne de gauche du
   hero fait ~626px a 1440 : 54px de corps donne ~570px, ca passe ;
   60px donnait 630px et cassait la ligne en deux. */
.hero__titre {
  font-size: clamp(30px, 3.8vw, 54px);
  max-width: 22ch;
  color: color-mix(in srgb, var(--sombre) 78%, transparent);
}
.hero__titre em { font-style: normal; color: var(--sombre); }
/* La ligne qui tourne. `inline-block` pour qu'elle accepte une largeur
   minimale — un element en ligne l'ignorerait — et cette largeur est
   figee par accueil.js sur la plus longue des accroches, sinon le titre
   se retrecit et s'elargit a chaque echange. */
.hero__accroche { display: inline-block; }
.hero__texte {
  margin-top: 20px;
  max-width: 42ch;
  font-size: clamp(15px, .5vw + 14px, 17px);
  color: var(--encre-douce);
}
.hero__actions { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 30px; }

.hero__scene { position: relative; justify-self: center; width: 100%; }
/* LE CARROUSEL DE CANNETTES
   Les quatre tiramisus sont empiles au meme endroit ; seul celui qui
   porte `data-actif` est visible. Le conteneur a un rapport carre fixe,
   donc la mise en page ne bouge pas d'un pixel quand on change d'image.
   Le flottement et l'inclinaison sont poses sur le CONTENEUR : les
   images gardent ainsi `transform` pour leur entree et leur sortie. */
.hero__carrousel {
  position: relative; z-index: 2;
  width: min(100%, 480px);
  aspect-ratio: 1;
  margin-inline: auto;
  filter: drop-shadow(0 26px 42px color-mix(in srgb, var(--sombre) 40%, transparent));
}
.hero__carrousel img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: contain;
  opacity: 0;
  /* Etat d'attente : la cannette suivante patiente en bas, tournee. */
  transform: rotate(-14deg) scale(.82) translateY(9%);
  transition: opacity .85s var(--sortie), transform .85s var(--sortie);
}
.hero__carrousel img[data-actif] { opacity: 1; transform: none; }
/* Celle qui part continue son mouvement vers le haut, de l'autre cote :
   sans cet etat, elle reviendrait en arriere et le passage se lirait
   comme un aller-retour au lieu d'une rotation. */
.hero__carrousel img[data-sortant] { opacity: 0; transform: rotate(14deg) scale(.82) translateY(-9%); }

/* L'etiquette : nom et prix de la cannette a l'ecran, cliquable. */
.hero__etiquette {
  position: relative; z-index: 3;
  display: flex; justify-content: center;
  margin-top: clamp(8px, 1.4vw, 18px);
}
.hero__etiquette a {
  display: inline-flex; align-items: center; gap: 12px;
  padding: 8px 8px 8px 18px;
  font-size: 14.5px; font-weight: 600;
  background: var(--sombre); color: var(--clair);
  border-radius: 999px;
  transition: background-color .25s var(--sortie);
}
.hero__etiquette a:hover { background: var(--sombre-survol); }
.hero__etiquette__prix {
  padding: 5px 13px;
  font-size: 13px;
  background: var(--accent); color: var(--sombre);
  border-radius: 999px;
}
/* Le mot geant, pose a la verticale sur le vert foret. `writing-mode`
   plutot qu'une rotation : le texte reste du vrai texte, pas une image.
   Il occupe toute la hauteur du panneau et defile en boucle — la piste
   est montee par `js/accueil.js`. */
.hero__mot {
  /* Il demarre SOUS l'en-tete : en partant de tout en haut, le ruban
     venait encombrer l'icone du panier, a l'extreme droite. */
  position: absolute; top: 82px; bottom: 0; right: clamp(-30px, -2vw, 0px); z-index: 1;
  margin: 0;
  writing-mode: vertical-rl;
  font-size: clamp(64px, 10.5vw, 132px);
  font-weight: 700; letter-spacing: -.04em; line-height: 1;
  color: var(--moyen);
  overflow: clip;
  pointer-events: none;
  user-select: none;
  /* Les deux bouts du ruban s'estompent : sans ca, la boucle a l'air
     coupee au couteau en haut et en bas. C'est un fondu d'opacite, pas
     un flou — la regle « aucun flou » du client tient. */
  mask-image: linear-gradient(to bottom, transparent, #000 9%, #000 91%, transparent);
}
/* `inline-block` et NON `block`. En `writing-mode: vertical-rl`, l'axe
   en ligne est VERTICAL : un bloc s'etire alors sur toute la hauteur du
   conteneur, et le `-50%` de l'animation se calculait sur cette hauteur
   (480px) au lieu de la longueur reelle de la piste (~4276px) — la
   boucle sautait au bout de 240px. En inline-block, la piste est
   dimensionnee par son contenu et le pourcentage retombe juste. */
.hero__mot__piste { display: inline-block; white-space: nowrap; }
/* Le point de separation entre deux repetitions. Purement decoratif :
   il est dessine par le CSS, donc absent du texte lu par un moteur. */
.hero__mot__piste span::after {
  content: "·";
  margin-block: .18em;
  opacity: .55;
}

/* =====================================================================
   5 bis. BANDEAU DES CATEGORIES
   ---------------------------------------------------------------------
   Juste sous le hero : les onze categories de la carte defilent en
   continu. Ce n'est pas un ornement — c'est la seule facon de montrer
   l'etendue de la carte avant que le visiteur ne scrolle. Chaque nom
   est un vrai lien vers sa section.
   Un seul bandeau defilant sur tout le site : deux, ca devient du
   remplissage.
   ===================================================================== */
.bandeau {
  padding: 16px 0;
  background: var(--sombre); color: var(--clair);
  overflow: hidden;
}
.bandeau__piste {
  display: flex; width: max-content;
  /* La piste contient DEUX fois la liste : quand la premiere moitie a
     entierement defile, la seconde est exactement a sa place de depart,
     et la boucle est invisible. D'ou le -50% dans `defile`. */
}
.bandeau__piste a, .bandeau__piste span {
  display: inline-flex; align-items: center; gap: clamp(24px, 3vw, 48px);
  padding-right: clamp(24px, 3vw, 48px);
  font-size: clamp(15px, 1.2vw, 18px); font-weight: 600; white-space: nowrap;
  color: var(--clair-doux);
  transition: color .2s var(--sortie);
}
.bandeau__piste a:hover { color: var(--accent); }
/* Le point de separation est decoratif : il ne doit pas etre lu a voix
   haute par un lecteur d'ecran. */
.bandeau__piste a::after {
  content: ""; flex: 0 0 auto;
  width: 5px; height: 5px; border-radius: 999px;
  background: currentColor; opacity: .5;
}

/* =====================================================================
   6. BANDES
   ===================================================================== */
.bande { padding-block: var(--bande); }
.bande--pale { background: var(--pale); }
.bande--moyen { background: var(--moyen); }
.bande--sombre { background: var(--sombre); color: var(--clair); }
.bande--neutre { background: var(--neutre); }

.bande--sombre .titre-bande { color: var(--clair); }

/* --- 6.a Le savoir-faire : visuels detoures + texte ------------------- */
.savoir {
  display: grid;
  grid-template-columns: minmax(0, 1.08fr) minmax(0, 1fr);
  align-items: center;
  gap: clamp(28px, 5vw, 72px);
}
.savoir__rang {
  display: flex; align-items: flex-end; justify-content: center;
  gap: clamp(4px, 1.4vw, 18px);
}
/* Les quatre visuels sont poses en dents de scie et de tailles
   differentes : alignes et egaux, ils faisaient catalogue.
   Le decalage est ecrit en `translate` et NON en `transform` : la
   propriete `transform` est reservee a l'animation de flottement
   (section 13), qui l'ecraserait sinon et remettrait les quatre
   visuels au meme niveau. */
.savoir__rang img { width: 26%; }
.savoir__rang img:nth-child(1) { width: 24%; translate: 0 10px; }
.savoir__rang img:nth-child(2) { width: 29%; }
.savoir__rang img:nth-child(3) { width: 25%; translate: 0 16px; }
.savoir__rang img:nth-child(4) { width: 27%; translate: 0 4px; }
.savoir__texte { text-align: right; }
.savoir__texte p {
  margin-top: 18px; margin-left: auto;
  max-width: 46ch; color: var(--encre-douce);
}

/* --- 6.b Grille de cartes produit ------------------------------------ */
.grille-produits {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(16px, 2vw, 28px);
  margin-top: clamp(32px, 4vw, 52px);
}

.carte {
  display: grid;
  grid-template-columns: clamp(96px, 11vw, 132px) minmax(0, 1fr);
  align-items: center;
  gap: clamp(14px, 1.6vw, 22px);
  padding: clamp(14px, 1.6vw, 20px);
  background: var(--moyen-clair);
  border-radius: var(--r-carte);
  transition: background-color .3s var(--sortie), transform .3s var(--sortie);
}
.carte:hover { background: var(--moyen-survol); transform: translateY(-7px); }
.bande--sombre .carte { background: var(--sombre-clair); }
.bande--sombre .carte:hover { background: var(--sombre-survol); }

.carte__ph {
  aspect-ratio: 1; width: 100%;
  object-fit: cover;
  border-radius: 999px;
}
.carte__nom { font-size: clamp(16px, 1.2vw, 19px); font-weight: 700; }
.carte__txt {
  margin-top: 6px;
  font-size: 13px; line-height: 1.45; color: var(--encre-douce);
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; overflow: hidden;
}
.bande--sombre .carte__txt { color: var(--clair-doux); }
.carte__bas {
  display: flex; align-items: center; flex-wrap: wrap; gap: 10px;
  margin-top: 14px;
}
.carte__prix { font-size: clamp(17px, 1.3vw, 21px); font-weight: 700; margin-right: auto; }
.carte__prix--vide { font-size: 13px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; }
.carte .bouton { min-height: 40px; padding: 9px 22px; font-size: 14px; }
.bande--sombre .carte .bouton--plein { background: var(--accent); color: var(--sombre); }
.bande--sombre .carte .bouton--plein:hover { background: var(--clair); }
.bande--sombre .carte .rond { background: var(--accent); color: var(--sombre); }
.bande--sombre .carte .rond:hover { background: var(--clair); }

/* --- 6.c Les signatures : deux grandes cartes a vignette debordante --- */
.signatures {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(24px, 3.4vw, 56px);
  margin-top: clamp(76px, 8vw, 104px);
}
.signature {
  position: relative;
  padding: clamp(88px, 8vw, 108px) clamp(22px, 2.6vw, 36px) clamp(24px, 2.6vw, 34px);
  text-align: center;
  background: var(--sombre-clair);
  border-radius: var(--r-carte);
  transition: background-color .3s var(--sortie);
}
.signature:hover { background: var(--sombre-survol); }
.signature__ph {
  position: absolute; top: clamp(-72px, -6vw, -56px); left: 50%;
  width: clamp(148px, 15vw, 196px); aspect-ratio: 1;
  object-fit: cover;
  border-radius: 999px;
  transform: translateX(-50%);
  transition: transform .45s var(--sortie), scale .45s var(--sortie);
}
.signature__nom { font-size: clamp(19px, 1.6vw, 25px); }
.signature__txt {
  margin: 12px auto 0; max-width: 44ch;
  font-size: 14px; color: var(--clair-doux);
}
.signature__bas {
  display: flex; align-items: center; justify-content: center; gap: 14px;
  margin-top: 22px;
}
.signature__prix { font-size: clamp(19px, 1.5vw, 24px); font-weight: 700; }
/* Le rond d'ajout est pose sur une carte deja sombre : il doit passer
   en pistache, sinon c'est du vert foret sur du vert foret. */
.signature .rond { background: var(--accent); color: var(--sombre); }
.signature .rond:hover { background: var(--clair); }

/* --- 6.d Composez la votre ------------------------------------------- */
.composer {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
  align-items: center;
  gap: clamp(28px, 5vw, 72px);
}
.composer__titre { font-size: clamp(28px, 3.4vw, 42px); }
.composer__titre span { color: var(--moyen-fonce); }
.composer__texte { margin-top: 18px; max-width: 46ch; color: var(--encre-douce); }
.composer__liste {
  display: grid; gap: 2px;
  margin: 26px 0 0; padding: 0; list-style: none;
  border-radius: var(--r-champ); overflow: hidden;
}
.composer__liste li {
  display: grid; grid-template-columns: minmax(0, auto) 1fr; align-items: baseline;
  column-gap: 12px;
  padding: 13px 16px;
  background: var(--pose-clair);
}
.composer__liste b { font-size: 14.5px; }
.composer__liste .sup { grid-column: 2; justify-self: end; font-size: 13px; font-weight: 700; }
.composer__liste .det {
  grid-column: 1 / -1; margin-top: 4px;
  font-size: 12.5px; line-height: 1.5; color: var(--encre-tenue);
}
.composer__ph { border-radius: var(--r-carte); aspect-ratio: 4 / 3; object-fit: cover; width: 100%; }

/* --- 6.e Le mur d'avis ------------------------------------------------
   Deux colonnes qui derivent en sens INVERSE pendant qu'on passe devant.
   Le mouvement fait deux choses a la fois : il montre qu'il y a plus
   d'avis que ce que le cadre laisse voir, et il donne a la section une
   vie que douze cartes posees n'auraient pas.

   Le cadre est coupe en `clip` et non `hidden` : `hidden` en ferait un
   conteneur de defilement, et les animations en `animation-timeline:
   view()` posees a l'interieur s'y accrocheraient et resteraient figees.
   ---------------------------------------------------------------------- */
.mur {
  display: grid;
  grid-template-columns: minmax(0, .82fr) minmax(0, 1fr);
  align-items: center;
  gap: clamp(28px, 5vw, 72px);
}

.mur__eyebrow {
  font-size: 11.5px; font-weight: 700;
  letter-spacing: .22em; text-transform: uppercase;
  color: var(--moyen-fonce);
}
/* La note en tres grand : c'est le chiffre qui porte la section, il doit
   se lire avant le texte. */
.mur__note {
  display: flex; align-items: baseline; gap: 8px;
  margin-top: 6px;
  font-size: clamp(66px, 8vw, 108px); font-weight: 800;
  letter-spacing: -.04em; line-height: .9;
  color: var(--sombre);
}
.mur__note small { font-size: clamp(16px, 1.4vw, 20px); font-weight: 700; color: var(--encre-tenue); }
.mur__titre { margin-top: 18px; font-size: clamp(24px, 2.6vw, 34px); max-width: 22ch; }
.mur__mot { margin-top: 14px; max-width: 40ch; font-size: 14.5px; color: var(--encre-douce); }
/* Les deux boutons passent a la ligne plutot que de rétrécir : sur la
   colonne étroite du mur, « Voir la fiche Google » et « Ajouter un avis »
   ne tiennent pas cote a cote en dessous de ~460px de large. */
.mur__actions {
  display: flex; flex-wrap: wrap; gap: 10px;
  margin-top: 26px;
}

.mur__colonnes {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(12px, 1.4vw, 20px);
  height: clamp(420px, 46vw, 560px);
  overflow: clip;
  /* Les avis s'estompent en haut et en bas : le mur n'a pas de fin
     visible, ce qui est exactement ce qu'on veut dire. */
  mask-image: linear-gradient(to bottom, transparent, #000 14%, #000 86%, transparent);
}
.mur__col { display: flex; flex-direction: column; }
.mur__piste { display: flex; flex-direction: column; gap: clamp(12px, 1.4vw, 20px); padding-bottom: clamp(12px, 1.4vw, 20px); }

.avisc {
  padding: clamp(15px, 1.7vw, 20px);
  background: var(--moyen-clair);
  border-radius: var(--r-carte);
}
.avisc__h { display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: 11px; }

/* L'AVATAR. Une photo quand on l'a ; sinon un monogramme — l'initiale
   sur un aplat — exactement ce que Google affiche pour un compte sans
   photo. On ne fabrique pas de portrait pour une personne reelle. */
.avisc__av {
  width: 40px; height: 40px; flex: 0 0 auto;
  border-radius: 999px; object-fit: cover;
}
.avisc__av--mono {
  display: grid; place-items: center;
  font-size: 16px; font-weight: 800;
  color: var(--clair);
}
.avisc__av--mono[data-teinte="0"] { background: #6d8163; }
.avisc__av--mono[data-teinte="1"] { background: #4b6b52; }
.avisc__av--mono[data-teinte="2"] { background: #87996f; }
.avisc__av--mono[data-teinte="3"] { background: #566e5c; }
.avisc__av--mono[data-teinte="4"] { background: #75886a; }

.avisc__ident { min-width: 0; }
.avisc__n { display: block; font-size: 14px; font-weight: 700; }
.avisc__meta { display: flex; align-items: center; gap: 8px; margin-top: 3px; }
.avisc__etoiles { display: inline-flex; gap: 1px; }
.avisc__e { display: block; width: 12px; height: 12px; color: var(--encre-tenue); opacity: .3; }
.avisc__e--plein { color: #f2b32e; opacity: 1; }
.avisc__e svg { width: 100%; height: 100%; }
.avisc__d { font-size: 12px; color: var(--encre-tenue); }
.avisc__g { width: 17px; height: 17px; flex: 0 0 auto; opacity: .85; }
.avisc__g svg { width: 100%; height: 100%; }

.avisc__t { margin-top: 13px; font-size: clamp(13.5px, 1.1vw, 15.5px); line-height: 1.5; }

/* --- 6.f Bloc de rappel centre ----------------------------------------
   Sert desormais aux blocs de fin de page (« Toute la carte vous
   attend », « Une allergie ? »). L'infolettre a ete retiree : sans outil
   d'envoi derriere, elle collectait des adresses inutilisables.
   ---------------------------------------------------------------------- */
.lettre { text-align: center; }
.lettre p { margin: 16px auto 0; max-width: 56ch; color: var(--encre-douce); }

/* --- 6.g Contact : bande coupee en deux ------------------------------- */
/* LE RAPPORT DE COLONNES EST CALCULE, PAS CHOISI
   A 1440px de large, la colonne gauche empile etat + titre + intro +
   deux liens + sous-titre + formulaire, soit ~880px. La droite doit
   loger les horaires (~330px, sept lignes) et donner au plan la place
   qui reste, ~500px : un plan plus court qu'un carre ne sert a rien.
   D'ou 1fr / .9fr — pas l'inverse de l'ancienne version. */
.contact { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, .9fr); }
.contact__gauche {
  padding: var(--bande) clamp(24px, 4vw, 64px);
  background: var(--sombre); color: var(--clair);
}
/* LA REGLE QUI SUPPRIME LE TROU
   `1fr` sur le plan, `auto` sur les horaires. Les horaires prennent
   leur hauteur naturelle, le plan avale tout le reste. `minmax(240px,
   1fr)` et non `1fr` seul : si un jour la colonne gauche devenait plus
   courte que les horaires, le plan tomberait a zero et disparaitrait
   sans erreur — le plancher l'en empeche.

   PLUS DE BICOLORE : la section entiere est en vert foret, a la demande
   du client. La version precedente coupait le fond en deux, sombre a
   gauche et sauge a droite, sur la verticale de la grille — le raccord
   se voyait d'autant plus que les deux colonnes n'ont pas la meme
   largeur. En un seul aplat, c'est le PLAN qui devient la seule tache
   claire de la section, et c'est tres bien : c'est l'element qu'on
   cherche. */
.contact__droite {
  display: grid;
  grid-template-rows: minmax(240px, 1fr) auto;
  padding: var(--bande) clamp(24px, 4vw, 64px);
  background: var(--sombre); color: var(--clair);
}
/* Sur un seul aplat, le rembourrage interieur des deux colonnes n'a plus
   a etre symetrique : la gauche garde sa marge de page, la droite n'a
   plus besoin d'en reprendre une a son bord gauche. */
.contact__droite { padding-left: 0; }
@media (max-width: 900px) {
  .contact__droite { padding-left: var(--marge); }
}
/* L'ETAT EN DIRECT
   Vert pistache quand c'est ouvert, teinte eteinte quand c'est ferme.
   Pas de rouge : « ferme » n'est pas une erreur, c'est un horaire. */
.etat {
  display: inline-flex; align-items: center; gap: 11px;
  margin-bottom: 22px; padding: 8px 18px 8px 15px;
  font-size: 14px; font-weight: 700;
  border-radius: 999px;
}
.etat[hidden] { display: none; }
.etat__pastille {
  width: 9px; height: 9px; flex: 0 0 auto;
  border-radius: 999px;
  background: currentColor;
}
.etat[data-ouvert="1"] { color: var(--sombre); background: var(--accent); }
.etat[data-ouvert="0"] { color: var(--clair-doux); background: var(--sombre-clair); }

/* `margin-top` explicite : l'interligne des titres est passe a 1,02 avec
   la fonte du logo, il ne reste plus assez d'air sous la derniere ligne
   pour separer le titre de son chapeau. */
.contact__intro { margin-top: 16px; max-width: 34ch; font-size: clamp(15px, .5vw + 14px, 17px); color: var(--clair-doux); }
.contact__liste { display: grid; gap: 12px; margin-top: 28px; }
.contact__liste a, .contact__liste div {
  display: flex; align-items: center; gap: 14px;
  padding: 13px 20px;
  font-size: 14.5px;
  background: var(--sombre-clair);
  border-radius: 999px;
  transition: background-color .25s var(--sortie);
}
.contact__liste a:hover { background: var(--sombre-survol); }
.contact__liste svg { width: 18px; height: 18px; flex: 0 0 auto; color: var(--accent); }

/* Le sous-titre du formulaire, maintenant sur le panneau sombre. Il est
   un cran sous le titre de section : la section a UNE seule adresse
   (« Passer nous voir »), le formulaire n'en est qu'un moyen. */
.contact__sous {
  margin-top: clamp(30px, 3.4vw, 44px);
  font-size: clamp(22px, 2vw, 30px);
}
/* Le formulaire a change de fond : il etait sur la sauge, il est sur le
   vert foret. Les etiquettes doivent suivre, sinon on lit du vert foncé
   sur du vert foncé. */
.contact__gauche .champ > label { color: var(--clair-doux); }

.contact__horaires { margin-top: 30px; }
.contact__horaires h3 { font-size: 15px; letter-spacing: .06em; text-transform: uppercase; }
.contact__horaires ul { display: grid; gap: 1px; margin: 14px 0 0; padding: 0; list-style: none; }
/* Les horaires sont sur le vert foret : teintes claires, et le jour
   courant en pistache, qui donne 7,5:1 sur ce fond.
   Une etape intermediaire les avait passes sur la sauge, ou la pistache
   plafonnait a 1,4:1 — illisible. La section etant repassee en aplat
   sombre, on retrouve le bon couple. */
.contact__horaires li {
  display: flex; justify-content: space-between; gap: 16px;
  padding: 9px 0;
  font-size: 14px; color: var(--clair-doux);
  border-bottom: 1px solid var(--trait-clair);
}
.contact__horaires li[data-aujourdhui] { color: var(--accent); font-weight: 700; }
.contact__horaires li[data-ferme] { color: var(--clair-tenu); }
/* Les jours et leurs tranches prennent la fonte du logo : ce sont des
   noms et des chiffres qu'on lit un par un, pas des phrases.
   `tabular-nums` est OBLIGATOIRE ici : les chiffres de Playfair sont
   proportionnels par defaut, le « 1 » de 15h30 est deux fois plus etroit
   qu'un « 0 », et les sept tranches ne s'alignent plus en colonne. */
.contact__horaires li {
  font-family: var(--titrage);
  font-size: 15.5px;
  font-variant-numeric: tabular-nums;
}

/* --- Le plan ----------------------------------------------------------
   L'iframe Google arrive avec son propre cadre : `border: 0` l'enleve.
   Le rayon est porte par le conteneur en `overflow: clip`, car un rayon
   pose sur l'iframe elle-meme n'est pas respecte par tous les moteurs.
   ---------------------------------------------------------------------- */
/* PAS D'`aspect-ratio` ICI, ET C'EST LE POINT ENTIER DE LA REFONTE.
   Un format impose donnerait au plan une hauteur propre, et la colonne
   retrouverait aussitot le trou qu'on vient de supprimer. Le plan est au
   contraire le seul element elastique de la section : il vaut la hauteur
   de sa piste de grille, et cette piste vaut « tout ce qui reste ».
   `min-height: 0` est indispensable — sans lui, un enfant de grille
   refuse de descendre sous la hauteur de son contenu, et l'iframe en
   `height: 100%` ferait deborder la piste. */
.plan {
  position: relative;
  min-height: 0;
  border-radius: var(--r-carte);
  overflow: clip;
  background: var(--sombre-clair);
}
/* Sur une seule colonne, « ce qui reste » ne veut plus rien dire : la
   piste `1fr` d'une grille en flux vertical se reduit au contenu, donc a
   zero. Le format revient donc en dessous du point de rupture. */
@media (max-width: 900px) {
  .plan { aspect-ratio: 16 / 11; }
}
.plan iframe { width: 100%; height: 100%; border: 0; display: block; }
.plan__lien {
  position: absolute; bottom: 12px; left: 12px;
  display: inline-flex; align-items: center; gap: 8px;
  padding: 9px 16px;
  font-size: 13.5px; font-weight: 700;
  background: var(--sombre); color: var(--clair);
  border-radius: 999px;
  transition: background-color .25s var(--sortie);
}
.plan__lien:hover { background: var(--accent); color: var(--sombre); }
.plan__lien svg { width: 14px; height: 14px; }

/* =====================================================================
   7. FORMULAIRES
   ---------------------------------------------------------------------
   Etiquette AU-DESSUS du champ, jamais un placeholder qui sert
   d'etiquette. Les erreurs s'affichent SOUS le champ concerne.
   ===================================================================== */
.form { display: grid; gap: 16px; margin-top: 26px; }
.form__duo { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; }

.champ { display: grid; gap: 7px; }
.champ > label { font-size: 13.5px; font-weight: 600; }
.champ input, .champ select, .champ textarea {
  width: 100%;
  padding: 13px 16px;
  font: inherit; font-size: 15px; color: var(--encre);
  background: var(--clair);
  border: 1.5px solid transparent;
  border-radius: var(--r-champ);
  transition: border-color .22s var(--sortie);
}
.champ textarea { min-height: 118px; resize: vertical; field-sizing: content; }
.champ input::placeholder, .champ textarea::placeholder { color: var(--encre-tenue); }
.champ input:focus, .champ select:focus, .champ textarea:focus {
  outline: none; border-color: var(--sombre);
}
.champ input:user-invalid, .champ textarea:user-invalid { border-color: var(--alerte); }
.champ__erreur { font-size: 13px; font-weight: 600; color: var(--alerte); }
.champ__erreur:empty { display: none; }

/* Choix en pastilles radio (sur place / a emporter, etc.). */
.choix { display: grid; gap: 10px; }
.choix__opt {
  display: flex; align-items: center; gap: 14px;
  padding: 14px 18px;
  background: var(--clair);
  border: 1.5px solid transparent;
  border-radius: var(--r-champ);
  cursor: pointer;
  transition: border-color .22s var(--sortie);
}
.choix__opt:has(input:checked) { border-color: var(--sombre); }
.choix__opt input { accent-color: var(--sombre); width: 18px; height: 18px; flex: 0 0 auto; }
.choix__opt span { font-size: 15px; font-weight: 600; }
.choix__opt small { display: block; font-size: 13px; font-weight: 500; color: var(--encre-tenue); }

.case { display: flex; align-items: flex-start; gap: 11px; font-size: 14px; cursor: pointer; }
.case input { accent-color: var(--sombre); width: 17px; height: 17px; margin-top: 2px; flex: 0 0 auto; }

.message {
  padding: 14px 18px;
  font-size: 14.5px; font-weight: 600;
  background: var(--clair); color: var(--encre);
  border-radius: var(--r-champ);
}
.message[hidden] { display: none; }

/* =====================================================================
   L'ECRAN DU VERROU DE TABLE
   ---------------------------------------------------------------------
   S'affiche dans le tiroir du panier et sur la page de commande quand
   aucun QR de table n'a ete scanne. Il explique, il ne reproche rien :
   la personne n'a pas fait d'erreur, elle n'est simplement pas encore
   assise. D'ou un pictogramme de QR code plutot qu'un cadenas — le
   cadenas dit « interdit », le QR dit « voici comment entrer ».
   ===================================================================== */
.verrou { padding: clamp(22px, 4vw, 38px) 4px; text-align: center; }
.verrou__pic {
  display: grid; place-items: center;
  width: 62px; height: 62px;
  margin: 0 auto 18px;
  border-radius: 18px;
  background: color-mix(in srgb, var(--sombre) 9%, transparent);
  color: var(--sombre);
}
.verrou__pic svg { width: 30px; height: 30px; }
.verrou__titre {
  font-family: var(--titrage);
  font-size: clamp(20px, 2vw, 26px); font-weight: 800;
  letter-spacing: -.02em; line-height: 1.1;
}
.verrou__mot {
  margin: 10px auto 0; max-width: 34ch;
  font-size: 14.5px; line-height: 1.55; color: var(--encre-douce);
}
.verrou__actions {
  display: flex; flex-wrap: wrap; justify-content: center; gap: 10px;
  margin-top: 22px;
}

/* Le numero de table, sur la page de commande. Il n'est pas saisissable :
   c'est une information, pas un champ — un champ libre rouvrirait le
   contournement que le QR ferme. */
.tablette {
  display: flex; align-items: center; gap: 16px;
  padding: 16px 20px;
  background: var(--pose-clair);
  border-radius: var(--r-champ);
}
.tablette__num {
  display: grid; place-items: center;
  width: 54px; height: 54px; flex: 0 0 auto;
  font-family: var(--titrage);
  font-size: 26px; font-weight: 800;
  border-radius: 16px;
  background: var(--sombre); color: var(--clair);
}
.tablette__mot { font-size: 13.5px; line-height: 1.5; color: var(--encre-douce); }

/* =====================================================================
   8. QUESTIONS FREQUENTES
   ---------------------------------------------------------------------
   Deux panneaux : les huit questions a gauche, la reponse a droite.
   Le raisonnement complet est dans `faq()` de accueil.js — en resume,
   un accordeon empile pousse les questions vers le bas a chaque
   ouverture, contraint la reponse a la largeur de la question, et
   interdit de comparer deux reponses.

   Le panneau de reponse est COLLANT : sur une liste de huit lignes,
   choisir la derniere question depuis le haut de la liste laisserait
   sinon la reponse hors du champ.
   ===================================================================== */
.faqx__tete {
  display: flex; flex-wrap: wrap; align-items: baseline;
  justify-content: space-between; gap: 16px;
}
.faqx__chapeau { font-size: clamp(14px, .5vw + 13px, 16px); color: var(--encre-tenue); }

/* PAS d'`align-items: start` ICI.
   Une premiere version l'avait, et le panneau de reponse se contentait
   alors de la hauteur de son texte : 365px de panneau pour 776px de
   liste, donc 410px de creme vide dessous — exactement le defaut qu'on
   venait de corriger dans le contact. En etirement (le comportement par
   defaut d'une grille), le panneau vaut toute la hauteur des huit
   questions qu'il couvre. */
.faqx {
  display: grid;
  grid-template-columns: minmax(0, .95fr) minmax(0, 1.05fr);
  gap: clamp(6px, .7vw, 10px) clamp(24px, 3vw, 48px);
  margin-top: clamp(28px, 3.4vw, 42px);
}

/* Les questions sont forcees en colonne 1. Sans cette declaration, le
   placement automatique les repartirait sur les deux colonnes et elles
   viendraient se glisser sous le panneau de reponse. */
.faqx__q {
  grid-column: 1;
  display: grid;
  grid-template-columns: 1fr auto;
  align-content: center;
  gap: 6px 16px;
  width: 100%;
  padding: 12px clamp(16px, 1.6vw, 22px);
  text-align: left;
  border-radius: var(--r-champ);
  transition: background-color .28s var(--sortie), color .28s var(--sortie);
}
.faqx__q:hover { background: color-mix(in srgb, var(--sombre) 7%, transparent); }
.faqx__q[aria-expanded="true"] { background: var(--sombre); color: var(--clair); }

.faqx__tag {
  grid-column: 1;
  font-family: var(--texte);
  font-size: 11px; font-weight: 700;
  letter-spacing: .1em; text-transform: uppercase;
  color: var(--moyen-fonce);
  transition: color .28s var(--sortie);
}
.faqx__q[aria-expanded="true"] .faqx__tag { color: var(--accent); }

.faqx__lib {
  grid-column: 1;
  font-family: var(--titrage);
  font-size: clamp(17px, 1.4vw, 21px); font-weight: 700; line-height: 1.2;
  letter-spacing: -.015em;
  text-wrap: balance;
}

/* La fleche occupe les deux rangees de la question, a droite. Elle
   avance de 5px a la selection : un deplacement, pas une rotation — la
   liste designe un panneau situe A DROITE, le geste doit pointer vers
   lui. */
.faqx__fleche {
  grid-column: 2; grid-row: 1 / 3;
  width: 22px; height: 22px;
  opacity: 0;
  translate: -8px 0;
  transition: opacity .28s var(--sortie), translate .28s var(--sortie);
}
.faqx__q:hover .faqx__fleche { opacity: .45; translate: 0 0; }
.faqx__q[aria-expanded="true"] .faqx__fleche { opacity: 1; translate: 5px 0; color: var(--accent); }

/* `grid-row` est pose par accueil.js : sa borne depend du nombre de
   questions, et `1 / -1` viserait la fin de la grille explicite, qui
   n'existe pas ici. */
/* En colonne FLEX et non en grille : c'est ce qui permet a la ligne
   d'aide de descendre au bas du panneau par un simple `margin-top: auto`.
   La reponse reste en haut, l'aide se colle en bas, et le vert foret
   entre les deux reste nu — le panneau couvre toute la hauteur des huit
   questions, c'est ce qui evite le trou dans la bande. */
.faqx__rep {
  grid-column: 2;
  display: flex; flex-direction: column;
  gap: clamp(16px, 1.8vw, 24px);
  padding: clamp(26px, 3vw, 40px);
  background: var(--sombre); color: var(--clair);
  border-radius: var(--r-carte);
  overflow: clip;
}
.faqx__aide { margin-top: auto; }
.faqx__tagrep {
  font-size: 11.5px; font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase;
  color: var(--accent);
}
/* LA REPONSE EST TRAITEE COMME UNE DECLARATION, PAS COMME DU CORPS.
   C'est le geste qui distingue cette FAQ d'un accordeon : la reponse a
   enfin la place d'etre composee en grand, dans la fonte du logo. */
.faqx__texte {
  font-family: var(--titrage);
  font-size: clamp(19px, 1.5vw + 12px, 26px);
  line-height: 1.32; letter-spacing: -.01em;
  color: var(--clair);
  text-wrap: pretty;
}
.faqx__aide {
  display: flex; flex-wrap: wrap; align-items: center;
  justify-content: space-between; gap: 14px;
  padding-top: clamp(14px, 1.6vw, 20px);
  border-top: 1px solid var(--trait-clair);
}
.faqx__aide span { font-size: 14px; color: var(--clair-doux); }

/* EN PILE
   La grille laisse la place a une colonne flex, et le panneau se glisse
   sous la question active via `order` (pose par accueil.js). Les
   proprietes de grille restent ecrites mais sont simplement ignorees.
   Le collant est retire : en pile, un panneau collant se decrocherait de
   la question qui l'a ouvert. */
@media (max-width: 900px) {
  .faqx { display: flex; flex-direction: column; gap: 8px; }
  /* `margin-top: auto` n'a plus de sens sans hauteur excedentaire : on
     le remet a zero pour que l'aide suive directement la reponse. */
  .faqx__rep { margin-block: 6px 14px; }
  .faqx__aide { margin-top: 0; }
  .faqx__texte { font-size: clamp(18px, 3.6vw, 22px); }
}

/* =====================================================================
   9. PIED
   ===================================================================== */
.pied { padding-block: clamp(48px, 6vw, 72px) 34px; background: var(--sombre); color: var(--clair); }
/* Trois colonnes de liens et non quatre : « Contact » et « En salle »
   ne portaient qu'un ou deux liens chacune, ce qui donnait deux colonnes
   presque vides a cote d'une colonne pleine. Elles sont fondues en
   « Nous joindre », et les liens orphelins de la galerie et de la FAQ
   ont rejoint les colonnes ou on les cherche. */
.pied__in {
  display: grid;
  grid-template-columns: minmax(0, 1.3fr) repeat(3, minmax(0, 1fr));
  gap: clamp(24px, 3vw, 44px);
}
.pied__marque svg { width: 168px; height: auto; color: var(--clair); }

/* LA SIGNATURE DE FIN A ETE RETIREE
   Une version precedente fermait la page sur le logotype compose pleine
   largeur. Retire a la demande du client. Le pied se termine donc sur sa
   ligne de bas de page, et le credit d'agence y est un vrai lien.
   `.pied__credit` : le lien se distingue du texte qui le porte, sinon
   « Core Agency » se lit comme la fin de la phrase et personne ne le
   clique. */
.pied__credit a {
  color: var(--clair);
  font-weight: 700;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  text-decoration-color: color-mix(in srgb, var(--clair) 45%, transparent);
  transition: text-decoration-color .22s var(--sortie);
}
.pied__credit a:hover { text-decoration-color: var(--accent); color: var(--accent); }
.pied__mot { margin-top: 18px; max-width: 30ch; font-size: 14px; color: var(--clair-doux); }
.pied h3 {
  font-size: 12.5px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
  color: var(--accent);
}
.pied ul { display: grid; gap: 9px; margin: 16px 0 0; padding: 0; list-style: none; }
.pied li a { font-size: 14px; color: var(--clair-doux); transition: color .2s var(--sortie); }
.pied li a:hover { color: var(--clair); }

/* =====================================================================
   LES TROIS TUILES SOCIALES DU PIED
   ---------------------------------------------------------------------
   EN CARREAUX, a la demande du client, et c'est la seule entorse a la
   regle de forme du site (pilule sur tout ce qui est interactif).
   6px ET NON 12 : une premiere version reprenait le rayon des champs pour
   rester dans le vocabulaire deja en place. Le client a redemande des
   carreaux — a 12px sur une tuile de 42, l'arrondi occupe plus du quart du
   cote et la forme se lit encore comme un bouton adouci. A 6px elle se lit
   comme un carreau, ce qui etait la demande.

   PLUS D'INVERSION DE COULEUR AU SURVOL, et c'est une correction de fond.
   L'ancienne version passait la tuile en pistache ET l'icone en vert
   fonce. Les deux etats finaux etaient contrastes (11,1:1 au repos,
   7,5:1 au survol) mais le CHEMIN entre les deux ne l'etait pas :
   `transition` ne portait que sur `background-color`, donc l'icone
   basculait au vert d'un coup pendant que le fond mettait 220ms a
   s'eclaircir. Mesure au milieu du parcours : 2,88:1, sous le minimum de
   3:1 exige d'un pictogramme — l'icone disparaissait une fraction de
   seconde. Transitionner `color` aussi n'aurait rien arrange : les deux
   couleurs se croisant en meme temps, le milieu tombait a 1,2:1.
   La bonne reponse est de ne pas croiser du tout. L'icone reste creme
   dans les deux etats, seule la tuile change de valeur : le contraste ne
   descend jamais sous 3,7:1, a aucun instant de la transition.
   ===================================================================== */
.reseaux { display: flex; gap: 10px; margin-top: 16px; }
.reseaux a {
  display: grid; place-items: center;
  width: 44px; height: 44px;
  border-radius: 6px;
  color: var(--clair);
  background: color-mix(in srgb, var(--clair) 12%, transparent);
  transition: background-color .22s var(--sortie), scale .2s var(--sortie);
}
.reseaux a:hover {
  background: var(--moyen-fonce);
  scale: 1.06;
}
.reseaux svg { width: 18px; height: 18px; }

.pied__bas {
  display: flex; flex-wrap: wrap; justify-content: space-between; gap: 12px;
  margin-top: clamp(38px, 5vw, 58px); padding-top: 22px;
  font-size: 13px; color: var(--clair-tenu);
  border-top: 1px solid var(--trait-clair);
}

/* =====================================================================
   10. PANIER (tiroir lateral, partage par les trois pages)
   ===================================================================== */
.panier {
  position: fixed; inset: 0; z-index: 90;
  display: grid; grid-template-columns: 1fr min(92vw, 420px);
  visibility: hidden;
  overflow: clip;   /* meme raison que .tiroir, voir plus haut */
}
.panier[data-ouvert="1"] { visibility: visible; }
.panier__voile { background: var(--voile); opacity: 0; transition: opacity .3s var(--sortie); }
.panier[data-ouvert="1"] .panier__voile { opacity: 1; }
.panier__panneau {
  display: flex; flex-direction: column;
  background: var(--pale);
  transform: translateX(100%);
  transition: transform .38s var(--sortie);
}
.panier[data-ouvert="1"] .panier__panneau { transform: none; }
.panier__haut {
  display: flex; align-items: center; justify-content: space-between;
  padding: 20px 24px;
  border-bottom: 1px solid var(--trait-sombre);
}
.panier__haut h2 { font-size: 19px; }
.panier__x { width: 40px; height: 40px; display: grid; place-items: center; border-radius: 999px; }
.panier__x svg { width: 20px; height: 20px; }
.panier__corps { flex: 1; overflow-y: auto; padding: 20px 24px; }
.panier__vide { padding: 48px 0; text-align: center; color: var(--encre-tenue); }
.panier__vide p { margin-bottom: 20px; }

.ligne {
  display: grid; grid-template-columns: 62px minmax(0, 1fr) auto;
  align-items: center; gap: 14px;
  padding: 14px 0;
  border-bottom: 1px solid var(--trait-sombre);
}
.ligne img { width: 62px; aspect-ratio: 1; object-fit: cover; border-radius: 999px; }
.ligne__nom { font-size: 14.5px; font-weight: 700; }
.ligne__prix { margin-top: 3px; font-size: 13.5px; color: var(--encre-tenue); }
.ligne__sup { margin-top: 6px; font-size: 12.5px; font-weight: 600; color: var(--encre-tenue); }
.ligne__sup:hover { color: var(--alerte); }

.compteur {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 4px;
  background: var(--clair);
  border-radius: 999px;
}
.compteur button {
  width: 30px; height: 30px; display: grid; place-items: center;
  border-radius: 999px;
  transition: background-color .2s var(--sortie);
}
.compteur button:hover { background: var(--moyen-clair); }
.compteur svg { width: 13px; height: 13px; }
.compteur output { min-width: 26px; text-align: center; font-size: 14.5px; font-weight: 700; }

.panier__bas { padding: 20px 24px; border-top: 1px solid var(--trait-sombre); }
.panier__total {
  display: flex; justify-content: space-between; align-items: baseline;
  margin-bottom: 16px;
  font-size: 19px; font-weight: 700;
}
.panier__note { margin-top: 12px; font-size: 12.5px; color: var(--encre-tenue); text-align: center; }

/* =====================================================================
   11. FICHE PRODUIT
   ===================================================================== */
.fiche { padding-block: 118px var(--bande); background: var(--sombre); color: var(--clair); }
.fiche__in {
  display: grid;
  grid-template-columns: minmax(0, .8fr) minmax(0, 1fr);
  align-items: start;
  gap: clamp(28px, 5vw, 76px);
}
.fiche__ph {
  width: 100%; aspect-ratio: 1; object-fit: cover;
  background: var(--sombre-clair);
  border-radius: var(--r-carte);
}
.fiche__famille { font-size: 14px; color: var(--clair-doux); }
.fiche__nom { margin-top: 8px; font-size: clamp(30px, 3.6vw, 46px); }
.fiche__prix { margin-top: 18px; font-size: clamp(24px, 2.4vw, 32px); font-weight: 700; }
.fiche__prix--vide { font-size: clamp(15px, 1.2vw, 18px); color: var(--accent); }
.fiche__qte { margin-top: 26px; }
.fiche__qte > span { display: block; margin-bottom: 8px; font-size: 13.5px; font-weight: 600; }
.fiche .compteur { background: var(--sombre-clair); }
.fiche .compteur button:hover { background: var(--sombre-survol); }
.fiche__texte { margin-top: 24px; max-width: 58ch; color: var(--clair-doux); }
.fiche__actions { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 30px; }
.fiche__actions .bouton--plein { background: var(--accent); color: var(--sombre); }
.fiche__actions .bouton--plein:hover { background: var(--clair); }
.fiche__actions .bouton--trait { color: var(--clair); }
.fiche__actions .bouton--trait:hover { background: var(--clair); color: var(--sombre); }

.fiche__suite { padding-block: var(--bande); background: var(--sombre); color: var(--clair); }
.fiche__suite .titre-bande { color: var(--clair); }

.aussi { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: clamp(16px, 2vw, 28px); margin-top: 36px; }
.aussi .carte { background: var(--sombre-clair); }
.aussi .carte:hover { background: var(--sombre-survol); }
.aussi .carte__txt { color: var(--clair-doux); }
.aussi .carte .bouton--plein { background: var(--accent); color: var(--sombre); }
.aussi .carte .rond { background: var(--accent); color: var(--sombre); }

/* =====================================================================
   12. PAGE COMMANDE
   ---------------------------------------------------------------------
   Meme composition que la reference — recapitulatif sombre a gauche,
   formulaire clair a droite — mais SANS champ de carte bancaire : le
   site ne prend pas le paiement, le reglement se fait au comptoir au
   moment du retrait. Un formulaire qui collecte un numero de carte
   sans le traiter n'a aucune raison d'exister.
   ===================================================================== */
.commande { display: grid; grid-template-columns: minmax(0, .82fr) minmax(0, 1.18fr); min-height: 100svh; }
.commande__recap {
  padding: 118px clamp(24px, 4vw, 60px) var(--bande);
  background: var(--sombre); color: var(--clair);
}
.commande__form {
  padding: 118px clamp(24px, 4vw, 60px) var(--bande);
  background: var(--pale);
}
.fil { display: flex; align-items: center; gap: 10px; font-size: 14px; color: var(--clair-doux); }
.fil svg { width: 13px; height: 13px; }
.fil b { font-weight: 700; color: var(--clair); }

.recap__tete {
  display: flex; justify-content: space-between;
  margin: 30px 0 4px;
  font-size: 13px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  color: var(--clair-tenu);
}
.commande__recap .ligne { border-bottom-color: var(--trait-clair); }
.commande__recap .ligne__prix { color: var(--clair-doux); }
.commande__recap .compteur { background: var(--sombre-clair); }
.commande__recap .compteur button:hover { background: var(--sombre-survol); }
.commande__recap .ligne__sup { color: var(--clair-tenu); }

.totaux { margin-top: 26px; display: grid; gap: 10px; }
.totaux div { display: flex; justify-content: space-between; font-size: 14.5px; color: var(--clair-doux); }
.totaux .total {
  margin-top: 8px; padding-top: 14px;
  font-size: 20px; font-weight: 700; color: var(--clair);
  border-top: 1px solid var(--trait-clair);
}

.bloc { margin-bottom: clamp(28px, 3.4vw, 44px); }
.bloc > h2 { font-size: clamp(20px, 1.8vw, 25px); margin-bottom: 16px; }

/* =====================================================================
   12 bis. LA CARTE COMPLETE
   ---------------------------------------------------------------------
   Onze categories, plus de soixante-dix articles. La page doit rester
   parcourable : bandeau de raccourcis colle en haut, recherche locale,
   et deux colonnes en grand ecran comme une double page de menu.
   ===================================================================== */
.carte-hero {
  padding: 122px 0 clamp(40px, 5vw, 64px);
  background: var(--sombre); color: var(--clair);
  text-align: center;
}
.carte-hero h1 { font-size: clamp(34px, 5vw, 60px); }
.carte-hero p {
  margin: 18px auto 0; max-width: 54ch;
  color: var(--clair-doux);
}

/* --- Bandeau de raccourcis ------------------------------------------- */
/* Sur la page carte, une ancre doit s'arreter SOUS l'en-tete ET sous le
   bandeau de raccourcis, sinon le titre vise se retrouve dessous. */
html:has(.rail) { scroll-padding-top: 172px; }

.rail {
  position: sticky; top: 74px; z-index: 40;
  padding: 14px 0;
  background: var(--pale);
}
.rail__in { display: flex; align-items: center; gap: 14px; }

.puces {
  display: flex; gap: 8px;
  overflow-x: auto;
  scrollbar-width: none;
  scroll-snap-type: x proximity;
  -webkit-overflow-scrolling: touch;
}
.puces::-webkit-scrollbar { display: none; }
.puce {
  flex: 0 0 auto;
  padding: 9px 16px;
  font-size: 13.5px; font-weight: 600; white-space: nowrap;
  color: var(--encre-douce);
  background: var(--moyen-clair);
  border-radius: 999px;
  scroll-snap-align: start;
  transition: background-color .22s var(--sortie), color .22s var(--sortie);
}
.puce:hover { background: var(--moyen); }
.puce--ici { background: var(--sombre); color: var(--clair); }

.chercher {
  display: flex; align-items: center; gap: 9px; flex: 0 0 auto;
  padding: 0 16px;
  background: var(--clair);
  border-radius: 999px;
}
.chercher svg { width: 16px; height: 16px; color: var(--encre-tenue); flex: 0 0 auto; }
.chercher input {
  width: clamp(120px, 16vw, 200px);
  padding: 11px 0;
  font: inherit; font-size: 14.5px; color: var(--encre);
  background: none; border: 0;
}
.chercher input::placeholder { color: var(--encre-tenue); }
.chercher input:focus { outline: none; }
.chercher:focus-within { outline: 2.5px solid var(--sombre); outline-offset: 2px; }
.chercher__compte {
  font-size: 13px; font-weight: 600; color: var(--encre-tenue);
  white-space: nowrap;
}

/* --- Les categories --------------------------------------------------- */
/* DEUX COLONNES EN FLUX, PAS UNE GRILLE
   Une grille a deux colonnes aligne les categories par RANGEES : une
   categorie de trois articles posee a cote d'une categorie de neuf
   laisse un grand blanc sous la premiere, et une categorie pleine
   largeur laisse carrement une cellule vide a cote d'elle. La mise en
   colonnes, elle, coule le contenu comme un journal : chaque colonne se
   remplit, sans trou. `break-inside: avoid` garde chaque categorie d'un
   seul tenant, et `column-span` sert aux deux categories « à composer »,
   dont les listes d'ingredients ont besoin de toute la largeur. */
.categories {
  columns: 2;
  column-gap: clamp(18px, 2.2vw, 30px);
  padding-block: clamp(28px, 4vw, 48px) var(--bande);
}
.cat {
  break-inside: avoid;
  margin-bottom: clamp(18px, 2.2vw, 30px);
  padding: clamp(20px, 2.4vw, 32px);
  background: var(--moyen-clair);
  border-radius: var(--r-carte);
}
.cat--large { column-span: all; }
.cat[hidden] { display: none; }

.cat__tete {
  display: flex; align-items: baseline; flex-wrap: wrap; gap: 6px 14px;
  padding-bottom: 14px;
  border-bottom: 1px solid var(--trait-sombre);
}
.cat__titre { font-size: clamp(21px, 1.9vw, 27px); }
.cat__sous { font-size: 13.5px; font-weight: 600; color: var(--encre-tenue); }
.cat__note {
  margin-top: 14px;
  font-size: 13.5px; line-height: 1.5; color: var(--encre-tenue);
}

/* La pastille de prix, reprise du menu papier.
   ELLE NE S'APPELLE PAS `.pastille` : ce nom est deja pris par le
   compteur du panier dans l'en-tete, qui est en `position: absolute`.
   Reutiliser la classe faisait heriter ce positionnement, et les cinq
   prix d'une categorie venaient s'empiler dans le coin haut-droit du
   panneau — d'autant plus surement que `.cat` porte `.rv`, dont
   l'animation applique un `transform` qui en fait un bloc conteneur. */
.tarif {
  flex: 0 0 auto;
  padding: 6px 15px;
  font-size: 14.5px; font-weight: 700; white-space: nowrap;
  color: var(--encre);
  background: var(--clair);
  border-radius: 999px;
}
.tarif--sup { font-size: 12.5px; }
.tarif--neuf {
  font-size: 11.5px; letter-spacing: .07em; text-transform: uppercase;
  background: var(--sombre); color: var(--clair);
}

/* --- Base d'une categorie a composer ---------------------------------- */
.base {
  display: flex; align-items: center; gap: 18px;
  margin-top: 18px; padding: 16px 18px;
  background: var(--pose-clair);
  border-radius: var(--r-champ);
}
.base__nom { display: block; font-size: 16px; font-weight: 700; }
.base__txt { display: block; margin-top: 4px; font-size: 13.5px; color: var(--encre-douce); }
.base div { flex: 1; }

/* --- Grille de supplements -------------------------------------------- */
.compo { display: grid; gap: 2px; margin: 14px 0 0; padding: 0; list-style: none; }
.compo li {
  display: grid;
  grid-template-columns: minmax(0, auto) 1fr;
  align-items: baseline; column-gap: 12px;
  padding: 13px 18px;
  background: var(--pose-clair);
  border-radius: var(--r-champ);
}
.compo__k { font-size: 14.5px; font-weight: 700; }
.compo li .tarif { grid-column: 2; justify-self: end; }
.compo__l {
  grid-column: 1 / -1; margin-top: 5px;
  font-size: 13px; line-height: 1.55; color: var(--encre-tenue);
}

/* --- Les lignes d'articles --------------------------------------------
   Pas de filet sous chaque ligne : c'est le motif que le client a
   refuse. On alterne le fond une ligne sur deux, comme un menu
   imprime. `:nth-of-type` et non `:nth-child` : les intertitres ne
   doivent pas decaler l'alternance.
   ---------------------------------------------------------------------- */
.lignes { margin-top: 16px; }
.lignes__groupe {
  margin: 20px 0 8px;
  font-size: 15px; letter-spacing: .04em; text-transform: uppercase;
  color: var(--moyen-fonce);
}
.lignes__groupe[hidden] { display: none; }

.ligne-carte {
  display: flex; align-items: center; gap: 14px;
  padding: 10px 14px;
  border-radius: var(--r-champ);
  transition: background-color .25s var(--sortie);
}
.ligne-carte[hidden] { display: none; }
.ligne-carte:nth-of-type(odd) { background: var(--pose-clair); }
.ligne-carte:hover { background: color-mix(in srgb, var(--sombre) 8%, transparent); }
.ligne-carte--fille { margin-left: clamp(12px, 2vw, 26px); }
.ligne-carte__b { flex: 1; min-width: 0; }
/* Le nom du plat prend la fonte du logo : sur une carte, c'est le mot
   qu'on cherche des yeux. La description reste a la geometrique — c'est
   une phrase, elle se lit d'un trait. */
.ligne-carte__n {
  font-family: var(--titrage);
  font-size: 17px; font-weight: 700; line-height: 1.2;
  letter-spacing: -.015em;
}
.ligne-carte__d {
  display: block; margin-top: 3px;
  font-size: 13px; line-height: 1.45; color: var(--encre-tenue);
}

/* =====================================================================
   LES VIGNETTES DE LA CARTE
   ---------------------------------------------------------------------
   Deux natures pour un seul gabarit : la photo du plat quand la maison
   nous l'a donnee, l'icone de la famille sinon. Le pourquoi est dans
   js/icones.js — en un mot, une icone ne pretend pas etre la
   photographie du plat, une photo approchante si.

   Meme encombrement dans les deux cas : c'est ce qui permet aux noms de
   tous les articles de demarrer sur la meme verticale, photo ou pas.
   Le rayon suit celui des champs et non la pilule des boutons : une
   vignette n'est pas un element interactif.
   ===================================================================== */
.vign {
  flex: 0 0 auto;
  display: grid; place-items: center;
  width: 52px; height: 52px;
  border-radius: 14px;
  overflow: clip;
}
.vign--photo img {
  width: 100%; height: 100%;
  object-fit: cover;
  /* La photo grossit legerement au survol de la ligne. Le recadrage se
     fait sous le rayon, d'ou le `overflow: clip` du parent. */
  transition: scale .5s var(--sortie);
}
.ligne-carte:hover .vign--photo img, .base:hover .vign--photo img { scale: 1.09; }

.vign--ico {
  background: color-mix(in srgb, var(--sombre) 9%, transparent);
  color: var(--moyen-fonce);
}
.vign--ico svg {
  width: 30px; height: 30px;
  transition: scale .4s var(--sortie), color .25s var(--sortie);
}
.ligne-carte:hover .vign--ico svg { scale: 1.12; color: var(--sombre); }

/* Sur une ligne alternee, la tuile de l'icone doit rester lisible : le
   fond de la ligne est deja un voile clair, un second voile de meme
   valeur ferait disparaitre la tuile. */
.ligne-carte:nth-of-type(odd) .vign--ico {
  background: color-mix(in srgb, var(--sombre) 13%, transparent);
}

/* La vignette de la base d'une famille a composer est un cran plus
   grande : elle represente toute la famille, pas un article. */
.base .vign { width: 60px; height: 60px; }
/* Un article qui a une fiche se signale par un souligne au survol —
   discret, mais il faut bien que le lien se voie. */
.ligne__lien { text-decoration: underline; text-decoration-color: transparent; text-underline-offset: 3px; transition: text-decoration-color .22s var(--sortie); }
.ligne-carte:hover .ligne__lien { text-decoration-color: currentColor; }

/* =====================================================================
   12 quinquies. LE PARCOURS D'UNE COMMANDE
   ---------------------------------------------------------------------
   Six etapes en ligne, chacune avec sa bulle numerotee, reliees par un
   filet. Trois decisions a garder en tete :

   1. LA NUMEROTATION EST LEGITIME ICI, et c'est le seul endroit du site
      ou elle l'est. Six etapes qui se suivent dans le temps forment une
      vraie sequence : le numero porte une information. C'est pour la
      raison inverse que la FAQ a perdu ses numeros — huit questions ne
      sont pas une sequence, l'ordre n'y encode rien.

   2. LE FILET EST TIRE PAR CHAQUE ETAPE VERS LA SUIVANTE, pas par le
      conteneur. Un seul filet pose sur le conteneur devrait connaitre la
      position de la premiere et de la derniere bulle ; ici chaque etape
      tire son propre segment et la derniere n'en tire pas. Ca marche quel
      que soit le nombre d'etapes, sans rien recalculer.

   3. EN PILE SOUS 760px, ET C'EST VOULU. Six etapes cote a cote sur un
      telephone donneraient des colonnes de 55px. Le filet passe alors a
      la verticale, a gauche des bulles : le meme geste, tourne d'un quart
      de tour. « En horizontal » vaut pour l'ecran qui a la largeur.
   ===================================================================== */
.etapes__tete {
  display: flex; flex-wrap: wrap; align-items: baseline;
  justify-content: space-between; gap: 16px;
  margin-bottom: clamp(30px, 3.6vw, 48px);
}
.etapes__chapeau { font-size: clamp(14px, .5vw + 13px, 16px); color: var(--encre-tenue); }

.etapes {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
  gap: clamp(14px, 1.6vw, 26px);
  margin: 0; padding: 0;
  list-style: none;
}
.etape { position: relative; min-width: 0; }

/* La bulle. `--r-bulle` sert deux fois : au dessin et au calcul de la
   position du filet, qui doit tomber pile sur son axe. */
.etape__num {
  --r-bulle: 42px;
  position: relative; z-index: 1;
  display: grid; place-items: center;
  width: var(--r-bulle); height: var(--r-bulle);
  font-family: var(--titrage);
  font-size: 19px; font-weight: 800;
  border-radius: 999px;
  background: var(--sombre); color: var(--clair);
}

/* Le segment vers l'etape suivante. `top: 21px` = la moitie de la bulle :
   le filet traverse son axe et non son bord. */
.etape:not(:last-child)::after {
  content: "";
  position: absolute; z-index: 0;
  top: 21px; left: 42px;
  width: calc(100% + clamp(14px, 1.6vw, 26px) - 42px);
  height: 2px;
  background: var(--trait-sombre);
}

.etape__titre {
  margin-top: 16px;
  font-size: clamp(16px, 1.3vw, 19px);
}
.etape__txt {
  margin-top: 7px;
  font-size: 13.5px; line-height: 1.5;
  color: var(--encre-douce);
}

@media (max-width: 760px) {
  .etapes {
    grid-auto-flow: row;
    grid-auto-columns: auto;
    gap: clamp(20px, 4vw, 30px);
  }
  /* En pile, le filet descend le long du bord gauche, d'une bulle a
     l'autre. La bulle et le texte passent cote a cote pour que la colonne
     ne s'allonge pas trop. */
  .etape {
    display: grid;
    grid-template-columns: 42px minmax(0, 1fr);
    column-gap: 16px;
  }
  .etape__num { grid-row: 1 / 3; }
  .etape__titre { margin-top: 8px; }
  .etape__txt { grid-column: 2; }
  .etape:not(:last-child)::after {
    top: 42px; left: 20px;
    width: 2px;
    height: calc(100% - 42px + clamp(20px, 4vw, 30px));
  }
}

/* =====================================================================
   12 quater. NOUS SUIVRE — TikTok et Instagram
   ---------------------------------------------------------------------
   Deux colonnes de largeur INEGALE, et c'est voulu : le lecteur TikTok
   est un iframe d'une autre origine dont on ne maitrise ni la largeur
   minimale (288px annonces par TikTok) ni le style. On lui donne donc la
   colonne la plus etroite mais suffisante, et on garde la plus large pour
   la grille Instagram, qui est la seule des deux qu'on dessine.

   ON NE TENTE PAS DE RESTYLER L'EMBED TIKTOK. Un iframe cross-origin est
   hermetique au CSS de la page : toute regle qui le viserait serait du
   code mort. On l'encadre, c'est tout — meme rayon et meme fond que le
   reste du site, pour que le raccord se voie le moins possible.
   ===================================================================== */
.soc {
  display: grid;
  grid-template-columns: minmax(0, .85fr) minmax(0, 1fr);
  gap: clamp(28px, 3.5vw, 56px);
  padding-block: clamp(36px, 4.5vw, 64px);
  align-items: start;
}
.soc__col { min-width: 0; }

.soc__tete {
  display: flex; align-items: center; flex-wrap: wrap; gap: 14px;
  margin-bottom: clamp(18px, 2vw, 26px);
}
.soc__tete h2 { font-size: clamp(22px, 2.2vw, 30px); }
.soc__compte {
  margin-top: 2px;
  font-size: 14px; color: var(--encre-tenue);
}
/* Le bouton part a droite, quelle que soit la longueur du nom de compte. */
.soc__tete .bouton { margin-left: auto; }

.soc__pic {
  display: grid; place-items: center;
  width: 44px; height: 44px; flex: 0 0 auto;
  border-radius: var(--r-champ);
  background: var(--sombre); color: var(--clair);
}
.soc__pic svg { width: 22px; height: 22px; }

/* Le cadre du lecteur TikTok. `overflow: clip` pour que l'iframe respecte
   le rayon : un rayon pose sur l'iframe elle-meme n'est pas honore
   partout. Meme raison que pour le plan Google du contact. */
/* PAS DE `min-height` ICI. Une premiere version en imposait 420px pour
   reserver la place du lecteur : la carte de demande se retrouvait bien
   plus haute que le renvoi Instagram d'en face, et les deux colonnes ne se
   repondaient plus. Le lecteur de TikTok arrive avec sa propre hauteur ;
   avant lui, la carte n'a qu'a faire la taille de son texte. */
.soc__cadre {
  border-radius: var(--r-carte);
  overflow: clip;
  background: var(--pose-clair);
}
.soc__cadre iframe { max-width: 100%; }
.soc__note {
  margin-top: 12px;
  font-size: 13px; color: var(--encre-tenue);
}
.soc__note[hidden] { display: none; }

/* LA DEMANDE AVANT CHARGEMENT
   Elle occupe le cadre a la place du lecteur, et elle est dessinee par
   nous — c'est le dernier moment ou l'on maitrise encore l'apparence de
   cette colonne : des que l'embed de TikTok est la, tout est a lui.
   Meme forme que le renvoi Instagram a droite, pour que les deux colonnes
   se repondent tant qu'aucune n'est chargee. */
.soc__demande {
  display: grid; place-items: center; align-content: center;
  gap: 12px; height: 100%;
  padding: clamp(30px, 4vw, 54px) clamp(20px, 3vw, 36px);
  text-align: center;
}
.soc__demande__titre {
  font-family: var(--titrage);
  font-size: clamp(19px, 1.8vw, 24px); font-weight: 800;
  letter-spacing: -.02em; line-height: 1.15;
}
.soc__demande__mot {
  max-width: 38ch;
  font-size: 13.5px; line-height: 1.55; color: var(--encre-douce);
}
.soc__demande .bouton { margin-top: 6px; }

/* --- La grille Instagram, celle qu'on dessine ----------------------- */
.soc__grille {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: clamp(8px, 1vw, 14px);
}
.soc__post {
  position: relative;
  display: block;
  aspect-ratio: 1;
  border-radius: var(--r-champ);
  overflow: clip;
  background: var(--pose-clair);
}
.soc__post img {
  width: 100%; height: 100%;
  object-fit: cover;
  transition: scale .5s var(--sortie);
}
.soc__post:hover img { scale: 1.07; }

/* Le triangle de lecture des vidéos. Aplat plein et non un voile :
   le site n'a pas de flou, et un pictogramme pose sur une photo doit
   rester lisible quelle que soit la photo. */
.soc__joue {
  position: absolute; top: 10px; right: 10px;
  display: grid; place-items: center;
  width: 28px; height: 28px;
  border-radius: 999px;
  background: var(--sombre); color: var(--clair);
}
.soc__joue svg { width: 14px; height: 14px; }

/* La legende ne s'affiche qu'au survol, et par le bas : sur une grille de
   douze carres, douze legendes visibles en permanence feraient un mur de
   texte illisible. */
.soc__leg {
  position: absolute; inset: auto 0 0 0;
  padding: 26px 12px 10px;
  font-size: 12px; line-height: 1.4;
  color: var(--clair);
  background: linear-gradient(transparent, color-mix(in srgb, var(--sombre) 88%, transparent) 45%);
  translate: 0 100%;
  transition: translate .3s var(--sortie);
  /* Trois lignes au maximum : une legende Instagram peut faire un
     paragraphe entier, et elle recouvrirait la photo. */
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
  overflow: hidden;
}
.soc__post:hover .soc__leg { translate: 0 0; }

/* --- Le renvoi, quand le flux n'est pas branche --------------------- */
.soc__renvoi {
  display: grid; place-items: center;
  gap: 12px;
  padding: clamp(34px, 5vw, 64px) clamp(20px, 3vw, 40px);
  text-align: center;
  background: var(--pose-clair);
  border-radius: var(--r-carte);
}
.soc__renvoi__titre {
  font-family: var(--titrage);
  font-size: clamp(19px, 1.8vw, 24px); font-weight: 800;
  letter-spacing: -.02em; line-height: 1.15;
}
.soc__renvoi__mot {
  max-width: 36ch;
  font-size: 14px; line-height: 1.55; color: var(--encre-douce);
}
.soc__renvoi .bouton { margin-top: 6px; }

/* En dessous de 900px les deux colonnes s'empilent. TikTok d'abord :
   c'est celle qui montre reellement quelque chose. */
@media (max-width: 900px) {
  .soc { grid-template-columns: minmax(0, 1fr); }
  .soc__cadre { min-height: 0; }
}

/* =====================================================================
   12 ter. LA GALERIE
   ---------------------------------------------------------------------
   Rien que des photos. En colonnes coulantes plutot qu'en grille : les
   images n'ont pas toutes le meme rapport, et une grille les recadrerait
   ou laisserait des trous. Ici chacune garde ses proportions et la
   colonne se remplit.
   ===================================================================== */
.gal {
  columns: 3;
  column-gap: clamp(12px, 1.4vw, 20px);
  padding-block: clamp(28px, 4vw, 48px) var(--bande);
}
.gal__item {
  position: relative;
  break-inside: avoid;
  margin: 0 0 clamp(12px, 1.4vw, 20px);
  border-radius: var(--r-carte);
  overflow: clip;
  background: var(--moyen-clair);
}
.gal__voir { display: block; width: 100%; padding: 0; cursor: zoom-in; }
.gal__voir img {
  width: 100%; height: auto; display: block;
  transition: scale .5s var(--sortie);
}
.gal__item:hover .gal__voir img { scale: 1.04; }

/* Le bouton de partage : toujours present pour le doigt et le clavier,
   simplement plus discret tant qu'on ne survole pas. */
.gal__part {
  position: absolute; top: 10px; right: 10px;
  display: grid; place-items: center;
  width: 38px; height: 38px;
  border-radius: 999px;
  background: var(--sombre); color: var(--clair);
  opacity: 0;
  transition: opacity .25s var(--sortie), background-color .25s var(--sortie);
}
.gal__item:hover .gal__part,
.gal__part:focus-visible { opacity: 1; }
.gal__part:hover { background: var(--accent); color: var(--sombre); }
.gal__part svg { width: 17px; height: 17px; }
@media (hover: none) { .gal__part { opacity: 1; } }

/* Le retour du partage sur ordinateur : une etiquette a cote du bouton.
   Sans elle, le clic ne produit aucun signe et on croit a une panne. */
[data-dit]::after {
  content: attr(data-dit);
  position: absolute; top: 50%; right: calc(100% + 10px);
  transform: translateY(-50%);
  padding: 7px 13px;
  font-size: 12.5px; font-weight: 700; white-space: nowrap;
  background: var(--accent); color: var(--sombre);
  border-radius: 999px;
}

/* --- La visionneuse ---------------------------------------------------- */
.vue {
  position: fixed; inset: 0; z-index: 120;
  display: grid; place-items: center;
  padding: clamp(16px, 4vw, 56px);
  visibility: hidden;
}
.vue[data-ouvert="1"] { visibility: visible; }
.vue__voile {
  position: absolute; inset: 0;
  background: color-mix(in srgb, var(--sombre) 92%, transparent);
  opacity: 0;
  transition: opacity .3s var(--sortie);
}
.vue[data-ouvert="1"] .vue__voile { opacity: 1; }

.vue__cadre {
  position: relative; z-index: 1;
  display: grid; gap: 14px; justify-items: center;
  max-width: min(100%, 1100px);
  transform: scale(.94);
  opacity: 0;
  transition: transform .35s var(--sortie), opacity .35s var(--sortie);
}
.vue[data-ouvert="1"] .vue__cadre { transform: none; opacity: 1; }
.vue__cadre img {
  max-width: 100%; max-height: 74svh;
  width: auto; height: auto;
  border-radius: var(--r-carte);
}
.vue__leg {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap; justify-content: center;
  font-size: 14px; color: var(--clair-doux);
}
.vue__nb { font-weight: 700; color: var(--clair-tenu); }

.vue__outils {
  position: absolute; z-index: 2; top: clamp(12px, 2vw, 24px); right: clamp(12px, 2vw, 24px);
  display: flex; gap: 8px;
}
.vue__outils button {
  position: relative;
  display: grid; place-items: center;
  width: 44px; height: 44px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--clair) 14%, transparent);
  color: var(--clair);
  transition: background-color .22s var(--sortie), color .22s var(--sortie);
}
.vue__outils button:hover { background: var(--accent); color: var(--sombre); }
.vue__outils svg { width: 19px; height: 19px; }

/* =====================================================================
   13. MOUVEMENT
   ---------------------------------------------------------------------
   UNE REGLE : chaque animation doit pouvoir se justifier en une phrase.
   Hierarchie (ou regarder), narration (dans quel ordre), retour (ce que
   je viens de faire a marche), profondeur (ce qui est loin bouge moins).
   Tout ce qui ne rentrait dans aucune de ces cases a ete retire.

   TOUT est sous `prefers-reduced-motion: no-preference`. Un visiteur qui
   a demande moins de mouvement obtient une page entierement statique,
   pas une page « un peu moins animee ».

   Le defilement pilote les animations en CSS quand le navigateur le sait
   (`animation-timeline`), donc hors du fil principal : ca ne rame pas
   meme sur un telephone modeste. Sinon, repli par IntersectionObserver.
   ===================================================================== */

/* --- 13.a Transitions entre pages -------------------------------------
   Le site fait quatre pages qui partagent le meme en-tete et le meme
   logo. Sans transition, chaque clic fait un flash blanc et l'en-tete
   « saute ». Avec, le navigateur fait le fondu lui-meme et garde
   l'en-tete en place : la navigation se lit comme un seul document.
   Ignore par les navigateurs qui ne connaissent pas la regle.
   --------------------------------------------------------------------- */
@view-transition { navigation: auto; }

@media (prefers-reduced-motion: no-preference) {
  ::view-transition-old(root),
  ::view-transition-new(root) { animation-duration: .35s; }
  /* L'en-tete et le logo sont les memes d'une page a l'autre : nommes,
     ils ne participent pas au fondu et restent simplement en place. */
  .tete { view-transition-name: entete; }
  .pied { view-transition-name: pied; }
}
@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) { animation: none !important; }
}

@media (prefers-reduced-motion: no-preference) {

  /* --- 13.a bis  LES TITRES SE COMPOSENT MOT A MOT --------------------
     Le geste principal du site. `js/site.js` enveloppe chaque mot dans
     un `.mot`, qui sert de MASQUE : le mot monte depuis dessous et
     apparait derriere une arete invisible.

     `overflow: hidden` couperait les jambages du p, du g et du j : on
     rend la place avec un `padding-bottom` compense par une marge
     negative, ce qui ne change rien a la mise en page.

     Le decalage entre mots ne passe PAS par `animation-delay` quand la
     timeline est le defilement : dans ce cas le delai s'exprime dans
     l'unite de la timeline, pas en secondes. On decale donc la PLAGE.
     ------------------------------------------------------------------ */
  .mot {
    display: inline-block;
    overflow: hidden;
    vertical-align: bottom;
    padding-bottom: .14em;
    margin-bottom: -.14em;
  }
  .mot > span { display: inline-block; }

  /* Le titre du hero est visible au chargement : son decalage est donc
     un vrai delai en secondes. */
  /* =================================================================
     LA CASCADE D'ENTREE, JUSTE APRES LE BALAYAGE DE L'OUVERTURE
     -----------------------------------------------------------------
     Tout part de `--intro`, qui vaut la duree de l'ouverture (3,1s) sur
     les pages qui la portent et ZERO partout ailleurs. Les retards
     s'ecrivent donc `calc(var(--intro) + …)` et la meme feuille sert aux
     deux cas sans condition.

     L'ordre dit quelque chose : d'abord le cadre (barre, logotype,
     navigation), ensuite la parole (titre mot a mot), enfin les moyens
     d'agir (sous-titre, boutons). On ne montre pas un bouton avant
     d'avoir dit de quoi il s'agit.

     RESSERREE le 30/07 : le client trouvait l'attente trop longue. Les
     retards ET les durees ont ete reduits — raccourcir les seuls retards
     aurait tasse les gestes les uns sur les autres sans rien gagner sur
     la fin. La cascade se termine maintenant 1,25s apres le rideau au
     lieu de 2,05s, et le titre commence a se composer 0,32s plus tot.

       +0,00  la barre descend
       +0,08  le logotype se decouvre de gauche a droite
       +0,16  la boite de navigation s'ouvre, ses liens suivent un a un
       +0,26  les outils de droite
       +0,12  la cannette se pose (elle est seule sur la moitie droite,
              elle ne prend le tour de personne)
       +0,28  le titre, mot a mot
       +0,50  le chapeau
       +0,60  les boutons
     ================================================================= */

  /* La barre vient de sous le bord haut. `translate` et non `transform` :
     l'en-tete change d'etat au defilement et la feuille lui applique
     deja des transformations. */
  .tete { animation: descendBarre .5s var(--sortie) var(--intro) both; }

  /* Le logotype se decouvre dans le sens de la lecture. C'est le meme
     geste que l'ouverture du site, ou la decoupe s'ecarte de la feve —
     ici il est simplement lateral, parce qu'un logotype se lit. */
  .marque svg { animation: decouvreLogo .5s var(--sortie) calc(var(--intro) + .06s) both; }

  .nav { animation: ouvrePilule .42s var(--pose) calc(var(--intro) + .12s) both; }
  /* Les liens arrivent l'un apres l'autre A L'INTERIEUR de la pilule
     deja ouverte, d'ou le retard de base a +0,22 : la boite doit avoir
     fini de s'etendre, sinon les libelles debordent pendant l'animation. */
  .nav a { animation: paraitre .34s var(--sortie) both; }
  .nav a:nth-child(1) { animation-delay: calc(var(--intro) + .18s); }
  .nav a:nth-child(2) { animation-delay: calc(var(--intro) + .21s); }
  .nav a:nth-child(3) { animation-delay: calc(var(--intro) + .24s); }
  .nav a:nth-child(4) { animation-delay: calc(var(--intro) + .27s); }
  .nav a:nth-child(5) { animation-delay: calc(var(--intro) + .30s); }

  .outils > * { animation: paraitre .38s var(--sortie) calc(var(--intro) + .20s) both; }

  /* Le pas entre deux mots passe de 0,07 a 0,045s : sur les six mots du
     titre, ca economise 0,15s sans que le geste cesse de se lire. */
  .hero__titre .mot > span {
    animation: motMonte .6s var(--sortie) both;
    animation-delay: calc(var(--intro) + .22s + var(--i) * .04s);
  }

  /* LA SECONDE LIGNE DU TITRE A BESOIN DE SA PROPRE ENTREE.
     Elle porte `data-tourne`, donc `decouperMots` la saute : elle n'a ni
     `.mot` ni masque, et la regle ci-dessus ne l'atteint pas. Sans cette
     declaration elle n'avait AUCUNE animation, donc aucun retard — elle
     s'affichait des le lever du rideau, avant la premiere ligne. Mesure a
     l'appui : opacite 1 des 3000ms quand tout le reste valait 0.

     Elle arrive a +0,44s, juste apres le dernier mot de la premiere ligne
     (+0,46s pour son depart) : le titre se compose donc de haut en bas.
     Et elle arrive avec `accrocheEntre`, le geste meme qu'elle utilisera a
     chaque changement — la ligne entre comme elle se renouvellera. */
  .hero__accroche {
    animation: accrocheEntre .45s var(--sortie) calc(var(--intro) + .34s) both;
  }

  /* Les titres de section arrivent au defilement. */
  @supports (animation-timeline: view()) {
    .titre-bande .mot > span,
    .fiche__nom .mot > span,
    .carte-hero h1 .mot > span {
      animation: motMonte both linear;
      animation-timeline: view();
      animation-range: entry calc(2% + var(--i) * 2%) entry calc(42% + var(--i) * 2%);
    }
  }
  @supports not (animation-timeline: view()) {
    .titre-bande .mot > span, .fiche__nom .mot > span, .carte-hero h1 .mot > span {
      animation: motMonte .8s var(--sortie) both;
      animation-delay: calc(var(--i) * .05s);
    }
  }

  /* --- 13.b Arrivee du hero -------------------------------------------
     Narration : le titre, puis la phrase, puis les boutons, puis
     l'objet. C'est l'ordre dans lequel on veut etre lu.
     ------------------------------------------------------------------ */
  /* SCOPE : `.hero__col`, PAS `.hero__in > div`. Le hero contient deux
     div freres — la colonne de texte et la scene — et le selecteur
     large attrapait aussi le mot geant et le visuel detoure, dont il
     ecrasait les animations propres (`recule` et `flotte`). La scene a
     sa propre entree, `poseObjet`. */
  /* Le titre est deja anime mot a mot : on ne l'anime pas une seconde
     fois en bloc, sinon les deux se combattent. */
  .hero__col > *:not(.hero__titre) { animation: arrive .5s var(--sortie) both; }
  /* Le chapeau et les boutons restent APRES le titre : ils servent une
     phrase, ils ne peuvent pas la preceder. L'ordre est conserve, seule
     l'attente est reduite. */
  /* Repousses de 0,06 / 0,04s pour laisser passer la seconde ligne du
     titre, qui entre a +0,44 : le chapeau ne doit pas la doubler. */
  .hero__col > *:nth-child(2) { animation-delay: calc(var(--intro) + .40s); }
  .hero__col > *:nth-child(3) { animation-delay: calc(var(--intro) + .48s); }
  .hero__scene { animation: poseObjet .7s var(--sortie) calc(var(--intro) + .10s) both; }

  /* Flottement continu de l'objet detoure. Justification : un visuel
     detoure pose sur un aplat a l'air colle. Neuf pixels de course et
     un degre d'oscillation suffisent a le faire exister. */
  .hero__carrousel { animation: flotte 7s ease-in-out calc(var(--intro) + 1.2s) infinite; }

  /* Parallaxe souris. Le JS n'ecrit que deux variables, et c'est la
     propriete `translate` — independante de `transform` — qui les
     applique : l'animation `poseObjet` garde ainsi `transform` pour
     elle, sans que les deux se marchent dessus.
     Bureau uniquement : sur telephone `.hero__scene` est l'aplat vert
     du bloc visuel, le deplacer decollerait la bande du bord. */
  @media (min-width: 901px) and (hover: hover) and (pointer: fine) {
    .hero__scene {
      translate: calc(var(--sx, 0) * 14px) calc(var(--sy, 0) * 10px);
      transition: translate .5s var(--sortie);
    }
  }

  /* Profondeur : le mot geant est « derriere », il defile donc moins
     vite que le reste. C'est le seul indice de profondeur de la page.
     BUREAU UNIQUEMENT : sous 900px le mot repasse dans le flux, sans
     `translateY(-50%)`, et l'animer le sortirait de son bloc. */
  /* LA BOUCLE DU MOT GEANT
     Il defile en continu le long du panneau, sans fin. C'est le
     mouvement permanent du hero : meme immobile, la page vit.
     La piste porte `transform`, le conteneur porte `translate` pour la
     parallaxe : deux proprietes distinctes, donc aucune des deux
     n'ecrase l'autre. */
  .hero__mot__piste { animation: defileVertical 34s linear infinite; }

  @media (min-width: 901px) {
    @supports (animation-timeline: view()) {
      /* `view()` et non `scroll()` : la plage se cale sur la traversee
         de l'element lui-meme, donc sur n'importe quelle hauteur de
         hero. Une premiere version ecrivait `scroll()` avec une plage
         en pixels — elle figeait la hauteur a 760px, et la declaration
         etait de toute facon invalide (« 0 » sans unite), donc morte. */
      .hero__mot {
        animation: recule linear both;
        animation-timeline: view();
        animation-range: cover;
      }
    }
  }

  /* --- 13.c Apparitions au defilement -------------------------------- */
  @supports (animation-timeline: view()) {
    .rv {
      animation: monte both linear;
      animation-timeline: view();
      animation-range: entry 8% entry 62%;
    }
    /* Dans une grille, les elements n'arrivent pas tous ensemble : on
       decale la PLAGE, pas un delai. Un delai se desynchronise du
       defilement ; decaler la plage garde le lien au geste. */
    .grille-produits > *:nth-child(2) { animation-range: entry 12% entry 66%; }
    .grille-produits > *:nth-child(3) { animation-range: entry 16% entry 70%; }
    .grille-produits > *:nth-child(4) { animation-range: entry 20% entry 74%; }
    .signatures > *:nth-child(2) { animation-range: entry 14% entry 68%; }
  }
  @supports not (animation-timeline: view()) {
    .rv { opacity: 0; transform: translateY(22px); transition: opacity .7s var(--sortie), transform .7s var(--sortie); }
    .rv.vu { opacity: 1; transform: none; }
    .grille-produits > .rv:nth-child(2), .signatures > .rv:nth-child(2) { transition-delay: .08s; }
    .grille-produits > .rv:nth-child(3) { transition-delay: .16s; }
    .grille-produits > .rv:nth-child(4) { transition-delay: .24s; }
  }

  /* Les titres de bande se devoilent par le bas, comme une carte qu'on
     sort d'une enveloppe. Hierarchie : le titre entre avant sa section. */
  @supports (animation-timeline: view()) {
    .titre-bande { animation: devoile both linear; animation-timeline: view(); animation-range: entry 5% entry 45%; }
  }

  /* --- 13.d Les visuels ----------------------------------------------
     Une photo qui se rapproche legerement pendant qu'elle traverse
     l'ecran donne de la vie sans bouger la mise en page (`scale` seul,
     jamais `width`).
     ------------------------------------------------------------------ */
  @supports (animation-timeline: view()) {
    /* Les grandes photos s'ouvrent comme un rideau, puis continuent de
       se rapprocher tout le long de leur traversee. Deux animations sur
       la meme timeline, avec des plages differentes. */
    .composer__ph {
      animation: rideau both linear;
      animation-timeline: view();
      animation-range: entry 0% entry 65%;
    }
    /* La vignette de signature est CENTREE par `transform:
       translateX(-50%)`. Toute animation de `transform` la decalerait
       de la moitie de sa largeur. On l'anime donc en `clip-path` et en
       `scale`, deux proprietes independantes de `transform`. */
    .signature__ph {
      animation: rideau both linear;
      animation-timeline: view();
      animation-range: entry 5% entry 55%;
    }
  }
  .carte__ph { transition: scale .45s var(--sortie); }
  .carte:hover .carte__ph { scale: 1.12; }
  .signature:hover .signature__ph { transform: translateX(-50%) rotate(-4deg); scale: 1.08; }

  /* Les quatre visuels detoures de la bande « fait maison » respirent,
     chacun a son rythme : cales ensemble, ils feraient decor imprime. */
  .savoir__rang img { animation: flotte 8s ease-in-out infinite; }
  .savoir__rang img:nth-child(2) { animation-duration: 9.5s; animation-delay: -2s; }
  .savoir__rang img:nth-child(3) { animation-duration: 7.5s; animation-delay: -4s; }
  .savoir__rang img:nth-child(4) { animation-duration: 10s; animation-delay: -1s; }

  /* ET ils DERIVENT lateralement au defilement, chacun a sa vitesse :
     le groupe s'ecarte puis se resserre pendant qu'on passe devant.
     C'est l'effet de profondeur de cette bande.
     Deux animations sur le meme element, avec DEUX timelines : la
     premiere sur l'horloge (flottement), la seconde sur le defilement.
     `animation-timeline` accepte une liste, dans l'ordre des animations.
     Le decalage en dents de scie est integre aux images-cles, sinon la
     derive l'ecraserait — les deux ecrivent `translate`. */
  @supports (animation-timeline: view()) {
    .savoir__rang img:nth-child(1) { animation-name: flotte, derive1; animation-timeline: auto, view(); animation-range: normal, cover; animation-fill-mode: none, both; animation-iteration-count: infinite, 1; animation-timing-function: ease-in-out, linear; }
    .savoir__rang img:nth-child(2) { animation-name: flotte, derive2; animation-timeline: auto, view(); animation-range: normal, cover; animation-fill-mode: none, both; animation-iteration-count: infinite, 1; animation-timing-function: ease-in-out, linear; }
    .savoir__rang img:nth-child(3) { animation-name: flotte, derive3; animation-timeline: auto, view(); animation-range: normal, cover; animation-fill-mode: none, both; animation-iteration-count: infinite, 1; animation-timing-function: ease-in-out, linear; }
    .savoir__rang img:nth-child(4) { animation-name: flotte, derive4; animation-timeline: auto, view(); animation-range: normal, cover; animation-fill-mode: none, both; animation-iteration-count: infinite, 1; animation-timing-function: ease-in-out, linear; }
  }

  /* LA CANETTE S'INCLINE VERS LE CURSEUR
     `rotate` est une propriete a part entiere : le flottement garde
     `transform`, la parallaxe garde `translate`, et l'inclinaison prend
     `rotate`. Trois mouvements independants sur le meme objet, sans
     qu'aucun n'ecrase les autres. */
  @media (min-width: 901px) and (hover: hover) and (pointer: fine) {
    .hero__scene { perspective: 1100px; }
    .hero__carrousel {
      rotate: y calc(var(--sx, 0) * 9deg);
      transition: rotate .5s var(--sortie);
    }
  }

  /* Les liens du pied se soulignent en balayage. */
  .pied li a {
    background-image: linear-gradient(currentColor, currentColor);
    background-size: 0% 1px;
    background-position: 0 100%;
    background-repeat: no-repeat;
    transition: color .2s var(--sortie), background-size .35s var(--sortie);
  }
  .pied li a:hover { background-size: 100% 1px; }

  /* --- 13.e Retours d'interaction ------------------------------------ */
  /* La puce du panier tressaute quand un article y entre : c'est le seul
     accuse de reception d'un ajout depuis une carte. */
  .pastille--pop { animation: pop .5s var(--sortie); }

  .bouton, .rond, .puce { will-change: transform; }

  /* Le tiroir mobile et le panier : leurs liens arrivent en cascade,
     ce qui indique le sens d'ouverture. */
  .tiroir[data-ouvert="1"] .tiroir__panneau > a { animation: entreDeDroite .5s var(--sortie) both; }
  .tiroir[data-ouvert="1"] .tiroir__panneau > a:nth-of-type(1) { animation-delay: .06s; }
  .tiroir[data-ouvert="1"] .tiroir__panneau > a:nth-of-type(2) { animation-delay: .12s; }
  .tiroir[data-ouvert="1"] .tiroir__panneau > a:nth-of-type(3) { animation-delay: .18s; }
  .tiroir[data-ouvert="1"] .tiroir__panneau > a:nth-of-type(4) { animation-delay: .24s; }

  /* --- 13.f Page carte ------------------------------------------------
     Les categories arrivent en fondu SANS transform : un descendant
     transforme perturbe la fragmentation en colonnes, et la page carte
     est justement en `columns`. L'opacite seule ne cree pas de bloc
     conteneur, elle est donc sans danger ici.
     ------------------------------------------------------------------ */
  @supports (animation-timeline: view()) {
    .cat { animation: paraitre both linear; animation-timeline: view(); animation-range: entry 0% entry 40%; }
  }

  /* `interpolate-size` reste declare : la fiche produit et le tiroir du
     panier s'appuient dessus pour animer vers `height: auto`. La FAQ ne
     l'utilise plus — elle n'ouvre plus rien, elle echange le contenu
     d'un panneau, et cet echange est anime en WAAPI dans accueil.js. */
  @supports (interpolate-size: allow-keywords) {
    :root { interpolate-size: allow-keywords; }
  }

  /* --- 13.f ter  LE MUR D'AVIS QUI DERIVE -----------------------------
     Les deux colonnes glissent en sens inverse pendant que la section
     traverse l'ecran. C'est le geste de cette section : il dit qu'il y a
     plus d'avis que le cadre n'en montre, sans bouton ni fleche.
     La course est en pourcentage de la piste, donc elle s'adapte a la
     hauteur reelle des cartes, quelle que soit la longueur des citations.
     ------------------------------------------------------------------ */
  /* Defilement AUTOMATIQUE et continu, pas lie au scroll : les avis
     avancent tout seuls, meme si le visiteur ne bouge pas.
     `infinite` et non `alternate` : chaque piste contient deux fois la
     liste, donc une course de -50 % retombe pile sur un contenu
     identique et la boucle ne se voit pas. Avec `alternate`, on verrait
     les avis repartir en arriere.
     Il s'arrete au survol et au focus clavier : on ne peut pas lire un
     texte qui bouge, et on ne peut pas viser un lien qui fuit. */
  .mur__col--haut .mur__piste { animation: deriveHaut 44s linear infinite; }
  .mur__col--bas .mur__piste { animation: deriveBas 44s linear infinite; }
  /* PLUS DE PAUSE AU SURVOL — retiree a la demande du client.
     Elle existait pour deux raisons, dont une seule tenait vraiment ici :
     on ne lit pas un texte qui bouge, et on ne vise pas un lien qui fuit.
     La seconde ne s'applique pas au mur d'avis : ses cartes ne contiennent
     aucun lien ni bouton, il n'y a rien a viser.
     Le moyen d'arreter le mouvement subsiste, et c'est le bon :
     `prefers-reduced-motion` coupe toute cette section d'animations. Le
     bandeau des categories, lui, GARDE sa pause au survol — il est fait de
     onze liens cliquables, et c'est un cas different. */

  /* Les questions arrivent l'une apres l'autre. Le decalage passe par la
     PLAGE et non par un delai : avec une timeline de defilement, un
     delai s'exprime dans l'unite de la timeline, pas en secondes.
     `nth-child` et non `nth-of-type` : les questions et le panneau de
     reponse sont tous des enfants directs de `.faqx`, mais de balises
     differentes — `nth-of-type` compterait les `button` a part et
     l'ordre ne correspondrait plus a l'ordre visuel. */
  @supports (animation-timeline: view()) {
    .faqx__q {
      animation: entreDeGauche both linear;
      animation-timeline: view();
    }
    .faqx__q:nth-child(1) { animation-range: entry 2% entry 34%; }
    .faqx__q:nth-child(2) { animation-range: entry 6% entry 38%; }
    .faqx__q:nth-child(3) { animation-range: entry 10% entry 42%; }
    .faqx__q:nth-child(4) { animation-range: entry 14% entry 46%; }
    .faqx__q:nth-child(5) { animation-range: entry 18% entry 50%; }
    .faqx__q:nth-child(6) { animation-range: entry 22% entry 54%; }
    .faqx__q:nth-child(7) { animation-range: entry 26% entry 58%; }
    .faqx__q:nth-child(8) { animation-range: entry 30% entry 62%; }
  }

  /* --- Le parcours d'une commande, etape par etape --------------------
     Les six etapes arrivent dans l'ordre ou on les lit. Le decalage passe
     par la PLAGE et non par un delai : sur une timeline de defilement, un
     delai s'exprime dans l'unite de la timeline, pas en secondes.
     C'est la seule section ou l'echelonnement porte du sens plutot que du
     confort — il rejoue la sequence qu'elle decrit. */
  @supports (animation-timeline: view()) {
    .etape {
      animation: paraitre both linear;
      animation-timeline: view();
    }
    .etape:nth-child(1) { animation-range: entry 5% entry 40%; }
    .etape:nth-child(2) { animation-range: entry 10% entry 45%; }
    .etape:nth-child(3) { animation-range: entry 15% entry 50%; }
    .etape:nth-child(4) { animation-range: entry 20% entry 55%; }
    .etape:nth-child(5) { animation-range: entry 25% entry 60%; }
    .etape:nth-child(6) { animation-range: entry 30% entry 65%; }
  }

  /* L'etat « ouvert » respire. Justification : c'est la seule information
     de la page qui change dans le temps, et le point qui pulse le dit
     sans un mot. Ferme, il ne bouge pas — il n'y a rien a signaler. */
  .etat[data-ouvert="1"] .etat__pastille { animation: pulse 2.4s ease-in-out infinite; }

  /* --- 13.g Bandeau des categories ------------------------------------
     Il defile en continu pour montrer d'un coup l'etendue de la carte.
     Il s'arrete au survol et au focus clavier : sinon on ne peut pas
     viser un lien qui bouge.
     ------------------------------------------------------------------ */
  .bandeau__piste { animation: defile 42s linear infinite; }
  .bandeau:hover .bandeau__piste,
  .bandeau:focus-within .bandeau__piste { animation-play-state: paused; }

  /* --- 13.h LES MOUVEMENTS PERPETUELS ---------------------------------
     Tout ce qui precede est declenche par le defilement : le geste se
     rejoue a chaque passage, mais la page est IMMOBILE si le visiteur ne
     bouge pas. Ce bloc-la tourne en permanence, meme a l'arret.

     La regle qui les gouverne : un mouvement perpetuel ne se pose que
     sur du DECOR. Jamais sur un texte a lire, jamais sur une cible a
     cliquer. Un paragraphe qui respire est illisible, un bouton qui
     derive est impossible a viser.
     ------------------------------------------------------------------ */


  /* La fleche de la question active avance et revient : elle designe le
     panneau de reponse, a droite. Seule la fleche bouge, pas la
     question — le libelle reste lisible et cliquable. */
  .faqx__q[aria-expanded="true"] .faqx__fleche { animation: pointe 2.6s ease-in-out infinite; }

  /* Le plan est vivant, mais l'iframe de Google ne se prete a rien : on
     anime le bouton d'itineraire pose par-dessus, discretement. */
  .plan__lien { animation: flotte 6s ease-in-out infinite; }
  .plan:hover .plan__lien { animation-play-state: paused; }
}


/* Les amplitudes ont ete DOUBLEES apres coup : la premiere version
   bougeait de 20px en 0,7s et de 9px pour le flottement — mesurable,
   mais invisible a l'oeil nu. Un mouvement qui ne se voit pas ne sert a
   rien : autant ne pas le payer. */
/* Pas de rotation ici. Une version precedente ajoutait `rotate(4deg)` :
   pendant l'entree, les mots inclines depassaient a droite et le
   document gagnait une barre de defilement horizontale pendant les
   1,6 s de l'animation. Une translation verticale de 105 % derriere le
   masque se voit tout aussi bien, sans elargir quoi que ce soit. */
@keyframes motMonte { from { opacity: 0; transform: translateY(105%); } to { opacity: 1; transform: none; } }
@keyframes monte { from { opacity: 0; transform: translateY(52px) scale(.96); } to { opacity: 1; transform: none; } }
@keyframes paraitre { from { opacity: 0; transform: translateY(24px); } to { opacity: 1; transform: none; } }
@keyframes arrive { from { opacity: 0; transform: translateY(38px); } to { opacity: 1; transform: none; } }
@keyframes poseObjet { from { opacity: 0; transform: translateY(70px) scale(.82) rotate(-6deg); } to { opacity: 1; transform: none; } }
@keyframes entreDeDroite { from { opacity: 0; transform: translateX(40px); } to { opacity: 1; transform: none; } }
@keyframes devoile { from { opacity: 0; transform: translateY(34px); } to { opacity: 1; transform: none; } }
@keyframes approche { from { scale: 1.16; } to { scale: 1; } }
/* La parallaxe passe par `translate` : `transform` sert desormais a la
   boucle du mot, et les deux doivent cohabiter. */
@keyframes recule { from { translate: 0 0; } to { translate: 0 -150px; } }
/* `--unite` est la longueur EXACTE d'un motif, mesuree par accueil.js.
   Le repli a -50 % sert si le script ne tourne pas : la boucle derive
   alors de quelques pixels par tour, ce qui vaut mieux que pas de
   boucle du tout. */
@keyframes defileVertical { from { transform: translateY(0); } to { transform: translateY(calc(-1 * var(--unite, 50%))); } }
@keyframes defileHorizontal { from { transform: translateX(0); } to { transform: translateX(calc(-1 * var(--unite, 50%))); } }
@keyframes flotte {
  0%, 100% { transform: translateY(0) rotate(0deg); }
  50% { transform: translateY(-22px) rotate(-3deg); }
}
/* Devoilement d'une photo : un rideau qui s'ouvre du bas vers le haut,
   pendant que l'image se rapproche. Beaucoup plus lisible qu'un simple
   fondu, et ca ne deplace rien autour. */
@keyframes rideau {
  from { clip-path: inset(0 0 100% 0); scale: 1.14; }
  to { clip-path: inset(0 0 0 0); scale: 1; }
}
/* La derive des quatre visuels detoures. Le second nombre de chaque
   `translate` est le decalage en dents de scie de la mise en page : il
   doit etre repris ici, sinon la derive l'effacerait. */
@keyframes derive1 { from { translate: 34px 10px; } to { translate: -30px 10px; } }
@keyframes derive2 { from { translate: -22px 0; } to { translate: 26px 0; } }
@keyframes derive3 { from { translate: 30px 16px; } to { translate: -34px 16px; } }
@keyframes derive4 { from { translate: -28px 4px; } to { translate: 22px 4px; } }

@keyframes entreDeGauche { from { opacity: 0; transform: translateX(-22px); } to { opacity: 1; transform: none; } }
/* La derive du mur d'avis. Les deux pistes contiennent deux fois la
   liste, donc une course de -50 % reste toujours dans du contenu. */
@keyframes deriveHaut { from { transform: translateY(0); } to { transform: translateY(-50%); } }
@keyframes deriveBas { from { transform: translateY(-50%); } to { transform: translateY(0); } }
@keyframes pulse {
  0%, 100% { transform: scale(1); opacity: 1; }
  50% { transform: scale(1.5); opacity: .5; }
}
@keyframes pop {
  0% { transform: scale(1); }
  40% { transform: scale(1.45); }
  100% { transform: scale(1); }
}
@keyframes defile { from { transform: translateX(0); } to { transform: translateX(-50%); } }

/* --- Les mouvements perpetuels ---------------------------------------- */

}

/* `translate` et non `transform` : la fleche porte deja un `translate`
   de mise en place dans son etat actif, et les deux proprietes ne se
   marchent pas dessus — c'est tout l'interet des proprietes
   independantes. Ici on redeclare donc la position active a chaque
   etape, decalage compris. */
@keyframes pointe {
  0%, 100% { translate: 5px 0; }
  50% { translate: 11px 0; }
}

/* La decoupe s'ecarte du centre vers les deux bords, comme l'ouverture
   du site s'ecarte de la feve de cafe. */
@keyframes ouvreDuCentre {
  from { clip-path: inset(0 50% 0 50%); }
  to { clip-path: inset(0 0 0 0); }
}

/* --- Les etapes de la cascade d'entree -------------------------------- */

/* La barre descend de sa propre hauteur. `-100%` et non une valeur en
   pixels : la barre mesure 74px en grand ecran et 66px en petit. */
@keyframes descendBarre {
  from { translate: 0 -100%; opacity: 0; }
  to { translate: 0 0; opacity: 1; }
}

/* Le logotype se decouvre de gauche a droite, dans le sens de la
   lecture. Pas d'echelle : le dessin ne doit pas se deformer. */
@keyframes decouvreLogo {
  from { clip-path: inset(0 100% 0 0); opacity: 0; }
  20% { opacity: 1; }
  to { clip-path: inset(0 0 0 0); opacity: 1; }
}

/* La pilule de navigation s'ouvre depuis son centre : elle part a 30 %
   de sa largeur et s'etend. `scaleX` seul deformerait les libelles a
   l'interieur — mais ils ne sont pas encore la, ils arrivent apres. */
@keyframes ouvrePilule {
  from { transform: scaleX(.3); opacity: 0; }
  to { transform: scaleX(1); opacity: 1; }
}

/* La seconde ligne du titre s'echange : l'ancienne monte en s'effacant,
   la nouvelle vient d'en dessous. Sans masque, donc sans risque de
   couper les jambages — un `overflow: clip` sur la ligne trancherait le
   « g » de « charge ». */
@keyframes accrocheSort {
  from { opacity: 1; translate: 0 0; }
  to { opacity: 0; translate: 0 -.32em; }
}
@keyframes accrocheEntre {
  from { opacity: 0; translate: 0 .32em; }
  to { opacity: 1; translate: 0 0; }
}

/* =====================================================================
   14. ADAPTATION
   ---------------------------------------------------------------------
   Chaque grille multi-colonnes declare ici son repli. Rien n'est laisse
   au hasard du navigateur.
   ===================================================================== */
@media (max-width: 1080px) {
  .pied__in { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .pied__marque { grid-column: 1 / -1; }
}
@media (max-width: 620px) {
  .pied__in { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 900px) {
  html { scroll-padding-top: 82px; }
  .nav { display: none; }
  .outils .burger { display: grid; }
  .tete__in { grid-template-columns: auto 1fr auto; height: 66px; }

  /* Sur telephone le hero est d'un seul aplat clair, le vert foret se
     limite au bloc du visuel. */
  .hero { background: var(--moyen); }
  .hero__in {
    grid-template-columns: minmax(0, 1fr);
    min-height: 0;
    padding-block: 104px 0;
    text-align: center;
  }
  .hero__texte { margin-inline: auto; }
  .hero__actions { justify-content: center; }
  .hero__titre { max-width: none; }
  .hero__titre em { color: var(--sombre); }
  /* Le mot geant ne tient pas a la verticale sur un telephone : il
     repasse a l'horizontale et devient le fond du visuel. */
  .hero__scene {
    margin-top: 34px;
    padding: 26px 0 0;
    background: var(--sombre);
    border-radius: var(--r-carte) var(--r-carte) 0 0;
  }
  /* Sur telephone, la boucle repasse a l'horizontale : c'est le meme
     ruban, tourne d'un quart de tour. La piste ne change pas, seul
     l'axe du defilement change. */
  .hero__mot {
    position: static;
    writing-mode: horizontal-tb;
    font-size: clamp(44px, 15vw, 88px);
    transform: none;
    translate: none;
    animation: none;
    /* Le fondu passe sur l'axe horizontal, comme le ruban. */
    mask-image: linear-gradient(to right, transparent, #000 8%, #000 92%, transparent);
  }
  .hero__mot__piste { animation-name: defileHorizontal; }
  .hero__mot__piste span::after { margin-inline: .18em; margin-block: 0; }
  .hero__carrousel { width: min(76%, 340px); margin-top: -12px; }

  .savoir, .composer, .mur { grid-template-columns: minmax(0, 1fr); }
  /* Sur telephone, une seule colonne d'avis : deux colonnes de 170px
     donneraient des citations coupees mot par mot. Le cadre est aussi
     plus court, la section n'ecrase pas le reste de la page. */
  .mur__colonnes { grid-template-columns: minmax(0, 1fr); height: clamp(340px, 78vw, 430px); }
  .mur__col--bas { display: none; }
  .savoir__texte { text-align: left; }
  .savoir__texte p { margin-left: 0; }

  .contact { grid-template-columns: minmax(0, 1fr); }
  .contact__gauche, .contact__droite { padding-inline: var(--marge); }
  /* Les deux pistes de la colonne droite n'ont plus de sens sur une
     seule colonne : on revient a un simple empilement, et le plan
     retrouve son format via la regle posee avec `.plan`. */
  .contact__droite { grid-template-rows: auto auto; }
  /* Le panneau sombre s'arrete au formulaire ; sans cette retouche il
     reste une bande de vert foret sous le bouton, la ou l'ancienne
     version avait le plan pour la remplir. */
  .contact__gauche { padding-bottom: clamp(44px, 6vw, 72px); }

  .categories { columns: 1; }
  .gal { columns: 2; }
  .rail { top: 66px; }
  .rail__in { flex-direction: column; align-items: stretch; gap: 10px; }
  .chercher { order: -1; }
  .chercher input { width: 100%; }

  .fiche__in, .commande { grid-template-columns: minmax(0, 1fr); }
  .fiche { padding-top: 100px; }
  .commande__recap { padding-top: 100px; padding-inline: var(--marge); }
  .commande__form { padding-top: clamp(40px, 6vw, 60px); padding-inline: var(--marge); }
}

@media (max-width: 720px) {
  .grille-produits, .signatures, .aussi { grid-template-columns: minmax(0, 1fr); }
  .gal { columns: 1; }
  .signatures { margin-top: 84px; }
  .signature + .signature { margin-top: 42px; }
  .form__duo { grid-template-columns: minmax(0, 1fr); }
  .pied__in { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 520px) {
  .carte { grid-template-columns: 84px minmax(0, 1fr); gap: 14px; padding: 14px; }
  .carte__txt { -webkit-line-clamp: 2; }
  .pied__in { grid-template-columns: minmax(0, 1fr); }
  .ligne { grid-template-columns: 54px minmax(0, 1fr); }
  .ligne img { width: 54px; }
  .ligne .compteur { grid-column: 2; justify-self: start; }
}

/* Ecrans larges : on laisse la page respirer sans l'etirer. */
@media (min-width: 1600px) { :root { --page: 1440px; } }
