/* =========================================================================
   ALEGIX — surcharges applicatives
   =========================================================================
   Chargé APRÈS style.css, et lui seul a le droit d'exister ici : le gabarit
   Egacy est vendor, on n'y touche pas. Toute correction de rendu propre à
   ALEGIX vient dans ce fichier.

   Règle de tenue : chaque bloc dit ce qu'il corrige et pourquoi. Une
   surcharge dont on a oublié la raison finit par être supprimée à tort, ou
   conservée à tort.

   Les sélecteurs sont volontairement longs : ils reprennent la chaîne du
   gabarit pour gagner en spécificité sans recourir à !important, qui rendrait
   les surcharges suivantes impossibles à écrire proprement.
   ========================================================================= */

/*-------------------------------*/
/* 2025 - CUSTOM ALEGIX 		 */
/*-------------------------------*/
.responsive {
	max-width: 100%;
	height: auto;
	display: block;
	margin-left: auto;
	margin-right: auto;
}

.font-size-medium {
	font-size: medium;
}

/* Messages d'alerte */
.alert {
    border-radius: 8px !important;
    border: none !important;
    margin-bottom: 1rem !important;
}

.alert-success {
    background-color: #d4edda !important;
    color: #155724 !important;
    border-left: 2px solid #28a745 !important;
}

.alert-danger {
    background-color: #f8d7da !important;
    color: #721c24 !important;
    border-left: 2px solid #dc3545 !important;
}

.alert-warning {
	background-color: #fff3cd !important;
	color: #856404 !important;
	border-left: 2px solid #856404 !important;
}

/* Champs requis */
.form-control:required {
    border-left: 2px solid #ffc107 !important;
}

/* Validation */
.is-invalid {
    border-color: #dc3545 !important;
    box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25) !important;
}

.is-valid {
    border-color: #28a745 !important;
    box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25) !important;
}




/* -------------------------------------------------------------------------
   Compteurs de la bannière d'accueil

   Le gabarit dimensionne ces compteurs pour un usage en pleine section :
   nombre en 48 px, libellé en 22 px Russo One, bloc d'information contraint
   à 190 px de large. Dans la bannière, à côté du bouton d'appel à l'action,
   deux compteurs à cette taille écrasent le slogan — qui est pourtant le
   premier message de la page.

   On réduit donc l'échelle sans toucher au style : même police, même filet
   dégradé, mêmes proportions. Le compteur redevient une note en marge du
   slogan, ce qu'il aurait toujours dû être.

   Le libellé ne descend PAS sous 16 px. Le corps de référence du site est de
   18 px et 16 px est le plus petit corps que le gabarit s'autorise, dans ses
   media queries. En dessous, en Russo One — une police d'affichage, dessinée
   pour les grandes tailles — le texte cesse d'être lisible. Un premier essai
   à 13 px l'a montré.
   ------------------------------------------------------------------------- */
.main-banner.main-banner-2 .banner-inner .banner-conent .counter .counter-number {
  font-size: 34px;
  line-height: 40px;
}

.main-banner.main-banner-2 .banner-inner .banner-conent .counter .counter-title {
  font-size: 16px;
  line-height: 22px;
}

/* 190 px imposait un retour à la ligne dès le troisième mot. À 16 px en Russo
   One, « d'indisponibilité » occupe à lui seul près de 160 px : il faut 230 px
   pour que le libellé tienne en deux lignes sans césure hasardeuse. */
.main-banner.main-banner-2 .banner-inner .banner-conent .counter.counter-style-1 .counter-info {
  width: 230px;
}

.main-banner.main-banner-2 .banner-inner .banner-conent .counter.counter-style-1 {
  padding: 12px 0;
}


/* -------------------------------------------------------------------------
   Bandeau de références — logos clients

   Le bandeau ne porte plus que des logos : à six références, un logo PLUS un
   nom en 42 px par item rendait la boucle interminable. Le nom subsiste dans
   l'attribut alt de l'image.

   Les logos portent donc la bande seuls, d'où des hauteurs relevées : ils
   avaient d'abord été calés pour cohabiter avec un texte de 42 px.

   Ils sont sur fond noir, où le bleu marine d'Alstom serait proprement
   invisible.

   D'où le passage en blanc monochrome (brightness(0) puis invert(1)) : le
   logo devient une silhouette blanche et sa lisibilité ne dépend plus de sa
   couleur de marque.

   Le second problème est géométrique. Les six logos vont de 0,83 (RATP, en
   portrait) à 4,23 (Médiane Système). À hauteur égale, RATP ferait 31 px de
   large contre 138 pour Alstom : optiquement, deux poids sans rapport. On
   fixe donc une hauteur de référence pour les logos larges, relevée au cas
   par cas pour les formats hauts, de façon à égaliser la masse perçue —
   c'est ainsi qu'on aligne des logos, jamais sur une seule dimension. Les
   trois valeurs conservent entre elles le rapport établi à l'origine.
   ------------------------------------------------------------------------- */
/* Le vide en fin de boucle, et pourquoi il apparaissait.

   Le gabarit anime .marquee-inner de translateX(0) à translateX(-100%), sur
   un élément dont la largeur vaut 100 % du conteneur. Le bloc se déplace donc
   de la largeur de la FENÊTRE, jamais de celle de son contenu. Avec les dix
   noms d'origine en 42 px, le contenu dépassait deux fois l'écran et le
   raccord ne se voyait pas. Réduit à huit logos, il est bien plus court : en
   fin de course, la fenêtre montre du vide sur presque toute sa largeur.

   Trois corrections, indissociables :

   1. width: max-content — la largeur du bloc devient celle de son contenu,
      condition pour qu'un pourcentage de translation veuille dire quelque
      chose.

   2. L'écart passe de gap à margin-right sur chaque item. Avec gap, un jeu de
      huit logos compte sept écarts internes mais huit une fois répété : la
      translation d'un tiers exact tombe alors à côté d'un demi-écart. Porté
      par l'item, l'écart est inclus dans le jeu et la division est juste.

   3. Le HTML répète le jeu quatre fois et l'animation translate d'un quart,
      soit exactement un jeu. Le raccord est invisible, et les trois jeux
      restants couvrent 4311 px — un écran 4K compris. Trois jeux ne
      couvraient que 2874 px : suffisant jusqu'en 2560 px, pas au-delà.
*/
.marquee-wrapper .marquee-inner {
  width: max-content;
  gap: 0;
  animation-name: alegix-marquee;
}

.marquee-wrapper .marquee-inner .marquee-item {
  margin-right: 60px;
}

/* Le sens de defilement, et pourquoi il a change le 2026-08-30.

   Le gabarit faisait glisser le bandeau vers la GAUCHE : les logos entraient
   par la droite et sortaient par la gauche, a rebours du sens de lecture. On
   percoit d'abord le mouvement, puis on remonte le regard pour lire -- deux
   gestes la ou il en faut un.

   Inverse : le bloc part de -25 % et revient a 0, les logos entrent donc par
   la gauche. Le point de depart vaut exactement un jeu, comme l'arrivee dans
   l'autre sens : le raccord reste invisible, et les quatre jeux du HTML
   couvrent toujours l'ecran. */
@keyframes alegix-marquee {
  0% {
    transform: translateX(-25%);
  }
  100% {
    transform: translateX(0);
  }
}

/* Le gabarit resserre l'écart sous 768 px ; on suit, sur la même propriété. */
@media (max-width: 767px) {
  .marquee-wrapper .marquee-inner .marquee-item {
    margin-right: 50px;
  }
}

.marquee-wrapper .marquee-inner .marquee-item .icon img.logo-client {
  height: 46px;
  width: auto;
  max-width: 170px;
  object-fit: contain;
  filter: brightness(0) invert(1);
  opacity: 0.65;
  transition: opacity 0.3s ease-in-out;
}

/* Le logo s'éclaire avec le nom, que le gabarit remplit déjà au survol. */
.marquee-wrapper .marquee-inner .marquee-item:hover .icon img.logo-client {
  opacity: 1;
}

/* RATP — 500 × 604, seul format portrait de la série. */
.marquee-wrapper .marquee-inner .marquee-item .icon img.logo-client.logo-portrait {
  height: 66px;
}

/* Groupama G2S — 500 × 392, presque carré. */
.marquee-wrapper .marquee-inner .marquee-item .icon img.logo-client.logo-carre {
  height: 58px;
}

/* Le sens de l'inclinaison, et pourquoi il a changé le 2026-09-07.

   Le gabarit incline le bandeau de +2.2deg (style.css, l. 5113). En CSS un
   angle positif tourne dans le sens des aiguilles d'une montre : le bandeau
   DESCEND de gauche à droite. Une ligne qui retombe se lit comme une courbe
   qui baisse — l'inverse de ce que des références doivent dire.

   Inversé à -2.2deg, il monte, dans le sens de lecture.

   Aucun débordement nouveau : une rotation de -t et une rotation de +t
   donnent à un rectangle la même boîte englobante. L'encombrement est
   identique, il est seulement en miroir.

   Même sélecteur que le gabarit, donc même spécificité (0,2,0) : c'est
   l'ordre de chargement qui tranche, alegix.css venant en dernier. */
