/* =========================================================================
   WINK PHONE — Système de design
   Commerce clair et net : fond presque blanc, filets d'un pixel, beaucoup de
   respiration. Toute la couleur vient des produits ; l'indigo ne sert qu'aux
   actions et aux éléments actifs.

   Les jetons ci-dessous sont ceux du mode **jour**. Le mode nuit ne redéfinit
   que des couleurs, dans `themes.css` — aucune règle de composant n'y change,
   ce qui garantit que les deux modes ont exactement la même mise en page.
   ========================================================================= */

:root {
  /* Les contrôles natifs — ascenseurs, sélecteurs de date, champs de saisie du système —
     suivent ce jeton et rien d'autre. Sans lui, un ascenseur clair restait collé au bord
     d'une page de nuit. Le mode nuit le repasse à `dark` dans `themes.css`. */
  color-scheme: light;

  /* couleurs ------------------------------------------------------------ */
  --ink-950: #F7F7FA;
  --ink-900: #FFFFFF;
  --surface: #FFFFFF;
  --surface-2: #F2F2F7;
  --surface-3: #E7E7EF;
  --line: rgba(17, 17, 34, .10);
  --line-strong: rgba(17, 17, 34, .18);

  --violet: #6D46F2;
  --violet-bright: #5B2FE0;
  --violet-deep: #4A20C9;
  --blue: #1D62F0;
  --blue-bright: #1D62F0;
  --cyan: #0E8F8F;

  --text: #14142B;
  --text-soft: #3C3C55;
  --muted: #55556D;       /* 7,4:1 sur le fond nuit */
  --muted-dim: #6A6A82;   /* 5,5:1 — palier le plus bas, toujours conforme AA */

  --success: #0E7C57;
  /* Encre posée sur les aplats de couleur d'état, comme `--on-gradient` pour le dégradé :
     si une charte ajuste `--danger`, le contraste suit au lieu de casser en silence. */
  --on-danger: #2A0A12;
  --warning: #FBBF24;
  --danger: #FB7185;

  /* Le rouge des promotions. Trois jetons plutôt qu'un, parce qu'un rouge de pastille et un
     rouge de texte ne peuvent pas être la même valeur : posé en aplat il lui faut une encre
     contrastée, posé en lettres sur le fond de la page il doit être plus clair pour rester
     lisible. Le mode nuit redéfinit les trois dans `themes.css` — un rouge écrit en dur ici
     serait illisible sur l'un des deux fonds.
       nuit : encre 5,2:1 sur la pastille, texte 8,1:1 sur le fond. */
  --promo: #D81E3A;         /* aplat : pastille de remise, bandeau d'annonce */
  --on-promo: #FFFFFF;      /* encre posée sur l'aplat */
  --promo-text: #C2182F;    /* le rouge en lettres, sur le fond de la page */
  --promo-soft: rgba(216, 30, 58, .07);
  --promo-line: rgba(216, 30, 58, .22);

  --gradient: linear-gradient(120deg, #6D46F2 0%, #1D62F0 100%);
  /* Couleur du texte pose sur le degrade : elle suit la clarte de celui-ci. */
  --on-gradient: #ffffff;
  --gradient-soft: linear-gradient(120deg, rgba(109, 70, 242, .09), rgba(29, 98, 240, .09));
  --glow-violet: radial-gradient(circle at center, rgba(109, 70, 242, .12), transparent 68%);
  --glow-blue: radial-gradient(circle at center, rgba(29, 98, 240, .10), transparent 68%);

  /* typographie --------------------------------------------------------- */
  /* Manrope porte les titres et le texte courant. Une seule famille pour les deux : ses
     graisses hautes ont assez de caractère pour un titre, ses graisses basses restent
     lisibles en petit corps — inutile d'en charger une seconde. La chasse fixe reste
     réservée aux prix, aux compteurs et aux références, où l'alignement des chiffres
     compte plus que l'élégance. */
  --font-display: 'Manrope', 'SF Pro Display', system-ui, sans-serif;
  --font-body: 'Manrope', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --font-mono: 'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, monospace;

  /* espacement ---------------------------------------------------------- */
  --s-1: 4px;  --s-2: 8px;  --s-3: 12px; --s-4: 16px; --s-5: 24px;
  --s-6: 32px; --s-7: 48px; --s-8: 64px; --s-9: 96px; --s-10: 128px;

  --radius-sm: 6px;
  --radius: 12px;
  --radius-lg: 18px;
  --radius-xl: 26px;

  --shadow-card: 0 1px 2px rgba(17, 17, 34, .05);
  --shadow-float: 0 18px 40px -22px rgba(17, 17, 34, .28);
  --shadow-pop: 0 24px 48px -18px rgba(17, 17, 34, .22);

  /* surfaces flottantes : en-tete, menus, infobulles, voile de la visionneuse */
  --header-bg: rgba(255, 255, 255, .86);
  /* L'en-tête compact s'opacifie pour se détacher du contenu qui défile dessous.
     C'est un jeton et non une valeur en dur : chaque direction artistique le redéfinit,
     faute de quoi la charte claire se retrouverait avec un texte sombre sur fond noir. */
  --header-bg-compact: rgba(255, 255, 255, .97);
  --pop-bg: #FFFFFF;
  /* Translucides, parce qu'elles flottent au-dessus du contenu et le laissent transparaître
     sous un flou. Le mode nuit en donne sa version sombre dans `themes.css`. */
  --barre-achat-fond: rgba(255, 255, 255, .94);
  --lb-close-fond: rgba(255, 255, 255, .92);
  --menu-bg: #FFFFFF;
  --veil-bg: rgba(20, 20, 43, .92);
  --grid-line: transparent;
  --media-bg: #FFFFFF;

  --wrap: 1280px;
  --header-h: 116px;
  --entete-bas: 116px;        /* remesuré au chargement, voir app.js */
  --ease: cubic-bezier(.22, .8, .32, 1);
  --repair-ombre: rgba(17, 17, 34, .13);
}

/* ------------------------------------------------------------------ base */

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

html {
  -webkit-text-size-adjust: 100%; scroll-behavior: smooth;
  /* L'en-tête est collant : sans cette marge, tout défilement automatique — ancre,
     mise au point d'un champ, retour de validation — dépose l'élément visé dessous. */
  scroll-padding-top: calc(var(--header-h) + 12px);
}
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background: var(--ink-950);
  color: var(--text);
  font-family: var(--font-body);
  font-size: 15px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  /* `clip` et non `hidden` : avec `hidden` sur un seul axe, l'autre passe d'office en
     `auto` et le body devient lui-même le conteneur de défilement. La page ne défile
     alors plus par la fenêtre — `window.scrollY` reste figé — et tout ce qui écoute le
     défilement (en-tête compact, barre d'achat collante) ne reçoit plus rien.
     `clip` masque le débordement horizontal sans créer de conteneur. */
  overflow-x: clip;
}

/* Trame technique de fond : une grille très discrète, pas un décor. */
body::before {
  content: '';
  position: fixed; inset: 0;
  background-image:
    linear-gradient(var(--grid-line) 1px, transparent 1px),
    linear-gradient(90deg, var(--grid-line) 1px, transparent 1px);
  background-size: 64px 64px;
  mask-image: radial-gradient(ellipse 120% 80% at 50% 0%, #000 20%, transparent 75%);
  pointer-events: none;
  z-index: 0;
}

#shell { position: relative; z-index: 1; }

h1, h2, h3, h4 { font-family: var(--font-display); font-weight: 600; letter-spacing: -.02em; margin: 0; line-height: 1.12; }
h1 { font-size: clamp(2.4rem, 5.4vw, 4.1rem); letter-spacing: -.035em; }
h2 { font-size: clamp(1.7rem, 3.2vw, 2.6rem); letter-spacing: -.028em; }
h3 { font-size: clamp(1.15rem, 1.7vw, 1.4rem); }
p  { margin: 0; }
a  { color: inherit; text-decoration: none; }
img { max-width: 100%; display: block; }
button, input, select, textarea { font: inherit; color: inherit; }
[hidden] { display: none !important; }

::selection { background: rgba(124, 92, 255, .38); color: #fff; }

:focus-visible {
  outline: 2px solid var(--violet-bright);
  outline-offset: 3px;
  border-radius: 4px;
}

.skip-link {
  position: absolute; left: -9999px; top: 8px; z-index: 100;
  background: var(--violet); color: #fff; padding: 12px 16px; border-radius: var(--radius-sm);
}
.skip-link:focus { left: 8px; }

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

.wrap { width: min(100% - 40px, var(--wrap)); margin-inline: auto; }
@media (max-width: 640px) { .wrap { width: calc(100% - 32px); } }

.eyebrow {
  font-family: var(--font-mono);
  font-size: .72rem; letter-spacing: .16em; text-transform: uppercase;
  color: var(--violet-bright);
  display: inline-flex; align-items: center; gap: var(--s-2);
}
.eyebrow::before {
  content: ''; width: 22px; height: 1px; background: var(--gradient);
}

.section { padding-block: clamp(56px, 8vw, 104px); position: relative; }
.section-head { display: flex; align-items: flex-end; justify-content: space-between; gap: var(--s-5); margin-bottom: var(--s-6); flex-wrap: wrap; }
.section-head p { color: var(--muted); max-width: 52ch; margin-top: var(--s-3); }

.lede { color: var(--text-soft); font-size: clamp(1rem, 1.4vw, 1.12rem); max-width: 58ch; }
.mono { font-family: var(--font-mono); }
.muted { color: var(--muted); }
.hairline { height: 1px; background: linear-gradient(90deg, transparent, var(--line-strong), transparent); border: 0; margin: 0; }

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

.btn {
  --btn-bg: var(--surface-2);
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s-2);
  padding: 12px 22px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  background: var(--btn-bg);
  font-weight: 500; font-size: .94rem;
  cursor: pointer;
  transition: transform .18s var(--ease), border-color .18s var(--ease), background .18s var(--ease), box-shadow .18s var(--ease);
  white-space: nowrap;
}
.btn:hover { transform: translateY(-1px); border-color: var(--violet); }
.btn:active { transform: translateY(0); }
.btn:disabled { opacity: .5; cursor: not-allowed; transform: none; }

.btn-primary {
  background: var(--gradient);
  border-color: transparent;
  color: var(--on-gradient); font-weight: 600;
  box-shadow: 0 10px 30px -12px rgba(102, 74, 255, .85);
}
.btn-primary:hover { box-shadow: 0 16px 38px -12px rgba(102, 74, 255, 1); border-color: transparent; }

.btn-ghost { background: transparent; border-color: var(--line); }
.btn-ghost:hover { background: var(--surface-2); }

.btn-lg { padding: 15px 28px; font-size: 1rem; border-radius: var(--radius-lg); }
.btn-sm { padding: 8px 14px; font-size: .84rem; border-radius: var(--radius-sm); }
.btn-block { width: 100%; }

/* ------------------------------------------------------------------ puces */

.chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 7px 14px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: rgba(22, 21, 46, .6);
  font-size: .85rem; color: var(--text-soft);
  cursor: pointer;
  transition: all .16s var(--ease);
}
.chip:hover { border-color: var(--violet); color: var(--text); transform: translateY(-1px); }
.chip[aria-pressed="true"], .chip.is-active { background: var(--gradient-soft); border-color: var(--violet); color: #fff; }
.chip .n { font-family: var(--font-mono); font-size: .72rem; color: var(--muted-dim); }

.tag {
  display: inline-flex; align-items: center; gap: 5px;
  font-family: var(--font-mono); font-size: .68rem; letter-spacing: .08em; text-transform: uppercase;
  padding: 4px 9px; border-radius: 5px;
  background: rgba(124, 92, 255, .12); color: var(--violet-bright);
  border: 1px solid rgba(124, 92, 255, .22);
}
.tag-blue { background: rgba(46,125,255,.12); color: var(--blue-bright); border-color: rgba(46,125,255,.24); }
.tag-green { background: rgba(52,211,153,.1); color: var(--success); border-color: rgba(52,211,153,.22); }

/* ----------------------------------------------------------------- cartes */

.card {
  background: linear-gradient(180deg, rgba(29,28,60,.55), rgba(16,16,36,.75));
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
}

/* ---------------------------------------------------------------- entrées */

.field { display: flex; flex-direction: column; gap: 7px; }
.field label { font-size: .82rem; color: var(--text-soft); font-weight: 500; }
.input, .select, textarea.input {
  width: 100%;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 12px 14px;
  transition: border-color .16s var(--ease), box-shadow .16s var(--ease);
}
.input:hover, .select:hover { border-color: var(--line-strong); }
.input:focus, .select:focus {
  outline: none; border-color: var(--violet);
  box-shadow: 0 0 0 3px rgba(124, 92, 255, .18);
}
.input::placeholder { color: var(--muted-dim); }
.select { appearance: none; cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5l5-5' stroke='%238B87B0' stroke-width='1.6' fill='none' stroke-linecap='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 13px center; background-size: 11px; padding-right: 34px; }

/* ------------------------------------------------------------------ prix */

.price { font-family: var(--font-display); font-weight: 600; letter-spacing: -.02em; }
.price-lg { font-size: clamp(1.9rem, 3vw, 2.4rem); }
.price .cur { font-size: .58em; margin-left: 2px; color: var(--text-soft); }
.price-ht { font-family: var(--font-mono); font-size: .76rem; color: var(--muted); }

/* ==================================================================== EN-TÊTE */

.header {
  position: sticky; top: 0; z-index: 50;
  background: var(--header-bg);
  backdrop-filter: blur(18px) saturate(140%);
  border-bottom: 1px solid var(--line);
}

.header-top {
  display: flex; align-items: center; gap: var(--s-5);
  width: min(100% - 40px, var(--wrap)); margin-inline: auto;
  height: 72px;
}

.logo { display: flex; align-items: center; gap: 10px; flex-shrink: 0; }

.logo svg { width: 34px; height: 34px; }
.logo-word { font-family: var(--font-display); font-weight: 700; font-size: 1.16rem; letter-spacing: -.03em; }
.logo-word span { color: var(--violet-bright); }

.header-search { flex: 1; max-width: 620px; margin-inline: auto; position: relative; }
.header-actions { display: flex; align-items: center; gap: var(--s-2); flex-shrink: 0; }

.icon-btn {
  position: relative;
  width: 42px; height: 42px; display: grid; place-items: center;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: rgba(22,21,46,.5); cursor: pointer;
  transition: all .16s var(--ease);
}
.icon-btn:hover { border-color: var(--violet); transform: translateY(-1px); }
.cart-count {
  position: absolute; top: -6px; right: -6px; min-width: 19px; height: 19px; padding: 0 5px;
  background: var(--gradient); border-radius: 999px;
  font-family: var(--font-mono); font-size: .66rem; font-weight: 500;
  display: grid; place-items: center; color: var(--on-gradient);
  border: 2px solid var(--ink-950);
}

.burger { display: none; }

/* ------------------------------------------------------- barre de marques */

.brand-bar { border-top: 1px solid var(--line); }
.brand-bar-inner {
  display: flex; align-items: center; gap: 2px;
  width: min(100% - 40px, var(--wrap)); margin-inline: auto;
  height: 43px;
}

/* Les rayons retrouvent leur pictogramme : le gérant les veut, et la barre élargie leur fait
   la place. Discret au repos, il prend la couleur de la marque au survol. */
.brand-ico { opacity: .5; flex-shrink: 0; }
.brand-link:hover .brand-ico { opacity: 1; color: var(--violet-bright); }
/* Seules les marques défilent : leur nombre vient du catalogue et n'est pas maîtrisé.
   Les rayons et les guides restent visibles quoi qu'il arrive. */
.brand-bar-marques {
  display: flex; align-items: center; gap: 2px; height: 100%;
  flex: 1 1 auto; min-width: 0; overflow-x: auto; scrollbar-width: none;
}
.brand-bar-marques::-webkit-scrollbar { display: none; }
.brand-bar-fixe { display: flex; align-items: center; gap: 2px; flex-shrink: 0; }

/* L'onglet d'une marque est en deux morceaux — le nom qui emmène au catalogue, le chevron
   qui déplie les modèles — mais il doit se voir comme un seul : même fond au survol, aucun
   écart entre les deux, le coin arrondi seulement aux extrémités. */
.brand-onglet {
  display: inline-flex; align-items: stretch;
  border-radius: var(--radius-sm);
  transition: background .15s var(--ease);
}
.brand-tab, .brand-link {
  display: inline-flex; align-items: center; gap: 2px;
  padding: 7px 5px; border: 0; background: none; border-radius: var(--radius-sm);
  font-size: .91rem; color: var(--text-soft); cursor: pointer; white-space: nowrap;
  text-decoration: none;
  transition: color .15s var(--ease), background .15s var(--ease);
}
.brand-onglet .brand-tab { padding-right: 2px; border-radius: var(--radius-sm) 0 0 var(--radius-sm); }
.brand-chevron {
  display: inline-flex; align-items: center;
  padding: 7px 6px 7px 2px; border: 0; background: none;
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  color: inherit; cursor: pointer;
}
.brand-chevron > svg { opacity: .5; transition: transform .2s var(--ease); }
.brand-tab > svg { opacity: .5; transition: transform .2s var(--ease); }

/* La pastille de marque : une initiale sur la couleur du constructeur.

   Elle est carrée et non ronde — un carré arrondi lit comme une icône d'application, et c'est
   exactement le registre : « voici la marque ». Le texte est toujours blanc, parce que les
   couleurs retenues sont toutes assez sombres pour le porter ; une marque plus claire se
   signalerait tout de suite à l'œil, et c'est préférable à une règle de contraste automatique
   qui donnerait du gris sur du jaune sans que personne ne le remarque.

   La taille est volontairement petite : la pastille situe, elle ne doit pas concurrencer le
   nom qu'elle accompagne. Un cran de plus et la rangée devient un damier. */
/* Dans le menu téléphone, la même pastille mais d'un cran plus grande : on y lit à bout de
   bras, et la rangée n'y est pas contrainte en largeur comme la barre de l'en-tête. */
.mobile-brand > summary .brand-pastille { width: 18px; height: 18px; font-size: .68rem; margin-right: 8px; }

.brand-pastille {
  width: 14px; height: 14px; flex-shrink: 0;
  display: inline-grid; place-items: center;
  border-radius: 4.5px; background: var(--pastille);
  color: #fff; font-size: .58rem; font-weight: 800; line-height: 1;
  letter-spacing: 0; font-family: var(--font-display);
  box-shadow: var(--voile-pastille, none);
}
/* Le voile de la pastille est un jeton, et non un sélecteur de thème : la nuit le remplit,
   le jour le laisse à `none`. Une règle de plus ici aurait fait une troisième place où le
   mode nuit se décide. */
.brand-link:hover { color: var(--text); background: rgba(124, 92, 255, .12); }
/* Le survol est porté par l'onglet entier : survoler le chevron ne doit pas éteindre le nom
   d'à côté, puisque les deux désignent la même marque. */
.brand-onglet:hover { background: rgba(124, 92, 255, .12); }
.brand-onglet:hover .brand-tab, .brand-onglet:hover .brand-chevron { color: var(--text); }
.brand-onglet.est-ouvert { background: var(--gradient-soft); }
.brand-onglet.est-ouvert .brand-tab,
.brand-onglet.est-ouvert .brand-chevron { color: #fff; }
.brand-onglet.est-ouvert .brand-chevron svg { transform: rotate(180deg); opacity: 1; }
/* Le clavier doit voir les deux moitiés séparément : c'est là que le découpage se justifie. */
.brand-tab:focus-visible, .brand-chevron:focus-visible {
  outline: 2px solid var(--violet-bright); outline-offset: -2px; border-radius: var(--radius-sm);
}

.brand-bar-sep { width: 1px; height: 18px; background: var(--line-strong); margin-inline: 6px; flex-shrink: 0; }



/* ---------------------------------------------------------------- méga-menu */

.megamenu {
  position: absolute; left: 0; right: 0; top: 100%;
  background: var(--menu-bg); border-bottom: 1px solid var(--line-strong);
  box-shadow: 0 34px 60px -26px rgba(0,0,0,.95);
  max-height: min(72vh, 620px); overflow-y: auto;
  animation: mm-in .18s var(--ease);
}
@keyframes mm-in { from { opacity: 0; transform: translateY(-6px); } }
@media (prefers-reduced-motion: reduce) { .megamenu { animation: none; } }

.megamenu-inner { width: min(100% - 40px, var(--wrap)); margin-inline: auto; padding-block: var(--s-5) var(--s-4); }
.megamenu-head { display: flex; align-items: center; gap: var(--s-4); flex-wrap: wrap; margin-bottom: var(--s-4); }
.megamenu-head h2 { font-size: 1.3rem; }
.megamenu-head p { color: var(--muted); font-size: .88rem; flex: 1; min-width: 220px; }

.megamenu-cols { display: grid; grid-template-columns: repeat(auto-fit, minmax(168px, 1fr)); gap: var(--s-4); align-items: start; }
.megamenu-col { min-width: 0; }
.megamenu-col-title {
  display: block; padding: 6px 10px; margin-bottom: 6px; border-radius: var(--radius-sm);
  background: var(--gradient-soft); border: 1px solid var(--line);
  font-family: var(--font-display); font-weight: 600; font-size: .86rem; color: #fff;
}
.megamenu-col-title:hover { border-color: var(--violet); }
.megamenu-col ul { list-style: none; margin: 0; padding: 0; max-height: 288px; overflow-y: auto; }
.megamenu-col ul::-webkit-scrollbar { width: 4px; }
.megamenu-col ul::-webkit-scrollbar-thumb { background: var(--line-strong); border-radius: 2px; }
.megamenu-col li a {
  display: block; padding: 5px 10px; border-radius: 5px;
  font-size: .85rem; color: var(--text-soft);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.megamenu-col li a:hover { background: rgba(124,92,255,.14); color: #fff; }
.megamenu-col li a.more { color: var(--violet-bright); font-size: .8rem; }

.megamenu-foot {
  display: flex; align-items: center; gap: 8px;
  margin-top: var(--s-5); padding-top: var(--s-4); border-top: 1px solid var(--line);
  font-size: .85rem; color: var(--muted);
}
.megamenu-foot svg { color: var(--violet-bright); flex-shrink: 0; }

/* ------------------------------------------------------- champ de recherche *//* ------------------------------------------------------- champ de recherche */

.search-box { position: relative; }
/* Pas de fond ici : il vient de `.input`, donc du thème. Cette règle en portait un, écrit
   en dur du temps où la boutique n'existait qu'en sombre ; plus spécifique, il gagnait — et
   la barre de recherche restait noire au milieu d'un en-tête clair. */
.search-box .input { padding-left: 42px; }
.search-box .search-icon { position: absolute; left: 14px; top: 50%; transform: translateY(-50%); color: var(--muted); pointer-events: none; }

#nav-suggest, #mobile-suggest { position: relative; z-index: 60; }

.suggest {
  position: absolute; top: 8px; left: 0; right: 0; z-index: 60;
  background: var(--pop-bg); border: 1px solid var(--line-strong); border-radius: var(--radius-lg);
  box-shadow: 0 30px 60px -20px rgba(0,0,0,.9);
  overflow: hidden; max-height: 70vh; overflow-y: auto;
}
.suggest-group { padding: 10px 0; }
.suggest-group + .suggest-group { border-top: 1px solid var(--line); }
.suggest-label { font-family: var(--font-mono); font-size: .66rem; letter-spacing: .14em; text-transform: uppercase; color: var(--muted-dim); padding: 4px 16px 8px; }
.suggest-item {
  display: flex; align-items: center; gap: 12px; padding: 9px 16px; cursor: pointer; width: 100%;
  background: none; border: 0; text-align: left;
}
.suggest-item:hover, .suggest-item.is-cursor { background: rgba(124, 92, 255, .12); }
.suggest-item img { width: 34px; height: 34px; object-fit: contain; border-radius: 6px; background: var(--media-bg); flex-shrink: 0; }
.suggest-item .name { font-size: .88rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.suggest-item .meta { font-family: var(--font-mono); font-size: .72rem; color: var(--muted); margin-left: auto; flex-shrink: 0; }

/* ========================================================== BANDEAU CONFIANCE */


/* ============================================================= MARQUES */

.brand-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: var(--s-3); }

.brand-card {
  position: relative; overflow: hidden;
  min-height: 104px; padding: var(--s-4);
  display: flex; flex-direction: column; justify-content: center; align-items: center; gap: 6px;
  border: 1px solid var(--line); border-radius: var(--radius-lg);
  background: linear-gradient(170deg, rgba(29,28,60,.5), rgba(12,11,26,.7));
  text-align: center;
  transition: transform .2s var(--ease), border-color .2s var(--ease);
}
.brand-card::after {
  content: ''; position: absolute; inset: 0; opacity: 0;
  background: radial-gradient(300px circle at var(--mx, 50%) var(--my, 50%), rgba(124,92,255,.2), transparent 60%);
  transition: opacity .3s var(--ease); pointer-events: none;
}
.brand-card:hover { transform: translateY(-3px); border-color: var(--violet); }
.brand-card:hover::after { opacity: 1; }
.brand-card .b-name { font-family: var(--font-display); font-size: 1.12rem; font-weight: 600; letter-spacing: -.02em; }
.brand-card .b-count { font-family: var(--font-mono); font-size: .72rem; color: var(--muted); }

/* ================================================================ PANNES */

.panne-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--s-3); }

.panne-card {
  display: flex; align-items: center; gap: var(--s-3);
  padding: var(--s-4); border: 1px solid var(--line); border-radius: var(--radius-lg);
  background: linear-gradient(170deg, rgba(29,28,60,.42), rgba(12,11,26,.7));
  transition: transform .18s var(--ease), border-color .18s var(--ease);
}
.panne-card:hover { transform: translateY(-2px); border-color: var(--violet); }
.panne-card .p-icon {
  width: 38px; height: 38px; flex-shrink: 0; display: grid; place-items: center; border-radius: 10px;
  background: var(--gradient-soft); border: 1px solid var(--line); color: var(--violet-bright);
}
.panne-card h3 { font-size: .96rem; font-weight: 600; letter-spacing: -.01em; }
.panne-card p { font-size: .8rem; color: var(--muted); margin-top: 3px; }
.panne-card .p-arrow { margin-left: auto; color: var(--muted-dim); flex-shrink: 0; }
.panne-card:hover .p-arrow { color: var(--violet-bright); }

/* =========================================================== CARTE PRODUIT */

.product-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(232px, 1fr)); gap: var(--s-4); }

.product-card {
  position: relative; display: flex; flex-direction: column;
  border: 1px solid var(--line); border-radius: var(--radius-lg);
  background: linear-gradient(180deg, rgba(29,28,60,.42), rgba(14,13,30,.75));
  overflow: hidden;
  transition: transform .2s var(--ease), border-color .2s var(--ease), box-shadow .2s var(--ease);
}
/* Le survol : la carte se soulève, et c'est tout.

   Elle portait avant trois ombres superposées, et chacune posait un problème.

   La première était un anneau de 1 px à 35 % d'opacité, posé juste à l'extérieur d'une
   bordure violette déjà pleine : deux traits de violets différents collés l'un à l'autre,
   que l'œil lit comme un détourage raté — surtout sur la charte claire, où le demi-ton
   devient une frange sale au lieu de se fondre dans le sombre.

   La seconde était un halo omnidirectionnel (`0 0 34px`), qui débordait autant au-dessus de
   la carte qu'en dessous. Or les cartes vivent dans un rail à défilement horizontal, et un
   conteneur qui défile en X **doit** masquer en Y : le halo était donc tranché net en haut
   et en bas, laissant deux arêtes droites autour d'un objet à coins arrondis. L'effet le
   plus visible du survol était ce rectangle.

   Il reste donc une ombre portée, dirigée vers le bas comme une vraie ombre, et assez
   resserrée pour tenir dans la place que le rail lui ménage. Les teintes viennent des
   variables : chaque charte garde son caractère sans qu'on écrive une couleur en dur. */
/* Les deux sélecteurs disent la même chose, et le second n'est là que pour peser plus lourd.

   Les cartes du catalogue entrent en scène avec `.reveal`, et `.reveal.is-in` remet leur
   transformation à zéro une fois l'entrée finie. Cette règle vit plus bas dans la feuille et
   a le même poids que celle-ci : elle gagnait, et le soulèvement au survol ne se produisait
   jamais en grille — seule la bordure s'allumait, ce qui donne une carte qui réagit à moitié.
   Dans les rails, où les cartes ne portent pas `.reveal`, il fonctionnait. La même page avait
   donc deux comportements au survol selon l'endroit. */
