/* =============================================================================
   Casa Paradisu — Galerie
   -----------------------------------------------------------------------------
   Chargée par `galerie.html` seule. La visionneuse, elle, sert aussi l'accueil
   et « La villa » : ses styles restent dans `components.css` et `premium.css`.

   Le principe de la page : les photographies sont le sujet, tout le reste est
   du mobilier. Aucune carte, aucune bordure, aucune ombre — des images, des
   intervalles, et des mouvements qui expliquent où l'on est.

   La grille est posée par le script (`assets/js/gallery.js`) : les vignettes
   sont positionnées en absolu, colonne par colonne. C'est le seul moyen de
   tenir les deux exigences à la fois — garder le rapport naturel de chaque
   image, portrait comme paysage, ET pouvoir animer leur déplacement quand on
   change de catégorie. Une grille CSS impose un rapport ; des colonnes CSS
   refusent qu'on anime. Le repli sans JavaScript reste un simple flux.
   ========================================================================== */

/* --- Rythme de la page ------------------------------------------------------
   Un peu plus d'air que le reste du site : c'est une galerie, l'intervalle
   fait partie du propos. */
body[data-page="galerie"] .page-head { padding-bottom: clamp(26px, 3vw, 52px); }
body[data-page="galerie"] .page-head .lead { max-width: 52ch; }

/* --- Tête de galerie : filtres à gauche, affichage à droite ----------------- */
.gal-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--sp-5);
  margin-bottom: clamp(22px, 2.4vw, 40px);
}
.gal-head .filter-bar { flex: 1 1 auto; min-width: 0; border-bottom: 0; }

/* La barre suit la lecture. Elle reste basse — une ligne — et son fond reprend
   l'ivoire de la page pour que les vignettes passent dessous sans transparaître.
   `--header-h` la pose juste sous le bandeau du site. */
.gal-sticky {
  position: sticky;
  top: var(--header-h);
  z-index: 20;
  background-color: var(--bg-page);
  border-bottom: 1px solid var(--rule-soft);
  padding-top: var(--sp-3);
  margin-bottom: clamp(22px, 2.4vw, 40px);
}
/* Le fond doit déborder de la gouttière, sinon les vignettes réapparaissent
   dans les marges en passant dessous. */
.gal-sticky::before {
  content: "";
  position: absolute;
  inset: 0 calc(var(--gutter) * -1);
  z-index: -1;
  background-color: var(--bg-page);
  border-bottom: 1px solid var(--rule-soft);
}
.gal-sticky .gal-head { margin-bottom: 0; }

/* --- La grille ---------------------------------------------------------------
   Hauteur posée par le script ; les vignettes y sont placées en absolu. Sans
   JavaScript, `is-laid` n'est jamais posée et tout retombe en flux normal. */
.gal-grid { position: relative; }
.gal-grid.is-laid > .gal-item {
  position: absolute;
  top: 0;
  left: 0;
  margin: 0;
}

.gal-item {
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  border-radius: 0;
  background-color: var(--bg-sunken);
  overflow: hidden;
  cursor: pointer;
  /* Le déplacement d'une vignette d'une catégorie à l'autre est animé par le
     script, en transform : la propriété est déclarée ici pour que la
     transition existe, et le script la coupe le temps de mesurer. */
  transition: transform 700ms var(--ease-out), opacity 500ms var(--ease);
  will-change: transform;
}
/* Tant que la source n'est pas posée, l'image est vide : sans cela, son texte
   alternatif s'afficherait dans le cadre. */
.gal-item img:not([src]) { opacity: 0; }
.gal-item img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Détente longue et très amortie : la photographie s'approche, elle ne
     bondit pas. Même valeur de zoom que partout ailleurs sur le site. */
  transition: transform 900ms cubic-bezier(0.22, 1, 0.36, 1), opacity 700ms var(--ease);
}

/* Apparition au défilement : opacité et huit pixels de montée, pas plus. Le
   décalage entre voisines est posé par le script, en fonction du rang réel
   dans la colonne — une cascade subie, pas programmée. */
