/* ==========================================================================
   AVAIRIS — feuille de style principale
   Palette : blanc cassé chaud, encre profonde, bronze doux
   ========================================================================== */

/* --- Apollo (177studio) — police unique du site ---------------------------
   Fichiers locaux dans assets/fonts/, servis en OTF : la fonte ne pèse que
   43 ko, un passage en WOFF2 ne gagnerait qu'une vingtaine de kilo-octets.
   Deux fichiers seulement, Regular et Italic — il n'existe pas de Bold : les
   graisses supérieures à 400 seront donc synthétisées par le navigateur.
   Les chemins sont relatifs à cette feuille, qui vit dans assets/css/. */

@font-face {
  font-family: 'Apollo';
  src: url('../fonts/APOLLO.otf') format('opentype');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Apollo';
  src: url('../fonts/APOLLOItalic.otf') format('opentype');
  font-weight: 400;
  font-style: italic;
  font-display: swap;
}

/* --- Creato Display Light — textes secondaires ----------------------------
   Apollo est une display : superbe en titre, fatigante en paragraphe. Creato
   Light prend donc tout le texte courant, Apollo garde les titres et le logo.
   Déclarée en 300 (Light) : c'est le seul fichier de la famille embarqué, les
   graisses supérieures seront synthétisées si une règle en demande.
   Licence SIL OFL, usage commercial autorisé — voir CreatoDisplay-OFL.txt. */

@font-face {
  font-family: 'Creato Display';
  src: url('../fonts/CreatoDisplay-Light.otf') format('opentype');
  font-weight: 300;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Creato Display';
  src: url('../fonts/CreatoDisplay-LightItalic.otf') format('opentype');
  font-weight: 300;
  font-style: italic;
  font-display: swap;
}

/* Deux graisses hautes de la même famille, pour que les rares éléments qui
   demandent du gras soient vraiment dessinés en gras. Sans elles, le navigateur
   épaissit le Light lui-même : sur une géométrique fine, ce faux gras empâte
   les courbes et salit le rendu. Ces fichiers ne sont téléchargés que si une
   règle réclame la graisse correspondante. */
@font-face {
  font-family: 'Creato Display';
  src: url('../fonts/CreatoDisplay-Medium.otf') format('opentype');
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Creato Display';
  src: url('../fonts/CreatoDisplay-Bold.otf') format('opentype');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

:root {
  --paper: #f6f3ee;
  --paper-2: #efeae2;
  --card: #ffffff;
  --ink: #1b1917;
  --ink-soft: #55504a;
  --ink-faint: #8c857c;
  --line: #ddd6cb;
  --line-soft: #e8e2d8;
  --bronze: #8a6a4b;
  --bronze-deep: #6b5039;

  /* Apollo porte tout le site. Les deux variables sont conservées — elles sont
     référencées à douze endroits — mais pointent désormais vers la même pile.
     Jost vient juste derrière, et pas seulement en secours de chargement :
     Apollo ne contient ni « » ni – — … · •, pourtant présents dans les textes.
     Le navigateur va chercher ces signes-là dans la police suivante, glyphe par
     glyphe ; autant que ce soit une géométrique proche plutôt qu'un Times. */
  /* Deux rôles, deux polices. --font-display : titres, logo, noms de produit
     et prix mis en avant — Apollo, inchangé. --font-sans : tout le texte
     courant et les libellés secondaires — désormais Creato Display Light.
     La hiérarchie tient au fait que h1–h4, .brand__mark, .card__name,
     .line__name, .step::before, .bundle__price et .drawer__total b déclarent
     explicitement --font-display : changer --font-sans ne les atteint pas. */
  --font-display: 'Apollo', 'Jost', -apple-system, 'Segoe UI', Helvetica, Arial,
    sans-serif;
  --font-sans: 'Creato Display', 'Jost', -apple-system, 'Segoe UI', Helvetica,
    Arial, sans-serif;

  --wrap: 1280px;
  --gutter: 28px;
  --radius: 2px;
  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
}

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

html {
  scroll-behavior: smooth;
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: 16px;
  font-weight: 300;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

/* Menu ouvert : la page derrière ne doit pas défiler. Posé sur html et body,
   sinon Safari continue de faire glisser le document. Pas de touch-action:none
   ici : il se propage aux descendants et bloquerait le défilement de la liste
   du menu en paysage. */
html.menu-open,
body.menu-open {
  overflow: hidden;
}

img {
  max-width: 100%;
  display: block;
}

a {
  color: inherit;
  text-decoration: none;
}

button {
  font: inherit;
  color: inherit;
  cursor: pointer;
  border: 0;
  background: none;
}

h1,
h2,
h3,
h4 {
  font-family: var(--font-display);
  font-weight: 300;
  /* Apollo dessine ses glyphes de +0,982 à -0,299 em, soit 1,281 em de haut.
     À 1,16 — la valeur héritée de Cormorant — un jambage (g, j, p) venait
     effleurer l'accent de la ligne suivante (é, à, î) sur les titres qui
     passent à la ligne. 1,3 dégage la rencontre. */
  line-height: 1.3;
  letter-spacing: 0.01em;
  margin: 0;
}

p {
  margin: 0 0 1em;
}

/* --- justification du texte courant --------------------------------------
   Volontairement énumérée plutôt qu'appliquée à « p » : les blocs centrés du
   site — .page-head, .newsletter, .announce, les deux lignes du Pack Duo —
   perdraient leur centrage, et justifier une étiquette d'une ligne ne fait
   rien de bon. Seules les colonnes de prose sont concernées.

   hyphens: auto est indispensable ici. Sans césure, justifier du français —
   mots longs, peu d'espaces d'ajustement — creuse des rivières blanches entre
   les mots. Les pages portent lang="fr", le navigateur applique donc les
   motifs de césure français. */
.prose p,
.prose li,
.split__body p,
.pdp__desc,
.accordion__panel p,
.collection__intro,
.step p,
.card__short,
.bundle p {
  text-align: justify;
  -webkit-hyphens: auto;
  hyphens: auto;
}

::selection {
  background: var(--ink);
  color: var(--paper);
}

/* --- utilitaires ---------------------------------------------------------- */

.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin: 0 auto;
  padding: 0 var(--gutter);
}

.wrap--narrow {
  max-width: 780px;
}

.eyebrow {
  font-size: 11px;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin: 0 0 18px;
  font-weight: 400;
}

.section {
  padding: clamp(64px, 9vw, 128px) 0;
}

.section--tight {
  padding: clamp(48px, 6vw, 80px) 0;
}

.section-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 24px;
  margin-bottom: clamp(32px, 5vw, 56px);
}

.section-head h2 {
  font-size: clamp(30px, 4.4vw, 52px);
}

.lead {
  font-size: clamp(16px, 1.5vw, 19px);
  color: var(--ink-soft);
  line-height: 1.75;
}

.rule {
  height: 1px;
  background: var(--line);
  border: 0;
  margin: 0;
}

/* --- boutons -------------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 15px 30px;
  font-size: 11px;
  font-weight: 400;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  border-radius: var(--radius);
  border: 1px solid var(--ink);
  background: var(--ink);
  color: var(--paper);
  transition: background 0.35s var(--ease), color 0.35s var(--ease),
    border-color 0.35s var(--ease), opacity 0.2s;
}

.btn:hover {
  background: var(--bronze-deep);
  border-color: var(--bronze-deep);
}

.btn:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

.btn--ghost {
  background: transparent;
  color: var(--ink);
}

.btn--ghost:hover {
  background: var(--ink);
  color: var(--paper);
  border-color: var(--ink);
}

.btn--light {
  background: #fff;
  border-color: #fff;
  color: var(--ink);
}

.btn--light:hover {
  background: transparent;
  color: #fff;
}

/* Variante champagne, réservée à l'offre Pack Duo du hero — un seul usage sur
   tout le site, dans index.html. Un aplat mat : pas de dégradé, pas de
   brillance.

   #e0cba6 remplace le doré #c9aa68, qui était trop saturé pour la photo. La
   teinte n'est pas choisie à l'œil : les tons chauds et clairs de cover2.png
   ont été relevés pixel par pixel, et les plus fréquents sont un lin #dbbfa7,
   un crème #e4cbb4 et un sable #d7b59b. #e0cba6 se tient à 13 unités RVB du
   lin dominant — l'ancien doré en était à 69. C'est la couleur de l'image
   elle-même, à peine tirée vers le champagne.

     saturation  0,48 → 0,26   (−46 %)
     clarté      0,42 → 0,61   (+45 %)
     écart R−B     97 → 58     (le lin de la photo est à 52 : moins jaune)

   Le texte reste en encre : 11,1:1 de contraste, contre 7,9 avant — bien
   au-delà du seuil AAA de 7:1. */
.btn--or {
  background: #e0cba6;
  border-color: #e0cba6;
  color: var(--ink);
  /* L'ombre était teintée pour l'ancien doré. Sous un aplat aussi clair, ce
     même ambre lisait comme un halo terne : elle est éclaircie dans la
     nouvelle famille et très légèrement allégée. Géométrie inchangée. */
  box-shadow: 0 6px 18px rgba(176, 150, 106, 0.26);
  /* .btn n'anime ni transform ni box-shadow : sans ce rappel, l'élévation au
     survol serait sèche. Les durées restent celles du site. */
  transition: background 0.35s var(--ease), color 0.35s var(--ease),
    border-color 0.35s var(--ease), transform 0.35s var(--ease),
    box-shadow 0.35s var(--ease);
}

/* Survol : le bouton ne s'éclaire plus, il se CHARGE. #d6bd8f est un cran plus
   doré — saturation 0,26 → 0,33 — et 14 % plus sombre. Assez pour se voir,
   trop peu pour clignoter ; le contraste reste à 9,6:1. L'élévation d'un pixel
   est conservée telle quelle. */
.btn--or:hover {
  background: #d6bd8f;
  border-color: #d6bd8f;
  color: var(--ink);
  transform: translateY(-1px);
  box-shadow: 0 10px 26px rgba(176, 150, 106, 0.32);
}

.btn--or:active {
  transform: none;
}

.btn--block {
  width: 100%;
}

.link-underline {
  font-size: 11px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  padding-bottom: 4px;
  border-bottom: 1px solid var(--ink);
  transition: opacity 0.25s, border-color 0.25s;
  white-space: nowrap;
}

.link-underline:hover {
  opacity: 0.55;
}

/* --- annonce -------------------------------------------------------------- */

.announce {
  background: var(--ink);
  color: var(--paper);
  text-align: center;
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  padding: 11px 16px;
}

/* --- en-tête -------------------------------------------------------------- */

.header {
  position: sticky;
  top: 0;
  z-index: 60;
  background: rgba(246, 243, 238, 0.9);
  backdrop-filter: saturate(160%) blur(14px);
  -webkit-backdrop-filter: saturate(160%) blur(14px);
  border-bottom: 1px solid transparent;
  transition: border-color 0.3s, background 0.3s;
}

.header.is-stuck {
  border-bottom-color: var(--line-soft);
}

/* --- en-tête superposé au hero (pages avec image de couverture) -----------
   #site-header devient le repère : le bandeau d'annonce y reste dans le flux,
   l'en-tête en sort. « top: 100% » le pose donc pile sous le bandeau, quelle
   que soit la hauteur de celui-ci — il passe sur deux lignes en mobile.
   #site-header ne mesure plus que la hauteur du bandeau : le hero remonte
   d'autant et son image commence bien derrière la barre.
   z-index 70 : au-dessus de .hero__fx (3), les fleurs continuent d'animer
   derrière sans jamais passer devant la navigation. */
#site-header {
  position: relative;
  z-index: 70;
}

.header--overlay {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  background: transparent;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  border-bottom: 0;
}

/* Le haut du hero est clair — flacons sur drapé crème — et le voile du média
   n'y monte qu'à 0,16 d'opacité. Sur une luminance de fond d'environ 0,85, du
   blanc ne donne que 2,1:1 de contraste, très en deçà des 4,5:1 attendus pour
   du 11 px.
   Assombrir jusqu'à atteindre ce ratio par le seul voile demanderait 0,78
   d'opacité : ce ne serait plus une barre transparente mais un bandeau noir.
   D'où le partage — un dégradé modéré, qui s'éteint avant la fin de la barre et
   laisse l'image lisible au travers, complété par une ombre portée sur le texte
   lui-même. L'ombre détache les lettres sans toucher au reste de l'image. */
.header--overlay::before {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: 200px;
  background: linear-gradient(
    to bottom,
    rgba(18, 16, 15, 0.58) 0%,
    rgba(18, 16, 15, 0.3) 46%,
    rgba(18, 16, 15, 0) 100%
  );
  pointer-events: none;
}

/* .wrap doit être positionné lui aussi : un ::before en position absolue se
   peint après le contenu en flux, et recouvrirait le texte. À égalité de
   z-index, c'est l'ordre du DOM qui tranche — et .wrap vient après. */
.header--overlay .wrap {
  position: relative;
}

.header--overlay .brand__mark,
.header--overlay .brand__tag,
.header--overlay .nav a {
  text-shadow: 0 1px 10px rgba(12, 10, 9, 0.6), 0 0 3px rgba(12, 10, 9, 0.45);
}

.header--overlay .icon-btn,
.header--overlay .brand__dots span,
.header--overlay .burger span {
  filter: drop-shadow(0 1px 4px rgba(12, 10, 9, 0.55));
}

.header--overlay .brand__mark {
  color: #fff;
}

.header--overlay .brand__tag {
  color: rgba(255, 255, 255, 0.74);
}

.header--overlay .brand__dots span {
  background: #fff;
}

.header--overlay .nav a {
  color: rgba(255, 255, 255, 0.84);
}

.header--overlay .nav a:hover,
.header--overlay .nav a[aria-current='page'] {
  color: #fff;
}

.header--overlay .nav a::after {
  background: #fff;
}

.header--overlay .icon-btn {
  color: rgba(255, 255, 255, 0.88);
}

.header--overlay .icon-btn:hover {
  color: #fff;
}

.header--overlay .burger span {
  background: #fff;
}

.header--overlay .cart-count {
  background: #fff;
  color: var(--ink);
}

.header--overlay .cart-count[data-empty='true'] {
  background: transparent;
  color: rgba(255, 255, 255, 0.85);
  border-color: rgba(255, 255, 255, 0.5);
}

/* 88 px et non 78 : le logo empile désormais deux points au-dessus du nom, et
   à l'ancienne hauteur le point supérieur venait buter contre le bandeau
   d'annonce — 6 px d'air, contre 17 px du temps du nom seul. */
.header__inner {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 20px;
  height: 88px;
}

/* Colonne centrale de la grille : la navigation est centrée sur la page, pas
   sur l'espace laissé entre le logo et les icônes. */
.nav {
  display: flex;
  align-items: center;
  justify-content: center;
  justify-self: center;
  gap: clamp(22px, 3vw, 46px);
}

/* Sans nowrap, « À propos » se plie en deux lignes et déforme la barre. */
.nav a {
  font-size: 11px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  white-space: nowrap;
  position: relative;
  padding: 6px 0;
  color: var(--ink-soft);
  transition: color 0.25s;
}

.nav a::after {
  content: '';
  position: absolute;
  left: 0;
  bottom: 0;
  width: 0;
  height: 1px;
  background: var(--ink);
  transition: width 0.35s var(--ease);
}

.nav a:hover,
.nav a[aria-current='page'] {
  color: var(--ink);
}

.nav a:hover::after,
.nav a[aria-current='page']::after {
  width: 100%;
}

/* Le logo redessiné en CSS : deux points puis le nom, sans le médaillon.
   Les proportions sont relevées sur le fichier d'origine — points de 60 et
   30 px pour une hauteur de capitale de 66 px, soit 0,9 et 0,45 fois cette
   hauteur — puis ramenées à l'échelle de l'en-tête.
   .brand n'existe que dans l'en-tête : le pied de page et le panneau mobile
   réutilisent .brand__mark / .brand__tag sans passer par ce conteneur, leurs
   réglages restent donc intacts. */
.brand {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0;
  justify-self: start;
}

.brand__dots {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  margin-bottom: 8px;
}

.brand__dots span {
  display: block;
  border-radius: 50%;
  background: var(--ink);
  transition: opacity 0.25s;
}

