/* ==========================================================================
   CMC 6 : système visuel
   Réplique du site live cmc6.fr (Webflow) : fond papier clair pour le
   contenu, coque hero + nav + bandeau logos + footer restent sombres
   (aurore magenta/crimson authentique, cf. --shape-*), CTA crimson plein.
   Display : Bricolage Grotesque. Body : Hanken Grotesk.
   ========================================================================== */

/* Euclid Circular A : police de corps du site, servie depuis ce domaine.
   POURQUOI ELLE A DEMENAGE. Elle etait encore chargee depuis le CDN de Webflow
   (cdn.prod.website-files.com), sur un compte arrete le 2026-08-14. Deux problemes en
   un : le jour ou Webflow purge ces fichiers, la typographie du site disparait sans
   prevenir ; et jusque-la, l'adresse IP de chaque visiteur partait chez un tiers
   americain avant toute action de sa part. Les quatre graisses ont ete recuperees le
   2026-08-19, avant coupure. Fichiers dans public/fonts/.
   LICENCE : Euclid Circular A est une police commerciale (Swiss Typefaces). Elle avait
   ete televersee sur Webflow, ce qui suppose une licence webfont acquise ; l'auto-
   hebergement entre dans le meme usage. A verifier aupres de l'operateur. */
@font-face {
  font-family: "Euclid Circular A";
  src: url("/fonts/euclid-circular-a-light.woff2") format("woff2");
  font-weight: 300;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Euclid Circular A";
  src: url("/fonts/euclid-circular-a-regular.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Euclid Circular A";
  src: url("/fonts/euclid-circular-a-medium.woff2") format("woff2");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Euclid Circular A";
  src: url("/fonts/euclid-circular-a-semibold.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}

/* ---------- Tokens ---------- */
:root {
  /* Reglages du cadre de verre, pilotables en direct par le panneau de
     tuning (?tune=1, voir tune-panel.js) : les valeurs par defaut ici sont
     celles committees, le panneau les surcharge en session sans y toucher. */
  --glass-radius: 146px;
  --glass-blur: 4px;
  /* 210 % : reglee par l'operateur sur /charte. La saturation compense la
     teinte sombre posee sur le cadre, qui eteindrait sinon les couleurs de
     l'aurore vues au travers. */
  --glass-saturate: 210%;
  --glass-max-width: 1600px;
  --glass-gutter: 50px;

  /* Fonds & textes PAR DEFAUT : clair-sur-sombre PARTOUT, comme sur cmc6.fr
     ou l'aurore (.page-aurora, fixe) reste visible derriere TOUTE la page,
     du hero jusqu'au footer (verifie en navigateur reel, captures a plusieurs
     profondeurs de scroll : le fond magenta/violet est present sur toute la
     hauteur, ce n'est PAS confine au hero). Il n'y a plus de zone "papier
     clair" par defaut : seuls des composants ponctuels (cartes chiffres,
     slider temoignages) portent leur propre fond clair local, pas la section. */
  --night: #171022;
  --night-deep: #100b18;
  --night-raised: #221a30;
  --cream: #f7f2f7;
  --lavender: #c3b4cd;
  --lavender-dim: #93829f;
  --lavender-08: rgba(195, 180, 205, 0.08);
  --lavender-15: rgba(195, 180, 205, 0.15);
  --lavender-30: rgba(195, 180, 205, 0.3);

  --ember: #f61942;
  --ember: oklch(60% 0.235 18);
  --magenta: #b81e8c;
  --magenta: oklch(52% 0.26 330);
  --violet: #4a2d6e;
  --violet: oklch(35% 0.12 295);

  /* Beige clair (texte sur fond sombre dans le live, fond de carte ici) */
  --beige: #fff4f0;

  /* Palette authentique des formes du fond (relevée sur cmc6.fr) */
  --shape-crimson: #f61942;
  /* Valeur EXACTE relevee via getComputedStyle sur cmc6.fr (pas le panneau
     Designer qui affichait "mauve cmc" #7474ff pour un swatch different et
     non visible - celui-ci, #7979c9, est la vraie couleur du degrade de
     shape-3 ET du fond plat de shape-5). */
  --shape-periwinkle: #7979c9;
  --shape-lilac: #b5a3d6;
  --shape-sky: #7fb5e5;
  --shape-navy: #24245a;

  --ember-glow: rgba(232, 39, 95, 0.35);
  --ember-glow: oklch(58% 0.24 0 / 0.35);

  /* Gris du footer live (bandeau uni, ni noir ni clair) */
  --footer-bg: #6f6d78;
  --footer-bg-deep: #5c5a64;

  /* Typographie (identique au live : Cal Sans pour les titres, Euclid
     Circular A pour le corps ; cf. webflow.css ligne 2049 et suivantes) */
  --font-display: "Cal Sans", "Arial Black", sans-serif;
  --font-body: "Euclid Circular A", Arial, sans-serif;
  --text-hero: clamp(2.5rem, 6.5vw + 0.5rem, 5.25rem);
  --text-h2: clamp(2rem, 3.2vw + 0.5rem, 3.25rem);
  --text-h3: 1.375rem;
  --text-lead: 1.1875rem;
  --text-body: 1.0625rem;
  --text-label: 0.8125rem;

  /* Espacement (base 4pt) */
  --s-2: 0.5rem;
  --s-3: 0.75rem;
  --s-4: 1rem;
  --s-6: 1.5rem;
  --s-8: 2rem;
  --s-12: 3rem;
  --s-16: 4rem;
  /* Rythme mesure sur le live (cmc6.fr, 2026-07-30) : les blocs de contenu
     s'enchainent avec ~60-100px d'ecart (margin-top/bottom des div-block-16
     internes), pas les ~160-290px que ce padding generait applique deux fois
     (bas de section + haut de la suivante). Retour operateur : controler la
     fidelite des espacements. */
  /* Rythme vertical des sections. Deux sections consecutives cumulent
     desormais leurs coussins : l'air entre elles vaut donc DEUX fois ce token.
     Avant, 97 `style="padding-top: 0;"` poses en inline annulaient le coussin
     haut au cas par cas, et le rythme dependait de la presence du hack plutot
     que du token : mesure au navigateur, de 20 a 133px d'air selon l'endroit,
     medianes 63, 70 et 92 selon la page. Le hack a ete retire partout, la
     valeur ci-dessous est donc la seule commande du rythme. */
  --section: clamp(3rem, 6.5vh, 4.5rem);

  /* Divers */
  /* Surfaces, valeurs arretees par l'operateur sur /charte le 2026-08-07 apres
     essais en direct. Toutes les cartes et blocs sont desormais des voiles
     SOMBRES sur l'aurore, avec du texte clair : c'est le seul couple qui tienne
     le contraste, puisque l'aurore passe du magenta vif au noir selon l'endroit
     de la page. Un voile clair aurait eclairci le fond et tue la lisibilite. */
  --card-opacite: 0.7;
  --card-voile: var(--night-deep);
  --encart-opacite: 0.56;
  --bloc-opacite: 0.7;
  /* Teinte du cadre de verre. Valeur par defaut = ambiance sombre, celle des
     deux essais retenus. L'autre vit dans .verre-clair juste apres, et
     l'interrupteur de /charte bascule entre les deux. */
  --cadre-teinte: var(--night);
  --cadre-teinte-opacite: 0.7;

  --radius: 28px;
  --radius-pill: 999px;
  --container: 1200px;
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);

  /* Grain : bruit SVG inline */
  --noise: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)' opacity='0.5'/%3E%3C/svg%3E");
}

/* Les deux ambiances du cadre de verre, entre lesquelles l'interrupteur de
   /charte bascule. Elles ne changent que trois valeurs : la teinte posee sur
   le cadre, son intensite, et la saturation du verre. Tout le reste (surfaces,
   textes, espacements) est commun, donc la bascule ne peut pas casser un
   contraste ailleurs.
   L'ambiance sombre est celle de :root : sans classe, c'est elle qui rend.
   Le choix est pose sur <html> par un script en tete de Layout, avant le
   premier affichage, pour ne pas faire clignoter une ambiance puis l'autre. */
/* Sans @property, une variable CSS est du texte : le navigateur ne sait pas
   passer de 0.06 a 0.7, il remplace. En la declarant avec son type, elle
   devient interpolable et la bascule jour/nuit peut s'animer au lieu de
   sauter. C'est la seule facon d'animer une valeur portee par une variable. */
@property --cadre-teinte {
  syntax: "<color>";
  inherits: true;
  initial-value: transparent;
}
@property --cadre-teinte-opacite {
  syntax: "<number>";
  inherits: true;
  initial-value: 0;
}
@property --glass-saturate {
  syntax: "<percentage>";
  inherits: true;
  initial-value: 100%;
}

/* La transition vit sur <html>, la ou les valeurs changent : une transition de
   variable s'applique sur l'element qui la redefinit, pas sur ceux qui en
   heritent. Tout ce qui consomme ces variables suit donc automatiquement. */
html {
  transition: --cadre-teinte 0.45s var(--ease-out),
    --cadre-teinte-opacite 0.45s var(--ease-out),
    --glass-saturate 0.45s var(--ease-out);
}
@media (prefers-reduced-motion: reduce) {
  html { transition: none; }
}

html.verre-clair {
  --cadre-teinte: var(--lavender);
  --cadre-teinte-opacite: 0.06;
  --glass-saturate: 150%;
}
html.verre-sombre {
  --cadre-teinte: var(--night);
  --cadre-teinte-opacite: 0.7;
  --glass-saturate: 210%;
}

/* Libelle reserve aux lecteurs d'ecran : le bouton n'affiche qu'une icone,
   mais il doit annoncer ce qu'il fait. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Bascule jour/nuit, en haut a droite de toutes les pages.
   Alignee sur la hauteur de la pilule de navigation en desktop ; en mobile la
   barre haute n'existe pas, elle occupe donc un coin libre. */
.verre-switch {
  position: fixed;
  top: var(--s-4, 16px);
  right: var(--s-4, 16px);
  z-index: 97;
  width: 48px;
  height: 48px;
  display: grid;
  place-items: center;
  border-radius: 999px;
  /* Contraste releve : la premiere version etait un rond sombre a bordure
     douce, pose sur un coin sombre. Elle passait inapercue, alors que ce
     bouton doit etre vu de tous. Fond plus dense, bordure claire, et une ombre
     portee qui le detache du fond quel que soit l'endroit de l'aurore. */
  background: rgba(10, 7, 16, 0.88);
  border: 1px solid rgba(247, 242, 247, 0.55);
  box-shadow: 0 6px 20px -8px rgba(0, 0, 0, 0.8);
  color: var(--cream);
  cursor: pointer;
  padding: 0;
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  transition: border-color 0.25s var(--ease-out), transform 0.2s var(--ease-out),
    background-color 0.25s var(--ease-out);
}
.verre-switch:hover {
  border-color: var(--cream);
  background: rgba(10, 7, 16, 0.96);
}
.verre-switch:active { transform: scale(0.94); }
.verre-switch:focus-visible {
  outline: 2px solid var(--ember);
  outline-offset: 3px;
}
.verre-switch-icone {
  position: relative;
  display: block;
  width: 22px;
  height: 22px;
}
.verre-switch svg {
  position: absolute;
  inset: 0;
  width: 22px;
  height: 22px;
  transition: opacity 0.28s var(--ease-out), transform 0.28s var(--ease-out);
}
/* Une seule icone visible a la fois : elle montre l'ambiance ACTUELLE, pas
   celle qu'on obtiendrait en cliquant. Un interrupteur qui affiche sa cible
   se lit a l'envers une fois sur deux. */
.verre-switch .verre-soleil { opacity: 0; transform: rotate(-90deg) scale(0.6); }
.verre-switch .verre-lune { opacity: 1; transform: none; }
html.verre-clair .verre-switch .verre-soleil { opacity: 1; transform: none; }
html.verre-clair .verre-switch .verre-lune { opacity: 0; transform: rotate(90deg) scale(0.6); }

@media (prefers-reduced-motion: reduce) {
  .verre-switch,
  .verre-switch svg {
    transition: none;
  }
}

.footer {
  background: var(--night-deep);
  color: var(--cream);
}

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

* {
  margin: 0;
}

html {
  scroll-behavior: smooth;
  scroll-padding-top: 96px;
  /* Fond sombre sur la RACINE (pas seulement body) : sans ça, flash BLANC pendant
     la transition de page, car la View Transition snapshote le fond de l'élément
     racine, qui par défaut est blanc. */
  background: var(--night-deep);
  /* Filet de securite : body a deja overflow-x:clip, mais un debordement peut
     rester scrollable via html si body ne couvre pas toute la hauteur (audit
     QA 2026-07-02, cf. founder-portrait). clip plutot que hidden, meme raison
     que sur body (compat position:sticky descendant). */
  overflow-x: clip;
}

body {
  background: var(--night);
  color: var(--cream);
  font-family: var(--font-body);
  font-size: var(--text-body);
  line-height: 1.65;
  letter-spacing: 0.01em;
  font-kerning: normal;
  -webkit-font-smoothing: antialiased;
  /* clip (pas hidden) : empeche le scroll horizontal SANS creer de conteneur de
     scroll, ce qui casserait un futur position:sticky descendant. */
  overflow-x: clip;
}

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

a {
  color: inherit;
}

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

h1,
h2,
h3 {
  font-family: var(--font-display);
  font-optical-sizing: auto;
  line-height: 1.2;
  text-wrap: balance;
  /* Live (.title1) : Cal Sans en graisse 400 UNIQUE + ss02 + letter-spacing
     positif 1px. Cal Sans n'existe qu'en une graisse : demander 700/800
     fabrique un faux-gras synthetique qui ne ressemble plus a la police du
     live (signale par l'operateur : "pas la meme police"). */
  font-weight: 400;
  font-feature-settings: "ss02";
  letter-spacing: 0.5px;
  /* Aucune regle ne posait de marge basse par defaut : sur les pages dont le
     titre n'est pas suivi d'un paragraphe d'intro (la plupart des pages
     services et de la page realisations), le titre touchait directement le
     contenu suivant. Mesure sur les 28 pages du site le 2026-08-28. */
  margin-bottom: var(--s-6);
}

/* .section-head enveloppe le titre d'une section (cf. charte.astro) sur ~25
   sections du site : le titre y garde sa marge basse a 0 pour rester colle a
   son eventuel paragraphe d'intro (les deux forment un seul bloc), et c'est
   .section-head elle-meme qui pose l'ecart avant le contenu suivant. */
.section-head {
  margin-bottom: var(--s-6);
}

.section-head h1,
.section-head h2,
.section-head h3,
.cta-band h2,
.results-head h2,
.mission-row h3 {
  margin-bottom: 0;
}

p {
  text-wrap: pretty;
}

::selection {
  background: var(--ember);
  color: var(--cream);
}

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

/* ---------- Lien d'évitement ---------- */
.skip-link {
  position: fixed;
  top: 16px;
  left: 16px;
  z-index: 60;
  padding: 0.75rem 1.5rem;
  background: var(--night-raised);
  color: var(--cream);
  border: 1px solid var(--lavender-30);
  border-radius: var(--radius-pill);
  font-family: var(--font-body);
  font-weight: 600;
  text-decoration: none;
  transform: translateY(-200%);
}

.skip-link:focus {
  transform: translateY(0);
}

/* ---------- Utilitaires ---------- */
.container {
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: clamp(1.25rem, 4vw, 2.5rem);
}

.eyebrow {
  display: block;
  font-size: var(--text-label);
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--lavender);
  margin-bottom: var(--s-4);
}

