@media (max-width: 900px) {
  
  .navbar {
    padding: 14px 0;
    backdrop-filter: blur(4px);
  }
  
  /* La barre tient sur UNE ligne, et c'est une contrainte de largeur, pas de
     goût : mesuré sur 375 px, les cinq liens réclament 338 px de texte plus
     88 px d'espacements, soit 426 pour 375 disponibles. « Clip musical », le
     plus long, se repliait alors sur deux lignes.

     Le sélecteur de langue coûtait 59 px à lui seul — cadre, padding et une
     gouttière. Il perd son cadre ici : au doigt il n'y a pas de survol à
     signaler, et deux lettres isolées en bout de barre se lisent très bien
     sans habillage. Le reste vient de l'espacement, resserré juste ce qu'il
     faut. */
  .navbar nav {
    gap: 12px;
  }

  .navbar a {
    font-size: 0.75rem;
    letter-spacing: 0.09em;
    opacity: 0.9;
    white-space: nowrap;
  }

  .nav-langue {
    border: 0;
    padding: 0;
    font-size: 0.75rem;
    opacity: 0.65;
  }
  
  #intro {
    height: auto;
    min-height: 100vh;
  }
  
  #intro.intro-expanded {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 100vh;
    height: auto;
    padding-top: 0;
    padding-bottom: 40px;
  }
  
  .hero.expanded {
    display: block;
    width: 100%;
    position: relative;
  }
  
  .hero.expanded .hero-expanded {
    position: relative;
    inset: auto;
    height: auto;
    margin: 90px 0 32px;
    padding: 40px 20px;
    background: rgba(0, 0, 0, 0.35);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    border-radius: 12px;
  }
  
  .hero.expanded .hero-content {
    position: absolute;
    inset: 0;
    height: 0;
    overflow: hidden;
  }
  
  .hero.expanded .hero-expanded-content {
    position: relative;
  }

  /* Ici le panneau n'est plus en position absolue : sa hauteur suit son
     contenu (voir .hero.expanded .hero-expanded ci-dessus). Le portrait peut
     donc s'empiler au-dessus du texte sans risque de débordement. Il reste
     modeste — 200 px — pour ne pas repousser la bio trop bas. */
  .hero-portrait {
    display: block;
    width: 200px;
    margin: 0 auto 28px;
  }
  
  /* Typographie hero expand (fusion 900 + 480) */
  .hero-expand-btn {
    font-size: 0.78rem;
    padding: 9px 18px;
    margin-top: 24px;
  }
  
  .hero-expanded-content h2 {
    font-size: 0.82rem;
    letter-spacing: 0.20em;
    margin-bottom: 24px;
  }
  
  .hero-expanded-content p {
    font-size: 0.88rem;
    line-height: 1.75;
    /* Justifié comme sur grand écran, mais la césure devient OBLIGATOIRE ici.

       Mesuré sur une colonne de 313 px (écran de 393 px), en multiples de la
       largeur d'une espace normale :

         sans césure   médiane 1,69 à 2,68×   pire ligne 7,16×
         avec césure   médiane 1,46 à 1,60×   pire ligne 2,70 à 4,11×

       Sans césure, le navigateur n'a qu'un levier — étirer les espaces — et
       une ligne finit à sept fois l'écart normal : ce sont les « rivières » de
       blancs. Avec césure il en a deux, et la médiane rejoint celle du bureau
       (1,33 à 1,45×), au prix d'un à trois mots coupés par paragraphe.

       Sur un téléphone plus étroit (375 px, colonne de 295 px) la médiane
       remonte vers 2×. À rouvrir si cela se voit. */
    text-align: justify;
    -webkit-hyphens: auto;
    hyphens: auto;
  }
  
  .hero.expanded .hero-collapse-btn {
    position: static;
    display: block;
    margin: 32px auto 0;
    padding: 12px 28px;
    min-height: 44px;
    font-size: 0.95rem;
  }
  
  /* Titre + fiche AVANT les images : empilées, des photogrammes placés au-dessus
     de leur titre se rattachent visuellement au projet précédent. */
  .project {
    display: flex !important;
    flex-direction: column !important;
    padding: 0px 20px;
    gap: 30px;
  }

  /* Sur une seule colonne, l'alternance gauche/droite du bureau n'existe plus :
     un bloc calé à gauche n'y démontre plus rien et flotte contre des images
     centrées dans leur piste. On centre, comme sur les pages projet. */
  .project.left .project-info,
  .project.right .project-info {
    order: 1 !important;
    margin-bottom: 4px;
    text-align: center;
  }

  .project-actions {
    align-items: center;
  }

  /* Pas de synopsis sur l'accueil en mobile.
     Les blocs y sont empilés — titre, fiche technique, puis images — et le
     synopsis s'intercalait entre la fiche et la galerie, repoussant les
     photogrammes loin sous la ligne de flottaison. Or c'est l'image qui donne
     envie d'ouvrir un projet, pas son résumé.
     Il reste intégralement sur la page projet, où il porte la classe
     `.project-synopsis` et où on vient précisément le lire. */
  .project-info .description {
    display: none;
  }

  .project.left .gallery-wrapper,
  .project.right .gallery-wrapper {
    order: 2 !important;
  }

  /* L'opacité de repos (0.85) suffit au doigt : pas de survol à compenser. */
  .project-actions {
    margin-top: 22px;
  }

  .gallery-wrapper {
    overflow: visible !important;
    width: 100% !important;
    display: block !important;
    margin-bottom: 28px;
  }

  .gallery-scroller {
    overflow-x: auto;
    overflow-y: hidden;
    width: 100%;
    /* Aucun ancrage magnétique. Le moteur passe ces conteneurs en
       `overflow-x: hidden` et pilote le défilement par transform : le snap
       natif n'a donc jamais eu d'effet utile. Pire, `mandatory` exige que le
       conteneur soit TOUJOURS aligné sur une cible — et il n'y en a plus
       depuis le passage à la grille CSS, `scroll-snap-align` vivant sur les
       anciennes diapos. WebKit se comporte mal dans ce cas : la page reprenait
       la main sur le défilement vertical. */
    position: relative;
    z-index: 2;
  }

  /* Grille en colonnes de trois images : l'équivalent exact des « diapos »
     que construisait autrefois buildMobileSlides(), mais obtenu sans toucher
     au DOM. La mise en page est donc correcte dès le premier rendu, et une
     ancre (#narratif) tombe au bon endroit. */
  /* Piste horizontale de colonnes. Le regroupement par trois est livré par
     la build (voir gallery_html), donc rien n'est restructuré au chargement.

     `align-items: center` est ce qui répond à la demande : une colonne
     incomplète — une ou deux images au lieu de trois — est plus courte que
     les autres, et se retrouve donc centrée verticalement au lieu de rester
     collée en haut. */
  .gallery {
    display: inline-flex !important;
    flex-direction: row !important;
    align-items: center;
    width: max-content !important;
    gap: 20px;
    flex-shrink: 0 !important;
    pointer-events: auto !important;
  }

  .gallery-slide {
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    gap: 10px;
    width: 80vw;
    flex-shrink: 0;
  }


  /* Même principe qu'en desktop : c'est le CADRE qui porte le ratio et fixe
     la taille, l'image ne fait que le remplir. Ici la contrainte est la
     largeur d'écran, donc la hauteur se déduit du ratio. */
  .gallery .img-frame,
  .gallery-slide .img-frame {
    position: relative;
    margin-right: 0 !important;
    /* La largeur vient de la colonne de grille (grid-auto-columns: 80vw). */
    width: 100%;
    max-width: 100%;
    height: auto;
    aspect-ratio: var(--ratio, 16 / 9);
    border-radius: 8px;
    overflow: hidden;
    background-color: #000;
    background-image: linear-gradient(
      105deg,
      transparent 40%,
      rgba(255, 255, 255, 0.04) 50%,
      transparent 60%
    );
    background-size: 200% 100%;
    animation: shimmer 1.8s ease-in-out infinite;
    display: flex;
    align-items: center;
    justify-content: center;
  }

  /* L'image remplit son cadre, sans rien décider : le cadre a déjà le bon
     ratio. Forcer ici une hauteur en vh la faisait déborder et se faire
     rogner par l'overflow du cadre. */
  .gallery .img-frame img,
  .gallery-slide .img-frame img {
    display: block;
    width: 100% !important;
    height: 100% !important;
    max-width: none !important;
    max-height: none !important;
    object-fit: contain !important;
    opacity: 0;
    transform: scale(0.97);
    /* `ease-in-out` plutôt que `ease-out` : la courbe est douce AUX DEUX
       bouts, l'image ne surgit plus d'un coup avant de se poser.

       Pas un `ease-in` pur, malgré son nom trompeur : il démarre lentement,
       donc l'image resterait noire plus longtemps avant d'arriver d'un bloc —
       l'inverse de ce qu'on cherche ici, où c'est justement la phase noire
       qu'on veut adoucir. */
    transition:
      opacity 1.2s ease-in-out,
      transform 1.4s cubic-bezier(0.16, 1, 0.3, 1);
    will-change: opacity, transform;
  }

  

  .gallery .img-frame img.loaded,
  .gallery-slide .img-frame img.loaded {
    opacity: 1;
    transform: scale(1);
    /* Fondu terminé : on relâche la couche de composition. Sans cela les 179
       images de l'accueil gardaient chacune la sienne, à vie. */
    will-change: auto;
  }

  /* Le retrait du miroitement est porté par `.gallery .img-frame.loaded`
     dans galleries.css, dont la spécificité l'emporte ici aussi. La règle
     `.gallery-slide` qui se trouvait à cet endroit ne correspondait plus à
     rien depuis le passage à la grille CSS. */

  
}