/* =========================================================
   BEELY STUDIO — Page d'accueil
   Design tokens (variables Figma) + composants (BEM)
   Thème sombre & clair · Police Inter (auto-hébergée, RGPD : aucun appel externe)
   ========================================================= */

@font-face {
  font-family: "Inter";
  src: url("../fonts/inter.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

:root {
  /* ---- Couleurs de marque (indépendantes du thème) ---- */
  --color-primary: #1e69fe;
  --color-primary-t5: rgba(30, 105, 254, 0.21);
  --color-primary-t3: rgba(30, 105, 254, 0.53);
  --color-pink: #ff2d55;
  --color-badge: #112040;        /* pastille bleu nuit (hero) */
  --on-primary: #ffffff;         /* texte sur fond bleu/sombre */

  /* ---- Tokens thématiques (valeurs = THÈME SOMBRE par défaut) ---- */
  --color-d6: #111827;           /* fond de page (bleu-gris, couleurs de tickets) */
  --color-d2: #1a2130;           /* surfaces : cartes, pastilles, inputs */
  --color-white: #ffffff;        /* texte principal */
  --color-w-t1: rgba(255, 255, 255, 0.88);
  --color-w-t3: rgba(255, 255, 255, 0.52);
  --color-w-t5: rgba(255, 255, 255, 0.16);
  --header-bg: var(--color-d2);
  --menu-bg: rgba(26, 33, 48, 0.98);
  --orb-color: rgba(30, 105, 254, 0.30);
  --glow-opacity: 0.85;
  --logo-filter: none;           /* inversion des SVG blancs en thème clair */
  --grid-line: rgba(255, 255, 255, 0.05);   /* grille de fond (héros) */
  --bloom: rgba(47, 107, 255, 0.16);         /* halo bleu atmosphérique */

  /* ---- Typographie ---- */
  --font: "Inter", system-ui, -apple-system, sans-serif;
  --fs-2xs: 0.65rem;     /* 10.4 */
  --fs-xs: 0.78rem;      /* 12.5 */
  --fs-s: 0.9375rem;     /* 15   */
  --fs-m: 1.125rem;      /* 18   */
  --fs-l: 1.3125rem;     /* 21   */
  --fs-xl: 1.625rem;     /* 26   */
  --fs-2xl: 2rem;        /* 32   */
  --lh-head: 1.2;
  --lh-body: 1.55;
  --fs-h1: clamp(1.75rem, 1.35rem + 1.5vw, 2rem);
  --fs-h2: clamp(1.4rem, 1.15rem + 1vw, 1.625rem);
  --fs-h3: clamp(1.2rem, 1.05rem + 0.5vw, 1.3125rem);

  /* ---- Espacements ---- */
  --space-4xs: 5px;
  --space-3xs: 5px;
  --space-2xs: 7px;
  --space-xs: 11px;
  --space-s: 16px;
  --space-m: 24px;
  --space-l: 34px;
  --space-xl: 50px;
  --space-2xl: 72px;
  --space-3xl: 80px;
  --space-section: clamp(44px, 5.5vw, 76px);

  /* ---- Rayons ---- */
  --radius-2xs: 3.82px;
  --radius-xs: 6.18px;
  --radius-s: 10px;
  --radius-m: 16.18px;
  --radius-xl: 42.36px;
  --radius-full: 99999px;

  /* ---- Largeurs ---- */
  --width-m: 540px;
  --width-l: 810px;
  --width-xl: 1080px;
  --site-max: 1366px;
  --control-h: 38px;             /* hauteur commune boutons / toggle */

  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
}

/* ---- THÈME CLAIR ---- */
[data-theme="light"] {
  --color-d6: #ffffff;           /* fond de page blanc en clair */
  --color-d2: #ffffff;
  --color-white: #14181f;        /* texte principal (sombre) */
  --color-w-t1: rgba(20, 24, 31, 0.84);
  --color-w-t3: rgba(20, 24, 31, 0.64);
  --color-w-t5: rgba(20, 24, 31, 0.12);
  --header-bg: var(--color-d2);
  --menu-bg: rgba(255, 255, 255, 0.98);
  --orb-color: rgba(30, 105, 254, 0.22);
  --glow-opacity: 0.4;
  --logo-filter: invert(1);
  --grid-line: rgba(20, 24, 31, 0.045);
  --bloom: rgba(47, 107, 255, 0.08);
}

/* =========================================================
   Reset
   ========================================================= */
*, *::before, *::after { box-sizing: border-box; }
html { scroll-behavior: smooth; scroll-padding-top: 88px; }
body {
  margin: 0;
  font-family: var(--font);
  font-size: var(--fs-s);
  font-weight: 400;
  line-height: var(--lh-body);
  color: var(--color-white);
  background: var(--color-d6);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  /* overflow-x : source unique dans le bloc « GOUTTIÈRE UNIQUE » (§2). */
  transition: background-color 0.4s var(--ease), color 0.4s var(--ease);
}
img, svg { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }
ul { margin: 0; padding: 0; list-style: none; }
h1, h2, h3, h4, p { margin: 0; }
button { font: inherit; color: inherit; cursor: pointer; background: none; border: none; }
input, textarea { font: inherit; }
:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 3px; border-radius: 4px; }

/* =========================================================
   Boule de gradient animée (fond global)
   ========================================================= */
.bg-orb {
  position: fixed;
  top: 0;
  left: 0;
  width: 30vmax;
  height: 30vmax;
  z-index: -10;
  pointer-events: none;
  border-radius: 50%;
  background: radial-gradient(circle, var(--orb-color) 0%, transparent 58%);
  filter: blur(30px);
  transform: translate(50vw, 30vh) translate(-50%, -50%);
  will-change: transform;
}

/* =========================================================
   Layout helpers
   ========================================================= */
.container { width: 100%; max-width: var(--site-max); margin-inline: auto; }
.section {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding-block: var(--space-section);
  padding-inline: var(--space-m);
  position: relative;
}
.section-head {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-xs);
  max-width: var(--width-l);
  text-align: center;
}

/* =========================================================
   Typographie
   ========================================================= */
.h1 { font-size: var(--fs-h1); font-weight: 700; line-height: var(--lh-head); }
.h2 { font-size: var(--fs-h2); font-weight: 700; line-height: var(--lh-head); }
.accent { color: var(--brand-ink); }
.lead { color: var(--color-w-t3); font-size: var(--fs-s); }

/* =========================================================
   Composants partagés
   ========================================================= */

/* ---- Pastilles ---- */
.pill {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-2xs) var(--space-m);
  background: var(--color-d2);
  border: 1px solid var(--color-w-t5);
  border-radius: var(--radius-full);
  font-size: var(--fs-xs);
  font-weight: 500;
  line-height: var(--lh-body);
  white-space: nowrap;
}
.pill-duo {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3xs);
  padding: var(--space-3xs);
  background: var(--color-d2);
  border: 1px solid var(--color-w-t5);
  border-radius: var(--radius-xs);
  font-size: var(--fs-xs);
  font-weight: 500;
}
.pill-duo__tag {
  background: var(--color-badge);
  color: #fff;
  padding: 2px var(--space-2xs);
  border-radius: var(--radius-xs);
}
.pill-duo__text { padding-right: var(--space-3xs); }

/* ---- Boutons ---- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2xs);
  height: var(--control-h);
  padding: 0 var(--space-s);
  border: 1px solid transparent;
  border-radius: var(--radius-s);
  font-size: var(--fs-xs);
  font-weight: 600;
  line-height: 1;
  white-space: nowrap;
  transition: transform 0.2s var(--ease), background-color 0.2s var(--ease),
              border-color 0.2s var(--ease), color 0.2s var(--ease);
}
.btn:hover { transform: translateY(-2px); }
.btn:active { transform: translateY(0); }
.btn--primary { background: var(--color-primary); color: var(--on-primary); }
.btn--primary:hover { background: #357bff; }
.btn--dark { background: #1e1d1d; color: #fff; border-color: rgba(255,255,255,0.08); }
.btn--dark:hover { background: #2a2828; }
.btn--outline { background: transparent; color: var(--color-white); border-color: var(--color-white); }
.btn--outline:hover { background: var(--color-white); color: var(--color-d6); }

/* ---- Logo ---- */
.logo { display: inline-flex; align-items: flex-start; gap: 4.2px; }
.logo__word { width: 72px; height: 24.72px; filter: var(--logo-filter); }
.logo__mark { width: 12.82px; height: 23.69px; }

/* ---- Bascule de thème ---- */
.theme-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--control-h);
  height: var(--control-h);
  flex: 0 0 auto;
  border-radius: var(--radius-s);
  background: var(--color-d2);
  border: 1px solid var(--color-w-t5);
  color: var(--color-white);
  transition: background-color 0.2s var(--ease);
}
.theme-toggle:hover { background: var(--color-primary-t5); }
.theme-toggle svg { width: 18px; height: 18px; }
.theme-toggle__moon { display: none; }
[data-theme="light"] .theme-toggle__sun { display: none; }
[data-theme="light"] .theme-toggle__moon { display: block; }

/* =========================================================
   HEADER
   ========================================================= */
.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  width: 100%;
  display: flex;
  justify-content: center;
  padding: var(--space-xs) var(--space-m);
  background: var(--header-bg);
  border-bottom: 1px solid var(--color-w-t5);
}
.site-header__inner { position: relative; display: flex; align-items: center; justify-content: space-between; gap: var(--space-m); }
.site-header__left { display: flex; align-items: center; gap: var(--space-l); }
.site-nav { display: flex; align-items: center; gap: var(--space-m); }
.site-nav__link { font-size: var(--fs-s); font-weight: 500; color: var(--color-white); transition: color 0.2s var(--ease); }
.site-nav__link:hover { color: var(--brand-ink); }
.site-nav__cta { display: none; }
.site-header__actions { display: flex; align-items: center; gap: var(--space-xs); }

/* Méga-menu desktop : navigation éditoriale, enrichie sans alourdir le mobile. */
.mega-menu { display: none; }
@media (min-width: 1101px) {
  .mega-menu { position: absolute; z-index: 60; top: calc(100% + 12px); left: 94px; display: grid; grid-template-columns: minmax(0, 1.3fr) minmax(220px, .7fr); gap: var(--space-l); width: min(760px, calc(100vw - 64px)); padding: var(--space-m); border: 1px solid var(--surface-border); border-radius: var(--radius-m); background: color-mix(in srgb, var(--color-d2) 94%, transparent); box-shadow: 0 28px 70px rgba(1, 8, 24, .24); backdrop-filter: blur(18px); -webkit-backdrop-filter: blur(18px); opacity: 0; visibility: hidden; pointer-events: none; transform: translateY(-8px) scale(.985); transition: opacity .2s var(--ease), transform .24s var(--ease), visibility 0s linear .24s; }
  .site-header.has-mega-open .mega-menu { opacity: 1; visibility: visible; pointer-events: auto; transform: none; transition-delay: 0s; }
  .mega-menu__eyebrow { margin-bottom: 7px; color: var(--brand-ink); font-size: var(--fs-2xs); font-weight: 700; letter-spacing: .1em; }
  .mega-menu__title { margin-bottom: 7px; font-size: var(--fs-l); line-height: 1.1; }
  .mega-menu__intro { max-width: 470px; color: var(--color-w-t3); font-size: var(--fs-xs); line-height: 1.55; }
  .mega-menu__links { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; margin-top: var(--space-m); }
  .mega-menu__link { display: flex; flex-direction: column; gap: 2px; min-width: 0; padding: 10px 11px; border: 1px solid transparent; border-radius: var(--radius-s); transition: background-color .2s var(--ease), border-color .2s var(--ease), transform .2s var(--ease); }
  .mega-menu__link:hover, .mega-menu__link:focus-visible { border-color: var(--surface-border); background: var(--color-primary-t5); transform: translateY(-1px); }
  .mega-menu__link b { color: var(--color-white); font-size: var(--fs-xs); }
  .mega-menu__link span { color: var(--color-w-t3); font-size: var(--fs-2xs); line-height: 1.4; }
  .mega-menu__feature { display: flex; flex-direction: column; justify-content: space-between; gap: var(--space-m); min-height: 100%; padding: var(--space-m); border: 1px solid var(--color-primary-t3); border-radius: var(--radius-m); background: linear-gradient(145deg, var(--color-primary-t5), transparent 76%); }
  .mega-menu__feature p { color: var(--color-w-t3); font-size: var(--fs-xs); line-height: 1.5; }
  .mega-menu__feature strong { display: block; margin-bottom: 6px; color: var(--color-white); font-size: var(--fs-m); line-height: 1.2; }
  .mega-menu__feature a { display: inline-flex; align-items: center; gap: 7px; width: fit-content; color: var(--brand-ink); font-size: var(--fs-xs); font-weight: 700; }
  .mega-menu__feature a::after { content: "→"; transition: transform .2s var(--ease); }
  .mega-menu__feature a:hover::after { transform: translateX(3px); }
  .site-nav__link.is-mega-active { color: var(--brand-ink); }
}

.nav-toggle { display: none; flex-direction: column; gap: 5px; width: 44px; height: 44px; align-items: center; justify-content: center; }
.nav-toggle__bar { width: 24px; height: 2px; background: var(--color-white); border-radius: 2px; transition: transform 0.25s var(--ease), opacity 0.2s var(--ease); }

/* =========================================================
   HERO
   ========================================================= */
.hero { position: relative; }
.hero__sticky {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  gap: var(--space-l);
  min-height: min(860px, 92vh);
  padding: var(--space-2xl) var(--space-m);
  background: radial-gradient(ellipse 60% 52% at 50% 24%, var(--bloom), transparent 66%);
}
/* En scène (JS) : le hero est haut, le groupe texte+mockup s'épingle, monte et le mockup grossit */
.js .hero--stage { height: 165vh; }
.js .hero--stage .hero__sticky {
  position: sticky; top: 0;
  height: 100vh; min-height: 0;
}
.hero__inner { display: flex; flex-direction: column; align-items: center; gap: var(--space-xl); width: 100%; will-change: transform; }
.hero__showcase { width: 100%; display: flex; justify-content: center; }
.js .hero--stage .mockup--hero {
  width: min(72vw, 1050px);
  transform-origin: center center;
  transform: scale(0.72);
}
.js .hero--stage .hero__showcase { margin-top: var(--space-m); }
.js .hero--stage .mockup__screen { padding: 26px; gap: 18px; }
.hero__glow {
  position: absolute;
  top: -8%;
  left: 50%;
  transform: translateX(-50%);
  width: min(820px, 88%);
  height: auto;
  z-index: -2;
  pointer-events: none;
  opacity: var(--glow-opacity);
}

.hero__content { display: flex; flex-direction: column; align-items: center; gap: var(--space-m); width: 100%; max-width: var(--width-l); text-align: center; }
.hero__title { font-size: var(--fs-h1); font-weight: 700; line-height: var(--lh-head); }
.hero__lead { max-width: 62ch; }   /* mesure de lecture : 810px (108ch) était trop long */
.hero__actions { display: flex; flex-wrap: wrap; gap: var(--space-xs); justify-content: center; }

/* =========================================================
   LOGOS (marquee clients)
   ========================================================= */
.logos { gap: var(--space-xl); }
.logos__inner { display: flex; flex-direction: column; align-items: center; gap: var(--space-xl); width: 100%; max-width: var(--site-max); }
.marquee {
  position: relative;
  width: 100%;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  gap: var(--space-s);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
}
.marquee__row { display: flex; align-items: center; gap: var(--space-l); width: max-content; animation: marquee 80s linear infinite; }
.marquee__row--reverse { animation-direction: reverse; }
.marquee__row:hover { animation-play-state: paused; }
.marquee__logo { display: flex; align-items: center; justify-content: center; height: 56px; flex: 0 0 auto; }
/* Silhouettes monochromes uniformes : tous les logos rendus d'une seule teinte, quel que soit leur source */
.marquee__logo img { width: auto; height: 30px; max-width: none; opacity: 0.7; filter: brightness(0) invert(1); }
[data-theme="light"] .marquee__logo img { filter: brightness(0); opacity: 0.5; }
@keyframes marquee { from { transform: translateX(0); } to { transform: translateX(-50%); } }

/* =========================================================
   TARIFS
   ========================================================= */
.pricing__inner { display: flex; flex-direction: column; align-items: center; gap: var(--space-xl); width: 100%; max-width: var(--site-max); }
/* gap : source unique = --sp-card-gap (§7). Ne pas le redéclarer ici. */
.pricing__grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); width: 100%; align-items: stretch; }
.pricing__grid--comparison { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.pricing__grid--comparison .price-feature::before {
  content: "✓";
  display: inline-grid;
  place-items: center;
  width: 20px;
  height: 20px;
  flex: 0 0 20px;
  margin-top: 1px;
  border-radius: 50%;
  background: var(--color-primary-t5);
  color: var(--brand-ink);
  font-size: 12px;
  font-weight: 700;
}
.pricing__grid--comparison .price-card--featured .price-feature::before { background: rgba(255, 255, 255, 0.14); color: #fff; }
.price-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-l);
  padding: var(--space-m);
  background: var(--color-d2);
  border: 1px solid var(--color-w-t5);
  border-radius: var(--radius-m);
}
.price-card--featured { background: var(--color-primary-t5); border-color: var(--color-primary-t3); }
.price-card__info { position: relative; display: flex; flex-direction: column; gap: var(--space-xs); }
.price-card__icon { display: inline-flex; align-items: center; justify-content: center; width: 46px; height: 46px; border-radius: var(--radius-s); background: var(--color-primary-t5); color: var(--brand-ink); }
.price-card__icon svg { width: 24px; height: 24px; }
.price-card--featured .price-card__icon { background: var(--color-primary); color: #fff; }
.price-card__title { font-size: var(--fs-l); font-weight: 600; line-height: var(--lh-head); }
.price-card__desc { color: var(--color-w-t3); }
.price-card--featured .price-card__desc { color: var(--color-w-t1); }
.price-card__price { position: absolute; top: 0; right: 0; display: flex; align-items: baseline; gap: var(--space-xs); }
.price-card__price small { font-size: var(--fs-xs); color: var(--color-w-t3); font-weight: 400; }
.price-card--featured .price-card__price small { color: var(--color-w-t1); }
.price-card__price b { font-size: var(--fs-xl); font-weight: 700; }
.price-features { display: flex; flex-direction: column; gap: var(--space-s); }
.price-features--cols { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-s); }
.price-feature { display: flex; align-items: flex-start; gap: var(--space-xs); }
.price-feature__ico { width: 20px; height: 20px; flex: 0 0 20px; margin-top: 1px; }
.price-card--featured .price-feature { color: var(--color-w-t1); }
.price-feature--highlight { font-weight: 700; text-decoration: underline; }
.price-card__cta { margin-top: auto; align-self: flex-start; }

/* =========================================================
   PROCESS (méthodologie)
   ========================================================= */
.process { overflow: clip; }

/* =========================================================
   PORTFOLIO
   ========================================================= */
/* Bloc consolidé « Une expertise forgée sur des projets d'envergure ».
   Une seule déclaration par sélecteur, zéro !important, tokens uniquement.
   Espacement : rôles sémantiques --sp-* (§1) — aucune 3e échelle.
   Typo : --fs-xs/s/m — aucune 3e échelle de type.
   Hooks JS préservés : .filter-btn/.is-active, .accordion-item/.is-open,
   .accordion-item__panel (max-height piloté en JS), data-filter/data-category.
   La classe .section-head est CONSERVÉE sur .portfolio__head : depuis la
   consolidation (§4) elle est ferrée à gauche partout, plus aucune surcharge
   ne la recentre — et script.js:563 garde son reveal sans liste à rallonge. */

/* ---- Enveloppe ---------------------------------------------------------
   RIEN à écrire ici, volontairement. §2 dé-gouttière .portfolio__inner et
   §4 en fait une colonne flex ferrée à gauche dont le `gap: --sp-head-gap`
   est le propriétaire UNIQUE des trois coutures (tête → filtres → accordéon
   → lien). Les enfants ne portent aucune margin : rien ne s'additionne,
   rien ne peut collapser, et le bloc respire au même rythme que le reste du
   site. Redéclarer gap/max-width ici ne ferait que ré-empiler une surcharge. */

/* ---- Filtres : chips discrets, ferrés à gauche ---- */
.portfolio__filters { display: flex; flex-wrap: wrap; gap: var(--sp-2); }

.filter-btn {
  appearance: none;
  padding: 7px 14px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: transparent;
  color: var(--text-2);
  font-family: var(--mono);
  font-size: var(--fs-xs);
  font-weight: 400;
  line-height: 1;
  letter-spacing: .06em;
  text-transform: uppercase;
  cursor: pointer;
  transition: color .18s var(--ease), border-color .18s var(--ease), background-color .18s var(--ease);
}
.filter-btn:hover { color: var(--text); border-color: var(--line-strong); }
.filter-btn.is-active { color: var(--brand-ink); border-color: var(--brand-t2); background: var(--brand-t); }

/* ---- Accordéon : lignes ouvertes à filets fins ---- */
.accordion { width: 100%; border-bottom: 1px solid var(--line); }
/* Le filet est porté par CHAQUE ligne : il reste correct quand le filtre
   masque le premier élément (display:none emporte sa bordure avec lui). */
.accordion-item { border-top: 1px solid var(--line); }

/* Les wrappers hérités s'effacent au profit de la grille de la ligne. */
.accordion-item__heading,
.accordion-item__sub { display: contents; }

.accordion-item__head {
  display: grid;
  grid-template-columns: 2.75rem minmax(110px, 1fr) minmax(96px, .85fr) minmax(0, 1.6fr) 1.25rem;
  grid-template-areas: "idx name type tag ico";
  align-items: center;
  gap: var(--sp-5);
  width: 100%;
  padding: var(--sp-5) 0;
  border: 0;
  background: transparent;
  color: inherit;
  text-align: left;
  cursor: pointer;
}

.accordion-item__index {
  grid-area: idx;
  font-family: var(--mono);
  font-size: var(--fs-xs);
  font-variant-numeric: tabular-nums;
  color: var(--text-3);
  transition: color .18s var(--ease);
}
.accordion-item__name {
  grid-area: name;
  width: auto;
  font-size: var(--fs-m);
  font-weight: 700;
  letter-spacing: -.01em;
  transition: color .18s var(--ease);
}
.accordion-item__type {
  grid-area: type;
  font-family: var(--mono);
  font-size: var(--fs-xs);
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--text-2);
}
.accordion-item__tag {
  grid-area: tag;
  min-width: 0;
  font-size: var(--fs-xs);
  color: var(--text-3);
}
/* <svg currentColor> : plus aucun filter à poser ni à annuler. */
.accordion-item__icon {
  grid-area: ico;
  width: 18px;
  height: 18px;
  justify-self: end;
  color: var(--text-3);
  transition: transform .3s var(--ease), color .18s var(--ease);
}

.accordion-item__head:hover .accordion-item__name,
.accordion-item__head:hover .accordion-item__icon { color: var(--brand-ink); }

.accordion-item.is-open .accordion-item__index,
.accordion-item.is-open .accordion-item__icon { color: var(--brand); }
.accordion-item.is-open .accordion-item__icon { transform: rotate(45deg); }

/* ---- Panneau (max-height piloté en JS : ne PAS ajouter de padding ici) ---- */
/* Ouverture via grid-template-rows 0fr→1fr : anime vers la hauteur RÉELLE du
   contenu. L'ancien `max-height` piloté en JS était une propriété de layout
   recalculée à chaque frame (saccades sur mobile) et dépendait d'une mesure
   JS. Ici tout est en CSS, la hauteur est exacte par construction.
   Safari 16+/Firefox/Chrome animent grid-template-rows ; les moteurs plus
   anciens ouvrent instantanément (dégradation acceptable). */
/* Ouverture en CSS PUR : height 0 → auto (cf. FAQ). Le JS ne fait que basculer
   `.is-open`. `interpolate-size` anime là où c'est supporté, instantané ailleurs. */
.accordion-item__panel {
  overflow: hidden;
  height: 0;
  interpolate-size: allow-keywords;
  transition: height .4s var(--ease);
}
.accordion-item.is-open .accordion-item__panel { height: auto; }
.accordion-item__content { padding-bottom: var(--sp-6); }
/* Fondu du contenu (opacity composité, jamais de saccade) : ouverture douce
   même sur Safari où la hauteur s'ouvre instantanément. Gated `.js` : sans JS
   l'accordéon est déplié et tout doit rester visible. */
.js .accordion-item__content { opacity: 0; transition: opacity 0.32s var(--ease); }
.js .accordion-item.is-open .accordion-item__content { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  .js .accordion-item__panel, .js .accordion-item__content { transition: none; }
}
.accordion-item__content {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.35fr);
  align-items: start;
  gap: var(--sp-6);
  padding: 0 0 var(--sp-6);
}
/* --fs-s (15px) et non --fs-xs : c'est le SEUL paragraphe de lecture du
   bloc. L'ancienne valeur 14.5px tombe entre les deux tokens ; l'arrondir
   vers le bas (12.5px) la mettrait au niveau des méta-libellés et rendrait
   le texte inconfortable. On reste dans l'échelle, au cran le plus proche. */
.accordion-item__desc {
  margin: 0;
  max-width: 46ch;
  font-size: var(--fs-s);
  line-height: 1.65;
  color: var(--text-2);
}
/* Les 2 visuels partagent la MÊME hauteur de ligne (pieds alignés) : la
   hauteur vient du visuel large, le carré l'étire en cover.
   §7 fournit display:grid + gap: var(--sp-card-gap) — gap resserré commun. */
.accordion-item__images { grid-template-columns: 1.55fr 1fr; align-items: stretch; }
.accordion-item__images img {
  display: block;
  width: 100%;
  border: 1px solid var(--line);
  border-radius: var(--r-m);
  object-fit: cover;
}
.accordion-item__img--wide   { height: auto; aspect-ratio: 470 / 264; }
.accordion-item__img--square { height: 100%; }

/* ---- Paliers du bloc ---------------------------------------------------
   §9 (760px) passe déjà .accordion-item__images en colonne unique : on ne
   le réécrit pas ici, on ne traite que ce qui lui est propre. */
@media (max-width: 860px) {
  .accordion-item__content { grid-template-columns: 1fr; gap: var(--sp-5); }
  .accordion-item__desc { max-width: none; }
}
@media (max-width: 760px) {
  .accordion-item__img--wide,
  .accordion-item__img--square { height: auto; aspect-ratio: 16 / 10; }
}
@media (max-width: 640px) {
  .accordion-item__head {
    grid-template-columns: 2.25rem minmax(0, 1fr) 1.25rem;
    grid-template-areas:
      "idx name ico"
      "idx type ico"
      "idx tag  ico";
    gap: var(--sp-1) var(--sp-4);
    padding: var(--sp-4) 0;
  }
  .accordion-item__index,
  .accordion-item__icon { align-self: center; }
}
@media (max-width: 420px) {
  .accordion-item__tag { display: none; }
  .accordion-item__head { grid-template-areas: "idx name ico" "idx type ico"; }
}

/* Toutes les transitions introduites par le bloc, pas seulement deux. */
@media (prefers-reduced-motion: reduce) {
  .accordion-item__panel,
  .accordion-item__icon,
  .accordion-item__index,
  .accordion-item__name,
  .filter-btn { transition: none; }
}

/* =========================================================
   FAQ — voir le bloc « FAQ — accordéon aéré (DA unique) »,
   source unique plus bas dans ce fichier. Rien ici.
   ========================================================= */

/* =========================================================
   CTA (contact)
   ========================================================= */
.cta { padding-top: clamp(48px, 6vw, 84px); padding-bottom: clamp(80px, 11vw, 158px); }
.cta__inner { display: flex; align-items: flex-start; gap: var(--space-s); width: 100%; max-width: var(--site-max); }
.cta__intro { display: flex; flex: 1 0 0; flex-direction: column; gap: var(--space-m); min-width: 0; }
.cta__title { font-size: var(--fs-h2); font-weight: 700; line-height: var(--lh-head); }
.cta__text { color: var(--color-w-t3); max-width: 550px; }
.cta__actions { display: flex; flex-wrap: wrap; gap: var(--space-xs); }
.cta__inner--banner { flex-direction: column; align-items: center; text-align: center; }
.cta__inner--banner .cta__intro { align-items: center; max-width: 640px; flex: 0 1 auto; }
.cta__inner--banner .cta__text { margin-inline: auto; }
.cta__inner--banner .cta__actions { justify-content: center; }
/* Formulaire : sans cadre, intégré au thème (clair/sombre) */
.cta-form { display: flex; flex-direction: column; gap: var(--space-m); width: 520px; flex: 0 0 auto; }
.form-status { font-size: var(--fs-xs); margin: 0; }

/* =========================================================
   FOOTER
   ========================================================= */
.site-footer { display: flex; flex-direction: column; align-items: center; gap: var(--space-m); padding: var(--space-section) var(--space-m); background: var(--color-d2); border-top: 1px solid var(--line); }
.site-footer__top { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-l); width: 100%; max-width: var(--site-max); }
.site-footer__brand { display: flex; flex-direction: column; gap: var(--space-m); align-items: flex-start; flex: 1 0 0; max-width: var(--width-m); min-width: 0; }
.site-footer__desc { color: var(--color-w-t3); font-size: var(--fs-xs); }
.site-footer__mail { color: var(--color-white); font-size: var(--fs-s); font-weight: 500; width: fit-content; }
.site-footer__mail:hover { color: var(--brand-ink); }
.site-footer__social { display: inline-flex; padding: 8px; background: var(--color-primary-t5); border-radius: var(--radius-s); color: var(--brand-ink); }
.site-footer__social svg { width: 22px; height: 22px; }
.footer-col { display: flex; flex-direction: column; gap: var(--space-m); font-size: var(--fs-xs); }
.footer-col__title { font-weight: 600; color: var(--color-white); }
.footer-col__links { display: flex; flex-direction: column; gap: var(--space-s); }
.footer-col__links a { color: var(--color-w-t3); transition: color 0.2s var(--ease); }
.footer-col__links a:hover { color: var(--color-white); }
.site-footer__bottom { display: grid; grid-template-columns: 1fr auto; align-items: center; gap: var(--space-m); width: 100%; max-width: var(--site-max); }
.site-footer__divider { grid-column: 1 / -1; width: 100%; height: 1px; background: var(--color-w-t5); }
.site-footer__copy { color: var(--color-w-t3); font-size: var(--fs-xs); }
.site-footer__bottom-links { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: var(--space-m); }
.site-footer__bottom-links a { color: var(--color-w-t3); font-size: var(--fs-xs); }
.site-footer__bottom-links a:hover, .site-footer__bottom-links a:focus-visible { color: var(--brand-ink); }
.footer-locations { display: flex; flex-direction: column; gap: var(--space-s); width: 100%; max-width: var(--site-max); padding: var(--space-m) 0 0; border-top: 1px solid var(--color-w-t5); }
.footer-locations__title { color: var(--color-w-t3); font-size: var(--fs-2xs); font-weight: 600; letter-spacing: 0.1em; }
.footer-locations__links { display: flex; flex-wrap: wrap; gap: 8px 22px; }
.footer-locations__links a { color: var(--color-w-t3); font-size: var(--fs-xs); transition: color 0.2s var(--ease); }
.footer-locations__links a:hover, .footer-locations__links a:focus-visible { color: var(--brand-ink); }

/* =========================================================
   Apparition au scroll
   ========================================================= */
.reveal { opacity: 0; transform: translateY(24px); transition: opacity 0.7s var(--ease), transform 0.7s var(--ease); }
.reveal.is-visible { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 0.001ms !important; transition-duration: 0.001ms !important; scroll-behavior: auto !important; }
  .reveal { opacity: 1; transform: none; }
  .marquee__row { animation: none; }
}

/* =========================================================
   RESPONSIVE
   ========================================================= */
@media (max-width: 1100px) {
  .pricing__grid { grid-template-columns: 1fr; }
  /* .faq__* : gérés par le bloc FAQ unique (@media 1024px). */
  .cta__inner { flex-direction: column; }
  .cta-form { width: 100%; }
  /* Portfolio : le palier mobile du bloc vit dans son bloc consolidé. */
}

@media (max-width: 900px) {
  .nav-toggle { display: flex; position: relative; z-index: 60; }
  .site-header__actions .btn { display: none; }
  /* Menu plein écran ouvert : header transparent pour laisser voir l'overlay (logo + croix restent au-dessus) */
  body.menu-open .site-header { background: transparent; border-bottom-color: transparent; }

  /* Menu plein écran (overlay verre + révélation circulaire depuis le burger) */
  .site-nav {
    position: fixed;
    inset: 0;
    z-index: 40;
    flex-direction: column;
    justify-content: flex-start;
    align-items: stretch;
    gap: 2px;
    overflow-y: auto;
    padding: max(88px, 12vh) clamp(22px, 7vw, 60px) 40px;
    counter-reset: navi;
    background:
      radial-gradient(130% 90% at 88% 0%, rgba(30, 105, 254, 0.22), transparent 55%),
      #07080b;
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    clip-path: circle(0% at calc(100% - 46px) 38px);
    transition: clip-path 0.55s var(--ease), opacity 0.3s var(--ease), visibility 0s linear 0.55s;
  }
  [data-theme="light"] .site-nav {
    background:
      radial-gradient(130% 90% at 88% 0%, rgba(30, 105, 254, 0.13), transparent 55%),
      #eef0f4;
  }
  .site-nav.is-open {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    clip-path: circle(150% at calc(100% - 46px) 38px);
    transition: clip-path 0.6s var(--ease), opacity 0.3s var(--ease);
  }

  /* Les rubriques et leurs sous-menus doivent conserver leur hauteur :
     c'est le panneau entier qui défile, jamais le contenu qui se tasse. */
  .site-nav__link,
  .mobile-mega,
  .site-nav__cta { flex: 0 0 auto; }

  .site-nav__link {
    display: flex;
    align-items: baseline;
    gap: 16px;
    padding: 14px 4px;
    border-bottom: 1px solid var(--color-w-t5);
    font-size: clamp(1.45rem, 7vw, 2.1rem);
    font-weight: 600;
    letter-spacing: -0.02em;
    color: var(--color-white);
    opacity: 0;
    transform: translateY(18px);
    transition: opacity 0.45s var(--ease), transform 0.45s var(--ease), color 0.2s var(--ease);
  }
  .site-nav__link::before {
    content: counter(navi, decimal-leading-zero);
    counter-increment: navi;
    font-family: ui-monospace, "SFMono-Regular", Menlo, monospace;
    font-size: 0.78rem;
    font-weight: 500;
    color: var(--brand-ink);
    transform: translateY(-0.45em);
  }
  .site-nav__link:hover,
  .site-nav__link:focus-visible { color: var(--brand-ink); }
  .site-nav.is-open .site-nav__link { opacity: 1; transform: none; }
  .site-nav.is-open .site-nav__link:nth-child(1) { transition-delay: 0.14s; }
  .site-nav.is-open .site-nav__link:nth-child(2) { transition-delay: 0.20s; }
  .site-nav.is-open .site-nav__link:nth-child(3) { transition-delay: 0.26s; }
  .site-nav.is-open .site-nav__link:nth-child(4) { transition-delay: 0.32s; }
  .site-nav.is-open .site-nav__link:nth-child(5) { transition-delay: 0.38s; }

  /* Sous-menu tactile : même densité éditoriale que le méga-menu desktop. */
  .mobile-mega {
    display: block;
    max-height: 0;
    padding: 0 4px;
    overflow: hidden;
    opacity: 0;
    transform: translateY(-8px);
    transition: max-height .45s var(--ease), opacity .25s var(--ease), transform .3s var(--ease), padding .35s var(--ease);
  }
  .mobile-mega.is-open {
    max-height: 620px;
    padding: 4px 4px 14px;
    opacity: 1;
    transform: none;
  }
  .mobile-mega__eyebrow { margin: 2px 0 4px; color: var(--brand-ink); font-size: var(--fs-2xs); font-weight: 700; letter-spacing: .12em; }
  .mobile-mega__intro { margin: 0 0 14px; color: var(--color-w-t3); font-size: var(--fs-xs); line-height: 1.5; }
  .mobile-mega__links { display: grid; gap: 7px; }
  .mobile-mega__links a { position: relative; display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 2px 12px; padding: 10px 12px; border: 1px solid var(--surface-border); border-radius: var(--radius-s); background: var(--color-primary-t5); }
  .mobile-mega__links span { color: var(--color-white); font-size: var(--fs-xs); font-weight: 700; }
  .mobile-mega__links small { grid-column: 1; color: var(--color-w-t3); font-size: var(--fs-2xs); line-height: 1.4; }
  .mobile-mega__links b { grid-row: 1 / span 2; grid-column: 2; align-self: center; color: var(--brand-ink); font-size: var(--fs-s); }
  .mobile-mega__cta { display: flex; align-items: center; justify-content: space-between; margin-top: 12px; padding: 0 2px; color: var(--brand-ink); font-size: var(--fs-xs); font-weight: 700; }
  .mobile-mega__cta span { font-size: var(--fs-s); }

  .site-nav__cta {
    display: flex;
    flex-direction: column;
    gap: 12px;
    margin-top: 28px;
    opacity: 0;
    transform: translateY(18px);
    transition: opacity 0.45s var(--ease) 0.44s, transform 0.45s var(--ease) 0.44s;
  }
  .site-nav.is-open .site-nav__cta { opacity: 1; transform: none; }
  .site-nav__cta .btn { width: 100%; height: 50px; font-size: var(--fs-s); }

  .nav-toggle.is-open .nav-toggle__bar:nth-child(1) { transform: translateY(7px) rotate(45deg); }
  .nav-toggle.is-open .nav-toggle__bar:nth-child(2) { opacity: 0; }
  .nav-toggle.is-open .nav-toggle__bar:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

  body.menu-open { overflow: hidden; }
}

