/* =====================================================================
   Portfolio Stéphan Rocoplan — feuille de style du site public

   Thème sombre, la photographie occupant tout l'espace visuel. L'interface
   se fait discrète : peu de contrastes, pas de bordures inutiles, et une
   mosaïque sans marge où les images se touchent.
   ===================================================================== */

/* ---------------------------------------------------------------------
   Roboto, hébergé localement

   Police variable : un seul fichier de 43 Ko couvre toutes les graisses,
   là où deux fichiers statiques en auraient pesé davantage. Servie depuis
   le serveur et non depuis un CDN, conformément à la règle du projet :
   aucun service tiers, et aucune requête vers Google au chargement.

   `font-display: swap` affiche immédiatement la police de repli plutôt
   que de laisser le texte invisible le temps du téléchargement.
   --------------------------------------------------------------------- */

@font-face {
    font-family: "Roboto";
    font-style: normal;
    font-weight: 100 900;
    font-display: swap;
    src: url("../fonts/roboto-latin-variable.woff2") format("woff2-variations"),
         url("../fonts/roboto-latin-variable.woff2") format("woff2");
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6,
                   U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F,
                   U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
    --police: "Roboto", "Helvetica Neue", Helvetica, Arial, sans-serif;
    --graisse-titre: 300;  /* Roboto Light — titres */
    --graisse-menu: 400;   /* Roboto Regular — navigation */

    --noir: #000;
    --fond: #0a0a0a;
    --fond-eleve: #141414;
    --texte: #e8e8e8;
    --texte-doux: #9a9a9a;
    --texte-tres-doux: #6a6a6a;
    /* Couleur secondaire : surlignage du menu, liens, boutons, titres de
       forfait. Un seul point à modifier pour la faire varier partout. */
    --accent: #4d9de0;
    --bordure: #232323;

    --largeur-page: 68rem;
    --gouttiere: 1.5rem;

    /* Séparation entre les photos des mosaïques. */
    --espacement-photos: 4px;

    --transition: 180ms ease;
}

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
    margin: 0;
    background: var(--fond);
    color: var(--texte);
    font-family: var(--police);
    font-weight: var(--graisse-menu);
    font-size: 16px;
    line-height: 1.65;
    -webkit-font-smoothing: antialiased;
}

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

a { color: inherit; text-decoration: none; }
a:hover { color: var(--accent); }

h1, h2, h3 { font-weight: var(--graisse-titre); line-height: 1.2; margin: 0 0 .6em; }
h1 { font-size: clamp(1.6rem, 3.5vw, 2.4rem); letter-spacing: .12em; text-transform: uppercase; }
h2 { font-size: 1.25rem; letter-spacing: .06em; }

/* Masque un élément à l'écran en le laissant lisible par les lecteurs d'écran
   et les moteurs de recherche. `display: none` l'en priverait aussi. */
