/* local.css — RÈGLES MAISON DE LA VITRINE.
 *
 * Chargé APRÈS `hisy-v3.css`, jamais avant. C'est le seul endroit où corriger le rendu du
 * design : `public/css/hisy-v3.css` est sous test de conformité octet-par-octet
 * (`DesignSystemConformityTest`), et l'ADR-0003 §2 impose que la règle maison vive ici.
 *
 * Chaque règle porte la raison de son existence. Une règle sans raison écrite est une règle que
 * personne n'osera retirer.
 */

/* ── 1. Les cartes produit restaient invisibles ──────────────────────────────────────────────
 *
 * `hisy-v3.css` déclare `.reveal{animation:.65s both reveal}` avec une seule image-clé :
 *
 *     @keyframes reveal { 0% { opacity:0; transform:translateY(18px) } }
 *
 * Il manque le `to`. Avec `animation-fill-mode: both`, l'animation se termine — son
 * `playState` passe bien à « finished » — mais n'a aucun état final vers lequel aller : elle
 * fige l'élément sur la seule étape existante, donc `opacity: 0` pour toujours. Et une
 * animation l'emporte sur une déclaration normale, si bien que le `.reveal{opacity:1}` du
 * bundle ne peut rien rattraper.
 *
 * Effet constaté en ligne le 2026-08-15 : 13 cartes sur 13 invisibles sur la boutique,
 * 19 sur 19 sur l'accueil. Le HTML était correct, le CSS chargé, les données servies — et la
 * vitrine ne montrait aucun produit.
 *
 * Redéclarer `@keyframes` sous le même nom REMPLACE la définition précédente : on rend son
 * état final à l'animation sans toucher au fichier du design. */
@keyframes reveal {
    from { opacity: 0; transform: translateY(18px); }
    to   { opacity: 1; transform: none; }
}

/* Le rail des arrivages désactive l'animation (`.arrival-rail .reveal{animation:none}`) et
 * s'en remet à un repli `.reveal.in{opacity:1}` — mais aucun script du site n'ajoute jamais la
 * classe `in` : il n'y a pas un seul `IntersectionObserver` dans les six fichiers JS servis.
 * Le repli ne se déclenche donc jamais. On rend la visibilité inconditionnelle là où le design
 * a lui-même renoncé à l'animer. */
.arrival-rail .reveal { opacity: 1; transform: none; }

/* ── 2. Les titres sur fond sombre étaient illisibles ────────────────────────────────────────
 *
 * Le bundle applique `h1,h2,h3,h4,h5,h6 { color: var(--color-text-heading) }` — soit #2d1505,
 * le brun foncé. Cette règle porte sur l'élément (spécificité 0,0,1) alors que le design, lui,
 * fait HÉRITER la couleur claire de ses sections sombres. Or l'héritage perd toujours face à
 * une déclaration directe, quel que soit l'ordre des feuilles.
 *
 * Résultat : le titre du héros s'affichait en #2d1505 sur un fond #2d1505 — un contraste de
 * 1,00:1, c'est-à-dire rien. Douze titres touchés sur le seul accueil (héros, cartes de
 * collection, lookbook, bloc « Approvisionnement / Contrôle / Transparence », pied).
 *
 * On ne surcharge PAS `h1..h6`. On redéfinit le jeton que cette règle consulte, sur les blocs
 * dont le fond est sombre : pas de `!important`, pas de conflit de spécificité, et les titres
 * des sections claires ne bougent pas d'un pixel.
 *
 * Un premier correctif écrivait `h1..h6{color:inherit}`. Il ne tenait que sur les blocs qui
 * posent eux-mêmes une couleur claire — `.collection-card`, `.store-facts-list`… — et laissait
 * illisibles ceux qui n'en posent aucune et héritent du corps, donc du brun foncé :
 * `.hero-home`, `.lookbook-feature`, `.lookbooks-card`. D'où des titres encore invisibles après
 * la première correction. Audit du PO sur les 14 pages : la page lookbook à elle seule en
 * portait treize.
 *
 * Tous les blocs ci-dessous ont pour fond `var(--brown)` — vérifié un par un dans la feuille du
 * design, pas déduit du nom. */
.hero-home, .collection-card, .lookbook-feature, .store-facts-list,
.lookbooks-card, .about-v2-promise, .about-v2-local, .legal-hero,
.store-visit,
/* Ajoutés le 2026-08-18, signalés par le PO puis MESURÉS sur les seize pages publiques : ce sont
 * les deux SEULS titres restants sous le seuil de contraste, tous deux sur /faq —
 * « Une question ? Commencez ici. » et « Parlez-nous de la pièce ou de la réservation », à
 * 1,00:1, c'est-à-dire brun #2d1505 sur brun #2d1505.
 *
 * Le balayage a aussi confirmé, chiffres à l'appui, l'exclusion notée plus haut : le fond de
 * `.footer-newsletter` est un DÉGRADÉ clair, son titre foncé est correct, et l'ajouter ici aurait
 * créé le défaut exactement inverse. Une sonde qui ignore les dégradés le signale à tort — c'est
 * ce qui est arrivé au premier passage. */
.faq-v3-hero, .faq-v3-contact { --color-text-heading: var(--cream); }

/* ── 3. « Confirmer la réservation » était brun sur brun, donc invisible ─────────────────────
 *
 * Sur `/panier`, le bouton qui déclenche la réservation s'affichait comme une pastille brune
 * VIDE : couleur du texte `#2d1505` sur fond `#2d1505`, contraste 1,00:1. C'est le dernier
 * geste avant la réservation, et il n'y avait rien à lire dessus.
 *
 * La cause n'est ni la spécificité ni l'ordre des feuilles, mais les COUCHES DE CASCADE. Notre
 * bundle range ses utilitaires dans `@layer utilities` ; `hisy-v3.css` déclare, LUI, `button` et
 * `a` HORS COUCHE :
 *
 *     button { color: inherit }
 *     a      { color: inherit; text-decoration: none }
 *
 * Une règle sans couche l'emporte sur toute règle en couche, quelle que soit sa spécificité —
 * `button` (0,0,1) bat donc `.text-text-on-dark` (0,1,0). Tout `<a>` et tout `<button>` des
 * pages restées au chrome Tailwind perd sa couleur de texte et hérite de celle du corps.
 *
 * On ne surcharge pas `button` : on redéclare, hors couche elle aussi, la poignée d'utilitaires
 * que ces pages posent réellement sur un lien ou un bouton. Portée volontairement étroite —
 * ailleurs, `color: inherit` est ce que le design veut.
 *
 * Audit de contraste des 17 pages publiques, le 2026-08-16 : c'était le seul texte réellement
 * illisible du site. Les pages du design, elles, sont propres.
 *
 * Cette règle disparaît le jour où `/panier` passe au chrome du design. */
a.text-text-on-dark, button.text-text-on-dark { color: var(--color-text-on-dark); }
a.text-brown,        button.text-brown        { color: var(--color-brown); }
a.text-text-muted,   button.text-text-muted   { color: var(--color-text-muted); }

/* Les variantes aussi : `hover:text-danger` compile en `.hover\:text-danger:hover`, qui vit dans
 * la même couche et perd donc de la même façon. Sur le panier, le survol de « Retirer » ne
 * changeait pas de couleur — le même défaut, en moins visible. */
a.hover\:text-danger:hover, button.hover\:text-danger:hover { color: var(--color-danger); }

/* DEUX EXCEPTIONS, à ne pas perdre : leur fond est CLAIR et leur titre doit rester foncé.
 * Les traiter comme les autres créerait le défaut exactement inverse.
 *
 *   · `.caramel-card` — fond `var(--caramel)` (#c8956a), texte `var(--brown)` ;
 *   · `.footer-newsletter` — fond `linear-gradient(var(--cream) → #ead2b0)`, texte
 *     `var(--brown)`. Il figurait dans la liste auditée ; le relevé de son fond montre qu'il
 *     n'a jamais été en défaut. Il n'est donc pas touché — c'est un écart assumé à cette liste,
 *     et la raison est ici pour qu'on puisse le contredire en connaissance de cause.
 *
 * `.caramel-card` est déclaré APRÈS `.collection-card` : même spécificité, la cascade tranche. */
.caramel-card { --color-text-heading: var(--brown); }

/* ── 4. Deux avis côte à côte sur un téléphone ───────────────────────────────────────────────
 *
 * Le design VEUT une colonne sur mobile. Sa règle est simplement MORTE. `hisy-v3.css` déclare,
 * dans cet ordre :
 *
 *     .reviews-page-grid-v3 { grid-template-columns: 1fr 1fr }                  (base)
 *     @media (width <= 820px) { .reviews-page-grid-v3 { grid-template-columns: 1fr } }
 *     .reviews-page-grid-v3 { grid-template-columns: minmax(0,1fr) minmax(0,1fr) }  (base, PLUS LOIN)
 *
 * Une requête média n'ajoute aucune spécificité. La troisième règle, sans condition et placée
 * APRÈS, l'emporte donc à TOUTES les largeurs : la règle responsive du design ne s'applique
 * jamais, et un téléphone reçoit deux colonnes. Ce n'est pas un choix du design qu'on corrige,
 * c'est une déclaration en double qui annule le sien.
 *
 * Le défaut est resté invisible jusqu'au 2026-08-16 parce que la page ne servait AUCUN avis :
 * la grille n'avait jamais eu deux cartes à placer.
 *
 * On rétablit donc son seuil à lui — 820 px — plutôt que d'en inventer un.
 * Décision PO du 2026-08-16 : un avis par ligne sur mobile. */
@media (max-width: 820px) {
  .reviews-page-grid-v3 { grid-template-columns: 1fr; }
}

/* Une colonne ne suffisait pas. Le titre du bloc porte un `width:401px` EN DUR, posé par
 * `avis.js` et repris du prototype statique : cette largeur devient celle du conteneur, donc
 * celle de la grille, donc celle des cartes. Mesuré sur un écran de 375 px : la carte allait de
 * 18 px à 419 px, et la citation se terminait 15 px HORS de l'écran — le texte de l'avis était
 * coupé à droite, sans même une barre de défilement pour le signaler.
 *
 * Le débordement n'existe qu'en dessous de ~437 px (401 px + les deux gouttières de 18) : la
 * règle s'arrête donc à 480 px, et la largeur voulue par le design est respectée partout
 * ailleurs. `!important` est le seul recours — rien d'autre ne l'emporte sur un attribut
 * `style`. */
@media (max-width: 480px) {
  .published-reviews-v3 h2[style] { width: auto !important; }
  .published-reviews-v3, .reviews-page-grid-v3 { max-width: 100%; }
}

/* ── Texte enrichi de la fiche pièce (ADR-0019) ───────────────────────────────────────────────
 * La description éditoriale contient désormais de l'HTML assaini. Le design ne stylait que
 * `.pdp-block p`, avec `margin:0` — deux paragraphes se seraient collés — et ne connaissait ni
 * liste, ni sous-titre, ni citation : son texte d'exemple était d'un seul tenant.
 * Ces règles vivent ICI parce que `hisy-v3.css` est sous conformité octet par octet (ADR-0003). */
/* Deux classes, et c'est délibéré : `.pdp-rich > * + *` ne pèse que 0-1-0 — l'étoile ne compte pas
 * — quand `.pdp-block p { margin: 0 }` du design pèse 0-1-1. La règle existait et ne gagnait pas ;
 * les paragraphes restaient collés. Mesuré à l'écran, pas relu. */
.pdp-block .pdp-rich > * + * { margin-top: .9em; }
.pdp-rich ul,
.pdp-rich ol { margin: 0; padding-left: 1.25rem; max-width: 82ch; font-size: 14px; line-height: 1.75; color: var(--body); }
.pdp-rich ul { list-style: disc; }
.pdp-rich ol { list-style: decimal; }
.pdp-rich li + li { margin-top: .35em; }
.pdp-rich h3,
.pdp-rich h4 { margin: 0; font-family: "Fraunces Variable", Georgia, serif; font-weight: 530; letter-spacing: -.03em; line-height: 1.2; color: var(--brown); }
.pdp-rich h3 { font-size: 17px; }
.pdp-rich h4 { font-size: 15px; }
.pdp-rich blockquote { margin: 0; padding-left: 14px; border-left: 2px solid var(--line); max-width: 82ch; font-size: 14px; line-height: 1.75; color: var(--muted); }
.pdp-rich a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }

/* ── Bandeau d'aperçu du staff (décision PO du 2026-08-17) ────────────────────────────────────
 * L'aperçu admin d'une pièce sert la fiche RÉELLE de la vitrine, brouillon compris. Ce bandeau
 * est la seule chose qui l'en distingue : il doit donc être immédiatement lisible comme n'étant
 * PAS la boutique. D'où le brun plein, hors de la palette des surfaces de la vitrine.
 * Jamais rendu pour le public : la coquille ne le pose que si `apercuStaff` est fourni. */
/* NON COLLANT, délibérément. L'en-tête de la vitrine l'est déjà : deux barres collantes se
 * chevauchaient et le bouton « Retour » passait sous le logo. Le staff lit le bandeau en haut,
 * puis descend dans la fiche telle que la cliente la verra — ce qui est le but. */
.staff-preview-bar {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 10px 18px;
  padding: 9px 20px;
  background: var(--brown); color: var(--cream);
  font-size: 13px; line-height: 1.45;
  box-shadow: 0 2px 10px rgb(0 0 0 / .18);
}
.staff-preview-bar strong { display: block; font-weight: 650; letter-spacing: .01em; }
.staff-preview-bar span { opacity: .82; font-size: 12px; }
.staff-preview-bar a {
  flex: none; padding: 6px 13px; border-radius: 8px;
  background: var(--caramel); color: var(--brown);
  font-weight: 620; text-decoration: none;
}
.staff-preview-bar a:hover { filter: brightness(1.06); }

/* ── Carte de scène « Dans nos murs » enrichie (décision PO du 2026-08-18) ────────────────────
 * ÉCART ASSUMÉ AU DESIGN DE LA VITRINE. Sa carte ne porte qu'une image et une `<figcaption>`.
 * L'écran d'admin, lui, fait saisir un SUR-TITRE, un TITRE et une DESCRIPTION — trois colonnes
 * réelles de `in_store_gallery_items`, qui n'étaient rendues nulle part. Un champ qu'on fait
 * remplir sans jamais l'afficher est la même faute qu'un champ affiché sans le remplir.
 *
 * La règle du design sur `figcaption` est CONSERVÉE telle quelle — position, couleur, serif,
 * taille fluide : c'est elle qui porte le titre. On ne fait qu'empiler autour, et chaque partie
 * disparaît quand elle est vide (le JS ne pose pas l'élément). Ici et jamais dans `hisy-v3.css`,
 * sous conformité octet par octet (ADR-0003 §2). */
.store-life-card figcaption { display: flex; flex-direction: column; gap: 6px; }

