/* ==========================================================================
   Colombes Cycles · Design System
   Police display : Fraunces (serif éditoriale) · Police UI : Inter
   ========================================================================== */

:root {
  --bg: #F6F8F2;
  --paper: #FFFFFF;
  --ink: #14170F;
  --ink-soft: #4A5340;
  --ink-faint: #66705C;    /* 4,86:1 sur --bg : lisible en petit corps */
  --line: rgba(20, 23, 15, 0.12);
  --line-strong: rgba(20, 23, 15, 0.22);
  --line-control: rgba(20, 23, 15, 0.55);  /* bordure des contrôles : 3:1 requis */

  --accent: #5CA727;       /* décor SEULEMENT : bordures et hovers.
                              2,80:1 sur --bg : ne jamais l'utiliser pour du texte,
                              une icône porteuse de sens, ni un fond sous du clair.
                              ::selection l'utilisait justement en fond sous du
                              blanc (2,99:1) : la règle enfreignait ce commentaire. */
  --accent-dark: #3A7317;  /* le vert qui a le droit de porter du texte.
                              La surface la plus serrée est --accent-soft, pas
                              --bg : à #3E7A18 le duo .price-legend tombait à
                              4,45:1. Ici 4,89:1 sur --accent-soft, 5,39:1 sur
                              --bg, 5,37:1 sous --on-accent. Ne pas éclaircir. */
  --accent-soft: #E3F1D6;
  --accent-light: #8FD65A; /* accents sur fonds sombres */
  --green: #2F6B1E;        /* vert course Peugeot/Gitane */
  --green-soft: #DCEBD2;

  /* Teintes posées sur les fonds sombres (header, footer, bande marques) */
  --on-dark-rgb: 233 240 224;
  --on-accent: #F5F8F1;

  --radius-m: 18px;
  --radius-l: 28px;
  --radius-full: 999px;

  --shadow-sm: 0 2px 10px rgba(20, 23, 15, 0.06);
  --shadow-md: 0 10px 30px rgba(20, 23, 15, 0.10);
  --shadow-lg: 0 24px 60px rgba(20, 23, 15, 0.16);

  /* Échelle d'espacement, base 4. Toute valeur d'espacement du site s'y rattache. */
  --s-hair: 2px;
  --s-1: 4px;
  --s-2: 8px;
  --s-3: 12px;
  --s-4: 16px;
  --s-5: 20px;
  --s-6: 24px;
  --s-7: 32px;
  --s-8: 40px;
  --s-9: 56px;

  /* Étoile pleine, répétée à l'horizontale : sert de masque à .stars. */
  --star-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath d='M10 1.6l2.58 5.23 5.77.84-4.18 4.07.99 5.75L10 14.77l-5.16 2.72.99-5.75L1.65 7.67l5.77-.84z'/%3E%3C/svg%3E") repeat-x left center;

  /* Micro-typographie : 5 pas au lieu de 10 valeurs ad hoc (0,62 / 0,72 /
     0,78 / 0,80 / 0,82 / 0,85 / 0,88 / 0,90 / 0,92 / 0,95 rem), qui tenaient
     dans une bande de 5,3 px avec des marches de 0,32 px : donc pas une
     échelle, un empilement. Progression régulière de ~1,08 : resserrée, ce
     qui convient à de la micro-typographie, mais constante. */
  --fs-micro: 0.6875rem;  /* 11 px : baseline du logo, seul emploi */
  --fs-xs: 0.75rem;       /* 12 px : étiquettes, notes, pied de page */
  --fs-sm: 0.8125rem;     /* 13 px : fil d'Ariane, légendes */
  --fs-md: 0.875rem;      /* 14 px : nav, cartes, listes */
  --fs-lg: 0.9375rem;     /* 15 px : boutons, corps des témoignages */

  --container: 1180px;
  --ff-display: "Fraunces", "Fraunces Fallback", "Iowan Old Style", serif;
  --ff-body: "Inter", "Inter Fallback", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}

/* ---------- Reset ---------- */
*, *::before, *::after { box-sizing: border-box; }
/* 96 px couvre la plus haute valeur de l'en-tete collant (84 px au-dela de
   578 px, 77 px en deca) plus une marge. A resynchroniser des que la hauteur
   de l'en-tete change, sinon les ancres inter-pages du pied atterrissent
   derriere lui. */
html { scroll-behavior: smooth; scroll-padding-top: 96px; }
body, h1, h2, h3, h4, p, figure, blockquote, dl, dd { margin: 0; }
ul[class], ol[class] { list-style: none; margin: 0; padding: 0; }
img, picture, svg { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }
input, textarea, select { font: inherit; }

body {
  background: var(--bg);
  color: var(--ink);
  font-family: var(--ff-body);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  position: relative;
}

/* subtle grain texture over the warm paper background */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  opacity: 0.5;
  /* Pas de mix-blend-mode : la texture est du NOIR pur à alpha 0,035, et
     multiply(0, fond) vaut 0, soit exactement ce que donne la composition
     normale. Mesuré sur captures avant/après : 12 % des pixels diffèrent,
     d'au plus 1/255 par canal, un arrondi. La propriété ne coûtait donc
     qu'une couche de compositing plein écran, sans rien apporter. */
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='120' height='120'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/><feColorMatrix type='matrix' values='0 0 0 0 0  0 0 0 0 0  0 0 0 0 0  0 0 0 0.035 0'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>");
}

main, header, footer { position: relative; z-index: 1; }

h1, h2, h3, h4 {
  font-family: var(--ff-display);
  font-weight: 600;
  line-height: 1.08;
  letter-spacing: -0.01em;
  color: var(--ink);
}

h1 { font-size: clamp(2.6rem, 5.4vw, 4.6rem); font-weight: 500; }
h2 { font-size: clamp(2rem, 3.6vw, 3rem); }
h3 { font-size: clamp(1.3rem, 2vw, 1.7rem); }

/* <sup> n'avait aucune règle : il héritait du `font-size: smaller` de l'agent
   utilisateur, un facteur RELATIF au parent. Le même ordinal « 1re » sortait
   donc à 10 px, 13,33 px et 13,60 px selon l'endroit. Un facteur en `em` est
   le bon choix (un ordinal doit suivre la taille de son texte), mais il
   tombait à 8,40 px dans une pilule de 12 px, illisible : d'où le plancher.
   line-height:0 l'empêche d'enfler la boîte de ligne. */
sup { font-size: max(0.7em, 0.625rem); line-height: 0; }

/* Longueurs de ligne équilibrées sur les titres, moins d'orphelines dans la prose. */
h1, h2, h3 { text-wrap: balance; }
p, li { text-wrap: pretty; }

