/* ============================================================================
   CABINET — parfums et bougies composés en France
   Système de design : palette de dix teintes, une seule rupture structurelle,
   profondeur obtenue par filets d'un pixel plutôt que par ombres portées.

   Conception et développement :
     Noélabs — agence IA & Django, Île-de-France · https://noelabs.com/
     Noésis Software Technologies · https://nsft.fr/

   Feuille écrite à la main : ni compilateur, ni chaîne d'outils, ni dépendance
   de construction (ADR-0034).
   ============================================================================ */

/* ============================================================================
   CABINET — feuille de styles
   Blueprint §19. Servie telle quelle : aucune compilation, aucun Node.

   Écrite à la main plutôt que générée. Le catalogue de classes est petit et
   fermé — toutes employées — et un générateur imposerait une étape de build que
   la plateforme de déploiement ne sait pas exécuter : elle ne construit qu'un
   environnement Python. Une feuille compilée absente ou périmée laisse le site
   sans styles sans que rien ne lève.

   Toute valeur de design vient de tokens.css. Aucune couleur, aucune taille et
   aucune durée n'est écrite en dur ici — la porte G10 le vérifie.

   RESPONSIVE — règle structurante (§19.3) : une seule rupture change la mise en
   page, à 1024px. Tout le reste s'adapte sans media query, par clamp(),
   minmax() et auto-fill. Ce n'est pas une contrainte subie : une grille qui se
   réorganise d'elle-même n'a pas de largeur où elle casse.

   Ordre du fichier : remise à zéro, base, mise en page, typographie, couleurs,
   composants, variantes responsives, impression.
   ========================================================================== */

/* ── Remise à zéro ────────────────────────────────────────────────────────── */

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

* {
  margin: 0;
}

html {
  -webkit-text-size-adjust: 100%;
  tab-size: 4;
  scroll-padding-top: calc(var(--height-header) + var(--space-8));
}

img,
picture,
video,
canvas,
svg {
  display: block;
  max-width: 100%;
}

input,
button,
textarea,
select {
  font: inherit;
  color: inherit;
}

p,
h1,
h2,
h3,
h4 {
  overflow-wrap: break-word;
}

ul,
ol {
  list-style: none;
  padding: 0;
}

summary::-webkit-details-marker {
  display: none;
}

/* ── Base ─────────────────────────────────────────────────────────────────── */

body {
  min-height: 100svh;
  font-family: var(--font-sans);
  font-size: var(--text-base);
  font-weight: var(--weight-regular);
  line-height: var(--leading-normal);
  letter-spacing: var(--tracking-wide);
  color: var(--color-ink);
  background: var(--color-paper);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

h1,
h2,
h3,
h4 {
  font-weight: var(--weight-medium);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-display);
}

a {
  color: inherit;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.2em;
}

/* Le focus reste visible partout : c'est la seule affordance dont dispose une
   navigation au clavier (§19.5, WCAG 2.4.7). */
:focus-visible {
  outline: 2px solid var(--color-ink);
  outline-offset: 2px;
}

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

/* Respecte une préférence système explicite : une animation non désirée peut
   déclencher des troubles vestibulaires (WCAG 2.3.3). */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }

  .marquee__track {
    animation-play-state: paused !important;
  }
}

/* ── Mise en page ─────────────────────────────────────────────────────────── */

.page {
  width: 100%;
  max-width: var(--container-page);
  margin-inline: auto;
  padding-inline: var(--space-4);
}

.page--wide { max-width: var(--container-wide); }
.page--prose { max-width: var(--width-prose); }
.page--form { max-width: var(--width-form); }

/* Une section pleine largeur qui échappe au conteneur sans provoquer de
   défilement horizontal : la marge négative est calculée, pas devinée. */
.bleed {
  width: 100vw;
  margin-inline: calc(50% - 50vw);
}

.block { display: block; }
.hidden { display: none; }
.flex { display: flex; }
.grid { display: grid; }
.contents { display: contents; }

.flex-col { flex-direction: column; }
.flex-1 { flex: 1 1 0%; }
.flex-none { flex: none; }
.flex-wrap { flex-wrap: wrap; }
.items-center { align-items: center; }
.items-start { align-items: flex-start; }
.items-end { align-items: flex-end; }
.items-baseline { align-items: baseline; }
.justify-between { justify-content: space-between; }
.justify-center { justify-content: center; }
.justify-end { justify-content: flex-end; }
.text-center { text-align: center; }

/* Grilles intrinsèques : la densité suit la largeur disponible, sans rupture.
   auto-fill plutôt que auto-fit — une rangée d'un seul produit ne doit pas
   s'étirer sur toute la page. */
.grid-cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(15rem, 100%), 1fr));
  column-gap: var(--space-4);
  row-gap: var(--space-12);
}

.grid-tiles {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(19rem, 100%), 1fr));
  gap: var(--space-4);
}

.grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.col-span-full { grid-column: 1 / -1; }

.mx-auto { margin-inline: auto; }
.w-full { width: 100%; }
.h-full { height: 100%; }

.sticky { position: sticky; }
.relative { position: relative; }
.top-0 { top: 0; }

.gap-1 { gap: var(--space-1); }
.gap-2 { gap: var(--space-2); }
.gap-3 { gap: var(--space-3); }
.gap-4 { gap: var(--space-4); }
.gap-5 { gap: var(--space-5); }
.gap-6 { gap: var(--space-6); }
.gap-8 { gap: var(--space-8); }
.gap-10 { gap: var(--space-10); }
.gap-12 { gap: var(--space-12); }

.mt-1 { margin-top: var(--space-1); }
.mt-2 { margin-top: var(--space-2); }
.mt-3 { margin-top: var(--space-3); }
.mt-4 { margin-top: var(--space-4); }
.mt-5 { margin-top: var(--space-5); }
.mt-6 { margin-top: var(--space-6); }
.mt-8 { margin-top: var(--space-8); }
.mt-10 { margin-top: var(--space-10); }
.mt-12 { margin-top: var(--space-12); }
.mt-16 { margin-top: var(--space-16); }
.mb-16 { margin-bottom: var(--space-16); }

.p-0 { padding: 0; }
.pb-4 { padding-bottom: var(--space-4); }
.pb-16 { padding-bottom: var(--space-16); }
.py-2 { padding-block: var(--space-2); }
.py-4 { padding-block: var(--space-4); }
.py-6 { padding-block: var(--space-6); }
.py-8 { padding-block: var(--space-8); }
.py-10 { padding-block: var(--space-10); }
.py-12 { padding-block: var(--space-12); }
.py-16 { padding-block: var(--space-16); }

/* Respiration entre sections. Elle porte le calme : c'est le seul « décor »
   que s'autorise une page sans ombres ni boîtes. */
.section {
  padding-block: clamp(var(--space-12), 7vw, var(--space-24));
}

.section--tight {
  padding-block: clamp(var(--space-8), 4vw, var(--space-16));
}

.section__head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--space-6);
  margin-bottom: var(--space-8);
}

/* ── Typographie ──────────────────────────────────────────────────────────── */