@media (max-width: 640px) {
  .site-header__actions .btn--primary { display: none; }
  .hero__glow { width: 130%; top: -4%; }
  .price-features--cols { grid-template-columns: 1fr; }

  /* Tarifs compacts en mobile : cartes presque entières sans scroller.
     (Le gap reste piloté par --sp-card-gap : une seule source.) */
  .price-card { gap: var(--space-s); padding: var(--space-s); }
  .price-card__icon { width: 38px; height: 38px; }
  .price-card__icon svg { width: 20px; height: 20px; }
  .price-card__title { font-size: var(--fs-m); }
  .price-card__desc { font-size: var(--fs-xs); line-height: 1.45; }
  .price-card__price b { font-size: var(--fs-l); }
  .price-features { gap: var(--space-xs); }
  .price-feature { gap: 10px; font-size: var(--fs-xs); }
  .price-feature__ico { width: 17px; height: 17px; flex: 0 0 17px; }
  .site-footer__top { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-l) var(--space-m); }
  .site-footer__brand { grid-column: 1 / -1; }
  .site-footer__bottom { grid-template-columns: 1fr; gap: var(--space-s); }
  .site-footer__bottom-links { justify-content: flex-start; }
}
.reveal.is-visible { opacity: 1; transform: none; }

/* Entrée animée du hero au chargement.
   On anime le CONTENEUR en un seul calque, et non chaque enfant séparément.
   Le stagger par enfant (4 animations déphasées sur ~1,15 s) forçait Firefox à
   repeindre la zone du hero en continu → ~17 frames perdues à l'arrivée sur Zen
   (Firefox), alors que Chromium/Safari layerisent et ne bronchent pas. En
   animant .hero__content d'un bloc : 1 frame perdue, entrée identique à l'œil.
   (Le build du mockup, lui, ne pose pas problème et garde son animation.) */
html.js .hero__content { opacity: 0; transform: translateY(24px); }
.hero-in .hero__content { animation: heroIn 0.7s var(--ease) forwards; }
@keyframes heroIn { to { opacity: 1; transform: none; } }
/* Firefox (donc Zen) re-rastérise le gros titre du hero à CHAQUE frame pendant
   l'entrée (qu'elle porte sur opacity ou transform) : il ne le promeut pas en
   calque, d'où ~15 frames perdues à l'arrivée, propres à ce moteur (mesuré ;
   Chromium et Safari layerisent et restent fluides). On y sert donc le hero
   directement en place — pas d'entrée animée, mais zéro rame. */
@-moz-document url-prefix() {
  html.js .hero__content { opacity: 1; transform: none; }
  .hero-in .hero__content { animation: none; }
}

/* Logos tech flottants (mouvement continu) */

/* Survol des cartes */
.price-card { position: relative; transition: transform 0.35s var(--ease), border-color 0.35s var(--ease), box-shadow 0.35s var(--ease); }
.price-card:hover { transform: translateY(-6px); border-color: var(--color-primary-t3); box-shadow: 0 24px 50px rgba(0, 0, 0, 0.28); }
[data-theme="light"] .price-card:hover { box-shadow: 0 24px 50px rgba(20, 24, 31, 0.12); }

/* Portfolio : transitions et hovers déclarés une seule fois, dans le bloc. */

.site-footer__social { transition: transform 0.2s var(--ease), background-color 0.2s var(--ease); }
.site-footer__social:hover { transform: translateY(-3px) scale(1.05); }

/* Soulignement animé des liens (header + footer), sous chaque lien */
.site-nav__link,
.footer-col__links a { position: relative; }
.footer-col__links a { width: fit-content; }
.site-nav__link::after,
.footer-col__links a::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: -3px;
  width: 0;
  height: 2px;
  background: var(--color-primary);
  transition: width 0.25s var(--ease);
}
.site-nav__link:hover, .site-nav__link:focus-visible,
.footer-col__links a:hover, .footer-col__links a:focus-visible { color: var(--brand-ink); }
.site-nav__link:hover::after, .site-nav__link:focus-visible::after,
.footer-col__links a:hover::after, .footer-col__links a:focus-visible::after { width: 100%; }

/* Barre de progression du scroll */
.scroll-progress {
  position: fixed;
  top: 0;
  left: 0;
  height: 3px;
  width: 0;
  background: linear-gradient(90deg, var(--color-primary), #6ea0ff);
  z-index: 60;
  transition: width 0.1s linear;
}

/* Boutons magnétiques (suivi du curseur en JS) */
.btn, .theme-toggle { will-change: transform; }

@media (max-width: 900px) {
  .site-nav__link::after { display: none; }
}

/* =========================================================
   PASSE DE RAFFINEMENT — plus fin, plus tech
   ========================================================= */
:root {
  --color-w-t5: rgba(255, 255, 255, 0.10);   /* hairlines */
  --surface: #1a2130;                         /* surfaces (couleurs de tickets) */
  --surface-2: #232c3b;
  --surface-border: rgba(255, 255, 255, 0.08);
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
  --danger: #ff4d63;                          /* sémantique erreur (dark) */
  --orb-color: rgba(30, 105, 254, 0.06);     /* halo très discret */
  --glow-opacity: 0.4;
  --control-h: 34px;                          /* contrôles compacts */
  --radius-m: 12px;
  --radius-s: 8px;
  --fs-l: 1.3125rem;                          /* 21 */
  --fs-h1: clamp(2rem, 1.35rem + 2.8vw, 2.75rem);
  --fs-h2: clamp(1.6rem, 1.2rem + 1.6vw, 2.1rem);
  --fs-h3: clamp(1.3rem, 1.12rem + 0.6vw, 1.5rem);
}
[data-theme="light"] {
  --color-w-t5: rgba(20, 24, 31, 0.08);
  --surface: #ffffff;
  --surface-2: #ffffff;
  --surface-border: rgba(20, 24, 31, 0.08);
  --danger: #e5384d;                          /* sémantique erreur (light) */
  --orb-color: rgba(30, 105, 254, 0.04);
  --glow-opacity: 0.26;
}

/* Corps plus aéré */
body { line-height: 1.6; }/* Titres : plus fins,
tracking serré */
.h1,
.h2,
.hero__title,
.cta__title,
.price-card__title {
  font-weight: 600;
  letter-spacing: -0.02em;
}

/* Eyebrows / labels : fins, espacés (look tech) */
.pill {
  padding: 6px 14px;
  font-size: var(--fs-2xs);
  font-weight: 500;
  letter-spacing: 0.08em;
  background: var(--surface);
  border-color: var(--surface-border);
}
.pill-duo { padding: 4px; font-size: var(--fs-2xs); background: var(--surface); border-color: var(--surface-border); }
.pill-duo__text { letter-spacing: 0.05em; }
.footer-col__title { font-size: var(--fs-2xs); letter-spacing: 0.1em; font-weight: 600; }

/* Surfaces "verre" + hairline */
.price-card { background: var(--surface); border-color: var(--surface-border); }
.price-card--featured { background: linear-gradient(180deg, rgba(30,105,254,0.10), rgba(30,105,254,0.035)); border-color: rgba(30,105,254,0.28); }
.theme-toggle { background: var(--surface); border-color: var(--surface-border); }
.site-footer__social { background: var(--surface); border: 1px solid var(--surface-border); }

/* Boutons plus fins */
.btn { font-weight: 600; }
.btn--dark { background: var(--surface-2); border-color: var(--surface-border); color: #fff; }
.btn--dark:hover { background: rgba(255,255,255,0.1); }
[data-theme="light"] .btn--dark { background: rgba(20, 24, 31, 0.05); border-color: rgba(20, 24, 31, 0.12); color: #14181f; }
[data-theme="light"] .btn--dark:hover { background: rgba(20, 24, 31, 0.1); }
/* Gélule du hero : pas de bleu nuit en thème clair */
[data-theme="light"] .pill-duo__tag { background: var(--color-primary); color: #fff; }

/* Halos discrets. blur réduit 38->22px : le radial-gradient (transparent à 58%)
   porte déjà l'essentiel de la douceur, l'aspect est identique (vérifié en
   capture, clair + sombre) mais la rastérisation du calque flouté à l'arrivée
   est bien plus légère sur GPU modeste. */
.bg-orb { filter: blur(22px); }
.marquee__logo { height: 46px; }
.marquee__logo img { height: 26px; opacity: 0.68; }

/* Lien d'évitement (accessibilité) */
.skip-link {
  position: fixed;
  top: 8px;
  left: 8px;
  z-index: 200;
  padding: 10px 16px;
  background: var(--color-primary);
  color: #fff;
  border-radius: 8px;
  font-size: var(--fs-xs);
  font-weight: 600;
  transform: translateY(-150%);
  transition: transform 0.2s var(--ease);
}
.skip-link:focus { transform: translateY(0); outline: 2px solid #fff; }

/* =========================================================
   PAGES LÉGALES
   ========================================================= */
.legal {
  width: 100%;
  max-width: 860px;
  margin-inline: auto;
  padding: clamp(90px, 12vw, 130px) var(--space-m) var(--space-section);
}
.legal__back {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--color-w-t3);
  font-size: var(--fs-xs);
  margin-bottom: var(--space-m);
}
.legal__back:hover { color: var(--brand-ink); }
.legal__title { font-size: var(--fs-h1); font-weight: 600; letter-spacing: -0.02em; line-height: 1.15; }
.legal__updated { color: var(--color-w-t3); font-size: var(--fs-xs); margin-top: var(--space-2xs); }
.legal h2 {
  font-size: var(--fs-h3);
  font-weight: 600;
  letter-spacing: -0.01em;
  margin-top: var(--space-xl);
  margin-bottom: var(--space-s);
  padding-top: var(--space-m);
  border-top: 1px solid var(--color-w-t5);
}
.legal h3 { font-size: var(--fs-m); font-weight: 600; margin-top: var(--space-m); margin-bottom: var(--space-2xs); }
.legal p { color: var(--color-w-t1); margin-bottom: var(--space-s); }
.legal a { color: var(--brand-ink); text-decoration: underline; }
.legal ul { margin: 0 0 var(--space-s); padding-left: 1.2em; list-style: disc; display: flex; flex-direction: column; gap: 6px; }
.legal li { color: var(--color-w-t1); }
.legal table { width: 100%; border-collapse: collapse; margin: var(--space-s) 0 var(--space-m); font-size: var(--fs-s); }
.legal th, .legal td { text-align: left; padding: 10px 12px; border: 1px solid var(--color-w-t5); vertical-align: top; color: var(--color-w-t1); }
.legal th { font-weight: 600; background: var(--surface); color: var(--color-white); }

/* ============================================================
   Hero — icônes en ligne sous les boutons en mobile / tablette
   (placé en fin de fichier pour primer sur les overrides globaux)
   ============================================================ */
@media (max-width: 1100px) {
  /* pas de scène épinglée sur tablette/mobile : hero classique empilé */
  .js .hero--stage { height: auto; }
  .js .hero--stage .hero__sticky { position: static; height: auto; min-height: min(720px, 92vh); justify-content: center; }
  .hero__content { order: 1; }
  .hero__showcase { order: 3; margin-top: var(--space-xl); }
  .js .hero--stage .mockup--hero { transform: none !important; transform-origin: center; width: min(90vw, 720px); }
}

/* =========================================================
   Prestations — grille de cartes (silo : home + pages offres)
   Réutilise le langage visuel des cartes (mêmes tokens/surfaces).
   ========================================================= */
.presta__grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--space-xs);
  width: 100%;
  max-width: var(--site-max);
}
.presta-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-s);
  padding: var(--space-m);
  background: var(--color-d2);
  border: 1px solid var(--color-w-t5);
  border-radius: var(--radius-m);
  color: inherit;
  transition: transform 0.35s var(--ease), border-color 0.35s var(--ease), box-shadow 0.35s var(--ease);
}
.presta-card:hover {
  transform: translateY(-4px);
  border-color: var(--color-primary-t3);
  box-shadow: 0 18px 40px -24px var(--color-primary-t3);
}
.presta-card__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 46px;
  height: 46px;
  border-radius: var(--radius-s);
  background: var(--color-primary-t5);
  color: var(--brand-ink);
}
.presta-card__icon svg { width: 24px; height: 24px; }
.presta-card__title { font-size: var(--fs-l); font-weight: 600; line-height: var(--lh-head); }
.presta-card__desc { color: var(--color-w-t3); font-size: var(--fs-s); flex: 1 1 auto; }
.presta-card__more {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3xs);
  color: var(--brand-ink);
  font-weight: 600;
  font-size: var(--fs-s);
}
.presta-card:hover .presta-card__more { gap: var(--space-xs); }
/* Lien « voir plus » sous une section (maillage interne) */
.section-more {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3xs);
  color: var(--brand-ink);
  font-weight: 600;
}
.section-more:hover { gap: var(--space-xs); }

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

/* =========================================================
   Sous-pages (pilier, offres, tarifs, méthode, à-propos)
   Layout éditorial long-form, cohérent avec la home.
   ========================================================= */
/* Fil d'Ariane */
.breadcrumb {
  width: 100%;
  max-width: var(--site-max);
  margin-inline: auto;
  padding: var(--space-l) var(--space-m) 0;
  font-size: var(--fs-xs);
  color: var(--color-w-t3);
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3xs);
  align-items: center;
}
.breadcrumb a { color: var(--color-w-t3); transition: color .25s var(--ease); }
.breadcrumb a:hover { color: var(--brand-ink); }
.breadcrumb__sep { color: var(--color-w-t5); }
.breadcrumb [aria-current] { color: var(--color-w-t1); }

/* En-tête de sous-page */
.subhero {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--space-s);
  max-width: var(--width-l);
  margin-inline: auto;
  padding: var(--space-xl) var(--space-m) var(--space-l);
}
.subhero__title { font-size: var(--fs-h1); font-weight: 700; line-height: var(--lh-head); }
.subhero .lead { font-size: var(--fs-m); }
.subhero__actions { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--space-s); margin-top: var(--space-xs); }

/* Corps éditorial */
.prose {
  width: 100%;
  max-width: var(--width-l);
  margin-inline: auto;
  display: flex;
  flex-direction: column;
  gap: var(--space-m);
}
.prose > h2 { font-size: var(--fs-h2); font-weight: 700; line-height: var(--lh-head); margin-top: var(--space-l); }
.prose > h2:first-child { margin-top: 0; }
.prose h3 { font-size: var(--fs-h3); font-weight: 600; line-height: var(--lh-head); margin-top: var(--space-xs); }
.prose p { color: var(--color-w-t1); }
.prose ul { display: flex; flex-direction: column; gap: var(--space-xs); }
.prose li { position: relative; padding-left: var(--space-m); color: var(--color-w-t1); }
.prose li::before { content: ""; position: absolute; left: 2px; top: 0.6em; width: 7px; height: 7px; border-radius: 50%; background: var(--color-primary); }
.prose a { color: var(--brand-ink); font-weight: 500; }
.prose a:hover { text-decoration: underline; }
.prose strong { color: var(--color-white); font-weight: 600; }
.prose__lead { color: var(--color-w-t1); font-size: var(--fs-m); }

/* Bloc FAQ éditorial (sous-pages) */
/* .prose-faq* : supprimé — aucun des 26 HTML n'utilisait ce markup. */

@media (max-width: 640px) {
  .subhero { padding-top: var(--space-l); }
}

/* Glow décoratif derrière l'en-tête de sous-page */
.subhero { position: relative; isolation: isolate; }
.subhero__glow {
  position: absolute; top: -12%; left: 50%; transform: translateX(-50%);
  width: min(760px, 94%); height: auto; z-index: -1;
  opacity: var(--glow-opacity); pointer-events: none;
}

/* =========================================================
   Grille de cartes à icône (bénéfices, fonctionnalités, raisons)
   Même langage visuel que les cartes du site.
   ========================================================= */
.feature-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-xs);
  width: 100%;
  max-width: var(--site-max);
}
.feature-grid--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.feature-grid--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.feature-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-s);
  padding: var(--space-m);
  background: var(--color-d2);
  border: 1px solid var(--color-w-t5);
  border-radius: var(--radius-m);
}
.feature-card__icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 46px; height: 46px; border-radius: var(--radius-s);
  background: var(--color-primary-t5); color: var(--brand-ink);
}
.feature-card__icon svg { width: 24px; height: 24px; }
.feature-card__title { font-size: var(--fs-l); font-weight: 600; line-height: var(--lh-head); }
.feature-card__text { color: var(--color-w-t3); font-size: var(--fs-s); }
.feature-card__text a { color: var(--brand-ink); font-weight: 500; }
.feature-card__text a:hover { text-decoration: underline; }

/* Liste à puces "check" (comme les features tarifaires) hors carte */
.check-list { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-xs); width: 100%; max-width: var(--width-xl); }
.check-list li { display: flex; align-items: flex-start; gap: var(--space-xs); color: var(--color-w-t1); }
.check-list__ico { width: 22px; height: 22px; flex: 0 0 22px; margin-top: 1px; }

/* Corps d'une section de sous-page (sous le .section-head) */
.section-body {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-l);
}
.section-body .prose { text-align: left; }

@media (max-width: 900px) {
  .feature-grid, .feature-grid--4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 640px) {
  .feature-grid, .feature-grid--2, .feature-grid--4 { grid-template-columns: 1fr; }
  .check-list { grid-template-columns: 1fr; }
  .section-body { margin-top: var(--space-l); }
}

/* Liste de liens en "chips" (zones desservies, secteurs) */
.linklist { display: flex; flex-wrap: wrap; gap: var(--space-xs); justify-content: center; width: 100%; max-width: var(--width-xl); }
.linklist a {
  display: inline-flex; align-items: center;
  padding: var(--space-2xs) var(--space-s);
  background: var(--color-d2); border: 1px solid var(--color-w-t5);
  border-radius: var(--radius-full); font-size: var(--fs-s); color: var(--color-w-t1);
  transition: border-color 0.25s var(--ease), color 0.25s var(--ease), transform 0.25s var(--ease);
}
.linklist a:hover { border-color: var(--color-primary-t3); color: var(--brand-ink); transform: translateY(-2px); }

/* Chiffres clés — traitement éditorial à filets fins (pas d'encadré) */
.stats {
  display: flex;
  flex-wrap: wrap;
  width: 100%;
  max-width: var(--width-l);
  border-top: 1px solid var(--color-w-t5);
  border-bottom: 1px solid var(--color-w-t5);
}
.stats__item {
  flex: 1 1 0;
  min-width: 150px;
  padding: var(--space-l) var(--space-m);
  text-align: center;
  border-left: 1px solid var(--color-w-t5);
}
.stats__item:first-child { border-left: 0; }
.stats__num {
  display: block;
  font-size: clamp(2rem, 1.4rem + 2vw, 2.75rem);
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.02em;
  color: var(--color-white);
}
.stats__num span { color: var(--brand-ink); }
.stats__lbl {
  display: block;
  margin-top: var(--space-xs);
  font-size: var(--fs-xs);
  color: var(--color-w-t3);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}
@media (max-width: 620px) {
  .stats { flex-direction: column; }
  .stats__item { border-left: 0; border-top: 1px solid var(--color-w-t5); padding-block: var(--space-m); }
  .stats__item:first-child { border-top: 0; }
}

/* =========================================================
   Ambiance — grille de fond + accent serif italique (inspiration éditoriale)
   ========================================================= */