.lead {
  font-size: var(--text-lead);
  color: var(--lavender);
  max-width: 56ch;
}

.section {
  padding-block: var(--section);
  position: relative;
}

/* ---------- Boutons ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  padding: 0.95rem 1.75rem;
  border-radius: var(--radius-pill);
  font-family: var(--font-body);
  font-size: 1rem;
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
  /* .btn a overflow:hidden (styles-fx.css, halo de survol) : ca desactive la
     protection min-width automatique de flexbox sur un bouton place en enfant
     flex (ex. .rdv-alt), qui peut alors retrecir sous la largeur de son texte
     et le tronquer sans ellipse (signale par l'operateur, bouton WhatsApp). */
  flex-shrink: 0;
  cursor: pointer;
  border: 1px solid transparent;
  transition: transform 0.3s var(--ease-out), box-shadow 0.3s var(--ease-out),
    border-color 0.3s var(--ease-out), background-color 0.3s var(--ease-out);
}

/* Pilule blanche au repos, fond rouge de marque + effet loupe au survol :
   comportement mesure sur le "On en discute ?" du hero de cmc6.fr. */
.btn-primary {
  background: #fff;
  color: #171022;
}

.btn-primary:hover {
  background: var(--ember);
  color: #fff;
  border-color: var(--ember);
  transform: scale(1.1);
  box-shadow: none;
  transition: background-color 1s cubic-bezier(0, 0.926, 0.25, 1),
    color 1s cubic-bezier(0, 0.926, 0.25, 1),
    border-color 1s cubic-bezier(0, 0.926, 0.25, 1),
    transform 1s cubic-bezier(0, 0.926, 0.25, 1);
}

.btn-primary:active {
  transform: scale(1.05);
}

.btn-ghost {
  background: transparent;
  border-color: var(--lavender-30);
  color: var(--cream);
}

.btn-ghost:hover {
  border-color: var(--lavender);
  background: var(--lavender-08);
}

.btn-note {
  display: block;
  margin-top: var(--s-3);
  font-size: 0.875rem;
  color: var(--lavender-dim);
}

/* ---------- Navigation ---------- */
.nav {
  position: fixed;
  inset-inline: 0;
  top: 16px;
  z-index: 50;
  display: flex;
  justify-content: center;
  padding-inline: 1rem;
  pointer-events: none;
}

.nav-pill {
  pointer-events: auto;
  display: flex;
  align-items: center;
  gap: var(--s-6);
  width: min(100%, 880px);
  padding: 0.6rem 0.6rem 0.6rem 1.4rem;
  border-radius: var(--radius-pill);
  background: color-mix(in oklab, var(--night-raised) 82%, transparent);
  border: 1px solid var(--lavender-15);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  transition: background-color 0.4s var(--ease-out), box-shadow 0.4s var(--ease-out);
}

.nav.scrolled .nav-pill {
  background: color-mix(in oklab, var(--night-raised) 95%, transparent);
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.4);
}

.nav-logo {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 1.25rem;
  letter-spacing: -0.02em;
  text-decoration: none;
  color: var(--cream);
  margin-right: auto;
  white-space: nowrap;
}

.nav-logo em {
  font-style: normal;
  color: var(--ember);
  /* Le letter-spacing negatif du lettrage collait le 6 au C qui le precede :
     deux courbes qui se cognent, visible des qu'on agrandit le logo. Valeur
     retenue le 2026-09-10 en comparant cinq ecarts, et reprise a l'identique
     dans les exports fournis a l'operateur (dossier Graphisme, LOGO 2026).
     En em, donc le rapport tient a 20 px dans la nav comme a 600 px. */
  margin-left: 0.08em;
}

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

.nav-links a {
  text-decoration: none;
  font-weight: 600;
  font-size: 0.9375rem;
  color: var(--lavender);
  padding: 0.55rem 0.9rem;
  border-radius: var(--radius-pill);
  transition: color 0.25s var(--ease-out), background-color 0.25s var(--ease-out);
}

.nav-links a:hover {
  color: var(--cream);
  background: var(--lavender-08);
}

.nav-links a[aria-current="page"] {
  color: var(--cream);
}

.nav-cta {
  flex-shrink: 0;
}

.nav-cta .btn {
  padding: 0.65rem 1.3rem;
  font-size: 0.9375rem;
}

/* .nav-burger : retire (element + regles). Le declencheur du menu mobile est
   desormais .mnav-toggle, dans le dock bas (fin de styles-fx.css). */

/* ---------- Hero ---------- */
.hero {
  position: relative;
  min-height: 100svh;
  display: flex;
  align-items: center;
  padding-block: clamp(8rem, 18vh, 11rem) clamp(4rem, 10vh, 7rem);
  overflow: hidden;
}
/* Degagement sous le hero des pages interieures.
   .glass-frame remonte de 70px (margin-top negatif) pour passer PAR-DESSUS ce
   qui la precede : c'est l'effet voulu. Sur l'accueil, la rangee de logos
   (.proof) s'intercale et absorbe ce chevauchement. Sur les pages interieures,
   le cadre suit directement le hero et mordait donc dans ses boutons : 21px
   avales sur /offres, 43px sur les pages de services (mesure au navigateur).
   Le degagement depasse les 70px du chevauchement pour laisser respirer. */
body.page-inner .hero {
  padding-bottom: max(clamp(4rem, 10vh, 7rem), 118px);
}

/* Scrim du hero RETIRE (demande operateur 2026-07-30) : le live n'a pas de
   calque semi-transparent au-dessus du fond anime, le texte porte seul. */



/* ---------- Fond anime : DEUX groupes de blocs floutes comme le live
   (audit DOM 2026-07-30) : .hs-main = blur(120px) seul (3 blocs de base),
   .hs-blend = blur(120px) + mix-blend-mode:overlay z:1 (6 blocs) qui agit
   comme un VOILE fondu par-dessus le groupe de base et le fond #1b1b1b.
   Unites "em" sur conteneur font-size:1vw comme le live. Les animations de
   derive sont plus bas (le live anime via Webflow IX2, pas en CSS). */
:root {
  --shape-blur: 120px;
}

.hero-shapes {
  position: absolute;
  inset: 0;
  /* PAS de z-index ici (etait 0) : position+z-index cree un nouveau
     contexte d'empilement qui coupait le calcul de mix-blend-mode:overlay
     (.hs-blend) de l'arriere-plan sombre de .page-aurora - le blend voyait
     un fond transparent au lieu du noir et n'assombrissait plus rien
     (retour operateur : "pas assez de zones sombres"). L'ordre DOM suffit
     deja, pas besoin d'empilement explicite. */
  font-size: 1vw;
  pointer-events: none;
}

.hs-group {
  position: absolute;
  inset: 0;
  filter: blur(var(--shape-blur));
}
.hs-blend {
  mix-blend-mode: overlay;
  z-index: 1;
}

.hs {
  position: absolute;
  border-radius: 999em;
}

/* shape-3 : pilule motrice mauve -> crimson. Transcrit du panneau
   Interactions Webflow ("Gradient Animation", trigger "Scroll into view"),
   mais mis en boucle infinie ici sur demande operateur (le live ne joue le
   cycle qu'une fois) - les keyframes reviennent exactement a l'etat 0% en
   fin de cycle (98.11%/100%), donc la boucle est sans a-coup.
   3 proprietes CSS separees (translate/rotate/scale, pas transform) car
   Webflow anime chacune avec son propre easing :
   - translate : (0,0) -> (74vw,39vw) sur 0-13s (in-out-quad) -> (0,0) sur
     13-26s (in-out-quad aussi)
   - rotate : -180deg -> 0deg sur 0-13s (linear) -> 180deg sur 13-26s
     (linear) -> snap instantane -180deg a 26s (duration 0 dans Webflow)
   - scale : 1.3 -> 1 sur 0-13s (linear) -> 1.3 sur 13-26s (linear)
   La classe .play est ajoutee par motion.js au chargement (element toujours
   au-dessus du pli, equivalent au trigger "scroll into view" de la home). */
.hs-rotor {
  width: 53em;
  height: 100em;
  left: -26em;
  top: -19.8em;
  border-radius: 999em;
  translate: 0 0;
  rotate: -180deg;
  scale: 1.3 1.3;
}
@media (min-width: 768px) {
  .hs-rotor.play {
    animation:
      rotorTranslate 26.5s infinite,
      rotorRotate 26.5s infinite,
      rotorScale 26.5s infinite;
  }
}

.hs-rotor-core {
  width: 100%;
  height: 100%;
  border-radius: 999em;
  background: linear-gradient(180deg, var(--shape-periwinkle), var(--shape-crimson));
}

/* shape-2 : lilas, satellite centre-gauche */
.hs-lilac {
  width: 43em;
  height: 40em;
  left: -27.4em;
  right: 0;
  top: -20em;
  margin-inline: auto;
  background: var(--shape-lilac);
}

/* shape-1 : bleu ciel, satellite haut-centre */
.hs-sky {
  width: 40em;
  height: 40em;
  left: 0;
  right: 0;
  top: -24.7em;
  margin-inline: auto;
  background: var(--shape-sky);
}

/* shape-8 : nappe crimson pleine. Le panneau Style Webflow affiche "Rotate
   29deg", mais le transform REELLEMENT applique sur le live (verifie via
   getComputedStyle sur cmc6.fr) est la matrice identite - AUCUNE rotation
   active. Meme constat sur shape-9/4/6/7 ci-dessous : la valeur du panneau
   Designer n'est pas ce qui est rendu. Sans transform, l'empreinte de
   chaque forme retrouve sa vraie taille (au lieu du rectangle englobant
   d'une forme pivotee, 2-3x plus large) - important pour la repartition
   des zones sombres/colorees du fond. */
.hs-crimson-tall {
  width: 47em;
  height: 119em;
  left: 26.7em;
  /* margin-left supplementaire trouve en comparant les positions rendues
     exactes (getBoundingClientRect) entre cmc6.fr et cette page - absent
     des deux videos operateur et de mes verifications precedentes. */
  margin-left: 13.15em;
  top: -17.1em;
  background: var(--shape-crimson);
}

/* shape-9 : degrade REPETE confirme sur deux passages video independants
   (angle 296deg, case "Repeat" cochee, Midnight Blue -> Crimson). Une
   correction precedente l'avait remplace par une couleur plate a tort
   (lecture d'un panneau replie qui n'affichait qu'un resume trompeur). */
.hs-stripes {
  width: 30em;
  height: 119em;
  right: 1.2em;
  top: -5.3em;
  background: var(--shape-navy);
  background-image: repeating-linear-gradient(296deg, var(--shape-navy), var(--shape-crimson));
}

/* shape-4 : nappe crimson large */
.hs-crimson-wide {
  width: 65em;
  height: 40em;
  left: 0;
  top: -11.3em;
  background: var(--shape-crimson);
}

/* shape-6 : masse navy, cote droit */
.hs-navy {
  width: 66em;
  height: 60em;
  right: -25em;
  top: -14.2em;
  background: var(--shape-navy);
}

/* shape-5 : boule mauve centree haut */
.hs-ball-mauve {
  width: 30em;
  height: 30em;
  left: 0;
  right: 0;
  top: -21.7em;
  margin-inline: auto;
  background: var(--shape-periwinkle);
}

/* shape-7 : petite pilule ciel decalee a droite */
.hs-pill-sky {
  width: 20em;
  height: 13em;
  left: 0;
  right: -23.9em;
  top: -4.1em;
  margin-inline: auto;
  background: var(--shape-sky);
}

/* Les 8 autres blocs (hs-lilac/hs-sky/hs-crimson-tall/hs-stripes/
   hs-crimson-wide/hs-navy/hs-ball-mauve/hs-pill-sky) restent STATIQUES,
   sur consigne explicite de l'operateur : "fait exactement et uniquement
   ce que je t'ai donne dans la video". La video (118 frames, verifiees
   exhaustivement par plusieurs sous-agents, zoom sur les transitions
   rapides inclus) ne montre JAMAIS leur panneau Interactions ouvert -
   seulement leurs proprietes Style statiques (deja transcrites plus haut :
   taille/couleur/rotation). Donc aucune animation inventee ici. Seule
   shape-3 (.hs-rotor) a une animation confirmee (rotorSpin, plus haut). */

/* Grain de bruit pose sur le halo : MEME image que le live (noise.png,
   copiee du CDN Webflow en local), opacity .3. Scope .page-aurora (l'element
   vit dans Layout.astro depuis le passage au calque fixe ; l'ancien scope
   .hero-shell ne matchait plus rien -> grain invisible, signale operateur). */
.page-aurora .background-noise {
  position: absolute;
  inset: 0;
  z-index: 1;
  opacity: 0.3;
  mix-blend-mode: normal;
  background-image: url("/images/noise.png");
  pointer-events: none;
}

.hero-inner {
  position: relative;
  z-index: 2;
  width: 100%;
}

.hero-content {
  max-width: 640px;
}

.hero h1 {
  font-size: var(--text-hero);
  margin-bottom: var(--s-6);
  margin-left: -0.04em;
}

.hero .lead {
  margin-bottom: var(--s-8);
}

.hero-chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2);
  margin-bottom: var(--s-8);
}

.hero-chips li {
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--lavender);
  padding: 0.35rem 0.85rem;
  border: 1px solid var(--lavender-15);
  border-radius: var(--radius-pill);
}

.hero-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-4);
  align-items: center;
}

.hero-numeral {
  position: absolute;
  z-index: 1;
  right: clamp(-2rem, 2vw, 4rem);
  top: 50%;
  transform: translateY(-50%);
  text-align: center;
  user-select: none;
  pointer-events: none;
}

.hero-numeral strong {
  display: block;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(13rem, 26vw, 24rem);
  line-height: 0.85;
  letter-spacing: -0.04em;
  color: transparent;
  -webkit-text-stroke: 1.5px var(--lavender-30);
}

.hero-numeral span {
  display: block;
  margin-top: var(--s-3);
  font-size: var(--text-label);
  font-weight: 600;
  letter-spacing: 0.35em;
  text-transform: uppercase;
  color: var(--lavender);
}

/* ---------- Bande de preuve (claire sur blanc, comme le live : la coque
   sombre s'arrete a la fin du hero, cf. cmc6-live-full.png) ---------- */
.proof {
  padding-block: var(--s-8);
  /* Degagement sous les logos. .glass-frame remonte de 70px (34px sous 768px)
     pour passer par-dessus ce qui la precede : c'est l'effet voulu, et il doit
     manger du vide, pas du contenu. Sans ce coussin, les logos clients
     passaient sous le cadre des le chargement (38px avales en desktop, mesure
     au navigateur, signale par l'operateur). Valeur = remontee du cadre plus
     une marge de respiration ; le cadre remonte moins sous 768px, le coussin
     y est donc plus court pour ne pas creuser un vide inutile sur un ecran
     ou la hauteur est comptee. */
  padding-bottom: 104px;
  /* border-block retiree (demande operateur : "sans les traits au-dessus
     en-dessous" du bandeau logos). */
  /* Sur le live, le bandeau logos est POSE SUR l'aurore sombre (logos clairs
     sur fond fonce), pas un bandeau blanc plein. Transparent : l'aurore fixe
     transparait, comme partout ailleurs. */
  background: transparent;
}
@media (max-width: 767.98px) {
  .proof {
    padding-bottom: 64px;
  }
}

.proof .eyebrow {
  text-align: center;
  margin-bottom: var(--s-8);
}