/* Un paragraphe nu occupait toute la largeur du conteneur, soit ~140 caractères
   à 1280px. La mesure confortable est 65-75 ; on plafonne à 68ch. */
main p:not([class]) { max-width: 68ch; }

/* Le reset met les marges à zéro : on restitue les écarts titre → corps ET
   paragraphe → paragraphe, qui se touchaient à 0px sur 5 pages. */
h1 + p, h2 + p, h3 + p, h4 + p, h1 + .lede, h2 + .lede, h3 + .lede { margin-top: var(--s-4); }
p + p, .lede + p, p + .lede, ul + p, p + ul { margin-top: var(--s-4); }

.lede {
  /* Le clamp restait collé à son minimum jusqu'à 1200 px : 16,80 px contre
     16 px de corps, soit un rapport de 1,05, invisible comme marche de
     hiérarchie. Il faut au moins 1,15 pour que l'œil distingue le chapô du
     texte courant. Ici 1,15 sur mobile, 1,25 au-delà de ~910 px. */
  font-size: clamp(1.15rem, 2.2vw, 1.25rem);
  color: var(--ink-soft);
  max-width: 46ch;
}

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent-dark);
}

.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--s-6);
}

section { padding-block: clamp(56px, 9vw, 112px); }

.section-head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, auto);
  align-items: end;
  gap: var(--s-6);
  margin-bottom: clamp(32px, 5vw, 56px);
}
@media (max-width: 900px) { .section-head { grid-template-columns: 1fr; } }
.section-head h2 { max-width: 18ch; }

/* ---------- Buttons ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-3) var(--s-6);
  border-radius: var(--radius-full);
  font-weight: 600;
  font-size: var(--fs-lg);
  white-space: nowrap;
  transition: transform 0.25s ease, box-shadow 0.25s ease, background 0.25s ease, color 0.25s ease;
}
.btn svg { width: 18px; height: 18px; flex-shrink: 0; }
.btn-primary {
  background: var(--accent-dark);   /* 4,89:1 ; --accent ne donnait que 2,79:1 */
  color: var(--on-accent);
  box-shadow: var(--shadow-sm);
}
.btn-primary:hover { background: var(--green); transform: translateY(-2px); box-shadow: var(--shadow-md); }
.btn-dark {
  background: var(--ink);
  color: var(--bg);
}
.btn-dark:hover { background: var(--green); transform: translateY(-2px); box-shadow: var(--shadow-md); }
.btn-ghost {
  background: transparent;
  border: 1.5px solid var(--line-control);
  color: var(--ink);
}
.btn-ghost:hover { border-color: var(--ink); background: var(--paper); }

.link-arrow {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  font-weight: 600;
  color: var(--ink);
  border-bottom: 1.5px solid var(--line-strong);
  padding-bottom: var(--s-hair);
  transition: border-color 0.2s ease, gap 0.2s ease, color .2s ease;
}
.link-arrow svg { width: 16px; height: 16px; transition: transform 0.2s ease; }
.link-arrow:hover { border-color: var(--accent); color: var(--accent-dark); }
.link-arrow:hover svg { transform: translateX(4px); }

/* ---------- Header / Nav ---------- */
.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: rgb(var(--on-dark-rgb) / 0.82);
  backdrop-filter: blur(14px) saturate(140%);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
  border-bottom: 1px solid transparent;
  transition: border-color 0.3s ease, box-shadow 0.3s ease;
}
.site-header.is-scrolled { border-color: var(--line); box-shadow: 0 4px 24px rgba(20, 23, 15,0.06); }

/* ---------- En-tete compact au scroll ----------
   A partir de 578 px seulement : en deca la baseline est deja masquee et
   l'en-tete au minimum, il n'y aurait rien a replier. Tout est scope a
   .site-header, car .brand et .brand-mark existent aussi dans le pied de page.
   Ce qui se replie est la baseline du logo : c'est la seule chose de l'en-tete
   dont on n'a plus besoin une fois qu'on lit. */
@media (min-width: 578px) {
  .site-header .navbar { transition: padding-block 0.28s cubic-bezier(0.22, 1, 0.36, 1); }
  .site-header .brand .brand-mark { transition: width 0.28s cubic-bezier(0.22, 1, 0.36, 1), height 0.28s cubic-bezier(0.22, 1, 0.36, 1); }
  .site-header .brand small {
    max-height: 2em;   /* relatif a --fs-micro : couvre la ligne sans la brider */
    overflow: hidden;
    transition: max-height 0.28s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.18s ease;
  }
  .site-header.is-compact .navbar { padding-block: var(--s-2); }
  .site-header.is-compact .brand .brand-mark { width: 32px; height: 32px; }
  .site-header.is-compact .brand small { max-height: 0; opacity: 0; }
}

/* Le repli reste, l'animation disparait : le mouvement n'est pas l'information. */
@media (prefers-reduced-motion: reduce) {
  .site-header .navbar,
  .site-header .brand .brand-mark,
  .site-header .brand small { transition: none; }
}

.navbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-6);
  padding-block: var(--s-4);
}

.brand {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  font-family: var(--ff-display);
  font-size: 1.3rem;
  font-weight: 600;
  letter-spacing: -0.01em;
}
.brand .brand-mark {
  width: 38px; height: 38px;
  border-radius: 50%;
  background: var(--accent-dark);
  display: grid; place-items: center;
  color: var(--on-accent);
  flex-shrink: 0;
}
.brand .brand-mark svg,
.brand .brand-mark img { width: 24px; height: 24px; object-fit: contain; }
/* --ink-faint tenait 4,51:1 sur l'en-tête posé au-dessus d'une page claire, mais
   l'en-tête est translucide (0,82) : dès qu'une section sombre défile dessous, le
   composite tombe à rgb(195,201,186) et la baseline avec lui, à 3,07:1. --ink-soft
   tient les deux cas : 7,00:1 sur clair, 4,76:1 sur sombre. */
.brand small { display: block; font-family: var(--ff-body); font-size: var(--fs-micro); font-weight: 600; letter-spacing: 0.12em; text-transform: uppercase; color: var(--ink-soft); }

.nav-links {
  display: flex;
  align-items: center;
  gap: var(--s-1);
}
.nav-links a {
  padding: var(--s-2) var(--s-4);
  border-radius: var(--radius-full);
  font-weight: 600;
  font-size: var(--fs-md);
  color: var(--ink-soft);
  transition: background 0.2s ease, color 0.2s ease;
}
.nav-links a:hover { background: var(--paper); color: var(--ink); box-shadow: var(--shadow-sm); }
/* « Vous êtes ici » doit se distinguer du simple survol : couleur + soulignement. */
.nav-links a[aria-current="page"] {
  background: var(--paper);
  color: var(--ink);
  box-shadow: var(--shadow-sm), inset 0 -2px 0 var(--accent-dark);
}
.mobile-panel a[aria-current="page"] { color: var(--accent-dark); box-shadow: inset 2px 0 0 var(--accent-dark); padding-left: var(--s-3); }

