/* ==========================================================================
   Château de la Valouze
   Feuille de style unique, partagée par la version anglaise (racine)
   et la version française (/fr/).

   Direction artistique : mot directeur « impressionniste ».
   Partis pris arrêtés par Tanguy le 09/09/2026 :
   - bords francs partout (--radius-media: 0), compensés par un filet d'or
     posé à 24 px de la photo, comme le passe-partout d'une gravure ;
   - filets, jamais d'ombres. Seule exception : l'ombre du header, teintée
     dans l'or à 10 %, reprise de Villa Gallici ;
   - header en verre dépoli à deux états, pilotés par des classes sur <html> ;
   - typographie fine : Jost 300 en capitales espacées pour les titres,
     Hanken Grotesk 400 pour le texte courant (jamais 300 : illisible sur crème),
     Cormorant Garamond italique pour la seule lettrine du site ;
   - toutes les animations sont réversibles (voir js/script.js).
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Polices, hébergées en local (jamais Google Fonts : 1,3 s de chaîne
   tierce en moins, et pas d'IP visiteur envoyée aux États-Unis).
   Jost est une police variable : un seul fichier couvre les graisses 300
   et 400, d'où la plage `font-weight: 100 900`.
   -------------------------------------------------------------------------- */
@font-face {
  font-family: 'Jost';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('../fonts/jost-var-latin.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;
}
@font-face {
  font-family: 'Jost';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('../fonts/jost-var-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Hanken Grotesk';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../fonts/hanken-400-latin.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;
}
@font-face {
  font-family: 'Hanken Grotesk';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../fonts/hanken-400-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Cormorant Garamond';
  font-style: italic;
  font-weight: 500;
  font-display: swap;
  src: url('../fonts/cormorant-garamond-500-italic-latin.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;
}
@font-face {
  font-family: 'Cormorant Garamond';
  font-style: italic;
  font-weight: 500;
  font-display: swap;
  src: url('../fonts/cormorant-garamond-500-italic-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* --------------------------------------------------------------------------
   2. Jetons de couleur, d'espacement et de mouvement
   Toutes les couleurs ont été prélevées au pixel sur les photos haute
   définition du château (ardoise des toitures, pierre calcaire, moulures
   dorées du hall d'honneur), jamais déduites d'une description.
   -------------------------------------------------------------------------- */
:root {
  --ink: #4F5B6B;          /* bleu ardoise des toitures : titres, header plein, footer */
  --cream: #F4EFE5;        /* pierre calcaire éclaircie : fond des sections */
  --gold: #B99A62;         /* moulures dorées, assombries pour rester lisibles sur crème */
  --white: #FFFFFF;
  --body: #5B6675;         /* ardoise adoucie : texte courant. Jamais l'encre, jamais le noir */
  --muted: #8A8578;        /* gris pierre : légendes, mentions */
  --line: rgba(185, 154, 98, 0.35);   /* filet d'or, la seule ornementation du site */
  --line-soft: rgba(79, 91, 107, 0.14);

  --radius-media: 0;       /* bords francs, exception assumée au standard Azimut (7 px) */
  --radius: 0;

  --font-title: 'Jost', 'Helvetica Neue', Arial, sans-serif;
  --font-body: 'Hanken Grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  --font-drop: 'Cormorant Garamond', 'Times New Roman', serif;

  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
  --section-y: 180px;      /* respiration : 180 px, contre 96 px sur les sites précédents */
  --header-h: 104px;
  --frame-gap: 24px;       /* distance du filet d'or au bord de la photo */
}

/* --------------------------------------------------------------------------
   3. Remise à zéro
   `overflow-x: clip` est sur <html> et non sur body : c'est <html> qui porte
   le défilement du document, et `clip` (pas `hidden`) évite de casser le
   `position: sticky`. Sans ça, le tiroir mobile en translateX(100%) allonge
   le scrollWidth et le site prend une barre horizontale sur toutes ses pages.
   -------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }

html {
  overflow-x: clip;
  scroll-behavior: auto;
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--white);
  color: var(--body);
  font-family: var(--font-body);
  font-size: 17px;
  font-weight: 400;
  line-height: 1.7;
  letter-spacing: 0.01em;
  -webkit-font-smoothing: antialiased;
}

/* `height: auto` est indispensable : les attributs width/height posés sur les
   <img> (utiles contre le layout shift) étirent verticalement l'image dès que
   sa colonne est plus étroite que l'attribut. */
img { max-width: 100%; height: auto; display: block; }

a { color: var(--gold); text-decoration: none; }
button { font: inherit; }

::selection { background: var(--gold); color: var(--white); }

:focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: 3px;
}

.skip-link {
  position: absolute; left: -9999px; top: 0;
  background: var(--ink); color: var(--cream);
  padding: 0.8rem 1.2rem; z-index: 999;
}
.skip-link:focus { left: 0; }

/* --------------------------------------------------------------------------
   4. Typographie
   Titres en Jost 300, capitales, interlettrage 0,16em, interligne 1,05.
   L'interlettrage ajoute un blanc après la dernière lettre : le
   `margin-right: -0.16em` le reprend pour que les titres centrés le soient
   réellement, et que les titres alignés à gauche gardent leur bord droit net.
   -------------------------------------------------------------------------- */
h1, h2, h3, h4 {
  font-family: var(--font-title);
  font-weight: 300;
  color: var(--ink);
  text-transform: uppercase;
  letter-spacing: 0.16em;
  line-height: 1.05;
  margin: 0 -0.16em 0 0;
  text-wrap: balance;
}

/* ⚠️ La compensation d'interlettrage (`margin-right: -0.16em`, qui reprend le
   blanc ajouté après la dernière lettre) décale un titre CENTRÉ vers la
   droite de la moitié de sa valeur, mesuré à 3 px sur la Valouze. Dans un
   contexte centré, elle se pose donc des DEUX côtés, pour que la boîte reste
   symétrique. */
.text-center h1, .text-center h2, .text-center h3,
.section-intro--center h1, .section-intro--center h2, .section-intro--center h3,
.photo-band-caption--center h2, .pin-caption h2 { margin-left: -0.16em; }

h1 { font-size: clamp(30px, 4.4vw, 52px); }
.hero h1 { font-size: clamp(34px, 5.4vw, 64px); }
h2 { font-size: clamp(24px, 2.9vw, 44px); }
h3 { font-size: clamp(19px, 1.7vw, 28px); letter-spacing: 0.13em; }
h4 { font-size: 15px; letter-spacing: 0.14em; }

/* Coupures de ligne du texte courant.
   `pretty` demande au navigateur d'éviter les coupures laides, en premier
   lieu le mot seul sur la dernière ligne. `balance` répartit les lignes d'un
   court paragraphe à égalité, ce qui règle le cas signalé par Tanguy le
   10/09/2026 : « La plupart » restait pendu en fin de ligne, séparé de « des
   mariés », alors que la coupure naturelle était la fin de la phrase juste
   avant. Un paragraphe de site premium ne se coupe pas au milieu d'une
   locution. */
p, li, figcaption, address, dd, dt { text-wrap: pretty; }
p { margin: 0 0 1.4rem; }
p:last-child { margin-bottom: 0; }

/* Les paragraphes courts posés dans une colonne étroite sont ceux qui se
   coupent le plus mal : ils passent en `balance`, plafonné par les
   navigateurs à quelques lignes, ce qui est exactement leur cas. */
.lede,
.col-text p, .container--narrow p,
.section-intro p, .section-intro--center p,
.space-item p, .tile p, .qa-item p, .quote p, .card li,
.stat-label, .awaiting span, .plan-legend span, .contact-aside li,
.photo-band-caption p, .pin-caption p,
figcaption { text-wrap: balance; }

.eyebrow {
  display: block;
  font-family: var(--font-title);
  font-weight: 400;
  font-size: 11px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--gold);
  margin: 0 0 1.4rem;
}

.lede {
  font-size: 19px;
  line-height: 1.65;
  color: var(--body);
  text-wrap: balance;
}

.small { font-size: 14px; color: var(--muted); }

.mention {
  font-family: var(--font-title);
  font-weight: 400;
  font-size: 12px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--muted);
}
.photo-band-caption .mention { color: rgba(244, 239, 229, 0.92); text-shadow: 0 1px 10px rgba(28, 34, 42, 0.55); }
.photo-band-caption .mention a { color: var(--cream); }

/* La lettrine, seule serif du site. Une par page, et seulement sur
   Le domaine et À propos : c'est ce qui la garde exceptionnelle. */
.dropcap::first-letter {
  font-family: var(--font-drop);
  font-style: italic;
  font-weight: 500;
  color: var(--gold);
  font-size: 5.1em;
  line-height: 0.82;
  float: left;
  padding: 0.06em 0.14em 0 0;
}

/* Filet doré qui se trace sous les titres de section (scaleX 0 → 1,
   réversible côté JS). Sans JS il est simplement déjà tracé. */
.rule {
  display: block;
  width: 64px; height: 1px;
  background: var(--gold);
  margin: 1.8rem 0 0;
  transform-origin: left center;
}
.text-center .rule, .section-intro--center .rule { margin-inline: auto; }

/* --------------------------------------------------------------------------
   5. Boutons : remplissage par balayage, jamais un changement de fond
   Le pseudo-élément part à translateX(-100%) et glisse à 0 en 0,7 s.
   -------------------------------------------------------------------------- */
.btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 0.7rem;
  overflow: hidden;
  z-index: 0;
  padding: 1.05rem 1.9rem;
  border: 1px solid var(--gold);
  border-radius: var(--radius);
  background: transparent;
  color: var(--ink);
  font-family: var(--font-title);
  font-weight: 400;
  font-size: 13px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  cursor: pointer;
  transition: color 0.5s var(--ease), border-color 0.5s var(--ease);
}
.btn::after {
  content: '';
  position: absolute; inset: 0;
  background: var(--gold);
  transform: translateX(-100%);
  transition: transform 0.7s ease-in-out;
  z-index: -1;
}
.btn:hover, .btn:focus-visible { color: var(--white); border-color: var(--gold); }
.btn:hover::after, .btn:focus-visible::after { transform: translateX(0); }

/* Bouton plein, pour les CTA principaux : le balayage se fait à l'encre. */
.btn--solid { background: var(--gold); color: var(--white); border-color: var(--gold); }
.btn--solid::after { background: var(--ink); }
.btn--solid:hover, .btn--solid:focus-visible { color: var(--white); border-color: var(--ink); }

/* Bouton posé sur photo : crème, se remplit de crème et passe à l'encre. */
.btn--light { border-color: rgba(244, 239, 229, 0.7); color: var(--cream); }
.btn--light::after { background: var(--cream); }
.btn--light:hover, .btn--light:focus-visible { color: var(--ink); border-color: var(--cream); }

/* Lien texte avec un trait qui se déploie. */
.link-line {
  position: relative;
  font-family: var(--font-title);
  font-weight: 400;
  font-size: 12px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--gold);
  display: inline-block;
}
.link-line::after {
  content: '';
  position: absolute; left: 0; bottom: -5px;
  width: 0; height: 1px;
  background: currentColor;
  transition: width 0.4s var(--ease);
}
.link-line:hover::after, .link-line:focus-visible::after { width: 100%; }

.btn-row { display: flex; flex-wrap: wrap; gap: 1rem 1.6rem; align-items: center; margin-top: 2.4rem; }

/* --------------------------------------------------------------------------
   6. Header : verre dépoli à deux états
   Les états vivent sur <html> (is-top, is-scrolled, nav-open) et non sur le
   header : c'est l'architecture de villagallici.com, et c'est elle qui rend
   la réversibilité gratuite. Le header reprend son grand format dès que l'on
   remonte au hero, sans une ligne de JS de sortie.
   -------------------------------------------------------------------------- */
.site-header {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 300;
  height: var(--header-h);
  display: flex;
  align-items: center;
  background: transparent;
  border-bottom: 1px solid transparent;
  transition: height 0.55s var(--ease), background-color 0.55s var(--ease),
              box-shadow 0.55s var(--ease), border-color 0.55s var(--ease);
}

/* La barre scrollée est en ARDOISE floutée, et c'est le LOGO qui l'a décidé.

   Sept essais de logo pour arriver là. Tanguy voulait, et a redemandé six
   fois, le fichier du pied de page tel quel dans le header : « tu as le logo
   dans le footer, pourquoi tu ne le prends pas directement et tu l'appliques ? »
   Ce fichier est en CRÈME, parce qu'il est posé sur l'ardoise du pied de
   page. Sur une barre claire il disparaît, et toute retouche pour le rendre
   visible sur fond clair a été refusée, à juste titre : ce n'était plus le
   logo du lieu, c'était un logo redessiné.

   La barre s'adapte donc au logo, et non l'inverse. C'est d'ailleurs ce que
   la palette du site prévoit depuis le début : « encre : titres, header
   plein, footer ». */
html.is-scrolled .site-header {
  height: 68px;
  background-color: rgba(255, 255, 255, 0.82);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
  backdrop-filter: blur(14px) saturate(140%);
  border-bottom-color: var(--line);
  /* la seule ombre du site, teintée dans l'or à 10 % */
  box-shadow: rgba(185, 154, 98, 0.1) 0 0 5px;
}

@supports not ((backdrop-filter: blur(2px)) or (-webkit-backdrop-filter: blur(2px))) {
  html.is-scrolled .site-header { background-color: rgba(255, 255, 255, 0.96); }
}

.header-row {
  width: min(100% - 3rem, 1384px);
  margin-inline: auto;
  display: grid;
  /* 7 liens : la colonne du logo doit avoir une largeur minimale garantie,
     sinon les deux groupes réclament toute la place et l'écrasent à 0 px
     sans aucune erreur visible (constaté sur Passiflorart). */
  grid-template-columns: 10.5rem 1fr 152px 1fr 10.5rem;
  align-items: center;
  gap: 2.4rem;
}

.lang-switch { justify-self: start; display: flex; align-items: center; gap: 0.5rem; }
.lang-switch a {
  font-family: var(--font-title);
  font-weight: 400;
  font-size: 11px;
  letter-spacing: 0.18em;
  color: var(--cream);
  opacity: 0.62;
  transition: opacity 0.4s var(--ease), color 0.55s var(--ease);
}
.lang-switch a.is-current { opacity: 1; }
.lang-switch a:hover { opacity: 1; }
.lang-switch span { font-size: 10px; color: var(--cream); opacity: 0.4; transition: color 0.55s var(--ease); }
html.is-scrolled .lang-switch a, html.is-scrolled .lang-switch span { color: var(--ink); }

.nav-group { display: flex; align-items: center; gap: 1.5rem; }
.nav-group--left { justify-content: flex-end; }
.nav-group--right { justify-content: flex-start; }

.nav-link {
  position: relative;
  font-family: var(--font-title);
  font-weight: 400;
  font-size: 11.5px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--cream);
  white-space: nowrap;
  transition: color 0.55s var(--ease), opacity 0.4s var(--ease);
}
/* Le trait suit currentColor : il bascule tout seul avec le texte,
   sans seconde règle pour l'état scrollé. */
