소스 검색

chore: initial commit — landing page + CV + Claude config

Single-page static site at bchanot.fr: landing (index.html) and CV
(HTML + PDF), pure HTML/CSS/JS, no build step. Includes project
conventions (CLAUDE.md), README, and .claude/ memory/tasks/audits
scaffolding.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bastien 1 일 전
커밋
54e830016c
12개의 변경된 파일2043개의 추가작업 그리고 0개의 파일을 삭제
  1. 34 0
      .claude/memory/blockers.md
  2. 66 0
      .claude/memory/decisions.md
  3. 35 0
      .claude/memory/evals.md
  4. 22 0
      .claude/memory/journal.md
  5. 32 0
      .claude/memory/learnings.md
  6. 29 0
      .claude/tasks/TODO.md
  7. 15 0
      .gitignore
  8. 130 0
      CLAUDE.md
  9. 558 0
      CV_Bastien_Chanot.html
  10. BIN
      CV_Bastien_Chanot.pdf
  11. 79 0
      README.md
  12. 1043 0
      index.html

+ 34 - 0
.claude/memory/blockers.md

@@ -0,0 +1,34 @@
+---
+type: blockers_registry
+entry_prefix: BLK
+schema:
+  id: BLK-XXX
+  date: YYYY-MM-DD
+  friction: string (what was blocked)
+  real_cause: string (root cause, not symptom)
+  solution: string (workaround or fix)
+  status: [open | resolved | upstream]
+rules:
+  - Open a blocker as soon as friction > 15 min wasted. Close it with a real cause, not "moved on".
+  - Link to upstream issue / PR / commit when applicable.
+  - If cause is a bug in a dependency, set status upstream with a pointer to the tracker.
+---
+
+# Blockers registry (BLK)
+
+## Index
+
+| ID | Date | Friction | Status |
+|----|------|---------|--------|
+
+<!-- Append entries below. Template:
+
+## BLK-XXX - <friction>
+
+- **Date** : YYYY-MM-DD
+- **Friction** : <ce qui était bloqué>
+- **Cause réelle** : <cause racine>
+- **Solution** : <workaround ou fix>
+- **Statut** : open | resolved | upstream
+
+-->

+ 66 - 0
.claude/memory/decisions.md

@@ -0,0 +1,66 @@
+---
+type: decisions_registry
+entry_prefix: BDR
+schema:
+  id: BDR-XXX
+  date: YYYY-MM-DD
+  title: string (<= 80 chars)
+  decision: string (what was chosen)
+  why: string (motivation, context)
+  alternatives: list of strings (what was rejected + why)
+  status: [proposed | accepted | deprecated | superseded]
+  supersedes: BDR-XXX (optional)
+rules:
+  - Append-only. Never rewrite past entries - add a new one with status superseded if needed.
+  - One entry per non-trivial choice. Trivial = reversible in under 10 min with no cross-file impact.
+  - Capture why more carefully than what - the what rots, the why lasts.
+---
+
+# Decisions registry (BDR)
+
+## Index
+
+| ID | Date | Title | Status |
+|----|------|-------|--------|
+| BDR-001 | 2026-05-15 | Static single-file site, no framework | accepted |
+| BDR-002 | 2026-05-15 | weasyprint pour PDF CV depuis HTML | accepted |
+| BDR-003 | 2026-05-15 | Position pro: CDI prioritaire, freelance parallèle | accepted |
+
+---
+
+## BDR-001 — Static single-file site, no framework
+
+- **Date**: 2026-05-15
+- **Statut**: accepted
+- **Décision**: `index.html` unique, CSS inline `<style>`, JS vanilla inline `<script>`. Aucun bundler, aucun build step.
+- **Pourquoi**: Landing perso 1 page. Audience recruteurs/CTO. Critère "click Contacter <10s". Zéro dep, zéro maintenance, zéro tracking. Indexable par défaut.
+- **Alternatives rejetées**:
+  - Astro — overkill, ajoute build step pour 1 page.
+  - React SPA — interdit pour site public indexable (cf `~/.claude/CLAUDE.md`).
+  - HTML + CSS externes — éclate 1 livrable en 3 fichiers sans bénéfice.
+- **Référence**: `index.html`, section Stack de `CLAUDE.md`.
+
+---
+
+## BDR-002 — `weasyprint` pour génération PDF CV
+
+- **Date**: 2026-05-15
+- **Statut**: accepted
+- **Décision**: `weasyprint CV_Bastien_Chanot.html CV_Bastien_Chanot.pdf` à chaque modif HTML CV.
+- **Pourquoi**: weasyprint déjà installé (`~/.local/bin/weasyprint`). Chromium absent. wkhtmltopdf déprécié.
+- **Alternatives rejetées**:
+  - Chromium `--print-to-pdf` — pas installé.
+  - wkhtmltopdf — déprécié + WebKit ancien, rendu moins fidèle.
+  - Print manuel via navigateur — pas reproductible, dérive entre HTML et PDF.
+- **Warnings connus**: `box-shadow: none` ignoré par weasyprint, sans impact visuel.
+- **Référence**: `CV_Bastien_Chanot.pdf`.
+
+---
+
+## BDR-003 — Position pro: CDI prioritaire, missions freelance en parallèle
+
+- **Date**: 2026-05-15
+- **Statut**: accepted
+- **Décision**: Site annonce **CDI systèmes/embarqué prioritaire**, ZenQuality (freelance) en parallèle. Géo: full remote idéal, hybride 1-2 j/mois si Paris, mobilité Pays de la Loire.
+- **Pourquoi**: Recadrage user. Première version annonçait "Missions long terme & expertise" — pas représentatif. Hiérarchie CDI > freelance maintenant explicite (hero eyebrow + about para + callout + CV header).
+- **Référence**: `index.html` (hero-eyebrow, about-text para 3, about-callout) + `CV_Bastien_Chanot.html` (header).

+ 35 - 0
.claude/memory/evals.md

@@ -0,0 +1,35 @@
+---
+type: evals_registry
+entry_prefix: EVAL
+schema:
+  id: EVAL-XXX
+  date: YYYY-MM-DD
+  output: string (what was produced)
+  method: string (how it was evaluated - manual read, test, benchmark, user feedback)
+  anomalies: list of strings (what was wrong, missing, surprising)
+  action: [keep | correct | deprecate]
+rules:
+  - Log an eval whenever you validate the quality of something Claude produced (report, audit, plan, generated code).
+  - Action keep - the output is fit for purpose as-is.
+  - Action correct - needs revision; capture what.
+  - Action deprecate - the approach itself is flawed; link to the decision that replaces it.
+---
+
+# Evals registry (EVAL)
+
+## Index
+
+| ID | Date | Output | Action |
+|----|------|--------|--------|
+
+<!-- Append entries below. Template:
+
+## EVAL-XXX - <output>
+
+- **Date** : YYYY-MM-DD
+- **Output** : <ce qui a été produit>
+- **Méthode** : <comment cela a été évalué>
+- **Anomalies** : <ce qui est faux, manquant, surprenant>
+- **Action** : keep | correct | deprecate
+
+-->

+ 22 - 0
.claude/memory/journal.md

@@ -0,0 +1,22 @@
+---
+type: journal
+schema:
+  entry: one date heading per working session
+  body: 3-5 lines max - what was done, decided, blocked
+rules:
+  - One heading per date (YYYY-MM-DD), not per session.
+  - Append at the end. Never edit past entries.
+  - Keep it terse. Details belong in decisions/learnings/blockers - this is a timeline only.
+---
+
+# Journal
+
+## 2026-05-15
+
+- Bootstrap `index.html`: hero stagger, sticky nav, stack cards 6 cat, timeline parcours, contact dark, footer.
+- Palette + 3 polices câblées via `:root`. CSS inline + JS vanilla. Aucune dep sauf Google Fonts.
+- CV HTML mis à jour: header suffix "· mobilité Pays de la Loire" à côté Yerres.
+- PDF CV regen via `weasyprint`. Warning `box-shadow: none` ignoré.
+- Serveur dev: `python3 -m http.server 8000 --bind 0.0.0.0` → LAN sur `192.168.1.101:8000`.
+- Position pro précisée: CDI embarqué/logiciel prioritaire, freelance ZenQuality parallèle, remote ou Paris 1-2 j/mois, mobilité Pays de la Loire.
+- Squelette `.claude/` + `CLAUDE.md` + `README.md` créés a posteriori (init-project skippé init pour single-file livrable).

+ 32 - 0
.claude/memory/learnings.md

@@ -0,0 +1,32 @@
+---
+type: learnings_registry
+entry_prefix: LRN
+schema:
+  id: LRN-XXX
+  date: YYYY-MM-DD
+  pattern: string (what was observed, abstracted)
+  context: string (where/when it happened - concrete)
+  future_application: string (when to recall this)
+rules:
+  - Capture learnings that apply beyond the current task.
+  - Abstract from the incident - the pattern is what is reusable, not the one-shot fact.
+  - Link to source (commit, file, PR) when possible.
+---
+
+# Learnings registry (LRN)
+
+## Index
+
+| ID | Date | Pattern | Applies to |
+|----|------|---------|------------|
+
+<!-- Append entries below. Template:
+
+## LRN-XXX - <pattern abstrait>
+
+- **Date** : YYYY-MM-DD
+- **Pattern** : <ce qui a été observé, formulé de manière réutilisable>
+- **Contexte** : <où et quand, concret>
+- **Application future** : <quand se rappeler de ceci>
+
+-->