.nav-actions { display: flex; align-items: center; gap: var(--s-2); }

.nav-toggle {
  display: none;
  width: 44px; height: 44px;
  border-radius: var(--radius-full);
  border: 1.5px solid var(--line-control);
  align-items: center;
  justify-content: center;
}
.nav-toggle svg { width: 20px; height: 20px; }

.mobile-panel {
  display: none;
  flex-direction: column;
  gap: var(--s-1);
  padding: var(--s-2) 24px 24px;
}
.mobile-panel a {
  padding: var(--s-3) var(--s-1);
  border-bottom: 1px solid var(--line);
  font-weight: 600;
}
/* ---------- Point de rupture de la navigation ----------
   1075 px est la derniere largeur a laquelle .nav-links ne tient PAS sur une
   ligne (mesure : 61 px de haut jusque-la, 38 px a partir de 1076). En deca,
   le menu horizontal se replie et entraine le logo avec lui, ce qui double la
   hauteur de l'en-tete. Le burger reste donc en place jusqu'a cette largeur.
   A remesurer si le nombre d'entrees du menu ou la largeur du logo change. */
@media (max-width: 1075px) { .mobile-panel.is-open { display: flex; } }

@media (max-width: 1075px) {
  .nav-links { display: none; }
  .nav-toggle { display: inline-flex; flex-shrink: 0; }
}
/* Masque le libellé visuellement mais le laisse dans l'arbre d'accessibilité. */
.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* En-tete compact. 577 px est la derniere largeur a laquelle la baseline du
   logo (« VÉLOS & RÉPARATIONS DEPUIS 2016 ») se replie en deux lignes, ce qui
   ferait passer l'en-tete de 77 a 101 px. En deca, elle est donc masquee.
   A remesurer si le texte de la baseline change. */
@media (max-width: 577px) {
  .navbar { flex-wrap: nowrap; gap: var(--s-2); }
  .brand { min-width: 0; flex-shrink: 1; }
  .brand small { display: none; }
  /* Scopé au header : sur .footer-brand ce nowrap gonflait la grille → scroll latéral à 320px. */
  .navbar .brand span { font-size: 1.05rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .brand .brand-mark { width: 34px; height: 34px; }
  .nav-actions { flex-shrink: 0; gap: var(--s-2); }
  .nav-actions .btn-primary .btn-label {
    position: absolute;
    width: 1px; height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
  .nav-actions .btn-primary { padding: var(--s-3); }  /* 44px, aligné sur le hamburger */
  .nav-actions .btn-primary svg { margin: 0; }
}

/* ---------- Hero ---------- */
.hero {
  padding-top: clamp(48px, 8vw, 88px);
  padding-bottom: clamp(40px, 6vw, 72px);
  position: relative;
  overflow: hidden;
}
.hero-grid {
  display: grid;
  grid-template-columns: 1.1fr 0.9fr;
  gap: clamp(32px, 6vw, 64px);
  align-items: center;
}
@media (max-width: 900px) {
  .hero-grid { grid-template-columns: 1fr; }
  .hero-visual-col { width: 100%; max-width: 420px; margin-inline: auto; }
  .hero-chips-row { justify-content: center; }
}
.hero-copy h1 { margin-bottom: var(--s-5); }
.hero-copy h1 em { font-style: italic; color: var(--accent-dark); }
.hero-actions { display: flex; flex-wrap: wrap; gap: var(--s-3); margin-top: var(--s-7); }

.hero-stats {
  display: flex;
  gap: var(--s-7);
  margin-top: 44px;
  flex-wrap: wrap;
}
.hero-stats .stat b { display: block; font-family: var(--ff-display); font-size: 1.8rem; color: var(--ink); }
.hero-stats .stat span { font-size: var(--fs-sm); color: var(--ink-faint); }

.hero-visual {
  position: relative;
  aspect-ratio: 16 / 9;
  border-radius: var(--radius-l);
  background: linear-gradient(155deg, var(--green) 0%, #0A0D07 100%);
  overflow: hidden;
  box-shadow: var(--shadow-lg);
}
.hero-visual svg { width: 100%; height: 100%; }
.hero-photo { width: 100%; height: 100%; object-fit: contain; display: block; }
.hero-visual::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(180deg, rgba(20,23,15,0) 42%, rgba(20,23,15,0.62) 100%);
  pointer-events: none;
}
.hero-badge {
  position: absolute;
  z-index: 2;
  bottom: 14px; left: 14px; right: 14px;
  background: rgba(255,253,248,0.95);
  border-radius: var(--radius-m);
  padding: var(--s-3) var(--s-4);
  display: flex;
  align-items: center;
  gap: var(--s-3);
  box-shadow: var(--shadow-md);
}
.hero-badge .stars { color: var(--accent-dark); font-size: var(--fs-lg); letter-spacing: 1px; }
.hero-badge b { font-family: var(--ff-display); font-size: 1.15rem; line-height: 1.2; display: block; }
.hero-badge span { font-size: var(--fs-xs); color: var(--ink-faint); display: block; }
.hero-badge-compact { padding: var(--s-2) var(--s-3); gap: var(--s-2); }
.hero-badge-compact b { font-size: 1rem; }

.hero-chips-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2);
  margin-top: var(--s-4);
}
.mini-pill {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius-full);
  padding: var(--s-2) var(--s-3);
  font-weight: 700;
  font-size: var(--fs-xs);
  box-shadow: var(--shadow-sm);
}
.mini-pill svg { width: 16px; height: 16px; color: var(--accent-dark); flex-shrink: 0; }
/* Cette pastille porte une phrase entiere, pas deux mots : sous 400px de large
   elle passe a deux lignes et « boutique » partait seul en veuve. */
.mini-pill.is-sentence span { text-wrap: balance; }

/* ---------- Trust bar ---------- */
.trust-bar {
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  padding-block: var(--s-5);
}
.trust-bar .container {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-5) 40px;
  justify-content: space-between;
  align-items: center;
}
.trust-item { display: flex; align-items: center; gap: var(--s-2); font-size: var(--fs-md); font-weight: 600; color: var(--ink-soft); }
.trust-item svg { width: 20px; height: 20px; color: var(--accent-dark); flex-shrink: 0; }