.marquee-wrapper.marquee-rotate {
  transform: rotate(-2.2deg);
}


/* -------------------------------------------------------------------------
   Section « Ce que nous éditons »

   Les cartes de cette section viennent du gabarit et de nulle part ailleurs :
   .service-wrapper.service-style-1 pour le produit, .testimonial-wrapper
   .testimonial-style-2 pour le témoignage. Une première version définissait
   ici une carte maison — inutile, le gabarit en fournit plusieurs, et deux
   grammaires visuelles dans une même page se voient.

   Ne subsiste que la ligne d'en-tête du produit, seul écart assumé : le
   gabarit empile l'icône au-dessus du titre, et ce vide vertical n'avait pas
   lieu d'être pour un logo de produit accompagné de son nom.
   ------------------------------------------------------------------------- */
.produit-entete {
  display: flex;
  align-items: center;
  gap: 18px;
  margin-bottom: 24px;
}

.produit-entete .produit-logo {
  max-width: 64px;
  height: auto;
}

/* .service-title porte déjà margin-bottom: 20px, que la ligne flex rend
   inutile et qui décalerait le logo vers le haut. */
.produit-entete .service-title {
  margin-bottom: 0;
}


/* -------------------------------------------------------------------------
   Section « Comment ça marche » — trait de la dernière étape

   Le gabarit dessine un trait pointillé le long des étapes, de 90 px sous le
   haut de chacune jusqu'à 60 px sous son pied — soit précisément l'écart qui
   la sépare de la suivante. C'est une colonne vertébrale : elle relie, elle
   ne souligne pas.

   D'où sa suppression sur la dernière étape dans le gabarit (content: normal),
   puisqu'elle n'aurait rien à rejoindre. Mais la séquence s'en trouve
   interrompue plutôt que close : l'œil attend une fin, il trouve un manque.

   On le rétablit donc, arrêté au pied de l'étape au lieu de dépasser dans le
   vide. Même trait, même graphie ; seule sa longueur change.
   ------------------------------------------------------------------------- */
.steps-wrapper .steps-item:last-child .step-arrow:before {
  content: "";
  bottom: 0;
}


/* -------------------------------------------------------------------------
   Bandeau de chaîne des expertises

   Les cinq expertises forment une chaîne de production — Consulting, Data,
   Tech, Labs, Soft — et ce bandeau la donne à lire en une ligne, haut de page.
   Il s'appuie entièrement sur .case-info-section du gabarit, dont la liste est
   déjà en cinq colonnes avec un filet en haut et en bas, et qui retombe seule
   à 3, 2 puis 1 colonne dans ses media queries. Rien à redéclarer là-dessus.

   Ne restent que quatre choses que le gabarit ne pouvait pas prévoir.

   1. La hauteur. .case-info-section est dessinée pour une fiche de projet :
      50 px de respiration en haut et en bas, un libellé en 22 px Russo One,
      la valeur en dessous. Deux lignes par cellule, près de 160 px de bande —
      pour dire cinq mots. Ici la cellule passe en ligne flex et le libellé
      descend à 15 px : un maillon tient sur une ligne, la bande sur une
      hauteur de ligne. C'est un repère, pas une section.

      La bascule du gabarit à 3, 2 puis 1 colonne reste inchangée : sur ces
      largeurs la bande occupe deux ou trois lignes, ce qui est le
      comportement voulu.

   2. La cellule est cliquable en entier, via .stretched-link de Bootstrap —
      déjà employé pour les cinq divisions de l'accueil. Il se positionne sur
      l'ancêtre positionné le plus proche, d'où le position: relative.

   3. Le survol éclaircit le libellé, puisque le lien n'a pas de texte visible
      sur lequel le gabarit pourrait agir.

   4. Le maillon courant se distingue par le dégradé de texte du gabarit —
      exactement celui de .step-number, repris à l'identique plutôt que
      réinventé : c'est déjà, sur l'accueil, la façon dont le site marque un
      numéro d'étape.

   Rien ne sépare le nom du maillon de son verbe : le nom est en Russo One, le
   verbe dans le corps de texte. Le changement de police fait la séparation —
   c'est le contraste typographique du gabarit lui-même, pas un signe ajouté.
   ------------------------------------------------------------------------- */
.case-info-section.chaine-expertises {
  padding: 18px 0;
}

/* La grille du gabarit est figee a cinq colonnes, et `1fr` vaut en realite
   `minmax(auto, 1fr)` : une colonne ne descend jamais sous la largeur de son
   contenu. Nos h6 portant `white-space: nowrap`, le plancher etait celui du
   plus long maillon -- et a 768 px la somme depassait la largeur disponible.
   « 05 Soft » sortait du cadre, sans barre de defilement pour le rattraper :
   la chaine se presentait amputee d'un maillon, exactement la ou elle porte
   le positionnement. Constate le 2026-08-30.

   auto-fit + minmax laisse le navigateur choisir le nombre de colonnes : cinq
   quand la place existe, quatre a 768 px avec le cinquieme qui passe a la
   ligne, deux sur mobile. Aucune largeur ne peut plus tronquer un maillon. */
.case-info-section.chaine-expertises .case-info ul {
  grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
  gap: 10px 24px;
  align-items: baseline;
}


.case-info-section.chaine-expertises .case-info ul li {
  position: relative;
  display: flex;
  align-items: baseline;
  gap: 10px;
  font-size: 15px;
  line-height: 24px;
}

.case-info-section.chaine-expertises .case-info ul li h6 {
  margin-bottom: 0;
  font-size: 15px;
  line-height: 24px;
  white-space: nowrap;
}

.case-info-section.chaine-expertises .case-info ul li h6 {
  transition: color 0.3s ease-in-out;
}

.case-info-section.chaine-expertises .case-info ul li:hover h6 {
  color: var(--color-title);
}

.case-info-section.chaine-expertises .case-info ul li.maillon-courant h6 {
  background: var(--color-accent);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
}


/* -------------------------------------------------------------------------
   Le bloc de preuve — quatre temps au lieu d'un mur

   Constat du CEO le 2026-09-04 : la section « La preuve » était un mur de
   texte. Six paragraphes justifiés d'un seul tenant sur Labs, trois sur les
   pages d'expertise, et une colonne de compteurs à droite dont rien ne disait
   ce qu'elle montrait.

   Le récit est donc découpé en quatre temps nommés — le contexte, ce que nous
   avons fait, la stack, ce qui fait la différence — et la colonne de droite
   en reçoit un cinquième : « Ce que ça a donné ». Les compteurs cessent d'être
   un ornement flottant pour devenir la réponse à une question posée.

   La règle qui rend les deux colonnes complémentaires plutôt que redondantes :
   AUCUN CHIFFRE DANS LES PUCES s'il figure déjà dans un compteur. Les puces
   disent l'action, les compteurs disent le résultat. Auparavant « vingt-trois
   personnes » était écrit dans le texte ET affiché en compteur.

   « La stack » est le seul temps variable : il n'apparaît que là où il y a une
   plateforme — Labs, Tech, Soft. Sur Consulting et Data la preuve est une
   mission, et le bloc saute.

   Le texte justifié a disparu de ces colonnes. Dans une laisse de sept unités
   avec des mots français longs, `text-align: justify` creuse des rivières
   blanches verticales : c'est un defaut de lisibilite, pas un parti pris.
   ------------------------------------------------------------------------- */
.preuve-intertitre {
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--color-accent);
  margin: 32px 0 14px;
}

/* Le premier intertitre d'une colonne ne pousse pas : il ouvre. */
.preuve-recit > :first-child,
.preuve-resultats > :first-child {
  margin-top: 0;
}

.preuve-recit p {
  margin-bottom: 16px;
}

.preuve-recit .list-wrapper .list {
  gap: 12px;
}

/* La stack en pastilles plutot qu'en phrase. Une liste de technologies se
   balaie du regard ; ecrite en prose, elle se lit mot a mot pour rien. */
.preuve-stack {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  padding: 0;
  margin: 0;
  list-style: none;
}

.preuve-stack li {
  padding: 6px 16px;
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 999px;
  font-size: 14px;
  color: var(--color-gray);
}