+ 29 - 0
.claude/tasks/TODO.md

@@ -0,0 +1,29 @@
+# TODO — bchanot.fr
+
+State of the landing page + CV project. Append-only: keep history readable.
+
+---
+
+## Current
+
+- Landing page `index.html` shipped (single file, inline CSS + JS).
+- CV `CV_Bastien_Chanot.html` + matching PDF regenerated via weasyprint.
+- Local serve verified at `http://192.168.1.101:8000/`.
+
+## Known follow-ups
+
+- Visual QA on real mobile device (375 px) — not just emulator.
+- Verify WCAG AA contrast on all green-on-parchment text.
+- Hosting decision: GitHub Pages vs Netlify vs Vercel vs nginx VPS — pending.
+- DNS / domain config for `bchanot.fr` — pending.
+- Consider OG image + favicon — not yet present.
+
+## Open ideas (not committed)
+
+- Light "what I'm working on right now" section (single line under hero).
+- Add a Gogs / GitHub link if a clean public repo is curated first.
+- Print-stylesheet polish for `CV_Bastien_Chanot.html` if weasyprint output drifts.
+
+---
+
+> Mark items done by moving them to `.claude/memory/journal.md` with a date heading.

+ 15 - 0
.gitignore

@@ -0,0 +1,15 @@
+# OS / editor
+.DS_Store
+Thumbs.db
+*.swp
+*~
+
+# Local servers / caches
+.ctx7-cache/
+graphify-out/
+
+# Logs
+*.log
+
+# Local Claude settings (per-user overrides — keep .claude/ tracked otherwise)
+.claude/settings.local.json

+ 130 - 0
CLAUDE.md

@@ -0,0 +1,130 @@
+# bchanot.fr — CLAUDE.md
+
+Single source of truth for Claude in this repo.
+Global rules: `~/.claude/CLAUDE.md` — this file extends or overrides them.
+
+---
+
+## Project overview
+
+Personal landing page + CV for Bastien Chanot (developer, systems & backend).
+Single-page static site served at https://bchanot.fr. Goal: a recruiter or CTO
+landing on the page decides whether to contact within 10 seconds.
+
+Audience: technical recruiters, CTOs, engineering managers. Use case: hub for
+contact + CV access. No tracking, no analytics, no cookie banner.
+
+---
+
+## Stack
+
+- Pure static HTML/CSS/JS — no framework, no build step.
+- Fonts: Google Fonts (`JetBrains Mono` mono, `Fraunces` display, `DM Sans` sans).
+- PDF generation for CV: `weasyprint` (from the matching HTML file).
+
+No package manager. No bundler. No runtime dependencies beyond Google Fonts.
+
+---
+
+## Files
+
+```
+index.html                  — landing page (single file, inline CSS + JS)
+CV_Bastien_Chanot.html      — CV (web version, linked from landing as "Voir le CV")
+CV_Bastien_Chanot.pdf       — CV (printable, served via "Télécharger PDF")
+README.md                   — repo readme
+CLAUDE.md                   — this file
+.claude/                    — Claude memory, tasks, audits
+```
+
+---
+
+## Serve locally
+
+```bash
+python3 -m http.server 8000 --bind 0.0.0.0
+# then visit http://192.168.1.101:8000/ from any device on the LAN
+```
+
+UFW may block the port — open it on demand only:
+
+```bash
+sudo ufw allow 8000/tcp
+```
+
+---
+
+## Regenerate CV PDF (after editing the HTML)
+
+```bash
+weasyprint CV_Bastien_Chanot.html CV_Bastien_Chanot.pdf
+```
+
+The PDF must match the latest HTML before pushing or sending.
+
+---
+
+## Design system (non-negotiable)
+
+Palette — exact hex:
+- `#0d1b12` — dark forest (nav, dark sections, footer)
+- `#1b5e3b` — green primary (links, section titles on light bg)
+- `#2d7a4f` — green accent (borders, dots, separators)
+- `#6ab98a` — green light (lisible on dark bg)
+- `#dff0e7` — green tint (pill bg)
+- `#f5f3ec` — parchment (page bg)
+
+Typography:
+- `Fraunces` (serif) — display: hero name, section titles, role headings
+- `JetBrains Mono` (mono) — eyebrows, badges, tech pills, nav, contact rows
+- `DM Sans` (sans) — body text
+
+Forbidden:
+- Pure white background (`#ffffff`)
+- `border-radius` > 6px except pills
+- Heavy SVG illustrations
+- Lorem ipsum or placeholder text
+- Mention of salary / TJM / pricing
+
+---
+
+## Project conventions
+
+- All CSS lives inline in `<head>` (`<style>`) — no external stylesheet.
+- All JS lives inline before `</body>` (`<script>`) — vanilla only.
+- CSS variables in `:root` for palette + typography + spacing scale.
+- Section comments in HTML: `<!-- HERO -->`, `<!-- ABOUT -->`, etc.
+- Sections semantic: `<header>`, `<main>`, `<section id="…">`, `<footer>`.
+- Mobile-first. Breakpoints: 768px (tablet), 1200px (desktop).
+- Animations CSS-only or vanilla JS. No GSAP, no Three.js, no Lottie.
+- `prefers-reduced-motion: reduce` must disable animation + smooth-scroll.
+
+---
+
+## Content rules
+
+- Only real information — never invent dates, companies, achievements.
+- French copy (audience is French market).
+- Profile state, including job search context, must stay consistent across
+  index.html and CV. Currently: looking for **CDI** in embedded / systems
+  software first; freelance missions (ZenQuality) in parallel.
+- Geography: Yerres (91) currently; targeting Pays de la Loire mid-term;
+  full remote preferred or hybrid 1–2 days/month if Paris.
+
+---
+
+## Exceptions to global rules
+
+None — global rules apply.
+
+---
+
+## Workflow expectations
+
+- Edits to `index.html` or `CV_Bastien_Chanot.html` must preserve the
+  palette + typography + structure unless explicitly asked to change them.
+- After editing `CV_Bastien_Chanot.html`, regenerate the PDF.
+- Never add external dependencies beyond Google Fonts.
+- Never add tracking, analytics, cookie banners or third-party scripts.
+- Always test in mobile width (375px) and desktop (1440px) before claiming done.
+- The HTTP server bound to `192.168.1.101:8000` is for local LAN testing only.

+ 558 - 0
CV_Bastien_Chanot.html

