/* =====================================================================
   Piste 0 - Reproduction FIDÈLE du premier jet Figma (mise au propre)
   Couleurs / typo / tailles / espacements : 100 % tokens.css
   ===================================================================== */

/* ---------- Overrides de piste : wrapper 1136 + zéro coin arrondi ---------- */
:root{
  --radius-xs:0; --radius-sm:0; --radius-md:0; --radius-lg:0;
}
.u-container{max-width:calc(1136px + 2*var(--gutter))} /* contenu réel = 1136 */

/* Fond des champs de saisie : un lin ALLÉGÉ dans le crème de la card. Le lin
   pur (#E9DEC8) alourdissait la card ; 60 % a été essayé, puis 40 % retenu le
   08/09 — la zone de saisie se lit sans que l'aplat pèse sur la card.
   C'est le seul réglage à toucher pour l'ajuster : les champs texte et les
   lignes de convives y puisent tous les deux.
   ⚠️ À 40 % le fond ne se détache de la card qu'à 1,07:1 : il ne délimite
   RIEN à lui seul, c'est le filet bas qui s'en charge. Ne pas le retirer. */
:root{--champ-fond:color-mix(in srgb, var(--lin) 40%, var(--creme))}

/* ---------- Réglages généraux ---------- */
/* défilement doux vers les ancres de la nav (désactivé plus bas en reduced-motion) */
html{scroll-behavior:smooth}
img{max-width:100%;display:block}
/* L'attribut `hidden` ne pose qu'un display:none de niveau navigateur : la
   moindre règle CSS qui déclare un `display` l'annule. Sans cette ligne, un
   élément masqué en HTML mais mis en `display:grid` par le CSS reste visible —
   c'est arrivé au bloc de remerciement du formulaire et aux croix de retrait
   des convives. On rend donc `hidden` prioritaire, une fois pour toutes. */
[hidden]{display:none!important}
a{color:inherit}
:focus-visible{outline:2px solid var(--accent-warm);outline-offset:3px}
.section--olive :focus-visible,
.temoins__strip:focus-visible{outline-color:var(--limoncello)}
/* le footer est passé en greige : le limoncello n'y est plus visible (1,16:1),
   le focus clavier serait invisible. Encre, comme le reste du footer. */
.footer :focus-visible{outline-color:var(--limoncello)}

/* ---------- Titres & overline (script au-dessus du Didot) ---------- */
.section__title{
  font-family:var(--font-display);
  font-weight:400;
  font-size:var(--fs-display-m);
  line-height:var(--lh-title);
  letter-spacing:var(--ls-display);
}
.section__title--invert{color:var(--ink-invert)}

.overline{
  display:block;
  font-family:var(--font-script);
  font-size:clamp(26px, 3vw, 32px);   /* Autography : ~32px, subordonné au titre Didot */
  line-height:1;
  color:var(--accent-warm);
  /* Respiration surtitre -> titre. À l'origine une marge NÉGATIVE de -4px, qui
     compensait le vide laissé sous la ligne de base par les jambages de
     l'Autography ; passée à +4px le 19/08 à la demande de la mariée.
     Un passage à 12px a été essayé le 08/09 puis abandonné : le surtitre
     commençait à décrocher du titre, les deux ne se lisaient plus comme un
     couple. 4px est la valeur retenue, ne pas la remonter sans regarder. */
  margin-bottom:var(--sp-1);
}
.overline--olive{color:var(--olive)}
.overline--gold{color:var(--gold)}
.overline--invert{color:var(--ink-invert)}
.section--olive .overline{color:var(--limoncello)}

/* margin-bottom passé de 48 à 64px le 19/08 : même demande de respiration,
   le bloc surtitre + titre se détache mieux du contenu qui suit. */
.section__head{text-align:center;margin-bottom:var(--sp-10)}
.section__center{text-align:center;display:flex;flex-direction:column;align-items:center}
.section__center .btn{margin-top:var(--sp-6)}
.section__lead{margin-top:var(--sp-5);color:inherit}   /* 16 -> 24px, cf. respiration du 19/08 */
.section__center .section__lead,
.section__head .section__lead{margin-inline:auto}

/* ---------- Rythme des sections ---------- */
.section{padding-block:var(--section-y)}
.section--creme{background:var(--bg);color:var(--ink)}
.section--olive{background:var(--bg-primary);color:var(--ink-invert)}

/* =====================================================================
   Boutons
   ===================================================================== */
.btn{
  display:inline-block;
  font-family:var(--font-body);
  font-size:var(--fs-overline);
  font-weight:500;
  letter-spacing:var(--ls-caps);
  text-transform:uppercase;
  text-decoration:none;
  padding:var(--sp-3) var(--sp-6);
  transition:background var(--dur-fast) var(--ease-out),
             color var(--dur-fast) var(--ease-out),
             border-color var(--dur-fast) var(--ease-out);
  cursor:pointer;
}
.btn--outline{border:1px solid currentColor;background:transparent;color:var(--ink)}
.btn--outline:hover{background:var(--ink);color:var(--bg)}
.btn--solid{border:1px solid var(--ink);background:var(--ink);color:var(--bg)}
.btn--solid:hover{background:transparent;color:var(--ink)}
.btn--light{border-color:var(--ink-invert);background:var(--ink-invert);color:var(--bg-primary)}
.btn--light:hover{background:transparent;color:var(--ink-invert)}

/* =====================================================================
   Placeholders dummy élégants (soft, chauds) — coins carrés
   ===================================================================== */
.placeholder{
  position:relative;
  overflow:hidden;
  background:
    linear-gradient(135deg, color-mix(in srgb, var(--greige) 26%, var(--creme)),
                            color-mix(in srgb, var(--greige) 48%, var(--lin)));
  border:1px solid var(--hairline);
}
.placeholder::after{
  content:"";
  position:absolute;inset:0;
  background-image:repeating-linear-gradient(45deg,
    rgba(46,38,32,.045) 0 1px, transparent 1px 14px);
  pointer-events:none;
}
.placeholder__label{
  position:absolute;inset:0;
  display:flex;align-items:center;justify-content:center;
  font-size:var(--fs-overline);
  letter-spacing:var(--ls-caps);
  text-transform:uppercase;
  color:var(--ink-soft);
  opacity:.55;
}

/* =====================================================================
   NAV STICKY — apparaît au scroll (Figma 8:150)
   ===================================================================== */
.sticky-nav{
  position:fixed;top:0;left:0;right:0;z-index:50;
  background:var(--creme);
  border-bottom:1px solid var(--hairline);
  transform:translateY(-100%);
  visibility:hidden;
  transition:transform var(--dur-mid) var(--ease-out), visibility 0s var(--dur-mid);
}
.sticky-nav.is-visible{
  transform:none;visibility:visible;
  transition:transform var(--dur-mid) var(--ease-out);
}
.sticky-nav__inner{
  max-width:calc(1136px + 2*var(--gutter));
  margin-inline:auto;
  padding-inline:var(--gutter);
  height:56px;
  display:flex;align-items:center;justify-content:center;
  gap:var(--sp-6);
}
.sticky-nav__logo{display:flex;align-items:center;gap:var(--sp-3);color:var(--olive);text-decoration:none}
.sticky-nav__logo .monogram__svg{height:32px;width:auto;aspect-ratio:322/450} /* ratio du glyphe AJ */
/* lockup mobile + burger : mobile uniquement */
.sticky-nav__lockup,.sticky-nav__burger{display:none}
.sticky-nav__list{
  list-style:none;
  display:flex;align-items:center;
  gap:var(--sp-3) var(--sp-6);
}
.sticky-nav__list a{
  text-decoration:none;
  font-size:var(--fs-body-s);
  letter-spacing:.04em;
  color:var(--ink-soft);
  padding-bottom:3px;
  border-bottom:1px solid transparent;
  white-space:nowrap;
  transition:color var(--dur-fast) var(--ease-out),border-color var(--dur-fast) var(--ease-out);
}
.sticky-nav__list a:hover,
.sticky-nav__list a.is-active{color:var(--ink);border-bottom-color:var(--limoncello)}
.sticky-nav__list a.nav__link--strong{font-weight:600;color:var(--ink)}

/* Les ancres se calent pile sous la barre fixe : hauteur de l'inner + le filet
   de 1px. Une valeur plus grande laisserait dépasser le bas de la section
   précédente sous la nav ; une valeur plus petite passerait dessous. */
:root{--barre-h:57px}          /* 56 + 1 */
section[id]{scroll-margin-top:var(--barre-h)}

/* =====================================================================
   Mobile : barre permanente (lockup + burger) et menu déroulant
   ===================================================================== */
@media (max-width:900px){
  /* la barre reste masquée au chargement (hero plein écran) et glisse au scroll,
     comme en desktop — cf. .sticky-nav / .sticky-nav.is-visible plus haut */
  .sticky-nav__inner{justify-content:space-between;height:48px;gap:var(--sp-4)}
  /* en mobile, le lockup horizontal complet remplace le monogramme seul (Figma 107:82) */
  .sticky-nav__logo .monogram__svg{display:none}
  /* min-width:0 sur le lien : sans lui, un flex item refuse de passer sous sa
     taille de contenu et le lockup pousserait le burger hors de l'écran. */
  .sticky-nav__logo{min-width:0}
  .sticky-nav__lockup{display:block;width:228px;max-width:100%;height:auto}

  /* burger */
  .sticky-nav__burger{
    display:grid;align-content:center;gap:5px;
    width:32px;height:32px;padding:0;flex:none;
    background:transparent;border:none;cursor:pointer;
  }
  .sticky-nav__burger span{
    display:block;width:26px;height:1px;background:var(--ink);
    transition:transform var(--dur-fast) var(--ease-out),opacity var(--dur-fast) var(--ease-out);
  }
  /* croix : le décalage suit l'épaisseur du trait (1px) + le gap (5px) */
  .sticky-nav__burger[aria-expanded="true"] span:nth-child(1){transform:translateY(6px) rotate(45deg)}
  .sticky-nav__burger[aria-expanded="true"] span:nth-child(2){opacity:0}
  .sticky-nav__burger[aria-expanded="true"] span:nth-child(3){transform:translateY(-6px) rotate(-45deg)}

  /* panneau de menu déroulant sous la barre */
  .sticky-nav__list{
    position:fixed;top:48px;left:0;right:0;
    flex-direction:column;align-items:flex-start;justify-content:flex-start;
    gap:var(--sp-5);
    background:var(--creme);
    /* le panneau recouvre le border-bottom de la barre (48px + 1px) : il porte
       donc lui-même le trait de séparation, sinon il n'y en aurait aucun */
    border-top:1px solid var(--hairline);
    border-bottom:1px solid var(--hairline);
    padding:var(--sp-8) var(--gutter) var(--sp-10);
    transform:translateY(-12px);opacity:0;visibility:hidden;pointer-events:none;
    transition:opacity var(--dur-mid) var(--ease-out),transform var(--dur-mid) var(--ease-out),visibility 0s var(--dur-mid);
  }
  .sticky-nav.is-menu-open .sticky-nav__list{
    transform:none;opacity:1;visibility:visible;pointer-events:auto;
    transition:opacity var(--dur-mid) var(--ease-out),transform var(--dur-mid) var(--ease-out);
  }
  .sticky-nav__list a{font-size:var(--fs-body);color:var(--ink);text-align:left}

  /* la nav du hero (bouton « Menu ») disparaît : la barre du haut la remplace */
  .hero .nav{display:none}

  /* la barre mobile fait 48px + le filet de 1px */
  :root{--barre-h:49px}

}