/* -------------------------------------------------------------------------
   Les compteurs — cales en haut, et enfin visibles

   Ce bloc REMPLACE une premiere approche du 2026-08-30 qu'il faut expliquer,
   parce qu'elle etait defendable et qu'elle a quand meme echoue.

   Elle donnait `flex: 1 1 0` aux trois compteurs pour qu'ils se partagent la
   hauteur de la colonne de recit, quelle qu'elle soit. L'intention : ne pas
   caler des paddings a la main, qui tombent juste face a un texte donne et se
   defont a la page suivante. Le raisonnement tenait ; le resultat, non. Sur
   Labs, six paragraphes font environ 700px de haut, donc PRES DE 230px PAR
   COMPTEUR — trois nombres perdus dans du vide, relies par des filets tires.
   Le CEO l'a releve le 2026-09-04 : « trop etires, visuellement ca ne va pas
   du tout ».

   Les compteurs reprennent donc leur hauteur naturelle et se calent en haut de
   la colonne. Le desequilibre de hauteur entre les deux colonnes redevient ce
   qu'il etait : normal, et invisible, parce qu'une colonne courte a cote d'une
   colonne longue ne choque pas — c'est une colonne ETIREE qui choque.

   VISIBILITE. Le nombre et son libelle etaient tous deux en Russo One blanc,
   48 et 22px : ils se disputaient l'attention. Le nombre passe a 64px et prend
   le degrade d'or de la charte — le meme que les surtitres, 7,30:1 sur le fond
   de page, donc AA. Le libelle passe a la fonte de texte, en gris : il redevient
   une legende, ce qu'il est.
   ------------------------------------------------------------------------- */
.preuve-resultats {
  align-self: flex-start;
}

.preuve-compteurs {
  display: flex;
  flex-direction: column;
  gap: 26px;
}

.preuve-compteurs .counter.counter-style-1 {
  flex: 0 0 auto;
  padding: 0 0 18px;
}

/* Le libelle perd la largeur fixe de 190px du gabarit et prend la place
   disponible : il se recompose selon la colonne au lieu de deborder. */
.preuve-compteurs .counter.counter-style-1 .counter-info {
  width: auto;
  flex: 1 1 auto;
  min-width: 0;
}

.preuve-compteurs .counter {
  gap: 20px;
  align-items: center;
}

.preuve-compteurs .counter-number {
  font-size: 64px;
  line-height: 1;
  background: linear-gradient(170deg, var(--color-accent) 0%, var(--color-accent-light) 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
}

.preuve-compteurs .counter-title {
  font-family: inherit;
  font-size: 16px;
  line-height: 24px;
  font-weight: 500;
  color: var(--color-gray);
}


/* -------------------------------------------------------------------------
   Portrait du fondateur — page « Qui nous sommes »

   Le gabarit ne style pas l'image de .team-style-1 : elle occupe la largeur de
   sa colonne à son rapport naturel. Ici, 600 × 843 dans une colonne de 450 px
   font 630 px de haut, quand la colonne de texte d'à côté en fait 560. Le
   portrait dépasse donc du texte qu'il accompagne — le même défaut que les
   compteurs de preuve, et il se règle de la même façon.

   La colonne devient flex, l'image prend ce qui reste une fois le bloc de
   liens sociaux posé, et object-fit: cover la recadre sans la déformer. Le
   cadrage part du haut : le visage occupe la moitié supérieure de la photo,
   c'est lui qu'il faut préserver quand la hauteur se réduit.

   min-height retient le recadrage avant qu'il ne devienne un bandeau : en
   dessous de 260 px, on ne verrait plus un portrait.

   Sous 992 px les colonnes s'empilent, la hauteur redevient libre et il n'y a
   plus rien à répartir : l'image reprend son rapport naturel.
   ------------------------------------------------------------------------- */
.portrait-fondateur {
  display: flex;
  flex-direction: column;
  height: 100%;
}

.portrait-fondateur .portrait-image {
  flex: 1 1 0;
  min-height: 260px;
  overflow: hidden;
  border-radius: 10px;
}

.portrait-fondateur .portrait-image img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center top;
}

/* Sous 991 px, le portrait reprenait son ratio naturel -- 600 x 843, soit
   506 px de haut sur un ecran de 360. Ajoute a la banniere, il occupait la
   totalite du premier ecran et repoussait tout le texte de presentation sous
   la ligne de flottaison. Ce n'etait pas casse, c'etait disproportionne.

   On borne le cadre plutot que l'image : un ratio 4/5 recadre par le haut,
   plafonne a 400 px. Le point focal descend a 20 % pour que le recadrage
   garde le visage centre et non le front. */
@media (max-width: 991px) {
  .portrait-fondateur .portrait-image {
    flex: 0 0 auto;
    aspect-ratio: 4 / 5;
    max-height: 400px;
  }
  .portrait-fondateur .portrait-image img {
    height: 100%;
    object-position: center 20%;
  }
}


/* ---------------------------------------------------------------------------
   Pied de page : la page en cours dans « Acces Rapides »
   ---------------------------------------------------------------------------
   Le pied liste les huit pages sans jamais dire ou l'on se trouve. On reprend
   la convention de l'en-tete, ou la page active est en or (--color-accent).

   Le repere est porte par aria-current="page" plutot que par une classe : le
   meme attribut sert au lecteur d'ecran et au style, et il n'y a rien a tenir
   a jour en double.

   La graisse accompagne la couleur. La couleur seule contreviendrait au
   critere WCAG 1.4.1 - elle ne doit jamais etre le seul moyen de transmettre
   une information -- et l'or du pied est deja la couleur du survol : sans la
   graisse, un lien survole et le lien de la page courante seraient identiques. */
.site-footer.footer-dark .widget-menu li a[aria-current="page"] {
  color: var(--color-accent);
  font-weight: 600;
}


/* ---------------------------------------------------------------------------
   Banniere de page : le visuel quitte les pages pour venir ici
   ---------------------------------------------------------------------------
   Le meme style="background-image: url(...)" etait recopie dans neuf pages,
   vers le meme fichier. Changer de visuel demandait donc neuf edits, et rien
   ne garantissait qu'on les fasse toutes.

   Le poids ensuite. Le JPEG fait 241 Ko, le plus gros fichier du site, servi
   sur chaque page a en-tete. Le WebP produit par deploy/bannieres-webp.py en
   fait 71 : 70 % de moins, sous un aplat navy a 85 % qui masque de toute
   facon les artefacts.

   Les deux background-image ne sont pas une erreur. Le premier est le repli :
   un navigateur qui ne connait pas image-set() ignore la seconde declaration
   et garde le JPEG. Cela nous rend aussi independants du type MIME servi pour
   le WebP par OVH, encore a verifier (point C06 de la trame de validation).

   Le jour ou chaque page aura son propre visuel, on ajoutera sous ce bloc une
   regle par page, et l'on ne rouvrira aucune page :

     body.page-tech .inner-header { background-image: ... }

   la classe page-<nom> etant posee sur le body par __header.php. */

/* Le visuel par defaut est celui de « Qui nous sommes », decide le 2026-09-01.

   Il ne sert plus que DEUX pages : mentions-legales et error-404, les seules
   sans regle propre. Toutes les autres ont la leur, plus bas.

   Ce qu'il remplace comptait plus que ce qu'il apporte. page-header-01.jpg
   etait la derniere photographie du gabarit encore servie, et sa licence n'a
   jamais ete transmise -- voir l'encadre de DOCS/01_SITE/bannieres.md. Le
   fichier est supprime du depot par le meme commit. */
.inner-header {
  background-image: url(../images/banner/inner-header/team.jpg);
  background-image: image-set(
    url(../images/banner/inner-header/team.webp) type("image/webp"),
    url(../images/banner/inner-header/team.jpg) type("image/jpeg"));
}


/* ---------------------------------------------------------------------------
   Pied de page : la newsletter, et l'alignement des « Acces Rapides »
   ---------------------------------------------------------------------------
   L'accroche etait a la taille courante du pied, la mention RGPD a 0,78 rem,
   posee en style="" : deux paragraphes du meme bloc, deux tailles, sans que
   rien ne le justifie. La taille descend donc ici, une fois, pour les deux.
   Ce qui les distingue est l'opacite : l'accroche invite, la mention informe.

   Cote « Acces Rapides », « Qui nous sommes » etait le seul libelle a passer
   sur deux lignes -- les colonnes en calc(25% - 8px) lui laissent trop peu de
   place -- et cassait la regularite de la grille. En laissant les colonnes se
   dimensionner sur leur contenu, chaque entree tient sur une ligne. Les paliers
   du gabarit (deux colonnes sous 991 px, une seule sous 575) sont conserves
   tels quels : ils s'appliquent apres, et n'ont pas ce probleme. */
.site-footer .widget-newsletter p {
  font-size: 0.78rem;
}

.site-footer .widget-newsletter p.mention-rgpd {
  opacity: 0.7;
}

@media (min-width: 992px) {
  .site-footer .widget-menu ul.list-col-4 {
    display: grid;
    grid-template-columns: repeat(4, auto);
    justify-content: start;
    column-gap: 40px;
  }
  .site-footer .widget-menu ul.list-col-4 li {
    width: auto;
    white-space: nowrap;
  }
}

/* Premier visuel propre a une page, pose le 2026-08-31 : la cartographie
   annotee de /tech. La mecanique annoncee plus haut sert enfin comme prevu,
   une regle et aucune page rouverte. Titre mesure a 4,92:1 sous le voile.
   Voir DOCS/01_SITE/bannieres.md et deploy/banniere-preview.py. */
