/* ==========================================================================
   custom.css, Abdeljalil Hcham
   Surcouche du template Isti. Aucune modification des CSS d'origine :
   tout ce qui est propre au site vit ici, préfixé .aj_

   --------------------------------------------------------------------------
   Vous lisez ceci dans l'onglet Sources et pas dans l'onglet Elements.
   Ça veut dire que vous cherchiez comment c'est fait, pas ce que ça affiche.
   Bonjour, donc.

   Puisque vous allez le voir de toute façon : oui, il y a beaucoup de
   !important dans ce fichier. Une vingtaine. Ce n'est pas de l'élégance,
   c'est de l'arithmétique.

   Le thème d'origine écrit lui-même des choses comme :

       img { width: 100% !important; height: 100% !important; }

   Quand la feuille du dessous joue avec !important sur des sélecteurs à un
   seul mot-clé, on ne gagne pas poliment. On peut soit réécrire le thème
   (et perdre les mises à jour de l'auteur) soit surcharger proprement
   depuis un fichier séparé et documenter chaque cas. J'ai choisi le second,
   et aucun de ces !important n'est là « au cas où » : chacun neutralise une
   règle précise que je pourrais vous montrer.

   C'est très exactement le genre d'arbitrage que je passe mes journées à
   faire, en plus gros. Le compromis parfait n'existait pas ; celui-ci se
   défend et s'explique en trois phrases.

   Il y a une suite dans la console :  abdeljalil.help()
   --------------------------------------------------------------------------
   ========================================================================== */

:root {
  --ajLine: rgba(199, 211, 0, 0.18);
  --ajPanel: #121309;
  --ajPanel2: #0f100a;
  --ajMuted: rgba(177, 177, 177, 0.75);
}

/* --- En-tête : visible en permanence ------------------------------------ */
/* Le template masque la barre de navigation dès que le visiteur descend
   (main.js écrit `top: -100px` en style inline) et ne la réaffiche qu'à la
   remontée. On veut au contraire un en-tête toujours présent : le recruteur
   doit pouvoir sauter à la FAQ ou au contact à n'importe quel moment de la
   page, sans avoir à remonter d'abord.

   Le `!important` est ici volontaire : il neutralise le style inline posé par
   le script du template, sans qu'on ait à modifier main.js. La barre reste
   `position: fixed`, elle suit donc toujours le défilement, et la classe
   `menu_fix` continue d'ajouter le fond noir au-delà de 100px. */
.tf__main_menu {
  top: 0 !important;
  /* Le thème pose `width: 100%` sur une barre en position fixed. Selon le
     navigateur et la fenêtre visuelle (zoom, clavier virtuel ouvert, barre
     d'URL rétractée sur mobile), ce 100% ne se résout pas toujours sur la
     largeur de l'écran : la barre déborde et le bouton du menu, calé à
     droite, sort du cadre. Sur téléphone, cela rend la navigation
     inaccessible.
     On ancre donc les deux bords : la largeur devient une conséquence de la
     position, plus un calcul de pourcentage. */
  left: 0;
  right: 0;
  width: auto !important;
  max-width: 100%;
  /* Le thème anime `all` sur cette barre, alors qu'il n'a besoin d'animer que
     l'apparition du fond noir au défilement. Tant que rien ne touche à sa
     géométrie, ça ne se voit pas. Dès qu'un pixel de position bouge — par
     exemple la largeur de la barre de défilement, neutralisée à l'ouverture
     d'une fiche de cas concret — ces quelques pixels se jouent en 0,3 s et la
     barre part en glissade sous les yeux du visiteur. Et comme le script du
     thème réécrit `style.top` à chaque événement de défilement, la transition
     redémarre en boucle et le glissement n'en finit pas.
     On restreint donc l'animation à ce qui doit réellement être animé. */
  transition: background linear 0.3s, border-color linear 0.3s,
    height linear 0.3s;
}

.tf__main_menu > .container-fluid {
  max-width: 100%;
}

/* --- Corrections globales du template ----------------------------------- */
/* SplitText découpe les libellés en <div class="menu-text"> (display:flex),
   ce qui avale les espaces entre les mots : « Mode d'emploi » devenait
   « Moded'emploi ». On rend l'espace au mot. */
.text_hover_animaiton .menu-text {
  margin-right: 0.3em;
  /* Le lien est un conteneur flex et .menu-text un de ses éléments : il peut
     donc rétrécir sous la largeur de son propre mot. Quand la barre manque
     de place, les <div> par caractère passent à la ligne, et comme
     .menu-text est en overflow:hidden sur deux lignes de haut, on lisait
     « Accu » au-dessus de « eil ». Le mot doit rester insécable et garder
     sa largeur. */
  flex: 0 0 auto;
  white-space: nowrap;
}

.text_hover_animaiton .menu-text:last-child {
  margin-right: 0;
}

/* Le thème ne prévoit qu'un seul cas : l'icône à la fin du bouton, avec
   `.tf__common_btn i { margin-left: 8px }`. Dès que l'icône ouvre le bouton,
   cette même marge la pousse vers la droite, donc contre le texte : il ne
   restait que l'espace typographique de la source HTML, environ 4px.

   Pourquoi une classe explicite et non `i:first-child` : en CSS,
   `:first-child` ne compte que les éléments, pas les nœuds de texte. Dans
   `<a>Télécharger le CV <i></i></a>`, l'icône est à la fois la dernière
   chose affichée et le premier élément enfant. Le sélecteur aurait donc
   inversé la marge sur les deux boutons, dont celui qu'il ne fallait pas
   toucher. Aucun sélecteur ne sait lire « il y a du texte avant » : c'est
   au HTML de le dire. */
.aj_btn_icon_left i {
  margin-left: 0;
  margin-right: 9px;
}

/* Ces boutons-là passent sur deux lignes en écran étroit. En flex, la
   deuxième ligne se replie sous la première lettre du texte et non sous
   l'icône, et l'icône reste centrée verticalement sur l'ensemble. */
.aj_btn_icon_left {
  display: inline-flex;
  align-items: center;
}

/* Le template force img{height:100% !important}, ce qui écrase les portraits */
.aj_img_auto img,
img.aj_img_auto {
  height: auto !important;
  object-fit: contain !important;
}

/* Le template met des `text-transform: capitalize` partout. En anglais ça
   passe, en français ça donne « Delivery Des Solutions Digitales ». */
.tf__single_skill_2_text h5,
.tf__single_skill_left span,
.tf__single_skill_left p,
.tf__single_skill_middle p,
.tf__footer_right h5,
.tf__contact_address ul li a,
.tf__footer_middle ul li a {
  text-transform: none;
}

/* Le thème fige la colonne de présentation : `.tf__about_text { width: 608px
   !important }`. Sa feuille responsive rend cette largeur automatique en
   dessous de 992px, mais oublie la tranche 992-1400px. Or la colonne n'y
   mesure que 480 à 570px : le texte débordait donc de 40 à 130px sur la
   colonne voisine et passait sous la fiche « l'essentiel ».

   `width: auto` avec un plafond à la même valeur donne exactement le même
   rendu au-delà de 1400px, où la colonne est plus large que 608px, et
   laisse le texte suivre sa colonne partout ailleurs. La version 90
   secondes utilise le même bloc en pleine largeur (`_left`) : on l'exclut,
   elle n'a pas de voisin à heurter. */
.tf__about_text:not(.tf__about_text_left) {
  width: auto !important;
  max-width: 608px;
  /* Le thème descend cette colonne de 80px pour la caler sur une photo qui
     n'existe plus ici : en face, il y a la fiche « l'essentiel », qui part du
     haut de la section. Les deux colonnes doivent donc démarrer à la même
     ligne. Sa feuille responsive remet déjà ce zéro en dessous de 992px, ce
     qui achève de montrer que la valeur ne tenait qu'au visuel d'origine. */
  margin-top: 0;
}

/* La remarque ferme la colonne : elle commente les trois paragraphes plutôt
   qu'elle ne les prolonge. À 18px du dernier, la marge par défaut de
   `.aj_note`, elle se lisait comme un quatrième paragraphe en italique. Le
   double d'écart suffit à la détacher, sans la faire flotter entre deux
   blocs. */
.tf__about_text:not(.tf__about_text_left) .aj_note {
  margin-top: 36px;
}

.aj_lead {
  font-size: 18px;
  line-height: 32px;
}

.tf__common_heading h2 {
  margin-bottom: 40px;
}

.aj_mb0 h2 {
  margin-bottom: 18px;
}

/* --- Logo texte (le template livrait un logo « Isti » en image) --------- */
.aj_logo {
  display: flex !important;
  align-items: center;
  gap: 14px;
  padding: 0;
}

.aj_logo_mark {
  flex: 0 0 auto;
  width: 46px;
  height: 46px;
  line-height: 46px;
  text-align: center;
  font-size: 17px;
  font-weight: 800;
  letter-spacing: 1px;
  color: var(--colorBlack) !important;
  background: var(--colorPrimary);
  transition: all linear 0.3s;
}

.aj_logo:hover .aj_logo_mark {
  background: var(--colorWhite);
}

.aj_logo_text {
  display: flex;
  flex-direction: column;
  line-height: 1.3;
}

.aj_logo_text b {
  font-size: 16px;
  font-weight: 700;
  color: var(--colorWhite);
}

.aj_logo_text em {
  font-size: 11px;
  font-style: normal;
  letter-spacing: 0.4px;
  color: var(--ajMuted);
}

.aj_logo_rail {
  width: 44px;
  height: 44px;
  line-height: 44px;
  text-align: center;
  font-size: 15px;
  font-weight: 800;
  letter-spacing: 1px;
  color: var(--colorBlack) !important;
  background: var(--colorPrimary);
}

@media (max-width: 575.98px) {
  .aj_logo_rail,
  .aj_lang_rail {
    width: 44px;
    height: 36px;
    line-height: 36px;
    font-size: 12px;
  }
}

.aj_logo_rail:hover {
  background: var(--colorWhite);
}

@media (max-width: 575px) {
  .aj_logo_text {
    display: none;
  }
}

/* --- Écran de chargement -------------------------------------------------
   Trois anneaux concentriques, chacun avec un seul arc coloré, tournant à
   des vitesses et dans des sens différents. Pas de texte : le nom du
   visiteur n'a rien à faire là, et celui du propriétaire du site non plus.
   Le <h5 class="preloader-text"> reste dans le HTML mais vide : le script du
   template s'en sert comme cible d'animation et déclenche depuis son
   onComplete la mise en route de toutes les apparitions au scroll. Le
   supprimer casserait la page entière.                                    */
/* Le nom du template est écrit en dur dans le CSS d'origine, pas dans le
   HTML : `h5.preloader-text::after { content: "Isti" }`. Vider la balise ne
   suffisait donc pas, le mot restait affiché pendant tout le chargement. */
h5.preloader-text {
  font-size: 0 !important;
}

h5.preloader-text::after {
  content: "" !important;
  display: none !important;
}

.aj_loader {
  position: relative;
  z-index: 2;
  width: 78px;
  height: 78px;
}

.aj_loader span {
  position: absolute;
  border: 2px solid transparent;
  border-radius: 50%;
  animation: aj_spin linear infinite;
}

.aj_loader span:nth-child(1) {
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  border-top-color: var(--colorPrimary);
  animation-duration: 1.1s;
}

.aj_loader span:nth-child(2) {
  top: 14px;
  right: 14px;
  bottom: 14px;
  left: 14px;
  border-right-color: rgba(255, 255, 255, 0.85);
  animation-duration: 1.7s;
  animation-direction: reverse;
}

.aj_loader span:nth-child(3) {
  top: 28px;
  right: 28px;
  bottom: 28px;
  left: 28px;
  border-bottom-color: var(--colorPrimary);
  animation-duration: 0.85s;
}

@keyframes aj_spin {
  to {
    transform: rotate(360deg);
  }
}

@media (prefers-reduced-motion: reduce) {
  .aj_loader span {
    animation-duration: 3s !important;
  }
}

/* --- Fiche « l'essentiel » ------------------------------------------------
   Face au texte de présentation : les informations qu'un recruteur cherche
   dans les trois premières minutes (disponibilité, localisation, mobilité,
   taille d'équipe, langues). Elles existaient déjà sur le site, éparpillées
   entre la FAQ et le pied de page. Ici elles sont réunies et scannables.  */
.aj_facts {
  position: relative;
  padding: 34px 32px 30px;
  background: var(--ajPanel);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-top: 3px solid var(--colorPrimary);
}

.aj_facts h6 {
  font-size: 13px;
  font-weight: 800;
  letter-spacing: 2px;
  text-transform: uppercase;
  color: var(--colorPrimary);
  margin-bottom: 26px;
}

/* Les trois promotions, en écho direct au titre de la section. */
.aj_ladder {
  position: relative;
  margin: 0 0 30px;
  padding: 0 0 4px 26px;
}

.aj_ladder::before {
  content: "";
  position: absolute;
  left: 4px;
  top: 8px;
  bottom: 20px;
  width: 1px;
  background: var(--ajLine);
}

.aj_ladder li {
  position: relative;
  padding: 0 0 16px;
}

.aj_ladder li::before {
  content: "";
  position: absolute;
  left: -26px;
  top: 7px;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--colorPrimary);
  opacity: 0.45;
}

.aj_ladder li:last-child::before {
  opacity: 1;
  box-shadow: 0 0 0 4px rgba(199, 211, 0, 0.16);
}

/* La période et l'intitulé sont sur la même ligne. Le `min-width` seul ne
   suffit pas à les séparer : « 2013 - 2017 » mesure 66px, donc il dépasse
   les 52px réservés et vient coller le titre, à trois pixels près.
   Le `padding-right` garantit l'espace quel que soit le libellé, et le
   `min-width` élargi aligne les intitulés sur une même colonne. */
.aj_ladder span {
  display: inline-block;
  min-width: 88px;
  padding-right: 14px;
  font-size: 13px;
  font-weight: 700;
  color: var(--colorPrimary);
}

.aj_ladder b {
  font-size: 16px;
  font-weight: 600;
  color: var(--colorWhite);
}

.aj_fact_list {
  margin: 0;
}

.aj_fact_list > div {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 18px;
  padding: 11px 0;
  border-top: 1px solid rgba(255, 255, 255, 0.07);
}

.aj_fact_list dt {
  flex: 0 0 150px;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.4px;
  text-transform: uppercase;
  color: rgba(177, 177, 177, 0.6);
  padding-top: 2px;
}

.aj_fact_list dd {
  flex: 1 1 220px;
  margin: 0;
  font-size: 16px;
  line-height: 26px;
  color: var(--colorBodyText);
}

.aj_facts_cta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 16px 24px;
  margin-top: 28px;
}

/* --- Carrousel des créations --------------------------------------------
   Défilement natif avec scroll-snap plutôt qu'un plugin : le glissement au
   doigt, la molette et le clavier fonctionnent sans une ligne de JavaScript.
   Le JS ne sert qu'aux flèches et aux points.

   Les captures sont des pages entières, donc très hautes. On les cadre en
   16/10 avec `object-position: top` : on montre l'en-tête de chaque site,
   la partie la plus dessinée et la plus reconnaissable.                   */
.aj_work {
  position: relative;
}

.aj_work_track {
  display: flex;
  gap: 18px;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  scrollbar-width: none;
  padding-bottom: 4px;
}

.aj_work_track::-webkit-scrollbar {
  display: none;
}

.aj_work_slide {
  flex: 0 0 100%;
  scroll-snap-align: start;
  margin: 0;
  position: relative;
  background: var(--ajPanel);
  border: 1px solid rgba(255, 255, 255, 0.08);
  overflow: hidden;
}

