/* ==========================================================
   1. Variables (relevées dans les maquettes, mobile 390 px)
   ========================================================== */
:root {
  /* Couleurs */
  --couleur-fond: #f1ecca;
  --couleur-accent: #3073b7;
  --couleur-texte: #203c7f;
  --couleur-blanc: #ffffff;
  --couleur-marine: #0e1c3e; /* marine de la palette pixel (main.js) : fond de la visionneuse */
  /* Teintes dérivées du texte (#203c7f) avec opacité */
  --couleur-filet: rgb(32 60 127 / 0.25);        /* lignes de séparation */
  --couleur-vide: rgb(32 60 127 / 0.12);         /* fond des images en attente */
  --couleur-point: rgb(32 60 127 / 0.3);         /* points de pagination inactifs */
  --couleur-frise: rgb(32 60 127 / 0.35);        /* ligne et puce grise du parcours */
  --couleur-fond-photo: rgb(32 60 127 / 0.25);   /* fond du haut de projet sans photo */
  --couleur-fond-site: rgb(32 60 127 / 0.08);    /* fond d'un site intégré */
  /* Calque "multiply" des vignettes en bleu et crème (.bichromie) : (crème − bleu foncé) / (1 − bleu foncé),
     canal par canal. Suivi du bleu foncé en "screen", il fait aller l'image du bleu foncé au crème */
  --couleur-bichromie: rgb(239 230 149);

  /* Voile dégradé sur la photo du haut de projet (lisibilité du texte blanc) */
  --voile-photo: linear-gradient(
    rgb(32 60 127 / 0.67) 0%,
    rgb(32 60 127 / 0.39) 20%,
    rgb(32 60 127 / 0.2) 40%,
    rgb(32 60 127 / 0.07) 60%,
    rgb(32 60 127 / 0) 90%
  );
  /* Ordinateur : voile du bas, sous le titre posé en bas de la photo */
  --voile-photo-bas: linear-gradient(
    0deg,
    rgb(32 60 127 / 0.7) 0%,
    rgb(32 60 127 / 0.3) 30%,
    rgb(32 60 127 / 0) 55%
  );

  /* Polices (Inter : Regular 400 et Medium 500 ; Berkah Raya : titres des pages projet) */
  --police: "Inter", sans-serif;
  --police-titre-projet: "Berkah Raya", var(--police); /* pas de chiffres : ils s'affichent en Inter */
  --graisse-normale: 400;
  --graisse-medium: 500;
  --interligne: 1.21; /* interligne "auto" d'Inter dans les maquettes */

  /* Tailles de texte */
  --taille-11: 11px;
  --taille-12: 12px;
  --taille-13: 13px;
  --taille-14: 14px;
  --taille-15: 15px;
  --taille-16: 16px;
  --taille-17: 17px;
  --taille-18: 18px;
  --taille-19: 19px;
  --taille-20: 20px;
  --taille-24: 24px;
  --taille-28: 28px;
  --taille-32: 32px;
  --taille-36: 36px;
  --taille-40: 40px;
  --taille-34: 34px;

  /* Tailles qui grandissent avec l'écran (redéfinies dans les paliers, sous ce bloc) */
  --taille-texte: var(--taille-16);           /* texte courant (body) */
  --taille-titre-section: var(--taille-24);   /* "Présentation", "Projets"… */
  --taille-accroche: var(--taille-19);        /* phrase d'introduction en grand */
  --taille-titre-page: var(--taille-32);      /* "Tous les projets", "Mentions légales" */
  --taille-intro: var(--taille-20);           /* paragraphe de la Présentation (ordinateur) */
  --taille-titre-contact: var(--taille-24);   /* "Contact" de l'accueil, en très grand sur ordinateur */
  --taille-contact: var(--taille-14);         /* adresse e-mail du contact, en très grand sur ordinateur */
  --taille-titre-projet: var(--taille-40);    /* titre du haut de la page projet */
  --taille-titre-ligne: var(--taille-24);     /* titre d'un projet en liste (.carte-projet--ligne, ordinateur) */

  /* Survols et focus : mêmes durées, mêmes courbes, même soulignement partout
     (le menu garde ses propres durées, liées à celles de main.js) */
  --duree-survol: 0.2s;                           /* couleurs, soulignements */
  --duree-mouvement: 0.35s;                       /* zoom, glissement, fondu d'une image */
  --courbe-sortie: cubic-bezier(0.22, 1, 0.36, 1); /* démarre vite, se pose en douceur */
  --decalage-soulignement: 0.2em;                 /* soulignement qui apparaît au survol */
  --duree-apparition: 0.45s;                      /* blocs qui apparaissent au défilement */
  --glissement-apparition: 14px;
  --duree-balayage: 0.35s;                        /* fond qui balaie une ligne : menu, contact (= DUREE_BALAYAGE dans main.js) */
  --courbe-balayage: cubic-bezier(0.65, 0, 0.35, 1);

  /* Mise en page */
  --largeur-max: 1440px;  /* largeur maximale du contenu, centré au-delà */
  --largeur-texte: 65ch;  /* longueur de ligne confortable des paragraphes (tablette et plus) */
  --colonnes: 12;         /* grille de l'ordinateur */
  --gouttiere: 24px;      /* espace entre les colonnes de la grille */

  /* Espacements */
  --marge-ecran: 24px; /* marge minimale entre le bord de l'écran et le contenu */
  --marge: 24px;       /* distance réelle entre le bord de l'écran et le contenu (= --marge-ecran, sauf au-delà de --largeur-max) */
  --espace-section: 32px; /* marge verticale des sections à partir de la tablette (en mobile : 32 px en haut, le bas dépend de la section) */
  --espace-2: 2px;
  --espace-3: 3px;
  --espace-4: 4px;
  --espace-5: 5px;
  --espace-6: 6px;
  --espace-8: 8px;
  --espace-9: 9px;
  --espace-10: 10px;
  --espace-11: 11px;
  --espace-12: 12px;
  --espace-16: 16px;
  --espace-17: 17px;
  --espace-18: 18px;
  --espace-20: 20px;
  --espace-21: 21px;
  --espace-22: 22px;
  --espace-24: 24px;
  --espace-25: 25px;
  --espace-27: 27px;
  --espace-28: 28px;
  --espace-30: 30px;
  --espace-31: 31px;
  --espace-32: 32px;
  --espace-35: 35px;
  --espace-38: 38px;
  --espace-39: 39px;
  --espace-40: 40px;
  --espace-41: 41px;
  --espace-42: 42px;
  --espace-44: 44px;
  --espace-46: 46px;
  --espace-48: 48px;
  --espace-50: 50px;
  --espace-52: 52px;
  --espace-57: 57px;
  --espace-60: 60px;
  --espace-62: 62px;
  --espace-64: 64px;
  --espace-66: 66px;

  /* Dimensions */
  --epaisseur-filet: 0.5px;
  --densite: 1; /* pixels de l'écran par pixel CSS (devicePixelRatio), mise à jour par main.js : portrait net */
  --hauteur-entete: 44px;          /* ligne de l'en-tête = zone tactile du burger */
  --hauteur-entete-complete: 92px; /* marge du haut + ligne + marge du bas (40 + 44 + 8) */
  --retrait-entete-colle: 32px;    /* part de la marge du haut qui disparaît quand l'en-tête colle */
  --hauteur-entete-colle: 60px;    /* hauteur visible de l'en-tête collé (92 - 32) */
  --haut-titre-colle: calc(var(--hauteur-entete-colle) + var(--espace-32)); /* titres de section collés (ordinateur) : 32 px sous l'en-tête collé */
  --hauteur-lien-menu: 68px;
  --hauteur-bouton: 52px;
  --hauteur-bouton-petit: 48px;  /* "Ouvrir en plein écran" */
  --hauteur-scene-min: 240px;     /* zone du portrait sur les écrans très bas (paysage) */
  --largeur-libelle: 116px;       /* colonne de gauche des lignes Localisation, E-mail… */
  --largeur-carte: 300px;         /* carte du carrousel (300 × 360) */
  --largeur-image-galerie: var(--largeur-carte); /* image de la galerie d'un projet (carrousel) */
  --taille-fleche: 44px;          /* boutons ← → du carrousel (tablette, souris) */
  --taille-point: 6px;            /* pastille "Disponible" et points de pagination */
  --taille-puce-frise: 9px;
  --taille-logo: 20px;            /* logos des compétences */
  --taille-icone-contact: 22px;
  --hauteur-etape-frise: 92px;
  --largeur-image-influence: 88px;
  --hauteur-image-influence: 110px;
  --hauteur-ligne-contact: 56px;
  --retrait-ligne-contact: 16px;  /* marge intérieure des lignes de contact, pour le fond qui les remplit (ordinateur) */
  --hauteur-haut-projet: 560px;
  --hauteur-site-integre: 480px;
  /* Écran de chargement (ordinateur seulement, voir includes/chargement.php) */
  --taille-logo-chargement: 96px;
  --largeur-barre-chargement: 160px;
  --epaisseur-barre-chargement: 2px;
  --duree-fondu-chargement: 0.3s; /* = DUREE_FONDU_CHARGEMENT dans main.js */
}

/* Tablette : la marge et les titres grandissent progressivement avec l'écran,
   de leur taille mobile à 768 px jusqu'à leur maximum à 1440 px.
   --marge : au-delà de 1440 px de contenu, la marge grandit pour centrer le cadre.
   100cqw = largeur de la page sans la barre de défilement (body est un conteneur, voir "Base") :
   la valeur est juste dans tous les éléments, y compris ceux qui débordent jusqu'au bord de l'écran */
@media (min-width: 48rem) {
  :root {
    --marge-ecran: clamp(24px, 8.333vw - 40px, 80px);
    --marge: max(var(--marge-ecran), (100cqw - var(--largeur-max)) / 2);
    --espace-section: 64px;
    --taille-titre-section: clamp(24px, 2.381vw + 5.71px, 40px);
    --taille-accroche: clamp(19px, 1.042vw + 11px, 26px);
    --taille-titre-page: clamp(32px, 4.762vw - 4.57px, 64px);
  }
}

/* Ordinateur : la respiration entre les sections grandit jusqu'à 128 px,
   le paragraphe de la Présentation de 20 px (1024) à 24 px (1440),
   le contact passe en très grand, les logos des compétences un peu plus grands */
@media (min-width: 64rem) {
  :root {
    --espace-section: clamp(80px, 8vw, 128px);
    --taille-intro: clamp(20px, 0.962vw + 10.15px, 24px);
    --taille-titre-contact: clamp(72px, 9vw, 160px);
    --taille-contact: clamp(36px, 5vw, 80px);
    --taille-logo: 24px;
    /* Page projet : haut à 80 % de l'écran, titres en très grand, site intégré à 80 % de l'écran */
    --taille-titre-projet: clamp(64px, 8vw, 144px);
    --taille-titre-ligne: clamp(28px, 2.8vw, 48px); /* 29 px en 1024, 40 px en 1440, 48 px au-delà */
    --hauteur-haut-projet: clamp(560px, 80svh, 900px);
    --hauteur-site-integre: clamp(480px, 80svh, 900px);
    /* Galerie : 2 images et demie dans la largeur du cadre (100cqw - 2 marges), avec 2 gouttières entre elles */
    --largeur-image-galerie: calc((100cqw - 2 * var(--marge) - 2 * var(--gouttiere)) / 2.5);
  }
}

/* Grand écran : texte courant un peu plus grand, gouttière plus large */
@media (min-width: 90rem) {
  :root {
    --taille-texte: var(--taille-17);
    --gouttiere: 32px;
  }
}

/* ==========================================================
   2. Polices (hébergées sur le site, réduites au latin)
   ========================================================== */