.nav-link::after {
  content: '';
  position: absolute; left: 0; bottom: -5px;
  width: 0; height: 1px;
  background: currentColor;
  transition: width 0.35s var(--ease);
}
html.is-scrolled .nav-link { color: var(--ink); }
.nav-link:hover::after, .nav-link:focus-visible::after, .nav-link.is-active::after { width: 100%; }

/* Le logo, deux dessins et deux couleurs, un par état :

   - au-dessus du visuel, le lockup de la VARIANTE HEADER en crème, emblème et
     nom. C'est le moment où le visiteur doit lire où il est, et cette
     variante est la seule où le nom reste lisible à cette taille, son emblème
     étant ramené à 60 % de la largeur du nom. Le lockup du pied de page a été
     essayé ici et écarté (« c'était mieux avant ») : son emblème plus grand
     écrase le nom ;
   - une fois scrollé, l'EMBLÈME SEUL à l'encre, sans le nom. « Je veux pas le
     texte sous le logo quand le header est en scrolling » : le nom a déjà été
     lu à l'arrivée, l'emblème suffit ensuite comme repère. C'est le dessin
     du PIED DE PAGE, repris tel quel, sans une seule retouche. C'est la
     demande de Tanguy, répétée six fois : « tu as le logo dans le footer,
     pourquoi tu ne le prends pas directement et tu l'appliques ? » Six
     variantes redessinées ont été essayées avant de comprendre que la
     question n'était pas le dessin mais la couleur de la barre.

   La bascule est un fondu d'opacité, jamais un filter (qui salit les aplats
   et coûte un repaint). */
.logo {
  position: relative;
  justify-self: center;
  display: block;
  height: 74px;
  width: 152px;
  transition: height 0.55s var(--ease);
}
.logo img {
  position: absolute;
  left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  width: auto;
  transition: opacity 0.55s var(--ease), height 0.55s var(--ease);
}
.logo-lockup { height: 74px; }
.logo-emblem { height: 48px; opacity: 0; }
/* Aucune ombre sur le lockup, retrait demandé par Tanguy le 10/09/2026. Le
   site tient sur des filets, pas des ombres : la seule ombre conservée est
   celle du header. Le voile du hero suffit à détacher le logo crème de la
   photo, le contraste a été remesuré après retrait. */

html.is-scrolled .logo { height: 48px; }
html.is-scrolled .logo-lockup { opacity: 0; }
html.is-scrolled .logo-emblem { opacity: 1; }

.header-cta { justify-self: end; }
.header-cta .btn { padding: 0.78rem 1.35rem; font-size: 11px; letter-spacing: 0.13em; white-space: nowrap; }
.header-cta .btn { border-color: rgba(244, 239, 229, 0.7); color: var(--cream); }
.header-cta .btn::after { background: var(--cream); }
.header-cta .btn:hover, .header-cta .btn:focus-visible { color: var(--ink); border-color: var(--cream); }

/* Spécificité volontairement forte : sans elle, le texte reste crème sur le
   remplissage crème dès que le header est en version pleine. */
/* Sur la barre claire, le texte repasse à l'encre. Spécificité volontairement
   forte, sinon le libellé reste crème sur le remplissage et devient
   illisible. */
html.is-scrolled .header-cta .btn { border-color: var(--ink); color: var(--ink); }
html.is-scrolled .header-cta .btn::after { background: var(--ink); }
html.is-scrolled .header-cta .btn:hover, html.is-scrolled .header-cta .btn:focus-visible { color: var(--cream); border-color: var(--ink); }

.nav-toggle { display: none; }

/* --------------------------------------------------------------------------
   7. Tiroir mobile
   Le tiroir est un frère du <header> dans le DOM, jamais un enfant : le
   backdrop-filter du header scrollé crée un contexte de positionnement pour
   ses descendants, et un tiroir `position: fixed` à l'intérieur se retrouve
   fixé par rapport au header (56 px de haut) au lieu de tout l'écran.
   -------------------------------------------------------------------------- */
.nav-drawer {
  position: fixed;
  inset: 0 0 0 auto;
  width: min(88vw, 400px);
  z-index: 290;
  background: var(--cream);
  border-left: 1px solid var(--line);
  padding: 7rem 2.4rem 2.4rem;
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  transform: translateX(100%);
  transition: transform 0.6s var(--ease);
  overflow-y: auto;
}
html.nav-open .nav-drawer { transform: translateX(0); }
.nav-drawer a {
  font-family: var(--font-title);
  font-weight: 300;
  font-size: 17px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink);
  padding: 0.95rem 0;
  border-bottom: 1px solid var(--line-soft);
}
.nav-drawer .drawer-lang { margin-top: 2rem; display: flex; gap: 0.6rem; }
.nav-drawer .drawer-lang a { border: 0; font-size: 12px; letter-spacing: 0.2em; padding: 0; color: var(--muted); }
.nav-drawer .drawer-lang a.is-current { color: var(--gold); }
.nav-drawer .btn { margin-top: 2rem; justify-content: center; }

.nav-scrim {
  position: fixed; inset: 0;
  z-index: 280;
  background: rgba(79, 91, 107, 0.4);
  opacity: 0; pointer-events: none;
  transition: opacity 0.5s var(--ease);
}
html.nav-open .nav-scrim { opacity: 1; pointer-events: auto; }
html.nav-open { overflow: hidden; }

/* --------------------------------------------------------------------------
   8. Hero de la page d'accueil
   Slider de 4 visuels. Seul le premier porte un `src` avec fetchpriority
   haute : les visuels d'un carrousel sont superposés au même endroit, donc
   tous dans la fenêtre visible, et le navigateur ignore leur loading="lazy".
   Sur Passiflorart, quatre visuels que personne ne voyait pesaient 999 Ko au
   premier affichage. Les suivants sont en data-src et chargés au load.
   -------------------------------------------------------------------------- */
.hero {
  position: relative;
  height: 100svh;
  min-height: 620px;
  overflow: hidden;
  display: flex;
  align-items: flex-end;
  /* ⚠️ Blanc, et non l'encre : la photo se déroule SUR LE BLANC DE LA PAGE.
     Un fond sombre sous un masque en cours de lever se lit comme un voile
     gris, ce que Tanguy a refusé le 10/09/2026. */
  background: var(--white);
}

/* Le calque animé contient la photo ET le scrim. Si le scrim reste dehors,
   son dégradé sombre s'affiche sur le blanc pendant toute l'animation : c'est
   le voile gris, par une autre porte. */
.hero-media { position: absolute; inset: 0; overflow: hidden; will-change: opacity, transform; }
.hero-slides { position: absolute; inset: 0; }
.hero-slide {
  position: absolute; inset: 0;
  opacity: 0;
  transition: opacity 1.6s var(--ease);
}
.hero-slide.is-active { opacity: 1; }
.hero-slide img { width: 100%; height: 100%; object-fit: cover; }

