/* ==========================================================================
   style.css — CV Online 2026
   --------------------------------------------------------------------------
   SOMMAIRE (Ctrl+F pour naviguer) :
   1)  VARIABLES THÈME     → :root + :root[data-theme="light"]
   2)  BASE                → reset, body, liens, container, utilitaires
   3)  TOPBAR              → brand / nav / actions
   4)  THEME TOGGLE        → bouton light/dark
   5)  BOUTONS             → .btn et variantes
   6)  TOAST               → notification de téléchargement
   7)  HERO                → titre, avatar, quick-info, chips, CTA
   8)  SECTIONS            → .section, .grid, .card, listes, chips/tags
   9)  REVEAL              → animations "puzzle" (apparition au scroll)
   10) TILT + GLARE        → effet 3D hover (desktop uniquement)
   11) TIMELINE            → expériences / frise chronologique
   12) MATRICE             → compétences (barres de progression)
   13) SWITCH LANGUES      → bouton radial flottant
   14) SCROLL SPY          → lien navbar actif (desktop + mobile)
   15) SCROLL HINT         → invite au scroll mobile (souris animée)
   16) SCROLL TO TOP       → bouton retour en haut flottant
   17) FORMULAIRE CONTACT  → layout, inputs, feedback, bouton centré
   18) TYPEWRITER          → messages rotatifs animés (contact)
   19) DÉCOR               → bg-blobs, noise overlay
   20) RESPONSIVE / MOBILE → @media regroupés (max-width : 900 / 820 / 480 / 420)
   21) ACCESSIBILITÉ       → prefers-reduced-motion
   ========================================================================== */


/* ========================================================================== */
/* 1) VARIABLES THÈME                                                          */
/* ========================================================================== */

/* ── Thème DARK (par défaut — aucun attribut sur <html>) ── */
:root {
  /* Couleurs de fond et panneaux */
  --bg:       #0b0f17;
  --panel:    rgba(255, 255, 255, .06);
  --panel2:   rgba(255, 255, 255, .08);

  /* Texte */
  --text:     rgba(255, 255, 255, .92);
  --muted:    rgba(255, 255, 255, .64);

  /* Bordures et ombres */
  --border:   rgba(255, 255, 255, .12);
  --shadow:   0 16px 60px rgba(0, 0, 0, .35);
  --shadow-float: 0 10px 28px rgba(0, 0, 0, .25);

  /* Accents */
  --primary:  #7c3aed;               /* violet */
  --primary2: #22c55e;               /* vert */

  /* Gradient spécial quick-row : lueur violette subtile qui s'estompe vers la droite */
  --quick-row: linear-gradient(90deg,
    color-mix(in oklab, var(--primary) 6%, transparent) 0%,
    transparent 70%
  );

  /* Layout */
  --radius:   18px;
  --max:      1100px;
  --font:     "Inter", system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif;

  /* Topbar — valeurs par défaut (recalculées en JS au chargement) */
  --topbar-h:   74px;                /* hauteur de la topbar (mise à jour en JS) */
  --topbar-gap: 14px;                /* espace entre le bord écran et la topbar  */

  /* Espace entre la topbar et le début du contenu (hero / h1)
     Desktop      → --hero-top: 25px
     Mobile ≤900  → redéfini dans @media (voir section 15)
     Mobile ≤480  → redéfini dans @media (voir section 15) */
  --hero-top: 25px;

  /* Espace entre la colonne texte/matrice et la hero-card (côté droit)
     Sur tablette/mobile en layout 1 colonne, devient l'espace entre
     le bloc texte+matrice et la hero-card empilée en dessous
     Desktop      → --hero-gap: 22px
     Tablette ≤900 → redéfini dans @media (voir section 15) */
  --hero-gap: 22px;

  /* Position du bouton langue flottant — partagée avec le bouton scroll-to-top */
  --orbit-right:  18px;
  --orbit-bottom: 18px;
}

/* ── Thème LIGHT → :root[data-theme="light"] ── */
:root[data-theme="light"] {
  --bg:       #f6f7fb;
  --panel:    rgba(0, 0, 0, .04);
  --panel2:   rgba(0, 0, 0, .06);
  --text:     rgba(0, 0, 0, .88);
  --muted:    rgba(0, 0, 0, .56);
  --border:   rgba(0, 0, 0, .10);
  --shadow:   0 16px 60px rgba(0, 0, 0, .10);
}


/* ========================================================================== */
/* 2) BASE : reset, body, liens, container, utilitaires                        */
/* ========================================================================== */

* { box-sizing: border-box; }
html, body { height: 100%; }
main.container { min-height: 100dvh; }

body {
  margin: 0;
  font-family: var(--font);

  /* Gradient de fond en 3 couches + couleur solide de secours.
     NOTE : background-attachment:fixed n'est PAS supporté sur iOS Safari
     (il est ignoré silencieusement) — le fond défilera normalement sur mobile,
     ce qui est visuellement acceptable. */
  background:
    radial-gradient(1200px 700px at 12% -10%,  rgba(124,  58, 237, .22), transparent 55%),
    radial-gradient(1200px 700px at 90%  10%,  rgba( 34, 197,  94, .18), transparent 55%),
    radial-gradient(230%   40%  at 10%  102%,  rgba( 10, 141, 255, .10), transparent 30%),
    var(--bg);
  background-repeat:     no-repeat;
  background-attachment: fixed;       /* ignoré sur iOS Safari (comportement normal) */

  color:      var(--text);
  overflow-x: hidden;
}

/* Liens */
a { color: inherit; text-decoration: none; }
a:hover { text-decoration: underline; text-underline-offset: 3px; }

/* Utilitaires couleur */
.muted   { color: var(--muted); }
strong   { color: var(--text); }

/* Images : anti-flash pendant les hot-reloads (Live Server) */
img {
  max-width: 100%;
  height:    auto;
  display:   block;
}

/* Conteneur principal centré */
.container {
  width:          min(var(--max), calc(100% - 40px));
  margin:         0 auto;
  padding:        calc(var(--topbar-h) + var(--topbar-gap) + var(--hero-top)) 0 0;
  display:        flex;
  flex-direction: column;
}

.typewriter-spacer {
  flex:            1;
  display:         flex;
  align-items:     center;
  justify-content: center;
  overflow:        hidden;
}

/* ── Offset de scroll pour les ancres (fix topbar fixe)  ──────────────────
   Problème : quand on clique sur "Expériences" dans la topbar, le navigateur
   scrolle jusqu'à ce que le HAUT de la section touche y=0 — mais la topbar
   fixe est par-dessus, ce qui cache le titre.

   Solution : scroll-padding-top sur <html> est la méthode générique.
   Elle s'applique UNE SEULE FOIS et affecte TOUS les liens d'ancrage
   de la page de façon uniforme — tous les titres de section atterriront
   exactement au même endroit vertical, sans exception.

   Pourquoi pas scroll-margin-top sur chaque section ?
   → Doit être répété sur chaque cible et peut donner des résultats
     légèrement différents selon la structure interne de chaque section.
   → scroll-padding-top sur html est déclaratif : "le viewport commence
     à Y px du haut" — le navigateur fait le reste, uniformément.
   ─────────────────────────────────────────────────────────────────────── */