/* Grille tech subtile derrière les héros, fondue sur les bords */
.hero__sticky::before, .subhero::before {
  content: "";
  position: absolute;
  inset: -1px;
  z-index: -3;
  background-image:
    linear-gradient(to right, var(--grid-line) 1px, transparent 1px),
    linear-gradient(to bottom, var(--grid-line) 1px, transparent 1px);
  background-size: 66px 66px;
  -webkit-mask-image: radial-gradient(ellipse 78% 82% at 50% 42%, #000 12%, transparent 72%);
          mask-image: radial-gradient(ellipse 78% 82% at 50% 42%, #000 12%, transparent 72%);
  pointer-events: none;
}
/* Halo bleu atmosphérique derrière le subhero (le hero l'a sur .hero__sticky) */
.subhero { background: radial-gradient(ellipse 62% 58% at 50% 30%, var(--bloom), transparent 66%); }

/* =========================================================
   Direction artistique des pages éditoriales — SaaS premium
   Une composition nette et asymétrique : les illustrations sont
   entièrement en CSS, donc sans poids d'image ni contenu factice.
   ========================================================= */
main > .subhero {
  width: min(100% - 2 * var(--space-m), var(--site-max));
  max-width: var(--site-max);
  min-height: clamp(410px, 40vw, 530px);
  align-items: flex-start;
  justify-content: center;
  gap: var(--space-m);
  margin-top: var(--space-m);
  padding: clamp(58px, 7vw, 104px) clamp(30px, 7vw, 110px) clamp(58px, 7vw, 104px) 0;
  overflow: visible;
  background: radial-gradient(ellipse 56% 64% at 76% 48%, var(--bloom), transparent 72%);
}
main > .subhero .subhero__glow { display: none; }
main > .subhero > :not(.subhero__showcase):not(.subhero__glow) { position: relative; z-index: 1; }
main > .subhero::before,
main > .subhero::after { display: none; }

/* Mockup navigateur : un objet produit concret, dans le même langage que la home. */
.subhero__showcase {
  position: absolute;
  top: 50%;
  right: clamp(-45px, -2vw, -10px);
  width: clamp(405px, 43vw, 620px);
  z-index: 0;
  transform: translateY(-47%) perspective(1200px) rotateY(-10deg) rotateX(4deg);
  transform-origin: right center;
  pointer-events: none;
}
.subhero__browser-bar span:nth-child(2) { background: #febc2e; }
.subhero__browser-bar span:nth-child(3) { background: #28c840; }
.subhero__browser-canvas > div i:nth-child(2) { transform: translateY(-10px); background: linear-gradient(145deg, var(--color-primary), var(--color-primary-t5)); }
main > .subhero .pill {
  color: var(--brand-ink);
  border-color: var(--color-primary-t3);
  background: var(--color-primary-t5);
  letter-spacing: .08em;
}
main > .subhero > .subhero__title {
  width: 480px;
  max-width: 100%;
  font-size: clamp(1.75rem, 1.3rem + 1.7vw, 2.65rem);
  letter-spacing: -.055em;
  text-align: left;
}
main > .subhero .lead { width: 460px; max-width: 100%; color: var(--color-w-t1); font-size: var(--fs-s); text-align: left; }
main > .subhero > .subhero__actions { justify-content: flex-start; margin-top: var(--space-xs); }

main > .subhero ~ .section .section-head .pill { color: var(--brand-ink); border-color: var(--color-primary-t3); }

main > .subhero ~ .section .prose {
  max-width: 760px;
  padding-left: var(--space-m);
  border-left: 2px solid var(--color-primary);
}
main > .subhero ~ .section .prose p { max-width: 68ch; }

/* Surfaces de produit : rayon contenu, bordure précise, mouvement discret. */
main > .subhero ~ .section .feature-card,
main > .subhero ~ .section .presta-card {
  position: relative;
  overflow: hidden;
  border-color: var(--surface-border);
  border-radius: 14px;
  box-shadow: 0 18px 42px -42px rgba(9, 29, 75, .78);
  transition: transform .24s var(--ease), border-color .24s var(--ease), box-shadow .24s var(--ease);
}
main > .subhero ~ .section .feature-card::before,
main > .subhero ~ .section .presta-card::before {
  content: "";
  position: absolute;
  inset: 0;
  border: 1px solid transparent;
  border-radius: inherit;
  background: linear-gradient(135deg, rgba(30, 105, 254, .13), transparent 35%) border-box;
  -webkit-mask: linear-gradient(#fff 0 0) padding-box, linear-gradient(#fff 0 0);
          mask: linear-gradient(#fff 0 0) padding-box, linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  pointer-events: none;
}
@media (hover: hover) and (pointer: fine) {
  main > .subhero ~ .section .feature-card:hover,
  main > .subhero ~ .section .presta-card:hover {
    transform: translateY(-4px);
    border-color: var(--color-primary-t3);
    box-shadow: 0 26px 54px -38px rgba(30, 105, 254, .28);
  }
}
main > .subhero ~ .section .check-list { gap: var(--space-s); max-width: 940px; }
main > .subhero ~ .section .check-list li {
  min-height: 64px;
  padding: var(--space-s);
  border: 1px solid var(--surface-border);
  border-radius: 10px;
  background: var(--color-d2);
}
main > .subhero ~ .section .check-list__ico { color: var(--brand-ink); }
main > .subhero ~ .section .stats {
  overflow: hidden;
  border: 1px solid var(--surface-border);
  border-radius: 14px;
  background: var(--color-d2);
}
main > .subhero ~ .section .stats__item { border-color: var(--surface-border); }
main > .subhero ~ .cta .cta__inner--banner {
  width: min(100%, var(--width-xl));
  padding: clamp(34px, 6vw, 70px);
  border: 1px solid var(--color-primary-t3);
  border-radius: 18px;
  background: linear-gradient(135deg, var(--color-primary-t5), transparent 48%), var(--color-d2);
  box-shadow: 0 32px 70px -55px rgba(30, 105, 254, .5);
}

@media (max-width: 760px) {
  main > .subhero {
    width: 100%;
    min-height: 0;
    align-items: center;
    margin-top: 0;
    padding: var(--space-l) var(--space-m) 0;
    overflow: clip;
    background: none;
  }
  .subhero__showcase {
    position: relative;
    top: auto;
    right: auto;
    bottom: auto;
    order: 5;
    width: min(100%, 360px);
    margin-top: var(--space-m);
    transform: none;
  }
  main > .subhero > .subhero__title { width: 100%; max-width: 22rem; font-size: clamp(1.95rem, 8.6vw, 2.55rem); text-align: center; }
  main > .subhero .lead { width: 100%; max-width: 34rem; font-size: var(--fs-s); text-align: center; text-wrap: balance; }
  main > .subhero > .subhero__actions { justify-content: center; }
  main > .subhero ~ .section .prose { padding-left: var(--space-s); }
  main > .subhero ~ .section .check-list li { min-height: 0; }
}

/* Les sous-pages réemploient volontairement les composants de la home.
   Seul le hero varie : c'est une extension, pas un deuxième design system. */
.subhero__showcase { display: grid; place-items: center; }
.subhero__mockup {
  width: 100%;
  margin: 0;
  transform: none;
  border-radius: var(--radius-m);
}
/* Variantes de squelettes : le visuel correspond au type de page, sans image lourde. */
.subhero__mockup--pricing .mk-row { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.subhero__mockup--pricing .mk-card:nth-child(2) { transform: translateY(-8px); background: var(--color-primary-t4); border-color: var(--color-primary-t3); }
.subhero__mockup--pricing .mk-card:nth-child(3) { display: none; }
.subhero__mockup--process .mk-row { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.subhero__mockup--process .mk-card { height: 54px; border-radius: 99px; }
.subhero__mockup--process .mk-card::after { content: ""; display: block; width: 8px; height: 8px; margin: 12px; border-radius: 50%; background: var(--color-primary); }
.subhero__mockup--editorial .mk-hero { padding-bottom: 8px; border-bottom: 1px solid var(--surface-border); }
.subhero__mockup--editorial .mk-row { grid-template-columns: 1fr; }
.subhero__mockup--editorial .mk-card { height: 42px; }
.subhero__mockup--editorial .mk-card:nth-child(2),
.subhero__mockup--editorial .mk-card:nth-child(3) { display: none; }
.subhero__mockup--profile .mk-hero { display: grid; grid-template-columns: 56px 1fr; column-gap: 14px; align-items: center; }
.subhero__mockup--profile .mk-tag { width: 56px; height: 56px; grid-row: span 3; border-radius: 50%; }
.subhero__mockup--profile .mk-title { width: 90%; }
.subhero__mockup--profile .mk-title--2 { width: 62%; }
.subhero__mockup--profile .mk-row { grid-template-columns: repeat(3, 1fr); }
.subhero__mockup--onepage .mk-row { grid-template-columns: 1fr; }
.subhero__mockup--onepage .mk-card { height: 92px; }
.subhero__mockup--onepage .mk-card:nth-child(2),
.subhero__mockup--onepage .mk-card:nth-child(3) { display: none; }
.subhero__mockup--refonte .mk-row { grid-template-columns: repeat(2, 1fr); }
.subhero__mockup--refonte .mk-card:first-child { opacity: .45; }
.subhero__mockup--refonte .mk-card:nth-child(2) { border-color: var(--color-primary-t3); background: var(--color-primary-t5); }
.subhero__mockup--refonte .mk-card:nth-child(3) { display: none; }
main > .subhero ~ .section .prose { padding-left: 0; border-left: 0; }
main > .subhero ~ .section .feature-card,
main > .subhero ~ .section .presta-card {
  border-radius: var(--radius-m);
  box-shadow: none;
  transform: none;
}
main > .subhero ~ .section .feature-card::before,
main > .subhero ~ .section .presta-card::before { display: none; }
main > .subhero ~ .section .check-list li { min-height: 0; padding: 0; border: 0; border-radius: 0; background: transparent; }
main > .subhero ~ .section .stats { border: 0; border-radius: 0; background: transparent; }
main > .subhero ~ .cta .cta__inner--banner { padding: 0; border: 0; border-radius: 0; background: transparent; box-shadow: none; }
.section--intro { padding-bottom: 0; }

@media (min-width: 761px) {
  main > .subhero:has(.subhero__showcase) { padding-right: clamp(260px, 28vw, 360px); }
  .subhero__showcase { right: calc((100vw - 100%) / -2 - 24px); }
  .subhero__showcase { z-index: 0; }
}

/* Les anciens mockups décoratifs des sous-pages sont remplacés par celui du hero. */
main > .subhero + .mockup-band { display: none; }

/* Mot-clé surligné en "chip" bleu (façon pixelmarin) */
.hl {
  color: var(--on-primary);
  background: var(--color-primary);
  padding: 0.04em 0.32em;
  border-radius: 7px;
  -webkit-box-decoration-break: clone;
          box-decoration-break: clone;
}

/* =========================================================
   Mockup navigateur — illustration de site animée (pur CSS)
   ========================================================= */
.mockup-band { padding-block: var(--space-s) var(--space-2xl); overflow: visible; }
/* Scène de zoom épinglé (home). Sans JS : simple bloc centré. */
.mockup-stage { position: relative; }
.mockup {
  width: 100%;
  border-radius: 16px; overflow: hidden;
  background: var(--surface-2);
  border: 1px solid var(--surface-border);
  box-shadow: 0 60px 120px -55px rgba(2, 8, 22, 0.5);
}
/* Mockup vedette : centré, large, grossit au scroll */
.mockup--hero {
  width: min(62vw, 940px);
  margin-inline: auto;
  transform-origin: center center;
  will-change: transform;
}
/* Barre du navigateur — contraste garanti en clair ET sombre */
.mockup__bar {
  display: flex; align-items: center; gap: 8px;
  padding: 12px 16px;
  background: var(--surface-2);
  border-bottom: 1px solid var(--surface-border);
}
.mockup__dot { width: 11px; height: 11px; border-radius: 50%; flex: 0 0 auto; }
.mockup__dot:nth-child(1) { background: #ff5f57; }
.mockup__dot:nth-child(2) { background: #febc2e; }
.mockup__dot:nth-child(3) { background: #28c840; }
.mockup__url {
  margin-left: 10px; flex: 1; max-width: 340px; height: 26px; border-radius: 8px;
  background: var(--color-d6); border: 1px solid var(--surface-border);
  display: flex; align-items: center; gap: 7px; padding: 0 12px;
  font-size: var(--fs-2xs); color: var(--color-w-t2); letter-spacing: 0.02em;
}
.mockup__url::before {
  content: ""; width: 9px; height: 9px; flex: 0 0 auto;
  border: 1.6px solid var(--color-w-t4); border-radius: 50%;
}
.mockup__url-text { flex: 0 0 auto; white-space: nowrap; }
.mockup__caret { flex: 0 0 1.5px; width: 1.5px; height: 13px; background: var(--color-primary); margin-left: 1px; }
.mockup__screen {
  position: relative; overflow: hidden;
  padding: 22px; display: flex; flex-direction: column; gap: 15px;
  background: var(--color-d6);
}
/* Balayage lumineux (contenu qui paraît vivant) */
.mockup__screen::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(105deg, transparent 40%, rgba(47, 107, 255, 0.11) 50%, transparent 60%);
  transform: translateX(-75%);
}
.mk-hero {
  position: relative; padding: 18px; border-radius: 11px;
  display: flex; flex-direction: column; gap: 9px; align-items: flex-start;
  background: radial-gradient(120% 140% at 18% 0%, var(--color-primary-t5), transparent 60%);
  border: 1px solid var(--surface-border);
}
.mk-tag { width: 66px; height: 12px; border-radius: 6px; background: var(--color-primary); opacity: 0.85; }
.mk-title { width: 72%; height: 13px; border-radius: 6px; background: var(--color-w-t3); }
.mk-title--2 { width: 48%; }
.mk-cta { width: 92px; height: 26px; border-radius: 8px; background: var(--color-primary); margin-top: 5px; }
.mk-row { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
.mk-card { height: 66px; border-radius: 10px; background: var(--surface-2); border: 1px solid var(--surface-border); position: relative; }
.mk-card::before { content: ""; position: absolute; left: 13px; top: 13px; width: 22px; height: 22px; border-radius: 7px; background: var(--color-primary-t5); }
.mk-line { height: 10px; border-radius: 5px; background: var(--color-w-t5); }
.mk-line--lg { height: 12px; width: 60%; background: var(--color-w-t3); }
.mk-line--sm { width: 38%; }

/* Contenu vivant (le zoom au scroll est géré en JS — voir script.js) */
.mockup__caret { animation: mk-caret 1.1s steps(1) infinite; }
.mockup__screen::after { animation: mk-shimmer 3.8s ease-in-out infinite; }
.mk-cta { animation: mk-pulse 2.6s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce) {
  .mockup__caret, .mockup__screen::after, .mk-cta { animation: none; }
}
@keyframes mk-caret { 50% { opacity: 0; } }
@keyframes mk-shimmer { 0% { transform: translateX(-75%); } 55%, 100% { transform: translateX(185%); } }
@keyframes mk-pulse { 0%, 100% { box-shadow: 0 0 0 0 var(--color-primary-t3); } 55% { box-shadow: 0 0 0 7px transparent; } }

/* Le contenu du mockup se construit une fois le mockup à pleine taille (.is-live) */
.js .hero--stage .mk-hero > *,
.js .hero--stage .mockup__screen > .mk-line { opacity: 0; transform-origin: left center; }
.js .hero--stage .mk-card::before { opacity: 0; transform: scale(0.6); }
.mockup--hero.is-live .mk-hero > *,
.mockup--hero.is-live .mockup__screen > .mk-line { animation: mk-draw 0.55s var(--ease) both; }
.mockup--hero.is-live .mk-card::before { animation: mk-fade 0.55s var(--ease) both; }
.mockup--hero.is-live .mk-hero > *:nth-child(1) { animation-delay: 0.06s; }
.mockup--hero.is-live .mk-hero > *:nth-child(2) { animation-delay: 0.14s; }
.mockup--hero.is-live .mk-hero > *:nth-child(3) { animation-delay: 0.21s; }
.mockup--hero.is-live .mk-hero > *:nth-child(4) { animation-delay: 0.30s; }
.mockup--hero.is-live .mk-card:nth-child(1)::before { animation-delay: 0.38s; }
.mockup--hero.is-live .mk-card:nth-child(2)::before { animation-delay: 0.46s; }
.mockup--hero.is-live .mk-card:nth-child(3)::before { animation-delay: 0.54s; }
.mockup--hero.is-live .mockup__screen > .mk-line:nth-child(3) { animation-delay: 0.62s; }
.mockup--hero.is-live .mockup__screen > .mk-line:nth-child(4) { animation-delay: 0.69s; }
.mockup--hero.is-live .mockup__screen > .mk-line:nth-child(5) { animation-delay: 0.76s; }
@keyframes mk-draw { from { transform: scaleX(0.04); opacity: 0; } to { transform: scaleX(1); opacity: 1; } }
@keyframes mk-fade { from { transform: scale(0.5); opacity: 0; } to { transform: scale(1); opacity: 1; } }

@media (max-width: 900px) {
  .mockup--hero { width: 90vw; }
}

/* Glow doux derrière le CTA final */
.section.cta { position: relative; isolation: isolate; }
.section.cta::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background: radial-gradient(ellipse 46% 62% at 50% 55%, var(--bloom), transparent 70%);
  pointer-events: none;
}

/* =========================================================
   BLOCS « DESIGN TECH » BESPOKE — couche d'alias de tokens
   Mappe le vocabulaire des blocs (--panel, --line, --text…)
   sur tes tokens réels : le thème dark/light reste géré par
   [data-theme] (aucune duplication). Retirer ce bloc + les
   <section class="bx-*"> des pages suffit à tout annuler.
   ========================================================= */
:root {
  --bg: var(--color-d6);
  --panel: var(--surface);
  --panel-2: #202a3a;                 /* inset (dark) */
  --line: var(--surface-border);
  --line-strong: rgba(255, 255, 255, 0.16);
  --text: var(--color-white);
  --text-2: var(--color-w-t3);
  --text-3: rgba(255, 255, 255, 0.52);
  --brand: var(--color-primary);
  --brand-2: #6ea0ff;
  --brand-ink: #6ea0ff;               /* accent lisible sur fond sombre */
  --brand-t: rgba(30, 105, 254, 0.12);
  --brand-t2: rgba(30, 105, 254, 0.22);
  --ok: #3ecf8e;
  --warn: #e5a13a;
  /* --danger et --mono existent déjà dans ta charte */
  --ok-t: rgba(62, 207, 142, 0.13);
  --danger-t: rgba(255, 77, 99, 0.13);
  --r-s: var(--radius-s);
  --r-m: var(--radius-m);
  --r-l: 20px;
  --r-pill: var(--radius-full);
  --maxw: var(--site-max);        /* largeur du site (1366px), comme le reste */
}
[data-theme="light"] {
  --panel-2: #f1f4fa;
  --line-strong: rgba(20, 24, 31, 0.18);
  --text-3: rgba(20, 24, 31, 0.60);
  --brand-ink: #1a56d6;               /* accent lisible sur fond clair */
  --brand-t: rgba(30, 105, 254, 0.07);
  --brand-t2: rgba(30, 105, 254, 0.13);
  --ok: #15925a;
  --warn: #b8760a;
  --ok-t: rgba(21, 146, 90, 0.10);
  --danger-t: rgba(210, 63, 74, 0.09);
}

/* =========================================================
   MEGA-MENU — version aérée (surcharge, desktop ≥1101px)
   Le panneau reste le seul élément « flottant » encadré ;
   à l'intérieur : lignes ouvertes + filets, plus de cartes.
   ========================================================= */
@media (min-width: 1101px) {
  .mega-menu {
    grid-template-columns: minmax(0, 1.5fr) minmax(206px, 0.7fr);
    padding: var(--space-l);
    background: color-mix(in srgb, var(--surface) 97%, transparent);
    border-color: var(--surface-border);
  }
  .mega-menu__title { letter-spacing: -0.02em; }
  .mega-menu__links {
    grid-template-columns: 1fr;
    gap: 3px;
    margin-top: var(--space-s);
  }
  .mega-menu__link {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    row-gap: 2px;
    align-items: center;
    padding: 11px 12px;
    border: 0;
    border-radius: var(--radius-s);
    background: transparent;
    transition: background-color 0.2s var(--ease);
  }
  .mega-menu__link b { grid-column: 1; grid-row: 1; }
  .mega-menu__link span { grid-column: 1; grid-row: 2; }
  .mega-menu__link::after {
    content: "→";
    grid-column: 2;
    grid-row: 1 / span 2;
    align-self: center;
    color: var(--color-w-t5);
    font-size: var(--fs-s);
    transition: transform 0.2s var(--ease), color 0.2s var(--ease);
  }
  .mega-menu__link:hover,
  .mega-menu__link:focus-visible {
    background: color-mix(in srgb, var(--color-primary) 8%, transparent);
  }
  .mega-menu__link:hover::after,
  .mega-menu__link:focus-visible::after {
    color: var(--brand-ink);
    transform: translateX(3px);
  }
  .mega-menu__feature {
    border: 0;
    border-left: 1px solid var(--surface-border);
    border-radius: 0;
    background: transparent;
    padding: 2px 0 2px var(--space-l);
  }
  .mega-menu__feature strong { letter-spacing: -0.01em; }
}

/* =========================================================
   PASSE DESIGN SYSTEM AÉRÉ + ANIMATIONS (composants + blocs)
   Généré : surcharges globales, se propagent à toutes les pages.
   ========================================================= */


/* ============ COMPOSANTS (restyle aéré) ============ */


/* ---- feature-card ---- */
/* =============================================================
   OVERRIDE — feature-card / feature-grid  →  AÉRÉ (Stripe-like)
   Cartes boxy (bordure + ombre + rond icône) transformées en
   items OUVERTS séparés par un filet, glyphe icône inline,
   entrée .is-in en cascade + micro-hover. Aucun HTML modifié.
   ============================================================= */

/* --- Grille : plus d'air, filet supérieur commun, reflow conservé ---
   FIX 2 : on centralise le padding-haut dans un token --grid-pad,
   partagé par la grille ET le tick d'accent (::after), pour qu'ils
   restent verrouillés ensemble (desktop + mobile). */
.feature-grid {
  --grid-pad: clamp(24px, 3vw, 40px);   /* source unique de vérité */
  gap: clamp(28px, 4vw, 52px) clamp(24px, 3vw, 44px);
  max-width: var(--maxw);
  /* petit filet qui coiffe la grille : effet "tableau aéré" */
  border-top: 1px solid var(--line);
  padding-top: var(--grid-pad);
}
/* on garde les variantes de colonnes telles quelles (2 / 4) — juste l'air change */

/* --- La carte : plus de boîte. Bloc ouvert, filet + glyphe --- */
.feature-card {
  gap: 12px;
  padding: 0;                       /* on retire le padding de boîte */
  background: transparent;          /* KILL le fond de carte */
  border: 0;                        /* KILL la bordure */
  border-radius: 0;
  /* état d'entrée (pré-anim) — révélé par .is-in.
     Cette transition (base) reste la SEULE déclarée : elle porte le
     transition-delay, que les règles nth-child ci-dessous décalent. */
  opacity: 0;
  transform: translateY(14px);
  transition:
    opacity .6s var(--ease),
    transform .6s var(--ease);
}
.feature-card.is-in { opacity: 1; transform: none; }

/* cascade : décalage progressif entre frères (pas de 70ms, plafonné) */
.feature-card:nth-child(1)  { transition-delay: 0ms; }
.feature-card:nth-child(2)  { transition-delay: 70ms; }
.feature-card:nth-child(3)  { transition-delay: 140ms; }
.feature-card:nth-child(4)  { transition-delay: 210ms; }
.feature-card:nth-child(5)  { transition-delay: 280ms; }
.feature-card:nth-child(6)  { transition-delay: 350ms; }
.feature-card:nth-child(n+7){ transition-delay: 420ms; }

/* --- Icône : glyphe inline, plus de pastille pleine --- */
.feature-card__icon {
  width: auto; height: auto;
  border-radius: 0;
  background: transparent;          /* KILL le rond bleu plein */
  color: var(--brand-ink);          /* accent lisible dans les 2 thèmes */
  align-self: flex-start;
  transition: transform .2s var(--ease), color .2s var(--ease);
}
.feature-card__icon svg {
  width: 22px; height: 22px;
  stroke-width: 1.6;                /* trait fin, cohérent maison */
}

/* --- Titre : 700, serré, réagit au survol --- */
.feature-card__title {
  font-weight: 700;
  letter-spacing: -.02em;
  color: var(--text);
  transition: color .2s var(--ease);
}

/* --- Texte : secondaire, lien = accent unique --- */
.feature-card__text {
  color: var(--text-2);
  font-size: var(--fs-s);
}
.feature-card__text a { color: var(--brand); font-weight: 500; }

/* =============================================================
   SURCHARGE DU SCOPE SOUS-PAGE — on annule la carte "produit"
   (bordure, ombre, ::before dégradé, hover lift) au profit de
   la version aérée + un filet-accent en tête qui se dessine.
   ============================================================= */
main > .subhero ~ .section .feature-card {
  position: relative;
  overflow: visible;                /* pas de clipping du filet-accent */
  border: 0;
  border-radius: 0;
  box-shadow: none;                 /* KILL l'ombre */
  transform: translateY(14px);      /* conserve l'état d'entrée */
  /* FIX 1 : PAS de raccourci `transition:` ici.
     Le shorthand aurait remis transition-delay à 0s et, comme ce
     sélecteur (0,3,1) bat .feature-card:nth-child(n) (0,2,0), il
     aurait écrasé la cascade → toutes les cartes apparaissaient
     ensemble sur les sous-pages subhero. La transition de base
     .feature-card s'applique déjà et préserve les délais nth-child. */
}
main > .subhero ~ .section .feature-card.is-in { transform: none; }

/* on neutralise le liseré dégradé d'origine… */
main > .subhero ~ .section .feature-card::before { display: none; }

/* …et on ajoute un court filet d'accent en tête, qui grandit au survol
   (analogue "underline grow", GPU-friendly : scaleX).
   FIX 2 : `top` calé sur le MÊME token --grid-pad que la grille,
   donc toujours posé pile sur le filet, desktop comme mobile. */

/* --- Micro-hover : titre → brand, glyphe glisse, filet-accent se dessine --- */
@media (hover: hover) and (pointer: fine) {
  main > .subhero ~ .section .feature-card:hover { transform: none; }

  .feature-card:hover .feature-card__title { color: var(--brand); }
  .feature-card:hover .feature-card__icon  { transform: translateX(3px); color: var(--brand); }

}

/* --- Reflow mobile : filet moins large, air resserré ---
   FIX 2 : on redéfinit --grid-pad ici → grille ET tick suivent
   automatiquement la même valeur mobile (26px). */
@media (max-width: 640px) {
  .feature-grid { --grid-pad: 26px; gap: 26px; }
}

/* =============================================================
   GARDE MOUVEMENT RÉDUIT — tout visible, aucune anim
   ============================================================= */
@media (prefers-reduced-motion: reduce) {
  .feature-card,
  .feature-card.is-in,
  main > .subhero ~ .section .feature-card,
  main > .subhero ~ .section .feature-card.is-in,
  .feature-card__icon,
  .feature-card__title,
  .feature-card__step,
  
}


/* ---- presta-card ---- */
/* =========================================================================
   OVERRIDE — presta-card  (services / prestations grid)
   Airy hairline restyle + staggered .is-in entrance + hover micro-interactions.
   Appended at end of stylesheet → equal-specificity rules win over originals.
   Colors use the alias layer only. Works in light + dark, reflows on mobile.

   REVEAL WIRING (fix 1, cleaner alternative): the class hook stays `.is-in`.
   A dedicated 4-line IntersectionObserver in assets/js/script.js observes
   `.presta-card` and toggles `.is-in`. It lives AFTER the `prefersReduced`
   early-return, so reduced-motion users never get the class (and the guard
   block below freezes the final state anyway). Keeping our own observer means
   the site's generic one does NOT overwrite our nth-child stagger with an
   inline transition-delay, nor push a conflicting `.reveal` 24px offset.
   ========================================================================= */

/* --- Grid: keep the 4-up reflow, breathe a little more between cards ------- */
.presta__grid {
  gap: var(--space-m);                 /* was --space-xs : more air */
  align-items: stretch;
}

/* --- Card: kill the box. Light hairline card, no shadow, no tint fill ------ */
.presta-card {
  gap: var(--space-s);
  padding: var(--space-l) var(--space-m);
  background: transparent;             /* drop --color-d2 fill */
  border: 1px solid var(--line);       /* single hairline */
  border-radius: var(--r-l);
  box-shadow: none;
  color: inherit;
  position: relative;
  isolation: isolate;
  /* entrance pre-state (revealed via .is-in) */
  opacity: 0;
  transform: translateY(14px);
  transition:
    opacity .6s var(--ease),
    transform .6s var(--ease),
    border-color .25s var(--ease),
    background-color .25s var(--ease);
}
.presta-card.is-in { opacity: 1; transform: none; }

/* Staggered reveal — continuous diagonal cascade, 50ms steps.
   The delay applies only to opacity+transform (rungs 1 & 2 of the shorthand);
   border-color/background-color keep 0s so hover stays instant.
   Rungs 1–8 cover the first two rows of a 4-up grid; any 9th+ card is capped
   at a shared 400ms so NOTHING past the ladder pops in with a 0ms delay. */
.presta-card:nth-child(1)      { transition-delay:   0ms,   0ms, 0s, 0s; }
.presta-card:nth-child(2)      { transition-delay:  50ms,  50ms, 0s, 0s; }
.presta-card:nth-child(3)      { transition-delay: 100ms, 100ms, 0s, 0s; }
.presta-card:nth-child(4)      { transition-delay: 150ms, 150ms, 0s, 0s; }
.presta-card:nth-child(5)      { transition-delay: 200ms, 200ms, 0s, 0s; }
.presta-card:nth-child(6)      { transition-delay: 250ms, 250ms, 0s, 0s; }
.presta-card:nth-child(7)      { transition-delay: 300ms, 300ms, 0s, 0s; }
.presta-card:nth-child(8)      { transition-delay: 350ms, 350ms, 0s, 0s; }
.presta-card:nth-child(n+9)    { transition-delay: 400ms, 400ms, 0s, 0s; }

/* Hover: subtle lift + faint brand tint + accent hairline (no drop shadow) */
@media (hover: hover) and (pointer: fine) {
  .presta-card:hover {
    transform: translateY(-4px);
    border-color: var(--brand-t);
    background: var(--brand-t2);        /* very light brand wash */
    box-shadow: none;
  }
}

/* --- Icon: flat brand-tint chip, no heavy fill ---------------------------- */
.presta-card__icon {
  width: 42px;
  height: 42px;
  border-radius: var(--r-m);
  background: var(--brand-t);
  color: var(--brand);
  transition: transform .25s var(--ease), background-color .25s var(--ease);
}
.presta-card__icon svg { stroke-width: 1.6; }
@media (hover: hover) and (pointer: fine) {
  .presta-card:hover .presta-card__icon {
    transform: translateY(-1px) scale(1.04);
    background: var(--brand-t2);
  }
}

/* --- Title: 700 / tight tracking ------------------------------------------ */
.presta-card__title {
  font-weight: 700;
  letter-spacing: -.02em;
  color: var(--text);
}

/* --- Description: secondary text ------------------------------------------ */
.presta-card__desc {
  color: var(--text-2);
  flex: 1 1 auto;
}

/* --- « en savoir plus »: mono uppercase link with animated → arrow -------- */
.presta-card__more {
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .08em;
  color: var(--brand-ink);
  gap: .5em;
}
/* Draw the arrow ourselves so we can animate it independently of icon markup.
   NOTE (fix 3): no permanent `will-change` here — a 4px nudge doesn't warrant
   a pinned compositing layer per card. The hover block below opts the layer in
   only for the pointer devices that can actually trigger the transform. */
/* Le HTML porte déjà un SVG flèche : sans ça on lisait « EN SAVOIR PLUS → → ».
   On masque le SVG plutôt que d'éditer 21 pages, et on garde le ::after,
   seul à être animé au survol. */
.presta-card__more > svg { display: none; }
.presta-card__more::after {
  content: "\2192";                    /* → */
  font-family: var(--mono);
  transition: transform .2s var(--ease);
}
@media (hover: hover) and (pointer: fine) {
  .presta-card:hover .presta-card__more { gap: .5em; }   /* neutralise old gap bump */
  .presta-card:hover .presta-card__more::after {
    transform: translateX(4px);
    will-change: transform;            /* scoped: only while hovering */
  }
}

/* =========================================================================
   Landing / sub-page variant (main > .subhero ~ .section .presta-card)
   Remove the boxed shadow + ::before gradient fill → same airy hairline.
   IMPORTANT (fix 2): the original rule at this selector (specificity 0,3,1)
   sets `transform: none`, which outranks both `.presta-card` (0,1,0) and
   `.presta-card.is-in` (0,2,0) — killing the slide on sub-pages. So we must
   re-establish BOTH the pre-state and the revealed state at THIS specificity,
   or subpage cards would fade without lifting.
   ========================================================================= */
main > .subhero ~ .section .presta-card {
  position: relative;
  overflow: visible;
  border: 1px solid var(--line);
  border-radius: var(--r-l);
  box-shadow: none;
  transform: translateY(14px);         /* re-arm the entrance offset */
}
main > .subhero ~ .section .presta-card.is-in {
  transform: none;                      /* revealed → slide home */
}
main > .subhero ~ .section .presta-card::before {
  display: none;                       /* kill gradient border-mask fill */
  content: none;
}
@media (hover: hover) and (pointer: fine) {
  main > .subhero ~ .section .presta-card:hover {
    transform: translateY(-4px);
    border-color: var(--brand-t);
    background: var(--brand-t2);
    box-shadow: none;
  }
}

/* --- Reduced-motion guard: freeze final state, no transitions/keyframes ----
   Covers the subpage selector too so its re-armed 14px offset can't strand a
   reduced-motion user on an invisible/offset card (the JS observer also never
   fires under reduced motion, so `.is-in` would never arrive on its own). */
@media (prefers-reduced-motion: reduce) {
  .presta-card,
  .presta-card.is-in,
  main > .subhero ~ .section .presta-card,
  main > .subhero ~ .section .presta-card.is-in,
  .presta-card__icon,
  .presta-card__more,
  .presta-card__more::after {
    opacity: 1 !important;
    transform: none !important;
    animation: none !important;
    transition: none !important;
    transition-delay: 0s !important;
  }
}


/* ---- pricing / price-card (+ replace conic border) ---- */
/* =========================================================================
   PRICING / PRICE-CARD — AIRY OVERRIDE (Stripe-like, hairline, aéré)
   Appended at end of stylesheet → equal-specificity rules WIN.
   Re-declares existing selectors only. No HTML/class changes.
   Site JS reveals .price-card via .reveal + .is-visible; we also honor
   the spec's .is-in. Reduced motion: JS skips .reveal → cards stay visible,
   plus an explicit guard below.
   ========================================================================= */

/* ---- Section shell: more air, comfortable rhythm ---------------------- */
.pricing__inner {
  gap: var(--space-2xl, var(--space-xl));
}
/* Les gaps des deux grilles de tarifs ont UNE seule source : --sp-card-gap
   (§7, fin de feuille). Les `clamp(16px,3vw,32px)` qui vivaient ici sont
   supprimés — ils écartaient les cartes de 32px sur desktop et rendaient
   toute tentative de resserrage inopérante. */

/* ---- Base card: kill the boxy filled panel → light hairline card ------ */
.price-card {
  position: relative;
  gap: var(--space-l);
  padding: clamp(22px, 3vw, 34px);
  background: transparent;                       /* no heavy fill */
  border: 1px solid var(--line);                 /* single hairline */
  border-radius: var(--r-l);
  box-shadow: none;                              /* no drop shadow */
  overflow: visible;                             /* le sheen s'auto-clippe (background-position) → badge « Recommandé » visible */
  /* hover uses transform/opacity + border/bg tint only (GPU-friendly) */
  transition: transform .3s var(--ease),
              border-color .3s var(--ease),
              background-color .3s var(--ease);
}
/* subtle hover lift + brand hairline tint, NO boxy shadow */
.price-card:hover {
  transform: translateY(-4px);
  border-color: var(--line-strong);
  box-shadow: none;
}

/* ---- Featured plan: NOT a heavy box — soft brand tint + accent line --- */
.price-card--featured {
  background: var(--brand-t);                    /* whisper of brand */
  border-color: var(--brand-t2);
}
.price-card--featured:hover {
  border-color: var(--brand);
  background: var(--brand-t);
}

/* mono "RECOMMANDÉ" tag straddling the top edge (badge, not a box) */
.price-card--featured::after {
  content: "RECOMMANDÉ";
  position: absolute;
  top: -11px;
  right: clamp(18px, 4vw, 30px);
  z-index: 3;
  padding: 4px 10px;
  border-radius: var(--r-pill);
  background: var(--brand);
  color: #fff;
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: .10em;
  line-height: 1;
  text-transform: uppercase;
  box-shadow: 0 0 0 1px var(--bg);               /* crisp seam over the border */
}
/* `.price-card > * { z-index: 2 }` : occurrence unique dans §7b (fin de feuille). */

/* ---- Price figure: tabular, quiet "à partir de" label ----------------- */
.price-card__price b { font-variant-numeric: tabular-nums; letter-spacing: -.02em; }
.price-card__price small {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--text-3);
}
.price-card--featured .price-card__price small { color: var(--brand-ink); }

/* titles: house style (700, tight tracking) */
.price-card__title { font-weight: 700; letter-spacing: -.02em; }

/* soften the icon chip to a hairline tile, not a filled block */
.price-card__icon {
  border-radius: var(--r-m, var(--radius-s));
  background: var(--brand-t);
  border: 1px solid var(--brand-t2);
  color: var(--brand-ink);
}
.price-card--featured .price-card__icon {
  background: var(--brand);
  border-color: transparent;
  color: #fff;
}

/* ---- Feature rows: hairline list, not gapped chips -------------------- */
.price-feature {
  align-items: center;
  gap: 12px;
  padding: 10px 0;
  border-bottom: 1px solid var(--line);          /* hairline separators */
  color: var(--text-2);
  line-height: 1.35;
}
.price-features .price-feature:last-child { border-bottom: 0; }
.price-card--featured .price-feature { color: var(--text); }
.price-feature__ico { width: 18px; height: 18px; flex: 0 0 18px; margin-top: 0; }
/* highlight row: brand ink instead of heavy underline */
.price-feature--highlight {
  font-weight: 600;
  text-decoration: none;
  color: var(--brand-ink);
}

/* comparison layout: restyle the ✓ glyph to an airy hairline check disc */
.pricing__grid--comparison .price-feature::before {
  width: 18px; height: 18px; flex: 0 0 18px;
  background: var(--brand-t);
  border: 1px solid var(--brand-t2);
  color: var(--brand-ink);
  font-size: 11px;
  font-weight: 700;
}
.pricing__grid--comparison .price-card--featured .price-feature::before {
  background: var(--brand);
  border-color: transparent;
  color: #fff;
}

/* CTA nudge on hover (arrow-like affordance via translate) */
.price-card__cta { transition: transform .2s var(--ease); }
.price-card:hover .price-card__cta { transform: translateX(2px); }

/* ========================================================================
   ENTRANCE ANIMATIONS (.is-in per spec + .is-visible used by this site)
   Card-level reveal is already handled by .reveal/.is-visible; we refine it
   and add a STAGGERED reveal for the feature rows inside a revealed card.
   Gated on .reveal so no-JS / reduced-motion (JS skips .reveal) = visible.
   ======================================================================== */
.price-card.reveal { opacity: 0; transform: translateY(16px); }
.price-card.reveal.is-visible,
.price-card.is-in { opacity: 1; transform: none; }

/* feature rows cascade in once their card is revealed */
.price-card.reveal .price-feature {
  opacity: 0;
  transform: translateY(8px);
  transition: opacity .5s var(--ease), transform .5s var(--ease);
}
.price-card.reveal.is-visible .price-feature,
.price-card.is-in .price-feature { opacity: 1; transform: none; }
/* stagger between sibling rows (60ms steps, capped) */
.price-card .price-feature:nth-child(1) { transition-delay: .10s; }
.price-card .price-feature:nth-child(2) { transition-delay: .16s; }
.price-card .price-feature:nth-child(3) { transition-delay: .22s; }
.price-card .price-feature:nth-child(4) { transition-delay: .28s; }
.price-card .price-feature:nth-child(5) { transition-delay: .34s; }
.price-card .price-feature:nth-child(n+6) { transition-delay: .40s; }

/* ---- Mobile: single column already reflows; keep it airy -------------- */
@media (max-width: 720px) {
  .price-card { padding: 20px; }
  .price-card--featured::after { right: 16px; font-size: 9px; }
  .price-features--cols { grid-template-columns: 1fr; }
}

/* ========================================================================
   FIX #1 (light-theme boxy shadow leak) — placed at the very end so it
   lands AFTER styles.css:860 `[data-theme="light"] .price-card:hover`
   at EQUAL specificity (0,0,3,0) and wins by source order. Otherwise the
   base big drop shadow returns on hover in the LIGHT theme.
   ======================================================================== */
[data-theme="light"] .price-card:hover {
  box-shadow: none;
  border-color: var(--line-strong);   /* keep the hover hairline tint consistent */
}

/* ---- Reduced-motion guard (belt & suspenders) ------------------------- */
@media (prefers-reduced-motion: reduce) {
  .price-card,
  .price-card.reveal,
  .price-card.reveal.is-visible,
  .price-card.is-in,
  .price-feature,
  .price-card.reveal .price-feature,
  .price-card.is-in .price-feature {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
    animation: none !important;
  }
  .price-card__cta { transition: none !important; }
  /* FIX #4: also neutralize the instantaneous hover transforms so nothing
     snaps under reduced motion. */
  .price-card:hover,
  .price-card:hover .price-card__cta { transform: none !important; }
}


/* ---- check-list ---- */
/* ============================================================
   COMPONENT OVERRIDE — .check-list  (REV)
   Airy hairline restyle + staggered check-draw entrance.
   Appended at end of stylesheet → equal specificity wins.

   Requires (tiny, add early in <head>):
     document.documentElement.classList.add('js');
   so the entrance pre-state only applies when JS can reveal it.
   ============================================================ */

/* ---- The list: strip any list chrome, open it up ---- */
.check-list{
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  /* top hairline so the first row reads as part of a ruled set */
  border-top: 1px solid var(--line);
}

/* ---- Rows: open, hairline-separated, NO per-item boxes ----
   FIX 1: pre-state (opacity/translate) is NOT applied here — it is
   gated on html.js below, so without JS the list is fully visible. */
.check-list li{
  position: relative;
  list-style: none;
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: start;
  column-gap: var(--space-3, 14px);
  padding: 14px 2px;
  margin: 0;
  /* kill any inherited card look */
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--line);
  border-radius: 0;
  box-shadow: none;
  color: var(--text);
  line-height: 1.5;
  /* transitions only — the animatable end-state lives here, but the
     row is already at its resting values unless html.js sets a pre-state */
  transition:
    opacity .6s var(--ease),
    transform .6s var(--ease),
    color .2s var(--ease);
}

/* FIX 1 — entrance pre-state ONLY when JS is present to reveal it.
   No JS / script error / observer failure → rows stay visible. */
html.js .check-list li{
  opacity: 0;
  transform: translateY(14px);
}

/* revealed state (JS IntersectionObserver adds .is-in) */
html.js .check-list li.is-in{
  opacity: 1;
  transform: none;
}

/* FIX 1 — belt-and-suspenders for engines exposing scripting: none */
@media (scripting: none){
  .check-list li{
    opacity: 1 !important;
    transform: none !important;
  }
}

/* Staggered reveal — 60ms steps between siblings */
.check-list li:nth-child(1){ transition-delay: 0ms; }
.check-list li:nth-child(2){ transition-delay: 60ms; }
.check-list li:nth-child(3){ transition-delay: 120ms; }
.check-list li:nth-child(4){ transition-delay: 180ms; }
.check-list li:nth-child(5){ transition-delay: 240ms; }
.check-list li:nth-child(6){ transition-delay: 300ms; }
.check-list li:nth-child(7){ transition-delay: 360ms; }
.check-list li:nth-child(8){ transition-delay: 420ms; }
.check-list li:nth-child(n+9){ transition-delay: 480ms; }

/* FIX 4 — hover now warms the text with a DISTINCT token, so the
   color .2s transition actually has somewhere to go. */
.check-list li:hover{
  color: var(--text-strong, var(--brand-ink, var(--color-primary)));
}
.check-list li:hover .check-list__ico{
  transform: translateY(1px) scale(1.06);
}

/* ---- The check glyph: airy tinted disc, brand-ink mark ----
   FIX 3: position:relative anchors the fallback legs to the disc.
   FIX 6: removed inert `flex: 0 0 auto` (this is a grid item). */
.check-list__ico{
  position: relative;
  width: 22px;
  height: 22px;
  margin-top: 1px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--r-pill, 999px);
  /* very light brand disc instead of a heavy filled circle */
  background: var(--brand-t, var(--color-primary-t5));
  color: var(--brand-ink, var(--color-primary));
  transition: transform .2s var(--ease), background .2s var(--ease);
}

/* If the icon ships an inline <svg>, keep it crisp + currentColor */
.check-list__ico svg{
  width: 13px;
  height: 13px;
  display: block;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* --- CHECK-DRAW: animate an inline SVG check path on reveal ---
   FIX 5: length-independent draw. Author the SVG check path with
   pathLength="1"; the dash length then maps to the normalized path,
   so any glyph hides/draws exactly regardless of its real length. */
.check-list__ico svg path,
.check-list__ico svg polyline{
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
}
html.js .check-list li.is-in .check-list__ico svg path,
html.js .check-list li.is-in .check-list__ico svg polyline{
  animation: cl-check-draw .5s var(--ease) forwards;
}
.check-list li:nth-child(2).is-in .check-list__ico svg path,
.check-list li:nth-child(2).is-in .check-list__ico svg polyline{ animation-delay: 60ms; }
.check-list li:nth-child(3).is-in .check-list__ico svg path,
.check-list li:nth-child(3).is-in .check-list__ico svg polyline{ animation-delay: 120ms; }
.check-list li:nth-child(4).is-in .check-list__ico svg path,
.check-list li:nth-child(4).is-in .check-list__ico svg polyline{ animation-delay: 180ms; }
.check-list li:nth-child(5).is-in .check-list__ico svg path,
.check-list li:nth-child(5).is-in .check-list__ico svg polyline{ animation-delay: 240ms; }
.check-list li:nth-child(n+6).is-in .check-list__ico svg path,
.check-list li:nth-child(n+6).is-in .check-list__ico svg polyline{ animation-delay: 300ms; }

/* --- Fallback CHECK-DRAW for icons WITHOUT an inline svg ---
   Draw a tick with two pseudo-element strokes so the glyph still
   animates. Guarded by :empty so it never doubles a real SVG.
   FIX 3: placement via explicit top/left offsets (anchored to the
   now-positioned disc) instead of fragile margin nudges. */
.check-list__ico:empty::before,
.check-list__ico:empty::after{
  content:"";
  position:absolute;
  background: currentColor;
  border-radius: 2px;
  transform-origin: left center;
}
/* short leg (bottom-left → vertex) */
.check-list__ico:empty::before{
  width: 4px; height: 1.8px;
  top: 12px; left: 6px;
  transform: rotate(45deg) scaleX(0);
}
/* long leg (vertex → top-right) */
.check-list__ico:empty::after{
  width: 8px; height: 1.8px;
  top: 8px; left: 8px;
  transform: rotate(-45deg) scaleX(0);
}
html.js .check-list li.is-in .check-list__ico:empty::before{
  animation: cl-leg-short .18s var(--ease) forwards;
}
html.js .check-list li.is-in .check-list__ico:empty::after{
  animation: cl-leg-long .26s var(--ease) .16s forwards;
}

@keyframes cl-check-draw{
  to{ stroke-dashoffset: 0; }
}
/* each leg keeps its own angle while scaling in */
@keyframes cl-leg-short{
  to{ transform: rotate(45deg) scaleX(1); }
}
@keyframes cl-leg-long{
  to{ transform: rotate(-45deg) scaleX(1); }
}

/* ---- Contextual: check-list inside a section after the subhero.
   Slightly airier rhythm + comfortable measure in that context. ---- */
main > .subhero ~ .section .check-list{
  max-width: 60ch;
  margin-inline: 0;
}
main > .subhero ~ .section .check-list li{
  padding-block: 16px;
  font-size: 1.02rem;
}

/* ---- Mobile: keep the 2-col glyph/text grid, tighten gutters ---- */
@media (max-width: 640px){
  .check-list li{
    column-gap: 12px;
    padding: 12px 0;
    font-size: .98rem;
  }
  .check-list__ico{ width: 20px; height: 20px; }
  main > .subhero ~ .section .check-list li{ padding-block: 13px; }
}

/* ---- REDUCED-MOTION GUARD ----
   FIX 2: the fallback-glyph pseudos are NOT in this blanket reset,
   so they don't get flattened to two horizontal bars. They get
   explicit drawn end-states below instead. */
@media (prefers-reduced-motion: reduce){
  .check-list li,
  .check-list li.is-in,
  .check-list__ico,
  .check-list__ico svg path,
  .check-list__ico svg polyline,
  html.js .check-list li,
  html.js .check-list li.is-in,
  html.js .check-list li.is-in .check-list__ico svg path,
  html.js .check-list li.is-in .check-list__ico svg polyline{
    opacity: 1 !important;
    transform: none !important;
    animation: none !important;
    transition: none !important;
    stroke-dashoffset: 0 !important;
  }

  /* FIX 2 — draw the fallback tick fully, keeping each leg's angle. */
  .check-list__ico:empty::before{
    transform: rotate(45deg) scaleX(1) !important;
    animation: none !important;
    transition: none !important;
    opacity: 1 !important;
  }
  .check-list__ico:empty::after{
    transform: rotate(-45deg) scaleX(1) !important;
    animation: none !important;
    transition: none !important;
    opacity: 1 !important;
  }
}


/* ---- stats ---- */
/* ============================================================
   STATS — airy override (Stripe / épuré / aéré)  [REVISED]
   Kills the bordered boxes. Big mono figures, hairline
   vertical separators, mono uppercase labels, staggered
   .is-in entrance + subtle hover, reduced-motion guarded.
   ============================================================ */

/* --- Container: open grid row, no card, no borders ---
   FIX #3: commit to a fixed 4-col desktop grid so wrapping is
   deterministic. auto-fit left a stray leading hairline on the
   first item of every wrapped row (only global :first-child was
   cleared). A fixed count keeps the single-row assumption true,
   so the :first-child border reset is always correct. --- */
.stats{
  display:grid;
  grid-template-columns:repeat(4,1fr);
  gap:0;                 /* separation handled by hairlines on items */
  align-items:stretch;
  padding:0;
  margin:0;
  background:transparent;
  border:0;
  box-shadow:none;
  border-radius:0;
}

/* --- Item: strip the box, add left hairline separator --- */
.stats__item{
  position:relative;
  display:flex;
  flex-direction:column;
  justify-content:flex-start;
  gap:var(--space-2,.5rem);
  /* generous internal whitespace instead of a bordered box */
  padding:var(--space-3,.75rem) var(--space-6,1.5rem);
  background:transparent;
  border:0;
  box-shadow:none;
  border-radius:0;
  /* thin VERTICAL hairline between figures */
  border-left:1px solid var(--line);
}
/* first item in each row has no leading rule (clean left edge) */
.stats__item:first-child{ border-left:0; }/* --- The big figure: mono,
tabular,
tight tracking --- */
.stats__item > :first-child,
.stats__item strong,
.stats__item b {
  font-family:var(--mono);
  font-variant-numeric:tabular-nums;
  font-weight:700;
  letter-spacing:-.02em;
  line-height:1;
  font-size:clamp(2rem,4vw,3rem);
  color:var(--text);
  /* the JS count-up writes into this node; keep it stable */
}/* --- The label: mono eyebrow,
uppercase,
faint --- */
.stats__item > :last-child,
.stats__item small,
.stats__item span:last-child {
  font-family:var(--mono);
  text-transform:uppercase;
  letter-spacing:.08em;
  font-size:11px;
  line-height:1.4;
  font-weight:500;
  color:var(--text-3);
}

/* --- Contextual variant: stats inside a section that follows
       the subhero — a touch more breathing room + top/bottom
       hairline framing the whole band (open, not boxed). --- */
main > .subhero ~ .section .stats{
  margin-top:var(--space-6,1.5rem);
  padding-top:var(--space-8,2rem);
  padding-bottom:var(--space-8,2rem);
  border-top:1px solid var(--line);
  border-bottom:1px solid var(--line);
}

/* ============================================================
   ANIMATION — staggered .is-in entrance
   ============================================================ */
.stats__item{
  opacity:0;
  transform:translateY(14px);
  transition:opacity .6s var(--ease),
             transform .6s var(--ease),
             border-color .25s var(--ease);
  will-change:opacity,transform;
}
.stats__item.is-in{
  opacity:1;
  transform:none;
  /* FIX #5: release the compositor layer once the item has
     landed so we don't hold a promoted layer indefinitely. */
  will-change:auto;
}
/* stagger siblings by 70ms steps */
.stats__item:nth-child(1){ transition-delay:0ms; }
.stats__item:nth-child(2){ transition-delay:70ms; }
.stats__item:nth-child(3){ transition-delay:140ms; }
.stats__item:nth-child(4){ transition-delay:210ms; }
.stats__item:nth-child(5){ transition-delay:280ms; }
.stats__item:nth-child(6){ transition-delay:350ms; }

/* Fallback: if the observer never fires (no JS on the container),
   reveal after a short delay so figures are never stuck hidden. */
.stats:not(.is-in) .stats__item{
  animation:stats-safety 0s linear 1.2s forwards;
}
@keyframes stats-safety{ to{ opacity:1; transform:none; } }

/* --- Hover micro-interaction: subtle lift + accent tint on the
       figure, and the separating hairline warms to brand.
   FIX #2: do NOT re-declare the base .stats__item transition here
   (it silently shortened the entrance slide to .25s while opacity
   stayed .6s, desyncing the desktop entrance). Instead keep the
   base .6s transform for entrance and scope the SNAPPY duration to
   the hover state only. --- */
@media (hover:hover){
  .stats__item.is-in:hover{
    transform:translateY(-3px);
    transition:transform .25s var(--ease);
  }
  .stats__item:hover{ border-left-color:var(--brand-t2); }
  .stats__item:hover > :first-child,
  .stats__item:hover strong,
  .stats__item:hover b{
    color:var(--brand);
    transition:color .2s var(--ease);
  }
}

/* --- Underline sheen sweeping under the figure on entrance,
       drawing the eye to the number.
   FIX #1: animate transform:scaleX (compositor-only) instead of
   width (paint-bound / janky). Base is the full 28px width scaled
   to 0 from the left, so the reveal is a pure GPU transform. --- */
.stats__item::after{
  content:"";
  position:absolute;
  left:var(--space-6,1.5rem);
  bottom:calc(var(--space-3,.75rem) - 2px);
  height:2px;
  width:28px;
  transform:scaleX(0);
  transform-origin:left center;
  background:linear-gradient(90deg,var(--brand),transparent);
  border-radius:var(--r-pill,999px);
  transition:transform .5s var(--ease) .2s;
  pointer-events:none;
}
.stats__item.is-in::after{ transform:scaleX(1); }

/* ============================================================
   RESPONSIVE — reflow cleanly on mobile
   ============================================================ */
@media (max-width:640px){
  .stats{
    grid-template-columns:1fr 1fr;   /* two per row */
  }
  .stats__item{
    padding:var(--space-3,.75rem) var(--space-4,1rem);
  }
  /* FIX #4: the underline's left offset must track the reduced
     padding, otherwise it no longer aligns to the figure's left
     edge at this breakpoint. */
  .stats__item::after{
    left:var(--space-4,1rem);
  }
  /* when items wrap onto a new row, the "first-child" rule no
     longer covers the left column of subsequent rows; restore
     the clean left edge for odd items in the 2-col layout. */
  .stats__item:nth-child(odd){ border-left:0; }
}
@media (max-width:400px){
  .stats{ grid-template-columns:1fr; }  /* single column */
  .stats__item{
    border-left:0 !important;
    border-top:1px solid var(--line);
    padding-top:var(--space-5,1.25rem);
  }
  .stats__item:first-child{ border-top:0; }
  /* keep the sheen flush with the figure at this padding too */
  .stats__item::after{ left:var(--space-4,1rem); }
}

/* ============================================================
   REDUCED MOTION GUARD
   ============================================================ */
@media (prefers-reduced-motion:reduce){
  .stats__item,
  .stats__item.is-in,
  .stats__item::after,
  .stats:not(.is-in) .stats__item{
    opacity:1 !important;
    transform:none !important;
    animation:none !important;
    transition:none !important;
  }
  /* FIX #1: base width:28px + transform:none yields the full
     static underline (no scaleX needed). */
  .stats__item::after{ transform:none !important; }
}


/* ---- faq ---- */
/* =========================================================================
   FAQ — accordéon aéré (DA unique) — SOURCE UNIQUE
   Remplace et absorbe les 5 couches empilées : styles.css §FAQ historique,
   la part FAQ du @media 1100px, `.faq__q` du groupe de titres, le bloc
   `.prose-faq*` (mort : aucun des 26 HTML ne l'utilise) et l'ancien §7c
   (qui doublait le padding : 24px sur .faq__item + 20px sur .faq__q = 44px).
   Aucune autre règle .faq ne doit être écrite ailleurs.

   Hooks JS préservés : .faq__q (role=button, tabindex, aria-expanded),
   .is-open sur .faq__item, .is-in (observer de cascade).
   Sans JS (html sans .js) : tout reste ouvert, visible et lisible.
   ========================================================================= */

/* ---------- Section : pas de gouttière en plus, pas de fond ---------- */
.faq { position: relative; background: transparent; border: 0; box-shadow: none; }

/* ---------- Grille : intro ferrée à gauche + colonne de questions ---------- */
.faq__inner {
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
  padding: 0;              /* la gouttière est celle de .section — jamais additionnée ici */
  display: grid;
  grid-template-columns: minmax(0, 0.78fr) minmax(0, 1.22fr);
  column-gap: clamp(40px, 5vw, 80px);
  row-gap: var(--sp-7);
  align-items: start;
}

/* ---------- Colonne intro ---------- */
.faq__intro {
  position: sticky;
  top: calc(var(--header-h) + var(--sp-5));
  align-self: start;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  text-align: left;
  gap: var(--sp-3);
  max-width: 38ch;
  min-width: 0;
}
.faq__intro > :is(h1, h2, h3) {
  margin: 0;
  font-size: clamp(26px, 3.4vw, 38px);
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.08;
  text-wrap: balance;
}
.faq__intro > p {
  margin: 0;
  color: var(--text-2);
  font-size: 16px;
  line-height: 1.55;
  text-wrap: pretty;
}

/* Lien de contact : lien fléché mono discret, pas un 2e accent plein.
   `.faq__link` = markup cible ; `.btn` = compat avec le HTML actuel. */
.faq__intro .faq__link,
.faq__intro .btn {
  all: unset;
  margin-top: var(--sp-2);
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--brand-ink);
  cursor: pointer;
  border-bottom: 1px solid transparent;
  transition: border-color 0.2s var(--ease);
}.faq__intro .btn::after {
  all: unset;              /* neutralise le shine absolu de .btn--primary::after */
  content: "→";
  transition: transform 0.2s var(--ease);
}
.faq__intro :is(.faq__link, .btn):hover { border-bottom-color: currentColor; }
.faq__intro :is(.faq__link, .btn):hover::after { transform: translateX(3px); animation: none; }
.faq__intro :is(.faq__link, .btn):focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 3px;
  border-radius: 3px;
}

/* ---------- Liste : UN filet par ligne, gap 0 (plus de trait flottant) ---------- */
.faq__list {
  display: flex;
  flex-direction: column;
  gap: 0;
  margin: 0;
  padding: 0;
  width: 100%;
  max-width: none;
  min-width: 0;
  list-style: none;
  border-top: 1px solid var(--line);
}
.faq__item {
  position: relative;
  margin: 0;
  padding: 0;              /* l'air est porté par .faq__q — jamais les deux */
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--line);
  border-radius: 0;
  box-shadow: none;
}
/* Le dernier item ne ferme pas la liste : le filet de la section suivante
   (§6a) ou celui du footer joue ce rôle — sinon deux traits à 24px. */
.faq__item:last-child { border-bottom: 0; }

/* ---------- Question (élément cliquable équipé par le JS) ---------- */
.faq__q {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--sp-5);
  margin: 0;
  padding-block: var(--sp-5);
  font-size: clamp(16px, 1.5vw, 18px);
  font-weight: 600;
  letter-spacing: -0.01em;
  line-height: 1.4;
  color: var(--text);
  text-wrap: pretty;
  transition: color 0.2s var(--ease);
}
.faq__q:hover { color: var(--brand-ink); }
/* Le JS insère un vrai <button> DANS le <h3> (motif ARIA accordéon : le titre reste un titre).
   Le bouton est transparent et hérite intégralement de la typographie du titre — visuellement
   rien ne change, mais le clavier et les lecteurs d'écran retrouvent un contrôle légitime. */
.faq__btn {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--sp-5);
  width: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: inherit;
  letter-spacing: inherit;
  line-height: inherit;
  text-align: left;
  text-wrap: pretty;
}
.js .faq__btn { cursor: pointer; -webkit-tap-highlight-color: transparent; }
.js .faq__btn:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
  border-radius: 4px;
}

/* Indicateur + → × : deux filets 1px, rotation 45°, jamais affiché sans JS.
   Porté par le <button> inséré par le JS (le contenu du titre y est déplacé). */
.faq__btn::after {
  content: "";
  flex: 0 0 auto;
  display: none;
  width: 13px;
  height: 13px;
  margin-top: 6px;
  color: var(--text-3);
  background:
    linear-gradient(currentColor, currentColor) center / 100% 1px no-repeat,
    linear-gradient(currentColor, currentColor) center / 1px 100% no-repeat;
  transition: transform 0.3s var(--ease), color 0.2s var(--ease);
}
.js .faq__btn::after { display: block; }
.js .faq__btn:hover::after { color: var(--brand-ink); }
.js .faq__item.is-open .faq__btn::after {
  transform: rotate(45deg);
  color: var(--brand-ink);
}

/* ---------- Réponse ---------- */
.faq__a {
  margin: 0;
  max-width: 68ch;
  color: var(--text-2);
  font-size: 15px;
  line-height: 1.65;
  text-wrap: pretty;
}
/* Ouverture en CSS PUR : height 0 → auto. Le JS ne fait plus que basculer
   `.is-open`. Fini l'animation de hauteur pilotée en JS qui relayoutait toute
   la page à chaque frame (saccade sur Safari quand le contenu du dessous bouge).
   `interpolate-size: allow-keywords` fait animer 0→auto là où c'est supporté
   (Chrome 129+) ; ailleurs (Safari/Firefox) l'ouverture est instantanée — donc
   AUCUNE frame à saccader. Simple et increvable. */
.js .faq__a-wrap {
  overflow: hidden;
  height: 0;
  interpolate-size: allow-keywords;
  transition: height 0.32s var(--ease);
}
.js .faq__item.is-open .faq__a-wrap { height: auto; }
/* Fondu du contenu : `opacity` est composité (GPU), il ne déclenche aucun
   layout et ne peut donc JAMAIS saccader. Il donne une ouverture douce même sur
   Safari, où la hauteur, elle, s'ouvre instantanément. */
.js .faq__a { opacity: 0; transition: opacity 0.3s var(--ease); }
.js .faq__item.is-open .faq__a { opacity: 1; }
/* Padding CONSTANT : il fait partie de la hauteur mesurée et le conteneur le
   clippe quand il est fermé. Aucune animation d'opacité ni de padding superposée
   à la hauteur → une seule et unique animation, nette partout (le double
   mouvement désynchronisé était rendu comme un scintillement par Safari). */
.js .faq__a { padding-bottom: var(--sp-5); }
.faq__a a { color: var(--brand-ink); text-decoration: underline; text-underline-offset: 2px; }

/* ---------- Reveal (.is-in posé par le JS) ----------
   UN SEUL système : l'observer `.is-in`. `.faq__item` a été retiré de la
   liste `revealTargets` du script (qui posait `.reveal` + un
   `style.transitionDelay` INLINE — un style inline bat toute feuille, ces
   délais-ci n'auraient jamais servi). Gated sous `.js` : sans JS, rien ne
   peut rester invisible. */
.js .faq__item {
  opacity: 0;
  transform: translateY(12px);
  transition: opacity 0.5s var(--ease), transform 0.5s var(--ease);
}
.js .faq__item.is-in { opacity: 1; transform: none; }
.js .faq__item:nth-child(2) { transition-delay: 0.05s; }
.js .faq__item:nth-child(3) { transition-delay: 0.10s; }
.js .faq__item:nth-child(4) { transition-delay: 0.15s; }
.js .faq__item:nth-child(5) { transition-delay: 0.20s; }
.js .faq__item:nth-child(n + 6) { transition-delay: 0.25s; }

@media (prefers-reduced-motion: reduce) {
  .js .faq__item { opacity: 1; transform: none; transition: none; }
  .faq__q::after, .faq__a { transition: none; }
}

/* ---------- Mobile ----------
   1024px et non 860 : à 900px de viewport la colonne intro tombait à ~330px
   pour un h2 en clamp(26px,3.4vw,38px). Aligné sur le reste du site. */
@media (max-width: 1024px) {
  .faq__inner { grid-template-columns: 1fr; row-gap: var(--sp-6); }
  .faq__intro { position: static; max-width: 46ch; }
  .faq__q { gap: var(--sp-4); padding-block: 20px; }
}

/* ---- cta banner ---- */
/* =========================================================
   CTA BANNER — override "airy / Stripe" + entrance animations
   Re-declares existing selectors (appended = wins at equal
   specificity). Open, centered, hairline; a soft brand bloom
   replaces the hard tinted/bordered box. Dark + light safe.
   ========================================================= */

/* Section: positioning context for the bloom + air.
   FIX 1: overflow:clip contains the @keyframes cta-bloom
   scale(1.04) so the inset:0 pseudo can't push ~2% past the
   edges and spawn a horizontal scrollbar on mobile. */
.section.cta{
  position: relative;
  isolation: isolate;
  overflow: clip;
  padding-top: clamp(56px, 7vw, 104px);
  padding-bottom: clamp(72px, 10vw, 150px);
}

/* Soft radial brand bloom — subtler, wider, no hard edge/box.
   Uses brand tint so it reads in both themes. */
.section.cta::before{
  content:"";
  position:absolute; inset:0; z-index:-1;
  background:
    radial-gradient(ellipse 52% 60% at 50% 46%,
      var(--brand-t, var(--color-primary-t5)), transparent 68%);
  opacity:.9;
  pointer-events:none;
}

/* Inner: open layout, generous max width, no shadow anywhere */
.cta__inner{
  display:flex;
  align-items:flex-start;
  gap: var(--space-s);
  width:100%;
  max-width: var(--maxw, var(--site-max));
}

/* Banner variant: centered, airy, NO boxy card.
   At most a hairline top rule + whitespace to anchor it. */
.cta__inner--banner{
  flex-direction:column;
  align-items:center;
  text-align:center;
  width: min(100%, 760px);
  margin-inline:auto;
  padding: clamp(40px, 6vw, 76px) clamp(20px, 4vw, 40px) 0;
  border:0;
  border-top:1px solid var(--line, var(--surface-border));
  border-radius:0;
  background:transparent;
  box-shadow:none;
}
.cta__inner--banner .cta__intro{
  align-items:center;
  max-width:640px;
  flex:0 1 auto;
  gap: var(--space-m);
}

/* Title: 700, tight tracking, accent word carries the one blue */
.cta__title{
  font-weight:700;
  letter-spacing:-.02em;
  line-height: var(--lh-head, 1.1);
  text-wrap:balance;
}
.cta__title .accent{
  color: var(--brand, var(--color-primary));
}
/* .hl reste un marqueur (texte blanc sur fond bleu) — ne pas recolorer le texte */

/* Text: secondary tone, comfortable measure, centered in banner */
.cta__text{
  color: var(--text-2, var(--color-w-t3));
  max-width:520px;
}
.cta__inner--banner .cta__text{ margin-inline:auto; }

/* Actions: keep pill buttons, center, add a touch of top air */
.cta__actions{
  display:flex;
  flex-wrap:wrap;
  gap: var(--space-xs);
  margin-top: var(--space-2xs, 6px);
}
.cta__inner--banner .cta__actions{ justify-content:center; }

/* Button hover micro-interaction (subtle lift, no layout shift).
   FIX 3: every var(--ease) carries an `ease` fallback so an
   undefined token can't invalidate the shorthand. */
.cta__actions .btn{
  transition: transform .2s var(--ease, ease),
              box-shadow .2s var(--ease, ease),
              background-color .2s var(--ease, ease),
              color .2s var(--ease, ease);
}
.cta__actions .btn:hover{ transform: translateY(-2px); }
.cta__actions .btn:active{ transform: translateY(0); }

/* ---------- ENTRANCE (.is-in cascade: title → text → buttons) ---------- */
/* Land state is the natural resting state (always defined so the
   content is visible by default). The pre-state below is gated so
   it only hides items when JS is present to reveal them again. */
.cta__title,
.cta__text,
.cta__actions{
  opacity:1;
  transform:none;
  transition: opacity .6s var(--ease, ease), transform .6s var(--ease, ease);
}

/* Pre-state: only apply the hidden start when JS can add .is-in.
   FIX 2a: gate behind a `.js` root class so if the bootstrap
   script never ran, the pre-state is never applied and content
   stays visible. (Set html.js as early as possible in <head>.) */
.js .cta__title,
.js .cta__text,
.js .cta__actions{
  opacity:0;
  transform: translateY(16px);
}

/* Land when the CTA (or its inner) receives .is-in */
.section.cta.is-in .cta__title,
.section.cta.is-in .cta__text,
.section.cta.is-in .cta__actions,
.cta__inner.is-in .cta__title,
.cta__inner.is-in .cta__text,
.cta__inner.is-in .cta__actions,
.cta__inner--banner.is-in .cta__title,
.cta__inner--banner.is-in .cta__text,
.cta__inner--banner.is-in .cta__actions{
  opacity:1;
  transform:none;
}
/* Fallback: also honour .is-in placed directly on each item */
.cta__title.is-in,
.cta__text.is-in,
.cta__actions.is-in{ opacity:1; transform:none; }

/* FIX 2b: belt-and-braces — if scripting is entirely unavailable,
   force the content visible regardless of any .js gating. */
@media (scripting: none){
  .cta__title,
  .cta__text,
  .cta__actions,
  .js .cta__title,
  .js .cta__text,
  .js .cta__actions{
    opacity:1;
    transform:none;
  }
}

/* Stagger the cascade (110ms steps) */
.cta__title{ transition-delay:0ms; }
.cta__text{ transition-delay:110ms; }
.cta__actions{ transition-delay:220ms; }

/* Bloom breathes gently once revealed (optional keyframe).
   Scale kept but now safely clipped by .section.cta overflow. */
@keyframes cta-bloom{
  0%,100%{ opacity:.72; transform:scale(1); }
  50%{ opacity:1; transform:scale(1.04); }
}
/* FIX 3: var(--ease) fallback on the bloom animation too. */
.section.cta.is-in::before{
  animation: cta-bloom 9s var(--ease, ease-in-out) infinite;
  transform-origin:50% 46%;
}

/* ---------- MOBILE REFLOW ---------- */
@media (max-width:760px){
  .cta__inner{ flex-direction:column; }
  .cta__inner--banner{
    width:100%;
    padding-inline: var(--space-m, 16px);
  }
  .cta__actions .btn{ flex:1 1 auto; }
}

/* ---------- REDUCED-MOTION GUARD ---------- */
@media (prefers-reduced-motion: reduce){
  .cta__title,.cta__text,.cta__actions,
  .js .cta__title,.js .cta__text,.js .cta__actions,
  .cta__title.is-in,.cta__text.is-in,.cta__actions.is-in,
  .section.cta.is-in .cta__title,
  .section.cta.is-in .cta__text,
  .section.cta.is-in .cta__actions,
  .cta__inner--banner.is-in .cta__title,
  .cta__inner--banner.is-in .cta__text,
  .cta__inner--banner.is-in .cta__actions{
    opacity:1!important;
    transform:none!important;
    animation:none!important;
    transition:none!important;
  }
  .section.cta.is-in::before{ animation:none!important; }
  .cta__actions .btn{ transition:none!important; }
}

/* STAGGER d'entrée — FIX 2 : scopé à .reveal pour ne PAS retarder la
   transition de hover (transform de la carte). Ne touche donc que l'entrée. */
.blog-card.reveal:nth-child(1) { transition-delay: 0ms; }
.blog-card.reveal:nth-child(2) { transition-delay: 60ms; }
.blog-card.reveal:nth-child(3) { transition-delay: 120ms; }
.blog-card.reveal:nth-child(4) { transition-delay: 180ms; }
.blog-card.reveal:nth-child(5) { transition-delay: 240ms; }
.blog-card.reveal:nth-child(6) { transition-delay: 300ms; }

/* ============================ REFLOW MOBILE ============================ */
@media (max-width: 1024px) {
}
@media (max-width: 640px) {
}

/* ============================ REDUCED MOTION ============================ */
@media (prefers-reduced-motion: reduce) {
}


/* ============ ANIMATIONS PAR BLOC ============ */


/* ---- bx-prix ---- */
/* ============================================================
   bx-prix — animations (appended; animates existing elements only)
   Ambient (always-on) + on-view builds (.is-in) + hover micro-interactions
   ============================================================ */

/* ---------- keyframes ---------- */
@keyframes bxp-rise   { from{ opacity:0; transform:translateY(14px) } to{ opacity:1; transform:none } }
@keyframes bxp-up     { from{ opacity:0; transform:translateY(10px) } to{ opacity:1; transform:none } }
@keyframes bxp-in-l   { from{ opacity:0; transform:translateX(-12px) } to{ opacity:1; transform:none } }
@keyframes bxp-pop    { from{ opacity:0; transform:scale(.96) } to{ opacity:1; transform:none } }
@keyframes bxp-zone   { from{ transform:scaleX(0) } to{ transform:scaleX(1) } }
@keyframes bxp-linex  { from{ transform:scaleX(0) } to{ transform:scaleX(1) } }
@keyframes bxp-draw   { from{ stroke-dashoffset:var(--bxp-dash,30) } to{ stroke-dashoffset:0 } }
/* ambient */
@keyframes bxp-sheen  { 0%{ background-position:200% 0 } 100%{ background-position:-120% 0 } }
@keyframes bxp-pulse  { 0%,100%{ transform:scale(1); box-shadow:0 0 0 0 color-mix(in srgb,var(--brand) 45%,transparent) }
                        50%    { transform:scale(1.18); box-shadow:0 0 0 4px color-mix(in srgb,var(--brand) 0%,transparent) } }
@keyframes bxp-breathe{ 0%,100%{ box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--brand) 22%,transparent) }
                        50%    { box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--brand) 42%,transparent) } }