/* ---------- Category / bento grid ---------- */
.bento {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--s-5);
}
.bento .card-cat:nth-child(1) { grid-column: span 2; grid-row: span 2; }
.bento .card-cat:nth-child(4) { grid-column: span 2; }

.cat-media, .cat-media img {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  z-index: 0;
}
/* Le recadrage vers le bas (84 %) etait cale sur l'ancienne photo, dont le sujet
   etait en bas de cadre. La nouvelle a son velo centre : on revient au centre. */
.cat-3 .cat-media img { object-position: center 46%; }
/* Le voile passe sur un ::before pour se superposer à l'image différée. */
.card-cat::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
}
/* cat-1 porte son texte EN BAS, et sa tuile change de hauteur : 480 px en bento
   desktop, 230 px en pile mobile. Avec le degrade diagonal d'origine le texte
   tombait au milieu de la rampe en mobile, pas sur son extremite sombre — 2,46:1
   pour le titre. Le voile est donc ANCRE EN BAS (`to top`) : la bande sombre est
   la meme quelle que soit la hauteur de la tuile. Mesure au pixel sous chaque
   ligne, texte masque, aux deux tailles. */
.cat-1::before { background: linear-gradient(to top, rgba(20,23,15,.88) 0%, rgba(20,23,15,.72) 34%, rgba(92,167,39,.20) 100%); }
/* cat-2 et cat-3 portent leur titre EN HAUT de la tuile, c'est-a-dire sur
   l'extremite claire du degrade : c'est le premier stop qui les gouverne, pas
   le second. Mesure au pixel sous le titre, texte masque : a .24 le pire etait
   a 2,30:1 pour un seuil de 3,0 (25,6 px / 600 = grand texte). */
.cat-2::before { background: linear-gradient(160deg, rgba(31,77,18,.58) 0%, rgba(20,23,15,.86) 100%); }
.cat-3::before { background: linear-gradient(160deg, rgba(47,107,30,.56) 0%, rgba(6,10,4,.78) 100%); }
.cat-4::before { background: linear-gradient(160deg, rgba(45,52,38,.46) 0%, rgba(20,23,15,.82) 100%); }
.card-cat > :not(.cat-media) { position: relative; z-index: 2; }

.card-cat {
  position: relative;
  border-radius: var(--radius-l);
  /* Réserve la gouttière droite occupée par .cat-icon et .cat-arrow (absolus). */
  padding: var(--s-7) 78px var(--s-7) var(--s-7);
  min-height: 230px;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  overflow: hidden;
  color: var(--on-accent);
  transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.card-cat:hover { transform: translateY(-4px); box-shadow: var(--shadow-lg); }
.card-cat h3 { color: inherit; margin-bottom: var(--s-1); padding-right: var(--s-9); }
.card-cat p { color: rgba(255,248,241,0.95); font-size: var(--fs-md); max-width: 30ch; }
.card-cat .cat-icon {
  position: absolute; top: 24px; right: 24px;
  width: 44px; height: 44px;
  border-radius: 50%;
  background: rgba(255,255,255,0.16);
  display: grid; place-items: center;
}
.card-cat .cat-icon svg { width: 22px; height: 22px; }
.card-cat .cat-arrow {
  position: absolute; bottom: 24px; right: 24px;
  width: 38px; height: 38px;
  border-radius: 50%;
  background: rgba(255,255,255,0.92);
  color: var(--ink);
  display: grid; place-items: center;
  transition: transform 0.3s ease;
}
.card-cat .cat-arrow svg { width: 18px; height: 18px; }
.card-cat:hover .cat-arrow { transform: rotate(45deg); }

/* Alphas abaissés (~.28–.62) : les gradients écrasaient trois photos différentes
   en une même bouillie verte. Un voile bas + sombre garde le texte lisible. */

@media (max-width: 760px) {
  .bento { grid-template-columns: 1fr 1fr; }
  .bento .card-cat:nth-child(1) { grid-column: span 2; grid-row: span 1; }
  .bento .card-cat:nth-child(4) { grid-column: span 2; }
  /* La flèche n'apporte rien au tactile et rognait le texte. */
  .card-cat .cat-arrow { display: none; }
  .card-cat { padding-right: var(--s-7); }
  .card-cat h3 { padding-right: var(--s-9); }
}
@media (max-width: 420px) {
  .bento { grid-template-columns: 1fr; }
  .bento .card-cat:nth-child(1), .bento .card-cat:nth-child(4) { grid-column: span 1; }
}

/* ---------- Generic card ---------- */
.card {
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius-l);
  padding: var(--s-7);
  transition: transform 0.25s ease, box-shadow 0.25s ease;
}
.card:hover { transform: translateY(-3px); box-shadow: var(--shadow-md); }
.card .icon-wrap {
  width: 52px; height: 52px;
  border-radius: var(--radius-m);
  background: var(--accent-soft);
  color: var(--green);
  display: grid; place-items: center;
  margin-bottom: var(--s-4);
}
.card .icon-wrap svg { width: 26px; height: 26px; }

/* Les cartes bordées s'étirent sur la hauteur de leur rangée : `align-items:start`
   y produisait des bas en dents de scie. Les blocs sans bordure gardent l'alignement haut. */
.grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s-5); }
.grid-2 { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--s-5); }
.grid-2:has(> .value-item), .grid-3:has(> .value-item),
.grid-2:has(> .team-card), .grid-3:has(> .team-card) { align-items: start; }
@media (max-width: 900px) { .grid-3 { grid-template-columns: 1fr; } .grid-2 { grid-template-columns: 1fr; } }

/* ---------- Brands strip ---------- */
.brand-strip {
  background: var(--ink);
  color: var(--bg);
}
.brand-strip .section-head h2 { color: var(--bg); }
.brand-strip .lede { color: rgb(var(--on-dark-rgb) / 0.62); }
.brand-marquee {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-3);
}
.brand-pill {
  border: 1px solid rgb(var(--on-dark-rgb) / 0.2);
  border-radius: var(--radius-full);
  padding: var(--s-3) var(--s-6);
  font-family: var(--ff-display);
  font-size: 1.1rem;
  color: var(--bg);
  transition: background .2s ease, border-color .2s ease;
}
.brand-pill:hover { background: rgb(var(--on-dark-rgb) / 0.08); border-color: var(--accent); }

/* ---------- Values ---------- */
/* « Nos valeurs » n'est pas une séquence : les repères 01/02/03 y simulaient un
   ordre qui n'existe pas. Un filet court ouvre le bloc sans mentir sur sa nature. */
