/* ==========================================================================
   LaunchFolio — feuille de style unique
   Concatenation de fonts.css + lenis.css + style.css, dans l'ordre des
   balises <link> d'origine. Les trois sources sont conservees dans
   _avant-concatenation/ pour reference.
   ========================================================================== */

/* ===== fonts.css ===== */
/* ==========================================================================
   LaunchFolio — polices auto-hebergees
   Copie conforme : les 92 regles @font-face sont reprises telles quelles du
   HTML source (launchfolio.framer.website/index.html), seule l'URL change.
   88 fichiers .woff2 ont ete telecharges dans ../fonts/ :
     - 85 depuis framerusercontent.com/assets/  (Inter, Inter Display, Switzer, Manrope)
     -  3 depuis fonts.gstatic.com              (Fragment Mono)
   Les 4 familles « Placeholder » sont des metriques de repli sur Arial,
   utilisees par Framer dans les piles font-family pour eviter le FOUT.
   Aucune requete reseau : tout est local.
   ========================================================================== */
@font-face {
  font-family: 'Fragment Mono';
  font-style: normal;
  font-weight: 400;
  src: url('../fonts/fragment-mono-4iCr6K5wfMRRjxp0DA6-2CLnB45HhrUI.woff2') format('woff2');
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
  font-display: swap;
}