html {
  scroll-padding-top: calc(var(--topbar-h) + var(--topbar-gap) + 16px);
}

/* ── Utilitaire : label avec icône inline */
.label {
  display:     inline-flex;
  align-items: center;
  gap:         8px;
}

/* Utilitaire : wrapper emoji aligné */
.emoji {
  display:         inline-flex;
  align-items:     center;
  justify-content: center;
  font-size:       1.05em;
  line-height:     1.2;
}

/* Icône dans un emoji (le display:block est volontaire pour le centrage) */
.emoji .icon {
  display:         block;          /* NOTE : inline-flex serait écrasé ici, on garde block */
  width:           22px;
  height:          22px;
}


/* ========================================================================== */
/* 3) TOPBAR                                                                   */
/* ========================================================================== */

.topbar {
  /* ── Position fixe en haut ── */
  position: fixed;
  top:      var(--topbar-gap);   /* 14px par défaut */
  left:     0;
  right:    0;
  z-index:  50;

  /* ── Dimensions et centrage ── */
  width:  min(var(--max), calc(100% - 28px));
  margin: 0 auto;

  /* ── Disposition interne ── */
  display:         flex;
  align-items:     center;
  justify-content: space-between;
  gap:             12px;
  padding:         10px 12px;

  /* ── Aspect visuel ── */
  background:      color-mix(in oklab, var(--panel) 88%, transparent);
  border:          1px solid var(--border);
  border-radius:   999px;
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  box-shadow:      var(--shadow);

  /* ──────────────────────────────────────────────────────────────────────
     FIX iOS Safari : "topbar qui bouge au scroll"
     ─────────────────────────────────────────────────────────────────────
     Sur iPhone/iPad, la barre d'URL du navigateur se rétracte au scroll,
     ce qui modifie la hauteur de la viewport visible (100dvh vs 100vh).
     Cela provoque un recalcul du layout et fait "sauter" les éléments
     en position:fixed sur certaines versions de Safari.

     Solution : forcer la topbar sur son propre compositing layer GPU.
     Le navigateur ne recalcule plus sa position dans le flux normal,
     ce qui supprime le saut.

     will-change: transform est un complément recommandé pour prévenir
     toute réintégration dans le flux par le moteur de rendu.
  ────────────────────────────────────────────────────────────────────── */
  -webkit-transform: translateZ(0);     /* préfixe Safari */
  transform:         translateZ(0);     /* force le compositing layer GPU */
  will-change:       transform;         /* empêche la réintégration dans le flux */
}

/* ── Marque / logo textuel ── */
.brand {
  display:     flex;
  align-items: center;
  gap:         10px;
  font-weight: 650;
  letter-spacing: -0.02em;
}

/* Point coloré à côté du nom */
.brand-dot {
  width:         10px;
  height:        10px;
  border-radius: 999px;
  background:    linear-gradient(135deg, var(--primary), var(--primary2));
  box-shadow:    0 0 0 6px color-mix(in oklab, var(--primary) 22%, transparent);
}

/* ── Navigation desktop ── */
.nav {
  display: flex;
  gap:     14px;
  padding: 0 10px;
}
.nav a {
  color:       var(--muted);
  font-weight: 520;
  font-size:   14px;
}
.nav a:hover,
.nav a.is-active { color: var(--text); text-decoration: none; }

.nav a.is-active {
  color:       var(--text);
  font-weight: 600;
}

/* ── Zone des boutons actions (thème + download + burger) ── */
.actions { display: flex; align-items: center; gap: 10px; }

/* Icône burger (animation → croix quand ouvert) */
.burger-icon {
  display:    inline-block;
  transition: transform 180ms ease;
}
#btnMenu[aria-expanded="true"] .burger-icon {
  transform: rotate(90deg);
}


/* ========================================================================== */
/* 4) THEME TOGGLE                                                             */
/* ========================================================================== */

.theme-toggle {
  /* ── Variables internes (plus facile à modifier) ── */
  --w:     88px;       /* largeur totale */
  --h:     42px;       /* hauteur totale */
  --pad:   6px;        /* padding interne */
  --thumb: 30px;       /* taille de la pastille */

  position:   relative;
  width:      var(--w);
  height:     var(--h);
  padding:    var(--pad);
  border-radius: 999px;
  border:     1px solid var(--border);
  background: var(--panel);
  cursor:     pointer;
  user-select: none;
  overflow:   hidden;

  display:         flex;
  align-items:     center;
  justify-content: space-between;   /* slots ☀️ et 🌙 aux deux extrémités */

  transition: transform .15s ease, background .15s ease;
}

.theme-toggle:hover          { transform: translateY(-1px); background: var(--panel2); }
.theme-toggle:active         { transform: translateY(0); }
.theme-toggle:focus          { outline: none; }   /* géré via focus-visible ci-dessous */
.theme-toggle:focus-visible  {
  outline:        2px solid color-mix(in oklab, var(--primary) 70%, transparent);
  outline-offset: 3px;
}

/* Icônes ☀️ et 🌙 */
.theme-toggle .tt-icon {
  width:       var(--thumb);
  height:      var(--thumb);
  display:     grid;
  place-items: center;
  line-height: 1;
  z-index:     2;
  opacity:     .7;
  transform:   translateZ(0);   /* force le compositing sur les icônes aussi */
}

/* L'icône active brille davantage selon le thème */
:root[data-theme="light"]        .theme-toggle .tt-sun  { opacity: 1; }
:root:not([data-theme="light"])  .theme-toggle .tt-moon { opacity: 1; }

/* Pastille coulissante */
.theme-toggle .tt-thumb {
  position:      absolute;
  top:           50%;
  left:          var(--pad);    /* ← position light (à gauche = soleil) */
  right:         auto;
  width:         var(--thumb);
  height:        var(--thumb);
  border-radius: 999px;
  background:    linear-gradient(135deg, var(--primary), var(--primary2));
  box-shadow:    0 10px 24px rgba(0, 0, 0, .22);
  transform:     translate3d(0, -50%, 0);
  transition:    left .22s ease, right .22s ease;
  will-change:   left, right;
}

/* En mode dark : pastille ancrée à droite (vers la lune) */
:root:not([data-theme="light"]) .theme-toggle .tt-thumb {
  left:  auto;
  right: var(--pad);
}


/* ========================================================================== */
/* 5) BOUTONS                                                                  */
/* ========================================================================== */

/* ── Bouton de base ── */
.btn {
  border:          1px solid var(--border);
  background:      var(--panel);
  color:           var(--text);
  padding:         10px 12px;
  border-radius:   999px;
  display:         inline-flex;
  align-items:     center;
  justify-content: center;
  gap:             8px;
  font-weight:     600;
  font-size:       14px;
  cursor:          pointer;
  user-select:     none;
  transition:      transform .15s ease, background .15s ease, border-color .15s ease;
}
.btn:hover  { transform: translateY(-1px); background: var(--panel2); text-decoration: none; }
.btn:active { transform: translateY(0); }