.value-item { display: flex; flex-direction: column; gap: var(--s-3); }
.value-item::before {
  content: "";
  width: var(--s-7);
  height: 2px;
  background: var(--accent-dark);
  border-radius: 2px;
}
.value-item h3 { margin-bottom: var(--s-2); }
.value-item p { color: var(--ink-soft); }

/* ---------- Testimonials ---------- */
.testi-track {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--s-5);
}
@media (max-width: 900px) { .testi-track { grid-template-columns: 1fr; } }
.testi-card {
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius-l);
  padding: var(--s-6);
  display: flex;
  flex-direction: column;
  gap: var(--s-3);
}
/* Le glyphe ★ (U+2605) n'existe dans AUCUNE des deux polices sources : les 41
   occurrences tombaient en police système, dont la note 4,8/5 qui porte la
   confiance de l'accueil. Un masque SVG règle le rendu à toute résolution, suit
   `currentColor` donc reste piloté par les tokens, et permet enfin un libellé
   accessible : « ★★★★★ » n'en avait aucun. */
.stars {
  display: inline-block;
  width: 5em;
  height: 1em;
  vertical-align: -0.12em;
  background-color: currentColor;
  -webkit-mask: var(--star-mask);
  mask: var(--star-mask);
  -webkit-mask-size: 1em 1em;
  mask-size: 1em 1em;
}
.stars-1 { width: 1em; }
.testi-stars { color: var(--accent-dark); font-size: var(--fs-md); }
.testi-card p { color: var(--ink-soft); font-size: var(--fs-lg); flex: 1; }
.testi-foot { display: flex; align-items: center; gap: var(--s-2); }
.testi-avatar {
  width: 38px; height: 38px; border-radius: 50%;
  background: var(--green-soft); color: var(--green);
  display: grid; place-items: center; font-weight: 700; font-family: var(--ff-display);
  flex-shrink: 0;
}
.testi-foot b { font-size: var(--fs-md); }
/* Scopé au bloc nom/qualité : `.testi-foot span` écrasait le display:grid de .testi-avatar. */
.testi-foot > div > span { font-size: var(--fs-xs); color: var(--ink-faint); display: block; }

/* ---------- Split / feature rows ---------- */
.split {
  display: grid;
  grid-template-columns: 0.85fr 1.15fr;
  gap: clamp(28px, 6vw, 72px);
  align-items: center;
}
/* align-items:center n'a de sens que face à une image, dont la hauteur est
   arbitraire. Un .split SANS visuel juxtapose deux colonnes de texte : le
   centrage faisait alors démarrer la colonne courte 210 px plus bas que la
   longue, sans rien pour justifier ce décalage. Un seul bloc du site est dans
   ce cas, « Comment se passe un achat » sur velos.html. Les navigateurs sans
   :has() ignorent la règle et retrouvent l'ancien comportement. */
.split:not(:has(.split-visual)) { align-items: start; }
.split.reverse { grid-template-columns: 1.15fr 0.85fr; }
.split.reverse .split-visual { order: 2; }
.split-visual {
  position: relative;
  width: 100%;
  border-radius: var(--radius-l);
  overflow: hidden;
  min-height: 320px;
  aspect-ratio: 4 / 3;   /* le même composant se recadrait en 455×344 et 440×420 */
  box-shadow: var(--shadow-md);
}
.split-visual svg { width: 100%; height: 100%; }
/* Un seul bloc 900px, placé APRÈS la base .split-visual pour garder la main. */
@media (max-width: 900px) {
  .split, .split.reverse { grid-template-columns: 1fr; }
  .split.reverse .split-visual { order: 0; }
  .split-visual { min-height: 280px; }
}

/* Cadre portrait pour un tirage qui l'est aussi. Le 4/3 par defaut coupait le
   velo du jeune coureur : en 946 px de large, tenir le casque ET le guidon
   demande 1182 px de haut, ce qu'aucun format paysage ne peut contenir. */
.split-visual.is-portrait { aspect-ratio: 4 / 5; }
.split-photo { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; }
.split-visual::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
}
.split-visual.tint-accent::before { background: linear-gradient(160deg, rgba(92,167,39,.28) 0%, rgba(20,23,15,.6) 100%); }
.split-visual.tint-ink::before { background: linear-gradient(160deg, rgba(20,23,15,.3) 0%, rgba(6,10,4,.62) 100%); }
.split-visual.tint-forest::before { background: linear-gradient(160deg, rgba(47,107,30,.28) 0%, rgba(31,77,18,.6) 100%); }

.split-tag {
  position: absolute;
  top: 22px;
  left: 22px;
  z-index: 2;
  /* Fond sombre opaque : le voile blanc à .14 tombait à 2,51:1 sur les photos claires. */
  background: rgba(20, 23, 15, 0.72);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border: 1px solid rgba(255,255,255,0.28);
  color: #FFFFFF;
  font-weight: 700;
  font-size: var(--fs-xs);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  padding: var(--s-2) var(--s-4);
  border-radius: var(--radius-full);
}

/* `ul.` et pas `.` : le reset `ul[class]` vaut 0-1-1 en spécificité, un sélecteur
   d'attribut comptant comme une classe. Une règle à classe unique (0-1-0) perd
   donc contre son `margin: 0`, et la marge haute tombait silencieusement à zéro —
   le titre se retrouvait collé à sa première puce. Même piège pour .cp-list.
   Toute nouvelle liste classée qui veut une marge doit être préfixée pareil. */
ul.check-list { display: flex; flex-direction: column; gap: var(--s-3); margin-top: var(--s-6); }
.check-list li { display: flex; align-items: flex-start; gap: var(--s-3); }
.check-list svg { width: 20px; height: 20px; color: var(--accent-dark); flex-shrink: 0; margin-top: var(--s-hair); }

/* ---------- Steps ---------- */
.steps { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--s-5); counter-reset: step; }
@media (max-width: 900px) { .steps { grid-template-columns: 1fr 1fr; } }
@media (max-width: 560px) { .steps { grid-template-columns: 1fr; } }
.step { position: relative; padding-top: var(--s-1); }
.step .step-num {
  font-family: var(--ff-display);
  font-size: 2.4rem;
  -webkit-text-stroke: 1.5px var(--accent-dark);
  color: transparent;
  display: block;
  margin-bottom: var(--s-2);
}
.step h3 { margin-bottom: var(--s-2); font-size: 1.15rem; }
.step p { color: var(--ink-soft); font-size: var(--fs-md); }