.brand__dots span:first-child {
  width: 13px;
  height: 13px;
}

.brand__dots span:last-child {
  width: 6px;
  height: 6px;
}

.brand:hover .brand__dots span {
  opacity: 0.55;
}

.brand .brand__mark {
  font-size: 23px;
}

/* Sans line-height explicite, la signature hérite du 1,65 du corps de texte et
   ajoute 5 px de vide sous elle — l'en-tête ne fait que 78 px de haut. */
.brand .brand__tag {
  line-height: 1;
  margin-top: 5px;
}

.brand__mark {
  font-family: var(--font-display);
  font-size: 27px;
  letter-spacing: 0.3em;
  text-indent: 0.3em;
  line-height: 1;
  font-weight: 400;
}

/* La signature du logo n'avait pas de police déclarée : elle héritait du corps
   de texte, donc d'Apollo. En passant le corps à Creato elle aurait changé
   avec lui — on la rattache explicitement au logo, qui reste tout en Apollo. */
.brand__tag {
  font-family: var(--font-display);
  font-size: 8px;
  letter-spacing: 0.42em;
  text-indent: 0.42em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.header__actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  justify-self: end;
  gap: 18px;
}

/* Boutons purement graphiques : le nom est porté par aria-label, plus par un
   libellé visible. La cible reste à 44 px malgré une icône de 19 px. */
.icon-btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  color: var(--ink-soft);
  transition: color 0.25s, opacity 0.25s;
}

.icon-btn:hover {
  color: var(--ink);
}

.icon-btn svg {
  display: block;
}

/* Pastille posée sur l'icône du sac plutôt qu'à côté : elle ne doit pas
   décaler les icônes quand le compteur passe de 0 à 1. */
.cart-count {
  position: absolute;
  top: 4px;
  right: 2px;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: 999px;
  background: var(--ink);
  color: var(--paper);
  font-size: 9px;
  letter-spacing: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.cart-count[data-empty='true'] {
  background: transparent;
  color: var(--ink-faint);
  border: 1px solid var(--line);
}

.burger {
  display: none;
  flex-direction: column;
  gap: 5px;
  padding: 8px 0;
}

.burger span {
  display: block;
  width: 22px;
  height: 1px;
  background: var(--ink);
  transition: transform 0.3s var(--ease), opacity 0.2s;
}

/* --- menu mobile (panneau) ------------------------------------------------
   Le panneau est monté au niveau du <body>, pas dans .header : l'en-tête est
   sticky avec z-index 60, ce qui crée un contexte d'empilement dont un enfant
   ne peut jamais sortir — le menu passait sous le tiroir panier et recouvrait
   son propre bouton burger. Ici il vit à côté du tiroir, à plat sur le body.
   Masqué en display:none au-dessus de 900 px : un seul menu existe à la fois,
   donc pas de doublon de navigation pour les lecteurs d'écran. */

.menu-overlay,
.menu {
  display: none;
}

@media (max-width: 900px) {
  .menu-overlay {
    display: block;
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.45);
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.4s var(--ease), visibility 0.4s;
    z-index: 190;
  }

  .menu-overlay.is-open {
    opacity: 1;
    visibility: visible;
  }

  .menu {
    --menu-bg: #f8f6f2;
    --menu-line: #ece7e1;
    --menu-hover: #f5f1ea;

    display: flex;
    flex-direction: column;
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100vh;
    height: 100dvh;
    background: var(--menu-bg);
    box-shadow: 20px 0 60px rgba(20, 18, 16, 0.16);
    transform: translateX(-100%);
    visibility: hidden;
    transition: transform 0.45s var(--ease), visibility 0.45s;
    z-index: 200;
    overflow: hidden;
  }

  .menu.is-open {
    transform: translateX(0);
    visibility: visible;
  }

  .menu__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    padding: 20px 22px 20px 30px;
    border-bottom: 1px solid var(--menu-line);
    flex: 0 0 auto;
  }

  .menu__brand {
    display: flex;
    flex-direction: column;
    gap: 3px;
  }

  .menu__brand .brand__mark {
    font-size: 22px;
  }

  .menu__close {
    width: 44px;
    height: 44px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 26px;
    line-height: 1;
    color: var(--ink-soft);
    border-radius: 50%;
    transition: background 0.3s, color 0.3s;
  }

  .menu__close:hover {
    background: var(--menu-hover);
    color: var(--ink);
  }

  /* La liste défile si l'écran est court (paysage), sans jamais pousser le
     pied de menu hors du panneau. */
  .menu__nav {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    padding: 10px 0;
  }

  .menu__link {
    display: flex;
    align-items: center;
    position: relative;
    height: 64px;
    flex: 0 0 auto;
    padding-left: 30px;
    padding-right: 24px;
    font-size: 17px;
    font-weight: 500;
    letter-spacing: 2px;
    text-transform: uppercase;
    color: var(--ink);
    border-bottom: 1px solid var(--menu-line);
    transition: background 0.3s var(--ease), color 0.3s;
  }

  .menu__nav .menu__link:last-child {
    border-bottom: 0;
  }

  /* Le libellé glisse, pas le fond : translater le <a> entier décalerait sa
     surface colorée et laisserait une bande vide à gauche. */
  .menu__link span {
    display: block;
    transition: transform 0.3s var(--ease);
  }

  .menu__link:hover,
  .menu__link:focus-visible {
    background: var(--menu-hover);
  }

  .menu__link:hover span,
  .menu__link:focus-visible span {
    transform: translateX(8px);
  }

  .menu__link[aria-current='page'] {
    color: var(--bronze-deep);
  }

  .menu__link[aria-current='page']::before {
    content: '';
    position: absolute;
    left: 0;
    top: 50%;
    transform: translateY(-50%);
    width: 3px;
    height: 26px;
    background: var(--bronze);
  }

  .menu__foot {
    flex: 0 0 auto;
    padding: 22px 30px calc(24px + env(safe-area-inset-bottom));
    border-top: 1px solid var(--menu-line);
  }

  .menu__ship {
    margin: 0 0 14px;
    font-size: 11px;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--ink-faint);
  }

  .menu__social {
    display: flex;
    align-items: center;
    gap: 22px;
  }

  .menu__social a {
    font-size: 12px;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--ink-soft);
    padding: 6px 0;
    border-bottom: 1px solid transparent;
    transition: color 0.3s, border-color 0.3s;
  }

  .menu__social a:hover {
    color: var(--ink);
    border-bottom-color: var(--ink);
  }
}

/* Tablette : un panneau pleine largeur ferait perdre le contexte de la page. */
@media (min-width: 560px) and (max-width: 900px) {
  .menu {
    width: 380px;
    max-width: 90%;
  }
}

/* --- hero ----------------------------------------------------------------- */

.hero {
  position: relative;
  min-height: min(88vh, 780px);
  display: flex;
  align-items: flex-end;
  color: #fff;
  overflow: hidden;
}

/* z-index sur le média : il isole la brume et les pétales, qui passeraient
   sinon par-dessus le titre et le délaveraient. */
.hero__media {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
}

/* Le cadre déborde de 26 px pour que la parallaxe ne découvre jamais un bord. */
/* NIVEAU 1 de la profondeur : l'image. Le mouvement le plus faible.
   Aucune `transition` ici, et c'est délibéré : le lissage se fait en JS, une
   interpolation par image (voir initHeroParallax). Une transition CSS relancée
   à chaque frame ne s'achève jamais — elle produit une traîne molle au lieu
   d'un suivi fluide. Les deux ensemble se cumuleraient en un retard visible. */
.hero__frame {
  position: absolute;
  inset: -26px;
  transform: translate3d(var(--px, 0), var(--py, 0), 0);
  will-change: transform;
}

/* <picture> est inline par défaut : sans ça, l'image perd sa hauteur. */
.hero__frame picture {
  display: block;
  width: 100%;
  height: 100%;
}

.hero__media img,
.hero__video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 42%;
  transform: scale(1.04);
  animation: heroDrift 18s var(--ease) forwards;
}

/* La vidéo se superpose à la photo, qui reste en secours si elle ne charge pas. */
.hero__video {
  position: absolute;
  inset: 0;
  z-index: 1;
}

@keyframes heroDrift {
  to {
    transform: scale(1);
  }
}

.hero__media::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 2;
  background: linear-gradient(
    to top,
    rgba(20, 18, 16, 0.8) 0%,
    rgba(20, 18, 16, 0.52) 42%,
    rgba(20, 18, 16, 0.14) 100%
  );
}

/* --- hero « affiche » ----------------------------------------------------- */

/* L'affiche porte son titre et son bouton dessinés : on l'affiche entière,
   sans recadrage, sans rideau sombre et sans rien superposer. Les vrais
   boutons passent dessous, dans le flux. */
.hero--cover {
  display: block;
  min-height: 0;
  color: inherit;
  background: var(--paper);
}

.hero--cover .hero__media {
  position: relative;
  aspect-ratio: 1717 / 916; /* cadrage exact de BOTTLES/cover1.png */
}

.hero--cover .hero__frame {
  inset: 0;
  transform: none;
}

.hero--cover .hero__media img,
.hero--cover .hero__video {
  object-position: center;
  transform: none;
  animation: none;
}

.hero--cover .hero__media::after {
  display: none;
}

/* Les boutons reprennent la place du bouton dessiné de l'affiche : centrés,
   juste sous l'accroche. Le repère est en pourcentage, donc il suit l'image
   quelle que soit la largeur. */
.hero--cover .hero__inner {
  position: absolute;
  top: 26%;
  left: 0;
  right: 0;
  display: flex;
  justify-content: center;
  max-width: none;
  padding: 0;
}

.hero--cover .hero__actions {
  justify-content: center;
}

/* Le second bouton est transparent par défaut : sur l'affiche, les
   vaporisateurs passaient au travers et le libellé devenait illisible. */
.hero--cover .hero__actions .btn--ghost {
  background: rgba(255, 253, 250, 0.92);
  border-color: rgba(27, 25, 23, 0.22);
}

/* Sous 760 px l'affiche devient trop petite pour porter les boutons : ils
   repassent sous l'image, dans le flux. */
@media (max-width: 760px) {
  .hero--cover .hero__inner {
    position: static;
    padding: clamp(26px, 5vw, 40px) 0 clamp(34px, 6vw, 56px);
  }
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}

/* --- hero : brume de vaporisation et pétales ------------------------------ */

/* Au-dessus du dégradé : la brume et les pétales doivent rester lumineux. */
.hero__fx {
  position: absolute;
  /* Débord de 90 px : la couche se déplace jusqu'à 81 px avec la souris, et son
     bord de découpe ne doit jamais entrer dans le hero — sinon une fleur se
     retrouve tranchée en plein cadre. Le hero est lui-même en overflow hidden. */
  inset: -90px;
  z-index: 3;
  overflow: hidden;
  pointer-events: none;
  /* NIVEAU 2 : la couche de fleurs. Elle double le décalage de l'image — c'est
     l'écart entre les deux qui fabrique la profondeur.
     --px/--py : décalage souris posé par initHeroParallax.
     --sy : décalage au défilement, posé par initHeroScrollDrift.
     Pas de transition non plus : le lissage vient du JS. */
  transform: translate3d(
    calc(var(--px, 0px) * 2),
    calc(var(--py, 0px) * 2 + var(--sy, 0px)),
    0
  );
  will-change: transform;
}

/* NIVEAU 3 : les pétales eux-mêmes, au premier plan. Ce conteneur n'avait
   aucune règle — il n'a pas d'animation propre, l'animation `heroPetal` vit sur
   ses enfants <i>. Lui donner un transform ne peut donc écraser aucune
   animation existante : les deux se composent, le parent décale, les enfants
   continuent de monter et de tourner.

   Le décalage s'AJOUTE à celui de .hero__fx, son parent : 2 + 1,1 = 3,1 fois
   celui de l'image. Trois profondeurs distinctes, sans toucher au balisage. */
/* `position: absolute; inset: 0` est INDISPENSABLE, pas décoratif.
   Ce conteneur n'avait aucune règle : un simple <div> statique, donc de hauteur
   nulle puisque ses douze <i> sont tous en position absolue et ne comptent pas
   dans le flux. Tant qu'il n'avait pas de `transform`, cela ne gênait pas — les
   pétales se référaient à .hero__fx, le premier ancêtre positionné, qui occupe
   tout le cadre.

   Poser un `transform` a changé cela : une valeur autre que `none` fait de
   l'élément le bloc conteneur de ses descendants absolus. Les pétales se sont
   donc mis à se caler sur une boîte de 0 px de haut — `bottom: -6%` valant
   alors 0, les douze se retrouvaient empilés au même point puis emportés
   850 px plus haut, hors du cadre dès la première image. Ils disparaissaient.

   En le dimensionnant sur .hero__fx, le bloc conteneur retrouve exactement la
   géométrie d'avant : `bottom: -6%` et `left: var(--x)` reprennent leurs
   valeurs d'origine, et le transform continue de servir de troisième plan. */
.hero__petals {
  position: absolute;
  inset: 0;
  transform: translate3d(
    calc(var(--px, 0px) * 1.1),
    calc(var(--py, 0px) * 1.1),
    0
  );
  will-change: transform;
}

/* trois bouffées décalées, qui montent du vaporisateur et se dispersent */
.hero__mist i {
  position: absolute;
  left: 54%;
  top: 30%;
  width: clamp(200px, 28vw, 420px);
  aspect-ratio: 1;
  border-radius: 50%;
  background: radial-gradient(
    circle at 50% 60%,
    rgba(255, 255, 255, 0.5),
    rgba(255, 246, 244, 0.16) 44%,
    transparent 72%
  );
  filter: blur(18px);
  opacity: 0;
  animation: heroMist 13.5s var(--d, 0s) infinite var(--ease);
}

@keyframes heroMist {
  0% {
    opacity: 0;
    transform: translate(-50%, 18%) scale(0.3);
  }
  18% {
    opacity: 0.5;
  }
  62% {
    opacity: 0.3;
  }
  100% {
    opacity: 0;
    transform: translate(-42%, -72%) scale(1.45);
  }
}

/* pétales : portés par la brume, ils traversent le cadre vers le haut */
/* Silhouette de pétale de cerisier (échancrure en haut, pointe en bas), en
   SVG inline : une forme en border-radius se lit comme un rond de bokeh. */
.hero__petals i {
  position: absolute;
  left: var(--x, 50%);
  bottom: -6%;
  width: 26px;
  height: 30px;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 28'%3E%3Cpath d='M12 28C6 24 2 18 2 12 2 6 6 2 10 4c1 1 2 3 2 5 0-2 1-4 2-5 4-2 8 2 8 10 0 6-4 12-10 16z' fill='%23fff' fill-opacity='.92'/%3E%3C/svg%3E")
    center / contain no-repeat;
  filter: drop-shadow(0 0 9px rgba(255, 234, 230, 0.6));
  opacity: 0;
  animation: heroPetal var(--t, 18s) var(--d, 0s) infinite linear;
}

@keyframes heroPetal {
  0% {
    opacity: 0;
    transform: translate3d(0, 0, 0) rotate(0deg) scale(var(--s, 1));
  }
  10% {
    opacity: 0.85;
  }
  72% {
    opacity: 0.6;
  }
  100% {
    opacity: 0;
    transform: translate3d(var(--dx, 60px), calc(-1 * var(--travel, 104vh)), 0)
      rotate(300deg) scale(var(--s, 1));
  }
}

.hero__inner {
  position: relative;
  padding-bottom: clamp(52px, 7vw, 96px);
  padding-top: 140px;
  max-width: 700px;
}

.hero .eyebrow {
  color: rgba(255, 255, 255, 0.7);
}

.hero h1 {
  font-size: clamp(42px, 7.2vw, 88px);
  line-height: 1.06;
  margin-bottom: 22px;
}

.hero p {
  font-size: clamp(15px, 1.6vw, 19px);
  color: rgba(255, 255, 255, 0.85);
  max-width: 44ch;
  margin-bottom: 34px;
}

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
}

/* Les deux appels à l'action du hero passent en gras — la seule propriété
   modifiée. Taille, couleurs, padding, interlettrage et position restent ceux
   de .btn : la géométrie des boutons est intacte.
   700 pointe sur le fichier Bold déclaré plus haut, pas sur un gras synthétisé.
   La famille n'a pas de SemiBold : demander 600 aurait de toute façon résolu
   vers ce même Bold, autant l'écrire.
   Portée limitée à .hero__actions : aucun autre bouton du site n'est touché. */