/* ── Variantes ── */
.btn.primary {
  background:   linear-gradient(135deg, var(--primary), color-mix(in oklab, var(--primary2) 35%, var(--primary)));
  border-color: transparent;
}
.btn.primary:hover {
  background: linear-gradient(135deg, color-mix(in oklab, var(--primary) 85%, #fff), var(--primary2));
}

.btn.ghost  { background: transparent; }
.btn.icon   { width: 42px; height: 42px; padding: 0; }
.btn.small  { padding: 8px 10px; font-size: 13px; }

/* Burger : caché sur desktop.
   IMPORTANT : cette règle doit être APRÈS .btn (qui déclare display:inline-flex)
   pour avoir la priorité — même spécificité, l'ordre dans le fichier décide. */
.burger { display: none; }

/* ── Bouton Télécharger (desktop) ── */
.btn-download {
  display:     inline-flex;
  align-items: center;
  gap:         10px;
}
.btn-download .dl-img {
  height:  25px;       /* taille icône download — ajuster si besoin */
  width:   auto;
  display: block;
}


/* ========================================================================== */
/* 6) TOAST (notification de téléchargement)                                   */
/* ========================================================================== */

/* ── Overlay transparent (intercepte les clics extérieurs) ── */
.toast-overlay {
  position:       fixed;
  inset:          0;
  z-index:        9998;             /* juste en-dessous du toast */
  background:     transparent;
  opacity:        0;
  pointer-events: none;
}
.toast-overlay.show {
  opacity:        1;
  pointer-events: auto;
}

/* ── Toast lui-même ── */
.toast {
  position:  fixed;
  /* Position de départ (état caché) : légèrement au-dessus de sa cible */
  top:       calc(var(--topbar-h) + var(--topbar-gap) + 12px);
  left:      50%;
  transform: translate(-50%, -16px);   /* centré + décalé vers le haut */
  z-index:   9999;

  background:      var(--panel2);
  color:           var(--text);
  border:          2px solid var(--border);
  box-shadow:      var(--shadow-float);
  backdrop-filter: blur(10px);

  border-radius: 14px;
  padding:       12px 14px;
  max-width:     min(560px, calc(100vw - 32px));
  width:         max-content;
  overflow:      hidden;              /* la barre de progression ne déborde pas */

  opacity:        0;
  pointer-events: none;
  transition:     transform 220ms ease, opacity 220ms ease;
}

/* Toast ancré à droite sur desktop (aligné sur le bord droit du container) */
.toast.toast--anchored {
  left:      auto;
  right:     var(--toast-right, 16px);
  transform: translateY(-16px);
}
.toast.toast--anchored.show {
  transform: translateY(0);
}

/* État visible */
.toast.show {
  opacity:        1;
  transform:      translate(-50%, 0);
  pointer-events: auto;
}

/* Ligne contenu (texte + bouton fermer) */
.toast__row {
  display:     flex;
  align-items: center;
  gap:         10px;
  position:    relative;
  z-index:     1;   /* au-dessus de la barre de progression ::before */
}

.toast__text {
  font-size:   14px;
  line-height: 1.25;
  font-weight: 600;
  color:       var(--text);
}

.toast__close {
  appearance:  none;
  border:      0;
  background:  transparent;
  color:       var(--muted);
  cursor:      pointer;
  padding:     4px 6px;
  border-radius: 10px;
}
.toast__close:hover { background: var(--panel); color: var(--text); }

/* ── Barre de progression (::before animée via --p) ── */
/* --p va de 0 à 1 pendant la durée du toast (animé en JS par RAF) */
.toast::before {
  content:          "";
  position:         absolute;
  inset:            0;
  transform-origin: left;
  transform:        scaleX(var(--p, 0));
  z-index:          0;
  /* gradient par défaut — surchargé par les variantes ci-dessous */
  background: color-mix(in srgb, var(--primary) 40%, transparent);
}

/* Sheen subtil en surimpression */
.toast::after {
  content:        "";
  position:       absolute;
  inset:          0;
  z-index:        0;
  pointer-events: none;
  background:     radial-gradient(80% 60% at 20% 0%, rgba(255,255,255,.10), rgba(255,255,255,0) 60%);
  opacity:        .3;
}

/* ── Variantes info / success ── */
.toast.toast--info {
  border-color: color-mix(in srgb, var(--primary) 45%, var(--border));
}
.toast.toast--info::before {
  background: linear-gradient(
    90deg,
    color-mix(in srgb, var(--primary)  36%, transparent) 0%,
    color-mix(in srgb, var(--primary2) 30%, transparent) 100%
  );
}

.toast.toast--success {
  border-color: color-mix(in srgb, var(--primary2) 45%, var(--border));
}
.toast.toast--success::before {
  background: linear-gradient(
    90deg,
    color-mix(in srgb, var(--primary2) 32%, transparent) 0%,
    color-mix(in srgb, var(--primary2) 18%, transparent) 100%
  );
}


/* ========================================================================== */
/* 7) HERO                                                                     */
/* ========================================================================== */

/* ── Layout hero : 2 colonnes sur desktop ── */
.hero {
  display:               grid;
  grid-template-columns: 1.5fr 1fr;
  gap:                   var(--hero-gap);
  align-items:           start;
  margin-top:            5px;
}

/* Kicker (chip disponibilité) */
.kicker {
  display:       inline-flex;
  align-items:   center;
  gap:           10px;
  padding:       8px 12px;
  border:        1px solid var(--border);
  border-radius: 999px;
  background:    var(--panel);
  color:         var(--muted);
  font-weight:   650;
  width:         fit-content;
}

/* Titre principal */
h1 {
  font-size:      clamp(34px, 4vw, 54px);
  line-height:    1.06;
  margin:         0 0 10px;
  letter-spacing: -0.04em;
}

/* Texte en dégradé violet → vert */
.gradient {
  background:              linear-gradient(135deg, var(--primary), var(--primary2));
  -webkit-background-clip: text;
  background-clip:         text;
  color:                   transparent;
}

/* Paragraphe descriptif — pleine largeur de la colonne hero-left
   pour s'aligner avec le bord droit de la matrice de compétences */
.lead {
  font-size:    16px;
  text-align:   justify;
  text-justify: inter-word;
  hyphens:      auto;
  line-height:  1.5;
  color:        var(--muted);
}

/* Groupe de boutons CTA */
.hero-cta    { display: flex; gap: 10px; margin: 18px 0 12px; flex-wrap: wrap; }
.hero-badges { display: flex; flex-wrap: wrap; gap: 8px; }

/* Card hero (droite) — sticky sur desktop */
.hero-card {
  position:      sticky;
  top:           calc(var(--topbar-h) + var(--topbar-gap) + 10px);  /* suit la topbar */
  padding:       18px;
  border-radius: var(--radius);
  background:    var(--panel);
  border:        1px solid var(--border);
  box-shadow:    var(--shadow);
}

/* En-tête de la hero-card */
.card-head {
  display:         flex;
  align-items:     flex-start;
  justify-content: space-between;
  gap:             14px;
}

.identity { margin-top: 0; }
.name     { font-size: 18px; font-weight: 700; letter-spacing: -0.02em; }
.role     { margin-top: 4px; }

/* Sur très petit écran : "Data Engineer" et "9 ans d'expériences" sur 2 lignes */
@media (max-width: 400px) {
  .role          { display: flex; flex-direction: column; gap: 1px; }
  .role-sep      { display: none; }
}

/* Avatar */
.avatar {
  width:         84px;
  height:        84px;
  border-radius: 22px;
  overflow:      hidden;
  border:        1px solid var(--border);
  background:    linear-gradient(135deg, rgba(124,58,237,.22), rgba(34,197,94,.18));
  display:       grid;
  place-items:   center;
}
.avatar img          { width: 100%; height: 100%; object-fit: cover; display: block; }
.avatar .avatar-fallback {
  font-weight:    800;
  letter-spacing: -0.04em;
  font-size:      22px;
}
.avatar.noimg .avatar-fallback { display: block; }
.avatar.noimg img              { display: none; }

/* Quick-info (email, localisation, etc.) */
.quick     { margin-top: 14px; display: grid; gap: 10px; }
.quick-row {
  display:         flex;
  align-items:     center;
  justify-content: space-between;
  gap:             10px;
  padding:         10px 12px;
  border-radius:   14px;
  background:      var(--quick-row);
  border:          1px solid var(--border);
}
.quick-row a       { color: var(--text); text-decoration: none; }
.quick-row a:hover { text-decoration: underline; }

/* Indicateur de disponibilité (point animé + texte) */
.status {
  margin-top:    14px;
  display:       flex;
  align-items:   center;
  gap:           10px;
  padding:       10px 12px;
  border-radius: 14px;
  border:        1px solid var(--border);
  background:    color-mix(in oklab, rgba(34,197,94,.18) 65%, var(--panel));
  color:         var(--text);
  font-weight:   650;
}

/* Ping animé (point vert qui pulse) */
.ping {
  width:         10px;
  height:        10px;
  border-radius: 999px;
  background:    #22c55e;
  box-shadow:    0 0 0 10px rgba(34,197,94,.14);
  animation:     ping 1.6s ease-out infinite;
}
@keyframes ping {
  0%   { transform: scale(1);   opacity: 1; }
  70%  { transform: scale(1.6); opacity: .2; }
  100% { transform: scale(1.6); opacity: 0; }
}


/* ========================================================================== */
/* 8) SECTIONS : profil, grilles, cards, listes, chips, tags, footer           */
/* ========================================================================== */

/* ── En-têtes de section ── */
.section         { margin-top: 10px; margin-bottom: 48px; }
.section:last-of-type { margin-bottom: 0; } /* pas d'espace après la dernière */
.section-head h2 { margin: 0; font-size: 22px; letter-spacing: -0.03em; }
.section-head p  { margin: 8px 0 16px; }

/* ── Grilles 2 et 3 colonnes ── */
.grid        { display: grid; gap: 14px; }
.grid.two    { grid-template-columns: repeat(2, 1fr); }
.grid.three  { grid-template-columns: repeat(3, 1fr); }

/* ── Card de base ── */
.card {
  padding:       18px;
  border-radius: var(--radius);
  background:    var(--panel);
  border:        1px solid var(--border);
  box-shadow:    0 12px 40px rgba(0,0,0,.12);
}

/* ── Titres h3 dans les cards ── */
h3 { margin: 0 0 10px; letter-spacing: -0.02em; }

/* ── Listes ── */
.list {
  margin:      10px 0 0;
  padding-left: 18px;
  color:       var(--muted);
  line-height: 1.65;
}
.list li { margin: 6px 0; }

/* ── Chips et Tags ── */
.chips, .tags { display: flex; flex-wrap: wrap; gap: 8px; }

.chip {
  padding:       8px 10px;
  border-radius: 999px;
  border:        1px solid var(--border);
  background:    color-mix(in oklab, var(--panel) 75%, transparent);
  font-weight:   650;
  font-size:     13px;
  color:         var(--text);
}

.tag {
  padding:       6px 10px;
  border-radius: 999px;
  border:        1px solid var(--border);
  background:    transparent;
  color:         var(--muted);
  font-size:     12.5px;
  font-weight:   650;
}

.pill {
  padding:       6px 10px;
  border-radius: 999px;
  border:        1px solid var(--border);
  background:    color-mix(in oklab, var(--panel2) 45%, transparent);
  color:         var(--text);
  font-weight:   700;
  font-size:     12.5px;
}

/* ── Liens projets et actions contact ── */
.project-top {
  display:     flex;
  align-items: center;
  justify-content: space-between;
  gap:         10px;
}
.project-links {
  margin-top: 12px;
  display:    flex;
  gap:        10px;
  flex-wrap:  wrap;
}
.contact-actions { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 10px; }

/* ── Footer ──────────────────────────────────────────────────────────────
   Dans le flux normal. opacity:0 par défaut.
   Visible uniquement via .is-visible (IntersectionObserver section 14).
────────────────────────────────────────────────────────────────────────── */
.footer {
  margin-top:     30px;
  padding-top:    20px;
  padding-bottom: 10px;
  border-top:     1px solid var(--border);
  display:        flex;
  flex-wrap:      wrap;
  justify-content: space-between;
  align-items:    center;
  gap:            6px 10px;
  opacity:        1;
  pointer-events: auto;
  font-size:      .82rem;
}


/* ========================================================================== */
/* 9) REVEAL "PUZZLE" (animations au scroll)                                   */
/* ========================================================================== */
/*
   Stratégie :
   - Sans JS : tout est visible (opacity:1, transform:none) par défaut.
   - Avec JS  : html.js .reveal cache les éléments et les révèle via
                IntersectionObserver (géré dans script.js).
   - Direction via data-reveal="left|right|up|down"
   - Stagger via variable CSS --d (définie en JS par section)
*/

/* ── Sans JS : tout visible ── */
.reveal {
  opacity:   1;
  transform: none;
}

/* ── Avec JS : état initial (caché, prêt pour l'animation) ── */
html.js .reveal {
  opacity:    0;
  transform:  translate3d(0, 14px, 0) scale(.985);
  transition:
    transform 650ms cubic-bezier(.2, .9, .2, 1),
    opacity   450ms ease;
  transition-delay: var(--d, 0ms);    /* stagger PAR SECTION injecté en JS */
  will-change: transform, opacity;
}

/* ── Variantes directionnelles (petits offsets pour éviter les saccades) ── */
html.js .reveal[data-reveal="left"]  { transform: translate3d(-24px,  0,    0) scale(.985); }
html.js .reveal[data-reveal="right"] { transform: translate3d( 24px,  0,    0) scale(.985); }
html.js .reveal[data-reveal="up"]    { transform: translate3d(  0,  -20px,  0) scale(.985); }
html.js .reveal[data-reveal="down"]  { transform: translate3d(  0,   20px,  0) scale(.985); }

/* ── État final : élément visible ── */
html.js .reveal.is-visible {
  opacity:   1;
  transform: translate3d(0, 0, 0) scale(1);
}


/* ========================================================================== */
/* 10) TILT + GLARE (effet 3D au survol — desktop uniquement)                  */
/* ========================================================================== */
/*
   Appliqué uniquement sur les appareils avec une souris fine (hover:hover).
   Sur mobile/tactile : aucun effet (les classes ne sont jamais ajoutées en JS).
*/

@media (hover: hover) and (pointer: fine) {

  /* ── Setup de la card pour le 3D ── */
  .card {
    position:        relative;
    transform-style: preserve-3d;
    will-change:     transform;
    transition:      transform 120ms ease, box-shadow 120ms ease;
    overflow:        hidden;    /* le glare ne déborde pas */
  }

  /* Ombre portée plus marquée pendant le tilt */
  .card.is-tilting {
    box-shadow: 0 22px 70px rgba(0,0,0,.20);
  }

  /* ── Overlay "glare" (reflet lumineux) ── */
  .card .glare {
    position:    absolute;
    inset:       -40%;          /* dépasse volontairement pour éviter les bords tronqués */
    pointer-events: none;
    opacity:     0;
    transition:  opacity 160ms ease;
    mix-blend-mode: overlay;    /* rendu "premium" */
    filter:      blur(2px);     /* adoucit le reflet */
  }
  .card.is-tilting .glare { opacity: .55; }

  /* ── Éléments 3D-pop (parallax interne) ── */
  .card .tilt-pop {
    transition:  transform 120ms ease;
    will-change: transform;
  }
}


/* ========================================================================== */
/* 11) TIMELINE (expériences)                                                  */
/* ========================================================================== */

/* Liste des items */
.timeline {
  list-style: none;
  margin:     0;
  padding:    0;
  display:    grid;
  gap:        18px;
}

/* Chaque item laisse de la place à gauche pour le dot + la ligne */
.tl-item {
  position:     relative;
  padding-left: 30px;
}

/* Dot coloré */
.tl-dot {
  position:      absolute;
  left:          10px;
  top:           26px;
  width:         12px;
  height:        12px;
  border-radius: 999px;
  background:    linear-gradient(135deg, var(--primary), var(--primary2));
  box-shadow:
    0 0 0 6px color-mix(in oklab, var(--primary) 14%, transparent),
    0 10px 22px rgba(0,0,0,.18);
}

/* Ligne verticale reliant les dots */
.tl-line {
  position:   absolute;
  left:       15px;
  top:        38px;           /* commence juste sous le dot */
  bottom:     -18px;          /* dépasse pour relier au dot suivant */
  width:      2px;
  background: color-mix(in oklab, var(--border) 70%, transparent);
}

/* Pas de ligne après le dernier item */
.timeline .tl-item:last-child .tl-line { display: none; }

/* En-tête d'un item timeline */
.tl-top {
  display:         flex;
  justify-content: space-between;
  align-items:     flex-start;
  gap:             10px;
}
.tl-title { font-weight: 700; letter-spacing: -0.02em; }

/* Ajustement desktop : un peu plus de marge gauche */
@media (min-width: 900px) {
  .tl-item { padding-left: 40px; }
  .tl-dot  { left: 12px; }
  .tl-line { left: 17px; }
}


/* ========================================================================== */
/* 12) MATRICE DES COMPÉTENCES (barres de progression)                         */
/* ========================================================================== */
/*
   Architecture :
   - 1 seule matrice dans le HTML (#matrice).
   - Desktop : déplacée dans le HERO (slot #hero-skills-slot).
   - Mobile  : déplacée dans #matrice-section (via JS placeSkillsMatrix).
   - Animation des barres : IntersectionObserver + CSS transition.
*/

/* Conteneur de la matrice (desktop)
   ─────────────────────────────────────────────────────────────────────────
   On définit les colonnes ICI (pas dans .skill-row) pour que toutes les
   lignes partagent exactement les mêmes tracks :
     col 1 : max-content → largeur du nom le plus long (jamais tronqué)
     col 2 : 1fr         → barre, prend tout l'espace restant
     col 3 : max-content → largeur du niveau le plus long ("En progression")
   → toutes les barres ont le même point de départ ET le même point de fin
   ───────────────────────────────────────────────────────────────────────── */
.skills-mini {
  display:               grid;
  grid-template-columns: max-content 1fr max-content;
  column-gap:            14px;
  row-gap:               10px;
  align-items:           center;   /* aligne verticalement nom, barre et niveau */
  margin-top:            16px;
  width:                 100%;
}

/* display: contents "efface" la boîte .skill-row sans effacer son contenu :
   les 3 enfants (skill-name, skill-bar, skill-lvl) entrent directement dans
   la grille de .skills-mini et héritent de ses colonnes.
   → la variable CSS --pct reste accessible via héritage.
   NOTE : on ne met PAS de background/border sur .skill-row (ils disparaîtraient),
   mais ce n'est pas un problème ici. */
.skill-row {
  --pct:   0%;
  display: contents;
}

/* Nom de la compétence — la colonne auto lui donne exactement la place nécessaire */
.skill-name {
  font-weight:    650;
  letter-spacing: .2px;
  white-space:    nowrap;
}

/* Barre (fond) */
.skill-bar {
  height:        6px;
  border-radius: 999px;
  background:    color-mix(in oklab, var(--border) 70%, transparent);
  overflow:      hidden;
}

/* Remplissage (animé quand .is-anim est ajouté) */
.skill-bar .fill {
  display:       block;
  height:        100%;
  width:         0%;
  border-radius: inherit;
  background:    linear-gradient(90deg, var(--primary), var(--primary2));
  transition:    width 900ms cubic-bezier(.2, .9, .2, 1);
}

/* Déclenche l'animation (classe ajoutée par IntersectionObserver en JS) */
.skills-mini.is-anim .skill-bar .fill { width: var(--pct); }

/* Niveau (texte à droite) */
.skill-lvl {
  color:       var(--muted);
  font-weight: 650;
  font-size:   13px;
  white-space: nowrap;
  text-align:  right;
}

/* ── Matrice sur mobile (pleine largeur, section dédiée) ── */
@media (max-width: 820px) {

  /* Section matrice : alignement gauche comme les autres sections */
  #matrice-section             { text-align: left; }
  #matrice-section .section-head { text-align: left; }

  /* La matrice elle-même prend toute la largeur */
  #matrice-section #matrice {
    width:                 100% !important;
    max-width:             none !important;
    margin:                0 !important;
    grid-template-columns: 1fr auto;
    /* dense : permet au lvl (col 2) de remonter sur la même ligne que name (col 1)
       même si bar (col 1/-1) est déclaré avant lui dans le HTML */
    grid-auto-flow:        row dense;
  }

  /* Avec display:contents sur .skill-row, les enfants sont dans la grille
     de #matrice. On les repositionne via grid-column / grid-row.
     Chaque "ligne" de compétence occupe 2 rangées dans la grille :
       - rangée impaire : skill-name (col 1) + skill-lvl (col 2)
       - rangée paire   : skill-bar sur toute la largeur */
  #matrice-section #matrice .skill-name {
    grid-column: 1;
    align-self:  center;
    line-height: 1.2;
  }

  #matrice-section #matrice .skill-lvl {
    grid-column: 2;
    align-self:  center;
    text-align:  right;
    white-space: nowrap;
    line-height: 1.2;
  }

  #matrice-section #matrice .skill-bar {
    grid-column:  1 / -1;    /* s'étale sur toutes les colonnes */
    width:        100% !important;
    max-width:    none !important;
    justify-self: stretch;
    display:      block;
  }
}


