/*
Theme Name: BOP IMPRESSION
Theme URI: https://bopimpression.sn
Author: BOP IMPRESSION
Description: Thème sur mesure pour BOP IMPRESSION, imprimerie numérique et offset au Sénégal. Tout le contenu (textes, photos, ateliers, équipe, catalogue) se modifie depuis l'admin WordPress : Apparence > Personnaliser pour les textes des sections, et les menus "Produits", "Équipe", "Ateliers", "Clients", "Services" et "Témoignages clients" dans le tableau de bord pour le contenu répétable.
Version: 3.67
Requires at least: 6.0
Requires PHP: 7.4
Text Domain: bop-impression
*/

:root{
    --blue:#0856AE;
    --blue-deep:#1361C4;
    --teal:#F5A623;
    --orange:#FF7A29;
    --orange-deep:#E85D04;
    --yellow:#F5A623;
    --sky:#2D9CDB;
    --ink:#0856AE;
    --paper:#F5F9FD;
    --paper-alt:#E9F1FB;
    --grey:#55677A;
    --line:#DCE6F0;
    --radius:16px;
    --shadow: 0 10px 30px rgba(10,30,51,0.10);
    --shadow-lg: 0 20px 50px rgba(10,30,51,0.18);
    --shadow-glow: 0 16px 40px rgba(19,97,196,0.22);
    /* Effet "3D" façon sonacos.sn : plusieurs ombres douces superposées
       (pas une seule ombre nette) — ça donne une impression de
       profondeur/flottement bien plus réaliste qu'une ombre plate classique.
       Utilisé sur les cartes/photos ci-dessous (voir --shadow-3d) et, en
       version pour objet détouré (logos, icônes, produits), via
       filter:drop-shadow (voir .effect-3d-drop). */
    --shadow-3d: 0 1px 2px rgba(10,30,51,0.06), 0 8px 16px rgba(10,30,51,0.10), 0 28px 48px -12px rgba(10,30,51,0.28);
    --shadow-3d-hover: 0 2px 3px rgba(10,30,51,0.07), 0 12px 22px rgba(10,30,51,0.12), 0 36px 60px -12px rgba(10,30,51,0.34);
  }
  *{box-sizing:border-box;}
  /* Le pied de page (footer{background:var(--ink)}) est toujours le dernier
     élément de la page. Sur Mac/trackpad, le "rebond" de défilement au-delà
     du bas de la page découvre brièvement le fond de <html> — s'il reste
     blanc (--paper) alors que le pied de page est bleu foncé, ça donne
     l'impression d'un "espace blanc" en bas quand on scrolle jusqu'au bout.
     On aligne donc le fond de <html> sur celui du pied de page. */
  html{scroll-behavior:smooth;overflow-x:hidden;background:var(--ink);}
  @media (prefers-reduced-motion: reduce){
    html{scroll-behavior:auto;}
    *,*::before,*::after{animation-duration:.001ms !important;animation-iteration-count:1 !important;transition-duration:.001ms !important;}
  }
  body{
    margin:0;
    /* v3.64 (demande du client : "quand j'ai essayer avec mon telephone
       cela bouge") — "overflow-x:hidden" n'était posé QUE sur <html>
       (ligne juste au-dessus). Sur Safari iOS en particulier, ça ne suffit
       pas toujours à empêcher le "rebond" élastique du bout des doigts vers
       la gauche/droite quand un élément plein-écran comme
       ".cat-carousel-wrap" (voir plus bas, "width:100vw") dépasse d'un
       pixel ou deux du cadre — la page semblait alors "bouger"/glisser
       horizontalement pendant le défilement. Il faut le poser sur <html>
       ET <body> ensemble pour que ce soit fiable sur iOS. */
    overflow-x:hidden;
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    color:var(--ink);
    background:var(--paper);
    line-height:1.6;
    /* Compense l'en-tête "position:absolute" (voir header.main) qui sort du
       flux normal — sans ça, tout le contenu remonterait sous l'en-tête sur
       les pages sans bannière/photo en haut. --header-h est mesuré en JS
       (site.js) ; le repli 140px sert avant que le JS ait mesuré. */
    padding-top:var(--header-h, 140px);
  }
  img,svg{display:block;max-width:100%;}
  a{color:inherit;text-decoration:none;}
  ul{margin:0;padding:0;list-style:none;}
  .wrap{max-width:1200px;margin:0 auto;padding:0 24px;}
  section{padding:88px 0;}
  h1,h2,h3,h4{margin:0 0 .5em 0;font-weight:800;letter-spacing:-0.01em;}
  /* v2.9 (demande du client : "les titres sur le corps du site exemple
     engagement et valeurs et tous autres grand titres doit etre en
     orange") — tous les grands titres de section (h2) du site passent en
     orange par défaut (déjà le cas pour certains, comme les titres de
     section sur l'accueil — voir "#realisations .cat-head h2" plus bas :
     on généralise ce même choix à tout le site au lieu de le répéter
     section par section). Exceptions volontaires, gardées en blanc pour
     rester lisibles : les titres posés SUR un fond déjà orange
     (".cat-cta-strip h2", ".warm-band h2" sur Services/Réalisations) —
     orange sur orange serait invisible. */
  h2{font-size:clamp(26px,4vw,38px);color:var(--orange);}
  h3{font-size:19px;}
  p{margin:0 0 1em 0;color:var(--grey);}

  /* ===== BOUTONS (avec effet de "brillance" au survol) ===== */
  .btn{
    position:relative;overflow:hidden;isolation:isolate;
    display:inline-flex;align-items:center;justify-content:center;gap:8px;
    padding:15px 30px;border-radius:100px;font-weight:700;font-size:14.5px;letter-spacing:.01em;
    border:2px solid transparent;cursor:pointer;transition:transform .2s ease, box-shadow .2s ease, background .2s ease, color .2s ease;
    white-space:nowrap;
  }
  .btn::after{
    content:'';position:absolute;top:0;left:-60%;width:40%;height:100%;z-index:1;
    background:linear-gradient(115deg, rgba(255,255,255,0) 0%, rgba(255,255,255,0.45) 50%, rgba(255,255,255,0) 100%);
    transform:skewX(-20deg);transition:left .55s ease;pointer-events:none;
  }
  .btn:hover::after{left:130%;}
  .btn-primary{background:linear-gradient(120deg, var(--blue), var(--blue-deep));color:#fff;box-shadow:var(--shadow-glow);}
  .btn-primary:hover{transform:translateY(-2px);box-shadow:0 20px 40px rgba(19,97,196,0.32);}
  .btn-outline{background:transparent;border-color:#fff;color:#fff;}
  .btn-outline:hover{background:#fff;color:var(--ink);transform:translateY(-2px);}
  .btn-dark{background:var(--ink);color:#fff;}
  .btn-dark:hover{background:var(--blue-deep);transform:translateY(-2px);}
  .section-head{position:relative;max-width:680px;margin:0 auto 48px;text-align:center;isolation:isolate;}
  .section-head::before,.section-head::after{
    content:'';position:absolute;border-radius:50%;filter:blur(38px);z-index:-1;opacity:.28;pointer-events:none;
    animation:blobFloat 10s ease-in-out infinite;
  }
  .section-head::before{width:170px;height:170px;background:var(--blue);top:-56px;left:-30px;}
  .section-head::after{width:130px;height:130px;background:var(--orange);bottom:-40px;right:-20px;animation-delay:2.4s;}
  @keyframes blobFloat{0%,100%{transform:translate(0,0) scale(1);}50%{transform:translate(14px,-16px) scale(1.1);}}
  .section-head .kicker{display:block;font-size:13px;font-weight:800;letter-spacing:.12em;text-transform:uppercase;color:var(--blue);margin-bottom:10px;}

  /* ===== MAIN HEADER =====
     Repassé en "static" (demande du client : "entete ne doit pas etre
     fixe") — l'en-tête défile de nouveau avec la page, il ne reste plus
     collé en haut de l'écran.
     ".main::after" (nouveau) : dégradé blanc → transparent posé APRÈS le
     bas de l'en-tête (pas dedans), pour que la transition vers la photo du
     diaporama (ou la bannière de page) se fasse en douceur au lieu d'une
     ligne nette (demande du client : "vers le bad de entete fait une
     degrader pour qu'on ne voit pas la separation du fond blanc"). Doit
     être "position:relative" sur "header.main" pour que ce dégradé se
     positionne par rapport à lui, et avoir un z-index plus élevé que le
     diaporama pour se voir PAR-DESSUS la photo plutôt que dessous. */
  /* v1.52 — nouvel essai (demande du client : "je veux que le header soit en
     plein largeur enleve le fond blanc... essaye de mettre seulement les
     menu sur une forme blanc ou bleu") : plus de fond blanc du tout sur
     l'en-tête — il devient transparent et flotte par-dessus la photo (comme
     la version "fixe" essayée plus tôt), mais en "position:absolute" et non
     "fixed" : il défile normalement avec la page au lieu de rester collé en
     haut de l'écran pendant le défilement (le client ne veut PAS d'en-tête
     fixe — voir plus haut). Comme un en-tête absolu sort du flux normal, on
     compense avec --header-h (mesuré en JS, voir site.js) : "body" reçoit un
     padding-top équivalent, et ".hero-wrap"/".cat-banner" reçoivent une
     marge négative du même montant pour que leur photo démarre bien à y=0,
     juste derrière l'en-tête transparent. Seul le menu (.nav-links) reste
     sur une forme blanche opaque, pour rester lisible quelle que soit la
     photo en dessous — logo et icônes sociales restent tels quels par-dessus
     la photo (à ajuster si besoin après retour du client). */
  /* v1.56 (référence DevTools envoyée par le client — un en-tête
     "fixed...bg-gradient-to-b from-white/90 via-white/55 to-transparent
     backdrop-blur-sm" — avec la consigne : "fait le moi pareille juste moi
     le nav ne doit pas rester fixe cela doit etre scroler mais tous le
     reste doit etre pareille") : on reprend EXACTEMENT ce style (dégradé
     blanc opaque en haut → transparent en bas, flou verre dépoli, pleine
     largeur, sans forme/pilule bleue) mais en "position:absolute" et non
     "fixed" — l'en-tête défile normalement avec la page au lieu de rester
     collé en haut (le client ne veut PAS d'en-tête fixe, redemandé
     plusieurs fois). Toujours pas de fond plein ni de pilule : juste ce
     dégradé, sur toute la largeur, qui laisse la photo transparaître en bas
     de l'en-tête. */
  header.main{
    position:absolute;top:0;left:0;right:0;width:100%;z-index:60;
    background:linear-gradient(to bottom, rgba(255,255,255,0.9) 0%, rgba(255,255,255,0.55) 55%, rgba(255,255,255,0) 100%);
    -webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);
  }
  /* v1.79 (demande du client : "pousse le logo vers le gauche encore") —
     même technique "plein écran" que les flèches du carrousel accueil et le
     bouton WhatsApp du pied de page : ".nav" sortait auparavant à peine du
     bord d'une colonne centrée à 1200px (donc un grand vide à gauche du
     logo sur les écrans larges) ; il atteint maintenant le vrai bord de la
     fenêtre. */
  /* v1.80 (le client a vu le résultat de v1.79 : "le menu est decaler, le
     menu doit rester au milieu de la page, seul le logo est bon à sa
     place") — le "gap" fixe utilisé en v1.79 collait le menu juste après le
     logo, donc pas centré sur la page dès que le logo n'était plus au
     centre lui non plus. On revient à "justify-content:space-between" pour
     le logo et les icônes (chacun reste bien à son bord), MAIS le menu
     (".nav-links") en sort : il devient position:absolute, centré tout
     seul par rapport à ".nav" (qui fait toute la largeur de l'écran depuis
     v1.79) — il reste donc exactement au milieu de la page quelle que soit
     la largeur du logo à gauche ou des icônes à droite. */
  .nav{display:flex;align-items:center;justify-content:space-between;padding:14px 32px;width:100vw;max-width:100vw;position:relative;left:50%;transform:translateX(-50%);}
  .brand{display:flex;align-items:center;gap:10px;font-weight:800;font-size:19px;flex-shrink:0;}
  /* Le logo BOP IMPRESSION est un visuel complet (emblème + nom en dessous),
     pas une icône carrée : on l'affiche donc à sa taille/proportion réelle
     (object-fit:contain, pas de recadrage ni de cercle) pour ne jamais le
     déformer ou en couper un morceau, quelle que soit sa forme d'origine. */
  /* Le logo empile l'emblème ("B") ET le nom complet ("BOP IMPRESSION &
     SERVICES") en petit texte tout en bas de l'image : à une hauteur trop
     réduite, ce texte devient illisible même si le logo entier est bien
     visible. On l'affiche donc nettement plus grand pour que ce texte reste
     lisible, quitte à ce que l'en-tête/le pied de page soient un peu plus
     hauts. */
  .brand-mark{height:112px;width:auto;max-width:340px;flex-shrink:0;object-fit:contain;}
  @media (max-width:700px){.brand-mark{height:84px;max-width:260px;}}
  /* v1.79 (demande du client : "sans toucher au fichier", option retenue —
     agrandir juste un peu l'emblème dans l'en-tête). Uniquement dans
     l'en-tête ("header.main .brand-mark", plus spécifique que ".brand-mark"
     seul) : le pied de page garde sa taille d'origine, on ne l'a pas
     demandé pour lui. */
  header.main .brand-mark{height:128px;max-width:380px;}
  @media (max-width:700px){header.main .brand-mark{height:96px;max-width:280px;}}
  /* v1.80 (demande du client : "pour le logo est-ce que tu ne vas pas
     ajouter de l'ombre blanche comme sur le footer") — sur le pied de page
     (fond bleu uni), le blanc de l'ovale du logo se détache déjà tout seul.
     Dans l'en-tête, le fond est une photo claire/floutée : on ajoute donc
     ici un halo blanc doux DERRIÈRE le logo (filter:drop-shadow, qui suit
     la forme réelle du PNG transparent plutôt qu'un carré) pour retrouver
     le même effet de contraste que sur le pied de page. */
  header.main .brand-mark{filter:drop-shadow(0 0 14px rgba(255,255,255,.85)) drop-shadow(0 0 28px rgba(255,255,255,.6)) drop-shadow(0 4px 10px rgba(10,30,51,.25));}
  /* Le menu doit rester centré par rapport à TOUTE la largeur de l'écran
     (voir commentaire v1.80 plus haut), pas par rapport à l'espace restant
     entre logo et icônes : position:absolute + left:50% le sort du flux
     flex normal, ".nav" (position:relative, pleine largeur) sert de repère. */
  /* width:max-content — sans ça, "left:50%" réduit la largeur disponible
     calculée par le navigateur (moitié de la largeur de l'écran environ),
     ce qui repliait les libellés sur 2 lignes ("NOS RÉALISATIONS" coupé)
     bien avant que ça soit vraiment nécessaire. Avec "max-content", la
     pilule garde sa largeur naturelle sur une seule ligne jusqu'à ce que le
     menu mobile prenne le relais (voir plus bas). */
  .nav-links{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);width:max-content;}
  /* Masqué visuellement mais gardé dans le DOM pour les lecteurs d'écran et
     le SEO (voir header.php/footer.php) — le nom du site est déjà écrit
     dans l'image du logo. */
  .sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0;}
  /* v1.56 : l'en-tête redevient un dégradé clair (voir header.main) au lieu
     d'un fond bleu plein — les liens repassent donc en texte sombre
     ("ink"), lisible sur ce fond clair. */
  /* v1.71 (demande du client : "met les ecriture ... en orange et tu ajoute
     effet 3d derriere pour que les ecriture soit plus visible") — l'en-tête
     flotte par-dessus la photo du haut de page (position:absolute, voir
     header.main), donc le texte a besoin d'une vraie ombre portée pour
     rester lisible quelle que soit la photo, pas juste d'une couleur. */
  /* v1.74 (le client trouve le menu toujours peu visible malgré l'ombre) —
     on avait renforcé l'ombre en un contour net (plusieurs ombres à 0
     décalage superposées), mais le client a montré une capture (référence
     "AfriBlog") où le menu repose sur une vraie pilule blanche opaque au
     lieu de compter sur une ombre pour se détacher de la photo. */
  /* v1.76 (demande du client : "met une fond blanc pour ici seulement sans
     toucher de l'autre fond comme cette entete en pj") — fond blanc opaque
     UNIQUEMENT derrière ".nav-links" (pas sur tout "header.main", qui garde
     son dégradé transparent d'origine : logo et icônes sociales restent
     par-dessus la photo sans changement). Comme le texte repose maintenant
     sur du blanc plein, l'ombre épaisse de v1.74 (pensée pour rester
     lisible SUR LA PHOTO) n'est plus nécessaire pour la lisibilité — on la
     remplace par une ombre légère, juste pour garder un peu de relief/3D. */
  /* v1.78 (demande du client, capture "AfriBlog" à l'appui) : petits points
     de séparation entre les liens, comme sur sa référence — posés en
     ::before sur chaque <li> SAUF le premier ("li + li"), pour ne pas avoir
     de point avant "ACCUEIL" ni après "CONTACT". "gap" repassé à 0 sur le
     <ul> : l'espacement entre les liens vient maintenant du point + de sa
     marge, pas d'un gap uniforme. */
  .nav-links{display:flex;align-items:center;gap:0;background:#fff;padding:14px 30px;border-radius:100px;box-shadow:0 12px 28px rgba(10,30,51,.16);}
  .nav-links li{display:flex;align-items:center;}
  .nav-links li + li{margin-left:24px;}
  .nav-links li + li::before{content:'';flex-shrink:0;width:5px;height:5px;border-radius:50%;background:var(--blue);margin-right:24px;}
  .nav-links a{position:relative;font-weight:700;font-size:14px;text-transform:uppercase;letter-spacing:.03em;color:var(--orange);text-shadow:1px 2px 3px rgba(10,30,51,.18);padding-bottom:4px;}
  .nav-links a::after{content:'';position:absolute;left:0;right:0;bottom:0;height:2px;background:linear-gradient(90deg,var(--blue),var(--orange));transform:scaleX(0);transform-origin:left;transition:transform .25s ease;}
  /* v1.78 (demande du client : "lorsqu'on touche sur une menu la couleur
     doit etre bleu") — le survol/appui passait auparavant à un orange plus
     soutenu ("orange-deep"), il devient franchement bleu. */
  .nav-links a:hover{color:var(--blue);}
  .nav-links a:hover::after{transform:scaleX(1);}
  /* Le lien "Contact" faisait autrefois exception (seul lien orange de la
     bande) — maintenant que tous les liens sont en orange, il garde juste
     un dégradé de soulignement légèrement différent pour rester repérable
     comme l'appel à l'action de l'en-tête. */
  .nav-links a.nav-contact::after{background:linear-gradient(90deg,var(--orange),var(--orange-deep));}
  .mobile-menu a.nav-contact{color:var(--orange);}
  /* v1.80 : "margin-left:auto" (v1.79) retiré — ".nav" est repassé à
     "justify-content:space-between" (voir plus haut), qui pousse déjà les
     icônes sociales/le burger jusqu'au bord droit tout seul. */
  .nav-cta{display:flex;align-items:center;gap:16px;}

  /* ===== BANDE TÉLÉPHONE AU-DESSUS DE L'EN-TÊTE (toutes les pages) =====
     v3.38 (demande du client : "copier sur l'ensemble du site ce qui est bon"
     chez yayediabtp.com) — repris de leur en-tête : le(s) numéro(s) bien
     visibles, cliquables. Volontairement une bande À PART au-dessus de
     ".nav" (pas ajoutée dans ".nav-cta" à côté des icônes) : ".nav-links"
     est centré indépendamment de tout le reste sur cet en-tête (voir plus
     haut, "position:absolute; left:50%"), donc tout ce qu'on ajoute dans
     ".nav-cta" grandit et vient le chevaucher sur la plupart des largeurs
     d'écran courantes, pas seulement les petites — testé, ça cassait le
     menu. Une bande dédiée, sur toute la largeur, n'entre en concurrence
     avec rien. Fond plein (au lieu du dégradé transparent du reste de
     l'en-tête) pour rester lisible même quand la photo du haut de page est
     claire. Générée depuis LOCATIONS (voir assets/js/site.js) — un numéro
     par atelier, sans doublon. */
  .header-topbar{position:relative;z-index:1;display:flex;justify-content:flex-end;align-items:center;gap:22px;padding:8px 32px;background:var(--ink);flex-wrap:wrap;}
  .header-topbar:empty{display:none;}
  .header-topbar a{display:flex;align-items:center;gap:7px;font-weight:600;font-size:12.5px;color:#fff;white-space:nowrap;}
  .header-topbar a:hover{color:var(--orange);}
  .header-topbar svg{width:13px;height:13px;flex-shrink:0;color:var(--orange);}
  @media (max-width:700px){.header-topbar{display:none;}}

  /* ===== RÉSEAUX SOCIAUX DANS L'EN-TÊTE (toutes les pages) =====
     Générés depuis le même objet SOCIAL (site.js) que le pied de page :
     modifiez SOCIAL une seule fois, ça met à jour l'en-tête ET le pied de page.
     v1.71 : pastilles en orange (au lieu de bleu) — demande du client, même
     changement de couleur que le texte du menu ci-dessus. Le relief (biseau
     clair/sombre en "box-shadow" + halo) reste le même, c'est lui qui donne
     l'effet 3D sur ces icônes rondes. */
  .header-social{display:flex;align-items:center;gap:8px;}
  .header-social a{position:relative;overflow:hidden;width:36px;height:36px;flex-shrink:0;border-radius:50%;background:linear-gradient(120deg,var(--orange),var(--orange-deep));display:flex;align-items:center;justify-content:center;color:#fff;box-shadow:inset 0 1px 0 rgba(255,255,255,.35), inset 0 -3px 5px rgba(0,0,0,.18), 0 6px 12px rgba(10,30,51,.22);transition:background .2s ease, transform .2s ease, box-shadow .2s ease;}
  .header-social a:hover{background:linear-gradient(120deg,var(--blue),var(--blue-deep));transform:translateY(-2px);}
  .header-social svg{width:16px;height:16px;}
  @media (max-width:1150px){.header-social{display:none;}}

  /* Fond blanc sur le bouton hamburger ET le menu déroulant mobile : comme
     l'en-tête lui-même est transparent (voir header.main), ces deux éléments
     ont besoin de leur propre fond pour rester lisibles/cliquables par-dessus
     la photo, une fois le menu ouvert. */
  .burger{display:none;background:none;border:none;cursor:pointer;padding:8px;}
  .burger svg{width:26px;height:26px;}
  /* Fond blanc quasi plein : l'en-tête lui-même n'est qu'un dégradé (voir
     header.main), ce panneau déroulant a besoin de son propre fond opaque
     pour rester lisible une fois ouvert. */
  .mobile-menu{display:none;flex-direction:column;gap:2px;padding:6px 24px 18px;background:rgba(255,255,255,0.97);-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);border-radius:0 0 18px 18px;box-shadow:0 14px 30px rgba(4,12,26,0.18);}
  /* v3.20 : le menu mobile reprend maintenant wp_nav_menu() (voir header.php),
     qui enveloppe chaque lien dans un <li> — l'ancien HTML écrit en dur
     n'avait que des <a> directement, donc pas besoin de ce reset avant. */
  .mobile-menu li{list-style:none;margin:0;}
  .mobile-menu a{padding:12px 0;font-weight:700;text-transform:uppercase;font-size:13px;border-bottom:1px solid var(--line);}
  body.menu-open .mobile-menu{display:flex;}
  /* v1.80 : seuil remonté de 860px à 1150px (même seuil que ".header-social"
     un peu plus haut) — depuis que le menu est centré indépendamment du
     logo/des icônes (position:absolute, voir plus haut), il se retrouvait à
     chevaucher le logo sur les largeurs intermédiaires (~950-1100px) avant
     de basculer sur le menu mobile. */
  @media (max-width:1150px){
    .nav-links{display:none;}
    .burger{display:inline-flex;}
  }

  /* ===== CATEGORY BAR ===== */
  /* margin-top : décale la bande sous l'en-tête (espace de respiration). */
  .catbar{background:var(--ink);overflow-x:auto;scrollbar-width:none;margin-top:22px;}
  .catbar::-webkit-scrollbar{display:none;}
  /* Sur l'accueil, la bande suit directement le diaporama (hero) plutôt que
     l'en-tête : on retire l'espace de respiration pour éviter une bande
     blanche visible entre le diaporama sombre et la bande. */
  .hero + .catbar{margin-top:0;}
  /* Alignement à gauche (pas de centrage) : avec 12 catégories, la bande dépasse
     presque toujours la largeur de l'écran — centrer un contenu qui dépasse
     masque/coupe systématiquement le début et la fin (négatif des marges).
     L'alignement à gauche garantit que la 1ʳᵉ vignette est toujours entière.
     "padding-right" généreux : la dernière vignette a de l'air à droite au lieu
     d'être coupée pile au bord de l'écran. */
  .catbar-track{
    display:flex;gap:6px;padding:16px 48px 16px 24px;
    justify-content:flex-start;
  }
  /* Chaque pastille garde EN PERMANENCE sa propre couleur (réglée par
     catégorie dans Produits > Catégories produit, champ "Couleur de
     bannière") — pas seulement pendant le pulse — pour que la bande soit
     visiblement multicolore par bloc, même à l'arrêt. */
  .catbar-track a{
    flex-shrink:0;white-space:nowrap;font-size:12.5px;font-weight:700;text-transform:uppercase;letter-spacing:.03em;
    color:#fff;padding:8px 16px;border-radius:100px;border:1px solid rgba(255,255,255,0.25);
    background:var(--pill-color, var(--blue));
    transition:filter .2s ease, box-shadow .2s ease, transform .2s ease;
  }
  .catbar-track a:hover{filter:brightness(1.14);transform:translateY(-1px);}
  /* .pill-flash : ajoutée/retirée en JS (voir assets/js/front-page.js,
     pulseCatbar()) sur UNE SEULE pastille à la fois — celle qui correspond au
     mot actuellement affiché dans le hero. La couleur de la pastille est déjà
     la sienne en permanence (ligne ci-dessus) : le pulse ajoute juste un
     halo + un léger agrandissement, il ne change plus la couleur (qui ne doit
     jamais retomber sur une couleur par défaut, orange ou autre). */
  .catbar-track a.pill-flash{box-shadow:0 0 0 3px rgba(255,255,255,0.6), 0 8px 20px rgba(0,0,0,0.35);transform:translateY(-2px) scale(1.05);}

  /* ===== HERO DIAPORAMA GRAND FORMAT + TEXTE EN SURIMPRESSION (à gauche) ===== */
  /* v1.86 (demande du client : "la taille du hero de l'accueil doit etre sur
     toute les tailles les memes dimensions") — à l'époque, la hauteur
     dépendait de la hauteur de l'écran ("min(88vh,760px)") : passée à une
     hauteur fixe unique (700px), identique quel que soit l'écran.
     v3.35 (demande du client, après avoir montré yayediabtp.com en
     référence : "c'est la taille du hero que je veux") — essayé en
     "min-height:100vh" (plein écran, comme leur section hero) le temps
     d'un aller-retour, mais le client a confirmé vouloir garder une
     hauteur IDENTIQUE quel que soit l'écran ("oui je veux une taille
     identique") — donc pas "100vh" (qui varie justement selon l'écran).
     v3.36 : hauteur fixe de 780px — mesurée directement sur
     yayediabtp.com (leur hero fait 779px de haut dans un navigateur
     ouvert en plein écran sur un ordinateur portable standard), donc la
     même taille que leur référence, mais fixe comme demandé à l'origine
     en v1.86. */
  /* v3.50 (demande du client : "la taille des photo de hero ... tu
     augmente un peu") — la photo reste entièrement visible ("object-
     fit:contain", voir juste plus bas — exigence du client depuis v3.27,
     "les banniere image tous entiere doit etre visible") : agrandir la
     photo elle-même (avec un zoom/"transform") l'aurait fait déborder du
     cadre et donc rogner ses bords, l'inverse de ce qui est demandé. La
     bannière elle-même est donc agrandie (780px → 860px) : la photo
     (proportionnelle à ce cadre) s'affiche mécaniquement plus grande, tout
     en restant entière. */
  .hero{position:relative;overflow:hidden;min-height:860px;background:#071627;}
  .hero-slide{position:absolute;inset:0;opacity:0;transition:opacity 1s ease;}
  .hero-slide.is-active{opacity:1;z-index:1;}
  .hero-slide-media{position:absolute;inset:0;overflow:hidden;}
  /* v3.27 (demande du client : "les banniere image tous entiere doit etre
     visible") — la photo n'est plus recadrée pour remplir tout le cadre
     ("object-fit:cover") : elle s'affiche maintenant EN ENTIER
     ("object-fit:contain"), quelle que soit sa forme (portrait, paysage...).
     Un fond flouté agrandi de la même photo (".hero-blur-bg", ajouté par
     assets/js/front-page.js) reste derrière pour ne jamais laisser de vide
     autour d'elle — même principe déjà utilisé pour les vidéos en mode
     "contain" juste en dessous. */
  /* "position:relative" (ajouté v3.29, même correctif que ".collage-photo-inner
     img") : sans ça, cette photo nette ("position:static" par défaut)
     passait TOUJOURS derrière ".hero-blur-bg" juste en dessous (lui,
     "position:absolute") — un élément positionné passe toujours devant un
     élément non positionné, quel que soit l'ordre dans le code. Le flou
     cachait donc entièrement la photo nette au lieu de rester juste
     derrière elle. */
  .hero-slide-media img{position:relative;width:100%;height:100%;object-fit:contain;display:block;transform:scale(1);transition:transform 6.5s ease-out;}
  .hero-slide.is-active .hero-slide-media img{transform:scale(1.08);}
  .hero-slide-media .hero-blur-bg{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;filter:blur(35px) brightness(0.7);transform:scale(1.15);}
  /* v3.64 : image de secours posée juste derrière la vidéo (voir
     assets/js/front-page.js) — si la vidéo ne se charge/lit pas (réseau
     mobile, lecture automatique refusée...), cette photo reste visible au
     lieu d'un fond vide. "position:absolute" ici + "position:relative"
     ajouté ci-dessous sur ".hero-vid-cover" : même principe que
     ".hero-blur-bg"/".hero-slide-media img" juste au-dessus — un élément
     positionné passe toujours devant un élément non positionné, donc sans
     ce "position:relative" la vidéo (bien qu'affichée après dans le code)
     resterait cachée derrière cette image de secours en permanence. */
  .hero-slide-media .hero-vid-fallback{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block;}
  .hero-slide-media video.hero-vid-contain{position:absolute;inset:0;margin:auto;width:auto;height:100%;max-width:100%;object-fit:contain;}
  .hero-slide-media video.hero-vid-cover{position:relative;width:100%;height:100%;object-fit:cover;display:block;}
  /* v1.7x : voile sombre du bas vers le haut, pour garder le sous-titre
     lisible sur n'importe quelle photo/vidéo.
     v3.45 (demande du client : "sur le hero du page d'acceuil on ne doit
     pas avoir une couleur sur les video ou photo pour que ca soit bien
     visible") — ce voile teintait TOUJOURS la photo/vidéo, même
     légèrement, sur toute sa hauteur : retiré pour que la photo/vidéo
     s'affiche avec ses vraies couleurs, sans aucun filtre par-dessus.
     v3.61 : le sous-titre qui justifiait encore ce commentaire (".hero-
     subtitle-overlay p", sa propre ombre portée) a été retiré du diaporama
     (demande du client : "le texte sur le hero tu enleve c'est pas la
     peine") — plus aucun texte ne reste posé directement sur la photo/vidéo. */

  /* "bottom:78px" (au lieu de 22px) : la bande du bas ("hero-ticker-bar")
     est devenue plus grande (bulle du titre agrandie le 14/08) et déborde
     maintenant davantage par-dessus le diaporama — sans ce décalage, les
     points de pagination se retrouvaient masqués derrière elle. */
  .hero-dots-wrap{position:absolute;left:0;right:0;bottom:78px;z-index:3;display:flex;justify-content:center;}
  .hero-dots{display:flex;gap:10px;background:rgba(0,0,0,0.28);padding:8px 12px;border-radius:100px;backdrop-filter:blur(4px);}
  .hero-dot{width:28px;height:4px;border-radius:4px;border:none;background:rgba(255,255,255,0.4);cursor:pointer;padding:0;transition:background .25s ease;}
  .hero-dot.is-active{background:var(--blue);}

  /* Flèches précédent/suivant rondes, semi-transparentes + flou (façon
     sonacos.sn) — appellent les mêmes fonctions goTo()/next() déjà utilisées
     par les points, voir la fin de l'IIFE hero dans assets/js/front-page.js.
     Cachées sur mobile (comme sur sonacos.sn : "hidden md:block"), où le
     défilement au doigt suffit. */
  .hero-arrow{
    position:absolute;top:50%;transform:translateY(-50%);z-index:4;
    width:46px;height:46px;border-radius:50%;border:1px solid rgba(255,255,255,0.15);
    background:rgba(0,0,0,0.32);color:#fff;display:none;
    align-items:center;justify-content:center;cursor:pointer;
    -webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);
    transition:transform .2s ease, background .2s ease;
  }
  .hero-arrow svg{width:20px;height:20px;}
  .hero-arrow:hover{background:rgba(0,0,0,0.5);transform:translateY(-50%) scale(1.1);}
  .hero-arrow-prev{left:18px;}
  .hero-arrow-next{right:18px;}
  @media (min-width:701px){.hero-arrow{display:flex;}}

  /* v3.13 : anneau décoratif coin haut-droit de la photo (posé ici,
     ".hero-deco-ring"). v3.45 (demande du client : "enleve se boule sur le
     hero") — retiré : le client trouvait cette forme ("boule") gênante sur
     la photo/vidéo du diaporama d'accueil. Balise HTML retirée dans
     front-page.php ; les règles ci-dessous le sont aussi (plus utilisées
     nulle part ailleurs). */

  /* ".hero-wrap" enveloppe le diaporama ET le bandeau (voir front-page.php) :
     c'est son bord bas — pas celui du diaporama lui-même — qui sert de
     repère à ".hero-ticker-bar" ci-dessous. Plus de marge négative ici :
     l'en-tête est repassé en "static" (défile avec la page), il n'y a donc
     plus besoin de compenser sa hauteur — voir plutôt "header.main::after"
     plus haut pour le dégradé qui adoucit la transition vers la photo. */
  /* Annule le padding-top de "body" ci-dessus : cette section a sa propre
     photo plein cadre et doit démarrer à y=0, juste derrière l'en-tête
     transparent (voir header.main). */
  .hero-wrap{position:relative;margin-top:calc(-1 * var(--header-h, 140px));}

  /* Gardé ici (utilisé par ".hero-subtitle-overlay p" ci-dessus) même après
     la suppression de l'ancienne bulle de titre qui s'en servait aussi. */
  @keyframes heroCaptionIn{from{opacity:0;transform:translateY(14px);}to{opacity:1;transform:translateY(0);}}

  /* v1.72 : bande unie pleine largeur, DANS le flux normal (plus
     "position:absolute" à cheval sur la couture diaporama/chiffres-clés
     comme avant) — icône + texte à chaque extrémité ("Nos produits" /
     "Nous trouver"), séparés du centre par de fins traits verticaux.
     v1.73 : le CENTRE revient à la bulle orange + mot animé d'origine
     (demande du client : "au milieu tu le laisse comme avant") — seuls les
     2 côtés gardent la nouvelle maquette simplifiée (plus de bouton pilule). */
  .hero-ticker-bar{
    position:relative;z-index:2;width:100%;
    display:flex;align-items:center;justify-content:center;gap:20px;
    /* "var(--ink)" est en fait la même couleur que "var(--blue)" dans ce
       thème (voir les variables tout en haut du fichier) — trop proche du
       bleu des chiffres clés juste en dessous pour créer un vrai contraste.
       On reprend donc le bleu marine bien sombre déjà utilisé ailleurs sur
       le site (bannières catégorie, carte de contact) pour que la bande
       tranche nettement, comme sur la capture de référence. */
    background:#071627;
    padding:14px 28px;
    /* v3.13 (demande du client, référence "Pangja" : "les forme sur le
       theme les animations") — mêmes taches floues qui flottent doucement
       que ".section-head"/".services-dark" ailleurs sur le site (réutilise
       "blobFloat", déjà défini plus haut), simplement posées ici aussi pour
       que cette bande sombre ne reste pas complètement plate. "overflow:hidden"
       + "isolation:isolate" : les taches restent DANS la bande (pas de
       débord sur la photo au-dessus ni sur les chiffres clés en dessous) et
       leur "z-index:-1" les garde bien derrière le texte/les icônes. */
    overflow:hidden;isolation:isolate;
  }
  .hero-ticker-bar::before,.hero-ticker-bar::after{
    content:'';position:absolute;border-radius:50%;filter:blur(30px);z-index:-1;pointer-events:none;
    animation:blobFloat 9s ease-in-out infinite;
  }
  .hero-ticker-bar::before{width:150px;height:150px;background:var(--blue);opacity:.4;top:-60px;left:6%;}
  .hero-ticker-bar::after{width:120px;height:120px;background:var(--orange);opacity:.35;bottom:-60px;right:8%;animation-delay:2.2s;animation-direction:alternate-reverse;}
  .hero-ticker-item{
    flex-shrink:0;display:inline-flex;align-items:center;gap:8px;
    color:var(--orange);font-weight:700;font-size:13.5px;white-space:nowrap;
    text-shadow:0 2px 4px rgba(0,0,0,.5);
    transition:color .2s ease;
  }
  .hero-ticker-item svg{width:18px;height:18px;flex-shrink:0;}
  .hero-ticker-item:hover{color:var(--orange-deep);}
  .hero-ticker-divider{width:1px;height:22px;background:rgba(255,255,255,0.2);flex-shrink:0;}

  /* Le texte qui défilait en fond a été retiré le 14/08 ("ce qui defile
     derriere le hero il passe tu enleve ca") — ce conteneur ne sert plus
     qu'à centrer la bulle du titre entre les 2 items latéraux. */
  .hero-ticker-track-wrap{
    position:relative;flex:1;min-width:0;overflow:hidden;height:76px;
    display:flex;align-items:center;justify-content:center;
  }
  /* Le titre, dans sa bulle. ".hero-ticker-caption" (ce bloc) ne s'occupe
     QUE du centrage (son propre "transform: translate(-50%,-50%)") ; c'est
     l'enfant ".hero-ticker-caption-text.reveal" qui porte l'entrée animée
     (voir site.js — l'IntersectionObserver la déclenche tout de suite
     puisque cette zone est visible dès le chargement de la page). Les deux
     transforms doivent vivre sur deux éléments différents : sur UN SEUL
     élément, celui posé en dernier dans la feuille de style écraserait
     l'autre. */
  .hero-ticker-caption{
    position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);z-index:1;
    max-width:calc(100% - 16px);
  }
  /* Contour animé façon ".why-photo" (section "Pourquoi nous choisir") posé
     autour de la bulle du titre. ".hero-ticker-caption-ring" ne porte QUE ce
     contour tournant — pas l'animation d'entrée de la bulle
     (".hero-ticker-caption-text" plus bas), pour la même raison que
     ci-dessus. */
  .hero-ticker-caption-ring{
    position:relative;display:block;border-radius:100px;padding:3px;overflow:hidden;
  }
  .hero-ticker-caption-ring::before{
    content:'';position:absolute;inset:0;border-radius:inherit;
    background:conic-gradient(from 0deg, var(--orange), transparent 30%, transparent 70%, var(--orange) 100%);
    animation:why-photo-spin 6s linear infinite;
  }
  /* Couleurs : fond orange, écriture blanche, mot qui change
     (".hero-rotator") en bleu. */
  .hero-ticker-caption-text{
    margin:0; /* v1.89 : maintenant un <h1> (voir front-page.php) — annule sa marge par défaut. */
    position:relative;display:block;
    background:linear-gradient(120deg, var(--orange), var(--orange-deep));border-radius:100px;padding:22px 36px;
    white-space:nowrap;max-width:100%;overflow:hidden;text-overflow:ellipsis;
    font-size:24px;font-weight:800;color:#fff;letter-spacing:-0.01em;
    animation:heroCaptionIn .7s ease .3s both;
  }
  .hero-ticker-caption .hero-rotator{color:var(--blue);}
  @media (max-width:900px){.hero-ticker-caption-text{font-size:19px;padding:16px 26px;}}

  /* ".hero-rotator" doit rester "position:relative" (pas juste "inline") :
     c'est le repère de positionnement des ".hero-rotator-word" empilés en
     "absolute" par-dessus les uns des autres, qui apparaissent/disparaissent
     en fondu à TOUR DE RÔLE au même endroit (voir front-page.js). */
  .hero-rotator{position:relative;display:inline-block;min-width:1px;color:var(--orange);}
  .hero-rotator-word{position:absolute;left:0;top:0;white-space:nowrap;opacity:0;transform:translateY(10px);transition:opacity .5s ease, transform .5s ease;}
  .hero-rotator-word.is-active{position:static;opacity:1;transform:translateY(0);}
  .hero-rotator-word.is-out{position:absolute;opacity:0;transform:translateY(-10px);}

  @media (max-width:700px){
    /* v1.86 : plus de "min-height" réduit ici — le hero garde la même
       hauteur fixe qu'ailleurs (voir ".hero" plus haut). */
    /* Sur mobile, le bandeau passe en 2 lignes : le titre seul en haut
       (plus de place pour lui), les 2 items icône+texte côte à côte en
       dessous ; les traits verticaux n'ont plus d'utilité entre des lignes
       différentes, on les cache. */
    .hero-ticker-bar{flex-wrap:wrap;gap:10px 14px;padding:14px 16px;justify-content:center;}
    .hero-ticker-track-wrap{order:-1;flex:0 0 100%;height:auto;padding:10px 6px 4px;}
    .hero-ticker-caption{position:static;transform:none;left:auto;top:auto;max-width:100%;}
    /* Sur mobile, la bulle redevient un simple texte sans fond (plus de
       place) — le contour tournant n'aurait donc plus rien à entourer, on
       le désactive ici. */
    .hero-ticker-caption-ring{padding:0;}
    .hero-ticker-caption-ring::before{display:none;}
    .hero-ticker-caption-text{
      display:block;width:100%;text-align:center;white-space:normal;
      background:transparent;padding:2px 4px;
    }
    .hero-ticker-divider{display:none;}
    .hero-ticker-item{font-size:12.5px;}
  }

  /* ===== POURQUOI NOUS CHOISIR (photo à gauche + carte texte à droite) ===== */
  .why-us{background:#fff;}
  .why-layout{display:grid;grid-template-columns:1fr 1fr;gap:56px;align-items:center;}
  /* Contour animé qui tourne en continu autour de la photo (façon
     monumentrenaissanceafricaine.com) : un dégradé conique orange qui
     tourne derrière la photo, visible uniquement dans la fine marge
     autour d'elle (".why-photo-frame" est légèrement plus petite et
     posée par-dessus). Changez "var(--orange)" ci-dessous pour une autre
     couleur de contour. */
  .why-photo{position:relative;border-radius:var(--radius);padding:5px;overflow:hidden;}
  .why-photo::before{
    content:'';position:absolute;inset:0;border-radius:inherit;
    background:conic-gradient(from 0deg, var(--orange), transparent 30%, transparent 70%, var(--orange) 100%);
    animation:why-photo-spin 6s linear infinite;
  }
  @keyframes why-photo-spin{to{transform:rotate(360deg);}}
  /* Même effet "3D" (ombres douces superposées, --shadow-3d) que sur les
     photos de la section "Une solution pour chaque besoin d'impression". */
  .why-photo-frame{position:relative;border-radius:calc(var(--radius) - 3px);overflow:hidden;box-shadow:var(--shadow-3d);aspect-ratio:4/3;background:#fff;transition:box-shadow .3s ease;}
  .why-photo-frame img{width:100%;height:100%;object-fit:contain;display:block;transition:transform .5s ease;}
  .why-photo-frame:hover{box-shadow:var(--shadow-3d-hover);}
  .why-photo-frame:hover img{transform:scale(1.06);}
  .why-photo-ribbon{position:absolute;top:0;left:0;width:74px;height:74px;background:linear-gradient(135deg,var(--blue) 50%,var(--orange) 50%);clip-path:polygon(0 0,100% 0,0 100%);transition:transform .4s ease;}
  .why-photo-frame:hover .why-photo-ribbon{transform:rotate(-6deg) scale(1.08);}
  /* v3.61 : "gap:8px" gardé même sans icône (voir front-page.php) — sans
     effet tant qu'il n'y a qu'un seul enfant, inoffensif si une icône revient
     un jour. ".why-kicker svg" (règle pour l'ancienne icône étoile) retirée,
     plus aucun <svg> ici désormais. */
  .why-kicker{display:inline-flex;align-items:center;gap:8px;font-size:13px;font-weight:800;letter-spacing:.1em;text-transform:uppercase;color:var(--orange);margin-bottom:14px;}
  .why-content h2{margin-bottom:16px;}
  .why-content > p{color:var(--grey);line-height:1.7;max-width:480px;margin-bottom:22px;}
  .why-divider{width:64px;height:4px;border-radius:4px;background:linear-gradient(90deg,var(--blue),var(--orange));margin-bottom:26px;}
  .why-points{display:grid;grid-template-columns:1fr 1fr;gap:16px 24px;list-style:none;margin:0 0 32px;padding:0;}
  .why-points li{display:flex;align-items:center;gap:12px;font-weight:700;font-size:14.5px;color:var(--ink);}
  .why-point-icon{position:relative;overflow:hidden;width:36px;height:36px;border-radius:11px;display:flex;align-items:center;justify-content:center;flex-shrink:0;color:#fff;box-shadow:inset 0 1px 0 rgba(255,255,255,.35), inset 0 -3px 5px rgba(0,0,0,.18), 0 6px 12px rgba(10,30,51,.22);}
  .why-point-icon svg{width:18px;height:18px;}
  .why-cta{display:inline-flex;align-items:center;gap:10px;padding:14px 26px;border-radius:100px;border:2px solid var(--ink);color:var(--ink);font-weight:700;font-size:14px;text-decoration:none;transition:background .2s ease, color .2s ease, transform .2s ease;}
  .why-cta svg{width:18px;height:18px;transition:transform .2s ease;}
  .why-cta:hover{background:var(--ink);color:#fff;transform:translateY(-2px);}
  .why-cta:hover svg{transform:translateX(3px);}
  @media (max-width:900px){
    .why-layout{grid-template-columns:1fr;gap:32px;}
    .why-photo-frame{aspect-ratio:16/10;}
  }
  @media (max-width:520px){.why-points{grid-template-columns:1fr;}}

  /* ===== TÉMOIGNAGES CLIENTS (accueil uniquement) =====
     Une carte par fiche du menu "Témoignages clients" — voir front-page.php.
     Ne s'affiche que si au moins un témoignage existe. */
  .testimonials{background:var(--paper-alt);}
  .testimonial-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:26px;}
  .testimonial-card{background:#fff;border-radius:var(--radius);padding:30px 26px;box-shadow:var(--shadow);display:flex;flex-direction:column;}
  .testimonial-quote-mark{width:30px;height:30px;color:var(--sky);opacity:.35;margin-bottom:6px;}
  .testimonial-quote{font-size:15px;line-height:1.65;color:var(--ink);flex:1;margin:0 0 22px;}
  .testimonial-author{display:flex;align-items:center;gap:12px;}
  .testimonial-author img{width:48px;height:48px;border-radius:50%;object-fit:cover;flex-shrink:0;}
  .testimonial-avatar-fallback{width:48px;height:48px;border-radius:50%;flex-shrink:0;background:linear-gradient(120deg,var(--blue),var(--blue-deep));color:#fff;display:flex;align-items:center;justify-content:center;font-weight:800;font-size:18px;}
  .testimonial-author strong{display:block;font-size:14.5px;color:var(--ink);}
  /* Ciblé via l'enfant direct (pas juste "span") pour ne pas écraser le
     style de l'avatar de repli (.testimonial-avatar-fallback), qui est LUI
     aussi un <span> mais placé directement dans .testimonial-author. */
  .testimonial-author > div > span{display:block;font-size:12.5px;color:var(--grey);}
  @media (max-width:900px){.testimonial-grid{grid-template-columns:1fr 1fr;}}
  @media (max-width:600px){.testimonial-grid{grid-template-columns:1fr;}}

  /* ===== BANDEAU STATISTIQUES (accueil uniquement) =====
     Reproduit l'effet repéré par le client sur sonacos.sn (section #stats,
     capture DevTools du 14/08) : fond en dégradé diagonal (chez eux
     vert→jaune via "bg-gradient-to-br from-green-600 via-green-500
     to-yellow-500" ; ici on reprend les couleurs BOP au lieu des leurs, la
     structure/l'effet est identique) + texture nid d'abeille très
     discrète en superposition + chiffres dans des cartes "verre dépoli"
     (fond blanc très transparent + flou). Le chiffre s'anime en comptant
     jusqu'à sa valeur dès qu'on scrolle jusqu'ici (déjà en place, voir
     site.js "Chiffres animés" — c'est "l'effet du diaporama des chiffres"). */
  /* Espace du haut très réduit (28px, contre 64px avant) : demande du
     client le 14/08 ("enleve espace... hero colle le") — les cartes
     doivent rester proches du bandeau ".hero-ticker-bar" au-dessus plutôt
     que de flotter dans une grande zone vide. L'espace du bas (64px) est
     gardé, pour respirer avant la section suivante. */
  .stats{
    position:relative;overflow:hidden;padding:28px 0 64px;
    background:linear-gradient(135deg, var(--blue) 0%, var(--blue-deep) 48%, var(--orange) 100%);
  }
  /* Texture nid d'abeille en pur CSS (aucune image à charger) : six
     dégradés linéaires superposés qui, mis côte à côte à ces tailles et
     décalages précis, dessinent une grille d'hexagones répétée. */
  .stats::before{
    content:'';position:absolute;inset:0;pointer-events:none;opacity:.5;
    background-image:
      linear-gradient(30deg, rgba(255,255,255,.09) 12%, transparent 12.5%, transparent 87%, rgba(255,255,255,.09) 87.5%, rgba(255,255,255,.09)),
      linear-gradient(150deg, rgba(255,255,255,.09) 12%, transparent 12.5%, transparent 87%, rgba(255,255,255,.09) 87.5%, rgba(255,255,255,.09)),
      linear-gradient(30deg, rgba(255,255,255,.09) 12%, transparent 12.5%, transparent 87%, rgba(255,255,255,.09) 87.5%, rgba(255,255,255,.09)),
      linear-gradient(150deg, rgba(255,255,255,.09) 12%, transparent 12.5%, transparent 87%, rgba(255,255,255,.09) 87.5%, rgba(255,255,255,.09)),
      linear-gradient(60deg, rgba(255,255,255,.11) 25%, transparent 25.5%, transparent 75%, rgba(255,255,255,.11) 75%, rgba(255,255,255,.11)),
      linear-gradient(60deg, rgba(255,255,255,.11) 25%, transparent 25.5%, transparent 75%, rgba(255,255,255,.11) 75%, rgba(255,255,255,.11));
    background-size:64px 112px;
    background-position:0 0, 0 0, 32px 56px, 32px 56px, 0 0, 32px 56px;
  }
  /* "auto-fit" plutôt qu'un nombre de colonnes fixe : il y a toujours
     exactement 4 chiffres (voir front-page.js), mais ce nombre ne doit pas
     être codé en dur ici. La carte-logo ("façon 50 ans" de sonacos.sn) a
     été retirée le 14/08 sur demande du client ("enleve le logo de bop
     ici") — il ne reste que les 4 vraies cartes chiffres. */
  /* v3.13 (demande du client, référence "Pangja" : "les forme sur le theme
     les animations") — 2 anneaux + 1 tache floue qui flottent doucement
     par-dessus le nid d'abeille, comme sur ".services-dark" plus bas
     (mêmes animations "bgRingPulse"/"bgShapeFloat", déjà définies plus
     bas dans ce fichier — réutilisées ici, pas redéfinies). Couleurs en
     blanc translucide (pas bleu/orange comme ailleurs) car le fond ICI est
     déjà le dégradé bleu→orange : des ronds colorés se verraient mal
     dessus, alors qu'un blanc discret ressort sur les deux teintes. */
  .stats::after{
    content:'';position:absolute;border-radius:50%;filter:blur(50px);z-index:0;pointer-events:none;
    width:240px;height:240px;background:#fff;opacity:.13;top:-70px;right:8%;
    animation:bgShapeFloat 9s ease-in-out infinite;
  }
  .stats .bg-ring{position:absolute;border-radius:50%;border:2px solid rgba(255,255,255,0.28);z-index:0;pointer-events:none;animation:bgRingPulse 6.5s ease-in-out infinite;}
  .stats .bg-ring-1{width:70px;height:70px;top:20px;left:10%;}
  .stats .bg-ring-2{width:38px;height:38px;bottom:24px;left:22%;border-color:rgba(255,255,255,0.4);animation-delay:1.6s;}
  .stats-grid{position:relative;z-index:1;display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:20px;text-align:left;}
  .stat-item{
    position:relative;overflow:hidden;padding:26px 22px;border-radius:18px;
    background:rgba(255,255,255,0.14);
    border:1px solid rgba(255,255,255,0.22);
    -webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);
    box-shadow:0 10px 26px rgba(8,20,40,0.16);
    transition:transform .3s ease, background .25s ease;
  }
  .stat-item:hover{transform:scale(1.045);background:rgba(255,255,255,0.2);}
  /* "Effet diapo" façon sonacos.sn — reproduit exactement leur structure
     vue dans DevTools le 14/08 (".map-content" passe à "opacity-0" au
     survol, PAS juste un calque qui apparaît par-dessus) : le chiffre/
     libellé (".stat-item-content") disparaît en fondu ET, en même temps,
     la photo assombrie (".stat-item-bg", voir front-page.js pour la
     provenance des photos) apparaît en fondu — la carte "se retourne"
     complètement sur l'image, elle ne reste pas un simple calque de
     couleur figé avec le chiffre qui resterait affiché par-dessus (c'est
     ce qui manquait avant et rendait l'effet invisible/statique). */
  .stat-item-bg{
    position:absolute;inset:0;z-index:0;
    background-size:cover;background-position:center;
    opacity:0;transition:opacity .3s ease;
  }
  .stat-item-bg::after{
    content:'';position:absolute;inset:0;
    background:linear-gradient(180deg, rgba(4,12,26,0.55), rgba(4,12,26,0.82));
  }
  .stat-item:hover .stat-item-bg{opacity:1;}
  .stat-item-content{position:relative;z-index:1;transition:opacity .3s ease;}
  .stat-item:hover .stat-item-content{opacity:0;}
  .stat-num-wrap{display:flex;align-items:baseline;gap:4px;}
  .stat-num{font-size:clamp(30px,4vw,42px);font-weight:800;color:#fff;letter-spacing:-0.02em;}
  .stat-suffix{font-size:clamp(18px,2.6vw,24px);font-weight:800;color:#fff;}
  .stat-item-label{display:flex;align-items:center;gap:8px;margin-top:10px;}
  .stat-item-label svg{width:16px;height:16px;flex-shrink:0;color:rgba(255,255,255,0.85);}
  .stat-item p{margin:0;color:rgba(255,255,255,0.85);font-size:13px;font-weight:700;text-transform:uppercase;letter-spacing:.03em;}
  @media (max-width:800px){.stats-grid{grid-template-columns:1fr 1fr;gap:16px;}}
  @media (max-width:480px){.stats-grid{grid-template-columns:1fr;}}

  /* ===== SERVICES — COLLAGE (fond blanc : texte + résumé à gauche, mosaïque
     de photos à droite — une photo par famille de produits, son nom en bas,
     chaque photo mène à sa section détaillée sur la page Services).
     Remplace l'ancienne grille à 4 cartes blanches : le détail produit par
     produit (les listes cliquables) reste sur la page Services pour ne pas
     surcharger l'accueil. ===== */
  .services-dark{background:#fff;position:relative;overflow:hidden;}
  /* Décor étalé sur toute la section (pas seulement derrière le texte) :
     2 taches de couleur supplémentaires + un petit anneau, dispersées côté
     droit et en bas, pour que le fond ne reste pas vide autour de la
     mosaïque de photos. Toujours pointer-events:none, toujours derrière
     .services-collage (z-index:1). */
  @keyframes bgShapeFloat{0%,100%{transform:translate(0,0) scale(1);}50%{transform:translate(-26px,28px) scale(1.18);}}
  @keyframes bgRingPulse{0%,100%{transform:scale(1) translateY(0);opacity:.8;}50%{transform:scale(1.25) translateY(-14px);opacity:.35;}}
  .services-dark::before,.services-dark::after{
    content:'';position:absolute;border-radius:50%;filter:blur(60px);z-index:0;pointer-events:none;
    animation:bgShapeFloat 8s ease-in-out infinite;
  }
  .services-dark::before{width:280px;height:280px;background:var(--yellow);opacity:.16;top:-40px;right:-80px;}
  .services-dark::after{width:220px;height:220px;background:var(--sky);opacity:.14;bottom:-70px;right:260px;animation-delay:2.4s;animation-direction:alternate-reverse;}
  .services-dark .bg-ring{position:absolute;border-radius:50%;border:2px solid rgba(8,86,174,0.14);z-index:0;pointer-events:none;animation:bgRingPulse 6s ease-in-out infinite;}
  .services-dark .bg-ring-2{animation-delay:1.4s;}
  .services-dark .bg-ring-1{width:90px;height:90px;top:60px;left:46%;}
  .services-dark .bg-ring-2{width:46px;height:46px;bottom:40px;left:38%;border-color:rgba(255,122,41,0.22);}
  .services-collage{position:relative;z-index:1;display:grid;grid-template-columns:1fr 1fr;gap:56px;align-items:center;}
  /* Décor du bloc de gauche : taches de couleur floutées façon éclaboussure
     d'encre (même principe que .section-head::before/::after ailleurs sur le
     site) + une silhouette d'imprimante en filigrane — tout est en
     pointer-events:none et en z-index négatif dans un contexte isolé, donc
     ça ne gêne jamais les clics sur le texte, la liste ou le bouton. */
  .services-collage-text{position:relative;isolation:isolate;}
  .services-collage-text::before,.services-collage-text::after{
    content:'';position:absolute;border-radius:50%;filter:blur(50px);z-index:-1;pointer-events:none;
    animation:blobFloat 11s ease-in-out infinite;
  }
  .services-collage-text::before{width:220px;height:220px;background:var(--orange);opacity:.20;top:-70px;left:-90px;}
  .services-collage-text::after{width:170px;height:170px;background:var(--sky);opacity:.16;bottom:20px;left:30px;animation-delay:2.6s;}
  /* Décalée sous le titre (pas derrière, pour ne pas brouiller sa lecture) et
     débordant vers la gauche du bloc de texte. */
  .print-shape-decor{position:absolute;top:100px;left:-60px;width:190px;height:190px;z-index:-1;fill:var(--ink);opacity:.05;pointer-events:none;}
  .services-collage-text .kicker{color:var(--orange);}
  .services-collage-text h2{margin-bottom:14px;}
  .services-collage-intro{color:var(--grey);margin-bottom:28px;}
  .services-collage-list{position:relative;display:grid;gap:20px;margin:0 0 32px;list-style:none;padding:0;}
  .services-collage-list::before{
    content:'';position:absolute;top:-18px;left:-38px;width:150px;height:150px;z-index:-1;pointer-events:none;
    background-image:radial-gradient(rgba(8,86,174,0.16) 1.6px, transparent 1.7px);background-size:11px 11px;opacity:.6;
  }
  .services-collage-item{display:flex;align-items:flex-start;gap:16px;}
  /* Fond blanc forcé (au lieu de la couleur par service réglée en PHP,
     "style=background:...") : demande du client — "change leur bloc en
     blanc oubien eux meme en blanc cest paa lisible" — les icônes fournies
     sont orange et se voyaient mal sur les badges bleus/orange foncé.
     !important nécessaire pour passer devant le style en ligne. */
  .services-collage-icon{position:relative;overflow:hidden;flex-shrink:0;width:44px;height:44px;border-radius:12px;display:flex;align-items:center;justify-content:center;background:#fff !important;box-shadow:inset 0 1px 0 rgba(255,255,255,.35), 0 8px 16px rgba(10,30,51,.18), 0 0 0 1px var(--line);}
  .services-collage-icon svg{width:22px;height:22px;color:#fff;}
  /* Icônes fournies par le client (voir bop_service_icon_svg) : des images
     PNG, pas des traits SVG — object-fit:contain pour ne jamais les déformer. */
  .services-collage-icon img{width:26px;height:26px;object-fit:contain;}
  .services-collage-item-body{display:block;font-size:14.5px;line-height:1.5;color:var(--grey);}
  .services-collage-item-body strong{display:block;color:var(--ink);font-size:16px;margin-bottom:3px;}
  /* Sur grand écran, les 4 photos ne sont PAS rangées dans une grille sage :
     elles s'enchaînent en diagonale, se chevauchent légèrement et débordent
     même du cadre en haut/à droite et en bas/à gauche — d'où le
     positionnement en "absolute" à des coordonnées précises plutôt qu'une
     grille classique. Un anneau décoratif habille la 3e photo. */
  .services-collage-photos{position:relative;aspect-ratio:1/0.95;}
  .services-collage-photos::before{content:'';position:absolute;top:40%;right:-8%;width:44%;height:44%;border-radius:50%;border:2px solid rgba(8,86,174,0.18);pointer-events:none;}
  /* Effet "3D" façon sonacos.sn : plusieurs ombres douces superposées
     (--shadow-3d) qui donnent l'impression que la photo flotte au-dessus
     de la page, au lieu d'une ombre plate classique.
     Contour animé façon ".why-photo" (section "Pourquoi nous choisir"),
     demandé aussi ici sur les 4 photos ("cette effet tu peut l mettre
     solution pour chaque tous les 4 blocs") : ".collage-photo" (cet
     élément, positionné/animé à l'entrée) ne porte QUE le contour tournant
     (padding + "::before") ; ".collage-photo-inner" (nouvel élément à
     l'intérieur, voir front-page.php) porte la photo, le fondu sombre et
     le titre — deux animations qui portent chacune leur propre
     "transform" (l'entrée en diagonale ICI, la rotation du contour DANS
     "::before") ne peuvent pas vivre sur le même élément, sinon l'une
     écraserait l'autre (bug déjà rencontré plusieurs fois dans ce
     fichier). */
  /* "--cp-radius" (au lieu d'écrire "26px" à deux endroits) : le rayon de
     l'anneau extérieur ET celui de la photo à l'intérieur (légèrement plus
     petit, "- 7px") doivent rester cohérents même quand ce rayon change en
     mobile (voir la requête média plus bas, qui redéfinit juste cette
     variable au lieu de recopier les deux valeurs séparément).
     v3.22 (remarque du client : "les effet qui sont sur l'accueil se tourne
     doit se tourner sur le cadre du bloc") — l'anneau tournant existait déjà
     (padding de 4px), mais si fin qu'il ne se voyait quasiment pas, surtout
     sur les 2 plus petites photos du bloc "Nos produits" : passé à 7px pour
     que le cadre qui tourne soit clairement visible sur chacun des 4 blocs,
     pas juste un fin liseré. */
  .collage-photo{--cp-radius:26px;position:absolute;display:block;border-radius:var(--cp-radius);padding:7px;overflow:hidden;box-shadow:var(--shadow-3d);transition:transform .25s ease, box-shadow .25s ease;background:var(--paper-alt);}
  .collage-photo::before{
    content:'';position:absolute;inset:0;border-radius:inherit;
    background:conic-gradient(from 0deg, var(--orange), transparent 30%, transparent 70%, var(--orange) 100%);
    animation:why-photo-spin 6s linear infinite;
  }
  /* v3.23 (remarque du client : "lorsqu'on a une image en png qui n'a pas
     de fond on voit le fond de ce qui tourne alors que ce qui tourne
     devrait etre une forme de contour") — "::before" (juste au-dessus)
     remplit TOUT le bloc, pas seulement une fine bordure : ça ne se
     voyait que dans la marge du padding tant que la photo par-dessus
     était opaque. Avec une image PNG à fond transparent, les parties
     transparentes laissaient voir le dégradé tournant EN ENTIER par
     transparence, au lieu d'un simple contour. Un fond blanc ici (même
     principe que ".why-photo-frame", déjà traité ainsi ailleurs sur le
     site) bloque ce dégradé derrière toute photo, PNG transparent inclus
     — il ne reste alors visible que dans la marge, comme un vrai contour. */
  .collage-photo-inner{position:relative;display:block;width:100%;height:100%;border-radius:calc(var(--cp-radius) - 7px);overflow:hidden;background:#fff;}
  /* "position:relative" (ajouté v3.29) : SANS ça, cette photo nette — qui
     n'a pas de position déclarée, donc "static" par défaut — passe
     TOUJOURS derrière ".collage-blur-bg" juste en dessous (qui, lui, est
     "position:absolute") quelles que soient leurs classes ou leur ordre
     dans le code : un élément positionné passe toujours devant un élément
     non positionné. Avec "position:relative" ici, la photo nette devient
     positionnée elle aussi et reprend sa place au-dessus, dans l'ordre où
     elle apparaît dans le code (après le flou). */
  .collage-photo-inner img{position:relative;width:100%;height:100%;object-fit:contain;display:block;}
  /* v3.29 (demande du client : "cette cadre reste l'image nest pas bien sur
     laccueil") — quand la photo (jamais recadrée) ne remplit pas tout le
     bloc carré, le fond blanc uni ci-dessus se voyait nettement autour
     d'elle, comme un cadre vide. Une copie floutée agrandie de la même
     photo comble maintenant cet espace — même principe que sur le
     diaporama d'accueil et les bannières de page. Sélecteur PLUS
     spécifique que ".collage-photo-inner img" juste au-dessus (2 classes
     contre 1 classe + balise) : passe bien devant lui malgré son ordre
     antérieur dans le fichier — et repasse en "position:absolute" pour
     rester en fond, sous la vraie photo (voir note juste au-dessus). */
  .collage-photo-inner .collage-blur-bg{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block;filter:blur(28px) brightness(0.85);transform:scale(1.15);}
  /* v1.61 (demande du client : "les titre ne sont pas tres visible") :
     fondu sombre du bas assombri (0.72 → 0.85) + taille/graisse et ombre du
     texte augmentées pour que le titre orange reste net quelle que soit la
     photo derrière.
     v3.45 (demande du client : "regarde comment le bloc de petit format est
     coupé") — sur la photo "Impression petit format" (une facture, donc
     avec du texte jusqu'en bas), ce fondu commençait dès 40% de la hauteur
     : la moitié basse de la photo — justement là où se trouve le total de
     la facture — se retrouvait assombrie/comme coupée. Repoussé à 62% :
     nettement plus de la photo reste bien visible avant que ça
     s'assombrisse, tout en gardant le titre ("Impression petit format" etc.)
     bien lisible tout en bas, sur les 4 photos. */
  .collage-photo-inner::after{content:'';position:absolute;inset:0;background:linear-gradient(180deg,rgba(0,0,0,0) 62%,rgba(0,0,0,0.85) 100%);}
  .collage-photo-title{position:absolute;left:14px;right:14px;bottom:12px;z-index:1;color:var(--orange);font-weight:800;font-size:16px;text-shadow:0 2px 4px rgba(0,0,0,0.9), 0 1px 12px rgba(0,0,0,0.7);}
  /* Pas de "transform" ici : l'animation d'entrée (voir plus bas,
     "animation-fill-mode:both") garde la priorité sur "transform" même au
     survol, quelle que soit la spécificité — un translateY ici resterait
     donc sans effet visuel une fois l'entrée jouée. Seule l'ombre change. */
  .collage-photo:hover{box-shadow:var(--shadow-3d-hover);}
  /* 1re photo : la plus grande, en haut à gauche de la zone. */
  .collage-photo-0{top:0;left:0;width:56%;height:58%;z-index:3;}
  /* 2e photo : déborde en haut et à droite du cadre. */
  .collage-photo-1{top:-6%;right:-6%;width:42%;height:40%;z-index:2;}
  /* 3e photo : chevauche la 1re et la 2e, au centre-droit, devant l'anneau. */
  .collage-photo-2{top:46%;right:0;width:36%;height:36%;z-index:4;}
  /* 4e photo : déborde en bas et à gauche du cadre, sous la 1re. */
  .collage-photo-3{bottom:-6%;left:10%;width:44%;height:42%;z-index:1;}
  /* Animation d'entrée : chaque photo arrive "de l'extérieur" du cadre, en
     diagonale vers son emplacement final, et les 4 se mettent en place
     l'une après l'autre (délai croissant sur chaque photo). Utilise
     "animation" (pas "transition") avec animation-play-state:paused par
     défaut — l'animation reste figée à 0% (photo décalée, invisible) tant
     que ".services-collage-photos" n'a pas reçu ".in-view" (classe posée
     par le reveal-au-scroll commun du site, voir site.js : IntersectionObserver
     sur .reveal). On ne joue donc l'entrée qu'au moment où la mosaïque
     devient réellement visible à l'écran (pas avant, pas "dans le dos" de
     l'utilisateur pendant qu'il défile), avec un délai propre à chaque
     photo pour l'effet "l'une après l'autre". --cp-x/--cp-y donnent la
     direction "extérieure" propre à chaque photo. */
  @keyframes collagePhotoIn{
    0%{opacity:0;transform:translate(var(--cp-x,0),var(--cp-y,0)) scale(.9);}
    100%{opacity:1;transform:translate(0,0) scale(1);}
  }
  .collage-photo{animation:collagePhotoIn .7s ease both;animation-play-state:paused;}
  .services-collage-photos.in-view .collage-photo{animation-play-state:running;}
  .collage-photo-0{--cp-x:-70px;--cp-y:-70px;animation-delay:.1s;}
  .collage-photo-1{--cp-x:70px;--cp-y:-70px;animation-delay:.35s;}
  .collage-photo-2{--cp-x:70px;--cp-y:70px;animation-delay:.6s;}
  .collage-photo-3{--cp-x:-70px;--cp-y:70px;animation-delay:.85s;}
  @media (max-width:900px){
    .services-collage{grid-template-columns:1fr;gap:36px;}
    .services-collage-photos{order:-1;aspect-ratio:auto;display:grid;grid-template-columns:1fr 1fr;gap:16px;}
    .services-collage-photos::before{display:none;}
    .collage-photo{--cp-radius:var(--radius);position:relative;top:auto;left:auto;right:auto;bottom:auto;width:auto;height:auto;aspect-ratio:4/3;box-shadow:var(--shadow-3d);}
    .collage-photo-0,.collage-photo-1,.collage-photo-2,.collage-photo-3{--cp-x:0px;--cp-y:26px;}
  }
  @media (max-width:560px){
    .collage-photo-title{font-size:14px;}
  }

  /* ===== PAGE "SERVICES" (services.html) — blocs photo + texte alternés ===== */
  .service-block{padding:76px 0;}
  .service-block:nth-child(even){background:var(--paper-alt);}
  .service-grid{position:relative;display:grid;grid-template-columns:1fr 1fr;gap:56px;align-items:center;}
  .service-grid.is-reverse .service-media{order:2;}
  /* v3.31 (demande du client : "tous les blocs en fond blanc") — fond blanc
     uni pour les marges vides de "object-fit:contain", comme sur tous les
     autres cadres photo du site. */
  .service-media{border-radius:var(--radius);overflow:hidden;box-shadow:var(--shadow-3d);aspect-ratio:4/3;transition:box-shadow .3s ease;background:#fff;}
  .service-media:hover{box-shadow:var(--shadow-3d-hover);}
  .service-media img{width:100%;height:100%;object-fit:contain;display:block;}
  /* Même fond blanc forcé que .services-collage-icon (voir plus haut) — mêmes raisons. */
  .service-icon{position:relative;overflow:hidden;width:52px;height:52px;border-radius:14px;display:flex;align-items:center;justify-content:center;margin-bottom:18px;background:#fff !important;box-shadow:inset 0 1px 0 rgba(255,255,255,.35), 0 10px 18px rgba(10,30,51,.16), 0 0 0 1px var(--line);}
  .service-icon svg{width:26px;height:26px;color:#fff;}
  .service-icon img{width:30px;height:30px;object-fit:contain;}
  /* v1.61 : les deux boutons ("Demander un devis" + "Voir tous les
     produits", voir page-services.php) côte à côte, repassent en colonne
     sur mobile pour ne pas se comprimer. */
  .service-block-ctas{display:flex;flex-wrap:wrap;gap:14px;}
  .service-list{display:grid;gap:12px;margin:22px 0 28px;}
  .service-list .svc-feat-thumb{width:52px;height:52px;border-radius:12px;}
  .service-list .svc-feat-thumb svg{width:19px;height:19px;}
  .service-list .svc-feat-name{font-size:15px;}
  .service-list .svc-feat-desc{font-size:12.5px;}

  /* ===== MINI-FICHES PRODUIT DES LISTES "SERVICES" (accueil .svc-list +
     page Services .service-list) — voir bop_render_feature_list() : photo à
     gauche, nom, courte caractéristique en dessous, tout le bloc cliquable
     vers la page catégorie du produit. Styles communs aux deux listes ;
     tailles (photo, texte) réglées séparément juste au-dessus selon
     l'espace disponible dans chaque liste. */
  .svc-feat{display:flex;}
  .svc-feat-link{display:flex;align-items:center;gap:12px;width:100%;color:inherit;text-decoration:none;padding:6px;margin:-6px;border-radius:12px;transition:background .2s ease;}
  .svc-feat-link:hover{background:var(--paper-alt);}
  .svc-feat-link:hover .svc-feat-name{color:var(--blue);}
  .svc-feat-no-link{align-items:center;gap:12px;padding:6px;margin:-6px;}
  /* v3.31 : fond blanc (comme les autres cadres photo du site). */
  .svc-feat-thumb{flex-shrink:0;overflow:hidden;background:#fff;display:flex;align-items:center;justify-content:center;}
  .svc-feat-thumb img{width:100%;height:100%;object-fit:contain;display:block;}
  .svc-feat-thumb svg{color:var(--orange);flex-shrink:0;}
  .svc-feat-body{display:flex;flex-direction:column;gap:2px;min-width:0;}
  .svc-feat-name{font-weight:700;color:var(--ink);transition:color .2s ease;}
  .svc-feat-desc{color:var(--grey);font-weight:500;line-height:1.35;overflow:hidden;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;}
  /* Sur fond blanc (.services-dark), le repli "pas de photo" garde la même
     coche jaune qu'avant ce changement, pour rester cohérent visuellement. */
  .services-dark .svc-feat-thumb svg{color:var(--yellow);}
  @media (max-width:900px){
    .service-grid{grid-template-columns:1fr;gap:28px;}
    .service-grid.is-reverse .service-media{order:0;}
  }

  /* ===== QUI SOMMES-NOUS (bande photo + texte) ===== */
  .about-band{background:linear-gradient(120deg, var(--orange), var(--orange-deep) 60%, #7A3406);position:relative;overflow:hidden;}
  .about-band-grid{position:relative;z-index:1;display:grid;grid-template-columns:1fr 1.1fr;gap:56px;align-items:center;}
  /* Même contour animé que sur le bloc "Pourquoi nous choisir" (voir
     .why-photo), mais en bleu ici pour trancher sur le fond orange. Changez
     "var(--blue)" ci-dessous pour une autre couleur de contour. */
  .about-band-media{position:relative;border-radius:var(--radius);padding:5px;overflow:hidden;aspect-ratio:4/3;}
  .about-band-media::before{
    content:'';position:absolute;inset:0;border-radius:inherit;
    background:conic-gradient(from 0deg, var(--blue), transparent 30%, transparent 70%, var(--blue) 100%);
    animation:why-photo-spin 6s linear infinite;
  }
  /* v3.30 (demande du client : "regarde aussi les fond noir sur les blocs")
     — ce cadre utilise "object-fit:contain" (photo entière, jamais coupée),
     ce qui laisse des marges vides autour de la photo si sa forme ne
     remplit pas exactement le cadre 4/3. Ces marges affichaient un fond
     NOIR ("background:#000"), très visible sur les photos claires/PNG à
     fond blanc. v3.31 (demande du client : "tous les blocs en fond blanc")
     — fond blanc uni, comme tous les autres cadres photo du site. */
  .about-band-media-frame{position:relative;width:100%;height:100%;border-radius:calc(var(--radius) - 3px);overflow:hidden;box-shadow:0 20px 50px rgba(10,30,51,0.18);background:#fff;}
  .about-band-media-frame img{width:100%;height:100%;object-fit:contain;display:block;position:absolute;inset:0;opacity:0;transition:transform .5s ease, opacity .4s ease;}
  .about-band-media-frame img.is-shown{opacity:1;}
  .about-band-media-frame:hover img{transform:scale(1.06);}
  .about-band p{color:rgba(255,255,255,0.92);max-width:480px;margin-bottom:26px;line-height:1.7;}
  @media (max-width:900px){.about-band-grid{grid-template-columns:1fr;gap:28px;}}

  /* ===== CATALOGUE — carrousel défilant façon yayediabtp.com (flèches dans l'en-tête) ===== */
  .cat-head{display:flex;justify-content:space-between;align-items:flex-end;gap:24px;flex-wrap:wrap;max-width:none;text-align:left;margin:0 0 40px;}
  .cat-head > div{max-width:640px;}
  @media (max-width:700px){.cat-head{align-items:flex-start;}}
  .cat-arrow{position:relative;overflow:hidden;width:44px;height:44px;border-radius:10px;border:none;cursor:pointer;background:var(--ink);color:#fff;display:flex;align-items:center;justify-content:center;box-shadow:inset 0 1px 0 rgba(255,255,255,.3), inset 0 -3px 5px rgba(0,0,0,.2), var(--shadow);transition:background .2s ease, transform .2s ease;}
  .cat-arrow:hover{background:var(--blue);transform:scale(1.06);}
  .cat-arrow svg{width:20px;height:20px;}
  /* v1.69 : les 2 flèches encadrent le carrousel, une de chaque côté (voir
     front-page.php) — ".cat-carousel-wrap" est le repère de position ;
     "z-index" au-dessus des vignettes (jusqu'à 100 en JS, voir render()
     dans front-page.js) pour rester cliquables par-dessus.
     v1.70 (demande du client : "pousse le sur les extermite") — à "left:0",
     les flèches restaient collées au bord INTÉRIEUR de ".wrap" (centré,
     max-width 1200px), donc loin des vrais bords de l'écran sur grand
     écran. Même technique de "sortie du wrap" que la barre de filtre de
     "Tous nos produits" (page-produits.php) : ".cat-carousel-wrap" s'étend
     sur 100% de la largeur de la FENÊTRE (pas juste de ".wrap"), pour que
     les flèches, elles, se retrouvent vraiment sur les bords de l'écran. */
  .cat-carousel-wrap{position:relative;width:100vw;left:50%;transform:translateX(-50%);}
  .cat-carousel-wrap .cat-arrow{position:absolute;top:50%;transform:translateY(-50%);z-index:200;}
  .cat-carousel-wrap .cat-arrow-prev{left:16px;}
  .cat-carousel-wrap .cat-arrow-next{right:16px;}
  @media (max-width:700px){
    .cat-carousel-wrap .cat-arrow{width:38px;height:38px;}
    .cat-carousel-wrap .cat-arrow svg{width:17px;height:17px;}
    .cat-carousel-wrap .cat-arrow-prev{left:8px;}
    .cat-carousel-wrap .cat-arrow-next{right:8px;}
  }
  /* Grille par défaut (utilisée telle quelle sur les 12 pages catégorie, section "Nos réalisations") */
  .cat-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:20px;margin-bottom:44px;}
  /* .cat-card est maintenant un lien (<a>, vers la fiche du produit) sur les
     pages catégorie et "Toutes nos réalisations" — display:block + couleur/
     décoration remises à zéro pour qu'il se comporte comme avant (un <div>). */
  /* v3.28 (demande du client : "le fond [...] qui est sur les bloc ne se
     separe pas avec le fond bleu clair il fait mettre en blanc et prendre
     le contour en bleu ... des realisation") — ".cat-visual" (juste en
     dessous) restait en bleu très pâle ("--paper-alt") alors que la carte
     qui l'entoure (".cat-card", ici) est blanche : dès qu'une photo ne
     remplissait pas tout le carré (voir "object-fit:contain" plus bas,
     v3.26), on voyait un carré bleuté se détacher nettement sur fond
     blanc, comme une "couture" au milieu de la carte. Contour repassé en
     bleu (au lieu du gris discret d'avant) pour bien détacher chaque
     carte, comme demandé. */
  .cat-card{display:block;background:#fff;border:2px solid var(--blue);border-radius:var(--radius);overflow:hidden;color:inherit;text-decoration:none;transition:transform .25s ease, box-shadow .25s ease;box-shadow:var(--shadow-3d);}
  .cat-card:hover{transform:translateY(-6px);box-shadow:var(--shadow-3d-hover);}
  .cat-visual{position:relative;aspect-ratio:1/1;background:#fff;display:flex;align-items:center;justify-content:center;overflow:hidden;}
  .cat-visual svg{width:60%;height:60%;}
  .cat-visual img{width:100%;height:100%;object-fit:contain;display:block;transition:transform .4s ease;}
  .cat-card:hover .cat-visual img{transform:scale(1.08);}
  .cat-name{padding:14px 16px;font-weight:700;font-size:14px;text-align:center;border-top:1px solid var(--line);}
  /* v3.49 (demande du client : "sue la page realisation les blocs de
     realisations les titres leur cadre doit etre en orange avec un titre
     blanc") — ".cat-card"/".cat-name" sont PARTAGÉS avec les 12 pages
     catégorie (voir la note juste au-dessus) : scopé à "#toutes-
     realisations" (section galerie de cette page précise, voir
     page-realisations.php) pour ne changer QUE cette page, pas les pages
     catégorie qui gardent leur cadre bleu habituel. */
  #toutes-realisations .cat-card{border-color:var(--orange);}
  #toutes-realisations .cat-name{background:var(--orange);color:#fff;border-top:none;}

  /* ===== VOILE AU SURVOL SUR LES VIGNETTES DU CARROUSEL "NOS PRODUITS" (accueil) =====
     Par défaut : seulement la photo. Au survol (ou en permanence sur mobile, où il n'y a
     pas de survol) : un voile coloré apparaît avec le nom de la vignette. */
  .cat-overlay{
    position:absolute;inset:0;
    background:rgba(232,93,4,0.55);
    opacity:0;display:flex;align-items:center;justify-content:center;text-align:center;padding:18px;
    transition:opacity .3s ease;
  }
  .cat-card:hover .cat-overlay,
  .cat-card:focus-within .cat-overlay{opacity:1;}
  .cat-overlay-name{
    color:#fff;font-weight:700;font-size:19px;line-height:1.3;
    text-shadow:0 2px 8px rgba(0,0,0,0.45);
    transform:translateY(8px);transition:transform .3s ease;
  }
  .cat-card:hover .cat-overlay-name,
  .cat-card:focus-within .cat-overlay-name{transform:translateY(0);}
  @media (max-width:700px){
    .cat-overlay{opacity:1;background:rgba(232,93,4,0.55);}
    .cat-overlay-name{transform:translateY(0);font-size:17px;}
  }
  /* v3.19 (demande du client : "ameliore le design de la page nos
     realisations") — petite icône loupe qui apparaît au survol/focus sur
     les photos de "Nos réalisations" (voir page-realisations.php +
     assets/js/page-realisations.js), pour montrer que la photo s'agrandit
     maintenant au clic. Scopée à ".cat-card-zoom" uniquement : les autres
     grilles ".cat-card" du site (catégories/produits, de vrais liens) ne
     sont pas concernées, elles gardent leur propre voile ".cat-overlay". */
  .cat-card-zoom{cursor:pointer;}
  .cat-card-zoom:focus-visible{outline:3px solid var(--orange);outline-offset:2px;}
  .cat-zoom-hint{
    position:absolute;top:10px;right:10px;width:34px;height:34px;border-radius:50%;
    background:rgba(7,22,39,0.55);color:#fff;display:flex;align-items:center;justify-content:center;
    opacity:0;transform:scale(.85);transition:opacity .25s ease, transform .25s ease;
    -webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px);pointer-events:none;
  }
  .cat-zoom-hint svg{width:17px;height:17px;}
  .cat-card-zoom:hover .cat-zoom-hint,
  .cat-card-zoom:focus-visible .cat-zoom-hint{opacity:1;transform:scale(1);}
  @media (max-width:1000px){.cat-grid{grid-template-columns:1fr 1fr;}}
  @media (max-width:560px){.cat-grid{grid-template-columns:1fr 1fr;gap:12px;}}

  /* Photo de fond floutée et assombrie derrière toute la section "Quelques
     réalisations" (accueil) — façon dislabwestafrica.com/brochure/, où seule
     la page centrale du carrousel reste nette et tout le reste (y compris le
     fond) est flou/sombre autour d'elle. Remplit l'espace vide autour du
     carrousel. Réglable depuis Personnaliser > Accueil — Catalogue
     (carrousel), vide = pas de photo. Sur un pseudo-élément débordant
     légèrement (inset négatif) pour que le flou ne laisse pas de bord net
     visible, et assombrie pour que le texte (kicker/titre) reste lisible
     par-dessus. */
  #realisations{position:relative;overflow:hidden;}
  #realisations::before{
    content:'';position:absolute;inset:-20px;
    background-image:var(--realisations-bg-image, none);
    background-size:cover;background-position:center;
    filter:blur(7px) brightness(.5);
    /* Réglable depuis Personnaliser > Accueil — Catalogue (carrousel) >
       Opacité de la photo de fond ; .5 = valeur par défaut si le réglage
       n'a jamais été enregistré. */
    opacity:var(--realisations-bg-opacity, .5);
    transform:scale(1.04);
    z-index:0;pointer-events:none;
  }
  #realisations > .wrap{position:relative;z-index:1;}
  /* v1.52 (demande du client : "le bloc nos realisation tu enleve tu met
     juste une ecrtiture avec une ombre pour que ca soit vsible") : la plaque
     orange derrière le titre/sous-titre est retirée — le texte repose
     directement sur la photo de fond, avec une ombre portée pour rester
     lisible quelle que soit la photo choisie. Même traitement pour les
     flèches du carrousel ("de meme que les fleche orange tu enleve") : plus
     de fond/cercle, juste l'icône orange avec une ombre. */
  #realisations .cat-head > div{
    background:none;color:#fff;padding:0;border-radius:0;box-shadow:none;
    display:block;
  }
  /* v1.54 (demande du client : "c'est pas visible change le couleur de met
     le en orange") : le blanc se voyait mal sur certaines photos de fond —
     passage à l'orange de la marque, plus l'ombre reste pour la lisibilité. */
  /* v1.84 (demande du client : "Quelques réalisations ENLEVE OMBRE") — ombre
     retirée du titre ("text-shadow" supprimé) ; à l'époque, gardée sur le
     sous-titre juste en dessous (non concerné par cette demande-là).
     v3.61 (demande du client : "sur quelque realisations le sous titres il
     a une ombre tu enleve ombre") — cette fois le sous-titre EST concerné :
     son "text-shadow" est retiré ci-dessous (voir "#realisations .cat-head
     p"), pour le même rendu plat que le titre depuis la v1.84. */

  /* ===== BANDE PHOTO PLEIN CADRE, DÉGRADÉ MODIFIABLE (v3.39) =====
     Demande du client : "je veux que tu copier cette style pour bop sur le
     page service tu ajoute une photo arriere plan et tu met du degrader
     modifiable sur personnaliser" — d'après la section "NOTTO" tout en bas
     de l'accueil de yayediabtp.com (photo plein cadre + dégradé de couleur
     par-dessus + titre/texte/bouton recentrés). Même principe que le fond
     flouté de "#realisations" plus haut (photo en "background-image" réglée
     en ligne par le template, voir page-services.php) : une seule COULEUR
     réglée depuis Personnaliser sert à construire tout le dégradé via
     "color-mix()", pas besoin de régler 2 couleurs séparées pour un début et
     une fin de dégradé. Sans photo réglée, la bande reste quand même
     entièrement colorée (pas de fond blanc/vide) grâce au repli "none" sur
     ".photo-band::before" (aucune image) combiné au dégradé de
     ".photo-band::after", qui lui ne dépend pas de la photo. */
  .photo-band{position:relative;overflow:hidden;padding:96px 0;text-align:center;}
  .photo-band::before{
    content:'';position:absolute;inset:-10px;
    background-image:var(--photo-band-img, none);
    background-size:cover;background-position:center;
    transform:scale(1.04);
  }
  .photo-band::after{
    content:'';position:absolute;inset:0;
    background:linear-gradient(165deg, color-mix(in srgb, var(--photo-band-bg, var(--orange)) 62%, black) 0%, color-mix(in srgb, var(--photo-band-bg, var(--orange)) 38%, transparent) 100%);
  }
  .photo-band-inner{position:relative;z-index:1;max-width:720px;margin:0 auto;padding:0 24px;}
  .photo-band h2{color:#fff;text-shadow:0 2px 12px rgba(0,0,0,.35);}
  .photo-band p{color:rgba(255,255,255,.92);margin-top:14px;font-size:17px;}
  .photo-band .btn{margin-top:28px;}
  @media (max-width:700px){.photo-band{padding:64px 0;}}
  #realisations .cat-head h2{color:var(--orange);margin-bottom:8px;}
  /* v1.55 (demande du client : "ce sous titre tu le met en bleu") */
  #realisations .cat-head p{color:var(--blue);margin-bottom:0;}

  /* v1.68 (demande du client : "l'entete les ecritures tu le met en orange
     et ajoute de ombre derriere ou effet 3d") — même traitement que le titre
     "Quelques réalisations" de l'accueil ci-dessus, appliqué ici au titre de
     la page "Toutes nos réalisations" (fond blanc, donc une ombre plus
     légère : sur une photo, l'ombre sert à rester lisible ; ici, elle sert
     juste d'effet de relief). */
  #toutes-realisations .cat-head h2{color:var(--orange);margin-bottom:8px;text-shadow:2px 3px 0 rgba(7,22,39,0.18), 4px 6px 14px rgba(7,22,39,0.22);}
  /* box-shadow:none + ::before désactivé : sans ça le cercle/liseré et le
     glacis (voir .cat-arrow::before, partagé avec d'autres boutons) restent
     visibles même avec un fond transparent — on veut vraiment juste l'icône. */
  #realisations.has-bg .cat-arrow{background:none;border:none;box-shadow:none;color:var(--orange);filter:drop-shadow(0 3px 8px rgba(0,0,0,0.45));}
  #realisations.has-bg .cat-arrow::before{content:none;}
  #realisations.has-bg .cat-arrow:hover{background:none;color:var(--orange-deep);}

  /* ===== CATALOGUE ACCUEIL — carrousel "coverflow" façon gravupub.com =====
     Une vignette centrale à plat, mise en avant, les autres reculent et
     s'inclinent en 3D de part et d'autre en se chevauchant partiellement
     (positionnement calculé en JS, voir assets/js/front-page.js) — plutôt
     qu'une bande qui défile à plat. Uniquement le carrousel de l'accueil :
     les grilles des pages catégorie / "Nos réalisations" (".cat-grid"/
     ".cat-card" seuls, plus haut) gardent l'ancien style à plat en grille. */
  /* Vignettes plus grandes, avec plus d'air autour ("c'est trop collé") —
     et surtout, la photo n'est plus recouverte par le bandeau de nom :
     l'image occupe toute sa vignette, le bandeau bleu vient APRÈS elle
     (en dessous, pas par-dessus), exactement comme sur gravupub.com. */
  .cat-carousel{
    position:relative;overflow:hidden;margin-bottom:24px;
    height:685px; /* place pour la vignette (photo + bandeau) + son reflet + la marge du "cadre" actif */
    -webkit-mask-image:linear-gradient(to right, transparent, #000 6%, #000 94%, transparent);
    mask-image:linear-gradient(to right, transparent, #000 6%, #000 94%, transparent);
  }
  /* v1.85 (demande du client, code copié depuis les DevTools de
     dislabwestafrica.com/brochure/ à l'appui — "c'est cette même
     configuration que je veux") : mêmes valeurs exactes que ".pages-wrap"
     sur cette page-là — perspective plus profonde (2400px, était 1600px) et
     surtout "perspective-origin" décalé vers le haut (50% 42% au lieu du
     centre par défaut, 50% 50%) : la "caméra" vise un peu au-dessus du
     centre de la vignette active, ce qui incline légèrement plus les
     vignettes latérales vers le bas — c'est ce détail qui donne l'effet
     "pages d'un livre vues d'en face" du site de référence. */
  .cat-carousel .cat-grid{
    display:block;margin-bottom:0;position:relative;width:100%;height:100%;
    perspective:2400px;perspective-origin:50% 42%;
  }
  /* Vignettes agrandies ("augmente les blocs") — 360px (était 320px, puis 280px). */
  .cat-carousel .cat-card{
    position:absolute;top:50%;left:50%;width:360px;margin:-274px 0 0 -180px; /* -largeur/2, -(hauteur photo + bandeau)/2 */
    display:flex;flex-direction:column;
    border:none;border-radius:14px;background:transparent;cursor:pointer;overflow:hidden;
    box-shadow:0 22px 40px -12px rgba(5,16,36,0.5);
    transition:transform .5s cubic-bezier(.22,.8,.28,1), box-shadow .3s ease, opacity .4s ease, filter .5s ease;
    -webkit-box-reflect:below 4px linear-gradient(rgba(255,255,255,.16), transparent 28%);
  }
  /* "Cadre" blanc qui met en valeur la vignette centrale, active — voir la
     classe ".is-active" ajoutée/retirée en JS sur la vignette du milieu. */
  .cat-carousel .cat-card.is-active{box-shadow:0 0 0 5px #fff, 0 30px 55px -10px rgba(5,16,36,0.6);}
  .cat-carousel .cat-visual{aspect-ratio:3/4;border-radius:0;flex-shrink:0;}
  @media (max-width:700px){
    .cat-carousel{height:500px;}
    .cat-carousel .cat-card{width:240px;margin:-194px 0 0 -120px;}
  }
  /* Bandeau de nom EN DESSOUS de la photo (jamais par-dessus) — un bloc uni,
     comme sur gravupub.com, pas un voile transparent posé sur l'image. */
  .cat-carousel .cat-overlay{
    position:static;inset:auto;opacity:1;display:flex;align-items:center;justify-content:flex-start;text-align:left;
    padding:16px 18px;min-height:68px;flex-shrink:0;
    background:var(--blue);
  }
  .cat-carousel .cat-overlay-name{font-size:16px;transform:none;}
  .gallery-strip{background:var(--paper-alt);}

  /* ===== LOGOS CLIENTS ("Ils nous ont fait confiance") — carrousel défilant =====
     v3.61 (demande du client : "le logo des client tu le met en carrousel")
     — remplace l'ancienne grille statique (".logo-grid", figée, ne
     défilait pas) : ".logo-carousel" est la fenêtre visible (overflow
     masqué, dégradé de fondu sur les bords pour ne pas couper les logos
     net) et ".logo-track" la piste, deux fois plus large que sa fenêtre
     (elle contient chaque logo en double, voir assets/js/front-page.js),
     animée en boucle de 0 à -50% de sa largeur — donc un défilement continu
     et sans fin, en pause au survol/au doigt pour pouvoir regarder un logo
     précis (voir ".logo-carousel:hover"/":active" plus bas). */
  .logo-carousel{
    overflow:hidden;position:relative;
    -webkit-mask-image:linear-gradient(90deg, transparent 0, #000 64px, #000 calc(100% - 64px), transparent 100%);
    mask-image:linear-gradient(90deg, transparent 0, #000 64px, #000 calc(100% - 64px), transparent 100%);
  }
  .logo-track{display:flex;gap:16px;width:max-content;animation:logoScroll linear infinite;}
  .logo-carousel:hover .logo-track, .logo-carousel:active .logo-track{animation-play-state:paused;}
  @keyframes logoScroll{from{transform:translateX(0);}to{transform:translateX(-50%);}}
  /* Personne n'a demandé de mouvement constant à l'écran : on respecte le
     réglage "Réduire les animations" du système d'exploitation/navigateur. */
  @media (prefers-reduced-motion: reduce){.logo-track{animation:none;}}
  .logo-tile{
    background:#fff;border:1px solid var(--line);border-radius:12px;height:110px;
    flex:0 0 200px;
    display:flex;align-items:center;justify-content:center;padding:16px;text-align:center;
    transition:transform .25s ease, box-shadow .25s ease, border-color .25s ease;
  }
  .logo-tile:hover{transform:translateY(-4px);box-shadow:var(--shadow-glow);border-color:transparent;}
  /* v3.14 (demande du client : "ne met pas les logo en gris laisse le
     comme ils sont") — logos clients affichés directement en couleur,
     plus de noir et blanc au repos (avant : gris + un peu transparent,
     et seule la couleur apparaissait au survol). */
  .logo-tile img{max-width:100%;max-height:56px;object-fit:contain;}
  .logo-tile-text{font-weight:800;font-size:13px;letter-spacing:.01em;text-transform:uppercase;color:var(--grey);transition:color .25s ease;}
  .logo-tile:hover .logo-tile-text{color:var(--blue);}
  @media (max-width:560px){.logo-tile{height:90px;flex-basis:150px;}}
  /* Points de pagination sous le carrousel — un point par réalisation,
     cliquable, le point de la vignette active en orange (voir
     dislabwestafrica.com/brochure/). */
  .cat-dots{display:flex;justify-content:center;gap:9px;margin:6px 0 28px;position:relative;z-index:1;}
  .cat-dot{width:9px;height:9px;border-radius:50%;border:none;padding:0;background:var(--line);cursor:pointer;transition:background .25s ease, transform .25s ease;}
  .cat-dot.is-active{background:var(--orange);transform:scale(1.25);}
  .cat-cta{text-align:center;position:relative;z-index:1;}

  /* Agrandissement en grand d'une réalisation (clic sur la vignette centrale
     du carrousel) — fenêtre plein écran par-dessus tout le site, fermée par
     défaut (display:none) pour ne jamais ajouter d'espace dans la page ;
     voir assets/js/front-page.js pour l'ouverture/fermeture. */
  .cat-lightbox{
    display:none;position:fixed;inset:0;z-index:200;
    background:rgba(6,14,30,0.92);
    align-items:center;justify-content:center;padding:40px 20px;
    opacity:0;transition:opacity .25s ease;
  }
  .cat-lightbox.is-open{display:flex;opacity:1;}
  .cat-lightbox-figure{margin:0;max-width:min(90vw,900px);max-height:88vh;display:flex;flex-direction:column;align-items:center;gap:14px;}
  .cat-lightbox-figure img{max-width:100%;max-height:78vh;width:auto;height:auto;object-fit:contain;border-radius:10px;box-shadow:0 30px 70px -15px rgba(0,0,0,0.6);background:#fff;}
  .cat-lightbox-figure figcaption{color:#fff;font-weight:700;font-size:15px;text-align:center;}
  .cat-lightbox-close{
    position:absolute;top:22px;right:26px;width:44px;height:44px;border-radius:50%;
    background:rgba(255,255,255,0.12);border:1px solid rgba(255,255,255,0.3);color:#fff;
    font-size:26px;line-height:1;cursor:pointer;display:flex;align-items:center;justify-content:center;
    transition:background .2s ease, transform .2s ease;
  }
  .cat-lightbox-close:hover{background:rgba(255,255,255,0.25);transform:scale(1.06);}
  @media (max-width:700px){
    .cat-lightbox{padding:24px 14px;}
    .cat-lightbox-close{top:14px;right:14px;width:38px;height:38px;font-size:22px;}
  }

  /* ===== FOOTER ===== */
  footer{background:var(--ink);color:rgba(255,255,255,0.62);padding:70px 0 0;position:relative;overflow:hidden;}
  /* v3.6 (demande du client : "une image derriere avec une tres faible
     opacite") — photo facultative (Personnaliser > Pied de page), en
     position absolue derrière tout le contenu (z-index 0) ; ".wrap"
     ci-dessous repasse au premier plan pour ne jamais être caché dessous. */
  footer .footer-bg{position:absolute;inset:0;z-index:0;background-size:cover;background-position:center;pointer-events:none;}
  /* v3.59 (retour du client, comparaison avec le pied de page d'un autre
     site : "tu as trop laissé les 2 côtés vide avec beaucoup de space") —
     le pied de page réutilisait ".wrap" (max-width:1200px, voir tout en
     haut de ce fichier), la même largeur que le contenu des pages : sur un
     grand écran, ça laissait deux bandes vides très larges de chaque côté.
     Élargi ICI UNIQUEMENT (sélecteur "footer > .wrap", donc sans toucher au
     ".wrap" du reste du site) pour que le pied de page utilise mieux la
     largeur de l'écran, comme sur le site comparé. */
  footer > .wrap{position:relative;z-index:1;max-width:1600px;}
  .footer-grid{display:grid;grid-template-columns:1.3fr 0.8fr 1fr 1.1fr;gap:44px;padding-bottom:46px;}
  .footer-brand .brand{color:#fff;margin-bottom:16px;}
  /* Le nom "BOP IMPRESSION & SERVICES" écrit dans le logo est en bleu foncé :
     bien lisible sur fond blanc (en-tête), mais illisible sur le fond bleu
     du pied de page. Plutôt qu'une plaque blanche derrière tout le logo, on
     ajoute juste une fine lueur/contour blanc qui suit le contour du logo
     (plusieurs drop-shadow superposées, sans flou fort, pour un liseré net
     plutôt qu'un halo flou) — ça détache le bleu du fond sans ajouter de
     bloc/forme visible autour. */
  .footer-brand .brand-mark{filter:drop-shadow(0 0 1px #fff) drop-shadow(0 0 1px #fff) drop-shadow(0 0 2px #fff) drop-shadow(0 0 2px #fff);}
  .footer-brand p{font-size:14px;color:rgba(255,255,255,0.55);max-width:300px;}
  .footer-social{display:flex;gap:10px;margin-top:18px;}
  .footer-social a{width:36px;height:36px;border-radius:50%;background:rgba(255,255,255,0.08);display:flex;align-items:center;justify-content:center;transition:background .2s ease, transform .2s ease;}
  .footer-social a:hover{background:linear-gradient(120deg,var(--blue),var(--orange));transform:translateY(-2px);}
  .footer-social svg{width:15px;height:15px;color:#fff;}
  footer h4{color:#fff;font-size:13px;letter-spacing:.06em;text-transform:uppercase;margin-bottom:18px;}
  footer .flinks li{margin-bottom:10px;}
  footer .flinks a{font-size:14px;color:rgba(255,255,255,0.65);}
  footer .flinks a:hover{color:var(--orange);}
  .footer-contact li{display:flex;gap:10px;align-items:flex-start;font-size:14px;margin-bottom:14px;color:rgba(255,255,255,0.65);}
  .footer-contact svg{width:16px;height:16px;color:#fff;flex-shrink:0;margin-top:3px;}
  /* v3.59 : ligne "téléphone(s)" du pied de page — une icône, un ou
     plusieurs numéros à sa droite séparés par " / " (voir assets/js/site.js).
     Remplace l'ancienne règle ".footer-contact li a{display:flex;...}" (pour
     l'ancien balisage "un seul atelier = un seul <a>" qui n'existe plus) —
     elle s'appliquait aussi, par erreur, aux nouveaux liens de numéros
     ci-dessous (même sélecteur générique "li a") et les transformait chacun
     en bloc flex séparé, ce qui isolait le "/" tout seul sur sa propre
     ligne. "white-space:nowrap" sur chaque numéro et sur le séparateur
     évite qu'un numéro ou le "/" se coupe au milieu à l'étroit (mobile). */
  .footer-contact li .footer-phone-group{display:flex;align-items:flex-start;gap:10px;flex-wrap:wrap;}
  .footer-contact li .footer-phone-group .phone-sep{white-space:nowrap;}
  .footer-contact li .footer-phone-group a{display:inline;color:inherit;text-decoration:none;white-space:nowrap;}
  .footer-contact li .footer-phone-group a:hover{color:#fff;}
  .footer-bottom{border-top:1px solid rgba(255,255,255,0.12);padding:22px 0;display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:12px;font-size:12.5px;}
  /* v3.58 : mention "Conçu par..." dans le pied de page (voir footer.php). */
  .footer-credit{color:rgba(255,255,255,0.45);}
  .footer-credit a{color:inherit;text-decoration:none;}
  .footer-credit a:hover{color:var(--orange);}
  @media (max-width:900px){.footer-grid{grid-template-columns:1fr 1fr;}}
  @media (max-width:560px){.footer-grid{grid-template-columns:1fr;}}

  /* ===== REVEAL ===== */
  .reveal{opacity:0;transform:translateY(24px);transition:opacity .6s ease, transform .6s ease;}
  .reveal.in-view{opacity:1;transform:translateY(0);}
  /* ---------- Variante "glisse depuis la gauche" (utilisée sur les
       kickers/titres de page-a-propos.php v2.3, demande du client : "tu
       ajoute de effet ... et mouvement meme sur les textes") — s'ajoute
       TOUJOURS en plus de ".reveal" sur le même élément (jamais seule) :
       comme ".in-view" est ajouté par le JS sur l'élément ".reveal"
       lui-même (voir site.js), un élément qui n'aurait que ".reveal-left"
       sans ".reveal" ne recevrait jamais ".in-view" et resterait invisible
       pour toujours. La double classe ".reveal.reveal-left" a une
       spécificité plus forte que ".reveal" seul, donc ce glissement
       horizontal remplace bien le glissement vertical par défaut. ---- */
  .reveal.reveal-left{transform:translateX(-32px);}
  .reveal.reveal-left.in-view{transform:translateX(0);}
  @media (max-width:700px){.reveal.reveal-left{transform:translateX(-16px);}}

  @media (max-width:900px){#contactGrid{grid-template-columns:1fr !important;}}

  /* ===== BANNIÈRE DE PAGE CATÉGORIE ===== */
  /* Même annulation que .hero-wrap ci-dessus, pour les bannières des autres pages. */
  /* v1.88 (demande du client : "est ce qu'on a les memes tailles de
     bannieres") — non : "min-height" laissait chaque bannière grandir selon
     la longueur de SON titre/texte (modifiables indépendamment depuis
     v1.87), donc Contact (582px, texte plus long) et Nos réalisations
     (509px) n'avaient pas la même hauteur. "height" fixe : toutes les
     bannières de page font maintenant exactement la même taille, quel que
     soit leur texte.
     v3.37 (remarque du client : "la taille de notre accueil est plus grand
     que les autres pages") — 600px → 780px, pour retomber exactement sur
     la même hauteur que le hero de l'accueil (voir ".hero" plus haut,
     passé à 780px en v3.36) : accueil et bannières de page font maintenant
     toutes la même taille. */
  .cat-banner{position:relative;height:780px;display:flex;align-items:center;overflow:hidden;background:var(--cat-banner-bg, var(--ink));margin-top:calc(-1 * var(--header-h, 140px));}
  /* v1.89 : demande du client — plusieurs photos qui défilent en fondu
     (diaporama) au lieu d'une seule photo fixe, plus un arrière-plan propre
     à chaque page (voir --cat-banner-bg, réglé en ligne par chaque template)
     qui reste visible pendant les fondus. Fonctionne aussi avec une seule
     photo (pas d'animation grâce à ":only-child" — comportement identique
     à avant pour les pages qui n'ont qu'une photo). */
  .cat-banner-media{position:absolute;inset:0;}
  /* v3.27 (demande du client : "les banniere image tous entiere doit etre
     visible") — chaque photo vit maintenant dans son propre petit
     conteneur ("cat-banner-photo-slide", voir bop_render_banner_photos()
     dans inc/template-tags.php) qui porte l'animation de fondu ; à
     l'intérieur, la photo s'affiche EN ENTIER ("cat-banner-photo",
     object-fit:contain) par-dessus une copie floutée agrandie d'elle-même
     ("cat-banner-blur-bg") qui comble tout vide autour d'elle. */
  .cat-banner-photo-slide{position:absolute;inset:0;opacity:0;animation:bopBannerFade 21s ease-in-out infinite;}
  .cat-banner-photo-slide:only-child{opacity:1;animation:none;}
  .cat-banner-photo-slide:nth-of-type(1){animation-delay:0s;}
  .cat-banner-photo-slide:nth-of-type(2){animation-delay:7s;}
  .cat-banner-photo-slide:nth-of-type(3){animation-delay:14s;}
  .cat-banner-blur-bg{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block;filter:blur(35px) brightness(0.7);transform:scale(1.15);}
  .cat-banner-photo{position:relative;z-index:1;width:100%;height:100%;object-fit:contain;display:block;}
  @keyframes bopBannerFade{
    0%{opacity:0;}
    5%{opacity:1;}
    28%{opacity:1;}
    34%{opacity:0;}
    100%{opacity:0;}
  }
  .cat-banner-overlay{position:absolute;inset:0;background:linear-gradient(100deg, rgba(7,22,39,0.88) 25%, rgba(7,22,39,0.6) 65%, rgba(7,22,39,0.35) 100%);}
  /* v2.8 (demande du client : "le couleur bleu tu dimunie opacite") — bleu
     moins dense/opaque qu'avant : mélangé avec du blanc (pas seulement du
     transparent, puisqu'il n'y a pas de photo derrière sur cette variante
     "no-photo" — Contact notamment) pour un bleu plus clair/doux, et le
     dégradé vers le bleu marine de fin s'arrête à une opacité plus faible. */
  .cat-banner.no-photo .cat-banner-overlay{background:linear-gradient(120deg, color-mix(in srgb, var(--banner-color, var(--cat-banner-bg, var(--blue))) 55%, white), rgba(7,22,39,0.55));}
  /* v1.92 (demande du client : "on doit avoir une photo en arriere plan dont
     on diminue opacite plus le fond orange et les objet en premiere plan")
     — sur les bannières "no-photo" (Services/Réalisations/Produits), le
     fond n'est plus une couleur 100% plate : une des photos déjà réglées
     pour la page défile en fondu tout en bas (comme avant v1.90), mais très
     estompée, sous un voile orange — les produits/mug restent eux nets, au
     premier plan, avec leur ombre automatique (".cat-banner-stickers" plus
     bas), inchangés. */
  /* v3.32 (le client trouve les bannières "pas professionnelles") — la
     photo de fond, réduite au fil des demandes précédentes jusqu'à 0.4
     d'opacité, ne se voyait presque plus : combinée à un fond de bannière
     très pâle (souvent proche du blanc), ça donnait une bannière plate et
     terne au lieu d'un vrai visuel. Remontée à 0.75 pour que la photo soit
     clairement visible — le voile de couleur ci-dessous continue d'assurer
     la lisibilité du texte par-dessus. */
  .cat-banner.no-photo .cat-banner-media{opacity:.75;}
  /* v2.8 (demande du client : "le couleur bleu tu dimunie opacite") — teinte
     bleue plus légère qu'avant (60% → 35%) sur les bannières avec photo en
     fond (Qui sommes-nous, Services, Réalisations, Produits), pour laisser
     plus voir la photo derrière au lieu d'un bleu trop couvrant. */
  .cat-banner.no-photo.has-bg-photo .cat-banner-overlay{background:linear-gradient(120deg, color-mix(in srgb, var(--banner-color, var(--cat-banner-bg, var(--blue))) 35%, transparent), rgba(7,22,39,0.3));}
  /* v3.54 (demande du client : "pour la page contact ou mettre arriere plan
     du photo de hero") — la règle juste au-dessus réglait déjà le voile de
     couleur pour un état "no-photo" + "has-bg-photo" en même temps, mais
     aucun template ne posait ces deux classes ensemble jusqu'ici (toujours
     l'une OU l'autre) : rien ne posait encore la photo elle-même. Ajoutée
     ici, seulement pour ce cas précis (page-contact.php, désormais seule
     page à combiner les deux classes) — les bannières Services/Réalisations/
     Produits/fiche service, qui n'utilisent jamais "no-photo" ET
     "has-bg-photo" en même temps, ne sont pas concernées. */
  .cat-banner-warm.has-bg-photo.no-photo{background-image:var(--cat-banner-fixed-bg, none);background-size:cover;background-position:center;}
  .cat-banner::after{
    content:'';position:absolute;top:0;left:-40%;width:30%;height:100%;z-index:1;pointer-events:none;
    background:linear-gradient(115deg, rgba(255,255,255,0) 0%, rgba(255,255,255,0.10) 50%, rgba(255,255,255,0) 100%);
    transform:skewX(-20deg);animation:bannerShine 7s ease-in-out infinite;
  }
  @keyframes bannerShine{0%{left:-40%;}45%{left:120%;}100%{left:120%;}}
  /* v1.93 (demande du client : "pourquoi les texte ne peut etre a gauche
     tout a fait a gauche vers extremite") — avant, ce bloc était posé dans
     un ".wrap" (max-width:1200px, centré) : sur un grand écran ça laissait
     ~144px de vide avant le texte. On retire le ".wrap" (voir les modèles
     de page) et on aligne le texte sur le MÊME repère que le logo dans
     l'en-tête (".nav{padding:14px 32px}") — 32px du vrai bord de l'écran,
     pas du cadre centré.
     v1.94 (demande du client : "pourquoi ton nos realisaons en deux lignes
     et les textes mettre a gauche mais le coler comme ca soit raisonnable")
     — 32px collait un peu trop court à l'arête de l'écran ; on respire un
     peu plus (48px). */
  .cat-banner-inner{position:relative;z-index:2;max-width:640px;padding:64px 32px 64px 48px;}
  /* v1.90 (demande du client : "arriere plan... juste le mug et apres les
     autres image qui doit etre diapo, les ecritures gene met le tout a
     gauche") — quand la bannière a un visuel fixe ("sticker", ex : le mug
     qui flotte sur "Nos produits"), le texte est resserré plus franchement
     à gauche pour ne jamais chevaucher ce visuel, qui reste immobile
     pendant que les VRAIES PHOTOS derrière lui continuent de défiler.
     v1.94 : 480px était trop juste pour un titre plus long ("Toutes nos
     réalisations") qui retombait sur deux lignes — élargi à 600px (le
     sticker reste sur la droite, largeur encore inchangée, donc pas de
     chevauchement) pour que les titres tiennent sur une seule ligne. */
  .cat-banner.has-sticker .cat-banner-inner{max-width:600px;}
  .cat-banner.has-sticker p{max-width:560px;}
  /* v1.90 (demande du client : "sur les object qu'on a ajouter apres chaque
     produits doit avoir de ombre automatique") — l'ombre n'est JAMAIS
     dessinée dans la photo elle-même : elle vient uniquement de ce filtre
     CSS, appliqué automatiquement à n'importe quelle image posée dans
     ".cat-banner-stickers" (photo détourée façon mug, ou simple photo
     rectangulaire) — pas de retouche manuelle à refaire à chaque nouveau
     produit ajouté. */
  .cat-banner-stickers{position:absolute;z-index:1;right:2%;bottom:0;width:38%;height:88%;pointer-events:none;}
  .cat-banner-sticker{
    position:absolute;right:0;bottom:0;
    max-width:100%;max-height:100%;width:auto;height:auto;
    filter:drop-shadow(0 20px 30px rgba(10,6,3,.35));
  }
  /* Une seule image = immobile (ex : le mug seul) ; plusieurs = elles
     défilent en fondu entre elles, comme les photos de fond (voir
     ".cat-banner-media img" plus haut) — demande du client : "arriere plan
     du hero doit etre fixe cest les produit comme mug qui se defile". */
  .cat-banner-stickers img{opacity:0;animation:bopBannerFade 21s ease-in-out infinite;}
  .cat-banner-stickers img:only-child{opacity:1;animation:none;}
  .cat-banner-stickers img:nth-of-type(1){animation-delay:0s;}
  .cat-banner-stickers img:nth-of-type(2){animation-delay:7s;}
  .cat-banner-stickers img:nth-of-type(3){animation-delay:14s;}
  /* v1.90 (demande du client : "sur mobile ne doit pas cacher", puis
     "c'est trop petit sur mobile") — reste visible sur mobile, poussé dans
     le coin bas-droit sous le texte pour ne jamais le recouvrir. La
     bannière (".has-sticker" seulement — les autres pages ne changent pas)
     est agrandie sur mobile pour lui laisser assez de place pour être
     grand, pas juste un petit coin caché. */
  @media (max-width:900px){
    .cat-banner.has-sticker{height:760px;}
    .cat-banner-stickers{width:58%;height:30%;right:4%;bottom:5%;}
  }
  @media (max-width:480px){
    .cat-banner.has-sticker{height:720px;}
    .cat-banner-stickers{width:62%;height:26%;}
  }
  .cat-banner .breadcrumb{color:rgba(255,255,255,0.7);font-size:13px;margin-bottom:16px;}
  .cat-banner .breadcrumb a{text-decoration:underline;color:rgba(255,255,255,0.85);}
  /* v1.88 : "line-clamp" en filet de sécurité — le titre/texte est modifiable
     depuis Personnaliser (v1.87) ; si un texte plus long que prévu est tapé
     un jour, il se coupe proprement avec "…" au lieu de déborder de la
     bannière à hauteur fixe (voir ".cat-banner" plus haut) ou de la faire
     grandir et casser l'uniformité entre les pages. */
  /* v2.7 (demande du client : "les grand titres sur les pages tu met sur
     une couleurs d'orange") — orange (déjà la couleur de tous les liens du
     menu et de plusieurs accents du site) au lieu de blanc, pour ressortir
     sur le nouveau fond bleu des bannières (voir bop_*_banner_bgcolor dans
     inc/defaults.php). Légère ombre sombre ajoutée pour rester lisible
     même sur les zones plus claires des photos en fond. */
  .cat-banner h1{color:var(--orange);text-shadow:0 2px 12px rgba(0,0,0,0.4);font-size:clamp(28px,4vw,42px);margin-bottom:14px;line-height:1.15;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden;}
  .cat-banner p{color:rgba(255,255,255,0.88);font-size:15.5px;margin-bottom:24px;max-width:520px;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:3;overflow:hidden;}
  /* v3.15 (demande du client : "le couleur bleu sur le hero des autres page
     realisation service et nos produit essaye d'enlever et retravailler sur
     le couleur du texte des hero") — Services/Réalisations/Produits
     reviennent à un fond ORANGE (voir inc/defaults.php,
     "bop_*_banner_bgcolor"), donc le titre ne peut plus rester orange lui
     aussi (orange sur orange, illisible) : titre repassé en blanc, comme
     sur la bulle orange du bandeau d'accueil (".hero-ticker-caption-text").
     ".cat-banner-warm" est posée uniquement sur ces 3 pages (voir
     page-services.php / page-realisations.php / page-produits.php) — la
     page Contact, non concernée par cette demande, garde son bandeau bleu
     et son titre orange d'origine. */
  .cat-banner-warm h1{color:#fff;}
  /* v3.16 : essai sans couleur (gris + photos en noir et blanc) — étape
     intermédiaire, remplacée juste en dessous.
     v3.17 (demande du client, après avoir envoyé une capture d'un bleu
     pâle : "utilise ce bleu") — ce bleu pâle (#EAF1FA, voir
     inc/defaults.php) remplace le gris neutre de l'essai précédent ; les
     photos redeviennent en couleur (l'essai "sans aucune couleur" est
     terminé, on a maintenant une vraie couleur à appliquer) et le voile
     sombre repasse sur le bleu marine du reste du site (au lieu du gris
     neutre de l'essai v3.16), pour rester cohérent avec les autres
     bannières (Contact, etc.) qui utilisent toutes ce même bleu marine en
     dessous de leur teinte. */
  .cat-banner-warm.has-bg-photo .cat-banner-overlay{background:linear-gradient(120deg, color-mix(in srgb, var(--cat-banner-bg, #0856AE) 35%, transparent), rgba(7,22,39,0.5));}
  /* v3.20/v3.21 (historique) : ce fond était un bleu PÂLE (#EAF1FA) —
     "55% blanc" éclaircissait encore ce bleu pâle pour un dégradé tout en
     douceur, avec un texte marine foncé pour rester lisible dessus.
     v3.42 (demande du client : "tous doit etre bleu avec du ecritures
     blanc") — le fond passe à un bleu FRANC (voir inc/defaults.php,
     "bop_*_banner_bgcolor" = "#0856AE" désormais) : ce mélange à 55% de
     blanc aurait reblanchi ce bleu franc et fait réapparaître le même effet
     "délavé/gris" que le client signale. Il est retiré — le dégradé part
     directement de la couleur réglée (déjà un vrai bleu) vers le marine
     plus sombre, sans jamais éclaircir. */
  .cat-banner-warm:not(.has-bg-photo) .cat-banner-overlay{background:linear-gradient(120deg, var(--cat-banner-bg, #0856AE), rgba(7,22,39,0.55));}
  /* v3.42 : texte repassé en BLANC (comme la toute première version, v3.15)
     — le fond n'est plus un bleu pâle nécessitant un texte foncé pour
     contraster (voir juste au-dessus) : avec un bleu FRANC partout,
     du blanc est à la fois plus lisible et plus cohérent avec la variante
     "photo" juste en dessous (".cat-banner-warm.has-bg-photo:not(.no-photo)
     h1/p"), déjà en blanc — un seul traitement de texte pour toute la
     bannière, avec ou sans photo. */
  .cat-banner-warm h1{color:#fff;text-shadow:0 2px 12px rgba(0,0,0,0.35);}
  .cat-banner-warm p{color:rgba(255,255,255,0.88);}
  /* v3.28 (demande du client : "pour page services individuelle toujours
     les ecriture sur le hero n'est pas visible en plus ... il faut le
     mettre a droite et dimunuer encore la taille des images") — deux bugs
     corrigés ensemble ici, à l'origine sur LA FICHE SERVICE INDIVIDUELLE
     UNIQUEMENT quand elle a une photo (".cat-banner-warm.has-bg-photo" SANS
     ".no-photo" — à l'époque, les 3 pages Services/Réalisations/Produits
     gardaient TOUJOURS ".no-photo" avec cette même classe, donc n'étaient
     pas concernées). v3.33 (demande du client : "fait la meme chose pour
     les autres hero tous doit avoir le meme designe") — Services,
     Réalisations et Produits ne posent plus ".no-photo" dès qu'une photo
     est réglée (voir leurs fichiers .php) : ce même habillage s'applique
     donc maintenant partout où il y a une photo, plus seulement ici :
     1) Le texte restait en bleu marine foncé (".cat-banner-warm h1/p"
        juste au-dessus, pensé pour le fond BLEU PÂLE des 3 pages
        "no-photo") alors que cette bannière-ci garde un voile beaucoup
        plus sombre (".cat-banner-warm.has-bg-photo .cat-banner-overlay"
        plus haut) — texte foncé sur fond foncé, illisible quelle que soit
        la photo choisie.
     2) La photo n'est plus un fond plein cadre derrière le texte (où un
        simple assombrissement ne suffisait jamais à garantir un texte
        lisible, quelle que soit la photo) : elle passe à DROITE, dans un
        cadre plus petit et net — comme ".service-grid" ailleurs sur le
        site — pendant que le texte reste à GAUCHE sur le fond bleu uni de
        la bannière, en blanc (repris du style de base ".cat-banner h1/p"
        plus haut, comme les autres bannières à fond uni). Rien à changer
        côté PHP : ".cat-banner-media"/".cat-banner-photo-slide" existent
        déjà, on les repositionne juste en CSS. */
  .cat-banner-warm.has-bg-photo:not(.no-photo){justify-content:space-between;gap:5%;padding:0 5%;}
  /* v3.41 (demande du client : "l'arriere plan doit avoir une seul photo
     fixe" + "tous doit etre identique") — cette bannière n'avait jusqu'ici
     qu'un fond de COULEUR unie (le voile ".cat-banner-overlay" était caché :
     pas besoin d'assombrir un fond uni). Une vraie photo de fond fixe
     ("--cat-banner-fixed-bg", réglée en ligne par chaque template) vient
     s'ajouter derrière : le voile est donc réactivé pour la teinter et
     garder le texte lisible par-dessus, avec le MÊME dégradé (même formule
     "color-mix") que partout ailleurs sur le site — c'est justement cette
     formule commune, à partir d'une seule couleur par page (déjà identique
     par défaut sur Services/Réalisations/Produits), qui garantit un rendu
     identique d'une page à l'autre. Sans photo de fond réglée,
     "--cat-banner-fixed-bg" reste "none" : la bannière garde son fond de
     couleur unie, inchangé. */
  .cat-banner-warm.has-bg-photo:not(.no-photo){background-image:var(--cat-banner-fixed-bg, none);background-size:cover;background-position:center;}
  .cat-banner-warm.has-bg-photo:not(.no-photo) .cat-banner-overlay{display:block;background:linear-gradient(120deg, color-mix(in srgb, var(--cat-banner-bg, #EAF1FA) 45%, transparent), rgba(7,22,39,.55));}
  /* v3.32 (bug trouvé en vérifiant les bannières : "Vos supports du
     quotidien, imprimés avec précision" s'affichait coupé en "...imprimés…")
     — ".cat-banner h1"/".cat-banner p" (plus haut) limitent le titre/texte
     à 2/3 lignes par défaut ("-webkit-line-clamp", pensé comme filet de
     sécurité), mais la colonne resserrée de cette variante (voir
     ".cat-banner-inner" juste en dessous) fait retomber un texte normal sur
     plus de lignes que prévu — un correctif ligne par ligne (v3.32/v3.33)
     s'est avéré fragile (recoupé différemment sur mobile, colonne encore
     plus étroite). v3.34 : clamp désactivé complètement ici — le titre et
     le texte s'affichent toujours en entier, quelle que soit leur longueur
     ou la largeur d'écran ; largement assez de hauteur disponible dans
     cette bannière pour ça. */
  .cat-banner-warm.has-bg-photo:not(.no-photo) h1{color:#fff;text-shadow:0 2px 12px rgba(0,0,0,0.4);display:block;-webkit-line-clamp:unset;overflow:visible;}
  .cat-banner-warm.has-bg-photo:not(.no-photo) p{color:rgba(255,255,255,0.88);display:block;-webkit-line-clamp:unset;overflow:visible;}
  /* v3.34 (demande du client : "tu fait descendre un peut les ecritures" —
     après retrait du bouton "Demander un devis") — la bannière centre son
     contenu verticalement (".cat-banner{align-items:center}") : ce bloc de
     texte, un peu plus court sans le bouton, remontait donc légèrement.
     "margin-top" (sans marge en bas) le repousse un peu plus bas tout en
     restant centré dans l'espace disponible. */
  .cat-banner-warm.has-bg-photo:not(.no-photo) .cat-banner-inner{order:1;flex:0 1 460px;padding:0;max-width:460px;margin-top:56px;}
  /* v3.33 : carte agrandie (320px → 380px) et fond flouté retiré : la photo
     reste entière ("object-fit:contain", jamais coupée) mais ses marges
     vides affichaient un simple fond blanc uni, dans un cadre (comme
     ".product-media" etc.).
     v3.40 (demande du client, après avoir vu la section "NOTTO" de
     yayediabtp.com — une photo découpée qui flotte directement sur le fond,
     sans cadre autour : "je veux que tu copier cette style... au lieu de
     mettre sur une bloc pour les hero") — le cadre blanc/l'ombre de carte
     ("border-radius", "overflow:hidden", "box-shadow", "background:#fff")
     sont retirés : la photo flotte maintenant directement sur le fond de la
     bannière, comme sur NOTTO. Elle garde quand même une ombre — pas
     dessinée dans le cadre (qui n'existe plus) mais un vrai
     "filter:drop-shadow()" appliqué à l'image elle-même (juste en dessous,
     ".cat-banner-photo") : il suit la forme RÉELLEMENT visible de la photo,
     pas un rectangle. Résultat identique à NOTTO avec une image détourée
     (fond transparent) ; avec une photo rectangulaire classique (pas
     détourée), la même règle s'applique automatiquement mais l'ombre suit
     alors le bord du rectangle — déjà le principe utilisé pour les anciens
     "stickers" de la page Produits (v1.90, voir ".cat-banner-sticker" plus
     bas, remplacé par ce système en v3.33 puis retrouvé ici). */
  /* v3.44 (demande du client : "les photo de hero en diapo doit etre en
     premier plan rien ne doit etre sur lui") — ".cat-banner-media" (le
     diaporama qui flotte devant) et ".cat-banner-overlay" (le voile dégradé
     qui assombrit la photo de fond FIXE, juste au-dessus) n'avaient ni l'un
     ni l'autre de "z-index" ici : sans ça, l'ordre d'affichage suit l'ordre
     du code HTML, et le voile — placé APRÈS le diaporama dans le code
     (voir page-services.php etc.) — passait donc PAR-DESSUS la photo qui
     flotte, la ternissant/teintant en bleu au lieu de la laisser bien nette
     au premier plan. "z-index:2" la fait maintenant passer devant ce voile
     (le voile continue d'assombrir la photo de fond fixe, juste plus
     derrière). */
  /* v3.51 (demande du client : "les photo qui affiche en diapo sur le page
     rea service etc ce sont ces photo que tu doit augmenter") — photo qui
     flotte devant, sur les bannières Services/Réalisations/Produits/fiche
     service : agrandie de 380px à 460px (280px à 320px sur mobile). */
  .cat-banner-warm.has-bg-photo:not(.no-photo) .cat-banner-media{order:2;position:relative;inset:auto;flex:0 0 460px;width:460px;height:460px;z-index:2;}
  .cat-banner-warm.has-bg-photo:not(.no-photo) .cat-banner-media .cat-banner-blur-bg{display:none;}
  .cat-banner-warm.has-bg-photo:not(.no-photo) .cat-banner-photo{filter:drop-shadow(0 20px 30px rgba(10,6,3,.35));}
  @media (max-width:900px){
    .cat-banner-warm.has-bg-photo:not(.no-photo){flex-wrap:wrap;justify-content:center;padding:100px 5% 32px;height:auto;min-height:600px;text-align:center;}
    .cat-banner-warm.has-bg-photo:not(.no-photo) .cat-banner-inner{max-width:100%;}
    .cat-banner-warm.has-bg-photo:not(.no-photo) .cat-banner-media{flex:0 0 320px;width:320px;height:320px;}
  }
  /* Correction au passage (v3.51) : la règle mobile ci-dessus ne fixait que
     "width"/"height", pas "flex" — sur un élément flex, c'est "flex-basis"
     (dans "flex:0 0 …") qui contrôle la largeur réelle affichée, pas
     "width" seul. Résultat : sur mobile la photo restait large de 460px au
     lieu de rétrécir à 320px (seule la hauteur changeait). Ce bug existait
     déjà avant cette demande (avec 380px/280px) ; corrigé ici en ajoutant
     "flex:0 0 320px" à la règle mobile. */
  /* v3.21 (demande du client : "prend ton temps pour diagnostiquer tous et
     le rendre bien visible") — même bug de fond, ailleurs : DÈS qu'une
     bannière ".cat-banner" passe en ".no-photo" (aucune photo réglée), son
     voile (".cat-banner.no-photo .cat-banner-overlay", voir juste
     au-dessus) mélange TOUJOURS sa couleur avec 55% de blanc — un mélange
     forcément clair, quelle que soit la couleur de départ. Le titre orange
     et le texte blanc par défaut (pensés pour une vraie photo sombre en
     fond) y perdent presque tout leur contraste. Concerné : la bannière
     Contact (page-contact.php, jamais de photo, fond bleu qui s'éclaircit
     donc de la même façon) et une page catégorie qui n'a encore aucun
     produit avec photo (taxonomy-bop_categorie.php) — aucune des deux
     n'utilise ".cat-banner-warm" (déjà couverte par la règle ci-dessus),
     d'où ce second correctif, même principe. */
  .cat-banner.no-photo:not(.cat-banner-warm) h1{color:var(--ink);text-shadow:0 1px 3px rgba(255,255,255,0.55);}
  .cat-banner.no-photo:not(.cat-banner-warm) p{color:var(--ink);}

  /* v3.46/v3.47 : bannière Contact — un essai en couleur pleine unie
     ("cat-banner-contact") est passé par ici puis a été retiré : le client
     voulait en fait l'orange sur les icônes des 3 cartes plus bas
     ("contact-action-icon"), pas sur cette bannière. Cette bannière utilise
     maintenant directement ".cat-banner-warm" (voir page-contact.php),
     donc plus besoin de règles séparées ici. */

  /* ===== BANDE CATÉGORIE ACTIVE (pages catégorie) ===== */
  /* "is-current" (catégorie de la page affichée) : un contour blanc plutôt
     qu'un changement de couleur — la pastille garde sa propre couleur. */
  .catbar-track a.is-current{box-shadow:0 0 0 2px rgba(255,255,255,0.85) inset;border-color:#fff;}

  /* ===== BLOC "CE QUE NOUS PROPOSONS" (pages catégorie) ===== */
  .cat-offer{max-width:640px;margin:0 auto 8px;}
  .cat-offer-list{display:grid;grid-template-columns:1fr 1fr;gap:14px;}
  .cat-offer-list li{display:flex;gap:10px;align-items:flex-start;font-size:14.5px;font-weight:600;color:var(--ink);}
  .cat-offer-list svg{width:16px;height:16px;color:var(--orange);flex-shrink:0;margin-top:3px;}
  @media (max-width:560px){.cat-offer-list{grid-template-columns:1fr;}}

  /* ===== CTA BAS DE PAGE CATÉGORIE ===== */
  /* v1.59 (demande du client, page "Nos réalisations" : "change la couleur de
     fond de avant footer tu le met en orange cette pas maque de orange") :
     fond orange au lieu de "--ink" (qui vaut en réalité le bleu de la marque
     ici, voir plus haut) — le bouton "Demander un devis" reste bleu
     (.btn-primary), donc le contraste reste bon. La tache floue en haut à
     droite passe au bleu (une tache orange serait invisible sur un fond
     orange). */
  .cat-cta-strip{background:linear-gradient(120deg, var(--orange), var(--orange-deep));text-align:center;position:relative;overflow:hidden;}
  .cat-cta-strip::before{content:'';position:absolute;width:240px;height:240px;border-radius:50%;filter:blur(60px);opacity:.22;background:var(--blue);top:-80px;right:-60px;pointer-events:none;}
  .cat-cta-strip h2{color:#fff;}
  .cat-cta-strip p{color:rgba(255,255,255,0.75);max-width:520px;margin:0 auto 26px;}

  /* ===== v1.97 : "Comment ça marche" (page Services) =====
     demande du client : "tu ameliore le contenu du page" — bande de 4
     étapes, style simple assorti aux cartes déjà utilisées ailleurs sur le
     site (fond ".paper-alt", coins arrondis). */
  .process-steps{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:22px;margin-top:32px;}
  .process-step{background:var(--paper-alt);border-radius:var(--radius);padding:28px 24px;}
  .process-step-num{font-size:36px;font-weight:800;color:var(--blue);opacity:.3;line-height:1;margin-bottom:10px;}
  .process-step h3{font-size:18px;margin:0 0 8px;color:var(--ink);}
  .process-step p{margin:0;color:var(--grey);font-size:14.5px;line-height:1.5;}

  /* ===== v1.97 : bande "chiffres clés" (page Nos réalisations) =====
     Reprend les mêmes réglages que l'accueil (Personnaliser > Accueil —
     Chiffres clés), affichage statique (pas d'animation de comptage ici,
     pour rester simple). */
  .mini-stats-strip{background:var(--paper-alt);}
  .mini-stats{display:flex;flex-wrap:wrap;justify-content:center;gap:36px 48px;padding:36px 0;}
  /* v3.43 (demande du client : "cette designe sur nos realisation ... cest
     trop simple") — les 4 blocs apparaissaient tous d'un coup avec
     ".reveal" (transition générique, sans délai). Même principe de cascade
     que ".flip-grid .flip-card:nth-child" plus haut : cette règle, plus
     précise que ".reveal", ajoute un délai ("--reveal-delay") réglable par
     bloc SANS retirer l'apparition en fondu/glissement déjà en place. */
  .mini-stat{text-align:center;min-width:130px;transition:opacity .6s ease var(--reveal-delay,0s), transform .6s ease var(--reveal-delay,0s);}
  .mini-stats .mini-stat:nth-child(2){--reveal-delay:.12s;}
  .mini-stats .mini-stat:nth-child(3){--reveal-delay:.24s;}
  .mini-stats .mini-stat:nth-child(4){--reveal-delay:.36s;}
  .mini-stat-num{font-size:32px;font-weight:800;color:var(--blue);line-height:1;}
  .mini-stat-label{font-size:13px;color:var(--grey);text-transform:uppercase;letter-spacing:.03em;margin-top:6px;}

  /* ===== FICHE PRODUIT (single-bop_produit.php) ===== */
  .product-breadcrumb{padding-top:28px;font-size:13px;color:var(--grey);}
  .product-breadcrumb a{color:var(--blue);text-decoration:underline;}
  .product-hero{padding:28px 0 76px;}
  .product-hero-grid{display:grid;grid-template-columns:1fr 1fr;gap:56px;align-items:start;}
  /* v3.31 : fond blanc (comme les autres cadres photo du site). */
  .product-media{border-radius:var(--radius);overflow:hidden;box-shadow:var(--shadow-3d);aspect-ratio:4/3;background:#fff;}
  .product-media img{width:100%;height:100%;object-fit:contain;display:block;}
  .product-tag{display:inline-block;background:var(--paper-alt);color:var(--blue);font-weight:800;font-size:12px;letter-spacing:.04em;padding:7px 14px;border-radius:100px;margin-bottom:14px;text-decoration:none;transition:background .2s ease;}
  .product-tag:hover{background:var(--line);}
  .product-info h1{font-size:clamp(26px,3.4vw,36px);margin-bottom:16px;}
  .product-desc{color:var(--grey);font-size:15.5px;line-height:1.7;margin-bottom:28px;}
  .product-desc p:last-child{margin-bottom:0;}
  .product-cta{margin-top:6px;}

  /* v1.67 : 2e bouton CTA "WhatsApp" à côté de "Demander un devis"
     (le site de référence evocom-sn.com propose systématiquement les deux). */
  .product-cta-row{display:flex;flex-wrap:wrap;gap:14px;margin-top:6px;}
  .product-cta-row .product-cta{margin-top:0;}
  .btn-whatsapp{display:inline-flex;align-items:center;gap:8px;background:linear-gradient(120deg,#25d366,#1da851);color:#fff;box-shadow:0 10px 26px rgba(37,211,102,0.35);}
  .btn-whatsapp:hover{transform:translateY(-2px);box-shadow:0 16px 34px rgba(37,211,102,0.45);}
  .btn-whatsapp svg{width:18px;height:18px;flex-shrink:0;}

  /* Groupes d'options (Format / Papier / Finition...) — boutons cliquables
     purement indicatifs : ce site n'a pas de panier, le choix se confirme
     par WhatsApp ou le formulaire de devis (voir bop_render_product_options). */
  .prod-options{display:flex;flex-direction:column;gap:20px;margin-bottom:30px;padding-top:22px;border-top:1px solid var(--line);}
  .prod-option-label{display:block;font-weight:800;font-size:12px;letter-spacing:.05em;color:var(--grey);text-transform:uppercase;margin-bottom:10px;}
  .prod-option-choices{display:flex;flex-wrap:wrap;gap:9px;}
  .prod-pill{background:#fff;border:1.5px solid var(--line);color:var(--ink);font-weight:700;font-size:13.5px;padding:10px 16px;border-radius:100px;cursor:pointer;transition:border-color .2s ease, background .2s ease, color .2s ease;}
  .prod-pill:hover{border-color:var(--blue);}
  .prod-pill.is-active{background:var(--blue);border-color:var(--blue);color:#fff;}

  /* Caractéristiques techniques (Format / Papier / Finition...) — petite
     fiche technique en label/valeur, juste au-dessus du bouton "Demander un
     devis" (voir bop_render_product_specs, v1.65). */
  .prod-specs{display:grid;grid-template-columns:1fr 1fr;gap:16px 24px;margin-bottom:30px;padding-top:22px;border-top:1px solid var(--line);}
  .prod-specs-heading{grid-column:1/-1;font-weight:800;font-size:12px;letter-spacing:.05em;color:var(--grey);text-transform:uppercase;margin:0 0 2px;}
  .prod-spec{display:flex;flex-direction:column;gap:2px;}
  .prod-spec-label{font-size:12px;color:var(--grey);font-weight:600;}
  .prod-spec-value{font-size:14.5px;color:var(--ink);font-weight:700;}
  @media (max-width:560px){.prod-specs{grid-template-columns:1fr;}}

  @media (max-width:900px){
    .product-hero-grid{grid-template-columns:1fr;gap:26px;}
  }

  /* ===== "PRODUITS SIMILAIRES" (bas de fiche produit) ===== */
  .related-products{background:var(--paper-alt);padding:60px 0 76px;}
  .related-products h2{margin-bottom:26px;}
  .related-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:20px;}
  .related-card{background:#fff;border:1px solid var(--line);border-radius:var(--radius);overflow:hidden;display:flex;flex-direction:column;padding:0 0 18px;text-decoration:none;color:inherit;transition:transform .25s ease, box-shadow .25s ease;}
  .related-card:hover{transform:translateY(-6px);box-shadow:var(--shadow-glow);}
  /* v3.31 : fond blanc (comme les autres cadres photo du site). */
  /* v3.57 (demande du client : "les photo ce coupe") — la carte entière
     (".related-card") a les coins arrondis + "overflow:hidden" ; sans marge
     ici, une photo qui remplit tout le cadre carré (le cas typique d'un
     visuel produit carré, sans marge blanche naturelle) se faisait rogner
     PAR l'arrondi lui-même dans les 2 coins du haut. Petite marge intérieure
     ajoutée : l'arrondi ne mord plus que sur la marge blanche, jamais sur la
     photo elle-même. */
  .related-card-media{aspect-ratio:1/1;background:#fff;overflow:hidden;padding:10px;box-sizing:border-box;}
  .related-card-media img{width:100%;height:100%;object-fit:contain;display:block;}
  .related-card-tag{color:var(--blue);font-weight:800;font-size:11px;letter-spacing:.04em;text-transform:uppercase;margin:16px 18px 4px;}
  .related-card-name{font-weight:800;font-size:15px;color:var(--ink);margin:0 18px 8px;}
  /* v3.56 (demande du client : "le categorie ne doit pas afficher la bas")
     — sur la page Nos produits, la carte n'a plus d'étiquette de catégorie
     au-dessus du titre (retirée dans page-produits.php) : le titre reprend
     ici l'espace que l'étiquette laissait au-dessus de lui (16px), pour ne
     pas coller directement sous la photo. Les cartes "Produits similaires"
     de la fiche produit gardent leur étiquette et leur espacement d'origine
     (pas concernées, elles ne sont pas dans "#tous-les-produits"). */
  #tous-les-produits .related-card-name{margin-top:16px;}
  .related-card-link{display:flex;align-items:center;gap:6px;color:var(--orange);font-weight:700;font-size:13px;margin:auto 18px 0;}
  .related-card-link svg{width:14px;height:14px;transition:transform .2s ease;}
  .related-card:hover .related-card-link svg{transform:translateX(3px);}
  @media (max-width:1000px){.related-grid{grid-template-columns:1fr 1fr;}}
  @media (max-width:560px){.related-grid{grid-template-columns:1fr;}}

  /* ===== PAGE "TOUTES NOS RÉALISATIONS" (realisations.html) ===== */
  .realisations-jump{display:flex;flex-wrap:wrap;gap:10px;}
  .realisations-jump a{font-size:13px;font-weight:700;color:var(--ink);background:var(--paper-alt);border:1px solid var(--line);border-radius:100px;padding:9px 18px;transition:background .2s ease, color .2s ease;}
  .realisations-jump a:hover{background:var(--ink);color:#fff;}
  .realisations-cat-block{padding-top:56px;margin-top:56px;border-top:1px solid var(--line);}
  .realisations-cat-block:first-child{padding-top:0;margin-top:0;border-top:none;}
  .realisations-cat-title{font-size:24px;font-weight:800;color:var(--ink);margin:0 0 24px;}

  /* ===== PAGE "TOUS NOS PRODUITS" (v1.66) =====
     Reprend la mise en page à 2 colonnes du catalogue de référence donné par
     le client (evocom-sn.com/produits.html) : une colonne de gauche "sticky",
     collée au bord GAUCHE DE L'ÉCRAN — donc ".produits-shell" ci-dessous
     n'utilise volontairement PAS ".wrap" (qui centre à 1200px max et laisse
     une grande marge vide de chaque côté sur un grand écran : c'est cette
     marge que le client a signalée en capture, il veut la colonne poussée
     tout à gauche) — qui liste toutes les familles ET, sous chacune, ses
     produits (lien direct vers chaque fiche produit, comme sur le site de
     référence). Chaque famille sert aussi de FILTRE cliquable (voir
     bopProduitsFilter() dans assets/js/site.js) : un clic n'affiche plus que
     sa section à droite et se surligne en bleu (".is-active") tant qu'elle
     reste sélectionnée. À droite : une section par famille avec le nombre de
     produits et une grille de fiches (image + catégorie + nom + "Voir le
     produit", classes .related-* déjà utilisées en bas de fiche produit —
     donc pas de nouveau style de carte à créer). */
  .produits-shell{width:100%;padding:0 24px 0 16px;}
  .produits-layout{display:grid;grid-template-columns:260px 1fr;gap:32px;align-items:start;}
  .produits-sidebar{position:sticky;top:24px;max-height:calc(100vh - 48px);overflow-y:auto;background:#fff;border:1px solid var(--line);border-radius:var(--radius);padding:8px 0;}
  .produits-sidebar-heading{display:flex;align-items:center;justify-content:space-between;gap:8px;font-size:12px;font-weight:800;letter-spacing:.06em;text-transform:uppercase;color:var(--grey);padding:14px 20px 12px;}
  .produits-sidebar-reset{display:none;background:none;border:none;color:var(--blue);font-weight:700;font-size:11.5px;letter-spacing:normal;text-transform:none;cursor:pointer;padding:0;font-family:inherit;}
  .produits-sidebar-reset:hover{text-decoration:underline;}
  [data-bop-produits-filter].has-filter .produits-sidebar-reset{display:inline;}
  .produits-sidebar-group{border-top:1px solid var(--line);}
  .produits-sidebar-group:first-of-type{border-top:none;}
  .produits-sidebar-cat{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:12px 20px;font-weight:700;font-size:14px;color:var(--ink);text-decoration:none;border-left:3px solid transparent;transition:color .15s ease, background .15s ease, border-color .15s ease;}
  .produits-sidebar-cat:hover{color:var(--blue);}
  .produits-sidebar-cat.is-active{color:var(--blue);background:var(--paper-alt);border-left-color:var(--blue);}
  .produits-sidebar-count{background:var(--paper-alt);color:var(--grey);font-size:11px;font-weight:700;border-radius:100px;padding:2px 9px;flex-shrink:0;}
  .produits-sidebar-cat.is-active .produits-sidebar-count{background:#fff;color:var(--blue);}
  .produits-sidebar-items{display:flex;flex-direction:column;padding:0 20px 14px;}
  .produits-sidebar-items a{font-size:13px;color:var(--grey);text-decoration:none;padding:5px 0 5px 14px;border-left:2px solid var(--line);transition:color .15s ease, border-color .15s ease;}
  .produits-sidebar-items a:hover{color:var(--blue);border-color:var(--blue);}
  .produits-main{min-width:0;}
  .produits-cat-block{padding-top:52px;margin-top:52px;border-top:1px solid var(--line);}
  .produits-cat-block:first-child{padding-top:0;margin-top:0;border-top:none;}
  .produits-cat-block.is-filtered-out{display:none;}
  .produits-cat-head{display:flex;align-items:center;justify-content:space-between;gap:16px;margin-bottom:24px;flex-wrap:wrap;}
  .produits-cat-head h2{margin:0;}
  .produits-cat-count{background:var(--paper-alt);color:var(--ink);font-weight:700;font-size:12.5px;border-radius:100px;padding:6px 14px;flex-shrink:0;white-space:nowrap;}

  /* Responsive : sous 900px la colonne passe au-dessus, en barre horizontale
     "sticky" qui défile elle-même sur le côté plutôt que de pousser tout le
     contenu vers le bas — reste utilisable au pouce sur mobile. */
  @media (max-width:900px){
    .produits-shell{padding:0 16px;}
    .produits-layout{grid-template-columns:1fr;gap:20px;}
    /* v3.64 : "overscroll-behavior-x:contain" — sans ça, sur iOS/Android,
       arriver au bout de cette bande qui défile sur le côté (au pouce)
       "renvoie" le geste de défilement à toute la page, qui semblait alors
       "bouger"/sauter horizontalement au lieu de rester stable. */
    .produits-sidebar{position:static;max-height:none;width:100%;display:flex;overflow-x:auto;overflow-y:visible;overscroll-behavior-x:contain;padding:6px;gap:2px;-webkit-overflow-scrolling:touch;}
    .produits-sidebar-heading{display:none;}
    .produits-sidebar-group{border-top:none;flex-shrink:0;}
    .produits-sidebar-group + .produits-sidebar-group{border-left:1px solid var(--line);}
    .produits-sidebar-cat{white-space:nowrap;border-left:none;border-bottom:3px solid transparent;}
    .produits-sidebar-cat.is-active{background:none;border-left-color:transparent;border-bottom-color:var(--blue);}
    .produits-sidebar-items{display:none;}
  }
  @media (max-width:560px){
    .produits-cat-head{gap:10px;}
  }

  /* ===== ÉTAT "PHOTOS À VENIR" (pages catégorie sans photo encore) ===== */
  .cat-empty{max-width:560px;margin:0 auto 46px;text-align:center;background:var(--paper-alt);border:1px dashed var(--line);border-radius:var(--radius);padding:30px 28px;}
  .cat-empty p{margin:0;font-size:14.5px;}

  /* ===== WIDGET WHATSAPP FLOTTANT (toutes les pages) =====
     Bouton pilule en bas à droite ; au clic, ouvre un petit panneau listant
     l'équipe commerciale (voir SALES_TEAM dans site.js). Chaque personne du
     panneau ouvre directement SA conversation WhatsApp. */
  /* Défile avec la page (plus fixe à l'écran) : placé juste après le pied de
     page dans le HTML (voir footer.php), il apparaît donc en bas de page,
     aligné à droite, comme le reste du contenu.
     Important : le panneau (.wa-panel) est en position absolute, ancré au
     bouton — sinon, même invisible (opacity:0), il continuait à occuper sa
     place dans la mise en page une fois le widget "en flux" (plus fixed),
     ce qui laissait une grande bande blanche vide après le pied de page.
     .wa-zone (fond bleu du pied de page) enveloppe le bouton : sans ça, les
     marges du bouton lui-même laissaient voir le fond clair de <body> juste
     après le bleu du footer — exactement la "bande blanche" toujours signalée
     en bas de page. Avec .wa-zone en bleu, plus aucun fond clair n'apparaît
     entre le pied de page et la vraie fin du site. */
  .wa-zone{background:var(--ink);}
  /* v1.77 (demande du client : "sur le footer ca decale le droite sur
     extermite") — ".wrap" est normalement limité à 1200px et centré (voir
     plus haut), donc "justify-content:flex-end" ne poussait le bouton que
     jusqu'au bord de cette colonne centrée, pas jusqu'au vrai bord de
     l'écran (visible sur les écrans larges). Même technique "plein écran"
     que les flèches du carrousel accueil : on sort ".wrap" de sa largeur
     max ici pour atteindre le bord réel de la fenêtre. */
  .wa-zone .wrap{display:flex;justify-content:flex-end;width:100vw;max-width:100vw;position:relative;left:50%;transform:translateX(-50%);padding:20px 32px;}
  .wa-widget{position:relative;z-index:80;display:flex;flex-direction:column;align-items:flex-end;width:max-content;}
  .wa-toggle{display:flex;align-items:center;gap:10px;background:linear-gradient(120deg,var(--orange),var(--orange-deep));color:#fff;border:none;cursor:pointer;font-family:inherit;padding:14px 22px 14px 16px;border-radius:100px;font-weight:700;font-size:14px;box-shadow:0 10px 30px rgba(255,122,41,0.4);transition:transform .25s ease, box-shadow .25s ease;}
  .wa-toggle svg{width:22px;height:22px;flex-shrink:0;}
  .wa-toggle:hover{transform:translateY(-3px);box-shadow:0 14px 36px rgba(255,122,41,0.5);}
  .wa-widget.is-open .wa-toggle{transform:none;}
  .wa-panel{position:absolute;right:0;bottom:calc(100% + 14px);width:290px;max-width:calc(100vw - 32px);background:#fff;border-radius:var(--radius);box-shadow:var(--shadow-lg);overflow:hidden;opacity:0;visibility:hidden;transform:translateY(14px) scale(.98);transform-origin:bottom right;pointer-events:none;transition:opacity .2s ease, transform .2s ease, visibility .2s;}
  .wa-widget.is-open .wa-panel{opacity:1;visibility:visible;transform:translateY(0) scale(1);pointer-events:auto;}
  .wa-panel-head{padding:18px 20px 14px;border-bottom:1px solid var(--line);}
  .wa-panel-head strong{display:block;font-size:15px;color:var(--ink);margin-bottom:4px;}
  .wa-panel-head p{font-size:13px;color:var(--grey);margin:0;line-height:1.5;}
  .wa-panel-list{max-height:300px;overflow-y:auto;}
  .wa-person{display:flex;align-items:center;gap:12px;padding:14px 20px;text-decoration:none;border-bottom:1px solid var(--line);transition:background .15s ease;}
  .wa-person:last-child{border-bottom:none;}
  .wa-person:hover{background:var(--paper-alt);}
  .wa-avatar{position:relative;width:44px;height:44px;border-radius:50%;flex-shrink:0;background:linear-gradient(120deg,var(--blue),var(--blue-deep));display:flex;align-items:center;justify-content:center;overflow:hidden;}
  .wa-avatar img{width:100%;height:100%;object-fit:cover;display:block;}
  .wa-avatar-fallback{color:#fff;font-weight:700;font-size:15px;}
  .wa-avatar-badge{position:absolute;bottom:-2px;right:-2px;width:18px;height:18px;border-radius:50%;background:var(--orange);border:2px solid #fff;display:flex;align-items:center;justify-content:center;color:#fff;}
  .wa-avatar-badge svg{width:10px;height:10px;}
  .wa-person-info{display:flex;flex-direction:column;min-width:0;}
  .wa-person-role{font-size:12px;color:var(--grey);}
  .wa-person-name{font-size:14.5px;font-weight:700;color:var(--ink);}

  /* ---- Aperçu "bulle de discussion" avant l'ouverture de WhatsApp ----
     Affiché quand on clique sur une personne : un message d'accueil (voir
     "greeting" dans SALES_TEAM, site.js) puis un vrai champ de saisie (v1.81,
     demande du client avec capture d'un site concurrent à l'appui — "je veux
     ça") : le client tape SON message ici, et WhatsApp s'ouvre avec CE texte
     (pas juste le "prefill" fixe d'avant). Construit entièrement en JS
     (site.js) — rien à modifier ici pour ajouter/retirer des personnes. */
  .wa-panel-head, .wa-panel-list{transition:opacity .15s ease;}
  .wa-detail{display:none;flex-direction:column;}
  .wa-widget.is-detail .wa-panel-head, .wa-widget.is-detail .wa-panel-list{display:none;}
  .wa-widget.is-detail .wa-detail{display:flex;}
  .wa-detail-head{display:flex;align-items:center;gap:12px;padding:16px 18px;background:linear-gradient(120deg,var(--orange),var(--orange-deep));color:#fff;}
  .wa-detail-back{flex-shrink:0;width:30px;height:30px;border-radius:50%;border:none;background:rgba(255,255,255,0.18);color:#fff;display:flex;align-items:center;justify-content:center;cursor:pointer;transition:background .15s ease;}
  .wa-detail-back:hover{background:rgba(255,255,255,0.32);}
  .wa-detail-back svg{width:16px;height:16px;}
  .wa-detail-who{display:flex;flex-direction:column;min-width:0;flex:1;}
  .wa-detail-who strong{font-size:15px;line-height:1.2;}
  .wa-detail-who span{font-size:12.5px;font-style:italic;opacity:.85;}
  .wa-detail-avatar{flex-shrink:0;width:38px;height:38px;border-radius:50%;overflow:hidden;background:rgba(255,255,255,0.2);display:flex;align-items:center;justify-content:center;font-weight:700;font-size:13px;}
  .wa-detail-avatar img{width:100%;height:100%;object-fit:cover;display:block;}
  .wa-detail-body{background:var(--paper-alt);padding:22px 18px;min-height:140px;}
  .wa-bubble{position:relative;background:linear-gradient(120deg,var(--orange),var(--orange-deep));color:#fff;font-size:14px;line-height:1.5;padding:14px 16px;border-radius:14px;max-width:88%;box-shadow:var(--shadow);}
  .wa-bubble::before{content:'';position:absolute;top:-7px;right:22px;width:14px;height:14px;background:var(--orange);clip-path:polygon(0 100%,100% 100%,50% 0);}
  /* v1.81 : remplace l'ancien bouton unique "Continuer vers WhatsApp"
     (".wa-detail-cta", texte fixe) par une vraie barre de saisie + un
     bouton d'envoi rond, comme sur la référence du client — le texte tapé
     ici devient le message envoyé sur WhatsApp. */
  .wa-detail-footer{display:flex;align-items:center;gap:10px;padding:14px 18px;border-top:1px solid var(--line);background:#fff;}
  .wa-detail-input{flex:1;min-width:0;border:1px solid var(--line);border-radius:100px;padding:11px 18px;font-size:14px;font-family:inherit;color:var(--ink);background:var(--paper-alt);outline:none;transition:border-color .15s ease;}
  .wa-detail-input:focus{border-color:var(--orange);}
  .wa-detail-send{flex-shrink:0;width:42px;height:42px;border-radius:50%;border:none;background:linear-gradient(120deg,var(--orange),var(--orange-deep));color:#fff;display:flex;align-items:center;justify-content:center;cursor:pointer;box-shadow:0 6px 14px rgba(255,122,41,.35);transition:transform .15s ease, box-shadow .15s ease;}
  .wa-detail-send:hover{transform:scale(1.06);box-shadow:0 8px 18px rgba(255,122,41,.45);}
  .wa-detail-send svg{width:18px;height:18px;}
  @media (max-width:700px){
    .wa-toggle{padding:12px 18px 12px 14px;font-size:13px;}
    .wa-toggle svg{width:20px;height:20px;}
  }
  @media (max-width:480px){
    /* Sur petit écran, le bouton devient une simple bulle ronde (comme la
       plupart des widgets WhatsApp) au lieu d'une longue pilule pleine
       largeur : plus discret, et il ne recouvre plus le texte en dessous
       (pied de page, chiffres clés...) quand on défile. */
    .wa-zone .wrap{padding:16px 16px;}
    .wa-toggle{width:56px;height:56px;padding:0;border-radius:50%;justify-content:center;box-shadow:0 8px 22px rgba(255,122,41,0.45);}
    .wa-toggle span{display:none;}
    .wa-toggle svg{width:26px;height:26px;}
    .wa-panel{width:min(320px, calc(100vw - 32px));}
  }

  /* ===== PAGE CONTACT ===== */
  .contact-actions{display:grid;grid-template-columns:repeat(3,1fr);gap:24px;margin-bottom:12px;}
  .contact-action-card{display:block;background:#fff;border:1px solid var(--line);border-radius:var(--radius);padding:32px 28px;text-align:center;transition:transform .25s ease, box-shadow .25s ease, border-color .25s ease;}
  .contact-action-card:hover{transform:translateY(-6px);box-shadow:var(--shadow-glow);border-color:transparent;}
  /* v3.47 (demande du client : "les boules en degrader qui doit etre en
     orange") — dégradé bleu → orange remplacé par le même dégradé orange
     déjà utilisé pour les autres bulles/icônes du site (réseaux sociaux de
     l'en-tête, bouton WhatsApp...) : "var(--orange), var(--orange-deep)". */
  .contact-action-icon{position:relative;overflow:hidden;width:56px;height:56px;border-radius:50%;background:linear-gradient(120deg,var(--orange),var(--orange-deep));display:flex;align-items:center;justify-content:center;margin:0 auto 18px;box-shadow:inset 0 1px 0 rgba(255,255,255,.35), inset 0 -3px 6px rgba(0,0,0,.18), 0 10px 18px rgba(10,30,51,.24);}
  .contact-action-icon svg{width:26px;height:26px;color:#fff;}
  .contact-action-card h3{margin-bottom:8px;}
  .contact-action-card p{font-size:14px;margin-bottom:0;}
  @media (max-width:860px){.contact-actions{grid-template-columns:1fr;}}

  .contact-grid{display:grid;grid-template-columns:.85fr 1.15fr;gap:44px;}
  .contact-info-card{background:var(--ink);border-radius:var(--radius);padding:34px 30px;color:#fff;position:relative;overflow:hidden;}
  .contact-info-card::before{content:'';position:absolute;width:200px;height:200px;border-radius:50%;background:var(--orange);filter:blur(60px);opacity:.2;bottom:-70px;left:-50px;pointer-events:none;}
  .contact-info-card h3{color:#fff;position:relative;z-index:1;}
  .contact-info-card .footer-contact{margin-top:18px;position:relative;z-index:1;}

  /* ---- Ateliers (LOCATIONS, voir site.js) : adresse + téléphone + carte,
     réutilisé à la fois dans le pied de page (toutes les pages) et sur la
     page Contact — les deux ont un fond sombre, d'où le texte clair. ---- */
  .location-item{position:relative;z-index:1;margin-bottom:22px;}
  .location-item:last-child{margin-bottom:0;}
  .location-item h4{color:#fff;font-size:14px;letter-spacing:.04em;text-transform:uppercase;margin-bottom:8px;}
  .location-item p{display:flex;align-items:flex-start;gap:8px;font-size:14px;color:rgba(255,255,255,0.62);margin:0 0 6px;}
  .location-item p svg{width:15px;height:15px;flex-shrink:0;margin-top:2px;}
  .location-item p a{color:rgba(255,255,255,0.85);text-decoration:none;}
  .location-item p a:hover{color:var(--orange);}
  .location-maps-link{display:inline-block;font-size:12.5px;font-weight:700;color:var(--orange);margin-bottom:2px;}
  .location-maps-link:hover{text-decoration:underline;}
  /* Pied de page (version compacte) : le nom de l'atelier EST le lien vers
     sa fiche Google Maps — pas de sous-ligné, juste un survol orange. */
  .location-name-link{text-decoration:none;display:block;}
  .location-name-link:hover h4{color:var(--orange);}
  .location-map{margin-top:10px;border-radius:12px;overflow:hidden;}
  .location-map iframe{width:100%;height:160px;border:0;display:block;}
  .contact-form-card{background:#fff;border:1px solid var(--line);border-radius:var(--radius);padding:34px;box-shadow:var(--shadow);}
  .form-grid{display:grid;grid-template-columns:1fr 1fr;gap:16px;}
  .form-field{grid-column:span 1;margin-bottom:16px;}
  .form-field.full{grid-column:1/-1;}
  .form-field label{font-size:13px;font-weight:700;display:block;margin-bottom:6px;}
  .form-field input,.form-field select,.form-field textarea{
    width:100%;border:1.5px solid var(--line);border-radius:8px;padding:12px 14px;font-family:inherit;font-size:14px;
    transition:border-color .2s ease, box-shadow .2s ease;
  }
  .form-field input:focus,.form-field select:focus,.form-field textarea:focus{outline:none;border-color:var(--blue);box-shadow:0 0 0 3px rgba(19,97,196,0.14);}
  .form-field textarea{min-height:110px;resize:vertical;}
  /* v3.65 : le bouton d'envoi vient maintenant de Contact Form 7 (voir
     inc/contact-form.php, balise "[submit class:btn class:btn-primary ...]")
     — il reprend déjà l'apparence ".btn"/".btn-primary" du reste du site,
     juste besoin de la pleine largeur (avant posée en style="" à même le
     bouton, qui n'existe plus en dur dans ce fichier). */
  .contact-form-card .wpcf7-submit{width:100%;}
  /* Champ-piège anti-spam (voir inc/contact-form.php,
     bop_maybe_create_cf7_form()/wpcf7_spam) : cette case ne doit jamais être
     visible ni atteignable au clavier pour une vraie personne — seul un
     robot qui remplit tous les champs automatiquement la remplira. */
  .bop-hp{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden;}
  @media (max-width:900px){.contact-grid{grid-template-columns:1fr;}.form-grid{grid-template-columns:1fr;}.form-field{grid-column:1/-1;}}

  /* ===== EFFET "3D" SUR LES PASTILLES D'ICÔNES (inspiré de sonacos.sn) =====
     Placé en fin de fichier pour être sûr qu'il s'applique après toutes les
     règles ci-dessus (même sélecteur = le dernier déclaré gagne). Chaque
     badge d'icône colorée (couleur réglée au cas par cas, en ligne, depuis
     l'admin) reçoit un vernis diagonal clair en haut / sombre en bas, en
     plus de l'ombre déjà ajoutée sur chaque badge — comme un petit bouton
     légèrement bombé plutôt qu'un aplat de couleur plat. */
  .services-collage-icon::before,
  .service-icon::before,
  .why-point-icon::before,
  .header-social a::before,
  .contact-action-icon::before,
  .cat-arrow::before{
    content:'';
    position:absolute;
    inset:0;
    background:linear-gradient(145deg, rgba(255,255,255,.4), rgba(255,255,255,0) 45%, rgba(0,0,0,.16) 100%);
    pointer-events:none;
  }
  .services-collage-icon svg,
  .services-collage-icon img,
  .service-icon svg,
  .service-icon img,
  .why-point-icon svg,
  .header-social a svg,
  .contact-action-icon svg,
  .cat-arrow svg{
    position:relative;
    z-index:1;
  }

  /* ===== EFFETS "QUI SOMMES-NOUS" (v1.99), réutilisés depuis v2.0 sur
     Services et Nos réalisations =====
     Créés au départ pour la page "Qui sommes-nous", d'après le parcours
     visuel de https://yayediabtp.com/qui-sommes-nous/ (demande du client :
     "fait moi le meme page ... tous le design les effet et tous") : bandes
     qui alternent fond dégradé chaud / fond clair, grand "mot fantôme" en
     filigrane derrière certains titres, blocs photo+texte qui alternent de
     côté, grille de valeurs avec icônes, et liste d'engagements à côté d'une
     photo pleine hauteur — avec les couleurs, photos et textes propres à
     BOP IMPRESSION, pas ceux du site de référence.
     v2.0 (demande du client : "mettre les memes effet de qui somme nous sur
     la page services et ameliore la page nos realisations cest trop
     bassique") — ces classes ne sont plus spécifiques à "Qui sommes-nous"
     (d'où leur nom générique ".warm-band"/".ghost-word"/etc., pas
     ".apropos-*") : elles servent maintenant aussi sur page-services.php et
     page-realisations.php, avec chaque fois un contenu et des photos propres
     à la page. */

  /* ---------- Bande dégradée (même dégradé chaud que ".about-band" sur
       l'accueil, réutilisé ici pour rester cohérent avec le reste du site)
       avec photo qui alterne de côté (comme ".service-grid.is-reverse"). --- */
  .warm-band{background:linear-gradient(120deg, var(--orange), var(--orange-deep) 60%, #7A3406);position:relative;overflow:hidden;padding:76px 0;}
  .warm-band .kicker{color:#fff;}
  .warm-band h2{color:#fff;}
  .warm-band p{color:rgba(255,255,255,0.92);}
  .warm-band h3{color:#fff;}
  .warm-band-grid{position:relative;z-index:1;display:grid;grid-template-columns:1fr 1fr;gap:56px;align-items:center;}
  .warm-band-grid.is-reverse .warm-band-media{order:2;}
  /* v3.52 (demande du client : "tu applique arriere plan d'un photo sur le 2
     capture") — modificateur ajouté UNIQUEMENT sur la bande "Notre studio de
     création" de page-services.php (classe ".has-photo-bg", posée seulement
     là — ".warm-band" reste inchangée sur "Qui sommes-nous"/"Nos
     réalisations", qui partagent la même classe de base) : une photo plein
     cadre en fond, avec le dégradé orange existant qui devient un voile
     semi-transparent PAR-DESSUS elle pour que le texte blanc reste lisible
     — même construction que ".photo-band" (bandeau retiré de cette page au
     même moment, voir page-services.php). Sans photo réglée, le dégradé
     plein d'origine s'applique quand même ("has-photo-bg" n'est ajoutée que
     si une photo est choisie). */
  .warm-band.has-photo-bg::before{
    content:'';position:absolute;inset:-10px;
    background-image:var(--warm-band-bg-img, none);
    background-size:cover;background-position:center;
    transform:scale(1.04);
  }
  /* v3.53 (demande du client : "ajoute un peu d'orange sur notre studio cela
     va un peu comme marron") — le dernier point du dégradé (côté droit de la
     photo) descendait à 40% d'opacité sur un brun foncé ("#7A3406") : la
     photo (fond bois) transparaissait trop et virait au marron. Remonté à un
     orange plus franc et plus opaque sur toute la largeur — plus de brun du
     tout, juste orange clair → orange soutenu, comme le reste du dégradé
     "--orange"/"--orange-deep" utilisé ailleurs sur le site. */
  .warm-band.has-photo-bg::after{
    content:'';position:absolute;inset:0;
    background:linear-gradient(120deg, color-mix(in srgb, var(--orange) 88%, black) 0%, color-mix(in srgb, var(--orange) 80%, transparent) 55%, color-mix(in srgb, var(--orange-deep) 74%, transparent) 100%);
  }
  /* Même contour animé que ".why-photo"/".about-band-media" ailleurs sur le
     site, en bleu pour trancher sur ce fond orange. */
  .warm-band-media{position:relative;border-radius:var(--radius);padding:5px;overflow:hidden;aspect-ratio:4/3;}
  .warm-band-media::before{
    content:'';position:absolute;inset:0;border-radius:inherit;
    background:conic-gradient(from 0deg, var(--blue), transparent 30%, transparent 70%, var(--blue) 100%);
    animation:why-photo-spin 6s linear infinite;
  }
  /* v3.30 : même correctif "fond noir" que ".about-band-media-frame" plus
     haut. v3.31 (demande du client : "tous les blocs en fond blanc") : fond
     blanc uni au lieu du bleu pâle utilisé en v3.30. */
  .warm-band-media-frame{position:relative;width:100%;height:100%;border-radius:calc(var(--radius) - 3px);overflow:hidden;box-shadow:0 20px 50px rgba(10,30,51,0.18);background:#fff;}
  /* v3.8 (demande du client, d'après https://yayediabtp.com/qui-sommes-nous/ :
     "effet de zoom sur les photo lorsqu'on scroll prend cette modele
     fidelement") — inspection du site de référence : ce n'est PAS une
     apparition figée une fois pour toutes (ce qu'on avait fait en v3.7),
     c'est un zoom CONTINU, recalculé à chaque défilement selon la position
     de la photo dans l'écran (site de référence : Elementor "Motion
     Effects", scale ~1.2 → ~1.0 au fil du scroll). Reproduit ici en JS pur,
     sans transition CSS (qui retarderait chaque mise à jour et casserait
     l'effet continu) — voir assets/js/site.js, "EFFET DE ZOOM AU DÉFILEMENT
     v3.8". Le zoom au survol est retiré : les deux effets sur la même
     propriété (transform) se seraient disputé la main. */
  .warm-band-media-frame img{width:100%;height:100%;object-fit:contain;display:block;will-change:transform;}
  @media (max-width:900px){
    .warm-band-grid{grid-template-columns:1fr;gap:32px;}
    .warm-band-grid.is-reverse .warm-band-media{order:0;}
  }

  /* ---------- Variante "grande photo" de la bande dégradée — demande du
       client (v2.3) : "notre philosophe et le confiance client tu le
       regroupe sur une meme une seul bop et tu met sur le cote droite une
       grand image". Même bande/dégradé que ".warm-band", mais colonne
       photo plus large et qui s'étire sur toute la hauteur du bloc de texte
       (au lieu du format 4/3 fixe de ".warm-band-media"), pour une seule
       grande image bien visible plutôt que deux petites côte à côte. ---- */
  .warm-band-grid-big{position:relative;z-index:1;display:grid;grid-template-columns:1.05fr 0.95fr;gap:56px;align-items:stretch;}
  /* Colonne de texte plus longue (présentation + philosophie + confiance
     client regroupées, v2.3) : centrée verticalement dans sa colonne
     (stretch par ".warm-band-grid-big" ci-dessus, donc aussi haute que la
     photo) pour rester équilibrée même si sa hauteur naturelle n'est pas
     EXACTEMENT celle de la photo — demande du client : "la longueur du
     texte du egale a la longueur du bloc d'image". */
  .warm-band-text-big{display:flex;flex-direction:column;justify-content:center;}
  .warm-band-text-divider{width:100%;height:1px;background:rgba(8,86,174,.14);margin:28px 0;}
  /* v3.1 (retour du client : "la longueur du photo depasse la longueur du
     texte on doit etre sur une meme longueur") — la photo (l'élément
     <img>) était en flux normal avec "height:100%" : dans une grille CSS
     dont la hauteur de ligne est automatique, ce pourcentage ne peut pas se
     résoudre correctement et le navigateur se rabat sur la hauteur NATURELLE
     de la photo (déterminée par ses propres proportions) — c'est ce qui
     faisait dépasser la photo, plus haute que le texte, au lieu de s'arrêter
     à sa hauteur. La photo passe en "position:absolute" (elle sort du flux
     normal, donc elle n'impose plus sa propre hauteur au conteneur) : la
     hauteur du conteneur est maintenant DÉTERMINÉE UNIQUEMENT par
     l'étirement de la grille pour égaler la colonne de texte
     (".warm-band-grid-big" plus haut, "align-items:stretch"), et la photo
     remplit simplement tout l'espace obtenu. */
  /* v3.10 (retour du client : "le photo d'ici na plus effet qui tourne") —
     avant, la photo était directement en "position:absolute;inset:5px" dans
     ce bloc : dès que l'effet de zoom au défilement l'agrandissait (v3.8),
     elle débordait par-dessus la bordure orange (qui n'occupe qu'une bande
     de 5px tout autour) et la cachait entièrement — plus de zoom possible ET
     plus de bordure visible en même temps avec cette construction.
     On reprend maintenant exactement la même construction, déjà utilisée et
     qui fonctionne bien, que ".warm-band-media" / ".warm-band-media-frame"
     plus bas sur cette page (section "Notre identité") : un cadre extérieur
     avec 5px de marge (ici, "padding:5px") qui porte la bordure animée, et
     un cadre intérieur séparé, avec son propre "overflow:hidden", qui
     referme strictement la photo à sa taille normale — la photo peut
     s'agrandir librement à l'intérieur sans jamais déborder sur la bordure. */
  .warm-band-media-big{position:relative;border-radius:var(--radius);overflow:hidden;min-height:460px;align-self:stretch;padding:5px;}
  .warm-band-media-big::before{
    content:'';position:absolute;inset:0;border-radius:inherit;
    /* v3.4 (demande du client) — contour animé en orange ici (au lieu du
       bleu utilisé ailleurs sur fond orange) : cette photo est sur fond
       clair (".info-band"), l'orange s'y voit mieux. */
    background:conic-gradient(from 0deg, var(--orange), transparent 30%, transparent 70%, var(--orange) 100%);
    animation:why-photo-spin 6s linear infinite;
  }
  /* v3.30 : même correctif "fond noir" que ".about-band-media-frame" /
     ".warm-band-media-frame" plus haut. v3.31 : fond blanc uni (au lieu du
     bleu pâle) — demande du client : "tous les blocs en fond blanc".
     v3.60 (retour du client : "le photo est coupé vers le haut et bas par
     un fond blanc") — ce cadre est très haut (toute la hauteur de la
     colonne de texte à côté) : une photo qui n'a pas ce format précis
     laissait ce fond blanc très visible en haut/bas, ressenti comme la
     photo "coupée". Le fond reste défini ici (repli si jamais aucune photo
     n'est réglée), mais il est maintenant recouvert par la copie floutée
     de la photo elle-même (".warm-band-media-blur-bg" juste en dessous)
     dès qu'une photo est présente. */
  .warm-band-media-big-frame{position:relative;width:100%;height:100%;overflow:hidden;border-radius:calc(var(--radius) - 3px);box-shadow:0 20px 50px rgba(10,30,51,0.18);background:#fff;}
  /* v3.60 : copie floutée et agrandie de la même photo, en fond du cadre —
     même principe déjà utilisé ailleurs sur le site pour éviter un cadre
     vide autour d'une photo affichée entière (".hero-blur-bg",
     ".cat-banner-blur-bg", ".collage-blur-bg") : remplit tout l'espace sans
     jamais recadrer la vraie photo au premier plan. */
  .warm-band-media-blur-bg{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;filter:blur(35px) brightness(0.7);transform:scale(1.15);}
  /* v3.8 : même zoom CONTINU lié au défilement que ".warm-band-media-frame"
     plus haut (voir cette note pour le détail) — plus de zoom au survol
     séparé, ni de transition CSS, pour laisser le JS piloter "transform"
     sans qu'une autre règle vienne le retarder ou le disputer.
     v3.60 : exclut ".warm-band-media-blur-bg" (règle juste au-dessus) —
     sinon cette règle plus générique ("tout <img> dans .warm-band-media-big")
     lui aurait aussi imposé "object-fit:contain", annulant son propre effet
     "cover" plein cadre. "position:relative;z-index:1" fait passer la
     vraie photo AU-DESSUS du fond flouté (qui est en "position:absolute",
     donc peint par-dessus le contenu normal sans ce réglage). */
  .warm-band-media-big img:not(.warm-band-media-blur-bg){position:relative;z-index:1;width:100%;height:100%;object-fit:contain;display:block;will-change:transform;}
  @media (max-width:900px){
    .warm-band-grid-big{grid-template-columns:1fr;gap:32px;}
    .warm-band-media-big{min-height:300px;}
    .warm-band-text-divider{margin:22px 0;}
  }

  /* ---------- Variante "fond clair" (même fond que le reste de la page,
       "--paper") pour les blocs texte+photo qui utilisaient auparavant une
       bande dégradée orange puis beige — historique de la demande sur
       "Qui sommes-nous" :
         v2.4 : orange → beige ("le fond ne le met pas en orange fait un
                fond peut etre beige"), sur la seule section
                présentation/philosophie/confiance client.
         v2.5 : la section "Notre identité" repasse aussi en beige (retour
                du client : "sa reste toujours" — avoir une bande orange ET
                une bande beige sur la même page cassait la cohérence).
         v2.6 : "trop de couleur sur cette page est qu'on va pas rester sur
                une seul fond le bleu clai qui sur la section nos valeurs"
                — on arrête les essais de teinte à part : ".info-band"
                utilise exactement le même fond que "Nos valeurs"/"Nos
                engagements" (et que le corps du site), plus de dégradé
                orange NI beige. Les couleurs de texte/icônes reprennent
                donc simplement les couleurs "fond clair" utilisées partout
                ailleurs sur le site (h2/h3 en "--ink", p en "--grey" —
                déjà les couleurs par défaut, pas de règle supplémentaire
                nécessaire pour elles). Les classes structurelles
                ".warm-band-grid-big"/".warm-band-media-big"/etc. restent
                utilisées pour la mise en page (texte+grande photo) — seul
                le fond change. ---- */
  .info-band{background:var(--paper);}
  .info-band .kicker{color:var(--orange);}
  /* v3.4 (demande du client : "identite le filigrane en orange") — seule
     ".info-band" avec un mot en filigrane est la section "Notre identité"
     (voir page-a-propos.php) : orange au lieu du bleu/ink d'origine. */
  .info-band .ghost-word{color:var(--orange);opacity:.07;}

  /* v3.4 (demande du client, page "Qui sommes-nous") — le titre de la
     section fusionnée "présentation + philosophie + confiance client"
     ("BOP IMPRESSION, imprimeur de confiance au Sénégal") repasse en bleu,
     au lieu de l'orange utilisé par défaut sur tous les h2 du site. */
  .warm-band-text-big > h2{color:var(--blue);}

  /* v3.4 (demande du client : "nos valeurs ... leur filigrane doit etre
     egalement en orange de meme que le ces blocs") — le titre "Nos
     valeurs" est déjà orange (règle h2 générale du site) ; son filigrane
     et les 3 icônes de ses cartes passent aussi en orange, pour une
     section entièrement dans le même ton. Voir aussi page-a-propos.php
     pour les 3 couleurs d'icône. */
  #apropos-valeurs .ghost-word{color:var(--orange);}

  /* v3.4 (demande du client : "nos engagement leut titres doits rester
     bleu et leus filigrane") — "Nos engagements" garde son titre ET son
     filigrane en bleu, pour trancher avec "Nos valeurs" (orange) juste
     au-dessus. */
  #apropos-engagements h2{color:var(--blue);}
  #apropos-engagements .ghost-word{color:var(--blue);}

  /* ---------- "Mot fantôme" géant en filigrane derrière un titre (effet vu
       sur la page de référence : un très grand mot très pâle, superposé
       juste derrière un sous-titre plus petit et bien contrasté). ---- */
  .ghost-wrap{position:relative;isolation:isolate;max-width:640px;}
  .ghost-word{position:absolute;top:-0.42em;left:-0.03em;z-index:0;margin:0;font-size:clamp(56px,9vw,128px);font-weight:800;line-height:1;letter-spacing:-0.02em;white-space:nowrap;color:currentColor;opacity:.10;pointer-events:none;user-select:none;}
  .ghost-wrap .kicker,.ghost-wrap h2{position:relative;z-index:1;}
  .warm-band .ghost-word{color:#fff;opacity:.14;}
  @media (max-width:700px){.ghost-word{font-size:clamp(38px,13vw,72px);}}

  /* ---------- Liste à puces (utilisée dans le bloc "savoir-faire reconnu",
       à côté d'une photo — même grille que ".service-grid"). ---- */
  .bullet-list{display:flex;flex-direction:column;gap:16px;margin:22px 0 28px;padding:0;list-style:none;}
  .bullet-list li{display:flex;gap:12px;align-items:flex-start;font-weight:700;font-size:15px;color:var(--ink);}
  .bullet-list li svg{flex-shrink:0;width:22px;height:22px;margin-top:1px;color:var(--orange);}
  .bullet-list li span{font-weight:400;color:var(--grey);}
  /* Sur fond dégradé chaud (".warm-band", ex : bloc "confiance client"
     fusionné, page-a-propos.php v2.3) : les couleurs ci-dessus (pensées
     pour un fond clair) deviendraient illisibles — on repasse tout en
     blanc/quasi-blanc, et l'icône reçoit un petit cercle clair derrière
     elle pour rester visible sur l'orange. */
  .warm-band .bullet-list li{color:#fff;}
  .warm-band .bullet-list li span{color:rgba(255,255,255,.86);}
  .warm-band .bullet-list li svg{color:#fff;background:rgba(255,255,255,.2);border-radius:50%;padding:4px;box-sizing:border-box;}

  /* ---------- Cartes "flip" (icône + titre au recto, description au verso)
       — effet vu sur https://dislab.fr/qui-sommes-nous-chez-dislab-sas-france/
       (demande du client : "fait moi la meme chose sur la partie nos
       valeurs ... de meme chose pour engagement") : au survol, le recto
       glisse vers le bas tandis que le verso (fond bleu) glisse depuis le
       haut — un "push", pas une rotation 3D, comme sur la page de référence.
       Utilisées pour "Nos valeurs" et "Nos engagements" (voir
       page-a-propos.php). Sans survol possible (mobile/tactile), le recto
       reste affiché — l'essentiel (icône + titre) est toujours visible. ---- */
  .flip-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:28px;margin-top:8px;}
  /* v2.3 : la transition ci-dessous ne portait avant QUE sur "box-shadow" —
     comme ".flip-card" a aussi la classe ".reveal" (voir page-a-propos.php),
     cette règle, posée plus bas dans le fichier, écrasait complètement la
     transition opacity/transform de ".reveal" (même propriété CSS, dernière
     déclaration gagne) : l'apparition au défilement ne s'animait donc pas
     du tout sur les cartes, elle "sautait" directement à son état final.
     On regroupe maintenant les 3 transitions dans la même déclaration, avec
     un délai ("--reveal-delay") réglable par carte pour un effet de cascade
     (voir ".flip-grid .flip-card:nth-child" plus bas) sans retarder aussi le
     survol (box-shadow n'a pas ce délai). */
  /* v3.10/v3.11 (retour du client : "parfois on a ce probleme sur ces
     bloc" / "cest toujours pas regler lorsque je touche sur une blocs
     autre s'ouvre") — recherche approfondie (reproduit et isolé dans des
     pages de test minimales) : dès que cette carte se trouve sur une page
     qui utilise l'effet "apparition au défilement" (".reveal", utilisé
     partout sur le site) — MÊME si l'animation est terminée depuis
     longtemps et que la carte est parfaitement immobile — certains
     navigateurs se "souviennent" que cette carte est capable de faire une
     transition d'opacité, et se mettent alors à mal découper (clipper) le
     verso de la carte pendant qu'IL glisse (au survol) : le verso apparaît
     alors brièvement à un endroit qui ne correspond pas à la carte, comme
     si "un autre bloc s'ouvrait". Essayé sans succès : "contain:paint",
     "clip-path", "overflow:clip", forcer une couche GPU séparée
     ("will-change"/"translateZ(0)"), déplacer l'apparition au défilement
     sur un cadre englobant séparé — le problème venait dans tous les cas
     du glissement ("transform:translateY(...)") utilisé pour cacher le
     verso, PAS du découpage en lui-même.
     Solution robuste : le verso ne glisse plus hors du cadre, il
     s'EFFACE/APPARAÎT en fondu (opacité) exactement à la même place que le
     recto. Comme rien ne sort jamais du cadre de la carte, il n'y a plus
     rien à mal découper, quel que soit le navigateur ou l'état de la page —
     le problème est réglé à la racine plutôt que contourné. */
  .flip-card{position:relative;overflow:hidden;border-radius:var(--radius);box-shadow:var(--shadow-3d);min-height:230px;background:#fff;transition:opacity .6s ease var(--reveal-delay,0s), transform .6s ease var(--reveal-delay,0s), box-shadow .3s ease;}
  .flip-card:hover{box-shadow:var(--shadow-3d-hover);}
  /* Cascade : chaque carte d'une même grille apparaît un peu après la
     précédente au lieu de toutes en même temps. */
  .flip-grid .flip-card:nth-child(2){--reveal-delay:.12s;}
  .flip-grid .flip-card:nth-child(3){--reveal-delay:.24s;}
  .flip-card-face{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;padding:32px 26px;transition:opacity .35s ease;}
  .flip-card-front{z-index:2;opacity:1;}
  .flip-card-back{background:linear-gradient(135deg, var(--blue), var(--blue-deep));color:#fff;opacity:0;}
  /* v3.5 (demande du client : "augmenter la taille du texte ... les picto
     pouvoir les augmenter ou diminuer") — taille des icônes/textes réglée
     via des variables CSS (--flip-icon-size/--flip-title-size/
     --flip-text-size), avec les valeurs d'origine en repli : tant que le
     client ne règle rien depuis Personnaliser, rien ne change à l'écran.
     Les réglages se posent sur ".flip-grid" (voir page-a-propos.php), donc
     communs à "Nos valeurs" ET "Nos engagements", comme demandé. */
  .flip-card-back p{margin:0;font-size:var(--flip-text-size,14.5px);line-height:1.6;color:rgba(255,255,255,.92);}
  .flip-card:hover .flip-card-front{opacity:0;}
  .flip-card:hover .flip-card-back{opacity:1;}
  .flip-card-icon{display:inline-flex;align-items:center;justify-content:center;width:var(--flip-icon-size,56px);height:var(--flip-icon-size,56px);border-radius:50%;background:var(--paper-alt);margin-bottom:16px;transition:transform .4s ease;overflow:hidden;}
  /* Taille de l'icône (SVG fourni par défaut OU photo importée par le
     client) toujours à 50% du cercle, pour rester proportionnée quelle que
     soit la taille du cercle réglée ci-dessus. */
  .flip-card-icon svg{width:50%;height:50%;}
  .flip-card-icon img{width:60%;height:60%;object-fit:contain;}
  /* Petit "rebond" de l'icône au survol — un mouvement de plus, discret,
     visible pendant que le recto commence à glisser vers le bas. */
  .flip-card:hover .flip-card-icon{transform:scale(1.14) rotate(-4deg);}
  .flip-card-front h3{margin:0;font-size:var(--flip-title-size,18px);color:var(--ink);}
  @media (max-width:700px){.flip-card{min-height:210px;}}

  /* ===========================================================
     QUI SOMMES-NOUS — EFFETS SUPPLÉMENTAIRES v2.3
     Le petit trait animé qui se dessinait sous les kickers ("Notre
     philosophie", "Notre identité") a été retiré en v3.0 (demande du
     client : "enleve le trait").
     =========================================================== */
