/* ===== SPOW Digital — Premium kit ===== */

/* (Barre de progression du scroll retirée le 11/10 — voir premium.js) */

/* Grain filmique global */
.grain {
  position: fixed; inset: 0; pointer-events: none; z-index: 9997; opacity: .045; mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* Transition de page (fondu) */
#page-fade {
  position: fixed; inset: 0; background: #000; z-index: 10000; opacity: 0; pointer-events: none;
  visibility: hidden;                                /* INACTIF = hors rendu → plus de calque fixe noir permanent (= "carré" fantôme Safari iOS) */
  transition: opacity .15s ease, visibility 0s .15s; /* passe hidden APRÈS le fondu de sortie (pour garder la transition visible) */
}
/* Lot E (22/10) : fondu ramené de .5s à .15s (plan lot D point 5 / C15.2) : le voile noir entre deux pages
   durait jusqu'à ~0,7 s, il dure maintenant ~0,3 s au total (départ 150 ms + arrivée 150 ms au plus, premium.js). */
#page-fade.show { opacity: 1; pointer-events: all; visibility: visible; transition: opacity .15s ease, visibility 0s; }

/* Spotlight sur les cartes (lumière qui suit le curseur) */
.spot { position: relative; }
.spot::before {
  content: ""; position: absolute; inset: 0; border-radius: inherit; opacity: 0;
  transition: opacity .35s ease; pointer-events: none; z-index: 1;
  background: radial-gradient(220px circle at var(--mx, 50%) var(--my, 50%), rgba(255,255,255,.07), transparent 62%);
  mix-blend-mode: soft-light;
}
.spot:hover::before { opacity: 1; }