.marquee {
  position: relative;
  overflow: hidden;
  /* MEME formule que .glass-frame (pas juste le meme max-width) : avec
     seulement max-width, en dessous de ~1700px de viewport le marquee
     restait plus large que le cadre (qui lui retrecit avec la gouttiere).
     Retour operateur : "rien sur le site ne doit etre plus large que ca". */
  width: calc(100% - (var(--glass-gutter) * 2));
  max-width: var(--glass-max-width);
  margin-inline: auto;
}

/* Fondus latéraux : les logos qui sortent de l'écran deviennent flous (pas
   un aplat blanc, qui ne correspondait a rien sur fond sombre). Le calque a
   son propre backdrop-filter:blur, et un mask-image degrade fait varier son
   OPACITE (pas la valeur de blur, fixe) pour donner l'impression d'un flou
   progressif qui s'intensifie vers le bord. */
.marquee::before,
.marquee::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  width: clamp(3rem, 8vw, 8rem);
  z-index: 1;
  pointer-events: none;
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}

.marquee::before {
  left: 0;
  -webkit-mask-image: linear-gradient(90deg, #000, transparent);
  mask-image: linear-gradient(90deg, #000, transparent);
}

.marquee::after {
  right: 0;
  -webkit-mask-image: linear-gradient(270deg, #000, transparent);
  mask-image: linear-gradient(270deg, #000, transparent);
}

.marquee-track {
  display: flex;
  align-items: center;
  gap: clamp(2.5rem, 6vw, 5rem);
  width: max-content;
  will-change: transform;
  animation: marquee 42s linear infinite;
}

/* Tentative 6 (31/08, figer #marquee sur Safari) annulee le meme jour :
   l'operateur confirme que ce n'etait pas non plus la cause. Voir
   project_cmc6_safari_glassframe_jank_2026_08_29.md. */

/* Pas de pause au survol : le bandeau de logos ne contient rien de cliquable,
   s'arreter sous le curseur donne juste l'impression que la page s'est figee.
   (Cette regle etait de toute facon morte : motion.js desactive l'animation
   CSS et pilote le transform lui-meme.) */

/* Scope #marquee : ces regles etaient ecrites en pensant "le seul contenu
   d'un marquee-track", mais .avis-marquee et .proj-marquee reutilisent la
   meme classe .marquee-track pour du contenu tres different (avis, cartes
   projet avec vraies photos). Sans le scope #marquee, .marquee-track li img
   plus bas repassait les screenshots de realisations en gris et 170px de
   large (constate : cartes qui semblaient ne jamais charger d'image). */
#marquee .marquee-track li {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 1.25rem;
  letter-spacing: -0.01em;
  /* logos clairs sur l'aurore, comme le bandeau du live (blanc translucide) */
  color: rgba(255, 255, 255, 0.72);
  white-space: nowrap;
  transition: color 0.3s var(--ease-out);
}

#marquee .marquee-track li:hover {
  color: var(--cream);
}

/* Vrais logos clients : mesure sur le live (2026-07-31) que .logo-image y a
   filter:none, opacity:1, height:60px - AUCUN filtre blanc. Corrige un essai
   precedent qui passait tout en silhouette blanche : ca marchait pour les
   wordmarks simples (Adidas, Snapchat) mais effacait completement les badges
   a fond opaque (La BA Com, Car Factory -> carre blanc illisible). Couleurs
   d'origine partout, comme l'original. */
#marquee .marquee-track li img {
  display: block;
  height: 56px;
  width: auto;
  max-width: 170px;
  object-fit: contain;
  /* Comme l'operateur l'avait fait a l'origine : noir et blanc au repos,
     couleur au survol. */
  filter: grayscale(1);
  transition: filter 0.3s var(--ease-out);
}
#marquee .marquee-track li:hover img {
  filter: grayscale(0);
}

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

.proof-partners {
  margin-top: var(--s-8);
  text-align: center;
  font-size: 0.9375rem;
  color: var(--lavender-dim);
}

.proof-partners strong {
  color: var(--lavender);
  font-weight: 600;
}

/* ---------- Bascule (statement) ---------- */
.statement .container {
  max-width: 1080px;
}

.statement p {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(1.75rem, 3.6vw + 0.25rem, 3.5rem);
  line-height: 1.15;
  letter-spacing: -0.02em;
  text-wrap: balance;
  /* Explicite : sur l'aurore vive derriere le cadre de verre, tout heritage
     plus sombre (lavande) devient illisible. Beige clair comme .title1 live. */
  color: var(--beige);
}

.statement .line-2 {
  display: block;
  margin-top: var(--s-4);
  padding-left: clamp(0rem, 8vw, 7rem);
}

.statement .line-2 b {
  color: var(--ember);
  font-weight: inherit;
}

.statement .statement-note {
  margin-top: var(--s-8);
  font-family: var(--font-body);
  font-size: var(--text-lead);
  font-weight: 400;
  letter-spacing: 0.01em;
  color: var(--lavender);
  max-width: 52ch;
}

/* ---------- Résultats ---------- */
.results-head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--s-6);
  margin-bottom: var(--s-12);
}

.results-head h2 {
  font-size: var(--text-h2);
  max-width: 16ch;
}

.results-head p {
  color: var(--lavender);
  max-width: 38ch;
}

.result {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: var(--s-6);
  align-items: center;
  padding: clamp(2rem, 5vh, 3rem) clamp(1.25rem, 3vw, 2.25rem);
  /* Sol sombre translucide. Sans lui, le chiffre en ember flottait directement
     sur l'aurore : sur ses zones rouges, un chiffre rouge disparait. Le fond
     ne masque pas l'aurore, il stabilise seulement le contraste sous le texte.
     --bloc-opacite pilotable depuis /charte. */
  background: color-mix(
    in oklab,
    var(--night-deep) calc(var(--bloc-opacite, 0.55) * 100%),
    transparent
  );
  border: 1px solid var(--lavender-15);
  border-radius: var(--radius);
}

.result:last-of-type {
  border-bottom: 1px solid var(--lavender-15);
}

.result-metric {
  grid-column: 1 / 6;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(3.5rem, 7vw, 6rem);
  line-height: 0.95;
  letter-spacing: -0.03em;
  color: var(--ember);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.result-metric sup {
  font-size: 0.45em;
  vertical-align: 0.7em;
  letter-spacing: 0;
}

.result-body {
  grid-column: 6 / 13;
}

.result:nth-of-type(even) .result-metric {
  grid-column: 8 / 13;
  order: 2;
  text-align: right;
}

.result:nth-of-type(even) .result-body {
  grid-column: 1 / 8;
  order: 1;
}

.result-body h3 {
  font-size: var(--text-h3);
  margin-bottom: var(--s-2);
}

.result-body p {
  color: var(--lavender);
  max-width: 48ch;
}

.result-source {
  display: block;
  margin-top: var(--s-3);
  font-size: var(--text-label);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--lavender-dim);
}

.results-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4);
  margin-top: var(--s-8);
}

.results-foot small {
  color: var(--lavender-dim);
  font-size: 0.875rem;
  max-width: 46ch;
}

/* Bloc "liens sortants" de fin de page (pages de services). Reprend
   .link-arrow tel quel, juste range en grille : aucune nouvelle valeur de
   design, seulement les tokens d'espacement deja en usage (.pledges-grid
   suit le meme repeat(auto-fit, minmax(...))). */
.related-links {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--s-3) var(--s-8);
  max-width: 960px;
}

.link-arrow {
  font-weight: 600;
  color: var(--cream);
  text-decoration: none;
  border-bottom: 1px solid var(--lavender-30);
  padding-bottom: 2px;
  transition: border-color 0.25s var(--ease-out), color 0.25s var(--ease-out);
}

.link-arrow:hover {
  color: var(--ember);
  border-color: var(--ember);
}

/* ---------- Carte glass : version de base (pages internes) ----------
   Sur la home, styles-fx.css surcharge cette regle (tilt 3D + halo au pointeur).
   Ici on garantit qu'une .glass-card hors home (ex. services d'offres.html) a un
   vrai fond, du padding et un feedback de survol coherent. Le lift passe par
   --lift, comme sur la home, pour ne jamais ecraser le transform de styles-fx. */
.glass-card {
  position: relative;
  border-radius: var(--radius);
  padding: clamp(1.5rem, 3vw, 2.25rem);
  /* Ce fond est ECRASE par styles-fx.css (charge apres, meme specificite), qui
     pose le vrai fond beige de la carte. Laisse tel quel : c'est la valeur de
     repli si styles-fx.css ne charge pas. Le reglage d'opacite vit dans le
     fichier qui gagne, pas ici. */
  background: color-mix(in oklab, var(--night-raised) 92%, var(--night-deep));
  box-shadow: inset 0 0 0 1px var(--lavender-15), 0 18px 50px -30px rgba(0, 0, 0, 0.8);
  transition: transform 0.35s var(--ease-out), box-shadow 0.35s var(--ease-out);
}

.glass-card:hover {
  box-shadow: inset 0 0 0 1px var(--lavender-30), 0 30px 70px -28px rgba(0, 0, 0, 0.9);
}

/* Grille des cartes (engagements home, services offres) */
.pledges-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--s-6);
}

/* Cartes de .pledges-grid utilisees avec un vrai <h3> (pages de services :
   symptomes d'une refonte, surfaces du referencement local, etapes de
   redaction). Un h3 nu n'a ni taille ni couleur de paragraphe fixees par la
   regle globale h1,h2,h3 (elle pose la police, pas la taille) : sans ceci il
   herite de la taille par defaut du navigateur et son <p> herite du --cream
   du corps, trop clair pour du texte secondaire. .testimonial-card et
   .project-card-body redefinissent deja leur propre <p> plus loin dans ce
   fichier et gagnent sur cette regle par ordre de source. */
.glass-card h3 {
  font-size: var(--text-h3);
  margin-bottom: var(--s-2);
}

.glass-card p {
  color: var(--lavender);
}

/* ---------- Lifts au survol (mouvement) ----------
   Centralises et gates : `hover: hover` evite le hover collant apres un tap sur
   tactile ; `no-preference` respecte prefers-reduced-motion. La specificite du
   selecteur [data-fx].is-in (0,3,0) bat cette regle et pose `transform: none`
   sur les elements reveles : compensee dans styles-fx.css par
   [data-fx].glass-card.is-in:hover / [data-fx].formula.is-in:hover. */
@media (hover: hover) and (prefers-reduced-motion: no-preference) {
  .btn-ghost:hover { transform: translateY(-2px); }
  .btn-ghost:active { transform: translateY(0); }
  .glass-card:hover { transform: translateY(-4px); }
  .formula:hover { transform: translateY(-4px); }
  .project-card:hover .project-card-media img { transform: scale(1.04); }
}

/* ---------- Méthode ---------- */
.method {
  background: var(--night-deep);
  overflow: hidden;
}

.method-watermark {
  position: absolute;
  right: -2%;
  bottom: -6%;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(16rem, 32vw, 30rem);
  line-height: 0.8;
  color: var(--lavender);
  opacity: 0.035;
  pointer-events: none;
  user-select: none;
  z-index: 0;
}

.method .container {
  position: relative;
  z-index: 1;
}

/* ---------- Sequence a cercles numerotes (section "Notre methode", home) ----------
   Fidele au live : 3 cercles crimson pleins relies par un trait horizontal,
   titre + description centres sous chaque cercle (cf. cmc6-live-mid.png). */
.method-circles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: var(--s-8) var(--s-6);
  counter-reset: none;
}

.method-step {
  position: relative;
  text-align: center;
  padding-top: var(--s-4);
}

/* Trait reliant les cercles : une ligne continue derriere toute la rangee,
   interrompue visuellement seulement par les cercles (z-index superieur). */
.method-circles::before {
  content: none;
}

.method-step::before {
  content: "";
  position: absolute;
  top: calc(var(--s-4) + 28px);
  left: -50%;
  width: 100%;
  height: 2px;
  background: color-mix(in oklab, var(--ember) 35%, transparent);
  z-index: 0;
}

.method-step:first-child::before {
  content: none;
}

.step-circle {
  position: relative;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 56px;
  height: 56px;
  border-radius: 50%;
  background: var(--ember);
  color: #fff;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 1.375rem;
  box-shadow: 0 10px 30px -10px var(--ember-glow);
  margin-bottom: var(--s-3);
}

.step-range {
  display: block;
  font-size: var(--text-label);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--lavender-dim);
  margin-bottom: var(--s-2);
}

.step-titre {
  font-family: var(--font-display);
  font-size: 1.15rem;
  margin-bottom: var(--s-2);
}

.step-texte {
  color: var(--lavender);
  font-size: 0.9375rem;
  max-width: 32ch;
  margin-inline: auto;
}

@media (max-width: 767.98px) {
  .method-circles { grid-template-columns: 1fr; gap: var(--s-6); }
  .method-step::before { content: none; }
}

.method-head {
  max-width: 620px;
  margin-bottom: var(--s-16);
}

.method-head h2 {
  font-size: var(--text-h2);
  margin-bottom: var(--s-4);
}

.method-head p {
  color: var(--lavender);
}

.phases {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(2rem, 4vw, 4rem);
  position: relative;
  padding-top: var(--s-8);
}

.phases::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 1px;
  background: linear-gradient(90deg, var(--ember), var(--magenta), var(--lavender-30));
}

.phase {
  position: relative;
}

.phase::before {
  content: "";
  position: absolute;
  top: calc(-1 * var(--s-8) - 5px);
  left: 0;
  width: 11px;
  height: 11px;
  border-radius: 50%;
  background: var(--ember);
}

.phase-days {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 1.5rem;
  letter-spacing: -0.01em;
  color: var(--ember);
  margin-bottom: var(--s-3);
}

.phase h3 {
  font-size: var(--text-h3);
  font-weight: 700;
  margin-bottom: var(--s-3);
}

.phase p {
  color: var(--lavender);
  font-size: 1rem;
}

.method-foot {
  margin-top: var(--s-16);
}

/* ---------- Fondateur ---------- */
.founder .container {
  display: grid;
  grid-template-columns: minmax(0, 7fr) minmax(0, 4fr);
  gap: clamp(2.5rem, 6vw, 5rem);
  align-items: center;
}

.founder blockquote {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(1.5rem, 2.4vw + 0.25rem, 2.25rem);
  line-height: 1.25;
  letter-spacing: -0.015em;
  text-wrap: balance;
}

.founder blockquote b {
  color: var(--ember);
  font-weight: inherit;
}

.founder figcaption {
  margin-top: var(--s-6);
  color: var(--lavender);
}

.founder figcaption strong {
  display: block;
  color: var(--cream);
  font-weight: 700;
}

.founder-portrait {
  position: relative;
  aspect-ratio: 4 / 5;
  border-radius: var(--radius);
  overflow: hidden;
  display: flex;
  align-items: flex-end;
  padding: 0;
  /* audit QA 2026-07-02 : en grid item stretch (.founder-grid), aspect-ratio
     calcule une largeur preferee depuis la hauteur etiree par la rangee, qui
     peut depasser la piste (min-width:auto par defaut sur un item de grille
     l'y autorise). min-width:0 laisse la piste reprendre la main. */
  min-width: 0;
}

/* Photo plein cadre (choisie par l'operateur parmi 6 pistes comparees le
   2026-08-29, "portrait cinematique") : remplace le degrade decoratif +
   monogramme d'origine (jamais branche a une vraie photo) par la vraie
   photo de Clement, en fond perdu. */