.product-card:hover,
.product-card.reveal.is-in:hover {
  transform: translateY(-7px);
  border-color: var(--violet);
  box-shadow: 0 12px 26px -14px color-mix(in srgb, var(--violet-deep) 38%, transparent),
              0 3px 10px -6px color-mix(in srgb, var(--violet) 24%, transparent);
}
.product-media {
  aspect-ratio: 4 / 3; background: var(--media-bg); position: relative; overflow: hidden;
  display: grid; place-items: center; padding: 10px;
}
.product-media img { width: 100%; height: 100%; object-fit: contain; transition: transform .45s var(--ease); }
.product-card:hover .product-media img { transform: scale(1.1); }
.product-media .ph { color: #6F6A94; display: grid; place-items: center; height: 100%; }
.product-body { padding: 14px 15px 16px; display: flex; flex-direction: column; gap: 8px; flex: 1; }
.product-body .fam { font-family: var(--font-mono); font-size: .66rem; letter-spacing: .1em; text-transform: uppercase; color: var(--violet-bright); }
.product-body .name {
  font-size: .93rem; font-weight: 500; line-height: 1.38; color: var(--text);
  display: -webkit-box; -webkit-line-clamp: 2; line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.product-foot { margin-top: auto; display: flex; align-items: flex-end; justify-content: space-between; gap: 10px; padding-top: 4px; }
.product-foot .price { font-size: 1.24rem; }
.add-btn {
  width: 38px; height: 38px; border-radius: 10px; display: grid; place-items: center;
  border: 1px solid var(--line-strong); background: rgba(124,92,255,.12); color: var(--violet-bright);
  cursor: pointer; transition: all .16s var(--ease); flex-shrink: 0;
}
.add-btn:hover { background: var(--gradient); color: var(--on-gradient); border-color: transparent; }
.add-btn.is-done { background: var(--success); border-color: transparent; color: #05221a; }

.product-card .stretched { position: absolute; inset: 0; z-index: 1; }
.product-card .add-btn { position: relative; z-index: 2; }

/* ================================================================ ÉDITORIAL */

.split { display: grid; grid-template-columns: minmax(0, .9fr) minmax(0, 1.1fr); gap: clamp(32px, 5vw, 72px); align-items: center; }

.steps { display: flex; flex-direction: column; }
.step { display: grid; grid-template-columns: 58px 1fr; gap: var(--s-4); padding-block: var(--s-5); border-top: 1px solid var(--line); }
.step:last-child { border-bottom: 1px solid var(--line); }
.step .num { font-family: var(--font-mono); font-size: .8rem; color: var(--violet-bright); padding-top: 3px; }
.step h4 { font-family: var(--font-display); font-size: 1.05rem; font-weight: 600; margin: 0 0 5px; }
.step p { color: var(--muted); font-size: .92rem; }

/* ====================================================================== FAQ */

.faq { border-top: 1px solid var(--line); }
.faq-item { border-bottom: 1px solid var(--line); }
.faq-q {
  width: 100%; background: none; border: 0; cursor: pointer;
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-4);
  padding: var(--s-5) 0; text-align: left;
  font-family: var(--font-display); font-size: 1.04rem; font-weight: 500;
}
.faq-q:hover { color: var(--violet-bright); }
.faq-q .sign { width: 24px; height: 24px; flex-shrink: 0; position: relative; }
.faq-q .sign::before, .faq-q .sign::after {
  content: ''; position: absolute; background: var(--violet-bright); transition: transform .24s var(--ease);
  left: 50%; top: 50%;
}
.faq-q .sign::before { width: 13px; height: 1.5px; transform: translate(-50%, -50%); }
.faq-q .sign::after  { width: 1.5px; height: 13px; transform: translate(-50%, -50%); }
.faq-item.is-open .faq-q .sign::after { transform: translate(-50%, -50%) scaleY(0); }
.faq-a { max-height: 0; overflow: hidden; transition: max-height .3s var(--ease); }
.faq-item.is-open .faq-a { max-height: 340px; }
.faq-a p { color: var(--muted); padding-bottom: var(--s-5); max-width: 74ch; }

/* ================================================================= CATALOGUE */

.catalogue { display: grid; grid-template-columns: 260px minmax(0, 1fr); gap: var(--s-6); align-items: start; padding-block: var(--s-6) var(--s-9); }

.filters { position: sticky; top: calc(var(--header-h) + 16px); display: grid; gap: var(--s-5); }
.filter-group h4 { font-family: var(--font-mono); font-size: .7rem; letter-spacing: .14em; text-transform: uppercase; color: var(--muted-dim); margin-bottom: var(--s-3); font-weight: 500; }
.filter-list { display: grid; gap: 1px; max-height: 260px; overflow-y: auto; margin: 0; padding: 0; list-style: none; }
.filter-list::-webkit-scrollbar { width: 5px; }
.filter-list::-webkit-scrollbar-thumb { background: var(--line-strong); border-radius: 3px; }
.filter-opt {
  display: flex; align-items: center; justify-content: space-between; gap: 8px; width: 100%;
  padding: 7px 10px; border-radius: var(--radius-sm); border: 0; background: none;
  font-size: .87rem; color: var(--text-soft); cursor: pointer; text-align: left;
  transition: background .14s var(--ease), color .14s var(--ease);
}
.filter-opt:hover { background: rgba(124,92,255,.1); color: var(--text); }
.filter-opt.is-active { background: var(--gradient-soft); color: #fff; }
.filter-opt .n { font-family: var(--font-mono); font-size: .7rem; color: var(--muted-dim); }
.filter-opt.is-active .n { color: var(--violet-bright); }

.price-range { display: flex; align-items: center; gap: 8px; }
.price-range .input { padding: 9px 11px; font-family: var(--font-mono); font-size: .84rem; }

.catalogue-head { display: flex; align-items: center; justify-content: space-between; gap: var(--s-4); flex-wrap: wrap; margin-bottom: var(--s-5); }
.catalogue-head { margin-top: var(--s-5); }
.catalogue-titre { font-size: clamp(1.6rem, 3vw, 2.2rem); }
/* Le tri est poussé à droite par la marge automatique : le bouton de filtres et le
   compteur restent groupés à gauche sans conteneur intermédiaire, ce qui permet à la
   version téléphone de les réagencer librement en grille. */
.catalogue-tri { display: flex; align-items: center; gap: 8px; margin-left: auto; }
.catalogue-tri .muted { font-size: .85rem; }
.catalogue-tri .select { width: auto; }
.catalogue-head .count { font-family: var(--font-mono); font-size: .8rem; color: var(--muted); }
.catalogue-head .count b { color: var(--text); font-weight: 500; }

.active-filters { display: flex; flex-wrap: wrap; gap: 7px; margin-bottom: var(--s-4); }
.active-filters .chip { background: var(--gradient-soft); border-color: var(--violet); color: #fff; }
.active-filters .chip .x { opacity: .7; }

/* Tout en un bloc, au centre — et les chiffres au centre exact de ce bloc.

   Deux essais pour y arriver. Centrer la rangée entière ne centre pas les chiffres : « ‹
   Précédent » mesure 115 px et « Suivant › » 99 px, le groupe tombait huit pixels à droite.
   Épingler les deux commandes aux bords réglait le centrage mais les envoyait à cinq cents
   pixels des chiffres, chacune dans son coin de page — on ne lisait plus une pagination mais
   trois éléments sans rapport.

   La réponse tient dans une largeur : les deux commandes occupent la même, le plus large des
   deux libellés. Les chiffres se retrouvent alors au milieu par construction, et l'ensemble
   reste un seul bloc qu'on embrasse du regard. */
.pagination {
  display: flex; justify-content: center; align-items: center;
  gap: 7px; margin-top: var(--s-7); flex-wrap: wrap;
}
.pagination-prec, .pagination-suiv {
  flex: 0 0 auto; min-width: 124px; display: flex; align-items: center;
}
.pagination-prec { justify-content: flex-end; }
.pagination-suiv { justify-content: flex-start; }
.pagination-pages {
  display: flex; align-items: center; justify-content: center; gap: 7px; flex-wrap: wrap;
}
.page-btn.est-ellipse { border: 0; background: none; }

/* Sur un téléphone, les trois zones ne tiennent pas sur une ligne : les chiffres passent
   au-dessus, et les deux commandes gardent les bords en dessous — là où le pouce les
   attrape. */
@media (max-width: 560px) {
  /* Les trois zones ne tiennent pas sur une ligne : les chiffres passent au-dessus, et les
     deux commandes gardent les bords en dessous — là où le pouce les attrape. */
  .pagination { row-gap: 12px; }
  .pagination-pages { order: -1; flex-basis: 100%; }
  .pagination-prec, .pagination-suiv { flex: 1 1 0; min-width: 0; }
  .pagination-prec { justify-content: flex-start; }
  .pagination-suiv { justify-content: flex-end; }
}
.page-btn {
  min-width: 40px; height: 40px; padding: 0 12px; border-radius: var(--radius-sm);
  border: 1px solid var(--line); background: rgba(22,21,46,.5); cursor: pointer;
  font-family: var(--font-mono); font-size: .84rem; transition: all .15s var(--ease);
}
.page-btn:hover:not(:disabled) { border-color: var(--violet); }
.page-btn.is-current { background: var(--gradient); border-color: transparent; color: var(--on-gradient); }
.page-btn:disabled { opacity: .35; cursor: not-allowed; }

.filters-toggle { display: none; }

/* ============================================================ FICHE PRODUIT */

.product-page { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, .92fr); gap: clamp(28px, 4vw, 64px); padding-block: var(--s-6) var(--s-8); align-items: start; }
.product-visual {
  background: var(--media-bg); border-radius: var(--radius-xl); padding: clamp(20px, 4vw, 52px);
  display: grid; place-items: center; aspect-ratio: 1/1; border: 1px solid var(--line);
  position: sticky; top: calc(var(--header-h) + 20px);
}
.product-visual img { width: 100%; height: 100%; object-fit: contain; }

.breadcrumb { display: flex; align-items: center; gap: 8px; font-family: var(--font-mono); font-size: .74rem; color: var(--muted-dim); padding-top: var(--s-5); flex-wrap: wrap; }
.breadcrumb a:hover { color: var(--violet-bright); }

.product-detail h1 { font-size: clamp(1.5rem, 2.6vw, 2.1rem); margin-block: var(--s-3) var(--s-4); line-height: 1.2; }
.buy-box { border: 1px solid var(--line-strong); border-radius: var(--radius-lg); padding: var(--s-5); background: rgba(16,16,36,.6); margin-block: var(--s-5); }
.buy-row { display: flex; align-items: flex-end; justify-content: space-between; gap: var(--s-4); flex-wrap: wrap; }
.qty { display: flex; align-items: center; border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; }
.qty button { width: 40px; height: 44px; background: rgba(22,21,46,.6); border: 0; cursor: pointer; color: var(--text-soft); font-size: 1.1rem; }
.qty button:hover { background: rgba(124,92,255,.16); color: #fff; }
.qty input { width: 52px; height: 44px; text-align: center; background: transparent; border: 0; font-family: var(--font-mono); }
.qty input::-webkit-outer-spin-button, .qty input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }

.spec-table { width: 100%; border-collapse: collapse; font-size: .89rem; }
.spec-table th, .spec-table td { text-align: left; padding: 11px 0; border-bottom: 1px solid var(--line); vertical-align: top; }
.spec-table th { color: var(--muted); font-weight: 400; width: 42%; }
.spec-table td { font-family: var(--font-mono); font-size: .84rem; }

.assurance { display: grid; gap: var(--s-3); margin-top: var(--s-5); }
.assurance li { list-style: none; display: flex; gap: 10px; align-items: flex-start; font-size: .89rem; color: var(--text-soft); }
.assurance svg { color: var(--success); flex-shrink: 0; margin-top: 3px; }
.assurance ul { margin: 0; padding: 0; }

/* ==================================================================== PANIER */

.cart-layout { display: grid; grid-template-columns: minmax(0, 1fr) 360px; gap: var(--s-6); align-items: start; padding-block: var(--s-6) var(--s-9); }
.cart-line { display: grid; grid-template-columns: 84px minmax(0, 1fr) auto; gap: var(--s-4); padding: var(--s-4) 4px var(--s-4) 0; border-bottom: 1px solid var(--line); align-items: center; }
.cart-line .thumb { width: 84px; height: 84px; background: var(--media-bg); border-radius: var(--radius); display: grid; place-items: center; padding: 7px; }
.cart-line .thumb img { max-height: 100%; object-fit: contain; }
.cart-line .name { font-size: .94rem; font-weight: 500; }
.cart-line .rm { background: none; border: 0; color: var(--muted-dim); cursor: pointer; font-size: .8rem; padding: 4px 0; }
.cart-line .rm:hover { color: var(--danger); }

.summary { position: sticky; top: calc(var(--header-h) + 16px); padding: var(--s-5); }
.summary-row { display: flex; justify-content: space-between; padding-block: 9px; font-size: .92rem; }
.summary-row.total { border-top: 1px solid var(--line); margin-top: var(--s-2); padding-top: var(--s-4); font-size: 1.06rem; font-weight: 600; }
.ship-bar { height: 5px; border-radius: 3px; background: var(--surface-3); overflow: hidden; margin-top: 8px; }
.ship-bar i { display: block; height: 100%; background: var(--gradient); transition: width .4s var(--ease); }

.form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-4); }
.form-grid .full { grid-column: 1 / -1; }

/* =================================================================== DIVERS */

.empty { text-align: center; padding: var(--s-9) var(--s-4); }
.empty h3 { margin-bottom: var(--s-3); }
.empty p { color: var(--muted); max-width: 46ch; margin-inline: auto; margin-bottom: var(--s-5); }

.skeleton { background: linear-gradient(90deg, var(--surface) 25%, var(--surface-2) 50%, var(--surface) 75%); background-size: 200% 100%; animation: shimmer 1.3s linear infinite; border-radius: var(--radius); }
@keyframes shimmer { to { background-position: -200% 0; } }
.skel-card { height: 292px; }

.toast-zone { position: fixed; bottom: 24px; left: 50%; transform: translateX(-50%); z-index: 90; display: grid; gap: 8px; width: min(92vw, 420px); }
.toast {
  display: flex; align-items: center; gap: 11px;
  background: var(--pop-bg); border: 1px solid var(--line-strong); border-radius: var(--radius);
  padding: 13px 16px; box-shadow: 0 24px 50px -20px rgba(0,0,0,.9);
  animation: toast-in .3s var(--ease);
}
.toast svg { color: var(--success); flex-shrink: 0; }
.toast.err svg { color: var(--danger); }
@keyframes toast-in { from { opacity: 0; transform: translateY(14px); } }


/* =================================================================== PIED DE PAGE */

.footer { border-top: 1px solid var(--line); background: linear-gradient(180deg, transparent, rgba(124,92,255,.05)); margin-top: var(--s-8); }
.footer-grid { display: grid; grid-template-columns: 1.6fr repeat(3, 1fr); gap: var(--s-6); padding-block: var(--s-8) var(--s-6); }
.footer h5 { font-family: var(--font-mono); font-size: .7rem; letter-spacing: .14em; text-transform: uppercase; color: var(--muted-dim); margin: 0 0 var(--s-4); font-weight: 500; }
.footer ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; }
.footer a { color: var(--text-soft); font-size: .9rem; }
.footer a:hover { color: var(--violet-bright); }
.footer-base { border-top: 1px solid var(--line); padding-block: var(--s-5); display: flex; justify-content: space-between; gap: var(--s-4); flex-wrap: wrap; font-size: .82rem; color: var(--muted-dim); }

/* ============================================================== RESPONSIVE */

@media (max-width: 1080px) {
  .brand-grid { grid-template-columns: repeat(4, 1fr); }
  .panne-grid { grid-template-columns: repeat(2, 1fr); }
  .catalogue { grid-template-columns: 1fr; }
  .filters { position: static; display: none; padding: var(--s-5); border: 1px solid var(--line); border-radius: var(--radius-lg); background: var(--surface); }
  .filters.is-open { display: grid; }
  .filters-toggle { display: inline-flex; }
  .product-page { grid-template-columns: 1fr; }
  /* `relative` et non `static` : l'image de la fiche est positionnée en absolu à
     l'intérieur de ce cadre. En le rendant non positionné, elle se calait sur la page
     entière et recouvrait le bouton « Ajouter au panier ». */
  .product-visual { position: relative; aspect-ratio: 4/3; }
  .cart-layout { grid-template-columns: 1fr; }
  .summary { position: static; }
  .split { grid-template-columns: 1fr; }
}

@media (max-width: 860px) {
  /* Sur mobile la barre de marques passe dans le menu : l'en-tête garde la recherche,
     qui reste le chemin le plus court vers un modèle. */
  .brand-bar { display: none; }
  .burger { display: grid; }
  /* La recherche prend toute la largeur sur une seconde ligne ; la hauteur réelle de
     l'en-tête est mesurée en JavaScript et publiée dans --header-h. */
  .header-top { height: auto; gap: var(--s-2); flex-wrap: wrap; padding-block: 10px; }
  .logo-word { font-size: 1.05rem; }
  .header-search { order: 3; flex-basis: 100%; max-width: none; }
  /* 44 px et non 40 : depuis que l'en-tête mobile ne garde que le menu et le panier, ces
     deux boutons portent toute la navigation. Les rétrécir était une économie de place
     qui n'a plus lieu d'être. */
  .icon-btn { width: 44px; height: 44px; }
  .footer-grid { grid-template-columns: 1fr 1fr; }
}

@media (max-width: 620px) {
  body { font-size: 14.5px; }
  .brand-grid { grid-template-columns: repeat(3, 1fr); }
  .panne-grid { grid-template-columns: 1fr; }
  .product-grid { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: var(--s-3); }
  .product-body { padding: 11px 12px 13px; }
  .product-foot .price { font-size: 1.08rem; }
  .form-grid { grid-template-columns: 1fr; }
  .cart-line { grid-template-columns: 62px minmax(0, 1fr); row-gap: var(--s-3); }
  .cart-line .thumb { width: 62px; height: 62px; }
  .cart-line > :last-child { grid-column: 1 / -1; display: flex; justify-content: space-between; align-items: center; }
  .footer-grid { grid-template-columns: 1fr; gap: var(--s-5); }
  .section { padding-block: clamp(44px, 9vw, 72px); }
}

/* ------------------------------------------------------------- menu mobile */

.mobile-menu {
  /* Le bord inférieur réel de l'en-tête, bandeau d'annonce compris — et non la hauteur de
     l'en-tête seul, qui faisait commencer le panneau 36 px trop haut. */
  position: fixed; inset: var(--entete-bas, var(--header-h)) 0 0;
  background: var(--ink-950); z-index: 45; padding: var(--s-5);
  display: none; overflow-y: auto;
}
.mobile-menu.is-open { display: block; }
.mobile-menu > a { display: block; padding: 13px 0; border-bottom: 1px solid var(--line); font-size: 1rem; }

.mobile-section-title {
  font-family: var(--font-mono); font-size: .68rem; letter-spacing: .14em; text-transform: uppercase;
  color: var(--muted-dim); margin: var(--s-5) 0 var(--s-2);
}

.mobile-brand { border-bottom: 1px solid var(--line); }
/* `gap` et non l'espace du balisage : dans un conteneur flex, les blancs entre deux enfants
   sont supprimés. Le nom de marque et son compteur sont deux éléments voisins, et
   « Apple 4 422 » s'affichait donc « Apple4 422 ». C'est la règle du flex, pas un oubli de
   balisage — l'espace est bien dans le gabarit.

   `justify-content` passe de `space-between` à `flex-start` : le chevron se place déjà seul
   à droite par sa marge automatique, et `space-between` poussait le compteur au milieu du
   vide au lieu de le laisser contre son nom. */
.mobile-brand > summary {
  display: flex; align-items: baseline; justify-content: flex-start; gap: 8px;
  padding: 14px 2px; cursor: pointer; list-style: none;
  font-family: var(--font-display); font-weight: 600; font-size: 1.02rem;
}
.mobile-brand > summary::-webkit-details-marker { display: none; }
.mobile-brand > summary::after {
  content: ''; width: 8px; height: 8px; margin-left: auto; margin-right: 6px;
  border-right: 1.6px solid var(--violet-bright); border-bottom: 1.6px solid var(--violet-bright);
  transform: rotate(45deg); transition: transform .2s var(--ease);
}
.mobile-brand[open] > summary::after { transform: rotate(-135deg); }
.mobile-brand > summary .n { font-family: var(--font-mono); font-size: .72rem; color: var(--muted); font-weight: 400; }

