/* ============================================================
   Covaria — feuille de style

   Direction artistique inspirée d'Anthropic et Ogury :
   - une seule famille de caractères (Inter), pas de serif dans la page
   - le blanc domine, les sections se séparent par le VIDE et non par
     des fonds colorés ou des bordures épaisses
   - aucune carte : ni ombre, ni bordure arrondie, ni fond gris.
     Les blocs sont délimités par des filets d'un pixel
   - alignement à gauche sur une largeur de lecture contrainte
   - les violets du logo servent d'accent, jamais de décor

   Fraunces reste dans le logo uniquement : c'est le seul élément
   éditorial du site, donc il se remarque davantage.
   ============================================================ */

:root {
  /* Violets du logo. Chaque niveau a un rôle, sinon l'échelle se dilue :
       900  case dense du logo, et fin du dégradé du nuage
       700  action : liens, focus, survol des boutons
       500  entrée du dégradé du nuage
       300  rappel : motifs, micro-matrices, accents discrets
       100  fond teinté */
  --violet-100: #EDEBFA;
  --violet-300: #AFA9EC;
  --violet-500: #7F77DD;
  --violet-700: #534AB7;
  --violet-900: #372F86;

  /* Neutres : le blanc domine, le crème n'est qu'un accent rare.
     Les trois encres forment une échelle de retrait, et chacune doit rester
     lisible : elles portent toutes du texte, jamais du décor.
       ink        17.7:1  corps et titres
       ink-soft    7.8:1  chapeaux, descriptions
       ink-faint   5.4:1  surtitres, libellés de champs, mentions

     ink-faint valait #8A857D, soit 3.66:1 sur blanc — sous le minimum de
     4.5:1 du WCAG AA, et appliqué précisément aux textes les plus petits
     de la page (12 à 14,5 px) : surtitres, libellés du formulaire, rôle
     des fondateurs, mentions du pied de page. C'est là que le contraste
     compte le plus, pas le moins.
     La teinte chaude d'origine est conservée (même axe R>G>B), seule la
     luminosité descend. L'écart avec ink-soft reste net, donc la
     hiérarchie de gris de la page ne s'aplatit pas. */
  --white:      #FFFFFF;
  --cream:      #FAF9F7;
  --ink:        #191817;
  --ink-soft:   #55524C;
  --ink-faint:  #6E6A62;
  --line:       rgba(25, 24, 23, 0.10);
  --line-soft:  rgba(25, 24, 23, 0.06);

  /* Une seule famille dans la page. Fraunces : logo uniquement. */
  --sans:  "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --serif: "Fraunces", Georgia, serif;

  /* Rythme : large, aéré */
  --wrap:   1120px;
  --narrow: 720px;
  --gutter: 24px;

  --ease: cubic-bezier(.22, .61, .36, 1);
}

/* ---------- Reset ---------- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
body {
  margin: 0;
  font-family: var(--sans);
  font-size: 17px;
  line-height: 1.6;
  color: var(--ink);
  background: var(--white);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}
img, svg { max-width: 100%; display: block; }

/* Titres en sans-serif, serrés : c'est le cœur du changement. */
h1, h2, h3, h4 {
  font-family: var(--sans);
  font-weight: 600;
  line-height: 1.1;
  letter-spacing: -0.022em;
  margin: 0;
}
p { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }
a { color: inherit; text-decoration: none; }
button { font: inherit; cursor: pointer; }

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

.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 200;
  background: var(--ink); color: var(--white);
  padding: 12px 20px;
}
.skip-link:focus { left: 0; }

/* ---------- Boutons ---------- */
/* 3 px et non 6 : le logo est une matrice de carrés nets, l'angle doit se
   lire. Assez de rayon pour rester accueillant, pas assez pour évoquer le
   bouton web générique. Même valeur que .founder-photo et le focus. */
.btn {
  display: inline-flex; align-items: center; justify-content: center;
  gap: 8px;
  padding: 13px 24px;
  border: 1px solid transparent;
  border-radius: 3px;
  font-size: 15px; font-weight: 500;
  letter-spacing: -0.005em;
  transition: background-color .2s var(--ease), border-color .2s var(--ease),
              color .2s var(--ease), opacity .2s var(--ease);
  white-space: nowrap;
}
.btn-primary {
  background: var(--ink); color: var(--white);
}
.btn-primary:hover { background: var(--violet-700); }

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

.btn-sm { padding: 9px 17px; font-size: 14px; }
.btn-block { width: 100%; }
.btn:disabled { opacity: .45; cursor: not-allowed; }

/* Surtitre en petites capitales, précédé de la micro-matrice : le motif
   de marque sert de ponctuation. inline-flex et non flex, sinon le <p>
   occuperait toute la largeur et le surtitre ne s'alignerait plus sur
   le texte qui le suit. */
.eyebrow {
  display: inline-flex; align-items: center; gap: 10px;
  font-size: 12px; font-weight: 500;
  letter-spacing: .1em; text-transform: uppercase;
  color: var(--ink-faint);
  margin-bottom: 20px;
}
.eyebrow::before {
  content: "";
  flex: none;
  width: 9px; height: 9px;
  background:
    linear-gradient(var(--violet-700) 0 0) 0 0 / 4px 4px no-repeat,
    linear-gradient(var(--violet-300) 0 0) 100% 0 / 4px 4px no-repeat,
    linear-gradient(var(--violet-300) 0 0) 0 100% / 4px 4px no-repeat,
    linear-gradient(var(--violet-700) 0 0) 100% 100% / 4px 4px no-repeat;
}

/* ============================================================
   HEADER
   Fin, discret, sans fond coloré. Un filet apparaît au scroll.
   ============================================================ */
.site-header {
  position: sticky; top: 0; z-index: 100;
  background: rgba(255, 255, 255, .85);
  backdrop-filter: saturate(180%) blur(14px);
  -webkit-backdrop-filter: saturate(180%) blur(14px);
  border-bottom: 1px solid transparent;
  transition: border-color .3s var(--ease);
}
.site-header.is-scrolled { border-bottom-color: var(--line-soft); }

/* En haut de page le logo est imposant ; dès que le visiteur descend,
   il se réduit et le header se compacte, pour libérer de la hauteur
   de lecture. Les deux transitions sont synchronisées. */
.header-inner {
  display: flex; align-items: center; gap: 32px;
  height: 104px;
  transition: height .35s var(--ease);
}
.site-header.is-scrolled .header-inner { height: 70px; }

.logo {
  display: flex; align-items: center; flex: none;
  color: var(--ink);
  transition: opacity .2s var(--ease);
}
.logo:hover { opacity: .65; }