/* Focus accessible */
:focus-visible { outline: 2px solid #fff; outline-offset: 3px; border-radius: 4px; }

/* Sélection */
::selection { background: #fff; color: #000; }

/* Scrollbar premium */
::-webkit-scrollbar { width: 9px; height: 9px; }
::-webkit-scrollbar-track { background: #000; }
::-webkit-scrollbar-thumb { background: #232323; border-radius: 9px; border: 2px solid #000; }
::-webkit-scrollbar-thumb:hover { background: #3a3a3a; }
html { scrollbar-width: thin; scrollbar-color: #232323 #000; }

/* Intro galaxie image : rotation lente */
#galaxy-img { will-change: transform; }
.galaxy-rotate { transform-origin: center; animation: gal-rot 150s linear infinite; }
@keyframes gal-rot {
  from { transform: translate(-50%,-50%) rotate(0deg); }
  to   { transform: translate(-50%,-50%) rotate(360deg); }
}
@media (prefers-reduced-motion: reduce) { .galaxy-rotate { animation: none; transform: translate(-50%,-50%); } }

/* ===== Marquee 3D perspective (technologies) ===== */
.tech3d-stage {
  position: relative; perspective: 1200px; overflow: hidden; padding: 40px 0;
  -webkit-mask-image: linear-gradient(90deg, transparent 0%, #000 13%, #000 87%, transparent 100%);
  mask-image: linear-gradient(90deg, transparent 0%, #000 13%, #000 87%, transparent 100%);
  touch-action: pan-y;
  pointer-events: none;
}
.tech3d-rot { transform: rotateX(6deg) rotateY(-18deg); transform-style: preserve-3d; touch-action: pan-y; }
/* ⚠️ PAS de `gap` sur le track : avec un gap flex, la largeur totale contient N-1 espaces, donc
   translateX(-50%) tombe une DEMI-espace trop court → SAUT visible à chaque fin de boucle
   (« quand il repart »). Avec un margin-right porté par CHAQUE item, la moitié du track vaut
   exactement une copie complète → boucle vraiment sans couture. */
.tech3d-track { display: flex; align-items: center; width: max-content; touch-action: pan-y; }
.tech3d-item {
  display: inline-flex; align-items: center; gap: 16px; flex: none;
  margin-right: 54px;                     /* remplace le gap du track (boucle sans couture) */
  opacity: .8; transition: opacity .3s ease, transform .3s ease;
}
.tech3d-item:hover { opacity: 1; transform: translateY(-4px); }
.tech-logo { height: 40px; width: auto; flex: none; }
.tech3d-word {
  font-family: 'Inter', sans-serif; font-weight: 800;
  font-size: clamp(28px, 4.5vw, 54px); letter-spacing: -0.03em;
  color: #fafafa; line-height: 1.25; white-space: nowrap;
}
@media (max-width: 760px) {
  .tech3d-item { margin-right: 38px; }   /* idem : marge sur l'item, PAS de gap sur le track */
  .tech-logo { height: 30px; }
  .tech3d-word { font-size: 28px; }
  /* défilé plus CALME sur mobile (David : « trop vif ») : sur un écran étroit, les logos traversent
     le viewport bien plus vite qu'en desktop à durée égale → on rallonge la durée (40s → 75s). */
  .tech3d-track, .marquee-content { animation-duration: 75s; }
}


/* ===== Liquid glass button ===== */
.glass-btn {
  position: relative; display: inline-flex; align-items: center; justify-content: center;
  border-radius: 9999px; color: #fff; font-weight: 700; cursor: pointer;
  background: rgba(255,255,255,0.05);
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px) url("#glassfilter");
  box-shadow:
    0 0 8px rgba(0,0,0,0.03), 0 2px 6px rgba(0,0,0,0.08),
    inset 3px 3px 0.5px -3.5px rgba(255,255,255,0.09),
    inset -3px -3px 0.5px -3.5px rgba(255,255,255,0.85),
    inset 1px 1px 1px -0.5px rgba(255,255,255,0.6),
    inset -1px -1px 1px -0.5px rgba(255,255,255,0.6),
    inset 0 0 6px 6px rgba(255,255,255,0.12),
    inset 0 0 2px 2px rgba(255,255,255,0.06),
    0 0 12px rgba(0,0,0,0.15);
  transition: transform .3s cubic-bezier(.16,1,.3,1), background-color .3s ease;
}
.glass-btn:hover { transform: scale(1.04); background: rgba(255,255,255,0.10); }
.glass-btn:active { transform: scale(.98); }

/* Variante FONCÉE du glass-btn — même esprit glossy, lisible sur fond clair (navbar blanche) */
.glass-btn-dark {
  position: relative; display: inline-flex; align-items: center; justify-content: center;
  border-radius: 9999px; color: #fff; font-weight: 700; cursor: pointer;
  background: rgba(12,12,12,0.92);
  -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px);
  box-shadow:
    inset 0 1px 0.5px rgba(255,255,255,0.28),
    inset 0 -1px 0.5px rgba(0,0,0,0.6),
    inset 1px 1px 1px -0.5px rgba(255,255,255,0.18),
    inset -1px -1px 1px -0.5px rgba(255,255,255,0.18),
    0 2px 10px rgba(0,0,0,0.28);
  transition: transform .3s cubic-bezier(.16,1,.3,1), background-color .3s ease;
}
.glass-btn-dark:hover { transform: scale(1.04); background: rgba(30,30,30,0.95); }
.glass-btn-dark:active { transform: scale(.98); }

/* ===== Lot D (19/10) : plus aucun bouton blanc sur le site =====
   Tous les boutons d'action sont en verre (.glass-btn ci-dessus), y compris leurs états survol et
   toucher. Un bouton de verre pleine largeur ne grossit pas au survol (il sortirait de sa carte) :
   il s'éclaire seulement. */
.glass-btn.w-full:hover { transform: none; }
/* rdv : créneau choisi = verre éclairé (avant : pastille blanche pleine, texte noir) */
.peer:checked + .rdv-slot {
  color: #fff; border-color: rgba(255,255,255,.7); background: rgba(255,255,255,.12);
  box-shadow: 0 0 8px rgba(0,0,0,.03), 0 2px 6px rgba(0,0,0,.08),
    inset 3px 3px .5px -3.5px rgba(255,255,255,.09), inset -3px -3px .5px -3.5px rgba(255,255,255,.85),
    inset 1px 1px 1px -.5px rgba(255,255,255,.6), inset -1px -1px 1px -.5px rgba(255,255,255,.6),
    inset 0 0 6px 6px rgba(255,255,255,.12), inset 0 0 2px 2px rgba(255,255,255,.06), 0 0 12px rgba(0,0,0,.15);
}

/* ===== Mesh-gradient animé (fond de l'intro) ===== */
#intro::before {
  content: ""; position: absolute; inset: -25%; z-index: 0; pointer-events: none;
  background:
    radial-gradient(38% 38% at 28% 32%, rgba(140,145,170,.22), transparent 62%),
    radial-gradient(34% 34% at 72% 38%, rgba(90,92,108,.20), transparent 62%),
    radial-gradient(44% 44% at 52% 74%, rgba(165,170,195,.15), transparent 62%);
  filter: blur(48px);
  animation: meshmove 20s ease-in-out infinite alternate;
}
@keyframes meshmove {
  0%   { transform: translate(0, 0) scale(1); }
  50%  { transform: translate(5%, -4%) scale(1.12); }
  100% { transform: translate(-4%, 5%) scale(1.06); }
}
@media (prefers-reduced-motion: reduce) { #intro::before { animation: none; } }

/* ===== Préloader ===== */
#loader {
  /* ⚠️ HAUTEUR DU LOADER SUR iPHONE — lire avant de modifier (31 juil.).
     Le bas de l'image doit TOUJOURS atteindre le bas du téléphone, et même CONTINUER
     derrière la barre Safari (qui est translucide et se pose par-dessus) : c'est le rendu
     de référence, celui de veodigital.fr.
     → il faut donc la hauteur du viewport BARRES REPLIÉES (`lvh`), la plus grande.
     ⚠️ PIÈGE : `dvh` semble le choix logique (« hauteur visible ») mais il fait l'inverse —
     il arrête la boîte PILE au bord de la barre, donc la zone derrière la barre n'est plus
     couverte et laisse une BANDE NOIRE. Testé le 31 juil., à ne pas refaire.
     ⚠️ UNE SEULE source de hauteur : `top` + `height`, JAMAIS aussi `bottom` (donc pas
     d'`inset:0`) et JAMAIS de hauteur en JS. Deux sources qui se contredisent = bandes
     vides en haut ET en bas (constaté). Tentatives déjà faites et annulées :
       1. `min-height:100lvh` AVEC `inset:0` → sur-contrainte, bande noire sous les planètes
       2. `html.intro-lock`                  → `height:100%` sur html+body, bloque le repli des barres
       3. hauteur pilotée en JS              → concurrence avec `inset:0`, bandes en haut et en bas
       4. `height:100dvh`                    → s'arrête au bord de la barre, bande noire en bas */
  position: fixed; top: 0; left: 0; right: 0; z-index: 10002; background: #000;
  height: 100vh;          /* repli (sur iOS, 100vh = déjà le viewport barres repliées) */
  height: 100lvh;         /* explicite : l'image passe DERRIÈRE les barres, jamais de bande */
  display: flex; flex-direction: column; align-items: center; justify-content: flex-start; padding-top: 13vh;
  /* ⚠️ PAS de `will-change` ici (2 août, 2e passe). Essayé `opacity`, puis `clip-path` : ni
     l'un ni l'autre n'a réglé le bug Chrome PC (zone de l'écran mal recomposée pendant la
     transition). Pour un `clip-path` en forme de courbe (pas un simple rectangle), forcer
     un calque avec `will-change` peut jouer contre nous plutôt que pour — on laisse Chrome
     décider lui-même comment composer cette animation, sans lui forcer la main. */
}
/* ⚠️ COUPURE NETTE, sans fondu (31 juil., 2e passe) : le fond du loader (#050507) est
   quasi-noir. Un fondu, même raccourci à .5s, se voyait donc TOUJOURS comme un temps
   d'écran noir avant le site — quelle que soit sa durée, tant qu'il fond depuis du noir,
   ça reste du noir à l'écran. La seule vraie solution : pas de fondu du tout. Le site
   apparaît instantanément avec l'animation GSAP du hero déjà lancée (voir gsap-veo.js,
   déclenchée au même instant que l'ajout de cette classe). */
#loader.done { opacity: 0; pointer-events: none; visibility: hidden; will-change: auto; }

/* ⚠️⚠️ LE SITE QUI TRANSPERCE L'INTRO (bouton « E-MAIL » visible en bas pendant l'intro, iPhone).
   MESURÉ : ce n'est PAS un problème de taille du loader — il couvre bien tout l'écran
   (0 → hauteur écran) et l'image de l'intro touche le bas. Le bouton est bien DERRIÈRE.
   C'est un bug WebKit : certains éléments (backdrop-filter, transformés par GSAP, …) sont
   peints dans leur propre calque composité, qui peut passer AU-DESSUS d'un calque plein
   écran quel que soit le z-index. Safari ne promeut ces calques que dans certains cas
   (selon le moment du chargement) → bug intermittent, d'où « ça marchait avant ».
   Chasser les causes une par une (flou, transformations GSAP…) n'a pas suffi : il suffit
   d'UN élément promu pour que ça recommence.
   CORRECTIF DÉFINITIF : pendant l'intro, on ne PEINT PAS le site. Un élément non peint ne
   peut pas transpercer, quel que soit son calque. `visibility` ne touche pas à la mise en
   page (aucun décalage au moment de la révélation) et n'affecte que l'affichage.
   Sécurité : la classe est retirée par done() (fin de la spirale, ou secours à 19 s) ET par
   un minuteur indépendant à 22 s dans premium.js — le site ne peut pas rester masqué. */
html.intro-en-cours body > *:not(#loader):not(#stars) {
  visibility: hidden !important;
  transition: none !important;   /* masquage INSTANTANÉ (voir la règle ci-dessous) */
}

/* ⚠️ Blocage du défilement pendant l'intro — REMPLACE `overflow:hidden` sur la racine, qui
   rognait tout au bord du viewport et coupait l'image de l'intro au-dessus de la barre
   Safari (« carré noir » du bas, 31 juil.). `touch-action` neutralise le geste de défilement
   sans jamais rien découper. */
html.intro-en-cours {
  touch-action: none;
  overscroll-behavior: none;
}

/* ⚠️⚠️ RÈGLE DE SÉCURITÉ — NE PAS SUPPRIMER. `transition-all` (Tailwind) anime TOUTES les
   propriétés, `visibility` comprise. Conséquences mesurées le 31 juil. sur la section du hero
   (`transition-all duration-1000`) :
     • au masquage : la section restait visible 1 s de plus que les autres blocs → c'est
       PENDANT cette seconde que le bouton « E-MAIL » transperçait l'intro ;
     • au démasquage : la transition de `visibility` pouvait rester bloquée en cours
       (onglet en arrière-plan, JS ralenti) et la section NE REVENAIT JAMAIS — contenu
       invisible, le pire des bugs sur ce site.
   On retire donc `visibility` de la liste des propriétés animées pour les blocs de premier
   niveau : le fondu voulu (opacité / déplacement) est conservé à l'identique.
   19/10 : l'accueil a maintenant un <main id="contenu"> (accessibilité) : les sections du
   hero et des offres sont ses enfants, la règle les couvre donc aussi (body > main > *). */
body > *[class*="transition-all"],
body > main > *[class*="transition-all"] {
  transition-property: opacity, transform, filter, box-shadow, background-color, border-color, color;
}

/* ceinture et bretelles : même masqués, ces effets restent une source de calques promus */
html.intro-en-cours .glass-btn,
html.intro-en-cours [class*="backdrop-blur"],
html.intro-en-cours [class*="backdrop-filter"] {
  -webkit-backdrop-filter: none !important;
  backdrop-filter: none !important;
}
.ld-word { font-family: 'JetBrains Mono', monospace; letter-spacing: .45em; font-size: 13px; color: #fff; opacity: .75; padding-left: .45em; }
.ld-logo { height: 132px; width: auto; margin-bottom: 10px; animation: ld-pulse 2.4s ease-in-out infinite; }
@keyframes ld-pulse { 0%,100% { transform: scale(1); opacity: .94; } 50% { transform: scale(1.05); opacity: 1; } }

/* ── Loader : animation spirale (canvas plein écran, logique dans le script) ── */
#loader-spiral { display: block; }
@media (prefers-reduced-motion: reduce) { .ld-logo { animation: none; } }

/* ===== Navbar premium ===== */
nav { transition: transform .55s cubic-bezier(.16,1,.3,1); }
nav.nav-hidden { transform: translateY(-150%); }
.nav-pill { transition: box-shadow .4s ease, transform .5s cubic-bezier(.16,1,.3,1), background-color .4s ease; }
nav.nav-scrolled .nav-pill { box-shadow: 0 14px 50px rgba(0,0,0,.35); transform: scale(.975); }

/* indicateur coulissant derrière le lien survolé/actif */
.nav-links { position: relative; }
.nav-links > a { position: relative; z-index: 1; }
.nav-ind {
  position: absolute; top: 50%; transform: translateY(-50%); height: 36px; left: 0; width: 0;
  border-radius: 9999px; background: rgba(0,0,0,.07); opacity: 0; z-index: 0; pointer-events: none;
  transition: left .4s cubic-bezier(.16,1,.3,1), width .4s cubic-bezier(.16,1,.3,1), opacity .3s ease;
}

/* texte qui se retourne au survol */
.nav-swap { position: relative; display: inline-block; overflow: hidden; vertical-align: middle; line-height: 1.1; }
.nav-swap-in, .nav-swap-out { display: block; transition: transform .45s cubic-bezier(.16,1,.3,1); }
.nav-swap-out { position: absolute; top: 100%; left: 0; }
.nav-links a:hover .nav-swap-in,
.nav-links a:hover .nav-swap-out { transform: translateY(-100%); }

/* bouton Réserver : reflet qui balaie */
nav a.bg-black { position: relative; overflow: hidden; }
nav a.bg-black::after {
  content: ""; position: absolute; top: 0; left: -130%; width: 55%; height: 100%;
  background: linear-gradient(120deg, transparent, rgba(255,255,255,.55), transparent);
  transform: skewX(-20deg); transition: left .7s cubic-bezier(.16,1,.3,1);
}
nav a.bg-black:hover::after { left: 150%; }

/* Fallback Spline sur mobile : gradient cosmique léger (zéro lag) */
@media (max-width: 760px) {
  #spline-intro { display: none !important; }
  #intro { background: #000 !important; }
}

/* ===== Fix carrés noirs Safari iOS =====
   Sur iOS, backdrop-filter (le flou) sur des éléments fixes ou translucides
   est repeint en NOIR pendant que la barre d'URL de Safari s'anime au scroll.
   On désactive le flou sur mobile et on remplace par un fond opaque équivalent. */
@media (max-width: 760px) {
  /* ⚠️ On COUPE TOUT backdrop-filter (flou) sur mobile : Safari iOS repeint la zone floutée
     en NOIR pendant l'anim de la barre d'URL / à l'ouverture d'un calque → le fameux "carré".
     (Les démos de cartes kards/barber utilisent .qn-panel/.fb-* → non touchées, elles gardent leur flou.) */
  [class*="backdrop-blur"],
  [style*="backdrop-filter"],
  #rdv-form,
  .glass-btn, .glass-btn-dark,
  .flip-words--mix {
    -webkit-backdrop-filter: none !important;
    backdrop-filter: none !important;
  }

  /* Navbar blanche : pilule opaque (remplace le translucide flouté) */
  nav .backdrop-blur-md,
  nav [class*="backdrop-blur"] { background-color: #ffffff !important; }

  /* Boutons "glass" du hero : fond translucide sombre sans flou */
  .glass-btn { background: rgba(255, 255, 255, 0.08) !important; }

  /* Formulaire rdv : fond sombre OPAQUE (le translucide + flou était LE carré en bas) */
  #rdv-form { background: rgba(11, 11, 13, 0.96) !important; }

  /* ⚠️ LE CARRÉ NOIR PERSISTANT : .grain est un calque `position:fixed` PLEIN ÉCRAN avec
     `mix-blend-mode:overlay`. Sur iOS Safari, un fixed + blend-mode plein écran est repeint
     en rectangle noir pendant l'anim de la barre d'URL au scroll → LE carré, présent partout.
     À 4,5% d'opacité c'est invisible sur un téléphone → on le supprime totalement sur mobile. */
  .grain { display: none !important; mix-blend-mode: normal !important; }
}

/* ===== Badge promo (Offres Sites) : halo doux + point pulsant =====
   Remplace le fond plat d'origine (bg-white/5) par un dégradé léger + une lueur, et un
   point qui pulse comme un indicateur "en cours" — attire l'œil sur l'offre sans couleur
   (le design system reste monochrome, primary = #fff). */
.promo-pill {
  background: linear-gradient(180deg, rgba(255,255,255,.09), rgba(255,255,255,.03));
  box-shadow: 0 0 30px rgba(255,255,255,.06), inset 0 1px 0 rgba(255,255,255,.08);
}

/* ===== GlowCard — carte à halo lumineux qui suit le curseur =====
   Inspiré du composant "spotlight-card" de 21st.dev, adapté en vanilla et en
   NOIR & BLANC (halo argenté). --gx/--gy = position du curseur dans la carte,
   mis à jour par glow-card.js. Bordure lumineuse via masque (compatible Safari). */
.glow-card { position: relative; isolation: isolate; }
/* bordure lumineuse */
.glow-card::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 1.5px;
  background: radial-gradient(
    240px circle at var(--gx, 50%) var(--gy, 50%),
    rgba(255,255,255,0.95),
    rgba(255,255,255,0.25) 35%,
    rgba(255,255,255,0.06) 70%
  );
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  opacity: 0;
  transition: opacity .35s ease;
  pointer-events: none;
  z-index: 2;
}
/* halo intérieur diffus */
.glow-card::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: radial-gradient(
    260px circle at var(--gx, 50%) var(--gy, 50%),
    rgba(255,255,255,0.08),
    transparent 60%
  );
  opacity: 0;
  transition: opacity .35s ease;
  pointer-events: none;
  z-index: 0;
}
/* Halo curseur : UNIQUEMENT sur appareils avec vrai survol (souris) — jamais au toucher */
@media (hover: hover) and (pointer: fine) {
  .glow-card:hover::before,
  .glow-card:hover::after { opacity: 1; }
}

/* ===== Scroll-stack : cartes services qui s'empilent au scroll ===== */
/* Chaque carte est "sticky" : elle se fige sous la navbar, et la carte suivante
   vient se poser par-dessus en laissant dépasser un petit liseré (effet deck).
   100% CSS, marche au scroll → identique desktop ET mobile (tactile). */
.stack-card {
  position: -webkit-sticky;
  position: sticky;
  /* le "top" de chaque carte est défini en inline (incrémental) pour le deck */
  box-shadow: 0 -12px 40px rgba(0, 0, 0, 0.45);
}
@media (max-width: 760px) {
  /* le préchargeur/animations restent ok ; rien de spécial ici */
  .stack-card { box-shadow: 0 -10px 30px rgba(0, 0, 0, 0.5); }
}

/* ===== Pile premium : profondeur des cartes sticky (pilotée par deck.js) ===== */
.stack-card {
  background-color: #121212;  /* opaque : masque proprement la carte d'en dessous */
  background-image: linear-gradient(180deg, rgba(255,255,255,0.045) 0%, rgba(255,255,255,0) 24%, rgba(0,0,0,0.18) 100%);
  transform-origin: center top;
  will-change: transform, filter;
  backface-visibility: hidden;
}

/* MOBILE : on REMET le déroulement (position:sticky natif) MAIS sans les "hints" de compositing
   (will-change / backface-visibility / transforms 3D de deck.js) qui sont la cause probable du
   rectangle "fantôme" iOS. Le sticky seul, sans calque GPU forcé, se repeint normalement.
   Néon + box-shadow conservés. → Si le carré revient quand même : enlever box-shadow, puis en
   dernier recours repasser .stack-card en `position: relative` (= version propre garantie). */
@media (max-width: 760px) {
  /* DECK MOBILE = FAKE-STICKY EN JS (deck.js) → l'empilement + la profondeur SONT là, SANS le carré,
     parce qu'on n'utilise JAMAIS position:sticky (LE coupable iOS). Les cartes sont en position:relative
     et deck.js les fige/recule via transform au scroll. ⚠️ NE PAS remettre transform:none/filter:none ici
     (ça bloquerait deck.js). box-shadow OFF (déclencheur), séparation par le contour néon. */
  .stack-card {
    position: relative !important;   /* PAS sticky — le "pin" est fait en JS via transform (deck.js) */
    top: auto !important;
    will-change: transform;
    backface-visibility: hidden;
    box-shadow: none !important;
    margin-bottom: 0;                /* les cartes s'empilent au "pin", pas d'espacement en flux */
  }
  .stack-wrap { gap: 0; }
  /* même hygiène pour le deck des prix (cartes empilées en absolute) : pas de calque permanent */
  .price-card { will-change: auto !important; }
}

/* Néon qui fait le tour de la carte, avec traînée qui s'efface derrière lui.
   Technique perf : un conic-gradient FIXE qu'on fait tourner en transform (GPU),
   masqué pour ne s'afficher que sur le liseré de bordure. */
.card-beam {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 1.5px;
  pointer-events: none;
  z-index: 4;
  overflow: hidden;
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
}
.card-beam::before {
  content: "";
  position: absolute;
  left: 50%; top: 50%;
  width: 185%;
  aspect-ratio: 1 / 1;
  transform: translate(-50%, -50%) rotate(0deg);
  background: conic-gradient(from 0deg,
    rgba(255,255,255,0) 0deg,
    rgba(255,255,255,0) 295deg,
    rgba(255,255,255,0.45) 335deg,
    #ffffff 353deg,
    rgba(255,255,255,0) 360deg);
  animation: card-beam-spin 4s linear infinite;
  will-change: transform;
}
@keyframes card-beam-spin {
  to { transform: translate(-50%, -50%) rotate(360deg); }
}
@media (prefers-reduced-motion: reduce) {
  .card-beam::before { animation: none; }
}

/* Carte prix "Recommandé" : néon uniquement sur MOBILE (le desktop reste tel quel,
   bordure blanche, sans néon). Sur mobile : bordure assombrie + néon qui ressort. */
/* Reflet lumineux qui balaie la carte (effet des cartes PRIX, distinct du néon).
   Bande translucide qui passe en diagonale, en boucle. Anim en transform (GPU). */
.card-shine {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  overflow: hidden;
  pointer-events: none;
  z-index: 4;
}
.card-shine::before {
  content: "";
  position: absolute;
  top: -60%;
  left: 0;
  width: 40%;
  height: 220%;
  background: linear-gradient(105deg,
    transparent 0%,
    rgba(255,255,255,0.04) 40%,
    rgba(255,255,255,0.22) 50%,
    rgba(255,255,255,0.04) 60%,
    transparent 100%);
  transform: translateX(-120%) rotate(10deg);
  animation: card-shine-sweep 4.5s ease-in-out infinite alternate;
  will-change: transform;
}
@keyframes card-shine-sweep {
  from { transform: translateX(-120%) rotate(10deg); }
  to   { transform: translateX(280%) rotate(10deg); }
}
@media (prefers-reduced-motion: reduce) {
  .card-shine::before { animation: none; opacity: 0; }
}
/* GARDE-FOU CARRÉ NOIR (voir anim.js, initGhostGuard) : ces deux halos tournent en boucle
   infinie avec will-change:transform — SUR CHAQUE CARTE DE PRIX du site (une dizaine, sur
   toute la page). Loin des yeux, ce calque composité continue de tourner ; Safari iOS le
   repeint parfois en tuile noire au retour dessus. `.est-loin` coupe l'animation et retire
   le will-change tant que la carte n'est pas à l'écran ; le mouvement reste identique une
   fois revenue dans le champ. */
.card-beam.est-loin::before,
.card-shine.est-loin::before { animation-play-state: paused; will-change: auto; }

@media (max-width: 760px) {
  /* mobile : néon sur les 3 cartes + bordure assombrie pour qu'il ressorte */
  .price-card { border-color: rgba(255,255,255,0.16) !important; }
}

/* ===== Formules de prix sur téléphone (16/10) : défilement natif à aimantation =====
   Remplace l'éventail de price-fan.js (cartes rognées des deux côtés, premier tap avalé :
   rapport d'audit P0). La carte « Recommandé » passe en premier, la suivante dépasse à
   droite pour inviter à glisser. Ordinateur (≥ 768 px) : grille Tailwind inchangée. */
@media (max-width: 767px) {
  #price-deck, .price-deck {
    display: flex; gap: 14px; margin: 0 -20px; padding: 22px 20px 14px;
    overflow-x: auto; overflow-y: hidden; scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch; overscroll-behavior-x: contain; scrollbar-width: none;
  }
  #price-deck::-webkit-scrollbar, .price-deck::-webkit-scrollbar { display: none; }
  /* 20/10 (David) : ordre naturel, on arrive centré sur la formule du milieu (script de index.html),
     ses deux voisines dépassent de chaque côté */
  #price-deck .price-card, .price-deck .price-card { flex: 0 0 min(76vw, 320px); scroll-snap-align: center; transform: none !important; }
}

/* ===== Texte brillant réutilisable (reflet auto en boucle, sans hover) — cousin de .shiny-title de l'accueil ===== */
@keyframes shimmer-sweep {
  0%   { background-position: -150% center, center; }
  100% { background-position:  250% center, center; }
}
/* E2 — Shine blanc : base claire lisible + reflet brillant qui balaie.
   (Remplace l'ancien shimmer gris qui rendait le texte terne.) */
.shiny-text {
  background:
    linear-gradient(100deg,
      rgba(255,255,255,0) 0%,
      rgba(255,255,255,0) 38%,
      rgba(255,255,255,.95) 50%,
      rgba(255,255,255,0) 62%,
      rgba(255,255,255,0) 100%),
    #dcdcdc;
  background-size: 220% auto, 100%;
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
  /* ⚠️ Reflet REMIS (14 août) : je l'avais figé le 13 août en même temps que `.chrome-text`,
     sur une fausse piste (la vraie cause du « ça bugue » sur Chrome était la démo FIZZ).
     Utilisé uniquement sur rdv.html, et neutralisé sur mobile par la media query plus haut.
     ⚠️ La DURÉE d'origine n'était sauvegardée nulle part : j'ai repris celle de `.shiny-title`
     de l'accueil (4s ease-in-out), son cousin direct. Si le balayage te paraît trop lent ou
     trop rapide par rapport à avant, dis-le, c'est ce seul chiffre à changer. */
  animation: shimmer-sweep 4s ease-in-out infinite;
}
/* un mot qu'on veut garder blanc plein au milieu d'un .shiny-text (ex. « vous ») */
.shiny-text .keep-white {
  -webkit-text-fill-color: #fff;
  color: #fff;
}
@media (prefers-reduced-motion: reduce) {
  .shiny-text { animation: none; }
}

/* E5 — Lettre par lettre : les lettres s'allument en cascade, en boucle.
   Le découpage en <span> par lettre est fait en JS (data-split). */
@keyframes letters-in {
  0%, 12%   { opacity: .12; filter: blur(2px); }
  30%, 70%  { opacity: 1;   filter: blur(0); }
  92%, 100% { opacity: .12; filter: blur(2px); }
}
.letters-in { color: #fff; }
.letters-in span { display: inline-block; animation: letters-in 5s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce) {
  .letters-in span { animation: none; opacity: 1; filter: none; }
}
/* MOBILE : on FIGE les anims de texte. Le `background-clip:text` animé (shimmer/chrome) + la cascade
   de lettres glitchent sur Safari iOS (texte fantôme/flou/dupliqué). Rendu STATIQUE = propre. */
@media (max-width: 760px) {
  .shiny-text { animation: none !important; background: none !important; -webkit-text-fill-color: #cfcfcf !important; color: #cfcfcf !important; }
  .shiny-text .keep-white { -webkit-text-fill-color: #fff !important; color: #fff !important; }
  .letters-in span { animation: none !important; opacity: 1 !important; filter: none !important; }
  .chrome-text { animation: none !important; }
}

/* ===== Coche de confirmation — SVG sur-mesure (remplace l'icône agenda) =====
   Le cercle puis la coche se dessinent à l'apparition, puis lueur qui respire. */
.vd-check { display: inline-block; animation: vd-check-glow 3.6s ease-in-out 1.1s infinite; }
.vd-check-ring {
  stroke-dasharray: 227; stroke-dashoffset: 227;
  animation: vd-draw .7s ease-out forwards;
}
.vd-check-mark {
  stroke-dasharray: 48; stroke-dashoffset: 48;
  animation: vd-draw .45s ease-out .55s forwards;
}
@keyframes vd-draw { to { stroke-dashoffset: 0; } }
@keyframes vd-check-glow {
  0%, 100% { filter: drop-shadow(0 0 4px rgba(255,255,255,.18)); }
  50%      { filter: drop-shadow(0 0 13px rgba(255,255,255,.5)); }
}
@media (prefers-reduced-motion: reduce) {
  .vd-check-ring, .vd-check-mark { animation: none; stroke-dashoffset: 0; }
  .vd-check { animation: none; }
}

/* ===== Apparition de l'écran de confirmation — fondu + montée ===== */
@keyframes conf-rise { from { opacity: 0; transform: translateY(34px) scale(.97); } to { opacity: 1; transform: translateY(0) scale(1); } }
.conf-rise { animation: conf-rise 1s cubic-bezier(.22,1,.36,1) both; }
@media (prefers-reduced-motion: reduce) {
  .conf-rise { animation: none; opacity: 1; transform: none; }
}

/* ===== Néon des champs = PORT FIDÈLE du composant "glowing input" (Uiverse), en MONOCHROME,
   adapté aux champs PLEINE LARGEUR. Couches conic empilées (glow + dark + white + border) derrière
   le champ opaque ; le champ masque le centre → il ne reste que l'anneau lumineux. La rotation se
   fait via `conic-gradient(from var(--fg-a))` + @property (pas via un gros calque tourné) → couvre
   TOUT le tour quelle que soit la taille de la case (aucun trou / décrochage, même sur le textarea).
   Au REPOS : liseré discret (bordure du champ). Au FOCUS : les 4 couches s'allument et tournent. ===== */
.field-glow { position: relative; border-radius: 20px; isolation: isolate; }   /* = rayon réel des champs (rounded-xl = 20px) */
.field-glow > input, .field-glow > textarea, .field-glow > select {
  position: relative; z-index: 1;
  display: block;                                                       /* tue le gap baseline inline-block (sinon le wrapper est ~6px plus haut que le champ → néon trop gros EN BAS) */
  outline: none !important; box-shadow: none !important;                 /* tue le halo BLEU du plugin @tailwindcss/forms */
  border-color: rgba(255,255,255,.10) !important;                       /* liseré discret COMPLET au repos (+ empêche la bordure bleue au focus) */
}
@property --fg-a { syntax: "<angle>"; inherits: false; initial-value: 90deg; }
@keyframes fg-a { to { --fg-a: 450deg; } }
.field-glow > .l-glow, .field-glow > .l-white, .field-glow > .l-border {
  position: absolute; z-index: 0; pointer-events: none; opacity: 0; transition: opacity .35s ease;
}
/* bordure : liseré chrome FIN et PLEIN → bordure complète + sheen qui balaie (fin = lit comme une
   bordure, PAS comme un carré). C'est CETTE couche qui donne la cohérence tout-le-tour. */
.field-glow > .l-border { inset: -1px; border-radius: 21px; filter: blur(.6px);
  background: conic-gradient(from var(--fg-a), #2a2a33, #ffffff 8%, #45454f 22%, #2a2a33 46%, #f2f2fa 62%, #45454f 78%, #2a2a33); }
/* éclats blancs : liseré d'1px SUR le trait (la lumière est DANS la bordure, pas un halo qui flotte).
   ⚠️ rgba(255,255,255,0), jamais `transparent` (= frange grise). */
.field-glow > .l-white  { inset: -1px; border-radius: 21px; filter: blur(1px);
  background: conic-gradient(from var(--fg-a), rgba(255,255,255,0), #ffffff 9%, rgba(255,255,255,0) 22%, rgba(255,255,255,0) 50%, #ffffff 65%, rgba(255,255,255,0) 78%); }
/* liseré lumineux net collé au trait (remplace l'ancien gros halo qui débordait = le "carré") : éclats vifs sur le bord */
.field-glow > .l-glow   { inset: -1px; border-radius: 21px; filter: blur(2.5px);
  background: conic-gradient(from var(--fg-a), rgba(255,255,255,0), #ffffff 7%, rgba(255,255,255,0) 20%, rgba(255,255,255,0) 55%, #ffffff 68%, rgba(255,255,255,0) 82%); }
/* FOCUS : les couches s'allument et TOURNENT (la lumière balaie tout le tour) */
.field-glow:focus-within > .l-border,
.field-glow:focus-within > .l-white { opacity: 1; }
.field-glow:focus-within > .l-glow { opacity: .6; }
.field-glow:focus-within > .l-glow,
.field-glow:focus-within > .l-white,
.field-glow:focus-within > .l-border { animation: fg-a 4s linear infinite; }
@media (prefers-reduced-motion: reduce) { .field-glow:focus-within > * { animation: none !important; } }

/* ===== Label flottant animé (lettre par lettre) dans les champs encadrés.
   Le label repose comme un placeholder, puis monte au focus / si rempli. ===== */
.field-glow.has-float > input,
.field-glow.has-float > textarea,
.field-glow.has-float > select { padding-top: 1.65rem !important; padding-bottom: .55rem !important; }
.float-label {
  position: absolute; left: 21px; top: 24px; z-index: 2; pointer-events: none;
  display: flex; white-space: nowrap;
}
.float-label .fl-letter {
  display: inline-block; font-size: 15px; line-height: 1; color: #777; white-space: pre;
  transition: transform .42s cubic-bezier(.34,1.56,.64,1), color .3s ease, font-size .3s ease;
}
.field-glow.is-active .float-label .fl-letter {
  transform: translateY(-17px); font-size: 11px; color: #9a9a9a; letter-spacing: .06em;
}
@media (prefers-reduced-motion: reduce) {
  .field-glow.is-active .float-label .fl-letter { transition: none; }
}

/* ===== Calendrier sur-mesure (remplace le date picker natif, moche & non stylable) ===== */
.rdv-cal { position: relative; }
.rdv-cal-trigger { cursor: pointer; font-size: 16px; color: #fff; }
.rdv-cal-pop {
  position: absolute; left: 0; right: 0; top: calc(100% + 8px); z-index: 30;
  border: 1px solid #1A1A1A; border-radius: 16px; padding: 16px;
  box-shadow: 0 24px 60px -14px rgba(0,0,0,.85);
  /* champ étoilé dense et statique sur fond noir obscur uniforme */
  background:
    radial-gradient(1.4px 1.4px at 8% 12%, rgba(255,255,255,.9), transparent),
    radial-gradient(1px 1px at 18% 28%, rgba(255,255,255,.55), transparent),
    radial-gradient(1.6px 1.6px at 27% 9%, rgba(255,255,255,.85), transparent),
    radial-gradient(1px 1px at 33% 40%, rgba(255,255,255,.5), transparent),
    radial-gradient(1.3px 1.3px at 42% 18%, rgba(255,255,255,.8), transparent),
    radial-gradient(1px 1px at 47% 56%, rgba(255,255,255,.45), transparent),
    radial-gradient(1.5px 1.5px at 55% 7%, rgba(255,255,255,.9), transparent),
    radial-gradient(1px 1px at 61% 34%, rgba(255,255,255,.55), transparent),
    radial-gradient(1.2px 1.2px at 68% 50%, rgba(255,255,255,.7), transparent),
    radial-gradient(1.5px 1.5px at 74% 16%, rgba(255,255,255,.85), transparent),
    radial-gradient(1px 1px at 80% 38%, rgba(255,255,255,.5), transparent),
    radial-gradient(1.4px 1.4px at 88% 22%, rgba(255,255,255,.8), transparent),
    radial-gradient(1px 1px at 93% 44%, rgba(255,255,255,.5), transparent),
    radial-gradient(1.2px 1.2px at 12% 52%, rgba(255,255,255,.65), transparent),
    radial-gradient(1.5px 1.5px at 22% 70%, rgba(255,255,255,.85), transparent),
    radial-gradient(1px 1px at 30% 86%, rgba(255,255,255,.5), transparent),
    radial-gradient(1.3px 1.3px at 40% 76%, rgba(255,255,255,.75), transparent),
    radial-gradient(1px 1px at 50% 92%, rgba(255,255,255,.5), transparent),
    radial-gradient(1.4px 1.4px at 58% 72%, rgba(255,255,255,.8), transparent),
    radial-gradient(1px 1px at 66% 88%, rgba(255,255,255,.5), transparent),
    radial-gradient(1.5px 1.5px at 76% 66%, rgba(255,255,255,.85), transparent),
    radial-gradient(1px 1px at 84% 84%, rgba(255,255,255,.5), transparent),
    radial-gradient(1.3px 1.3px at 92% 70%, rgba(255,255,255,.75), transparent),
    radial-gradient(1px 1px at 6% 80%, rgba(255,255,255,.5), transparent),
    radial-gradient(1.2px 1.2px at 16% 94%, rgba(255,255,255,.6), transparent),
    #090909;
}
.rdv-cal-pop[hidden] { display: none; }
.rdv-cal-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; }
.rdv-cal-title { font-family: 'JetBrains Mono', monospace; font-size: 13px; letter-spacing: .04em; text-transform: capitalize; color: #fff; }
.rdv-cal-nav { width: 34px; height: 34px; display: grid; place-items: center; border: 1px solid #1A1A1A; border-radius: 10px; color: #cfcfcf; cursor: pointer; background: transparent; transition: border-color .2s ease, color .2s ease; }
.rdv-cal-nav:hover:not(:disabled) { border-color: #fff; color: #fff; }
.rdv-cal-nav:disabled { opacity: .3; cursor: not-allowed; }
.rdv-cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; }
.rdv-cal-dow { text-align: center; font-family: 'JetBrains Mono', monospace; font-size: 11px; letter-spacing: .04em; text-transform: uppercase; color: #6f6f6f; padding: 4px 0; }
.rdv-cal-day { aspect-ratio: 1; display: grid; place-items: center; border-radius: 10px; font-size: 14px; color: #e0e0e0; cursor: pointer; border: 1px solid transparent; transition: background .2s ease, color .2s ease, border-color .2s ease; }
.rdv-cal-day:hover:not(.is-disabled):not(.is-empty) { border-color: rgba(255,255,255,.4); }
.rdv-cal-day.is-today { border-color: rgba(255,255,255,.22); }
.rdv-cal-day.is-selected { color: #fff; font-weight: 600; border-color: rgba(255,255,255,.7); background: rgba(255,255,255,.12);   /* lot D : verre, plus de blanc */
  box-shadow: inset 1px 1px 1px -.5px rgba(255,255,255,.6), inset -1px -1px 1px -.5px rgba(255,255,255,.6), inset 0 0 6px 4px rgba(255,255,255,.1); }
.rdv-cal-day.is-disabled { color: #3a3a3a; cursor: not-allowed; }
.rdv-cal-day.is-empty { cursor: default; }

/* ===== Bouton premium (adapté de 21st.dev, monochrome, sans étoile) :
   lettres qui brillent une par une + lueur au survol/clic + état « Envoi… ». ===== */
.gen-btn {
  --t: .4s; --hl: #fff; --hl-30: rgba(255,255,255,.3);
  position: relative; z-index: 0;
  display: inline-flex; align-items: center; justify-content: center;
  cursor: pointer; user-select: none; -webkit-tap-highlight-color: transparent;
  border-radius: 24px; padding: 18px 26px;
  background: #0c0c0c; color: #fff; border: 1px solid rgba(255,255,255,.2);
  font-family: 'JetBrains Mono', monospace; font-size: 12px; letter-spacing: .12em; text-transform: uppercase;
  box-shadow:
    inset 0 1px 1px rgba(255,255,255,.2), inset 0 2px 2px rgba(255,255,255,.15),
    inset 0 4px 4px rgba(255,255,255,.1), inset 0 8px 8px rgba(255,255,255,.05),
    inset 0 16px 16px rgba(255,255,255,.05),
    0 -4px 4px rgba(0,0,0,.05), 0 -8px 8px rgba(0,0,0,.06), 0 -16px 16px rgba(0,0,0,.08);
  transition: box-shadow var(--t), border-color var(--t), background-color var(--t);
}
.gen-btn::before {
  content: ""; position: absolute; inset: -4px; border-radius: 28px; pointer-events: none; z-index: -1;
  background-image: linear-gradient(0deg, #0004, #000a);
  box-shadow: 1px 1px 1px #fff2, -1px -1px 1px #0002;
  transition: box-shadow var(--t), filter var(--t);
}
.gen-btn::after {
  content: ""; position: absolute; inset: 0; border-radius: inherit; pointer-events: none;
  background-image: linear-gradient(0deg, #fff, var(--hl), rgba(255,255,255,.5) 8%, transparent);
  opacity: 0; transition: opacity var(--t), filter var(--t);
  -webkit-mask-image: linear-gradient(0deg, #fff, transparent); mask-image: linear-gradient(0deg, #fff, transparent);
}
.gen-btn__labels { position: relative; display: inline-flex; align-items: center; justify-content: center; }
.gen-btn__label { display: inline-flex; }
.gen-btn__label--idle { position: relative; }
.gen-btn__label--active { position: absolute; left: 0; right: 0; justify-content: center; opacity: 0; }
.gb-letter { display: inline-block; color: #ffffff88; animation: gb-letter 2s ease-in-out infinite; transition: color var(--t), text-shadow var(--t); }
@keyframes gb-letter { 50% { text-shadow: 0 0 3px #fff8; color: #fff; } }
.gen-btn:hover { border-color: rgba(255,255,255,.4); }
.gen-btn:hover::after { opacity: 1; }
.gen-btn:hover::before { box-shadow: 0 -8px 8px -6px #fffa inset, 0 -16px 16px -8px var(--hl-30) inset, 1px 1px 1px #fff2, -1px -1px 1px #0002; }
.gen-btn:active { border-color: rgba(255,255,255,.7); background-color: rgba(255,255,255,.06); }
.gen-btn:active::after { opacity: 1; filter: brightness(200%); }
.gen-btn:active .gb-letter { text-shadow: 0 0 1px rgba(255,255,255,.9); animation: none; }
.gen-btn.is-generating .gen-btn__label--idle { opacity: 0; }
.gen-btn.is-generating .gen-btn__label--active { opacity: 1; }
.gen-btn.is-generating::after { opacity: .6; }
.gen-btn.is-generating .gb-letter { animation: gb-burst 1s ease-in-out forwards, gb-letter 1.2s ease-in-out infinite 1s; }
@keyframes gb-burst { 0%, 100% { filter: blur(0); transform: scale(1); } 50% { transform: scale(1.7); filter: blur(7px) brightness(150%) drop-shadow(-18px 7px 9px var(--hl)); } }
@media (prefers-reduced-motion: reduce) { .gb-letter { animation: none; } }

/* ===== Bouton RETOUR façon « Flow » (d'après 21st.dev, adapté vanilla + mobile)
   La flèche glisse, un cercle blanc s'étend, le pill se carre.
   Réactif au survol ET au tap (iPhone) ; flèche qui « pousse » seule au repos. ===== */
.back-flow {
  position: relative; display: inline-flex; align-items: center; gap: .5rem;
  overflow: hidden; isolation: isolate;
  border: 1.5px solid rgba(255,255,255,.18);
  border-radius: 100px;
  padding: .6rem 1.3rem;
  color: #cfcfcf;
  font-family: 'JetBrains Mono', monospace; font-size: 12px; letter-spacing: .1em; text-transform: uppercase;
  background: rgba(255,255,255,.03);
  cursor: pointer; text-decoration: none;
  -webkit-tap-highlight-color: transparent;
  transition: color .5s ease, border-color .5s ease, border-radius .5s ease, background .5s ease;
}
.back-flow .bf-label { position: relative; z-index: 2; }
.back-flow .bf-arrow {
  position: relative; z-index: 2; width: 16px; height: 16px;
  animation: bf-nudge 2.4s ease-in-out infinite;
  transition: transform .6s cubic-bezier(.34,1.56,.64,1);
}
.back-flow .bf-circle {
  position: absolute; top: 50%; left: 50%; translate: -50% -50%;
  width: 14px; height: 14px; border-radius: 50%; background: #fff; opacity: 0; z-index: 0;
  transition: width .6s cubic-bezier(.19,1,.22,1), height .6s cubic-bezier(.19,1,.22,1), opacity .5s ease;
}
@keyframes bf-nudge { 0%, 100% { transform: translateX(0); } 50% { transform: translateX(-3px); } }

/* survol (desktop) + tap/focus (mobile) : remplit en blanc, texte noir, coins carrés */
.back-flow:hover, .back-flow:active, .back-flow:focus-visible {
  color: #000; border-color: transparent; border-radius: 14px;
}
.back-flow:hover .bf-circle, .back-flow:active .bf-circle, .back-flow:focus-visible .bf-circle {
  width: 340px; height: 340px; opacity: 1;
}
.back-flow:hover .bf-arrow, .back-flow:active .bf-arrow, .back-flow:focus-visible .bf-arrow {
  animation: none; transform: translateX(-3px);
}
@media (prefers-reduced-motion: reduce) { .back-flow .bf-arrow { animation: none; } }
/* MOBILE : le remplissage blanc (bf-circle) reste COINCÉ à mi-course au tap (focus qui persiste) → cassé.
   → sur tactile, bouton statique, aucun remplissage. */
@media (max-width: 760px) {
  .back-flow:hover, .back-flow:active, .back-flow:focus, .back-flow:focus-visible {
    color: #cfcfcf; border-color: rgba(255,255,255,.18); border-radius: 100px; background: rgba(255,255,255,.03);
  }
  .back-flow:hover .bf-circle, .back-flow:active .bf-circle, .back-flow:focus .bf-circle, .back-flow:focus-visible .bf-circle {
    width: 14px; height: 14px; opacity: 0;
  }
  .back-flow .bf-arrow { animation: none; }
}

/* ===== Champ date — thème sombre premium (icône calendrier visible) ===== */
input[type="date"]::-webkit-calendar-picker-indicator {
  filter: invert(1);
  opacity: 0.55;
  cursor: pointer;
  transition: opacity .2s ease;
}
input[type="date"]::-webkit-calendar-picker-indicator:hover {
  opacity: 1;
}

/* ===== EFFET 1 — Texte CHROME / métallique (éclat qui flue en boucle) ===== */
@keyframes chrome-flow {
  0%   { background-position: 0% center; }
  100% { background-position: 200% center; }
}
/* ⚠️ REMIS EXACTEMENT COMME AVANT (14 août) — NE PLUS Y TOUCHER SANS DEMANDER À DAVID.
   Le 13 août j'avais figé ce dégradé et éclairci ses teintes, en pensant que l'animation
   était la cause du « ça bugue » sur Chrome. C'ÉTAIT UNE FAUSSE PISTE : la vraie cause était
   la démo FIZZ qui tournait en version complète dans la vitrine sur PC (voir fizz/script.js).
   Effet de bord de ma fausse piste : sur iPhone l'animation était DÉJÀ désactivée (voir la
   media query max-width:760px plus haut) — donc seules mes nouvelles COULEURS s'y voyaient,
   et toute la typo argentée du site a changé d'aspect sur mobile. David l'a vu tout de suite.
   Valeurs d'origine restaurées à l'identique : dégradé, background-size ET animation.
   Si le coût de repeinture redevient un sujet un jour, la bonne réponse n'est PAS de figer
   le dégradé mais de déplacer un calque de reflet en `transform` (composité GPU, zéro repeint). */
.chrome-text {
  background: linear-gradient(100deg,
    #4a4a4a 0%,
    #9c9c9c 18%,
    #ffffff 34%,
    #cfcfcf 50%,
    #ffffff 66%,
    #9c9c9c 82%,
    #4a4a4a 100%);
  background-size: 200% auto;
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
}
/* le reflet ne bouge que sur les titres (66 occurrences animées en boucle au repos : audit 16/10) */
h1 .chrome-text, h2 .chrome-text, .hero-titre .chrome-text { animation: chrome-flow 5s linear infinite; }
@media (prefers-reduced-motion: reduce) { .chrome-text { animation: none !important; } }

/* ===== EFFET 2 — Halo qui respire derrière un titre ===== */
.halo-breathe { position: relative; isolation: isolate; }
.halo-breathe::before {
  content: "";
  position: absolute;
  left: 50%; top: 50%;
  width: 115%; aspect-ratio: 2.4 / 1;
  transform: translate(-50%, -50%);
  background: radial-gradient(ellipse at center,
    rgba(255,255,255,0.20),
    rgba(255,255,255,0.05) 45%,
    transparent 70%);
  filter: blur(16px);
  z-index: -1;
  pointer-events: none;
  animation: halo-pulse 4s ease-in-out infinite;
}
@keyframes halo-pulse {
  0%, 100% { opacity: .45; transform: translate(-50%,-50%) scale(0.9); }
  50%      { opacity: 1;   transform: translate(-50%,-50%) scale(1.1); }
}
@media (prefers-reduced-motion: reduce) { .halo-breathe::before { animation: none; } }

/* ===== EFFET 3 — Sparkles ✦ scintillants ===== */
@keyframes sparkle-twinkle {
  0%, 100% { opacity: 0; transform: scale(0.3) rotate(0deg); }
  50%      { opacity: 1; transform: scale(1)   rotate(90deg); }
}
.sparkle {
  position: absolute;
  color: #fff;
  line-height: 1;
  pointer-events: none;
  text-shadow: 0 0 6px rgba(255,255,255,0.85);
  animation: sparkle-twinkle 2.8s ease-in-out infinite;
  z-index: 3;
}
@media (prefers-reduced-motion: reduce) { .sparkle { animation: none; opacity: .6; } }

/* ===== Carte réseaux sociaux (footer + section Suivez-nous) ===== */
.vd-social-card {
  background: #0d0d0d;
  border: 2px solid #2c2c2c;
  border-radius: 30px;
  width: 200px; height: 200px;
  transition: transform .3s cubic-bezier(.16,1,.3,1);
  position: relative;
  overflow: hidden;
  box-shadow: 0 7px 29px rgba(100,100,111,.2);
  flex-shrink: 0;
}
.vd-social-bg {
  background-color: #0d0d0d;
  background-image: linear-gradient(140deg, #232323, #0b0b0b 55%, #191919);
  position: absolute; top: 0; right: 0; bottom: 0; left: 0;
}
.vd-social-logo {
  color: #fff; letter-spacing: 3px; font-size: 1.3em; font-weight: 600;
  font-family: 'Inter', sans-serif;
  transition: transform .3s cubic-bezier(.16,1,.3,1), letter-spacing .3s ease;
  position: absolute; bottom: 50%; right: 50%;
  transform: translate(50%, 50%); z-index: 6;
  pointer-events: none; white-space: nowrap;
}
.vd-social-icon { width: 20px; height: 20px; display: inline-block; }
.vd-social-icon .svg { fill: rgba(255,255,255,.8); width: 100%; transition: fill .2s ease, filter .2s ease; }
.vd-social-petal {
  text-align: right; transform-origin: 0 100%;
  background: rgba(255,255,255,.12);
  border-top: 2px solid rgba(255,255,255,.3); border-right: 1px solid rgba(255,255,255,.2);
  border-radius: 10% 13% 42% 0% / 10% 12% 75% 0%;
  padding: 10px; transition: bottom .35s cubic-bezier(.16,1,.3,1), left .35s cubic-bezier(.16,1,.3,1);
  position: absolute; box-shadow: -7px 7px 29px rgba(100,100,111,.35);
}
.vd-social-petal::before {
  content: ""; border-radius: inherit; opacity: 0;
  transition: opacity .25s ease;
  position: absolute; top: 0; right: 0; bottom: 0; left: 0;
}
.vd-social-petal:hover .svg { fill: #fff; }
.vd-petal-tiktok { width: 70%; height: 70%; bottom: -70%; left: -70%; z-index: 1; }
.vd-petal-tiktok::before { background: radial-gradient(circle at 30% 107%, #25f4ee, #0b0b0b 48%, #fe2c55 90%); }
.vd-petal-tiktok:hover::before { opacity: 1; }
.vd-petal-tiktok:hover .svg { filter: drop-shadow(0 0 5px #fff); }
.vd-petal-insta { width: 50%; height: 50%; bottom: -50%; left: -50%; z-index: 2; transition-delay: .2s; }
.vd-petal-insta::before { background: radial-gradient(circle at 30% 107%, #feda75, #d62976 45%, #962fbf 70%, #4f5bd5 100%); }
.vd-petal-insta:hover::before { opacity: 1; }
.vd-petal-insta:hover .svg { filter: drop-shadow(0 0 5px #fff); }
.vd-social-card:hover { transform: scale(1.1); }
.vd-social-card:hover .vd-social-petal { bottom: -1px; left: -1px; }
.vd-social-card:hover .vd-social-logo { letter-spacing: 0; transform: translate(70px, -52px); }
@media (hover: none) {
  .vd-social-card .vd-social-petal { bottom: -1px; left: -1px; }
  .vd-social-card .vd-social-logo { letter-spacing: 0; transform: translate(70px, -52px); }
}

/* ===== Star Button (fond starfield + lumière qui parcourt la bordure) ===== */
.star-btn {
  position: relative; z-index: 3; overflow: hidden; isolation: isolate;
  display: inline-flex; align-items: center; justify-content: center; gap: .5rem;
  border-radius: 9999px; cursor: pointer; background: transparent;
  --light-width: 110px; --light-color: #fafafa; --sb-border: 1.5px; --sb-duration: 3.2s;
}
.star-btn .star-btn-light {
  position: absolute; aspect-ratio: 1; inset: 0; width: var(--light-width); z-index: 1;
  background: radial-gradient(ellipse at center, var(--light-color), transparent, transparent);
  offset-path: var(--path); offset-distance: 0%;
  animation: star-btn-travel var(--sb-duration) linear infinite; animation-play-state: paused; pointer-events: none;
}
.star-btn.is-live .star-btn-light { animation-play-state: running; }
@keyframes star-btn-travel { to { offset-distance: 100%; } }
.star-btn .star-btn-bg {
  position: absolute; inset: 0; z-index: 2; overflow: hidden; border-radius: inherit;
  border: var(--sb-border) solid rgba(255,255,255,.16); color: #0f0f13; pointer-events: none;
}
.star-btn .star-btn-bg svg { display: block; width: 100%; height: 100%; }
.star-btn .star-btn-label {
  position: relative; z-index: 10;
  background: linear-gradient(to top, #8f8f8f, #ffffff);
  -webkit-background-clip: text; background-clip: text; color: transparent;
}
.star-btn { transition: transform .2s cubic-bezier(.16,1,.3,1); }
.star-btn:hover { transform: scale(1.03); }
.star-btn:active { transform: scale(.99); }
@media (prefers-reduced-motion: reduce) { .star-btn .star-btn-light { animation: none; } }


/* ===== FAQ — ouverture/fermeture en CSS MAISON (15 août) =====
   ⚠️ NE PAS revenir aux utilitaires `hidden` + `group-[.active]:block`.
   Ces deux classes sont GÉNÉRÉES AU CHARGEMENT par le CDN Tailwind. Tant que
   cdn.tailwindcss.com n'a pas répondu — ou s'il n'arrive jamais, ce qui est
   exactement ce qui est arrivé aux polices sur l'iPhone de David le même soir —
   la classe `hidden` n'existe pas : TOUTES les réponses s'affichent dépliées, et
   le clic donne l'impression de les faire disparaître. Signalé par David.
   Ces règles-ci sont servies par notre domaine, elles ne peuvent pas manquer.

   ⚠️⚠️ L'ÉTAT S'APPELLE `vfaq-open` ET NON `active` — NE PAS RENOMMER.
   `anim.js` fait `document.querySelectorAll('.reveal-on-scroll').forEach(el =>
   el.classList.add('active'))`. Les blocs FAQ portant `reveal-on-scroll`, ils recevaient
   donc `active` dès leur apparition à l'écran : TOUTES les questions s'ouvraient seules,
   et le `+` était déjà pivoté en `×`. Collision de noms, signalée par David le 16 août. */
.vfaq-a{ display:none; }
.vfaq.vfaq-open .vfaq-a{ display:block; }
.vfaq-plus{ transition:transform .25s ease; }
.vfaq.vfaq-open .vfaq-plus{ transform:rotate(45deg); }

/* ===== Bouton WhatsApp (page rdv) — VERRE LIQUIDE (18 août) =====
   David a tranché : verre liquide plutôt que pastille verte. Le vert ne subsiste que sur
   le logo — c'est lui qui identifie WhatsApp, le reste suit la charte noir/blanc du site.
   Raison de fond : ce bouton est le chemin SECONDAIRE ; « Réserver votre appel » juste
   au-dessus doit rester le plus visible. Une pastille verte pleine criait plus fort que
   l'action principale.
   ⚠️ Flou déclaré SANS `url(#glassfilter)` : ce filtre SVG n'existe que dans index.html,
   et une référence manquante invalide toute la règle (constaté sur kards.html, 15 août).
   ⚠️ Aucune classe Tailwind pour l'apparence : le bouton doit tenir même si le CDN
   n'a pas répondu. */
.wa-btn{
  position:relative; display:inline-flex; align-items:center; justify-content:center; gap:11px;
  min-height:58px; padding:0 32px; border-radius:9999px;
  font-family:'JetBrains Mono',ui-monospace,SFMono-Regular,Menlo,monospace;
  font-size:12px; font-weight:700; letter-spacing:.15em; text-transform:uppercase;
  color:#fff; text-decoration:none; white-space:nowrap;
  background:rgba(255,255,255,.05);
  -webkit-backdrop-filter:blur(10px) saturate(150%); backdrop-filter:blur(10px) saturate(150%);
  border:1px solid rgba(255,255,255,.16);
  box-shadow:
    0 2px 6px rgba(0,0,0,.28),
    inset 3px 3px .5px -3.5px rgba(255,255,255,.5),
    inset -3px -3px .5px -3.5px rgba(255,255,255,.85),
    inset 1px 1px 1px -.5px rgba(255,255,255,.45),
    inset -1px -1px 1px -.5px rgba(255,255,255,.45),
    inset 0 0 8px 8px rgba(255,255,255,.045),
    0 0 16px rgba(0,0,0,.2);
  transition:transform .3s cubic-bezier(.16,1,.3,1), background-color .3s ease, border-color .3s ease;
}
.wa-btn svg{ width:19px; height:19px; flex:none; fill:#25D366; }
.wa-btn:hover{ transform:scale(1.03); background:rgba(255,255,255,.10); border-color:rgba(255,255,255,.3); }
.wa-btn:active{ transform:scale(.98); }
@media (max-width:420px){ .wa-btn{ width:100%; padding:0 18px; font-size:11px; letter-spacing:.1em; } }
/* Libellé sur un SPAN à part : `background-clip:text` posé sur le bouton découperait le
   fond de la pastille sur les lettres et ferait s'effondrer le bouton (38 px au lieu de
   258, constaté le 18 août). Dégradé FIXE, jamais animé. */
.wa-btn-t{
  background:linear-gradient(180deg, #ffffff 0%, #ffffff 55%, #cfd6d2 100%);
  -webkit-background-clip:text; background-clip:text;
  -webkit-text-fill-color:transparent; color:transparent;
}

/* ===== Survol réservé aux vraies souris (au doigt, le « hover » colle après le tap) — 16/10 ===== */
@media (hover: none) {
  .glass-btn:hover, .glass-btn-dark:hover, .star-btn:hover, .anim-lift:hover, .vd-social-card:hover { transform: none; }
}

/* ===== Accessibilité (audit 19/10) — aucune modification visible à la souris ou au doigt ===== */
/* Réseaux (pied de page) : au clavier, les pétales sortent comme au survol et l'anneau se dessine
   sur le pétale (le lien lui-même n'a pas de boîte : son anneau faisait 0 × 0 px, invisible). */
.vd-social-card:has(a:focus-visible) .vd-social-petal { bottom: -1px; left: -1px; }
.vd-social-card:has(a:focus-visible) .vd-social-logo { letter-spacing: 0; transform: translate(70px, -52px); }
.vd-social-card a:focus-visible { outline: none; }
.vd-social-card a:focus-visible .vd-social-petal { outline: 2px solid #fff; outline-offset: -5px; }
.vd-social-card a:focus-visible .vd-social-petal::before { opacity: 1; }
/* Créneaux du formulaire RDV : la vraie case radio est masquée (sr-only), l'anneau va sur la pastille */
label > input.sr-only:focus-visible + span { outline: 2px solid #fff; outline-offset: 3px; }
/* Lien d'évitement : invisible tant qu'on ne tabule pas dessus */
.skip-link { position: absolute; left: 12px; top: -200px; z-index: 10000; padding: 12px 18px; border-radius: 999px;
  background: #0f0f11; color: #fff; font: 600 14px/1 'Inter', sans-serif; text-decoration: none;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.25); }
.skip-link:focus, .skip-link:focus-visible { top: 12px; }
/* Cible de focus programmatique (confirmation RDV, panneaux) : pas d'anneau sur un titre */
[tabindex="-1"]:focus { outline: none; }
/* FAQ : bouton natif dans le titre, rendu identique au texte d'avant */
.vfaq-q { background: none; border: 0; padding: 0; margin: 0; font: inherit; color: inherit; letter-spacing: inherit; text-align: left; cursor: pointer; -webkit-tap-highlight-color: transparent; }
/* Calendrier RDV : les jours sont des <button> (a11y 19/10) — rendu identique aux anciennes <div> */
button.rdv-cal-day { background-color: transparent; padding: 0; margin: 0; font-family: inherit; line-height: inherit; }
button.rdv-cal-day:disabled { opacity: 1; }
.rdv-cal-day:focus-visible { outline-offset: 1px; border-radius: 10px; }
@media (prefers-reduced-motion: reduce) {
  /* boucles décoratives encore actives avec le réglage (mesuré le 19/10) */
  .qn2-celeb-halo::before, .qn2-pb, .scroll-cue { animation: none !important; }
}
/* FAQ : l'anneau de focus entoure toute la ligne, comme avant (le bouton est dans le titre) */
@supports selector(:has(*)) {
  .vfaq:has(.vfaq-q:focus-visible) { outline: 2px solid #fff; outline-offset: 3px; border-radius: 4px; }
  .vfaq-q:focus-visible { outline: none; }
}
/* Barre de navigation fixe : un élément qui reçoit le focus clavier (Maj+Tab en remontant) ou une
   ancre ne doit pas finir caché dessous (WCAG 2.4.11). N'agit que sur le défilement automatique. */
html { scroll-padding-top: 96px; }
/* panneaux « Nos créations / Nos logos » ouverts au doigt ou à la souris : focus posé sans anneau (Safari) */
.creas-bar.sans-anneau:focus { outline: none; }

/* ===== Lot D (19/10) : UN SEUL haut de page pour les pages intérieures =====
   Même voix que le titre validé de l'accueil (Inter 700, approche serrée, lignes équilibrées),
   un cran plus petit pour que l'accueil reste au-dessus : 64 px tablette, 72 px ordinateur ; sur
   téléphone 44 px au plus, ramené à la largeur (40 px sur un écran de 390 px) pour que les titres
   longs tiennent en 4 lignes. Les retours à la ligne forcés ne servent que sur grand écran.
   Pages légales et 404 : version « document », 32 / 48 px.
   Plus d'étiquette au-dessus du titre, plus de mot encadré, plus de ligne de réassurance sous les
   boutons (refusés par David). Le haut de page n'apparaît qu'en fondu : jamais de translation ni
   de flou sur le contenu d'un hero. */
h1.titre-page { font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; font-weight: 700;
  font-size: clamp(36px, 10.4vw, 44px); line-height: 1.05; letter-spacing: -.04em; text-wrap: balance; color: #fff; }
@media (max-width: 767px) { h1.titre-page br { display: none; } }
@media (min-width: 768px) { h1.titre-page { font-size: 64px; } }
@media (min-width: 1024px) { h1.titre-page { font-size: 72px; } }
h1.titre-page--doc { font-size: 32px; line-height: 1.1; }
@media (min-width: 768px) { h1.titre-page--doc { font-size: 48px; } }
/* même hauteur de départ partout : le titre commence ~50 px sous la barre fixe */
section.hero-page { padding-top: 128px; }
@media (min-width: 768px) { section.hero-page { padding-top: 168px; } }
.hero-page .reveal-on-scroll, .hero-page > .translate-y-8,
.hero-page h1.e404-in, .hero-page p.e404-in, .hero-page .e404-acts { transform: none !important; }