@@ -0,0 +1,558 @@
+<!DOCTYPE html>
+<html lang="fr">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Bastien Chanot — CV</title>
+<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;600;700&family=Fraunces:ital,wght@0,300;0,600;0,700;1,300&family=DM+Sans:wght@300;400;500&display=swap" rel="stylesheet">
+<style>
+  :root {
+    /* Zones sombres (header, footer) */
+    --dark:       #0d1b12;
+    --dark-mid:   #183325;
+
+    /* Échelle verte — même teinte, 5 niveaux */
+    --g900:  #0e3320;   /* texte sur fond clair : très fort */
+    --g700:  #1b5e3b;   /* titres section, rôles, accroches */
+    --g500:  #2d7a4f;   /* bordures accent, liens */
+    --g300:  #6ab98a;   /* sur fond sombre : lisible */
+    --g100:  #dff0e7;   /* tag bg clair */
+    --g050:  #eef7f1;   /* skills block bg */
+
+    /* Typographie — neutrals chauds */
+    --ink-1:  #111111;  /* noms entreprises, school, titres forts */
+    --ink-2:  #1e1e1e;  /* corps de texte, bullets */
+    --ink-3:  #636363;  /* dates, lieux, labels légers */
+
+    /* Surfaces */
+    --page:   #f5f3ec;  /* fond document */
+    --rule:   #d8d4c8;  /* séparateurs */
+    --tag:    #e6e2d8;  /* tag générique */
+
+    --mono: 'JetBrains Mono', monospace;
+    --serif: 'Fraunces', serif;
+    --sans: 'DM Sans', sans-serif;
+  }
+
+  * { margin: 0; padding: 0; box-sizing: border-box; }
+
+  body {
+    background: #fff;
+    font-family: var(--sans);
+    -webkit-font-smoothing: antialiased;
+    margin: 0;
+    padding: 0;
+  }
+
+
+
+  .page {
+    width: 210mm;
+    max-width: 100%;
+    margin: 0 auto;
+    background: var(--page);
+
+    overflow: hidden;
+  }
+
+  /* ── HEADER DARK BLOCK ── */
+  .cv-header {
+    background: var(--dark);
+    background-image: radial-gradient(ellipse 120% 200% at 0% 50%, var(--dark-mid) 0%, var(--dark) 60%);
+    padding: 22px 20mm 18px;
+    display: grid;
+    grid-template-columns: 1fr auto;
+    gap: 20px;
+    align-items: center;
+    position: relative;
+    overflow: hidden;
+  }
+
+  .cv-header::before {
+    content: '';
+    position: absolute;
+    left: 0; top: 0; bottom: 0;
+    width: 4px;
+    background: linear-gradient(to bottom, var(--g300), var(--g700));
+  }
+
+  .cv-header::after {
+    content: '';
+    position: absolute;
+    right: -40px; top: -40px;
+    width: 200px; height: 200px;
+    border-radius: 50%;
+    background: radial-gradient(circle, rgba(45,122,79,0.12) 0%, transparent 70%);
+    pointer-events: none;
+  }
+
+  .header-name {
+    font-family: var(--serif);
+    font-size: 26pt;
+    font-weight: 700;
+    color: #fff;
+    line-height: 1;
+    letter-spacing: -0.5px;
+  }
+
+  .header-title {
+    font-family: var(--mono);
+    font-size: 9pt;
+    font-weight: 600;
+    color: var(--g300);
+    letter-spacing: 2.5px;
+    text-transform: uppercase;
+    margin-top: 6px;
+    opacity: 0.9;
+  }
+
+  .header-contact {
+    text-align: right;
+    font-family: var(--mono);
+    font-size: 7.5pt;
+    color: rgba(255,255,255,0.55);
+    line-height: 1.8;
+    white-space: nowrap;
+    min-width: 210px;
+    flex-shrink: 0;
+  }
+  .contact-sep {
+    color: rgba(106,185,138,0.4);
+    margin: 0 4px;
+  }
+  .header-contact a {
+    color: rgba(255,255,255,0.8);
+    text-decoration: underline;
+    text-decoration-color: rgba(106,185,138,0.5);
+    text-underline-offset: 2px;
+  }
+
+  /* ── BODY ── */
+  .cv-body {
+    padding: 14px 20mm 16px;
+    background:
+      url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='6' height='6'%3E%3Ccircle cx='1' cy='1' r='0.6' fill='rgba(26,71,48,0.05)'/%3E%3C/svg%3E"),
+      linear-gradient(160deg, #f5f3ec 0%, #edeadf 55%, #f2efe6 100%);
+  }
+
+  .accroche {
+    font-family: var(--serif);
+    font-style: italic;
+    font-weight: 400;
+    font-size: 11.5pt;
+    color: var(--ink-2);
+    line-height: 1.7;
+    border-left: 3px solid var(--g500);
+    padding: 6px 0 6px 12px;
+    margin: 14px 0;
+    background: linear-gradient(to right, rgba(14,51,32,0.04), transparent);
+  }
+
+  .rule {
+    height: 1px;
+    background: linear-gradient(to right, var(--rule), rgba(216,212,200,0));
+    margin: 10px 0;
+  }
+
+  .section { margin-bottom: 13px; }
+
+  .section-title {
+    font-family: var(--mono);
+    font-size: 9pt;
+    font-weight: 700;
+    letter-spacing: 2.5px;
+    text-transform: uppercase;
+    color: var(--g700);
+    margin-bottom: 9px;
+    display: flex;
+    align-items: center;
+    gap: 8px;
+  }
+
+  .section-title::after {
+    content: '';
+    flex: 1;
+    height: 1px;
+    background: var(--rule);
+  }
+
+  /* ── XP ── */
+  .xp-block { margin-bottom: 11px; }
+
+  .xp-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: baseline;
+    margin-bottom: 1px;
+  }
+
+  .xp-company {
+    font-family: var(--mono);
+    font-size: 12pt;
+    font-weight: 700;
+    color: var(--ink-1);
+  }
+
+  .xp-dates {
+    font-family: var(--mono);
+    font-size: 8.5pt;
+    color: var(--ink-3);
+    white-space: nowrap;
+    background: var(--tag);
+    padding: 1px 6px;
+    border-radius: 2px;
+  }
+
+  .xp-role {
+    font-size: 10pt;
+    font-weight: 500;
+    color: var(--g700);
+    margin-bottom: 5px;
+  }
+
+  .xp-loc {
+    font-size: 7.5pt;
+    color: var(--ink-3);
+    font-style: italic;
+  }
+
+  ul.bullets { list-style: none; }
+
+  ul.bullets li {
+    padding-left: 13px;
+    position: relative;
+    margin-bottom: 3px;
+    font-size: 10.5pt;
+    font-weight: 500;
+    color: var(--ink-2);
+    line-height: 1.55;
+  }
+
+  ul.bullets li::before {
+    content: '▸';
+    position: absolute;
+    left: 0;
+    color: var(--g300);
+    font-size: 7pt;
+    top: 2px;
+  }
+
+  .tag {
+    display: inline-block;
+    background: var(--g100);
+    border: 1px solid #a8d4bc;
+    font-family: var(--mono);
+    font-size: 6.5pt;
+    padding: 1px 5px;
+    border-radius: 3px;
+    color: var(--g900);
+    margin: 0 1px;
+    white-space: nowrap;
+  }
+
+  /* ── SKILLS ── */
+  .skills-grid {
+    display: grid;
+    grid-template-columns: 88px 1fr;
+    gap: 4px 10px;
+    font-size: 8.4pt;
+
+  }
+
+  .skill-label {
+    font-family: var(--mono);
+    font-size: 9.5pt;
+    font-weight: 700;
+    color: var(--g700);
+    padding-top: 1px;
+  }
+
+  .skill-values { font-size: 10.5pt; font-weight: 500; color: var(--ink-2); }
+
+  /* ── EDU ── */
+  .edu-block { margin-bottom: 9px; }
+
+  .edu-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: baseline;
+  }
+
+  .edu-school {
+    font-family: var(--mono);
+    font-size: 11.5pt;
+    font-weight: 700;
+    color: var(--ink-1);
+  }
+
+  .edu-dates {
+    font-family: var(--mono);
+    font-size: 7.2pt;
+    color: var(--ink-3);
+    background: var(--tag);
+    padding: 1px 6px;
+    border-radius: 2px;
+  }
+
+  .edu-degree {
+    font-size: 10pt;
+    font-weight: 500;
+    color: var(--g700);
+    margin-bottom: 3px;
+  }
+
+  .edu-detail {
+    font-size: 10pt;
+    font-weight: 500;
+    color: var(--ink-2);
+    line-height: 1.55;
+  }
+
+  /* ── TWO COL BOTTOM ── */
+  .two-col {
+    display: grid;
+    grid-template-columns: 1fr 1fr;
+    gap: 0 20px;
+  }
+
+  .lang-row { display: flex; gap: 5px; flex-wrap: wrap; }
+
+  .lang-item {
+    font-family: var(--mono);
+    font-size: 9pt;
+    color: var(--g900);
+    background: var(--g100);
+    border: 1px solid #a8d4bc;
+    padding: 3px 10px;
+    border-radius: 12px;
+  }
+
+  .lang-item .level {
+    color: var(--ink-3);
+    font-size: 8pt;
+  }
+
+  .interests { display: flex; flex-wrap: wrap; gap: 5px; }
+
+  .interest-tag {
+    font-family: var(--mono);
+    font-size: 9pt;
+    color: var(--g900);
+    background: var(--g100);
+    border: 1px solid #a8d4bc;
+    padding: 3px 10px;
+    border-radius: 12px;
+  }
+
+  /* ── FOOTER ── */
+  .footer-bar {
+    background: var(--dark);
+    padding: 7px 20mm;
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+  }
+
+  .footer-bar span {
+    font-family: var(--mono);
+    font-size: 6.8pt;
+    color: rgba(255,255,255,0.3);
+  }
+
+  .footer-bar a {
+    font-family: var(--mono);
+    font-size: 6.8pt;
+    color: var(--g300);
+    text-decoration: none;
+    opacity: 0.7;
+  }
+
+  /* ── PRINT ── */
+  @media print {
+    body { background: white; padding: 0; }
+    .screen-label { display: none; }
+    .page { box-shadow: none; }
+    .cv-header { padding: 18px 14mm 14px; }
+    .cv-body { padding: 12px 14mm 14px; }
+    .footer-bar { padding: 6px 14mm; }
+    @page { size: A4; margin: 0; }
+  }
+</style>
+</head>
+<body>
+
+
+<div class="page">
+
+  <div class="cv-header">
+    <div>
+      <div class="header-name">Bastien Chanot</div>
+      <div class="header-title">Développeur confirmé &nbsp;·&nbsp; Systèmes &amp; Backend</div>
+    </div>
+    <div class="header-contact">
+      <a href="tel:+33778822297">+33 7 78 82 22 97</a><br>
+      <a href="mailto:chanot.bastien@gmail.com">chanot.bastien@gmail.com</a><br>
+      Yerres → Reloc. Pays de la Loire<br>
+      Open full remote <span class="contact-sep">|</span> Permis B &amp; A
+    </div>
+  </div>
+
+  <div class="cv-body">
+
+    <div class="accroche">
+      Développeur systèmes confirmé (7 ans), spécialisé en C/Rust/Java sur Linux bare-metal, kernel drivers et environnements AOSP.
+      Expérience étendue sur l'architecture et l'exploitation de serveurs GPU haute densité, la conteneurisation LXC/LXD,
+      et le développement système embarqué de bout en bout. Habitué à opérer seul sur des périmètres larges,
+      de la couche kernel au backend réseau.
+    </div>
+
+    <div class="rule"></div>
+
+    <div class="section">
+      <div class="section-title">Expérience professionnelle</div>
+
+      <div class="xp-block">
+        <div class="xp-header">
+          <span class="xp-company">ZenQuality</span>
+          <span class="xp-dates">Avr. 2026 – présent</span>
+        </div>
+        <div class="xp-role">Développeur indépendant <span class="xp-loc">· Yerres · <a href="https://zenquality.fr" style="color:var(--g500);text-decoration:none;border-bottom:1px solid var(--g300);">zenquality.fr</a></span></div>
+        <ul class="bullets">
+          <li>Développement logiciel et conseil technique en indépendant — systèmes, backend, infrastructure.</li>
+        </ul>
+      </div>
+
+      <div class="xp-block">
+        <div class="xp-header">
+          <span class="xp-company">CareGame</span>
+          <span class="xp-dates">Mars 2019 – Mars 2025</span>
+        </div>
+        <div class="xp-role">Développeur logiciel — Systèmes &amp; Backend <span class="xp-loc">· Paris · Full remote dès 2020</span></div>
+        <ul class="bullets">
+          <li>Développement solo de drivers kernel Linux en <span class="tag">C</span> pour environnement AOSP (x86 / ARM) — adaptation de modules GPU, isolation CPU/GPU par instance de jeu.</li>
+          <li>Développement d'un backend <span class="tag">Rust</span> (~2 000 lignes) : communication WebSocket entre clients et instances, intégration <span class="tag">Docker</span> / <span class="tag">LXC</span> pour l'orchestration des conteneurs AOSP.</li>
+          <li>Développement de virtual touchscreen et virtual gamepad AOSP (<span class="tag">Java</span>), servant de drivers hôtes communicant avec le backend Rust.</li>
+          <li>Architecture et optimisation de serveurs GPU bare-metal (g4 : 8× GPU T4, 64 vCPU) — isolation de 2 cœurs CPU/session, I/O sur ramdisk, 32 sessions AAA simultanées (Asphalt 9 : 3 sessions / GPU T4).</li>
+          <li>Intégration et amélioration d'un système <span class="tag">LXC/LXD</span> issu d'une R&amp;D Nvidia — résultats reconnus par les équipes Nvidia au-delà des attentes initiales.</li>
+          <li>Collaboration avec Canonical (<span class="tag">Anbox</span>, versions non commerciales LXC/LXD) et Ampere Computing (serveurs ARM pré-commerciaux).</li>
+          <li>Développement du système d'installation automatique de jeux sur AOSP et gestion des sauvegardes utilisateur (Android Backup + script custom).</li>
+          <li>Scripting <span class="tag">Bash</span> pour l'automatisation de l'infrastructure et du cycle de vie des instances.</li>
+        </ul>
+      </div>
+
+      <div class="xp-block">
+        <div class="xp-header">
+          <span class="xp-company">Deewee</span>
+          <span class="xp-dates">2017</span>
+        </div>
+        <div class="xp-role">Développeur C — Système embarqué <span class="xp-loc">· Ivry-sur-Seine</span></div>
+        <ul class="bullets">
+          <li>Développement en <span class="tag">C</span> sur Orange Pi (<span class="tag">Debian ARM</span>) interceptant le flux <span class="tag">ESC/POS</span> d'une imprimante thermique pour générer une image PNG du ticket avant impression.</li>
+          <li>Gestion <span class="tag">GPIO</span> physique (bouton) pour déclenchement conditionnel de l'impression avec timeout.</li>
+          <li>Hotspot WiFi embarqué + diffusion des credentials via antenne <span class="tag">NFC</span> (scan automatique depuis application mobile tierce).</li>
+          <li>Envoi de l'image générée en WiFi direct vers l'application mobile à la connexion.</li>
+        </ul>
+      </div>
+    </div>
+
+    <div class="rule"></div>
+
+    <div class="section">
+      <div class="section-title">Formation</div>
+
+      <div class="edu-block">
+        <div class="edu-header">
+          <span class="edu-school">École 42</span>
+          <span class="edu-dates">2015 – 2019</span>
+        </div>
+        <div class="edu-degree">Programmation informatique <span class="xp-loc">· Clichy</span></div>
+        <div class="edu-detail">
+          <strong>Systèmes / Kernel :</strong> ft_linux &amp; kfs-1 (Linux From Scratch — bootloader ASM, GDT, interruptions, driver char device) · drivers &amp; interrupt · process &amp; memory · little penguin<br>
+          <strong>Bas niveau :</strong> malloc (allocateur mémoire) · nm (parsing ELF) · ft_ls · ft_select · 42sh (shell POSIX complet)<br>
+          <strong>Réseau / Sécurité :</strong> ft_ssl_md5 (crypto) · snow crash (exploitation système) · lem-in · push-swap · doctor quine
+        </div>
+      </div>
+
+      <div class="edu-block">
+        <div class="edu-header">
+          <span class="edu-school">Next Formation</span>
+          <span class="edu-dates">2013 – 2015</span>
+        </div>
+        <div class="edu-degree">TSRIT — Technicien Supérieur Réseaux &amp; Télécoms, <em>Félicitations du jury</em> <span class="xp-loc">· Vincennes</span></div>
+      </div>
+
+      <div class="edu-block">
+        <div class="edu-header">
+          <span class="edu-school">Lycée Rosa Parks</span>
+          <span class="edu-dates">2009 – 2013</span>
+        </div>
+        <div class="edu-degree">Bac S — génie informatique, électrique et mécanique <span class="xp-loc">· Montgeron</span></div>
+      </div>
+    </div>
+
+    <div class="rule"></div>
+
+    <div class="section">
+      <div class="section-title">Compétences techniques</div>
+      <div class="skills-grid">
+        <div class="skill-label">Langages</div>
+        <div class="skill-values">C · Rust · Java · Bash · Python · C++ (notions)</div>
+
+        <div class="skill-label">Embarqué</div>
+        <div class="skill-values">Linux kernel drivers · AOSP · ARM / x86 · GPIO · NFC · ESC/POS · Orange Pi · cross-compilation GCC</div>
+
+        <div class="skill-label">Conteneurs</div>
+        <div class="skill-values">Docker · LXC / LXD · QEMU · VMware · VirtualBox</div>
+
+        <div class="skill-label">Backend</div>
+        <div class="skill-values">Rust · WebSocket · architecture GPU bare-metal · ramdisk I/O</div>
+
+        <div class="skill-label">Systèmes</div>
+        <div class="skill-values">Linux bare-metal · AOSP · Android Backup · systemd · Windows · macOS</div>
+
+        <div class="skill-label">DevOps</div>
+        <div class="skill-values">Git · GitHub / GitLab · CI/CD · Gitflow · Agile (Scrum, Kanban)</div>
+
+        <div class="skill-label">IA / Outils</div>
+        <div class="skill-values">Claude Code (agents/skills custom) · N8N · automatisation · prompting avancé</div>
+
+        <div class="skill-label">Gestion</div>
+        <div class="skill-values">Trello · Jira · Confluence · Notion</div>
+      </div>
+    </div>
+
+    <div class="rule"></div>
+
+    <div class="two-col">
+      <div class="section">
+        <div class="section-title">Langues</div>
+        <div class="lang-row">
+          <div class="lang-item">Anglais <span class="level">C2</span></div>
+          <div class="lang-item">Espagnol <span class="level">B1</span></div>
+          <div class="lang-item">Français <span class="level">natif</span></div>
+        </div>
+      </div>
+
+      <div class="section">
+        <div class="section-title">Centres d'intérêt</div>
+        <div class="interests">
+          <span class="interest-tag">Voyage</span>
+          <span class="interest-tag">Sciences</span>
+          <span class="interest-tag">Astronomie</span>
+          <span class="interest-tag">Nature</span>
+          <span class="interest-tag">Jeux vidéo</span>
+          <span class="interest-tag">Musique</span>
+        </div>
+      </div>
+    </div>
+
+  </div>
+
+  <div class="footer-bar">
+    <a href="https://bchanot.fr">bchanot.fr</a>
+    <span>Bastien Chanot · CV 2026</span>
+  </div>
+
+</div>
+</body>
+</html>