/* Dégradé sombre en haut : il porte la lisibilité du logo crème et des liens
   du header par-dessus un visuel clair. */
.hero-scrim {
  position: absolute; inset: 0;
  background:
    linear-gradient(to bottom, rgba(37, 44, 54, 0.5) 0%, rgba(37, 44, 54, 0) 30%),
    linear-gradient(to top, rgba(37, 44, 54, 0.82) 0%, rgba(37, 44, 54, 0.66) 24%, rgba(37, 44, 54, 0.42) 50%, rgba(37, 44, 54, 0.08) 72%, rgba(37, 44, 54, 0) 82%);
}

/* Cadre intérieur : filet crème de 1 px, à 28 px du bord de l'écran. */
.hero-frame {
  position: absolute;
  top: var(--header-h); right: 28px; bottom: 28px; left: 28px;
  border: 1px solid rgba(244, 239, 229, 0.4);
  pointer-events: none;
  z-index: 3;
}

.hero-content, .page-hero-content { will-change: opacity; }
.hero-content {
  position: relative;
  z-index: 4;
  width: min(100% - 6rem, 1200px);
  margin: 0 auto 7rem;
  color: var(--cream);
}
.hero-content .eyebrow { color: rgba(244, 239, 229, 0.92); text-shadow: 0 1px 10px rgba(28, 34, 42, 0.55); }
.hero-content h1 { color: var(--cream); max-width: 16em; text-shadow: 0 2px 22px rgba(28, 34, 42, 0.45); }
.hero-content .lede {
  color: rgba(244, 239, 229, 0.95);
  max-width: 34em;
  margin-top: 1.8rem;
  text-shadow: 0 1px 14px rgba(28, 34, 42, 0.5);
}

/* Compteur du hero : chiffres dans la police de titre et filet qui se remplit
   à l'or. Plus jamais de points ronds, c'est ce qui datait nos sliders. */
.slider-count {
  display: flex;
  align-items: center;
  gap: 1rem;
  font-family: var(--font-title);
  font-weight: 300;
  font-size: 12px;
  letter-spacing: 0.2em;
}
.slider-count-track {
  position: relative;
  width: 96px; height: 1px;
  background: rgba(244, 239, 229, 0.28);
}
.slider-count-fill {
  position: absolute; left: 0; top: 0; bottom: 0;
  width: 0;
  background: var(--gold);
  transition: width 0.8s var(--ease);
}
.hero-count { position: absolute; right: max(3rem, 6vw); bottom: 7rem; z-index: 4; color: var(--cream); }

/* Le trait vertical d'invitation au défilement a été retiré le 10/09/2026 à la
   demande de Tanguy. Il posait un repère de plus au bas d'un hero qui en avait
   déjà deux, le cadre intérieur et le compteur, et un site dont la seule
   ornementation est le filet n'a pas besoin d'un filet qui ne cadre rien. */

/* --------------------------------------------------------------------------
   9. Bannière des pages intérieures
   Hauteur volontairement contenue (558 px sur 1440) : c'est une bannière, pas
   un second hero. La photo est surdimensionnée à 150 % pour le parallax, et
   elle n'a JAMAIS loading="lazy" puisqu'elle est visible au chargement.
   -------------------------------------------------------------------------- */
.page-hero {
  position: relative;
  height: clamp(420px, 46vw, 600px);
  overflow: hidden;
  display: flex;
  align-items: flex-end;
  background: var(--ink);
}
/* Le calque de la photo de bannière.

   ⚠️ Il existe pour une raison précise : le masque du déroulé ne doit PAS se
   poser sur la même balise que le parallax. Mesuré le 10/09/2026 : une
   animation de `clip-path` sur l'image remettait sa transformation à zéro en
   se terminant, effaçant le décalage de départ du parallax, ce qui produisait
   un saut de 35 px pile à la fin du déroulé. Le masque va donc sur ce calque,
   la transformation reste à l'image, et les deux effets ne se voient plus. */
.page-hero-media { position: absolute; inset: 0; overflow: hidden; }

/* --------------------------------------------------------------------------
   ARRIVÉE DE LA BANNIÈRE, EN CSS PUR ET SANS DÉPENDANCE

   Tanguy a signalé TROIS FOIS que la bannière n'avait pas d'animation « de
   temps en temps » au changement de page. J'ai corrigé deux vrais défauts au
   passage (la position de départ du parallax, puis le préchargement), et le
   symptôme restait, parce que la cause était ailleurs : le fondu venait
   UNIQUEMENT de la transition inter-documents (retirée depuis, voir la
   section 23), et cette
   transition est abandonnée par le navigateur dans un grand nombre de cas,
   sans erreur et sans qu'on puisse le prévoir.

   Mesuré ici, journal de session à l'appui : `pageswap` se déclenche bien mais
   avec `viewTransition` à null, et `pagereveal` ne se déclenche jamais. Et
   cela alors que TOUTES les conditions documentées sont réunies : l'at-rule
   est parsée en `CSSViewTransitionRule` avec `navigation: auto`,
   `prefers-reduced-motion` est faux, le document est de premier niveau, la
   navigation est de même origine, Chrome 148 supporte `pagereveal` et
   `pageswap`, les en-têtes ne portent pas de `no-store`, et le refus persiste
   même avec la page déjà en cache.

   LA LEÇON : une animation qui n'existe que dans une transition
   inter-documents est une animation dont on ne maîtrise pas l'exécution.
   C'est une API très récente, abandonnée silencieusement pour des raisons que
   la page ne contrôle pas.

   La bannière porte donc désormais SA PROPRE arrivée, en CSS, qui se joue à
   chaque affichage de la page, sans JavaScript, sans GSAP, sans dépendre de
   quoi que ce soit. Si la transition inter-documents tourne aussi, les deux se
   superposent sans se gêner : le fondu de la transition porte la page entière,
   celui-ci porte la photo.

   ⚠️ L'animation est posée sur `.page-hero-media`, le calque, et JAMAIS sur
   l'image : c'est elle qui porte la transformation du parallax, et une
   animation de `transform` sur le même élément l'écraserait. C'est la même
   séparation qui avait déjà réglé le saut de 35 px en fin de déroulé.
   -------------------------------------------------------------------------- */
/* ⚠️ AUCUNE OPACITÉ ANIMÉE ICI, ET C'EST DÉLIBÉRÉ.

   Première version de cette animation : `from { opacity: 0 }`. Mesuré aussitôt
   après : dans un onglet dont le compositeur est en pause, l'animation reste
   à l'état « running » sans que son horloge n'avance, et elle MAINTIENT donc
   son état de départ. La photo de bannière restait invisible au-delà de
   1 200 ms, et `animation-fill-mode: none` n'y change rien : le mode de
   remplissage ne gouverne que l'avant et l'après, pas l'intervalle actif.

   Une animation ne doit jamais avoir « invisible » comme mode de défaillance.
   C'est la règle qui a coûté le plus cher sur ce site (tous les grands titres
   invisibles, le 10/09/2026 au matin), et elle vaut aussi en CSS pur.

   D'où le choix de n'animer que deux propriétés dont l'état de départ est
   inoffensif : une échelle de 4,5 % et une luminosité de 88 %. Si l'animation
   ne tourne pas, le visiteur voit une photo à peine plus grande et à peine
   plus sombre, ce que personne n'appellera un défaut. Et quand elle tourne,
   la photo se pose et s'éclaircit, ce qui se lit comme un fondu. */
@keyframes arrivee-banniere {
  from { transform: scale(1.045); filter: brightness(0.88); }
  to   { transform: scale(1);     filter: brightness(1); }
}
@media (prefers-reduced-motion: no-preference) {
  .page-hero-media {
    /* ⚠️ AUCUN mode de remplissage, et surtout PAS `both`.
       Avec `both`, l'état de départ (opacité 0) est conservé tant que
       l'animation n'a pas avancé : une photo de bannière restait donc
       INVISIBLE si l'animation ne tournait pas, par exemple dans un onglet
       d'arrière-plan dont le compositeur est en pause. Mesuré : opacité 0
       maintenue au-delà de 1 200 ms, avec `avancement: 0`.
       Sans mode de remplissage, l'état naturel de l'élément est l'état final
       (opaque, sans échelle) : l'animation n'est qu'un passage. Si elle ne
       tourne pas, la photo est simplement là, ce qui est le bon défaut.
       C'est la même règle que pour les titres : une animation ne doit jamais
       avoir « invisible » comme mode de défaillance. */
    animation: arrivee-banniere 0.85s cubic-bezier(0.22, 0.61, 0.36, 1);
  }
}
.page-hero .parallax-img {
  position: absolute; top: -8%; left: 0;
  width: 100%; height: 116%;
  object-fit: cover;
  will-change: transform;
}
.page-hero .hero-scrim { z-index: 2; }
.page-hero-content {
  position: relative;
  z-index: 4;
  width: min(100% - 6rem, 1200px);
  margin: 0 auto 4.5rem;
  color: var(--cream);
}
.page-hero-content h1 { color: var(--cream); text-shadow: 0 2px 22px rgba(28, 34, 42, 0.45); }
.page-hero-content .eyebrow { color: rgba(244, 239, 229, 0.92); text-shadow: 0 1px 10px rgba(28, 34, 42, 0.55); }

/* La ligne de bannière, POSÉE SOUS LE TITRE.

   Elle remplace le sur-titre des pages intérieures, qui répétait mot pour mot
   le titre juste en dessous : « GALERIE » puis « Galerie » (signalé par
   Tanguy le 10/09/2026). Sous le titre plutôt qu'au-dessus, c'est son choix,
   et c'est aussi le bon : un sur-titre annonce, une ligne posée après le
   titre précise, et c'est de précision qu'il s'agit ici.

   Elle est dans la police de texte et non en capitales espacées : quelques
   mots en capitales à 0,2em d'interlettrage se déchiffrent au lieu de se
   lire. Le hero de l'accueil suit déjà ce schéma, titre puis accroche. */
.page-hero-content .page-hero-ligne {
  /* 0,5rem et non 1,5 : l'ecart entre les deux boites n'est pas l'ecart vu.
     Le titre est en capitales dans une boite de ligne de 54,6 px pour un corps
     de 52, ce qui laisse une dizaine de pixels de vide sous les capitales, et
     la ligne ajoute encore la moitie de son interligne au-dessus de ses
     hautes de casse. A 24 px de marge, l'ecart optique atteignait 39 px et le
     titre et sa ligne ne se lisaient plus comme un bloc (signale par Tanguy
     le 10/09/2026). */
  margin: 0.5rem 0 0;
  max-width: 30em;
  font-size: 17px;
  line-height: 1.6;
  color: rgba(244, 239, 229, 0.95);
  text-shadow: 0 1px 14px rgba(28, 34, 42, 0.5);
}
.page-hero .hero-frame { top: var(--header-h); right: 20px; bottom: 20px; left: 20px; }