body.page-tech .inner-header {
  background-image: url(../images/banner/inner-header/tech.jpg);
  background-image: image-set(
    url(../images/banner/inner-header/tech.webp) type("image/webp"),
    url(../images/banner/inner-header/tech.jpg) type("image/jpeg"));
}

/* Bannière de /consulting, posée le 2026-08-31. Contraste du titre : 5.38:1. */
body.page-consulting .inner-header {
  background-image: url(../images/banner/inner-header/consulting.jpg);
  background-image: image-set(
    url(../images/banner/inner-header/consulting.webp) type("image/webp"),
    url(../images/banner/inner-header/consulting.jpg) type("image/jpeg"));
}

/* Bannière de /data, posée le 2026-08-31. Contraste du titre : 5.23:1. */
body.page-data .inner-header {
  background-image: url(../images/banner/inner-header/data.jpg);
  background-image: image-set(
    url(../images/banner/inner-header/data.webp) type("image/webp"),
    url(../images/banner/inner-header/data.jpg) type("image/jpeg"));
}

/* Bannière de /labs, posée le 2026-08-31. Contraste du titre : 5.53:1. */
body.page-labs .inner-header {
  background-image: url(../images/banner/inner-header/labs.jpg);
  background-image: image-set(
    url(../images/banner/inner-header/labs.webp) type("image/webp"),
    url(../images/banner/inner-header/labs.jpg) type("image/jpeg"));
}

/* Bannière de /soft, posée le 2026-08-31. Contraste du titre : 5.11:1. */
body.page-soft .inner-header {
  background-image: url(../images/banner/inner-header/soft.jpg);
  background-image: image-set(
    url(../images/banner/inner-header/soft.webp) type("image/webp"),
    url(../images/banner/inner-header/soft.jpg) type("image/jpeg"));
}

/* Bannière de /contact-us, posée le 2026-08-31. Contraste du titre : 4.91:1. */
body.page-contact-us .inner-header {
  background-image: url(../images/banner/inner-header/contact-us.jpg);
  background-image: image-set(
    url(../images/banner/inner-header/contact-us.webp) type("image/webp"),
    url(../images/banner/inner-header/contact-us.jpg) type("image/jpeg"));
}

/* Bannière de /team, posée le 2026-09-01. Contraste du titre : 4.70:1. */
body.page-team .inner-header {
  background-image: url(../images/banner/inner-header/team.jpg);
  background-image: image-set(
    url(../images/banner/inner-header/team.webp) type("image/webp"),
    url(../images/banner/inner-header/team.jpg) type("image/jpeg"));
}

/* Coins arrondis des vignettes d'etapes de l'accueil.

   Ils vivaient dans le canal ALPHA des PNG du gabarit : rayon de 17 px, mesure
   sur steps/01.png. Les sortir dans le CSS libere le format -- une photographie
   aux coins ronds n'a plus besoin d'etre un PNG, elle peut etre un JPEG ou un
   WebP, environ dix fois plus leger. C'est la seule des trois images de la
   section dont le poids se voyait.

   La regle est posee AVANT le remplacement des visuels : appliquee aux PNG
   actuels elle arrondit ce qui l'est deja, sans effet visible. Le jour ou les
   nouvelles images arrivent en JPEG, rien d'autre a faire.

   Verifie le 2026-09-01 : steps/01 a 03 sont les seules images arrondies
   reellement servies. Les autres PNG a coins ronds du gabarit -- about,
   case-studies, banner-02/01 -- sont orphelins. */
.steps-wrapper .steps-item .step-image img {
  border-radius: 16px;
}

/*---------------------------------------------------------------------------
  Le menu du haut : fixe, et par-dessus la banniere -- 2026-09-01
---------------------------------------------------------------------------*/
/* Le gabarit fait apparaitre et disparaitre le menu selon le SENS du
   defilement : `custom.js` pose `.is-sticky` passe la hauteur du header, ce qui
   le bascule en `position: fixed; top: -110px`, puis n'ajoute `.sticky-show`
   (top: 0) qu'au moment ou l'on remonte. Deux defauts signales ensemble le
   2026-09-01 : le menu n'est jamais la quand on descend, et il coute a la
   banniere sa propre hauteur -- 86 px sur dix, a 1920.

   Le menu devient donc FIXE et TRANSPARENT en haut de page : il flotte
   au-dessus de la banniere au lieu de la pousser vers le bas. La banniere
   remonte jusqu'au bord haut de la fenetre et recupere ces 86 px d'image ;
   son contenu, lui, est repousse sous le menu par un `padding-top`, de sorte
   que rien n'est cache. Des qu'on defile, `.is-sticky` lui rend son fond navy
   et son filet : c'est le seul comportement du gabarit qu'on garde.

   La lisibilite du menu au-dessus de la banniere est acquise par construction :
   c'est le meme voile a 85 % qui garantit 4,98:1 au titre, meme sur un pixel
   source blanc pur (voir DOCS/01_SITE/bannieres.md).

   `custom.js` n'est pas touche -- c'est du vendor. On neutralise ses trois
   etats en CSS. */

/* LA HAUTEUR DU MENU EST UNE CONSTANTE, et il a fallu la rendre telle.

   Mesuree le 2026-09-01, elle valait 86 px a 1920, 72 a 1200 et 67 sous 992 :
   elle SUIVAIT la taille du logo, dont la largeur etait exprimee en `10em` puis
   bornee a `50%` de son conteneur -- donc dependante de la largeur de fenetre,
   et par un chemin que rien ne documentait. Une compensation calee sur une
   valeur qui derive au moindre changement de logo n'aurait tenu que le temps
   du prochain.

   On inverse donc la dependance : la variable fixe la hauteur, et le logo la
   suit. Les trois valeurs reprennent celles rendues aujourd'hui a chaque
   rupture -- l'aspect ne change pas, il devient seulement previsible. */
:root { --alegix-menu: 86px; }
@media (max-width: 1199px) { :root { --alegix-menu: 72px; } }
@media (max-width: 991px)  { :root { --alegix-menu: 68px; } }

.header-default .site-logo .navbar-brand {
  width: auto;
  max-width: none;
  padding: 5px 0;
}

.header-default .site-logo .navbar-brand img {
  height: calc(var(--alegix-menu) - 10px); /* les 2 x 5px de padding ci-dessus */
  width: auto;
}

.header-default.header-sticky,
.header-default.header-sticky.is-sticky,
.header-default.header-sticky.is-sticky.sticky-show {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  width: auto;
  z-index: 1030; /* au-dessus du contenu, sous le backdrop d'offcanvas (1040) */
  animation: none; /* stickySlideDown n'a plus d'objet : il ne descend plus */
}

/* En haut de page, le menu laisse voir la banniere ; des le premier
   defilement, `custom.js` pose `.is-sticky` et le fond revient. */
.header-default.header-sticky {
  background-color: transparent;
  border-bottom-color: transparent;
  transition: background-color .3s ease-in-out, border-color .3s ease-in-out;
}

.header-default.header-sticky.is-sticky {
  background-color: var(--color-black);
  border-bottom-color: rgba(255, 255, 255, 0.1);
}

/* LA BANNIERE REPREND LA HAUTEUR DU MENU.

   Le menu ne pousse plus rien : la banniere commence au bord haut de la
   fenetre. Son `min-height` grandit d'autant, et son `padding-top` remet son
   contenu sous le menu -- `box-sizing: border-box` etant pose globalement par
   le gabarit, le padding est bien compris dans le min-height. Le titre reste
   donc centre dans la partie VISIBLE de la bande, et la bande, elle, montre
   86 px d'image de plus. */
.inner-header {
  min-height: calc(250px + var(--alegix-menu));
  padding-top: calc(30px + var(--alegix-menu));
}

@media (max-width: 991px) {
  .inner-header { min-height: calc(200px + var(--alegix-menu)); }
}

@media (max-width: 767px) {
  .inner-header { min-height: calc(150px + var(--alegix-menu)); }
}

/* L'accueil n'a pas d'`.inner-header` : sa banniere est `.main-banner-2`, dont
   le gabarit regle le retrait haut a trois valeurs selon la largeur. On leur
   ajoute la meme constante. */
.main-banner.main-banner-2 { padding-top: calc(3em + var(--alegix-menu)); }

@media (max-width: 1199px) {
  .main-banner.main-banner-2 { padding-top: calc(200px + var(--alegix-menu)); }
}

@media (max-width: 991px) {
  .main-banner.main-banner-2 { padding-top: calc(140px + var(--alegix-menu)); }
}