@keyframes bxp-badge  { 0%,100%{ opacity:.72 } 50%{ opacity:1 } }
@keyframes bxp-cursor { 0%{ transform:translateX(-30%) } 100%{ transform:translateX(430%) } }

/* ============================================================
   AMBIENT — always-on, subtle, infinite (no JS)
   ============================================================ */

/* budget track: a slow light "cursor" sweeps the whole rail, like a value being scanned */
.bx-prix .bx-prix__track{ position:relative }
.bx-prix .bx-prix__track::after{
  content:""; position:absolute; top:0; bottom:0; left:0; width:22%; pointer-events:none; z-index:2;
  background:linear-gradient(90deg,transparent, color-mix(in srgb,#fff 22%,transparent), transparent);
  mix-blend-mode:overlay; will-change:transform;
  animation:bxp-cursor 5.5s cubic-bezier(.65,0,.35,1) infinite;
}

/* the brand "sur-mesure" dot breathes/pulses like a live status */
.bx-prix .bx-prix__dot--c{ will-change:transform; animation:bxp-pulse 3s var(--ease) infinite }

/* highlighted "Recommandé" column: a soft brand sheen drifts down the whole column */
.bx-prix .bx-prix__col--hi{
  background-image:linear-gradient(100deg,
    transparent 34%,
    color-mix(in srgb,var(--brand) 9%,transparent) 50%,
    transparent 66%);
  background-size:220% 100%; background-repeat:no-repeat;
  animation:bxp-sheen 6s linear infinite;
}
/* the "Recommandé" badge itself softly pulses */
.bx-prix .bx-prix__badge{ animation:bxp-badge 3.4s var(--ease) infinite }

/* recurring-fees total: a gentle breathing brand hairline glow */
.bx-prix .bx-prix__total{ animation:bxp-breathe 4.5s var(--ease) infinite }

/* ============================================================
   ON-VIEW BUILD — plays once when block / sub-section scrolls in
   root .bx-prix.is-in  +  each .bx-prix__part.is-in
   ============================================================ */

/* --- header (root) --- */
.bx-prix.is-in .bx-prix__eyebrow{ animation:bxp-up .5s var(--ease) both }
.bx-prix.is-in h2               { animation:bxp-rise .6s var(--ease) .06s both }
.bx-prix.is-in .bx-prix__lede   { animation:bxp-rise .6s var(--ease) .14s both }

/* --- every part: kick / h3 / sub lead in --- */
.bx-prix__part.is-in .bx-prix__kick{ animation:bxp-up .5s var(--ease) both }
.bx-prix__part.is-in .bx-prix__h3  { animation:bxp-rise .55s var(--ease) .06s both }
.bx-prix__part.is-in .bx-prix__sub { animation:bxp-rise .55s var(--ease) .12s both }

/* --- 01 · budget SCALE: segments fill in from left, staggered like a gauge --- */
.bx-prix__part.is-in .bx-prix__zone{ transform-origin:left center; will-change:transform;
  animation:bxp-zone .6s cubic-bezier(.5,0,.2,1) both }
.bx-prix__part.is-in .bx-prix__zone--floor{ animation-delay:.18s }
.bx-prix__part.is-in .bx-prix__zone--a    { animation-delay:.30s }
.bx-prix__part.is-in .bx-prix__zone--b    { animation-delay:.42s }
.bx-prix__part.is-in .bx-prix__zone--c    { animation-delay:.54s }
/* segment labels + numeric marks fade up after the fill */
.bx-prix__part.is-in .bx-prix__seglabels span{ animation:bxp-up .5s var(--ease) both }
.bx-prix__part.is-in .bx-prix__seglabels span:nth-child(1){ animation-delay:.24s }
.bx-prix__part.is-in .bx-prix__seglabels span:nth-child(2){ animation-delay:.34s }
.bx-prix__part.is-in .bx-prix__seglabels span:nth-child(3){ animation-delay:.44s }
.bx-prix__part.is-in .bx-prix__seglabels span:nth-child(4){ animation-delay:.54s }
.bx-prix__part.is-in .bx-prix__mark{ animation:bxp-up .5s var(--ease) both }
.bx-prix__part.is-in .bx-prix__mark:nth-child(1){ animation-delay:.5s }
.bx-prix__part.is-in .bx-prix__mark:nth-child(2){ animation-delay:.6s }
.bx-prix__part.is-in .bx-prix__mark:nth-child(3){ animation-delay:.7s }
.bx-prix__part.is-in .bx-prix__mark:nth-child(4){ animation-delay:.8s }

/* --- 01 · TIER columns stagger up --- */
.bx-prix__part.is-in .bx-prix__tier{ animation:bxp-up .55s var(--ease) both }
.bx-prix__part.is-in .bx-prix__tier:nth-child(1){ animation-delay:.62s }
.bx-prix__part.is-in .bx-prix__tier:nth-child(2){ animation-delay:.72s }
.bx-prix__part.is-in .bx-prix__tier:nth-child(3){ animation-delay:.82s }

/* --- 01 · six FACTORS: hairline top draws, text fades, staggered --- */
.bx-prix__part.is-in .bx-prix__factor{ position:relative; animation:bxp-up .5s var(--ease) both }
.bx-prix__part.is-in .bx-prix__factor::after{
  content:""; position:absolute; top:-1px; left:0; right:0; height:1px; background:var(--brand);
  transform-origin:left center; transform:scaleX(0);
  animation:bxp-linex .55s cubic-bezier(.5,0,.2,1) both;
}
.bx-prix__part.is-in .bx-prix__factor:nth-child(1){ animation-delay:.10s } .bx-prix__part.is-in .bx-prix__factor:nth-child(1)::after{ animation-delay:.10s }
.bx-prix__part.is-in .bx-prix__factor:nth-child(2){ animation-delay:.18s } .bx-prix__part.is-in .bx-prix__factor:nth-child(2)::after{ animation-delay:.18s }
.bx-prix__part.is-in .bx-prix__factor:nth-child(3){ animation-delay:.26s } .bx-prix__part.is-in .bx-prix__factor:nth-child(3)::after{ animation-delay:.26s }
.bx-prix__part.is-in .bx-prix__factor:nth-child(4){ animation-delay:.34s } .bx-prix__part.is-in .bx-prix__factor:nth-child(4)::after{ animation-delay:.34s }
.bx-prix__part.is-in .bx-prix__factor:nth-child(5){ animation-delay:.42s } .bx-prix__part.is-in .bx-prix__factor:nth-child(5)::after{ animation-delay:.42s }
.bx-prix__part.is-in .bx-prix__factor:nth-child(6){ animation-delay:.50s } .bx-prix__part.is-in .bx-prix__factor:nth-child(6)::after{ animation-delay:.50s }

/* --- 02 · MATRIX rows slide/stagger in --- */
.bx-prix__part.is-in .bx-prix__matrix thead th{ animation:bxp-up .5s var(--ease) both }
.bx-prix__part.is-in .bx-prix__matrix tbody tr{ animation:bxp-in-l .5s var(--ease) both }
.bx-prix__part.is-in .bx-prix__matrix tbody tr:nth-child(1){ animation-delay:.16s }
.bx-prix__part.is-in .bx-prix__matrix tbody tr:nth-child(2){ animation-delay:.26s }
.bx-prix__part.is-in .bx-prix__matrix tbody tr:nth-child(3){ animation-delay:.36s }
.bx-prix__part.is-in .bx-prix__matrix tbody tr:nth-child(4){ animation-delay:.46s }
.bx-prix__part.is-in .bx-prix__matrix tbody tr:nth-child(5){ animation-delay:.56s }
/* matrix mark icons draw themselves (check / dash / cross) */
.bx-prix__part.is-in .bx-prix__mk svg path{
  --bxp-dash:34; stroke-dasharray:34; stroke-dashoffset:34;
  animation:bxp-draw .5s cubic-bezier(.6,0,.2,1) .5s both;
}

/* --- 03 · CHECKLIST: rows stagger + checkmarks draw --- */
.bx-prix__part.is-in .bx-prix__list li{ animation:bxp-up .5s var(--ease) both }
.bx-prix__part.is-in .bx-prix__list li:nth-child(1){ animation-delay:.10s }
.bx-prix__part.is-in .bx-prix__list li:nth-child(2){ animation-delay:.18s }
.bx-prix__part.is-in .bx-prix__list li:nth-child(3){ animation-delay:.26s }
.bx-prix__part.is-in .bx-prix__list li:nth-child(4){ animation-delay:.34s }
.bx-prix__part.is-in .bx-prix__list li:nth-child(5){ animation-delay:.42s }
.bx-prix__part.is-in .bx-prix__list li:nth-child(6){ animation-delay:.50s }
.bx-prix__part.is-in .bx-prix__list li svg path{
  --bxp-dash:30; stroke-dasharray:30; stroke-dashoffset:30;
  animation:bxp-draw .45s cubic-bezier(.6,0,.2,1) both;
}
.bx-prix__part.is-in .bx-prix__list li:nth-child(1) svg path{ animation-delay:.28s }
.bx-prix__part.is-in .bx-prix__list li:nth-child(2) svg path{ animation-delay:.36s }
.bx-prix__part.is-in .bx-prix__list li:nth-child(3) svg path{ animation-delay:.44s }
.bx-prix__part.is-in .bx-prix__list li:nth-child(4) svg path{ animation-delay:.52s }
.bx-prix__part.is-in .bx-prix__list li:nth-child(5) svg path{ animation-delay:.60s }
.bx-prix__part.is-in .bx-prix__list li:nth-child(6) svg path{ animation-delay:.68s }

/* --- 03 · RECURRING FEES: rows stagger, dotted leaders draw across --- */
.bx-prix__part.is-in .bx-prix__recline{ animation:bxp-up .5s var(--ease) both }
.bx-prix__part.is-in .bx-prix__recline:nth-child(1){ animation-delay:.14s }
.bx-prix__part.is-in .bx-prix__recline:nth-child(2){ animation-delay:.22s }
.bx-prix__part.is-in .bx-prix__recline:nth-child(3){ animation-delay:.30s }
.bx-prix__part.is-in .bx-prix__recline:nth-child(4){ animation-delay:.38s }
.bx-prix__part.is-in .bx-prix__lead{ transform-origin:left center;
  animation:bxp-linex .5s cubic-bezier(.5,0,.2,1) both }
.bx-prix__part.is-in .bx-prix__recline:nth-child(1) .bx-prix__lead{ animation-delay:.24s }
.bx-prix__part.is-in .bx-prix__recline:nth-child(2) .bx-prix__lead{ animation-delay:.32s }
.bx-prix__part.is-in .bx-prix__recline:nth-child(3) .bx-prix__lead{ animation-delay:.40s }
.bx-prix__part.is-in .bx-prix__recline:nth-child(4) .bx-prix__lead{ animation-delay:.48s }
/* total pops in */
.bx-prix__part.is-in .bx-prix__total{ animation:bxp-pop .55s var(--ease) .5s both, bxp-breathe 4.5s var(--ease) 1.1s infinite }

/* ============================================================
   HOVER — micro-interactions (.2s)
   ============================================================ */
.bx-prix .bx-prix__tier{ transition:transform .2s var(--ease) }
.bx-prix .bx-prix__tier:hover{ transform:translateY(-2px) }

.bx-prix .bx-prix__factor{ transition:transform .2s var(--ease) }
.bx-prix .bx-prix__factor .bx-prix__fname{ transition:color .2s var(--ease) }
.bx-prix .bx-prix__factor:hover{ transform:translateY(-2px) }
.bx-prix .bx-prix__factor:hover .bx-prix__fname{ color:var(--brand-ink) }

.bx-prix .bx-prix__matrix tbody tr{ transition:background-color .2s var(--ease) }
.bx-prix .bx-prix__matrix tbody tr:hover{ background-color:color-mix(in srgb,var(--text) 3%,transparent) }

.bx-prix .bx-prix__list li{ transition:transform .2s var(--ease) }
.bx-prix .bx-prix__list li b{ transition:color .2s var(--ease) }
.bx-prix .bx-prix__list li:hover{ transform:translateX(3px) }
.bx-prix .bx-prix__list li:hover b{ color:var(--brand-ink) }

.bx-prix .bx-prix__recline .bx-prix__recval{ transition:color .2s var(--ease) }
.bx-prix .bx-prix__recline:hover .bx-prix__recval{ color:var(--brand-ink) }

/* ============================================================
   REDUCED MOTION — hard stop, nothing left hidden or mid-animation
   ============================================================ */
@media (prefers-reduced-motion: reduce){
  .bx-prix *{ animation:none!important; transition:none!important }
  .bx-prix [style], .bx-prix *{ opacity:1!important; transform:none!important }
  .bx-prix .bx-prix__zone,
  .bx-prix .bx-prix__lead,
  .bx-prix .bx-prix__factor::after{ transform:none!important }
  .bx-prix svg path{ stroke-dashoffset:0!important }
  .bx-prix .bx-prix__track::after{ display:none!important }
}


/* ---- bx-refonte ---- */
/* =====================================================================
   bx-refonte — animations (append-only, animates existing elements)
   Meaning: avant/après browser mockups, a connecting arrow, delta stats,
   severity signal rows, and a lossless 301 SEO-migration flow.
   ===================================================================== */

/* ---------- AMBIENT (always-on, subtle, no JS) ---------------------- */

/* The "Après" mockup gently floats — feels live, modern, alive */
@keyframes bxrf-float{0%,100%{transform:translateY(0)}50%{transform:translateY(-4px)}}
.bx-refonte .bx-refonte-chrome.bx-refonte-after{animation:bxrf-float 5.5s var(--ease) infinite;will-change:transform}

/* Premium sheen sweeping the brand pill in the redesigned mockup */
.bx-refonte .bx-refonte-new .bx-refonte-pill{position:relative;overflow:hidden}
.bx-refonte .bx-refonte-new .bx-refonte-pill::after{
  content:"";position:absolute;inset:0;
  background:linear-gradient(105deg,transparent 35%,rgba(255,255,255,.55) 50%,transparent 65%);
  transform:translateX(-130%);animation:bxrf-sheen 4.5s var(--ease) infinite}
@keyframes bxrf-sheen{0%,55%{transform:translateX(-130%)}100%{transform:translateX(130%)}}

/* Breathing glow on the "301" migration badge (set here; re-declared on-view) */
@keyframes bxrf-glow{0%,100%{box-shadow:0 0 0 0 transparent}
  50%{box-shadow:0 0 12px color-mix(in srgb,var(--brand) 42%,transparent)}}
.bx-refonte .bx-refonte-code{animation:bxrf-glow 3.6s var(--ease) infinite}

/* Slow shimmer travelling along the eyebrow hairline */
@keyframes bxrf-line{0%,100%{opacity:.5}50%{opacity:1}}
.bx-refonte .bx-refonte-eyebrow::before{animation:bxrf-line 4s var(--ease) infinite}

/* ---------- ON-VIEW BUILD · 01 AVANT / APRÈS ----------------------- */

/* Browser chrome assembles: bar drops in, then view pieces stagger */
.bx-refonte .bx-refonte-ba .bx-refonte-bar{opacity:0}
.bx-refonte .bx-refonte-ba .bx-refonte-view>*{opacity:0}

@keyframes bxrf-bar{from{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:none}}
@keyframes bxrf-piece{from{opacity:0;transform:translateY(10px) scale(.98)}to{opacity:1;transform:none}}

.bx-refonte-sub.is-in .bx-refonte-ba .bx-refonte-bar{animation:bxrf-bar .5s var(--ease) .1s both}
.bx-refonte-sub.is-in .bx-refonte-ba .bx-refonte-view>*{animation:bxrf-piece .55s var(--ease) both}
.bx-refonte-sub.is-in .bx-refonte-ba .bx-refonte-view>*:nth-child(1){animation-delay:.32s}
.bx-refonte-sub.is-in .bx-refonte-ba .bx-refonte-view>*:nth-child(2){animation-delay:.40s}
.bx-refonte-sub.is-in .bx-refonte-ba .bx-refonte-view>*:nth-child(3){animation-delay:.48s}
.bx-refonte-sub.is-in .bx-refonte-ba .bx-refonte-view>*:nth-child(4){animation-delay:.56s}

/* Connecting arrow draws itself, shaft then head */
.bx-refonte .bx-refonte-arrow svg path{stroke-dasharray:30;stroke-dashoffset:30}
@keyframes bxrf-draw{to{stroke-dashoffset:0}}
.bx-refonte-sub.is-in .bx-refonte-arrow svg path{animation:bxrf-draw .5s var(--ease) .55s forwards}
.bx-refonte-sub.is-in .bx-refonte-arrow svg path:nth-child(2){animation-delay:.75s}
/* …then keeps a subtle forward nudge, hinting the migration direction */
@keyframes bxrf-nudge{0%,100%{transform:translateX(0)}50%{transform:translateX(3px)}}
.bx-refonte .bx-refonte-arrow svg{animation:bxrf-nudge 2.6s var(--ease) 1.2s infinite}

/* Delta stat cards rise and stagger (values count up via JS) */
.bx-refonte .bx-refonte-deltas .bx-refonte-delta{opacity:0}
@keyframes bxrf-rise{from{opacity:0;transform:translateY(14px)}to{opacity:1;transform:none}}
.bx-refonte-sub.is-in .bx-refonte-deltas .bx-refonte-delta{animation:bxrf-rise .55s var(--ease) both}
.bx-refonte-sub.is-in .bx-refonte-deltas .bx-refonte-delta:nth-child(1){animation-delay:.65s}
.bx-refonte-sub.is-in .bx-refonte-deltas .bx-refonte-delta:nth-child(2){animation-delay:.75s}
.bx-refonte-sub.is-in .bx-refonte-deltas .bx-refonte-delta:nth-child(3){animation-delay:.85s}

/* ---------- ON-VIEW BUILD · 02 SIGNAUX ----------------------------- */

/* Signal rows slide + stagger in */
.bx-refonte .bx-refonte-rows .bx-refonte-row{opacity:0}
@keyframes bxrf-rowin{from{opacity:0;transform:translateX(-14px)}to{opacity:1;transform:none}}
.bx-refonte-sub.is-in .bx-refonte-rows .bx-refonte-row{animation:bxrf-rowin .5s var(--ease) both}
.bx-refonte-sub.is-in .bx-refonte-rows .bx-refonte-row:nth-child(1){animation-delay:.05s}
.bx-refonte-sub.is-in .bx-refonte-rows .bx-refonte-row:nth-child(2){animation-delay:.13s}
.bx-refonte-sub.is-in .bx-refonte-rows .bx-refonte-row:nth-child(3){animation-delay:.21s}
.bx-refonte-sub.is-in .bx-refonte-rows .bx-refonte-row:nth-child(4){animation-delay:.29s}
.bx-refonte-sub.is-in .bx-refonte-rows .bx-refonte-row:nth-child(5){animation-delay:.37s}

/* Severity dots pop; critical (hi) dots then pulse a warning ring */
.bx-refonte .bx-refonte-rows .bx-refonte-sev{transform:scale(0)}
@keyframes bxrf-pop{from{transform:scale(0)}60%{transform:scale(1.25)}to{transform:scale(1)}}
@keyframes bxrf-dot{0%,100%{box-shadow:0 0 0 0 color-mix(in srgb,var(--danger) 55%,transparent)}
  70%,100%{box-shadow:0 0 0 7px color-mix(in srgb,var(--danger) 0%,transparent)}}
.bx-refonte-sub.is-in .bx-refonte-rows .bx-refonte-sev{animation:bxrf-pop .45s var(--ease) .3s both}
.bx-refonte-sub.is-in .bx-refonte-rows .bx-refonte-sev.bx-refonte-hi{
  animation:bxrf-pop .45s var(--ease) .3s both, bxrf-dot 2.8s var(--ease) .9s infinite}

/* ---------- ON-VIEW BUILD · SEO 301 FLOW --------------------------- */
/* (.bx-refonte-seo is not a *-sub, so keyed to the block root .is-in) */
.bx-refonte .bx-refonte-flow>*{opacity:0}
@keyframes bxrf-flowin{from{opacity:0;transform:translateX(-10px)}to{opacity:1;transform:none}}
.bx-refonte.is-in .bx-refonte-flow>*{animation:bxrf-flowin .5s var(--ease) both}
.bx-refonte.is-in .bx-refonte-flow>*:nth-child(1){animation-delay:.10s}
.bx-refonte.is-in .bx-refonte-flow>*:nth-child(2){animation-delay:.20s}
.bx-refonte.is-in .bx-refonte-flow>*:nth-child(4){animation-delay:.40s}
.bx-refonte.is-in .bx-refonte-flow>*:nth-child(5){animation-delay:.50s}
/* 301 badge: builds in, then resumes its ambient breathing glow */
.bx-refonte.is-in .bx-refonte-flow>.bx-refonte-code{
  animation:bxrf-flowin .5s var(--ease) .30s both, bxrf-glow 3.6s var(--ease) .9s infinite}

/* ---------- HOVER micro-interactions ------------------------------- */

/* Mockups lift softly on hover */
.bx-refonte .bx-refonte-chrome{transition:box-shadow .2s var(--ease),border-color .2s var(--ease)}
.bx-refonte .bx-refonte-chrome:hover{box-shadow:0 10px 28px color-mix(in srgb,var(--brand-ink) 14%,transparent)}

/* Signal rows tint + nudge on hover */
.bx-refonte .bx-refonte-row{transition:background-color .2s var(--ease),transform .2s var(--ease)}
.bx-refonte .bx-refonte-row:hover{background-color:color-mix(in srgb,var(--brand) 5%,transparent);transform:translateX(2px)}
.bx-refonte .bx-refonte-row .bx-refonte-tag{transition:color .2s var(--ease)}
.bx-refonte .bx-refonte-row:hover .bx-refonte-tag{color:var(--brand-ink)}

/* Delta cards subtly warm on hover */
.bx-refonte .bx-refonte-delta{transition:background-color .2s var(--ease)}
.bx-refonte .bx-refonte-delta:hover{background-color:color-mix(in srgb,var(--brand) 4%,transparent)}

/* ---------- REDUCED MOTION GUARD ----------------------------------- */
@media (prefers-reduced-motion: reduce){
  .bx-refonte *{animation:none!important;transition:none!important}
  .bx-refonte [style],.bx-refonte *{opacity:1!important;transform:none!important}
  .bx-refonte .bx-refonte-arrow svg path{stroke-dashoffset:0!important}
  .bx-refonte .bx-refonte-rows .bx-refonte-sev{transform:none!important}
  .bx-refonte .bx-refonte-new .bx-refonte-pill::after{display:none!important}
}


/* ---- bx-compare ---- */
/* ============================================================
   bx-compare — animations (appended; animate-only, no relayout)
   Meaning: a "vitrine OU e-commerce" arbitration table.
   The "ou" pill is the fulcrum → it breathes. Rows build in
   like a ledger being written, checkmarks draw, figures settle,
   arrows lead the eye toward each verdict.
   ============================================================ */

/* --- keyframes --- */
@keyframes bxc-rise{from{opacity:0;transform:translateY(14px)}to{opacity:1;transform:none}}
@keyframes bxc-rise-sm{from{opacity:0;transform:translateY(9px)}to{opacity:1;transform:none}}
@keyframes bxc-fade{from{opacity:0}to{opacity:1}}
@keyframes bxc-row-in{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}
@keyframes bxc-col-in{from{opacity:0;transform:translateX(10px)}to{opacity:1;transform:none}}
@keyframes bxc-draw{to{stroke-dashoffset:0}}
@keyframes bxc-fig-in{from{opacity:0;transform:translateY(5px)}to{opacity:1;transform:none}}
/* eyebrow hairline grows out from the text */
@keyframes bxc-line-grow{from{opacity:0;transform:scaleX(0)}to{opacity:.55;transform:scaleX(1)}}
/* ambient: the "ou" fulcrum breathes, softly weighing both sides */
@keyframes bxc-ou-breathe{
  0%,100%{transform:translate(-50%,-50%) scale(1);border-color:var(--line);box-shadow:0 0 0 0 var(--brand-t)}
  50%{transform:translate(-50%,-50%) scale(1.06);border-color:var(--brand-t2);box-shadow:0 0 0 4px transparent}
}
/* ambient: mobile "ou" cue breathes on compositor-safe props only
   (transform+opacity) — mirrors the desktop fulcrum; no layout thrash */
@keyframes bxc-oum-breathe{
  0%,100%{opacity:.8;transform:scale(1)}
  50%{opacity:1;transform:scale(1.04)}
}
/* ambient: featured "yes" ticks keep a faint alive glow */
@keyframes bxc-yes-pulse{
  0%,100%{opacity:.85}
  50%{opacity:1;filter:drop-shadow(0 0 3px var(--brand-t))}
}
/* ambient: arrows in the decision list drift forward, inviting */
@keyframes bxc-arrow-drift{
  0%,100%{transform:translateX(0)}
  50%{transform:translateX(3px)}
}

/* ===== HEADER build (root section) ===== */
.bx-compare.is-in header .bx-compare-eyebrow{animation:bxc-fade .5s var(--ease) both}
.bx-compare.is-in header .bx-compare-eyebrow::before{transform-origin:left center;animation:bxc-line-grow .7s var(--ease) .12s both}
.bx-compare.is-in header .bx-compare-h2{animation:bxc-rise .6s var(--ease) .08s both}
.bx-compare.is-in header .bx-compare-lede{animation:bxc-rise .6s var(--ease) .18s both}

/* ===== SUB-SECTION build (each .bx-compare-sec.is-in) ===== */
.bx-compare-sec .bx-compare-kicker,
.bx-compare-sec .bx-compare-h3{opacity:0}
.bx-compare-sec.is-in .bx-compare-kicker{animation:bxc-rise-sm .5s var(--ease) both}
.bx-compare-sec.is-in .bx-compare-h3{animation:bxc-rise .6s var(--ease) .07s both}

/* ---- Ledger: header columns slide in from the divide ---- */
.bx-compare-sec.is-in .bx-compare-head .bx-compare-c{animation:bxc-col-in .55s var(--ease) both}
.bx-compare-sec.is-in .bx-compare-head .bx-compare-c--crit{animation-delay:.16s}
.bx-compare-sec.is-in .bx-compare-head .bx-compare-c:nth-child(2){animation-delay:.22s}
.bx-compare-sec.is-in .bx-compare-head .bx-compare-c--ecom{animation-delay:.30s}
/* the "ou" pill drops in last, then hands off to its ambient breathe */
.bx-compare-sec.is-in .bx-compare-ou{animation:bxc-fade .4s var(--ease) .5s both,bxc-ou-breathe 4.5s var(--ease) 1s infinite}

/* ---- Ledger rows: staggered ledger-writing build ---- */
.bx-compare-ledger .bx-compare-row{opacity:0}
.bx-compare-sec.is-in .bx-compare-ledger .bx-compare-row{animation:bxc-row-in .5s var(--ease) both}
.bx-compare-sec.is-in .bx-compare-ledger .bx-compare-row:nth-child(2){animation-delay:.34s}
.bx-compare-sec.is-in .bx-compare-ledger .bx-compare-row:nth-child(3){animation-delay:.42s}
.bx-compare-sec.is-in .bx-compare-ledger .bx-compare-row:nth-child(4){animation-delay:.50s}
.bx-compare-sec.is-in .bx-compare-ledger .bx-compare-row:nth-child(5){animation-delay:.58s}
.bx-compare-sec.is-in .bx-compare-ledger .bx-compare-row:nth-child(6){animation-delay:.66s}
.bx-compare-sec.is-in .bx-compare-ledger .bx-compare-row:nth-child(7){animation-delay:.74s}

/* ---- Checkmarks draw themselves in, then keep a faint pulse ---- */
.bx-compare-yes path{stroke-dasharray:30;stroke-dashoffset:30}
.bx-compare-sec.is-in .bx-compare-yes path{animation:bxc-draw .55s var(--ease) .9s forwards}
.bx-compare-sec.is-in .bx-compare-yes{animation:bxc-yes-pulse 4s ease-in-out 1.6s infinite}
/* the "aucun/non" dashes fade up gently */
.bx-compare-sec.is-in .bx-compare-no{animation:bxc-fade .5s var(--ease) .95s both}

/* ---- Figures (budget / délai) settle in after their row ---- */
.bx-compare-fig{opacity:0}
.bx-compare-sec.is-in .bx-compare-fig{animation:bxc-fig-in .5s var(--ease) both}
.bx-compare-sec.is-in .bx-compare-row:nth-child(5) .bx-compare-fig{animation-delay:.72s}
.bx-compare-sec.is-in .bx-compare-row:nth-child(6) .bx-compare-fig{animation-delay:.80s}

/* ---- Decision rows: stagger in, arrows draw + ambient drift ---- */
.bx-compare-drow{opacity:0}
.bx-compare-sec.is-in .bx-compare-drow{animation:bxc-row-in .55s var(--ease) both}
.bx-compare-sec.is-in .bx-compare-drow:nth-child(1){animation-delay:.14s}
.bx-compare-sec.is-in .bx-compare-drow:nth-child(2){animation-delay:.24s}
.bx-compare-sec.is-in .bx-compare-drow:nth-child(3){animation-delay:.34s}
.bx-compare-a svg path{stroke-dasharray:34;stroke-dashoffset:34}
.bx-compare-sec.is-in .bx-compare-a svg path{animation:bxc-draw .6s var(--ease) .5s forwards}
.bx-compare-sec.is-in .bx-compare-a svg{animation:bxc-arrow-drift 3.4s ease-in-out 1.3s infinite}

/* ---- Hybrid note: icon draws (padlock), text fades ---- */
.bx-compare-hybrid path{stroke-dasharray:60;stroke-dashoffset:60}
.bx-compare-sec.is-in .bx-compare-hybrid path{animation:bxc-draw .8s var(--ease) .3s forwards}
.bx-compare-sec.is-in .bx-compare-hybrid span{animation:bxc-rise-sm .6s var(--ease) .3s both}

/* ---- Mobile "ou" cue: ambient breathe (no desktop grid cell) ---- */
.bx-compare-oum{animation:bxc-oum-breathe 4.5s ease-in-out infinite}

/* ===== HOVER micro-interactions ===== */
.bx-compare-row{transition:background .2s var(--ease)}
.bx-compare-row:hover{background:var(--brand-t)}
.bx-compare-drow{transition:transform .2s var(--ease)}
.bx-compare-drow:hover{transform:translateX(3px)}
.bx-compare-drow .bx-compare-a svg{transition:transform .2s var(--ease)}
.bx-compare-drow:hover .bx-compare-a svg{transform:translateX(4px)}
.bx-compare-drow:hover .bx-compare-a{color:var(--brand)}

/* ===== reduced motion ===== */
@media (prefers-reduced-motion: reduce){
  .bx-compare *{animation:none!important;transition:none!important}
  .bx-compare [style],.bx-compare *{opacity:1!important;transform:none!important}
  .bx-compare .bx-compare-yes path,
  .bx-compare .bx-compare-a svg path,
  .bx-compare .bx-compare-hybrid path{stroke-dashoffset:0!important}
  .bx-compare .bx-compare-eyebrow::before{opacity:.55!important}
}


/* ---- bx-seo ---- */
/* ============================================================
   bx-seo — animations (append-only; animates existing elements)
   Block meaning: a faux Google local result (browser chrome,
   listing w/ stars+score, map w/ roads+pin) + a 3-lever ledger
   with metrics + a NAP coherence checkmark.
   Triggers: AMBIENT (always-on) + ON-VIEW BUILD (.is-in on root
   .bx-seo and hairline sub .bx-seo-sub) + HOVER micro-interactions.
   --- REVISION: ambient twinkle + dotpulse now survive the build
   (they were permanently shadowed by the .is-in `pop` rules while
   `.is-in` stays on the root). Both loops are re-chained onto the
   build rules so they hand off cleanly after the pop completes.
   ============================================================ */

/* ---------- keyframes ---------- */
@keyframes bx-seo-rise{from{opacity:0;transform:translateY(14px)}to{opacity:1;transform:none}}
@keyframes bx-seo-pop{from{opacity:0;transform:scale(.6)}60%{opacity:1;transform:scale(1.06)}to{opacity:1;transform:scale(1)}}
@keyframes bx-seo-ui-in{from{opacity:0;transform:translateY(18px) scale(.985)}to{opacity:1;transform:none}}
@keyframes bx-seo-bar-in{from{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:none}}
@keyframes bx-seo-block-in{from{opacity:0;transform:scale(.4)}to{opacity:.35;transform:scale(1)}}
@keyframes bx-seo-draw{from{stroke-dashoffset:var(--bx-seo-len,260)}to{stroke-dashoffset:0}}
@keyframes bx-seo-pin-drop{from{opacity:0;transform:translate(-50%,-170%)}60%{opacity:1;transform:translate(-50%,-92%)}to{opacity:1;transform:translate(-50%,-100%)}}
@keyframes bx-seo-label-in{from{opacity:0;transform:translateX(-50%) translateY(6px)}to{opacity:1;transform:translateX(-50%)}}
/* ambient */
@keyframes bx-seo-bob{0%,100%{transform:translateY(0)}50%{transform:translateY(-2px)}}
@keyframes bx-seo-ping{0%{transform:translate(-50%,-50%) scale(.4);opacity:.5}70%{opacity:0}100%{transform:translate(-50%,-50%) scale(2.4);opacity:0}}
@keyframes bx-seo-twinkle{0%,100%{filter:none;opacity:1}50%{filter:brightness(1.28);opacity:.86}}
@keyframes bx-seo-sheen{0%{transform:translateX(-120%)}55%,100%{transform:translateX(220%)}}
@keyframes bx-seo-breathe{0%,100%{opacity:1}50%{opacity:.62}}
@keyframes bx-seo-dotpulse{0%,100%{opacity:.55;transform:scale(1)}50%{opacity:1;transform:scale(1.12)}}
@keyframes bx-seo-flow{to{stroke-dashoffset:-48}}

/* =========================================================
   ON-VIEW BUILD — root section (.bx-seo.is-in)
   ========================================================= */
/* intro reveal */
.bx-seo.is-in .bx-seo-eyebrow{animation:bx-seo-rise .5s var(--ease) both}
.bx-seo.is-in .bx-seo-h2{animation:bx-seo-rise .55s var(--ease) .06s both}
.bx-seo.is-in .bx-seo-lede{animation:bx-seo-rise .55s var(--ease) .12s both}

/* the UI object assembles piece by piece */
.bx-seo.is-in .bx-seo-ui{animation:bx-seo-ui-in .6s var(--ease) .18s both}
.bx-seo.is-in .bx-seo-uibar{animation:bx-seo-bar-in .5s var(--ease) .32s both}

/* browser dots: pop in on build, THEN hand off to the ambient
   traffic-light pulse. Both animations are chained on the SAME
   rule so specificity can't strip the loop once .is-in persists.
   Matched delays: pulse starts ~after each dot's pop settles. */
.bx-seo.is-in .bx-seo-dot{animation:bx-seo-pop .4s var(--ease) both, bx-seo-dotpulse 3s ease-in-out 1.6s infinite}
.bx-seo.is-in .bx-seo-dot:nth-child(1){animation-delay:.42s,1.6s}
.bx-seo.is-in .bx-seo-dot:nth-child(2){animation-delay:.50s,1.9s}
.bx-seo.is-in .bx-seo-dot:nth-child(3){animation-delay:.58s,2.2s}

/* listing rows stagger in */
.bx-seo.is-in .bx-seo-listing>*{animation:bx-seo-rise .5s var(--ease) both}
.bx-seo.is-in .bx-seo-cat{animation-delay:.46s}
.bx-seo.is-in .bx-seo-name{animation-delay:.54s}
.bx-seo.is-in .bx-seo-rate{animation-delay:.62s}
.bx-seo.is-in .bx-seo-meta{animation-delay:.70s}
.bx-seo.is-in .bx-seo-actions{animation-delay:.78s}

/* stars pop one by one (the featured rating), THEN hand off to
   the ambient twinkle. Both chained on the same rule so the loop
   survives the persistent .is-in. Twinkle keeps its staggered feel
   by spacing each star's loop start after its pop resolves. */
.bx-seo.is-in .bx-seo-stars svg{animation:bx-seo-pop .42s var(--ease) both, bx-seo-twinkle 4.2s var(--ease) 1.6s infinite}
.bx-seo.is-in .bx-seo-stars svg:nth-child(1){animation-delay:.66s,1.6s}
.bx-seo.is-in .bx-seo-stars svg:nth-child(2){animation-delay:.73s,2.0s}
.bx-seo.is-in .bx-seo-stars svg:nth-child(3){animation-delay:.80s,2.4s}
.bx-seo.is-in .bx-seo-stars svg:nth-child(4){animation-delay:.87s,2.8s}
.bx-seo.is-in .bx-seo-stars svg:nth-child(5){animation-delay:.94s,3.2s}

/* map assembles: blocks scale, roads draw, pin drops */
.bx-seo-mapblock{transform-box:fill-box;transform-origin:center}
.bx-seo.is-in .bx-seo-mapblock{animation:bx-seo-block-in .55s var(--ease) both}
.bx-seo.is-in .bx-seo-mapblock:nth-of-type(1){animation-delay:.40s}
.bx-seo.is-in .bx-seo-mapblock:nth-of-type(2){animation-delay:.48s}
.bx-seo.is-in .bx-seo-mapblock:nth-of-type(3){animation-delay:.56s}
.bx-seo.is-in .bx-seo-mapblock:nth-of-type(4){animation-delay:.64s}
/* main roads draw (horizontal + vertical = nth-of-type 1 & 2) */
.bx-seo.is-in .bx-seo-maproad:nth-of-type(1),
.bx-seo.is-in .bx-seo-maproad:nth-of-type(2){--bx-seo-len:210;stroke-dasharray:210;animation:bx-seo-draw .7s var(--ease) .42s both}
.bx-seo.is-in .bx-seo-maproad:nth-of-type(2){animation-delay:.54s}
/* pin lands + label fades up */
.bx-seo.is-in .bx-seo-pin{animation:bx-seo-pin-drop .6s var(--ease) .82s both}
.bx-seo.is-in .bx-seo-pinlabel{animation:bx-seo-label-in .45s var(--ease) 1s both}

/* NAP coherence checkmark draws in */
.bx-seo-nap svg path{stroke-dasharray:26}
.bx-seo.is-in .bx-seo-nap{animation:bx-seo-rise .5s var(--ease) both}
.bx-seo.is-in .bx-seo-nap svg path{animation:bx-seo-draw .7s var(--ease) .15s both;--bx-seo-len:26}

/* =========================================================
   ON-VIEW BUILD — ledger sub-section (.bx-seo-sub.is-in)
   ========================================================= */
.bx-seo-sub.is-in .bx-seo-kicker{animation:bx-seo-rise .5s var(--ease) both}
.bx-seo-sub.is-in .bx-seo-h3{animation:bx-seo-rise .5s var(--ease) .06s both}
/* rows slide/stagger in */
.bx-seo-sub.is-in .bx-seo-row{animation:bx-seo-rise .55s var(--ease) both}
.bx-seo-sub.is-in .bx-seo-row:nth-child(1){animation-delay:.12s}
.bx-seo-sub.is-in .bx-seo-row:nth-child(2){animation-delay:.22s}
.bx-seo-sub.is-in .bx-seo-row:nth-child(3){animation-delay:.32s}
/* index numbers + metrics pop for emphasis */
.bx-seo-sub.is-in .bx-seo-num{animation:bx-seo-pop .45s var(--ease) both}
.bx-seo-sub.is-in .bx-seo-row:nth-child(1) .bx-seo-num{animation-delay:.18s}
.bx-seo-sub.is-in .bx-seo-row:nth-child(2) .bx-seo-num{animation-delay:.28s}
.bx-seo-sub.is-in .bx-seo-row:nth-child(3) .bx-seo-num{animation-delay:.38s}
.bx-seo-sub.is-in .bx-seo-metric b{animation:bx-seo-pop .5s var(--ease) both}
.bx-seo-sub.is-in .bx-seo-row:nth-child(1) .bx-seo-metric b{animation-delay:.34s}
.bx-seo-sub.is-in .bx-seo-row:nth-child(2) .bx-seo-metric b{animation-delay:.44s}
.bx-seo-sub.is-in .bx-seo-row:nth-child(3) .bx-seo-metric b{animation-delay:.54s}
/* tags stagger */
.bx-seo-sub.is-in .bx-seo-tag{animation:bx-seo-rise .4s var(--ease) both}
.bx-seo-sub.is-in .bx-seo-tag:nth-child(1){animation-delay:.30s}
.bx-seo-sub.is-in .bx-seo-tag:nth-child(2){animation-delay:.36s}
.bx-seo-sub.is-in .bx-seo-tag:nth-child(3){animation-delay:.42s}
.bx-seo-sub.is-in .bx-seo-tag:nth-child(4){animation-delay:.48s}

/* =========================================================
   AMBIENT — always-on, subtle, infinite (no JS)
   ========================================================= */
/* map pin gently bobs (svg has no base transform) + radiating ping */
.bx-seo-pin svg{animation:bx-seo-bob 3.4s ease-in-out infinite}
.bx-seo-pin::after{content:"";position:absolute;left:50%;top:11px;width:12px;height:12px;border:2px solid var(--brand);border-radius:50%;transform:translate(-50%,-50%);z-index:-1;animation:bx-seo-ping 3.4s ease-out infinite}

/* diagonal road = a live "route": flowing dashes */
.bx-seo-maproad:nth-of-type(3){stroke-dasharray:10 14;animation:bx-seo-flow 2.6s linear infinite}

/* the featured rating: stars quietly twinkle (staggered) — fallback
   for the pre-build state; after .is-in the chained loop above owns it */
.bx-seo-stars svg{animation:bx-seo-twinkle 4.2s var(--ease) infinite}
.bx-seo-stars svg:nth-child(2){animation-delay:.5s}
.bx-seo-stars svg:nth-child(3){animation-delay:1s}
.bx-seo-stars svg:nth-child(4){animation-delay:1.5s}
.bx-seo-stars svg:nth-child(5){animation-delay:2s}

/* "Ouvert" status softly breathes like a live indicator */
.bx-seo-open{animation:bx-seo-breathe 3s ease-in-out infinite}

/* browser dots: slow sequential pulse — pre-build fallback;
   after .is-in the chained loop above owns it */
.bx-seo-dot{animation:bx-seo-dotpulse 3s ease-in-out infinite}
.bx-seo-dot:nth-child(2){animation-delay:.3s}
.bx-seo-dot:nth-child(3){animation-delay:.6s}

/* primary "Itinéraire" button: slow premium sheen sweep
   (lengthened for a calmer cadence alongside the resumed loops) */
.bx-seo-btn--primary{position:relative;overflow:hidden}
.bx-seo-btn--primary::after{content:"";position:absolute;top:0;left:0;width:60%;height:100%;background:linear-gradient(100deg,transparent,color-mix(in srgb,var(--brand-2) 42%,transparent),transparent);transform:translateX(-120%);pointer-events:none;animation:bx-seo-sheen 7.5s ease-in-out infinite}

/* slow gradient sweep across the browser chrome bar (lengthened) */
.bx-seo-uibar{position:relative;overflow:hidden}
.bx-seo-uibar::after{content:"";position:absolute;top:0;left:0;width:45%;height:100%;background:linear-gradient(100deg,transparent,color-mix(in srgb,var(--brand-t2) 55%,transparent),transparent);transform:translateX(-120%);pointer-events:none;animation:bx-seo-sheen 9.5s ease-in-out infinite}

/* =========================================================
   HOVER — micro-interactions (.2s var(--ease))
   ========================================================= */
.bx-seo-btn{transition:transform .2s var(--ease),border-color .2s var(--ease),background .2s var(--ease)}
.bx-seo-btn:hover{transform:translateY(-2px);border-color:var(--brand-ink)}
.bx-seo-btn svg{transition:transform .2s var(--ease)}
.bx-seo-btn:hover svg{transform:translateX(1px) scale(1.06)}
.bx-seo-btn--primary:hover{background:var(--brand-t2)}

.bx-seo-row{transition:transform .2s var(--ease)}
.bx-seo-row:hover{transform:translateX(4px)}
.bx-seo-num{transition:color .2s var(--ease),transform .2s var(--ease)}
.bx-seo-row:hover .bx-seo-num{transform:scale(1.12)}
.bx-seo-metric b{transition:color .2s var(--ease)}
.bx-seo-row:hover .bx-seo-metric b{color:var(--brand-ink)}
.bx-seo-tag{transition:color .2s var(--ease)}
.bx-seo-row:hover .bx-seo-tag{color:var(--text-2)}

/* =========================================================
   REDUCED-MOTION GUARD — nothing left mid-animation/invisible
   (re-assert pin/pinlabel base transforms so they stay placed)
   ========================================================= */
@media (prefers-reduced-motion: reduce){
  .bx-seo *{animation:none!important;transition:none!important}
  .bx-seo *{opacity:1!important;transform:none!important}
  .bx-seo .bx-seo-mapblock{opacity:.35!important}
  .bx-seo .bx-seo-maproad{stroke-dasharray:none!important;stroke-dashoffset:0!important}
  .bx-seo .bx-seo-nap svg path{stroke-dasharray:none!important;stroke-dashoffset:0!important}
  .bx-seo .bx-seo-pin{transform:translate(-50%,-100%)!important}
  .bx-seo .bx-seo-pinlabel{transform:translateX(-50%)!important}
  .bx-seo .bx-seo-btn--primary::after,.bx-seo .bx-seo-uibar::after,.bx-seo .bx-seo-pin::after{display:none!important}
}


/* ---- bx-mobile ---- */
/* ===================================================================
   bx-mobile — animations (append-only, animates existing elements)
   Meaning: "mobile-first" — a desktop mockup + phone preview assemble,
   touch-target figures count & draw, X-pitfalls draw, method rows stagger.
   =================================================================== */

/* ---------- AMBIENT (always-on, subtle, no JS) ---------- */

/* Browser chrome "traffic-light" dots breathe like a live tab */
@keyframes bx-mobile-dotpulse{
  0%,100%{ opacity:.55 }
  50%    { opacity:1 }
}
.bx-mobile .bx-mobile-dots span{ animation:bx-mobile-dotpulse 3.4s var(--ease) infinite }
.bx-mobile .bx-mobile-dots span:nth-child(2){ animation-delay:.35s }
.bx-mobile .bx-mobile-dots span:nth-child(3){ animation-delay:.7s }

/* The phone preview gently floats — feels held in the hand */
@keyframes bx-mobile-float{
  0%,100%{ transform:translateY(0) }
  50%    { transform:translateY(-6px) }
}
.bx-mobile .bx-mobile-phone{ animation:bx-mobile-float 5.5s var(--ease) infinite; will-change:transform }

/* Featured CTA inside the phone: soft breathing brand glow (the "recommandé" beat) */
@keyframes bx-mobile-ctaglow{
  0%,100%{ box-shadow:0 0 0 0 var(--brand-t) }
  50%    { box-shadow:0 0 0 3px var(--brand-t) }
}
.bx-mobile .bx-mobile-cta{ animation:bx-mobile-ctaglow 3.6s var(--ease) infinite }

/* Slow sheen sweeping the eyebrow hairline — quiet sign of life */
@keyframes bx-mobile-sheen{
  0%  { background-position:-140% 0 }
  100%{ background-position:140% 0 }
}
.bx-mobile .bx-mobile-eyebrow::before{
  background:linear-gradient(90deg,var(--line-strong) 0%,var(--line-strong) 38%,var(--brand-ink) 50%,var(--line-strong) 62%,var(--line-strong) 100%);
  background-size:250% 100%;
  animation:bx-mobile-sheen 4.5s linear infinite;
}

/* ---------- ON-VIEW BUILD (plays once, gated on .is-in) ---------- */

/* Reusable keyframes */
@keyframes bx-mobile-rise{ from{ opacity:0; transform:translateY(14px) } to{ opacity:1; transform:none } }
@keyframes bx-mobile-fillx{ from{ transform:scaleX(0) } to{ transform:scaleX(1) } }
@keyframes bx-mobile-pop{ from{ opacity:0; transform:scale(.94) translateY(10px) } to{ opacity:1; transform:none } }
@keyframes bx-mobile-drawline{ from{ transform:scaleX(0) } to{ transform:scaleX(1) } }
@keyframes bx-mobile-drawcheck{ to{ stroke-dashoffset:0 } }

/* Header intro (root section) */
.bx-mobile.is-in .bx-mobile-eyebrow,
.bx-mobile.is-in .bx-mobile-h2,
.bx-mobile.is-in .bx-mobile-lede{ animation:bx-mobile-rise .6s var(--ease) both }
.bx-mobile.is-in .bx-mobile-h2{ animation-delay:.08s }
.bx-mobile.is-in .bx-mobile-lede{ animation-delay:.16s }

/* Sub-section kicker/head intro */
.bx-mobile-sub.is-in .bx-mobile-kicker,
.bx-mobile-sub.is-in .bx-mobile-h3,
.bx-mobile-sub.is-in .bx-mobile-note{ animation:bx-mobile-rise .55s var(--ease) both }
.bx-mobile-sub.is-in .bx-mobile-h3{ animation-delay:.06s }
.bx-mobile-sub.is-in .bx-mobile-note{ animation-delay:.12s }

/* --- Desktop browser mockup assembles piece by piece --- */
.bx-mobile-sub.is-in .bx-mobile-desk{ animation:bx-mobile-pop .55s var(--ease) both; animation-delay:.14s }
/* chrome bar slides down first */
.bx-mobile-sub.is-in .bx-mobile-bar{ animation:bx-mobile-rise .45s var(--ease) both; animation-delay:.22s }
/* content bars "fill" from the left like a skeleton drawing in */
.bx-mobile-sub.is-in .bx-mobile-deskbody .bx-mobile-blk,
.bx-mobile-sub.is-in .bx-mobile-deskbody .bx-mobile-col span{
  transform-origin:left center;
  animation:bx-mobile-fillx .5s var(--ease) both;
}
.bx-mobile-sub.is-in .bx-mobile-blk.lg{ animation-delay:.34s }
.bx-mobile-sub.is-in .bx-mobile-row .bx-mobile-blk:nth-child(1){ animation-delay:.42s }
.bx-mobile-sub.is-in .bx-mobile-row .bx-mobile-blk:nth-child(2){ animation-delay:.50s }
.bx-mobile-sub.is-in .bx-mobile-row .bx-mobile-blk:nth-child(3){ animation-delay:.58s }
.bx-mobile-sub.is-in .bx-mobile-col:nth-child(1) span{ animation-delay:.60s }
.bx-mobile-sub.is-in .bx-mobile-col:nth-child(2) span{ animation-delay:.66s }
.bx-mobile-sub.is-in .bx-mobile-col:nth-child(3) span{ animation-delay:.72s }

/* --- Phone preview assembles after the desktop, its own rows fill --- */
.bx-mobile-sub.is-in .bx-mobile-phone{ animation:bx-mobile-pop .6s var(--ease) both, bx-mobile-float 5.5s var(--ease) .9s infinite; animation-delay:.5s }
.bx-mobile-sub.is-in .bx-mobile-pbody > *{
  transform-origin:left center;
  animation:bx-mobile-fillx .45s var(--ease) both;
}
.bx-mobile-sub.is-in .bx-mobile-pbody > *:nth-child(1){ animation-delay:.66s }
.bx-mobile-sub.is-in .bx-mobile-pbody > *:nth-child(2){ animation-delay:.72s }
.bx-mobile-sub.is-in .bx-mobile-pbody > *:nth-child(3){ animation-delay:.78s }
.bx-mobile-sub.is-in .bx-mobile-pbody > *:nth-child(4){ animation:bx-mobile-pop .45s var(--ease) both; animation-delay:.86s }
.bx-mobile-sub.is-in .bx-mobile-pbody > *:nth-child(5){ animation-delay:.92s }

/* --- Touch-target notes: stagger in, and their leading hairline "draws" --- */
.bx-mobile-sub.is-in .bx-mobile-notes li{ animation:bx-mobile-rise .5s var(--ease) both }
.bx-mobile-sub.is-in .bx-mobile-notes li:nth-child(1){ animation-delay:.30s }
.bx-mobile-sub.is-in .bx-mobile-notes li:nth-child(2){ animation-delay:.40s }
.bx-mobile-sub.is-in .bx-mobile-notes li:nth-child(3){ animation-delay:.50s }
.bx-mobile-sub.is-in .bx-mobile-notes li:nth-child(4){ animation-delay:.60s }
.bx-mobile-sub.is-in .bx-mobile-fig::before{
  transform-origin:left center;
  animation:bx-mobile-drawline .45s var(--ease) both;
}
.bx-mobile-sub.is-in .bx-mobile-notes li:nth-child(1) .bx-mobile-fig::before{ animation-delay:.42s }
.bx-mobile-sub.is-in .bx-mobile-notes li:nth-child(2) .bx-mobile-fig::before{ animation-delay:.52s }
.bx-mobile-sub.is-in .bx-mobile-notes li:nth-child(3) .bx-mobile-fig::before{ animation-delay:.62s }
.bx-mobile-sub.is-in .bx-mobile-notes li:nth-child(4) .bx-mobile-fig::before{ animation-delay:.72s }

/* --- Pitfalls (X) & method (numbers): staggered rows --- */
.bx-mobile-sub.is-in .bx-mobile-list li{ animation:bx-mobile-rise .5s var(--ease) both }
.bx-mobile-sub.is-in .bx-mobile-list li:nth-child(1){ animation-delay:.10s }
.bx-mobile-sub.is-in .bx-mobile-list li:nth-child(2){ animation-delay:.18s }
.bx-mobile-sub.is-in .bx-mobile-list li:nth-child(3){ animation-delay:.26s }
.bx-mobile-sub.is-in .bx-mobile-list li:nth-child(4){ animation-delay:.34s }
.bx-mobile-sub.is-in .bx-mobile-list li:nth-child(5){ animation-delay:.42s }

/* The X strokes draw themselves (stroke-dashoffset) */
.bx-mobile-sub.is-in .bx-mobile-x path{
  stroke-dasharray:34;
  stroke-dashoffset:34;
  animation:bx-mobile-drawcheck .5s var(--ease) both;
}
.bx-mobile-sub.is-in .bx-mobile-list li:nth-child(1) .bx-mobile-x path{ animation-delay:.24s }
.bx-mobile-sub.is-in .bx-mobile-list li:nth-child(2) .bx-mobile-x path{ animation-delay:.32s }
.bx-mobile-sub.is-in .bx-mobile-list li:nth-child(3) .bx-mobile-x path{ animation-delay:.40s }
.bx-mobile-sub.is-in .bx-mobile-list li:nth-child(4) .bx-mobile-x path{ animation-delay:.48s }

/* Method numbers tick to brand as they land */
@keyframes bx-mobile-numin{ from{ opacity:0; transform:translateX(-6px) } to{ opacity:1; transform:none } }
.bx-mobile-sub.is-in .bx-mobile-num{ animation:bx-mobile-numin .45s var(--ease) both }
.bx-mobile-sub.is-in .bx-mobile-list li:nth-child(1) .bx-mobile-num{ animation-delay:.20s }
.bx-mobile-sub.is-in .bx-mobile-list li:nth-child(2) .bx-mobile-num{ animation-delay:.28s }
.bx-mobile-sub.is-in .bx-mobile-list li:nth-child(3) .bx-mobile-num{ animation-delay:.36s }
.bx-mobile-sub.is-in .bx-mobile-list li:nth-child(4) .bx-mobile-num{ animation-delay:.44s }
.bx-mobile-sub.is-in .bx-mobile-list li:nth-child(5) .bx-mobile-num{ animation-delay:.52s }

/* ---------- HOVER micro-interactions ---------- */
.bx-mobile .bx-mobile-notes li,
.bx-mobile .bx-mobile-list li{ transition:transform .2s var(--ease), color .2s var(--ease) }
.bx-mobile .bx-mobile-notes li:hover{ transform:translateX(3px) }
.bx-mobile .bx-mobile-list li:hover{ transform:translateX(3px) }
.bx-mobile .bx-mobile-fig{ transition:transform .2s var(--ease) }
.bx-mobile .bx-mobile-notes li:hover .bx-mobile-fig{ transform:translateX(2px) }
/* Whole desk lifts on hover — invites inspection */
.bx-mobile .bx-mobile-desk{ transition:transform .2s var(--ease), box-shadow .2s var(--ease) }
.bx-mobile .bx-mobile-stage:hover .bx-mobile-desk{ transform:translateY(-2px); box-shadow:0 10px 30px -18px var(--brand-t2) }

/* ---------- REDUCED-MOTION GUARD ---------- */
@media (prefers-reduced-motion: reduce){
  .bx-mobile *{ animation:none!important; transition:none!important }
  .bx-mobile [style], .bx-mobile *{ opacity:1!important; transform:none!important }
  /* ensure drawn/filled elements rest in their final state */
  .bx-mobile .bx-mobile-x path{ stroke-dashoffset:0!important }
  .bx-mobile .bx-mobile-blk,
  .bx-mobile .bx-mobile-col span,
  .bx-mobile .bx-mobile-pbody > *,
  .bx-mobile .bx-mobile-fig::before{ transform:none!important }
}


/* ---- bx-methode ---- */
/* ==========================================================================
   bx-methode — animations (appended; animates existing elements only)
   Meaning: a linear 4-step method (spine + nodes) → phase cards → delay bars.
   Ambient touches + on-view builds + hover. GPU-friendly. RM-gated at end.
   Trigger: JS adds .is-in to .bx-methode (root) when it scrolls into view.
   ========================================================================== */

/* ---- keyframes ---------------------------------------------------------- */
@keyframes bxm-rise{from{opacity:0;transform:translateY(14px)}to{opacity:1;transform:none}}
@keyframes bxm-line{from{transform:scaleX(0)}to{transform:scaleX(1)}}
@keyframes bxm-rail-draw{from{transform:scaleX(0)}to{transform:scaleX(1)}}
@keyframes bxm-node-pop{0%{opacity:0;transform:scale(0)}70%{transform:scale(1.35)}100%{opacity:1;transform:scale(1)}}
@keyframes bxm-tag-in{from{opacity:0;transform:translateX(-8px)}to{opacity:1;transform:none}}
@keyframes bxm-fill-grow{from{transform:scaleX(0)}to{transform:scaleX(1)}}

/* ambient */
@keyframes bxm-rail-sheen{0%{background-position:-30% 0}100%{background-position:130% 0}}
@keyframes bxm-node-pulse{0%,100%{box-shadow:0 0 0 4px var(--brand-t2)}50%{box-shadow:0 0 0 7px transparent}}
@keyframes bxm-breathe{0%,100%{transform:scale(1);opacity:.9}50%{transform:scale(1.09);opacity:1}}
@keyframes bxm-fill-sheen{0%{background-position:-40% 0}100%{background-position:140% 0}}

/* ---- AMBIENT (always-on, subtle, infinite) ----------------------------- */
/* Slow light traveling along the process spine — expresses the linear flow */
.bx-methode-rail{
  background-image:linear-gradient(90deg,transparent,var(--brand-t2),transparent);
  background-size:35% 100%;background-repeat:no-repeat;background-position:-30% 0;
  animation:bxm-rail-sheen 5.5s var(--ease) infinite;
}
/* Current-step node breathes (living "you are here" pin) */
.bx-methode-node.is-current{animation:bxm-node-pulse 3s var(--ease) infinite}
/* Single interlocutor icon gently breathes */
.bx-methode-solo svg{animation:bxm-breathe 4.5s var(--ease) infinite;transform-origin:center}
/* Recommended "one-page" bar carries a soft brand sheen */
.bx-methode-fill.is-brand{
  background-image:linear-gradient(100deg,transparent,var(--brand-t2),transparent);
  background-size:45% 100%;background-repeat:no-repeat;background-position:-40% 0;
  animation:bxm-fill-sheen 4.5s var(--ease) 1.2s infinite;
}

/* ---- ON-VIEW BUILD (plays once when block scrolls in) ------------------- */
/* Header assembles */
.bx-methode.is-in .bx-methode-eyebrow::before{animation:bxm-line .6s var(--ease) both;transform-origin:left}
.bx-methode.is-in .bx-methode-h2{animation:bxm-rise .6s var(--ease) both;animation-delay:.05s}
.bx-methode.is-in .bx-methode-lede{animation:bxm-rise .6s var(--ease) both;animation-delay:.13s}
.bx-methode.is-in .bx-methode-solo{animation:bxm-rise .6s var(--ease) both;animation-delay:.21s}

/* Spine draws left→right, then nodes pop in sequence */
.bx-methode.is-in .bx-methode-rail{
  transform-origin:left;
  animation:bxm-rail-draw .7s var(--ease) both, bxm-rail-sheen 5.5s var(--ease) .9s infinite;
  animation-delay:.28s, .9s;
}
.bx-methode.is-in .bx-methode-node{animation:bxm-node-pop .5s var(--ease) both;transform-origin:center}
.bx-methode.is-in .bx-methode-node:nth-child(1){animation-delay:.85s}
.bx-methode.is-in .bx-methode-node:nth-child(2){animation-delay:1s}
.bx-methode.is-in .bx-methode-node:nth-child(3){animation-delay:1.15s}
.bx-methode.is-in .bx-methode-node:nth-child(4){animation-delay:1.3s}
/* current node: pop in, then hand off to the ambient breathe */
.bx-methode.is-in .bx-methode-node.is-current{
  animation:bxm-node-pop .5s var(--ease) both, bxm-node-pulse 3s var(--ease) 1.35s infinite;
  animation-delay:.85s, 1.35s;
}

/* Phase cards stagger up */
.bx-methode.is-in .bx-methode-phase{animation:bxm-rise .6s var(--ease) both}
.bx-methode.is-in .bx-methode-phase:nth-child(1){animation-delay:.35s}
.bx-methode.is-in .bx-methode-phase:nth-child(2){animation-delay:.47s}
.bx-methode.is-in .bx-methode-phase:nth-child(3){animation-delay:.59s}
.bx-methode.is-in .bx-methode-phase:nth-child(4){animation-delay:.71s}
/* Tag rows slide in within each phase (secondary life) */
.bx-methode.is-in .bx-methode-tags li{animation:bxm-tag-in .45s var(--ease) both}
.bx-methode.is-in .bx-methode-tags li:nth-child(1){animation-delay:.62s}
.bx-methode.is-in .bx-methode-tags li:nth-child(2){animation-delay:.72s}
.bx-methode.is-in .bx-methode-tags li:nth-child(3){animation-delay:.82s}

/* Timeline: kicker/title rise, rows slide, tracks fill from 0 → their width */
.bx-methode.is-in .bx-methode-tkicker{animation:bxm-rise .6s var(--ease) both;animation-delay:.5s}
.bx-methode.is-in .bx-methode-th3{animation:bxm-rise .6s var(--ease) both;animation-delay:.58s}
.bx-methode.is-in .bx-methode-row{animation:bxm-rise .55s var(--ease) both}
.bx-methode.is-in .bx-methode-row:nth-of-type(1){animation-delay:.66s}
.bx-methode.is-in .bx-methode-row:nth-of-type(2){animation-delay:.78s}
.bx-methode.is-in .bx-methode-fill{transform-origin:left;animation:bxm-fill-grow .9s var(--ease) both}
.bx-methode.is-in .bx-methode-row:nth-of-type(1) .bx-methode-fill{animation-delay:.95s}
.bx-methode.is-in .bx-methode-row:nth-of-type(2) .bx-methode-fill{animation-delay:1.08s}
/* recommended bar: grow, then resume its ambient sheen */
.bx-methode.is-in .bx-methode-fill.is-brand{
  animation:bxm-fill-grow .9s var(--ease) both, bxm-fill-sheen 4.5s var(--ease) infinite;
  animation-delay:.95s, 1.9s;
}
.bx-methode.is-in .bx-methode-scale{animation:bxm-rise .6s var(--ease) both;animation-delay:1.2s}

/* ---- HOVER micro-interactions ------------------------------------------ */
.bx-methode-phase{transition:transform .2s var(--ease)}
.bx-methode-phase:hover{transform:translateY(-3px)}
.bx-methode-phase:hover .bx-methode-num{color:var(--brand)}
.bx-methode-num{transition:color .2s var(--ease)}

.bx-methode-tags li{transition:color .2s var(--ease),transform .2s var(--ease)}
.bx-methode-tags li:hover{color:var(--text);transform:translateX(3px)}
.bx-methode-dot{transition:transform .2s var(--ease)}
.bx-methode-tags li:hover .bx-methode-dot{transform:scale(1.9)}

.bx-methode-row{transition:transform .2s var(--ease)}
.bx-methode-row:hover{transform:translateX(2px)}
.bx-methode-rlabel{transition:color .2s var(--ease)}
.bx-methode-row:hover .bx-methode-rlabel{color:var(--text)}

.bx-methode-solo{transition:color .2s var(--ease)}
.bx-methode-solo:hover{color:var(--text)}

/* ---- reduced motion: nothing left hidden or mid-animation --------------- */
@media (prefers-reduced-motion: reduce){
  .bx-methode *{animation:none!important;transition:none!important}
  .bx-methode [style], .bx-methode *{opacity:1!important;transform:none!important}
}


/* ---- bx-erreurs ---- */
/* ============================================================
   bx-erreurs — animations (diagnostic ledger)
   Meaning: a register of 7 site errors + their corrections.
   Motion vocabulary: pulsing status dots (err = alert, fix = healthy),
   a hairline that draws, ledger rows that "post in" like entries
   being written, and the correction column sliding off its divider.
   Only transform / opacity / filter — no relayout.
   ============================================================ */

/* ---------- AMBIENT (always-on, subtle, infinite) ---------- */

/* Error status dots: a slow diagnostic pulse (like a warning blip).
   Tiny amplitude, long period so it feels alive, not busy. */
@keyframes bx-erreurs-pulse-err{
  0%,100%{ box-shadow:0 0 0 0 var(--danger); filter:none; opacity:1 }
  50%    { box-shadow:0 0 0 3px color-mix(in srgb,var(--danger) 0%,transparent); filter:brightness(1.15); opacity:.82 }
}
.bx-erreurs-dot.is-err{
  animation:bx-erreurs-pulse-err 2.8s var(--ease) infinite;
  will-change:filter,opacity;
}

/* Correction dots: a calmer "healthy" breathing glow, offset in phase
   so the err/fix pair reads as problem vs. resolved. */
@keyframes bx-erreurs-breathe-fix{
  0%,100%{ filter:none; opacity:.9 }
  50%    { filter:brightness(1.25) drop-shadow(0 0 4px color-mix(in srgb,var(--brand) 55%,transparent)); opacity:1 }
}
.bx-erreurs-dot.is-fix{
  animation:bx-erreurs-breathe-fix 4.2s var(--ease) infinite;
  animation-delay:-1.4s;
  will-change:filter,opacity;
}

/* ---------- ON-VIEW BUILD (plays once when block scrolls in) ---------- */

/* Initial pre-entry states (only meaningful once .is-in wires the reveal;
   reduced-motion guard below restores everything to visible). */
.bx-erreurs-eyebrow::before{ transform:scaleX(0); transform-origin:left center }
.bx-erreurs-head > *{ opacity:0; transform:translateY(10px) }
.bx-erreurs-legend span{ opacity:0; transform:translateY(6px) }
.bx-erreurs-ledger{ opacity:0 }
.bx-erreurs-row{ opacity:0 }
.bx-erreurs-foot > *{ opacity:0; transform:translateY(8px) }

/* Eyebrow hairline draws out like a pen stroke opening the register. */
@keyframes bx-erreurs-draw-line{ from{ transform:scaleX(0) } to{ transform:scaleX(1) } }
.bx-erreurs.is-in .bx-erreurs-eyebrow::before{
  animation:bx-erreurs-draw-line .55s var(--ease) .05s both;
}

/* Head: eyebrow text, title, lede rise in sequence. */
@keyframes bx-erreurs-rise{ to{ opacity:1; transform:none } }
.bx-erreurs.is-in .bx-erreurs-eyebrow{ animation:bx-erreurs-rise .5s var(--ease) both }
.bx-erreurs.is-in .bx-erreurs-head > *{ animation:bx-erreurs-rise .6s var(--ease) both }
.bx-erreurs.is-in .bx-erreurs-h2{ animation-delay:.10s }
.bx-erreurs.is-in .bx-erreurs-lede{ animation-delay:.18s }
.bx-erreurs.is-in .bx-erreurs-legend span{ animation:bx-erreurs-rise .5s var(--ease) both }
.bx-erreurs.is-in .bx-erreurs-legend span:nth-child(1){ animation-delay:.26s }
.bx-erreurs.is-in .bx-erreurs-legend span:nth-child(2){ animation-delay:.34s }

/* Ledger container fades up as a surface for the rows to post onto. */
.bx-erreurs.is-in .bx-erreurs-ledger{ animation:bx-erreurs-rise .5s var(--ease) .30s both }

/* Rows "post in" one by one — like entries being written to the register. */
@keyframes bx-erreurs-row-in{
  from{ opacity:0; transform:translateY(16px) }
  to  { opacity:1; transform:none }
}
.bx-erreurs.is-in .bx-erreurs-row{ animation:bx-erreurs-row-in .6s var(--ease) both }
.bx-erreurs.is-in .bx-erreurs-row:nth-child(1){ animation-delay:.40s }
.bx-erreurs.is-in .bx-erreurs-row:nth-child(2){ animation-delay:.50s }
.bx-erreurs.is-in .bx-erreurs-row:nth-child(3){ animation-delay:.60s }
.bx-erreurs.is-in .bx-erreurs-row:nth-child(4){ animation-delay:.70s }
.bx-erreurs.is-in .bx-erreurs-row:nth-child(5){ animation-delay:.80s }
.bx-erreurs.is-in .bx-erreurs-row:nth-child(6){ animation-delay:.90s }
.bx-erreurs.is-in .bx-erreurs-row:nth-child(7){ animation-delay:1.00s }

/* Mono index counts up its weight — a quick "stamp" as each row lands. */
@keyframes bx-erreurs-stamp{
  0%  { opacity:0; transform:translateX(-4px) scale(.92) }
  60% { opacity:1; transform:translateX(0) scale(1.04) }
  100%{ opacity:1; transform:none }
}
.bx-erreurs.is-in .bx-erreurs-idx{ animation:bx-erreurs-stamp .5s var(--ease) both }
.bx-erreurs.is-in .bx-erreurs-row:nth-child(1) .bx-erreurs-idx{ animation-delay:.46s }
.bx-erreurs.is-in .bx-erreurs-row:nth-child(2) .bx-erreurs-idx{ animation-delay:.56s }
.bx-erreurs.is-in .bx-erreurs-row:nth-child(3) .bx-erreurs-idx{ animation-delay:.66s }
.bx-erreurs.is-in .bx-erreurs-row:nth-child(4) .bx-erreurs-idx{ animation-delay:.76s }
.bx-erreurs.is-in .bx-erreurs-row:nth-child(5) .bx-erreurs-idx{ animation-delay:.86s }
.bx-erreurs.is-in .bx-erreurs-row:nth-child(6) .bx-erreurs-idx{ animation-delay:.96s }
.bx-erreurs.is-in .bx-erreurs-row:nth-child(7) .bx-erreurs-idx{ animation-delay:1.06s }

/* The correction column slides in off its divider — the "fix" arriving
   after the error is stated. Nudged from the left border, settles home. */
@keyframes bx-erreurs-fix-in{
  from{ opacity:0; transform:translateX(14px) }
  to  { opacity:1; transform:none }
}
.bx-erreurs .bx-erreurs-cell.is-fix{ will-change:transform,opacity }
.bx-erreurs.is-in .bx-erreurs-cell.is-fix{ animation:bx-erreurs-fix-in .6s var(--ease) both }
.bx-erreurs.is-in .bx-erreurs-row:nth-child(1) .bx-erreurs-cell.is-fix{ animation-delay:.54s }
.bx-erreurs.is-in .bx-erreurs-row:nth-child(2) .bx-erreurs-cell.is-fix{ animation-delay:.64s }
.bx-erreurs.is-in .bx-erreurs-row:nth-child(3) .bx-erreurs-cell.is-fix{ animation-delay:.74s }
.bx-erreurs.is-in .bx-erreurs-row:nth-child(4) .bx-erreurs-cell.is-fix{ animation-delay:.84s }
.bx-erreurs.is-in .bx-erreurs-row:nth-child(5) .bx-erreurs-cell.is-fix{ animation-delay:.94s }
.bx-erreurs.is-in .bx-erreurs-row:nth-child(6) .bx-erreurs-cell.is-fix{ animation-delay:1.04s }
.bx-erreurs.is-in .bx-erreurs-row:nth-child(7) .bx-erreurs-cell.is-fix{ animation-delay:1.14s }

/* Footer CTA rises in last. */
.bx-erreurs.is-in .bx-erreurs-foot > *{ animation:bx-erreurs-rise .55s var(--ease) both }
.bx-erreurs.is-in .bx-erreurs-foot > *:nth-child(1){ animation-delay:1.10s }
.bx-erreurs.is-in .bx-erreurs-foot > *:nth-child(2){ animation-delay:1.18s }

/* ---------- HOVER micro-interactions ---------- */

/* Row hover: gentle lift + the index brightens, as if that entry is
   pulled forward for review. */
.bx-erreurs-row{ transition:transform .2s var(--ease) }
.bx-erreurs-idx{ transition:transform .2s var(--ease),color .2s var(--ease),text-shadow .2s var(--ease) }
.bx-erreurs-row:hover{ transform:translateY(-2px) }
.bx-erreurs-row:hover .bx-erreurs-idx{
  transform:translateX(2px);
  text-shadow:0 0 10px color-mix(in srgb,var(--brand) 40%,transparent);
}

/* Titles tint toward their state colour on hover of the row. */
.bx-erreurs-title-err,.bx-erreurs-title-fix{ transition:color .2s var(--ease) }
.bx-erreurs-row:hover .bx-erreurs-title-err{ color:var(--danger) }
.bx-erreurs-row:hover .bx-erreurs-title-fix{ color:var(--brand-ink) }

/* Footer link: arrow-like nudge + brand tint. */
.bx-erreurs-foot a{ transition:color .2s var(--ease),transform .2s var(--ease); display:inline-block }
.bx-erreurs-foot a:hover{ color:var(--brand); transform:translateX(2px) }

/* ---------- Reduced motion: everything visible, nothing mid-flight ---------- */
@media (prefers-reduced-motion: reduce){
  .bx-erreurs *{ animation:none!important; transition:none!important }
  .bx-erreurs [style], .bx-erreurs *{ opacity:1!important; transform:none!important }
  .bx-erreurs-eyebrow::before{ transform:scaleX(1)!important }
}


/* ---- bx-apropos ---- */
/* ===================================================================
   bx-apropos — animations (appended) — REVISED
   Meaning: a career timeline on a connector line, a client roster,
   big stat figures, and a grouped tooling stack.
   FIXES in this revision:
     - Hand off the two 'is-now' ambient loops on a (0,6,0) selector so
       the on-view build no longer clobbers pin-pulse / now-glow.
     - Give the timeline connector a JS-free fallback via
       @media (scripting:none) so it renders even when .is-in never lands.
   =================================================================== */

/* ---------- keyframes ---------- */
@keyframes bxa-rise{from{opacity:0;transform:translateY(14px)}to{opacity:1;transform:translateY(0)}}
@keyframes bxa-rise-sm{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}
@keyframes bxa-slide-in{from{opacity:0;transform:translateX(-16px)}to{opacity:1;transform:translateX(0)}}
@keyframes bxa-pop{0%{opacity:0;transform:scale(.4)}60%{opacity:1;transform:scale(1.18)}100%{opacity:1;transform:scale(1)}}
@keyframes bxa-chip-in{from{opacity:0;transform:translateY(6px) scale(.94)}to{opacity:1;transform:translateY(0) scale(1)}}
@keyframes bxa-draw-x{from{transform:scaleX(0)}to{transform:scaleX(1)}}
/* ambient: light travelling along the timeline hairline */
@keyframes bxa-line-sweep{0%{background-position:200% 0}100%{background-position:-100% 0}}
/* ambient: the "aujourd'hui / is-now" pin breathing */
@keyframes bxa-pin-pulse{0%,100%{box-shadow:0 0 0 0 var(--brand-t)}55%{box-shadow:0 0 0 6px transparent}}
/* ambient: soft sheen on the eyebrow hairline */
@keyframes bxa-eyebrow-sheen{0%{background-position:200% 0}100%{background-position:-100% 0}}
/* ambient: gentle breathing on the active step title */
@keyframes bxa-now-glow{0%,100%{opacity:.85}50%{opacity:1}}

/* ---------- AMBIENT (always on, JS-free) ---------- */

/* eyebrow rule gets a slow travelling sheen */
.bx-apropos-eyebrow::before{
  background:linear-gradient(90deg,var(--line),var(--brand),var(--line));
  background-size:220% 100%;
  animation:bxa-eyebrow-sheen 5.5s linear infinite;
}

/* timeline connector: faint light drifting along the track */
.bx-apropos-steps::before{
  background:linear-gradient(90deg,var(--line) 0%,var(--line) 40%,var(--brand-t2) 50%,var(--line) 60%,var(--line) 100%);
  background-size:220% 100%;
  animation:bxa-line-sweep 6s linear infinite;
}

/* the "now" step pin quietly breathes, and its title glows.
   NOTE: these plain (0,3,0) ambient rules are the pre-scroll / no-build
   fallback. Once the build reveals the block, the (0,6,0) hand-off rules
   below take over (reveal-then-loop) so the ambient motion survives. */
.bx-apropos-step.is-now .bx-apropos-dot{animation:bxa-pin-pulse 3.2s ease-in-out infinite}
.bx-apropos-step.is-now .bx-apropos-step-t{animation:bxa-now-glow 3.6s ease-in-out infinite}

/* ---------- ON-VIEW BUILD ---------- */

/* header (keyed to the block root) */
.bx-apropos-eyebrow,.bx-apropos-title,.bx-apropos-lede{opacity:0}
.bx-apropos.is-in .bx-apropos-eyebrow{animation:bxa-rise-sm .55s var(--ease) .02s both}
.bx-apropos.is-in .bx-apropos-title{animation:bxa-rise .6s var(--ease) .10s both}
.bx-apropos.is-in .bx-apropos-lede{animation:bxa-rise .6s var(--ease) .20s both}

/* every sub kicker + h3 rises when its part scrolls in */
.bx-apropos-sub .bx-apropos-kicker,.bx-apropos-sub .bx-apropos-h3{opacity:0}
.bx-apropos-sub.is-in .bx-apropos-kicker{animation:bxa-rise-sm .5s var(--ease) .02s both}
.bx-apropos-sub.is-in .bx-apropos-h3{animation:bxa-rise .55s var(--ease) .10s both}

/* 01 · PARCOURS — connector draws left→right, dots pop, cards rise */
.bx-apropos-steps::before{transform:scaleX(0);transform-origin:left center}
.bx-apropos-sub.is-in .bx-apropos-steps::before{
  animation:bxa-line-sweep 6s linear infinite,
            bxa-draw-x .75s var(--ease) .15s both;
}
/* FIX 3: no-JS / no-observer fallback — restore the connector when
   scripting is unavailable so the timeline still renders without JS
   (the collapsed scaleX(0) state above is only meant to precede the
   .is-in draw). Precisely targets no-JS UAs and never misfires while
   JS is present, unlike a `.js` class convention we don't control. */
@media (scripting: none){
  .bx-apropos-steps::before{transform:none}
}

.bx-apropos-step .bx-apropos-dot{opacity:0}
.bx-apropos-step .bx-apropos-step-n,
.bx-apropos-step .bx-apropos-step-t,
.bx-apropos-step .bx-apropos-step-d{opacity:0}
.bx-apropos-sub.is-in .bx-apropos-step:nth-child(1) .bx-apropos-dot{animation:bxa-pop .5s var(--ease) .30s both}
.bx-apropos-sub.is-in .bx-apropos-step:nth-child(2) .bx-apropos-dot{animation:bxa-pop .5s var(--ease) .45s both}
.bx-apropos-sub.is-in .bx-apropos-step:nth-child(3) .bx-apropos-dot{animation:bxa-pop .5s var(--ease) .60s both}
.bx-apropos-sub.is-in .bx-apropos-step:nth-child(1) .bx-apropos-step-n,
.bx-apropos-sub.is-in .bx-apropos-step:nth-child(1) .bx-apropos-step-t,
.bx-apropos-sub.is-in .bx-apropos-step:nth-child(1) .bx-apropos-step-d{animation:bxa-rise-sm .55s var(--ease) .38s both}
.bx-apropos-sub.is-in .bx-apropos-step:nth-child(2) .bx-apropos-step-n,
.bx-apropos-sub.is-in .bx-apropos-step:nth-child(2) .bx-apropos-step-t,
.bx-apropos-sub.is-in .bx-apropos-step:nth-child(2) .bx-apropos-step-d{animation:bxa-rise-sm .55s var(--ease) .53s both}
.bx-apropos-sub.is-in .bx-apropos-step:nth-child(3) .bx-apropos-step-n,
.bx-apropos-sub.is-in .bx-apropos-step:nth-child(3) .bx-apropos-step-t,
.bx-apropos-sub.is-in .bx-apropos-step:nth-child(3) .bx-apropos-step-d{animation:bxa-rise-sm .55s var(--ease) .68s both}

/* FIX 1 + 2: 'aujourd'hui' hand-off. These selectors are (0,6,0), so
   they outrank the plain (0,5,0) nth-child build rules that would
   otherwise set `animation` on the same is-now dot/title and kill the
   ambient loop after reveal. Each runs the reveal ONCE (matching the
   nth-child(3) stagger: dot .60s, title .68s) then starts the infinite
   ambient loop, delayed until the reveal has fully landed so there is a
   seamless reveal→loop hand-off with no re-trigger flash. */
.bx-apropos-sub.is-in .bx-apropos-step.is-now .bx-apropos-dot{
  animation:bxa-pop .5s var(--ease) .60s both,
            bxa-pin-pulse 3.2s ease-in-out 1.3s infinite;
}
.bx-apropos-sub.is-in .bx-apropos-step.is-now .bx-apropos-step-t{
  animation:bxa-rise-sm .55s var(--ease) .68s both,
            bxa-now-glow 3.6s ease-in-out 1.4s infinite;
}

/* client roster — rows stagger in from the left */
.bx-apropos-clients li{opacity:0}
.bx-apropos-sub.is-in .bx-apropos-clients li{animation:bxa-slide-in .5s var(--ease) both}
.bx-apropos-sub.is-in .bx-apropos-clients li:nth-child(1){animation-delay:.10s}
.bx-apropos-sub.is-in .bx-apropos-clients li:nth-child(2){animation-delay:.18s}
.bx-apropos-sub.is-in .bx-apropos-clients li:nth-child(3){animation-delay:.26s}
.bx-apropos-sub.is-in .bx-apropos-clients li:nth-child(4){animation-delay:.34s}

/* stat figures — rise + count up (see dataCount) */
.bx-apropos-stat{opacity:0}
.bx-apropos-sub.is-in .bx-apropos-stat:nth-child(1){animation:bxa-rise .55s var(--ease) .12s both}
.bx-apropos-sub.is-in .bx-apropos-stat:nth-child(2){animation:bxa-rise .55s var(--ease) .24s both}
.bx-apropos-sub.is-in .bx-apropos-stat:nth-child(3){animation:bxa-rise .55s var(--ease) .36s both}

/* 02 · STACK — tool rows stagger, chips pop in one by one */
.bx-apropos-tool-row{opacity:0}
.bx-apropos-sub.is-in .bx-apropos-tool-row:nth-child(1){animation:bxa-rise-sm .5s var(--ease) .10s both}
.bx-apropos-sub.is-in .bx-apropos-tool-row:nth-child(2){animation:bxa-rise-sm .5s var(--ease) .18s both}
.bx-apropos-sub.is-in .bx-apropos-tool-row:nth-child(3){animation:bxa-rise-sm .5s var(--ease) .26s both}
.bx-apropos-sub.is-in .bx-apropos-tool-row:nth-child(4){animation:bxa-rise-sm .5s var(--ease) .34s both}
.bx-apropos-chip{opacity:0}
.bx-apropos-sub.is-in .bx-apropos-chip{animation:bxa-chip-in .42s var(--ease) both}
.bx-apropos-sub.is-in .bx-apropos-chip:nth-child(1){animation-delay:.28s}
.bx-apropos-sub.is-in .bx-apropos-chip:nth-child(2){animation-delay:.35s}
.bx-apropos-sub.is-in .bx-apropos-chip:nth-child(3){animation-delay:.42s}
.bx-apropos-sub.is-in .bx-apropos-chip:nth-child(4){animation-delay:.49s}

/* ---------- HOVER micro-interactions ---------- */

/* client rows: subtle lift + tint, role nudges toward its value */
.bx-apropos-clients li{transition:background .2s var(--ease),transform .2s var(--ease),padding-left .2s var(--ease)}
.bx-apropos-clients li:hover{background:var(--panel);transform:translateX(2px);padding-left:8px}
.bx-apropos-client-name{transition:color .2s var(--ease)}
.bx-apropos-clients li:hover .bx-apropos-client-name{color:var(--brand-ink)}
.bx-apropos-client-role{transition:color .2s var(--ease),transform .2s var(--ease)}
.bx-apropos-clients li:hover .bx-apropos-client-role{color:var(--text-2);transform:translateX(-2px)}

/* stat cell: figure lifts a touch on hover */
.bx-apropos-stat-fig{transition:transform .2s var(--ease),color .2s var(--ease)}
.bx-apropos-stat:hover .bx-apropos-stat-fig{transform:translateY(-2px);color:var(--brand-ink)}

/* chips: lift + brand tint */
.bx-apropos-chip{transition:background .2s var(--ease),color .2s var(--ease),transform .2s var(--ease)}
.bx-apropos-chip:hover{background:var(--brand-t);color:var(--brand-ink);transform:translateY(-2px)}

/* tool row: faint tint of its role label on hover */
.bx-apropos-tool-role b{transition:color .2s var(--ease)}
.bx-apropos-tool-row:hover .bx-apropos-tool-role b{color:var(--brand)}

/* ---------- reduced motion guard ---------- */
@media (prefers-reduced-motion: reduce){
  .bx-apropos *{animation:none!important;transition:none!important}
  .bx-apropos [style],.bx-apropos *{opacity:1!important;transform:none!important}
  .bx-apropos-steps::before{transform:none!important}
}


/* ---- bx-vitrine ---- */
/* ============================================================
   bx-vitrine — animations (append-only, animates existing els)
   Meaning: a "spec sheet" of a vitrine site — checklist rows,
   an assembling browser + sitemap mockup with drawing connectors,
   a highlighted "choice" panel, and audience chips.
   ============================================================ */

/* ---- default rest states (only for on-view built elements) ---- */
/* Guarded so nothing hides if JS/observer never fires: only
   applied while the block is NOT yet .is-in. */
.bx-vitrine:not(.is-in) header > *{opacity:0}
.bx-vitrine-sec:not(.is-in) .bx-vitrine-row{opacity:0}
.bx-vitrine-sec:not(.is-in) .bx-vitrine-browser{opacity:0}
.bx-vitrine-sec:not(.is-in) .bx-vitrine-choice{opacity:0}
.bx-vitrine-sec:not(.is-in) .bx-vitrine-kicker,
.bx-vitrine-sec:not(.is-in) .bx-vitrine-h3,
.bx-vitrine-sec:not(.is-in) .bx-vitrine-note{opacity:0}
.bx-vitrine:not(.is-in) .bx-vitrine-for-lbl,
.bx-vitrine:not(.is-in) .bx-vitrine-chip{opacity:0}

/* =============== ON-VIEW BUILD : HEADER =============== */
@keyframes bxv-up{from{opacity:0;transform:translateY(14px)}to{opacity:1;transform:none}}
@keyframes bxv-rise{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}

.bx-vitrine.is-in .bx-vitrine-eyebrow{animation:bxv-up .55s var(--ease) both}
.bx-vitrine.is-in .bx-vitrine-h2{animation:bxv-up .6s var(--ease) .08s both}
.bx-vitrine.is-in .bx-vitrine-lede{animation:bxv-up .6s var(--ease) .16s both}
/* eyebrow hairline draws in from the label */
@keyframes bxv-hair{from{transform:scaleX(0)}to{transform:scaleX(1)}}
.bx-vitrine.is-in .bx-vitrine-eyebrow::before{transform-origin:left center;animation:bxv-hair .5s var(--ease) .2s both}

/* =============== ON-VIEW BUILD : SECTION HEADINGS =============== */
.bx-vitrine-sec.is-in .bx-vitrine-kicker{animation:bxv-rise .5s var(--ease) both}
.bx-vitrine-sec.is-in .bx-vitrine-h3{animation:bxv-rise .55s var(--ease) .06s both}
.bx-vitrine-sec.is-in .bx-vitrine-note{animation:bxv-rise .55s var(--ease) .12s both}

/* =============== ON-VIEW BUILD : CHECKLIST ROWS =============== */
/* Rows slide + stagger in; the spec sheet fills line by line. */
@keyframes bxv-row{from{opacity:0;transform:translateX(-10px)}to{opacity:1;transform:none}}
.bx-vitrine-sec.is-in .bx-vitrine-row{animation:bxv-row .5s var(--ease) both}
.bx-vitrine-sec.is-in .bx-vitrine-row:nth-child(1){animation-delay:.14s}
.bx-vitrine-sec.is-in .bx-vitrine-row:nth-child(2){animation-delay:.22s}
.bx-vitrine-sec.is-in .bx-vitrine-row:nth-child(3){animation-delay:.30s}
.bx-vitrine-sec.is-in .bx-vitrine-row:nth-child(4){animation-delay:.38s}
.bx-vitrine-sec.is-in .bx-vitrine-row:nth-child(5){animation-delay:.46s}

/* Checkmark literally draws itself (stroke-dashoffset), right after its row */
.bx-vitrine .bx-vitrine-check{stroke-dasharray:26;stroke-dashoffset:0}
@keyframes bxv-check{from{stroke-dashoffset:26}to{stroke-dashoffset:0}}
.bx-vitrine-sec.is-in .bx-vitrine-check{animation:bxv-check .5s var(--ease) both}
.bx-vitrine-sec.is-in .bx-vitrine-row:nth-child(1) .bx-vitrine-check{animation-delay:.30s}
.bx-vitrine-sec.is-in .bx-vitrine-row:nth-child(2) .bx-vitrine-check{animation-delay:.38s}
.bx-vitrine-sec.is-in .bx-vitrine-row:nth-child(3) .bx-vitrine-check{animation-delay:.46s}
.bx-vitrine-sec.is-in .bx-vitrine-row:nth-child(4) .bx-vitrine-check{animation-delay:.54s}
.bx-vitrine-sec.is-in .bx-vitrine-row:nth-child(5) .bx-vitrine-check{animation-delay:.62s}
/* tags pop after their check */
@keyframes bxv-pop{from{opacity:0;transform:translateY(4px) scale(.94)}to{opacity:1;transform:none}}
.bx-vitrine-sec.is-in .bx-vitrine-tag{animation:bxv-pop .4s var(--ease) both}
.bx-vitrine-sec.is-in .bx-vitrine-row:nth-child(1) .bx-vitrine-tag{animation-delay:.40s}
.bx-vitrine-sec.is-in .bx-vitrine-row:nth-child(2) .bx-vitrine-tag{animation-delay:.48s}
.bx-vitrine-sec.is-in .bx-vitrine-row:nth-child(3) .bx-vitrine-tag{animation-delay:.56s}
.bx-vitrine-sec.is-in .bx-vitrine-row:nth-child(4) .bx-vitrine-tag{animation-delay:.64s}
.bx-vitrine-sec.is-in .bx-vitrine-row:nth-child(5) .bx-vitrine-tag{animation-delay:.72s}

/* =============== ON-VIEW BUILD : BROWSER / SITEMAP MOCKUP =============== */
/* The mockup assembles: frame settles, chrome, dots, url, then the
   sitemap tree with connector lines drawing and nodes popping in. */
@keyframes bxv-frame{from{opacity:0;transform:translateY(16px) scale(.985)}to{opacity:1;transform:none}}
.bx-vitrine-sec.is-in .bx-vitrine-browser{animation:bxv-frame .6s var(--ease) .5s both}

@keyframes bxv-slidedown{from{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:none}}
.bx-vitrine-sec.is-in .bx-vitrine-chrome{animation:bxv-slidedown .45s var(--ease) .62s both}

/* traffic-light dots pop in one by one */
.bx-vitrine-sec.is-in .bx-vitrine-dots i{animation:bxv-pop .35s var(--ease) both}
.bx-vitrine-sec.is-in .bx-vitrine-dots i:nth-child(1){animation-delay:.70s}
.bx-vitrine-sec.is-in .bx-vitrine-dots i:nth-child(2){animation-delay:.76s}
.bx-vitrine-sec.is-in .bx-vitrine-dots i:nth-child(3){animation-delay:.82s}

/* url reveals like it's being typed/loaded (clip from left) */
@keyframes bxv-type{from{clip-path:inset(0 100% 0 0)}to{clip-path:inset(0 0 0 0)}}
.bx-vitrine-sec.is-in .bx-vitrine-url{animation:bxv-type .55s steps(24,end) .86s both}

/* root node (home / "Accueil") lands with a soft brand glow */
@keyframes bxv-node{from{opacity:0;transform:translateY(8px) scale(.96)}to{opacity:1;transform:none}}
.bx-vitrine-sec.is-in .bx-vitrine-node.is-root{animation:bxv-node .5s var(--ease) .95s both}

/* connector lines DRAW: vertical trunk grows down, branches grow right */
.bx-vitrine .bx-vitrine-children::before{transform-origin:top center}
.bx-vitrine .bx-vitrine-child::before{transform-origin:left center}
@keyframes bxv-trunk{from{transform:scaleY(0)}to{transform:scaleY(1)}}
@keyframes bxv-branch{from{transform:scaleX(0)}to{transform:scaleX(1)}}
.bx-vitrine-sec.is-in .bx-vitrine-children::before{animation:bxv-trunk .5s var(--ease) 1.05s both}
.bx-vitrine-sec.is-in .bx-vitrine-child::before{animation:bxv-branch .3s var(--ease) both}
.bx-vitrine-sec.is-in .bx-vitrine-child:nth-child(1)::before{animation-delay:1.18s}
.bx-vitrine-sec.is-in .bx-vitrine-child:nth-child(2)::before{animation-delay:1.28s}
.bx-vitrine-sec.is-in .bx-vitrine-child:nth-child(3)::before{animation-delay:1.38s}
.bx-vitrine-sec.is-in .bx-vitrine-child:nth-child(4)::before{animation-delay:1.48s}

/* child nodes pop in right after their branch draws */
.bx-vitrine-sec.is-in .bx-vitrine-child .bx-vitrine-node{animation:bxv-node .4s var(--ease) both}
.bx-vitrine-sec.is-in .bx-vitrine-child:nth-child(1) .bx-vitrine-node{animation-delay:1.24s}
.bx-vitrine-sec.is-in .bx-vitrine-child:nth-child(2) .bx-vitrine-node{animation-delay:1.34s}
.bx-vitrine-sec.is-in .bx-vitrine-child:nth-child(3) .bx-vitrine-node{animation-delay:1.44s}
.bx-vitrine-sec.is-in .bx-vitrine-child:nth-child(4) .bx-vitrine-node{animation-delay:1.54s}

/* legend fades up last */
.bx-vitrine-sec.is-in .bx-vitrine-legend{animation:bxv-rise .5s var(--ease) 1.6s both}

/* the "one-page vs multi-page" choice panel slides in */
@keyframes bxv-panel{from{opacity:0;transform:translateY(12px)}to{opacity:1;transform:none}}
.bx-vitrine-sec.is-in .bx-vitrine-choice{animation:bxv-panel .55s var(--ease) 1.7s both}

/* =============== ON-VIEW BUILD : "CONÇU POUR" CHIPS =============== */
.bx-vitrine.is-in .bx-vitrine-for-lbl{animation:bxv-rise .5s var(--ease) both}
.bx-vitrine.is-in .bx-vitrine-chip{animation:bxv-pop .4s var(--ease) both}
.bx-vitrine.is-in .bx-vitrine-chip:nth-child(1){animation-delay:.08s}
.bx-vitrine.is-in .bx-vitrine-chip:nth-child(2){animation-delay:.16s}
.bx-vitrine.is-in .bx-vitrine-chip:nth-child(3){animation-delay:.24s}
.bx-vitrine.is-in .bx-vitrine-chip:nth-child(4){animation-delay:.32s}

/* =============== AMBIENT (always-on, subtle, infinite) =============== */

/* Root sitemap node breathes a soft brand glow — the "live" homepage */
@keyframes bxv-breathe{0%,100%{box-shadow:0 0 0 0 var(--brand-t)}50%{box-shadow:0 0 0 5px var(--brand-t)}}
.bx-vitrine .bx-vitrine-node.is-root{animation:bxv-breathe 4.5s ease-in-out 2.4s infinite}

/* Sheen sweeps across the root node like a passing highlight */
.bx-vitrine .bx-vitrine-node.is-root{position:relative;overflow:hidden}
.bx-vitrine .bx-vitrine-node.is-root::after{
  content:"";position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(105deg,transparent 30%,var(--brand-t2) 48%,transparent 66%);
  background-size:220% 100%;background-repeat:no-repeat;background-position:180% 0;
  animation:bxv-sheen 5.5s ease-in-out 2.6s infinite}
@keyframes bxv-sheen{0%{background-position:180% 0}55%,100%{background-position:-80% 0}}

/* Highlighted "choice" panel gets a slow diagonal sheen sweep */
.bx-vitrine .bx-vitrine-choice{position:relative;overflow:hidden}
.bx-vitrine .bx-vitrine-choice::after{
  content:"";position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(115deg,transparent 38%,var(--brand-t2) 50%,transparent 62%);
  background-size:260% 100%;background-repeat:no-repeat;background-position:200% 0;
  animation:bxv-sheen2 6s ease-in-out 3s infinite}
@keyframes bxv-sheen2{0%{background-position:200% 0}50%,100%{background-position:-120% 0}}

/* the choice-panel icon gently pulses */
@keyframes bxv-iconpulse{0%,100%{opacity:.75}50%{opacity:1}}
.bx-vitrine .bx-vitrine-choice svg{animation:bxv-iconpulse 3.2s ease-in-out 3s infinite}

/* "Option" tag softly pulses to flag it's optional */
@keyframes bxv-optpulse{0%,100%{opacity:.7}50%{opacity:1}}
.bx-vitrine .bx-vitrine-tag.is-opt{animation:bxv-optpulse 3s ease-in-out 1s infinite}

/* browser traffic-light dots gently shimmer, staggered, after assembly */
@keyframes bxv-dot{0%,100%{opacity:.55}50%{opacity:.95}}
.bx-vitrine .bx-vitrine-dots i:nth-child(1){animation:bxv-dot 3.4s ease-in-out 2s infinite}
.bx-vitrine .bx-vitrine-dots i:nth-child(2){animation:bxv-dot 3.4s ease-in-out 2.25s infinite}
.bx-vitrine .bx-vitrine-dots i:nth-child(3){animation:bxv-dot 3.4s ease-in-out 2.5s infinite}

/* legend "page principale" dot softly pulses like a live status */
@keyframes bxv-statuspulse{0%,100%{transform:scale(1);opacity:.85}50%{transform:scale(1.25);opacity:1}}
.bx-vitrine .bx-vitrine-legend i.d1{animation:bxv-statuspulse 2.8s ease-in-out 2.2s infinite}

/* =============== HOVER MICRO-INTERACTIONS =============== */
/* Spec rows lift + tint their label; check nudges */
.bx-vitrine .bx-vitrine-row{transition:transform .2s var(--ease)}
.bx-vitrine .bx-vitrine-row:hover{transform:translateX(3px)}
.bx-vitrine .bx-vitrine-label,
.bx-vitrine .bx-vitrine-check{transition:color .2s var(--ease),transform .2s var(--ease)}
.bx-vitrine .bx-vitrine-row:hover .bx-vitrine-check{transform:translateY(2px) scale(1.12)}

/* sitemap child nodes lift + tint toward brand on hover */
.bx-vitrine .bx-vitrine-child .bx-vitrine-node{transition:transform .2s var(--ease),border-color .2s var(--ease),color .2s var(--ease),background .2s var(--ease)}
.bx-vitrine .bx-vitrine-child .bx-vitrine-node:hover{transform:translateY(-2px);border-color:var(--brand);color:var(--brand-ink);background:var(--brand-t)}

/* audience chips lift + tint */
.bx-vitrine .bx-vitrine-chip{transition:transform .2s var(--ease),color .2s var(--ease)}
.bx-vitrine .bx-vitrine-chip:hover{transform:translateY(-2px);color:var(--text)}

/* =============== REDUCED MOTION GUARD =============== */
@media (prefers-reduced-motion: reduce){
  .bx-vitrine *{animation:none!important;transition:none!important}
  .bx-vitrine [style], .bx-vitrine *{opacity:1!important;transform:none!important}
  .bx-vitrine .bx-vitrine-check{stroke-dashoffset:0!important}
  .bx-vitrine .bx-vitrine-url{clip-path:none!important}
  .bx-vitrine .bx-vitrine-node.is-root::after,
  .bx-vitrine .bx-vitrine-choice::after{display:none!important}
  .bx-vitrine :not(.is-in) header>*,
  .bx-vitrine-sec:not(.is-in) .bx-vitrine-row,
  .bx-vitrine-sec:not(.is-in) .bx-vitrine-browser,
  .bx-vitrine-sec:not(.is-in) .bx-vitrine-choice,
  .bx-vitrine-sec:not(.is-in) .bx-vitrine-kicker,
  .bx-vitrine-sec:not(.is-in) .bx-vitrine-h3,
  .bx-vitrine-sec:not(.is-in) .bx-vitrine-note,
  .bx-vitrine:not(.is-in) .bx-vitrine-for-lbl,
  .bx-vitrine:not(.is-in) .bx-vitrine-chip{opacity:1!important}
}


/* ============ GARDE NO-JS : rien d'invisible sans JavaScript ============ */
html:not(.js) :is(.feature-card,
  .presta-card,
  .price-card,
  .price-feature,
  .check-list li,
  .stats__item,
  .faq__item,
  .section-head,
  .cta__intro,
  [class^="bx-"] [class$="-sub"],
  [class^="bx-"] [class$="-sec"],
  [class^="bx-"] [class*="__part"]) {
  opacity: 1 !important; transform: none !important; animation: none !important;
}

/* =========================================================
   CORRECTIFS post-vérification
   ========================================================= */
/* Tables de contenu (pages légales / prose) : défilables sur mobile
   sans casser l'alignement des colonnes (display:block => scroll du bloc,
   l'intérieur reste une table). On exclut la matrice bx-prix (déjà gérée). */
main table:not(.bx-prix__matrix){
  display: block;
  max-width: 100%;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}
/* Sécurité : les conteneurs scrollables des blocs bx ne poussent pas la page.
   `min-width: 0` : sans lui un parent grid/flex refuse de rétrécir le
   conteneur sous la largeur de son contenu → c'est la PAGE qui déborde.
   `overscroll-behavior-x: contain` : le scroll de la matrice ne se chaîne
   pas vers le document (plus de « swipe qui emporte la page » sur mobile). */
.bx-prix__scroll,
[class^="bx-"] [class$="__scroll"],
[class^="bx-"] [class$="-scroll"]{
  max-width: 100%;
  min-width: 0;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  -webkit-overflow-scrolling: touch;
}

/* =========================================================
   HARMONISATION EYEBROWS + ÉTAPES (langage aéré)
   ========================================================= */
/* Les eyebrows de section (.pill) : plus de pilule arrondie → texte
   mono majuscule, comme les nouveaux blocs. (!important pour battre
   les règles de thème/scope existantes, purement cosmétique.) */
.pill{
  display: inline-block;
  background: transparent !important;
  border: 0 !important;
  padding: 0 !important;
  color: var(--brand-ink);
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .12em;
  text-transform: uppercase;
  line-height: 1.2;
}
[data-theme="light"] .pill{ color: var(--brand-ink); }


/* =========================================================================
   PRICING SHOWPIECE — AIRY ELEVATION
   Appended at the very end → equal-specificity rules WIN by source order.
   Re-declares existing selectors only. No HTML / class changes; tokens only.
   Shared by HOME (index.html) + /tarifs → styled to read great on both.
   Deliberately does NOT touch .price-card--featured::before / ::after so the
   "RECOMMANDÉ" badge and the top brand sheen from the earlier block stay live.
   Entrance is gated on .reveal (JS early-returns under reduced-motion / no-IO,
   so .reveal is never added → price stays visible) — never an unconditional
   opacity:0. Belt-&-suspenders reduced-motion guard included at the end.
   ========================================================================= */

/* ---- A) PRICE = the focal point ---------------------------------------
   The markup places <small>À PARTIR DE</small> then <b>999€</b> inside a
   .price-card__price that the base stylesheet pins absolute top/right. We
   un-pin it into the flow as its own hairline-separated zone at the foot of
   .price-card__info: a quiet mono micro-label sitting ABOVE a large mono
   figure. Column flow puts <small> above <b> automatically (DOM order). */
.price-card__price {
  position: static;                 /* was absolute top/right → now in flow */
  display: flex;
  flex-direction: column;           /* label above figure */
  align-items: flex-start;
  gap: 6px;
  margin-top: var(--space-s);
  padding-top: var(--space-s);
  border-top: 1px solid var(--line);/* crisp seam: info ↔ price */
}
/* micro-label: mono, uppercase, whisper-quiet */
.price-card__price small {
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: .14em;
  text-transform: uppercase;
  line-height: 1;
  color: var(--text-3);
}
.price-card--featured .price-card__price small { color: var(--brand-ink); }
/* the figure: large mono numeral with real presence */
.price-card__price b {
  font-family: var(--mono);
  font-size: clamp(34px, 4.6vw, 46px);
  font-weight: 700;
  line-height: 1;
  letter-spacing: -.03em;
  font-variant-numeric: tabular-nums;
  color: var(--text);
}

/* ---- B) Feature check glyph → theme-aware brand-ink hairline check -----
   The inline SVG hardcodes fill/stroke = var(--color-primary). Author CSS
   fill/stroke on the shapes overrides inline presentation attributes, so we
   retint the check to --brand-ink (the accent that stays legible in BOTH
   themes) while keeping the disc airy via its inline fill-opacity. */
.price-feature__ico circle { fill: var(--brand-ink); }
.price-feature__ico path   { stroke: var(--brand-ink); }
.price-card--featured .price-feature__ico circle { fill-opacity: .20; }

/* ---- C) Feature rows: reaffirm airy hairline list; highlight = brand ---
   (row hairlines/padding already set upstream; here we keep the highlight
   readable — a touch more weight + brand ink, no heavy underline.) */
.price-feature--highlight {
  font-weight: 600;
  text-decoration: none;
  letter-spacing: -.005em;
  color: var(--brand-ink);
}

/* ---- D) Tasteful entrance: price rises in with the card's cascade ------
   Mirrors the feature-row stagger already in place. Gated on .reveal only,
   so no-JS / reduced-motion (JS never adds .reveal) leaves it fully visible.
   Reveals on .is-visible (this site) AND .is-in (spec) — never fights the
   card-level reveal, just layers under it. */
.price-card.reveal .price-card__price {
  opacity: 0;
  transform: translateY(8px);
  transition: opacity .55s var(--ease), transform .55s var(--ease);
  transition-delay: .08s;
}
.price-card.reveal.is-visible .price-card__price,
.price-card.is-in .price-card__price {
  opacity: 1;
  transform: none;
}

/* ---- E) Mobile: keep the figure bold but overflow-safe at 380px -------- */
@media (max-width: 480px) {
  .price-card__price {
    margin-top: var(--space-xs);
    padding-top: var(--space-xs);
  }
  .price-card__price b { font-size: clamp(30px, 9vw, 40px); }
}

/* ---- F) Reduced-motion guard (belt & suspenders) ---------------------- */
@media (prefers-reduced-motion: reduce) {
  .price-card.reveal .price-card__price,
  .price-card.reveal.is-visible .price-card__price,
  .price-card.is-in .price-card__price {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
}

/* =========================================================
   ANIMATIONS SIGNATURE
   ========================================================= */
/* Shine premium au survol des CTA primaires (au repos : hors-champ, invisible) */
.btn--primary{ position: relative; overflow: hidden; }
.btn--primary::after{
  content:""; position:absolute; top:0; bottom:0; left:-80%; width:55%;
  background: linear-gradient(100deg, transparent, rgba(255,255,255,.40), transparent);
  transform: skewX(-18deg); pointer-events:none;
}
.btn--primary:hover::after{ animation: btnShine .8s var(--ease); }
@keyframes btnShine{ from{ left:-80%; } to{ left:135%; } }
@media (prefers-reduced-motion: reduce){ .btn--primary:hover::after{ animation:none !important; } }

/* =========================================================
   SUBHERO SHOWCASE — mini-produits par page + build-in animé
   Enrichissement additif : ne touche ni la position, ni l'overflow,
   ni le débordement voulu du showcase. Base toujours visible ;
   le mouvement ne s'ajoute que par-dessus (JS + no-preference).
   Tokens de thème uniquement.
   ========================================================= */

/* ---- A) Chaque variante devient un mini-produit reconnaissable ---------- */

/* PRICING — cartes = paliers tarifaires : prix en tête + lignes de features */
.subhero__mockup--pricing .mk-card { height: 96px; }
.subhero__mockup--pricing .mk-card::before {
  left: 14px; top: 16px; right: 42%; width: auto; height: 13px;
  border-radius: 99px; background: var(--color-primary); opacity: .9;
}
.subhero__mockup--pricing .mk-card::after {
  content: ""; position: absolute; left: 14px; right: 14px; bottom: 14px; height: 34px;
  border-radius: 6px;
  background: repeating-linear-gradient(var(--color-w-t5) 0 5px, transparent 5px 13px);
}
.subhero__mockup--pricing .mk-card:nth-child(2)::before { opacity: 1; right: 32%; }

/* PROCESS — étapes reliées : connecteur + pastille d'index + libellé */
.subhero__mockup--process .mk-row { position: relative; align-items: center; }
.subhero__mockup--process .mk-row::before {
  content: ""; position: absolute; left: 12%; right: 12%; top: 50%; height: 2px;
  transform: translateY(-50%); background: var(--color-primary-t3); z-index: 0;
}
.subhero__mockup--process .mk-card {
  z-index: 1; display: flex; align-items: center; gap: 8px; padding: 0 12px;
}
.subhero__mockup--process .mk-card::before {
  position: static; flex: 0 0 auto; width: 16px; height: 16px;
  border-radius: 50%; background: var(--color-primary-t5); border: 1px solid var(--color-primary-t3);
}
.subhero__mockup--process .mk-card::after {
  margin: 0; flex: 1; width: auto; height: 8px; border-radius: 99px; background: var(--color-w-t5);
}
.subhero__mockup--process .mk-card:first-child::before {
  background: var(--color-primary); border-color: var(--color-primary);
}

/* EDITORIAL — un article : image de une + signature (avatar + nom) */
.subhero__mockup--editorial .mk-card {
  height: 84px;
  background: linear-gradient(135deg, var(--color-primary-t5), transparent 70%);
}
.subhero__mockup--editorial .mk-card::before {
  left: 14px; top: 14px; width: 26px; height: 26px; border-radius: 7px;
  background: var(--color-primary-t3);
}
.subhero__mockup--editorial .mk-card::after {
  content: ""; position: absolute; right: 16px; top: 16px; width: 18px; height: 18px;
  border-radius: 50%; background: var(--color-primary);
}
.subhero__mockup--editorial .mk-cta {
  display: flex; align-items: center; gap: 8px;
  width: auto; height: 18px; margin-top: 10px;
  background: transparent; border-radius: 0; animation: none;
}
.subhero__mockup--editorial .mk-cta::before {
  content: ""; flex: 0 0 auto; width: 18px; height: 18px; border-radius: 50%;
  background: var(--color-primary-t5); border: 1px solid var(--color-primary-t3);
}
.subhero__mockup--editorial .mk-cta::after {
  content: ""; width: 90px; height: 9px; border-radius: 99px; background: var(--color-w-t5);
}

/* PROFILE — avatar vérifié + cartes de statistiques (chiffre + label) */
.subhero__mockup--profile .mk-tag { position: relative; }
.subhero__mockup--profile .mk-tag::after {
  content: ""; position: absolute; right: -1px; bottom: -1px; width: 16px; height: 16px;
  border-radius: 50%; background: var(--color-primary); border: 2px solid var(--surface-2);
}
.subhero__mockup--profile .mk-card {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px;
}
.subhero__mockup--profile .mk-card::before {
  position: static; width: 40%; height: 15px; border-radius: 5px;
  background: var(--color-primary); opacity: .9;
}
.subhero__mockup--profile .mk-card::after {
  content: ""; width: 60%; height: 7px; border-radius: 99px; background: var(--color-w-t5);
}

/* ONEPAGE — page longue : bandeau hero + sections empilées + scrollbar */
.subhero__mockup--onepage .mk-card { height: 150px; overflow: hidden; }
.subhero__mockup--onepage .mk-card::before {
  left: 0; top: 0; width: 100%; height: 42px; border-radius: 0;
  background: linear-gradient(135deg, var(--color-primary-t5), transparent 75%);
  border-bottom: 1px solid var(--surface-border);
}
.subhero__mockup--onepage .mk-card::after {
  content: ""; position: absolute; left: 14px; right: 14px; top: 56px; bottom: 14px;
  border-radius: 6px;
  background: repeating-linear-gradient(var(--color-w-t5) 0 6px, transparent 6px 22px);
}
.subhero__mockup--onepage .mk-row { position: relative; }
.subhero__mockup--onepage .mk-row::after {
  content: ""; position: absolute; top: 4px; bottom: 4px; right: 3px; width: 3px; z-index: 2;
  border-radius: 99px;
  background: linear-gradient(var(--color-primary-t3) 0 30%, var(--color-w-t5) 30% 100%);
}

/* REFONTE — comparatif avant/après : ancienne page atténuée, nouvelle + curseur */
.subhero__mockup--refonte .mk-card::before {
  left: 0; top: 0; width: 100%; height: 14px; border-radius: 0; background: var(--color-w-t5);
}
.subhero__mockup--refonte .mk-card:nth-child(2)::before { background: var(--color-primary-t3); }
.subhero__mockup--refonte .mk-row { position: relative; }
.subhero__mockup--refonte .mk-row::before {
  content: ""; position: absolute; left: 50%; top: -5px; bottom: -5px; width: 2px; z-index: 2;
  transform: translateX(-50%); background: var(--color-primary);
}
.subhero__mockup--refonte .mk-row::after {
  content: ""; position: absolute; left: 50%; top: 50%; width: 22px; height: 22px; z-index: 3;
  transform: translate(-50%, -50%); border-radius: 50%;
  background: var(--surface-2); border: 2px solid var(--color-primary);
}

/* ---- B) Build-in animé : le squelette s'assemble en cascade ------------- */
/* Transitions (pas d'animation) → les boucles ambiantes (caret/pulse/shimmer)
   restent intactes. État masqué uniquement si JS ET mouvement autorisé. */
@media (prefers-reduced-motion: no-preference) {
  .js .subhero__showcase .mk-hero > *,
  .js .subhero__showcase .mockup__screen > .mk-line {
    opacity: 0;
    transform: translateY(14px);
    transition: opacity .55s var(--ease), transform .55s var(--ease);
  }
  /* Cartes : opacité seule → les transforms de variante (carte mise en avant) sont préservées */
  .js .subhero__showcase .mk-row > .mk-card {
    opacity: 0;
    transition: opacity .55s var(--ease);
  }

  /* Séquence d'assemblage */
  .js .subhero__showcase .mk-hero > *:nth-child(1) { transition-delay: .08s; }
  .js .subhero__showcase .mk-hero > *:nth-child(2) { transition-delay: .16s; }
  .js .subhero__showcase .mk-hero > *:nth-child(3) { transition-delay: .24s; }
  .js .subhero__showcase .mk-hero > *:nth-child(4) { transition-delay: .32s; }
  .js .subhero__showcase .mk-row > .mk-card:nth-child(1) { transition-delay: .40s; }
  .js .subhero__showcase .mk-row > .mk-card:nth-child(2) { transition-delay: .48s; }
  .js .subhero__showcase .mk-row > .mk-card:nth-child(3) { transition-delay: .56s; }
  .js .subhero__showcase .mockup__screen > .mk-line:nth-child(3) { transition-delay: .62s; }
  .js .subhero__showcase .mockup__screen > .mk-line:nth-child(4) { transition-delay: .70s; }
  .js .subhero__showcase .mockup__screen > .mk-line:nth-child(5) { transition-delay: .78s; }

  /* État révélé */
  .subhero__showcase.is-built .mk-hero > *,
  .subhero__showcase.is-built .mockup__screen > .mk-line { opacity: 1; transform: translateY(0); }
  .subhero__showcase.is-built .mk-row > .mk-card { opacity: 1; }

  /* ---- C) Flottement ambiant très discret (desktop uniquement) ---- */
  @media (min-width: 761px) {
    .subhero__mockup { animation: subhero-float 7s var(--ease) infinite; }
  }
}
@keyframes subhero-float {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-6px); }
}

/* ---- Garde MOUVEMENT RÉDUIT : tout visible, aucune animation ------------ */
@media (prefers-reduced-motion: reduce) {
  .subhero__showcase .mk-hero > *,
  .subhero__showcase .mockup__screen > .mk-line {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
  .subhero__showcase .mk-row > .mk-card {
    opacity: 1 !important;
    transition: none !important;
  }
  .subhero__mockup { animation: none !important; }
}

/* =========================================================
   STEPS-PIN — scroll-pinned horizontal-progress stepper
   Generic driver for every [data-steps-pin]. Structure:
     .steps-pin            → tall track wrapper (creates the pin travel)
       .steps-pin__stage   → position:sticky, top = live header height
         .bx-methode-board → rail + fill + nodes + 4 phases (the board)
   JS sets --p (0→1) + the sticky top live each frame; CSS maps --p
   to the horizontal fill width. Fallback (<=900px / reduced motion):
   no pin — the board flows and stacks normally, fully visible.
   ========================================================= */
.steps-pin { position: relative; margin-top: 0; }
/* Pas de `top` ici : le driver écrit `stage.style.top = headerH + 'px'` en
   inline à chaque frame (script.js). Un `top: var(--steps-pin-top, 88px)`
   serait inopérant — et --steps-pin-top n'est défini nulle part. */
.steps-pin__stage { position: sticky; display: flow-root; }

/* La scène occupe la hauteur utile sous le header et répartit le vide en
   haut ET en bas (avant : tout le vide tombait sous les cartes, la scène
   semblait « pas assez haute »). --hh est posée par le driver (hauteur réelle
   du header sticky) ; le fallback couvre le cas où le JS n'a pas encore tourné.
   Bornée aux viewports où le pin est actif — cf. les media queries du driver. */
@media (min-width: 901px) and (min-height: 761px) {
  .js .steps-pin[data-steps-pin] .steps-pin__stage {
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: var(--sp-head-gap);
    min-height: calc(100vh - var(--hh, 88px));
  }
  /* En flex, la marge du header ferait doublon avec le `gap`. */
  .js .steps-pin[data-steps-pin] .steps-pin__head { margin-block: 0; }
}

/* La TÊTE DE SECTION vit DANS la scène : elle reste visible pendant tout le
   pin (on sait toujours de quel bloc on lit les étapes). Elle est donc
   comptée dans la hauteur de scène — voir le seuil `max-height` plus bas.
   Elle ouvre une sous-section séparée par un filet (celui porté par
   .bx-methode-board) : elle suit donc le rythme --sp-block / --sp-block-in
   comme tous les autres « filet + air » du site, pas un espacement ad hoc. */
.steps-pin__head { margin-block: 0 var(--sp-block); }
.steps-pin__head .bx-methode-eyebrow { margin-bottom: var(--sp-3); }
.steps-pin__head .bx-methode-h2      { margin-bottom: var(--sp-2); }
.steps-pin__head .bx-methode-lede    { max-width: var(--measure-lead); }
.steps-pin__head .bx-methode-solo    { margin-top: var(--sp-3); }
/* Aucune surcharge de font-size : `.bx-methode-h2` / `.bx-methode-lede`
   portent l'échelle du site, identique desktop et mobile. */

/* The scroll travel while pinned is created by an in-flow spacer AFTER the
   sticky stage — this extends the wrapper's CONTENT box so the sticky
   constraint rect has room. (Bottom padding does NOT work in Chromium: the
   sticky rect is clamped to the content box, so a padded wrapper never pins.)
   Gated on .js so the page has no dead space when scripting is off. */
.steps-pin__spacer { height: 0; }
.js .steps-pin[data-steps-pin] .steps-pin__spacer { height: 190vh; }

/* Le board n'ajoute plus de margin (c'est le head qui porte la couture au-
   dessus du filet) et son air sous le filet passe sur le token commun.
   Spécificité (0,2,0) : bat le `<style>` inline des pages (0,1,0) sans
   `!important`, malgré sa position postérieure dans la cascade. */
.steps-pin .bx-methode-board { margin-top: 0; padding-top: var(--sp-block-in); }

/* 2 CTA SOUS les étapes, FERRÉS À GAUCHE comme tout le reste du site
   (`.section`/`.section-head` sont en align-items:flex-start — les centrer
   ici recréerait la couture « deux designs greffés »).
   Ils sont HORS de .steps-pin (frère suivant), donc dans le flux normal :
   ils entrent d'eux-mêmes dans le champ à la FIN de la course du pin. Aucun
   mécanisme d'opacité — `opacity:0` ne retire pas les pointer-events, deux
   liens invisibles resteraient cliquables par-dessus le contenu. Et la scène
   reste courte, donc elle tient dans un viewport d'ordinateur portable. */
.steps-pin__ctas {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;   /* CENTRÉS sous les étapes (demande client) */
  gap: var(--sp-inline);
  margin-top: var(--sp-7);
}

/* Horizontal progress fill riding the spine, driven by --p on the stage. */
.steps-pin__fill {
  position: absolute;
  left: 0;
  top: -0.5px;
  height: 2px;
  width: calc(var(--p, 0) * 100%);
  background: var(--brand);
  border-radius: 2px;
  z-index: 0;
  will-change: width;
}
.bx-methode-rail .bx-methode-nodes { z-index: 1; }

/* The board list must never render "1./2." markers. */
.bx-methode-grid { list-style: none; margin: 0; padding: 0; }

/* FALLBACK — no pin: board flows / stacks and stays fully visible.
   Le seuil `max-height` vient d'une MESURE, pas d'une estimation :
   scène (head + board) = 643px au pire (901→1100px de large) et en-tête
   collant = 73px, soit 716px requis. En dessous de 760px de haut la scène
   dépasserait le viewport et son bas serait coupé → on ne pin pas.
   ⚠ Ces DEUX valeurs (900 / 760) sont dupliquées dans script.js (mqSmall /
   mqShort). Si l'une change, l'autre doit changer : un breakpoint CSS que le
   JS ignore met le spacer à 0 pendant que le driver reste en mode pin
   (travel ≈ 0 → --p bloqué à 0, les 4 étapes gèlent sur l'étape 1). */
@media (max-width: 900px), (max-height: 760px) {
  .js .steps-pin[data-steps-pin] .steps-pin__spacer { height: 0; }
  .steps-pin__stage { position: static; }
  .steps-pin__fill { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .js .steps-pin[data-steps-pin] .steps-pin__spacer { height: 0; }
  .steps-pin__stage { position: static; }
}

/* =========================================================
   SITE VIVANT — Couche d'animations pilotées par le scroll
   (append-only). Premium, GPU-only (transform/opacity),
   entièrement conditionnée à prefers-reduced-motion.
   Principe : enhancement pur. L'état de base est TOUJOURS
   pleinement visible ; ni JS ni scroll-timeline ne sont requis
   pour lire le contenu — le mouvement n'est qu'une surcouche.
   Un halo ambiant (.bg-orb) dérive déjà en JS sur toutes les
   pages : on ne le touche pas ici (conflit de transform inline).
   ========================================================= */

/* ---------- 1 · Parallaxe des halos ambiants (scroll-linked) ----------
   Les halos décoratifs dérivent doucement (≤40px d'amplitude), plus
   lentement que le scroll. Pilotage 100% CSS via scroll-timeline quand
   le navigateur le supporte ; sinon un rAF discret (script.js) prend le
   relais. transform-only, jamais width/height/top/left. */
@keyframes bv-hero-glow-drift {
  from { transform: translate(-50%, -18px); }
  to   { transform: translate(-50%, 18px); }
}
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    /* Halo du hero : dérive au fil du scroll racine (visible pendant le pin). */
    .hero__glow {
      animation: bv-hero-glow-drift linear both;
      animation-timeline: scroll(root block);
      will-change: transform;
    }
  }
}

/* ---------- 2 · Révélations directionnelles (alternance gauche/droite) ----------
   On superpose une légère direction (18px) à la révélation .is-in existante,
   en PRÉ-ÉTAT uniquement (:not(.is-in)) pour ne jamais figer l'état de repos.
   Gated .js + no-preference : sans JS ou en reduced-motion, aucun décalage.
   La cascade nth-child (transition-delay) et l'opacité restent pilotées par
   les règles existantes — on ne fait que composer le translateX au translateY.
   Spécificité (0,4,1) : bat les pré-états de base ET le scope sous-page. */
@media (prefers-reduced-motion: no-preference) {
  html.js .presta-card:nth-child(odd):not(.is-in)   { transform: translate(-18px, 14px); }
  html.js .presta-card:nth-child(even):not(.is-in)  { transform: translate(18px, 14px); }
  html.js .feature-card:nth-child(odd):not(.is-in)  { transform: translate(-18px, 14px); }
  html.js .feature-card:nth-child(even):not(.is-in) { transform: translate(18px, 14px); }
}

/* ---------- 3 · Accent scrollé : jauge budget (bx-prix, page /prix) ----------
   Un filet de marque se remplit de gauche à droite au fil du scroll, comme une
   valeur qui se révèle. Pseudo dédié (::before libre — le sweep ambiant utilise
   ::after), scaleX only, piloté par view(). N'existe QUE sous @supports +
   no-preference : sans support ou en reduced-motion, la jauge reste normale
   (aucun contenu masqué — c'est un élément décoratif purement additif). */
@keyframes bv-prix-fill {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    /* La timeline suit le RAIL (pas le filet de 2px), sinon la course serait
       quasi nulle : timeline nommée sur .bx-prix__track, consommée par ::before. */
    .bx-prix .bx-prix__track {
      view-timeline-name: --bv-prix-track;
      view-timeline-axis: block;
    }
    .bx-prix .bx-prix__track::before {
      content: "";
      position: absolute;
      left: 0; right: 0; bottom: 0;
      height: 2px;
      background: linear-gradient(90deg,
        color-mix(in srgb, var(--brand) 55%, transparent), var(--brand));
      transform-origin: left center;
      transform: scaleX(0);
      pointer-events: none;
      z-index: 3;
      animation: bv-prix-fill linear both;
      animation-timeline: --bv-prix-track;
      animation-range: cover 15% cover 78%;
      will-change: transform;
    }
  }
}