/* --------------------------------------------------------------------------
   10. Sections et conteneur
   -------------------------------------------------------------------------- */
.container { width: min(100% - 3rem, 1200px); margin-inline: auto; }
.container--narrow { width: min(100% - 3rem, 700px); margin-inline: auto; }

.section { padding-block: var(--section-y); }
.section--tight { padding-block: calc(var(--section-y) * 0.6); }
.section--cream { background: var(--cream); }
.section--white { background: var(--white); }
.section--ink { background: var(--ink); color: rgba(244, 239, 229, 0.82); }
.section--ink h2, .section--ink h3 { color: var(--cream); }
.section--ink .eyebrow { color: var(--gold); }

.section-intro { max-width: 46em; margin-bottom: 4.5rem; }
.section-intro--center { max-width: 44em; margin-inline: auto; text-align: center; margin-bottom: 5rem; }
.section-intro p { margin-top: 2rem; }
.text-center { text-align: center; }

.section-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 2rem;
  margin-bottom: 4rem;
}
.section-head .section-intro { margin-bottom: 0; }

/* --------------------------------------------------------------------------
   11. Le filet d'or, compensation des bords francs
   Les coins sont francs partout (--radius-media: 0). Pour que ça lise
   « gravure » et non « pas fini », chaque photo de contenu reçoit un filet de
   1 px à l'or posé à 24 px de son bord, comme un passe-partout.
   -------------------------------------------------------------------------- */
.framed { position: relative; }
.framed::after {
  content: '';
  position: absolute;
  inset: calc(var(--frame-gap) * -1);
  border: 1px solid var(--line);
  pointer-events: none;
}
/* Variante pour une photo collée au bord d'une grille : le filet ne sort que
   de trois côtés, sinon il déborde du conteneur et crée un scroll horizontal. */
.framed--inset::after { inset: 14px; }

/* Planche : la photo garde ses proportions d'origine (aucun rognage) mais sa
   hauteur est bornée, et le filet d'or vient épouser ses bords au lieu
   d'entourer la colonne. Sans cette borne, un portrait de 2 000 px de haut
   ouvrait un vide d'un écran entier en face de son texte. */
.plate-col { text-align: center; }
.framed--plate { display: inline-block; max-width: calc(100% - var(--frame-gap) * 2); }
.framed--plate .mask-media { line-height: 0; }
.framed--plate img { max-height: 540px; width: auto; max-width: 100%; margin-inline: auto; }
.framed--plate .stack img { max-height: 290px; }

/* --------------------------------------------------------------------------
   Variantes de cadre, page Le domaine (règle Tanguy, 10/09/2026)

   Six blocs texte + photo à la suite portaient exactement le même cadre, un
   filet à 24 px tout autour. Répété six fois, ce n'est plus une ornementation,
   c'est un gabarit. Trois variantes s'ajoutent, et le filet simple revient
   deux fois pour que l'ensemble reste un système et non un catalogue.

   Le mécanisme vient de villagallici.com, la référence donnée par Tanguy, et
   il a été MESURÉ chez eux : la photo n'est pas centrée dans son cadre, elle
   est poussée dans un angle et le cadre respire d'un seul côté (relevé sur
   leur `.panel-image` : 160 px de vide à gauche, 80 en haut, la photo calée en
   bas à droite d'une boîte de 720 x 720).

   ⚠️ Ne pas reprendre leur aplat d'or : leur `.item-image` porte bien un fond
   #BD9E57, mais la photo le recouvre entièrement, c'est une couleur de
   chargement. L'effet ne vient pas de là, et le copier ne donne rien.

   Toutes les variantes respectent la DA : bords francs, filets, aucune ombre.
   C'est le décalage seul qui crée la profondeur.
   -------------------------------------------------------------------------- */

/* 1. Cadre décalé : le filet n'épouse plus la photo, il ouvre d'un seul côté.
      La photo se resserre d'autant, sinon la boîte du filet dépasse la
      colonne et ouvre un défilement horizontal. */
.framed--decale { max-width: calc(100% - 96px); }
.framed--decale::after { inset: -20px -20px -20px -68px; }
.framed--decale-droite::after { inset: -20px -68px -20px -20px; }

/* 2. Cadre rempli posé DERRIÈRE la photo et décalé sur le côté, ce que Tanguy
      décrit par « des cadres remplis derrière la photo sur le côté ». L'or à
      13 % suffit : à pleine saturation il concurrence la photo au lieu de la
      porter. Le filet est retiré, les deux ensemble surchargent. */
.framed--plein { isolation: isolate; }
.framed--plein::after { display: none; }
.framed--plein::before {
  content: '';
  position: absolute;
  inset: 34px -34px -34px 34px;
  background: rgba(185, 154, 98, 0.13);
  z-index: 0;
}
.framed--plein-gauche::before { inset: 34px 34px -34px -34px; }
.framed--plein > * { position: relative; z-index: 1; }

/* 3. Paire croisée : la seconde photo recouvre le coin bas-gauche de la
      première, et son propre filet ressort du côté opposé au recouvrement.
      Le filet extérieur de la planche est retiré : deux cadres imbriqués sur
      un bloc déjà croisé ne se lisent plus. */
.framed--croise::after { display: none; }
.stack--croise { display: block; }
.stack--croise .croise-a { width: 100%; }
.stack--croise .croise-a img { max-height: 340px; }
.stack--croise .croise-b {
  position: relative;
  width: 64%;
  margin: -13% auto 0 0;
  z-index: 2;
}
.stack--croise .croise-b img { max-height: 250px; }
.stack--croise .croise-b::after {
  content: '';
  position: absolute;
  inset: 16px 16px -18px -18px;
  /* ⚠️ `var(--line)`, jamais `var(--gold)`. C'est le même or, mais à 35 %
     contre 100 %, et TOUS les filets du site sont à 35 %. Posé à pleine
     saturation, celui-ci ressortait trois fois plus que les autres et se
     voyait comme une barre jaune (repéré par Tanguy le 10/09/2026). Un filet
     plus saturé que les autres n'est pas un accent, c'est une faute de
     système. */
  border: 1px solid var(--line);
  pointer-events: none;
}

/* --------------------------------------------------------------------------
   12. Grilles texte + photo
   -------------------------------------------------------------------------- */
.grid-2 {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(2.5rem, 6vw, 6rem);
  align-items: center;
}
.grid-2--stretch { align-items: stretch; }
.grid-2--stretch .parallax-wrap, .grid-2--stretch .framed, .grid-2--stretch .zoom-frame { height: 100%; }
.grid-2--stretch .parallax-wrap { min-height: 420px; }
.grid-2--wide-left { grid-template-columns: 1.15fr 1fr; }
.grid-2--wide-right { grid-template-columns: 1fr 1.15fr; }
.grid-2 img { border-radius: var(--radius-media); }
.grid-2 .col-text { max-width: 34em; }
.grid-2 + .grid-2 { margin-top: clamp(5rem, 10vw, 9rem); }

/* Deux photos entières empilées dans une colonne, quand un espace en mérite
   deux et qu'aucune des deux ne doit être rognée. */
.stack { display: grid; gap: 1.2rem; }

.order-first { order: -1; }

.grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(1.6rem, 3vw, 2.6rem); }

/* Une mosaïque de photos aux rapports différents rend une rangée en dents de
   scie. Le cadrage carré met tout au même gabarit, et c'est aussi le format
   demandé pour ce site (photos en square, bords francs). Le rapport 4/5 est
   réservé aux photos de personnes, où un carré couperait les silhouettes. */
.media-tile { position: relative; overflow: hidden; aspect-ratio: 1 / 1; }
.media-tile--portrait { aspect-ratio: 4 / 5; }
.media-tile > img { width: 100%; height: 100%; object-fit: cover; }

/* --------------------------------------------------------------------------
   Respiration des photos au survol (règle Tanguy, 10/09/2026)

   Chaque photo du site s'anime doucement quand la souris passe dessus :
   1,4 s et 4,5 % d'échelle. C'est volontairement lent et faible ; un zoom
   rapide ou marqué lit « diaporama », pas « demeure ».

   ⚠️ Deux familles de photos sont exclues, et c'est technique, pas
   esthétique : celles que GSAP déplace lui-même (`.parallax-img`, le cadre du
   zoom-settle) écrivent leur transformation en ligne, qui l'emporte sur toute
   règle CSS, et les slides d'un slider, où le survol entrerait en concurrence
   avec le fondu d'un visuel à l'autre.
   -------------------------------------------------------------------------- */
.mask-media:not(.parallax-wrap):not(.slider-viewport):not(.zoom-frame) { overflow: hidden; }

.media-tile > img,
.gallery-item img,
.tile-media img,
.media-duo-inset img,
.mask-media:not(.parallax-wrap):not(.slider-viewport):not(.zoom-frame) > img:not(.parallax-img) {
  transition: transform 1.4s var(--ease);
}
.media-tile:hover > img,
.tile:hover .tile-media img,
.media-duo-inset:hover img,
.mask-media:not(.parallax-wrap):not(.slider-viewport):not(.zoom-frame):hover > img:not(.parallax-img) {
  transform: scale(1.045);
}
/* La galerie garde un survol un peu plus franc : ce sont des vignettes que
   l'on parcourt, le retour visuel doit être net. */
.gallery-item:hover img { transform: scale(1.06); }

/* Le cadre d'une planche suit sa photo : le filet d'or reste collé au bord. */
.framed--plate .mask-media { overflow: hidden; }

@media (hover: none) {
  .media-tile > img, .gallery-item img, .tile-media img, .media-duo-inset img,
  .mask-media > img { transition: none; }
}
.grid-4 { display: grid; grid-template-columns: repeat(4, 1fr); gap: clamp(1.6rem, 3vw, 2.4rem); }

/* Parallax : le cadre garde sa taille, la photo dérive dedans.
   Amplitude ±12 % (et non ±20) : avec l'image à height 150 % / top -25 %,
   c'est ce qui garantit que le cadre reste couvert aux deux extrêmes du
   scroll. ±20 laissait apparaître un mince liseré de fond. */
.parallax-wrap {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-media);
  height: clamp(300px, 30vw, 430px);
}
.parallax-wrap .parallax-img {
  position: absolute; top: -8%; left: 0;
  width: 100%; height: 116%;
  object-fit: cover;
  will-change: transform;
}
.parallax-wrap--tall { height: clamp(360px, 40vw, 560px); }

/* Photo pleine largeur, bords francs assumés */
.photo-band {
  position: relative;
  aspect-ratio: 21 / 10;
  overflow: hidden;
  background: var(--ink);
}
.photo-band > img { width: 100%; height: 100%; object-fit: cover; }
/* Une bande photo pleine largeur porte un parallax, comme la bannière d'une
   page intérieure : l'image y est surdimensionnée et déjà rognée par le
   format, ce qui est la condition du procédé. Ce parallax de composant ne
   compte pas dans les deux parallax de contenu de la page d'accueil. */