/* =====================================================================
   1. HERO
   ===================================================================== */
.hero{background:var(--bg);color:var(--ink);overflow-x:clip}
.hero__inner{
  padding-top:var(--sp-8);
  padding-bottom:var(--section-y);
  text-align:center;
  display:flex;flex-direction:column;align-items:center;
}

/* Lockup hero : « Julie & Alix » + monogramme limoncello + « 3 juillet 2027 » (SVG complet) */
.hero__brand{display:block}
.hero__brand img{display:block;width:357px;max-width:84vw;height:auto}

/* Monogramme AJ seul (footer) */
.monogram{display:inline-block;width:64px;color:var(--olive)}
.monogram__svg{display:block;width:100%;height:auto;overflow:visible;fill:currentColor}

/* Navigation horizontale (gap lockup → nav : 24px, Figma 14:143) */
.nav{margin-top:var(--sp-5);width:100%}
.nav__toggle{
  display:none;
  align-items:center;gap:var(--sp-2);
  margin-inline:auto;
  background:transparent;border:1px solid var(--hairline);
  padding:var(--sp-2) var(--sp-4);
  font-family:var(--font-body);
  font-size:var(--fs-overline);
  letter-spacing:var(--ls-caps);
  text-transform:uppercase;
  color:var(--ink);
  cursor:pointer;
}
.nav__toggle-line{width:18px;height:1px;background:currentColor;box-shadow:0 5px 0 currentColor,0 -5px 0 currentColor}
.nav__list{
  list-style:none;
  display:flex;flex-wrap:wrap;justify-content:center;
  gap:var(--sp-3) var(--sp-6);
}
.nav__list a{
  text-decoration:none;
  font-size:var(--fs-body-s);
  letter-spacing:.04em;
  color:var(--ink-soft);
  padding-bottom:6px;
  border-bottom:1px solid transparent;
  transition:color var(--dur-fast) var(--ease-out),border-color var(--dur-fast) var(--ease-out);
}
.nav__list a:hover{color:var(--ink);border-bottom-color:var(--limoncello)}
.nav__list a.nav__link--strong{font-weight:600;color:var(--ink)}

/* Grand visuel aquarelle (PNG détouré, flotte sur la crème) */
.hero__visual{margin-top:var(--sp-4);width:100%}
.hero__visual img{width:100%;height:auto}

/* Signature du domaine : ferrée à droite, à cheval sur le bas de l'aquarelle */
.hero__domaine{
  align-self:stretch;
  position:relative;z-index:1;
  margin-top:calc(var(--sp-12) * -1);
  text-align:right;
  font-size:clamp(26px, 3vw, 32px);
  line-height:1;
  color:var(--azur);
}

/* =====================================================================
   2. LE PROGRAMME
   ===================================================================== */
.programme{list-style:none;display:grid;gap:var(--sp-6)}
.programme__card{display:flex;flex-direction:column;text-align:center}
.programme__media{position:relative;margin:0}
.programme__photo{
  aspect-ratio:4/5;width:100%;object-fit:cover;display:block;
}
/* Pastille picto pleine limoncello, à cheval image / socle (Figma 8:325) */
.programme__badge{
  position:absolute;left:50%;bottom:0;
  transform:translate(-50%,50%);
  width:62px;height:62px;border-radius:50%;
  background:var(--limoncello);
  display:grid;place-items:center;
  color:var(--blanc);
  z-index:1;
}
.programme__badge svg{
  width:26px;height:26px;
  fill:none;stroke:currentColor;stroke-width:1.4;
  stroke-linecap:round;stroke-linejoin:round;
}
/* Socle crème sous la photo */
.programme__body{
  background:var(--creme);
  color:var(--ink);
  padding:calc(31px + var(--sp-4)) var(--sp-4) var(--sp-6);
}
.programme__event{
  font-family:var(--font-display);
  font-size:var(--fs-title);
  font-weight:700;
  line-height:var(--lh-title);
}
.programme__date{
  margin-top:var(--sp-1);
  font-size:var(--fs-body);
  letter-spacing:.06em;
  color:var(--ink-soft);
}

/* =====================================================================
   3. LE DOMAINE — 50/50 dans le wrapper (Figma) :
   texte ferré à gauche du wrapper, visuel remplissant sa moitié
   ===================================================================== */
.section--domaine{background:var(--bg-alt);color:var(--ink);padding-block:0}
.domaine__layout{display:grid}
.domaine__text{padding-block:var(--section-y);padding-inline:var(--gutter)}
.domaine__lead{margin-top:var(--sp-4);color:var(--ink-soft)}
/* deux colonnes de même niveau : Itinéraire | Adresse */
.domaine__cols{
  display:grid;grid-template-columns:1fr 1fr;
  gap:var(--sp-8) var(--sp-6);
  margin-top:var(--sp-8);
  align-items:start;
}
.domaine__col-titre{
  margin-bottom:var(--sp-4);
  font-family:var(--font-display);font-weight:400;
  font-size:var(--fs-title);
}
.domaine__adresse{
  font-style:normal;font-size:var(--fs-body-s);line-height:1.5;color:var(--ink-soft);
}
/* le CTA conclut le bloc, sous les deux colonnes */
.domaine__cta{margin-top:var(--sp-6)}
.itineraire{display:grid;gap:var(--sp-5)}

/* colonnes empilées quand la place manque */
@media (max-width:700px){
  .domaine__cols{grid-template-columns:1fr;gap:var(--sp-8)}
}
.itineraire__mode p{font-size:var(--fs-body-s);line-height:1.5;color:var(--ink-soft)}
.itineraire__label{
  color:var(--accent-warm)!important;
  font-weight:600;
  font-size:var(--fs-body)!important;
  margin-bottom:var(--sp-1);
}
.domaine__visual{position:relative;min-height:340px;margin:0;overflow:hidden}
/* diaporama : la 1re image reste dans le flux (elle porte la hauteur du bloc),
   les autres se superposent en absolu. Fondu piloté par .is-active. */
.domaine__img{
  width:100%;height:100%;object-fit:cover;
  opacity:0;transition:opacity 1.2s var(--ease-out);
}
.domaine__img:first-child{position:relative}
.domaine__img:not(:first-child){position:absolute;inset:0}
/* fondu enchaîné : la photo qui arrive se révèle PAR-DESSUS la précédente, qui
   reste opaque en dessous. Sans ça les deux passent à 50 % en même temps et on
   voit le fond au travers, ce qui creuse la transition. */
.domaine__img.is-active{opacity:1;z-index:2}
.domaine__img.is-under{opacity:1;z-index:1;transition:none}

/* =====================================================================
   4. LES HÉBERGEMENTS (carrousel)
   ===================================================================== */
/* Carrousel style piste 3 (slides image + panneau texte), sans coins arrondis,
   flèches EN DEHORS du wrapper (Figma) */
.carousel{position:relative}
.carousel__viewport{overflow:hidden}
.carousel__track{
  list-style:none;margin:0;padding:0;display:flex;
  transition:transform var(--dur-mid) var(--ease-inout);
}
.heb-slide{
  flex:0 0 100%;display:grid;grid-template-columns:1.2fr 1fr;
  background:var(--bg-alt);
}
.heb-slide__media{min-height:420px;position:relative;overflow:hidden}
.heb-slide__media img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
/* Visuel provisoire, en attendant les photos des 3 hébergements extérieurs.
   Le placeholder doit remplir le cadre comme le ferait la photo, sinon la
   slide s'effondre à la hauteur du seul texte. */
.heb-slide__media .placeholder{position:absolute;inset:0}
/* (les aplats data-mood ont sauté : les 4 hébergements ont désormais leur photo) */
.heb-slide__body{
  padding:var(--sp-8);display:flex;flex-direction:column;
  justify-content:center;gap:var(--sp-4);
  /* Conteneur de requête : le filet vertical du bloc d'action dépend de la
     largeur de CETTE colonne, pas de celle de la fenêtre. Confinement sur
     l'axe inline seulement, la hauteur reste libre — vérifié, les 6 slides
     gardent la même hauteur qu'avant. */
  container-type:inline-size;
}
/* Le nom du logement mène la colonne : grossi le 08/09 pour rééquilibrer la
   card face au tag et au reste du texte. L'échelle ne propose rien entre
   --fs-title (24 au plus) et --fs-display-m (44 au plus, trop pour une card
   de 450 px) : d'où ce clamp local, 24 sur mobile et 32 sur grand écran. */
.heb-slide__title{
  /* Même traitement que « La cérémonie » / « Le dîner » du programme :
     Didot BOLD (700) et non le 400 qu'il portait. Les deux jouent le même
     rôle — le nom d'un bloc dans une carte — autant qu'ils se ressemblent.
     La taille reste celle du clamp ci-dessus : plus grande que les titres du
     programme, la colonne d'une card d'hébergement étant plus chargée. */
  font-family:var(--font-display);font-weight:700;
  font-size:clamp(1.5rem, 1.25rem + 1.1vw, 2rem);
  line-height:var(--lh-title);
  /* Le `<h3>` porte `u-display`, qui applique --ls-display (.01em) ; les
     titres du programme, eux, n'ont pas cette classe et sont donc en
     interlettrage normal. On aligne sur eux. La classe est conservée par
     cohérence avec tous les autres titres Didot du site. */
  letter-spacing:normal;
}
/* Capacité d'accueil : information clé, placée juste sous le titre, et donnée
   en petit tag olive — c'est le seul aplat de couleur de la colonne, il attire
   l'œil sans concurrencer le titre en Didot. */
.heb-slide__capacite{
  /* Rattaché au titre sans y être collé : 12px, le `gap` de 16px du body étant
     annulé. Le tag a désormais son propre aplat, il n'a plus besoin d'être à
     4px pour se lire comme le sous-titre du nom du gîte — et le titre grossi
     réclamait de l'air. */
  margin-top:calc(var(--sp-3) - var(--sp-4));
  /* Le body est une colonne flex : sans `align-self` le tag s'étirerait sur
     toute la largeur au lieu d'épouser son texte. */
  align-self:flex-start;
  padding:var(--sp-1) var(--sp-3);
  /* pas d'angle arrondi : la piste met tous les rayons à zéro (style.css l.8) */
  /* Jaune des puces des couchages, pour que la colonne n'ait qu'un seul
     accent. Le texte encre y est à 8,69:1. En revanche le jaune ne détache
     que de 1,28:1 sur le lin de la card : c'est le texte sombre qui porte
     la lecture, l'aplat reste volontairement doux. */
  background:var(--limoncello);
  font-size:var(--fs-body-s);
  font-weight:600;              /* le corps du site est en 300 : l'écart se voit */
  letter-spacing:.06em;
  text-transform:uppercase;
  color:var(--ink);
}
/* Proximité du domaine. Même corps et même couleur que les couchages : c'est
   une information de même nature — où se trouve le logement — et elle se lit
   dans la continuité de la liste. Sans puce en revanche : ce n'est pas un
   élément d'énumération, et en italique, ce qui la met à part de la liste
   sans la faire monter dans la hiérarchie. */