/* Empilement EXPLICITE des trois couches (photo / voile / legende).
   Sans ca, `.glass-card > * { position:relative; z-index:1 }` de
   styles-fx.css (charge apres, il gagne) posait z-index:1 sur l'IMG, alors
   que le voile ::after restait a z-index auto : l'image passait donc
   par-dessus le voile et le masquait entierement. Diagnostic : voile force
   en rouge vif -> aucun rouge visible a l'ecran, alors que ses styles
   calcules etaient corrects. C'est ce qui rendait la legende illisible
   (contraste mesure 1.27:1 au lieu des 4.5:1 minimum). */
.founder-portrait img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  z-index: 0;
}

/* padding:0 pose dans la regle .founder-portrait ci-dessus est mort :
   styles-fx.css redeclare .glass-card{padding:clamp(...)} et gagne (meme
   specificite, fichier charge apres - c'est documente dans ce fichier-la).
   Il faut donc une specificite superieure pour coller la legende au bord. */
.founder-portrait.glass-card {
  padding: 0;
}

.founder-portrait::after {
  content: "";
  position: absolute;
  inset: 0;
  /* z-index 1 : au-dessus de la photo (0), sous la legende (2). */
  z-index: 1;
  /* Meme degrade que la maquette "Portrait plein cadre" validee par
     l'operateur : assombrit seulement la moitie basse, pour garder la
     photo lisible en haut. Un assombrissement plus fort/plus tot avait ete
     essaye ici pour la lisibilite mobile (carte 16/10, plus courte), mais
     ca changeait le rendu desktop par rapport a la maquette choisie -
     ce renfort vit maintenant seulement dans le media query mobile. */
  /* Pas de var(--noise) ici : la regle d'origine le posait avec
     opacity:0.05 sur tout le pseudo-element (grain discret). Ce voile-ci
     doit etre opaque pour assombrir, donc l'opacite globale est partie, et
     le grain se retrouvait alors applique a 100 % : photo visiblement
     granuleuse. Le degrade seul suffit. */
  background: linear-gradient(180deg, transparent 45%, rgba(16, 11, 24, 0.94) 100%);
  background-repeat: no-repeat;
}

.founder-portrait .portrait-meta {
  position: relative;
  /* 2 : au-dessus du voile (1), lui-meme au-dessus de la photo (0). */
  z-index: 2;
  width: 100%;
  padding: var(--s-6);
}

.founder-portrait .portrait-name {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 1.4rem;
  color: var(--cream);
}

.founder-portrait .portrait-role {
  margin: 0.3rem 0 0;
  color: var(--lavender-dim);
  font-size: 0.82rem;
}

.founder-portrait figcaption,
.founder-portrait .portrait-label {
  position: relative;
  z-index: 1;
  font-size: var(--text-label);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--cream);
}

/* ---------- Engagements ---------- */
.pledges {
  background: var(--night-deep);
}

.pledges h2 {
  font-size: var(--text-h2);
  max-width: 18ch;
  margin-bottom: var(--s-12);
}

.pledges ol {
  list-style: none;
  padding: 0;
  counter-reset: pledge;
  max-width: 880px;
}

.pledges li {
  counter-increment: pledge;
  display: grid;
  grid-template-columns: 96px 1fr;
  gap: var(--s-6);
  align-items: baseline;
  padding-block: var(--s-6);
  border-top: 1px solid var(--lavender-15);
}

.pledges li:last-child {
  border-bottom: 1px solid var(--lavender-15);
}

.pledges li::before {
  content: "0" counter(pledge);
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 2.25rem;
  letter-spacing: -0.02em;
  color: transparent;
  -webkit-text-stroke: 1px var(--lavender-30);
}

.pledges li strong {
  display: block;
  font-size: var(--text-h3);
  font-weight: 400;
  font-family: var(--font-display);
  letter-spacing: -0.01em;
  margin-bottom: var(--s-2);
}

.pledges li p {
  color: var(--lavender);
  font-size: 1rem;
  max-width: 52ch;
}

/* ---------- FAQ ---------- */
.faq .container {
  max-width: 880px;
}

.faq h2 {
  font-size: var(--text-h2);
  margin-bottom: var(--s-12);
}

.faq details {
  border-top: 1px solid var(--lavender-15);
}

.faq details:last-of-type {
  border-bottom: 1px solid var(--lavender-15);
}

.faq summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4);
  padding-block: var(--s-6);
  cursor: pointer;
  list-style: none;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 1.1875rem;
  letter-spacing: -0.01em;
  transition: color 0.25s var(--ease-out);
}

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

.faq summary:hover {
  color: var(--ember);
}

.faq summary::after {
  content: "";
  flex-shrink: 0;
  width: 12px;
  height: 12px;
  border-right: 2px solid var(--lavender);
  border-bottom: 2px solid var(--lavender);
  transform: rotate(45deg);
  transition: transform 0.3s var(--ease-out);
  margin-right: 4px;
}

.faq details[open] summary::after {
  transform: rotate(-135deg) translate(-2px, -2px);
}

.faq details p {
  padding-bottom: var(--s-6);
  color: var(--lavender);
  max-width: 65ch;
}

/* ---------- RDV ---------- */
.rdv {
  position: relative;
  overflow: hidden;
}

.rdv::before {
  content: "";
  position: absolute;
  inset: 0;
  background:
    radial-gradient(55% 60% at 15% 0%, var(--shape-crimson) 0%, transparent 60%),
    radial-gradient(45% 50% at 60% 10%, var(--shape-periwinkle) 0%, transparent 55%),
    radial-gradient(50% 55% at 90% 100%, var(--shape-navy) 0%, transparent 65%);
  opacity: 0.4;
}

.rdv::after {
  content: "";
  position: absolute;
  inset: 0;
  background-image: var(--noise);
  opacity: 0.035;
  pointer-events: none;
}

.rdv .container {
  position: relative;
  z-index: 1;
}

.rdv-head {
  max-width: 640px;
  margin-bottom: var(--s-12);
}

.rdv-head h2 {
  font-size: var(--text-h2);
  margin-bottom: var(--s-4);
}

.rdv-head p {
  color: var(--lavender);
  font-size: var(--text-lead);
}

/* --------------------------------------------------------------------------
   Demande d'audit sans rendez-vous
   --------------------------------------------------------------------------
   Pose sous le calendrier, separe par un « ou » : les deux voies sont
   presentees comme equivalentes, pas l'une en repli de l'autre. Celui qui
   veut parler prend un creneau, celui qui veut juste l'audit laisse son
   adresse. Le bouton du hero promet le second, la page ne proposait que le
   premier.
   -------------------------------------------------------------------------- */
.rdv-ou {
  display: flex;
  align-items: center;
  gap: 1rem;
  margin: var(--s-7, 2.5rem) auto;
  max-width: 62ch;
  color: var(--lavender-dim);
  font-size: .9rem;
}
.rdv-ou::before,
.rdv-ou::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--lavender-15);
}

.rdv-form-carte {
  max-width: 62ch;
  margin-inline: auto;
  padding: clamp(1.5rem, 4vw, 2.4rem);
  border-radius: var(--radius);
  background: color-mix(in oklab, var(--night-raised) 90%, var(--night-deep));
  box-shadow: inset 0 0 0 1px var(--lavender-15);
}
.rdv-form-carte h3 {
  font-size: clamp(1.2rem, 2.4vw, 1.5rem);
  margin-bottom: .5rem;
}
.rdv-form-carte > p { color: var(--lavender); margin-bottom: var(--s-5); }
.rdv-form { display: grid; gap: var(--s-4); }
/* Styles des champs repris ici plutot qu'importes des pages d'outils : cette
   page ne charge pas leur feuille, et l'y ajouter pour deux champs
   embarquerait tout le materiel des convertisseurs. */
.rdv-form .ot-field { display: grid; gap: .35rem; }
.rdv-form .ot-field label { font-size: .9rem; color: var(--lavender-dim); }
.rdv-form .ot-field input {
  font: inherit;
  font-size: .95rem;
  color: var(--cream);
  background: var(--night-deep);
  border: 1px solid var(--lavender-30);
  border-radius: 12px;
  padding: .7rem 1rem;
  width: 100%;
}
.rdv-form .ot-field input::placeholder { color: var(--lavender-dim); }
.rdv-form .ot-field input:focus-visible { outline: 2px solid var(--ember); outline-offset: 2px; }
.rdv-form-grille {
  display: grid;
  gap: var(--s-4);
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}
.rdv-form-pied {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .75rem 1rem;
  margin-top: .3rem;
}
.rdv-form-etat[hidden] { display: none; }
.rdv-form-etat { margin: 0; font-size: .9rem; font-weight: 600; }
.rdv-form-note { margin: 0; font-size: .82rem; color: var(--lavender-dim); }

@media (max-width: 560px) {
  .rdv-form-pied .btn { width: 100%; justify-content: center; }
}

.cal-frame {
  background: var(--night-raised);
  border: 1px solid var(--lavender-15);
  border-radius: var(--radius);
  min-height: 640px;
  overflow: hidden;
  display: grid;
  /* Le calendrier de Cal.com mesure 570 px de haut, le cadre en impose 640 :
     cale en debut de grille, il laissait 70 px de vide sous lui et paraissait
     pousse vers le haut. Centre, le cadre respire des deux cotes. Sans effet
     quand le contenu depasse la hauteur minimale (choix d'un creneau, puis
     formulaire), la grille reprend alors la hauteur du contenu. */
  align-items: center;
}

.cal-frame > * {
  grid-area: 1 / 1;
}

.cal-loading {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--s-4);
  color: var(--lavender-dim);
  font-size: 0.9375rem;
}

.cal-loading .dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--ember);
  animation: pulse 1.2s ease-in-out infinite;
}

/* La zone n'attend plus un chargement automatique : elle porte le bouton qui le
   declenche (cf. script.js, section Cal.com). La note explique pourquoi le
   calendrier n'est pas deja la, sinon le clic ressemble a une friction gratuite. */
.cal-loading {
  padding: var(--s-8) var(--s-4);
  text-align: center;
}

.cal-loading .cal-note {
  max-width: 42ch;
  font-size: 0.8125rem;
  line-height: 1.5;
  opacity: 0.8;
}

@keyframes pulse {
  50% {
    opacity: 0.25;
    transform: scale(0.8);
  }
}

.rdv-alt {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4);
  margin-top: var(--s-6);
  color: var(--lavender-dim);
  font-size: 0.9375rem;
}

.rdv-alt a {
  color: var(--lavender);
  font-weight: 600;
}

.rdv-alt a:hover {
  color: var(--cream);
}

/* ---------- Footer ---------- */
/* Refondu le 2026-09-10. L'ancien posait cinq colonnes de liens, une carte
   "CMC Flow" isolee en bas a gauche et 500 px de vide dans la colonne de
   marque, pour 1045 px de haut en desktop et 2130 px au telephone. Sept
   libelles cassaient sur deux lignes faute de largeur, et la barre du bas
   etait un bandeau gris clair (--footer-bg) sur un site sombre.
   La signature parle en premier, les liens suivent. */
.footer {
  /* Le fond reel est pose dans styles-fx.css (voile a 80 % + fondu qui deborde
     au-dessus) : ce fichier est chargé avant, sa valeur ne sert que de repli si
     styles-fx ne charge pas. Pas de border-top, elle tracerait une ligne au
     milieu du fondu ; pas d'overflow hidden, il empecherait le ::after de
     deborder vers le haut. */
  background: var(--night-deep);
  color: var(--cream);
  padding: 0;
  position: relative;
}

.footer .container {
  position: relative;
}

.f2-signature {
  padding-block: var(--s-16) var(--s-12);
  border-bottom: 1px solid var(--lavender-08);
}

.f2-signature p {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(1.75rem, 3.3vw, 2.75rem);
  line-height: 1.12;
  letter-spacing: 0.5px;
  color: var(--cream);
  margin: 0;
  max-width: 25ch;
}

.f2-signature p em {
  font-style: normal;
  color: var(--ember);
}

.f2-actions {
  display: flex;
  gap: var(--s-3);
  flex-wrap: wrap;
  margin-top: var(--s-8);
}

.f2-actions a {
  display: inline-flex;
  align-items: center;
  padding: 0.7rem 1.25rem;
  border-radius: var(--radius-pill);
  text-decoration: none;
  font-size: 0.9375rem;
  font-weight: 500;
  transition: transform 0.4s var(--ease-out), background-color 0.4s var(--ease-out);
}

.f2-actions .primaire {
  background: var(--ember);
  color: #fff;
}

.f2-actions .primaire:hover {
  transform: translateY(-2px);
}

.f2-actions .secondaire {
  color: var(--lavender);
  border: 1px solid var(--lavender-30);
}

.f2-actions .secondaire:hover {
  color: var(--cream);
  background: var(--lavender-08);
}

.f2-liens {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--s-8);
  padding-block: var(--s-12) var(--s-8);
}

.footer h4 {
  font-size: 0.6875rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--lavender-dim);
  margin: 0 0 var(--s-3);
  font-family: var(--font-body);
  font-weight: 500;
}

.f2-liens ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
}

.f2-liens a {
  color: var(--lavender);
  text-decoration: none;
  font-size: 0.875rem;
  line-height: 1.35;
  transition: color 0.25s var(--ease-out);
}

.f2-liens a:hover {
  color: var(--cream);
}

.f2-bas {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--s-4);
  flex-wrap: wrap;
  padding-block: var(--s-4) var(--s-6);
  border-top: 1px solid var(--lavender-08);
}

.f2-bas .nav-logo {
  font-size: 1.125rem;
}

/* La tagline reste dans le pied : elle porte l'activite et la ville sur les
   33 pages du site, et c'est un champ que le client edite depuis le portail. */
.f2-tagline {
  color: var(--lavender-dim);
  font-size: 0.8125rem;
  line-height: 1.5;
  margin: 0;
  max-width: 46ch;
  flex: 1 1 320px;
}

/* Le copyright et les deux liens se touchaient ("Confidentialite(c) 2026") :
   les marges laterales ne separaient que les <a> entre eux. Un gap sur le
   conteneur separe les trois quoi qu'il arrive. */
.f2-bas .mentions {
  color: var(--lavender-dim);
  font-size: 0.8125rem;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--s-2) var(--s-6);
}

/* En desktop le copyright ouvre la ligne, les liens suivent. */
.f2-bas .mentions .copy {
  order: -1;
}

.f2-bas .mentions a {
  color: var(--lavender-dim);
  text-decoration: none;
}

.f2-bas .mentions a:hover {
  color: var(--cream);
  text-decoration: underline;
}

@media (max-width: 820px) {
  .f2-liens {
    grid-template-columns: repeat(2, 1fr);
    gap: var(--s-8) var(--s-6);
  }
}

@media (max-width: 640px) {
  .f2-signature {
    padding-block: var(--s-12) var(--s-8);
  }

  .f2-actions a {
    flex: 1 1 auto;
    justify-content: center;
  }

  .f2-bas {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--s-3);
  }

  /* Le copyright et les deux liens legaux se melangeaient en fin de ligne
     ("Montpellier. Mentions" puis "legales" a la ligne). Ils s'empilent, les
     liens d'abord parce qu'ils sont cliquables. */
  .f2-bas .mentions {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--s-2);
  }

  .f2-bas .mentions .copy {
    order: 2;
  }
}


/* Zones tactiles elargies : ces liens texte courts (soulignement colle au
   texte par design) mesurent 19-28px de haut en reel sur mobile, sous le
   minimum tactile confortable (~40-44px). Le pseudo-element ajoute une zone
   de tap invisible qui deborde du texte sans toucher au rendu visuel ni au
   layout (absolument positionne, hors flux). */
