/* ==========================================================
   main.css — socle commun a toutes les pages
   Ordre du fichier :
     1. Variables et typographie
     2. Liens, accessibilite, elements persistants
     3. Medias et couches d'ambiance
     4. Blocs de contenu et boutons
     5. Carrousel des secteurs — comportement en fin de fichier
   
   Regle de partage avec le JavaScript : tout seuil present a la fois en CSS
   et en JS est ecrit une seule fois de chaque cote, a l'identique, et
   surveille par un test (voir le dossier tests/).
   ========================================================== */

/* ==========================================================
   VARIABLES GLOBALES
   ========================================================== */
:root {
  --background-color: #050505;
  --text-color: #ffffff;
  --page-padding: 5vw;
  --aunor-gradient: linear-gradient(120deg, rgba(139, 0, 255, 1) 0%, rgba(255, 0, 84, 0.5) 30%, rgba(0, 122, 255, 0.5) 70%, rgba(0, 255, 255, 1) 100%);

  /* ----------------------------------------------------------
     Course verticale de l'animation d'apparition sur une vignette
     de secteur. Sert à lui réserver la place dans le carrousel à
     défilement (voir .secteur-container en mode compact).

         course = 25 % de la hauteur          [main.js]
                = 25 % × (largeur × 4/3)      [aspect-ratio 3/4]
                = largeur / 3

     On retient la plus large des vignettes du contexte — `.square`
     fait 35vw ici — de sorte que la réserve couvre les deux
     variantes. Sur-réserver est sans effet visible : la marge
     négative l'annule.

     Cette valeur est solidaire du 25 % écrit dans main.js. Les deux
     ne peuvent pas diverger sans que tests/carrousel-test.cjs tombe.
     La déclinaison pour les petits écrans en portrait est dans
     responsive.css, à côté des largeurs auxquelles elle se réfère.
  ---------------------------------------------------------- */
  --secteur-course: calc((35vw - var(--page-padding) / 2) / 3);
}

/* ==========================================================
   TYPOGRAPHIE
   Work Sans en fonte VARIABLE : un seul fichier couvre les graisses 100 a
   900, la ou une famille classique demanderait un telechargement par graisse.
   
   L'echelle .display-* est definie ici en rem et redefinie en unites de
   viewport dans responsive.css.
   ========================================================== */
@font-face {
  font-family: 'Work Sans';
  src: url('../fonts/WorkSans-VariableFont_wght.ttf') format('truetype');
  font-weight: 100 900;
  font-style: normal;
}

html {
  overflow-x: hidden;
  overflow-y: auto;
  font-family: 'Work Sans', sans-serif;
  background-color: var(--background-color);
  color: var(--text-color);
  font-size: 20px;
  letter-spacing: -4%;
}

.filled {
  background-color: var(--background-color);
  padding: 1px 0 15px 0;
}

body {
  margin: 0;
  padding: 0;
}

.display-xl {
  font-size: 4rem;
  line-height: 4rem;
  font-weight: 480;
  margin: unset;
  position: relative;
  z-index: 5;
  max-width: calc(100vw - var(--page-padding) * 2);
}

.display-lg {
  font-size: 2.7rem;
  line-height: 2.7rem;
  font-weight: 480;
  margin: unset;
  position: relative;
  z-index: 5;
  max-width: calc(100vw - var(--page-padding) * 2);
}

.display-lg.mini {
  font-size: 2rem;
  line-height: 2rem;
}

.display-normal {
  font-size: 1rem;
  font-weight: 400;
}

.table .display-normal, .display-normal.maxi {
  font-size: 1.3rem;
}

/* ==========================================================
   LIENS ET ACCESSIBILITE
   L'anneau de focus est retire au clic mais conserve au clavier grace a
   :focus-visible — la navigation au clavier doit rester lisible.
   ========================================================== */
a {
  text-decoration: none;
  color: inherit;
  outline: none;
  -webkit-tap-highlight-color: transparent;
}

a:focus,
a:focus-visible {
  outline: none;
}

