/* ==========================================================================
   MAFOTO — L'ouverture                                            [FUEL]
   ==========================================================================
   Relevé sur fuel.framer.website :
     • héros SOMBRE plein cadre ;
     • la section claire suivante est inclinée et tirée par-dessus —
           matrix(1, -0.0586633, 0, 1, 0, -105.5)  =  skewY(-3.36deg)
       c'est son bord haut qui fait la diagonale ;
     • le mot de marque posé en bas, énorme, à cheval sur ce bord.
   ========================================================================== */

.opening {
  position: relative;
  min-height: 100svh;
  display: grid;
  align-content: space-between;
  padding: calc(var(--gutter) + 46px) var(--gutter) 0;
  background: #07070a;
  color: #fff;
  overflow: hidden;
  isolation: isolate;
}

/* --------------------------------------------------------------------------
   0. LE CONTENEUR DE FOND
   Il regroupe vidéo, nappe de secours, voile et grain pour qu'UN SEUL élément
   joue l'entrée « dézoom fondu » de Create (voir css/intro.css).

   `position: absolute` est OBLIGATOIRE, et pas seulement pour la mise en page :
   l'animation pose un `transform` sur ce bloc, ce qui en fait le bloc
   conteneur de tous ses descendants en `position: absolute`. Laissé en
   `static`, il tombait dans la grille de `.opening` avec une hauteur de 0 —
   et la vidéo, calée sur `inset: 0`, mesurait 934 × 0 px. Fond noir.
   -------------------------------------------------------------------------- */
.opening__bg {
  position: absolute;
  inset: 0;
  z-index: -1;
}

/* --------------------------------------------------------------------------
   1. LE FOND VIDÉO
   Les fichiers livrés par Mux sous l'extension `.m3u8` ne sont PAS des
   playlists HLS : leur en-tête est `ftypisom…avc1…mdat`, donc du MP4 mal
   nommé. Renommés en `.mp4`, ils se lisent nativement — ni hls.js, ni appel
   réseau vers Mux, ni facturation à la minute diffusée. Voir README §Vidéo.
   -------------------------------------------------------------------------- */
.opening__video {
  position: absolute;
  inset: 0;
  z-index: -3;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 900ms var(--ease);
}
.opening__video[data-ready='true'] { opacity: 1; }

/* Nappe de secours : visible tant que la vidéo n'est pas prête, et seule
   image de fond si la vidéo est refusée (données limitées, mouvement réduit).

   Elle était en or / rouge brique / bleu, floutée à 80 px : à chaque
   chargement le héros virait à l'orange une demi-seconde avant que la vidéo
   ne prenne la main, et le `blur(80px)` plein écran tournait pour rien
   derrière une vidéo opaque. On garde la palette RÉELLE du film — indigo,
   violet, presque noir — pour que le raccord ne se voie pas. */