/* ---------- FAQ ---------- */
.faq-list { display: flex; flex-direction: column; gap: var(--s-3); }
.faq-item {
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius-m);
  overflow: hidden;
}
.faq-q {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4);
  padding: var(--s-5) 24px;
  text-align: left;
  font-weight: 600;
  font-size: 1.02rem;
  transition: background 0.2s ease;
}
/* Le libellé est encapsulé pour ne former QU'UN item flex. Sans ce span, les
   nœuds de texte autour du <sup> de « 1re » devenaient des items anonymes
   distincts, que `justify-content: space-between` écartait sur toute la
   largeur du bouton : « En quoi consiste la 1 … re … révision offerte ». */
.faq-q-label { flex: 1; }

.faq-q:hover { background: var(--accent-soft); }
/* .faq-item a overflow:hidden : un outline-offset positif est clippé à 100 %.
   En négatif, l'anneau se dessine à l'intérieur du bouton et reste visible. */
.faq-q:focus-visible { outline-offset: -3px; }
/* Le <h3> enveloppe le <button> (et non l'inverse) : ARIA rend les enfants d'un
   bouton présentationnels, donc un titre imbriqué dedans n'est pas fiable.
   Le <h3> ne porte aucun style : tout est sur le bouton. */
.faq-q-title { font: inherit; color: inherit; margin: 0; letter-spacing: normal; }
.faq-q svg { width: 20px; height: 20px; color: var(--accent-dark); flex-shrink: 0; transition: transform 0.25s ease; }
.faq-item.is-open .faq-q svg { transform: rotate(45deg); }
/* [hidden] sort la réponse repliée du tab order ET de l'arbre d'accessibilité ; 
   max-height:0 seul la laissait focusable et lue par les lecteurs d'écran. */
/* Les réponses sont livrées DÉPLIÉES dans le HTML : 576 des 789 mots de la FAQ
   sont là, donc trouvables au Ctrl+F, lisibles sans JS et présents pour les
   moteurs. C'est le JS qui les replie, comme pour .reveal, et le même minuteur
   de secours du <head> les redonne si main.js n'a pas démarré. Ne pas remettre
   d'attribut `hidden` dans le HTML : il survivrait au filet. */
html.js .faq-a { display: none; }
html.js .faq-item.is-open .faq-a { display: block; }
.faq-a { color: var(--ink-soft); }
.faq-a p, .faq-a ul { padding: 0 24px 22px; }
.faq-a ul { padding-left: var(--s-8); list-style: disc; display: flex; flex-direction: column; gap: var(--s-1); }
/* Un lien noyé dans un paragraphe doit se distinguer AUTREMENT que par la
   couleur (SC 1.4.1, niveau A). Sept d'entre eux étaient à 1,00-1,09:1 du
   texte qui les entoure, sans soulignement : rien n'indiquait qu'ils étaient
   cliquables. Les liens qui portent déjà leur propre affordance (.btn et
   .link-arrow) sont exclus par :not([class]). */
main p a:not([class]),
main li a:not([class]) {
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.18em;
  text-decoration-color: var(--line-control);
}
main p a:not([class]):hover,
main li a:not([class]):hover { text-decoration-color: currentColor; }

.faq-a a { text-decoration: underline; }

/* ---------- Contact / map ---------- */
.contact-panel {
  background: var(--ink);
  color: var(--bg);
  border-radius: var(--radius-l);
  overflow: hidden;
  display: grid;
  grid-template-columns: 1fr 1fr;
}
@media (max-width: 900px) { .contact-panel { grid-template-columns: 1fr; } }
.contact-panel .cp-info { padding: clamp(28px, 5vw, 52px); }
.contact-panel h2 { color: var(--bg); }
.contact-panel .lede { color: rgb(var(--on-dark-rgb) / 0.6); }
/* `ul.` obligatoire, même raison que .check-list : sans ça le reset `ul[class]`
   l'emporte et la marge haute vaut 0. */
ul.cp-list { margin-top: var(--s-7); display: flex; flex-direction: column; gap: var(--s-5); }
.cp-list li { display: flex; gap: var(--s-3); align-items: flex-start; }
.cp-list svg { width: 22px; height: 22px; color: var(--accent-dark); flex-shrink: 0; margin-top: var(--s-hair); }
.cp-list b { display: block; margin-bottom: var(--s-hair); }
.cp-list span, .cp-list a { color: rgb(var(--on-dark-rgb) / 0.68); font-size: var(--fs-md); }
/* Le plan est une image auto-hebergee, tuiles OpenStreetMap : il s'affiche
   d'emblee sans qu'aucune requete ne parte vers un tiers, donc sans
   consentement a demander. Le clic ouvre l'itineraire chez Google : la
   transmission de donnees n'a lieu qu'apres cette action deliberee, ce que la
   page « mentions legales » decrit deja. */
.cp-map { min-height: 340px; position: relative; }
.map-static { position: absolute; inset: 0; display: block; overflow: hidden; background: var(--green-soft); }
.map-static picture, .map-static img { width: 100%; height: 100%; }
.map-static img { object-fit: cover; filter: grayscale(0.18) contrast(1.03); transition: transform 0.4s cubic-bezier(0.22, 1, 0.36, 1), filter 0.3s ease; }
.map-static:hover img, .map-static:focus-visible img { transform: scale(1.03); filter: grayscale(0) contrast(1.05); }
.map-static-cta {
  position: absolute; left: var(--s-4); bottom: var(--s-4);
  display: inline-flex; align-items: center; gap: var(--s-2);
  padding: var(--s-3) var(--s-5);
  border-radius: var(--radius-full);
  background: var(--paper); color: var(--ink);
  font-size: var(--fs-lg); font-weight: 600;
  box-shadow: var(--shadow-md);
}
/* Attribution ODbL : obligatoire. Fond blanc a 0,82 pour laisser deviner le
   plan dessous ; mesure au pire pixel, texte masque : 10,2:1, tres au-dessus
   du 4,5:1 requis. A remesurer si le plan change. */
.map-credit {
  position: absolute; right: var(--s-2); bottom: var(--s-2);
  margin: 0; padding: 2px var(--s-2);
  border-radius: var(--radius-full);
  background: rgb(255 255 255 / 0.82);
  font-size: var(--fs-micro); color: var(--ink);
}
.map-credit a { text-decoration: underline; }
@media (prefers-reduced-motion: reduce) {
  .map-static img { transition: none; }
  .map-static:hover img, .map-static:focus-visible img { transform: none; }
}

/* La même table sert sur le panneau sombre (accueil, contact) et sur le fond
   clair des mentions légales : le filet se dérive de currentColor plutôt que
   d'une couleur « on-dark » fixe, qui y tombait à 1:1 et disparaissait.
   Le libellé de jour est un <th scope="row"> ; sans ces règles il hérite du
   gras centré de l'agent utilisateur et perd sa bordure de séparation. */