/* Le logo garde Fraunces : seul élément éditorial du site.
   On anime la hauteur (et non un scale) : le SVG se redessine net à
   chaque taille, sans flou d'interpolation. */
.logo-mark {
  /* flex:none indispensable : sans lui, le SVG est étiré par le
     conteneur flex et ignore toute hauteur imposée, y compris en
     inline. C'est ce qui empêchait le rétrécissement au scroll. */
  flex: none;
  height: 62px; width: auto;
  transition: height .35s var(--ease);
}
.site-header.is-scrolled .logo-mark { height: 38px; }

.logo-mark text { fill: currentColor; }

/* ------------------------------------------------------------
   Intro du logo : la matrice se résout
   La matrice 3×3 complète apparaît case par case, puis s'efface
   en ne laissant que la diagonale — le logo. L'animation répond
   à la question que pose le signe : pourquoi trois carrés en
   escalier ? Parce que ce sont les seules cases qui comptent.

   Les neuf cases fantômes n'existent QUE pendant l'intro : hors
   .is-intro elles sont en display:none, donc invisibles aux
   lecteurs d'écran comme au rendu. La classe est posée par le
   script une fois par session, puis retirée à la fin.
   ------------------------------------------------------------ */
.logo-matrix { display: none; }

.logo-mark.is-intro .logo-matrix { display: block; }

.logo-mark.is-intro .mx {
  opacity: 0;
  transform-box: fill-box;
  transform-origin: center;
  animation: logo-matrix-flash .92s var(--ease) forwards;
}
/* Balayage ligne par ligne, 30 ms d'écart : assez pour qu'on perçoive
   un remplissage, assez court pour que la grille soit là d'un bloc. */
.logo-mark.is-intro .mx:nth-child(1) { animation-delay: .02s; }
.logo-mark.is-intro .mx:nth-child(2) { animation-delay: .05s; }
.logo-mark.is-intro .mx:nth-child(3) { animation-delay: .08s; }
.logo-mark.is-intro .mx:nth-child(4) { animation-delay: .11s; }
.logo-mark.is-intro .mx:nth-child(5) { animation-delay: .14s; }
.logo-mark.is-intro .mx:nth-child(6) { animation-delay: .17s; }
.logo-mark.is-intro .mx:nth-child(7) { animation-delay: .20s; }
.logo-mark.is-intro .mx:nth-child(8) { animation-delay: .23s; }
.logo-mark.is-intro .mx:nth-child(9) { animation-delay: .26s; }

/* La diagonale se pose pendant que la grille s'efface : les deux phases
   se recouvrent, sinon un trou apparaît au milieu de la séquence. */
.logo-mark.is-intro .logo-sq {
  opacity: 0;
  transform-box: fill-box;
  transform-origin: center;
  animation: logo-diag-in .42s var(--ease) forwards;
}
.logo-mark.is-intro .s1 { animation-delay: .58s; }
.logo-mark.is-intro .s2 { animation-delay: .66s; }
.logo-mark.is-intro .s3 { animation-delay: .74s; }

.logo-mark.is-intro .logo-word {
  opacity: 0;
  animation: logo-word-in .5s var(--ease) .84s forwards;
}

@keyframes logo-matrix-flash {
  0%   { opacity: 0; transform: scale(.62); }
  18%  { opacity: 1; transform: none; }
  52%  { opacity: 1; }
  100% { opacity: 0; }
}
@keyframes logo-diag-in {
  from { opacity: 0; transform: scale(.62); }
  to   { opacity: 1; transform: none; }
}
@keyframes logo-word-in {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}

.nav {
  display: flex; gap: 30px;
  margin-left: auto;
  font-size: 15px; font-weight: 450;
}
.nav a {
  color: var(--ink-soft);
  /* .2s : durée relevée sur 72 des 80 transitions du CSS d'Anthropic. */
  transition: color .2s var(--ease);
}
.nav a:hover, .nav a.is-current {
  color: var(--ink);
  /* Soulignement détaché du texte plutôt que collé : c'est le motif
     signature d'Anthropic, présent sur leur nav, leur footer et leurs
     cartes. Le décalage évite que le trait morde sur les jambages. */
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: .2em;
}

.header-actions { display: flex; align-items: center; gap: 20px; }

.lang-switch {
  display: flex; align-items: center;
  font-size: 13px; font-weight: 500;
  color: var(--ink-faint);
}
/* La cible fait 44 px de haut, le libellé continue d'en occuper 13.
   Le padding porte la zone tactile sans déplacer le texte : « FR / EN »
   se lit exactement comme avant, mais le doigt ne rate plus.
   Avant, la cible mesurée faisait 20 × 27 px — sous le minimum de
   44 × 44 (Apple HIG) / 48 × 48 dp (Material), pour un contrôle qui
   change la langue de tout le site.
   Le gap du conteneur est retiré : l'écartement vient désormais du
   padding des boutons, qui compte lui dans la surface tactile. */
.lang-switch button {
  background: none; border: 0;
  padding: 0 8px;
  min-height: 44px;
  display: inline-flex; align-items: center;
  color: var(--ink-faint);
  transition: color .2s var(--ease);
}
.lang-switch button:hover { color: var(--ink); }
.lang-switch button.is-active { color: var(--ink); font-weight: 600; }

/* 44 px et non 40 : c'est le minimum tactile, et le bouton n'a pas de
   libellé pour rattraper un appui manqué. Les trois barres gardent leurs
   20 px de large, seule la zone d'appui grandit. */