.f2-liens a,
.f2-bas .mentions a,
.f2-bas .nav-logo,
.link-arrow,
.proj-link,
.post-more,
.nav-pill-logo {
  position: relative;
}
.f2-liens a::before,
.f2-bas .mentions a::before,
.f2-bas .nav-logo::before,
.link-arrow::before,
.proj-link::before,
.post-more::before,
.nav-pill-logo::before {
  content: "";
  position: absolute;
  inset: -10px -6px;
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  .marquee-track {
    animation: none;
    flex-wrap: wrap;
    width: auto;
    justify-content: center;
  }

  /* Bug signale par l'operateur le 31/08 : sans cette regle, reduire les
     animations affichait les 2-3 copies dupliquees (necessaires au bouclage
     infini normal, marquees aria-hidden) empilees/etirees les unes sous les
     autres - logos a la verticale, avis "tronques" par le stretch sur des
     rangees dupliquees. On ne garde que la copie reelle (non aria-hidden). */
  .marquee-track li[aria-hidden="true"] {
    display: none;
  }

  .cal-loading .dot {
    animation: none;
  }

  .btn,
  .nav-pill {
    transition: none;
  }
}

/* ---------- Responsive ---------- */
@media (max-width: 960px) {
  .hero-numeral {
    right: -8%;
    opacity: 0.5;
  }

  .founder .container {
    grid-template-columns: 1fr;
  }

  /* audit QA 2026-07-02 : .founder .container ne matche rien dans le DOM actuel
     (la section fondateur est .section > .container > .results-grid, pas
     .founder .container) ; regle orpheline conservee telle quelle (hors
     perimetre), fix reel cible la vraie classe posee sur l'element. */
  .founder-grid {
    grid-template-columns: 1fr !important;
  }

  .founder-portrait {
    /* Photo avant le texte sur mobile (retour operateur : "ca hook mieux") :
       order seul, le HTML/DOM ne bouge pas, donc desktop (grille 2
       colonnes, ordre naturel) n'est pas touche. */
    order: -1;
    /* width:100% EXPLICITE plutot qu'un plafond en pixels. L'ancien
       max-width:min(380px,100%) servait de garde-fou anti-debordement quand
       la largeur etait deduite de la hauteur par aspect-ratio ; mais des que
       la colonne depasse 380px (grands telephones), la carte photo restait
       coincee a 380px et paraissait plus etroite que la carte citation
       au-dessus (constate par l'operateur sur son iPhone). En fixant la
       largeur sur la colonne, la hauteur se deduit de l'aspect-ratio et non
       l'inverse : plus de debordement possible, et les deux cartes sont
       alignees.
       aspect-ratio 16/10 (plus court) etait un reste d'avant la vraie
       photo : une fois le visage dedans, ce format coupait trop haut et le
       prenom se retrouvait colle dessus, voire dessus (retour operateur :
       "mon prenom passe sur mon visage"). Encore plus haute que le 4/5
       desktop (3/4) a la demande de l'operateur : plus de marge entre le
       visage et la legende sur mobile specifiquement. */
    width: 100%;
    max-width: 100%;
    align-self: start;
    aspect-ratio: 3 / 4;
  }

  /* Carte bien plus courte qu'en 4:5 desktop : le nom passait sur deux
     lignes et poussait le role hors de la zone assombrie. */
  .founder-portrait .portrait-meta {
    padding: var(--s-4);
  }
  .founder-portrait .portrait-name {
    font-size: 1.05rem;
  }
  /* Voile progressif renforce : 4 paliers au lieu de 2-3, pour un
     assombrissement qui se sent vraiment "progressif" (pas un a-plat qui
     apparait d'un coup) et qui est deja tres sombre bien avant la legende,
     pas juste au moment ou le texte commence. */
  /* Retour operateur : la montee vers l'opaque etait trop resserree (50%
     a 91%, avec un bond net entre 68% et 91%) - ca lisait comme un
     interrupteur plutot qu'un fondu. Etalee ici sur toute la hauteur (20%
     a 100%) en 6 paliers rapproches pour une courbe lisse, et le tout bas
     de l'image est desormais a l'opacite pleine (1, pas 0.99). */
  .founder-portrait::after {
    background: linear-gradient(
      180deg,
      transparent 20%,
      rgba(16, 11, 24, 0.15) 40%,
      rgba(16, 11, 24, 0.4) 60%,
      rgba(16, 11, 24, 0.68) 78%,
      rgba(16, 11, 24, 0.9) 92%,
      rgba(16, 11, 24, 1) 100%
    );
    background-repeat: no-repeat;
  }

}

@media (max-width: 767.98px) {
  /* Menu mobile : plus rien ici. Sous 768px, le header .nav est masque et la
     navigation vit dans le dock bas + bottom sheet (.mnav-*, fin de
     styles-fx.css). L'ancienne pilule haute (morph clip-path + filter) est
     supprimee : liens hors zone de pouce et morph juge pas fluide. */

  .hero {
    min-height: auto;
    display: block;
  }

  :root {
    --shape-blur: 80px;
  }

  .hero-numeral {
    position: relative;
    right: auto;
    top: auto;
    transform: none;
    margin-top: var(--s-12);
    opacity: 0.65;
    text-align: left;
  }

  .hero-actions .btn {
    width: 100%;
    justify-content: center;
  }

  .hero-numeral strong {
    font-size: clamp(8rem, 36vw, 13rem);
  }

  .result,
  .result:nth-of-type(even) {
    grid-template-columns: 1fr;
    gap: var(--s-4);
  }

  .result-metric,
  .result:nth-of-type(even) .result-metric {
    grid-column: 1;
    order: 0;
    text-align: left;
  }

  .result-body,
  .result:nth-of-type(even) .result-body {
    grid-column: 1;
    order: 1;
  }

  .phases {
    grid-template-columns: 1fr;
    gap: var(--s-8);
    padding-top: 0;
    padding-left: var(--s-6);
  }

  .phases::before {
    width: 1px;
    height: 100%;
    right: auto;
    background: linear-gradient(180deg, var(--ember), var(--magenta), var(--lavender-30));
  }

  .phase::before {
    top: 6px;
    left: calc(-1 * var(--s-6) - 5px);
  }

  .pledges li {
    grid-template-columns: 64px 1fr;
    gap: var(--s-4);
  }


  .cal-frame {
    min-height: 560px;
  }
}

/* ==========================================================================
   Pages internes
   ========================================================================== */

/* ---------- Hero compact ---------- */
.hero-compact {
  min-height: auto;
  padding-block: clamp(9rem, 20vh, 12rem) clamp(3rem, 8vh, 5rem);
}

.hero-compact .hero-content {
  max-width: 720px;
}

.hero-compact h1 {
  font-size: clamp(2.25rem, 4.5vw + 0.5rem, 4rem);
}

/* ---------- Réalisations : projets vedettes ---------- */
.projects-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: clamp(1.5rem, 3vw, 2.5rem);
}

.project-card {
  display: flex;
  flex-direction: column;
  padding: 0;
}

/* Cadre repris du portail Mona Hub (apps/portal/src/pages/SiteHome.tsx,
   vue "ordinateur") : anneau clair en inset + ombres empilees plutot qu'un
   faux moniteur avec barre de titre, deja ecarte la-bas comme trop cliche.
   Couleurs adaptees a la palette nuit/lavande de cmc6 (le portail est en
   bleu marine). */
.project-card-media {
  position: relative;
  aspect-ratio: 16 / 10;
  border-radius: 18px;
  overflow: hidden;
  background: var(--night-raised);
  box-shadow: inset 0 0 0 1px var(--lavender-15), 0 10px 30px -12px rgba(0, 0, 0, 0.6),
    0 30px 60px -20px rgba(0, 0, 0, 0.55);
  transition: box-shadow 0.35s var(--ease-out);
}

.project-card:hover .project-card-media {
  box-shadow: inset 0 0 0 1px var(--lavender-30), 0 10px 30px -12px rgba(0, 0, 0, 0.7),
    0 30px 60px -18px rgba(0, 0, 0, 0.65);
}

.project-card-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: transform 0.6s var(--ease-out);
}

/* Etiquette en eyebrow au-dessus du titre : etait en surimpression sur
   l'image (position absolute), ce qui cachait un coin de chaque capture.
   Descendue dans le corps de la carte, l'image reste entierement visible. */
.project-card-tag {
  align-self: flex-start;
  padding: 0.4rem 0.85rem;
  font-size: 0.75rem;
  margin-bottom: var(--s-3);
}

.project-card-body {
  display: flex;
  flex: 1;
  flex-direction: column;
  padding: clamp(1.5rem, 3vw, 2rem);
}

.project-card-body h3 {
  font-size: clamp(1.25rem, 1.6vw, 1.5rem);
  margin-bottom: var(--s-3);
}

.project-meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2);
  margin-bottom: var(--s-4);
}

.project-meta li {
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--lavender);
  padding: 0.25rem 0.7rem;
  border: 1px solid var(--lavender-15);
  border-radius: var(--radius-pill);
}

.project-card-body p {
  color: var(--lavender);
  margin-bottom: var(--s-4);
  flex: 1;
}

.project-card-body .link-arrow {
  align-self: flex-start;
}

/* Une seule note, pas un tableau de bord : la cible (dirigeants de TPE/PME)
   ne lit pas "Navigation agentique" ou "First Contentful Paint". Le detail
   technique (4 categories Google) reste accessible via le titre au survol,
   sans jamais s'imposer a la lecture. */
.score-line {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  color: var(--lavender);
  margin: 0 0 var(--s-4);
}
.score-line span[title] {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
}
.score-line svg {
  flex-shrink: 0;
  color: var(--ember);
}
.score-line strong {
  color: var(--cream);
  font-weight: 600;
}
.score-sep {
  color: var(--lavender-dim);
}

/* Info-bulle du detail des 4 notes, au survol/focus du badge. En position:fixed
   (coordonnees posees par motion.js a l'ouverture) plutot qu'absolute : les
   cartes realisations de l'accueil defilent dans un conteneur overflow-x:auto,
   qui force aussi overflow-y (impossible d'avoir scroll-x + visible-y en CSS),
   ce qui aurait coupe une info-bulle ancree en absolu des qu'elle depasse la
   carte. fixed echappe a tout ancetre a defilement, ou qu'elle soit posee. */
.score-trigger {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  cursor: default;
  border-radius: 6px;
}
.score-trigger:focus-visible {
  outline: 2px solid var(--ember);
  outline-offset: 3px;
}
.score-tooltip {
  position: fixed;
  z-index: 50;
  display: grid;
  gap: 0.35rem;
  min-width: 190px;
  padding: 0.75rem 1rem;
  border-radius: 14px;
  background: var(--night-raised);
  border: 1px solid var(--lavender-15);
  box-shadow: 0 18px 40px -20px rgba(0, 0, 0, 0.7);
  opacity: 0;
  transform: scale(0.95) translateY(4px);
  transform-origin: bottom left;
  transition: opacity 0.15s var(--ease-out), transform 0.15s var(--ease-out);
  pointer-events: none;
}
.score-tooltip.is-visible {
  opacity: 1;
  transform: scale(1) translateY(0);
}
.score-tooltip-row {
  display: flex;
  justify-content: space-between;
  gap: 1.2rem;
  font-size: 0.8rem;
  color: var(--lavender);
  white-space: nowrap;
}
.score-tooltip-row strong {
  color: var(--ember);
  font-weight: 600;
}
@media (prefers-reduced-motion: reduce) {
  .score-tooltip { transition: opacity 0.15s var(--ease-out); }
}

/* ---------- Réalisations : témoignages ---------- */
.testimonials-wrap {
  display: flex;
  flex-direction: column;
  gap: var(--s-8);
}

.testimonial-stars {
  color: var(--ember);
  letter-spacing: 0.15em;
  margin-bottom: var(--s-3);
}

.testimonial-card p {
  color: var(--cream);
  margin-bottom: var(--s-4);
}

.testimonial-author {
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--lavender);
}

.testimonial-author small {
  font-weight: 500;
  color: var(--lavender-dim);
}

/* Avis le plus recent, mis en avant en pleine largeur (demande operateur
   2026-08-25) : l'avis Frederic Roger (~500 caracteres) rendait la grille a
   3 colonnes bancale, une carte deux fois plus haute que ses voisines dans
   la meme rangee. */
.testimonial-featured {
  max-width: 76ch;
  /* .testimonials-wrap est en colonne flex, align-items:stretch par defaut :
     un enfant plus etroit que le conteneur (a cause de max-width) reste
     colle au bord de depart au lieu d'etre centre. Constate en prod le
     2026-08-25 : 357px de vide a droite sur desktop. */
  margin-inline: auto;
}

.testimonial-featured .testimonial-stars {
  font-size: 1.05em;
}

.testimonial-featured p {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(1.15rem, 0.6vw + 1rem, 1.375rem);
  line-height: 1.5;
}

/* --------------------------------------------------------------------------
   Lisibilite de l'avis mis en avant
   --------------------------------------------------------------------------
   Mesure de l'existant : 557 caracteres, 90 mots, 9 lignes de 64 signes, en
   Cal Sans a 22px avec un interligne de 1,5.

   Le probleme n'est pas la largeur : 64 signes par ligne est une bonne
   mesure. C'est la POLICE. Cal Sans est la police d'affichage du site, dessinee
   pour des titres de quelques mots : graisse unique, lettres serrees, peu de
   contraste entre les formes. Elle porte tres bien cinq mots et fatigue sur
   quatre-vingt-dix. Les avis du bandeau, eux, sont deja en police de texte.

   On rend donc le corps de l'avis a la police de texte, avec un interligne
   plus ouvert, et on garde Cal Sans pour la seule signature. Comparable en
   direct via ?avis=actuel | texte | citation.
   -------------------------------------------------------------------------- */
html[data-avis="texte"] .testimonial-featured p,
html[data-avis="citation"] .testimonial-featured p,
html[data-avis="extrait"] .testimonial-featured p {
  font-family: var(--font-body);
  /* Un cran en dessous : la police de texte a un oeil plus grand que Cal Sans,
     elle reste aussi presente a taille legerement reduite. */
  font-size: clamp(1.0625rem, 0.45vw + 0.95rem, 1.25rem);
  /* 1,62 plutot que 1,5 : sur neuf lignes, c'est ce qui fait qu'on retrouve
     le debut de la ligne suivante sans effort. */
  line-height: 1.62;
  letter-spacing: 0.005em;
}

/* La citation reprend le reglage ci-dessus et lui ajoute un point d'entree.
   Neuf lignes sans aucun repere se lisent comme un mur ; un filet lateral et
   un guillemet donnent a l'oeil ou se poser, et signalent une parole
   rapportee plutot qu'un paragraphe de plus. */
