/* ═══ Système d'étude de cas ══════════════════════════════════════════════════
   Extrait du <style> de dotlab.html, partagé par les trois pages projet.
   Deux familles de règles cohabitent :
   — la mise en page (cs-layout, cs-toc, cs-colonne, cs-cover, cs-fiche, cs-cta,
     l'échelle typographique, le rythme vertical) : commune aux trois pages ;
   — les composants propres à DotLab (cs-slot, cs-etape, cs-grille, cs-jetes,
     cs-sticker, cs-plan…) : sans effet sur les pages qui ne les emploient pas.
   Les couleurs sont préfixées body.projet, et l'échelle typographique est en fin
   de fichier : à spécificité égale, la dernière règle l'emporte.
   ═════════════════════════════════════════════════════════════════════════ */

/* ── Une seule cadence pour toute la page ────────────────────────────────────
   Chaque effet avait sa durée et sa courbe : des « ease », deux cubic-bézier
   différentes, un « ease-in » au départ d'un glissé, et jusqu'à un léger
   rebond sur le repère du sommaire. Mis bout à bout, ils se répondaient mal.
   Tout passe désormais par trois durées et une seule courbe — un ease-out
   franc : le geste part vite et se pose, jamais l'inverse. Les jetons
   (--courbe, --vite, --pose, --ample) sont déclarés dans styles-v3.css, la
   feuille commune : l'accueil, qui ne charge pas celle-ci, s'en sert aussi.
   Seule exception, le rond d'attente : une rotation continue se fait en
   « linear », sinon elle bat la mesure. */

/* ── Typographie de la page : Denim en titres. ───────────────────────
   Le corps garde la police du site. Les Suisse fournies étaient des
   versions d'essai dont la table des caractères redirige chaque lettre
   accentuée vers la lettre nue (é→e, ç→c) : impossible sur une page en
   français. Avec un fichier Suisse Int'l complet, tout est prêt ci-dessous.

   @font-face { font-family: "Suisse Intl"; src: url("assets/fonts/dotlab/SuisseIntl-Regular.woff2") format("woff2"); font-weight: 400; font-display: swap; }
   @font-face { font-family: "Suisse Intl"; src: url("assets/fonts/dotlab/SuisseIntl-Medium.woff2")  format("woff2"); font-weight: 500; font-display: swap; }
   @font-face { font-family: "Suisse Intl"; src: url("assets/fonts/dotlab/SuisseIntl-Semibold.woff2") format("woff2"); font-weight: 600; font-display: swap; }
   .project, .project .h-btn, .project .nav-button { font-family: "Suisse Intl", "Geist", system-ui, sans-serif; }
   ─────────────────────────────────────────────────────────────────── */
/* Denim est déclarée dans styles-v3.css, partagée avec l'accueil. */

/* Denim est fournie en quatre graisses : jamais de gras de synthèse,
   le vrai dessin existe. */
.project .project__title,
.project .cs-h2,
.project .cs-h3,
.project .project__body-item-title {
  font-family: "Denim", ui-sans-serif, system-ui, sans-serif;
  /* 500 et non 400 : Denim fournit un vrai Medium, et font-synthesis: none
     garantit que c'est ce dessin-là qui sert, pas un gras simulé. */
  font-weight: 500;
  font-synthesis: none;
  letter-spacing: -0.01em;
}
.project .cs-h3 { font-size: clamp(1.1875rem, 1.8vw, 1.5rem); }
/* ── Étude de cas : titres, emplacements média, notes ─────────────── */

/* Le titre d'une section : une affirmation, sous son label. */
.cs-h2 {
  margin: 0;
  font-size: clamp(1.5rem, 3.2vw, 2.375rem);
  line-height: 1.15;
  letter-spacing: -0.02em;
  font-weight: var(--font-weight-regular);
}
.cs-h3 {
  margin: 0;
  font-size: clamp(1.0625rem, 1.6vw, 1.375rem);
  line-height: 1.25;
  letter-spacing: -0.01em;
  font-weight: 600;
}

/* Un geste de l'outil : son texte, puis son média. */

/* Emplacement média en attente du fichier. */
.cs-slot {
  width: 100%;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.75rem;
  text-align: center;
  min-height: 13rem;
  padding: var(--spacing-21) 1.5rem;
  border: 1px dashed rgb(var(--encre) / 0.16);
  border-radius: var(--r-coin);
  background: rgb(var(--encre) / 0.02);
}
.cs-slot--tall { min-height: 22rem; }
.cs-slot__kind {
  font-size: 0.625rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  opacity: 0.5;
  border: 1px solid rgb(var(--encre) / 0.18);
  border-radius: var(--r-pilule);
  padding: 0.25rem 0.625rem;
}
.cs-slot__note {
  margin: 0;
  max-width: 30rem;
  font-size: 0.875rem;
  line-height: 1.6;
  opacity: 0.45;
}

/* Un média posé dans la colonne : même cadre que les emplacements. */
.cs-media {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--r-coin);
  border: 1px solid rgb(var(--encre) / 0.1);
  background: var(--plaque);
}

/* Une grille de tirages : trois par ligne, carrés, sans légende —
   c'est la quantité et la variété qui parlent. */