.mobile-serie { padding: 4px 0 12px; }
.mobile-serie-title {
  display: inline-block; padding: 5px 10px; margin-bottom: 6px; border-radius: var(--radius-sm);
  background: var(--gradient-soft); border: 1px solid var(--line);
  font-size: .8rem; color: #fff;
}
.mobile-model { display: block; padding: 9px 10px; font-size: .92rem; color: var(--text-soft); border-radius: 6px; }
.mobile-model:active, .mobile-model:hover { background: rgba(124,92,255,.12); color: #fff; }
.mobile-model.all { color: var(--violet-bright); font-size: .86rem; padding-left: 10px; }

.mobile-search { display: block; }
#mobile-suggest .suggest { position: static; margin-top: -8px; margin-bottom: 8px; }

/* ============================================================== VISIONNEUSE */

.product-visual { border: 1px solid var(--line); cursor: zoom-in; padding: clamp(20px, 4vw, 52px); }
.product-visual .visual-zoom {
  position: absolute; bottom: 14px; right: 14px;
  display: inline-flex; align-items: center; gap: 6px;
  padding: 7px 12px; border-radius: 999px;
  background: rgba(10, 9, 22, .78); color: #fff;
  font-size: .78rem; backdrop-filter: blur(6px);
  opacity: 0; transform: translateY(4px);
  transition: opacity .2s var(--ease), transform .2s var(--ease);
}
.product-visual:hover .visual-zoom, .product-visual:focus-visible .visual-zoom { opacity: 1; transform: none; }

.lightbox {
  position: fixed; inset: 0; z-index: 100;
  background: var(--veil-bg);
  backdrop-filter: blur(10px);
  display: none;
  /* `1fr` vaut `minmax(auto, 1fr)` : la rangee se laissait etirer par l'image en 2080 px,
     si bien que le `max-height: 100%` de celle-ci ne butait sur rien. `minmax(0, 1fr)`
     borne la rangee a la hauteur de l'ecran et remet l'image a l'echelle. */
  grid-template-rows: minmax(0, 1fr) auto;
}
.lightbox.is-open { display: grid; }

.lb-close {
  position: absolute; top: 18px; right: 18px; z-index: 2;
  width: 44px; height: 44px; display: grid; place-items: center;
  border: 1px solid var(--line-strong); border-radius: 50%;
  background: var(--lb-close-fond); color: var(--text); cursor: pointer;
  transition: border-color .16s var(--ease), transform .16s var(--ease);
}
.lb-close:hover { border-color: var(--violet); transform: rotate(90deg); }

.lb-stage {
  position: relative; overflow: hidden; min-height: 0;
  display: grid; place-items: center; padding: 56px 24px 12px;
}
.lb-img {
  max-width: 100%; max-height: 100%; min-width: 0; min-height: 0;
  background: var(--media-bg); border-radius: var(--radius);
  transition: transform .18s var(--ease), filter .3s var(--ease);
  cursor: zoom-in; touch-action: none; user-select: none;
}
.lb-img.is-low { filter: blur(6px); }
.lightbox.is-zoomed .lb-img { cursor: grab; transition: none; }

.lb-spinner {
  position: absolute; bottom: 22px; left: 50%; transform: translateX(-50%);
  width: 22px; height: 22px; border-radius: 50%;
  border: 2px solid var(--line-strong); border-top-color: var(--violet-bright);
  animation: lb-spin .8s linear infinite;
}
@keyframes lb-spin { to { transform: translateX(-50%) rotate(360deg); } }

.lb-bar {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-4);
  padding: 14px clamp(16px, 4vw, 32px) 22px;
  width: min(100% , var(--wrap)); margin-inline: auto; flex-wrap: wrap;
}
.lb-caption { font-size: .9rem; color: var(--text-soft); }
.lb-zoom { display: flex; align-items: center; gap: 4px; border: 1px solid var(--line); border-radius: 999px; padding: 4px; }
.lb-zoom button {
  width: 32px; height: 32px; border: 0; border-radius: 50%;
  background: none; color: var(--text-soft); cursor: pointer; font-size: 1.15rem; line-height: 1;
}
.lb-zoom button:hover { background: rgba(124, 92, 255, .18); color: #fff; }
.lb-level { font-family: var(--font-mono); font-size: .76rem; color: var(--muted); min-width: 52px; text-align: center; }

@media (prefers-reduced-motion: reduce) {
  .lb-img, .lb-close, .visual-zoom { transition: none; }
  .lb-spinner { animation: none; }
}

/* ================================================================= BANDEAU D'ACCUEIL

   Un carrousel, comme avant — mais plus les trois pastels quasi identiques d'origine.
   Trois tonalités qui se répondent : une nuit violette, un clair presque blanc, un flux
   violet-bleu. Les vues portent leurs propres couleurs, indépendantes du thème du site :
   ce sont des visuels de marque, identiques dans les trois directions artistiques.

   Le contraste se mesure sur le **fond composé sous chaque texte**, pas sur la couleur
   médiane du dégradé : le sur-titre est rendu en haut à gauche, donc sur l'arrêt 0 % d'un
   dégradé à 140deg, et le halo radial éclaircit encore la zone. Mesurer au milieu donnait
   5,15:1 pour une valeur réelle de 3,82:1.

   Valeurs réelles, balayées pixel par pixel sur la boîte de texte, halo compris :
   petit texte au plus bas à 5,20:1 (`--b-eyebrow` de la nuit, seuil 4,5) ; `--b-accent`
   descend à 4,29:1 mais ne sert qu'au mot accentué du titre (27 px gras) et aux icônes des
   atouts, dont le seuil est 3:1. Aucune encre sous son seuil.
   ------------------------------------------------------------------------------- */

.bandeau {
  position: relative; overflow: hidden;
  border-bottom: 1px solid var(--line);
  isolation: isolate;
}

/* En mode image, la bannière rentre dans la largeur de page au lieu de filer d'un bord à
   l'autre de la fenêtre.

   Pleine fenêtre, elle était le seul bloc de la page à ne pas s'aligner sur la grille : le
   logo Wink Phone peint dans le visuel se retrouvait collé au bord de l'écran, à quelques
   centimètres sous le vrai logo du site, et le titre partait sous le cadre. Cadrée, elle
   devient une pièce de la page comme les tuiles d'offres qui la suivent — et elle n'est
   plus agrandie de 1 440 à 1 920 px selon l'écran, ce qui lui coûtait sa netteté. */
.bandeau:has(.bandeau-vue.est-image) {
  width: min(100% - 40px, var(--wrap));
  margin: clamp(14px, 1.6vw, 22px) auto 0;
  border: 1px solid var(--line);
  border-radius: var(--radius-xl);
}
/* La gouttière est celle de `.wrap`, à la règle près, y compris sa rupture à 640 px : trois
   systèmes de marge cohabitaient sous cette largeur, et le cadre de la bannière tombait 4 px
   avant les tuiles d'offres posées juste en dessous. */
@media (max-width: 640px) {
  .bandeau:has(.bandeau-vue.est-image) {
    width: calc(100% - 32px);
    border-radius: var(--radius-lg);
  }
}
.bandeau:focus-visible { outline: 2px solid var(--violet-bright); outline-offset: -4px; }

.bandeau-piste { display: flex; transition: transform .55s var(--ease); }

.bandeau-vue {
  flex: 0 0 100%;
  position: relative;
  display: grid; grid-template-columns: minmax(0, 1.06fr) minmax(0, .94fr);
  gap: clamp(16px, 3vw, 40px); align-items: center;
  min-height: clamp(250px, 27vw, 330px);
  padding-block: clamp(24px, 2.8vw, 38px);
  /* 76 px au minimum : la flèche gauche occupe jusqu'à 62 px du bord, et un simple
     alignement sur la largeur de page la faisait passer PAR-DESSUS le texte de 861 px à
     1400 px — donc sur les deux largeurs d'écran portable les plus répandues, 1280 et
     1366. Le `backdrop-filter` de la flèche rendait le texte illisible, pas seulement
     teinté. Le défaut ne se voyait pas à 1440, la largeur de la maquette. */
  padding-inline: max(76px, calc((100% - var(--wrap)) / 2));

  /* Un fond de repli sur la vue elle-même : sans lui, une tonalité inconnue — une valeur
     restée d'une ancienne version, une faute de frappe écrite par l'API — donnait une vue
     sans fond du tout, avec des encres claires. Sur la charte Studio, qui est fond clair,
     c'était du blanc sur blanc. */
  background:
    radial-gradient(120% 90% at 82% 18%, rgba(139, 123, 255, .34), transparent 62%),
    linear-gradient(145deg, #1B1044 0%, #120B2E 52%, #0C0722 100%);
  --b-titre: #FFFFFF;
  --b-doux: #C5BCEC;
  --b-eyebrow: #A78BFA;
  --b-accent: #8B7BFF;
  --b-cta-fond: #FFFFFF;
  --b-cta-encre: #120B2E;
}

/* ---- les trois tonalités ----
   Les anciens noms (lilas, bleu, noir) restent reconnus : les bandeaux déjà enregistrés
   continuent de s'afficher sans migration. */
.bandeau-vue[data-ton="nuit"],
.bandeau-vue[data-ton="noir"] { /* valeurs par défaut ci-dessus */ }

.bandeau-vue[data-ton="clair"],
.bandeau-vue[data-ton="lilas"] {
  background: linear-gradient(140deg, #FFFFFF 0%, #F2F1F7 58%, #EAE8F4 100%);
  --b-titre: #14142B;
  --b-doux: #4A4A63;
  --b-eyebrow: #5B2FE0;
  --b-accent: #5B2FE0;
  --b-cta-fond: #14142B;
  --b-cta-encre: #FFFFFF;
}
.bandeau-vue[data-ton="flux"],
.bandeau-vue[data-ton="bleu"] {
  background:
    radial-gradient(110% 120% at 88% 12%, rgba(159, 232, 255, .30), transparent 58%),
    linear-gradient(140deg, #5B2FE0 0%, #3A1FC4 56%, #1D3FD0 100%);
  --b-titre: #FFFFFF;
  --b-doux: #DCD9FF;
  /* #BFB4FF tombait à 3,82:1 sur l'arrêt 0 %, là où le sur-titre est rendu. */
  --b-eyebrow: #E6E1FF;
  --b-accent: #9FE8FF;
  --b-cta-fond: #9FE8FF;
  --b-cta-encre: #1B0B5E;
}

/* ---- le texte ---- */
.bandeau-texte { position: relative; z-index: 1; }

.bandeau-eyebrow {
  display: block;
  font-family: var(--font-mono); font-size: .68rem; letter-spacing: .18em;
  text-transform: uppercase; color: var(--b-eyebrow); margin-bottom: 14px;
}
.bandeau-vue h2 {
  color: var(--b-titre); margin: 0;
  font-size: clamp(1.7rem, 3.1vw, 2.9rem);
  letter-spacing: -.038em; line-height: 1.06; font-weight: 700;
}
.bandeau-vue h2 em { font-style: normal; color: var(--b-accent); }
.bandeau-vue p {
  color: var(--b-doux); margin: 14px 0 0;
  font-size: clamp(.88rem, 1vw, 1rem); line-height: 1.55; max-width: 46ch;
}

.bandeau-cta {
  display: inline-flex; align-items: center; gap: 9px;
  margin-top: clamp(18px, 2vw, 26px);
  background: var(--b-cta-fond); color: var(--b-cta-encre);
  font-weight: 600; letter-spacing: -.01em;
  border-radius: 999px; padding: 12px 22px; font-size: .94rem;
  transition: transform .18s var(--ease), box-shadow .18s var(--ease);
}
.bandeau-cta:hover { transform: translateY(-1px); box-shadow: 0 14px 28px -14px rgba(0, 0, 0, .6); }
.bandeau-cta:active { transform: scale(.975); }

.bandeau-atouts {
  display: flex; gap: clamp(14px, 2vw, 30px); list-style: none;
  margin: clamp(18px, 2vw, 26px) 0 0; padding: 0; flex-wrap: wrap;
}
.bandeau-atouts li { display: flex; align-items: center; gap: 9px; }
.bandeau-atouts svg { color: var(--b-accent); flex-shrink: 0; }
.bandeau-atouts span { display: flex; flex-direction: column; font-size: .78rem; line-height: 1.3; color: var(--b-doux); }
.bandeau-atouts b { color: var(--b-titre); font-weight: 600; }

/* ---- le visuel produit ---- */
.bandeau-visuel { position: relative; display: grid; place-items: center; height: 100%; }
.bandeau-visuel img {
  position: relative; z-index: 1;
  max-width: 100%; max-height: clamp(200px, 22vw, 270px);
  object-fit: contain;
  filter: drop-shadow(0 22px 36px rgba(0, 0, 0, .42));
}

/* Une source de lumière derrière le produit, sur les vues sombres.

   Les visuels du catalogue sont des détourages d'écrans et de coques **noirs**, faits pour
   les fonds pastel d'origine. Posés sur la nuit violette, ils disparaissaient : seules les
   spatules métalliques ressortaient. C'est le problème classique de la photo produit sur
   fond sombre, et sa réponse classique — un halo derrière le sujet, qui détache la
   silhouette sans éclaircir le fond de la vue. */
.bandeau-vue:not([data-ton="clair"]):not([data-ton="lilas"]) .bandeau-visuel::before {
  content: '';
  position: absolute; inset: 6% 4%;
  border-radius: 50%;
  background: radial-gradient(closest-side, rgba(233, 230, 255, .30), rgba(233, 230, 255, .10) 58%, transparent 76%);
}
.bandeau-vue:not([data-ton="clair"]):not([data-ton="lilas"]) .bandeau-visuel img {
  filter: drop-shadow(0 22px 36px rgba(0, 0, 0, .55)) brightness(1.12) contrast(1.06);
}
.bandeau-vue[data-ton="clair"] .bandeau-visuel img,
.bandeau-vue[data-ton="lilas"] .bandeau-visuel img {
  filter: drop-shadow(0 18px 30px rgba(20, 20, 43, .22));
}

/* ---- mode image : la vue entière est un visuel ---- */
/* `container-type: inline-size` fait du bloc la référence des unités `cqw` employées plus
   bas : le bouton flottant se dimensionne alors sur la largeur de l'image, et non sur celle
   de la fenêtre. Les deux coïncident tant que la bannière est pleine largeur — elles
   cesseront de coïncider le jour où elle entrera dans une colonne, et c'est précisément ce
   qu'on ne veut pas avoir à rattraper. */
.bandeau-vue.est-image { display: block; padding: 0; min-height: 0; container-type: inline-size; }
.bandeau-vue.est-image .bandeau-image-lien { display: block; }
/* Le rapport exact du format d'export, 2360 × 550.

   Il a d'abord été à 4/1, puis à 1370/350 : `cover` rognait alors tout visuel qui n'était pas
   à ce rapport précis. Le cadre suit désormais le format dans lequel les bannières sont
   dessinées, et non l'inverse — un export conforme n'est plus jamais recadré. Le rapport reste
   fixe plutôt que déduit de chaque image : six vues de hauteurs différentes feraient sauter la
   page à chaque rotation du carrousel. */
.bandeau-vue.est-image img {
  width: 100%; height: auto; display: block;
  aspect-ratio: 2360 / 550; object-fit: cover;
}

/* ---- le bouton posé sur le visuel ----

   Les bannières arrivent sans bouton, volontairement : leur texte est peint dans le fichier,
   le bouton reste du HTML. Il se place en pourcentage de l'image (`--cta-x` à gauche,
   `--cta-y` au centre vertical), réglé par vue depuis le back-office, et il se dimensionne
   en `cqw` : à toutes les largeurs, il garde la même place et la même taille relative sur le
   visuel, au lieu de grossir dessus quand l'écran rétrécit. */
.bandeau-cta-flottant {
  position: absolute; z-index: 2;
  left: var(--cta-x, 4%);
  /* La position réglée en back-office est bornée ici, et non seulement à l'enregistrement :
     elle vise le *milieu* du bouton, dont la hauteur ne rétrécit pas aussi vite que l'image.
     À 88 % sur une bannière de 1370 px, le bouton tient ; sur la même bannière réduite à
     390 px de large — donc 100 px de haut — il dépassait d'un demi-pixel en bas. Les bornes
     se resserrent avec la largeur, là où le bouton pèse le plus lourd dans l'image. */
  top: clamp(8%, var(--cta-y, 72%), 92%);
  transform: translateY(-50%);
  display: inline-flex; align-items: center; gap: clamp(5px, .62cqw, 9px);
  max-width: calc(97% - var(--cta-x, 4%));
  /* La teinte vient de la vue (`--cta-c`, prélevée sur le visuel) ; le second arrêt du
     dégradé en est dérivé plutôt que stocké, pour que le back-office n'ait qu'un seul
     réglage à proposer. `oklab` assombrit sans virer : en sRGB, l'orange de Weave tournait
     au brun et le magenta des Accessoires au prune. */
  --cta-sombre: color-mix(in oklab, var(--cta-c, #6D3BF5) 76%, #000);
  background: linear-gradient(135deg, var(--cta-c, #6D3BF5) 0%, var(--cta-sombre) 100%);
  color: #fff; text-decoration: none; cursor: pointer;
  font-weight: 650; letter-spacing: -.005em; line-height: 1.15; white-space: nowrap;
  font-size: clamp(.52rem, 1.2vw, .95rem);
  font-size: clamp(.52rem, 1.2cqw, .95rem);
  padding: clamp(5px, .95vw, 13px) clamp(10px, 1.9vw, 26px);
  padding: clamp(5px, .95cqw, 13px) clamp(10px, 1.9cqw, 26px);
  border-radius: clamp(8px, 1.2vw, 16px);
  border-radius: clamp(8px, 1.2cqw, 16px);
  /* L'ombre est teintée de la couleur du bouton, et non d'un violet fixe : une ombre violette
     sous un bouton orange se lit comme une erreur d'impression. Le liseré clair en haut
     donne le galbe — sans lui, un aplat de 44 px posé sur une photo a l'air d'un autocollant. */
  box-shadow:
    0 8px 22px -8px color-mix(in srgb, var(--cta-c, #6D3BF5) 75%, transparent),
    0 2px 5px rgba(12, 10, 32, .3),
    inset 0 1px 0 rgba(255, 255, 255, .26);
  transition: transform .2s var(--ease), box-shadow .2s var(--ease), filter .2s var(--ease);
}
.bandeau-cta-flottant svg {
  flex-shrink: 0; width: auto; height: clamp(11px, 1.2cqw, 16px);
  transition: transform .2s var(--ease);
}
.bandeau-cta-flottant:hover {
  transform: translateY(calc(-50% - 2px));
  box-shadow:
    0 16px 32px -8px color-mix(in srgb, var(--cta-c, #6D3BF5) 90%, transparent),
    0 0 0 4px color-mix(in srgb, var(--cta-c, #6D3BF5) 26%, transparent),
    inset 0 1px 0 rgba(255, 255, 255, .34);
  filter: saturate(1.06) brightness(1.04);
}
/* La flèche avance au survol : le mouvement dit « ça mène ailleurs » mieux qu'un
   changement de teinte, et il coûte une transformation sur un seul élément. */
.bandeau-cta-flottant:hover svg { transform: translateX(3px); }
.bandeau-cta-flottant:active { transform: translateY(-50%); }
/* Le bouton se pose sur une image quelconque : un anneau d'une seule couleur peut y
   disparaître. Le liseré blanc tient sur les visuels sombres, le violet sur les clairs. */
.bandeau-cta-flottant:focus-visible {
  outline: 2px solid #fff; outline-offset: 2px;
  box-shadow: 0 0 0 5px rgba(91, 61, 245, .9);
}
/* ---- flèches et puces ----

   Les trois contrôles sont enveloppés, mais l'enveloppe est transparente par défaut
   (`display: contents`) : en mode « texte + image », les flèches restent aux bords et les
   puces au centre, comme avant.

   En mode image, elles ne le peuvent plus. La bannière occupe alors toute la largeur sans
   la réserve de 76 px qui tenait les flèches à l'écart du texte : la flèche gauche tombait
   sur le titre peint dans le visuel, et les puces, au centre en bas, se posaient en travers
   de « Paiement sécurisé » et de « Compatible MagSafe ». Les trois se regroupent donc en bas
   à droite, sur un fond sombre qui tient aussi bien sur un visuel clair que sur un sombre —
   et la moitié gauche de l'image, celle qui porte le message et le bouton, reste libre. */
/* ---- flèches et puces ----

   Les trois contrôles sont enveloppés dans `.bandeau-controles`, mais l'enveloppe est
   transparente (`display: contents`) : les flèches restent aux bords, les puces au centre en
   bas, et le balisage ne décide de rien.

   Ils ont été regroupés en bas à droite le temps d'une version, parce qu'en pleine fenêtre la
   flèche gauche tombait sur le titre peint dans le visuel. Ce n'est plus le cas : la bannière
   rentre dans la largeur de page, et le gabarit d'export réserve désormais les deux bords et
   la bande du bas. Le repère de défilement reprend donc sa place sous le visuel, là où une
   jauge qui se remplit se lit comme une jauge. */
.bandeau-controles { display: contents; }

.bandeau-fleche {
  position: absolute; top: 50%; transform: translateY(-50%); z-index: 3;
  width: 40px; height: 40px; border-radius: 50%;
  display: grid; place-items: center; cursor: pointer;
  border: 1px solid rgba(255, 255, 255, .22);
  background: rgba(255, 255, 255, .14); color: #fff;
  backdrop-filter: blur(6px);
  transition: background .18s var(--ease);
}
.bandeau-fleche:hover { background: rgba(255, 255, 255, .28); }
.bandeau-fleche.est-avant { left: clamp(10px, 1.6vw, 22px); }
.bandeau-fleche.est-avant svg { transform: rotate(180deg); }
.bandeau-fleche.est-apres { right: clamp(10px, 1.6vw, 22px); }
/* Sur une vue claire, une flèche blanche translucide disparaît. */
.bandeau[data-ton="clair"] .bandeau-fleche,
/* « clair » manquait : sur la pierre de Weave et le ciel de Pokémon, une flèche blanche sur
   fond blanc translucide ne se voyait plus. */
.bandeau[data-ton="lilas"] .bandeau-fleche,
.bandeau[data-ton="clair"] .bandeau-fleche {
  background: rgba(255, 255, 255, .55); border-color: rgba(20, 20, 43, .16); color: #14142B;
}
.bandeau[data-ton="clair"] .bandeau-fleche:hover,
.bandeau[data-ton="lilas"] .bandeau-fleche:hover { background: rgba(20, 20, 43, .2); }

.bandeau-puces {
  position: absolute; z-index: 3;
  left: 50%; transform: translateX(-50%);
  bottom: clamp(12px, 1.6vw, 20px);
  display: flex; gap: 8px;
}
.bandeau-puces button {
  border: 0; background: none; padding: 6px 0; cursor: pointer;
}
.bandeau-puces i {
  display: block; width: 34px; height: 3px; border-radius: 999px;
  background: rgba(255, 255, 255, .34); overflow: hidden;
}
.bandeau[data-ton="clair"] .bandeau-puces i,
.bandeau[data-ton="lilas"] .bandeau-puces i { background: rgba(20, 20, 43, .18); }
.bandeau-puces b { display: block; width: 0; height: 100%; border-radius: 999px; background: #fff; }
.bandeau[data-ton="clair"] .bandeau-puces b,
.bandeau[data-ton="lilas"] .bandeau-puces b { background: #14142B; }
/* La jauge ne court que pendant que le bandeau tourne : à l'arrêt, une barre qui avance
   promettrait un défilement qui n'arrive pas. */
/* La puce active est pleine par défaut ; la jauge ne la remplit progressivement que
   pendant le défilement. Conditionner le remplissage au mouvement laissait la puce active
   vide à l'arrêt — et systématiquement en mouvement réduit, où le bandeau ne tourne pas :
   le seul repère était alors une nuance d'opacité sur une barre de 34 × 3 px. */
.bandeau-puces [aria-selected="true"] b { width: 100%; }
.bandeau.est-en-marche .bandeau-puces [aria-selected="true"] b { animation: jauge var(--duree-jauge, 4200ms) linear forwards; }
.bandeau-puces [aria-selected="true"] i { background: rgba(255, 255, 255, .5); }

/* Sur un visuel chargé — l'amoncellement de cartes de la bannière Pokémon — une barre à
   18 % d'opacité se fondait dans le fond quelle que soit sa couleur. Une ombre portée la
   détache sans lui ajouter de cadre : sombre sous une barre claire, claire sous une sombre.
   Elle ne concerne que le mode image ; en mode texte la jauge est posée sur un aplat. */
/* En mode image, la jauge se range à droite plutôt qu'au centre.

   Elle était centrée, et le bouton flottant se place là où le visuel est calme — ce qui tombe
   souvent au milieu en bas. Dès que le gérant a posé ses propres boutons, deux vues sur six
   avaient la jauge dessous, à toutes les largeurs. Le bouton vit dans la moitié gauche par
   construction, la droite lui est donc réservée : la jauge reste en bas, et les deux ne se
   rencontrent plus. */
.bandeau:has(.bandeau-vue.est-image) .bandeau-puces {
  left: auto; right: clamp(12px, 1.6vw, 22px); transform: none;
  filter: drop-shadow(0 1px 2px rgba(10, 8, 26, .6));
}
.bandeau[data-ton="clair"]:has(.bandeau-vue.est-image) .bandeau-puces,
.bandeau[data-ton="lilas"]:has(.bandeau-vue.est-image) .bandeau-puces {
  filter: drop-shadow(0 1px 2px rgba(255, 255, 255, .9)) drop-shadow(0 0 1px rgba(255, 255, 255, .95));
}
@keyframes jauge { from { width: 0 } to { width: 100% } }

@media (max-width: 860px) {
  /* Sur téléphone, le bouton est bridé vers la gauche en plus de l'être vers le bas.
     Un libellé comme « Protéger mon mobile », posé à 55 % sur une bannière de 358 px, finit
     sous la jauge : à cette largeur le bouton occupe un quart du cadre là où il en occupe un
     huitième sur un écran d'ordinateur. La composition du gérant est respectée tant qu'elle
     tient ; au-delà, le bouton revient dans la moitié gauche, qui est la sienne. */
  .bandeau-cta-flottant {
    top: clamp(15%, var(--cta-y, 72%), 86%);
    left: min(var(--cta-x, 4%), 34%);
  }
  /* Six barres de 34 px tenaient la moitié d'une bannière de 390 px.

     Et surtout, elles la tenaient par le milieu : sur une bannière de 89 px de haut, la jauge
     centrée et le bouton flottant se partagent la même bande horizontale, et les quatre vues
     dont le bouton est posé entre 24 % et 44 % le voyaient passer dessus. La jauge se range
     donc à droite — le bouton est toujours dans la moitié gauche, par construction — et les
     flèches, masquées au doigt à cette largeur, lui laissent la place. */
  .bandeau:has(.bandeau-vue.est-image) .bandeau-puces { gap: 3px; bottom: 5px; right: 8px; }
  .bandeau:has(.bandeau-vue.est-image) .bandeau-puces i { width: 6px; }
  .bandeau:has(.bandeau-vue.est-image) .bandeau-puces button { padding: 8px 0; }
}


@media (prefers-reduced-motion: reduce) {
  .bandeau-piste { transition: none; }
  .bandeau-cta { transition: none; }
  .bandeau-cta:hover, .bandeau-cta:active { transform: none; }
  .bandeau-cta-flottant, .bandeau-cta-flottant svg { transition: none; }
  .bandeau-cta-flottant:hover { transform: translateY(-50%); }
  .bandeau-cta-flottant:hover svg { transform: none; }
  .bandeau.est-en-marche .bandeau-puces [aria-selected="true"] b { animation: none; width: 100%; }
}

/* ---- téléphone ---- */
@media (max-width: 860px) {
  /* Le visuel passe au-dessus du texte par `order`, sans toucher au balisage : l'ordre de
     lecture et celui du lecteur d'écran restent le texte d'abord. La réserve en bas laisse
     la place aux puces, qui doivent rester sur la couleur du bandeau — posées en dessous,
     elles tombaient sur une bande blanche entre le bandeau et les offres. */
  .bandeau-vue {
    grid-template-columns: minmax(0, 1fr); min-height: 0;
    padding-bottom: 44px;
  }
  .bandeau-visuel { order: -1; }
  .bandeau-visuel img { max-height: clamp(112px, 26vw, 160px); }
  /* Les flèches recouvraient le titre : le balayage et les puces suffisent au doigt. */
  .bandeau-fleche { display: none; }
  .bandeau-puces { bottom: 16px; }
}
@media (max-width: 860px) {
  /* Les flèches sont masquées ici : la réserve de 76 px n'a plus de raison d'être. */
  .bandeau-vue { padding-inline: max(var(--s-5), calc((100% - var(--wrap)) / 2)); }
}
@media (max-width: 560px) {
  .bandeau-vue { padding-inline: var(--s-4); }
  .bandeau-atouts { gap: 14px; }
}

/* ================================================================= LES PETITES OFFRES

   Une bande dense sous le bandeau : un chiffre, une légende, une vignette, un lien. Chaque
   nombre est lu dans le catalogue au moment de l'affichage — jamais une promesse écrite à
   la main, qui survivrait au prix qu'elle annonce. */

/* Autant de colonnes que de tuiles, plutôt qu'un `auto-fit` qui en abandonnait une sur une
   deuxième rangée : à 901–918 px, quatre offres donnaient trois colonnes et une orpheline.
   Le nombre est publié par le JS dans `--offres`, parce qu'il varie — une promotion qui
   s'arrête, un seuil de franco à zéro, et il n'y en a plus que trois.

   Une variable et non un sélecteur par nombre : les trois règles `[data-offres="…"]` qu'il y
   avait ici pesaient plus lourd (0,2,0) que la requête de média (0,1,0), qui devait donc les
   répéter une à une pour reprendre la main — et une cinquième tuile, le jour où il y en aura
   une, serait retombée sur quatre colonnes avec une orpheline. */
.offres {
  display: grid;
  grid-template-columns: repeat(var(--offres, 4), minmax(0, 1fr));
  gap: clamp(10px, 1.1vw, 16px);
  padding-inline: max(var(--s-5), calc((100% - var(--wrap)) / 2));
  padding-block: clamp(14px, 1.6vw, 22px);
  border-bottom: 1px solid var(--line);
}
.offre {
  position: relative; overflow: hidden;
  border-radius: var(--radius-lg);
  border: 1px solid var(--line);
  background: var(--surface);
  padding: clamp(14px, 1.4vw, 20px);
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  min-height: clamp(92px, 8vw, 112px);
  transition: transform .24s var(--ease), border-color .24s var(--ease), box-shadow .24s var(--ease);
}
.offre:hover { transform: translateY(-3px); border-color: var(--violet); box-shadow: var(--shadow-float); }
@media (prefers-reduced-motion: reduce) {
  .offre { transition: none; }
  .offre:hover { transform: none; }
}

/* Le chiffre d'abord, la légende dessous. La première version mettait la phrase entière en
   gros — « Reconditionnés dès 379,90 € » — qui débordait sous la vignette et noyait le seul
   élément qui fait cliquer. Sur une tuile d'offre, c'est le nombre qui est le titre. */
.offre-texte { display: flex; flex-direction: column; gap: 3px; min-width: 0; flex: 1; }
.offre-texte b {
  font-family: var(--font-display); font-weight: 700;
  font-size: clamp(1.08rem, 1.5vw, 1.58rem); line-height: 1.05;
  letter-spacing: -.035em; color: var(--text);
  /* La ligne peut se replier — « dès » au-dessus du montant — mais le montant lui-même
     reste d'un bloc : il porte une espace insécable avant son symbole. */
  overflow-wrap: break-word;
}
.offre-texte > span { font-size: .78rem; line-height: 1.32; color: var(--muted); overflow-wrap: anywhere; }

.offre img {
  width: clamp(44px, 4.4vw, 58px); height: clamp(54px, 5.4vw, 72px);
  object-fit: contain; flex-shrink: 0;
  filter: drop-shadow(0 8px 14px rgba(0, 0, 0, .28));
}
.offre-icone { color: var(--violet-bright); flex-shrink: 0; display: grid; place-items: center; }

/* La promotion se distingue par le rouge de la charte des remises, pas par un rouge à
   elle : c'est la même offre que le bandeau d'annonce et que les pastilles des cartes. */
.offre.est-promo {
  border-color: var(--promo-line);
  background: linear-gradient(140deg, var(--promo-soft), transparent 70%), var(--surface);
}
.offre.est-promo .offre-texte b { color: var(--promo-text); }
.offre.est-promo:hover { border-color: var(--promo); }

/* Sous 940 px, trois colonnes deviennent trop étroites pour un nombre et sa légende : on
   passe à deux. Avec un nombre impair de tuiles, la dernière prend la largeur des deux
   plutôt que de laisser une cellule vide à côté d'elle — quel que soit ce nombre. */
@media (max-width: 940px) {
  .offres { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .offres .offre:last-child:nth-child(odd) { grid-column: span 2; }
}
@media (max-width: 560px) {
  .offres { padding-inline: var(--s-4); grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .offre { min-height: 86px; padding: 13px; gap: 8px; }
  .offre-texte b { font-size: 1.18rem; }
  .offre-texte > span { font-size: .72rem; }
  .offre img { width: 38px; height: 48px; }
}

/* =================================================================== RAYONS */

.rail-head {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: var(--s-5); flex-wrap: wrap; margin-bottom: var(--s-5);
}
.rail-head h2 { font-size: clamp(1.5rem, 2.6vw, 2.1rem); margin-top: 12px; }
.rail-sub { color: var(--muted); font-size: .9rem; margin-top: 8px; max-width: 58ch; }

.rail-tools { display: flex; align-items: center; gap: var(--s-4); }
.rail-count { font-family: var(--font-mono); font-size: .76rem; color: var(--muted-dim); white-space: nowrap; }
.rail-all { font-size: .88rem; color: var(--violet-bright); white-space: nowrap; }
.rail-all:hover { text-decoration: underline; text-underline-offset: 3px; }

.rail-arrows { display: flex; gap: 6px; }
.rail-arrow {
  width: 38px; height: 38px; display: grid; place-items: center;
  border: 1px solid var(--line); border-radius: 50%;
  background: var(--surface-2); color: var(--text-soft); cursor: pointer;
  transition: border-color .16s var(--ease), color .16s var(--ease), opacity .16s var(--ease);
}
.rail-arrow:hover:not(:disabled) { border-color: var(--violet); color: var(--text); }
.rail-arrow:disabled { opacity: .3; cursor: default; }
.rail-arrow[data-rail-prev] svg { transform: rotate(180deg); }

/* Le défilement natif est conservé : molette, trackpad et doigt fonctionnent seuls,
   les flèches ne font qu'avancer d'une page. */
/* Un conteneur qui défile horizontalement masque forcément en vertical — la spécification
   ne permet pas `overflow-y: visible` à côté d'un `overflow-x: auto`. Tout ce qui dépasse
   d'une carte en haut ou en bas est donc coupé net, y compris les 7 px dont elle se soulève
   au survol et l'ombre qui l'accompagne.

   On lui ménage la place à l'intérieur de la zone masquée, et on la reprend en marge
   négative pour que la mise en page ne bouge pas d'un pixel. Dix pixels suffisent : l'ombre
   du survol a été resserrée exprès pour tenir ici. */
.rail {
  overflow-x: auto; overflow-y: hidden;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
  margin-inline: -4px; padding-inline: 4px;
  padding-block: 10px; margin-block: -10px;
}
.rail::-webkit-scrollbar { display: none; }
.rail:focus-visible { outline: 2px solid var(--violet-bright); outline-offset: 2px; border-radius: var(--radius); }

.rail-track { display: flex; gap: var(--s-4); }
.rail-track > * {
  flex: 0 0 clamp(190px, 21vw, 248px);
  scroll-snap-align: start;
}

/* L'étiquette du rayon, en haut à gauche. Un aplat violet plein tirait l'œil avant la
   photo du produit, alors qu'elle ne fait que situer l'article : elle devient une puce
   claire à point coloré, lisible sans crier. */
.product-card .rail-badge {
  position: absolute; top: 10px; left: 10px; z-index: 2;
  display: inline-flex; align-items: center; gap: 6px;
  font-family: var(--font-mono); font-size: .62rem; letter-spacing: .1em; text-transform: uppercase;
  padding: 5px 10px 5px 8px; border-radius: 999px;
  background: var(--surface); color: var(--text-soft);
  border: 1px solid var(--line); backdrop-filter: blur(6px);
}
.product-card .rail-badge::before {
  content: ''; width: 6px; height: 6px; border-radius: 50%;
  background: var(--violet-bright); flex-shrink: 0;
}

@media (max-width: 620px) {
  /* 62 vw donnait une carte de 248 x 380 px sur un ecran de 400 : une seule tenait a
     l'ecran, et elle paraissait enorme a cote de celles du catalogue, qui font 176 x 289.
     On vise desormais deux cartes plus un debord — le debord est ce qui dit qu'il y a
     autre chose a voir — et surtout la meme carte que dans le catalogue, pour qu'un produit
     ait la meme taille partout dans la boutique. */
  .rail-track > * { flex: 0 0 clamp(150px, 45vw, 200px); }
  .rail-count { display: none; }
}

/* ================================================================== COMPTE */

.compte-initiale {
  display: grid; place-items: center; width: 100%; height: 100%;
  font-family: var(--font-display); font-weight: 600; font-size: .95rem;
  background: var(--gradient); color: var(--on-gradient); border-radius: inherit;
}
.icon-btn.is-connecte { padding: 0; overflow: hidden; border-color: transparent; }

.auth-page {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, .72fr);
  gap: clamp(28px, 5vw, 64px); align-items: start;
  padding-block: clamp(32px, 5vw, 64px) 96px;
}
.auth-card { padding: clamp(24px, 3vw, 36px); }

.auth-tabs { display: flex; gap: 4px; padding: 4px; margin-bottom: var(--s-5);
  background: rgba(10, 9, 22, .5); border: 1px solid var(--line); border-radius: var(--radius); }
.auth-tabs a {
  flex: 1; text-align: center; padding: 10px 12px; border-radius: var(--radius-sm);
  font-size: .89rem; color: var(--muted);
}
.auth-tabs a[aria-selected="true"] { background: var(--gradient-soft); color: var(--text); }
.auth-tabs a:hover { color: var(--text); }

.auth-form { display: grid; gap: var(--s-4); }
.field-hint { font-size: .78rem; color: var(--muted-dim); }
.auth-error { color: var(--danger); font-size: .86rem; min-height: 1.2em; margin: 0; }
.auth-alt { font-size: .86rem; color: var(--muted); text-align: center; }
.auth-alt a { color: var(--violet-bright); }

.auth-aside { padding-top: var(--s-5); }
.auth-aside h1 { font-size: clamp(1.6rem, 3vw, 2.2rem); margin-bottom: var(--s-5); }

.checkout-compte {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin-top: var(--s-4); padding: 12px 14px;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--gradient-soft); font-size: .87rem; color: var(--text-soft);
}
.checkout-compte svg { color: var(--violet-bright); flex-shrink: 0; }
.checkout-compte a { color: var(--violet-bright); text-decoration: underline; text-underline-offset: 3px; }

.compte-head { display: flex; align-items: flex-end; justify-content: space-between; gap: var(--s-5); flex-wrap: wrap; margin-bottom: var(--s-6); }
.compte-grid { display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(0, .9fr); gap: var(--s-4); align-items: start; }

.commandes { display: grid; gap: var(--s-3); margin-top: var(--s-5); }
.commande { border: 1px solid var(--line); border-radius: var(--radius); padding: var(--s-4); }
.commande header { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.commande header .mono { font-size: .84rem; }
.commande > p { font-size: .8rem; margin-top: 6px; }
.commande ul { list-style: none; margin: 12px 0 0; padding: 0; display: grid; gap: 5px; font-size: .86rem; color: var(--text-soft); }
.commande ul a:hover { color: var(--violet-bright); }
.commande footer { display: flex; justify-content: space-between; align-items: baseline;
  margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--line); font-size: .9rem; }

@media (max-width: 1080px) {
  .auth-page { grid-template-columns: 1fr; }
  .auth-aside { order: -1; padding-top: 0; }
  .compte-grid { grid-template-columns: 1fr; }
}

/* ============================================================== RÉPARATION

   Boucle de 5 s. Le découpage temporel est le même pour tous les calques :
     0 →  8 %  le téléphone entre, écran cassé
     8 → 26 %  le tournevis dévisse, les vis sortent
    26 → 44 %  l'écran cassé est retiré
    44 → 62 %  l'écran neuf est posé
    62 → 82 %  il s'allume, l'étincelle apparaît
    82 → 100 % le téléphone sort du cadre, ce qui masque la reprise
   ------------------------------------------------------------------------ */

.repair { width: 100%; max-width: 460px; margin-inline: auto; }
.repair svg { width: 100%; height: auto; overflow: visible; }

.rep-ombre { fill: var(--repair-ombre, rgba(0, 0, 0, .35)); filter: blur(1px); }
.rep-etabli { fill: var(--line-strong); }
.rep-chassis { fill: var(--surface-3); stroke: var(--line-strong); stroke-width: 2; }
.rep-nappe { fill: var(--ink-950); }
.rep-circuit { stroke: var(--line-strong); stroke-width: 2; stroke-linecap: round; }
.rep-casse rect { fill: #14121f; stroke: var(--line-strong); stroke-width: 1.5; }
.rep-fissure { stroke: rgba(210, 214, 255, .85); stroke-width: 1.6; fill: none; stroke-linejoin: round; }
.rep-reflet { fill: #fff; opacity: .14; }
.rep-vis circle { fill: none; stroke: var(--muted); stroke-width: 2; }
.rep-vis path { stroke: var(--muted); stroke-width: 2; stroke-linecap: round; }
.rep-paume, .rep-doigt { fill: var(--text-soft); }
.rep-lame { stroke: url(#rep-metal); stroke-width: 7; stroke-linecap: round; }
.rep-manche { stroke: var(--violet); stroke-width: 24; stroke-linecap: round; }
.rep-manche-trait { stroke: rgba(0, 0, 0, .3); stroke-width: 2; stroke-linecap: round; }
.rep-eclat { stroke: var(--cyan); stroke-width: 2.4; stroke-linecap: round; opacity: 0; }
.rep-halo { opacity: 0; }

/* ---- le plateau entier : entrée et sortie de champ ---- */
.rep-scene { animation: rep-defile 5s linear infinite; }
@keyframes rep-defile {
  0%   { transform: translateX(46px); opacity: 0; }
  6%   { transform: translateX(0);    opacity: 1; }
  84%  { transform: translateX(0);    opacity: 1; }
  94%  { transform: translateX(-52px); opacity: 0; }
  100% { transform: translateX(46px); opacity: 0; }
}

/* ---- l'outil entre, visse, puis se retire ---- */
.rep-outil { animation: rep-outil 5s ease-in-out infinite; }
@keyframes rep-outil {
  /* L'outil entre et repart par la droite, à hauteur d'établi : une sortie vers le bas
     le faisait passer sous le plan de travail. */
  0%, 6%   { transform: translate(120px, 6px); }
  16%, 28% { transform: translate(0, 0); }
  42%      { transform: translate(126px, 2px); }
  82%      { transform: translate(126px, 2px); }
  100%     { transform: translate(120px, 6px); }
}
/* Oscillation courte autour de la pointe : le geste du vissage. */
.rep-tournevis { transform-box: view-box; transform-origin: 236px 246px; animation: rep-visse 5s linear infinite; }
@keyframes rep-visse {
  0%, 15%          { transform: rotate(0deg); }
  17.5%            { transform: rotate(13deg); }
  20%              { transform: rotate(-4deg); }
  22.5%            { transform: rotate(13deg); }
  25%              { transform: rotate(-4deg); }
  27.5%, 100%      { transform: rotate(0deg); }
}

/* ---- les vis sortent de leur logement ---- */
.rep-vis-a, .rep-vis-b { animation: rep-vis 5s ease-in infinite; }
.rep-vis-b { animation-delay: .35s; }
@keyframes rep-vis {
  0%, 14%  { transform: translateY(0); opacity: 1; }
  30%      { transform: translateY(16px); opacity: 0; }
  100%     { transform: translateY(16px); opacity: 0; }
}

/* ---- l'écran cassé est soulevé et sorti ---- */
.rep-casse { animation: rep-casse 5s ease-in-out infinite; }
@keyframes rep-casse {
  0%, 28%  { transform: translate(0, 0) rotate(0deg); opacity: 1; }
  38%      { transform: translate(18px, -26px) rotate(7deg); opacity: 1; }
  46%      { transform: translate(120px, -62px) rotate(16deg); opacity: 0; }
  100%     { transform: translate(120px, -62px) rotate(16deg); opacity: 0; }
}

/* ---- l'écran neuf descend en place, puis s'allume ---- */
.rep-neuf { opacity: 0; animation: rep-neuf 5s ease-out infinite; }
@keyframes rep-neuf {
  0%, 46%  { transform: translate(110px, -56px) rotate(12deg); opacity: 0; }
  52%      { opacity: 1; }
  62%      { transform: translate(0, 0) rotate(0deg); opacity: 1; }
  100%     { transform: translate(0, 0) rotate(0deg); opacity: 1; }
}
.rep-halo { animation: rep-halo 5s ease-out infinite; }
@keyframes rep-halo { 0%, 62% { opacity: 0; } 72% { opacity: 1; } 84% { opacity: .55; } 100% { opacity: 0; } }

.rep-eclat { animation: rep-eclat 5s ease-out infinite; }
@keyframes rep-eclat {
  0%, 64%  { opacity: 0; transform: scale(.4); }
  72%      { opacity: 1; transform: scale(1); }
  82%      { opacity: 0; transform: scale(1.25); }
  100%     { opacity: 0; transform: scale(.4); }
}

/* Toutes les transformations tournent autour du téléphone, pas du coin du SVG. */
.rep-scene, .rep-casse, .rep-neuf, .rep-outil, .rep-vis-a, .rep-vis-b, .rep-eclat, .rep-halo {
  transform-box: view-box; transform-origin: 210px 145px;
}
.rep-eclat { transform-origin: 286px 86px; }
.rep-vis-a { transform-origin: 186px 244px; }
.rep-vis-b { transform-origin: 234px 244px; }

/* Sans animation : on montre l'état d'arrivée, écran neuf posé et allumé. */
@media (prefers-reduced-motion: reduce) {
  .rep-scene, .rep-outil, .rep-tournevis, .rep-vis-a, .rep-vis-b,
  .rep-casse, .rep-neuf, .rep-halo, .rep-eclat { animation: none; }
  .rep-casse { opacity: 0; }
  .rep-neuf { opacity: 1; }
  .rep-vis-a, .rep-vis-b { opacity: 1; }
  .rep-outil { transform: translate(64px, 34px); }
  .rep-tournevis { transform: translate(230px, 250px); }
}

/* ------------------------------------------------ mot de passe oublié */

.oubli-confirme {
  display: flex; gap: 12px; align-items: flex-start;
  padding: 18px; border-radius: var(--radius);
  background: var(--gradient-soft); border: 1px solid var(--line-strong);
}
.oubli-confirme > svg { color: var(--success); flex-shrink: 0; margin-top: 2px; }
.oubli-avertissement {
  display: flex; gap: 8px; align-items: flex-start;
  font-size: .82rem; color: var(--warning);
  padding-top: 12px; border-top: 1px solid var(--line);
}
.oubli-avertissement svg { flex-shrink: 0; margin-top: 2px; }

/* ==================================================================== BLOG */

/* Le contenu servi aux moteurs n'est visible que tant que l'application n'a pas démarré ;
   il reste dans le flux pour qu'un lecteur sans JavaScript puisse le lire. */
#prerendu { width: min(100% - 40px, 760px); margin: 40px auto; color: var(--text-soft); }
#prerendu h1 { margin-bottom: 16px; }
#prerendu a { color: var(--violet-bright); }

/* Le choix de rubrique, sous le chapô de la liste. Deux puces, pas un menu : il n'y a
   que deux rubriques et elles doivent se lire d'un coup d'œil. */
.rubriques { display: flex; gap: 8px; flex-wrap: wrap; margin-top: var(--s-5); }
.rubriques .chip[aria-current="page"] { border-color: var(--violet); }

.blog-grille { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: var(--s-4); margin-top: var(--s-6); }

.blog-carte {
  border: 1px solid var(--line); border-radius: var(--radius-lg); overflow: hidden;
  background: linear-gradient(180deg, rgba(29,28,60,.42), rgba(14,13,30,.75));
  transition: transform .2s var(--ease), border-color .2s var(--ease);
}
.blog-carte:hover { transform: translateY(-3px); border-color: var(--violet); }
.blog-carte.est-une { grid-column: 1 / -1; }
.blog-carte.est-une .blog-corps h2 { font-size: clamp(1.4rem, 2.4vw, 1.9rem); }
.blog-visuel { aspect-ratio: 16/9; background: var(--media-bg); }
.blog-visuel img { width: 100%; height: 100%; object-fit: cover; }
.blog-corps { padding: var(--s-5); }
.blog-meta { font-family: var(--font-mono); font-size: .72rem; color: var(--muted-dim); letter-spacing: .06em; }
/* Sur la page du blog le titre d'un article est un h2 ; dans la section de l'accueil,
   il passe sous le h2 de la section et devient un h3. La hiérarchie des titres compte
   pour le référencement, la mise en forme ne doit donc pas dépendre du niveau. */
.blog-corps :is(h2, h3) { font-size: 1.2rem; margin-block: 10px 10px; }
.blog-corps > p { color: var(--muted); font-size: .92rem; }
.blog-lire { display: inline-flex; align-items: center; gap: 6px; margin-top: var(--s-4); color: var(--violet-bright); font-size: .88rem; }

/* ---------------------------------------------------- article et page fixe */

.article-page {
  display: grid; grid-template-columns: minmax(0, 1fr) 300px;
  gap: clamp(28px, 4vw, 60px); align-items: start;
  padding-block: var(--s-5) var(--s-9);
}
/* Le fil d'Ariane occupe toute la largeur : sans cela il prend la première cellule
   et pousse l'article dans la colonne étroite. */
.article-page > .breadcrumb { grid-column: 1 / -1; padding-top: 0; }
.article { max-width: 720px; }
.article header h1 { font-size: clamp(1.8rem, 3.6vw, 2.7rem); margin-block: 12px 16px; }
.article-chapo { font-size: clamp(1rem, 1.4vw, 1.14rem); color: var(--text-soft); line-height: 1.6; }
.article-meta { font-family: var(--font-mono); font-size: .76rem; color: var(--muted-dim); margin-top: var(--s-4); }
.article-visuel { width: 100%; border-radius: var(--radius-lg); margin-top: var(--s-5); }

/* Mesure de ligne confortable : autour de 70 signes, c'est ce qui se lit le mieux. */
.article-corps { margin-top: var(--s-6); font-size: 1.02rem; line-height: 1.75; color: var(--text-soft); }
.article-corps h2 { font-size: clamp(1.25rem, 2vw, 1.55rem); margin-block: var(--s-7) var(--s-3); color: var(--text); }
.article-corps h3 { font-size: 1.08rem; margin-block: var(--s-5) var(--s-2); color: var(--text); }
.article-corps p { margin-bottom: var(--s-4); max-width: 68ch; }
.article-corps ul { margin: 0 0 var(--s-4); padding-left: 20px; display: grid; gap: 8px; max-width: 68ch; }
.article-corps li::marker { color: var(--violet-bright); }
.article-corps a { color: var(--violet-bright); text-decoration: underline; text-underline-offset: 3px; }
.article-corps strong { color: var(--text); }
.article-corps blockquote {
  margin: var(--s-5) 0; padding: var(--s-4) var(--s-5);
  border-left: 3px solid var(--violet); border-radius: 0 var(--radius) var(--radius) 0;
  background: var(--gradient-soft); color: var(--text); font-size: 1.02rem;
}

.article-suite { position: sticky; top: calc(var(--header-h) + 16px); display: grid; gap: var(--s-4); }
.article-cta { padding: var(--s-5); }
.article-cta h2 { font-size: 1.05rem; margin-bottom: 8px; }
.article-cta p { font-size: .88rem; color: var(--muted); margin-bottom: var(--s-4); }
.article-autres h2 { font-size: .8rem; font-family: var(--font-mono); letter-spacing: .12em; text-transform: uppercase; color: var(--muted-dim); margin-bottom: var(--s-3); }
.article-autres ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 2px; }
.article-autres a { display: grid; gap: 4px; padding: 12px; border-radius: var(--radius); }
.article-autres a:hover { background: rgba(124,92,255,.1); }
.article-autres b { font-size: .92rem; font-weight: 500; }
.article-autres span { font-size: .8rem; color: var(--muted); display: -webkit-box; -webkit-line-clamp: 2; line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }

@media (max-width: 1080px) {
  .article-page { grid-template-columns: 1fr; }
  .article-suite { position: static; }
}

/* ================================================================= CONTACT */

.contact-bouton { position: relative; }
.contact-pastille {
  position: absolute; top: 7px; right: 7px;
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--success); border: 2px solid var(--ink-950);
  animation: contact-battement 2.4s ease-in-out infinite;
}
@keyframes contact-battement { 0%, 100% { transform: scale(1); opacity: 1; } 50% { transform: scale(1.25); opacity: .7; } }
@media (prefers-reduced-motion: reduce) { .contact-pastille { animation: none; } }

.contact-zone { position: fixed; inset: 0; z-index: 88; display: flex; justify-content: flex-end; }
.contact-voile { position: absolute; inset: 0; background: rgba(4, 4, 10, .66); backdrop-filter: blur(3px); }

.contact-panneau {
  position: relative; z-index: 1;
  width: min(430px, 100%); height: 100%; overflow-y: auto;
  background: var(--ink-900); border-left: 1px solid var(--line-strong);
  padding: 24px; box-shadow: -34px 0 64px -34px rgba(0, 0, 0, .9);
  animation: contact-entree .24s var(--ease);
}
@keyframes contact-entree { from { transform: translateX(34px); opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .contact-panneau { animation: none; } }

.contact-panneau > header { display: flex; align-items: flex-start; justify-content: space-between; gap: 14px; margin-bottom: 18px; }
.contact-panneau h2 { font-size: 1.3rem; }

.contact-etat { display: flex; align-items: center; gap: 7px; margin-top: 6px; font-size: .84rem; color: var(--muted); }
.contact-etat .pastille { width: 8px; height: 8px; border-radius: 50%; background: var(--muted-dim); flex-shrink: 0; }
.contact-etat.est-ouvert { color: var(--success); }
.contact-etat.est-ouvert .pastille { background: var(--success); }

.contact-promesse {
  display: flex; gap: 9px; align-items: flex-start;
  padding: 13px 14px; border-radius: var(--radius);
  background: var(--gradient-soft); border: 1px solid var(--line);
  font-size: .88rem; color: var(--text-soft); margin-bottom: 18px;
}
.contact-promesse svg { color: var(--success); flex-shrink: 0; margin-top: 2px; }

/* Appeler est l'action qui convertit le mieux quand quelqu'un peut décrocher : elle est
   donc mise en avant, et disparaît en dehors des horaires. */
.contact-appel {
  display: flex; align-items: center; gap: 13px;
  padding: 15px 18px; border-radius: var(--radius-lg);
  background: var(--gradient); color: var(--on-gradient);
  box-shadow: 0 12px 30px -14px rgba(102, 74, 255, .9);
  transition: transform .18s var(--ease);
}
.contact-appel:hover { transform: translateY(-1px); }
.contact-appel span { display: flex; flex-direction: column; }
.contact-appel b { font-family: var(--font-display); font-size: 1.16rem; letter-spacing: -.01em; }
.contact-appel small { font-size: .78rem; opacity: .85; }
.contact-ou { text-align: center; font-size: .82rem; color: var(--muted-dim); margin: 14px 0; }

.contact-contexte {
  display: flex; gap: 8px; align-items: flex-start;
  font-size: .82rem; color: var(--muted); margin-bottom: 14px;
  padding: 10px 12px; border: 1px dashed var(--line-strong); border-radius: var(--radius);
}
.contact-contexte svg { color: var(--violet-bright); flex-shrink: 0; margin-top: 2px; }
.contact-contexte b { color: var(--text-soft); }

.contact-form { display: grid; gap: var(--s-4); }
.contact-plus summary { cursor: pointer; font-size: .84rem; color: var(--violet-bright); }
.contact-mentions { font-size: .76rem; color: var(--muted-dim); text-align: center; }

.contact-envoye { text-align: center; display: grid; gap: 10px; padding: 20px 0; }
.contact-envoye > svg { color: var(--success); margin-inline: auto; }
.contact-envoye h3 { font-size: 1.15rem; }
.contact-envoye p { font-size: .92rem; color: var(--text-soft); }
.contact-envoye .btn { margin-top: 10px; }

@media (max-width: 620px) { .contact-panneau { width: 100%; border-left: 0; } }


/* ======================================================= FINITIONS TÉLÉPHONE

   Ces règles closent la feuille à dessein : elles doivent l'emporter sur les sections
   définies plus haut, or à spécificité égale c'est la dernière déclaration qui gagne.
   Elles traitent deux choses que le grand écran pardonne et le téléphone non : les
   libellés trop petits, et les zones tactiles trop étroites pour un pouce. */

@media (max-width: 760px) {
  /* Sous 11 px, un libellé devient pénible à lire sur un téléphone tenu à bout de bras. */
  .product-body .fam { font-size: .74rem; letter-spacing: .06em; }
  .tag { font-size: .74rem; padding: 6px 11px; }
  .product-card .rail-badge { font-size: .7rem; padding: 5px 10px; }
  .price-ht, .blog-meta, .article-meta, .rail-count { font-size: .78rem; }
  .bandeau-atouts span { font-size: .8rem; }

  /* Apple recommande 44 px de côté pour une cible tactile ; en dessous on rate sa cible. */
  .rail-all,
  .auth-alt a,
  .article-autres a,
  .breadcrumb a,
  .footer a { min-height: 44px; display: inline-flex; align-items: center; }
  .product-detail .tag { min-height: 34px; }
  .faq-q { padding-block: var(--s-4); }
  .qty button { width: 46px; height: 48px; }
  .qty input { height: 48px; }
  .add-btn { width: 44px; height: 44px; }
  .filter-opt { padding-block: 11px; }
  .chip { padding: 10px 15px; }
}

/* ================================================================ MOUVEMENT

   Le mouvement sert à trois choses ici : annoncer ce qui arrive, confirmer ce qui vient
   d'être fait, et situer où l'on en est. Rien ne dépasse 600 ms — au-delà l'interface
   paraît lente plutôt que vivante — et tout s'efface si le visiteur a demandé à réduire
   les animations. */

.reveal {
  opacity: 0; transform: translateY(30px) scale(.965);
  transition: opacity .62s var(--ease) var(--retard, 0ms),
              transform .62s var(--ease) var(--retard, 0ms);
}
.reveal.is-in { opacity: 1; transform: none; }

/* Entrée de vue : un effacement court, qui signale le changement de page sans le ralentir.
   Le remplissage est `backwards` et non `both` : avec `both`, l'animation laisse une
   transformation — fût-elle l'identité — sur le conteneur après la fin, et un ancêtre
   transformé casse le `position: fixed` de ses descendants. La barre d'achat collante
   se retrouvait alors positionnée hors de l'écran. */
.vue-entre { animation: vue-entre .42s var(--ease) backwards; }
@keyframes vue-entre { from { opacity: 0; transform: translateY(20px); } }

/* Le compteur du panier sursaute à l'ajout : on le voit du coin de l'œil. */
.cart-count.sursaute { animation: sursaut .5s var(--ease); }
@keyframes sursaut {
  0% { transform: scale(1); }
  35% { transform: scale(1.45); }
  60% { transform: scale(.92); }
  100% { transform: scale(1); }
}

/* Retour d'appui : le bouton s'enfonce légèrement. Sur tactile, c'est souvent le seul
   signe qu'on a bien touché. */
.btn:active, .bandeau-cta:active, .add-btn:active, .icon-btn:active, .chip:active { transform: scale(.97); }
.btn, .bandeau-cta, .add-btn, .icon-btn, .chip { transition: transform .12s var(--ease), background .16s var(--ease), border-color .16s var(--ease), box-shadow .18s var(--ease); }

/* La carte produit se soulève et son image respire : l'objet paraît saisissable. */
.product-card { will-change: transform; }
.product-card:hover .product-media img { transform: scale(1.1); }
.brand-card, .panne-card, .blog-carte { transition: transform .22s var(--ease), border-color .22s var(--ease), box-shadow .22s var(--ease); }

/* Le pictogramme d'un argument suit le survol de sa carte. */
.panne-card .p-arrow { transition: transform .2s var(--ease), color .2s var(--ease); }
.panne-card:hover .p-arrow { transform: translateX(4px); }
.rail-all, .blog-lire { transition: color .16s var(--ease); }
.blog-carte:hover .blog-lire { text-decoration: underline; text-underline-offset: 3px; }

/* Les squelettes cèdent la place en fondu plutôt que d'un coup. */
@keyframes apparition { from { opacity: 0; } }
.product-grid, .rail-track, .blog-grille { animation: apparition .3s var(--ease); }

/* --------------------------------- barre d'achat collante sur téléphone ---
   Sur une fiche longue, le bouton d'achat disparaît dès qu'on lit les caractéristiques.
   Cette barre le ramène sous le pouce, sans jamais masquer le contenu. */

.barre-achat {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 60;
  display: none; align-items: center; gap: 12px;
  padding: 10px 14px calc(10px + env(safe-area-inset-bottom));
  background: var(--barre-achat-fond); backdrop-filter: blur(14px);
  border-top: 1px solid var(--line-strong);
  transform: translateY(105%); transition: transform .28s var(--ease);
}
.barre-achat.est-visible { transform: none; }
.barre-achat .ba-prix { display: flex; flex-direction: column; line-height: 1.2; }
.barre-achat .ba-prix b { font-family: var(--font-display); font-size: 1.1rem; }
.barre-achat .ba-prix span { font-size: .72rem; color: var(--muted); }
.barre-achat .btn { flex: 1; }

@media (max-width: 860px) { .barre-achat { display: flex; } }

@media (prefers-reduced-motion: reduce) {
  .reveal { opacity: 1; transform: none; transition: none; }
  .vue-entre, .cart-count.sursaute,
  .product-grid, .rail-track, .blog-grille { animation: none; }
  .btn:active, .bandeau-cta:active, .add-btn:active, .icon-btn:active, .chip:active { transform: none; }
  .barre-achat { transition: none; }
}

/* =================================================================== SOUFFLE

   Ce que le bloc MOUVEMENT ci-dessus fait, il le faisait trop discrètement : des
   fondus de quelques pixels qu'on ne perçoit pas en naviguant normalement. Ici on
   ajoute ce qui se voit vraiment — une atmosphère qui respire en fond, un en-tête
   qui se compacte au défilement, des surfaces qui réagissent franchement — sans
   tomber dans l'agitation : rien ne clignote, rien ne rebondit, rien ne tourne. */

/* ---- l'atmosphère : deux halos violet et bleu qui dérivent lentement -------
   C'est le seul élément permanent. Il occupe le fond derrière le contenu
   (`#shell` est en z-index 1), il ne capte aucun clic, et sa lenteur — une demi-
   minute par cycle — fait qu'on le ressent sans jamais le regarder. */
body::after {
  content: '';
  position: fixed; inset: -20%;
  z-index: 0; pointer-events: none;
  background:
    radial-gradient(38% 42% at 22% 18%, rgba(124, 92, 255, .20), transparent 62%),
    radial-gradient(34% 38% at 78% 32%, rgba(46, 125, 255, .16), transparent 62%),
    radial-gradient(30% 34% at 55% 82%, rgba(63, 224, 224, .08), transparent 64%);
  animation: derive 34s ease-in-out infinite alternate;
  will-change: transform;
}
@keyframes derive {
  0%   { transform: translate3d(0, 0, 0) scale(1); }
  50%  { transform: translate3d(2.5%, -2%, 0) scale(1.08); }
  100% { transform: translate3d(-2%, 2.5%, 0) scale(1.04); }
}

/* ---- l'en-tête se compacte dès qu'on défile ------------------------------
   Le gain de hauteur est réel sur un portable, et le mouvement dit au visiteur
   qu'il a quitté le haut de page. La classe est posée par `enTeteCompact()`. */
.header { transition: box-shadow .3s var(--ease), background .3s var(--ease); }
.header-top { transition: height .28s var(--ease); }
.logo svg { transition: width .28s var(--ease), height .28s var(--ease); }
.brand-bar { transition: opacity .28s var(--ease); }

.header.est-compact {
  background: var(--header-bg-compact);
  box-shadow: 0 18px 40px -28px rgba(0, 0, 0, 1),
              0 1px 0 color-mix(in srgb, var(--violet) 18%, transparent);
}
/* Sous 860 px l'en-tête s'enroule sur plusieurs lignes et sa hauteur est mesurée en
   JavaScript : lui imposer une hauteur fixe écraserait les rangées repliées. Le grand
   écran gagne donc de la place, le téléphone se contente du relief. */
@media (min-width: 861px) {
  .header.est-compact .header-top { height: 58px; }
  .header.est-compact .logo svg { width: 28px; height: 28px; }
}

/* ---- un reflet traverse le bouton principal au survol --------------------
   Réservé au bouton principal : c'est ce qui le distingue des autres au premier
   coup d'œil, et l'effet perdrait tout son sens s'il était partout. */
.btn-primary { position: relative; overflow: hidden; }
.btn-primary::after {
  content: ''; position: absolute; top: 0; bottom: 0; left: -60%; width: 45%;
  background: linear-gradient(100deg, transparent, rgba(255, 255, 255, .34), transparent);
  transform: skewX(-18deg);
  pointer-events: none;
}
.btn-primary:hover::after { animation: reflet .72s var(--ease); }
@keyframes reflet { to { left: 130%; } }

/* ---- le titre de section s'annonce par un trait qui se trace -------------- */
.section-head .eyebrow::before { transition: width .5s var(--ease) .1s; }
.section-head.reveal .eyebrow::before { width: 0; }
.section-head.reveal.is-in .eyebrow::before { width: 22px; }

/* ---- les surfaces cliquables se soulèvent pour de bon --------------------- */
.brand-card:hover { transform: translateY(-6px); box-shadow: 0 22px 44px -26px rgba(0, 0, 0, .95), 0 0 26px -8px color-mix(in srgb, var(--violet) 40%, transparent); }
.panne-card:hover { transform: translateY(-5px); box-shadow: 0 20px 40px -26px rgba(0, 0, 0, .95), 0 0 24px -10px color-mix(in srgb, var(--violet) 38%, transparent); }
.panne-card:hover .p-arrow { transform: translateX(7px); }
.blog-carte:hover { transform: translateY(-6px); border-color: var(--violet); }
.panne-card .p-icon { transition: transform .2s var(--ease), background .2s var(--ease); }
.panne-card:hover .p-icon { transform: scale(1.1) rotate(-4deg); }

/* ---- l'icône du panier accuse réception ----------------------------------- */
.icon-btn:hover { transform: translateY(-2px); border-color: var(--violet); box-shadow: 0 0 18px -6px color-mix(in srgb, var(--violet) 60%, transparent); }

/* ---- respect du réglage système ------------------------------------------
   Tout ce qui précède s'annule si le visiteur a demandé à réduire les animations.
   Les survols restent, eux : ce sont des retours d'action, pas de la décoration. */
@media (prefers-reduced-motion: reduce) {
  body::after { animation: none; }
  .btn-primary:hover::after { animation: none; }
  .header-top, .logo svg, .header, .brand-bar { transition: none; }
  .section-head .eyebrow::before,
  .section-head.reveal.is-in .eyebrow::before { transition: none; width: 22px; }
}

/* ================================================================= ASSISTANT

   Le panneau de contact accueille désormais deux volets. Il devient donc une colonne :
   l'en-tête et les onglets restent en place, et c'est le volet actif qui défile. Sans ça,
   le champ de saisie de la conversation partait sous la ligne de flottaison dès que
   l'échange dépassait quelques messages. */

.contact-panneau { display: flex; flex-direction: column; overflow: hidden; }
.contact-volet { flex: 1; min-height: 0; overflow-y: auto; }
#volet-assistant { display: flex; flex-direction: column; overflow: hidden; }

.contact-onglets {
  display: flex; gap: 4px; padding: 4px; margin-bottom: 16px; flex-shrink: 0;
  background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--radius);
}
.contact-onglet {
  flex: 1; padding: 9px 12px; border: 0; border-radius: var(--radius-sm);
  background: none; color: var(--muted); font-size: .88rem; font-weight: 500; cursor: pointer;
  transition: background .16s var(--ease), color .16s var(--ease);
}
.contact-onglet:hover { color: var(--text); }
.contact-onglet.est-actif { background: var(--gradient); color: var(--on-gradient); }

/* ---- la conversation ---- */

.assistant { display: flex; flex-direction: column; min-height: 0; flex: 1; }

.assist-fil {
  flex: 1; min-height: 0; overflow-y: auto;
  display: flex; flex-direction: column; align-items: flex-start; gap: 10px;
  padding-right: 4px;
}

.bulle {
  max-width: 88%; padding: 10px 13px; border-radius: var(--radius);
  font-size: .9rem; line-height: 1.5; white-space: pre-wrap;
}
.bulle.est-assistant {
  background: var(--surface-2); border: 1px solid var(--line); color: var(--text-soft);
  border-bottom-left-radius: 4px;
}
.bulle.est-client {
  align-self: flex-end; background: var(--gradient); color: var(--on-gradient);
  border-bottom-right-radius: 4px;
}

/* Trois points qui respirent pendant l'attente : sans ce signal, un délai réseau passe
   pour une panne et le visiteur renvoie son message. */
.assist-chargement { display: flex; gap: 5px; padding: 12px 13px; }
.assist-chargement span {
  width: 6px; height: 6px; border-radius: 50%; background: var(--muted-dim);
  animation: assist-respire 1.1s var(--ease) infinite;
}
.assist-chargement span:nth-child(2) { animation-delay: .15s; }
.assist-chargement span:nth-child(3) { animation-delay: .3s; }
@keyframes assist-respire { 0%, 60%, 100% { opacity: .25; } 30% { opacity: 1; } }

/* ---- les pièces proposées : de vraies cartes, cliquables jusqu'à la fiche ---- */

.assist-produits { display: grid; gap: 7px; width: 100%; }
.assist-produit {
  display: flex; align-items: center; gap: 11px; padding: 9px;
  border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface);
  transition: border-color .16s var(--ease), transform .16s var(--ease);
}
.assist-produit:hover { border-color: var(--violet); transform: translateX(3px); }
.assist-produit-visuel {
  width: 46px; height: 46px; flex-shrink: 0; border-radius: 8px; overflow: hidden;
  background: var(--media-bg); display: grid; place-items: center;
}
.assist-produit-visuel img { width: 100%; height: 100%; object-fit: contain; padding: 3px; }
/* Le conteneur est toujours blanc (`--media-bg`) : `#b9b5de` n'y offrait que 1,96:1,
   sous le minimum de 3:1 attendu d'un élément graphique. */
.assist-produit-vide { color: #6F6A94; display: grid; place-items: center; }
.assist-produit-texte { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.assist-produit-texte b {
  font-size: .84rem; font-weight: 500; line-height: 1.35; color: var(--text);
  display: -webkit-box; -webkit-line-clamp: 2; line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.assist-produit-prix { font-family: var(--font-display); font-size: .95rem; color: var(--violet-bright); }
.assist-produit svg { flex-shrink: 0; color: var(--muted-dim); }

.assist-lien {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: .86rem; color: var(--violet-bright);
}
.assist-lien:hover { text-decoration: underline; text-underline-offset: 3px; }
.assist-humain { align-self: flex-start; }

/* ---- réponses rapides et saisie ---- */

.assist-rapides { display: flex; flex-wrap: wrap; gap: 6px; padding: 12px 0 10px; flex-shrink: 0; }
.assist-rapides .chip { font-size: .82rem; padding: 6px 12px; cursor: pointer; }

.assist-saisie { display: flex; gap: 8px; flex-shrink: 0; }
.assist-saisie .input { flex: 1; min-width: 0; }
.assist-saisie .btn { padding: 12px 15px; flex-shrink: 0; }

.assist-mentions {
  margin-top: 9px; font-size: .74rem; color: var(--muted-dim); line-height: 1.45; flex-shrink: 0;
}

@media (max-width: 620px) {
  /* Le clavier virtuel ampute la hauteur : la conversation doit pouvoir se réduire sans
     jamais pousser le champ de saisie hors de l'écran. */
  .bulle { max-width: 92%; font-size: .93rem; }
  .assist-rapides .chip { font-size: .86rem; padding: 8px 13px; }
  .contact-onglet { padding: 11px 10px; }
}

@media (prefers-reduced-motion: reduce) {
  .assist-chargement span { animation: none; opacity: .5; }
  .assist-produit:hover { transform: none; }
}

/* ---- lanceur flottant de l'assistant --------------------------------------
   Il occupe le coin bas-droit, la place que les visiteurs cherchent d'instinct. Il passe
   sous le panneau (z-index 88) et s'efface quand celui-ci s'ouvre : garder un bouton
   « ouvrir » visible alors que la fenêtre est déjà ouverte n'aide personne. */

.assist-lanceur {
  position: fixed; right: 18px; bottom: 18px; z-index: 87;
  display: flex; align-items: center; gap: 11px;
  padding: 10px 18px 10px 11px;
  border: 1px solid var(--line-strong); border-radius: 999px;
  background: var(--pop-bg); color: var(--text);
  box-shadow: var(--shadow-pop), 0 0 0 0 color-mix(in srgb, var(--violet) 40%, transparent);
  cursor: pointer; text-align: left;
  transition: transform .18s var(--ease), box-shadow .22s var(--ease), border-color .18s var(--ease);
}
.assist-lanceur:hover {
  transform: translateY(-2px);
  border-color: var(--violet);
  box-shadow: var(--shadow-pop), 0 0 26px -6px color-mix(in srgb, var(--violet) 55%, transparent);
}
.assist-lanceur:active { transform: translateY(0); }

.assist-lanceur-icone {
  width: 36px; height: 36px; flex-shrink: 0; display: grid; place-items: center;
  border-radius: 50%; background: var(--gradient); color: var(--on-gradient);
}
.assist-lanceur-texte { display: flex; flex-direction: column; line-height: 1.25; }
.assist-lanceur-texte b { font-size: .9rem; font-weight: 600; }
.assist-lanceur-texte small { font-size: .74rem; color: var(--muted); }

body:has(.contact-zone) .assist-lanceur { display: none; }

/* Une seule respiration au chargement, pour attirer l'œil sans harceler : trois pulsations
   puis plus rien. Un halo qui clignote en permanence se fait ignorer en dix secondes. */
@keyframes assist-appel {
  0%, 100% { box-shadow: var(--shadow-pop), 0 0 0 0 color-mix(in srgb, var(--violet) 45%, transparent); }
  50%      { box-shadow: var(--shadow-pop), 0 0 0 12px color-mix(in srgb, var(--violet) 0%, transparent); }
}
.assist-lanceur { animation: assist-appel 2.2s var(--ease) 3s 3; }
.assist-lanceur:hover { animation: none; }

/* Sur téléphone, le libellé prendrait la moitié de la largeur : on garde la pastille seule,
   remontée au-dessus de la barre d'achat collante pour ne jamais la recouvrir. */
@media (max-width: 620px) {
  .assist-lanceur { padding: 10px; right: 14px; bottom: 14px; }
  .assist-lanceur-texte { display: none; }
}
@media (max-width: 860px) {
  body:has(.barre-achat.est-visible) .assist-lanceur { bottom: calc(76px + env(safe-area-inset-bottom)); }
}

@media (prefers-reduced-motion: reduce) {
  .assist-lanceur { animation: none; }
  .assist-lanceur:hover { transform: none; }
}

/* ---- sélecteur de langue ---------------------------------------------------
   Un drapeau et deux lettres : le drapeau seul est ambigu (lequel pour l'anglais ?) et le
   nom entier prendrait la place de deux icônes dans l'en-tête. Les noms complets
   apparaissent dans la liste, chacun écrit dans sa propre langue — un visiteur allemand
   cherche « Deutsch », pas « Allemand ». */

.langue { position: relative; flex-shrink: 0; }
.langue-bouton {
  display: flex; align-items: center; gap: 6px; height: 42px; padding: 0 10px;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: rgba(22,21,46,.5); color: var(--text-soft); cursor: pointer;
  transition: border-color .16s var(--ease), color .16s var(--ease);
}
.langue-bouton:hover { border-color: var(--violet); color: var(--text); }
.langue-drapeau {
  width: 21px; height: 15px; flex-shrink: 0; display: block; border-radius: 2px;
  overflow: hidden; box-shadow: 0 0 0 1px rgba(0,0,0,.18) inset;
}
.langue-drapeau svg { width: 100%; height: 100%; display: block; }
.langue-code { font-family: var(--font-mono); font-size: .74rem; letter-spacing: .04em; }

.langue-liste {
  position: absolute; top: calc(100% + 8px); right: 0; z-index: 60;
  min-width: 172px; margin: 0; padding: 6px; list-style: none;
  background: var(--pop-bg); border: 1px solid var(--line-strong);
  border-radius: var(--radius); box-shadow: var(--shadow-pop);
}
.langue-liste button {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 9px 10px; border: 0; border-radius: var(--radius-sm);
  background: none; color: var(--text-soft); font-size: .88rem; cursor: pointer; text-align: left;
  transition: background .14s var(--ease), color .14s var(--ease);
}
.langue-liste button:hover { background: var(--surface-2); color: var(--text); }
.langue-liste button[aria-current="true"] { background: var(--gradient); color: var(--on-gradient); }

/* Sous 860 px l'en-tête s'enroule déjà : le sélecteur garde sa place mais se resserre. */
@media (max-width: 860px) {
  .langue-bouton { height: 38px; padding: 0 8px; }
  .langue-code { display: none; }
}

/* ---- favoris ---------------------------------------------------------------
   Le cœur se pose sur la carte produit, au-dessus du lien qui la couvre entièrement.
   Il reste discret tant qu'il n'est pas actif : une grille de vingt cœurs rouges ferait
   passer la liste d'envies avant les produits. */

.fav-btn {
  position: absolute; top: 9px; right: 9px; z-index: 3;
  width: 34px; height: 34px; display: grid; place-items: center;
  border: 1px solid var(--line); border-radius: 50%;
  background: var(--surface); color: var(--muted-dim);
  cursor: pointer;
  transition: color .16s var(--ease), border-color .16s var(--ease), transform .16s var(--ease);
}
.fav-btn:hover { color: var(--violet-bright); border-color: var(--violet); transform: scale(1.08); }
.fav-btn.est-actif { color: var(--danger); border-color: var(--danger); }
.fav-btn.est-actif svg { fill: currentColor; }

/* Comparer : même pastille que le cœur, posée juste en dessous. La couleur active est
   l'indigo des actions et non le rouge des favoris — ce sont deux listes différentes, et
   les confondre d'un coup d'œil ferait croire à un seul bouton à deux états. */
.comp-btn {
  position: absolute; top: 50px; right: 9px; z-index: 3;
  width: 34px; height: 34px; display: grid; place-items: center;
  border: 1px solid var(--line); border-radius: 50%;
  background: var(--surface); color: var(--muted-dim);
  cursor: pointer;
  transition: color .16s var(--ease), border-color .16s var(--ease), transform .16s var(--ease);
}
.comp-btn:hover { color: var(--violet-bright); border-color: var(--violet); transform: scale(1.08); }
.comp-btn.est-actif { color: var(--violet); border-color: var(--violet); background: var(--gradient-soft); }

/* Le compteur de l'en-tête reprend l'aplat du panier, mais dans la couleur des favoris
   pour qu'on distingue les deux pastilles du coin de l'œil. */
.fav-count {
  position: absolute; top: -6px; right: -6px; min-width: 19px; height: 19px; padding: 0 5px;
  background: var(--danger); border-radius: 999px;
  font-family: var(--font-mono); font-size: .66rem; font-weight: 500;
  display: grid; place-items: center; color: var(--on-danger);
  border: 2px solid var(--ink-950);
}

@media (prefers-reduced-motion: reduce) { .fav-btn:hover, .comp-btn:hover { transform: none; } }

/* ========================================================= TÉLÉPHONE, REPRISE

   Ce bloc clôt la feuille : à spécificité égale, la dernière déclaration gagne. Il corrige
   ce que la première passe mobile avait laissé passer, constaté en rendant réellement les
   pages en 390 px de large plutôt qu'en réduisant une fenêtre de bureau. */

@media (max-width: 860px) {
  /* ---- l'en-tête tenait sur trois rangées ----
     Le logo et six boutons ne rentrent pas dans 390 px : la rangée se repliait, l'en-tête
     montait à plus de 300 px et mangeait un tiers de l'écran avant le premier produit.
     Langue, favoris, contact et compte passent dans le menu — où ils figurent déjà — et
     l'en-tête garde l'essentiel : ouvrir le menu, revenir à l'accueil, voir son panier. */
  .header-actions .langue,
  .header-actions .contact-bouton,
  .header-actions #lien-compte,
  .header-actions #lien-comparateur,
  .header-actions #lien-favoris { display: none; }

  .header-top { padding-block: 9px; row-gap: 9px; }
  /* Sans cette marge, les boutons se collaient au logo au lieu de tenir le bord droit :
     la recherche part sur la seconde rangée, il ne reste rien pour pousser les actions. */
  .header-actions { margin-left: auto; }
  .header-search .input { padding-block: 11px; }
  .logo svg { width: 30px; height: 30px; }

  /* Les drapeaux dans le menu : une rangée, pas un menu dans un menu. */
  .langue-mobile { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 6px; }
  .langue-mobile button {
    display: inline-flex; align-items: center; gap: 7px;
    padding: 9px 12px; min-height: 44px;
    border: 1px solid var(--line); border-radius: var(--radius);
    background: var(--surface); color: var(--text-soft);
    font-family: var(--font-mono); font-size: .76rem; cursor: pointer;
  }
  .langue-mobile button[aria-current="true"] {
    background: var(--gradient); color: var(--on-gradient); border-color: transparent;
  }
}

@media (max-width: 620px) {
  /* ---- les lignes du panier ----
     Le bloc quantité + total occupait les deux colonnes, donc il démarrait sous la
     vignette pendant que le nom restait décalé à droite : la ligne paraissait disloquée.
     En le gardant dans la colonne du texte, tout ce qui concerne l'article s'aligne. */
  .cart-line {
    grid-template-columns: 62px minmax(0, 1fr);
    row-gap: var(--s-3); padding-block: var(--s-4);
  }
  .cart-line > :last-child {
    grid-column: 2; display: flex; justify-content: space-between; align-items: center;
    gap: var(--s-3);
  }
  /* Pas de réduction du sélecteur de quantité ici : la section « FINITIONS TÉLÉPHONE »
     l'agrandit volontairement à 46 × 48 px pour le pouce, et une règle plus spécifique
     posée ici le ramenait à 42 px — en dessous du minimum que le projet s'impose. La
     colonne de texte offre environ 280 px à 390 px de large, la largeur suffit. */

  /* « Retirer » faisait 28 px de haut : trop peu pour un doigt, et c'est une action
     qu'on ne veut ni rater ni déclencher par erreur. */
  .cart-line .rm { min-height: 44px; display: inline-flex; align-items: center; gap: 6px; }
  /* Le nom du produit ouvre sa fiche : 19 px de haut, c'était une ligne de texte, pas un
     lien qu'on vise au pouce. */
  .cart-line .name { display: flex; align-items: center; min-height: 44px; }
  /* Le logo ramène à l'accueil ; sa hauteur suivait celle du pictogramme. */
  .logo { min-height: 44px; }

  /* Le récapitulatif est la dernière chose qu'on lit avant de payer : il mérite de se
     détacher du reste plutôt que de couler dans la page. */
  .summary { border: 1px solid var(--line-strong); border-radius: var(--radius-lg); }

  /* ---- l'en-tête de page du catalogue ----
     Fil d'Ariane, titre, bouton de filtres, compteur et tri occupaient cinq rangées : on
     défilait un écran entier avant de voir une pièce. */
  .catalogue-titre { font-size: 1.45rem; }
  .breadcrumb { font-size: .78rem; margin-bottom: 10px !important; }
  /* Filtres et tri sur la même rangée, le compteur en dessous : cinq rangées empilées
     obligeaient à défiler un écran entier avant la première pièce. */
  .catalogue-head {
    margin-top: var(--s-4); margin-bottom: var(--s-4);
    display: grid; grid-template-columns: auto minmax(0, 1fr); gap: var(--s-3) var(--s-3);
    align-items: center;
  }
  .filters-toggle { grid-column: 1; grid-row: 1; }
  .catalogue-tri { grid-column: 2; grid-row: 1; margin-left: 0; min-width: 0; }
  .catalogue-tri .muted { display: none; }
  .catalogue-tri .select { width: 100%; min-width: 0; }
  .catalogue-head .count { grid-column: 1 / -1; grid-row: 2; }

  /* ---- la bulle d'assistance ----
     Elle flotte en bas à droite et recouvrait le sous-total du panier comme le pied de
     page. On la réduit, et on réserve la place qu'elle occupe en bas de chaque page. */
  .assist-lanceur { right: 12px; bottom: 12px; padding: 8px; }
  .assist-lanceur-icone { width: 32px; height: 32px; }
  #outlet { padding-bottom: 68px; }

}

/* Un bouton flottant finit toujours par recouvrir quelque chose : le récapitulatif du
   panier sur téléphone, le menu de choix du modèle du comparateur sur grand écran. Il
   s'efface donc pendant qu'on descend — on lit — et revient dès qu'on remonte, geste qui
   accompagne presque toujours la recherche d'une aide. La règle valait d'abord sous
   620 px ; elle vaut partout, le problème n'est pas une affaire de largeur. */
.assist-lanceur { transition: transform .22s var(--ease), opacity .22s var(--ease); }
.assist-lanceur.est-repliee { transform: translateY(96px); opacity: 0; pointer-events: none; }

@media (prefers-reduced-motion: reduce) {
  .assist-lanceur { transition: none; }
}

/* ======================================================= RÉPARER OU REMPLACER

   La bande qui suit le bandeau. Elle met face à face le prix des réparations courantes du
   modèle choisi et celui du même modèle reconditionné — deux chiffres du catalogue, pas
   une estimation. C'est la question que se pose le visiteur, et la boutique est l'une des
   rares à pouvoir y répondre avec ses propres prix. */

.reparer {
  border-block: 1px solid var(--line);
  padding: clamp(20px, 2.6vw, 30px) 0;
  background:
    radial-gradient(60% 120% at 12% 0%, color-mix(in srgb, var(--violet) 8%, transparent), transparent 70%),
    var(--ink-950);
}
.reparer > * { width: min(100% - 40px, var(--wrap)); margin-inline: auto; }

.reparer-tete {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: var(--s-5); flex-wrap: wrap; margin-bottom: var(--s-5);
}
.reparer-tete h2 { font-size: clamp(1.15rem, 2.1vw, 1.5rem); margin-top: 10px; }
.reparer-choix { display: flex; align-items: center; gap: 10px; }
.reparer-choix .muted { font-size: .85rem; white-space: nowrap; }

/* Les réparations à gauche, le remplacement à droite : la lecture va du moins cher au plus
   cher, et le trait vertical marque le choix à faire. */
.reparer-corps {
  display: grid; grid-template-columns: minmax(0, 1fr) auto;
  gap: var(--s-5); align-items: stretch;
}
.reparer-pieces {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(126px, 1fr)); gap: var(--s-3);
}
.reparer-piece, .reparer-remplacement > a {
  display: flex; flex-direction: column; gap: 5px;
  padding: 14px 16px; border-radius: var(--radius);
  border: 1px solid var(--line); background: var(--surface);
  transition: border-color .16s var(--ease), transform .16s var(--ease);
}
.reparer-piece:hover, .reparer-remplacement > a:hover {
  border-color: var(--violet); transform: translateY(-2px);
}
.reparer-famille { font-size: .78rem; color: var(--muted); }
.reparer-piece b, .reparer-remplacement b {
  font-family: var(--font-display); font-size: 1.12rem; font-weight: 700; letter-spacing: -.02em;
}

.reparer-remplacement {
  display: flex; align-items: stretch;
  padding-left: var(--s-5); border-left: 1px solid var(--line);
}
.reparer-remplacement > a { justify-content: center; min-width: 210px; }
.reparer-remplacement > a b { color: var(--violet-bright); }
.reparer-remplacement > .reparer-famille { align-self: center; max-width: 210px; }

/* Le pied porte deux mentions : ce que dit la comparaison, et quand la commande part.
   Elles se placent côte à côte tant qu'il y a la place, et passent l'une sous l'autre
   sinon — plutôt que de se serrer jusqu'à devenir illisibles. */
.reparer-pied {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px var(--s-5);
  margin-top: var(--s-4); font-size: .88rem; color: var(--text-soft);
}
.reparer-verdict, .reparer-expedition { display: inline-flex; align-items: center; gap: 8px; }
.reparer-verdict svg { color: var(--violet-bright); flex-shrink: 0; }
.reparer-expedition { color: var(--muted); }
.reparer-expedition svg { color: var(--muted-dim); flex-shrink: 0; }

@media (max-width: 860px) {
  /* Le remplacement passe sous les pièces : côte à côte, la colonne de droite écrasait
     les prix de réparation à deux par ligne. */
  .reparer-corps { grid-template-columns: 1fr; gap: var(--s-4); }
  .reparer-remplacement { padding-left: 0; border-left: 0; padding-top: var(--s-4); border-top: 1px solid var(--line); }
  .reparer-remplacement > a { min-width: 0; width: 100%; }
}
@media (max-width: 620px) {
  .reparer-pieces { grid-template-columns: repeat(2, 1fr); }
  .reparer-tete { gap: var(--s-3); }
  .reparer-choix { width: 100%; }
  .reparer-choix .select { flex: 1; min-width: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .reparer-piece:hover, .reparer-remplacement > a:hover { transform: none; }
}

/* ======================================================== OFFRES PROMOTIONNELLES

   Le rouge ne sert qu'à une chose ici : signaler qu'un prix a baissé. C'est la seule
   couleur de la charte qui ne soit ni violette ni bleue, et elle perd tout son pouvoir
   d'accroche si on la met ailleurs. Pas de rouge décoratif, pas de rouge sur un bouton
   qui n'est pas une offre.

   Toutes les valeurs passent par `--promo`, `--on-promo` et `--promo-text`, redéfinis par
   direction artistique : le même code rend un rouge vif sur fond nuit et un rouge sombre
   sur fond clair, chacun mesuré au-dessus du seuil AA. */

/* ---- la pastille de remise, sur la vignette produit */
.badge-remise {
  position: absolute; top: 10px; left: 10px; z-index: 2;
  background: var(--promo); color: var(--on-promo);
  font-family: var(--font-display); font-weight: 800; font-size: .82rem;
  letter-spacing: -.01em; line-height: 1;
  padding: 6px 9px; border-radius: var(--radius-sm);
  box-shadow: 0 2px 10px -2px var(--promo-line);
}

/* ---- le prix habituel, barré, à côté du prix remisé */
.prix-groupe { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.prix-groupe .price { color: var(--promo-text); }
.prix-avant {
  font-family: var(--font-display); font-weight: 500;
  color: var(--muted); text-decoration: line-through;
  text-decoration-thickness: 1.5px; font-size: .88em;
}

/* Les documents d'une commande, sous son total : c'est là qu'on les cherche — « la facture
   de ma commande » — et non dans une liste de numéros qu'on ne connaît pas. */
.commande-docs {
  display: flex; flex-wrap: wrap; gap: 8px 16px;
  margin-top: 10px; padding-top: 10px;
  border-top: 1px solid var(--line);
}
.commande-docs a {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: .82rem; font-weight: 600; color: var(--text-soft);
}
.commande-docs a:hover { color: var(--violet-bright); }

/* ---- la boîte de dialogue du devis

   Un `<dialog>` natif plutôt qu'un panneau maison : le navigateur gère seul le piège de
   focus, la fermeture à Échap, l'inertie du reste de la page et le rôle ARIA. Trois sources
   de bogues en moins, et le comportement est celui que la plateforme a déjà testé.

   `::backdrop` n'hérite d'aucune variable du document — c'est une pseudo-élément hors de
   l'arbre —, d'où une valeur littérale ici, et seulement ici. */
.modal {
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-lg);
  background: var(--surface);
  color: var(--text);
  padding: 0;
  width: min(440px, calc(100vw - 32px));
  box-shadow: 0 32px 64px -24px rgba(0, 0, 0, .45);
}
.modal::backdrop { background: rgba(8, 8, 20, .55); }
.modal-corps { padding: clamp(20px, 3vw, 28px); }
.modal .field + .field { margin-top: 14px; }

/* ---- le prix à l'unité des conditionnements par lot

   Une ligne discrète sous le prix : l'information vaut d'être lue, mais ce n'est pas elle
   qu'on facture, et elle ne doit pas entrer en concurrence avec le prix. En encre
   secondaire (`--text-soft`) plutôt qu'en `--muted` : `--muted` est réservé aux mentions
   qu'on peut ne pas lire, et ici l'argument est précisément qu'il faut la lire. Les deux
   jetons sont redéfinis dans chaque charte — aucune couleur en dur, c'est le défaut le plus
   fréquent de ce projet : invisible sur « Atelier », illisible sur « Studio ». */
.prix-unitaire {
  display: block;
  margin-top: 3px;
  font-size: .76rem;
  font-weight: 500;
  letter-spacing: -.005em;
  color: var(--text-soft);
}

/* ---- le bandeau d'annonce, tout en haut de la page */
.bandeau-offre {
  background: var(--promo); color: var(--on-promo);
  position: relative; z-index: 60;
}
.bandeau-offre-inner {
  max-width: var(--wrap, 1280px); margin-inline: auto;
  padding: 9px clamp(16px, 4vw, 28px);
  display: flex; align-items: center; justify-content: center; gap: 10px 18px;
  flex-wrap: wrap; text-align: center;
  font-size: .86rem; font-weight: 600; letter-spacing: -.005em;
}
.bandeau-offre a {
  color: inherit; text-decoration: underline; text-underline-offset: 3px;
  text-decoration-thickness: 1.5px; font-weight: 700;
}
.bandeau-offre a:hover { text-decoration-thickness: 2.5px; }
.bandeau-offre svg { flex-shrink: 0; opacity: .9; }
/* La mention d'expédition perd un cran d'importance : c'est un appui, pas l'offre. */
.bandeau-offre .appui { display: inline-flex; align-items: center; gap: 6px; font-weight: 500; opacity: .92; }
.bandeau-offre-fermer {
  position: absolute; right: 10px; top: 50%; transform: translateY(-50%);
  background: none; border: 0; color: inherit; cursor: pointer; opacity: .7;
  display: grid; place-items: center; width: 30px; height: 30px; border-radius: 8px;
}
/* Le voile de survol se dérive de l'encre du bandeau : un `rgba(0,0,0,…)` en dur y serait
   la seule couleur non tokenisée du bloc, et n'aurait aucun sens sur une charte à encre
   claire. */
.bandeau-offre-fermer:hover {
  opacity: 1;
  background: color-mix(in srgb, var(--on-promo) 14%, transparent);
}
@media (max-width: 720px) {
  .bandeau-offre-inner { padding-right: 44px; font-size: .8rem; gap: 6px 12px; }
}
/* Sur téléphone la barre doit tenir sur une ligne : au-dessus de la ligne de flottaison,
   chaque ligne supplémentaire repousse le produit. Le nombre de références est le détail
   le moins utile de la phrase — c'est lui qui part en premier. */
@media (max-width: 560px) {
  .bandeau-offre .detail { display: none; }
  .bandeau-offre .appui { display: none; }
  .bandeau-offre-inner { padding-block: 8px; }
}

/* ---- l'encart sur la fiche produit */
.offre-encart {
  display: flex; align-items: center; gap: 12px;
  background: var(--promo-soft); border: 1px solid var(--promo-line);
  border-radius: var(--radius); padding: 12px 14px;
  color: var(--promo-text); font-size: .88rem; font-weight: 600;
}
.offre-encart svg { flex-shrink: 0; }
.offre-encart .detail { display: block; font-weight: 500; color: var(--text-soft); font-size: .82rem; margin-top: 2px; }

/* ---- l'économie réalisée, sous le total du panier.

   Elle se place APRÈS le total et sans signe « moins » : posée entre le sous-total et la
   livraison, avec un « − 264,00 € », elle se lisait comme une soustraction à faire — alors
   que le sous-total est déjà remisé et que l'opération ne tombait pas juste. C'est une
   information sur ce que la commande a fait gagner, pas une ligne de calcul. */
.summary-economie {
  display: flex; align-items: center; gap: 7px; margin: 10px 0 0;
  background: var(--promo-soft); border: 1px solid var(--promo-line);
  border-radius: var(--radius-sm); padding: 9px 11px;
  color: var(--promo-text); font-size: .84rem; font-weight: 500;
}
.summary-economie b { font-weight: 700; }
.summary-economie svg { flex-shrink: 0; }

/* ---- la puce de filtre « en promotion » du catalogue

   Sélecteur volontairement porté par `.filters` : `themes.css` restyle `.chip[aria-pressed]`
   avec la même spécificité (0,3,0) et, chargé après, il gagnait — la puce active affichait
   du violet sur du rouge. Un cran de spécificité de plus la met hors d'atteinte du thème. */
.filters .chip.est-promo[aria-pressed="true"],
.filters .chip.est-promo.is-active {
  background: var(--promo);
  border-color: transparent;
  color: var(--on-promo);
}
.filters .chip.est-promo[aria-pressed="true"] svg,
.filters .chip.est-promo.is-active svg { color: inherit; }

/* Le bouton qui ouvre la recherche sur téléphone : absent du grand écran, où le champ est
   dans l'en-tête. Déclaré ici plutôt que dans le bloc mobile pour que la valeur par défaut
   soit « caché » — une règle oubliée doit pécher par absence, pas par apparition. */
.search-trigger { display: none; }

/* ==================================================== LA VERSION TÉLÉPHONE

   Le constat qui a déclenché cette refonte : la page d'accueil mesurait **10 310 px** sur un
   iPhone, soit vingt-six écrans à faire défiler, pour un en-tête permanent de 119 px et un
   corps de texte à 14,5 px. Deux sites nous servent de repère, mesurés et non devinés :

   | | Apple (page iPhone) | Utopya | Nous, avant |
   |---|---|---|---|
   | En-tête | 48 px, une ligne | 42 px | **119 px, deux lignes** |
   | Corps | 17 px | 14 px | 14,5 px |
   | Rails horizontaux | 5 | 0 | 0 |
   | Hauteur de page | 10 978 px, pleine d'images | **2 133 px** | 10 310 px, pleine de vide |

   Apple est long mais chaque écran montre quelque chose ; Utopya est court et dense. Notre
   page était longue **et** vide : toutes les grilles du grand écran s'empilaient en une
   colonne unique. Une grille de marques sur trois colonnes devenait 452 px, une grille de
   pannes devenait 681 px en une seule colonne, chaque bloc d'articles 950 px.

   Les quatre décisions, par ordre de gain :

     1. **Ce qui est une grille sur grand écran devient un rail horizontal sur téléphone.**
        C'est le geste d'Apple, et il n'enlève rien : on voit toujours le premier élément, on
        devine le suivant au débord, et le pouce balaie au lieu de défiler sur six écrans.
     2. **L'en-tête tient sur une ligne.** La recherche quitte sa deuxième ligne pour devenir
        un bouton qui ouvre le panneau où elle vit déjà.
     3. **Le corps remonte à 16 px.** En dessous, on plisse les yeux ; c'est le changement le
        plus visible et le moins coûteux.
     4. **Le pied de page devient des accordéons.** Dix-sept liens en quatre colonnes
        empilées faisaient 1 419 px, soit un écran et demi de pied de page. */

@media (max-width: 860px) {
  /* ---- 1. L'en-tête sur une seule ligne.

     La recherche était posée en `flex-basis: 100%` sur une deuxième ligne, et doublait la
     hauteur de l'en-tête — qui est collant, donc présent tout le temps. Elle existe déjà
     dans le panneau mobile : un bouton l'y emmène, le champ y est mis au point
     automatiquement, et on récupère 60 px sur chaque écran. */
  .header-search { display: none; }
  .header-top { flex-wrap: nowrap; height: 56px; padding-block: 0; }
  .search-trigger { display: grid; }

  /* Deux icônes de moins : les favoris et le compte vivent dans le menu, où ils ne coûtent
     rien. L'en-tête garde ce qu'on touche en marchant — chercher, le panier, le menu. */
  .header-actions #lien-favoris,
  .header-actions #lien-compte,
  .header-actions #lien-comparateur,
  .header-actions .contact-bouton,
  .header-actions .langue { display: none; }
  .header-actions { gap: 4px; }

  /* ---- 2. L'échelle typographique.

     Les titres de section étaient dimensionnés pour un écran large : « Quelle est la marque
     de votre téléphone ? » prenait deux lignes à 34 px, et son chapô quatre lignes. Le bloc
     de titre pesait 218 px à lui seul, cinq fois dans la page. */
  .section-head h2,
  .section > .wrap > h2 { font-size: clamp(1.45rem, 6.2vw, 1.72rem); line-height: 1.18; }
  .section-head { gap: 10px; }
  .lede { font-size: .95rem; line-height: 1.5; }

  /* ---- 3. Les grilles deviennent des rails.

     Un rail au doigt : défilement horizontal, arrêt sur chaque élément, et une marge à
     droite pour que le dernier ne colle pas au bord. Le débord du suivant n'est pas
     décoratif — c'est ce qui dit qu'il y a autre chose à voir. */
  .brand-grid,
  .panne-grid,
  .blog-grille {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: 74%;
    grid-template-columns: none;
    gap: 12px;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scroll-snap-type: x mandatory;
    /* Le rail sort du `wrap` pour aller chercher les bords de l'écran : un rail qui s'arrête
       à 16 px du bord ne donne pas envie d'être balayé. */
    margin-inline: calc(var(--s-4) * -1);
    padding-inline: var(--s-4);
    padding-bottom: 6px;
    scrollbar-width: none;
  }
  .brand-grid::-webkit-scrollbar,
  .panne-grid::-webkit-scrollbar,
  .blog-grille::-webkit-scrollbar { display: none; }
  .brand-grid > *,
  .panne-grid > *,
  .blog-grille > * { scroll-snap-align: start; }
  /* Les marques sont des pastilles courtes : on en met trois par écran, pas une. */
  .brand-grid { grid-auto-columns: 38%; }
  .panne-grid { grid-auto-columns: 66%; }

  /* ---- 4. Le pied de page en accordéons. */
  .pied-bloc > summary {
    display: flex; align-items: center; justify-content: space-between;
    padding-block: 14px; cursor: pointer; list-style: none;
    font-family: var(--font-display); font-weight: 600; font-size: .95rem;
    border-top: 1px solid var(--line);
  }
  .pied-bloc > summary::-webkit-details-marker { display: none; }
  .pied-bloc > summary::after {
    content: ''; width: 9px; height: 9px; margin-right: 3px;
    border-right: 2px solid var(--muted); border-bottom: 2px solid var(--muted);
    transform: rotate(45deg) translate(-2px, -2px);
    transition: transform .2s var(--ease);
  }
  .pied-bloc[open] > summary::after { transform: rotate(-135deg) translate(-2px, -2px); }
  .pied-bloc > ul { padding-bottom: 14px; }
  .footer-grid { gap: 0; }
}

@media (max-width: 620px) {
  /* 16 px et non 14,5 : en dessous, un texte courant se lit en plissant les yeux. Apple est
     à 17 px sur la même page. C'est le changement le plus visible de cette refonte, et il ne
     coûte que de la place qu'on vient de récupérer ailleurs. */
  body { font-size: 16px; }

  /* Les sections respiraient à la mesure d'un grand écran : douze fois 72 px de marge haute
     et basse font deux écrans de blanc à eux seuls. */
  .section { padding-block: clamp(32px, 7vw, 52px); }

  /* Deux colonnes de produits, comme Utopya : à une seule, le catalogue devient infini ;
     à trois, on ne lit plus ni le nom ni le prix. */
  .product-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }

  /* Toute cible se touche au pouce. Le doigt moyen couvre 45 px ; en dessous on vise. */
  .btn, .icon-btn, .chip, .qty button, .fav-btn, .comp-btn, .add-btn { min-height: 44px; }
  .fav-btn, .comp-btn, .add-btn { min-width: 44px; }
  .qty button { min-width: 44px; }
}

/* La barre d'achat collante portait un fond noir écrit en dur : sur la charte claire, une
   barre sombre apparaissait en bas d'une page blanche. Elle suit maintenant la surface. */
@media (max-width: 860px) {
  /* ---- 5. Les blocs de titre.

     Ils se répètent douze fois dans la page d'accueil : à 165-210 px pièce, c'est deux
     écrans entiers de titres. Trois économies, dans l'ordre d'évidence :

       — **Les flèches du rail disparaissent.** On balaie du doigt ; deux boutons de 44 px
         empilés sous le titre servaient uniquement à faire ce que le pouce fait mieux.
       — **« Voir tout » remonte à côté du titre**, où il était de toute façon sur grand
         écran, au lieu de prendre une ligne à lui seul.
       — **Les écarts se resserrent** : 24 px entre un titre et son chapô sont justes sur
         une colonne de 1 200 px, excessifs sur 360. */
  /* `nowrap` : le bloc de titre et « Voir tout » tiennent sur la même rangée. En `wrap`, le
     bloc de titre prenait toute la largeur et le lien tombait seul sur une ligne de 50 px —
     huit fois dans la page. `min-width: 0` sur le bloc de titre est indispensable : sans
     lui, un élément flex refuse de rétrécir sous la largeur de son contenu, et le titre
     poussait le lien hors de l'écran au lieu de se replier. */
  .rail-head { gap: 10px 14px; align-items: baseline; margin-bottom: var(--s-4); flex-wrap: nowrap; }
  .rail-head > div:first-child { min-width: 0; flex: 1; }
  .rail-tools { flex-shrink: 0; }
  /* Les flèches disparaissent entièrement, conteneur compris : laisser le conteneur vide
     gardait sa ligne de 44 px, c'est-à-dire tout ce qu'on cherchait à économiser. */
  .rail-arrows { display: none; }
  /* Et « Voir tout » cesse de prendre une ligne pour lui seul : il revient à côté du titre,
     où il est sur grand écran. La règle à 620 px qui lui donnait toute la largeur est
     neutralisée ici, pas supprimée — elle sert encore à la tablette. */
  .rail-tools { gap: 8px; width: auto; }
  .rail-sub { font-size: .9rem; }
  .section-head { gap: 12px; }
  .section-head .lede, .rail-sub {
    /* Deux lignes suffisent à situer une section ; au-delà, personne ne lit et tout le monde
       fait défiler. Le texte reste entier dans le balisage, pour les moteurs et les lecteurs
       d'écran : seule son étendue visible est bornée. */
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
    overflow: hidden;
  }

  /* ---- 6. Les trois étapes deviennent un rail, comme le reste.
     Trois cartes empilées faisaient 416 px pour trois phrases. */
  .steps {
    display: grid; grid-auto-flow: column; grid-auto-columns: 78%;
    gap: 12px; overflow-x: auto; scroll-snap-type: x mandatory;
    margin-inline: calc(var(--s-4) * -1); padding-inline: var(--s-4);
    scrollbar-width: none;
  }
  .steps::-webkit-scrollbar { display: none; }
  .steps > * { scroll-snap-align: start; }
  /* Le bloc de texte qui accompagne les étapes passe sous elles plutôt qu'à côté. */
  .split { gap: var(--s-4); }
}

@media (max-width: 620px) {
  /* ---- 7. La carte produit se resserre.

     À deux colonnes, la carte faisait encore 320 px de haut : deux articles par écran, soit
     un catalogue de 17 000 références qu'on parcourt à la cuillère. Utopya en montre quatre
     sur la même surface, et c'est le bon chiffre — assez pour comparer, assez grand pour
     lire le nom et le prix.

     Le visuel passe du 4/3 au carré : une pièce détachée est photographiée de face sur fond
     blanc, elle ne perd rien au recadrage, et on récupère un quart de la hauteur. */
  .product-media { aspect-ratio: 1 / 1; padding: 6px; }
  .product-body { padding: 10px 11px 12px; gap: 5px; }
  .product-body .fam { font-size: .6rem; }
  .product-body .name { font-size: .88rem; line-height: 1.3; }
  .product-foot .price { font-size: 1rem; }
  /* Le bouton d'ajout garde ses 44 px : c'est la cible, on ne la rogne pas. */
  .add-btn { width: 44px; height: 44px; }
  .fav-btn { width: 40px; height: 40px; top: 8px; right: 8px; }
  .comp-btn { width: 40px; height: 40px; top: 54px; right: 8px; }
  .product-card .rail-badge { font-size: .56rem; padding: 4px 8px 4px 6px; top: 8px; left: 8px; }

  /* ---- 8. L'en-tête de catalogue.
     Fil d'Ariane, titre, filtres, compteur : 500 px avant le premier produit. */
  .catalogue-head h1 { font-size: clamp(1.5rem, 6.5vw, 1.9rem); }
  .breadcrumb { font-size: .74rem; }
  .catalogue-count { font-size: .76rem; }
}

@media (max-width: 620px) {
  /* ---- 9. Les quatre dernières cibles trop petites.

     Quarante-cinq pixels, c'est la largeur moyenne d'une pulpe de pouce. En dessous, on ne
     touche plus : on vise, et on rate une fois sur cinq. Les quatre coupables restants se
     corrigent sans rien changer à ce qu'on voit.

     Les règles qui suivent sont postérieures à celles qu'elles corrigent dans le fichier :
     c'est délibéré, à spécificité égale la dernière gagne, et les dupliquer plus haut
     obligerait à les tenir à deux endroits. */

  /* Deux boutons de carte, à 42 px : il manquait deux pixels de chaque côté. */
  .product-card .fav-btn,
  .product-card .add-btn { width: 44px; height: 44px; }

  /* Les puces du bandeau font 34 × 3 px, et c'est bien ainsi : ce sont des traits, pas des
     boutons. On agrandit la zone touchable autour sans toucher au trait — le remède exact
     pour une cible visuellement fine. */
  .bandeau-puces { gap: 2px; }
  .bandeau-puces button { padding: 21px 6px; }

  /* La croix du bandeau d'annonce ferme quelque chose : la rater, c'est cliquer sur l'offre
     qu'on voulait écarter. */
  .bandeau-offre-fermer { width: 44px; height: 44px; }
}

@media (max-width: 620px) {
  /* ---- 10. Le prix remisé sur une carte étroite.

     « 160,90 € » suivi de « 201,90 € » barré fait 131 px, et il reste 158 px dans la carte
     une fois le bouton d'ajout posé : le prix barré passait donc à la ligne tout seul, et
     le bouton se retrouvait à flotter en face de lui. Rien ne se chevauchait — mesuré —
     mais cela se lisait comme un défaut, ce qui revient au même.

     On empile donc délibérément : prix d'abord, prix barré dessous, bouton centré en face
     des deux. Un retour à la ligne voulu se lit comme une mise en page ; un retour à la
     ligne subi se lit comme un bogue. */
  .product-card .prix-groupe { flex-direction: column; align-items: flex-start; gap: 2px; }
  .product-card .product-foot { align-items: center; }
  .product-card .prix-avant { font-size: .8rem; }
}

/* ======================================================= LE MENU TÉLÉPHONE

   Le bas du menu affichait « ProtectionsAccessoiresReconditionnés Guides Actualités ». La
   feuille de style ne visait que `.mobile-menu > a` — les enfants **directs** du panneau —
   et ces liens-là vivent dans un conteneur imbriqué, rempli après coup par le script de
   navigation : ils retombaient en texte courant, collés les uns aux autres.

   Le remède n'est pas d'élargir le sélecteur, c'est de nommer la chose. Une rangée de menu
   est une rangée de menu où qu'elle soit posée, et elle a désormais sa classe. Un sélecteur
   qui dépend de l'endroit où un élément se trouve se casse au premier déplacement.

   Le reste de ce bloc rend le panneau à ce qu'il doit être sur un téléphone : la navigation
   principale. Trois règles, empruntées aux réglages d'iOS parce qu'elles y ont fait leurs
   preuves — des groupes séparés par un titre, une rangée par destination avec son chevron,
   et des traits qui ne touchent pas le bord gauche pour que l'œil suive la colonne. */

.menu-groupe {
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
  overflow: hidden;
}

.menu-rangee {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  min-height: 52px; padding: 0 14px;
  font-size: .97rem; font-weight: 500; color: var(--text);
  transition: background .14s var(--ease);
}
/* Le trait démarre après le texte, pas au bord de la boîte : c'est ce décalage qui fait
   lire une liste comme une liste, et non comme des cases empilées. */
.menu-rangee + .menu-rangee { box-shadow: inset 0 1px 0 var(--line); }
.menu-rangee:active { background: var(--surface-2); }
.menu-chevron { color: var(--muted-dim); flex-shrink: 0; transform: rotate(-90deg); }

/* Les groupes de marques reprennent le même cadre : le menu n'a qu'une grammaire. */
.menu-groupe .mobile-brand { border-bottom: 1px solid var(--line); }
.menu-groupe .mobile-brand:last-child { border-bottom: 0; }
.menu-groupe .mobile-brand > summary { padding: 15px 14px; min-height: 52px; }
.menu-groupe .mobile-brand > summary::after { margin-right: 0; }
.menu-groupe .mobile-serie { padding: 4px 14px 12px; }

/* ---- les trois tuiles d'en-tête */
.menu-tuiles {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px;
  margin-bottom: var(--s-5);
}
.menu-tuile {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 7px;
  min-height: 76px; padding: 10px 6px;
  border: 1px solid var(--line); border-radius: var(--radius-lg);
  background: var(--surface); color: var(--text);
  font-size: .78rem; font-weight: 600; text-align: center;
  cursor: pointer; transition: border-color .14s var(--ease), background .14s var(--ease);
}
.menu-tuile svg { color: var(--violet-bright); }
.menu-tuile:active { background: var(--surface-2); border-color: var(--violet); }

/* Le titre de section respire au-dessus de son groupe, et pas en dessous. */
.mobile-section-title { margin: var(--s-5) 0 10px; }
.mobile-menu .langue-mobile { margin-bottom: var(--s-6); }

@media (max-width: 860px) {
  /* ---- 11. Les quatre derniers défauts relevés en recette.

     Trois tiennent à la même cause : un élément dont la zone touchable suit son contenu au
     lieu de suivre sa ligne. Un lien court dans une liste de liens n'offre que la largeur de
     son mot — « CGV » faisait 29 px de large dans une rangée de 44 px de haut, et il fallait
     viser le mot. Les rangées se touchent, pas les mots. */
  .pied-bloc ul a,
  .footer-base a { display: block; }

  /* La pagination du catalogue était restée à 40 px : c'est le bouton qu'on touche le plus
     souvent sur un catalogue de dix-sept mille références. */
  .page-btn { min-height: 44px; min-width: 44px; }

  /* Le défilement au clavier s'arrêtait 87 px trop tôt sur le rail des pannes : le
     navigateur amène l'élément focalisé juste dans le cadre, sans tenir compte du
     dépassement que le rail s'autorise sur les bords de l'écran. On lui donne cette marge
     explicitement, et la carte focalisée arrive entière. */
  .brand-grid, .panne-grid, .blog-grille, .steps, .rail {
    scroll-padding-inline: var(--s-4);
  }

  /* La barre d'achat collante recouvrait la ligne de copyright, en butée de défilement :
     rien d'interactif, mais un texte qu'on ne pouvait plus dégager. Le pied de page se
     réserve la hauteur de la barre. */
  .footer { padding-bottom: 76px; }
}

/* ================================================== SUGGESTIONS DE PANIER

   Elles sont sous le panier et non à côté : à côté, elles entrent en concurrence avec le
   bouton de commande, qui reste l'action principale. Dessous, elles se lisent après la
   décision d'acheter, au moment où l'on se demande s'il ne manque rien.

   Chaque carte met la **raison** au-dessus du nom. C'est contraire à l'habitude — le nom
   d'abord, l'argument ensuite — et c'est délibéré : le nom d'une pièce détachée ne dit rien
   à qui ne connaît pas la référence, alors que « pendant que le téléphone est ouvert »
   s'adresse à tout le monde. */

.suggestions { margin-top: var(--s-7); }
.suggestions-tete {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 12px 20px; flex-wrap: wrap; margin-bottom: var(--s-4);
}
.suggestions-tete h2 { font-family: var(--font-display); font-size: 1.15rem; font-weight: 600; }
.suggestions-franco {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: .85rem; color: var(--text-soft);
}
.suggestions-franco svg { color: var(--violet-bright); flex-shrink: 0; }
.suggestions-franco b { color: var(--text); }

.suggestions-liste { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }

.suggestion {
  display: flex; gap: 12px; align-items: stretch;
  border: 1px solid var(--line); border-radius: var(--radius-lg);
  background: var(--surface); padding: 10px;
  transition: border-color .16s var(--ease);
}
.suggestion:hover { border-color: var(--line-strong); }
.suggestion-visuel {
  position: relative; flex: 0 0 72px; height: 72px;
  border-radius: var(--radius); overflow: hidden; background: var(--media-bg);
  display: grid; place-items: center;
}
.suggestion-corps { display: flex; flex-direction: column; gap: 3px; min-width: 0; flex: 1; }
.suggestion-raison {
  font-size: .72rem; font-weight: 600; letter-spacing: -.005em;
  color: var(--violet-bright);
}
.suggestion-nom {
  font-size: .86rem; line-height: 1.3; color: var(--text);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.suggestion-nom:hover { text-decoration: underline; text-underline-offset: 2px; }
.suggestion-pied {
  margin-top: auto; padding-top: 6px;
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
}
.suggestion-pied .price { font-size: 1rem; }

@media (max-width: 860px) {
  /* Une colonne : à deux, la carte tombe sous 170 px et le nom se réduit à deux mots. */
  .suggestions-liste { grid-template-columns: minmax(0, 1fr); }
  .suggestion-visuel { flex-basis: 64px; height: 64px; }
}

/* Le panier qui se met à jour sans rechargement.

   Deux signes, et pas un de plus. Un panier qui s'anime à chaque clic fatigue ; un panier
   qui ne montre rien laisse croire que le clic n'a pas pris.

   `aria-busy` est posé pendant l'aller-retour vers le serveur : le temps est de l'ordre de
   quelques dizaines de millisecondes en local, mais il peut atteindre deux ou trois cents
   sur un téléphone en 4G, et c'est là qu'un montant figé inquiète. On ne grise que les
   chiffres — les lignes et les boutons restent nets et cliquables, parce que rien n'empêche
   d'enchaîner un second clic. La transition est volontairement plus lente à l'extinction
   qu'à l'allumage : une mise à jour rapide ne doit pas produire de clignotement. */
.cart-layout[aria-busy="true"] [data-montants],
.cart-layout[aria-busy="true"] .ligne-total {
  opacity: .45;
  transition: opacity .12s ease-out;
}
.cart-layout [data-montants],
.cart-layout .ligne-total { transition: opacity .3s ease-in; }

/* L'article ajouté depuis les suggestions arrive en haut des lignes, alors que le clic a eu
   lieu en bas de page : sans ce halo, on ne voit rien bouger. Il s'efface de lui-même. */
@keyframes ligne-neuve {
  from { background: color-mix(in srgb, var(--violet) 16%, transparent); }
  to   { background: transparent; }
}
.cart-line.est-neuve { animation: ligne-neuve 1.6s ease-out 1; }

@media (prefers-reduced-motion: reduce) {
  .cart-line.est-neuve { animation: none; }
  .cart-layout [data-montants],
  .cart-layout .ligne-total { transition: none; }
}

/* LA FICHE PRODUIT : POINTS, DESCRIPTION, PRATIQUE, QUESTIONS

   Quatre blocs de nature différente, et quatre traitements. Les points se parcourent — courts,
   alignés, coche verte, et ils restent près du prix. La description se lit — mesure de ligne
   contenue, interligne confortable, c'est le seul endroit du site où l'on demande de lire
   plusieurs phrases d'affilée. Les encarts se consultent — pose et caractéristiques, posés sur
   une surface distincte parce qu'on y revient sans relire le reste. Les questions se
   survolent — deux colonnes, titres courts, on s'arrête sur celle qui nous retenait.

   Le tout occupe la pleine largeur, sous le bloc d'achat. Dans la colonne du prix, la
   description faisait 584 px de large et commençait après un écran de défilement : le gérant
   l'a résumée par « y'a pas de description », et c'était exact du point de vue qui compte,
   le sien. */
/* L'accroche sous le titre : la première phrase de la description, pour que le premier écran
   dise enfin quelque chose de la pièce. Un ton au-dessus du gris des libellés secondaires —
   on veut qu'elle se lise, pas qu'elle meuble. */
.product-accroche {
  font-size: .95rem; line-height: 1.6; color: var(--muted);
  max-width: 62ch; margin-top: 2px;
}

.fiche { padding-block: var(--s-7) 0; border-top: 1px solid var(--line); margin-top: var(--s-7); }

/* Le texte à gauche, le pratique à droite. La colonne de droite est plus étroite : elle porte
   des listes, pas de la prose, et elle doit laisser à la lecture la largeur dont elle a
   besoin. */
.fiche-corps {
  display: grid; grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr);
  gap: clamp(24px, 3.5vw, 56px); align-items: start;
}
.fiche-texte { min-width: 0; }
.fiche-cote { display: grid; gap: var(--s-4); min-width: 0; }

/* Les encarts : une surface à peine distincte, pas une carte de plus. Le site en a déjà
   beaucoup, et empiler les cadres finit par ne plus rien hiérarchiser. */
.fiche-encart {
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: var(--radius-lg); padding: var(--s-4) var(--s-5) var(--s-5);
}
.fiche-encart h3 {
  font-family: var(--font-display); font-size: .98rem; font-weight: 600;
  margin-bottom: 12px;
}
.fiche-encart .hint { margin-top: 10px; font-size: .76rem; }
.fiche-encart .spec-table { font-size: .85rem; }
.fiche-encart .spec-table th, .fiche-encart .spec-table td { padding: 9px 0; }

.pose-liste { display: grid; gap: 10px; margin: 0; }
.pose-ligne { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.25fr); gap: 12px; align-items: start; }
.pose-ligne dt { font-size: .82rem; color: var(--muted); }
.pose-ligne dd { margin: 0; font-size: .86rem; color: var(--text); font-weight: 500; }

/* Les questions. Deux colonnes sur grand écran : sept questions en une seule colonne font une
   page qu'on ne finit pas, et c'est justement celle qu'on voulait faire lire. */
.fiche-faq { margin-top: var(--s-7); }
.fiche-faq > h2 {
  font-family: var(--font-display); font-size: clamp(1.15rem, 2.1vw, 1.45rem);
  margin-bottom: var(--s-4);
}
.faq-grille { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: var(--s-4) clamp(24px, 3.5vw, 48px); }
.faq-item h3 {
  font-size: .94rem; font-weight: 600; color: var(--text); line-height: 1.4;
  margin-bottom: 7px;
}
.faq-item p { font-size: .88rem; line-height: 1.62; color: var(--muted); margin: 0; }

@media (max-width: 860px) {
  .fiche-corps { grid-template-columns: minmax(0, 1fr); }
  .faq-grille { grid-template-columns: minmax(0, 1fr); gap: var(--s-5); }
  .fiche { padding-top: var(--s-6); margin-top: var(--s-6); }
}

/* Les points de lecture rapide, restés près du prix. */
.fiche-points {
  list-style: none; margin: var(--s-5) 0 0; padding: 0;
  display: grid; gap: 8px;
}
.fiche-points li {
  display: flex; align-items: flex-start; gap: 9px;
  font-size: .89rem; color: var(--text);
}
.fiche-points svg { color: var(--success); flex-shrink: 0; margin-top: 2px; }

.fiche-bloc { margin-bottom: var(--s-5); }
.fiche-bloc:last-child { margin-bottom: 0; }
.fiche-bloc h2 {
  font-family: var(--font-display); font-size: 1.08rem; font-weight: 600;
  margin-bottom: 10px;
}
/* Une mesure de ligne autour de 70 signes : au-delà, l'œil perd la ligne suivante en
   revenant à la marge, et c'est le défaut le plus courant des descriptions de boutique. */
.fiche-bloc p {
  font-size: .93rem; line-height: 1.68; color: var(--muted);
  max-width: 70ch; margin-bottom: 12px;
}
.fiche-bloc p:last-child { margin-bottom: 0; }

@media (max-width: 860px) {
  .fiche-bloc p { font-size: .95rem; line-height: 1.62; }
  .fiche-points li { font-size: .92rem; }
}

/* LE CODE PROMO

   Replié par défaut : un champ ouvert sous chaque panier annonce une remise à qui n'en a pas
   et envoie chercher un code ailleurs. La ligne qui l'ouvre reste discrète — c'est un service
   rendu à celui qui a un code, pas un argument de vente.

   Le bloc se distingue du récapitulatif par un filet au-dessus plutôt que par un cadre : à
   l'intérieur d'une carte déjà bordée, un second cadre fait empilement. */
.code-promo {
  margin-top: var(--s-4);
  padding-top: var(--s-4);
  border-top: 1px solid var(--line);
}
.code-promo [hidden] { display: none !important; }

.code-promo-saisie > summary {
  display: flex; align-items: center; gap: 8px;
  font-size: .84rem; color: var(--muted);
  cursor: pointer; list-style: none;
  padding-block: 4px;
  min-height: 32px;
}
.code-promo-saisie > summary::-webkit-details-marker { display: none; }
.code-promo-saisie > summary:hover { color: var(--text); }
.code-promo-saisie > summary svg { color: var(--violet-bright); flex-shrink: 0; }

.code-promo-saisie form {
  display: flex; gap: 8px; margin-top: 10px;
}
/* Les codes se lisent en majuscules et ne se coupent pas : l'espacement les rend plus
   faciles à relire quand on les recopie depuis un e-mail. */
.code-promo-saisie input {
  flex: 1; min-width: 0;
  text-transform: uppercase;
  letter-spacing: .04em;
  font-family: var(--font-mono, inherit);
}
/* « Appliquer » reste un bouton secondaire : il est posé juste au-dessus de « Passer
   commande », et deux boutons pleins l'un sur l'autre se disputent le regard. Un seul geste
   principal par écran — celui qui fait avancer la vente. */
.code-promo-saisie button { flex-shrink: 0; }

.code-promo-refus {
  margin-top: 8px;
  font-size: .8rem; line-height: 1.35;
  color: var(--danger);
}

/* Le code accepté : un seul coup d'œil doit suffire à voir qu'il a pris. Le retrait reste
   possible et discret — on retire surtout pour en essayer un autre. */
.code-promo-actif {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  font-size: .84rem; color: var(--text);
}
.code-promo-actif svg { color: var(--success); flex-shrink: 0; }
.code-promo-actif b { letter-spacing: .04em; }
.code-promo-retirer {
  margin-left: auto;
  background: none; border: 0; padding: 4px 0;
  color: var(--muted-dim); font-size: .78rem; cursor: pointer;
  text-decoration: underline; text-underline-offset: 2px;
}
.code-promo-retirer:hover { color: var(--danger); }

/* La remise dans le récapitulatif : en vert comme les économies, parce que c'est de l'argent
   qui reste au client, et non une dépense de plus. */
.summary-row.est-remise { color: var(--success); font-weight: 500; }
.summary-row.est-remise b { letter-spacing: .03em; }

@media (max-width: 860px) {
  /* Les cibles tactiles passent à 44 px, comme partout ailleurs sur téléphone. */
  .code-promo-saisie > summary { min-height: 44px; }
  .code-promo-retirer { min-height: 44px; display: inline-flex; align-items: center; }
  .code-promo-saisie form { flex-wrap: wrap; }
  .code-promo-saisie input { min-height: 44px; }
  .code-promo-saisie button { min-height: 44px; }
}

/* Le pied de page replié sur téléphone est fait de `<details>`, et sur grand écran ils
   restent ouverts. Mais ouverts ne veut pas dire invisibles : le marqueur natif du
   navigateur s'affichait, et les quatre colonnes du pied portaient un triangle « ▼ » devant
   leur titre. Le marqueur ne se masque que là où on le dit — la règle qui le faisait vivait
   dans la requête de média du téléphone, et ne s'appliquait donc pas au-dessus. */
.pied-bloc > summary {
  list-style: none;
  cursor: default;
}
.pied-bloc > summary::-webkit-details-marker { display: none; }

@media (min-width: 861px) {
  /* Sur grand écran, le titre de bloc redevient l'intertitre d'origine — petites capitales
     en police à chasse fixe — et non un titre à part : ni chevron, ni curseur de clic,
     puisqu'il n'y a rien à déplier. Les `<h5>` ont disparu du pied en devenant des
     `<summary>` ; leur apparence, elle, n'avait aucune raison de changer. */
  .pied-bloc > summary {
    font-family: var(--font-mono); font-size: .7rem; letter-spacing: .14em;
    text-transform: uppercase; color: var(--muted-dim); font-weight: 500;
    margin: 0 0 var(--s-4); padding-block: 0; border-top: 0;
  }
  .pied-bloc > summary::after { content: none; }
}

/* ================================================================ PROMESSE DE LIVRAISON

   « Livraison en 24 h dans toute l'Europe… » apparaît à cinq endroits — sous le bandeau de
   l'accueil, dans la colonne d'achat d'une fiche, au panier, au pied de page, et dans la
   bouche de l'assistant. Un seul rendu, un seul style : c'est la même phrase, elle doit
   avoir la même tête partout. */
.promesse-livraison {
  display: inline-flex; align-items: center; gap: 9px;
  font-size: .86rem; line-height: 1.35; color: var(--text-soft);
}
.promesse-livraison svg { color: var(--violet-bright); flex-shrink: 0; }

/* Sous le ruban de marques, elle tient toute la largeur et se détache sur un liseré. */
.bande-livraison {
  display: flex; justify-content: center; gap: 10px;
  margin: clamp(14px, 1.8vw, 22px) auto 0;
  padding: 11px 18px; border: 1px solid var(--line); border-radius: 999px;
  background: color-mix(in srgb, var(--violet) 7%, transparent);
}
.bande-livraison .promesse-livraison { font-size: .9rem; font-weight: 550; color: var(--text); }

.footer .promesse-livraison { margin-top: 14px; font-size: .82rem; }
.prod-achat .promesse-livraison, .buy .promesse-livraison { margin-top: 12px; }

/* ---- WhatsApp ----
   Le vert de la marque sert d'accent, pas de fond : un aplat vert au milieu d'une interface
   violette attire l'œil plus que le panier. */
.lien-whatsapp { display: inline-flex; align-items: center; gap: 9px; }
.lien-whatsapp svg { color: #25D366; flex-shrink: 0; }
.lien-whatsapp:hover svg { color: #1EBE5A; }
.pied-whatsapp { margin-top: 14px; }

.assist-contacts { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 6px; }

/* ---- la pastille WhatsApp ----

   Elle n'existe dans la page que si un numéro est enregistré — `brancherWhatsapp()` la retire
   sinon.

   Le vert est celui de la marque, et il ne sort qu'ici : c'est le seul endroit du site où il
   désigne un service identifiable du premier coup d'œil. */
.wa-flottant {
  /* En bas à **gauche**, et non empilée au-dessus du lanceur d'assistance.

     Le coin droit est déjà pris par deux objets flottants — le lanceur, et le sélecteur de
     direction artistique juste au-dessus : une troisième pastille y passait derrière le
     second. Le coin gauche est libre à toutes les largeurs, et deux actions flottantes aux
     deux extrémités se lisent mieux qu'une pile de trois. */
  position: fixed; left: 20px; bottom: 20px; z-index: 86;
  display: inline-flex; align-items: center; gap: 10px;
  padding: 11px 16px 11px 13px; border-radius: 999px;
  background: #25D366; color: #06331A; font-weight: 650; font-size: .9rem;
  box-shadow: 0 12px 28px -10px rgba(37, 211, 102, .75), 0 2px 6px rgba(12, 10, 32, .3);
  transition: transform .2s var(--ease), box-shadow .2s var(--ease);
}
.wa-flottant svg { color: #06331A; flex-shrink: 0; }
.wa-flottant:hover { transform: translateY(-2px); box-shadow: 0 18px 34px -10px rgba(37, 211, 102, .9); }
.wa-flottant:focus-visible { outline: 2px solid #fff; outline-offset: 3px; }

/* Sur téléphone, le texte tombe : deux pastilles côte à côte mangeraient la moitié de la
   largeur, et le logo seul suffit à dire de quoi il s'agit. */
@media (max-width: 640px) {
  .wa-flottant { bottom: calc(14px + env(safe-area-inset-bottom)); left: 14px; padding: 12px; gap: 0; }
  .wa-flottant-texte { display: none; }
}
/* La barre d'achat de la fiche produit occupe toute la largeur du bas sur téléphone : la
   pastille monte au-dessus plutôt que de passer dessous. */
body:has(.barre-achat.est-visible) .wa-flottant,
body:has(.barre-achat[style*="translateY(0"]) .wa-flottant { bottom: 82px; }
@media (prefers-reduced-motion: reduce) {
  .wa-flottant { transition: none; }
  .wa-flottant:hover { transform: none; }
}

/* ======================================================================= AVIS

   Le bloc vit à deux endroits — sous une fiche produit et sur la page de la boutique — et il
   est dessiné pour l'état « aucun avis » autant que pour les autres : c'est l'état normal
   d'une boutique qui ouvre, et il doit donner envie d'écrire le premier plutôt que de
   ressembler à une panne. */
.avis-bloc { margin: clamp(36px, 5vw, 64px) 0; }
.avis-entete {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 16px; flex-wrap: wrap; margin-bottom: 18px;
}
.avis-entete h2 { font-size: clamp(1.25rem, 2.4vw, 1.6rem); margin: 0; }

.etoiles { display: inline-flex; gap: 2px; vertical-align: -2px; }
.etoiles svg { color: var(--line-strong); }
.etoiles .est-pleine svg { color: #F5A623; fill: #F5A623; }

.note-resume { display: inline-flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.note-chiffre { font-weight: 650; }
.note-compte, .note-vide { color: var(--text-soft); font-size: .88rem; }

.note-barres { list-style: none; margin: 0 0 22px; padding: 0; display: grid; gap: 5px; max-width: 320px; }
.note-barres li { display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: 10px; font-size: .82rem; }
.nb-label { display: inline-flex; align-items: center; gap: 3px; color: var(--text-soft); }
.nb-label svg { color: #F5A623; fill: #F5A623; }
.nb-piste { height: 6px; border-radius: 999px; background: var(--line); overflow: hidden; }
.nb-piste i { display: block; height: 100%; border-radius: 999px; background: #F5A623; }
.nb-n { color: var(--text-soft); min-width: 2ch; text-align: right; }

.avis-liste { display: grid; gap: 14px; }
.avis {
  border: 1px solid var(--line); border-radius: var(--radius-lg);
  padding: clamp(14px, 2vw, 20px); background: var(--surface);
}
.avis-tete { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.avis-tete h4 { margin: 0; font-size: 1rem; }
.avis-texte { margin: 10px 0 0; line-height: 1.6; white-space: pre-wrap; }
.avis-pied {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  margin-top: 12px; font-size: .82rem; color: var(--text-soft);
}
.avis-verifie { display: inline-flex; align-items: center; gap: 5px; color: #2FA36B; font-weight: 600; }
.avis-verifie svg { color: #2FA36B; }
/* La réponse du gérant est décalée et marquée : on doit voir d'un coup d'œil que ce n'est
   plus le client qui parle. */
.avis-reponse {
  margin: 14px 0 0; padding: 12px 14px;
  border-left: 3px solid var(--violet); border-radius: 0 var(--radius) var(--radius) 0;
  background: color-mix(in srgb, var(--violet) 7%, transparent);
}
.avis-reponse b { font-size: .82rem; color: var(--violet-bright); }
.avis-reponse p { margin: 5px 0 0; font-size: .9rem; line-height: 1.55; white-space: pre-wrap; }
.avis-aucun { color: var(--text-soft); margin: 0; padding: 22px 0; }

.avis-ouvrir { margin-top: 18px; }
.avis-form {
  margin-top: 18px; padding: clamp(16px, 2.4vw, 24px);
  border: 1px solid var(--line); border-radius: var(--radius-lg); background: var(--surface);
  display: grid; gap: 16px;
}
.avis-form h4 { margin: 0; font-size: 1.05rem; }
.avis-notes { border: 0; margin: 0; padding: 0; }
.avis-notes legend { padding: 0 0 8px; font-size: .88rem; color: var(--text-soft); }
/* Les cinq notes tiennent sur une ligne. Empilées, elles faisaient quatre cent trente pixels
   de haut avant le premier champ du formulaire — le geste le plus simple du bloc devenait
   le plus long. Le libellé complet reste là pour les lecteurs d'écran. */
/* Cinq étoiles, une par choix — et non cinq étoiles dans chacun des cinq choix.

   La première version montrait l'aperçu complet à chaque ligne : chaque choix mesurait 146 px,
   les cinq 728 px, et sur un téléphone de 390 px ils s'empilaient sur cinq lignes. Le contrôle
   le plus banal du formulaire était le plus encombrant.

   Le remplissage se fait en CSS : une étoile s'allume si elle-même ou **une suivante** porte la
   coche, ce que `:has(~ …)` sait exprimer. Les boutons radio restent là, invisibles mais bien
   présents : c'est eux que le clavier parcourt et que les lecteurs d'écran annoncent, avec le
   libellé complet « 4 étoiles sur 5 ». */
.avis-notes-choix { display: inline-flex; gap: 2px; }
.avis-note-choix { display: inline-flex; padding: 3px; cursor: pointer; line-height: 0; border-radius: var(--radius-sm); }
.avis-note-choix input { position: absolute; opacity: 0; width: 1px; height: 1px; margin: 0; }
.avis-note-choix svg { color: var(--line-strong); transition: color .12s var(--ease), transform .12s var(--ease); }
.avis-note-choix:has(input:checked) svg,
.avis-note-choix:has(~ .avis-note-choix input:checked) svg { color: #F5A623; fill: #F5A623; }
.avis-note-choix:hover svg,
.avis-note-choix:has(~ .avis-note-choix:hover) svg { color: #F5A623; fill: #F5A623; transform: scale(1.06); }
.avis-note-choix:has(input:focus-visible) { outline: 2px solid var(--violet-bright); outline-offset: 1px; }
.avis-champs { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
/* Les champs de saisie s'empilent, les choix de note non : la règle vise les enfants directs
   du formulaire et ceux de la grille, pas tout ce qui porte un `<label>`. */
.avis-form > label, .avis-champs > label { display: grid; gap: 6px; font-size: .88rem; }
.avis-form small { color: var(--text-soft); font-size: .78rem; }
.avis-actions { display: flex; gap: 10px; flex-wrap: wrap; }
.avis-mentions { margin: 0; font-size: .78rem; color: var(--text-soft); line-height: 1.5; }
.avis-erreur { margin: 0; color: var(--danger, #FF5C7A); font-size: .88rem; }
.avis-merci {
  display: inline-flex; align-items: center; gap: 9px; margin-top: 18px;
  padding: 12px 16px; border-radius: var(--radius); font-size: .92rem;
  background: color-mix(in srgb, #2FA36B 14%, transparent); color: #3FBE82;
}
.avis-merci svg { color: #3FBE82; }
/* Le champ-piège : hors de l'écran sans `display:none`, que certains robots savent détecter. */
.avis-piege { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

@media (max-width: 640px) {
  .avis-champs { grid-template-columns: 1fr; }
}

/* Lu par les lecteurs d'écran, invisible à l'œil. `clip-path` plutôt que `display:none`,
   qui retirerait aussi le texte de l'arbre d'accessibilité. */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* ---- l'invitation à noter ----
   Le premier clic est la note elle-même : pas de bouton « donner mon avis » qui ouvre un
   formulaire de quatre champs avant qu'on ait rien dit. */
.avis-invite {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  margin-top: 20px; padding: 14px 18px;
  border: 1px solid var(--line); border-radius: var(--radius-lg);
  background: var(--surface);
}
.avis-invite-texte { font-size: .92rem; }
.avis-invite-etoiles { display: inline-flex; gap: 2px; }
.avis-etoile-btn {
  border: 0; background: none; padding: 3px; cursor: pointer; line-height: 0;
  border-radius: var(--radius-sm);
}
.avis-etoile-btn svg { color: var(--line-strong); transition: color .12s var(--ease), transform .12s var(--ease); }
.avis-etoile-btn.est-pleine svg { color: #F5A623; fill: #F5A623; transform: scale(1.08); }
.avis-etoile-btn:focus-visible { outline: 2px solid var(--violet-bright); outline-offset: 1px; }

.avis-identifie {
  display: flex; align-items: center; gap: 8px; margin: 0;
  font-size: .86rem; color: var(--text-soft);
}
.avis-identifie svg { color: var(--violet-bright); }

/* Le rappel d'avis dans la liste des commandes du compte. */
.commande .avis-depuis {
  display: inline-flex; align-items: center; gap: 5px;
  margin-left: 10px; font-size: .8rem; color: var(--violet-bright);
}

/* Les avis des pièces voisines : leur propre bloc, leur propre titre, et chaque avis dit de
   quelle pièce il parle. Jamais mélangés à ceux de la fiche affichée. */
.avis-voisins { margin-top: 26px; padding-top: 22px; border-top: 1px dashed var(--line); }
.avis-voisins h3 { font-size: .95rem; color: var(--text-soft); font-weight: 600; margin: 0 0 14px; }
.avis-voisins .avis { background: transparent; }
.avis-sur { font-size: .82rem; color: var(--violet-bright); }

/* ================================================================== RUBAN DE MARQUES

   Une bande qui défile en continu sous le bandeau d'accueil, et qui dit en un coup d'œil ce
   que la boutique couvre.

   La piste contient deux fois la même série et se déplace de la moitié de sa largeur : au
   rebouclage, la seconde série est exactement là où était la première, donc le raccord est
   invisible. C'est ce qui distingue une boucle sans fin d'une animation qui revient en
   arrière — et ça tient en une seule règle. */
.ruban {
  /* Fond transparent, pas de cadre : le ruban se pose sur la page au lieu de s'y découper.
     Seuls les logos bougent, ce qui est bien l'effet recherché — une bande de marques, pas
     une barre d'outils.

     La largeur est **exactement celle de la bannière** — même formule, même rupture à 640 px.
     Pleine fenêtre, le ruban débordait de deux côtés d'un bandeau cadré juste au-dessus, et
     les deux blocs les plus visibles de l'accueil ne partageaient aucun bord. */
  position: relative; overflow: hidden;
  width: min(100% - 40px, var(--wrap)); margin-inline: auto;
  padding-block: clamp(16px, 2vw, 26px);
  background: transparent;
}
@media (max-width: 640px) { .ruban { width: calc(100% - 32px); } }
/* Les deux bords s'estompent : une tuile coupée net au ras du cadre donne l'impression d'un
   débordement, alors qu'elle entre et sort d'un ruban. */
.ruban::before, .ruban::after {
  content: ''; position: absolute; top: 0; bottom: 0; width: clamp(28px, 6vw, 80px);
  z-index: 2; pointer-events: none;
}
.ruban::before { left: 0; background: linear-gradient(90deg, var(--bg), transparent); }
.ruban::after { right: 0; background: linear-gradient(270deg, var(--bg), transparent); }

.ruban-piste { display: flex; width: max-content; animation: ruban var(--duree, 32s) linear infinite; }
.ruban-serie { display: flex; align-items: center; }
@keyframes ruban { from { transform: translateX(0); } to { transform: translateX(-50%); } }

/* Le défilement s'arrête quand on survole ou quand le clavier entre dedans : on ne clique pas
   sur une cible qui bouge, et on ne lit pas une étiquette qui file. */
.ruban:hover .ruban-piste,
.ruban:focus-within .ruban-piste { animation-play-state: paused; }

/* Chaque marque porte sa couleur, tout le temps. Elle était grise au repos : la bande était
   juste, et fade. Le survol fait maintenant le contraire du gris — il agrandit légèrement et
   accentue, au lieu de révéler une couleur qu'on cachait. */
.ruban-marque {
  display: inline-flex; align-items: center; justify-content: center;
  height: clamp(30px, 3.4vw, 42px); padding-inline: clamp(18px, 2.6vw, 38px);
  color: var(--marque, var(--violet-bright));
  transition: transform .18s var(--ease), filter .18s var(--ease);
}
.ruban-marque:hover { transform: translateY(-2px) scale(1.04); filter: saturate(1.15); }
.ruban-marque:focus-visible {
  outline: 2px solid var(--violet-bright); outline-offset: 4px; border-radius: var(--radius-sm);
}
/* Une tonalité presque noire — l'Apple #1D1D1F, le Sony #111827 — disparaîtrait sur un fond
   sombre. Il n'existe pas de « si cette couleur est trop sombre » en CSS : chaque marque
   fournit donc sa version éclaircie, et c'est `themes.css` qui la choisit la nuit. */

.logo-marque { display: block; }
/* Les trois formes partagent la même hauteur apparente : sans cela, une pomme de 42 px
   écraserait un mot de 16 px et la bande paraîtrait bancale. */
.logo-marque.est-fichier { height: 100%; width: auto; max-width: clamp(90px, 11vw, 150px);
  object-fit: contain; }
/* Le tracé officiel est carré (24 × 24) : une pomme et un logotype Samsung n'ont pas la même
   largeur, mais ils doivent avoir la même hauteur apparente, sinon la bande paraît bancale. */
.logo-marque.est-tracé {
  height: calc(100% * var(--hauteur, 1)); aspect-ratio: var(--rapport, 1);
}
.logo-marque.est-tracé svg { height: 100%; width: 100%; display: block; }
.logo-marque.est-mot { font-size: clamp(1.05rem, 1.7vw, 1.5rem); line-height: 1; white-space: nowrap; }

@media (max-width: 640px) {
  .ruban { padding-block: 12px; }
  .ruban-marque { height: 26px; padding-inline: 15px; }
  .logo-marque.est-mot { font-size: 1rem; }
}

/* En mouvement réduit, le ruban ne défile pas : il devient une bande qu'on fait glisser au
   doigt ou à la molette. L'information reste entière, le mouvement disparaît. */
@media (prefers-reduced-motion: reduce) {
  .ruban { overflow-x: auto; scrollbar-width: none; }
  .ruban::-webkit-scrollbar { display: none; }
  .ruban-piste { animation: none; }
  .ruban-serie:last-child { display: none; }
}

/* ================================================================= HEURES LIMITES

   « Commandez avant telle heure, ça part aujourd'hui. » Une carte par transporteur, son logo
   en grand, son heure en dessous. C'est volontairement la chose la plus simple de la page :
   l'information est courte, elle doit se lire sans effort depuis un téléphone tenu à bout de
   bras dans un atelier. */
/* Le bloc a rejoint les autres sections de la page : il en reprend l'en-tête — sur-titre,
   titre, phrase — au lieu d'avoir sa propre mise en page. Une section qui ne ressemble à
   aucune autre se lit comme une pièce rapportée. */

/* `auto-fit` plutôt qu'un nombre de colonnes figé : trois transporteurs doivent remplir la
   largeur comme quatre, sans laisser un trou à droite. */
.cutoff-grille {
  display: grid; gap: clamp(12px, 1.6vw, 20px);
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
}
.cutoff {
  display: flex; flex-direction: column; align-items: center; gap: 16px;
  padding: clamp(22px, 3vw, 34px) 18px;
  border: 1px solid var(--line); border-radius: var(--radius-xl);
  background: var(--surface); text-align: center;
}
/* Le logo prend la couleur du transporteur, et sa version éclaircie sur les thèmes sombres —
   le marron UPS et le violet FedEx y disparaîtraient.

   La hauteur est posée en valeur absolue sur l'enfant, et non en `height: 100%`. Dans une
   grille centrée, la rangée se dimensionne sur son contenu et le contenu sur la rangée : le
   pourcentage n'a rien contre quoi se résoudre, il retombe sur `auto`, et le logo s'affichait
   à sa taille naturelle — un DPD de trois cents pixels par-dessus toute la carte. */
.cutoff-logo {
  --h-logo: clamp(30px, 3.6vw, 42px);
  height: var(--h-logo); display: flex; align-items: center; justify-content: center;
  color: var(--teinte);
}
.cutoff-logo .logo-marque.est-tracé { height: calc(var(--h-logo) * var(--hauteur, 1)); }
.cutoff-logo .logo-marque.est-fichier { height: var(--h-logo); width: auto; max-width: 160px; }
.cutoff-logo .logo-marque.est-mot { font-size: clamp(1.1rem, 1.9vw, 1.5rem); }

.cutoff-heure { margin: 0; display: flex; align-items: baseline; gap: 7px; flex-wrap: wrap; justify-content: center; }
.cutoff-heure b { font-size: .88rem; }
.cutoff-heure span { font-size: 1.15rem; font-weight: 700; color: var(--violet-bright); }
.cutoff-note { margin: 0; font-size: .8rem; color: var(--text-soft); }

/* `auto-fit` ici aussi, et non `repeat(2, 1fr)` : à deux colonnes figées, un nombre impair de
   transporteurs laissait la dernière carte seule avec un trou à sa droite — exactement ce que
   la règle du dessus évite au-delà de 560 px. */
@media (max-width: 560px) {
  .cutoff-grille { grid-template-columns: repeat(auto-fit, minmax(135px, 1fr)); gap: 10px; }
  .cutoff { padding: 18px 10px; gap: 12px; }
}

/* ============================================================ ENTRÉE PAR LA PANNE

   « Qu'est-ce qui ne va pas avec votre téléphone ? » — posé juste sous le bandeau, en une
   ligne. Volontairement compact : la page vient d'être allégée de deux bandes, en rajouter une
   épaisse reviendrait à refaire le problème qu'on vient de corriger. */
.panne-entree { padding-block: clamp(16px, 2.2vw, 26px) 0; }
.panne-barre { display: grid; gap: 10px; }

.panne-saisie {
  display: flex; align-items: center; gap: 10px;
  padding: 7px 7px 7px 16px;
  border: 1px solid var(--line-strong); border-radius: 999px;
  background: var(--surface);
  transition: border-color .18s var(--ease), box-shadow .18s var(--ease);
}
.panne-saisie:focus-within {
  border-color: var(--violet-bright);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--violet) 18%, transparent);
}
.panne-icone { display: inline-flex; color: var(--violet-bright); flex-shrink: 0; }
.panne-saisie input {
  flex: 1; min-width: 0; border: 0; background: none; color: var(--text);
  font: inherit; font-size: clamp(.92rem, 1.4vw, 1.05rem); padding: 10px 0;
}
.panne-saisie input:focus { outline: none; }
.panne-saisie input::placeholder { color: var(--muted-dim); }
.panne-saisie .btn { flex-shrink: 0; border-radius: 999px; padding: 11px 20px; }

.panne-exemples {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin: 0; font-size: .84rem; color: var(--text-soft);
}
.panne-exemples .chip { font-size: .84rem; }

/* Sur téléphone, le libellé du bouton tombe : l'icône suffit, et la phrase saisie compte plus
   que le mot « chercher ». */
/* Sur téléphone, les trois exemples passaient à deux puis trois lignes : la barre montait à
   182 px à 390 px et 234 px à 320 px, alors qu'elle en coûte 138 sur un écran d'ordinateur.
   Elle redevenait la bande épaisse qu'on cherchait à éviter. Ils défilent donc à l'horizontale
   sur une seule ligne, sans barre de défilement visible. */
@media (max-width: 560px) {
  .panne-saisie { padding-left: 13px; gap: 7px; }
  .panne-saisie .btn { padding: 11px 13px; }
  .panne-saisie .btn span { display: none; }
  .panne-exemples > span { display: none; }
  .panne-exemples {
    flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
    margin-inline: calc(var(--s-4) * -1); padding-inline: var(--s-4);
  }
  .panne-exemples::-webkit-scrollbar { display: none; }
  .panne-exemples .chip { flex-shrink: 0; }
}

.empty-actions { display: flex; gap: 10px; flex-wrap: wrap; justify-content: center; margin-top: 18px; }
.empty-actions .btn { display: inline-flex; align-items: center; gap: 8px; }

/* Les compléments de réparation : même carte que les pièces proposées, mais décalée et
   discrète — ils accompagnent la réparation, ils ne la font pas. */
.assist-produits.est-complement {
  border-left: 2px solid var(--line-strong); padding-left: 10px; margin-left: 2px;
}
.assist-produits.est-complement .assist-produit { background: transparent; }

/* Le compteur d'une colonne de rayon : « Coque & étui 1 983 ». Il dit l'ampleur du rayon là
   où une marque n'en a pas besoin — on sait ce que pèse Apple, pas ce que pèse « Protections ». */
.megamenu-n { color: var(--text-soft); font-weight: 400; font-size: .82em; }
/* L'onglet d'un rayon porte son pictogramme comme avant, plus le chevron des marques. */
.brand-tab.est-rayon .brand-ico { opacity: .5; }
.brand-onglet.est-ouvert .brand-ico { opacity: 1; }

/* Le chevron du sélecteur de langue : il tourne à l'ouverture, comme tous les dépliants du
   site. Sans lui, le drapeau et les deux lettres ressemblaient aux autres pastilles rondes de
   l'en-tête, qui sont des boutons d'action et non des menus. */
.langue-chevron {
  color: var(--text-soft); flex-shrink: 0; margin-left: -1px;
  transition: transform .18s var(--ease);
}
.langue-bouton[aria-expanded="true"] .langue-chevron { transform: rotate(180deg); }

/* Attente de la confirmation de paiement.

   Un anneau qui tourne, et non trois points : l'attente dure quelques secondes au plus, et
   celui qui vient de donner son numéro de carte a besoin de voir que quelque chose se passe
   — pas d'un ornement. Mouvement réduit : l'anneau s'arrête et pulse légèrement, parce qu'un
   indicateur figé ne se distingue plus d'une page bloquée.

   La couleur est héritée (`currentColor`) : le cercle qui l'entoure change de fond selon
   l'état, et la bordure doit suivre sans qu'on ait à redéclarer la teinte deux fois. */
.conf-attente {
  display: block;
  width: 26px;
  height: 26px;
  border: 3px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  animation: conf-tourne .85s linear infinite;
}
@keyframes conf-tourne { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .conf-attente { animation: conf-pulse 1.4s ease-in-out infinite; border-top-color: currentColor; }
  @keyframes conf-pulse { 50% { opacity: .35; } }
}

/* ------------------------------------------------------------- tutoriels

   Un tutoriel se lit sur un téléphone posé à côté de celui qu'on démonte, souvent les mains
   occupées et parfois grasses. D'où trois partis pris : des numéros d'étape assez gros pour
   se retrouver d'un coup d'œil, un schéma par étape plutôt qu'une photo contextuelle, et
   aucune information décisive — durée, niveau, outils — en dessous de la première étape. */

.tuto-entete { max-width: 60ch; margin-block: clamp(20px, 4vw, 40px) 28px; }
.tuto-entete h1 { font-size: clamp(1.7rem, 4vw, 2.4rem); letter-spacing: -.02em; }
.tuto-entete p { margin-top: 12px; color: var(--muted); line-height: 1.65; }

.tuto-niveaux {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr));
  gap: 12px;
  margin-bottom: 28px;
}
.tuto-niveau {
  padding: 14px 16px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface-2, rgba(255, 255, 255, .02));
}
.tuto-niveau p { margin: 8px 0 0; font-size: .84rem; color: var(--muted); line-height: 1.5; }

.tuto-grille {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(300px, 100%), 1fr));
  gap: 16px;
  margin-bottom: 48px;
}
.tuto-carte {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 20px;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg, 16px);
  background: var(--surface, rgba(255, 255, 255, .03));
  color: inherit;
  text-decoration: none;
  transition: border-color .18s, transform .18s;
}
.tuto-carte:hover, .tuto-carte:focus-visible {
  border-color: var(--violet-bright);
  transform: translateY(-2px);
}
.tuto-carte-haut { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.tuto-duree { display: inline-flex; align-items: center; gap: 5px; font-size: .8rem; color: var(--muted); }
.tuto-carte h3 { font-size: 1.05rem; line-height: 1.35; letter-spacing: -.01em; }
.tuto-carte p { margin: 0; font-size: .86rem; color: var(--muted); line-height: 1.55; }
.tuto-carte-pied {
  display: flex; align-items: center; gap: 6px;
  margin-top: auto; padding-top: 6px;
  font-size: .8rem; color: var(--muted-dim, var(--muted));
}
.tuto-carte-pied svg { margin-left: auto; }

/* ------------------------------------------------------------- la fiche */

.tuto-fiche { max-width: 760px; padding-bottom: 64px; }
.tuto-tete h1 { font-size: clamp(1.6rem, 4vw, 2.2rem); letter-spacing: -.02em; margin-top: 12px; }
.tuto-chapo { margin-top: 12px; font-size: 1.02rem; line-height: 1.7; color: var(--muted); }

.tuto-reperes {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
  margin-top: 24px;
}
.tuto-reperes > div {
  padding: 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  text-align: center;
}
.tuto-reperes .l {
  display: block;
  font-size: .66rem; letter-spacing: .12em; text-transform: uppercase; color: var(--muted);
}
.tuto-reperes b { display: block; margin-top: 6px; font-size: 1.1rem; }
.tuto-niveau-detail { margin-top: 12px; font-size: .86rem; color: var(--muted); }

.tuto-outils { margin-top: 36px; }
.tuto-outils h2, .tuto-pieces h2 { font-size: 1.15rem; letter-spacing: -.01em; }
.tuto-outils ul {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(220px, 100%), 1fr));
  gap: 8px 16px;
  margin-top: 14px;
  padding: 0;
  list-style: none;
}
.tuto-outils li { display: flex; align-items: center; gap: 8px; font-size: .9rem; }
.tuto-outils li svg { color: var(--violet-bright); flex: 0 0 auto; }

/* Les étapes. Le numéro est dans la marge sur grand écran et au-dessus sur téléphone :
   superposé au texte, il mangeait une ligne entière à 320 px. */
.tuto-etapes { margin-top: 36px; padding: 0; list-style: none; counter-reset: etape; }
.tuto-etape {
  display: grid;
  grid-template-columns: 44px 1fr;
  gap: 8px 16px;
  padding-block: 24px;
  border-top: 1px solid var(--line);
}
.tuto-etape-num {
  display: grid; place-items: center;
  width: 38px; height: 38px;
  border-radius: 50%;
  background: var(--gradient);
  color: #fff;
  font-weight: 700; font-size: 1rem;
}
.tuto-etape-corps h3 { font-size: 1.05rem; letter-spacing: -.01em; }
.tuto-etape-corps p { margin: 10px 0 0; line-height: 1.7; }
.tuto-etape-corps ul { margin: 10px 0 0; padding-left: 20px; line-height: 1.7; }

.tuto-attention {
  display: flex; align-items: flex-start; gap: 10px;
  margin-top: 14px !important;
  padding: 12px 14px;
  border-radius: var(--radius);
  border: 1px solid color-mix(in oklab, var(--warning) 40%, transparent);
  background: color-mix(in oklab, var(--warning) 10%, transparent);
  font-size: .88rem; line-height: 1.55;
}
.tuto-attention svg { flex: 0 0 auto; color: var(--warning); margin-top: 1px; }

.tuto-schema {
  grid-column: 2;
  margin: 16px 0 0;
  padding: 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface-2, rgba(255, 255, 255, .02));
}
.tuto-schema .schema { display: block; width: 100%; max-width: 320px; height: auto; margin-inline: auto; }

.tuto-pieces { margin-top: 44px; }
.tuto-pieces-liste { display: grid; gap: 8px; margin-top: 16px; }
.tuto-piece {
  display: flex; align-items: center; gap: 12px;
  padding: 10px 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  color: inherit; text-decoration: none;
  transition: border-color .16s;
}
.tuto-piece:hover, .tuto-piece:focus-visible { border-color: var(--violet-bright); }
.tuto-piece-visuel {
  display: grid; place-items: center;
  width: 44px; height: 44px; flex: 0 0 auto;
  border-radius: 8px; overflow: hidden;
  background: #fff; color: var(--muted);
}
.tuto-piece-visuel img { width: 100%; height: 100%; object-fit: contain; }
.tuto-piece-texte { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
.tuto-piece-texte b { font-size: .88rem; font-weight: 500; }
.tuto-piece-prix { font-weight: 600; color: var(--violet-bright); }

.tuto-pied {
  display: flex; flex-wrap: wrap; gap: 14px; align-items: center;
  margin-top: 44px; padding-top: 24px;
  border-top: 1px solid var(--line);
}
.tuto-pied p { margin: 0; flex: 1 1 260px; font-size: .9rem; color: var(--muted); line-height: 1.6; }

@media (max-width: 560px) {
  .tuto-etape { grid-template-columns: 1fr; gap: 12px; }
  .tuto-schema { grid-column: 1; }
  .tuto-reperes { grid-template-columns: 1fr; }
  .tuto-reperes > div { display: flex; align-items: baseline; justify-content: space-between; text-align: left; }
  .tuto-reperes b { margin-top: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .tuto-carte:hover, .tuto-carte:focus-visible { transform: none; }
}

/* Le bloc tutoriel de la fiche produit.

   Il est posé au-dessus des avis et non en bas de page : l'objection qu'il lève — « est-ce
   que je vais y arriver » — se pose au moment de cliquer sur « ajouter », pas après avoir
   lu tous les commentaires. */
.fiche-tutos { margin-block: 48px 0; }
.fiche-tutos h2 { font-size: 1.4rem; letter-spacing: -.015em; }
.fiche-tutos > p { margin-top: 10px; max-width: 62ch; line-height: 1.6; }
.fiche-tutos-liste {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr));
  gap: 10px;
  margin-top: 18px;
}
.fiche-tuto {
  display: flex; align-items: center; gap: 12px;
  padding: 14px 16px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  color: inherit; text-decoration: none;
  transition: border-color .16s;
}
.fiche-tuto:hover, .fiche-tuto:focus-visible { border-color: var(--violet-bright); }
.fiche-tuto-texte { display: flex; flex-direction: column; gap: 3px; min-width: 0; flex: 1; }
.fiche-tuto-texte b { font-size: .95rem; font-weight: 500; }
.fiche-tuto-texte .muted { font-size: .8rem; }
.fiche-tuto svg { flex: 0 0 auto; color: var(--violet-bright); }

/* ------------------------------------------- « Il vous faudra aussi »

   Une liste, pas une grille de cartes. Une carte produit invite à cliquer dessus pour en
   savoir plus — c'est-à-dire à quitter la page qu'on vient de décider d'acheter. Une ligne
   dense avec sa raison à gauche et son bouton à droite invite à ajouter sans partir, ce qui
   est exactement ce qu'on cherche ici.

   Le bloc est volontairement sobre : il arrive juste après le bouton d'achat, et deux
   sollicitations fortes qui se suivent se neutralisent. */
.complements { margin-block: 44px 0; }
.complements-tete {
  display: flex; flex-wrap: wrap; gap: 14px;
  align-items: flex-end; justify-content: space-between;
  margin-bottom: 18px;
}
.complements-tete h2 { margin-top: 10px; font-size: clamp(1.25rem, 2.4vw, 1.5rem); letter-spacing: -.015em; }

.complements-liste { display: grid; gap: 8px; }
.complement {
  display: grid;
  grid-template-columns: 56px minmax(0, 1fr) auto;
  gap: 14px;
  align-items: center;
  padding: 12px 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface, rgba(255, 255, 255, .02));
  transition: border-color .16s;
}
.complement:hover { border-color: color-mix(in oklab, var(--violet-bright) 55%, transparent); }

.complement-visuel {
  display: grid; place-items: center;
  width: 56px; height: 56px;
  border-radius: 10px; overflow: hidden;
  background: #fff; color: var(--muted);
}
.complement-visuel img { width: 100%; height: 100%; object-fit: contain; }

.complement-texte { min-width: 0; }
/* La raison passe avant le nom, et elle est en couleur : c'est elle qui décide, le nom ne
   fait que dire de quoi il s'agit. L'ordre inverse — nom d'abord — ramenait le bloc à une
   liste de produits de plus. */
.complement-raison {
  margin: 0 0 3px;
  font-size: .78rem; font-weight: 500;
  color: var(--violet-bright);
}
.complement-nom {
  display: block;
  font-size: .92rem; line-height: 1.35;
  color: inherit; text-decoration: none;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.complement-nom:hover { text-decoration: underline; }

.complement-achat { display: flex; align-items: center; gap: 12px; }
.complement-prix { font-weight: 600; white-space: nowrap; }
.complement-achat .btn { gap: 6px; white-space: nowrap; }

@media (max-width: 560px) {
  /* Le nom peut alors tenir sur deux lignes : à 320 px, une ligne tronquée ne dit plus de
     quelle pièce il s'agit. */
  .complement { grid-template-columns: 48px minmax(0, 1fr); row-gap: 10px; }
  .complement-visuel { width: 48px; height: 48px; }
  .complement-nom { white-space: normal; }
  .complement-achat { grid-column: 1 / -1; justify-content: space-between; }
  .complements-tete [data-tout-ajouter] { width: 100%; }
}

/* La gamme de la pièce, expliquée sous le titre.

   Encadrée et non en texte courant : c'est une réponse à une question que le client se pose
   — « pourquoi celle-ci coûte-t-elle trois fois plus que l'autre » — et elle doit se repérer
   d'un coup d'œil entre le titre et le prix. */
.product-gamme {
  display: flex; align-items: flex-start; gap: 9px;
  margin-top: 12px;
  padding: 10px 13px;
  border: 1px solid color-mix(in oklab, var(--violet-bright) 32%, transparent);
  border-radius: var(--radius);
  background: color-mix(in oklab, var(--violet-bright) 8%, transparent);
  font-size: .87rem; line-height: 1.5;
}
.product-gamme svg { flex: 0 0 auto; margin-top: 2px; color: var(--violet-bright); }
.product-gamme b { font-weight: 600; }

/* La pastille de disponibilité.

   Un point coloré plutôt qu'un fond plein : la carte produit porte déjà un badge de remise
   et un cœur, et une troisième étiquette pleine aurait fait de la vignette un sapin. Le point
   se lit d'un coup d'œil dans une grille, et la couleur n'est jamais seule à porter
   l'information — le mot est écrit à côté, pour qui ne distingue pas le vert de l'orange. */
.dispo {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: .72rem; font-weight: 500;
  white-space: nowrap;
}
.dispo-point {
  width: 7px; height: 7px; border-radius: 50%;
  background: currentColor;
  flex: 0 0 auto;
}
.dispo.est-dispo { color: var(--success, #3FBF7F); }
.dispo.est-commande { color: var(--warning, #E8A33D); }

/* Sur la carte du catalogue : une seule ligne, la famille cède la place à la pastille.

   `flex-wrap` aurait été plus simple et c'était la première version — mais les cartes dont
   le rayon porte un nom long passaient alors sur deux lignes et devenaient plus hautes que
   leurs voisines. Mesuré : 349 px contre 313, et le nom du produit qui démarrait à trois
   hauteurs différentes dans la même grille. La famille se tronque, la pastille jamais : du
   rayon on devine la fin, de « Disponible » tronqué on ne devine rien. */
.product-card .fam {
  display: flex; align-items: center; gap: 8px;
  min-width: 0;
}
.product-card .fam-nom {
  min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.product-card .fam .dispo { flex: 0 0 auto; }
.product-card .fam .dispo::before {
  content: '·';
  margin-right: 3px;
  color: var(--muted);
}

/* Sur la fiche produit, elle prend la forme d'une étiquette pleine comme ses voisines, et
   porte sa phrase d'explication : c'est là qu'on décide, et « Sur commande » sans délai
   annoncé est une inquiétude plutôt qu'une information. */
.product-detail .dispo {
  padding: 5px 10px;
  border-radius: 999px;
  font-size: .78rem;
  border: 1px solid currentColor;
  background: color-mix(in oklab, currentColor 10%, transparent);
  /* `nowrap` est hérité de la pastille des cartes, où la phrase d'explication n'existe pas.
     Ici elle existe, et sur un téléphone de 320 px elle poussait la fiche 77 px hors de
     l'écran. On rend la main au retour à la ligne, et on borne la largeur. */
  white-space: normal;
  max-width: 100%;
}
.dispo-detail {
  margin-left: 6px;
  color: var(--muted);
  font-weight: 400;
}
@media (max-width: 560px) {
  /* Le détail passe sous le libellé plutôt que d'étirer l'étiquette hors de l'écran. */
  .product-detail .dispo { flex-wrap: wrap; }
  .dispo-detail { margin-left: 0; flex-basis: 100%; }
}

/* ============================================================== comparateur
   Un tableau, et un seul geste de lecture : repérer ce qui diffère. Les lignes
   identiques s'effacent au gris, les différentes gardent l'encre pleine et un
   filet indigo à gauche. La bascule « différences seulement » les masque tout
   à fait, pour qui veut aller droit au but.

   La première colonne est collée à gauche : sur un téléphone, quatre colonnes
   ne tiennent pas et le tableau défile — sans cela on perd de vue l'intitulé
   de la ligne qu'on est en train de lire, et la comparaison ne veut plus rien
   dire. */

.comp-enveloppe {
  overflow-x: auto;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
  -webkit-overflow-scrolling: touch;
}

.comp-table { width: 100%; border-collapse: collapse; font-size: .92rem; }

.comp-table th,
.comp-table td {
  padding: 13px 16px;
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid var(--line);
}

/* L'intitulé de ligne, toujours visible pendant le défilement horizontal. */
.comp-table tbody th[scope="row"],
.comp-table thead th:first-child {
  position: sticky;
  left: 0;
  z-index: 2;
  background: var(--surface-2);
  color: var(--muted);
  font-weight: 600;
  font-size: .82rem;
  min-width: 160px;
  border-right: 1px solid var(--line);
}

.comp-tete { min-width: 190px; text-align: center; position: relative; }
.comp-tete a { display: block; color: inherit; text-decoration: none; }
/* `media()` pose son image en absolu, calée sur dix pixels de marge : elle attend donc un
   parent positionné. Sans cette ligne, l'image se calait sur la cellule entière — la plus
   proche ancêtre positionnée — et passait par-dessus le nom du produit. */
.comp-visuel {
  position: relative;
  display: block;
  aspect-ratio: 1;
  max-width: 120px;
  margin: 0 auto 10px;
  background: var(--media-bg);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
}
.comp-nom {
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
  overflow: hidden;
  font-weight: 600;
  line-height: 1.35;
  margin-bottom: 10px;
}
.comp-ajouter { width: 100%; justify-content: center; }

.comp-retirer {
  position: absolute; top: 6px; right: 6px;
  width: 26px; height: 26px; display: grid; place-items: center;
  border: 1px solid var(--line); border-radius: 50%;
  background: var(--surface); color: var(--muted-dim); cursor: pointer;
}
.comp-retirer:hover { color: var(--danger); border-color: var(--danger); }

/* Le bouton de la fiche produit. Enfoncé, il prend l'indigo des pastilles de carte : c'est
   la même action, elle doit se reconnaître d'un écran à l'autre. */
.comp-fiche { margin-top: 12px; }
.comp-fiche.est-actif { color: var(--violet); border-color: var(--violet); }
.comp-fiche.est-actif svg { color: var(--violet); }

/* Ce qui ne change pas d'une colonne à l'autre s'efface : c'est du bruit pour
   qui compare. Ce qui change garde l'encre pleine et se signale. */
.comp-ligne td { color: var(--muted); }
.comp-ligne.est-differente td { color: var(--text); font-weight: 600; }
.comp-ligne.est-differente th[scope="row"] {
  color: var(--violet);
  box-shadow: inset 3px 0 0 var(--violet);
}
.comp-ligne-prix td { font-family: var(--font-mono); font-size: 1.02rem; }

.comp-table.seulement-diff .comp-ligne:not(.est-differente) { display: none; }

.comp-actions { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.comp-filtre { white-space: nowrap; }

/* Le compteur de l'en-tête, dans l'indigo des actions : il ne doit pas se
   confondre d'un coup d'œil avec la pastille rouge des favoris. */
.comp-count {
  position: absolute; top: -6px; right: -6px; min-width: 19px; height: 19px; padding: 0 5px;
  background: var(--violet); border-radius: 999px;
  color: var(--on-gradient); font-size: .68rem; font-weight: 700;
  display: grid; place-items: center;
}

@media (max-width: 640px) {
  .comp-table th, .comp-table td { padding: 11px 12px; }
  .comp-table tbody th[scope="row"], .comp-table thead th:first-child { min-width: 118px; font-size: .76rem; }
  .comp-tete { min-width: 150px; }
  .comp-visuel { max-width: 88px; }
}

/* Le compteur sur la tuile du menu mobile : un nombre discret, à droite du libellé. */
.menu-tuile-n {
  min-width: 18px; height: 18px; padding: 0 5px;
  background: var(--violet); border-radius: 999px;
  color: var(--on-gradient); font-size: .66rem; font-weight: 700;
  display: grid; place-items: center;
}

/* ====== les pages réglementaires ===========================================

   Un texte de loi se lit mal en pleine largeur et se parcourt encore plus mal sans repères :
   l'acheteur ne lit pas la page, il y cherche une réponse — « est-ce que je peux renvoyer »,
   « qui est derrière ce site ». D'où une colonne étroite, des numéros d'article visibles dans
   la marge, et deux encadrés qui tranchent sur le reste : ce qui est garanti, et ce qui
   manque encore. */

.legal { font-size: .95rem; line-height: 1.65; }
.legal .legal-maj { color: var(--muted); font-size: .82rem; margin-top: 10px; }
.legal .legal-langue {
  margin-top: 14px; padding: 10px 14px;
  background: var(--surface-2); border-radius: var(--radius-sm);
  color: var(--text-soft); font-size: .86rem;
}

.legal-art { margin-top: 44px; }
.legal-art h2 {
  font-size: 1.22rem; margin-bottom: 14px;
  display: flex; align-items: baseline; gap: 10px;
}
/* Le numéro d'article en pastille : on cite « l'article 8 » dans un courriel de réclamation,
   il faut pouvoir le retrouver d'un coup d'œil en faisant défiler. */
.legal-num {
  flex-shrink: 0;
  min-width: 26px; height: 26px; padding: 0 6px;
  display: inline-grid; place-items: center;
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  font-family: var(--font-mono); font-size: .78rem; font-weight: 600;
  color: var(--violet-bright);
}
.legal-art p { margin-bottom: 12px; color: var(--text-soft); }
.legal-art p:last-child { margin-bottom: 0; }
.legal-art ul { margin: 0 0 12px; padding-left: 20px; color: var(--text-soft); }
.legal-art li { margin-bottom: 6px; }
.legal-art b { color: var(--text); font-weight: 600; }
.legal-art a { color: var(--violet-bright); text-decoration: underline; text-underline-offset: 2px; }

.legal-table { margin-bottom: 14px; }
.legal-table td { font-family: var(--font-body); font-size: .9rem; line-height: 1.6; }

/* Ce que la loi garantit, mis en évidence : c'est la partie que l'on vient lire. */
.legal-encadre {
  margin: 16px 0;
  padding: 18px 20px;
  background: var(--surface-2);
  border-left: 3px solid var(--violet);
  border-radius: var(--radius-sm);
}
.legal-encadre p:last-child { margin-bottom: 0; }

/* Ce qui manque encore. Volontairement voyant : une mention légale incomplète doit se voir
   depuis la page, pas seulement dans une liste de tâches. */
.legal-trou {
  display: flex; gap: 10px; align-items: flex-start;
  margin: 14px 0;
  padding: 12px 14px;
  background: color-mix(in srgb, var(--warning) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--warning) 38%, transparent);
  border-radius: var(--radius-sm);
  font-size: .87rem; color: var(--text-soft);
}
.legal-trou svg { color: var(--warning); flex-shrink: 0; margin-top: 3px; }
.legal-trou b { color: var(--text); }

.legal-formulaire {
  margin-top: 18px; padding: 20px;
  border: 1px dashed var(--line-strong); border-radius: var(--radius);
}
.legal-formulaire h3 { font-size: 1rem; margin-bottom: 4px; }
.legal-formulaire ul { list-style: none; padding-left: 0; margin-top: 10px; }
.legal-formulaire li { font-family: var(--font-mono); font-size: .82rem; }

.legal-liens {
  margin-top: 56px; padding-top: 22px;
  border-top: 1px solid var(--line);
  font-size: .9rem; color: var(--muted);
}
.legal-liens a { color: var(--violet-bright); }

/* ====== le bouton « Continuer avec Google » ================================

   Fond de surface et filet discret : les règles de marque de Google demandent un bouton
   neutre et leur logo intact, et c'est aussi ce qui convient ici — ce bouton n'est pas
   l'action principale de la page, c'est un raccourci à côté du formulaire. */
.btn-google { gap: 10px; }
.btn-google svg { flex-shrink: 0; }

/* « ou », avec un filet de part et d'autre. */
.auth-ou {
  display: flex; align-items: center; gap: 12px;
  margin: 18px 0; color: var(--muted); font-size: .82rem;
}
.auth-ou::before, .auth-ou::after {
  content: ''; flex: 1; height: 1px; background: var(--line);
}
.auth-error.est-visible { display: block; }

/* ====== la note sur une carte de catalogue =================================

   Les étoiles elles-mêmes sont celles de la fiche produit — mêmes classes, même teinte — et
   ne sont pas redéfinies ici. Il ne reste que la ligne qui les porte sur la carte. */

.note-carte {
  display: inline-flex; align-items: center; gap: 6px;
  margin-top: 6px;
}
.note-carte .etoiles { gap: 1px; }
.note-carte-chiffre { font-size: .78rem; font-weight: 600; color: var(--text-soft); }
.note-carte-n { font-size: .72rem; color: var(--muted); font-family: var(--font-mono); }

/* ====== les déclinaisons d'une pièce =======================================

   Des liens, pas des pastilles de couleur : le catalogue vend aussi des longueurs de câble,
   des capacités et des qualités, et un rond coloré ne sait dire que la couleur. Le mot dit
   tout, et il se lit. */

.declinaisons { margin-top: 20px; }
.declinaisons-titre {
  display: block; margin-bottom: 8px;
  font-size: .78rem; color: var(--muted);
}
.declinaisons-liste { display: flex; flex-wrap: wrap; gap: 6px; }
.decl {
  display: inline-flex; align-items: baseline; gap: 6px;
  padding: 6px 10px;
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--surface);
  font-size: .84rem; color: var(--text-soft); text-decoration: none;
  transition: border-color .15s var(--ease), color .15s var(--ease);
}
.decl:hover { border-color: var(--violet); color: var(--text); }
.decl i { font-style: normal; font-size: .76rem; color: var(--muted); font-variant-numeric: tabular-nums; }
.decl.est-courante {
  border-color: var(--violet); color: var(--text); font-weight: 600;
  background: var(--gradient-soft);
}
/* Une déclinaison épuisée reste visible et cliquable : sa fiche dit quand elle revient, et
   la masquer donnerait l'impression qu'elle n'existe pas. */
.decl.est-epuise { opacity: .55; }