.text-2xs { font-size: var(--text-2xs); }
.text-xs { font-size: var(--text-xs); }
.text-sm { font-size: var(--text-sm); }
.text-base { font-size: var(--text-base); }
.text-md { font-size: var(--text-md); }
.text-lg { font-size: var(--text-lg); }
.text-xl { font-size: var(--text-xl); }
.text-2xl { font-size: var(--text-2xl); }
.text-3xl { font-size: var(--text-3xl); }

.leading-none { line-height: var(--leading-none); }
.leading-tight { line-height: var(--leading-tight); }
.leading-snug { line-height: var(--leading-snug); }
.tracking-caps { letter-spacing: var(--tracking-caps); }
.uppercase { text-transform: uppercase; }
.underline { text-decoration-line: underline; }
.no-underline { text-decoration-line: none; }
.font-medium { font-weight: var(--weight-medium); }
.font-strong { font-weight: var(--weight-strong); }
.serif { font-family: var(--font-serif); }
.balance { text-wrap: balance; }

/* Le cartouche à graisses mixtes (§19.5, geste n°2) : dans une même ligne, les
   mots porteurs passent en 600, le reste reste en 400. Le titre accessible
   complet vit dans un .sr-only ; la version composée est aria-hidden. */
.display {
  font-family: var(--font-serif);
  font-weight: var(--weight-regular);
  font-size: clamp(var(--text-4xl), 6.2vw, var(--text-5xl));
  line-height: var(--leading-none);
  letter-spacing: var(--tracking-display);
  text-wrap: balance;
}

.display--xl { font-size: clamp(var(--text-4xl), 8vw, var(--text-6xl)); }

/* Variante capitales : la référence pose ses bandes éditoriales en grotesque
   serrée plutôt qu'en serif. Même échelle, autre voix. */
.display--caps {
  font-family: var(--font-sans);
  font-weight: var(--weight-strong);
  text-transform: uppercase;
  letter-spacing: var(--tracking-display);
  line-height: var(--leading-none);
}
.display--sm { font-size: clamp(var(--text-2xl), 3.6vw, var(--text-4xl)); }

.display em {
  font-style: normal;
  font-family: var(--font-sans);
  font-weight: var(--weight-strong);
  letter-spacing: var(--tracking-display);
}

.lede {
  font-size: var(--text-md);
  line-height: var(--leading-normal);
  max-width: 46ch;
}

/* Sous chaque filet, un libellé en capitales avec interlettre large (§19.5). */
.eyebrow {
  display: block;
  font-size: var(--text-2xs);
  font-weight: var(--weight-medium);
  letter-spacing: var(--tracking-caps);
  line-height: var(--leading-snug);
  text-transform: uppercase;
  color: var(--color-muted);
}

.eyebrow--ink { color: var(--color-ink); }

/* Les chiffres alignés sont indispensables partout où des montants se
   superposent : sans eux, une colonne de prix ondule. */
.tabular {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

.strike { text-decoration: line-through; }

/* ── Couleurs de surface ──────────────────────────────────────────────────── */

.bg-paper { background-color: var(--color-paper); }
.bg-cream { background-color: var(--color-cream); }
.bg-sand { background-color: var(--color-sand); }
.bg-ink { background-color: var(--color-ink); }

.text-ink { color: var(--color-ink); }
.text-paper { color: var(--color-paper); }
.text-muted { color: var(--color-muted); }
.text-signal { color: var(--color-signal); }
.text-gold-ink { color: var(--color-gold-ink); }

/* Sur fond sombre, le gris secondaire n'existe pas : muted sur ink tombe à
   3,02:1. Le texte secondaire y devient un filet clair, et l'accent doré n'est
   lisible QUE là — 7,79:1 (§19.1, règle n°1). */
.on-ink { color: var(--color-paper); }
.on-ink .text-muted { color: var(--color-line); }
.on-ink .eyebrow { color: var(--color-gold); }

/* ── Bordures et filets ───────────────────────────────────────────────────── */

.border-0 { border: 0; }
.border-t { border-top: 1px solid var(--color-line); }
.border-b { border-bottom: 1px solid var(--color-line); }
.rounded-sm { border-radius: var(--radius-sm); }
.rounded-card { border-radius: var(--radius-card); }
.rounded-pill { border-radius: var(--radius-pill); }

/* La profondeur vient du filet et de l'alternance paper/cream, jamais d'une
   ombre portée (§19.3). */
.rule {
  height: 1px;
  margin-block: clamp(var(--space-10), 5vw, var(--space-16));
  border: 0;
  background: var(--color-line);
}

/* ══ Composants ═══════════════════════════════════════════════════════════ */

/* ── C-01 · Bandeau d'annonce ─────────────────────────────────────────────── */

.announce {
  position: relative;
  z-index: var(--z-header);
  background: var(--color-ink);
  color: var(--color-paper);
}

.announce[hidden] { display: none; }

.announce__bar {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-4);
  min-height: var(--height-announcement);
  padding-block: var(--space-2);
}

/* Les messages sont empilés au même endroit ; un seul porte data-current.
   Une hauteur de ligne fixe évite que la page saute au changement. */
.announce__slot {
  position: relative;
  flex: 1 1 auto;
  min-width: 0;
  height: 1.25rem;
}

.announce__msg {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  font-size: var(--text-2xs);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  text-align: center;
  opacity: 0;
  transform: translateY(0.55rem);
  pointer-events: none;
  transition:
    opacity var(--duration-slow) var(--ease-brand),
    transform var(--duration-slow) var(--ease-brand);
}

.announce__msg[data-current="true"] {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}

.announce__msg strong {
  font-weight: var(--weight-strong);
  color: var(--color-gold);
}

.announce__close {
  position: relative;
  flex: none;
  padding: var(--space-2);
  border: 0;
  background: transparent;
  color: var(--color-paper);
  line-height: 1;
  cursor: pointer;
  opacity: 0.7;
  transition: opacity var(--duration-fast) var(--ease-brand);
}

.announce__close:hover,
.announce__close:focus-visible { opacity: 1; }

.announce__close svg { width: 1rem; height: 1rem; }

/* ── C-02 · En-tête collant ───────────────────────────────────────────────── */

.masthead {
  position: sticky;
  top: 0;
  z-index: var(--z-header);
  background: var(--color-paper);
  border-bottom: 1px solid var(--color-line);
  transition: transform var(--duration-base) var(--ease-brand);
}

/* Masqué au défilement descendant, rendu au premier geste vers le haut. */
.masthead[data-hidden="true"] { transform: translateY(-100%); }

.masthead__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  height: var(--height-header);
}

.brandmark {
  flex: none;
  font-family: var(--font-serif);
  font-size: var(--text-xl);
  letter-spacing: 0.22em;
  text-transform: uppercase;
  text-decoration: none;
  line-height: 1;
}

/* ── C-03 · Navigation et méga-menu ───────────────────────────────────────── */

/* Rendu serveur, présent dans le HTML initial : un méga-menu construit en JS
   est invisible pour les moteurs et inutilisable sans script (§19.7, C-03).
   Le panneau s'ouvre au survol ET au focus — jamais l'un sans l'autre. */
.nav {
  display: none;
  align-items: center;
  gap: var(--space-6);
}

.nav__item { position: static; }