.hours-table { width: 100%; border-collapse: collapse; margin-top: var(--s-4); }
.hours-table th,
.hours-table td {
  padding: var(--s-2) 0;
  border-bottom: 1px solid color-mix(in srgb, currentColor 16%, transparent);
  font-size: var(--fs-md);
  text-align: left;
  font-weight: 400;
}
.hours-table td:last-child { text-align: right; font-weight: 600; }

/* ---------- Price list (tarifs) ---------- */
.price-card { display: flex; flex-direction: column; }
.price-card h3 { margin-bottom: var(--s-1); }
/* Un vrai tableau : bénéfice cumulé lecteurs d'écran + moteurs de réponse. */
.price-list {
  margin-top: var(--s-3);
  max-width: 560px;
  width: 100%;
  border-collapse: collapse;
  font-size: var(--fs-md);
}
.price-list thead th {
  text-align: left;
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-faint);
  padding-bottom: var(--s-2);
  border-bottom: 1px solid var(--line-strong);
}
.price-list thead th:last-child { text-align: right; }
.price-list td, .price-list tbody th {
  padding: var(--s-2) 0;
  border-bottom: 1px solid var(--line);
}
.price-list tbody tr:last-child td,
.price-list tbody tr:last-child th { border-bottom: 0; }
.price-list .price-label { color: var(--ink-soft); font-weight: 400; text-align: left; }
.price-list .price-value { font-weight: 700; color: var(--ink); white-space: nowrap; text-align: right; }
.price-note { font-size: var(--fs-xs); color: var(--ink-faint); margin-top: var(--s-hair); }
.price-legend {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  background: var(--accent-soft);
  color: var(--accent-dark);
  border-radius: var(--radius-m);
  padding: var(--s-4) 20px;
  font-size: var(--fs-md);
  font-weight: 600;
}
.price-legend svg { width: 20px; height: 20px; flex-shrink: 0; }

/* ---------- CTA banner ---------- */
.cta-banner {
  /* Aplat, pas dégradé. Les deux arrêts n'étaient distants que de ΔE76 = 6,9
     étalés sur 1132 px : indiscernable d'un aplat, donc un dégradé décoratif
     qui ne décore rien, et PRODUCT.md:59 les interdit explicitement.
     --accent-dark tient 5,37:1 sous --on-accent, comme le premier arrêt. */
  background: var(--accent-dark);
  border-radius: var(--radius-l);
  padding: clamp(36px, 6vw, 64px);
  color: var(--on-accent);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-6);
}
.cta-banner h2 { color: var(--on-accent); max-width: 16ch; }
.cta-banner .btn-dark { background: var(--on-accent); color: var(--accent-dark); }
.cta-banner .btn-dark:hover { background: var(--ink); color: var(--on-accent); }

/* ---------- Page header (inner pages) ---------- */
/* Le bas fait doublon avec le padding haut de la section suivante : 40px n'y
   ajoutaient rien de lisible et coûtaient un débordement sur un portable 720px. */
.page-header { padding-block: clamp(40px, 8vw, 84px) clamp(20px, 3vw, 32px); }

/* Tout s'empilait dans une seule colonne : titre pleine largeur, chapô à 46ch,
   prose à 68ch, boutons en bas. À 1440px la moitié droite du bloc restait vide,
   trois bords droits différents se succédaient, et les boutons tombaient sous
   la ligne de flottaison. Le titre garde la pleine largeur ; le chapô et la
   prose d'appoint passent côte à côte. Aucun texte n'est supprimé, et sur
   mobile la pile remet les boutons AVANT la prose, donc plus haut.
   Le :has écarte 404.html, dont le .page-header enveloppe un .container. */
.page-header:has(> h1) {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  column-gap: clamp(32px, 5vw, 72px);
  row-gap: var(--s-7);
  align-items: start;
}
.page-header > .breadcrumb,
.page-header > h1 { grid-column: 1 / -1; }
/* row-gap porte déjà les écarts : les marges de rythme héritées feraient
   doublon. Sans ça, les pages sans colonne d'appoint — où le chapô reste le
   frère direct du titre — affichaient 48px quand les autres en montrent 32. */
.page-header:has(> h1) > .breadcrumb { margin-bottom: 0; }
.page-header:has(> h1) > h1 + .lede { margin-top: 0; }
/* Au-delà de ~22 caractères, une ligne de titre à 73px se suit mal. */
.page-header > h1 { max-width: 22ch; }
/* La colonne porte la mesure. Les 46ch du chapô et les 68ch de la prose
   imposaient leur propre largeur À L'INTÉRIEUR d'une colonne déjà étroite,
   d'où les bords irréguliers. */
.ph-lead > .lede,
.ph-aside > p { max-width: none; }
/* La prose d'appoint s'affichait plus foncée que le chapô qu'elle complète :
   hiérarchie inversée, le secondaire pesait plus lourd que le principal. */
.ph-aside > p { color: var(--ink-soft); }
@media (max-width: 900px) {
  /* En pile, le même écart se répète à chaque rang au lieu d'une seule fois
     sous le titre : à 32px il rallongeait le bloc de presque cent pixels. */
  .page-header:has(> h1) { grid-template-columns: 1fr; row-gap: var(--s-5); }
}

.breadcrumb { display: flex; gap: var(--s-2); align-items: center; font-size: var(--fs-sm); color: var(--ink-faint); margin-bottom: var(--s-4); }
.breadcrumb a:hover { color: var(--accent-dark); }

/* ---------- Timeline (a-propos) ---------- */
.timeline { display: flex; flex-direction: column; gap: 0; border-left: 2px solid var(--line-strong); margin-left: var(--s-2); }
.timeline-item { position: relative; padding: 0 0 40px 32px; }
.timeline-item:last-child { padding-bottom: 0; }
.timeline-item::before {
  content: "";
  position: absolute; left: -7px; top: 4px;
  width: 12px; height: 12px; border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 4px var(--bg);
}
.timeline-item b { display: block; color: var(--accent-dark); font-size: var(--fs-sm); font-weight: 700; letter-spacing: 0.04em; margin-bottom: var(--s-1); }
.timeline-item p { color: var(--ink-soft); }