a:focus:not(:focus-visible) {
  outline: none;
}

/* ==========================================================
   ELEMENTS D'INTERFACE PERSISTANTS
   Presents sur toutes les pages et places hors du conteneur que le routeur
   remplace : copyright, logo, indicateur de defilement.
   ========================================================== */
#copyright {
  margin: 0;
  padding: 10px 15px;
  font-size: small;
  font-weight: 200;
  letter-spacing: normal;
}

.logo {
  z-index: 11;
  width: 330px;
  position: fixed;
  margin-top: calc(var(--page-padding) / 1.5);
  margin-left: var(--page-padding);
}

.scroll-down-icon.center {
  right: unset;
  left: 50%;
  transform: translateX(-50%);
}

.scroll-down-icon {
  width: 50px;
  position: absolute;
  right: var(--page-padding);
  bottom: calc(var(--page-padding) - 15px);
  z-index: 1;
  opacity: 0;
  animation: scroll-down-icon-fadeIn 0.8s ease 2s forwards;
}

@keyframes scroll-down-icon-fadeIn {
  from {
    opacity: 0;
  }

  to {
    opacity: 1;
  }
}

/* ==========================================================
   MEDIAS ET UTILITAIRES DE MISE EN PAGE
   `pointer-events: none` sur toutes les videos : elles sont decoratives et ne
   doivent jamais capter un clic ni un glisser destine a la page.
   
   RAPPEL IMPORTANT : aucune balise video ne porte `autoplay`. La lecture est
   l'affaire exclusive de js/video-manager.js. Un `autoplay` reintroduit ici ou
   dans un gabarit annulerait tout le chargement differe et ferait remonter la
   memoire de plusieurs centaines de megaoctets.
   ========================================================== */
video {
  pointer-events: none;
}

.full.width {
  width: 100vw;
}

/* ==========================================================
   HAUTEUR DE VIEWPORT SUR MOBILE — pourquoi dvh et pas vh
   -----------------------------------------------------------
   Sur mobile, `100vh` vaut la hauteur de l'ecran barre d'url
   MASQUEE. C'est la plus grande des deux valeurs possibles.
   Un element ancre par `bottom:` dans un conteneur de 100vh se
   retrouve donc SOUS la barre des que celle-ci est visible, et
   devient illisible.

   `100dvh` (dynamic viewport height) suit la barre : le
   conteneur se redimensionne quand elle apparait ou disparait,
   et l'ancrage reste toujours dans la zone visible.

   La declaration en `vh` est conservee juste avant, comme
   repli : un navigateur qui ne connait pas l'unite `dvh` ignore
   la seconde ligne et garde la premiere.

   PERIMETRE VOLONTAIREMENT LIMITE. Seuls les conteneurs qui
   ANCRENT du contenu passent en dvh. Les hauteurs qui fixent
   une LONGUEUR DE DEFILEMENT restent en vh :

     <section style="height: 100vh">    longueur de defilement
     .horizontal-sticky                 course du carrousel

   Les convertir ferait varier la distance de defilement a
   chaque mouvement de la barre, ce qui desynchroniserait les
   sections collees et les animations liees au defilement.
   ========================================================== */

.full.height {
  height: 100vh;
  height: 100dvh;
}

.page-padding {
  padding-left: var(--page-padding);
  padding-right: var(--page-padding);
}

.left-sided-content {
  max-width: 60%;
}

/* ==========================================================
   COUCHES D'AMBIANCE
   Quatre calques fixes superposes a la page, du plus profond au plus proche :
   
     .fallback-bg        fond uni — evite le blanc si une video ou un degrade
                         n'est pas encore charge
     .bottom-blur        flou progressif du bas de l'ecran, masque en degrade
     .shadow             vignettage radial et grain, en soft-light
     .calibrate-shadow   releve les noirs pour eviter l'ecrasement en bas
                         d'echelle
   
   Ce sont des elements plein ecran avec filtre : ils sont volontairement peu
   nombreux, chacun coutant une couche de composition permanente en memoire
   graphique.
   ========================================================== */