.nav__link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding-block: var(--space-2);
  border: 0;
  background: transparent;
  font-size: var(--text-2xs);
  font-weight: var(--weight-medium);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  text-decoration: none;
  color: var(--color-ink);
  cursor: pointer;
  transition: color var(--duration-fast) var(--ease-brand);
}

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

.nav__link[aria-current="page"] {
  text-decoration: underline;
  text-underline-offset: 0.45em;
}

.nav__chevron {
  width: 0.4rem;
  height: 0.4rem;
  border-right: 1px solid currentcolor;
  border-bottom: 1px solid currentcolor;
  transform: translateY(-0.12em) rotate(45deg);
  transition: transform var(--duration-fast) var(--ease-brand);
}

.megamenu {
  position: absolute;
  left: 0;
  right: 0;
  top: 100%;
  background: var(--color-paper);
  border-top: 1px solid var(--color-line);
  border-bottom: 1px solid var(--color-line);
  box-shadow: var(--shadow-overlay);
  opacity: 0;
  visibility: hidden;
  transform: translateY(-0.5rem);
  transition:
    opacity var(--duration-fast) var(--ease-brand),
    transform var(--duration-fast) var(--ease-brand),
    visibility var(--duration-fast) var(--ease-brand);
}

.nav__item:hover .megamenu,
.nav__item:focus-within .megamenu {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}

.nav__item:hover .nav__chevron,
.nav__item:focus-within .nav__chevron {
  transform: translateY(0.08em) rotate(225deg);
}

.megamenu__inner {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(13rem, 100%), 1fr));
  gap: var(--space-10);
  padding-block: var(--space-10);
}

.megamenu__list {
  display: grid;
  gap: var(--space-3);
  margin-top: var(--space-4);
}

.megamenu__link {
  font-size: var(--text-sm);
  text-decoration: none;
  transition: color var(--duration-fast) var(--ease-brand);
}

.megamenu__link:hover,
.megamenu__link:focus-visible { color: var(--color-gold-ink); }

.megamenu__feature {
  display: grid;
  align-content: end;
  gap: var(--space-2);
  min-height: 11rem;
  padding: var(--space-5);
  border-radius: var(--radius-card);
  background: var(--tile-amber);
  text-decoration: none;
}

/* ── Recherche et actions d'en-tête ───────────────────────────────────────── */

.header__actions {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.searchbox {
  position: relative;
  display: none;
  align-items: center;
  min-width: 13rem;
}

.iconbtn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.5rem;
  height: 2.5rem;
  padding: 0;
  border: 0;
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--color-ink);
  text-decoration: none;
  cursor: pointer;
  transition: background var(--duration-fast) var(--ease-brand);
}

.iconbtn:hover,
.iconbtn:focus-visible { background: var(--color-cream); }

.iconbtn svg { width: 1.25rem; height: 1.25rem; }

.iconbtn__badge {
  position: absolute;
  top: 0.15rem;
  right: 0.1rem;
  min-width: 1.05rem;
  padding-inline: 0.2rem;
  border-radius: var(--radius-pill);
  background: var(--color-ink);
  color: var(--color-paper);
  font-size: var(--text-2xs);
  line-height: 1.05rem;
  text-align: center;
}

.iconbtn__badge[data-empty="true"] { display: none; }

/* ── C-10 · La pastille inversée (geste signature n°1, §19.5) ─────────────── */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* Sans cet écart, « Ajouter au panier » et le prix se collent en
     « AJOUTER AU PANIER50,00 € » (ADR-0054). */
  gap: var(--space-3);
  min-height: var(--height-btn);
  padding-inline: var(--space-6);
  border: 1px solid var(--color-ink);
  border-radius: var(--radius-pill);
  background: var(--color-ink);
  color: var(--color-paper);
  font-size: var(--text-2xs);
  font-weight: var(--weight-medium);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition:
    background var(--duration-base) var(--ease-brand),
    color var(--duration-base) var(--ease-brand),
    border-color var(--duration-base) var(--ease-brand);
}

.btn:hover,
.btn:focus-visible {
  background: var(--color-paper);
  color: var(--color-ink);
}

.btn:disabled,
.btn[disabled] {
  border-color: var(--color-line-strong);
  background: var(--color-line-strong);
  color: var(--color-paper);
  cursor: not-allowed;
}

/* Secondaire : part de paper bordé ink. Après inversion, les deux variantes
   sont visuellement interchangeables — c'est ce qui crée l'effet. */
.btn--ghost {
  background: transparent;
  color: var(--color-ink);
}

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

/* Sur un visuel sombre ou une bande d'encre. */
.btn--light {
  border-color: var(--color-paper);
  background: var(--color-paper);
  color: var(--color-ink);
}

.btn--light:hover,
.btn--light:focus-visible {
  background: transparent;
  color: var(--color-paper);
}

.btn--outline-light {
  border-color: var(--color-paper);
  background: transparent;
  color: var(--color-paper);
}

.btn--outline-light:hover,
.btn--outline-light:focus-visible {
  background: var(--color-paper);
  color: var(--color-ink);
}

.btn--card {
  min-height: var(--height-btn-card);
  padding-inline: var(--space-4);
}

.btn--small {
  min-height: var(--height-btn-small);
  padding-inline: var(--space-4);
}

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

/* Le prix est un élément propre, poussé à droite de la pastille : c'est le
   premier critère de comparaison d'une grille, il ne s'enfouit pas dans un
   libellé d'action (ADR-0054). */
.btn__price {
  margin-left: auto;
  font-variant-numeric: tabular-nums;
  letter-spacing: var(--tracking-wide);
}

/* Contenance retenue : l'inversion signale la sélection sans recourir à la
   couleur seule (WCAG 1.4.1), et aria-pressed porte la même information. */
.btn[aria-pressed="true"] {
  background: var(--color-ink);
  color: var(--color-paper);
}

/* Contenance épuisée : barrée et atténuée, mais toujours cliquable — le client
   doit pouvoir consulter le prix d'un format qu'il ne peut pas commander. */
/* Épuisé, mais lisible. L'opacité qui servait ici tombait à 2,1:1 — le client
   devait deviner quelle contenance manquait. La teinte discrète tient 5,2:1 sur
   du papier, et c'est la rature qui porte le sens : une information dite par la
   seule couleur ne se transmet pas à qui ne la distingue pas. */
.btn[data-depleted="true"] {
  text-decoration: line-through;
  color: var(--color-muted);
  border-color: var(--color-line);
}

/* Filet de sécurité : quand tout est épuisé, la contenance retenue garde le
   fond encre du bouton pressé, et la teinte discrète y tomberait à 1,9:1. Sur
   ce fond, c'est le papier qui porte le texte ; la rature dit l'indisponibilité. */
.btn[data-depleted="true"][aria-pressed="true"] {
  color: var(--color-paper);
  border-color: var(--color-ink);
}

.linkline {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-2xs);
  font-weight: var(--weight-medium);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  text-underline-offset: 0.5em;
}

/* ── Pastilles d'information ──────────────────────────────────────────────── */

.pill {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: 0.15rem var(--space-3);
  border-radius: var(--radius-pill);
  background: var(--color-cream);
  color: var(--color-ink);
  font-size: var(--text-2xs);
  font-weight: var(--weight-medium);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  white-space: nowrap;
}