.aj_work_slide img {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 10;
  height: auto !important;
  object-fit: cover !important;
  object-position: top center !important;
  filter: saturate(0.92);
  transition: transform linear 6s;
}

.aj_work_slide:hover img {
  transform: translateY(-14%);
}

.aj_work_slide figcaption {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 12px;
  padding: 16px 20px;
  border-top: 1px solid rgba(255, 255, 255, 0.08);
}

.aj_work_slide figcaption b {
  font-size: 16px;
  font-weight: 700;
  color: var(--colorWhite);
}

.aj_work_slide figcaption span {
  font-size: 14px;
  color: var(--ajMuted);
}

.aj_work_nav {
  position: absolute;
  top: 34%;
  width: 44px;
  height: 44px;
  line-height: 46px;
  text-align: center;
  font-size: 18px;
  color: var(--colorBlack);
  background: var(--colorPrimary);
  opacity: 0.85;
  transition: opacity linear 0.25s;
  cursor: pointer;
}

.aj_work_nav:hover {
  opacity: 1;
}

.aj_work_nav[disabled] {
  opacity: 0.25;
  cursor: default;
}

.aj_work_prev {
  left: 0;
}

.aj_work_next {
  right: 0;
}

.aj_work_dots {
  display: flex;
  justify-content: center;
  gap: 8px;
  margin-top: 18px;
}

.aj_work_dots button {
  width: 9px;
  height: 9px;
  padding: 0;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.22);
  cursor: pointer;
  transition: all linear 0.25s;
}

.aj_work_dots button.is_active {
  width: 26px;
  border-radius: 5px;
  background: var(--colorPrimary);
}

/* --- Bascule de langue --------------------------------------------------- */
.aj_lang {
  display: inline-flex;
  align-items: center;
  border: 1px solid rgba(255, 255, 255, 0.16);
}

.aj_lang a {
  padding: 7px 12px;
  font-size: 12px;
  font-weight: 800;
  letter-spacing: 1px;
  line-height: 1;
  color: var(--ajMuted) !important;
  transition: all linear 0.25s;
}

.aj_lang a + a {
  border-left: 1px solid rgba(255, 255, 255, 0.16);
}

.aj_lang a:hover {
  color: var(--colorWhite) !important;
}

.aj_lang a.is_active {
  color: var(--colorBlack) !important;
  background: var(--colorPrimary);
}

.tf__main_menu .navbar-nav .aj_lang_item {
  display: flex;
  align-items: center;
  margin-left: 22px;
}

/* --- La barre tient-elle sur une ligne ? ---------------------------------
   Le thème livrait un logo en image et avait taillé la boîte pour elle :
   `.tf__main_menu .navbar-brand { max-width: 74px }`. Notre logo est du
   texte et mesure environ 240px : il débordait donc de sa boîte, et entre
   992 et 1400px il venait s'imprimer par-dessus le premier élément du menu.
   On rend d'abord à la marque sa largeur réelle, puis on récupère la place
   ailleurs : c'est le menu qui cède, pas le nom.

   Ce bloc vient après les définitions du logo et de la bascule de langue,
   volontairement : à spécificité égale, c'est le dernier écrit qui gagne.
   Le placer plus haut dans le fichier, à côté des autres correctifs de
   navigation, aurait été plus logique à lire et sans effet à l'affichage. */
.tf__main_menu .navbar-brand.aj_logo {
  max-width: none;
  width: auto;
}

/* Sous 1400px, la ligne de fonction sous le nom est ce qui coûte le plus
   cher en largeur : c'est la première chose qu'on retire. Elle est de toute
   façon reprise dans le titre de la page et dans le bandeau d'accueil. */
@media (max-width: 1399.98px) {
  .aj_logo_text em {
    display: none;
  }
}

@media (min-width: 992px) and (max-width: 1399.98px) {
  .tf__main_menu .navbar-nav .nav-item > a {
    padding-left: 14px;
    padding-right: 14px;
  }

  .tf__main_menu .navbar-nav .aj_lang_item {
    margin-left: 14px;
  }
}

/* Entre 992 et 1200px, le menu est encore déplié mais les sept entrées
   françaises, les plus longues des deux langues, plus la bascule ne
   laissent plus la place au nom. Le carré « AH » suffit à identifier la
   page : c'est lui qu'on garde. */
@media (min-width: 992px) and (max-width: 1199.98px) {
  .tf__main_menu .navbar-nav .nav-item > a {
    padding-left: 10px;
    padding-right: 10px;
  }

  .aj_logo_text {
    display: none;
  }
}

/* --- Rail de la version 90 secondes : compensation du contenu ------------
   Le rail est en `position: fixed` : il ne pousse rien, il recouvre. Replié
   au chargement, il ne gêne personne ; dès que le visiteur l'ouvre, il passe
   par-dessus le contenu et masque le début de chaque ligne.

   Le rail et `.main` sont frères dans le DOM : on décale donc le contenu de
   la largeur exacte du rail tant qu'il est ouvert, et on rend la place dès
   que le visiteur le referme. Au-delà de 1200px le thème gère déjà la
   marge, on n'y touche pas. */
/* Le contenu vit dans un `.container` centré, de 1320px au maximum. Sa marge
   gauche vaut donc (largeur écran - 1320) / 2, et ne dépasse la largeur du
   rail (116px) qu'à partir de 1552px. En dessous, le rail passe par-dessus
   le texte : sur la version 90 secondes, il recouvrait le début du titre et
   des boutons. On compense jusqu'au point où la marge suffit d'elle-même. */
@media (max-width: 1559.98px) {
  .main {
    transition: padding-left linear 0.3s;
  }

  .tf__main_2.show ~ .main {
    padding-left: 116px;
  }
}

/* Sur téléphone, 116px de rail sur 375px d'écran laisseraient une colonne
   de texte trop étroite : on resserre le rail et on ajuste d'autant.

   Le compte doit tomber juste. Le rail mesurait 90px : 44px d'icône, 10px
   de marge intérieure de chaque côté, et 12px de plus de chaque côté ajoutés
   par le `.container-fluid` que le thème glisse à l'intérieur. Pendant que
   le contenu n'était décalé que de 70px. Résultat : les 8 premiers pixels de
   chaque ligne passaient sous le rail, ce qui rognait la première lettre de
   tous les titres et de tous les paragraphes.

   On supprime la marge intérieure redondante (le rail a déjà la sienne), ce
   qui ramène le rail à 72px, et on décale le contenu de 74px. */
@media (max-width: 575.98px) {
  .tf__main_2 {
    padding: 8px 10px;
  }

  .tf__main_2 > .container-fluid {
    padding-left: 0;
    padding-right: 0;
  }

  .tf__main_manu_2 li a {
    width: 44px;
    height: 44px;
    line-height: 44px;
    font-size: 20px;
  }

  .tf__main_2.show ~ .main {
    padding-left: 74px;
  }
}

/* --- Rail replié : garder le bouton attrapable --------------------------
   Le rail part vers la gauche par un `left` négatif, et le bouton
   d'ouverture vit à l'intérieur : il part donc avec lui. Le thème compte sur
   le débordement du bouton (`left: 130%`) pour qu'il reste visible, mais le
   calcul ne tient qu'avec le rail d'origine. Mesuré sur un écran de 375px :
   sur les 50px du bouton, il en restait 6 à l'écran. Le menu devenait
   inatteignable, ce qui est une façon coûteuse d'apprendre qu'on l'avait
   déplié par défaut pour ne pas avoir à regarder ce cas.

   On cale donc le retrait sur le débordement du bouton et non sur la largeur
   du rail : à -70px, le bouton commence à 5px du bord sur téléphone et à
   10px au-delà, toujours entier. Le rail, lui, dépasse alors de quelques
   pixels ; on le rend transparent le temps qu'il est fermé et ces pixels ne
   se voient plus.

   Le fond transparent règle le cas du panneau, pas celui des icônes du
   menu, qui ont chacune le leur : mesuré à 768px, il restait 18px de chaque
   pastille le long du bord gauche, dont celle en jaune vif de la rubrique
   active. On les efface aussi, en fondu pour accompagner le glissement.

   `pointer-events` parce qu'un panneau invisible reste cliquable : sans
   cette ligne, une bande morte longeait le bord gauche de la page et
   avalait les taps destinés au texte.

   Tout ceci vit sous `max-width: 1600px`, la condition exacte que le thème
   utilise pour afficher le bouton : le repli n'existe que là où il y a de
   quoi le défaire. Au-delà, le thème masque le bouton et affiche le rail en
   permanence, et on le laisse faire.

   Le palier symétrique « min-width: 1601px » aurait semblé équivalent. Il ne
   l'est pas : entre les deux passent les largeurs fractionnaires, 1600,5px,
   que produisent un zoom navigateur ou un rapport de pixels non entier.
   Dans cette fente, aucune des deux règles ne s'applique, le rail serait
   replié et le bouton masqué. Une seule condition, pas deux qui se
   croisent. */
.tf__main_manu_2 {
  transition: opacity linear 0.3s;
}

@media (max-width: 1600px) {
  .tf__main_2:not(.show) {
    left: -70px;
    background: transparent;
    border-color: transparent;
    pointer-events: none;
  }

  .tf__main_2:not(.show) .mobile_menu_icon {
    pointer-events: auto;
  }

  .tf__main_2:not(.show) .tf__main_manu_2 {
    opacity: 0;
  }
}

/* Dans le rail vertical de la version 90 secondes, la place est comptée :
   un seul lien, vers l'autre langue. */
.aj_lang_rail {
  width: 44px;
  height: 34px;
  line-height: 34px;
  text-align: center;
  font-size: 12px;
  font-weight: 800;
  letter-spacing: 1px;
  color: var(--colorPrimary) !important;
  border: 1px solid var(--ajLine);
}

.aj_lang_rail:hover {
  color: var(--colorBlack) !important;
  background: var(--colorPrimary);
}

@media (max-width: 991px) {
  .tf__main_menu .navbar-nav .aj_lang_item {
    margin: 14px 0 0;
  }
}

/* --- Note en marge : le petit commentaire honnête sous chaque section ---- */
.aj_note {
  display: block;
  position: relative;
  margin-top: 18px;
  padding-left: 26px;
  font-size: 15px;
  line-height: 26px;
  color: var(--ajMuted);
  font-style: italic;
}

.aj_note::before {
  content: "";
  position: absolute;
  left: 0;
  top: 11px;
  width: 16px;
  height: 2px;
  background: var(--colorPrimary);
}

.aj_note b {
  color: var(--colorPrimary);
  font-style: normal;
  font-weight: 700;
}

/* --- Pills : stack, outils, langues ------------------------------------- */
.aj_pillgroup {
  margin-bottom: 38px;
}

.aj_pillgroup h6 {
  font-size: 13px;
  font-weight: 800;
  letter-spacing: 2px;
  text-transform: uppercase;
  color: var(--colorPrimary);
  margin-bottom: 16px;
}

.aj_pills {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

.aj_pills li {
  font-size: 14px;
  font-weight: 500;
  color: var(--colorBodyText);
  border: 1px solid rgba(255, 255, 255, 0.12);
  background: var(--ajPanel2);
  padding: 9px 16px;
  transition: all linear 0.25s;
}

.aj_pills li:hover {
  color: var(--colorBlack);
  background: var(--colorPrimary);
  border-color: var(--colorPrimary);
}

/* --- Compétences : trois colonnes plutôt que deux -------------------------
   Neuf familles réparties en deux colonnes, cela faisait une page très haute
   et beaucoup de blanc à droite des libellés courts. En trois colonnes de
   trois familles, la répartition tombe juste : 23, 23 et 22 étiquettes, et la
   section perd près d'un tiers de sa hauteur.

   Le prix à payer est la largeur de colonne. Elle vaut 364px au-dessus de
   1200px et descend à 304px entre 992 et 1199, où le conteneur Bootstrap
   n'offre plus que 960px. C'est cette valeur basse qui commande le reste.

   Les étiquettes passent donc à 13px avec un rembourrage plus court : la plus
   longue de la liste, « Gouvernance des mises en production », occupe alors
   247px et tient sur une ligne jusqu'en bas de la plage. À 14px elle repassait
   à la ligne et la colonne se remplissait d'étiquettes coupées en deux.

   Les intitulés de famille descendent à 11px, un cran sous les 12 auxquels on
   penserait d'abord. En capitales et à 800, « PILOTAGE DU DELIVERY & DES
   PROGRAMMES » fait 327px à 12px : elle serait passée sur deux lignes dans
   toute la plage 992-1199. À 11px avec un interlettrage un peu resserré, elle
   revient à 298px. C'est la même taille que les intitulés du bloc « CV vs
   réalité », donc rien de nouveau dans la page.

   Trois colonnes jusqu'à 992px, puis une seule. Le palier intermédiaire à deux
   colonnes a été essayé et retiré : avec trois blocs dans une rangée flex de
   deux, le troisième repart à la ligne et se cale sous le plus haut des deux
   premiers. Il restait un trou de deux cents pixels en bas à droite, visible
   sur toutes les tablettes. Une colonne pleine largeur n'a pas ce défaut, et
   les étiquettes s'y étalent sur trois ou quatre de front, ce qui compense la
   hauteur qu'on aurait cru perdre. */
.aj_skillgrid .aj_pillgroup h6 {
  font-size: 11px;
  letter-spacing: 1.4px;
  margin-bottom: 14px;
}

.aj_skillgrid .aj_pills {
  gap: 8px;
}

.aj_skillgrid .aj_pills li {
  font-size: 13px;
  line-height: 19px;
  padding: 7px 13px;
}

/* --- Carte générique ----------------------------------------------------- */
.aj_card {
  position: relative;
  height: 100%;
  padding: 38px 32px 34px;
  background: var(--ajPanel);
  border: 1px solid rgba(255, 255, 255, 0.07);
  /* Surtout pas `transition: all` : GSAP écrit opacity et transform image par
     image sur ces cartes pour l'animation d'apparition, et une transition CSS
     sur « all » entre en concurrence avec lui. On ne transitionne que ce qui
     relève réellement du survol. */
  transition: border-color linear 0.3s, transform linear 0.3s;
  overflow: hidden;
}

.aj_card::after {
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  width: 0;
  height: 3px;
  background: var(--colorPrimary);
  transition: all linear 0.35s;
}

.aj_card:hover {
  border-color: var(--ajLine);
  transform: translateY(-6px);
}

.aj_card:hover::after {
  width: 100%;
}

.aj_card_icon {
  display: block;
  font-size: 34px;
  line-height: 1;
  color: var(--colorPrimary);
  margin-bottom: 22px;
}

.aj_card h4 {
  font-size: 21px;
  font-weight: 700;
  margin-bottom: 14px;
}

.aj_card p {
  font-size: 16px;
  line-height: 27px;
}

.aj_card p + p {
  margin-top: 14px;
}

.aj_card_tag {
  display: inline-block;
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  color: var(--colorBlack);
  background: var(--colorPrimary);
  padding: 5px 12px;
  margin-bottom: 18px;
}

.aj_grid_row > [class*="col-"] {
  margin-bottom: 30px;
}

/* --- Mode d'emploi : l'endroit où la page arrête de faire des cartes ------
   Trois sections d'affilée reposent déjà sur `.aj_card` — les six
   différences, la formation, les cas concrets. Celle-ci était la quatrième.
   Arrivé là, le visiteur a traversé une trentaine de rectangles empilés et
   il en saute la moitié. Le contenu n'y est pour rien : c'est la forme qui a
   fini par user l'attention.

   Cette section change donc de registre plutôt que de couleur. Ce n'est plus
   une grille d'objets, c'est un schéma : un axe vertical qui traverse toute
   la section, six stations numérotées posées dessus, et le texte accroché de
   part et d'autre par une amorce. Aucun fond, aucune boîte, aucune ombre —
   des filets, des chiffres et des icônes, le vocabulaire de l'infographie.

   L'axe n'est pas une chronologie et ne prétend pas l'être : c'est un fil de
   lecture. Il donne un ordre à six points qui, alignés en grille, n'en
   avaient aucun, et il occupe le centre de la page — donc toute la largeur
   utile, ce qu'une colonne de texte ne fait jamais.

   L'alternance gauche/droite sert la respiration : chaque entrée dispose de
   la moitié de la page pour une mesure de 560px, soit une soixantaine de
   signes par ligne. Le texte est bien plus au large qu'en trois colonnes de
   cartes, et la page reste pleine. */
.aj_manual {
  position: relative;
}

.aj_manual_list {
  position: relative;
  list-style: none;
  margin: 0;
  padding: 0;
}

/* L'axe. Il se fond dans le noir à ses deux extrémités : un trait qui
   s'arrête net en haut et en bas donne l'impression d'un tableau tronqué,
   alors qu'ici il n'y a rien avant la première station ni après la
   dernière. */
.aj_manual_list::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  width: 1px;
  margin-left: -1px;
  background: linear-gradient(
    180deg,
    transparent 0%,
    var(--ajLine) 7%,
    var(--ajLine) 93%,
    transparent 100%
  );
}

