/* ═══════════════════════════════════════════════════════════════════════════
   BoutiqueOuf — couche d'apparence (violet & blanc)
   Chargée APRÈS style.css : elle affine sans réécrire la base, donc rien ne casse.
   Palette alignée sur Manage (Colors.primary #6B21A8) pour que les deux apps
   se répondent : la gérante et sa cliente doivent voir la même marque.
   ═══════════════════════════════════════════════════════════════════════════ */

:root {
  /* Violet de MARQUE, jamais écrasé. `--primary` est réassigné en JS par la couleur choisie par
     la gérante (sa vitrine lui appartient) : les éléments BoutiqueOuf — barre du logo, assistante
     — doivent donc s'appuyer sur --bo-* pour rester reconnaissables d'une boutique à l'autre. */
  --bo-purple: #6B21A8;
  --bo-purple-dark: #581C87;
  --bo-purple-light: #A855F7;

  --primary: #6B21A8;
  --primary-dark: #581C87;
  --primary-light: #A855F7;
  --primary-bg: #F3E8FF;
  --secondary: #2E8B3C;          /* vert du logo, en touche */
  --bg: #FAFAFA;                 /* blanc neutre : aucune teinte, il ne concurrence rien */
  --surface: #FFFFFF;
  --text: #1A1A2E;
  --text-sec: #6B7280;
  --border: #ECE9F1;
  --radius: 16px;
  --radius-sm: 12px;
  /* ── LA TRANCHE ET LA LUEUR SUIVENT LA COULEUR DU BOUTON ────────────────────────────────
     JM, le 9 octobre 2026 : « ce bouton est rouge, l'ombre ne peut pas être violette. » La
     tranche sous « CONTINUER » était écrite #4C1D95 et sa lueur en violet : sur une boutique
     rose ou rouge, le bouton portait le dessous d'une autre marque.
     Un NOIR TRANSLUCIDE assombrit n'importe quelle couleur sans la teinter — c'est le repli,
     valable partout. Juste en dessous, color-mix rend la lueur à la couleur du bouton pour les
     navigateurs qui savent la calculer. */
  --bo-tranche: rgba(0,0,0,.34);
  --bo-tranche-appui: rgba(0,0,0,.30);
  --bo-lueur: rgba(0,0,0,.20);
  --bo-lueur-appui: rgba(0,0,0,.17);
  /* Ombres douces et colorées : une ombre grise donne un rendu « bureautique ». */
  --shadow-sm: 0 1px 3px rgba(88, 28, 135, .06), 0 1px 2px rgba(88, 28, 135, .04);
  --shadow-md: 0 4px 16px rgba(88, 28, 135, .10);
  --shadow-lg: 0 12px 32px rgba(88, 28, 135, .18);
}