/* ========================================================================== */
/* 13) SWITCH LANGUES (bouton radial flottant)                                 */
/* ========================================================================== */
/*
   Comportement :
   - Desktop : ouverture au survol (CSS hover).
   - Mobile  : tap sur le bouton principal => class .is-open (géré en JS).
   - Le drapeau du bouton central reflète la langue active (géré en JS).
*/

.lang-orbit {
  /* Variables internes */
  --size:   40px;     /* taille bouton principal */
  --child:  40px;     /* taille boutons orbit */
  --radius: 65px;     /* distance d'éjection */
  --safe:   42px;     /* marge pour la zone de déploiement */

  /* Positionnement flottant en bas à droite */
  position: fixed;
  right:    var(--orbit-right, 18px);
  bottom:   var(--orbit-bottom, 18px);
  z-index:  99999;

  /* Surface élargie pour capturer le hover vers les items */
  width:  calc(var(--size) + var(--radius) + var(--safe));
  height: calc(var(--size) + var(--radius) + var(--safe));

  /* pointer-events: auto pour que le CSS :hover fonctionne sur toute la zone
     du conteneur — sinon la souris "sort" du hover en traversant l'espace vide
     entre le bouton principal et les drapeaux */
  pointer-events: auto;
}

/* ── Style commun : bouton principal + boutons orbit ── */
.lang-main,
.lang-item {
  pointer-events: auto;   /* réactive les clics sur les vrais boutons */
  border-radius: 999px;
  border:        1px solid var(--border);
  background:    var(--panel);
  box-shadow:    var(--shadow-float);
  color:         var(--text);

  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);

  display:     grid;
  place-items: center;

  overflow:      hidden;            /* clip les images rondes */
  touch-action:  manipulation;
}