html[data-avis="citation"] .testimonial-featured,
html[data-avis="extrait"] .testimonial-featured {
  position: relative;
  padding-left: clamp(1.1rem, 2.4vw, 2rem);
}
html[data-avis="citation"] .testimonial-featured::before,
html[data-avis="extrait"] .testimonial-featured::before {
  content: "";
  position: absolute;
  left: 0;
  /* Cale sur le padding de la carte, donc sur la ligne ou commence vraiment
     le contenu. La carte a un rayon de 28px : un filet parti de plus haut
     entre dans la zone arrondie, ou le bord gauche n'est plus vertical, et
     il donne l'impression d'etre coupe par le coin. */
  top: clamp(1.5rem, 3vw, 2.25rem);
  bottom: clamp(1.5rem, 3vw, 2.25rem);
  width: 2px;
  border-radius: 2px;
  background: linear-gradient(
    to bottom,
    var(--ember) 0%,
    color-mix(in oklab, var(--ember) 35%, transparent) 55%,
    transparent 100%
  );
}
/* --------------------------------------------------------------------------
   L'extrait : traiter la MASSE, pas seulement la lisibilite
   --------------------------------------------------------------------------
   Rendre le texte a la police de lecture le rend confortable, mais 557
   signes d'un seul tenant restent un mur : huit lignes sans respiration, et
   l'oeil renonce avant d'avoir commence. Un temoignage n'a pas besoin d'etre
   lu en entier d'emblee ; les premieres lignes portent la substance, et qui
   veut la suite la demande.

   On coupe donc a quatre lignes, avec un fondu vers le bas qui dit que le
   texte continue, et un bouton pour deplier. Le texte entier reste dans le
   document, simplement masque a l'affichage : rien n'est retire pour les
   moteurs de recherche ni pour un lecteur d'ecran, qui lit le paragraphe en
   entier.

   La coupe est purement visuelle et ne touche jamais au contenu du champ :
   le <p> porte un marqueur data-cms, et y injecter le moindre element
   casserait l'edition depuis le portail. Le bouton est ajoute APRES lui.
   -------------------------------------------------------------------------- */
html[data-avis="extrait"] .testimonial-featured p {
  position: relative;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 4;
  line-clamp: 4;
  overflow: hidden;
  /* Le fondu remplace la coupe nette, qui donne l'impression d'un texte
     tranche par accident plutot que volontairement abrege. */
  mask-image: linear-gradient(to bottom, #000 62%, transparent 100%);
}
html[data-avis="extrait"] .testimonial-featured.est-deplie p {
  -webkit-line-clamp: unset;
  line-clamp: unset;
  display: block;
  mask-image: none;
}
/* Pendant le depliage seulement : la hauteur est bornee par le script, il
   faut masquer ce qui depasse. Hors animation aucun overflow n'est impose,
   ce qui evite de couper une selection ou un anneau de focus. */
html[data-avis="extrait"] .testimonial-featured.est-en-cours p {
  overflow: hidden;
}

/* --------------------------------------------------------------------------
   L'extrait mis en avant
   --------------------------------------------------------------------------
   Un avis long suit toujours le meme ordre : le contexte d'abord (qui, quoi,
   pour quel projet), le resultat ensuite. Coupe a quatre lignes, la carte
   montrait donc trois lignes de presentations et coupait pile a l'endroit ou
   arrive le seul chiffre qui vend.

   On sort donc la phrase de resultat du corps du texte et on la pose en tete,
   dans la police d'affichage. Le recit passe en second plan, en retrait de
   couleur : la promesse se lit en premier, la preuve se lit ensuite.
   Positions comparables via ?accroche=tete | cote | non.
   -------------------------------------------------------------------------- */
.avis-accroche {
  font-family: var(--font-display);
  font-weight: 400;
  /* La taille suit la LONGUEUR de la phrase, transmise par --n depuis le
     rendu. Une taille fixe marchait pour un extrait court et cassait la ligne
     des qu'il s'allongeait : mesure faite sur celui de Frederic Roger, 70
     signes, il manquait 11 px pour tenir. La formule vise une ligne unique
     sur desktop, avec un plancher pour ne jamais devenir illisible ; sous ce
     plancher, sur mobile, la phrase repasse a deux lignes, ce qui est le bon
     compromis a 390 px de large. */
  font-size: clamp(1.05rem, calc(2.62rem - var(--n, 40) * 0.021rem), 1.5rem);
  line-height: 1.3;
  color: var(--cream);
  margin-bottom: var(--s-3);
  text-wrap: balance;
}
/* Le corps recule d'un cran des qu'une accroche le precede : sans cette
   difference de couleur, deux blocs de meme valeur se disputent l'entree de
   lecture et la hierarchie ne se voit plus. */
html[data-avis] .avis-accroche + p {
  color: var(--lavender);
}
/* Et il se resserre a trois lignes : accroche plus quatre lignes de texte
   refabriquaient le pave qu'on vient de defaire. */
html[data-avis="extrait"] .avis-accroche + p {
  -webkit-line-clamp: 3;
  line-clamp: 3;
}

/* Variante : la phrase posee sur la ligne des etoiles. La carte devient une
   grille le temps de la premiere rangee ; tout le reste reprend la pleine
   largeur. Ecartee a l'essai (la phrase se casse en deux lignes et les etoiles
   se desalignent du texte), gardee accessible par ?accroche=cote. */
@media (min-width: 700px) {
  html[data-accroche="cote"] .testimonial-featured {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    column-gap: clamp(0.9rem, 2vw, 1.5rem);
    align-items: baseline;
  }
  html[data-accroche="cote"] .testimonial-featured .testimonial-stars {
    grid-column: 1;
    grid-row: 1;
    margin-bottom: var(--s-4);
  }
  html[data-accroche="cote"] .testimonial-featured .avis-accroche {
    grid-column: 2;
    grid-row: 1;
    margin-bottom: var(--s-4);
  }
  html[data-accroche="cote"] .testimonial-featured > p:not(.avis-accroche),
  html[data-accroche="cote"] .testimonial-featured > .avis-plus,
  html[data-accroche="cote"] .testimonial-featured > .testimonial-author {
    grid-column: 1 / -1;
  }
}
/* Sous 700px la phrase repasse en tete : a cote de cinq etoiles, il ne reste
   pas de largeur utile pour une ligne de texte. */

html[data-accroche="non"] .avis-accroche { display: none; }

/* Bouton de depliage, cree par motion.js seulement si le texte deborde
   vraiment : un avis court n'a rien a deplier et ne doit pas afficher
   l'invite. */
.avis-plus {
  /* flex et non inline-flex : la signature qui suit est un <span> en ligne,
     et un bouton en ligne venait se poser juste devant elle, sur la meme
     ligne, ce qui donnait "Lire la suite v Frederic Roger". En bloc, il
     garde sa ligne ; width:fit-content evite que la zone cliquable ne
     s'etende sur toute la largeur de la carte. */
  display: flex;
  width: fit-content;
  align-items: center;
  gap: 0.4em;
  margin-top: 0.15rem;
  margin-bottom: var(--s-4);
  padding: 0;
  border: 0;
  background: none;
  color: var(--ember);
  font-family: var(--font-body);
  font-size: 0.9375rem;
  font-weight: 600;
  cursor: pointer;
  border-bottom: 1px solid transparent;
  transition: border-color 0.25s var(--ease-out);
}
.avis-plus:hover { border-bottom-color: currentColor; }
.avis-plus:focus-visible {
  outline: 2px solid var(--ember);
  outline-offset: 3px;
  border-radius: 2px;
}
/* Le chevron pivote vers le haut une fois deplie : l'etat du bloc se lit
   sans avoir a relire l'etiquette. */
.avis-plus svg {
  width: 15px;
  height: 15px;
  transition: transform 0.3s var(--ease-spring);
}
.est-deplie .avis-plus svg { transform: rotate(180deg); }

/* Deux ajouts ont ete essayes puis abandonnes, pour memoire.
   1. Un guillemet ouvrant decoratif : le texte porte deja ses propres
      chevrons dans le markup, un second signe fait doublon, et place apres
      les etoiles il se lit comme une coquille. Le filet suffit a marquer la
      parole rapportee.
   2. La signature en police d'affichage, pour garder du caractere. Mais
      .testimonial-author est en font-weight 600 et Cal Sans n'a qu'une seule
      graisse : le navigateur fabrique alors un faux gras synthetique, et le
      nom sort baveux (verifie a la mesure, "Test" fait exactement la meme
      largeur en 400 et en 600). La signature reste donc en police de texte,
      ou le 600 existe vraiment. */

.testimonial-featured .testimonial-author {
  font-size: 0.9375rem;
}

/* Les avis restants defilent en plus petit : meme mecanique que .marquee/
   .marquee-track du bandeau logos (fondus lateraux, boucle par duplication
   d'items), simplement SANS le pilotage JS a la velocite du scroll de
   motion.js, qui est scope a #marquee : le @keyframes marquee CSS suffit ici,
   coupe par la meme regle prefers-reduced-motion generique sur .marquee-track. */
.avis-marquee .marquee-track {
  align-items: stretch;
  gap: var(--s-6);
  animation-duration: 34s;
}

/* Fondu en masque d'alpha plutot qu'en degrade de couleur peinte ou en
   backdrop-filter. Le fond derriere ce bandeau (aurore mouvante, pas un
   aplat --night-deep) rendait tout degrade de couleur peinte visible comme
   une tache au lieu d'un fondu (signale par l'operateur : "ca fait un bloc
   au milieu de la page"). Le masque fait disparaitre le contenu lui-meme en
   transparence : ce qui est reellement derriere apparait, quel qu'il soit,
   sans compositing cumule avec le fond translucide de .testimonial-mini
   (meme logique deja eprouvee sur .compare-wrap plus haut dans ce fichier). */
.avis-marquee::before,
.avis-marquee::after {
  content: none;
}
.avis-marquee {
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 14%, #000 86%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 14%, #000 86%, transparent);
}

.testimonial-mini {
  flex-shrink: 0;
  width: min(340px, 82vw);
  border-radius: var(--radius);
  padding: var(--s-6);
  background: color-mix(in oklab, var(--night-deep) 70%, transparent);
  box-shadow: inset 0 0 0 1px var(--lavender-15);
  white-space: normal;
}

.testimonial-mini .testimonial-stars {
  font-size: 0.875rem;
  margin-bottom: var(--s-2);
}

/* Pas de troncature (line-clamp) : un avis coupe a 4 lignes tombait parfois
   en plein milieu de la chute d'un temoignage court (retour operateur : avis
   de Marion coupe avant sa fin, lecture bizarre). align-items:stretch sur
   .avis-marquee .marquee-track etire toutes les cartes a la hauteur du plus
   long avis de la piste : les cartes ne sont plus toutes egales, mais aucun
   avis n'est plus jamais amputé. */
.testimonial-mini p {
  color: var(--cream);
  font-size: 0.9375rem;
  line-height: 1.5;
  margin-bottom: var(--s-3);
}

.testimonial-mini .testimonial-author {
  font-size: 0.8125rem;
}

@media (max-width: 900px) {
  .testimonial-featured p {
    font-size: 1.0625rem;
  }
}

/* ---------- CTA avec avatar (bandeau de fin) ---------- */
.cta-avatar-pill {
  display: inline-flex;
  align-items: center;
  gap: var(--s-3);
  padding: 0.6rem 1.5rem 0.6rem 0.6rem;
  border-radius: var(--radius-pill);
  background: var(--cream);
  color: var(--night);
  text-decoration: none;
  transition: transform 0.3s var(--ease-out), box-shadow 0.3s var(--ease-out);
}

.cta-avatar-pill:hover {
  transform: translateY(-2px);
  box-shadow: 0 10px 36px var(--ember-glow);
}

.cta-avatar-pill:active {
  transform: translateY(0);
}

.cta-avatar-pill img {
  width: 44px;
  height: 44px;
  border-radius: 50%;
  object-fit: cover;
  flex-shrink: 0;
}

.cta-avatar-pill span {
  display: flex;
  flex-direction: column;
  line-height: 1.3;
}

.cta-avatar-pill strong {
  font-size: 1rem;
  font-weight: 700;
}

.cta-avatar-pill small {
  font-size: 0.8125rem;
  font-weight: 500;
  color: var(--night);
  opacity: 0.65;
}

/* ---------- Réalisations : autres missions ---------- */
.missions {
  background: var(--night-deep);
}

.missions h2 {
  font-size: var(--text-h2);
  max-width: 20ch;
  margin-bottom: var(--s-4);
}

.missions-intro {
  color: var(--lavender);
  max-width: 56ch;
  margin-bottom: var(--s-12);
}

/* Deux surfaces consecutives ne se touchent jamais. Sans cette regle, des
   cartes empilees hors grille se collaient bord a bord et se lisaient comme un
   seul pave (constate dans la section Imbrications de /charte, trois cartes a
   0px d'ecart, et sur les blocs de resultats).
   Le selecteur exclut les parents en grille : la grille pose deja son propre
   gap, et une marge en plus le doublerait.
   .mission-row n'est PAS dans la liste : sa densite est voulue, ses rangees se
   separent par une bordure haute, pas par du vide. */
:not(.pledges-grid):not(.days):not(.projects-grid):not(.marquee-track):not(.results-grid):not(.blog-grid) > .glass-card + .glass-card,
:not(.pledges-grid) > .glass-card + .callout,
:not(.pledges-grid) > .callout + .glass-card,
.callout + .callout,
.result + .result,
.testimonial-card + .testimonial-card {
  margin-top: var(--s-6);
}

.mission-row {
  display: grid;
  grid-template-columns: 1fr 2fr auto;
  gap: var(--s-6);
  align-items: baseline;
  padding-block: var(--s-6);
  border-top: 1px solid var(--lavender-15);
}

.mission-row:last-of-type {
  border-bottom: 1px solid var(--lavender-15);
}

.mission-row h3 {
  font-size: 1.125rem;
}

.mission-row p {
  color: var(--lavender);
  font-size: 0.9375rem;
}

.mission-row .mission-year {
  font-size: var(--text-label);
  font-weight: 600;
  letter-spacing: 0.08em;
  color: var(--lavender-dim);
}

/* ---------- Blog : grille d'articles ---------- */
.blog-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--s-6);
}

@media (max-width: 860px) {
  .blog-grid {
    grid-template-columns: 1fr;
  }
}

.post-card {
  display: flex;
  flex-direction: column;
  text-decoration: none;
}

.post-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-3);
  color: var(--lavender-dim);
  font-size: 0.875rem;
  margin-bottom: var(--s-4);
}

.post-tag {
  font-size: var(--text-label);
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--cream);
  background: var(--lavender-15);
  border-radius: var(--radius-pill);
  padding: 0.3rem 0.8rem;
}

.post-card h2,
.post-card h3 {
  font-size: clamp(1.25rem, 1.8vw, 1.5rem);
  margin-bottom: var(--s-3);
}

.post-card p {
  color: var(--lavender);
  margin-bottom: var(--s-4);
  flex: 1;
}

.post-more {
  font-weight: 600;
  color: var(--cream);
  border-bottom: 1px solid var(--lavender-30);
  align-self: flex-start;
  transition: border-color 0.25s var(--ease-out), color 0.25s var(--ease-out);
}

@media (hover: hover) {
  .post-card:hover .post-more {
    color: var(--ember);
    border-color: var(--ember);
  }
}

/* ---------- Méthode : déroulé jour par jour ---------- */
.days {
  max-width: 880px;
  position: relative;
  padding-left: clamp(2.5rem, 6vw, 4rem);
}

.days::before {
  content: "";
  position: absolute;
  left: 8px;
  top: 12px;
  bottom: 12px;
  width: 1px;
  background: linear-gradient(180deg, var(--ember), var(--magenta), var(--lavender-30));
}

.day {
  position: relative;
  /* Meme raison que .result : le texte flottait sur l'aurore et perdait son
     contraste sur les zones vives. Le sol est plus discret ici, le bloc de
     jour restant une etape de sequence et non une carte. */
  padding: clamp(1.25rem, 3vh, 1.75rem) clamp(1.25rem, 2.5vw, 1.75rem)
    clamp(1.5rem, 3.5vh, 2rem);
  background: color-mix(
    in oklab,
    var(--night-deep) calc(var(--bloc-opacite, 0.55) * 100%),
    transparent
  );
  border-radius: var(--radius);
  margin-bottom: clamp(1rem, 2.5vh, 1.5rem);
}