/*---------------------------------------------------------------------------
  Le slogan de l'accueil -- 2026-09-01
---------------------------------------------------------------------------*/
/* Un seul bloc, deux lignes au plus, et jamais par-dessus l'image.

   LA LARGEUR. L'image est posee en absolu a droite de `.banner-inner`, qui fait
   toute la largeur de la fenetre ; le texte, lui, vit dans `.container`. En
   bornant l'image a 50vw, la frontiere tombe pile au milieu de la fenetre, et
   la place restante pour le texte vaut exactement la moitie du conteneur --
   d'ou `calc(50% - 30px)`, valable a toutes les largeurs sans cas particulier.
   Sous 768 px le gabarit masque deja l'image : la borne ne s'applique plus.

   LA TAILLE. Elle suit la fenetre plutot qu'un palier par media query. Le
   facteur 3,6vw est celui qui fait tenir les 39 caracteres sur deux lignes dans
   `calc(50% - 30px)` a chaque rupture de Bootstrap -- verifie a 768, 992, 1200
   et 1400. Plafond a 50px : au-dela, la phrase tiendrait toujours mais ecraserait
   le reste de la banniere.

   `text-wrap: balance` repartit les deux lignes a peu pres egales sans qu'on ait
   a couper la phrase a la main. La ou il n'est pas connu, la coupure est
   simplement naturelle -- ce qui reste correct, c'etait le but. */
.main-banner.main-banner-2 .banner-inner .banner-bg {
  max-width: 50vw;
  height: auto;
}

.main-banner.main-banner-2 .banner-inner .banner-conent .banner-slogan {
  max-width: calc(50% - 30px);
  font-size: clamp(26px, 3.6vw, 50px);
  line-height: 1.14;
  text-wrap: balance;
}

/* Le paragraphe suit la meme frontiere : a 1400 px, ses 450 px de large
   depassaient eux aussi sur l'image. */
.main-banner.main-banner-2 .banner-inner .banner-conent .conent .small-text {
  max-width: min(450px, calc(50% - 30px));
}

@media (max-width: 767px) {
  .main-banner.main-banner-2 .banner-inner .banner-conent .banner-slogan,
  .main-banner.main-banner-2 .banner-inner .banner-conent .conent .small-text {
    max-width: 100%;
  }
}

/*---------------------------------------------------------------------------
  Le bandeau d'avis de /labs -- 2026-09-01
---------------------------------------------------------------------------*/
/* Trois cartes compactes, portrait rond + citation courte. Le gabarit fournit
   .testimonial-wrapper.testimonial-style-2 pour UNE citation en grand (22px,
   italique) : plaque trois fois cote a cote, elle deborde. D'ou ce jeu de
   classes propre, dimensionne pour la densite de trois cartes par ligne.

   Le portrait ne depasse jamais 80px d'affichage : la plus grande source
   (Ester N., 267x258) commence a pixelliser au-dela sur un ecran haute
   densite. Voir DOCS/01_SITE/crmflexs-integration.md et
   deploy/avis-portraits.py, qui livre les fichiers a 160px (2x) pour cette
   taille d'affichage. */
.avis-carte {
  height: 100%;
  padding: 30px;
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 10px;
  text-align: center;
}

.avis-portrait {
  width: 80px;
  height: 80px;
  border-radius: 50%;
  object-fit: cover;
  margin-bottom: 20px;
}

.avis-texte {
  font-size: 16px;
  line-height: 26px;
  font-style: italic;
  color: var(--color-gray);
  margin-bottom: 18px;
}

.avis-auteur {
  font-size: 15px;
  font-weight: 600;
  color: var(--color-title);
  margin-bottom: 0;
}

.avis-auteur span {
  display: block;
  font-weight: 400;
  font-size: 14px;
  color: var(--color-gray);
  margin-top: 2px;
}

/*---------------------------------------------------------------------------
  Titres de section : casse, taille, respiration -- 2026-09-04
---------------------------------------------------------------------------*/
/* Cinq constats du CEO, cinq causes distinctes, toutes ici.

   1. LA CAPITALISATION. Le gabarit pose `text-transform: capitalize` sur
      `.section-title .title` (style.css, l. 347). Il met donc une majuscule a
      CHAQUE mot : « Du Mono-Client Au SaaS Multi-Tenant A IA Generative ». En
      anglais c'est une convention de titre ; en francais, non -- et sur un
      titre long, l'oeil bute a chaque mot. On revient a la casse ecrite.

   2. LA TAILLE. h2 vaut 60px pour 70px d'interligne. Sur des titres de trois
      lignes, le bloc pese plus que le contenu qu'il annonce. La taille suit
      desormais la fenetre, entre 30 et 46px, avec une interligne resserree a
      1,18 -- 70/60 valait 1,17 : c'est la taille qui baisse, pas l'air entre
      les lignes.

   3. LA MARGE BASSE. 25px sous le titre, 50px sous le bloc de titre entier.
      Ramenes a 16 et 34 : le sous-titre, le titre et le texte forment un
      groupe, ils doivent se lire comme tel.

   4. LES COUPURES. `text-wrap: balance` repartit les lignes d'un titre a peu
      pres egales au lieu de laisser un mot seul en derniere ligne ;
      `text-wrap: pretty` fait le meme travail sur les paragraphes, en
      empechant l'orpheline. La ou ils ne sont pas connus, la coupure reste
      celle d'avant -- aucune regression possible.

   5. L'ALIGNEMENT DES COLONNES, traite dans les pages : voir plus bas. */

.section-title .title {
  text-transform: none;
  font-size: clamp(30px, calc(1.6vw + 16px), 46px);
  line-height: 1.18;
  margin-bottom: 16px;
  text-wrap: balance;
}

.section-title {
  margin-bottom: 34px;
}

/* Les paragraphes du bloc de titre suivent la meme descente : 20px a cote
   d'un titre de 46px etait juste, a cote d'un titre de 60px il ne l'est
   plus. */
.section-title p {
  font-size: 18px;
  text-wrap: pretty;
}

/* Les titres de banniere de page ne sont pas des titres de section -- ils ont
   leur propre echelle, deja arbitree. Ils gagnent seulement l'equilibrage des
   lignes, qui leur manquait. */
.inner-header .title,
.service-title,
.step-title {
  text-wrap: balance;
}

/* Le texte courant : jamais de mot seul sur la derniere ligne. */
p {
  text-wrap: pretty;
}

/* Et la vraie cause du desalignement des titres, trouvee apres coup : le
   gabarit ancre le contenu des cartes en BAS -- `.service-content` porte
   `margin-top: auto` dans un `.service-inner` en colonne flex (style.css,
   l. 2295). Les cartes ont toutes la meme hauteur ; leur contenu est donc
   cale par le bas, et un titre remonte des que sa liste prend une ligne de
   plus que la voisine.

   C'est exactement le « des qu'il y a plusieurs lignes » du constat. On ancre
   par le HAUT : le logo, puis le titre, puis la liste -- les cinq titres sur
   la meme ligne, quelle que soit la longueur de ce qui suit. */
.service-wrapper.service-style-2 .service-content {
  margin-top: 0;
}

/* UNE SEULE MESURE POUR TOUS LES TITRES -- 2026-09-04

   L'inventaire des vingt-et-un titres de section en donnait CINQ largeurs
   differentes selon la colonne qui les portait : col-12, col-lg-6, col-lg-8,
   col-xxl-7 col-lg-8, col-xl-9 col-lg-9. D'une page a l'autre, et parfois
   dans la meme page, un titre ne se composait donc pas sur la meme laisse.

   Les colonnes ont ete ramenees a col-lg-8 la ou c'etait sans risque ; mais
   normaliser la GRILLE ne suffit pas -- contact-us porte un col-xl-9 dont la
   colonne voisine tient des cartes, pas du texte, et le toucher casserait sa
   mise en page.

   La mesure se pose donc ici, sur le titre lui-meme, independamment de la
   colonne : 850px, la largeur utile d'un col-lg-8 dans un conteneur de
   1320px. Elle est inerte partout ou la colonne est deja plus etroite, ce qui
   la rend sans effet en dessous de lg. Un titre centre garde son centrage. */
.section-title .title {
  max-width: 850px;
}

.section-title.text-center .title,
.text-center .section-title .title {
  margin-left: auto;
  margin-right: auto;
}

/*---------------------------------------------------------------------------
  Rythme vertical : une seule echelle -- 2026-09-04
---------------------------------------------------------------------------*/
/* Deux constats du CEO, une meme cause : l'espace vertical etait compose a
   plusieurs endroits a la fois, et personne ne tenait la somme.

   MESURE DE L'EXISTANT (Chrome 1440px, les huit pages, 25 sections) :

     padding de section   30/30 sur 21 sections, 60/60 sur le bandeau de
                          references, 50/0 sur le formulaire de contact,
                          18/18 sur la chaine d'expertises ;
     puis, PAR-DESSUS     .container-fluid.pt-5 (+48) sur ce meme bandeau,
                          .mt-5.pt-lg-4 (+72) sur la grille des cinq metiers ;
     bloc de titre        0, 24, 34 ou 58px avant le contenu qui suit, selon
                          les utilitaires poses sur le balisage.

   Le 58 est le cas d'ecole : `.section-title` (34) PLUS `.row.mt-4` (24) sur
   la meme jointure. Le 24 est le meme empilement avec `mb-lg-0` a la place du
   34. Deux ecritures pour une seule intention, et deux resultats differents.

   LE PRINCIPE : une jointure, une regle, et elle vit ici. Les utilitaires de
   marge ont ete retires du balisage -- `mb-lg-0`, `mb-0`, `mb-4` sur les
   blocs de titre, `mt-4` sur les rangs qui les suivent, `mt-5 pt-lg-4` et
   `pt-5` sur les deux conteneurs de l'accueil. Aucun espace vertical de
   structure ne se decide plus dans le HTML.

   30px reste la valeur de section : c'est celle de 21 sections sur 25, donc
   la mise en page validee. Ce qui change, ce sont les quatre exceptions, qui
   la rejoignent. */

