@font-face{font-family:"DM Sans";src:url("/assets/fonts/dm-sans-latin.woff2") format("woff2");font-style:normal;font-weight:100 1000;font-display:swap}
@font-face{font-family:"Lora";src:url("/assets/fonts/lora-latin.woff2") format("woff2");font-style:normal;font-weight:400 700;font-display:swap}
@font-face{font-family:"Lora";src:url("/assets/fonts/lora-italic-latin.woff2") format("woff2");font-style:italic;font-weight:400 700;font-display:swap}
@font-face{font-family:"Inter";src:url("/assets/fonts/inter-latin.woff2") format("woff2");font-style:normal;font-weight:100 900;font-display:swap}

:root{
  color-scheme:dark;
  /* Palette relevée sur Logo-RUNCH.jpeg : blanc #FCFCFD, rouge #E50D13,
     bleu #1058CE, jaune #F1D90A, bleu nuit #020435. Aucun vert.
     Bases sombres reprises de la référence d'ambiance (#232228 / #151419). */
  --red:#E50D13;
  --red-deep:#A8090E;
  --red-bright:#FF5A4E;     /* rouge pour petits textes : contraste AA sur sombre */
  --blue:#1058CE;
  /* Bleu unique de la page (2026-09-18). Il sert À LA FOIS d'accent sur fond
     sombre (titres, icônes) et de fond de bouton plein portant du texte blanc.
     Ces deux rôles imposent une valeur intermédiaire :
       - sur le fond nuit #0A121F, il donne 3,96:1 → au-dessus du seuil 3:1
         exigé pour un grand texte et pour un élément non textuel ;
       - sous du texte blanc de 14,5 px gras, il donne 4,78:1 → au-dessus du
         seuil 4,5:1 du texte courant.
     L'ancien #4E8FE8 ne tenait que le premier rôle : blanc sur #4E8FE8 ne
     donnait que 3,27:1, d'où le défaut de contraste du bouton d'envoi.
     Il reste plus clair que --blue (#1058CE), d'où son nom. */
  --blue-bright:#3F73BD;
  --yellow:#F1D90A;
  --navy:#020435;
  --night:#0A121F;
  --surface:#232228;
  --footer:#151419;
  --cream:#E1E0CC;
  --white:#FCFCFD;
  --ink:var(--cream);
  --muted:rgba(225,224,204,.74);
  --muted-soft:rgba(225,224,204,.62);
  --card:rgba(26,25,31,.80);
  --card-solid:rgba(20,19,25,.90);
  --line:rgba(225,224,204,.16);
  --line-soft:rgba(225,224,204,.08);
  --field:rgba(252,252,253,.055);
  --error:var(--red-bright);
  --radius:20px;
  --font:"DM Sans",-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;
  --serif:"Lora",Georgia,serif;
  --ease-pull:cubic-bezier(.16,1,.3,1);
  /* Blur Fade : courbe plus douce que --ease-pull (qui part très vite). Avec
     une courbe « pull », le texte devient net en ~20 % de la durée et le flou
     n'est presque pas lisible. Ici la progression est régulière, le flou reste
     visible pendant tout l'aller-retour. */
  --ease-fade:cubic-bezier(.4,0,.2,1);
}
*,*::before,*::after{box-sizing:border-box}
html{scroll-behavior:smooth;scroll-padding-top:28px;-webkit-text-size-adjust:100%}
/* overscroll-behavior-y:contain — évite le pull-to-refresh d'Android sur le hero. */
body{margin:0;padding:env(safe-area-inset-top) env(safe-area-inset-right) 0 env(safe-area-inset-left);background:var(--night);color:var(--ink);font-family:var(--font);font-size:15px;line-height:1.6;-webkit-font-smoothing:antialiased;overflow-wrap:break-word;overscroll-behavior-y:contain}
/* ── Intro vidéo plein écran : RETIRÉE le 2026-09-21 ────────────────────────
   Tout le bloc a été supprimé d'un seul tenant, parce que rien ne lui
   survivait : `body.splash-active` (verrou de défilement), `.splash` et son
   fond --night, la règle `html:not([data-splash="play"]) .splash{display:none}`
   qui décidait de l'afficher, `.splash.is-hiding`, `.splash-video` (contain sur
   desktop, cover sous 700 px), `@keyframes splashIn`, les boutons
   `.splash-skip` / `.splash.skip-ready` / `.splash-mute` (avec ses deux icônes
   et son masquage en mouvement réduit), et l'effet rideau
   `@media (prefers-reduced-motion:no-preference)`.
   Ce dernier emportait trois règles qui n'existaient QUE pour la bascule :
   la transition de `.bg-photo` et son `scale(1.06)`, et la transition de
   `main.page-width` avec son `translateY(26px)`. Aucune n'a d'autre
   consommateur — vérifié : `body.splash-active` n'apparaissait que trois fois
   dans toute la feuille, toutes ici. Le fond est désormais en place dès la
   première image, sans zoom d'arrivée. ──────────────────────────────────── */