@font-face {
  font-family: "Inter";
  src: url("../fonts/inter-regular.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Inter";
  src: url("../fonts/inter-medium.woff2") format("woff2");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}

/* Berkah Raya (gothique, même esprit que le logo) — réduite au latin, une seule graisse */
@font-face {
  font-family: "Berkah Raya";
  src: url("../fonts/berkahraya-regular.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

/* ==========================================================
   3. Base
   ========================================================== */
*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  scroll-padding-top: var(--hauteur-entete-colle); /* les ancres ne passent pas sous l'en-tête collé */
}

/* Les liens d'ancre (menu, "Haut de page") font glisser la page au lieu de la téléporter */
@media (prefers-reduced-motion: no-preference) {
  html {
    scroll-behavior: smooth;
  }
}

/* Fondu entre 2 pages : la page quittée reste affichée jusqu'à ce que la suivante soit prête.
   Réservé aux liens du menu (les autres l'annulent, voir "pageswap" dans main.js) */
@media (prefers-reduced-motion: no-preference) {
  @view-transition {
    navigation: auto;
  }
}

/* Bloque le scroll de la page quand le menu est ouvert */
html.menu-ouvert {
  overflow: hidden;
}

/* body sert de conteneur : 100cqw = largeur de la page sans la barre de défilement (calcul de --marge) */
body {
  container-type: inline-size;
  margin: 0;
  background: var(--couleur-fond);
  color: var(--couleur-texte);
  font-family: var(--police);
  font-weight: var(--graisse-normale);
  font-size: var(--taille-texte);
  line-height: var(--interligne);
}

h1,
h2,
h3,
p,
ul,
ol,
dl,
dd {
  margin: 0;
}

h1,
h2,
h3 {
  font-size: inherit;
  font-weight: inherit;
}

ul,
ol {
  padding: 0;
  list-style: none;
}

img {
  display: block;
  max-width: 100%;
  height: auto;
}

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

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

/* Focus visible au clavier uniquement */
:focus-visible {
  outline: 2px solid var(--couleur-accent);
  outline-offset: 2px;
}

/* --- Apparition au défilement (main.js : .est-visible, une seule fois) ---
   Les blocs principaux sous le haut de page arrivent en fondu, avec un léger glissement.
   Masqués seulement si le JS est là (.avec-js, posé dans head.php) : sans JS, tout est visible.
   Pas sur le hero ni les accordéons, qui ont déjà leur effet pixel. Rien sans animation */
@media (prefers-reduced-motion: no-preference) {
  .avec-js :is(main .section, .fiche-projet, .chapitre, .navigation-projets, .mention) {
    transition:
      opacity var(--duree-apparition) var(--courbe-sortie),
      translate var(--duree-apparition) var(--courbe-sortie);
  }

  .avec-js :is(main .section, .fiche-projet, .chapitre, .navigation-projets, .mention):not(.est-visible) {
    opacity: 0;
    translate: 0 var(--glissement-apparition);
  }
}

/* ==========================================================
   4. Composants
   ========================================================== */

/* --- Écran de chargement (includes/chargement.php) ---
   Masqué partout par défaut. Il n'existe visuellement qu'avec .est-en-chargement sur <html>,
   posée par head.php (ordinateur, animations permises, 1re page de la session) et retirée par main.js */
.chargement {
  display: none;
}

/* Ordinateur : plein écran crème, logo au centre, barre fine en dessous.
   La page ne défile pas sous l'écran ; la place de la barre de défilement reste réservée
   (scrollbar-gutter) : rien ne bouge quand elle réapparaît à la sortie */
@media (min-width: 64rem) {
  .est-en-chargement {
    overflow: hidden;
    scrollbar-gutter: stable;
  }

  .est-en-chargement .chargement {
    position: fixed;
    z-index: 20; /* au-dessus de l'en-tête collant (10) */
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--espace-24);
    background: var(--couleur-fond);
    transition: opacity var(--duree-fondu-chargement) var(--courbe-sortie);
  }

  /* Fin du chargement : fondu, la page est déjà utilisable dessous */
  .chargement.est-en-sortie {
    opacity: 0;
    pointer-events: none;
  }

  .chargement__logo {
    width: var(--taille-logo-chargement);
  }

  /* Rail couleur des filets ; le remplissage en accent avance de gauche à droite
     (--progression, de 0 à 1, posée par main.js) */
  .chargement__barre {
    width: var(--largeur-barre-chargement);
    height: var(--epaisseur-barre-chargement);
    overflow: hidden;
    background: var(--couleur-filet);
  }

  .chargement__barre::before {
    content: "";
    display: block;
    height: 100%;
    background: var(--couleur-accent);
    transform-origin: left;
    scale: var(--progression, 0) 1;
    transition: scale var(--duree-survol) var(--courbe-sortie); /* = DUREE_REMPLISSAGE dans main.js */
  }
}

/* --- Bouton plein (ex. "Télécharger mon CV") --- */
.bouton {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: var(--hauteur-bouton);
  background: var(--couleur-accent);
  color: var(--couleur-blanc);
  font-size: var(--taille-15);
  font-weight: var(--graisse-medium);
}

/* Variante à contour (ex. "Tous les projets") */
.bouton--contour {
  border: 1px solid var(--couleur-accent);
  background: none;
  color: var(--couleur-accent);
}

/* Survol : le bouton plein fonce, le bouton à contour se remplit */
.bouton {
  transition: background-color var(--duree-survol), color var(--duree-survol);
}

@media (hover: hover) {
  .bouton:hover {
    background-color: var(--couleur-texte);
  }

  .bouton--contour:hover {
    background-color: var(--couleur-accent);
    color: var(--couleur-blanc);
  }
}

/* --- Liens discrets (pied de page, bas du menu) : soulignés au survol --- */
.lien-discret {
  text-decoration: underline 1px transparent;
  text-underline-offset: var(--decalage-soulignement);
  transition: text-decoration-color var(--duree-survol);
}

@media (hover: hover) {
  .lien-discret:hover {
    text-decoration-color: currentColor;
  }
}

/* --- En-tête : "Accueil | Django" à gauche, burger / croix à droite --- */
.entete {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--espace-16);
  padding: var(--espace-40) var(--marge) var(--espace-8);
}

/* En-tête du site, collé en haut au scroll : seule une partie de la marge du haut disparaît,
   le logo garde de l'air au-dessus. Le filet apparaît une fois collé (.est-colle, posée par main.js) */
.entete--collant {
  position: sticky;
  z-index: 10;
  top: calc(-1 * var(--retrait-entete-colle));
  background: var(--couleur-fond);
  transition: background-color var(--duree-survol), box-shadow var(--duree-survol);
}

/* Filet en ombre plutôt qu'en bordure : il ne change pas la hauteur de l'en-tête */
.entete--collant.est-colle {
  box-shadow: 0 var(--epaisseur-filet) 0 var(--couleur-filet);
}

.entete--collant :is(.entete__logo, .entete__logo-separateur, .bouton-menu) {
  transition: color var(--duree-survol);
}

.entete__logo,
.entete__bouton {
  display: flex;
  align-items: center;
  min-height: var(--hauteur-entete);
}

.entete__logo {
  min-width: 0; /* peut rétrécir : un nom de projet trop long est coupé (…) */
  gap: 0.4em;
  color: var(--couleur-accent);
  font-size: var(--taille-17);
  font-weight: var(--graisse-medium);
}

.entete__logo > * {
  flex-shrink: 0;
  white-space: nowrap;
}

/* Les 2 liens : toute la hauteur de l'en-tête est cliquable, soulignés au survol */
.entete__logo > a {
  display: flex;
  align-items: center;
  align-self: stretch;
  text-decoration: underline 1px transparent;
  text-underline-offset: var(--decalage-soulignement);
  transition: color var(--duree-survol), text-decoration-color var(--duree-survol);
}

@media (hover: hover) {
  .entete__logo > a:hover {
    text-decoration-color: currentColor;
  }
}

/* Où on est : section de l'accueil, "Projets", nom du projet… */
.entete__logo > .entete__logo-nom {
  display: block;
  flex-shrink: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  line-height: var(--hauteur-entete); /* centré verticalement sans flex (sinon pas de "…") */
}

/* "Accueil" et la barre, plus discrets que la partie droite */
.entete__logo-prefixe {
  color: var(--couleur-texte);
  font-weight: var(--graisse-normale);
}

.entete__logo-separateur {
  color: var(--couleur-frise);
  font-weight: var(--graisse-normale);
}

/* Variante blanche, posée sur la photo du haut de projet (la marge négative la superpose à la photo).
   Une fois collé, l'en-tête reprend son fond crème et son texte bleu */
.entete--clair {
  margin-bottom: calc(-1 * var(--hauteur-entete-complete));
}

.entete--clair:not(.est-colle) {
  background-color: transparent;
  color: var(--couleur-blanc);
}

.entete--clair:not(.est-colle) :is(.entete__logo, .entete__logo-prefixe) {
  color: var(--couleur-blanc);
}

.entete--clair:not(.est-colle) .entete__logo-separateur {
  color: rgb(255 255 255 / 0.6);
}

/* Sur la photo, le focus bleu se verrait mal : il passe en blanc */
.entete--clair:not(.est-colle) :focus-visible {
  outline-color: var(--couleur-blanc);
}

/* Barre de progression de la lecture (page projet, ordinateur) : fine ligne d'accent sous l'en-tête,
   qui s'allonge avec le défilement de la page (animation liée au scroll, sans JS).
   Absente si le navigateur ne sait pas lier une animation au scroll, ou sans animation */
.entete__progression {
  display: none;
}

@media (min-width: 64rem) and (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: scroll()) {
    .entete__progression {
      position: absolute;
      right: 0;
      bottom: 0;
      left: 0;
      display: block;
      height: 2px;
      background: var(--couleur-accent);
      transform-origin: left;
      animation: progression-lecture linear both;
      animation-timeline: scroll(root);
    }
  }
}

@keyframes progression-lecture {
  from {
    scale: 0 1;
  }
  to {
    scale: 1 1;
  }
}

/* --- Bouton burger (3 barres) qui se transforme en croix (2 barres) ---
   Ouverture : les barres se rejoignent au centre (celle du milieu s'efface), puis pivotent.
   Fermeture : elles se redressent, puis s'écartent (celle du milieu réapparaît). */
.bouton-menu {
  position: relative;
  flex-shrink: 0;
  justify-content: center;
  width: 44px;
  min-height: 44px;
  margin-right: -11px; /* zone tactile de 44 px, mais barres alignées sur la marge */
}

.bouton-menu__barre {
  --decalage: -6px; /* position dans le burger */
  --angle: 45deg;   /* rotation dans la croix */
  position: absolute;
  top: 50%;
  left: 50%;
  width: 22px;
  height: 1.5px;
  margin: -0.75px 0 0 -11px; /* centrée dans le bouton */
  border-radius: 1px;
  background: currentColor;
  translate: 0 var(--decalage);
  /* Retour au burger : d'abord la rotation, puis l'écartement */
  transition:
    rotate 0.15s cubic-bezier(0.65, 0, 0.35, 1),
    translate 0.15s cubic-bezier(0.65, 0, 0.35, 1) 0.15s;
}

.bouton-menu__barre:last-child {
  --decalage: 6px;
  --angle: -45deg;
}

/* Barre du milieu : ne bouge pas, réapparaît pendant l'écartement */
.bouton-menu__barre:nth-child(2) {
  --decalage: 0px;
  transition: opacity 0.15s cubic-bezier(0.65, 0, 0.35, 1) 0.15s;
}

/* État croix : bouton de l'en-tête menu ouvert, ou bouton du menu */
.bouton-menu[aria-expanded="true"] .bouton-menu__barre,
.bouton-menu--croix .bouton-menu__barre {
  translate: 0 0;
  rotate: var(--angle);
  /* Passage en croix : d'abord le rapprochement, puis la rotation */
  transition:
    translate 0.15s cubic-bezier(0.65, 0, 0.35, 1),
    rotate 0.15s cubic-bezier(0.65, 0, 0.35, 1) 0.15s;
}

/* La barre du milieu s'efface pendant le rapprochement : il n'en reste que 2 */
.bouton-menu[aria-expanded="true"] .bouton-menu__barre:nth-child(2),
.bouton-menu--croix .bouton-menu__barre:nth-child(2) {
  rotate: 0deg;
  opacity: 0;
  transition: opacity 0.15s cubic-bezier(0.65, 0, 0.35, 1);
}

/* Le bouton du menu apparaît avec le menu : il rejoue la transformation burger → croix */
.bouton-menu--croix .bouton-menu__barre {
  animation: barre-en-croix 0.3s both;
}

.bouton-menu--croix .bouton-menu__barre:nth-child(2) {
  animation-name: barre-centrale-disparait;
}

@keyframes barre-en-croix {
  0% {
    translate: 0 var(--decalage);
    rotate: 0deg;
    animation-timing-function: cubic-bezier(0.65, 0, 0.35, 1);
  }
  50% {
    translate: 0 0;
    rotate: 0deg;
    animation-timing-function: cubic-bezier(0.65, 0, 0.35, 1);
  }
  100% {
    translate: 0 0;
    rotate: var(--angle);
  }
}

@keyframes barre-centrale-disparait {
  0% {
    opacity: 1;
    animation-timing-function: cubic-bezier(0.65, 0, 0.35, 1);
  }
  50%,
  100% {
    opacity: 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  .bouton-menu__barre {
    transition: none !important;
    animation: none !important;
  }
}

/* --- Menu plein écran --- */
.menu {
  width: 100%;
  height: 100%;
  max-width: 100%;
  max-height: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  overflow-y: auto;
  background: var(--couleur-fond);
  color: var(--couleur-texte);
  transition: background-color 0.35s;
}

/* Pas de voile gris derrière le menu : le site doit apparaître pendant la sortie */
.menu::backdrop {
  background: transparent;
}

.menu[open] {
  display: flex;
  flex-direction: column;
}

/* Même position que l'en-tête du site, collé ou non (--decalage-entete posée par main.js) :
   le burger se change en croix sur place */
.menu .entete {
  position: relative;
  z-index: 1; /* le menu qui descend passe sous son en-tête */
  margin-top: var(--decalage-entete, 0px);
  background: var(--couleur-fond);
}

/* --- Arrivée et départ du menu (classes posées par main.js) ---
   Le menu descend du haut de l'écran, et y remonte à la fermeture (croix, Échap).
   Son en-tête fait le mouvement inverse (--hauteur-menu = hauteur de l'écran) : il reste immobile,
   et le burger se change en croix sur place */
.menu:is(.est-en-arrivee, .est-en-fermeture) {
  overflow: hidden; /* pas de barre de défilement pendant le mouvement */
}

.menu.est-en-arrivee {
  animation: menu-descend 0.45s cubic-bezier(0.22, 1, 0.36, 1);
}

.menu.est-en-arrivee .entete {
  animation: entete-immobile 0.45s cubic-bezier(0.22, 1, 0.36, 1);
}

/* Jouée à l'envers, la courbe s'inverse aussi : cette "ease-out" donne un départ lent qui accélère */
.menu.est-en-fermeture {
  animation: menu-descend 0.35s cubic-bezier(0.33, 1, 0.68, 1) reverse forwards;
}

.menu.est-en-fermeture .entete {
  animation: entete-immobile 0.35s cubic-bezier(0.33, 1, 0.68, 1) reverse forwards;
}

/* Menu repris sur une nouvelle page après un lien du menu (.est-sans-arrivee) : déjà en croix */
.menu.est-sans-arrivee .bouton-menu__barre {
  animation: none;
}

/* À la fermeture, la croix redevient un burger (l'inverse de barre-en-croix) */
.menu.est-en-fermeture .bouton-menu__barre {
  animation: barre-en-burger 0.3s both;
}

.menu.est-en-fermeture .bouton-menu__barre:nth-child(2) {
  animation-name: barre-centrale-reapparait;
}

@keyframes menu-descend {
  from {
    translate: 0 -100%;
  }
}

@keyframes entete-immobile {
  from {
    translate: 0 var(--hauteur-menu, 100vh);
  }
}

@keyframes barre-en-burger {
  0% {
    translate: 0 0;
    rotate: var(--angle);
    animation-timing-function: cubic-bezier(0.65, 0, 0.35, 1);
  }
  50% {
    translate: 0 0;
    rotate: 0deg;
    animation-timing-function: cubic-bezier(0.65, 0, 0.35, 1);
  }
  100% {
    translate: 0 var(--decalage);
    rotate: 0deg;
  }
}

@keyframes barre-centrale-reapparait {
  0%,
  50% {
    opacity: 0;
    animation-timing-function: cubic-bezier(0.65, 0, 0.35, 1);
  }
  100% {
    opacity: 1;
  }
}

@media (prefers-reduced-motion: reduce) {
  .menu,
  .menu .entete {
    animation: none !important;
  }
}

.menu__corps {
  display: flex;
  flex: 1 0 auto;
  flex-direction: column;
  padding: var(--espace-32) var(--marge) var(--espace-40);
}

.menu__liste {
  border-top: var(--epaisseur-filet) solid var(--couleur-filet);
}

.menu__liste li {
  border-bottom: var(--epaisseur-filet) solid var(--couleur-filet);
}

.menu__lien {
  position: relative;
  z-index: 0; /* garde le fond qui balaie (z-index: -1) derrière le texte */
  display: flex;
  align-items: center;
  min-height: calc(var(--hauteur-lien-menu) - var(--epaisseur-filet));
  color: var(--couleur-accent);
  font-size: var(--taille-34);
  font-weight: var(--graisse-medium);
  transition: color var(--duree-balayage);
}
/* Fond qui balaie la ligne de gauche à droite ("Sweep To Right"),
   d'un bord à l'autre de l'écran pour que le texte ne bouge pas */
.menu__lien::before {
  content: "";
  position: absolute;
  z-index: -1;
  inset: 0 calc(-1 * var(--marge));
  background: var(--couleur-texte);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--duree-balayage) var(--courbe-balayage);
}