.heb-slide__proximite{
  /* 32px au-dessus (le `gap` de 16 du corps, plus 16 de marge) : elle suit la
     liste des couchages sans en avoir l'air, et cet écart la détache du bloc
     des caractéristiques du gîte. */
  margin-top:calc(var(--sp-6) - var(--sp-4));
  font-size:var(--fs-body);
  font-style:italic;
  line-height:1.45;
  color:var(--ink-soft);
}
/* Sur les logements 4 et 5, la ligne de proximité suit directement le titre :
   cet écart supplémentaire n'a alors rien à détacher, il ne ferait que
   décrocher la ligne de son titre. */
.heb-slide__title + .heb-slide__proximite{margin-top:0}
/* « Se rapprocher des mariés pour la réservation. » Ce n'est pas une mention
   de bas de card : c'est la marche à suivre, l'équivalent du bouton
   « Réserver sur Booking » des trois autres logements. Elle en reprend donc
   exactement la typo — Mulish 11px, 500, capitales espacées de --ls-caps —
   pour occuper la même place à l'œil, sans être un bouton : rien à cliquer
   ici, c'est une consigne. */
.heb-slide__text{
  /* Le filet est repassé sur `.heb-slide__action`, qui enveloppe désormais
     cette mention et le lien photos sur les trois gîtes : un seul trait pour
     le bloc, comme sur les trois autres logements. */
  font-family:var(--font-body);
  font-size:var(--fs-overline);
  font-weight:500;
  letter-spacing:var(--ls-caps);
  text-transform:uppercase;
  /* Le retour à la ligne est posé dans le HTML, entre les deux moitiés de la
     phrase : les deux lignes tiennent donc largement, et l'interlignage peut
     redescendre à 1,5. Il était à 1,9 quand le retour était subi et pouvait
     tomber n'importe où. */
  line-height:1.5;
  color:var(--ink);
}
/* Prix, en gras sur toute la ligne. Il ne porte plus le filet : remonté le
   08/09 entre les couchages et la ligne de proximité, il se lit avec les
   caractéristiques du gîte. Le filet est passé sur `.heb-slide__text`, qui
   ferme la slide — il sépare désormais l'information de l'action, comme
   `.heb-slide__action` le fait sur les trois autres logements. */
.heb-slide__prix{
  font-size:var(--fs-body);font-weight:600;
  line-height:1.45;color:var(--ink);
}
.heb-slide__prix strong{font-weight:inherit}
/* couchages : liste à puces discrètes, en limoncello */
.heb-slide__couchages{
  list-style:none;
  /* Pas de margin-top : le `gap` de 16px du body suffit. Cette marge datait de
     l'époque où la liste suivait directement le titre ; depuis l'ajout de la
     capacité au-dessus, elle s'ajoutait au gap et creusait 32px entre le
     nombre de personnes et les couchages, qui vont pourtant ensemble. */
  display:grid;gap:var(--sp-2);
}
.heb-slide__couchages li{
  position:relative;padding-left:var(--sp-4);
  font-size:var(--fs-body);line-height:1.45;color:var(--ink-soft);
}
.heb-slide__couchages li::before{
  content:"";position:absolute;left:0;top:.55em;
  width:6px;height:6px;background:var(--limoncello);
}
.heb-slide__body .btn{align-self:flex-start;margin-top:var(--sp-2)}
/* Même filet que le prix, pour les slides qui n'en ont pas : il ferme le bloc
   de texte avant l'action. Mêmes valeurs de part et d'autre du trait. */
.heb-slide__action{
  /* mêmes valeurs que `.heb-slide__prix` : c'est le même filet */
  margin-top:calc(var(--sp-6) - var(--sp-4));
  padding-top:var(--sp-6);
  border-top:1px solid var(--hairline);
  /* Les deux actions sur une ligne, « Voir les photos » à droite du bouton
     (essai du 09/09 ; elles étaient empilées avant). `wrap` parce que sur une
     colonne étroite les deux ne tiennent pas côte à côte : elles repassent
     alors l'une sous l'autre, avec le même écart. `center` aligne le lien sur
     le milieu du bouton et non sur sa première ligne de texte. */
  display:flex;flex-wrap:wrap;align-items:center;gap:var(--sp-5);
}
/* la marge du bouton s'ajouterait au padding du wrapper et casserait la
   symétrie autour du trait */
.heb-slide__action .btn{margin-top:0}

/* « Voir les photos ». Traité en LIEN et non en second bouton : là où il
   cohabite avec « Réserver sur Booking », deux boutons de même poids se
   disputeraient l'attention, alors que la réservation est l'action qui
   compte. Même dessin sur les six logements, y compris là où il est seul.
   Sans JavaScript la visionneuse ne peut pas s'ouvrir : le lien est masqué
   plutôt que de promettre une galerie qui ne viendrait pas. */
.lien-photos{display:none}
.js .lien-photos{
  align-self:flex-start;
  margin-top:var(--sp-2);
  padding:0;border:none;background:transparent;
  display:inline-flex;align-items:center;gap:var(--sp-2);
  font-family:var(--font-body);
  font-size:var(--fs-body-s);
  color:var(--ink);
  cursor:pointer;
}
/* dans le bloc d'action, la marge est portée par le `gap` du conteneur */
/* `align-self:center` est INDISPENSABLE ici : `.js .lien-photos` pose un
   `align-self:flex-start` (utile quand le lien est seul dans la colonne), qui
   l'emportait sur l'`align-items:center` du conteneur — le lien se calait en
   haut du bouton au lieu de son milieu. */
.js .heb-slide__action .lien-photos{margin-top:0;align-self:center}

/* Filet vertical entre la mention de réservation et le lien, sur les trois
   gîtes. `stretch` pour que le trait fasse la hauteur des deux lignes de
   texte : centré, la boîte du lien ne mesurerait que 18px et le trait serait
   un tiret. Le `gap` de 24px du conteneur donne l'écart avant le trait, le
   `padding` le même après.

   Le trait n'a de sens que si les deux éléments sont côte à côte. Or ce qui
   décide du passage à la ligne est la largeur de la COLONNE, pas celle de la
   fenêtre : d'où une requête de conteneur et non une `media`. Mesuré, il faut
   353px pour tenir sur une ligne ; à 380 on garde de la marge. Sans ce
   garde-fou, à 1000px de fenêtre la colonne tombait à 329 et le trait se
   retrouvait planté à gauche du lien, seul sur sa ligne. */
.js .heb-slide__action .heb-slide__text + .lien-photos{align-self:center}
@container (min-width:380px){
  .js .heb-slide__action .heb-slide__text + .lien-photos{
    align-self:stretch;
    padding-left:var(--sp-5);
    border-left:1px solid var(--hairline);
  }
}
/* le soulignement porte sur le TEXTE seul, comme « Ajouter un convive » :
   un trait sous le picto le salirait */
.lien-photos span{text-decoration:underline;text-underline-offset:3px}
.lien-photos svg{width:18px;height:18px;flex:0 0 auto;color:var(--gold)}
.lien-photos:hover span{text-decoration-thickness:2px}

.carousel__arrow{position:absolute;top:50%;transform:translateY(-50%);z-index:2}
.carousel__arrow--prev{left:-72px}
.carousel__arrow--next{right:-72px}
.carousel__dots{display:flex;justify-content:center;gap:var(--sp-2);margin-top:var(--sp-5)}
.carousel__dot{
  width:9px;height:9px;border-radius:50%;cursor:pointer;padding:0;
  border:1px solid var(--gold);background:transparent;
  transition:background-color var(--dur-fast) var(--ease-out);
}
.carousel__dot[aria-selected="true"]{background:var(--gold)}
.carousel__arrow{
  flex:none;
  width:44px;height:44px;
  display:grid;place-items:center;
  border:1px solid var(--hairline);
  border-radius:50%;
  background:transparent;
  color:var(--ink);
  cursor:pointer;
  transition:background var(--dur-fast) var(--ease-out),color var(--dur-fast) var(--ease-out);
}
.carousel__arrow svg{width:22px;height:22px}
.carousel__arrow:hover{background:var(--ink);color:var(--bg)}

/* ---------------------------------------------------------------------
   Visionneuse des photos de logement
   Une seule pour les six logements. Fond sombre : les photos sont de
   provenances et de cadrages très différents, un fond neutre et profond
   les met à égalité et évite le patchwork.
   --------------------------------------------------------------------- */
.visionneuse{
  position:fixed;inset:0;
  /* au-dessus de la barre de navigation, qui est en 50 */
  z-index:100;
  display:flex;align-items:center;justify-content:center;
  padding:var(--sp-4);
  /* Taille des trois boutons ronds (croix et flèches). Source unique : c'est
     elle qui donne aussi le retrait de l'en-tête, pour que le titre s'aligne
     sur le cadre de la photo et non sur les flèches. */
  --vis-nav:44px;
}
/* Le scroll de la page est bloqué pendant l'affichage : scroller le fond
   derrière une fenêtre modale donne l'impression d'avoir perdu sa place. */
.a-visionneuse,.a-visionneuse body{overflow:hidden}
/* Encre pleine, et non plus une transparence à 96 % : la page derrière ne se
   lisait pas, mais elle remontait de quelques points de luminance par endroits
   et le fond n'était pas parfaitement uni d'un bord à l'autre. */
.visionneuse__fond{position:absolute;inset:0;background:var(--ink);cursor:pointer}
.visionneuse__cadre{
  /* Conteneur focalisable pour l'ouverture, mais pas un contrôle : pas
     d'anneau de focus. La tabulation suivante en montre un, sur la croix. */
  outline:none;
  position:relative;
  display:flex;flex-direction:column;gap:var(--sp-3);
  width:100%;max-width:1100px;max-height:100%;
}
/* L'en-tête est en retrait de la largeur d'une flèche plus son `gap` : le
   titre se cale ainsi sur le bord GAUCHE du cadre de la photo, et la croix sur
   son bord droit, au lieu de s'aligner sur les flèches — le titre semblait
   flotter en dehors de l'image. */
.visionneuse__tete{
  display:flex;align-items:center;gap:var(--sp-4);
  padding-inline:calc(var(--vis-nav) + var(--sp-4));
  color:var(--creme);
}
.visionneuse__titre{
  flex:1 1 auto;
  font-family:var(--font-display);font-weight:400;
  font-size:var(--fs-title);line-height:var(--lh-title);
}
/* 70 % de crème sur le fond : 7,22:1, largement lisible, et le compteur ne
   vient pas concurrencer le nom du logement. */