.gal-item { opacity: 0; }
.gal-item.is-in { opacity: 1; }
.gal-item.is-in img { transform: none; }
.gal-item:not(.is-in) img { transform: translateY(22px) scale(1.03); }

@media (hover: hover) and (pointer: fine) {
  .gal-item:hover img { transform: scale(var(--zoom-hover)); }
}
.gal-item:focus-visible { outline: 2px solid var(--focus); outline-offset: 4px; }

/* La vignette d'origine s'efface le temps que la grande image la remplace :
   sans cela on verrait la petite sous la grande pendant l'agrandissement. */
.gal-item.is-lifted { opacity: 0; transition: opacity 120ms linear; }

/* --- Curseur « Voir » --------------------------------------------------------
   Un disque qui suit la main, et seulement au-dessus des photographies. Il
   remplace l'icône et le voile noir qu'on aurait sinon posés dessus. */
.gal-cursor {
  position: fixed;
  top: 0; left: 0;
  z-index: 30;
  width: 76px; height: 76px;
  margin: -38px 0 0 -38px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background-color: rgba(23, 26, 22, 0.82);
  color: var(--c-ivory);
  font-size: var(--fs-eyebrow);
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-eyebrow);
  text-transform: uppercase;
  pointer-events: none;
  opacity: 0;
  transform: scale(0.6);
  transition: opacity 260ms var(--ease), transform 420ms var(--ease-out);
}
.gal-cursor.is-on { opacity: 1; transform: scale(1); }
@media (hover: none), (pointer: coarse), (prefers-reduced-motion: reduce) {
  .gal-cursor { display: none; }
}

/* --- Vue flottante -----------------------------------------------------------
   Le mode par défaut. Sept photographies posées à des places choisies, de
   tailles inégales, qui se recouvrent un peu et dérivent lentement. Ce n'est
   pas une grille désordonnée : les emplacements sont fixes et composés, seule
   l'amplitude de la dérive est propre à chacune.

   Deux transformations empilées, et il le faut : l'emplacement est porté par
   le bouton, la dérive par son enfant. Sur un seul élément, la dérive écraserait
   le rapprochement au clic. */
.gal-float-stage {
  position: relative;
  display: none;
  height: calc(100svh - var(--header-h) - 124px);
  min-height: 420px;
}
.is-flottant .gal-float-stage { display: block; }
.is-flottant .gal-grid { display: none; }

.gal-float {
  position: absolute;
  left: var(--x);
  top: var(--y);
  z-index: var(--z, 1);
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  overflow: visible;
  /* `--tx`, `--ty` et `--s` sont posés par le script au clic : la
     photographie choisie vient au centre, les autres s'écartent dans l'axe qui
     les éloigne déjà. */
  transform: translate(-50%, -50%)
             translate3d(var(--tx, 0px), var(--ty, 0px), 0)
             scale(var(--s, 1));
  /* Deux durées, et pas une. Le rapprochement au centre est un geste ample —
     1 100 ms ; le survol est une réponse à la main — 620 ms. Une seule valeur
     rendait l'un mou ou l'autre sec. */
  transition: transform 620ms cubic-bezier(0.22, 1, 0.36, 1),
              opacity 700ms var(--ease);
  will-change: transform;
}
.gal-float.is-front,
.gal-float.is-back {
  transition: transform 1100ms cubic-bezier(0.22, 1, 0.36, 1),
              opacity 700ms var(--ease);
}
.gal-float__in {
  display: block;
  width: 100%; height: 100%;
  overflow: hidden;
  background-color: var(--bg-sunken);
  /* La dérive : quelques pixels, très lentement, jamais en phase d'une
     photographie à l'autre. */
  animation: gal-derive var(--d, 9s) ease-in-out var(--dl, 0s) infinite alternate;
}
.gal-float__in img {
  display: block;
  width: 100%; height: 100%;
  object-fit: cover;
  transition: transform 900ms cubic-bezier(0.22, 1, 0.36, 1);
}
@keyframes gal-derive {
  from { transform: translate3d(0, calc(var(--a, 7px) * -1), 0); }
  to   { transform: translate3d(0, var(--a, 7px), 0); }
}