/* Trois colonnes : le texte, l'axe, le texte. La colonne centrale fait
   106px, soit la station (58px) plus les deux amorces de 24px qui la relient
   au texte. Les trois valeurs sont liées — changer l'une sans les autres
   décroche les amorces des blocs.

   L'espacement entre entrées vit sur la ligne et non dans le bloc de texte :
   le filet vertical du bloc doit s'arrêter à la dernière ligne de son
   paragraphe. Posé en `padding-bottom`, il descendait cinquante pixels plus
   bas et pendait dans le vide. */
.aj_manual_item {
  display: grid;
  grid-template-columns: 1fr 106px 1fr;
  align-items: start;
  padding-bottom: 54px;
}

.aj_manual_item:last-child {
  padding-bottom: 0;
}

/* `grid-row: 1` sur les deux cellules, et ce n'est pas une précaution.

   Le placement automatique parcourt la grille de gauche à droite : sur une
   entrée paire, le texte est déclaré en colonne 3, donc le curseur dépasse la
   colonne 2 avant que la station ne soit placée. Ne trouvant plus de place à
   sa gauche sur la ligne courante, l'algorithme la renvoie à la ligne
   suivante — et la station se retrouvait soixante pixels sous son propre
   paragraphe, une entrée sur deux.

   L'ordre du HTML reste texte puis station : c'est l'ordre de lecture, et la
   station n'est qu'un repère (`aria-hidden`). La grille, elle, s'en moque. */
.aj_manual_body {
  grid-row: 1;
  grid-column: 1;
  justify-self: end;
  max-width: 560px;
  padding-right: 26px;
  border-right: 1px solid rgba(255, 255, 255, 0.08);
  transition: border-color linear 0.3s;
}

.aj_manual_item:nth-child(even) .aj_manual_body {
  grid-column: 3;
  justify-self: start;
  padding-right: 0;
  padding-left: 26px;
  border-right: 0;
  border-left: 1px solid rgba(255, 255, 255, 0.08);
}

.aj_manual_item:hover .aj_manual_body {
  border-color: var(--colorPrimary);
}

/* Icône et titre sur la même ligne. L'icône est un repère, pas une
   illustration : sa taille reste proche de celle du titre, loin des 34px des
   grandes icônes de cartes. */
.aj_manual_head {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-bottom: 13px;
}

.aj_manual_icon {
  flex: 0 0 auto;
  font-size: 26px;
  line-height: 1;
  color: var(--colorPrimary);
}

.aj_manual_head h4 {
  font-size: 22px;
  font-weight: 700;
  transition: color linear 0.3s;
}

.aj_manual_item:hover .aj_manual_head h4 {
  color: var(--colorPrimary);
}

.aj_manual_body p {
  font-size: 16px;
  line-height: 28px;
}

/* La station. Fond opaque et non transparent : l'axe passe derrière, la
   station doit le masquer sur sa hauteur, sinon le trait traverse le chiffre.
   La couleur est celle du corps de page et non `--ajPanel` : le bloc doit
   disparaître dans le fond et ne laisser voir que son contour. */
.aj_manual_node {
  position: relative;
  z-index: 1;
  grid-row: 1;
  grid-column: 2;
  justify-self: center;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 58px;
  height: 58px;
  background: var(--colorBlack);
  border: 1px solid var(--ajLine);
  font-size: 18px;
  font-weight: 800;
  letter-spacing: 1px;
  color: var(--colorPrimary);
  transition: background linear 0.3s, border-color linear 0.3s,
    color linear 0.3s;
}

.aj_manual_item:hover .aj_manual_node {
  background: var(--colorPrimary);
  border-color: var(--colorPrimary);
  color: var(--colorBlack);
}

/* L'amorce qui relie la station au bloc de texte : 24px, exactement le vide
   laissé par la station dans sa colonne. Elle touche donc le filet du bloc,
   et le schéma se lit d'un seul trait. */
.aj_manual_node::before {
  content: "";
  position: absolute;
  top: 50%;
  width: 24px;
  height: 1px;
  background: var(--ajLine);
  transition: background linear 0.3s;
}

.aj_manual_item:nth-child(odd) .aj_manual_node::before {
  right: 100%;
}

.aj_manual_item:nth-child(even) .aj_manual_node::before {
  left: 100%;
}

.aj_manual_item:hover .aj_manual_node::before {
  background: var(--colorPrimary);
}

/* La remarque de fin n'appartient à aucune des deux colonnes : elle reprend
   une largeur de lecture normale, centrée sous le schéma. */
.aj_manual_note {
  max-width: 760px;
  margin: 46px auto 0;
}

/* L'alternance bascule à 1200px et non à 992, parce que la bonne borne se
   calcule et ne se devine pas. Le conteneur Bootstrap vaut 960px entre 992 et
   1199 : ôtez ses marges, les 106px de la colonne centrale, la moitié du
   reste, puis le rembourrage du bloc, il reste 389px de texte, soit moins de
   cinquante signes par ligne. À 1200 le conteneur passe à 1140 et la même
   soustraction rend 479px, ce qui tient. L'alternance ne vit donc qu'au-dessus
   de cette marche.

   En dessous, l'axe se range à gauche, les six entrées passent du même côté et
   le schéma devient une échelle : la même grammaire, en plus étroit. */
@media (max-width: 1199.98px) {
  /* La gouttière de 24px n'est pas décorative : sans elle, la station
     touche le filet du bloc et son amorce, qui part de son bord droit, se
     dessine par-dessus le rembourrage du texte au lieu de rejoindre le
     filet. Le trait traversait la ligne verticale au lieu de s'y arrêter. */
  .aj_manual_item {
    grid-template-columns: 58px 1fr;
    column-gap: 24px;
    padding-bottom: 44px;
  }

  .aj_manual_list::before {
    left: 29px;
    margin-left: 0;
  }

  .aj_manual_node,
  .aj_manual_item:nth-child(even) .aj_manual_node {
    grid-column: 1;
    justify-self: start;
  }

  .aj_manual_body,
  .aj_manual_item:nth-child(even) .aj_manual_body {
    grid-column: 2;
    justify-self: start;
    max-width: none;
    padding-right: 0;
    padding-left: 26px;
    border-right: 0;
    border-left: 1px solid rgba(255, 255, 255, 0.08);
  }

  .aj_manual_item:nth-child(odd) .aj_manual_node::before {
    right: auto;
    left: 100%;
  }
}

/* Sous 576px la station tombe à 44px : à 58 elle mangeait un sixième de la
   largeur de l'écran pour afficher deux chiffres. */
@media (max-width: 575.98px) {
  .aj_manual_item {
    grid-template-columns: 44px 1fr;
    column-gap: 18px;
    padding-bottom: 38px;
  }

  .aj_manual_list::before {
    left: 22px;
  }

  .aj_manual_node {
    width: 44px;
    height: 44px;
    font-size: 15px;
  }

  .aj_manual_node::before {
    width: 18px;
  }

  .aj_manual_body,
  .aj_manual_item:nth-child(even) .aj_manual_body {
    padding-left: 18px;
  }

  .aj_manual_head {
    gap: 11px;
    margin-bottom: 10px;
  }

  .aj_manual_head h4 {
    font-size: 19px;
  }

  .aj_manual_icon {
    font-size: 22px;
  }
}

/* --- CV vs réalité ------------------------------------------------------- */
.aj_vs {
  display: grid;
  grid-template-columns: 1fr 1fr;
  border: 1px solid rgba(255, 255, 255, 0.07);
  margin-bottom: 22px;
  background: var(--ajPanel2);
}

.aj_vs > div {
  padding: 30px 32px;
}

.aj_vs > div:first-child {
  border-right: 1px solid rgba(255, 255, 255, 0.07);
}

.aj_vs h6 {
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 2px;
  text-transform: uppercase;
  margin-bottom: 14px;
  color: rgba(177, 177, 177, 0.55);
}

.aj_vs > div:last-child h6 {
  color: var(--colorPrimary);
}

.aj_vs p {
  font-size: 16px;
  line-height: 28px;
}

.aj_vs strong {
  color: var(--colorWhite);
  font-weight: 600;
}

@media (max-width: 767px) {
  .aj_vs {
    grid-template-columns: 1fr;
  }
  .aj_vs > div:first-child {
    border-right: none;
    border-bottom: 1px solid rgba(255, 255, 255, 0.07);
  }
}

/* --- FAQ recruteur (accordéon) ------------------------------------------ */
.aj_faq_item {
  border-bottom: 1px solid rgba(255, 255, 255, 0.09);
}

.aj_faq_q {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 20px;
  width: 100%;
  text-align: left;
  background: transparent;
  padding: 28px 0;
  cursor: pointer;
  font-family: var(--bodyFont);
  font-size: 21px;
  font-weight: 600;
  line-height: 32px;
  color: var(--colorWhite);
  transition: color linear 0.25s;
}

.aj_faq_q:hover,
.aj_faq_item.is_open .aj_faq_q {
  color: var(--colorPrimary);
}

.aj_faq_q i {
  flex: 0 0 auto;
  font-size: 18px;
  margin-top: 6px;
  color: var(--colorPrimary);
  transition: transform linear 0.3s;
}

.aj_faq_item.is_open .aj_faq_q i {
  transform: rotate(45deg);
}

.aj_faq_a {
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.4s ease;
}

.aj_faq_a_inner {
  padding: 0 0 30px;
  max-width: 860px;
}

.aj_faq_a p {
  font-size: 17px;
  line-height: 30px;
}

.aj_faq_a p + p {
  margin-top: 16px;
}

/* --- Références ---------------------------------------------------------- */
.aj_ref {
  height: 100%;
  padding: 32px 30px;
  background: var(--ajPanel);
  border: 1px solid rgba(255, 255, 255, 0.07);
  border-left: 3px solid var(--colorPrimary);
}

.aj_ref h5 {
  font-size: 19px;
  font-weight: 700;
  margin-bottom: 6px;
}

.aj_ref span {
  display: block;
  font-size: 14px;
  color: var(--colorPrimary);
  margin-bottom: 14px;
}

/* --- Nom du référent, chiffré -------------------------------------------
   Une largeur minimale exprimée en `ch` plutôt qu'un calage sur le contenu :
   les caractères changent quatorze fois par seconde, et la police à chasse
   fixe n'est qu'un repli si aucune des polices système n'est trouvée. Sans
   cette réserve, la carte tremblerait en continu.

   Les sélecteurs sont préfixés par `.aj_ref` non par goût, mais par
   nécessité : le thème pose `.aj_ref span { display: block }` pour la ligne
   de fonction. Les caractères étant eux aussi des <span>, ils héritaient de
   la règle et s'affichaient les uns sous les autres, un par ligne. Il faut
   donc repasser devant, d'où les deux classes. */
.aj_ref .aj_cipher {
  display: inline-block;
  min-width: 15ch;
  margin-bottom: 0;
  font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas,
    "Liberation Mono", monospace;
  font-size: 15px;
  font-weight: 600;
  letter-spacing: 0.5px;
  line-height: 26px;
  color: rgba(199, 211, 0, 0.42);
  white-space: nowrap;
  user-select: none;
}

.aj_ref .aj_cipher span {
  display: inline;
  margin-bottom: 0;
  font-size: inherit;
  color: inherit;
}

/* La tête de lecture : un seul caractère en pleine lumière, qui balaie la
   ligne. C'est ce qui fait lire « chiffré » plutôt que « cassé ». */
.aj_ref .aj_cipher span.is_head {
  color: var(--colorWhite);
  text-shadow: 0 0 10px rgba(199, 211, 0, 0.85);
}

/* Un nom absent se remplace par une raison, pas par un blanc. */
.aj_ref_hint {
  display: block;
  margin-bottom: 12px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: rgba(177, 177, 177, 0.5);
}

.aj_ref p {
  font-size: 15px;
  line-height: 26px;
}

/* --- Ce que je cherche / ce que je ne cherche pas ------------------------ */
.aj_seek li {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  padding: 13px 0;
  font-size: 16px;
  line-height: 27px;
  color: var(--colorBodyText);
  border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}

.aj_seek li i {
  flex: 0 0 auto;
  margin-top: 5px;
  font-size: 15px;
}

.aj_seek.aj_yes li i {
  color: var(--colorPrimary);
}

.aj_seek.aj_no li i {
  color: #7a7a6a;
}

.aj_seek.aj_no li {
  color: rgba(177, 177, 177, 0.6);
}

/* --- Bandeau chiffres ---------------------------------------------------- */
/* Le template colore les compteurs en noir (fond vert) sur la home 1 et les
   met en contour blanc sur la home 2. On uniformise : chiffres pleins, en
   couleur primaire, sur fond sombre comme le reste du site. */
.aj_kpi h4,
.aj_kpi h4 span {
  font-size: 52px;
  font-weight: 800;
  color: var(--colorPrimary) !important;
  -webkit-text-stroke: 0 !important;
  line-height: 1;
  margin-bottom: 10px;
}

.aj_kpi p {
  font-size: 15px;
  line-height: 24px;
  color: var(--colorBodyText) !important;
}

.aj_kpi_band .tf__single_counter {
  background: var(--ajPanel);
  border: 1px solid rgba(255, 255, 255, 0.07);
}

/* --- Hauteurs égales dans les bandeaux de chiffres -----------------------
   Le thème laisse chaque encadré se dimensionner sur son propre texte. Nos
   libellés n'ont pas tous la même longueur : deux tenaient sur deux lignes,
   deux sur trois, et le bandeau se lisait comme quatre chiffres d'importance
   inégale. Or c'est faux, ils comptent autant les uns que les autres.

   Bootstrap étire déjà les colonnes d'une même ligne à la hauteur de la plus
   haute (`align-items: stretch` par défaut). Il ne manquait qu'un maillon :
   la carte ne remplissait pas sa colonne. On rend la colonne flex et la
   carte devient un élément étirable. Aucune hauteur en dur, donc rien à
   maintenir si un libellé change. */
.aj_kpi_row > [class*="col-"] {
  display: flex;
}

.aj_kpi_row > [class*="col-"] > .aj_kpi {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
}

/* La colonne pleine largeur ne porte que la note en marge : elle n'a pas à
   entrer dans le calcul. */
.aj_kpi_row > .col-xl-12 {
  display: block;
}

.aj_kpi p {
  margin-bottom: 0;
}