.visionneuse__compteur{
  flex:0 0 auto;
  font-size:var(--fs-body-s);
  letter-spacing:.06em;
  color:color-mix(in srgb, var(--creme) 70%, transparent);
  font-variant-numeric:tabular-nums;   /* le compteur ne tremble pas */
}
.visionneuse__fermer,.visionneuse__nav{
  flex:none;
  padding:0;border:none;
  background:transparent;color:var(--creme);
  cursor:pointer;
  transition:background var(--dur-fast) var(--ease-out),
             color var(--dur-fast) var(--ease-out),
             opacity var(--dur-fast) var(--ease-out);
}
/* Les flèches gardent leur pastille ronde : posées au milieu d'un grand fond
   sombre, elles ont besoin d'une forme pour se donner à cliquer. */
.visionneuse__nav{
  width:var(--vis-nav);height:var(--vis-nav);   /* cible tactile confortable */
  display:grid;place-items:center;
  border-radius:50%;
  border:1px solid color-mix(in srgb, var(--creme) 30%, transparent);
}
.visionneuse__nav svg{width:22px;height:22px}
.visionneuse__nav:hover{background:var(--creme);color:var(--ink)}

/* « Fermer » + la croix, dans la typo des boutons du site. La croix reste nue :
   dans l'en-tête, en face du titre, le cercle faisait un bouton de trop. */
.visionneuse__fermer{
  display:inline-flex;align-items:center;gap:var(--sp-2);
  /* AUCUN padding à droite, et une croix dont le dessin remplit son cadre
     (viewBox 5 5 14 14) : c'est ce qui fait affleurer le trait de la croix au
     bord droit du cadre de la photo. La cible tactile est prise à gauche et
     en hauteur. */
  padding:var(--sp-2) 0 var(--sp-2) var(--sp-4);
  min-height:var(--vis-nav);
  font-family:var(--font-body);
  font-size:var(--fs-overline);
  font-weight:500;
  letter-spacing:var(--ls-caps);
  text-transform:uppercase;
  opacity:.8;
}
.visionneuse__fermer svg{width:16px;height:16px}
.visionneuse__fermer:hover,.visionneuse__fermer:focus-visible{opacity:1}
.visionneuse__scene{
  position:relative;
  display:flex;align-items:center;gap:var(--sp-4);
  min-height:0;                          /* sinon l'image déborde du cadre */
}
/* `contain` et non `cover` : les photos vont du 4:3 au 16:9 selon les
   logements, on préfère des bords vides à un recadrage aveugle. */
.visionneuse__image{
  flex:1 1 auto;min-width:0;
  /* 260px = l'en-tête, la bande de vignettes, leurs gouttières et le padding
     de la visionneuse. Sans cette réserve la bande sortait de l'écran sur les
     écrans bas. */
  width:100%;max-height:calc(100vh - 260px);
  object-fit:contain;
  background:rgba(246,241,231,.06);      /* le cadre se devine pendant le chargement */
}

/* Bande de vignettes, alignée sur le cadre de la photo comme l'en-tête. */
.visionneuse__vignettes{
  list-style:none;margin:0;
  padding-inline:calc(var(--vis-nav) + var(--sp-4));
  display:flex;gap:var(--sp-2);
  /* défilement horizontal si la galerie s'allonge un jour ; la barre est
     masquée, la vignette courante étant ramenée dans la vue par le JS */
  overflow-x:auto;scrollbar-width:none;
}
.visionneuse__vignettes::-webkit-scrollbar{display:none}
.visionneuse__vignette{
  display:block;flex:0 0 auto;
  width:96px;height:64px;                /* servies en 192x128, soit 2x */
  padding:0;border:none;background:none;
  cursor:pointer;
  /* Les non-courantes sont assombries plutôt que réduites : la bande garde un
     rythme régulier, et l'œil revient sur la seule vignette pleinement
     lumineuse. */
  opacity:.45;
  transition:opacity var(--dur-fast) var(--ease-out);
}
.visionneuse__vignette img{width:100%;height:100%;object-fit:cover;display:block}
.visionneuse__vignette:hover,.visionneuse__vignette:focus-visible{opacity:.8}
.visionneuse__vignette.est-active{
  opacity:1;
  /* le filet crème referme la vignette courante ; `outline` et non `border`,
     qui rognerait l'image de 2px et la ferait sauter à la sélection */
  outline:2px solid var(--creme);outline-offset:-2px;
}
@media (max-width:700px){
  .visionneuse__scene{display:block}
  /* Pas de flèches sur mobile. Elles étaient posées PAR-DESSUS la photo, aux
     coins bas — sur 375px de large, deux pastilles de 44px masquent une part
     réelle de ce qu'on est venu regarder. Et elles faisaient un troisième
     moyen de navigation, après le balayage et la bande de vignettes.
     ⚠️ On ne perd pas la navigation au bouton pour autant : chaque vignette
     de la bande EST un bouton qui saute à sa photo. Le geste n'est donc pas
     le seul chemin, et le piège à focus les prend en compte — il filtre sur
     `offsetParent`, les flèches masquées en sortent d'elles-mêmes. */
  .visionneuse__nav{display:none}
  /* Sans colonne de flèches, la photo occupe toute la largeur du cadre :
     l'en-tête et la bande n'ont plus à se mettre en retrait. */
  .visionneuse__tete,.visionneuse__vignettes{padding-inline:0}
  .visionneuse__image{max-height:calc(100vh - 260px)}
}

/* =====================================================================
   5. NOUS RÉPONDRE (visuel full width + card par-dessus)
   ===================================================================== */
.section--repondre{
  position:relative;isolation:isolate;overflow:hidden;
  display:grid;place-items:center;
  padding-block:var(--section-y);
  min-height:min(80vh,680px);
}
/* Le fond est en `z-index:0` et NON en négatif. Un enfant à z-index négatif
   se peint derrière le fond de son parent, et Safari s'y trompe au repaint
   partiel : cocher un bouton radio du formulaire ne salissait qu'un rectangle
   de l'écran, que le navigateur redessinait avec une copie décalée de la
   photo — un second fond en surimpression (signalé le 18/09).
   La section porte `isolation:isolate`, donc 0 et 1 suffisent à empiler le
   fond puis le contenu, sans jamais passer sous le parent. */
.repondre__bg{
  position:absolute;inset:0;z-index:0;
  width:100%;height:100%;object-fit:cover;object-position:center 60%;
  /* ⚠️ NE PAS RETIRER — corrige un défaut d'affichage Safari (18/09).
     Cocher un bouton radio fait apparaître un bloc conditionnel : la card
     grandit, et la section avec elle. Mesuré : 680 -> 808px au clic sur
     « Oui », 861 après l'ajout d'un convive. Cette image en `object-fit:cover`
     est donc REDIMENSIONNÉE de plus de 100px, dans un `overflow:hidden`.
     Safari rate cette re-rastérisation et laisse l'ancienne version dans une
     partie de la zone : un second fond décalé apparaît, avec des coutures
     nettes. `translateZ(0)` lui donne son propre calque de composition, qu'il
     redessine proprement. Validé dans Safari le 18/09.
     Le fond de « Notre lune de miel » n'en a pas besoin : sa card ne contient
     aucun formulaire, donc sa section ne change jamais de hauteur — et on
     évite un calque de plus, cf. le mur de témoins où 13 calques avaient
     provoqué l'effet inverse sur iPhone. */
  transform:translateZ(0);
}
.repondre__inner{position:relative;z-index:1;display:flex;justify-content:center;width:100%}
.repondre__card{
  background:var(--creme);color:var(--ink);
  width:100%;max-width:520px;
  padding:clamp(var(--sp-6),4vw,var(--sp-10)) clamp(var(--sp-5),4vw,var(--sp-8));
  box-shadow:var(--shadow-lg);
  text-align:center;
}
/* Dans la card, le bloc titre n'a pas besoin des 64px qu'il prend ailleurs :
   les deux marges s'effondrant, c'est lui qui imposait l'écart. On le met à
   zéro et l'espace ne vient plus que du formulaire — 64px deviennent 32. */
.repondre__card .section__head{margin-bottom:0}
.rsvp{display:grid;gap:var(--sp-5);margin-top:var(--sp-6);text-align:left}
.field{display:grid;gap:var(--sp-2)}
/* `> label` et non `label` : ce style est celui des INTITULÉS de champ, qui
   sont enfants directs de .field. Les labels des boutons radio, eux, sont
   imbriqués dans .choix — sans le chevron, ils héritaient de ce style (11px,
   capitales, interlettrage) et « Oui » se lisait comme un intitulé. */
.field > label{
  font-size:var(--fs-overline);
  letter-spacing:var(--ls-caps);
  text-transform:uppercase;
  color:var(--ink-soft);
}
/* `> input` pour la même raison que `> label` : les champs texte sont enfants
   directs de .field, les boutons radio sont imbriqués dans .choix. Sans le
   chevron, les radios héritaient du padding et du border-bottom des champs,
   ce qui les étirait à 26px de haut au lieu de 18. */
/* Fond lin plutôt qu'un simple filet : avec plusieurs lignes de convives, une
   succession de traits flotte, alors qu'un aplat donne une zone de saisie
   franche. Le lin est le fond secondaire du site (slides d'hébergement, mur
   des témoins), pas un gris — la palette n'en contient aucun.
   Le filet bas est CONSERVÉ : le lin ne se détache de la card crème qu'à
   1,18:1, ce qui ne suffit pas à délimiter un champ à lui seul. */
.field > input{
  font-family:var(--font-body);
  font-size:var(--fs-body);
  padding:var(--sp-3) var(--sp-4);
  background:var(--champ-fond);
  border:none;
  border-bottom:1px solid color-mix(in srgb,var(--ink) 40%, transparent);
  color:var(--ink);
}
.field > input:focus{outline:none;border-bottom-color:var(--ink)}
/* Le placeholder est un exemple, pas une consigne : il doit se lire comme un
   texte secondaire sans devenir illisible. Mesuré sur la card crème :
   52% d'encre ne donne que 3,15:1, il faut 65% pour atteindre 4,53:1 et
   passer le seuil AA. Ne pas éclaircir davantage. */
.field > input::placeholder,
.convive input::placeholder{
  /* 70 % et non 65 % : sur le fond lin, 65 % retombait à 4,22:1, sous le seuil.
     À 70 % on repasse au-dessus de 4,5:1. */
  color:color-mix(in srgb,var(--ink) 70%, transparent);
  opacity:1;                    /* Firefox applique sinon une opacité propre */
}

/* « Serez-vous présent ? » : le fieldset n'a ni bordure ni padding par défaut
   du navigateur, et sa legend reprend exactement le style des autres labels
   pour que la question ne se lise pas comme un titre. */