:root {
  --alegix-section: 30px;   /* respiration haute et basse d'une section     */
  --alegix-bloc: 34px;      /* du bloc de titre au contenu qu'il annonce    */
  --alegix-ligne: 16px;     /* entre deux lignes du bloc de titre           */
}

@media (max-width: 767px) {
  :root { --alegix-section: 24px; }
}

/* `!important` n'est pas un choix : style.css (l. 801) en pose deja un sur
   `.space-ptb`, ce qui neutralise du meme coup ses propres paliers
   responsives -- ils descendent de 120 a 50px, donc AU-DESSUS de 30, et
   grossiraient l'espace sur petit ecran s'ils reprenaient la main. On reprend
   la declaration ici, en dernier, plutot que d'aller retirer le drapeau la-bas
   et d'avoir a desamorcer les cinq paliers un par un. */
.space-ptb,
.space-small-ptb,
.space-medium-ptb {
  padding-top: var(--alegix-section) !important;
  padding-bottom: var(--alegix-section) !important;
}

.space-pt,
.space-small-pt,
.space-medium-pt {
  padding-top: var(--alegix-section) !important;
  padding-bottom: 0 !important;
}

.space-pb,
.space-small-pb,
.space-medium-pb {
  padding-top: 0 !important;
  padding-bottom: var(--alegix-section) !important;
}

/* La chaine d'expertises garde ses 18px : c'est un filet de navigation, pas
   une section de contenu. L'aligner sur 30 l'epaissirait sans rien apporter. */

/* --- Le bloc de titre ---------------------------------------------------
   Trois jointures, une seule valeur chacune, plus aucune exception :
     surtitre -> titre   16px
     titre -> paragraphe 16px  (deja pose plus haut avec la taille du titre)
     bloc -> contenu     34px  */
.section-title .sub-title {
  margin-bottom: var(--alegix-ligne);
}

.section-title .title {
  margin-bottom: var(--alegix-ligne);
}

.section-title p + p {
  margin-top: var(--alegix-ligne);
}

.section-title {
  margin-bottom: var(--alegix-bloc);
}

/* Ce qui remplace les `mb-0` du balisage : la derniere ligne du bloc ne
   traine jamais sa marge, quelle qu'elle soit -- titre seul, titre suivi d'un
   paragraphe, ou paragraphe suivi d'un second. L'espace sous le bloc est
   alors toujours celui du bloc, et lui seul. */
.section-title > :last-child {
  margin-bottom: 0;
}

/*---------------------------------------------------------------------------
  Cartes de service : les textes demarrent au meme niveau -- 2026-09-04
---------------------------------------------------------------------------*/
/* Constat du CEO sur /team : les cartes dont le titre fait deux lignes font
   demarrer leur texte plus bas que les autres.

   MESURE (Chrome, trois largeurs, les six pages qui portent ces cartes) --
   le desalignement n'est pas propre a /team, et il s'aggrave a mesure que la
   colonne se resserre :

     /team      1440px  2 / 2 / 1 / 1 lignes de titre
     /labs      1440px  3 / 3 / 2 / 2
     /labs      1200px  4 / 5 / 3 / 2   <- trois lignes d'ecart dans un rang
     /soft      1200px  3 / 4 / 2 / 2

   Une `min-height` sur le titre ne peut donc pas repondre : calee sur le pire
   cas de /labs a 1200px, elle creuserait 124px de vide sous « Ce qu'elle ne
   remplace pas ». La hauteur a egaliser n'est pas une constante, c'est le
   maximum du RANG -- ce que seule la grille sait.

   D'ou `subgrid` : la carte cesse d'etre une boite opaque et rend ses lignes
   a la grille qui la contient. Chaque rang dimensionne alors quatre pistes --
   icone, titre, texte, liens -- sur le contenu le plus haut du rang, et les
   quatre cartes s'y accrochent. Aucune valeur en dur, aucun recalcul quand un
   libelle change.

   Sous `@supports` : la ou subgrid n'est pas connu, la mise en page reste
   exactement celle d'aujourd'hui. Aucune regression possible.

   Ne s'applique QU'AUX cartes des grilles : la carte produit de l'accueil
   porte la meme classe mais vit dans un `.row` Bootstrap, hors grille, et son
   titre est dans `.produit-entete` a cote du logo -- elle n'est pas
   concernee, et le selecteur d'enfant direct l'exclut. */
@supports (grid-template-rows: subgrid) {
  .grid-wrapper > .service-wrapper.service-style-1 {
    grid-row: span 4;
    display: grid;
    grid-template-rows: subgrid;
  }

  .grid-wrapper > .service-wrapper.service-style-1 > .service-inner {
    grid-row: span 4;
    display: grid;
    grid-template-rows: subgrid;
  }

  /* Le fond decoratif est hors flux : il ne prend pas de piste. */
  .grid-wrapper > .service-wrapper.service-style-1 > .service-inner > .service-content {
    grid-row: span 3;
    display: grid;
    grid-template-rows: subgrid;
  }
}

/*---------------------------------------------------------------------------
  Titres de cartes : la taille suit la COLONNE, pas la fenetre -- 2026-09-04
---------------------------------------------------------------------------*/
/* Suite du point precedent : aligner les textes revele le vrai defaut, un
   titre de 26px fixes dans une colonne qui, elle, ne l'est pas.

   LE NOMBRE DE COLONNES CHANGE AVEC LA FENETRE -- 4 au-dessus de 1200, 3 au
   dessus de 992, 2 au-dessus de 576, 1 en dessous. Une fenetre qui retrecit
   ne donne donc PAS une colonne plus etroite : elle donne une colonne plus
   LARGE des qu'on passe un palier. Largeur mesuree d'une carte :

     1440px  4 colonnes   350px      le titre y respire
     1200px  4 colonnes   279px      le pire cas, jusqu'a 5 lignes
      992px  3 colonnes   312px      a nouveau confortable
      768px  2 colonnes   372px      la plus large a plusieurs colonnes
      375px  1 colonne    351px      pleine largeur

   Un `clamp()` sur `vw`, comme celui des titres de section, se tromperait
   donc de variable : il retrecirait le titre a 768px, la ou la colonne est la
   plus large. La reference doit etre la carte.

   POURQUOI LE CONTENEUR EST LA GRILLE ET NON LA CARTE. Premier essai :
   `container-type: inline-size` sur la carte elle-meme. Il a casse
   l'alignement du bloc precedent -- la containment de mise en page qu'il
   implique retire a la carte sa qualite de sous-grille, et les textes se sont
   remis a demarrer a des hauteurs differentes (mesure : 184 / 198 / 211 /
   223 dans un meme rang). Le conteneur remonte donc sur la grille, qui n'est
   pas une sous-grille et n'a rien a perdre.

   La largeur d'une carte s'en deduit : la grille divisee par ses colonnes --
   ces grilles portent `grid-gap-0`, il n'y a pas de gouttiere a retrancher.
   Moins le padding interne de la carte, on obtient la largeur utile du titre.
   La regle se lit alors : 7 % de cette largeur, plus 7px, entre 19 et 26.
   Meme construction qu'en haut de ce fichier -- une pente, une origine, deux
   bornes -- avec la carte pour reference au lieu de la fenetre.

   Verification : 26px a 1440, 21 a 1200, 23 a 992, 26 a 768, et 26 en pleine
   largeur sous 576.

   Sous `@supports`, comme le subgrid : sans les requetes de conteneur, le
   titre garde ses 26px d'aujourd'hui. */
@supports (container-type: inline-size) {
  .services.grid-wrapper.grid-xl-4 {
    container-type: inline-size;
    --colonnes: 4;
  }

  @media (max-width: 1199px) { .services.grid-wrapper.grid-xl-4 { --colonnes: 3; } }
  @media (max-width: 991px)  { .services.grid-wrapper.grid-xl-4 { --colonnes: 2; } }
  @media (max-width: 575px)  { .services.grid-wrapper.grid-xl-4 { --colonnes: 1; } }

  /* `--carte-padding` est declaree plus bas, avec le padding qu'elle decrit --
     une seule ecriture pour une seule valeur. La declarer ici aussi menait a
     l'inverse du but recherche : une media query n'ajoute pas de specificite,
     donc la valeur de bureau ecrite plus bas SANS media query l'emportait sur
     le palier mobile ecrit ici. */

  .grid-wrapper > .service-wrapper.service-style-1 .service-content > .service-title {
    font-size: clamp(19px, calc((100cqi / var(--colonnes, 4) - var(--carte-padding, 80px)) * 0.07 + 7px), 26px);
    line-height: 1.2;
  }
}