.visuellement-cache {
    position: absolute;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

/* Lien d'évitement : invisible tant qu'il n'a pas le focus clavier. */
.saut-contenu {
    position: absolute; left: -9999px;
    background: var(--accent); color: var(--noir);
    padding: .75rem 1.25rem; z-index: 200;
}
.saut-contenu:focus { left: 1rem; top: 1rem; }

/* =====================================================================
   En-tête
   ===================================================================== */

.entete {
    background: var(--noir);
    border-bottom: 1px solid var(--bordure);
    position: sticky; top: 0; z-index: 100;
}

.entete__barre {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    gap: var(--gouttiere);
    padding: 1rem var(--gouttiere);
}

.logo { display: block; }
.logo img {
    width: auto;
    /* Le logo est un filigrane clair sur fond transparent : il se pose
       directement sur le noir de l'en-tête, sans cartouche. */
    height: clamp(38px, 4.5vw, 52px);
    transition: opacity var(--transition);
}
.logo:hover img { opacity: .8; }

.entete__titre {
    margin: 0; text-align: center;
    font-family: var(--police);
    font-weight: var(--graisse-titre);
    font-size: clamp(.85rem, 2vw, 1.15rem);
    letter-spacing: .42em; text-transform: uppercase;
    color: var(--texte); white-space: nowrap;
}

.reseaux {
    list-style: none; margin: 0; padding: 0;
    display: flex; gap: 1rem; justify-content: flex-end; align-items: center;
}
.reseaux a { color: var(--texte-doux); transition: color var(--transition); display: block; }
.reseaux a:hover { color: var(--accent); }
.icone { width: 20px; height: 20px; }

/* ---------------------------------------------------------------------
   Navigation
   --------------------------------------------------------------------- */

.navigation__bascule {
    display: none;
    background: none; border: 0; color: var(--texte);
    font: inherit; font-size: .8rem; letter-spacing: .2em; text-transform: uppercase;
    padding: .9rem var(--gouttiere); width: 100%;
    align-items: center; gap: .7rem; cursor: pointer;
}

.navigation__bascule-barres,
.navigation__bascule-barres::before,
.navigation__bascule-barres::after {
    display: block; width: 20px; height: 1px; background: currentColor; position: relative;
}
.navigation__bascule-barres::before,
.navigation__bascule-barres::after { content: ""; position: absolute; left: 0; }
.navigation__bascule-barres::before { top: -6px; }
.navigation__bascule-barres::after { top: 6px; }

.menu {
    list-style: none; margin: 0;
    padding: 0 var(--gouttiere) .85rem;
    display: flex; justify-content: center; flex-wrap: wrap;
    gap: 1.6rem;
}

.menu > li > a {
    font-family: var(--police);
    font-weight: var(--graisse-menu);
    font-size: .78rem; letter-spacing: .18em; text-transform: uppercase;
    color: var(--texte-doux);
    padding: .3rem 0;
    display: inline-flex; align-items: center; gap: .4rem;
    border-bottom: 1px solid transparent;
    transition: color var(--transition), border-color var(--transition);
}
.menu > li > a:hover,
.menu > li > a[aria-current="page"] { color: var(--texte); border-bottom-color: var(--accent); }

.menu__fleche {
    width: 0; height: 0;
    border-left: 3.5px solid transparent;
    border-right: 3.5px solid transparent;
    border-top: 4px solid currentColor;
}

/* Sous-menu Portfolio : alimenté par les galeries publiées depuis Lightroom. */
.menu__deroulant { position: relative; }

.sous-menu {
    list-style: none; margin: 0; padding: .5rem 0;
    position: absolute; top: 100%; left: 50%; transform: translateX(-50%) translateY(-6px);
    min-width: 15rem;
    background: var(--noir);
    border: 1px solid var(--bordure);
    opacity: 0; visibility: hidden;
    transition: opacity var(--transition), transform var(--transition), visibility var(--transition);
    z-index: 110;
}

.menu__deroulant:hover .sous-menu,
.menu__deroulant:focus-within .sous-menu,
.sous-menu.est-ouvert {
    opacity: 1; visibility: visible; transform: translateX(-50%) translateY(0);
}

.sous-menu a {
    display: block; padding: .5rem 1.2rem;
    font-family: var(--police);
    font-weight: var(--graisse-menu);
    font-size: .8rem; letter-spacing: .08em;
    color: var(--texte-doux);
    transition: color var(--transition), background var(--transition);
}
.sous-menu a:hover,
.sous-menu a[aria-current="page"] { color: var(--texte); background: var(--fond-eleve); }

/* =====================================================================
   Mosaïque

   Colonnes CSS plutôt que JavaScript : la disposition est calculée par le
   navigateur avant le premier rendu, donc aucun saut de mise en page au
   chargement — ce qui compte d'autant plus que les images sont nombreuses.
   ===================================================================== */

.mosaique {
    column-count: 4;
    /* Fine séparation entre les images, comme sur le site d'origine : elle
       délimite les photos sans les isoler. `--espacement-photos` la pilote
       horizontalement et verticalement d'un seul endroit. */
    column-gap: var(--espacement-photos);
    padding: var(--espacement-photos);
}

@media (max-width: 1200px) { .mosaique { column-count: 3; } }
@media (max-width: 800px)  { .mosaique { column-count: 2; } }
@media (max-width: 480px)  { .mosaique { column-count: 1; } }

.mosaique__element {
    margin: 0 0 var(--espacement-photos);
    break-inside: avoid;
    page-break-inside: avoid; /* anciens moteurs */
}

.mosaique__declencheur {
    display: block; width: 100%;
    padding: 0; border: 0; background: none;
    cursor: zoom-in;
    position: relative;
    overflow: hidden;
}

.mosaique__declencheur img {
    width: 100%; height: auto;
    transition: opacity var(--transition), transform 400ms ease;
}
.mosaique__declencheur:hover img { opacity: .55; transform: scale(1.015); }
.mosaique__declencheur:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* Loupe au survol : indique que la photo s'ouvre en plein écran. Elle est
   dessinée en CSS plutôt qu'en image — une requête de moins, et elle suit la
   couleur du texte. */
.mosaique__loupe {
    position: absolute;
    top: 50%; left: 50%;
    width: 46px; height: 46px;
    margin: -23px 0 0 -23px;
    opacity: 0;
    transform: scale(.7);
    transition: opacity 250ms ease, transform 250ms ease;
    pointer-events: none;
    color: #fff;
}

.mosaique__declencheur:hover .mosaique__loupe,
.mosaique__declencheur:focus-visible .mosaique__loupe {
    opacity: 1;
    transform: scale(1);
}

.mosaique__loupe svg {
    width: 100%; height: 100%;
    /* Ombre portée discrète : sans elle, la loupe disparaît sur une photo
       claire. */
    filter: drop-shadow(0 1px 3px rgba(0, 0, 0, .6));
}

/* =====================================================================
   Lightbox
   ===================================================================== */

.lightbox {
    position: fixed; inset: 0; z-index: 300;
    background: rgba(0, 0, 0, .96);
    display: flex; align-items: center; justify-content: center;
}
.lightbox[hidden] { display: none; }

.lightbox__cadre {
    margin: 0;
    max-width: 92vw; max-height: 88vh;
    display: flex; flex-direction: column; align-items: center; gap: .9rem;
}

/* Transition d'une photo à l'autre.

   Fondu enchaîné avec un léger retrait d'échelle, plutôt qu'un glissement
   latéral. Le glissement supposait de deviner un sens de lecture, et se
   lisait mal : l'image sortait d'un côté puis la suivante surgissait sans
   transition d'entrée. Le fondu reste juste quel que soit le sens, et
   convient mieux à la consultation posée d'un portfolio.

   La nouvelle image n'est posée qu'une fois chargée — sans quoi le fondu
   d'entrée jouerait sur un cadre vide. */
.lightbox__image {
    max-width: 92vw; max-height: 82vh;
    width: auto; height: auto;
    object-fit: contain;
    transition: opacity 260ms ease, transform 260ms ease;
}

.lightbox__image.en-transition {
    opacity: 0;
    transform: scale(.965);
}

.lightbox__legende {
    color: var(--texte-doux); font-size: .85rem; letter-spacing: .06em; text-align: center;
    transition: opacity 260ms ease;
}

.lightbox__legende.en-transition { opacity: 0; }

.lightbox__fermer,
.lightbox__precedent,
.lightbox__suivant {
    position: absolute;
    background: none; border: 0; color: var(--texte-doux);
    cursor: pointer; line-height: 1;
    transition: color var(--transition);
    padding: 1rem;
}
.lightbox__fermer:hover,
.lightbox__precedent:hover,
.lightbox__suivant:hover { color: var(--texte); }

.lightbox__fermer { top: .5rem; right: 1rem; font-size: 2.6rem; }
.lightbox__precedent { left: .5rem; top: 50%; transform: translateY(-50%); font-size: 4rem; }
.lightbox__suivant  { right: .5rem; top: 50%; transform: translateY(-50%); font-size: 4rem; }

.lightbox__compteur {
    position: absolute; bottom: 1.2rem; left: 50%; transform: translateX(-50%);
    margin: 0; color: var(--texte-tres-doux); font-size: .75rem; letter-spacing: .2em;
}

@media (max-width: 640px) {
    .lightbox__precedent, .lightbox__suivant { font-size: 2.4rem; padding: .5rem; }
}

/* Diaporama (galerie client) : rien que l'image, en vrai plein écran
   (Fullscreen API, posé par galerie-client.js) — ni flèches, ni compteur,
   ni légende, ni notes/commentaire. Seule la croix reste, réduite, pour
   fermer sans imposer sa présence. */
.lightbox--diaporama .lightbox__precedent,
.lightbox--diaporama .lightbox__suivant,
.lightbox--diaporama .lightbox__compteur,
.lightbox--diaporama .lightbox__legende,
.lightbox--diaporama .lightbox__retours {
    display: none;
}

/* Conteneur à taille FIXE (100vw/100vh) plutôt qu'ajusté à chaque photo
   comme en consultation normale : c'est ce qui permet à deux calques de se
   superposer exactement au même endroit pour un vrai fondu enchaîné — deux
   images de tailles différentes ne pourraient jamais se recouvrir proprement
   pendant la transition. */
.lightbox--diaporama .lightbox__cadre {
    position: fixed;
    inset: 0;
    width: 100vw;
    height: 100vh;
    max-width: none;
    max-height: none;
    display: block;
}

.lightbox--diaporama .lightbox__image,
.lightbox--diaporama .lightbox__image-ombre {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    max-width: none;
    max-height: none;
    object-fit: contain;
    opacity: 0;
    /* Remplace la transition de la consultation normale (260ms, pensée pour
       un allègement rapide entre deux clics) : un fondu enchaîné se voit,
       une disparition-réapparition de 260ms sur une photo plein écran
       ressemblerait à un clignotement plutôt qu'à un vrai fondu. */
    transition: opacity var(--duree-fondu-diaporama, 1200ms) ease;
    transform: none;
}

.lightbox--diaporama .lightbox__image.est-visible,
.lightbox--diaporama .lightbox__image-ombre.est-visible {
    opacity: 1;
}

.lightbox--diaporama .lightbox__fermer {
    font-size: 1.4rem;
    opacity: .45;
    z-index: 1;
}
.lightbox--diaporama .lightbox__fermer:hover { opacity: 1; }

/* Zoom très lent pendant l'affichage de chaque photo, pour donner une
   dynamique au diaporama plutôt qu'un enchaînement de plans fixes — relancé
   à chaque nouvelle photo par galerie-client.js (retrait puis remise de
   l'une de ces deux classes, sur le calque qui vient d'apparaître).

   Sa durée (`--duree-zoom-diaporama`) dépasse volontairement celle de
   l'affichage : le zoom continue sa course PENDANT le fondu vers la photo
   suivante, plutôt que de s'arrêter juste avant — c'est ce chevauchement qui
   fait que le fondu se voit en plein mouvement.

   Zoom avant et zoom arrière alternent d'une photo à l'autre (voir
   `relancerZoom()`) : la photo qui arrive reprend l'échelle exacte où la
   précédente vient de s'arrêter, sans à-coup au changement. */
@keyframes diaporama-zoom-avant {
    from { transform: scale(1); }
    to   { transform: scale(1.08); }
}
@keyframes diaporama-zoom-arriere {
    from { transform: scale(1.08); }
    to   { transform: scale(1); }
}

.lightbox--diaporama .lightbox__image.est-en-zoom-avant,
.lightbox--diaporama .lightbox__image-ombre.est-en-zoom-avant {
    animation: diaporama-zoom-avant var(--duree-zoom-diaporama, 6200ms) linear forwards;
}
.lightbox--diaporama .lightbox__image.est-en-zoom-arriere,
.lightbox--diaporama .lightbox__image-ombre.est-en-zoom-arriere {
    animation: diaporama-zoom-arriere var(--duree-zoom-diaporama, 6200ms) linear forwards;
}

/* Hors diaporama : le second calque n'a aucune raison d'exister. */
.lightbox__image-ombre { display: none; }
.lightbox--diaporama .lightbox__image-ombre { display: block; }

/* =====================================================================
   Sommaire du portfolio
   ===================================================================== */

.galerie-entete {
    text-align: center;
    padding: 3rem var(--gouttiere) 2rem;
}
.galerie-entete__description {
    max-width: 42rem; margin: 0 auto;
    color: var(--texte-doux);
}

/* Les albums adoptent la même mosaïque que les photos : chaque couverture
   garde son rapport d'aspect, ce qui évite le rythme mécanique d'une grille
   à cases identiques. */
.mosaique-albums {
    column-count: 3;
    column-gap: 2px;
    padding: 0 0 3rem;
}

@media (max-width: 1100px) { .mosaique-albums { column-count: 2; } }
@media (max-width: 680px)  { .mosaique-albums { column-count: 1; } }

.album {
    position: relative;
    display: block;
    overflow: hidden;
    margin-bottom: 2px;
    break-inside: avoid;
    page-break-inside: avoid;
}
.album:hover { color: inherit; }

.album__image {
    width: 100%; height: auto;
    transition: transform 500ms ease, opacity 600ms ease;
}
.album:hover .album__image { transform: scale(1.04); opacity: .72; }

.album__vide { display: block; width: 100%; aspect-ratio: 4 / 3; background: var(--fond-eleve); }

.album__legende {
    position: absolute; inset: auto 0 0 0;
    padding: 3rem 1.2rem 1.1rem;
    background: linear-gradient(to top, rgba(0, 0, 0, .9), transparent);
    display: flex; flex-direction: column; gap: .15rem;
}
.album__nom {
    font-weight: var(--graisse-menu);
    font-size: .95rem; letter-spacing: .14em; text-transform: uppercase;
}
.album__compte { font-size: .72rem; letter-spacing: .1em; color: var(--texte-doux); }

/* ---------------------------------------------------------------------
   Apparition en fondu

   Les images arrivent en fondu une fois chargées, plutôt que de surgir
   brutalement. L'état initial est posé par le CSS, la classe `est-chargee`
   par le JavaScript au chargement de chaque image.

   Le repli `no-js` est indispensable : sans lui, un visiteur dont le
   JavaScript ne s'exécute pas ne verrait jamais aucune image.
   --------------------------------------------------------------------- */

.image-fondu {
    opacity: 0;
    transform: scale(1.02);
    transition: opacity 700ms ease, transform 700ms ease;

    /* Filet de sécurité : si le JavaScript n'ajoute jamais `est-chargee`
       — erreur d'exécution, navigateur exotique —, l'image apparaît malgré
       tout au bout de quelques secondes. Sur un site de photographe, des
       images définitivement invisibles seraient bien pires qu'un fondu
       imparfait. */
    animation: revelation-secours 400ms ease 5s forwards;
}

@keyframes revelation-secours {
    to { opacity: 1; transform: none; }
}

.image-fondu.est-chargee,
html.sans-js .image-fondu {
    opacity: 1;
    transform: none;
    /* Le fondu normal a eu lieu : le filet n'a plus lieu d'être. */
    animation: none;
}

/* Le survol d'un album ne doit pas être contrarié par la transformation
   résiduelle du fondu. */
.album:hover .album__image.est-chargee { transform: scale(1.04); }

/* =====================================================================
   Pages de contenu
   ===================================================================== */

.page {
    max-width: var(--largeur-page);
    margin: 0 auto;
    padding: 3.5rem var(--gouttiere) 4rem;
}

.page__entete { text-align: center; margin-bottom: 2.5rem; }

.page__chapeau {
    font-size: 1.1rem; color: var(--texte);
    max-width: 44rem; margin: 0 auto 2rem;
    text-align: center;
}

.page__corps p { color: var(--texte-doux); max-width: 44rem; margin: 0 auto 1.2rem; }
.page__corps strong { color: var(--texte); font-weight: 500; }

.page__section { margin-top: 3rem; }
.page__section h2 { text-align: center; color: var(--texte); }

.page__appel { text-align: center; margin-top: 3rem; }

.bouton {
    display: inline-block;
    padding: .8rem 2rem;
    border: 1px solid var(--accent);
    color: var(--accent);
    font-size: .78rem; letter-spacing: .2em; text-transform: uppercase;
    background: none; cursor: pointer; font-family: inherit;
    transition: background var(--transition), color var(--transition);
}
.bouton:hover { background: var(--accent); color: var(--noir); }
.bouton--discret { border-color: var(--bordure); color: var(--texte-doux); }
.bouton--discret:hover { border-color: var(--accent); background: none; color: var(--accent); }

/* Formules ---------------------------------------------------------- */

/* Deux offres seulement : elles occupent chacune la moitié de la largeur et
   sont traitées comme des propositions, non comme des lignes de tarif. */
.formules {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(19rem, 1fr));
    gap: 1px;
    background: var(--bordure);
    border: 1px solid var(--bordure);
    margin: 2.5rem 0;
}