.pill--gold { background: var(--color-gold); color: var(--color-ink); }
.pill--signal { background: var(--color-signal); color: var(--color-paper); }
.pill--ink { background: var(--color-ink); color: var(--color-paper); }

.pill--outline {
  background: transparent;
  border: 1px solid var(--color-line-strong);
  color: var(--color-ink);
}

/* ── Héros ────────────────────────────────────────────────────────────────── */

.hero {
  position: relative;
  display: grid;
  overflow: hidden;
  border-radius: var(--radius-card);
  background: var(--color-cream);
}

.hero__media {
  grid-area: 1 / 1;
  aspect-ratio: var(--ratio-hero);
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Le voile ne s'applique qu'au bas du visuel : assombrir toute la photographie
   pour y poser trois lignes de texte est un aveu de composition. */
.hero__scrim {
  grid-area: 1 / 1;
  background: var(--scrim);
}

.hero__panel {
  grid-area: 1 / 1;
  align-self: end;
  display: grid;
  gap: var(--space-4);
  justify-items: start;
  padding: clamp(var(--space-6), 4vw, var(--space-16));
  max-width: 46rem;
}

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-top: var(--space-2);
}

/* Variante éditoriale : visuel d'un côté, panneau crème de l'autre. Empilée
   sous 1024px, côte à côte au-delà — la seule bascule de mise en page. */
.hero--split {
  grid-template-columns: 1fr;
  background: var(--color-cream);
}

.hero--split .hero__media {
  grid-area: auto;
  aspect-ratio: var(--ratio-tile);
  /* contain, pas cover : le visuel d'un flacon est un objet détouré, pas une
     photographie d'ambiance. Le recadrer lui coupe le bouchon. */
  object-fit: contain;
  background: var(--tile-linen);
}

.hero--split .hero__panel {
  grid-area: auto;
  align-self: center;
  align-content: center;
  color: var(--color-ink);
}

/* ── C-09 · Carrousel CSS pur ─────────────────────────────────────────────── */

/* Swiper.js pèse ~140 Ko pour ce que scroll-snap fait nativement. Les flèches
   tiennent en quelques lignes de JavaScript et restent facultatives : sans
   elles, le rail se fait glisser au doigt, à la molette ou au clavier. */
.rail__head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--space-6);
  margin-bottom: var(--space-6);
}

.rail__nav {
  display: none;
  gap: var(--space-2);
}

.rail__arrow {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.5rem;
  height: 2.5rem;
  border: 1px solid var(--color-line-strong);
  border-radius: var(--radius-pill);
  background: var(--color-paper);
  color: var(--color-ink);
  cursor: pointer;
  transition:
    background var(--duration-fast) var(--ease-brand),
    color var(--duration-fast) var(--ease-brand);
}

.rail__arrow:hover,
.rail__arrow:focus-visible {
  background: var(--color-ink);
  color: var(--color-paper);
}

.rail__arrow:disabled { opacity: 0.35; cursor: not-allowed; }
.rail__arrow svg { width: 0.9rem; height: 0.9rem; }

.swimlane {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(min(16rem, 74vw), 1fr);
  gap: var(--space-4);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  scroll-padding-inline: var(--space-4);
  padding-bottom: var(--space-4);
  overscroll-behavior-x: contain;
}

.swimlane > * { scroll-snap-align: start; }

.swimlane--wide { grid-auto-columns: minmax(min(26rem, 86vw), 1fr); }

/* ── C-08 · Carte produit, variante grille ────────────────────────────────── */

.card {
  position: relative;
  display: flex;
  flex-direction: column;
}

.card__media {
  position: relative;
  display: block;
  overflow: hidden;
  border-radius: var(--radius-card);
  background: var(--color-cream);
  text-decoration: none;
}

.card__img {
  aspect-ratio: var(--ratio-card);
  width: 100%;
  height: auto;
  /* contain, pas cover : un flacon est un objet vertical, et un recadrage lui
     coupe le bouchon et le socle. Le fond teinté du visuel prolonge celui du
     cadre, donc la lettre de côté ne se voit pas (ADR-0049). */
  object-fit: contain;
  transition: transform var(--duration-slow) var(--ease-brand);
}

.card:hover .card__img { transform: scale(1.03); }

.card__badges {
  position: absolute;
  top: var(--space-3);
  left: var(--space-3);
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  max-width: calc(100% - var(--space-6));
}

.card__body {
  display: grid;
  gap: var(--space-1);
  padding-top: var(--space-4);
}

.card__name {
  font-family: var(--font-serif);
  font-size: var(--text-xl);
  font-weight: var(--weight-regular);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-display);
}

.card__name a { text-decoration: none; }

/* Toute la carte est cliquable sans imbriquer un lien dans un lien : le
   pseudo-élément couvre la carte, les boutons restent au-dessus. */
.card__name a::after {
  content: "";
  position: absolute;
  inset: 0;
}

.card__price {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  font-size: var(--text-md);
  font-variant-numeric: tabular-nums;
}

.card__cta {
  position: relative;
  z-index: 1;
  margin-top: var(--space-3);
}

/* ── C-07 · Carte produit, variante surimpression (carrousel) ─────────────── */

.showcard {
  position: relative;
  display: grid;
  overflow: hidden;
  border-radius: var(--radius-card);
  background: var(--color-cream);
  text-decoration: none;
}

.showcard__img {
  grid-area: 1 / 1;
  aspect-ratio: var(--ratio-tile);
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--duration-slow) var(--ease-brand);
}

.showcard:hover .showcard__img { transform: scale(1.04); }

.showcard__scrim {
  grid-area: 1 / 1;
  background: var(--scrim);
}

.showcard__caption {
  grid-area: 1 / 1;
  align-self: end;
  display: grid;
  gap: var(--space-2);
  padding: var(--space-5);
}

.showcard__name {
  font-family: var(--font-serif);
  font-size: var(--text-2xl);
  font-weight: var(--weight-regular);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-display);
}

.showcard__name a { text-decoration: none; }

/* Le bouton vit dans la carte, sous le nom : c'est la disposition de la
   référence, et elle évite la pastille flottante qui apparaît au survol —
   invisible au doigt, donc inutilisable sur la moitié du trafic. */
.showcard__cta {
  margin-top: var(--space-3);
}

/* Sur une superposition claire, l'accent doré perd son contraste : le libellé
   secondaire passe au blanc plein plutôt qu'au doré (§19.1). */
.showcard__caption.overlay-light .eyebrow { color: var(--color-paper); }
.showcard__caption.overlay-dark .eyebrow { color: var(--color-muted); }

/* Superpositions sur visuel : deux thèmes explicites plutôt qu'un calcul de
   luminance au rendu — le piège C-07 du blueprint. */
.overlay-light { color: var(--color-paper); }
.overlay-dark { color: var(--color-ink); }

/* ── C-14 · Tuile éditoriale ──────────────────────────────────────────────── */

.tile {
  position: relative;
  display: grid;
  align-content: end;
  gap: var(--space-2);
  min-height: 17rem;
  padding: clamp(var(--space-5), 3vw, var(--space-10));
  border-radius: var(--radius-card);
  overflow: hidden;
  background: var(--tile-linen);
  text-decoration: none;
  transition: transform var(--duration-base) var(--ease-brand);
}

