index.html 41 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228
  1. <!DOCTYPE html>
  2. <html lang="fr">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <meta name="description" content="Bastien Chanot — Développeur confirmé · Systèmes & Backend. 7 ans en C, Rust, Linux kernel, AOSP, embarqué.">
  7. <meta name="author" content="Bastien Chanot">
  8. <meta name="theme-color" content="#0d1b12">
  9. <title>Bastien Chanot — Développeur Systèmes & Backend</title>
  10. <link rel="preconnect" href="https://fonts.googleapis.com">
  11. <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
  12. <link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&family=Fraunces:ital,wght@0,300;0,500;0,600;0,700;1,400&family=DM+Sans:wght@300;400;500;600&display=swap" rel="stylesheet">
  13. <style>
  14. :root {
  15. /* Palette — non négociable */
  16. --dark: #0d1b12;
  17. --dark-mid: #183325;
  18. --g900: #0e3320;
  19. --g700: #1b5e3b;
  20. --g500: #2d7a4f;
  21. --g300: #6ab98a;
  22. --g100: #dff0e7;
  23. --g050: #eef7f1;
  24. --ink-1: #111111;
  25. --ink-2: #1e1e1e;
  26. --ink-3: #636363;
  27. --page: #f5f3ec;
  28. --rule: #d8d4c8;
  29. --tag: #e6e2d8;
  30. /* Typographies */
  31. --mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, monospace;
  32. --serif: 'Fraunces', Georgia, serif;
  33. --sans: 'DM Sans', system-ui, -apple-system, sans-serif;
  34. /* Échelle */
  35. --r-sm: 4px;
  36. --r-md: 6px;
  37. --r-pill: 999px;
  38. --shadow-sm: 0 1px 2px rgba(13, 27, 18, 0.06);
  39. --shadow-md: 0 6px 24px rgba(13, 27, 18, 0.08);
  40. --shadow-lg: 0 20px 60px rgba(13, 27, 18, 0.12);
  41. --nav-h: 64px;
  42. --max-w: 1100px;
  43. }
  44. *, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
  45. html { scroll-behavior: smooth; scroll-padding-top: calc(var(--nav-h) + 16px); }
  46. body {
  47. background: var(--page);
  48. color: var(--ink-2);
  49. font-family: var(--sans);
  50. font-size: 16px;
  51. line-height: 1.6;
  52. -webkit-font-smoothing: antialiased;
  53. -moz-osx-font-smoothing: grayscale;
  54. overflow-x: hidden;
  55. }
  56. ::selection { background: var(--g300); color: var(--dark); }
  57. a { color: var(--g700); text-decoration: none; transition: color .18s ease; }
  58. a:hover { color: var(--g500); }
  59. a:focus-visible { outline: 2px solid var(--g500); outline-offset: 3px; border-radius: 2px; }
  60. button, .btn { font-family: inherit; cursor: pointer; }
  61. /* ── NAV ── */
  62. .nav {
  63. position: fixed;
  64. top: 0; left: 0; right: 0;
  65. height: var(--nav-h);
  66. background: rgba(13, 27, 18, 0.92);
  67. backdrop-filter: saturate(140%) blur(10px);
  68. -webkit-backdrop-filter: saturate(140%) blur(10px);
  69. border-bottom: 1px solid rgba(106, 185, 138, 0.12);
  70. z-index: 50;
  71. }
  72. .nav-inner {
  73. max-width: var(--max-w);
  74. height: 100%;
  75. margin: 0 auto;
  76. padding: 0 24px;
  77. display: flex;
  78. align-items: center;
  79. justify-content: space-between;
  80. gap: 24px;
  81. }
  82. .brand {
  83. font-family: var(--mono);
  84. font-size: 14px;
  85. font-weight: 600;
  86. color: var(--g100);
  87. letter-spacing: 0.5px;
  88. display: inline-flex;
  89. align-items: center;
  90. gap: 8px;
  91. }
  92. .brand::before {
  93. content: "";
  94. width: 8px; height: 8px;
  95. border-radius: 50%;
  96. background: var(--g300);
  97. box-shadow: 0 0 0 0 rgba(106,185,138,.7);
  98. animation: pulse 2.4s infinite;
  99. }
  100. @keyframes pulse {
  101. 0% { box-shadow: 0 0 0 0 rgba(106,185,138,.5); }
  102. 70% { box-shadow: 0 0 0 10px rgba(106,185,138,0); }
  103. 100% { box-shadow: 0 0 0 0 rgba(106,185,138,0); }
  104. }
  105. .nav-links { display: flex; gap: 4px; list-style: none; }
  106. .nav-links a {
  107. font-family: var(--mono);
  108. font-size: 13px;
  109. color: rgba(223, 240, 231, 0.75);
  110. padding: 8px 12px;
  111. border-radius: var(--r-sm);
  112. transition: color .18s ease, background .18s ease;
  113. }
  114. .nav-links a:hover { color: var(--g100); background: rgba(106,185,138,0.08); }
  115. .nav-toggle { display: none; }
  116. @media (max-width: 768px) {
  117. .nav-links {
  118. position: absolute;
  119. top: var(--nav-h); left: 0; right: 0;
  120. flex-direction: column;
  121. gap: 0;
  122. background: var(--dark);
  123. border-bottom: 1px solid rgba(106,185,138,0.12);
  124. padding: 8px 0;
  125. transform: translateY(-200%);
  126. transition: transform .25s ease;
  127. }
  128. .nav-links.open { transform: translateY(0); }
  129. .nav-links a { padding: 14px 24px; border-radius: 0; }
  130. .nav-toggle {
  131. display: inline-flex;
  132. align-items: center;
  133. justify-content: center;
  134. width: 40px; height: 40px;
  135. margin-left: auto;
  136. background: transparent;
  137. border: 1px solid rgba(106,185,138,0.25);
  138. border-radius: var(--r-sm);
  139. color: var(--g100);
  140. }
  141. .nav-toggle svg { width: 18px; height: 18px; }
  142. }
  143. /* ── LAYOUT ── */
  144. main { padding-top: var(--nav-h); }
  145. section { padding: 96px 24px; }
  146. .container { max-width: var(--max-w); margin: 0 auto; }
  147. .section-label {
  148. display: inline-flex;
  149. align-items: center;
  150. gap: 10px;
  151. font-family: var(--mono);
  152. font-size: 12px;
  153. font-weight: 600;
  154. color: var(--g500);
  155. letter-spacing: 0.12em;
  156. text-transform: uppercase;
  157. margin-bottom: 18px;
  158. }
  159. .section-label::before {
  160. content: "";
  161. width: 24px; height: 1px; background: var(--g500);
  162. }
  163. .section-dark .section-label { color: var(--g300); }
  164. .section-dark .section-label::before { background: var(--g300); }
  165. h2.section-title {
  166. font-family: var(--serif);
  167. font-weight: 600;
  168. font-size: clamp(28px, 4vw, 40px);
  169. line-height: 1.1;
  170. color: var(--ink-1);
  171. letter-spacing: -0.02em;
  172. margin-bottom: 20px;
  173. }
  174. .section-dark h2.section-title { color: #fff; }
  175. .section-intro {
  176. font-family: var(--sans);
  177. font-size: 17px;
  178. color: var(--ink-2);
  179. max-width: 64ch;
  180. }
  181. .section-dark .section-intro { color: rgba(223, 240, 231, 0.85); }
  182. /* ── HERO ── */
  183. .hero {
  184. position: relative;
  185. min-height: calc(100vh - var(--nav-h));
  186. padding: 80px 24px 96px;
  187. display: flex;
  188. align-items: center;
  189. overflow: hidden;
  190. background:
  191. radial-gradient(ellipse 80% 60% at 80% 0%, rgba(45,122,79,0.10) 0%, transparent 60%),
  192. radial-gradient(ellipse 60% 50% at 0% 100%, rgba(27,94,59,0.08) 0%, transparent 55%),
  193. var(--page);
  194. }
  195. .hero-inner {
  196. max-width: var(--max-w);
  197. width: 100%;
  198. margin: 0 auto;
  199. display: grid;
  200. grid-template-columns: 1fr;
  201. gap: 28px;
  202. position: relative;
  203. }
  204. .hero-eyebrow {
  205. font-family: var(--mono);
  206. font-size: 13px;
  207. color: var(--g700);
  208. letter-spacing: 0.08em;
  209. display: inline-flex;
  210. align-items: center;
  211. gap: 10px;
  212. }
  213. .hero-eyebrow::before {
  214. content: "▍";
  215. color: var(--g500);
  216. font-weight: 700;
  217. }
  218. .hero-name {
  219. font-family: var(--serif);
  220. font-size: clamp(48px, 8vw, 96px);
  221. font-weight: 600;
  222. line-height: 0.95;
  223. letter-spacing: -0.04em;
  224. color: var(--ink-1);
  225. }
  226. .hero-name em {
  227. font-style: italic;
  228. font-weight: 400;
  229. color: var(--g700);
  230. }
  231. .hero-title {
  232. font-family: var(--mono);
  233. font-size: clamp(14px, 1.6vw, 17px);
  234. color: var(--ink-2);
  235. font-weight: 500;
  236. letter-spacing: 0.02em;
  237. }
  238. .hero-title .sep { color: var(--g500); margin: 0 8px; }
  239. .hero-tagline {
  240. font-family: var(--serif);
  241. font-style: italic;
  242. font-weight: 300;
  243. font-size: clamp(20px, 2.6vw, 28px);
  244. line-height: 1.35;
  245. color: var(--ink-2);
  246. max-width: 56ch;
  247. border-left: 3px solid var(--g500);
  248. padding-left: 20px;
  249. }
  250. .hero-cta {
  251. display: flex;
  252. gap: 14px;
  253. flex-wrap: wrap;
  254. margin-top: 12px;
  255. }
  256. .btn {
  257. display: inline-flex;
  258. align-items: center;
  259. gap: 10px;
  260. font-family: var(--mono);
  261. font-size: 14px;
  262. font-weight: 600;
  263. letter-spacing: 0.02em;
  264. padding: 14px 22px;
  265. border-radius: var(--r-md);
  266. border: 1px solid transparent;
  267. transition: transform .18s ease, background .18s ease, color .18s ease, border-color .18s ease, box-shadow .18s ease;
  268. text-decoration: none;
  269. }
  270. .btn-primary {
  271. background: var(--dark);
  272. color: var(--g100);
  273. border-color: var(--dark);
  274. box-shadow: var(--shadow-sm);
  275. }
  276. .btn-primary:hover {
  277. background: var(--g700);
  278. border-color: var(--g700);
  279. color: #fff;
  280. transform: translateY(-1px);
  281. box-shadow: var(--shadow-md);
  282. }
  283. .btn-secondary {
  284. background: transparent;
  285. color: var(--dark);
  286. border-color: var(--rule);
  287. }
  288. .btn-secondary:hover {
  289. border-color: var(--g500);
  290. color: var(--g700);
  291. background: var(--g050);
  292. transform: translateY(-1px);
  293. }
  294. .btn .arrow {
  295. width: 14px; height: 14px;
  296. transition: transform .18s ease;
  297. }
  298. .btn:hover .arrow { transform: translate(2px, -2px); }
  299. .hero-meta {
  300. display: flex;
  301. flex-wrap: wrap;
  302. gap: 24px;
  303. margin-top: 16px;
  304. font-family: var(--mono);
  305. font-size: 12px;
  306. color: var(--ink-3);
  307. letter-spacing: 0.04em;
  308. }
  309. .hero-meta span { display: inline-flex; align-items: center; gap: 8px; }
  310. .hero-meta .dot {
  311. display: inline-block;
  312. width: 6px; height: 6px;
  313. border-radius: 50%;
  314. background: var(--g500);
  315. }
  316. /* Stagger entrance */
  317. .reveal { opacity: 0; transform: translateY(14px); animation: rise .8s cubic-bezier(.2,.7,.2,1) forwards; }
  318. .reveal.d1 { animation-delay: .08s; }
  319. .reveal.d2 { animation-delay: .18s; }
  320. .reveal.d3 { animation-delay: .30s; }
  321. .reveal.d4 { animation-delay: .42s; }
  322. .reveal.d5 { animation-delay: .55s; }
  323. .reveal.d6 { animation-delay: .68s; }
  324. @keyframes rise {
  325. to { opacity: 1; transform: translateY(0); }
  326. }
  327. @media (prefers-reduced-motion: reduce) {
  328. .reveal { opacity: 1; transform: none; animation: none; }
  329. .brand::before { animation: none; }
  330. html { scroll-behavior: auto; }
  331. }
  332. /* ── ABOUT ── */
  333. .about {
  334. background: var(--dark);
  335. background-image:
  336. radial-gradient(ellipse 60% 50% at 100% 0%, rgba(45,122,79,0.16) 0%, transparent 55%),
  337. radial-gradient(ellipse 50% 40% at 0% 100%, rgba(27,94,59,0.12) 0%, transparent 60%);
  338. color: #fff;
  339. position: relative;
  340. }
  341. .about::before {
  342. content: ""; position: absolute; left: 0; top: 0; bottom: 0;
  343. width: 3px;
  344. background: linear-gradient(to bottom, var(--g300), var(--g700));
  345. }
  346. .about-grid {
  347. display: grid;
  348. grid-template-columns: 1fr;
  349. gap: 32px;
  350. align-items: start;
  351. }
  352. .about-text p {
  353. font-size: 18px;
  354. line-height: 1.65;
  355. color: rgba(223, 240, 231, 0.92);
  356. margin-bottom: 16px;
  357. max-width: 62ch;
  358. }
  359. .about-text strong { color: var(--g300); font-weight: 500; }
  360. .about-callout {
  361. border: 1px solid rgba(106,185,138,0.25);
  362. border-radius: var(--r-md);
  363. padding: 20px 22px;
  364. background: rgba(13, 27, 18, 0.4);
  365. }
  366. .about-callout dt {
  367. font-family: var(--mono);
  368. font-size: 11px;
  369. color: var(--g300);
  370. letter-spacing: 0.12em;
  371. text-transform: uppercase;
  372. margin-bottom: 6px;
  373. }
  374. .about-callout dd {
  375. font-family: var(--sans);
  376. font-size: 15px;
  377. color: var(--g100);
  378. margin-bottom: 14px;
  379. }
  380. .about-callout dd:last-child { margin-bottom: 0; }
  381. .about-callout a { color: var(--g300); }
  382. .about-callout a:hover { color: var(--g100); }
  383. @media (min-width: 768px) {
  384. .about-grid { grid-template-columns: 1.6fr 1fr; gap: 56px; }
  385. }
  386. /* ── STACK ── */
  387. .stack { background: var(--page); }
  388. .stack-grid {
  389. display: grid;
  390. grid-template-columns: 1fr;
  391. gap: 20px;
  392. margin-top: 40px;
  393. }
  394. .stack-card {
  395. background: #fff;
  396. border: 1px solid var(--rule);
  397. border-radius: var(--r-md);
  398. padding: 24px;
  399. transition: border-color .25s ease, transform .25s ease, box-shadow .25s ease;
  400. }
  401. .stack-card:hover {
  402. border-color: var(--g300);
  403. transform: translateY(-2px);
  404. box-shadow: var(--shadow-md);
  405. }
  406. .stack-card-head {
  407. display: flex;
  408. align-items: baseline;
  409. justify-content: space-between;
  410. margin-bottom: 16px;
  411. padding-bottom: 12px;
  412. border-bottom: 1px dashed var(--rule);
  413. }
  414. .stack-card h3 {
  415. font-family: var(--serif);
  416. font-weight: 600;
  417. font-size: 19px;
  418. color: var(--ink-1);
  419. letter-spacing: -0.01em;
  420. }
  421. .stack-card-tag {
  422. font-family: var(--mono);
  423. font-size: 11px;
  424. color: var(--g500);
  425. letter-spacing: 0.1em;
  426. }
  427. .pills {
  428. display: flex;
  429. flex-wrap: wrap;
  430. gap: 8px;
  431. list-style: none;
  432. }
  433. .pill {
  434. display: inline-block;
  435. font-family: var(--mono);
  436. font-size: 12px;
  437. font-weight: 500;
  438. color: var(--g900);
  439. background: var(--g050);
  440. border: 1px solid var(--g100);
  441. padding: 6px 12px;
  442. border-radius: var(--r-pill);
  443. transition: background .18s ease, border-color .18s ease, color .18s ease;
  444. }
  445. .pill:hover {
  446. background: var(--g100);
  447. border-color: var(--g300);
  448. color: var(--g700);
  449. }
  450. @media (min-width: 768px) { .stack-grid { grid-template-columns: repeat(2, 1fr); } }
  451. @media (min-width: 1200px) { .stack-grid { grid-template-columns: repeat(3, 1fr); } }
  452. /* ── EXPERIENCE ── */
  453. .experience { background: var(--g050); }
  454. .timeline {
  455. position: relative;
  456. margin-top: 40px;
  457. padding-left: 28px;
  458. border-left: 2px solid var(--g100);
  459. }
  460. .timeline-item {
  461. position: relative;
  462. padding: 0 0 36px 24px;
  463. }
  464. .timeline-item:last-child { padding-bottom: 0; }
  465. .timeline-item::before {
  466. content: "";
  467. position: absolute;
  468. left: -34px; top: 6px;
  469. width: 12px; height: 12px;
  470. border-radius: 50%;
  471. background: var(--page);
  472. border: 2px solid var(--g500);
  473. box-shadow: 0 0 0 4px var(--g050);
  474. }
  475. .timeline-item.current::before {
  476. background: var(--g500);
  477. box-shadow: 0 0 0 4px var(--g050), 0 0 0 8px rgba(45,122,79,0.18);
  478. }
  479. .timeline-meta {
  480. display: flex;
  481. flex-wrap: wrap;
  482. align-items: baseline;
  483. gap: 12px;
  484. margin-bottom: 8px;
  485. font-family: var(--mono);
  486. font-size: 12px;
  487. color: var(--ink-3);
  488. letter-spacing: 0.04em;
  489. }
  490. .timeline-meta .period {
  491. color: var(--g700);
  492. font-weight: 600;
  493. }
  494. .timeline-meta .badge {
  495. display: inline-block;
  496. background: var(--g100);
  497. color: var(--g700);
  498. padding: 2px 8px;
  499. border-radius: var(--r-pill);
  500. font-size: 10px;
  501. font-weight: 600;
  502. letter-spacing: 0.08em;
  503. text-transform: uppercase;
  504. }
  505. .timeline-item h3 {
  506. font-family: var(--serif);
  507. font-weight: 600;
  508. font-size: 22px;
  509. color: var(--ink-1);
  510. margin-bottom: 4px;
  511. letter-spacing: -0.01em;
  512. }
  513. .timeline-item h3 a {
  514. color: inherit;
  515. border-bottom: 1px solid transparent;
  516. transition: border-color .18s ease, color .18s ease;
  517. }
  518. .timeline-item h3 a:hover { color: var(--g700); border-color: var(--g500); }
  519. .timeline-role {
  520. font-family: var(--mono);
  521. font-size: 13px;
  522. color: var(--g700);
  523. margin-bottom: 12px;
  524. }
  525. .timeline-desc {
  526. color: var(--ink-2);
  527. line-height: 1.6;
  528. max-width: 64ch;
  529. }
  530. /* ── FORMATION ── */
  531. .formation { background: var(--page); }
  532. .formation .timeline { border-left-color: var(--g100); }
  533. .formation .timeline-item::before { box-shadow: 0 0 0 4px var(--page); }
  534. .formation-school-desc {
  535. font-family: var(--serif);
  536. font-style: italic;
  537. font-weight: 300;
  538. color: var(--ink-2);
  539. line-height: 1.55;
  540. max-width: 64ch;
  541. margin-bottom: 20px;
  542. }
  543. .formation-themes {
  544. display: grid;
  545. grid-template-columns: 1fr;
  546. gap: 18px;
  547. margin-top: 24px;
  548. }
  549. @media (min-width: 768px) { .formation-themes { grid-template-columns: repeat(2, 1fr); } }
  550. @media (min-width: 1200px) { .formation-themes { grid-template-columns: repeat(3, 1fr); } }
  551. .theme-card {
  552. background: #fff;
  553. border: 1px solid var(--rule);
  554. border-radius: var(--r-md);
  555. padding: 22px;
  556. transition: border-color .25s ease, transform .25s ease, box-shadow .25s ease;
  557. display: flex;
  558. flex-direction: column;
  559. }
  560. .theme-card:hover {
  561. border-color: var(--g300);
  562. transform: translateY(-2px);
  563. box-shadow: var(--shadow-md);
  564. }
  565. .theme-card-head {
  566. display: flex;
  567. align-items: baseline;
  568. justify-content: space-between;
  569. gap: 12px;
  570. margin-bottom: 12px;
  571. padding-bottom: 10px;
  572. border-bottom: 1px dashed var(--rule);
  573. }
  574. .theme-card h4 {
  575. font-family: var(--serif);
  576. font-weight: 600;
  577. font-size: 18px;
  578. color: var(--ink-1);
  579. letter-spacing: -0.01em;
  580. line-height: 1.2;
  581. }
  582. .theme-card-tag {
  583. font-family: var(--mono);
  584. font-size: 11px;
  585. color: var(--g500);
  586. letter-spacing: 0.1em;
  587. flex-shrink: 0;
  588. }
  589. .theme-quote {
  590. font-family: var(--serif);
  591. font-style: italic;
  592. font-weight: 300;
  593. color: var(--ink-2);
  594. font-size: 14px;
  595. line-height: 1.5;
  596. padding-left: 12px;
  597. border-left: 2px solid var(--g500);
  598. margin-bottom: 16px;
  599. }
  600. .theme-list {
  601. list-style: none;
  602. display: flex;
  603. flex-direction: column;
  604. gap: 10px;
  605. }
  606. .theme-list li {
  607. font-size: 13.5px;
  608. line-height: 1.55;
  609. color: var(--ink-2);
  610. }
  611. .theme-list code {
  612. font-family: var(--mono);
  613. font-size: 12px;
  614. font-weight: 500;
  615. color: var(--g700);
  616. background: var(--g050);
  617. border: 1px solid var(--g100);
  618. padding: 1px 7px;
  619. border-radius: var(--r-sm);
  620. }
  621. .formation-tsrit-list {
  622. list-style: none;
  623. display: flex;
  624. flex-direction: column;
  625. gap: 10px;
  626. margin-top: 12px;
  627. max-width: 64ch;
  628. }
  629. .formation-tsrit-list li {
  630. color: var(--ink-2);
  631. line-height: 1.55;
  632. padding-left: 18px;
  633. position: relative;
  634. }
  635. .formation-tsrit-list li::before {
  636. content: "";
  637. position: absolute;
  638. left: 0; top: 0.65em;
  639. width: 8px; height: 1px;
  640. background: var(--g500);
  641. }
  642. .formation-tsrit-list li strong { color: var(--ink-1); font-weight: 600; }
  643. .honors {
  644. display: inline-flex;
  645. align-items: center;
  646. gap: 8px;
  647. font-family: var(--mono);
  648. font-size: 12px;
  649. font-weight: 600;
  650. color: var(--g700);
  651. background: var(--g100);
  652. border: 1px solid var(--g300);
  653. padding: 6px 14px;
  654. border-radius: var(--r-pill);
  655. letter-spacing: 0.04em;
  656. margin: 4px 0 14px;
  657. }
  658. .honors::before {
  659. content: "★";
  660. color: var(--g500);
  661. font-size: 13px;
  662. }
  663. /* ── CONTACT ── */
  664. .contact {
  665. background: var(--dark);
  666. color: #fff;
  667. position: relative;
  668. overflow: hidden;
  669. }
  670. .contact::after {
  671. content: "";
  672. position: absolute;
  673. right: -120px; bottom: -120px;
  674. width: 360px; height: 360px;
  675. border-radius: 50%;
  676. background: radial-gradient(circle, rgba(45,122,79,0.18) 0%, transparent 70%);
  677. pointer-events: none;
  678. }
  679. .contact-grid {
  680. display: grid;
  681. grid-template-columns: 1fr;
  682. gap: 32px;
  683. position: relative;
  684. z-index: 1;
  685. }
  686. .contact-list {
  687. list-style: none;
  688. display: grid;
  689. grid-template-columns: 1fr;
  690. gap: 12px;
  691. margin-top: 32px;
  692. }
  693. .contact-row {
  694. display: grid;
  695. grid-template-columns: 28px 1fr;
  696. gap: 18px;
  697. align-items: center;
  698. padding: 18px 20px;
  699. background: rgba(255, 255, 255, 0.04);
  700. border: 1px solid rgba(106,185,138,0.18);
  701. border-radius: var(--r-md);
  702. transition: border-color .18s ease, background .18s ease, transform .18s ease;
  703. color: var(--g100);
  704. }
  705. .contact-row:hover {
  706. border-color: var(--g300);
  707. background: rgba(106,185,138,0.08);
  708. transform: translateY(-1px);
  709. color: #fff;
  710. }
  711. .contact-row svg { width: 20px; height: 20px; color: var(--g300); flex-shrink: 0; }
  712. .contact-row .label {
  713. display: block;
  714. font-family: var(--mono);
  715. font-size: 11px;
  716. color: var(--g300);
  717. letter-spacing: 0.12em;
  718. text-transform: uppercase;
  719. margin-bottom: 2px;
  720. }
  721. .contact-row .value {
  722. font-family: var(--mono);
  723. font-size: 15px;
  724. color: #fff;
  725. font-weight: 500;
  726. word-break: break-word;
  727. }
  728. @media (min-width: 768px) {
  729. .contact-list { grid-template-columns: repeat(2, 1fr); }
  730. }
  731. /* ── FOOTER ── */
  732. .footer {
  733. background: #061008;
  734. color: rgba(223, 240, 231, 0.55);
  735. padding: 32px 24px;
  736. border-top: 1px solid rgba(106,185,138,0.1);
  737. }
  738. .footer-inner {
  739. max-width: var(--max-w);
  740. margin: 0 auto;
  741. display: flex;
  742. flex-wrap: wrap;
  743. gap: 16px 32px;
  744. justify-content: space-between;
  745. align-items: center;
  746. font-family: var(--mono);
  747. font-size: 12px;
  748. letter-spacing: 0.04em;
  749. }
  750. .footer a { color: var(--g300); }
  751. .footer a:hover { color: var(--g100); }
  752. .footer-links { display: flex; gap: 20px; flex-wrap: wrap; }
  753. /* ── RESPONSIVE TWEAKS ── */
  754. @media (max-width: 768px) {
  755. section { padding: 72px 20px; }
  756. .hero { padding: 56px 20px 72px; }
  757. .hero-cta .btn { flex: 1 1 auto; justify-content: center; }
  758. .timeline { padding-left: 22px; }
  759. .timeline-item { padding-left: 18px; }
  760. .timeline-item::before { left: -29px; }
  761. }
  762. @media (min-width: 1200px) {
  763. section { padding: 120px 24px; }
  764. }
  765. </style>
  766. </head>
  767. <body>
  768. <!-- NAV -->
  769. <header class="nav" role="banner">
  770. <div class="nav-inner">
  771. <a href="#hero" class="brand" aria-label="Bastien Chanot — accueil">bchanot.fr</a>
  772. <button class="nav-toggle" type="button" aria-label="Ouvrir le menu" aria-expanded="false" aria-controls="primary-nav">
  773. <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/></svg>
  774. </button>
  775. <nav aria-label="Navigation principale">
  776. <ul id="primary-nav" class="nav-links">
  777. <li><a href="#about">À propos</a></li>
  778. <li><a href="#stack">Stack</a></li>
  779. <li><a href="#experience">Parcours</a></li>
  780. <li><a href="#formation">Formation</a></li>
  781. <li><a href="#contact">Contact</a></li>
  782. </ul>
  783. </nav>
  784. </div>
  785. </header>
  786. <main>
  787. <!-- HERO -->
  788. <section id="hero" class="hero" aria-labelledby="hero-name">
  789. <div class="hero-inner">
  790. <p class="hero-eyebrow reveal">Disponible — CDI systèmes&nbsp;/&nbsp;embarqué · missions freelance</p>
  791. <h1 id="hero-name" class="hero-name reveal d1">Bastien&nbsp;<em>Chanot</em></h1>
  792. <p class="hero-title reveal d2">Développeur confirmé<span class="sep">·</span>Systèmes &amp; Backend</p>
  793. <p class="hero-tagline reveal d3">Du kernel Linux au backend Rust — 7 ans de développement systèmes en production.</p>
  794. <div class="hero-cta reveal d4">
  795. <a class="btn btn-primary" href="#contact">
  796. Me contacter
  797. <svg class="arrow" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><line x1="5" y1="19" x2="19" y2="5"/><polyline points="8 5 19 5 19 16"/></svg>
  798. </a>
  799. <a class="btn btn-secondary" href="CV_Bastien_Chanot.html">
  800. Voir le CV
  801. <svg class="arrow" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><line x1="5" y1="19" x2="19" y2="5"/><polyline points="8 5 19 5 19 16"/></svg>
  802. </a>
  803. <a class="btn btn-secondary" href="CV_Bastien_Chanot.pdf" download>
  804. Télécharger PDF
  805. <svg class="arrow" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
  806. </a>
  807. </div>
  808. <div class="hero-meta reveal d5">
  809. <span><span class="dot"></span>C · Rust · Linux Kernel</span>
  810. <span><span class="dot"></span>AOSP · Embarqué</span>
  811. <span><span class="dot"></span>Backend · DevOps</span>
  812. </div>
  813. </div>
  814. </section>
  815. <!-- ABOUT -->
  816. <section id="about" class="about section-dark" aria-labelledby="about-title">
  817. <div class="container">
  818. <span class="section-label">À propos</span>
  819. <h2 id="about-title" class="section-title">Sept ans à écrire du code qui tourne là où ça compte.</h2>
  820. <div class="about-grid">
  821. <div class="about-text">
  822. <p>Je suis développeur systèmes confirmé, formé à l'<strong>École&nbsp;42</strong> et passé par six ans chez <strong>CareGame</strong> où j'ai écrit des drivers Linux kernel, un backend Rust temps réel et fait tourner des serveurs GPU bare-metal en production.</p>
  823. <p>Mon terrain de jeu : <strong>C, Rust, Linux kernel, AOSP, embarqué, infrastructure</strong>. Quand un problème touche au bas niveau — port matériel, latence, sécurité, kernel — c'est là que j'apporte le plus de valeur.</p>
  824. <p>Aujourd'hui indépendant sous la marque <strong>ZenQuality</strong>, mais avant tout en recherche d'un <strong>CDI en systèmes embarqués ou logiciel</strong> — les missions freelance se font en parallèle.</p>
  825. <p>Côté présence : <strong>full remote</strong> idéalement, ou <strong>hybride 1 à 2 jours par mois</strong> si l'équipe est à Paris. Mobilité visée à moyen terme : <strong>Pays de la Loire</strong>.</p>
  826. </div>
  827. <dl class="about-callout">
  828. <dt>Recherche prioritaire</dt>
  829. <dd>CDI systèmes embarqués / logiciel</dd>
  830. <dt>En parallèle</dt>
  831. <dd>Missions freelance · ZenQuality</dd>
  832. <dt>Localisation actuelle</dt>
  833. <dd>Yerres (91) · mobilité Pays de la Loire</dd>
  834. <dt>Présence</dt>
  835. <dd>Full remote · ou 1–2 j/mois si Paris</dd>
  836. <dt>Site pro</dt>
  837. <dd><a href="https://zenquality.fr" target="_blank" rel="noopener">zenquality.fr&nbsp;↗</a></dd>
  838. </dl>
  839. </div>
  840. </div>
  841. </section>
  842. <!-- STACK -->
  843. <section id="stack" class="stack" aria-labelledby="stack-title">
  844. <div class="container">
  845. <span class="section-label">Stack technique</span>
  846. <h2 id="stack-title" class="section-title">Ce avec quoi je travaille, sans le marketing.</h2>
  847. <p class="section-intro">Outils éprouvés, choisis pour leurs garanties — pas pour leur hype. Tout ce qui suit est en production ou l'a été.</p>
  848. <div class="stack-grid">
  849. <article class="stack-card">
  850. <header class="stack-card-head">
  851. <h3>Langages</h3>
  852. <span class="stack-card-tag">// 01</span>
  853. </header>
  854. <ul class="pills">
  855. <li class="pill">C</li>
  856. <li class="pill">Rust</li>
  857. <li class="pill">Java</li>
  858. <li class="pill">Bash</li>
  859. <li class="pill">Python</li>
  860. </ul>
  861. </article>
  862. <article class="stack-card">
  863. <header class="stack-card-head">
  864. <h3>Embarqué</h3>
  865. <span class="stack-card-tag">// 02</span>
  866. </header>
  867. <ul class="pills">
  868. <li class="pill">Linux kernel drivers</li>
  869. <li class="pill">AOSP</li>
  870. <li class="pill">ARM / x86</li>
  871. <li class="pill">GPIO</li>
  872. <li class="pill">NFC</li>
  873. <li class="pill">ESC/POS</li>
  874. <li class="pill">cross-compilation GCC</li>
  875. </ul>
  876. </article>
  877. <article class="stack-card">
  878. <header class="stack-card-head">
  879. <h3>Conteneurs</h3>
  880. <span class="stack-card-tag">// 03</span>
  881. </header>
  882. <ul class="pills">
  883. <li class="pill">Docker</li>
  884. <li class="pill">LXC / LXD</li>
  885. <li class="pill">QEMU</li>
  886. <li class="pill">VMware</li>
  887. </ul>
  888. </article>
  889. <article class="stack-card">
  890. <header class="stack-card-head">
  891. <h3>Backend</h3>
  892. <span class="stack-card-tag">// 04</span>
  893. </header>
  894. <ul class="pills">
  895. <li class="pill">Rust</li>
  896. <li class="pill">WebSocket</li>
  897. <li class="pill">GPU bare-metal</li>
  898. <li class="pill">ramdisk I/O</li>
  899. </ul>
  900. </article>
  901. <article class="stack-card">
  902. <header class="stack-card-head">
  903. <h3>Systèmes</h3>
  904. <span class="stack-card-tag">// 05</span>
  905. </header>
  906. <ul class="pills">
  907. <li class="pill">Linux bare-metal</li>
  908. <li class="pill">AOSP</li>
  909. <li class="pill">Android Backup</li>
  910. <li class="pill">systemd</li>
  911. </ul>
  912. </article>
  913. <article class="stack-card">
  914. <header class="stack-card-head">
  915. <h3>DevOps</h3>
  916. <span class="stack-card-tag">// 06</span>
  917. </header>
  918. <ul class="pills">
  919. <li class="pill">Git</li>
  920. <li class="pill">GitHub / GitLab</li>
  921. <li class="pill">CI / CD</li>
  922. <li class="pill">Gitflow</li>
  923. <li class="pill">Agile</li>
  924. </ul>
  925. </article>
  926. </div>
  927. </div>
  928. </section>
  929. <!-- EXPERIENCE -->
  930. <section id="experience" class="experience" aria-labelledby="experience-title">
  931. <div class="container">
  932. <span class="section-label">Parcours</span>
  933. <h2 id="experience-title" class="section-title">Trois expériences qui résument l'essentiel.</h2>
  934. <p class="section-intro">Sept ans de développement systèmes en production — du kernel au backend, de la puce au serveur.</p>
  935. <ol class="timeline">
  936. <li class="timeline-item current">
  937. <div class="timeline-meta">
  938. <span class="period">avr.&nbsp;2026 — présent</span>
  939. <span class="badge">En cours</span>
  940. <span>Yerres · Full remote</span>
  941. </div>
  942. <h3><a href="https://zenquality.fr" target="_blank" rel="noopener">ZenQuality</a></h3>
  943. <p class="timeline-role">Développeur indépendant · Systèmes &amp; Backend</p>
  944. <p class="timeline-desc">Développement système et embarqué en indépendant. Interventions sur Linux, Rust, kernel et infrastructure critique. Premier contrat : audit SEO et conformité légale pour un client e-commerce (portfolio en construction).</p>
  945. </li>
  946. <li class="timeline-item">
  947. <div class="timeline-meta">
  948. <span class="period">mars&nbsp;2019 — mars&nbsp;2025</span>
  949. <span>Paris · Full remote dès 2020</span>
  950. </div>
  951. <h3>CareGame</h3>
  952. <p class="timeline-role">Développeur systèmes &amp; backend</p>
  953. <p class="timeline-desc">Seul responsable de la couche kernel, AOSP et serveur de jeu pendant 6 ans. Développement de drivers Linux en C (x86/ARM), backend Rust (~2 000 lignes, WebSocket), virtual touchscreen/gamepad AOSP en Java. Architecture de serveurs GPU bare-metal (8× GPU T4, 64 vCPU) — isolation CPU/GPU par session, I/O sur ramdisk, 32 sessions jeux AAA simultanées. Intégration d'un système LXC/LXD issu d'une R&amp;D Nvidia abandonnée — résultats reconnus par leurs équipes. Collaboration directe avec Canonical (Anbox, accès pre-commercial) et Ampere Computing (serveurs ARM pre-launch).</p>
  954. </li>
  955. <li class="timeline-item">
  956. <div class="timeline-meta">
  957. <span class="period">2017</span>
  958. <span>Ivry-sur-Seine</span>
  959. </div>
  960. <h3>Deewee</h3>
  961. <p class="timeline-role">Développeur C embarqué</p>
  962. <p class="timeline-desc">Système embarqué complet développé seul en C sur Orange Pi (Debian ARM) : interception du flux ESC/POS d'une imprimante thermique, génération PNG du ticket, déclenchement GPIO sur bouton physique, création d'un hotspot WiFi embarqué avec diffusion des credentials via NFC, envoi de l'image en WiFi direct vers une application mobile tierce.</p>
  963. </li>
  964. </ol>
  965. </div>
  966. </section>
  967. <!-- FORMATION -->
  968. <section id="formation" class="formation" aria-labelledby="formation-title">
  969. <div class="container">
  970. <span class="section-label">Formation</span>
  971. <h2 id="formation-title" class="section-title">Formation</h2>
  972. <p class="section-intro"><em>Le socle technique derrière sept ans de production.</em></p>
  973. <ol class="timeline">
  974. <li class="timeline-item">
  975. <div class="timeline-meta">
  976. <span class="period">2015 — 2019</span>
  977. <span>Clichy</span>
  978. </div>
  979. <h3>École 42</h3>
  980. <p class="timeline-role">Programmation par projets · sans cours, sans notes</p>
  981. <p class="formation-school-desc">Uniquement du code qui passe ou qui ne passe pas.</p>
  982. <div class="formation-themes">
  983. <article class="theme-card">
  984. <header class="theme-card-head">
  985. <h4>Systèmes &amp; Kernel</h4>
  986. <span class="theme-card-tag">// 01</span>
  987. </header>
  988. <p class="theme-quote">Bootstrap d'OS, drivers, gestion matérielle, allocation mémoire.</p>
  989. <ul class="theme-list">
  990. <li><code>ft_linux</code> &amp; <code>kfs-1</code> — Linux From Scratch et noyau minimaliste : bootloader ASM, GDT, interruptions, driver char device clavier mappé.</li>
  991. <li><code>drivers &amp; interrupt</code> — drivers kernel Linux et gestion d'interruptions niveau noyau.</li>
  992. <li><code>process &amp; memory</code> — modèle de processus Unix, gestion mémoire kernel.</li>
  993. <li><code>little penguin</code> — contribution kernel Linux : style guide, patch submission, contrib upstream.</li>
  994. <li><code>malloc</code> — allocateur mémoire complet (libft_malloc.so) avec mmap, zones tiny / small / large.</li>
  995. </ul>
  996. </article>
  997. <article class="theme-card">
  998. <header class="theme-card-head">
  999. <h4>Bas niveau &amp; Outils système</h4>
  1000. <span class="theme-card-tag">// 02</span>
  1001. </header>
  1002. <p class="theme-quote">Recoder les outils qu'on utilise tous les jours pour comprendre vraiment ce qu'ils font.</p>
  1003. <ul class="theme-list">
  1004. <li><code>nm</code> — parsing d'exécutables ELF, tables de symboles.</li>
  1005. <li><code>42sh</code> / <code>21</code> — shell POSIX complet : parser, redirections, jobs, history, autocomplétion.</li>
  1006. <li><code>ft_ls</code> — réimplémentation <code>ls</code> avec options POSIX, permissions, tri, formats.</li>
  1007. <li><code>ft_select</code> — TUI custom avec gestion termios, signaux, redessin partiel.</li>
  1008. </ul>
  1009. </article>
  1010. <article class="theme-card">
  1011. <header class="theme-card-head">
  1012. <h4>Sécurité &amp; Algorithmie</h4>
  1013. <span class="theme-card-tag">// 03</span>
  1014. </header>
  1015. <p class="theme-quote">Comprendre comment un système peut être cassé pour savoir le sécuriser.</p>
  1016. <ul class="theme-list">
  1017. <li><code>snow crash</code> — wargame exploitation système : escalation de privilèges, stack overflow, format string, race conditions.</li>
  1018. <li><code>doctor quine</code> — programme auto-réplicatif (métaprogrammation).</li>
  1019. <li><code>ft_ssl_md5</code> — réimplémentation MD5, SHA-256, base64.</li>
  1020. <li><code>lem-in</code>, <code>push-swap</code> — algorithmique : pathfinding (Edmonds-Karp), optimisation de tri sous contrainte.</li>
  1021. </ul>
  1022. </article>
  1023. </div>
  1024. </li>
  1025. <li class="timeline-item">
  1026. <div class="timeline-meta">
  1027. <span class="period">2013 — 2015</span>
  1028. <span>Vincennes</span>
  1029. </div>
  1030. <h3>TSRIT — Next Formation</h3>
  1031. <p class="timeline-role">BTS Technicien Supérieur Réseaux Informatiques &amp; Télécoms</p>
  1032. <span class="honors">Félicitations du jury</span>
  1033. <p class="timeline-desc">Le socle réseau et infrastructure derrière les compétences systèmes.</p>
  1034. <ul class="formation-tsrit-list">
  1035. <li>Architecture réseau (<strong>OSI</strong>, <strong>TCP/IP</strong>), routage, commutation.</li>
  1036. <li>Administration <strong>Linux</strong> / <strong>Windows Server</strong>, sécurité réseau, virtualisation.</li>
  1037. <li>Stage final transformé en <strong>CDI chez CareGame</strong>.</li>
  1038. </ul>
  1039. </li>
  1040. </ol>
  1041. </div>
  1042. </section>
  1043. <!-- CONTACT -->
  1044. <section id="contact" class="contact section-dark" aria-labelledby="contact-title">
  1045. <div class="container">
  1046. <span class="section-label">Contact</span>
  1047. <h2 id="contact-title" class="section-title">Une mission, une question technique, un&nbsp;projet&nbsp;?</h2>
  1048. <p class="section-intro">Le plus simple : l'email. Réponse sous 48&nbsp;h ouvrées.</p>
  1049. <div class="contact-grid">
  1050. <ul class="contact-list">
  1051. <li>
  1052. <a class="contact-row" href="mailto:chanot.bastien@gmail.com">
  1053. <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="5" width="18" height="14" rx="2"/><polyline points="3 7 12 13 21 7"/></svg>
  1054. <span>
  1055. <span class="label">Email</span>
  1056. <span class="value">chanot.bastien@gmail.com</span>
  1057. </span>
  1058. </a>
  1059. </li>
  1060. <li>
  1061. <a class="contact-row" href="tel:+33778822297">
  1062. <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.86 19.86 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6A19.86 19.86 0 0 1 2.12 4.18 2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72c.13.96.37 1.9.72 2.8a2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45c.9.35 1.84.59 2.8.72A2 2 0 0 1 22 16.92z"/></svg>
  1063. <span>
  1064. <span class="label">Téléphone</span>
  1065. <span class="value">+33 7 78 82 22 97</span>
  1066. </span>
  1067. </a>
  1068. </li>
  1069. <li>
  1070. <a class="contact-row" href="https://zenquality.fr" target="_blank" rel="noopener">
  1071. <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>
  1072. <span>
  1073. <span class="label">Site pro</span>
  1074. <span class="value">zenquality.fr</span>
  1075. </span>
  1076. </a>
  1077. </li>
  1078. <li>
  1079. <a class="contact-row" href="CV_Bastien_Chanot.pdf" download>
  1080. <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
  1081. <span>
  1082. <span class="label">CV</span>
  1083. <span class="value">Télécharger le PDF</span>
  1084. </span>
  1085. </a>
  1086. </li>
  1087. </ul>
  1088. </div>
  1089. </div>
  1090. </section>
  1091. </main>
  1092. <!-- FOOTER -->
  1093. <footer class="footer" role="contentinfo">
  1094. <div class="footer-inner">
  1095. <span>© 2026 · Bastien Chanot · bchanot.fr</span>
  1096. <div class="footer-links">
  1097. <a href="CV_Bastien_Chanot.html">CV (HTML)</a>
  1098. <a href="CV_Bastien_Chanot.pdf" download>CV (PDF)</a>
  1099. <a href="https://zenquality.fr" target="_blank" rel="noopener">zenquality.fr ↗</a>
  1100. </div>
  1101. </div>
  1102. </footer>
  1103. <script>
  1104. // Mobile nav toggle
  1105. (function () {
  1106. const toggle = document.querySelector('.nav-toggle');
  1107. const links = document.getElementById('primary-nav');
  1108. if (!toggle || !links) return;
  1109. toggle.addEventListener('click', () => {
  1110. const open = links.classList.toggle('open');
  1111. toggle.setAttribute('aria-expanded', String(open));
  1112. toggle.setAttribute('aria-label', open ? 'Fermer le menu' : 'Ouvrir le menu');
  1113. });
  1114. links.addEventListener('click', (e) => {
  1115. if (e.target instanceof HTMLAnchorElement && links.classList.contains('open')) {
  1116. links.classList.remove('open');
  1117. toggle.setAttribute('aria-expanded', 'false');
  1118. toggle.setAttribute('aria-label', 'Ouvrir le menu');
  1119. }
  1120. });
  1121. })();
  1122. // Active section highlight in nav (subtle — color shift only)
  1123. (function () {
  1124. const sections = document.querySelectorAll('main section[id]');
  1125. const navLinks = document.querySelectorAll('.nav-links a[href^="#"]');
  1126. if (!('IntersectionObserver' in window) || !sections.length) return;
  1127. const map = new Map();
  1128. navLinks.forEach(a => {
  1129. const id = a.getAttribute('href').slice(1);
  1130. map.set(id, a);
  1131. });
  1132. const io = new IntersectionObserver((entries) => {
  1133. entries.forEach(entry => {
  1134. const link = map.get(entry.target.id);
  1135. if (!link) return;
  1136. if (entry.isIntersecting) {
  1137. navLinks.forEach(a => a.style.color = '');
  1138. link.style.color = 'var(--g100)';
  1139. }
  1140. });
  1141. }, { rootMargin: '-40% 0px -55% 0px', threshold: 0 });
  1142. sections.forEach(s => io.observe(s));
  1143. })();
  1144. </script>
  1145. </body>
  1146. </html>