.formule {
    background: var(--fond);
    padding: 2.4rem 2rem;
    display: flex; flex-direction: column; gap: .6rem;
}

/* La formule principale se distingue par un liseré haut et un fond légèrement
   relevé, sans rompre l'unité de la page. */
.formule--principale {
    background: var(--fond-eleve);
    border-top: 2px solid var(--accent);
}

.formule__etiquette {
    margin: 0;
    font-size: .7rem; letter-spacing: .24em; text-transform: uppercase;
    color: var(--texte-tres-doux);
}

.formule__nom { color: var(--texte); margin: 0 0 .4rem; font-size: 1.6rem; }
.formule--principale .formule__nom { color: var(--accent); }

.formule__prix { margin: 0 0 .2rem; display: flex; align-items: baseline; gap: .4rem; }
.formule__montant { font-size: 2.4rem; font-weight: var(--graisse-titre); color: var(--texte); }
.formule__mention { font-size: .8rem; letter-spacing: .16em; color: var(--texte-doux); }

.formule__duree {
    margin: 0 0 1rem;
    font-size: .85rem; letter-spacing: .1em;
    color: var(--texte-doux);
}

.formule__points { list-style: none; padding: 0; margin: 0; color: var(--texte-doux); }
.formule__points li { padding-left: 1.4rem; position: relative; margin-bottom: .5rem; font-size: .95rem; }
.formule__points li::before { content: "—"; position: absolute; left: 0; color: var(--accent); }