/* Balayage au survol (souris uniquement) et sur le lien cliqué */
@media (hover: hover) {
  .menu__lien:hover {
    color: var(--couleur-fond);
  }

  .menu__lien:hover::before {
    transform: scaleX(1);
  }
}

.menu__lien.est-choisi {
  color: var(--couleur-fond);
}

.menu__lien.est-choisi::before {
  transform: scaleX(1);
}

/* --- Sortie du menu après un clic sur un lien (classes posées par main.js) ---
   1er temps (0,35 s) : le lien est balayé, tout le reste s'efface, le site apparaît.
   Pause (0,15 s) : la barre du lien reste seule sur le site.
   2e temps (0,25 s) : la barre s'efface à son tour, la page défile. */
.menu .entete,
.menu__liste,
.menu__liste li,
.menu__cv,
.menu__bas {
  transition: opacity 0.35s, border-color 0.35s;
}

.menu.est-en-sortie {
  background-color: transparent;
}

.menu.est-en-sortie :is(.entete, .menu__cv, .menu__bas, .menu__liste li:not(:has(.est-choisi))) {
  opacity: 0;
}

.menu.est-en-sortie :is(.menu__liste, .menu__liste li) {
  border-color: transparent;
}

.menu.est-en-sortie--fin .est-choisi {
  opacity: 0;
  transition: opacity 0.25s;
}

@media (prefers-reduced-motion: reduce) {
  .menu,
  .menu *,
  .menu__lien::before {
    transition: none !important;
  }
}

/* "Télécharger mon CV" sous les liens, sur toute leur largeur */
.menu__cv {
  margin-top: var(--espace-32);
}

.menu__bas {
  display: flex;
  justify-content: space-between;
  margin-top: auto; /* colle la ligne en bas de l'écran */
  padding-top: var(--espace-40);
  font-size: var(--taille-13);
}

/* --- Pied de page --- */
.pied {
  display: grid;
  grid-template-columns: 1fr auto;
  row-gap: var(--espace-8);
  margin: 0 var(--marge);
  padding: var(--espace-28) 0 var(--espace-32);
  border-top: var(--epaisseur-filet) solid var(--couleur-filet);
  font-size: var(--taille-13);
}

/* Ordinateur : une seule ligne sur la grille. "Mentions légales" s'aligne sur la colonne de contenu
   des sections (colonne 5), "Haut de page" sur le bord droit */
@media (min-width: 64rem) {
  .pied {
    grid-template-columns: repeat(var(--colonnes), minmax(0, 1fr));
    column-gap: var(--gouttiere);
  }

  .pied > * {
    grid-row: 1;
  }

  .pied > p {
    grid-column: 1 / 5;
  }

  .pied__mentions {
    grid-column: 5 / 9;
  }

  .pied__haut {
    grid-column: 9 / -1;
    justify-self: end;
  }
}

/* ==========================================================
   5. Blocs de contenu réutilisables
   ========================================================== */

/* --- Section : filet en haut, 32 px au-dessus du titre ---
   L'espace du bas dépend de la section (relevé dans les maquettes) : chaque variante règle --espace-bas-section */
.section {
  margin: 0 var(--marge);
  padding-top: var(--espace-32);
  padding-bottom: var(--espace-bas-section, 0px);
  border-top: var(--epaisseur-filet) solid var(--couleur-filet);
}

/* Tablette : la même respiration au-dessus et en dessous de chaque section */
@media (min-width: 48rem) {
  .section {
    padding-block: var(--espace-section);
  }
}

/* Ordinateur : grille de 12 colonnes, titre à gauche (colonnes 1 à 3), contenu à droite (5 à 12).
   Le titre couvre toutes les rangées pour rester collé sous l'en-tête tant que sa section défile :
   10 rangées pour le contenu (3 au plus aujourd'hui), la dernière (1fr) absorbe un titre plus haut que le contenu */
@media (min-width: 64rem) {
  .section {
    display: grid;
    grid-template-columns: repeat(var(--colonnes), minmax(0, 1fr));
    grid-template-rows: repeat(10, auto) 1fr;
    align-items: start; /* sans étirement : le titre a de la place pour glisser dans sa colonne */
    column-gap: var(--gouttiere);
  }

  .section > * {
    grid-column: 5 / -1;
  }

  .section > :is(.titre-section, .section__entete) {
    position: sticky;
    top: var(--haut-titre-colle);
    grid-column: 1 / 4;
    grid-row: 1 / -1;
  }

  /* Le contenu commence à la hauteur du titre, pas en dessous */
  .section:not(.section--pleine) > :is(.titre-section, .section__entete) + * {
    margin-top: 0;
  }

  /* Carrousel dans la colonne de contenu : il ne déborde qu'à droite, jamais sous le titre */
  .section:not(.section--pleine) > .carrousel {
    margin-left: 0;
    padding-left: 0;
    scroll-padding-left: 0;
  }

  /* Variante pleine largeur (projets, contact) : titre au-dessus, contenu sur toute la largeur */
  .section--pleine {
    display: block;
  }

  .section--pleine > :is(.titre-section, .section__entete) {
    position: static;
  }
}

/* Titre + élément aligné à droite (ex. compteur "1 / 10") */
.section__entete {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
}

.titre-section {
  color: var(--couleur-accent);
  font-size: var(--taille-titre-section);
  font-weight: var(--graisse-medium);
}

/* Titre d'appel en très grand sur ordinateur, dans la police des grands titres
   ("Contact" de l'accueil, "On en parle ?" de la page À propos) */
@media (min-width: 64rem) {
  .titre-section--geant {
    font-family: var(--police-titre-projet);
    font-size: var(--taille-titre-contact);
    font-weight: var(--graisse-normale); /* Berkah Raya n'a qu'une graisse : pas de faux gras */
    line-height: 1;
  }
}

/* --- Paragraphes sous un titre de section --- */
.texte {
  margin-top: var(--espace-10);
}

.texte p + p {
  margin-top: var(--espace-12);
}

/* Tablette : lignes de 65 caractères au plus */
@media (min-width: 48rem) {
  .texte {
    max-width: var(--largeur-texte);
  }
}

/* --- Lignes "libellé / valeur" séparées par des filets --- */
.infos {
  border-top: var(--epaisseur-filet) solid var(--couleur-filet);
  font-size: var(--taille-14);
}

.infos__ligne {
  display: grid;
  grid-template-columns: var(--largeur-libelle) 1fr;
  align-items: baseline;
  padding: var(--espace-17) 0;
  border-bottom: var(--epaisseur-filet) solid var(--couleur-filet);
}

/* Variante sur 2 colonnes à partir de l'ordinateur (ex. "En dehors" de la page À propos) */
@media (min-width: 64rem) {
  .infos--colonnes {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: var(--gouttiere);
  }
}

/* --- Lien texte souligné (ex. "Voir tous les projets →") --- */
.lien-souligne {
  display: block;
  width: fit-content;
  color: var(--couleur-accent);
  font-size: var(--taille-15);
  font-weight: var(--graisse-medium);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 6px;
  transition: color var(--duree-survol);
}

/* Déjà souligné : au survol, il passe au bleu foncé du texte */
@media (hover: hover) {
  .lien-souligne:hover {
    color: var(--couleur-texte);
  }
}

/* Précision affichée seulement sur ordinateur (ex. nombre de projets) */
.lien-souligne__detail {
  display: none;
}

@media (min-width: 64rem) {
  .lien-souligne__detail {
    display: inline;
  }
}

/* --- Liste dont chaque élément est séparé par un filet --- */
.liste-filets {
  border-top: var(--epaisseur-filet) solid var(--couleur-filet);
}

.liste-filets > li {
  border-bottom: var(--epaisseur-filet) solid var(--couleur-filet);
}

/* --- Carte projet (vignette, titre, catégorie) ---
   Survol ou focus clavier : le titre se souligne, une flèche ↗ glisse
   (et la vignette reprend ses couleurs quand elle est en .bichromie, sur ordinateur) */
.carte-projet {
  display: block;
}

.carte-projet__visuel {
  display: block;
}

.carte-projet__image {
  width: 100%;
  aspect-ratio: 5 / 6;
  object-fit: cover;
  background: var(--couleur-vide);
}

.carte-projet__titre {
  margin-top: var(--espace-16);
  color: var(--couleur-accent);
  font-size: var(--taille-15);
  font-weight: var(--graisse-medium);
  text-decoration: underline 1px transparent;
  text-underline-offset: var(--decalage-soulignement);
  transition: text-decoration-color var(--duree-survol);
}

/* Flèche ↗ : seulement avec une souris (invisible au repos, elle glisse en place au survol) */
.carte-projet__fleche {
  display: none;
}

@media (hover: hover) {
  .carte-projet__fleche {
    display: inline-block;
    margin-left: 0.3em;
    opacity: 0;
    translate: -4px 4px;
    transition: opacity var(--duree-survol), translate var(--duree-mouvement) var(--courbe-sortie);
  }

  .carte-projet:is(:hover, :focus-visible) .carte-projet__fleche {
    opacity: 1;
    translate: 0 0;
  }
}

.carte-projet:is(:hover, :focus-visible) .carte-projet__titre {
  text-decoration-color: currentColor;
}

/* Sans animation : seulement le soulignement */
@media (prefers-reduced-motion: reduce) {
  .carte-projet__fleche {
    display: none;
  }
}

.carte-projet__categorie {
  margin-top: var(--espace-4);
  font-size: var(--taille-13);
}

/* Année et accroche : seulement dans la variante en ligne (ordinateur) */
:is(.carte-projet__annee, .carte-projet__accroche) {
  display: none;
}

/* Ordinateur, variante en ligne (.carte-projet--ligne) : un projet sur toute la largeur de la grille.
   Vignette en bandeau 3:1 à gauche (colonnes 1 à 5), pour une ligne compacte ; à droite (7 à 12), le titre en grand
   et la flèche ↗ au bout de la ligne, la catégorie et l'année, puis l'accroche (2 lignes au plus) posée sur le bas
   de la vignette.
   Toute la ligne reste un seul lien. Les filets entre les lignes viennent de la liste (.liste-projets) */
@media (min-width: 64rem) {
  .carte-projet--ligne {
    display: grid;
    grid-template-columns: repeat(var(--colonnes), minmax(0, 1fr));
    grid-template-rows: auto auto 1fr;
    column-gap: var(--gouttiere);
  }

  /* Sans étirement : si le texte est plus haut, le cadre (et ses calques .bichromie) reste à la taille de l'image */
  .carte-projet--ligne .carte-projet__visuel {
    grid-column: 1 / 6;
    grid-row: 1 / -1;
    align-self: start;
  }

  .carte-projet--ligne .carte-projet__image {
    aspect-ratio: 3 / 1;
  }

  .carte-projet--ligne :is(.carte-projet__titre, .carte-projet__categorie, .carte-projet__accroche) {
    grid-column: 7 / -1;
  }

  .carte-projet--ligne .carte-projet__titre {
    margin-top: 0;
    font-size: var(--taille-titre-ligne);
    line-height: 1.05;
    letter-spacing: -0.01em;
    overflow-wrap: break-word;
  }

  /* Flèche toujours visible, calée à droite de la 1re ligne du titre.
     En flottant : elle échappe au soulignement du titre */
  .carte-projet--ligne .carte-projet__fleche {
    display: inline-block;
    float: right;
    margin-left: var(--espace-16);
    opacity: 1;
    translate: 0 0;
  }

  .carte-projet--ligne .carte-projet__categorie {
    margin-top: var(--espace-12);
    font-size: var(--taille-14);
  }

  .carte-projet--ligne .carte-projet__annee {
    display: inline;
  }

  .carte-projet--ligne .carte-projet__accroche {
    display: -webkit-box;
    align-self: end;
    max-width: var(--largeur-texte);
    overflow: hidden;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    line-clamp: 2;
  }
}

/* Survol ou focus : la flèche part un peu vers le haut à droite */
@media (min-width: 64rem) and (hover: hover) and (prefers-reduced-motion: no-preference) {
  .carte-projet--ligne:is(:hover, :focus-visible) .carte-projet__fleche {
    translate: var(--espace-4) calc(-1 * var(--espace-4));
  }
}

/* --- Liste de projets (ordinateur) : une carte en ligne par élément, séparées par des filets --- */
@media (min-width: 64rem) {
  .liste-projets {
    border-top: var(--epaisseur-filet) solid var(--couleur-filet);
  }

  .liste-projets > li {
    padding-block: var(--espace-16);
    border-bottom: var(--epaisseur-filet) solid var(--couleur-filet);
  }
}

/* --- Bichromie : image en bleu foncé (ombres) et crème (lumières), vraies couleurs au survol ---
   Ordinateur seulement. Posée sur le cadre de l'image ; le survol ou le focus clavier du lien qui la contient
   rend les vraies couleurs en fondu. L'image passe en niveaux de gris (L), puis 2 calques :
   - ::before, --couleur-bichromie en "multiply" : L × couleur ;
   - ::after, bleu foncé en "screen" : bleu foncé + (1 − bleu foncé) × L × couleur.
   --couleur-bichromie est choisie pour que ce résultat aille exactement du bleu foncé (L = 0) au crème (L = 1).
   Seuls l'opacité des calques et le filtre changent : le fondu est possible */
@media (min-width: 64rem) {
  .bichromie {
    position: relative;
    isolation: isolate; /* les calques ne se mélangent qu'à l'image */
  }

  .bichromie img {
    filter: grayscale(1);
  }

  /* z-index : l'image filtrée est peinte avec les éléments positionnés, dans l'ordre du document ;
     sans lui, elle passerait au-dessus de ::before */
  .bichromie::before,
  .bichromie::after {
    content: "";
    position: absolute;
    z-index: 1;
    inset: 0;
    pointer-events: none;
  }

  .bichromie::before {
    background: var(--couleur-bichromie);
    mix-blend-mode: multiply;
  }

  .bichromie::after {
    background: var(--couleur-texte);
    mix-blend-mode: screen;
  }

  a:focus-visible .bichromie img {
    filter: none;
  }

  a:focus-visible .bichromie::before,
  a:focus-visible .bichromie::after {
    opacity: 0;
  }
}