/*---------------------------------------------------------------------------
  Cartes de service : l'air passe dehors, pas dedans -- 2026-09-04
---------------------------------------------------------------------------*/
/* Le gabarit pose `padding: 60px 40px` sur la carte et `grid-gap: 0` sur la
   grille. Le rapport est inverse : 60px de vide au-dessus de l'icone, 80px
   entre les textes de deux cartes voisines, et ZERO entre les cartes
   elles-memes, dont les bordures se touchent.

   Les 60px datent d'un gabarit ou la carte tenait trois lignes ; les notres en
   tiennent dix. Le padding se resserre donc a 36/32, et 28/20 en mobile.

   Le `grid-gap: 0` reste, arbitre par le CEO le 2026-09-04 : les bordures
   jointives font une grille tabulaire, c'est un parti pris du gabarit et il
   tient. Detacher les cartes serait un autre design, pas un reglage.

   La formule de taille du titre n'a rien a recalculer : elle soustrait le
   padding par variable, qui suit ici. */
.grid-wrapper > .service-wrapper.service-style-1 {
  padding: 36px 32px;
}

.services.grid-wrapper.grid-xl-4 {
  --carte-padding: 64px;
}

@media (max-width: 767px) {
  .grid-wrapper > .service-wrapper.service-style-1 {
    padding: 28px 20px;
  }
  .services.grid-wrapper.grid-xl-4 {
    --carte-padding: 40px;
  }
}

/*---------------------------------------------------------------------------
  Le rappel a l'action de milieu de page -- 2026-09-04
---------------------------------------------------------------------------*/
/* Les pages se terminaient sur leur unique appel a l'action. Un lecteur qui
   decroche a mi-parcours n'avait rien a saisir.

   Pourquoi une LIGNE et pas une section. Une seconde section d'appel mettrait
   deux invitations de meme poids sur la meme page : elles se neutralisent, le
   lecteur reporte, puis oublie. La ligne pese 40px et ne prend la parole qu'une
   fois le sujet expose.

   Et surtout, elle n'engage PAS a la meme chose. Celle du bas demande le
   budget, le nombre d'equipes et ce qui bloque -- c'est une qualification.
   Celle-ci demande trente minutes. Elle attrape celui qui n'est pas encore
   pret a se qualifier, sans lui retirer l'autre porte. */
.cta-ligne {
  margin: 34px 0 0;
  text-align: center;
  font-size: 17px;
  color: var(--color-gray);
}

.cta-ligne a {
  color: var(--color-accent);
  font-weight: 600;
  white-space: nowrap;
}

.cta-ligne a span {
  display: inline-block;
  transition: transform .25s ease-in-out;
}

.cta-ligne a:hover span {
  transform: translateX(5px);
}


/*---------------------------------------------------------------------------
  Les trois avis : dire ce que chacun EST -- 2026-09-04
---------------------------------------------------------------------------*/
/* Le CEO demandait des badges. Un badge « Verifie » sans mecanisme de
   verification derriere est une perte de credibilite devant un DSI -- c'est
   exactement le public qui va le tester, et un label qu'on se decerne produit
   l'effet inverse de celui recherche.

   Ce qui a de la valeur, en revanche, c'est de dire ce que chaque avis EST.
   Les trois etaient presentes a l'identique alors qu'ils ne disent pas la meme
   chose : Mitch M. enonce un resultat mesurable (« plus de 90 % »), les deux
   autres felicitent l'equipe. Presentes pareil, le fort est tire vers le bas
   par les deux autres.

   D'ou trois etages, uniformes et sans redite : le nom, l'attribution (une
   societe quand on la connait, un role sinon), puis la nature de l'avis. Et la
   carte qui porte un resultat client se detache par son filet.

   Au passage, l'incoherence d'avant : Mitch portait une societe, les deux
   autres un role, dans le meme emplacement. */
.avis-auteur {
  font-size: 16px;
  font-weight: 700;
  color: var(--color-title);
  margin-bottom: 2px;
}

.avis-attribution {
  font-size: 14px;
  color: var(--color-gray);
  margin-bottom: 12px;
}

.avis-nature {
  display: inline-block;
  padding: 4px 14px;
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 999px;
  font-size: 12px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--color-gray);
  margin-bottom: 0;
}

.avis-nature-client {
  border-color: var(--color-accent);
  color: var(--color-accent);
}

.avis-carte-vedette {
  border-color: rgba(211, 168, 56, 0.45);
}


/*---------------------------------------------------------------------------
  Le schema d'architecture -- 2026-09-04
---------------------------------------------------------------------------*/
/* En SVG inline plutot qu'en image : net a toute densite, il suit la charte
   par variables, ne pese rien et se lit au lecteur d'ecran par son `desc`.

   Sur petit ecran il ne se replie pas -- un schema qui s'empile cesse d'etre
   un schema. Il defile horizontalement dans son propre cadre, avec une largeur
   minimale sous laquelle les libelles deviendraient illisibles. Le corps de la
   page, lui, ne defile jamais. */
.schema-cadre {
  overflow-x: auto;
  padding-bottom: 8px;
}

.schema {
  display: block;
  width: 100%;
  min-width: 760px;
  height: auto;
  font-family: inherit;
}

.schema-bande text {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.1em;
  fill: var(--color-accent);
}

.schema-boite rect {
  fill: rgba(255, 255, 255, 0.02);
  stroke: rgba(255, 255, 255, 0.18);
  stroke-width: 1;
}

/* Les bases se distinguent des autres boites : c'est le seul endroit du schema
   ou la repetition porte le sens. */
.schema-base rect {
  fill: rgba(211, 168, 56, 0.06);
  stroke: rgba(211, 168, 56, 0.45);
  stroke-width: 1;
}

.schema-trait path {
  fill: none;
  stroke: rgba(255, 255, 255, 0.28);
  stroke-width: 1.5;
}

.schema-titre text {
  font-size: 17px;
  font-weight: 600;
  fill: var(--color-title);
}

.schema-titre-base text {
  font-size: 15px;
  font-weight: 500;
}

.schema-legende text {
  font-size: 13px;
  fill: var(--color-gray);
}

.schema-note {
  font-size: 13px;
  fill: var(--color-accent);
}

.schema-fleche polygon {
  fill: rgba(255, 255, 255, 0.34);
}

/* Une zone qui defile sans le dire est une zone qu'on ne fait pas defiler.
   L'aide n'apparait qu'en dessous du palier ou le schema deborde de son cadre. */
.schema-aide {
  display: none;
  margin: 10px 0 0;
  font-size: 13px;
  color: var(--color-gray);
}

@media (max-width: 767px) {
  .schema-aide {
    display: block;
  }
}

/*---------------------------------------------------------------------------
  La capture du Majordome -- 2026-09-04
---------------------------------------------------------------------------*/
/* Le seul endroit du site ou l'on VOIT un produit fonctionner. Elle vient de
   la demonstration publique de crmflexs.com, et la legende nomme la source :
   une image d'interface qui ne dit pas d'ou elle vient se lit comme une
   maquette, et une maquette presentee comme un produit est un faux.

   Elle est claire sur un fond sombre, et c'est voulu : le produit a son
   interface, la page a la sienne. Le filet et l'arrondi disent « fenetre »
   plutot que « rectangle colle ».

   ELLE PREND LA LARGEUR DE SA COLONNE, et pas davantage. Posee sur toute la
   largeur, elle pesait plus que le schema qui la suit -- 880px pour une image
   qui illustre, contre un schema qui, lui, demontre. Dans la colonne des
   resultats elle fait environ 550px, sous les compteurs, donc en face du
   dernier temps du recit. Elle y remplit un vide qui existait deja : la
   colonne de droite s'arretait aux compteurs. */
.capture-produit {
  max-width: 100%;
  margin: 34px 0 0;
}

.capture-produit img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 12px;
  border: 1px solid rgba(255, 255, 255, 0.12);
}

.capture-produit figcaption {
  margin-top: 12px;
  text-align: center;
  font-size: 13px;
  color: var(--color-gray);
}