.hero__actions .btn {
  font-weight: 700;
}

/* --- hero « panorama » ---------------------------------------------------- */

/* L'image des six flacons est très large (2,9:1). Dans un hero de 88vh, le
   recadrage en `cover` n'en laisserait voir que quatre : on baisse la hauteur
   pour que la scène tienne presque entière. Ce bloc doit rester après les
   règles de base du hero — même spécificité, c'est l'ordre qui tranche. */
.hero--flacons {
  min-height: min(82vh, 690px);
}

/* Le retrait haut doit dégager la navbar, qui se superpose désormais au hero :
   88 px en bureau, 74 px en mobile. À 70 px, un écran bas en paysage — où le
   contenu remplit toute la hauteur et cesse d'être calé en bas — faisait passer
   le sur-titre sous la barre. */
.hero--flacons .hero__inner {
  padding-top: clamp(88px, 9vw, 108px);
  max-width: 560px;
}

/* Le sur-titre est au-dessus de la zone foncée du dégradé : à 0,7 il s'efface. */
.hero--flacons .eyebrow {
  color: rgba(255, 255, 255, 0.88);
}

/* Le hero fait 690 px, pas une hauteur d'écran : les fleurs doivent traverser
   cette distance-là, sinon elles sortent du cadre en une fraction du cycle.
   --travel suit donc toute variation de min-height, dans le même rapport
   (1,23 ici). C'est ce qui CONSERVE l'animation quand le cadre grandit : à
   valeur figée, les pétales s'arrêteraient avant le haut. */
.hero--flacons {
  --travel: 850px;
}

/* Petite fleur à cinq pétales plutôt qu'un pétale isolé, cohérente avec le
   jasmin et les fleurs de cerisier de la photo. Blanc chaud + ombre portée
   discrète : le blanc pur disparaîtrait sur le haut clair de l'image, et un
   halo lumineux disparaîtrait sur le bas assombri par le dégradé. */
.hero--flacons .hero__petals i {
  width: 22px;
  height: 22px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cg fill='%23fffaf6'%3E%3Cellipse cx='12' cy='6' rx='3.6' ry='5.6'/%3E%3Cellipse cx='12' cy='6' rx='3.6' ry='5.6' transform='rotate(72 12 12)'/%3E%3Cellipse cx='12' cy='6' rx='3.6' ry='5.6' transform='rotate(144 12 12)'/%3E%3Cellipse cx='12' cy='6' rx='3.6' ry='5.6' transform='rotate(216 12 12)'/%3E%3Cellipse cx='12' cy='6' rx='3.6' ry='5.6' transform='rotate(288 12 12)'/%3E%3C/g%3E%3Ccircle cx='12' cy='12' r='1.9' fill='%23e8b98a'/%3E%3C/svg%3E");
  filter: drop-shadow(0 1px 4px rgba(88, 62, 58, 0.4));
}

/* Titre plus petit que sur un hero plein écran : à 88 px il passe sur trois
   lignes, le bloc dépasse la hauteur voulue et le recadrage se resserre. */
.hero--flacons h1 {
  font-size: clamp(28px, 3.4vw, 44px);
  margin-bottom: 16px;
}

.hero--flacons p {
  margin-bottom: 26px;
}

/* --- colonne de texte du hero, en bureau ----------------------------------
   Le bloc reste à 560 px : les deux boutons demandent 437 px à eux seuls, un
   plafond de 420 px les ferait passer sur deux lignes. C'est donc le TITRE et
   les PARAGRAPHES qui sont bridés à 420 px, recentrés par marges automatiques
   dans les 504 px utiles du bloc. Les boutons, eux, se centrent sur le même
   axe (voir .hero--flacons .hero__actions plus bas). */
.hero--flacons .hero__inner {
  text-align: center;
}

/* Seuls les PARAGRAPHES sont bridés à 420 px. Le titre ne l'est plus : à 44 px
   il demande environ 460 à 490 px, et le plafond le cassait en « L'art de la /
   fraîcheur. ». La composition retenue le veut sur une seule ligne, et la
   capture d'origine confirme qu'il y tenait — il reste donc borné par les
   504 px utiles du bloc, sans plafond propre. Le plafond mobile de 340 px,
   lui, vit dans le bloc @media et n'est pas concerné. */
.hero--flacons .hero__inner p {
  max-width: 420px;
  margin-left: auto;
  margin-right: auto;
}

/* Ni justification ni césure ici, et c'est la nouvelle accroche qui le permet :
   74 caractères au lieu de 129, soit DEUX lignes au lieu de trois. Justifier un
   bloc de deux lignes n'en étire que la première — la seconde reste à 60-79 % —
   et le paragraphe paraît bancal. Le centrage hérité du bloc est plus juste, et
   c'est exactement la composition voulue :

     Une fraîcheur délicate pour votre linge,
     vos vêtements et votre intérieur.

   La taille fluide d'origine, clamp(15px, 1.6vw, 19px), est conservée : mesurée
   sur toute la plage de chasse plausible (0,46 à 0,58em), cette phrase ne laisse
   JAMAIS un mot seul en fin de bloc, à aucune taille de 17 à 19 px. Le
   rétrécissement à 17 px, posé pour l'ancienne phrase, n'a donc plus lieu
   d'être. Le sur-titre, lui, retombe sur une seule ligne : 255 à 296 px pour
   420 px de colonne, contre 318 à 371 px auparavant.

   Les boutons se centrent sur le même axe que la colonne de texte. Leurs
   dimensions ne changent pas : seule leur position dans la ligne. */
.hero--flacons .hero__actions {
  justify-content: center;
}

/* --- voile du hero : local, pas uniforme -----------------------------------
   Le dégradé précédent était un simple `to top` : il assombrissait TOUTE la
   largeur — 0,84 en bas, 0,62 à mi-hauteur — y compris la partie droite de la
   photo, où se trouvent les flacons. La composition disparaissait sous le
   voile alors que rien ne s'y lit.

   Deux couches se superposent maintenant. La première (peinte au-dessus) est
   une ellipse centrée sur le bloc de texte ; la seconde, un voile général très
   léger qui lie l'ensemble et retient le bas du cadre. Elles se composent :
   opacité effective = 1 − (1 − a₁)(1 − a₂).

   Opacité effective mesurée point par point, avant → après :

     haut du cadre         0,11 → 0,03      titre         0,43 → 0,55
     flacons, mi-hauteur   0,60 → 0,10      description   0,66 → 0,59
     bord droit            0,60 → 0,10      boutons       0,74 → 0,57
     bas du cadre          0,82 → 0,28      sur-titre     0,25 → 0,45

   Moyenne sur tout le cadre : 0,514 → 0,302, soit 41 % de voile en moins. La
   colonne de droite — celle des flacons — en perd 84 %.

   La colonne de texte, elle, ne s'éclaircit que sous la description et les
   boutons ; le titre et le sur-titre s'assombrissent un peu, et c'est
   volontaire. Mesuré sur les pixels de cover2.png (compositing en sRGB, comme
   le navigateur), le titre tombait à 3,02:1 sur les reflets blancs de la photo
   — tout juste au seuil des 3:1 du grand texte — et le sur-titre à ≈1,8:1,
   donc illisible par endroits. Ils passent à 4,15:1 et au-delà. Le corps de
   texte descend de 6,09:1 à 4,87:1 sur son pire pixel, toujours au-dessus des
   4,5:1 exigés. Aucune zone de texte ne perd son seuil.

   Rayons en POURCENTAGES : ils suivent la boîte, donc le voile se redimensionne
   avec le hero au lieu d'être calé sur des pixels. Le fondu compte sept paliers
   pour éteindre toute limite visible : la plus forte marche entre deux pixels
   voisins est de 1,7 niveau sur 255, très en deçà des ~8 niveaux à partir
   desquels un contour se perçoit. Pas de rectangle, pas d'anneau. Le ::after
   est porté par .hero__media et non par .hero__frame : il ne suit pas la
   parallaxe et reste calé sur le texte.

   Le haut n'a pas besoin d'être foncé : la navbar a son propre voile
   (.header--overlay::before) doublé d'une ombre portée sur ses lettres. */
.hero--flacons .hero__media::after {
  background: radial-gradient(
      ellipse 44% 50% at 30% 52%,
      rgba(18, 16, 14, 0.54) 0%,
      rgba(18, 16, 14, 0.52) 40%,
      rgba(18, 16, 14, 0.44) 60%,
      rgba(18, 16, 14, 0.28) 78%,
      rgba(18, 16, 14, 0.1) 88%,
      rgba(18, 16, 14, 0.03) 95%,
      rgba(18, 16, 14, 0) 100%
    ),
    linear-gradient(
      to top,
      rgba(18, 16, 14, 0.3) 0%,
      rgba(18, 16, 14, 0.16) 30%,
      rgba(18, 16, 14, 0.06) 62%,
      rgba(18, 16, 14, 0.03) 100%
    );
}

/* --- grille produits ------------------------------------------------------ */

/* `minmax(0, 1fr)` et non `1fr`. Les deux sont identiques tant que le contenu
   peut se replier : `1fr` vaut `minmax(auto, 1fr)`, et ce minimum `auto` est la
   largeur min-content de la carte. Depuis que le titre et l'accroche sont en
   `white-space: nowrap`, leur min-content vaut leur texte ENTIER — la colonne
   refusait donc de descendre en dessous de ~410 px, s'élargissait, et la
   quatrième carte sortait de l'écran. Les images, dimensionnées par la colonne,
   grossissaient d'autant.
   Le plancher à 0 rend les colonnes strictement égales : c'est le texte qui se
   termine en points de suspension, plus la grille qui s'étire. */
.grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(26px, 3.4vw, 48px) clamp(20px, 2.4vw, 32px);
}

/* Deux grilles montrent quatre produits et prennent donc une colonne de plus,
   dès que la largeur le permet : la vitrine de l'accueil et « Vous aimerez
   aussi » sur la fiche produit. La boutique garde ses trois colonnes.

   Une classe pour la seconde, et surtout pas `#also` : un identifiant pèse
   (1,0,0) et l'emporterait sur les règles .grid des @media, qui valent
   (0,1,0) — les quatre colonnes resteraient jusque sur téléphone.
   Une classe, elle, pèse le même poids que ces règles, et l'ordre du fichier
   tranche en leur faveur.

   Sélecteur d'attribut SEUL, et surtout pas qualifié par la classe : qualifié,
   il pèserait (0,2,0) et l'emporterait sur les règles .grid des @media plus
   bas, qui valent (0,1,0) — la vitrine resterait en quatre colonnes jusque sur
   téléphone. À spécificité égale c'est l'ordre du fichier qui tranche, et les
   @media viennent après : sous 1040 px la règle générale reprend la main et
   range les quatre parfums en deux lignes de deux.

   Largeur obtenue : 282 px par carte au-delà de 1280 px d'écran, 228 px à
   1041 px — le point le plus étroit avant le passage à deux colonnes. */
[data-grid='featured'],
.grid--quatre {
  grid-template-columns: repeat(4, minmax(0, 1fr));
}

/* Vitrine de l'accueil : le prix disparaît de la ligne du nom. Il reste porté
   par le bouton posé sur la photo, « AJOUTER 18,000 DT », qui affiche déjà la
   même valeur — aucune information n'est perdue, et le bouton est toujours
   dans le DOM pour les lecteurs d'écran, même quand il est transparent.

   La règle est en CSS et non dans cardHTML : le même composant sert les trois
   grilles du site, et seule celle-ci porte `data-grid="featured"`. La boutique
   (`data-grid="all"`) et « Vous aimerez aussi » gardent donc leur prix sans
   qu'aucune condition n'entre dans le code de la carte.

   Spécificité (0,2,0), au-dessus des deux règles `.card__price` (0,1,0) : elle
   l'emporte en bureau comme en mobile, sans dépendre de l'ordre du fichier.

   .card__head reste un conteneur flex avec un seul enfant : le titre, en
   `flex: 1 1 auto`, occupe la ligne entière. Il récupère au passage les 72 px
   que le prix et son écart consommaient, ce qui laisse respirer l'indice. */
[data-grid='featured'] .card__price {
  display: none;
}

.card {
  position: relative;
  display: flex;
  flex-direction: column;
  /* Second verrou, indispensable avec le titre en `nowrap` : un élément de
     grille vaut lui aussi `min-width: auto`. Sans ce zéro, la carte déborderait
     de sa piste — désormais bornée par `minmax(0, 1fr)` — et empiéterait sur sa
     voisine au lieu de laisser le texte se couper. */
  min-width: 0;
}

.card__media {
  position: relative;
  aspect-ratio: 4 / 5;
  overflow: hidden;
  background: var(--paper-2);
  border-radius: var(--radius);
  margin-bottom: 20px;
}

.card__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* les visuels sont très verticaux : on remonte le cadrage pour garder
     le vaporisateur et l'étiquette dans le champ */
  object-position: center 34%;
  transition: transform 1.1s var(--ease), opacity 0.55s var(--ease);
}

.card__media img.is-alt {
  position: absolute;
  inset: 0;
  opacity: 0;
}

.card:hover .card__media img.is-base {
  transform: scale(1.045);
}

.card:hover .card__media img.is-alt {
  opacity: 1;
  transform: scale(1.045);
}

.card__link {
  position: absolute;
  inset: 0;
  z-index: 1;
}

/* Les trois pastilles — BEST-SELLER, NOUVEAU, ÉDITION D'ÉTÉ — passent déjà par
   cette seule règle : même fond, même couleur, même coin, même ancrage. Ce qui
   manquait, c'est une HAUTEUR garantie : sans line-height, elle dépendait de
   l'interligne hérité du corps de texte. `line-height: 1` la fixe à 22 px pour
   les trois, quel que soit le libellé.
   L'interlettrage passe de 0,20 à 0,14em : « ÉDITION D'ÉTÉ » mesurait 121 px,
   soit 43 % d'une carte de 282 px sur l'accueil. Il retombe à 104 px et les
   trois largeurs se resserrent autour de la même échelle. */
.card__badge {
  position: absolute;
  top: 14px;
  left: 14px;
  z-index: 2;
  background: rgba(255, 255, 255, 0.94);
  color: var(--ink);
  font-size: 9.5px;
  line-height: 1;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  padding: 6px 10px;
  border-radius: var(--radius);
}

.card__quick {
  position: absolute;
  left: 12px;
  right: 12px;
  bottom: 12px;
  z-index: 3;
  padding: 13px;
  font-size: 10.5px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  background: rgba(255, 255, 255, 0.96);
  color: var(--ink);
  border-radius: var(--radius);
  opacity: 0;
  transform: translateY(10px);
  transition: opacity 0.35s var(--ease), transform 0.35s var(--ease),
    background 0.25s;
}

.card:hover .card__quick,
.card__quick:focus-visible {
  opacity: 1;
  transform: translateY(0);
}

.card__quick:hover {
  background: var(--ink);
  color: #fff;
}

/* Sur un écran tactile, :hover ne se déclenche pas — ou reste collé après un
   tap. Le bouton d'ajout était donc invisible et inatteignable sur téléphone
   et tablette : le seul chemin vers le panier passait par la fiche produit.
   `hover: none` cible exactement ces appareils ; il y est posé d'emblée, sans
   toucher au dévoilement au survol des machines à pointeur. */
@media (hover: none) {
  .card__quick {
    opacity: 1;
    transform: none;
  }
}

.card__head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 14px;
}

/* Titre sur une seule ligne : nom et indice ne se séparent jamais.
   `min-width: 0` est indispensable sur les deux niveaux — un élément flex vaut
   `min-width: auto` par défaut, ce qui l'empêche de descendre sous la largeur
   de son texte. Sans lui, le titre déborderait de la carte au lieu de se
   terminer en points de suspension. */
.card__name {
  font-family: var(--font-display);
  font-size: 24px;
  line-height: 1.2;
  flex: 1 1 auto;
  min-width: 0;
}