.tile:hover { transform: translateY(-2px); }

.tile--linen { background: var(--tile-linen); }
.tile--amber { background: var(--tile-amber); }
.tile--ash { background: var(--tile-ash); }
.tile--clay { background: var(--tile-clay); }
.tile--ink { background: var(--tile-ink); color: var(--color-paper); }

.tile__img {
  position: absolute;
  right: 0;
  bottom: 0;
  width: 56%;
  max-width: 17rem;
  object-fit: contain;
  pointer-events: none;
}

.tile__body {
  position: relative;
  display: grid;
  gap: var(--space-2);
  max-width: 24ch;
}

/* Les deux grandes tuiles de collection de la référence : dégradé entre deux
   tons de la palette, texte en haut à gauche, bouton circulaire en bas à
   droite. Le dégradé se calcule par color-mix entre jetons existants, donc
   aucune onzième couleur n'entre par cette porte (§19.7). */
.duo {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(22rem, 100%), 1fr));
  gap: var(--space-4);
}

.collection {
  position: relative;
  display: grid;
  align-content: start;
  gap: var(--space-3);
  min-height: 21rem;
  padding: clamp(var(--space-6), 3vw, var(--space-10));
  border-radius: var(--radius-card);
  overflow: hidden;
  text-decoration: none;
}

.collection__img {
  position: absolute;
  right: -6%;
  bottom: 0;
  width: 58%;
  max-width: 20rem;
  object-fit: contain;
  pointer-events: none;
}

.collection__body {
  position: relative;
  display: grid;
  gap: var(--space-2);
  max-width: 22ch;
}

.collection__go {
  position: absolute;
  right: var(--space-5);
  bottom: var(--space-5);
  display: grid;
  place-items: center;
  width: 2.75rem;
  height: 2.75rem;
  border-radius: var(--radius-pill);
  background: var(--color-paper);
  color: var(--color-ink);
  transition: transform var(--duration-base) var(--ease-brand);
}

.collection:hover .collection__go { transform: translateX(3px); }
.collection__go svg { width: 0.9rem; height: 0.9rem; }

/* Encart promotionnel : un aplat crème, deux lignes, une pastille. */
.promo {
  display: grid;
  gap: var(--space-2);
  padding: clamp(var(--space-6), 3vw, var(--space-10));
  border-radius: var(--radius-card);
  background: var(--color-cream);
}

.promo__actions { margin-top: var(--space-4); }

/* Tuile de catégorie, plus compacte : une vignette et un libellé. */
.catcard {
  display: grid;
  gap: var(--space-3);
  text-decoration: none;
}

.catcard__media {
  aspect-ratio: var(--ratio-card);
  width: 100%;
  object-fit: contain;
  border-radius: var(--radius-card);
  background: var(--tile-linen);
  transition: transform var(--duration-base) var(--ease-brand);
}

.catcard:hover .catcard__media { transform: translateY(-3px); }

/* ── C-11 · Étoiles d'avis, rendues par nous ──────────────────────────────── */

.stars {
  position: relative;
  display: inline-block;
  line-height: 1;
  color: var(--color-line);
}

.stars__fill {
  position: absolute;
  inset: 0;
  overflow: hidden;
  white-space: nowrap;
  color: var(--color-gold-ink);
}

.stars svg {
  display: inline-block;
  width: 0.85rem;
  height: 0.85rem;
}

.ratingline {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-2xs);
  color: var(--color-muted);
}

/* Répartition des notes : une barre par niveau. */
.meter {
  height: 0.4rem;
  border-radius: var(--radius-pill);
  background: var(--color-line);
  overflow: hidden;
}

.meter__fill {
  height: 100%;
  border-radius: var(--radius-pill);
  background: var(--color-gold-ink);
}

/* Un remplissage se mesure en pourcentage, et un pourcentage écrit en attribut
   `style` est interdit par la CSP du §18 comme par la porte G10. Le pas de 10 %
   — une demi-étoile — est déclaré ici, une fois, et l'attribut ne fait que
   choisir. Sans JavaScript, la note s'affiche quand même. */
[data-fill="0"] { width: 0; }
[data-fill="10"] { width: 10%; }
[data-fill="20"] { width: 20%; }
[data-fill="30"] { width: 30%; }
[data-fill="40"] { width: 40%; }
[data-fill="50"] { width: 50%; }
[data-fill="60"] { width: 60%; }
[data-fill="70"] { width: 70%; }
[data-fill="80"] { width: 80%; }
[data-fill="90"] { width: 90%; }
[data-fill="100"] { width: 100%; }

/* ── C-12 · Barre de facettes ─────────────────────────────────────────────── */

.filterbar {
  position: sticky;
  top: var(--height-header);
  z-index: var(--z-sticky);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  padding-block: var(--space-4);
  background: var(--color-paper);
  border-bottom: 1px solid var(--color-line);
}

.chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: 0.3rem var(--space-4);
  border: 1px solid var(--color-line-strong);
  border-radius: var(--radius-pill);
  background: var(--color-paper);
  color: var(--color-ink);
  font-size: var(--text-2xs);
  letter-spacing: var(--tracking-wide);
  text-decoration: none;
  cursor: pointer;
  transition:
    background var(--duration-fast) var(--ease-brand),
    color var(--duration-fast) var(--ease-brand);
}

.chip:hover { background: var(--color-cream); }

.chip[aria-current="page"],
.chip[data-selected="true"] {
  background: var(--color-ink);
  border-color: var(--color-ink);
  color: var(--color-paper);
}

/* Le panneau de filtres est un <details> unique, rendu une seule fois.
   L'afficher deux fois — une version mobile repliée, une version bureau en
   colonne — dupliquait chaque `id` du formulaire : deux `<label for="tri">`
   pointaient vers deux `<select id="tri">`, et cliquer sur un libellé pouvait
   activer le mauvais champ. C'est la porte G14 qui l'a signalé.

   Au-delà de la rupture, le résumé disparaît et le contenu se déplie sans
   JavaScript : `::details-content` pour les navigateurs récents, `display` sur
   l'enfant pour les autres. Là où ni l'un ni l'autre n'est compris, le panneau
   reste replié derrière son résumé — dégradé, mais utilisable. */
.facetbox {
  min-width: 0;
}

.facetbox > summary {
  display: inline-flex;
  width: fit-content;
  margin-bottom: var(--space-4);
  cursor: pointer;
  list-style: none;
}

.facets {
  border-top: 1px solid var(--color-line);
  padding-block: var(--space-6);
}

.facets__group {
  border: 0;
  padding: 0;
  margin: 0;
}

.facets__list {
  display: grid;
  gap: var(--space-2);
  margin-top: var(--space-3);
}

.check {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-sm);
  cursor: pointer;
}

.check input {
  width: 1rem;
  height: 1rem;
  accent-color: var(--color-ink);
  flex: none;
}

/* ── Accordéon (fiche produit) ────────────────────────────────────────────── */

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

.accordion__summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  padding-block: var(--space-4);
  font-size: var(--text-2xs);
  font-weight: var(--weight-medium);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  cursor: pointer;
  list-style: none;
}