@supports (color: color-mix(in srgb, red 50%, blue)) {
  :root {
    --bo-lueur: color-mix(in srgb, var(--primary, #6B21A8) 42%, transparent);
    --bo-lueur-appui: color-mix(in srgb, var(--primary, #6B21A8) 36%, transparent);
  }
}

/* Teinte des barres. La couleur de la gérante est ramenée vers un fond profond presque neutre :
   on garde son identité, on retire la saturation qui donne l'impression d'un aplat au feutre.
   `color-mix` fait ce travail QUELLE QUE SOIT sa couleur — rose vif, rouge, turquoise — là où des
   valeurs écrites à la main n'auraient corrigé que le violet. Les deux lignes ci-dessous sont le
   repli pour les navigateurs sans color-mix ; le bloc @supports les remplace ensuite. */
/* ═══ TOUT DESCEND DE `--primary`, LA COULEUR VIVE DE LA GÉRANTE ═══
   La barre, les icônes de rayons, le bouton de l'assistante, les fonds de promotion.

   ON A ESSAYÉ DE PARTIR DU MÉLANGE des deux couleurs de la boutique — celui dont naît le
   dégradé de la barre — pour que tout vienne d'une seule source. C'était une mauvaise idée, et
   voici pourquoi : la seconde couleur est souvent très CLAIRE (Élégance d'Afrique a #ECF0F1,
   un blanc cassé). Mélangée à parts égales, elle délave la teinte. Les icônes de rayons et le
   bouton de l'assistante, qui étaient d'un violet franc, sont devenus gris-mauve. Toute la
   vitrine avait l'air éteinte.

   La couleur vive, c'est la PRIMAIRE. C'est elle que la gérante a choisie, c'est elle qu'on
   garde partout. La secondaire ne sert qu'au dégradé de la barre, où elle apporte une nuance
   sans jamais délaver — c'est le rôle qu'elle a toujours eu. */
:root { --head-1: #2C1C40; --head-2: #1A1027; }
@supports (color: color-mix(in oklab, red, blue)) {
  :root {
    /* LES DEUX BOUTS DU DEGRADE, TELS QUE LE JS LES A CALCULES — sans les retoucher ici.
       `--head-2` reprenait `--teinte-pied` et l'assombrissait ENCORE de 26 % vers un
       presque-noir : la barre plongeait donc deux fois, une fois dans le calcul et une fois
       dans la feuille de style, et le reglage de la gerante arrivait en bout de course dejà
       eteint. Les deux variables portent maintenant exactement ce que la barre affiche. */
    --head-1: var(--teinte-base, var(--accent));
    --head-2: var(--teinte-pied, var(--accent));
  }
}

/* Fond général : BLANC, et rien d'autre.
   Les halos colorés partaient d'une bonne intention — éviter la platitude — mais une fois toute
   l'application ramenée à une seule teinte, ils sont devenus la dernière chose qui mettait de la
   couleur là où l'œil n'a rien à lire. Un blanc franc, à peine plus sourd que celui des cartes,
   laisse la marchandise porter la page : c'est exactement ce que font Vinted, Shopify et Amazon.
   Le grain ci-dessous suffit à lui donner de la matière. */
body { background: var(--bg); }

/* Le vrai travail est ici : un GRAIN, à 3 % d'opacité.
   C'est ce qui sépare une interface soignée d'un site vitrine, et personne ne peut le nommer en
   regardant l'écran. Il fait deux choses : il casse le « banding » — ces marches visibles dans
   les dégradés très pâles sur écran de téléphone — et il donne au blanc une matière de papier
   plutôt qu'une surface plastique. Le bruit est généré par un filtre SVG en data-URI : aucune
   image à télécharger, aucune requête réseau.
   PAS de mix-blend-mode : une couche fusionnée en plein écran force TOUT le document dans un
   groupe de fusion, que le navigateur doit repeindre à chaque défilement — sur un téléphone
   d'entrée de gamme, c'est exactement le genre de détail décoratif qui rend une page poussive.
   Trois pour cent d'opacité simple donnent le même rendu pour rien. */
body::before {
  content: '';
  position: fixed; inset: 0; z-index: -1; pointer-events: none;
  opacity: .03;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* Les cartes passent en blanc PLEIN. Le décor coloré qu'elles laissaient transparaître n'existe
   plus, et sur un fond presque blanc un blanc translucide vire au gris : les cartes se seraient
   fondues dans la page. Blanc pur sur blanc cassé — ce sont le filet et l'ombre qui les
   détachent, pas un écart de couleur. */
.product-card, .accordion-section {
  background: #FFFFFF;
}

/* ── En-tête boutique ─────────────────────────────────────────────────────── */
/* Même traitement que .bo-head : teinte assourdie + trame, pour que toutes les barres de l'app
   parlent d'une seule voix.

   `--head-motif` porte la trame choisie par la gérante (Manage → Boutique → Identité → Fond de
   l'en-tête). Elle est posée par `shopHeaderGradient()` dans js/api.js. Le repli écrit ici, ce
   sont les hachures : tant que le script n'a pas répondu — ou si la boutique n'a jamais rien
   choisi — la barre a l'aspect qu'elle a toujours eu, jamais un fond nu. */
.header {
  background:
    var(--head-motif, repeating-linear-gradient(135deg, rgba(255, 255, 255, .035) 0 1px, transparent 1px 7px)),
    linear-gradient(180deg, rgba(255, 255, 255, .07), transparent 46%),
    linear-gradient(165deg, var(--head-1), var(--head-2));
  border-radius: 0 0 24px 24px;          /* arrondi bas : le bandeau « pose » sur le contenu */
  box-shadow: 0 2px 14px rgba(17, 10, 28, .12);
  padding-bottom: 18px;
}
.header .shop-name { letter-spacing: -.3px; }
.header .logo img { border-radius: 14px; }

/* Panier de l'en-tête : pastille en verre, plus un emoji nu */
.cart-header {
  width: 44px; height: 44px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 14px;
  background: rgba(255, 255, 255, .16);
  backdrop-filter: blur(6px);
  border: 1px solid rgba(255, 255, 255, .22);
  transition: transform .15s ease, background .15s ease;
}
.cart-header:active { transform: scale(.94); background: rgba(255, 255, 255, .26); }
.cart-header .cart-icon { font-size: 0; }         /* masque l'emoji, remplacé par le SVG */
.cart-header .cart-icon svg { width: 22px; height: 22px; color: #fff; display: block; }
/* Posée sur la pastille colorée de l'en-tête : son liseré reprend ce fond-là. Taille, rayon et
   couleur du compteur viennent de la géométrie commune (css/style.css). */
.cart-header .badge { --badge-bordure: var(--primary-dark); }

/* Bouton menu dans l'en-tête des pages autres que l'accueil (le carrousel a le sien). */
.bo-head-menu {
  position: absolute; top: 12px; left: 12px; z-index: 5;
  width: 40px; height: 40px; border-radius: 12px;
  border: 1px solid rgba(255, 255, 255, .24);
  background: rgba(255, 255, 255, .16); backdrop-filter: blur(6px);
  color: #fff; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
}
.bo-head-menu:active { background: rgba(255, 255, 255, .3); }
.bo-head-menu svg { width: 20px; height: 20px; }
.header { position: relative; }   /* ancre le bouton ci-dessus */

/* ── Navigation du bas ──────────────────────────────────────────────────────
   CE BLOC NE DÉCRIT PLUS RIEN, ET C'EST VOULU (JM, le 21 octobre 2026, deux captures :
   « il faut que le footer soit exactement ce qu'on a sur l'image 1 »).

   Il datait de la barre COLLÉE AU BORD, pleine largeur, sans hauteur fixe. Depuis que le pied
   est une PASTILLE FLOTTANTE de 66 px (style.css, « La navigation du bas FLOTTE »), chacune de
   ses trois déclarations cassait quelque chose, et toutes les trois n'étaient visibles QUE sur
   iPhone — d'où un pied correct en PWA et de travers dans l'IPA :

   • `padding: 6px 0 calc(6px + env(safe-area-inset-bottom))`. Mesuré sur iPhone 17 Pro,
     l'encart du bas vaut 34 px. Avec `box-sizing: border-box` et `height: 66px`, la boîte de
     contenu tombait à 66 − 6 − 40 = 20 px pour une icône de 24 px et son mot : les quatre
     icônes étaient tranchées en haut de la pastille et la moitié basse restait vide. Dans un
     navigateur l'encart vaut ZÉRO, la boîte gardait ses 54 px, et tout paraissait normal.
     Le fond n'a plus à remonter sous la ligne de gestes : la pastille ne la touche plus, elle
     flotte à `--bo-pastille-bas` au-dessus.

   • `border-top: 1px solid var(--border)` et `box-shadow: 0 -2px 20px`. style.css pose
     `border-top: 0` exprès — « plus de filet gris en haut ». Ces deux lignes le remettaient :
     le trait et le halo gris que JM voyait juste au-dessus du pied, et qui recouvraient les
     boutons d'« À propos de l'application ».

   Le verre dépoli, lui, n'est pas perdu : il est redéclaré plus bas (« le contenu défile
   dessous »), avec le repli `@supports` qui va avec. */
.footer-nav a {
  gap: 3px;
  font-size: 11px;
  font-weight: 600;
  color: #8A8A9A;
  /* 4 px et non 14 : depuis que les quatre cases se partagent la pastille en parts égales
     (style.css, « LES QUATRE CASES ONT LA MÊME LARGEUR »), un rembourrage large ne décale
     plus rien, il rogne seulement la place du mot. Ce fichier est chargé APRÈS style.css :
     sans cette ligne, c'est le 14 px d'ici qui gagnerait. */
  padding: 6px 4px 4px;
  border-radius: 14px;
  transition: color .18s ease, transform .18s ease;
}
.footer-nav a .nav-icon { font-size: 0; line-height: 0; }   /* neutralise l'emoji résiduel */
.footer-nav a .nav-icon svg { display: block; width: 24px; height: 24px; }
/* Onglet actif : ENCRE, pas couleur.
   Le rose de la boutique posé sur l'onglet actif faisait deux choses de travers. Il criait — une
   couleur saturée en bas d'écran attire l'œil en permanence alors que la barre n'est qu'un moyen
   de circuler. Et il datait : la pastille de couleur + le point en dessous, c'est la convention
   des applications d'il y a dix ans. Vinted, Airbnb, Instagram font l'inverse — actif = noir et
   icône pleine, inactif = gris et icône en trait. L'état se lit à la MASSE, pas à la teinte, et
   la couleur reste disponible pour ce qui compte vraiment : les prix, les promotions.
   Bénéfice discret : ça marche aussi bien pour une boutique turquoise que pour une rouge. */
.footer-nav a.active { color: #16121F; font-weight: 700; }
.footer-nav a.active .nav-icon { transform: translateY(-1px); }
.footer-nav a:active { transform: scale(.92); }
/* Le compteur du pied prend la géométrie commune (css/style.css), liseré blanc compris :
   il repeignait ici la même chose dans une taille différente. */

/* ── Cartes produit ───────────────────────────────────────────────────────── */
.product-card {
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
  border: 1px solid var(--border);
  overflow: hidden;
  transition: transform .16s ease, box-shadow .16s ease;
}
.product-card:active { transform: scale(.97); box-shadow: var(--shadow-md); }
.product-card .info { padding: 10px 12px 12px; }
.product-card .price { color: var(--primary); font-weight: 800; }

/* Bouton « + » sur la vignette — remplace l'emoji caddie.
   Pastille blanche translucide posée sur la photo : visible sur clair comme sur foncé, et qui
   ne cache presque rien du produit. Devient verte (✓) quand l'article est au panier. */
.cart-overlay-btn {
  position: absolute; top: 8px; right: 8px; z-index: 3;
  width: 34px; height: 34px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: rgba(255, 255, 255, .94);
  color: var(--primary);
  border: 1px solid rgba(107, 33, 168, .14);
  box-shadow: 0 2px 8px rgba(24, 6, 46, .18);
  cursor: pointer; padding: 0;
  transition: transform .15s cubic-bezier(.34, 1.56, .64, 1), background .15s ease, color .15s ease;
}
.cart-overlay-btn:active { transform: scale(.86); }
.cart-overlay-btn .ico-ok { display: none; }
.cart-overlay-btn.in-cart { background: var(--secondary); color: #fff; border-color: transparent; }
.cart-overlay-btn.in-cart .ico-add { display: none; }
.cart-overlay-btn.in-cart .ico-ok { display: block; }
.cart-overlay-btn .cart-icon, .cart-overlay-btn .cart-x { display: none; }  /* anciens emojis neutralisés */

/* Info produit — hiérarchie façon marketplace : nom lisible sur 2 lignes, PRIX dominant. */
.product-card .name {
  font-size: 13px; line-height: 1.35; font-weight: 600; color: var(--text);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; min-height: 2.7em;      /* hauteur constante : les prix s'alignent entre cartes */
  margin-bottom: 4px;
}
.product-card .price {
  font-size: 17px; font-weight: 800; color: var(--text);
  letter-spacing: -.4px; line-height: 1.1;
}
/* « FCFA » en plus petit : l'œil accroche le nombre, pas la devise (convention Amazon). */
.product-card .price .cur { font-size: 12px; font-weight: 700; color: var(--text-sec); margin-left: 2px; }
.product-card .stock-ok { color: var(--secondary); font-weight: 700; font-size: 11px; margin-top: 3px; }
.product-card .stock-low { color: #C2410C; font-weight: 700; font-size: 11px; margin-top: 3px; }
.product-card .stock-out { color: #9CA3AF; font-weight: 600; font-size: 11px; margin-top: 3px; }

/* ── Barre supérieure : logo BoutiqueOuf à gauche + menu (façon Amazon) ───── */
.bo-topbar {
  position: sticky; top: 0; z-index: 90;
  display: flex; align-items: center; gap: 10px;
  padding: 8px 12px;
  /* Suit la couleur choisie par la gérante : sa vitrine, ses couleurs — mais assourdie et
     texturée comme .bo-head, sinon la barre écrase tout ce qu'il y a en dessous. */
  background:
    var(--head-motif, repeating-linear-gradient(135deg, rgba(255, 255, 255, .035) 0 1px, transparent 1px 7px)),
    linear-gradient(165deg, var(--head-1), var(--head-2));
  border-bottom: 1px solid rgba(255, 255, 255, .09);
  color: #fff;
}
.bo-topbar .bo-brand { display: flex; align-items: center; gap: 8px; text-decoration: none; color: #fff; }
.bo-topbar .bo-brand img { width: 30px; height: 30px; border-radius: 9px; background: #fff; object-fit: contain; }
.bo-topbar .bo-brand span { font-weight: 800; font-size: 15px; letter-spacing: -.2px; }
.bo-topbar .bo-spacer { flex: 1; }
.bo-burger {
  width: 38px; height: 38px; border-radius: 11px; border: 1px solid rgba(255,255,255,.22);
  background: rgba(255,255,255,.14); color: #fff; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
}
.bo-burger:active { background: rgba(255,255,255,.26); }
.bo-burger svg { width: 20px; height: 20px; }

/* Panneau déroulant : promo de la gérante + accès rapides */
.bo-menu {
  position: fixed; top: 0; bottom: 0; left: var(--bo-gauche); width: min(var(--bo-cadre), 100vw); z-index: 200;
  background: rgba(24, 6, 46, .45); backdrop-filter: blur(2px);
  display: none;
}
.bo-menu.open { display: block; }
.bo-menu-sheet {
  position: absolute; top: 0; right: 0; bottom: 0;
  width: min(320px, 86vw); background: #fff;
  box-shadow: -12px 0 40px rgba(24, 6, 46, .3);
  padding: 16px; overflow-y: auto;
  animation: boSheetIn .24s cubic-bezier(.22, 1, .36, 1);
}
@keyframes boSheetIn { from { transform: translateX(100%) } to { transform: none } }
.bo-menu-promo {
  background: linear-gradient(135deg, var(--primary-bg), #fff);
  border: 1px solid rgba(107, 33, 168, .16);
  border-radius: 14px; padding: 12px 14px; margin-bottom: 14px;
  font-size: 13.5px; font-weight: 600; color: var(--primary-dark); line-height: 1.4;
}
.bo-menu-link {
  display: flex; align-items: center; gap: 12px;
  padding: 13px 12px; border-radius: 12px;
  color: var(--text); text-decoration: none; font-weight: 600; font-size: 14.5px;
}
.bo-menu-link svg { width: 21px; height: 21px; color: var(--primary); flex: none; }
.bo-menu-link:active { background: var(--primary-bg); }

/* ── Vitrine de promotion (remplace le gros bloc logo/nom) ────────────────── */
/* Principe des sites de mode : une bande d'accroche plein cadre, un titre par-dessus, et les
   produits qui défilent. Le nom de la boutique reste, mais en surtitre discret — c'est la
   marchandise qui doit occuper l'écran, pas un logo centré. */
.bo-hero {
  position: relative;
  margin: 0 0 14px;
  padding: 18px 0 16px;
  background:
    radial-gradient(120% 100% at 50% 0%, rgba(255, 255, 255, .18), transparent 60%),
    linear-gradient(135deg, var(--primary, var(--bo-purple)), var(--primary-dark, var(--bo-purple-dark)));
  border-radius: 0 0 26px 26px;
  box-shadow: var(--shadow-md);
  overflow: hidden;
}
.bo-hero-top { display: flex; align-items: center; gap: 10px; padding: 0 16px 12px; }
.bo-hero-logo {
  width: 42px; height: 42px; border-radius: 13px; flex: none;
  background: #fff; object-fit: contain; padding: 3px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, .18);
}
.bo-hero-name { font-size: 16px; font-weight: 800; color: #fff; line-height: 1.15; letter-spacing: -.2px; }
.bo-hero-desc { font-size: 12px; color: rgba(255, 255, 255, .82); margin-top: 1px; }

/* Accroche : le message de promotion de la gérante (« Arrivage Istanbul »). */
.bo-hero-title { padding: 0 16px 12px; }
.bo-hero-title strong { display: block; font-size: 23px; font-weight: 800; color: #fff; line-height: 1.15; letter-spacing: -.5px; }
.bo-hero-title span { display: block; font-size: 14px; color: rgba(255, 255, 255, .85); margin-top: 3px; font-weight: 500; }

/* Défilement horizontal, avec aimantation : chaque carte s'aligne, comme sur les grandes vitrines. */
.bo-hero-track {
  display: flex; gap: 12px;
  padding: 2px 16px 4px;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.bo-hero-track::-webkit-scrollbar { display: none; }  /* la barre casse l'effet vitrine */
.bo-hero-card {
  flex: none; width: 132px;
  scroll-snap-align: start;
  background: #fff; border-radius: 16px; overflow: hidden;
  box-shadow: 0 6px 18px rgba(24, 6, 46, .22);
  text-decoration: none; color: var(--text);
  transition: transform .16s ease;
}
.bo-hero-card:active { transform: scale(.96); }
.bo-hero-card .ph { width: 100%; height: 132px; background: #F3F0F7; display: block; object-fit: cover; }
.bo-hero-card .bd { padding: 8px 10px 10px; }
.bo-hero-card .nm {
  font-size: 12px; font-weight: 600; line-height: 1.3; color: var(--text);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
  min-height: 2.6em;
}
/* Pastille de prix reprise des vitrines mode : ronde, verte, ancrée sur la photo. */
.bo-hero-card .pr {
  margin-top: 4px; font-size: 14px; font-weight: 800; color: var(--primary, var(--bo-purple));
  letter-spacing: -.3px;
}
.bo-hero-card .pr .cur { font-size: 10px; font-weight: 700; color: var(--text-sec); margin-left: 2px; }

/* ── Carrousel sous l'en-tête (promotions + produits) ─────────────────────── */
/* ── En-tête COLLANT, identique sur toutes les pages ──────────────────────── */
/* La barre ne porte plus l'aplat vif de la boutique : à pleine saturation, une bande de couleur
   large occupe tout le regard et fait « colorié au feutre ». On garde la teinte, ramenée vers un
   fond profond (--head-*), et on y pose une TRAME de fines hachures à 3,5 % de blanc — de la
   texture de tissu, pas un motif : à un bras de distance on ne voit pas les traits, on voit une
   matière. Le reflet du haut donne l'épaisseur, la ligne claire du bas tranche net sur le contenu
   là où l'ombre floue précédente bavait. */
.bo-head {
  position: sticky; top: 0; z-index: 95;
  /* ANTI-FOUC, comme `.header` dans style.css. Sans jeu de couleur connu, cette barre prenait
     le repli --head-1/--head-2 — un violet qui n'appartient à AUCUNE boutique — et restait
     visible avec cette couleur générique jusqu'à ce que `bo-shop-ready` livre les vraies
     teintes. Sur les pages secondaires (commandes, panier, profil) où l'en-tête se pose avant
     que le script de la page ait fini de charger la boutique, cela peignait un bandeau violet
     qui n'a rien à voir avec la boutique qu'on visite. `header.js` la révèle dès que les
     couleurs réelles sont posées — ou, au pire, après un court délai de secours. */
  visibility: hidden;
  display: flex; align-items: center; gap: 11px;
  padding: 10px 14px;
  background:
    var(--head-motif, repeating-linear-gradient(135deg, rgba(255, 255, 255, .035) 0 1px, transparent 1px 7px)),
    linear-gradient(180deg, rgba(255, 255, 255, .07), transparent 42%),
    linear-gradient(165deg, var(--head-1), var(--head-2));
  border-bottom: 1px solid rgba(255, 255, 255, .09);
  box-shadow: 0 1px 10px rgba(17, 10, 28, .13);
}
.bo-head-logo {
  width: 40px; height: 40px; flex: none; border-radius: 12px;
  background: #fff; object-fit: contain; padding: 3px;
  border: 1px solid rgba(255, 255, 255, .3);
}
.bo-head-txt { flex: 1; min-width: 0; }
/* Le nom devient une SIGNATURE, pas une étiquette.
   Un gras compact de 16 px se lit comme le titre d'un formulaire. Un romain italique, un peu
   plus grand et à peine espacé, se lit comme le nom d'une maison de mode — c'est le seul endroit
   de l'écran où l'on peut se permettre du caractère, puisqu'il n'y en a qu'un. */
.bo-head-nom {
  font-family: Georgia, 'Iowan Old Style', 'Times New Roman', serif;
  font-style: italic; font-weight: 600;
  font-size: 19px; color: #fff; letter-spacing: .1px;
  line-height: 1.12; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* « by Aminata » : minuscule, très espacé, à demi effacé. Cette ligne signe, elle ne raconte
   rien — dès qu'elle devient lisible de loin, elle concurrence le nom. */
.bo-head-desc {
  font-size: 9.5px; font-weight: 600; letter-spacing: 1.6px; text-transform: uppercase;
  color: rgba(255, 255, 255, .62); margin-top: 3px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.bo-head-btn {
  position: relative; width: 40px; height: 40px; flex: none; border-radius: 12px;
  border: 1px solid rgba(255, 255, 255, .26);
  background: rgba(255, 255, 255, .16); backdrop-filter: blur(6px);
  color: #fff; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  transition: background .15s ease, transform .15s ease;
}
.bo-head-btn:active { transform: scale(.92); background: rgba(255, 255, 255, .3); }
/* Menu : plus discret que le panier, qui est l'action principale. */
.bo-head-btn.ghost { background: transparent; border-color: rgba(255, 255, 255, .18); }
.bo-head-btn svg { width: 21px; height: 21px; }
/* LA PASTILLE DU CADDIE COLLANT — une seule déclaration, ici.
   Elle était définie deux fois dans ce fichier (ici, puis de nouveau ~750 lignes plus bas,
   qui repeignait ce qu'on venait de poser) : on corrigeait la première sans rien voir bouger.
   Taille, rayon, couleur du compteur : géométrie commune, dans css/style.css.
   Le bouton qui la porte est CLAIR (`#FBFAFC`, voir `.bo-head-btn`), donc son liseré reprend
   ce clair — c'est le fond qui la porte qui détoure la pastille. */
.bo-head-badge {
  position: absolute; top: -5px; right: -5px;
  display: none;
  --badge-bordure: #FBFAFC;
}

/* Bandeau affiché quand une promotion filtre la boutique.
   Une phrase, des pastilles, une croix — plutôt qu'une étiquette et un gros bouton. */
.bo-filtre {
  display: flex; align-items: center; gap: 10px;
  margin: 0 16px 14px; padding: 10px 12px;
  background: linear-gradient(135deg, #fff, var(--primary-bg));
  border: 1px solid rgba(107, 33, 168, .16);
  border-radius: 14px; box-shadow: var(--shadow-sm);
  animation: boFiltreIn .28s cubic-bezier(.22, 1, .36, 1);
}
@keyframes boFiltreIn { from { opacity: 0; transform: translateY(-6px) } to { opacity: 1; transform: none } }
.bo-filtre-ico {
  width: 30px; height: 30px; flex: none; border-radius: 9px;
  background: var(--primary); color: #fff;
  display: flex; align-items: center; justify-content: center;
}
.bo-filtre-ico svg { width: 16px; height: 16px; }
.bo-filtre-txt {
  flex: 1; min-width: 0; font-size: 13px; color: var(--text-sec);
  display: flex; align-items: center; gap: 5px; flex-wrap: wrap;
}
/* Catégories en pastilles : on lit la sélection d'un coup d'œil. */
.bo-filtre-txt em {
  font-style: normal; font-weight: 700; font-size: 12px;
  color: var(--primary-dark); background: rgba(107, 33, 168, .1);
  border-radius: 999px; padding: 3px 9px;
}
/* « Voir tout » : bouton à CONTOUR, pas un aplat violet. Le libellé dit ce qu'il fait, la
   légèreté laisse la vedette aux produits. */
.bo-filtre-tout {
  flex: none; border: 1.5px solid var(--primary); background: #fff;
  color: var(--primary); border-radius: 999px;
  padding: 7px 14px; font-size: 12.5px; font-weight: 700; font-family: inherit;
  cursor: pointer; white-space: nowrap;
  transition: background .15s ease, color .15s ease, transform .15s ease;
}
.bo-filtre-tout:active { transform: scale(.94); background: var(--primary); color: #fff; }

/* Appel à l'action d'une promotion ciblant des catégories. */
.bo-car-slide.cliquable { cursor: pointer; }
.bo-car-cta {
  display: block; margin-top: 10px; font-size: 12.5px; font-weight: 700;
  color: #fff; opacity: .95;
}

/* Réservé dès le départ (min-height) : sans ça, l'arrivée des diapos poussait le contenu vers le
   bas et la page « sautait » sous les yeux de la cliente. */
.bo-carousel { position: relative; margin: 12px 0 16px; min-height: 190px; }

/* Barre intégrée au carrousel : logo + nom de la boutique + menu à trois traits.
   Elle remplace l'ancien en-tête plein cadre, qui mangeait un tiers de l'écran. */
.bo-car-bar { display: flex; align-items: center; gap: 10px; padding: 4px 16px 12px; }
.bo-car-logo {
  width: 42px; height: 42px; flex: none; border-radius: 13px;
  background: #fff; object-fit: contain; padding: 3px;
  box-shadow: var(--shadow-sm); border: 1px solid var(--border);
}
.bo-car-shop { flex: 1; min-width: 0; }
.bo-car-shop-nom {
  font-size: 16px; font-weight: 800; color: var(--text); letter-spacing: -.3px;
  line-height: 1.15; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.bo-car-shop-desc {
  font-size: 12px; color: var(--text-sec); margin-top: 1px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.bo-car-menu {
  width: 40px; height: 40px; flex: none; border-radius: 12px;
  border: 1px solid var(--border); background: var(--surface);
  color: var(--text); cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  box-shadow: var(--shadow-sm);
}
.bo-car-menu:active { background: var(--primary-bg); }
.bo-car-menu svg { width: 20px; height: 20px; }
.bo-car-track {
  display: flex; gap: 12px;
  padding: 2px 16px;
  overflow-x: auto;
  scroll-snap-type: x mandatory;      /* chaque diapo s'aligne : pas d'arrêt à mi-chemin */
  -webkit-overflow-scrolling: touch;  /* inertie native au doigt */
  scrollbar-width: none;
}
.bo-car-track::-webkit-scrollbar { display: none; }

.bo-car-slide {
  flex: none; width: 78%; max-width: 320px;
  scroll-snap-align: center;
  border-radius: 18px; overflow: hidden;
  background: var(--surface);
  box-shadow: var(--shadow-md);
  text-decoration: none; color: var(--text);
  transition: transform .16s ease;
}
.bo-car-slide:active { transform: scale(.97); }
.bo-car-slide.produit img { width: 100%; height: 190px; object-fit: cover; display: block; background: #F3F0F7; }
.bo-car-info { padding: 10px 14px 13px; }
.bo-car-nom {
  font-size: 14px; font-weight: 600; line-height: 1.3; color: var(--text);
  display: -webkit-box; -webkit-line-clamp: 1; -webkit-box-orient: vertical; overflow: hidden;
}
.bo-car-prix { font-size: 17px; font-weight: 800; color: var(--text); letter-spacing: -.4px; margin-top: 3px; }
.bo-car-prix span { font-size: 11px; font-weight: 700; color: var(--text-sec); margin-left: 3px; }

/* Diapos de promotion.
   Les ambiances pastel — menthe, lavande, abricot en rotation — plus un halo blanc animé sous un
   texte noir de 24 px : ensemble, ça fatiguait l'œil. C'est la plus grande surface de l'accueil,
   et elle vibrait en permanence pendant qu'on essayait de lire.
   Le parti est celui de Vinted : la carte est BLANCHE, cernée d'un filet, et c'est le texte seul
   qui porte. Aucune animation de fond. Une seule touche de la couleur de la boutique, sur le mot
   « Promotion » — assez pour signer, trop petite pour éblouir. */
.bo-car-slide.promo {
  min-height: 150px; width: 88%; max-width: 460px;
  display: flex; align-items: flex-end;
  position: relative; overflow: hidden;
  background: #fff;
  border: 1px solid rgba(24, 6, 46, .09);
  box-shadow: 0 1px 3px rgba(24, 6, 46, .05);
}
/* Un LAVIS dans l'angle : un souffle de couleur, à peine 5 %, qui donne du relief à la diapo
   sans que rien ne crie.

   C'ÉTAIT TROIS COULEURS ÉCRITES EN DUR — un violet #A855F7, un rose #EC4899, un turquoise
   #2DD4BF — distribuées une diapo sur trois. Elles ne venaient d'aucune boutique : une
   gérante en rose voyait donc une promotion sur trois virer au turquoise, dans un carrousel
   posé juste sous sa barre violette. Trois diapos voisines se distinguaient, oui, mais en
   introduisant deux couleurs étrangères pour y arriver.

   Les trois lavis restent DISTINCTS, par la densité et non par la teinte : 7 %, 5 %, 3,5 % de
   la MÊME couleur de la boutique. C'est la règle déjà retenue pour les tuiles de rayons —
   quatre intensités d'une seule teinte plutôt que quatre couleurs. */
.bo-car-slide.promo::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
}
.bo-car-slide.promo:nth-child(3n+1)::after { background: radial-gradient(120% 90% at 100% 0%, color-mix(in srgb, var(--accent) 7%, transparent), transparent 62%); }
.bo-car-slide.promo:nth-child(3n+2)::after { background: radial-gradient(120% 90% at 100% 0%, color-mix(in srgb, var(--accent) 5%, transparent), transparent 62%); }
.bo-car-slide.promo:nth-child(3n+3)::after { background: radial-gradient(120% 90% at 100% 0%, color-mix(in srgb, var(--accent) 3.5%, transparent), transparent 62%); }

.bo-car-promo-in { padding: 22px 20px 20px; position: relative; z-index: 1; }

/* Étiquette « Promotion » : très petite, très espacée, en filet — le style des vitrines de mode.
   Elle annonce sans concurrencer le message. */
.bo-car-badge {
  display: inline-flex; align-items: center; gap: 6px; margin-bottom: 11px;
  background: transparent; border: none; padding: 0;
  /* La couleur de la gérante, tempérée : à 9,5 px un rose pur vibre et devient pénible à fixer.
     Mélangée à un gris profond, elle reste identifiable sans agresser. */
  color: color-mix(in oklab, var(--primary, #6B21A8) 68%, #4A4256);
  font-size: 9.5px; font-weight: 800;
  letter-spacing: 2.4px; text-transform: uppercase;
}
/* Petit trait avant le mot : repère graphique discret, plus élégant qu'une pastille de plus. */
.bo-car-badge::before {
  content: ''; width: 18px; height: 2px; border-radius: 2px;
  background: currentColor; opacity: .5;
}

/* Le message. 24 px en graisse 800 et noir profond, c'était une affiche de soldes hurlée à la
   figure de la cliente. Descendu à 19 px en demi-gras, sur un gris très foncé plutôt que du noir
   pur : le contraste reste largement suffisant, la lecture devient calme. L'interlignage
   respire. Plus d'ombre portée — elle n'existait que pour survivre au dégradé, qui n'est plus là. */
.bo-car-slide.promo strong {
  display: block; color: #2A2137;
  font-size: 19px; font-weight: 600;
  line-height: 1.34; letter-spacing: -.1px;
  text-wrap: balance;                              /* évite le mot orphelin en fin de ligne */
}

/* Appel à l'action : souligné fin plutôt que bouton — la diapo entière est déjà cliquable. */
.bo-car-cta {
  display: inline-block; margin-top: 12px;
  color: var(--primary-dark) !important; opacity: 1 !important;
  font-size: 12.5px; font-weight: 700;
  border-bottom: 1.5px solid rgba(107, 33, 168, .35);
  padding-bottom: 2px;
}

/* Flèches : discrètes, en verre, elles s'effacent en bout de course. */
.bo-car-arrow {
  position: absolute; top: 50%; transform: translateY(-50%);
  z-index: 4; width: 38px; height: 38px; border-radius: 50%;
  border: 1px solid rgba(255,255,255,.5);
  background: rgba(255,255,255,.86); backdrop-filter: blur(6px);
  color: var(--text); cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 3px 12px rgba(24,6,46,.2);
  transition: opacity .2s ease, transform .15s ease;
}
.bo-car-arrow svg { width: 19px; height: 19px; }
.bo-car-arrow.left { left: 6px; }
.bo-car-arrow.right { right: 6px; }
.bo-car-arrow:active { transform: translateY(-50%) scale(.9); }
/* Masquée, pas supprimée : la mise en page ne doit pas sauter. */
.bo-car-arrow.cache { opacity: 0; pointer-events: none; }

.bo-car-dots { display: flex; justify-content: center; gap: 6px; margin-top: 10px; }
.bo-car-dot { width: 6px; height: 6px; border-radius: 50%; background: #D8D3E2; transition: all .2s ease; }
/* Un point par diapo, l'actif compris : allongé, il se lisait comme deux points collés
   (JM, le 01/10, sur le carrousel de Plus dans Manage). Même taille, seule la couleur change. */
.bo-car-dot.on { background: var(--primary, var(--bo-purple)); }

/* ── Accordéons de catégories ─────────────────────────────────────────────── */
.accordion-section {
  border-radius: var(--radius-sm);
  overflow: hidden;
  margin-bottom: 10px;
  box-shadow: var(--shadow-sm);
  border: 1px solid var(--border);
  background: var(--surface);
}
.accordion-header {
  background: var(--surface);
  padding: 14px 15px;
  font-weight: 700;
  gap: 10px;
}
/* Ouverte : une barre verticale colorée à gauche marque la section active, plus élégant qu'un
   fond teinté sur toute la largeur. */
.accordion-section.open .accordion-header {
  background: #fff;
  box-shadow: inset 3px 0 0 var(--primary);
}
.accordion-header-left { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; min-width: 0; }
.accordion-title {
  font-size: 14.5px; font-weight: 700; color: var(--text); letter-spacing: -.2px;
}
/* Ni chevron ni compteur : `shop.js` n'en rend plus depuis que les catégories sont des rangées
   horizontales, toujours ouvertes. Leurs règles ont été retirées d'ici et de style.css. */
/* Le badge « Promo » n'existe plus : `shop.js` ne rend que la PHRASE de promotion, jamais la
   pastille. Ses règles ont été retirées d'ici, de style.css et du bloc des rangées — c'était du
   CSS que plus rien ne portait. `.accordion-promo-text`, lui, est bien rendu : on le garde. */
.accordion-promo-text { font-size: 11.5px; color: var(--text-sec); font-weight: 600; width: 100%; }

/* ── Boutons ──────────────────────────────────────────────────────────────── */
.btn-primary, .add-to-cart, #orderBtn, .welcome-close {
  background: linear-gradient(135deg, var(--primary), var(--primary-dark));
  border-radius: 14px;
  font-weight: 800;
  box-shadow: var(--shadow-md);
  transition: transform .15s ease, box-shadow .15s ease;
}
.btn-primary:active, .add-to-cart:active, #orderBtn:active, .welcome-close:active {
  transform: translateY(1px) scale(.99);
  box-shadow: var(--shadow-sm);
}

/* ── Popup de bienvenue (la promotion de la gérante) ──────────────────────── */
.welcome-popup { border-radius: 22px; box-shadow: var(--shadow-lg); }
.welcome-text { color: var(--text); font-weight: 600; }

/* ── Assistant IA — bouton flottant DÉPLAÇABLE ────────────────────────────── */
/* Pas un simple rond violet : pastille en dégradé, halo doux, et surtout on peut
   la déplacer où on veut (comme l'AssistiveTouch d'iPhone), sur TOUTES les pages. */
#boChatFab {
  position: fixed; z-index: 9998;
  width: 58px; height: 58px; border-radius: 20px;
  background: linear-gradient(135deg, #7C3AED 0%, var(--bo-purple) 55%, var(--bo-purple-dark) 100%);
  border: none; cursor: grab;
  display: flex; align-items: center; justify-content: center;
  color: #fff;
  box-shadow: 0 8px 24px rgba(107, 33, 168, .38), 0 2px 6px rgba(0, 0, 0, .12);
  touch-action: none;                 /* indispensable : sinon le doigt scrolle la page au lieu de glisser */
  transition: transform .18s cubic-bezier(.34, 1.56, .64, 1), box-shadow .18s ease, opacity .3s ease;
  /* PAS d'animation d'apparition ici. Elle laissait le bouton bloqué à opacity:0 (animation
     « running » mais jamais avancée, l'élément étant inséré par JS après le calcul du style).
     Une entrée décorative ne vaut pas une assistante invisible : on affiche, simplement. */
  opacity: 1;
}
#boChatFab svg { width: 26px; height: 26px; }
/* OUFY TOUT SEUL, comme dans Manage : ni carré, ni dégradé, ni ombre de boîte. Une ombre au sol
   le détache du contenu. Les !important battent le fond posé plus bas pour les thèmes. */
#boChatFab.oufy, #boChatHead .boAvatar.oufy {
  background: none !important; box-shadow: none; border-radius: 0; overflow: visible;
}
#boChatFab.oufy { filter: drop-shadow(0 4px 6px rgba(45, 27, 78, .28)); }
#boChatFab.dragging.oufy { box-shadow: none; filter: drop-shadow(0 10px 12px rgba(45, 27, 78, .35)); }
#boChatFab:active { transform: scale(.92); }
#boChatFab.dragging { cursor: grabbing; transform: scale(1.12); box-shadow: 0 14px 34px rgba(107, 33, 168, .5); }
/* Au repos, la pastille s'efface un peu pour ne pas manger le contenu — sans devenir fantomatique :
   à .55 elle passait inaperçue, et une assistante qu'on ne voit pas ne sert à personne. */
#boChatFab.idle { opacity: .82; }
#boChatFab.idle:hover { opacity: 1; }
/* Halo qui respire : signale une assistante vivante sans clignoter agressivement. */
#boChatFab::before {
  content: ''; position: absolute; inset: -4px; border-radius: 24px;
  background: radial-gradient(circle, rgba(168, 85, 247, .35), transparent 70%);
  animation: boPulse 2.8s ease-in-out infinite; z-index: -1;
}
@keyframes boPulse { 0%,100% { transform: scale(1); opacity: .5 } 50% { transform: scale(1.18); opacity: .9 } }
@keyframes boFabIn { from { transform: scale(0) rotate(-90deg); opacity: 0 } to { transform: scale(1) rotate(0); opacity: 1 } }

/* ── Assistant IA — panneau, inspiré de Gemini ────────────────────────────── */
#boChatPanel {
  position: fixed; z-index: 9999;
  right: 10px; bottom: 10px;
  width: min(370px, 94vw); height: min(620px, 85vh);
  background: var(--surface);
  border-radius: 26px;
  box-shadow: 0 24px 60px rgba(24, 6, 46, .32);
  display: none; flex-direction: column; overflow: hidden;
  border: 1px solid rgba(107, 33, 168, .12);
}
#boChatPanel.open { display: flex; animation: boPanelIn .28s cubic-bezier(.22, 1, .36, 1); }
@keyframes boPanelIn { from { opacity: 0; transform: translateY(24px) scale(.96) } to { opacity: 1; transform: none } }

#boChatHead {
  display: flex; align-items: center; gap: 10px;
  padding: 14px 16px;
  border-bottom: 1px solid var(--border);
  background: #fff;
}
#boChatHead .boAvatar {
  width: 34px; height: 34px; border-radius: 12px; flex: none;
  background: linear-gradient(135deg, #7C3AED, var(--bo-purple-dark));
  display: flex; align-items: center; justify-content: center; color: #fff;
}
#boChatHead .boAvatar svg { width: 19px; height: 19px; }
#boChatHead .boTitle { font-weight: 800; font-size: 15px; color: var(--text); line-height: 1.15; }
/* La croix est un SVG de BoIcons, plus le caractere `×` : elle se centre donc a la flexbox
   et se dimensionne au pixel, au lieu de dependre de la police du telephone. */
#boChatHead .boClose {
  margin-left: auto; background: transparent; border: none;
  line-height: 1; color: #9CA3AF; cursor: pointer;
  width: 34px; height: 34px; border-radius: 10px;
  display: flex; align-items: center; justify-content: center; padding: 0;
}
#boChatHead .boClose svg { width: 20px; height: 20px; }
#boChatHead .boClose:active { background: #F3F4F6; }

#boChatBody {
  flex: 1; overflow-y: auto; padding: 18px 16px 8px;
  display: flex; flex-direction: column; gap: 14px;
  background: #fff;
  scroll-behavior: smooth;
}

/* Écran d'accueil façon Gemini : salutation en dégradé + suggestions cliquables */
.boGreet { padding: 6px 2px 2px; }
.boGreet h2 {
  font-size: 27px; line-height: 1.2; font-weight: 700; margin: 0 0 4px;
  background: linear-gradient(90deg, #7C3AED, #C026D3 45%, var(--primary-dark));
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent; color: transparent;
}
.boGreet p { font-size: 22px; line-height: 1.25; color: #5F6368; margin: 0; font-weight: 400; }

/* Bulles */
.boMsg { max-width: 86%; font-size: 14.5px; line-height: 1.5; white-space: pre-wrap; word-break: break-word; }
.boMsg.customer {
  align-self: flex-end;
  background: linear-gradient(135deg, #7C3AED, var(--primary));
  color: #fff; padding: 11px 15px;
  border-radius: 20px 20px 5px 20px;
  box-shadow: var(--shadow-sm);
}
.boMsg.ai {
  align-self: flex-start; color: var(--text);
  padding: 2px 0;                 /* l'IA « parle » à plat, comme Gemini : pas de cartouche */
  max-width: 100%;
}
.boMsg.system {
  align-self: center; text-align: center;
  background: #FFF7ED; color: #9A5B00; border: 1px solid #FED7AA;
  font-size: 12.5px; padding: 9px 14px; border-radius: 14px; max-width: 94%;
}

/* « L'assistante écrit… » : trois points animés, plus vivant qu'un texte figé */
.boTyping { display: flex; gap: 5px; padding: 6px 2px; align-self: flex-start; }
.boTyping span {
  width: 8px; height: 8px; border-radius: 50%; background: var(--primary-light);
  animation: boDot 1.3s ease-in-out infinite;
}
.boTyping span:nth-child(2) { animation-delay: .18s }
.boTyping span:nth-child(3) { animation-delay: .36s }
@keyframes boDot { 0%,60%,100% { transform: translateY(0); opacity: .45 } 30% { transform: translateY(-6px); opacity: 1 } }

/* Encart de coût (phase de test) : sobre, en bas du fil, sans voler la vedette à la conversation. */
#boChatUsage {
  margin: 0 12px 8px; padding: 8px 12px;
  background: #F6F3FB; border: 1px dashed rgba(107, 33, 168, .28);
  border-radius: 12px;
  font-size: 11.5px; color: var(--text-sec); line-height: 1.5;
}
#boChatUsage b { color: var(--bo-purple-dark); font-weight: 800; }
.boUsageDot {
  display: inline-block; width: 7px; height: 7px; border-radius: 50%;
  background: var(--secondary); margin-right: 6px; vertical-align: middle;
}
.boUsageMeta { display: block; font-size: 10.5px; color: #9CA3AF; margin-top: 2px; }



/* ── Formulaires : « Moi », panier, adresses ──────────────────────────────── */
/* Champs hauts et respirants, focus visible : sur mobile, un champ trop court et sans état de
   focus donne l'impression d'un formulaire administratif. */
.order-form, .profile-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 16px;
  box-shadow: var(--shadow-sm);
  margin-bottom: 14px;
}
.form-group { margin-bottom: 14px; }
.form-group label {
  display: block; font-size: 12.5px; font-weight: 700; color: var(--text-sec);
  margin-bottom: 6px; letter-spacing: .1px;
}
.order-form input[type=text], .order-form input[type=tel], .order-form input[type=email],
.order-form textarea, .order-form select {
  width: 100%;
  padding: 13px 14px;
  font-size: 15px;
  font-family: inherit;
  color: var(--text);
  background: #FAFAFC;
  border: 1.5px solid var(--border);
  border-radius: 12px;
  outline: none;
  transition: border-color .16s ease, box-shadow .16s ease, background .16s ease;
}
.order-form input:focus, .order-form textarea:focus, .order-form select:focus {
  background: #fff;
  border-color: var(--primary, var(--bo-purple));
  box-shadow: 0 0 0 3px rgba(107, 33, 168, .12);
}
.order-form input::placeholder { color: #B6B6C2; }

/* Titres de section : barre colorée à gauche plutôt qu'un emoji */
h2[data-testid$="mes-informations"], h3[data-testid$="adresses-livraison"],
.section-title {
  display: flex; align-items: center; gap: 9px;
  font-size: 16px; font-weight: 800; color: var(--text);
  margin: 18px 0 10px; letter-spacing: -.2px;
}
h2[data-testid$="mes-informations"]::before, h3[data-testid$="adresses-livraison"]::before {
  content: ''; width: 4px; height: 18px; border-radius: 3px;
  background: var(--primary, var(--bo-purple)); flex: none;
}

/* Boutons secondaires (Ajouter, Ma position, Changer de n°) : pilule sobre et cliquable */
[data-geo] {
  padding: 9px 14px !important;
  border-radius: 999px !important;
  border: 1.5px solid var(--primary, var(--bo-purple)) !important;
  background: transparent !important;
  color: var(--primary, var(--bo-purple)) !important;
  font-size: 13px !important; font-weight: 700 !important;
  cursor: pointer;
}
[data-geo]:active { background: var(--primary-bg) !important; }

/* Carte d'adresse : bloc distinct, pour ne pas noyer plusieurs adresses en une bouillie */
.address-block, [data-addr-block] {
  background: #FAFAFC; border: 1px solid var(--border);
  border-radius: 14px; padding: 12px; margin-bottom: 12px;
}

/* ── Panier ───────────────────────────────────────────────────────────────── */
.cart-item {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: 14px; padding: 10px; margin-bottom: 10px;
  box-shadow: var(--shadow-sm);
}
.cart-item img { border-radius: 10px; }
.cart-total, .total-row {
  background: linear-gradient(135deg, var(--primary-bg), #fff);
  border: 1px solid rgba(107, 33, 168, .16);
  border-radius: 14px; padding: 14px; margin: 12px 0;
  font-weight: 800;
}
/* Bouton principal de commande : pleine largeur, ancré bas — le geste clé de la page */
#orderBtn {
  width: 100%; padding: 16px; font-size: 16px; border-radius: 16px;
  letter-spacing: -.2px;
}

/* ── Fiche produit ────────────────────────────────────────────────────────── */
.product-detail .price, .detail-price {
  font-size: 26px; font-weight: 800; color: var(--text); letter-spacing: -.6px;
}
.product-detail .name, .detail-name {
  font-size: 19px; font-weight: 700; line-height: 1.3; color: var(--text);
}
.size-btn, .color-btn {
  border-radius: 999px; border: 1.5px solid var(--border);
  padding: 9px 16px; font-weight: 700; font-size: 13.5px;
  background: #fff; color: var(--text);
}
.size-btn.selected, .color-btn.selected {
  border-color: var(--primary, var(--bo-purple));
  background: var(--primary-bg);
  color: var(--primary-dark, var(--bo-purple-dark));
}

/* ── États vides ──────────────────────────────────────────────────────────── */
/* Un « Aucune commande » nu est décourageant : on centre, on aère, on adoucit. */
.empty-state {
  text-align: center; padding: 48px 24px; color: var(--text-sec);
}
.empty-state svg { width: 56px; height: 56px; color: #D8D3E2; margin-bottom: 12px; }

/* Plein écran sur petit mobile : un panneau flottant y serait à l'étroit */
@media (max-width: 480px) {
  #boChatPanel {
    right: 0; bottom: 0; left: 0;
    width: 100%; height: 88vh;
    border-radius: 24px 24px 0 0;
  }
}

/* ── Fiche de la boutique (touche du logo) ────────────────────────────────── */
/* Tout ce que la barre d'en-tête ne dit plus se trouve ici : qui tient la boutique, ce qu'on y
   trouve, comment la joindre. Une feuille qui monte du bas — le geste attendu sur téléphone, et
   qui laisse voir la boutique derrière : on n'a pas quitté la page, on a ouvert un tiroir. */
/* Au-dessus du bouton d'assistante (z-index 9999) : c'est une fenêtre modale, et la pastille
   violette se posait en plein milieu de son texte. */
.bo-fiche { position: fixed; top: 0; bottom: 0; left: var(--bo-gauche); width: min(var(--bo-cadre), 100vw); z-index: 10000; display: none; }
.bo-fiche.open { display: block; }
.bo-fiche-fond {
  position: absolute; inset: 0;
  background: rgba(18, 12, 26, .38); backdrop-filter: blur(3px);
  animation: boFicheFond .22s ease;
}
@keyframes boFicheFond { from { opacity: 0 } to { opacity: 1 } }
.bo-fiche-carte {
  position: absolute; left: 0; right: 0; bottom: 0;
  background: #fff; border-radius: 24px 24px 0 0;
  padding: 10px 22px calc(26px + env(safe-area-inset-bottom, 0px));
  max-height: 86vh; overflow-y: auto;
  box-shadow: 0 -18px 50px rgba(18, 12, 26, .22);
  animation: boFicheIn .28s cubic-bezier(.22, 1, .36, 1);
  text-align: center;
}
@keyframes boFicheIn { from { transform: translateY(100%) } to { transform: none } }
/* La poignée : elle ne fait rien, elle DIT que le panneau se referme en glissant. */
.bo-fiche-poignee {
  display: block; width: 38px; height: 4px; border-radius: 3px;
  background: #E2DEE9; margin: 0 auto 18px;
}
.bo-fiche-logo {
  width: 74px; height: 74px; border-radius: 22px; object-fit: contain;
  background: #fff; padding: 6px; border: 1px solid var(--border);
  box-shadow: var(--shadow-sm);
}
.bo-fiche-nom {
  font-family: Georgia, 'Iowan Old Style', 'Times New Roman', serif;
  font-style: italic; font-weight: 600; font-size: 25px;
  color: var(--text); margin: 14px 0 0; letter-spacing: .1px;
}
.bo-fiche-par {
  font-size: 10px; font-weight: 600; letter-spacing: 1.8px; text-transform: uppercase;
  color: var(--text-sec); margin-top: 6px;
}
.bo-fiche-desc {
  font-size: 14.5px; line-height: 1.55; color: #4A4356;
  margin: 16px auto 0; max-width: 32em;
}
.bo-fiche-bloc { margin-top: 22px; }
.bo-fiche-titre {
  display: block; font-size: 9.5px; font-weight: 800; letter-spacing: 2.2px;
  text-transform: uppercase; color: var(--text-sec); margin-bottom: 10px;
}
/* Les rayons en pastilles neutres : ils disent le TYPE de boutique d'un coup d'œil, ce qu'une
   phrase de description met dix mots à faire. */
.bo-fiche-rayons { display: flex; flex-wrap: wrap; gap: 7px; justify-content: center; }
.bo-fiche-rayons span {
  padding: 6px 13px; border-radius: 999px;
  background: #F6F4F9; border: 1px solid var(--border);
  font-size: 12.5px; font-weight: 600; color: #4A4356;
}
/* Joindre la boutique : la seule action de la fiche, donc la seule chose colorée. */
.bo-fiche-contact {
  display: flex; align-items: center; justify-content: center; gap: 9px;
  margin-top: 22px; padding: 14px; border-radius: 14px;
  background: var(--head-1); color: #fff; text-decoration: none;
  font-size: 14.5px; font-weight: 700;
}
.bo-fiche-contact svg { width: 19px; height: 19px; }
.bo-fiche-contact:active { transform: scale(.98); }
.bo-fiche-ok {
  width: 100%; margin-top: 10px; padding: 13px; border: none; background: none;
  color: var(--text-sec); font-size: 14px; font-weight: 600; cursor: pointer;
}

/* ═══════════════════════════════════════════════════════════════════════════
   HARMONISATION — toute la boutique sur UNE seule teinte
   ═══════════════════════════════════════════════════════════════════════════
   Le problème n'était pas telle ou telle couleur, c'était leur NOMBRE. Une page
   accumulait le rose de la boutique, un vert « en stock », un ambre « Promo », un
   orange « plus que 2 », un rouge « rupture », un violet pour l'assistante. Six
   familles de teintes qui ne se sont jamais parlé : aucune ne pouvait sortir du
   lot puisqu'elles criaient toutes, et l'ensemble donnait le patchwork.

   Vinted tient tout avec un seul vert-canard, du noir, du gris. On fait pareil :
   TOUTES les couleurs de l'application descendent maintenant de la teinte choisie
   par la gérante, à trois intensités, plus deux neutres.

     --deep    (= --head-1/2)  les barres            — la teinte, assombrie
     --accent                  les actions et liens  — la teinte, tempérée
     --accent-tint             les fonds de pastille — la teinte, effacée
     --ink                     les textes            — neutre foncé
     --muted                   le secondaire         — neutre gris

   Tout est calculé à partir de --primary : une gérante qui choisit turquoise
   obtient une application turquoise cohérente, sans qu'une seule valeur soit à
   réécrire ici. */
:root {
  --ink: #16121F;
  --muted: #6E6880;
  --accent: #6B21A8;              /* repli sans color-mix */
  --accent-tint: #F4F1F8;
}
@supports (color: color-mix(in oklab, red, blue)) {
  :root {
    /* Tempéré : la teinte pure passe mal sur du blanc — elle vibre et fatigue.
       Ramenée vers un gris profond, elle reste identifiable et devient lisible. */
    /* MÊME BASE QUE LA BARRE — `--teinte`, pas `--primary`. Les icônes de rayons ignoraient
       la couleur secondaire de la gérante : sur une boutique dont la barre mélange un violet
       et un rose, elles restaient d'un violet pur que la barre n'avait plus. */
    /* LA TEINTE TELLE QUELLE, sans rien de gris dedans.
       Elle était ramenée de 26 % vers #3E3448 « pour qu'elle ne vibre pas sur du blanc ». Le
       résultat : un violet éteint, et une barre sombre au-dessus de rayons ternes. La couleur
       que la gérante a choisie est celle qu'on affiche — c'est la sienne, pas la nôtre. */
    --accent: var(--teinte-base, var(--primary, #6B21A8));
    --accent-tint: color-mix(in oklab, var(--teinte-base, var(--primary, #6B21A8)) 7%, #fff);

    /* NOIR CHROMATIQUE — le dernier détail, et le moins visible.
       Le texte de l'app était #1A1A2E : un noir BLEUTÉ. Sur une boutique rose, ça
       faisait subsister une seconde famille de teinte sur toute la page — pas assez
       pour qu'on la nomme, assez pour que rien ne colle tout à fait. On y verse
       12 % de la teinte de la boutique : le noir reste du noir, mais il appartient
       enfin à la même famille que le reste. Les gris et les filets suivent.
       C'est le genre de correction qu'on ne voit pas ; on voit seulement que
       l'ensemble tient. */
    --ink: color-mix(in oklab, var(--primary, #6B21A8) 12%, #131019);
    --muted: color-mix(in oklab, var(--primary, #6B21A8) 10%, #6A6777);
    --border: color-mix(in oklab, var(--primary, #6B21A8) 7%, #E9E6EE);
  }
}
/* style.css raisonne encore en --text/--text-sec : on les branche sur la palette
   plutôt que de réécrire toutes ses règles une par une. */
:root { --text: var(--ink); --text-sec: var(--muted); --line: var(--border); }
/* Les anciennes couleurs « sémantiques » sont réaffectées à la palette plutôt que
   supprimées : d'autres feuilles et scripts les utilisent encore, les vider
   casserait leur affichage. */
:root {
  --secondary: var(--accent);
  --success: var(--muted);
  --danger: var(--ink);
  --warning: var(--accent);
}

/* ── Textes ─────────────────────────────────────────────────────────────── */
/* Le prix en rose sur chaque vignette faisait vingt taches de couleur par écran.
   Chez Vinted comme chez Amazon le prix est NOIR : c'est l'information la plus
   lue, elle n'a pas besoin d'être colorée pour être trouvée. */
.product-card .price, .bo-car-prix { color: var(--ink); }

/* ── Stock ──────────────────────────────────────────────────────────────── */
/* Vert / orange / rouge, c'était un feu tricolore posé au milieu d'une vitrine.
   Une disponibilité normale ne mérite pas de couleur — elle passe en gris. Le
   stock faible, lui, est la seule information qui doit pousser à agir : il garde
   l'accent, donc la teinte de la boutique. Une rupture est une ABSENCE : le gris
   pâle le dit mieux qu'un rouge d'alarme, qui affole sans rien proposer. */
.product-card .stock-badge,
.product-detail .stock,
.cart-item .stock-badge,
.product-card .stock-ok { color: var(--muted) !important; background: none !important; }
.product-card .stock-badge.urgent,
.product-detail .stock.urgent,
.cart-item .stock-badge.urgent,
.product-card .stock-low {
  color: var(--accent) !important; background: none !important;
  border: none !important; font-weight: 700;
}
.product-card .stock-badge.out,
.product-detail .stock.out,
.cart-item .stock-badge.out,
.product-card .stock-out { color: #A5A0B0 !important; background: none !important; }

/* ── Liens et actions ───────────────────────────────────────────────────── */
/* « Voir les articles concernés → » était écrit dans le violet le plus sombre de
   la palette : un texte presque noir mais teinté, qui ne ressemblait ni à du texte
   ni à un lien. Il prend l'accent, comme tous les liens de l'application. */
.bo-car-cta { color: var(--accent) !important; }
.bo-filtre-txt em { color: var(--ink); }
.bo-filtre-tout { border-color: var(--accent); color: var(--accent); }
.bo-filtre-tout:active { background: var(--accent); color: #fff; }
.bo-filtre { background: var(--accent-tint); border: 1px solid var(--line, #E8E4EE); }
.bo-filtre-ico { color: var(--accent); }

/* Boutons pleins : un aplat d'accent, sans dégradé. Un dégradé sur un bouton est
   une décoration de plus à faire cohabiter avec le reste. */
.btn-primary, .add-to-cart, #orderBtn, .welcome-close {
  background: var(--accent);
}

/* ── Accordéon ──────────────────────────────────────────────────────────── */
.accordion-section.open .accordion-header { box-shadow: inset 3px 0 0 var(--accent); }

/* ── Panier de l'en-tête ────────────────────────────────────────────────── */
/* C'est LA chose qu'on doit trouver sans chercher, et le seul endroit où la teinte
   de la boutique s'exprime à pleine force. Un bouton translucide de plus sur une
   barre foncée se confondait avec le fond : rempli, cerné d'un anneau clair et
   posé sur une lueur de sa propre couleur, il se détache aussitôt.
   Toute la retenue appliquée ailleurs sert à ça — quand une seule chose est
   colorée, on la voit. */
.bo-head-btn {
  background: var(--primary);
  border: none;
  box-shadow: 0 0 0 3.5px rgba(255, 255, 255, .16),
              0 5px 16px color-mix(in srgb, var(--primary) 45%, transparent);
}
.bo-head-btn:active { transform: scale(.92); background: var(--primary); }
.bo-head-btn.ghost { background: transparent; box-shadow: none; border: 1px solid rgba(255,255,255,.2); }
/* Le compte d'articles se dessine comme les deux autres pastilles de la vitrine — géométrie et
   couleur communes, dans css/style.css. Il se peignait ici en blanc sur fond foncé, puis une
   TROISIÈME déclaration plus bas le repeignait encore : trois pastilles pour un seul panier,
   dans trois couleurs. */

/* ── Assistante ─────────────────────────────────────────────────────────── */
/* Le violet BoutiqueOuf de la pastille était une SIXIÈME couleur, étrangère à la
   boutique : chez une gérante en rose, ce rond violet n'appartenait à rien. Il
   prend la teinte des barres — l'assistante fait partie de la maison.
   Le halo qui pulsait en permanence est retiré : une lueur qui bat toutes les
   2,8 secondes dans le coin de l'œil est fatigante sur une page où l'on regarde
   des vêtements. Une ombre portée suffit à décoller le bouton. */
#boChatFab {
  background: linear-gradient(165deg, var(--head-1), var(--head-2));
  box-shadow: 0 6px 20px rgba(17, 10, 28, .28), 0 1px 3px rgba(0, 0, 0, .14);
}
#boChatFab::before { display: none; }
#boChatFab.dragging { box-shadow: 0 14px 34px rgba(17, 10, 28, .4); }
#boChatPanel { border-color: var(--line, #E8E4EE); }

/* ── Panier de l'en-tête : APLAT CLAIR ───────────────────────────────────── */
/* Le remplir de la couleur vive de la boutique le rendait bien visible, mais il
   ramenait sur la barre exactement ce qu'on venait d'en retirer : une tache
   saturée. Sur un fond profond, le contraste le plus fort n'est pas une couleur —
   c'est le CLAIR. Un aplat ivoire, franc, sans dégradé ni lueur : il se détache
   plus qu'un bouton rose, et il ne coûte aucune couleur à l'harmonie.
   Le caddie passe dans la teinte sombre de la barre, donc il appartient encore à
   la maison. */
.bo-head-btn {
  background: #FBFAFC;
  color: var(--head-2);
  border: none;
  box-shadow: 0 2px 10px rgba(0, 0, 0, .18);
}
.bo-head-btn:active { transform: scale(.92); background: #F1EEF4; }
/* La pastille du caddie était REDÉFINIE ici, écrasant sa déclaration d'origine. Tout ce
   qu'elle apportait (le liseré clair du bouton) a été fusionné dans `.bo-head-badge`
   plus haut dans ce fichier. */

/* ── Titres de catégorie ─────────────────────────────────────────────────── */
/* Une barre d'accordéon n'offre presque aucune prise : un mot, un chevron. Le peu
   qu'il y a, autant le soigner. Le titre reçoit un DÉGRADÉ CASSÉ — encre sur la
   première moitié, puis bascule vers la teinte de la boutique. Le mot se termine
   dans la couleur de la maison sans qu'aucun aplat ne soit ajouté à la page.
   L'effet est appliqué au texte lui-même (background-clip), donc il ne coûte ni
   image, ni élément, ni pixel de plus.
   @supports est INDISPENSABLE : sans le clip, `color: transparent` rendrait tous
   les titres purement et simplement invisibles. */
.accordion-title { font-size: 15px; letter-spacing: -.1px; }
@supports ((-webkit-background-clip: text) or (background-clip: text)) {
  .accordion-title {
    background: linear-gradient(96deg, var(--ink) 0%, var(--ink) 44%, var(--accent) 100%);
    -webkit-background-clip: text; background-clip: text;
    -webkit-text-fill-color: transparent; color: transparent;
  }
  /* Ouverte, la bascule vient plus tôt : la catégorie qu'on lit s'affirme un peu
     plus que les autres, sans changer de taille ni de graisse. */
  .accordion-section.open .accordion-title {
    background: linear-gradient(96deg, var(--ink) 0%, var(--accent) 78%);
    -webkit-background-clip: text; background-clip: text;
  }
}

/* ── Derniers liens hors palette ─────────────────────────────────────────── */
/* « ← Continuer mes achats » était en bleu (#1565C0 hérité), tout comme les prix
   de style.css : une couleur qui n'appartenait à aucune boutique. */
.back-link { color: var(--accent); }
.product-card .price, .product-detail .price { color: var(--ink); }

/* ═══════════════════════════════════════════════════════════════════════════
   HALO DU PANIER · ASSISTANTE · CHAMPS — dernière passe
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── Le panier devient un médaillon ─────────────────────────────────────── */
/* Le bouton reste blanc — c'est lui qu'on doit voir. Autour, un anneau dans la
   teinte EXACTE de la barre, portant la même trame de hachures : le halo n'ajoute
   donc aucune couleur à la page, il ne fait que creuser un relief autour du
   bouton. Un liseré clair l'isole du fond, sinon un anneau de la couleur du fond
   serait purement et simplement invisible.
   L'anneau est un pseudo-élément, pas une ombre : une box-shadow ne sait pas
   porter de motif. */
/* `backdrop-filter` doit être ANNULÉ ici, sinon rien ne marche : il crée un contexte
   d'empilement sur le bouton, et l'anneau en `z-index: -1` reste alors prisonnier de ce
   contexte — au lieu de passer derrière le bouton, il se peint PAR-DESSUS et recouvre le
   panier. Le flou ne servait plus à rien depuis que le fond est un aplat opaque. */
.bo-head-btn {
  position: relative; overflow: visible;
  backdrop-filter: none; -webkit-backdrop-filter: none;
}
.bo-head-btn::before {
  content: ''; position: absolute; inset: -7px; border-radius: 19px; z-index: -1;
  background:
    repeating-linear-gradient(135deg, rgba(255, 255, 255, .07) 0 1px, transparent 1px 6px),
    linear-gradient(165deg, var(--head-1), var(--head-2));
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .22),
              0 4px 18px rgba(0, 0, 0, .3);
}
/* Deuxième cercle, très pâle et plus large : c'est lui qui fait « halo » et
   détache l'ensemble du bandeau, sans jamais devenir une tache. */
.bo-head-btn::after {
  content: ''; position: absolute; inset: -13px; border-radius: 25px; z-index: -2;
  background: radial-gradient(closest-side, rgba(255, 255, 255, .14), transparent 72%);
  pointer-events: none;
}
.bo-head-badge { z-index: 2; }

/* ── Assistante : la page entière suit la boutique ──────────────────────── */
/* Le panneau était bâti sur le violet BoutiqueOuf en dur (#7C3AED, #C026D3…).
   Chez une gérante en rose, il ouvrait une fenêtre d'une autre application par
   dessus la sienne. Tout passe sur la teinte de la boutique. */
/* `.boChip svg` et `#boChatSend` retirés de cette liste : les suggestions et le bouton
   d'envoi n'existent plus depuis que l'assistante de la vitrine ne prend plus de message. */
#boChatHead .boAvatar,
#boChatFab { color: #fff; }
#boChatHead .boAvatar { background: linear-gradient(165deg, var(--head-1), var(--head-2)) !important; }
#boChatTitle, #boChatUsage b { color: var(--ink) !important; }
.boMsg.me, .boMsg.user, .boBubble.me {
  background: linear-gradient(165deg, var(--head-1), var(--head-2)) !important;
  color: #fff !important;
}
#boChatUsage { background: var(--accent-tint) !important; border-color: var(--border) !important; }
.boTyping span { background: var(--accent) !important; }

/* ── Assistante : le clavier ne doit plus pousser le panneau hors écran ─── */
/* Sur Android et iOS, l'ouverture du clavier réduit la fenêtre visible sans que
   la page en soit informée : le panneau, dimensionné en `vh`, gardait sa hauteur
   d'avant et se retrouvait à moitié sous le clavier — on ne voyait plus ni la
   question ni la réponse.
   `dvh` (dynamic viewport height) suit la zone RÉELLEMENT visible et se
   recalcule quand le clavier apparaît. Le repli en `vh` reste pour les vieux
   navigateurs. Le champ de saisie est ancré en bas du panneau, donc il remonte
   avec lui au lieu d'être poussé dehors. */
#boChatPanel { height: min(620px, 85vh); }
@supports (height: 100dvh) {
  #boChatPanel { height: min(620px, 82dvh); max-height: calc(100dvh - 20px); }
}
/* En plein écran mobile, le panneau occupe la place disponible plutôt qu'une
   hauteur fixe : c'est la seule façon de rester au-dessus du clavier. */
@media (max-width: 560px) {
  #boChatPanel {
    left: 8px; right: 8px; bottom: 8px; width: auto;
    height: min(560px, 78dvh, calc(100dvh - 90px));
  }
}

/* ── Champs de formulaire ───────────────────────────────────────────────── */
/* « Ton téléphone * » : l'astérisque est une convention de formulaire administratif.
   La plupart des champs sont obligatoires ici — c'est donc l'EXCEPTION qu'il faut
   signaler, et en toutes lettres. */
.form-group label .opt {
  font-style: normal; font-size: 11px; font-weight: 600;
  color: var(--muted); letter-spacing: .2px; margin-left: 5px;
}
/* Les icônes glissées dans les libellés et les titres prennent la couleur du texte
   qui les porte et s'en écartent d'un cheveu — assez pour respirer, pas assez
   pour former une colonne. */
.form-group label [data-ico], .order-form h2 [data-ico],
.status-badge [data-ico], .back-link [data-ico] { margin-right: 5px; opacity: .78; }
.order-form h2 [data-ico] { opacity: .55; }

/* ── Page « Moi » et formulaires : dernières couleurs hors palette ───────── */
/* Ces règles ont été écrites avant la palette : elles pointent encore --primary,
   c'est-à-dire la teinte de la boutique À PLEINE SATURATION. Sur du blanc, un
   #E91E63 pur vibre — c'est le « rose trop pur » qui sortait du cadre. Elles
   passent sur --accent, la même teinte tempérée, comme le reste de l'app. */
h2[data-testid$="mes-informations"]::before,
h3[data-testid$="adresses-livraison"]::before,
.section-title::before { background: var(--accent) !important; }

[data-geo] {
  border-color: color-mix(in srgb, var(--accent) 40%, transparent) !important;
  color: var(--accent) !important;
}
[data-geo]:active {
  background: var(--accent-tint) !important;
}
/* Cadre de saisie au focus — pour les FORMULAIRES seulement.
   La version précédente visait input, select et textarea sans distinction : elle attrapait aussi
   la zone de texte de l'assistante, qui se retrouvait cernée d'un rectangle rose dès qu'on
   commençait à écrire — par-dessus son propre repère, donc deux cadres imbriqués. Le chat n'est
   pas un formulaire : il est explicitement écarté. */
.order-form input:focus, .order-form select:focus,
input:focus, select:focus, textarea:focus {
  border-color: var(--accent) !important;
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 14%, transparent) !important;
}

/* Sélections, pastilles diverses restées sur la teinte pure. */
.payment-option.selected, .bo-car-dot.on { background: var(--accent) !important; }
.payment-option.selected { border-color: var(--accent) !important; }
.bo-car-dot { background: var(--muted) !important; opacity: .38; }
.bo-car-dot.on { opacity: 1; }

/* ── Sélecteur de pays (téléphone) ──────────────────────────────────────── */
/* Les libellés étaient en #374151 / #6b7280 : des gris BLEUS de bibliothèque, la
   dernière poche de teinte étrangère du parcours de commande. Ils passent sur les
   neutres de la palette, qui portent déjà un souffle de la couleur de la boutique.
   (Les drapeaux gardent leurs vraies couleurs : un drapeau retouché ne se
   reconnaît plus, et c'est précisément à ça qu'il sert.) */
.bo-phone-item-name, .bo-phone-list, .bo-phone-btn-dial { color: var(--ink) !important; }
.bo-phone-item-dial, .bo-phone-hint, .bo-phone-btn-chevron { color: var(--muted) !important; }
.bo-phone-item:hover, .bo-phone-item.on { background: var(--accent-tint) !important; }
.bo-phone-list, .bo-phone-btn { border-color: var(--border) !important; }

/* ═══════════════════════════════════════════════════════════════════════════
   FORMULAIRES ET PANIER — refonte
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── Champ « boîte » ──────────────────────────────────────────────────────
   L'ancien empilement libellé-gris / rectangle-gris se répétait dix fois de
   suite : dix lignes identiques, aucun relief, on ne savait plus où l'on en
   était. C'est le modèle Stripe / Shopify qui est repris ici — le libellé entre
   DANS le cadre, en tout petit, et la valeur saisie s'inscrit en dessous, grande
   et nette. Chaque champ devient un objet qu'on remplit, plus une ligne dans un
   questionnaire. Le cadre entier réagit au focus, pas seulement le champ : la
   cible visuelle correspond enfin à la zone qu'on touche.
   La taille 16 px n'est pas un choix esthétique : en dessous, iOS ZOOME la page
   à chaque fois qu'on touche un champ, et la cliente doit repincer pour lire. */
.form-group {
  position: relative;
  margin-bottom: 10px;
  padding: 9px 14px 8px;
  background: #fff;
  border: 1.5px solid var(--border);
  border-radius: 14px;
  transition: border-color .15s ease, box-shadow .15s ease;
}
.form-group:focus-within {
  border-color: var(--accent);
  box-shadow: 0 0 0 3.5px color-mix(in srgb, var(--accent) 13%, transparent);
}
.form-group label {
  display: block; margin: 0 0 1px;
  font-size: 10.5px; font-weight: 700; letter-spacing: .55px;
  text-transform: uppercase; color: var(--muted);
}
.form-group input, .form-group select, .form-group textarea {
  width: 100%; padding: 1px 0 0; margin: 0;
  border: none !important; background: transparent !important;
  font-size: 16px; font-weight: 600; color: var(--ink);
  font-family: inherit; border-radius: 0;
}
.form-group input:focus, .form-group select:focus, .form-group textarea:focus {
  outline: none; box-shadow: none !important; border: none !important;
}
.form-group input::placeholder { color: #B7B2C2; font-weight: 500; }
/* Le sélecteur de pays vit dans le même cadre : il perd le sien, sinon on voit
   une boîte dans une boîte. */
.form-group .bo-phone-wrap { display: flex; align-items: center; gap: 8px; }
.form-group .bo-phone-btn {
  border: none !important; background: transparent !important;
  padding: 0 !important; font-size: 16px !important; font-weight: 700;
  color: var(--ink) !important;
}

/* Titres de section : une étiquette, pas un titre. Ils séparent « livraison » de
   « paiement » sans peser autant qu'un vrai niveau de titre. */
.order-form h2 {
  display: flex; align-items: center; gap: 7px;
  font-size: 10.5px !important; font-weight: 800; letter-spacing: 1.6px;
  text-transform: uppercase; color: var(--muted);
  margin: 22px 0 10px !important;
}
.order-form { padding: 0 !important; background: transparent !important; }
.order-form h2:first-child { margin-top: 4px !important; }

/* Boutons radio et cases : le bleu du système n'appartient à aucune boutique.
   `accent-color` recolore le contrôle NATIF — donc sans le remplacer par un faux
   bouton en CSS, qui perdrait l'accessibilité et le comportement au clavier. */
input[type="radio"], input[type="checkbox"] { accent-color: var(--accent); }
.payment-option {
  border: 1.5px solid var(--border) !important; border-radius: 14px !important;
  padding: 13px 14px !important; margin-bottom: 8px !important;
  background: #fff !important; transition: border-color .15s ease;
}
.payment-option.selected {
  background: #fff !important;
  border-color: var(--accent) !important;
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 12%, transparent);
}
.payment-option .payment-icon { color: var(--muted); font-size: 20px; }
.payment-option.selected .payment-icon { color: var(--accent); }

/* ── Panier : le récapitulatif ────────────────────────────────────────────
   « Panier (1) » écrit avec des parenthèses, c'est de la notation de programmeur.
   Le compte devient une pastille à côté du titre. Le total, lui, était en gros
   et en couleur pour « faire important » : dans un récapitulatif, la hiérarchie
   se lit au CONTRASTE et à l'alignement, pas à la taille. Les lignes secondaires
   passent en gris, le total reste en encre — il ressort d'autant mieux que le
   reste s'efface. */
[data-testid="cart-section-title"] {
  display: flex; align-items: center; gap: 9px;
  font-size: 21px; font-weight: 800; letter-spacing: -.4px; color: var(--ink);
  margin: 4px 0 14px;
}
.bo-compte {
  font-size: 11.5px; font-weight: 800; letter-spacing: .2px;
  min-width: 22px; height: 22px; padding: 0 7px; border-radius: 999px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--accent-tint); color: var(--accent);
}
.cart-total {
  padding: 14px 16px !important;
  border: 1px solid var(--border); border-radius: 16px;
}
.cart-total .row {
  font-size: 13.5px; color: var(--muted); font-weight: 600;
  margin-bottom: 7px;
}
.cart-total .row.total {
  font-size: 15px !important; color: var(--ink) !important; font-weight: 700 !important;
  padding-top: 11px; margin-top: 3px;
  border-top: 1px solid var(--border);
}
/* Seul le MONTANT grossit — le mot « Total » reste à sa place. */
.cart-total .row.total span:last-child {
  font-size: 20px; font-weight: 800; letter-spacing: -.5px;
}

/* ── Quantité et suppression ──────────────────────────────────────────────
   Les « − » et « + » étaient des caractères de police : leur épaisseur et leur
   centrage changeaient d'un téléphone à l'autre. Ce sont des traits dessinés
   maintenant, dans un groupe unique aux bords arrondis — le modèle de tous les
   paniers modernes. */
/* LE COMPTEUR REFAIT (JM, le 7 octobre 2026, capture : « le design est vilain, ça m'énerve »).
   C'était deux carrés blancs collés dans un ovale gris : on ne voyait pas où appuyer. Une
   pilule lavande, deux RONDS : « − » blanc à liseré, « + » plein, dans la couleur de la
   boutique, parce que c'est le geste qu'on espère. Le chiffre au milieu, en gras. */
.cart-item .qty-row {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 4px; border: 0; border-radius: 999px;
  background: color-mix(in srgb, var(--accent) 9%, #fff); overflow: visible;
}
.qty-btn {
  width: 32px !important; height: 32px !important; border-radius: 50% !important;
  display: flex; align-items: center; justify-content: center; cursor: pointer;
  transition: transform .1s ease;
}
.qty-btn:first-child {
  background: #fff !important; color: var(--accent) !important;
  border: 1.5px solid color-mix(in srgb, var(--accent) 30%, transparent) !important;
}
.qty-btn:last-child {
  background: var(--accent) !important; color: #fff !important; border: 0 !important;
  box-shadow: 0 4px 6px -3px color-mix(in srgb, var(--accent) 55%, transparent);
}
.qty-btn:active { transform: scale(.9); }
.qty-val { min-width: 22px; text-align: center; font-size: 15px; font-weight: 800; color: var(--ink); }
.cart-item .remove-btn {
  color: var(--muted) !important; font-size: 17px !important;
  background: transparent !important; border: none !important;
}
.cart-item .remove-btn:active { color: var(--ink) !important; }

/* ── « Continuer mes achats » ─────────────────────────────────────────────
   C'était un texte coloré perdu en haut de page, ni bouton ni titre. Il devient
   une pastille à contour, calée à gauche : on voit tout de suite que ça se
   touche, et ça ne coûte aucune couleur. */
.back-link {
  display: inline-flex; align-items: center; gap: 6px;
  margin: 12px 16px 2px; padding: 8px 15px 8px 11px !important;
  border: 1px solid var(--border); border-radius: 999px;
  background: #fff; color: var(--ink) !important;
  font-size: 13px !important; font-weight: 600 !important;
  box-shadow: 0 1px 2px rgba(18, 12, 26, .04);
}
.back-link [data-ico] { opacity: .55; margin: 0; font-size: 15px; }
.back-link:active { background: var(--accent-tint); border-color: var(--accent); }

/* ── Bandeau hors ligne ───────────────────────────────────────────────────
   Une pastille discrète en bas, au-dessus de la navigation. La boutique marche
   hors ligne : l'annoncer en ambre d'alerte, en haut, sur toute la largeur,
   c'était traiter une fonctionnalité comme une panne — et décaler toute la page
   au passage. */
.offline-banner {
  position: fixed !important;
  top: auto !important; left: 50% !important; right: auto !important;
  bottom: calc(72px + env(safe-area-inset-bottom, 0px)) !important;
  transform: translateX(-50%);
  width: auto !important;
  display: inline-flex; align-items: center; gap: 7px;
  padding: 8px 15px !important;
  border: none !important; border-radius: 999px !important;
  background: var(--head-1) !important; color: #fff !important;
  font-size: 12.5px !important; font-weight: 700 !important;
  box-shadow: 0 6px 20px rgba(17, 10, 28, .28) !important;
  white-space: nowrap;
}

/* ── Assistante : le « Bonjour » ──────────────────────────────────────────
   Il était peint dans un dégradé violet écrit en dur : chez une gérante en rose,
   la première chose que disait l'assistante était d'une autre couleur que sa
   boutique. Le dégradé est conservé — c'est lui qui donne son allure au mot —
   mais construit à partir de la teinte du site. */
/* Le « Bonjour » était INVISIBLE : on ne voyait qu'une barre violette pleine largeur.
   Cause exacte, mesurée dans le navigateur : `background` est un raccourci, et un raccourci
   REMET À ZÉRO les propriétés qu'il contient — dont `background-clip`, repassé à `border-box`.
   Le dégradé se peignait donc sur la boîte au lieu d'être découpé dans les lettres, pendant que
   `-webkit-text-fill-color: transparent` effaçait le texte. Deux réglages qui se contredisent.

   Plutôt que de rafistoler ce montage, on l'abandonne : la salutation prend un BANDEAU sombre et
   le texte passe en blanc, avec un dégradé léger du blanc pur vers un blanc voilé. C'est lisible
   quoi qu'il arrive — un texte blanc sur fond sombre ne peut pas disparaître — et c'est demandé :
   le « Bonjour » ne suit PAS la couleur de la boutique, il reste le même partout. Le bandeau est
   volontairement neutre, donc il ne rajoute pas une teinte de plus à la palette. */
.boGreet {
  background: linear-gradient(150deg, #241C33 0%, #16111F 100%);
  border-radius: 20px;
  padding: 20px 20px 22px !important;
  margin-bottom: 4px;
}
.boGreet h2 {
  background-image: linear-gradient(96deg, #FFFFFF 0%, #FFFFFF 45%, rgba(255,255,255,.68) 100%);
  background-clip: text; -webkit-background-clip: text;
  -webkit-text-fill-color: transparent; color: transparent;
  margin: 0 0 6px;
}
/* Filet de sécurité : si le navigateur ne sait pas découper un fond dans du texte, la règle
   ci-dessus rendrait les lettres transparentes — donc invisibles, exactement le défaut qu'on
   corrige. On repeint alors en blanc plein. */
@supports not ((-webkit-background-clip: text) or (background-clip: text)) {
  .boGreet h2 { -webkit-text-fill-color: #fff; color: #fff; background-image: none; }
}
.boGreet p { color: rgba(255, 255, 255, .74) !important; }

/* Le 16 px du champ était repris à 15 px par une règle plus spécifique écrite plus haut
   (`.order-form input[type="tel"]`, poids 0-2-1 contre 0-1-1). On se place au même niveau, plus
   bas dans la feuille. Ce n'est pas un détail de goût : sous 16 px, iOS ZOOME la page dès qu'on
   touche un champ, et la cliente doit repincer pour lire la suite du formulaire. */
.form-group input[type], .form-group select, .form-group textarea {
  font-size: 16px;
}

/* ── Ligne d'article du panier ────────────────────────────────────────────
   Elle était étranglée : la colonne du milieu n'avait aucune largeur minimale, si
   bien que le prix se coupait en deux lignes et « Plus que 3 en stock ! » en
   quatre. Le tout en violet et sur fond jaune vif — un avertissement d'autoroute
   posé au milieu d'une vitrine.
   La ligne est repensée en deux étages : nom et prix en haut, actions en bas.
   Chacun a la largeur de la carte, donc plus rien ne se coupe. */
.cart-item {
  display: grid !important;
  grid-template-columns: 64px 1fr;
  grid-template-areas: "vignette infos" "vignette actions";
  gap: 4px 12px !important;
  padding: 12px 14px !important;
  border: 1px solid var(--border);
  border-radius: 16px !important;
  align-items: start;
}
.cart-item .thumb { grid-area: vignette; align-self: center; border-radius: 12px !important; }
.cart-item .details { grid-area: infos; min-width: 0; }
.cart-item .cart-actions {
  grid-area: actions;
  justify-content: space-between !important;
  align-self: center; width: 100%;
}
.cart-item .item-name {
  font-size: 14.5px; font-weight: 600; color: var(--ink);
  line-height: 1.3;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
/* Le prix en encre, comme sur les vignettes : c'est l'information la plus lue, elle
   n'a pas besoin d'être colorée pour être trouvée. */
.cart-item .item-price {
  font-size: 15px !important; font-weight: 800 !important;
  color: var(--ink) !important; letter-spacing: -.3px;
  margin-top: 3px !important; white-space: nowrap;
}
/* Le stock : une ligne, jamais coupée, sans fond ni cadre. Le jaune fluo et le
   rouge sang criaient une urgence que trois articles restants ne justifient pas. */
.cart-item .stock-badge, .cart-item .stock-badge.urgent, .cart-item .stock-badge.out {
  display: block !important;
  font-size: 11px !important; font-weight: 700 !important;
  background: none !important; border: none !important;
  padding: 0 !important; margin-top: 3px !important;
  white-space: nowrap;
}
.cart-item .stock-badge.urgent { color: var(--accent) !important; }
.cart-item .stock-badge, .cart-item .stock-badge.out { color: var(--muted) !important; }

/* L'en-tête du panneau ne doit JAMAIS partir : c'est lui qui porte le bouton fermer.
   Le panneau est une colonne flex — l'en-tête ne défile donc pas avec les messages — mais on
   verrouille sa hauteur (flex: none) pour qu'il ne se laisse pas comprimer quand la place
   manque, clavier ouvert. Sans ça il s'écrase jusqu'à devenir illisible avant de disparaître. */
#boChatHead {
  position: sticky; top: 0; z-index: 3;
  flex: none;
}
#boChatBody { flex: 1 1 auto; min-height: 0; overflow-y: auto; }

/* ── Blocs pliants de la page « Moi » ────────────────────────────────────── */
/* La page alignait dix champs d'affilée : on ne voyait ni où elle commençait ni où elle
   finissait, et remplir une adresse obligeait à traverser toute l'identité. Repliés, les deux
   blocs tiennent sur un écran — on ouvre celui dont on a besoin. Même dessin que l'accordéon de
   la boutique, pour que la cliente reconnaisse le geste d'une page à l'autre. */
.bo-pliant {
  background: #fff; border: 1px solid var(--border); border-radius: 16px;
  margin-bottom: 12px; overflow: hidden;
}
.bo-pliant-tete {
  width: 100%; display: flex; align-items: center; justify-content: space-between;
  gap: 10px; padding: 15px 16px;
  background: none; border: none; cursor: pointer; text-align: left;
}
.bo-pliant-tete h2, .bo-pliant-tete h3 {
  margin: 0; font-size: 15.5px; font-weight: 800; letter-spacing: -.2px; color: var(--ink);
}
.bo-pliant-chev { display: flex; color: var(--muted); transition: transform .25s cubic-bezier(.34,1.4,.64,1); }
.bo-pliant-chev svg { width: 18px; height: 18px; }
.bo-pliant.open .bo-pliant-chev { transform: rotate(180deg); color: var(--ink); }
/* Replié par défaut : `max-height: 0` plutôt que `display: none` pour garder l'ouverture animée.
   Le corps reste dans le DOM, donc les scripts du profil trouvent toujours leurs champs. */
.bo-pliant-corps { max-height: 0; overflow: hidden; transition: max-height .3s ease; }
.bo-pliant.open .bo-pliant-corps { max-height: 4000px; }
.bo-pliant-corps > * { padding-left: 16px; padding-right: 16px; }
.bo-pliant-corps > .order-form { padding-bottom: 14px; }
.bo-pliant.open .bo-pliant-tete { box-shadow: inset 3px 0 0 var(--accent); }

/* Coupe les animations le temps d'une image, pour mesurer la mise en page RÉELLE après un repli.
   Sans elle, la hauteur lue juste après le clic est encore celle d'avant, et la compensation du
   défilement — celle qui évite le va-et-vient — serait calculée sur une valeur périmée. */
.category-accordion.bo-sans-anim,
.category-accordion.bo-sans-anim * { transition: none !important; animation: none !important; }

/* Ancrage de défilement DÉSACTIVÉ sur la liste des catégories.
   Chrome corrige tout seul la position quand du contenu au-dessus change de hauteur, pour que
   la page « ne bouge pas sous les yeux ». Ici cette bonne intention se retournait contre nous :
   pendant le repli d'une catégorie, le navigateur ramenait le défilement là où il était et
   ANNULAIT le glissement qu'on venait de lancer. Mesuré : scrollTo(311) appelé, page restée à
   900. C'est ce combat entre les deux qui donnait l'aller-retour. */
/* Il ne suffit PAS de l'exclure sur la liste des catégories : le navigateur choisit alors un
   autre élément comme point d'ancrage et corrige quand même. Mesuré après cette première
   tentative : le glissement partait, puis s'arrêtait au bout de 50 px. La désactivation doit
   donc porter sur le document entier — c'est lui qui défile. */
html, body, .category-accordion { overflow-anchor: none; }

/* ── Salutation de l'assistante : plus de bandeau noir ───────────────────── */
/* Le bandeau sombre était une réponse à un bug (le texte transparent invisible), pas un choix :
   il posait un rectangle noir en haut d'un panneau blanc, comme une pièce rapportée. On revient
   au blanc, avec un texte simplement écrit — lisible sans artifice. */
.boGreet {
  background: none !important;
  border-radius: 0;
  padding: 4px 2px 2px !important;
  margin-bottom: 2px;
}
/* Le dégradé sur le mot lui-même : gris très foncé qui s'éclaircit à peine vers la fin. Assez
   pour qu'on sente une intention typographique, jamais assez pour gêner la lecture — et il ne
   dépend d'aucune couleur de boutique, comme demandé. */
.boGreet h2 {
  background-image: linear-gradient(96deg, #1B1626 0%, #1B1626 40%, #6A6478 100%) !important;
  background-clip: text; -webkit-background-clip: text;
  -webkit-text-fill-color: transparent; color: transparent;
  font-size: 27px; font-weight: 700; letter-spacing: -.6px; line-height: 1.15;
  margin: 0 0 4px !important;
}
@supports not ((-webkit-background-clip: text) or (background-clip: text)) {
  .boGreet h2 { -webkit-text-fill-color: #1B1626; color: #1B1626; background-image: none !important; }
}
.boGreet p { color: #7A7488 !important; font-size: 20px !important; line-height: 1.3; }

/* ── Zone de saisie, façon Gemini ────────────────────────────────────────── */



/* ── Diapos de promotion avec PHOTO ──────────────────────────────────────── */
/* Une promotion sur fond blanc se lit, mais ne donne envie de rien. Avec la photo d'un article
   derrière, elle montre CE dont elle parle — c'est le principe de toutes les vitrines.
   Le texte reste lisible grâce à un voile sombre dégradé, posé seulement en bas : une photo
   assombrie en entier perd tout son intérêt, alors qu'un dégradé ne mange que la zone du texte. */
.bo-car-slide.promo.avec-photo {
  min-height: 210px;
  align-items: flex-end;
  border: none;
  background: #2A2137;
}
.bo-car-slide.promo.avec-photo img {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; display: block;
}
/* LA VIDÉO D UNE PUBLICATION occupe la diapo exactement comme la photo : même cadrage, même
   voile par-dessus, même texte dessous. C est ce que JM demande — << que ça se présente comme
   le carrousel des promos, juste que ce sera en vidéo >>. Une règle à part lui aurait donné
   une autre allure au premier changement de style. */
.bo-car-slide.promo.avec-photo video {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; display: block;
}
.bo-car-slide.promo.avec-photo::after {
  background: linear-gradient(to top,
    rgba(12, 8, 20, .86) 0%,
    rgba(12, 8, 20, .55) 34%,
    rgba(12, 8, 20, .10) 62%,
    transparent 100%) !important;
}
.bo-car-slide.promo.avec-photo .bo-car-promo-in { color: #fff; }
.bo-car-slide.promo.avec-photo strong {
  color: #fff !important;
  font-size: 21px; font-weight: 700; line-height: 1.28;
  text-shadow: 0 1px 12px rgba(0, 0, 0, .5);
}
.bo-car-slide.promo.avec-photo .bo-car-badge {
  color: rgba(255, 255, 255, .92) !important;
}
.bo-car-slide.promo.avec-photo .bo-car-cta {
  color: #fff !important;
  border-bottom-color: rgba(255, 255, 255, .5) !important;
}

/* ═══════════════════════════════════════════════════════════════════════════
   ACCUEIL EN RANGÉES — les catégories ne se déplient plus, elles défilent
   ═══════════════════════════════════════════════════════════════════════════
   Un accordéon cache : il faut deviner ce qu'il contient, l'ouvrir, le refermer, et la page
   n'arrête pas de changer de hauteur sous les doigts. Une rangée montre trois articles tout de
   suite et laisse glisser pour voir la suite. La cliente parcourt toute la boutique en
   descendant, sans jamais rien ouvrir — c'est la disposition de toutes les applications qui
   présentent un catalogue. Tout le jeu de correctifs de défilement disparaît avec l'accordéon. */
.bo-rangees { display: block; }
.bo-rangee {
  background: none !important;
  border: none !important;
  border-radius: 0 !important;
  box-shadow: none !important;
  margin: 0 0 22px;
  overflow: visible;
}

/* Tête : le titre à gauche, « voir plus » à droite. Rien d'autre. */
.bo-rangee-tete {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 0 16px 10px !important;
  background: none !important;
  box-shadow: none !important;
  cursor: default;
}
.bo-rangee .accordion-header-left { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.bo-rangee .accordion-title { font-size: 17px; font-weight: 800; letter-spacing: -.35px; }
.bo-rangee .accordion-promo-text {
  width: 100%; font-size: 11.5px; color: var(--muted); font-weight: 600; margin-top: 1px;
}
.bo-rangee-plus {
  flex: none; display: inline-flex; align-items: center; gap: 3px;
  background: none; border: none; cursor: pointer; padding: 4px 0;
  font-size: 12.5px; font-weight: 600; color: var(--muted);
}
.bo-rangee-plus svg { width: 15px; height: 15px; }
.bo-rangee-plus:active { color: var(--ink); }

/* Le corps ne se replie plus : la limitation de hauteur et sa transition n'ont plus lieu d'être,
   et les laisser rognerait la rangée à 0. */
.bo-rangee .accordion-body { max-height: none !important; overflow: visible !important; transition: none !important; }

/* La piste : défilement horizontal au doigt, avec aimantation pour que les cartes ne s'arrêtent
   jamais à moitié coupées. Le débord latéral est volontaire — voir une carte dépasser du bord
   dit qu'il y en a d'autres, là où une rangée qui s'arrête net donne l'impression d'être finie. */
.bo-rangee .product-grid {
  display: flex !important;
  grid-template-columns: none !important;
  gap: 12px !important;
  padding: 2px 16px 6px !important;
  overflow-x: auto;
  scroll-snap-type: x proximity;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.bo-rangee .product-grid::-webkit-scrollbar { display: none; }
.bo-rangee .product-card {
  flex: 0 0 auto;
  width: 46vw; max-width: 190px;
  scroll-snap-align: start;
}

/* « Voir plus » : la rangée redevient une grille, pour tout voir d'un coup. */
.bo-rangee.deplie .product-grid {
  display: grid !important;
  grid-template-columns: 1fr 1fr !important;
  overflow-x: visible;
}
.bo-rangee.deplie .product-card { width: auto; max-width: none; }

/* ── Carrousel : les flèches sur l'image ─────────────────────────────────── */
/* Les pastilles blanches posaient deux ronds opaques par-dessus la photo, comme deux boutons
   collés après coup. En verre dépoli, elles laissent voir l'image au travers : on comprend
   qu'elles appartiennent à la diapo. Le contour clair les garde visibles sur une photo claire. */
.bo-car-arrow {
  background: rgba(20, 14, 30, .34) !important;
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  border: 1px solid rgba(255, 255, 255, .3) !important;
  color: #fff !important;
  box-shadow: 0 2px 10px rgba(0, 0, 0, .22) !important;
}
.bo-car-arrow:active { background: rgba(20, 14, 30, .5) !important; }

/* Espacements du carrousel, calés sur ceux des rangées : la diapo déborde légèrement à droite,
   même logique que les cartes — on voit qu'il y a une suite. */
.bo-carousel { margin-bottom: 20px; }
.bo-car-track { padding: 0 16px !important; gap: 12px !important; }
.bo-car-slide.promo { width: 84% !important; }

/* ── Rangées, deuxième passe ─────────────────────────────────────────────── */

/* LE TITRE À GAUCHE. Il se retrouvait à 105 px du bord, comme centré : le groupe de gauche
   occupait toute la largeur restante et le texte de promotion, mis à la ligne, poussait le titre
   au milieu. On sépare les trois éléments : le titre seul en premier, collé au bord ; le lien à
   droite ; la phrase de promotion sur sa propre ligne, sous les deux. */
.bo-rangee-tete {
  display: grid !important;
  grid-template-columns: 1fr auto;
  align-items: baseline;
  row-gap: 2px;
  padding: 0 16px 9px !important;
  text-align: left !important;
}
.bo-rangee .accordion-header-left {
  display: contents !important;          /* les enfants se placent eux-mêmes dans la grille */
}
.bo-rangee .accordion-title {
  grid-column: 1; grid-row: 1;
  justify-self: start;
  font-size: 18px; font-weight: 800; letter-spacing: -.4px;
}
/* La pastille « Promo » a disparu de la tête de rangée : la phrase juste en dessous dit déjà
   qu'il y a une promotion, en toutes lettres. Deux signaux pour la même information, c'est un de
   trop — et c'est lui qui déséquilibrait la ligne du titre. `shop.js` ne la pose plus du tout,
   ses règles sont donc parties avec elle. */
.bo-rangee .accordion-promo-text {
  grid-column: 1 / -1; grid-row: 2;
  font-size: 12px; color: var(--muted); font-weight: 600; margin: 0;
}
.bo-rangee-plus {
  grid-column: 2; grid-row: 1;
  justify-self: end;
}

/* PLUS DE BANDE GRISE AU-DESSUS DE LA PHOTO.
   La carte était un rectangle blanc bordé, contenant une image en `contain` posée sur un fond
   gris : au-dessus d'une photo verticale, ce fond formait une bande droite, et l'on voyait donc
   un bord droit AVANT le bord arrondi de l'image. C'est ce que montre la comparaison.
   La carte perd son cadre : c'est la PHOTO qui devient l'objet arrondi, et le texte se pose
   dessous, sans boîte. Le recadrage passe en `cover` — mieux vaut rogner un peu une photo que
   l'entourer de gris. */
.bo-rangee .product-card {
  background: none !important;
  border: none !important;
  box-shadow: none !important;
  border-radius: 0 !important;
  overflow: visible !important;
}
.bo-rangee .product-card .img-wrap {
  border-radius: 18px !important;
  overflow: hidden !important;
  background: #F1EFF4 !important;
  aspect-ratio: 1 / 1;                    /* carré : le 4/5 essayé d'abord RALLONGEAIT la carte
                                             (289 px mesurés contre 265 avant) — l'image gagnait
                                             plus en hauteur que le cadre n'en perdait. */
  height: auto !important;
}
.bo-rangee .product-card .img-wrap img,
.bo-rangee .product-card .img-wrap video {
  width: 100%; height: 100%;
  object-fit: cover !important;           /* remplit le cadre : plus de bandes */
  background: none !important;
}
/* Le texte sous la photo, sans boîte ni retrait : il appartient à la carte par la proximité. */
.bo-rangee .product-card .info { padding: 7px 2px 0 !important; }
/* Le nom tient sur UNE ligne : sur deux, la moitié des cartes d'une rangée devenaient plus
   hautes que l'autre moitié, et les prix ne s'alignaient plus d'une carte à l'autre. */
.bo-rangee .product-card .name {
  font-size: 13.5px; font-weight: 600; line-height: 1.3;
  display: -webkit-box; -webkit-line-clamp: 1; -webkit-box-orient: vertical; overflow: hidden;
}
.bo-rangee .product-card .price { font-size: 14.5px; margin-top: 2px; }
.bo-rangee .product-card .price .cur { font-size: 10.5px; }
.bo-rangee .product-card .stock-badge,
.bo-rangee .product-card .stock-ok,
.bo-rangee .product-card .stock-low,
.bo-rangee .product-card .stock-out { font-size: 10.5px !important; margin-top: 2px !important; }
.bo-rangee .product-card:active { transform: scale(.97); }

/* Le bouton d'ajout suit le coin arrondi de la photo. */
.bo-rangee .cart-overlay-btn { top: 9px; right: 9px; }

/* ── Bandeau de filtre : juste « voir tout » ─────────────────────────────── */
/* « Tu regardes Robes » racontait ce que la cliente voyait déjà : la boutique n'affiche plus que
   des robes, elle le sait. Ne reste que la sortie, alignée sur les titres — un lien discret,
   là où l'œil revient naturellement. */
.bo-filtre {
  display: flex; align-items: center;
  background: none !important; border: none !important;
  margin: 0 0 14px !important; padding: 0 16px !important;
}
.bo-filtre-tout {
  display: inline-flex; align-items: center; gap: 6px;
  border: none !important; background: none !important;
  padding: 0 !important;
  color: var(--accent) !important;
  font-size: 14px !important; font-weight: 700 !important; letter-spacing: -.1px;
}
.bo-filtre-tout::after {
  content: ''; width: 15px; height: 15px; flex: none;
  background: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M9 5l7 7-7 7'/%3E%3C/svg%3E") center/contain no-repeat;
          mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M9 5l7 7-7 7'/%3E%3C/svg%3E") center/contain no-repeat;
}
.bo-filtre-tout:active { opacity: .6; transform: none !important; background: none !important; }

/* ── Raccourcis de catégories ────────────────────────────────────────────── */
/* Posés entre le carrousel et les rangées : c'est là qu'on les cherche, juste après avoir vu la
   promotion et juste avant d'entrer dans les rayons. Avec huit catégories, atteindre « Sacs »
   demandait sinon de faire défiler tout l'écran — et surtout on ne savait pas qu'il existait. */
.bo-raccourcis {
  display: flex; gap: 14px;
  padding: 2px 16px 18px;
  overflow-x: auto;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.bo-raccourcis::-webkit-scrollbar { display: none; }
.bo-raccourci {
  flex: 0 0 auto;
  display: flex; flex-direction: column; align-items: center; gap: 7px;
  width: 62px; padding: 0; border: none; background: none; cursor: pointer;
}
/* Pastille photo. Un carré très arrondi plutôt qu'un cercle : le cercle rogne les vêtements par
   les épaules, le carré arrondi garde la silhouette lisible tout en restant doux. */
.bo-raccourci-img {
  width: 58px; height: 58px; border-radius: 19px; overflow: hidden;
  background: var(--accent-tint);
  border: 1.5px solid transparent;
  display: block; flex: none;
  transition: border-color .16s ease, transform .16s ease;
}
.bo-raccourci-img img { width: 100%; height: 100%; object-fit: cover; display: block; }
.bo-raccourci-nom {
  font-size: 11.5px; font-weight: 600; color: var(--ink);
  line-height: 1.2; text-align: center;
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.bo-raccourci:active .bo-raccourci-img { transform: scale(.93); }
/* Le rayon qu'on vient de rejoindre reste marqué : sans ça, après le défilement, on ne sait plus
   sur lequel on a appuyé. */
.bo-raccourci.on .bo-raccourci-img { border-color: var(--accent); }
.bo-raccourci.on .bo-raccourci-nom { color: var(--accent); font-weight: 800; }

/* ── Raccourcis : tuiles à pictogramme ───────────────────────────────────── */
/* Les pastilles montraient la photo du premier article. C'était informatif, mais une photo change
   à chaque nouveau produit, tourne au vignettage illisible à 58 px, et huit photos différentes
   côte à côte font une mosaïque agitée juste sous le carrousel — l'inverse du calme cherché
   partout ailleurs. Un pictogramme est stable et se lit d'un coup d'œil.

   Les fonds varient par QUATRE INTENSITÉS de la même teinte, pas par quatre couleurs
   différentes. La référence en utilise quatre distinctes ; ici ce serait rouvrir la porte au
   patchwork qu'on vient de fermer. La variation reste perceptible — les tuiles ne se confondent
   pas — sans qu'aucune couleur étrangère n'entre dans la page. */
.bo-raccourci-tuile {
  width: 56px; height: 56px; border-radius: 18px;
  display: flex; align-items: center; justify-content: center;
  flex: none;
  transition: transform .16s cubic-bezier(.34, 1.4, .64, 1), box-shadow .16s ease;
}
.bo-raccourci.t0 .bo-raccourci-tuile { background: color-mix(in srgb, var(--accent) 13%, #fff); color: var(--accent); }
.bo-raccourci.t1 .bo-raccourci-tuile { background: color-mix(in srgb, var(--accent) 8%,  #fff); color: color-mix(in srgb, var(--accent) 78%, #55506180); }
.bo-raccourci.t2 .bo-raccourci-tuile { background: color-mix(in srgb, var(--head-1) 11%, #fff); color: var(--head-1); }
.bo-raccourci.t3 .bo-raccourci-tuile { background: #F2F0F5; color: #5A5468; }
.bo-raccourci:active .bo-raccourci-tuile { transform: scale(.9); }
/* Le rayon rejoint : la tuile se remplit franchement. Un simple contour se voyait mal à cette
   taille, et il fallait deviner lequel on venait d'appuyer. */
.bo-raccourci.on .bo-raccourci-tuile {
  background: var(--accent) !important; color: #fff !important;
  box-shadow: 0 6px 16px color-mix(in srgb, var(--accent) 34%, transparent);
}
.bo-raccourci.on .bo-raccourci-nom { color: var(--accent); font-weight: 800; }
.bo-raccourci-nom { font-size: 11.5px; font-weight: 600; color: var(--ink); }

/* ── Retour en haut ──────────────────────────────────────────────────────── */
/* Placé à la FIN de la liste, pas en flottant : la cliente n'y pense qu'une fois arrivée au bout,
   et un bouton flottant de plus se disputerait l'écran avec l'assistante. */
.bo-haut {
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  margin: 10px auto 30px; padding: 0;
  background: none; border: none; cursor: pointer;
}
.bo-haut-rond {
  width: 46px; height: 46px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: #fff; color: var(--accent);
  border: 1.5px solid var(--border);
  box-shadow: 0 2px 8px rgba(18, 12, 26, .07);
  transition: transform .2s cubic-bezier(.34, 1.5, .64, 1), box-shadow .2s ease, border-color .2s ease;
}
/* Au repos, la flèche respire très légèrement : elle a l'air prête à partir, sans clignoter ni
   attirer l'œil pendant qu'on regarde les articles juste au-dessus. */
@keyframes boPretADecoller {
  0%, 88%, 100% { transform: translateY(0); }
  94%           { transform: translateY(-3px); }
}
.bo-haut-rond { animation: boPretADecoller 3.4s ease-in-out infinite; }
.bo-haut:active .bo-haut-rond {
  border-color: var(--accent);
  box-shadow: 0 8px 20px color-mix(in srgb, var(--accent) 28%, transparent);
}
/* Au clic, elle décolle vraiment : elle monte, s'efface, et revient à sa place — le geste
   accompagne le retour en haut au lieu de le laisser se faire dans le dos de la cliente. */
@keyframes boDecollage {
  0%   { transform: translateY(0);     opacity: 1; }
  55%  { transform: translateY(-26px); opacity: 0; }
  56%  { transform: translateY(10px);  opacity: 0; }
  100% { transform: translateY(0);     opacity: 1; }
}
.bo-haut.decolle .bo-haut-rond { animation: boDecollage .6s cubic-bezier(.4, 0, .2, 1); }
.bo-haut-txt { font-size: 12px; font-weight: 600; color: var(--muted); letter-spacing: .1px; }

/* Les anciennes pastilles photo n'existent plus ; la règle reste au cas où une page en cache
   afficherait encore l'ancien balisage, pour qu'elle ne se retrouve pas sans style. */
.bo-raccourci-img { display: none; }

/* ── Tuiles : six teintes DISTINCTES ─────────────────────────────────────── */
/* Les quatre intensités d'une même teinte tenaient l'harmonie mais ne distinguaient rien : de
   loin, huit tuiles presque identiques. Six couleurs franches, choisies très désaturées et très
   claires — c'est ce qui les empêche de faire patchwork. La règle tenue partout ailleurs reste
   valable : la couleur de la boutique garde les actions (prix, liens, boutons) ; ici elle ne sert
   qu'à ÉTIQUETER un rayon, ce qui est un autre métier. */
.bo-raccourci.t0 .bo-raccourci-tuile { background: #FFEDE6; color: #C2410C; }   /* terre cuite */
.bo-raccourci.t1 .bo-raccourci-tuile { background: #E7F0FE; color: #1D4ED8; }   /* bleu encre   */
.bo-raccourci.t2 .bo-raccourci-tuile { background: #FDE9EF; color: #BE185D; }   /* rose profond */
.bo-raccourci.t3 .bo-raccourci-tuile { background: #FEF4DC; color: #A16207; }   /* ambre        */
.bo-raccourci.t4 .bo-raccourci-tuile { background: #E6F6EE; color: #15803D; }   /* vert doux    */
.bo-raccourci.t5 .bo-raccourci-tuile { background: #F0EBFD; color: #6D28D9; }   /* violet       */

/* ── Titres de rangée : chacun le sien ───────────────────────────────────── */
/* Huit titres identiques à la suite se ressemblaient tous ; en descendant on ne savait plus où
   l'on en était. Chaque rangée reprend le pictogramme ET la teinte de sa tuile : deux repères
   pour un seul rayon, accordés entre eux. Le titre lui-même reste en encre — colorer le texte
   aurait été le coup de trop. */
.bo-rangee-ico {
  grid-column: 1; grid-row: 1;
  justify-self: start; align-self: center;
  width: 28px; height: 28px; border-radius: 9px;
  display: inline-flex; align-items: center; justify-content: center;
  flex: none; margin-right: 8px;
}
.bo-rangee-ico svg { width: 17px; height: 17px; }
.bo-rangee-ico.t0 { background: #FFEDE6; color: #C2410C; }
.bo-rangee-ico.t1 { background: #E7F0FE; color: #1D4ED8; }
.bo-rangee-ico.t2 { background: #FDE9EF; color: #BE185D; }
.bo-rangee-ico.t3 { background: #FEF4DC; color: #A16207; }
.bo-rangee-ico.t4 { background: #E6F6EE; color: #15803D; }
.bo-rangee-ico.t5 { background: #F0EBFD; color: #6D28D9; }
/* Le pictogramme et le titre partagent la même case de la grille : ils forment une seule unité,
   collée au bord gauche, et le lien « voir plus » reste à droite. */
.bo-rangee-tete { grid-template-columns: auto 1fr auto; }
.bo-rangee .accordion-title { grid-column: 2; justify-self: start; }
.bo-rangee .accordion-promo-text { grid-column: 1 / -1; grid-row: 2; }
.bo-rangee-plus { grid-column: 3; grid-row: 1; }

/* ── Le prix sur la photo ────────────────────────────────────────────────── */
/* Sous l'image, entre le nom et le stock, le prix flottait au milieu d'un bloc de texte : dans
   une rangée où les cartes se suivent, on ne voyait plus à quel article il se rapportait. Posé
   en bas de la photo, au centre, il ne peut appartenir qu'à elle.
   Pastille blanche opaque plutôt que translucide : sur une photo claire, un fond translucide
   rendait les chiffres illisibles — et un prix qui se devine ne sert à rien. */
.bo-prix-photo {
  position: absolute; left: 50%; bottom: 8px; transform: translateX(-50%);
  z-index: 2;
  display: inline-flex; align-items: baseline; gap: 3px;
  padding: 5px 12px; border-radius: 999px;
  background: #fff;
  color: var(--ink);
  font-size: 13.5px; font-weight: 800; letter-spacing: -.3px;
  white-space: nowrap;
  box-shadow: 0 2px 10px rgba(12, 8, 20, .22);
}
.bo-prix-photo .cur { font-size: 9.5px; font-weight: 700; color: var(--muted); }
.product-card .img-wrap { position: relative; }
/* Le prix n'apparaît plus deux fois : celui du bloc de texte est retiré du balisage, la règle
   reste pour les pages encore en cache. */
.bo-rangee .product-card .info .price { display: none; }
.bo-rangee .product-card .info { padding: 6px 2px 0 !important; text-align: center; }
.bo-rangee .product-card .name { font-size: 13px; }

/* ── Retour en haut : moins de place ─────────────────────────────────────── */
/* Le bloc prenait près de 100 px en bas de page pour un bouton qu'on utilise une fois. Rond plus
   petit, marges resserrées, et le libellé passe sur la même ligne que la flèche : la fonction
   reste évidente, l'encombrement est divisé par deux. */
.bo-haut {
  flex-direction: row !important;
  gap: 9px !important;
  margin: 2px auto 14px !important;
  padding: 6px 14px 6px 6px;
  border-radius: 999px;
}
.bo-haut-rond { width: 34px !important; height: 34px !important; }
.bo-haut-rond svg { width: 16px; height: 16px; }
.bo-haut-txt { font-size: 12.5px; }

/* ── Flèche des raccourcis ───────────────────────────────────────────────── */
/* Au doigt, on fait glisser la bande sans y penser. À la souris, il n'y a rien à attraper — la
   barre de défilement est masquée — et les rayons au-delà du bord n'existent tout simplement pas
   pour qui visite depuis un ordinateur. Le dégradé sous la flèche évite qu'une tuile se retrouve
   coupée net derrière elle. */
/* ══════════════════════════════════════════════════════════════════════════════════════
   LE RÂTELIER RESTE SOUS L'EN-TÊTE QUAND ON DESCEND.
   ══════════════════════════════════════════════════════════════════════════════════════

   JM, le 20/10/2026 : « le râtelier qui doit rester en bas du header, en sticky, quand on
   scrolle dans shop ». Et, plus tôt : « quand on scrolle vers le bas, le râtelier n'est pas
   visible en sticky header, il se confond avec le header qui vient le masquer ».

   L'en-tête était collant, le râtelier non : il montait sous l'en-tête et disparaissait
   dessous. La cliente perdait ses rayons dès le premier article, et devait remonter toute
   la page pour en changer.

   ── POURQUOI UNE VARIABLE, ET PAS UN NOMBRE ──────────────────────────────────────────

   `top` doit valoir la hauteur EXACTE de l'en-tête, et celle-ci change : l'encoche du
   téléphone s'y ajoute (`env(safe-area-inset-top)`), le logo se pose au-dessus ou à côté
   selon la boutique, le nom passe parfois sur deux lignes. Un nombre écrit en dur serait
   juste sur un téléphone et faux sur les autres — le râtelier flotterait sous l'en-tête ou
   se glisserait dessous.

   `js/header.js` mesure donc l'en-tête et pose `--bo-entete-h`. Tant qu'il n'a pas mesuré,
   le repli de zéro laisse le râtelier coller en haut : visible, à la mauvaise hauteur
   pendant une image, plutôt qu'invisible. */
.bo-raccourcis-wrap {
  position: sticky;
  top: var(--bo-entete-h, 0px);
  /* Sous l'en-tête (100) et au-dessus des rangées : il passe PAR-DESSUS les articles qui
     défilent, jamais par-dessus l'en-tête. */
  z-index: 90;
  background: var(--bg);
}
.bo-raccourcis-fleche {
  position: absolute; right: 8px; top: calc(50% - 16px); transform: translateY(-50%);
  width: 32px; height: 32px; border-radius: 50%;
  display: none; align-items: center; justify-content: center;
  background: #fff; color: var(--ink);
  border: 1px solid var(--border);
  box-shadow: 0 2px 10px rgba(18, 12, 26, .14);
  cursor: pointer; z-index: 2;
}
.bo-raccourcis-fleche svg { width: 16px; height: 16px; }
.bo-raccourcis-fleche:active { transform: translateY(-50%) scale(.9); }
.bo-raccourcis-wrap::after {
  content: ''; position: absolute; right: 0; top: 0; bottom: 18px; width: 52px;
  background: linear-gradient(to right, transparent, var(--bg) 72%);
  pointer-events: none;
}

/* ── Réponses de l'assistante : de la mise en forme ──────────────────────── */
/* Un pavé de texte d'un seul tenant ne se lit pas sur un téléphone. Paragraphes espacés, listes
   à puces nettes, gras qui ressort : on parcourt du regard au lieu de lire ligne à ligne. */
.boMsg.ai p { margin: 0 0 8px; line-height: 1.5; }
.boMsg.ai p:last-child { margin-bottom: 0; }
.boMsg.ai b { font-weight: 800; color: var(--ink); }
.boMsg.ai ul { margin: 4px 0 10px; padding: 0; list-style: none; }
.boMsg.ai li {
  position: relative; padding-left: 16px; margin-bottom: 5px; line-height: 1.45;
}
.boMsg.ai li::before {
  content: ''; position: absolute; left: 3px; top: .62em;
  width: 5px; height: 5px; border-radius: 50%; background: var(--accent);
}

/* La carte d'article que l'assistante peut glisser dans sa réponse. Elle MONTRE ce dont elle
   parle : un nom d'article seul demande à la cliente de se rappeler à quoi il ressemble. */
.boCarte {
  display: flex; align-items: center; gap: 10px;
  margin: 8px 0 10px; padding: 8px; border-radius: 14px;
  background: #fff; border: 1px solid var(--border);
  text-decoration: none; color: inherit;
  box-shadow: 0 1px 3px rgba(18, 12, 26, .05);
}
.boCarte img { width: 54px; height: 54px; border-radius: 11px; object-fit: cover; flex: none; }
.boCarteTxt { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.boCarteTxt b { font-size: 13.5px; font-weight: 700; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.boCarteTxt i { font-style: normal; font-size: 13px; font-weight: 800; color: var(--accent); }
.boCarte:active { transform: scale(.98); }

/* ── Message « transmis à la gérante » ───────────────────────────────────── */
/* C'était une ligne de texte brut au milieu du fil, indistincte d'une réponse ratée. Elle devient
   un encart : un filet, une pastille, et le ton d'une bonne nouvelle — parce que c'en est une,
   quelqu'un s'occupe d'elle. */
.boMsg.system {
  display: flex; align-items: flex-start; gap: 10px;
  margin: 10px 0; padding: 12px 14px;
  background: var(--accent-tint);
  border: 1px solid color-mix(in srgb, var(--accent) 18%, transparent);
  border-radius: 14px;
  font-size: 13px; line-height: 1.45; color: #453C55;
  text-align: left;
}
.boMsg.system::before {
  content: ''; flex: none; width: 20px; height: 20px; margin-top: 1px;
  background: var(--accent);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 21s7-5.3 7-11a7 7 0 1 0-14 0c0 5.7 7 11 7 11Z'/%3E%3Cpath d='M9 10.5l2 2 4-4'/%3E%3C/svg%3E") center/contain no-repeat;
          mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 21s7-5.3 7-11a7 7 0 1 0-14 0c0 5.7 7 11 7 11Z'/%3E%3Cpath d='M9 10.5l2 2 4-4'/%3E%3C/svg%3E") center/contain no-repeat;
}

/* ── Le prix, à même la photo ────────────────────────────────────────────── */
/* La pastille blanche opaque était lisible mais posait un jeton par-dessus l'article : elle
   cachait justement la partie basse du vêtement. On retire le fond et on écrit le prix EN BLANC
   directement sur la photo.
   Pour que ce soit lisible sur une photo claire, ce n'est pas une pastille qui revient mais un
   voile DÉGRADÉ au bas de l'image : il s'éteint vers le haut, donc l'article reste entièrement
   visible, et il n'existe que là où le texte se pose. Un fond simplement translucide, lui,
   restait un rectangle — et devenait illisible dès que la photo était claire. */
.bo-prix-photo {
  background: none !important;
  box-shadow: none !important;
  color: #fff !important;
  padding: 0 !important;
  bottom: 10px;
  font-size: 15px; font-weight: 800; letter-spacing: -.2px;
  text-shadow: 0 1px 5px rgba(8, 5, 14, .75), 0 0 18px rgba(8, 5, 14, .45);
}
.bo-prix-photo .cur {
  color: rgba(255, 255, 255, .82) !important;
  font-size: 10px; font-weight: 700;
}
/* Le voile. Posé en ::after du cadre image, sous le prix (z-index 1 contre 2). */
.bo-rangee .product-card .img-wrap::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 46%;
  background: linear-gradient(to top, rgba(8, 5, 14, .62) 0%, rgba(8, 5, 14, .26) 45%, transparent 100%);
  pointer-events: none; z-index: 1;
}

/* Seconde flèche, à gauche. Une seule flèche à droite laissait la visiteuse arrivée au bout —
   sur « Sacs » — sans aucun moyen de revenir : la barre de défilement est masquée et il n'y a
   rien à attraper à la souris. Chacune n'apparaît que si elle mène quelque part. */
.bo-raccourcis-fleche.droite { right: 8px; left: auto; }
.bo-raccourcis-fleche.gauche { left: 8px; right: auto; }
.bo-raccourcis-wrap::before {
  content: ''; position: absolute; left: 0; top: 0; bottom: 18px; width: 52px;
  background: linear-gradient(to left, transparent, var(--bg) 72%);
  pointer-events: none; opacity: 0; transition: opacity .18s ease;
}
.bo-raccourcis-wrap:has(.bo-raccourcis-fleche.gauche[style*="flex"])::before { opacity: 1; }





/* ── L'œil : voir un article sans quitter la conversation ────────────────── */
/* L'assistante annonce « Robe pagne tissé : 43 042 FCFA » — et ensuite ? Sans ce raccourci, la
   cliente doit aller chercher l'article elle-même dans la boutique, et la conversation n'aura
   servi à rien. La pastille se glisse contre le nom, à peine visible tant qu'on ne la cherche
   pas ; elle ouvre la fiche juste en dessous, sans faire bouger le fil. */
.boNomArt { white-space: nowrap; }
.boOeil {
  display: inline-flex; align-items: center; justify-content: center;
  width: 20px; height: 20px; margin-left: 4px; vertical-align: -.28em;
  border: none; border-radius: 50%; padding: 0; cursor: pointer;
  background: var(--accent-tint); color: var(--accent);
  transition: background .15s ease, color .15s ease, transform .15s ease;
}
.boOeil svg { width: 13px; height: 13px; }
.boOeil:active { transform: scale(.88); }
.boOeil.on { background: var(--accent); color: #fff; }

.boFicheArt {
  display: flex; align-items: center; gap: 10px;
  margin: 8px 0 10px; padding: 8px; border-radius: 14px;
  background: #fff; border: 1px solid var(--border);
  box-shadow: 0 1px 3px rgba(18, 12, 26, .05);
  animation: boFicheOuvre .22s cubic-bezier(.22, 1, .36, 1);
}
@keyframes boFicheOuvre { from { opacity: 0; transform: translateY(-4px) } to { opacity: 1; transform: none } }
.boFicheLien { display: flex; align-items: center; gap: 10px; flex: 1; min-width: 0; text-decoration: none; color: inherit; }
.boFicheArt img, .boFicheVide { width: 52px; height: 52px; border-radius: 11px; object-fit: cover; flex: none; background: var(--accent-tint); }
.boFicheTxt { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.boFicheTxt b { font-size: 13px; font-weight: 700; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.boFicheTxt i { font-style: normal; font-size: 13.5px; font-weight: 800; color: var(--ink); }
.boFicheTxt em { font-style: normal; font-size: 10.5px; font-weight: 600; color: var(--muted); }
/* Le petit panier : c'est l'action, donc la seule chose colorée de la fiche. */
.boFichePanier {
  flex: none; width: 38px; height: 38px; border-radius: 50%;
  border: none; background: var(--accent); color: #fff; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  transition: transform .15s ease, background .2s ease;
}
.boFichePanier svg { width: 19px; height: 19px; }
.boFichePanier:active { transform: scale(.9); }
/* Une fois ajouté, le bouton le DIT : sans retour, on appuie deux fois et on se retrouve avec
   deux articles sans comprendre pourquoi. */
.boFichePanier.ok { background: var(--head-1); }
.boFichePanier.ok svg { display: none; }
.boFichePanier.ok::after {
  content: ''; width: 17px; height: 17px; background: #fff;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12.5l4.5 4.5L19 7.5'/%3E%3C/svg%3E") center/contain no-repeat;
          mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12.5l4.5 4.5L19 7.5'/%3E%3C/svg%3E") center/contain no-repeat;
}

/* ═══════════════════════════════════════════════════════════════════════════
   ON NE RECADRE JAMAIS UN ARTICLE
   ═══════════════════════════════════════════════════════════════════════════
   `cover` remplit le cadre en rognant ce qui dépasse. Sur une photo de mode, ce qui dépasse,
   c'est justement l'article : une robe longue perd son ourlet, un sac perd son anse, un
   mannequin perd la tête. C'est visible sur Perle d'Afrique, où les photos sont plus verticales
   que le carré du cadre.
   On repasse en `contain` : la photo entre ENTIÈRE. Le fond du cadre reprend la teinte très pâle
   de la boutique — les marges éventuelles se lisent alors comme un cartouche, pas comme un trou
   gris. C'était le vrai défaut du premier essai en `contain` : le fond, pas le cadrage. */
.bo-rangee .product-card .img-wrap {
  background: color-mix(in srgb, var(--accent) 5%, #FAFAFB) !important;
}
.bo-rangee .product-card .img-wrap img,
.bo-rangee .product-card .img-wrap video {
  object-fit: contain !important;
}
/* Même règle sur la grille dépliée et sur la fiche produit. */
.product-card .img-wrap img, .product-card .img-wrap video { object-fit: contain !important; }
.bo-car-slide.produit img { object-fit: contain !important; background: #FAFAFB !important; }

/* Les vignettes RONDES des raccourcis et des pastilles de conversation restent en `cover` :
   elles ne montrent pas l'article, elles l'évoquent au format d'une icône. Y mettre `contain`
   donnerait une photo minuscule perdue au milieu d'un rond. */

/* ── La photo remplit le rectangle SANS être coupée ───────────────────────── */
/* `object-fit: contain` avait réglé le recadrage — la robe n'était plus tronquée — mais il
   laissait deux bandes vides sur les côtés dès que la photo n'était pas carrée. La carte avait
   l'air d'un cadre à moitié rempli.

   On ne peut pas remplir un rectangle avec une image d'un autre format sans couper... sauf en
   comblant avec l'image ELLE-MÊME. Derrière la photo, un agrandissement flou de la même image
   occupe tout le cadre ; devant, la photo entière, intacte. Le rectangle est plein, ce sont les
   couleurs de l'article qui comblent, et RIEN n'est coupé.

   L'URL arrive par la variable --photo, posée sur le cadre par shop.js. Sans elle (vidéo, ou
   ancien rendu en cache), on retombe simplement sur le fond uni : rien ne casse. */
.product-card .img-wrap { overflow: hidden; }
.product-card .img-wrap::before {
  content: '';
  position: absolute; inset: -14%;          /* déborde : le flou ne doit pas laisser voir ses bords */
  background-image: var(--photo);
  background-size: cover;
  background-position: center;
  /* ── LES TRAITS HORIZONTAUX ET VERTICAUX DU RÂTELIER, C'EST ICI ────────────────────
     JM, le 21 octobre 2026 : « les traits souvent horizontaux souvent verticaux qui
     apparaissent dans le râtelier quand on change de catégories ; ça arrive dans Promotion et
     dans l'Accueil ».

     CE N'ÉTAIT NI UN BORD, NI UNE BORDURE, NI UN DÉFAUT DE RENDU : c'est la COUTURE entre la
     photo nette et son propre agrandissement flou. `object-fit: contain` pose la photo entière
     au milieu du carré ; le reste du carré est comblé par ce `::before`. Les deux montrent la
     MÊME image, à deux échelles et deux netteté : l'œil attend la continuité et reçoit une
     rupture franche. Elle tombe en HAUT ET EN BAS quand la photo est couchée, et à GAUCHE ET À
     DROITE quand elle est debout — « souvent horizontaux, souvent verticaux ». Et elle change à
     chaque rayon parce que chaque rayon amène des photos d'un autre format : un rayon de robes
     (photos hautes) donne deux traits verticaux, un rayon d'accessoires (photos larges) deux
     traits horizontaux. D'où « quand on change de catégories ».

     LE REMÈDE N'EST PAS DE RECADRER (`cover` couperait l'article — c'est le défaut qu'on avait
     écarté) mais de faire en sorte que le fond CESSE DE SE FAIRE PASSER POUR LA PHOTO. Plus de
     flou, presque plus de couleur, et à demi transparent sur le fond pâle du cadre : il ne reste
     qu'un lavis aux teintes de l'article. La photo se lit alors comme une photo posée sur un
     cartouche — ce qu'elle est — et non comme une image coupée en deux par un trait. */
  filter: blur(30px) saturate(.5) brightness(1.08);
  opacity: .42;
  transform: scale(1.14);                   /* mange les coins qu'un flou large éclaircit */
  z-index: 0;
  pointer-events: none;
}
/* Sans photo (vidéo, cache ancien) : pas de voile gris par-dessus le fond uni. */
.product-card .img-wrap:not([style*="--photo"])::before { display: none; }

/* La photo passe DEVANT son propre fond. */
.product-card .img-wrap img,
.product-card .img-wrap video { position: relative; z-index: 1; }

/* Le voile du prix reste au-dessus des deux. */
.bo-rangee .product-card .img-wrap::after { z-index: 2; }
.bo-prix-photo { z-index: 3; }

/* ── Fiche produit : la photo prend TOUTE la place, les boutons se répondent ────────── */
/* La photo laissait deux bandes vides sur les côtés et un cadre à moitié rempli. Même remède
   que sur les cartes : un agrandissement flou de la MÊME image comble le fond, la photo entière
   reste devant. Rien n'est coupé, plus rien n'est vide. */
.product-detail .gallery { position: relative; }
/* Même couture, même remède que sur les cartes du râtelier (voir ci-dessus) : sur la fiche,
   une photo haute laissait deux traits verticaux de part et d'autre de l'article. */
.product-detail .gallery .gallery-main-wrap::before {
  content: '';
  position: absolute; inset: -12%;
  background-image: var(--photo);
  background-size: cover;
  background-position: center;
  filter: blur(34px) saturate(.5) brightness(1.08);
  opacity: .42;
  transform: scale(1.12);
  z-index: 0;
}
.product-detail .gallery .gallery-main-media { position: relative; z-index: 1; }
/* 4/5 au lieu de 5/6 : la photo gagne en largeur sans jamais rogner l'article. */
.product-detail .gallery .gallery-main { aspect-ratio: 4 / 5; }

/* LA BARRE DE COMMANDE de la fiche (JM, le 7 octobre 2026) : le retour à gauche, court ;
   « Commander » à droite, qui prend la place restante. Même hauteur pour les deux. */
.add-bar-row { display: flex; align-items: stretch; gap: 10px; }
.btn-commander {
  flex: 1; height: 46px;
  display: flex; align-items: center; justify-content: center; gap: 9px;
  border: none; border-radius: 14px; cursor: pointer;
  background: var(--accent, #6B21A8); color: #fff;
  font: inherit; font-size: 15px; font-weight: 800; letter-spacing: -.2px;
  box-shadow: 0 2px 10px rgba(88, 28, 135, .18);
  transition: transform .12s ease;
}
.btn-commander:active { transform: scale(.97); }
.btn-commander svg { flex: none; }
.btn-back-cat {
  flex: none; height: 46px; padding: 0 16px;
  display: flex; align-items: center; justify-content: center; gap: 7px;
  border-radius: 14px; text-decoration: none;
  font-size: 14.5px; font-weight: 700; letter-spacing: -.2px;
  color: var(--ink, #16121F);
  background: var(--accent-tint, #F4F1F8);
  border: 1px solid var(--border, #E9E6EE);
}
/* La flèche est dessinée, pas tapée : « ← » change de forme selon la police. */
.btn-back-cat::before {
  content: ''; width: 8px; height: 8px;
  border-left: 2px solid currentColor; border-bottom: 2px solid currentColor;
  transform: rotate(45deg); opacity: .75;
}
.btn-back-cat:active { background: var(--border, #E9E6EE); }

/* ── « Moi » : la position ne déborde plus du formulaire ────────────────────────────── */
/* Latitude et longitude sortaient à droite, au-delà du cadre : une valeur illisible et une
   page qui défile de travers. Elles descendent en colonne et se coupent proprement. */
.bo-position, .position-row, [data-testid*="position"] { max-width: 100%; box-sizing: border-box; }
.bo-position-coords {
  display: flex; flex-wrap: wrap; gap: 4px 12px;
  max-width: 100%; overflow-wrap: anywhere; word-break: break-word;
  font-variant-numeric: tabular-nums;
}

/* ── Promotions : la robe entière, et la diapo pleine ───────────────────────────────── */
/* `cover` remplissait bien la diapo — en coupant le haut de la robe. On ne voyait plus l'article
   qu'on met en avant, ce qui est le contraire du but d'une promotion.
   Même remède que partout : la photo passe en `contain`, un agrandissement flou d'elle-même
   comble le fond. Diapo pleine, robe entière. */
.bo-car-slide.promo.avec-photo::before {
  content: '';
  position: absolute; inset: -12%;
  background-image: var(--photo);
  background-size: cover;
  background-position: center;
  filter: blur(26px) saturate(1.18) brightness(.96);
  transform: scale(1.14);
  z-index: 0;
}
.bo-car-slide.promo.avec-photo img {
  object-fit: contain !important;
  z-index: 1;
}
.bo-car-slide.promo.avec-photo::after { z-index: 2; }
.bo-car-slide.promo.avec-photo .bo-car-promo-in { position: relative; z-index: 3; }

/* ── Les raccourcis de catégories : plus discrets ───────────────────────────────────── */
/* 56 px, ils pesaient autant que le carrousel juste au-dessus et repoussaient les articles hors
   de l'écran. Un raccourci se remarque, il ne réclame pas la place d'une vignette produit. */
.bo-raccourci-tuile { width: 46px; height: 46px; border-radius: 15px; }
.bo-raccourci-tuile svg { width: 22px; height: 22px; }
.bo-raccourci-nom { font-size: 11px; }
.bo-raccourcis { gap: 10px !important; }

/* ── « by Aminata » ─────────────────────────────────────────────────────────────────── */
/* La signature de la maison mérite mieux qu'une ligne grise : un filet la précède, comme sur une
   étiquette cousue. Le nom reste en capitales espacées, la préposition en minuscules — c'est ce
   contraste qui fait qu'on lit « by Aminata » et non « BY AMINATA ». */
.bo-head-desc {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 9.5px; font-weight: 700; letter-spacing: 1.9px;
  color: rgba(255, 255, 255, .74);
}
/* Le filet n existe QUE s il y a une signature. Sans le :not(:empty), une boutique sans auteur
   affichait un trait tout seul sous son nom — un tiret orphelin que personne ne pouvait
   expliquer. Un ornement ne doit jamais survivre au texte qu il accompagne. */
.bo-head-desc:empty { display: none; }
.bo-head-desc:not(:empty)::before {
  content: ''; width: 14px; height: 1px; flex: none;
  background: rgba(255, 255, 255, .45);
}

/* ── Les sections dépliables de « Moi » ─────────────────────────────────────────────── */
/* Le titre collant a ete RETIRE ici apres coup : pose sur ces boutons, il desorganisait toute
   la page — les sections se chevauchaient. La regle reste vraie (voir Prompts IA), mais elle
   demande ici un travail sur la structure des `.bo-pliant`, pas une regle posee par-dessus.
   Une page en desordre est pire que l absence d un confort de defilement. */

/* ── « À propos » ───────────────────────────────────────────────────────────────────── */
.bo-apropos { padding: 4px 2px 2px; }
.bo-apropos-ligne {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
  padding: 9px 0;
}
.bo-apropos-ligne span { font-size: 13px; color: var(--muted, #6E6880); }
.bo-apropos-ligne strong {
  font-size: 13px; font-weight: 700; color: var(--ink, #16121F);
  font-variant-numeric: tabular-nums;
}
/* « Revoir la visite » emprunte le meme bouton entoure que « Passer la visite ». Un bouton
   secondaire se dessine d'UNE seule facon dans toute l'application. */
.bo-apropos-ligne button {
  font-size: 12.5px; font-weight: 700; color: var(--muted, #6E6880);
  background: none; border: 1px solid var(--bord, #E8E8E8); border-radius: 999px;
  padding: 7px 14px; cursor: pointer;
}
.bo-apropos-ligne button:hover { border-color: var(--primary, #6B21A8); color: var(--primary, #6B21A8); }

/* ── Le carrousel : UNE image, pas trois zones ──────────────────────────────────────── */
/* On voyait la diapo découpée en trois : flou · photo · flou. C'était géométrique, pas un
   réglage. Une photo de robe est PORTRAIT ; la diapo était PAYSAGE (328 × 210). Quel que soit le
   remplissage, il reste deux bandes de chaque côté — et un fond flou reste visible, donc lisible
   comme une bande.
   La seule sortie sans recadrer : donner à la diapo le format de la photo. En 3/4, une photo
   portrait la remplit d'un bord à l'autre. Il ne reste rien à combler, donc plus rien à voir. */
.bo-car-slide.promo.avec-photo {
  aspect-ratio: 3 / 4;
  min-height: 0;
}
.bo-car-slide.promo.avec-photo img {
  /* `cover` redevient possible SANS couper : le cadre a désormais le format de la photo, il n'y
     a donc (presque) rien à rogner. Et l'ancrage en HAUT protège ce qui compte — on ne coupe
     jamais la tête du mannequin ni le haut du vêtement. */
  object-fit: cover !important;
  object-position: center top;
}
/* Le fond flou ne sert plus qu'aux photos de format inhabituel. Poussé plus loin pour qu'il ne
   se lise jamais comme une bande distincte. */
.bo-car-slide.promo.avec-photo::before {
  inset: -20%;
  filter: blur(34px) saturate(1.1) brightness(.9);
  transform: scale(1.25);
}

/* ── Les raccourcis de catégories : encore plus discrets ────────────────────────────── */
/* Ils gardent la première place de l'écran alors qu'ils ne sont qu'un chemin de traverse : les
   articles, eux, sont la raison de la visite. */
.bo-raccourci-tuile { width: 38px; height: 38px; border-radius: 12px; }
.bo-raccourci-tuile svg { width: 19px; height: 19px; }
.bo-raccourci-nom { font-size: 10px; }
.bo-raccourcis { gap: 8px !important; }

/* ── Le carrousel en PLEIN CADRE ────────────────────────────────────────────────────── */
/* On sentait encore les bords : un liseré de la diapo suivante à droite, une marge à gauche, un
   blanc entre l'en-tête et la photo. Trois indices qui disent « ceci est un composant » au lieu
   de « ceci est la boutique ».
   Une seule photo à la fois, sur TOUTE la largeur, collée sous l'en-tête. Plus de gouttière,
   plus de marge, plus de coin arrondi : il n'y a plus de bord à sentir. */
.bo-carousel { margin: 0 0 14px !important; min-height: 0 !important; }
.bo-car-track { padding: 0 !important; gap: 0 !important; }
.bo-car-slide.promo { width: 100% !important; flex: 0 0 100% !important; }
.bo-car-slide.promo.avec-photo {
  /* Carré, et jamais plus de 44 % de la hauteur d'écran : la promotion ouvre l'accueil, elle ne
     le remplit pas. Une diapo trop haute repoussait les articles hors de vue — or c'est pour eux
     qu'on est venue. */
  aspect-ratio: 1 / 1;
  max-height: 44dvh;
  border-radius: 0 !important;
  margin: 0 !important;
}
/* L'ancrage en HAUT reste : on rogne par le bas, jamais la tête du mannequin ni le haut du
   vêtement. C'était le défaut d'origine, il ne doit pas revenir par la bande. */
.bo-car-slide.promo.avec-photo img { object-position: center top; }

/* Les flèches DANS la photo, pas à côté : posées sur le bord, elles empiétaient sur la page et
   donnaient l'impression que le carrousel n'était pas tout à fait chez lui. */
.bo-car-arrow.left { left: 12px; }
.bo-car-arrow.right { right: 12px; }

/* Les pastilles de position reviennent SUR la photo, en bas : sous elle, elles ajoutaient une
   bande blanche entre la promotion et les raccourcis. */
.bo-car-dots { position: absolute; left: 0; right: 0; bottom: 10px; z-index: 4; margin: 0 !important; }
/* Posées sur la photo, les pastilles doivent rester lisibles sur un fond clair comme sur un fond
   sombre : blanches, avec une ombre portée qui les détache sans les alourdir. */
.bo-car-dots span {
  background: rgba(255, 255, 255, .5) !important;
  box-shadow: 0 1px 3px rgba(8, 5, 14, .45);
}
.bo-car-dots span.on { background: #fff !important; }

/* ── Hauteur du carrousel : le compromis, expliqué ──────────────────────────────────── */
/* À largeur pleine et sans bandes sur les côtés, la hauteur du cadre DÉCIDE de ce qui est
   rogné : photo portrait dans un cadre plus court = plus de rognage. Raccourcir, tout garder et
   ne rien laisser sur les côtés sont trois exigences qu'aucune géométrie ne satisfait ensemble.

   On réduit donc la hauteur, et on déplace la fenêtre de vue vers le BAS (32 %) : ce qu'on perd
   est le ciel au-dessus des épaules — la partie vide de presque toutes les photos de vêtement —
   et non l'ourlet, qui est ce qu'on vient regarder. */
.bo-car-slide.promo.avec-photo {
  aspect-ratio: 5 / 4;
  max-height: 34dvh;
}
.bo-car-slide.promo.avec-photo img { object-position: center 32%; }

/* ── L'image ENTIÈRE, et un fond qui ne se voit pas ─────────────────────────────────── */
/* Le haut ET le bas étaient rognés : en `cover`, un cadre plus court que la photo mange des deux
   côtés de la fenêtre de vue. La seule façon de tout montrer est `contain` — la photo entre en
   entier, et il reste de la place autour.
   Cette place, on ne peut pas la supprimer : elle est la différence entre le format de la photo
   et celui du cadre. On peut seulement la rendre INVISIBLE. Trois choses pour ça :
     • un cadre au format le plus proche possible d'une photo de vêtement (4/5), donc peu de vide ;
     • un fond très flou tiré de la photo elle-même, assombri, sans contour ni bord net ;
     • un dégradé qui éteint les côtés vers le centre, pour qu'aucune ligne verticale ne se lise.
   Ce n'est pas un remplissage qu'on regarde : c'est une ombre de la photo. */
.bo-car-slide.promo.avec-photo {
  aspect-ratio: 4 / 5;
  max-height: 42dvh;
  background: #1A1420;
}
.bo-car-slide.promo.avec-photo img {
  object-fit: contain !important;
  object-position: center center;
}
.bo-car-slide.promo.avec-photo::before {
  inset: -30%;
  filter: blur(46px) saturate(.85) brightness(.62);
  transform: scale(1.5);
  /* Le dégradé mange les bords du fond : plus de ligne verticale entre le flou et la photo. */
  -webkit-mask-image: radial-gradient(120% 100% at 50% 50%, #000 42%, transparent 96%);
  mask-image: radial-gradient(120% 100% at 50% 50%, #000 42%, transparent 96%);
}

/* ── La barre des rayons, collée sous l'en-tête ─────────────────────────────────────── */
/* Elle remplace les titres retirés de chaque rangée : le rayon allumé EST le titre. Chaque
   rangée reprenait son nom et son pictogramme sur une bande de 46 px — multipliée par huit
   rayons, c'est presque deux écrans de hauteur pour redire ce que la barre dit déjà. */
.bo-raccourcis-wrap {
  position: sticky;
  /* LA HAUTEUR MESURÉE, JAMAIS UN NOMBRE. `js/header.js` pose `--bo-entete-h` en lisant
     l'en-tête. Les 61 px écrits ici gagnaient sur la règle d'au-dessus (même sélecteur, plus
     bas dans le fichier) et laissaient 9 px de page entre un en-tête de 52 px et le râtelier.
     Le repli de 0 colle le râtelier tout en haut le temps d'une image : visible à la mauvaise
     hauteur vaut mieux qu'un trou. */
  top: var(--bo-entete-h, 0px);
  z-index: 25;
  /* Fond OPAQUE, sinon les articles se lisent par transparence à travers les rayons. */
  background: var(--bg, #FAFAFA);
  padding-top: 8px;
  margin-bottom: 2px;
  box-shadow: 0 6px 12px -10px rgba(8, 5, 14, .35);
}

/* Le rayon où l'on se trouve : plein, franc. Un simple contour se voyait mal à cette taille. */
.bo-raccourci.on .bo-raccourci-tuile { transform: scale(1.06); }
.bo-raccourci.on .bo-raccourci-nom { font-weight: 800; }

/* ── Les rangées : plus de titre, deux flèches ──────────────────────────────────────── */
/* La tête ne porte plus que la promotion. Quand il n'y en a pas, elle disparaît entièrement —
   une bande vide entre deux rangées se lit comme une séparation qu'on n'a pas voulue. */
.bo-rangee-tete.vide { display: none !important; }
.bo-rangee-tete { padding: 0 16px 6px !important; min-height: 0 !important; border: none !important; }
.bo-rangee { margin-bottom: 14px; }
.bo-rangee .accordion-body { position: relative; }

/* Les flèches, une de chaque côté des articles. Même geste que sur le carrousel juste au-dessus,
   et elles remplacent « voir plus » : on avance tant qu'il y a des articles, au lieu d'avoir à
   choisir entre parcourir et tout voir. */
.bo-rangee-fleche {
  position: absolute; top: 38%; transform: translateY(-50%);
  width: 30px; height: 30px; border-radius: 50%;
  display: none; align-items: center; justify-content: center;
  border: 1px solid var(--border, #E9E6EE);
  background: rgba(255, 255, 255, .94);
  color: var(--ink, #16121F);
  box-shadow: 0 2px 8px rgba(8, 5, 14, .14);
  cursor: pointer; z-index: 5;
}
.bo-rangee-fleche svg { width: 15px; height: 15px; }
.bo-rangee-fleche.gauche { left: 6px; }
.bo-rangee-fleche.droite { right: 6px; }
.bo-rangee-fleche:active { transform: translateY(-50%) scale(.9); }

/* ── Le micro de la conversation ────────────────────────────────────────────────────── */


/* EN TRAIN D'ÉCOUTER. Un simple changement de couleur ne suffit pas : on doit voir que ça tourne,
   sinon on ne sait pas si l'appui a pris et on parle dans le vide. */
@keyframes boPouls {
  0%   { transform: scale(1);   opacity: .35; }
  100% { transform: scale(1.9); opacity: 0; }
}


/* ── Le rayon en cours se détache, les autres reculent ──────────────────────────────── */
/* Six pastilles de même poids, on ne voyait pas laquelle était active — un simple grossissement
   se perd dans la rangée. Les autres s'estompent légèrement : assez pour que l'œil aille droit
   au bon, pas assez pour qu'on ne puisse plus lire les suivants. Un flou franc rendrait la barre
   inutilisable, or c'est justement là qu'on va pour changer de rayon. */
.bo-raccourcis .bo-raccourci {
  transition: opacity .22s ease, filter .22s ease, transform .22s ease;
}
.bo-raccourcis:has(.bo-raccourci.on) .bo-raccourci:not(.on) {
  opacity: .42;
  filter: blur(.6px) saturate(.55);
}
.bo-raccourcis:has(.bo-raccourci.on) .bo-raccourci:not(.on):active {
  opacity: 1; filter: none;   /* on le touche : il redevient net immédiatement */
}
.bo-raccourci.on .bo-raccourci-tuile {
  transform: scale(1.1);
  box-shadow: 0 4px 14px -4px rgba(8, 5, 14, .3);
}

/* ── Les promotions : un éclat, pas un feu d'artifice ───────────────────────────────── */
/* Le texte d'une promotion se lisait comme une ligne d'information parmi d'autres. Il lui faut
   un signe — mais un GIF ou une flamme animée mangerait de la place, tirerait la batterie et
   ferait « bannière de casino » sur la plus belle surface de la vitrine.
   Un reflet qui traverse le texte, et rien de plus : il ne prend aucune place supplémentaire,
   il n'existe que sur les lettres, et il s'arrête sur les téléphones qui demandent moins
   d'animation. C'est le même effet que sur une carte bancaire qu'on incline. */
.accordion-promo-text,
.bo-car-promo-in strong {
  background-image: linear-gradient(100deg,
    currentColor 0%, currentColor 38%,
    rgba(255, 255, 255, .95) 47%,
    rgba(255, 255, 255, .95) 53%,
    currentColor 62%, currentColor 100%);
  background-size: 260% 100%;
  /* `background-clip: text` a besoin du texte transparent pour laisser voir le fond. */
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  animation: boEclat 4.6s ease-in-out infinite;
}
@keyframes boEclat {
  0%, 62%  { background-position: 130% 0; }   /* long temps mort : l'éclat ne doit pas harceler */
  100%     { background-position: -30% 0; }
}
@media (prefers-reduced-motion: reduce) {
  .accordion-promo-text, .bo-car-promo-in strong { animation: none; }
}

/* ── La fiche produit : l'article d'abord ───────────────────────────────────────────── */
/* La photo était posée dans un cadre, avec des marges de chaque côté et le nom en dessous comme
   une légende. On regardait une fiche, pas un article. Elle prend maintenant toute la largeur et
   se colle sous l'en-tête, exactement comme le carrousel de l'accueil — à ceci près qu'on la
   fait défiler soi-même pour voir les autres photos. */
.product-detail .gallery {
  margin: 0 !important;
  border-radius: 0 !important;
  background: #1A1420;
}
.product-detail .gallery .gallery-main-wrap { border-radius: 0 !important; }
.product-detail .gallery .gallery-main {
  aspect-ratio: 4 / 5;
  max-height: 52dvh;
  object-fit: contain;
}
/* Les flèches DANS la photo, comme partout ailleurs. */
.product-detail .gallery-nav { z-index: 4; }
.product-detail .gallery-nav-l { left: 12px; }
.product-detail .gallery-nav-r { right: 12px; }

/* Le NOM devient le titre de la page, pas une légende de photo. */
.product-detail .content { padding: 16px 16px 22px; }
.product-detail .content .name {
  font-size: 20px; font-weight: 800; letter-spacing: -.4px; line-height: 1.28;
  color: var(--ink, #16121F); margin-bottom: 4px;
}
.product-detail .content .price {
  font-size: 22px; font-weight: 800; letter-spacing: -.5px;
  color: var(--accent, #6B21A8); margin-bottom: 10px;
}

/* ── La description : quatre lignes, puis on déplie ─────────────────────────────────── */
/* Une description de dix lignes repoussait les tailles et les couleurs hors de l'écran — or ce
   sont elles qui décident de l'achat. Quatre lignes suffisent à donner envie ; le reste attend
   d'être demandé. */
/* LA DESCRIPTION PORTE SON INTITULÉ, comme Catégories, Taille et Couleur.
   JM, le 21/10/2026 : « CATÉGORIES, Tops, Chemises, Style Istanbul tissu premium, ça ne veut
   rien dire, c'est la description ou c'est quoi ». C'était la seule des quatre sections sans
   titre : son texte se posait sous les pastilles de rayons et se lisait comme une troisième
   catégorie. Même intitulé, même écart au bloc d'avant (14 px, pas 12) : quatre sections qui
   se ressemblent se lisent d'un coup. */
.bo-desc { margin-top: 14px; }
.bo-desc-txt {
  font-size: 14px; line-height: 1.62; color: var(--muted, #6E6880);
  display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical;
  overflow: hidden;
}
.bo-desc-txt.ouvert { -webkit-line-clamp: unset; overflow: visible; }
.bo-desc-plus {
  display: inline-flex; align-items: center; gap: 5px; margin-top: 7px;
  padding: 0; border: none; background: none; cursor: pointer;
  font-size: 13px; font-weight: 700; color: var(--accent, #6B21A8);
}
/* ── ET QUAND IL EST CACHÉ, IL EST CACHÉ ──────────────────────────────────────────────
   JM, le 20 octobre 2026 : « j'ai dit que lire la suite doit apparaître seulement s'il y a
   du texte à lire ».

   Le calcul était là et il était juste (`js/product.js`, « ON MESURE QUAND LA POLICE EST
   LÀ ») : il posait `btn.hidden = true` dès que la description tenait dans ses quatre
   lignes. Seulement, `hidden` ne cache un élément que par la feuille de style du
   NAVIGATEUR, et la règle juste au-dessus pose `display: inline-flex`. Une règle écrite par
   nous bat toujours celle du navigateur : l'attribut ne faisait rien, et le bouton
   s'affichait sur toutes les fiches, même sur une description de deux mots.

   Essayé dans le navigateur avant d'écrire cette ligne : un `.bo-desc-plus` portant
   `hidden` rendait `display: inline-flex`, et il était visible.

   La même chose vaut pour « Voir tout » des tailles et des couleurs, construit par le même
   fichier : il n'est ajouté que si la liste déborde, mais s'il vient à porter `hidden` un
   jour, qu'il se taise aussi. */
.bo-desc-plus[hidden],
.bo-option-plus[hidden] { display: none; }
.bo-desc-plus svg { width: 15px; height: 15px; transition: transform .2s ease; }
.bo-desc-plus.ouvert svg { transform: rotate(180deg); }

/* ── La flamme des soldes ───────────────────────────────────────────────────────────── */
/* Un dessin, pas un carré coloré : deux langues de feu et un cœur clair, avec le dégradé qui va
   du rouge profond au jaune. C'est ce cœur qui fait qu'on lit « feu » et non « triangle orange ».
   Dessinée plutôt qu'en emoji — un emoji change de forme d'un téléphone à l'autre, et sur le seul
   signe qui doit attirer l'œil ce serait dommage.
   Elle ondule légèrement, comme une vraie flamme : les deux parties bougent à des rythmes
   différents, sinon l'ensemble « pulse » et fait clignotant de vitrine. */
.bo-flamme {
  width: 15px; height: 17px; flex: none;
  margin-right: 6px; vertical-align: -3px;
  filter: drop-shadow(0 1px 3px rgba(255, 77, 0, .38));
}
.bo-flamme-corps {
  transform-origin: 50% 92%;
  animation: boFlammeCorps 1.9s ease-in-out infinite;
}
.bo-flamme-coeur {
  transform-origin: 50% 92%;
  animation: boFlammeCoeur 1.25s ease-in-out infinite;
}
@keyframes boFlammeCorps {
  0%, 100% { transform: scaleY(1)    scaleX(1);    }
  38%      { transform: scaleY(1.09) scaleX(.955); }
  70%      { transform: scaleY(.97)  scaleX(1.02); }
}
@keyframes boFlammeCoeur {
  0%, 100% { transform: scaleY(1)    translateY(0);     opacity: .92; }
  45%      { transform: scaleY(1.18) translateY(-.5px); opacity: 1;   }
}
@media (prefers-reduced-motion: reduce) {
  .bo-flamme-corps, .bo-flamme-coeur { animation: none; }
}
.bo-rangee-tete { display: flex; align-items: center; }

/* ── Les rayons : celui qu'on regarde passe DEVANT ──────────────────────────────────── */
/* Le flou seul ne suffisait pas — tout restait sur le même plan. Les autres reculent vraiment :
   ils descendent un peu, rapetissent, s'estompent. Celui qui est actif monte, grossit, et porte
   une ombre portée qui le décolle de la rangée. C'est la profondeur qui fait la hiérarchie, pas
   la couleur. */
.bo-raccourcis:has(.bo-raccourci.on) .bo-raccourci:not(.on) {
  opacity: .38;
  filter: blur(.7px) saturate(.5);
  transform: translateY(3px) scale(.88);
}
.bo-raccourci.on {
  transform: translateY(-3px);
  z-index: 2;
}
.bo-raccourci.on .bo-raccourci-tuile {
  transform: scale(1.14);
  box-shadow: 0 7px 16px -5px rgba(8, 5, 14, .38), 0 2px 5px -2px rgba(8, 5, 14, .22);
}
.bo-raccourci.on .bo-raccourci-nom { font-weight: 800; }

/* ── Le panier d'une carte : il ROUGIT, et c'est tout ───────────────────────────────── */
/* Il basculait vers une coche : l'icône du sac disparaissait, remplacée par un symbole qui ne
   dit ni « panier » ni « déjà dedans » — juste « validé », ce qui n'est pas la même chose. Et
   comme le bouton sert AUSSI à retirer, une coche laissait croire qu'il n'y avait plus rien à
   faire.
   L'article est dans le panier : le sac devient rouge. Même dessin, autre couleur — on comprend
   sans qu'on l'explique, et on voit encore que c'est un panier. */
.cart-overlay-btn.in-cart {
  background: #D91E4A !important;
  border-color: transparent !important;
  color: #fff !important;
}
/* Le sac reste visible dans les deux états. La coche ne sert plus. */
.cart-overlay-btn.in-cart .ico-add { display: block !important; }
.cart-overlay-btn.in-cart .ico-ok  { display: none !important; }
/* Le « + » du sac disparaît quand l'article y est déjà : on ne peut plus l'ajouter, on l'enlève. */
.cart-overlay-btn.in-cart .ico-add path:last-of-type { opacity: 0; }

/* ── La barre des rayons : de la place pour respirer ────────────────────────────────── */
/* Le rayon actif monte de 3 px et grossit de 14 % : sans marge au-dessus, il passait sous le
   bord de la barre collante et se retrouvait coupé net. Un élément qu'on met en avant ne doit
   surtout pas être celui qu'on rogne. */
.bo-raccourcis-wrap { padding-top: 14px; overflow: visible; }
.bo-raccourcis { padding-top: 6px !important; padding-bottom: 4px !important; overflow-y: visible; }

/* ── LE TRAJET : la sélection VOYAGE d'un rayon à l'autre ───────────────────────────── */
/* La couleur sautait d'un rayon au suivant, sans transition : on ne voyait pas qu'elle s'était
   déplacée, on voyait deux images différentes. En lui faisant parcourir la distance, le geste se
   raconte tout seul — on comprend qu'on descend dans la boutique et que la barre suit.
   Une barre lumineuse glisse SOUS les rayons, du départ vers l'arrivée. Elle est posée par le
   JavaScript, qui seul connaît la position de chacun. */
.bo-raccourcis-wrap { position: sticky; }
.bo-trajet {
  position: absolute; bottom: 2px; height: 3px; border-radius: 2px;
  background: linear-gradient(90deg,
    transparent 0%,
    var(--accent, #6B21A8) 22%,
    color-mix(in srgb, var(--accent, #6B21A8) 55%, #fff) 50%,
    var(--accent, #6B21A8) 78%,
    transparent 100%);
  box-shadow: 0 0 10px -2px var(--accent, #6B21A8);
  opacity: 0;
  pointer-events: none;
  z-index: 1;
  /* Le ressort : la barre part vite, arrive en freinant. Une vitesse constante donne un objet
     mécanique ; celle-ci a l'air de se poser. */
  transition: left .42s cubic-bezier(.22, 1.1, .36, 1),
              width .42s cubic-bezier(.22, 1.1, .36, 1),
              opacity .2s ease;
}
.bo-trajet.visible { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  .bo-trajet { transition: opacity .2s ease; }
}

/* ── Tailles et couleurs : une ligne, puis on déplie ────────────────────────────────── */
/* Douze tailles occupaient trois lignes, autant pour les couleurs : six lignes de pastilles
   avant même d'avoir vu le bouton d'achat. Une ligne suffit à montrer ce qui existe ; le reste
   attend d'être demandé, exactement comme la description. */
.product-detail .option-group { margin-top: 14px; }
.product-detail .option-label {
  font-size: 11px; font-weight: 800; letter-spacing: .8px; text-transform: uppercase;
  color: var(--muted, #6E6880); margin-bottom: 7px;
}
.product-detail .option-list {
  display: flex; flex-wrap: wrap; gap: 8px;
  max-height: 38px; overflow: hidden;          /* la hauteur d'UNE ligne de pastilles */
  transition: max-height .28s ease;
}
.product-detail .option-group.ouvert .option-list { max-height: 400px; }
.bo-option-plus {
  display: inline-flex; align-items: center; gap: 5px; margin-top: 8px;
  padding: 0; border: none; background: none; cursor: pointer;
  font-size: 12.5px; font-weight: 700; color: var(--accent, #6B21A8);
}
.bo-option-plus svg { width: 14px; height: 14px; transition: transform .22s ease; }
.option-group.ouvert .bo-option-plus svg { transform: rotate(180deg); }

/* ── « Lire la suite » : les mêmes propriétés que nos accordéons ────────────────────── */
/* Le repli de la description utilisait un `line-clamp` maison, avec sa propre animation et son
   propre bouton : un troisième comportement pour un geste qu'on a déjà tranché deux fois.
   Il reprend l'ouverture douce, la flèche qui pivote, et le même libellé que partout. */
.bo-desc-txt {
  transition: max-height .3s ease;
}

/* ── « Ma position » : le bouton EST l'information ──────────────────────────────────── */
/* Un champ « Latitude, Longitude » à côté demandait à la cliente de taper des coordonnées GPS —
   personne ne fait ça — et affichait deux nombres à douze décimales qui ne veulent rien dire.
   Le bouton suffit : gris, la position n'est pas prise ; rouge, elle l'est. */
.bo-position {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 9px 14px; border-radius: 10px; cursor: pointer;
  font-size: 13px; font-weight: 600;
  border: 1px solid var(--border, #E9E6EE);
  background: var(--surface, #fff);
  color: var(--muted, #6E6880);
  transition: background .18s ease, color .18s ease, border-color .18s ease;
}
.bo-position [data-ico] { width: 15px; height: 15px; }
.bo-position.prise {
  background: #D91E4A;
  border-color: transparent;
  color: #fff;
}

/* ── Les flèches des rangées : transparentes ────────────────────────────────────────── */
/* Un disque blanc posé sur la photo cachait l'article qu'il est censé aider à voir. En verre
   dépoli, on distingue l'image derrière — la flèche reste lisible parce qu'elle porte son
   propre contraste, pas parce qu'elle masque le fond. */
.bo-rangee-fleche {
  background: rgba(255, 255, 255, .18) !important;
  backdrop-filter: blur(7px) saturate(1.2);
  -webkit-backdrop-filter: blur(7px) saturate(1.2);
  border: 1px solid rgba(255, 255, 255, .34) !important;
  color: #fff !important;
  box-shadow: 0 2px 10px rgba(8, 5, 14, .28) !important;
}
.bo-rangee-fleche svg { filter: drop-shadow(0 1px 2px rgba(8, 5, 14, .55)); }

/* ── La flamme À GAUCHE du texte, sans prendre de place ─────────────────────────────── */
/* Posée au-dessus, elle poussait le texte d'une ligne entière. Sur la même ligne, à gauche, elle
   ne coûte que sa propre largeur — et c'est là qu'on la lit naturellement, avant le mot
   « Soldes ». Elle brûle plus franchement : l'amplitude passe de 9 à 16 %. */
.bo-rangee-tete { display: flex !important; align-items: center !important; gap: 0 !important; }
.bo-flamme { margin-right: 7px; vertical-align: middle; }
@keyframes boFlammeCorps {
  0%, 100% { transform: scaleY(1)    scaleX(1)    rotate(0deg);   }
  30%      { transform: scaleY(1.16) scaleX(.92)  rotate(-2.5deg); }
  62%      { transform: scaleY(.94)  scaleX(1.05) rotate(2deg);   }
}
@keyframes boFlammeCoeur {
  0%, 100% { transform: scaleY(1)    translateY(0);   opacity: .88; }
  40%      { transform: scaleY(1.3)  translateY(-1px); opacity: 1;  }
  75%      { transform: scaleY(.9)   translateY(.5px); opacity: .8; }
}

/* ═══════════════════════════════════════════════════════════════════════════════════════
   LES FLÈCHES DE NAVIGATION — collées, sans fond, sans cercle.
   Règle valable PARTOUT : carrousel, barre des rayons, rangées d'articles, fiche produit.

   Un disque blanc de 30 px posé sur une photo, c'est un jeton qui cache l'article. Et comme il
   fallait le décoller du bord pour qu'il respire, il mangeait une bande à gauche et à droite de
   chaque rangée. Reste le chevron seul, à 2 px du bord, avec une ombre portée pour rester
   lisible sur clair comme sur sombre. Il ne prend plus de place : il en indique.
   ═══════════════════════════════════════════════════════════════════════════════════════ */
.bo-rangee-fleche,
.bo-car-arrow,
.bo-raccourcis-fleche,
.product-detail .gallery-nav {
  /* UN DISQUE TRANSLUCIDE, PARTOUT OU IL Y A UN CHEVRON DE BORD.
     Ils n'avaient AUCUN fond : un chevron nu, tenu lisible par deux ombres portees. Sur une
     photo chargee ca marchait a peu pres, sur une autre le trait se perdait dedans — et il n'y
     avait plus rien a viser du doigt qu'un trait de deux pixels. Le disque rend la cible
     evidente sans masquer ce qu'il y a dessous : il est TRANSLUCIDE et floute le fond, comme
     les commandes d'une application photo. Meme geometrie pour les quatre, parce que c'est le
     meme geste — la rangee d'articles, le carrousel, la barre des rayons, la galerie. */
  width: 34px !important;
  height: 34px !important;
  border-radius: 50% !important;
  border: none !important;
  background: rgba(12, 8, 20, .34) !important;
  backdrop-filter: blur(7px) !important;
  -webkit-backdrop-filter: blur(7px) !important;
  box-shadow: 0 2px 10px rgba(8, 5, 14, .22) !important;
  color: #fff !important;
  filter: none !important;
}
.bo-rangee-fleche svg,
.bo-car-arrow svg,
.bo-raccourcis-fleche svg,
.product-detail .gallery-nav svg { width: 20px !important; height: 20px !important; }

/* Collées : 2 px, pas 12. */
.bo-rangee-fleche.gauche, .bo-car-arrow.left,
.bo-raccourcis-fleche.gauche, .product-detail .gallery-nav-l { left: 2px !important; }
.bo-rangee-fleche.droite, .bo-car-arrow.right,
.bo-raccourcis-fleche.droite, .product-detail .gallery-nav-r { right: 2px !important; }

/* Les flèches de la barre des rayons sont sur fond clair : elles s'y encrent au lieu de blanchir. */
.bo-raccourcis-fleche {
  /* LE CERCLE, SANS LE DISQUE. Le voile blanc plein rendait la cible évidente, mais sur le
     fond déjà clair de la barre il ne se distinguait plus d'une pastille pleine — on voyait un
     rond, pas un chevron avec un contour. Le fond redevient TRANSPARENT ; ce qui reste visible,
     c'est le TRAIT qui dessine le cercle, et le flou hérité de la règle commune (`backdrop-
     filter`) qui trouble doucement les icônes derrière lui, sans les couvrir — un verre
     dépoli, pas une étiquette. */
  background: transparent !important;
  border: 1.5px solid rgba(18, 12, 26, .16) !important;
  box-shadow: none !important;
  color: var(--ink, #16121F) !important;
}

/* ── Le panier de l'en-tête : pas de fond derrière la pastille ──────────────────────── */
/* Un rond rouge derrière le sac blanc faisait deux objets superposés là où il n'y a qu'un
   bouton. La pastille du NOMBRE garde sa couleur — c'est elle qui doit sauter aux yeux. */
.bo-head-btn { background: #fff !important; border-color: transparent !important; }
.bo-head-btn::after, .bo-head-btn::before { display: none !important; }

/* ── « Toute la boutique » : la sortie du filtre ────────────────────────────────────── */
/* « voir tout » en minuscules se lisait comme une note de bas de page, alors que c'est la seule
   sortie : sans lui, on reste enfermée dans une promotion sans comprendre pourquoi le reste a
   disparu. Il devient un vrai bouton, avec l'icône d'une liste complète. */
.bo-filtre-tout {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 9px 16px; border-radius: 999px; cursor: pointer;
  font-size: 13px; font-weight: 700; letter-spacing: -.1px;
  border: 1px solid var(--border, #E9E6EE);
  background: var(--surface, #fff);
  color: var(--ink, #16121F);
  box-shadow: 0 2px 8px -3px rgba(8, 5, 14, .18);
  transition: transform .12s ease, background .15s ease;
}
.bo-filtre-tout svg { width: 15px; height: 15px; opacity: .7; }
.bo-filtre-tout:active { transform: scale(.96); background: var(--accent-tint, #F4F1F8); }

/* ═══════════════════════════════════════════════════════════════════════════════════════
   LA PAGE DE CONFIRMATION — la seule qu'une cliente relit.
   C'est sa preuve d'achat. Elle était faite de paragraphes gris empilés et de deux boutons de
   styles étrangers l'un à l'autre : un bloc vert vif pour WhatsApp, un lien rose pour le retour.
   Rien n'y ressemblait au reste de la boutique, et surtout rien ne disait « ça y est ».
   ═══════════════════════════════════════════════════════════════════════════════════════ */
.bo-conf { padding: 28px 18px 40px; text-align: center; max-width: 460px; margin: 0 auto; }

/* Le sceau. La coche se DESSINE à l'arrivée — un demi-tour de cercle puis le trait — au lieu
   d'apparaître déjà faite. C'est ce geste qui dit « c'est fait maintenant » ; une icône statique
   dit seulement « c'est fait », ce qu'on savait déjà en cliquant. */
.bo-conf-sceau {
  width: 78px; height: 78px; margin: 0 auto 20px;
  border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: color-mix(in srgb, var(--accent, #6B21A8) 9%, #fff);
}
.bo-conf-sceau svg { width: 46px; height: 46px; }
.bo-conf-cercle, .bo-conf-coche { stroke: var(--accent, #6B21A8); }
.bo-conf-cercle {
  stroke-dasharray: 145; stroke-dashoffset: 145;
  animation: boConfCercle .55s cubic-bezier(.4, 0, .2, 1) forwards;
}
.bo-conf-coche {
  stroke-dasharray: 36; stroke-dashoffset: 36;
  animation: boConfCoche .32s cubic-bezier(.4, 0, .2, 1) .42s forwards;
}
@keyframes boConfCercle { to { stroke-dashoffset: 0; } }
@keyframes boConfCoche  { to { stroke-dashoffset: 0; } }
@media (prefers-reduced-motion: reduce) {
  .bo-conf-cercle, .bo-conf-coche { animation: none; stroke-dashoffset: 0; }
}
.bo-conf-sceau.attente { background: #FFF4E5; }

.bo-conf-titre {
  font-size: 24px; font-weight: 800; letter-spacing: -.6px; line-height: 1.22;
  color: var(--ink, #16121F); margin: 0 0 6px;
}
.bo-conf-sous { font-size: 14.5px; color: var(--muted, #6E6880); margin: 0 0 22px; }

.bo-conf-hors-ligne {
  text-align: left; font-size: 13px; line-height: 1.5;
  background: #FFF4E5; border: 1px solid #FFD9A0; color: #7A4A00;
  border-radius: 14px; padding: 13px 15px; margin-bottom: 18px;
}

/* LE REÇU. Chaque ligne aligne son libellé à gauche et sa valeur à droite : on relit un montant,
   pas une phrase. Le total est détaché par un filet — c'est le chiffre qu'on vient vérifier. */
.bo-conf-recu {
  text-align: left; background: var(--surface, #fff);
  border: 1px solid var(--border, #E9E6EE); border-radius: 18px;
  padding: 16px 18px; margin-bottom: 22px;
}
.bo-conf-articles {
  font-size: 14px; line-height: 1.55; color: var(--ink, #16121F); font-weight: 600;
  padding-bottom: 12px; margin-bottom: 4px;
  border-bottom: 1px solid var(--border, #E9E6EE);
}
.bo-conf-ligne {
  display: flex; justify-content: space-between; align-items: baseline; gap: 14px;
  padding: 8px 0; font-size: 13.5px;
}
.bo-conf-ligne span { color: var(--muted, #6E6880); flex: none; }
.bo-conf-ligne strong { color: var(--ink, #16121F); font-weight: 600; text-align: right; }
.bo-conf-total {
  display: flex; justify-content: space-between; align-items: baseline;
  margin-top: 8px; padding-top: 13px; border-top: 1px solid var(--border, #E9E6EE);
}
.bo-conf-total span { font-size: 13px; font-weight: 700; letter-spacing: .6px; text-transform: uppercase; color: var(--muted, #6E6880); }
.bo-conf-total strong {
  font-size: 21px; font-weight: 800; letter-spacing: -.5px;
  color: var(--accent, #6B21A8); font-variant-numeric: tabular-nums;
}

/* VIOLET, PAS VERT (JM, le 7 octobre 2026 : « le bouton vert Écrire à la boutique, j'aime pas le
   vert, mets violet »). Le logo WhatsApp dit déjà où l'on va ; le bouton, lui, est celui de la
   boutique, à la couleur de ses autres gestes. Même forme : pleine largeur, coins de 14, 52 de haut. */
.bo-conf-wa {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  width: 100%; height: 52px; border-radius: 14px;
  background: var(--accent, #6B21A8); color: #fff; text-decoration: none;
  font-size: 15px; font-weight: 700; letter-spacing: -.2px;
  box-shadow: 0 6px 18px -8px rgba(107, 33, 168, .7);
  transition: transform .12s ease, box-shadow .15s ease;
}
.bo-conf-wa svg { width: 21px; height: 21px; }
.bo-conf-wa:active { transform: scale(.975); box-shadow: 0 3px 10px -6px rgba(107, 33, 168, .7); }

/* Le retour n'est pas une action de second rang qu'on cache en petit rose : c'est ce que fera la
   plupart des clientes. Il a la même surface, en plus discret. */
.bo-conf-retour {
  display: flex; align-items: center; justify-content: center;
  width: 100%; height: 48px; margin-top: 10px;
  border-radius: 14px; text-decoration: none;
  font-size: 14.5px; font-weight: 700;
  color: var(--ink, #16121F);
  background: var(--accent-tint, #F4F1F8);
  border: 1px solid var(--border, #E9E6EE);
}
.bo-conf-retour:active { background: var(--border, #E9E6EE); }

/* ── Le texte de promotion respire ──────────────────────────────────────────────────── */
/* Il était collé au titre de la catégorie, au point qu'on lisait « TopsLivraison gratuite ». */
.accordion-promo-text { margin-left: 12px; }
.bo-rangee-tete .accordion-header-left { margin-right: 4px; }

/* ── LE TITRE DE L'ARTICLE : il doit appeler le doigt ───────────────────────────────── */
/* LE NOM DE L'ARTICLE EST UN BOUTON (JM, le 7 octobre 2026 : « Robe pagne tissé devient le bouton,
   plus de chevron »). En texte, avec un chevron et un trait au toucher, on ne voyait pas assez
   qu'il ouvrait la fiche. Une pilule teintée de la boutique, sur une ligne : un nom trop long
   finit en « … ». */
/* LE NOM PREND LE BOUTON MODÈLE, SANS L'APLAT (JM, le 9 octobre 2026 : « fais-le comme le
   bouton Continuer, mais cette fois sans fond totalement violet ou rouge, avec le même design,
   la même ombre »). Il garde donc la tranche de 3 px, l'ombre portée sous lui et l'enfoncement
   à l'appui ; la couleur de la boutique n'arrive qu'en voile, du blanc vers sa teinte claire,
   plus un liseré de la même famille. Trente cartes d'affilée ne font plus trente pavés pleins. */
.bo-rangee .product-card .name,
.product-card .name {
  display: block; height: 34px; line-height: 32px; padding: 0 14px; margin-top: 6px;
  border-radius: 999px; text-align: center; box-sizing: border-box;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  background: linear-gradient(180deg, #FFFFFF 0%, var(--accent-tint, #F4F1F8) 100%);
  border: 1px solid var(--accent-liseret, rgba(107,33,168,.16));
  color: var(--accent, #6B21A8);
  font-size: 13.5px; font-weight: 800; letter-spacing: -.2px;
  -webkit-line-clamp: unset;
  /* Sur un fond pâle, un noir translucide ferait une ligne sale : ici la tranche est la
     couleur de la boutique, très diluée. */
  box-shadow: inset 0 -3px 0 var(--accent-tranche, rgba(107,33,168,.14)), 0 6px 10px -8px var(--bo-lueur);
  transition: transform .09s ease, box-shadow .09s ease, background .09s ease;
}
.product-card:active .name {
  transform: translateY(2px);
  background: var(--accent-tint, #F4F1F8);
  box-shadow: inset 0 -1px 0 var(--accent-tranche, rgba(107,33,168,.14)), 0 2px 4px -3px var(--bo-lueur-appui);
}
@supports (color: color-mix(in srgb, red 50%, blue)) {
  :root {
    --accent-liseret: color-mix(in srgb, var(--accent, #6B21A8) 20%, transparent);
    --accent-tranche: color-mix(in srgb, var(--accent, #6B21A8) 17%, transparent);
    --accent-trait: color-mix(in srgb, var(--accent, #6B21A8) 38%, transparent);
  }
}

/* L'écart entre le titre de catégorie et le texte de promotion.
   `margin-left` ne suffisait pas : l'en-tête est une rangée flex dont l'écart était forcé à 0,
   et la marge se retrouvait écrasée. On rend l'écart à la rangée elle-même. */
.bo-rangee-tete { gap: 12px !important; }
.accordion-promo-text { margin-left: 0; }

/* ═══════════════════════════════════════════════════════════════════════════════════════
   LA TÊTE DE RANGÉE — une seule mise en page, la même pour TOUTES les catégories.

   Elle en avait deux qui se disputaient : une grille écrite quand le titre devait cohabiter
   avec « voir tout », et le flex ajouté depuis. Restait un `display: contents` qui sortait
   l'icône et le titre de leur groupe et les laissait s'écarter tout seuls — d'où « Chemises »
   plaqué à droite, loin de son icône, alors que « Lingerie » restait à gauche.

   Deux catégories qui ne se ressemblent pas, ce n'est pas une variation : c'est un défaut. On
   annule la grille et on garde une seule règle — icône, titre, puis la promotion.
   ═══════════════════════════════════════════════════════════════════════════════════════ */
.bo-rangee-tete {
  display: flex !important;
  grid-template-columns: none !important;
  align-items: center !important;
  justify-content: flex-start !important;
  gap: 12px !important;
  padding: 0 16px 9px !important;
  text-align: left !important;
}
/* Le groupe icône + titre redevient UN bloc : c'est ce qui les tient ensemble. */
.bo-rangee .accordion-header-left {
  display: inline-flex !important;
  align-items: center;
  gap: 9px;
  flex: none;
  min-width: 0;
}
.bo-rangee .accordion-title {
  grid-column: auto !important; grid-row: auto !important;
  font-size: 18px; font-weight: 800; letter-spacing: -.4px;
  color: var(--accent, #6B21A8);
  white-space: nowrap;
}
/* La promotion prend la place qui reste, sur la même ligne, et se coupe proprement si elle est
   trop longue plutôt que de repousser le titre. */
.bo-rangee .accordion-promo-text {
  grid-column: auto !important; grid-row: auto !important;
  flex: 1 1 auto; min-width: 0;
  font-size: 12px; font-weight: 600; margin: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* ── L'ordre de la tête de rangée, posé sans ambiguïté ──────────────────────────────── */
/* Icône, puis le nom JUSTE à côté, puis la promotion poussée à droite.
   Des règles de grille survivaient sur l'icône (`grid-column`, `justify-self`) : inoffensives en
   apparence, elles suffisaient à la décaler dès que le conteneur changeait de mode. On annule, et
   on fixe l'ordre explicitement plutôt que de compter sur celui du HTML — c'est la seule façon
   qu'une même règle donne le même résultat pour les six catégories. */
.bo-rangee .accordion-header-left {
  order: 1;
  grid-column: auto !important; grid-row: auto !important;
}
.bo-rangee-ico {
  order: 0;
  grid-column: auto !important; grid-row: auto !important;
  justify-self: auto !important;
  margin-right: 0 !important;
}
.bo-rangee .accordion-title { order: 1; }

/* La promotion part à DROITE : `margin-left: auto` la pousse au bout de la ligne, et elle se
   coupe proprement plutôt que de repousser le titre. */
.bo-rangee .accordion-promo-text {
  order: 9;
  margin-left: auto !important;
  flex: 0 1 auto !important;
  text-align: right;
}
.bo-flamme { order: 8; margin-left: auto; }

/* Le groupe icône + titre sur UNE LIGNE.
   Une règle héritée le passait en `column` : l'icône et le nom s'empilaient verticalement, ce
   qui donnait à l'œil un décalage variable selon la longueur du nom — et expliquait pourquoi
   « Chemises » et « Lingerie » ne se ressemblaient pas.
   On fixe la direction ET l'alignement : les deux se lisent côte à côte, centrés l'un sur l'autre. */
.bo-rangee .accordion-header-left {
  flex-direction: row !important;
  flex-wrap: nowrap !important;
  align-items: center !important;
  gap: 9px !important;
}

/* ═══════════════════════════════════════════════════════════════════════════════════════
   LA BARRE DES RAYONS : L'AIR AUTOUR, ET LA FLÈCHE À CÔTÉ DES ICÔNES
   ═══════════════════════════════════════════════════════════════════════════════════════
   Mesuré avant : 31 px entre le bas du carrousel et le haut du rayon allumé, contre 7 px
   en dessous. Quatre fois plus d'air d'un côté que de l'autre — la barre ne semblait
   appartenir ni à ce qui la précède ni à ce qui la suit, elle flottait.

   Les 31 px venaient de trois marges empilées qui s'ignoraient : le carrousel poussait
   14 px sous lui, le cadre de la barre en ajoutait 14, la barre elle-même 6 dont le rayon
   allumé reprenait 3 en montant. Personne n'avait décidé de 31 : c'est la somme.

   On les nomme donc une bonne fois, et on les partage — la flèche s'aligne sur les mêmes
   valeurs au lieu de deviner sa hauteur dans son coin.
   ═══════════════════════════════════════════════════════════════════════════════════════ */
.bo-raccourcis-wrap {
  /* L'air au-dessus des icônes. Le même que dessous : 4 + 3 (la barre) = 7, comme en bas. */
  --rac-air: 4px;
  /* Ce que la barre garde au-dessus pour que le rayon allumé — qui monte de 3 px et grossit
     de 14 % — ne soit pas rogné net par le bord. En dessous de 6 px, il se fait couper. */
  --rac-barre-air: 6px;
  --rac-ico: 38px;
  padding-top: var(--rac-air) !important;
}
/* Le carrousel ne pousse plus : la barre se colle dessous, son fond opaque fait la césure. */
.bo-carousel { margin-bottom: 0 !important; }
.bo-raccourcis { padding-top: var(--rac-barre-air) !important; }
.bo-raccourci-tuile { width: var(--rac-ico); height: var(--rac-ico); }

/* LA FLÈCHE, À DROITE DE L'ICÔNE — pas au-dessus.
   Elle se centrait sur la moitié du cadre, or ce cadre contient les icônes ET leurs noms :
   son milieu tombe entre les deux, donc 12 px sous le centre des icônes… ce qui la faisait
   paraître haute, décrochée de la rangée qu'elle est censée faire défiler.
   Elle vise maintenant le centre de l'icône, calculé avec les mêmes valeurs que ci-dessus :
   l'air du cadre, l'air de la barre, puis la moitié de l'icône. */
.bo-raccourcis-fleche {
  top: calc(var(--rac-air) + var(--rac-barre-air) + var(--rac-ico) / 2) !important;
  transform: translateY(-50%);
}
.bo-raccourcis-fleche:active { transform: translateY(-50%) scale(.9); }

/* AUCUN FOND. La flèche n'en avait déjà plus, mais le cadre posait derrière elle un voile
   blanc de 52 px de large — un dégradé censé empêcher qu'une tuile soit coupée net. À
   l'écran, ça ne se lit pas comme un fondu : ça se lit comme un bloc blanc sous la flèche,
   exactement ce qu'on avait retiré du bouton lui-même. L'ombre portée du chevron suffit à
   le détacher. */
.bo-raccourcis-wrap::after,
.bo-raccourcis-wrap::before { display: none !important; }

/* ═══════════════════════════════════════════════════════════════════════════════════════
   LA PROMOTION D'UN RAYON DOIT SE VOIR
   ═══════════════════════════════════════════════════════════════════════════════════════
   « Soldes -30 % sur les robes wax » s'écrivait en 12 px gris, à droite du titre, dans la
   teinte exacte des mentions secondaires. Une remise de 30 % habillée comme une note de
   bas de page : l'œil la classait avec le décor et passait dessus sans la lire.

   Elle devient une pastille aux couleurs de la boutique, avec un reflet qui la traverse
   lentement. Le reflet n'est pas un ornement : c'est la seule chose qui bouge dans une
   page immobile, et c'est ce mouvement qu'on remarque du coin de l'œil. Il passe toutes
   les 3,4 s — assez pour attraper le regard, assez rare pour ne pas agacer.
   ═══════════════════════════════════════════════════════════════════════════════════════ */
.accordion-promo-text {
  position: relative;
  display: inline-flex; align-items: center;
  padding: 3px 9px; border-radius: 999px;
  font-size: 11px !important; font-weight: 800 !important; letter-spacing: .2px;

  /* ── CES QUATRE LIGNES ANNULENT UNE RÈGLE PLUS HAUT, ET IL FAUT SAVOIR POURQUOI ──────
     Une règle précédente donne à .accordion-promo-text un éclat qui balaie les LETTRES :
     `background-clip: text` + `-webkit-text-fill-color: transparent`. Le texte y est rendu
     transparent EXPRÈS, pour laisser voir le dégradé au travers des glyphes.

     En posant ici un fond de pastille, on a repeint TOUTE la surface — la propriété
     raccourcie `background` remet `background-clip` à `border-box` — mais
     `-webkit-text-fill-color` n'est pas une propriété de fond : elle a survécu. Résultat,
     un bandeau plein sans une seule lettre dedans. Le texte était toujours là, peint en
     transparent sur son propre fond.

     Le même piège est décrit plus haut dans ce fichier à propos de .boGreet h2. Deux fois
     la même cause : une règle rend le texte transparent, une autre lui donne un fond. */
  color: #fff !important;
  -webkit-text-fill-color: #fff !important;
  -webkit-background-clip: border-box !important;
  background-clip: border-box !important;

  background-image: linear-gradient(135deg,
    var(--accent, #6B21A8),
    color-mix(in oklab, var(--accent, #6B21A8) 68%, #E11D48)) !important;
  background-size: auto !important;
  box-shadow: 0 1px 6px -1px color-mix(in oklab, var(--accent, #6B21A8) 55%, transparent);
  /* La pastille ne doit jamais pousser le titre du rayon hors de l'écran : elle rétrécit
     avant lui et coupe proprement. */
  min-width: 0; max-width: 62%;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  animation: bo-promo-respire 3.4s ease-in-out infinite !important;
}
/* Le reflet : une bande claire en biais qui traverse la pastille. Il passe DERRIÈRE le texte
   (z-index 0 contre le `position: relative` du texte) — devant, il le lavait au passage. */
.accordion-promo-text::after {
  content: ''; position: absolute; inset: 0; z-index: 0; border-radius: inherit;
  background: linear-gradient(105deg,
    transparent 30%, rgba(255, 255, 255, .42) 48%, transparent 66%);
  transform: translateX(-120%);
  animation: bo-promo-reflet 3.4s ease-in-out infinite;
  pointer-events: none;
}
@keyframes bo-promo-reflet {
  0%, 62% { transform: translateX(-120%); }
  86%, 100% { transform: translateX(120%); }
}
/* Une respiration de l'ombre, pas de la taille : faire grossir la pastille décalerait le
   titre du rayon à chaque battement. */
@keyframes bo-promo-respire {
  0%, 100% { box-shadow: 0 1px 6px -1px color-mix(in oklab, var(--accent, #6B21A8) 45%, transparent); }
  50% { box-shadow: 0 2px 12px 0 color-mix(in oklab, var(--accent, #6B21A8) 75%, transparent); }
}
/* Qui a demandé moins d'animations en reçoit zéro : la pastille reste, colorée et lisible,
   simplement immobile. */
@media (prefers-reduced-motion: reduce) {
  .accordion-promo-text, .accordion-promo-text::after { animation: none !important; }
  .accordion-promo-text::after { display: none; }
}

/* ═══════════════════════════════════════════════════════════════════════════════════════
   DANS LA CONVERSATION : LE NOM DE L'ARTICLE EST LE LIEN
   ═══════════════════════════════════════════════════════════════════════════════════════
   L'assistante énumère « Robe pagne tissé — 43 042 FCFA » et posait à côté une pastille de
   20 px. Personne ne devine que c'est elle qu'il faut viser : on appuie sur le titre, il ne
   se passe rien, et on en conclut que l'assistante ne sait rien faire d'autre que parler.

   Le titre porte donc le lien, aux couleurs de la boutique et en gras : assez pour qu'on le
   voie cliquable. PAS SOULIGNÉ (JM, le 01/10 : « arrêtons de toujours souligner les liens,
   c'est vilain »). L'œil reste, plus grand, comme repère de ce qu'il va se passer : les deux
   ouvrent la même fiche.
   ═══════════════════════════════════════════════════════════════════════════════════════ */
.boNomLien {
  font: inherit; color: var(--accent, #6B21A8); font-weight: 700;
  background: none; border: 0; padding: 0; cursor: pointer;
  text-decoration: none;
}
.boNomLien:active { opacity: .6; }

/* L'œil passe de 20 à 26 px, et son dessin de 13 à 17. À 20 px sur un écran de téléphone,
   la cible était sous le seuil de ce qu'un pouce vise sans réfléchir. */
.boOeil { width: 26px !important; height: 26px !important; margin-left: 5px; vertical-align: -.34em; }
.boOeil svg { width: 17px !important; height: 17px !important; }



/* ═══ LA REGLE QUI A COUTE UNE JOURNEE, ET QU ON GARDE ═══
   Trois versions du micro se disputaient #boMicro.actif dans ce meme fichier, dont une avec
   !important : c est elle qui gagnait, le bouton virait au rouge pale au lieu de grossir. On a
   cru pendant des heures a un cache navigateur. Il n y avait aucun cache.

   REGLE : quand on remplace un bloc de style, on SUPPRIME l ancien dans le meme geste. Une
   feuille ou deux regles visent le meme selecteur ne se debogue pas a la lecture — on lit la
   bonne, on la croit appliquee, et on cherche le probleme partout ailleurs.

   Applique ici : tout le style de la barre de saisie et du micro de la vitrine est parti avec
   eux, `@keyframes boMicroBat` compris. Un fichier ou survivent les regles d un composant
   supprime, c est la meme journee perdue le jour ou quelqu un les relira. */

/* ═══════════════════════════════════════════════════════════════════════════════════════
   LA PHOTO AGRANDIE SE DÉTACHE SUR UN FOND FLOU
   ═══════════════════════════════════════════════════════════════════════════════════════
   Le fond était totalement transparent — la page restait nette derrière la photo. L'idée
   était bonne (ne rien cacher) mais le résultat ne l'est pas : la robe agrandie se retrouve
   posée sur d'autres robes, aussi nettes qu'elle, et l'œil ne sait plus laquelle regarder.
   On ne voyait pas qu'une photo s'était ouverte.

   Un noir opaque réglerait ça et coûterait le contexte : on ne saurait plus d'où l'on vient.
   Le flou fait les deux — la boutique reste là, reconnaissable, mais recule d'un plan. Le
   sujet net devient le seul point de fixation possible.
   ═══════════════════════════════════════════════════════════════════════════════════════ */
.zoom-overlay {
  /* L'assombrissement est LÉGER : c'est le flou qui fait le travail. Trop de noir et on perd
     la boutique ; le voile ne sert qu'à empêcher une photo claire de se fondre dans une page
     claire. */
  background: rgba(18, 12, 26, .28) !important;
  backdrop-filter: blur(14px) saturate(.9);
  -webkit-backdrop-filter: blur(14px) saturate(.9);
  animation: boZoomEntre .18s ease-out;
}
@keyframes boZoomEntre {
  from { opacity: 0; backdrop-filter: blur(0); -webkit-backdrop-filter: blur(0); }
  to   { opacity: 1; }
}
/* Le zoom ANCRÉ (photo principale d'une fiche article) garde son cadre : le flou s'arrête au
   bord de la vignette, sinon il déborderait sur le nom et le prix qu'on veut justement
   continuer de lire pendant qu'on regarde la photo. */
.zoom-overlay.zoom-anchored { border-radius: 18px; overflow: hidden; }

























/* ═══════════════════════════════════════════════════════════════════════════════════════
   LA BARRE DE SAISIE DE L'ASSISTANTE — REMISE
   ═══════════════════════════════════════════════════════════════════════════════════════
   Elle avait ete supprimee le 18/08 au matin, puis redemandee le meme jour : la cliente doit
   pouvoir ecrire, dicter, joindre et photographier. Ces regles sont celles d'avant, reprises
   telles quelles de d42122f — pas reecrites : une reecriture aurait redonne une barre
   « presque pareille », et c'est exactement ce qu'on passe son temps a corriger ici.

   Elles sont regroupees en UN SEUL bloc, en fin de fichier. Eparpillees comme avant, elles
   redonnaient trois generations de #boMicro.actif dans le meme fichier.
   ═══════════════════════════════════════════════════════════════════════════════════════ */

.boChips { display: flex; flex-direction: column; gap: 9px; margin-top: 20px; }
.boChip {
  display: flex; align-items: center; gap: 10px;
  background: #F6F3FB; border: 1px solid transparent;
  border-radius: 999px; padding: 11px 16px;
  font-size: 14px; color: var(--text); text-align: left;
  cursor: pointer; transition: background .15s ease, transform .15s ease;
  font-family: inherit;
}
.boChip:active { transform: scale(.98); background: var(--primary-bg); }
.boChip svg { width: 17px; height: 17px; color: var(--primary); flex: none; }
#boChatFoot { padding: 10px 12px 14px; background: #fff; border-top: 1px solid var(--border); }
.boInputWrap {
  display: flex; align-items: flex-end; gap: 8px;
  background: #F6F3FB; border: 1px solid var(--border);
  border-radius: 26px; padding: 6px 6px 6px 16px;
  transition: border-color .18s ease, box-shadow .18s ease;
}
.boInputWrap:focus-within { border-color: var(--primary-light); box-shadow: 0 0 0 3px rgba(168, 85, 247, .14); }
#boChatInput {
  flex: 1; border: none; background: transparent; outline: none; resize: none;
  font-size: 15px; font-family: inherit; color: var(--text);
  padding: 8px 0; max-height: 96px; line-height: 1.4;
}
#boChatSend {
  width: 38px; height: 38px; flex: none; border: none; border-radius: 50%;
  background: linear-gradient(135deg, #7C3AED, var(--bo-purple-dark));
  color: #fff; cursor: pointer; display: flex; align-items: center; justify-content: center;
  transition: transform .15s ease, opacity .15s ease;
}
#boChatSend svg { width: 18px; height: 18px; }
#boChatSend:disabled { opacity: .35; cursor: default; }
#boChatSend:not(:disabled):active { transform: scale(.9); }
#boChatSend { background: var(--accent) !important; }
.boInputWrap:focus-within {
  border-color: var(--accent) !important;
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 16%, transparent) !important;
}
.boChip { border-color: var(--border) !important; }
.boChip svg { color: var(--accent) !important; }
.boChip:active { background: var(--accent-tint) !important; }
#boChatInput, #boChatInput:focus, #boChatInput:focus-visible {
  border: none !important;
  box-shadow: none !important;
  outline: none !important;
}
#boChatFoot { flex: none; }
#boChatFoot { padding: 10px 12px 14px; border-top: none; background: #fff; }
.boInputWrap {
  background: #fff !important;
  border: 1.5px solid #E4E0EB !important;
  border-radius: 28px !important;
  padding: 5px 5px 5px 18px !important;
  box-shadow: 0 1px 2px rgba(18, 12, 26, .04), 0 6px 18px rgba(18, 12, 26, .05);
  transition: border-color .18s ease, box-shadow .18s ease;
}
.boInputWrap:focus-within {
  border-color: #B9B2C6 !important;
  box-shadow: 0 2px 6px rgba(18, 12, 26, .07), 0 10px 26px rgba(18, 12, 26, .09) !important;
}
/* SEIZE PIXELS, PAS MOINS : en dessous, l'iPhone ZOOME toute la page dès qu'on touche le champ.
   Mesuré dans le simulateur le 13 octobre 2026 : à 15,5 px la page de 440 n'en montrait plus que
   413, le panneau débordait des deux côtés et tout se décalait. */
#boChatInput { font-size: 16px; padding: 9px 0; }
#boChatInput::placeholder { color: #A9A3B6; }
#boChatSend {
  width: 40px !important; height: 40px !important;
  background: #EDEAF2 !important; color: #9A94A8 !important;
  transition: background .18s ease, color .18s ease, transform .15s ease;
}
#boChatSend:not(:disabled) { background: var(--accent) !important; color: #fff !important; }
#boChatSend:not(:disabled):active { transform: scale(.92); }
#boJoindre {
  width: 34px; height: 34px; flex: none; align-self: flex-end; margin-bottom: 3px;
  border: none; background: none; color: #9A94A8; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%; transition: color .15s ease, background .15s ease;
}
#boJoindre svg { width: 19px; height: 19px; }
#boJoindre:active { background: #F1EEF5; color: var(--ink); }
.boInputWrap { padding-left: 6px !important; }
#boJointeApercu {
  display: flex; align-items: center; gap: 9px;
  margin: 0 4px 8px; padding: 7px 9px;
  background: #fff; border: 1.5px solid var(--border); border-radius: 14px;
}
#boJointeApercu img { width: 38px; height: 38px; border-radius: 9px; object-fit: cover; flex: none; }
.boJointeIco {
  width: 38px; height: 38px; border-radius: 9px; flex: none;
  background: var(--accent-tint);
}
.boJointeNom {
  flex: 1; min-width: 0; font-size: 12.5px; font-weight: 600; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
#boJointeX {
  flex: none; width: 26px; height: 26px; border-radius: 50%;
  border: none; background: #F1EEF5; color: var(--muted);
  line-height: 1; cursor: pointer;
  display: flex; align-items: center; justify-content: center; padding: 0;
}
#boJointeX svg { width: 15px; height: 15px; }
/* ══ LE MICRO DE L'ASSISTANTE — UNE SEULE DÉCLARATION ══════════════════════════════════
   `#boMicro` était déclaré HUIT fois dans ce fichier, chacune écrasant la précédente à coups
   de `!important` : 34, 36, 46, 56, 64, 48, 66 puis 54 px de côté, et son SVG 19, 22, 23, 27,
   29, 23, 30 puis 25 px. On corrigeait la première, rien ne bougeait, et il fallait descendre
   de trois cents lignes pour trouver celle qui gagnait vraiment.

   Une seule déclaration, aux valeurs qui gagnaient réellement, et plus un seul `!important`.

   54 px : la hauteur de la barre de saisie vide, plus la marge du trait. On MAINTIENT ce
   bouton, on ne le tape pas — le pouce se pose à plat et couvre 15 mm — mais 66 px débordait
   de la barre en haut comme en bas. La cible reste large grâce au débord tactile, qui n'a
   jamais eu besoin que le DESSIN grossisse. */
#boMicro {
  position: relative;
  width: 54px; height: 54px; flex: none; align-self: flex-end; margin-bottom: 3px;
  border: none; border-radius: 50%;
  background: var(--accent, #6B21A8); color: #fff; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  /* `touch-action: none` est INDISPENSABLE : sans lui, le glissement vers le haut fait
     défiler la page au lieu d'atteindre le cadenas, et le geste devient impossible au doigt. */
  touch-action: none;
  /* AUCUNE transition sur `transform` : le déplacement suit le doigt, sinon la pastille
     traîne derrière le pouce et le geste paraît mou. */
  transition: background .15s ease, box-shadow .15s ease;
  /* La zone tactile déborde encore de 6 px tout autour, sans que le dessin grossisse. */
  box-shadow: 0 2px 12px -2px color-mix(in oklab, var(--accent, #6B21A8) 60%, transparent),
              0 0 0 6px transparent;
}
#boMicro svg { width: 25px; height: 25px; }
#boMicro:active { transform: scale(.92); }
/* Pas de `color` ici : le glyphe reste BLANC sur la pastille violette. Le `var(--accent)`
   qu'on y trouvait peignait un micro violet sur un fond violet — invisible. Il ne s'est
   jamais vu parce qu'un `!important` du bloc suivant le neutralisait ; en retirant les
   `!important` il serait apparu pour de bon. Idem pour le `background` du `:active`. */
#boMicro.travaille { opacity: .55; pointer-events: none; }
@keyframes boMicroBat {
  from { opacity: .45; transform: scale(.7); }
  to   { opacity: 1;   transform: scale(1.15); }
}
#boChatFoot .boInputWrap { flex: 1; min-width: 0; }
#boChatFoot > div:last-child,
#boChatFoot { display: flex; align-items: flex-end; gap: 8px; }
#boMicro .boMicroIco { display: flex; }
#boMicro.actif { transform: scale(1.12); box-shadow: 0 0 0 8px color-mix(in oklab, var(--accent, #6B21A8) 18%, transparent); }
#boMicro .boMicroVerrou {
  position: absolute; bottom: calc(100% + 8px); left: 50%; transform: translateX(-50%);
  display: none; flex-direction: column; align-items: center; gap: 5px;
  pointer-events: none;
}
#boMicro.actif .boMicroVerrou { display: flex; }
/* ═══ LE CADENAS DU MICRO : UNE SEULE DECLARATION ═══
   Il etait ecrit SEPT fois, a sept tailles — 34, 38, 42, 32, 30, 36, 32 px — chacune ecrasant
   la precedente a coups de `!important`. Sept reponses a la meme question : on lisait la
   premiere du fichier et on en voyait une autre a l'ecran. C'est le defaut deja corrige sur
   `#boMicro` lui-meme, laisse en place sur son voisin.

   Ce qui gagnait REELLEMENT : 32 px, glyphe a 15 px. C'est ce qui reste, ecrit une fois, ici.
   Plus aucun `!important` : rien d'autre dans la feuille ne vise ce selecteur, et `chat.js` ne
   pose sa transformation que sur `#boMicro` et sur `.boMicroVerrou` — jamais sur la pastille.

   UNE SEULE TAILLE, quel que soit l'etat : pas de `scale` au blocage. Un objet qui change de
   taille en meme temps que de couleur ne se lit pas comme le meme objet — seule la couleur dit
   que c'est pris, et un fondu de 180 ms relie les deux etats. Les deux dessins, ouvert et
   ferme, sont empiles en absolu : affiches l'un a la place de l'autre dans le flux, la boite se
   recalculait et le cadenas se decalait d'un demi-pixel a chaque bascule. */
#boMicro .boMicroCadenas {
  position: relative;
  width: 32px; height: 32px; border-radius: 16px;
  display: flex; align-items: center; justify-content: center;
  /* Blanc, pas teinte : c'est la cible du geste, elle doit se detacher de tout ce qu'il y a
     derriere — une conversation, une photo, un fond clair comme un fond sombre. */
  background: #fff; color: var(--accent, #6B21A8);
  box-shadow: 0 2px 10px rgba(18, 12, 26, .22);
  transform: none;
  transition: background .18s ease, color .18s ease;
}
#boMicro .boMicroCadenas svg {
  position: absolute; top: 50%; left: 50%;
  width: 15px; height: 15px;
  margin: 0; transform: translate(-50%, -50%);
}
/* ETAT bloque — pas un doublon : seule la couleur change. Le violet de la boutique, celui des
   choses qui ont pris. Le rouge reste reserve a l'annulation, le seul incident du geste. */
#boMicro.verrouille .boMicroCadenas {
  background: var(--accent, #6B21A8); color: #fff;
}
#boMicro.verrouille .boMicroIco svg { display: none; }
#boMicro.verrouille .boMicroIco::before {
  content: ''; display: block; width: 16px; height: 16px; border-radius: 3px; background: #fff;
}
/* LE DECOMPTE NE COUVRE PLUS LE TEXTE QU'ON DICTE.
   Il etait pose en `inset: 0` : une nappe opaque sur TOUTE la barre de saisie. Les mots
   transcrits s'ecrivaient dessous, invisibles — au moment precis ou l'on veut se relire.
   Il devient une petite pastille, calee A DROITE et DEBORDANT SOUS la barre : le champ reste
   entierement lisible, et le compteur se voit sans rien masquer. */
#boMicroBandeau {
  position: absolute; right: 10px; bottom: -13px; left: auto; top: auto; z-index: 3;
  display: flex; align-items: center; gap: 6px;
  height: 26px; padding: 0 10px; border-radius: 13px;
  background: #F6F3FB; border: 1px solid var(--line, #E8E4EE);
  box-shadow: 0 2px 6px rgba(18, 12, 26, .08);
}
#boMicroBandeau.annule { background: color-mix(in oklab, #E5484D 10%, #F6F3FB); }
/* Le point d'enregistrement est declare UNE SEULE FOIS, plus bas dans ce fichier, et il est
   VIOLET. Il etait defini ici en rouge clignotant, puis redefini en violet fixe 80 lignes
   plus loin : on corrigeait le rouge sans que rien ne change a l'ecran. */
/* LE CHRONOMETRE PREND LE GABARIT DES ICONES DE LA BARRE (34 px, comme le sourire, le
   trombone et l'appareil photo). Pose sans hauteur, il flottait entre deux reperes et se
   confondait avec le texte autour — on ne savait plus si l'on lisait un compteur ou un debut
   de phrase. `min-width` fige sa largeur : sans elle, le passage de 0:09 a 0:10 decalait tout
   ce qui suit d'un caractere. */
#boMicroChrono {
  height: 26px; min-width: 34px; display: flex; align-items: center; justify-content: center;
  font-size: 12.5px; font-weight: 800; color: var(--ink, #16121F); font-variant-numeric: tabular-nums;
}
.boInputWrap { position: relative; }
#boEmoji, #boJoindre, #boPhoto {
  width: 34px; height: 34px; flex: none;
  border: none; background: none; color: #8A8698; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%; align-self: flex-end; margin-bottom: 3px;
  transition: color .15s ease, background .15s ease;
}
#boEmoji svg, #boJoindre svg, #boPhoto svg { width: 21px; height: 21px; }
#boEmoji:active, #boJoindre:active, #boPhoto:active { background: rgba(0, 0, 0, .05); color: var(--accent, #6B21A8); }
#boChatInput { caret-color: var(--accent, #6B21A8); }
#boEmojiTiroir {
  display: flex; gap: 2px; overflow-x: auto; scrollbar-width: none;
  margin-top: 8px; padding: 6px 4px;
  background: #F6F3FB; border-radius: 18px;
}
#boEmojiTiroir::-webkit-scrollbar { display: none; }
.boEmojiChoix {
  flex: none; width: 38px; height: 38px; border: none; background: none;
  font-size: 22px; line-height: 1; cursor: pointer; border-radius: 12px;
}
.boEmojiChoix:active { background: rgba(0, 0, 0, .06); }
#boMicro.verrouille .boMicroIco::before { width: 19px; height: 19px; border-radius: 4px; }
.boInputWrap { align-items: flex-end; }
#boChatInput {
  max-height: 156px !important;      /* ≈ 7 lignes, au-delà ça défile */
  min-height: 24px;
  line-height: 1.45 !important;
  overflow-y: auto;
}
#boEmoji, #boJoindre, #boPhoto { align-self: flex-end; margin-bottom: 3px; }
#boMicro.verrouille .boMicroIco::before { width: 21px; height: 21px; border-radius: 5px; }
#boChatFoot { align-items: flex-end; }
/* ═══════════════════════════════════════════════════════════════════════════════════════
   LE MICRO DE LA VITRINE PREND CELUI DE MANAGE, AU DETAIL PRES
   ═══════════════════════════════════════════════════════════════════════════════════════
   Reference : `src/components/BandeauMicro.tsx` et `MicTranscribeButton.tsx` dans
   manageboutique. Le meme geste montrait deux choses differentes selon l'application.

   LE BANDEAU NE RECOUVRE PLUS LA BARRE. Il etait pose en `inset: 0` : des qu'on maintenait le
   micro, le sourire et le placeholder « Message » DISPARAISSAIENT derriere un bandeau lavande
   pleine largeur. Une barre qui s'efface pendant qu'on parle fait douter d'avoir encore une
   barre — on relache pour verifier, et on coupe son propre enregistrement. Manage pose une
   pastille BLANCHE bordee au bout du champ, et le reste de la barre continue d'exister.

   LE POINT EST VIOLET ET FIXE, pas rouge et clignotant. Un rond rouge qui bat se lit comme une
   alerte — « quelque chose ne va pas » — la ou il dit seulement « ca enregistre ». Il ne passe
   au rouge que pour annoncer l'annulation, ou c'en est bien une.

   LE CADENAS REVIENT A 32 px. Il avait grandi avec la pastille et se retrouvait presque aussi
   gros qu'elle : deux cibles de meme taille l'une au-dessus de l'autre, on ne sait plus
   laquelle on vise.

   LE CARRE D'ARRET des la premiere seconde, et non une fois bloque seulement : c'est ce que
   fait Manage, et c'est plus juste — des cet instant, le seul geste possible est d'arreter.
   ═══════════════════════════════════════════════════════════════════════════════════════ */
#boMicroBandeau {
  position: absolute; inset: auto 6px 5px auto; z-index: 2;
  display: flex; align-items: center; gap: 8px;
  padding: 7px 11px; border-radius: 999px;
  min-width: 210px;
  background: #fff; border: 1px solid #F0F0F0;
}
#boMicroBandeau.annule { background: rgba(229, 72, 77, .055); border-color: #E5484D; }
/* LE POINT EST VIOLET ET FIXE, PAS ROUGE ET CLIGNOTANT — comme dans Manage
   (`src/components/BandeauMicro.tsx`, qui fait foi des deux cotes). Un rond rouge qui bat se
   lit comme une alerte — « quelque chose ne va pas » — la ou il dit seulement « ca
   enregistre ». Le ROUGE est reserve a l'annulation, juste en dessous : c'est le seul moment
   ou il y a vraiment quelque chose a annoncer. */
.boMicroPoint {
  width: 8px; height: 8px; border-radius: 50%; flex: none;
  background: var(--accent, #6B21A8); animation: none;
}
#boMicroBandeau.annule .boMicroPoint { background: #E5484D; }
#boMicroChrono { font-size: 12.5px; font-weight: 800; color: var(--ink, #16121F); }

#boMicro.actif .boMicroIco svg,
#boMicro.verrouille .boMicroIco svg { display: none; }
#boMicro.actif .boMicroIco::before,
#boMicro.verrouille .boMicroIco::before {
  content: ''; display: block; width: 21px; height: 21px; border-radius: 5px; background: #fff;
}

/* ═══ LE MICRO A LA HAUTEUR DU CHAMP, PAS PLUS ═══
   Il etait passe a 64 px pour qu'on ne le rate pas au pouce. Le calcul etait juste pour la
   cible, faux pour l'oeil : une pastille d'une fois et demie la hauteur de la barre a cote de
   laquelle elle est posee ne fait plus partie de la barre — elle flotte devant, et deborde en
   haut comme en bas.

   48 px : exactement la hauteur de la pilule de saisie. La cible reste large grace au
   `hitSlop` du geste, qui n'a jamais eu besoin que le DESSIN grossisse.

   Le cadenas reste ou il est. C'est la pastille qui monte vers lui — voir `afficher()` dans
   chat.js. Une serrure qui bouge en meme temps que ce qu'on pousse vers elle ne se rejoint
   jamais.

   La TAILLE de la pastille a rejoint la declaration unique de `#boMicro`, tout en haut, et
   celle du cadenas la declaration unique de `.boMicroCadenas`, au meme endroit. */
#boMicro.actif .boMicroIco::before,
#boMicro.verrouille .boMicroIco::before { width: 16px; height: 16px; border-radius: 4px; }
/* ═══ LE CADENAS : OUVERT, PUIS FERME QUAND LE MICRO LE REJOINT ═══
   Il etait deja ferme des le depart : immobile et clos, il ne demandait rien. Ouvert, il pose
   une question — « viens ici » — et c'est l'arrivee de la pastille sur lui qui le referme.
   C'est le seul moment ou l'on comprend que le blocage a pris. */
#boMicro .boCadFerme { display: none; }
#boMicro.verrouille .boCadOuvert { display: none; }
#boMicro.verrouille .boCadFerme { display: block; }
/* La colonne du verrou ne suit PAS la pastille : chat.js lui applique la translation inverse.
   Sans transition, sinon elle traine derriere et l'on voit le cadenas osciller. */
#boMicro .boMicroVerrou { transition: none; }

/* ═══════════════════════════════════════════════════════════════════════════════════════
   LE TEXTE PREND TOUTE LA LARGEUR, LES ICONES PASSENT DESSOUS
   ═══════════════════════════════════════════════════════════════════════════════════════
   Le champ etait coince ENTRE le sourire a gauche et le trombone + l'appareil photo a droite :
   il ne restait qu'une bande centrale d'environ la moitie de la barre. Apres un vocal de vingt
   secondes — le moment ou l'on veut TOUT relire avant d'envoyer — le texte s'entassait sur
   quatre lignes etroites au milieu d'un cadre aux deux tiers vide.

   Le texte passe SEUL sur la premiere ligne, pleine largeur, et les icones se rangent sur une
   ligne en dessous. C'est la disposition de tous les champs de saisie longs, et pour la meme
   raison : ce qu'on ecrit compte plus que ce avec quoi on l'ecrit.

   `flex-wrap` plutot qu'un conteneur de plus : les icones sont deja des freres du champ, il
   suffit de les faire passer a la ligne. Un div intermediaire aurait demande de toucher au
   gabarit — et le bandeau du micro, qui s'ancre sur ce cadre, aurait change de reference.
   ═══════════════════════════════════════════════════════════════════════════════════════ */
.boInputWrap {
  flex-wrap: wrap !important;
  align-items: center !important;
  padding: 8px 12px 6px !important;
  row-gap: 2px;
}
#boChatInput {
  flex: 1 0 100% !important;
  order: 1;
  width: 100%;
  padding: 4px 2px !important;
  max-height: 176px !important;   /* ~8 lignes, puis ca defile */
}
#boEmoji, #boJoindre, #boPhoto { order: 2; align-self: center !important; margin-bottom: 0 !important; }
/* Le bandeau se pose sur la LIGNE DES ICONES, pas par-dessus le texte : pendant qu'on parle,
   ce qui est deja transcrit doit rester lisible. */
#boMicroBandeau { inset: auto 8px 6px auto !important; }

/* ═══ LE MICRO : PLUS GROS, PARCE QU'ON LE MAINTIENT ═══
   48 px se ratait. Un bouton qu'on MAINTIENT n'est pas un bouton qu'on tape : le pouce se pose
   a plat et couvre 15 mm ; sous 60 px, un appui sur deux glisse a cote et ne prend pas. Il ne
   deborde plus de la barre maintenant que celle-ci a deux lignes.
   (La taille finalement retenue est celle de la declaration unique du haut : 54 px.) */
#boMicro.actif .boMicroIco::before,
#boMicro.verrouille .boMicroIco::before { width: 22px; height: 22px; border-radius: 5px; }
#boChatSend { width: 44px !important; height: 44px !important; align-self: flex-end; }

/* ═══ VIDE : UNE LIGNE. AVEC DU TEXTE : DEUX BLOCS. ═══
   La barre passait en deux blocs EN PERMANENCE — texte en haut, icones en dessous. Vide, elle
   occupait donc deux lignes pour montrer « Message » et trois pictogrammes : un pave au bas de
   l'ecran, alors qu'il n'y a rien dedans.

   Elle ne s'ouvre que quand elle a quelque chose a montrer. La classe `aDuTexte` est posee par
   chat.js des que le champ n'est plus vide — dictee et emojis compris, qui remplissent le champ
   sans declencher l'evenement `input`.

   Et la hauteur suit le CONTENU : deux lignes de texte donnent deux lignes. Un champ fige a
   sept lignes pour en afficher deux, c'est le meme defaut que celui qu'on vient de corriger,
   pris par l'autre bout. */
.boInputWrap { flex-wrap: nowrap !important; border-radius: 999px !important; }
#boChatInput { flex: 1 1 auto !important; }
.boInputWrap.aDuTexte { flex-wrap: wrap !important; border-radius: 22px !important; }
.boInputWrap.aDuTexte #boChatInput { flex: 1 0 100% !important; }

/* UN SEUL CADENAS, QUI CHANGE DE COULEUR : pas de `scale`, un fondu de 180 ms, les deux dessins
   empiles en absolu. Le seul mouvement du geste est celui de la pastille qui monte — c'etait le
   but. Tout est dans la declaration unique de `.boMicroCadenas`, plus haut. */

/* ═══ LE MICRO REDESCEND A 54 px — il debordait de la barre vide ═══
   66 px etait calcule pour une barre de deux lignes ; vide, elle n'en a qu'une (~50 px) et la
   pastille depassait en haut et en bas. 54 px : la hauteur de la barre vide, plus la marge du
   trait. La cible du pouce reste au-dessus du seuil des 15 mm reels grace au debord tactile.
   C'est CETTE valeur qui a ete portee dans la declaration unique de `#boMicro`, tout en haut
   du bloc de l'assistante — plus rien ne la reecrit ici. */
#boMicro.actif .boMicroIco::before,
#boMicro.verrouille .boMicroIco::before { width: 18px; height: 18px; border-radius: 4px; }

/* ═══ LE CADENAS : UN SEUL DESSIN ═══
   Les deux variantes ouvert/ferme donnaient l'impression de deux objets. Fin du debat : UN
   dessin, le cadenas ferme, affiche dans les deux etats. Il est cloue a sa place au-dessus du
   bouton — le contre-deplacement de chat.js l'y maintient pendant le glissement.
   La taille et les couleurs sont dans la declaration unique de `.boMicroCadenas`, plus haut. */
#boMicro .boCadOuvert { display: none !important; }
#boMicro .boCadFerme { display: block !important; }

/* ═══ BARRE VIDE : LE SOURIRE RESTE A GAUCHE ═══
   La regle `order: 2` posee pour la barre ouverte s'appliquait AUSSI a la barre vide : les
   trois icones passaient apres le champ, et le sourire se retrouvait a droite. L'ordre du DOM
   — sourire, champ, trombone, appareil — est le bon tant qu'on tient sur une ligne. */
.boInputWrap:not(.aDuTexte) #boEmoji,
.boInputWrap:not(.aDuTexte) #boJoindre,
.boInputWrap:not(.aDuTexte) #boPhoto { order: 0; }
/* Ouverte : le sourire descend SUR LA LIGNE DES ICONES, avec le trombone et l'appareil. Il
   restait seul en haut, au-dessus du texte — un sourire orphelin qui semblait etre un titre. */
.boInputWrap.aDuTexte #boEmoji,
.boInputWrap.aDuTexte #boJoindre,
.boInputWrap.aDuTexte #boPhoto { order: 2; }

/* ═══ LE PANNEAU DE L'ASSISTANTE NE SORT JAMAIS DU CADRE ═══
   Regle de la maison : aucun element hors du viewport. `dvh` plutot que `vh` — sur telephone,
   `vh` ignore la barre du navigateur et le pied du panneau passait dessous. */
#boChatPanel {
  max-width: calc(100vw - 16px);
  height: min(620px, calc(100dvh - 20px));
}

/* ═══ LA VIGNETTE DU PANIER REMPLIT SA CASE ═══
   `object-fit: contain` sur fond NOIR : une photo en portrait laissait deux bandes noires de
   part et d'autre, une photo en paysage deux bandes en haut et en bas — la moitie de la case
   etait du vide sombre. `cover` remplit la case en recadrant, comme toutes les vignettes de la
   vitrine ; le fond clair ne sert plus que pendant le chargement. */
.cart-item .thumb img,
.cart-item .thumb video { object-fit: cover !important; background: #F3F4F6 !important; }

/* ═══ REGLE : AUCUN ELEMENT HORS DU CADRE TELEPHONE ═══
   La vitrine EST un telephone : le corps de page fait --bo-cadre de large (body, style.css). Le panneau de
   l'assistante et sa pastille etaient ancres sur la FENETRE (`right: 10px`) : sur un ecran
   d'ordinateur ils flottaient a droite, hors du cadre — comme une fenetre d'une autre
   application posee a cote de la boutique. Tout element flottant s'ancre sur le CADRE. */
#boChatPanel {
  right: auto;
  left: calc(var(--bo-gauche) + max(10px, calc(min(100vw, var(--bo-cadre)) - min(370px, 94vw) - 10px)));
}

/* ═══ SUR TÉLÉPHONE, LE PANNEAU D'OUFY EST CENTRÉ, PAS POUSSÉ À DROITE ═══
   JM, le 12 octobre 2026 : « la page pour Oufy dans shop, elle est penchée vers la droite,
   elle n'est pas bien centrée, tout est mélangé là-bas ».

   La règle du dessus ancre le panneau sur le cadre en calculant une marge GAUCHE à partir
   d'une largeur de 370 px. Sur un téléphone de 390 px, cette marge valait 13 px à gauche
   pendant que `width: auto` (posé par le média 560 px juste au-dessus, dont le `right: 8px`
   est écrasé ici) laissait le panneau s'étaler jusqu'au bord : 13 px d'un côté, 3 de l'autre.
   Le panneau penchait, et son contenu, serré contre le bord, paraissait mêlé.

   Sur téléphone il prend donc toute la largeur du cadre moins huit pixels de chaque côté, et
   son bas se pose au-dessus de la barre d'accueil de l'iPhone. */
@media (max-width: 560px) {
  #boChatPanel {
    left: calc(var(--bo-gauche) + 8px);
    right: auto;
    width: calc(min(100vw, var(--bo-cadre)) - 16px);
    max-width: calc(min(100vw, var(--bo-cadre)) - 16px);
    bottom: calc(8px + env(safe-area-inset-bottom, 0px));
  }
}

/* ═══ LE CADENAS NE DESCEND PLUS DE 30 px AU BLOCAGE ═══
   Mesure dans le navigateur : maintenu, le cadenas etait a y=105,7 ; bloque, a y=136. Les
   chevrons sous lui passaient en `display: none`, la colonne — ancree par le BAS au-dessus du
   bouton — se raccourcissait, et le cadenas tombait de toute leur hauteur. C'etait CA, les
   « deux cadenas a deux hauteurs ».
   `visibility: hidden` retire les chevrons de l'ecran mais pas de la mise en page : la colonne
   garde sa hauteur, le cadenas ne bouge pas d'un pixel. */

/* LE CADENAS BLOQUE EST VIOLET, pas rouge : bloquer l'enregistrement est un succes, pas un
   incident. La regle est remontee dans la declaration unique de `.boMicroCadenas`. */

/* ═══ LE HEADER FAIT EXACTEMENT 70 px, comme celui de Manage ═══
   Les deux barres se laissaient porter par leur contenu — 65 px ici, autre chose la-bas — et
   l'ecart se voyait en passant d'une application a l'autre sans qu'on sache le nommer. */
/* LA BARRE COLLANTE DESCEND SOUS L'ENCOCHE (JM, le 12 octobre 2026). Mesuré dans l'émulateur :
   `env(safe-area-inset-top)` vaut 62 px sur un iPhone 17 Pro Max, et `.header` l'appliquait
   déjà ; cette barre-ci, posée par header.js et haute de 70 px fixes, ne le faisait pas. Le nom
   de la boutique passait donc SOUS l'heure et l'îlot. Son fond remplit maintenant la zone sûre,
   comme le veut la règle : la couleur du header, jamais du gris ni du blanc étranger. Dans un
   navigateur d'ordinateur, `env()` vaut zéro et la barre garde ses 70 px exactement. */
.bo-head {
  /* LA BARRE REMONTE D'UN CRAN (JM, le 12 octobre 2026 : « la zone header peut monter encore
     plus haut, on la fait monter un peu »). Elle garde toute la zone sûre — le nom de la
     boutique ne repassera pas sous l'îlot — mais son contenu se cale HAUT dans la bande au
     lieu d'être poussé par une hauteur fixe, et la bande elle-même perd huit pixels. */
  /* ── ENCORE UN CRAN, ET C'EST LE DERNIER POSSIBLE ──────────────────────────────────
     JM, le 20 octobre 2026 : « regarde l'en-tête comme il est haut, il descend trop, il doit
     rester très proche de la safe zone ».

     La bande sous l'encoche faisait 62 px pour un logo de 40 : onze pixels de vide au-dessus,
     onze au-dessous. Sur un iPhone 14 Pro Max ça donnait 59 + 62 = 121 pt de barre, et c'est
     ce que mesure sa capture.

     Elle passe à 52 px : six pixels de part et d'autre du logo. C'est le PLANCHER tant que le
     logo fait 40 px — descendre plus bas demanderait de rapetisser le logo, pas de serrer la
     bande. Dix pixels gagnés, et le titre de la boutique vient se poser juste sous l'heure.

     `--bo-entete-bande` nomme cette bande, pour n'avoir qu'un nombre à changer le jour où le
     logo change. Elle ne se confond PAS avec `--bo-entete-h`, que `js/header.js` mesure et
     pose sur la page entière : celle-là vaut la barre TOUT ENTIÈRE, encoche comprise, et sert
     au râtelier collant. */
  --bo-entete-bande: 52px;
  height: calc(var(--bo-entete-bande) + env(safe-area-inset-top, 0px));
  box-sizing: border-box;
  padding: env(safe-area-inset-top, 0px) 14px 0;
  align-items: center;
}

/* ═══════════════════════════════════════════════════════════════════════════════════════
   LES MOTIFS DEVIENNENT VISIBLES
   ═══════════════════════════════════════════════════════════════════════════════════════
   « On ne sent meme pas qu'ils existent : avec ou sans, c'est pareil. » Et c'etait vrai — du
   blanc a 3,5 ou 5 % sur un violet moyen, l'ecart de luminosite est de l'ordre de deux points
   sur 255. Invisible sur un ecran de telephone en plein jour.

   Deux corrections, pas une :

   1. L'OPACITE MONTE — autour de 11 a 13 % au lieu de 3,5 a 5,5. C'est encore une trame, pas
      un dessin : a 25 % on ne verrait plus la couleur de la boutique, seulement les pois.

   2. ON AJOUTE UNE OMBRE, et c'est elle qui fait tout. Un motif uniquement plus CLAIR se noie
      des que le fond est clair ; double d'un trait plus SOMBRE decale d'un demi-pixel, il se
      detache sur n'importe quelle teinte — c'est ainsi que sont graves les motifs sur les
      pagnes et les tissus imprimes. Le relief se voit meme quand la lumiere ne s'y prete pas.
   ═══════════════════════════════════════════════════════════════════════════════════════ */
/* `position: relative` SEULEMENT LA OU CE N'EST PAS DEJA POSITIONNE.

   Cette ligne ancrait le calque d'ombre du motif. Elle a fait bien plus que ca : elle a
   DECOLLE l'en-tete de la boutique. `.bo-head` est `position: sticky` depuis toujours, mais
   cette regle vient 3 600 lignes plus bas et l'ecrasait sans bruit — le bandeau « Elegance
   d'Afrique — BY AMINATA » remontait avec la page au lieu de rester sous les yeux, et rien
   dans la feuille ne disait pourquoi. Deux regles qui parlent de la meme propriete a 3 600
   lignes d'ecart, c'est la derniere qui gagne, meme si elle ne savait pas qu'elle decidait.

   `sticky` etablit un bloc contenant exactement comme `relative` : le calque d'ombre s'ancre
   aussi bien sur l'un que sur l'autre. Seule `.bo-topbar`, qui n'est pas positionnee, en avait
   besoin. */
.bo-topbar { position: relative; }
.header { position: relative; }
.bo-head { position: sticky; top: 0; z-index: 95; }
.bo-head::after, .header::after, .bo-topbar::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  /* L'ombre du motif : la meme trame, en NOIR a 9 %, decalee d'un pixel vers le bas. */
  background: var(--head-motif-ombre, none);
  background-position: 1px 1px;
  opacity: .55;
}
/* Le contenu de l'en-tete passe AU-DESSUS de la trame — sans quoi le nom de la boutique se
   lirait a travers un voile. */
.bo-head > *, .header > *, .bo-topbar > * { position: relative; z-index: 1; }


/* ═══ LES CHEVRONS REVIENNENT, ET LE CADENAS REMONTE ═══
   Ils disent « MONTE vers le cadenas ». Sans eux, rien n'indique le geste a celles qui ne le
   connaissent pas — et le cadenas seul, plus bas, ne se lisait plus comme une cible a
   rejoindre.

   Ils s'EFFACENT au blocage sans etre RETIRES (`visibility`, pas `display`) : la colonne est
   ancree par le bas, et les enlever de la mise en page la raccourcissait — le cadenas tombait
   alors de toute leur hauteur, au moment precis ou il doit etre immobile.

   `bottom: calc(100% + 12px)` remonte l'ensemble : le cadenas etait trop pres de la pastille
   pour qu'on ait envie d'aller le chercher. */
@keyframes boChevronsMontent {
  0%, 100% { transform: translateY(3px); opacity: .35; }
  50%      { transform: translateY(-3px); opacity: 1; }
}
#boMicro .boMicroVerrou { bottom: calc(100% + 12px); gap: 5px; }
#boMicro .boMicroFleches {
  display: block; color: #fff; opacity: .95;
  filter: drop-shadow(0 1px 3px rgba(18, 12, 26, .5));
}
#boMicro .boMicroFleches svg {
  width: 15px !important; height: 19px !important;
  animation: boChevronsMontent 1.4s ease-in-out infinite;
}
#boMicro.verrouille .boMicroFleches { visibility: hidden; }

/* ═══ PLUS D'ASCENSEUR DANS LES ZONES DE SAISIE ═══
   Le champ multiligne affichait la barre de defilement du navigateur — un rail gris avec deux
   fleches, colle contre le bord droit de la pilule. Ce n'est pas un element de l'application :
   c'est le navigateur qui s'invite dans une barre dessinee au pixel. Le champ grandit deja avec
   le texte jusqu'a six lignes, et au-dela on fait defiler au doigt comme partout ailleurs. */
#boChatInput {
  scrollbar-width: none;
  -ms-overflow-style: none;
}
#boChatInput::-webkit-scrollbar { display: none; width: 0; height: 0; }

/* ═══════════════════════════════════════════════════════════════════════════════════════════
   PAYER — la pastille sous le prix, et la feuille qui donne le numéro.
   ═══════════════════════════════════════════════════════════════════════════════════════════

   JM, le 17/09 : « un truc avec icône d'argent un peu comme livraison, juste en bas du prix,
   de façon verticale, bien designé et moderne ».

   LA PASTILLE PREND LA COULEUR DE LA BOUTIQUE, en fond très pâle. Pleine, elle rivaliserait
   avec le prix qu'elle accompagne ; grise, elle passerait pour un état et non pour un geste.
   Elle est sous le prix et alignée à droite comme lui : la colonne de droite se lit d'un trait,
   le montant puis ce qu'on en fait. */
.bo-payer {
  display: inline-flex; align-items: center; gap: 5px;
  margin-top: 6px;
  padding: 6px 11px;
  border: none; border-radius: 999px;
  background: color-mix(in srgb, var(--primary) 12%, transparent);
  color: var(--primary);
  font-size: 12px; font-weight: 800; font-family: inherit;
  cursor: pointer;
  transition: transform .12s ease, background .15s ease;
}
.bo-payer:active { transform: scale(.94); background: color-mix(in srgb, var(--primary) 20%, transparent); }
.bo-payer-ico { display: inline-flex; }
.bo-payer-ico svg { width: 14px; height: 14px; }

/* LA FEUILLE EST ANCRÉE SUR LE CADRE, PAS SUR LA FENÊTRE. Sur un écran large, un voile en
   `position: fixed` couvre tout le bureau et la feuille se pose à côté du téléphone, comme
   une autre application. Même confinement que `.zoom-overlay`. */
.bo-pay-voile {
  position: fixed; top: 0; bottom: 0; left: var(--bo-gauche);
  width: min(var(--bo-cadre), 100vw);
  z-index: 100001;
  display: none; align-items: flex-end;
  background: rgba(20, 12, 32, 0); backdrop-filter: blur(0px);
  transition: background .18s ease;
}
.bo-pay-voile.ouvert { background: rgba(20, 12, 32, .45); }

.bo-pay-sheet {
  width: 100%;
  background: var(--surface);
  border-radius: 22px 22px 0 0;
  padding: 10px 16px calc(20px + env(safe-area-inset-bottom, 0px));
  box-shadow: 0 -8px 34px rgba(24, 6, 46, .26);
  transform: translateY(100%);
  transition: transform .22s cubic-bezier(.22, .8, .3, 1);
  max-height: 88vh; overflow-y: auto;
}
.bo-pay-voile.ouvert .bo-pay-sheet { transform: translateY(0); }

/* La poignée ne sert à rien de fonctionnel : elle dit que la chose vient d'en bas et y
   retourne, ce qu'aucun texte ne dirait aussi vite. */
.bo-pay-poignee {
  width: 38px; height: 4px; border-radius: 2px;
  background: var(--border); margin: 2px auto 12px;
}

.bo-pay-tete { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; }
.bo-pay-titre { font-size: 16px; font-weight: 800; color: var(--text); }
.bo-pay-ref { font-size: 11.5px; color: var(--text-sec); margin-top: 2px; font-weight: 700; }
.bo-pay-fermer {
  border: none; background: var(--bg); color: var(--text-sec);
  width: 30px; height: 30px; border-radius: 15px; cursor: pointer;
  display: flex; align-items: center; justify-content: center; flex: none;
}
.bo-pay-fermer svg { width: 15px; height: 15px; }

/* LE MONTANT ET SA DEVISE SUR UNE SEULE LIGNE, alignés sur la même base — la devise empilée
   sous le nombre en faisait deux informations au lieu d'une somme. */
.bo-pay-montant {
  display: flex; align-items: baseline; gap: 5px;
  margin: 14px 0 4px;
}
.bo-pay-somme { font-size: 28px; font-weight: 900; color: var(--primary); letter-spacing: .2px; }
.bo-pay-devise { font-size: 13px; font-weight: 700; color: var(--text-sec); }

.bo-pay-liste { margin-top: 12px; display: flex; flex-direction: column; gap: 8px; }
.bo-pay-moyen {
  display: flex; align-items: center; gap: 11px;
  border: 1px solid var(--border); border-radius: 14px; padding: 11px 12px;
}
.bo-pay-badge {
  width: 34px; height: 34px; border-radius: 11px; flex: none;
  display: flex; align-items: center; justify-content: center;
  font-size: 15px; font-weight: 900;
}
/* Le logo garde son propre fond : le cadre est blanc, pas la couleur de la marque. Meme
   dessin que le badge de MoyensDePaiement.tsx dans Manage. */
.bo-pay-badge-logo { background: #FFFFFF; overflow: hidden; }
.bo-pay-badge-logo img { width: 34px; height: 34px; object-fit: contain; display: block; }
.bo-pay-infos { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.bo-pay-nom { font-size: 12px; font-weight: 700; color: var(--text-sec); }
/* LE NUMÉRO EST CE QU'ON EST VENU CHERCHER : il est plus gros que le nom du service, en
   chiffres à chasse fixe pour que les groupes de deux s'alignent d'une ligne à l'autre. */
.bo-pay-num {
  font-size: 16px; font-weight: 800; color: var(--text);
  font-variant-numeric: tabular-nums; letter-spacing: .3px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.bo-pay-copier {
  border: none; border-radius: 999px; flex: none; cursor: pointer;
  padding: 7px 12px; font-family: inherit;
  background: color-mix(in srgb, var(--primary) 12%, transparent);
  color: var(--primary); font-size: 11.5px; font-weight: 800;
  transition: background .15s ease, color .15s ease;
}
.bo-pay-copier.fait { background: #2E8B3C; color: #fff; }

.bo-pay-titulaire {
  display: flex; align-items: baseline; gap: 6px; margin-top: 12px;
  font-size: 12.5px; color: var(--text-sec);
}
.bo-pay-titulaire strong { color: var(--text); font-weight: 800; }

.bo-pay-apres {
  margin-top: 12px; padding: 11px 12px;
  background: var(--bg); border-radius: 12px;
  font-size: 12px; line-height: 1.45; color: var(--text-sec);
}

/* ═══════════════════════════════════════════════════════════════════════════════════════
   LA VIDÉO D'UNE PUBLICATION SE VOIT EN ENTIER, ET AU MILIEU
   ═══════════════════════════════════════════════════════════════════════════════════════
   JM : « la vidéo dans le carrousel doit être bien centrée pour qu'on puisse bien la voir ».

   ── CE QUI SE PASSAIT ────────────────────────────────────────────────────────────────
   Les PHOTOS ont fini en `contain`, centrées, sur un fond flou : c'est le réglage qui les
   montre entières (voir plus haut, « L'image ENTIÈRE, et un fond qui ne se voit pas »). La
   règle `.avec-photo video` était restée en `cover`, écrite avant ce virage et jamais
   reprise — et `cover` est bien plus violent sur une vidéo que sur une photo : l'Atelier
   sort du 1080 × 1920, c'est-à-dire du 9/16, et le cadre est en 4/5. Rogner du 9/16 pour
   remplir du 4/5 coupe QUARANTE POUR CENT de la hauteur. Un mannequin filmé en pied
   arrivait cadré sur le ventre.

   ── CE QU'ON FAIT ───────────────────────────────────────────────────────────────────
   Même réglage que la photo : `contain`, centré. La vidéo entre en entier, il reste du vide
   à gauche et à droite, et ce vide est le fond sombre du cadre — pas de flou ici. Le flou
   des photos vient de `--photo`, que seule une image peut porter ; le reproduire pour une
   vidéo demanderait de décoder le même fichier DEUX FOIS sur le téléphone d'une cliente,
   pour un bord qu'un fond sombre suffit à éteindre. */
.bo-car-slide.promo.avec-photo.avec-video {
  background: #14101A;
}
.bo-car-slide.promo.avec-photo.avec-video video {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: contain;
  object-position: center center;
  display: block;
  z-index: 1;
}
/* Pas de fond flou derrière une vidéo : `--photo` n'est pas posé sur cette diapo, et
   `::before` afficherait alors un rectangle vide par-dessus l'image. */
.bo-car-slide.promo.avec-photo.avec-video::before { content: none; }

/* ═══════════════════════════════════════════════════════════════════════════════════════
   UN RAYON SE VOIT COMMENCER ET FINIR (JM, le 9 octobre 2026)
   ═══════════════════════════════════════════════════════════════════════════════════════
   « Mets une petite ligne dans le design qui continue avec le terme Accessoires pour délimiter
   la partie accessoire, ligne en haut et en bas. »

   En défilant, « Accessoires » n'était qu'un mot de plus posé au-dessus de deux photos : rien ne
   disait où le rayon précédent s'arrêtait.

   LE TRAIT EST SOUS LE NOM, PAS À CÔTÉ (JM, le 9 octobre 2026 : « le trait n'est pas en haut à
   côté du texte Réseaux mais en bas, le trait est en bas »). Posé en élément de la rangée, il
   se rangeait sur la ligne du titre et faisait un soulignement de côté. En absolu, au bas de la
   tête, il souligne le rayon entier et sépare vraiment ce qui précède de ce qui suit.
   Il s'efface vers la droite : la couleur de la boutique, diluée — un repère, jamais un cadre. */
.bo-rangee-tete { position: relative; }
/* Le trait a SA propre teinte, plus franche que le liseré des boutons : à 20 % il était là,
   mesuré, et invisible à l'œil sur le fond lavande. À 38 % sur 2,5 px, il se lit sans peser. */
.bo-rangee-tete::after {
  content: ''; position: absolute; left: 16px; right: 16px; bottom: 1px; height: 2.5px;
  border-radius: 999px;
  background: linear-gradient(90deg, var(--accent-trait, rgba(107,33,168,.38)) 0%, transparent 92%);
}
.bo-rangee { position: relative; }
.bo-rangee + .bo-rangee { margin-top: 26px; }

/* ═══════════════════════════════════════════════════════════════════════════════════════
   LE TEXTE DE PROMOTION DEVIENT UN LIEN, ET IL EST MOINS LARGE (JM, le 9 octobre 2026)
   ═══════════════════════════════════════════════════════════════════════════════════════
   « Il faut aussi que le texte de la promo on diminue sa largeur horizontale, et aussi il est
   comme les liens dans le carrousel : il donne sur la page promotion. »

   Il prenait toute la place restante sur la ligne du titre et finissait en « … » au milieu
   d'une phrase. Il est maintenant borné à 46 % de la tête, et c'est un lien : la couleur de la
   boutique, du gras, un chevron. On voit qu'il mène quelque part. */
.bo-rangee .accordion-promo-text {
  max-width: 46%;
  display: inline-flex; align-items: center; gap: 4px;
  color: var(--accent, #6B21A8); font-weight: 700; text-decoration: none;
  padding: 3px 0;
}
a.accordion-promo-text::after {
  content: ''; width: 6px; height: 6px; flex: none;
  border-right: 2px solid currentColor; border-top: 2px solid currentColor;
  transform: rotate(45deg); margin-left: 1px;
}
a.accordion-promo-text:active { opacity: .6; }

/* ═══════════════════════════════════════════════════════════════════════════════════════
   LA PAGE PROMOTION — voir promotion.html
   ═══════════════════════════════════════════════════════════════════════════════════════
   Bâtie comme les écrans de la page Plus : un titre souligné d'une barre courte, puis des
   cartes blanches posées sur le fond, chacune avec son étiquette, sa phrase, les rayons
   concernés et un aperçu des articles. */
.bo-promos { padding: 14px 16px 96px; }
.bo-promos-titre {
  font-size: 22px; font-weight: 800; letter-spacing: -.5px; color: var(--text, #1A1A2E); margin: 0 0 6px;
}
.bo-promos-titre::after {
  content: ''; display: block; width: 34px; height: 3px; margin: 9px 0 18px;
  border-radius: 999px; background: var(--accent, #6B21A8);
}
.bo-promos-vide { text-align: center; padding: 48px 20px; color: var(--text-sec, #6B7280); }
.bo-promos-vide-ic { color: var(--accent, #6B21A8); opacity: .4; margin-bottom: 12px; }
.bo-promos-retour {
  display: inline-block; margin-top: 14px; font-size: 14px; font-weight: 700;
  color: var(--accent, #6B21A8); text-decoration: none;
}

/* ═══════════════════════════════════════════════════════════════════════════════════════
   LA ZONE DE SAISIE D'OUFY, CELLE DE MANAGE (JM, le 9 octobre 2026)
   ═══════════════════════════════════════════════════════════════════════════════════════
   « Les modifications effectuées sur les zones de texte, que ce soit dans la messagerie dans
   Manage ou dans la pastille, s'appliquent ici. La seule différence entre ces deux assistants,
   c'est qu'ici il fait les trucs pour le shop. Applique-moi tout ce que je t'ai dit dans la
   zone de texte, le bouton, tout doit être conforme. »

   La zone de la vitrine était une PILULE : sourire, texte, trombone et appareil sur une seule
   ligne, le micro posé dehors, à côté. Celle de Manage est une CARTE : le texte sur toute la
   largeur, puis une rangée d'outils en dessous, l'envoi au bout à droite. Deux dessins pour le
   même geste, dans deux applications de la même maison.

   Les mesures viennent de manageboutique/src/components/ZoneSaisie.tsx, au pixel : cadre de 22
   de rayon bordé de #E4DEEE, texte en 15/21, outils ronds de 34, envoi et micro de 36, écart de
   6 entre eux et 8 au-dessus. Le seul écart assumé : pas de pilule « Assistant » ici — Oufy EST
   l'assistante, il n'a personne à qui passer la main.

   LE RANGEMENT SE FAIT AU CSS, pas en refaisant le HTML : les boutons gardent leur code, leurs
   identifiants et leurs écouteurs, `flex-wrap` met le texte sur sa ligne et `order` donne la
   suite. Réécrire la barre aurait rouvert le micro à verrou, qui marche. */
#boChatFoot {
  padding: 6px 10px 10px !important;
  background: #fff; border-top: none;
}
/* TOUJOURS DEUX LIGNES, comme en face. La vitrine ouvrait sa barre en pilule tant que le champ
   était vide et ne la dépliait qu'à la première lettre (règle « aDuTexte », plus haut). Manage,
   lui, montre toujours sa carte : le texte, puis les outils. Deux comportements pour la même
   zone, c'est précisément ce que JM demande d'arrêter. */
.boInputWrap, .boInputWrap.aDuTexte {
  position: relative;
  display: flex !important; flex-wrap: wrap !important; align-items: center;
  row-gap: 8px; column-gap: 6px;
  padding: 10px 12px 6px !important;
  border-radius: 22px !important;
  background: var(--surface, #fff) !important;
  min-height: 0 !important;
  border: 1px solid #E4DEEE !important;
  box-shadow: none !important;
}
.boInputWrap:focus-within {
  border-color: var(--accent, #6B21A8) !important;
  box-shadow: none !important;
}
.boInputWrap #boChatInput, .boInputWrap.aDuTexte #boChatInput {
  order: 0; flex: 1 0 100% !important; width: 100%; min-width: 0;
  min-height: 22px; max-height: 132px;
  padding: 2px 0 !important; margin: 0;
  border: 0; background: none; resize: none;
  /* 16 px et pas 15 : c'est CETTE règle qui décide (`!important`), et en dessous de seize
     l'iPhone zoome la page au toucher. Voir plus haut, « SEIZE PIXELS, PAS MOINS ». */
  font-family: inherit; font-size: 16px !important; line-height: 21px; color: var(--ink, #1A1A2E);
  scrollbar-width: none;
}
#boChatInput::-webkit-scrollbar { display: none; }
#boChatInput::placeholder { color: #8A8698; }

/* LES OUTILS SONT RONDS, 34 de côté : la même cible qu'en face, et un appui qui reste un
   disque. Le trombone d'abord, puis le sourire, comme dans Manage ; l'appareil photo ensuite,
   lui n'existe que dans la boutique. */
/* Les sélecteurs portent `.boInputWrap` : une règle plus haut range les icônes autrement
   quand la barre est vide (`:not(.aDuTexte)`), et elle gagnerait sur un `#bo...` seul. */
.boInputWrap #boJoindre, .boInputWrap:not(.aDuTexte) #boJoindre, .boInputWrap.aDuTexte #boJoindre { order: 1; }
.boInputWrap #boEmoji, .boInputWrap:not(.aDuTexte) #boEmoji, .boInputWrap.aDuTexte #boEmoji { order: 2; }
.boInputWrap #boPhoto, .boInputWrap:not(.aDuTexte) #boPhoto, .boInputWrap.aDuTexte #boPhoto { order: 3; }
.boInputWrap #boMicro, .boInputWrap.aDuTexte #boMicro { order: 8; }
.boInputWrap #boChatSend, .boInputWrap.aDuTexte #boChatSend { order: 9; }
#boJoindre, #boEmoji, #boPhoto {
  width: 34px !important; height: 34px !important; flex: none;
  border-radius: 17px !important; border: 0 !important; padding: 0 !important; margin: 0 !important;
  display: flex; align-items: center; justify-content: center;
  background: none !important; color: #3B3646 !important; cursor: pointer;
}
#boJoindre svg, #boEmoji svg, #boPhoto svg { width: 23px !important; height: 23px !important; }
#boJoindre:active, #boEmoji:active, #boPhoto:active { background: #F1EEF5 !important; }

/* LE MICRO ET L'ENVOI AU BOUT DE LA RANGÉE. `margin-left: auto` tient lieu du ressort que
   Manage écrit en <View style={{flex:1}} /> : tout ce qui suit est poussé à droite. */
#boMicro {
  order: 8; margin-left: auto !important;
  position: relative !important; right: auto !important; bottom: auto !important;
  width: 36px !important; height: 36px !important; flex: none;
  border-radius: 18px !important; border: 0 !important; padding: 0 !important;
  display: flex; align-items: center; justify-content: center;
  background: none !important; color: #3B3646 !important; box-shadow: none !important;
}
#boMicro svg { width: 23px !important; height: 23px !important; }
#boMicro:active { background: #F1EEF5 !important; }
/* LE RESSORT EST SUR LES DEUX : le micro et l'envoi se relaient au même endroit, un seul est
   visible à la fois. Posé sur le micro seulement, il disparaissait dès qu'on tapait une lettre
   et l'avion remontait se coller à l'appareil photo. */
#boChatSend {
  order: 9; flex: none; align-self: center !important;
  width: 36px !important; height: 36px !important; border-radius: 18px !important;
  border: 0 !important; padding: 0 !important;
  margin: 0 0 0 auto !important;
  display: flex; align-items: center; justify-content: center;
  background: var(--accent, #6B21A8) !important; color: #fff !important;
}
#boChatSend[style*="display:none"], #boChatSend[style*="display: none"] { display: none !important; }
#boChatSend svg { width: 16px !important; height: 16px !important; }
#boChatSend:not(:disabled):active { transform: scale(.92); }

/* Le bandeau d'enregistrement recouvre la LIGNE DU TEXTE, pas la rangée d'outils : on doit
   pouvoir abandonner ou verrouiller pendant qu'il tourne. */
#boMicroBandeau {
  position: absolute !important; left: 12px; right: 12px; top: 10px; height: 25px;
  display: flex; align-items: center; gap: 10px; background: var(--surface, #fff);
}

/* ═══════════════════════════════════════════════════════════════════════════════════════
   LA PAGE PROMOTION, DEUXIÈME PASSE (JM, le 9 octobre 2026)
   ═══════════════════════════════════════════════════════════════════════════════════════
   « Ici Robes et Tops deviennent des onglets pour trier, et les flèches à gauche et à droite
   pour pouvoir scroller. Le prix dans l'image, le nom en bouton, exactement comme dans accueil.
   Un chevron en haut pour le retour. »

   LES RAYONS SONT LES TUILES DU HAUT, et chaque rangée en est un (JM, le 9 octobre 2026 :
   « ça doit être Robes, Accessoires, Jupes, Chemises qui deviennent les catégories, et
   l’onglet Tout ne doit plus exister »). La page tenait deux tris l’un sur l’autre : les
   types en haut, les rayons dans chaque rangée. Il n’en reste qu’un, celui de l’accueil, et
   le type de promotion devient la pilule à droite du titre. */
.bo-promos { padding-top: 8px; }
.bo-promos-chevron {
  display: inline-flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; margin: 0 0 2px -8px;
  color: var(--ink, #16121F); text-decoration: none;
}
.bo-promos-chevron:active { opacity: .55; }

/* ── LE RETOUR EST UN CHEVRON À GAUCHE DU TITRE, SUR TOUTES LES PAGES ──────────────────
   JM, le 21 octobre 2026 : « supprime le bouton Continuer mes achats dans toutes les pages de
   shop, il faut mettre à la place un chevron juste à gauche de Mes commandes, comme avec
   Promotions ».

   « ← Continuer mes achats » était une pilule bordée posée SOUS l'en-tête, sur sa propre ligne :
   une ligne entière de page mangée avant le premier mot du titre, et trois libellés différents
   pour un seul geste — reculer. Le chevron de Promotion fait le même geste sans prendre de
   place, et c'est déjà celui de la connexion de Manage.

   Une seule géométrie pour tous (`.bo-promos-chevron` ci-dessus) : la règle « tout doit être
   unifié » de CLAUDE.md veut qu'en corrigeant un chevron on corrige tous ses jumeaux.
   Le `-8px` rattrape le rembourrage de la zone tactile de 40 px pour que le trait du chevron
   tombe à l'aplomb du texte de la page, pas 8 px à sa droite. */
.bo-titre-ligne { display: flex; align-items: center; gap: 2px; }
.bo-titre-ligne .bo-promos-chevron { margin: 0 0 0 -8px; flex: none; }
.bo-titre-ligne > h1,
.bo-titre-ligne > h2 { margin: 0 !important; }

/* LES PILULES DE PROMOTION, dans la tête de rangée. Une seule, c’est exactement l’accueil.
   Deux, et elles se suivent dans une bande qui glisse plutôt que de pousser le titre dehors :
   un rayon peut porter une remise ET la livraison offerte. */
.bo-promo-pilules {
  /* order 9 : la place de la promotion dans la tête de rangée de l’accueil. Sans lui, la bande
     passerait AVANT le titre, les autres enfants portant déjà un ordre explicite. */
  order: 9;
  margin-left: auto; display: flex; align-items: center; gap: 6px; min-width: 0;
  overflow-x: auto; scrollbar-width: none;
}
.bo-promo-pilules::-webkit-scrollbar { display: none; }
.bo-promo-pilules .bo-flamme { margin-left: 0 !important; margin-right: 0 !important; }
.bo-promo-pilules .accordion-promo-text { margin-left: 0 !important; flex: none; }

/* La rangée vit DANS la carte : elle n'a pas à reprendre les 16 px de marge de l'accueil, la


/* ── LA FLAMME SE COLLE AU DÉBUT DU TEXTE (JM, le 9 octobre 2026) ─────────────────────── */
/* « Le feu doit être devant Soldes, juste à côté l'un de l'autre, au début du texte. » La
   flamme ET la phrase portaient toutes deux `margin-left: auto` : le vide de la ligne se
   partageait entre elles, et la flamme flottait au milieu, à mi-chemin de rien.
   Un seul ressort, sur la flamme ; la phrase la suit immédiatement. Le `margin-right` négatif
   annule l'écart de 12 px de la rangée : les deux se touchent, comme une seule étiquette.
   Sans flamme (une livraison offerte n'en a pas), la phrase garde son propre ressort. */
.bo-flamme { margin-left: auto !important; margin-right: -8px !important; }
.bo-flamme + .accordion-promo-text,
.bo-rangee .bo-flamme + .accordion-promo-text { margin-left: 0 !important; }

/* ═══════════════════════════════════════════════════════════════════════════════════════
   LE TRAIT DU RAYON PREND SA COULEUR, ET SUIT LA BARRE (JM, le 9 octobre 2026)
   ═══════════════════════════════════════════════════════════════════════════════════════
   « Il faut que le trait qui est en bas de Chemises soit synchro avec l'icône violet en haut
   dans le sticky header ; le trait peut devenir un tout petit peu violet. Amuse-toi, fais du
   waouh, mais pas trop exagéré. »

   Chaque rayon a déjà sa teinte, celle de sa pastille (.bo-rangee-ico.t0 à t5) : le trait la
   reprend, et l'on voit d'un coup que le titre, la pastille du haut et le trait parlent du
   MÊME rayon. `:has()` lit la teinte sur l'icône plutôt que de la recopier en JS — et là où il
   n'est pas compris, le trait garde la couleur de la boutique, qui était déjà la sienne.

   LE WAOUH TIENT EN TROIS CHOSES, toutes petites : le trait du rayon qu'on lit monte à 3 px,
   gagne une lueur de sa propre couleur, et se dessine de gauche à droite en un tiers de
   seconde. Les autres restent en sourdine. Rien ne clignote, rien ne tourne. */
.bo-rangee-tete:has(.bo-rangee-ico.t0) { --trait: #C2410C; }
.bo-rangee-tete:has(.bo-rangee-ico.t1) { --trait: #1D4ED8; }
.bo-rangee-tete:has(.bo-rangee-ico.t2) { --trait: #BE185D; }
.bo-rangee-tete:has(.bo-rangee-ico.t3) { --trait: #A16207; }
.bo-rangee-tete:has(.bo-rangee-ico.t4) { --trait: #15803D; }
.bo-rangee-tete:has(.bo-rangee-ico.t5) { --trait: #6D28D9; }

/* LE TRAIT SE VOIT, ET IL SE DESSINE DE GAUCHE À DROITE (JM, le 9 octobre 2026 : « il faut
   qu'on sente la ligne qui est en bas de Robes devenir plus visible, ou même devenir plus
   visible en allant de gauche à droite dans un sens, ça serait plus joli »).

   DEUX COUCHES SUPERPOSÉES dans le même fond :
     · dessous, le trait de base, la couleur du rayon qui s'efface vers la droite ;
     · dessus, un court segment PLEIN qui traverse une fois, de gauche à droite, quand le rayon
       devient celui qu'on lit.
   Le segment est une image de fond de 36 % de large qu'on déplace : aucun élément de plus, rien
   à nettoyer, et l'animation ne touche que la position du fond, que le navigateur sait animer
   sans refaire la mise en page.

   Le trait de base monte de 62 % à 78 % d'encre et de 2,5 à 3 px : il était mesurable et pas
   lisible, ce qui revient au même qu'absent. */
.bo-rangee-tete::after {
  height: 3px;
  background:
    linear-gradient(90deg, transparent 0%, var(--trait, var(--accent, #6B21A8)) 48%, transparent 100%) -40% 0 / 36% 100% no-repeat,
    linear-gradient(90deg, var(--trait-encre, var(--accent-trait, rgba(107,33,168,.5))) 0%, transparent 94%);
  opacity: .78;
  transform-origin: left center;
  transition: height .25s ease, opacity .25s ease, box-shadow .25s ease;
}
@supports (color: color-mix(in srgb, red 50%, blue)) {
  .bo-rangee-tete { --trait-encre: color-mix(in srgb, var(--trait, var(--accent, #6B21A8)) 78%, transparent); }
}
/* Le rayon qu'on lit : un peu plus épais, une lueur de sa propre couleur, et la lumière qui
   passe UNE FOIS. Une onde qui tournerait en boucle ferait clignoter la page entière. */
.bo-rangee.rayon-courant .bo-rangee-tete::after,
.bo-promo-rangee.visee .bo-rangee-tete::after {
  /* ALLUMÉ, MAIS PAS CRIANT (JM, le 9 octobre 2026 : « le trait en bas doit devenir plus fin
     ou moins lumineux, juste un peu — ce que tu as fait est cool, mais touche un peu »). Il
     passait à 3,5 px pleins avec un halo de 10 px : sur « Tops », le trait pesait plus que le
     mot. Il garde l’épaisseur du repos, et c’est l’encre qui dit lequel on lit. */
  height: 3px; opacity: .94;
  box-shadow: 0 1px 6px -4px var(--trait, var(--accent, #6B21A8));
  animation: boTraitPousse .32s cubic-bezier(.32,.72,.28,1) both,
             boTraitOnde .95s .18s cubic-bezier(.4,0,.2,1) 1;
}
@keyframes boTraitPousse { from { transform: scaleX(.12); } to { transform: scaleX(1); } }
@keyframes boTraitOnde {
  from { background-position: -40% 0, 0 0; }
  to { background-position: 140% 0, 0 0; }
}
@media (prefers-reduced-motion: reduce) {
  .bo-rangee.rayon-courant .bo-rangee-tete::after,
  .bo-promo-rangee.visee .bo-rangee-tete::after { animation: none; }
}

/* ═══════════════════════════════════════════════════════════════════════════════════════
   PLUS DE BANDE BLANCHE AU BOUT DE LA RANGÉE (JM, le 9 octobre 2026)
   ═══════════════════════════════════════════════════════════════════════════════════════
   « On a l'impression que l'image est croppée par une bande verticale blanche ; il faut la
   retirer pour laisser l'image s'épanouir, sans pour autant tout gâcher. »

   La piste gardait 16 px de marge à droite : la carte suivante, à moitié visible, était donc
   coupée par un couloir de fond clair au lieu de l'être par le bord du téléphone. L'œil lisait
   un recadrage raté, pas une suite à faire glisser.
   La marge passe à zéro, et un cale invisible la rend EN FIN DE COURSE : la dernière carte ne
   se colle pas au bord quand on a tout parcouru. Le débord reste — c'est lui qui dit qu'il y a
   encore des articles — mais il se fait maintenant contre le bord de l'écran. */
.bo-rangee .product-grid {
  padding-right: 0 !important;
  scroll-padding-left: 16px;
}
.bo-rangee .product-grid::after { content: ''; flex: 0 0 16px; align-self: stretch; }


/* ═══════════════════════════════════════════════════════════════════════════════════════
   LA PAGE PROMOTION A L'ARCHITECTURE DE L'ACCUEIL (JM, le 9 octobre 2026)
   ═══════════════════════════════════════════════════════════════════════════════════════
   « Les onglets Tout, Remise, Livraison, Nouveauté doivent devenir comme les onglets du sticky
   header, Robes et les autres. Et les trucs du genre Nouveauté deviennent ce qui est au-dessus.
   Fais ce que tu peux pour qu'on ait une unicité. »

   Il n'y a donc presque RIEN ici : la page porte les classes de l'accueil — `bo-raccourcis`
   pour la barre de tuiles, `bo-rangee` pour chaque promotion — et hérite de tous leurs styles.
   Ce qui suit ne fait qu'ajuster ce qui diffère vraiment : la largeur des cartes, un peu plus
   petite parce qu'une promotion en montre plus d'un coup, et la marque de celle qu'on lit. */
/* LE CHEVRON SUR LA MÊME LIGNE QUE LE TITRE (JM, le 9 octobre 2026 : « le chevron de retour
   est à gauche du texte Promotions, et pas en haut »). Il tenait sa propre ligne et volait
   40 px de hauteur à une page qui commence par six tuiles — or il se lit comme le début du
   titre, pas comme un étage au-dessus. Le soulignement du titre reste sous le mot seul :
   il appartient au titre, pas à la ligne. */
.bo-promos { padding: 8px 16px 2px; display: flex; align-items: center; gap: 2px; }
.bo-promos .bo-promos-chevron { margin: 0 0 0 -8px; flex: none; }
.bo-promos .bo-promos-titre { margin: 0; }
.bo-promos .bo-promos-titre::after { margin: 7px 0 0; }
.bo-promo-rangee .product-card { width: 40vw !important; max-width: 168px !important; }
.bo-promo-rangee .product-card[hidden] { display: none !important; }
.bo-promo-rangee .accordion-body { position: relative; }

/* LA PROMOTION QU'ON LIT (JM : « c'est quand on clique dans une carte qu'elles apparaissent,
   pour montrer que c'est elle qui est sélectionnée »). Sur l'accueil, le rayon courant se dit
   par sa tuile allumée et son trait ; ici s'ajoute un fond très pâle et un repère sur le bord
   gauche — une promotion est une offre, pas un rayon, et il faut pouvoir la désigner.
   Le repère est posé en `box-shadow`, donc il ne pousse rien : la rangée ne bouge pas d'un
   pixel en s'allumant. */
.bo-promo-rangee {
  margin: 0 0 20px !important;
  padding: 10px 0 4px;
  border-radius: 18px;
  transition: background .18s ease, box-shadow .18s ease;
}
.bo-promo-rangee.visee {
  background: var(--accent-tint, #F4F1F8);
  box-shadow: inset 3px 0 0 var(--accent, #6B21A8);
}


/* ══════════════════════════════════════════════════════════════════════════════════════════
   CHANGER DE PAGE SANS QU'ON LE SENTE (JM, le 13 octobre 2026) — voir js/natif.js
   ══════════════════════════════════════════════════════════════════════════════════════════
   « À chaque truc cliqué on recharge la page, on sent que ça clique. »

   Le navigateur garde l'ancienne page à l'écran jusqu'à la première image de la nouvelle, puis
   passe de l'une à l'autre. La barre du bas, l'en-tête et la pastille d'Oufy portent un nom :
   identiques des deux côtés, ils ne bougent pas pendant que le reste change, comme la barre
   d'onglets d'une application. Le sens (`avance`, `retour`, `onglet`) est posé par natif.js. */
@view-transition { navigation: auto; }

.footer-nav { view-transition-name: bo-pied; }
.bo-head { view-transition-name: bo-entete; }
#boChatFab { view-transition-name: bo-oufy; }

::view-transition-old(root),
::view-transition-new(root) { animation-duration: .18s; }
::view-transition-group(bo-pied),
::view-transition-group(bo-entete),
::view-transition-group(bo-oufy) { animation-duration: .18s; z-index: 3; }

/* OUVRIR UN ARTICLE POUSSE LA PAGE PAR LA DROITE, en revenir la retire : le geste d'un
   téléphone. L'ancienne page recule d'un quart et s'éteint un peu, elle ne sort pas : c'est
   ce léger décalage qui donne deux plans au lieu d'un fondu plat. */
html:active-view-transition-type(avance)::view-transition-old(root) {
  animation: boPageRecule .32s cubic-bezier(.32,.72,.24,1) both; mix-blend-mode: normal;
}
html:active-view-transition-type(avance)::view-transition-new(root) {
  animation: boPageEntre .32s cubic-bezier(.32,.72,.24,1) both; mix-blend-mode: normal;
  box-shadow: -12px 0 28px -18px rgba(22,18,31,.45);
}
html:active-view-transition-type(retour)::view-transition-old(root) {
  animation: boPageSort .3s cubic-bezier(.32,.72,.24,1) both; mix-blend-mode: normal; z-index: 1;
  box-shadow: -12px 0 28px -18px rgba(22,18,31,.45);
}
html:active-view-transition-type(retour)::view-transition-new(root) {
  animation: boPageRevient .3s cubic-bezier(.32,.72,.24,1) both; mix-blend-mode: normal; z-index: 0;
}
@keyframes boPageEntre { from { transform: translateX(100%); } to { transform: none; } }
@keyframes boPageRecule { from { transform: none; opacity: 1; } to { transform: translateX(-24%); opacity: .55; } }
@keyframes boPageSort { from { transform: none; } to { transform: translateX(100%); } }
@keyframes boPageRevient { from { transform: translateX(-24%); opacity: .55; } to { transform: none; opacity: 1; } }

@media (prefers-reduced-motion: reduce) {
  @view-transition { navigation: none; }
}

/* LE DOIGT. Le voile gris que le navigateur pose sur tout ce qu'on touche est la signature
   d'une page web : chaque bouton a déjà son propre état pressé. */
html { -webkit-tap-highlight-color: transparent; }
a, button { touch-action: manipulation; }
.footer-nav a, .bo-raccourci, .product-card, .bo-promos-chevron { -webkit-touch-callout: none; user-select: none; -webkit-user-select: none; }

/* ══════════════════════════════════════════════════════════════════════════════════════════
   L'ATTENTE A LA FORME DE CE QUI ARRIVE (JM, le 13 octobre 2026)
   ══════════════════════════════════════════════════════════════════════════════════════════
   Une roue qui tourne au milieu d'une page vide, puis la page qui apparaît d'un coup : c'est
   le moment où « on sent que ça recharge ». Des blocs gris à la place des tuiles et des cartes
   tiennent la mise en page ; quand les articles arrivent, rien ne saute. */
.bo-sq { padding: 12px 16px 0; overflow: hidden; }
.bo-sq i {
  display: block; border-radius: 14px;
  background: linear-gradient(100deg, #EEECF2 32%, #F8F7FA 50%, #EEECF2 68%) 0 0 / 300% 100%;
  animation: boSqLueur 1.25s linear infinite;
}
.bo-sq-tuiles { display: flex; gap: 16px; margin-bottom: 20px; }
.bo-sq-tuiles i { flex: none; width: 46px; height: 46px; border-radius: 15px; }
.bo-sq i.bo-sq-titre { width: 36%; height: 16px; border-radius: 8px; margin-bottom: 12px; }
.bo-sq-cartes { display: flex; gap: 12px; margin-bottom: 24px; }
.bo-sq-cartes i { flex: none; width: 40vw; max-width: 168px; aspect-ratio: 3 / 4; border-radius: 16px; }
.bo-sq i.bo-sq-affiche { height: 150px; border-radius: 20px; margin-bottom: 18px; }
.bo-sq i.bo-sq-photo { margin: -12px -16px 16px; aspect-ratio: 1 / 1; border-radius: 0; }
.bo-sq i.bo-sq-ligne { height: 14px; border-radius: 7px; margin-bottom: 12px; }
.bo-sq i.bo-sq-ligne.courte { width: 46%; }
.bo-sq i.bo-sq-carte { height: 84px; border-radius: 18px; margin-bottom: 12px; }
@keyframes boSqLueur { to { background-position: -300% 0; } }
@media (prefers-reduced-motion: reduce) { .bo-sq i { animation: none; } }

/* ══════════════════════════════════════════════════════════════════════════════════════════
   LA PAGE PROMOTION : UNE TUILE PAR PROMOTION, SES RAYONS DEDANS (JM, le 13 octobre 2026)
   ══════════════════════════════════════════════════════════════════════════════════════════
   « Ici on doit avoir Nouveauté, et quand on clique sur Nouveauté on garde ce qu'on a
   actuellement : les catégories Robes, Jupes qui font partie de la promotion Nouveauté. Donc
   dans une icône Nouveauté, qui est une promotion, on trouve les catégories qu'on a ajoutées
   dedans. »

   Deux étages, donc, et les mêmes classes que l'accueil aux deux : la barre de tuiles et les
   rangées. En haut une tuile par promotion, avec l'icône de son type. Dedans, ce que la page
   montrait déjà : une tuile et une rangée par rayon. */
.bo-promos-phrase {
  margin: 2px 16px 6px; font-size: 14px; font-weight: 600; line-height: 1.35;
  color: var(--text-sec, #6B7280);
}
.bo-promos-phrase[hidden] { display: none; }
/* La rangée d'une promotion s'ouvre : toute sa tête répond au doigt, pas seulement la phrase. */
.bo-promo-rangee[data-ouvre] .bo-rangee-tete { cursor: pointer; }
.bo-promo-rangee[data-ouvre] .bo-rangee-tete:active { opacity: .6; }

/* ══════════════════════════════════════════════════════════════════════════════════════════
   L'ATTENTE DE L'ARRIVÉE : OUFY TIRE LA JAUGE (JM, le 13 octobre 2026) — voir js/voile.js
   ══════════════════════════════════════════════════════════════════════════════════════════
   Les mesures de la page de chargement de Manage (`manageboutique/app/+html.tsx`), une pour
   une : `--p` est ce qui est rempli, la piste et Oufy le lisent tous les deux, il est donc
   toujours au bout du plein, du côté encore vide. La course s'arrête 38 px avant le bord droit,
   la place d'Oufy arrivé au bout. */
#boVoile {
  position: fixed; inset: 0; z-index: 99999; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 18px; background: #FFFFFF;
  transition: opacity .35s ease;
}
#boVoile.parti { opacity: 0; pointer-events: none; }
#boVoile img { width: min(40vw, 156px); height: auto; display: block; }
#boVoileJauge { position: relative; width: min(64vw, 250px); height: 46px; --p: 0%; }
#boVoileCourse { position: absolute; left: 0; right: 38px; top: 0; bottom: 0; }
#boVoilePiste {
  position: absolute; left: 0; right: 0; top: 21px; height: 8px; border-radius: 999px;
  background: #EDE4F7; overflow: hidden;
}
#boVoilePiste span {
  display: block; width: var(--p); height: 100%; border-radius: 999px;
  background: linear-gradient(90deg, #6B21A8, #16A34A);
}
/* Penché en arrière, et qui donne des à-coups : c'est ce qui fait lire « il tire ». */
#boVoileOufy {
  position: absolute; top: 0; left: calc(var(--p) - 9px); width: 46px; height: 46px;
  transform-origin: 50% 92%; animation: boVoileTire .46s ease-in-out infinite alternate;
}
#boVoileOufy svg { display: block; width: 100%; height: 100%; }
@keyframes boVoileTire { from { transform: rotate(8deg); } to { transform: rotate(15deg) translateX(2px); } }
@media (prefers-reduced-motion: reduce) { #boVoileOufy { animation: none; transform: rotate(10deg); } }

/* ══════════════════════════════════════════════════════════════════════════════════════
   LA FICHE PRODUIT SE RELIT (JM, le 21 octobre 2026).
   ══════════════════════════════════════════════════════════════════════════════════════

   « Change le style du texte Taille, Couleur, Description, en stock, Style Istanbul ; change
   le style, la police, le texte, tout est nase, même la disposition de catégorie est nase. »

   CE QUI N'ALLAIT PAS, mesuré sur la fiche avant d'écrire une ligne :

     • LES INTITULÉS faisaient 11 px, en capitales, espacés de 0,8 px, en gris clair. À cette
       taille les capitales espacées se déchiffrent au lieu de se lire, et le gris les met au
       même rang que le texte secondaire alors qu'ils DÉCOUPENT la fiche. Ils passent en
       13,5 px, bas de casse, en couleur d'encre : un titre de section, pas une étiquette.

     • LE STOCK était une ligne de texte nue, sans forme : « 17 en stock » se lisait comme une
       note de bas de page tombée sous le prix. Il devient une pastille à point, de la même
       famille que les autres pastilles de la boutique.

       MAIS PAS UN FEU TRICOLORE. La règle « ── Stock ── », plus haut dans ce fichier, a déjà
       tranché : « vert / orange / rouge, c'était un feu tricolore posé au milieu d'une
       vitrine ; une disponibilité normale ne mérite pas de couleur ». Elle tient toujours, et
       ses `!important` disent qu'on y tenait. On ne change donc QUE la forme : le gris reste
       le gris, le stock faible garde l'accent de la boutique — la seule information qui doit
       pousser à agir — et une rupture reste une absence, en gris pâle. Le fond doit reprendre
       un `!important` : cette règle-là en posait un sur `background`.

     • LA DESCRIPTION était du même gris que les intitulés : la phrase de la gérante, qui
       donne envie, s'effaçait. Elle reprend 80 % de l'encre et une interligne plus large.

     • LES RAYONS portaient le gabarit des tailles — même taille de pastille, même graisse —
       posés juste au-dessus d'elles. Deux rangées de pastilles qui se ressemblent, dont l'une
       se touche et l'autre non. Ils rapetissent et prennent une teinte de la couleur de la
       boutique : une étiquette, plus un bouton. */

.product-detail .option-label {
  font-size: 13.5px;
  font-weight: 700;
  letter-spacing: -.1px;
  text-transform: none;
  color: var(--ink, #16121F);
  margin-bottom: 9px;
}

/* Un peu plus d'air entre les sections : elles portent un vrai titre maintenant. */
.product-detail .option-group,
.product-detail .bo-desc { margin-top: 18px; }

/* ── LE STOCK : UNE PASTILLE À POINT ───────────────────────────────────────────────── */
/* `display: inline-flex` et non `block` : la pastille épouse son texte. En bloc, le fond
   tiré sur toute la largeur redevient le bandeau qu'on veut éviter. */
.product-detail .stock {
  display: inline-flex; align-items: center; gap: 7px;
  margin-top: 10px; padding: 5px 12px 5px 10px;
  border-radius: 999px;
  font-size: 12.5px; font-weight: 700; letter-spacing: -.1px;
  background: color-mix(in oklab, var(--muted, #6E6880) 11%, var(--bg, #FAFAFA)) !important;
}
.product-detail .stock::before {
  content: ''; width: 7px; height: 7px; border-radius: 50%; flex: none;
  background: var(--muted, #6E6880);
}
/* Plus que trois : l'accent de la boutique, la seule disponibilité qui pousse à agir.
   L'ancien fond jaune vif à texte rouge criait plus fort que le prix, pour une information
   qui n'empêche pas d'acheter. */
.product-detail .stock.urgent {
  padding: 5px 12px 5px 10px;
  border-radius: 999px;
  font-weight: 800;
  background: color-mix(in oklab, var(--accent, #6B21A8) 10%, var(--bg, #FAFAFA)) !important;
}
.product-detail .stock.urgent::before { background: var(--accent, #6B21A8); }
/* Plus rien, ou sur commande : une absence, en gris pâle. */
.product-detail .stock.out {
  background: color-mix(in oklab, #A5A0B0 12%, var(--bg, #FAFAFA)) !important;
}
.product-detail .stock.out::before { background: #A5A0B0; }

/* ── LA DESCRIPTION : LA PHRASE DE LA GÉRANTE SE LIT ───────────────────────────────── */
.product-detail .bo-desc-txt {
  font-size: 14.5px;
  line-height: 1.68;
  color: color-mix(in oklab, var(--ink, #16121F) 80%, var(--bg, #FAFAFA));
}

/* ── LES RAYONS : UNE ÉTIQUETTE, PAS UN BOUTON ─────────────────────────────────────── */
/* Plus petits et plus clairs que les tailles juste en dessous : on voit du premier coup d'œil
   lesquels se touchent. La teinte suit la couleur de la boutique, comme le reste de la page. */
.product-detail .bo-rayon-pastille {
  padding: 5px 11px;
  border-radius: 8px;
  font-size: 12.5px;
  font-weight: 600;
  letter-spacing: -.1px;
  background: color-mix(in oklab, var(--primary, #6B21A8) 8%, var(--bg, #FAFAFA));
  color: color-mix(in oklab, var(--primary, #6B21A8) 62%, var(--ink, #16121F));
}

/* La hauteur d'UNE ligne de pastilles, recalculée : les rayons ont rapetissé, la ligne aussi.
   Sans ça, la fiche gardait 38 px de vide sous une seule rangée d'étiquettes de 28. */
.product-detail .option-group:has(.bo-rayon-pastille) .option-list { max-height: 29px; }
.product-detail .option-group:has(.bo-rayon-pastille).ouvert .option-list { max-height: 400px; }

/* ══════════════════════════════════════════════════════════════════════════════════════
   LE RÂTELIER ET LE PIED EN VERRE DÉPOLI (JM, le 21 octobre 2026).
   ══════════════════════════════════════════════════════════════════════════════════════

   « Essaie de le faire lui et le footer un peu transparent comme le header du site
   boutiqueouf.com, juste un peu, comme la mise à jour d'iOS 27 pour WhatsApp. »

   LA RECETTE EST CELLE DU SITE, à la lettre : `rgba(255,255,255,.86)` et
   `saturate(180%) blur(14px)` — `siteboutique/css/style.css`, règle `.entete`. Les deux
   barres de la vitrine la reprennent, à deux détails près : un flou plus large (18 px), parce
   qu'ici ce sont des PHOTOS d'articles qui passent dessous et non du texte sur fond clair, et
   la saturation qui ravive les couleurs lavées par le flou.

   POURQUOI LE RÂTELIER PEUT ENFIN S'OUVRIR. Son fond était opaque, et c'était motivé :
   « sinon les articles se lisent par transparence à travers les rayons ». Un simple
   `rgba()` aurait ramené ce défaut. `backdrop-filter` ne fait pas qu'éclaircir : il FLOUTE ce
   qui passe dessous, donc plus aucune forme d'article ne reste lisible sous les noms de
   rayons. La raison du fond opaque tombe avec le flou qui la remplace.

   `-webkit-backdrop-filter` EST OBLIGATOIRE : la WebView de l'iPhone ne connaît que la
   version préfixée, et sans elle l'IPA rendrait deux barres à demi transparentes, sans flou —
   soit exactement le défaut qu'on vient d'écarter.

   LE REPLI : `@supports` garde le fond plein pour un moteur sans `backdrop-filter`. Sans lui,
   ce moteur afficherait les articles en clair sous les rayons. */

.bo-raccourcis-wrap {
  background: rgba(255, 255, 255, .72);
  -webkit-backdrop-filter: saturate(180%) blur(18px);
  backdrop-filter: saturate(180%) blur(18px);
}
.footer-nav {
  background: rgba(255, 255, 255, .76);
  -webkit-backdrop-filter: saturate(180%) blur(18px);
  backdrop-filter: saturate(180%) blur(18px);
}
/* Le dégradé qui ferme le râtelier à droite tirait vers le fond PLEIN de la page : sur du
   verre, il dessinait un rectangle blanc au bout de la bande. Il s'efface vers le même
   blanc translucide. */
.bo-raccourcis-wrap::after {
  background: linear-gradient(to right, rgba(255, 255, 255, 0), rgba(255, 255, 255, .72) 72%);
}

@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .bo-raccourcis-wrap { background: var(--bg, #FAFAFA); }
  .footer-nav { background: rgba(255, 255, 255, .97); }
  .bo-raccourcis-wrap::after {
    background: linear-gradient(to right, transparent, var(--bg, #FAFAFA) 72%);
  }
}

/* ── LA PHRASE DE LA PROMOTION, SUR LA LIGNE DU TITRE ──────────────────────────────── */
/* JM, le 21 octobre 2026 : « le -30% sur les robes wax doit être aligné next to Promotions
   et non en bas ». Elle prenait une ligne pleine sous le titre, juste au-dessus des tuiles :
   deux lignes de texte avant le premier article. `margin-left: auto` la pousse au bout de la
   ligne du titre, dans la place qui y était vide.

   Une pastille, et non du texte nu : à droite d'un titre de 22 px, une phrase grise se serait
   lue comme un sous-titre du titre, alors qu'elle appartient à la tuile allumée.

   UNE SEULE LIGNE (JM, le 21 octobre 2026 : « "2 acheté = 1 offert" sur une ligne »). Sur deux
   lignes, la pastille doublait de hauteur et poussait la bande de tuiles vers le bas : la
   ligne du titre changeait de hauteur selon la promotion ouverte.

   LA LIMITE EST POSÉE EN AMONT, dans Manage : `LONGUEUR_MAX_PROMO` vaut 32 caractères, mesurés
   contre les 210 px de texte que laisse cette pastille. La coupure ici est le filet pour les
   promotions écrites avant cette limite — elles s'affichent coupées jusqu'à ce que la gérante
   les rouvre.

   `text-wrap` AVANT `white-space` : les deux règlent `text-wrap-mode`, et l'ordre inverse
   annule le `nowrap`. Payé sur le carrousel le même jour. */
.bo-promos .bo-promos-phrase {
  margin: 0 0 0 auto;
  padding: 5px 11px;
  border-radius: 999px;
  max-width: 60%;
  font-size: 12.5px; font-weight: 700; line-height: 1.3; letter-spacing: -.1px;
  background: color-mix(in oklab, var(--accent, #6B21A8) 11%, var(--bg, #FAFAFA));
  color: color-mix(in oklab, var(--accent, #6B21A8) 72%, var(--ink, #16121F));
  text-wrap: auto;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.bo-promos .bo-promos-phrase[hidden] { display: none; }

/* ── LE TEXTE D'UNE DIAPO TIENT SUR UNE LIGNE, TOUJOURS ────────────────────────────── */
/* JM, le 21 octobre 2026, capture à l'appui : « il ne faut jamais que le texte du carrousel
   dépasse une ligne, toujours comme ça : le type Réseaux ou Promotion, le texte, et Voir
   l'article ou Voir la promotion ; c'est toujours ça que je veux ».

   Une légende de gérante n'a pas de longueur maximale : « Robes longues de bureau pour dames,
   robe décontractée pour femmes, vêtements de… » prenait trois lignes, poussait « Voir
   l'article » vers le bas et déformait la diapo — les diapos voisines, elles, en gardaient
   une. Le carrousel changeait de hauteur à chaque glissement.

   Une seule ligne, coupée par des points de suspension. La diapo entière est cliquable et la
   page de destination porte le texte en entier : rien ne se perd, tout se range.

   `text-wrap: balance` part avec : il répartit un texte sur PLUSIEURS lignes, il n'a plus
   d'objet quand il n'y en a qu'une. */
.bo-car-slide.promo strong,
.bo-car-slide.promo.avec-photo strong,
.bo-car-promo-in strong {
  /* `text-wrap` DOIT PASSER AVANT `white-space` : les deux règlent la même propriété,
     `text-wrap-mode`. Écrit après, `text-wrap: auto` annulait le `nowrap` qu'on venait de
     poser et le texte reprenait ses trois lignes. Mesuré dans le navigateur : `white-space`
     calculé valait `normal` alors que la règle au-dessus disait `nowrap`, et `text-overflow`,
     lui, était bien passé — c'est ce désaccord entre deux déclarations voisines qui a mis
     sur la piste. */
  text-wrap: auto;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ── LE FONDU D'UN CHANGEMENT DE TUILE (voir promotion.html, `changerDePromotion`) ──── */
/* 130 ms, sur les seules rangées : assez pour que la bascule ne se voie pas, trop court pour
   qu'on attende. La bande de tuiles et le titre restent en place pendant ce temps — c'est ce
   qui distingue un onglet d'un changement de page. */
#promoContent { transition: opacity .13s ease; }
#promoContent.bo-promos-echange { opacity: 0; }
@media (prefers-reduced-motion: reduce) {
  #promoContent { transition: none; }
}

/* ── LA VIDÉO QUI CHARGE : UN FOND CALME, PAS UN RECTANGLE NOIR ────────────────────── */
/* Voir `marquerLesVideosQuiChargent` dans js/carousel.js pour le pourquoi.
   Le fond nu de la diapo vidéo (#14101A) se lisait comme une panne. Il prend une teinte
   profonde de la boutique : sombre, donc les bords d'une vidéo verticale restent éteints,
   mais habité — on voit que quelque chose arrive, pas que quelque chose manque. */
.bo-car-slide.promo.avec-photo.avec-video {
  background: color-mix(in oklab, var(--primary, #6B21A8) 17%, #14101A);
}
/* Le reflet qui traverse pendant l'attente : lent, large, très peu contrasté. La même
   grammaire que les squelettes de la vitrine, pour qu'une attente se reconnaisse partout. */
.bo-car-slide.promo.avec-photo.avec-video.bo-car-charge {
  background-image: linear-gradient(100deg,
    rgba(255, 255, 255, 0) 24%,
    rgba(255, 255, 255, .075) 40%,
    rgba(255, 255, 255, 0) 56%);
  background-size: 240% 100%;
  background-repeat: no-repeat;
  animation: boCarCharge 1.5s ease-in-out infinite;
}
@keyframes boCarCharge {
  from { background-position: 175% 0; }
  to { background-position: -45% 0; }
}
/* LA VIDÉO SE FOND QUAND ELLE EST PRÊTE. Sans ce fondu, la première image remplace le fond
   d'un coup sec, et le changement se voit autant que le rectangle qu'on enlève. */
.bo-car-slide.promo.avec-photo.avec-video video {
  opacity: 1;
  transition: opacity .34s ease;
}
.bo-car-slide.promo.avec-photo.avec-video.bo-car-charge video { opacity: 0; }
@media (prefers-reduced-motion: reduce) {
  .bo-car-slide.promo.avec-photo.avec-video.bo-car-charge { animation: none; }
  .bo-car-slide.promo.avec-photo.avec-video video { transition: none; }
}

/* ── LE TEXTE D'UNE DIAPO S'ARRÊTE AVANT LE BORD ───────────────────────────────────── */
/* JM, le 21 octobre 2026, capture à l'appui : « le texte ne doit pas arriver à toute la ligne,
   il doit avoir une limite, il se limite vers la fin de la slide, pas sur toute la ligne […]
   s'il est long je préfère que ça s'arrête à "pour dames" ».

   CE N'ÉTAIT PAS UN PROBLÈME DE COUPURE MAIS DE LARGEUR. Mesuré : la diapo fait 430 px, et le
   texte en faisait 836 — il DÉBORDAIT de la diapo, et c'est le bord de celle-ci qui le
   tranchait. D'où l'impression qu'il court jusqu'au bout de la ligne : il allait bien plus
   loin que le bout. Les points de suspension, eux, ne s'affichaient jamais, puisque le texte
   avait toute la place qu'il demandait.

   `white-space: nowrap` sans largeur maximale fait exactement ça : l'élément grandit jusqu'à
   son contenu. Il fallait donc lui rendre la largeur de la diapo.

   Le bloc reprend la largeur de la diapo, rembourrage compris, et le texte la sienne : il
   reste 390 px. « Robes longues de bureau pour dames » en demande 371 — la coupure tombe donc
   juste après « pour dames », et les 20 px de rembourrage gardent l'air qu'il faut avant le
   bord. */
.bo-car-slide.promo .bo-car-promo-in {
  max-width: 100%;
  box-sizing: border-box;
}
.bo-car-slide.promo strong,
.bo-car-slide.promo.avec-photo strong,
.bo-car-promo-in strong {
  max-width: 100%;
}

/* ── LA PASTILLE « PROMO » : PETITE, ET POUSSÉE À DROITE ───────────────────────────── */
/* JM, le 21 octobre 2026, capture à l'appui : « laisse le feu à côté de chaque texte promo
   mais diminue sa largeur, il faut qu'il soit plus poussé vers la droite, très petit ».

   Elle portait la phrase entière de la gérante, et s'étirait donc sur toute la place qui
   restait — mesuré : 183 px pour le seul mot « Promo ». Depuis qu'elle ne dit plus que ce
   mot, elle n'a aucune raison de s'étirer : `flex: none` et `width: auto` la font tenir à la
   taille de son texte, et `margin-left: auto` la pousse au bout de la ligne, contre le
   « voir plus ».

   La flamme la précède, à l'extérieur : elle reste le signe qui attire l'œil, la pastille
   n'est plus que son étiquette. */
.bo-rangee-tete .accordion-promo-text {
  flex: none;
  width: auto;
  max-width: none;
  margin-left: auto;
  padding: 3px 10px;
  font-size: 10.5px;
  font-weight: 800;
  letter-spacing: .3px;
}
/* La flamme suit la pastille au bout de la ligne : sans cette marge, elle resterait collée au
   titre du rayon, à l'autre bout, et l'on verrait un feu sans rien à côté. */
.bo-rangee-tete .bo-flamme {
  margin-left: auto;
  margin-right: 4px;
  width: 13px; height: 15px;
}
/* Quand la flamme est là, c'est ELLE qui est poussée à droite, et la pastille la suit. */
.bo-rangee-tete .bo-flamme + .accordion-promo-text { margin-left: 0; }

/* ══════════════════════════════════════════════════════════════════════════════════════
   LE GABARIT STATIQUE : LA PAGE NE DÉFILE PAS, LA BOÎTE DÉFILE.
   ══════════════════════════════════════════════════════════════════════════════════════

   Voir `emballerLeDefilement` dans js/header.js pour le pourquoi, et la section du
   21 octobre 2026 dans CONTEXTE.md pour le raisonnement complet.

   `body.bo-statique` n'est posée que lorsque la boîte a VRAIMENT été construite : une page
   qui n'aurait pas `header.js`, ou dont l'emballage échoue, garde le défilement de la
   fenêtre et reste lisible. On ne ferme jamais le défilement sans avoir ouvert l'autre. */
body.bo-statique,
html:has(body.bo-statique) {
  height: 100%;
  overflow: hidden;
  overscroll-behavior: none;
}
body.bo-statique {
  display: flex;
  flex-direction: column;
  height: 100dvh;
}
/* L'en-tête ne défile plus : il est hors de la boîte. Il n'a donc plus besoin d'être
   `sticky` — il est simplement le premier élément d'une colonne qui ne bouge pas. */
body.bo-statique .bo-head { flex: 0 0 auto; }
/* `min-height: 0` : sans lui, une boîte flexible refuse de rétrécir sous la hauteur de son
   contenu, et c'est le document qui recommencerait à défiler — la panne qu'on ferme. */
.bo-defile {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
}
.bo-defile::-webkit-scrollbar { width: 0; height: 0; }

/* LE RÂTELIER COLLE AU HAUT DE LA BOÎTE, PAS SOUS L'EN-TÊTE. L'en-tête est sorti du
   défilement : la boîte commence déjà sous lui, et lui réserver sa hauteur laisserait
   maintenant un trou grand comme l'en-tête. `--bo-entete-h` ne sert plus qu'aux pages
   restées en défilement de fenêtre. */
body.bo-statique .bo-raccourcis-wrap { top: 0; }

/* ── TIRER VERS LE BAS POUR ACTUALISER ───────────────────────────────────────────────────
   Voir `tirerPourActualiser()` dans js/natif.js pour le pourquoi et le geste.

   ANCRÉ SUR LE CADRE TÉLÉPHONE, PAS SUR LA FENÊTRE (règle de CLAUDE.md) : un `left: 50%` seul
   centrerait le rond sur l'ÉCRAN, donc à côté de la boutique sur un navigateur large. Il se
   centre dans la colonne de 430 px, comme l'en-tête et le pied.
   `top` est posé par le script : c'est le haut de la boîte qui défile, pas celui de l'écran —
   sinon le rond sortirait de sous l'en-tête. */
.bo-tirer {
  position: fixed;
  left: calc(var(--bo-gauche) + min(var(--bo-cadre), 100vw) / 2);
  top: 0;
  z-index: 90;
  width: 34px; height: 34px;
  margin-top: -34px;                 /* au repos, il est caché juste au-dessus du bord */
  border-radius: 50%;
  background: var(--surface, #fff);
  box-shadow: 0 3px 12px rgba(17, 12, 24, .18);
  display: grid; place-items: center;
  opacity: 0;
  pointer-events: none;
  transform: translate(-50%, 0);
}
/* Pendant le geste, le rond SUIT LE DOIGT : aucune transition, sinon il traîne derrière.
   Elle n'est posée que pour le retour et pour la mise en place du rechargement. */
.bo-tirer.revient { transition: transform .24s ease, opacity .24s ease; }
.bo-tirer svg { width: 20px; height: 20px; display: block; }
.bo-tirer circle {
  fill: none;
  stroke: var(--muted, #6E6880);
  stroke-width: 2.4;
  stroke-linecap: round;
  /* Un arc, pas un cercle : c'est l'ouverture qui fait voir que ça tourne. */
  stroke-dasharray: 44 13;
  transition: stroke .18s ease;
}
/* Passé le seuil, le rond prend la couleur de la boutique : le doigt sait qu'il peut lâcher. */
.bo-tirer.pret circle { stroke: var(--accent, #6B21A8); }
.bo-tirer.tourne svg { animation: bo-tirer-tourne .72s linear infinite; }
@keyframes bo-tirer-tourne { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .bo-tirer.tourne svg { animation-duration: 1.6s; }
}