/* ── Bouton principal (ancré en bas à droite de .lang-orbit) ── */
.lang-main {
  position: absolute;
  right:    0;
  bottom:   0;

  width:    var(--size);
  height:   var(--size);
  cursor:   pointer;
  z-index:  2;

  /* Reset du <button> pour un rendu uniforme avec les <a> */
  padding:            0;
  appearance:         none;
  -webkit-appearance: none;
  font:               inherit;
  line-height:        0;
  min-width:          0;
  min-height:         0;

  transition: transform .18s ease, background .18s ease, border-color .18s ease, box-shadow .18s ease;
}

/* ── Boutons orbit (cachés par défaut, éjectés au hover/clic) ── */
.lang-item {
  position: absolute;
  right:    calc((var(--size) - var(--child)) / 2);
  bottom:   calc((var(--size) - var(--child)) / 2);

  width:    var(--child);
  height:   var(--child);

  text-decoration: none;
  z-index:         1;

  /* Caché par défaut */
  opacity:        0;
  pointer-events: none;

  will-change: transform, opacity;
  transform:   translate3d(0, 0, 0) scale(.75);

  transition:
    opacity   .18s ease,
    transform .28s cubic-bezier(.16, 1, .3, 1),
    background   .18s ease,
    border-color .18s ease,
    box-shadow   .18s ease;
}