@media (min-width: 64rem) and (hover: hover) {
  a:hover .bichromie img {
    filter: none;
  }

  a:hover .bichromie::before,
  a:hover .bichromie::after {
    opacity: 0;
  }
}

/* Fondu entre les deux états ; instantané sans animation */
@media (min-width: 64rem) and (prefers-reduced-motion: no-preference) {
  .bichromie img,
  .bichromie::before,
  .bichromie::after {
    transition:
      filter var(--duree-mouvement) var(--courbe-sortie),
      opacity var(--duree-mouvement) var(--courbe-sortie);
  }
}

/* ==========================================================
   6. Accueil
   ========================================================== */

/* --- Hero --- */
/* Occupe tout l'écran sous l'en-tête : le portrait est collé en bas,
   l'espace au-dessus s'ajuste à la hauteur de l'écran */
.hero {
  display: flex;
  flex-direction: column;
  min-height: calc(100vh - var(--hauteur-entete-complete));
  min-height: calc(100svh - var(--hauteur-entete-complete));
  padding: var(--espace-32) var(--marge) 0;
  /* L'ancre #accueil (lien "Accueil" du menu) ramène tout en haut de la page, en-tête compris :
     cette marge dépasse la hauteur de l'en-tête, le défilement s'arrête donc à 0 */
  scroll-margin-top: var(--hauteur-entete-complete);
}

/* Ordinateur : 2 moitiés (= colonnes 1 à 6 et 7 à 12 de la grille).
   À gauche, le logo et le bouton "Me contacter", centrés en hauteur ; à droite, le portrait posé sur le bandeau ;
   en bas, le bandeau d'un bord à l'autre. En-tête + hero = tout l'écran : à l'arrivée, rien de la
   Présentation n'est visible, pas même son filet (il commence juste sous le bas de l'écran).
   --pixel-min : plus petit agrandissement du portrait (× 2), arrondi à un nombre entier de pixels de l'écran */
@media (min-width: 64rem) {
  .hero {
    --pixel-min: calc(round(up, 2px * var(--densite), 1px) / var(--densite));
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    grid-template-rows: 1fr auto auto 1fr auto;
    grid-template-areas:
      ".        scene"
      "titre    scene"
      "actions  scene"
      ".        scene"
      "defilant defilant";
    column-gap: var(--gouttiere);
    padding-top: 0;
  }

  .hero__titre {
    grid-area: titre;
  }
}

/* Logo "Django" (SVG) en grand : grossit avec l'écran (194 px à 320 px de large, 236 px à 390 px, 404 px max) */
.hero__titre img {
  width: clamp(141px, 60.6vw, 404px);
}

/* Ordinateur : toute la moitié gauche, jusqu'à 640 px */
@media (min-width: 64rem) {
  .hero__titre img {
    width: min(100%, 640px);
  }
}

/* Qui je suis, en une phrase, sous le logo */
.hero__accroche {
  max-width: 22em;
  margin-top: var(--espace-12);
}

/* Ordinateur : hero épuré, logo et bouton seulement (display: none : les lecteurs d'écran la sautent aussi) */
@media (min-width: 64rem) {
  .hero__accroche {
    display: none;
  }
}

/* Bouton "Me contacter" : ordinateur seulement (en mobile, le contact suit juste en dessous) */
.hero__actions {
  display: none;
}

@media (min-width: 64rem) {
  .hero__actions {
    grid-area: actions;
    display: flex;
    flex-wrap: wrap;
    gap: var(--espace-12);
    margin-top: var(--espace-32);
  }

  .hero__actions .bouton {
    padding-inline: var(--espace-32);
  }
}

/* --- Bandeau défilant "| Graphisme | Web & UI/UX | …" ---
   Chaque copie de la liste recule de sa propre largeur en boucle :
   les copies se relaient, la boucle est donc continue quel que soit l'écran. */
.defilant {
  display: flex;
  margin: var(--espace-20) calc(-1 * var(--marge)) 0; /* d'un bord à l'autre de l'écran */
  overflow: hidden;
  /* Fondu sur les bords, de la largeur de la marge minimale (pas de --marge : 240 px de fondu en 1920) */
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 var(--marge-ecran), #000 calc(100% - var(--marge-ecran)), transparent);
  mask-image: linear-gradient(90deg, transparent, #000 var(--marge-ecran), #000 calc(100% - var(--marge-ecran)), transparent);
}

/* Ordinateur : bande entre 2 filets, sous le portrait (le filet du haut lui sert de sol) */
@media (min-width: 64rem) {
  .defilant {
    grid-area: defilant;
    margin-top: 0;
    padding-block: var(--espace-12);
    border-block: var(--epaisseur-filet) solid var(--couleur-filet);
  }
}

.defilant__liste {
  display: flex;
  flex: none;
  animation: defiler 22s linear infinite;
}

.defilant__liste li {
  white-space: nowrap;
  padding-right: 0.6em;
}

/* "|" devant chaque mot : en boucle, le dernier mot est suivi du "|" du premier */
.defilant__liste li::before {
  content: "|";
  margin-right: 0.6em;
  color: var(--couleur-frise);
}

/* Pause au survol pour pouvoir lire tranquillement */
.defilant:hover .defilant__liste {
  animation-play-state: paused;
}

@keyframes defiler {
  to {
    translate: -100% 0;
  }
}

/* Sans animation : une seule liste, fixe, qui passe à la ligne si besoin */
@media (prefers-reduced-motion: reduce) {
  .defilant {
    margin-inline: 0;
    -webkit-mask-image: none;
    mask-image: none;
  }

  .defilant__liste {
    flex: 1;
    flex-wrap: wrap;
    animation: none;
  }

  .defilant__liste[aria-hidden="true"] {
    display: none;
  }

  /* Le "|" de fin : "| Graphisme | … | IA |" */
  .defilant__liste::after {
    content: "|";
    color: var(--couleur-frise);
  }
}

/* --- Scène du portrait : tout l'espace entre le bandeau et le bas de l'écran,
   d'un bord à l'autre --- */
.hero__scene {
  position: relative;
  flex: 1;
  min-height: var(--hauteur-scene-min);
  margin-inline: calc(-1 * var(--marge));
}

/* Ordinateur : la moitié droite du hero, jusqu'au bord droit de l'écran (le portrait y est collé),
   assez haute pour le portrait agrandi au moins 2 fois */
@media (min-width: 64rem) {
  .hero__scene {
    grid-area: scene;
    min-height: calc(208 * var(--pixel-min));
    margin-left: 0;
  }
}

/* Cadre aux dimensions de la scène, qui sert d'unités (cqw / cqh) au portrait.
   En absolu car Chrome résout cqh à 0 sur un conteneur étiré par flex: 1. */
.hero__cadre {
  position: absolute;
  inset: 0;
  container-type: size;
}

/* Pastille "Disponible pour un stage" : posée au-dessus du portrait
   (hors du flux, pour ne pas lui prendre de hauteur) */
.disponibilite {
  position: absolute;
  z-index: 1;
  top: var(--espace-28);
  left: var(--marge);
  display: flex;
  align-items: center;
  gap: var(--espace-8);
  color: var(--couleur-accent);
  font-size: var(--taille-14);
}

.disponibilite::before {
  content: "";
  width: var(--taille-point);
  height: var(--taille-point);
  border-radius: 50%;
  background: currentColor;
}

/* Ordinateur : pas de pastille (hero épuré), les dates du stage sont dans la Présentation */
@media (min-width: 64rem) {
  .disponibilite {
    display: none;
  }
}

/* Portrait collé dans le coin en bas à droite, aussi grand que possible sans être rogné :
   il s'arrête à 10 % du bord gauche OU à 10 % de la scène sous le bandeau, selon ce qui
   arrive en premier (largeur = 90 % du cadre, ou 90 % de sa hauteur × ratio 187 / 208).
   <img> puis <canvas> de main.js : pixel art agrandi sans flou */
.hero__portrait {
  position: absolute;
  right: 0;
  bottom: 0;
  display: block;
  width: min(90cqw, 90cqh * 187 / 208);
  height: auto;
  image-rendering: pixelated;
}

/* Ordinateur : agrandissement entier, sinon les pixels n'ont pas tous la même taille.
   --taille-pixel = taille d'un pixel du portrait : le plus grand nombre entier de pixels de l'écran
   (--densite, posée par main.js) qui tient dans le cadre, au moins --pixel-min */
@media (min-width: 64rem) {
  .hero__portrait {
    --taille-pixel: max(
      round(down, min(100cqw / 187, 100cqh / 208) * var(--densite), 1px) / var(--densite),
      var(--pixel-min)
    );
    width: calc(187 * var(--taille-pixel));
  }
}

/* Ordinateur : l'image reste cachée jusqu'à ce que main.js la remplace par le canvas
   qui la génère pixel par pixel (sans JS ou sans animation, elle s'affiche normalement).
   Vaut aussi pour le portrait de l'À propos (a-propos.css) */
@media (min-width: 64rem) and (prefers-reduced-motion: no-preference) {
  .avec-js img[data-portrait-pixels] {
    visibility: hidden;
  }
}

/* --- Présentation --- */
.section--presentation {
  --espace-bas-section: var(--espace-50);
}

.section--presentation .infos {
  margin-top: var(--espace-32);
}

.presentation__cv {
  margin-top: var(--espace-24);
}

/* Ordinateur : le paragraphe en grand (colonnes 5 à 9) ; à côté, dans une colonne étroite (10 à 12),
   les infos (libellé au-dessus de la valeur) puis le bouton CV, à la largeur de cette colonne */
@media (min-width: 64rem) {
  .section--presentation .texte {
    grid-row: 1 / span 2;
    grid-column: 5 / 10;
    font-size: var(--taille-intro);
  }

  .section--presentation .infos {
    grid-row: 1;
    grid-column: 10 / -1;
    margin-top: 0;
  }

  .section--presentation .infos__ligne {
    grid-template-columns: 1fr;
    row-gap: var(--espace-4);
  }

  .presentation__cv {
    grid-row: 2;
    grid-column: 10 / -1;
    padding-inline: var(--espace-16);
  }
}

/* --- Projets : carrousel horizontal --- */
.section--projets {
  --espace-bas-section: var(--espace-62);
}

/* Toujours à droite du titre, suivi des flèches quand elles sont là */
.carrousel__compteur {
  margin-left: auto;
  font-size: var(--taille-14);
}

/* Flèches ← → (créées par main.js) : tablette et ordinateur, avec une souris seulement */
.carrousel__fleches {
  display: none;
  gap: var(--espace-8);
  align-self: center;
  margin-left: var(--espace-16);
}

.carrousel__fleche {
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--taille-fleche);
  height: var(--taille-fleche);
  border: 1px solid var(--couleur-accent);
  color: var(--couleur-accent);
  transition: background-color var(--duree-survol), color var(--duree-survol), opacity var(--duree-survol);
}

/* Au bout du carrousel */
.carrousel__fleche:disabled {
  opacity: 0.35;
  cursor: default;
}

@media (hover: hover) {
  .carrousel__fleche:not(:disabled):hover {
    background-color: var(--couleur-accent);
    color: var(--couleur-fond);
  }
}

@media (min-width: 48rem) and (hover: hover) and (pointer: fine) {
  .carrousel__fleches {
    display: flex;
  }
}

/* Déborde des marges pour défiler jusqu'aux bords de l'écran */
.carrousel {
  display: flex;
  gap: var(--espace-12);
  margin: var(--espace-18) calc(-1 * var(--marge)) 0;
  padding: 0 var(--marge);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-padding-inline: var(--marge);
  scrollbar-width: none;
}

.carrousel::-webkit-scrollbar {
  display: none;
}

.carrousel > li {
  flex: 0 0 var(--largeur-carte);
  scroll-snap-align: start;
}

.carrousel__points {
  display: flex;
  gap: var(--espace-8);
  margin-top: var(--espace-20);
}

.carrousel__points span {
  width: var(--taille-point);
  height: var(--taille-point);
  border-radius: 50%;
  background: var(--couleur-point);
}

.carrousel__points .est-actif {
  background: var(--couleur-accent);
}

.section--projets .lien-souligne {
  margin-top: var(--espace-32);
}

/* Ordinateur : le carrousel devient la liste des projets phares (.liste-projets, .carte-projet--ligne),
   les autres sont masqués. Compteur, flèches et points n'ont plus lieu d'être */
@media (min-width: 64rem) {
  .section--projets :is(.carrousel__compteur, .carrousel__fleches, .carrousel__points) {
    display: none;
  }

  .section--projets .carrousel {
    display: block;
    margin: var(--espace-32) 0 0;
    padding: 0;
    overflow: visible;
    scroll-snap-type: none;
  }

  .section--projets .carrousel > .carrousel__carte--secondaire {
    display: none;
  }

  .section--projets .lien-souligne {
    margin-top: var(--espace-48);
    font-size: var(--taille-20);
  }
}

/* --- Accordéons (Parcours, Compétences, Influences) --- */
.accordeon {
  margin: 0 var(--marge);
  border-top: var(--epaisseur-filet) solid var(--couleur-filet);
}

/* Ordinateur : Parcours et Compétences côte à côte, chacun sur une moitié.
   Sans étirement : l'un peut se replier sans changer la hauteur de l'autre.
   Même respiration que les sections au-dessus du titre et sous le contenu */
@media (min-width: 64rem) {
  .accordeons {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    align-items: start;
    column-gap: var(--gouttiere);
    margin: 0 var(--marge);
  }

  .accordeons > .accordeon {
    margin: 0;
  }

  .accordeons .accordeon__titre {
    padding-top: var(--espace-section);
  }

  .accordeons .accordeon__contenu {
    padding-bottom: var(--espace-section);
  }
}

/* Ligne "Parcours & Compétences" : remplace les 2 accordéons tant qu'ils sont fermés (.est-fusionne, main.js).
   Ordinateur et JS seulement ; même filet et mêmes espacements qu'un titre d'accordéon fermé :
   la hauteur ne saute pas quand la ligne laisse place aux accordéons */
.accordeons__fusion {
  display: none;
}