@font-face {
  font-family: 'Fragment Mono';
  font-style: normal;
  font-weight: 400;
  src: url('../fonts/fragment-mono-4iCr6K5wfMRRjxp0DA6-2CLnB41HhrUI.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
  font-display: swap;
}

@font-face {
  font-family: 'Fragment Mono';
  font-style: normal;
  font-weight: 400;
  src: url('../fonts/fragment-mono-4iCr6K5wfMRRjxp0DA6-2CLnB4NHhg.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
  font-display: swap;
}

@font-face { font-family: "Inter"; src: url('../fonts/5vvr9Vy74if2I6bQbJvbw7SY1pQ.woff2'); font-display: swap; font-style: normal; font-weight: 400; unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F }

@font-face { font-family: "Inter"; src: url('../fonts/EOr0mi4hNtlgWNn9if640EZzXCo.woff2'); font-display: swap; font-style: normal; font-weight: 400; unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116 }

@font-face { font-family: "Inter"; src: url('../fonts/Y9k9QrlZAqio88Klkmbd8VoMQc.woff2'); font-display: swap; font-style: normal; font-weight: 400; unicode-range: U+1F00-1FFF }

@font-face { font-family: "Inter"; src: url('../fonts/OYrD2tBIBPvoJXiIHnLoOXnY9M.woff2'); font-display: swap; font-style: normal; font-weight: 400; unicode-range: U+0370-03FF }

@font-face { font-family: "Inter"; src: url('../fonts/JeYwfuaPfZHQhEG8U5gtPDZ7WQ.woff2'); font-display: swap; font-style: normal; font-weight: 400; unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF }

@font-face { font-family: "Inter"; src: url('../fonts/GrgcKwrN6d3Uz8EwcLHZxwEfC4.woff2'); font-display: swap; font-style: normal; font-weight: 400; unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2070, U+2074-207E, U+2080-208E, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD }

@font-face { font-family: "Inter"; src: url('../fonts/b6Y37FthZeALduNqHicBT6FutY.woff2'); font-display: swap; font-style: normal; font-weight: 400; unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB }

@font-face { font-family: "Inter"; src: url('../fonts/DpPBYI0sL4fYLgAkX8KXOPVt7c.woff2'); font-display: swap; font-style: normal; font-weight: 700; unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F }

@font-face { font-family: "Inter"; src: url('../fonts/4RAEQdEOrcnDkhHiiCbJOw92Lk.woff2'); font-display: swap; font-style: normal; font-weight: 700; unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116 }

@font-face { font-family: "Inter"; src: url('../fonts/1K3W8DizY3v4emK8Mb08YHxTbs.woff2'); font-display: swap; font-style: normal; font-weight: 700; unicode-range: U+1F00-1FFF }

@font-face { font-family: "Inter"; src: url('../fonts/tUSCtfYVM1I1IchuyCwz9gDdQ.woff2'); font-display: swap; font-style: normal; font-weight: 700; unicode-range: U+0370-03FF }

@font-face { font-family: "Inter"; src: url('../fonts/VgYFWiwsAC5OYxAycRXXvhze58.woff2'); font-display: swap; font-style: normal; font-weight: 700; unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF }

@font-face { font-family: "Inter"; src: url('../fonts/syRNPWzAMIrcJ3wIlPIP43KjQs.woff2'); font-display: swap; font-style: normal; font-weight: 700; unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2070, U+2074-207E, U+2080-208E, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD }

@font-face { font-family: "Inter"; src: url('../fonts/GIryZETIX4IFypco5pYZONKhJIo.woff2'); font-display: swap; font-style: normal; font-weight: 700; unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB }

@font-face { font-family: "Inter"; src: url('../fonts/H89BbHkbHDzlxZzxi8uPzTsp90.woff2'); font-display: swap; font-style: italic; font-weight: 700; unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F }

@font-face { font-family: "Inter"; src: url('../fonts/u6gJwDuwB143kpNK1T1MDKDWkMc.woff2'); font-display: swap; font-style: italic; font-weight: 700; unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116 }

@font-face { font-family: "Inter"; src: url('../fonts/43sJ6MfOPh1LCJt46OvyDuSbA6o.woff2'); font-display: swap; font-style: italic; font-weight: 700; unicode-range: U+1F00-1FFF }

@font-face { font-family: "Inter"; src: url('../fonts/wccHG0r4gBDAIRhfHiOlq6oEkqw.woff2'); font-display: swap; font-style: italic; font-weight: 700; unicode-range: U+0370-03FF }

@font-face { font-family: "Inter"; src: url('../fonts/WZ367JPwf9bRW6LdTHN8rXgSjw.woff2'); font-display: swap; font-style: italic; font-weight: 700; unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF }

@font-face { font-family: "Inter"; src: url('../fonts/ia3uin3hQWqDrVloC1zEtYHWw.woff2'); font-display: swap; font-style: italic; font-weight: 700; unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2070, U+2074-207E, U+2080-208E, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD }

@font-face { font-family: "Inter"; src: url('../fonts/2A4Xx7CngadFGlVV4xrO06OBHY.woff2'); font-display: swap; font-style: italic; font-weight: 700; unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB }

@font-face { font-family: "Inter"; src: url('../fonts/CfMzU8w2e7tHgF4T4rATMPuWosA.woff2'); font-display: swap; font-style: italic; font-weight: 400; unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F }

@font-face { font-family: "Inter"; src: url('../fonts/867QObYax8ANsfX4TGEVU9YiCM.woff2'); font-display: swap; font-style: italic; font-weight: 400; unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116 }

@font-face { font-family: "Inter"; src: url('../fonts/Oyn2ZbENFdnW7mt2Lzjk1h9Zb9k.woff2'); font-display: swap; font-style: italic; font-weight: 400; unicode-range: U+1F00-1FFF }

@font-face { font-family: "Inter"; src: url('../fonts/cdAe8hgZ1cMyLu9g005pAW3xMo.woff2'); font-display: swap; font-style: italic; font-weight: 400; unicode-range: U+0370-03FF }

@font-face { font-family: "Inter"; src: url('../fonts/DOfvtmE1UplCq161m6Hj8CSQYg.woff2'); font-display: swap; font-style: italic; font-weight: 400; unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF }

@font-face { font-family: "Inter"; src: url('../fonts/pKRFNWFoZl77qYCAIp84lN1h944.woff2'); font-display: swap; font-style: italic; font-weight: 400; unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2070, U+2074-207E, U+2080-208E, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD }

@font-face { font-family: "Inter"; src: url('../fonts/tKtBcDnBMevsEEJKdNGhhkLzYo.woff2'); font-display: swap; font-style: italic; font-weight: 400; unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB }

@font-face { font-family: "Inter"; src: url('../fonts/mkY5Sgyq51ik0AMrSBwhm9DJg.woff2'); font-style: normal; font-weight: 900; unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F   font-display: swap;
}

@font-face { font-family: "Inter"; src: url('../fonts/X5hj6qzcHUYv7h1390c8Rhm6550.woff2'); font-style: normal; font-weight: 900; unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116   font-display: swap;
}

@font-face { font-family: "Inter"; src: url('../fonts/gQhNpS3tN86g8RcVKYUUaKt2oMQ.woff2'); font-style: normal; font-weight: 900; unicode-range: U+1F00-1FFF   font-display: swap;
}

@font-face { font-family: "Inter"; src: url('../fonts/cugnVhSraaRyANCaUtI5FV17wk.woff2'); font-style: normal; font-weight: 900; unicode-range: U+0370-03FF   font-display: swap;
}

@font-face { font-family: "Inter"; src: url('../fonts/5HcVoGak8k5agFJSaKa4floXVu0.woff2'); font-style: normal; font-weight: 900; unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF   font-display: swap;
}

@font-face { font-family: "Inter"; src: url('../fonts/jn4BtSPLlS0NDp1KiFAtFKiiY0o.woff2'); font-style: normal; font-weight: 900; unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD   font-display: swap;
}

@font-face { font-family: "Inter"; src: url('../fonts/P2Bw01CtL0b9wqygO0sSVogWbo.woff2'); font-style: normal; font-weight: 900; unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB   font-display: swap;
}

@font-face { font-family: "Inter"; src: url('../fonts/05KsVHGDmqXSBXM4yRZ65P8i0s.woff2'); font-style: italic; font-weight: 900; unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F   font-display: swap;
}

@font-face { font-family: "Inter"; src: url('../fonts/ky8ovPukK4dJ1Pxq74qGhOqCYI.woff2'); font-style: italic; font-weight: 900; unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116   font-display: swap;
}

@font-face { font-family: "Inter"; src: url('../fonts/vvNSqIj42qeQ2bvCRBIWKHscrc.woff2'); font-style: italic; font-weight: 900; unicode-range: U+1F00-1FFF   font-display: swap;
}

@font-face { font-family: "Inter"; src: url('../fonts/3ZmXbBKToJifDV9gwcifVd1tEY.woff2'); font-style: italic; font-weight: 900; unicode-range: U+0370-03FF   font-display: swap;
}

@font-face { font-family: "Inter"; src: url('../fonts/FNfhX3dt4ChuLJq2PwdlxHO7PU.woff2'); font-style: italic; font-weight: 900; unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF   font-display: swap;
}

@font-face { font-family: "Inter"; src: url('../fonts/g0c8vEViiXNlKAgI4Ymmk3Ig.woff2'); font-style: italic; font-weight: 900; unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD   font-display: swap;
}

@font-face { font-family: "Inter"; src: url('../fonts/efTfQcBJ53kM2pB1hezSZ3RDUFs.woff2'); font-style: italic; font-weight: 900; unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB   font-display: swap;
}

@font-face { font-family: "Inter"; src: url('../fonts/QxmhnWTzLtyjIiZcfaLIJ8EFBXU.woff2'); font-display: swap; font-style: italic; font-weight: 700; unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD }

@font-face { font-family: "Inter Display"; src: url('../fonts/gazZKZuUEtvr9ULhdA4SprP0AZ0.woff2'); font-display: swap; font-style: normal; font-weight: 600; unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F }

@font-face { font-family: "Inter Display"; src: url('../fonts/pe8RoujoPxuTZhqoNzYqHX2MXA.woff2'); font-display: swap; font-style: normal; font-weight: 600; unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116 }

@font-face { font-family: "Inter Display"; src: url('../fonts/teGhWnhH3bCqefKGsIsqFy3hK8.woff2'); font-display: swap; font-style: normal; font-weight: 600; unicode-range: U+1F00-1FFF }

@font-face { font-family: "Inter Display"; src: url('../fonts/qQHxgTnEk6Czu1yW4xS82HQWFOk.woff2'); font-display: swap; font-style: normal; font-weight: 600; unicode-range: U+0370-03FF }

@font-face { font-family: "Inter Display"; src: url('../fonts/MJ3N6lfN4iP5Um8rJGqLYl03tE.woff2'); font-display: swap; font-style: normal; font-weight: 600; unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF }

@font-face { font-family: "Inter Display"; src: url('../fonts/dHHUz45rhM2KCQpj9zttNVlibk.woff2'); font-display: swap; font-style: normal; font-weight: 600; unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD }

@font-face { font-family: "Inter Display"; src: url('../fonts/0SEEmmWc3vovhaai4RlRQSWRrz0.woff2'); font-display: swap; font-style: normal; font-weight: 600; unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB }

@font-face { font-family: "Inter Display"; src: url('../fonts/I11LrmuBDQZweplJ62KkVsklU5Y.woff2'); font-display: swap; font-style: normal; font-weight: 700; unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F }

@font-face { font-family: "Inter Display"; src: url('../fonts/UjFZPDy3qGuDktQM4q9CxhKfIa8.woff2'); font-display: swap; font-style: normal; font-weight: 700; unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116 }

@font-face { font-family: "Inter Display"; src: url('../fonts/8exwVHJy2DhJ4N5prYlVMrEKmQ.woff2'); font-display: swap; font-style: normal; font-weight: 700; unicode-range: U+1F00-1FFF }

@font-face { font-family: "Inter Display"; src: url('../fonts/UTeedEK21hO5jDxEUldzdScUqpg.woff2'); font-display: swap; font-style: normal; font-weight: 700; unicode-range: U+0370-03FF }

@font-face { font-family: "Inter Display"; src: url('../fonts/Ig8B8nzy11hzIWEIYnkg91sofjo.woff2'); font-display: swap; font-style: normal; font-weight: 700; unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF }

@font-face { font-family: "Inter Display"; src: url('../fonts/n9CXKI3tsmCPeC6MCT9NziShSuQ.woff2'); font-display: swap; font-style: normal; font-weight: 700; unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD }

@font-face { font-family: "Inter Display"; src: url('../fonts/qctQFoJqJ9aIbRSIp0AhCQpFxn8.woff2'); font-display: swap; font-style: normal; font-weight: 700; unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB }

@font-face { font-family: "Inter Display"; src: url('../fonts/fXvVh2JeZlehNcEhKHpHH0frSl0.woff2'); font-display: swap; font-style: italic; font-weight: 700; unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F }

@font-face { font-family: "Inter Display"; src: url('../fonts/7pScaNeb6M7n2HF2jKemDqzCIr4.woff2'); font-display: swap; font-style: italic; font-weight: 700; unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116 }

@font-face { font-family: "Inter Display"; src: url('../fonts/qS4UjQYyATcVV9rODk0Zx9KhkY8.woff2'); font-display: swap; font-style: italic; font-weight: 700; unicode-range: U+1F00-1FFF }

@font-face { font-family: "Inter Display"; src: url('../fonts/VfD2n20yM7v0hrUEBHEyafsmMBY.woff2'); font-display: swap; font-style: italic; font-weight: 700; unicode-range: U+0370-03FF }

@font-face { font-family: "Inter Display"; src: url('../fonts/4oIO9fB59bn3cKFWz7piCj28z9s.woff2'); font-display: swap; font-style: italic; font-weight: 700; unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF }

@font-face { font-family: "Inter Display"; src: url('../fonts/TBccIZR9kIpkRce5i9ATfPp7a4.woff2'); font-display: swap; font-style: italic; font-weight: 700; unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD }

@font-face { font-family: "Inter Display"; src: url('../fonts/F5Lmfd3fCAu7TwiYbI4DLWw4ks.woff2'); font-display: swap; font-style: italic; font-weight: 700; unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB }

@font-face { font-family: "Inter Display"; src: url('../fonts/KKQXBq6BF0xEmVbrTnWxh3Yw.woff2'); font-display: swap; font-style: italic; font-weight: 600; unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F }

@font-face { font-family: "Inter Display"; src: url('../fonts/ugFYeIKZEJXDxI6Kh0YQUX6EK9I.woff2'); font-display: swap; font-style: italic; font-weight: 600; unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116 }

@font-face { font-family: "Inter Display"; src: url('../fonts/DgDysCLt0HkIpWMu2uN5tivgQ.woff2'); font-display: swap; font-style: italic; font-weight: 600; unicode-range: U+1F00-1FFF }

@font-face { font-family: "Inter Display"; src: url('../fonts/2yEyxokC6poTg3Uc5E2ogfVi6WE.woff2'); font-display: swap; font-style: italic; font-weight: 600; unicode-range: U+0370-03FF }

@font-face { font-family: "Inter Display"; src: url('../fonts/wmHLUqO4QxLaADUScavxvknnBQ.woff2'); font-display: swap; font-style: italic; font-weight: 600; unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF }

@font-face { font-family: "Inter Display"; src: url('../fonts/08bCJfmvizuFi36VeazboYh54CQ.woff2'); font-display: swap; font-style: italic; font-weight: 600; unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD }

@font-face { font-family: "Inter Display"; src: url('../fonts/DuYGXsyDXsSnxlZ8qzmcA4x3JiI.woff2'); font-display: swap; font-style: italic; font-weight: 600; unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB }

@font-face { font-family: "Inter Display"; src: url('../fonts/PfdOpgzFf7N2Uye9JX7xRKYTgSc.woff2'); font-display: swap; font-style: normal; font-weight: 600; unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2070, U+2074-207E, U+2080-208E, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD }

@font-face { font-family: "Inter"; src: url('../fonts/DXD0Q7LSl7HEvDzucnyLnGBHM.woff2'); font-display: swap; font-style: normal; font-weight: 700; unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD }

@font-face { font-family: "Inter"; src: url('../fonts/vFzuJY0c65av44uhEKB6vyjFMg.woff2'); font-display: swap; font-style: italic; font-weight: 400; unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD }

@font-face { font-family: "Switzer"; src: url('../fonts/YS3VPNVO4B3TOJMEXDGFZQ4TLZGGSRZC.woff2'); font-display: swap; font-style: normal; font-weight: 600 }

@font-face { font-family: "Switzer"; src: url('../fonts/TOHQHMLIEIPKVF2JPM6SVKXFYGO5G2TJ.woff2'); font-style: normal; font-weight: 900   font-display: swap;
}

@font-face { font-family: "Switzer"; src: url('../fonts/J7XVDQ2IJENINPUAVBDJNH3VEXVDSPQQ.woff2'); font-style: italic; font-weight: 900   font-display: swap;
}

@font-face { font-family: "Switzer"; src: url('../fonts/IOAFMDAPNX3S6OL2GYQQJ2FFJIRSVB3N.woff2'); font-display: swap; font-style: italic; font-weight: 600 }

@font-face { font-family: "Switzer"; src: url('../fonts/6XPIMU23OJVRY676OG5YVJMWEHWICATX.woff2'); font-display: swap; font-style: normal; font-weight: 500 }

@font-face { font-family: "Switzer"; src: url('../fonts/C6VLICDGQWYKV6YT65TM5G3OVN4AOZBN.woff2'); font-display: swap; font-style: italic; font-weight: 500 }

@font-face { font-family: "Switzer"; src: url('../fonts/Y7SCNZJOT2MW5ADSGOFLDGH4TNL4JCQY.woff2'); font-display: swap; font-style: normal; font-weight: 700 }

@font-face { font-family: "Switzer"; src: url('../fonts/A7AS3UPAGLC7MDVC67SBNTE5FGF2Z2RG.woff2'); font-display: swap; font-style: italic; font-weight: 700 }

@font-face { font-family: "Manrope"; src: url('../fonts/6P4FPMFQH7CCC7RZ4UU4NKSGJ2RLF7V5.woff2'); font-display: swap; font-style: normal; font-weight: 700 }

@font-face { font-family: "Manrope"; src: url('../fonts/K4ZMLVLHYIFVTTTWGVOTVGOFUUX7NVGI.woff2'); font-display: swap; font-style: normal; font-weight: 800 }

@font-face { font-family: "Manrope"; src: url('../fonts/JNU3GNMUBPWW6V6JTED3S27XL5HN7NM5.woff2'); font-display: swap; font-style: normal; font-weight: 600 }

@font-face { font-family: "Inter Placeholder"; src: local("Arial"); ascent-override: 89.79%; descent-override: 22.36%; line-gap-override: 0.00%; size-adjust: 107.89% }

@font-face { font-family: "Inter Display Placeholder"; src: local("Arial"); ascent-override: 98.56%; descent-override: 24.54%; line-gap-override: 0.00%; size-adjust: 98.29% }

@font-face { font-family: "Switzer Placeholder"; src: local("Arial"); ascent-override: 96.67%; descent-override: 24.66%; line-gap-override: 8.88%; size-adjust: 101.38% }

@font-face { font-family: "Manrope Placeholder"; src: local("Arial"); ascent-override: 102.74%; descent-override: 28.91%; line-gap-override: 0.00%; size-adjust: 103.76% }



/* ===== lenis.css ===== */
html.lenis,
html.lenis body {
  height: auto;
}

.lenis:not(.lenis-autoToggle).lenis-stopped {
  overflow: clip;
}

.lenis [data-lenis-prevent],
.lenis [data-lenis-prevent-wheel],
.lenis [data-lenis-prevent-touch],
.lenis [data-lenis-prevent-vertical],
.lenis [data-lenis-prevent-horizontal] {
  overscroll-behavior: contain;
}

.lenis.lenis-smooth iframe {
  pointer-events: none;
}

.lenis.lenis-autoToggle {
  transition-property: overflow;
  transition-duration: 1ms;
  transition-behavior: allow-discrete;
}


/* ===== style.css ===== */
/* ==========================================================================
   LaunchFolio — copie statique conforme
   Reconstruction sans Framer de launchfolio.framer.website/index.html

   Toutes les valeurs (tailles, graisses, interlettrages, interlignes,
   espacements, rayons, ombres, couleurs) ont ete relevees par mesure du
   rendu de l'original dans le navigateur, aux 4 points de rupture declares
   par Framer :
       >= 1200px  |  992-1199px  |  768-991px  |  <= 767px
   Le DOM d'origine dupliquait chaque bloc par point de rupture
   (92 blocs .ssr-variant) ; ici chaque bloc n'existe qu'une fois et le
   responsive est gere uniquement en CSS.

   Sommaire
   01. Reinitialisation
   02. Jetons
   03. Typographie
   04. Utilitaires de mise en page
   05. Boutons, liens, icones
   06. Barre de navigation (pilule)
   07. Widget de contact flottant
   08. Hero
   09. Bandeau logos (ticker)
   10. Latest Projects
   11. Big Quote
   12. Services
   13. About Me
   14. Pricing
   15. Testimonials
   16. FAQ
   17. Articles
   18. Pied de page
   19. Animations
   20. Preferences de mouvement / impression
   ========================================================================== */


/* ==========================================================================
   01. Reinitialisation
   ========================================================================== */

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body, h1, h2, h3, h4, p, figure, blockquote, dl, dd, ul, ol {
  margin: 0;
  padding: 0;
}

ul, ol { list-style: none; }

img, svg, video { display: block; max-width: 100%; }

/* Les visuels servis en WebP avec repli JPEG sont enveloppes dans <picture>.
   display: contents efface la boite du conteneur : l'<img> continue de se
   dimensionner sur son parent reel (.project__media, .article__media,
   .about__portrait), et les regles existantes s'appliquent sans changement. */
picture { display: contents; }

button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }

a { color: inherit; text-decoration: none; }

summary::-webkit-details-marker { display: none; }


/* ==========================================================================
   02. Jetons
   Les 11 variables de couleur reprennent exactement les tokens Framer.
   ========================================================================== */

:root {
  --black:     #000;
  --gray-900:  #2b2b2b;
  --gray-700:  #545454;
  --gray-500:  #828282;
  --gray-400:  #b8b8b8;
  --gray-300:  #dedede;
  --gray-200:  #f0f0f0;
  --gray-150:  #f7f7f7;
  --gray-100:  #fafafa;
  --white:     #fff;
  --green:     #12b33f;

  --font:         "Switzer", "Switzer Placeholder", sans-serif;
  --font-display: "Inter Display", "Inter Display Placeholder", sans-serif;
  --font-mono:    "Fragment Mono", ui-monospace, monospace;
  --font-manrope: "Manrope", "Manrope Placeholder", sans-serif;

  --container:  1080px;
  --gutter:     44px;
  --section-y:  128px;
  --hero-top:   144px;

  --ease:       cubic-bezier(0.4, 0, 0.2, 1);

  /* Filet de detourage des surfaces blanches. Le template posait ses pilules
     sur un fond blanc sans contour, en ne comptant que sur une ombre tres
     douce : la pastille de disponibilite, la barre de navigation et le widget
     de contact se fondaient dans la page. Ce jeton leur donne un bord net.
     0.14 : visible sur blanc, invisible sur le gris 100 des sections. */
  --hairline:   rgba(0, 0, 0, 0.14);

  /* Ombres relevees sur l'original */
  --shadow-pill:  0 0.602187px 0.602187px -1.25px rgba(0, 0, 0, 0.18),
                  0 2.28853px 2.28853px -2.5px rgba(0, 0, 0, 0.16),
                  0 10px 10px -3.75px rgba(0, 0, 0, 0.06);
  --shadow-soft:  0 0.597144px 0.597144px -0.9375px rgba(0, 0, 0, 0.07),
                  0 1.81088px 1.81088px -1.875px rgba(0, 0, 0, 0.07),
                  0 4.78699px 4.78699px -2.8125px rgba(0, 0, 0, 0.06),
                  0 15px 15px -3.75px rgba(0, 0, 0, 0.03);
  --shadow-cta:   inset 0 2px 4px 0 rgba(255, 255, 255, 0.4),
                  0 0.740973px 0.740973px -0.75px rgba(0, 0, 0, 0.33),
                  0 2.01787px 2.01787px -1.5px rgba(0, 0, 0, 0.32),
                  0 4.43051px 4.43051px -2.25px rgba(0, 0, 0, 0.3),
                  0 9.83471px 9.83471px -3px rgba(0, 0, 0, 0.25),
                  0 25px 25px -3.75px rgba(0, 0, 0, 0.11),
                  0 0 0 1px var(--gray-700);
}

@media (max-width: 767px) {
  :root {
    --gutter:    22px;
    --section-y: 48px;
    --hero-top:  128px;
  }
}

body {
  font-family: var(--font);
  font-size: 16px;
  font-weight: 500;
  line-height: 1.4;
  letter-spacing: -0.02em;
  color: var(--black);
  background: var(--white);
  -webkit-font-smoothing: antialiased;
  /* Aucun ancetre ne doit isoler le fond, sans quoi l'inversion du curseur
     (mix-blend-mode: difference) ne traverse plus les sections. */
  isolation: auto;
}


/* ==========================================================================
   03. Typographie
   Echelle relevee au pixel pres sur l'original, point de rupture par point
   de rupture. Les pourcentages d'interligne sont ceux des presets Framer.
   ========================================================================== */

.t-h1 {
  font-size: 72px;
  font-weight: 500;
  line-height: 95%;
  letter-spacing: -0.03em;
}

.t-h2 {
  font-size: 64px;
  font-weight: 500;
  line-height: 100%;
  letter-spacing: -0.03em;
}

.t-h3 {
  font-size: 32px;
  font-weight: 500;
  line-height: 115%;
  letter-spacing: -0.03em;
}

.t-h4 {
  font-size: 24px;
  font-weight: 500;
  line-height: 130%;
  letter-spacing: -0.03em;
}

.t-quote {
  font-size: 28px;
  font-weight: 500;
  line-height: 140%;
  letter-spacing: -0.03em;
}

.t-quote-lg {
  font-family: var(--font-display);
  font-size: 40px;
  font-weight: 600;
  line-height: 120%;
  letter-spacing: -0.03em;
}

.t-body {
  font-size: 18px;
  font-weight: 500;
  line-height: 140%;
  letter-spacing: -0.02em;
}

.t-sm {
  font-size: 16px;
  font-weight: 500;
  line-height: 150%;
  letter-spacing: -0.02em;
}

.t-xs {
  font-size: 14px;
  font-weight: 600;
  line-height: 112%;
  letter-spacing: -0.02em;
}

.t-2xs {
  font-size: 12px;
  font-weight: 600;
  line-height: 112%;
  letter-spacing: -0.02em;
}

@media (max-width: 1199px) {
  .t-h1 { font-size: 64px; }
  .t-h2 { font-size: 54px; }
  .t-h3 { font-size: 30px; }
  .t-quote { font-size: 26px; }
}

@media (max-width: 991px) {
  .t-h1 { font-size: 48px; }
  .t-h2 { font-size: 46px; }
  .t-h3 { font-size: 28px; }
  .t-h4 { font-size: 22px; }
  .t-quote { font-size: 24px; }
  .t-quote-lg { font-size: 38px; }
  .t-body { font-size: 16px; }
  .t-sm { font-size: 14px; }
}

@media (max-width: 767px) {
  .t-h1 { font-size: 42px; }
  .t-h2 { font-size: 36px; }
  .t-h3 { font-size: 26px; }
  .t-h4 { font-size: 20px; }
  .t-quote { font-size: 22px; }
  .t-quote-lg { font-size: 36px; }
}

.c-gray   { color: var(--gray-500); }
.c-gray-7 { color: var(--gray-700); }
strong    { font-weight: 600; }


/* ==========================================================================
   04. Utilitaires de mise en page
   ========================================================================== */

.section {
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 100%;
  padding-block: var(--section-y);
}

.section--gray { background: var(--gray-100); }

/* Premiere section d'une page secondaire (projets.html, blog.html). Elle
   remplace le hero : il lui faut le meme degagement en haut, sans quoi le
   titre passe sous la pilule de navigation, fixe a 24 px du haut sur 56 px
   de haut. --hero-top vaut 144 px, 128 px sous 768 px. */
.section--page { padding-top: var(--hero-top); }

/* En-tete de page secondaire : titre + sous-titre centres, comme l'en-tete
   de la section « Projets récents » dont il reprend le gabarit. */
.pagehead {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
  width: 100%;
  text-align: center;
}
.pagehead p { max-width: 56ch; }

/* Les cartes projet sont projetees dans le hero par une translation : entre
   992 et ~1330 px de large, la carte la plus a droite depassait le bord de la
   fenetre et ouvrait une barre de defilement horizontale (122 px mesures a
   1100 px). Le defaut existait avant le repositionnement du 16 aout ; il n'est
   visible ni a 1440 px (la marge du conteneur absorbe le debordement) ni sous
   992 px (la translation y est desactivee), d'ou son passage inapercu.
   « clip » plutot que « hidden » : il ne cree pas de conteneur de defilement,
   donc ni Lenis, ni la nav fixe, ni le panneau de contact ne sont affectes.
   L'axe vertical reste visible — les cartes doivent pouvoir remonter dans le
   hero, qui est plus haut dans le meme <main>. */
main { overflow-x: clip; }

.container {
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 100%;
  max-width: var(--container);
  padding-inline: var(--gutter);
}

.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}