/* Positions d'éjection (angles ajustés pour un rendu "éventail") */
.lang-item[data-lang="fr"] { --x: 0px;                          --y: calc(-1.05 * var(--radius)); }
.lang-item[data-lang="en"] { --x: calc(-0.75 * var(--radius));  --y: calc(-0.75 * var(--radius)); }

/* Stagger à l'ouverture (effet premium) */
.lang-item[data-lang="fr"] { transition-delay: 0ms; }
.lang-item[data-lang="en"] { transition-delay: 30ms; }

/* Drapeaux */
.lang-orbit .flag-img {
  width:         100%;
  height:        100%;
  object-fit:    cover;
  border-radius: 999px;
  display:       block;
}

/* Marge interne (effet "bouton" avec contour visible) */
.lang-main .flag-img,
.lang-item .flag-img {
  width:      calc(100% - 4px);
  height:     calc(100% - 4px);
  margin:     2px;
  box-shadow: none;
}

/* ── Ouverture desktop (JS mouseenter sur .lang-main uniquement) ── */
/* L'ouverture CSS :hover sur toute la zone est remplacée par JS pour
   éviter le déclenchement accidentel au survol du bouton scroll-top */
@media (hover: hover) {
  .lang-main:hover,
  .lang-item:hover {
    background:   var(--panel2);
    border-color: color-mix(in srgb, var(--border) 55%, var(--primary) 45%);
    box-shadow:
      var(--shadow-float),
      0 0 0 3px  color-mix(in srgb, var(--primary) 20%, transparent),
      0 14px 40px color-mix(in srgb, var(--primary2) 10%, transparent);
  }
}

/* ── Ouverture mobile (class .is-open ajoutée en JS) ── */
.lang-orbit.is-open .lang-item {
  opacity:        1;
  pointer-events: auto;
  transform:      translate3d(var(--x), var(--y), 0) scale(1);
}

/* Supprime le stagger à la fermeture (disparition instantanée) */
.lang-orbit:not(.is-open) .lang-item { transition-delay: 0ms; }

/* Focus clavier */
.lang-main:focus-visible,
.lang-item:focus-visible {
  outline:      none;
  border-color: color-mix(in srgb, var(--border) 55%, var(--primary) 45%);
  box-shadow:
    var(--shadow-float),
    0 0 0 3px color-mix(in srgb, var(--primary) 28%, transparent);
}

/* Responsive */
@media (max-width: 640px) {
  :root { --orbit-right: 12px; --orbit-bottom: 12px; }
}


/* ========================================================================== */
/* 14) DÉCOR : blobs de fond + noise overlay                                   */
/* ========================================================================== */

/* Blobs colorés (position:fixed, derrière tout) */
.bg-blob {
  position:      fixed;
  width:         520px;
  height:        520px;
  border-radius: 50%;
  filter:        blur(60px);
  opacity:       .22;
  z-index:       -3;
}