.field--choix{border:none;padding:0;margin:0}
.field--choix legend{
  padding:0;
  font-size:var(--fs-overline);
  letter-spacing:var(--ls-caps);
  text-transform:uppercase;
  color:var(--ink-soft);
}
.choix{display:flex;gap:var(--sp-5);margin-top:var(--sp-3)}
/* « Oui » / « Non » se lisent comme le texte que l'on saisit dans les champs,
   pas comme un intitulé : mêmes police, corps, graisse et couleur que
   `.field input`. Le font-weight est explicite parce qu'un input ne suit pas
   le 300 du body — il reste à 400 — et il faut retomber sur la même valeur. */
.choix__option{
  display:inline-flex;align-items:center;gap:var(--sp-2);
  font-family:var(--font-body);
  font-size:var(--fs-body);
  font-weight:400;
  letter-spacing:normal;
  text-transform:none;
  line-height:normal;
  color:var(--ink);
  cursor:pointer;
}
/* Rond dessiné à la main : `accent-color` ne permet pas de traiter le contour
   et le point central séparément (il les teinte ensemble), et le point actif
   doit être marron. L'astuce évite tout pseudo-élément — un fond chocolat
   creusé par une ombre interne couleur du fond laisse apparaître un disque
   central de 8px. Plus fiable qu'un ::before sur un input. */
.choix__option input{
  appearance:none;-webkit-appearance:none;
  width:18px;height:18px;margin:0;
  border:1px solid color-mix(in srgb,var(--ink) 40%, transparent);
  border-radius:50%;
  background:transparent;
  cursor:pointer;
  transition:background var(--dur-fast) var(--ease-out),
             border-color var(--dur-fast) var(--ease-out);
}
.choix__option input:checked{
  background:var(--chocolat);
  border-color:var(--chocolat);
  box-shadow:inset 0 0 0 4px var(--creme);   /* creuse l'anneau, isole le point */
}
/* appearance:none supprime aussi le focus natif : on le remet explicitement,
   sinon la navigation au clavier devient invisible. */
.choix__option input:focus-visible{
  outline:2px solid var(--ink);
  outline-offset:2px;
}
.rsvp .btn{justify-self:center;margin-top:var(--sp-2)}

/* Les deux blocs conditionnels (liste de convives / nom seul) sont masqués
   tant que la réponse n'est pas choisie — mais SEULEMENT si le JS tourne :
   sans lui, ils restent visibles et le serveur fait le tri. C'est le même
   principe que `.js .reveal` pour les apparitions au scroll. */
.js .rsvp__conditionnel{display:none}
.js .rsvp__conditionnel.est-actif{display:grid}

/* La liste des convives : une ligne par personne, chacune avec sa croix. */
.rsvp__conditionnel legend{
  padding:0;
  font-size:var(--fs-overline);
  letter-spacing:var(--ls-caps);
  text-transform:uppercase;
  color:var(--ink-soft);
}
.rsvp__conditionnel{border:none;padding:0;margin:0;gap:var(--sp-2)}
.convives{list-style:none;display:grid;gap:var(--sp-2);margin-top:var(--sp-3)}
.convive{display:flex;align-items:center;gap:var(--sp-2)}
.convive input{
  flex:1;min-width:0;
  font-family:var(--font-body);
  font-size:var(--fs-body);
  padding:var(--sp-3) var(--sp-4);
  background:var(--champ-fond);
  border:none;
  border-bottom:1px solid color-mix(in srgb,var(--ink) 40%, transparent);
  color:var(--ink);
}
.convive input:focus{outline:none;border-bottom-color:var(--ink)}
.convive input[aria-invalid="true"]{border-bottom-color:var(--erreur)}
/* La croix ne s'affiche qu'à partir de la 2e ligne : retirer la seule ligne
   présente laisserait la liste vide et sans moyen d'y revenir. */
.convive__retirer{
  flex:0 0 auto;
  width:28px;height:28px;
  display:grid;place-items:center;
  padding:0;border:none;background:transparent;
  color:var(--ink-soft);cursor:pointer;
  transition:color var(--dur-fast) var(--ease-out);
}
.convive__retirer:hover{color:var(--erreur)}
.convive__retirer svg{width:16px;height:16px}
/* Bouton texte, volontairement discret : c'est une action secondaire, elle ne
   doit pas rivaliser avec « Envoyer ». */
.convives__ajouter{
  /* Ferré à gauche : aligné sur le début des champs, donc sur le sens de
     lecture. Le ferrage à droite (sous la colonne des croix) a été essayé le
     08/09 puis écarté. */
  justify-self:start;
  margin-top:var(--sp-2);
  padding:0;border:none;background:transparent;
  display:inline-flex;align-items:center;gap:var(--sp-2);
  font-family:var(--font-body);
  font-size:var(--fs-body-s);
  color:var(--ink);
  cursor:pointer;
}
/* Le soulignement porte sur le TEXTE seul : souligner tout le bouton passerait
   un trait sous le + et sous l'espace, ce qui salit le signe. */
.convives__ajouter span{text-decoration:underline;text-underline-offset:3px}
.convives__ajouter svg{
  width:14px;height:14px;flex:0 0 auto;
  /* le + reprend le tracé des autres pictos du site (1,6px, bouts arrondis) */
}
.convives__ajouter[disabled]{color:var(--ink-soft);cursor:default}
.convives__ajouter[disabled] span{text-decoration:none}
.convives__limite{
  margin-top:var(--sp-2);
  font-size:var(--fs-body-s);
  color:var(--ink-soft);
}

/* Message d'erreur sous le champ concerné. Il ne se repose pas sur la seule
   couleur : le texte dit ce qui manque, et le champ reçoit aussi un liseré.
   Teinte `--erreur` (6,60:1 sur la card crème) — le terracotta de la palette
   n'atteignait que 3,40:1. */
.field__erreur{
  margin-top:var(--sp-2);
  font-size:var(--fs-body-s);
  line-height:1.4;
  color:var(--erreur);
}
.field > input[aria-invalid="true"]{border-bottom-color:var(--erreur)}
.field--choix[data-invalide] .choix__option input{border-color:var(--erreur)}

/* Confirmation affichée à la place du formulaire, dans la même card : on ne
   change pas de page, le titre de section reste au-dessus. */
/* La confirmation ne porte plus de titre : c'est le titre de la section qui
   devient « Merci ! ». Il ne reste que le texte, sous l'en-tête, d'où une
   marge nulle — la card règle déjà l'espace après son en-tête. */
.rsvp__merci{
  display:grid;gap:var(--sp-4);
  text-align:center;
}
.rsvp__merci-texte{color:var(--ink-soft);line-height:1.6}
/* Le bloc est une grille : sans `justify-self`, le bouton s'étire sur toute la
   largeur de la card et pèse plus lourd que le remerciement lui-même. */
.rsvp__merci .btn{justify-self:center;margin-top:var(--sp-2)}

/* =====================================================================
   8. NOTRE LUNE DE MIEL (visuel full width + card à gauche)
   ===================================================================== */
.section--lune{
  position:relative;isolation:isolate;overflow:hidden;
  display:grid;place-items:center;
  padding-block:var(--section-y);
  min-height:min(80vh,680px);
}
/* hors flux : ne doit pas occuper de ligne de grille. `z-index:0` et non
   négatif, pour la même raison que `.repondre__bg` ci-dessus — c'est le même
   montage, donc le même piège de repaint Safari. */
.section--lune picture{position:absolute;inset:0;z-index:0}
.lune__bg{
  position:absolute;inset:0;z-index:0;
  width:100%;height:100%;object-fit:cover;object-position:center 78%;
}
.lune__inner{position:relative;z-index:1;display:flex;justify-content:flex-start;width:100%}
.lune__card{
  background:var(--creme);color:var(--ink);
  width:100%;max-width:520px;
  padding:clamp(var(--sp-6),4vw,var(--sp-10)) clamp(var(--sp-5),4vw,var(--sp-8));
  box-shadow:var(--shadow-lg);
  text-align:center;
}
.lune__card .section__head{margin-bottom:var(--sp-5)}
.lune__lead{color:var(--ink-soft);margin-bottom:var(--sp-6)}
/* mobile : visuel vertical 1:2 dédié (couple dans le tiers bas), card au-dessus.
   La card n'est plus posée SUR l'image en absolu : elle est dans le flux, et
   l'image remonte derrière elle d'un chevauchement fixe (en vw, donc
   proportionnel). Le couple est à ~1,2 × la largeur sous le haut de l'image :
   il reste donc toujours largement sous la card, quelle que soit la taille de
   l'écran ou la longueur du texte. C'est la seule façon de garantir le
   « jamais » — un simple object-position se fait rattraper sur écran étroit,
   où la card grandit pendant que les têtes remontent. */
@media (max-width:700px){
  .section--lune{
    /* La photo reste en fond sur TOUTE la section (le <picture> est en absolu).
       La garantie « jamais de tête coupée » vient de la 2e rangée : elle réserve
       100vw sous la card. Avec object-position bottom, le haut du chignon se
       situe à 0,794 × la largeur au-dessus du bas de l'image — donc toujours
       dans cette réserve, et jamais sous la card, quelles que soient la largeur
       de l'écran et la hauteur du texte. */
    grid-template-rows:auto 100vw;
    align-items:start;justify-items:stretch;
    padding-block:var(--sp-8) 0;
    min-height:0;
  }
  .lune__bg{object-position:center bottom}
}

/* =====================================================================
   6. L'ÉQUIPE DU MARIAGE — mur de témoins (accordéon d'images)
   ===================================================================== */