.formule__action { margin: auto 0 0; padding-top: 1.6rem; }

/* Types de prise de vue --------------------------------------------- */

.types-shooting {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
    gap: 1.5rem;
    margin: 2rem 0;
}

.type-shooting { margin: 0; }
.type-shooting__image { width: 100%; height: auto; }
.type-shooting__nom {
    margin: .9rem 0 .3rem;
    font-size: .95rem; letter-spacing: .14em; text-transform: uppercase;
    color: var(--accent);
}
.type-shooting__texte { color: var(--texte-doux); font-size: .9rem; margin: 0; }

/* Supports de diffusion --------------------------------------------- */

.supports {
    list-style: none; padding: 0;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
    gap: 1px;
    background: var(--bordure);
    border: 1px solid var(--bordure);
    margin: 2rem 0;
    max-width: none;
}

.support { background: var(--fond); padding: 1.6rem 1.3rem; }
.support__nom {
    margin: 0 0 .5rem;
    font-size: .85rem; letter-spacing: .16em; text-transform: uppercase;
    color: var(--accent);
}
.support__texte { margin: 0; color: var(--texte-doux); font-size: .9rem; }

/* Démarche créative -------------------------------------------------- */

.animations {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr));
    gap: 1.5rem;
    margin: 2rem 0;
}
.animation { margin: 0; }
.animation img { width: 100%; height: auto; }