.card__name > a {
  display: flex;
  /* Les deux blocs n'ont pas la même taille : c'est la ligne de base qui les
     aligne, pas le haut ni le centre. */
  align-items: baseline;
  /* Écart resserré à 0,18em (contre 0,32) : le cadratin porte déjà sa propre
     chasse, un blanc large de part et d'autre ne faisait que repousser
     l'indice hors du cadre. Écart de COLONNE seulement — la valeur de rangée
     reste à zéro pour que les deux lignes, en cas de repli, se suivent à leur
     interligne naturel. */
  gap: 0 0.18em;
  /* Dernier recours quand la ligne ne tient pas : l'indice passe ENTIER à la
     ligne suivante, au lieu d'être rogné. C'est la contrepartie assumée du
     « jamais de points de suspension sur l'indice ». */
  flex-wrap: wrap;
  min-width: 0;
}

/* Le nom du parfum est l'information principale : il ne se coupe qu'en tout
   dernier recours. Il porte quand même l'ellipse, pour le cas d'une carte plus
   étroite que lui — sur la vitrine de l'accueil en quatre colonnes, sous
   1300 px. */
.card__nom {
  flex: 0 1 auto;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* L'indice, discret : même police que le nom, 0,66 de sa taille (contre 0,72).
   La valeur étant relative, elle suit le nom en bureau (24 → 15,8 px) comme en
   mobile (16 → 10,6 px). L'encre adoucie fait le reste, sans changer de
   graisse — 7,21:1 sur le papier, bien au-delà du seuil.

   `flex: 0 0 auto` : il ne se réduit plus. Il n'a plus non plus ni `overflow`
   ni `text-overflow` — il ne peut donc PAS être tronqué, ni au milieu d'un mot
   ni par des points de suspension. S'il ne tient pas, `flex-wrap` du lien le
   fait passer entier à la ligne suivante. */
.card__indice {
  flex: 0 0 auto;
  white-space: nowrap;
  font-size: 0.66em;
  color: var(--ink-soft);
}

.card__price {
  font-size: 13px;
  color: var(--ink-soft);
  white-space: nowrap;
  letter-spacing: 0.04em;
}

/* Famille et humeur sont la donnée qu'on balaie du regard pour trier une
   grille. Elles étaient en --ink-faint (3,29:1, sous le seuil AA) et étirées à
   0,20em : « FLORAL · ENVELOPPANT ET PRÉCIEUX » couvrait 269 px et se lisait
   lettre à lettre. En --ink-soft (7,21:1) et à 0,14em elle retombe à 240 px et
   se lit d'un bloc.
   Elle reste subordonnée par la TAILLE — 10,5 px contre 24 px pour le nom et
   14 px pour l'accroche : c'est l'échelle qui porte la hiérarchie, pas le
   délavage. */
/* Deux libellés qui se suivent : la famille, puis la contenance. Ils partagent
   exactement le même réglage — même taille, même interlettrage, même encre —
   pour se lire comme un seul bloc de spécifications sous le nom. Seules les
   marges diffèrent : elles resserrent les deux lignes entre elles, puis
   ménagent l'écart d'origine avant l'accroche. */
.card__meta,
.card__size {
  font-size: 10.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

.card__meta {
  margin: 6px 0 3px;
}

.card__size {
  margin: 0 0 9px;
}

.card__short {
  font-size: 14px;
  color: var(--ink-soft);
  line-height: 1.6;
  /* Marge nulle : ces 12 px avaient été posés ici pour garantir un écart
     au-dessus du filet « Indice parfum », dont la marge haute était passée en
     `auto`. L'indice a rejoint le nom du produit, la description ferme
     désormais la carte — cette marge n'aurait plus servi qu'à décaler le bas
     des cartes. */
  margin: 0;
  max-width: 38ch;
  /* Une seule ligne partout. La hauteur réservée de deux lignes n'a plus lieu
     d'être : chaque accroche occupe exactement une ligne, les cartes sont donc
     homogènes par construction. Quand la carte est trop étroite, le texte se
     termine en points de suspension au lieu de passer à la ligne.
     `text-align: left` annule le `justify` de la règle groupée plus haut
     (.footer__about, .step p, .card__short, .bundle p) : justifier une ligne
     unique tronquée n'a aucun sens. */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  text-align: left;
}

/* --- indice parfum --------------------------------------------------------
   Une seule information : la note dominante. Ni pastille, ni icône, ni
   couleur d'accent — l'intitulé reprend exactement les réglages des petits
   libellés du site (.card__meta, .eyebrow), la valeur reste en texte courant. */
.indice {
  margin: 0;
  text-align: left;
}

.indice__label {
  display: block;
  font-size: 10px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.indice__valeur {
  display: block;
  color: var(--ink);
}

/* Sur la carte, l'indice ferme le bloc : un filet fin le détache du texte de
   présentation sans ajouter de cadre.

   `margin-top: auto` le colle au bas de la carte. .card étant une colonne flex
   et les cartes d'une même ligne étant étirées à la même hauteur par la grille,
   les filets se retrouvent tous à la même altitude — y compris quand une carte
   a une ligne de texte de plus que sa voisine. C'est le cas entre 1041 et
   1150 px : à 241 px de large, « FLORAL · ENVELOPPANT ET PRÉCIEUX » (255 px)
   passe sur deux lignes alors que les trois autres tiennent sur une.
   L'écart minimal au-dessus du filet est tenu par la marge basse de l'accroche.
   Cette règle valait déjà sur téléphone ; elle est simplement remontée ici pour
   valoir aussi en bureau et en tablette. */
.indice--carte {
  margin-top: auto;
  padding-top: 11px;
  border-top: 1px solid var(--line-soft);
}

.indice--carte .indice__valeur {
  font-size: 13.5px;
  margin-top: 3px;
}

/* Sur la fiche, l'information respire davantage et la valeur passe dans la
   police de titrage, comme le prix juste au-dessus. */
.indice--fiche {
  margin: -6px 0 26px;
}

.indice--fiche .indice__valeur {
  font-family: var(--font-display);
  font-size: 21px;
  line-height: 1.25;
  margin-top: 6px;
}

/* --- édito / bandeau image ------------------------------------------------ */

.split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: center;
  gap: clamp(32px, 6vw, 88px);
}

.split__media {
  aspect-ratio: 4 / 5;
  overflow: hidden;
  border-radius: var(--radius);
  background: var(--paper-2);
}

.split__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 38%;
}

.split__body h2 {
  font-size: clamp(30px, 4vw, 50px);
  margin-bottom: 22px;
}

.split--reverse .split__media {
  order: 2;
}

/* Variante à photo réduite (page « La maison ») : le texte y porte le propos,
   l'image l'accompagne. La colonne passe de la moitié de la largeur à ~35 %,
   plutôt qu'un max-width sur le média — ça éviterait de laisser un vide entre
   la photo rétrécie et le texte, la grille se resserre d'elle-même.
   .split--reverse déplace le média en deuxième colonne par « order », c'est
   donc la colonne étroite qui doit changer de côté. */
/* Variante à photo réduite (page « La maison ») : la colonne image passe de la
   moitié de la largeur à ~34 %, le texte prend le reste. */
.split--compact {
  grid-template-columns: minmax(0, 0.55fr) minmax(0, 1fr);
}

.split--compact.split--reverse {
  grid-template-columns: minmax(0, 1fr) minmax(0, 0.55fr);
}

/* Photo entière : ni cadre au ratio imposé, ni hauteur dictée par le texte.
   La case prend la largeur de sa colonne et l'image y déroule sa hauteur
   naturelle — rien n'est rogné. Le ratio des deux photos étant très différent
   (0,81 et 0,55), leurs hauteurs le sont aussi : c'est voulu.
   L'aplat de fond disparaît : la case épouse exactement l'image, il ne se
   verrait jamais, et il masquerait un défaut de chargement au lieu de le
   laisser apparaître. */
.split--compact .split__media {
  aspect-ratio: auto;
  background: none;
}

.split--compact .split__media img {
  width: 100%;
  height: auto;
}

/* --- rituel / étapes ------------------------------------------------------ */

.steps {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(24px, 3vw, 48px);
  counter-reset: step;
}

.step {
  border-top: 1px solid var(--line);
  padding-top: 22px;
}

.step::before {
  counter-increment: step;
  content: '0' counter(step);
  display: block;
  font-family: var(--font-display);
  font-size: 34px;
  color: var(--bronze);
  margin-bottom: 10px;
  line-height: 1;
}

.step h3 {
  font-size: 22px;
  margin-bottom: 8px;
}

.step p {
  font-size: 14.5px;
  color: var(--ink-soft);
  margin: 0;
}

/* --- coffret -------------------------------------------------------------- */

.bundle {
  background: var(--ink);
  color: var(--paper);
  border-radius: var(--radius);
  padding: clamp(38px, 5vw, 72px);
  display: grid;
  grid-template-columns: 1.2fr 1fr;
  gap: clamp(28px, 4vw, 64px);
  align-items: center;
}

.bundle h2 {
  font-size: clamp(28px, 3.6vw, 44px);
  margin-bottom: 16px;
}

.bundle p {
  color: rgba(246, 243, 238, 0.72);
  margin-bottom: 26px;
}

.bundle__price {
  font-family: var(--font-display);
  font-size: 34px;
  margin-bottom: 20px;
}

.bundle__eyebrow {
  color: rgba(246, 243, 238, 0.55);
}

/* --- pack duo : colonne de sélection dans le cadre sombre ------------------
   Le bloc réutilise .field et .btn ; seules les couleurs sont reprises pour
   tenir sur le fond encre, la géométrie reste celle des formulaires du site. */
.duo {
  display: grid;
  gap: 16px;
  align-content: center;
}

.duo .field {
  margin-bottom: 0;
}

/* Le Pack Duo n'utilise plus de <select> natif : ses anciennes règles (libellé,
   champ, options) sont remplacées par le composant .dselect ci-dessous. */

/* Le bouton reste visible mais clairement inactif tant que la sélection est
   incomplète — le clic est bloqué en JS, l'état est annoncé par aria-disabled. */
.duo [data-duo-order][aria-disabled='true'] {
  opacity: 0.45;
  cursor: not-allowed;
}

/* --- aperçu du Pack Duo ---------------------------------------------------
   Placé dans la colonne de texte du .bundle : la grille à deux colonnes reste
   inchangée. */
.duo__apercu {
  display: grid;
  gap: 12px;
  margin-top: 24px;
}

/* Deux colonnes strictement égales. minmax(0, 1fr) et non 1fr : une piste
   « 1fr » se dimensionne au minimum sur le contenu et une image large pourrait
   la faire déborder de sa moitié. */
.duo__apercu--paire {
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  align-items: start;
}

.duo__apercu img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--radius);
}

/* Les fichiers produit vont de 0,595 à 0,806 de ratio : à largeur égale, leurs
   hauteurs s'écartaient de 88 px et les deux photos n'étaient jamais alignées.
   Le cadre impose donc un format unique, et object-fit: cover y inscrit la
   photo sans jamais la déformer — elle est recadrée, pas étirée.
   3/4 est le ratio de six des onze fichiers : ceux-là ne perdent rien du tout,
   le recadrage ne joue que sur les autres.
   Ne s'applique qu'à la paire : la photo officielle du pack, seule dans son
   cadre, garde son format d'origine. */
.duo__apercu--paire img {
  aspect-ratio: 3 / 4;
  height: 100%;
  object-fit: cover;
  object-position: center;
}

/* --- sélecteur du Pack Duo (composant) ------------------------------------
   Un <select> natif ne peut pas afficher deux styles dans son champ fermé, et
   un calque posé par-dessus finit par déborder. D'où ce composant : un bouton
   qui EST le champ — nom, tiret, indice et flèche vivent tous à l'intérieur,
   rien ne peut en sortir. Géométrie et couleurs reprises de .field pour que le
   champ soit indistinguable des autres du site. */
.dselect__label {
  display: block;
  font-size: 10.5px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(246, 243, 238, 0.6);
  margin-bottom: 8px;
}

.dselect {
  position: relative;
}

.dselect__champ {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  /* Mêmes valeurs que .field select : la hauteur ne bouge pas. */
  padding: 14px 16px;
  font: inherit;
  font-size: 15px;
  text-align: left;
  background: rgba(246, 243, 238, 0.06);
  border: 1px solid rgba(246, 243, 238, 0.28);
  border-radius: var(--radius);
  color: var(--paper);
  transition: border-color 0.25s;
}

.dselect__champ:hover,
.dselect__champ:focus-visible,
.dselect.is-open .dselect__champ {
  outline: none;
  border-color: var(--paper);
}

/* Le texte occupe toute la largeur restante et se coupe proprement : c'est ce
   conteneur qui borne le contenu, la flèche garde toujours sa place. */
.dselect__texte {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: baseline;
  gap: 8px;
  overflow: hidden;
  white-space: nowrap;
}

/* Le nom cède en premier s'il manque de place ; l'indice reste lisible et le
   nom complet demeure visible dans la liste. */
.dselect__nom {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  color: var(--paper);
}

.dselect__nom--vide {
  color: rgba(246, 243, 238, 0.55);
}

.dselect__sep {
  flex: 0 0 auto;
  color: rgba(246, 243, 238, 0.4);
}

/* Plus discret que le nom : un cran plus petit et moins contrasté. */
.dselect__indice {
  flex: 0 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  font-size: 13px;
  color: rgba(246, 243, 238, 0.62);
}

/* Chevron dessiné en CSS : aucune icône ni police supplémentaire. */
.dselect__fleche {
  flex: 0 0 auto;
  width: 8px;
  height: 8px;
  margin-left: 2px;
  border-right: 1px solid rgba(246, 243, 238, 0.7);
  border-bottom: 1px solid rgba(246, 243, 238, 0.7);
  transform: translateY(-2px) rotate(45deg);
  transition: transform 0.25s var(--ease);
}

.dselect.is-open .dselect__fleche {
  transform: translateY(1px) rotate(-135deg);
}

.dselect__liste {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  right: 0;
  z-index: 5;
  margin: 0;
  padding: 6px 0;
  list-style: none;
  max-height: 280px;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  background: #211e1c;
  border: 1px solid rgba(246, 243, 238, 0.22);
  border-radius: var(--radius);
  box-shadow: 0 18px 44px rgba(0, 0, 0, 0.42);
}

/* « display: flex » plus bas l'emporterait sur le display:none du navigateur :
   sans cette ligne, l'attribut hidden n'aurait aucun effet. */
.dselect__liste[hidden] {
  display: none;
}

.dselect__option {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 11px 16px;
  font-size: 14.5px;
  color: var(--paper);
  cursor: pointer;
  transition: background 0.2s;
}

.dselect__option:hover,
.dselect__option:focus-visible {
  outline: none;
  background: rgba(246, 243, 238, 0.09);
}

.dselect__option[aria-selected='true'] {
  background: rgba(246, 243, 238, 0.06);
}

.dselect__option .dselect__nom {
  flex: 0 1 auto;
}

.duo .duo__note {
  margin: 0;
  font-size: 12px;
  letter-spacing: 0.08em;
  color: rgba(246, 243, 238, 0.6);
  text-align: center;
}

.duo .duo__ship {
  margin: 0;
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(246, 243, 238, 0.82);
  text-align: center;
}

.collection__intro {
  margin-top: 10px;
  max-width: 56ch;
  color: var(--ink-faint);
}

.bundle__list {
  list-style: none;
  margin: 0;
  padding: 0;
  border-top: 1px solid rgba(246, 243, 238, 0.18);
}

.bundle__list li {
  display: flex;
  justify-content: space-between;
  gap: 16px;
  padding: 13px 0;
  border-bottom: 1px solid rgba(246, 243, 238, 0.18);
  font-size: 13px;
  letter-spacing: 0.06em;
}

.bundle__list span {
  color: rgba(246, 243, 238, 0.55);
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}

/* --- newsletter ----------------------------------------------------------- */

.newsletter {
  background: var(--paper-2);
  text-align: center;
}

/* Sur la page collection, ce bloc suit immédiatement onze parfums. Son fond
   --paper-2 ne se détache du papier que de 1,08:1 : sans limite dessinée, il
   se lisait comme la suite de la grille plutôt que comme une offre à part.
   Deux filets le referment en bande. C'est le même procédé que « Vous aimerez
   aussi » sur la fiche produit — aucune couleur nouvelle, aucun bloc ajouté.
   Modificateur, et pas `.newsletter` tout court : la même classe sert aussi
   sur « À propos » et « Nos collections », où le bloc ferme la page et n'a
   rien à délimiter. */