@media (min-width: 64rem) {
  .avec-js .accordeons.est-fusionne > .accordeon {
    display: none;
  }

  .avec-js .accordeons.est-fusionne > .accordeons__fusion {
    display: block;
    grid-column: 1 / -1;
    border-top: var(--epaisseur-filet) solid var(--couleur-filet);
  }

  .accordeons__bouton {
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 100%;
    padding: var(--espace-section) 0 var(--espace-42);
    text-align: left;
  }

  /* Chaque mot glisse seul pendant la transition (un nom de transition demande une boîte d'un seul tenant) */
  .accordeons__titre {
    display: inline-block;
  }
}

.accordeon__titre {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--espace-32) 0 var(--espace-42);
  list-style: none; /* retire le triangle par défaut */
  cursor: pointer;
}

.accordeon__titre::-webkit-details-marker {
  display: none;
}

/* Icône "+" en 2 barres : la verticale pivote et se couche sur l'horizontale pour faire "−".
   Pendant la fermeture animée (.est-en-fermeture, posée par main.js), elle se relève tout de suite */
.accordeon__icone {
  position: relative;
  width: 12px;
  height: 12px;
  color: var(--couleur-accent);
}

.accordeon__icone::before,
.accordeon__icone::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 0;
  width: 100%;
  height: 1.5px;
  margin-top: -0.75px;
  border-radius: 1px;
  background: currentColor;
}

.accordeon__icone::after {
  rotate: 90deg;
  transition: rotate 0.4s cubic-bezier(0.65, 0, 0.35, 1);
}

.accordeon[open]:not(.est-en-fermeture) .accordeon__icone::after {
  rotate: 0deg;
}

/* Le titre garde le même espacement ouvert ou fermé (sa hauteur ne saute pas pendant l'animation) :
   le contenu remonte sous sa marge du bas (42 px) pour n'en garder que 18 px */
.accordeon__contenu {
  position: relative; /* repère du voile pixelisé */
  margin-top: calc(var(--espace-18) - var(--espace-42));
  padding-bottom: var(--espace-50);
}

/* Voile pixelisé (2 canvas de main.js) posé sur le contenu pendant l'ouverture / la fermeture :
   un cache crème (= fond) et une teinte bleu pâle / bleu */
.accordeon__voile {
  position: absolute;
  inset: 0;
  z-index: 1;
  width: 100%;
  height: 100%;
  image-rendering: pixelated;
  pointer-events: none;
}

/* Garde la couleur la plus claire : le fond beige reste beige, seuls textes, logos et photos se teintent */
.accordeon__voile--teinte {
  mix-blend-mode: lighten;
}

@media (prefers-reduced-motion: reduce) {
  .accordeon__icone::after {
    transition: none;
  }
}

/* Parcours : frise verticale (puce + ligne reliant les étapes) */
.frise {
  padding-top: var(--espace-4);
}

.frise__etape {
  position: relative;
  min-height: var(--hauteur-etape-frise);
  padding-left: var(--espace-28);
}

.frise__etape::before {
  content: "";
  position: absolute;
  z-index: 1;
  top: 4px;
  left: 1.5px;
  width: var(--taille-puce-frise);
  height: var(--taille-puce-frise);
  border-radius: 50%;
  background: var(--couleur-accent);
}

/* Ligne du centre de la puce jusqu'à la puce suivante */
.frise__etape:not(:last-child)::after {
  content: "";
  position: absolute;
  top: 8.5px;
  left: 5.5px;
  width: 1px;
  height: 100%;
  background: var(--couleur-frise);
}

.frise__date {
  font-size: var(--taille-13);
}

.frise__titre {
  margin-top: var(--espace-3);
  color: var(--couleur-accent);
  font-size: var(--taille-16);
  font-weight: var(--graisse-medium);
}

.frise__texte {
  font-size: var(--taille-14);
}

/* Étape encore à compléter : puce grise, titre non accentué */
.frise__etape--a-completer::before {
  background: var(--couleur-frise);
}

.frise__etape--a-completer .frise__titre {
  color: var(--couleur-texte);
}

/* Compétences */
.competence {
  padding: var(--espace-16) 0 var(--espace-24);
}

.competence__titre {
  color: var(--couleur-accent);
  font-size: var(--taille-16);
  font-weight: var(--graisse-medium);
}

.outils {
  display: flex;
  flex-wrap: wrap;
  gap: var(--espace-8) var(--espace-16);
  margin-top: var(--espace-8);
  font-size: var(--taille-14);
}

.outil {
  display: flex;
  align-items: center;
  gap: var(--espace-8);
}

/* Logos de taille identique, quelle que soit leur forme d'origine */
.outil__logo {
  flex: none;
  width: var(--taille-logo);
  height: var(--taille-logo);
  object-fit: contain;
}

/* Ordinateur : les 3 groupes côte à côte, un outil par ligne */
@media (min-width: 64rem) {
  .accordeon__contenu > .liste-filets {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    column-gap: var(--espace-24);
  }

  .outils {
    flex-direction: column;
    gap: var(--espace-12);
    margin-top: var(--espace-16);
  }
}

/* Influences : image à gauche, texte à droite */
.influence {
  display: flex;
  align-items: flex-start;
  gap: var(--espace-12);
  padding: var(--espace-20) 0;
}

.influence__image {
  flex: 0 0 var(--largeur-image-influence);
  height: var(--hauteur-image-influence);
  object-fit: cover;
  background: var(--couleur-vide);
}

.influence__nom {
  margin-top: 2px;
  color: var(--couleur-accent);
  font-size: var(--taille-16);
  font-weight: var(--graisse-medium);
}

.influence__role {
  margin-top: var(--espace-3);
  font-size: var(--taille-13);
}

.influence__texte {
  margin-top: var(--espace-3);
  font-size: var(--taille-14);
}

/* --- Contact --- */
.section--contact {
  --espace-bas-section: var(--espace-60);
}

.liens-contact {
  margin-top: var(--espace-24);
  border-top: var(--epaisseur-filet) solid var(--couleur-filet);
}

.liens-contact li {
  border-bottom: var(--epaisseur-filet) solid var(--couleur-filet);
}

.lien-contact {
  display: grid;
  grid-template-columns: var(--largeur-libelle) 1fr auto;
  align-items: center;
  min-height: calc(var(--hauteur-ligne-contact) - var(--epaisseur-filet));
  font-size: var(--taille-14);
}

/* Survol : le texte (pas l'icône ni la flèche) se souligne (mobile et tablette ; sur ordinateur, la case se remplit) */
.lien-contact > :not([aria-hidden]) {
  text-decoration: underline 1px transparent;
  text-underline-offset: var(--decalage-soulignement);
  transition: text-decoration-color var(--duree-survol);
}

@media (hover: hover) {
  .lien-contact:hover > :not([aria-hidden]) {
    text-decoration-color: currentColor;
  }
}

/* "Copier l'adresse" (main.js) : ordinateur seulement */
.liens-contact__copier {
  display: none;
}

/* Ordinateur : "Contact" en très grand (.titre-section--geant) ; l'e-mail en très grand sur toute la largeur,
   avec le bouton copier ; téléphone, Instagram et LinkedIn sur une rangée de 3 en dessous */
@media (min-width: 64rem) {
  .section--contact .texte {
    margin-top: var(--espace-24);
  }

  .liens-contact {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    column-gap: var(--gouttiere);
    margin-top: var(--espace-48);
  }

  .lien-contact {
    grid-template-columns: auto 1fr auto;
    column-gap: var(--espace-12);
  }

  .liens-contact__principal {
    display: flex;
    grid-column: 1 / -1;
    align-items: center;
    gap: var(--espace-24);
  }

  .liens-contact__principal .lien-contact {
    display: flex;
    flex: 1;
    align-items: baseline;
    gap: 0.3em;
    min-width: 0;
    min-height: 0;
    margin-right: 0; /* le fond s'arrête à son retrait : l'écart avec "Copier l'adresse" reste entier */
    padding-block: var(--espace-24);
    color: var(--couleur-accent);
    font-size: var(--taille-contact);
    font-weight: var(--graisse-medium);
    line-height: 1.1;
    overflow-wrap: anywhere;
  }

  .liens-contact__principal .lien-contact__icone {
    display: none;
  }

  /* Largeur fixe : pas de saut quand le texte passe à "Copié ✓" */
  .liens-contact__copier {
    display: flex;
    flex-shrink: 0;
    min-width: 12em; /* plus large que "Copier l'adresse" */
    padding-inline: var(--espace-24);
  }
}

/* Ordinateur : survol ou focus clavier d'une ligne de contact = toute la case se remplit de bleu foncé,
   texte, flèche et icône en crème (contraste élevé ; le crème sur l'accent n'atteindrait pas AA en 14 px).
   Même balayage que les liens du menu : de gauche à droite, même durée, même courbe. Plus de soulignement.
   Retrait intérieur compensé par une marge négative : au repos, le texte reste aligné sur la grille,
   seul le fond déborde de --retrait-ligne-contact dans la marge ou la gouttière.
   "Copier l'adresse" est à côté du lien, pas dedans : son survol ne remplit pas la ligne de l'e-mail */
@media (min-width: 64rem) {
  .lien-contact {
    position: relative;
    z-index: 0; /* garde le fond qui balaie (z-index: -1) derrière le texte */
    margin-inline: calc(-1 * var(--retrait-ligne-contact));
    padding-inline: var(--retrait-ligne-contact);
    transition: color var(--duree-balayage);
  }

  .lien-contact::before {
    content: "";
    position: absolute;
    z-index: -1;
    inset: 0;
    background: var(--couleur-texte);
    transform: scaleX(0);
    transform-origin: left;
    transition: transform var(--duree-balayage) var(--courbe-balayage);
  }

  .lien-contact > :not([aria-hidden]) {
    text-decoration: none;
  }

  .lien-contact__icone {
    transition: background-color var(--duree-balayage);
  }

  .lien-contact:focus-visible {
    color: var(--couleur-fond);
  }

  .lien-contact:focus-visible::before {
    transform: scaleX(1);
  }

  .lien-contact:focus-visible .lien-contact__icone {
    background: var(--couleur-fond);
  }
}

@media (min-width: 64rem) and (hover: hover) {
  .lien-contact:hover {
    color: var(--couleur-fond);
  }

  .lien-contact:hover::before {
    transform: scaleX(1);
  }

  .lien-contact:hover .lien-contact__icone {
    background: var(--couleur-fond);
  }
}

/* Sans animation : la case se remplit d'un coup */
@media (min-width: 64rem) and (prefers-reduced-motion: reduce) {
  .lien-contact,
  .lien-contact::before,
  .lien-contact__icone {
    transition: none;
  }
}

/* ==========================================================
   7. Pages intérieures (projets, projet, mentions légales)
   ========================================================== */
.page {
  margin: 0 var(--marge);
}

.titre-page {
  margin-top: var(--espace-40);
  color: var(--couleur-accent);
  font-size: var(--taille-titre-page);
  font-weight: var(--graisse-medium);
}

/* Tablette : plus d'air sous l'en-tête, le titre en grand garde sa place */
@media (min-width: 48rem) {
  .titre-page {
    margin-top: var(--espace-48);
    line-height: 1.1;
  }
}

.sous-titre-page {
  margin-top: var(--espace-8);
  font-size: var(--taille-15);
}

/* ==========================================================
   8. Tous les projets
   ========================================================== */
.page--projets {
  padding-bottom: var(--espace-57);
}

@media (min-width: 48rem) {
  .page--projets {
    padding-bottom: var(--espace-section);
  }
}

/* --- Sous-titre et filtres ---
   Tablette : sur une ligne sous le titre (sous-titre à gauche, filtres à droite, alignés sur la ligne de base) ;
   le filet passe sous toute la ligne. Ils repassent l'un sous l'autre si la place manque */
@media (min-width: 48rem) {
  .barre-filtres {
    display: flex;
    flex-wrap: wrap;
    align-items: last baseline;
    justify-content: space-between;
    column-gap: var(--espace-32);
    margin-top: var(--espace-32);
    border-bottom: var(--epaisseur-filet) solid var(--couleur-filet);
  }

  .barre-filtres .sous-titre-page {
    margin-top: 0;
  }
}

/* --- Filtres : onglet actif souligné, filet sous l'ensemble --- */
.filtres {
  display: flex;
  gap: var(--espace-18);
  margin-top: var(--espace-39);
  border-bottom: var(--epaisseur-filet) solid var(--couleur-filet);
}

.filtre {
  padding-bottom: var(--espace-10);
  border-bottom: 1px solid transparent;
  font-size: var(--taille-14);
}

.filtre[aria-pressed="true"] {
  border-bottom-color: var(--couleur-accent);
  color: var(--couleur-accent);
  font-weight: var(--graisse-medium);
}

.filtre {
  transition: color var(--duree-survol);
}

@media (hover: hover) {
  .filtre:hover {
    color: var(--couleur-accent);
  }
}

/* Tablette : filtres plus grands, cliquables sur toute la hauteur de la ligne (44 px) */
@media (min-width: 48rem) {
  .filtres {
    gap: var(--espace-32);
    margin-top: 0;
    border-bottom: 0;
  }

  .filtre {
    display: flex;
    align-items: flex-end;
    min-height: var(--hauteur-entete);
    padding-bottom: var(--espace-12);
    font-size: var(--taille-16);
  }
}

/* Filtrage animé (document.startViewTransition, main.js) : les cartes glissent jusqu'à leur nouvelle place,
   celles qui partent ou arrivent se fondent ; le reste de la page ne bouge pas.
   Même réglage pour la ligne "Parcours & Compétences" de l'accueil (.est-en-fusion) : les titres glissent,
   le "&" se fond, et les accordéons s'animent en direct dessous.
   Limité à ces 2 classes : le fondu entre 2 pages (@view-transition, plus haut) n'est pas touché */
html:is(.est-en-filtrage, .est-en-fusion)::view-transition-old(root),
html:is(.est-en-filtrage, .est-en-fusion)::view-transition-new(root) {
  animation: none;
}

html:is(.est-en-filtrage, .est-en-fusion)::view-transition-group(*) {
  animation-duration: 0.4s;
  animation-timing-function: cubic-bezier(0.65, 0, 0.35, 1);
}

/* --- Grille de cartes sur 2 colonnes --- */
.grille-projets {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--espace-27) var(--espace-12);
  margin-top: var(--espace-24);
}

/* Tablette : 3 colonnes, espacées comme la grille */
@media (min-width: 48rem) {
  .grille-projets {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--espace-48) var(--gouttiere);
    margin-top: var(--espace-32);
  }
}