.nav-toggle {
  display: none;
  flex-direction: column; justify-content: center; gap: 5px;
  width: 44px; height: 44px;
  background: none; border: 0; padding: 0;
}
.nav-toggle span {
  display: block; height: 1.5px; width: 20px;
  background: var(--ink); margin-inline: auto;
  transition: transform .3s var(--ease), opacity .2s var(--ease);
}
.nav-toggle[aria-expanded="true"] span:nth-child(1) { transform: translateY(6.5px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.nav-toggle[aria-expanded="true"] span:nth-child(3) { transform: translateY(-6.5px) rotate(-45deg); }

.mobile-nav[hidden] { display: none; }
.mobile-nav {
  display: flex; flex-direction: column;
  padding: 4px var(--gutter) 20px;
  border-top: 1px solid var(--line-soft);
  background: var(--white);
}
.mobile-nav a {
  padding: 15px 0;
  font-size: 17px; font-weight: 500;
  border-bottom: 1px solid var(--line-soft);
}
.mobile-nav a:last-child { border-bottom: 0; }

@media (max-width: 780px) {
  /* Amplitude réduite en mobile : la hauteur d'écran y est précieuse,
     le logo part déjà d'une taille modérée. */
  .header-inner { height: 76px; }
  .site-header.is-scrolled .header-inner { height: 60px; }
  .logo-mark { height: 46px; }
  .site-header.is-scrolled .logo-mark { height: 32px; }
}

/* Très petits écrans (iPhone SE 1re génération et assimilés).
   À 320px, le logo (153px) + la bascule de langue + le bouton ☰ dépassent
   de la ligne : le bouton sortait de 8px à droite. Les cibles tactiles de
   44px ne sont pas négociables, c'est donc la marge et le logo qui cèdent.
   La marge passe de 24 à 16px et le logo perd 6px de haut : le header
   redevient contenu, sans qu'aucune cible descende sous 44px. */
@media (max-width: 360px) {
  .wrap { padding-inline: 16px; }
  .logo-mark { height: 40px; }
  /* Le lien garde 44px de haut même quand le SVG passe à 40 : c'est lui
     qui porte le clic, pas l'image. */
  .logo { min-height: 44px; }
  /* Le séparateur « / » de la bascule mange une largeur qu'on n'a plus.
     Les deux boutons restent lisibles et gardent leurs 44px de haut. */
  .lang-switch button { padding: 0 6px; }
}

/* ============================================================
   HERO
   Texte seul sur blanc, très grand titre, beaucoup de vide.
   ============================================================ */
.hero {
  position: relative;
  /* Le haut garde son ampleur — c'est l'entrée de page. Le bas suit la
     nouvelle échelle des sections : il forme avec elles le premier
     inter-section, qui n'a pas de filet pour l'articuler. */
  padding: clamp(80px, 13vw, 168px) 0 clamp(56px, 8vw, 100px);
  overflow: hidden;
}

/* Le champ de points reste, très atténué : c'est la signature liée
   au logo, mais il ne doit jamais concurrencer le texte. */
.hero-field {
  position: absolute; inset: 0;
  pointer-events: none;
  opacity: .8;
  --mask-x: linear-gradient(90deg,
    rgba(0,0,0,.18) 0%, rgba(0,0,0,.26) 42%, rgba(0,0,0,.8) 72%, rgba(0,0,0,1) 100%);
  --mask-r: radial-gradient(ellipse 80% 80% at 78% 42%,
    #000 18%, rgba(0,0,0,.5) 60%, transparent 86%);
  mask-image: var(--mask-x), var(--mask-r);
  -webkit-mask-image: var(--mask-x), var(--mask-r);
  mask-composite: intersect;
  -webkit-mask-composite: source-in;
}
@media (max-width: 980px) {
  .hero-field { opacity: .3; }
}
#covariance-field { width: 100%; height: 100%; display: block; }

.hero-inner { position: relative; z-index: 1; max-width: 880px; }

.hero h1 {
  font-size: clamp(40px, 6.4vw, 76px);
  line-height: 1.03;
  letter-spacing: -0.035em;
  margin-bottom: 26px;
  text-wrap: balance;
}
/* Plus d'italique serif : l'accent passe par la couleur seule. */
.hero h1 em {
  font-style: normal;
  color: var(--violet-700);
}
.lede {
  font-size: clamp(18px, 1.9vw, 21px);
  line-height: 1.55;
  color: var(--ink-soft);
  max-width: 56ch;
  letter-spacing: -0.008em;
}
/* Le mot pivot de l'accroche — celui qui porte la différence. Encre pleine
   et graisse à peine relevée : sur un paragraphe en --ink-soft, le contraste
   suffit à le détacher sans donner à la ligne une allure de titre. */
.lede strong {
  font-weight: 600;
  color: var(--ink);
}
.hero-cta {
  display: flex; flex-wrap: wrap; gap: 12px;
  margin-top: 40px;
}

/* ============================================================
   CARTES .skill
   Écrites pour la section Savoir-faire, elles servent aujourd'hui à
   « Ce que nous offrons » (avec .offer-grid) et, en V2, à « Ce que vous
   gardez sous contrôle » (avec .why-grid). Le déploiement, lui, n'est pas
   une carte : c'est la ligne sous la grille (voir .deploy-note plus bas).
   Seule entorse assumée au « aucune
   carte » de la DA : le contour d'un pixel délimite des ensembles qui,
   sans lui, se liraient comme une seule longue liste. Ni ombre ni fond,
   le trait reste discret.
   ============================================================ */
.skills {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(16px, 2vw, 26px);
}
.skill {
  display: flex; flex-direction: column;
  padding: clamp(22px, 2.4vw, 32px);
  border: 1px solid var(--line);
  border-radius: 3px;
  transition: border-color .25s var(--ease);
}
.skill:hover { border-color: var(--violet-500); }

/* La case du logo en guise de marqueur. Sa densité suit la diagonale
   (900 → 500 → 300) et situe la carte dans la séquence : c'est ce qui
   remplace le numéro, qui faisait « slide de conseil ». */
.skill-mark {
  width: 13px; height: 13px;
  flex: none;
  margin-bottom: 18px;
  border-radius: 2px;
}
.skill:nth-child(1) .skill-mark { background: var(--violet-900); }
.skill:nth-child(2) .skill-mark { background: var(--violet-500); }
.skill:nth-child(3) .skill-mark { background: var(--violet-300); }

/* La rampe est calée sur celle de .skill-d, sans quoi la marche entre les
   deux se referme au milieu de la plage. L'ancienne valeur, clamp(18px,
   1.6vw, 22px), n'atteignait ses 22 px qu'à partir de 1375 px alors que la
   description plafonne à 16 px dès 1067 px : entre les deux, le titre
   tombait à 1,19× le corps du texte, soit presque rien. Les deux bornes
   d'ici tiennent le rapport entre 1,32 et 1,41 sur toute la plage. */
.skill-t {
  font-size: clamp(1.25rem, 1.1rem + 0.6vw, 1.375rem);
  letter-spacing: -0.02em;
  margin-bottom: 10px;
  /* Les titres de deux lignes se coupaient là où la ligne était pleine,
     laissant un mot seul dessous. Même règle que .section-head h2. */
  text-wrap: balance;
}
/* Trois corrections d'un même défaut : le texte était à 14,5 px pour une
   liste à 14 px juste dessous. Un demi-pixel d'écart ne fait pas une
   hiérarchie — la description et ses puces se lisaient comme un seul bloc,
   et le titre à 22 px tombait sur elles d'un coup (1,52×).

   1. La taille devient fluide, de 15 px sous 390 px à 16 px à partir de
      1280. Le calcul est en rem plus vw et non en vw seul : une taille en
      vw pur ignore la préférence de corps du navigateur et ne grandit plus
      au zoom texte. La marche fait 16 / 14 = 1,14 avec les puces et
      22 / 16 = 1,375 avec le titre.

   2. `max-width` en ch borne la justure. À 1440 px la colonne de texte
      fait 457 px, soit une soixantaine de signes : la borne ne joue pas.
      Elle sert au palier à une colonne, où la carte occupe 688 px et où la
      ligne passait à plus de 80 signes, bien au-delà du confort de lecture.

   3. `text-wrap: pretty` empêche la ligne veuve d'un seul mot en fin de
      paragraphe. Ignoré par les moteurs qui ne le connaissent pas, sans
      solution de repli à prévoir. */
.skill-d {
  font-size: clamp(0.9375rem, 0.9rem + 0.15vw, 1rem);
  line-height: 1.6;
  max-width: 60ch;
  color: var(--ink-soft);
  margin-bottom: 22px;
  text-wrap: pretty;
}
/* La marge basse de .skill-d séparait le texte d'une liste de puces
   (.skill-l). Plus aucun bloc n'en porte : la description est toujours le
   dernier enfant de la carte, et la marge laisserait un vide sous son
   dernier mot — les contours des cartes voisines se termineraient alors à
   des hauteurs qui sembleraient arbitraires. Les règles .skill-l ont été
   retirées avec les listes ; git en garde la trace. */
.skill-d:last-child { margin-bottom: 0; }

/* ---------- Ce que nous offrons : 2×2 ----------
   Quatre blocs, alors que .skills en attend trois : le quatrième
   resterait seul sur une deuxième ligne. Deux colonnes les répartissent
   en carré.

   Le carré n'est pas qu'un pis-aller de grille : il tient les quatre
   blocs sur un même plan, et le marqueur y descend en densité (900, 700,
   500, 300) pour situer chaque bloc dans la séquence.

   Les règles .skill:nth-child(1..3) ci-dessus s'arrêtent à trois et
   suivent la diagonale du logo (900, 500, 300). Elles sont écrasées ici,
   sinon le quatrième bloc n'aurait pas de marqueur du tout. */
.offer-grid {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
.offer-grid .skill:nth-child(1) .skill-mark { background: var(--violet-900); }
.offer-grid .skill:nth-child(2) .skill-mark { background: var(--violet-700); }
.offer-grid .skill:nth-child(3) .skill-mark { background: var(--violet-500); }
.offer-grid .skill:nth-child(4) .skill-mark { background: var(--violet-300); }

/* ============================================================
   SECTIONS
   Séparation par le vide. Un filet fin marque le début d'une
   section, jamais un fond coloré.
   ============================================================ */
/* Le vide entre deux sections n'est pas cette seule valeur : il additionne
   le padding bas de la précédente, le padding haut de la suivante et le
   retrait du filet de tête ci-dessous. Trois clamps qui se cumulaient à
   316px sur grand écran — la page se lisait par fenêtres séparées plutôt
   que d'une traite. Le rapport entre les trois est conservé, l'échelle
   descend d'environ un cinquième (248px au maximum). */
.section {
  padding: clamp(56px, 7vw, 88px) 0;
}
/* Les listes au filet se terminent déjà par un trait : le padding bas
   de la section suffit, inutile d'y ajouter une marge. */
.founders { margin-bottom: 0; }
#equipe .founders { margin-bottom: 0; }
.section-alt { background: var(--white); }

/* Filet de tête : structure la page sans la cloisonner. */
.section + .section > .wrap,
.section-contact > .wrap {
  border-top: 1px solid var(--line-soft);
  /* Un peu moins d'air sous le filet qu'au-dessus (rapport ~1,2) : le trait
     appartient à la section qu'il ouvre, il ne flotte pas entre les deux. */
  padding-top: clamp(44px, 5.5vw, 72px);
}

.section-head {
  max-width: var(--narrow);
  margin-bottom: clamp(48px, 6vw, 76px);
}
.section-head h2 {
  font-size: clamp(30px, 3.8vw, 44px);
  letter-spacing: -0.028em;
  margin-bottom: 18px;
  text-wrap: balance;
}
.section-lede {
  font-size: 18px;
  color: var(--ink-soft);
  line-height: 1.6;
  letter-spacing: -0.006em;
}

/* ---------- Notre conviction ----------
   Pas de titre de section ni de carte. Un h2 aurait rangé le bloc au même
   niveau que les rubriques qui décrivent l'offre, alors qu'il dit
   pourquoi cette offre existe : le surtitre suffit à l'annoncer.

   Ce qui a changé, et pourquoi
   ----------------------------
   La version précédente posait un seul paragraphe de six lignes au corps
   23px. Le texte était juste, la lecture ne suivait pas : à cet endroit
   de la page — deuxième écran, juste après le hero — le lecteur survole,
   et un pavé plein sans point d'entrée se saute en entier.

   Trois changements, aucun ornement neuf :

   1. DEUX COLONNES, LA GAUCHE COLLANTE
      Le surtitre reste en vis-à-vis du texte pendant toute la traversée
      du bloc. Le lecteur garde un repère fixe, et le bloc cesse de
      ressembler à la colonne unique de toutes les autres sections.

   2. L'AFFIRMATION AU CORPS D'UN TITRE
      La première phrase est celle qui doit rester si rien d'autre n'est
      lu : elle passe donc au corps d'un h2, le reste redescend au corps
      courant. La hiérarchie fait le travail que le gras faisait mal.

   3. L'ENCRAGE AU DÉFILEMENT (voir plus bas)
      Les mots passent de l'estompe à l'encre pleine au rythme du
      défilement. C'est ce qui fait ralentir l'œil sur les deux écrans
      que dure le bloc.

   Une matrice du logo en neuf cases a occupé la colonne de gauche sous
   le surtitre, remplie case par case au défilement. Retirée : à cette
   échelle, le motif du logo devenait une illustration, et la page en
   avait déjà deux — la grille de fond du hero et les marqueurs
   d'étape. La colonne ne porte plus que le surtitre, ce qui laisse le
   texte seul à tenir le bloc. */
.belief {
  display: grid;
  /* La colonne de gauche tient le surtitre en une ligne (« Notre
     conviction » ≈ 150px au corps 11px espacé) sans jamais dépasser le
     sixième de la largeur : au-delà, le texte perdrait sa mesure. */
  grid-template-columns: clamp(150px, 17vw, 210px) minmax(0, 1fr);
  gap: clamp(28px, 4vw, 64px);
  align-items: start;
}

.belief-rail {
  position: sticky;
  /* Hauteur de l'en-tête compacté (70px) plus une respiration. Sans ce
     décalage la colonne se colle sous l'en-tête, qui la recouvre. */
  top: 102px;
}

.belief-body {
  /* Mesure de lecture : la colonne de gauche mange déjà sa part de
     .wrap, donc pas de var(--narrow) ici, qui la rétrécirait deux fois. */
  max-width: 62ch;
  display: flex;
  flex-direction: column;
  gap: clamp(20px, 2.2vw, 30px);
}

/* L'affirmation. Corps d'un titre de section, mais posée dans un <p> :
   c'est une déclaration, pas une rubrique, et lui donner un h2 la ferait
   entrer dans le sommaire du document. */
.belief-lead {
  font-size: clamp(25px, 3.2vw, 38px);
  line-height: 1.16;
  letter-spacing: -0.03em;
  font-weight: 500;
  text-wrap: balance;
}
/* Le violet-900 détache l'affirmation sans ajouter de graisse : à ce
   corps, un vrai gras aurait fait un pavé noir au milieu de la phrase. */
.belief-lead strong {
  font-weight: 500;
  color: var(--violet-900);
}

/* Les règles .belief-p ont été retirées avec les trois paragraphes de la
   conviction : la section ne porte plus que son chapô. */

/* ---------- L'encrage au défilement ----------
   Les mots sont encrés au rythme du défilement : estompés tant que le
   bloc monte, à l'encre pleine quand la ligne arrive à hauteur de
   lecture. La matrice se remplit case par case sur la même horloge.

   Tout est porté par une animation à défilement (`animation-timeline`) :
   le navigateur pilote la progression lui-même, hors du fil principal.
   Aucun écouteur de scroll, aucun rAF, rien à recalculer au
   redimensionnement — c'est la raison du choix, pas l'effet lui-même,
   qu'un IntersectionObserver aurait aussi pu donner en moins fin.

   La découpe en mots est faite par script.js (section 1 bis), qui pose
   une classe .w et une variable --d (0 à 100 : position du mot dans le
   paragraphe). --d décale la plage de chaque mot, ce qui produit le
   balayage. On ne peut pas s'en remettre à animation-delay : sur une
   timeline de défilement, les délais n'existent pas, seule la plage
   compte.

   TROIS FILETS DE SÉCURITÉ, dans cet ordre :
     - hors @supports (Firefox < 144, Safari < 26), les mots restent à
       l'encre pleine : le bloc se lit normalement, sans effet ;
     - sans JS, aucun .w n'est créé, donc rien à animer, même résultat ;
     - en mouvement réduit, la règle n'est pas posée du tout.
   L'état par défaut est toujours le texte lisible : l'effet ne peut pas
   laisser une page vide s'il ne se déclenche pas. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    /* La timeline est nommée et posée sur le bloc entier, pas sur chaque
       mot : les mots et la matrice, qui sont dans deux colonnes
       différentes, doivent partager la même horloge. Un `view()` anonyme
       par élément aurait donné à chaque mot sa propre progression, donc
       aucun balayage. */
    .belief { view-timeline: --belief block; }

    .belief [data-ink] .w {
      opacity: .2;
      animation: belief-ink linear both;
      animation-timeline: --belief;
      /* La plage est prise sur `cover`, et non sur `entry`. `entry` ne
         dure que la hauteur du bloc — ici ~320px : le texte finissait de
         s'encrer alors qu'il était encore en bas de l'écran, avant
         d'arriver à hauteur de lecture. `cover` court sur la hauteur de
         la fenêtre PLUS celle du bloc (~1000px), donc l'encrage
         accompagne la lecture au lieu de la précéder.

         Chaque mot s'encre sur 12% de cette course, et sa plage est
         décalée de --d : le premier mot part à 8%, le dernier est à
         l'encre pleine à 45% — c'est-à-dire quand le bloc occupe le
         milieu de l'écran, donc AVANT qu'on ait à le lire. Finir plus
         tard donnerait un texte encore estompé sous les yeux du lecteur,
         ce qui n'est plus un effet mais une gêne. */
      animation-range: cover calc(8% + var(--d, 0) * .25%)
                       cover calc(20% + var(--d, 0) * .25%);
    }

  }
}

/* L'opacité plutôt que la couleur : elle préserve le violet-900 de
   l'affirmation et l'encre pleine de la promesse, qu'une interpolation
   vers --ink aurait tous deux écrasés en gris. Et c'est la seule
   propriété, avec transform, que le compositeur anime sans repeindre. */
@keyframes belief-ink { to { opacity: 1; } }

/* ---------- Déploiement : la phrase qui ferme la grille de l'offre ----------
   Le déploiement avait une section à lui, en séquence de trois paliers
   (.path : trait vertical, une case pleine par étape). Section et règles
   ont été retirées ; git en garde la trace.

   Ce qu'il en reste suit .founders-note à la lettre — mêmes marges, même
   filet, même mesure bornée, même corps de texte. Ce n'est pas une
   économie : c'est la même situation de page (une phrase qui ferme une
   rangée de blocs), et la page n'a aucune raison de la traiter de deux
   façons. Au corps des cartes (15–16,5px) et sans filet, la note se
   lisait comme une cinquième carte privée de son cadre.

   Pas de fond ni de contour, comme partout ailleurs : le filet fin
   suffit à fermer la rangée. */
.deploy-note {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: start;
  column-gap: clamp(16px, 1.8vw, 26px);
  margin-top: clamp(40px, 5vw, 64px);
  padding-top: clamp(28px, 3.5vw, 40px);
  border-top: 1px solid var(--line-soft);
  /* Même borne que .founders-note : la phrase suit la rangée de blocs
     sans dépasser une justure lisible. */
  max-width: min(100%, 920px);
}
/* Le symbole prend la place du filet violet de .founders-note (26×2) :
   même fonction — ouvrir la note — et même largeur d'attaque, à quelques
   pixels près. flex/width fixes : sans eux il se comprime dès que la
   phrase remplit la ligne, et les trois paliers s'écrasent en traits.
   Le décalage haut cale la première case sur la hauteur d'œil de la
   première ligne, que l'alignement de grille laisserait 3px trop haut. */
.deploy-mark {
  width: clamp(30px, 2.6vw, 38px);
  height: auto;
  margin-top: 5px;
}
/* Les fills sont ici et non dans le SVG, comme pour le logo d'en-tête :
   la descente de violet suit alors les jetons, et la V2 la redéfinit sans
   toucher au balisage. La série s'arrête au 500 : le 300 ne fait que
   2.16:1 sur blanc, et le dernier palier disparaîtrait. */
.deploy-sq.d1 { fill: var(--violet-900); }
.deploy-sq.d2 { fill: var(--violet-700); }
.deploy-sq.d3 { fill: var(--violet-500); }
.deploy-text {
  margin: 0;
  font-size: clamp(17px, 1.5vw, 19px);
  line-height: 1.62;
  letter-spacing: -0.008em;
  color: var(--ink-soft);
  text-wrap: pretty;
}
/* Comme dans .founders-note et dans le chapeau de la bannière : ce qui
   est promis repasse en pleine encre, le détail reste en gris. */
.deploy-text strong {
  font-weight: 600;
  color: var(--ink);
}

/* ============================================================
   ÉQUIPE
   Portraits verticaux alignés sur une même ligne : les trois
   cofondateurs sont au même niveau, aucun n'est mis en avant par
   la mise en page. Toujours aucune carte — la photo EST le bloc,
   le reste est typographie et filets.
   ============================================================ */
.founders {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(20px, 3vw, 40px);
  border-top: 0;
}

.founder {
  display: block;
  padding: 0;
  border: 0;
  text-align: left;
  background: transparent;
  transform: none;
}

/* ---- Le média ---- */
.founder-media {
  display: block;
  position: relative;
  aspect-ratio: 4 / 5;
  overflow: hidden;
  border-radius: 3px;
  background: var(--violet-100);
  /* La photo est le lien : tout le cadre est cliquable. */
  cursor: pointer;
}

.founder-photo {
  /* Positionnement absolu plutôt que object-fit : c'est ce qui rend le
     cadrage vertical réellement pilotable, voir la note ci-dessous.
     --zoom donne la hauteur de l'image en proportion du cadre, --y le
     décalage vertical, exprimé lui aussi en % de la hauteur du cadre.
     La source étant carrée, la largeur suit la hauteur : à --zoom >= 1
     et un cadre en 4/5, l'image déborde toujours horizontalement. */
  position: absolute;
  top: calc(var(--y, 0) * 1%);
  left: 50%;
  width: auto;
  height: calc(var(--zoom, 1) * 100%);
  max-width: none;
  margin: 0;
  border-radius: 0;
  /* N&B au repos : les trois portraits n'ont pas le même fond de studio
     (gris pour l'un, dégradé bleu pour les deux autres). Le gris les
     ramène à une seule série ; la couleur ne revient qu'au survol. */
  filter: grayscale(1) contrast(1.04);
  transform: translateX(-50%);
  transition: filter .55s var(--ease), transform .75s var(--ease);
}
.founder-media:hover .founder-photo,
.founder-media:focus-visible .founder-photo {
  filter: grayscale(0) contrast(1);
  transform: translateX(-50%) scale(1.04);
}

/* Harmonisation du cadrage : le haut des vestes est aligné à 80% de la
   hauteur du cadre pour les trois, et les têtes sont à la même échelle.

   Le modèle est simple : un point situé à la fraction s de la hauteur
   source se retrouve à --y + s × --zoom dans le cadre. Trois contraintes
   bornent les valeurs :

   1. Couverture — l'image doit couvrir le cadre de bord à bord, donc
      100 × (1 - --zoom) <= --y <= 0. En sortir laisse apparaître le fond.
   2. Cheveux — le sommet de la chevelure doit rester dans le cadre.
   3. Échelle — la distance menton–yeux doit être la même pour les trois.

   Relevés sur les sources (en % de leur hauteur) — sommet des cheveux,
   ligne des yeux, menton, haut de la veste :

     Ulysse    4.5 / 35.0 / 52.4 / 59.0
     Grégoire  8.2 / 36.6 / 57.4 / 65.8
     Sami      6.0 / 42.7 / 65.0 / 80.5

   Les trois cadrages d'origine sont très inégaux : la tête d'Ulysse est
   la plus petite (menton–yeux de 17.4 contre 22.3 pour Sami), d'où son
   zoom nettement plus fort — c'est ce qui égalise les têtes, pas ce qui
   les déséquilibre.

   80% est le plus haut alignement possible sans rogner personne, et
   Sami en est la cause : sa veste est à 80.5% de sa source, il ne reste
   donc que 19.5% d'image sous ses épaules. Remonter la ligne le forcerait
   à un zoom qui couperait le sommet de ses boucles. Ne pas descendre son
   --y sous 100 × (1 - --zoom) : le fond apparaîtrait en bas de son cadre.

   Son zoom est volontairement bien sous l'égalisation stricte des têtes :
   c'est ce qui fait descendre son visage dans le cadre. Sa veste retombe
   du coup à 81.5% au lieu de 80% — écart invisible, et le compromis est
   payant : les trois lignes des yeux se retrouvent à 43-45%. Descendre
   plus bas demanderait un zoom sous 1.03, où la couverture du cadre n'est
   plus assurée en bas. */
.founder[data-shift="0"] .founder-photo { --zoom: 1.41; --y: -3.2; }  /* Ulysse   */
.founder[data-shift="1"] .founder-photo { --zoom: 1.22; --y: -0.3; }  /* Grégoire */
.founder[data-shift="2"] .founder-photo { --zoom: 1.04; --y: -1.7; }  /* Sami     */

/* Voile violet : lie les photos à la palette au repos, et se retire au
   survol. C'est ce qui fait tenir trois clichés d'origines différentes
   dans une même image de marque. */
.founder-veil {
  position: absolute; inset: 0;
  background: linear-gradient(
    to top,
    rgba(55, 47, 134, .30) 0%,
    rgba(55, 47, 134, .06) 42%,
    rgba(55, 47, 134, 0) 72%
  );
  opacity: 1;
  transition: opacity .55s var(--ease);
  pointer-events: none;
}
.founder-media:hover .founder-veil,
.founder-media:focus-visible .founder-veil { opacity: .25; }

/* Pastille LinkedIn : posée dans l'image, elle monte au survol. Elle
   remplace le lien libellé de l'ancienne liste — le cadre entier étant
   cliquable, un second libellé serait redondant. */
.founder-badge {
  position: absolute;
  right: 14px; bottom: 14px;
  width: 34px; height: 34px;
  display: grid; place-items: center;
  border-radius: 3px;
  background: rgba(255, 255, 255, .92);
  backdrop-filter: blur(6px);
  opacity: 0;
  transform: translateY(8px);
  transition: opacity .35s var(--ease), transform .35s var(--ease),
              background-color .2s var(--ease);
  pointer-events: none;
}
.founder-badge svg { width: 15px; height: 15px; fill: var(--violet-900); }
.founder-media:hover .founder-badge,
.founder-media:focus-visible .founder-badge {
  opacity: 1;
  transform: translateY(0);
}

/* ---- L'identité, sous la photo ---- */
.founder-id {
  display: flex; flex-direction: column;
  padding-top: 18px;
}

/* Filet court qui s'étire au survol : le seul mouvement du bloc texte. */
.founder-rule {
  display: block;
  width: 26px; height: 2px;
  margin-bottom: 14px;
  background: var(--violet-500);
  transform-origin: left center;
  transition: transform .45s var(--ease), background-color .45s var(--ease);
}
.founder:hover .founder-rule {
  transform: scaleX(2.6);
  background: var(--violet-900);
}

.founder-name {
  font-size: clamp(19px, 1.6vw, 23px);
  font-weight: 600;
  letter-spacing: -0.018em;
  margin: 0 0 5px;
}
.founder-role {
  font-size: 12px; font-weight: 500;
  letter-spacing: .09em; text-transform: uppercase;
  color: var(--ink-faint);
  margin: 0 0 10px;
}

/* CentraleSupélec sous chaque nom : répété trois fois, c'est le signal.
   La petite case violette est le motif du logo réduit à une unité. */
.founder-school {
  display: flex; align-items: center; gap: 8px;
  font-size: 14px; font-weight: 500;
  letter-spacing: -0.006em;
  color: var(--violet-700);
  margin: 0;
}
.founder-school-mark {
  width: 8px; height: 8px;
  flex: none;
  border-radius: 2px;
  background: var(--violet-500);
}

/* Cascade d'apparition : la 2e photo entre après la 1re, la 3e après la
   2e. Le décalage n'existe plus que dans le temps — les trois portraits
   arrivent à la même hauteur. */
.founders .founder.reveal {
  opacity: 0;
  transform: translateY(26px);
  transition: opacity .7s var(--ease), transform .7s var(--ease);
}
.founders .founder.reveal.is-visible {
  opacity: 1;
  transform: none;
}
.founders .founder.reveal[data-shift="1"] { transition-delay: .09s; }
.founders .founder.reveal[data-shift="2"] { transition-delay: .18s; }

/* ---- Le mot des fondateurs ----
   Sous la rangée de portraits : un filet ferme la rangée, la case
   violette reprend le motif du logo — le même repère que sous chaque
   nom, ici en tête de phrase. Aucun fond, aucune carte : la page se
   tient à la typographie et aux filets. */
.founders-note {
  margin-top: clamp(40px, 5vw, 64px);
  padding-top: clamp(28px, 3.5vw, 40px);
  border-top: 1px solid var(--line-soft);
  /* Plus large que la colonne narrow : le mot des fondateurs suit la
     rangée de portraits au lieu de se replier dessous. Borné quand
     même, pour ne pas dépasser une justure lisible. */
  max-width: min(100%, 920px);
}
.founders-note::before {
  content: "";
  display: block;
  width: 26px; height: 2px;
  margin-bottom: 18px;
  background: var(--violet-500);
}
.founders-note p {
  margin: 0;
  font-size: clamp(17px, 1.5vw, 19px);
  line-height: 1.62;
  letter-spacing: -0.008em;
  color: var(--ink-soft);
  text-wrap: pretty;
}
/* Comme dans le chapeau de la bannière : la promesse repasse en pleine
   encre, le reste de la phrase reste en gris. */
.founders-note strong {
  font-weight: 600;
  color: var(--ink);
}

/* ============================================================
   CONTACT
   Formulaire sans carte : champs soulignés d'un filet.
   ============================================================ */
.section-contact { background: var(--white); }
.contact-inner {
  display: grid;
  grid-template-columns: 1fr minmax(320px, 440px);
  gap: clamp(40px, 6vw, 88px);
  align-items: start;
}
.contact-copy h2 {
  font-size: clamp(30px, 3.8vw, 44px);
  letter-spacing: -0.028em;
  margin-bottom: 18px;
  text-wrap: balance;
}
.contact-direct {
  display: flex; flex-direction: column; gap: 6px;
  margin-top: 30px; padding-top: 26px;
  border-top: 1px solid var(--line-soft);
}
.contact-direct span { color: var(--ink-faint); font-size: 14px; }

.contact-form {
  background: transparent;
  border: 0; border-radius: 0;
  padding: 0;
  box-shadow: none;
  display: grid; gap: 26px;
}
.field { display: grid; gap: 6px; }
.field label {
  font-size: 13px; font-weight: 500;
  letter-spacing: .04em; text-transform: uppercase;
  color: var(--ink-faint);
}
/* Champs soulignés plutôt qu'encadrés : moins de matière à l'écran. */
.field input, .field textarea {
  font: inherit; font-size: 16px;
  color: var(--ink);
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--line);
  border-radius: 0;
  padding: 9px 0;
  width: 100%;
  transition: border-color .2s var(--ease);
}
.field textarea { resize: vertical; min-height: 92px; }
.field input:focus, .field textarea:focus {
  outline: none;
  border-bottom-color: var(--ink);
  box-shadow: none;
}
.field.has-error input, .field.has-error textarea { border-bottom-color: #C0392B; }
.error { font-size: 12.5px; color: #C0392B; min-height: 0; }
.field.has-error .error { min-height: 18px; }

/* Champ piège à robots : retiré du flux sans display:none ni
   visibility:hidden, que les robots savent détecter et ignorer. */
.honey {
  position: absolute;
  left: -9999px;
  width: 1px; height: 1px;
  overflow: hidden;
}

.form-note { font-size: 12.5px; color: var(--ink-faint); line-height: 1.5; }
.form-status {
  font-size: 14.5px; font-weight: 450;
  line-height: 1.5;
  color: var(--violet-700);
  min-height: 0;
}
.form-status.is-visible { min-height: 22px; }
.form-status.is-error { color: #C0392B; }

/* ============================================================
   FOOTER
   Clair et discret plutôt que bloc noir : le blanc domine
   jusqu'au bas de la page.
   ============================================================ */
.site-footer {
  background: var(--white);
  color: var(--ink);
  border-top: 1px solid var(--line);
  padding: clamp(40px, 5vw, 56px) 0 32px;
}
.footer-inner {
  display: grid;
  grid-template-columns: 1.5fr 1fr;
  gap: 36px;
}
.footer-logo { height: 30px; color: var(--ink); margin-bottom: 16px; }
.footer-brand p {
  font-size: 14.5px; line-height: 1.6;
  color: var(--ink-faint);
  max-width: 32ch;
}
/* Le gap tombe à 0 : l'écart entre les liens vient maintenant de leur
   propre padding vertical, qui lui appartient à la surface tactile.
   Avec gap:11px et des liens de 23 px de haut, l'espacement existait mais
   ne se touchait pas — 23 px de cible pour trois liens voisins, c'est le
   cas d'appui manqué typique en pied de page. */
.footer-nav {
  display: flex; flex-direction: column;
  font-size: 14.5px;
  justify-self: start;
}
/* width:fit-content est conservé : le soulignement au survol doit s'arrêter
   au mot, pas traverser la colonne. C'est donc le padding vertical qui
   porte la hauteur de 44 px, et non un étirement du bloc. */
.footer-nav a {
  color: var(--ink-soft);
  transition: color .2s var(--ease);
  width: fit-content;
  padding: 10px 0;
  display: inline-flex; align-items: center;
  min-height: 44px;
  box-sizing: border-box;
}
.footer-nav a:hover {
  color: var(--ink);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  /* Décalage un peu plus large ici, comme sur leur footer (.25em). */
  text-underline-offset: .25em;
}
.footer-legal {
  grid-column: 1 / -1;
  display: flex; flex-wrap: wrap; gap: 8px 22px;
  margin-top: 32px; padding-top: 22px;
  border-top: 1px solid var(--line-soft);
  font-size: 13px;
  color: var(--ink-faint);
}

/* ============================================================
   ANIMATIONS D'APPARITION
   Déplacement plus court : l'effet se remarque moins.
   ============================================================ */
.reveal {
  opacity: 0;
  transform: translateY(12px);
  transition: opacity .6s var(--ease), transform .6s var(--ease);
}
.reveal.is-visible { opacity: 1; transform: none; }

/* ============================================================
   RESPONSIVE
   ============================================================ */
@media (max-width: 940px) {
  .contact-inner { grid-template-columns: 1fr; }
  .footer-inner { grid-template-columns: 1fr; }
  /* Les cartes .skill passent à deux colonnes : sous ~300px de large,
     leurs listes coupent les intitulés en plein milieu (« Traitement /
     et / pipelines »). La dernière prend toute la largeur sous les
     autres, ce qui vaut pour les grilles de trois. */
  .skills { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .skills .skill:last-child { grid-column: 1 / -1; }

  /* L'offre est déjà en deux colonnes : rien à changer à la grille. Mais
     la règle ci-dessus étendrait son quatrième bloc sur toute la largeur
     et casserait le carré, alors que rien ne le justifie ici — les
     quatre blocs tiennent en deux colonnes à cette largeur. */
  .offer-grid .skill:last-child { grid-column: auto; }
}

@media (max-width: 780px) {
  .nav { display: none; }
  .header-cta { display: none; }
  .nav-toggle { display: flex; }
  .header-inner { gap: 16px; }
  .header-actions { margin-left: auto; }

  /* Une seule colonne : à cette largeur, deux cartes .skill redeviennent
     trop étroites pour leur texte. Empilées, chacune garde toute la
     largeur du texte. */
  .skills { grid-template-columns: 1fr; }
  .skills .skill:last-child { grid-column: auto; }

  /* Deux colonnes, toujours alignées. */
  .founders { grid-template-columns: repeat(2, 1fr); }

  /* Conviction : le surtitre passe au-dessus du texte et cesse d'être
     collant. À cette largeur la colonne prendrait un tiers de l'écran
     pour une ligne de petites capitales, et une colonne collante de la
     hauteur de l'écran n'a plus rien à quoi se coller. */
  .belief { grid-template-columns: 1fr; gap: clamp(20px, 4vw, 28px); }
  .belief-rail { position: static; }
  .belief-body { max-width: none; }
}

@media (max-width: 560px) {
  body { font-size: 16px; }
  .hero-cta .btn { flex: 1 1 100%; }

  /* Une colonne : portraits empilés. */
  .founders {
    grid-template-columns: 1fr;
    gap: 40px;
  }
  .founders .founder.reveal { transform: translateY(22px); }
  .founders .founder.reveal.is-visible { transform: none; }
  /* Le portrait plein cadre est trop haut sur mobile : on l'aplatit. */
  .founder-media { aspect-ratio: 1 / 1; }
  /* Pas de survol au doigt : la pastille reste visible. */
  .founder-badge { opacity: 1; transform: none; }
  .founder-photo { filter: grayscale(0); }
  .founder-veil { opacity: .18; }
}

/* ============================================================
   BANNIÈRE LOGOS (TICKER / MARQUEE)
   ============================================================ */
.section-logos {
  overflow: hidden;
}

.logo-ticker {
  position: relative;
  width: 100%;
  overflow: hidden;
  padding: 28px 0;
  -webkit-mask-image: linear-gradient(to right, transparent, #000 10%, #000 90%, transparent);
  mask-image: linear-gradient(to right, transparent, #000 10%, #000 90%, transparent);
}

.logo-ticker-track {
  display: flex;
  align-items: center;
  gap: clamp(48px, 6vw, 88px);
  width: max-content;
  will-change: transform;
  animation: logo-marquee 35s linear infinite;
}

.logo-item {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  height: 52px;
  padding: 0 8px;
}

.logo-item img {
  height: clamp(32px, 3.5vw, 44px);
  width: auto;
  max-width: 180px;
  object-fit: contain;
  filter: grayscale(100%) opacity(0.55);
  transition: filter 0.35s var(--ease), transform 0.35s var(--ease);
}

/* Survol individuel du logo : mise en valeur visuelle */
.logo-item:hover img {
  filter: grayscale(0%) opacity(1);
  transform: scale(1.08);
}

@keyframes logo-marquee {
  0% { transform: translateX(0); }
  100% { transform: translateX(calc(-100% / 3)); }
}

/* ============================================================
   PRÉFÉRENCES UTILISATEUR
   ============================================================ */
/* Note pour la conviction : l'encrage au défilement n'est pas neutralisé
   ici, il n'est jamais posé — sa règle vit sous
   `@media (prefers-reduced-motion: no-preference)`. Neutraliser après coup
   aurait laissé les mots à l'opacité .2 le temps d'une cascade. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
  .reveal { opacity: 1; transform: none; }

  /* Les portraits restent alignés et visibles sans animation. */
  .founders .founder.reveal,
  .founders .founder.reveal.is-visible { opacity: 1; transform: none; }

  /* Intro du logo désactivée : les animations ci-dessus se terminent en
     `forwards`, la grille finirait donc figée à opacity 0 et la diagonale
     aussi. On rétablit explicitement l'état final — logo simplement là. */
  .logo-mark.is-intro .logo-matrix { display: none; }
  .logo-mark.is-intro .logo-sq,
  .logo-mark.is-intro .logo-word {
    opacity: 1;
    transform: none;
    animation: none;
  }
}

@media print {
  .site-header, .hero-field, .contact-form { display: none; }
  body { background: #fff; }
}