.newsletter--offre {
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}

.newsletter h2 {
  font-size: clamp(28px, 3.6vw, 44px);
  margin-bottom: 14px;
}

.newsletter p {
  color: var(--ink-soft);
  max-width: 52ch;
  margin: 0 auto 28px;
}

.form-inline {
  display: flex;
  gap: 10px;
  max-width: 480px;
  margin: 0 auto;
}

.form-inline input {
  flex: 1;
  min-width: 0;
  padding: 15px 18px;
  font: inherit;
  font-size: 14px;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  color: var(--ink);
}

.form-inline input:focus {
  outline: none;
  border-color: var(--ink);
}

.form-note {
  font-size: 12px;
  color: var(--ink-faint);
  margin: 16px 0 0;
}

/* --- pied de page --------------------------------------------------------- */

/* Hauteur resserrée : le retrait haut passe de 56–92 px à 40–60 px, le bas de
   32 à 24. Le pied de page respire encore, mais ne se termine plus sur une
   grande plage noire vide. */
.footer {
  background: var(--ink);
  color: rgba(246, 243, 238, 0.72);
  padding: clamp(40px, 4.5vw, 60px) 0 24px;
  font-size: 14px;
}

/* Quatre colonnes, pondérées par leur contenu : le texte de marque a besoin de
   place, la navigation tient en libellés courts, le Pack Duo doit loger son
   bouton sans le comprimer. */
.footer__grid {
  display: grid;
  grid-template-columns: 1.5fr 0.9fr 1.25fr 1.15fr;
  gap: clamp(28px, 4vw, 56px);
  /* align-items: start — sans lui les colonnes s'étirent sur la hauteur de la
     plus haute, et le bouton du Pack Duo se retrouvait poussé vers le bas.
     Les quatre démarrent maintenant exactement sur la même ligne. */
  align-items: start;
  padding-bottom: clamp(26px, 3vw, 36px);
}

/* Médaillon du pied de page, à la place du mot « AVAIRIS ». Même traitement que
   le sceau de la page À propos : le fichier est un carré, le cercle y est
   inscrit au pixel près, et le border-radius retire les coins de dégradé — sans
   lui, un carré clair se détacherait du noir. Aucune déformation : la source
   est carrée et les deux dimensions sont égales. */
/* --- logo du pied de page -------------------------------------------------
   Reprend les classes du logo de l'en-tête (.brand__dots / .brand__mark /
   .brand__tag) : même symbole, même police, même composition. Seules la taille
   et les couleurs sont adaptées au fond noir.
   inline-flex : le bloc se réduit à la largeur du mot, donc il se cale à gauche
   de la colonne tout en gardant les points centrés au-dessus, comme dans le
   hero. */
.footer__brand {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  margin-bottom: clamp(16px, 1.6vw, 22px);
}

/* Un cran au-dessus de l'en-tête (23 px) : le pied de page n'a pas la
   contrainte de hauteur d'une barre de navigation. */
.footer__brand .brand__mark {
  color: var(--paper);
  font-size: clamp(26px, 2.4vw, 32px);
  margin-bottom: 0;
}

.footer__brand .brand__tag {
  color: rgba(246, 243, 238, 0.72);
  font-size: clamp(9px, 0.8vw, 10px);
  line-height: 1;
  margin-top: 7px;
}

/* Les points sont blancs et suivent l'échelle du nom. */
.footer__brand .brand__dots {
  gap: 4px;
  margin-bottom: 11px;
}

.footer__brand .brand__dots span {
  background: var(--paper);
}

.footer__brand .brand__dots span:first-child {
  width: 16px;
  height: 16px;
}

.footer__brand .brand__dots span:last-child {
  width: 7px;
  height: 7px;
}

/* Conservé : le pied de page n'utilise plus cette règle, mais le menu mobile
   s'appuie toujours sur .brand__mark. */
.footer .brand__mark {
  color: var(--paper);
  font-size: 24px;
  margin-bottom: 14px;
}

/* --- le paragraphe sous le logo -------------------------------------------
   Il figurait dans la règle groupée `text-align: justify` + `hyphens: auto`
   tout en haut de la feuille. C'est cette césure automatique qui coupait des
   mots en fin de ligne et y ajoutait un tiret. Il en a été retiré ; les autres
   blocs de la liste la conservent.

   Trois verrous, chacun pour une façon différente de casser un mot :
     hyphens: none      — plus de césure automatique, donc plus de tiret ajouté
     word-break: keep-all — plus de coupure APRÈS un trait d'union existant :
                            un mot composé bascule entier à la ligne suivante
     overflow-wrap: normal — plus de coupure forcée d'un mot trop long

   Ce dernier point ne fait courir aucun risque de débordement ici : le mot le
   plus long du paragraphe, « fragrances », mesure 76 px, pour une colonne qui
   ne descend jamais sous 238 px (le plafond de 34ch est atteint partout, même
   sur un écran de 320 px).

   Le texte reste JUSTIFIÉ, sur décision explicite. C'est un attelage inhabituel
   — justification et césure vont normalement de pair — et il a un coût mesuré :
   dans une colonne de 238 px, la première ligne se remplit à 76-82 % et sa
   justification ajoute jusqu'à 19 px de blanc par espace, contre 4 px pour un
   espace normal. Les lignes suivantes, remplies à 83-99 %, n'en ajoutent que
   0,5 à 10 px. C'est donc la première ligne qui portera l'écart visible.

   `word-break: keep-all` et non `break-all` : le premier interdit les coupures,
   le second les force. Les deux se ressemblent mais font l'inverse. */
.footer__about {
  max-width: 34ch;
  line-height: 1.75;
  text-align: justify;
  -webkit-hyphens: none;
  hyphens: none;
  word-break: keep-all;
  overflow-wrap: normal;
}

/* Les trois intitulés de colonne sont calés sur le haut du logo : line-height
   à 1 et marge nulle en haut, sinon l'interligne héritée du corps de texte les
   décalait de quelques pixels vers le bas et les colonnes semblaient basses. */
.footer h4 {
  font-family: var(--font-sans);
  font-size: 11px;
  line-height: 1;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--paper);
  margin: 0 0 14px;
  font-weight: 400;
}

.footer ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 9px;
}

/* Hover discret : un simple éclaircissement, sans soulignement ni déplacement. */
.footer a {
  transition: color 0.25s var(--ease);
}

.footer a:hover {
  color: var(--paper);
}

/* --- « Nous suivre » : deux logos ------------------------------------------
   La colonne ne porte plus de <ul> : deux liens iconiques, alignés sur une
   ligne. L'écart de 6 px est celui des BOÎTES ; les dessins étant centrés dans
   des cibles de 44 px, l'espace visible entre les deux logos est de 26 px.

   44 px, c'est le minimum recommandé pour une cible tactile — le logo, lui,
   reste à 24 px. Les marges négatives rattrapent exactement ce rembourrage :
   sans elles, le premier logo serait décalé de 10 px vers la droite et de
   10 px vers le bas par rapport aux colonnes voisines. Le bord gauche des
   logos tombe ainsi pile sur celui de « NOUS SUIVRE ». */
.footer__socials {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: -10px 0 0 -10px;
}

.footer__social {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  color: rgba(246, 243, 238, 0.78);
  /* .footer a n'anime que `color` : sans ce rappel, le léger soulèvement au
     survol serait sec. Les durées restent celles du site. */
  transition: color 0.25s var(--ease), transform 0.25s var(--ease);
}

.footer__social svg {
  width: 24px;
  height: 24px;
  display: block;
}

/* Champagne du bouton Pack Duo (#c9aa68) : la couleur existe déjà dans la
   palette, aucun ton nouveau n'est introduit. Le soulèvement d'un pixel est
   celui de .btn--or ; l'agrandissement reste sous le seuil du remarquable. */
.footer__social:hover,
.footer__social:focus-visible {
  color: #c9aa68;
  transform: translateY(-1px) scale(1.04);
}

/* Les deux arguments du Pack Duo ne sont pas des liens : ils reprennent la
   couleur de texte du pied de page, sans effet de survol. */
.footer__pack ul {
  margin-bottom: 18px;
}

/* Le bouton standard fait 15/30 : trop massif dans une colonne de pied de page,
   il déborderait la piste à 1024 px. */
.footer__cta {
  padding: 13px 22px;
  font-size: 10.5px;
  letter-spacing: 0.16em;
}

/* La ligne se rapproche du contenu (26 → 18 px) et les deux mentions sont
   centrées l'une par rapport à l'autre : elles n'ont pas la même longueur, sans
   align-items elles se calaient sur des lignes de base différentes. */
.footer__bottom {
  border-top: 1px solid rgba(246, 243, 238, 0.16);
  padding-top: 18px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px 14px;
  font-size: 12px;
  line-height: 1.5;
  color: rgba(246, 243, 238, 0.5);
}

/* Lien vers les conditions générales de vente. Sur ce site `a` n'a ni couleur
   ni décoration propre : sans ce rappel, le libellé serait exactement le même
   texte gris que la mention qui le suit, et rien ne dirait qu'il se clique.
   Le trait est posé à 0,3 d'opacité — visible, jamais bruyant — et se remplit
   au survol, où .footer a éclaircit déjà la couleur du texte. Ni taille, ni
   graisse, ni interlettrage ne changent : le pied de page garde sa ligne. */
.footer__legal {
  text-decoration: underline;
  text-decoration-color: rgba(246, 243, 238, 0.3);
  text-underline-offset: 3px;
  transition: color 0.25s var(--ease),
    text-decoration-color 0.25s var(--ease);
}

.footer__legal:hover,
.footer__legal:focus-visible {
  text-decoration-color: currentColor;
}

/* --- fil d'ariane --------------------------------------------------------- */

.crumbs {
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-faint);
  padding: 26px 0;
}

.crumbs a:hover {
  color: var(--ink);
}

/* --- page collection ------------------------------------------------------ */

.page-head {
  padding: clamp(36px, 5vw, 64px) 0 clamp(28px, 4vw, 44px);
  text-align: center;
}

.page-head h1 {
  font-size: clamp(38px, 6vw, 68px);
  margin-bottom: 16px;
}

/* Variante resserrée, posée sur la seule page « Tous les parfums ». Là-bas
   l'en-tête est suivi d'une barre de filtres puis d'une grille : trois blocs
   qui doivent se lire d'un trait. Ailleurs (à propos, contact, FAQ) l'en-tête
   ouvre un texte long et garde sa respiration d'origine.
   Ces deux règles doivent rester APRÈS `.page-head h1` : même spécificité
   (0,1,1), c'est l'ordre du fichier qui tranche. */
.page-head--serre {
  padding-bottom: clamp(20px, 2.6vw, 30px);
}

.page-head--serre h1 {
  margin-bottom: 12px;
}

/* Sceau de marque : le fichier source est un carré, le médaillon beige y est
   inscrit au pixel près — le border-radius retire les quatre coins de dégradé
   qui restent. Un aplat de la teinte du médaillon derrière évite le carré
   sombre le temps que la photo se charge. */
.brand-seal {
  width: clamp(104px, 15vw, 148px);
  aspect-ratio: 1;
  margin: 0 auto clamp(20px, 3vw, 30px);
  border-radius: 50%;
  overflow: hidden;
  background: #cabb9e;
  box-shadow: 0 14px 38px rgba(20, 18, 16, 0.14);
}

/* Quand le sceau sert de <h1>, la règle .page-head h1 est plus spécifique que
   .brand-seal et écraserait sa marge basse. */
.page-head h1.brand-seal {
  margin-bottom: clamp(24px, 3.4vw, 34px);
}

.brand-seal img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.page-head p {
  color: var(--ink-soft);
  max-width: 56ch;
  margin: 0 auto;
}

.toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 16px;
  /* Resserré : la barre est un rail de navigation, pas une section. Le regard
     doit passer du titre aux produits sans détour. 18 → 14 px de rembourrage,
     et l'écart avant la grille de 52 à 34 px au plus. */
  padding: 14px 0;
  border-top: 1px solid var(--line-soft);
  border-bottom: 1px solid var(--line-soft);
  margin-bottom: clamp(22px, 2.8vw, 34px);
}

.filters {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

/* La bordure était en --line : 1,30:1 sur le papier, autant dire invisible.
   Les puces inactives ne se lisaient pas comme des boutons, seulement comme du
   texte posé, et l'écart avec la puce active tenait au seul aplat noir.
   --ink-faint monte à 3,29:1, le minimum recommandé pour la limite d'un
   élément d'interface. Le libellé, lui, reste en --ink-soft : un contour, pas
   un cadre. */
.chip {
  padding: 9px 17px;
  font-size: 10.5px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  border: 1px solid var(--ink-faint);
  border-radius: 999px;
  color: var(--ink-soft);
  transition: all 0.25s var(--ease);
}

.chip:hover {
  border-color: var(--ink);
  color: var(--ink);
}

.chip[aria-pressed='true'] {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--paper);
}

/* « 11 PARFUMS » se posait en --ink-faint : 3,29:1 sur le papier, sous le seuil
   AA de 4,5:1 pour du petit texte. En --ink-soft il passe à 7,21:1. La taille
   monte d'un demi-point et l'interlettrage se resserre — à 0,18em, onze
   caractères capitales se lisaient lettre à lettre plutôt que d'un bloc. */
.count {
  font-size: 11.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

/* --- page produit --------------------------------------------------------- */

.pdp {
  display: grid;
  grid-template-columns: 1.05fr 1fr;
  gap: clamp(32px, 5vw, 80px);
  align-items: start;
  padding-bottom: clamp(56px, 7vw, 96px);
}

.pdp__gallery {
  display: grid;
  gap: 12px;
  position: sticky;
  top: 100px;
}

.pdp__main {
  aspect-ratio: 4 / 5;
  overflow: hidden;
  border-radius: var(--radius);
  background: var(--paper-2);
}

.pdp__main img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 34%;
}

.pdp__thumbs {
  display: flex;
  gap: 10px;
}

.pdp__thumbs button {
  width: 76px;
  aspect-ratio: 4 / 5;
  overflow: hidden;
  border-radius: var(--radius);
  border: 1px solid transparent;
  opacity: 0.6;
  transition: opacity 0.25s, border-color 0.25s;
  padding: 0;
}

.pdp__thumbs button[aria-pressed='true'],
.pdp__thumbs button:hover {
  opacity: 1;
  border-color: var(--ink);
}

.pdp__thumbs img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.pdp__info h1 {
  font-size: clamp(34px, 4.6vw, 56px);
  margin: 6px 0 12px;
}

.pdp__price {
  font-size: 20px;
  color: var(--ink);
  letter-spacing: 0.04em;
  margin-bottom: 22px;
}

.pdp__price small {
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-left: 10px;
}

.pdp__desc {
  color: var(--ink-soft);
  line-height: 1.8;
  margin-bottom: 28px;
}

.qty {
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  height: 51px;
}

.qty button {
  width: 44px;
  height: 100%;
  font-size: 17px;
  color: var(--ink-soft);
  transition: color 0.2s;
}

.qty button:hover {
  color: var(--ink);
}

.qty span {
  min-width: 30px;
  text-align: center;
  font-size: 14px;
}

.pdp__buy {
  display: flex;
  gap: 12px;
  margin-bottom: 14px;
}

.pdp__buy .btn {
  flex: 1;
}

.shipping-note {
  font-size: 12.5px;
  color: var(--ink-faint);
  margin-bottom: 34px;
}

.accordion {
  border-top: 1px solid var(--line);
}

.accordion__item {
  border-bottom: 1px solid var(--line);
}

.accordion__trigger {
  width: 100%;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
  padding: 18px 0;
  text-align: left;
  font-size: 11px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
}

.accordion__trigger i {
  font-style: normal;
  font-size: 17px;
  color: var(--ink-faint);
  transition: transform 0.3s var(--ease);
}

.accordion__item.is-open .accordion__trigger i {
  transform: rotate(45deg);
}

.accordion__panel {
  display: none;
  padding: 0 0 22px;
  color: var(--ink-soft);
  font-size: 14.5px;
  line-height: 1.75;
}

.accordion__item.is-open .accordion__panel {
  display: block;
}

.notes {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 14px;
}