.day::before {
  content: "";
  position: absolute;
  left: calc(-1 * clamp(2.5rem, 6vw, 4rem) + 3px);
  top: 8px;
  width: 11px;
  height: 11px;
  border-radius: 50%;
  background: var(--ember);
}

.day-label {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 1.25rem;
  color: var(--ember);
  margin-bottom: var(--s-2);
}

.day h3 {
  font-size: var(--text-h3);
  margin-bottom: var(--s-2);
}

.day p {
  color: var(--lavender);
  max-width: 58ch;
}

.day ul {
  margin-top: var(--s-3);
  color: var(--lavender);
  display: grid;
  gap: var(--s-2);
}

.day ul li {
  padding-left: 1.2em;
  position: relative;
}

.day ul li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.62em;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--lavender-30);
}

/* Encadré attentes client */
.callout {
  border: 1px solid var(--lavender-15);
  border-radius: var(--radius);
  padding: clamp(1.5rem, 4vw, 2.5rem);
  max-width: 880px;
  /* Meme principe que .glass-card : --encart-opacite, defaut 1. */
  background: color-mix(
    in oklab,
    var(--night-raised) calc(var(--encart-opacite, 1) * 100%),
    transparent
  );
}

.callout h2 {
  font-size: 1.375rem;
  margin-bottom: var(--s-3);
}

.callout p {
  color: var(--lavender);
}

/* ---------- Offres ---------- */
.formulas {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(1.25rem, 3vw, 2rem);
  align-items: stretch;
}

.formula {
  /* Voile indispensable : ces cartes n'avaient AUCUN fond, le degrade anime de la page
     passait donc directement derriere le texte. Mesure au pixel sur la page en ligne, meme
     paragraphe, 6 instants du cycle : le contraste oscillait entre 2.14 et 6.28. Le texte
     redevenait illisible a chaque passage clair du degrade, ce qu'une mesure isolee ne
     montre jamais - la premiere correction, sur la couleur du texte seule, ne pouvait pas
     y suffire.
     .64 tient a 5.08 au pire sur 7 instants, avec de la marge, sans effacer l'effet de
     verre (.55 passait de justesse a 4.62, trop court pour un cycle dont on ignore la
     periode exacte). */
  background: rgba(23, 16, 34, 0.64);
  border: 1px solid var(--lavender-15);
  border-radius: var(--radius);
  padding: clamp(1.5rem, 3vw, 2.25rem);
  display: flex;
  flex-direction: column;
  gap: var(--s-4);
  transition: transform 0.35s var(--ease-out), border-color 0.35s var(--ease-out),
    box-shadow 0.35s var(--ease-out);
}

.formula:hover {
  border-color: var(--lavender-30);
  box-shadow: 0 24px 60px -32px rgba(0, 0, 0, 0.85);
}

.formula.featured:hover {
  border-color: var(--ember);
  box-shadow: 0 24px 60px -28px var(--ember-glow);
}

.formula.featured {
  border-color: var(--ember);
  background: var(--night-raised);
  position: relative;
}

.formula .formula-badge {
  position: absolute;
  top: -12px;
  left: clamp(1.5rem, 3vw, 2.25rem);
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  background: var(--ember);
  color: var(--cream);
  padding: 0.25rem 0.75rem;
  border-radius: var(--radius-pill);
}

.formula h2 {
  font-size: 1.5rem;
}

.formula .formula-sub {
  /* Pas --lavender-dim (#93829f) : mesure au pixel sur la page en ligne, ces trois
     descriptions tombaient a 3.41 / 3.93 / 4.11 en mode nuit, deja sous le seuil AA de
     4.5, et la premiere s'effondrait a 1.57 en mode jour, ou le verre s'eclaircit et
     sature. Illisible sur la page qui vend les formules.
     #b9aec4 tient dans les DEUX modes (pire cas 4.76). Les valeurs plus claires font
     baisser le ratio au lieu de le monter : en mode jour le fond de la premiere carte
     devient clair, et un texte pale sur fond pale se perd. Teste en direct dans le
     navigateur avant deploiement, pas apres. */
  color: #b9aec4;
  font-size: 0.9375rem;
  min-height: 2.6em;
}

.formula ul {
  display: grid;
  gap: var(--s-3);
  color: var(--lavender);
  font-size: 0.9375rem;
  flex-grow: 1;
}

.formula ul li {
  padding-left: 1.4em;
  position: relative;
}

.formula ul li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.45em;
  width: 8px;
  height: 8px;
  border-radius: 2px;
  background: var(--ember);
}

.formula .btn {
  align-self: flex-start;
}

/* Tableau comparatif */
.compare-hint {
  display: none;
}
.compare-wrap {
  overflow-x: auto;
  border: 1px solid var(--lavender-15);
  border-radius: var(--radius);
}
@media (max-width: 700px) {
  /* Le tableau garde son min-width (640px) sous ce seuil : la scrollbar
     horizontale existe (overflow-x:auto) mais sans indice, rien ne montre
     qu'il y a 3 colonnes cachees. Fondu sur le bord droit (mask, pas un
     degrade peint : le fond derriere le cadre de verre est translucide,
     un aplat de couleur jurerait) + phrase explicite au-dessus. */
  .compare-hint {
    display: block;
    font-size: 0.8125rem;
    color: var(--lavender-dim);
    margin-bottom: var(--s-3);
  }
  .compare-wrap {
    -webkit-mask-image: linear-gradient(to right, black calc(100% - 28px), transparent 100%);
    mask-image: linear-gradient(to right, black calc(100% - 28px), transparent 100%);
  }
  /* Colonne des libelles figee pendant le swipe horizontal : sans ca, une
     fois la colonne "Achat" affichee, les libelles de ligne (Paiement,
     Livraison...) defilent hors champ eux aussi et on perd le repere de
     quelle ligne on compare (signale par l'operateur). Fond obligatoire
     (sinon les autres colonnes glisseraient visiblement sous la sticky). */
  .compare th:first-child {
    position: sticky;
    left: 0;
    background: var(--night-raised);
    z-index: 1;
  }
}

table.compare {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9375rem;
  min-width: 640px;
}

.compare th,
.compare td {
  padding: 1rem 1.25rem;
  text-align: left;
  border-bottom: 1px solid var(--lavender-08);
}

.compare thead th {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 1rem;
  background: var(--night-raised);
}

.compare tbody th {
  font-weight: 600;
  color: var(--lavender);
  width: 34%;
}

.compare td {
  color: var(--cream);
}

.compare tbody tr:last-child th,
.compare tbody tr:last-child td {
  border-bottom: none;
}

.compare tbody tr {
  transition: background-color 0.2s var(--ease-out);
}

.compare tbody tr:hover {
  background: var(--lavender-08);
}

.compare .yes {
  color: var(--ember);
  font-weight: 700;
}

.compare .muted {
  color: var(--lavender-dim);
}

/* ---------- Audit gratuit (landing) ---------- */
.audit-points {
  max-width: 880px;
  counter-reset: pt;
}

.audit-points li {
  counter-increment: pt;
  display: grid;
  grid-template-columns: 72px 1fr;
  gap: var(--s-6);
  align-items: baseline;
  padding-block: var(--s-6);
  border-top: 1px solid var(--lavender-15);
}

.audit-points li:last-child {
  border-bottom: 1px solid var(--lavender-15);
}

.audit-points li::before {
  content: "0" counter(pt);
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 2rem;
  color: transparent;
  -webkit-text-stroke: 1px var(--lavender-30);
}

.audit-points strong {
  display: block;
  font-family: var(--font-display);
  font-size: 1.25rem;
  letter-spacing: -0.01em;
  margin-bottom: var(--s-2);
}

.audit-points p {
  color: var(--lavender);
  max-width: 52ch;
}

/* ---------- Prose légale ---------- */
.prose {
  max-width: 72ch;
}

.prose h2 {
  font-size: 1.5rem;
  margin-top: var(--s-12);
  margin-bottom: var(--s-4);
}

.prose h2:first-of-type {
  margin-top: 0;
}

/* Sous-titre ponctuel dans un bloc .prose (ex. "Le plan de recherche est
   pose au jour 5" au fil d'une section /creation-site-internet-montpellier) :
   sans cette regle un h3 nu herite de la taille par defaut du navigateur,
   plus petite que --text-h3 utilise partout ailleurs sur le site. */
.prose h3 {
  font-size: var(--text-h3);
  margin-top: var(--s-8);
  margin-bottom: var(--s-2);
}

.prose p,
.prose li {
  color: var(--lavender);
  margin-bottom: var(--s-3);
}

.prose a {
  color: var(--cream);
}

.prose .todo {
  color: var(--ember);
  font-weight: 600;
}

/* ---------- 404 ---------- */
.error-hero {
  min-height: 100svh;
  display: flex;
  align-items: center;
}

.error-hero .error-code {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(8rem, 22vw, 18rem);
  line-height: 0.9;
  letter-spacing: -0.04em;
  color: transparent;
  -webkit-text-stroke: 2px var(--lavender-30);
  display: block;
}

.error-hero h1 {
  font-size: var(--text-h2);
  margin-block: var(--s-6);
}

.error-hero p {
  color: var(--lavender);
  margin-bottom: var(--s-8);
  max-width: 46ch;
}

/* ---------- Bandeau CTA (pages internes) ---------- */
.cta-band {
  position: relative;
  overflow: hidden;
  background: var(--night-deep);
}

/* Bandes pleine largeur du cadre de verre.
   Une section qui porte un fond plein doit aller d'un bord a l'autre du cadre,
   sinon son fond s'arrete avant la bordure et la bande a l'air posee de
   travers. Elles annulent donc le coussin horizontal du parent au lieu de s'y
   inscrire. Le contenu, lui, reste dans son .container.
   Le rayon du cadre les decoupe naturellement (overflow: clip du parent), ce
   qui arrondit proprement une bande de fin de page. */
.cta-band,
.pledges {
  margin-inline: calc(-1 * var(--glass-pad-x, 0px));
  width: calc(100% + 2 * var(--glass-pad-x, 0px));
}

.cta-band::before {
  content: "";
  position: absolute;
  inset: 0;
  /* L'orbe cramoisi au centre droit a ete retire (demande operateur) : il
     posait une deuxieme source de rouge dans un champ de vision qui contient
     deja le bouton principal, et les deux se neutralisaient. Reste le halo
     navy en bas a gauche, qui donne de la profondeur sans concurrencer
     l'accent. */
  background: radial-gradient(45% 60% at 10% 90%, var(--shape-navy) 0%, transparent 65%);
  opacity: 0.35;
}

.cta-band .container {
  position: relative;
  z-index: 1;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-6);
}

.cta-band h2 {
  font-size: clamp(1.75rem, 3vw, 2.5rem);
  max-width: 22ch;
}

.cta-band .cta-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-4);
}

/* ---------- Responsive pages internes ---------- */
@media (max-width: 860px) {
  .projects-grid {
    grid-template-columns: 1fr;
  }

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

  .mission-row {
    grid-template-columns: 1fr;
    gap: var(--s-2);
  }

  .audit-points li {
    grid-template-columns: 52px 1fr;
    gap: var(--s-4);
  }
}

/* ==========================================================================
   CTA audit glassmorphisme (porté du concept React "concept")
   Bouton qui se déplie en capture email inline. Le backdrop-filter capte
   les couleurs du fond animé du hero. Bordure degradee masquee facon
   liquid-glass. PE : sans JS, le bouton reste un lien vers /audit-gratuit.
   ========================================================================== */
.audit-cta {
  position: relative;
  display: flex;
  align-items: center;
  min-height: 52px;
}

/* Coquille glass commune au bouton et au formulaire */
.audit-glass {
  position: relative;
  overflow: hidden;
  border: none;
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.04);
  -webkit-backdrop-filter: blur(16px) saturate(160%);
  backdrop-filter: blur(16px) saturate(160%);
  box-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.12);
}
.audit-glass::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 1.4px;
  background: linear-gradient(
    180deg,
    rgba(255, 255, 255, 0.5) 0%,
    rgba(255, 255, 255, 0.16) 22%,
    rgba(255, 255, 255, 0) 45%,
    rgba(255, 255, 255, 0) 55%,
    rgba(255, 255, 255, 0.16) 78%,
    rgba(255, 255, 255, 0.5) 100%
  );
  -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
          mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  pointer-events: none;
}

/* Bouton */
.audit-glass-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  padding: 0.95rem 2rem;
  font-family: var(--font-body);
  font-size: 1rem;
  font-weight: 600;
  color: var(--cream);
  text-decoration: none;
  cursor: pointer;
  transition: transform 0.3s var(--ease-out), box-shadow 0.3s var(--ease-out);
}
.audit-glass-btn:hover {
  transform: translateY(-2px);
  box-shadow: 0 10px 36px rgba(0, 0, 0, 0.28), inset 0 1px 1px rgba(255, 255, 255, 0.2);
}
.audit-glass-btn:active { transform: translateY(0); }

/* Formulaire */
.audit-glass-form {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  width: min(100%, 360px);
  padding: 0.4rem 0.4rem 0.4rem 1.25rem;
}
.audit-glass-form input {
  flex: 1;
  min-width: 0;
  background: transparent;
  border: none;
  outline: none;
  color: var(--cream);
  font-family: var(--font-body);
  font-size: 1rem;
  letter-spacing: 0.01em;
}
.audit-glass-form input::placeholder { color: rgba(247, 242, 247, 0.5); }
.audit-glass-form input:read-only { cursor: default; }

.audit-glass-submit {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border: none;
  border-radius: 50%;
  background: var(--ember);
  color: #fff;
  cursor: pointer;
  transition: background-color 0.3s var(--ease-out), transform 0.2s var(--ease-out);
}
.audit-glass-submit:hover { background: oklch(64% 0.235 18); transform: scale(1.05); }
.audit-glass-submit:active { transform: scale(0.96); }
.audit-glass-submit svg { width: 18px; height: 18px; }

/* Bascule bouton <-> formulaire */
.audit-state { animation: auditPop 0.2s var(--ease-out); }
@keyframes auditPop {
  from { opacity: 0; transform: scale(0.95); }
  to   { opacity: 1; transform: scale(1); }
}

@media (max-width: 860px) {
  .hero-actions .audit-cta { width: 100%; }
  .audit-glass-btn { width: 100%; justify-content: center; }
  .audit-glass-form { width: 100%; }
}

@media (prefers-reduced-motion: reduce) {
  .audit-state { animation: none; }
  .audit-glass-btn:hover,
  .audit-glass-submit:hover,
  .audit-glass-submit:active { transform: none; }
}

/* Liens preuve sur la page audit : rangee compacte de sites clients */
.audit-proof-links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-3) var(--s-6);
  margin-block: var(--s-4) var(--s-6);
}
.audit-proof-links a {
  font-weight: 600;
  color: var(--cream);
  text-decoration: none;
  border-bottom: 1px solid var(--lavender-30);
  padding-bottom: 2px;
  transition: color 0.25s var(--ease-out), border-color 0.25s var(--ease-out);
}
.audit-proof-links a:hover {
  color: var(--ember);
  border-color: var(--ember);
}