.section--temoins{background:var(--bg-alt);color:var(--ink);padding-block:var(--section-y) 0}
.temoins__wall{display:flex;height:min(78vh,720px);overflow:hidden}
.temoins__strip{
  position:relative;flex:1;min-width:0;
  padding:0;border:none;overflow:hidden;cursor:pointer;
  background:var(--lin);
  /* flex-grow = ouverture au survol ; apparition au scroll : fade + translation globale */
  transition:flex-grow var(--dur-mid) var(--ease-inout),
             opacity .9s var(--ease-out),
             transform .9s var(--ease-out);
}
/* état initial avant l'entrée au scroll */
.js .temoins__strip{opacity:0;transform:translateY(44px)}
.js .temoins__wall.is-in .temoins__strip{opacity:1;transform:none}
.temoins__strip img{
  position:absolute;inset:0;width:100%;height:100%;
  object-fit:cover;object-position:50% 18%;
  filter:brightness(.82) saturate(.9);
  transition:filter var(--dur-mid) var(--ease-out);
}
.temoins__strip:hover,.temoins__strip.is-open{flex-grow:3.4}
.temoins__strip:hover img,.temoins__strip.is-open img{filter:none}
/* voile de lisibilité en pied de bande */
.temoins__strip::after{
  content:"";position:absolute;inset:auto 0 0 0;height:46%;z-index:1;
  background:linear-gradient(to top, rgba(46,38,32,.72), transparent);
  opacity:0;transition:opacity var(--dur-mid) var(--ease-out);
}
.temoins__strip:hover::after,.temoins__strip.is-open::after{opacity:1}
/* bloc infos : largeur FIXE (celle du panneau ouvert) pour éviter tout reflow pendant l'anim */
.temoins__meta{
  position:absolute;left:50%;bottom:0;z-index:2;
  width:min(300px, 84vw);
  padding:var(--sp-5) var(--sp-4);
  text-align:center;color:var(--creme);
  opacity:0;transform:translate(-50%,10px);
  transition:opacity var(--dur-fast) var(--ease-out),transform var(--dur-fast) var(--ease-out);
}
/* entrée : le fade du texte ne part qu'une fois le panneau ouvert */
.temoins__strip:hover .temoins__meta,.temoins__strip.is-open .temoins__meta{
  opacity:1;transform:translate(-50%,0);
  transition:opacity var(--dur-mid) var(--ease-out) var(--dur-mid),transform var(--dur-mid) var(--ease-out) var(--dur-mid);
}
.temoins__meta-name{font-family:var(--font-display);font-size:var(--fs-display-m);line-height:var(--lh-title)}
.temoins__meta-desc{font-size:var(--fs-body-s);line-height:1.5;max-width:32ch;margin:var(--sp-2) auto 0;white-space:normal}
/* prénom vertical quand la bande est repliée */
.temoins__spine{
  position:absolute;left:50%;bottom:var(--sp-5);z-index:2;
  transform:translateX(-50%) rotate(180deg);
  writing-mode:vertical-rl;
  font-family:var(--font-display);font-size:var(--fs-body);letter-spacing:.12em;
  color:var(--creme);text-shadow:0 1px 8px rgba(46,38,32,.6);
  transition:opacity var(--dur-fast) var(--ease-out);
}
.temoins__strip:hover .temoins__spine,.temoins__strip.is-open .temoins__spine{opacity:0}

/* =====================================================================
   7. LE THÈME (mosaïque rythmée — reprise piste 3)
   ===================================================================== */
/* Planche du thème — relevé du Figma 116:141.
   5 colonnes × 9 rangées carrées (grid-auto-rows = largeur d'une colonne),
   gap 12px : sur le wrapper 1136 cela donne 217,6px de colonne et 2054px de
   haut, soit la maquette au pixel. Chaque pièce est placée explicitement par
   grid-area — le pavage est complet (45/45 cellules), donc aucun trou possible.
   Les --c/--r doublent l'info pour le repli mobile en aspect-ratio. */
.mosaic{
  margin-top:var(--sp-8);
  display:grid;
  grid-template-columns:repeat(5,1fr);
  grid-auto-rows:calc((min(1136px, 100vw - 2*var(--gutter)) - 4*var(--sp-3)) / 5);
  gap:var(--sp-3);
}
.mosaic > *{
  grid-column:span var(--c,3);
  grid-row:span var(--r,4);
}
.tile{
  position:relative;overflow:hidden;
  box-shadow:var(--shadow-sm);
  transition:box-shadow var(--dur-mid) var(--ease-out);
}
.tile:hover{box-shadow:var(--shadow-md);z-index:2}
/* Parallax : l'image glisse dans son cadre, le cadre ne bouge jamais — déplacer
   les tuiles d'une grille dense les ferait se chevaucher. Débord de 7 % de part
   et d'autre, --py plafonné à ±5 % : aucun bord vide possible. */
/* AUCUN transform par défaut — c'est volontaire et c'est un correctif.
   Le débord de 7 % et le glissement ne servent QUE le parallax, et le parallax
   est désactivé sous 700px (cf. `petitEcran` dans script.js). Or le CSS
   appliquait quand même un transform permanent aux 13 images : 13 couches de
   composition sur mobile pour un effet qui n'y tourne jamais. Sur iPhone, une
   tuile du wall restait non peinte — et réapparaissait au passage en paysage,
   c'est-à-dire au premier relayout complet, signature d'un défaut de peinture
   et non de layout. Sans transform, plus de couche à composer.
   Bénéfice au passage : sur mobile l'image n'est plus rognée de 7 %. */
.tile img{
  position:absolute;inset:0;width:100%;height:100%;
  object-fit:cover;
}
/* Le seuil 701px doit rester aligné sur `petitEcran()` dans script.js (<= 700). */
@media (min-width:701px){
  .tile img{transform:scale(1.14) translateY(var(--py,0%))}
}
/* Photo dont le sujet touche le bord bas (la Fiat sur la route) : le débord de
   7 % lui couperait les roues. On l'affiche pleine hauteur, donc sans parallax —
   en échange le rognage latéral tombe de 343 à 217px, on en voit plus. */
.tile--ancree img{transform:none}   /* pas de scale, donc pas de débord */

/* Les 4 repères du thème, sous la planche (Figma 119:432) */
.reperes{
  list-style:none;
  margin-top:var(--sp-10);
  display:grid;grid-template-columns:repeat(4,1fr);
  gap:var(--sp-6);
  text-align:center;
}
/* Desktop : largeur fixe relevée du Figma (222px), répartie en space-between.
   C'est cette largeur qui casse les titres sur deux lignes — en 1fr la colonne
   fait 260px et « Esprit méditerranéen » tient tout juste sur une ligne. */
@media (min-width:1100px){
  .reperes{grid-template-columns:repeat(4,222px);justify-content:space-between}
}
.repere__icone{
  display:block;margin:0 auto var(--sp-4);
  height:48px;width:auto;
}
.repere__titre{
  font-family:var(--font-display);font-weight:700;
  font-size:var(--fs-title);line-height:1.25;
  margin-bottom:var(--sp-2);
}
.repere__texte{
  font-size:var(--fs-body-s);line-height:1.5;
  color:var(--ink-soft);
  max-width:26ch;margin-inline:auto;
}
@media (max-width:700px){
  .reperes{grid-template-columns:repeat(2,1fr);gap:var(--sp-8) var(--sp-4);margin-top:var(--sp-8)}
}

/* Sous 700px : deux colonnes en masonry. display:block désactive la grille, donc
   les grid-area sont ignorés ; les proportions sont reprises par aspect-ratio. */
@media (max-width:700px){
  .mosaic{
    display:block;
    column-count:2;column-gap:var(--sp-2);
  }
  .mosaic > *{
    break-inside:avoid;-webkit-column-break-inside:avoid;
    margin:0 0 var(--sp-2);
    aspect-ratio:var(--c,2) / var(--r,2);
    --py:0%;
  }
  /* la card « Dolce Vita » sort du mur en mobile : elle déséquilibrait les deux
     colonnes. Restent 12 photos — 2 portraits, 2 paysages, 8 carrés — qui se
     répartissent en deux colonnes de hauteur identique. */
  .tile--carte{display:none}
}
/* bandeau palette (5 aplats côte à côte, comme le moodboard) */
.tile--palette{display:flex;box-shadow:none;aspect-ratio:3/4}
.tile--palette span{flex:1}
/* tuiles typographiques */
.tile--script{display:grid;place-items:center;background:var(--lin);box-shadow:none;padding:var(--sp-4);aspect-ratio:4/5}
.tile--script .u-script{font-size:clamp(30px,3.4vw,46px);color:var(--ink);line-height:.9;text-align:center}
.tile--quote{display:grid;place-items:center;background:var(--olive);color:var(--creme);box-shadow:none;padding:var(--sp-4);text-align:center;aspect-ratio:4/5}
.tile--quote p{font-family:var(--font-display);font-style:italic;font-size:var(--fs-title);line-height:var(--lh-title);max-width:22ch}




/* =====================================================================
   9. FOOTER (couleur encre)
   ===================================================================== */
/* Bloc centré (Figma 109:170) : grand lockup, adresse, puis la signature script */
/* Footer en greige depuis le 19/08 (demande de la mariée, #CBB68F = --greige).
   Le fond passant de sombre à clair, TOUT le contenu a dû s'inverser : sur
   greige, le crème tombe à 1,76:1 et le limoncello à 1,16:1 — invisibles.
   Mesuré : encre 7,51:1, chocolat 5,81:1, olive 2,53:1 (donc exclu). */
/* TEST 08/09 — footer VERT. L'olive pur (#6E7346) ne tient pas : rien dans la
   palette n'y atteint 4,5:1 (crème 4,44 · limoncello 2,92 · greige 2,53), seul
   le blanc pur passe, à 5,00. D'où un olive assombri d'encre (55/45) : le vert
   reste franc et le contenu clair redevient confortable — crème 7,32:1,
   limoncello 4,82:1. C'est la SEULE ligne à toucher pour revenir au greige,
   avec les trois couleurs de contenu ci-dessous et le lockup dans le HTML. */
.footer{
  background:color-mix(in srgb, var(--olive) 55%, var(--ink));
  color:var(--creme);
}
.footer__inner{
  min-height:382px;
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  text-align:center;
  gap:var(--sp-5);
  padding-block:var(--sp-8);   /* laisse min-height fixer les 382px du Figma */
}
.footer__brand{display:block}
/* le lockup porte déjà ses couleurs (monogramme limoncello, texte crème) */
.footer__brand img{display:block;width:280px;max-width:84vw;height:auto}
.footer__adresse{
  font-style:normal;
  font-size:var(--fs-body-s);line-height:1.6;letter-spacing:.02em;
  color:color-mix(in srgb,var(--creme) 78%, transparent);
}
.footer__lieu{display:block;font-weight:600;color:var(--creme)}
.footer__signoff{
  margin-top:var(--sp-2);
  font-size:var(--fs-display-m);
  /* le fond redevenant sombre, le limoncello reprend son rôle d'accent
     lumineux : 4,82:1 sur ce vert, et c'est un grand corps (28 à 44px). */
  color:var(--limoncello);
}
/* mobile : un peu moins haut, et le lockup calé sur celui du hero (74vw) —
   les deux SVG font 357×90, donc même largeur = même hauteur */
@media (max-width:900px){
  .footer__inner{min-height:0;padding-block:var(--sp-10);gap:var(--sp-4)}
  /* plafonné à la largeur desktop, comme pour le hero */
  .footer__brand img{width:74vw;max-width:280px}
}

/* =====================================================================
   Reveal au scroll — état par défaut VISIBLE si pas de JS
   ===================================================================== */
.js .reveal{opacity:0;transform:translateY(20px);transition:opacity var(--dur-slow) var(--ease-out),transform var(--dur-slow) var(--ease-out)}
.js .reveal.is-in{opacity:1;transform:none}
/* la signature du domaine se pose après l'aquarelle */
.js .hero__domaine.reveal{transition-delay:.55s}

@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  /* le diaporama ne tourne pas (cf. script.js) : la 1re photo reste affichée */
  .domaine__img{transition:none}
  .js .reveal{opacity:1;transform:none}
  .js .temoins__strip{opacity:1;transform:none;transition:none}
  .tile,.tile:hover{transition:none;transform:none}
}

/* =====================================================================
   RESPONSIVE
   ===================================================================== */