.notes li {
  display: grid;
  grid-template-columns: 84px 1fr;
  gap: 16px;
  align-items: baseline;
}

.notes strong {
  font-size: 10.5px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-faint);
  font-weight: 400;
}

/* --- formulaire de commande ----------------------------------------------
   Au-dessus du tiroir panier (100) et du panneau de menu (200), puisqu'il
   s'ouvre par-dessus l'un ou l'autre. Reprend les composants du site : .field
   pour les champs, .btn pour l'action, mêmes bordures et mêmes couleurs. */

.order-overlay {
  position: fixed;
  inset: 0;
  background: rgba(20, 18, 16, 0.55);
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.35s var(--ease), visibility 0.35s;
  z-index: 300;
}

.order-overlay.is-open {
  opacity: 1;
  visibility: visible;
}

.order {
  position: fixed;
  inset: 0;
  z-index: 310;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(16px, 4vw, 40px);
  visibility: hidden;
  pointer-events: none;
}

.order.is-open {
  visibility: visible;
  pointer-events: auto;
}

.order__panel {
  width: min(560px, 100%);
  max-height: 100%;
  display: flex;
  flex-direction: column;
  background: var(--paper);
  border-radius: var(--radius);
  box-shadow: 0 30px 90px rgba(20, 18, 16, 0.28);
  transform: translateY(14px);
  opacity: 0;
  transition: transform 0.4s var(--ease), opacity 0.35s var(--ease);
}

.order.is-open .order__panel {
  transform: none;
  opacity: 1;
}

.order__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 22px 26px;
  border-bottom: 1px solid var(--line-soft);
  flex: 0 0 auto;
}

.order__head h3 {
  font-size: 11px;
  font-family: var(--font-sans);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  margin: 0;
}

.order__close {
  width: 44px;
  height: 44px;
  margin: -10px -12px -10px 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 24px;
  line-height: 1;
  color: var(--ink-faint);
  border-radius: 50%;
  transition: background 0.25s, color 0.25s;
}

.order__close:hover {
  background: var(--paper-2);
  color: var(--ink);
}

/* Le corps défile, l'en-tête reste : sur un écran bas, le formulaire complet
   ne tient pas et la croix de fermeture doit rester atteignable. */
.order__body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: 24px 26px 28px;
}

.order__recap {
  background: var(--paper-2);
  border-radius: var(--radius);
  padding: 20px 22px;
  margin-bottom: 26px;
}

.order__recap h4 {
  font-family: var(--font-sans);
  font-size: 10.5px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin: 0 0 14px;
}

.recap {
  list-style: none;
  margin: 0 0 14px;
  padding: 0;
}

.recap li {
  display: flex;
  align-items: baseline;
  gap: 10px;
  font-size: 14px;
  padding: 5px 0;
}

.recap__nom {
  flex: 1 1 auto;
}

.recap__qty {
  color: var(--ink-faint);
  white-space: nowrap;
}

.recap__prix {
  white-space: nowrap;
}

.recap__unit {
  font-size: 12px;
  color: var(--ink-faint);
  padding-top: 0;
}

.recap__ligne {
  display: flex;
  justify-content: space-between;
  gap: 16px;
  font-size: 13.5px;
  padding: 6px 0;
}

.recap__total {
  border-top: 1px solid var(--line);
  margin-top: 8px;
  padding-top: 12px;
  font-size: 16px;
}

.recap__total span:last-child {
  font-family: var(--font-display);
  font-size: 20px;
}

.order__row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0 16px;
}

.field__opt {
  color: var(--ink-faint);
  letter-spacing: 0.08em;
  text-transform: none;
}

/* --- choix du canal d'envoi ------------------------------------------------
   Un <fieldset> n'a de bordure et de rembourrage que parce que le navigateur
   lui en donne par défaut : on les remet à zéro. `min-width: 0` corrige un
   comportement ancien du fieldset, qui refuse sinon de descendre sous la
   largeur intrinsèque de son contenu et fait déborder la modale sur mobile. */
.order__envoi {
  border: 0;
  margin: 0 0 20px;
  padding: 0;
  min-width: 0;
}

/* La question reprend exactement les réglages des libellés de champ : même
   taille, même interlettrage, même encre. Le groupe se lit donc comme une
   quatrième question du formulaire, pas comme un bloc rapporté. */
.order__envoi legend {
  padding: 0;
  margin-bottom: 10px;
  font-size: 10.5px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.envoi {
  display: grid;
  gap: 10px;
}

.envoi__choix {
  display: block;
  cursor: pointer;
}

/* Le bouton radio reste dans le parcours clavier : `display: none` l'en
   sortirait. Il est réduit à rien et rendu transparent, en position absolue
   pour ne pas occuper de place. */
.envoi__choix input {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
  pointer-events: none;
}

.envoi__carte {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px 16px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: transparent;
  transition: border-color 0.25s var(--ease), background 0.25s var(--ease);
}

.envoi__choix:hover .envoi__carte {
  border-color: var(--ink-faint);
}

/* Sélection : trois signes concordants plutôt qu'un seul — la bordure passe à
   l'encre (1,30:1 → 15,8:1 contre le papier), le fond s'éclaircit du papier au
   blanc, et la pastille se remplit. Aucune couleur nouvelle. */
.envoi__choix input:checked + .envoi__carte {
  border-color: var(--ink);
  background: var(--card);
}

/* Le focus clavier doit se voir même sans changer la sélection : le radio est
   invisible, c'est donc la carte qui porte l'anneau. */
.envoi__choix input:focus-visible + .envoi__carte {
  outline: 2px solid var(--ink);
  outline-offset: 2px;
}

.envoi__icone {
  flex: 0 0 auto;
  display: flex;
  color: var(--ink-faint);
  transition: color 0.25s var(--ease);
}

.envoi__icone svg {
  width: 22px;
  height: 22px;
  display: block;
}

.envoi__choix input:checked + .envoi__carte .envoi__icone {
  color: var(--ink);
}

/* `min-width: 0` : sans lui, un élément flex refuse de descendre sous la
   largeur de son texte, et la description pousserait la carte hors de la
   modale sur un écran étroit. */
.envoi__texte {
  flex: 1 1 auto;
  min-width: 0;
  display: grid;
  gap: 3px;
}

.envoi__nom {
  font-size: 14px;
  color: var(--ink);
}

.envoi__desc {
  font-size: 12px;
  line-height: 1.45;
  color: var(--ink-soft);
}

.envoi__coche {
  flex: 0 0 auto;
  position: relative;
  width: 16px;
  height: 16px;
  border: 1px solid var(--line);
  border-radius: 50%;
  transition: border-color 0.25s var(--ease);
}

.envoi__choix input:checked + .envoi__carte .envoi__coche {
  border-color: var(--ink);
}

.envoi__choix input:checked + .envoi__carte .envoi__coche::after {
  content: '';
  position: absolute;
  inset: 3px;
  border-radius: 50%;
  background: var(--ink);
}

/* Ce que fera le bouton, dit avant qu'on l'actionne. Le texte change avec le
   canal (voir CANAUX dans main.js). */
.envoi__note {
  margin: 10px 0 0;
  font-size: 12px;
  line-height: 1.55;
  color: var(--ink-soft);
}

.order__error {
  margin: 0 0 16px;
  padding: 11px 14px;
  border-left: 2px solid var(--bronze);
  background: var(--paper-2);
  font-size: 13px;
  color: var(--ink);
}

.order__note {
  margin: 14px 0 0;
  font-size: 12px;
  line-height: 1.6;
  color: var(--ink-faint);
  text-align: center;
}

/* --- modale « commander sur Instagram » ------------------------------------
   Elle réutilise .order-overlay, .order, .order__panel, .order__head,
   .order__body et .order__note : seul le contenu propre au parcours est
   habillé ici. */
.insta__intro {
  margin: 0 0 18px;
  font-size: 14px;
  line-height: 1.65;
  color: var(--ink-soft);
}

/* Les trois étapes. Compte-rendu volontairement compact : le parcours doit se
   saisir en quelques secondes, une ligne de titre et une d'explication
   suffisent. La structure accepte une capture d'écran par étape sans être
   retouchée — il suffira d'insérer une image sous .etape__txt. */
.etapes {
  list-style: none;
  margin: 0 0 20px;
  padding: 16px 18px;
  display: grid;
  gap: 14px;
  background: var(--paper-2);
  border-radius: var(--radius);
}

.etape {
  display: flex;
  align-items: baseline;
  gap: 14px;
}

.etape__n {
  flex: 0 0 auto;
  font-family: var(--font-display);
  font-size: 13px;
  letter-spacing: 0.1em;
  color: var(--ink-faint);
}

/* `min-width: 0` : un élément flex refuse sinon de descendre sous la largeur
   de son texte, et l'étape déborderait de la modale sur un écran étroit. */
.etape__txt {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 13px;
  line-height: 1.5;
  color: var(--ink-soft);
}

.etape__txt b {
  display: block;
  font-weight: 400;
  color: var(--ink);
}

.insta__label {
  display: block;
  margin-bottom: 8px;
  font-size: 10.5px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

/* Un vrai champ de texte, en lecture seule : c'est lui le recours quand le
   presse-papiers est refusé — le client sélectionne et copie à la main.
   `resize: vertical` lui laisse la main s'il veut tout voir d'un coup. */
.insta__msg {
  width: 100%;
  display: block;
  font: inherit;
  font-size: 13px;
  line-height: 1.6;
  padding: 14px 16px;
  margin-bottom: 14px;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  color: var(--ink);
  resize: vertical;
  white-space: pre-wrap;
}

.insta__msg:focus {
  outline: none;
  border-color: var(--ink);
}

#insta-copy {
  margin-bottom: 10px;
}

/* État « copié » : le vert n'appartient pas à la palette du site. La
   confirmation passe donc par le seul retournement d'encre, comme partout
   ailleurs — fond papier, texte encre, bordure encre. */
#insta-copy.is-copie {
  background: var(--paper-2);
  border-color: var(--ink);
  color: var(--ink);
}

/* Bouton Instagram : même géométrie que .btn, l'icône en plus. .btn est déjà
   un inline-flex avec 10 px de gap — l'icône s'y place sans rien ajouter. */
.btn--insta {
  background: transparent;
  border-color: var(--ink);
  color: var(--ink);
}

.btn--insta:hover {
  background: var(--ink);
  color: var(--paper);
}

.btn--insta svg {
  width: 17px;
  height: 17px;
  flex: 0 0 auto;
}

/* Sur téléphone la modale occupe presque tout l'écran, comme demandé pour un
   formulaire de cette longueur. */
@media (max-width: 620px) {
  .order {
    padding: 0;
  }

  .order__panel {
    width: 100%;
    height: 100%;
    max-height: none;
    border-radius: 0;
  }

  .order__row {
    grid-template-columns: 1fr;
  }
}

/* --- panier (tiroir) ------------------------------------------------------ */

.overlay {
  position: fixed;
  inset: 0;
  background: rgba(20, 18, 16, 0.42);
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.4s var(--ease), visibility 0.4s;
  z-index: 90;
}

.overlay.is-open {
  opacity: 1;
  visibility: visible;
}

.drawer {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  width: min(420px, 100%);
  background: var(--paper);
  z-index: 100;
  display: flex;
  flex-direction: column;
  transform: translateX(100%);
  transition: transform 0.45s var(--ease);
  box-shadow: -20px 0 60px rgba(20, 18, 16, 0.12);
}

.drawer.is-open {
  transform: translateX(0);
}

.drawer__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 24px 26px;
  border-bottom: 1px solid var(--line-soft);
}

.drawer__head h3 {
  font-size: 11px;
  font-family: var(--font-sans);
  letter-spacing: 0.24em;
  text-transform: uppercase;
}

.drawer__close {
  font-size: 22px;
  line-height: 1;
  color: var(--ink-faint);
  transition: color 0.2s;
}

.drawer__close:hover {
  color: var(--ink);
}

.drawer__body {
  flex: 1;
  overflow-y: auto;
  padding: 8px 26px;
}

.drawer__foot {
  padding: 22px 26px 26px;
  border-top: 1px solid var(--line-soft);
  background: var(--paper-2);
}

.drawer__total {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-bottom: 6px;
  font-size: 11px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
}

.drawer__total b {
  font-family: var(--font-display);
  font-size: 26px;
  letter-spacing: 0.02em;
  font-weight: 400;
  text-transform: none;
}

.drawer__hint {
  font-size: 12px;
  color: var(--ink-faint);
  margin: 0 0 18px;
}

.line {
  display: grid;
  grid-template-columns: 74px 1fr auto;
  gap: 15px;
  padding: 20px 0;
  border-bottom: 1px solid var(--line-soft);
}

.line__media {
  aspect-ratio: 4 / 5;
  overflow: hidden;
  border-radius: var(--radius);
  background: var(--paper-2);
}

.line__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.line__name {
  font-family: var(--font-display);
  font-size: 20px;
  line-height: 1.2;
  margin-bottom: 3px;
}

.line__meta {
  font-size: 10.5px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-bottom: 9px;
}

.line__qty {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  font-size: 13px;
}

.line__qty button {
  color: var(--ink-faint);
  font-size: 15px;
  line-height: 1;
  transition: color 0.2s;
}

.line__qty button:hover {
  color: var(--ink);
}

.line__right {
  text-align: right;
  font-size: 13px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  align-items: flex-end;
}

.line__remove {
  font-size: 10.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-faint);
  text-decoration: underline;
  text-underline-offset: 3px;
}

.line__remove:hover {
  color: var(--ink);
}

.empty {
  text-align: center;
  padding: 60px 10px;
  color: var(--ink-faint);
}

.empty p {
  margin-bottom: 22px;
  font-size: 14px;
}

/* --- toast ---------------------------------------------------------------- */

.toast {
  position: fixed;
  left: 50%;
  bottom: 30px;
  transform: translate(-50%, 22px);
  background: var(--ink);
  color: var(--paper);
  padding: 13px 24px;
  border-radius: var(--radius);
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  z-index: 120;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.3s, transform 0.35s var(--ease), visibility 0.3s;
}

.toast.is-visible {
  opacity: 1;
  visibility: visible;
  transform: translate(-50%, 0);
}

/* --- contenu éditorial ---------------------------------------------------- */

.prose h2 {
  font-size: clamp(26px, 3.2vw, 38px);
  margin: 48px 0 16px;
}

.prose h3 {
  font-size: 22px;
  margin: 34px 0 10px;
}

.prose p,
.prose li {
  color: var(--ink-soft);
  line-height: 1.85;
}

.prose ul {
  padding-left: 20px;
  margin-bottom: 1em;
}