.team-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--s-6) var(--s-5); justify-items: center; }
@media (max-width: 760px) { .team-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.team-card { text-align: center; max-width: 22ch; }
/* Le <picture> est inline par défaut : sans display:block, le margin auto de
   l'image ne centre rien et la pastille se colle à gauche de sa colonne. */
.team-card picture { display: block; }
/* Les quatre membres ont désormais leur portrait ; la pastille-lettre qui
   tenait la place de François a disparu avec sa règle. */
.team-photo {
  /* height:auto est obligatoire, pas décoratif : les attributs width/height de
     l'image (400×400, gardés pour réserver la place et éviter le décalage) sont
     des hints de présentation, et sans déclaration CSS de height c'est le 400
     qui gagne — aspect-ratio ne gouverne que si l'autre axe vaut auto. Sans
     cette ligne les portraits s'affichent en ellipses de 148×400. */
  width: clamp(104px, 12vw, 148px); height: auto; aspect-ratio: 1;
  border-radius: 50%;
  margin: 0 auto var(--s-3);
  box-shadow: 0 0 0 1px var(--accent-soft);
}
.team-avatar {
  background: var(--green-soft); color: var(--green);
  display: grid; place-items: center;
  font-family: var(--ff-display); font-size: clamp(1.6rem, 3vw, 2.4rem);
}
.team-photo { display: block; object-fit: cover; }
.team-card b { display: block; }
.team-card span { font-size: var(--fs-sm); color: var(--ink-faint); }

/* ---------- Footer ---------- */
.site-footer { background: var(--ink); color: rgb(var(--on-dark-rgb) / 0.7); padding-block: var(--s-9) var(--s-7); }
.footer-grid {
  display: grid;
  grid-template-columns: 1.4fr 1fr 1fr 1fr;
  gap: var(--s-7);
  padding-bottom: 44px;
  border-bottom: 1px solid rgb(var(--on-dark-rgb) / 0.12);
}
@media (max-width: 900px) { .footer-grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 560px) { .footer-grid { grid-template-columns: 1fr; } }
.footer-grid h3 { color: var(--bg); font-family: var(--ff-body); font-size: var(--fs-sm); letter-spacing: 0.08em; text-transform: uppercase; margin-bottom: var(--s-4); font-weight: 700; }
.footer-grid ul { display: flex; flex-direction: column; gap: var(--s-3); list-style: none; margin: 0; padding: 0; }
.footer-grid a:hover { color: var(--bg); }
.footer-brand .brand { color: var(--bg); }
.footer-brand p { margin-top: var(--s-3); max-width: 32ch; font-size: var(--fs-md); }
.footer-social { display: flex; gap: var(--s-2); margin-top: var(--s-5); }
.footer-social a { width: 38px; height: 38px; border-radius: 50%; border: 1px solid rgb(var(--on-dark-rgb) / 0.2); display: grid; place-items: center; }
.footer-social a:hover { background: var(--accent); border-color: var(--accent); color: var(--ink); }
.footer-social svg { width: 17px; height: 17px; }
.footer-bottom { display: flex; flex-wrap: wrap; justify-content: space-between; gap: var(--s-3); padding-top: var(--s-6); font-size: var(--fs-xs); }
.footer-bottom a:hover { color: var(--bg); }

/* ---------- Confort tactile ----------
   WCAG 2.2 SC 2.5.8 passe DÉJÀ, par la dérogation d'espacement : aucune paire
   de cibles n'est à moins de 24 px de centre à centre (mesuré à 390 px). Ce
   n'est donc pas un défaut de conformité, mais ces liens ne font que 17 à
   21 px de haut, loin des ~45 px d'une pulpe de doigt, et le numéro de
   téléphone est l'action de conversion principale du site.
   La zone cliquable est étendue par un pseudo-élément plutôt que par du
   padding : la cible grandit, la mise en page ne bouge pas d'un pixel, et
   l'anneau de focus continue d'épouser le texte, pas la zone étendue.
   Marge de 5 px : les écarts les plus serrés sont de var(--s-3) = 12 px, il
   reste donc 2 px de zone morte entre deux cibles voisines. */
.footer-grid li a,
.footer-bottom a,
.breadcrumb a,
.cp-list a { position: relative; }
.footer-grid li a::after,
.footer-bottom a::after,
.breadcrumb a::after,
.cp-list a::after {
  content: "";
  position: absolute;
  inset: -5px -2px;
}

/* ---------- Utility ---------- */
.mt-0 { margin-top: 0; }
.mt-16 { margin-top: var(--s-4); }
.mt-28 { margin-top: var(--s-7); }
.prose { max-width: 68ch; }
.stack-32 { display: flex; flex-direction: column; gap: var(--s-7); }
.grid-tight { gap: var(--s-2) 32px; }
/* L'eyebrow vert foncé devient illisible sur les panneaux sombres. */
.hero-badge-static { position: static; max-width: 220px; }

/* L'animation d'apparition n'est armée QUE si le JS a tourné (html.js).
   Sans cette garde, un main.js absent laissait 88 % de la page en opacity:0. */
html.js .reveal { opacity: 0; transform: translateY(18px); transition: opacity 0.6s ease, transform 0.6s ease; }
html.js .reveal.is-visible { opacity: 1; transform: translateY(0); }

.skip-link {
  position: absolute; left: -999px; top: 0;
  background: var(--ink); color: var(--bg);
  padding: var(--s-3) var(--s-5); border-radius: 0 0 8px 0; z-index: 999;
}
.skip-link:focus { left: 0; }

/* Anneau de focus. La couleur suit la SURFACE SOUS L'ANNEAU, pas le composant :
   avec `outline-offset` positif l'anneau est tracé HORS de l'élément, donc c'est
   le fond du parent qui le porte, et non celui de l'élément. Vérifier la surface
   environnante avant de choisir la couleur : le seuil du SC 1.4.11 est 3,0:1. */
:focus-visible { outline: 3px solid var(--accent-dark); outline-offset: 3px; }

/* Surfaces sombres : anneau clair. */
.site-footer :focus-visible,
.brand-strip :focus-visible,
.contact-panel :focus-visible { outline-color: var(--accent-light); }

/* Le bandeau CTA part exactement de --accent-dark : l'anneau y était de la même
   couleur que son fond. --on-accent donne 5,37:1 au départ, 6,04:1 à l'arrivée. */
.cta-banner :focus-visible { outline-color: var(--on-accent); }

/* Îlots CLAIRS, y compris au sein d'un parent sombre : retour à l'anneau foncé.
   .card-cat est une tuile sombre, mais son anneau tombe hors d'elle, sur --bg. */
.card-cat:focus-visible { outline-color: var(--accent-dark); }

/* --accent est interdit en fond sous du clair (voir son commentaire ligne 16) ;
   cette règle l'enfreignait : #fff dessus donnait 2,99:1. */
::selection { background: var(--accent-dark); color: var(--on-accent); }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  html.js .reveal { opacity: 1; transform: none; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}