.photo-band > .parallax-img {
  position: absolute; top: -8%; left: 0;
  width: 100%; height: 116%;
  object-fit: cover;
  will-change: transform;
}
.photo-band--slim { aspect-ratio: 32 / 10; }

/* Le bandeau : une bande volontairement fine, qui sert de respiration entre
   deux sections plutôt que de support à un texte. 40/10 donne 360 px sur un
   écran de 1 440, soit 40 % de la fenêtre : assez pour que la photo existe,
   assez peu pour rester une transition (demande de Tanguy, 10/09/2026). */
.photo-band--bandeau { aspect-ratio: 40 / 10; }

/* Parallax appuyé, réservé au bandeau. L'amplitude d'un parallax est bornée
   par le surdimensionnement de l'image : à 116 % de hauteur, on ne peut pas
   dériver de plus de 8 % sans découvrir le fond. Pour aller à ±12 %, l'image
   monte à 150 % et se décale de -25 %, ce qui laisse 25 % de marge de chaque
   côté. C'est sur une bande fine que le procédé se voit le mieux : la fenêtre
   de lecture est courte, donc le déplacement relatif est grand. */
.photo-band--parallax-fort > .parallax-img { top: -25%; height: 150%; }

/* --------------------------------------------------------------------------
   ⚠️ POSITION DE DÉPART DU PARALLAX, POSÉE ICI ET PAS AILLEURS

   C'est la correction d'un saut d'image au changement de page, signalé par
   Tanguy le 10/09/2026 : « des bugs de saut d'image sur certains hero de page
   quand je passe d'une page à l'autre ».

   La mécanique : aucune règle ne donnait de transformation de départ à
   `.parallax-img`. Le premier affichage posait donc l'image à 0, puis
   `gsap.fromTo` l'envoyait à -5 % dès que le script tournait, soit un saut de
   35 px sur une bannière de 600 px. Sur un chargement ordinaire on le devine à
   peine ; avec la transition inter-documents, ce premier affichage est
   précisément ce qui est composité pendant le changement de page, et le saut
   se voit en plein milieu.

   Deux points à ne pas défaire :

   1. La règle N'EST PAS conditionnée à `.anim`. Cette classe est posée par
      script.js lui-même, donc trop tard : elle arriverait après le premier
      affichage, ce qui est exactement le problème que l'on corrige. Sans
      script, ou sous `prefers-reduced-motion`, l'image reste à -5 % et le
      cadre reste couvert (elle est surdimensionnée à 116 % et décalée de
      -8 %), donc rien ne se voit.
   2. Le `y: 0` dans LES DEUX états du `fromTo` (voir js/script.js) est
      obligatoire à partir de maintenant. GSAP lit la transformation calculée
      en pixels, la range dans sa composante `y`, puis applique `yPercent`
      PAR-DESSUS : sans ce `y: 0`, on additionnerait -35 px et -5 %. C'est le
      même piège que celui qui rendait tous les titres invisibles.
   -------------------------------------------------------------------------- */
.parallax-img { transform: translateY(-5%); }
.photo-band--parallax-fort > .parallax-img { transform: translateY(-12%); }
.photo-band .hero-scrim { z-index: 2; }
.photo-band--veil .hero-scrim {
  background: linear-gradient(rgba(37, 44, 54, 0.52), rgba(37, 44, 54, 0.64));
}
.photo-band-caption {
  position: absolute; inset: 0;
  z-index: 3;
  display: flex; flex-direction: column; justify-content: center;
  width: min(100% - 6rem, 1100px);
  margin-inline: auto;
  color: var(--cream);
}
.photo-band-caption h2 { color: var(--cream); max-width: 20em; text-shadow: 0 2px 22px rgba(28, 34, 42, 0.45); }
.photo-band-caption p { color: rgba(244, 239, 229, 0.94); max-width: 38em; margin-top: 1.8rem; text-shadow: 0 1px 14px rgba(28, 34, 42, 0.5); }
.photo-band-caption--center { text-align: center; align-items: center; }

/* Effet pin : la photo est entièrement figée le temps d'une traversée.
   Désactivé au doigt : un pin fige la page pendant qu'on scrolle, ce qui est
   un effet à la souris et un blocage au doigt (« ça s'arrête net »). */
.pin-wrap { position: relative; height: 100svh; overflow: hidden; background: var(--ink); }
.pin-wrap > img { width: 100%; height: 100%; object-fit: cover; }
.pin-caption {
  position: absolute; inset: 0;
  z-index: 3;
  display: flex; align-items: center; justify-content: center;
  text-align: center;
  padding: 2rem;
  color: var(--cream);
}
.pin-caption .inner { width: min(100%, 44em); }
.pin-caption h2 { color: var(--cream); text-shadow: 0 2px 22px rgba(28, 34, 42, 0.45); }
.pin-caption p { color: rgba(244, 239, 229, 0.94); margin-top: 2rem; text-shadow: 0 1px 14px rgba(28, 34, 42, 0.5); }
.pin-wrap .hero-scrim {
  background: linear-gradient(to bottom, rgba(37, 44, 54, 0.42), rgba(37, 44, 54, 0.58));
}

/* Contre-scroll texte/photo, page À propos uniquement */
.scroll-split-text, .scroll-split-media { will-change: transform; }

/* Zoom-settle, page Réceptions uniquement : la photo arrive un peu zoomée et
   se règle à 1.0 en montant au centre. L'échelle initiale est posée par GSAP,
   pas en CSS, pour que sans GSAP l'image reste à l'échelle 1. */
.zoom-frame {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-media);
  height: clamp(280px, 32vw, 460px);
}
.zoom-frame img { width: 100%; height: 100%; object-fit: cover; will-change: transform; }

/* Variante : la photo garde SON format, le cadre s'adapte. Posee sur la
   ceremonie de la page Receptions (demande de Tanguy, 30/09/2026) : en hauteur
   fixe, le recadrage coupait la toiture du chateau, qui est le sujet. */
.zoom-frame--entiere { height: auto; }
.zoom-frame--entiere img { height: auto; }

/* Rapprochement de deux cartes : mouvement uniquement vers l'intérieur,
   jamais vers l'extérieur, sinon les cartes dépassent leurs bords sur les
   largeurs proches de celle du conteneur et créent un scroll horizontal. */
.converge > * { will-change: transform; }

/* Duo de photos superposées au coin. Ici sans ombre (parti pris du site) :
   c'est un filet crème de 6 px qui détache l'incrustation. */
.media-duo { position: relative; }
.media-duo .media-duo-main { height: clamp(320px, 33vw, 460px); }
.media-duo-inset {
  position: absolute; bottom: -26px;
  width: 44%; max-width: 232px;
  aspect-ratio: 4 / 5;
  overflow: hidden;
  border: 6px solid var(--white);
  outline: 1px solid var(--line);
}
.media-duo--inset-left .media-duo-inset { left: -26px; }
.media-duo--inset-right .media-duo-inset { right: -26px; }
.media-duo-inset img { width: 100%; height: 100%; object-fit: cover; }
.section--cream .media-duo-inset { border-color: var(--cream); }

/* Duo à photo ENTIÈRE : la grande photo garde son cadrage d'origine au lieu
   d'être rognée à la hauteur du conteneur (demande de Tanguy, 10/09/2026,
   « mets la grande photo au format original, rectangle »).

   C'est aussi la règle de fond de la DA : on ne rogne pas une photo de
   contenu pour la faire entrer dans un cadre. Le duo standard la rognait
   parce que `.media-duo-main` impose une hauteur, condition du parallax.

   ⚠️ La contrepartie est assumée : une photo entière ne peut PAS porter de
   parallax. Le procédé demande une image surdimensionnée et déjà rognée par
   son cadre, sinon la dérive découvre le fond. Le générateur retire donc la
   classe `parallax-img` sur cette variante, ce qui évite aussi de créer un
   déclencheur pour rien. */
.media-duo--plein .media-duo-main { height: auto; }
.media-duo--plein .media-duo-main > img {
  position: static;
  width: 100%; height: auto;
  top: auto; left: auto;
  object-fit: unset;
}

/* --------------------------------------------------------------------------
   13. Bandeau de preuve à chiffres
   Chaque valeur vient d'une donnée confirmée dans Infos.md : 1861 (date
   d'achèvement), 34 hectares, 96 couchages, 4,6 sur 5 pour 82 avis Google.
   Aucun chiffre n'est estimé.
   -------------------------------------------------------------------------- */
/* La bande de chiffres est un ruban, pas une section : elle se lit d'un coup
   d'œil entre le hero et le premier texte. Sa hauteur est donc réglée à part
   des 180 px de respiration du reste du site (Tanguy, 10/09/2026). */
.stats-band { padding-block: 66px; }

.stats-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 2.5rem;
}
.stat-item {
  display: flex; flex-direction: column; gap: 0.9rem;
  padding-left: 1.8rem;
  border-left: 1px solid var(--line);
}
.stat-line { display: flex; align-items: baseline; gap: 0.5rem; flex-wrap: wrap; }
.stat-unit {
  font-family: var(--font-title);
  font-weight: 400;
  font-size: 12px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--gold);
}
.stat-value {
  font-family: var(--font-title);
  font-weight: 300;
  font-size: clamp(2.1rem, 3.4vw, 3.1rem);
  letter-spacing: 0.06em;
  line-height: 1;
  color: var(--cream);
}
.section--cream .stat-value, .section--white .stat-value { color: var(--ink); }
.stat-label {
  font-family: var(--font-title);
  font-weight: 400;
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(244, 239, 229, 0.7);
}
.section--cream .stat-label, .section--white .stat-label { color: var(--muted); }

/* --------------------------------------------------------------------------
   14. Cartes, listes d'espaces, témoignages
   -------------------------------------------------------------------------- */
.card {
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: var(--radius-media);
  padding: clamp(2rem, 3.4vw, 3rem);
}
.section--white .card { background: var(--cream); }
.card h3 { margin-bottom: 1.8rem; }
.card ul { list-style: none; margin: 0; padding: 0; }
.card li {
  position: relative;
  padding: 0.7rem 0 0.7rem 1.6rem;
  border-bottom: 1px solid var(--line-soft);
  font-size: 16px;
}
.card li:last-child { border-bottom: 0; }
.card li::before {
  content: '';
  position: absolute; left: 0; top: 1.5rem;
  width: 7px; height: 1px;
  background: var(--gold);
}

/* Liste des espaces du domaine : un titre, un texte, un filet. */
.space-list { display: grid; gap: 0; }
.space-item {
  display: grid;
  grid-template-columns: 4rem 1fr;
  gap: clamp(1.4rem, 3vw, 3rem);
  padding: 2.6rem 0;
  border-top: 1px solid var(--line-soft);
}
.space-item:last-child { border-bottom: 1px solid var(--line-soft); }
.space-num {
  font-family: var(--font-title);
  font-weight: 300;
  font-size: 12px;
  letter-spacing: 0.2em;
  color: var(--gold);
  padding-top: 0.5rem;
}
.space-item h3 { margin-bottom: 1.1rem; }
.space-item--plain { grid-template-columns: 1fr; }