.bottom-blur {
  position: fixed;
  z-index: 2;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  inset: 0;

  mix-blend-mode: multiply;
  backdrop-filter: blur(60px);
  -webkit-backdrop-filter: blur(45px) saturate(0.5);

  mask-image: linear-gradient(5deg, rgba(0, 0, 0, 1) 40%, rgba(0, 0, 0, 0) 55%);

  -webkit-mask-image: linear-gradient(5deg, rgba(0, 0, 0, 1) 40%, rgba(0, 0, 0, 0) 55%);
}

/* ==========================================================
   CONTENEUR DE VIDEO DE FOND
   z-index negatif : la video passe sous le contenu textuel sans qu'il faille
   empiler les contextes.
   
   Les .webm ont un canal alpha et se posent sur une image degradee. Ce
   montage est ce qui permet des degrades sans banding : la video seule
   afficherait des bandes visibles dans les aplats sombres.
   ========================================================== */
.background-video {
  position: relative;
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
  overflow: hidden;
  z-index: -1;
}

.background-video video {
  position: relative;
  z-index: 1;
  object-fit: cover;
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
  top: 0;
}

/* ==========================================================
   ASSOMBRISSEMENT PAR DÉFAUT DES VIDÉOS
   Auparavant écrit en style inline sous la forme
   `filter: blur(0px) brightness(0.75)`, et réécrit à chaque
   frame de scroll par main.js.

   Un filtre — même `blur(0px)` — force le navigateur à créer une
   couche de composition de la taille de l'élément. Sur une vidéo
   plein écran cela représente plusieurs dizaines de mégaoctets de
   mémoire GPU, en permanence, pour un effet visuel nul.

   Le brightness vit donc ici, et main.js n'écrit un `filter`
   inline que lorsqu'il y a réellement un flou à appliquer.
========================================================== */

.scroll-opacity video:not(.no-filter),
.video-container video:not(.no-filter),
video.fade-in-out:not(.no-filter) {
  filter: brightness(0.75);
}

.sub-hero-vid {
  filter: brightness(0.8);
}

.fallback-bg {
  background-color: var(--background-color);
  position: fixed;
  z-index: -2;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
}