/* Arrivée : elles montent et paraissent, décalées les unes des autres. */
.gal-float { opacity: 0; }
.gal-float.is-in { opacity: 1; }
.gal-float:not(.is-in) { transform: translate(-50%, -50%) translate3d(0, 34px, 0) scale(0.94); }

/* Le survol : la photographie s'avance. `z-index` ne s'anime pas — il bascule
   d'un coup, et c'est ce qui rendait le passage devant brutal. Une échelle qui
   monte en même temps donne au saut de plan une durée visible, et la dérive
   s'arrête pour que l'image se pose sous la main au lieu de continuer à
   flotter. */
@media (hover: hover) and (pointer: fine) {
  .gal-float:hover,
  .gal-float:focus-visible { z-index: 9; --s: 1.045; }
  .gal-float:hover .gal-float__in,
  .gal-float:focus-visible .gal-float__in { animation-play-state: paused; }
  .gal-float:hover .gal-float__in img { transform: scale(var(--zoom-hover)); }
  /* Une photographie centrée ou écartée ne réagit pas au survol : son échelle
     est posée en style en ligne par le script, qui l'emporte sur cette
     feuille. Rien à écrire pour l'en empêcher. */
}
.gal-float:focus-visible { outline: 2px solid var(--focus); outline-offset: 6px; }

/* Une photographie est au centre : elle passe devant, les autres s'effacent et
   cessent de dériver — rien ne doit bouger autour de ce qu'on regarde. */
.gal-float.is-front { z-index: 20; }
.gal-float.is-front .gal-float__in { animation: none; }
.gal-float.is-back { opacity: 0.14; pointer-events: none; }
.gal-float.is-back .gal-float__in { animation: none; }

/* Pied : compteur, catégorie, et les deux flèches. Elles ne paraissent que
   lorsqu'une photographie est au centre. */
.gal-float-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  margin-top: var(--sp-4);
  color: var(--text-muted);
  font-size: var(--fs-fine);
  font-variant-numeric: tabular-nums;
}
.gal-float-nav {
  display: flex;
  gap: var(--sp-2);
  opacity: 0;
  pointer-events: none;
  transition: opacity 420ms var(--ease);
}
.is-centre .gal-float-nav { opacity: 1; pointer-events: auto; }
.gal-imm-btn {
  width: 44px; height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--rule);
  border-radius: 50%;
  color: var(--text-strong);
  background: none;
  cursor: pointer;
  transition: border-color var(--dur) var(--ease), background-color var(--dur) var(--ease);
}
.gal-imm-btn:hover { border-color: var(--text-strong); background-color: var(--bg-sunken); }
.gal-imm-btn:focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; }

/* --- Compteur ---------------------------------------------------------------- */
.gal-count {
  margin-bottom: var(--sp-4);
  color: var(--text-muted);
  font-size: var(--fs-fine);
  font-variant-numeric: tabular-nums;
}

/* --- Tablette et mobile ------------------------------------------------------
   Le nombre de colonnes est décidé par le script, qui seul connaît la largeur
   réelle du conteneur. Ici, uniquement ce qui relève du dessin. */
@media (max-width: 900px) {
  .gal-head { display: block; }
  .gal-float-stage { height: calc(100svh - var(--header-h) - 170px); min-height: 340px; }
}
@media (max-width: 560px) {
  /* Le bandeau du site reste fixe sur téléphone : la barre doit se caler
     DESSOUS. Posée à zéro, sa première rangée passait derrière lui. */
  .gal-sticky { padding-top: var(--sp-2); }
  .gal-float-stage { height: 64svh; min-height: 300px; }
}

/* --- Mouvement réduit --------------------------------------------------------
   Tout reste en place et lisible : seules les durées tombent. */
@media (prefers-reduced-motion: reduce) {
  .gal-item,
  .gal-item img,
  .gal-float,
  .gal-float__in img { transition: none; }
  .gal-float__in { animation: none; }
  .gal-item:not(.is-in) img { transform: none; }
  .gal-item { opacity: 1; }
  .gal-float { opacity: 1; }
  .gal-float:not(.is-in) { transform: translate(-50%, -50%); }
}

/* Le plan sur lequel les photographies sont posées : il remplit la scène et
   sert de repère à leurs coordonnées. */