/* Aperçu des espaces en vignettes carrées (page d'accueil).
   La vignette est un lien : `color: inherit` annule la couleur d'or des
   liens, qui teindrait le titre et le texte de la carte. */
.tile { display: block; position: relative; color: inherit; }
.tile h3 { transition: color 0.4s var(--ease); }
.tile:hover h3 { color: var(--gold); }
.tile-media { position: relative; aspect-ratio: 1 / 1; overflow: hidden; background: var(--ink); }
.tile-media img { width: 100%; height: 100%; object-fit: cover; }

.tile h3 { margin: 1.5rem 0 0.7rem; font-size: 17px; letter-spacing: 0.14em; }
.tile p { font-size: 15.5px; line-height: 1.65; }

/* Témoignages */
.quote {
  border-left: 1px solid var(--gold);
  padding: 0.4rem 0 0.4rem 2rem;
}
.quote p { font-size: 17.5px; line-height: 1.75; }
.quote footer {
  margin-top: 1.4rem;
  font-family: var(--font-title);
  font-weight: 400;
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--gold);
}
.quote-grid { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(2.5rem, 5vw, 4.5rem); }

/* Tableaux : capacités, chambres. Filets seulement, aucun fond alterné. */
.table { width: 100%; border-collapse: collapse; font-size: 16px; }
.table th, .table td {
  text-align: left;
  padding: 1.05rem 1.2rem 1.05rem 0;
  border-bottom: 1px solid var(--line-soft);
  vertical-align: top;
}
.table th {
  font-family: var(--font-title);
  font-weight: 400;
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--gold);
}
.table td:first-child { color: var(--ink); width: 42%; }
.table td strong { font-weight: 400; color: var(--ink); }
.table--rooms td:first-child { width: 46%; }

/* Questions/réponses de la page d'accueil : deux colonnes, parce que six
   questions à la file font une colonne bien plus haute que n'importe quelle
   photo posée à côté. */
.qa-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0 clamp(2.5rem, 6vw, 5rem); }

/* Questions/réponses de la page d'accueil */
.qa-item { padding: 2.2rem 0; border-top: 1px solid var(--line-soft); }
.qa-item:last-child { border-bottom: 1px solid var(--line-soft); }
.qa-item h3 { font-size: 17px; letter-spacing: 0.12em; margin-bottom: 1rem; }
.qa-item p { font-size: 16px; }

/* --------------------------------------------------------------------------
   15. Slider de contenu (hébergement)
   `width: 100%` est obligatoire : les slides sont en position absolue, donc
   le slider n'a aucune largeur de contenu propre, et combiné à un
   margin-inline auto il s'effondre à 0 px sans aucune erreur.
   Chaque slider porte ses deux flèches en plus du compteur : le visiteur doit
   pouvoir passer les photos lui-même (zone tactile 44 px minimum).
   -------------------------------------------------------------------------- */
.slider { position: relative; width: 100%; }
.slider-viewport {
  position: relative;
  width: 100%;
  aspect-ratio: 3 / 2;
  overflow: hidden;
  background: var(--ink);
}
.slider-slide {
  position: absolute; inset: 0;
  opacity: 0;
  transition: opacity 0.9s var(--ease);
}
.slider-slide.is-active { opacity: 1; }
.slider-slide img { width: 100%; height: 100%; object-fit: cover; }

.slider-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  margin-top: 1.4rem;
}
.slider-arrows { display: flex; gap: 0.6rem; }
.slider-arrow {
  width: 46px; height: 46px;
  display: grid; place-items: center;
  border: 1px solid var(--line);
  background: transparent;
  color: var(--ink);
  cursor: pointer;
  transition: background-color 0.4s var(--ease), color 0.4s var(--ease), border-color 0.4s var(--ease);
}
.slider-arrow:hover { background: var(--gold); border-color: var(--gold); color: var(--white); }
.slider-arrow svg { width: 15px; height: 15px; }

/* --------------------------------------------------------------------------
   Compteur pilote du hero (règle Tanguy, 10/09/2026)

   Le compteur du hero affichait la position sans permettre d'y toucher : le
   visiteur voyait « 01 / 04 » et n'avait aucun moyen de reprendre la main sur
   un défilement automatique de 6,8 s. Il devient un vrai pilote : deux
   flèches, et le chiffre courant lui-même cliquable, ce que Tanguy avait
   proposé.

   Les flèches sont NUES sur le hero, sans leur carré de 46 px bordé : posé sur
   une photo, ce carré fait bouton d'interface et casse le calme du visuel. Le
   chevron seul suffit, avec une zone touchable de 44 px qui, elle, reste
   entière.
   -------------------------------------------------------------------------- */
.slider-count--pilote { display: flex; align-items: center; gap: 0.35rem; }
.slider-arrow--nu {
  width: 44px; height: 44px;
  border: 0;
  background: transparent;
  color: var(--cream);
  opacity: 0.72;
  transition: opacity 0.4s var(--ease), color 0.4s var(--ease);
}
.slider-arrow--nu:hover { opacity: 1; color: var(--gold); background: transparent; border: 0; }
.slider-arrow--nu svg { width: 18px; height: 18px; }

/* Le chiffre courant est un bouton, mais il doit continuer à se lire comme un
   chiffre : on annule l'apparence de bouton, on ne garde que le curseur. */
button.slider-count-current {
  border: 0;
  background: transparent;
  /* La zone touchable doit atteindre 44 px comme les flèches : à 22 x 27 px
     elle était deux fois trop petite au doigt. Le rembourrage l'agrandit sans
     changer la taille du chiffre, et l'écart du conteneur est resserré
     d'autant pour que l'ensemble garde sa largeur. */
  padding: 0.82rem 0.72rem;
  cursor: pointer;
  font: inherit;
  color: inherit;
  letter-spacing: inherit;
  transition: color 0.4s var(--ease);
}
button.slider-count-current:hover { color: var(--gold); }
.slider-arrow--nu:focus-visible,
button.slider-count-current:focus-visible {
  outline: 1px solid var(--gold);
  outline-offset: 3px;
  opacity: 1;
}
.slider .slider-count { color: var(--muted); }
.slider .slider-count-track { background: var(--line-soft); }

/* Carton d'attente pour un logement sans photographie. Jamais la photo d'un
   autre logement : un slider qui montre l'appartement Sisley sous le titre
   « Saint-Émilion » est un mensonge que la visite découvre. */
.awaiting {
  aspect-ratio: 3 / 2;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  gap: 1.6rem;
  background: var(--cream);
  border: 1px solid var(--line);
  text-align: center;
  padding: 2rem;
}
.section--cream .awaiting { background: var(--white); }
.awaiting img { width: 92px; opacity: 0.5; }
.awaiting span {
  font-family: var(--font-title);
  font-weight: 400;
  font-size: 11px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--muted);
}

/* Onglets des plans d'étage */
.tabs { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-bottom: 2.5rem; }
.tab {
  padding: 0.85rem 1.4rem;
  border: 1px solid var(--line);
  background: transparent;
  color: var(--body);
  font-family: var(--font-title);
  font-weight: 400;
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  cursor: pointer;
  transition: background-color 0.4s var(--ease), color 0.4s var(--ease);
}
.tab[aria-selected="true"] { background: var(--ink); border-color: var(--ink); color: var(--cream); }
.tab-panel { background: var(--white); border: 1px solid var(--line); padding: clamp(1rem, 2.5vw, 2rem); }
/* Les plans ont leur propre fond très clair : un panneau crème derrière eux
   ferait un rectangle dans le rectangle. Le panneau reste blanc, et c'est le
   filet d'or qui le détache du fond de section. */
.section--cream .tab-panel { background: var(--white); }
.plan-legend {
  display: flex; flex-wrap: wrap; gap: 1.4rem;
  margin-top: 1.6rem;
  font-family: var(--font-title);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
}

/* --------------------------------------------------------------------------
   16. Galerie : zoom au survol, CSS pur.
   Le cadrage (hauteur, overflow) est porté par .gallery-item, jamais par
   l'<img> : sinon le zoom débordait visuellement de sa case.
   Écarté volontairement : le passage noir et blanc vers couleur au survol.
   C'est daté, et ça abîme la lumière dorée qui est l'atout du lieu.
   -------------------------------------------------------------------------- */
.gallery-nav {
  display: flex; flex-wrap: wrap; gap: 0.4rem 1.8rem;
  padding-block: 1.6rem;
  border-bottom: 1px solid var(--line-soft);
  margin-bottom: 4rem;
}
.gallery-nav a {
  font-family: var(--font-title);
  font-weight: 400;
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--muted);
  transition: color 0.4s var(--ease);
}
.gallery-nav a:hover { color: var(--gold); }

.gallery-section { margin-bottom: clamp(4rem, 8vw, 7rem); scroll-margin-top: 96px; }
.gallery-section h2 { font-size: clamp(20px, 2vw, 28px); margin-bottom: 2.4rem; }

.gallery-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.9rem; }
.gallery-item { position: relative; overflow: hidden; border-radius: var(--radius-media); aspect-ratio: 3 / 2; }
.gallery-item img { width: 100%; height: 100%; object-fit: cover; }

.gallery-item--tall { aspect-ratio: 2 / 3; grid-row: span 2; }

/* --------------------------------------------------------------------------
   17. Formulaire
   -------------------------------------------------------------------------- */
.contact-layout { display: grid; grid-template-columns: 1.25fr 1fr; gap: clamp(3rem, 6vw, 6rem); align-items: start; }
.form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 1.5rem 1.8rem; }
.field { display: flex; flex-direction: column; gap: 0.6rem; }
.field--full { grid-column: 1 / -1; }
.field label {
  font-family: var(--font-title);
  font-weight: 400;
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--muted);
}
.field input, .field select, .field textarea {
  font-family: var(--font-body);
  font-size: 16px;
  color: var(--ink);
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--line);
  border-radius: 0;
  padding: 0.75rem 0;
  transition: border-color 0.4s var(--ease);
  -webkit-appearance: none;
  appearance: none;
}
.field select { background-image: none; }
.field textarea { min-height: 120px; resize: vertical; border: 1px solid var(--line); padding: 0.9rem 1rem; }
.field input:focus, .field select:focus, .field textarea:focus { outline: 0; border-color: var(--gold); }

.field--check { flex-direction: row; align-items: flex-start; gap: 0.9rem; grid-column: 1 / -1; }
.field--check input { width: 17px; height: 17px; flex: 0 0 auto; margin-top: 0.35rem; -webkit-appearance: auto; appearance: auto; accent-color: var(--gold); }
.field--check label { text-transform: none; letter-spacing: 0.01em; font-family: var(--font-body); font-size: 14.5px; color: var(--body); line-height: 1.6; }

.form-feedback {
  display: none;
  grid-column: 1 / -1;
  padding: 1rem 1.2rem;
  border: 1px solid var(--line);
  background: var(--cream);
  font-size: 15px;
}
.section--cream .form-feedback { background: var(--white); }