/* --- page « Conditions générales de vente » --------------------------------
   Le bloc porte .prose ET .cgv. Toute la typographie — police, corps,
   interligne 1,85, couleur, mesure de 780 px — vient de .prose et de
   .wrap--narrow, exactement comme en FAQ et à propos. .cgv n'ajoute qu'un
   réglage : celui que .prose ne couvre pas.

   La justification, elle, est déjà acquise : `.prose p` figure dans la règle
   groupée en tête de feuille, avec `hyphens: auto`. Cette césure n'est pas
   décorative — sans elle, justifier du français creuse des rivières blanches
   entre les mots — et elle sert une seconde fois ici, en donnant au moteur
   assez de points de coupure pour que la règle ci-dessous ait de quoi
   travailler.

   `text-wrap: pretty` est le seul levier qui traite VRAIMENT la ligne creuse.
   Le moteur cesse de composer ligne à ligne : il évalue le paragraphe entier
   et redistribue ses dernières lignes pour qu'aucune ne se réduise à un mot
   isolé. La déclaration n'a pas de contrepartie — là où elle n'est pas
   implémentée (Chrome < 117, Safari < 17.5, Firefox < 137), elle est ignorée
   et le rendu reste celui d'aujourd'hui, jamais dégradé.

   Elle vaut aux deux bouts de la plage : c'est sur téléphone, où la colonne
   tombe à 264 px et où une ligne ne porte plus que six ou sept mots, que le
   mot orphelin est le plus fréquent et le plus visible.

   Ce qui a été écarté, et pourquoi :
     `orphans`            ne joue que sur les coupures de page ou de colonne,
                          jamais sur la dernière ligne d'un paragraphe ;
     `white-space: nowrap` exclu par la demande, et à raison : il supprimerait
                          tout retour à la ligne et ferait déborder la page ;
     rétrécir la colonne  ne supprime pas le reste d'une division, il le
                          déplace — un paragraphe assaini, un autre gâté. La
                          mesure du site est donc conservée telle quelle, et
                          avec elle la cohérence des pages éditoriales.

   MOTS ENTIERS — aucun tiret de césure sur cette page. Trois verrous, un par
   façon de casser un mot ; c'est exactement le jeu déjà en place sur
   .footer__about, repris ici à l'identique plutôt que réinventé :

     hyphens: none        plus de césure automatique, donc plus de tiret ajouté
                          par le navigateur — c'est la règle demandée, celle
                          qui supprime « sou-mis » ;
     word-break: keep-all plus de coupure APRÈS un trait d'union DÉJÀ écrit :
                          « contactez-nous » bascule entier à la ligne suivante
                          au lieu de se rompre après le tiret. Sans ce second
                          verrou, `hyphens: none` seul laisserait passer ce
                          cas-là. C'est bien keep-all et non break-all : le
                          premier INTERDIT les coupures, le second les FORCE ;
     overflow-wrap: normal plus de coupure d'autorité d'un mot trop long.

   Ces trois règles surchargent le `hyphens: auto` de la règle groupée en tête
   de feuille. Même spécificité (0,1,1) que `.prose p` : c'est l'ordre du
   fichier qui tranche, ce bloc doit donc rester APRÈS elle.

   Aucun risque de débordement en échange : le mot le plus long de la page,
   « réglementation », mesure une centaine de pixels, et le plus long groupe
   soudé par un trait d'union, « contactez-nous », guère plus. La colonne, elle,
   ne descend jamais sous 280 px — 320 px d'écran moins deux gouttières de
   20 px. La marge est de plus du double.

   Le texte reste JUSTIFIÉ, sur demande explicite, et le prix est connu :
   justification et césure vont normalement de pair, les retirer la seconde
   élargit les espaces entre les mots. L'effet se voit surtout sur les lignes
   courtes, donc sur téléphone. `text-wrap: pretty` ci-dessus travaille
   d'ailleurs avec moins de points de coupure qu'avant — il reste utile, il est
   seulement moins efficace. */
.cgv p {
  text-wrap: pretty;
  text-align: justify;
  -webkit-hyphens: none;
  hyphens: none;
  word-break: keep-all;
  overflow-wrap: normal;
}

/* --- coordonnée cliquable --------------------------------------------------
   Une coordonnée qui S'AFFICHE et se clique : le numéro de téléphone des
   conditions générales, l'adresse e-mail de la page contact. Le libellé est la
   donnée elle-même, pas un intitulé — donc ni bouton ni .link-underline, dont
   les capitales interlettrées défigureraient une adresse et la feraient
   déborder sur mobile. Le texte garde le corps et la police du texte courant ;
   seul un trait pâle le signale comme cliquable.

   Sans cette règle, rien ne le signalerait : `a` n'a sur ce site ni couleur ni
   décoration propre. Le trait est posé en --line, se remplit en --ink au
   survol et au focus clavier. Aucun déplacement, aucun changement de corps :
   la ligne de texte ne bouge pas, l'effet reste discret.

   Une classe, et non `.prose a` : ce dernier atteindrait aussi « Voir la
   collection » sur la page à propos, qui porte déjà .link-underline et se
   retrouverait avec deux traits. */
.link-coord {
  color: var(--ink);
  text-decoration: underline;
  text-decoration-color: var(--line);
  text-underline-offset: 3px;
  transition: text-decoration-color 0.25s var(--ease);
}

.link-coord:hover,
.link-coord:focus-visible {
  text-decoration-color: var(--ink);
}

.faq__item {
  border-bottom: 1px solid var(--line);
}

.contact-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(32px, 5vw, 72px);
  align-items: start;
}

.field {
  display: grid;
  gap: 8px;
  margin-bottom: 18px;
}

.field label {
  font-size: 10.5px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.field input,
.field textarea,
.field select {
  font: inherit;
  font-size: 15px;
  padding: 14px 16px;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  color: var(--ink);
}

.field textarea {
  min-height: 150px;
  resize: vertical;
}

.field input:focus,
.field textarea:focus,
.field select:focus {
  outline: none;
  border-color: var(--ink);
}

.info-list {
  list-style: none;
  margin: 0;
  padding: 0;
  border-top: 1px solid var(--line);
}

.info-list li {
  padding: 18px 0;
  border-bottom: 1px solid var(--line);
}

.info-list strong {
  display: block;
  font-size: 10.5px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  font-weight: 400;
  color: var(--ink-faint);
  margin-bottom: 5px;
}

/* --- apparition au scroll ------------------------------------------------- */

.reveal {
  opacity: 0;
  transform: translateY(22px);
  transition: opacity 0.85s var(--ease), transform 0.85s var(--ease);
}

.reveal.is-in {
  opacity: 1;
  transform: none;
}

/* --- galerie instagram ---------------------------------------------------- */

.social-grid {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: clamp(10px, 1.2vw, 16px);
}

.social-grid__item {
  position: relative;
  display: block;
  aspect-ratio: 4 / 5;
  overflow: hidden;
  border-radius: var(--radius);
  background: var(--paper-2);
}

.social-grid__item img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 34%;
  transition: transform 1.1s var(--ease);
}

.social-grid__item span {
  position: absolute;
  inset: auto 0 0 0;
  padding: 22px 14px 12px;
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: #fff;
  background: linear-gradient(to top, rgba(24, 21, 18, 0.62), transparent);
  opacity: 0;
  transition: opacity 0.35s var(--ease);
}

.social-grid__item:hover img {
  transform: scale(1.05);
}

.social-grid__item:hover span,
.social-grid__item:focus-visible span {
  opacity: 1;
}

/* --- responsive ----------------------------------------------------------- */

@media (max-width: 1040px) {
  .grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .social-grid {
    grid-template-columns: repeat(3, 1fr);
  }

  /* Quatre colonnes ne tiennent plus : la marque prend toute la largeur en
     tête, les trois autres se rangent dessous à parts égales. */
  .footer__grid {
    grid-template-columns: repeat(3, 1fr);
  }

  /* La marque passe en bandeau : la marge la sépare des trois colonnes qui la
     suivent, mais reste courte pour ne pas rouvrir un vide. */
  .footer__grid > :first-child {
    grid-column: 1 / -1;
    margin-bottom: 6px;
  }
}

@media (max-width: 900px) {
  :root {
    --gutter: 20px;
  }

  /* La navigation centrale disparaît sous 900 px : il ne reste que deux
     éléments dans la grille. Deux colonnes, donc — sinon les actions se
     rangeraient dans la colonne du milieu au lieu du bord droit. */
  .header__inner {
    grid-template-columns: 1fr auto;
    height: 74px;
  }

  /* L'en-tête tombe à 66 px : la pile points + nom + signature se resserre pour
     garder de l'air au-dessus et en dessous. */
  .brand__dots {
    gap: 2px;
    margin-bottom: 6px;
  }

  .brand__dots span:first-child {
    width: 10px;
    height: 10px;
  }

  .brand__dots span:last-child {
    width: 5px;
    height: 5px;
  }

  .brand .brand__mark {
    font-size: 19px;
    letter-spacing: 0.26em;
    text-indent: 0.26em;
  }

  .brand .brand__tag {
    font-size: 7px;
    margin-top: 4px;
  }

  .burger {
    display: flex;
  }

  /* La navigation de bureau disparaît complètement : sous 900 px c'est le
     panneau .menu qui prend le relais. */
  .nav {
    display: none;
  }

  body.menu-open .burger span:nth-child(1) {
    transform: translateY(6px) rotate(45deg);
  }

  body.menu-open .burger span:nth-child(2) {
    opacity: 0;
  }

  body.menu-open .burger span:nth-child(3) {
    transform: translateY(-6px) rotate(-45deg);
  }

  /* Cibles tactiles : 44 px minimum. Sans ça le burger fait 29 px de haut et le
     bouton panier 31 × 21 — trop petits pour un pouce. */
  .burger,
  #cart-open {
    min-width: 44px;
    min-height: 44px;
    align-items: center;
    justify-content: center;
  }

  .header__actions {
    gap: 6px;
  }

  /* .split--compact.split--reverse pèse deux classes : sans le rappeler ici,
     il l'emporterait sur le simple .split et garderait ses deux colonnes. */
  .split,
  .split--compact,
  .split--compact.split--reverse,
  .bundle,
  .pdp,
  .contact-grid {
    grid-template-columns: 1fr;
  }

  /* Sur une seule colonne, la photo occuperait toute la largeur de l'écran :
     on la borne et on la recentre. Elle reste entière. */
  .split--compact .split__media {
    max-width: 320px;
    margin-left: auto;
    margin-right: auto;
  }

  .split--reverse .split__media {
    order: 0;
  }

  .pdp__gallery {
    position: static;
  }

  .steps {
    grid-template-columns: 1fr;
  }

  .hero {
    min-height: 78vh;
  }
}

@media (max-width: 620px) {
  /* --- grille produits : deux colonnes sur téléphone ----------------------
     Une seule carte par ligne obligeait à faire défiler onze écrans pour
     parcourir la collection. Deux colonnes ramènent la boutique à six lignes
     et donnent la lecture d'un vrai catalogue mobile.

     Le sélecteur est unique et couvre les trois grilles du site — la vitrine
     de l'accueil (data-grid="featured"), la collection (data-grid="all") et
     « Vous aimerez aussi » sur la fiche produit — donc aucune divergence
     possible entre elles.

     Les colonnes sont en `1fr 1fr` : elles se partagent la largeur restante à
     parts strictement égales, quelles que soient les longueurs de texte. La
     gouttière (--gutter, 20 px ici) n'est pas touchée, pour que le bord des
     cartes reste aligné sur celui des titres de section et de l'en-tête ;
     c'est l'écart entre cartes qui se resserre, de 40 px à 14 px, là où la
     largeur était réellement perdue.

     Largeur de carte obtenue : (390 − 40 − 14) / 2 = 168 px sur un écran de
     390 px, 153 px sur 360 px, 133 px sur 320 px. Les tailles de texte plus
     bas sont calées sur cette plage. */
  .grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 30px 14px;
  }

  .social-grid {
    grid-template-columns: repeat(2, 1fr);
  }

  /* Les douze pétales sont conservés ici aussi : masquer les pairs vidait à
     moitié le hero sur téléphone, là où il tient tout l'écran. L'allègement
     pour les appareils modestes passe désormais par le seul flou de la brume,
     nettement plus coûteux à composer qu'une translation de pétale. */
  .hero__mist i {
    filter: blur(12px);
  }

  /* Fleurs plus lentes et plus discrètes sur téléphone. Le hero y occupe
     presque tout l'écran : à taille et vitesse de bureau, la chute devient
     envahissante sur une surface trois fois plus petite.

     La durée est multipliée, pas remplacée : chaque pétale garde son --t
     propre (16 à 24 s, posé en ligne dans le HTML), simplement étiré à 23-35 s.
     Les douze restent donc désynchronisés, aucun ne bat avec son voisin.

     `opacity()` dans le filtre plutôt qu'une retouche des images-clés : la
     propriété `opacity` est animée par heroPetal, la toucher casserait le
     fondu. Le filtre, lui, se multiplie par-dessus sans y toucher. */
  .hero__petals i {
    width: 20px;
    height: 23px;
    animation-duration: calc(var(--t, 18s) * 1.45);
    filter: drop-shadow(0 0 6px rgba(255, 234, 230, 0.45)) opacity(0.72);
  }

  /* --- la carte dans une colonne de 168 px --------------------------------
     Le cadre garde son ratio 3/4 et `object-fit: cover` : la photo remplit la
     carte sans jamais être étirée, seul le cadrage se resserre. `flex: none`
     l'immunise contre l'étirement des cartes d'une même ligne — sans lui, une
     carte plus haute que sa voisine pourrait comprimer l'image et casser le
     ratio, les éléments flex étant compressibles par défaut. */
  .card__media {
    aspect-ratio: 3 / 4;
    flex: none;
    margin-bottom: 13px;
  }

  /* La pastille reprend l'échelle de la carte : « Édition d'été », la plus
     longue, mesure alors 103 px pour 133 px disponibles au pire cas. */
  .card__badge {
    top: 9px;
    left: 9px;
    font-size: 9px;
    letter-spacing: 0.14em;
    padding: 5px 8px;
  }

  /* Le bouton de survol reste invisible au doigt (il n'y a pas de :hover sur
     écran tactile) mais il est redimensionné pour ne pas déborder si un
     navigateur le déclenche au tap : « AJOUTER — 18,000 DT » tient en 116 px
     pour 117 px de largeur intérieure sur la carte la plus étroite. */
  .card__quick {
    left: 8px;
    right: 8px;
    bottom: 8px;
    padding: 10px 6px;
    font-size: 9px;
    letter-spacing: 0.08em;
  }

  /* Nom et prix côte à côte ne tiennent plus : à 168 px, le prix en
     `white-space: nowrap` mangeait 58 px et le nom se brisait sur trois
     lignes. Ils s'empilent, le nom garde toute la largeur. */
  .card__head {
    flex-direction: column;
    align-items: flex-start;
    gap: 3px;
  }

  /* 17 → 16 px : une réduction d'un point, pour rendre 8 px au titre sur une
     carte de 168 px. L'indice suit, puisqu'il est exprimé en em. Mesuré sur les
     onze produits, cela fait passer de 6 à 4 le nombre de titres qui doivent se
     replier sur deux lignes à 390 px — et de 2 à 0 à 430 px. */
  /* --- la carte sur téléphone : cinq niveaux, empilés ----------------------
     nom / indice / catégorie / contenance / accroche.

     Le titre n'est plus un rail horizontal mais un bloc : le nom se replie
     naturellement sur deux lignes s'il en a besoin, et l'indice descend sur sa
     propre ligne. Aucune troncature, aucune police rapetissée pour forcer une
     ligne unique — c'était le compromis du bureau, il n'a pas lieu d'être ici
     où la carte est le seul contenu de sa colonne. */
  .card__name {
    font-size: 16px;
  }

  .card__name > a {
    display: block;
  }

  .card__nom,
  .card__indice {
    display: block;
    white-space: normal;
    overflow: visible;
  }

  /* Le cadratin n'a de sens qu'en ligne. Sur deux lignes, il ouvrirait la
     seconde par un tiret orphelin. */
  .card__sep {
    display: none;
  }

  .card__indice {
    margin-top: 2px;
    font-size: 0.8em;
  }

  .card__price {
    font-size: 12.5px;
  }

  .card__meta,
  .card__size {
    font-size: 9.5px;
    letter-spacing: 0.12em;
  }

  .card__meta {
    margin: 5px 0 2px;
  }

  .card__size {
    margin: 0 0 7px;
  }

  /* Sur la collection, le prix quitte la ligne du nom : le bouton posé sur la
     photo est affiché d'emblée sur écran tactile (voir `@media (hover: none)`)
     et porte déjà la même valeur. La carte gagne une ligne, ce qui compense
     celle qu'ajoute la contenance.

     Restreint au téléphone ET à la grille de la collection : en bureau le
     bouton ne paraît qu'au survol, masquer le prix y laisserait la page sans
     aucun montant visible. La vitrine de l'accueil, elle, a sa propre règle. */
  [data-grid='all'] .card__price {
    display: none;
  }

  /* L'accroche se replie de nouveau sur téléphone : la couper à l'ellipse
     n'avait plus de sens une fois le nom autorisé à passer sur deux lignes.
     Bornée à deux lignes pour que les cartes d'une même rangée finissent au
     même endroit, et la place est réservée d'office — c'est ce qui garde la
     grille alignée quand une accroche est plus courte que sa voisine. */
  .card__short {
    font-size: 12.5px;
    line-height: 1.5;
    max-width: none;
    white-space: normal;
    text-align: left;
    min-height: 3em;
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    overflow: hidden;
  }

  /* `margin-top: auto` est passé dans la règle de base : il vaut désormais sur
     toutes les largeurs, pas seulement ici. Seul le rapprochement du filet
     reste propre au téléphone. */
  .indice--carte {
    padding-top: 9px;
  }

  .indice--carte .indice__label {
    font-size: 9px;
    letter-spacing: 0.16em;
  }

  .indice--carte .indice__valeur {
    font-size: 12px;
    margin-top: 2px;
  }

  /* --- filtres de la collection -------------------------------------------
     Six puces (Tous, Floral, Coton, Gourmand, Orientale, Pack Duo) tenaient
     sur trois rangées et repoussaient la grille sous la ligne de flottaison.
     Resserrées, elles tiennent sur deux rangées à 360 px.

     Seule la largeur se resserre : le rembourrage vertical passe au contraire
     de 9 à 10 px, pour une hauteur de touche de 34 px contre 33 avant. Des
     puces plus petites que sur la version actuelle auraient été un recul au
     doigt, alors que la demande porte sur la largeur perdue. */
  .toolbar {
    justify-content: flex-start;
    gap: 12px;
    padding: 14px 0;
  }

  .filters {
    gap: 7px;
  }

  .chip {
    padding: 10px 13px;
    font-size: 10px;
    letter-spacing: 0.13em;
  }

  /* Deux colonnes plutôt qu'une seule pile : quatre blocs empilés donneraient un
     pied de page interminable sur téléphone. La marque garde la pleine largeur,
     les trois autres se répartissent deux par ligne. */
  .footer__grid {
    grid-template-columns: 1fr 1fr;
    gap: 24px clamp(20px, 5vw, 40px);
    padding-bottom: 24px;
  }

  /* --- bouton « Découvrir le Pack Duo », pied de page mobile ---------------
     Il était réglé sur `width: 100%`, ce qui donnait deux comportements ratés
     de part et d'autre de 420 px : entre 421 et 620 px la colonne ne fait que
     180 à 275 px et le libellé se brisait en laissant « DUO » seul sur la
     seconde ligne ; sous 420 px le pied de page passe à une colonne et le
     bouton s'étirait sur toute la largeur de l'écran, jusqu'à 380 px.

     `min(100%, 180px)` règle les deux d'un coup : le bouton ne dépasse jamais
     180 px, et se laisse brider par la colonne quand elle est plus étroite —
     il ne peut donc pas déborder, y compris sur un écran de 320 px.

     `display: flex` remplace le `inline-flex` de .btn : les marges `auto` ne
     centrent que les boîtes de niveau bloc, sur une boîte en ligne elles
     valent zéro et le bouton serait resté collé à gauche. */
  .footer__cta {
    display: flex;
    width: 100%;
    max-width: 180px;
    margin-inline: auto;
    padding: 12px 14px;
    font-size: 11px;
    letter-spacing: 0.12em;
    /* Le gras du bouton Pack Duo du hero (.hero__actions .btn). La couleur
       reste celle de .btn--light : c'est la graisse, pas la teinte, qui fait
       le rappel. */
    font-weight: 700;
    /* Deux lignes possibles : sans `text-align`, la seconde se calerait à
       gauche — `justify-content` de .btn centre la boîte de texte, pas les
       lignes qu'elle contient. L'interligne est resserré (1.6 par défaut
       aurait donné un bouton de 61 px). */
    text-align: center;
    line-height: 1.35;
  }

  /* Respiration entre « Livraison gratuite » et le bouton : 18 px suffisaient
     dans une colonne de bureau, ils sont trop serrés une fois le bouton
     détaché et centré. */
  .footer__pack ul {
    margin-bottom: 22px;
  }

  /* --- blocs de texte long ------------------------------------------------
     Le bloc « parti pris » de l'accueil porte deux paragraphes de 274 et 155
     caractères : huit lignes puis cinq sur un téléphone, un mur avant même la
     première image de produit.

     Le second est retiré de l'affichage mobile et le premier borné à quatre
     lignes. Le texte reste ENTIER dans le HTML — il n'est ni supprimé ni
     tronqué à la source, seulement replié — et le lien « Lire notre histoire »
     qui suit mène déjà à la version longue. C'est le « en savoir plus » que
     réclame la lecture mobile, avec le libellé du site.

     `display: none` sur le second retire aussi son contenu de la lecture
     d'écran : c'est assumé, la page à propos porte le même propos en entier. */
  .split__body .lead:first-of-type {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 4;
    line-clamp: 4;
    overflow: hidden;
  }

  .split__body .lead + .lead {
    display: none;
  }

  .section-head {
    flex-direction: column;
    align-items: flex-start;
    gap: 14px;
  }

  .form-inline {
    flex-direction: column;
  }

  /* `justify-content: flex-start` a rejoint le bloc .toolbar plus haut, avec
     les autres réglages de la barre de filtres — il y avait deux règles
     .toolbar dans ce même @media. */

  .brand__mark {
    font-size: 22px;
  }
}