/* Comparatifs avant / après ------------------------------------------

   L'image retouchée est posée en premier, l'originale par-dessus. C'est cette
   dernière qui s'efface au survol : le résultat final reste donc affiché par
   défaut, et l'on découvre le fichier de départ.

   `tabindex` sur la figure rend la bascule accessible au clavier, et la classe
   posée en JavaScript la rend utilisable au doigt — le survol n'existant pas
   sur un écran tactile. */

.comparatifs {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr));
    gap: 1.5rem;
    margin: 2rem 0;
}

.comparatif {
    position: relative;
    margin: 0;
    overflow: hidden;
    cursor: help;
}

.comparatif img { width: 100%; height: auto; display: block; }

.comparatif__resultat {
    position: absolute; inset: 0;
    width: 100%; height: 100%;
    object-fit: cover;
    transition: opacity 350ms ease;
}

.comparatif:hover .comparatif__resultat,
.comparatif:focus-visible .comparatif__resultat,
.comparatif.montre-origine .comparatif__resultat { opacity: 0; }

.comparatif__etiquette {
    position: absolute; left: 0; bottom: 0;
    padding: .45rem .9rem;
    background: rgba(0, 0, 0, .75);
    font-size: .7rem; letter-spacing: .16em; text-transform: uppercase;
    color: var(--texte-doux);
}

.comparatif__etat::before { content: "Après retouche"; }
.comparatif:hover .comparatif__etat::before,
.comparatif:focus-visible .comparatif__etat::before,
.comparatif.montre-origine .comparatif__etat::before { content: "Avant retouche"; color: var(--accent); }

/* Le texte servant de repli sans CSS est masqué une fois la feuille chargée. */
.comparatif__etat { font-size: 0; }
.comparatif__etat::before { font-size: .7rem; }

/* Foire aux questions ------------------------------------------------ */

.faq { max-width: 44rem; margin: 2rem auto 0; }

.faq__item { border-bottom: 1px solid var(--bordure); }

.faq__question {
    cursor: pointer;
    padding: 1rem 2rem 1rem 0;
    position: relative;
    color: var(--texte);
    font-size: 1rem; letter-spacing: .04em;
    list-style: none;
}
.faq__question::-webkit-details-marker { display: none; }

.faq__question::after {
    content: "+";
    position: absolute; right: .3rem; top: 50%;
    transform: translateY(-50%);
    color: var(--accent); font-size: 1.3rem; line-height: 1;
    transition: transform var(--transition);
}
.faq__item[open] .faq__question::after { content: "−"; }

.faq__question:hover { color: var(--accent); }

.faq__reponse {
    margin: 0 0 1.2rem;
    color: var(--texte-doux);
    font-size: .95rem;
}

/* Listes ------------------------------------------------------------ */

.liste-check, .liste-sujets, .references {
    list-style: none; padding: 0;
    max-width: 44rem; margin: 0 auto;
    color: var(--texte-doux);
}
.liste-check li { padding-left: 1.6rem; position: relative; margin-bottom: .5rem; }
.liste-check li::before { content: "—"; position: absolute; left: 0; color: var(--accent); }

.liste-sujets { display: grid; grid-template-columns: repeat(auto-fill, minmax(13rem, 1fr)); gap: .4rem 1.5rem; }
.liste-sujets li { padding-left: 1.2rem; position: relative; }
.liste-sujets li::before { content: "·"; position: absolute; left: 0; color: var(--accent); font-size: 1.4em; line-height: 1; }

.fiche { max-width: 34rem; margin: 0 auto; }
.fiche__ligne { display: flex; justify-content: space-between; gap: 1rem; padding: .55rem 0; border-bottom: 1px solid var(--bordure); }
.fiche__ligne dt { color: var(--texte-doux); font-size: .85rem; letter-spacing: .08em; }
.fiche__ligne dd { margin: 0; text-align: right; }
.fiche__note { font-size: .82rem; color: var(--texte-tres-doux); text-align: center; margin-top: 1.2rem; }

/* Logos clients.

   Fond clair, à rebours du reste du site : un logo est presque toujours dessiné
   pour du papier blanc, et beaucoup sont en noir. Sur le fond sombre des pages,
   ils seraient tout simplement invisibles. */
.references {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
    gap: 1px;
    background: var(--bordure);
    border: 1px solid var(--bordure);
    max-width: none;
    margin: 2.5rem 0;
}

.reference {
    background: #f4f4f4;
    aspect-ratio: 3 / 2;
    padding: 1.6rem;
    display: flex; align-items: center; justify-content: center;
}

.reference__logo {
    max-width: 100%; max-height: 100%;
    width: auto; height: auto;
    object-fit: contain;
    mix-blend-mode: multiply;
}

.reference__nom { font-size: 1.1rem; letter-spacing: .16em; text-transform: uppercase; color: var(--texte-doux); }