/*---------------------------------------------------------------------------
  A38 : l'ordre des titres cesse de sauter des niveaux -- 2026-09-04
---------------------------------------------------------------------------*/
/* L'audit du 2026-09-04 releve la sequence rendue :

     h1  h6 h6 h6 h6 h6  h2  h2  h5 h5 h5 h5  h2  h3...  h2  h2  h3  h5 h5 h5

   Quatre sauts, tous herites du gabarit. Un lecteur d'ecran qui navigue de
   titre en titre -- ce que font la plupart de leurs utilisateurs pour se
   reperer -- tombe donc de h1 a h6 des la premiere etape, puis remonte.

   Ce qui change, et pourquoi :

     la chaine d'expertises   `h6` -> `span`. Ce sont des libelles de
                              navigation dans un `<nav>`, pas des titres. Le
                              bon correctif n'est pas de changer leur niveau,
                              c'est de cesser d'en faire des titres.
     les cartes de service    `h5` -> `h3`, sous un `h2` de section.
     les etapes de l'accueil  `h4` -> `h3`, meme raison.
     le nom d'un temoignage   `h6` -> `p`. Un nom de personne n'est pas un
                              titre de section. Labs le faisait deja.
     le pied de page          `h5` -> `h2`. Ce sont bien des titres, et ils
                              ouvrent des sections de premier niveau du pied.

   LE RENDU NE DOIT PAS BOUGER D'UN PIXEL. Or ces elements tiraient leur
   taille de leur NIVEAU : `.service-title` et `.widget-title` valaient 26px
   parce que `h5` vaut 26px, `.step-title` valait 32 parce que `h4` vaut 32.
   Les passer en h3 ou h2 les aurait fait bondir a 42 et 60.

   Les tailles sont donc figees ici, AVANT le changement de balise, et a
   specificite de CLASSE (0,1,0) et non davantage : c'est exactement le poids
   qu'avait le niveau qu'on retire, donc les paliers responsives du gabarit,
   ecrits plus specifiquement, continuent de l'emporter. Une regle plus forte
   les aurait ecrases.

   Verifie par mesure avant / apres sur les trois pages temoins : tailles,
   interlignes et boites identiques au pixel. */
.service-title {
  font-size: 26px;
  line-height: normal;
}

.step-title {
  font-size: 32px;
  line-height: normal;
}

.widget-title {
  font-size: 26px;
}

/* CE QUE J'AVAIS OUBLIE, ET QUI A PRODUIT UNE REGRESSION VISIBLE.

   Un titre ne porte pas que sa taille. Le gabarit donne AUSSI aux six niveaux
   la fonte Russo One et la couleur `--color-title` (style.css, l. 183). Les
   deux elements qui cessent d'etre des titres -- le maillon de chaine et le
   nom d'auteur -- les perdaient donc, et le maillon est repasse en Montserrat
   gris la ou il etait en Russo One creme. Le CEO l'a vu tout de suite.

   Les quatre autres conversions restent des titres et ne sont pas concernees.

   Lecon a retenir pour toute conversion de balise : figer la taille ne suffit
   pas, il faut relever TOUT ce que le selecteur d'element apportait. */
.author-name {
  margin-top: 0;
  font-family: "Russo One", sans-serif;
}

/* Le maillon de la chaine devient un span, donc en ligne par defaut. Il
   occupait toute la largeur de son element de liste. */
.case-info-section.chaine-expertises .case-info ul li .maillon-nom {
  display: block;
  margin-bottom: 0;
  font-family: "Russo One", sans-serif;
  color: var(--color-title);
  font-size: 15px;
  line-height: 24px;
  white-space: nowrap;
  transition: color 0.3s ease-in-out;
}

.case-info-section.chaine-expertises .case-info ul li:hover .maillon-nom {
  color: var(--color-title);
}

.case-info-section.chaine-expertises .case-info ul li.maillon-courant .maillon-nom {
  background: var(--color-accent);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
}

/* Meme raison : `.city-name` tirait ses 26px du niveau `h5` qu'on retire.
   `.title` de la page contact, lui, est dans un `.section-title` et prend
   deja sa taille de la regle de bloc -- rien a figer. */
.city-name {
  font-size: 26px;
}

/* Les images de la charte, resservies par une URL neuve.

   Constaté le 2026-09-10 : le CDN d'OVH resservait les versions bleues de ces
   images, passées à l'or sous le même nom au déploiement du 2026-09-08. Il en
   garde des copies sur plusieurs noeuds, pour la durée que le .htaccess lui
   accordait - trente jours. Une query string change la clé de cache : chaque
   noeud, et chaque navigateur, va chercher la version dorée.

   Le gabarit appelle ces images depuis style.css, auquel on ne touche pas.
   Les sélecteurs ci-dessous reprennent les siens à l'identique et l'emportent
   parce que cette feuille est chargée après. */
.header-default .site-menu .main-menu .nav-item.active > a:before,
.header-default .site-menu .main-menu .nav-item:hover > a:before,
.header-3 .site-menu .main-menu .nav-item.active > a:before,
.header-3 .site-menu .main-menu .nav-item:hover > a:before {
  background-image: url(../images/menu-hover.png?v=20260910);
}

/* Les deux halos passent par image-set : le WebP d'abord, le PNG en repli pour
   qui ne sait pas le lire. Meme idiome que les bannieres, quelques centaines de
   lignes plus haut. 137 Ko de PNG deviennent 92 Ko - un degrade se comprime
   bien, mais il reste un degrade sur 1920 px de large. */
.ellipse-top {
  background-image: url("../images/bg/ellipse-top.png?v=20260910");
  background-image: image-set(
    url("../images/bg/ellipse-top.webp") type("image/webp"),
    url("../images/bg/ellipse-top.png?v=20260910") type("image/png"));
}

.ellipse-bottom {
  background-image: url("../images/bg/ellipse-bottom.png?v=20260910");
  background-image: image-set(
    url("../images/bg/ellipse-bottom.webp") type("image/webp"),
    url("../images/bg/ellipse-bottom.png?v=20260910") type("image/png"));
}

/* « Paris » au survol de la carte contact : un flash d'or en quittant.

   Le gabarit dore le texte par un fond or découpé à la forme des lettres
   (background-clip: text, remplissage transparent), et anime `all`. À la
   sortie du survol, le découpage et le remplissage transparent sautent
   aussitôt - ils ne s'animent pas - pendant que la couleur de fond met 0,3 s
   à s'éteindre : l'or se peint alors en bloc derrière le mot. Constaté le
   2026-09-10.

   L'or de la charte est une couleur pleine, et le découpage n'apporte rien
   qu'une couleur de texte ne fasse. On le neutralise ; c'est la couleur du
   lien qui s'anime, dans les deux sens, sans rien peindre derrière. */
.location-wrapper.location-style-1 .city-info .city-name,
.location-wrapper.location-style-1 .city-info .city-name a {
  transition: color 0.3s ease-in-out;
}

.location-wrapper.location-style-1:hover .city-info .city-name {
  background: none;
  -webkit-background-clip: border-box;
  background-clip: border-box;
  -webkit-text-fill-color: currentColor;
}

.location-wrapper.location-style-1:hover .city-info .city-name a {
  color: var(--color-accent);
  -webkit-text-fill-color: currentColor;
}

/* ---------------------------------------------------------------------------
   <picture> ne doit pas s'interposer dans la mise en page
   ---------------------------------------------------------------------------
   Depuis le 2026-09-13, les images lourdes sont rendues par imageAlegix()
   (_image.php), qui emballe l'<img> dans un <picture> pour servir le WebP.

   Un <picture> est une boite en ligne : la ou une regle visait l'image comme
   enfant direct de son conteneur, elle ne l'atteint plus, et l'image reprend
   sa largeur intrinseque. Constate sur le logo de l'en-tete, qui est passe de
   129 a 320 px de large.

   display: contents fait disparaitre la boite : l'<img> redevient, pour la
   mise en page comme pour les selecteurs, l'enfant direct du conteneur. Rien
   d'autre a changer, et les <picture> ecrits a la main avant cette date en
   beneficient aussi. */
picture {
  display: contents;
}

/* ---------------------------------------------------------------------------
   Les logos dont la HAUTEUR est imposee gardent leurs proportions
   ---------------------------------------------------------------------------
   Deux endroits commandent le logo par sa hauteur, et laissent sa largeur se
   deduire : le pied de page (`.footer-logo`, lien inline-flex de 4em) et
   l'en-tete du panneau de menu (`max-height: 45px`).

   Tant que l'image ne portait pas de dimensions, cette deduction se faisait
   toute seule. Depuis qu'elle porte width="320" - pose par imageAlegix pour
   reserver la place et eviter que la page saute - cette largeur devient sa
   base, et la hauteur imposee l'ecrase :

     pied de page      320x72  au lieu de 123x72
     panneau de menu   299x45  au lieu de  77x45

   width: auto rend le calcul aux proportions. Les valeurs de reference
   ci-dessus sont celles mesurees en production le 2026-09-13, avant la bascule.

   Toute image dont le CSS fixe la hauteur sans fixer la largeur releve du meme
   cas : c'est le point a verifier apres avoir donne ses dimensions a une
   image. */
.footer-logo img,
.offcanvas.main-menu-offcanvas .offcanvas-header .navbar-brand img {
  width: auto;
}

.footer-logo img {
  height: 100%;
}