@media (min-width:700px){
  /* 2 colonnes depuis le 19/08 : l'encart « Le brunch » du dimanche 4 a été
     retiré à la demande de la mariée. Avec repeat(3,1fr) il restait une
     colonne vide à droite. À repasser à 3 si une 3e carte revient. */
  .programme{grid-template-columns:repeat(2,1fr)}
  /* Photos en PAYSAGE à partir d'ici, et seulement ici. Passer de 3 à
     2 colonnes a élargi les cartes de 357 à 552px, donc grandi les photos de
     446 à 690px de haut : la section atteignait 1188px. En 4/3 elle retombe à
     912px (-276). Vérifié sur les deux photos : le recadrage `cover` ne coupe
     rien d'important (l'arche et l'allée d'un côté, la tablée de l'autre).
     En mobile la colonne est unique et la hauteur n'a jamais posé problème :
     le 4/5 y reste, une photo portrait s'y tient mieux. */
  .programme__photo{aspect-ratio:4/3}

}

@media (min-width:900px){
  /* 3 colonnes : marge gauche calculée · zone texte élargie · reste (visuel full-bleed à droite) */
  .domaine__layout{
    --wrap:min(1136px, 100% - 2*var(--gutter));
    /* de combien le fond crème dépasse la colonne de texte : c'est le seul réglage
       à toucher pour déplacer la coupure. La même valeur est reprise en padding
       à droite du texte, donc la largeur du contenu ne bouge pas d'un pixel. */
    --creme-extra:var(--sp-10);
    grid-template-columns:calc((100% - var(--wrap))/2) calc(var(--wrap) * .5 + var(--creme-extra)) 1fr;
  }
  .domaine__text{grid-column:2;display:flex;align-items:center;padding-inline:0 calc(var(--sp-12) + var(--creme-extra))}
  .domaine__text-inner{width:100%}
  .domaine__visual{grid-column:3;min-height:100%}
}

/* Flèches du carrousel : repli par-dessus l'image quand l'écran est trop étroit */
@media (max-width:1330px){
  .carousel__arrow--prev{left:var(--sp-4)}
  .carousel__arrow--next{right:var(--sp-4)}
  .carousel__arrow{background:color-mix(in srgb,var(--creme) 82%, transparent);backdrop-filter:blur(2px)}
}

/* Carrousel : slide empilée sur mobile */
@media (max-width:700px){
  /* Hauteur du visuel VERROUILLÉE par une rangée explicite. Avec deux rangées
     `auto`, l'espace libre de la slide (le flex les étire toutes à la hauteur
     de la plus haute) était absorbé par la photo : plus le texte était court,
     plus la photo grandissait - mesuré de 280 à 319px selon la slide.
     Le `1fr` de la 2e rangée encaisse désormais tout le rab. */
  .carousel{--heb-media-h:280px}
  .heb-slide{grid-template-columns:1fr;grid-template-rows:var(--heb-media-h) auto}
  .heb-slide__media{min-height:0}
  .heb-slide__body{padding:var(--sp-6)}
  /* Chaque slide reprend sa hauteur propre. Sans ça, le flex les étire toutes
     à la hauteur de la plus longue : les logements à trois lignes de texte
     flottaient au milieu de 600px de vide. La photo, elle, reste verrouillée
     à 280px — c'est la seule chose qui doit rester constante d'une slide à
     l'autre. Le JS règle ensuite la hauteur du cadre sur la slide affichée,
     avec une transition, pour que le passage se fasse en douceur au lieu de
     faire sauter la page. */
  .carousel__track{align-items:flex-start}
  .carousel__viewport{transition:height var(--dur-mid) var(--ease-inout)}
  /* les flèches se calent au milieu du visuel, pas au milieu de la slide entière */
  .carousel__arrow{top:calc(var(--heb-media-h) / 2)}
}

/* Menu déroulant nav sur mobile */
@media (max-width:640px){
  .nav__toggle{display:inline-flex}
  .nav__list{
    display:none;
    flex-direction:column;align-items:center;
    gap:var(--sp-4);
    margin-top:var(--sp-5);
  }
  .nav.is-open .nav__list{display:flex}
}

/* Mur de témoins en mobile : slider à balayage (scroll-snap) */
@media (max-width:900px){
  /* en desktop les bandes tombent à ras du bas de la section (padding-bottom:0) ;
     en mobile on rend un peu de fond sous les photos */
  .section--temoins{padding-bottom:var(--sp-6)}
  .temoins__wall{
    height:auto;
    overflow-x:auto;overflow-y:hidden;
    scroll-snap-type:x mandatory;
    gap:var(--sp-3);
    padding-inline:var(--gutter);
    scrollbar-width:none;
    -webkit-overflow-scrolling:touch;
  }
  .temoins__wall::-webkit-scrollbar{display:none}
  .temoins__strip{
    flex:0 0 76vw;max-width:340px;
    aspect-ratio:1/2;max-height:72vh; /* ratio proche des bandes desktop */
    scroll-snap-align:center;
    /* pas d'ombre portée : --shadow-md floute sur 30px alors que le gap n'en fait
       que 12 — les deux ombres voisines le teintaient entièrement, ce qui le
       faisait ressortir contre le fond franc du padding en dessous. */
    box-shadow:none;
    transition:opacity .9s var(--ease-out),
               transform .9s var(--ease-out);
  }
  .temoins__strip:hover,.temoins__strip.is-open{flex-grow:0}
  .temoins__strip img,.temoins__strip:hover img,.temoins__strip.is-open img{filter:none;object-position:50% 20%}
  /* voile + texte seulement sur la carte active (snappée au centre), avec anim d'apparition */
  .temoins__strip::after{opacity:0;transition:opacity var(--dur-mid) var(--ease-out)}
  .temoins__strip.is-active::after{opacity:1}
  .temoins__meta,.temoins__strip:hover .temoins__meta,.temoins__strip.is-open .temoins__meta{
    opacity:0;transform:translate(-50%,14px);width:100%;
    transition:opacity var(--dur-mid) var(--ease-out) .1s,transform var(--dur-mid) var(--ease-out) .1s;
  }
  .temoins__strip.is-active .temoins__meta{opacity:1;transform:translate(-50%,0)}
  .temoins__spine{display:none}
}

/* Hero mobile : un écran plein, contenu centré verticalement (Figma 100:50)
   — placé en fin de fichier pour passer après les règles de base du hero
   — flex:none sinon le parent flex les rétrécit à la largeur du wrapper */
@media (max-width:900px){
  /* le hero occupe exactement la fenêtre : rien d'autre n'est visible au chargement.
     dvh suit la barre d'URL mobile ; vh sert de repli aux navigateurs anciens. */
  .hero__inner{
    min-height:100vh;
    min-height:100dvh;
    justify-content:center;
    padding-top:var(--sp-8);
    padding-bottom:var(--sp-8);
  }

  /* 357px = largeur du lockup en desktop (et taille native du SVG) : le mobile
     ne doit jamais le dépasser, sinon à 900px le 74vw donnerait 666px */
  .hero__brand img{width:74vw;max-width:357px}

  /* aquarelle carrée dédiée au mobile, pleine largeur.
     max-height : garde-fou pour les écrans courts (paysage, petits terminaux). */
  .hero__visual{width:100vw;max-width:none;flex:none;margin-top:0}
  .hero__visual img{width:100%;height:auto;max-height:56dvh;object-fit:contain}

  /* la signature du domaine se recentre sous l'aquarelle */
  .hero__domaine{text-align:center;margin-top:var(--sp-5)}
}

/* =====================================================================
   BACK-OFFICE (pages Symfony : /admin, connexion, remerciement)
   Ces pages ne font pas partie du site statique mais réutilisent ses
   tokens, pour rester dans le même univers visuel.
   ===================================================================== */
.page-symfony{background:var(--bg);color:var(--ink);font-family:var(--font-body)}
.bo{
  max-width:calc(1136px + 2*var(--gutter));
  margin-inline:auto;
  padding:var(--sp-10) var(--gutter);
}
.bo--centre{min-height:100vh;display:grid;place-items:center}
.bo__tete{
  display:flex;flex-wrap:wrap;gap:var(--sp-5);
  align-items:flex-end;justify-content:space-between;
  /* Filet de clôture : il sépare l'identité de la page de son contenu. Même
     valeur de part et d'autre du trait, comme les filets des cards. */
  padding-bottom:var(--sp-5);
  border-bottom:1px solid var(--hairline);
  margin-bottom:var(--sp-5);
}
.bo__actions{display:flex;align-items:center;gap:var(--sp-5)}
.bo__deconnexion{font-size:var(--fs-body-s);color:var(--ink-soft)}