BIN
CV_Bastien_Chanot.pdf


+ 79 - 0
README.md

@@ -0,0 +1,79 @@
+# bchanot.fr
+
+Personal landing page + CV for Bastien Chanot — developer, systems & backend.
+
+Static single-page site (no framework, no build step). Lives at https://bchanot.fr.
+
+## Contents
+
+| File | Purpose |
+|------|---------|
+| `index.html`                  | Landing page (inline CSS + JS, single file) |
+| `CV_Bastien_Chanot.html`      | CV — web version, linked from landing as "Voir le CV" |
+| `CV_Bastien_Chanot.pdf`       | CV — printable, served via "Télécharger PDF" |
+| `CLAUDE.md`                   | Project rules for the Claude assistant |
+| `.claude/`                    | Memory registries, tasks, audits |
+
+## Local preview
+
+```bash
+python3 -m http.server 8000 --bind 0.0.0.0
+```
+
+Then open `http://localhost:8000/` (same machine) or `http://192.168.1.101:8000/`
+from any device on the LAN.
+
+If the LAN URL is unreachable, the firewall is likely blocking the port:
+
+```bash
+sudo ufw allow 8000/tcp
+```
+
+## Regenerate the CV PDF after editing the HTML
+
+```bash
+weasyprint CV_Bastien_Chanot.html CV_Bastien_Chanot.pdf
+```
+
+Run this every time `CV_Bastien_Chanot.html` is modified so the served PDF
+stays in sync.
+
+## Stack
+
+- HTML5 + CSS3 (inline `<style>` in `<head>`)
+- Vanilla JS (inline `<script>` before `</body>`)
+- Google Fonts: JetBrains Mono, Fraunces, DM Sans
+- `weasyprint` for HTML → PDF conversion (CV only)
+
+No bundler. No npm. No runtime dependencies beyond Google Fonts.
+
+## Design rules
+
+Strict palette (non-negotiable):
+
+| Hex       | Role |
+|-----------|------|
+| `#0d1b12` | Dark forest — nav, dark sections, footer |
+| `#1b5e3b` | Green primary — section titles, links on light bg |
+| `#2d7a4f` | Green accent — borders, dots, separators |
+| `#6ab98a` | Green light — text on dark bg |
+| `#dff0e7` | Green tint — pill background |
+| `#f5f3ec` | Parchment — page background |
+
+Typography:
+- `Fraunces` — display (names, titles)
+- `JetBrains Mono` — technical labels, badges, pills, nav, contact
+- `DM Sans` — body text
+
+Mobile-first, responsive at 768px + 1200px breakpoints.
+WCAG AA contrast. Focus visible. Semantic HTML.
+
+## Deploy
+
+Static files — drop `index.html`, `CV_Bastien_Chanot.html`, and
+`CV_Bastien_Chanot.pdf` onto any static host (Netlify, Vercel, GitHub Pages,
+plain nginx) at the root.
+
+## License
+
+Personal site content — © Bastien Chanot.