.gal-float-stage__in { position: absolute; inset: 0; }

/* --- Passer d'une vue à l'autre ----------------------------------------------
   Ce n'est pas une catégorie : c'est une action, et elle se lit comme un lien
   du site. Elle se tient là où le regard est déjà — sous les photographies
   flottantes, puis en tête de la grille. */
.gal-switch {
  appearance: none;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  font: inherit;
  color: var(--text-strong);
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  border-bottom: 1px solid var(--rule);
  padding-bottom: 2px;
  transition: border-color var(--dur) var(--ease), color var(--dur) var(--ease);
}
.gal-switch:hover { border-bottom-color: var(--text-strong); }
.gal-switch:focus-visible { outline: 2px solid var(--focus); outline-offset: 4px; }
.gal-switch .arrow { transition: transform var(--dur) var(--ease); }
.gal-switch:hover .arrow { transform: translateX(3px); }

/* En grille, la ligne de tête porte le compte à gauche et le retour à droite. */
.gal-count {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-4);
  flex-wrap: wrap;
}

/* --- Ouverture : le titre, et une photographie à sa droite --------------------
   La page s'ouvrait sur du texte seul et beaucoup de vide à droite. Une
   photographie verticale y tient le regard le temps de lire, et annonce ce que
   la galerie contient. Elle ne remplace pas le titre : elle l'accompagne. */
body[data-page="galerie"] .page-head--duo .shell {
  display: grid;
  /* La photographie prend la moitié de la ligne : en 38fr plafonnés à 420 px,
     il restait un large vide entre le chapô et elle. */
  grid-template-columns: minmax(0, 50fr) minmax(0, 44fr);
  column-gap: clamp(28px, 3.4vw, 60px);
  align-items: center;
}
body[data-page="galerie"] .page-head__vue {
  margin: 0;
  justify-self: end;
  width: 100%;
  max-width: 560px;
}

/* --- Le texte tient la hauteur de la photographie ----------------------------
   Un bloc de 224 px posé au milieu d'une image de 497 laissait 136 px de vide
   au-dessus et autant au-dessous. Deux leviers, tirés ensemble : la
   photographie raccourcit — 5/4 puis 4/3, sans rien perdre de la scène — et le
   texte grandit, d'un cran au titre comme au chapô. */
@media (min-width: 901px) {
  body[data-page="galerie"] .page-head--duo h1 {
    font-size: clamp(2.25rem, 1.05rem + 4.3vw, 4.5rem);
  }
  body[data-page="galerie"] .page-head--duo .lead {
    font-size: clamp(1.125rem, 0.9rem + 0.85vw, 1.4375rem);
    /* Mesure resserrée : le chapô garde ses trois lignes en grandissant, et
       c'est le nombre de lignes qui fait la hauteur. */
    max-width: 30ch;
    margin-top: var(--sp-6);
  }
  body[data-page="galerie"] .page-head--duo h1 { margin-top: var(--sp-4); }
}
/* Le cadre suit le recadrage servi : 5/4 au bureau, carré en colonne étroite.
   Ni l'un ni l'autre n'est le 2/3 de la prise de vue — le vertical étroit
   poussait la galerie hors de vue et laissait du plafond et du sol vides de
   part et d'autre du sujet. */
.frame--vue { aspect-ratio: 1 / 1; }
@media (min-width: 901px) {
  .frame--vue { aspect-ratio: 4 / 3; }
}
/* L'image vit dans un `<picture>` : `.frame > img` ne l'atteindrait plus. */
body[data-page="galerie"] .page-head__vue picture { display: contents; }
body[data-page="galerie"] .page-head__vue img {
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
}

/* En colonne unique, la photographie passe SOUS le texte : l'ordre de lecture
   reste le titre, le chapô, puis l'image — et un portrait plein écran ne
   repousse pas la galerie hors de vue. */
@media (max-width: 900px) {
  body[data-page="galerie"] .page-head--duo .shell { grid-template-columns: minmax(0, 1fr); }
  body[data-page="galerie"] .page-head__vue {
    justify-self: start;
    max-width: 300px;
    margin-top: var(--sp-6);
  }
}