.accordion__summary::after {
  content: "";
  flex: none;
  width: 0.5rem;
  height: 0.5rem;
  border-right: 1px solid currentcolor;
  border-bottom: 1px solid currentcolor;
  transform: translateY(-0.15em) rotate(45deg);
  transition: transform var(--duration-fast) var(--ease-brand);
}

.accordion[open] .accordion__summary::after {
  transform: translateY(0.1em) rotate(225deg);
}

.accordion__body {
  padding-bottom: var(--space-6);
  font-size: var(--text-sm);
  color: var(--color-muted);
}

/* ── Fiche produit ────────────────────────────────────────────────────────── */

.pdp {
  display: grid;
  gap: clamp(var(--space-8), 4vw, var(--space-16));
  align-items: start;
}

.gallery {
  display: grid;
  gap: var(--space-3);
  align-content: start;
}

.gallery__main {
  aspect-ratio: var(--ratio-card);
  width: 100%;
  object-fit: contain;
  border-radius: var(--radius-card);
  background: var(--color-cream);
}

.gallery__thumbs {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(4.5rem, 1fr));
  gap: var(--space-2);
}

.gallery__thumb {
  aspect-ratio: 1 / 1;
  width: 100%;
  object-fit: contain;
  border: 1px solid var(--color-line);
  border-radius: var(--radius-sm);
  background: var(--color-cream);
  cursor: pointer;
  transition: border-color var(--duration-fast) var(--ease-brand);
}

.gallery__thumb:hover,
.gallery__thumb[data-active="true"] { border-color: var(--color-ink); }

.buybox {
  display: grid;
  gap: var(--space-5);
  align-content: start;
}

.priceline {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--space-3);
  font-size: var(--text-3xl);
  font-variant-numeric: tabular-nums;
}

.sizes {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.trustrow {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(9rem, 100%), 1fr));
  gap: var(--space-4);
  padding-block: var(--space-6);
  border-top: 1px solid var(--color-line);
  border-bottom: 1px solid var(--color-line);
  font-size: var(--text-2xs);
  color: var(--color-muted);
  text-align: center;
}

.trustrow svg {
  width: 1.15rem;
  height: 1.15rem;
  margin-inline: auto;
  margin-bottom: var(--space-2);
  color: var(--color-ink);
}

/* Pyramide olfactive : trois colonnes, tête · cœur · fond. */
.pyramid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(14rem, 100%), 1fr));
  gap: var(--space-8);
}

.pyramid__col {
  display: grid;
  gap: var(--space-3);
  align-content: start;
}

.pyramid__disc {
  display: grid;
  place-items: center;
  width: 4.5rem;
  height: 4.5rem;
  border-radius: var(--radius-pill);
  background: var(--tile-amber);
  font-family: var(--font-serif);
  font-size: var(--text-lg);
}

/* ── Tuile de rayon illustrée ────────────────────────────────────────────── */

/* La photographie remplace la teinte de thème quand elle existe. Le `alt` reste
   vide à dessein : la tuile porte déjà son nom en texte, et redire « Bougies »
   ferait entendre deux fois la même chose à un lecteur d'écran. */
.collection--photo {
  position: relative;
  isolation: isolate;
}

.collection__photo {
  position: absolute;
  inset: 0;
  z-index: -2;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Même raisonnement que pour la bannière : un texte posé sur une photographie
   quelconque n'a pas de contraste garanti, donc on impose une luminance connue
   plutôt que d'espérer. */
.collection__veil {
  position: absolute;
  inset: 0;
  z-index: -1;
  background: var(--scrim-video);
}

.collection--photo .collection__body,
.collection--photo .collection__go {
  color: var(--color-paper);
}

.collection--photo .eyebrow--ink,
.collection--photo .text-muted {
  color: var(--color-paper);
}

/* ── Bannière animée (§19.4) ─────────────────────────────────────────────── */

.hero__video {
  grid-area: 1 / 1;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Le modificateur porte l'état « cette bannière est animée » : le voile
   s'alourdit, et la commande de pause n'apparaît que là. */
.hero--video {
  position: relative;
}

.hero--video .hero__scrim {
  background: var(--scrim-video);
}

/* La commande de pause n'est pas une option de confort : un contenu qui bouge
   seul plus de cinq secondes doit pouvoir être arrêté (WCAG 2.2.2). Elle est
   discrète mais réelle, et atteignable au clavier. */
.hero__pause {
  grid-area: 1 / 1;
  z-index: 2;
  justify-self: end;
  align-self: start;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--target-min);
  height: var(--target-min);
  margin: var(--space-4);
  border: 1px solid var(--color-paper);
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--color-paper);
}

.hero__pause:hover,
.hero__pause:focus-visible {
  background: var(--color-paper);
  color: var(--color-ink);
}

.hero__pause svg {
  width: 1rem;
  height: 1rem;
}

/* Qui a demandé moins d'animations garde l'affiche fixe : la vidéo est masquée
   et le poster du `<video>` occupe la place, sans qu'aucune image bouge. */
@media (prefers-reduced-motion: reduce) {
  .hero__pause { display: none; }
}

/* ── Suggestions de recherche (§8) ───────────────────────────────────────── */

/* La liste se pose au-dessus de la page : c'est le seul endroit du système où
   une surface flotte, donc le seul emploi légitime de --shadow-overlay (§19.3).
   Sans elle, la limite entre la liste et le contenu qu'elle recouvre disparaît. */
.suggestions {
  position: absolute;
  z-index: 40;
  top: calc(100% + var(--space-2));
  left: 0;
  right: 0;
  max-height: 60vh;
  overflow-y: auto;
  padding: var(--space-2);
  border: 1px solid var(--color-line);
  border-radius: var(--radius-card);
  background: var(--color-paper);
  box-shadow: var(--shadow-overlay);
}

.suggestions:empty,
.suggestions[hidden] { display: none; }

.suggestions__group {
  padding: var(--space-2) var(--space-3) var(--space-1);
  font-size: var(--text-2xs);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--color-muted);
}

.suggestions__item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  min-height: var(--target-min);
  padding-inline: var(--space-3);
  border-radius: var(--radius-card);
  text-decoration: none;
  color: var(--color-ink);
}

.suggestions__item:hover,
.suggestions__item:focus-visible { background: var(--color-cream); }

.suggestions__all {
  display: flex;
  align-items: center;
  min-height: var(--target-min);
  margin-top: var(--space-2);
  padding-inline: var(--space-3);
  border-top: 1px solid var(--color-line);
  font-size: var(--text-2xs);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
}

.suggestions__empty {
  padding: var(--space-3);
  font-size: var(--text-sm);
  color: var(--color-muted);
}

/* ── Lecture ─────────────────────────────────────────────────────────────── */

/* L'en-tête est collant : sans cette réserve, une ancre — un lien du
   récapitulatif d'erreurs, le retour vers #reviews — dépose sa cible sous la
   barre, et l'utilisateur croit que le lien n'a rien fait. */
html {
  scroll-padding-top: calc(var(--height-header) + var(--space-6));
}