/* ==========================================================================
   05. Boutons, liens, icones
   ========================================================================== */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  border-radius: 24px;
  transition: background-color 250ms var(--ease), color 250ms var(--ease),
              transform 250ms var(--ease), box-shadow 250ms var(--ease);
}

/* CTA principal noir du hero : avatar + libelle.
   Repos 180 x 44 ; survol 230 x 44. Le groupe passe de 28 a 78 px. */
.btn-cta {
  gap: 10px;
  height: 44px;
  padding: 8px 16px 8px 8px;
  background: var(--black);
  color: var(--white);
  border-radius: 24px;
  box-shadow: var(--shadow-cta);
  transition: background-color 250ms var(--ease), box-shadow 250ms var(--ease),
              transform 250ms var(--ease);
}

.btn-cta__faces {
  display: flex;
  align-items: center;
  gap: 4px;
  flex: none;
}

.btn-cta__avatar {
  flex: none;
  width: 28px; height: 28px;
  border-radius: 1000px;
  object-fit: cover;
}

/* Le « + » et le cercle « You » sont replies au repos : largeur nulle,
   invisibles, sans marge. Au survol ils reprennent leur place dans le flux. */
.btn-cta__plus,
.btn-cta__you {
  flex: none;
  height: 28px;
  width: 0;
  opacity: 0;
  overflow: hidden;
  transform: scale(0.6);
  transition: width 320ms var(--ease), opacity 260ms var(--ease),
              transform 320ms var(--ease), margin-left 320ms var(--ease);
}

/* Symbole « + » dessine en CSS */
.btn-cta__plus {
  position: relative;
  margin-left: -4px;
}
.btn-cta__plus::before,
.btn-cta__plus::after {
  content: '';
  position: absolute;
  top: 50%; left: 50%;
  background: var(--white);
  border-radius: 2px;
  transform: translate(-50%, -50%);
}
.btn-cta__plus::before { width: 10px; height: 2px; }
.btn-cta__plus::after  { width: 2px; height: 10px; }

/* Cercle blanc portant « You » */
.btn-cta__you {
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 1000px;
  background: var(--white);
  color: var(--black);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: -0.22px;
  white-space: nowrap;
  margin-left: -4px;
}

.btn-cta:hover .btn-cta__plus,
.btn-cta:focus-visible .btn-cta__plus {
  width: 14px;
  opacity: 1;
  transform: scale(1);
  margin-left: 0;
}
.btn-cta:hover .btn-cta__you,
.btn-cta:focus-visible .btn-cta__you {
  width: 28px;
  opacity: 1;
  transform: scale(1);
  margin-left: 0;
}

.btn-cta__label {
  font-size: 14px;
  font-weight: 600;
  letter-spacing: -0.28px;
  white-space: nowrap;
}

/* Bouton d'appel a l'action de la nav.
   Il etait clair (gris 100 sur pilule blanche) et se confondait avec les cinq
   liens voisins : il est desormais plein noir, c'est le seul element colore de
   la barre. Le nom de classe est conserve — .nav__links a:not(.btn-light)
   s'appuie dessus pour ne pas styler le bouton comme un lien.
   Attention : tout changement de padding ou de bordure ici modifie la largeur
   de .nav__right (voir section 06). */
.btn-light {
  height: 32px;
  padding: 8px 14px;
  background: var(--black);
  color: var(--white);
  border: 1px solid var(--black);
  border-radius: 24px;
  box-shadow: var(--shadow-soft);
  font-size: 14px;
  font-weight: 600;
  letter-spacing: -0.28px;
}
.btn-light:hover { background: var(--gray-900); border-color: var(--gray-900); }

/* --- Reponse au survol, commune a tous les appels a l'action -------------
   Le CTA du hero, celui de fin de page, celui de la section Investissement et
   les deux boutons du widget flottant n'avaient aucun retour visuel : seuls
   le « + » et la pastille « Vous » bougeaient sur .btn-cta, rien ailleurs.
   Meme geste partout : la surface se souleve de 2 px, l'ombre se creuse, le
   noir s'eclaircit d'un cran. A l'appui, le bouton redescend — c'est ce
   retour qui donne la sensation d'un vrai bouton.
   .btn-light, .cpanel__submit et .cpanel__alt-btn ont deja leur changement de
   fond : ils ne recoivent ici que le relief. */

.btn-cta,
.plan__cta-light,
.btn-light,
.cpanel__submit,
.cpanel__alt-btn,
.dock-btn,
.deck__toggle {
  will-change: transform;
}

.btn-cta:hover,
.plan__cta-light:hover,
.btn-light:hover,
.cpanel__submit:hover,
.cpanel__alt-btn:hover,
.dock-btn:hover,
.deck__toggle:hover {
  transform: translateY(-2px);
}

.btn-cta:active,
.plan__cta-light:active,
.btn-light:active,
.cpanel__submit:active,
.cpanel__alt-btn:active,
.dock-btn:active,
.deck__toggle:active {
  transform: translateY(0);
  transition-duration: 80ms;
}

/* Les boutons noirs pleins : l'ombre se creuse et le fond s'eclaircit. */
.btn-cta:hover,
.plan__cta-light:hover {
  background: var(--gray-900);
  box-shadow: var(--shadow-cta), 0 12px 24px -12px rgba(0, 0, 0, 0.45);
}

/* Le widget flottant est deja en mouvement au survol (il se deploie) : le
   relief y est porte par les deux boutons seuls, sans changement de fond. */
.dock-btn--dark:hover  { background: var(--gray-900); }
.dock-btn--light:hover { background: var(--white); }

/* Lien souligne « View all my projects » : le trait se deploie au survol */
.link-underline {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  position: relative;
}
.link-underline::after {
  content: '';
  position: absolute;
  left: 0; right: 0; bottom: -2px;
  height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: right center;
  transition: transform 350ms var(--ease);
}
.link-underline:hover::after { transform: scaleX(1); transform-origin: left center; }

.arrow { display: inline-block; transition: transform 250ms var(--ease); }
a:hover > .arrow,
.link-underline:hover .arrow { transform: translate(2px, -2px); }

.stars { display: flex; align-items: center; gap: 4px; }
/* flex: none — sans quoi les etoiles sont ecrasees dans un conteneur etroit */
.stars svg { flex: none; width: 12px; height: 12px; }

:where(a, button, summary):focus-visible {
  outline: 2px solid var(--black);
  outline-offset: 3px;
  border-radius: 4px;
}

/* Reserve d'icones : les 28 definitions SVG d'origine sont conservees dans
   un bloc masque en tete de <body> et appelees par <use href="#id">. */
.svg-defs {
  position: absolute;
  width: 0; height: 0;
  overflow: hidden;
}


/* ==========================================================================
   06. Barre de navigation (pilule)
   Etat replie par defaut : avatar + nom + trois points.
   Au survol (souris) ou au clic (tactile) la pilule s'elargit et les liens
   apparaissent en vague, 60 ms d'ecart entre chacun. Six elements depuis
   l'adaptation francaise : Realisations, Services, Processus, FAQ, Contact
   et le bouton « Reserver un appel ».
   ========================================================================== */

.nav-wrap {
  position: fixed;
  top: 24px;
  left: 0;
  width: 100%;
  display: flex;
  justify-content: center;
  z-index: 100;
  pointer-events: none;
}

/* Fond releve de 0.5 a 0.72 et filet de detourage : a 50 % d'opacite la
   pilule disparaissait des qu'une carte projet passait dessous. Le flou est
   conserve, le style general ne change pas. */
.nav {
  display: flex;
  align-items: center;
  gap: 32px;
  padding: 12px;
  background: rgba(255, 255, 255, 0.72);
  backdrop-filter: blur(5px);
  -webkit-backdrop-filter: blur(5px);
  border: 1px solid var(--hairline);
  border-radius: 32px;
  box-shadow: var(--shadow-pill);
  pointer-events: auto;
  transition: gap 400ms var(--ease);
}

.nav__id {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: none;
}
.nav__id img {
  width: 32px; height: 32px;
  border-radius: 1000px;
  object-fit: cover;
  /* l'avatar suit la vague : leger retrait au repli de la pilule */
  transition: transform 450ms var(--ease);
}
.nav__name {
  font-size: 14px;
  font-weight: 600;
  letter-spacing: -0.28px;
  white-space: nowrap;
}

/* Logo texte : l'avatar de l'ancien porteur de marque a disparu, le nom
   porte seul l'identite. Taille legerement relevee pour tenir ce role. */
.nav__logo {
  font-size: 16px;
  letter-spacing: -0.4px;
  padding-left: 4px;
}

/* Zone de droite : les liens cedent la place aux trois points.
   Etat par defaut = deploye. Au repli, la largeur tombe a 24 px et les
   points prennent le relais.
   La largeur deployee (492 px a l'origine 310 px) est celle des six
   elements francais — cinq liens plus le bouton — mesuree sur le rendu.
   Toute retouche des libelles impose de la reverifier : les liens sont en
   position absolue et deborderaient sur le logo. */
.nav__right {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  /* 492 px a l'origine ; +8 px depuis que le bouton d'appel est passe en
     noir plein (padding 12 -> 14 px et bordure de 1 px). */
  width: 500px;
  height: 32px;
  transition: width 450ms var(--ease);
}

.nav__dots {
  position: absolute;
  right: 0;
  display: flex;
  align-items: center;
  gap: 3px;
  padding: 4px 0;
  opacity: 0;
  pointer-events: none;
  transition: opacity 250ms var(--ease);
}
.nav__dots span {
  width: 6px; height: 6px;
  border-radius: 24px;
  background: var(--gray-500);
}
.nav__dots span:last-child { background: var(--black); }

.nav__links {
  position: absolute;
  right: 0;
  display: flex;
  align-items: center;
  gap: 16px;
  white-space: nowrap;
}

/* L'effet de vague : chaque element part avec 60 ms d'ecart sur le suivant,
   dans l'ordre des noms de l'original (Work 0, Services .1, Pricing .2,
   Blog .3, Contact .4). Au repli la vague s'inverse (dernier parti en
   premier) pour que le mouvement reste dirige vers le centre. */
.nav__links > * {
  opacity: 1;
  transform: translateY(0) scale(1);
  transition: opacity 320ms var(--ease), transform 320ms var(--ease);
}
.nav__links > *:nth-child(1) { transition-delay: 0ms; }
.nav__links > *:nth-child(2) { transition-delay: 60ms; }
.nav__links > *:nth-child(3) { transition-delay: 120ms; }
.nav__links > *:nth-child(4) { transition-delay: 180ms; }
.nav__links > *:nth-child(5) { transition-delay: 240ms; }
.nav__links > *:nth-child(6) { transition-delay: 300ms; }

.nav__links a:not(.btn-light) {
  font-size: 14px;
  font-weight: 600;
  letter-spacing: -0.28px;
  transition: color 200ms var(--ease), opacity 320ms var(--ease), transform 320ms var(--ease);
}
.nav__links a:not(.btn-light):hover { color: var(--gray-700); }