.contact-aside .info-block { padding-block: 2rem; border-bottom: 1px solid var(--line-soft); }
.contact-aside .info-block:first-child { padding-top: 0; }
.contact-aside h3 { font-size: 15px; letter-spacing: 0.16em; margin-bottom: 1.2rem; }
.contact-aside address { font-style: normal; font-size: 16px; line-height: 1.9; }
.contact-aside a { color: var(--ink); }
.contact-aside a:hover { color: var(--gold); }
.contact-aside ul { list-style: none; margin: 0; padding: 0; font-size: 16px; }
.contact-aside li { display: flex; justify-content: space-between; gap: 1.5rem; padding: 0.5rem 0; }
.contact-aside li span:last-child { color: var(--ink); white-space: nowrap; }

.map-frame { border: 1px solid var(--line); line-height: 0; }
.map-frame iframe { width: 100%; height: 460px; border: 0; display: block; filter: saturate(0.78) contrast(1.02); }

/* --------------------------------------------------------------------------
   18. Pied de page
   -------------------------------------------------------------------------- */
.site-footer { background: var(--ink); color: rgba(244, 239, 229, 0.72); padding-block: 6rem 2.5rem; }
.footer-grid { display: grid; grid-template-columns: 1.2fr 1fr 1fr 1.2fr; gap: 3rem; }
.footer-logo img { width: 172px; }
.footer-grid h4 { color: var(--cream); margin-bottom: 1.5rem; }
.site-footer ul { list-style: none; margin: 0; padding: 0; }
.site-footer li { margin-bottom: 0.75rem; }
.site-footer a { color: rgba(244, 239, 229, 0.72); font-size: 15px; transition: color 0.35s var(--ease); }
.site-footer a:hover { color: var(--gold); }
.site-footer address { font-style: normal; font-size: 15px; line-height: 1.9; }
.footer-bottom {
  display: flex; flex-wrap: wrap; gap: 1rem 2rem;
  justify-content: space-between; align-items: center;
  margin-top: 4.5rem; padding-top: 2rem;
  border-top: 1px solid rgba(244, 239, 229, 0.14);
  font-size: 13px;
}
.footer-bottom a { font-size: 13px; }
.footer-legal { display: flex; flex-wrap: wrap; gap: 1.5rem; }

/* --------------------------------------------------------------------------
   19. Bandeau cookies, aux couleurs du lieu
   -------------------------------------------------------------------------- */
.cookie-banner {
  position: fixed;
  left: 1.5rem; right: 1.5rem; bottom: 1.5rem;
  z-index: 400;
  background: var(--ink);
  color: rgba(244, 239, 229, 0.9);
  border: 1px solid var(--line);
  padding: 1.2rem 1.6rem;
  display: flex; align-items: center; justify-content: space-between;
  gap: 1.5rem; flex-wrap: wrap;
  transform: translateY(160%);
  transition: transform 0.6s var(--ease);
}
.cookie-banner.is-visible { transform: translateY(0); }
.cookie-banner p { margin: 0; font-size: 14px; max-width: 660px; }
.cookie-banner a { color: var(--cream); text-decoration: underline; }
.cookie-banner .btn { border-color: rgba(244, 239, 229, 0.6); color: var(--cream); }
.cookie-banner .btn::after { background: var(--cream); }
.cookie-banner .btn:hover { color: var(--ink); }

/* --------------------------------------------------------------------------
   20. Pages légales
   -------------------------------------------------------------------------- */
.legal { padding-block: calc(var(--header-h) + 5rem) var(--section-y); }
.legal h2 { font-size: clamp(19px, 1.8vw, 26px); margin-top: 3.5rem; margin-bottom: 1.4rem; }
.legal h3 { font-size: 15px; letter-spacing: 0.14em; margin-top: 2.2rem; margin-bottom: 0.8rem; }
.legal ul { padding-left: 1.2rem; }
.legal li { margin-bottom: 0.5rem; }
/* Sur une page légale il n'y a pas de hero : le header doit être lisible dès
   le chargement, donc l'état scrollé est posé d'emblée par le JS. */


/* --------------------------------------------------------------------------
   23. Transitions d'une page à l'autre : RETIRÉES le 11/09/2026

   Le site portait l'API View Transitions en mode inter-documents
   (`@view-transition { navigation: auto }`, un fondu sur la racine, le header
   nommé, et un morphing vignette → bannière en JavaScript). Tout a été retiré,
   sur décision de Tanguy, après avoir MESURÉ que la transition ne démarrait
   jamais.

   Le relevé, fait avec un journal en `sessionStorage` qui survit aux
   changements de page, dans le navigateur intégré ET dans le Chrome 152 de
   Tanguy : `pageswap` se déclenche bien, mais avec `viewTransition` à **null**,
   et `pagereveal` ne se déclenche jamais. Et cela alors que toutes les
   conditions documentées étaient réunies : at-rule parsée en
   `CSSViewTransitionRule` avec `navigation: auto`, `prefers-reduced-motion`
   faux, document de premier niveau, même origine, événements supportés, aucun
   `no-store` dans les en-têtes, refus identique page déjà en cache, et la
   seconde `@view-transition` imbriquée dans un `@media` innocentée par un test
   dédié.

   Ce qui portait vraiment l'effet, et qui reste : l'animation d'arrivée de la
   bannière, en CSS pur, plus haut dans ce fichier. Elle se joue à chaque
   affichage et ne dépend de rien.

   ⚠️ Ne pas remettre cette API sans l'avoir d'abord mesurée sur le site réel.
   Elle a coûté trois tours de faux diagnostics parce qu'elle échoue en
   silence, et qu'un effet invisible ressemble à un effet mal réglé.
   -------------------------------------------------------------------------- */

/* --------------------------------------------------------------------------
   21. États de départ des animations
   Tous conditionnés à `html.anim`, et cette classe est posée par
   `js/script.js` lui-même, jamais par le script en ligne du <head>.

   La nuance est la seule chose qui protège vraiment : l'état caché doit
   dépendre du fichier qui le lève. Si `script.js` n'arrive pas (404, cache
   périmé, extension qui le bloque), la classe n'est jamais posée et tout le
   site s'affiche en clair. Avec une classe posée dans le <head>, le même
   incident laissait tous les titres invisibles.

   C'est la première ligne de défense ; le filet de sécurité du ticker gelé
   (voir js/script.js) est la seconde.
   -------------------------------------------------------------------------- */
/* ⚠️ ICI, JAMAIS DE `transform` EN CSS SUR UN ÉLÉMENT ANIMÉ PAR GSAP.
   Ces états de départ n'utilisent que `opacity`, et c'est une correction, pas
   un choix de style. Deux pannes, toutes deux silencieuses :

   1. GSAP lit la transformation déjà calculée de l'élément et la range dans
      sa composante `y`, puis applique `yPercent` PAR-DESSUS. Un titre posé à
      118 % en CSS arrivait donc à 118 % + 0 %, soit toujours sous son masque.
   2. Quand une animation se termine à la position neutre, GSAP retire sa
      déclaration `transform` en ligne, puisqu'elle ne sert plus à rien. La
      règle CSS reprend alors la main et le titre replonge sous son masque,
      après s'être affiché correctement.

   Le décalage de départ est donc posé par le `gsap.fromTo` lui-même
   (`immediateRender`), et l'opacité seule empêche le clignotement avant que
   le script ne tourne : GSAP écrit toujours l'opacité en ligne, elle ne peut
   pas entrer en conflit avec la règle CSS. */
html.anim .reveal { opacity: 0; }

/* Arrivée des titres par masque de ligne.
   L'interligne des titres est de 1,05 : sans un peu de fond de boîte, la
   queue du Q et la cédille du Ç de Jost seraient rognées par l'overflow.
   D'où le padding-bottom de 0,14em, repris par une marge négative pour ne
   rien changer à la mise en page, et un yPercent de 118 (et non 105) pour
   que la ligne parte réellement sous son masque malgré ce fond ajouté.

   Le titre entier est masqué en opacité le temps d'être découpé en lignes,
   sinon il apparaît une image en texte plein avant que le script ne passe. */
html.anim .split, html.anim .split-texte { opacity: 0; }
html.anim .split.is-split, html.anim .split-texte.is-split { opacity: 1; }

/* Le masque de ligne sur du texte courant : l'interligne de 1,7 laisse déjà
   la place aux jambages, contrairement à celui d'un titre à 1,05. Le fond de
   boîte du `.split-line` ne gêne pas, il est simplement inutile ici. */
.split-texte .split-line { padding-bottom: 0; margin-bottom: 0; }
.split-line { display: block; overflow: hidden; padding-bottom: 0.14em; margin-bottom: -0.14em; }
.split-line-inner { display: block; will-change: transform; }

/* Révélation des photos par masque qui se lève, jamais par fondu. */
html.anim .mask-media { clip-path: inset(100% 0 0 0); }

@media (prefers-reduced-motion: reduce) {
  html.anim .reveal { opacity: 1; transform: none; }
  html.anim .split, html.anim .split.is-split,
  html.anim .split-texte, html.anim .split-texte.is-split { opacity: 1; }
  html.anim .split-line-inner { transform: none; }
  html.anim .mask-media { clip-path: none; }
  .btn::after, .nav-link::after, .link-line::after,
  .tile-media img, .gallery-item img, .site-header, .logo, .logo img,
  .cookie-banner, .nav-drawer, .nav-scrim, .hero-slide, .slider-slide,
  .slider-count-fill { transition: none !important; }
  html { scroll-behavior: auto; }
}

/* --------------------------------------------------------------------------
   22. Adaptations d'écran
   La bascule vers le menu mobile se fait à 1200 px, et non 1024 : avec sept
   liens et une colonne de logo à largeur garantie, les deux groupes n'ont
   plus la place de tenir sur une ligne en dessous.
   -------------------------------------------------------------------------- */
@media (max-width: 1360px) {
  .header-row { grid-template-columns: 8.5rem 1fr 140px 1fr 8.5rem; gap: 1.4rem; }
  .nav-group { gap: 1.1rem; }
  .nav-link { font-size: 11px; letter-spacing: 0.13em; }
  .logo { width: 140px; }
}

@media (max-width: 1200px) {
  :root { --header-h: 84px; }
  .header-row { grid-template-columns: auto 1fr auto; gap: 1rem; }
  .nav-group, .header-cta { display: none; }
  .lang-switch { justify-self: start; }
  .logo { justify-self: center; width: 132px; height: 64px; }
  .logo-lockup { height: 64px; }
  .logo-emblem { height: 42px; }
  html.is-scrolled .logo { height: 42px; }

  .nav-toggle {
    display: flex; flex-direction: column; justify-content: center; gap: 5px;
    justify-self: end;
    width: 46px; height: 46px;
    padding: 0 8px;
    background: transparent; border: 0; cursor: pointer;
  }
  .nav-toggle span {
    display: block; height: 1px; width: 100%;
    background: var(--cream);
    transition: transform 0.4s var(--ease), opacity 0.3s var(--ease), background-color 0.55s var(--ease);
  }
  html.is-scrolled .nav-toggle span { background: var(--ink); }
  html.nav-open .nav-toggle span { background: var(--ink); }
  html.nav-open .nav-toggle span:nth-child(1) { transform: translateY(6px) rotate(45deg); }
  html.nav-open .nav-toggle span:nth-child(2) { opacity: 0; }
  html.nav-open .nav-toggle span:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }
}