/* Ordinateur : la grille devient une liste, une ligne par projet (.liste-projets, .carte-projet--ligne).
   Collée à la barre des filtres, dont le filet sert de premier filet (pas deux filets à 32 px l'un de l'autre) */
@media (min-width: 64rem) {
  .grille-projets {
    display: block;
    margin-top: 0;
    border-top: 0;
  }

  /* Plus de place réservée pour un titre sur 2 lignes (voir plus bas) : chaque ligne suit son contenu */
  .grille-projets .carte-projet__titre {
    min-height: 0;
  }
}

/* Réglages des cartes de la grille (mobile et tablette). En :where() : ils ne pèsent pas plus
   qu'une classe, la variante en ligne de l'ordinateur (.carte-projet--ligne) passe devant */
:where(.grille-projets) .carte-projet__image {
  aspect-ratio: 4 / 5;
}

/* Place réservée pour un titre sur 2 lignes : les catégories restent alignées */
:where(.grille-projets) .carte-projet__titre {
  min-height: calc(2em * var(--interligne));
  margin-top: var(--espace-6);
}

:where(.grille-projets) .carte-projet__categorie {
  margin-top: var(--espace-9);
}

/* ==========================================================
   9. Projet
   ========================================================== */

/* --- Haut : photo de fond + voile, texte blanc --- */
.haut-projet {
  position: relative;
  isolation: isolate; /* garde la photo et le voile derrière le texte */
  height: var(--hauteur-haut-projet);
  padding: var(--hauteur-entete-complete) var(--marge) 0; /* l'en-tête est superposé à la photo */
  background: var(--couleur-fond-photo);
  color: var(--couleur-blanc);
}

.haut-projet__fond {
  position: absolute;
  z-index: -2;
  inset: 0;
  width: 100%;
  max-width: none;
  height: 100%;
  object-fit: cover;
}

.haut-projet::before {
  content: "";
  position: absolute;
  z-index: -1;
  inset: 0;
  background: var(--voile-photo);
}

.haut-projet__categorie {
  margin-top: var(--espace-40);
  font-size: var(--taille-14);
}

.haut-projet__titre {
  font-family: var(--police-titre-projet);
  font-size: var(--taille-titre-projet);
  font-weight: var(--graisse-normale); /* Berkah Raya n'a qu'une graisse : pas de faux gras */
  overflow-wrap: break-word; /* un mot trop long passe à la ligne au lieu de déborder */
}

/* Titre en art ASCII ("titreAscii" dans projet.json), comme dans le jeu, en blanc.
   Variante --balayage (si le projet a des couleurs) : toutes les 3 s, les couleurs du jeu le balaient
   de gauche à droite (1,5 s), puis il redevient blanc. Le dégradé blanc | couleurs | blanc (--degrade-titre,
   posé par projet.php) fait 3 fois la largeur du titre et glisse derrière le texte (background-clip: text).
   Fixe et blanc sans animation.
   La taille des caractères suit la largeur disponible pour que la ligne la plus longue
   (--colonnes-titre) tienne toujours ; lignes collées pour que les blocs se touchent */
.haut-projet__titre--ascii {
  container-type: inline-size;
  width: 100%;
  margin-top: var(--espace-12);
}

.titre-ascii {
  margin: 0;
  font-family: Consolas, ui-monospace, "Cascadia Mono", Menlo, "DejaVu Sans Mono", monospace;
  font-size: min(12px, 100cqi / var(--colonnes-titre) / 0.6);
  line-height: 1;
  white-space: pre;
  width: fit-content; /* le dégradé se cale sur le titre, pas sur toute la largeur */
  color: var(--couleur-blanc);
  filter: drop-shadow(0 0 0.35em rgb(255 255 255 / 0.35)); /* halo blanc, comme le néon du jeu */
}

@media (prefers-reduced-motion: no-preference) {
  .titre-ascii--balayage {
    background: var(--degrade-titre) 100% 0 / 300% 100% no-repeat;
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    animation: titre-balayage 3s ease-in-out infinite;
  }
}

@keyframes titre-balayage {
  0% {
    background-position: 100% 0; /* tiers droit du dégradé : blanc */
  }

  50%,
  100% {
    background-position: 0% 0; /* tiers gauche : blanc, les couleurs sont passées */
  }
}

/* Logo à la place du titre ("logoTitre" dans projet.json) : un peu plus haut que le titre en texte,
   pour garder le même poids visuel ; jamais plus large que l'écran */
.haut-projet__logo {
  width: auto;
  max-width: 100%;
  height: max(64px, var(--taille-titre-projet) * 1.1);
  object-fit: contain; /* un logo très large rétrécit sans être écrasé */
  object-position: left bottom;
  margin-top: var(--espace-12);
}

/* Ordinateur : 80 % de l'écran (--hauteur-haut-projet), titre en très grand en bas à gauche,
   catégorie et année au-dessus ; un 2e voile, en bas, garde le texte blanc lisible sur la photo */
@media (min-width: 64rem) {
  .haut-projet {
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    padding-bottom: var(--espace-48);
  }

  .haut-projet::before {
    background: var(--voile-photo), var(--voile-photo-bas);
  }

  .haut-projet__categorie {
    margin-top: 0;
    font-size: var(--taille-16);
  }

  .haut-projet__titre {
    margin-top: var(--espace-8);
    line-height: 1;
  }
}

/* --- Accroche + infos --- */
.intro-projet {
  margin: 0 var(--marge);
  padding: var(--espace-35) 0 var(--espace-50);
}

.accroche {
  font-size: var(--taille-accroche);
}

/* Tablette : en grand, la phrase reste courte (environ 55 caractères par ligne) */
@media (min-width: 48rem) {
  .accroche {
    max-width: 32em;
  }
}

.intro-projet .infos {
  margin-top: var(--espace-66);
}

/* --- Fiche (accroche + infos + présentation) ---
   Ordinateur : grille de 12 colonnes. Les infos (colonnes 1 à 4) restent collées sous l'en-tête pendant
   qu'on lit l'accroche et la présentation (colonnes 6 à 12) ; elles s'arrêtent à la fin de la fiche.
   L'ordre du HTML (accroche, infos, présentation) est celui du mobile : .intro-projet s'efface (display: contents)
   pour que ses enfants se placent directement dans la grille */
@media (min-width: 64rem) {
  .fiche-projet {
    display: grid;
    grid-template-columns: repeat(var(--colonnes), minmax(0, 1fr));
    grid-template-rows: auto 1fr;
    column-gap: var(--gouttiere);
    margin: 0 var(--marge);
    padding-block: var(--espace-section);
  }

  .fiche-projet > .intro-projet {
    display: contents;
  }

  .fiche-projet .accroche {
    grid-row: 1;
    grid-column: 6 / -1;
  }

  .fiche-projet .infos {
    position: sticky;
    top: var(--haut-titre-colle);
    grid-row: 1 / -1;
    grid-column: 1 / 5;
    align-self: start;
    margin-top: 0;
  }

  .fiche-projet > .section {
    grid-row: 2;
    grid-column: 6 / -1;
    margin: var(--espace-48) 0 0;
    padding: var(--espace-32) 0 0;
  }
}

/* --- Présentation : Contexte, Démarche, Résultat --- */
.section--presentation-projet {
  --espace-bas-section: var(--espace-31);
}

.etapes-projet {
  margin-top: var(--espace-17);
}

.etape-projet + .etape-projet {
  margin-top: var(--espace-30);
}

.etape-projet__titre {
  color: var(--couleur-accent);
  font-size: var(--taille-16);
  font-weight: var(--graisse-medium);
}

.etape-projet__texte {
  margin-top: var(--espace-2);
}

/* --- Galerie (carrousel de l'accueil, images 4:3) --- */
.section--galerie {
  --espace-bas-section: var(--espace-64);
}

/* Chaque image est un bouton qui l'ouvre dans la visionneuse (main.js) */
.galerie__bouton {
  display: block;
  width: 100%;
  cursor: zoom-in;
}

.galerie__image {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  background: var(--couleur-vide);
}

.section--galerie .carrousel__points {
  margin-top: var(--espace-25);
}

.section--galerie .carrousel > li {
  flex-basis: var(--largeur-image-galerie);
}

/* Ordinateur : le même carrousel que sur tablette (compteur, points, flèches, touches ← →),
   avec des images plus grandes (--largeur-image-galerie) espacées comme la grille */
@media (min-width: 64rem) {
  .section--galerie .carrousel {
    gap: var(--gouttiere);
  }
}

/* --- Étude de cas : chapitres numérotés (includes/chapitres.php) ---
   Mobile : numéro, titre, texte puis blocs, les uns sous les autres.
   Ordinateur : titre à gauche (colonnes 1 à 4) et texte à droite (6 à 12), comme la fiche au-dessus ;
   les visuels prennent ensuite toute la largeur de la grille */
.chapitre {
  margin: 0 var(--marge);
  padding: var(--espace-32) 0 var(--espace-48);
  border-top: var(--epaisseur-filet) solid var(--couleur-filet);
}

.chapitre__numero {
  display: block;
  margin-bottom: var(--espace-4);
  color: var(--couleur-texte);
  font-size: var(--taille-14);
  font-weight: var(--graisse-normale);
  font-variant-numeric: tabular-nums;
}

.chapitre__texte {
  margin-top: var(--espace-10);
}

@media (min-width: 48rem) {
  .chapitre {
    padding-block: var(--espace-section);
  }

  .chapitre__texte {
    max-width: var(--largeur-texte);
  }
}

@media (min-width: 64rem) {
  .chapitre {
    display: grid;
    grid-template-columns: repeat(var(--colonnes), minmax(0, 1fr));
    align-items: start;
    column-gap: var(--gouttiere);
  }

  .chapitre > * {
    grid-column: 1 / -1;
  }

  .chapitre__titre {
    grid-column: 1 / 5;
  }

  .chapitre__texte {
    grid-row: 1;
    grid-column: 6 / -1;
    margin-top: 0;
    font-size: var(--taille-intro);
  }
}

/* --- Blocs d'un chapitre ---
   Des visuels qui se suivent restent serrés : ils se lisent comme une seule composition */
.bloc {
  margin: var(--espace-32) 0 0;
}

.bloc:is(.bloc--image, .bloc--mosaique, .bloc--rangee) + .bloc:is(.bloc--image, .bloc--mosaique, .bloc--rangee) {
  margin-top: var(--espace-12);
}

@media (min-width: 64rem) {
  .bloc {
    margin-top: var(--espace-64);
  }

  .bloc:is(.bloc--image, .bloc--mosaique, .bloc--rangee) + .bloc:is(.bloc--image, .bloc--mosaique, .bloc--rangee) {
    margin-top: var(--gouttiere);
  }

  /* Paragraphes titrés : dans la colonne du texte, à côté du titre s'ils le suivent */
  .chapitre > .bloc--points {
    grid-column: 6 / -1;
  }

  .chapitre__titre + .bloc--points {
    margin-top: 0;
  }

  /* Option "largeur": "etroite" : alignée sur la colonne du texte */
  .chapitre > .bloc--etroit {
    grid-column: 6 / -1;
  }
}

/* Image en entier, à ses proportions (width / height dans le HTML) */
.bloc__image {
  width: 100%;
  background: var(--couleur-vide);
}

.bloc__titre {
  color: var(--couleur-accent);
  font-size: var(--taille-16);
  font-weight: var(--graisse-medium);
}

.bloc__legende {
  margin-top: var(--espace-11);
  font-size: var(--taille-13);
}

/* Mosaïque : la 1re image en grand, les 2 suivantes en dessous côte à côte (mobile)
   ou empilées à sa droite (ordinateur, 2/3 - 1/3 : des images 16:9 tombent juste) */
.mosaique {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--espace-12);
}

.mosaique > :first-child {
  grid-column: 1 / -1;
}

.mosaique .bloc__image {
  height: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
}

@media (min-width: 64rem) {
  .mosaique {
    grid-template-columns: 2fr 1fr;
    gap: var(--gouttiere);
  }

  .mosaique > :first-child {
    grid-row: 1 / span 2;
    grid-column: 1;
  }

  .mosaique > .galerie__bouton {
    height: 100%;
  }

  .mosaique > :first-child .bloc__image {
    aspect-ratio: auto;
  }
}

/* Rangée : images à la même hauteur, chacune aussi large que sa proportion (--proportion, posée par chapitres.php).
   Mobile : empilées, sauf la variante compacte (images en portrait) qui reste sur une ligne */
.rangee {
  display: flex;
  flex-direction: column;
  gap: var(--espace-12);
}

.rangee--compacte {
  flex-direction: row;
}

.rangee--compacte > .rangee__element {
  flex: var(--proportion) 1 0%;
  min-width: 0;
}

@media (min-width: 48rem) {
  .rangee {
    flex-direction: row;
    gap: var(--gouttiere);
  }

  .rangee__element {
    flex: var(--proportion) 1 0%;
    min-width: 0;
  }
}

/* Grille d'images de même format : 2 colonnes en mobile, --colonnes-grille (posée par chapitres.php) ensuite */
.bloc--grille {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--espace-12);
}

/* Mobile : une dernière image seule sur sa ligne prend toute la largeur, plutôt que de laisser un trou
   (avec une vedette, qui occupe déjà une ligne entière, c'est le cas quand le total est pair) */
@media (max-width: 47.999rem) {
  .bloc--grille:not(.bloc--grille-vedette) > :last-child:nth-child(odd),
  .bloc--grille-vedette > :last-child:nth-child(even) {
    grid-column: 1 / -1;
  }
}

/* Vedette : la 1re image sur toute la largeur en mobile, sur 2 colonnes et 2 lignes ensuite
   (les 2 images suivantes s'empilent à sa droite avec 3 colonnes) */
.bloc--grille-vedette > :first-child {
  grid-column: 1 / -1;
}

@media (min-width: 48rem) {
  .bloc--grille-vedette > :first-child {
    grid-column: span 2;
    grid-row: span 2;
  }
}

@media (min-width: 48rem) {
  .bloc--grille {
    grid-template-columns: repeat(var(--colonnes-grille), minmax(0, 1fr));
    gap: var(--gouttiere);
  }
}

/* Des visuels qui se suivent restent serrés, grille comprise */
.bloc:is(.bloc--image, .bloc--mosaique, .bloc--rangee, .bloc--grille) + .bloc--grille {
  margin-top: var(--espace-12);
}

@media (min-width: 64rem) {
  .bloc:is(.bloc--image, .bloc--mosaique, .bloc--rangee, .bloc--grille) + .bloc--grille {
    margin-top: var(--gouttiere);
  }
}

