/* ==========================================================================
   SKYNET — LANGAGE « CLAIR »
   Direction arrêtée par Yann le 03/08/2026, mot pour mot :
   « beaucoup de blanc sur les pages, lumineux, police grasse et facile à lire »
   « aircall et unyc sont moderne, dynamique et épuré »
   « UX très bien pensé, on se laisse conduire par la visite »

   Le nuit ne disparaît pas : il devient un MOMENT, une seule section dans
   toute la page. Il frappe parce qu'il est le seul.

   À charger APRÈS skynet.css et skynet-design.css, et APRÈS skynet-nuit.css
   (dont on ne garde que les composants scopés en .nuit).
   ========================================================================== */

:root{
  /* L'encre passe du gris-noir au bleu-nuit : c'est ce qui fait tenir toute la
     page ensemble avec l'indigo de marque. Contraste sur blanc : 17,2:1. */
  --encre:#0E1330;
  --texte:#464D6B;        /* corps de texte — 7,9:1 sur blanc                 */
  /* ⚠️ CE TOKEN A MENTI DEUX FOIS. HISTORIQUE COMPLET, PARCE QUE LA LEÇON VAUT
     PLUS QUE LA VALEUR.
     03/08 — commenté « 4,6:1 » alors qu'il mesurait 4,44:1, SOUS le seuil AA.
       Deux agents ont « corrigé » des contrastes en basculant dessus, en faisant
       confiance au commentaire. Valeur et commentaire repris.
     04/08 — le commentaire disait « 4,70:1 sur blanc, VÉRIFIÉ ». C'était vrai
       SUR BLANC. Mais huit ninjas ont mesuré 4,42:1 sur le fond --papier
       (#F7F8FC) du bandeau de parcours, où ce token est justement employé.
       Un contraste ne se vérifie pas dans l'absolu : il se vérifie CONTRE LE
       FOND SUR LEQUEL LE TEXTE EST RÉELLEMENT POSÉ. Les deux tokens portent
       désormais leurs DEUX chiffres, et sont assombris pour passer partout.
     Un commentaire faux est plus dangereux qu'une absence de commentaire. */
  --texte-doux:#5C6384;   /* secondaire — 5,88:1 blanc · 5,54:1 papier         */
  --texte-mini:#626987;   /* légendes  — 5,40 blanc · 5,09 papier · 4,87 p-2   */
  --ligne:#E7EAF3;        /* filets et bordures                               */
  --ligne-forte:#D3D9E9;
  --papier:#F7F8FC;       /* panneaux et sections de respiration              */
  --papier-2:#F1F3FA;

  /* Ombres en DEUX couches. Une ombre unique et floue, c'est le marqueur du
     gabarit gratuit : dans la vraie vie un objet pose un contact serré ET
     diffuse une ombre large. */
  --ombre-1:0 1px 2px rgba(14,19,48,.05);
  --ombre-2:0 1px 2px rgba(14,19,48,.05), 0 10px 26px rgba(14,19,48,.06);
  --ombre-3:0 2px 4px rgba(14,19,48,.06), 0 18px 44px rgba(14,19,48,.10);
  --ombre-4:0 3px 6px rgba(14,19,48,.07), 0 30px 70px rgba(14,19,48,.14);

  --courbe:cubic-bezier(.22,1,.36,1);      /* sortie franche, arrivée douce   */
  --courbe-doux:cubic-bezier(.4,0,.2,1);
}

/* ── LE FOND : blanc, mais pas mort ───────────────────────────────────────
   Deux voiles de marque à opacité minuscule, posés en haut de page. Ça doit
   se SENTIR et jamais se voir : sur une capture, on ne doit pas les nommer. */