/* --- hero panorama : tablette et téléphone -------------------------------- */

/* Ces blocs doivent rester après le `@media (max-width: 900px)` plus haut, qui
   remet `.hero` à 78vh : à spécificité égale, c'est l'ordre qui tranche, et
   sans ça la variante panorama reprend une hauteur qui la recadre à l'excès. */
@media (max-width: 900px) {
  .hero--flacons {
    min-height: min(72vh, 518px);
    --travel: 630px;
  }

  .hero--flacons .hero__inner {
    padding-top: clamp(56px, 12vw, 84px);
  }
}

@media (max-width: 760px) {
  /* Sous 760 px, c'est le recadrage portrait qui est servi (voir le <picture>
     dans index.html) : il supporte un hero plus haut sans être écrasé. */
  .hero--flacons {
    min-height: min(85vh, 675px);
    --travel: 765px;
  }

  .hero--flacons .hero__media img {
    object-position: center 38%;
  }

  /* Même principe qu'en bureau, mais l'ellipse suit le texte : sous 768 px il
     n'est plus bridé à 560 px, il occupe toute la largeur (5 % à 95 %) et
     descend plus bas dans le cadre (32 % à 91 % de la hauteur). L'ellipse
     s'élargit donc à 96 % et son centre descend de 52 % à 64 %.

     Elle s'aplatit aussi — 46 % de rayon vertical contre 52 — parce que le
     cadrage portrait laisse davantage de ciel au-dessus du texte : c'est cette
     zone-là qu'il faut garder claire, les fleurs et les drapés y sont. */
  .hero--flacons .hero__media::after {
    background: radial-gradient(
        ellipse 92% 50% at 50% 62%,
        rgba(18, 16, 14, 0.54) 0%,
        rgba(18, 16, 14, 0.52) 40%,
        rgba(18, 16, 14, 0.44) 60%,
        rgba(18, 16, 14, 0.26) 78%,
        rgba(18, 16, 14, 0.09) 88%,
        rgba(18, 16, 14, 0.03) 95%,
        rgba(18, 16, 14, 0) 100%
      ),
      linear-gradient(
        to top,
        rgba(18, 16, 14, 0.34) 0%,
        rgba(18, 16, 14, 0.18) 28%,
        rgba(18, 16, 14, 0.07) 60%,
        rgba(18, 16, 14, 0.03) 100%
      );
  }
}

/* Sur Android, la barre d'URL qui se replie change la valeur de vh et fait
   sauter la hauteur au défilement. dvh suit le viewport réellement visible. */
@supports (height: 100dvh) {
  @media (max-width: 760px) {
    .hero--flacons {
      min-height: min(85dvh, 675px);
    }
  }
}

/* Téléphone en paysage : l'écran fait 360 à 412 px de haut. Une hauteur en vh
   remplissait tout l'écran et poussait les boutons sous la ligne de flottaison.
   Ici le hero prend la hauteur de son contenu, réduit au strict nécessaire. */
@media (max-height: 500px) and (orientation: landscape) {
  .hero--flacons {
    min-height: 0;
    --travel: 420px;
  }

  .hero--flacons .hero__inner {
    padding-top: clamp(26px, 7vh, 44px);
    padding-bottom: clamp(22px, 6vh, 38px);
  }

  .hero--flacons h1 {
    font-size: clamp(22px, 5vh, 32px);
    margin-bottom: 10px;
  }

  .hero--flacons p {
    font-size: 14px;
    margin-bottom: 18px;
  }

  /* En paysage, le <picture> sert le panorama et le hero prend la hauteur de
     son contenu : le texte remplit presque tout le cadre, du haut au bas.
     L'ellipse s'étire donc en hauteur (72 %) — il ne reste pas de « haut à
     garder clair », seuls les côtés peuvent s'ouvrir. */
  .hero--flacons .hero__media::after {
    background: radial-gradient(
        ellipse 60% 78% at 32% 52%,
        rgba(18, 16, 14, 0.52) 0%,
        rgba(18, 16, 14, 0.5) 42%,
        rgba(18, 16, 14, 0.42) 62%,
        rgba(18, 16, 14, 0.24) 80%,
        rgba(18, 16, 14, 0.08) 90%,
        rgba(18, 16, 14, 0) 100%
      ),
      linear-gradient(
        to top,
        rgba(18, 16, 14, 0.28) 0%,
        rgba(18, 16, 14, 0.14) 40%,
        rgba(18, 16, 14, 0.05) 100%
      );
  }
}

/* Entre 901 et 1040 px — téléphone en paysage, petite tablette — le menu
   complet tient encore, mais « Le rituel » et « La maison » passaient sur deux
   lignes et déformaient l'en-tête. */
@media (min-width: 901px) and (max-width: 1040px) {
  .nav {
    gap: 20px;
  }

  .nav a {
    white-space: nowrap;
    font-size: 11px;
  }
}

/* --- hero sur mobile : tout centré, les deux boutons en tête d'affiche ----
   Le bloc n'est plus plafonné à 560 px comme en bureau : il reprend une largeur
   propre au téléphone. 460 px n'agit qu'entre 461 et 768 px — en deçà, c'est le
   viewport qui commande, et le resserrement passe par les largeurs posées plus
   bas sur le titre et sur le paragraphe. Le centrage horizontal, lui, vient du
   `margin: 0 auto` de .wrap : le bloc étant un élément flex plafonné, les marges
   automatiques absorbent l'espace restant.

   Le titre est bridé pour ne pas courir sur toute la largeur. La valeur a suivi
   le texte : 300 px calaient la coupure de l'ancien titre après sa virgule ;
   « L'art de la fraîcheur. » tient désormais sur une ligne, et 300 px l'auraient
   brisé en « L'art de la » / « fraîcheur. » entre 421 et 768 px, où la police
   passe à 28 px et le titre demande 313 px au pire. 340 px lui laissent 27 px de
   marge, tout en gardant 80 px de réserve sur la largeur du bloc.

   Rythme vertical : le texte se resserre (titre 16 → 14 px de marge basse), puis
   l'écart avant les boutons s'ouvre (26 → 32 px). Le bloc de texte se lit comme
   une seule masse, et les deux appels à l'action s'en détachent. */
@media (max-width: 768px) {
  /* Le bloc n'est plus collé au bas du hero mais centré, puis remonté. Le
     décalage vient de l'asymétrie des rembourrages : la boîte étant centrée,
     32 px de plus en bas placent son contenu 16 px au-dessus du milieu.

     Les 76 px du haut ne sont pas décoratifs : l'en-tête se superpose au hero
     et mesure 74 px sur mobile (.header__inner sous 900 px). Sur un téléphone
     court — 360 × 600, où le hero tombe à 456 px — la boîte devient plus haute
     que lui, `min-height` laisse le hero grandir, et le contenu démarre alors
     exactement à 76 px : juste sous la navbar. En dessous de cette valeur, le
     sur-titre passerait dessous. */
  .hero--flacons {
    align-items: center;
  }

  .hero--flacons .hero__inner {
    max-width: 460px;
    text-align: center;
    padding-top: 76px;
    padding-bottom: 108px;
  }

  /* Le texte respire : chaque intervalle s'ouvre d'un cran, du plus petit
     libellé au groupe d'action. La progression reste croissante — 24, 20, 40 —
     pour que l'œil descende vers les boutons. */
  .hero--flacons .eyebrow {
    margin-bottom: 24px;
  }

  /* `.hero__inner h1` et non `.hero--flacons h1` : la règle de bureau qui bride
     le titre à 420 px pèse (0,2,1). À (0,1,1) celle-ci perdait quelle que soit
     sa place dans le fichier, et le plafond mobile de 340 px n'aurait jamais
     été appliqué. */
  .hero--flacons .hero__inner h1 {
    max-width: 340px;
    margin-left: auto;
    margin-right: auto;
    margin-bottom: 20px;
  }

  /* Même spécificité (0,2,1) que le plafond de 420 px du bureau, mais plus bas
     dans le fichier : c'est l'ordre qui tranche, et 38ch l'emporte.
     38ch ≈ 285 px à 15 px, soit deux à trois lignes de lecture confortable.
     Plus rien à annuler : le bureau ne pose plus ni justification ni césure. */
  .hero--flacons .hero__inner p {
    max-width: 38ch;
    margin-left: auto;
    margin-right: auto;
    margin-bottom: 40px;
    line-height: 1.55;
  }

  /* Les deux boutons empilés et centrés. L'ordre du DOM est conservé :
     « Découvrir les parfums » (.btn--light, le plus contrasté sur la photo)
     reste au-dessus en appel principal, « Le Pack Duo » en second. */
  .hero__actions {
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 13px;
  }

  /* Écart entre la fin du hero et « La Collection ». Il vient entièrement du
     rembourrage haut de cette section : le hero n'en a aucun sous lui. 64 px
     par défaut sur téléphone, ramenés dans la fourchette demandée.
     Un identifiant (1,0,0) : il l'emporte sur .section (0,1,0) sans dépendre
     de l'ordre, et #parfums n'existe que sur l'accueil — les autres sections
     du site gardent leur respiration. Seul le haut est touché, le bas reste
     à sa valeur d'origine. */
  #parfums {
    padding-top: 36px;
  }

  /* Boutons resserrés en largeur, allégés visuellement, plus lisibles.

     280 px au lieu de 300 : environ 28 px de marge de chaque côté dans la boîte
     de contenu à 375 px d'écran, 35 à 390, 60 à 440 — soit 48 à 80 px depuis le
     bord de l'écran, gouttière comprise.

     Le texte passe de 11 à 14 px et son interlettrage de 0,20 à 0,08em. Sans ce
     resserrement, « DÉCOUVRIR LES PARFUMS » demanderait 300 px à 14 px et
     déborderait ; à 0,08em il tient en 215 px pour 238 utiles.

     Hauteur : 15 × 2 de rembourrage + 14 × 1,4 d'interligne + 2 de bordures,
     soit 51,6 px — la cible de 52 px, et bien au-delà des 44 px de cible
     tactile recommandés. `line-height` est posé explicitement : sans lui, le
     bouton héritait du 1,65 du corps de texte et sa hauteur dépendait d'une
     valeur définie ailleurs. */
  .hero__actions .btn {
    width: 100%;
    max-width: 280px;
    padding: 15px 20px;
    font-size: 14px;
    letter-spacing: 0.08em;
    line-height: 1.4;
  }
}

@media (max-width: 420px) {
  /* Deux colonnes donnent des pistes de 165 px : le bouton du Pack Duo en
     demande 202, et « Instagram — @avairis.spray » 163 sans marge. On empile. */
  .footer__grid {
    grid-template-columns: 1fr;
    gap: 26px;
  }

  /* « Voile de Paradis — Poudre bébé » est la paire la plus large : 199 px, pour
     188 px disponibles à 360 px d'écran. Un cran de moins sur les deux tailles
     la fait rentrer ; en deçà, le nom se termine en points de suspension et
     reste lisible en entier dans la liste déroulante. */
  .dselect__champ {
    font-size: 14px;
    gap: 6px;
  }

  .dselect__texte {
    gap: 6px;
  }

  .dselect__indice {
    font-size: 12px;
  }

  .hero--flacons h1 {
    font-size: 26px;
  }

  /* L'empilement et la largeur des boutons sont désormais posés dans le bloc
     768 px, avec un plafond de 300 px pour garder une marge sur les bords. Les
     deux règles « pleine largeur » qui vivaient ici sont retirées : elles
     collaient les boutons aux bords de l'écran. */
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }

  .reveal {
    opacity: 1;
    transform: none;
  }

  /* Les boucles du hero passeraient à 0,01 ms et clignoteraient : on les coupe. */
  .hero__fx {
    display: none;
  }

  .hero__frame {
    transform: none;
  }
}