/* Carrousel d'un chapitre : images à leurs proportions, presque toute la largeur en mobile,
   une et demie à l'écran sur ordinateur (on voit qu'il y a une suite) */
.carrousel--bloc > li {
  flex-basis: calc(100cqw - 2 * var(--marge) - var(--espace-32));
}

.bloc--carrousel .carrousel__points {
  margin-top: var(--espace-25);
}

@media (min-width: 64rem) {
  .carrousel--bloc {
    gap: var(--gouttiere);
  }

  .carrousel--bloc > li {
    flex-basis: calc((100cqw - 2 * var(--marge) - var(--gouttiere)) / 1.5);
  }
}

/* Logos blancs sur l'aplat de couleur de la marque (couleur posée par chapitres.php) */
.bloc--logos {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--espace-40) var(--espace-64);
  padding: var(--espace-48) var(--espace-24);
}

.bloc__logo {
  width: auto;
  max-width: 100%;
  height: clamp(72px, 10vw, 152px);
  object-fit: contain; /* un logo très large rétrécit sans être écrasé */
}

@media (min-width: 64rem) {
  .bloc--logos {
    padding-block: var(--espace-section);
  }
}

/* Couleurs de la charte : pastille, nom, valeur. 3 par ligne en mobile, toutes sur une ligne ensuite */
.bloc--couleurs {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--espace-24) var(--espace-12);
}

@media (min-width: 48rem) {
  .bloc--couleurs {
    grid-template-columns: none;
    grid-auto-columns: minmax(0, 1fr);
    grid-auto-flow: column;
    column-gap: var(--gouttiere);
  }
}

.couleur {
  display: flex;
  flex-direction: column;
}

.couleur__pastille {
  aspect-ratio: 4 / 3;
  margin-bottom: var(--espace-10);
  box-shadow: inset 0 0 0 var(--epaisseur-filet) var(--couleur-filet); /* une couleur claire reste visible sur le fond */
}

@media (min-width: 48rem) {
  .couleur__pastille {
    aspect-ratio: 2 / 1;
  }
}

.couleur__nom {
  font-size: var(--taille-14);
  font-weight: var(--graisse-medium);
}

.couleur__valeur {
  margin-top: var(--espace-2);
  font-size: var(--taille-12);
  font-variant-numeric: tabular-nums;
}

/* Chiffres clés : 2 par ligne en mobile, tous sur une ligne ensuite */
.bloc--chiffres {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--espace-24) var(--espace-12);
}

@media (min-width: 48rem) {
  .bloc--chiffres {
    grid-template-columns: none;
    grid-auto-columns: minmax(0, 1fr);
    grid-auto-flow: column;
    column-gap: var(--gouttiere);
  }
}

.chiffre {
  display: flex;
  flex-direction: column;
  gap: var(--espace-4);
  padding-top: var(--espace-12);
  border-top: var(--epaisseur-filet) solid var(--couleur-filet);
}

.chiffre__valeur {
  color: var(--couleur-accent);
  font-size: clamp(40px, 5vw, 72px);
  font-weight: var(--graisse-medium);
  font-variant-numeric: tabular-nums;
  line-height: 1;
}

.chiffre__libelle {
  font-size: var(--taille-14);
}

/* Étapes numérotées : 1 colonne en mobile, 2 sur tablette, 3 sur ordinateur */
.bloc--etapes {
  display: grid;
  gap: var(--espace-24) var(--gouttiere);
}

@media (min-width: 48rem) {
  .bloc--etapes {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (min-width: 64rem) {
  .bloc--etapes {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

.etape {
  padding-top: var(--espace-12);
  border-top: var(--epaisseur-filet) solid var(--couleur-filet);
}

.etape__numero {
  font-size: var(--taille-13);
  font-variant-numeric: tabular-nums;
}

.etape__titre {
  margin-top: var(--espace-4);
  color: var(--couleur-accent);
  font-weight: var(--graisse-medium);
}

.etape__texte {
  margin-top: var(--espace-2);
  font-size: var(--taille-14);
}

/* Avant / après : "avant" recouvre "après", découpée à --position (posée par main.js selon le curseur).
   Le curseur est un input range invisible sur toute l'image : clic, glissement, tactile et flèches du clavier.
   Sa poignée (zone de prise) couvre toute la hauteur, pour qu'on puisse l'attraper au doigt n'importe où */
.avant-apres {
  position: relative;
  max-width: min(100%, 80svh); /* image carrée : elle tient dans l'écran, on la manipule sans défiler */
  overflow: hidden;
  background: var(--couleur-vide);
}

.avant-apres__image {
  display: block;
  width: 100%;
}

.avant-apres__image--avant {
  position: absolute;
  inset: 0;
  height: 100%;
  clip-path: inset(0 calc(100% - var(--position)) 0 0);
}

.avant-apres__libelle {
  position: absolute;
  top: var(--espace-12);
  left: var(--espace-12);
  padding: var(--espace-4) var(--espace-8);
  background: rgb(14 28 62 / 0.7); /* --couleur-marine voilée */
  color: var(--couleur-blanc);
  font-size: var(--taille-13);
  pointer-events: none;
}

.avant-apres__libelle--apres {
  left: auto;
  right: var(--espace-12);
}

/* Ligne verticale + rond à flèches, à la position du curseur */
.avant-apres__poignee {
  position: absolute;
  top: 0;
  bottom: 0;
  left: var(--position);
  width: 2px;
  background: var(--couleur-blanc);
  translate: -50% 0;
  pointer-events: none;
}

.avant-apres__poignee::after {
  content: "‹ ›";
  position: absolute;
  top: 50%;
  left: 50%;
  display: grid;
  place-items: center;
  width: var(--taille-fleche);
  height: var(--taille-fleche);
  border-radius: 50%;
  background: var(--couleur-blanc);
  color: var(--couleur-marine);
  font-size: var(--taille-18);
  translate: -50% -50%;
}

.avant-apres__curseur {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  cursor: ew-resize;
  appearance: none;
  -webkit-appearance: none;
}

.avant-apres__curseur::-webkit-slider-thumb {
  width: var(--taille-fleche);
  height: 100vh;
  -webkit-appearance: none;
}

.avant-apres__curseur::-moz-range-thumb {
  width: var(--taille-fleche);
  height: 100vh;
}

/* Le curseur est invisible : le focus clavier s'affiche autour de l'image */
.avant-apres:has(.avant-apres__curseur:focus-visible) {
  outline: 2px solid var(--couleur-accent);
  outline-offset: 2px;
}

/* Modèle 3D : carré en mobile, 4:3 ensuite, jamais plus haut que l'écran (on le manipule sans défiler).
   L'aperçu fixe occupe le cadre jusqu'à ce que <model-viewer> ait chargé le modèle (.est-charge, main.js) */
.modele-3d {
  position: relative;
  aspect-ratio: 1;
  max-height: 80svh;
  margin-inline: auto;
  background: var(--couleur-vide);
}

@media (min-width: 48rem) {
  .modele-3d {
    aspect-ratio: 4 / 3;
  }
}

.modele-3d__apercu,
.modele-3d__visionneuse {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

.modele-3d__apercu {
  object-fit: contain;
}

.modele-3d__visionneuse {
  --poster-color: transparent;
  --progress-bar-height: 0px; /* pas de barre de chargement : l'aperçu fixe occupe la place pendant ce temps */
  opacity: 0;
  transition: opacity var(--duree-mouvement) var(--courbe-sortie);
}

.modele-3d.est-charge .modele-3d__visionneuse {
  opacity: 1;
}

.modele-3d.est-charge .modele-3d__apercu {
  visibility: hidden;
}

/* Configurateur : scène 3D teintée de la variante (--teinte, posée par chapitres.php puis main.js), fiche,
   puis pastilles et flèches. Mobile : les uns sous les autres. Ordinateur : scène à gauche (colonnes 1 à 7),
   fiche et choix à droite (9 à 12), centrés en hauteur */
.bloc--configurateur {
  display: grid;
  gap: var(--espace-24);
}

.configurateur__scene {
  aspect-ratio: 4 / 5;
  width: 100%;
  background: color-mix(in oklab, var(--teinte) 18%, var(--couleur-blanc)); /* teinte claire et nette (un mélange avec le crème grisait les bleus) */
  transition: background-color var(--duree-mouvement) var(--courbe-sortie);
}

@media (min-width: 48rem) {
  .configurateur__scene {
    aspect-ratio: 1;
  }
}

@media (min-width: 64rem) {
  .bloc--configurateur {
    grid-template-columns: repeat(var(--colonnes), minmax(0, 1fr));
    align-items: center;
    column-gap: var(--gouttiere);
  }

  .configurateur__scene {
    grid-column: 1 / 8;
  }

  .configurateur__panneau {
    grid-column: 9 / -1;
  }
}

.configurateur__numero {
  font-size: var(--taille-13);
  font-variant-numeric: tabular-nums;
}

.configurateur__nom {
  margin-top: var(--espace-4);
  color: var(--couleur-accent);
  font-size: var(--taille-titre-section);
  font-weight: var(--graisse-medium);
}

.configurateur__texte {
  margin-top: var(--espace-8);
}

.configurateur__communs {
  margin-top: var(--espace-24);
}

.configurateur__choix {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--espace-12);
  margin-top: var(--espace-24);
}

/* Sans JS, les boutons ne feraient rien : seule la 1re variante est présentée */
html:not(.avec-js) .configurateur__choix {
  display: none;
}

.configurateur__pastilles {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--espace-12);
}

/* Pastille : la couleur du goût ; la variante choisie est entourée d'un anneau */
.configurateur__pastille {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: var(--fond-pastille);
  box-shadow: inset 0 0 0 var(--epaisseur-filet) var(--couleur-filet);
  outline: 2px solid transparent;
  outline-offset: 3px;
  transition: outline-color var(--duree-survol), scale var(--duree-survol) var(--courbe-sortie);
}

.configurateur__pastille[aria-pressed="true"] {
  outline-color: var(--couleur-accent);
}

/* Focus clavier : anneau en pointillés, distinct de la sélection (la règle générale serait écrasée ici) */
.configurateur__pastille:focus-visible {
  outline: 2px dashed var(--couleur-accent);
  outline-offset: 3px;
}

@media (hover: hover) {
  .configurateur__pastille:hover {
    scale: 1.1;
  }
}

@media (prefers-reduced-motion: reduce) {
  .configurateur__scene,
  .configurateur__pastille {
    transition: none;
  }
}

.configurateur__panneau .bloc__legende {
  margin-top: var(--espace-16);
}

/* Annotations : l'image et ses repères numérotés, puis la liste des zones.
   Ordinateur : image à gauche (colonnes 1 à 5), liste à droite (7 à 12), centrées en hauteur */
.bloc--annotations {
  display: grid;
  gap: var(--espace-32);
}

@media (min-width: 64rem) {
  .bloc--annotations {
    grid-template-columns: repeat(var(--colonnes), minmax(0, 1fr));
    align-items: center;
    column-gap: var(--gouttiere);
  }

  .annotations__visuel {
    grid-column: 1 / 6;
  }

  .annotations__liste {
    grid-column: 7 / -1;
  }
}

.annotations__visuel {
  position: relative;
  margin: 0;
}

/* Repère : pastille numérotée centrée sur le point désigné ; ressort quand on survole sa zone dans la liste */
.repere {
  position: absolute;
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: var(--couleur-accent);
  box-shadow: 0 0 0 2px var(--couleur-blanc);
  color: var(--couleur-blanc);
  font-size: var(--taille-13);
  font-weight: var(--graisse-medium);
  translate: -50% -50%;
  pointer-events: none; /* le clic passe à l'image, qui s'ouvre en grand */
  transition: scale var(--duree-survol) var(--courbe-sortie), background-color var(--duree-survol);
}

.repere.est-actif {
  scale: 1.35;
  background: var(--couleur-texte);
}

.annotation {
  display: grid;
  grid-template-columns: 28px 1fr;
  column-gap: var(--espace-16);
  padding: var(--espace-16) 0;
  border-top: var(--epaisseur-filet) solid var(--couleur-filet);
}

.annotation:last-child {
  border-bottom: var(--epaisseur-filet) solid var(--couleur-filet);
}

.annotation > :not(.annotation__numero) {
  grid-column: 2;
}

.annotation__numero {
  grid-row: 1 / span 2;
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  border: 1px solid var(--couleur-accent);
  color: var(--couleur-accent);
  font-size: var(--taille-13);
  font-weight: var(--graisse-medium);
}

@media (prefers-reduced-motion: reduce) {
  .repere {
    transition: none;
  }
}

/* Magazine à feuilleter : livre ouvert de 2 pages de large (--rapport-page = largeur / hauteur d'une page).
   Les feuilles sont posées sur la moitié droite et pivotent autour de la reliure (bord gauche) ;
   fermé (.est-ferme), le livre glisse d'un quart pour centrer la couverture.
   Sans JS (.est-pret absent), les planches s'affichent les unes sous les autres à la place */
.magazine__scene {
  perspective: 2600px;
  padding-block: var(--espace-16);
}

.magazine__livre {
  position: relative;
  width: min(100%, 80svh * 2 * var(--rapport-page)); /* le livre ouvert tient dans la hauteur de l'écran */
  margin-inline: auto;
  aspect-ratio: calc(2 * var(--rapport-page));
  transform-style: preserve-3d;
  cursor: pointer;
  touch-action: pan-y;
  transition: translate 0.9s var(--courbe-balayage);
}

.magazine__livre.est-ferme {
  translate: -25% 0;
}

.feuille {
  position: absolute;
  top: 0;
  left: 50%;
  width: 50%;
  height: 100%;
  transform-origin: left center;
  transform-style: preserve-3d;
  transition: transform 0.9s var(--courbe-balayage);
}

.feuille.est-tournee {
  transform: rotateY(-180deg);
}

.feuille.est-en-mouvement {
  z-index: 100 !important; /* devant les autres pendant qu'elle tourne (sinon : z-index posé par main.js) */
}

.feuille__face {
  position: absolute;
  inset: 0;
  overflow: hidden;
  backface-visibility: hidden;
  background: var(--couleur-blanc);
  box-shadow: 0 10px 30px rgb(14 28 62 / 0.18);
}

.feuille__face--verso {
  transform: rotateY(180deg);
}

.feuille__face img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  user-select: none;
}

/* Ombre de la reliure : sur le bord gauche d'un recto, sur le bord droit d'un verso (une fois tourné) */
.feuille__face::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(to right, rgb(0 0 0 / 0.18), transparent 7%);
}

.feuille__face--verso::after {
  background: linear-gradient(to left, rgb(0 0 0 / 0.18), transparent 7%);
}

/* Variante simple : une page à la fois, centrée ; la page tournée pivote autour de son bord gauche
   puis s'efface, découvrant la suivante (comme la page d'un carnet) */
.magazine--simple .magazine__livre {
  width: min(100%, 80svh * var(--rapport-page));
  aspect-ratio: var(--rapport-page);
}

.magazine--simple .magazine__livre.est-ferme {
  translate: none;
}

.magazine--simple .feuille {
  left: 0;
  width: 100%;
}

.magazine--simple .feuille.est-tournee {
  opacity: 0;
  transition: transform 0.9s var(--courbe-balayage), opacity 0.25s linear 0.45s;
}

.magazine--simple .feuille__face::after {
  content: none;
}

.magazine__commandes {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--espace-16);
  margin-top: var(--espace-16);
}