/*
   NOTE : On évite d'utiliser left/right négatifs sur des éléments fixed
   car cela crée un scrollWidth non nul sur certains navigateurs mobiles.
   Solution : on ancre en left:0 / right:0 puis on décale via transform.
*/
.blob-1 { left: 0;   top: 0;    transform: translate(-160px, -160px); background: rgba(124, 58,  237, .75); }
.blob-2 { right: 0;  top: 40px; transform: translateX(160px);         background: rgba( 34, 197,  94, .65); }

/* Texture de bruit (grain photo subtil) */
.noise {
  position:        fixed;
  inset:           0;
  pointer-events:  none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.8' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)' opacity='.16'/%3E%3C/svg%3E");
  opacity: .06;
  z-index: -2;
}


/* ========================================================================== */
/* 15) RESPONSIVE / MOBILE                                                     */
/* ========================================================================== */
/* Tous les @media regroupés ici pour s'y retrouver (Ctrl+F "@media") */

/* Lien "Haut ↑" du footer : caché sur mobile (remplacé par le bouton flottant) */
/* ── Scroll hint — invite au scroll, mobile uniquement ── */
.scroll-hint {
  display: none;
}

@media (max-width: 900px) {
  .scroll-hint {
    display:        flex;
    flex-direction: column;
    align-items:    center;
    gap:            6px;
    position:       fixed;
    bottom:         14px;
    left:           50%;
    transform:      translateX(-50%);
    z-index:        9000;
    opacity:        0;
    pointer-events: none;
    transition:     opacity 600ms ease;
  }

  .scroll-hint.is-visible { opacity: 1; }

  /* Forme souris */
  .scroll-hint-mouse {
    width:        26px;
    height:       42px;
    border:       2px solid color-mix(in oklab, var(--text) 50%, transparent);
    border-radius: 13px;
    display:      flex;
    justify-content: center;
    padding-top:  6px;
    box-sizing:   border-box;
  }

  /* Dot qui descend */
  .scroll-hint-dot {
    width:         5px;
    height:        5px;
    border-radius: 50%;
    background:    var(--primary);
    animation:     scrollDot 1.6s ease-in-out infinite;
  }

  @keyframes scrollDot {
    0%   { opacity: 1; transform: translateY(0);    }
    80%  { opacity: 0; transform: translateY(14px); }
    100% { opacity: 0; transform: translateY(0);    }
  }

  .scroll-hint-label {
    font-size:      10px;
    font-weight:    500;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color:          color-mix(in oklab, var(--text) 45%, transparent);
  }
}

/* ── Bouton "retour en haut" flottant (mobile uniquement) ─────────────────
   Apparaît au-dessus du bouton langue quand on est en bas de page.
   Animation pulse lente pour attirer l'attention de l'utilisateur.
────────────────────────────────────────────────────────────────────────── */
.btn-scroll-top {
  display:         flex;
  align-items:     center;
  justify-content: center;
  position:        fixed;
  right:           var(--orbit-right, 18px);
  bottom:          calc(var(--orbit-bottom, 18px) + 65px);
  z-index:         100000;
  width:           40px;
  height:          40px;
  border-radius:   50%;
  border:          none;
  cursor:          pointer;
  background:      color-mix(in oklab, var(--primary) 55%, transparent);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  color:           #fff;
  font-size:       18px;
  box-shadow:      0 2px 12px color-mix(in oklab, var(--primary) 40%, transparent);
  opacity:         0;
  transform:       scale(0.8);
  pointer-events:  none;
  transition:      opacity 300ms ease, transform 300ms ease;
}

.btn-scroll-top.is-visible {
  opacity:        1;
  transform:      scale(1);
  pointer-events: auto;
  animation:      scrollTopPulse 2s ease-in-out infinite;
}

@media (max-width: 900px) {
  .btn-scroll-top {
    display: flex; /* déjà défini globalement, on garde pour compatibilité */
  }
  /* Quand l'orbit est ouvert : passe au-dessus du bouton ↑ */
  .lang-orbit.is-open { z-index: 100001; }
}

@keyframes scrollTopPulse {
  0%,  100% { box-shadow: 0 2px 12px color-mix(in oklab, var(--primary) 40%, transparent); }
  50%        { box-shadow: 0 2px 24px color-mix(in oklab, var(--primary) 70%, transparent),
                            0 0  0 8px color-mix(in oklab, var(--primary) 12%, transparent); }
}

@media (max-width: 900px) {
  /* Lien "Haut ↑" du footer : caché sur mobile (remplacé par le bouton flottant) */
  .footer-top { display: none; }

  /* Hero : 1 seule colonne */
  .hero { grid-template-columns: 1fr; }

  /* Hero-card : plus sticky, dans le flux normal */
  .hero-card { position: relative; top: 0; }

  /* Grilles : 1 colonne */
  .grid.three { grid-template-columns: 1fr; }
  .grid.two   { grid-template-columns: 1fr; }

  /* Nav horizontale : cachée (remplacée par le burger) */
  .nav    { display: none; }
  /* Burger : visible */
  .burger { display: inline-flex; }

  /* Espace sous la topbar — réduit sur mobile */
  :root { --hero-top: 10px; }    /* ← ajuster ici pour le mobile ≤900px */

  /* Espace entre le bloc texte et la hero-card (empilés en colonne sur mobile)
     On réduit pour rapprocher les deux éléments sur tablette/mobile */
  :root { --hero-gap: 10px; }    /* ← ajuster ici pour le mobile ≤900px */

  /* Espacement inter-sections réduit sur mobile */
  .section { margin-bottom: 32px; }

  /* ── Hero plein écran sur mobile/tablette ─────────────────────────────
     Objectif : à l'ouverture du site, l'utilisateur voit uniquement
     la topbar + h1 + descriptif + hero-card. La matrice des compétences
     n'est visible qu'après le premier scroll → incite à explorer.

     Calcul : hauteur disponible = viewport - topbar - gap topbar - hero-top
     On utilise 100dvh (dynamic viewport height) plutôt que 100vh car sur
     iOS Safari, 100vh inclut la barre d'URL alors que 100dvh reflète
     la hauteur visuelle réelle disponible à l'instant T.
  ────────────────────────────────────────────────────────────────────── */
  .hero {
    min-height: calc(
      100dvh
      - var(--topbar-h)
      - var(--topbar-gap)
      - var(--hero-top)
      - 16px   /* petit espace de respiration en bas */
    );
    align-content: start;  /* les rangées restent groupées en haut,
                              l'espace vide tombe après la hero-card */
  }
}