button,input,select{font:inherit}
button,a,input,select{-webkit-tap-highlight-color:transparent}
button,a{touch-action:manipulation}
button{cursor:pointer}
a{color:inherit}
button:disabled{cursor:wait}
img{display:block;max-width:100%}
h1,h2,p{margin:0}
button,fieldset{border:0}
[hidden]{display:none!important}
::selection{background:var(--yellow);color:var(--navy)}
:focus-visible{outline:3px solid var(--yellow);outline-offset:3px;border-radius:4px}
:focus:not(:focus-visible){outline:none}
.sr-only,.svg-library{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
.icon{display:inline-block;flex-shrink:0;width:20px;height:20px;fill:none;stroke:currentColor;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round;vertical-align:middle}
.page-width{width:calc(100% - 64px);max-width:1120px;margin-inline:auto;position:relative;z-index:1}
.skip-link{position:fixed;top:12px;left:16px;z-index:40;background:var(--yellow);color:var(--navy);padding:12px 20px;border-radius:99px;transform:translateY(-180%);text-decoration:none;font-weight:700}
.skip-link:focus{transform:translateY(0)}

/* ── Empilement d'arrière-plan : fond animé « faisceaux » (canvas).
   .bg-stack et .bg-photo gardent le même rôle qu'avant — fixed sous tout le
   contenu (z-index:-1) pour l'un, couche plein cadre pour l'autre. Seul ce
   qu'ils affichent a changé : plus de photo, un canvas de faisceaux et son
   voile. Le léger zoom d'arrivée que .bg-photo portait pendant l'intro vidéo a
   disparu avec elle (2026-09-21) : le fond est en place dès la première image.
   Le fond de base reste --night (le composant d'origine utilise un presque-noir
   neutre : on conserve la teinte de la palette du site). */
.bg-stack{position:fixed;inset:0;z-index:-1;pointer-events:none;overflow:hidden;contain:strict}
.bg-photo{position:absolute;inset:0;background-color:var(--night)}
/* Aucun `filter` ici, volontairement : le composant d'origine floute chaque
   faisceau à chaque dessin (`ctx.filter`) puis le canvas entier en CSS. Un flou
   plein écran, même statique, alourdit toute la composition de la page. Les
   faisceaux sont donc doux PAR CONSTRUCTION (dégradé radial étiré, cf. drawBeam
   dans app.js) : même rendu, sans aucun filtre. */
.bg-beams{position:absolute;inset:0;width:100%;height:100%;display:block}
/* Voile sombre « respirant » : opacité 0,05 → 0,15 → 0,05 sur 10 s, en écho au
   motion.div du composant. Son `backdrop-filter: blur(50px)` d'origine n'est pas
   repris : le canvas est déjà très flou, et un backdrop-filter plein écran
   recalculé à chaque image est le premier poste de saccade sur mobile. */
.bg-veil{position:absolute;inset:0;background-color:rgba(10,10,10,.35);opacity:.05;animation:veilPulse 10s ease-in-out infinite}
@keyframes veilPulse{0%,100%{opacity:.05}50%{opacity:.15}}

/* Plus de header : le logo se suffit à lui-même et vit en haut du hero. */
.site-header{display:none}

/* ── HERO — titre seul, posé sur la photo Cervin plein cadre.
   Le hero vit au-dessus du fond Cervin (z-index:1), ne porte plus la photo
   (qui est dans `.bg-photo`, fixed) et ne gère plus de transition de phase.

   ATTENTION — piège vh/dvh (corrigé 2026-09-11) : ne JAMAIS combiner une
   hauteur en `dvh` avec un padding interne en `vh`. Sur iOS Safari, `vh`
   désigne le GRAND viewport (barre d'URL masquée) alors que `dvh` suit le
   viewport réel : avec la barre visible, le hero valait 400 px mais le contenu
   était poussé de 16vh (~135 px) → le titre débordait sous le cadre et
   touchait le formulaire. Invisible sur desktop et dans les émulateurs, qui
   n'ont pas de barre d'URL (vh == dvh).
   D'où : `min-height` (et non `height`) + `align-items:flex-start`, la hauteur
   du hero suit donc son contenu et ne peut plus être débordée. */
.hero{position:relative;z-index:1;min-height:400px;min-height:max(52dvh,400px);display:flex;align-items:flex-start;justify-content:center;padding:clamp(48px,8vh,96px) 24px clamp(24px,3vh,40px);overflow:visible}
/* padding-top 11vh = 30 % de moins que les 16vh d'origine : le bloc titre +
   encadrement est remonté. Pas de height:100% — le hero se dimensionne sur le
   contenu (voir la note vh/dvh ci-dessus). */
.hero-stack{position:relative;display:flex;flex-direction:column;align-items:center;justify-content:flex-start;padding:11vh 24px 0;width:100%;max-width:1180px;text-align:center}

/* Titre 2 lignes qui cycle, effet « Blur Fade » (refonte 2026-09-11) : chaque
   ligne passe de flou (14 px) + transparent à net + opaque, SANS translation.
   Refonte 2026-09-11 v2 : police Inter (sans-serif néo-grotesque, plus
   adaptée aux grands titres que Lora italic), tout en blanc, et le h1 vit
   dans un cadre glassmorphisme (fond fumé + backdrop-filter + bord subtil).
   Ligne 1 = titre (Inter Black 900, imposant) ; ligne 2 = sous-titre (Inter
   Regular 400, plus petit, blanc). Le sous-titre est décalé de ~130 ms (entrée
   en cascade) et part en premier à la sortie.

   GRID STACK : les 2 .hero-state occupent la même cellule de grille
   (grid-area: 1/1) au lieu de position:absolute — le h1 prend toujours
   la hauteur du plus grand état, aucun débordement sur le formulaire,
   plus besoin de mesure JS.
   WRAP : overflow-wrap/word-break/hyphens neutralisés (le body global a
   break-word pour le formulaire — sans ce contre-poids, « Bienvenue » se
   cassait en « Bienvenu / e » sur mobile). text-wrap:balance répartit
   proprement si une ligne doit passer sur 2 lignes. */
.hero-title{
  position:relative;display:grid;
  font-family:"Inter",-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;
  font-weight:900;font-style:normal;line-height:.96;letter-spacing:-.04em;
  margin:0;width:100%;max-width:1180px;text-align:center;
  overflow-wrap:normal;word-break:normal;hyphens:none;
  /* text-wrap: normal (greedy) — pas de balance, qui équilibre sur 3 lignes
     pour ces deux titres. On laisse le wrap se faire mot par mot, ce qui
     donne ici deux lignes nettes (« Bienvenue zot / Toute !! » et
     « Nouveau en / Suisse ? »). */
  /* Cadre glassmorphisme : fond fumé + backdrop-filter + bord subtil.
     `isolation:isolate` crée un nouveau contexte de composition pour que le
     backdrop-filter ne fuie pas dans le gradient du bg-photo derrière. */
  isolation:isolate;
  background:linear-gradient(180deg,rgba(255,255,255,.10),rgba(255,255,255,.04));
  -webkit-backdrop-filter:blur(22px) saturate(140%);
  backdrop-filter:blur(22px) saturate(140%);
  border:1px solid rgba(255,255,255,.18);
  border-radius:24px;
  padding:clamp(20px,3.2vh,32px) clamp(24px,4vw,52px);
  box-shadow:0 18px 48px rgba(2,4,53,.45),inset 0 1px 0 rgba(255,255,255,.22);
}
.hero-state{grid-area:1/1;display:flex;flex-direction:column;align-items:center;justify-content:flex-start;pointer-events:none}
.hero-state.is-active{pointer-events:auto}
/* Au repos une ligne est floue et transparente ; l'état actif la rend nette.
   Le flou max passe de 14 → 22 px pour un Blur Fade plus insistant. */
.hero-line{display:block;opacity:0;filter:blur(22px);text-shadow:0 2px 18px rgba(20,8,40,.55), 0 1px 2px rgba(20,8,40,.45);will-change:opacity,filter}
.hero-state.is-active .hero-line{opacity:1;filter:blur(0)}
/* Ligne 1 — titre : grand mais légèrement réduit. 86 px max (au lieu de 100)
   reste imposant tout en laissant plus de respiration avec le sous-titre. */
.hero-line-welcome,.hero-line-q,.hero-line-kosa{font-size:clamp(44px,8.5vw,86px);font-weight:900;color:var(--white);letter-spacing:-.045em}
/* Accents de couleur dans les titres (un mot mis en avant par affichage).
   À cette taille (clamp 44→86 px) en graisse 900, c'est du « grand texte »
   WCAG : seuil 3:1. --red (#E50D13) donne 3,95:1, --yellow (#F1D90A) ~13:1 et
   --blue-bright (#3F73BD) 3,96:1 — les trois passent. --blue (#1058CE) est plus
   sombre et plafonne à ~3,0:1 : trop juste pour un titre, on ne l'emploie donc
   jamais comme couleur de texte. */
.hero-swiss{color:var(--red)}
.hero-yellow{color:var(--yellow)}
.hero-blue{color:var(--blue-bright)}
/* Ligne 2 — sous-titre : plus petit, Inter Regular 400, blanc, sous le titre. */
.hero-line-flag,.hero-line-sun,.hero-line-mem{font-size:clamp(24px,4.4vw,42px);font-weight:400;line-height:1.1;letter-spacing:-.02em;margin-top:clamp(10px,1.6vh,16px);color:var(--white)}
/* Décalage vertical des sous-titres par rapport à A1 « Komen i lé ? » :
   - A1 (Komen i lé ?) : pas de décalage, juste sous le titre
   - A3 (Besoin in ti conseil) : +1 × font-size (~1 ligne de sous-titre)
   - A2 (Nou lé la mêm') : +1,5 × font-size (~1,5 ligne, un peu plus bas) */
.hero-line-sun{margin-top:calc(clamp(24px,4.4vw,42px) * 1.5 + clamp(10px,1.6vh,16px))}
.hero-line-mem{margin-top:calc(clamp(24px,4.4vw,42px) * 1   + clamp(10px,1.6vh,16px))}

/* Blur Fade : flou + opacité uniquement, aucune translation.
   Refonte 2026-09-11 v3 : le flou est désormais pleinement lisible EN ENTRÉE
   COMME EN SORTIE (la sortie était 2× plus rapide que l'entrée, on ne voyait
   pas le flou au départ). Flou max 22 px, courbe --ease-fade (progression
   régulière au lieu du « pull » qui devient net en 20 % de la durée). */
@keyframes blurFadeIn{from{opacity:0;filter:blur(22px)}to{opacity:1;filter:blur(0)}}
@keyframes blurFadeOut{from{opacity:1;filter:blur(0)}to{opacity:0;filter:blur(22px)}}
/* L'entrée du hero passe par la MÊME animation blurFadeIn, via la classe
   .blur-in posée par app.js. Une seule mécanique pour l'arrivée et pour les
   bascules — donc AUCUNE règle animant
   `.hero.is-staggered` : comme cette classe reste sur le hero, une telle règle
   garderait la main en permanence (spécificité supérieure à `.blur-out`) et
   l'affichage sortant ne partirait jamais, les 2 textes se superposeraient. */
/* Bascule : sortie COMPLÈTE puis entrée — les affichages ont des contenus
   différents, un crossfade les superposerait et les rendrait illisibles.
   Sortie 0,85 s : assez lente pour que le flou soit bien visible au départ
   (sous-titre d'abord, titre 0,1 s après). */
.hero-state.blur-out .hero-line-flag,.hero-state.blur-out .hero-line-sun,.hero-state.blur-out .hero-line-mem{animation:blurFadeOut .85s var(--ease-fade) both}
.hero-state.blur-out .hero-line-welcome,.hero-state.blur-out .hero-line-q,.hero-state.blur-out .hero-line-kosa{animation:blurFadeOut .85s var(--ease-fade) .1s both}
/* Entrée 1,4 s : démarre après la sortie (finie à 0,95 s), titre puis
   sous-titre. Séquence totale ~2,5 s — app.js retire les classes à 2,5 s. */
.hero-state.blur-in .hero-line-welcome,.hero-state.blur-in .hero-line-q,.hero-state.blur-in .hero-line-kosa{animation:blurFadeIn 1.4s var(--ease-fade) 1s both}
.hero-state.blur-in .hero-line-flag,.hero-state.blur-in .hero-line-sun,.hero-state.blur-in .hero-line-mem{animation:blurFadeIn 1.4s var(--ease-fade) 1.2s both}

/* Menu principal (refonte 2026-09-11) — 4 boutons circulaires centrés en
   haut, fixés, et au-dessus de tout depuis le retrait de l'intro vidéo
   (2026-09-21). Animation GradientMenu adaptée du
   composant shadcn : cercle blanc 48 px → largeur mesurée au hover, icône
   scale→0, libellé scale→0→1, fond gradient + glow. */
/* Bandeau de VERRE (glassmorphisme) SOUS les boutons (2026-09-19). Les cinq
   pastilles flottaient directement au-dessus du contenu, et la superposition
   avec les champs du formulaire était gênante (le texte passait derrière les
   cercles). Le menu occupe maintenant toute la largeur et porte un bandeau
   flouté : ce qui défile dessous est adouci puis fondu vers le bas, et les
   boutons se détachent nettement.
   - `backdrop-filter` floute l'arrière-plan (le canvas des faisceaux comme le
     formulaire) ; le dégradé pose la teinte nuit par-dessus ;
   - `mask-image` fait s'évanouir le flou ET la teinte sur la dernière bande :
     aucune arête franche, le bandeau se fond dans la page ;
   - `pointer-events:none` est hérité du parent : le bandeau ne capte jamais
     un clic destiné au contenu.
   Le menu reste centré : `display:flex` + `justify-content:center` remplacent
   l'ancien `left:50%` + `translateX(-50%)`, ce qui libère `transform` et
   permet au bandeau de couvrir exactement toute la largeur (pas de 100vw, donc
   aucun risque de débordement horizontal). */
.top-menu{position:fixed;top:0;left:0;right:0;z-index:39;pointer-events:none;display:flex;justify-content:center;padding:calc(14px + env(safe-area-inset-top)) 14px 14px}
.top-menu::before{content:"";position:absolute;inset:0;z-index:-1;pointer-events:none;background:linear-gradient(180deg,rgba(6,9,18,.82) 0%,rgba(6,9,18,.62) 46%,rgba(6,9,18,.24) 78%,rgba(6,9,18,0) 100%);-webkit-backdrop-filter:blur(18px) saturate(150%);backdrop-filter:blur(18px) saturate(150%);-webkit-mask-image:linear-gradient(180deg,#000 0%,#000 54%,rgba(0,0,0,.42) 82%,transparent 100%);mask-image:linear-gradient(180deg,#000 0%,#000 54%,rgba(0,0,0,.42) 82%,transparent 100%)}
.top-menu ul{list-style:none;margin:0;padding:0;display:flex;gap:18px;flex:0 0 auto}
/* `flex:none` soustrait chaque bouton au rétrécissement du conteneur. Sans lui,
   l'ouverture d'un bouton comprimerait la rangée et rapetisserait TOUS les
   autres — les cercles ne feraient plus 48 px. La rangée déborde alors
   symétriquement (le menu est centré), ce qui est sans conséquence :
   `.top-menu` est en `position:fixed`, et le débordement d'un élément fixe ne
   crée pas de barre de défilement horizontale. */
.menu-item{--gfrom:#a955ff;--gto:#ea51ff;--largeur-ouverte:140px;position:relative;width:48px;height:48px;flex:none;background:rgba(255,255,255,.94);border:0;border-radius:999px;display:flex;align-items:center;justify-content:center;cursor:pointer;font:inherit;color:inherit;text-decoration:none;box-shadow:0 4px 14px rgba(2,4,53,.35);transition:width .45s var(--ease-pull),box-shadow .45s var(--ease-pull);overflow:hidden;pointer-events:auto;-webkit-tap-highlight-color:transparent}
/* ÉLARGISSEMENT au survol/focus : le bouton s'ouvre jusqu'à `--largeur-ouverte`
   pour que le libellé ENTIER tienne à l'intérieur.
   ⚠️ C'est une CORRECTION, pas un réglage. La version précédente conservait
   `width:48px` en toutes circonstances (pour éviter que les voisins ne
   coulissent) : le libellé apparaissait bien, mais rogné net par
   `overflow:hidden` — illisible, et tronqué au milieu d'un mot. Un libellé
   tronqué est pire que pas de libellé du tout.
   La largeur n'est pas devinée : `app.js` mesure la largeur réelle du libellé
   et pose `--largeur-ouverte` = largeur mesurée + 40 px (20 px de marge de
   chaque côté). La mesure est refaite une fois les polices chargées, pour que
   la valeur reste juste même si « Inter » n'est pas disponible. La valeur
   déclarée ici n'est que le repli sans JavaScript. */
.menu-item:hover,.menu-item:focus-visible{width:var(--largeur-ouverte)}
.menu-item:focus-visible{outline:2px solid var(--yellow);outline-offset:3px}
.menu-bg{position:absolute;inset:0;border-radius:999px;background:linear-gradient(45deg,var(--gfrom),var(--gto));opacity:0;transition:opacity .45s var(--ease-pull)}
.menu-item:hover .menu-bg,.menu-item:focus-visible .menu-bg{opacity:1}
.menu-glow{position:absolute;top:8px;inset-inline:0;height:100%;border-radius:999px;background:linear-gradient(45deg,var(--gfrom),var(--gto));filter:blur(14px);opacity:0;z-index:-1;transition:opacity .45s var(--ease-pull)}
.menu-item:hover .menu-glow,.menu-item:focus-visible .menu-glow{opacity:.55}
.menu-icon{position:relative;z-index:1;color:#4b4b53;display:inline-flex;align-items:center;justify-content:center;transition:transform .35s var(--ease-pull) .08s,opacity .25s ease}
.menu-item:hover .menu-icon,.menu-item:focus-visible .menu-icon{transform:scale(0);opacity:0}
.menu-icon svg{width:22px;height:22px;display:block;fill:none;stroke:currentColor;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round}
.menu-label{position:absolute;color:#fff;font-size:13.5px;font-weight:600;letter-spacing:.02em;white-space:nowrap;transform:scale(0);transition:transform .35s var(--ease-pull) .18s}
.menu-item:hover .menu-label,.menu-item:focus-visible .menu-label{transform:scale(1)}
/* Couleurs par item (palette de l'app : --yellow, --red, --blue). Au survol,
   chaque cercle s'illumine avec son gradient respectif.
   « Kontakt » était en VERT (#00B389 → #2BD4A8) jusqu'au 2026-09-21, ce qui
   contredisait la charte (« Aucun vert », cf. --red/--blue/--yellow ci-dessus).
   `check-no-green.mjs` ne scanne que les images, jamais les dégradés CSS : le
   contrôle ne pouvait pas le voir. Il prend le bleu de marque, comme le titre
   de sa section (.contact-accent → --blue-bright).

   « Partaz a li » est passé au ROUGE le 2026-09-21, sur demande. Ce commentaire
   portait alors un avertissement : « Kontakt » et « Partaz a li » partageaient
   le même bleu, et il invitait à donner au second une autre couleur de la
   palette. C'est ce qui a été fait — mais PAS en reprenant le rouge de
   « Ki sa nou lé », qui les aurait rendus identiques au survol : le problème
   aurait été déplacé, pas résolu.

   Le rouge retenu est --red-deep → --red (#A8090E → #E50D13), et non
   --red → --red-bright comme pour « Ki sa nou lé ». Deux raisons, dans cet
   ordre :
     1. Les deux boutons restent DISTINGUABLES au survol, ce qui est la raison
        d'être de ce changement.
     2. Le libellé est BLANC (.menu-label) posé sur ce dégradé. Sur
        #A8090E → #E50D13 le contraste va de 7,75:1 à 4,77:1 — au-dessus du
        seuil AA (4,5:1) sur TOUTE la plage, comme le bleu de « Kontakt »
        (4,77:1 minimum). Le rouge clair de « Ki sa nou lé » descend à 3,08:1
        sur son extrémité #FF5A4E, donc sous le seuil : c'est un défaut
        PRÉEXISTANT, mesuré ici mais volontairement laissé tel quel — la
        consigne était de ne rien changer d'autre. */
.menu-item[data-action="home"]{--gfrom:#F1D90A;--gto:#FFB300}
.menu-item[data-action="about"]{--gfrom:#E50D13;--gto:#FF5A4E}
.menu-item[data-action="contact"]{--gfrom:#1058CE;--gto:#3F73BD}
/* « Évènement » (5e bouton, 2026-11-07). Il lui fallait un dégradé distinct
   des quatre autres, sans vert et sans reprendre le jaune de Home ou le rouge
   des deux boutons déjà rouges : nuit → bleu de marque (#020435 → #1058CE).
   Le libellé blanc tient AA sur toute la plage (contraste ≥ 5,6:1). */
.menu-item[data-action="evenement"]{--gfrom:var(--navy);--gto:var(--blue)}
.menu-item[data-action="share"]{--gfrom:#A8090E;--gto:#E50D13}
.share-toast{margin:0;padding:5px 12px;border-radius:99px;background:rgba(10,18,31,.72);font-size:12px;color:var(--white);font-family:var(--serif);font-style:italic;text-shadow:0 1px 4px rgba(20,8,40,.5);position:fixed;top:calc(88px + env(safe-area-inset-top));left:50%;transform:translateX(-50%);z-index:40}
.share-toast[hidden]{display:none}

/* Hero : réduite sur les très petits écrans. */
@media (max-width:600px){
  .hero{padding-top:clamp(84px,11vh,104px)}
}
/* CINQ boutons depuis le 2026-11-07 (« Évènement »). C'est le commentaire que
   la demande pointait : la 5e icône DOIT tenir sur un mobile de 390 px, et
   même sur 320 px. Calcul au repos, avec .menu-item à 48 px :
     5×48 + 4×18 + 2×14 (padding du bandeau) = 340 px → tient sur 390,
     mais DÉBORDE sur 320.
   On resserre donc l'écart sous 430 px :
     5×48 + 4×10 + 28 = 308 px → tient sur 390 ET sur 320.
   Et même un bouton ouvert (« Évènement », ~116 px mesurés par app.js) donne
   348 + 28 = 376 px, encore sous les 390 px du mobile cible.
   Le menu reste fixé en haut (position:fixed héritée de .top-menu) donc sticky
   même en défilement ; son débordement éventuel ne crée aucune barre
   horizontale. */
@media (max-width:430px){
  .top-menu ul{gap:10px}
}
@media (max-width:350px){
  .top-menu ul{gap:8px}
}

/* Préférence « mouvement réduit » : aucune cascade, le titre s'affiche net
   immédiatement (affichage 1 seul — l'affichage 2 reste masqué et la bascule
   n'est pas lancée, voir app.js). */
@media (prefers-reduced-motion:reduce){
  .hero-line{animation:none!important}
}

/* Le formulaire n'apparaît qu'au scroll (IntersectionObserver ajoute
   .is-revealed) ou, pour ce qui est déjà visible à l'arrivée, dès le
   chargement (app.js appelle revealVisibleSections() depuis startHeroIntro()).
   RÉGLAGE DU 2026-09-19 (2e passe) — le fondu était devenu INVISIBLE :
   la révélation partait 18 % SOUS le pli, si bien que la transition de 0,55 s
   était déjà terminée quand le bloc entrait réellement dans l'écran. On voyait
   donc le contenu « déjà là ». Deux corrections :
     1. le déclenchement se fait maintenant quand le bloc est franchement
        entré dans l'écran (marge basse NÉGATIVE de 12 %, cf. app.js) : le
        fondu se joue sous les yeux de l'utilisateur ;
     2. la transition passe à 0,8 s avec une courbe douce et un déplacement de
        28 px : nettement perceptible, mais toujours SANS DÉLAI — la classe et
        le fondu partent au même instant, donc aucun trou vide possible (le
        bloc est en opacity 0 pendant l'approche, ce qui ne réserve aucune
        place : opacity ne modifie pas la mise en page, il n'y a pas de saut).
   Animations en transform + opacity uniquement (perf mobile).              */
[data-reveal]{opacity:0;transform:translateY(28px);transition:opacity .8s cubic-bezier(.25,.7,.3,1),transform .8s cubic-bezier(.25,.7,.3,1)}
[data-reveal].is-revealed{opacity:1;transform:translateY(0)}
/* Chaque grand bloc porte [data-reveal] et se révèle quand son bord haut a
   dépassé 88 % de la hauteur d'écran (seuil 0 + marge basse -12 %, voir
   app.js) : le bloc est donc déjà visible quand le fondu démarre.
   Ordre document : bloc des titres → texte d'introduction →
   « Ki sa nou lé » → « Kosa ou la besoin » → formulaire de contact →
   « Kosa nou pé dire ». */

.button{display:inline-flex;align-items:center;justify-content:center;gap:14px;min-height:50px;padding:13px 21px;border:1px solid transparent;border-radius:99px;text-decoration:none;font-size:13px;line-height:1.5;font-weight:600;transition:background-color .18s ease,color .18s ease,box-shadow .18s ease,transform .18s ease,border-color .18s ease}
.button .icon{width:18px;height:18px}
.button-primary{background:var(--red);color:#fff;box-shadow:0 6px 20px rgba(229,13,19,.30)}
.button-primary:hover:not(:disabled){background:var(--red-deep);box-shadow:0 10px 28px rgba(229,13,19,.42);transform:translateY(-1px)}
.button-primary:active:not(:disabled){transform:translateY(0);box-shadow:none}

/* ── Invitation à remplir le formulaire, posée juste sous le bloc des titres.
   Même animation que le texte « Ki sa nou lé » : chaque mot arrive en cascade
   avec flou — mêmes keyframes `wordBlurIn`, même durée .4 s, même décalage
   .025 s × index (cf. animateWords() pour la section équipe).
   Bloc centré et largeur limitée pour rester confortable à lire.
   Depuis le 2026-09-21, c'est aussi l'INTRODUCTION SEO de la page : corps plus
   grand, gras et italique. L'italique est un VRAI italique, et c'est la raison
   du changement de famille : DM Sans et Inter n'ont pas de fichier oblique dans
   le jeu de polices, le navigateur se contenterait donc de pencher
   mécaniquement les lettres. Lora, elle, a son fichier italique déjà préchargé
   pour les lignes 2 et 3 du titre — aucune requête supplémentaire, et une
   police qui appartient déjà à la charte.
   La graisse reste dans la plage de la police (Lora variable : 400–700). ── */
.hero-intro{max-width:58ch;margin:clamp(18px,2.6vh,28px) auto 0;text-align:center;font-family:var(--serif);font-style:italic;font-weight:700;font-size:clamp(19px,2.4vw,24px);line-height:1.7;color:var(--muted)}
/* Le lien interne « ce formulaire ». Il est SOULIGNÉ, et pas seulement coloré :
   la WCAG 1.4.1 demande qu'un lien au fil d'un paragraphe se distingue
   autrement que par la couleur — ici, quelqu'un qui ne perçoit pas le jaune
   doit quand même voir qu'il y a un lien. Le jaune est l'accent de la page
   (#F1D90A : ~13:1 sur le fond nuit, bien au-dessus du seuil de 4,5:1), et le
   trait est épaissi parce que l'italique du paragraphe amincit le rendu. */
.hero-intro a{color:var(--yellow);text-decoration:underline;text-decoration-thickness:1.5px;text-underline-offset:3px;text-decoration-color:rgba(241,217,10,.45);transition:color .18s ease,text-decoration-color .18s ease}
.hero-intro a:hover,.hero-intro a:focus-visible{text-decoration-color:currentColor}
.hero-intro .intro-word{display:inline-block;opacity:0;filter:blur(10px);transform:translateY(5px)}
.hero-intro.is-words-in .intro-word{animation:wordBlurIn .4s var(--ease-fade) both}
/* Mouvement réduit : pas de cascade, le texte est lisible immédiatement
   (même traitement que .team-word dans la section « Ki sa nou lé »). */
@media (prefers-reduced-motion:reduce){
  .hero-intro .intro-word,
  .hero-intro.is-words-in .intro-word{opacity:1;filter:none;transform:none;animation:none}
}

/* ── Bandeau d'annonce de la soirée (2026-11-07) ─────────────────────────────
   UNE porte, zéro formulaire : ce bloc renvoie vers /evenement.html. Il est
   volontairement compact et en `flex-wrap` — à 320 px les trois éléments
   passent les uns sous les autres au lieu de faire déborder la page (contrôle
   `scrollWidth <= innerWidth` de contact.spec.js). Le lien porte une cible
   tactile de 44 px, comme les autres liens isolés du site. */
.event-teaser{display:flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:8px 14px;max-width:760px;margin:clamp(18px,2.6vh,26px) auto 0;padding:12px 16px;border:1px solid rgba(241,217,10,.34);border-radius:16px;background:rgba(26,25,31,.72);text-align:center;font-size:12.5px;line-height:1.6;color:var(--cream)}
.event-teaser-badge{flex:none;padding:4px 10px;border-radius:99px;background:var(--yellow);color:var(--navy);font-size:10.5px;font-weight:800;letter-spacing:.06em}
.event-teaser-texte{min-width:0}
.event-teaser-texte strong{color:var(--yellow)}
.event-teaser-link{display:inline-flex;align-items:center;justify-content:center;gap:7px;min-height:44px;color:var(--yellow);font-weight:700;text-decoration:underline;text-underline-offset:3px;white-space:nowrap}
.event-teaser-link .icon{width:16px;height:16px}
.event-teaser-link:hover,.event-teaser-link:focus-visible{color:var(--white)}

/* Un seul formulaire. Les deux groupes ne sont pas des étapes.
   La section est remontée au plus près du hero (le haut du formulaire devine
   sous le pli et invite au scroll). */
/* Espacement au-dessus du formulaire : « Kontakt a ou » était trop collé à la
   dernière vignette de « Kosa ou la besoin ? ». Une seule valeur pilote l'écart
   (les marges du titre et de la section fusionnent, donc on ne cumule plus). */
.contact-section{max-width:800px;margin:clamp(48px,8vh,80px) auto 0;scroll-margin-top:28px;position:relative;z-index:1}
/* Titre « Contact » visible, placé en tête de section, juste sous la
   deuxième photo de « Ki sa nou lé ». Sert aussi d'étiquette accessible à la
   section et au formulaire (aria-labelledby="contact-title"). Remplace l'ancien
   titre .sr-only « Remplissez le formulaire de contact ». */
/* Titre « Kontakt a ou » : le mot « Kontakt » est en bleu (.contact-accent),
   la suite en blanc — même dessin que les autres titres de section.
   TAILLE ALIGNÉE sur « Ki sa nou lé ? » (demandé le 2026-09-18) : les deux
   titres partagent exactement le même `clamp(40px,7vw,72px)`.
   L'espace AU-DESSUS du titre n'est plus porté ici mais par la section
   (.contact-section) : les marges fusionnent, et une seule valeur évite de
   compter deux fois. L'espacement en dessous reste généreux pour ne plus
   coller le formulaire. */
.contact-title{font-family:"Inter",-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;font-weight:900;color:var(--white);font-size:clamp(40px,7vw,72px);line-height:.96;letter-spacing:-.045em;text-align:center;margin:0 0 clamp(26px,4vh,40px)}
.contact-accent{color:var(--blue-bright)}
.contact-card{background:rgba(15,17,23,.55);backdrop-filter:blur(22px);-webkit-backdrop-filter:blur(22px);border:1px solid var(--line);border-radius:var(--radius);padding:28px 30px 24px;box-shadow:0 26px 70px rgba(2,4,53,.55)}
.required-note{font-size:11.5px;color:var(--muted);margin:0 0 22px}
.required-note>span,.required-mark{color:var(--red-bright)}
.required-mark{margin-left:2px}
fieldset{padding:0;margin:0;min-width:0}
legend{display:flex;align-items:center;gap:9px;padding:0;margin-bottom:18px;font-size:14px;font-weight:650;letter-spacing:-.1px;color:var(--cream)}
.section-number{display:inline-grid;place-items:center;width:23px;height:23px;background:var(--yellow);border:0;border-radius:7px;font-size:9px;font-weight:800;letter-spacing:.1px;color:var(--navy)}
.form-grid{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:19px 21px}
.field{min-width:0}
.field label{display:block;margin-bottom:7px;font-size:13px;font-weight:600;color:var(--cream);line-height:1.5}
.field input,.field select{display:block;min-width:0;width:100%;min-height:48px;height:48px;padding:12px 14px;border:1.5px solid var(--line);border-radius:11px;background:var(--field);color:var(--cream);font-size:14px;line-height:1.6;transition:border-color .18s,box-shadow .18s,transform .18s,background-color .18s;-webkit-appearance:none;appearance:none}
.field input::placeholder{color:var(--muted-soft);opacity:1;font-weight:400}
.field select:invalid{color:var(--muted-soft)}
.field select{padding-right:36px;cursor:pointer;text-overflow:ellipsis}
.field option{color:var(--ink);background:var(--surface)}
.field input:hover,.field select:hover{border-color:var(--blue-bright)}
.field input:focus,.field select:focus{border-color:var(--yellow);outline:0;box-shadow:0 0 0 4px rgba(241,217,10,.16);transform:scale(1.01);background:rgba(252,252,253,.08)}
.field input[aria-invalid="true"],.field select[aria-invalid="true"]{border-color:var(--red-bright);background:rgba(229,13,19,.10)}
.field input[aria-invalid="true"]:focus,.field select[aria-invalid="true"]:focus{box-shadow:0 0 0 4px rgba(229,13,19,.16)}
.field input[type="date"],.field input[type="datetime-local"]{position:relative;color:var(--cream);font-family:var(--font)}
.field input[type="date"]:invalid,.field input[type="datetime-local"]:invalid{color:var(--muted-soft)}
.field input::-webkit-date-and-time-value{text-align:left;min-height:1.5em}
.field input::-webkit-datetime-edit{padding:0}
.field input::-webkit-calendar-picker-indicator{margin:0;opacity:.6;cursor:pointer;width:15px;height:15px}
.select-wrap{position:relative}
.select-chevron{position:absolute;right:14px;top:50%;transform:translateY(-50%);width:15px;height:15px;stroke:var(--cream);stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round;pointer-events:none}
.field-hint{font-size:11px;color:var(--muted);line-height:1.6;margin-top:7px}
.field-error{font-size:12px;line-height:1.5;color:var(--error);margin-top:6px}
.situation-fields{margin-top:29px;padding-top:25px;border-top:1px solid var(--line-soft)}
.situation-fields legend{float:left;width:100%;margin-bottom:18px}
.situation-fields .form-grid{clear:both}
/* ── Protection des données — case à cocher obligatoire ───────────────────────
   Son propre bloc, détaché du reste : cadenas dans une pastille à gauche,
   intitulé en gras, mention en dessous. Même famille de surface que les autres
   cartes du site (fond translucide + flou + liseré). Le bloc est posé après les
   disponibilités et avant l'envoi, comme sur la maquette fournie.
   La case elle-même suit `accent-color` : la coche prend donc le bleu de la
   pastille, sans dessiner de case personnalisée (le rendu natif reste le plus
   fiable au clavier comme au doigt). L'anneau de focus jaune vient de la règle
   globale `:focus-visible`, rien à ajouter. ───────────────────────────────── */
.consent-block{display:flex;align-items:flex-start;gap:14px;margin-top:24px;padding:18px;border-radius:16px;background:rgba(15,17,23,.55);-webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px);border:1px solid var(--line);box-shadow:0 14px 38px rgba(2,4,53,.35)}
.consent-icon{display:grid;place-items:center;flex:0 0 auto;width:40px;height:40px;border-radius:12px;background:rgba(63,115,189,.14);border:1px solid rgba(63,115,189,.26);color:var(--blue-bright)}
.consent-icon .icon{width:20px;height:20px;stroke-width:1.6}
.consent-body{min-width:0}
/* Case « Protection de vos données ». Audit mobile du 2026-09-19 : la case
   seule mesurait 18 × 18 px, très en dessous des 44 px recommandés. Elle est
   désormais imbriquée dans son <label>, qui devient la cible de toute la ligne.
   C'est le PADDING qui porte la hauteur (11 px en haut et en bas autour d'un
   texte de 22 px = 44 px) plutôt qu'un `min-height` : le padding fait partie
   de la boîte du label, il est donc toujours cliquable, et la zone tapable
   couvre exactement ce que l'œil voit.
   Vérifié par balayage au pixel près (un clic tous les 1 px sur toute la
   hauteur) : les 44 px répondent, sans aucune zone morte. Piège rencontré
   pendant la vérification : la section porte [data-reveal], dont le
   `transform` s'anime sur 0,8 s. Mesurer pendant l'animation donne une origine
   décalée de plusieurs pixels et fait croire à une zone morte qui n'existe
   pas. Toujours attendre la fin de la transition avant de mesurer. */
.consent-check{display:flex;align-items:center;gap:10px}
.consent-check input{flex:0 0 auto;width:18px;height:18px;margin:0;accent-color:var(--blue-bright);cursor:pointer}
.consent-title{flex:1 1 auto;display:flex;align-items:center;gap:10px;padding:11px 0;font-size:14px;font-weight:700;letter-spacing:-.01em;color:var(--white);cursor:pointer}
.consent-mention{margin:2px 0 0;font-size:12px;line-height:1.6;color:var(--muted)}
/* Lien « Politique de confidentialité », ajouté le 2026-09-21 avec la
   réécriture de la mention. Il est AU FIL de la phrase, donc WCAG 2.5.8
   (target size) ne lui impose pas 44 px — l'exception vise explicitement les
   liens en ligne. Le `padding` vertical est là malgré tout : il porte la zone
   tapable d'environ 19 px à environ 31 px sans casser le paragraphe, et le
   `font-weight` le détache du gris de la mention sans ajouter de couleur. */
.consent-mention a{display:inline-block;padding:6px 0;color:var(--cream);font-weight:600;text-decoration:underline;text-decoration-color:rgba(252,252,253,.34);text-underline-offset:2px;transition:text-decoration-color .18s ease}
.consent-mention a:hover,.consent-mention a:focus-visible{text-decoration-color:currentColor}
.honeypot{position:absolute!important;width:1px;height:1px;overflow:hidden;opacity:0;pointer-events:none;left:-10000px}
.form-bottom{margin-top:26px;padding-top:23px;border-top:1px solid var(--line-soft)}
.privacy-note{display:flex;align-items:flex-start;justify-content:center;gap:8px;color:var(--muted);font-size:11.5px;line-height:1.7;text-align:left}
.privacy-note>.icon{width:14px;height:14px;margin-top:2px;stroke-width:1.5;color:var(--blue-bright)}
.privacy-note>span>span{color:var(--muted);font-size:11px}
/* ── Bouton « Envoyer ma demande » — refonte 2026-09-19.
   Il était TROP VOLUMINEUX et, sur mobile, VERTICAL (124 × 164 px) : un pavé
   posé sous le formulaire. Désormais allongé À L'HORIZONTALE partout — même
   forme et même hauteur (58 px) sur ordinateur comme sur téléphone ; seule la
   largeur disponible change.
   Dessin résolument premium : dégradé bleu à trois arrêts, liseré interne
   clair en haut (la lumière), ombre portée colorée, creux sombre en bas, et un
   reflet diagonal qui balaie le bouton au survol. La flèche avance d'un cran
   au même moment : le bouton répond au doigt comme à la souris.
   Centré sous le formulaire (`display:flex` + `margin auto`, pour centrer sans
   casser le `justify-content:center` interne du composant .button). ── */
.submit-button{position:relative;display:flex;width:min(100%,380px);margin:22px auto 0;min-height:58px;padding:0 30px;gap:14px;font-size:14.5px;font-weight:700;letter-spacing:.015em;border:1px solid rgba(255,255,255,.20);border-radius:99px;overflow:hidden;background:linear-gradient(135deg,#4C86D8 0%,#3F73BD 45%,#2C5899 100%);color:#fff;box-shadow:0 10px 26px rgba(16,88,206,.34),0 1px 0 rgba(255,255,255,.30) inset,0 -10px 20px rgba(4,16,44,.34) inset}
/* Le reflet : une bande diagonale qui traverse le bouton de gauche à droite. */
.submit-button::after{content:"";position:absolute;inset:0;pointer-events:none;background:linear-gradient(115deg,transparent 32%,rgba(255,255,255,.30) 48%,transparent 64%);transform:translateX(-130%);transition:transform .9s var(--ease-pull)}
.submit-button:hover:not(:disabled)::after{transform:translateX(130%)}
/* Le contenu passe AU-DESSUS du reflet. */
.submit-button>span,.submit-button>.icon{position:relative;z-index:1}
.submit-button .submit-arrow{transition:transform .3s var(--ease-pull)}
.submit-button:hover:not(:disabled){background:linear-gradient(135deg,#5A93E2 0%,#3F73BD 45%,#24508F 100%);box-shadow:0 16px 34px rgba(16,88,206,.46),0 1px 0 rgba(255,255,255,.38) inset,0 -10px 20px rgba(4,16,44,.30) inset;transform:translateY(-2px)}
.submit-button:hover:not(:disabled) .submit-arrow{transform:translateX(4px)}
.submit-button:active:not(:disabled){transform:translateY(0);box-shadow:0 6px 16px rgba(16,88,206,.32),0 1px 0 rgba(255,255,255,.22) inset}
.submit-button:focus-visible{outline:2px solid var(--yellow);outline-offset:3px}
/* Bouton inactif (tant que le formulaire n'est pas valide) : plat, sans reflet. */
.submit-button:disabled{background:rgba(225,224,204,.16);color:var(--muted);opacity:1;border-color:transparent;box-shadow:none;transform:none}
.submit-button:disabled::after{display:none}
.submit-button.is-offline{cursor:not-allowed}
.notice{padding:13px 15px;border-radius:11px;margin-top:17px;font-size:12px;line-height:1.6}
.notice-error{background:rgba(229,13,19,.14);color:var(--red-bright);border:1px solid rgba(229,13,19,.40)}
.notice-offline{display:flex;align-items:flex-start;gap:10px;background:rgba(241,217,10,.13);color:var(--yellow);border:1px solid rgba(241,217,10,.34)}
.notice-offline>.icon{width:18px;height:18px;margin-top:2px}
.button-spinner{width:17px;height:17px;border:2px solid rgba(255,255,255,.28);border-top-color:#fff;border-radius:50%;animation:spin .8s linear infinite}
@keyframes spin{to{transform:rotate(360deg)}}

/* Confirmation uniquement après acceptation réelle du message par le serveur. */
.confirmation{position:relative;text-align:center;padding:27px 12px 21px;color:var(--ink)}
.success-symbol{position:relative;display:grid;place-items:center;width:73px;height:73px;margin:0 auto 29px;background:var(--yellow);border:0;border-radius:50%;color:var(--navy);box-shadow:0 0 0 9px rgba(241,217,10,.12)}
.success-symbol>.icon{width:34px;height:34px;stroke-width:1.9}
.success-spark{position:absolute;top:-4px;right:-9px;color:var(--cream);background:var(--red);border-radius:50%;padding:3px}
.success-spark .icon{width:19px;height:19px}
.confirmation h2{font-family:var(--serif);font-size:clamp(30px,6vw,40px);font-weight:500;line-height:1.15;letter-spacing:-.035em;margin:9px 0 13px;color:var(--cream)}
.confirmation>p{font-size:13px;color:var(--muted)}
.confirmation>.section-eyebrow{font-size:9px;color:var(--yellow)}
.confirmation>.confirmation-lead{font-weight:650;color:var(--cream);margin-bottom:5px;font-size:15px}
.confirmation-creole{font-family:var(--serif);font-style:italic;font-weight:400;color:var(--yellow);font-size:16px;margin:12px 0 0;letter-spacing:-.02em}
.confirmation>.confirmation-footnote{display:flex;gap:6px;align-items:center;justify-content:center;margin-top:25px;font-size:10px;color:var(--muted)}
.confirmation-footnote .icon{width:13px;height:13px}
.button-secondary{border:1.5px solid var(--line);background:transparent;color:var(--cream);font-size:12.5px;font-weight:600;margin-top:23px;min-height:44px}
.button-secondary:hover{background:var(--cream);border-color:var(--cream);color:var(--navy)}
/* La confirmation glisse vers le haut à l'apparition. */
#confirmation:not([hidden]){animation:confirmIn .7s var(--ease-pull) both}
@keyframes confirmIn{from{opacity:0;transform:translateY(18px)}to{opacity:1;transform:translateY(0)}}

/* Halo chaleureux autour de l'icône de succès : il s'étend puis s'efface,
   et l'anneau statique d'origine reprend sa place à la fin. Combiné à
   l'animation d'apparition ci-dessus sur la même règle, sans conflit (les
   deux touchent des propriétés distinctes). */
@keyframes successHalo{
  0%  {box-shadow:0 0 0 0  rgba(241,217,10,.55)}
  65% {box-shadow:0 0 0 30px rgba(241,217,10,0)}
  100%{box-shadow:0 0 0 9px rgba(241,217,10,.12)}
}
.confirmation:not([hidden]) .success-symbol{
  animation:successHalo 1.8s var(--ease-pull) .15s both
}
/* Le petit soleil rouge, en haut-droite, fait un léger « hop » à l'arrivée :
   il attire l'œil sans surcharger la composition. */
@keyframes sparkHop{
  0%   {transform:scale(.6) rotate(-20deg);opacity:0}
  60%  {transform:scale(1.12) rotate(6deg);opacity:1}
  100% {transform:scale(1) rotate(0);opacity:1}
}
.confirmation:not([hidden]) .success-spark{
  animation:sparkHop .8s var(--ease-pull) .55s both
}

.confetti{position:absolute;inset:0;width:100%;height:100%;pointer-events:none}

.site-footer{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;margin-top:44px;padding:18px 4px calc(22px + env(safe-area-inset-bottom));font-size:11px;line-height:1.6;color:var(--muted-soft);position:relative;z-index:1;text-align:center}
/* Le lien de crédit mesurait 57 × 13 px : sous les 44 px recommandés, donc
   difficile à viser au doigt. `inline-flex` + `min-height:44px` lui donne une
   vraie zone tapable, sans déplacer le texte (il reste centré). */
.site-footer a{display:inline-flex;align-items:center;justify-content:center;min-height:44px;padding:0 8px;color:inherit;text-decoration:underline;text-decoration-color:rgba(252,252,253,.3);text-underline-offset:3px;transition:text-decoration-color .18s ease,color .18s ease}
.site-footer a:hover,.site-footer a:focus-visible{text-decoration-color:currentColor;color:var(--cream)}

/* ─────────────────────────────────────────────────────────────────────────
   Logo du pied de page — tout en bas de la page, SOUS les blocs animés de
   « Kosa nou pé dire ? » (ajouté le 2026-09-21).
   Le socle 3D (boîte carrée, perspective 1200px, couche qui s'incline) est
   GROUPÉ avec celui des portraits de « Ki sa nou lé » : voir `.team-photos` et
   `.team-photo-3d` plus bas. Ici ne vivent que les différences — la largeur et
   la plaque.

   POURQUOI UNE PLAQUE CLAIRE, ET PAS UNE DÉCOUPE
   L'artwork du logo a un fond blanc (≈ #FBFCFC, mesuré aux quatre coins) et son
   sommet enneigé est blanc LUI AUSSI. Les deux sont indiscernables au pixel :
   une découpe en transparence supprimerait la montagne en même temps que le
   fond. On assume donc le fond blanc et on en fait un CADRE — la plaque reprend
   la teinte exacte du logo et porte l'arrondi et l'ombre portée des cartes
   photo, pour que le blanc se lise comme un choix et non comme un oubli.
   Détail : scripts/make-footer-logo.mjs.
   340 px de large — un peu plus que les 320 px des portraits, parce que
   l'artwork n'occupe que ~73 % de la hauteur de son carré : à taille de boîte
   égale, le logo paraîtrait plus petit que les photos.
   ───────────────────────────────────────────────────────────────────────── */
.footer-logo{max-width:340px;margin-top:clamp(44px,7.5vh,76px);border-radius:20px;background:#FBFCFC;box-shadow:0 18px 48px rgba(2,4,53,.5),0 0 0 1px rgba(252,252,253,.10)}
.footer-logo-img{width:100%;height:100%;object-fit:contain;display:block;border-radius:20px}

@media(min-width:1500px){.page-width{max-width:1160px}}
@media(max-width:1050px){
  .contact-section{max-width:760px}
}
@media(max-width:800px){
  .page-width{width:calc(100% - 40px)}
  .site-header{min-height:80px;padding-block:18px}
  .hero{padding-inline:18px}
  /* Android Chrome : le backdrop-filter d'une carte qui scrolle au-dessus d'un
     fond fixed se repeint mal (labels fantômes affichés en double pendant le
     scroll). Sur mobile : fond quasi opaque, sans blur — net et moins coûteux. */
  .contact-card{background:rgba(15,17,23,.92);backdrop-filter:none;-webkit-backdrop-filter:none}
  .contact-section{max-width:660px;margin-top:48px}
  .contact-card{padding-inline:24px}
  .field label{font-size:12.5px}
  .field input,.field select{font-size:14px}
  .form-grid{column-gap:18px}
}
@media(max-width:600px){
  html{scroll-padding-top:20px}
  .page-width{width:calc(100% - 32px)}
  .site-header{min-height:72px;padding:14px 2px}
  .contact-section{margin-top:40px;scroll-margin-top:20px}
  .contact-card{padding:23px 20px 22px;border-radius:17px}
  .required-note{font-size:11px;line-height:1.7;margin-bottom:23px}
  legend{font-size:13px;gap:8px;margin-bottom:18px}.section-number{width:23px;height:23px;font-size:9px}
  .form-grid{grid-template-columns:minmax(0,1fr);gap:18px}
  .field label{font-size:13px;margin-bottom:7px;line-height:1.5}
  .field input,.field select{min-height:50px;height:50px;font-size:16px;padding:12px;border-radius:11px;line-height:1.5;letter-spacing:-.25px}
  .field input::placeholder{font-size:14px;letter-spacing:0}
  .field select{padding-right:30px}
  .field input[type="date"],.field input[type="datetime-local"]{font-size:16px}
  .field input::-webkit-calendar-picker-indicator{width:17px;height:17px}
  .select-chevron{right:12px}.field-hint{font-size:11px;line-height:1.6}.field-error{font-size:12px}
  .situation-fields{margin-top:25px;padding-top:23px}.situation-fields legend{margin-bottom:19px}
  .form-bottom{margin-top:24px;padding-top:21px}
  .privacy-note{font-size:10.5px;line-height:1.8;gap:6px}.privacy-note>span>span{font-size:10px}.privacy-note>.icon{width:12px;height:12px;margin-top:3px}
  /* Bouton d'envoi sur mobile (refonte 2026-09-19) : il était VERTICAL
     (124 × 164 px) — un pavé. Il garde maintenant exactement la même forme
     qu'ailleurs : allongé à l'horizontale, toute la largeur disponible, 58 px
     de haut. Le libellé tient sur une seule ligne. */
  .submit-button{width:100%;min-height:58px;padding:0 22px;gap:12px;font-size:14.5px;margin-top:20px}
  .site-footer{margin-top:31px;font-size:10px;gap:10px}
  .notice{font-size:11px;padding:12px}.notice-offline{gap:8px}
  .confirmation{padding:23px 0 15px}.confirmation>p{font-size:12px}.confirmation>.confirmation-lead{font-size:13px}
  .confirmation-creole{font-size:14px}
}
@media(max-width:360px){
  /* Les 6 lignes (3 affichages × titre + sous-titre) doivent toutes être
     couvertes — sinon la 3e (« Kosa la fé ? » / « Besoin un conseil ») garde les
     tailles de base et casse la cohérence du cycle sur petit écran. */
  .page-width{width:calc(100% - 28px)}
  .hero-line-welcome,.hero-line-q,.hero-line-kosa{font-size:clamp(32px,11vw,46px)}
  .hero-line-flag,.hero-line-sun,.hero-line-mem{font-size:clamp(17px,5vw,22px)}
  .contact-card{padding-inline:17px}
}

/* ── Accessibilité moteur : rien ne bouge si l'utilisateur le demande.
   L'inclinaison 3D des portraits et du logo du pied de page est remise à plat
   (voir .team-photo-3d / .footer-logo-layer plus haut) et toutes les animations
   sont ramenées à leur durée minimale. ──────────────────────────────────── */
@media(prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important;scroll-behavior:auto!important}
  .button-primary:hover:not(:disabled){transform:none}
  [data-reveal]{opacity:1;transform:none}
  /* Pas d'inclinaison flottante sur mobile non plus : le relief 3D est plat.
     Le logo du pied de page est neutralisé en même temps que les portraits. */
  .team-photo-3d,.footer-logo-layer{animation:none!important;transform:rotateX(0) rotateY(0)!important}
  #confirmation:not([hidden]){animation:none!important}
}

/* ─────────────────────────────────────────────────────────────────────────
   Section « Ki sa nou lé » — SECTION DE PAGE depuis la restructuration en une
   seule page à défilement vertical (2026-09-16). Ce n'est plus une fenêtre
   superposée : plus de position fixed, plus de voile, plus de z-index 80, plus
   de blocage du scroll — elle vit simplement dans le flux, après l'introduction.
   Ce qui est conservé : la mise en page photo + texte, l'effet 3D d'inclinaison
   (composant card-17) et la cascade mot par mot du texte (AnimatedTestimonials).
   ───────────────────────────────────────────────────────────────────────── */
.team-section{max-width:1120px;margin:clamp(34px,6vh,64px) auto 0;position:relative;z-index:1;scroll-margin-top:88px}
.team-stack{display:flex;flex-direction:column;gap:clamp(30px,5vh,54px);width:100%;position:relative}
.team-title{font-family:"Inter",-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;font-weight:900;color:var(--white);font-size:clamp(40px,7vw,72px);line-height:.96;letter-spacing:-.045em;margin:0 0 8px;text-align:center}
/* Accent « Ki sa » : jaune par défaut (le rouge servait à suivre la photo
   active du carrousel, disparu). */
.team-accent{color:var(--yellow)}
/* « Ki sa » (titre de la section « Ki sa nou lé ») est en ROUGE depuis le
   2026-09-18. Le sélecteur reste scopé à #team : c'est la seule section qui
   porte un accent aujourd'hui. */
#team .team-accent{color:var(--red)}
.team-stage{display:grid;grid-template-columns:1fr;gap:clamp(20px,3vh,32px);align-items:center}
@media(min-width:780px){.team-stage{grid-template-columns:320px 1fr;gap:48px;align-items:start}}
/* Cadre CARRÉ : les 2 photos sources sont carrées (3024×3024), donc
   `object-fit: cover` ne rogne plus RIEN — les visages, cheveux et mains sont
   entièrement visibles. Le carrousel ayant disparu, le curseur « grab » et le
   `touch-action: pan-y` du balayage ont été retirés.
   ── SOCLE 3D PARTAGÉ ─────────────────────────────────────────────────────
   `.footer-logo` — le logo ajouté tout en bas de la page, sous les blocs
   animés de « Kosa nou pé dire ? » (2026-09-21) — est groupé ici avec
   `.team-photos` : même boîte carrée, même `perspective`, et plus bas la même
   couche interne qui reçoit --tilt-x / --tilt-y (`.team-photo-3d` et
   `.footer-logo-layer` sont groupés de la même façon). C'est ce qui rend la
   cohérence RÉELLE plutôt qu'approchante : les deux blocs partagent les
   déclarations, donc ils ne peuvent pas dériver l'un de l'autre. Seules les
   largeurs diffèrent. */
.team-photos,.footer-logo{position:relative;width:100%;aspect-ratio:1/1;margin-inline:auto;perspective:1200px;user-select:none;-webkit-user-select:none}
.team-photos{max-width:320px}
/* `overflow:hidden` a été retiré de la carte : c'est une propriété « groupante »
   qui force transform-style:flat et écraserait tout le relief 3D du texte.
   Le rognage aux coins est donc porté par l'image elle-même (border-radius). */
.team-photo-card{position:absolute;inset:0;border-radius:18px;box-shadow:0 18px 48px rgba(2,4,53,.45);opacity:0;transform:translateY(0) scale(.92) rotateY(-12deg);transition:opacity .45s var(--ease-fade),transform .45s var(--ease-fade);pointer-events:none;transform-style:preserve-3d}
.team-photo-card.is-active{opacity:1;transform:translateY(0) scale(1) rotateY(0);pointer-events:auto;z-index:2}
.team-photo-card img{width:100%;height:100%;object-fit:cover;display:block;background:var(--night);border-radius:18px}
/* ── Effet 3D « tilt » — réécriture en CSS natif du composant card-17
   (LocationCard de 21st.dev, framer-motion). Même concept, mêmes valeurs :
   la carte s'incline de ±10° en suivant le pointeur (rotateX piloté par Y,
   rotateY piloté par X, comme les useTransform du composant d'origine), le
   lissage du `useSpring` est obtenu par une transition courte sur --ease-pull,
   et le texte se détache en profondeur via translateZ — d'où le preserve-3d à
   chaque niveau. L'inclinaison vit sur une couche interne pour ne pas entrer en
   conflit avec la transition de 450 ms que le carrousel anime déjà sur
   `transform`. --tilt-x / --tilt-y sont posées par app.js (0 au repos).
   Groupé avec `.footer-logo-layer` : le logo du pied de page reçoit exactement
   la même inclinaison, avec la même transition et la même amplitude. ── */
.team-photo-3d,.footer-logo-layer{position:absolute;inset:0;transform-style:preserve-3d;transform:rotateX(var(--tilt-x,0deg)) rotateY(var(--tilt-y,0deg));transition:transform .25s var(--ease-pull)}
/* ── 3D au TACTILE ──────────────────────────────────────────────────────────
   Sur ordinateur, l'inclinaison suit le pointeur (pointermove, ci-dessus).
   Sur mobile il n'y a pas de survol : le doigt ne fait que défiler, donc
   l'effet 3D restait imperceptible. Solution adaptée : une inclinaison
   « flottante » DOUCE et CONTINUE, jouée en boucle sur chaque photo
   (équivalent tactile au tilt au pointeur). Elle est réservée aux appareils
   à pointeur grossier (hover:none / pointer:coarse) et neutralisée en
   prefers-reduced-motion (cf. bloc dédié). La perspective héritée de
   .team-photos (1200px) suffit à rendre le relief lisible. Le logo du pied de
   page flotte exactement de la même façon. ───────────────────────────────── */
@media (hover:none) and (pointer:coarse){
  .team-photo-3d,.footer-logo-layer{animation:photoFloat 7s ease-in-out infinite}
}
@keyframes photoFloat{
  0%   {transform:rotateX(8deg) rotateY(-12deg)}
  50%  {transform:rotateX(-8deg) rotateY(12deg)}
  100% {transform:rotateX(8deg) rotateY(-12deg)}
}
/* Dégradé de lisibilité du composant d'origine (from-black/70 via-black/30
   to-transparent), décliné sur le bleu nuit de la palette du site. */
.team-photo-scrim{position:absolute;inset:0;border-radius:18px;background:linear-gradient(to top,rgba(2,4,53,.80) 0%,rgba(2,4,53,.40) 40%,rgba(2,4,53,0) 70%)}
/* Légende posée en bas de la photo, détachée de 50 px vers l'avant — exactement
   le translateZ(50px) du bloc de texte du composant d'origine. */
/* `padding` un peu plus large que les 16 px habituels : le translateZ agrandit
   la légende d'environ 4 % autour du centre de la photo, ce qui rapproche son
   texte des bords d'autant. 20 px rendent donc ~13 px d'air réel.
   Police montée de 2 tailles (12→14 px, 13→15 px) et texte centré. */
.team-photo-caption{position:absolute;left:0;right:0;bottom:0;margin:0;padding:20px;transform:translateZ(50px);color:var(--white);font-size:clamp(14px,3.6vw,15px);line-height:1.35;text-align:center;text-wrap:balance;text-shadow:0 1px 6px rgba(2,4,53,.8)}
.team-photo-caption strong{font-weight:700;letter-spacing:.01em}
.team-text{position:relative;min-height:120px;display:grid}
.team-text-card{opacity:0;transform:translateY(8px);transition:opacity .35s var(--ease-fade),transform .35s var(--ease-fade);pointer-events:none;grid-area:1/1}
.team-text-card.is-active{opacity:1;transform:translateY(0);pointer-events:auto}
.team-quote{font-family:var(--serif);font-style:italic;font-weight:400;color:var(--ink);font-size:clamp(15px,1.4vw,18px);line-height:1.65;margin:0;max-width:60ch}
.team-word{display:inline-block;opacity:0;filter:blur(10px);transform:translateY(5px)}
/* La cascade part quand le bloc devient visible : app.js pose `is-words-in`
   (et non plus `is-active`, qui servait au carrousel). */
.team-text-card.is-words-in .team-word{animation:wordBlurIn .4s var(--ease-fade) both}
@keyframes wordBlurIn{from{opacity:0;filter:blur(10px);transform:translateY(5px)}to{opacity:1;filter:blur(0);transform:translateY(0)}}
@media (prefers-reduced-motion:reduce){
  .team-photo-card.is-active{animation:none}
  .team-text-card.is-words-in .team-word{animation:none;opacity:1;filter:none;transform:none}
}

/* Le formulaire est une cible d'ancre du menu (« Kontakt ») : il a besoin d'une
   marge de défilement pour ne pas se glisser sous le menu fixe. La section
   « Zévènmen », qui portait la même règle, a été retirée le 2026-09-21. */
#contact{scroll-margin-top:88px}

/* ─────────────────────────────────────────────────────────────────────────
   Section « Kosa nou pé fé ? » — les six rubriques de l'accompagnement.
   Elle prolonge directement le bloc des grands titres : le titre reprend le
   dessin des titres de section du site (.team-title) et seul « Kosa » passe en
   rouge. --red (#E50D13) donne 3,95:1 sur fond sombre : au-dessus du seuil 3:1
   exigé pour un grand texte (≥ 34 px, graisse 900) — même raisonnement que pour
   les accents du titre d'accueil.
   Les rubriques sont des briques GLASSMORPHISME : recette identique à celle du
   cadre du titre d'accueil (.hero-title) et des cartes du site (.contact-card)
   — fond translucide en dégradé, backdrop-filter, bord lumineux, ombre portée
   et liseré interne. La composition reprend celle de la maquette fournie :
   pastille d'icône ronde à gauche, intitulé en gras, description atténuée.
   ───────────────────────────────────────────────────────────────────────── */
.services-section{max-width:1120px;margin:clamp(52px,8.5vh,84px) auto 0;position:relative;z-index:1;scroll-margin-top:88px}
.services-title{font-family:"Inter",-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;font-weight:900;color:var(--white);font-size:clamp(34px,6vw,64px);line-height:.98;letter-spacing:-.045em;margin:0 0 clamp(16px,2.4vh,24px);text-align:center}
.services-accent{color:var(--red)}
/* La PISTE porte deux mises en page, selon que app.js a posé .is-deck ou non :
   - par défaut (sans JavaScript, ou en « mouvement réduit ») : la grille
     3 colonnes d'origine, où les six rubriques sont visibles d'un coup ;
   - en mode PILE : les cartes quittent le flux et s'empilent dans une scène
     de hauteur fixe (règles plus bas). */
.services-track{list-style:none;margin:0;padding:0;display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px}
.services-deck{position:relative}
/* La carte, version COMPACTE. Les six rubriques occupaient 266 px de haut :
   trop volumineuses et trop denses. On resserre tout — rembourrage, pastille
   d'icône, corps de texte — sans RIEN retirer du contenu : les six rubriques
   restent entièrement lisibles, sur ordinateur comme sur mobile.
   Deux précautions conservées :
   - `opacity:0` au repos : la carte n'apparaît qu'avec l'animation d'entrée
     ci-dessous (mode grille) ou avec sa place dans la pile (mode .is-deck) ;
   - le survol déplace la carte avec `translate` (propriété À PART du
     `transform`) : l'animation d'entrée, PUIS la pile 3D qui pilote `transform`,
     gardent ainsi la main sans jamais écraser le relief du survol, et
     réciproquement. */
.service-card{display:flex;align-items:flex-start;gap:11px;padding:13px 14px;min-height:82px;border-radius:15px;background:linear-gradient(180deg,rgba(255,255,255,.10),rgba(255,255,255,.04));-webkit-backdrop-filter:blur(18px) saturate(140%);backdrop-filter:blur(18px) saturate(140%);border:1px solid rgba(255,255,255,.18);box-shadow:0 12px 30px rgba(2,4,53,.40),inset 0 1px 0 rgba(255,255,255,.22);opacity:0;transition:translate .3s var(--ease-pull),border-color .3s var(--ease-pull),box-shadow .3s var(--ease-pull)}
@media (hover:hover){
  .service-card:hover{translate:0 -3px;border-color:rgba(255,255,255,.32);box-shadow:0 18px 40px rgba(2,4,53,.50),inset 0 1px 0 rgba(255,255,255,.32)}
}
/* Pastille d'icône : rond teinté de ROUGE depuis le 2026-09-18 (il était bleu),
   pour s'accorder au « Kosa » rouge du titre. Le tracé de l'icône utilise
   --red-bright (#FF5A4E) plutôt que --red : sur la pastille sombre, il donne
   ~6:1 alors que --red plafonne à ~3,9:1 — plus de marge si le fond animé
   éclaircit la brique. */
.service-icon{display:grid;place-items:center;flex:0 0 auto;width:32px;height:32px;border-radius:50%;background:rgba(229,13,19,.16);border:1px solid rgba(229,13,19,.32);color:var(--red-bright)}
.service-icon .icon{width:16px;height:16px;stroke-width:1.8}
.service-body{min-width:0}
.service-name{font-size:13.5px;font-weight:700;line-height:1.3;letter-spacing:-.01em;color:var(--white);margin:0 0 3px}
.service-desc{font-size:14px;line-height:1.55;color:var(--muted);margin:0;max-height:none;overflow:visible}
/* ── Entrée en mode GRILLE (sans JavaScript, ou « mouvement réduit ») : la
   section se révèle en bloc ([data-reveal]), puis les six cartes se posent
   l'une après l'autre (80 ms d'écart). `both` fige l'état final, donc aucun
   saut après l'animation. L'animation est réservée à `:not(.is-deck)` : en
   mode pile, c'est la pile elle-même qui pilote `transform` — les deux ne
   doivent jamais se superposer. ── */
.services-section.is-revealed .services-deck:not(.is-deck) .service-card{animation:cardIn .7s var(--ease-pull) both}
.services-section.is-revealed .services-deck:not(.is-deck) .service-card:nth-child(2){animation-delay:.08s}
.services-section.is-revealed .services-deck:not(.is-deck) .service-card:nth-child(3){animation-delay:.16s}
.services-section.is-revealed .services-deck:not(.is-deck) .service-card:nth-child(4){animation-delay:.24s}
.services-section.is-revealed .services-deck:not(.is-deck) .service-card:nth-child(5){animation-delay:.32s}
.services-section.is-revealed .services-deck:not(.is-deck) .service-card:nth-child(6){animation-delay:.4s}
@keyframes cardIn{from{opacity:0;transform:translateY(18px)}to{opacity:1;transform:translateY(0)}}

/* ─────────────────────────────────────────────────────────────────────────
   MODE PILE 3D — posé par app.js (classe .is-deck sur #services-deck).
   Scène de hauteur fixe ; les cartes quittent le flux et s'ancrent en BAS.
   Cinq états, exactement comme le composant « Animation 3D Card » :
     .is-slot-0  → carte de devant : échelle 1,    opacité 1
     .is-slot-1  → 2e carte        : échelle .95,  remontée de --deck-lift-1
     .is-slot-2  → 3e carte        : échelle .90,  remontée de --deck-lift-2
     .is-parked  → les suivantes   : place de la 3e, opacité 0 (en attente)
     .is-leaving → la carte sortie : descend sous la scène en s'effaçant
   `transform-origin:50% 100%` : la réduction d'échelle s'appuie sur le BAS de
   la carte, donc le bord inférieur ne bouge jamais — la pile reste stable et
   bien alignée pendant toute l'animation.
   La scène coupe ce qui dépasse (`overflow:hidden`) : la carte sortante
   disparaît proprement par le bas, sans jamais créer de barre de défilement.
   Seule la carte de devant reçoit les événements du pointeur : les cartes en
   retrait ne réagissent pas au survol dans la zone où elles dépassent.
   ───────────────────────────────────────────────────────────────────────── */
.services-deck.is-deck{--deck-h:140px;--deck-lift-1:34px;--deck-lift-2:60px;height:calc(var(--deck-h) + var(--deck-lift-2));overflow:hidden}
.services-deck.is-deck .services-track{display:block;height:100%}
.services-deck.is-deck .service-card{position:absolute;left:0;right:0;bottom:0;margin-inline:auto;width:min(100%,620px);height:var(--deck-h);min-height:0;align-items:center;gap:16px;padding:0 26px;transform-origin:50% 100%;transform:translateY(calc(-1 * var(--deck-lift-2))) scale(.9);opacity:0;z-index:0;pointer-events:none;background-color:rgba(9,13,22,.62);background-image:linear-gradient(180deg,rgba(255,255,255,.13),rgba(255,255,255,.05));transition:transform .72s var(--ease-pull),opacity .72s var(--ease-pull),translate .3s var(--ease-pull),border-color .3s var(--ease-pull),box-shadow .3s var(--ease-pull);will-change:transform,opacity}
/* Première peinture : les cartes rejoignent leur place SANS transition — sinon
   elles « voleraient » depuis le coin au moment où la pile s'installe. */
.services-deck.is-deck.is-deck-init .service-card,
.services-deck.is-deck.is-deck-init .service-card .service-icon,
.services-deck.is-deck.is-deck-init .service-card .service-body{transition:none}
/* Le contenu n'apparaît QUE sur la carte de devant (et pendant qu'elle sort) :
   les cartes en retrait ne montrent que leur forme. Sans cela, leur texte
   transparaîtrait à travers la carte de devant, qui est en verre — c'est
   exactement le défaut que l'on voyait. Quand une carte prend la place de
   devant, son contenu se pose juste après elle (léger décalage de 180 ms). */
.services-deck.is-deck .service-card .service-icon,
.services-deck.is-deck .service-card .service-body{opacity:0;transition:opacity .45s ease}
.services-deck.is-deck .service-card.is-slot-0 .service-icon,
.services-deck.is-deck .service-card.is-slot-0 .service-body{opacity:1;transition-delay:.18s}
.services-deck.is-deck .service-card.is-leaving .service-icon,
.services-deck.is-deck .service-card.is-leaving .service-body{opacity:1;transition-delay:0s}
.services-deck.is-deck .service-card.is-slot-0{transform:translateY(0) scale(1);opacity:1;z-index:3;pointer-events:auto}
.services-deck.is-deck .service-card.is-slot-1{transform:translateY(calc(-1 * var(--deck-lift-1))) scale(.95);opacity:.66;z-index:2}
.services-deck.is-deck .service-card.is-slot-2{transform:translateY(calc(-1 * var(--deck-lift-2))) scale(.9);opacity:.42;z-index:1}
.services-deck.is-deck .service-card.is-parked{transform:translateY(calc(-1 * var(--deck-lift-2))) scale(.9);opacity:0;z-index:0}
.services-deck.is-deck .service-card.is-leaving{transform:translateY(var(--deck-h)) scale(1);opacity:0;z-index:4}
/* Dans la pile, la carte respire davantage qu'en grille : pastille plus large,
   intitulé et description d'un cran au-dessus, interligne confortable. */
.services-deck.is-deck .service-icon{width:42px;height:42px;background:rgba(229,13,19,.18);border-color:rgba(229,13,19,.36)}
.services-deck.is-deck .service-icon .icon{width:21px;height:21px;stroke-width:1.7}
.services-deck.is-deck .service-name{font-size:16px;margin-bottom:5px}
.services-deck.is-deck .service-desc{font-size:13.5px;line-height:1.6}
/* 3 colonnes → 2 → 1 : la carte reste lisible sans jamais comprimer le texte. */
@media (max-width:1050px){
  .services-track{grid-template-columns:repeat(2,minmax(0,1fr))}
  .services-deck.is-deck{--deck-h:148px}
}
/* Android Chrome : le backdrop-filter d'une carte qui défile au-dessus d'un
   fond fixed se repeint mal — même correctif que .contact-card. */
@media (max-width:800px){
  .service-card{background:rgba(15,17,23,.92);-webkit-backdrop-filter:none;backdrop-filter:none}
  /* Sans flou, la carte de la pile doit être quasi opaque : sinon les cartes
     en retrait se verraient au travers. */
  .services-deck.is-deck .service-card{background-color:rgba(11,15,24,.93)}
}
@media (max-width:700px){
  .services-track{grid-template-columns:minmax(0,1fr);gap:10px}
  .services-deck.is-deck{--deck-h:170px;--deck-lift-1:32px;--deck-lift-2:60px}
  .services-deck.is-deck .service-card{padding:0 20px;gap:14px}
}
@media (max-width:420px){
  .services-deck.is-deck{--deck-h:196px;--deck-lift-1:30px;--deck-lift-2:58px}
  .services-deck.is-deck .service-icon{width:38px;height:38px}
}
/* Mouvement réduit : aucune cascade et aucune pile (app.js ne pose jamais
   .is-deck dans ce cas) — les cartes sont visibles d'emblée, texte entier. */
@media (prefers-reduced-motion:reduce){
  .service-card{opacity:1;transform:none;translate:none;transition:none;animation:none!important}
  .service-desc{max-height:none;overflow:visible;animation:none!important}
}

/* ─────────────────────────────────────────────────────────────────────────
   Section « Kosa ou la besoin ? » — vignettes SÉLECTIONNABLES.
   Elle suit immédiatement les rubriques « Kosa nou pé fé ? » et reprend le même
   dessin de titre, avec « Kosa » en jaune cette fois.
   Deux catégories côte à côte (Guide / Installation · Conception & Promotion),
   chacune avec sa vignette. PLUSIEURS choix possibles à la fois : rien n'oblige
   à n'en cocher qu'une. La section en comptait quatre jusqu'au 2026-09-21 —
   Assurance, Impôts et Prévoyance & Patrimoine ont été remplacés par le seul
   module « Accompagnement à l'installation & Démarches helvétiques ».
   Le dessin des vignettes suit la maquette fournie : pastille d'icône en haut,
   intitulé en gras, description atténuée — même glassmorphisme que les briques
   de la section précédente.
   LA SÉLECTION SE MATÉRIALISE PAR UNE SEULE CHOSE : LA COULEUR DU LISERÉ, et
   par rien d'autre — aucun changement de fond, aucune coche, aucune icône
   d'état, aucune surbrillance, aucun décalage. La largeur du liseré ne variant
   pas, la mise en forme des vignettes reste identique dans tous les états.
   Le survol, lui, se contente d'ÉCLAIRCIR le contour neutre : il ne prend
   jamais une couleur, sinon on ne saurait plus distinguer « survolé » de
   « choisi ». Le liseré de sélection est JAUNE partout, sauf sur
   « Conception & Promotion », où il est ROUGE. Voir plus bas.
   ───────────────────────────────────────────────────────────────────────── */
.needs-section{max-width:1120px;margin:clamp(38px,6.5vh,72px) auto 0;position:relative;z-index:1;scroll-margin-top:88px}
.needs-title{font-family:"Inter",-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;font-weight:900;color:var(--white);font-size:clamp(34px,6vw,64px);line-height:.98;letter-spacing:-.045em;margin:0 0 clamp(24px,3.6vh,36px);text-align:center}
.needs-accent{color:var(--yellow)}
/* Mention « Choisissez selon vos besoins » : centrée sous le titre, avec la
   petite croix rouge qui marque le caractère obligatoire. Le `-10px` de marge
   haute la rapproche du titre (dont la marge basse est généreuse). */
.needs-hint{display:flex;align-items:center;justify-content:center;gap:8px;margin:-10px 0 clamp(20px,3vh,28px);font-size:13px;line-height:1.5;color:var(--muted);text-align:center}
.needs-cross{font-size:14px;font-weight:800;line-height:1;color:var(--red)}
/* Le message d'erreur des vignettes suit la mention, pas les champs. */
.needs-error{margin:calc(-1 * clamp(12px,2vh,20px)) 0 clamp(16px,2.4vh,22px);text-align:center}
/* Deux domaines depuis le 2026-09-21 (les trois domaines d'assurance, d'impôts
   et de prévoyance ont fusionné en un seul module) : une colonne chacun sur
   grand écran, puis une seule colonne. */
.needs-groups{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:22px;align-items:start}
.need-group{margin:0;padding:0;border:0}
/* Nom de catégorie : petites capitales jaunes, comme l'accent du titre. */
.need-group-name{display:block;padding:0;margin:0 0 13px;font-size:11.5px;font-weight:800;letter-spacing:.14em;text-transform:uppercase;color:var(--yellow)}
.needs-grid{display:grid;grid-template-columns:minmax(0,1fr);gap:12px}
/* La vignette est un <label> : toute sa surface est cliquable, donc rien à
   viser de précis au doigt. L'<input> reste dans le flux d'accessibilité
   (focusable, annoncé, coché/décoché au clavier) mais n'est pas peint : il est
   ramené à 1 px et rogné, SANS `left:-10000px` — sinon le focus clavier ferait
   défiler la page hors de la vignette. */
.need-card{position:relative;display:block;cursor:pointer;opacity:0}
.need-input{position:absolute;width:1px;height:1px;margin:0;padding:0;border:0;overflow:hidden;clip-path:inset(50%);white-space:nowrap}
.need-face{position:relative;display:flex;flex-direction:column;align-items:flex-start;gap:9px;height:100%;padding:18px;border-radius:16px;background:linear-gradient(180deg,rgba(255,255,255,.09),rgba(255,255,255,.035));-webkit-backdrop-filter:blur(18px) saturate(140%);backdrop-filter:blur(18px) saturate(140%);border:1.5px solid var(--liseré-repos);box-shadow:0 14px 38px rgba(2,4,53,.40),inset 0 1px 0 rgba(255,255,255,.16);transition:border-color .3s var(--ease-pull)}
/* ── Couleur du liseré, selon l'état de la vignette ─────────────────────────
   Trois variables pilotent toute la section, et rien d'autre ne décide des
   couleurs : c'est ce qui permet de tout éteindre d'un seul geste (voir
   l'interrupteur juste après).

     --liseré-repos   le contour permanent, identique sur les deux vignettes
     --liseré-survol  le même contour, simplement ÉCLAIRCI — jamais une couleur
     --liseré-choisi  la couleur de sélection, une fois la vignette cochée

   Par défaut, la sélection est jaune : c'est le signal unique de la section.
   Le domaine « Conception & Promotion » ne déroge qu'à la SÉLECTION, où elle
   passe au rouge — voir plus bas. */
.needs-section{
  --liseré-repos:rgba(255,255,255,.16);
  --liseré-survol:rgba(255,255,255,.38);
  --liseré-choisi:var(--yellow);
}
/* LA COULEUR DU LISERÉ NE DIT QU'UNE CHOSE : LA SÉLECTION.
   Deux règles décident de la couleur, et deux seulement :

     :hover (sans coche)  → contour neutre éclairci
     :checked             → couleur de sélection

   Le survol est donc délibérément NEUTRE, et non jaune. C'était le défaut :
   le jaune du survol était la couleur même de la sélection, si bien qu'une
   vignette décochée mais encore survolée restait jaune — impossible de voir
   que le clic avait bien décoché, il fallait éloigner le pointeur pour que le
   liseré s'éteigne. Le bloc rouge, lui, échappait au problème par accident
   (une règle neutralisait son survol), d'où l'incohérence constatée : rouge
   fiable, jaunes non.

   Avec un survol neutre, les deux vignettes se comportent à l'identique : le
   clic allume la couleur, un second clic l'éteint IMMÉDIATEMENT, sans que le
   pointeur ait à bouger. La couleur ne peut plus être confondue avec autre
   chose que la sélection.

   `:not(:checked)` sur la règle de survol rend les deux règles mutuellement
   exclusives : une vignette cochée et survolée garde sa couleur, et cela ne
   dépend pas de l'ordre du fichier.

   Rien d'autre ne change : même fond, même ombre, même position (pas de
   décalage au survol), et aucune coche ni icône d'état. La largeur du liseré
   ne bouge pas non plus, donc aucune option ne se réagence quand on la coche.
   `:checked +` évite `:has()` et tout l'état est porté par le CSS : les choix
   fonctionnent même sans JavaScript. Les deux vignettes partagent la même
   classe — leur mise en forme reste donc rigoureusement identique.

   Le survol est réservé aux appareils qui en ont vraiment un (`hover:hover`) :
   sur écran tactile, un appui laisse un `:hover` collé, et le contour resterait
   éclairci après le décochage — exactement le défaut qu'on vient de corriger. */
@media (hover:hover){
  .need-card:hover .need-input:not(:checked)+.need-face{border-color:var(--liseré-survol)}
}
.need-input:checked+.need-face{border-color:var(--liseré-choisi)}
.need-input:focus-visible+.need-face{outline:2px solid var(--yellow);outline-offset:3px}
.need-icon{display:grid;place-items:center;width:40px;height:40px;border-radius:12px;background:rgba(241,217,10,.14);border:1px solid rgba(241,217,10,.24);color:var(--yellow)}
.need-icon .icon{width:20px;height:20px;stroke-width:1.7}
.need-name{font-size:14.5px;font-weight:700;line-height:1.3;letter-spacing:-.01em;color:var(--white)}
.need-desc{font-size:12.5px;line-height:1.55;color:var(--muted)}

/* ── « Conception & Promotion » : le bloc au liseré ROUGE ───────────────────
   Le titre (nom de catégorie) et le sigle caméra sont rouges en permanence :
   c'est ce qui identifie le domaine au repos. Le LISERÉ, lui, n'est rouge que
   lorsqu'une vignette est CHOISIE — il sert à confirmer le choix, pas à
   décorer.

   C'est la SEULE ligne qui distingue ce bloc de l'autre, et
   elle ne touche que la couleur de SÉLECTION : contour de repos, survol,
   transition et géométrie sont communs aux deux vignettes. La règle
   `.need-face--pro{--liseré-survol:var(--liseré-repos)}` qui neutralisait son
   survol a été supprimée : le survol est neutre pour tout le monde maintenant,
   et c'est précisément cette exception qui rendait le bloc rouge fiable alors
   que les jaunes ne l'étaient pas.

   Le rouge de sélection est posé en `--red-bright` plein (~4,9:1 sur le fond
   réellement peint de la vignette), donc bien au-dessus du seuil de 3:1 d'un
   élément non textuel. La transition de .3 s de `.need-face` fait apparaître et
   disparaître le liseré en suivant l'état de la case, y compris quand on la
   décoche.

   La garde `:not([data-liseres="off"])` est indispensable : sans elle, cette
   règle l'emporterait sur l'interrupteur (elle vise la vignette elle-même,
   alors que l'interrupteur ne pose la valeur que sur la section), et le rouge
   résisterait à la mise hors service. */
.need-group-name--pro{color:var(--red-bright)}
.needs-section:not([data-liseres="off"]) .need-face--pro{--liseré-choisi:var(--red-bright)}
.need-icon--pro{background:rgba(229,13,19,.16);border-color:rgba(255,90,78,.34);color:var(--red-bright)}
/* La description est demandée en blanc, alors qu'elle est atténuée partout
   ailleurs. La hiérarchie tient quand même : le nom reste plus grand (14,5 px)
   et plus gras (700) que la description (12,5 px, 400). */
.need-desc--pro{color:var(--white)}

/* ── Interrupteur : afficher ou masquer les liserés ────────────────────────
   Poser `data-liseres="off"` sur la section éteint TOUS les liserés de la
   section d'un seul coup : plus aucun contour ne change, ni au survol, ni à la
   sélection, sur aucune des deux vignettes. Le contour neutre du repos reste —
   sans lui, les vignettes flotteraient sans bord.

   Dans index.html :      <section class="needs-section" id="besoins" data-liseres="off" …>
   Depuis la console :    document.getElementById('besoins').dataset.liseres = 'off'
   Par l'URL :            /?liseres=off        (et /?liseres=on pour rallumer)
   Pour rallumer :        retirer l'attribut, ou lui donner toute autre valeur.

   Le paramètre d'URL est posé par app.js, qui ne fait que recopier la valeur
   dans l'attribut : le rendu reste entièrement décidé ici, par le CSS. Le
   réglage fonctionne donc aussi sans JavaScript, et les cases à cocher restent
   parfaitement utilisables — seul le retour visuel disparaît. */
.needs-section[data-liseres="off"]{
  --liseré-survol:var(--liseré-repos);
  --liseré-choisi:var(--liseré-repos);
}
/* Entrée en cascade, même rythme que les briques de « Kosa nou pé fé ? ».
   Chaque domaine ne portant plus qu'une vignette, le décalage se prend d'un
   domaine à l'autre (gauche → droite) et non plus entre deux vignettes d'une
   même colonne. */
.needs-section.is-revealed .need-card{animation:cardIn .7s var(--ease-pull) both}
.needs-section.is-revealed .need-group:nth-child(2) .need-card{animation-delay:.08s}
/* 2 colonnes → 1 : chaque domaine garde sa vignette lisible. Sous 820 px, deux
   colonnes donneraient des vignettes trop étroites pour l'intitulé long
   (« Accompagnement à l'installation & Démarches helvétiques »). */
@media (max-width:820px){.needs-groups{grid-template-columns:minmax(0,1fr)}}
@media (max-width:800px){
  /* Même correctif Android que .contact-card et .service-card. Le fond ne
     change plus à la sélection : seule la couleur du liseré varie. */
  .need-face{background:rgba(15,17,23,.92);-webkit-backdrop-filter:none;backdrop-filter:none}
}
@media (max-width:700px){
  .needs-groups{gap:20px}
  .need-face{padding:16px}
}
/* Mouvement réduit : pas de cascade, vignettes visibles d'emblée. */
@media (prefers-reduced-motion:reduce){
  .need-card{opacity:1;transform:none}
}

/* ═════════════════════════════════════════════════════════════════════════
   PAGE « POLITIQUE DE CONFIDENTIALITÉ » (/confidentialite.html)
   Ajoutée le 2026-09-21 pour fermer l'écart de conformité relevé à l'audit
   (§15.5) : la mention sous la case de consentement affirmait que rien n'était
   transmis à des tiers, alors que le formulaire remet la demande à une
   messagerie tierce. La page décrit le flux RÉEL, et la mention y renvoie.

   Elle ne charge AUCUN script, et c'est un choix, pas un oubli : c'est une page
   de contenu, alors que app.js est un module lié à la page unique (menu,
   formulaire, canvas de fond, confettis, service worker) qui n'aurait rien à y
   faire et s'exécuterait à vide. Conséquence utile : la CSP de production
   (`script-src 'self'`, sans 'unsafe-inline') n'est jamais sollicitée ici — ni
   style ni script en ligne, tout vient de cette feuille et des polices locales.

   Le fond réutilise la pile existante (.bg-stack / .bg-photo / .bg-veil) SANS
   le canvas : on garde la base nuit et le voile qui respire, sans dessiner de
   faisceaux. Le voile est neutralisé en mouvement réduit par le bloc global
   ci-dessus (durées ramenées à .01ms), donc rien à ajouter ici.

   Aucune couleur en dur : tout passe par les variables de :root, pour que la
   page suive la palette du site si elle évolue. ───────────────────────── */
.legal{padding-top:clamp(24px,5vh,48px)}
.legal-back{margin:0 0 clamp(18px,3vh,28px)}
.legal-back-link{display:inline-flex;align-items:center;gap:8px;min-height:44px;font-size:13px;font-weight:600;color:var(--muted);text-decoration:none;transition:color .18s ease}
.legal-back-link:hover,.legal-back-link:focus-visible{color:var(--cream)}
.legal-back-link .icon{width:16px;height:16px}
.legal-head{max-width:64ch}
.legal-eyebrow{margin:0 0 12px;font-size:10px;font-weight:800;letter-spacing:.16em;text-transform:uppercase;color:var(--yellow)}
.legal-title{font-family:"Inter",-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;font-weight:900;font-size:clamp(29px,5vw,44px);line-height:1.05;letter-spacing:-.04em;color:var(--white);margin:0 0 18px}
.legal-lead{font-family:var(--serif);font-style:italic;font-weight:400;font-size:clamp(15px,1.9vw,18px);line-height:1.7;letter-spacing:-.01em;color:var(--muted);margin:0}
.legal-updated{margin:16px 0 0;font-size:11.5px;color:var(--muted-soft)}

/* Corps de page : une seule colonne de lecture, largeur confortable (~72
   caractères), pour que les paragraphes juridiques restent lisibles. */
.legal-body{max-width:72ch;margin:clamp(28px,5vh,44px) 0 0}
.legal-section{margin:0 0 clamp(26px,4vh,36px)}
.legal-section:last-child{margin-bottom:0}
.legal-body h2{font-family:var(--font);font-size:clamp(17px,2.2vw,20px);font-weight:750;letter-spacing:-.02em;line-height:1.35;color:var(--white);margin:0 0 12px}
.legal-body h3{font-size:13px;font-weight:700;letter-spacing:.02em;text-transform:uppercase;color:var(--blue-bright);margin:22px 0 8px}
.legal-body p{font-size:14px;line-height:1.75;color:var(--muted);margin:0 0 12px}
.legal-body p:last-child{margin-bottom:0}
.legal-body strong{color:var(--cream);font-weight:650}
.legal-body a{color:var(--cream);text-decoration:underline;text-decoration-color:rgba(252,252,253,.34);text-underline-offset:3px;transition:text-decoration-color .18s ease}
.legal-body a:hover,.legal-body a:focus-visible{text-decoration-color:currentColor}
.legal-body ul{margin:0 0 12px;padding:0;list-style:none}
.legal-body li{position:relative;padding-left:20px;margin-bottom:9px;font-size:14px;line-height:1.7;color:var(--muted)}
.legal-body li:last-child{margin-bottom:0}
/* Puce dessinée en CSS (le tiret de la palette) : aucune image à charger, donc
   rien à ajouter au précache du service worker. */
.legal-body li::before{content:"";position:absolute;left:2px;top:.68em;width:7px;height:1.5px;background:var(--blue-bright);border-radius:2px}
.legal-body li strong{color:var(--cream)}

/* Tableau des champs recueillis. Pas de <table> : sur un téléphone, un vrai
   tableau se met à défiler horizontalement ou à casser ses colonnes. Ici, deux
   colonnes souples — le libellé, puis l'étiquette obligatoire/facultatif — et
   les filets de séparation sont obtenus par le fond de la grille (gap:1px),
   ce qui évite toute bordure à poser sur chaque ligne. */
.legal-fields{display:grid;gap:1px;margin:0 0 12px;padding:0;list-style:none;background:var(--line-soft);border:1px solid var(--line);border-radius:14px;overflow:hidden}
.legal-fields li{display:flex;align-items:baseline;justify-content:space-between;gap:14px;padding:10px 15px;margin:0;background:rgba(15,17,23,.72);font-size:13.5px;line-height:1.5}
.legal-fields li::before{content:none}
.legal-field-tag{flex:0 0 auto;font-size:9.5px;font-weight:800;letter-spacing:.09em;text-transform:uppercase;color:var(--muted-soft);white-space:nowrap}
/* « obligatoire » porte le bleu du site (AA sur fond nuit), « facultatif »
   reste en gris : la hiérarchie se lit sans avoir à comparer deux couleurs. */
.legal-field-tag.is-required{color:var(--blue-bright)}

/* Encadré d'information : sert à la divulgation du destinataire, le point que
   la mention précédente passait sous silence. Même famille de surface que les
   autres cartes du site (fond translucide teinté + liseré). */
.legal-note{display:flex;gap:13px;margin:18px 0;padding:16px 18px;border-radius:14px;background:rgba(63,115,189,.10);border:1px solid rgba(63,115,189,.28)}
.legal-note>.icon{flex:0 0 auto;width:19px;height:19px;margin-top:2px;color:var(--blue-bright)}
.legal-note p{margin:0;font-size:13.5px}
.legal-note p+p{margin-top:9px}
.legal-note strong{color:var(--cream)}

@media (max-width:560px){
  /* Sur un écran étroit, l'étiquette passe SOUS le libellé : sur une seule
     ligne, « Jour et tranche horaire préférés » et « facultatif » se
     comprimeraient l'un contre l'autre. */
  .legal-fields li{flex-direction:column;gap:3px;align-items:flex-start}
}