/* --- Etat replie (pose par main.js au defilement vers le bas) ----------- */

.nav.is-collapsed { gap: 32px; }
.nav.is-collapsed .nav__right { width: 24px; }
.nav.is-collapsed .nav__links { pointer-events: none; }

.nav.is-collapsed .nav__links > * {
  opacity: 0;
  transform: translateY(-6px) scale(0.9);
}
/* vague inversee au repli */
.nav.is-collapsed .nav__links > *:nth-child(1) { transition-delay: 300ms; }
.nav.is-collapsed .nav__links > *:nth-child(2) { transition-delay: 240ms; }
.nav.is-collapsed .nav__links > *:nth-child(3) { transition-delay: 180ms; }
.nav.is-collapsed .nav__links > *:nth-child(4) { transition-delay: 120ms; }
.nav.is-collapsed .nav__links > *:nth-child(5) { transition-delay: 60ms; }
.nav.is-collapsed .nav__links > *:nth-child(6) { transition-delay: 0ms; }

.nav.is-collapsed .nav__dots { opacity: 1; pointer-events: auto; transition-delay: 320ms; }

.nav.is-collapsed .nav__id img { transform: scale(0.94); }

/* Vague des trois points : une fois la pilule repliee, les points sont le seul
   element vivant de la barre. Ils montent l'un apres l'autre, 160 ms d'ecart,
   avec un long temps mort en fin de cycle pour que le mouvement respire au
   lieu de clignoter. L'animation ne tourne qu'a l'etat replie — deployee, la
   pilule a deja la vague de ses liens. */
@keyframes nav-dots-wave {
  0%, 45%, 100% { transform: translateY(0);     opacity: 0.7; }
  20%           { transform: translateY(-3px);  opacity: 1; }
}

.nav.is-collapsed .nav__dots span {
  animation: nav-dots-wave 1.6s var(--ease) infinite;
}
.nav.is-collapsed .nav__dots span:nth-child(1) { animation-delay: 0ms; }
.nav.is-collapsed .nav__dots span:nth-child(2) { animation-delay: 160ms; }
.nav.is-collapsed .nav__dots span:nth-child(3) { animation-delay: 320ms; }

@media (max-width: 767px) {
  /* Sous 768 px la pilule reste compacte ; les points ouvrent un panneau.
     La barre est donc en permanence dans son etat replie : la vague des
     points s'y applique aussi, sauf quand le panneau est ouvert. */
  .nav { gap: 16px; }
  .nav__right { width: 24px; }
  .nav__dots { opacity: 1; pointer-events: auto; }
  .nav__dots span { animation: nav-dots-wave 1.6s var(--ease) infinite; }
  .nav__dots span:nth-child(2) { animation-delay: 160ms; }
  .nav__dots span:nth-child(3) { animation-delay: 320ms; }
  .nav.is-open .nav__dots span { animation: none; }
  .nav__links {
    position: absolute;
    top: calc(100% + 14px);
    right: -12px;
    left: auto;
    flex-direction: column;
    align-items: flex-start;
    gap: 12px;
    padding: 16px 20px;
    background: var(--white);
    border-radius: 20px;
    box-shadow: var(--shadow-pill);
    opacity: 0;
    transform: translateY(-8px);
    pointer-events: none;
    transition: opacity 250ms var(--ease), transform 250ms var(--ease);
  }
  .nav__links > * { transition-delay: 0ms !important; }
  .nav.is-open .nav__links { opacity: 1; transform: none; pointer-events: auto; }
  .nav.is-open .nav__links > * { opacity: 1; transform: none; }
  .nav.is-collapsed .nav__links > * { opacity: 1; transform: none; }
}


/* ==========================================================================
   07. Widget de contact flottant
   Apparait au defilement (JS ajoute .is-visible).
   ========================================================================== */

.contact-dock {
  position: fixed;
  bottom: 24px;
  left: 0;
  width: 100%;
  display: flex;
  justify-content: center;
  z-index: 99;
  pointer-events: none;
  opacity: 0;
  transform: translateY(16px);
  transition: opacity 400ms var(--ease), transform 400ms var(--ease);
}
.contact-dock.is-visible { opacity: 1; transform: none; }

.contact-dock__inner {
  display: flex;
  align-items: center;
  gap: 18px;
  padding: 12px 12px 12px 24px;
  background: rgba(255, 255, 255, 0.72);
  backdrop-filter: blur(5px);
  -webkit-backdrop-filter: blur(5px);
  border: 1px solid var(--hairline);
  border-radius: 48px;
  box-shadow: var(--shadow-pill);
  pointer-events: auto;
}

/* Bloc de texte : se retracte et s'efface a l'ouverture.
   grid-template-columns 1fr -> 0fr donne une largeur animable. */
.contact-dock__text {
  display: grid;
  grid-template-columns: 1fr;
  gap: 5px;
  transition: grid-template-columns 380ms var(--ease), opacity 260ms var(--ease),
              margin-right 380ms var(--ease);
}
.contact-dock__text > * { overflow: hidden; white-space: nowrap; min-width: 0; }

.contact-dock__icons { display: flex; align-items: center; gap: 8px; }

.dock-btn {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 0;
  width: 40px; height: 40px;
  padding: 8px;
  border-radius: 24px;
  overflow: hidden;
  flex: none;
  transition: width 380ms var(--ease), gap 380ms var(--ease),
              padding 380ms var(--ease), background-color 300ms var(--ease),
              transform 250ms var(--ease);
}
.dock-btn svg { width: 24px; height: 24px; flex: none; }

.dock-btn__label {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: -0.24px;
  white-space: nowrap;
  opacity: 0;
  transition: opacity 240ms var(--ease);
}

/* --- Etat deploye : largeurs relevees sur l'original ------------------- */
.contact-dock.is-open .contact-dock__inner { padding: 12px; }
.contact-dock.is-open .contact-dock__text {
  grid-template-columns: 0fr;
  opacity: 0;
  margin-right: -18px;
}
.contact-dock.is-open .dock-btn {
  gap: 6px;
  padding: 8px 12px 8px 8px;
}
/* Largeurs recalees sur les libelles francais : « E-mail » et « Reserver ». */
.contact-dock.is-open .dock-btn--dark  { width: 90px; }
.contact-dock.is-open .dock-btn--light { width: 104px; }
.contact-dock.is-open .dock-btn__label { opacity: 1; transition-delay: 120ms; }
.dock-btn--dark {
  background: var(--black);
  color: var(--white);
  box-shadow: var(--shadow-cta);
}
.dock-btn--light {
  background: var(--gray-100);
  color: var(--black);
  border: 1px solid var(--hairline);
  box-shadow: var(--shadow-soft);
}


/* ==========================================================================
   08. Hero
   ========================================================================== */

.hero {
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 100%;
  padding-top: var(--hero-top);
}

.hero__inner {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 32px;
  width: 100%;
  max-width: var(--container);
  padding: 0 var(--gutter) 96px;
}

@media (max-width: 767px) {
  .hero__inner { padding-bottom: 48px; }
}

.hero__head {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 24px;
  width: 100%;
}

/* Pastille de disponibilite */
.availability {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  /* L'original mesure 30 px de haut malgre un padding annonce de 12 px :
     Framer contraignait la hauteur du conteneur. On reproduit la hauteur. */
  padding: 7px 16px 7px 8px;
  background: var(--white);
  border: 1px solid var(--hairline);
  border-radius: 24px;
  box-shadow: var(--shadow-pill);
}

.pulse {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 16px; height: 16px;
  flex: none;
}
.pulse__outer,
.pulse__inner {
  position: absolute;
  border-radius: 1000px;
  background: var(--green);
}
.pulse__outer {
  width: 16px; height: 16px;
  opacity: 0.25;
  animation: pulse-ring 2s var(--ease) infinite;
}
.pulse__inner { width: 8px; height: 8px; opacity: 1; }

/* Les deux largeurs ont ete relachees pour le texte francais, plus long que
   l'anglais d'origine (12ch / 390px). Les sauts de ligne du titre restent
   pilotes par le <br> du balisage ; la largeur ne sert qu'a empecher une
   ligne de courir sur toute la grille. */
.hero__title { max-width: 15ch; }
.hero__lead { max-width: 560px; }

/* Rangee d'actions. Elle n'a plus qu'un seul bouton depuis le retrait du lien
   secondaire « Voir une realisation » ; l'enveloppe est conservee, elle porte
   l'alignement et resterait utile si un second appel a l'action revenait. */
.hero__actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 24px;
}

.hero__proof {
  display: flex;
  align-items: center;
  gap: 12px;
  height: 44px;
  padding-right: 12px;
  border-radius: 100px;
  flex: none;
}

.stack-avatars { display: flex; align-items: center; padding: 0 16px; flex: none; }
.stack-avatars img {
  flex: none;
  width: 32px; height: 32px;
  border-radius: 1000px;
  object-fit: cover;
  border: 2px solid var(--white);
}
.stack-avatars img + img { margin-left: -16px; }

.hero__rating { display: flex; flex-direction: column; gap: 6px; flex: none; }
.hero__rating > span { white-space: nowrap; }


/* ==========================================================================
   09. Bandeau sous le hero
   Le gabarit (.logos-row) est celui du bandeau de logos d'origine ; il porte
   desormais les quatre garanties. Le ticker reste defini plus bas : il n'est
   plus utilise sur la page d'accueil mais le composant est conserve.
   ========================================================================== */

/* Le padding haut est tombe de 32 a 4 px : la barre de garanties remonte de
   28 px vers le hero, dont elle prolonge la lecture. Le padding bas est
   conserve, il tient la separation avec la section suivante. */
.logos-row {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 20px;
  width: 100%;
  padding: 4px 0 32px;
  overflow: hidden;
}

.logos-row__inner {
  display: flex;
  align-items: center;
  gap: 32px;
  width: 100%;
  max-width: var(--container);
  padding-left: var(--gutter);
  overflow: hidden;
}

.ticker {
  position: relative;
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
}

.ticker__track {
  display: flex;
  align-items: center;
  gap: 24px;
  width: max-content;
  animation: ticker-scroll 30s linear infinite;
}
.ticker:hover .ticker__track { animation-play-state: paused; }

.ticker__track > * { flex: none; }

@keyframes ticker-scroll {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}

.ticker--reverse .ticker__track { animation-direction: reverse; }
.ticker--slow .ticker__track { animation-duration: 55s; }

.logo-item { display: flex; align-items: center; height: 35px; opacity: 0.85; }
.logo-item svg { height: 35px; width: auto; }

@media (max-width: 991px) {
  .logos-row__inner { padding-left: var(--gutter); }
  .hero__proof { display: none; }
}

/* --- Barre de garanties ---------------------------------------------------- */
/* Quatre promesses en pastilles noires pleines. La version d'origine reprenait
   le gris discret du bandeau de logos (12 px, gris 700, separateurs en point
   median) : illisible sur un fond blanc, juste sous un hero tres contraste.
   Elles sont maintenant traitees comme des etiquettes a part entiere — fond
   noir, texte blanc, 13 px — et les separateurs ont disparu, la forme des
   pastilles suffit a les distinguer. */

.guarantees {
  justify-content: center;
  flex-wrap: wrap;
  gap: 10px;
  padding-inline: var(--gutter);
  overflow: visible;
}

.guarantee {
  display: inline-flex;
  align-items: center;
  white-space: nowrap;
  padding: 9px 16px;
  background: var(--black);
  color: var(--white);
  border: 1px solid var(--black);
  border-radius: 1000px;
  box-shadow: var(--shadow-soft);
  font-size: 13px;
  font-weight: 600;
  line-height: 1;
  letter-spacing: -0.02em;
}

@media (max-width: 767px) {
  /* Sous 768 px les quatre pastilles s'empilent sur deux rangees : on resserre
     l'ecart et le padding pour que deux tiennent par ligne a 390 px. */
  .guarantees { gap: 8px; }
  .guarantee { padding: 8px 12px; font-size: 12px; }
}


/* ==========================================================================
   10. Latest Projects
   Grille 2 x 2 (992px de contenu, gouttiere 24px, carte 484px, ratio 4:3).
   ========================================================================== */

.projects__inner { gap: 64px; }

/* Titre + sous-titre empiles et centres (l'original n'avait que le titre). */
.projects__header {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 16px;
  width: 100%;
  text-align: center;
}
.projects__header p { max-width: 52ch; }

.projects__grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 24px;
  width: 100%;
}

/* Trois cartes dans une grille a deux colonnes : la derniere occupe seule sa
   rangee. On la centre a la largeur d'une colonne plutot que de la laisser
   collee a gauche. La regle ne s'applique qu'a un nombre impair de cartes. */
.projects__grid > .project-fly:last-child:nth-child(odd) {
  grid-column: 1 / -1;
  width: calc(50% - 12px);
  justify-self: center;
}

@media (max-width: 767px) {
  .projects__grid { grid-template-columns: minmax(0, 1fr); }
  .projects__inner { gap: 32px; }
  .projects__grid > .project-fly:last-child:nth-child(odd) { width: 100%; }
}

/* Lien « Voir tous les projets », sous la grille de l'accueil. Le gap de
   .projects__inner vaut 64 px : on le resserre, le lien appartient a la
   grille qu'il prolonge. */
.projects__all { margin-top: -24px; }


/* --- 10 bis. Grille de la page « Tous les projets » -----------------------
   Meme carte (.project) que sur l'accueil, mais sans l'enveloppe .project-fly
   ni sa projection dans le hero : sur une page de listing les cartes sont
   simplement posees. La grille accepte donc un nombre quelconque de projets,
   sans la regle de centrage du dernier element propre a l'accueil. */

.projectgrid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 56px 24px;
  width: 100%;
}

@media (max-width: 767px) {
  .projectgrid { grid-template-columns: minmax(0, 1fr); gap: 40px; }
}


/* Conteneur pilote par le defilement : au repos (haut de page) la carte est
   projetee dans le hero — echelle 0,70, rotation et translation propres a
   chaque carte, portees par --r / --x / --y en attribut style. main.js
   interpole lineairement vers l'identite sur les 715 premiers pixels de
   defilement, exactement comme l'original.
   L'origine 50% 43,4% correspond au centre du visuel (181,5 / 418). */