.aj_kpi_band .tf__counter_icon {
  background: var(--colorPrimary);
  color: var(--colorBlack);
}

.aj_kpi_band .tf__counter_icon i {
  color: var(--colorBlack);
}

/* --- Répartition du temps par poste --------------------------------------
   Le thème n'offrait qu'une seule barre par ligne. Une métrique unique
   racontait mal l'évolution : on montre donc trois dimensions par poste,
   ce qui rend visible le glissement du code vers l'architecture puis vers
   l'encadrement.                                                          */
.aj_mix {
  padding-top: 4px;
}

.aj_mix_title {
  font-size: 12px !important;
  font-weight: 700;
  letter-spacing: 1.6px;
  text-transform: uppercase;
  color: var(--colorPrimary) !important;
  margin-bottom: 20px !important;
}

.aj_mix_row {
  position: relative;
  padding-bottom: 20px;
}

.aj_mix_row span {
  display: inline-block;
  max-width: 78%;
  font-size: 14px;
  line-height: 20px;
  color: var(--colorBodyText);
}

.aj_mix_row b {
  position: absolute;
  right: 0;
  top: 0;
  font-size: 14px;
  font-weight: 700;
  color: var(--colorWhite);
}

.aj_mix_row i {
  display: block;
  position: relative;
  z-index: 1; /* passe au-dessus du rail gris tracé par ::after */
  height: 4px;
  margin-top: 8px;
  background: var(--colorPrimary);
  border-radius: 2px;
}

/* Rail de fond sur toute la largeur. Pas de z-index négatif ici : il
   ferait passer le rail derrière le fond de la carte, donc invisible. */
.aj_mix_row::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 20px;
  height: 4px;
  border-radius: 2px;
  background: rgba(255, 255, 255, 0.09);
}

/* --- Barres du parcours -------------------------------------------------- */
/* La bulle de pourcentage est calée à -45px de la barre : sans marge
   supplémentaire, elle passe par-dessus le libellé. */
.tf__team_skills_bar_single p {
  margin-bottom: 46px;
  letter-spacing: 0.5px;
}

/* --- Timeline sobre (parcours) ------------------------------------------ */
.aj_role_meta {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 16px;
}

.aj_role_meta li {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.5px;
  color: var(--colorPrimary);
  border: 1px solid var(--ajLine);
  padding: 5px 11px;
}

/* --- La ligne suivante du parcours, laissée ouverte -----------------------
   Un parcours qui s'arrête au poste précédent oblige le lecteur à faire le
   raccord tout seul, et le raccord qu'il fait n'est pas toujours celui qu'on
   voudrait : « il part, donc il repartira ». Autant occuper la place et
   répondre à la question pendant qu'elle se pose.

   Le cadre en pointillés fait le travail avant le texte : c'est le vocabulaire
   des emplacements vides, un formulaire à remplir plutôt qu'une ligne de plus.
   Le nom de l'employeur, à l'endroit où les quatre lignes précédentes disent
   « Deloitte », devient un champ vide qui attend un nom. Le reste du bloc
   reprend exactement la géométrie des autres postes : c'est la même colonne,
   pas un encart publicitaire greffé en bas de section.

   Le fond est à 2,5% et non à 5 : il faut que le pointillé se lise comme un
   vide, pas comme une case mise en avant. */
.aj_next_slot {
  border: 1px dashed var(--ajLine);
  background: rgba(199, 211, 0, 0.025);
  padding: 34px 30px;
  transition: border-color linear 0.3s;
}

.aj_next_slot:hover {
  border-color: rgba(199, 211, 0, 0.42);
}

/* Le champ à remplir. `.tf__single_skill_left span` porte les noms
   d'employeur, en blanc et en capitales : ici le même emplacement reste
   typographiquement identique et gagne seulement son contour. */
.tf__single_skill_left .aj_next_host {
  display: inline-block;
  margin-top: 4px;
  padding: 6px 12px;
  border: 1px dashed rgba(199, 211, 0, 0.45);
}

/* La colonne de droite portait une liste « ce que je cherche ». Elle a été
   retirée pour une raison de fond : trois exigences alignées se lisent comme
   un filtre, et un recruteur qui n'est pas certain de pouvoir les tenir
   n'écrit pas — l'encart censé ouvrir la conversation la fermait. La colonne
   dit maintenant l'inverse, en texte suivi, et ce texte doit se distinguer du
   récit de la colonne du milieu : légèrement plus petit, dans le gris de
   second plan, avec le trait vertical qui le rattache à son titre. */
.aj_next_cta {
  padding-top: 4px;
  padding-left: 22px;
  border-left: 1px solid var(--ajLine);
}

.aj_next_cta p:not(.aj_mix_title) {
  font-size: 15px;
  line-height: 26px;
  color: var(--ajMuted);
}

.aj_next_cta .tf__common_btn {
  display: inline-block;
  margin-top: 20px;
  padding: 15px 26px;
}

/* Les trois colonnes du poste ne portent que `col-xl-*` et `col-lg-*` : sous
   992px elles se réempilent. Le filet vertical n'a alors plus rien à séparer
   et laisserait un trait orphelin au-dessus du bouton. */
@media (max-width: 991.98px) {
  .aj_next_cta {
    padding-left: 0;
    border-left: 0;
    margin-top: 26px;
  }
}

/* --- La même case, version 90 secondes -----------------------------------
   La version courte utilise `.tf__single_skill_2`, dont l'icône est
   positionnée en absolu par rapport au bloc (top 75, left 18) pour venir se
   caler sous le grand chiffre. Ajouter un cadre ajoute un rembourrage, et le
   rembourrage décale le chiffre sans décaler l'icône : les 30px sont donc
   reportés sur les deux coordonnées, sinon la flèche flotte à gauche du
   numéro au lieu d'être dessous. */
.aj_next_slot_2 {
  /* Les quatre postes précédents laissent 130px sous eux. La case ouverte
     est la dernière du bloc : elle rend une partie de cet espace à la
     section, sans casser le rythme vertical. */
  margin-bottom: 30px;
  padding: 30px;
}

/* Le chiffre en contour dit « pas encore rempli » avant qu'on ait lu le
   titre — le même vocabulaire que le pointillé du cadre. Enfermé dans un
   `@supports`, sinon `color: transparent` s'appliquerait seul et le 05
   disparaîtrait. */
@supports (-webkit-text-stroke: 1px #000) {
  .aj_next_slot_2 > h2 {
    color: transparent;
    -webkit-text-stroke: 1px var(--colorPrimary);
  }
}

.aj_next_slot_2 .tf__skill_2_icon {
  top: 105px;
  left: 48px;
}

/* Le titre de la case tient lieu de nom d'employeur : son filet passe en
   pointillés, comme le cadre. */
.aj_next_slot_2 .aj_next_host {
  border-bottom-style: dashed;
}

.aj_next_link {
  display: inline-block;
  margin-top: 18px;
  font-weight: 700;
}

/* Les bornes sont celles de responsive.css — 991.99 et 767.99, et non les
   .98 employés ailleurs dans ce fichier. Ici ce n'est pas cosmétique : la
   position de l'icône est un décalage calculé à partir de la valeur du
   thème, les deux règles doivent donc basculer au même pixel. */
@media (max-width: 991.99px) {
  .aj_next_slot,
  .aj_next_slot_2 {
    padding: 26px 20px;
  }

  /* Le thème garde ici son icône à top 75 / left 18. */
  .aj_next_slot_2 .tf__skill_2_icon {
    top: 101px;
    left: 38px;
  }
}

/* Sous 768px le thème rapetisse le chiffre et remonte l'icône à
   top 55 / left 14 : le décalage repart de là.

   Le texte reprend au passage toute la place disponible. Le thème le fixe à
   75% de la case, ce qui va tant que la case n'a pas de rembourrage. La
   nôtre en a quarante pixels, et sur un écran de 375 la colonne tombait à
   215px, soit à peu près quatre mots par ligne. En flex, `width: 100%` sur un
   élément qui ne peut pas tenir revient à « prends ce qui reste après le
   chiffre » : c'est exactement ce qu'on veut ici. */
@media (max-width: 767.99px) {
  .aj_next_slot_2 .tf__skill_2_icon {
    top: 81px;
    left: 34px;
  }

  .aj_next_slot_2 .tf__single_skill_2_text {
    width: 100%;
  }
}

/* --- Chronomètre de la version 90 secondes ------------------------------ */
.aj_watch {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  padding: 12px 20px;
  margin-bottom: 26px;
  background: var(--ajPanel);
  border: 1px solid var(--ajLine);
}

.aj_watch i {
  color: var(--colorPrimary);
  font-size: 17px;
}

.aj_watch_time {
  font-size: 17px;
  font-weight: 800;
  letter-spacing: 2px;
  color: var(--colorWhite) !important;
  font-variant-numeric: tabular-nums;
  min-width: 62px;
}

.aj_watch_label {
  font-size: 13px;
  color: var(--ajMuted) !important;
}

.aj_watch.is_done .aj_watch_time {
  color: var(--colorPrimary) !important;
}

/* --- Divers -------------------------------------------------------------- */
/* Le cadre et le tampon doivent rester accrochés au bas de la photo, décalés
   du même nombre de pixels vers la droite et vers le bas. Ils étaient tous
   les deux positionnés sur le conteneur — et le thème fige la hauteur de ce
   conteneur (`.tf__banner_img { height: 750px !important }`, ramenée à 480px
   entre 1200 et 1400px), alors que la photo, elle, garde ses proportions :
   420px de large pour un original de 500 × 736, cela fait 618px de haut.

   Le conteneur et la photo n'avaient donc jamais la même hauteur, et l'écart
   changeait à chaque palier de largeur : tantôt le tampon flottait 112px
   au-dessus du bas de la photo, tantôt il tombait juste. D'où un bug qui ne
   se voyait qu'à certaines tailles de fenêtre.

   Le conteneur épouse maintenant la photo, et un seul réglage —
   `--ajHeroOffset` — commande à la fois le débord du cadre et celui du
   tampon. Les deux ne peuvent plus se désolidariser : il n'y a plus qu'une
   valeur à changer par palier, au lieu de quatre à garder cohérentes. */
.aj_hero_photo {
  --ajHeroOffset: 22px;
  position: relative;
  max-width: 420px;
  margin: 0 auto;
  height: auto !important;
}

.aj_hero_photo img {
  position: relative;
  z-index: 1;
  /* `display: block` : en inline, l'image traîne l'espace réservé aux
     jambages sous la ligne de base, et le conteneur mesure quelques pixels
     de plus qu'elle. Le tampon s'en trouverait décollé d'autant. */
  display: block;
  height: auto !important;
  object-fit: cover !important;
  filter: grayscale(100%) contrast(1.05);
  transition: filter linear 0.5s;
}

.aj_hero_photo:hover img {
  filter: grayscale(0%);
}

.aj_hero_photo::before {
  content: "";
  position: absolute;
  inset: var(--ajHeroOffset) calc(-1 * var(--ajHeroOffset))
    calc(-1 * var(--ajHeroOffset)) var(--ajHeroOffset);
  border: 1px solid var(--ajLine);
  z-index: 0;
}

/* Le tampon se cale exactement sur l'angle bas-droit du cadre : mêmes bords,
   même débord. C'est ce qui fait lire l'ensemble comme un décalage voulu. */
.aj_stamp {
  position: absolute;
  z-index: 2;
  right: calc(-1 * var(--ajHeroOffset));
  bottom: calc(-1 * var(--ajHeroOffset));
  background: var(--colorPrimary);
  color: var(--colorBlack);
  font-size: 12px;
  font-weight: 800;
  letter-spacing: 1px;
  text-transform: uppercase;
  padding: 12px 18px;
  max-width: 210px;
  line-height: 18px;
}

.aj_inline_link {
  color: var(--colorPrimary);
  border-bottom: 1px solid var(--ajLine);
}

.aj_inline_link:hover {
  border-bottom-color: var(--colorPrimary);
}

.aj_contact_line {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 16px 0;
  border-bottom: 1px solid rgba(255, 255, 255, 0.07);
}

.aj_contact_line i {
  color: var(--colorPrimary);
  font-size: 17px;
  width: 22px;
  text-align: center;
}

.aj_contact_line a,
.aj_contact_line span {
  font-size: 16px;
  color: var(--colorBodyText);
  transition: color linear 0.25s;
}

.aj_contact_line a:hover {
  color: var(--colorPrimary);
}

.aj_secondary_btn {
  background: transparent !important;
  border: 2px solid rgba(255, 255, 255, 0.18) !important;
  color: var(--colorWhite) !important;
}

.aj_secondary_btn:hover {
  border-color: var(--colorPrimary) !important;
  color: var(--colorPrimary) !important;
}

/* --- « J'ai 90 secondes » : le bouton qu'il ne faut pas rater -------------
   Ce bouton est la sortie de secours de la page : il mène à la version
   courte, pour qui n'a pas le temps de tout lire. Posé à côté du
   téléchargement du CV, il se faisait avaler — même forme, même hauteur,
   contour discret — et les gens pressés, exactement ceux à qui il s'adresse,
   passaient devant sans le voir.

   Le signal doit donc attraper l'œil périphérique sans réclamer le clic : le
   but n'est pas de détourner de la version longue, seulement de signaler
   qu'une porte existe. D'où un cycle de sept secondes qui ne contient que
   deux gestes très courts — un anneau qui s'écarte une fois, l'aiguille du
   chronomètre qui fait un aller-retour — et six secondes d'immobilité
   complète. Une animation en boucle serrée aurait fait l'inverse : elle
   aurait tiré l'œil hors du texte en permanence.

   L'anneau est un pseudo-élément et non la bordure du bouton. Ce n'est pas
   un choix esthétique : `.aj_secondary_btn` pose sa bordure en `!important`
   (elle doit battre `.tf__common_btn`, plus haut dans la cascade), et une
   déclaration `!important` l'emporte sur une animation. Animer `border-color`
   n'aurait tout simplement rien produit. */
.aj_pulse_btn {
  position: relative;
}

/* `inset: -2px` recouvre exactement la boîte de bordure : au repos l'anneau
   est invisible, il ne double pas le contour. */
.aj_pulse_btn::after {
  content: "";
  position: absolute;
  inset: -2px;
  border: 2px solid var(--colorPrimary);
  opacity: 0;
  pointer-events: none;
  animation: aj_btn_ping 7s ease-out 3s infinite;
}

/* Les quatre décalages plutôt qu'un `transform: scale()`. Deux raisons.

   La première est géométrique : le bouton fait environ 230 × 62px, et une
   homothétie écarte les côtés proportionnellement — 15px à gauche et à
   droite, 4px en haut et en bas. L'anneau ne s'éloigne pas, il s'aplatit.
   Un écart en pixels s'éloigne du même nombre de pixels partout.

   La seconde est un bug évité : sous 992px le bouton passe en pleine
   largeur, et 13% de 351px font 23px qui sortent de l'écran de chaque côté.
   `body` étant en `overflow-x: hidden`, il n'y aurait pas eu de barre de
   défilement — seulement un anneau coupé net sur les deux bords, ce qui est
   pire qu'une barre : ça ressemble à un défaut, pas à un effet.

   Les quatre longhands plutôt que le raccourci `inset` : le raccourci
   s'anime correctement partout aujourd'hui, mais l'écrire éclaté ne coûte
   rien et lève la question avant qu'elle se pose. */
@keyframes aj_btn_ping {
  0% {
    opacity: 0;
    top: -2px;
    right: -2px;
    bottom: -2px;
    left: -2px;
  }
  4% {
    opacity: 0.5;
    top: -2px;
    right: -2px;
    bottom: -2px;
    left: -2px;
  }
  20% {
    opacity: 0;
    top: -10px;
    right: -10px;
    bottom: -10px;
    left: -10px;
  }
  100% {
    opacity: 0;
    top: -10px;
    right: -10px;
    bottom: -10px;
    left: -10px;
  }
}