/* ---------- Garde-fou reduced-motion (obligatoire) ----------
   Redondant avec les gates no-preference ci-dessus, mais explicite : toute la
   couche « site vivant » est neutralisée, sans état intermédiaire ni élément
   masqué. Les halos reprennent leur transform de base (centrage conservé). */
@media (prefers-reduced-motion: reduce) {
  .hero__glow { animation: none !important; }
  html.js .presta-card:not(.is-in),
  html.js .feature-card:not(.is-in) { transform: none !important; }
  .bx-prix .bx-prix__track::before { content: none !important; animation: none !important; }
}


/* =========================================================================
   BEELY — SYSTÈME CENTRAL CONSOLIDÉ  (rév. 2)
   Rythme · Alignement · Séparateurs · Cards
   -------------------------------------------------------------------------
   Ce bloc REMPLACE (il ne s'ajoute pas) — suppressions faites dans le même
   commit, sinon rien n'est consolidé :
     styles.css  55-65    1re échelle --space-*            → réécrite ici
     styles.css  957-960  2e échelle --space-* (résiduelle) → SUPPRIMÉE
     styles.css  150-165  .section / .section-head          → réécrites ici
     styles.css  862-884  conic + borderSpin (price-card)   → SUPPRIMÉ
     styles.css  1126, 1169, 1270, 1298, 1348               → SUPPRIMÉS
     styles.css  1498-1511, 1605-1606, 1646-1673 (zone rouge) → SUPPRIMÉE
     styles.css  1859-1872 (padding bx + sous-blocs)        → réécrit ici
     styles.css  1963 (max-width feature-grid--2)           → SUPPRIMÉ
     styles.css  2385-2447 (::before sheen + priceSheen)    → SUPPRIMÉ
     12 pages    <style> inline : le motif « filet + air »  → SUPPRIMÉ du HTML
   Zéro !important. Aucun `~`, aucun `:has()`, aucun sélecteur d'attribut.
   ========================================================================= */