/* ── ≤ 820px : topbar compacte + menu mobile ── */
@media (max-width: 820px) {

  /* Nav : cachée (déjà gérée dans ≤900px, on renforce) */
  .nav    { display: none; }
  .burger { display: inline-flex; }

  /* ── Menu mobile (panel sous la topbar) ── */
  .mobile-menu {
    display: grid;
    gap:     10px;

    position: fixed;
    /* On utilise la variable --topbar-h (mise à jour en JS) pour coller
       exactement sous la topbar quelle que soit sa hauteur réelle */
    top:     calc(var(--topbar-h) + var(--topbar-gap) + 6px);
    left:    14px;
    right:   14px;
    z-index: 80;   /* au-dessus de la topbar (z=50) */

    padding:         14px;
    border-radius:   18px;
    border:          2px solid color-mix(in oklab, var(--primary) 55%, var(--border));
    background:      color-mix(in oklab, var(--panel) 75%, transparent);
    backdrop-filter: blur(16px);
    -webkit-backdrop-filter: blur(16px);
    box-shadow:      var(--shadow);

    /* Même fix GPU que la topbar : empêche le saut au scroll iOS */
    -webkit-transform: translateZ(0);
    transform:         translateZ(0);
    will-change:       transform;
  }

  /* Liens du menu mobile */
  .mobile-menu a {
    padding:       10px 12px;
    border-radius: 12px;
    background:    color-mix(in oklab, var(--panel2) 45%, transparent);
    color:         var(--text);
    font-weight:   650;
    text-decoration: none;
    transition:    color 200ms ease, background 200ms ease;
  }

  .mobile-menu a.is-active {
    font-weight:  700;
    color:        #fff;
    background:   linear-gradient(135deg,
                    color-mix(in oklab, var(--primary) 35%, var(--panel2)),
                    color-mix(in oklab, var(--primary2) 20%, var(--panel2)));
    border-left:  3px solid var(--primary);
    -webkit-text-fill-color: unset;
  }

  /* Quand [hidden] est présent (état fermé) => vraiment caché */
  .mobile-menu[hidden] { display: none !important; }
}

/* ── ≤ 480px : topbar très compacte (iPhone SE, etc.) ── */
@media (max-width: 480px) {

  /* Topbar : prend presque toute la largeur, padding réduit */
  .topbar {
    width:   calc(100% - 16px);
    padding: 8px 10px;
    gap:     8px;
  }

  /* Brand : autorise le rétrécissement */
  .brand     { gap: 8px; min-width: 0; }
  .brand span { font-size: 14px; white-space: nowrap; }

  /* Actions : espacement réduit */
  .actions { gap: 6px; }

  /* Bouton Télécharger : mode icône uniquement (texte masqué) */
  .btn-download {
    width:    42px;
    height:   42px;
    padding:  0;
    font-size: 0;      /* masque le texte */
    gap:      0;
    display:      inline-flex;
    align-items:  center;
    justify-content: center;
    line-height:  1;
  }
  .btn-download .dl-text { display: none; }
  .btn-download .dl-img  { height: 25px; width: auto; display: block; }

  /* Réduction de l'espace sous la topbar */
  :root { --hero-top: 0px; }             /* ← ajuster ici pour le très petit mobile ≤480px */

  /* Marges héro réduites */
  .hero .chip,
  .hero .badge,
  .hero .availability { margin-bottom: 10px !important; }
  .hero h1             { margin-top: 8px !important; }
}

/* ── ≤ 420px : quick-rows plus compacts ── */
@media (max-width: 420px) {
  .quick-row            { padding: 9px 10px; gap: 8px; }
  .quick-row .muted     { font-size: 13px; }
  .quick-row > :nth-child(2) { font-size: 13px; }
}

/* ── Toast : centré sur mobile ── */
@media (max-width: 640px) {
  .toast,
  .toast.toast--anchored {
    right:     auto;
    left:      50%;
    transform: translate(-50%, -16px);
    max-width: calc(100vw - 24px);
  }
  .toast.show,
  .toast.toast--anchored.show {
    transform: translate(-50%, 0);
  }
}


/* ========================================================================== */
/* 15b) FORMULAIRE DE CONTACT                                                  */
/* ========================================================================== */

.contact-form-card {
  grid-column: 1 / -1;
}

.typewriter-contact {
  margin-top:  0;
  font-size:   1.05rem;
  font-weight: 600;
  color:       var(--text);
  opacity:     .75;
  min-height:  1.6em;
  letter-spacing: -0.01em;
  text-align:  center;
  width:       100%;
}

@media (max-width: 480px) {
  .typewriter-contact { font-size: .95rem; margin-top: 18px; }
}

.typewriter-contact::after {
  content:    '|';
  display:    inline-block;
  margin-left: 2px;
  animation:  twCursor 700ms steps(1) infinite;
  color:      var(--primary);
}

@keyframes twCursor {
  0%, 100% { opacity: 1; }
  50%       { opacity: 0; }
}

/* Espace visuel sous le formulaire avant le footer */
#contact {
  padding-bottom: 20px;
}

/* Section Contact : espacements condensés */
#contact .section-head p { margin: 4px 0 10px; }

#contact .card {
  padding: 14px;
}

.required-star {
  color:       #e55;
  font-weight: 700;
  margin-right: 1px;
}

.form-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
}

@media (max-width: 520px) {
  .form-row { grid-template-columns: 1fr; }
}

.contact-form {
  display:        flex;
  flex-direction: column;
  gap:            10px;
  margin-top:     6px;
}

.form-group {
  display:        flex;
  flex-direction: column;
  gap:            3px;
}

.form-group label {
  font-size:   .80rem;
  font-weight: 500;
  color:       var(--text);
  opacity:     .8;
}

.form-group input,
.form-group textarea {
  width:         100%;
  padding:       8px 11px;
  border-radius: 8px;
  border:        1px solid var(--border);
  background:    color-mix(in oklab, var(--bg) 80%, var(--panel));
  color:         var(--text);
  font-family:   inherit;
  font-size:     .92rem;
  outline:       none;
  transition:    border-color 200ms ease, box-shadow 200ms ease;
  box-sizing:    border-box;
  resize:        vertical;
}

.form-group textarea {
  rows: 3;
  min-height: 72px;
}

.form-group input:focus,
.form-group textarea:focus {
  border-color: var(--primary);
  box-shadow:   0 0 0 3px color-mix(in oklab, var(--primary) 18%, transparent);
}

.form-group input.is-invalid,
.form-group textarea.is-invalid {
  border-color: #e55;
}

.form-submit {
  align-self:      center;
  min-width:   120px;
  justify-content: center;
  gap:         8px;
}

.cf-feedback {
  font-size:   .88rem;
  padding:     6px 10px;
  border-radius: 7px;
  margin-top:  2px;
}
.cf-feedback.is-success {
  background: color-mix(in oklab, var(--primary) 15%, transparent);
  color:      color-mix(in oklab, var(--primary) 80%, var(--text));
}
.cf-feedback.is-error {
  background: color-mix(in oklab, #e55 12%, transparent);
  color:      #e55;
}


/* ========================================================================== */
/* 16) ACCESSIBILITÉ                                                            */
/* ========================================================================== */

/* Si l'utilisateur a activé "Réduire les animations" dans son OS :
   on désactive tous les reveals pour afficher tout immédiatement */
@media (prefers-reduced-motion: reduce) {
  html.js .reveal {
    opacity:    1 !important;
    transform:  none !important;
    transition: none !important;
  }
}