/* Un aller-retour d'aiguille, puis plus rien. Un chronomètre qui tournerait
   en continu serait un scintillement permanent en bas du bandeau. */
.aj_pulse_btn i {
  animation: aj_btn_tick 7s ease-in-out 3s infinite;
}

@keyframes aj_btn_tick {
  0%,
  5% {
    transform: rotate(0deg);
  }
  8% {
    transform: rotate(11deg);
  }
  11% {
    transform: rotate(-7deg);
  }
  15%,
  100% {
    transform: rotate(0deg);
  }
}

/* Au survol et au clavier, le bouton a déjà son propre état : l'appel n'a
   plus de raison d'être, et deux signaux simultanés en font un de trop. */
.aj_pulse_btn:hover::after,
.aj_pulse_btn:focus-visible::after {
  animation: none;
  opacity: 0;
}

.aj_pulse_btn:hover i,
.aj_pulse_btn:focus-visible i {
  animation: none;
}

/* Pour qui a demandé moins d'animations, le repère devient fixe : l'anneau
   reste posé sur la bordure, à faible opacité. Le bouton se distingue encore
   de son voisin, simplement sans rien qui bouge — « moins de mouvement » ne
   veut pas dire « moins d'information ». */
@media (prefers-reduced-motion: reduce) {
  .aj_pulse_btn::after {
    animation: none;
    opacity: 0.4;
  }

  .aj_pulse_btn i {
    animation: none;
  }
}

.aj_langline {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  padding: 14px 0;
  border-bottom: 1px solid rgba(255, 255, 255, 0.07);
}

.aj_langline h6 {
  font-size: 17px;
  font-weight: 600;
  color: var(--colorWhite);
}

.aj_langline span {
  font-size: 14px;
  color: var(--ajMuted);
  text-align: right;
}

/* Marqueur des blocs à personnaliser (section « Hors du bureau »).
   Aucun effet visuel : le site reste publiable tel quel. Pour repérer les
   blocs concernés pendant l'édition, décommenter la règle ci-dessous. */
.aj_todo {
  /* outline: 1px dashed rgba(199, 211, 0, 0.45); */
}

/* ==========================================================================
   PORTRAIT DU BANDEAU SOUS 1200px
   Le thème dimensionne le portrait en dur : `.tf__banner_img { width: 750px;
   height: 750px !important }`. Ces valeurs ne sont reprises que dans les
   paliers 1200-1600px ; en dessous, le thème se contente de masquer le bloc
   (`display: none`).

   Comme on réaffiche le portrait sur téléphone, on héritait donc des 750px
   de haut avec une image de 294px : environ 450px de vide entre le visage
   et le texte de présentation.

   On rend donc au conteneur sa hauteur naturelle. Au passage, on réaffiche
   le portrait sur tablette : il n'y a pas de raison qu'un recruteur sur iPad
   soit le seul à ne pas voir de visage.
   ========================================================================== */
@media (max-width: 1199.98px) {
  .tf__banner_img.aj_hero_photo {
    display: block !important;
    width: auto;
    height: auto !important;
    max-width: 320px;
  }
}

/* --- Bloc contact : deux panneaux, une seule paire -----------------------
   Les deux moitiés du bloc contact ne se ressemblaient pas. À gauche, le
   panneau de fond est un ::after de 940px de large ancré à droite : il
   débordait donc largement du conteneur vers la gauche, jusqu'à sortir de
   l'écran (c'était au passage la deuxième cause de défilement horizontal sur
   téléphone). À droite, le formulaire a un fond plein, strictement à la
   taille de sa colonne. Et comme chacun ne fait que la hauteur de son
   contenu, les deux fonds s'arrêtaient à des hauteurs différentes.

   On les ramène à la même géométrie : le fond de gauche est calé sur sa
   colonne comme celui de droite, les deux prennent toute la hauteur de la
   rangée, et le rembourrage de gauche reprend celui du formulaire pour que
   le texte s'aligne d'un panneau à l'autre. La correction de débordement
   qui vivait ici n'a plus lieu d'être : il n'y a plus rien qui déborde. */
.tf__contact_area {
  padding: 55px 30px;
  height: 100%;
}

.tf__contact_area::after {
  width: auto;
  left: 0;
  right: 0;
}

.tf__form {
  height: 100%;
}

/* Les colonnes ne se placent côte à côte qu'à partir de 1200px (`col-xl-6`).
   En dessous elles s'empilent : chacune occupe déjà toute la largeur, et
   `height: 100%` n'aurait plus de rangée à remplir. */
@media (max-width: 1199.98px) {
  .tf__contact_area,
  .tf__form {
    height: auto;
  }
}

/* ==========================================================================
   FORMULAIRE DE CONTACT : erreurs, état d'envoi, accusé de réception
   --------------------------------------------------------------------------
   Le formulaire poste vers contact.php. Ce qui est décrit ici, ce sont les
   trois choses qu'un formulaire doit savoir dire et que la version
   précédente n'avait pas à dire, puisqu'elle ne faisait qu'ouvrir une
   messagerie : ce qui ne va pas, que c'est parti, et que ça a échoué.
   ========================================================================== */

/* Le rouge est la seule couleur ajoutée à la palette depuis le début du
   site, et c'est délibéré : signaler une erreur avec le jaune de la marque
   reviendrait à demander au visiteur de deviner, à la teinte près, si on lui
   parle d'un accent ou d'un problème. Choisi chaud plutôt que rouge pur, et
   vérifié à 6,7:1 sur le fond de page — un message d'erreur illisible est
   une erreur de plus. */
:root {
  --ajError: #ff7a68;
}

/* Le piège à robots. Hors écran, et surtout pas en `display: none` : la
   moitié des robots à formulaire sautent les champs masqués de cette
   façon-là, ce qui viderait le piège de son intérêt. `aria-hidden` et
   `tabindex="-1"` sont sur le HTML : à eux deux, ils le retirent aussi du
   parcours au clavier et de la lecture d'écran. */
.aj_form_trap {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* Le message se glisse dans la marge basse du champ (24px, style.css) plutôt
   que de l'agrandir : la marge négative le remonte à 6px sous la bordure, et
   c'est ce qui le rattache visuellement au champ qu'il commente plutôt qu'au
   champ suivant. */
.aj_form_err {
  display: block;
  margin: -18px 0 14px;
  font-size: 13px;
  line-height: 20px;
  color: var(--ajError);
}

/* Tant qu'il n'y a rien à dire, l'élément ne prend pas de place : les
   messages sont écrits dans un <span> déjà présent dans le HTML, pour que le
   lecteur d'écran l'ait déjà associé au champ quand le texte arrive. Un
   élément créé au moment de l'erreur n'est pas annoncé. */
.aj_form_err:empty {
  display: none;
}

.tf__single_form input.is_invalid,
.tf__single_form textarea.is_invalid {
  border-color: var(--ajError);
}

.tf__single_form input:focus-visible,
.tf__single_form textarea:focus-visible {
  outline: 2px solid var(--colorPrimary);
  outline-offset: 2px;
}

.aj_form_status {
  margin: 16px 0 0;
  font-size: 14px;
  line-height: 23px;
  color: var(--ajMuted);
}

.aj_form_status:empty {
  display: none;
}

.aj_form_status_error {
  color: var(--ajError);
}

.tf__form button[disabled] {
  opacity: 0.6;
  cursor: progress;
}

/* L'accusé de réception remplace le formulaire, il ne s'ajoute pas dessous.
   Un formulaire encore rempli et encore actif après un envoi réussi est une
   invitation au double envoi, et la moitié des gens le font. */
.aj_form_done {
  display: flex;
  align-items: flex-start;
  gap: 18px;
  padding: 34px 32px;
  background: var(--ajPanel);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-left: 3px solid var(--colorPrimary);
}

.aj_form_done i {
  flex: 0 0 auto;
  font-size: 26px;
  line-height: 1.15;
  color: var(--colorPrimary);
}

.aj_form_done p {
  margin: 0;
  font-size: 16px;
  line-height: 27px;
}

/* Les deux colonnes de la section « Qui je suis » passent l'une sous l'autre
   en dessous de 992px. La note en marge de la colonne de gauche venait alors
   se coller au filet supérieur de la fiche « l'essentiel », sans rien pour
   signaler qu'on change de bloc. */
@media (max-width: 991.98px) {
  .tf__about > .container > .row > [class*="col-"] + [class*="col-"] {
    margin-top: 44px;
  }
}

@media (max-width: 991px) {
  /* Sélecteur doublé pour repasser devant le bloc 1199.98px ci-dessus, qui
     porte déjà deux classes. */
  .tf__banner_img.aj_hero_photo {
    max-width: 300px;
    margin: 0 auto 50px;
    /* Le tampon était collé au bord droit de la photo (`right: 0`) pendant
       que le cadre, lui, débordait toujours de 22px : les deux ne se
       superposaient plus. On garde un décalage, simplement plus court. */
    --ajHeroOffset: 18px;
  }
  .aj_kpi h4 {
    font-size: 40px;
  }
  .aj_faq_q {
    font-size: 18px;
    line-height: 28px;
  }
}

/* ==========================================================================
   TÉLÉPHONES
   Deux paliers : 576px couvre tous les téléphones actuels en portrait,
   400px vise les écrans plus étroits et les appareils plus anciens encore
   très répandus (iPhone SE et 8 à 375px, Galaxy S8/S9 à 360px, et jusqu'à
   320px pour les plus vieux).
   ========================================================================== */
@media (max-width: 575.99px) {
  /* Le thème masque purement et simplement le portrait sous 576px. Sur un
     site de candidature c'est une erreur : la plupart des recruteurs
     ouvriront le lien depuis LinkedIn ou un e-mail, donc sur téléphone,
     et le visage est précisément ce qui crée la confiance. On le remet,
     plus petit, au-dessus du texte. */
  .tf__banner_img.aj_hero_photo {
    display: block !important;
    max-width: 230px;
    margin: 0 auto 42px;
  }

  /* Le débord de 22px respire sur grand écran ; sur 375px il vient toucher
     le bord de l'écran. On réduit le réglage, le cadre et le tampon suivent
     ensemble. */
  .tf__banner_img.aj_hero_photo {
    --ajHeroOffset: 16px;
  }

  .aj_stamp {
    max-width: 170px;
    font-size: 10px;
    line-height: 15px;
    padding: 9px 13px;
  }

  /* 165px de marge haute laissaient un grand vide avant le portrait. */
  .tf__banner {
    padding-top: 118px !important;
    padding-bottom: 70px !important;
  }

  /* Les deux boutons prenaient des largeurs différentes selon leur texte. */
  .tf__banner_text ul {
    margin-top: 34px;
  }

  .tf__banner_text ul li {
    width: 100%;
    margin-right: 0;
    margin-bottom: 14px;
  }

  .tf__banner_text ul li .tf__common_btn {
    width: 100%;
    justify-content: center;
    text-align: center;
  }

  /* 48px de titre sur un écran de 375px, c'est trois mots par ligne. */
  .tf__banner_text h1 {
    font-size: 34px !important;
    line-height: 42px !important;
  }

  .tf__banner_text h3 {
    font-size: 19px !important;
  }

  .tf__common_heading h2 {
    font-size: 28px !important;
    line-height: 36px !important;
  }

  .aj_lead {
    font-size: 16px;
    line-height: 28px;
  }

  /* Fiche « l'essentiel » : libellé au-dessus de la valeur, sinon la
     colonne de gauche mange toute la largeur. */
  .aj_facts {
    padding: 26px 20px 24px;
  }

  .aj_fact_list > div {
    display: block;
    padding: 12px 0;
  }

  .aj_fact_list dt {
    margin-bottom: 3px;
    font-size: 12px;
  }

  .aj_fact_list dd {
    font-size: 15px;
    line-height: 24px;
  }

  /* L'intitulé passe sous la période : la réserve horizontale n'a plus lieu
     d'être, c'est un petit interligne qui les sépare. */
  .aj_ladder span {
    min-width: 0;
    padding-right: 0;
    margin-bottom: 5px;
  }

  .aj_ladder b {
    display: block;
    font-size: 15px;
  }

  .aj_facts_cta {
    gap: 14px;
  }

  .aj_facts_cta .tf__common_btn {
    width: 100%;
    text-align: center;
  }

  .aj_card {
    padding: 28px 22px 24px;
  }

  .aj_vs > div {
    padding: 22px 20px;
  }

  .aj_ref {
    padding: 24px 20px;
  }

  /* Quinze caractères à espacement fixe dans une carte de téléphone : à
     15px il ne restait que neuf pixels de marge, et la police de repli
     n'est pas la même d'un appareil à l'autre. On resserre. */
  .aj_ref .aj_cipher {
    font-size: 13px;
    letter-spacing: 0.3px;
  }

  /* Le chronomètre de la version courte tenait sur une seule ligne. */
  .aj_watch {
    flex-wrap: wrap;
    padding: 12px 16px;
  }

  .aj_watch_label {
    flex: 1 0 100%;
    font-size: 12px !important;
    line-height: 18px;
  }

  /* Une adresse e-mail ne se coupe pas toute seule et débordait. */
  .aj_contact_line a,
  .aj_contact_line span {
    overflow-wrap: anywhere;
    word-break: break-word;
    font-size: 15px;
  }

  .aj_contact_line {
    align-items: flex-start;
    gap: 12px;
  }

  /* Cibles tactiles : 30px de haut, c'est trop petit pour un pouce. */
  .aj_lang a {
    padding: 11px 16px;
  }

  .aj_note {
    font-size: 14px;
    line-height: 24px;
  }

  .aj_pills li {
    font-size: 13px;
    padding: 8px 13px;
  }
}

@media (max-width: 400px) {
  .tf__banner_text h1 {
    font-size: 29px !important;
    line-height: 37px !important;
  }

  .tf__common_heading h2 {
    font-size: 25px !important;
    line-height: 33px !important;
  }

  .aj_kpi h4,
  .aj_kpi h4 span {
    font-size: 34px;
  }

  .aj_faq_q {
    font-size: 16px;
    line-height: 26px;
  }

  .aj_facts,
  .aj_card {
    padding-left: 17px;
    padding-right: 17px;
  }

  .tf__banner_img.aj_hero_photo {
    max-width: 200px;
    --ajHeroOffset: 10px;
  }

  .aj_stamp {
    max-width: 150px;
  }
}

/* ==========================================================================
   CAS CONCRETS — grille de cartes + fenêtre de lecture
   --------------------------------------------------------------------------
   Aucun jeton de couleur, aucune fonte, aucun rayon nouveau : tout reprend
   ce qui existe déjà (.aj_card, .aj_card_tag, --colorPrimary, --ajPanel).
   La carte est un <button> : il faut donc annuler ce que le navigateur pose
   sur les boutons (fonte système, texte centré, largeur au contenu) avant de
   retrouver l'apparence d'une carte du site.
   ========================================================================== */

/* --- La grille ----------------------------------------------------------
   Cette grille-ci ne passe pas par les colonnes Bootstrap, contrairement au
   reste du site, et c'est la seule exception. Voici pourquoi.

   Une `.row` Bootstrap est un conteneur flex en `wrap`. Chaque ligne de ce
   conteneur se dimensionne indépendamment des autres : les trois cartes
   d'une même ligne s'égalisent bien, mais la ligne du dessous s'égalise sur
   son propre contenu. Avec six cas dont les résumés ne font pas la même
   longueur, on obtenait 512px de haut sur la première ligne et 455px sur la
   seconde. Deux blocs de cartes de tailles différentes empilés, ce qui se
   lit comme une erreur de gabarit et non comme une composition.

   Aucun réglage flex ne corrige ça : en `flex-wrap: wrap`, une ligne ne peut
   pas connaître la hauteur des autres. `grid-auto-rows: 1fr` le fait en un
   mot — toutes les rangées implicites prennent la même hauteur, celle qu'il
   faut à la carte la plus haute de la section entière.

   `gap` remplace au passage le montage d'origine, qui mélangeait deux
   mesures : 24px d'horizontal (la gouttière Bootstrap, moitié à gauche et
   moitié à droite de chaque colonne) et 30px de vertical (la marge basse de
   `.aj_grid_row`). L'écart se voyait. Ici les deux valent 30px.

   La carte pleine largeur qui suit reste, elle, dans une `.row` classique :
   elle est seule sur sa ligne, elle n'a personne avec qui s'égaliser, et sa
   hauteur doit rester celle de son contenu. */
.aj_cs_grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  grid-auto-rows: 1fr;
  gap: 30px;
  /* Le même écart entre la dernière rangée et la carte pleine largeur
     qu'entre deux rangées de la grille. */
  margin-bottom: 30px;
}