.project-fly {
  position: relative;
  transform-origin: 50% 43.4%;
  will-change: transform;
  /* Fondu au chargement : les cartes sont les premieres a l'ecran (delai 0),
     le texte du hero suit a 400 ms. .is-loaded est pose par main.js une fois
     les images decodees. */
  opacity: 0;
  transition: opacity 500ms var(--ease);
}
.project-fly.is-loaded { opacity: 1; }

@media (max-width: 991px) {
  /* Les valeurs de dispersion sont calees sur la grille 2 colonnes ;
     sous 992 px les cartes restent en place. */
  .project-fly { transform: none !important; }
}

.project {
  display: flex;
  flex-direction: column;
  gap: 12px;
  /* Le basculement 3D suit la souris : la variable est ecrite par main.js */
  transform: perspective(1200px) var(--tilt, none);
  transition: transform 500ms var(--ease);
  will-change: transform;
}

.project__media {
  position: relative;
  width: 100%;
  aspect-ratio: 4 / 3;
  border-radius: 16px;
  overflow: hidden;
  background: var(--gray-200);
}

.project__media img {
  width: 100%; height: 100%;
  object-fit: cover;
  transform: scale(1.1);
  transition: transform 700ms var(--ease);
}
.project:hover .project__media img { transform: scale(1); }

.project__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  width: 100%;
}

.project__meta { display: flex; flex-direction: column; gap: 4px; }

.project__cta {
  display: flex;
  align-items: center;
  gap: 4px;
  flex: none;
}
.project__cta-icon {
  position: relative;
  width: 18px; height: 18px;
  overflow: hidden;
}
.project__cta-icon svg {
  position: absolute;
  inset: 0;
  width: 18px; height: 18px;
  transition: transform 300ms var(--ease);
}
.project__cta-icon svg:nth-child(2) { transform: translate(-100%, 100%); }
.project:hover .project__cta-icon svg:nth-child(1) { transform: translate(100%, -100%); }
.project:hover .project__cta-icon svg:nth-child(2) { transform: none; }

/* Pastille posee sur le visuel. INUTILISEE : la mention « Concept de
   demonstration » a ete retiree des trois cartes, la section ne se presente
   plus comme une demonstration. La regle est conservee au cas ou une carte
   devrait porter un libelle en surimpression (« En cours », « Bientot »). */
.project__demo {
  position: absolute;
  top: 12px; left: 12px;
  padding: 5px 10px;
  border-radius: 1000px;
  background: rgba(255, 255, 255, 0.88);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  color: var(--gray-700);
  box-shadow: var(--shadow-soft);
  pointer-events: none;
}

/* Troisieme ligne de la carte : ce qui a ete livre. */
.project__note {
  display: block;
  padding-top: 4px;
  border-top: 1px solid var(--gray-200);
}


/* ==========================================================================
   11. Big Quote
   ========================================================================== */

/* Le respire de la section fait tout le travail : c'est une respiration entre
   les realisations et la demonstration par comparaison, pas un argument de
   plus. Padding double de celui des autres sections. */
.bigquote { padding-block: 128px; }

.bigquote__inner {
  align-items: center;
  gap: 40px;
  padding: 24px var(--gutter);
}

/* Pleine largeur du conteneur, centre. 900 px plutot que 780 : l'echelle
   t-quote-lg (40 px) pose la citation sur trois lignes a cette largeur.
   .bigquote__text porte maintenant directement le <blockquote> : la citation
   est anonyme depuis le retrait de la signature, il n'y a plus de figure. */
.bigquote__text {
  max-width: 900px;
  text-align: center;
  text-wrap: balance;
}

@media (max-width: 767px) {
  .bigquote { padding-block: 72px; }
  .bigquote__inner { gap: 28px; }
}


/* ==========================================================================
   11 bis. Preuve par comparaison — tableau a cinq canaux
   Six criteres en lignes, cinq canaux en colonnes. Vrai <table> : les
   en-tetes de lignes et de colonnes portent leur scope, un lecteur d'ecran
   annonce donc « Instagram, On voit votre travail avant de vous parler,
   Oui ». Sous 768 px le tableau bascule en blocs empiles : chaque cellule
   reprend son intitule de colonne par ::before (attribut data-col).
   La derniere colonne (« Notre service ») est detachee en noir : c'est la
   seule qui coche les six lignes, autant que la lecture le voie tout de suite.
   ========================================================================== */

.compare__inner { gap: 48px; }

.compare__head { width: 100%; text-align: center; }

/* Filet de securite : si la fenetre est trop etroite pour les six colonnes
   entre 768 et ~900 px, le tableau defile pour lui-meme plutot que de pousser
   la page (le <main> est en overflow-x: clip, un debordement serait coupe). */
.ctable__scroll {
  width: 100%;
  overflow-x: auto;
  overscroll-behavior-x: contain;
}

/* min-width calee sur le point de rupture : a 768 px le conteneur offre
   680 px de contenu (viewport - 2 x 44 de gouttiere). A 640 px le tableau y
   tient sans defilement interne, et sous 768 px il bascule de toute facon en
   blocs empiles. Le conteneur .ctable__scroll ne sert donc que de filet. */
.ctable {
  width: 100%;
  min-width: 640px;
  border-collapse: collapse;
  table-layout: fixed;
}

/* La colonne des criteres prend le tiers, les cinq canaux se partagent
   le reste a egalite. */
.ctable th[scope="col"]:first-child { width: 34%; }

.ctable thead th {
  padding: 0 8px 12px;
  vertical-align: bottom;
  text-align: center;
  font-weight: 600;
  color: var(--gray-700);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  text-wrap: balance;
}

.ctable tbody tr { border-top: 1px solid var(--gray-200); }
.ctable tbody tr:last-child { border-bottom: 1px solid var(--gray-200); }

.ctable th[scope="row"] {
  padding: 18px 24px 18px 0;
  text-align: left;
  font-weight: 500;
}

.ctable td {
  padding: 18px 8px;
  text-align: center;
  vertical-align: middle;
}

/* Colonne « Notre service » : fond noir plein, coin arrondi en haut et en bas
   pour qu'elle se lise comme une carte posee par-dessus le tableau. */
.ctable__ours {
  background: var(--black);
  color: var(--white);
}
.ctable thead .ctable__ours {
  padding-top: 12px;
  border-radius: 12px 12px 0 0;
  color: var(--white);
}
.ctable tbody tr:last-child .ctable__ours { border-radius: 0 0 12px 12px; }

/* --- Coches et croix ------------------------------------------------------
   Dessinees en CSS plutot qu'en glyphes : ✓ et ✗ n'existent pas dans toutes
   les polices de la pile et tomberaient sur un repli aux metriques
   differentes. Le trait suit la couleur du texte, la colonne noire les
   obtient donc en blanc sans regle supplementaire. */

.mark {
  position: relative;
  display: inline-block;
  width: 16px; height: 16px;
  vertical-align: middle;
}
.mark::before,
.mark::after {
  content: '';
  position: absolute;
  background: currentColor;
  border-radius: 1px;
}

/* Coche : deux traits, un court et un long, formant un V incline */
.mark--yes::before {
  width: 2px; height: 7px;
  left: 3px; top: 6px;
  transform: rotate(-45deg);
  transform-origin: bottom center;
}
.mark--yes::after {
  width: 2px; height: 13px;
  left: 9px; top: 1px;
  transform: rotate(40deg);
  transform-origin: bottom center;
}

/* Croix : deux traits croises, en gris pour rester en retrait de la coche */
.mark--no { color: var(--gray-400); }
.ctable__ours .mark--no { color: rgba(255, 255, 255, 0.45); }
.mark--no::before,
.mark--no::after {
  width: 2px; height: 14px;
  left: 7px; top: 1px;
}
.mark--no::before { transform: rotate(45deg); }
.mark--no::after  { transform: rotate(-45deg); }

.compare__close {
  max-width: 60ch;
  text-align: center;
}

@media (max-width: 767px) {
  /* Bascule en blocs : une carte par critere, les cinq canaux en lignes.
     Le tableau perd sa mise en forme tabulaire (display: block) mais garde
     sa semantique — seule la presentation change. */
  .ctable__scroll { overflow-x: visible; }
  .ctable,
  .ctable tbody,
  .ctable tr,
  .ctable th,
  .ctable td { display: block; width: auto; }

  .ctable { min-width: 0; }
  .ctable thead { display: none; }

  .ctable tbody tr {
    padding: 4px 0 12px;
    border-top: 1px solid var(--gray-200);
  }
  .ctable tbody tr:last-child { border-bottom: 1px solid var(--gray-200); }

  .ctable th[scope="row"] {
    padding: 16px 0 12px;
    font-weight: 600;
  }

  /* Intitule de colonne a gauche, marque a droite */
  .ctable td {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    padding: 7px 0;
    text-align: left;
  }
  .ctable td::before {
    content: attr(data-col);
    font-size: 13px;
    font-weight: 500;
    color: var(--gray-700);
  }

  /* La colonne noire redevient une simple ligne : un aplat noir sur une
     rangee de 30 px serait une tache. On la marque par le texte en gras et
     un fond tres clair, coins arrondis. */
  .ctable__ours {
    background: var(--gray-150);
    color: var(--black);
    padding-inline: 12px;
    border-radius: 10px;
    margin-top: 4px;
  }
  .ctable tbody tr:last-child .ctable__ours,
  .ctable thead .ctable__ours { border-radius: 10px; }
  .ctable__ours::before { font-weight: 700; color: var(--black); }
  .ctable__ours .mark--no { color: var(--gray-400); }
}


/* ==========================================================================
   12. Services
   Deux colonnes : titre + stack a gauche, liste des services a droite.
   ========================================================================== */

/* Les deux colonnes etaient centrees l'une sur l'autre. Depuis que le titre
   court « Ce que je livre » a laisse place a une phrase sur deux lignes, la
   colonne de gauche est presque aussi haute que la liste de droite : on cale
   les deux en haut, le titre ouvre la section au meme niveau que la premiere
   prestation. */
.services__inner {
  flex-direction: row;
  align-items: flex-start;
  gap: 64px;
}

/* Le titre porte desormais la colonne de gauche. Il n'a pas de largeur
   propre : il se plie a celle de la colonne, le <br> du balisage ne servant
   qu'a isoler la seconde moitie de la phrase, en noir. */
.services__title { width: 100%; }

@media (max-width: 767px) {
  .services__inner { flex-direction: column; align-items: flex-start; gap: 40px; }
}

.services__left {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 64px;
  flex: 1 1 0;
  min-width: 0;
}

@media (max-width: 767px) { .services__left { gap: 40px; } }

.techstack { display: flex; flex-direction: column; gap: 16px; width: 100%; }

.techstack__logos { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }

.tool {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
}

.tool__logo {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 56px; height: 56px;
  padding: 16px;
  background: var(--white);
  border: 1px solid var(--hairline);
  border-radius: 12px;
  box-shadow: var(--shadow-soft);
  transition: transform 250ms var(--ease);
}
.tool:hover .tool__logo { transform: translateY(-4px); }
.tool__logo svg { width: 24px; height: 24px; }

/* Infobulle : purement CSS, revelee au survol et au focus clavier */
.tool__tip {
  position: absolute;
  bottom: calc(100% + 8px);
  left: 50%;
  padding: 4px 8px;
  background: var(--black);
  color: var(--white);
  border-radius: 1000px;
  white-space: nowrap;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: -0.24px;
  opacity: 0;
  transform: translate(-50%, 4px);
  pointer-events: none;
  transition: opacity 200ms var(--ease), transform 200ms var(--ease);
}
.tool:hover .tool__tip,
.tool:focus-within .tool__tip { opacity: 1; transform: translate(-50%, 0); }

.services__list {
  display: flex;
  flex-direction: column;
  flex: 1 1 0;
  min-width: 0;
  width: 100%;
}

/* La rangee portait un simple libelle ; elle porte maintenant un nom et une
   description. L'icone reste alignee sur la premiere ligne, pas au centre
   d'un bloc de deux lignes. */
.service-row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  padding: 20px 0;
  border-bottom: 1px solid var(--gray-200);
  transition: padding-left 300ms var(--ease), color 300ms var(--ease);
}
.service-row:first-child { border-top: 1px solid var(--gray-200); }
.service-row:hover { padding-left: 8px; }

.service-row__main { display: flex; flex-direction: column; gap: 6px; min-width: 0; }

.service-row__icon {
  flex: none;
  width: 18px; height: 18px;
  margin-top: 6px;
  opacity: 0;
  transition: opacity 300ms var(--ease);
}
.service-row:hover .service-row__icon { opacity: 1; }


/* ==========================================================================
   13 bis. Processus
   Cinq etapes numerotees, en rangees separees par un filet, comme la liste
   des services. Le numero occupe une colonne fixe de 48 px.
   ========================================================================== */

.process__inner { gap: 48px; }

.process__title { width: 100%; text-align: center; }

.process { width: 100%; }

.process__step {
  display: flex;
  align-items: flex-start;
  gap: 24px;
  padding: 28px 0;
  border-bottom: 1px solid var(--gray-200);
}
.process__step:first-child { border-top: 1px solid var(--gray-200); }

.process__num {
  flex: none;
  width: 48px;
  padding-top: 4px;
  color: var(--gray-500);
  font-variant-numeric: tabular-nums;
}