.cs-grille {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0.625rem;
  width: 100%;
}
.cs-grille img {
  display: block;
  width: 100%;
  aspect-ratio: 1 / 1;
  height: auto;
  object-fit: cover;
  border-radius: var(--r-coin);
}
@container (max-width: 30rem) {
  .cs-grille { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* ── Retours en stickers : des post-it FigJam, dans un carrousel ──
   Défilement natif avec accroche, boutons par-dessus : rien à charger. */
.cs-carrousel { width: 100%; min-width: 0; }
.cs-carrousel__barre {
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 0.875rem;
}
.cs-carrousel__boutons { display: flex; gap: 0.375rem; }
.cs-carrousel__btn {
  appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.25rem; height: 2.25rem;
  border-radius: var(--r-coin);
  border: 1px solid rgb(var(--encre) / 0.18);
  background: transparent;
  color: inherit;
  font: inherit;
  cursor: pointer;
  transition: background var(--vite) var(--courbe), opacity var(--vite) var(--courbe);
}
@media (hover: hover) and (pointer: fine) {
  .cs-carrousel__btn:hover { background: rgb(var(--encre) / 0.08); }
}
.cs-carrousel__btn:focus-visible { outline: 2px solid currentColor; outline-offset: 2px; }

.cs-carrousel__piste {
  list-style: none;
  margin: 0;
  padding: 0.75rem 0.25rem 1.25rem; /* la rotation et l'ombre débordent un peu */
  /* L'accroche compte cette marge : la première carte tombe à scrollLeft 0. */
  scroll-padding-inline: 0.25rem;
  display: flex;
  gap: 1rem;
  overflow-x: auto;
  /* « proximity » plutôt que « mandatory » : l'accroche ne reprend la main que
     si on s'arrête près d'une carte. En mandatory, un défilement libre était
     rattrapé de force à chaque geste, et le recadrage de la boucle infinie
     pouvait déclencher une seconde accroche par-dessus la première. */
  scroll-snap-type: x proximity;
  /* Surtout pas de scroll-behavior: smooth ici — le recadrage de la boucle
     infinie repose sur des sauts de scrollLeft instantanés, qui deviendraient
     alors des glissades bien visibles. Le défilement animé des boutons est
     demandé au cas par cas par le script. */
  overscroll-behavior-x: contain; /* le bout de piste ne tire pas la page */
  touch-action: pan-x pan-y;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
@media (prefers-reduced-motion: reduce) {
  .cs-carrousel__piste { scroll-behavior: auto; scroll-snap-type: none; }
}
.cs-carrousel__piste::-webkit-scrollbar { display: none; }

.cs-sticker {
  flex: 0 0 14rem;
  scroll-snap-align: start;
  transform-origin: center center;
  aspect-ratio: 1 / 1;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  padding: 1.125rem 1.125rem 1rem;
  border-radius: var(--r-coin);
  color: #1b1b1b;
  transition: transform var(--vite) var(--courbe);
}
/* Chaque sticker penche un peu, jamais deux voisins du même côté. */
.cs-sticker:nth-child(6n/**/+1) { transform: rotate(-1.6deg); }
.cs-sticker:nth-child(6n/**/+2) { transform: rotate(1.2deg); }
.cs-sticker:nth-child(6n/**/+3) { transform: rotate(-0.8deg); }
.cs-sticker:nth-child(6n/**/+4) { transform: rotate(1.8deg); }
.cs-sticker:nth-child(6n/**/+5) { transform: rotate(-1.2deg); }
.cs-sticker:nth-child(6n/**/+6) { transform: rotate(0.9deg); }
@media (hover: hover) and (pointer: fine) {
  .cs-sticker:hover { transform: rotate(0) scale(1.02); }
}
.cs-sticker__texte {
  margin: 0;
  font-size: 1rem;
  line-height: 1.4;
  font-weight: 500;
  letter-spacing: -0.005em;
}
.cs-sticker__auteur {
  font-size: 0.6875rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  opacity: 0.55;
}
/* Les teintes des post-it FigJam. */
.cs-sticker--jaune  { background: #f7d774; }
.cs-sticker--rose   { background: #f5a9c4; }
.cs-sticker--vert   { background: #a9e3a4; }
.cs-sticker--bleu   { background: #9fd2f4; }
.cs-sticker--violet { background: #c9b8f3; }
.cs-sticker--orange { background: #f8b97e; }

@container (max-width: 34rem) {
  .cs-sticker { flex-basis: 12rem; }
}
@media (prefers-reduced-motion: reduce) {
  .cs-sticker { transition: none; }
}

/* ── Le CTA de la fiche : la variante pleine de l'accueil, un cran plus
   grande. Seul élément coloré de la fiche, il n'a pas à crier. ── */
.cs-cta {
  padding: 0.8125rem 1.375rem;
  font-size: 0.9375rem;
  gap: 0.5rem;
}
.cs-cta__fleche {
  display: inline-block;
  transition: transform var(--vite) var(--courbe);
}
.cs-cta:focus-visible .cs-cta__fleche { transform: translateX(3px); }
@media (hover: hover) and (pointer: fine) {
  .cs-cta:hover .cs-cta__fleche { transform: translateX(3px); }
}
.cs-cta:focus-visible { outline: 2px solid rgb(var(--encre)); outline-offset: 3px; }
@media (prefers-reduced-motion: reduce) {
  .cs-cta__fleche { transition: none; }
}

/* ── Ce que j'ai jeté : trois familles de rebuts, une légende chacune ── */
.cs-jetes { display: flex; flex-direction: column; gap: 2rem; width: 100%; }
.cs-jetes__groupe { margin: 0; display: flex; flex-direction: column; gap: 0.75rem; }
.cs-jetes__legende {
  font-size: 0.9375rem;
  line-height: 1.6;
  opacity: 0.8;
}
.cs-jetes__tuiles {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0.625rem;
}
.cs-jetes__tuiles img {
  display: block;
  width: 100%;
  aspect-ratio: 1 / 1;
  height: auto;
  object-fit: cover;
  border-radius: var(--r-coin);
  border: 1px solid rgb(var(--encre) / 0.08);
}

/* ── Le plan de travail : cinq pièces posées en vrac, qui se chevauchent.
   Le plan déborde de la colonne — 3 rem à gauche, la gouttière du sommaire,
   et à droite jusqu'à 8 rem selon la place réellement libre, calculée
   depuis la largeur de l'écran : jamais de défilement horizontal. ── */
.cs-plan-wrap {
  --libre: max(0px, (100vw - 56rem - 2 * var(--project-pad-x)) / 2);
  width: calc(100% + 4.5rem + min(8rem, var(--libre)));
  margin-left: -4.5rem;
}
.cs-plan {
  list-style: none;
  margin: 0;
  padding: 0;
  position: relative;
  width: 100%;
  aspect-ratio: 10 / 7;
}
.cs-piece {
  position: absolute;
  left: var(--x);
  top: var(--y);
  height: var(--h);
  /* La profondeur vient d'une variable posée par le script au fil des
     glissés : une classe (survol, saisie) peut ainsi toujours passer devant,
     ce qu'un z-index écrit en inline lui aurait interdit. */
  z-index: var(--z, auto);
  transform: rotate(var(--r));
  transition: transform var(--pose) var(--courbe);
  user-select: none;
  -webkit-user-select: none;
}
.cs-piece__prise {
  appearance: none;
  border: 0; padding: 0; margin: 0;
  background: none; font: inherit; color: inherit;
  display: block; height: 100%; position: relative;
  cursor: grab;
  touch-action: none; /* le doigt déplace la pièce, il ne fait pas défiler la page */
}
.cs-piece img {
  display: block;
  height: 100%;
  width: auto;
  border-radius: var(--r-coin);
  border: 1px solid rgb(var(--encre) / 0.12);
  /* Réglée pour la page claire : une ombre à 50 % de noir, héritée du fond
     sombre, faisait une tache sur du blanc. Mêmes valeurs que le tas de
     tirages de l'accueil, les deux objets se prenant de la même façon. */
  box-shadow: 0 0.75rem 1.5rem rgba(0, 0, 0, 0.16);
  transition: box-shadow var(--pose) var(--courbe);
  pointer-events: none;
}
/* Survolée, focalisée ou saisie : la pièce se redresse, se soulève, passe devant. */
.cs-piece:focus-within,
.cs-piece.est-saisie {
  transform: rotate(0deg) scale(1.04);
  z-index: 100;
}
@media (hover: hover) and (pointer: fine) {
  .cs-piece:hover {
    transform: rotate(0deg) scale(1.04);
    z-index: 100;
  }
}
.cs-piece.est-saisie { transition: none; }
.cs-piece.est-saisie .cs-piece__prise { cursor: grabbing; }
.cs-piece:focus-within img,
.cs-piece.est-saisie img { box-shadow: 0 1.5rem 2.5rem rgba(0, 0, 0, 0.24); }
@media (hover: hover) and (pointer: fine) {
  .cs-piece:hover img { box-shadow: 0 1rem 2rem rgba(0, 0, 0, 0.2); }
}
.cs-piece__prise:focus-visible { outline: 2px solid rgb(var(--encre)); outline-offset: 4px; border-radius: var(--r-coin); }

.cs-plan__reset {
  appearance: none;
  border: 0; padding: 0; margin: 0.875rem 0 0 4.5rem;
  background: none; font: inherit; color: inherit;
  font-size: 0.75rem;
  opacity: 0.45;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 0.2em;
  transition: opacity var(--vite) var(--courbe);
}
.cs-plan__reset:focus-visible { opacity: 0.9; outline: none; }
@media (hover: hover) and (pointer: fine) {
  .cs-plan__reset:hover { opacity: 0.9; outline: none; }
}

/* Sous 58rem le sommaire n'est plus là : plus de gouttière où déborder. */
@media (max-width: 58rem) {
  .cs-plan-wrap { width: 100%; margin-left: 0; }
  .cs-plan__reset { margin-left: 0; }
}
/* En colonne étroite, le chevauchement devient illisible : les pièces
   reprennent le fil, à plat et l'une sous l'autre, sans glisser. */
@container (max-width: 34rem) {
  .cs-plan { aspect-ratio: auto; display: flex; flex-direction: column; gap: 1.5rem; }
  .cs-piece { position: static; height: auto; transform: none; }
  .cs-piece__prise { touch-action: auto; cursor: default; }
  .cs-piece img { height: auto; width: 100%; }
  .cs-plan__reset { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .cs-piece, .cs-piece img { transition: none; }
}

/* ── Un visuel qu'on peut ouvrir en grand ──
   Le bouton est posé par case-study.js autour de chaque tirage : il prend la
   place de l'image dans la grille, au même rayon. Au survol l'image avance
   d'un cran sous une loupe, et le débordement est coupé pour que la grille ne
   bouge pas d'un pixel. Les pièces du plan ont déjà leur bouton : elles
   gardent leur survol à elles, et le clic ouvre la même visionneuse. */
.cs-zoom {
  appearance: none;
  display: block;
  position: relative;
  width: 100%;
  padding: 0;
  margin: 0;
  border: 0;
  background: none;
  font: inherit;
  color: inherit;
  border-radius: var(--r-coin);
  overflow: hidden;
  isolation: isolate; /* Safari : l'enfant agrandi reste dans le rayon */
  cursor: zoom-in;
  -webkit-tap-highlight-color: transparent;
}
/* ── Rien par-dessus le visuel ──
   Un voile noir à 38 % montait au survol, et une pastille blanche annonçait
   « Plein écran » par-dessus. Deux couches pour dire une chose, au prix de
   l'image : on assombrissait ce qu'on proposait justement de mieux regarder,
   et le mot se lisait comme une légende collée sur le tirage.

   Il ne reste que le tirage, qui avance de trois pour cent. C'est le curseur
   qui dit l'action, en devenant une loupe au-dessus du visuel — voir « Le
   curseur » dans styles-v3.css. Le geste appartient à la main, pas à
   l'image. */
.cs-zoom img,
.cs-zoom video { transition: transform var(--pose) var(--courbe); }
.cs-zoom:focus-visible { outline: 2px solid rgb(var(--encre)); outline-offset: 4px; }
/* Un média encadré : le bouton reprend bordure, rayon et marge, et le média
   les lui laisse — sinon on verrait deux cadres l'un dans l'autre. */
.cs-zoom--media,
.cs-zoom--etape {
  border: 1px solid rgb(var(--encre) / 0.1);
  background: var(--plaque);
}
.cs-zoom--media { border-radius: var(--r-coin); }
.cs-zoom--etape { border-radius: var(--r-coin); margin-bottom: 0.625rem; }
.cs-zoom--media > .cs-media,
.cs-zoom--etape > .cs-etape__media {
  border: 0;
  border-radius: 0;
  margin: 0;
}
/* Le numéro de l'étape se pose sur le visuel : il ne doit pas en avaler le
   coin, qui ouvre la vidéo comme le reste. */
.cs-etape__num { pointer-events: none; }
@media (hover: hover) and (pointer: fine) {
  .cs-zoom:hover img,
  .cs-zoom:hover video { transform: scale(1.03); }
}
/* Au doigt, le tirage s'enfonce légèrement au contact : il n'y a pas de
   survol pour annoncer qu'il s'ouvre, et pas de curseur pour le dire. */
.cs-zoom:active img,
.cs-zoom:active video { transform: scale(0.99); }
@media (prefers-reduced-motion: reduce) {
  .cs-zoom img, .cs-zoom video { transition: none; }
  .cs-zoom:hover img, .cs-zoom:hover video { transform: none; }
}

/* La visionneuse s'efface entre deux ouvertures : .lb-overlay impose un
   display, qui l'emporterait sur [hidden] et laisserait ses boutons dans
   l'ordre de tabulation de la page. */
.lb-overlay[hidden] { display: none; }
.lb-nav[hidden] { display: none; }
/* Une vidéo en grand se cadre comme un tirage ; l'un ou l'autre est montré,
   jamais les deux — et .lb-img-wrap img impose un display qui l'emporterait
   sur [hidden]. */
.lb-overlay--cs .lb-img-wrap video {
  display: block;
  max-width: 100%;
  max-height: 92vh;
  width: auto;
  height: auto;
  border-radius: var(--r-coin);
  background: #08090a;
}
.lb-overlay--cs .lb-img-wrap img[hidden],
.lb-overlay--cs .lb-img-wrap video[hidden] { display: none; }
/* ── La barre de lecture ──────────────────────────────────────────────────────
   Posée sur la vidéo, pas sous elle : elle emprunte le vocabulaire des badges
   de la page — fond sombre translucide, verre dépoli. Sans filet autour :
   le fond suffit à la dessiner. Elle s'efface pendant la lecture et revient au moindre mouvement. */
.lb-player {
  position: absolute;
  left: 0.75rem;
  right: 0.75rem;
  bottom: 0.75rem;
  z-index: 2;
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.5rem 0.875rem;
  /* Le rayon des cartes et des boutons du site : en pilule, la barre et ses
     boutons ronds ne ressemblaient à rien d'autre sur la page. La piste et
     son curseur, eux, restent ronds. */
  border-radius: var(--r-coin);
  background: rgba(8, 9, 10, 0.66);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  transition: opacity var(--pose) var(--courbe), transform var(--pose) var(--courbe);
}
.lb-player[hidden] { display: none; }
/* Au repos, elle s'efface — mais seulement en lecture (voir reveiller()). */
.lb-player.est-au-repos {
  opacity: 0;
  transform: translateY(0.5rem);
  pointer-events: none;
}
.lb-player__jouer {
  flex: none;
  width: 2rem;
  height: 2rem;
  padding: 0;
  border: 0;
  border-radius: var(--r-coin);
  background: rgba(255, 255, 255, 0.14);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: background var(--vite) var(--courbe);
}
@media (hover: hover) and (pointer: fine) {
  .lb-player__jouer:hover { background: rgba(255, 255, 255, 0.26); }
}
.lb-player__temps {
  flex: none;
  font-size: 0.75rem;
  font-variant-numeric: tabular-nums; /* le temps ne doit pas danser */
  color: rgba(255, 255, 255, 0.72);
  letter-spacing: 0.02em;
}
/* La piste : un trait, rempli par une variable que le script met à jour. */
.lb-player__piste {
  appearance: none;
  -webkit-appearance: none;
  flex: 1 1 auto;
  min-width: 0;
  height: 1rem;              /* zone de saisie confortable */
  margin: 0;
  background: none;
  cursor: pointer;
}
.lb-player__piste::-webkit-slider-runnable-track {
  height: 0.25rem;
  border-radius: var(--r-pilule);
  background: linear-gradient(to right,
    #fff 0 var(--rempli, 0%),
    rgba(255, 255, 255, 0.24) var(--rempli, 0%) 100%);
}
.lb-player__piste::-moz-range-track {
  height: 0.25rem;
  border-radius: var(--r-pilule);
  background: linear-gradient(to right,
    #fff 0 var(--rempli, 0%),
    rgba(255, 255, 255, 0.24) var(--rempli, 0%) 100%);
}
.lb-player__piste::-webkit-slider-thumb {
  appearance: none;
  -webkit-appearance: none;
  width: 0.75rem;
  height: 0.75rem;
  margin-top: -0.25rem;      /* centré sur un rail de 0,25rem */
  border-radius: 50%;
  background: #fff;
  border: 0;
  transition: transform var(--vite) var(--courbe);
}
.lb-player__piste::-moz-range-thumb {
  width: 0.75rem;
  height: 0.75rem;
  border-radius: 50%;
  background: #fff;
  border: 0;
}
@media (hover: hover) and (pointer: fine) {
  .lb-player__piste:hover::-webkit-slider-thumb { transform: scale(1.2); }
}
.lb-player__piste:focus-visible,
.lb-player__jouer:focus-visible { outline: 2px solid #fff; outline-offset: 3px; }
/* Sur un téléphone, la barre reprend toute la largeur sous le visuel. */
@media (max-width: 34rem) {
  .lb-player { left: 0.5rem; right: 0.5rem; bottom: 0.5rem; gap: 0.5rem; padding: 0.4375rem 0.625rem; }
  .lb-player__temps { font-size: 0.6875rem; }
}
@media (prefers-reduced-motion: reduce) {
  .lb-player,
  .lb-player__jouer,
  .lb-player__piste::-webkit-slider-thumb { transition: none; }
  .lb-player.est-au-repos { opacity: 1; transform: none; pointer-events: auto; }
}

/* ── L'ouverture : le visuel grandit, il ne se fond pas ───────────────────────
   La visionneuse d'accueil fait varier l'opacité de tout son bloc — voile,
   boutons et visuel ensemble. Ici le visuel doit grandir depuis sa vignette
   sans jamais changer d'opacité : on sépare donc les trois couches. Le voile
   passe dans un ::before qui, lui, a le droit de fondre ; les commandes fondent
   de leur côté ; le visuel ne fait que se déplacer et changer de taille. */
.lb-overlay--cs {
  opacity: 1;
  background: none;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  transition: none;
}
/* ── Le voile ──
   Un aplat sombre derrière la fenêtre : la page s'efface, le visuel reste
   seul. Sans lui, le sommaire et le texte transparaissaient de part et
   d'autre de la fenêtre, et l'on ne savait plus ce qu'on regardait.

   Il s'était allégé à 62 % du temps où une fenêtre blanche détachait le
   visuel à sa place. Cette fenêtre n'existe plus et le visuel est posé à nu :
   le voile reprend la charge, et remonte à 82 %.

   C'est la seule opacité animée qui reste dans le geste. Un fond n'est pas un
   objet : il n'a ni place ni taille d'où partir, donc pas de géométrie à lui
   donner. Il prend la durée et la courbe du reste, pour qu'on ne lise qu'un
   seul mouvement. */
.lb-overlay--cs::before {
  content: "";
  position: absolute;
  inset: 0;
  /* Assez pour isoler le visuel, pas au point d'éteindre la page : à 82 %,
     elle disparaissait presque. */
  background: rgb(0 0 0 / 0.6);
  backdrop-filter: blur(0.5rem);
  -webkit-backdrop-filter: blur(0.5rem);
  opacity: 0;
  transition: opacity var(--ample) var(--courbe-trajet);
  pointer-events: none;
}
.lb-overlay--cs.is-open::before { opacity: 1; }
/* La fermeture, le compteur et la barre de lecture vivent dans la fenêtre :
   la découpe qui s'ouvre les révèle avec elle, ils n'ont rien à faire de plus.
   Ils montaient en fondu, ce qui faisait une horloge de plus pour un seul
   geste — et une opacité de plus.

   Les flèches, elles, sont hors de la fenêtre, contre les bords de l'écran :
   la découpe ne les atteint pas. Elles arrivent donc en glissant depuis leur
   bord, sur la durée du reste. Un déplacement, pas un fondu. */
.lb-overlay--cs .lb-nav {
  transition: translate var(--ample) var(--courbe-trajet),
              background var(--vite) var(--courbe),
              color var(--vite) var(--courbe);
}
/* Plus loin du bord, et plus loin de la fenêtre : on va chercher le chevron,
   on ne le heurte pas en visant l'image. */
.lb-overlay--cs .lb-prev { left: 2rem; translate: -1.5rem 0; }
.lb-overlay--cs .lb-next { right: 2rem; translate: 1.5rem 0; }
.lb-overlay--cs.is-open .lb-prev,
.lb-overlay--cs.is-open .lb-next { translate: 0 0; }
/* Au repos pendant la lecture, la barre s'efface : c'est un état de veille,
   pas l'ouverture — la seule opacité qui reste, et elle est voulue. */
.lb-overlay--cs.is-open .lb-player.est-au-repos { opacity: 0; }
/* Le cadre ne fait plus de bond : c'est le média qui porte le mouvement. */
.lb-overlay--cs .lb-img-wrap { transform: none; transition: none; }
@media (prefers-reduced-motion: reduce) {
  .lb-overlay--cs::before,
  .lb-overlay--cs .lb-nav,
  .lb-overlay--cs .lb-player { transition: none; }
}

/* Le glissé horizontal appartient à la visionneuse ; le vertical reste au
   navigateur. Sans ça, le doigt fait défiler la page derrière l'écran noir. */
.lb-overlay--cs.lb-overlay--serie .lb-img-wrap { touch-action: pan-y; }
.lb-overlay--cs .lb-img-wrap img { -webkit-user-drag: none; user-select: none; }
/* L'image porte son propre calque : le glissé reste sur le compositeur et
   n'oblige pas à repeindre le flou d'arrière-plan du voile à chaque image. */
.lb-overlay--cs .lb-img-wrap img,
.lb-overlay--cs .lb-img-wrap video { transform: translateZ(0); }
/* Sur un téléphone, les flèches se posent sur les bords du visuel : elles
   descendent au pied de l'écran, de part et d'autre du compteur, et le
   visuel reste entier. Le doigt le fait aussi glisser. */
@media (max-width: 34rem) {
  .lb-overlay--cs .lb-nav {
    top: auto;
    bottom: 0.9rem;
    transform: none;
  }
  .lb-overlay--cs .lb-prev { left: calc(50% - 5.5rem); }
  .lb-overlay--cs .lb-next { right: calc(50% - 5.5rem); }
  /* La hauteur et la réserve de la ligne de repères sont plus bas, avec le
     reste de ses règles ; elles étaient redites ici, avec le rembourrage de
     la fenêtre qui n'existe plus. */
  .lb-overlay--cs .lb-img-wrap,
  .lb-overlay--cs .lb-img-wrap img,
  .lb-overlay--cs .lb-img-wrap video { max-height: 76vh; }
}
@media (prefers-reduced-motion: reduce) {
  .lb-overlay--cs,
  .lb-overlay--cs .lb-img-wrap { transition: none; }
  .lb-overlay--cs .lb-img-wrap { transform: none; }
}

/* Un bloc de texte qui reste à écrire. */

/* Ces trois blocs avaient ici leur repli pour une page claire, à l'époque où
   la feuille était écrite pour du sombre. Les règles de base s'écrivant
   maintenant en rgb(var(--encre) / …), elles donnent d'elles-mêmes le bon
   résultat des deux côtés, et le repli n'a plus d'objet. */

@media (max-width: 40rem) {
  .cs-slot { min-height: 10rem; }
  .cs-slot--tall { min-height: 15rem; }
}

/* ── L'architecture de la page : sommaire collant, colonne de lecture ──
   La lecture n'est plus centrée dans le viewport mais calée contre son
   sommaire, comme une table des matières de rapport. ────────────────── */
.cs-layout {
  display: grid;
  grid-template-columns: 10rem minmax(0, 44rem);
  gap: 4.5rem;
  align-items: start;
  /* Le bloc entier se centre dans l'écran ; le texte, lui, reste ferré
     à gauche dans sa colonne. */
  justify-content: center;
  /* Le projet démarre plus bas : le titre a de l'air au-dessus de lui. */
  padding: 7rem var(--project-pad-x) var(--spacing-21);
}
.cs-colonne { min-width: 0; display: flex; flex-direction: column; }

/* Le titre vit dans la colonne : plus petit, calé sur le sommaire. */
.cs-colonne .project__header { padding: 0 0 1.75rem; gap: 0.875rem; }
.cs-colonne .project__title {
  font-size: clamp(1.75rem, 3.2vw, 2.5rem);
  line-height: 1.14;
  letter-spacing: -0.02em;
}

.cs-toc {
  position: sticky;
  /* Sous la barre : sa marge, ses 2,75 rem, et un quart de rem d'air. */
  top: calc(var(--barre-haut) + 3rem);
  /* Le retour s'aligne sur l'eyebrow du titre, en face. */
  padding-top: 0.125rem;
  display: flex;
  flex-direction: column;
  gap: var(--spacing-14);
  font-size: 0.875rem;
  line-height: 1.5;
}
.cs-toc__retour { text-decoration: none; color: inherit; opacity: 0.45; transition: opacity var(--vite) var(--courbe); }
@media (hover: hover) and (pointer: fine) {
  .cs-toc__retour:hover { opacity: 1; }
}
.cs-toc__liste { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.6rem; }
.cs-toc__liste a {
  text-decoration: none;
  color: inherit;
  opacity: 0.35;
  transition: opacity var(--vite) var(--courbe);
  display: block;
}
@media (hover: hover) and (pointer: fine) {
  .cs-toc__liste a:hover { opacity: 0.75; }
}
/* La section en cours de lecture, marquée à l'œil sans puce ni trait. */
.cs-toc__liste a.est-lue { opacity: 1; }

/* ── Le rythme vertical : deux étages, pas un seul ─────────────────
   Un bloc avec un id ouvre une section : grand blanc avant lui.
   Tout ce qui n'en a pas — média, sous-titre, suite de texte —
   continue la section en cours : petit blanc. Le DOM est plat, mais
   c'est lui qui dit à quoi chaque bloc appartient. ─────────────── */
.cs-layout {
  --cs-inter: 6rem;
  --cs-intra: 2.5rem;
}
/* La colonne est le conteneur que lisent toutes les @container de la
   page : sans cette ligne, elles ne se déclenchent jamais. */
.cs-layout .project__gallery { padding: 0; gap: 0; container-type: inline-size; }
.cs-layout .project__gallery > * + * { margin-top: var(--cs-intra); }
.cs-layout .project__gallery > * + [id] { margin-top: var(--cs-inter); }

/* Un en-tête sans rien derrière lui ne réserve plus de place pour rien :
   sinon ses 21 px s'ajoutaient à chaque blanc et faussaient la mesure. */
.cs-layout .project__body-header:last-child { padding-bottom: 0; }
/* Un titre de 40 px demande un peu plus d'air avant son texte. */
.cs-layout .cs-h2 { margin-bottom: 0.25rem; }

/* Une container query ne peut styler que les descendants du conteneur.
   .cs-layout est son ancêtre : la surcharge doit se poser sur les
   enfants de la galerie, ceux-là mêmes qui lisent var(). */
@container (max-width: 34rem) {
  .cs-layout .project__gallery > * { --cs-inter: 4rem; --cs-intra: 1.75rem; }
}
.cs-layout .project__body { align-items: flex-start; padding: 0; }
.cs-layout .project__body-header,
.cs-layout .project__body-items,
.cs-layout .project__body-item-text { max-width: none; }
.cs-layout .cs-slot{ text-align: left; align-items: flex-start; }
.cs-layout .cs-slot__note { text-align: left; }

@media (max-width: 58rem) {
  .cs-layout { grid-template-columns: minmax(0, 44rem); padding-top: 2.5rem; gap: 2rem; }
  /* Sous cette largeur le sommaire coûte plus qu'il ne sert — mais le
     retour reste : seul, en tête de colonne, dans le fil de la page. */
  .cs-toc { position: static; padding-top: 0; }
  .cs-toc__liste { display: none; }
}

/* ── La cover : pleine largeur, elle prend l'écran avant la lecture ── */
.cs-cover {
  margin: 0 0 2rem;
  padding: 0;
}

.cs-cover video,
.cs-cover img {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  height: auto;
  object-fit: cover;
  border-radius: var(--r-coin);
}

/* ── La fiche : la méta sur six colonnes, les actions à droite ── */
.cs-fiche {
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
  padding: 0 0 2.5rem;
  border-bottom: 1px solid rgb(var(--encre) / 0.08);
  margin-bottom: 3.5rem;
}
.cs-fiche__meta {
  padding: 0;
  /* Trois colonnes sur deux rangées : six cases cramponnées sur une
     ligne cassaient chaque valeur sur quatre lignes. */
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1.5rem 2rem;
}
.cs-fiche__actions { padding: 0; display: block; }
/* Le bouton prend toute la largeur de la colonne : un seul appel, franc. */
.cs-fiche__actions .cs-cta { width: 100%; justify-content: center; box-sizing: border-box; }
@media (max-width: 40rem) {
  .cs-fiche__meta { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* ── Les étapes : une grille, chaque case porte son média ── */
.cs-etapes {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 2.5rem 1.75rem;
  width: 100%;
}
.cs-etape {
  position: relative; /* le badge se cale sur le visuel, premier enfant */
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
.cs-etape__media {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9; /* celui des vidéos : rien n'est rogné */
  height: auto;
  object-fit: cover;
  min-height: 0;
  margin-bottom: 0.625rem;
  border-radius: var(--r-coin);
  border: 1px solid rgb(var(--encre) / 0.1);
  background: var(--plaque);
}
/* Le numéro, en badge dans le coin du visuel. */
.cs-etape__num {
  position: absolute;
  top: 0.625rem;
  left: 0.625rem;
  min-width: 1.75rem;
  height: 1.75rem;
  padding: 0 0.5rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--r-pilule);
  /* Ce numéro ne se pose pas sur la page mais sur un visuel : il ne suit donc
     pas l'encre du thème, sinon il en prend la couleur sans en prendre le
     fond. C'est exactement ce qui s'était produit au passage en clair — un
     chiffre noir sur une pastille noire, invisible.
     La pastille est claire, à l'image de la page, et son chiffre sombre : sur
     les captures d'interface, qui sont sombres, elle ressort d'elle-même ; sur
     un visuel clair, le filet la détoure. Elle tient des deux côtés, ce
     qu'aucune des deux valeurs d'encre ne permettait. */
  background: rgb(255 255 255 / 0.88);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  border: 1px solid rgb(0 0 0 / 0.08);
  color: #08090a;
  font-family: ui-monospace, "SF Mono", SFMono-Regular, Menlo, monospace;
  font-size: 0.75rem;
  line-height: 1;
  letter-spacing: 0.04em;
}
.cs-etape__texte {
  margin: 0;
  font-size: 0.9375rem;
  line-height: 1.7;
  opacity: 0.8;
}
/* Un emplacement encore vide garde la proportion d'un média. */
.cs-etape .cs-slot { padding: 1rem; justify-content: center; }

@container (max-width: 34rem) {
  .cs-etapes { grid-template-columns: minmax(0, 1fr); }
}

/* ── Échelle typographique de la page : quatre paliers, un seul corps ──
   Titre 52 · section 32 · sous-titre 24 · corps 18. Le corps de lecture
   est unique (18 / 1,65 / 0,85) ; les étapes et légendes forment un
   palier « petit » à 15 ; les labels se subordonnent à 0,5.
   Posé en toute fin de feuille : à spécificité égale, le dernier gagne.
   Les couleurs portent body.projet, le préfixe des règles sombres du site,
   sans quoi elles perdaient d'un cran. ────────────────────────────── */
.cs-colonne .project__title {
  font-size: clamp(2.25rem, 3.8vw, 3.25rem);
  line-height: 1.1;
  letter-spacing: -0.01em;
}
.project .cs-h2 { font-size: clamp(1.625rem, 2.6vw, 2rem); line-height: 1.15; }
.project .cs-h3 { font-size: 1.5rem; line-height: 1.25; }
body.projet .cs-colonne .project__body-heading {
  font-size: 0.75rem;
  letter-spacing: 0.12em;
  color: rgb(var(--encre) / 0.5);
  opacity: 1;
}
body.projet .cs-layout .project__body-intro,
body.projet .cs-layout .project__body-item-text {
  font-size: 1.125rem;
  line-height: 1.65;
  color: rgb(var(--encre) / 0.85);
  opacity: 1;
}
/* Certigna et Cobalt titrent leurs blocs avec .project__body-item-title,
   une grotesque grasse héritée de l'ancienne mise en page : même palier
   que les .cs-h3 de DotLab, pour que les trois pages parlent pareil.
   Cette règle est plus spécifique que celle qui fixe la graisse des titres :
   sans la reprendre ici, ces intertitres seraient restés seuls en 400. */
body.projet .cs-layout .project__body-item-title {
  font-size: 1.5rem;
  line-height: 1.25;
  font-weight: 500;
  color: rgb(var(--encre));
  opacity: 1;
}
.cs-layout .cs-etape__texte,
.cs-layout .cs-jetes__legende {
  font-size: 0.9375rem;
  line-height: 1.6;
  color: rgb(var(--encre) / 0.72);
  opacity: 1;
}
body.projet .cs-fiche__meta dt { font-size: 0.75rem; letter-spacing: 0.1em; color: rgb(var(--encre) / 0.45); opacity: 1; }
body.projet .cs-fiche__meta dd { font-size: 1rem; line-height: 1.4; color: rgb(var(--encre) / 0.9); }

/* ─── Le projet suivant : la sortie du projet, pas sa dernière image ──────────
   Tant qu'il portait une vignette 16/9 pleine colonne, il avait exactement la
   forme des figures de l'étude au-dessus : on croyait lire un visuel de plus
   du projet en cours. Deux choses l'en séparent maintenant. Le sol : la zone
   est précédée d'un grand blanc — on a quitté l'article, sans qu'un aplat ni
   un filet aient à le dire. Et la forme : plus de pleine colonne,
   mais une ligne, vignette à gauche et titre à droite, la proportion qu'aucune
   figure de la page n'emploie. Le bloc reste calé sur la colonne de lecture,
   avec la même gouttière de sommaire à sa gauche. ──────────────────────────── */
.cs-suivant-zone {
  display: grid;
  /* Même grille que .cs-layout, pour que la carte reste sous la colonne de
     lecture plutôt que de dériver de son côté. Les deux valeurs sont reprises
     par le même override au-delà de 58 rem, plus bas dans ce fichier. */
  grid-template-columns: 10rem minmax(0, 44rem);
  gap: 4.5rem;
  justify-content: center;
  margin-top: 7rem;
  /* Le bas reprenait --spacing-21, soit 21 px : la carte « projet suivant »
     avait 112 px d'air au-dessus d'elle et 22 px en dessous, si bien qu'elle
     se retrouvait collée au trait du pied. On lui rend une respiration du même
     ordre que celle qui l'annonce — un peu moindre, parce qu'un pied de page
     se rapproche légitimement plus qu'une section ne s'ouvre. */
  padding: 0 var(--project-pad-x) clamp(3.5rem, 7vw, 5.5rem);
}
.cs-suivant {
  grid-column: 2;
  display: grid;
  grid-template-columns: 13rem minmax(0, 1fr);
  grid-template-areas:
    "label label"
    "cadre pied";
  column-gap: 1.75rem;
  row-gap: 1.25rem;
  align-items: center;
  text-decoration: none;
  color: inherit;
}
.cs-suivant__label { grid-area: label; }
.cs-suivant__cadre { grid-area: cadre; }
.cs-suivant__pied { grid-area: pied; }
.cs-suivant__label {
  font-size: 0.75rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgb(var(--encre) / 0.5);
}
/* Le cadre rogne le zoom du survol : la vignette grossit dedans, pas hors de sa boite. */
.cs-suivant__cadre {
  display: block;
  overflow: hidden;
  isolation: isolate;
  border-radius: var(--r-coin);
  border: 1px solid rgb(var(--encre) / 0.1);
  background: rgb(var(--encre) / 0.04);
}
.cs-suivant__vignette {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 10;
  height: auto;
  object-fit: cover;
  transition: transform 600ms var(--courbe);
}
/* Titre et tag a gauche, fleche a droite, centres sur la vignette. */
.cs-suivant__pied {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
}
.cs-suivant__texte { display: flex; flex-direction: column; gap: 0.375rem; min-width: 0; }
.cs-suivant__titre {
  font-family: "Denim", ui-sans-serif, system-ui, sans-serif;
  /* 500 et non 400 : Denim fournit un vrai Medium, et font-synthesis: none
     garantit que c'est ce dessin-là qui sert, pas un gras simulé. */
  font-weight: 500;
  font-synthesis: none;
  font-size: 1.5rem;
  line-height: 1.22;
  letter-spacing: -0.01em;
  color: rgb(var(--encre));
}
.cs-suivant__tag { font-size: 0.8125rem; color: rgb(var(--encre) / 0.55); }
.cs-suivant__fleche {
  flex: none;
  /* Le corps ne sert plus qu'à dimensionner l'icône, qui est en em. */
  font-size: 1.5rem;
  color: rgb(var(--encre) / 0.5);
  transition: transform var(--vite) var(--courbe), color var(--vite) var(--courbe);
}
.cs-suivant:focus-visible .cs-suivant__vignette { transform: scale(1.03); }
@media (hover: hover) and (pointer: fine) {
  .cs-suivant:hover .cs-suivant__vignette { transform: scale(1.03); }
}
.cs-suivant:focus-visible .cs-suivant__fleche { transform: translateX(6px); color: rgb(var(--encre)); }
@media (hover: hover) and (pointer: fine) {
  .cs-suivant:hover .cs-suivant__fleche { transform: translateX(6px); color: rgb(var(--encre)); }
}
.cs-suivant:focus-visible { outline: 2px solid rgb(var(--encre)); outline-offset: 6px; border-radius: var(--r-coin); }

@media (max-width: 58rem) {
  .cs-suivant-zone { grid-template-columns: minmax(0, 44rem); }
  .cs-suivant { grid-column: 1; }
}
@media (min-width: 58.0625rem) {
  .cs-suivant-zone {
    grid-template-columns: 11.5rem minmax(0, 44rem);
    gap: 6rem;
    /* Même calage que .cs-layout : la carte reste sous la colonne. */
    justify-content: start;
    padding-left: calc(var(--bord-colonne) - 17.5rem);
  }
}
@media (max-width: 40rem) {
  .cs-suivant-zone { margin-top: 4.5rem; padding-bottom: 3.5rem; }
  /* La ligne se resserre mais reste une ligne : sur un telephone, c'est le
     format d'une entree de liste, et rien sur la page ne lui ressemble. */
  .cs-suivant {
    grid-template-columns: 6rem minmax(0, 1fr);
    column-gap: 1rem;
    row-gap: 1rem;
  }
  .cs-suivant__fleche { font-size: 1.25rem; }
  .cs-suivant__pied { gap: 0.75rem; }
}
@media (prefers-reduced-motion: reduce) {
  .cs-suivant__vignette, .cs-suivant__fleche { transition: none; }
}

/* Sous 40rem la marge latérale suit celle de l'accueil : 1.75rem, pour que
   la colonne de lecture fasse la largeur des cartes de la page d'accueil. */
@media (max-width: 40rem) {
  .cs-layout, .cs-suivant-zone { padding-left: 1.75rem; padding-right: 1.75rem; }
}
/* ── L'échelle, rejouée pour le téléphone ──────────────────────────────
   Sur 375 px la colonne ne fait plus que 319 px, et l'échelle de bureau
   s'y écrase : section à 26, sous-titre à 24. Deux pixels d'écart, donc
   aucun palier — la page n'avait plus que du texte de la même taille.
   On la rejoue pour l'écran étroit : 32 / 24 / 20 / 17. Le corps perd un
   pixel, ce qui lui rend deux ou trois signes par ligne — à 18 px on
   lisait par tranches de 34 caractères. Les couleurs portent body.projet,
   comme plus haut : sans ce préfixe elles perdent d'un cran. ────────── */
@media (max-width: 40rem) {
  .cs-colonne .project__title { font-size: 2rem; line-height: 1.12; }
  .project .cs-h2 { font-size: 1.5rem; line-height: 1.2; }
  .project .cs-h3 { font-size: 1.25rem; line-height: 1.3; }
  body.projet .cs-layout .project__body-item-title { font-size: 1.25rem; line-height: 1.3; }

  body.projet .cs-layout .project__body-intro,
  body.projet .cs-layout .project__body-item-text {
    font-size: 1.0625rem;
    line-height: 1.62;
  }

  .cs-layout .cs-etape__texte,
  .cs-layout .cs-jetes__legende { font-size: 0.9375rem; line-height: 1.55; }

  /* Un label de 12 px à 50 % de blanc tombe à 3,9:1 sur le fond noir :
     sous le seuil. Il remonte, et se resserre d'un demi-pixel. */
  body.projet .cs-colonne .project__body-heading {
    font-size: 0.6875rem;
    letter-spacing: 0.1em;
    color: rgb(var(--encre) / 0.62);
  }

  body.projet .cs-fiche__meta dt { font-size: 0.6875rem; color: rgb(var(--encre) / 0.55); }
  body.projet .cs-fiche__meta dd { font-size: 0.9375rem; line-height: 1.45; }

  /* La fiche et la cover se resserrent : sur un écran haut, trois écrans
     de blanc avant le premier paragraphe, c'est deux de trop. */
  .cs-cover { margin-bottom: 1.5rem; }
  .cs-fiche { gap: 1.25rem; padding-bottom: 2rem; margin-bottom: 2.75rem; }

  .cs-suivant__titre { font-size: 1.125rem; line-height: 1.25; }
}

/* ════════════════════════════════════════════════════════════════════════════
   REFONTE DE L'ÉTUDE DE CAS — d'après le fichier Paper
   ════════════════════════════════════════════════════════════════════════════
   La page passe en clair, comme l'accueil. Rien n'a été redessiné pour cela :
   toutes les couleurs de la feuille s'écrivent désormais rgb(var(--encre) / …)
   sur var(--fond), et il suffit d'échanger les deux valeurs pour que la page
   entière se retourne — les filets, les plaques, les textes en retrait.

   La classe du <body> a suivi : elle nommait un thème, elle nomme maintenant
   un type de page. Une étude de cas reste une étude de cas quelle que soit sa
   couleur.

   La visionneuse plein écran garde ses vrais blancs : elle ne se pose pas sur
   la page mais sur un voile noir. La pastille « agrandir » du survol, elle,
   est passée en clair avec le reste.

   Le fond est le crème de l'accueil, #FFFCF5, et non le blanc : les deux
   pages se suivent d'un clic, et le papier ne doit pas changer en route. La
   plaque à média suit du même pas — mêlée au crème, elle garde son écart au
   fond au lieu de virer au gris froid. */

/* Le papier d'un projet est celui des cartes de l'accueil tel qu'on le voit :
   là-bas, #fffcf5 passe sous la lumière du feuillage et s'y réchauffe. Posé
   à plat ici, le même crème paraissait presque blanc. D'où un blanc cassé
   un cran plus chaud, pris sur les cartes à l'écran. */
body.projet {
  --encre: 0 0 0;
  --fond: var(--papier-projet);
  /* La plaque qui reçoit un média : un gris à peine posé, là où le sombre
     mettait un noir. Elle sert de fond aux visuels sur fond transparent. */
  --plaque: #efeadf;
  color-scheme: light;
}
html:has(body.projet) { background: var(--papier-projet); }

/* Les visuels sont imprimés sur la feuille : en multiply, leurs blancs
   prennent le papier. Une capture d'écran sur fond blanc faisait sinon un
   rectangle blanc pur, posé sur le crème. Le reste de l'image n'en bouge
   presque pas, le papier étant très clair. */
body.projet .project img,
body.projet .project video { mix-blend-mode: multiply; }
/* Le multiply ne voit que ce qui est peint dans son groupe : le bouton de
   zoom est isolé (isolation, pour Safari) et sans fond, et l'image s'y
   multipliait sur du vide. Il prend donc le papier sous elle, sauf là où
   une plaque est déjà posée. Même chose pour le cadre du projet suivant,
   dont le voile translucide devient un aplat. */
body.projet .cs-zoom:not(.cs-zoom--media):not(.cs-zoom--etape) { background-color: var(--fond); }
body.projet .cs-suivant__cadre { background: var(--papier); }

/* ─── Les titres passent à Freight ───────────────────────────────────────────
   Même serif que l'accueil, et même partage des rôles : Book pour le titre du
   projet, Medium pour les intitulés de section — un titre de 44 px n'a pas
   besoin de la graisse qu'un intertitre de 24 réclame. */
body.projet .project__title,
body.projet .cs-h2,
body.projet .cs-h3,
body.projet .project__body-item-title {
  font-family: "Freight", "Freight Text", Georgia, "Times New Roman", serif;
  font-weight: 500;
  font-synthesis: none;
  letter-spacing: -0.01em;
}
body.projet .project__title {
  /* Le titre redescend à 400 : à 44 px, le Medium qui tient un intertitre
     devient massif. Correction optique, comme sur l'accueil. */
  font-weight: 400;
  letter-spacing: -0.025em;
  line-height: 1.2;
}

/* ─── Le bouton de la fiche ──────────────────────────────────────────────────
   Il passe du bleu au noir. Sur la page sombre, le bleu était la seule tache
   de couleur et servait d'appel ; sur du blanc, le noir plein va plus loin
   sans rien emprunter à la marque du client. Pleine largeur de la colonne,
   comme dans le dessin. */
body.projet .cs-cta {
  display: flex;
  width: 100%;
  height: 2.6875rem;
  padding: 0;
  justify-content: center;
  align-items: center;
  gap: 0.5rem;
  border: 0;
  border-radius: var(--r-coin);
  background: rgb(var(--encre));
  color: var(--fond);
  font-size: 0.9375rem;
}
@media (hover: hover) and (pointer: fine) {
  body.projet .cs-cta:hover {
    background: rgb(var(--encre) / 0.82);
    color: var(--fond);
  }
}

/* ─── Le sommaire ────────────────────────────────────────────────────────────
   La section en cours ne se distingue plus par la seule opacité : elle prend
   une pastille grise, comme l'onglet actif de la barre. Sur du blanc, un écart
   d'opacité seul se lisait mal d'un coup d'œil. */
body.projet .cs-toc__liste { gap: 0.6rem; }
body.projet .cs-toc__liste a {
  padding: 0.25rem 0.75rem;
  border-radius: var(--r-coin);
  opacity: 1;
  color: rgb(var(--encre) / 0.42);
  transition: color var(--vite) var(--courbe), background var(--vite) var(--courbe);
}
@media (hover: hover) and (pointer: fine) {
  body.projet .cs-toc__liste a:hover {
    opacity: 1;
    color: rgb(var(--encre) / 0.72);
  }
}
body.projet .cs-toc__liste a.est-lue {
  background: rgb(var(--encre) / 0.05);
  color: rgb(var(--encre) / 0.72);
}

/* ─── La grille de la page ───────────────────────────────────────────────────
   Les trois mesures du dessin : 184 px de sommaire, 96 d'écart, 704 de
   colonne. Le sommaire gagne 24 px sur la version précédente — c'est ce qui
   manquait pour que « Continuité cross-device » tienne sur une ligne une fois
   la pastille posée autour.
   La règle s'arrête à 58 rem : en dessous, le sommaire disparaît et la page
   repasse sur une seule colonne — lui imposer deux colonnes fixes la faisait
   déborder de l'écran. */
@media (min-width: 58.0625rem) {
  body.projet .cs-layout {
    grid-template-columns: 11.5rem minmax(0, 44rem);
    gap: 6rem;
    padding-top: 7rem;
    /* La grille ne se centre plus d'elle-même : elle se cale sur le bord de
       texte que la barre annonce (--bord-colonne), moins la place du sommaire
       et de sa gouttière — 280 px. Le titre tombe ainsi sous « Work », que la
       barre soit au centre de la fenêtre ou qu'elle ait dû glisser. */
    justify-content: start;
    padding-left: calc(var(--bord-colonne) - 17.5rem);
  }
}

/* Le titre du projet suivant est un titre comme les autres : il passe à
   Freight avec eux. C'était le dernier endroit de la page où Denim restait,
   et la seule raison pour laquelle son fichier était encore téléchargé. */
body.projet .cs-suivant__titre {
  font-family: "Freight", "Freight Text", Georgia, "Times New Roman", serif;
  font-weight: 500;
  font-synthesis: none;
  letter-spacing: -0.01em;
}


/* ─── Le pied des pages projet ───────────────────────────────────────────────
   Il reste clair. Sur l'accueil, la bande noire du pied prolonge « À propos »
   qui la précède : elle termine un mouvement commencé plus haut. Ici rien ne
   la prépare, et une bande noire tomberait sans raison à la fin d'une page
   entièrement blanche. Le pied s'y ferme sur un filet, et c'est tout. */

/* ─── Le bouton de la fiche ──────────────────────────────────────────────────
   Son texte est centré sur les deux axes, flèche comprise : le groupe mot +
   icône se place comme un seul objet au milieu du bouton, et la ligne de base
   ne dérive pas avec la hauteur. */
body.projet .cs-cta {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  padding: 0;
  height: 2.75rem;
  border-radius: var(--r-coin);
  line-height: 1;
  text-align: center;
}
/* Le mot et la flèche : même axe, quel que soit le corps. */
body.projet .cs-cta > * { display: inline-flex; align-items: center; }

/* ─── Les rayons de l'étude de cas ───────────────────────────────────────────
   L'échelle vient de styles-v3.css, et chaque règle y puise là où elle est
   écrite : cover, visuels, sommaire et boutons prennent tous le même coin.
   Les rappels qui se trouvaient ici répétaient la source,
   et l'un d'eux la contredisait — la cover y était ramenée à une surface
   alors que son image portait un rayon à elle.

   Reste ce qui ne peut pas s'écrire à la source : la vignette du projet
   suivant est rognée par un cadre au filet d'un pixel, elle prend donc le
   rayon du cadre moins ce filet. */
body.projet .cs-suivant__vignette { border-radius: calc(var(--r-coin) - 1px); }

/* ─── Les commandes de la visionneuse ────────────────────────────────────────
   Chacune est une pastille pleine, et le signe respire dedans.

   Elles n'avaient plus de fond du tout — le cercle dessiné dans le SVG en
   tenait lieu, et quand il est parti il n'est resté qu'un trait nu posé sur
   l'image : rien pour dire où l'on clique, et une croix blanche qui passait
   sur les aplats clairs des captures. Le signe remplissait par-dessus le
   marché toute la boîte du bouton, d'un bord à l'autre, sans un pixel de
   marge.

   Deux fonds unis, donc, chacun accordé à ce qu'il couvre : les chevrons sont
   sur le voile, ils prennent un disque d'encre et un signe clair ; la croix
   est dans la barre blanche de la fenêtre, elle prend un disque clair et un
   signe d'encre. Ni transparence ni verre dépoli — un aplat, qui tient devant
   n'importe quelle image.

   Quarante-quatre pixels de pastille pour dix-huit de signe : treize de marge
   tout autour. C'est la cible tactile de référence, et le signe cesse d'être
   à l'étroit. */
.lb-overlay--cs .lb-nav {
  width: 2.75rem;
  height: 2.75rem;
  padding: 0;
  border: 0;
  /* Des boutons comme les autres, au rayon des cartes (voir .lb-player). */
  border-radius: var(--r-coin);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background-color var(--vite) var(--courbe),
              color var(--vite) var(--courbe),
              translate var(--ample) var(--courbe-trajet);
}
/* Les chevrons, sur le voile. */
.lb-overlay--cs .lb-nav {
  background: #17181a;
  color: rgb(255 255 255 / 0.92);
}
/* Taille donnée en dur, pas en pourcentage : le bouton est une boîte flex
   centrée, et un svg en hauteur relative s'y étire au lieu de rester carré. */
.lb-overlay--cs .lb-nav svg {
  width: 1.125rem;
  height: 1.125rem;
  flex: none;
  display: block;
}
/* Le focus clavier : l'anneau du navigateur est orange et carré, il ne
   ressemble à rien de la page. Chacune reprend celui du site, dans l'encre
   qui tranche sur son propre fond. */
.lb-overlay--cs .lb-nav:focus-visible {
  outline: 2px solid rgb(255 255 255 / 0.9);
  outline-offset: 3px;
}
@media (hover: hover) and (pointer: fine) {
  .lb-overlay--cs .lb-nav:hover {
    background: #2c2e32;
    color: #fff;
  }
}
.lb-overlay--cs .lb-nav:disabled { opacity: 0.25; }

/* Le carrousel des retours suit la même règle : l'icône fait le bouton. */
/* La barre du carrousel passe sous les post-it : la phrase qui les résume à
   gauche, les flèches à droite, sur la même ligne. */
body.projet .cs-carrousel__barre {
  justify-content: space-between;
  gap: 1.5rem;
  margin: 1.25rem 0 0;
}
body.projet .cs-carrousel__legende { margin: 0; }
body.projet .cs-carrousel__boutons { flex: none; margin-left: auto; }

/* Les flèches : des pavés au rayon du site, dans la teinte des commandes de
   la barre (l'onglet actif, la langue), un voile d'encre à 7 % et le signe
   un ton en dessous du texte. Le noir plein des autres boutons tirait l'œil
   plus que les post-it qu'elles font défiler. Le cercle dessiné autour du
   chevron disparaît : c'est le pavé qui fait le bouton. */
body.projet .cs-carrousel__btn {
  width: 2.25rem;
  height: 2.25rem;
  padding: 0;
  border: 0;
  border-radius: var(--r-coin);
  background: rgb(var(--encre) / 0.07);
  color: rgb(var(--encre) / 0.75);
  transition: background var(--vite) var(--courbe), color var(--vite) var(--courbe);
}
body.projet .cs-carrousel__btn .ico { width: 1.875rem; height: 1.875rem; }
body.projet .cs-carrousel__btn circle { display: none; }
@media (hover: hover) and (pointer: fine) {
  body.projet .cs-carrousel__btn:hover { background: rgb(var(--encre) / 0.12); color: rgb(var(--encre)); }
}


/* ─── Le sommaire : une pastille qui glisse, et des lignes resserrées ────────
   Le repère de lecture ne vit plus dans chaque ligne : c'est un seul fond,
   posé sur la liste, qui se déplace du chapitre quitté vers le chapitre
   atteint. Deux fonds qui se croisaient en fondu faisaient un clignotement ;
   un fond qui se déplace se lit comme un mouvement, et l'œil le suit.

   Position et hauteur viennent de case-study.js, en variables. Le déplacement
   passe par transform : il reste sur le compositeur et ne repeint rien, ce qui
   compte pour un élément qui bouge pendant qu'on fait défiler la page. */
body.projet .cs-toc__liste {
  position: relative;
  /* Plus de gouttière entre les lignes : c'est leur propre rembourrage qui
     les espace désormais. Neuf chapitres séparés de 9,6 px faisaient une
     colonne de trous — on lisait les intervalles autant que les titres. */
  gap: 0;
}
body.projet .cs-toc__liste::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: var(--toc-h, 0);
  border-radius: var(--r-coin);
  background: rgb(var(--encre) / 0.05);
  transform: translateY(var(--toc-y, 0));
  /* Rien tant que la première mesure n'a pas eu lieu : sinon la pastille
     s'affiche en haut de la liste avant de rejoindre sa place. */
  opacity: 0;
}
body.projet .cs-toc__liste.a-repere::before {
  opacity: 1;
  transition: transform var(--ample) var(--courbe), height var(--ample) var(--courbe),
              opacity var(--vite) var(--courbe);
}
/* Les liens repassent devant la pastille, qui sinon les recouvrirait. */
body.projet .cs-toc__liste li { position: relative; }

/* La ligne ne porte plus de fond à elle : seule sa couleur dit qu'on y est.
   Le rembourrage vertical remplace la gouttière — la pastille a ainsi une
   hauteur pleine, et les titres se suivent sans trou entre eux. */
body.projet .cs-toc__liste a {
  /* Cinq pixels au-dessus et en dessous d'une ligne de 21 : le pas tombe à
     31 px, contre 38,6 avant — un cinquième de moins. Assez pour que les neuf
     chapitres se lisent comme une liste, assez encore pour viser au doigt. */
  padding: 0.3125rem 0.75rem;
  border-radius: var(--r-coin);
}
body.projet .cs-toc__liste a.est-lue {
  background: none;
  color: rgb(var(--encre) / 0.82);
}

/* Le retour reste près de la liste, mais s'en détache d'un cran : à 8 px,
   « Retour aux projets » se lisait comme le premier chapitre. Le décalage
   compense le rembourrage des chapitres, pour que tous les mots tombent sur
   la même verticale. */
body.projet .cs-toc { gap: 1rem; }
body.projet .cs-toc__retour { padding-left: 0.75rem; }
/* Le retour sur la même ligne de pied que l'eyebrow du titre, en face
   (« Projet · DotLab · … ») : leurs corps diffèrent (14 et 12 px), et le
   retour tombait 6 px plus bas. Seulement quand le sommaire est à côté de la
   colonne ; au-dessous de 58 rem, il passe au-dessus d'elle. */
@media (min-width: 58.0625rem) {
  body.projet .cs-toc__retour { margin-top: -0.375rem; }
}

@media (prefers-reduced-motion: reduce) {
  body.projet .cs-toc__liste.a-repere::before { transition: opacity var(--vite) var(--courbe); }
}


/* ════════════════════════════════════════════════════════════════════════════
   LA VISIONNEUSE — le visuel seul sur le voile
   ════════════════════════════════════════════════════════════════════════════
   Il y avait ici une fenêtre claire, reprise du composant « agent screen » :
   un fond blanc, un rembourrage, une ombre portée et une barre de titre, le
   tout autour du visuel. Elle est partie. Ce qu'on vient regarder, c'est
   l'image ; un cadre blanc autour ajoutait un second objet à lire, une
   seconde couleur, et un bord qui n'appartenait pas au projet montré.

   Reste la géométrie. Le bloc tient toujours la barre au-dessus et le visuel
   dessous, et c'est encore sa découpe qui s'ouvre depuis l'empreinte de la
   vignette. Mais il ne peint plus rien : ni fond, ni rembourrage, ni ombre.
   La légende et les commandes se posent donc directement sur le voile, d'où
   leur encre claire et la pastille sombre de la croix, désormais la même que
   celle des chevrons.

   Le visuel se dimensionne lui-même — c'est lui qui donne sa taille au bloc,
   jamais l'inverse, si bien qu'on le voit toujours en entier, sans coupe ni
   bande noire. Son rayon lui appartient : il est posé par le script, à la
   valeur exacte de la vignette d'où il vient. */
.lb-fenetre {
  position: relative;
  display: flex;
  flex-direction: column;
  max-height: 100%;
  /* Le visuel est mis à l'échelle depuis sa vignette à l'ouverture : la
     boîte le rogne pendant le trajet, et la découpe le révèle. */
  overflow: hidden;
}
/* La fenêtre n'a ni fondu ni mise à l'échelle : elle est posée à sa place et
   à sa taille dès le départ, et c'est sa découpe qui s'ouvre depuis
   l'empreinte de la vignette — voir ouvrirFenetre() dans case-study.js, qui
   l'anime en clip-path sur la durée et la courbe du visuel.

   Avant, elle montait en opacité avec un scale(0.965) pendant que le visuel
   faisait un vrai trajet depuis son tirage : deux gestes de nature différente
   pour une seule ouverture. Rien ici ne varie plus en opacité ; tout est
   géométrie, et les deux mouvements n'en font qu'un. */

/* Il y avait ici une ligne de repères posée au-dessus du visuel : la légende
   à gauche, le compteur et la croix à droite. Elle a été retirée. On vient
   regarder une image, pas lire son nom ; et une croix, un titre et un rang
   font trois objets de plus à l'écran pour un geste qui n'en demande aucun.

   Ce qu'elle portait ne disparaît pas pour autant. La légende reste dans le
   texte de remplacement de l'image, où un lecteur d'écran la trouve. Le rang
   se lit aux deux chevrons, qui disent déjà qu'il y a un avant et un après.
   Et pour fermer, il y a Échap, ou un clic n'importe où hors du visuel. */

/* ─── L'encadré du visuel ────────────────────────────────────────────────────
   Il ne dimensionne rien et ne peint rien : c'est le visuel qui le remplit et
   qui le porte. Il n'a plus de fond — un aplat clair derrière une image
   transparente redessinait le cadre qu'on vient d'enlever. Son rayon est
   celui du visuel, pour que le rognage du trajet suive les mêmes angles. */
.lb-overlay--cs .lb-img-wrap {
  position: relative;
  display: flex;
  min-height: 0;
  overflow: hidden;
  border-radius: var(--r-coin);
}
/* Bornes du visuel : la hauteur de l'écran moins la barre, le titre et l'air
   autour ; la largeur, celle d'une fenêtre confortable sans dépasser l'écran.
   object-fit: contain n'a plus lieu d'être — l'image est à sa taille. */
.lb-overlay--cs .lb-img-wrap img,
.lb-overlay--cs .lb-img-wrap video {
  display: block;
  width: auto;
  height: auto;
  /* La largeur réserve de quoi poser un chevron de chaque côté : 44 px de
     pastille, 32 du bord et un peu d'air. À 90vw, la fenêtre venait sous eux
     et l'on cliquait sur l'image en croyant viser la flèche. */
  max-width: min(64rem, calc(100vw - 14rem));
  /* La ligne de repères prenait cinquante-six pixels au-dessus du visuel :
     il les récupère, et il ne reste que l'air des deux bords. */
  max-height: calc(100vh - 6rem);
  border-radius: 0;
  object-fit: contain;
}

/* ─── Le chargement ──────────────────────────────────────────────────────────
   Ces captures pèsent leurs centaines de kilo-octets : sans rien, la fenêtre
   s'ouvrait sur un rectangle vide. Le disque ne paraît qu'au bout d'un court
   délai, sinon il clignoterait pour une image déjà en cache. */
.lb-img-wrap.est-en-attente::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: 1.625rem;
  height: 1.625rem;
  margin: -0.8125rem 0 0 -0.8125rem;
  border-radius: 50%;
  border: 2px solid rgb(255 255 255 / 0.18);
  border-top-color: rgb(255 255 255 / 0.7);
  animation: lb-tourne 1.1s linear infinite;
}
@keyframes lb-tourne { to { transform: rotate(1turn); } }

@media (prefers-reduced-motion: reduce) {
  .lb-fenetre { clip-path: none !important; }
  .lb-img-wrap.est-en-attente::after { animation: none; }
}

@media (max-width: 34rem) {
  /* Les chevrons sont descendus au pied de l'écran : le visuel n'a plus à
     leur réserver une pastille de chaque côté, et reprend toute la largeur
     moins une gouttière. Il gardait la réserve de l'écran large — deux cent
     vingt-quatre pixels — et se retrouvait à cent cinquante de large sur un
     téléphone de trois cent soixante-quinze. */
  .lb-overlay--cs .lb-img-wrap img,
  .lb-overlay--cs .lb-img-wrap video {
    max-width: calc(100vw - 1.5rem);
    max-height: calc(100vh - 8rem);
  }
}

/* ─── Le projet suivant, en carte de l'accueil ───────────────────────────────
   Le projet suivant a le même papier que les cartes de l'accueil : un
   crème un cran plus clair que la page, penché, tenu par une punaise,
   la vignette en 2:1 en haut, le titre en Freight et le tag dessous. Pas de
   flèche : les cartes de l'accueil n'en ont pas, et le papier dit déjà
   qu'on peut le prendre. Au survol, le même geste qu'à l'accueil, lent : le
   papier se redresse et s'approche d'un rien, la vignette grossit sur
   place. Le lien ne bouge pas, seul le papier dedans (voir « Les papiers »
   dans styles-v3.css). */
body.projet .cs-suivant {
  display: block;
  width: min(100%, 32rem);
}
body.projet .cs-suivant__label {
  display: block;
  margin-bottom: 1.5rem;
}
body.projet .cs-suivant__papier {
  position: relative;
  display: block;
  padding: 0.75rem 0.75rem 1.25rem;
  background: var(--papier);
  border-radius: var(--r-coin);
  rotate: -1.2deg;
  transition:
    rotate 1100ms var(--courbe),
    scale 1100ms var(--courbe);
}
/* Tenu par une punaise, comme les cartes de l'accueil : un rendu figé de
   modeles/punaise.glb, même blanc crème, même soleil, son ombre comprise.
   La tête est au centre de l'image, qui fait trois fois sa largeur. */
body.projet .cs-suivant__papier::before {
  content: "";
  position: absolute;
  z-index: 2;
  top: 1rem;
  left: 50%;
  width: 5.5rem;
  aspect-ratio: 1;
  translate: -50% -50%;
  background: url("assets/accueil/punaise.png") center / contain no-repeat;
  pointer-events: none;
}
body.projet .cs-suivant__cadre {
  border: 0;
  border-radius: var(--r-coin);
}
body.projet .cs-suivant__vignette {
  aspect-ratio: 2 / 1;
  border-radius: 0;
  transition: transform 1100ms var(--courbe);
}
body.projet .cs-suivant__pied {
  display: block;
  padding: 1rem 0.25rem 0;
}
body.projet .cs-suivant__texte { gap: 0.375rem; }
body.projet .cs-suivant__titre {
  font-size: 1.375rem;
  font-weight: 500;
  line-height: 1.25;
  letter-spacing: -0.025em;
  color: rgb(var(--encre) / 0.92);
}
body.projet .cs-suivant__tag {
  font-size: 1rem;
  line-height: 1.125;
  color: rgb(var(--encre) / 0.56);
}
@media (hover: hover) and (pointer: fine) {
  body.projet .cs-suivant:hover .cs-suivant__papier { rotate: 0deg; scale: 1.015; }
  body.projet .cs-suivant:hover .cs-suivant__vignette { transform: scale(1.04); }
}
body.projet .cs-suivant:focus-visible { outline: none; }
body.projet .cs-suivant:focus-visible .cs-suivant__papier {
  rotate: 0deg;
  outline: 2px solid rgb(var(--encre));
  outline-offset: 4px;
}
body.projet .cs-suivant:focus-visible .cs-suivant__vignette { transform: scale(1.04); }
@media (prefers-reduced-motion: reduce) {
  body.projet .cs-suivant__papier { transition: none; }
  body.projet .cs-suivant:hover .cs-suivant__papier { scale: none; }
}

/* ─── La barre ne suit pas dans un projet ─────────────────────────────────────
   Dans un projet, la barre reste en haut de la page et part avec elle : rien
   ne flotte au-dessus de la lecture. Elle garde sa place et sa forme, elle ne
   colle plus. Le sommaire, lui, reste collant, et n'a plus à laisser la place
   de la barre : il monte au haut de la fenêtre, à la même marge. Les ancres
   non plus n'ont plus de barre à éviter. */
body.projet .h-header { position: relative; top: auto; }
body.projet .cs-toc { top: var(--barre-haut); }
html:has(body.projet) { scroll-padding-top: var(--barre-haut); }