body{background:#fff;color:var(--encre)}
body::before{content:"";position:fixed;inset:0;z-index:-1;pointer-events:none;
  background:
    radial-gradient(58rem 34rem at 88% -8%, rgba(41,171,226,.09) 0%, transparent 62%),
    radial-gradient(46rem 30rem at 4% 2%,  rgba(46,49,146,.06) 0%, transparent 60%)}

/* ── RESPIRATION : « épuré » veut dire plus de vide, pas des blocs plus petits */
.section{padding-block:clamp(4.5rem, 2.6rem + 5.4vw, 8.5rem)}
.section-sm{padding-block:clamp(3rem, 2rem + 3vw, 5rem)}
.s-blanc{background:#fff}
.s-papier{background:var(--papier)}
.s-clair{background:linear-gradient(180deg,#F4FAFE 0%,#FFFFFF 100%)}

/* Le raccord entre deux sections : un filet qui s'efface sur les bords, jamais
   une ligne franche d'un bord à l'autre. */
.raccord{position:relative}
.raccord::before{content:"";position:absolute;top:0;left:12%;right:12%;height:1px;
  background:linear-gradient(90deg,transparent,var(--ligne-forte) 22%,var(--ligne-forte) 78%,transparent)}

/* ── EN-TÊTE CLAIR ───────────────────────────────────────────────────────── */
.entete{background:rgba(255,255,255,.86);backdrop-filter:blur(14px) saturate(150%);
  border-bottom:1px solid transparent;transition:border-color .3s var(--courbe-doux),
  box-shadow .3s var(--courbe-doux),background .3s var(--courbe-doux)}
.entete.defile{border-bottom-color:var(--ligne);box-shadow:var(--ombre-1);
  background:rgba(255,255,255,.94)}
.entete .nav a{color:var(--texte);font-weight:550;position:relative;padding-block:.4rem}
.entete .nav a::after{content:"";position:absolute;left:0;right:100%;bottom:0;height:2px;
  border-radius:2px;background:var(--indigo-500);transition:right .28s var(--courbe)}
.entete .nav a:hover{color:var(--encre)}
.entete .nav a:hover::after{right:0}
.entete .nav a[aria-current="page"]{color:var(--indigo-500);font-weight:650}
.entete .nav a[aria-current="page"]::after{right:0}
.entete .tel-entete{color:var(--encre);font-weight:600}
.entete .tel-entete svg{stroke:var(--indigo-500)}
.entete .burger svg{stroke:var(--encre)}

/* ── TYPOGRAPHIE DE PAGE ─────────────────────────────────────────────────── */
.display,h1{color:var(--encre)}
h2,h3,h4,.titre-xl{color:var(--encre)}
.chapo,.chapo-xl{color:var(--texte)}
.chapo-xl{font-size:clamp(1.12rem, 1.02rem + .55vw, 1.42rem);line-height:1.6}
p{color:var(--texte)}
.petit{color:var(--texte-doux)}
.mini{color:var(--texte-mini)}

/* Le mot en dégradé du titre : sur fond clair la rampe de marque est LISIBLE
   telle quelle (indigo 10,66:1). On garde le geste identitaire. */
.display em,.titre-xl em{font-style:normal;
  background:linear-gradient(96deg,#2E3192 0%,#3E6FBE 55%,#1B7AA6 100%);
  -webkit-background-clip:text;background-clip:text;color:transparent}

/* Le sur-titre : le seul cyan lisible sur blanc, à 4,7:1 */
.kicker,.sur-titre{color:var(--cyan-700);font-weight:700;font-size:.78rem}

/* ── CARTES ──────────────────────────────────────────────────────────────
   Sur fond blanc, une carte n'existe que par son filet et son ombre. C'est
   là que tout se joue : trop, ça fait lourd ; pas assez, ça flotte. */
.carte{background:#fff;border:1px solid var(--ligne);border-radius:18px;
  padding:var(--s8) var(--s6);box-shadow:var(--ombre-1);
  transition:transform .34s var(--courbe),box-shadow .34s var(--courbe),
             border-color .34s var(--courbe)}
.carte:hover{transform:translateY(-5px);box-shadow:var(--ombre-3);
  border-color:var(--indigo-100)}
.carte h3{font-size:1.24rem;font-weight:700;margin-bottom:var(--s3)}
.carte p{font-size:1rem;line-height:1.62;color:var(--texte)}

/* Le liseré de couleur en haut de carte — repris de la plaquette, mais en
   filet fin plutôt qu'en bandeau : sur clair, un aplat écrase. */
.carte-filet::before{content:"";position:absolute;left:var(--s6);right:var(--s6);top:0;height:3px;
  border-radius:0 0 3px 3px;transition:left .34s var(--courbe),right .34s var(--courbe)}
.carte-filet:hover::before{left:0;right:0}
.c1::before{background:var(--cyan-500)}
.c2::before{background:#3D7BD6}
.c3::before{background:#4A4BC4}
.c4::before{background:var(--indigo-500)}

/* La tuile d'icône : le dégradé de marque sur une surface, jamais sous du texte */
.tuile{width:56px;height:56px;border-radius:16px;display:grid;place-items:center;
  margin-bottom:var(--s5);flex-shrink:0;background:var(--degrade-d);
  box-shadow:0 6px 18px rgba(var(--rgb-indigo),.24)}
.tuile svg{width:27px;height:27px;stroke:#fff;fill:none;stroke-width:1.9;
  stroke-linecap:round;stroke-linejoin:round}
.tuile-sm{width:46px;height:46px;border-radius:13px;margin-bottom:var(--s4)}
.tuile-sm svg{width:22px;height:22px}
.tuile-1{background:linear-gradient(140deg,#29ABE2 0%,#1A7FAB 100%)}
.tuile-2{background:linear-gradient(140deg,#4A8BE0 0%,#2E5FB8 100%)}
.tuile-3{background:linear-gradient(140deg,#5B5CD4 0%,#2E3192 100%)}
.tuile-4{background:linear-gradient(140deg,#3B3EA8 0%,#22246B 100%)}

/* Le lien fléché de bas de carte */
.lien-fleche{color:var(--indigo-500);font-weight:650;font-size:.97rem;text-decoration:none;
  display:inline-flex;align-items:center;gap:.45rem;margin-top:auto;padding-top:var(--s5)}
.lien-fleche svg{width:16px;height:16px;stroke:currentColor;fill:none;stroke-width:2.2;
  transition:transform .3s var(--courbe)}
.carte:hover .lien-fleche svg{transform:translateX(5px)}

/* ── PHOTOS SUR FOND CLAIR ───────────────────────────────────────────────
   Une photo posée nue sur du blanc flotte et fait « article de blog ».
   Il lui faut un ancrage : filet fin, ombre en deux couches, coins francs. */
.photo{position:relative;border-radius:20px;overflow:hidden;background:var(--papier);
  border:1px solid var(--ligne);box-shadow:var(--ombre-2)}
.photo img{width:100%;height:100%;object-fit:cover;display:block}
.photo::after{content:none}
.photo-ratio-16-9{aspect-ratio:16/9}
.photo-ratio-4-3{aspect-ratio:4/3}
.photo-ratio-3-2{aspect-ratio:3/2}

/* La légende sort de l'image et devient éditoriale : sur fond clair, du texte
   blanc sur un voile sombre n'a plus aucune raison d'être. */
.photo-legende{position:static;padding:0;margin-top:var(--s4);
  font-size:.92rem;font-weight:550;line-height:1.5;color:var(--texte-doux);
  text-shadow:none;display:flex;gap:.6rem;align-items:flex-start}
.photo-legende::before{content:"";width:16px;height:2px;background:var(--cyan-500);
  border-radius:2px;margin-top:.62em;flex-shrink:0}

/* Le badge flottant, version claire */
.photo-badge{position:absolute;z-index:4;display:inline-flex;align-items:center;gap:.5rem;
  padding:.6rem 1rem;border-radius:999px;font-size:.83rem;font-weight:650;color:var(--encre);
  background:rgba(255,255,255,.94);border:1px solid var(--ligne);
  backdrop-filter:blur(10px);box-shadow:var(--ombre-2);white-space:nowrap}
.photo-badge i{width:8px;height:8px;border-radius:50%;background:var(--cyan-500);flex-shrink:0;
  box-shadow:0 0 0 4px rgba(41,171,226,.20)}
.photo-badge.vert i{background:#12A46B;box-shadow:0 0 0 4px rgba(18,164,107,.18)}

/* Le duo décalé */
.photo-duo{position:relative;padding-bottom:var(--s12);padding-right:var(--s6)}
.photo-duo .p1{width:90%}
.photo-duo .p2{position:absolute;right:0;bottom:0;width:52%;
  border:4px solid #fff;box-shadow:var(--ombre-4)}
@media(max-width:640px){
  .photo-duo{padding-bottom:var(--s10);padding-right:var(--s10)}
  .photo-duo .p1{width:88%}
  .photo-duo .p2{width:66%}
}

/* Le produit détouré : fond de marque très clair, jamais un aplat saturé */
.photo-produit{background:linear-gradient(150deg,#EEF1FB 0%,#E6F4FC 100%);
  border:1px solid var(--ligne);border-radius:22px;padding:var(--s10) var(--s8);
  display:grid;place-items:center;box-shadow:var(--ombre-1)}
.photo-produit img{max-width:100%;height:auto;
  filter:drop-shadow(0 22px 40px rgba(14,19,48,.20))}

/* ── LA SECTION « MOMENT » EN NUIT ───────────────────────────────────────
   Une seule dans toute la page. Coins arrondis et marges latérales pour
   qu'elle se lise comme un objet posé sur la page, pas comme un changement
   de décor subi. */
.moment{background:linear-gradient(165deg,#0A1230 0%,#131C48 55%,#0A1230 100%);
  border-radius:32px;margin-inline:clamp(0px, -1.5rem + 4vw, 2.5rem);
  position:relative;overflow:hidden;box-shadow:0 40px 90px rgba(14,19,48,.28)}
.moment::before{content:"";position:absolute;inset:0;pointer-events:none;opacity:.5;
  background-image:radial-gradient(rgba(255,255,255,.10) 1px,transparent 1px);
  background-size:26px 26px;
  -webkit-mask-image:radial-gradient(70% 60% at 50% 40%,#000,transparent);
  mask-image:radial-gradient(70% 60% at 50% 40%,#000,transparent)}
.moment>*{position:relative;z-index:1}
.moment h2,.moment h3,.moment h4,.moment .titre-xl{color:#fff}
.moment p{color:#C3C9E4}
.moment .kicker{color:#7CCBEE}
@media(max-width:720px){.moment{border-radius:22px;margin-inline:0}}

/* ── LES CHIFFRES ────────────────────────────────────────────────────────── */
.stats-xl{gap:var(--s10) var(--s8);align-items:start}
.stat-xl b{display:block;font-size:clamp(2.4rem, 1.7rem + 2.1vw, 3.6rem);font-weight:800;
  line-height:1;letter-spacing:-.035em;color:var(--indigo-500);
  background:none;-webkit-background-clip:border-box;background-clip:border-box;
  margin-bottom:var(--s3)}
.stat-xl span{display:block;font-size:.98rem;line-height:1.5;color:var(--texte-doux)}

/* ── LISTES ──────────────────────────────────────────────────────────────── */
.liste li{color:var(--texte);font-size:1.02rem;line-height:1.62}
.liste li strong{color:var(--encre);font-weight:650}
.liste li::before{background:var(--degrade-h);top:.62em}
.liste-check li::before{border-color:var(--indigo-500);top:.3em}

/* ── LA PREUVE CLIENT ────────────────────────────────────────────────────── */
.preuve{margin-top:var(--s8);padding:var(--s5) var(--s6);border-left:3px solid var(--cyan-500);
  background:var(--papier);border-radius:0 14px 14px 0;
  font-size:1.02rem;font-weight:600;color:var(--encre);line-height:1.55}

/* ── BOUTONS ─────────────────────────────────────────────────────────────── */
.btn{font-weight:650}
.btn-1{background:var(--degrade-texte);color:#fff;
  box-shadow:0 4px 14px rgba(var(--rgb-indigo),.26)}
.btn-1:hover{color:#fff;box-shadow:0 10px 28px rgba(var(--rgb-indigo),.34);
  transform:translateY(-2px)}
.btn-2{background:#fff;color:var(--encre);border-color:var(--ligne-forte);
  box-shadow:var(--ombre-1)}
.btn-2:hover{border-color:var(--indigo-300);color:var(--indigo-500);
  box-shadow:var(--ombre-2);transform:translateY(-2px)}
.btn:active{transform:translateY(0)}
.btn:focus-visible,a:focus-visible,button:focus-visible,
input:focus-visible,textarea:focus-visible{
  outline:3px solid rgba(41,171,226,.55);outline-offset:3px;border-radius:var(--r-full)}

/* ── LE BLOC D'ACTION FINAL ──────────────────────────────────────────────── */
.cta-bloc{background:var(--degrade-texte);color:#fff;border-radius:32px;
  box-shadow:0 34px 80px rgba(var(--rgb-indigo),.30)}
.cta-bloc h2,.cta-bloc .titre-xl{color:#fff}
.cta-bloc p{color:rgba(255,255,255,.90);max-width:56ch;margin-inline:auto}
.cta-bloc .btn-3{background:#fff;color:var(--indigo-500);font-weight:700}
.cta-bloc .btn-3:hover{background:#fff;transform:translateY(-2px);
  box-shadow:0 12px 30px rgba(0,0,0,.22)}

/* ── LA BANDE DE TECHNOLOGIES ────────────────────────────────────────────── */
.bande-tech{border-top:1px solid var(--ligne);border-bottom:1px solid var(--ligne);
  padding-block:var(--s8)}
/* ⚠️ RÈGLE MORT-NÉE, RÉPARÉE LE 04/08 — piège de spécificité, le troisième du
   projet. « .bande-tech-titre » (0,1,0) était écrasé par « .bande-tech span »
   de skynet-design.css:132 (0,1,1) : une classe seule ne bat jamais une classe
   + un élément, quel que soit l'ordre des feuilles. Quatre ninjas ont mesuré
   la couleur RÉELLEMENT calculée par le navigateur : rgb(148,154,168), soit
   2,82:1 sur blanc — le pire contraste de texte du site — et une taille de
   16,3 px au lieu des 11,8 px prévus. Le sur-titre criait au lieu de chuchoter.
   En qualifiant la classe par son élément, on repasse à 0,1,1 ET on gagne
   l'ordre des feuilles. La taille est reprise en même temps, pour la même
   raison : elle venait de la même règle trop générale. */
.bande-tech>span.bande-tech-titre{color:var(--texte-mini);font-size:.74rem}
.bande-tech>span:not(.bande-tech-titre){color:var(--texte);font-weight:600}

/* ── PIED DE PAGE ────────────────────────────────────────────────────────── */
.pied{background:var(--encre);color:#A7AECB;margin-top:0}
.pied h4{color:#fff}
.pied a{color:#A7AECB;text-decoration:none}
.pied a:hover{color:#fff}

/* ── RÉVÉLATIONS EN CASCADE ──────────────────────────────────────────────── */
.apparait{opacity:0;transform:translateY(22px);
  transition:opacity .7s var(--courbe),transform .7s var(--courbe)}
.apparait.vu{opacity:1;transform:none}
.grille>.apparait:nth-child(1){transition-delay:0ms}
.grille>.apparait:nth-child(2){transition-delay:90ms}
.grille>.apparait:nth-child(3){transition-delay:180ms}
.grille>.apparait:nth-child(4){transition-delay:270ms}

::selection{background:rgba(46,49,146,.16);color:var(--encre)}

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

/* Le lien d'évitement : invisible jusqu'à ce qu'on tabule dessus. Obligatoire
   pour la navigation clavier, et c'est le tout premier élément de la page. */
.saut-contenu{position:absolute;left:-9999px;top:0;z-index:200;
  background:var(--indigo-500);color:#fff;padding:.8rem 1.4rem;border-radius:0 0 12px 0;
  font-weight:650;text-decoration:none}
.saut-contenu:focus{left:0}

/* Le logo dans l'en-tête clair */
.entete .logo img{height:36px;width:auto}
@media(max-width:640px){.entete .logo img{height:30px}}

/* La barre d'action mobile, version claire */
.barre-mobile{background:rgba(255,255,255,.96);backdrop-filter:blur(14px);
  border-top:1px solid var(--ligne);box-shadow:0 -6px 24px rgba(14,19,48,.07)}

/* Le bandeau de conclusion, dans la section « moment » */
.moment .bandeau{background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.13)}
.moment .bandeau p{color:#fff;font-weight:650}
.moment .nuit-tete .sous{color:#9FA7CC}

/* ── CORRECTIFS MESURÉS AU NAVIGATEUR ────────────────────────────────────── */

/* 1. Le filet de carte ne s'affichait qu'en pointillé : la règle d'origine fixe
      une LARGEUR, et quand left+right+width coexistent en absolu, c'est width
      qui gagne et right qui est ignoré. */
.carte-filet::before{width:auto}

/* 2. Les cartes d'une même rangée n'avaient pas la même hauteur et le lien
      « Découvrir » flottait au milieu. */
.grille>.carte{display:flex;flex-direction:column}
.grille>.carte p{flex:1}

/* 3. « 10 000 km » cassait en deux : Poppins est une géométrique, elle prend
      ~8 % de largeur en plus. On cale le chiffre sur quatre colonnes. */
.stat-xl b{font-size:clamp(2rem, 1.5rem + 1.5vw, 2.9rem)}

/* 4. Le titre du hero partait sur quatre lignes dont une orpheline. On laisse
      l'équilibrage automatique faire la coupe plutôt qu'un <br> forcé. */
.hero-xl .display{font-size:clamp(2.35rem, 1.3rem + 3.1vw, 3.9rem);max-width:13ch}

/* 5. Les titres de section à deux lignes : même traitement. */
.titre-xl{max-width:18ch}
.section-tete.centre .titre-xl,.section-tete.centre .chapo{margin-inline:auto}

/* Les étiquettes du schéma en étoile passent de h4 à h3 : sous un h2, un h4 fait
   sauter un niveau et un lecteur d'écran annonce une hiérarchie cassée. */
.moment .sat h3{font-size:1.02rem;font-weight:700;color:#fff;margin:0 0 .2rem;letter-spacing:-.01em}
.moment .sat span{font-size:.83rem;color:#9AA2C8;display:block;line-height:1.45}

/* Les titres de colonne du pied passent en h3 (hiérarchie continue). */
.pied h3{color:#fff;font-size:1rem;font-weight:700;margin-bottom:var(--s4);letter-spacing:.02em}

/* ── DÉBORDEMENT HORIZONTAL : LE PIÈGE DE min-width:auto ──────────────────
   Un enfant de grille ou de flex vaut « min-width:auto » par défaut : il REFUSE
   de descendre sous la largeur intrinsèque de son contenu. Une photo déclarée
   width="699" impose donc 699 px à sa colonne, et la page déborde sur téléphone
   même si l'image porte max-width:100%.
   Mesuré : 398 px de contenu dans un écran de 375. Correctif de classe, pas
   d'instance — toutes les grilles du site sont concernées.
   ────────────────────────────────────────────────────────────────────── */
.alterne>*,.grille>*,.hero-xl .wrap>*,.stats-xl>*,.pied-grille>*{min-width:0}
.alterne-visuel,.hero-visuel{max-width:100%}
.alterne-visuel figure,.alterne-visuel .photo,.alterne-visuel img{max-width:100%}

/* Un tableau large reste lisible : il défile DANS son cadre, jamais la page. */
.tableau-wrap{overflow-x:auto;-webkit-overflow-scrolling:touch;
  border:1px solid var(--ligne);border-radius:14px;background:#fff}
.tableau-wrap table{margin:0}

/* ── UN BOUTON NE DOIT JAMAIS FAIRE DÉFILER LA PAGE ──────────────────────
   .btn porte white-space:nowrap : un libellé long dépasse alors l'écran et
   crée un défilement horizontal sur téléphone. Mesuré : 398 px de contenu
   dans un écran de 375. Sur petit écran, le libellé a le droit de passer
   à la ligne — c'est toujours mieux qu'une page qui glisse latéralement. */
@media(max-width:560px){
  .btn{white-space:normal;text-align:center;max-width:100%}
  .hero-actions,.cta-actions{width:100%}
  .hero-actions .btn,.cta-actions .btn{width:100%}
}

/* ══════════════════════════════════════════════════════════════════════════
   TROIS DÉFAUTS TROUVÉS PAR LE VERIFY ADVERSE — 03/08/2026, 02h30
   Les trois sont dans MES feuilles, pas dans les pages. Ils touchaient donc
   les 10 pages d'un coup. Aucun n'était visible sans mesure au navigateur.
   ══════════════════════════════════════════════════════════════════════════ */

/* 1. BLOQUANT — LES PHOTOS ÉTAIENT FLOUES SUR TABLETTE ET PETIT PORTABLE.
      Ma règle « .alterne-visuel,.hero-visuel{max-width:100%} » neutralisait
      celle de skynet-design.css « @media(max-width:980px){…max-width:520px} » :
      même spécificité (0,1,0), une media query n'en ajoute pas, et ma feuille
      est chargée en dernier. Résultat mesuré entre 640 et 980 px de large :
      le routeur (566 px natif) affiché à 738 px = ×1,30 · la baie (537 px)
      à 646 px = ×1,20. Une image agrandie au-delà de sa définition est floue,
      et c'est exactement la largeur d'un iPad ou d'un petit portable. */
/*    ⚠️ CORRECTIF DU 04/08 — « width:100% » AJOUTÉ, ET IL N'EST PAS COSMÉTIQUE.
      Sans lui, dans une grille, « margin-inline:auto » annule l'étirement : la
      boîte repasse en largeur-du-contenu. Un SVG sans attribut « width » n'a
      alors plus rien pour se dimensionner et retombe sur sa taille par défaut
      de 300 px — la règle « svg{width:100%} » plus bas devient circulaire et
      ne sert à rien. MESURÉ par un shogun sur l'accueil : le visuel de hero
      faisait 300 × 173 px au lieu de 472 × 272. Le défaut touchait TOUTES les
      pages qui posent une illustration : c'était une classe de bug.
      Avec « width:100% », la largeur est définie (100 % de la case, plafonnée
      à 520 px) et les marges automatiques ne centrent plus que le reste. */
.alterne-visuel,.hero-visuel{width:100%;max-width:min(100%, 520px);margin-inline:auto}

/*    ⚠️ 04/08 — LE PLAFOND DE 520 px NE DOIT PAS S'APPLIQUER AUX ILLUSTRATIONS.
      Il existe pour une seule raison : empêcher une PHOTO d'être affichée plus
      large que sa définition, ce qui la rend floue. Un SVG n'a pas de
      définition — il est net à n'importe quelle taille. Le plafond bridait donc
      mes propres dessins à 520 px dans une colonne qui en offre 560, sans
      aucune contrepartie : je m'interdisais du visuel pour protéger un flou
      qui ne peut pas exister.
      ⚠️ HONNÊTETÉ DE MESURE : en écrivant cette règle j'ai annoncé que c'était
      « le principal plafond du ratio visuel ». C'ÉTAIT FAUX, et la mesure me
      l'a dit tout de suite : les trois leviers globaux de ce lot n'ont rapporté
      que +0,7 point (22,7 → 23,4 %). La raison est arithmétique et je l'avais
      manquée — agrandir un visuel existant ajoute la même surface au NUMÉRATEUR
      et au DÉNOMINATEUR, donc ça ne déplace presque rien. Le seul levier fort,
      c'est de RETIRER de la surface purement textuelle. La règle reste : elle
      est juste, et elle rend les illustrations plus belles. Mais elle ne
      méritait pas ma grande phrase.
      « :has() » est disponible sur tous les navigateurs à jour ; ailleurs, on
      retombe simplement sur l'affichage précédent, un peu plus petit. */
.alterne-visuel:has(> .illu),.hero-visuel:has(> .illu),
.alterne-visuel:has(> svg),.hero-visuel:has(> svg){max-width:100%}

/* 2. LE SUR-TITRE ÉTAIT INVISIBLE DANS LA SECTION SOMBRE — 1,67:1.
      « .moment .kicker » ne repeignait que la COULEUR DU TEXTE en cyan clair,
      en laissant la pastille sur son fond cyan très pâle d'origine (--cyan-50).
      Du cyan pâle sur du cyan pâle. Il fallait aussi reprendre le fond. */
.moment .kicker,.moment .sur-titre{
  background:rgba(255,255,255,.08);
  border-color:rgba(255,255,255,.18);
  color:#8FD3F2}                      /* 8,4:1 sur le fond nuit */

/* 3. LES EN-TÊTES « CENTRÉS » NE L'ÉTAIENT PAS.
      J'écrivais <div class="section-tete centre"> alors qu'aucune feuille ne
      définit « .section-tete.centre » : skynet.css ne connaît que le sélecteur
      DESCENDANT « .centre .section-tete ». Décalage mesuré : 409 px à 1440 px.
      Le titre se lisait franchement à gauche d'un bloc annoncé centré. */
.section-tete.centre{margin-inline:auto;text-align:center}
.section-tete.centre .chapo,.section-tete.centre .titre-xl,
.section-tete.centre h2,.section-tete.centre p{margin-inline:auto}

/* 4. Le pied portait une couleur en style en ligne, recopiée sur les 10 pages.
      Une couleur ne s'écrit jamais dans le HTML : elle devient impossible à
      changer d'un seul geste. */
.pied strong{color:#fff}
.pied table caption,caption{text-align:left}

/* ══════════════════════════════════════════════════════════════════════════
   NAVIGATION À PANNEAUX — 03/08/2026, repositionnement ESN
   Le modèle vient du meilleur étalon relevé : deux niveaux MAXIMUM, des
   panneaux à colonnes, jamais trois étages. Accessible au clavier : le
   déclencheur est un vrai <button>, aria-expanded suit l'état, Échap ferme.
   ══════════════════════════════════════════════════════════════════════════ */
.nav-groupe{position:relative;display:inline-flex}
.nav-declencheur{display:inline-flex;align-items:center;gap:.35rem;
  font:inherit;font-weight:550;color:var(--texte);background:none;border:0;
  cursor:pointer;padding:.4rem 0;letter-spacing:inherit}
.nav-declencheur svg{width:14px;height:14px;stroke:currentColor;fill:none;
  stroke-width:2.2;stroke-linecap:round;stroke-linejoin:round;
  transition:transform .25s var(--courbe)}
.nav-declencheur:hover{color:var(--encre)}
.nav-groupe.ouvert .nav-declencheur{color:var(--indigo-500)}
.nav-groupe.ouvert .nav-declencheur svg{transform:rotate(180deg)}

.nav-panneau{position:absolute;top:calc(100% + 14px);left:50%;
  transform:translateX(-50%) translateY(6px);min-width:340px;
  background:#fff;border:1px solid var(--ligne);border-radius:16px;
  box-shadow:var(--ombre-4);padding:var(--s3);z-index:120;
  opacity:0;visibility:hidden;pointer-events:none;
  transition:opacity .22s var(--courbe),transform .22s var(--courbe),visibility 0s .22s}
.nav-groupe.ouvert .nav-panneau{opacity:1;visibility:visible;pointer-events:auto;
  transform:translateX(-50%) translateY(0);transition-delay:0s}
/* Le pont invisible entre le bouton et le panneau : sans lui, la souris
   « tombe » dans l'interstice et le menu se referme au passage. */
.nav-panneau::before{content:"";position:absolute;left:0;right:0;top:-14px;height:14px}

.nav-panneau a{display:block;padding:.7rem .85rem;border-radius:10px;
  text-decoration:none;transition:background .18s var(--courbe)}
.nav-panneau a:hover{background:var(--papier)}
.nav-panneau a strong{display:block;font-weight:650;color:var(--encre);font-size:.98rem}
.nav-panneau a span{display:block;font-size:.83rem;color:var(--texte-doux);
  line-height:1.45;margin-top:2px}
.nav-panneau a:hover strong{color:var(--indigo-500)}

/* Le soulignement animé ne s'applique qu'aux liens plats, pas aux boutons. */
.entete .nav .nav-declencheur::after{content:none}

/* ── Mobile : le menu devient un accordéon plein écran ────────────────── */
@media(max-width:980px){
  .nav-groupe{display:block;width:100%}
  .nav-declencheur{width:100%;justify-content:space-between;padding:.65rem 0;
    font-size:1.05rem;font-weight:650;color:var(--encre)}
  .nav-panneau{position:static;transform:none;min-width:0;border:0;box-shadow:none;
    border-radius:0;padding:0 0 .5rem .85rem;
    display:none;opacity:1;visibility:visible;pointer-events:auto}
  .nav-groupe.ouvert .nav-panneau{display:block;transform:none}
  .nav-panneau::before{content:none}
}

/* ── Le sur-titre passe en chasse fixe : il dit « technique » sans un mot.
     C'est le procédé relevé chez l'étalon n°1 de Yann — la pile système
     suffit, aucun téléchargement de plus. Une ligne à retirer s'il n'aime pas. */
.kicker,.sur-titre{font-family:var(--police-mono);font-size:.74rem;
  letter-spacing:.16em;font-weight:600}

/* Le sommaire du glossaire : ses libellés de carte ne sont pas des titres de
   section (ils précédaient le premier h2 et cassaient la hiérarchie pour un
   lecteur d'écran). Ils gardent l'apparence d'un titre de carte, sans le rang. */
.carte .carte-titre{font-size:1.24rem;font-weight:700;color:var(--encre);
  margin-bottom:var(--s3);letter-spacing:-.015em;line-height:1.25}

/* ── LES ILLUSTRATIONS DE MARQUE ──────────────────────────────────────────
   Quatre photos pour vingt pages : la baie apparaissait seize fois, l'espace
   de travail quinze. Une image répétée seize fois n'illustre plus rien.
   Chaque pôle reçoit donc SON dessin, dans le dégradé de marque — ownable,
   sans droits à payer, et net à toutes les tailles. */
.illu{width:100%;height:auto;display:block;max-width:560px;margin-inline:auto}
.illu text{font-family:var(--police)}
.alterne-visuel .illu,.hero-visuel .illu{max-width:100%}

/* ══════════════════════════════════════════════════════════════════════════
   CORRECTIFS DE L'AUDIT VISUEL — 03/08/2026
   Trouvés en REGARDANT les pages rendues, pas en lisant le code. Aucun n'était
   détectable par script : ce sont des défauts de composition.
   ══════════════════════════════════════════════════════════════════════════ */

/* 1. BLOQUANT — « 10 000 km » cassait en deux lignes dans les cartes sombres
      alors que « 100 % », « 0 » et « 24/7 » tenaient sur une : la rangée de
      chiffres partait en escalier et la légende glissait sous le voisin. */
.nuit .stat-xl b,.s-nuit .stat-xl b,.moment .stat-xl b{
  font-size:clamp(1.85rem, 1.15rem + 1.35vw, 2.5rem);white-space:nowrap}

/* 2. BLOQUANT — sur téléphone, le duo de photos s'effondrait : la vignette du
      routeur recouvrait 55 % de la baie, qui devenait une bande verticale
      illisible. Recouvrement ramené à ~36 %. */
@media(max-width:640px){
  .hero-xl .photo-duo .p1{width:78%}
  .hero-xl .photo-duo .p2{width:58%}
}

/* 3. IMPORTANT — .alterne centrait verticalement ses deux colonnes : quand le
      texte était deux fois plus haut que le visuel, il restait 250 à 350 px de
      blanc mort en haut à droite. Les colonnes s'alignent désormais par le haut,
      et le visuel suit le défilement au lieu de flotter au milieu du vide. */
.alterne{align-items:start}
@media(min-width:981px){
  .alterne-visuel{position:sticky;top:calc(var(--s20) + 1rem)}
}

/* 4. IMPORTANT — dans une même rangée, un titre de carte sur une ligne et son
      voisin sur deux désalignaient tous les paragraphes en dessous. Les titres
      réservent la place de deux lignes. */
.grille.g4>.carte h3,.grille.g3>.carte h3{min-height:calc(2 * 1.25em)}
@media(max-width:720px){
  .grille.g4>.carte h3,.grille.g3>.carte h3{min-height:0}
}

/* ══════════════════════════════════════════════════════════════════════════
   SIX DÉFAUTS TROUVÉS PAR L'AUDIT VISUEL — tous dans MES feuilles, donc tous
   sur les 20 pages à la fois. Aucun n'était détectable par script.
   ══════════════════════════════════════════════════════════════════════════ */

/* 1. BLOQUANT — TOUS LES SCHÉMAS S'AFFICHAIENT À 300 px DANS UNE COLONNE DE 536.
      Un SVG sans largeur explicite retombe sur sa taille intrinsèque par défaut.
      La règle existait pour .hero-visuel, jamais pour .alterne-visuel : la moitié
      des mises en page avait un petit dessin perdu au milieu d'une demi-page vide. */
.alterne-visuel svg,.alterne-visuel .illu,
.hero-visuel svg,.hero-visuel .illu{width:100%;height:auto;display:block}

/* 2. BLOQUANT — LE TITRE DES BLOCS SOMBRES ÉTAIT DÉCALÉ DE 206 px.
      .nuit-tete.centre ne donnait margin-inline:auto qu'au chapô, alors que
      .titre-xl porte max-width:18ch : le titre restait collé à gauche pendant
      que la pastille et le chapô, eux, étaient bien centrés. */
.nuit-tete.centre>*,.moment .nuit-tete.centre>*,
.section-tete.centre>*{margin-inline:auto}
.nuit-tete.centre,.moment .nuit-tete.centre{text-align:center}

/* 3. BLOQUANT — LES JAMBAGES DU MOT EN DÉGRADÉ ÉTAIENT TRANCHÉS NET.
      background-clip:text découpe sur la boîte du texte : sans réserve basse,
      les descendantes (p, g, q, j) sont coupées au ras. */
.display em,.titre-xl em{padding-bottom:.14em;margin-bottom:-.14em;
  padding-right:.08em}

/* 4. BLOQUANT — 174 BLOCS ÉTAIENT INVISIBLES SANS JAVASCRIPT.
      .apparait part à opacity:0 et n'est révélé que par l'observateur. Si le JS
      ne se charge pas — réseau, extension, robot d'indexation strict — un tiers
      du site n'existe pas. On ne masque QUE si le JS a pu s'annoncer. */
.apparait{opacity:1;transform:none}
.js .apparait{opacity:0;transform:translateY(22px)}
.js .apparait.vu{opacity:1;transform:none}

/* 5. IMPORTANT — LE SUR-TITRE ÉTAIT À 4,7:1 SUR SA PASTILLE CLAIRE.
      Le token existait déjà : --cyan-800 donne 7,81:1 sur #EDF8FD. */
.kicker,.sur-titre{color:var(--cyan-800)}

/* 6. BLOQUANT SUR TÉLÉPHONE — LES GRANDS TABLEAUX ÉTAIENT AMPUTÉS.
      table{min-width:520px} dans un cadre à défilement : la 3e colonne sortait
      de l'écran et rien n'indiquait qu'il fallait faire glisser. Sous 700 px,
      chaque ligne devient une fiche empilée, en-tête repris en étiquette. */
@media(max-width:700px){
  .tableau-wrap{overflow-x:visible;border:0;background:none;border-radius:0}
  .tableau-wrap table{min-width:0;width:100%;display:block}
  .tableau-wrap thead{position:absolute;width:1px;height:1px;overflow:hidden;
    clip-path:inset(50%);white-space:nowrap}
  .tableau-wrap tbody,.tableau-wrap tr,.tableau-wrap td,.tableau-wrap th{display:block;width:auto}
  .tableau-wrap tbody tr{background:#fff;border:1px solid var(--ligne);
    border-radius:14px;padding:var(--s4) var(--s5);margin-bottom:var(--s4);
    box-shadow:var(--ombre-1)}
  .tableau-wrap tbody td{border:0;padding:var(--s2) 0}
  .tableau-wrap tbody td::before{content:attr(data-libelle);display:block;
    font-size:.72rem;font-weight:700;letter-spacing:.1em;text-transform:uppercase;
    color:var(--texte-mini);margin-bottom:.2rem}
  .tableau-wrap tbody td:first-child{font-weight:650;color:var(--encre)}
  .tableau-wrap caption{text-align:left;margin-bottom:var(--s4)}
}

/* ── LE GLOSSAIRE : un rail unique et deux colonnes ───────────────────────
   Ses quatre sections étaient en wrap-etroit alors que le reste de la page
   est en pleine largeur : 211 px de décalage sur 85 % de la hauteur, ça se
   lit comme un bug et non comme une intention. Et 28 accordéons empilés en
   une colonne pincée faisaient 5 600 px de mur. */
@media(min-width:900px){
  #pole-telecom .faq,#pole-cybersecurite .faq,#pole-ia .faq,#pole-digital .faq{
    display:grid;grid-template-columns:1fr 1fr;gap:var(--s5);align-items:start}
}

/* Le badge flottant d'une photo ne doit jamais sortir de son cadre : sur
   petit écran, un libellé long débordait de la vignette qui le porte. */
.photo .photo-badge{max-width:calc(100% - 2 * var(--s4))}
@media(max-width:640px){
  .photo .photo-badge{font-size:.72rem;padding:.4rem .7rem;
    left:var(--s3);right:auto;max-width:calc(100% - 2 * var(--s3))}
  .photo .photo-badge.haut-droite{right:var(--s3);left:auto}
}

/* ── L'ADRESSE E-MAIL PROTÉGÉE ────────────────────────────────────────────
   Le repli sans JavaScript s'affiche via ::after ; dès que le script a
   reconstitué l'adresse, il remplace le contenu et le repli disparaît. */
.courriel{color:var(--indigo-500);font-weight:650;text-decoration:none;
  text-underline-offset:3px;word-break:break-word}
.courriel:hover{text-decoration:underline}
.js .courriel[data-courriel]{visibility:hidden}

/* ── LA CARTE DE LOCALISATION ─────────────────────────────────────────────
   Dessinée, pas embarquée : un cadre Google Maps déposerait l'IP du visiteur
   chez un tiers avant tout consentement — sur un site qui promet « aucun
   transfert hors UE », ce serait se contredire soi-même en page contact. */
.carte-lieu{display:grid;grid-template-columns:1.35fr 1fr;gap:0;
  border:1px solid var(--ligne);border-radius:22px;overflow:hidden;
  background:#fff;box-shadow:var(--ombre-3)}
.carte-lieu-plan{width:100%;height:100%;display:block;object-fit:cover}
.carte-lieu-infos{padding:clamp(1.5rem, 1rem + 2vw, 2.75rem);display:flex;
  flex-direction:column;justify-content:center;gap:var(--s4)}
.carte-lieu-adresse{font-size:1.06rem;line-height:1.55;color:var(--encre)}
.carte-lieu-adresse strong{font-weight:700}
.carte-lieu-detail{font-size:.97rem;line-height:1.6;color:var(--texte)}
.carte-lieu-actions{display:flex;gap:var(--s3);flex-wrap:wrap;margin-top:var(--s2)}
.carte-lieu-horaires{font-size:.92rem;line-height:1.55;color:var(--texte-doux);
  padding-top:var(--s4);border-top:1px solid var(--ligne)}
@media(max-width:860px){
  .carte-lieu{grid-template-columns:1fr}
  .carte-lieu-plan{aspect-ratio:16/10}
}
@media(prefers-reduced-motion:reduce){
  .carte-lieu-plan animate{display:none}
}

/* ══════════════════════════════════════════════════════════════════════════
   LA COUCHE VISUELLE — ordre de Yann du 04/08/2026
   « des images pleine largeur sur chaque page, c'est la touche SkyNet »
   « plus de 30% de contenus visuels : images, photos et animations »
   « les gens ne lisent plus, ils regardent »
   État de départ mesuré : UN visuel pour 448 mots. Cible : un pour 150.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── LA PLEINE LARGEUR ────────────────────────────────────────────────────
   Sortir du conteneur de 1180 px et toucher les deux bords de l'écran.
   On mesure sur documentElement.clientWidth et non sur 100vw : 100vw inclut
   la barre de défilement et produit un débordement horizontal de 15 px sur
   Windows — la faute classique de cette technique. */
.pleine{
  width:calc(100vw - var(--barre-defilement, 0px));
  margin-inline:calc(50% - 50vw + var(--barre-defilement, 0px) / 2);
  position:relative}

/* ── BANDEAU PHOTO PLEINE LARGEUR ─────────────────────────────────────── */
.bande{position:relative;overflow:hidden;isolation:isolate;
  /* Relevé le 04/08 : un bandeau pleine largeur qui ne fait que 520 px de haut
     sur un écran de 1440 se lit comme un encart, pas comme un moment. */
  min-height:clamp(340px, 30vw + 180px, 660px);
  display:grid;align-items:center}
.bande>img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;z-index:-2}
.bande::before{content:"";position:absolute;inset:0;z-index:-1;
  background:linear-gradient(100deg,rgba(10,18,48,.92) 0%,rgba(10,18,48,.72) 46%,rgba(10,18,48,.34) 100%)}
.bande .wrap{position:relative;z-index:1;padding-block:clamp(2.5rem,1.5rem + 3vw,4rem)}
.bande h2,.bande .titre-xl{color:#fff;max-width:20ch}
.bande p{color:rgba(255,255,255,.90);max-width:52ch;margin-top:var(--s5);font-size:1.06rem}
/* ⚠️ 04/08 — LE SUR-TITRE DES BANDEAUX ÉTAIT SOUS LE SEUIL AA.
   Il était en #8FD3F2 sur une pastille blanche à 10 % : le texte flottait donc
   directement sur le dégradé, dont la couleur change d'un bout à l'autre.
   Calculé : 6,48:1 sur l'extrémité marine, mais 3,56:1 au milieu — un ninja
   l'a relevé sur les pixels rendus, à 4,38:1. Un contraste qui dépend de
   l'endroit où le texte tombe dans un dégradé n'est pas un contraste.
   Correction : texte BLANC sur une pastille SOMBRE translucide. Le fond de la
   pastille suit le dégradé mais l'assombrit assez pour que le blanc passe
   partout. Vérifié aux deux extrémités avec _contraste.py :
     côté cyan   (#29ABE2 assombri à 42 %) → 5,82:1
     côté marine (#2E3192 assombri à 42 %) → 14,81:1
   Le pire cas passe AA pour du texte courant, et le sur-titre est en 0,74 rem. */
.bande .kicker{color:#fff;background:rgba(14,19,48,.42);
  border-color:rgba(255,255,255,.30)}
/* La pastille du sur-titre est peinte avec le dégradé horizontal de marque
   (skynet-design.css) : sur un fond déjà dégradé, sa moitié marine disparaît
   et le point n'apparaît qu'à moitié. Sur un bandeau, il devient uni. */
.bande .kicker::before{background:#8FD3F2}
/* Un <strong> sur un bandeau sombre héritait de « strong,b{color:var(--encre)} »
   (skynet.css) : encre marine sur photo noire, donc illisible. La règle porte
   sur l'élément lui-même, elle bat donc l'héritage de « .bande p ». Deux
   ninjas s'étaient INTERDIT d'écrire <strong> dans un bandeau pour la
   contourner — c'est le symptôme d'un défaut de feuille, pas d'un choix. */
.bande strong,.bande b,.moment strong,.moment b{color:#fff}
.bande em{font-style:normal;
  background:linear-gradient(96deg,#8E91EE 0%,#29ABE2 60%,#7CCBEE 100%);
  -webkit-background-clip:text;background-clip:text;color:transparent;
  padding-bottom:.14em;margin-bottom:-.14em}

/* Variante dégradé de marque, sans photo — quand aucune image ne convient */
.bande-degrade{background:linear-gradient(118deg,#2E3192 0%,#2B58AC 52%,#1B7AA6 100%)}
.bande-degrade::before{content:"";
  background-image:radial-gradient(rgba(255,255,255,.14) 1px,transparent 1px);
  background-size:28px 28px;opacity:.7;
  -webkit-mask-image:radial-gradient(70% 70% at 70% 30%,#000,transparent);
  mask-image:radial-gradient(70% 70% at 70% 30%,#000,transparent)}

/* ── BANDEAU DE CHIFFRES ANIMÉS ───────────────────────────────────────── */
.bande-chiffres{background:var(--papier);border-block:1px solid var(--ligne);
  padding-block:clamp(2.5rem,1.5rem + 3vw,4.5rem)}
.chiffres-rangee{display:grid;grid-template-columns:repeat(auto-fit,minmax(230px,1fr));
  gap:var(--s10) var(--s8);text-align:center}
/* ⚠️ PAS DE DÉGRADÉ DE TEXTE ICI, ET C'EST UNE RÈGLE GÉNÉRALE.
   background-clip:text découpe le fond sur la boîte de l'élément qui le porte.
   Dès qu'il y a des balises ENFANTS — ici un <span> pour le compteur et un
   autre pour l'unité — celles-ci héritent de color:transparent SANS hériter du
   fond : elles deviennent purement invisibles. Le chiffre disparaissait.
   Le dégradé de texte ne s'emploie que sur du texte SANS enfant. */
.chiffre b{display:block;font-size:clamp(2rem, 1.2rem + 2.2vw, 3.3rem);font-weight:800;
  line-height:1;letter-spacing:-.04em;white-space:nowrap;
  color:var(--indigo-500);font-variant-numeric:lining-nums}
.chiffre b>span{color:inherit;display:inline;font-size:inherit;font-weight:inherit}
.chiffre>span{display:block;margin-top:var(--s3);font-size:.97rem;line-height:1.5;
  color:var(--texte);max-width:24ch}
/* Le recentrage n'appartient qu'à la RANGÉE centrée. Posé sur .chiffre tout
   court, il recentrait la légende dès qu'un bloc réemployait le composant en
   alignement à gauche : 135 px de décalage mesurés par un ninja, la légende
   ne tombait plus sous son propre chiffre. */
.chiffres-rangee .chiffre>span{margin-inline:auto}
/* .unite supprimée : le suffixe fait partie du texte du compteur. */

/* ── BANDEAU DE PARCOURS HORIZONTAL ───────────────────────────────────── */
.parcours{display:grid;grid-auto-flow:column;grid-auto-columns:1fr;gap:var(--s6);
  counter-reset:etape;position:relative;padding-top:var(--s10)}
.parcours::before{content:"";position:absolute;top:calc(var(--s10) + 21px);left:6%;right:6%;
  height:2px;background:linear-gradient(90deg,var(--indigo-500),var(--cyan-500))}
/* ⚠️ 04/08 — LE TITRE PASSAIT SOUS LA PASTILLE NUMÉROTÉE. Signalé par SEPT
   ninjas indépendants, chacun l'ayant contourné dans SA page avec un
   padding-top en ligne : quand sept pages contournent la même règle, ce n'est
   plus une page qui a tort, c'est la feuille.
   L'arithmétique : la pastille fait 44 px de haut, posée à top:0, PLUS un
   anneau blanc de 6 px (box-shadow 0 0 0 6px #fff). Elle occupe donc de −6 px
   à +50 px. Le padding-top valait var(--s10) = 40 px : le titre démarrait
   10 px À L'INTÉRIEUR de la pastille, et l'anneau blanc, peint par-dessus,
   lui rognait le haut des lettres.
   62 px laisse 12 px de respiration sous l'anneau. La position du filet de
   liaison, elle, ne bouge pas : il est calé sur .parcours, pas sur .etape. */
.etape{position:relative;text-align:center;padding-top:calc(var(--s10) + 22px)}
.etape::before{counter-increment:etape;content:counter(etape);
  position:absolute;top:0;left:50%;transform:translateX(-50%);
  width:44px;height:44px;border-radius:50%;display:grid;place-items:center;
  font-weight:800;font-size:1rem;color:#fff;
  background:linear-gradient(140deg,#2E3192,#1B7AA6);
  box-shadow:0 0 0 6px #fff, 0 6px 18px rgba(46,49,146,.28)}
.etape h3{font-size:1.04rem;font-weight:700;margin-bottom:var(--s2)}
.etape p{font-size:.93rem;line-height:1.55;color:var(--texte-doux)}
/* ══════════════════════════════════════════════════════════════════════════
   LE PARCOURS SUR MOBILE — RÉÉCRIT LE 04/08/2026.
   C'est le défaut le plus signalé de tout le lot : NEUF ninjas, sur neuf pages
   différentes, ont photographié la même chose. Le filet de liaison passait en
   trait VERTICAL à left:calc(50% - 1px) sur toute la hauteur du bloc, pendant
   que .etape restait en text-align:center et sans fond opaque. Résultat : le
   trait BARRAIT littéralement les titres et les phrases — « On dé|ploie »,
   « On re|cense ». Mesuré à 390 et à 768 px.
   J'avais écrit ça sans jamais regarder le rendu mobile : une frise
   horizontale ne se transpose pas en la faisant simplement pivoter.
   La forme juste est la frise verticale classique : la pastille et le rail à
   GAUCHE, le texte aligné à gauche à côté. Le rail relie alors les pastilles
   au lieu de traverser les mots. Rail à 21 px = l'axe d'une pastille de 44 px
   posée à left:0 ; le texte démarre à 68 px, soit 24 px après elle.
   ══════════════════════════════════════════════════════════════════════════ */
@media(max-width:860px){
  .parcours{grid-auto-flow:row;gap:var(--s8)}
  .parcours::before{left:21px;right:auto;top:var(--s10);bottom:var(--s10);
    width:2px;height:auto;background:linear-gradient(180deg,var(--indigo-500),var(--cyan-500))}
  .etape{text-align:left;padding-top:0;padding-left:68px;min-height:44px}
  .etape::before{left:0;transform:none;top:0}
}

/* ── LA MOSAÏQUE VISUELLE ─────────────────────────────────────────────── */
/* Rangées à la taille de l'écran, pas figées à 150 px : sur un grand écran, une
   mosaïque haute de 310 px au milieu d'une page de 10 000 px ne se remarque
   même pas. Elle passe à ~420 px en 1440, et reste sage sur petit écran. */
.mosaique{display:grid;grid-template-columns:repeat(4,1fr);gap:var(--s4);
  grid-auto-rows:minmax(clamp(160px, 5rem + 8vw, 240px),auto)}
.mosaique>*{border-radius:18px;overflow:hidden;position:relative;
  border:1px solid var(--ligne);box-shadow:var(--ombre-1)}
.mosaique .m-large{grid-column:span 2;grid-row:span 2}
.mosaique .m-haut{grid-row:span 2}
.mosaique img{width:100%;height:100%;object-fit:cover;display:block}
@media(max-width:860px){
  .mosaique{grid-template-columns:repeat(2,1fr)}
  /* ⚠️ 04/08 — LA GRANDE TUILE PRENAIT TOUTE LA LARGEUR, ET FLOUTAIT LA PHOTO.
     Dix ninjas ont mesuré la même chose : sous 861 px la mosaïque passe à deux
     colonnes, et « span 2 » faisait donc occuper à .m-large la TOTALITÉ du
     conteneur — 702 px relevés à 768 px de fenêtre. Or la photo la plus définie
     de la bibliothèque fait 623 px, et celle qui y est posée 537 : un
     agrandissement de ×1,31, soit une image molle sur exactement la largeur
     d'une tablette.
     Sur deux colonnes, une tuile « large » n'a plus de sens de toute façon :
     elle redevient une tuile parmi quatre, à ~350 px, donc sous la définition
     native de toutes les photos. Le grain revient, et la mosaïque reste une
     mosaïque. C'est le seul correctif possible sans nouvelles photos. */
  .mosaique .m-large,.mosaique .m-haut{grid-column:span 1;grid-row:span 1}
}

/* ── LES ANIMATIONS ───────────────────────────────────────────────────── */
@keyframes sk-flux{to{stroke-dashoffset:-120}}
@keyframes sk-pulse{0%{transform:scale(1);opacity:.55}100%{transform:scale(1.45);opacity:0}}
@keyframes sk-monte{from{opacity:0;transform:translateY(26px)}to{opacity:1;transform:none}}
/* ⚠️ 04/08 — SÉLECTEUR BEAUCOUP TROP LARGE, corrigé. Six ninjas ont
   photographié la même chose : « .anim-flux path » attrapait TOUS les <path>
   du dessin, pas seulement les courbes de flux. La silhouette « Votre
   interlocuteur » sortait donc hachée en pointillés, comme si elle clignotait.
   Deux pages l'avaient déjà contourné par un style en ligne — le symptôme.
   La correction s'appuie sur le balisage RÉEL : dans les cinq illustrations et
   dans le bandeau, les traits de flux sont TOUJOURS regroupés dans un <g> dont
   l'attribut stroke vaut une référence de dégradé — « stroke="url(#ia-flux)" ».
   Les pictogrammes, eux, portent une couleur en dur. Le sélecteur d'attribut
   distingue donc exactement les deux, SANS toucher une seule page. La classe
   « .flux » est acceptée en plus, pour les dessins futurs. */
.js .anim-flux g[stroke^="url("] path,
.js .anim-flux .flux path{stroke-dasharray:9 11;animation:sk-flux 3.4s linear infinite}
.js .anim-pulse{transform-origin:center;animation:sk-pulse 2.8s ease-out infinite}

/* La cascade : chaque enfant entre légèrement après son voisin.
   ⚠️ RÈGLE ABSOLUE — L'ANIMATION EST ADDITIVE, JAMAIS SOUSTRACTIVE.
   Version précédente : les enfants partaient à opacity:0 et on les révélait.
   C'est le schéma qui a rendu 174 blocs invisibles le 03/08, et je venais de
   le refaire. Ici, RIEN n'est masqué par défaut : l'animation ne se déclenche
   qu'avec la classe .vu, et `backwards` lui fait tenir son état de départ
   pendant son propre délai. Si le JavaScript ne tourne pas, si l'observateur
   ne se déclenche jamais, si un robot lit la page : tout est visible. */
.js .cascade.vu>*{animation:sk-monte .7s var(--courbe) backwards}
.js .cascade>*:nth-child(1){animation-delay:0ms}
.js .cascade>*:nth-child(2){animation-delay:90ms}
.js .cascade>*:nth-child(3){animation-delay:180ms}
.js .cascade>*:nth-child(4){animation-delay:270ms}
.js .cascade>*:nth-child(5){animation-delay:360ms}
.js .cascade>*:nth-child(6){animation-delay:450ms}

@media(prefers-reduced-motion:reduce){
  /* « animation:none » suffisait à ARRÊTER le flux, mais laissait le trait en
     pointillés : un visiteur en mouvement réduit voyait donc un schéma aux
     traits hachés et figés, ce qui ressemble à un dessin cassé plutôt qu'à un
     dessin apaisé. Il faut aussi rendre le trait continu. Relevé par le ninja
     de hotellerie, qui a pensé à photographier CE mode-là. */
  .js .anim-flux path,.js .anim-pulse{animation:none;stroke-dasharray:none}
  .js .cascade>*{opacity:1;animation:none}
}

/* ══════════════════════════════════════════════════════════════════════════
   LE HERO CINÉTIQUE — boucle de 9 secondes, 100 % CSS
   Technique reprise du motion design du graphiste de Yann (réutilisation
   autorisée le 04/08/2026) : mots en cadre plein, coupes rapides, fonds qui
   se relaient, sortie sur la marque.
   Tout est calé sur UNE durée commune : --cine. Changer cette variable
   change le tempo de toute la séquence d'un coup.
   ══════════════════════════════════════════════════════════════════════════ */
.cine{--cine:9s;
  position:relative;overflow:hidden;isolation:isolate;
  aspect-ratio:16/9;max-height:min(78vh, 660px);width:100%;
  border-radius:26px;background:#0A1230;
  box-shadow:0 30px 80px rgba(14,19,48,.30)}

/* Le texte réel : visible par défaut, masqué seulement si l'animation tourne */
.cine-texte{position:absolute;inset:0;display:grid;place-items:center;padding:8%;
  color:#fff;font-size:1.15rem;line-height:1.6;text-align:center;z-index:5}
/* La règle globale « p{color:var(--texte)} » repeint tout <p> enfant : la
   couleur posée sur le conteneur ne suffit pas. Sans ça, le texte de repli du
   hero — celui que voient Google et un visiteur sans JavaScript — sortait en
   gris ardoise sur fond marine. Relevé par le ninja de l'accueil. */
.cine-texte p{color:#fff}
.js .cine-texte{position:absolute;width:1px;height:1px;overflow:hidden;
  clip-path:inset(50%);white-space:nowrap;padding:0}

/* ── LES FONDS ─────────────────────────────────────────────────────────── */
.cine-fonds,.cine-fond{position:absolute;inset:0}
.cine-fond{opacity:0}
.cine-fond img{width:100%;height:100%;object-fit:cover;display:block;
  filter:saturate(.55) contrast(1.06)}
.cine-fond::after{content:"";position:absolute;inset:0;
  background:linear-gradient(180deg,rgba(10,18,48,.55),rgba(10,18,48,.78))}
.cine-fond.f1{background:linear-gradient(128deg,#2E3192 0%,#3B5FC0 46%,#29ABE2 100%)}
.cine-fond.f5{background:linear-gradient(128deg,#0A1230 0%,#1A2A6B 52%,#123E63 100%)}
.cine-fond.f1::after,.cine-fond.f5::after{content:none}
/* La trame de points : elle donne de la matière aux deux aplats */
.cine-fond.f1::before,.cine-fond.f5::before{content:"";position:absolute;inset:0;
  background-image:radial-gradient(rgba(255,255,255,.16) 1px,transparent 1px);
  background-size:26px 26px;opacity:.6}

/* ── LES MOTS ──────────────────────────────────────────────────────────── */
.cine-mots{position:absolute;inset:0;display:grid;place-items:center;z-index:2}
.cine-mot{position:absolute;opacity:0;
  /* ⚠️ 04/08 — RELEVÉ SUR CAPTURE : les mots plafonnaient à 57 px dans un
     cadre de 1 132 px. Le mot le plus long n'occupait que la moitié de la
     largeur : ça flotte, alors que tout l'effet du motion design de référence
     vient de mots qui REMPLISSENT le cadre. Recalculé pour que « NOUS
     CONSTRUISONS », le plus long des cinq, occupe environ 80 % de la largeur
     sur grand écran, et tienne encore sans déborder à 390 px.
     Le calcul tient compte du fait que le cadre vaut ~79 % de la fenêtre. */
  font-size:clamp(1.4rem, .3rem + 5.4vw, 5rem);font-weight:800;
  letter-spacing:-.02em;line-height:1;color:#fff;white-space:nowrap;
  background:#0E1330;padding:.22em .5em;border-radius:.14em;
  box-shadow:0 18px 44px rgba(0,0,0,.42)}
.cine-mot i{font-style:normal;color:#29ABE2}

/* ── LA SORTIE ─────────────────────────────────────────────────────────── */
.cine-fin{position:absolute;inset:0;display:flex;flex-direction:column;
  align-items:center;justify-content:center;gap:var(--s5);z-index:3;
  opacity:0;padding:6%;text-align:center}
.cine-fin img{width:clamp(140px, 18vw, 230px);height:auto}
.cine-fin-phrase{font-size:clamp(1.15rem, .7rem + 1.6vw, 2rem);font-weight:800;
  line-height:1.15;letter-spacing:-.03em;color:#fff;max-width:22ch}
.cine-fin-phrase em{font-style:normal;color:#7CCBEE}
.cine-fin-detail{font-size:clamp(.78rem, .7rem + .3vw, .95rem);color:#A7B0D8;
  font-family:var(--police-mono);letter-spacing:.06em}

/* ── LA JAUGE DE BOUCLE ────────────────────────────────────────────────── */
.cine-jauge{position:absolute;left:0;right:0;bottom:0;height:3px;
  background:rgba(255,255,255,.14);z-index:4}
.cine-jauge i{display:block;height:100%;width:0;
  background:linear-gradient(90deg,#29ABE2,#7CCBEE)}

/* ══ LA CHORÉGRAPHIE — 9 s, découpées en pourcentages ══
   0-11 % BONJOUR (aplat) · 11-25 % NOUS OPÉRONS · 25-39 % VOS LIENS
   39-54 % NOUS CONSTRUISONS · 54-68 % VOS AGENTS IA · 68-100 % la marque   */
@keyframes cine-fond1{0%,10%{opacity:1}14%,100%{opacity:0}}
@keyframes cine-fond2{9%{opacity:0}13%,24%{opacity:1}28%,100%{opacity:0}}
@keyframes cine-fond3{23%{opacity:0}27%,38%{opacity:1}42%,100%{opacity:0}}
@keyframes cine-fond4{37%{opacity:0}41%,53%{opacity:1}57%,100%{opacity:0}}
@keyframes cine-fond5{52%{opacity:0}56%,96%{opacity:1}100%{opacity:0}}

/* Les mots arrivent d'un coup sec et repartent en fondu : c'est ce contraste
   qui donne l'impression de coupe franche du montage d'origine. */
@keyframes cine-mot1{0%{opacity:0;transform:scale(.9)}2%,9%{opacity:1;transform:none}12%,100%{opacity:0}}
@keyframes cine-mot2{11%{opacity:0;transform:scale(.9)}13%,22%{opacity:1;transform:none}25%,100%{opacity:0}}
@keyframes cine-mot3{25%{opacity:0;transform:scale(.9)}27%,36%{opacity:1;transform:none}39%,100%{opacity:0}}
@keyframes cine-mot4{39%{opacity:0;transform:scale(.9)}41%,51%{opacity:1;transform:none}54%,100%{opacity:0}}
@keyframes cine-mot5{54%{opacity:0;transform:scale(.9)}56%,64%{opacity:1;transform:none}67%,100%{opacity:0}}
@keyframes cine-sortie{68%{opacity:0;transform:translateY(14px)}74%,97%{opacity:1;transform:none}100%{opacity:0}}
@keyframes cine-jauge{0%{width:0}100%{width:100%}}

.js .cine-fond.f1{animation:cine-fond1 var(--cine) linear infinite}
.js .cine-fond.f2{animation:cine-fond2 var(--cine) linear infinite}
.js .cine-fond.f3{animation:cine-fond3 var(--cine) linear infinite}
.js .cine-fond.f4{animation:cine-fond4 var(--cine) linear infinite}
.js .cine-fond.f5{animation:cine-fond5 var(--cine) linear infinite}
.js .cine-mot.m1{animation:cine-mot1 var(--cine) linear infinite}
.js .cine-mot.m2{animation:cine-mot2 var(--cine) linear infinite}
.js .cine-mot.m3{animation:cine-mot3 var(--cine) linear infinite}
.js .cine-mot.m4{animation:cine-mot4 var(--cine) linear infinite}
.js .cine-mot.m5{animation:cine-mot5 var(--cine) linear infinite}
.js .cine-fin{animation:cine-sortie var(--cine) linear infinite}
.js .cine-jauge i{animation:cine-jauge var(--cine) linear infinite}

/* ── SANS JAVASCRIPT, OU SI LE VISITEUR VEUT MOINS DE MOUVEMENT ──────────
   On ne montre PAS une séquence figée au hasard : on affiche directement
   l'image de fin, celle qui porte le message. Rien ne clignote, rien ne
   manque, et le message passe. */
@media(prefers-reduced-motion:reduce){
  .js .cine-fond,.js .cine-mot,.js .cine-fin,.js .cine-jauge i{animation:none}
  .js .cine-fond.f5{opacity:1}
  .js .cine-fin{opacity:1}
  .js .cine-jauge{display:none}
}

@media(max-width:640px){
  .cine{aspect-ratio:4/5;max-height:none;border-radius:20px}
  .cine-mot{white-space:normal;text-align:center;max-width:86%;line-height:1.12}
}

/* ══════════════════════════════════════════════════════════════════════════
   LE RETOUR DU FORMULAIRE DE CONTACT — 04/08/2026
   Le récepteur PHP ne réaffiche jamais la saisie du visiteur (ce serait la
   porte d'entrée d'une injection). Il renvoie un code court, que skynet.js
   traduit en phrase. Voici l'habillage de cette phrase.
   Le message n'est PAS masqué par défaut en CSS : il porte l'attribut HTML
   « hidden », que le JavaScript retire. Un bloc masqué par le style et révélé
   par un observateur, c'est le schéma qui a rendu 174 blocs invisibles le
   03/08 — on ne le refait pas.
   ══════════════════════════════════════════════════════════════════════════ */
.reponse-envoi{margin-bottom:var(--s6);padding:var(--s5) var(--s6);
  border-radius:var(--r-md);border:1px solid var(--ligne);
  background:var(--papier);color:var(--texte);
  font-size:1.02rem;line-height:1.55}
.reponse-envoi[data-etat="erreur"]{
  border-color:#E4B4B4;background:#FDF4F4;color:#7A2E2E}
.reponse-envoi:focus{outline:3px solid var(--indigo-500);outline-offset:3px}

/* ══════════════════════════════════════════════════════════════════════════
   .titre-md — L'ÉCHELON MANQUANT, ajouté le 04/08/2026.

   Le site n'avait que deux tailles de titre : « .display » pour le <h1> et
   « .titre-xl » pour les ouvertures de section. Il manquait le cran d'en
   dessous, celui d'un titre À L'INTÉRIEUR d'un bloc — l'encadré « Ce qu'il
   faut retenir » d'un article, par exemple.
   ⚠️ C'est MOI qui ai employé cette classe dans le gabarit d'article sans
   l'avoir créée : quatre articles sur douze sont sortis avec un titre non
   stylé, c'est-à-dire au corps du texte. Le contrôle l'a vu, pas moi.
   Une classe utilisée mais jamais définie ne produit RIEN, et ça ne se voit
   pas à la relecture du code — seulement au rendu.
   Taille calée entre .titre-xl (2 → 3,2 rem) et le texte courant. */
.titre-md{font-size:clamp(1.3rem, 1.05rem + .8vw, 1.72rem);
  font-weight:800;line-height:1.2;letter-spacing:-.015em;
  color:var(--encre);margin-bottom:var(--s4);max-width:26ch}
.titre-md em{font-style:normal;color:var(--indigo-600)}
.note .titre-md{margin-top:0}