.opening__fallback {
  position: absolute;
  inset: 0;
  z-index: -4;
  background:
    radial-gradient(75% 60% at 62% 34%, #241a4d 0%, transparent 68%),
    radial-gradient(65% 55% at 22% 72%, #16215c 0%, transparent 66%),
    #07070a;
}

/* --------------------------------------------------------------------------
   ASSOMBRISSEMENT — localisé, jamais plein cadre.
   La version précédente couvrait toute la surface (55 % en haut, 25 % au
   milieu, 78 % en bas) : le film de synthèse en ressortait gris et éteint,
   c'est ce qui « gâchait le fond vidéo ».

   Relevé sur fuel.framer.website : le héros n'a AUCUN voile. Le texte tient
   parce qu'il est posé là où le média est déjà sombre. On ne peut pas
   entièrement s'offrir ça — notre film s'éclaircit par endroits — donc on
   assombrit UNIQUEMENT les deux bandes qui portent du texte (haut : l'accroche
   et l'en-tête ; bas : le logo), et on laisse le centre intact.
   -------------------------------------------------------------------------- */
.opening__shade {
  position: absolute;
  inset: 0;
  z-index: -2;
  background:
    linear-gradient(180deg, rgba(4, 4, 10, .58) 0%, rgba(4, 4, 10, .14) 20%, rgba(4, 4, 10, 0) 38%),
    linear-gradient(0deg,   rgba(4, 4, 10, .50) 0%, rgba(4, 4, 10, .10) 18%, rgba(4, 4, 10, 0) 36%);
  pointer-events: none;
}

/* Grain fin : sans lui, une vidéo de synthèse fait « fond d'écran ».
   Avec, ça fait pellicule. Baissé de 12 % à 8 % : au-delà, le grain mange les
   dégradés lisses du film et donne un rendu sale plutôt que argentique. */
.opening__grain {
  position: absolute;
  inset: 0;
  z-index: -1;
  opacity: .08;
  pointer-events: none;
  background-image: url("data:image/svg+xml;utf8,\
<svg xmlns='http://www.w3.org/2000/svg' width='140' height='140'>\
<filter id='n'><feTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='3'/></filter>\
<rect width='140' height='140' filter='url(%23n)' opacity='.55'/></svg>");
}

/* --------------------------------------------------------------------------
   2. LA SURFACE D'EAU — RETIRÉE
   Un `feTurbulence` + `feDisplacementMap` était appliqué sur la vidéo plein
   écran. Deux problèmes, tous deux visibles à l'œil nu :

     • le déplacement va CHERCHER des pixels hors du cadre source. Là où il
       n'y en a pas, il ramène du transparent : d'où le liseré noir qui
       ondulait sur les quatre bords du film. C'est ça, « les ombres noires
       qui gâchent le fond vidéo » ;
     • filtrer 1920 × 1080 image par image en SVG coûte plus cher que tout le
       reste de la page réunie, pour un effet que personne ne nomme.

   Le film 3D est DÉJÀ du liquide. Le rider une seconde fois ne le rend pas
   plus liquide, ça le salit. Fuel ne filtre rien : le média est le média.
   Ce qui remplace l'effet : le flou progressif du bas (.veil, css/fx.css),
   qui est ce que Fuel fait réellement.
   -------------------------------------------------------------------------- */

/* --------------------------------------------------------------------------
   3. Bloc de tête
   -------------------------------------------------------------------------- */
.opening__top { display: grid; gap: 18px; max-width: 46ch; }
.opening__claim { font-size: var(--t-lead); line-height: 1.35; color: rgba(255,255,255,.94); }
.opening__claim b { font-weight: var(--w-medium); }
.opening__claim span { color: rgba(255,255,255,.6); }

.opening__cta {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  width: max-content;
  padding-bottom: 10px;
  border-bottom: 1px solid rgba(255,255,255,.45);
  color: #fff;
  font-size: var(--t-lead);
  transition: border-color 320ms var(--ease), gap 320ms var(--ease);
}
.opening__cta:hover { border-color: #fff; gap: 18px; }

.opening__index {
  list-style: none;
  padding: 0;
  display: grid;
  gap: 2px;
  font-size: var(--t-body);
  color: rgba(255,255,255,.88);
  text-shadow: 0 1px 12px rgba(0,0,0,.5);
}
.opening__index li:first-child { color: rgba(255,255,255,.55); }

/* --------------------------------------------------------------------------
   4. LE LOGO ET SON REFLET                                     [REVYME]
   Relevé sur revyme.com, bloc « own the code » : le mot garde sa couleur
   d'origine et un reflet coloré le traverse. Ici le PNG blanc de la marque
   sert de MASQUE — donc aucun texte, aucune police : c'est le vrai logo,
   celui tamponné sur les photos. Le blanc reste le blanc ; seul un reflet
   violet / or / cyan glisse de DROITE à GAUCHE par-dessus.
   -------------------------------------------------------------------------- */
.opening__mark {
  position: relative;
  overflow: hidden;
  width: min(88vw, 1180px);
  aspect-ratio: 900 / 164;              /* dimensions du PNG recadré */
  margin: 0 auto -2.2vw;
  justify-self: center;

  -webkit-mask: url('../assets/img/brand/mafoto-blanc.png') center / contain no-repeat;
          mask: url('../assets/img/brand/mafoto-blanc.png') center / contain no-repeat;

  background: #ffffff;                  /* la couleur d'origine, point */
}

.opening__sweep {
  position: absolute;
  inset: 0;
  background: linear-gradient(96deg,
    transparent 0%,
    rgba(168, 85, 247, .00) 14%,
    rgba(168, 85, 247, .95) 32%,
    rgba(255, 205, 110, .98) 47%,
    rgba(126, 216, 255, .95) 62%,
    transparent 84%);
  transform: translateX(110%);
  animation: mark-sweep 5.4s cubic-bezier(.45, .05, .35, 1) infinite;
}
@keyframes mark-sweep {
  0%   { transform: translateX(110%); }
  70%  { transform: translateX(-110%); }
  100% { transform: translateX(-110%); }
}
@media (prefers-reduced-motion: reduce) { .opening__sweep { animation: none; opacity: 0; } }

.opening__foot {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 24px;
  padding-bottom: 26px;
  color: rgba(255,255,255,.65);
  font-size: var(--t-meta);
  text-transform: uppercase;
  letter-spacing: .06em;
}

/* --------------------------------------------------------------------------
   5. LES COUTURES DIAGONALES
   ==========================================================================
   RÈGLE : une diagonale marque un CHANGEMENT DE COULEUR, jamais un simple
   changement de sujet. C'est ce qui lui donne un sens — sinon c'est un motif
   qu'on répète et qui ne raconte plus rien.

   Concrètement, sur cette page :

       ouverture (sombre)   ──╲── couture
       la Pile (clair)
       Sur le terrain (clair)      pas de couture : même couleur
       les chantiers (sombre) ─╲── couture
       Ce qu'on fabrique (sombre)  pas de couture : déjà sombre
       Parler du projet (clair) ╲── couture

   Trois coutures, trois bascules de couleur. La Pile et Sur le terrain se
   suivent sans rien, parce qu'il ne se passe rien de plus qu'un défilement.

   MISE EN ŒUVRE. Une section qui bascule porte `data-seam` ; elle reçoit un
   `<span class="seam">` en premier enfant. Le mécanisme est celui, déjà réglé,
   de la coupe sous l'ouverture — mêmes variables, même pilote (index.html).
   `.cut` n'est qu'un cas particulier : la première couture de la page.

   Seule une BANDE est inclinée, jamais le contenu. La version d'avant inclinait
   tout le bloc `.cut` puis contre-inclinait ses enfants : un ancêtre transformé
   oblige le navigateur à inverser la transformation pour le test de survol, et
   combiné à la scène `preserve-3d` de la Pile, ça décalait les zones cliquables.
   Ce choix-là ne bouge pas.

   CE QUI EST CORRIGÉ — le raccord.
   La bande pivotait autour de son coin BAS-GAUCHE (`transform-origin: 0 100%`)
   et ne mesurait que sa propre hauteur. En s'inclinant, son bord bas se
   soulevait à droite et découvrait un triangle de vidéo à nu entre la bande et
   le corps clair : c'est le « découpage » sombre visible sous la diagonale, à
   droite, là où se retrouvait « FAITES DÉFILER ».

   Trois changements, et le trou disparaît par construction :
     • la bande fait DEUX fois --cut-h et déborde autant sous le bord qu'au
       dessus : quoi qu'elle fasse en s'inclinant, sa moitié basse reste noyée
       dans le corps clair ;
     • elle pivote autour de son CENTRE, comme chez Fuel — le bord plonge à
       gauche, monte à droite, au lieu de tourner autour d'un coin ;
     • `z-index: -1` : elle passe DERRIÈRE le contenu clair (la Pile, les
       titres) mais reste DEVANT l'ouverture, parce que `.cut` est lui-même un
       contexte d'empilement posé à z-index 2. Sans ça, en descendant sous le
       bord elle repeindrait par-dessus le haut de la Pile.

   Budget de hauteur, à respecter si on retouche l'angle. La bande déborde de
   8 % de chaque côté, donc son demi-écart au centre vaut 0,58 × largeur ; il
   faut que sa demi-hauteur couvre la remontée à cet écart :
       --cut-h  >  0,58 × largeur × tan(angle)   =   0,060 × largeur à −5,9°
   10vw tient jusqu'à 2 600 px ; le plafond est monté à 260 px pour couvrir les
   ultra-larges (3 440 / 3 840), où 200 px laissaient réapparaître le trou.

   L'ANGLE : −5,9°, relevé sur fuel.framer.website —
       transform: translateY(-184.652px) skewY(-5.8753deg)
   Les −3,4° notés ici avant venaient d'une lecture de matrice sur un autre
   bloc ; à cet angle-là, la diagonale ne se lit tout simplement pas.

   LA LEVÉE (--cut-lift) : au repos elle vaut --cut-h, donc la bande est
   entièrement noyée dans le corps clair et le bord est DROIT, à ras du bas de
   l'ouverture — le héros occupe l'écran entier, rien n'est rogné. Elle tombe à
   0 au fil du défilement : la bande remonte et se creuse. C'est index.html qui
   écrit les deux variables.
   -------------------------------------------------------------------------- */
[data-seam] {
  position: relative;
  --cut-h: clamp(96px, 10vw, 260px);
  --cut-skew: 0deg;
  --cut-lift: var(--cut-h);

  /* La couture est en `z-index: -1` : il FAUT un contexte d'empilement sur la
     section, sinon elle remonte dans celui du parent et passe derrière le fond
     des sections. `isolation` en crée un sans toucher à l'ordre des calques. */
  isolation: isolate;
}

.cut__edge,
[data-seam] > .seam {
  position: absolute;
  left: -8%;
  right: -8%;
  top: calc(-1 * var(--cut-h));
  height: calc(var(--cut-h) * 2);
  z-index: -1;
  background: var(--seam-bg, var(--paper));
  transform: translateY(var(--cut-lift)) skewY(var(--cut-skew));
  transform-origin: 50% 50%;
  pointer-events: none;
  will-change: transform;
}

/* `.cut` porte en plus le fond du corps clair et se pose au-dessus de
   l'ouverture — c'est la seule couture qui recouvre une section entière. */
.cut { z-index: 2; background: var(--paper); }

/* Une couture prend la couleur de la section dans laquelle elle ARRIVE :
   c'est le nouveau chapitre qui monte sur l'ancien, pas l'inverse. */
.section--dark { --seam-bg: #08080b; }

/* --------------------------------------------------------------------------
   REPLI TÉLÉPHONE ET TABLETTE
   -------------------------------------------------------------------------- */

/* LE LOGO ÉTAIT COUPÉ, et c'est mesuré : `.opening` porte 16 px de gouttière
   de chaque côté, donc sa boîte de contenu vaut 328 px sur un écran de 360.
   Le logo était posé à `94vw` = 338 px — 10 px de TROP, rognés par le
   `overflow: hidden` du héros. Ce qui disparaissait, c'était le diaphragme, à
   droite : le seul élément figuratif de la marque.

   `width: 100%` le cale sur la boîte de contenu, quelle que soit la largeur
   d'écran. Une valeur en `vw` ne connaît pas la gouttière de son parent ;
   c'est le piège, et il ne se voit qu'en dessous de 700 px. */
@media (max-width: 640px) {
  [data-seam] { --cut-h: clamp(72px, 14vw, 130px); }
  .opening { padding-top: calc(var(--gutter) + 60px); }
  .opening__mark { width: 100%; margin-bottom: 2vw; }

  /* 20 px d'accroche sur 360 px de large, ça fait quatre lignes qui remplissent
     le tiers haut de l'écran. 17 px en fait trois, et le héros respire. */
  .opening__claim { font-size: 17px; }
  .opening__cta   { font-size: 17px; }
  .opening__index { font-size: 13px; }
}

/* LE PIED PASSAIT SOUS LE DOCK. Le dock flotte à 14 px du bas et mesure 45 px :
   il occupe donc la bande 14–59. Le pied était à 26 px — « © 2026 » et
   « FAITES DÉFILER » se retrouvaient coupés en deux par la pastille de verre.
   Sur grand écran le problème n'existe pas : le dock est étroit et centré, il
   reste de l'air aux deux bords. */
@media (max-width: 900px) {
  .opening__foot { padding-bottom: 78px; }
}

/* --------------------------------------------------------------------------
   En-tête et dock au-dessus d'un chapitre sombre
   L'en-tête vit en haut de l'écran, le dock en bas : ils ne survolent pas la
   même chose. Un seul drapeau `data-over` les faisait basculer ensemble, donc
   l'un des deux était toujours faux pendant les transitions — flagrant depuis
   qu'un chapitre sombre (Services) est intercalé dans le corps clair.
   Deux drapeaux, deux sondes : `data-head` et `data-dock` (voir index.html).
   -------------------------------------------------------------------------- */
body[data-head='dark'] .head { color: #fff; }
body[data-head='dark'] .head__social a { color: rgba(255, 255, 255, .62); }
body[data-head='dark'] .head__social a:hover { color: #fff; }
body[data-head='dark'] .head .meta--dim { color: rgba(255, 255, 255, .55); }

body[data-dock='dark'] .dock {
  background: rgba(255, 255, 255, .07);
  border-color: rgba(255, 255, 255, .16);
}
body[data-dock='dark'] .dock a { color: rgba(255, 255, 255, .68); }
body[data-dock='dark'] .dock a:hover { color: #fff; }
body[data-dock='dark'] .dock a[aria-current='page'] {
  background: rgba(255, 255, 255, .16);
  color: #fff;
  box-shadow: none;
}
.head, .head__social a, .dock, .dock a {
  transition: color 420ms var(--ease), background 420ms var(--ease), border-color 420ms var(--ease);
}

/* --------------------------------------------------------------------------
   CARTES DE VERRE NÉON                                        [FUSION AI]
   Sur fond clair, un néon franc jure : il passe par la BORDURE et une ombre
   colorée, jamais par le fond — la lisibilité du texte reste intacte.
   -------------------------------------------------------------------------- */
.card-glass {
  --neon: #ffc000;
  overflow: hidden;
  transition: transform 420ms var(--ease), box-shadow 420ms var(--ease), border-color 420ms var(--ease);
}
.card-glass:nth-child(3n+2) { --neon: #4fd8ff; }
.card-glass:nth-child(3n+3) { --neon: #ff6a3d; }

.card-glass::after {
  content: '';
  position: absolute;
  inset: auto -20% -60% -20%;
  height: 120%;
  z-index: -1;
  background: radial-gradient(50% 50% at 50% 50%, var(--neon), transparent 70%);
  opacity: 0;
  filter: blur(40px);
  transition: opacity 420ms var(--ease);
  pointer-events: none;
}
.card-glass:hover {
  transform: translateY(-4px);
  border-color: color-mix(in srgb, var(--neon) 55%, transparent);
  box-shadow:
    0 0 0 1px color-mix(in srgb, var(--neon) 30%, transparent),
    0 18px 50px color-mix(in srgb, var(--neon) 22%, transparent);
}
.card-glass:hover::after { opacity: .30; }
.card-glass__index { transition: color 420ms var(--ease); }
.card-glass:hover .card-glass__index { color: var(--neon); }