/* `minmax(0, 1fr)` ci-dessus et `min-width: 0` ici disent la même chose à
   deux niveaux : une piste de grille refuse par défaut de descendre sous la
   taille minimale de son contenu, et un titre de cas contient des mots
   assez longs pour élargir la colonne au-delà de sa part. Sans ces deux
   réglages, la grille déborde du conteneur au lieu de couper le mot. */
.aj_cs_cell {
  min-width: 0;
}

/* Deux colonnes dès que trois ne tiennent plus, une seule sur téléphone —
   les mêmes seuils que `col-xl-4 col-md-6` sur le reste du site, pour que
   les sections voisines changent de disposition en même temps que celle-ci.

   Sur une seule colonne, on rend leur hauteur naturelle aux cartes :
   « toutes de la hauteur de la plus haute » n'a plus de sens quand aucune
   n'est côte à côte, et ne produirait que du vide sous les plus courtes. */
@media (max-width: 1199.98px) {
  .aj_cs_grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 767.98px) {
  .aj_cs_grid {
    grid-template-columns: minmax(0, 1fr);
    grid-auto-rows: auto;
  }
}

.aj_cs_card {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  width: 100%;
  text-align: left;
  font-family: var(--bodyFont);
  color: var(--colorBodyText);
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
}

/* Le focus clavier doit rester visible. On ne supprime jamais l'anneau : on
   le remplace par un liseré dans la couleur du site, plus lisible sur fond
   sombre que l'anneau par défaut. */
.aj_cs_card:focus-visible,
.aj_cs_close:focus-visible,
.aj_cs_dialog:focus-visible {
  outline: 2px solid var(--colorPrimary);
  outline-offset: 3px;
}

/* Icône et libellé de type sur la même ligne : la carte gagne un repère
   visuel sans gagner une ligne de hauteur. L'icône reprend la taille et la
   couleur de `.aj_card_icon`, en plus petit — c'est un marqueur, pas une
   illustration. */
.aj_cs_head {
  display: flex;
  align-items: center;
  gap: 13px;
  margin-bottom: 18px;
}

.aj_cs_head .aj_card_tag {
  margin-bottom: 0;
}

.aj_cs_icon {
  flex: 0 0 auto;
  font-size: 25px;
  line-height: 1;
  color: var(--colorPrimary);
}

.aj_cs_title {
  display: block;
  font-size: 21px;
  font-weight: 700;
  line-height: 30px;
  color: var(--colorWhite);
  margin-bottom: 14px;
}

.aj_cs_teaser {
  display: block;
  font-size: 16px;
  line-height: 27px;
  margin-bottom: 20px;
}

.aj_cs_chips {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 20px;
}

.aj_cs_chips span {
  font-size: 12px;
  font-weight: 500;
  line-height: 20px;
  color: var(--ajMuted);
  border: 1px solid rgba(255, 255, 255, 0.12);
  background: var(--ajPanel2);
  padding: 4px 11px;
}

/* Poussé en bas de la carte : les six cartes n'ont pas la même hauteur de
   texte, l'appel à l'action doit malgré tout s'aligner d'une carte à l'autre. */
.aj_cs_more {
  display: block;
  margin-top: auto;
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.5px;
  text-transform: uppercase;
  color: var(--colorPrimary);
}

.aj_cs_more i {
  margin-left: 8px;
  transition: transform linear 0.25s;
}

.aj_cs_card:hover .aj_cs_more i {
  transform: translateX(5px);
}

/* --- La carte pleine largeur (mode d'emploi du delivery) ------------------
   Ce n'est pas un septième cas : c'est un autre type de contenu. Le poids
   visuel doit le dire avant que le titre ne le dise, d'où le fond légèrement
   différent, la barre à gauche et la ligne de texte plus large. */
.aj_cs_card_wide {
  background: var(--ajPanel2);
  border-left: 3px solid var(--colorPrimary);
  padding: 40px 40px 36px;
}

.aj_cs_card_wide::after {
  display: none;
}

.aj_cs_card_wide:hover {
  transform: none;
  border-color: var(--ajLine);
  border-left-color: var(--colorPrimary);
}

.aj_cs_card_wide .aj_cs_title {
  font-size: 26px;
  line-height: 36px;
  max-width: 760px;
}

/* Le titre est un titre : il garde une mesure courte, 760px, parce qu'il
   peut passer à la ligne sans dommage.

   L'accroche, elle, n'est pas un paragraphe : c'est une phrase unique, et
   une phrase unique qui se casse en deux sur un écran large donne l'air
   d'un texte trop long pour la boîte alors que la place est là. Elle
   reprend donc la largeur de la carte. La version française fait environ
   940px à 16px, la carte en offre près de 1190 sur écran large : la phrase
   tient sur une ligne, et sous 1200px elle se replie d'elle-même — ce qui
   est le comportement voulu, il n'y a alors plus la place. */
.aj_cs_card_wide .aj_cs_teaser {
  max-width: none;
}

/* Le texte intégral vit dans la page, masqué : la modale le recopie.
   L'attribut `hidden` suffit ; la règle explicite protège des feuilles du
   thème qui redéfinissent `display` sur les div. */
.aj_cs_source[hidden] {
  display: none;
}

.aj_cs_noscript {
  display: block;
  margin-top: 10px;
}

/* --- Fenêtre de lecture --------------------------------------------------
   z-index 999999 : au-dessus du menu fixe (99999) et de l'offcanvas, en
   dessous du curseur personnalisé (1000000) et du preloader, qui doivent
   rester les deux derniers calques du site. */
.aj_cs_modal {
  position: fixed;
  inset: 0;
  z-index: 999999;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 40px 20px;
  opacity: 0;
  transition: opacity linear 0.25s;
}

.aj_cs_modal[hidden] {
  display: none;
}

.aj_cs_modal.is_open {
  opacity: 1;
}

.aj_cs_backdrop {
  position: absolute;
  inset: 0;
  background: rgba(6, 6, 4, 0.88);
  /* La page ne doit pas défiler sous la modale, même au doigt. */
  touch-action: none;
}

.aj_cs_dialog {
  position: relative;
  width: 100%;
  /* 1120px et non 820 : la fiche a maintenant une colonne de repères à
     gauche. À 820, cette colonne aurait été prise sur la largeur du texte,
     qui serait tombé sous les 50 caractères par ligne — on aurait échangé
     un mur de texte contre une colonne de journal. */
  max-width: 1120px;
  max-height: 100%;
  display: flex;
  flex-direction: column;
  background: var(--ajPanel);
  border: 1px solid rgba(255, 255, 255, 0.1);
  /* Le liseré haut n'est plus peint en pleine couleur : il sert de rail à la
     jauge de lecture, qui le recouvre au fur et à mesure. */
  border-top: 3px solid rgba(199, 211, 0, 0.22);
  transform: translateY(14px);
  transition: transform linear 0.25s;
}

.aj_cs_modal.is_open .aj_cs_dialog {
  transform: translateY(0);
}

/* --- La jauge de lecture -------------------------------------------------
   Posée sur le liseré haut du cadre : `top: -3px` la place exactement sur la
   bordure, et les -1px latéraux rattrapent les bordures gauche et droite,
   sans quoi la jauge s'arrêterait deux pixels avant le coin.

   Le script écrit `scaleX()` et rien d'autre. Une jauge dont on animerait la
   largeur ferait recalculer la mise en page à chaque image de défilement,
   pendant que le visiteur fait défiler — le pire moment pour ça. */
.aj_cs_progress {
  position: absolute;
  inset: -3px -1px auto;
  height: 3px;
  background: var(--colorPrimary);
  transform: scaleX(0);
  transform-origin: 0 50%;
  z-index: 3;
}

/* La barre de défilement du système, sur Windows, mesure une quinzaine de
   pixels et se dessine en gris clair : dans une fiche sombre, c'est la
   première chose que l'œil attrape. On l'affine et on la repeint.

   Deux mécanismes, et c'est volontaire. `scrollbar-width` / `scrollbar-color`
   sont les propriétés standard : elles donnent une barre d'une dizaine de
   pixels aux couleurs du site sur Chrome, Edge, Firefox et Safari récent.
   Les pseudo-éléments `::-webkit-scrollbar` ne servent que de repli pour les
   navigateurs qui ignorent encore les premières (Safari avant 18.2) — dès
   qu'un moteur comprend `scrollbar-color`, il désactive ces pseudo-éléments
   et c'est la voie standard qui s'applique. Aucun des deux n'est nécessaire
   au fonctionnement : au pire, la barre reste celle du système. */
.aj_cs_scroll {
  overflow-y: auto;
  overscroll-behavior: contain;
  /* Aucune marge intérieure ici : le bandeau de titre et le pied de fiche
     vont d'un bord à l'autre du cadre, et ce sont les blocs eux-mêmes qui
     portent leur retrait. */
  padding: 0;
  scrollbar-width: thin;
  scrollbar-color: rgba(199, 211, 0, 0.35) transparent;
}

.aj_cs_scroll::-webkit-scrollbar {
  width: 6px;
}

.aj_cs_scroll::-webkit-scrollbar-track {
  background: transparent;
}

.aj_cs_scroll::-webkit-scrollbar-thumb {
  background: rgba(199, 211, 0, 0.35);
  border-radius: 3px;
}

.aj_cs_scroll::-webkit-scrollbar-thumb:hover {
  background: var(--colorPrimary);
}

/* Le bouton est calé sur le cadre, pas sur le contenu : il reste en place
   pendant que le texte défile dessous. D'où le fond opaque — sans lui, les
   lignes passent au travers de la croix et on ne la distingue plus. */
.aj_cs_close {
  position: absolute;
  top: 0;
  right: 0;
  z-index: 2;
  width: 52px;
  height: 52px;
  font-size: 20px;
  color: var(--colorBodyText);
  background: var(--ajPanel);
  border-bottom: 1px solid rgba(255, 255, 255, 0.08);
  border-left: 1px solid rgba(255, 255, 255, 0.08);
  cursor: pointer;
  transition: color linear 0.25s;
}

.aj_cs_close:hover {
  color: var(--colorPrimary);
}

/* Le libellé du bouton de fermeture : lu par les lecteurs d'écran, invisible
   à l'œil. Pas de `display:none`, qui le retirerait aussi de la lecture. */
.aj_cs_sr {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* --- Le bandeau de titre ------------------------------------------------
   Ce qu'il fait, avant d'être joli : il annonce le prix d'entrée. Type de
   situation, titre, nombre de parties, temps de lecture. Un visiteur décide
   de lire ou de fermer en connaissance de cause, au lieu de commencer à
   l'aveugle et d'abandonner à la troisième section.

   Il va d'un bord à l'autre et porte sa propre couleur : c'est ce qui coupe
   la fiche en deux au premier coup d'œil, un en-tête puis un corps, plutôt
   qu'un seul bloc de texte qui commence en haut et finit en bas.

   Le dégradé reprend la composition du bandeau d'accueil, dans les mêmes
   couleurs et à des intensités plus basses : la fiche a l'air de venir de
   la page qui l'a ouverte. */
.aj_cs_banner {
  position: relative;
  overflow: hidden;
  padding: 46px 54px 36px;
  background:
    radial-gradient(
      120% 190% at 6% 0%,
      rgba(199, 211, 0, 0.17),
      rgba(199, 211, 0, 0) 58%
    ),
    radial-gradient(
      90% 160% at 92% 8%,
      rgba(55, 195, 62, 0.12),
      rgba(55, 195, 62, 0) 62%
    ),
    var(--ajPanel2);
  border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}

/* L'icône du cas, en très grand et à 7% d'opacité, à moitié sortie du cadre.
   C'est la seule image de la fiche, et elle ne prétend rien : une photo de
   bureau prise dans une banque d'images raconterait un client qui n'existe
   pas. Celle-ci ne fait que donner au bandeau quelque chose à regarder et
   rendre les sept fiches distinguables l'une de l'autre du premier regard. */
.aj_cs_banner_icon {
  position: absolute;
  right: -20px;
  top: 50%;
  transform: translateY(-50%);
  font-size: 190px;
  line-height: 1;
  color: var(--colorPrimary);
  opacity: 0.07;
  pointer-events: none;
}

/* Le texte repasse devant l'icône, et s'arrête avant elle. */
.aj_cs_banner_in {
  position: relative;
  max-width: 720px;
}

.aj_cs_type {
  margin-bottom: 0;
}

.aj_cs_banner h2 {
  font-size: 34px;
  font-weight: 700;
  line-height: 46px;
  color: var(--colorWhite);
  margin: 18px 0 0;
}

/* Nombre de parties et temps de lecture, séparés par un point dessiné et non
   par un caractère : un « · » entre deux <span> aurait dû vivre dans le
   script, donc être traduit, alors qu'il ne se lit pas. */
.aj_cs_meta {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 22px;
  margin: 18px 0 0;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  color: var(--ajMuted);
}

/* `font-size` et `color` explicites, et non hérités : le thème pose
   `p, span { font-size: 16px; color: var(--colorBodyText) }` sur tous les
   <span> du site. Sans ces deux lignes, les deux mentions repasseraient en
   16px et en gris clair au milieu d'une ligne réglée à 12px. */
.aj_cs_meta span {
  position: relative;
  font-size: inherit;
  color: inherit;
}

.aj_cs_meta span + span::before {
  content: "";
  position: absolute;
  left: -12px;
  top: 50%;
  width: 3px;
  height: 3px;
  margin-top: -1px;
  border-radius: 50%;
  background: var(--colorPrimary);
}

/* --- Corps : colonne de repères + récit ---------------------------------
   La colonne de gauche ne contient rien qui ne soit ailleurs dans la page :
   le contexte, le sommaire, les étiquettes. Elle existe parce qu'un lecteur
   qui descend dans un texte long perd deux choses, où il en est et combien
   il en reste, et que les lui redonner coûte moins cher que de raccourcir le
   texte. */
.aj_cs_layout {
  display: grid;
  grid-template-columns: 236px minmax(0, 1fr);
  gap: 0 48px;
  padding: 42px 54px 46px;
}

/* `align-self: start` n'est pas un détail de finition : un élément de grille
   s'étire par défaut sur toute la hauteur de sa rangée. La colonne mesurerait
   donc exactement la hauteur du récit, et `position: sticky` n'aurait plus
   aucune marge pour faire glisser quoi que ce soit. */
.aj_cs_rail {
  align-self: start;
  position: sticky;
  top: 34px;
  /* Une colonne collante plus haute que ce qu'on voit d'elle est un piège :
     elle se cale en haut, son bas passe sous le cadre, et les étiquettes
     deviennent inatteignables — sur un portable en 1366x768, il s'en faut
     d'une trentaine de pixels. Elle reçoit donc sa propre zone de
     défilement, qui ne sert que dans ce cas-là et reste invisible partout
     ailleurs. */
  max-height: calc(100vh - 150px);
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: rgba(199, 211, 0, 0.35) transparent;
}

.aj_cs_rail h6 {
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 2px;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.38);
  margin: 0 0 13px;
}