/* =========================================================================
   1. ÉCHELLE D'ESPACEMENT UNIQUE
   Base 4px, 8 crans. Les composants n'écrivent QUE les rôles sémantiques :
   c'est ce qui empêche la ré-apparition des 34 gaps sauvages.
   ========================================================================= */
:root {
  /* — crans bruts — */
  --sp-1: 4px;
  --sp-2: 8px;
  --sp-3: 12px;
  --sp-4: 16px;
  --sp-5: 24px;
  --sp-6: 32px;
  --sp-7: 48px;
  --sp-8: 64px;

  /* — rôles sémantiques : les SEULS à écrire dans les composants — */
  --sp-gutter:    clamp(20px, 3vw, 24px);   /* gouttière unique : header, section, bx-*, footer */
  --sp-section:   clamp(56px, 5vw, 80px);   /* padding-block unique de toute section */
  --sp-head-gap:  var(--sp-7);              /* tête de section → contenu */
  --sp-block:     var(--sp-8);              /* AU-DESSUS d'un filet de sous-section */
  --sp-block-in:  var(--sp-6);              /* EN DESSOUS du filet */
  --sp-card-gap:  var(--sp-3);              /* ⇦ LE gap de toutes les grilles de cards (12px) */
  --sp-card-pad:  clamp(20px, 2.4vw, 28px); /* padding interne d'une card encadrée */
  --sp-stack:     var(--sp-5);              /* rythme vertical d'une pile de texte */
  --sp-inline:    var(--sp-3);              /* gap horizontal boutons / chips */

  /* — hauteur du header sticky : LE point d'ancrage de tout `position:sticky`.
       Mesurée sur .site-header (59px desktop / 69px dès que la nav passe sur
       deux lignes). Remplace `var(--steps-pin-top, 88px)`, un token qui
       n'était déclaré nulle part et valait donc toujours son fallback. — */
  --header-h: 60px;

  /* — mesures de lecture (ferrage à gauche : on limite le TEXTE, jamais le conteneur) — */
  --measure-title: 46ch;
  --measure-lead:  62ch;

  /* — ALIAS de compatibilité : les ~86 usages de --space-* retombent dans le
       rythme sans être réécrits un à un. Ne PAS créer de 3e échelle. — */
  --space-4xs: var(--sp-1);
  --space-3xs: var(--sp-1);
  --space-2xs: var(--sp-2);
  --space-xs:  var(--sp-3);
  --space-s:   var(--sp-4);
  --space-m:   var(--sp-5);
  --space-l:   var(--sp-6);
  --space-xl:  var(--sp-7);
  --space-2xl: var(--sp-8);
  --space-3xl: var(--sp-8);
  --space-section: var(--sp-section);
}