/* =====================================================================
   Formulaire de contact
   ===================================================================== */

.page--contact { max-width: 48rem; }

.formulaire { display: flex; flex-direction: column; gap: 1.3rem; }
.formulaire__ligne { display: grid; grid-template-columns: 1fr 1fr; gap: 1.3rem; }
@media (max-width: 600px) { .formulaire__ligne { grid-template-columns: 1fr; } }

.champ { margin: 0; display: flex; flex-direction: column; gap: .4rem; }
.champ label, .champ legend {
    font-size: .75rem; letter-spacing: .16em; text-transform: uppercase; color: var(--texte-doux);
}
.champ__requis { color: var(--accent); }

.champ input, .champ textarea {
    background: var(--fond-eleve);
    border: 1px solid var(--bordure);
    color: var(--texte);
    font: inherit; font-size: .95rem;
    padding: .7rem .85rem;
    transition: border-color var(--transition);
}
.champ input:focus, .champ textarea:focus { outline: none; border-color: var(--accent); }
.champ textarea { resize: vertical; }

.champ--choix { border: 0; padding: 0; }
.champ--choix legend { padding: 0 0 .5rem; }
.choix { display: inline-flex; align-items: center; gap: .45rem; margin-right: 1.4rem; text-transform: none; letter-spacing: normal; }
.choix span { font-size: .9rem; color: var(--texte); }