.aj_cs_fact {
  border-left: 2px solid var(--ajLine);
  padding-left: 15px;
  margin-bottom: 32px;
}

.aj_cs_fact[hidden] {
  display: none;
}

.aj_cs_fact p {
  font-size: 14px;
  line-height: 24px;
  color: var(--ajMuted);
  margin: 0;
}

/* --- Le sommaire --------------------------------------------------------
   Ce sont des <button> et non des <a href="#...">, pour une raison précise :
   l'adresse de la page porte déjà un hash, `#case/<id>`, qui est ce qui
   maintient la fiche ouverte. Un lien d'ancre l'écraserait, la modale se
   refermerait toute seule au premier saut de section. */
.aj_cs_index ol {
  list-style: none;
  margin: 0;
  padding: 0;
  counter-reset: none;
}

.aj_cs_index li + li {
  margin-top: 2px;
}

.aj_cs_index button {
  display: flex;
  align-items: baseline;
  gap: 10px;
  width: 100%;
  text-align: left;
  background: transparent;
  border: 0;
  border-left: 2px solid rgba(255, 255, 255, 0.1);
  padding: 7px 0 7px 13px;
  font-family: var(--bodyFont);
  font-size: 13.5px;
  line-height: 21px;
  color: var(--ajMuted);
  cursor: pointer;
  transition: color linear 0.2s, border-color linear 0.2s;
}

.aj_cs_index button span {
  flex: 0 0 auto;
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.5px;
  color: rgba(255, 255, 255, 0.28);
}

.aj_cs_index button:hover {
  color: var(--colorWhite);
  border-left-color: var(--ajLine);
}

.aj_cs_index button:focus-visible {
  outline: 2px solid var(--colorPrimary);
  outline-offset: 2px;
}

/* La section en cours de lecture. Le script pose cette classe, en se calant
   sur le quart haut de la fenêtre de lecture (voir js/case-studies.js). */
.aj_cs_index button.is_current {
  color: var(--colorWhite);
  border-left-color: var(--colorPrimary);
}

.aj_cs_index button.is_current span {
  color: var(--colorPrimary);
}

/* --- Une section du récit -----------------------------------------------
   Deux colonnes : le numéro à gauche, le titre et le texte à droite. Le
   numéro n'est pas une décoration — c'est lui qui dit que ces sections se
   suivent dans le temps. Un cas concret est un enchaînement de décisions,
   pas une liste de rubriques, et sept titres empilés sans numéro se lisent
   comme une liste. */
.aj_cs_sec {
  position: relative;
  display: grid;
  grid-template-columns: 42px minmax(0, 1fr);
  column-gap: 20px;
  padding-bottom: 34px;
}

.aj_cs_sec:last-child {
  padding-bottom: 0;
}

/* Le fil qui relie un numéro au suivant. Il s'éteint vers le bas : sans ce
   dégradé, il faudrait le couper net quelque part, et une ligne coupée net
   se lit comme une erreur d'alignement. */
.aj_cs_sec::before {
  content: "";
  position: absolute;
  left: 21px;
  top: 51px;
  bottom: 9px;
  width: 1px;
  background: linear-gradient(
    rgba(199, 211, 0, 0.3),
    rgba(255, 255, 255, 0.04)
  );
}

.aj_cs_sec:last-child::before {
  display: none;
}

.aj_cs_num {
  grid-column: 1;
  grid-row: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 42px;
  height: 42px;
  font-size: 12px;
  font-weight: 800;
  letter-spacing: 0.5px;
  color: var(--colorPrimary);
  background: var(--ajPanel2);
  border: 1px solid rgba(199, 211, 0, 0.28);
}

.aj_cs_sec > h4 {
  grid-column: 2;
  grid-row: 1;
  align-self: center;
  font-size: 13px;
  font-weight: 800;
  letter-spacing: 2px;
  text-transform: uppercase;
  color: var(--colorPrimary);
  margin: 0;
}

.aj_cs_sec_text {
  grid-column: 2;
  margin-top: 16px;
}

.aj_cs_modal_body p {
  font-size: 17px;
  line-height: 30px;
}

.aj_cs_modal_body p + p {
  margin-top: 16px;
}

.aj_cs_modal_body strong {
  color: var(--colorWhite);
  font-weight: 600;
}

/* --- Les trois encadrés -------------------------------------------------
   Ce que ça a donné, ce que j'en retiens, et les chiffres. Ce sont les trois
   choses qu'on cherche dans un cas concret, et ce sont exactement celles qui
   se perdent quand elles sont mises en page comme le reste.

   Sortir une section du flux ne fonctionne que si c'est rare : trois types
   d'encadrés au maximum, et jamais plus de deux dans une même fiche.
   Au-delà, plus rien ne ressort puisque tout ressort. C'est aussi pourquoi
   le fichier de contenu doit le déclarer explicitement (voir `accent` dans
   js/caseStudies.js) : un encadré est une décision éditoriale.

   Les trois prennent le jaune de la marque. La leçon a d'abord été essayée
   en vert secondaire, pour la distinguer du résultat : deux accents dans une
   même fiche, ça fait deux marques à interpréter là où le titre de la
   section dit déjà laquelle est laquelle. La couleur ne servait plus à
   distinguer, seulement à occuper. */
.aj_cs_sec_outcome .aj_cs_sec_text,
.aj_cs_sec_lesson .aj_cs_sec_text,
.aj_cs_sec_metric .aj_cs_sec_text {
  padding: 24px 28px;
  background: var(--ajPanel2);
  border: 1px solid rgba(255, 255, 255, 0.07);
  border-left: 3px solid var(--colorPrimary);
}

/* Le numéro se remplit sur les sections encadrées : le repère se voit depuis
   la colonne de gauche, avant même d'arriver au texte. */
.aj_cs_sec_outcome .aj_cs_num,
.aj_cs_sec_lesson .aj_cs_num,
.aj_cs_sec_metric .aj_cs_num {
  color: var(--colorBlack);
  background: var(--colorPrimary);
  border-color: var(--colorPrimary);
}

/* Le chiffre sorti de son titre. « Retention: 95% » se lisait à la vitesse
   d'une phrase ; « 95% » se lit à la vitesse d'un chiffre. Le paragraphe qui
   suit dit toujours comment il a été mesuré — un chiffre sans sa méthode
   n'engage personne, et c'est précisément ce que la page reproche aux CV. */
.aj_cs_figure {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0 14px;
  margin: 0 0 16px;
}

.aj_cs_figure b {
  font-size: 46px;
  font-weight: 800;
  line-height: 1;
  letter-spacing: -1px;
  color: var(--colorPrimary);
}

.aj_cs_figure span {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  color: var(--ajMuted);
}

.aj_cs_modal_chips {
  margin: 32px 0 0;
  padding-top: 26px;
  border-top: 1px solid rgba(255, 255, 255, 0.09);
}

/* --- Le pied de fiche ---------------------------------------------------
   Il enchaîne sur un autre cas au lieu de renvoyer à la grille. Quelqu'un
   qui vient d'en lire un est la personne la plus susceptible d'en lire un
   second ; lui faire refermer la fenêtre, retrouver sa place dans la page et
   viser une autre carte, c'est lui donner trois occasions de s'arrêter là.

   Le trait entre les deux boutons est le fond qui passe à travers le `gap` :
   une bordure aurait dû être posée d'un côté seulement, donc gérée à part
   quand ils s'empilent sur téléphone. */
.aj_cs_footer {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1px;
  background: rgba(255, 255, 255, 0.08);
  border-top: 1px solid rgba(255, 255, 255, 0.08);
}

.aj_cs_nav {
  display: block;
  width: 100%;
  text-align: left;
  padding: 24px 30px 26px;
  background: var(--ajPanel);
  border: 0;
  font-family: var(--bodyFont);
  cursor: pointer;
  transition: background linear 0.25s;
}

.aj_cs_nav:hover {
  background: var(--ajPanel2);
}

.aj_cs_nav:focus-visible {
  outline: 2px solid var(--colorPrimary);
  outline-offset: -4px;
}

.aj_cs_nav_next {
  text-align: right;
}

.aj_cs_nav_lbl {
  display: flex;
  align-items: center;
  gap: 9px;
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  color: var(--colorPrimary);
  margin-bottom: 9px;
}

/* Le script écrit l'icône en premier dans les deux boutons. `row-reverse` la
   renvoie à droite pour « suivant », et la colle au bord droit puisque, sur
   un axe inversé, `flex-start` est ce bord-là. */
.aj_cs_nav_next .aj_cs_nav_lbl {
  flex-direction: row-reverse;
}

.aj_cs_nav_ttl {
  display: block;
  font-size: 15px;
  line-height: 24px;
  color: var(--colorWhite);
}

/* --- Verrouillage du défilement de la page -------------------------------
   Verrouiller, c'est passer la page en `overflow: hidden`, donc supprimer sa
   barre de défilement. La fenêtre gagne alors sa largeur — une quinzaine de
   pixels — et tout ce qui est calé à droite en position fixed (le menu, le
   bouton de retour en haut) se déplace d'autant.

   `scrollbar-gutter: stable` réserve cette gouttière en permanence : elle ne
   disparaît plus à l'ouverture, donc plus rien ne bouge, et il n'y a aucun
   décalage à compenser après coup. C'est la seule solution qui protège aussi
   les éléments fixes, qu'une compensation sur <body> ne touche pas.

   La règle suivante ne sert que de filet pour les navigateurs qui ignorent
   `scrollbar-gutter` (Safari avant 18.2) : le script n'y renseigne `--ajSbw`
   que dans ce cas, et la variable vaut 0 partout ailleurs. */
html {
  scrollbar-gutter: stable;
}

html.aj_cs_locked {
  overflow: hidden;
}

html.aj_cs_locked body {
  padding-right: var(--ajSbw, 0px);
}

html.aj_cs_locked .tf__main_menu {
  right: var(--ajSbw, 0px);
}

@media (prefers-reduced-motion: reduce) {
  .aj_cs_modal,
  .aj_cs_dialog,
  .aj_cs_more i {
    transition: none;
  }

  .aj_cs_dialog {
    transform: none;
  }
}

/* En dessous de 992px, la colonne de repères ne peut plus tenir à côté du
   récit sans étrangler l'un des deux. Elle repasse au-dessus, et perd son
   `sticky` : collée en haut d'une colonne unique, elle recouvrirait le texte
   qu'elle est censée aider à lire.

   Le sommaire reste, lui. C'est le dispositif qui sert le plus sur petit
   écran, celui où l'on voit le moins de choses à la fois. */
@media (max-width: 991.98px) {
  .aj_cs_card_wide {
    padding: 34px 30px 30px;
  }

  .aj_cs_card_wide .aj_cs_title {
    font-size: 22px;
    line-height: 32px;
  }

  .aj_cs_banner {
    padding: 40px 34px 30px;
  }

  .aj_cs_banner h2 {
    font-size: 27px;
    line-height: 38px;
  }

  .aj_cs_banner_icon {
    font-size: 150px;
  }

  .aj_cs_layout {
    grid-template-columns: minmax(0, 1fr);
    gap: 0;
    padding: 34px 34px 40px;
  }

  .aj_cs_rail {
    position: static;
    max-height: none;
    overflow: visible;
    padding-bottom: 32px;
    margin-bottom: 34px;
    border-bottom: 1px solid rgba(255, 255, 255, 0.09);
  }

  .aj_cs_modal_chips {
    margin-top: 26px;
    padding-top: 22px;
  }
}

@media (max-width: 575.98px) {
  .aj_cs_modal {
    padding: 0;
  }

  .aj_cs_dialog {
    max-width: none;
    height: 100%;
    max-height: 100%;
    border-left: 0;
    border-right: 0;
  }

  .aj_cs_banner {
    padding: 34px 20px 26px;
  }

  .aj_cs_banner h2 {
    font-size: 23px;
    line-height: 33px;
  }

  .aj_cs_banner_icon {
    font-size: 115px;
    right: -14px;
  }

  .aj_cs_layout {
    padding: 28px 20px 34px;
  }

  /* Numéro plus petit et gouttière plus étroite : à 42px de badge et 20px de
     gouttière, le texte perdait 62 des 320px de large d'un téléphone. */
  .aj_cs_sec {
    grid-template-columns: 32px minmax(0, 1fr);
    column-gap: 14px;
    padding-bottom: 30px;
  }

  .aj_cs_num {
    width: 32px;
    height: 32px;
    font-size: 11px;
  }

  .aj_cs_sec::before {
    left: 16px;
    top: 41px;
  }

  .aj_cs_sec_outcome .aj_cs_sec_text,
  .aj_cs_sec_lesson .aj_cs_sec_text,
  .aj_cs_sec_metric .aj_cs_sec_text {
    padding: 18px 18px 20px;
  }

  .aj_cs_figure b {
    font-size: 38px;
  }

  /* Les deux boutons de fiche s'empilent : « suivant » repasse à gauche comme
     « précédent », sans quoi un bouton pleine largeur porterait son texte
     contre le bord droit sans raison. */
  .aj_cs_footer {
    grid-template-columns: minmax(0, 1fr);
  }

  .aj_cs_nav {
    padding: 20px 20px 22px;
  }

  .aj_cs_nav_next {
    text-align: left;
  }

  .aj_cs_nav_next .aj_cs_nav_lbl {
    flex-direction: row;
  }

  .aj_cs_modal_body p,
  .aj_cs_teaser {
    font-size: 16px;
    line-height: 28px;
  }

  .aj_cs_card_wide {
    padding: 30px 17px 26px;
  }
}

/* ==========================================================================
   PAGE FREELANCE : enchaînement des formats + bloc technique
   --------------------------------------------------------------------------
   Ces classes ne servent que sur /fr/freelance et /en/freelance. Elles
   reprennent les jetons du site, sans en introduire de nouveaux.
   ========================================================================== */

/* --- Enchaînement des formats -------------------------------------------
   Les trois formats ne sont pas trois options interchangeables : ils se
   suivent. Beaucoup de missions commencent par un audit et s'arrêtent là,
   d'autres continuent. La flèche dit cette progression mieux qu'un
   paragraphe, et la bordure haute se charge en couleur à mesure que
   l'engagement augmente. */
.aj_flow {
  display: flex;
  align-items: stretch;
}

.aj_flow_step {
  flex: 1 1 0;
  position: relative;
  padding: 42px 28px 30px;
  background: var(--ajPanel);
  border: 1px solid rgba(255, 255, 255, 0.07);
  border-top: 3px solid rgba(199, 211, 0, 0.22);
  transition: border-color linear 0.3s, transform linear 0.3s;
}

.aj_flow_step.is_2 {
  border-top-color: rgba(199, 211, 0, 0.55);
}

.aj_flow_step.is_3 {
  border-top-color: var(--colorPrimary);
}

.aj_flow_step:hover {
  transform: translateY(-6px);
  border-color: var(--ajLine);
}

.aj_flow_step:hover.is_1 {
  border-top-color: rgba(199, 211, 0, 0.22);
}

.aj_flow_step:hover.is_2 {
  border-top-color: rgba(199, 211, 0, 0.55);
}