/* Compteurs : le chiffre d'abord, le mot ensuite */
.bo__compteurs{
  list-style:none;display:flex;flex-wrap:wrap;gap:var(--sp-4);
  margin-bottom:var(--sp-6);
}
.bo__compteur{
  background:var(--bg-alt);
  padding:var(--sp-4) var(--sp-6);
  min-width:120px;
}
.bo__compteur strong{
  display:block;
  /* Mulish et non Didot : ce sont des chiffres qu'on lit et compare, pas un
     titre. Le Didot est là pour l'apparat du site, pas pour un tableau de
     bord. `tabular-nums` en prime, les compteurs s'alignent entre eux. */
  font-family:var(--font-body);
  font-weight:600;
  font-variant-numeric:tabular-nums;
  font-size:var(--fs-display-m);line-height:1;
}
.bo__compteur span{
  font-size:var(--fs-overline);letter-spacing:var(--ls-caps);
  text-transform:uppercase;color:var(--ink);font-weight:700;
}
/* teintes reprises de l'export Excel, pour que les deux se lisent pareil */
.bo__compteur--oui{background:#C6E7C6}
.bo__compteur--non{background:#F2C7C7}

/* Tableau : il peut être large, il défile dans son cadre plutôt que de
   forcer la page à défiler horizontalement. */
.bo__tableau-cadre{overflow-x:auto}
.bo__tableau{width:100%;border-collapse:collapse;font-size:var(--fs-body-s)}
.bo__tableau th,.bo__tableau td{
  text-align:left;padding:var(--sp-3) var(--sp-4);
  border-bottom:1px solid var(--hairline);
  vertical-align:top;
}
.bo__tableau th{
  font-size:var(--fs-overline);letter-spacing:var(--ls-caps);
  text-transform:uppercase;color:var(--ink-soft);
  background:var(--bg-alt);white-space:nowrap;
}
.bo__tableau td:last-child{white-space:nowrap;color:var(--ink-soft)}

/* La pastille porte le MOT, pas seulement une couleur : un tableau qui ne se
   lit qu'en couleur est inutilisable pour qui ne les distingue pas. */
.bo__pastille{
  display:inline-block;padding:2px var(--sp-3);
  font-size:var(--fs-overline);letter-spacing:var(--ls-caps);
  text-transform:uppercase;font-weight:600;
}
.est-oui .bo__pastille{background:#C6E7C6;color:#1f4d1f}
.est-non .bo__pastille{background:#F2C7C7;color:#6b1f1f}

/* Liste des convives dans une cellule : une personne par ligne, sans puce —
   la cellule est déjà une unité, une puce en plus alourdirait le tableau. */
.bo__convives{list-style:none;display:grid;gap:2px}
/* Colonne du décompte. Ferrée à gauche comme les autres colonnes — le
   centrage a été essayé puis écarté : l'en-tête restant à gauche, les
   chiffres se retrouvaient décalés par rapport à leur propre titre.
   En gras, pour se repérer en balayant la colonne. */
.bo__col-nb{white-space:nowrap}
td.bo__col-nb{font-weight:600}
/* Le compteur qui compte vraiment : c'est le nombre de couverts. */
.bo__compteur--personnes{background:var(--lin)}

/* L'export en face des compteurs. `flex-end` : le bouton se cale sur le PIED
   des blocs, pas sur leur milieu — les blocs sont hauts, un alignement centré
   l'aurait fait flotter. `wrap` pour qu'il passe dessous quand la place
   manque, plutôt que d'écraser les compteurs. */
.bo__bandeau{
  display:flex;flex-wrap:wrap;align-items:flex-end;justify-content:space-between;
  gap:var(--sp-5);
  margin-bottom:var(--sp-6);   /* la marge portée jusqu'ici par les compteurs */
}
.bo__bandeau .bo__compteurs{margin-bottom:0}
/* Déconnexion et export, ferrés à droite. La colonne s'étire sur la hauteur
   des compteurs (`align-self:stretch`, qui l'emporte sur le `flex-end` du
   bandeau) et écarte ses deux éléments : la déconnexion se cale sur le HAUT
   des blocs, l'export sur leur pied. */
.bo__bandeau-actions{
  align-self:stretch;
  display:flex;flex-direction:column;align-items:flex-end;
  justify-content:space-between;gap:var(--sp-3);
}
.bo__vide{color:var(--ink-soft)}

/* ---- Édition en place ---------------------------------------------------
   Le tableau est un document de travail : les noms et les adresses s'y
   corrigent directement. Les champs se fondent dans le tableau au repos —
   sinon 30 cadres de saisie rendraient la lecture impossible — et se
   révèlent au survol et à la saisie. */
.bo__legende{
  caption-side:top;text-align:left;
  padding-bottom:var(--sp-3);
  font-size:var(--fs-body-s);color:var(--ink-soft);
}
/* La cellule est le repère de la coche, posée EN ABSOLU par-dessus la fin du
   champ : en flux, son apparition rétrécirait le champ et ferait sauter le
   texte. Le champ réserve donc en permanence la place à droite.
   Les marges négatives — qui évitent que le rembourrage du champ n'élargisse
   la colonne — sont portées par la CELLULE et non par le champ : sinon la
   boîte de référence de la coche est décalée de 8px à droite et de 4px en
   bas par rapport au champ visible, et la coche déborde du cadre. */
.bo__cellule{
  position:relative;display:block;
  margin:calc(-1 * var(--sp-1)) calc(-1 * var(--sp-2));
  /* Source unique de la géométrie de la coche : sa largeur, la place que le
     champ lui réserve à droite et l'arrondi qu'elle doit suivre en dérivent. */
  --bo-coche:30px;
  /* Arrondi léger, exception locale assumée : la piste met tous les rayons à
     zéro (style.css l.8), mais un champ de saisie dans un tableau n'est pas
     un élément de la direction artistique du site public. */
  --bo-rayon:3px;
}
.bo__champ{
  display:block;
  width:100%;min-width:14ch;
  padding:var(--sp-1) calc(var(--bo-coche) + var(--sp-2)) var(--sp-1) var(--sp-2);
  margin:0;
  font-family:inherit;font-size:inherit;line-height:inherit;color:inherit;
  background:transparent;border:none;border-radius:var(--bo-rayon);
  transition:background var(--dur-fast) var(--ease-out);
}
.bo__champ:hover{background:color-mix(in srgb, var(--lin) 55%, transparent)}
/* Encre, comme la coche qui la prolonge : les deux forment un seul objet
   bordé. 2px et non 1 : c'est aussi l'indicateur de focus du champ. */
.bo__champ:focus{background:var(--champ-fond);outline:2px solid var(--ink);outline-offset:0}

/* La coche : cachée tant que rien n'a changé, elle est le seul signal que la
   valeur est modifiée ET le moyen de l'enregistrer. */
.bo__valider{
  /* Toute la hauteur du champ : `top`/`bottom` à 0 plutôt qu'une hauteur
     fixe centrée — un petit carré flottant au milieu du champ ne se
     rattachait à rien. */
  position:absolute;top:0;bottom:0;right:0;
  width:var(--bo-coche);
  display:grid;place-items:center;
  /* les coins de droite suivent l'arrondi du champ, sinon la coche dépasse */
  padding:0;border:1px solid var(--ink);
  border-radius:0 var(--bo-rayon) var(--bo-rayon) 0;
  /* Encre et crème, comme `.btn--solid` : la coche est un bouton du site, pas
     une pastille à part. Le survol inverse le rapport, là aussi comme le
     bouton plein. */
  background:var(--ink);color:var(--bg);
  cursor:pointer;
  transition:background var(--dur-fast) var(--ease-out),
             color var(--dur-fast) var(--ease-out);
}
.bo__valider svg{width:15px;height:15px}
.bo__valider:hover:not(:disabled){background:transparent;color:var(--ink)}
/* Visible mais inactive tant que rien n'a changé : elle annonce ce qui va se
   passer sans se donner à cliquer. En contour plutôt qu'en aplat, l'écart
   avec l'état actif se voit d'un coup d'œil. */
.bo__valider:disabled{
  background:transparent;color:var(--ink);
  border-color:color-mix(in srgb, var(--ink) 35%, transparent);
  opacity:.45;cursor:default;
}

/* Confirmation brève après enregistrement, puis retour à la normale. */
.est-enregistre .bo__champ{background:#E4F0E4}
/* L'erreur est affichée SOUS le champ concerné, pas en haut de page : c'est
   là qu'on regarde en validant. */
.est-en-erreur .bo__champ{background:#F6E2E1;outline:2px solid var(--erreur)}
.bo__cellule[data-message]::after{
  content:attr(data-message);
  display:block;margin-top:var(--sp-2);
  font-size:var(--fs-body-s);color:var(--erreur);
}

/* Le lien de suppression reste discret : c'est l'action rare de la ligne, et
   elle passe de toute façon par une confirmation. Le rouge n'apparaît qu'au
   survol, quand l'intention est là. */
/* Une poubelle, sans libellé visible : le mot « Supprimer » répété sur chaque
   ligne alourdissait le tableau. Le libellé reste dans le DOM pour les
   lecteurs d'écran (`.u-invisible`), et la cible fait 32px malgré un picto de
   18 — un picto seul est vite trop petit à cliquer. */
.bo__supprimer{
  display:inline-grid;place-items:center;
  width:32px;height:32px;
  margin:-7px;                     /* la cible ne gonfle pas la ligne */
  color:var(--ink-soft);
  transition:color var(--dur-fast) var(--ease-out);
}
.bo__supprimer svg{width:18px;height:18px}
.bo__supprimer:hover{color:var(--erreur)}
.bo__bouton-danger{background:var(--erreur);border-color:var(--erreur)}
.bo__bouton-danger:hover{background:var(--ink);border-color:var(--ink)}

/* Retours d'action. Le fond ne suffit pas à distinguer une réussite d'un
   échec pour qui ne voit pas les couleurs : le bord gauche épais donne un
   second signal, et le texte dit toujours ce qui s'est passé. */
.bo__flash{
  margin-bottom:var(--sp-4);
  padding:var(--sp-3) var(--sp-4);
  border-left:4px solid;
  font-size:var(--fs-body-s);
}
.bo__flash--succes{background:#E4F0E4;border-color:#4A7A4A;color:var(--ink)}
.bo__flash--erreur{background:#F6E2E1;border-color:var(--erreur);color:var(--ink)}

/* ---- Popin de confirmation (élément <dialog> natif) ---- */
.bo__dialogue{
  width:min(440px, calc(100vw - 2 * var(--sp-5)));
  /* Le navigateur centre les `<dialog>` modaux avec un `margin:auto` de sa
     feuille par défaut — que le reset du site écrase. On le remet, sinon la
     popin se colle en haut à gauche de l'écran. */
  inset:0;margin:auto;
  padding:var(--sp-6);
  border:none;
  background:var(--creme);color:var(--ink);
  box-shadow:var(--shadow-lg);
}
.bo__dialogue::backdrop{background:rgba(46,38,32,.6)}
.bo__dialogue-titre{font-size:var(--fs-title);line-height:var(--lh-title)}
.bo__dialogue-qui{margin-top:var(--sp-3);font-weight:600}
.bo__dialogue-note{margin-top:var(--sp-2);font-size:var(--fs-body-s);color:var(--ink-soft)}
.bo__dialogue-actions{
  margin-top:var(--sp-5);
  display:flex;align-items:center;justify-content:flex-end;gap:var(--sp-5);
}
.bo__dialogue-actions .bo__deconnexion{
  padding:0;border:none;background:none;cursor:pointer;
  font-family:inherit;text-decoration:underline;text-underline-offset:3px;
}

/* Écran de confirmation, le repli sans JavaScript de la popin. */
.bo__recap{
  margin-top:var(--sp-5);padding:var(--sp-5);
  background:var(--bg-alt);
}
.bo__recap-ligne{display:flex;align-items:center;gap:var(--sp-3);color:var(--ink-soft)}
.bo__recap .bo__convives{margin-top:var(--sp-3);font-weight:600}
.bo__recap-email{margin-top:var(--sp-3);color:var(--ink-soft)}
.bo__avertissement{margin-top:var(--sp-4);color:var(--ink)}
.bo .bo__actions{margin-top:var(--sp-6)}

/* Réservé aux lecteurs d'écran : l'en-tête de la colonne d'actions n'a rien à
   afficher, mais la colonne doit être annoncée. */
.u-invisible{
  position:absolute;width:1px;height:1px;
  margin:-1px;padding:0;overflow:hidden;
  clip-path:inset(50%);white-space:nowrap;
}
.bo__connexion,.bo__message{
  background:var(--creme);
  box-shadow:var(--shadow-lg);
  width:100%;max-width:440px;
  padding:clamp(var(--sp-6),4vw,var(--sp-10));
  display:grid;gap:var(--sp-5);
  text-align:center;
}
.bo__connexion .field{text-align:left}
.bo__connexion .btn,.bo__message .btn{justify-self:center}
.bo__erreur{
  background:#F2C7C7;color:#6b1f1f;
  padding:var(--sp-3) var(--sp-4);font-size:var(--fs-body-s);
}
.bo__erreurs{list-style:none;display:grid;gap:var(--sp-2);color:var(--ink-soft)}