.champ__erreur { color: #e07a5f; font-size: .82rem; margin: 0; }

/* Le piège à robots doit rester invisible sans être `display:none`, que
   certains automates savent détecter. */
.piege { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

.formulaire__actions { margin: .5rem 0 0; }
.formulaire__mention { font-size: .78rem; color: var(--texte-tres-doux); margin: 0; }

.alerte { padding: 1rem 1.2rem; border: 1px solid var(--bordure); margin-bottom: 2rem; }
.alerte p { margin: 0; }
.alerte--succes { border-color: var(--accent); color: var(--texte); }
.alerte--erreur { border-color: #e07a5f; color: #e0a08f; }

.contact-direct { margin-top: 3.5rem; padding-top: 2rem; border-top: 1px solid var(--bordure); text-align: center; }
.contact-direct a { color: var(--accent); }
.contact-direct__zone { color: var(--texte-tres-doux); font-size: .85rem; }

/* =====================================================================
   États vides et pied de page
   ===================================================================== */

.message-vide { text-align: center; padding: 6rem var(--gouttiere); color: var(--texte-doux); }
.message-vide__aide { font-size: .88rem; color: var(--texte-tres-doux); }
.message-vide a { color: var(--accent); }

.pied {
    border-top: 1px solid var(--bordure);
    padding: 2.5rem var(--gouttiere);
    text-align: center;
}
.pied__mention { margin: 0 0 .5rem; font-size: .8rem; letter-spacing: .1em; color: var(--texte-doux); }
.pied__liens { margin: 0; font-size: .82rem; color: var(--texte-tres-doux); }
.pied__liens a { color: var(--texte-tres-doux); }
.pied__liens a:hover { color: var(--accent); }

/* =====================================================================
   Galeries client

   Pas de menu Portfolio ici : un en-tête réduit au logo, un titre qui porte
   le nom du client plutôt que celui d'une catégorie, et un lien de
   téléchargement ajouté sous chaque vignette de la mosaïque partagée avec
   le portfolio public.
   ===================================================================== */

.entete-client {
    text-align: center;
    padding: 2rem var(--gouttiere) 1rem;
}
.entete-client__marque img { width: 180px; height: auto; }

.page--code-client { max-width: 30rem; }

.champ__code {
    text-align: center;
    letter-spacing: .3em;
    text-transform: uppercase;
    font-size: 1.2rem;
}

.page__entete--galerie-client {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 1.5rem;
    text-align: left;
}
/* Sans `flex`/`min-width: 0` ici, ce conteneur ne prend que la largeur de
   son contenu (comportement par défaut d'un enfant flex) : le chapeau ne
   pouvait alors jamais utiliser l'espace libéré par la mise en page
   pleine largeur de la galerie client, quelle que soit sa propre largeur
   maximale. */
.page__entete--galerie-client > div { flex: 1 1 auto; min-width: 0; }
.page__entete--galerie-client .page__chapeau {
    text-align: left;
    margin: .4rem 0 0;
    max-width: none;
}

.galerie-client__visiteur {
    flex-shrink: 0;
    font-size: .82rem;
    color: var(--texte-tres-doux);
    margin: 0;
}
.galerie-client__visiteur a { color: var(--texte-doux); }
.galerie-client__visiteur a:hover { color: var(--accent); }

@media (max-width: 700px) {
    .page__entete--galerie-client { flex-direction: column; align-items: flex-start; }
}

/* Le cœur se pose sur la vignette elle-même : la figure devient le point
   d'ancrage de sa position absolue. `.mosaique__declencheur` ne convient pas
   — son `overflow: hidden` le découperait s'il y était imbriqué, d'où un
   bouton frère plutôt qu'enfant (deux boutons ne s'imbriquent pas en HTML
   valide de toute façon). */
.mosaique__element { position: relative; }

.mosaique__coeur,
.mosaique__telecharger {
    position: absolute;
    right: 10px;
    width: 34px;
    height: 34px;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 0;
    border-radius: 50%;
    background: rgba(10, 10, 10, .55);
    color: #fff;
    cursor: pointer;
    transition: background var(--transition), color var(--transition), transform 150ms ease;
}
.mosaique__coeur svg,
.mosaique__telecharger svg { width: 18px; height: 18px; }
.mosaique__coeur:hover,
.mosaique__telecharger:hover { transform: scale(1.08); }
.mosaique__coeur:focus-visible,
.mosaique__telecharger:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.mosaique__coeur { top: 10px; }
.mosaique__telecharger:hover { color: var(--accent); }

.mosaique__coeur.est-choisie {
    background: var(--accent);
    color: var(--noir);
}
.mosaique__coeur.est-choisie svg { fill: currentColor; }

/* Juste sous le cœur : 10px de marge, 34px de bouton, 10px d'écart. */
.mosaique__telecharger { top: 54px; }

/* Galerie client : la page n'est plus bridée à --largeur-page (68rem) comme
   les pages de contenu ordinaires — sur un grand moniteur, la mosaïque doit
   occuper toute la largeur disponible, pas rester à l'étroit dans un cadre
   pensé pour du texte. */
.page--galerie-client { max-width: none; }

/* Grille plutôt que colonnes CSS — changement de fond, constaté nécessaire
   le 23/09/2026. Les colonnes CSS (.mosaique, plus haut) remplissent la
   PREMIÈRE colonne de haut en bas avant de passer à la suivante : la photo
   n°2 atterrit sous la n°1, pas à sa droite. Pour une chronologie de
   mariage, le client doit pouvoir la lire comme un texte — gauche à droite,
   puis ligne suivante — quelle que soit la largeur de fenêtre. Une grille en
   lignes fait exactement ça (`grid-auto-flow` vaut `row` par défaut) ;
   contrepartie assumée : chaque vignette est recadrée en carré
   (`aspect-ratio` + `object-fit: cover` ci-dessous) au lieu de garder sa
   hauteur naturelle, seul moyen d'aligner proprement des rangées quand
   l'ordre de lecture est imposé plutôt que déduit du remplissage. */
.page--galerie-client .mosaique {
    display: grid;
    grid-template-columns: repeat(6, 1fr);
    gap: var(--espacement-photos);
}
@media (max-width: 1899px) { .page--galerie-client .mosaique { grid-template-columns: repeat(5, 1fr); } }
@media (max-width: 1499px) { .page--galerie-client .mosaique { grid-template-columns: repeat(4, 1fr); } }
@media (max-width: 1099px) { .page--galerie-client .mosaique { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 799px)  { .page--galerie-client .mosaique { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 479px)  { .page--galerie-client .mosaique { grid-template-columns: repeat(1, 1fr); } }

.page--galerie-client .mosaique__element { margin: 0; break-inside: auto; }
.page--galerie-client .mosaique__declencheur { aspect-ratio: 1 / 1; }
.page--galerie-client .mosaique__declencheur img { width: 100%; height: 100%; object-fit: cover; }

/* Photo mise en avant (libellé violet dans Lightroom) : bannière pleine
   largeur juste sous le nom du client, avant le bandeau d'options. */
.galerie-client__mise-en-avant {
    margin: 0 0 var(--espacement-photos);
}
.galerie-client__mise-en-avant img {
    display: block;
    width: 100%;
    max-height: 70vh;
    object-fit: cover;
}

.galerie-client__barre {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .8rem 1.5rem;
    padding: 1rem 1.2rem;
    margin-bottom: var(--espacement-photos);
    background: var(--fond-eleve);
    border: 1px solid var(--bordure);
}

.galerie-client__compte {
    margin: 0;
    font-size: .85rem;
    color: var(--texte-doux);
    flex-shrink: 0;
}

/* Un seul groupe qui pousse à droite, plutôt qu'un `margin-left: auto` posé
   sur chaque élément pris séparément : sans ça, sur une largeur moyenne, le
   groupe se scinde et un seul bouton finit isolé sur sa propre ligne pendant
   que les autres restent groupés au-dessus — l'effet « brouillon » constaté
   le 23/09/2026. Le groupe entier passe à la ligne d'un bloc s'il le faut,
   jamais élément par élément. */
.galerie-client__actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .6rem;
    margin-left: auto;
}
@media (max-width: 700px) { .galerie-client__actions { margin-left: 0; } }

/* Modale d'accueil : explique le fonctionnement de la galerie, à chaque
   chargement (voir galerie-client.js). */
.modale-accueil {
    position: fixed; inset: 0; z-index: 400;
    background: rgba(0, 0, 0, .75);
    display: flex; align-items: center; justify-content: center;
    padding: var(--gouttiere);
}
.modale-accueil[hidden] { display: none; }

.modale-accueil__panneau {
    position: relative;
    width: 100%;
    max-width: 32rem;
    max-height: 85vh;
    overflow-y: auto;
    background: var(--fond-eleve);
    border: 1px solid var(--bordure);
    padding: 2rem;
}

.modale-accueil__panneau h2 { margin: 0 0 1rem; }
.modale-accueil__panneau p { color: var(--texte-doux); margin: 0 0 1.2rem; line-height: 1.5; }

.modale-accueil__liste {
    margin: 0 0 1.3rem;
    padding-left: 1.2rem;
    color: var(--texte-doux);
    line-height: 1.5;
}
.modale-accueil__liste li { margin-bottom: .7rem; }
.modale-accueil__liste li:last-child { margin-bottom: 0; }

.modale-accueil__signature { font-size: .85rem; color: var(--texte-tres-doux); }

.modale-accueil__panneau p.modale-accueil__pied { margin: 1.4rem 0 0; text-align: center; }

.modale-accueil__fermer {
    position: absolute; top: .7rem; right: .9rem;
    background: none; border: 0; color: var(--texte-doux);
    font-size: 1.8rem; line-height: 1; cursor: pointer;
    transition: color var(--transition);
}
.modale-accueil__fermer:hover { color: var(--texte); }

.galerie-client__tailles {
    flex-basis: 100%;
    margin: 0;
    font-size: .78rem;
    color: var(--texte-tres-doux);
}

/* Menu de téléchargement à plusieurs parties : un simple <details>, replié
   par défaut, pour ne PAS étaler une rangée de boutons « Partie 1, 2, 3… »
   dans la barre — le cas courant (une seule partie) reste un bouton ordinaire
   posé directement par $controleTelechargement, sans passer par ici. */
.menu-telechargement { position: relative; }

.menu-telechargement > summary {
    list-style: none;
    cursor: pointer;
}
.menu-telechargement > summary::-webkit-details-marker { display: none; }
.menu-telechargement > summary::marker { content: ''; }

.menu-telechargement__panneau {
    position: absolute;
    top: calc(100% + .5rem);
    left: 0;
    z-index: 60;
    min-width: 16rem;
    max-width: calc(100vw - 2 * var(--gouttiere));
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
    padding: 1rem;
    background: var(--fond-eleve);
    border: 1px solid var(--bordure);
}

.menu-telechargement__note {
    flex-basis: 100%;
    margin: .3rem 0 0;
    font-size: .78rem;
    color: var(--texte-tres-doux);
}

/* Repasse en flux normal sous le bouton plutôt qu'en survol flottant : sur
   un écran étroit, un panneau qui s'étend hors de l'écran serait pire que le
   petit espace vertical qu'il prend en s'insérant simplement dans la page. */
@media (max-width: 480px) {
    .menu-telechargement__panneau { position: static; margin-top: .5rem; max-width: none; }
}

/* Note et commentaire dans la visionneuse : un unique bloc, puisque la
   visionneuse elle-même est unique et partagée par toutes les photos —
   galerie-client.js le repeuple à chaque changement de photo. */
.lightbox__retours {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .6rem;
    width: min(92vw, 34rem);
}

.lightbox__notes { display: flex; gap: .3rem; }

.lightbox__etoile {
    background: none;
    border: 0;
    padding: .2rem;
    color: var(--texte-tres-doux);
    cursor: pointer;
    transition: color var(--transition), transform 120ms ease;
}
.lightbox__etoile svg { width: 22px; height: 22px; }
/* Remplies via galerie-client.js plutôt qu'un sélecteur CSS de frères
   précédents (qui n'existe pas en CSS standard — seul `~` vers les frères
   SUIVANTS existe, inutilisable ici sans inverser l'ordre du DOM) : la note
   déjà enregistrée (`est-notee`) et la prévisualisation au survol
   (`est-survolee`) sont deux classes pilotées en JavaScript, étoile par
   étoile jusqu'au rang concerné. */
.lightbox__etoile.est-notee,
.lightbox__etoile.est-survolee { color: var(--accent); }
.lightbox__etoile.est-notee svg,
.lightbox__etoile.est-survolee svg { fill: currentColor; }
.lightbox__etoile:hover { transform: scale(1.12); }
.lightbox__etoile:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.lightbox__champ-commentaire { display: block; width: 100%; }
.lightbox__commentaire {
    display: block;
    width: 100%;
    min-height: 6rem;
    background: rgba(255, 255, 255, .06);
    border: 1px solid var(--bordure);
    color: var(--texte);
    font: inherit;
    font-size: .85rem;
    padding: .6rem .75rem;
    resize: vertical;
    transition: border-color var(--transition);
}
.lightbox__commentaire::placeholder { color: var(--texte-tres-doux); }
.lightbox__commentaire:focus { outline: none; border-color: var(--accent); }

.galerie-client__commentaire-general {
    max-width: var(--largeur-page);
    margin: 2.5rem auto 0;
    padding: 0 var(--gouttiere);
}
.galerie-client__commentaire-general label {
    display: block;
    margin-bottom: .5rem;
    font-size: .8rem;
    letter-spacing: .05em;
    color: var(--texte-doux);
}
.galerie-client__commentaire-general textarea {
    display: block;
    width: 100%;
    background: var(--fond-eleve);
    border: 1px solid var(--bordure);
    color: var(--texte);
    font: inherit;
    font-size: .95rem;
    padding: .7rem .85rem;
    resize: vertical;
    transition: border-color var(--transition);
}
.galerie-client__commentaire-general textarea:focus { outline: none; border-color: var(--accent); }
.galerie-client__commentaire-etat {
    margin: .4rem 0 0;
    font-size: .75rem;
    color: var(--texte-tres-doux);
    min-height: 1.1em;
}

/* =====================================================================
   Navigation repliée sur petit écran
   ===================================================================== */

@media (max-width: 900px) {
    .entete__barre { grid-template-columns: 1fr auto; grid-template-areas: "logo reseaux" "titre titre"; }
    .logo { grid-area: logo; }
    .reseaux { grid-area: reseaux; }
    .entete__titre { grid-area: titre; text-align: center; letter-spacing: .3em; }

    .navigation__bascule { display: flex; justify-content: center; border-top: 1px solid var(--bordure); }

    .menu { display: none; flex-direction: column; align-items: center; gap: 0; padding-bottom: 0; }
    .menu.est-ouvert { display: flex; }
    .menu > li { width: 100%; text-align: center; border-top: 1px solid var(--bordure); }
    .menu > li > a { display: flex; justify-content: center; padding: .85rem 0; width: 100%; }

    .sous-menu {
        position: static; transform: none; opacity: 1; visibility: visible;
        display: none; border: 0; border-top: 1px solid var(--bordure);
        background: var(--fond-eleve); min-width: 0;
    }
    .sous-menu.est-ouvert { display: block; }
    .menu__deroulant:hover .sous-menu { display: none; }
    .menu__deroulant:hover .sous-menu.est-ouvert { display: block; }
}

/* Respect de la préférence système : pas d'animation pour qui n'en veut pas. */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after { transition-duration: .01ms !important; animation-duration: .01ms !important; }
}