.process__body { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.process__body .t-xs { max-width: 78ch; }

/* Precision sur le point de depart du delai, sous les cinq etapes. */
.process__note {
  width: 100%;
  max-width: 72ch;
  margin-top: -16px;   /* le gap de .process__inner vaut 48, on le resserre */
}

@media (max-width: 767px) {
  .process__step { gap: 16px; padding: 20px 0; }
  .process__num { width: 32px; }
}


/* ==========================================================================
   13. About Me — section « Qui suis-je » (#apropos)
   Ces regles etaient conservees inutilisees depuis le retrait de la section
   About du template (PROJECT_STATUS §3.4, « pour que ces blocs puissent etre
   reposes sans les reecrire »). C'est exactement ce qui s'est passe le
   16 aout : la section « Qui suis-je » les remet en service telles quelles.
   Deux ajustements seulement, en fin de bloc :
     - le parcours (.about__history) passe en bas de section, pleine largeur,
       alors qu'il vivait dans la colonne de gauche ;
     - les elements devenus sans objet (pastille reseaux, signature SVG)
       restent definis mais ne sont plus appeles par le balisage.
   ========================================================================== */

.about__inner { gap: 64px; align-items: flex-start; }
.about__title { align-self: flex-start; }

/* Colonnes de l'original : 371 px a gauche, 557 px a droite, gouttiere 64. */
.about__grid {
  display: grid;
  grid-template-columns: 371px minmax(0, 1fr);
  gap: 64px;
  width: 100%;
  align-items: start;
}

@media (max-width: 991px) {
  .about__grid { grid-template-columns: minmax(0, 1fr); gap: 40px; }
}

/* 16 px entre la photo et l'identite, 64 px avant l'historique (original). */
.about__left {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.about__portrait {
  position: relative;
  border-radius: 16px;
  overflow: hidden;
  background: var(--gray-200);
  aspect-ratio: 371 / 410;
}
.about__portrait img { width: 100%; height: 100%; object-fit: cover; }

/* Pastille reseaux posee en bas a droite de la photo */
.about__socials {
  position: absolute;
  right: 12px;
  bottom: 12px;
  display: flex;
  align-items: center;
  gap: 6px;
  background: rgba(255, 255, 255, 0.55);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  border-radius: 1000px;
}

.about__social {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  width: 30px; height: 30px;
  border-radius: 1000px;
  background: var(--white);
  transition: background-color 200ms var(--ease), transform 200ms var(--ease);
}
.about__social:hover { transform: translateY(-2px); }
.about__social svg { width: 14px; height: 14px; }
.about__social--count { width: auto; padding: 0 10px 0 8px; }

.about__ident {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

/* Le parcours n'est plus dans la colonne de gauche mais en bas de section,
   sur toute la largeur : la consigne le voulait sous l'image et le texte.
   Il est donc enfant direct de .about__inner (gap 64), d'ou la marge nulle. */
.about__history {
  display: flex;
  flex-direction: column;
  gap: 16px;
  width: 100%;
}

/* --- Pile de cartes « work history » ------------------------------------
   Fiche 1 dans le flux, fiches 2 et 3 en absolu derriere elle : 8 px plus
   bas et 9 px plus etroites de chaque cote a chaque rang. « Show all »
   deplie la pile en liste verticale. */

.deck { position: relative; }

.deck__card {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 18px;
  background: var(--white);
  border: 1px solid var(--hairline);
  border-radius: 16px;
  box-shadow: var(--shadow-soft);
  transition: transform 420ms var(--ease), margin-top 420ms var(--ease),
              opacity 320ms var(--ease);
}
.deck__left { display: flex; flex-direction: column; gap: 4px; }

/* Etat empile */
.deck__card:not(:first-child) {
  position: absolute;
  top: 0; left: 0; right: 0;
  z-index: calc(3 - var(--i));
  transform: translateY(calc(var(--i) * 8px)) scale(calc(1 - var(--i) * 0.049));
}
.deck__card:first-child { position: relative; z-index: 3; }

/* Etat deplie */
.deck.is-open .deck__card:not(:first-child) {
  position: relative;
  transform: none;
  margin-top: 12px;
}

.deck__toggle {
  align-self: flex-end;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 8px 16px;
  border-radius: 1000px;
  color: var(--black);
  transition: background-color 200ms var(--ease), transform 250ms var(--ease);
}
.deck__toggle:hover { background: var(--gray-150); }
.deck__toggle svg { transition: transform 300ms var(--ease); }
.deck__toggle[aria-expanded="true"] svg { transform: rotate(180deg); }

/* Le portrait etait en 371 x 410 dans le template ; ici la colonne de gauche
   est la seule image de la section, on lui rend son format portrait. */
.about__portrait { aspect-ratio: 3 / 4; }

/* Colonne droite */
.about__philosophy { display: flex; flex-direction: column; gap: 48px; }
.about__bio { display: flex; flex-direction: column; gap: 24px; }
.about__signature {
  display: block;
  align-self: flex-start;
  width: 97px;
  color: var(--black);
}
.about__signature svg { width: 97px; height: 97px; }


/* ==========================================================================
   14 bis. Votre presence en ligne
   Remplace la section « Investissement ». Deux blocs opposes par le contraste :
   un bloc blanc a trois volets (ce qui est compris) et un bloc noir (ce qui
   est en option), articules par un « + » isole.
   Aucun chiffre n'apparait : c'est un inventaire, pas un tarif.
   ========================================================================== */

.presence__inner { gap: 32px; }

/* --- Bloc blanc : trois volets separes par un « + » ---------------------- */
/* Grille explicite plutot que flex : les trois volets doivent faire la meme
   largeur quel que soit le nombre de lignes de chaque liste, et les deux « + »
   n'occupent qu'une colonne etroite. */
.presence__pack {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr) auto minmax(0, 1fr);
  align-items: start;
  gap: 32px;
  width: 100%;
  padding: 40px;
  background: var(--white);
  border: 1px solid var(--hairline);
  border-radius: 24px;
  box-shadow: var(--shadow-soft);
}

.pcol { display: flex; flex-direction: column; gap: 20px; min-width: 0; }

.pcol__head {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding-bottom: 16px;
  border-bottom: 1px solid var(--gray-200);
}

/* Les « + » de separation, cales sur la premiere ligne de texte du volet */
.pplus {
  align-self: start;
  padding-top: 2px;
  font-size: 24px;
  font-weight: 500;
  line-height: 1;
  color: var(--gray-400);
  user-select: none;
}

/* Le « + » isole, entre le bloc blanc et le bloc noir. Plus grand, centre,
   il marque l'articulation entre ce qui est compris et ce qui s'ajoute. */
.pplus--solo {
  align-self: center;
  padding: 0;
  font-size: 40px;
  color: var(--gray-500);
}

/* --- Listes --------------------------------------------------------------
   Puce dessinee en pseudo-element : la liste est en list-style: none depuis
   la reinitialisation, et un tiret cadre mieux avec le reste de la page
   qu'une puce ronde. */
.plist { display: flex; flex-direction: column; gap: 10px; }

.plist li {
  position: relative;
  padding-left: 18px;
  color: var(--gray-700);
}
.plist li::before {
  content: '';
  position: absolute;
  left: 0; top: 0.62em;
  width: 8px; height: 1px;
  background: var(--gray-400);
}

.plist--dark li { color: rgba(255, 255, 255, 0.75); }
.plist--dark li::before { background: rgba(255, 255, 255, 0.45); }

/* --- Bloc noir : l'option ------------------------------------------------ */
.presence__extra {
  display: flex;
  flex-direction: column;
  gap: 16px;
  width: 100%;
  padding: 40px;
  background: var(--black);
  color: var(--white);
  border-radius: 24px;
}

.pextra__tag {
  align-self: flex-start;
  padding: 6px 12px;
  border: 1px solid rgba(255, 255, 255, 0.25);
  border-radius: 1000px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.pextra__lead { color: rgba(255, 255, 255, 0.85); max-width: 60ch; }
.presence__extra .plist { margin-top: 8px; }

/* Deux colonnes pour les six lignes : une colonne unique sur toute la largeur
   du bloc laisserait une grande zone vide a droite. */
.presence__extra .plist {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px 40px;
}

@media (max-width: 991px) {
  /* Les trois volets s'empilent : les « + » passent en separateurs
     horizontaux, centres entre deux volets. */
  .presence__pack {
    grid-template-columns: minmax(0, 1fr);
    gap: 24px;
    padding: 32px;
  }
  .pplus { justify-self: center; align-self: center; padding-top: 0; }
  .presence__extra { padding: 32px; }
  .presence__extra .plist { grid-template-columns: minmax(0, 1fr); gap: 10px; }
}

@media (max-width: 767px) {
  .presence__pack { padding: 24px; border-radius: 20px; }
  .presence__extra { padding: 24px; border-radius: 20px; }
  .pplus--solo { font-size: 32px; }
}


/* ==========================================================================
   14. Pricing
   ========================================================================== */

.pricing__inner { gap: 64px; }

.pricing__steps {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 24px;
  width: 100%;
}

@media (max-width: 767px) {
  .pricing__steps { grid-template-columns: minmax(0, 1fr); }
}

.step {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 24px;
  background: var(--gray-100);
  border: 1px solid var(--gray-200);
  border-radius: 16px;
}
.step__icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px; height: 40px;
  background: var(--white);
  border: 1px solid var(--gray-200);
  border-radius: 12px;
}

.plans {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 24px;
  width: 100%;
}

@media (max-width: 991px) {
  .plans { grid-template-columns: minmax(0, 1fr); }
}

.plan {
  display: flex;
  flex-direction: column;
  gap: 24px;
  padding: 32px;
  border: 1px solid var(--gray-200);
  border-radius: 24px;
  background: var(--white);
}
.plan--dark {
  background: var(--black);
  border-color: var(--black);
  color: var(--white);
}
.plan__price { display: flex; align-items: baseline; gap: 8px; }
.plan__features { display: flex; flex-direction: column; gap: 12px; }
.plan__features li { display: flex; align-items: center; gap: 10px; }
.plan__features svg { flex: none; width: 18px; height: 18px; }
.plan .btn { width: 100%; height: 44px; margin-top: auto; }

.plan__cta-dark {
  background: var(--white);
  color: var(--black);
  font-size: 14px;
  font-weight: 600;
}
.plan__cta-light {
  background: var(--black);
  color: var(--white);
  font-size: 14px;
  font-weight: 600;
  box-shadow: var(--shadow-cta);
}

.benefits { display: flex; flex-wrap: wrap; gap: 8px; }

/* --- Blocs d'introduction centres de la section Pricing ------------------- */
/* Remplacent des attributs style= deplaces depuis index.html (etape B). */

.pricing__head,
.pricing__sub {
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 100%;
}
.pricing__head { gap: 16px; text-align: center; }
.pricing__head p { max-width: 46ch; }

.pricing__sub { gap: 24px; }
.pricing__sub p { text-align: center; max-width: 34ch; }
.pricing__sub .benefits { justify-content: center; }

/* Section Investissement : plus de cartes tarifaires, un seul appel a
   l'action centre sous le texte. */
.invest__cta {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 20px;
  width: 100%;
}
.invest__cta .btn { height: 48px; padding-inline: 28px; }
.invest__cta .benefits { justify-content: center; }

.plan__desc { margin-top: 8px; font-weight: 500; }
.plan--dark .plan__desc { color: var(--gray-300); }
.plan--dark .plan__features { color: var(--white); }

.plan__powered {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
}
.benefit {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 12px;
  border-radius: 1000px;
  border: 1px solid var(--hairline);
  background: var(--white);
}


/* ==========================================================================
   15. Garantie (ex-Testimonials)
   Le gabarit de carte (.tcard) est conserve, mais les trois engagements sont
   poses en grille : deux pistes qui defilent n'avaient de sens que pour une
   liste longue de temoignages.
   ========================================================================== */

.testimonials__inner { gap: 48px; }

.testimonials__head {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 24px;
  width: 100%;
  text-align: center;
}
.testimonials__head .ticker { width: 100%; }
.trusted-item { padding-inline: 16px; }

.testimonials__rows { display: flex; flex-direction: column; gap: 24px; width: 100%; }

.tcard {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 24px;
  width: 380px;
  padding: 24px;
  background: var(--white);
  border: 1px solid var(--hairline);
  border-radius: 16px;
  transition: border-color 250ms var(--ease), transform 250ms var(--ease);
}
.tcard:hover { border-color: var(--black); transform: translateY(-3px); }

.tcard__quote-mark { color: var(--gray-300); }
.tcard__quote-mark svg { width: 24px; height: 24px; }

.tcard__client { display: flex; align-items: center; gap: 12px; }
.tcard__client img {
  width: 38px; height: 38px;
  border-radius: 100%;
  object-fit: cover;
}
.tcard__who { display: flex; flex-direction: column; gap: 6px; }

@media (max-width: 767px) {
  .tcard { width: 300px; }
}

/* --- Grille des trois engagements ------------------------------------------ */
/* .tcard--static libere la largeur fixe de 380 px, calee sur le ticker. */

.guarantees__grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 24px;
  width: 100%;
  align-items: stretch;
}

.tcard--static {
  width: auto;
  justify-content: flex-start;
  gap: 16px;
}

.tcard__mark { color: var(--gray-500); line-height: 0; }
.tcard__mark svg { width: 22px; height: 22px; }

@media (max-width: 991px) {
  .guarantees__grid { grid-template-columns: minmax(0, 1fr); }
  .tcard--static { width: auto; }
}


/* ==========================================================================
   16. FAQ
   Accordeon natif <details>/<summary> : accessible, fonctionnel sans JS.
   L'ouverture est animee via grid-template-rows.
   ========================================================================== */

.faq__inner { gap: 64px; }

.faq__grid {
  display: grid;
  grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr);
  gap: 64px;
  width: 100%;
  align-items: start;
}

@media (max-width: 991px) {
  .faq__grid { grid-template-columns: minmax(0, 1fr); gap: 40px; }
}

.faq__list { display: flex; flex-direction: column; width: 100%; }

.faq__item { border-bottom: 1px solid var(--gray-200); }
.faq__item:first-child { border-top: 1px solid var(--gray-200); }

.faq__q {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 24px 0;
  cursor: pointer;
  list-style: none;
  transition: color 200ms var(--ease);
}
.faq__q:hover { color: var(--gray-700); }

.faq__num { flex: none; width: 24px; color: var(--gray-500); }