.magazine__libelle {
  min-width: 16ch;
  font-size: var(--taille-14);
  text-align: center;
}

.magazine__agrandir {
  margin: var(--espace-12) auto 0;
}

/* Les planches ne servent qu'à la visionneuse quand le magazine fonctionne */
.est-pret .magazine__planches {
  display: none;
}

.bloc--magazine:not(.est-pret) :is(.magazine__scene, .magazine__commandes, .magazine__agrandir) {
  display: none;
}

.magazine__planches {
  display: grid;
  gap: var(--espace-12);
}

@media (prefers-reduced-motion: reduce) {
  .magazine__livre,
  .feuille {
    transition: none;
  }
}

/* Boucles vidéo : côte à côte, au format vertical 9:16, jamais plus hautes que l'écran */
.bloc--boucles {
  display: flex;
  justify-content: center;
  gap: var(--espace-12);
}

@media (min-width: 48rem) {
  .bloc--boucles {
    gap: var(--gouttiere);
  }
}

.boucle {
  flex: 1 1 0;
  max-width: calc(70svh * 9 / 16);
  margin: 0;
}

.boucle__video {
  display: block;
  width: 100%;
  aspect-ratio: 9 / 16;
  object-fit: cover;
  background: var(--couleur-marine);
}

/* Cube ASCII (main.js) : 40 caractères de large, taille calculée pour tenir dans sa colonne, centré.
   Arêtes de face dans la couleur du texte, arêtes du fond dans l'accent, plus pâles.
   Ordinateur : à gauche (colonnes 1 à 4), à côté de l'image étroite qui le suit (6 à 12), centré en hauteur */
.bloc--cube-ascii {
  container-type: inline-size;
}

.cube-ascii {
  width: fit-content;
  margin: 0 auto;
  font-family: ui-monospace, "Cascadia Mono", Consolas, "Courier New", monospace;
  font-size: min(16px, 100cqi / 40 / 0.62);
  line-height: 1.15;
  white-space: pre;
}

.cube-ascii__fond {
  color: var(--couleur-accent);
  opacity: 0.55;
}

@media (min-width: 64rem) {
  .chapitre > .bloc--cube-ascii {
    grid-column: 1 / 5;
    align-self: center;
  }
}

/* Site ou jeu intégré : aperçu + bouton "Lancer" sur un voile, remplacés par l'iframe au clic (main.js).
   Sans JS, seul le lien "plein écran" reste. Sur écran tactile (jeu au clavier), un message remplace le bouton */
.site-integre {
  position: relative;
  aspect-ratio: 16 / 9;
  max-height: 85svh;
  width: 100%;
  overflow: hidden;
  background: var(--couleur-marine);
}

.site-integre__apercu {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.site-integre__lancement {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--espace-12);
  padding: var(--espace-16);
  background: rgb(14 28 62 / 0.55); /* --couleur-marine voilée : le texte blanc reste lisible sur l'aperçu */
  color: var(--couleur-blanc);
  text-align: center;
}

.site-integre__bouton {
  padding-inline: var(--espace-32);
}

html:not(.avec-js) .site-integre__bouton {
  display: none;
}

@media (hover: hover) {
  .site-integre__bouton:hover {
    background: var(--couleur-blanc);
    color: var(--couleur-marine);
  }
}

.site-integre__mobile {
  display: none;
  max-width: 28ch;
  font-size: var(--taille-15);
}

.site-integre__cadre {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
}

@media (min-width: 64rem) {
  .site-integre__plein-ecran {
    width: fit-content;
    padding-inline: var(--espace-32);
  }
}

@media (hover: none) and (pointer: coarse) {
  .site-integre__mobile {
    display: block;
  }

  :is(.site-integre__bouton, .site-integre__legende, .site-integre__plein-ecran) {
    display: none;
  }
}

/* --- Visionneuse : une image de la galerie en plein écran, sur fond marine ---
   Compteur et croix en haut ; flèches de part et d'autre de l'image (en dessous en mobile) */
.visionneuse {
  width: 100%;
  height: 100%;
  max-width: 100%;
  max-height: 100%;
  margin: 0;
  padding: var(--espace-16) var(--marge) var(--espace-24);
  border: 0;
  background: var(--couleur-marine);
  color: var(--couleur-blanc);
}

.visionneuse::backdrop {
  background: transparent;
}

.visionneuse[open] {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  grid-template-rows: auto minmax(0, 1fr) auto;
  grid-template-areas:
    "compteur   .      fermer"
    "image      image  image"
    "precedente .      suivante";
  align-items: center;
  gap: var(--espace-16);
}

/* La page ne défile pas derrière la visionneuse */
html:has(.visionneuse[open]) {
  overflow: hidden;
}

.visionneuse__compteur {
  grid-area: compteur;
  font-size: var(--taille-14);
}

.visionneuse__fermer {
  grid-area: fermer;
  justify-self: end;
}

/* Toute la place disponible, sans jamais être rognée */
.visionneuse__image {
  grid-area: image;
  justify-self: center;
  width: auto;
  max-width: 100%;
  height: auto;
  max-height: 100%;
  object-fit: contain;
}

.visionneuse__fleche {
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--hauteur-bouton-petit);
  height: var(--hauteur-bouton-petit);
  border: 1px solid rgb(255 255 255 / 0.4);
  transition: background-color var(--duree-survol), color var(--duree-survol);
}

.visionneuse__fleche--precedente {
  grid-area: precedente;
}

.visionneuse__fleche--suivante {
  grid-area: suivante;
  justify-self: end;
}

@media (hover: hover) {
  .visionneuse__fleche:hover {
    background-color: var(--couleur-blanc);
    color: var(--couleur-marine);
  }
}

.visionneuse :focus-visible {
  outline-color: var(--couleur-blanc);
}

/* Tablette : les flèches passent de part et d'autre de l'image */
@media (min-width: 48rem) {
  .visionneuse[open] {
    grid-template-rows: auto minmax(0, 1fr);
    grid-template-areas:
      "compteur   .     fermer"
      "precedente image suivante";
    column-gap: var(--espace-24);
  }
}

@media (prefers-reduced-motion: no-preference) {
  .visionneuse[open] {
    animation: visionneuse-entree 0.25s cubic-bezier(0.22, 1, 0.36, 1);
  }
}

@keyframes visionneuse-entree {
  from {
    opacity: 0;
  }
}

/* --- Aperçu : photo, vidéo ou site intégré --- */
.section--apercu {
  --espace-bas-section: var(--espace-41);
}

.media {
  margin: var(--espace-18) 0 0;
}

.media__photo {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  background: var(--couleur-vide);
}

.media__video {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  background: var(--couleur-texte);
}

/* Vidéo tournée en vertical (9:16) : centrée, sans dépasser la hauteur de l'écran */
.media__video--vertical {
  width: auto;
  max-width: 100%;
  height: min(85svh, 48rem);
  margin-inline: auto;
  aspect-ratio: 9 / 16;
}

.media__site {
  display: block;
  width: 100%;
  height: var(--hauteur-site-integre);
  border: 1px solid var(--couleur-filet);
  background: var(--couleur-fond-site);
}

/* Adresse du site intégré, au-dessus du cadre (ordinateur) : un simple bandeau, pas un faux navigateur */
.media__adresse {
  display: none;
}

.media__legende {
  margin-top: var(--espace-11);
  font-size: var(--taille-13);
}

.media__plein-ecran {
  min-height: var(--hauteur-bouton-petit);
  margin-top: var(--espace-16);
}

/* Ordinateur : le site intégré prend 80 % de l'écran (--hauteur-site-integre), son adresse au-dessus,
   dans le même cadre ; le bouton "plein écran" reprend la taille de son texte */
@media (min-width: 64rem) {
  .media__adresse {
    display: block;
    padding: var(--espace-10) var(--espace-16);
    border: 1px solid var(--couleur-filet);
    border-bottom: 0;
    background: var(--couleur-fond-site);
    font-size: var(--taille-13);
  }

  .media__plein-ecran {
    width: fit-content;
    padding-inline: var(--espace-32);
  }
}

/* --- Navigation : précédent / suivant + tous les projets --- */
.navigation-projets {
  margin: 0 var(--marge);
  padding: var(--espace-21) 0 var(--espace-46);
  border-top: var(--epaisseur-filet) solid var(--couleur-filet);
}

.voisins {
  display: flex;
  gap: var(--espace-16);
}

.voisin {
  display: flex;
  flex-direction: column;
  gap: var(--espace-5);
}

/* Toujours à droite, même sans lien "Précédent" */
.voisin--suivant {
  align-items: flex-end;
  margin-left: auto;
  text-align: right;
}

.voisin__sens {
  font-size: var(--taille-13);
}

.voisin__nom {
  color: var(--couleur-accent);
  font-size: var(--taille-16);
  font-weight: var(--graisse-medium);
  text-decoration: underline 1px transparent;
  text-underline-offset: var(--decalage-soulignement);
  transition: text-decoration-color var(--duree-survol);
}

.voisin:is(:hover, :focus-visible) .voisin__nom {
  text-decoration-color: currentColor;
}

/* Vignette du projet suivant : ordinateur seulement */
.voisin__visuel {
  display: none;
}

.navigation-projets__tous {
  margin-top: var(--espace-30);
}

/* Ordinateur : "Précédent" (colonnes 1 à 3) et "Suivant" (7 à 12) se répondent, avec la même taille de texte,
   alignés en haut. "Suivant" garde la vignette du projet (colonnes 10 à 12), en bleu et crème (.bichromie),
   qui reprend ses couleurs au survol ou au focus ; son texte se cale contre elle, à droite */
@media (min-width: 64rem) {
  .navigation-projets {
    padding: var(--espace-48) 0 var(--espace-section);
  }

  .voisins {
    display: grid;
    grid-template-columns: repeat(var(--colonnes), minmax(0, 1fr));
    align-items: start;
    column-gap: var(--gouttiere);
  }

  .voisin {
    grid-column: 1 / 4;
  }

  /* Ses colonnes sont celles de la grille (subgrid) : texte sur 7 à 9, vignette sur 10 à 12 */
  .voisin--suivant {
    display: grid;
    grid-template-columns: subgrid;
    grid-template-rows: auto 1fr;
    grid-column: 7 / -1;
    align-items: start;
    justify-items: end;
    gap: var(--espace-5) normal; /* normal : la gouttière de la grille parente (subgrid) */
    margin-left: 0; /* la grille le place : plus besoin de le pousser à droite */
  }

  .voisin--suivant :is(.voisin__sens, .voisin__nom) {
    grid-column: 1 / 4;
  }

  .voisin__visuel {
    display: block;
    grid-column: 4 / -1;
    grid-row: 1 / -1;
    justify-self: stretch;
    aspect-ratio: 4 / 3;
    background: var(--couleur-vide);
  }

  .voisin__image {
    width: 100%;
    height: 100%;
    object-fit: cover;
  }

  .navigation-projets__tous {
    width: fit-content;
    margin-top: var(--espace-48);
    padding-inline: var(--espace-32);
  }
}

/* ==========================================================
   10. Mentions légales
   ========================================================== */
.mentions {
  margin-top: var(--espace-32);
}

.mention {
  padding: var(--espace-16) 0 var(--espace-44);
  border-top: var(--epaisseur-filet) solid var(--couleur-filet);
  font-size: var(--taille-14);
}

.mention__titre {
  margin-bottom: var(--espace-4);
  color: var(--couleur-accent);
  font-size: var(--taille-16);
  font-weight: var(--graisse-medium);
}

.mention p + p {
  margin-top: var(--espace-12);
}

/* Tablette : lignes de 65 caractères au plus, texte un peu plus grand */
@media (min-width: 48rem) {
  .mentions {
    margin-top: var(--espace-48);
    padding-bottom: var(--espace-section);
  }

  .mention {
    font-size: var(--taille-15);
  }

  .mention p {
    max-width: var(--largeur-texte);
  }
}

/* Ordinateur : comme les sections, titre à gauche (colonnes 1 à 3), texte dans la colonne de contenu (5 à 12).
   Le titre couvre les rangées des paragraphes */
@media (min-width: 64rem) {
  .mention {
    display: grid;
    grid-template-columns: repeat(var(--colonnes), minmax(0, 1fr));
    align-items: start;
    column-gap: var(--gouttiere);
    padding: var(--espace-32) 0 var(--espace-48);
  }

  .mention > * {
    grid-column: 5 / -1;
  }

  .mention__titre {
    grid-row: 1 / span 10;
    grid-column: 1 / 4;
    margin-bottom: 0;
    font-size: var(--taille-18);
  }
}

/* Contact : le logo remplace le libellé */
.lien-contact__icone {
  flex: none;
  width: var(--taille-icone-contact);
  height: var(--taille-icone-contact);
  background: var(--couleur-texte);
  mask: center / contain no-repeat;
  -webkit-mask: center / contain no-repeat;
}

.lien-contact__icone--mail { mask-image: url("../img/contact/mail.svg"); -webkit-mask-image: url("../img/contact/mail.svg"); }
.lien-contact__icone--phone { mask-image: url("../img/contact/phone.svg"); -webkit-mask-image: url("../img/contact/phone.svg"); }
.lien-contact__icone--instagram { mask-image: url("../img/contact/instagram.svg"); -webkit-mask-image: url("../img/contact/instagram.svg"); }
.lien-contact__icone--linkedin { mask-image: url("../img/contact/linkedin.svg"); -webkit-mask-image: url("../img/contact/linkedin.svg"); }