@media (max-width: 1024px) {
  :root { --section-y: 118px; --frame-gap: 16px; }
  .qa-grid { grid-template-columns: 1fr; }
  .grid-2, .grid-2--wide-left, .grid-2--wide-right { grid-template-columns: 1fr; gap: 2.6rem; }
  .grid-2 .col-text { max-width: none; }
  .order-first { order: 0; }
  .grid-3 { grid-template-columns: repeat(2, 1fr); }
  .grid-4 { grid-template-columns: repeat(2, 1fr); }
  .stats-band { padding-block: 54px; }
  .stats-grid { grid-template-columns: repeat(2, 1fr); gap: 2.2rem 1.8rem; }
  .quote-grid { grid-template-columns: 1fr; }
  .contact-layout { grid-template-columns: 1fr; }
  .footer-grid { grid-template-columns: 1fr 1fr; gap: 2.6rem; }
  .gallery-grid { grid-template-columns: repeat(2, 1fr); }
  .hero-count { bottom: 5rem; right: 3rem; }
  .hero-content { margin-bottom: 5.5rem; }
  /* Le duo revient dans le flux : en pile, l'incrustation en négatif
     déborderait de l'écran et créerait un scroll horizontal. */
  .media-duo-inset { width: 38%; bottom: -18px; }
  .media-duo--inset-left .media-duo-inset { left: 14px; }
  .media-duo--inset-right .media-duo-inset { right: 14px; }
}

@media (max-width: 720px) {
  :root { --section-y: 92px; --frame-gap: 12px; }
  body { font-size: 16.5px; }
  .container, .container--narrow { width: min(100% - 2.4rem, 1200px); }
  .hero { height: 100svh; min-height: 540px; }

  /* ⚠️ Le texte et le cadre intérieur ne doivent jamais se toucher, et sur
     téléphone ils se touchaient (signalé par Tanguy le 10/09/2026 : « le texte
     est collé au rectangle blanc »).

     Mesuré avant correction, en 375 px : le cadre à 14 px du bord, le contenu
     à 19,2 px, donc 5 px d'air, et le `h1` de l'accueil affleurant le filet à
     0 px. Sur les bannières de page, c'était pire : la règle
     `.page-hero .hero-frame` (spécificité 0,2,0) échappait à celle du bloc
     mobile (0,1,0), son cadre restait donc à 20 px pendant que le contenu
     était à 19,2, et le titre passait 6 px À L'EXTÉRIEUR du filet. Sur les
     huit pages intérieures et les deux langues.

     D'où les deux corrections conjointes : le cadre se resserre à 12 px, le
     texte se retire à 4,4rem, et les DEUX cadres sont visés nommément pour
     que la spécificité ne rejoue pas le tour. Il reste 23 px de respiration
     de chaque côté. */
  .hero-frame,
  .page-hero .hero-frame { top: var(--header-h); right: 12px; bottom: 12px; left: 12px; }
  .hero-content { width: min(100% - 4.4rem, 1200px); margin-bottom: 4.5rem; }

  /* La hiérarchie du hero se resserre avec la largeur : à 34 px de titre et
     19 px d'accroche, le bloc de texte occupait la moitié basse de l'écran et
     l'accroche tenait sur trois lignes pleine largeur, ce qui écrasait la
     photo. Le titre passe à 30 px, l'accroche à 16,5 px, la même valeur que
     le texte courant du site en mobile. */
  .hero h1 { font-size: 30px; }
  .hero-content .lede { font-size: 16.5px; line-height: 1.6; margin-top: 1.4rem; }

  /* Le bouton prend toute la colonne : à 248 px dans une colonne de 337, il
     laissait 94 px de vide à sa droite alors que le texte au-dessus courait
     jusqu'au filet, et l'ensemble penchait à gauche. Pleine largeur, il
     s'aligne sur le texte et sa cible tactile double. */
  .hero-content .btn { width: 100%; justify-content: center; }

  .hero-count { right: 1.2rem; bottom: 1.6rem; }
  .hero-count .slider-count-track { width: 56px; }
  .page-hero { height: clamp(340px, 62vw, 460px); }
  .page-hero-content { width: min(100% - 4.4rem, 1200px); margin-bottom: 3rem; }
  .page-hero-content .page-hero-ligne { font-size: 15.5px; margin-top: 0.45rem; }
  .photo-band { aspect-ratio: 4 / 5; }
  .photo-band--slim { aspect-ratio: 4 / 3; }
  /* Le bandeau reste un bandeau sur téléphone : en 4/5 comme les autres
     bandes, il deviendrait un bloc plein écran et perdrait sa fonction de
     transition. 16/10 donne 234 px sur 375 de large. */
  .photo-band--bandeau { aspect-ratio: 16 / 10; }

  /* Les cadres décalés se resserrent sur téléphone : les débords calculés
     pour une colonne de 600 px ouvriraient un défilement horizontal sur 375. */
  .framed--decale { max-width: calc(100% - 40px); }
  .framed--decale::after { inset: -12px -12px -12px -30px; }
  .framed--decale-droite::after { inset: -12px -30px -12px -12px; }
  .framed--plein::before { inset: 18px -18px -18px 18px; }
  .framed--plein-gauche::before { inset: 18px 18px -18px -18px; }
  .stack--croise .croise-b { width: 72%; margin-top: -10%; }
  .photo-band-caption { width: min(100% - 2.4rem, 1100px); }
  /* Sur téléphone le pin est désactivé : la section devient une simple photo
     haute. Il lui faut de la place, sinon la légende recouvre toute l'image. */
  /* ⚠️ Jamais `aspect-ratio` avec `min-height` sur un bloc pleine largeur :
     la largeur se déduit alors de la hauteur (600 px en 3/4 donnaient 450 px
     de large pour une fenêtre de 390) et la page prend une barre de
     défilement horizontale sans qu'aucune règle ne le laisse voir. */
  .pin-wrap { height: 80svh; min-height: 560px; aspect-ratio: auto; }
  .pin-caption { padding: 2rem 1.4rem; }
  .pin-caption p { font-size: 15.5px; }
  .grid-3, .grid-4 { grid-template-columns: 1fr; }
  .stats-band { padding-block: 44px; }
  .stats-grid { grid-template-columns: 1fr 1fr; }
  .form-grid { grid-template-columns: 1fr; }
  .space-item { grid-template-columns: 1fr; gap: 0.8rem; }
  .space-num { padding-top: 0; }
  .footer-grid { grid-template-columns: 1fr; }
  /* La galerie repasse à deux colonnes sur téléphone. En une seule, ses 166
     vignettes donnaient une page de 42 554 px, soit 52 écrans de défilement :
     personne ne descend jusqu'au bas. À deux colonnes elle en fait la moitié,
     et 163 px de vignette reste le standard d'une galerie sur téléphone. */
  .gallery-grid { grid-template-columns: repeat(2, 1fr); gap: 0.6rem; }
  .gallery-item--tall { grid-row: span 1; aspect-ratio: 3 / 2; }

  /* Les liens de section de la galerie faisaient 19 px de haut, sur une page
     qui n'a que ce moyen de navigation. */
  .gallery-nav { gap: 0 0.6rem; padding-block: 0.8rem; }
  .gallery-nav a { padding: 13px 8px; }
  .dropcap::first-letter { font-size: 4.2em; }
  .media-duo .media-duo-main { height: clamp(280px, 62vw, 400px); }
  .table td:first-child, .table--rooms td:first-child { width: 50%; }
  .map-frame iframe { height: 340px; }
  .contact-aside li { flex-direction: column; gap: 0.1rem; }
  .contact-aside li span:last-child { white-space: normal; }

  /* Cibles tactiles : EN et FR mesuraient 19 x 19 px, très en dessous des
     44 px recommandés, et les deux sont côte à côte. Le texte ne change pas de
     taille, c'est la zone touchable qui s'étend, sans effet sur la hauteur du
     header qui est fixe. */
  .lang-switch a { padding: 13px 5px; }
  .lang-switch { gap: 0.15rem; margin-left: -5px; }

  /* 12 px est trop petit sur téléphone pour une ligne que l'on veut vraiment
     voir lue (la note d'avis Google). */
  .mention { font-size: 13px; }

  /* Le sélecteur de langue du tiroir : deux liens de 20 x 20 px collés l'un à
     l'autre, la mauvaise langue se touche aussi souvent que la bonne. La zone
     touchable passe à 48 px de haut, le retrait négatif garde l'alignement
     avec les liens de navigation au-dessus. */
  .nav-drawer .drawer-lang { margin-left: -10px; gap: 0.2rem; }
  .nav-drawer .drawer-lang a { padding: 14px 10px; }

  /* Le bouton d'envoi prend toute la largeur du formulaire, comme celui du
     hero : à 241 px il flottait au milieu d'une colonne de 337. */
  form .btn { width: 100%; justify-content: center; }
}

/* Impression : on garde le texte, on retire les décors. */
@media print {
  .site-header, .nav-drawer, .nav-scrim, .cookie-banner, .hero, .page-hero,
  .photo-band, .pin-wrap, .site-footer { display: none !important; }
  body { color: #000; font-size: 12pt; }
}

/* --------------------------------------------------------------------------
   Slider groupe par chambre (page Hebergement, dernier etage)
   La liste des chambres pilote les photos. Le nom reste lisible sur la photo
   pour celui qui ne clique pas : c'est ce lien nom/photo qui manquait.
   -------------------------------------------------------------------------- */
.slider-legende {
  position: absolute; left: 0; bottom: 0; margin: 0;
  padding: 0.7rem 1.1rem;
  background: rgba(28, 34, 42, 0.42);
  color: var(--cream);
  font-family: var(--font-title);
  font-size: 12px; letter-spacing: 0.16em; text-transform: uppercase;
  backdrop-filter: blur(6px);
  pointer-events: none;
}
.slider-legende:empty { display: none; }
.room-btn {
  appearance: none; background: none; border: 0; padding: 0;
  font: inherit; color: inherit; text-align: left; cursor: pointer;
  transition: color 0.4s var(--ease);
}
.room-btn strong { font-weight: inherit; }
.room-btn:hover { color: var(--gold); }
.table--rooms tr.is-active td { color: var(--ink); }
.table--rooms tr.is-active .slider-legende:empty { display: none; }
.room-btn { color: var(--gold); }
.table--rooms tr.is-active td:first-child { box-shadow: inset 2px 0 0 var(--gold); padding-left: 0.9rem; }
.room-btn:focus-visible { outline: 1px solid var(--gold); outline-offset: 4px; }