.faq__sign {
  position: relative;
  flex: none;
  margin-left: auto;
  width: 16px; height: 16px;
}
.faq__sign::before,
.faq__sign::after {
  content: '';
  position: absolute;
  top: 50%; left: 0; right: 0;
  height: 1.5px;
  background: currentColor;
  border-radius: 2px;
  transition: transform 300ms var(--ease);
}
.faq__sign::after { transform: rotate(90deg); }
.faq__item[open] .faq__sign::after { transform: rotate(0deg); }

/* Animation de hauteur : la grille passe de 0fr a 1fr */
.faq__a {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 350ms var(--ease);
}
.faq__item[open] .faq__a { grid-template-rows: 1fr; }
.faq__a > div { overflow: hidden; }
.faq__a p { padding: 0 40px 24px; max-width: 62ch; font-weight: 500; }

@media (max-width: 767px) {
  .faq__a p { padding-inline: 0 0; }
}

.faq__cta p { font-weight: 500; }
/* Le libelle francais est long (« Reserver un creneau · Gratuit, sans
   engagement ») : le bouton passe en hauteur libre, avec un vrai rembourrage,
   plutot que de rester bloque a 44 px avec un texte qui deborde. */
.faq__cta .btn {
  height: auto;
  min-height: 44px;
  padding: 12px 20px;
  text-align: center;
}
.faq__cta .btn svg { opacity: 0.6; }

.faq__cta {
  margin-top: 32px;
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding: 32px;
  background: var(--gray-100);
  border: 1px solid var(--gray-200);
  border-radius: 24px;
}


/* ==========================================================================
   17 bis. Appel a l'action final
   ========================================================================== */

.finalcta__inner {
  gap: 28px;
  text-align: center;
}
.finalcta__title { max-width: 20ch; }
.finalcta__lead { max-width: 58ch; }
.finalcta .btn-cta { margin-top: 4px; }


/* --- 17 ter. Calendrier Cal.com inline -----------------------------------
   Depuis le 20 aout, la section #contact ne porte plus un bouton mais le
   calendrier Cal.com lui-meme (voir le bloc <script> avant </body>).

   Cal.com injecte un <iframe> dans ce conteneur et lui donne sa propre
   hauteur une fois charge. Avant cela le conteneur est vide : sans
   min-height la section se replierait puis sauterait d'un coup a l'arrivee
   du calendrier. Les 680 px couvrent la vue mois complete sur desktop.

   overflow: auto plutot que scroll : pas de barre de defilement dessinee
   tant que rien ne deborde.

   La carte blanche reprend le geste de .faq__cta (meme rayon, meme filet) :
   le calendrier est un bloc etranger, l'encadrer le raccroche a la page. */

.calinline {
  width: 100%;
  max-width: 940px;
  margin-top: 4px;
  overflow: auto;
  background: var(--white);
  border: 1px solid var(--gray-200);
  border-radius: 24px;
}

/* Reservation de hauteur AVANT l'injection, et seulement la : tant que le
   conteneur est vide la section garderait une hauteur nulle puis sauterait
   d'un coup a l'arrivee du calendrier.
   Des que Cal.com a injecte son <cal-inline>, la regle tombe et la carte
   epouse la hauteur que Cal donne a son iframe — 560 px en vue mois sur
   desktop, ~800 px en vue liste sous 768 px. Une min-height fixe laissait
   sinon 120 px de blanc mort sous le calendrier sur desktop. */
.calinline:empty { min-height: 600px; }

/* L'iframe de Cal.com arrive en display:inline et laisse un liseret sous la
   ligne de base ; il doit aussi remplir la carte. */
.calinline iframe { display: block; width: 100%; border: 0; }

@media (max-width: 767px) {
  .calinline { border-radius: 16px; }
}


/* ==========================================================================
   17. Articles
   Section retiree de la page d'accueil lors de l'adaptation francaise ; le
   balisage est conserve dans _sections-retirees.html. Regles gardees en vue
   d'une future page blog.
   ========================================================================== */

.articles__inner { gap: 48px; }

.articles__header {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 24px;
  width: 100%;
}

.articles__grid {
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr);
  gap: 24px;
  width: 100%;
}

@media (max-width: 991px) {
  .articles__grid { grid-template-columns: minmax(0, 1fr); }
}

.article { display: flex; flex-direction: column; gap: 16px; }

.article__media {
  position: relative;
  width: 100%;
  border-radius: 16px;
  overflow: hidden;
  background: var(--gray-200);
  aspect-ratio: 4 / 3;
}
.article__media img { width: 100%; height: 100%; object-fit: cover; transition: transform 700ms var(--ease); }
.article:hover .article__media img { transform: scale(1.04); }

.article__meta { display: flex; align-items: center; gap: 8px; }
.article__excerpt { font-weight: 500; }
.article--row .article__meta { margin-top: 8px; }

.articles__side { display: flex; flex-direction: column; gap: 24px; }

.article--row { flex-direction: row; align-items: center; gap: 16px; }
.article--row .article__media { width: 140px; flex: none; aspect-ratio: 1 / 1; }

@media (max-width: 767px) {
  .article--row { flex-direction: column; align-items: stretch; }
  .article--row .article__media { width: 100%; aspect-ratio: 4 / 3; }
}


/* --- 17 bis. Grille de la page « Ressources » -----------------------------
   La grille de l'accueil (.articles__grid) opposait un grand article a une
   colonne de deux petits : ce desequilibre servait a hierarchiser trois
   articles. Sur une page de listing, les articles sont a egalite — grille
   reguliere a deux colonnes, qui accepte n'importe quel nombre de cartes.
   La carte (.article) est celle du template, reprise sans changement. */

.bloggrid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 56px 24px;
  width: 100%;
}

/* « Lire l'article » : un <span>, pas un <a>. Toute la carte est deja un
   lien, imbriquer une ancre dans une ancre serait invalide. Le trait du
   survol est donc pilote par la carte, pas par le pseudo-lien. */
.article__more {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  position: relative;
  align-self: flex-start;
  margin-top: -4px;
  font-size: 14px;
  font-weight: 600;
  letter-spacing: -0.02em;
}
.article__more::after {
  content: '';
  position: absolute;
  left: 0; right: 0; bottom: -2px;
  height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: right center;
  transition: transform 350ms var(--ease);
}
.article:hover .article__more::after { transform: scaleX(1); transform-origin: left center; }
.article:hover .article__more .arrow { transform: translate(2px, -2px); }

@media (max-width: 767px) {
  .bloggrid { grid-template-columns: minmax(0, 1fr); gap: 40px; }
}


/* ==========================================================================
   18. Pied de page
   ========================================================================== */

.footer {
  background: var(--black);
  color: var(--white);
  width: 100%;
}

.footer__inner {
  display: flex;
  flex-direction: column;
  gap: 64px;
  padding-block: 96px 0;
}

.footer__top {
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) repeat(3, minmax(0, 0.6fr));
  gap: 48px;
  width: 100%;
}

@media (max-width: 991px) {
  .footer__top { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 40px; }
}
@media (max-width: 767px) {
  .footer__top { grid-template-columns: minmax(0, 1fr); }
  .footer__inner { padding-top: 64px; gap: 40px; }
}

/* Titre a mots rotatifs : « Lets [design|build|create] incredible work together. » */
.rotator {
  display: inline-grid;
  vertical-align: bottom;
  overflow: hidden;
  height: 1em;
  text-align: left;
}
.rotator span {
  grid-area: 1 / 1;
  animation: rotate-word 6s infinite;
  opacity: 0;
}
.rotator span:nth-child(1) { animation-delay: 0s; }
.rotator span:nth-child(2) { animation-delay: 2s; }
.rotator span:nth-child(3) { animation-delay: 4s; }

@keyframes rotate-word {
  0%   { opacity: 0; transform: translateY(100%); }
  5%   { opacity: 1; transform: translateY(0); }
  30%  { opacity: 1; transform: translateY(0); }
  35%  { opacity: 0; transform: translateY(-100%); }
  100% { opacity: 0; transform: translateY(-100%); }
}

.footer__col { display: flex; flex-direction: column; gap: 16px; }
/* Les intitules de colonne passent de h4 a h3 (etape B) : la page n'avait
   aucun h3 entre les h2 de section et ces libelles. */
.footer__col h3 { color: var(--gray-500); }
/* Un second intitule dans la meme colonne est detache du groupe precedent */
.footer__col h3 ~ h3 { margin-top: 16px; }
.footer__count { color: var(--gray-500); }
.footer__col a { color: var(--gray-300); transition: color 200ms var(--ease); }
.footer__col a:hover { color: var(--white); }

.footer__socials { display: flex; align-items: center; gap: 12px; }
.footer__socials a {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 36px; height: 36px;
  border-radius: 1000px;
  background: rgba(255, 255, 255, 0.08);
  transition: background-color 200ms var(--ease);
}
.footer__socials a:hover { background: rgba(255, 255, 255, 0.18); }
.footer__socials svg { width: 18px; height: 18px; }

.footer__bottom {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  width: 100%;
  padding-block: 24px;
  border-top: 1px solid rgba(255, 255, 255, 0.12);
  color: var(--gray-500);
}

/* Signature typographique geante */
.footer__wordmark {
  width: 100%;
  padding-top: 32px;
  font-size: clamp(64px, 20vw, 300px);
  font-weight: 600;
  line-height: 0.8;
  letter-spacing: -0.05em;
  text-align: center;
  color: rgba(255, 255, 255, 0.07);
  user-select: none;
  overflow: hidden;
}


/* ==========================================================================
   18 bis. Panneau de contact coulissant
   Ouvert par tout element portant data-contact-open. Le voile couvre la page,
   la feuille glisse depuis la droite. Ferme par la croix, le voile ou Echap
   (main.js section 13).
   ========================================================================== */

.cpanel {
  position: fixed;
  inset: 0;
  z-index: 200;              /* au-dessus de la nav (100) et du widget (99) */
  visibility: hidden;
  pointer-events: none;
}
.cpanel.is-open { visibility: visible; pointer-events: auto; }

.cpanel__scrim {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.45);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  opacity: 0;
  transition: opacity 420ms var(--ease);
}
.cpanel.is-open .cpanel__scrim { opacity: 1; }

/* La feuille : 100 % de la hauteur, largeur fixe sur desktop, plein ecran
   sous 560 px. Elle defile pour elle-meme si le formulaire depasse. */
.cpanel__sheet {
  position: absolute;
  top: 0; right: 0;
  display: flex;
  flex-direction: column;
  gap: 24px;
  width: min(440px, 100%);
  height: 100%;
  padding: 88px 40px 40px;
  overflow-y: auto;
  overscroll-behavior: contain;
  background: var(--white);
  box-shadow: -20px 0 60px -20px rgba(0, 0, 0, 0.25);
  transform: translateX(100%);
  transition: transform 480ms var(--ease);
}
.cpanel.is-open .cpanel__sheet { transform: none; }

.cpanel__close {
  position: absolute;
  top: 24px; right: 24px;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px; height: 40px;
  border-radius: 1000px;
  background: var(--gray-150);
  transition: background-color 250ms var(--ease), transform 250ms var(--ease);
}
.cpanel__close svg { width: 20px; height: 20px; }
.cpanel__close:hover { background: var(--gray-200); transform: rotate(90deg); }

.cpanel__head { display: flex; flex-direction: column; gap: 12px; }

.cpanel__form { display: flex; flex-direction: column; gap: 16px; }
/* display: flex l'emporterait sur la regle [hidden] du navigateur : le
   formulaire doit disparaitre une fois l'envoi confirme. */
.cpanel__form[hidden] { display: none; }

.cfield { display: flex; flex-direction: column; gap: 6px; }
.cfield > span { color: var(--gray-700); }

.cfield input,
.cfield select,
.cfield textarea {
  width: 100%;
  padding: 12px 14px;
  font: inherit;
  font-size: 15px;
  color: var(--black);
  background: var(--gray-150);
  border: 1px solid var(--gray-200);
  border-radius: 12px;
  transition: border-color 200ms var(--ease), background-color 200ms var(--ease);
}
.cfield textarea { resize: vertical; min-height: 96px; }
.cfield input::placeholder,
.cfield textarea::placeholder { color: var(--gray-400); }
.cfield input:focus,
.cfield select:focus,
.cfield textarea:focus {
  outline: none;
  background: var(--white);
  border-color: var(--black);
}

.cpanel__submit {
  height: 46px;
  margin-top: 4px;
  background: var(--black);
  color: var(--white);
  border-radius: 24px;
  box-shadow: var(--shadow-cta);
  font-size: 14px;
  font-weight: 600;
  letter-spacing: -0.28px;
}
.cpanel__submit:hover { background: var(--gray-900); }

/* Message de confirmation : remplace le formulaire une fois l'envoi simule. */
.cpanel__done {
  padding: 16px 18px;
  background: var(--gray-150);
  border: 1px solid var(--gray-200);
  border-radius: 12px;
}

/* Les deux issues directes, en bas de panneau. */
.cpanel__alt {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: auto;
  padding-top: 24px;
  border-top: 1px solid var(--gray-200);
}

.cpanel__alt-btn {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 16px;
  border: 1px solid var(--gray-300);
  border-radius: 1000px;
  font-size: 14px;
  font-weight: 600;
  letter-spacing: -0.28px;
  transition: background-color 250ms var(--ease), border-color 250ms var(--ease),
              transform 250ms var(--ease);
}
.cpanel__alt-btn svg { flex: none; width: 18px; height: 18px; }
.cpanel__alt-btn:hover { background: var(--gray-150); border-color: var(--black); }

@media (max-width: 559px) {
  .cpanel__sheet { padding: 80px 22px 32px; }
}

/* Le curseur inverse est inutile au-dessus du panneau : il gene la lecture
   d'un formulaire. */
.cpanel.is-open ~ .cursor { opacity: 0; }


/* ==========================================================================
   18 ter. Habillage « plan d'architecte »
   Un filet vertical de chaque cote, un filet horizontal en tete de chaque
   section : chaque section devient un rectangle cote, et la bande etroite de
   gauche porte des graduations, comme la marge d'un plan.
   Tout est en pseudo-elements sur les sections existantes : aucun element
   ajoute au balisage, aucune modification de la mise en page. Les filets sont
   volontairement tres pales — a 7 % de noir ils se lisent sur le blanc comme
   sur le gris 100 des sections alternees.
   ========================================================================== */