+ 1043 - 0
index.html

@@ -0,0 +1,1043 @@
+<!DOCTYPE html>
+<html lang="fr">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<meta name="description" content="Bastien Chanot — Développeur confirmé · Systèmes & Backend. 7 ans en C, Rust, Linux kernel, AOSP, embarqué.">
+<meta name="author" content="Bastien Chanot">
+<meta name="theme-color" content="#0d1b12">
+<title>Bastien Chanot — Développeur Systèmes & Backend</title>
+<link rel="preconnect" href="https://fonts.googleapis.com">
+<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
+<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">
+<style>
+  :root {
+    /* Palette — non négociable */
+    --dark:       #0d1b12;
+    --dark-mid:   #183325;
+    --g900:       #0e3320;
+    --g700:       #1b5e3b;
+    --g500:       #2d7a4f;
+    --g300:       #6ab98a;
+    --g100:       #dff0e7;
+    --g050:       #eef7f1;
+
+    --ink-1:      #111111;
+    --ink-2:      #1e1e1e;
+    --ink-3:      #636363;
+
+    --page:       #f5f3ec;
+    --rule:       #d8d4c8;
+    --tag:        #e6e2d8;
+
+    /* Typographies */
+    --mono:  'JetBrains Mono', ui-monospace, SFMono-Regular, monospace;
+    --serif: 'Fraunces', Georgia, serif;
+    --sans:  'DM Sans', system-ui, -apple-system, sans-serif;
+
+    /* Échelle */
+    --r-sm: 4px;
+    --r-md: 6px;
+    --r-pill: 999px;
+    --shadow-sm: 0 1px 2px rgba(13, 27, 18, 0.06);
+    --shadow-md: 0 6px 24px rgba(13, 27, 18, 0.08);
+    --shadow-lg: 0 20px 60px rgba(13, 27, 18, 0.12);
+
+    --nav-h: 64px;
+    --max-w: 1100px;
+  }
+
+  *, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
+
+  html { scroll-behavior: smooth; scroll-padding-top: calc(var(--nav-h) + 16px); }
+
+  body {
+    background: var(--page);
+    color: var(--ink-2);
+    font-family: var(--sans);
+    font-size: 16px;
+    line-height: 1.6;
+    -webkit-font-smoothing: antialiased;
+    -moz-osx-font-smoothing: grayscale;
+    overflow-x: hidden;
+  }
+
+  ::selection { background: var(--g300); color: var(--dark); }
+
+  a { color: var(--g700); text-decoration: none; transition: color .18s ease; }
+  a:hover { color: var(--g500); }
+  a:focus-visible { outline: 2px solid var(--g500); outline-offset: 3px; border-radius: 2px; }
+
+  button, .btn { font-family: inherit; cursor: pointer; }
+
+  /* ── NAV ── */
+  .nav {
+    position: fixed;
+    top: 0; left: 0; right: 0;
+    height: var(--nav-h);
+    background: rgba(13, 27, 18, 0.92);
+    backdrop-filter: saturate(140%) blur(10px);
+    -webkit-backdrop-filter: saturate(140%) blur(10px);
+    border-bottom: 1px solid rgba(106, 185, 138, 0.12);
+    z-index: 50;
+  }
+  .nav-inner {
+    max-width: var(--max-w);
+    height: 100%;
+    margin: 0 auto;
+    padding: 0 24px;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    gap: 24px;
+  }
+  .brand {
+    font-family: var(--mono);
+    font-size: 14px;
+    font-weight: 600;
+    color: var(--g100);
+    letter-spacing: 0.5px;
+    display: inline-flex;
+    align-items: center;
+    gap: 8px;
+  }
+  .brand::before {
+    content: "";
+    width: 8px; height: 8px;
+    border-radius: 50%;
+    background: var(--g300);
+    box-shadow: 0 0 0 0 rgba(106,185,138,.7);
+    animation: pulse 2.4s infinite;
+  }
+  @keyframes pulse {
+    0%   { box-shadow: 0 0 0 0 rgba(106,185,138,.5); }
+    70%  { box-shadow: 0 0 0 10px rgba(106,185,138,0); }
+    100% { box-shadow: 0 0 0 0 rgba(106,185,138,0); }
+  }
+  .nav-links { display: flex; gap: 4px; list-style: none; }
+  .nav-links a {
+    font-family: var(--mono);
+    font-size: 13px;
+    color: rgba(223, 240, 231, 0.75);
+    padding: 8px 12px;
+    border-radius: var(--r-sm);
+    transition: color .18s ease, background .18s ease;
+  }
+  .nav-links a:hover { color: var(--g100); background: rgba(106,185,138,0.08); }
+  .nav-toggle { display: none; }
+
+  @media (max-width: 768px) {
+    .nav-links {
+      position: absolute;
+      top: var(--nav-h); left: 0; right: 0;
+      flex-direction: column;
+      gap: 0;
+      background: var(--dark);
+      border-bottom: 1px solid rgba(106,185,138,0.12);
+      padding: 8px 0;
+      transform: translateY(-200%);
+      transition: transform .25s ease;
+    }
+    .nav-links.open { transform: translateY(0); }
+    .nav-links a { padding: 14px 24px; border-radius: 0; }
+    .nav-toggle {
+      display: inline-flex;
+      align-items: center;
+      justify-content: center;
+      width: 40px; height: 40px;
+      margin-left: auto;
+      background: transparent;
+      border: 1px solid rgba(106,185,138,0.25);
+      border-radius: var(--r-sm);
+      color: var(--g100);
+    }
+    .nav-toggle svg { width: 18px; height: 18px; }
+  }
+
+  /* ── LAYOUT ── */
+  main { padding-top: var(--nav-h); }
+  section { padding: 96px 24px; }
+  .container { max-width: var(--max-w); margin: 0 auto; }
+
+  .section-label {
+    display: inline-flex;
+    align-items: center;
+    gap: 10px;
+    font-family: var(--mono);
+    font-size: 12px;
+    font-weight: 600;
+    color: var(--g500);
+    letter-spacing: 0.12em;
+    text-transform: uppercase;
+    margin-bottom: 18px;
+  }
+  .section-label::before {
+    content: "";
+    width: 24px; height: 1px; background: var(--g500);
+  }
+  .section-dark .section-label { color: var(--g300); }
+  .section-dark .section-label::before { background: var(--g300); }
+
+  h2.section-title {
+    font-family: var(--serif);
+    font-weight: 600;
+    font-size: clamp(28px, 4vw, 40px);
+    line-height: 1.1;
+    color: var(--ink-1);
+    letter-spacing: -0.02em;
+    margin-bottom: 20px;
+  }
+  .section-dark h2.section-title { color: #fff; }
+
+  .section-intro {
+    font-family: var(--sans);
+    font-size: 17px;
+    color: var(--ink-2);
+    max-width: 64ch;
+  }
+  .section-dark .section-intro { color: rgba(223, 240, 231, 0.85); }
+
+  /* ── HERO ── */
+  .hero {
+    position: relative;
+    min-height: calc(100vh - var(--nav-h));
+    padding: 80px 24px 96px;
+    display: flex;
+    align-items: center;
+    overflow: hidden;
+    background:
+      radial-gradient(ellipse 80% 60% at 80% 0%, rgba(45,122,79,0.10) 0%, transparent 60%),
+      radial-gradient(ellipse 60% 50% at 0% 100%, rgba(27,94,59,0.08) 0%, transparent 55%),
+      var(--page);
+  }
+  .hero-inner {
+    max-width: var(--max-w);
+    width: 100%;
+    margin: 0 auto;
+    display: grid;
+    grid-template-columns: 1fr;
+    gap: 28px;
+    position: relative;
+  }
+  .hero-eyebrow {
+    font-family: var(--mono);
+    font-size: 13px;
+    color: var(--g700);
+    letter-spacing: 0.08em;
+    display: inline-flex;
+    align-items: center;
+    gap: 10px;
+  }
+  .hero-eyebrow::before {
+    content: "▍";
+    color: var(--g500);
+    font-weight: 700;
+  }
+  .hero-name {
+    font-family: var(--serif);
+    font-size: clamp(48px, 8vw, 96px);
+    font-weight: 600;
+    line-height: 0.95;
+    letter-spacing: -0.04em;
+    color: var(--ink-1);
+  }
+  .hero-name em {
+    font-style: italic;
+    font-weight: 400;
+    color: var(--g700);
+  }
+  .hero-title {
+    font-family: var(--mono);
+    font-size: clamp(14px, 1.6vw, 17px);
+    color: var(--ink-2);
+    font-weight: 500;
+    letter-spacing: 0.02em;
+  }
+  .hero-title .sep { color: var(--g500); margin: 0 8px; }
+  .hero-tagline {
+    font-family: var(--serif);
+    font-style: italic;
+    font-weight: 300;
+    font-size: clamp(20px, 2.6vw, 28px);
+    line-height: 1.35;
+    color: var(--ink-2);
+    max-width: 56ch;
+    border-left: 3px solid var(--g500);
+    padding-left: 20px;
+  }
+  .hero-cta {
+    display: flex;
+    gap: 14px;
+    flex-wrap: wrap;
+    margin-top: 12px;
+  }
+  .btn {
+    display: inline-flex;
+    align-items: center;
+    gap: 10px;
+    font-family: var(--mono);
+    font-size: 14px;
+    font-weight: 600;
+    letter-spacing: 0.02em;
+    padding: 14px 22px;
+    border-radius: var(--r-md);
+    border: 1px solid transparent;
+    transition: transform .18s ease, background .18s ease, color .18s ease, border-color .18s ease, box-shadow .18s ease;
+    text-decoration: none;
+  }
+  .btn-primary {
+    background: var(--dark);
+    color: var(--g100);
+    border-color: var(--dark);
+    box-shadow: var(--shadow-sm);
+  }
+  .btn-primary:hover {
+    background: var(--g700);
+    border-color: var(--g700);
+    color: #fff;
+    transform: translateY(-1px);
+    box-shadow: var(--shadow-md);
+  }
+  .btn-secondary {
+    background: transparent;
+    color: var(--dark);
+    border-color: var(--rule);
+  }
+  .btn-secondary:hover {
+    border-color: var(--g500);
+    color: var(--g700);
+    background: var(--g050);
+    transform: translateY(-1px);
+  }
+  .btn .arrow {
+    width: 14px; height: 14px;
+    transition: transform .18s ease;
+  }
+  .btn:hover .arrow { transform: translate(2px, -2px); }
+
+  .hero-meta {
+    display: flex;
+    flex-wrap: wrap;
+    gap: 24px;
+    margin-top: 16px;
+    font-family: var(--mono);
+    font-size: 12px;
+    color: var(--ink-3);
+    letter-spacing: 0.04em;
+  }
+  .hero-meta span { display: inline-flex; align-items: center; gap: 8px; }
+  .hero-meta .dot {
+    display: inline-block;
+    width: 6px; height: 6px;
+    border-radius: 50%;
+    background: var(--g500);
+  }
+
+  /* Stagger entrance */
+  .reveal { opacity: 0; transform: translateY(14px); animation: rise .8s cubic-bezier(.2,.7,.2,1) forwards; }
+  .reveal.d1 { animation-delay: .08s; }
+  .reveal.d2 { animation-delay: .18s; }
+  .reveal.d3 { animation-delay: .30s; }
+  .reveal.d4 { animation-delay: .42s; }
+  .reveal.d5 { animation-delay: .55s; }
+  .reveal.d6 { animation-delay: .68s; }
+  @keyframes rise {
+    to { opacity: 1; transform: translateY(0); }
+  }
+  @media (prefers-reduced-motion: reduce) {
+    .reveal { opacity: 1; transform: none; animation: none; }
+    .brand::before { animation: none; }
+    html { scroll-behavior: auto; }
+  }
+
+  /* ── ABOUT ── */
+  .about {
+    background: var(--dark);
+    background-image:
+      radial-gradient(ellipse 60% 50% at 100% 0%, rgba(45,122,79,0.16) 0%, transparent 55%),
+      radial-gradient(ellipse 50% 40% at 0% 100%, rgba(27,94,59,0.12) 0%, transparent 60%);
+    color: #fff;
+    position: relative;
+  }
+  .about::before {
+    content: ""; position: absolute; left: 0; top: 0; bottom: 0;
+    width: 3px;
+    background: linear-gradient(to bottom, var(--g300), var(--g700));
+  }
+  .about-grid {
+    display: grid;
+    grid-template-columns: 1fr;
+    gap: 32px;
+    align-items: start;
+  }
+  .about-text p {
+    font-size: 18px;
+    line-height: 1.65;
+    color: rgba(223, 240, 231, 0.92);
+    margin-bottom: 16px;
+    max-width: 62ch;
+  }
+  .about-text strong { color: var(--g300); font-weight: 500; }
+  .about-callout {
+    border: 1px solid rgba(106,185,138,0.25);
+    border-radius: var(--r-md);
+    padding: 20px 22px;
+    background: rgba(13, 27, 18, 0.4);
+  }
+  .about-callout dt {
+    font-family: var(--mono);
+    font-size: 11px;
+    color: var(--g300);
+    letter-spacing: 0.12em;
+    text-transform: uppercase;
+    margin-bottom: 6px;
+  }
+  .about-callout dd {
+    font-family: var(--sans);
+    font-size: 15px;
+    color: var(--g100);
+    margin-bottom: 14px;
+  }
+  .about-callout dd:last-child { margin-bottom: 0; }
+  .about-callout a { color: var(--g300); }
+  .about-callout a:hover { color: var(--g100); }
+
+  @media (min-width: 768px) {
+    .about-grid { grid-template-columns: 1.6fr 1fr; gap: 56px; }
+  }
+
+  /* ── STACK ── */
+  .stack { background: var(--page); }
+  .stack-grid {
+    display: grid;
+    grid-template-columns: 1fr;
+    gap: 20px;
+    margin-top: 40px;
+  }
+  .stack-card {
+    background: #fff;
+    border: 1px solid var(--rule);
+    border-radius: var(--r-md);
+    padding: 24px;
+    transition: border-color .25s ease, transform .25s ease, box-shadow .25s ease;
+  }
+  .stack-card:hover {
+    border-color: var(--g300);
+    transform: translateY(-2px);
+    box-shadow: var(--shadow-md);
+  }
+  .stack-card-head {
+    display: flex;
+    align-items: baseline;
+    justify-content: space-between;
+    margin-bottom: 16px;
+    padding-bottom: 12px;
+    border-bottom: 1px dashed var(--rule);
+  }
+  .stack-card h3 {
+    font-family: var(--serif);
+    font-weight: 600;
+    font-size: 19px;
+    color: var(--ink-1);
+    letter-spacing: -0.01em;
+  }
+  .stack-card-tag {
+    font-family: var(--mono);
+    font-size: 11px;
+    color: var(--g500);
+    letter-spacing: 0.1em;
+  }
+  .pills {
+    display: flex;
+    flex-wrap: wrap;
+    gap: 8px;
+    list-style: none;
+  }
+  .pill {
+    display: inline-block;
+    font-family: var(--mono);
+    font-size: 12px;
+    font-weight: 500;
+    color: var(--g900);
+    background: var(--g050);
+    border: 1px solid var(--g100);
+    padding: 6px 12px;
+    border-radius: var(--r-pill);
+    transition: background .18s ease, border-color .18s ease, color .18s ease;
+  }
+  .pill:hover {
+    background: var(--g100);
+    border-color: var(--g300);
+    color: var(--g700);
+  }
+
+  @media (min-width: 768px) { .stack-grid { grid-template-columns: repeat(2, 1fr); } }
+  @media (min-width: 1200px) { .stack-grid { grid-template-columns: repeat(3, 1fr); } }
+
+  /* ── EXPERIENCE ── */
+  .experience { background: var(--g050); }
+  .timeline {
+    position: relative;
+    margin-top: 40px;
+    padding-left: 28px;
+    border-left: 2px solid var(--g100);
+  }
+  .timeline-item {
+    position: relative;
+    padding: 0 0 36px 24px;
+  }
+  .timeline-item:last-child { padding-bottom: 0; }
+  .timeline-item::before {
+    content: "";
+    position: absolute;
+    left: -34px; top: 6px;
+    width: 12px; height: 12px;
+    border-radius: 50%;
+    background: var(--page);
+    border: 2px solid var(--g500);
+    box-shadow: 0 0 0 4px var(--g050);
+  }
+  .timeline-item.current::before {
+    background: var(--g500);
+    box-shadow: 0 0 0 4px var(--g050), 0 0 0 8px rgba(45,122,79,0.18);
+  }
+  .timeline-meta {
+    display: flex;
+    flex-wrap: wrap;
+    align-items: baseline;
+    gap: 12px;
+    margin-bottom: 8px;
+    font-family: var(--mono);
+    font-size: 12px;
+    color: var(--ink-3);
+    letter-spacing: 0.04em;
+  }
+  .timeline-meta .period {
+    color: var(--g700);
+    font-weight: 600;
+  }
+  .timeline-meta .badge {
+    display: inline-block;
+    background: var(--g100);
+    color: var(--g700);
+    padding: 2px 8px;
+    border-radius: var(--r-pill);
+    font-size: 10px;
+    font-weight: 600;
+    letter-spacing: 0.08em;
+    text-transform: uppercase;
+  }
+  .timeline-item h3 {
+    font-family: var(--serif);
+    font-weight: 600;
+    font-size: 22px;
+    color: var(--ink-1);
+    margin-bottom: 4px;
+    letter-spacing: -0.01em;
+  }
+  .timeline-item h3 a {
+    color: inherit;
+    border-bottom: 1px solid transparent;
+    transition: border-color .18s ease, color .18s ease;
+  }
+  .timeline-item h3 a:hover { color: var(--g700); border-color: var(--g500); }
+  .timeline-role {
+    font-family: var(--mono);
+    font-size: 13px;
+    color: var(--g700);
+    margin-bottom: 12px;
+  }
+  .timeline-desc {
+    color: var(--ink-2);
+    line-height: 1.6;
+    max-width: 64ch;
+  }
+
+  /* ── CONTACT ── */
+  .contact {
+    background: var(--dark);
+    color: #fff;
+    position: relative;
+    overflow: hidden;
+  }
+  .contact::after {
+    content: "";
+    position: absolute;
+    right: -120px; bottom: -120px;
+    width: 360px; height: 360px;
+    border-radius: 50%;
+    background: radial-gradient(circle, rgba(45,122,79,0.18) 0%, transparent 70%);
+    pointer-events: none;
+  }
+  .contact-grid {
+    display: grid;
+    grid-template-columns: 1fr;
+    gap: 32px;
+    position: relative;
+    z-index: 1;
+  }
+  .contact-list {
+    list-style: none;
+    display: grid;
+    grid-template-columns: 1fr;
+    gap: 12px;
+    margin-top: 32px;
+  }
+  .contact-row {
+    display: grid;
+    grid-template-columns: 28px 1fr;
+    gap: 18px;
+    align-items: center;
+    padding: 18px 20px;
+    background: rgba(255, 255, 255, 0.04);
+    border: 1px solid rgba(106,185,138,0.18);
+    border-radius: var(--r-md);
+    transition: border-color .18s ease, background .18s ease, transform .18s ease;
+    color: var(--g100);
+  }
+  .contact-row:hover {
+    border-color: var(--g300);
+    background: rgba(106,185,138,0.08);
+    transform: translateY(-1px);
+    color: #fff;
+  }
+  .contact-row svg { width: 20px; height: 20px; color: var(--g300); flex-shrink: 0; }
+  .contact-row .label {
+    display: block;
+    font-family: var(--mono);
+    font-size: 11px;
+    color: var(--g300);
+    letter-spacing: 0.12em;
+    text-transform: uppercase;
+    margin-bottom: 2px;
+  }
+  .contact-row .value {
+    font-family: var(--mono);
+    font-size: 15px;
+    color: #fff;
+    font-weight: 500;
+    word-break: break-word;
+  }
+
+  .contact-side {
+    border: 1px solid rgba(106,185,138,0.2);
+    border-radius: var(--r-md);
+    padding: 24px;
+    background: rgba(13, 27, 18, 0.5);
+  }
+  .contact-side h3 {
+    font-family: var(--serif);
+    font-size: 20px;
+    color: #fff;
+    margin-bottom: 10px;
+  }
+  .contact-side p {
+    color: rgba(223, 240, 231, 0.85);
+    font-size: 14px;
+    line-height: 1.6;
+    margin-bottom: 16px;
+  }
+  .contact-side .education {
+    list-style: none;
+    border-top: 1px solid rgba(106,185,138,0.15);
+    padding-top: 16px;
+    margin-top: 16px;
+  }
+  .contact-side .education li {
+    font-family: var(--mono);
+    font-size: 12px;
+    color: rgba(223, 240, 231, 0.7);
+    margin-bottom: 6px;
+  }
+  .contact-side .education strong { color: var(--g100); font-weight: 600; }
+
+  @media (min-width: 768px) {
+    .contact-grid { grid-template-columns: 1.4fr 1fr; gap: 48px; }
+  }
+
+  /* ── FOOTER ── */
+  .footer {
+    background: #061008;
+    color: rgba(223, 240, 231, 0.55);
+    padding: 32px 24px;
+    border-top: 1px solid rgba(106,185,138,0.1);
+  }
+  .footer-inner {
+    max-width: var(--max-w);
+    margin: 0 auto;
+    display: flex;
+    flex-wrap: wrap;
+    gap: 16px 32px;
+    justify-content: space-between;
+    align-items: center;
+    font-family: var(--mono);
+    font-size: 12px;
+    letter-spacing: 0.04em;
+  }
+  .footer a { color: var(--g300); }
+  .footer a:hover { color: var(--g100); }
+  .footer-links { display: flex; gap: 20px; flex-wrap: wrap; }
+
+  /* ── RESPONSIVE TWEAKS ── */
+  @media (max-width: 768px) {
+    section { padding: 72px 20px; }
+    .hero { padding: 56px 20px 72px; }
+    .hero-cta .btn { flex: 1 1 auto; justify-content: center; }
+    .timeline { padding-left: 22px; }
+    .timeline-item { padding-left: 18px; }
+    .timeline-item::before { left: -29px; }
+  }
+
+  @media (min-width: 1200px) {
+    section { padding: 120px 24px; }
+  }
+
+</style>
+</head>
+<body>
+
+<!-- NAV -->
+<header class="nav" role="banner">
+  <div class="nav-inner">
+    <a href="#hero" class="brand" aria-label="Bastien Chanot — accueil">bchanot.fr</a>
+    <button class="nav-toggle" type="button" aria-label="Ouvrir le menu" aria-expanded="false" aria-controls="primary-nav">
+      <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>
+    </button>
+    <nav aria-label="Navigation principale">
+      <ul id="primary-nav" class="nav-links">
+        <li><a href="#about">À propos</a></li>
+        <li><a href="#stack">Stack</a></li>
+        <li><a href="#experience">Parcours</a></li>
+        <li><a href="#contact">Contact</a></li>
+      </ul>
+    </nav>
+  </div>
+</header>
+
+<main>
+
+  <!-- HERO -->
+  <section id="hero" class="hero" aria-labelledby="hero-name">
+    <div class="hero-inner">
+      <p class="hero-eyebrow reveal">Disponible — CDI systèmes&nbsp;/&nbsp;embarqué · missions freelance</p>
+      <h1 id="hero-name" class="hero-name reveal d1">Bastien&nbsp;<em>Chanot</em></h1>
+      <p class="hero-title reveal d2">Développeur confirmé<span class="sep">·</span>Systèmes &amp; Backend</p>
+      <p class="hero-tagline reveal d3">Du kernel Linux au backend Rust — 7 ans de développement systèmes en production.</p>
+      <div class="hero-cta reveal d4">
+        <a class="btn btn-primary" href="#contact">
+          Me contacter
+          <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>
+        </a>
+        <a class="btn btn-secondary" href="CV_Bastien_Chanot.html">
+          Voir le CV
+          <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>
+        </a>
+        <a class="btn btn-secondary" href="CV_Bastien_Chanot.pdf" download>
+          Télécharger PDF
+          <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>
+        </a>
+      </div>
+      <div class="hero-meta reveal d5">
+        <span><span class="dot"></span>C · Rust · Linux Kernel</span>
+        <span><span class="dot"></span>AOSP · Embarqué</span>
+        <span><span class="dot"></span>Backend · DevOps</span>
+      </div>
+    </div>
+  </section>
+
+  <!-- ABOUT -->
+  <section id="about" class="about section-dark" aria-labelledby="about-title">
+    <div class="container">
+      <span class="section-label">À propos</span>
+      <h2 id="about-title" class="section-title">Sept ans à écrire du code qui tourne là où ça compte.</h2>
+      <div class="about-grid">
+        <div class="about-text">
+          <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>
+          <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>
+          <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>
+          <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>
+        </div>
+        <dl class="about-callout">
+          <dt>Recherche prioritaire</dt>
+          <dd>CDI systèmes embarqués / logiciel</dd>
+          <dt>En parallèle</dt>
+          <dd>Missions freelance · ZenQuality</dd>
+          <dt>Localisation actuelle</dt>
+          <dd>Yerres (91) · mobilité Pays de la Loire</dd>
+          <dt>Présence</dt>
+          <dd>Full remote · ou 1–2 j/mois si Paris</dd>
+          <dt>Site pro</dt>
+          <dd><a href="https://zenquality.fr" target="_blank" rel="noopener">zenquality.fr&nbsp;↗</a></dd>
+        </dl>
+      </div>
+    </div>
+  </section>
+
+  <!-- STACK -->
+  <section id="stack" class="stack" aria-labelledby="stack-title">
+    <div class="container">
+      <span class="section-label">Stack technique</span>
+      <h2 id="stack-title" class="section-title">Ce avec quoi je travaille, sans le marketing.</h2>
+      <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>
+      <div class="stack-grid">
+
+        <article class="stack-card">
+          <header class="stack-card-head">
+            <h3>Langages</h3>
+            <span class="stack-card-tag">// 01</span>
+          </header>
+          <ul class="pills">
+            <li class="pill">C</li>
+            <li class="pill">Rust</li>
+            <li class="pill">Java</li>
+            <li class="pill">Bash</li>
+            <li class="pill">Python</li>
+          </ul>
+        </article>
+
+        <article class="stack-card">
+          <header class="stack-card-head">
+            <h3>Embarqué</h3>
+            <span class="stack-card-tag">// 02</span>
+          </header>
+          <ul class="pills">
+            <li class="pill">Linux kernel drivers</li>
+            <li class="pill">AOSP</li>
+            <li class="pill">ARM / x86</li>
+            <li class="pill">GPIO</li>
+            <li class="pill">NFC</li>
+            <li class="pill">ESC/POS</li>
+            <li class="pill">cross-compilation GCC</li>
+          </ul>
+        </article>
+
+        <article class="stack-card">
+          <header class="stack-card-head">
+            <h3>Conteneurs</h3>
+            <span class="stack-card-tag">// 03</span>
+          </header>
+          <ul class="pills">
+            <li class="pill">Docker</li>
+            <li class="pill">LXC / LXD</li>
+            <li class="pill">QEMU</li>
+            <li class="pill">VMware</li>
+          </ul>
+        </article>
+
+        <article class="stack-card">
+          <header class="stack-card-head">
+            <h3>Backend</h3>
+            <span class="stack-card-tag">// 04</span>
+          </header>
+          <ul class="pills">
+            <li class="pill">Rust</li>
+            <li class="pill">WebSocket</li>
+            <li class="pill">GPU bare-metal</li>
+            <li class="pill">ramdisk I/O</li>
+          </ul>
+        </article>
+
+        <article class="stack-card">
+          <header class="stack-card-head">
+            <h3>Systèmes</h3>
+            <span class="stack-card-tag">// 05</span>
+          </header>
+          <ul class="pills">
+            <li class="pill">Linux bare-metal</li>
+            <li class="pill">AOSP</li>
+            <li class="pill">Android Backup</li>
+            <li class="pill">systemd</li>
+          </ul>
+        </article>
+
+        <article class="stack-card">
+          <header class="stack-card-head">
+            <h3>DevOps</h3>
+            <span class="stack-card-tag">// 06</span>
+          </header>
+          <ul class="pills">
+            <li class="pill">Git</li>
+            <li class="pill">GitHub / GitLab</li>
+            <li class="pill">CI / CD</li>
+            <li class="pill">Gitflow</li>
+            <li class="pill">Agile</li>
+          </ul>
+        </article>
+
+      </div>
+    </div>
+  </section>
+
+  <!-- EXPERIENCE -->
+  <section id="experience" class="experience" aria-labelledby="experience-title">
+    <div class="container">
+      <span class="section-label">Parcours</span>
+      <h2 id="experience-title" class="section-title">Trois expériences qui résument l'essentiel.</h2>
+      <p class="section-intro">Sept ans de développement systèmes en production — du kernel au backend, de la puce au serveur.</p>
+
+      <ol class="timeline">
+
+        <li class="timeline-item current">
+          <div class="timeline-meta">
+            <span class="period">avr.&nbsp;2026 — présent</span>
+            <span class="badge">En cours</span>
+            <span>Yerres · Full remote</span>
+          </div>
+          <h3><a href="https://zenquality.fr" target="_blank" rel="noopener">ZenQuality</a></h3>
+          <p class="timeline-role">Développeur indépendant · Systèmes &amp; Backend</p>
+          <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>
+        </li>
+
+        <li class="timeline-item">
+          <div class="timeline-meta">
+            <span class="period">mars&nbsp;2019 — mars&nbsp;2025</span>
+            <span>Paris · Full remote dès 2020</span>
+          </div>
+          <h3>CareGame</h3>
+          <p class="timeline-role">Développeur systèmes &amp; backend</p>
+          <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>
+        </li>
+
+        <li class="timeline-item">
+          <div class="timeline-meta">
+            <span class="period">2017</span>
+            <span>Ivry-sur-Seine</span>
+          </div>
+          <h3>Deewee</h3>
+          <p class="timeline-role">Développeur C embarqué</p>
+          <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>
+        </li>
+
+      </ol>
+    </div>
+  </section>
+
+  <!-- CONTACT -->
+  <section id="contact" class="contact section-dark" aria-labelledby="contact-title">
+    <div class="container">
+      <span class="section-label">Contact</span>
+      <h2 id="contact-title" class="section-title">Une mission, une question technique, un&nbsp;projet&nbsp;?</h2>
+      <p class="section-intro">Le plus simple : l'email. Réponse sous 48&nbsp;h ouvrées.</p>
+
+      <div class="contact-grid">
+        <ul class="contact-list">
+
+          <li>
+            <a class="contact-row" href="mailto:chanot.bastien@gmail.com">
+              <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>
+              <span>
+                <span class="label">Email</span>
+                <span class="value">chanot.bastien@gmail.com</span>
+              </span>
+            </a>
+          </li>
+
+          <li>
+            <a class="contact-row" href="tel:+33778822297">
+              <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>
+              <span>
+                <span class="label">Téléphone</span>
+                <span class="value">+33 7 78 82 22 97</span>
+              </span>
+            </a>
+          </li>
+
+          <li>
+            <a class="contact-row" href="https://zenquality.fr" target="_blank" rel="noopener">
+              <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>
+              <span>
+                <span class="label">Site pro</span>
+                <span class="value">zenquality.fr</span>
+              </span>
+            </a>
+          </li>
+
+          <li>
+            <a class="contact-row" href="CV_Bastien_Chanot.pdf" download>
+              <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>
+              <span>
+                <span class="label">CV</span>
+                <span class="value">Télécharger le PDF</span>
+              </span>
+            </a>
+          </li>
+
+        </ul>
+
+        <aside class="contact-side">
+          <h3>Formation</h3>
+          <p>Formation par la pratique et le bas niveau — kernel, mémoire, shell, sécurité.</p>
+          <ul class="education">
+            <li><strong>École 42</strong> · Clichy · 2015–2019<br>kernel, malloc, shell, sécurité</li>
+            <li><strong>TSRIT</strong> · Next Formation · 2013–2015<br>Félicitations du jury</li>
+          </ul>
+        </aside>
+      </div>
+    </div>
+  </section>
+
+</main>
+
+<!-- FOOTER -->
+<footer class="footer" role="contentinfo">
+  <div class="footer-inner">
+    <span>© 2026 · Bastien Chanot · bchanot.fr</span>
+    <div class="footer-links">
+      <a href="CV_Bastien_Chanot.html">CV (HTML)</a>
+      <a href="CV_Bastien_Chanot.pdf" download>CV (PDF)</a>
+      <a href="https://zenquality.fr" target="_blank" rel="noopener">zenquality.fr ↗</a>
+    </div>
+  </div>
+</footer>
+
+<script>
+  // Mobile nav toggle
+  (function () {
+    const toggle = document.querySelector('.nav-toggle');
+    const links  = document.getElementById('primary-nav');
+    if (!toggle || !links) return;
+
+    toggle.addEventListener('click', () => {
+      const open = links.classList.toggle('open');
+      toggle.setAttribute('aria-expanded', String(open));
+      toggle.setAttribute('aria-label', open ? 'Fermer le menu' : 'Ouvrir le menu');
+    });
+
+    links.addEventListener('click', (e) => {
+      if (e.target instanceof HTMLAnchorElement && links.classList.contains('open')) {
+        links.classList.remove('open');
+        toggle.setAttribute('aria-expanded', 'false');
+        toggle.setAttribute('aria-label', 'Ouvrir le menu');
+      }
+    });
+  })();
+
+  // Active section highlight in nav (subtle — color shift only)
+  (function () {
+    const sections = document.querySelectorAll('main section[id]');
+    const navLinks = document.querySelectorAll('.nav-links a[href^="#"]');
+    if (!('IntersectionObserver' in window) || !sections.length) return;
+
+    const map = new Map();
+    navLinks.forEach(a => {
+      const id = a.getAttribute('href').slice(1);
+      map.set(id, a);
+    });
+
+    const io = new IntersectionObserver((entries) => {
+      entries.forEach(entry => {
+        const link = map.get(entry.target.id);
+        if (!link) return;
+        if (entry.isIntersecting) {
+          navLinks.forEach(a => a.style.color = '');
+          link.style.color = 'var(--g100)';
+        }
+      });
+    }, { rootMargin: '-40% 0px -55% 0px', threshold: 0 });
+
+    sections.forEach(s => io.observe(s));
+  })();
+</script>
+
+</body>
+</html>