
html{scroll-behavior:smooth}
body{margin:0;background:#0b0c0b}
a{color:#cfe98a}a:hover{color:#ffffff}
::selection{background:#9dc41a;color:#0b0c0b}
.mq{display:flex;width:max-content;animation:mq 52s linear infinite}
@keyframes mq{to{transform:translateX(-50%)}}
.ln{display:block;overflow:hidden;overflow:clip;padding-bottom:.05em}
.ln>span{display:block;animation:up 1.15s cubic-bezier(.2,.75,.15,1) both}
.d1>span{animation-delay:.05s}.d2>span{animation-delay:.16s}.d3>span{animation-delay:.27s}
@keyframes up{from{transform:translateY(108%)}to{transform:none}}
.fade{animation:fade 1.3s .55s ease both}.fade2{animation:fade 1.3s .8s ease both}
@keyframes fade{from{opacity:0;transform:translateY(18px)}to{opacity:1;transform:none}}
.kb{animation:kb 20s cubic-bezier(.2,.6,.2,1) both}
/* Hero : une interface en 3D qui se monte au scroll (pièces en contour qui tournent, s'emboîtent puis se remplissent) */
.scrub{height:220vh}
.stage{position:sticky;top:0;height:100vh;min-height:720px;max-height:1100px;--p:0;--g:calc(var(--p) * var(--p) * (3 - 2 * var(--p)))}
.hlift{--h:clamp(0, var(--p) / 0.32, 1);translate:0 calc((1 - var(--h) * var(--h) * (3 - 2 * var(--h))) * 140px)}
.rise{--r:clamp(0, (var(--p) - var(--t)) / 0.2, 1);opacity:var(--r);translate:0 calc((1 - var(--r)) * 46px)}
/* Une fois le bouton apparu, tout le bloc de texte continue de monter doucement jusqu'à la fin du montage 3D */
.herotxt{--k:clamp(0, (var(--p) - 0.56) / 0.44, 1);translate:0 calc(var(--k) * -1 * clamp(30px, (100vh - 800px) / 2, 120px))}
@media (max-width: 767px){.hlift,.herotxt{translate:none}}
.persp{position:absolute;inset:0;perspective:1800px;perspective-origin:68% 45%;pointer-events:none}
.sway{position:absolute;left:calc(69% - 380px);top:calc(50% - 230px);width:760px;height:480px;transform-style:preserve-3d;animation:sway 11s ease-in-out infinite}
@keyframes sway{0%,100%{transform:rotateY(-2.5deg) translateY(-5px)}50%{transform:rotateY(2.5deg) translateY(7px)}}
.scene{position:absolute;inset:0;transform-style:preserve-3d;scale:var(--s,1);transform:rotateX(calc((18 - 10 * var(--g)) * 1deg)) rotateY(calc((-52 + 34 * var(--g)) * 1deg))}
.pn{position:absolute;border-radius:12px;--q:clamp(0, (var(--p) - var(--t)) / 0.4, 1);--e:calc(var(--q) * var(--q) * (3 - 2 * var(--q)));--f:clamp(0, (var(--e) - 0.55) / 0.45, 1);--u:calc(1 - var(--e));transform:translate3d(calc(var(--x) * var(--u) * 1px), calc(var(--y) * var(--u) * 1px), calc((var(--z) * 0.55 * var(--u) + var(--fz)) * 1px)) rotateX(calc(var(--rx) * var(--u) * 1deg)) rotateY(calc(var(--ry) * var(--u) * 1deg));opacity:calc(0.9 + var(--q))}
.pn::before{content:"";position:absolute;inset:0;border-radius:inherit;border:1px solid var(--ac);box-shadow:0 0 16px -4px var(--ac), inset 0 0 16px -8px var(--ac);opacity:calc(1 - var(--f))}
.pn>.bg{position:absolute;inset:0;border-radius:inherit;background:linear-gradient(160deg,#22261f,#121411);border:1px solid rgba(255,255,255,.1);box-shadow:0 40px 80px -40px rgba(0,0,0,.9);opacity:var(--f)}
.pn .b{position:absolute;border-radius:6px;border:1px solid rgba(238,240,234,.34);background:rgba(238,240,234,calc(0.15 * var(--f)))}
.pn .b.ac{border-color:var(--ac)}.pn .b.ac::after{content:"";position:absolute;inset:-1px;border-radius:inherit;background:var(--ac);opacity:var(--f)}
.pn .ph{position:absolute;border-radius:8px;border:1px dashed rgba(238,240,234,.36);overflow:hidden;overflow:clip}
.pn .ph::after{content:"";position:absolute;inset:0;background:linear-gradient(160deg, color-mix(in srgb, var(--ac) 9%, #20241f), #1a1d19);opacity:var(--f)}
.pn .cd{position:absolute;left:18px;top:16px;margin:0;font-family:'JetBrains Mono',monospace;font-size:12.5px;line-height:1.75;white-space:pre;color:#8f958c;opacity:calc(0.35 + 0.65 * var(--f))}
.glow3d{position:absolute;inset:0;background:radial-gradient(34% 46% at 69% 52%, color-mix(in srgb, var(--ac) 13%, transparent), transparent 72%)}
@media (max-width: 767px){.sway{left:calc(50% - 380px);top:-25px}.glow3d{background:radial-gradient(60% 24% at 50% 17%, color-mix(in srgb, var(--ac) 14%, transparent), transparent 72%)}.herotxt{padding-top:350px!important}.stage{position:relative;height:auto;min-height:900px;max-height:none}.scrub{height:auto}}
@media (prefers-reduced-motion: reduce){.sway{animation:none}.scrub{height:auto}.stage{position:relative;height:auto;min-height:900px;max-height:none}}
@keyframes kb{from{transform:scale(1.04)}to{transform:scale(1)}}
.scrollcue{animation:cue 2.2s ease-in-out infinite}
@keyframes cue{0%{transform:scaleY(0);transform-origin:top}45%{transform:scaleY(1);transform-origin:top}55%{transform:scaleY(1);transform-origin:bottom}100%{transform:scaleY(0);transform-origin:bottom}}
.pulse{animation:pulse 2.4s ease-out infinite}
@keyframes pulse{0%{box-shadow:0 0 0 0 rgba(157,196,26,.55)}100%{box-shadow:0 0 0 12px rgba(157,196,26,0)}}
.nl{transition:background .3s,color .3s}.nl:hover{background:rgba(255,255,255,.08);color:#fff!important}
.btn{transition:transform .35s cubic-bezier(.2,.75,.15,1),box-shadow .35s}.btn:hover{transform:translateY(-2px);box-shadow:0 14px 34px -12px rgba(157,196,26,.75)}/* Menu : une seule pastille grise qui glisse sous le lien survolé et revient sur « Me contacter » */
.mnav>a{position:relative;z-index:1}.mnav .nl:hover{background:transparent}
.npill{position:absolute;left:0;top:0;width:0;height:0;border-radius:999px;background:rgba(255,255,255,.08);pointer-events:none;transition:transform .5s cubic-bezier(.2,.75,.15,1),width .5s cubic-bezier(.2,.75,.15,1),height .5s cubic-bezier(.2,.75,.15,1)}
@media (prefers-reduced-motion: reduce){.npill{transition:none}}
/* Mobile et tablette : bouton rond (même verre que la pastille de la M) qui déplie le menu en panneau */
.mbtn{display:none}
@media (max-width: 899px){
.hdrin{flex-wrap:nowrap!important}
.mbtn{position:relative;display:inline-flex;align-items:center;justify-content:center;flex:none;width:58px;height:58px;padding:0;border-radius:50%;border:1px solid rgba(255,255,255,0.1);background:rgba(11,12,11,0.74);-webkit-backdrop-filter:blur(18px) saturate(150%);backdrop-filter:blur(18px) saturate(150%);box-shadow:0 10px 30px -14px rgba(0,0,0,0.5);color:#eef0ea;cursor:pointer}
.mbtn span{position:absolute;left:50%;top:50%;width:22px;height:1.5px;margin:-.75px 0 0 -11px;border-radius:2px;background:currentColor;transition:transform .4s cubic-bezier(.2,.75,.15,1)}
.mbtn span:first-child{transform:translateY(-4px)}.mbtn span:last-child{transform:translateY(4px)}
.hdr.is-open .mbtn span:first-child{transform:rotate(45deg)}.hdr.is-open .mbtn span:last-child{transform:rotate(-45deg)}
.mnav{position:absolute!important;left:16px;right:16px;top:calc(100% + 2px);flex-direction:column;flex-wrap:nowrap!important;align-items:stretch!important;gap:2px!important;padding:12px!important;border-radius:28px!important;background:rgba(11,12,11,0.9)!important;opacity:0;visibility:hidden;translate:0 -12px;transition:opacity .3s ease,translate .45s cubic-bezier(.2,.75,.15,1),visibility 0s linear .45s}
.hdr.is-open .mnav{opacity:1;visibility:visible;translate:none;transition:opacity .3s ease,translate .45s cubic-bezier(.2,.75,.15,1)}
.mnav>a{min-height:56px!important;padding:0 22px!important;font-size:19px!important;justify-content:space-between}
.mnav>a.nl:active{background:rgba(255,255,255,.08)}
.mnav .ncta{margin-top:6px;background:rgba(255,255,255,.08)}
.npill{display:none}
}
.arw svg{transition:transform .35s cubic-bezier(.2,.75,.15,1)}.arw:hover svg{transform:translate(4px,-4px)}.arw.arwd:hover svg{transform:translateY(4px)}
.lift2{transition:transform .6s cubic-bezier(.2,.75,.15,1)}.lift2:hover{transform:translateY(-6px)}
.lift{transition:transform .6s cubic-bezier(.2,.75,.15,1),border-color .5s}.lift:hover{transform:translateY(-6px);border-color:rgba(157,196,26,.5)!important}
.zoom{overflow:hidden;overflow:clip}.zoom img{transition:transform 1.4s cubic-bezier(.2,.75,.15,1)}.zoom:hover img{transform:scale(1.05)}
/* Autres projets : bouton « Voir le site » au survol de la capture */
.zoom img{transition:transform 1.4s cubic-bezier(.2,.75,.15,1),filter .45s ease}.zoom:has(.vsite):hover img{filter:brightness(.62)}
.vsite{position:absolute;left:50%;top:50%;translate:-50% -50%;display:inline-flex;align-items:center;gap:8px;min-height:46px;padding:0 20px;border-radius:999px;border:1px solid rgba(255,255,255,.18);background:rgba(11,12,11,.6);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);color:#fff!important;font-size:15px;font-weight:500;text-decoration:none;white-space:nowrap;opacity:0;scale:.9;transition:opacity .35s ease,scale .5s cubic-bezier(.2,.75,.15,1)}
.zoom:hover .vsite,.vsite:focus-visible{opacity:1;scale:1}
@media (hover: none){.vsite{opacity:1;scale:1;left:auto;top:auto;right:12px;bottom:12px;translate:none;min-height:40px;padding:0 14px;font-size:14px}}
.bento{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));grid-template-areas:"conc conc integ" "wp cms integ" "perf maint maint";gap:16px}
.bento>*{position:relative}
@media (max-width:920px){.bento{grid-template-columns:minmax(0,1fr);grid-template-areas:"conc" "integ" "wp" "cms" "perf" "maint"}}
.sel{opacity:0;transform:scale(.97);transition:opacity .35s,transform .45s cubic-bezier(.2,.75,.15,1)}
.cg:hover .sel{opacity:1;transform:none}
.tl{display:block;animation:type 9s steps(28,end) infinite both}
@keyframes type{0%{clip-path:inset(0 100% 0 0)}14%,86%{clip-path:inset(0 0 0 0)}100%{clip-path:inset(0 0 0 0);opacity:0}}
.caret{animation:blink 1s steps(2,start) infinite}
@keyframes blink{to{visibility:hidden}}
.resp{animation:resp 7s cubic-bezier(.6,0,.2,1) infinite}
@keyframes resp{0%,18%{width:100%}40%,58%{width:56%}78%,92%{width:30%}100%{width:100%}}
.spin{animation:spin 26s linear infinite}
@keyframes spin{to{transform:rotate(360deg)}}
.tg{animation:tgbg 4s ease-in-out infinite}
@keyframes tgbg{0%,40%{background:rgba(255,255,255,.18)}50%,90%{background:#9dc41a}100%{background:rgba(255,255,255,.18)}}
.tgk{animation:tgk 4s ease-in-out infinite}
@keyframes tgk{0%,40%{transform:none}50%,90%{transform:translateX(14px)}100%{transform:none}}
.log{animation:log 16s linear infinite}
@keyframes log{to{transform:translateY(-50%)}}
.words .w{position:absolute;left:0;right:0;top:0;text-align:center;white-space:nowrap;opacity:0;animation:word 18.2s cubic-bezier(.2,.75,.15,1) infinite both}
@keyframes word{0%{opacity:0;transform:translateY(55%);filter:blur(12px)}3.5%{opacity:1;transform:none;filter:blur(0)}12.8%{opacity:1;transform:none;filter:blur(0)}15.8%{opacity:0;transform:translateY(-45%);filter:blur(12px)}100%{opacity:0}}
.wbar{transform-origin:left;animation:wbar 2.6s linear infinite}
@keyframes wbar{from{transform:scaleX(0)}to{transform:scaleX(1)}}
/* La « M » se trace d'un seul trait, une plume verte en tête, puis le point apparaît ; boucle de 15 s */
/* La M ne se dessine qu'une fois la moitié de la section entrée à l'écran (classe .go posée en JS) */
.mline{stroke-dasharray:1;stroke-dashoffset:1}.drawm.go .mline{animation:mline 15s infinite}
@keyframes mline{0%{stroke-dashoffset:1;opacity:1;animation-timing-function:cubic-bezier(.65,0,.35,1)}44%{stroke-dashoffset:0;opacity:1}84%{stroke-dashoffset:0;opacity:1}94%{stroke-dashoffset:0;opacity:0}100%{stroke-dashoffset:1;opacity:0}}
.mpen{stroke-dasharray:.018 1;stroke-dashoffset:.018;filter:drop-shadow(0 0 3px rgba(157,196,26,.9));opacity:0}.drawm.go .mpen{animation:mpen 15s infinite}
@keyframes mpen{0%{stroke-dashoffset:.018;opacity:1;animation-timing-function:cubic-bezier(.65,0,.35,1)}44%{stroke-dashoffset:-.982;opacity:1}48%{stroke-dashoffset:-.982;opacity:0}100%{stroke-dashoffset:-.982;opacity:0}}
.mdot{transform-box:fill-box;transform-origin:center;transform:scale(0)}.drawm.go .mdot{animation:mdot 15s infinite}
@keyframes mdot{0%,44%{transform:scale(0);opacity:1}50%{transform:scale(1.18)}54%{transform:scale(1)}84%{transform:scale(1);opacity:1}94%{transform:scale(1);opacity:0}100%{transform:scale(0);opacity:0}}
.sl{overflow-x:auto;scroll-snap-type:x mandatory;padding:10px max(28px, calc((100% - 1320px) / 2 + 28px)) 22px;scroll-padding-inline:max(28px, calc((100% - 1320px) / 2 + 28px));scrollbar-width:none}
.sl::-webkit-scrollbar{display:none}
.sltrack{display:flex;gap:22px;width:max-content}
.sltrack>article{flex:0 0 clamp(280px, 28vw, 400px);scroll-snap-align:start}
@media (prefers-reduced-motion: reduce){.mq,.ln>span,.fade,.fade2,.kb,.scrollcue,.pulse,.tl,.caret,.resp,.spin,.tg,.tgk,.log,.wbar,.mline,.mpen,.mdot{animation:none}.mline{stroke-dashoffset:0}.mpen{opacity:0}.words .w{animation:none}.words .w:first-child{opacity:1}.drawm.go .mline,.drawm.go .mpen,.drawm.go .mdot{animation:none}.drawm .mline{stroke-dashoffset:0}.drawm .mdot{transform:none}}
/* En-tête sans fond : au scroll, le nom s'efface et la M seule prend une pastille du même style que le menu */
.hcirc{position:absolute;left:-9px;top:50%;width:58px;height:58px;margin-top:-29px;box-sizing:border-box;border-radius:50%;border:1px solid rgba(255,255,255,0.1);background:rgba(11,12,11,0.74);-webkit-backdrop-filter:blur(18px) saturate(150%);backdrop-filter:blur(18px) saturate(150%);box-shadow:0 10px 30px -14px rgba(0,0,0,0.5);opacity:0;scale:.6;transition:opacity .3s ease,scale .3s ease}
/* Animation de l'en-tête jouée d'un coup (classe .is-s posée au premier scroll, retirée en revenant en haut) */
.hlogo{height:46px;transition:height .6s cubic-bezier(.2,.75,.15,1)}
.hword{transition:opacity .45s ease .2s}
.hdrin{transition:padding .6s cubic-bezier(.2,.75,.15,1)}
.hdr.is-s .hlogo{height:24px}
.hdr.is-s .hword{opacity:0;transition-delay:0s}
.hdr.is-s .hcirc{opacity:1;scale:1;transition:opacity .45s ease .22s,scale .6s cubic-bezier(.2,.75,.15,1) .22s}
.hdr.is-s .hdrin{padding-top:10px!important;padding-bottom:10px!important}
@media (prefers-reduced-motion: reduce){.hlogo,.hword,.hcirc,.hdrin{transition:none!important}}
@supports (animation-timeline: view()){
@media (prefers-reduced-motion: no-preference){
.rv{animation:rv linear both;animation-timeline:view();animation-range:entry 0% cover 26%}
@keyframes rv{from{opacity:0;translate:0 56px}to{opacity:1;translate:0 0}}
.hd>p:first-child{animation:lin linear both;animation-timeline:view();animation-range:entry 0% cover 22%}
@keyframes lin{from{opacity:0}to{opacity:1}}
.hd>h2{animation:tin linear both,tout linear forwards;animation-timeline:view(),view();animation-range:entry 5% cover 30%,exit 15% exit 95%}
@keyframes tin{from{opacity:0;translate:0 80px;clip-path:inset(0 0 100% 0)}to{opacity:1;translate:0 0;clip-path:inset(-10% -10% -25% -10%)}}
@keyframes tout{from{opacity:1;translate:0 0}to{opacity:0;translate:0 -60px}}
.hd>h2~p{animation:pin linear both,tout linear forwards;animation-timeline:view(),view();animation-range:entry 25% cover 45%,exit 15% exit 95%}
@keyframes pin{from{opacity:0;translate:0 44px}to{opacity:1;translate:0 0}}
.hd>h3{animation:tin linear both;animation-timeline:view();animation-range:entry 5% cover 30%}
.hd>:where(h3~*,p:first-child~p){animation:pin linear both;animation-timeline:view();animation-range:entry 25% cover 45%}
.pop{animation:pop linear both;animation-timeline:view();animation-range:entry 0% exit 100%}
@keyframes pop{0%{opacity:0;scale:.9;translate:0 70px}30%{opacity:1;scale:1.025;translate:0 0}55%{scale:1}100%{opacity:.7;scale:.94}}
.clip{animation:clip linear both;animation-timeline:view();animation-range:entry 0% exit 100%}
@keyframes clip{0%{clip-path:inset(12% 10% 12% 10% round 24px);scale:.94}35%{clip-path:inset(0 0 0 0 round 24px);scale:1.025}60%{clip-path:inset(0 0 0 0 round 24px);scale:1}100%{clip-path:inset(0 0 0 0 round 24px);scale:.95}}
.para{animation:para linear both;animation-timeline:scroll(root);animation-range:0 1000px}
@keyframes para{to{transform:translateY(220px)}}
.draw path{stroke-dasharray:1;stroke-dashoffset:1;animation:draw linear both;animation-timeline:view();animation-range:entry 0% cover 55%}
@keyframes draw{to{stroke-dashoffset:0}}
.drawx{animation:drawx linear both;animation-timeline:view();animation-range:entry 20% cover 50%}
@keyframes drawx{from{transform:scaleX(0)}to{transform:scaleX(1)}}
.bar{animation:bar linear both;animation-timeline:view();animation-range:entry 10% cover 45%}
@keyframes bar{from{transform:scaleX(0);transform-origin:left}to{transform:scaleX(1);transform-origin:left}}
.slide{animation:slide linear both;animation-timeline:view();animation-range:entry 0% exit 100%}
@keyframes slide{from{transform:translateX(3%)}to{transform:translateX(-5%)}}
}}

/* ── Ajouts du site statique ── */
.halo{background:radial-gradient(520px circle at var(--hx,-2000px) var(--hy,-2000px), rgba(157,196,26,0.22), rgba(157,196,26,0) 62%)}
.fstat{min-height:1.4em;margin:0;font-size:15px}
.fstat.ok{color:#cfe98a}.fstat.err{color:#f2b544}
.hp{position:absolute!important;left:-10000px!important;width:1px;height:1px;overflow:hidden}
/* Carrousel : glissé à la souris et au doigt par le script (touch-action pan-y = le navigateur garde le défilement vertical) */
.sl{touch-action:pan-y;cursor:grab;-webkit-user-select:none;user-select:none}
.sl.drag{cursor:grabbing;scroll-snap-type:none!important}
.sl img{-webkit-user-drag:none;user-drag:none;pointer-events:none}
form button[disabled]{opacity:.55;cursor:wait}
.legal{max-width:780px;margin:0 auto;padding:150px 28px 110px;color:#c8ccc4;font-size:17px;line-height:1.7}
.legal h1{margin:0 0 34px;font-family:'Oswald',sans-serif;font-weight:500;font-size:clamp(38px,6vw,64px);line-height:1.08;text-transform:uppercase;color:#eef0ea}
.legal h2{display:none}
.legal h3{margin:42px 0 12px;font-family:'Oswald',sans-serif;font-weight:500;font-size:24px;line-height:1.2;text-transform:uppercase;color:#eef0ea}
.legal a{color:#cfe98a}.legal strong{color:#eef0ea;font-weight:500}
.legal mark{font-size:15px}