/* Un titre qui laisse un mot seul sur la dernière ligne se lit comme une
   coquille. `balance` répartit les lignes, `pretty` se contente d'éviter
   l'orpheline sur les paragraphes — moins coûteux sur un texte long. */
h1, h2, h3,
.display,
.showcard__name,
.card__name {
  text-wrap: balance;
}

p,
.lede,
.prose {
  text-wrap: pretty;
}

/* La longueur de ligne conditionne la lecture plus que le corps de texte.
   Au-delà d'environ 75 signes, l'œil rate le début de la ligne suivante. */
.prose,
.lede {
  max-width: 68ch;
}

/* ── Cibles tactiles et retours d'appui ──────────────────────────────────── */

/* Mesuré avant d'être corrigé : sur cinq pages en 390 px, 38 cases à cocher
   tenaient dans 16 px, les boutons plafonnaient à 40 px de haut, la croix du
   bandeau à 32. Aucun de ces contrôles n'a besoin de grossir à l'œil — c'est
   la surface cliquable qui manquait.

   Les liens en pleine phrase sont exclus : WCAG 2.5.8 les exempte, et les
   espacer casserait l'interlignage du texte courant. */

.btn,
.chip,
.check,
.linkline,
.facetbox > summary,
.accordion__summary {
  min-height: var(--target-min);
}

/* Lien étendu : le titre porte le lien, son pseudo-élément couvre la carte.
   Toute la carte devient cliquable sans qu'un deuxième lien apparaisse dans
   l'arbre d'accessibilité — la version précédente doublait le lien sur
   l'image et le masquait par `aria-hidden`, ce qui marchait mais laissait au
   doigt une bande de vingt-neuf pixels sur le titre et rien sur le corps.
   Le bouton d'ajout repasse au-dessus : c'est une action distincte. */
.card,
.showcard {
  position: relative;
}

.card__name a::after,
.showcard__name a::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
}

.card__cta,
.showcard__cta,
.card__badges,
.card .stars {
  position: relative;
  z-index: 2;
}

.iconbtn {
  width: var(--target-min);
  height: var(--target-min);
}

/* La marque et les liens de pied de page sont hauts de dix-neuf pixels par
   dessin. On garde le dessin, on épaissit la zone. */
.brandmark,
.footer__list a,
.megamenu__link,
.crumbs a {
  display: inline-flex;
  align-items: center;
  min-height: var(--target-min);
}

/* La croix du bandeau reste dessinée en 32 px ; sa zone de contact déborde. */
.announce__close::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: var(--target-min);
  height: var(--target-min);
  transform: translate(-50%, -50%);
}

/* Supprime le délai de 300 ms hérité du double-tap, et empêche le navigateur
   d'interpréter un appui maintenu sur un bouton comme un début de zoom. */
a,
button,
summary,
label,
select,
[role="button"],
input[type="checkbox"],
input[type="radio"] {
  touch-action: manipulation;
}

button,
summary,
label[for],
[role="button"],
input[type="checkbox"],
input[type="radio"] {
  cursor: pointer;
}

/* Retour à l'appui. Sans lui, un bouton tapé sur mobile ne répond qu'à la fin
   de la requête : l'utilisateur tape une deuxième fois, et commande deux fois. */
.btn:active,
.iconbtn:active,
.chip:active,
.collection:active,
.card__name a:active {
  opacity: var(--pressed-opacity);
}

/* Un élément désactivé doit se voir désactivé avant d'être cliqué. */
.btn[disabled],
button[disabled],
[aria-disabled="true"] {
  opacity: 0.45;
  cursor: not-allowed;
}

/* ── Avis clients (§5.8) ──────────────────────────────────────────────────── */

/* La distribution vit à côté de la moyenne, jamais à sa place : une note de
   4,2 peut recouvrir vingt avis tièdes comme dix enthousiastes et dix furieux,
   et le client a le droit de voir lequel des deux il achète. */
.reviews__summary {
  display: grid;
  gap: var(--space-8);
  padding: clamp(var(--space-6), 3vw, var(--space-10));
  border-radius: var(--radius-card);
  background: var(--color-cream);
}

.reviews__score {
  font-family: var(--font-serif);
  font-size: var(--text-5xl);
  line-height: var(--leading-none);
  font-variant-numeric: tabular-nums;
}

.reviews__bars {
  display: grid;
  gap: var(--space-2);
}

.reviews__bar {
  display: grid;
  grid-template-columns: 2.5rem minmax(0, 1fr) 2.5rem;
  align-items: center;
  gap: var(--space-3);
  font-size: var(--text-2xs);
  color: var(--color-muted);
}

.review {
  display: grid;
  gap: var(--space-2);
  padding-block: var(--space-6);
  border-bottom: 1px solid var(--color-line);
}

.review__head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
}

.review__title {
  font-family: var(--font-serif);
  font-size: var(--text-lg);
  line-height: var(--leading-tight);
}

.review__body {
  font-size: var(--text-sm);
  line-height: var(--leading-normal);
}

/* ── C-15 · Bandeau défilant ──────────────────────────────────────────────── */

.marquee {
  overflow: hidden;
  padding-block: var(--space-4);
  border-top: 1px solid var(--color-line);
  border-bottom: 1px solid var(--color-line);
}

.marquee__track {
  display: flex;
  gap: var(--space-10);
  width: max-content;
  animation: marquee 44s linear infinite;
}

.marquee__track > * {
  font-size: var(--text-2xs);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--color-muted);
  white-space: nowrap;
}

@keyframes marquee {
  from { transform: translateX(0); }
  to { transform: translateX(-50%); }
}

/* ── Formulaires ──────────────────────────────────────────────────────────── */

/* La bordure atteint le seuil de contraste des éléments non textuels (3:1,
   WCAG 1.4.11) — d'où line-strong et non line. Un formulaire ne se pose jamais
   sur sand : la bordure y tomberait à 2,88:1. */
.field {
  display: block;
  width: 100%;
  min-height: var(--height-btn);
  padding: var(--space-2) var(--space-4);
  border: 1px solid var(--color-line-strong);
  border-radius: var(--radius-sm);
  background: var(--color-paper);
  color: var(--color-ink);
  font-size: var(--text-base); /* 16px : en dessous, iOS zoome au focus */
}

.field:focus-visible {
  border-color: var(--color-ink);
  outline: 2px solid var(--color-ink);
  outline-offset: 1px;
}

.field--pill {
  border-radius: var(--radius-pill);
  padding-inline: var(--space-5);
}

.field--inline { min-height: var(--height-btn-card); }

.formrow { display: grid; gap: var(--space-2); }

/* ── Tiroirs : panier et navigation mobile ────────────────────────────────── */

.drawer {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  z-index: var(--z-drawer);
  display: flex;
  flex-direction: column;
  width: min(28rem, 100vw);
  background: var(--color-paper);
  box-shadow: var(--shadow-overlay);
  transform: translateX(100%);
  transition: transform var(--duration-base) var(--ease-brand);
}

.drawer--left {
  right: auto;
  left: 0;
  transform: translateX(-100%);
}

/* Ouvert seulement s'il contient quelque chose : un panneau vide qui glisse à
   l'écran au chargement est une nuisance, pas une confirmation (ADR-0052). */