.aj_flow_step:hover.is_3 {
  border-top-color: var(--colorPrimary);
}

/* Le numéro chevauche la bordure haute : il ancre l'étape sans consommer
   une ligne de texte. */
.aj_flow_num {
  position: absolute;
  top: -17px;
  left: 28px;
  width: 32px;
  height: 32px;
  line-height: 32px;
  text-align: center;
  font-size: 14px;
  font-weight: 800;
  color: var(--colorBlack);
  background: var(--colorPrimary);
}

.aj_flow_step h4 {
  font-size: 20px;
  font-weight: 700;
  margin-bottom: 8px;
}

.aj_flow_meta {
  display: block;
  font-size: 12px;
  font-weight: 800;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  color: var(--colorPrimary);
  margin-bottom: 20px;
}

.aj_flow_step ul li {
  position: relative;
  padding-left: 20px;
  font-size: 15px;
  line-height: 26px;
  color: var(--colorBodyText);
  margin-bottom: 9px;
}

.aj_flow_step ul li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 12px;
  width: 9px;
  height: 1px;
  background: var(--colorPrimary);
}

.aj_flow_step ul li:last-child {
  margin-bottom: 0;
}

.aj_flow_arrow {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 16px;
  font-size: 17px;
  color: rgba(199, 211, 0, 0.55);
}

@media (max-width: 991.98px) {
  .aj_flow {
    flex-direction: column;
  }

  .aj_flow_arrow {
    padding: 16px 0;
    transform: rotate(90deg);
  }
}

@media (max-width: 575.98px) {
  .aj_flow_step {
    padding: 38px 20px 26px;
  }

  .aj_flow_num {
    left: 20px;
  }
}

/* --- Bloc « je viens du code » -------------------------------------------
   Une ligne de temps compacte : quatre postes, de développeur à manager.
   Sur la page candidature, le parcours occupe une section entière ; ici il
   ne sert qu'à établir une chose, que la personne qui pilotera votre projet
   a écrit du code pendant sept ans. */
.aj_track {
  position: relative;
  padding-left: 26px;
}

.aj_track::before {
  content: "";
  position: absolute;
  left: 4px;
  top: 6px;
  bottom: 6px;
  width: 1px;
  background: rgba(199, 211, 0, 0.28);
}

.aj_track_item {
  position: relative;
  padding-bottom: 26px;
}

.aj_track_item:last-child {
  padding-bottom: 0;
}

.aj_track_item::before {
  content: "";
  position: absolute;
  left: -26px;
  top: 7px;
  width: 9px;
  height: 9px;
  background: var(--colorPrimary);
}

.aj_track_item b {
  display: block;
  font-size: 12px;
  font-weight: 800;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  color: var(--colorPrimary);
  margin-bottom: 4px;
}

.aj_track_item h6 {
  font-size: 17px;
  font-weight: 700;
  color: var(--colorWhite);
  margin-bottom: 6px;
}

.aj_track_item p {
  font-size: 15px;
  line-height: 26px;
  margin: 0;
}

/* --- Profils de clients --------------------------------------------------
   Volontairement plus sobre que `.aj_card` : c'est une liste de situations,
   pas une offre. */
.aj_profile {
  height: 100%;
  padding: 28px 26px;
  background: var(--ajPanel2);
  border: 1px solid rgba(255, 255, 255, 0.07);
}

.aj_profile i {
  display: block;
  font-size: 22px;
  line-height: 1;
  color: var(--colorPrimary);
  margin-bottom: 16px;
}

.aj_profile h4 {
  font-size: 17px;
  font-weight: 700;
  line-height: 26px;
  margin-bottom: 10px;
}

.aj_profile p {
  font-size: 15px;
  line-height: 26px;
  margin: 0;
}

/* ==========================================================================
   BANDEAU : le fond liquide
   --------------------------------------------------------------------------
   Trois couches, et une idée en une phrase : des disques de couleur pleine
   qui rebondissent sur les bords, et une vitre floutée posée par-dessus.

     1. une nappe de dégradés fixe          (CSS, `.aj_hero_anim::before`)
     2. sept disques qui rebondissent        (posés par js/aj-hero.js)
     3. une vitre en `backdrop-filter`       (idem)

   C'est le flou de la couche 3 qui fait tout le travail. Un disque de 500px
   flouté à 140px n'a plus de bord : il ne reste qu'un halo. Deux halos qui
   se recouvrent fusionnent en une seule forme, s'étirent quand ils se
   séparent, se coupent en deux quand ils s'écartent. Aucune de ces formes
   n'est décrite nulle part — elles sortent du recouvrement, et c'est
   exactement de cette façon qu'un liquide se comporte.

   Ce que ça remplace : un réseau de nœuds reliés dessiné au canevas, et deux
   nappes de dégradés qui tournaient l'une sur l'autre. Les deux racontaient
   « plateforme d'entreprise » ; celle-ci ne raconte rien et se regarde mieux.

   --- pourquoi une vitre plutôt qu'un flou sur les disques ----------------
   `filter: blur()` posé sur le conteneur des disques donnerait presque la
   même image, à un défaut près : le flou déborde de la boîte et s'y éteint,
   donc les quatre bords du bandeau perdraient leur couleur et on verrait un
   cadre sombre autour du fond.

   `backdrop-filter` travaille dans l'autre sens : la vitre floute ce qui est
   *derrière elle*, c'est-à-dire les disques **et** le noir du bandeau. Le
   noir participe au calcul jusqu'au ras du bord, il n'y a donc rien à
   éteindre et aucun cadre n'apparaît.

   Le repli `@supports` plus bas couvre les navigateurs qui n'ont pas
   `backdrop-filter` : on y retombe sur le flou classique, cadre compris.
   C'est moins beau et c'est très largement préférable à sept disques
   parfaitement nets en travers du portrait.

   --- lisibilité, et comment les valeurs ont été trouvées -----------------
   Le bandeau ne porte pas que du gros titre : il y a un paragraphe en 16px,
   en rgb(177,177,177). Ce texte-là exige 4,5:1 contre ce qu'il y a derrière,
   et « ce qu'il y a derrière » change en permanence puisque les disques se
   déplacent. Une valeur choisie à l'œil sur une capture ne dit rien du pire
   cas.

   Les opacités ci-dessous ont donc été calculées, pas essayées. On rejoue la
   géométrie du bandeau dans un canevas, on tire soixante dispositions au
   hasard dans les bornes exactes du script, on applique le même flou, puis on
   relève le pixel le plus clair de la zone où le texte est posé. Ce qui en
   sort, pour ces valeurs-ci :

                              texte 16px      titre en jaune (3:1 requis)
       ordinateur              4,77:1                 6,21:1
       tablette                5,16:1                 6,72:1
       téléphone               4,61:1                 6,00:1

   Une première version passait les disques à des opacités deux fois plus
   fortes (0,26 à 0,50) avec un voile à 26%. Le fond était superbe et le
   paragraphe tombait à 2,8:1 : illisible, et invisible sur une capture
   d'écran prise au bon moment. Le fond n'a pas à gagner contre le texte.

   Trois valeurs bougent ensemble et ne doivent jamais être touchées
   séparément : l'opacité des disques, le rayon du flou, et le voile de la
   vitre. Un flou plus court moyenne moins, donc éclaircit le pire cas — c'est
   pour ça que le voile monte à 42% puis 45% à mesure que le rayon descend
   sur les petits écrans.
   ========================================================================== */
.aj_hero_anim {
  position: relative;
  background-color: var(--colorBlack);
  /* Les disques rebondissent sur les bords de la section, mais un disque de
     500px dont le centre longe le bord dépasse forcément. Sans cette coupe,
     la page s'allongerait et défilerait de côté sur téléphone. */
  overflow: hidden;
}

/* --- couche 1 : la nappe fixe -------------------------------------------
   Elle ne bouge pas et ne sert qu'à une chose : que le bandeau reste un
   bandeau quand le JavaScript ne tourne pas. Les couches 2 et 3 sont posées
   par un script ; s'il ne se charge pas, il ne doit pas rester un rectangle
   noir à la place du fond.

   Elle est aussi *derrière* la vitre, donc floutée avec le reste : la
   flouter ne change rien puisqu'un dégradé est déjà lisse, mais ça évite
   qu'elle ne se lise comme une couche à part.

   Intensités volontairement basses. Additionnée aux disques, une nappe à
   pleine force aurait rallumé le problème de lisibilité que le voile de la
   vitre vient de régler. */
.aj_hero_anim::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background:
    radial-gradient(
      58% 68% at 12% 18%,
      rgba(199, 211, 0, 0.09),
      rgba(199, 211, 0, 0) 62%
    ),
    radial-gradient(
      48% 58% at 90% 28%,
      rgba(199, 211, 0, 0.06),
      rgba(199, 211, 0, 0) 60%
    ),
    radial-gradient(
      44% 50% at 60% 74%,
      rgba(55, 195, 62, 0.045),
      rgba(55, 195, 62, 0) 64%
    );
}

/* --- couche 2 : les disques ---------------------------------------------
   Le conteneur, posé par le script. Il n'existe pas dans le HTML des six
   pages : ce sont sept div sans contenu, qui n'ont rien à faire dans le
   document pour un lecteur d'écran ou un moteur de recherche.

   `pointer-events: none` sur toute la pile : rien ici ne peut intercepter un
   clic destiné au bouton « Télécharger le CV ». */
/* Pas d'`opacity` ici, et c'est une contrainte, pas un oubli. Une opacité
   inférieure à 1 sur ce conteneur en ferait une « racine d'arrière-plan » :
   `backdrop-filter` ne verrait plus que ce qui est *à l'intérieur*, donc les
   disques mais pas le noir de la section. La vitre flouterait alors du vide,
   et on retomberait sur le cadre sombre que `backdrop-filter` était
   justement là pour éviter.

   Un fondu d'allumage a été essayé ici et retiré pour cette raison : il
   n'était pas seulement inutile, il changeait le rendu pendant sa durée puis
   le rechangeait en arrivant à 1 — `opacity: 1` ne crée pas de racine, `0.99`
   si. Le préchargeur du thème couvre déjà le moment où les disques se
   placent. */
.aj_hero_blobs {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  overflow: hidden;
}

/* `vmax` et non `vw` : sur un téléphone, le bandeau fait 375 de large pour
   1000 de haut. En `vw`, les disques deviendraient des billes perdues dans
   une colonne ; en `vmax` ils gardent leur taille par rapport au grand côté,
   donc par rapport à ce qu'il y a à remplir.

   `will-change: transform` promeut chaque disque sur le processeur
   graphique. C'est la seule propriété que le script écrit, et il l'écrit
   soixante fois par seconde sur sept éléments. */
.aj_hero_blob {
  position: absolute;
  top: 0;
  left: 0;
  width: 38vmax;
  aspect-ratio: 1;
  border-radius: 50%;
  transform-origin: left top;
  will-change: transform;
}

/* Les couleurs de la page, et rien d'autre : le jaune acide de la marque, le
   vert secondaire, et un jaune très clair qui sert de point chaud — c'est
   lui qui donne au fond son centre lumineux, comme la tache blanche du
   modèle d'origine. */
.aj_hero_blob_1 {
  background: rgba(199, 211, 0, 0.28);
}

.aj_hero_blob_2 {
  background: rgba(199, 211, 0, 0.2);
}

.aj_hero_blob_3 {
  background: rgba(199, 211, 0, 0.14);
  width: 46vmax;
}

.aj_hero_blob_4 {
  background: rgba(55, 195, 62, 0.19);
}

.aj_hero_blob_5 {
  background: rgba(55, 195, 62, 0.13);
  width: 30vmax;
}

/* Le point chaud : plus petit, plus clair, au-dessus des autres. Il peut se
   permettre l'opacité la plus haute des six précisément parce qu'il est le
   plus petit — un disque de 215px passé dans un flou de 140 perd l'essentiel
   de son intensité, là où un disque de 580 garde la sienne en son centre. */
.aj_hero_blob_6 {
  background: rgba(233, 243, 150, 0.23);
  width: 17vmax;
  z-index: 2;
}

/* Et un disque sombre, qui creuse. Sans lui, sept disques clairs finissent
   par saturer le milieu du bandeau et le fond devient une seule flaque. */
.aj_hero_blob_7 {
  background: rgba(9, 10, 6, 0.55);
  width: 40vmax;
  z-index: 3;
}

/* --- couche 3 : la vitre ------------------------------------------------
   140px de flou : c'est la valeur du modèle d'origine, et elle se justifie.
   En dessous de 100 environ, on distingue encore les disques et on lit
   « sept ronds qui bougent » ; au-delà de 160, tout se moyenne et il ne
   reste qu'un aplat uniforme qui ne bouge plus.

   Le fond noir à 26% posé sur la même couche fait office de voile : la vitre
   est déjà là et déjà composée, un élément de plus n'aurait rien apporté. */
/* `z-index: 5`, au-dessus des sept disques. Deux d'entre eux montent à 2 et
   à 3 pour se placer les uns par rapport aux autres ; une vitre à 1 les
   aurait laissés passer devant elle, donc parfaitement nets, et on aurait vu
   deux ronds découpés en travers d'un fond flou. */
.aj_hero_glass {
  position: absolute;
  inset: 0;
  z-index: 5;
  pointer-events: none;
  background: rgba(12, 12, 8, 0.36);
  -webkit-backdrop-filter: blur(140px);
  backdrop-filter: blur(140px);
}

/* Repli pour qui n'a pas `backdrop-filter` (Firefox avant 103, vieux
   Chromium embarqués). On floute alors les disques directement. Le cadre
   sombre décrit en tête de fichier réapparaît sur les bords ; c'est le
   défaut qu'on accepte, et il ne concerne plus grand monde. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .aj_hero_blobs {
    filter: blur(110px);
  }
}

/* Le contenu repasse devant toutes les couches. */
.aj_hero_anim > .container {
  position: relative;
  z-index: 2;
}

/* --- Formats étroits -----------------------------------------------------
   Le flou est l'opération la plus coûteuse de la page et son coût suit la
   surface : le rayon descend donc sur les petits écrans, comme dans le modèle
   d'origine. Mais un flou plus court moyenne moins, donc laisse remonter les
   pointes de luminance — le voile de la vitre monte d'autant, sans quoi le
   paragraphe du bandeau repasserait sous 4,5:1 sur téléphone alors qu'il
   tient sur ordinateur. Les deux valeurs vont ensemble. */
@media (max-width: 1199.98px) {
  .aj_hero_glass {
    background: rgba(12, 12, 8, 0.42);
    -webkit-backdrop-filter: blur(130px);
    backdrop-filter: blur(130px);
  }
}

@media (max-width: 767.98px) {
  .aj_hero_blob {
    width: 52vmax;
  }

  .aj_hero_blob_3 {
    width: 60vmax;
  }

  .aj_hero_blob_5 {
    width: 42vmax;
  }

  .aj_hero_blob_6 {
    width: 24vmax;
  }

  .aj_hero_blob_7 {
    width: 54vmax;
  }

  .aj_hero_glass {
    background: rgba(12, 12, 8, 0.45);
    -webkit-backdrop-filter: blur(110px);
    backdrop-filter: blur(110px);
  }

  @supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
    .aj_hero_blobs {
      filter: blur(85px);
    }
  }
}

/* Immobile pour qui a demandé moins d'animations. Les disques restent
   affichés, à leur position de départ : le script les place puis n'ouvre
   aucune boucle (voir js/aj-hero.js). Un fond fixe plutôt qu'un fond absent,
   parce que « moins de mouvement » ne veut pas dire « moins de page ». */
@media (prefers-reduced-motion: reduce) {
  .aj_hero_blob {
    will-change: auto;
  }
}