/* ==========================================================================
   Formes du hero : rendues par <HeroShapes /> (src/components/HeroShapes.astro),
   inserees UNE SEULE FOIS dans Layout.astro (.page-aurora), PAS dans chaque
   .hero-shell. Verifie en navigateur reel sur le live (scroll jusqu'au footer,
   plusieurs captures) : l'aurore est un calque FIXE plein ecran qui reste
   visible derriere TOUTE la page, du hero jusqu'au footer (cf. .section-2
   .blueblur.home { margin:0 70px } dans webflow.css : ce sont les marges qui
   laissent l'aurore fixe deborder sur les cotes, pas un fond confine au hero).
   ========================================================================== */
.page-aurora {
  position: fixed;
  inset: 0;
  z-index: -1;
  height: 100dvh;
  background: #1b1b1b;
  overflow: hidden;
  pointer-events: none;
}
.page-aurora .hero-shapes,
.page-aurora .background-noise { position: absolute; inset: 0; }

@keyframes rotorTranslate {
  0% { translate: 0 0; animation-timing-function: cubic-bezier(0.455, 0.03, 0.515, 0.955); } /* In Out Quad */
  49.06% { /* t=13s */ translate: 74vw 39vw; animation-timing-function: cubic-bezier(0.455, 0.03, 0.515, 0.955); }
  98.11%, 100% { /* t=26s, retour a l'origine, tenu jusqu'a la fin */ translate: 0 0; }
}
@keyframes rotorRotate {
  0% { rotate: -180deg; animation-timing-function: linear; }
  49.06% { /* t=13s */ rotate: 0deg; animation-timing-function: linear; }
  /* 180deg et -180deg sont la meme orientation : pas de palier intermediaire
     ici (contrairement a une version precedente) qui forcait CSS a interpoler
     un tour complet en ~50ms a chaque boucle - c'etait le flash signale par
     l'operateur. Le retour a -180deg au prochain 0% est deja instantane et
     sans effet visuel, gratuit via le bouclage natif de l'animation infinie. */
  98.11%, 100% { /* t=26s, tenu jusqu'a la fin */ rotate: 180deg; }
}
@keyframes rotorScale {
  0% { scale: 1.3 1.3; animation-timing-function: linear; }
  49.06% { /* t=13s */ scale: 1 1; animation-timing-function: linear; }
  98.11%, 100% { /* t=26s, tenu jusqu'a la fin */ scale: 1.3 1.3; }
}

/* Le footer est un aplat sombre UNI (pas de transparence sur l'aurore),
   cf. .footer { background: var(--night-deep) } pose plus haut. Il etait gris
   clair (--footer-bg) avant la refonte du 2026-09-10. */

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

/* ==========================================================================
   Transitions de page (View Transitions API, navigation cross-document).
   L'API anime un SNAPSHOT (image), pas le DOM live : un transform ici ne casse
   donc pas les elements position:fixed (cf piege connu). La nav est nommee pour
   PERSISTER pendant la transition (le contenu glisse en fondu, la nav reste).
   Progressive enhancement : navigation normale si non supporte.
   ========================================================================== */
@view-transition { navigation: auto; }

.nav { view-transition-name: cmc-nav; }

@media (prefers-reduced-motion: no-preference) {
  /* L'ancienne page reste OPAQUE dessous (juste un leger recul de profondeur) :
     pas de fondu vers 0, donc pas de creux de transparence ni d'artefact.
     La nouvelle page monte en fondu PAR-DESSUS. */
  ::view-transition-old(root) {
    animation: cmcPageOut 0.34s var(--ease-out) both;
    z-index: 1;
  }
  ::view-transition-new(root) {
    animation: cmcPageIn 0.42s var(--ease-spring, cubic-bezier(0.22,1,0.36,1)) both;
    z-index: 2;
  }
}
@keyframes cmcPageOut { to { transform: scale(0.992); } }
@keyframes cmcPageIn { from { opacity: 0; transform: translateY(12px); } }

/* reduced-motion : coupe toute animation de transition de page (cut instantane) */
@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) { animation: none !important; }
}

/* ==========================================================================
   CADRE DE VERRE (le "truc particulier" du live qui contient tout le corps
   de page) : transcription de .container-8.home.accueil du webflow.css
   source (border 5px --blur-1 #ffffff40, backdrop-filter saturate(134%)
   blur(46px), fond degrade sombre translucide 147deg, radius 250px herite
   de .home, marges laterales 70px). L'aurore fixe reste visible autour ET
   a travers (saturation) du cadre.
   ========================================================================== */
.glass-frame {
  position: relative;
  z-index: 2;
  /* Retour operateur : "le bloc de contenu dans le verre flottant passe un
     peu au-dessus du contenu au-dessus de lui". Verifie sur le live que ce
     N'EST PAS une parallaxe liee au scroll (position mesuree = flow normal,
     ecart <1px) : c'est un chevauchement STATIQUE, marge haute negative +
     z-index superieur, le bord flou du cadre couvre legerement le bas de
     la section precedente (.proof). Renforce a -120px (etait -56px) : le
     petit debattement d'entree (motion.js, 90px) ne suffisait pas a lui
     seul a donner l'impression que le cadre passe par-dessus. */
  /* margin-top pilote par motion.js (anime au scroll) ; valeur ici = repli
     si le JS ne charge pas. -120px etait trop ("un petit peu trop haut"). */
  margin-top: -70px;
  margin-bottom: 60px;
  margin-inline: auto;
  /* Largeur plafonnee (retour operateur : "je l'avais mis en max width 1440"),
     centree ; --glass-gutter de gouttiere tant que l'ecran est plus etroit. */
  width: calc(100% - (var(--glass-gutter) * 2));
  max-width: var(--glass-max-width);
  border: 5px solid #ffffff40;
  /* Coins moins ronds, façon icone iOS (l'ancien 250px faisait un dome/pilule
     en haut du cadre, pas ce que l'operateur avait fait a l'origine). */
  border-radius: var(--glass-radius);
  /* Deux couches. Au-dessus, une teinte pleine et reglable depuis /charte
     (--cadre-teinte, une couleur de la charte ; --cadre-teinte-opacite, sa
     transparence). Par defaut l'opacite vaut 0, donc la couche n'existe pas et
     le rendu est celui d'avant. En dessous, le degre sombre d'origine. */
  background-image: linear-gradient(
      0deg,
      color-mix(
          in oklab,
          var(--cadre-teinte, transparent) calc(var(--cadre-teinte-opacite, 0) * 100%),
          transparent
        )
        0%,
      color-mix(
          in oklab,
          var(--cadre-teinte, transparent) calc(var(--cadre-teinte-opacite, 0) * 100%),
          transparent
        )
        100%
    ),
    linear-gradient(299deg, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.21));
  -webkit-backdrop-filter: saturate(var(--glass-saturate)) blur(var(--glass-blur));
  backdrop-filter: saturate(var(--glass-saturate)) blur(var(--glass-blur));
  /* Coussin horizontal expose en variable : la bande de conversion s'en sert
     pour ressortir en pleine largeur du cadre (marges negatives egales). */
  --glass-pad-x: 30px;
  padding: 90px var(--glass-pad-x) 20px;
  /* clip et pas hidden : ne casse pas un position:sticky descendant */
  overflow: clip;
}
@media (max-width: 1100px) {
  /* --glass-gutter (pas margin-left/right) : la largeur de base est calculee
     depuis cette variable (calc(100% - gutter*2)), donc la reduire ici garde
     le centrage margin-inline:auto herite. Un raccourci margin: ... ici
     ecraserait cet auto par une valeur fixe et decentrerait le cadre (bug
     constate sur mobile : 90px de vide a droite pour 10px a gauche). */
  .glass-frame { --glass-gutter: 20px; margin-top: 30px; margin-bottom: 40px; border-width: 4px; border-radius: 66px; }
}
@media (max-width: 767.98px) {
  .glass-frame {
    --glass-gutter: 10px;
    /* Negatif comme en desktop (-70px) : c'est ce qui fait passer tout le
       bloc PAR-DESSUS le hero, l'effet que l'operateur voulait retrouver.
       Etait a +20px, soit un vide au lieu d'un chevauchement. Amplitude
       reduite par rapport au desktop : le hero mobile est bien plus court,
       70px y mangeraient la rangee de logos qui le precede. */
    margin-top: -34px;
    margin-bottom: 30px;
    border-radius: 30px;
    --glass-pad-x: 5px;
    padding: 10px var(--glass-pad-x);
    /* Le live coupe le blur sur mobile et ne garde que la saturation ; on le
       remet a la demande de l'operateur, au meme rayon que le desktop
       (--glass-blur, 4px). Rayon volontairement conserve tel quel et pas
       augmente : .page-aurora est en position:fixed ET animee derriere toute
       la page, donc le fond vu par ce filtre change a chaque image meme sans
       defilement, et le cout d'un backdrop-filter monte vite avec le rayon.
       La saturation reste a 134% (valeur mobile du live), plus basse que les
       180% du desktop. */
    -webkit-backdrop-filter: saturate(134%) blur(var(--glass-blur));
    backdrop-filter: saturate(134%) blur(var(--glass-blur));
  }
}

/* 3 tentatives (blur a 0, backdrop-filter retire, cadre rendu opaque) toutes
   annulees le 31/08 : aucune n'a change la saccade Safari mobile signalee le
   29/08, donc le flou/la translucidite du cadre n'etait pas la cause. Cout
   visuel (perte de l'effet verre) sans aucun benefice, retire entierement.
   Voir project_cmc6_safari_glassframe_jank_2026_08_29.md pour la suite. */

/* Piste separee, DESKTOP UNIQUEMENT (>=768px, comme .hs-rotor.play plus
   bas) : la video envoyee par l'operateur montre le bug sur MOBILE Safari,
   ou cette animation ne tourne meme pas (gate en @media min-width:768px) -
   elle n'explique donc PAS ce cas precis. Gardee telle quelle par prudence
   pour le desktop Safari (non teste, non signale, mais le meme genre de
   cout GPU pourrait s'y appliquer) : .hs-rotor (forme geante, degrade)
   anime translate/rotate/scale separement (3 easings differents d'origine
   Webflow), des proprietes plus recentes que `transform` et moins bien
   composees par Safari. Repasse par un unique transform combine pour
   Safari seulement, quitte a perdre la nuance des 3 courbes (le
   cubic-bezier de la translation s'applique alors aux trois). */
@supports (-webkit-hyphens: none) {
  @keyframes rotorSafari {
    0% { transform: translate(0, 0) rotate(-180deg) scale(1.3); animation-timing-function: cubic-bezier(0.455, 0.03, 0.515, 0.955); }
    49.06% { transform: translate(74vw, 39vw) rotate(0deg) scale(1); animation-timing-function: cubic-bezier(0.455, 0.03, 0.515, 0.955); }
    98.11%, 100% { transform: translate(0, 0) rotate(180deg) scale(1.3); }
  }
  .hs-rotor {
    translate: none;
    rotate: none;
    scale: none;
    transform: translate(0, 0) rotate(-180deg) scale(1.3);
  }
  @media (min-width: 768px) {
    .hs-rotor.play {
      animation: rotorSafari 26.5s infinite;
    }
  }
}

/* ==========================================================================
   FIDELITE LIVE round 3 : hero centre + coches, stats en gros chiffres nus,
   FAQ en rangees crimson (transcrit des captures live vp-0 / vp-2700).
   ========================================================================== */

/* --- Hero centre comme le live (eyebrow + titre court + coches + CTA) --- */
.hero-inner { width: 100%; }
.hero-content {
  margin-inline: auto;
  max-width: 880px;
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
}
.hero-content .lead { margin-inline: auto; max-width: 62ch; }
.hero-actions { justify-content: center; margin-top: var(--s-8); }
/* Titre a l'echelle du live (~2.9vw, moitie de notre ancien 5.25rem) */
:root { --text-hero: clamp(2rem, 2.9vw + 0.4rem, 3.3rem); }
/* Meuble de la refonte absent du live : numeral géant en filigrane */
.hero-numeral { display: none; }
/* .hero-trust EST sur le live (mesure : "En ligne depuis 2016 : logos" tient
   dans le premier ecran, juste sous le CTA). Retire le display:none pose
   par erreur au round 3 ("le marquee suffit" : faux, le live a les deux). */

/* Coches façon live (Branding / Design / Intégration / SEO) */
.hero-checks {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.6rem 1.6rem;
  list-style: none;
  padding: 0;
  margin-block: var(--s-4);
  color: var(--cream);
  font-size: 0.95rem;
}
.hero-checks li { display: inline-flex; align-items: center; gap: 0.5em; }
.hero-checks li::before {
  content: "";
  width: 0.85em;
  height: 0.85em;
  border-radius: 3px;
  background: #d9d9d9 center / 70% no-repeat
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23171022' d='M9 16.2 4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4z'/%3E%3C/svg%3E");
}

/* --- Stats : gros chiffres nus sur l'aurore, separes par filets verticaux --- */
#resultats .results-grid { align-items: start; }
#resultats .glass-card.result-card {
  /* .glass-card rescope --cream en sombre pour les cartes beiges (contraste
     texte-sur-beige) : cette carte est transparente sur l'aurore, il lui
     faut l'inverse, sinon le degrade du compteur a rouleaux (var(--cream))
     devient noir sur noir. */
  --cream: #f7f2f7;
  background: transparent;
  box-shadow: none;
  text-align: center;
  padding-block: var(--s-4);
}
.icon-list {
  list-style: none;
}
.icon-list li {
  display: flex;
  align-items: flex-start;
  gap: 0.6rem;
}
.icon-list li svg {
  flex-shrink: 0;
  margin-top: 0.15rem;
  color: var(--ember);
}
.icon-list li:not(:has(svg))::before {
  content: "";
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--ember);
  opacity: 0.3;
  flex-shrink: 0;
  margin-top: 0.3rem;
}

#resultats .result-icon {
  display: inline-flex;
  margin-bottom: var(--s-3);
}
#resultats .result-card .metric {
  font-family: var(--font-display);
  font-size: clamp(3rem, 5vw, 4.5rem);
  line-height: 1;
  color: #fff;
  margin-bottom: var(--s-3);
}
#resultats .result-card h3 { color: #fff4f0; font-size: 1.05rem; }
#resultats .result-card p { color: #e3d9ea; font-size: 0.95rem; }
#resultats .result-card .src { color: rgba(255, 255, 255, 0.55); }
@media (min-width: 861px) {
  #resultats .result-card + .result-card { border-left: 1px solid rgba(255, 255, 255, 0.25); border-top: none; }
}

/* --- FAQ : rangees crimson pleine largeur, chevron a droite (live) --- */
/* L'accordeon est entierement defini dans styles-fx.css, charge apres celui-ci.
   Toute regle posee ici serait morte : c'est le piege qui a deja coute une
   correction sur le fond des cartes de verre. Voir la section Imbrications de
   /charte. */


/* Cartes beiges : les tokens globaux sont clair-sur-sombre, donc tout texte
   en var(--cream)/var(--lavender) devient creme-sur-beige = invisible (vu en
   capture : titres des cartes engagements illisibles). On rescope des tokens
   SOMBRES a l'interieur de chaque carte beige. Les stats (#resultats), cartes
   transparentes sur l'aurore, sont en couleurs litterales et ne bougent pas. */
/* La carte basculait ici tout son jeu de variables en sombre, parce qu'elle
   posait un fond beige plein : texte sombre sur fond clair. Depuis que son
   fond est translucide (--card-opacite 0.18), ce fond clair n'existe plus et
   c'est l'aurore sombre qu'on voit a travers : le texte sombre y devenait
   illisible. La carte herite donc du jeu clair de la page, comme le reste du
   site. Si le fond redevient un jour plein et clair, il faudra remettre cette
   bascule ET la retirer du panneau de /charte, les deux vont ensemble. */