/* Sous 900px la barre du header se réorganise et gagne ~10px : le token suit,
   sinon tout élément sticky se colle 10px trop haut. */
@media (max-width: 900px) { :root { --header-h: 70px; } }


/* =========================================================================
   2. GOUTTIÈRE UNIQUE
   Une seule mécanique de colonne : header, sections, blocs bx-*, footer.
   Tous les filets partagent le même x gauche et le même x droit.
   ========================================================================= */
.section,
.site-header__inner,
.site-footer__top,
.site-footer__bottom,
.footer-locations,
.mockup-band {
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--sp-gutter);
}
/* `.footer-locations` porte son propre filet : il doit partir de la même
   colonne que les autres, sinon on voit deux gouttières dans le footer. */
.footer-locations { padding-block: var(--sp-6) 0; }

/* Les wrappers internes hérités ne re-gouttièrent plus (sinon 24 + 24)
   et ne rétrécissent plus la colonne (sinon les filets se décalent). */
.section > .container,
.section > .pricing__inner,
.section > .portfolio__inner,
.section > .faq__inner,
.section > .logos__inner,
.section > .contact__inner,
.section > .cta__inner,
.section > .section-body {
  width: 100%;
  max-width: none;
  margin-inline: 0;
  padding-inline: 0;
}