:root {
  --bp-line: rgba(0, 0, 0, 0.07);
  --bp-rail: 56px;   /* largeur de la colonne de marge, a gauche et a droite */
  --bp-tick: 64px;   /* pas des graduations */
}

/* Les sections deviennent le repere des filets. Leur contenu est remonte
   d'un cran pour passer au-dessus (le trace est en z-index 0). */
.hero, main > section, .footer { position: relative; }
.hero > *, main > section > *, .footer > * { position: relative; z-index: 1; }

.hero::before,
main > section::before,
.footer::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background-image:
    linear-gradient(var(--bp-line), var(--bp-line)),   /* filet vertical gauche */
    linear-gradient(var(--bp-line), var(--bp-line)),   /* filet vertical droit  */
    linear-gradient(var(--bp-line), var(--bp-line));   /* filet horizontal haut */
  background-repeat: no-repeat;
  background-position: var(--bp-rail) 0, calc(100% - var(--bp-rail)) 0, 0 0;
  background-size: 1px 100%, 1px 100%, 100% 1px;
}

/* Graduations de la colonne de gauche : des traits courts, tous les 64 px,
   masques au-dela de 12 px pour ne laisser depasser que l'amorce. */
.hero::after,
main > section::after,
.footer::after {
  content: '';
  position: absolute;
  top: 0; bottom: 0; left: 0;
  width: var(--bp-rail);
  z-index: 0;
  pointer-events: none;
  background-image: repeating-linear-gradient(
    to bottom,
    var(--bp-line) 0 1px,
    transparent 1px var(--bp-tick)
  );
  -webkit-mask-image: linear-gradient(90deg, #000 0 12px, transparent 12px);
          mask-image: linear-gradient(90deg, #000 0 12px, transparent 12px);
}

/* Le hero n'a pas de filet horizontal en tete : rien ne le precede. */
.hero::before {
  background-image:
    linear-gradient(var(--bp-line), var(--bp-line)),
    linear-gradient(var(--bp-line), var(--bp-line));
  background-position: var(--bp-rail) 0, calc(100% - var(--bp-rail)) 0;
  background-size: 1px 100%, 1px 100%;
}

/* Le pied de page est noir : un filet a 7 % de noir y serait invisible. On
   inverse le jeton, la variable etant heritee par les pseudo-elements. */
.footer { --bp-line: rgba(255, 255, 255, 0.09); }

@media (max-width: 991px) {
  :root { --bp-rail: 28px; --bp-tick: 56px; }
}

@media (max-width: 767px) {
  /* Sous 768 px la gouttiere tombe a 22 px : une colonne de marge la
     recouvrirait. On ne garde que les filets horizontaux, qui suffisent a
     marquer la separation des sections. */
  :root { --bp-rail: 0px; }
  .hero::after,
  main > section::after,
  .footer::after { display: none; }
  .hero::before { display: none; }
  main > section::before,
  .footer::before {
    background-image: linear-gradient(var(--bp-line), var(--bp-line));
    background-position: 0 0;
    background-size: 100% 1px;
  }
}


/* ==========================================================================
   19. Animations
   Parametres repris du JSON __framer__appearAnimationsContent :
     easing  cubic-bezier(0.4, 0, 0.2, 1)
     duree   0.5s a 0.6s
     delais  0.4s / 0.6s / 1.8s selon l'element
   Aucun element ne conserve opacity: 0.001 : l'etat initial est porte par
   une classe, et .is-in (posee au chargement ou par IntersectionObserver)
   ramene systematiquement a l'etat final.
   ========================================================================== */

/* --- Apparition au chargement --------------------------------------------- */

.appear {
  opacity: 0;
  transition: opacity 600ms var(--ease), transform 600ms var(--ease);
}
.appear.is-in { opacity: 1; transform: none; }

.appear--d400    { transition-delay: 400ms; }
.appear--d600    { transition-delay: 600ms; }

/* --- Revelation mot a mot -------------------------------------------------- */
/* Etat initial d'origine : opacity .001, filter blur(5px), translateY(10px).
   Le decalage entre mots (60 ms) et la duree (500 ms) ne figuraient pas dans
   le HTML : ils vivaient dans le module JS absent du mirror. Valeurs
   retenues apres comparaison visuelle avec l'original. */

/* Ce sont les mots eux-memes qui sont animes, pas les groupes de couleur :
   main.js pose la classe .w sur chaque span feuille et y numerote --i.
   --base decale tout le groupe ; sur le hero il vaut 400 ms, de sorte que
   les mots ne demarrent qu'une fois les images en place (cf. section 19 bis). */
.reveal-words .w {
  display: inline-block;
  opacity: 0;
  filter: blur(5px);
  transform: translateY(10px);
  transition: opacity 500ms var(--ease), filter 500ms var(--ease), transform 500ms var(--ease);
  transition-delay: calc(var(--base, 0ms) + var(--i, 0) * 60ms);
}
.reveal-words.is-in .w {
  opacity: 1;
  filter: blur(0);
  transform: none;
}

/* Variante automatique : main.js decoupe le texte en mots et pose --i sur
   chaque span, sans toucher au balisage d'origine (<strong>, <span> colores
   sont preserves). Utilisee pour la biographie de la section About Me. */
.reveal-words-auto > .w {
  display: inline-block;
  opacity: 0;
  filter: blur(5px);
  transform: translateY(10px);
  transition: opacity 500ms var(--ease), filter 500ms var(--ease), transform 500ms var(--ease);
  transition-delay: calc(var(--i, 0) * 26ms);
}
.reveal-words-auto.is-in > .w,
.reveal-words-auto.is-in .w {
  opacity: 1;
  filter: blur(0);
  transform: none;
}
.reveal-words-auto .w {
  display: inline-block;
  opacity: 0;
  filter: blur(5px);
  transform: translateY(10px);
  transition: opacity 500ms var(--ease), filter 500ms var(--ease), transform 500ms var(--ease);
  transition-delay: calc(var(--i, 0) * 26ms);
}

/* --- 19 bis. Ordre de chargement : images d'abord, texte ensuite ----------- */
/* Releve dans __framer__appearAnimationsContent : l'animation portee par les
   cartes projet (id 19y3y9x) a un delai de 0 ms, tandis que la pastille de
   disponibilite et le bouton demarrent a 400 ms et le bandeau de logos a
   600 ms. Les images sont donc les premieres a l'ecran. On reproduit cet
   ordre : les cartes se devoilent des le chargement, le texte du hero suit
   avec --base: 400ms. */

/* Le hero demarre son texte apres les images */
.hero__head .reveal-words,
.hero__head .reveal-block { --base: 400ms; }
.hero__lead.reveal-block { transition-delay: 400ms; }

/* Sous 820 px (iPad Air en portrait, telephones), les cartes projet ne volent
   plus dans le hero : elles restent dans leur grille, plus bas. Le delai de
   400 ms ne suffit donc plus a garantir que les images precedent le texte —
   rien ne retient le texte, il s'affiche avant que la moindre image ne soit
   decodee.
   main.js pose donc .is-holding sur le hero tant que les images ne sont pas
   pretes (section 3), et ces regles maintiennent le texte a son etat initial
   pendant ce temps. Au retrait de la classe, chaque element repart avec son
   propre delai — 400 ms pour le titre et l'accroche, comme a l'origine.
   Au-dessus de 820 px, rien ne change : les cartes sont dans le hero et le
   comportement d'origine est conserve. */
@media (max-width: 819px) {
  .hero.is-holding .appear { opacity: 0 !important; }
  .hero.is-holding .reveal-block { opacity: 0 !important; filter: blur(5px) !important; transform: translateY(10px) !important; }
  .hero.is-holding .reveal-words .w { opacity: 0 !important; filter: blur(5px) !important; transform: translateY(10px) !important; }
}


/* --- Revelation des textes au defilement ----------------------------------- */
/* Applique a tous les textes autonomes : titres, paragraphes, citations,
   elements de liste. Flou leger + remontee, duree courte pour rester
   discret. Pose par main.js sur les elements qui n'ont pas deja une
   animation propre. */

.text-reveal {
  opacity: 0;
  filter: blur(6px);
  transform: translateY(14px);
  transition: opacity 600ms var(--ease), filter 600ms var(--ease), transform 600ms var(--ease);
  transition-delay: calc(var(--td, 0ms));
}
.text-reveal.is-in {
  opacity: 1;
  filter: blur(0);
  transform: none;
}


/* --- Entree du bloc « My tech stack » -------------------------------------- */
/* Dans l'original les huit icones portent les noms « 1 - 0 delay » a
   « 8 - 0.7 delay » : c'est une entree echelonnee de 100 ms par icone, pas
   un ticker — le bloc est statique une fois pose (verifie : aucun mouvement
   apres stabilisation, aucun .ticker-item, overflow visible).
   Les icones arrivent decalees vers la gauche et glissent vers la droite
   jusqu'a leur position finale. */

.techstack__logos .tool {
  opacity: 0;
  filter: blur(4px);
  transform: translateX(-40px);
  transition: opacity 600ms var(--ease), filter 600ms var(--ease), transform 600ms var(--ease);
  transition-delay: calc(var(--i, 0) * 100ms);
}
.techstack__logos.is-in .tool {
  opacity: 1;
  filter: blur(0);
  transform: none;
}


/* --- Flou d'apparition en bas d'ecran -------------------------------------- */
/* Les elements qui entrent par le bas de la fenetre arrivent legerement
   flous puis se stabilisent. Le flou n'est applique que pendant la montee
   dans la moitie basse de l'ecran : une fois nets, ils le restent. */

.blur-in {
  opacity: 0;
  filter: blur(8px);
  transform: translateY(28px);
  transition: opacity 700ms var(--ease), filter 700ms var(--ease), transform 700ms var(--ease);
}
.blur-in.is-in {
  opacity: 1;
  filter: blur(0);
  transform: none;
}

/* Variante bloc (paragraphes, sous-titres) */
.reveal-block {
  opacity: 0;
  filter: blur(5px);
  transform: translateY(10px);
  transition: opacity 500ms var(--ease), filter 500ms var(--ease), transform 500ms var(--ease);
}
.reveal-block.is-in { opacity: 1; filter: blur(0); transform: none; }

/* --- Apparition au defilement ---------------------------------------------- */

.reveal {
  opacity: 0;
  transform: translateY(40px);
  transition: opacity 600ms var(--ease), transform 600ms var(--ease);
}
.reveal.is-in { opacity: 1; transform: none; }

/* Cascade pour les enfants directs */
.reveal-stagger > * {
  opacity: 0;
  transform: translateY(40px);
  transition: opacity 600ms var(--ease), transform 600ms var(--ease);
}
.reveal-stagger.is-in > * { opacity: 1; transform: none; }
.reveal-stagger.is-in > *:nth-child(1) { transition-delay: 0ms; }
.reveal-stagger.is-in > *:nth-child(2) { transition-delay: 80ms; }
.reveal-stagger.is-in > *:nth-child(3) { transition-delay: 160ms; }
.reveal-stagger.is-in > *:nth-child(4) { transition-delay: 240ms; }
.reveal-stagger.is-in > *:nth-child(5) { transition-delay: 320ms; }
.reveal-stagger.is-in > *:nth-child(6) { transition-delay: 400ms; }

/* --- Pulsation du point de disponibilite ----------------------------------- */

@keyframes pulse-ring {
  0%   { transform: scale(1);   opacity: 0.25; }
  70%  { transform: scale(2.2); opacity: 0; }
  100% { transform: scale(2.2); opacity: 0; }
}

/* --- Curseur inverse ------------------------------------------------------- */
/* Dans l'original, l'attribut data-framer-cursor est pose sur le conteneur de
   page entier (1425 x 9654) : l'effet couvre donc tout le site, titres,
   cartes, boutons et images compris. On reproduit ce champ d'application.

   Le disque est blanc et compose en mix-blend-mode: difference — sur fond
   clair il vire au noir, sur fond noir au blanc, sur une image il inverse
   les couleurs. Un disque noir serait neutre en difference (x - 0 = x) et
   donc invisible : le blanc est indispensable ici.

   Tailles : 8 px au repos, 20 px sur les zones interactives. Le diametre de
   survol passe de 56 a 20 px, soit une reduction des deux tiers. */

.cursor {
  position: fixed;
  top: 0; left: 0;
  z-index: 2147483647;
  width: 8px; height: 8px;
  margin: -4px 0 0 -4px;
  border-radius: 1000px;
  background: #fff;
  pointer-events: none;
  opacity: 0;
  will-change: transform;
  transition: width 220ms var(--ease), height 220ms var(--ease),
              margin 220ms var(--ease), opacity 200ms var(--ease);
  mix-blend-mode: difference;
}
.cursor.is-active { opacity: 1; }
.cursor.is-hover {
  width: 20px; height: 20px;
  margin: -10px 0 0 -10px;
}

@media (pointer: coarse) {
  .cursor { display: none; }
}


/* ==========================================================================
   20. Preferences de mouvement / impression
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    transition-delay: 0ms !important;
    scroll-behavior: auto !important;
  }
  .appear, .reveal, .reveal-block, .blur-in, .text-reveal,
  .reveal-words > span, .reveal-stagger > *, .reveal-words-auto .w,
  .techstack__logos .tool {
    opacity: 1 !important;
    filter: none !important;
    transform: none !important;
  }
  .project-fly { transform: none !important; opacity: 1 !important; }
  .ticker__track { animation: none; }
  .cursor { display: none; }
}

@media print {
  .nav-wrap, .contact-dock, .cursor, .footer__wordmark, .cpanel { display: none; }
  /* Le trace « plan d'architecte » est purement decoratif : inutile a l'encre. */
  .hero::before, .hero::after,
  main > section::before, main > section::after,
  .footer::before, .footer::after { display: none; }
  body { background: #fff; color: #000; }
}