.store-life-kicker {
  font-family: "Inter Variable", system-ui, sans-serif;
  font-size: 11px; font-weight: 700; letter-spacing: .11em; text-transform: uppercase;
  color: var(--caramel, #c8956a);
}
/* Le titre n'a AUCUNE règle propre : il hérite de `figcaption`, donc du design. Lui en donner
 * une le ferait diverger le jour où le design change sa typographie. */
.store-life-desc {
  font-family: "Inter Variable", system-ui, sans-serif;
  font-size: 13px; font-weight: 400; line-height: 1.5;
  color: var(--cream, #f7f1e8); opacity: .82;
  max-width: 34ch;
}
@media (max-width: 640px) {
  .store-life-kicker { font-size: 10px; }
  .store-life-desc { font-size: 12px; max-width: none; }
}

/* ────────────────────────────────────────────────────────────────────────────────────
   REPÈRES DU LOOKBOOK : un calque calé sur la PHOTO, pas sur le cadre (2026-08-18)
   ────────────────────────────────────────────────────────────────────────────────────
   `hisy-v3.css` rend l'image du dialogue en `object-fit: contain` : elle n'occupe donc
   qu'une partie de la scène, le reste étant la version floutée en fond. Les repères,
   eux, étaient positionnés en pourcentage de LA SCÈNE — un point à 70 % tombait donc
   bien plus bas que 70 % de la photo. Signalé par le PO : un repère posé sur un pantalon
   s'affichait sur le sol.
   `lookbook.js` mesure la boîte réellement occupée par l'image (ratio natif fourni par le
   navigateur) et pose ces quatre valeurs en ligne. Cette règle ne fait que préparer le
   calque : positionné, transparent, et laissant passer les clics partout sauf sur les
   repères eux-mêmes — sans quoi il masquerait la photo au clic. */
.lookbook-hotspot-frame {
  position: absolute;
  inset: 0;
  pointer-events: none;
}
.lookbook-hotspot-frame > .lookbook-hotspot {
  pointer-events: auto;
}

/* L'IMAGE REMPLIT SON CADRE (décision PO du 2026-08-18).
   `hisy-v3.css` la rend en `object-fit: contain` : les photos de look étant en PAYSAGE
   (1536×1024 pour l'illustration du design) dans un cadre portrait 3:4, elles flottaient
   entre deux bandes floues et n'occupaient qu'une fraction de la place. Le cadre RESTE en
   3:4 — c'est le format retenu par le PO, et celui du design d'origine — mais l'image le
   REMPLIT désormais : elle est recadrée sur les côtés au lieu d'être encadrée de vide.
   La gérante compose dans ce même recadrage, donc ce qu'elle voit est ce qui part.
   La feuille du design est verrouillée octet par octet (ADR-0003) : la surcharge vit ici. */
.lookbook-dialog-media-stage > img { object-fit: cover; }

/* ────────────────────────────────────────────────────────────────────────────────────
   REPÈRES DU LOOKBOOK — plus discrets, sans perdre en accessibilité (2026-08-18)
   ────────────────────────────────────────────────────────────────────────────────────
   Demande PO : « diminuer la taille des puces sur vitrine ». Le design pose une pastille
   de 39 px dans une zone de 56, halo compris jusqu'à ~76 px. Sur une scène de 465 px de
   haut, un repère masquait la pièce qu'il désigne — et trois repères en masquaient trois.

   LA RÈGLE QUI GOUVERNE CE BLOC : on rétrécit ce qui SE VOIT, jamais ce qui SE CLIQUE.
   La zone cible reste à 44 px, le minimum AAA du critère WCAG 2.5.5 (Target Size). Une
   pastille de 26 px cliquable sur 26 px serait plus jolie et inutilisable au pouce.

   Le halo suit le même raisonnement : discret au repos, franc au survol et au focus. Un
   halo permanent et large est une décoration ; un halo qui répond est une affordance. */

/* Zone cliquable : 56 → 44 px. Invisible, et c'est le but. */
.lookbook-hotspot { width: 44px; height: 44px; }

/* Pastille visible : 39 → 26 px. La bordure passe de 3 à 2 px pour que le chiffre
   respire à cette taille, et la police suit — 17 → 12 px reste lisible en Fraunces. */
.lookbook-hotspot-number {
  width: 26px;
  height: 26px;
  font-size: 12px;
  border-width: 2px;
  box-shadow: 0 4px 12px rgba(45, 21, 5, .34);
}

/* Halo au repos : resserré (−10 → −4 px) et allégé. Il signale, il n'encombre pas. */
.lookbook-hotspot-halo {
  inset: -4px;
  opacity: .55;
  box-shadow: 0 0 0 4px rgba(45, 21, 5, .08);
}

/* Au survol ET au focus clavier : le halo s'ouvre franchement. L'écart doit être plus
   marqué qu'avant, puisqu'on part de plus petit. */
.lookbook-hotspot:hover .lookbook-hotspot-halo,
.lookbook-hotspot:focus-visible .lookbook-hotspot-halo,
.lookbook-hotspot.is-active .lookbook-hotspot-halo {
  opacity: 1;
  transform: scale(1.22);
}

/* FOCUS CLAVIER : le design n'en pose AUCUN sur ce bouton. Sans fond ni bordure, l'anneau
   par défaut du navigateur est invisible sur une photo sombre — le repère devenait
   inatteignable au clavier sans qu'on sache où l'on est. */
.lookbook-hotspot:focus-visible {
  outline: none;
}
.lookbook-hotspot:focus-visible .lookbook-hotspot-number {
  box-shadow: 0 0 0 3px var(--cream, #f5ecd7), 0 0 0 6px rgba(45, 21, 5, .55);
}

/* Repère actif : l'anneau se resserre avec la pastille (4 → 3 px). */
.lookbook-hotspot.is-active .lookbook-hotspot-number {
  box-shadow: 0 0 0 3px rgba(200, 149, 106, .72), 0 5px 16px rgba(45, 21, 5, .38);
}

/* Pièce vendue : même resserrement, halo tramé conservé. */
.lookbook-hotspot.is-sold .lookbook-hotspot-halo { inset: -7px; }

/* Étiquette « Vendu » : elle suivait une pastille de 39 px, elle en suit une de 26. Et
   7 px n'est pas une taille de texte — 9 px avec sa graisse et son interlettrage reste
   sobre tout en étant lisible. */
.lookbook-hotspot-status {
  top: 34px;
  font-size: 9px;
  padding: 4px 7px;
}

/* Le halo s'anime : on le tait pour qui a demandé moins de mouvement. */
@media (prefers-reduced-motion: reduce) {
  .lookbook-hotspot-halo { transition: none; }
  .lookbook-hotspot:hover .lookbook-hotspot-halo,
  .lookbook-hotspot:focus-visible .lookbook-hotspot-halo,
  .lookbook-hotspot.is-active .lookbook-hotspot-halo { transform: none; }
}

/* ── L'état « Aucun avis publié » se peignait dans la colonne du panneau de note ───────────────
 *
 * Constaté à l'écran par le PO le 2026-08-19 : le titre « Aucun avis publié pour l'instant »
 * tombait à UN MOT PAR LIGNE, le paragraphe était comprimé dans une colonne de 68 px, et le
 * libellé du bouton se cassait sur trois lignes. Mesuré sur l'aperçu, à 1440 px de large.
 *
 * DEUX RÈGLES DU DESIGN SE PERCUTENT, et il faut les deux pour expliquer le rendu.
 *
 * 1. LA COLONNE. La section est une grille à deux pistes :
 *
 *        .reviews-service-content { grid-template-columns: 320px minmax(0,1fr) }
 *        @media … { .reviews-service-content { grid-template-columns: 280px minmax(0,1fr) } }
 *
 *    La première piste est celle du panneau de note (`aside.rating-panel-v3`), la seconde celle
 *    de la liste. Or `avis.js` MASQUE le panneau tant qu'aucun avis n'est publié
 *    (`panel.hidden = true`, public/js/avis.js). Un élément `hidden` quitte le flux de la
 *    grille : la liste est alors placée automatiquement dans la PREMIÈRE piste — celle qui fait
 *    320 px et qui était taillée pour le panneau — pendant que la piste de 810 px reste vide.
 *    Mesuré : `.published-reviews-v3` large de 320 px sur un écran de 1440.
 *
 * 2. LA GRILLE INTERNE. Le design déclare
 *
 *        .published-reviews-v3 > div:first-child { grid-template-columns: .88fr 1.12fr;
 *                                                  align-items: end; gap: 50px; display: grid }
 *
 *    Cette règle vise l'EN-TÊTE du cas où des avis existent (« Ce que disent nos clients. » et
 *    son paragraphe, côte à côte). Mais dans l'état vide, le premier enfant est le panneau vide
 *    LUI-MÊME : il hérite donc des deux colonnes et de `align-items: end`, qui contredisent son
 *    propre bloc `.reviews-empty-v3 { display: grid; justify-items: center; gap: 14px }`.
 *    Mesuré à 1440 px : deux pistes de 151 px et 68,5 px, un h2 haut de 210 px.
 *
 * LE DÉFAUT EST CELUI DU DESIGN, pas de notre intégration : la feuille du kit
 * (docs/reference/design-system/source/v3/v3-vitrine/site/assets/css/hisy-v3.css:197-200) porte
 * les mêmes règles, et son propre `avis.js` injecte le même panneau au même endroit. Le kit
 * n'avait simplement jamais eu à peindre cet état. On ne touche donc pas `hisy-v3.css`
 * (conformité octet-par-octet, ADR-0003 §2) : on rend au panneau la mise en page que le design
 * lui décrit ailleurs, sans rien inventer.
 *
 * En dessous du seuil où la section passe à une colonne, rien de tout cela ne se produit :
 * mesuré à 375 px, le panneau est déjà correct (339 px de large, une colonne). Les deux règles
 * ci-dessous ne changent donc que les largeurs où le défaut existe. */

/* La liste prend la rangée entière quand le panneau de note est masqué. Spécificité 0-3-0
 * (.rating-panel-v3 + [hidden] + .published-reviews-v3) ; rien d'autre ne pose `grid-column`
 * sur cet élément. Le sélecteur dit exactement le fait : PAS de panneau, PAS de colonne pour
 * lui. Quand le panneau est visible, il ne s'applique pas — la mise en page du design est
 * intacte. */
.rating-panel-v3[hidden] + .published-reviews-v3 { grid-column: 1 / -1; }

/* Le panneau vide retrouve SA grille — celle de son propre bloc dans le design : une colonne
 * (`grid-template-columns` non déclaré, donc `none`), pas d'alignement de fin (`align-items`
 * non déclaré, donc `normal`), et l'espacement de 14 px qu'il déclare.
 * Spécificité 0-3-0 contre 0-2-1 pour la règle du design (`.published-reviews-v3 > div:first-child`) :
 * elle l'emporte sur la spécificité, sans avoir besoin de `!important`. `justify-items: center`
 * n'est pas repris : le design le pose déjà et personne ne le contredit. */
.published-reviews-v3 > .reviews-empty-v3:first-child {
  grid-template-columns: none;
  align-items: normal;
  gap: 14px;
}

/* ── Bandeau promo : icône et fermeture (arbitrage PO du 2026-08-24) ─────────────────────────────
 * L'écran d'administration propose une icône et une case « masquable par le client » ; son aperçu
 * montre même la croix. Le design de la vitrine ne dessine ni l'une ni l'autre — ces deux éléments
 * sont donc habillés ici, dans la maison, sans toucher `hisy-v3.css` (ADR-0003 §2).
 * Les couleurs, elles, viennent de la campagne et sont posées en style en ligne : elles changent
 * d'une promotion à l'autre et n'ont pas de place dans une feuille figée. */
.announcement { position: relative; }

.announcement-icon {
  flex: 0 0 auto;
  margin-right: 8px;
  vertical-align: -3px;
}

.announcement-close {
  position: absolute;
  right: 10px;
  top: 50%;
  transform: translateY(-50%);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  padding: 0;
  border: none;
  border-radius: 9999px;
  background: transparent;
  color: inherit;
  opacity: .65;
  cursor: pointer;
}

.announcement-close:hover,
.announcement-close:focus-visible { opacity: 1; background: rgba(0, 0, 0, .08); }

/* Sous 640 px le texte occupe toute la largeur : la croix ne doit pas le recouvrir. */
@media (max-width: 640px) {
  .announcement-center { padding-right: 34px; }
}

/* ── Popup de bienvenue (arbitrage PO du 2026-08-24) ─────────────────────────────────────────────
 * Le design de la vitrine ne prévoit pas de popup ; l'écran d'administration, si — il en montre
 * même l'aperçu. L'habillage vit donc ici, dans la maison, avec les jetons du design pour que la
 * boîte reste de la même famille que le reste du site. */
.hisy-welcome {
  position: fixed;
  inset: 0;
  z-index: 120;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  background: rgba(45, 21, 5, .55);
}

.hisy-welcome[hidden] { display: none; }

.hisy-welcome-card {
  position: relative;
  width: 100%;
  max-width: 400px;
  padding: 32px 26px 26px;
  border-radius: 20px;
  background: #FDF8F2;
  text-align: center;
  box-shadow: 0 30px 80px rgba(45, 21, 5, .4);
}

.hisy-welcome-card h2 {
  margin: 0;
  font-family: var(--font-display, "Fraunces Variable", serif);
  font-size: 1.5rem;
  font-weight: 800;
  line-height: 1.2;
  color: #2D1505;
}

.hisy-welcome-card p { margin: 10px 0 16px; font-size: .9rem; color: #7A6A57; }

.hisy-welcome-code {
  margin-bottom: 16px;
  padding: 10px;
  border-radius: 10px;
  background: #F5ECD7;
  color: #A8734E;
  font-family: ui-monospace, monospace;
  font-weight: 800;
  letter-spacing: .06em;
}

.hisy-welcome-close {
  position: absolute;
  right: 12px;
  top: 12px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  padding: 0;
  border: none;
  border-radius: 9px;
  background: #F5ECD7;
  color: #2D1505;
  cursor: pointer;
}

/* Bandeau promo en mode IMAGE : le visuel occupe TOUTE la largeur, en HAUTEUR AUTOMATIQUE
 * (demande PO du 2026-08-24 : « laisser le bandeau texte ou image prendre toute la largeur et une
 * hauteur auto pour ne pas déformer l'image »).
 *
 * Une hauteur imposée obligeait à recadrer (`object-fit: cover`) : sur un bandeau de 8:1, le
 * recadrage coupait le message. La hauteur suit donc le rapport de l'image — c'est la boutique qui
 * choisit le format de son bandeau, pas la feuille de style. */
.announcement.announcement-visuel {
  padding: 0;
  display: block;
  /* LA BANDE DE COULEUR SOUS L'IMAGE VENAIT DE LA : le lien du design est `inline-flex`, donc une
   * boite de ligne — et une ligne reserve la place des jambages sous l'image. Six pixels de fond
   * qui se lisaient comme un second bandeau vide. Mettre l'interligne a zero les supprime. */
  line-height: 0;
  font-size: 0;
  min-height: 0;
}

/* Specificite 0-2-0 : la regle du design sur `.announcement a` est en 0-1-1 et l'emportait,
 * laissant le lien en `inline-flex`. */
.announcement.announcement-visuel > .announcement-visuel-lien { display: block; line-height: 0; }

.announcement-visuel-lien img {
  display: block;
  width: 100%;
  height: auto;
}

/* Visuel du popup de bienvenue, en 4:3 (demande PO du 2026-08-24). Le rapport est imposé par le
 * cadre et l'image recadrée au centre : sans cela, une image d'un autre format étirerait la boîte
 * au lieu de s'y ranger. */
/* MODE IMAGE : le visuel EST le popup — pas une illustration posée au-dessus d'un texte
 * (demande PO du 2026-08-25, même dualité que le bandeau). La carte n'a donc plus de marge
 * intérieure : elle est le cadre de l'image. */
.hisy-welcome-card.hisy-welcome-illustre { padding: 0; overflow: hidden; }

.hisy-welcome-visuel {
  display: block;
  aspect-ratio: 4 / 3;
  background: #F5ECD7;
  line-height: 0;
}

.hisy-welcome-visuel img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* ── Le bandeau d'annonce FIXE s'efface devant le bandeau de campagne ────────────────────────────
 * (demande PO du 2026-08-24 : « cacher ceci quand un bandeau est actif »).
 *
 * Le design pose en haut de page une bande d'annonces permanentes — « Pièces uniques · Nouveautés
 * chaque semaine · Retrait à Aïtchedji ». Quand une campagne est publiée, deux bandes se succèdent
 * et disent deux choses à la fois : la promotion perd la place qu'on lui a donnée.
 *
 * Écrit en CSS et non dans le gabarit : le header vient du kit du design et n'est pas retouché
 * (ADR-0003 §2). Le sélecteur de voisinage suffit — le bandeau de campagne précède la bande fixe
 * dans la coquille, et n'existe que si une campagne est en cours. */
.announcement-promo ~ .announcement { display: none; }

/* La croix reste lisible SUR une image : sans fond, elle se perdait dans un visuel clair. */
.announcement-visuel .announcement-close {
  background: rgba(45, 21, 5, .55);
  color: #FDF8F2;
  opacity: 1;
}

.announcement-visuel .announcement-close:hover { background: rgba(45, 21, 5, .8); }

/* Pastille « Vente flash » de la fiche pièce : le design pose `pdp-badge-new`, `-unique` et
 * `-deal`, pas de variante flash — elle reprend donc les jetons de `-deal`, en caramel, pour
 * rester de la même famille. (Demande PO du 2026-08-24.) */
.pdp-badge.pdp-badge-flash {
  background: var(--caramel, #C8956A);
  color: var(--brown, #2D1505);
  font-variant-numeric: tabular-nums;
}

/* Chiffres à chasse fixe : sans cela le décompte fait sautiller la pastille à chaque seconde. */
.product-flash[data-flash-fin] { font-variant-numeric: tabular-nums; }

/* ── Le bandeau de campagne, au modèle de la barre qu'il remplace ────────────────────────────────
 * (arbitrage PO du 2026-08-24 : « il doit juste servir de modèle pour un bandeau de texte ».)
 * Deux barres annonçaient la même promotion, chacune avec sa source. Il n'en reste qu'une : celle
 * du design, alimentée par la campagne publiée, avec l'étiquette et l'appel à l'action de l'autre. */
/* LA BANDE DU DESIGN EST UNE GRILLE `1fr auto 1fr` — trois colonnes pour ses trois annonces
 * permanentes. Notre bandeau n'a qu'un enfant : il atterrissait donc dans la PREMIÈRE colonne,
 * c'est-à-dire un tiers de la largeur, d'où un titre qui passait à la ligne pendant que les deux
 * autres tiers restaient vides (constat PO du 2026-08-25).
 * En flex, l'unique lien prend toute la largeur du conteneur du design, qui garde sa marge. */
.announcement.announcement-promo { display: flex; }

.announcement-barre {
  display: flex;
  align-items: center;
  gap: 14px;
  flex: 1 1 auto;
  min-width: 0;
  justify-content: flex-start;
  /* La croix de fermeture est posée à droite, en absolu : sans cette réserve, l'appel à
   * l'action passerait dessous. */
  padding-right: 30px;
}

/* L'appel à l'action au bout, le titre au début : la lecture va du quoi vers le où. */
.announcement-promo .announcement-cta { margin-left: auto; }

.announcement-tag {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex: 0 0 auto;
  padding: 4px 11px;
  border-radius: 9999px;
  background: var(--caramel, #C8956A);
  color: var(--brown, #2D1505);
  font-size: .64rem;
  font-weight: 800;
  letter-spacing: .08em;
  text-transform: uppercase;
}

/* Le titre s'allonge et se tronque plutôt que de passer à la ligne : une bande d'annonce qui
 * grandit d'une ligne pousse toute la page vers le bas à chaque changement de campagne. */
.announcement-titre {
  flex: 0 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-weight: 700;
  letter-spacing: .02em;
}

/* Le détail cède la place en premier : c'est le seul élément dont l'absence ne coûte rien. */
.announcement-detail { opacity: .72; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.announcement-cta { flex: 0 0 auto; display: inline-flex; align-items: center; gap: 6px; font-weight: 700; }

@media (max-width: 900px) { .announcement-detail { display: none; } }

@media (max-width: 640px) {
  .announcement-barre { gap: 8px; justify-content: flex-start; }
  .announcement-cta { display: none; }
}

/* ── Bande lookbook : hauteur fixe et visuel en fond ─────────────────────────────────────────────
 * (demande PO du 2026-08-25 : « mettre en cover sur l'arrière afin de fixer la hauteur à 720 px ».)
 *
 * Le design pose `min-height: 620px` : la bande grandissait avec le texte à sa droite, et changeait
 * de hauteur d'un lookbook à l'autre. Une hauteur fixe donne un bloc stable, et le visuel du
 * lookbook — c'est le même média que la page Lookbook — le remplit en `cover`. */
.lookbook-image {
  height: 720px;
  min-height: 0;
}

/* Sous 900 px la colonne passe sous le texte : 720 px y feraient un mur à faire défiler. */
@media (max-width: 900px) {
  .lookbook-image { height: 420px; }
}

/* ── Liens des promotions en cours, dans la boutique ─────────────────────────────────────────────
 * (demande PO du 2026-08-25 : « il faudra un lien pour chaque promotion ».)
 *
 * Le filtre « Bon plan » de la barre rassemble TOUT ce qui a subi une réduction. Ces liens montrent
 * UNE opération — « les pièces de la vente flash du week-end » — et se partagent tels quels. */
.promo-liens {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 12px;
}

.promo-lien {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 6px 12px;
  border: 1px solid var(--border, #E8D4B4);
  border-radius: 9999px;
  background: var(--cream-light, #FDF8F2);
  color: var(--brown, #2D1505);
  font-size: .78rem;
  font-weight: 700;
  text-decoration: none;
  line-height: 1.2;
}

.promo-lien:hover,
.promo-lien:focus-visible { border-color: var(--caramel, #C8956A); }

/* La promotion affichée se distingue : sans cela, on ne sait pas ce qu'on regarde. */
.promo-lien.is-active {
  background: var(--caramel, #C8956A);
  border-color: var(--caramel, #C8956A);
}

/* Le nombre de pièces, en retrait : c'est un repère, pas le sujet. */
.promo-lien small {
  font-size: .68rem;
  font-weight: 800;
  opacity: .6;
  font-variant-numeric: tabular-nums;
}

/* Les chiffres du décompte à chasse fixe, et l'horloge alignée sur le texte. */
.product-flash svg, .pdp-badge-flash svg { flex: 0 0 auto; vertical-align: -1px; }

/* ══════════════════════════════════════════════════════════════════════════════════════════
   LE NOM SUR LE FORMULAIRE NEWSLETTER (demande PO du 2026-08-26, ADR-0073 §4.1)
   ══════════════════════════════════════════════════════════════════════════════════════════
   Le formulaire du design est une grille a DEUX colonnes — `minmax(220px,1fr) auto` : le champ
   e-mail, puis le bouton. Un troisieme enfant atterrirait dans la colonne du bouton et
   l'ecraserait. Le nom prend donc sa propre ligne, comme le consentement et le message d'etat
   qui portent deja `grid-column: 1 / -1` dans la reference. La ligne e-mail + bouton du design
   n'est pas touchee. */
.newsletter-email-form .newsletter-email-name {
  grid-column: 1 / -1;
}

/* « (facultatif) » doit se lire comme une precision, pas comme une seconde etiquette : meme
   couleur que le libelle, poids normal, et sans majuscule imposee par le design. */
.newsletter-email-form .nl-optional {
  font-weight: 400;
  text-transform: none;
  opacity: .7;
}

/* ══════════════════════════════════════════════════════════════════════════════════════════
   « EN LOT », À LA SUITE DES DÉTAILS (demande PO du 2026-08-26, ADR-0074 §7)
   ══════════════════════════════════════════════════════════════════════════════════════════
   ELLE NE RESSEMBLE PAS À UNE PASTILLE DE REMISE, et c'est délibéré. « −30 % » annonce un prix
   déjà baissé, visible sur la ligne du dessus ; « lot 6/5 » annonce une offre À SAISIR, qui
   n'existera qu'au panier. Les confondre ferait chercher un prix barré qui ne viendra pas — et une
   cliente qui cherche un prix barré absent conclut à une erreur.

   ET ELLE TIENT DANS LA LIGNE. La phrase entière — « 6 articles au prix de 5 · Femme » — prenait
   trois lignes sur une grille déjà dense. Elle vit maintenant à la suite de « Taille L · Grade B »,
   dans sa forme courte ; le libellé complet est dans `title` et `aria-label`, donc au survol pour
   la souris et à la lecture pour un lecteur d'écran.

   SUR TACTILE, `title` NE S'OUVRE PAS. C'est assumé : la forme courte se suffit — « lot 6/5 » dit
   déjà l'offre, et le panier la nomme en entier dès qu'elle s'applique. */
.catalog-product-details .product-deal {
  color: var(--caramel-dark, #A8734E);
  font-weight: 700;
  cursor: help;
  /* Le pointillé dit qu'il y a quelque chose de plus à lire — sans l'encadrer comme une pastille. */
  text-decoration: underline dotted currentColor;
  text-underline-offset: 3px;
}


.drawer-deal-hint {
  margin: 6px 0 0;
  font-size: 11px;
  color: var(--caramel-dark, #A8734E);
}

/* ── Le bandeau de marques (ADR-0075 pour la règle, ADR-0076 §3 pour la forme) ─────────────────
 *
 * TRANSPOSITION FIDÈLE de `brand-marquee.css`, livré par le PO le 2026-08-26. Le design V3 est
 * maître (ADR-0002) : ma première version — pastilles encadrées, 42 s, 96 px de large — est
 * remplacée par la sienne, à l'identique.
 *
 * CE QUE LE DESIGN FAIT ET QUE LA MIENNE NE FAISAIT PAS : le logo SEUL, sans cadre, à 45 %
 * d'opacité. La marque s'efface, la boutique reste le sujet — et le survol la ramène en pleine
 * lumière. Une pile de pastilles encadrées, elle, se lit comme une barre de filtres.
 *
 * LA BOUCLE EST UNE ANIMATION CSS SUR UNE PISTE DUPLIQUÉE : elle n'occupe pas le fil principal,
 * elle continue si un script échoue, et elle s'arrête d'elle-même sous `prefers-reduced-motion`.
 * Le décalage est de -50 %, exactement la moitié de la piste — toute autre valeur produirait un
 * saut visible à chaque tour. */
.brand-marquee {
  border-block: 1px solid var(--line, #E3D8C3);
  background: var(--cream-light, #FBF7EE);
  padding: 30px 0;
  overflow: hidden;
}

/* Les extrémités s'estompent : sans cela les logos apparaissent et disparaissent net, et le
   défilement se lit comme un décrochage plutôt que comme une boucle. */
.brand-marquee-viewport {
  -webkit-mask-image: linear-gradient(90deg, #0000, #000 7%, #000 93%, #0000);
  mask-image: linear-gradient(90deg, #0000, #000 7%, #000 93%, #0000);
  overflow: hidden;
}

.brand-marquee-track {
  width: max-content;
  display: flex;
  align-items: center;
  animation: brand-marquee-scroll 70s linear infinite;
}

/* UNE PASTILLE QUI FUIT SOUS LE CURSEUR NE SE CLIQUE PAS. */
.brand-marquee:hover .brand-marquee-track,
.brand-marquee:focus-within .brand-marquee-track { animation-play-state: paused; }

.brand-marquee-item {
  flex: none;
  place-items: center;
  height: 42px;
  margin-right: 66px;
  display: grid;
  opacity: .45;
  transition: opacity .25s, transform .25s;
}

.brand-marquee-item img {
  width: auto;
  max-width: 132px;
  height: auto;
  max-height: 42px;
  object-fit: contain;
  display: block;
}

.brand-marquee-name {
  color: var(--brown, #2D1505);
  letter-spacing: .02em;
  white-space: nowrap;
  font-family: 'Fraunces Variable', Fraunces, Georgia, serif;
  font-size: 17px;
  font-weight: 640;
  line-height: 1;
  text-decoration: none;
}

.brand-marquee-item:hover,
.brand-marquee-item:focus-visible { opacity: 1; transform: scale(1.05); }

/* La marque filtrée reste en pleine lumière : sans cela, on ne sait pas ce qu'on regarde. */
.brand-marquee-item.is-active { opacity: 1; }

@keyframes brand-marquee-scroll {
  from { transform: translate3d(0, 0, 0); }
  to { transform: translate3d(-50%, 0, 0); }
}

@media (max-width: 720px) {
  .brand-marquee { padding: 22px 0; }
  .brand-marquee-item { height: 30px; max-width: 100px; margin-right: 42px; }
  .brand-marquee-item img { max-width: 100px; max-height: 30px; }
}

/* CEUX QUI DEMANDENT MOINS DE MOUVEMENT N'EN ONT PAS. Le bandeau reste lisible et défilable à la
   main : une bande figée vaut mieux qu'une bande qui donne le vertige. */
@media (prefers-reduced-motion: reduce) {
  .brand-marquee-track { animation: none; }
  .brand-marquee-viewport { overflow-x: auto; scrollbar-width: none; }
}

/* ── Le décompte de vente flash tient sur UNE ligne (demande PO du 2026-08-26) ─────────────────
 *
 * La pastille est posée dans une pile étroite, en haut à gauche de l'image. Sans consigne de
 * césure, « VENTE FLASH 39:06:11 » se coupait en deux lignes et l'heure passait sous le libellé :
 * on lisait un badge à deux étages sur une vignette de 200 px, et le compte à rebours — tout
 * l'objet de la pastille — devenait le second rôle.
 *
 * LES CHIFFRES À CHASSE FIXE, parce qu'ils changent chaque seconde : sans `tabular-nums`, la
 * pastille respire à chaque battement et l'œil suit un texte qui bouge. */
.product-flash,
.pdp-badge-flash {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  white-space: nowrap;
}

.product-flash [data-flash-txt],
.pdp-badge-flash [data-flash-txt] {
  font-variant-numeric: tabular-nums;
}

/* Silhouette dont aucune piece n'est plus en ligne (correctif du 2026-08-28). La liste des
   pieces laissait un vide sans phrase ; le design n'avait pas de cas pour cela, on l'ajoute ici
   plutot que dans la feuille de reference. */
.lookbook-products-empty {
  margin: 12px 0 0;
  color: rgba(45, 21, 5, .68);
  font-size: 15px;
  line-height: 1.5;
}

/* LE TITRE DU BLOC LOOKBOOK OCCUPAIT LE TIERS DE SA COLONNE (demande PO du 2026-08-28).
   Mesure a l'ecran, viewport 782 px : colonne interieure de 723 px, titre de 258 px — 465 px de
   vide a droite. Deux causes qui se cumulent, et une seule est corrigeable sans toucher au dessin :
   · `.lookbook-copy` aligne ses enfants en `flex-start`, donc le h2 se retracte a la largeur de sa
     plus longue ligne au lieu de prendre la colonne ;
   · sa taille est `clamp(54px, 6vw, 92px)` : entre 700 et 900 px de large, `6vw` vaut 42 a 54 px,
     donc c'est le PLANCHER qui s'applique — le titre ne grandit plus alors que la grille est deja
     passee en une seule colonne, large.
   On elargit la boite et on redresse la pente : `11vw` ne change rien au mobile (le plancher tient
   sous 490 px) ni au grand desktop (le plafond tient au-dela de 836 px). Seule la zone
   intermediaire, celle qui etait creuse, se remplit. */
.lookbook-copy h2 {
  width: 100%;
  font-size: clamp(54px, 11vw, 92px);
}

/* L'ADRESSE DU PIED DE PAGE TIENT SUR UNE LIGNE (demande PO du 2026-08-28).
   Mesure a l'ecran, viewport 1060 px : « Face FECECAM, Aitchedji » se cassait en DEUX lignes dans
   118 px utiles. La cause est la grille du kit — `grid-template-columns:.85fr 1.15fr` — qui donne
   la plus petite part a la colonne de l'adresse et la plus grande a celle des horaires.
   Ces deux textes viennent desormais des REGLAGES (ADR-0098) : leurs longueurs changent avec la
   boutique, et une proportion figee ne peut pas les suivre. La colonne de gauche ne descend donc
   plus sous la largeur de son contenu ; au-dela, elle garde la proportion voulue par le design.
   AU-DESSUS DE 520 px SEULEMENT : en dessous, le kit passe a une colonne unique, et c'est bien. */
@media (width > 520px) {
  .store-visit-details {
    grid-template-columns: minmax(max-content, 0.85fr) minmax(0, 1.15fr);
  }
}

/* LES DEUX BLOCS DU PIED DE PAGE COMMENCENT A LA MEME HAUTEUR (demande PO du 2026-08-28).
   Le kit les centre verticalement (`align-items:center`). Tant que les deux textes tenaient sur le
   meme nombre de lignes, cela se voyait a peine ; depuis qu'ils viennent des REGLAGES (ADR-0098),
   leurs longueurs different — mesure a l'ecran : icones a la meme hauteur, mais titres decales de
   7 px, l'un a 215 px et l'autre a 208. On aligne donc en haut : l'icone, le titre et le detail de
   chaque bloc partent de la meme ligne, quelle que soit la longueur des textes. */
.store-visit-details > div {
  align-items: flex-start;
}
* local.css — RÈGLES MAISON DE LA VITRINE.
 *
 * Chargé APRÈS `hisy-v3.css`, jamais avant. C'est le seul endroit où corriger le rendu du
 * design : `public/css/hisy-v3.css` est sous test de conformité octet-par-octet
 * (`DesignSystemConformityTest`), et l'ADR-0003 §2 impose que la règle maison vive ici.
 *
 * Chaque règle porte la raison de son existence. Une règle sans raison écrite est une règle que
 * personne n'osera retirer.
 */

/* ── 1. Les cartes produit restaient invisibles ──────────────────────────────────────────────
 *
 * `hisy-v3.css` déclare `.reveal{animation:.65s both reveal}` avec une seule image-clé :
 *
 *     @keyframes reveal { 0% { opacity:0; transform:translateY(18px) } }
 *
 * Il manque le `to`. Avec `animation-fill-mode: both`, l'animation se termine — son
 * `playState` passe bien à « finished » — mais n'a aucun état final vers lequel aller : elle
 * fige l'élément sur la seule étape existante, donc `opacity: 0` pour toujours. Et une
 * animation l'emporte sur une déclaration normale, si bien que le `.reveal{opacity:1}` du
 * bundle ne peut rien rattraper.
 *
 * Effet constaté en ligne le 2026-08-15 : 13 cartes sur 13 invisibles sur la boutique,
 * 19 sur 19 sur l'accueil. Le HTML était correct, le CSS chargé, les données servies — et la
 * vitrine ne montrait aucun produit.
 *
 * Redéclarer `@keyframes` sous le même nom REMPLACE la définition précédente : on rend son
 * état final à l'animation sans toucher au fichier du design. */
@keyframes reveal {
    from { opacity: 0; transform: translateY(18px); }
    to   { opacity: 1; transform: none; }
}

/* Le rail des arrivages désactive l'animation (`.arrival-rail .reveal{animation:none}`) et
 * s'en remet à un repli `.reveal.in{opacity:1}` — mais aucun script du site n'ajoute jamais la
 * classe `in` : il n'y a pas un seul `IntersectionObserver` dans les six fichiers JS servis.
 * Le repli ne se déclenche donc jamais. On rend la visibilité inconditionnelle là où le design
 * a lui-même renoncé à l'animer. */
.arrival-rail .reveal { opacity: 1; transform: none; }

/* ── 2. Les titres sur fond sombre étaient illisibles ────────────────────────────────────────
 *
 * Le bundle applique `h1,h2,h3,h4,h5,h6 { color: var(--color-text-heading) }` — soit #2d1505,
 * le brun foncé. Cette règle porte sur l'élément (spécificité 0,0,1) alors que le design, lui,
 * fait HÉRITER la couleur claire de ses sections sombres. Or l'héritage perd toujours face à
 * une déclaration directe, quel que soit l'ordre des feuilles.
 *
 * Résultat : le titre du héros s'affichait en #2d1505 sur un fond #2d1505 — un contraste de
 * 1,00:1, c'est-à-dire rien. Douze titres touchés sur le seul accueil (héros, cartes de
 * collection, lookbook, bloc « Approvisionnement / Contrôle / Transparence », pied).
 *
 * On ne surcharge PAS `h1..h6`. On redéfinit le jeton que cette règle consulte, sur les blocs
 * dont le fond est sombre : pas de `!important`, pas de conflit de spécificité, et les titres
 * des sections claires ne bougent pas d'un pixel.
 *
 * Un premier correctif écrivait `h1..h6{color:inherit}`. Il ne tenait que sur les blocs qui
 * posent eux-mêmes une couleur claire — `.collection-card`, `.store-facts-list`… — et laissait
 * illisibles ceux qui n'en posent aucune et héritent du corps, donc du brun foncé :
 * `.hero-home`, `.lookbook-feature`, `.lookbooks-card`. D'où des titres encore invisibles après
 * la première correction. Audit du PO sur les 14 pages : la page lookbook à elle seule en
 * portait treize.
 *
 * Tous les blocs ci-dessous ont pour fond `var(--brown)` — vérifié un par un dans la feuille du
 * design, pas déduit du nom. */
.hero-home, .collection-card, .lookbook-feature, .store-facts-list,
.lookbooks-card, .about-v2-promise, .about-v2-local, .legal-hero,
.store-visit,
/* Ajoutés le 2026-08-18, signalés par le PO puis MESURÉS sur les seize pages publiques : ce sont
 * les deux SEULS titres restants sous le seuil de contraste, tous deux sur /faq —
 * « Une question ? Commencez ici. » et « Parlez-nous de la pièce ou de la réservation », à
 * 1,00:1, c'est-à-dire brun #2d1505 sur brun #2d1505.
 *
 * Le balayage a aussi confirmé, chiffres à l'appui, l'exclusion notée plus haut : le fond de
 * `.footer-newsletter` est un DÉGRADÉ clair, son titre foncé est correct, et l'ajouter ici aurait
 * créé le défaut exactement inverse. Une sonde qui ignore les dégradés le signale à tort — c'est
 * ce qui est arrivé au premier passage. */
.faq-v3-hero, .faq-v3-contact { --color-text-heading: var(--cream); }

/* ── 3. « Confirmer la réservation » était brun sur brun, donc invisible ─────────────────────
 *
 * Sur `/panier`, le bouton qui déclenche la réservation s'affichait comme une pastille brune
 * VIDE : couleur du texte `#2d1505` sur fond `#2d1505`, contraste 1,00:1. C'est le dernier
 * geste avant la réservation, et il n'y avait rien à lire dessus.
 *
 * La cause n'est ni la spécificité ni l'ordre des feuilles, mais les COUCHES DE CASCADE. Notre
 * bundle range ses utilitaires dans `@layer utilities` ; `hisy-v3.css` déclare, LUI, `button` et
 * `a` HORS COUCHE :
 *
 *     button { color: inherit }
 *     a      { color: inherit; text-decoration: none }
 *
 * Une règle sans couche l'emporte sur toute règle en couche, quelle que soit sa spécificité —
 * `button` (0,0,1) bat donc `.text-text-on-dark` (0,1,0). Tout `<a>` et tout `<button>` des
 * pages restées au chrome Tailwind perd sa couleur de texte et hérite de celle du corps.
 *
 * On ne surcharge pas `button` : on redéclare, hors couche elle aussi, la poignée d'utilitaires
 * que ces pages posent réellement sur un lien ou un bouton. Portée volontairement étroite —
 * ailleurs, `color: inherit` est ce que le design veut.
 *
 * Audit de contraste des 17 pages publiques, le 2026-08-16 : c'était le seul texte réellement
 * illisible du site. Les pages du design, elles, sont propres.
 *
 * Cette règle disparaît le jour où `/panier` passe au chrome du design. */
a.text-text-on-dark, button.text-text-on-dark { color: var(--color-text-on-dark); }
a.text-brown,        button.text-brown        { color: var(--color-brown); }
a.text-text-muted,   button.text-text-muted   { color: var(--color-text-muted); }

/* Les variantes aussi : `hover:text-danger` compile en `.hover\:text-danger:hover`, qui vit dans
 * la même couche et perd donc de la même façon. Sur le panier, le survol de « Retirer » ne
 * changeait pas de couleur — le même défaut, en moins visible. */
a.hover\:text-danger:hover, button.hover\:text-danger:hover { color: var(--color-danger); }

/* DEUX EXCEPTIONS, à ne pas perdre : leur fond est CLAIR et leur titre doit rester foncé.
 * Les traiter comme les autres créerait le défaut exactement inverse.
 *
 *   · `.caramel-card` — fond `var(--caramel)` (#c8956a), texte `var(--brown)` ;
 *   · `.footer-newsletter` — fond `linear-gradient(var(--cream) → #ead2b0)`, texte
 *     `var(--brown)`. Il figurait dans la liste auditée ; le relevé de son fond montre qu'il
 *     n'a jamais été en défaut. Il n'est donc pas touché — c'est un écart assumé à cette liste,
 *     et la raison est ici pour qu'on puisse le contredire en connaissance de cause.
 *
 * `.caramel-card` est déclaré APRÈS `.collection-card` : même spécificité, la cascade tranche. */
.caramel-card { --color-text-heading: var(--brown); }

/* ── 4. Deux avis côte à côte sur un téléphone ───────────────────────────────────────────────
 *
 * Le design VEUT une colonne sur mobile. Sa règle est simplement MORTE. `hisy-v3.css` déclare,
 * dans cet ordre :
 *
 *     .reviews-page-grid-v3 { grid-template-columns: 1fr 1fr }                  (base)
 *     @media (width <= 820px) { .reviews-page-grid-v3 { grid-template-columns: 1fr } }
 *     .reviews-page-grid-v3 { grid-template-columns: minmax(0,1fr) minmax(0,1fr) }  (base, PLUS LOIN)
 *
 * Une requête média n'ajoute aucune spécificité. La troisième règle, sans condition et placée
 * APRÈS, l'emporte donc à TOUTES les largeurs : la règle responsive du design ne s'applique
 * jamais, et un téléphone reçoit deux colonnes. Ce n'est pas un choix du design qu'on corrige,
 * c'est une déclaration en double qui annule le sien.
 *
 * Le défaut est resté invisible jusqu'au 2026-08-16 parce que la page ne servait AUCUN avis :
 * la grille n'avait jamais eu deux cartes à placer.
 *
 * On rétablit donc son seuil à lui — 820 px — plutôt que d'en inventer un.
 * Décision PO du 2026-08-16 : un avis par ligne sur mobile. */
@media (max-width: 820px) {
  .reviews-page-grid-v3 { grid-template-columns: 1fr; }
}

/* Une colonne ne suffisait pas. Le titre du bloc porte un `width:401px` EN DUR, posé par
 * `avis.js` et repris du prototype statique : cette largeur devient celle du conteneur, donc
 * celle de la grille, donc celle des cartes. Mesuré sur un écran de 375 px : la carte allait de
 * 18 px à 419 px, et la citation se terminait 15 px HORS de l'écran — le texte de l'avis était
 * coupé à droite, sans même une barre de défilement pour le signaler.
 *
 * Le débordement n'existe qu'en dessous de ~437 px (401 px + les deux gouttières de 18) : la
 * règle s'arrête donc à 480 px, et la largeur voulue par le design est respectée partout
 * ailleurs. `!important` est le seul recours — rien d'autre ne l'emporte sur un attribut
 * `style`. */
@media (max-width: 480px) {
  .published-reviews-v3 h2[style] { width: auto !important; }
  .published-reviews-v3, .reviews-page-grid-v3 { max-width: 100%; }
}

/* ── Texte enrichi de la fiche pièce (ADR-0019) ───────────────────────────────────────────────
 * La description éditoriale contient désormais de l'HTML assaini. Le design ne stylait que
 * `.pdp-block p`, avec `margin:0` — deux paragraphes se seraient collés — et ne connaissait ni
 * liste, ni sous-titre, ni citation : son texte d'exemple était d'un seul tenant.
 * Ces règles vivent ICI parce que `hisy-v3.css` est sous conformité octet par octet (ADR-0003). */
/* Deux classes, et c'est délibéré : `.pdp-rich > * + *` ne pèse que 0-1-0 — l'étoile ne compte pas
 * — quand `.pdp-block p { margin: 0 }` du design pèse 0-1-1. La règle existait et ne gagnait pas ;
 * les paragraphes restaient collés. Mesuré à l'écran, pas relu. */
.pdp-block .pdp-rich > * + * { margin-top: .9em; }
.pdp-rich ul,
.pdp-rich ol { margin: 0; padding-left: 1.25rem; max-width: 82ch; font-size: 14px; line-height: 1.75; color: var(--body); }
.pdp-rich ul { list-style: disc; }
.pdp-rich ol { list-style: decimal; }
.pdp-rich li + li { margin-top: .35em; }
.pdp-rich h3,
.pdp-rich h4 { margin: 0; font-family: "Fraunces Variable", Georgia, serif; font-weight: 530; letter-spacing: -.03em; line-height: 1.2; color: var(--brown); }
.pdp-rich h3 { font-size: 17px; }
.pdp-rich h4 { font-size: 15px; }
.pdp-rich blockquote { margin: 0; padding-left: 14px; border-left: 2px solid var(--line); max-width: 82ch; font-size: 14px; line-height: 1.75; color: var(--muted); }
.pdp-rich a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }

/* ── Bandeau d'aperçu du staff (décision PO du 2026-08-17) ────────────────────────────────────
 * L'aperçu admin d'une pièce sert la fiche RÉELLE de la vitrine, brouillon compris. Ce bandeau
 * est la seule chose qui l'en distingue : il doit donc être immédiatement lisible comme n'étant
 * PAS la boutique. D'où le brun plein, hors de la palette des surfaces de la vitrine.
 * Jamais rendu pour le public : la coquille ne le pose que si `apercuStaff` est fourni. */
/* NON COLLANT, délibérément. L'en-tête de la vitrine l'est déjà : deux barres collantes se
 * chevauchaient et le bouton « Retour » passait sous le logo. Le staff lit le bandeau en haut,
 * puis descend dans la fiche telle que la cliente la verra — ce qui est le but. */
.staff-preview-bar {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 10px 18px;
  padding: 9px 20px;
  background: var(--brown); color: var(--cream);
  font-size: 13px; line-height: 1.45;
  box-shadow: 0 2px 10px rgb(0 0 0 / .18);
}
.staff-preview-bar strong { display: block; font-weight: 650; letter-spacing: .01em; }
.staff-preview-bar span { opacity: .82; font-size: 12px; }
.staff-preview-bar a {
  flex: none; padding: 6px 13px; border-radius: 8px;
  background: var(--caramel); color: var(--brown);
  font-weight: 620; text-decoration: none;
}
.staff-preview-bar a:hover { filter: brightness(1.06); }

/* ── Carte de scène « Dans nos murs » enrichie (décision PO du 2026-08-18) ────────────────────
 * ÉCART ASSUMÉ AU DESIGN DE LA VITRINE. Sa carte ne porte qu'une image et une `<figcaption>`.
 * L'écran d'admin, lui, fait saisir un SUR-TITRE, un TITRE et une DESCRIPTION — trois colonnes
 * réelles de `in_store_gallery_items`, qui n'étaient rendues nulle part. Un champ qu'on fait
 * remplir sans jamais l'afficher est la même faute qu'un champ affiché sans le remplir.
 *
 * La règle du design sur `figcaption` est CONSERVÉE telle quelle — position, couleur, serif,
 * taille fluide : c'est elle qui porte le titre. On ne fait qu'empiler autour, et chaque partie
 * disparaît quand elle est vide (le JS ne pose pas l'élément). Ici et jamais dans `hisy-v3.css`,
 * sous conformité octet par octet (ADR-0003 §2). */
.store-life-card figcaption { display: flex; flex-direction: column; gap: 6px; }

.store-life-kicker {
  font-family: "Inter Variable", system-ui, sans-serif;
  font-size: 11px; font-weight: 700; letter-spacing: .11em; text-transform: uppercase;
  color: var(--caramel, #c8956a);
}
/* Le titre n'a AUCUNE règle propre : il hérite de `figcaption`, donc du design. Lui en donner
 * une le ferait diverger le jour où le design change sa typographie. */
.store-life-desc {
  font-family: "Inter Variable", system-ui, sans-serif;
  font-size: 13px; font-weight: 400; line-height: 1.5;
  color: var(--cream, #f7f1e8); opacity: .82;
  max-width: 34ch;
}
@media (max-width: 640px) {
  .store-life-kicker { font-size: 10px; }
  .store-life-desc { font-size: 12px; max-width: none; }
}

/* ────────────────────────────────────────────────────────────────────────────────────
   REPÈRES DU LOOKBOOK : un calque calé sur la PHOTO, pas sur le cadre (2026-08-18)
   ────────────────────────────────────────────────────────────────────────────────────
   `hisy-v3.css` rend l'image du dialogue en `object-fit: contain` : elle n'occupe donc
   qu'une partie de la scène, le reste étant la version floutée en fond. Les repères,
   eux, étaient positionnés en pourcentage de LA SCÈNE — un point à 70 % tombait donc
   bien plus bas que 70 % de la photo. Signalé par le PO : un repère posé sur un pantalon
   s'affichait sur le sol.
   `lookbook.js` mesure la boîte réellement occupée par l'image (ratio natif fourni par le
   navigateur) et pose ces quatre valeurs en ligne. Cette règle ne fait que préparer le
   calque : positionné, transparent, et laissant passer les clics partout sauf sur les
   repères eux-mêmes — sans quoi il masquerait la photo au clic. */
.lookbook-hotspot-frame {
  position: absolute;
  inset: 0;
  pointer-events: none;
}
.lookbook-hotspot-frame > .lookbook-hotspot {
  pointer-events: auto;
}

/* L'IMAGE REMPLIT SON CADRE (décision PO du 2026-08-18).
   `hisy-v3.css` la rend en `object-fit: contain` : les photos de look étant en PAYSAGE
   (1536×1024 pour l'illustration du design) dans un cadre portrait 3:4, elles flottaient
   entre deux bandes floues et n'occupaient qu'une fraction de la place. Le cadre RESTE en
   3:4 — c'est le format retenu par le PO, et celui du design d'origine — mais l'image le
   REMPLIT désormais : elle est recadrée sur les côtés au lieu d'être encadrée de vide.
   La gérante compose dans ce même recadrage, donc ce qu'elle voit est ce qui part.
   La feuille du design est verrouillée octet par octet (ADR-0003) : la surcharge vit ici. */
.lookbook-dialog-media-stage > img { object-fit: cover; }

/* ────────────────────────────────────────────────────────────────────────────────────
   REPÈRES DU LOOKBOOK — plus discrets, sans perdre en accessibilité (2026-08-18)
   ────────────────────────────────────────────────────────────────────────────────────
   Demande PO : « diminuer la taille des puces sur vitrine ». Le design pose une pastille
   de 39 px dans une zone de 56, halo compris jusqu'à ~76 px. Sur une scène de 465 px de
   haut, un repère masquait la pièce qu'il désigne — et trois repères en masquaient trois.

   LA RÈGLE QUI GOUVERNE CE BLOC : on rétrécit ce qui SE VOIT, jamais ce qui SE CLIQUE.
   La zone cible reste à 44 px, le minimum AAA du critère WCAG 2.5.5 (Target Size). Une
   pastille de 26 px cliquable sur 26 px serait plus jolie et inutilisable au pouce.

   Le halo suit le même raisonnement : discret au repos, franc au survol et au focus. Un
   halo permanent et large est une décoration ; un halo qui répond est une affordance. */

/* Zone cliquable : 56 → 44 px. Invisible, et c'est le but. */
.lookbook-hotspot { width: 44px; height: 44px; }

/* Pastille visible : 39 → 26 px. La bordure passe de 3 à 2 px pour que le chiffre
   respire à cette taille, et la police suit — 17 → 12 px reste lisible en Fraunces. */
.lookbook-hotspot-number {
  width: 26px;
  height: 26px;
  font-size: 12px;
  border-width: 2px;
  box-shadow: 0 4px 12px rgba(45, 21, 5, .34);
}

/* Halo au repos : resserré (−10 → −4 px) et allégé. Il signale, il n'encombre pas. */
.lookbook-hotspot-halo {
  inset: -4px;
  opacity: .55;
  box-shadow: 0 0 0 4px rgba(45, 21, 5, .08);
}

/* Au survol ET au focus clavier : le halo s'ouvre franchement. L'écart doit être plus
   marqué qu'avant, puisqu'on part de plus petit. */
.lookbook-hotspot:hover .lookbook-hotspot-halo,
.lookbook-hotspot:focus-visible .lookbook-hotspot-halo,
.lookbook-hotspot.is-active .lookbook-hotspot-halo {
  opacity: 1;
  transform: scale(1.22);
}

/* FOCUS CLAVIER : le design n'en pose AUCUN sur ce bouton. Sans fond ni bordure, l'anneau
   par défaut du navigateur est invisible sur une photo sombre — le repère devenait
   inatteignable au clavier sans qu'on sache où l'on est. */
.lookbook-hotspot:focus-visible {
  outline: none;
}
.lookbook-hotspot:focus-visible .lookbook-hotspot-number {
  box-shadow: 0 0 0 3px var(--cream, #f5ecd7), 0 0 0 6px rgba(45, 21, 5, .55);
}

/* Repère actif : l'anneau se resserre avec la pastille (4 → 3 px). */
.lookbook-hotspot.is-active .lookbook-hotspot-number {
  box-shadow: 0 0 0 3px rgba(200, 149, 106, .72), 0 5px 16px rgba(45, 21, 5, .38);
}

/* Pièce vendue : même resserrement, halo tramé conservé. */
.lookbook-hotspot.is-sold .lookbook-hotspot-halo { inset: -7px; }

/* Étiquette « Vendu » : elle suivait une pastille de 39 px, elle en suit une de 26. Et
   7 px n'est pas une taille de texte — 9 px avec sa graisse et son interlettrage reste
   sobre tout en étant lisible. */
.lookbook-hotspot-status {
  top: 34px;
  font-size: 9px;
  padding: 4px 7px;
}

/* Le halo s'anime : on le tait pour qui a demandé moins de mouvement. */
@media (prefers-reduced-motion: reduce) {
  .lookbook-hotspot-halo { transition: none; }
  .lookbook-hotspot:hover .lookbook-hotspot-halo,
  .lookbook-hotspot:focus-visible .lookbook-hotspot-halo,
  .lookbook-hotspot.is-active .lookbook-hotspot-halo { transform: none; }
}

/* ── L'état « Aucun avis publié » se peignait dans la colonne du panneau de note ───────────────
 *
 * Constaté à l'écran par le PO le 2026-08-19 : le titre « Aucun avis publié pour l'instant »
 * tombait à UN MOT PAR LIGNE, le paragraphe était comprimé dans une colonne de 68 px, et le
 * libellé du bouton se cassait sur trois lignes. Mesuré sur l'aperçu, à 1440 px de large.
 *
 * DEUX RÈGLES DU DESIGN SE PERCUTENT, et il faut les deux pour expliquer le rendu.
 *
 * 1. LA COLONNE. La section est une grille à deux pistes :
 *
 *        .reviews-service-content { grid-template-columns: 320px minmax(0,1fr) }
 *        @media … { .reviews-service-content { grid-template-columns: 280px minmax(0,1fr) } }
 *
 *    La première piste est celle du panneau de note (`aside.rating-panel-v3`), la seconde celle
 *    de la liste. Or `avis.js` MASQUE le panneau tant qu'aucun avis n'est publié
 *    (`panel.hidden = true`, public/js/avis.js). Un élément `hidden` quitte le flux de la
 *    grille : la liste est alors placée automatiquement dans la PREMIÈRE piste — celle qui fait
 *    320 px et qui était taillée pour le panneau — pendant que la piste de 810 px reste vide.
 *    Mesuré : `.published-reviews-v3` large de 320 px sur un écran de 1440.
 *
 * 2. LA GRILLE INTERNE. Le design déclare
 *
 *        .published-reviews-v3 > div:first-child { grid-template-columns: .88fr 1.12fr;
 *                                                  align-items: end; gap: 50px; display: grid }
 *
 *    Cette règle vise l'EN-TÊTE du cas où des avis existent (« Ce que disent nos clients. » et
 *    son paragraphe, côte à côte). Mais dans l'état vide, le premier enfant est le panneau vide
 *    LUI-MÊME : il hérite donc des deux colonnes et de `align-items: end`, qui contredisent son
 *    propre bloc `.reviews-empty-v3 { display: grid; justify-items: center; gap: 14px }`.
 *    Mesuré à 1440 px : deux pistes de 151 px et 68,5 px, un h2 haut de 210 px.
 *
 * LE DÉFAUT EST CELUI DU DESIGN, pas de notre intégration : la feuille du kit
 * (docs/reference/design-system/source/v3/v3-vitrine/site/assets/css/hisy-v3.css:197-200) porte
 * les mêmes règles, et son propre `avis.js` injecte le même panneau au même endroit. Le kit
 * n'avait simplement jamais eu à peindre cet état. On ne touche donc pas `hisy-v3.css`
 * (conformité octet-par-octet, ADR-0003 §2) : on rend au panneau la mise en page que le design
 * lui décrit ailleurs, sans rien inventer.
 *
 * En dessous du seuil où la section passe à une colonne, rien de tout cela ne se produit :
 * mesuré à 375 px, le panneau est déjà correct (339 px de large, une colonne). Les deux règles
 * ci-dessous ne changent donc que les largeurs où le défaut existe. */

/* La liste prend la rangée entière quand le panneau de note est masqué. Spécificité 0-3-0
 * (.rating-panel-v3 + [hidden] + .published-reviews-v3) ; rien d'autre ne pose `grid-column`
 * sur cet élément. Le sélecteur dit exactement le fait : PAS de panneau, PAS de colonne pour
 * lui. Quand le panneau est visible, il ne s'applique pas — la mise en page du design est
 * intacte. */
.rating-panel-v3[hidden] + .published-reviews-v3 { grid-column: 1 / -1; }

/* Le panneau vide retrouve SA grille — celle de son propre bloc dans le design : une colonne
 * (`grid-template-columns` non déclaré, donc `none`), pas d'alignement de fin (`align-items`
 * non déclaré, donc `normal`), et l'espacement de 14 px qu'il déclare.
 * Spécificité 0-3-0 contre 0-2-1 pour la règle du design (`.published-reviews-v3 > div:first-child`) :
 * elle l'emporte sur la spécificité, sans avoir besoin de `!important`. `justify-items: center`
 * n'est pas repris : le design le pose déjà et personne ne le contredit. */
.published-reviews-v3 > .reviews-empty-v3:first-child {
  grid-template-columns: none;
  align-items: normal;
  gap: 14px;
}

/* ── Bandeau promo : icône et fermeture (arbitrage PO du 2026-08-24) ─────────────────────────────
 * L'écran d'administration propose une icône et une case « masquable par le client » ; son aperçu
 * montre même la croix. Le design de la vitrine ne dessine ni l'une ni l'autre — ces deux éléments
 * sont donc habillés ici, dans la maison, sans toucher `hisy-v3.css` (ADR-0003 §2).
 * Les couleurs, elles, viennent de la campagne et sont posées en style en ligne : elles changent
 * d'une promotion à l'autre et n'ont pas de place dans une feuille figée. */
.announcement { position: relative; }

.announcement-icon {
  flex: 0 0 auto;
  margin-right: 8px;
  vertical-align: -3px;
}

.announcement-close {
  position: absolute;
  right: 10px;
  top: 50%;
  transform: translateY(-50%);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  padding: 0;
  border: none;
  border-radius: 9999px;
  background: transparent;
  color: inherit;
  opacity: .65;
  cursor: pointer;
}

.announcement-close:hover,
.announcement-close:focus-visible { opacity: 1; background: rgba(0, 0, 0, .08); }

/* Sous 640 px le texte occupe toute la largeur : la croix ne doit pas le recouvrir. */
@media (max-width: 640px) {
  .announcement-center { padding-right: 34px; }
}

/* ── Popup de bienvenue (arbitrage PO du 2026-08-24) ─────────────────────────────────────────────
 * Le design de la vitrine ne prévoit pas de popup ; l'écran d'administration, si — il en montre
 * même l'aperçu. L'habillage vit donc ici, dans la maison, avec les jetons du design pour que la
 * boîte reste de la même famille que le reste du site. */
.hisy-welcome {
  position: fixed;
  inset: 0;
  z-index: 120;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  background: rgba(45, 21, 5, .55);
}

.hisy-welcome[hidden] { display: none; }

.hisy-welcome-card {
  position: relative;
  width: 100%;
  max-width: 400px;
  padding: 32px 26px 26px;
  border-radius: 20px;
  background: #FDF8F2;
  text-align: center;
  box-shadow: 0 30px 80px rgba(45, 21, 5, .4);
}

.hisy-welcome-card h2 {
  margin: 0;
  font-family: var(--font-display, "Fraunces Variable", serif);
  font-size: 1.5rem;
  font-weight: 800;
  line-height: 1.2;
  color: #2D1505;
}

.hisy-welcome-card p { margin: 10px 0 16px; font-size: .9rem; color: #7A6A57; }

.hisy-welcome-code {
  margin-bottom: 16px;
  padding: 10px;
  border-radius: 10px;
  background: #F5ECD7;
  color: #A8734E;
  font-family: ui-monospace, monospace;
  font-weight: 800;
  letter-spacing: .06em;
}

.hisy-welcome-close {
  position: absolute;
  right: 12px;
  top: 12px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  padding: 0;
  border: none;
  border-radius: 9px;
  background: #F5ECD7;
  color: #2D1505;
  cursor: pointer;
}

/* Bandeau promo en mode IMAGE : le visuel occupe TOUTE la largeur, en HAUTEUR AUTOMATIQUE
 * (demande PO du 2026-08-24 : « laisser le bandeau texte ou image prendre toute la largeur et une
 * hauteur auto pour ne pas déformer l'image »).
 *
 * Une hauteur imposée obligeait à recadrer (`object-fit: cover`) : sur un bandeau de 8:1, le
 * recadrage coupait le message. La hauteur suit donc le rapport de l'image — c'est la boutique qui
 * choisit le format de son bandeau, pas la feuille de style. */
.announcement.announcement-visuel {
  padding: 0;
  display: block;
  /* LA BANDE DE COULEUR SOUS L'IMAGE VENAIT DE LA : le lien du design est `inline-flex`, donc une
   * boite de ligne — et une ligne reserve la place des jambages sous l'image. Six pixels de fond
   * qui se lisaient comme un second bandeau vide. Mettre l'interligne a zero les supprime. */
  line-height: 0;
  font-size: 0;
  min-height: 0;
}

/* Specificite 0-2-0 : la regle du design sur `.announcement a` est en 0-1-1 et l'emportait,
 * laissant le lien en `inline-flex`. */
.announcement.announcement-visuel > .announcement-visuel-lien { display: block; line-height: 0; }

.announcement-visuel-lien img {
  display: block;
  width: 100%;
  height: auto;
}

/* Visuel du popup de bienvenue, en 4:3 (demande PO du 2026-08-24). Le rapport est imposé par le
 * cadre et l'image recadrée au centre : sans cela, une image d'un autre format étirerait la boîte
 * au lieu de s'y ranger. */
/* MODE IMAGE : le visuel EST le popup — pas une illustration posée au-dessus d'un texte
 * (demande PO du 2026-08-25, même dualité que le bandeau). La carte n'a donc plus de marge
 * intérieure : elle est le cadre de l'image. */
.hisy-welcome-card.hisy-welcome-illustre { padding: 0; overflow: hidden; }

.hisy-welcome-visuel {
  display: block;
  aspect-ratio: 4 / 3;
  background: #F5ECD7;
  line-height: 0;
}

.hisy-welcome-visuel img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* ── Le bandeau d'annonce FIXE s'efface devant le bandeau de campagne ────────────────────────────
 * (demande PO du 2026-08-24 : « cacher ceci quand un bandeau est actif »).
 *
 * Le design pose en haut de page une bande d'annonces permanentes — « Pièces uniques · Nouveautés
 * chaque semaine · Retrait à Aïtchedji ». Quand une campagne est publiée, deux bandes se succèdent
 * et disent deux choses à la fois : la promotion perd la place qu'on lui a donnée.
 *
 * Écrit en CSS et non dans le gabarit : le header vient du kit du design et n'est pas retouché
 * (ADR-0003 §2). Le sélecteur de voisinage suffit — le bandeau de campagne précède la bande fixe
 * dans la coquille, et n'existe que si une campagne est en cours. */
.announcement-promo ~ .announcement { display: none; }

/* La croix reste lisible SUR une image : sans fond, elle se perdait dans un visuel clair. */
.announcement-visuel .announcement-close {
  background: rgba(45, 21, 5, .55);
  color: #FDF8F2;
  opacity: 1;
}

.announcement-visuel .announcement-close:hover { background: rgba(45, 21, 5, .8); }

/* Pastille « Vente flash » de la fiche pièce : le design pose `pdp-badge-new`, `-unique` et
 * `-deal`, pas de variante flash — elle reprend donc les jetons de `-deal`, en caramel, pour
 * rester de la même famille. (Demande PO du 2026-08-24.) */
.pdp-badge.pdp-badge-flash {
  background: var(--caramel, #C8956A);
  color: var(--brown, #2D1505);
  font-variant-numeric: tabular-nums;
}

/* Chiffres à chasse fixe : sans cela le décompte fait sautiller la pastille à chaque seconde. */
.product-flash[data-flash-fin] { font-variant-numeric: tabular-nums; }

/* ── Le bandeau de campagne, au modèle de la barre qu'il remplace ────────────────────────────────
 * (arbitrage PO du 2026-08-24 : « il doit juste servir de modèle pour un bandeau de texte ».)
 * Deux barres annonçaient la même promotion, chacune avec sa source. Il n'en reste qu'une : celle
 * du design, alimentée par la campagne publiée, avec l'étiquette et l'appel à l'action de l'autre. */
/* LA BANDE DU DESIGN EST UNE GRILLE `1fr auto 1fr` — trois colonnes pour ses trois annonces
 * permanentes. Notre bandeau n'a qu'un enfant : il atterrissait donc dans la PREMIÈRE colonne,
 * c'est-à-dire un tiers de la largeur, d'où un titre qui passait à la ligne pendant que les deux
 * autres tiers restaient vides (constat PO du 2026-08-25).
 * En flex, l'unique lien prend toute la largeur du conteneur du design, qui garde sa marge. */
.announcement.announcement-promo { display: flex; }

.announcement-barre {
  display: flex;
  align-items: center;
  gap: 14px;
  flex: 1 1 auto;
  min-width: 0;
  justify-content: flex-start;
  /* La croix de fermeture est posée à droite, en absolu : sans cette réserve, l'appel à
   * l'action passerait dessous. */
  padding-right: 30px;
}

/* L'appel à l'action au bout, le titre au début : la lecture va du quoi vers le où. */
.announcement-promo .announcement-cta { margin-left: auto; }

.announcement-tag {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex: 0 0 auto;
  padding: 4px 11px;
  border-radius: 9999px;
  background: var(--caramel, #C8956A);
  color: var(--brown, #2D1505);
  font-size: .64rem;
  font-weight: 800;
  letter-spacing: .08em;
  text-transform: uppercase;
}

/* Le titre s'allonge et se tronque plutôt que de passer à la ligne : une bande d'annonce qui
 * grandit d'une ligne pousse toute la page vers le bas à chaque changement de campagne. */
.announcement-titre {
  flex: 0 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-weight: 700;
  letter-spacing: .02em;
}

/* Le détail cède la place en premier : c'est le seul élément dont l'absence ne coûte rien. */
.announcement-detail { opacity: .72; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.announcement-cta { flex: 0 0 auto; display: inline-flex; align-items: center; gap: 6px; font-weight: 700; }

@media (max-width: 900px) { .announcement-detail { display: none; } }

@media (max-width: 640px) {
  .announcement-barre { gap: 8px; justify-content: flex-start; }
  .announcement-cta { display: none; }
}

/* ── Bande lookbook : hauteur fixe et visuel en fond ─────────────────────────────────────────────
 * (demande PO du 2026-08-25 : « mettre en cover sur l'arrière afin de fixer la hauteur à 720 px ».)
 *
 * Le design pose `min-height: 620px` : la bande grandissait avec le texte à sa droite, et changeait
 * de hauteur d'un lookbook à l'autre. Une hauteur fixe donne un bloc stable, et le visuel du
 * lookbook — c'est le même média que la page Lookbook — le remplit en `cover`. */
.lookbook-image {
  height: 720px;
  min-height: 0;
}

/* Sous 900 px la colonne passe sous le texte : 720 px y feraient un mur à faire défiler. */
@media (max-width: 900px) {
  .lookbook-image { height: 420px; }
}

/* ── Liens des promotions en cours, dans la boutique ─────────────────────────────────────────────
 * (demande PO du 2026-08-25 : « il faudra un lien pour chaque promotion ».)
 *
 * Le filtre « Bon plan » de la barre rassemble TOUT ce qui a subi une réduction. Ces liens montrent
 * UNE opération — « les pièces de la vente flash du week-end » — et se partagent tels quels. */
.promo-liens {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 12px;
}

.promo-lien {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 6px 12px;
  border: 1px solid var(--border, #E8D4B4);
  border-radius: 9999px;
  background: var(--cream-light, #FDF8F2);
  color: var(--brown, #2D1505);
  font-size: .78rem;
  font-weight: 700;
  text-decoration: none;
  line-height: 1.2;
}

.promo-lien:hover,
.promo-lien:focus-visible { border-color: var(--caramel, #C8956A); }

/* La promotion affichée se distingue : sans cela, on ne sait pas ce qu'on regarde. */
.promo-lien.is-active {
  background: var(--caramel, #C8956A);
  border-color: var(--caramel, #C8956A);
}

/* Le nombre de pièces, en retrait : c'est un repère, pas le sujet. */
.promo-lien small {
  font-size: .68rem;
  font-weight: 800;
  opacity: .6;
  font-variant-numeric: tabular-nums;
}

/* Les chiffres du décompte à chasse fixe, et l'horloge alignée sur le texte. */
.product-flash svg, .pdp-badge-flash svg { flex: 0 0 auto; vertical-align: -1px; }

/* ══════════════════════════════════════════════════════════════════════════════════════════
   LE NOM SUR LE FORMULAIRE NEWSLETTER (demande PO du 2026-08-26, ADR-0073 §4.1)
   ══════════════════════════════════════════════════════════════════════════════════════════
   Le formulaire du design est une grille a DEUX colonnes — `minmax(220px,1fr) auto` : le champ
   e-mail, puis le bouton. Un troisieme enfant atterrirait dans la colonne du bouton et
   l'ecraserait. Le nom prend donc sa propre ligne, comme le consentement et le message d'etat
   qui portent deja `grid-column: 1 / -1` dans la reference. La ligne e-mail + bouton du design
   n'est pas touchee. */
.newsletter-email-form .newsletter-email-name {
  grid-column: 1 / -1;
}

/* « (facultatif) » doit se lire comme une precision, pas comme une seconde etiquette : meme
   couleur que le libelle, poids normal, et sans majuscule imposee par le design. */
.newsletter-email-form .nl-optional {
  font-weight: 400;
  text-transform: none;
  opacity: .7;
}

/* ══════════════════════════════════════════════════════════════════════════════════════════
   « EN LOT », À LA SUITE DES DÉTAILS (demande PO du 2026-08-26, ADR-0074 §7)
   ══════════════════════════════════════════════════════════════════════════════════════════
   ELLE NE RESSEMBLE PAS À UNE PASTILLE DE REMISE, et c'est délibéré. « −30 % » annonce un prix
   déjà baissé, visible sur la ligne du dessus ; « lot 6/5 » annonce une offre À SAISIR, qui
   n'existera qu'au panier. Les confondre ferait chercher un prix barré qui ne viendra pas — et une
   cliente qui cherche un prix barré absent conclut à une erreur.

   ET ELLE TIENT DANS LA LIGNE. La phrase entière — « 6 articles au prix de 5 · Femme » — prenait
   trois lignes sur une grille déjà dense. Elle vit maintenant à la suite de « Taille L · Grade B »,
   dans sa forme courte ; le libellé complet est dans `title` et `aria-label`, donc au survol pour
   la souris et à la lecture pour un lecteur d'écran.

   SUR TACTILE, `title` NE S'OUVRE PAS. C'est assumé : la forme courte se suffit — « lot 6/5 » dit
   déjà l'offre, et le panier la nomme en entier dès qu'elle s'applique. */
.catalog-product-details .product-deal {
  color: var(--caramel-dark, #A8734E);
  font-weight: 700;
  cursor: help;
  /* Le pointillé dit qu'il y a quelque chose de plus à lire — sans l'encadrer comme une pastille. */
  text-decoration: underline dotted currentColor;
  text-underline-offset: 3px;
}


.drawer-deal-hint {
  margin: 6px 0 0;
  font-size: 11px;
  color: var(--caramel-dark, #A8734E);
}

/* ── Le bandeau de marques (ADR-0075 pour la règle, ADR-0076 §3 pour la forme) ─────────────────
 *
 * TRANSPOSITION FIDÈLE de `brand-marquee.css`, livré par le PO le 2026-08-26. Le design V3 est
 * maître (ADR-0002) : ma première version — pastilles encadrées, 42 s, 96 px de large — est
 * remplacée par la sienne, à l'identique.
 *
 * CE QUE LE DESIGN FAIT ET QUE LA MIENNE NE FAISAIT PAS : le logo SEUL, sans cadre, à 45 %
 * d'opacité. La marque s'efface, la boutique reste le sujet — et le survol la ramène en pleine
 * lumière. Une pile de pastilles encadrées, elle, se lit comme une barre de filtres.
 *
 * LA BOUCLE EST UNE ANIMATION CSS SUR UNE PISTE DUPLIQUÉE : elle n'occupe pas le fil principal,
 * elle continue si un script échoue, et elle s'arrête d'elle-même sous `prefers-reduced-motion`.
 * Le décalage est de -50 %, exactement la moitié de la piste — toute autre valeur produirait un
 * saut visible à chaque tour. */
.brand-marquee {
  border-block: 1px solid var(--line, #E3D8C3);
  background: var(--cream-light, #FBF7EE);
  padding: 30px 0;
  overflow: hidden;
}

/* Les extrémités s'estompent : sans cela les logos apparaissent et disparaissent net, et le
   défilement se lit comme un décrochage plutôt que comme une boucle. */
.brand-marquee-viewport {
  -webkit-mask-image: linear-gradient(90deg, #0000, #000 7%, #000 93%, #0000);
  mask-image: linear-gradient(90deg, #0000, #000 7%, #000 93%, #0000);
  overflow: hidden;
}

.brand-marquee-track {
  width: max-content;
  display: flex;
  align-items: center;
  animation: brand-marquee-scroll 70s linear infinite;
}

/* UNE PASTILLE QUI FUIT SOUS LE CURSEUR NE SE CLIQUE PAS. */
.brand-marquee:hover .brand-marquee-track,
.brand-marquee:focus-within .brand-marquee-track { animation-play-state: paused; }

.brand-marquee-item {
  flex: none;
  place-items: center;
  height: 42px;
  margin-right: 66px;
  display: grid;
  opacity: .45;
  transition: opacity .25s, transform .25s;
}

.brand-marquee-item img {
  width: auto;
  max-width: 132px;
  height: auto;
  max-height: 42px;
  object-fit: contain;
  display: block;
}

.brand-marquee-name {
  color: var(--brown, #2D1505);
  letter-spacing: .02em;
  white-space: nowrap;
  font-family: 'Fraunces Variable', Fraunces, Georgia, serif;
  font-size: 17px;
  font-weight: 640;
  line-height: 1;
  text-decoration: none;
}

.brand-marquee-item:hover,
.brand-marquee-item:focus-visible { opacity: 1; transform: scale(1.05); }

/* La marque filtrée reste en pleine lumière : sans cela, on ne sait pas ce qu'on regarde. */
.brand-marquee-item.is-active { opacity: 1; }

@keyframes brand-marquee-scroll {
  from { transform: translate3d(0, 0, 0); }
  to { transform: translate3d(-50%, 0, 0); }
}

@media (max-width: 720px) {
  .brand-marquee { padding: 22px 0; }
  .brand-marquee-item { height: 30px; max-width: 100px; margin-right: 42px; }
  .brand-marquee-item img { max-width: 100px; max-height: 30px; }
}

/* CEUX QUI DEMANDENT MOINS DE MOUVEMENT N'EN ONT PAS. Le bandeau reste lisible et défilable à la
   main : une bande figée vaut mieux qu'une bande qui donne le vertige. */
@media (prefers-reduced-motion: reduce) {
  .brand-marquee-track { animation: none; }
  .brand-marquee-viewport { overflow-x: auto; scrollbar-width: none; }
}

/* ── Le décompte de vente flash tient sur UNE ligne (demande PO du 2026-08-26) ─────────────────
 *
 * La pastille est posée dans une pile étroite, en haut à gauche de l'image. Sans consigne de
 * césure, « VENTE FLASH 39:06:11 » se coupait en deux lignes et l'heure passait sous le libellé :
 * on lisait un badge à deux étages sur une vignette de 200 px, et le compte à rebours — tout
 * l'objet de la pastille — devenait le second rôle.
 *
 * LES CHIFFRES À CHASSE FIXE, parce qu'ils changent chaque seconde : sans `tabular-nums`, la
 * pastille respire à chaque battement et l'œil suit un texte qui bouge. */
.product-flash,
.pdp-badge-flash {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  white-space: nowrap;
}

.product-flash [data-flash-txt],
.pdp-badge-flash [data-flash-txt] {
  font-variant-numeric: tabular-nums;
}

/* Silhouette dont aucune piece n'est plus en ligne (correctif du 2026-08-28). La liste des
   pieces laissait un vide sans phrase ; le design n'avait pas de cas pour cela, on l'ajoute ici
   plutot que dans la feuille de reference. */
.lookbook-products-empty {
  margin: 12px 0 0;
  color: rgba(45, 21, 5, .68);
  font-size: 15px;
  line-height: 1.5;
}

/* LE TITRE DU BLOC LOOKBOOK OCCUPAIT LE TIERS DE SA COLONNE (demande PO du 2026-08-28).
   Mesure a l'ecran, viewport 782 px : colonne interieure de 723 px, titre de 258 px — 465 px de
   vide a droite. Deux causes qui se cumulent, et une seule est corrigeable sans toucher au dessin :
   · `.lookbook-copy` aligne ses enfants en `flex-start`, donc le h2 se retracte a la largeur de sa
     plus longue ligne au lieu de prendre la colonne ;
   · sa taille est `clamp(54px, 6vw, 92px)` : entre 700 et 900 px de large, `6vw` vaut 42 a 54 px,
     donc c'est le PLANCHER qui s'applique — le titre ne grandit plus alors que la grille est deja
     passee en une seule colonne, large.
   On elargit la boite et on redresse la pente : `11vw` ne change rien au mobile (le plancher tient
   sous 490 px) ni au grand desktop (le plafond tient au-dela de 836 px). Seule la zone
   intermediaire, celle qui etait creuse, se remplit. */
.lookbook-copy h2 {
  width: 100%;
  font-size: clamp(54px, 11vw, 92px);
}

/* L'ADRESSE DU PIED DE PAGE TIENT SUR UNE LIGNE (demande PO du 2026-08-28).
   Mesure a l'ecran, viewport 1060 px : « Face FECECAM, Aitchedji » se cassait en DEUX lignes dans
   118 px utiles. La cause est la grille du kit — `grid-template-columns:.85fr 1.15fr` — qui donne
   la plus petite part a la colonne de l'adresse et la plus grande a celle des horaires.
   Ces deux textes viennent desormais des REGLAGES (ADR-0098) : leurs longueurs changent avec la
   boutique, et une proportion figee ne peut pas les suivre. La colonne de gauche ne descend donc
   plus sous la largeur de son contenu ; au-dela, elle garde la proportion voulue par le design.
   AU-DESSUS DE 520 px SEULEMENT : en dessous, le kit passe a une colonne unique, et c'est bien. */
@media (width > 520px) {
  .store-visit-details {
    grid-template-columns: minmax(max-content, 0.85fr) minmax(0, 1.15fr);
  }
}

/* LES DEUX BLOCS DU PIED DE PAGE COMMENCENT A LA MEME HAUTEUR (demande PO du 2026-08-28).
   Le kit les centre verticalement (`align-items:center`). Tant que les deux textes tenaient sur le
   meme nombre de lignes, cela se voyait a peine ; depuis qu'ils viennent des REGLAGES (ADR-0098),
   leurs longueurs different — mesure a l'ecran : icones a la meme hauteur, mais titres decales de
   7 px, l'un a 215 px et l'autre a 208. On aligne donc en haut : l'icone, le titre et le detail de
   chaque bloc partent de la meme ligne, quelle que soit la longueur des textes. */
.store-visit-details > div {
  align-items: flex-start;
}

/* LE TITRE DE LA CARTE « AVIS » ETAIT BRUN SUR BRUN (demande PO du 2026-08-29).
   La carte editoriale porte `background: linear-gradient(145deg, var(--brown-mid), var(--brown))`
   et `color: var(--cream)` : son titre doit donc heriter du creme. Mesure a l'ecran : il sortait en
   `rgb(45, 21, 5)` — le brun du fond. Seul l'`<em>` restait lisible, en caramel clair, si bien que
   la page affichait « racontee par vous. » et rien avant.
   `inherit` plutot qu'une couleur ecrite : le jour ou la carte changera de fond, le titre suivra. */
.review-editorial-card h2 {
  color: inherit;
}

/* ── UNE NOTE SANS COMMENTAIRE (ADR-0102 §1.2) ──
   Le texte de l'avis est devenu facultatif : une note de cinq étoiles sans phrase EST un avis.
   Restait le dessin — le grand guillemet ouvrant annonçait une citation, et sans texte la carte
   ouvrait sur deux cents pixels de vide, l'air cassée alors qu'elle disait vrai.

   ON N'INVENTE AUCUNE PHRASE À LA PLACE DE LA CLIENTE. Le guillemet part (fait côté HTML et JS),
   et la note prend la place qu'occupait la citation : les étoiles deviennent le propos de la
   carte, au centre, grandes. Rien n'est ajouté qui n'ait été dit. */
.review-card-note-seule .home-review-card-header {
  flex: 1;
  align-items: center;
  justify-content: center;
}
.review-card-note-seule .home-review-stars,
.review-card-note-seule .review-stars-v3 {
  font-size: 1.9rem;
  letter-spacing: 0.1em;
}
.review-card-note-seule .home-review-source {
  font-size: 0.78rem;
}


/* ── LES DEUX PLATEFORMES DANS LE HERO D'AVIS (demande PO du 2026-08-29) ──
   Le hero annonçait « partagez votre expérience sur nos plateformes officielles » et laissait
   descendre la page pour les trouver. La rangée tient dans la marge du hero (390 px imposés,
   270 occupés) : sa hauteur ne bouge pas. `flex-wrap` pour que deux boutons longs passent à la
   ligne sur mobile plutôt que de déborder. */
.reviews-hero-actions-v3 {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-top: 20px;
}
.reviews-hero-actions-v3[hidden] {
  display: none;
}

/* ── LA BARRE « AJOUTER À L'ÉCRAN D'ACCUEIL » (ADR-0105) ──
   Elle ne couvre pas la page : une barre en bas, refermable, qui laisse tout lisible derrière.
   `translateY` plutôt qu'un `display` : la sortie se voit, l'entrée aussi, et rien ne saute.
   `prefers-reduced-motion` la fait apparaître sans glisser. */
.install-bar-v3 {
  position: fixed;
  left: 50%;
  bottom: 16px;
  z-index: 60;
  width: min(560px, calc(100vw - 32px));
  transform: translate(-50%, 140%);
  transition: transform 0.26s ease, opacity 0.26s ease;
  opacity: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 14px 18px;
  border-radius: 16px;
  background: var(--brown, #2d1505);
  color: var(--cream, #f5ecd7);
  box-shadow: 0 18px 40px rgba(0, 0, 0, 0.28);
}
.install-bar-v3.is-open {
  transform: translate(-50%, 0);
  opacity: 1;
}
.install-bar-v3 p {
  margin: 0;
  font-size: 0.86rem;
  line-height: 1.45;
  flex: 1 1 240px;
}
.install-bar-v3 > div {
  display: flex;
  gap: 8px;
  flex-shrink: 0;
}
.install-bar-v3 .button-ghost {
  color: var(--cream, #f5ecd7);
}
@media (prefers-reduced-motion: reduce) {
  .install-bar-v3 {
    transition: opacity 0.2s ease;
    transform: translate(-50%, 0);
  }
}

/* ── L'EN-TÊTE DE LA FENÊTRE DE CANDIDATURE, RESSERRÉ (demande PO du 2026-08-29) ──
   Le kit V3 donne à cette fenêtre l'en-tête d'une page : surtitre, très grand titre, trois
   pastilles. Sur un écran d'ordinateur en 16/9 — et plus encore sur un téléphone — il occupait
   la moitié de la hauteur disponible, et le formulaire commençait sous la ligne de flottaison :
   on ouvrait « Devenir affilié » sans voir un seul champ.

   On ne retire rien : le titre, le surtitre et les trois promesses restent. Ils tiennent
   simplement dans moins de place. Écart assumé avec la référence, inscrit au registre. */
.partner-apply-dialog-head {
  padding-block: clamp(18px, 2.4vw, 26px);
  gap: 8px;
}
.partner-apply-dialog-head h2 {
  font-size: clamp(1.5rem, 3.2vw, 2rem);
  line-height: 1.15;
  margin: 0;
}
.partner-apply-dialog-head ul {
  gap: 8px;
  margin-top: 2px;
}
.partner-apply-dialog-head ul li {
  padding: 5px 12px;
  font-size: 0.74rem;
}
/* Le corps récupère la hauteur rendue par l'en-tête. */
.partner-apply-dialog-body {
  padding-block: clamp(18px, 2.4vw, 28px);
}


/* ── 5. La fenêtre de candidature Ambassadeurs (ADR-0128) ────────────────────────────────────
 *
 * MESURÉ AVANT D'ÉCRIRE. Le nouveau formulaire a introduit quatre classes que personne n'avait
 * stylées : un `<select>` de 21 px sans bordure au milieu de champs de 50 px bordés, des aides
 * en 16 px brun qu'on prenait pour des libellés, des messages d'erreur sans couleur, et des
 * cases à cocher empilées au lieu d'être alignées avec leur phrase.
 */

/* LE SELECT EST UN CHAMP DU MÊME FORMULAIRE, aux mêmes valeurs.
 *
 * Le design ne fournit pas de select encadré — ses trois selects existants sont des barres
 * d'outils sans bordure ou des lignes soulignées. Ce qui fait référence ici, c'est donc la règle
 * VOISINE : `.partner-v2-application-form input, textarea`. Ses valeurs sont reprises au chiffre
 * près — 52 px, 13/14 de rentrée, rayon 5, 16 px, interligne 1,5, `var(--line)` — et non
 * approchées : j'avais d'abord écrit 50 px et 12 px de rentrée, deux pixels d'écart qui se
 * voyaient sur une ligne où le select est à côté d'un champ.
 *
 * Ce que cette règle AJOUTE, et qui n'existe nulle part ailleurs : neutraliser l'apparence native
 * et faire la place au chevron. Le chevron est un SVG encodé — aucune requête, aucun fichier. */
.partner-v2-application-form select {
  width: 100%;
  min-height: 52px;
  padding: 13px 40px 13px 14px;
  border: 1px solid var(--line);
  border-radius: 5px;
  line-height: 1.5;
  background-color: var(--white);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='9' viewBox='0 0 14 9' fill='none' stroke='%238b5838' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m1 1 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 14px center;
  color: var(--brown);
  font-family: inherit;
  font-size: 16px;
  appearance: none;
  outline: 0;
  transition: border-color 0.2s, box-shadow 0.2s;
}
/* Tant qu'aucun choix n'est fait, l'invite se lit comme un placeholder — pas comme une réponse. */
.partner-v2-application-form select:invalid { color: #8a7766; }
.partner-v2-application-form select:focus {
  border-color: var(--caramel-dark);
  box-shadow: 0 0 0 3px #c8956a38;
}

/* L'AIDE N'EST PAS UNE CONSIGNE. En 16 px brun elle pesait autant que le libellé ; on lisait deux
   instructions par champ. Petite et sourde, elle se lit quand on en a besoin. */
.partner-v2-field-hint {
  margin-top: -2px;
  color: var(--muted);
  font-size: 0.76rem;
  line-height: 1.4;
}

/* ── L'aide longue se replie derrière une icône (demande PO) ──────────────────────────────────
 *
 * Sous un champ de 250 px, vingt mots s'empilent sur trois lignes et poussent le champ suivant
 * hors de l'écran — le constat qui avait fait replier les aides des Réglages du programme.
 *
 * LE TEXTE RESTE DANS LE DOM, cible de l'`aria-describedby` du champ : un lecteur d'écran
 * l'annonce avec le champ, sans avoir à trouver l'icône. Il est replié VISUELLEMENT, pas retiré.
 *
 * AUCUN JAVASCRIPT. `:has()` fait le lien entre l'icône, dans le libellé, et l'aide, qui suit le
 * champ — ils ne sont pas frères, et c'est la seule façon de les relier en CSS. Le projet s'en
 * sert déjà pour les cases à cocher de ce même formulaire.
 */
.partner-v2-bulle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  margin-left: 5px;
  vertical-align: -3px;
  border-radius: 50%;
  color: var(--caramel-dark);
  cursor: help;
}
.partner-v2-bulle:focus-visible {
  outline: 2px solid var(--caramel-dark);
  outline-offset: 1px;
}

/* La rangée porte le repère de position ; l'aide s'y accroche. */
.partner-apply-dialog .partner-v2-field-row:has(.partner-v2-bulle) { position: relative; }

.partner-apply-dialog .partner-v2-field-row:has(.partner-v2-bulle) .partner-v2-field-hint {
  position: absolute;
  z-index: 5;
  top: 100%;
  left: 0;
  right: 0;
  margin-top: 4px;
  padding: 9px 11px;
  border: 1px solid var(--cream-dark);
  border-radius: 5px;
  background: var(--brown);
  color: var(--cream);
  font-size: 0.75rem;
  line-height: 1.45;
  box-shadow: 0 10px 24px #2d150526;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.15s;
}

/* Au survol ET au focus clavier : une aide qui n'existe qu'à la souris n'est pas une aide. */
/* `:focus` EN PLUS de `:focus-visible`. L'icône ne prend le focus que par tabulation — elle n'est
   ni un champ ni un bouton — donc `:focus` ne peut pas créer d'ouverture parasite à la souris.
   Mesuré : avec `:focus-visible` seul, l'aide restait fermée quand le focus arrivait par script,
   et c'est exactement ce qu'un lecteur d'écran fait en parcourant le formulaire. */
.partner-apply-dialog .partner-v2-field-row:has(.partner-v2-bulle:hover) .partner-v2-field-hint,
.partner-apply-dialog .partner-v2-field-row:has(.partner-v2-bulle:focus) .partner-v2-field-hint {
  opacity: 1;
  visibility: visible;
}

/* Le curseur d'abonnés réemploie `.partner-v2-audience` du design : rien à redessiner ici. Seule
   la règle de hauteur du formulaire doit être neutralisée — elle impose 52 px à tous ses `input`,
   piste de 3 px comprise. */
.partner-v2-application-form .partner-v2-audience input[type="range"] { min-height: 0; }
.partner-v2-audience output { color: var(--brown); }

/* Le compteur du pitch va à droite, sous le champ : c'est une jauge, pas une phrase. */
[data-partner-pitch-count] {
  align-self: flex-end;
  font-variant-numeric: tabular-nums;
}

/* UN REFUS DOIT SE VOIR. Sans couleur ni marqueur, le message se confondait avec l'aide juste
   au-dessus — et on relisait le champ sans comprendre ce qu'on devait corriger. */
.partner-v2-field-error {
  display: flex;
  gap: 6px;
  margin-top: -2px;
  color: #b3261e;
  font-size: 0.78rem;
  font-weight: 600;
  line-height: 1.4;
}
.partner-v2-field-error::before {
  content: "!";
  flex: none;
  width: 15px;
  height: 15px;
  margin-top: 1px;
  border-radius: 50%;
  background: #b3261e;
  color: var(--white);
  font-size: 0.66rem;
  font-weight: 800;
  line-height: 15px;
  text-align: center;
}

/* Le champ refusé se signale AUSSI par sa bordure : le message seul oblige à relier un texte du
   bas à un champ du haut, ce qui se fait mal sur un écran étroit. */
.partner-v2-application-form :is(input, select, textarea)[aria-invalid="true"] {
  border-color: #b3261e;
  background-color: #fdf5f4;
}

/* LA SYNTHÈSE D'ERREURS, en tête du formulaire. Elle reçoit le focus au retour du serveur : c'est
   elle qu'on doit lire d'abord, pas le premier champ — souvent le seul qui était juste. */
[data-partner-errors] {
  padding: 12px 14px;
  border: 1px solid #f0c7c3;
  border-left: 3px solid #b3261e;
  border-radius: 5px;
  background: #fdf5f4;
  color: #7d1a15;
  font-size: 0.82rem;
  line-height: 1.5;
}
[data-partner-errors] p { margin: 0 0 4px; }
[data-partner-errors] ul { margin: 0; padding-left: 18px; }
[data-partner-errors] li { margin-top: 2px; }
[data-partner-errors]:focus-visible { outline: 2px solid #b3261e; outline-offset: 2px; }

/* LES DEUX CONFIRMATIONS. En `display:block`, la case flottait au-dessus de sa phrase. Une grille
   à deux colonnes les aligne, et la zone cliquable couvre le texte entier — sur un téléphone, on
   vise la phrase, pas le carré de 16 px. */
.partner-v2-check {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 10px;
  align-items: start;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: 5px;
  background: var(--cream-light);
  cursor: pointer;
  font-size: 0.82rem;
  font-weight: 400;
  line-height: 1.45;
}
.partner-v2-check input[type="checkbox"] {
  width: 18px;
  height: 18px;
  min-height: 0;
  margin: 2px 0 0;
  padding: 0;
  accent-color: var(--caramel-dark);
  cursor: pointer;
}
.partner-v2-check:has(input:focus-visible) {
  border-color: var(--caramel-dark);
  box-shadow: 0 0 0 3px #c8956a38;
}
.partner-v2-check a { color: var(--caramel-dark); text-decoration: underline; }

/* RESSERRER. Neuf champs ne tiendront pas dans 600 px utiles, et prétendre le contraire
   conduirait à des champs trop bas pour être confortables. On gagne ce qui se gagne sans rogner
   sur les zones tactiles : 50 px de champ, 44 px minimum partout. */
.partner-apply-dialog-body .partner-v2-application-form { gap: 14px; }
.partner-apply-dialog .partner-v2-field-row,
.partner-apply-dialog .partner-v2-field-full { gap: 6px; }
.partner-apply-dialog .partner-v2-application-form textarea { min-height: 76px; }
.partner-apply-dialog .partner-v2-form-submit { min-height: 50px; }
.partner-apply-dialog-lead { margin: 0; font-size: 0.86rem; line-height: 1.5; }

/* Le pied de formulaire, discret : c'est une garantie, pas un argument. */
.partner-apply-dialog .partner-v2-form-note {
  margin: 0;
  color: var(--muted);
  font-size: 0.76rem;
  line-height: 1.45;
}

/* L'EN-TÊTE NE DÉFILE PAS. Le corps est plus haut que la fenêtre : sans cela, le titre et la
   croix de fermeture disparaissent dès qu'on descend d'un champ. */
.partner-apply-dialog { display: flex; flex-direction: column; }
.partner-apply-dialog-head { flex: none; }
.partner-apply-dialog-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; }

/* Sur un téléphone, une seule colonne — et rien ne dépasse en largeur. */
@media (max-width: 640px) {
  .partner-apply-dialog-body .partner-v2-application-form { grid-template-columns: minmax(0, 1fr); }
}

/* ─── 6. Vidéo de la fiche pièce (ADR-0132) ────────────────────────────────────────────────────
   La scène est dessinée par `hisy-v3.css` pour une image (`.pdp-stage img`). La vidéo occupe le
   même cadre, au même recadrage : sans cela elle garderait son ratio natif et laisserait deux
   bandes dans un cadre 3/4. `.pdp-thumb-play` existait déjà — le design avait prévu la vignette
   vidéo, il lui manquait la vidéo. */
.pdp-stage .pdp-video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* LE BOUTON DE SON, en bas à droite : le coin haut-gauche porte déjà l'état de la pièce
   (`figcaption`), et le centre est le sujet. Il ne s'affiche que sur la vue vidéo. */
.pdp-stage .pdp-sound {
  position: absolute;
  right: 15px;
  bottom: 15px;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 9px 14px;
  border: 1px solid rgba(45, 21, 5, .12);
  border-radius: 999px;
  background: rgba(253, 248, 242, .95);
  color: var(--brown);
  font: inherit;
  font-size: 12px;
  font-weight: 700;
  cursor: pointer;
  transition: background .18s, transform .18s;
}
.pdp-stage .pdp-sound:hover { background: #fff; transform: translateY(-1px); }
.pdp-stage .pdp-sound:focus-visible { outline: 2px solid var(--caramel); outline-offset: 2px; }

/* LE BOUTON LECTURE / PAUSE, à gauche du son (arbitrage PO du 2026-09-03). Une boucle qui démarre
   seule sans moyen de l'arrêter ne respecte pas « Pause, Stop, Hide » — et sur un forfait mobile,
   elle se paie en francs. Même dessin que le bouton de son : ce sont deux commandes du même média,
   elles ne doivent pas avoir l'air d'appartenir à deux écrans. */
.pdp-stage .pdp-play {
  position: absolute;
  right: 15px;
  bottom: 62px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  padding: 0;
  border: 1px solid rgba(45, 21, 5, .12);
  border-radius: 999px;
  background: rgba(253, 248, 242, .95);
  color: var(--brown);
  cursor: pointer;
  transition: background .18s, transform .18s;
}
.pdp-stage .pdp-play:hover { background: #fff; transform: translateY(-1px); }
.pdp-stage .pdp-play:focus-visible { outline: 2px solid var(--caramel); outline-offset: 2px; }

/* L'ICÔNE SUIT `aria-pressed`, comme le bouton de son : une seule source d'état. Pressé = la vidéo
   joue, on propose donc la PAUSE ; relâché = elle est arrêtée, on propose la LECTURE. */
.pdp-stage .pdp-play .pdp-play-go { display: none; }
.pdp-stage .pdp-play[aria-pressed="false"] .pdp-play-pause { display: none; }
.pdp-stage .pdp-play[aria-pressed="false"] .pdp-play-go { display: inline-flex; }

/* L'ICÔNE SUIT L'ÉTAT, et l'état est porté par `aria-pressed` — une seule source, lue par le
   lecteur d'écran comme par le style. Muet : le haut-parleur barré. Sonore : les ondes. */
.pdp-stage .pdp-sound .pdp-sound-off { display: none; }
.pdp-stage .pdp-sound[aria-pressed="true"] .pdp-sound-on { display: none; }
.pdp-stage .pdp-sound[aria-pressed="true"] .pdp-sound-off { display: inline-flex; }

@media (max-width: 640px) {
  /* Sur un écran étroit, le libellé cède la place : l'icône suffit, et le cadre reste au sujet. */
  .pdp-stage .pdp-sound { right: 10px; bottom: 10px; padding: 9px; }
  .pdp-stage .pdp-sound .pdp-sound-txt { display: none; }
}

/* La vignette vidéo de la fiche porte un `<video>` et non un `<img>` : il doit remplir le cadre
   exactement comme lui (`hisy-v3.css:907`), sinon il garde son ratio natif et laisse deux bandes. */
.pdp-thumb video { display: block; width: 100%; height: 100%; object-fit: cover; }

/* ─── 7. Sections de la fiche : les corrections du handoff design (ADR-0134) ────────────────────
   Le handoff `design_handoff_pdp_sections` partagé par le PO le 2026-08-31 porte deux valeurs
   corrigées que notre copie de `hisy-v3.css` ne connaît pas encore — elle date d'avant.

   `hisy-v3.css` est un fichier du DESIGN, reconstruit à chaque import (ADR-0003) : on ne l'édite
   pas à la main. Les corrections vivent donc ici, et disparaîtront d'elles-mêmes le jour où le
   design sera réimporté avec ces valeurs. */

/* 1. LA COLONNE DE TEXTE. Le design a corrigé `14px / max-width:82ch` en `16px / max-width:none` :
      « trop petit et trop étroit », dit le handoff. Notre copie porte encore l'ancienne valeur. */
.pdp-block p { max-width: none; font-size: 16px; line-height: 1.8; }

/* 2. LA BARRE SE CALE SOUS L'EN-TÊTE MESURÉ. Notre copie fige `scroll-margin-top: 150px` et ne
      connaît pas `--pdp-bar-top` : la barre se posait donc à une hauteur devinée, et une ancre
      suivie nativement atterrissait à côté. Le script écrit ces deux variables à la volée — et
      seulement quand la hauteur mesurée est NON NULLE, sinon le repli ci-dessous est défait. */
.pdp-navbar { top: calc(var(--pdp-bar-top, 78px) + 10px); }
.pdp-block { scroll-margin-top: var(--pdp-jump, 150px); }

/* La ligne de carte porte « rayon · marque » puis une puce de couleur : la puce doit s'aligner
   sur le texte, pas s'asseoir sur la ligne de base. `.pdp-swatch` vient du design (fiche) et n'a
   pas d'alignement propre — il vivait dans une cellule en `flex`. */
/* `flex` ET NON `inline-flex` : en ligne, ce `<small>` laissait le nom de la pièce se coller à
   sa suite — « FEMME · UNIQLOTop col V nervuré ». Le design le rend en bloc (`.product-meta
   small`), et c'est cette ligne propre qu'il faut garder ; `flex` est un bloc, il dimensionne la
   puce sans rien changer au flux. */
.product-meta .product-line { display: flex; align-items: center; gap: 6px; }

/* La puce de la CARTE fait 11 px (design PO du 2026-08-31) : celle de la fiche en fait 13, et une
   ligne de carte est plus petite. `.pdp-swatch` est le composant du design — on ne redéfinit que
   sa taille, dans le contexte de la carte, sans toucher à son dessin. */
.product-meta .product-line .pdp-swatch {
  /* `inline-block` ET NON `inline`, ET C'EST TOUT LE DÉFAUT (« la puce n'est pas bien visible ») :
     mesurée à l'écran, elle faisait **0 px de large**. Le design lui donne `flex: none; width`,
     ce qui suppose un parent en `flex` — or `.product-meta small { display: block }` du design
     l'emporte sur notre `.product-line { display: inline-flex }`, moins spécifique. La pastille
     restait donc `inline`, et un élément inline non remplacé ignore `width`. Elle était présente
     dans le markup, lue par les lecteurs d'écran, et invisible à l'œil.
     Les deux sélecteurs passent par `.product-meta` pour repasser devant la règle du design. */
  display: inline-block;
  width: 11px;
  height: 11px;
  /* LE CONTOUR EST RENFORCÉ (demande PO du 2026-08-31 : « la puce n'est pas bien visible »).
     Le design la cerne d'un filet brun à 20 % — assez pour 13 px sur la fiche, trop discret pour
     11 px sur une carte, où une teinte claire (rose pâle, ivoire, blanc) se fondait dans le fond
     crème. L'anneau blanc la détache du fond, le filet brun lui rend son bord : la puce se lit
     désormais quelle que soit sa couleur, SANS grossir — la taille reste celle que vous avez
     fixée. */
  box-shadow: 0 0 0 1px #fff, 0 0 0 2px #2d15054d;
}

/* ────────────────────────────────────────────────────────────────────────────────────────────
   SÉLECTEUR DE DÉCLINAISON — fiche d'un MODÈLE (ADR-0135, spécification §3.2).

   CE COMPOSANT N'EXISTE PAS AU HANDOFF : le design V3 ne connaît que la pièce unique, et
   `hisy-v3.css` est régénéré par le script d'import — une règle écrite là-bas disparaîtrait au
   prochain passage (ADR-0003). Il est donc habillé ici, avec les jetons du design et rien
   d'autre : mêmes rayons, mêmes filets, même cible tactile de 44 px que `.pdp-sizelink`.
   ──────────────────────────────────────────────────────────────────────────────────────────── */
.pdp-picker { margin: 16px 0 0; }
.pdp-picker-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 10px; }
.pdp-picker-label { font-size: 12.5px; font-weight: 750; color: var(--brown); letter-spacing: .02em; }

.pdp-picks { display: flex; flex-wrap: wrap; gap: 8px; margin: 10px 0 0; padding: 0; list-style: none; }

/* La puce est un LIEN : elle doit donc annuler le soulignement et la couleur d'ancre du thème. */
.pdp-pick {
    display: flex; flex-direction: column; gap: 2px; align-items: flex-start;
    min-height: 44px; justify-content: center; padding: 6px 14px;
    border: 1px solid var(--line); border-radius: 12px; background: var(--cream-light);
    font-size: 12.5px; font-weight: 750; color: var(--brown); text-decoration: none;
    transition: border-color .18s, background .18s, color .18s;
}
.pdp-pick > span { display: inline-flex; align-items: center; gap: 7px; }
.pdp-pick small { font-size: 11px; font-weight: 600; color: var(--body); }

a.pdp-pick:hover { border-color: var(--caramel); background: var(--white); color: var(--caramel-dark); }
a.pdp-pick:focus-visible { outline: 2px solid var(--caramel-dark); outline-offset: 2px; }

/* LE CHOIX SERVI SE VOIT SANS LA COULEUR SEULE : le filet double d'épaisseur et le fond change.
   Une distinction portée par la seule teinte échapperait à qui la perçoit mal. */
.pdp-pick.is-current { border-color: var(--brown); border-width: 2px; padding: 5px 13px; background: var(--white); }

/* ÉPUISÉE — visible, jamais cliquable. Le barré dit l'indisponibilité au-delà du gris seul. */
.pdp-pick.is-out { cursor: not-allowed; opacity: .55; }
.pdp-pick.is-out > span { text-decoration: line-through; }


/* « CHOISIR » EST UN LIEN, ET LE DESIGN NE L'AVAIT PAS PRÉVU (ADR-0135 §3.1).

   Sur une carte de modèle, l'ajout rapide devient un `<a>` — il mène à la fiche au lieu de mettre
   un SKU concret de côté. Mais `.product-image-wrap > a { width:100%; height:100% }` du design
   vise TOUT lien enfant direct du cadre : écrite pour la vignette cliquable, la règle attrapait
   aussi celui-ci. Résultat vu à l'écran : au survol, « Choisir » couvrait l'image entière d'un
   voile crème flouté au lieu de la barre posée en bas.

   `auto` rend la main aux décalages `left/right/bottom` que `.quick-add` pose déjà. On ne touche
   ni au dessin ni au comportement du composant — seulement à l'héritage qu'il ne devrait pas
   recevoir. */
.product-image-wrap > a.quick-add { width: auto; height: auto; }

/* LA GRILLE DES PIÈCES LIÉES EST DEVENUE UNE `<ul>` (fiche produit, « Compléter la silhouette »).

   Elle sert désormais le MÊME partial de carte que le catalogue — il ne doit rester qu'une source
   de ce markup — et ce partial rend un `<li>`. Le design n'avait pas prévu de liste ici : ces deux
   règles retirent seulement ce que le navigateur ajoute de lui-même à toute `<ul>`. La grille, sa
   mesure et ses gouttières restent celles du design.

   `min-width: 0` comme sur `.shop-grid-v2 > li` : sans lui, un nom de pièce sans espace fait
   déborder sa colonne de grille au lieu de se couper. */
ul.product-grid { list-style: none; padding: 0; }
ul.product-grid > li { min-width: 0; }

/* ══════════════════════════════════════════════════════════════════════════════════════════════
   LA CARTE DE L'AUTEURE D'UN LOOK CO-CRÉÉ (design de la co-création, 2026-09-03).

   ELLE NE PORTE QU'UN NOM. Pas de compte social, pas de lien, pas de code de suivi : le PO a
   arbitré que la page appartient à la boutique, et que le crédit se rend par la signature, non par
   une captation d'attribution. Publier en plus le compte Instagram d'une partenaire serait une
   donnée personnelle de plus sur une page indexée — une décision qui n'a pas été prise.

   Les règles vivent ici, jamais dans un fichier du design (AGENTS.md) : `hisy-v3.css` est
   remplacée à chaque import.
   ══════════════════════════════════════════════════════════════════════════════════════════════ */
/* LA SIGNATURE DE L'AUTEURE — À L'ÉCHELLE DE LA FENÊTRE (demande PO du 2026-09-03 : « rendre le
   design pro »).

   « Pro » ici veut dire NATIF, pas décoré. Cette fenêtre a une gamme : des pastilles de 36 px en
   crème, des noms de pièce à 12 px, des méta à 9 px, un sur-titre à 10 px en caramel foncé espacé
   de .14em, et Fraunces pour tout ce qui titre. Ma carte parlait une autre langue — rond brun plein
   de 32 px, corps à .88rem — et se lisait comme un encart rapporté.

   Elle emprunte donc au voisin immédiat : le rond reprend EXACTEMENT `.lookbook-product-index`
   (36 px, fond crème, chiffre caramel en Fraunces), le sur-titre reprend `.lookbook-dialog-kicker`,
   le rôle reprend la méta d'une pièce. Le nom seul monte à 14 px en Fraunces : c'est une personne,
   pas une référence produit.

   Aucune couleur nouvelle, aucune taille inventée. */
.lookbook-author {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 18px;
  padding-top: 14px;
  border-top: 1px solid var(--border, #E8D4B4);
}

/* Le même rond que les repères de la liste, à la lettre près. */
.lookbook-author-ava {
  flex: 0 0 36px;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: var(--cream, #F5ECD7);
  color: var(--caramel-dark, #A9703F);
  display: grid;
  place-items: center;
  font-family: 'Fraunces Variable', Georgia, serif;
  font-size: 15px;
  font-weight: 650;
  line-height: 1;
}

.lookbook-author-copy { display: flex; flex-direction: column; gap: 3px; min-width: 0; }

/* `.lookbook-dialog-kicker`, mot pour mot. */
.lookbook-author-kicker {
  color: var(--caramel-dark, #A9703F);
  letter-spacing: .14em;
  text-transform: uppercase;
  font-size: 10px;
  font-weight: 780;
}

.lookbook-author-line { display: flex; align-items: baseline; flex-wrap: wrap; gap: 8px; min-width: 0; }

.lookbook-author-line strong {
  font-family: 'Fraunces Variable', Georgia, serif;
  font-size: 14px;
  font-weight: 590;
  color: var(--brown, #2D1505);
}

/* La méta d'une pièce, même taille et même gris. */
.lookbook-author-line small { font-size: 9px; color: var(--muted, #8A7A66); }
.contact-v2-map-consent {
  min-height: 360px;
  display: grid;
  place-content: center;
  justify-items: center;
  gap: 16px;
  padding: 32px;
  text-align: center;
  background: var(--cream);
}

@media print {
  .site-header,
  .site-footer,
  .legal-layout > aside,
  .legal-hero,
  #withdrawal-form button {
    display: none !important;
  }

  .legal-layout,
  .legal-content {
    display: block;
    max-width: none;
    padding: 0;
  }
}

/* ── Bandeau de consentement aux contenus tiers (ADR-0171) ─────────────────────────────────────
 *
 * ÉCART DÉCLARÉ : le design de référence ne prévoit aucun bandeau. Il vit donc ici, dans la
 * feuille maison chargée après `hisy-v3.css`, et jamais dans le fichier du design que l'import
 * écraserait.
 *
 * DISCRET, comme le PO l'a demandé : une barre en bas, qui laisse naviguer et lire. Une modale
 * bloquante pour une carte serait disproportionnée — et coûteuse en conversion.
 *
 * Les deux boutons sont STRICTEMENT identiques. Donner plus de poids à « Accepter » ferait de ce
 * bandeau une case forcée plutôt qu'un consentement. */
.consentement {
    position: fixed;
    z-index: 200;
    right: 16px;
    bottom: 16px;
    left: 16px;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 12px 20px;
    max-width: 720px;
    margin: 0 auto;
    padding: 14px 18px;
    border: 1px solid var(--line, #e7dccb);
    border-radius: 14px;
    background: var(--cream-light, #fbf7ec);
    box-shadow: 0 18px 44px rgba(45, 21, 5, 0.18);
}

.consentement-texte {
    flex: 1 1 320px;
    margin: 0;
    font-size: 0.82rem;
    line-height: 1.55;
    color: var(--muted, #7a6a57);
}

.consentement-texte a {
    color: var(--caramel-dark, #a8734e);
    font-weight: 600;
    text-underline-offset: 2px;
}

.consentement-actions {
    display: flex;
    flex: 0 0 auto;
    gap: 10px;
}

.consentement-btn {
    padding: 9px 18px;
    border: 1.5px solid var(--caramel-dark, #a8734e);
    border-radius: 10px;
    background: transparent;
    color: var(--caramel-dark, #a8734e);
    font-family: inherit;
    font-size: 0.82rem;
    font-weight: 650;
    cursor: pointer;
    transition: background-color 0.15s, color 0.15s;
}

.consentement-btn:hover,
.consentement-btn:focus-visible {
    background: var(--caramel-dark, #a8734e);
    color: #fff;
}

/* Sur téléphone, les deux boutons prennent la largeur : viser une cible de 9 px de haut au pouce
   sur un bandeau collé au bord n'est pas raisonnable. */
@media (max-width: 560px) {
    .consentement {
        right: 10px;
        bottom: 10px;
        left: 10px;
        padding: 14px;
    }

    .consentement-actions {
        flex: 1 1 100%;
    }

    .consentement-btn {
        flex: 1 1 0;
    }
}

/* ── Les blocs d'identité des pages légales se lisent, au lieu de se déchiffrer ────────────────
 *
 * MESURÉ À L'ÉCRAN : le bloc « Éditeur du site » formait un paragraphe de 760 px, dix valeurs
 * séparées par des `<br>`, chaque libellé noyé dans la ligne. On y cherche le RCCM à l'œil.
 *
 * En liste de définitions, le libellé passe en colonne et la valeur en regard : chaque ligne est
 * courte, et l'œil descend la colonne des libellés jusqu'à celui qu'il cherche. C'est ce qu'on
 * attend d'une mention légale — une information qu'on vient VÉRIFIER, pas lire d'un trait.
 *
 * ÉCART DÉCLARÉ (ADR-0003), demande du PO du 2026-09-04. Le design ne style aucune liste dans
 * `legal-content` : la règle vit donc ici, dans la feuille chargée après lui. */
.legal-content .legal-identity {
    display: grid;
    grid-template-columns: max-content 1fr;
    gap: 7px 18px;
    margin: 0 0 16px;
    font-size: 15px;
    line-height: 1.6;
}

.legal-content .legal-identity dt {
    color: var(--brown, #2d1505);
    font-weight: 650;
}

.legal-content .legal-identity dd {
    margin: 0;
    color: var(--body, #5c4a3a);
}

/* La valeur qui porte l'identité de l'entité se distingue : c'est celle qu'on vient vérifier. */
.legal-content .legal-identity dd strong {
    color: var(--brown, #2d1505);
    font-weight: 620;
}

/* Sous 560 px, deux colonnes écraseraient la valeur : le libellé passe au-dessus. */
@media (max-width: 560px) {
    .legal-content .legal-identity {
        grid-template-columns: 1fr;
        gap: 0;
    }

    .legal-content .legal-identity dt {
        margin-top: 10px;
    }

    .legal-content .legal-identity dt:first-child {
        margin-top: 0;
    }
}

/* ------------------------------------------------------------------------------------------------
   NAVIGATION DES PAGES LÉGALES — survol et page courante.
   Écart déclaré (ADR-0003), demandé par le PO le 2026-09-04.

   Le design pose `.legal-layout aside a` avec une couleur et une bordure, sans `:hover` ni état
   courant : huit liens identiques, et rien n'indiquait la page où l'on se trouve.

   L'IDIOME EST CELUI DU DESIGN, PAS LE MIEN. Il marque déjà l'élément courant d'une navigation par
   `[aria-current]` — `.quicknav [aria-current=page]{color:var(--brown)}` et
   `.pdp-crumb [aria-current]{color:var(--brown);font-weight:760}`. La page courante reprend donc le
   brun et la graisse ; le survol prend le caramel foncé, celui de l'intitulé « Informations » au-dessus
   de la colonne, pour rester distinct de l'état courant.

   DEUX INDICES POUR LA PAGE COURANTE, pas seulement la couleur : la graisse et le filet supérieur
   qui passe au caramel. Une couleur seule laisse de côté qui la distingue mal. */
.legal-layout aside a {
    transition: color .16s ease, border-color .16s ease;
}

.legal-layout aside a:hover,
.legal-layout aside a:focus-visible {
    color: var(--caramel-dark, #8b5838);
}

.legal-layout aside a[aria-current="page"] {
    color: var(--brown, #2d1505);
    font-weight: 700;
    border-top-color: var(--caramel, #c8956a);
}

/* ------------------------------------------------------------------------------------------------
   LE TUNNEL DE PAIEMENT SUR MOBILE — écart déclaré à l'ADR-0003, demande PO du 2026-09-06
   (« rendre le design de la page panier et du checkout sur mobile pro UI/UX »).

   Le tunnel `.co-*` du design est dessiné pour le bureau : deux colonnes, `max-width: 880px`. Sous
   760 px, mesuré à l'écran sur 375×812 :

   · `.co-modal` porte `overflow: hidden` et son contenu fait 1 257 px pour 763 px de haut. Le bas
     du formulaire — dont « Vers le paiement » — n'était pas atteignable au doigt. C'est un tunnel
     de paiement : personne ne pouvait payer depuis un téléphone de cette taille ;
   · le récapitulatif sombre poussait le formulaire à 339 px du haut, et son TOTAL restait sous la
     ligne de flottaison de son propre bloc.

   La correction reste dans la logique du design : la colonne du formulaire (`.co-panel`) porte déjà
   `overflow-y: auto` — il lui manquait d'être contrainte pour que ce défilement serve. On contraint,
   on plafonne le récapitulatif, et on colle l'action principale en bas de la zone qui défile.
   Le bouton SOMBRE seulement : le « Retour » fantôme reste dans le flux, sinon les deux se
   superposaient (constaté à l'écran).
   ------------------------------------------------------------------------------------------------ */
@media (max-width: 760px) {
    /* Le défilement vit dans la colonne du formulaire, pas dans la fenêtre. */
    .co-modal {
        overflow: hidden;
    }

    .co-body {
        min-height: 0;
    }

    .co-main {
        flex: 1 1 auto;
        min-height: 0;
    }

    .co-panel {
        min-height: 0;
    }

    /* Le récapitulatif garde sa place, sans manger l'écran : total et code promo restent visibles.
       `.co-lines` se contente du reste — mesuré à l'écran, une ligne d'article y tient entière. */
    .co-aside {
        flex: 0 0 auto;
        max-height: 46%;
        padding: 16px 20px;
    }

    .co-lines {
        flex: 0 1 auto;
        min-height: 0;
        overflow-y: auto;
    }

    .co-line-media {
        width: 40px;
        height: 40px;
    }

    .co-aside-foot {
        padding-top: 12px;
    }

    /* L'action de l'étape reste sous le pouce, quelle que soit la longueur du formulaire. */
    .co-actions {
        position: sticky;
        bottom: 0;
        z-index: 3;
        margin-top: 6px;
        padding-block: 12px;
        background: var(--cream-light);
        box-shadow: 0 -12px 22px -14px rgb(45 21 5 / 35%);
    }

    .co-panel .co-btn-dark.co-btn-full {
        position: sticky;
        bottom: calc(12px + env(safe-area-inset-bottom, 0px));
        z-index: 3;
        box-shadow: 0 12px 26px -14px rgb(45 21 5 / 60%);
    }

    /* COLLÉE AU BAS, VRAIMENT (constat du PO à l'écran, 2026-09-06 : « n'est pas collé au bottom »).
       `.co-panel` — la colonne qui défile — porte 18 px de marge basse. Une barre collante est bornée
       par son bloc conteneur, donc par la boîte de CONTENU du panneau : elle s'arrêtait 18 px au-dessus
       du bord, et ce vide la faisait lire comme un bloc flottant au milieu du formulaire.
       La marge passe sur la barre elle-même, fond compris — et l'encoche des téléphones récents avec. */
    .co-panel {
        padding-bottom: 0;
    }

    .co-actions {
        padding-bottom: calc(12px + env(safe-area-inset-bottom, 0px));
    }

    /* LES DEUX ACTIONS SUR UNE LIGNE (demande PO du 2026-09-06, maquette à l'appui). Le design les
       empile sous 760 px (`.co-actions{flex-direction:column}`) : deux boutons pleine largeur
       mangeaient 120 px de hauteur sur un écran qui en manque. Le principal garde plus de place. */
    .co-actions {
        flex-direction: row;
        align-items: stretch;
        gap: 10px;
    }

    .co-actions .co-btn {
        flex: 1 1 0;
        width: auto;
        min-width: 0;
        padding-inline: 12px;
    }

    .co-actions .co-btn-dark {
        flex: 1.6 1 0;
    }

    /* Sur une ligne, chaque libellé doit tenir sur une ligne lui aussi. */
    .co-actions .co-btn {
        gap: 6px;
        font-size: 12px;
        letter-spacing: 0.06em;
        white-space: nowrap;
    }

    /* EN-TÊTE COMPACT (demande PO du 2026-09-06 : « réduire la taille des éléments »). Le titre, la
       bascule et la croix se disputaient la ligne : chacun rétrécit, et l'en-tête passe de 44 à
       35 px — autant de rendu au formulaire. Le `padding` réserve la place de la croix, qui est en
       position absolue et se superposait à la bascule. */
    .co-aside-top {
        margin-bottom: 12px;
        padding-right: 42px;
    }

    .co-close {
        top: 12px;
        right: 12px;
        width: 32px;
        height: 32px;
    }

    .co-close svg {
        width: 15px;
        height: 15px;
    }

    /* 11 px et non 8,5 : la règle de lisibilité du 2026-09-08 (bloc 12) vaut aussi pour les règles
       maison. Le compactage recherché ici tient dans la marge, pas dans une taille illisible. */
    .co-eyebrow {
        margin-bottom: 3px;
        font-size: 11px;
    }

    .co-aside .co-aside-top h2 {
        font-size: 17px;
    }

    /* LE RÉCAPITULATIF SE REPLIE (« Détail » / « Masquer »). Déroulé, il poussait le premier champ
       à 339 px du haut ; replié, il garde ce qu'on vérifie avant de payer — le compteur et le
       TOTAL — et rend l'écran au formulaire. Le contenu reste dans le DOM : `aria-expanded` porte
       l'état, rien n'est reconstruit.

       ON MASQUE LES DEUX BLOCS, ON N'EN AJOUTE PAS UN TROISIÈME : `.co-aside` est une colonne flex
       dont les enfants DIRECTS portent la mise en page du design. Une enveloppe autour d'eux
       cassait le rendu — c'est ce que le PO a vu sur l'ordinateur le 2026-09-06. */
    .co-aside.is-collapsed .co-lines,
    .co-aside.is-collapsed .co-promo {
        display: none;
    }

    .co-aside.is-collapsed {
        max-height: none;
    }

    .co-aside.is-collapsed .co-aside-foot {
        margin-top: 0;
        padding-top: 14px;
    }
}

/* La bascule elle-même : pilule discrète dans l'en-tête sombre du récapitulatif, à 44 px de haut —
   la borne tactile. Au-dessus de 760 px, les deux colonnes tiennent côte à côte : rien à replier. */
.co-detail {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    min-height: 44px;
    padding: 0 14px;
    border: 1px solid rgb(245 236 215 / 28%);
    border-radius: 999px;
    background: rgb(245 236 215 / 8%);
    color: var(--cream, #f5ecd7);
    font-size: 11px;
    font-weight: 800;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    cursor: pointer;
}

.co-detail:hover,
.co-detail:focus-visible {
    background: rgb(245 236 215 / 16%);
}

.co-detail-chev {
    transition: transform 0.18s ease;
}

.co-aside.is-collapsed .co-detail-chev {
    transform: rotate(-180deg);
}

.co-aside-tools {
    display: flex;
    flex: none;
    align-items: center;
    gap: 10px;
}

/* Sur mobile, la bascule se fait discrète : 34 px de haut, un libellé court. Le compteur cède la
   place sous 480 px — le nombre d'articles se relit dans le détail, le titre non. */
@media (max-width: 760px) {
    .co-detail {
        min-height: 34px;
        padding: 0 11px;
        font-size: 10px;
        letter-spacing: 0.1em;
        gap: 5px;
    }

    .co-detail-chev {
        width: 12px;
        height: 12px;
    }
}

@media (max-width: 480px) {
    .co-aside-tools .co-count {
        display: none;
    }
}

@media (min-width: 761px) {
    /* Au-dessus de 760 px, les deux colonnes tiennent côte à côte : rien à replier, pas de bascule.
       Le masquage, lui, vit dans le media mobile — il n'y a donc rien à annuler ici, et surtout pas
       un `display` que le design n'a pas choisi pour ces blocs. */
    .co-detail {
        display: none;
    }
}

/* ------------------------------------------------------------------------------------------------
   PAGE D'ATTENTE DE LA VITRINE — ADR-0190, gabarit `design_handoff_maintenance` du 2026-09-06.

   Le gabarit porte ces règles en style inline dans sa page ; ce dépôt n'accepte pas de CSS inline
   massif, et la feuille du design est sous test de conformité octet par octet. Elles vivent donc
   ici, dans la feuille d'écarts chargée APRÈS le design — et remonteront dans `hisy-v3.css` le jour
   où le design les adoptera.

   Aucune couleur nouvelle : brun de fond, caramel du statut, crème du texte, et le vert de la
   marque WhatsApp, qui n'est pas le nôtre mais celui d'un bouton tiers.
   ------------------------------------------------------------------------------------------------ */
.mt {
    display: grid;
    min-height: 100dvh;
    padding: 32px 20px;
    position: relative;
    place-items: center;
    overflow: hidden;
    background: #2d1505;
}

.mt-bg {
    position: absolute;
    inset: 0;
    background-size: cover;
    background-position: 52% 38%;
}

/* LE DÉGRADÉ EST UN CALQUE, PAS UN FOND. Le fond porte l'IMAGE (ou le poster d'une vidéo) ; la
   vidéo, quand l'emplacement en est une, se glisse entre le fond et ce calque. Un dégradé dans le
   `background-image` aurait disparu sous la vidéo. */
.mt-bg::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 1;
    background-image: linear-gradient(168deg, #2d1505f2, #2d1505cc 46%, #241004f7);
}

/* La vidéo du hero d'accueil, si c'en est une (demande PO du 2026-09-07). Muette, en boucle, sans
   contrôle : c'est un décor. Qui a demandé moins de mouvement garde le poster, immobile. */
.mt-video {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: 52% 38%;
}

@media (prefers-reduced-motion: reduce) {
    .mt-video {
        display: none;
    }
}

/* Le cercle de contour en haut à droite : le motif des hero du site, repris tel quel. */
.mt-bg::after {
    content: "";
    position: absolute;
    z-index: 2;
    top: -140px;
    right: -120px;
    width: 460px;
    height: 460px;
    border: 1px solid #c8956a2e;
    border-radius: 50%;
    box-shadow: inset 0 0 0 40px #c8956a08, 0 0 0 44px #c8956a08;
}

.mt-inner {
    position: relative;
    z-index: 1;
    width: 100%;
    max-width: 660px;
    text-align: center;
    color: var(--cream, #f5ecd7);
}

/* CAPSULE D'IDENTITÉ ET DE STATUT. Le statut n'est pas une pastille pleine à côté du logo : deux
   étiquettes pleines côte à côte se disputent la place. C'est la convention des pages de statut. */
.mt-top {
    display: inline-flex;
    align-items: center;
    gap: 18px;
    margin-bottom: 32px;
    padding: 9px 20px 9px 14px;
    border: 1px solid #f5ecd71f;
    border-radius: 999px;
    background: #f5ecd70a;
}

.mt-brand {
    display: block;
    flex: none;
}

.mt-brand img {
    display: block;
    width: 96px;
    height: 48px;
    object-fit: contain;
}

.mt-top-rule {
    flex: none;
    width: 1px;
    height: 26px;
    background: #f5ecd72e;
}

.mt-status {
    display: inline-flex;
    flex: none;
    align-items: center;
    gap: 9px;
    color: var(--caramel-light, #d9a87c);
    font-size: 10.5px;
    font-weight: 800;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    white-space: nowrap;
}

.mt-dot {
    position: relative;
    flex: none;
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--caramel, #c8956a);
}

.mt-dot::after {
    content: "";
    position: absolute;
    inset: -4px;
    border: 1px solid var(--caramel, #c8956a);
    border-radius: 50%;
    opacity: 0.6;
    animation: mt-pulse 2.4s ease-out infinite;
}

@keyframes mt-pulse {
    0% { transform: scale(0.6); opacity: 0.7; }
    70% { transform: scale(1.5); opacity: 0; }
    100% { opacity: 0; }
}

/* Le halo s'arrête pour qui a demandé moins de mouvement ; le point, lui, reste visible. */
@media (prefers-reduced-motion: reduce) {
    .mt-dot::after {
        opacity: 0.35;
        animation: none;
    }
}

.mt-inner h1 {
    margin: 0;
    color: #fff;
    font-family: "Fraunces Variable", Fraunces, Georgia, serif;
    font-size: clamp(34px, 6vw, 58px);
    font-weight: 480;
    line-height: 1.02;
    letter-spacing: -0.04em;
    text-wrap: balance;
}

.mt-inner p.mt-msg {
    margin: 18px auto 0;
    max-width: 520px;
    color: #f5ecd7c4;
    font-size: 15px;
    line-height: 1.75;
    text-wrap: pretty;
}

.mt-back {
    display: inline-flex;
    align-items: baseline;
    gap: 9px;
    margin-top: 26px;
    padding: 13px 20px;
    border: 1px solid #f5ecd726;
    border-radius: 16px;
    background: #f5ecd70f;
}

.mt-back span {
    color: var(--caramel, #c8956a);
    font-size: 10.5px;
    font-weight: 800;
    letter-spacing: 0.14em;
    text-transform: uppercase;
}

.mt-back b {
    color: #fff;
    font-family: "Fraunces Variable", Fraunces, Georgia, serif;
    font-size: 17px;
    font-weight: 600;
}

.mt-shop {
    display: grid;
    gap: 14px;
    margin-top: 34px;
    padding-top: 28px;
    border-top: 1px solid #f5ecd71f;
    justify-items: center;
}

.mt-shop-kicker {
    color: var(--caramel, #c8956a);
    font-size: 10.5px;
    font-weight: 800;
    letter-spacing: 0.14em;
    text-transform: uppercase;
}

.mt-shop-addr {
    margin: 0;
    color: #fff;
    font-family: "Fraunces Variable", Fraunces, Georgia, serif;
    font-size: 19px;
    font-weight: 600;
}

.mt-hours {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 10px;
}

.mt-hour {
    display: flex;
    flex-direction: column;
    gap: 3px;
    min-width: 132px;
    padding: 9px 15px;
    border-radius: 12px;
    background: #f5ecd70d;
}

.mt-hour span {
    color: #f5ecd78c;
    font-size: 10px;
    font-weight: 750;
    letter-spacing: 0.1em;
    text-transform: uppercase;
}

.mt-hour b {
    color: #fff;
    font-size: 14px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

.mt-hour.is-today {
    background: #c8956a26;
}

.mt-hour.is-today span {
    color: var(--caramel-light, #d9a87c);
}

.mt-actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 10px;
    margin-top: 8px;
}

.mt-btn {
    display: inline-flex;
    align-items: center;
    gap: 9px;
    min-height: 50px;
    padding: 0 22px;
    border-radius: 999px;
    font-size: 13.5px;
    font-weight: 700;
    text-decoration: none;
    transition: background 0.2s, border-color 0.2s, color 0.2s;
}

.mt-btn-wa {
    background: #25d366;
    color: #08300f;
}

.mt-btn-wa:hover {
    background: #1fb857;
    text-decoration: none;
}

.mt-btn-ghost {
    border: 1px solid #f5ecd73d;
    color: var(--cream, #f5ecd7);
}

.mt-btn-ghost:hover {
    border-color: var(--caramel, #c8956a);
    background: #f5ecd714;
    text-decoration: none;
}

.mt-social {
    display: flex;
    justify-content: center;
    gap: 9px;
    margin-top: 24px;
}

.mt-social a {
    display: grid;
    width: 42px;
    height: 42px;
    border: 1px solid #f5ecd72e;
    border-radius: 50%;
    color: #f5ecd7bf;
    font-size: 15px;
    place-items: center;
    transition: background 0.2s, color 0.2s, border-color 0.2s;
}

.mt-social a:hover {
    border-color: var(--caramel, #c8956a);
    background: var(--caramel, #c8956a);
    color: #2d1505;
    text-decoration: none;
}

/* Bandeau d'aperçu : visible seulement pour qui est entré par jeton. */
.mt-preview {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 5;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 9px;
    padding: 11px 16px calc(11px + env(safe-area-inset-bottom, 0px));
    background: var(--caramel, #c8956a);
    color: #2d1505;
    font-size: 12.5px;
    font-weight: 700;
}

/* `:where()` RAMÈNE LA SPÉCIFICITÉ À CELLE DU GABARIT. Le design écrit `a { color }` nu (0,0,1) ;
   porté ici en `.mt a` (0,1,1), il battait `.mt-btn-wa` (0,1,0) et repeignait le bouton WhatsApp
   en caramel clair sur vert — contraste de 2:1, « le bouton n'est pas lisible » (PO). */
:where(.mt) a {
    color: var(--caramel-light, #d9a87c);
}

:where(.mt) a:hover {
    color: #fff;
}

@media (max-width: 520px) {
    .mt { padding: 26px 16px; }
    .mt-top { gap: 13px; padding: 8px 16px 8px 12px; }
    .mt-brand img { width: 84px; height: 42px; }
    .mt-status { font-size: 9.5px; letter-spacing: 0.12em; }
    .mt-hour { min-width: 118px; }
    .mt-btn { width: 100%; justify-content: center; }
}

/* ------------------------------------------------------------------------------------------------
   LA VITRINE EN PAUSE — ADR-0190 §8, arbitrage PO du 2026-09-06.

   En portée « boutique et paiement », l'accueil et les pages d'information restent SERVIES. Elles
   continuaient d'inviter à entrer dans la zone fermée : mesuré à l'écran sur l'accueil, 41 liens
   sur 78 y menaient, le bouton du panier était là, et la navigation proposait Boutique, Lookbook,
   Cartes cadeaux et « Vérifier une réservation ». La visiteuse cliquait et tombait sur un 503.

   ON FERME LES PORTES ICI, EN CSS, ET PAS DANS LE MARKUP : le chrome vient du design et n'est pas
   retouché (ADR-0003). `display: none` — et non une opacité ou un `pointer-events` — pour que le
   lien quitte AUSSI l'arbre d'accessibilité : un lien invisible mais annoncé au lecteur d'écran
   serait le même piège, en pire.
   ------------------------------------------------------------------------------------------------ */

/* Le bandeau : il dit la pause avant le premier clic, et donne l'adresse. */
.pause-bandeau {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-wrap: wrap;
    gap: 8px 14px;
    padding: 10px 20px;
    background: var(--brown, #2d1505);
    color: var(--cream, #f5ecd7);
    font-size: 13px;
    line-height: 1.45;
    text-align: center;
}

.pause-bandeau strong {
    color: #fff;
    font-weight: 700;
}

.pause-point {
    flex: none;
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--caramel, #c8956a);
}

.pause-lien {
    flex: none;
    padding: 5px 14px;
    border: 1px solid rgb(245 236 215 / 34%);
    border-radius: 999px;
    color: var(--cream, #f5ecd7);
    font-size: 12px;
    font-weight: 700;
    text-decoration: none;
    white-space: nowrap;
}

.pause-lien:hover {
    border-color: var(--caramel, #c8956a);
    background: rgb(245 236 215 / 10%);
    color: #fff;
    text-decoration: none;
}

@media (max-width: 600px) {
    .pause-bandeau { padding: 9px 14px; font-size: 12px; }
    .pause-texte { display: block; width: 100%; }
}

/* LES PORTES VERS LA ZONE FERMÉE. Chacune est nommée : une règle large masquerait demain un lien
   qu'on voulait garder, et personne ne saurait pourquoi. */
.vitrine-en-pause [data-open="cart"],
.vitrine-en-pause [data-open="favourites"],
.vitrine-en-pause .desktop-nav a[href="/boutique"],
.vitrine-en-pause .desktop-nav a[href="/lookbook"],
.vitrine-en-pause .quicknav a[href="/boutique"],
.vitrine-en-pause .quicknav a[href="/lookbook"],
.vitrine-en-pause .quicknav a[href="/suivi"],
.vitrine-en-pause .site-footer a[href="/boutique"],
.vitrine-en-pause .site-footer a[href="/lookbook"],
.vitrine-en-pause .site-footer a[href="/cartes-cadeaux"],
.vitrine-en-pause .site-footer a[href="/suivi"],
.vitrine-en-pause .site-footer a[href^="/boutique?"] {
    display: none !important;
}

/* LE `!important` EST ASSUMÉ, ET IL EST RARE. Ces sélecteurs luttent contre des règles du design
   qui posent `display: flex` sur les mêmes éléments, et qui arrivent après dans le bundle. Sans
   lui, le bouton du panier restait affiché — constaté à l'écran. */

/* LE PIED DE PAGE : on retire la PUCE, pas seulement le lien. Masquer le seul `<a>` laisserait
   une ligne vide dans la liste — un trou que personne ne sait lire. Le groupe « Explorer » ne
   contient QUE des liens de boutique : il disparaît en entier plutôt que de porter un titre
   au-dessus de rien. */
.vitrine-en-pause .site-footer .footer-link-group-explorer,
.vitrine-en-pause .site-footer li:has(a[href^="/boutique"]),
.vitrine-en-pause .site-footer li:has(a[href="/lookbook"]),
.vitrine-en-pause .site-footer li:has(a[href="/cartes-cadeaux"]),
.vitrine-en-pause .site-footer li:has(a[href="/suivi"]) {
    display: none !important;
}

/* LES DEUX SERVICES QUI PASSENT PAR LA CAISSE. La section « Les services H'isy » reste — elle
   parle de la maison, pas du stock — mais deux de ses cartes mènent à des pages fermées :
   acheter une carte cadeau, et vérifier une réservation. Les trois autres (guide des tailles,
   retrait, contact) n'ont rien à voir avec le paiement et restent servies. */
.vitrine-en-pause .service-card-grid a[href="/cartes-cadeaux"],
.vitrine-en-pause .service-card-grid a[href="/suivi"] {
    display: none !important;
}

/* ------------------------------------------------------------------------------------------------
   LES PUCES DU CURSEUR DE PRIX RETOMBENT SUR LA BARRE — demande PO du 2026-09-06.

   MESURÉ, PAS DEVINÉ. En sondant le point où la puce capte le pointeur (elle seule porte
   `pointer-events: auto`), son centre tombait 5,2 px SOUS le centre de la barre. Ce n'est pas un
   défaut de notre intégration : le design se contredit lui-même — son markup pose
   `style="height:12px"` en ligne sur les deux curseurs, sa feuille en demande 24 plus bas, et
   l'attribut gagne. Rendre la hauteur au design aggravait l'écart, mesuré à 11,2 px.

   LA CAUSE. WebKit aligne le HAUT de la puce sur le haut de sa piste de glissement, et non son
   centre sur le centre de la piste. Avec une piste de 3 px et une puce de 14, la puce descend donc
   de (14 − 3) / 2 = 5,5 px — les 5,2 px mesurés, à l'arrondi près. Firefox, lui, centre déjà :
   `::-moz-range-thumb` n'a rien à corriger, et lui appliquer la même marge le décalerait.

   La correction vit ici, dans la feuille d'écarts chargée après le design (ADR-0003) : la feuille
   de référence est sous test de conformité octet par octet.
   ------------------------------------------------------------------------------------------------ */
.catalog-price-slider input::-webkit-slider-thumb {
    margin-top: -5.5px;
}

/* Le tiroir de filtres du mobile a sa propre échelle : puce de 18 px sur la même piste de 3. */
.catalog-filter-dialog .catalog-price-slider input::-webkit-slider-thumb {
    margin-top: -7.5px;
}

/* ------------------------------------------------------------------------------------------------
   LA BARRE D'OUTILS DE LA BOUTIQUE TIENT SUR UNE LIGNE, SUR MOBILE — demande PO du 2026-09-06,
   maquette à l'appui : « 15 articles » à gauche, « Filtres » et le tri à droite.

   Sous 430 px, le design EMPILE : `flex-wrap: wrap` sur la barre et `flex: 1 0 100%` sur les
   actions renvoient les deux commandes à la ligne suivante, et la pilule de tri s'étire alors sur
   toute la largeur. Cela coûtait une rangée entière au-dessus de la grille — 103 px de haut pour
   une barre qui en demande 46 — sur l'écran où la place manque le plus.

   CINQ DÉFAUTS CORRIGÉS ENSEMBLE. Les trois premiers sur 375 px, les deux derniers signalés en
   revue et reproduits à l'écran :

   · la barre passait sur deux lignes (103 px) là où la maquette en veut une ;
   · « Filtres » faisait 44 px de haut, la pilule de tri 46. Un pixel de décalage en haut, un de
     plus en bas : côte à côte, elles n'étaient pas sur la ligne ;
   · le bouton de tri ne faisait que 129 px dans une pilule de 254, laissant 108 px de blanc à
     droite — la pilule se lisait comme coupée ;
   · à 320 px, avec le libellé le plus long, le déclencheur gardait sa largeur intrinsèque et son
     chevron sortait de 22 px hors de la pilule : une ellipse sans boîte contrainte ne tronque rien ;
   · le menu de tri, ancré à gauche de son champ par le design, débordait de 60 px à droite de
     l'écran — et la vitrine masque le débordement horizontal, donc ces options étaient invisibles
     et inatteignables.

   Le point de rupture est celui du design (430 px), pas un nouveau : dupliquer une valeur de
   rupture, c'est se préparer deux comportements qui divergent d'un pixel, sans savoir lequel des
   deux fichiers avait raison.
   ------------------------------------------------------------------------------------------------ */
@media (max-width: 430px) {
    /* Une ligne, et le compte cède la place le premier s'il en manque. */
    .catalog-toolbar {
        flex-wrap: nowrap;
        gap: 10px;
    }

    .catalog-result-count {
        flex: 0 1 auto;
        min-width: 0;
        white-space: nowrap;
    }

    /* Les actions reprennent leur largeur naturelle : la grille du design les faisait occuper
       toute la ligne, ce qui obligeait à passer à la ligne suivante. */
    .catalog-toolbar-actions {
        display: flex;
        flex: 0 1 auto;
        gap: 8px;
        min-width: 0;
    }

    /* Une seule hauteur pour les deux commandes, et c'est celle de la borne tactile.

       `width: auto` DÉFAIT UNE RÈGLE DU DESIGN, et il faut le dire : sous 430 px, il pose
       `width: 100%` sur « Filtres » parce que le bouton était alors seul dans sa colonne de
       grille. Sur une ligne partagée, ce 100 % le faisait passer de 81 à 237 px et poussait le
       tri hors de l'écran — mesuré. */
    .catalog-toolbar-actions .catalog-mobile-filter,
    .catalog-toolbar-actions .catalog-sort {
        min-height: 44px;
        height: 44px;
    }

    .catalog-toolbar-actions .catalog-mobile-filter {
        flex: 0 0 auto;
        width: auto;
    }

    /* Le tri cède le premier s'il manque de place, et tronque son libellé : mieux vaut
       « Meilleure réd… » qu'un chevron hors cadre. */
    .catalog-toolbar-actions .catalog-sort {
        flex: 0 1 auto;
        min-width: 0;
    }

    /* La pilule de tri épouse son contenu : le chevron suit le libellé, comme sur la maquette. */
    .catalog-toolbar-actions .catalog-sort-field {
        flex: 0 1 auto;
        min-width: 0;
    }

    .catalog-toolbar-actions .catalog-sort-button {
        width: auto;
        max-width: 100%;
        min-height: 40px;
    }

    /* SANS BOÎTE CONTRAINTE, UNE ELLIPSE NE TRONQUE RIEN. Le déclencheur est en `inline-flex` :
       laissé à sa largeur intrinsèque, il gardait 157 px dans un champ de 118 à 320 px, et son
       chevron sortait de 22 px hors de la pilule — mesuré. Le `max-width` ci-dessus le borne au
       champ ; ce `min-width: 0` autorise le libellé, enfant de flex, à rétrécir sous sa largeur
       de contenu, sans quoi il pousse le chevron malgré tout. */
    .catalog-toolbar-actions .catalog-sort-button > span {
        min-width: 0;
    }

    /* LE MENU DE TRI RESTE DANS L'ÉCRAN. Le design l'ancre à GAUCHE du champ (`left: 0`), ce qui
       convenait quand la pilule occupait toute la largeur. Sur une ligne partagée, le champ est
       collé à droite : la liste débordait de 60 px à 375 px, et la vitrine masque le débordement
       horizontal — les options de droite étaient donc invisibles ET inatteignables. On l'ancre à
       droite, et on la borne à la gouttière de la page pour qu'elle ne sorte pas par l'autre bord
       sur un très petit écran. */
    .catalog-toolbar-actions .catalog-sort-list {
        left: auto;
        right: 0;
        max-width: calc(100vw - 32px);
    }

    /* Le libellé se tronque plutôt que de pousser le chevron hors de la pilule : « Meilleure
       réduction » est le plus long des cinq tris, et c'est lui qui décide si la ligne tient. */
    .catalog-toolbar-actions .catalog-sort-button > span {
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
}

/* ── L'ASSISTANT DES CARTES CADEAUX NE SAUTE PAS D'ÉTAPE (PO du 2026-09-07) ──────────────────
 * `services.js` n'avance plus sur un clic dans la frise ; les étapes à venir portent
 * `aria-disabled="true"` et ne doivent pas promettre un clic. Les étapes faites restent des retours. */
.gift-steps li[aria-disabled="true"] { cursor: default; }

/* ── LE CONSENTEMENT À DEUX COUCHES (ADR-0194) ─────────────────────────────────────────────
 * Le bandeau existant gagne un lien de refus de même rang et un bouton secondaire ; le panneau est
 * un dialogue posé sur un voile. Jetons du design uniquement, aucune classe du kit détournée. */
.consentement-lien {
    padding: 9px 6px;
    border: 0;
    background: none;
    color: var(--muted, #7a6a57);
    font: inherit;
    font-size: 0.82rem;
    text-decoration: underline;
    text-underline-offset: 2px;
    cursor: pointer;
}
.consentement-lien:hover,
.consentement-lien:focus-visible { color: var(--brown, #2d1505); }
.consentement-btn-secondaire {
    background: none;
    color: var(--caramel-dark, #a8734e);
}
.consentement-voile {
    position: fixed;
    inset: 0;
    z-index: 210;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 16px;
    background: rgba(45, 21, 5, 0.45);
}
.consentement-panneau {
    position: relative;
    width: min(680px, 100%);
    max-height: calc(100vh - 32px);
    overflow: auto;
    padding: 28px 28px 22px;
    border: 1px solid var(--line, #e7dccb);
    border-radius: 18px;
    background: var(--cream-light, #fbf7ec);
    box-shadow: 0 24px 60px rgba(45, 21, 5, 0.28);
    font-size: 0.86rem;
    line-height: 1.55;
    color: var(--muted, #7a6a57);
}
.consentement-panneau h2 {
    margin: 0 0 12px;
    font-size: 1.25rem;
    color: var(--brown, #2d1505);
}
.consentement-panneau h2:focus { outline: none; }
.consentement-panneau p { margin: 0 0 12px; }
.consentement-panneau a { color: var(--caramel-dark, #a8734e); font-weight: 600; text-underline-offset: 2px; }
.consentement-sous-titre {
    margin: 18px 0 6px;
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}
.consentement-finalites {
    margin: 0 0 14px;
    padding: 0;
    list-style: none;
}
.consentement-finalites li {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    padding: 12px 0;
    border-top: 1px solid var(--line, #e7dccb);
}
.consentement-finalites li strong { display: block; color: var(--brown, #2d1505); }
.consentement-finalites li small { display: block; font-size: 0.76rem; }
.consentement-choix { display: flex; flex: 0 0 auto; gap: 8px; }
.consentement-choix button {
    padding: 7px 14px;
    border: 1.5px solid var(--line, #e7dccb);
    border-radius: 9px;
    background: #fff;
    color: var(--muted, #7a6a57);
    font: inherit;
    font-size: 0.8rem;
    font-weight: 600;
    cursor: pointer;
}
.consentement-choix button[aria-pressed="true"] {
    border-color: var(--caramel-dark, #a8734e);
    background: var(--caramel-dark, #a8734e);
    color: #fff;
}
.consentement-note { font-size: 0.78rem; }
.consentement-fermer {
    position: absolute;
    top: 12px;
    right: 14px;
    width: 34px;
    height: 34px;
    border: 0;
    border-radius: 50%;
    background: none;
    color: var(--muted, #7a6a57);
    font-size: 1.4rem;
    line-height: 1;
    cursor: pointer;
}
.consentement-fermer:hover,
.consentement-fermer:focus-visible { background: rgba(45, 21, 5, 0.08); color: var(--brown, #2d1505); }
@media (max-width: 560px) {
    .consentement-finalites li { flex-direction: column; align-items: flex-start; }
}

/* ── 12. Les micro-libellés sous 9 px passent à 11 px ───────────────────────
 *
 * Écart déclaré (ADR-0003 §2), accord PO du 2026-09-08 — audit responsive du 2026-09-07,
 * constat n° 2 : « polices de 8 px issues du design de référence », approuvé.
 *
 * CE QUE C'ÉTAIT. `hisy-v3.css` descend sous 9 px sur 76 sélecteurs : 8 px pour les tampons du
 * héros, les pastilles de pièce, les libellés de TOUS les formulaires (contact, suivi, avis, carte
 * cadeau, commande, portail affilié), les en-têtes du guide des tailles, les mentions du panier et
 * du récapitulatif ; 8,5 px pour les libellés de la navigation basse sous 380 px ; et jusqu'à 7 px
 * — 6 px pour une légende de la page « à propos » — sur les téléphones étroits, là où l'écran est
 * le plus petit. Ce sont des CAPITALES espacées de .1 à .2 em, la forme la moins lisible qui soit à
 * petite taille, et elles portent le texte qu'une cliente doit lire pour commander.
 *
 * POURQUOI 11 PX, ET PAS 12 NI 10. Le corps de texte du design est à 12 px : monter à 12 effacerait
 * la hiérarchie que ces libellés servent à créer. Rester à 10 ne réglerait rien — l'audit a relevé
 * le seuil de lisibilité à 11 px. 11 px est donc le plus petit pas qui rend le texte lisible sans
 * toucher au rang.
 *
 * LE SEUIL EST 9 PX, ET IL EST DÉLIBÉRÉ. L'accord du PO porte sur « les polices de 8 px » ; tout ce
 * qui est PLUS PETIT que 8 px l'est a fortiori, d'où la borne à 9. Les micro-libellés que le design
 * pose à 9 et 10 px restent intacts : ils n'ont pas été arbitrés, et les toucher élargirait l'écart
 * au-delà de ce qui a été accordé.
 *
 * LA LISTE EST CELLE DU DESIGN, RECOPIÉE. Les sélecteurs sont repris tels quels de `hisy-v3.css` —
 * même ordre, même écriture — pour qu'une relecture puisse les comparer ligne à ligne.
 * `LisibiliteVitrineTest` échoue si le design en ajoute un que cette liste ne couvre pas.
 */
.hero-stamp span,
.hero-stamp small,
.hero-caption span,
.product-badge,
.product-discount,
.product-flash,
.store-life-progress small,
.footer-payment-tooltip small,
.search-results small,
.mobile-menu nav span,
.hero-vertical-note,
.filter-panel span,
.lookbook-hero-image span,
.about-v2-media figcaption,
.contact-cards small,
.contact-form-wrap form label,
.contact-form-wrap form > small,
.cart-lines small,
.order-summary > small,
.store-gallery-item > span small,
.size-table th,
.order-summary nav,
.payment-current small,
.payment-future small,
.checkout-legal,
.look-detail-notes small,
.tracking-privacy small,
.tracking-form label,
.tracking-form > small,
.status-preview small,
.gift-benefits small,
.gift-card-visual > small,
.gift-controls legend,
.gift-controls > label,
.gift-controls > small,
.partner-hero-art small,
.partner-apply-card > small,
.partner-login-brand > div small,
.partner-login-form-wrap form > label,
.review-card footer span,
.faq-list summary > span,
.rating-bars > div,
.review-form-copy > div small,
.review-form-card > label,
.review-form-card legend,
.review-form-card > small,
.checkout-summary-items small,
.checkout-summary-items b,
.checkout-order-summary > small,
.checkout-stepper > span small,
.checkout-fields label,
.checkout-choice legend,
.checkout-payment-options legend,
.checkout-payment-options small,
.checkout-consents label,
.checkout-complete > small,
.lookbooks-filters small,
.lookbook-hotspot-status,
.lookbook-products-heading > span,
.lookbook-product-status,
.co-date small,
.pdp-thumb-slot small { font-size: 11px; }

/* Le design ne descend là qu'en dessous de 520 px : la règle maison l'y suit, et laisse
   intacte la taille au-dessus. */
@media (max-width: 520px) {
    .contact-v2-map-heading small,
    .arrival-nav .arrival-autoplay-toggle,
    .product-badge,
    .product-discount,
    .product-flash,
    .store-life-demo-label,
    .about-v2-media figcaption,
    .lookbooks-gallery-heading .eyebrow,
    .lookbooks-card-number,
    .lookbook-hotspot-help,
    .lookbook-dialog-kicker,
    .lookbook-product-copy small { font-size: 11px; }
}

/* Le design ne descend là qu'en dessous de 700 px : la règle maison l'y suit, et laisse
   intacte la taille au-dessus. */
@media (max-width: 700px) {
    .size-card-label-v3 span { font-size: 11px; }
}

/* Le design ne descend là qu'en dessous de 380 px : la règle maison l'y suit, et laisse
   intacte la taille au-dessus. */
@media (max-width: 380px) {
    .quicknav-label { font-size: 11px; }
}

/* UN 8 PX QUI NE VIENT PAS DU DESIGN. Sur la page des avis, les pourcentages des barres de notation
   sont des `<small>` : le navigateur les réduit d'un cinquième, et 10 px de parent donnent 8 px à
   l'écran. Aucune règle à surcharger — il faut annuler la réduction. `inherit` rend la taille du
   parent, 10 px, sans inventer de valeur ni toucher au 10 px du design (hors accord). */
.rating-bars small { font-size: inherit; }

/* ── 13. Les zones qui défilent le disent ───────────────────────────────────
 *
 * Écart déclaré (ADR-0003 §2), audit responsive du 2026-09-07, constat n° 6.
 *
 * CE QUI MANQUAIT. Sur une fiche pièce à 375 px, la barre de sections mesure 517 px dans un cadre
 * de 342 : « Avis clients » et « Retrait & paiement » sont hors de l'écran, et rien ne dit qu'ils
 * existent. Une cliente qui ne voit pas l'onglet des avis ne le cherche pas.
 *
 * MÊME TECHNIQUE QUE CÔTÉ ADMIN, et pour la même raison : deux voiles attachés au contenu
 * (`local`) effacent deux ombres attachées au cadre (`scroll`) tant qu'on est au bord. L'indice
 * paraît quand il y a quelque chose à voir, et pas avant. Aucune balise ajoutée, aucun script.
 *
 * Le voile reprend le fond de la barre — `#fdf8f2`, celui que le design lui donne — et la règle
 * garde son flou d'arrière-plan : seule la couche de fond change.
 */
.pdp-navbar {
    background:
        linear-gradient(to right, #fdf8f2 30%, rgba(253, 248, 242, 0)) left center / 34px 100% no-repeat local,
        linear-gradient(to left, #fdf8f2 30%, rgba(253, 248, 242, 0)) right center / 34px 100% no-repeat local,
        radial-gradient(farthest-side at 0 50%, rgba(45, 21, 5, .16), rgba(45, 21, 5, 0)) left center / 13px 100% no-repeat scroll,
        radial-gradient(farthest-side at 100% 50%, rgba(45, 21, 5, .16), rgba(45, 21, 5, 0)) right center / 13px 100% no-repeat scroll,
        #fdf8f2;
}

/* ── 14. Les liens du pied de page se touchent au doigt ─────────────────────
 *
 * Écart déclaré (ADR-0003 §2), audit responsive du 2026-09-07, constat n° 7.
 *
 * MESURÉ À 375 px : « Confidentialité » 75 × 18, « Mentions légales » 88 × 18, « CGV » 24 × 18,
 * « Retours » 40 × 18, « Cookies » 42 × 18. Le minimum normatif est 24 px de haut (WCAG 2.5.8) :
 * tous échouent, et ce sont les pages que la loi impose d'atteindre.
 *
 * `pointer: coarse` ET RIEN D'AUTRE, comme côté admin : au doigt la surface manque, à la souris
 * elle suffit. Le DESSIN ne bouge pas — la surface est étendue par un pseudo-élément transparent
 * centré sur le lien, sans effet sur la mise en page : agrandir le texte aurait défait l'alignement
 * du pied de page, qui est une grille. */
@media (pointer: coarse) {
    .footer-bottom nav[aria-label="Liens légaux"] a,
    .footer-bottom-copy a {
        position: relative;
    }

    .footer-bottom nav[aria-label="Liens légaux"] a::after,
    .footer-bottom-copy a::after {
        content: '';
        position: absolute;
        left: 0;
        right: 0;
        top: 50%;
        transform: translateY(-50%);
        height: 44px;
    }
}

/* ── 15. La section « Dimensions » de la fiche, revue ───────────────────────
 *
 * Écart déclaré (ADR-0003 §2), demande PO du 2026-09-08 : « revoir le design de la tab Dimensions
 * de la page produit ».
 *
 * CE QUI N'ALLAIT PAS. La grille du kit (`pdp-facts`) coupe l'espace en DEUX colonnes : c'est juste
 * pour « Rayon · Catégorie », deux mots ; ce ne l'est pas pour « d'une aisselle à l'autre, à plat :
 * doublez cette largeur pour un tour de poitrine », qui tombait sur cinq lignes dans une demi-
 * largeur, et la valeur se noyait dans sa définition faute de contraste.
 *
 * CE QUE LA RÈGLE FAIT. Une mesure par ligne, sur toute la largeur. Sur la ligne : le libellé à
 * gauche, la valeur à DROITE, en chiffres tabulaires — l'œil descend une colonne de nombres, comme
 * sur une fiche technique. Dessous, la définition en clair, à 12 px, sur toute la largeur. Le
 * vocabulaire du kit ne change pas : ce sont les mêmes `pdp-fact`, `dt` et `dd`, avec un
 * modificateur sur la grille.
 */
.pdp-facts-dimensions {
    grid-template-columns: minmax(0, 1fr);
    margin-top: 18px;
}

.pdp-facts-dimensions .pdp-fact {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: baseline;
    column-gap: 18px;
    padding: 13px 0;
}

/* La règle du kit met en retrait une cellule sur deux : en une seule colonne, elle n'a plus lieu. */
.pdp-facts-dimensions .pdp-fact:nth-child(2n) {
    padding-left: 0;
    border-left: 0;
}

.pdp-facts-dimensions .pdp-fact dd {
    display: contents;
}

.pdp-facts-dimensions .pdp-fact dd b {
    grid-column: 2;
    grid-row: 1;
    font-size: 1rem;
    font-weight: 700;
    color: var(--brown, #2d1505);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.pdp-facts-dimensions .pdp-fact dd span {
    grid-column: 1 / -1;
    grid-row: 2;
    margin-top: 4px;
    color: var(--muted, #8a7460);
    font-size: 12px;
    font-weight: 400;
    line-height: 1.5;
}