.shadow {
  background:
    50% 0% / 100% 100% repeat-y radial-gradient(160% 95% at 85% 0%, #05050500 47%, #05050512 53%, #05050582 70%, #050505c7 80%, #050505ed 86%, #050505fa 93%, #050505ff 100%),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.8' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.25'/%3E%3C/svg%3E");

  background-blend-mode: soft-light;

  position: fixed;
  z-index: 2;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  mix-blend-mode: soft-light;
}

.calibrate-shadow {
  background: #050505;
  mix-blend-mode: lighten;
  position: fixed;
  z-index: 2;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
}

/* ==========================================================
   BLOCS DE TEXTE GLISSANTS
   Texte colle en bas du viewport pendant que la video defile derriere lui.
   flex-direction: column-reverse ancre le contenu au bas de la boite quelle
   que soit sa hauteur.
   ========================================================== */
section.menu-slide-video .slipper,
.slipper {
  position: absolute;
}

section.menu-slide-video .slipper.text,
.slipper.text {
  /* dvh : voir la note sur les hauteurs de viewport plus haut. */
  height: 100vh;
  height: 100dvh;
  bottom: var(--page-padding);
  display: flex;
  flex-direction: column-reverse;
}

section.menu-slide-video .slipper .text {
  position: sticky;
  bottom: var(--page-padding);
}

.text {
  z-index: 4;
}

/* ==========================================================
   BOUTONS A CONTOUR DEGRADE
   Le contour est un ::before en degrade que le padding revele au survol : une
   seule couche animee, aucun recalcul de mise en page, et le degrade reste net
   a toute taille — contrairement a une bordure en image.
   ========================================================== */
.btn-gradient {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;

  padding: 3vh 5vh;

  border-radius: 999px;
  text-decoration: none;
  overflow: hidden;
}

.copy-contact {
  padding: 28px 28px;
}

.btn-gradient::before {
  content: "";
  position: absolute;
  inset: 0;
  padding: 5px;
  border-radius: inherit;
  background: var(--aunor-gradient);
  opacity: 0.4;

  -webkit-mask:
    linear-gradient(#fff 0 0) content-box,
    linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;

  transition: padding 200ms cubic-bezier(.27, .5, .34, .98),
    opacity 200ms cubic-bezier(.27, .5, .34, .98);
}

.btn-gradient span {
  position: relative;
  z-index: 1;
  color: #fff;
  font-family: sans-serif;
  font-size: 1.5rem;
  font-weight: 500;
  letter-spacing: normal;
}

.btn-icon {
  max-width: 48px;
  max-height: 48px;

  width: 7vh;
  height: 7vh;

  margin-left: 20px;

  background-image: url('../img/btn-icon.svg');
  background-size: contain;
  background-repeat: no-repeat;
  background-position: center;

  transform: translateX(0px);
  transition: transform 200ms cubic-bezier(.27, .5, .34, .98);
}

.btn-icon.copyContact {
  background-image: url('../img/copy-icon.svg');
  margin: 0;
}

/* ==========================================================
   ETAT ACTIF — survol et equivalent tactile
   `.is-active` est posee par le JavaScript au toucher : sur un ecran tactile,
   :hover reste colle apres le tap, alors qu'une classe peut etre retiree.
   ========================================================== */

.btn-gradient:hover::before,
.btn-gradient.is-active::before {
  padding: 10px;
  opacity: 1;
}

.btn-gradient:hover .btn-icon,
.btn-gradient.is-active .btn-icon {
  transform: translateX(10px);
}

/* Le bouton « copier le contact » ne se decale pas : son icone est un
   symbole d'action, pas une fleche de direction. */
.btn-gradient:hover .btn-icon.copyContact,
.btn-gradient.is-active .btn-icon.copyContact {
  transform: none;
}

/* ==========================================================
   CARROUSEL DES SECTEURS — vignettes
   Les regles de COMPORTEMENT du conteneur (defilement ou translation) sont
   regroupees en fin de fichier. Ici, uniquement la forme des vignettes.
   
   `pointer-events: none` sur .secteur est essentiel : sans lui, le glisser
   demarrerait une selection de texte au lieu d'entrainer le carrousel.
   ========================================================== */
.secteur-container {
  display: flex;
  white-space: nowrap;
  gap: 2.5vw;
  /* Firefox */
  scrollbar-width: none;
}

/* `-webkit-scrollbar: none` figurait ici : ce n'est pas une propriété
   valide et ne masquait donc rien. La règle qui fonctionne sous
   Safari et Chrome est un pseudo-élément. */
.secteur-container::-webkit-scrollbar {
  display: none;
}

.secteur {
  flex: 0 0 auto;
  aspect-ratio: 3 / 4;
  width: calc(25vw - var(--page-padding) / 2);
  line-height: 100px;
  pointer-events: none;
  user-select: none;

  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding: calc(var(--page-padding) / 2);
}

.secteur.square {
  aspect-ratio: 1 / 1;
  width: calc(35vw - var(--page-padding) / 2);
}

.secteur h2,
.secteur p {
  white-space: normal;
  line-height: normal;
}

.horizontal-sticky-content {
  top: 50%;
  transform: translateY(-50%);
  position: relative;

}

.horizontal-sticky {
  position: sticky;
  overflow: hidden;
  min-height: fit-content;
}

/* `width: max-content` et `will-change: transform` etaient poses ici, sans
   condition. Ils sont desormais reserves a l'amelioration « defilement
   vertical lie » (voir la requete media en fin de fichier) : une largeur
   `max-content` empeche le conteneur de defiler, ce qui neutraliserait le
   carrousel classique du mode compact. */

/* ==========================================================
   VIDEO SECONDAIRE D'EN-TETE
   ========================================================== */
.sub-hero-vid,
.sub-hero-vid video {
  height: 55vh;
}

.sub-hero-vid video {
  object-fit: cover;
}

/* ==========================================================
   MODULE CAL, NOTIFICATION ET INFOBULLE
   `.bg-default` force le fond de l'iframe Cal.com, qui arrive en clair par
   defaut. `.toast` est la notification de confirmation apres copie.
   L'infobulle de .copy-contact bascule en position laterale et reste ouverte
   sur mobile — il n'y a pas de survol possible — puis se masque apres le clic.
   ========================================================== */
.bg-default {
  background: #171717 !important;
}

.toast {
  position: fixed;
  bottom: 24px;
  left: 50%;
  transform: translate(-50%, 10px);
  background: #181818;
  color: white;
  padding: 10px 14px;
  border-radius: 8px;
  font-size: 12px;
  z-index: 9999;
  pointer-events: none;
  text-align: center;
  max-width: 80vw;
  line-height: 1.3;

  opacity: 0;
  transition: opacity 300ms ease, transform 300ms ease;
}

.toast.show {
  opacity: 1;
  transform: translate(-50%, 0);
}

.copy-contact {
  position: relative;
  overflow: unset;
}

.copy-contact .tooltip {
  position: absolute;
  left: 50%;
  bottom: calc(100% + 18px);

  transform: translateX(-70%) translateY(8px);
  opacity: 0;
  visibility: hidden;

  background: #181818;
  color: #fff;
  padding: 10px 14px;
  border-radius: 8px;
  white-space: nowrap;
  font-size: 12px !important;
  line-height: 15px;

  pointer-events: none;
  z-index: 1000;

  transition:
    opacity .2s ease,
    transform .2s ease,
    visibility .2s;
}

.copy-contact .tooltip::after {
  content: "";
  position: absolute;
  top: 100%;
  left: calc(50% + 20%);
  transform: translateX(-50%);

  border: 6px solid transparent;
  border-top-color: #181818;
}

.copy-contact:hover .tooltip {
  opacity: 1;
  visibility: visible;
  transform: translateX(-70%) translateY(0);
}

/* --- Declinaison mobile de l'infobulle --- */

.mobile .copy-contact .tooltip {
  left: calc(100% + 12px);
  bottom: 50%;

  transform: translateY(50%);

  opacity: 1;
  visibility: visible;
}

/* La fleche passe sur le flanc gauche. */
.mobile .copy-contact .tooltip::after {
  top: 50%;
  left: -12px;
  transform: translateY(-50%);

  border: 6px solid transparent;
  border-right-color: #181818;
}

/* Etat masque apres le clic : sur mobile l'infobulle est ouverte par
   defaut, il faut donc un moyen explicite de la refermer. */
.mobile .copy-contact.tooltip-hidden .tooltip {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}
/* ==========================================================
   CARROUSEL DES SECTEURS — ÉTAT DE BASE
   -----------------------------------------------------------
   Le carrousel est, par défaut, un simple conteneur à défilement
   horizontal natif. Il fonctionne sans une ligne de JavaScript :
   panoramique tactile avec l'inertie du système, flèches du
   clavier, bornes gérées par le navigateur.

   Le pilotage par le défilement vertical (section allongée,
   contenu collé, translation) est une AMÉLIORATION posée par-dessus
   quand la hauteur du viewport le permet — voir plus bas.

   Pourquoi la molette ne défile pas le carrousel : Lenis est actif
   sur le site avec `smoothWheel: true`, il intercepte l'événement
   `wheel` et l'annule avant que le conteneur ne le voie. La molette
   fait donc défiler la page, jamais le carrousel. Son `syncTouch`
   restant à `false`, le tactile lui reste natif.
========================================================== */

.secteur-container {
  overflow-x: auto;
  /* Le geste horizontal ne doit pas déborder sur l'historique du
     navigateur : sans cela, un balayage vers la droite en butée
     déclenche le retour à la page précédente. */
  overscroll-behavior-x: contain;
  cursor: grab;
}

.secteur-container.is-grabbing {
  cursor: grabbing;
}

/* ==========================================================
   AMÉLIORATION : DÉFILEMENT VERTICAL LIÉ
   -----------------------------------------------------------
   Active au-dessus de 500 px de hauteur ET de 500 px de largeur.
   La section est alors artificiellement allongée, son contenu reste
   collé, et la course verticale fabrique la course horizontale.

   Deux seuils, deux raisons distinctes :

     hauteur   c'est la course verticale qui fabrique le mouvement ;
               sous 500 px il n'y a plus assez de course pour que la
               translation soit lisible.

     largeur   sous 500 px, une seule vignette tient à l'écran. Une
               section allongée obligerait alors à un très long
               défilement vertical pour parcourir six vignettes, là
               où un balayage horizontal direct est immédiat.

   Le conteneur cesse d'être défilable : sa largeur passe en
   `max-content` et la translation prend le relais. Les deux
   mécanismes ne coexistent jamais.

   Le seuil est déclaré une seule fois côté JS (COMPACT_QUERY dans
   main.js) et repris ici à l'identique : les deux ne peuvent pas
   se désynchroniser.
========================================================== */

@media (min-height: 500px) and (min-width: 500px) {
  .secteur-container {
    width: max-content;
    overflow-x: visible;
    will-change: transform;
    cursor: auto;
  }
}

/* ==========================================================
   MODE COMPACT — neutralisation du collage
   -----------------------------------------------------------
   Complémentaire exact de la requête précédente : celle-ci exige
   les DEUX conditions (`and`), celle-ci en suffit d'UNE (virgule).
   Aucun recouvrement, aucun trou.

   Les `!important` sont volontaires et limités à ce bloc : ces
   hauteurs viennent d'attributs `style=` posés dans les gabarits
   (includes/sectors-section.php, approche/index.php), et un style
   inline ne peut être repris autrement.
========================================================== */

@media (max-height: 499px), (max-width: 499px) {
  .horizontal-sticky {
    position: static;
    height: auto !important;
    min-height: 0 !important;
    /* Le conteneur défile lui-même : le rognage du parent n'a plus
       lieu d'être et couperait la fin du carrousel. */
    overflow: visible;
  }

  /* ------------------------------------------------------------
     RÉSERVE POUR L'ANIMATION D'APPARITION

     L'animation décale une vignette de 25 % de sa hauteur vers le
     bas TANT QU'ELLE N'EST PAS ENTRÉE dans le viewport — ce n'est
     pas un dépassement transitoire, c'est un état permanent. Or les
     vignettes d'un carrousel horizontal sont, par construction,
     presque toutes hors champ à un instant donné : leur décalage
     créerait un débordement vertical permanent, et le carrousel
     deviendrait défilable en Y.

     `overflow-y: hidden` supprimerait ce défilement, mais rognerait
     du même coup la partie basse de l'animation. On réserve donc la
     place plutôt que de couper : le décalage tient alors dans la
     boîte de padding — qui n'est ni débordement ni zone défilable —
     et la marge négative annule l'effet sur la mise en page.

     Le rognage d'un conteneur à défilement se fait au bord de la
     boîte de PADDING : ce qui est dans la réserve reste visible.

     Les 12 px supplémentaires couvrent le flou de 10 px de l'état
     initial, qui déborde visuellement sans compter dans la zone
     défilable.
  ------------------------------------------------------------ */
  .secteur-container {
    padding-top: 12px;
    padding-bottom: calc(var(--secteur-course) + 12px);
    margin-top: -12px;
    margin-bottom: calc(-1 * (var(--secteur-course) + 12px));
  }

  /* Le centrage vertical n'a plus de sens hors du mode collé :
     sans ce retour à zéro, le bloc remonterait de la moitié de sa
     propre hauteur et chevaucherait ce qui le précède. */
  .horizontal-sticky-content {
    top: auto;
    transform: none;
  }
}