/* Garde-fou scroll horizontal. `clip` (et non `hidden`) : aucun conteneur
   de défilement créé, donc position:sticky (scroll-pin des étapes) intact. */
html,
body,
main { overflow-x: clip; }


/* =========================================================================
   3. SECTION — rythme vertical et alignement
   UN seul padding-block pour toutes les sections : plus de coutures à
   140 / 170 / 174px sur la même page.
   ========================================================================= */
.section,
.site-footer {
  padding-block: var(--sp-section);
}

/* --- Correctif gouttière (header / footer / subhero) ---------------------
   Le header (styles.css:283) et le footer (:711) portaient un padding-inline
   var(--space-m) sur le PARENT, EN PLUS de la gouttière --sp-gutter portée par
   leurs enfants (.site-header__inner, .site-footer__top/__bottom, .footer-locations)
   → double gouttière : alignés à 1440 par le seul jeu du centrage, mais décalés
   de ~24px sous 1366px (header/footer à 48, sections à 24). On retire le padding
   horizontal du parent : seuls les enfants gouttièrent, à la même colonne que
   les sections, à TOUTES les largeurs. */
.site-header { padding-inline: 0; }
.site-footer { padding-inline: 0; }

/* Le subhero utilisait `width: min(100% - 2*space-m, site-max)` + padding-left 0
   → ferré à 37px @1440 au lieu de 61 (pas de gouttière gauche). On l'aligne sur
   la mécanique des sections : largeur pleine plafonnée à --maxw, centrée, plus la
   gouttière à gauche. Le padding-droit (réservé au mockup en position:absolute)
   et le padding-bloc sont conservés. */
main > .subhero {
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
  padding-left: var(--sp-gutter);
}

.section {
  display: flex;
  flex-direction: column;
  align-items: flex-start;   /* ⇦ FERRÉ À GAUCHE par défaut */
  position: relative;
}

/* Chaque enfant occupe la colonne : le ferrage vient de l'alignement du
   TEXTE, pas d'une largeur réduite qui recentrerait le bloc. */
.section > * { width: 100%; }

/* --- RÈGLE D'OR : UN SEUL PROPRIÉTAIRE D'ESPACE PAR COUTURE ---------------
   L'espace vertical appartient au parent, via cette unique règle adjacente.
   Les composants ne portent plus de margin verticale : plus rien ne
   s'additionne, et rien ne peut collapser (contexte flex). */
.section > * { margin-block: 0; }
.section > * + * { margin-top: var(--sp-head-gap); }

/* Un lien de rappel se rattache à ce qui le précède : couture courte. */
.section > .section-more { margin-top: var(--sp-6); }

/* Décoratifs positionnés : ils ne sont ni dans la colonne ni dans le rythme. */
/* `.faq__glow` retiré : le halo était imbriqué DANS `.faq__inner`, donc ce
   sélecteur enfant-direct ne l'a jamais atteint — et l'<img> a été supprimé
   des 26 pages avec la refonte du bloc FAQ. */
.section > .hero__glow { width: auto; margin-top: 0; }


/* =========================================================================
   4. TÊTE DE SECTION — ferrée à gauche, une seule fois
   Remplace styles.css:158-165 ET les 5 surcharges contradictoires.
   Le ferrage à gauche s'obtient en SUPPRIMANT les annulations.
   ========================================================================= */
.section-head {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--sp-3);
  max-width: none;      /* ⇦ tuer var(--width-l) qui recentrait le bloc */
  text-align: left;
}

/* On limite la MESURE du texte, pas la largeur du conteneur. */
.section-head .h2,
.section-head h2 { max-width: var(--measure-title); letter-spacing: -.02em; }
.section-head .lead,
.section-head p  { max-width: var(--measure-lead); }

/* Corps de section : même logique, aucune margin propre. */
.section-body {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--sp-6);
  width: 100%;
  text-align: left;
}

/* Wrappers hérités qui contiennent une tête + un contenu empilés : ils
   deviennent eux aussi des colonnes rythmées, sinon `.section > * + *` ne
   se déclenche jamais (la tête est imbriquée) et la tête colle au contenu.
   `.faq__inner` est VOLONTAIREMENT exclu : c'est une grille 2 colonnes
   (intro à gauche / liste à droite), la passer en flex-column casserait la
   FAQ. Son gap propre assure déjà la couture. */
.section > .pricing__inner,
.section > .portfolio__inner,
.section > .logos__inner,
.section > .contact__inner,
.section > .container {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--sp-head-gap);
  text-align: left;
}
/* .portfolio__filters n'est plus listé ici : plus rien ne le centre depuis
   que son bloc est consolidé, cette dé-centrage était devenu une surcharge
   sans cible. .linklist, lui, est encore centré l.1435. */
.linklist { justify-content: flex-start; }

/* Le CTA de fin de page est la seule occurrence prévue.
   Il reste PLEINE LARGEUR : son halo (`.section.cta::before{inset:0}`) doit
   couvrir toute la fenêtre. La colonne de lecture est reconstituée par le
   padding, pas par une max-width — sinon le halo se coupe net à 1366px.
   (Pas de 100vw : cela déborderait de la largeur de la barre de défilement.) */
.section.cta {
  align-items: center;
  max-width: none;
  /* même colonne que les autres sections : demi-marge + gouttière, donc le
     texte du CTA démarre exactement au même x que le reste de la page. */
  padding-inline: max(var(--sp-gutter), (100% - var(--maxw)) / 2 + var(--sp-gutter));
  /* le CTA suit le rythme commun (il portait 104px/150px : couture visible). */
  padding-block: var(--sp-section);
}
.section.cta .cta__inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-5);
  text-align: center;
}
.section.cta .cta__text { max-width: var(--measure-lead); margin-inline: auto; }
.section.cta .cta__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-inline);
  justify-content: center;
}


/* =========================================================================
   6. SÉPARATEURS — une règle, un filet, jamais deux
   Le filet est porté par l'élément QUI COMMENCE, sur son border-top : il
   partage son bord avec l'espacement, impossible de désynchroniser.
   ========================================================================= */

/* 6a — LE filet inter-sections. Source de vérité unique, home ET sous-pages. */
.section + .section { border-top: 1px solid var(--line); }

/* 6b — AUCUN autre porteur de filet de section : on neutralise les traits
        orphelins ou en doublon.
        Le border-top du `.site-footer` (styles.css:614) est CONSERVÉ : c'est
        le seul séparateur de fin de page. Il est simplement retokenisé en
        var(--line) pour s'aligner sur les autres filets. */
.cta__inner--banner,
.section--intro,
.mockup-band { border-top: 0; border-bottom: 0; }

/* 6c — Les porteurs de filet ne prennent JAMAIS de max-width locale, sinon
        les traits ne se répondent pas verticalement. */
.feature-grid,
.feature-grid--2,
.feature-grid--4,
.stats,
.check-list { max-width: none; }   /* .faq__list : déclaré dans le bloc FAQ unique */

/* 6d — Sous-sections des blocs bx-* : motif « filet + air » écrit UNE fois,
        sur des classes EXPLICITES (plus de sélecteur d'attribut).
        Remplace styles.css:1866-1872, où margin-top(89) + padding-top(44)
        s'additionnaient à 133px avec 3 rythmes différents — et où
        `[class$="-sub"]` frappait aussi `.bx-methode-sub`, un simple
        paragraphe de chapô, qui héritait d'un filet et de 90px d'air.
        Les mêmes déclarations ont été retirées des <style> inline des pages :
        sans cela, ces règles-ci seraient des no-op (spécificité égale,
        position postérieure dans la cascade). */
.bx-why__part,
.bx-prix__part,
.bx-apropos-sub,
.bx-refonte-sub,
.bx-seo-sub,
.bx-mobile-sub,
.bx-compare-sec,
.bx-vitrine-sec,
.bx-vitrine-for,
.bx-methode-timeline {
  margin-top: var(--sp-block);
  padding-top: var(--sp-block-in);
  border-top: 1px solid var(--line);
}

/* Le premier de la série n'ouvre pas sur un filet orphelin. */
.bx-part:first-child,
.bx-why__part:first-child,
.bx-prix__part:first-child,
.bx-apropos-sub:first-child,
.bx-refonte-sub:first-child,
.bx-seo-sub:first-child,
.bx-mobile-sub:first-child,
.bx-compare-sec:first-child,
.bx-vitrine-sec:first-child,
.bx-vitrine-for:first-child,
.bx-methode-timeline:first-child {
  margin-top: 0;
  padding-top: 0;
  border-top: 0;
}

/* `.bx-vitrine-for` reste une ligne de chips : le motif ne doit pas casser
   sa mise en page horizontale. */
.bx-vitrine-for { display: flex; flex-wrap: wrap; align-items: baseline; gap: 10px 22px; }/* 6e — Les sections qui portent un bloc bx-* suivent --sp-section comme les
        autres : plus de padding spécial,
et `.section > *{width:100%}` (§3)
        couvre déjà leur largeur. Rien à écrire ici. */


/* =========================================================================
   7. CARDS — gaps resserrés et homogènes
   Hier : 24 / 32 / 43.2 / 52 / 34 / 44px. Aujourd'hui : un seul token.
   Resserrer tout le site de 16 à 12px = UNE ligne (--sp-card-gap).
   ========================================================================= */
.pricing__grid,
.pricing__grid--comparison,
.presta__grid,
.feature-grid,
.feature-grid--2,
.feature-grid--4,
.check-list,
.accordion-item__images {
  display: grid;
  gap: var(--sp-card-gap);   /* ⇦ row ET column : plus de 52/43.2 dissymétrique */
  width: 100%;
  padding: 0;                /* ⇦ tue le padding-top:40px de .feature-grid */
  margin: 0;
  list-style: none;
}/* Colonnes — `min(…,
100%)` sur CHAQUE piste : à 320px de viewport la boîte
   de contenu fait 280px,
une piste rigide de 280/300px déborderait et
   overflow-x:clip rognerait le contenu au lieu de le reflower. */
.presta__grid,
.feature-grid { grid-template-columns: repeat(auto-fit, minmax(min(clamp(240px, 24vw, 300px), 100%), 1fr)); }
.feature-grid--2,
.check-list          { grid-template-columns: repeat(auto-fit, minmax(min(280px, 100%), 1fr)); }
.feature-grid--4     { grid-template-columns: repeat(auto-fit, minmax(min(200px, 100%), 1fr)); }
.pricing__grid,
.pricing__grid--comparison { grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr)); }

/* Padding interne unique de toute card encadrée : le gap se resserre, l'air
   passe À L'INTÉRIEUR — la respiration reste, les cartes se rapprochent. */
.feature-card,
.presta-card,
.price-card {
  padding: var(--sp-card-pad);
  border: 1px solid var(--line);
  border-radius: var(--r-m);
  background: var(--panel);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--sp-3);
  text-align: left;
  margin: 0;      /* aucune card ne pousse sa voisine */
}

/* 7b — Carte « Expert » : liseré COMPLET, les 4 côtés.
        Le liseré ne faisait plus le tour parce qu'il avait été réduit à un
        trait de 2px en haut (::before) posé PAR-DESSUS la bordure : double
        filet haut, rien sur les 3 autres côtés.
        Ici : un anneau dégradé de 1px obtenu par masque (la padding-box est
        exclue de la border-box). Statique → zéro repaint permanent.
        · le rayon suit celui de la card : .price-card est en var(--r-m),
          l'anneau doit donc être en calc(var(--r-m) + 1px) — pas --r-l.
        · les stops sont OPAQUES : --brand-t2 (alpha .13/.22) rendait
          l'anneau quasi invisible au milieu du dégradé, et --brand-2 est un
          hex codé en dur sans variante claire. color-mix garde le contraste
          dans les deux thèmes. */
.price-card--featured {
  border-color: transparent;   /* la bordure est remplacée par l'anneau */
  transition: background-color .3s var(--ease);
}
.price-card--featured::before {
  content: "";
  position: absolute;
  inset: -1px;                 /* épouse la border-box sur les 4 côtés */
  z-index: 1;
  padding: 1px;                /* = épaisseur du liseré */
  border-radius: calc(var(--r-m) + 1px);
  pointer-events: none;
  background: linear-gradient(140deg,
              var(--brand) 0%,
              color-mix(in srgb, var(--brand) 55%, var(--panel)) 50%,
              var(--brand) 100%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask-composite: exclude;
}
.price-card--featured:hover { border-color: transparent; background: var(--brand-t2); }
/* Le badge « RECOMMANDÉ » passe au-dessus de l'anneau, le contenu au-dessus des deux. */
.price-card--featured::after { z-index: 3; }
.price-card > *              { position: relative; z-index: 2; }

/* 7c — FAQ : voir le bloc « FAQ — accordéon aéré » (source unique).
        L'ancienne surcharge posait padding-block:24px sur .faq__item alors
        que .faq__q portait déjà padding:20px : 44px d'air par question au
        lieu de 24. Les deux couches sont fusionnées en une seule. */


/* =========================================================================
   8. ACCESSIBILITÉ — focus visible sur un site majoritairement sans bordure
   Pas de `border-radius` ici : il s'appliquerait à l'ÉLÉMENT, pas à
   l'outline, et écraserait le rayon pill d'un `.btn` ou d'un chip au clavier.
   L'outline suit déjà le rayon propre de l'élément.
   ========================================================================= */
:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
}
/* On ne retire jamais l'outline sans le remplacer. */
a:focus-visible,
button:focus-visible,
summary:focus-visible,
[tabindex]:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }


/* =========================================================================
   9. MOBILE — le rythme se compresse, il ne change pas de nature
   Un seul palier, mêmes règles, valeurs plus courtes. Aucun re-centrage.
   ========================================================================= */
@media (max-width: 760px) {
  :root {
    --sp-head-gap: var(--sp-6);   /* 48 → 32 */
    --sp-block:    var(--sp-7);   /* 64 → 48 */
    --sp-block-in: var(--sp-5);   /* 32 → 24 */
  }
  .section { align-items: flex-start; }
  .section-head { align-items: flex-start; text-align: left; }

  /* TOUTES les grilles passent en colonne unique — y compris celles qui
     gardaient une piste de 280/300px et débordaient à 320px. */
  .pricing__grid,
  .pricing__grid--comparison,
  .presta__grid,
  .feature-grid,
  .feature-grid--2,
  .feature-grid--4,
  .check-list,
  .accordion-item__images { grid-template-columns: 1fr; }
}

/* =========================================================================
   PERFORMANCE — animations infinies hors écran
   Cf. le bloc « ANIMATIONS INFINIES » de script.js. `.anim-idle` est posée
   par un IntersectionObserver quand l'élément quitte le champ (+200px de marge).
   ========================================================================= */
.js .anim-idle,
.js .anim-idle * { animation-play-state: paused !important; }

/* Le marquee anime `transform` en boucle sur des rangées qui contiennent
   des logos sous `filter`. Sans promotion explicite, chaque frame repeint
   les images filtrées ; avec, la rangée est rasterisée une fois puis
   simplement composée. `will-change` est légitime ici : l'animation est
   permanente tant que la rangée est visible. */
.marquee__row { will-change: transform; }
.marquee__row.anim-idle { will-change: auto; }

/* =========================================================================
   PROSE ÉDITORIALE — refonte airée (remplace le rendu « article de blog brut »)
   S'applique à toutes les sections .prose (27 pages de contenu long). Appended
   en fin de feuille pour gagner la cascade sur les anciennes règles .prose.
   Objectif : mêmes codes que les blocs bx-* — ferré à gauche, chapitres séparés
   par un filet fin avec un index mono, typo travaillée, une seule couleur
   d'accent, tokens theme-aware (rendu correct en clair ET en sombre).
   ========================================================================= */
main { counter-reset: prose-ch; }   /* compteur continu par page (pas de « 01 » dupliqué) */

.prose {
  max-width: 74ch;
  margin-inline: 0;                 /* ferré à gauche, aligné aux eyebrows */
  padding-left: 0;
  border-left: 0;                   /* neutralise l'ancien filet bleu */
  display: flex;
  flex-direction: column;
  gap: clamp(15px, 1.5vw, 19px);
  text-align: left;
  color: var(--text-2);
}

/* Accroche : premier paragraphe / .prose__lead — plus grand, couleur pleine */
.prose > .prose__lead,
.prose:first-of-type > p:first-child {
  color: var(--text);
  font-size: clamp(1.08rem, 1.5vw, 1.26rem);
  line-height: 1.62;
  letter-spacing: -0.01em;
}

/* Chapitre : chaque h2 ouvre une section, précédée d'un filet fin et d'un
   index mono numéroté (compteur continu dans la section-body). */
.prose > h2 {
  counter-increment: prose-ch;
  margin-top: clamp(46px, 6vw, 72px);
  padding-top: clamp(26px, 3.4vw, 40px);
  border-top: 1px solid var(--line);
  font-size: clamp(1.5rem, 2.9vw, 2.05rem);
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.14;
  color: var(--text);
  text-wrap: balance;
}
.prose > h2::before {
  content: counter(prose-ch, decimal-leading-zero);
  display: block;
  margin-bottom: 14px;
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 400;
  letter-spacing: 0.08em;
  color: var(--brand-ink);
}
/* Le tout premier chapitre de la section ne porte pas de filet (rien au-dessus). */
.section-body > .prose:first-child > h2:first-child,
.prose:first-of-type > h2:first-child {
  margin-top: 0;
  padding-top: 0;
  border-top: 0;
}

/* Sous-titre */
.prose h3 {
  margin-top: clamp(22px, 2.6vw, 30px);
  font-size: 1.16rem;
  font-weight: 700;
  letter-spacing: -0.01em;
  line-height: 1.3;
  color: var(--text);
}

.prose p {
  color: var(--text-2);
  font-size: 1.02rem;
  line-height: 1.72;
  max-width: 68ch;
}
.prose strong { color: var(--text); font-weight: 600; }
.prose a { color: var(--brand-ink); font-weight: 500; text-underline-offset: 3px; }
.prose a:hover { text-decoration: underline; }

/* Listes : puce fine alignée sur la ligne de base */
.prose ul { display: flex; flex-direction: column; gap: 11px; margin: 2px 0; }
.prose li {
  position: relative;
  padding-left: 26px;
  color: var(--text-2);
  line-height: 1.6;
  max-width: 66ch;
}
.prose li::before {
  content: "";
  position: absolute;
  left: 2px;
  top: 0.6em;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--brand);
}

/* =========================================================================
   CTA DE MILIEU D'ARTICLE — relance de conversion sur les pages longues.
   Injectée par script.js après le 2e chapitre des pages à article long (celles
   qui font beaucoup lire avant la CTA finale). Style airé, ferré à gauche, borné
   par des filets fins — pas d'encadrement (cohérent avec la DA).
   ========================================================================= */
.prose-cta {
  max-width: 74ch;
  margin-block: clamp(38px, 5vw, 60px);
  padding-block: clamp(22px, 3vw, 30px);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 18px 28px;
}
.prose-cta__txt { display: flex; flex-direction: column; gap: 5px; }
.prose-cta__title {
  color: var(--text);
  font-weight: 700;
  font-size: 1.16rem;
  letter-spacing: -0.01em;
}
.prose-cta__sub { color: var(--text-2); font-size: 0.96rem; line-height: 1.5; max-width: 46ch; }
.prose-cta .btn { flex: none; }
@media (max-width: 560px) {
  .prose-cta { align-items: flex-start; }
  .prose-cta .btn { width: 100%; justify-content: center; }
}

/* =========================================================================
   PROSE — le bloc occupe TOUTE la colonne, seul le TEXTE est borné.
   Avant : `.prose` était plafonnée à 760px dans une colonne de ~1318px (58 %),
   d'où l'impression que la section ne faisait que la moitié de la page. On
   libère le conteneur (les filets de chapitre traversent donc toute la colonne,
   comme les séparateurs des autres sections) et on garde une mesure de lecture
   confortable sur les éléments de texte uniquement.
   ========================================================================= */
main > .subhero ~ .section .prose,
.section-body .prose,
.prose {
  max-width: none;
}
.prose > p,
.prose > ul,
.prose > ol,
.prose > h3,
.prose > .prose__lead { max-width: 72ch; }
.prose > h2 { max-width: none; }

/* Le montant est en chiffres tabulaires MONOSPACE : l'espace de séparation des
   milliers y occupe la largeur d'un chiffre, ce qui donnait « 2␣␣␣499€ ».
   On la resserre pour retrouver une espace fine typographique. Sans effet sur
   les montants sans séparateur (999€). */
.price-card__price b { word-spacing: -0.38em; }

/* Les cartes de prix passent côte à côte dès ~720px : à 900px de viewport
   chacune ne fait plus que ~420px, et la liste en 2 colonnes y tombe à ~200px
   par colonne — les libellés se cassent sur 4 lignes. On replie la liste sur
   une colonne tant que la carte reste étroite (le repli n'existait qu'à 720px). */
@media (max-width: 1024px) {
  .price-features--cols { grid-template-columns: 1fr; }
}

/* =========================================================================
   PROSE — chapitres en 2 colonnes (titre à gauche, corps à droite).
   Les .prose-ch sont construits par script.js à partir du markup plat.
   Objectif : occuper toute la colonne. Avant, le texte plafonnait à ~700px
   dans une colonne de ~1300px, laissant la moitié droite vide.
   ========================================================================= */
.prose-ch { display: grid; grid-template-columns: 1fr; }
.prose-ch__body { display: flex; flex-direction: column; gap: clamp(15px, 1.5vw, 19px); }

/* Le filet de chapitre appartient désormais au chapitre entier (il traverse
   les deux colonnes) et non plus au seul h2. */
.prose-ch {
  margin-top: clamp(46px, 6vw, 72px);
  padding-top: clamp(26px, 3.4vw, 40px);
  border-top: 1px solid var(--line);
}
.prose > .prose-ch:first-child { margin-top: 0; padding-top: 0; border-top: 0; }
.prose .prose-ch > .prose-ch__head > h2 {
  margin-top: 0;
  padding-top: 0;
  border-top: 0;
}

@media (min-width: 1000px) {
  .prose-ch {
    grid-template-columns: minmax(0, 0.62fr) minmax(0, 1fr);
    gap: clamp(36px, 3.6vw, 64px);
    align-items: start;
  }
  /* Le titre reste visible pendant la lecture du chapitre. */
  .prose-ch__head { position: sticky; top: calc(var(--header-h, 72px) + 24px); }
  /* Dans la colonne de droite, le texte occupe toute sa largeur : c'est la
     colonne elle-même qui borne désormais la mesure de lecture. */
  .prose-ch__body > p,
  .prose-ch__body > ul,
  .prose-ch__body > ol,
  .prose-ch__body > h3 { max-width: none; }
}

/* Le h2 n'est plus enfant direct de .prose (il vit dans .prose-ch__head) :
   les sélecteurs `.prose > h2` ne matchaient plus et le titre perdait sa
   typographie ainsi que son index de chapitre. On re-cible. */
.prose .prose-ch__head > h2 {
  counter-increment: prose-ch;
  margin: 0;
  padding: 0;
  border-top: 0;
  font-size: clamp(1.5rem, 2.9vw, 2.05rem);
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.14;
  color: var(--text);
  text-wrap: balance;
  max-width: none;
}
.prose .prose-ch__head > h2::before {
  content: counter(prose-ch, decimal-leading-zero);
  display: block;
  margin-bottom: 14px;
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 400;
  letter-spacing: 0.08em;
  color: var(--brand-ink);
}

/* La CTA de milieu d'article sépare deux chapitres : elle doit traverser toute
   la colonne, comme les filets, et non rester à la mesure de lecture. */
.prose > .prose-cta { max-width: none; }

/* La check-list empilait 4 items courts sur une colonne bornée à 60ch (~567px),
   avec ~750px de vide à droite (même défaut que les chapitres de prose). Sur
   grand écran elle passe en 2 colonnes PLEINE largeur : on lève d'abord le
   plafond `max-width:60ch` posé plus haut (sinon les 2 colonnes ne font que
   couper 567px en deux). Les items restent courts : lecture préservée, page
   raccourcie, largeur occupée. */
/* Entre 640 et 1000px : 1 colonne mais pleine largeur (on lève le plafond 60ch
   qui laissait du vide à droite ; les items sont un peu plus longs ici, 1 colonne
   large se lit mieux que 2 serrées). */
@media (min-width: 640px) {
  main > .subhero ~ .section .check-list,
  .check-list { max-width: none; }
}
/* Au-delà de 1000px : 2 colonnes pleine largeur. */
@media (min-width: 1000px) {
  main > .subhero ~ .section .check-list,
  .check-list {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: clamp(32px, 3.5vw, 64px);
  }
}

/* =========================================================================
   THÈME CLAIR — délimitation des cartes.
   En clair, price-card / presta-card / feature-card avaient un fond identique
   à la page (contraste 1.000) et feature-card n'avait aucune bordure → cartes
   qui se fondent dans le fond. On leur donne une présence subtile (bordure fine
   + ombre douce façon Stripe), SANS toucher aux tokens globaux (--color-d2 /
   --surface-border piloteraient header/footer/mockups → effets de bord). Scopé
   [data-theme="light"] uniquement ; la carte « Expert » vedette garde son liseré.
   ========================================================================= */
[data-theme="light"] .price-card:not(.price-card--featured),
[data-theme="light"] .presta-card,
[data-theme="light"] .feature-card {
  border: 1px solid rgba(20, 24, 31, 0.09);
  box-shadow: 0 1px 2px rgba(20, 24, 31, 0.04), 0 10px 28px -12px rgba(20, 24, 31, 0.10);
}

/* =========================================================================
   MAILLAGE INTERNE LOCAL — chips de liens vers villes proches / métiers liés,
   sur les 19 landing pages. Style aéré (DA), pastilles à filet fin.
   ========================================================================= */
.maillage__links {
  display: flex;
  flex-wrap: wrap;
  gap: 12px 14px;
  list-style: none;
  padding: 0;
  margin-top: var(--sp-6);
}
.maillage__links a {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 11px 17px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  font-size: var(--fs-s);
  color: var(--text);
  transition: border-color 0.2s var(--ease), color 0.2s var(--ease);
}
.maillage__links a:hover,
.maillage__links a:focus-visible { border-color: var(--brand); color: var(--brand-ink); }
.maillage__links svg { color: var(--text-3); transition: transform 0.2s var(--ease), color 0.2s var(--ease); flex: none; }
.maillage__links a:hover svg { transform: translateX(2px); color: var(--brand-ink); }