.drawer[data-open="true"] { transform: translateX(0); }

.drawer__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-5) var(--space-6);
  border-bottom: 1px solid var(--color-line);
}

.drawer__body {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: var(--space-6);
}

.drawer__foot {
  padding: var(--space-6);
  border-top: 1px solid var(--color-line);
  background: var(--color-cream);
}

.drawer__veil {
  position: fixed;
  inset: 0;
  z-index: var(--z-veil);
  background: var(--veil);
}

.drawer__veil[hidden] { display: none; }

.mobilenav { display: grid; gap: var(--space-1); }

.mobilenav a {
  display: block;
  padding-block: var(--space-3);
  border-bottom: 1px solid var(--color-line);
  font-family: var(--font-serif);
  font-size: var(--text-2xl);
  text-decoration: none;
}

/* ── Panier ───────────────────────────────────────────────────────────────── */

.cartline {
  display: grid;
  grid-template-columns: 4.5rem minmax(0, 1fr) auto;
  gap: var(--space-4);
  align-items: start;
  padding-bottom: var(--space-5);
  border-bottom: 1px solid var(--color-line);
}

.cartline__img {
  aspect-ratio: 1 / 1;
  width: 100%;
  object-fit: contain;
  border-radius: var(--radius-sm);
  background: var(--color-cream);
}

.qty {
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--color-line-strong);
  border-radius: var(--radius-pill);
  overflow: hidden;
}

.qty input {
  width: 3rem;
  min-height: var(--height-btn-small);
  padding: 0;
  border: 0;
  background: transparent;
  text-align: center;
  font-variant-numeric: tabular-nums;
}

.totals { display: grid; gap: var(--space-2); }

.totals__row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-4);
  font-size: var(--text-sm);
}

.totals__row--strong {
  font-size: var(--text-md);
  font-weight: var(--weight-medium);
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-line);
}

/* ── Tunnel ───────────────────────────────────────────────────────────────── */

.steps {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  padding-bottom: var(--space-4);
  border-bottom: 1px solid var(--color-line);
  font-size: var(--text-2xs);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
}

.summary {
  padding: var(--space-6);
  border-radius: var(--radius-card);
  background: var(--color-cream);
}

.notice {
  padding: var(--space-4) var(--space-5);
  border-radius: var(--radius-sm);
  background: var(--color-cream);
  font-size: var(--text-sm);
}

.notice--error {
  background: var(--color-paper);
  border: 1px solid var(--color-signal);
  color: var(--color-signal);
}

.crumbs {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  padding-block: var(--space-5);
  font-size: var(--text-2xs);
  color: var(--color-muted);
}

.crumbs a { text-decoration: none; }
.crumbs a:hover { text-decoration: underline; }

/* ── C-16 · Pied de page ──────────────────────────────────────────────────── */

.footer {
  margin-top: clamp(var(--space-16), 8vw, var(--space-32));
  border-top: 1px solid var(--color-line);
  background: var(--color-cream);
}

.footer__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(13rem, 100%), 1fr));
  gap: var(--space-10);
  padding-block: clamp(var(--space-10), 5vw, var(--space-16));
}

.footer__list {
  display: grid;
  gap: var(--space-2);
  margin-top: var(--space-4);
  font-size: var(--text-sm);
}

.footer__list a { text-decoration: none; }
.footer__list a:hover { text-decoration: underline; }

/* Le crédit de conception vit sur la même ligne que les mentions de prix :
   c'est une information de provenance, pas un argument de vente. */
.footer__credit {
  color: var(--color-muted);
}

.footer__credit a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 0.2em;
}

.footer__credit a:hover,
.footer__credit a:focus-visible { color: var(--color-ink); }

.footer__bottom {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: var(--space-4);
  padding-block: var(--space-6);
  border-top: 1px solid var(--color-line);
  font-size: var(--text-2xs);
  color: var(--color-muted);
}

/* ── Divers ───────────────────────────────────────────────────────────────── */

.prose { max-width: var(--width-prose); }

.prose p + p { margin-top: var(--space-4); }

/* Lien d'évitement : premier élément focalisable, invisible jusqu'au focus. */
.skip-link {
  position: absolute;
  left: var(--space-2);
  top: -4rem;
  z-index: var(--z-skip);
  padding: var(--space-3) var(--space-4);
  background: var(--color-ink);
  color: var(--color-paper);
  border-radius: var(--radius-sm);
  transition: top var(--duration-fast) var(--ease-brand);
}

.skip-link:focus { top: var(--space-2); }

/* Visible pour un lecteur d'écran, absent de l'affichage. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ── Variantes responsives ────────────────────────────────────────────────────
   Une seule rupture STRUCTURELLE, à 1024px (§19.3) : c'est la seule qui change
   la mise en page. Les ruptures 768px et 1280px n'ajustent que la densité d'une
   grille, ce qui ne réorganise rien — et le test de contrat
   test_a_single_breakpoint_changes_the_layout le vérifie propriété par
   propriété. Tout le reste s'adapte par clamp() et minmax(), sans rupture. */

@media (width >= 768px) {
  .md\:block { display: block; }
  .md\:flex { display: flex; }
  .md\:grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .md\:grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

@media (width >= 1024px) {
  .page { padding-inline: var(--space-6); }

  .lg\:hidden { display: none; }
  .lg\:block { display: block; }
  .lg\:flex { display: flex; }
  .lg\:grid { display: grid; }
  .lg\:grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .lg\:grid-cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }

  .nav { display: flex; }
  .searchbox { display: flex; }
  .rail__nav { display: flex; }

  .hero__media { aspect-ratio: var(--ratio-hero-wide); }
  .hero--split { grid-template-columns: 1.05fr 1fr; }
  .hero--split .hero__media { aspect-ratio: auto; height: 100%; min-height: 32rem; }

  .pdp { grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr); }

  /* La galerie suit la lecture de la colonne droite, plus longue. */
  .gallery { position: sticky; top: calc(var(--height-header) + var(--space-6)); }

  .listing { display: grid; grid-template-columns: 16rem minmax(0, 1fr); gap: var(--space-12); align-items: start; }

  /* Contenu à gauche, récapitulatif à droite : panier et tunnel. */
  .withside { display: grid; grid-template-columns: minmax(0, 1fr) 21rem; gap: var(--space-10); align-items: start; }
  .withside > aside { position: sticky; top: calc(var(--height-header) + var(--space-6)); }

  .facets { position: sticky; top: calc(var(--height-header) + var(--space-6)); border-top: 0; padding-block: 0; }

  .reviews__summary { grid-template-columns: minmax(0, 14rem) minmax(0, 1fr); align-items: center; }

  .facetbox > summary { display: none; }
  .facetbox::details-content { content-visibility: visible; }
  .facetbox > .facets { display: block; }
}

@media (width >= 1280px) {
  .xl\:grid-cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

/* ── Impression ───────────────────────────────────────────────────────────────
   Une commande se garde sur papier : l'en-tête, le pied et les commandes
   d'interface n'y ont rien à faire. */

@media print {
  .skip-link,
  .announce,
  .masthead,
  .footer,
  .drawer,
  .drawer__veil,
  .btn,
  form {
    display: none !important;
  }

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