/* =============================================================================
   Casa Paradisu — « Tarifs et disponibilités »
   -----------------------------------------------------------------------------
   TROISIÈME couche, chargée par cette seule page, après `premium.css`.

   Règle de la feuille : elle ne contient QUE de la présentation. Aucun montant,
   aucune règle de séjour, aucune date n'y figure — tout cela reste dans
   `rates-data.js` et `calendar.js`, que ce fichier ne touche pas.

   Deux familles de sélecteurs, et pas une troisième :
     — `body[data-page="tarifs"] …` pour tout ce qui affine un composant
       partagé (le calendrier, la carte d'estimation, le tableau). Le poids
       supplémentaire du sélecteur d'attribut suffit à passer devant
       `components.css` sans `!important`, et garantit qu'aucune autre page
       n'est touchée.
     — des classes neuves (`.frise`, `.respire`, `.pas`, `.cal-swap`) pour ce
       qui n'existait pas.

   Le vocabulaire de mouvement tient en une ligne : horizontal pour ce qui
   relie (le trait arrivée-départ, la frise des mois, le changement de mois),
   vertical et très court pour ce qui apparaît (8 à 18 px, jamais davantage).
   ========================================================================== */


/* ==========================================================================
   1. Jetons de mouvement
   Portés par le `body` : ils descendent partout par héritage sans exister
   ailleurs sur le site. La courbe `--ease-lux` n'est pas une invention — elle
   est déjà écrite littéralement une quinzaine de fois dans `premium.css` ;
   on lui donne enfin un nom ici, le jour où elle remontera dans `tokens.css`
   ces déclarations disparaîtront d'elles-mêmes.
   ========================================================================== */

body[data-page="tarifs"] {
  --ease-lux:    cubic-bezier(0.22, 1, 0.36, 1);     /* sortie longue, sans rebond */
  --ease-doux:   cubic-bezier(0.33, 0, 0.2, 1);      /* aller-retour court */

  --dur-appui:    90ms;   /* enfoncement : plus bref que tout le reste */
  --dur-micro:   170ms;   /* survol d'une cible : la main ne doit pas attendre */
  --dur-moyen:   380ms;   /* changement d'état visible */
  --dur-long:    620ms;   /* apparition, trait qui se dessine */

  /* La bande de séjour. `--c-stone` pur (le réglage d'origine) fait un aplat
     de sable franc : lisible, mais il pèse autant que les bornes du séjour et
     la lecture hésite. Deux niveaux translucides suffisent — les nuits
     retenues, puis l'aperçu pendant le choix, deux fois plus léger. */
  --cal-band:    rgba(213, 200, 182, 0.55);
  --cal-apercu:  rgba(213, 200, 182, 0.28);

  --surface-hover: rgba(32, 35, 31, 0.045);
  --border-hover:  rgba(32, 35, 31, 0.42);
}


/* ==========================================================================
   2. Entrée de page
   L'ordre est déjà celui du brief — surtitre, titre, chapeau, précision — et
   `.home-seq` de `pages.css` échelonne déjà les trois premiers de 80 ms. Il
   ne manquait que le quatrième (la précision sur le calendrier, qui n'était
   pas `.reveal` du tout) et le masque du titre.
   ========================================================================== */

/* Le titre monte derrière son propre bord bas. Un tiers de cadratin, pas
   davantage : sur deux lignes, un masque de hauteur pleine ferait entrer la
   première ligne depuis la place de la seconde, ce qui se remarque. Là, seule
   la dernière ligne émerge, et c'est exactement l'effet cherché.

   `overflow: hidden` est posé sur le `h1`, qui porte aussi `.reveal--rise` et
   se fait donc observer par `main.js`. Sans danger : contrairement à
   `clip-path`, `overflow` ne réduit pas la boîte de l'élément lui-même, seule
   sa descendance est rognée — l'IntersectionObserver continue de le voir. */
/* Le retrait haut de `.page-head` a quitté ce fichier : Contact souffrait du
   même vide de 160 px, et la même valeur écrite à deux endroits aurait fini
   par diverger. La règle vit maintenant dans `premium.css`, section 10,
   « Entrée des pages d'action », pour les deux pages à la fois. */

body[data-page="tarifs"] .page-head h1 {
  overflow: hidden;
  /* Les jambages de « p » et « y » vivent sous la ligne de base : sans ce
     demi-cadratin, `overflow` les couperait en permanence, y compris une
     fois l'animation terminée. */
  padding-bottom: 0.08em;
}
/* `.reveal--rise` deplace deja le h1 de douze pixels. Laisser les deux
   agir aurait fait trente-deux pixels de course sur un titre — le double de
   ce qu'on s'autorise partout ailleurs. Le masque reprend donc seul le
   mouvement, avec EXACTEMENT la courbe et la duree de `.reveal--rise` : le
   fondu du titre et sa remontee ne font plus qu'un seul geste.
   L'amplitude est en cadratins, pas en pixels : dix-huit pixels sous un
   titre de soixante, neuf sous un titre de trente. Elle suit la typographie
   au lieu de la contredire. */
body[data-page="tarifs"] .page-head h1.reveal--rise { transform: none; }
body[data-page="tarifs"] .page-head h1 > span {
  display: block;
  transform: translateY(0.3em);
  transition: transform 1400ms cubic-bezier(0.16, 1, 0.3, 1);
}
body[data-page="tarifs"] .page-head h1.is-in > span { transform: none; }

/* Parallaxe d'entrée. `initParallax()` de `premium.js` pose `--shift` en
   pixels sur tout `[data-parallax]` ; il ne restait qu'à en faire quelque
   chose ici. Translation en deux dimensions, volontairement : `translate3d`
   promeut la couche et laisse le texte se ramollir pendant le déplacement.
   Huit pixels d'amplitude sur un titre de soixante, c'est de la respiration,
   pas un effet. */
body[data-page="tarifs"] .page-head .shell[data-parallax] {
  transform: translate(0, var(--shift, 0px));
}


/* ==========================================================================
   3. Le calendrier
   La logique ne bouge pas d'une ligne : `calendar.js` garde la main sur les
   états, les classes et le rendu. Tout ce qui suit ne fait que redessiner ce
   qu'il annonce.
   ========================================================================== */

/* --- 3.1 Le socle d'une journée ------------------------------------------ */

/* `isolation` fait de chaque case un contexte d'empilement à elle seule. Les
   deux pseudo-éléments qui suivent portent un `z-index` négatif : dans un
   contexte local, ils se peignent APRÈS le fond de la case et AVANT son
   texte. C'est ce qui permet d'avoir une bande, une pastille et un chiffre
   superposés dans le bon ordre sans ajouter le moindre élément au balisage —
   et le balisage, ici, appartient à `calendar.js`. */
body[data-page="tarifs"] .day {
  isolation: isolate;
  transition:
    background-color var(--dur-micro) var(--ease-doux),
    border-color     var(--dur-micro) var(--ease-doux),
    color            var(--dur-micro) var(--ease-doux),
    box-shadow       var(--dur-micro) var(--ease-doux),
    transform        var(--dur-micro) var(--ease-doux);
}

/* --- 3.2 Survol d'une journée libre --------------------------------------- */

/* Un pixel d'élévation, une ombre très diffuse, un filet qui se précise. La
   case passe devant ses voisines le temps du survol pour que l'ombre ne soit
   pas coupée par la case suivante. */
body[data-page="tarifs"] .day--request:hover {
  transform: translateY(-1px);
  box-shadow: 0 3px 12px -7px rgba(32, 35, 31, 0.55);
  z-index: 3;
}

/* Appui. `:active` tient pendant tout le maintien du bouton ; au relâchement
   `calendar.js` reconstruit la grille et la case est remplacée — la détente
   est donc reprise par l'animation d'arrivée de la pastille, plus bas. */
body[data-page="tarifs"] .day--request:active {
  transform: scale(0.96);
  transition-duration: var(--dur-appui);
}

/* --- 3.3 La plage de séjour ----------------------------------------------- */

/* La bande. `inset-inline: -2px` n'est pas un réglage approximatif : la boîte
   de rembourrage s'arrête 1 px avant le bord de la case (il y a un filet), et
   la grille ajoute 2 px de gouttière. Deux pixels de débord de chaque côté
   rejoignent donc exactement la bande voisine — ni couture visible, ni
   recouvrement. Aux deux extrémités d'une ligne, 1 px déborde du mois : c'est
   invisible, et c'est le prix d'une bande réellement continue. */
body[data-page="tarifs"] .day--between::before,
body[data-page="tarifs"] .day--preview::before,
body[data-page="tarifs"] .day--start::before,
body[data-page="tarifs"] .day--end::before {
  content: "";
  position: absolute;
  z-index: -2;
  inset-block: 4px;
  inset-inline: -2px;
  background-color: var(--cal-band);
  transition: background-color var(--dur-micro) var(--ease-doux);
}

/* Les fonds d'origine sont neutralisés : opaques, ils masqueraient la bande
   qui se peint juste derrière eux. */
body[data-page="tarifs"] .day--between,
body[data-page="tarifs"] .day--preview,
body[data-page="tarifs"] .day--start,
body[data-page="tarifs"] .day--end {
  background-color: transparent;
  border-color: transparent;
}

/* L'aperçu, pendant le choix de la date de départ, est deux fois plus léger
   que la plage confirmée : on distingue immédiatement ce qui est retenu de ce
   qui est seulement proposé. */
body[data-page="tarifs"] .day--preview::before { background-color: var(--cal-apercu); }

/* La bande part du centre de l'arrivée et s'arrête au centre du départ.
   Elle ne se dessine que si une plage existe réellement : sans `:has()`, une
   simple date d'arrivée traînerait derrière elle une demi-bande vers nulle
   part. Les navigateurs sans `:has()` n'affichent pas cette moitié — la
   sélection reste parfaitement lisible, seul le raccord est plus sec. */
body[data-page="tarifs"] .day--start::before { display: none; }
body[data-page="tarifs"] .calendar__months:has(.day--end) .day--start::before,
body[data-page="tarifs"] .calendar__months:has(.day--preview) .day--start::before {
  display: block;
  inset-inline-start: 50%;
}
body[data-page="tarifs"] .day--end::before { inset-inline-end: 50%; }

/* Les bornes : une pastille ronde, pleine, posée SUR la bande. `z-index: -1`
   la place au-dessus de la bande (-2) et sous le chiffre. */
body[data-page="tarifs"] .day--start::after,
body[data-page="tarifs"] .day--end::after {
  content: "";
  position: absolute;
  z-index: -1;
  inset: 1px;
  border-radius: 999px;
  background-color: var(--c-charcoal);
  animation: tarif-borne var(--dur-moyen) var(--ease-lux) both;
}
body[data-page="tarifs"] .day--start,
body[data-page="tarifs"] .day--end {
  color: var(--c-ivory);
  border-radius: 999px;
}

/* L'arrivée de la borne — c'est elle qui rend l'appui. La grille étant
   reconstruite à chaque clic, l'élément est neuf et l'animation se joue
   toute seule, sans qu'aucun script n'ait à l'amorcer. */
@keyframes tarif-borne {
  from { transform: scale(0.72); opacity: 0; }
  to   { transform: scale(1);    opacity: 1; }
}

/* Une case déjà prise dans la plage ne se soulève pas et ne devient pas
   blanche : la bande doit rester d'un seul tenant. Elle se fonce d'un cran,
   ce qui suffit à signaler qu'elle est survolable. */
body[data-page="tarifs"] .day--between:hover,
body[data-page="tarifs"] .day--preview:hover,
body[data-page="tarifs"] .day--start:hover,
body[data-page="tarifs"] .day--end:hover {
  transform: none;
  box-shadow: none;
  background-color: transparent;
}
body[data-page="tarifs"] .day--between:hover::before,
body[data-page="tarifs"] .day--preview:hover::before {
  background-color: rgba(213, 200, 182, 0.72);
}

/* --- 3.4 Journées indisponibles ------------------------------------------- */

/* La barre oblique d'origine est conservée : elle dit « fermé » sans couleur.
   Seule la barre est adoucie — l'opacité du bloc, elle, ne bouge pas.
   Premier réglage : 0,55 au lieu de 0,7. Mesuré : le chiffre du jour tombait
   de 2,84:1 à 2,21:1 sur son fond. Ces journées sont des commandes inactives,
   donc hors du champ du critère 1.4.3, mais « discrètes » ne veut pas dire
   moins lisibles qu'avant. Le réglage d'origine est conservé tel quel. */
body[data-page="tarifs"] .day--closed::after { opacity: 0.45; }

/* --- 3.5 Changement de mois ------------------------------------------------ */

/* `.cal-swap` n'existe que pour donner un repère au fantôme. Rien d'autre. */
.cal-swap { position: relative; }

/* Le mois sortant. `calendar.js` vide `innerHTML` puis reconstruit ; le
   script de cette page récupère les nœuds retirés — ils sont intacts — et les
   replace ici le temps de la sortie. Aucune copie, aucun surcoût. */
.cal-ghost {
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 1;
  transition:
    opacity   var(--dur-moyen) var(--ease-doux),
    transform var(--dur-moyen) var(--ease-lux);
}
.cal-ghost.is-out {
  opacity: 0;
  transform: translate3d(calc(var(--sens, 1) * -18px), 0, 0);
}

body[data-page="tarifs"] .calendar__months {
  transition:
    opacity   var(--dur-moyen) var(--ease-doux),
    transform var(--dur-long)  var(--ease-lux);
}
/* `transition: none` n'est pas une precaution, c'est le coeur du reglage.
   Sans elle, poser cette classe lance une transition VERS l'etat de depart :
   le mois entrant se met a disparaitre pendant deux images, on le lui retire,
   il revient — et l'entree n'a jamais eu lieu. L'etat de depart doit etre
   pris instantanement. Au retrait de la classe, le style d'arrivee porte de
   nouveau une duree, et c'est la que la transition se joue. */
body[data-page="tarifs"] .calendar__months.is-entrant {
  opacity: 0;
  transform: translate3d(calc(var(--sens, 1) * 18px), 0, 0);
  transition: none;
}

/* --- 3.6 Les flèches de navigation ---------------------------------------- */

/* Le survol d'origine retournait le bouton en aplat charbon : deux carrés
   noirs à côté du nom du mois, c'était la note la plus dure de la page. Un
   lavis léger et un filet net disent la même chose plus bas. */
body[data-page="tarifs"] .calendar__navbtn {
  transition:
    background-color var(--dur-micro) var(--ease-doux),
    border-color     var(--dur-micro) var(--ease-doux),
    transform        var(--dur-micro) var(--ease-doux);
}
body[data-page="tarifs"] .calendar__navbtn:hover:not([disabled]) {
  background-color: var(--surface-hover);
  border-color: var(--c-charcoal);
  color: var(--c-charcoal);
}
body[data-page="tarifs"] .calendar__navbtn:active:not([disabled]) { transform: scale(0.94); }
body[data-page="tarifs"] .calendar__navbtn svg {
  transition: transform var(--dur-micro) var(--ease-lux);
}
body[data-page="tarifs"] [data-calendar-prev]:hover:not([disabled]) svg { transform: translateX(-3px); }
body[data-page="tarifs"] [data-calendar-next]:hover:not([disabled]) svg { transform: translateX(3px); }

/* --- 3.7 La légende ------------------------------------------------------- */

/* Les pastilles doivent montrer exactement les valeurs employées plus haut,
   sans quoi la légende ment. */
body[data-page="tarifs"] .legend__swatch--range { background-color: var(--cal-band); border-color: var(--cal-band); }
body[data-page="tarifs"] .legend__swatch--selected { border-radius: 999px; }


/* ==========================================================================
   3 bis. La grille de réservation
   Le détail des montants était rejeté en pleine largeur SOUS les deux
   colonnes : il commençait cent pixels plus bas que le bas de la carte
   d'estimation, soit à 1 291 px du haut de la page. Sur un portable, il
   fallait défiler quatre cents pixels après le calendrier pour découvrir
   d'où venait le total qu'on venait de lire.

   Il passe donc dans la colonne de DROITE, juste sous la carte : on lit
   « 4 860 € », puis immédiatement d'où viennent les 4 860 €. C'est la
   continuité qui manquait.

   Deux conséquences assumées :
     - la colonne droite devient la plus chargée ; le rapport des colonnes
       se resserre de 1,6 à 1,24 pour lui donner de quoi tenir un tableau
       à trois colonnes ;
     - la carte d'estimation perd son adhérence (voir plus bas).
   ========================================================================== */

@media (min-width: 1025px) {
  body[data-page="tarifs"] .booking {
    grid-template-columns: minmax(0, 1.24fr) minmax(0, 1fr);
  }
}
body[data-page="tarifs"] .booking { row-gap: clamp(32px, 3vw, 52px); }

/* Le tableau suit le total, à distance de lecture — pas de respiration de
   section entre une somme et son explication. */
body[data-page="tarifs"] .booking__detail { margin-top: clamp(28px, 3vw, 44px); }


/* ==========================================================================
   4. La carte d'estimation
   ========================================================================== */

/* L'adhérence est retirée, et ce n'est pas un renoncement.

   Elle ne servait déjà presque à rien : la carte mesure 537 px et le
   calendrier 509 — la carte est la plus haute des deux, elle ne peut donc
   pas s'échapper pendant qu'on choisit ses dates. Elle ne se décollait qu'
   une fois les deux dépassés.

   Et surtout, elle devient nuisible : son bloc conteneur est désormais la
   colonne entière, tableau compris. Le calcul est net — la carte se serait
   décollée de 433 px et aurait recouvert le tableau à partir du moment où
   l'on descend le lire. */
body[data-page="tarifs"] .estimate { position: static; }

body[data-page="tarifs"] .estimate {
  /* Posée par le script pendant le survol, remise à zéro au départ du
     pointeur : au repos la carte n'a aucune transformation, et son texte est
     rendu exactement comme celui du reste de la page. */
  transform: var(--carte-tilt, none);
  transition: transform var(--dur-moyen) var(--ease-lux);
}
body[data-page="tarifs"] .estimate.is-tilting { transition-duration: var(--dur-appui); }

/* Le titre de la carte et l'écart des champs : ces trois valeurs étaient
   écrites en attribut `style` dans le HTML. Elles sont simplement remontées
   ici, à l'identique. */
body[data-page="tarifs"] .estimate > h2 { margin-bottom: var(--gap-title); }
body[data-page="tarifs"] .estimate .field--voyageurs { margin-bottom: var(--field-gap); }
body[data-page="tarifs"] .estimate .field__hint { margin-bottom: var(--field-gap); }
body[data-page="tarifs"] .estimate .actions { margin-top: var(--gap-action); }

/* --- 4.1 Nombre de voyageurs ---------------------------------------------- */

/* Trois colonnes de largeur fixe, calees a gauche : « - 2 + » se lit comme
   une commande. En pleine largeur, le chiffre flottait au milieu de trois
   cents pixels de vide et le bloc se lisait comme un champ de saisie ordinaire
   — ce qu'il n'est plus. */
.pas {
  display: grid;
  grid-template-columns: var(--btn-h) 5.5rem var(--btn-h);
  align-items: stretch;
  gap: var(--sp-2);
}
.pas__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--btn-h);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background-color: var(--bg-raised);
  color: var(--text-strong);
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
  transition:
    background-color var(--dur-micro) var(--ease-doux),
    border-color     var(--dur-micro) var(--ease-doux),
    opacity          var(--dur-micro) var(--ease-doux),
    transform        var(--dur-micro) var(--ease-doux);
}
.pas__btn:hover:not([disabled]) { background-color: var(--surface-hover); border-color: var(--c-charcoal); }
.pas__btn:active:not([disabled]) { transform: scale(0.93); }
.pas__btn[disabled] { opacity: 0.28; cursor: not-allowed; }

/* Le champ reste un `input[type=number]` : clavier numérique sur mobile,
   saisie directe au clavier, et surtout `calendar.js` continue d'écouter son
   événement `change` sans savoir que des boutons existent. Seules les
   flèches natives disparaissent, doublées par les deux boutons. */
.pas__num { text-align: center; -moz-appearance: textfield; appearance: textfield; }
.pas__num::-webkit-outer-spin-button,
.pas__num::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }

.pas__num.is-bump { animation: pas-bump var(--dur-micro) var(--ease-lux); }
@keyframes pas-bump {
  from { transform: translateY(var(--sens-pas, 4px)); opacity: 0.45; }
  to   { transform: none; opacity: 1; }
}

/* --- 4.2 Arrivée et départ : le trait qui relie --------------------------- */

/* Vingt pixels réservés en permanence sous les deux champs. Les réserver
   coûte un vide discret tant qu'aucune plage n'existe ; les ajouter au moment
   de la sélection ferait sauter toute la carte, et la carte est adhérente. */
body[data-page="tarifs"] .field-row--dates {
  position: relative;
  padding-bottom: 20px;
  margin-bottom: var(--sp-2);
}

/* Le trait part du centre du champ d'arrivée et court jusqu'au centre du
   champ de départ. Il se dessine de la gauche vers la droite, dans le sens
   du séjour. */
body[data-page="tarifs"] .field-row--dates::before {
  content: "";
  position: absolute;
  /* Le centre d'une colonne n'est pas a 25 % : la gouttiere lui prend un
     quart de sa largeur. `25% - gouttiere/4` tombe sur le milieu exact des
     deux champs, quelle que soit la largeur de la carte. */
  left: calc(25% - var(--field-gap) / 4);
  right: calc(25% - var(--field-gap) / 4);
  bottom: 9px;
  height: 1px;
  background-color: var(--c-charcoal-16);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform var(--dur-long) var(--ease-lux);
}
/* Le point d'arrivée, posé une fois le trait rendu. */
body[data-page="tarifs"] .field-row--dates::after {
  content: "";
  position: absolute;
  right: calc(25% - var(--field-gap) / 4 - 2px);
  bottom: 7px;
  width: 5px; height: 5px;
  border-radius: 999px;
  background-color: var(--c-charcoal);
  opacity: 0;
  transform: scale(0.4);
  transition:
    opacity   260ms var(--ease-doux) 460ms,
    transform 360ms var(--ease-lux)  460ms;
}
body[data-page="tarifs"] .field-row--dates.is-liee::before { transform: scaleX(1); }
body[data-page="tarifs"] .field-row--dates.is-liee::after  { opacity: 1; transform: scale(1); }

/* Un champ qui reçoit sa date : le libellé se renforce brièvement. */
body[data-page="tarifs"] .field__label { transition: color var(--dur-moyen) var(--ease-doux); }
body[data-page="tarifs"] .field:focus-within .field__label { color: var(--text-strong); }
body[data-page="tarifs"] .input[readonly] { cursor: default; }
body[data-page="tarifs"] .input[readonly]:not([value=""]) { color: var(--text-strong); }

/* --- 4.3 Le détail chiffré ------------------------------------------------ */

/* `renderEstimate()` reconstruit la liste entière à chaque changement : les
   lignes sont donc neuves et une `animation` se joue d'elle-même. Un
   `transition` aurait demandé une seconde image pour amorcer, donc du script
   pour rien. Quatre pixels, quarante millisecondes d'écart : on perçoit un
   ordre de lecture, pas une cascade. */
body[data-page="tarifs"] .estimate__row {
  animation: estim-in var(--dur-moyen) var(--ease-lux) both;
}
body[data-page="tarifs"] .estimate__row:nth-child(2) { animation-delay: 40ms; }
body[data-page="tarifs"] .estimate__row:nth-child(3) { animation-delay: 80ms; }
body[data-page="tarifs"] .estimate__row:nth-child(4) { animation-delay: 120ms; }
body[data-page="tarifs"] .estimate__row:nth-child(5) { animation-delay: 160ms; }
body[data-page="tarifs"] .estimate__row:nth-child(6) { animation-delay: 200ms; }
body[data-page="tarifs"] .estimate__row:nth-child(n+7) { animation-delay: 240ms; }
@keyframes estim-in {
  from { opacity: 0; transform: translateY(5px); }
  to   { opacity: 1; transform: none; }
}

/* Les remises méritent d'être vues : le montant passe au vert de maquis —
   c'était déjà le cas — et le libellé cesse d'être gris. */
body[data-page="tarifs"] .estimate__row--discount dt { color: var(--text-body); }

/* Le total. `font-variant-numeric: tabular-nums` est déjà posé par
   `components.css` : pendant le décompte, la largeur ne bouge pas d'un
   dixième de pixel et rien ne tremble. */
body[data-page="tarifs"] .estimate__num { display: inline-block; }

body[data-page="tarifs"] .estimate__note { transition: opacity var(--dur-moyen) var(--ease-doux); }

/* --- 4.4 « Effacer la sélection » ----------------------------------------- */

/* Le bouton garde sa place — la carte est adhérente, elle ne doit pas
   changer de hauteur sous le curseur — mais il ne se montre que lorsqu'il a
   quelque chose à effacer. Le script pose aussi `disabled`, sans quoi la
   tabulation s'arrêterait sur une commande invisible. */
body[data-page="tarifs"] .btn--effacer {
  opacity: 0;
  transform: translateY(4px);
  pointer-events: none;
  transition:
    opacity   var(--dur-moyen) var(--ease-doux),
    transform var(--dur-moyen) var(--ease-lux),
    background-color var(--dur) var(--ease),
    border-color var(--dur) var(--ease),
    color var(--dur) var(--ease);
}
body[data-page="tarifs"] .btn--effacer.is-utile {
  opacity: 1;
  transform: none;
  pointer-events: auto;
}


/* ==========================================================================
   5. Les boutons d'appel
   Une seule mécanique, deux emplacements : la carte d'estimation et la bande
   de clôture. Le magnétisme et l'appui passent par des propriétés
   personnalisées — sans quoi le script écraserait la transformation d'appui
   en écrivant `style.transform`, et réciproquement.
   ========================================================================== */

.btn--fleche { position: relative; }
.btn--fleche .btn__mot {
  display: inline-block;
  transition: transform 450ms var(--ease-lux);
}
.btn__fleche {
  flex: 0 0 auto;
  width: 18px;
  height: auto;
  transition: transform 450ms var(--ease-lux);
}
.btn--fleche:hover .btn__fleche,
.btn--fleche:focus-visible .btn__fleche { transform: translateX(6px); }
.btn--fleche:hover .btn__mot,
.btn--fleche:focus-visible .btn__mot { transform: translateX(-2px); }

[data-magnetic] {
  transform: translate3d(var(--mx, 0px), var(--my, 0px), 0) scale(var(--press, 1));
  transition: transform var(--dur-moyen) var(--ease-lux);
}
[data-magnetic].is-magnet { transition-duration: 120ms; }
[data-magnetic]:active { --press: 0.98; transition-duration: var(--dur-appui); }


/* ==========================================================================
   6. Détail des montants
   Le tableau ne change pas de style : mêmes filets, mêmes graisses, mêmes
   colonnes. Il gagne une entrée en séquence et un survol qui isole la ligne
   lue — c'est la continuité demandée entre l'estimation et son explication.
   ========================================================================== */

/* Les lignes arrivent dans l'ordre du tableau. `transform` est explicitement
   annulé : le socle `.reveal` déplace de six pixels, et une transformation
   sur un `tr` reste un terrain incertain d'un moteur à l'autre. L'opacité
   seule dit exactement la même chose et ne risque rien. */
body[data-page="tarifs"] .rate-table tbody tr.reveal { transform: none; }
body[data-page="tarifs"] .rate-table tbody tr.reveal { transition-duration: 520ms; }
body[data-page="tarifs"] .rate-table tbody tr:nth-child(2) { transition-delay: 60ms; }
body[data-page="tarifs"] .rate-table tbody tr:nth-child(3) { transition-delay: 120ms; }
body[data-page="tarifs"] .rate-table tbody tr:nth-child(4) { transition-delay: 180ms; }
body[data-page="tarifs"] .rate-table tbody tr:nth-child(5) { transition-delay: 240ms; }
body[data-page="tarifs"] .rate-table tbody tr:nth-child(6) { transition-delay: 300ms; }

/* `pages.css` retire le filet de la derniere rangee sur les `td` seulement :
   il restait un bout de trait sous le libelle, large d'une colonne. Le
   tableau se fermait de travers. */
body[data-page="tarifs"] .rate-table tbody tr:last-child th { border-bottom: 0; }

body[data-page="tarifs"] .rate-table th,
body[data-page="tarifs"] .rate-table td {
  transition:
    opacity          var(--dur-moyen) var(--ease-doux),
    color            var(--dur-moyen) var(--ease-doux),
    border-color     var(--dur-moyen) var(--ease-doux),
    background-color var(--dur-moyen) var(--ease-doux);
}

/* Le reste du tableau s'efface, la ligne lue reste pleine. Le survol est posé
   sur le `tbody` : quand le curseur quitte le tableau, tout revient d'un
   seul mouvement. `:focus-within` fait le même travail au clavier. */
@media (hover: hover) and (pointer: fine) {
  body[data-page="tarifs"] .rate-table tbody:hover tr.is-in th,
  body[data-page="tarifs"] .rate-table tbody:hover tr.is-in td { opacity: 0.5; }
  body[data-page="tarifs"] .rate-table tbody tr:hover th,
  body[data-page="tarifs"] .rate-table tbody tr:hover td { opacity: 1; }
  body[data-page="tarifs"] .rate-table tbody tr:hover td { border-bottom-color: var(--rule); }
  body[data-page="tarifs"] .rate-table tbody tr:hover th { color: var(--text-strong); }

  /* Trois pixels, dans deux sens opposés : le libellé avance, le montant
     recule. La transformation porte sur un `span`, jamais sur la cellule. */
  body[data-page="tarifs"] .rate-table tbody tr:hover .rate-lib { transform: translateX(3px); }
  body[data-page="tarifs"] .rate-table tbody tr:hover .rate-mt  { transform: translateX(-3px); }
}

body[data-page="tarifs"] .rate-lib,
body[data-page="tarifs"] .rate-mt {
  display: inline-block;
  transition: transform var(--dur-moyen) var(--ease-lux);
}

/* La précision des remises. Elle occupe exactement la place de la description
   et prend le relais au survol : aucune hauteur ne change, aucune infobulle
   ne s'ouvre. Les deux textes restent dans l'arbre d'accessibilité — un
   lecteur d'écran lit la description puis sa précision, ce qui est
   précisément l'ordre voulu. */
/* Les deux textes occupent la MÊME cellule de grille. La boîte prend donc la
   hauteur du plus haut des deux, et aucun des deux ne peut être rogné, quelle
   que soit sa longueur.

   Premier réglage : `position: absolute; inset: 0`. La place offerte à la
   précision dépendait alors de la longueur de la DESCRIPTION — « Location à
   la semaine » tient sur une ligne, sa précision en demande deux, et la
   seconde disparaissait. Une règle de mise en page ne doit pas dépendre du
   nombre de caractères qu'on lui confie. */
body[data-page="tarifs"] .rate-desc { display: grid; }
body[data-page="tarifs"] .rate-desc > * { grid-area: 1 / 1; }
body[data-page="tarifs"] .rate-why {
  opacity: 0;
  /* Invisible, mais posée sur la description : sans cela elle intercepterait
     la sélection du texte qu'elle recouvre. */
  pointer-events: none;
  color: var(--text-strong);
  transition: opacity var(--dur-moyen) var(--ease-doux);
}
/* `:has(.rate-why)` n'est pas une élégance : sans lui, survoler une rangée
   dépourvue de précision effaçait sa description et ne mettait RIEN à la
   place. Les six rangées en ont une aujourd'hui, mais la règle ne doit pas
   dépendre de cela — une septième rangée ajoutée demain sans précision
   garderait simplement sa description. */
@media (hover: hover) and (pointer: fine) {
  body[data-page="tarifs"] .rate-table tr:hover .rate-desc:has(.rate-why) > .rate-dit { opacity: 0; }
  body[data-page="tarifs"] .rate-table tr:hover .rate-why { opacity: 1; }
}
body[data-page="tarifs"] .rate-dit { transition: opacity var(--dur-moyen) var(--ease-doux); }

/* Les deux remises, mises d'un cran en avant : c'est l'information la plus
   favorable du tableau, elle ne doit pas se lire comme une ligne de frais. */
body[data-page="tarifs"] .rate-table .rate-mt--remise {
  color: var(--c-maquis);
  font-weight: var(--fw-medium);
}


/* ==========================================================================
   7. Périodes d'ouverture : la frise
   Treize mois glissants, du mois courant au même mois l'an prochain. Elle
   n'est plus écrite en dur : `tarifs.js` la bâtit à partir de
   `rates.openPeriods` et de la date du jour. Elle suit donc toute seule si la
   saison change, et le point d'aujourd'hui est juste par construction.
   Sans script elle reste vide — `:empty` la retire du flux, et les trois
   notes en dessous disent déjà tout.
   ========================================================================== */

.frise { margin-top: var(--sp-6); }
.frise:empty { display: none; }

/* Le pas d'un mois, gouttière comprise : c'est la seule mesure dont dépend
   tout le reste, y compris la position du point d'aujourd'hui. */
.frise__in {
  --frise-n: 13;
  --frise-jeu: 3px;
  --frise-pas: calc((100% - (var(--frise-n) - 1) * var(--frise-jeu)) / var(--frise-n));
}

/* --- Les repères d'année -------------------------------------------------- */

/* Une seule étiquette par année, posée sur toute la course des mois qu'elle
   couvre : « 2026 » court de septembre à décembre, « 2027 » prend la suite.
   Même grille que les mois, donc les deux rangées tombent au même endroit. */
.frise__ans {
  display: grid;
  grid-template-columns: repeat(13, minmax(0, 1fr));
  gap: var(--frise-jeu);
  margin-bottom: var(--sp-3);
}
.frise__an {
  font-family: var(--font-text);
  font-size: var(--fs-eyebrow);
  font-weight: var(--fw-medium);
  line-height: var(--lh-ui);
  letter-spacing: var(--ls-eyebrow);
  color: var(--text-muted);
  white-space: nowrap;
}
/* L'année en cours est celle qu'on lit en premier : elle reste sombre. */
.frise__an--ici { color: var(--text-strong); }

/* --- La piste ------------------------------------------------------------- */

.frise__piste { position: relative; }

.frise__mois {
  display: grid;
  grid-template-columns: repeat(13, minmax(0, 1fr));
  gap: var(--frise-jeu);
  list-style: none;
  margin: 0;
  padding: 0;
}
.frise__item { display: flex; flex-direction: column; gap: var(--sp-3); min-width: 0; }

/* Le trait : c'est lui qui porte l'information — désormais le TARIF du mois,
   la maison étant ouverte toute l'année.

   La boîte fait 9px quel que soit le montant, et c'est `scaleY` qui la
   remplit : la mise en page ne bouge donc pas d'un mois à l'autre, les noms
   et les montants restent tous sur la même ligne de base. Une hauteur
   variable les aurait décalés verticalement de sept pixels entre janvier et
   août.

   `transform-origin` en bas à gauche sert les deux gestes à la fois : le
   trait se dessine de la gauche (scaleX) et s'épaissit vers le haut depuis
   sa ligne de base (scaleY). */
.frise__trait {
  display: block;
  height: 9px;
  background-color: var(--c-charcoal);
  transform: scaleX(0) scaleY(var(--epaisseur, 0.28));
  transform-origin: left bottom;
  transition: transform var(--dur-long) var(--ease-lux);
}
/* Le mois le plus cher de la frise, seul, prend l'or : un repère, pas une
   deuxième échelle. */
.frise__item--haute .frise__trait { background-color: var(--c-gold-ink); }

/* Le pontage d'un mois à l'autre a été RETIRÉ, et son absence est le réglage.

   Il servait un tout autre propos : quand la frise disait « ouvert / fermé »,
   deux mois ouverts consécutifs formaient une période, et un trait continu
   valait mieux qu'une suite de segments — seul juillet gardait son bord,
   parce qu'août était fermé.

   La frise porte maintenant le TARIF, et la maison est ouverte toute l'année.
   Les treize traits se soudaient donc en un seul ruban de bout en bout, dont
   les épaisseurs se lisaient comme des accidents plutôt que comme douze
   valeurs. Un profil a besoin de ses intervalles : c'est la gouttière qui
   sépare une mesure de la suivante. */

.frise__item--ferme .frise__trait {
  height: 1px;
  margin-block: 0.5px;
  background-color: transparent;
  background-image: repeating-linear-gradient(
    to right,
    rgba(32, 35, 31, 0.4) 0 3px,
    transparent 3px 6px
  );
}

/* Les traits se dessinent l'un après l'autre, de gauche à droite : la saison
   se déroule. Le retard est posé par le script (`--rang`), une seule règle
   suffit désormais pour treize mois — ou pour n'importe quel nombre. */
.frise.is-in .frise__trait { transform: scaleX(1) scaleY(var(--epaisseur, 0.28)); }
.frise__trait { transition-delay: calc(var(--rang, 0) * 38ms); }

.frise__nom {
  font-family: var(--font-text);
  font-size: var(--fs-eyebrow);
  font-weight: var(--fw-medium);
  line-height: var(--lh-ui);
  letter-spacing: var(--ls-eyebrow);
  text-transform: uppercase;
  color: var(--text-muted);
  white-space: nowrap;
}
.frise__item--ouvert .frise__nom { color: var(--text-strong); }

/* Le montant. Un profil se compare, il ne se chiffre pas : il faut les deux.
   Chiffres à chasse fixe pour que les colonnes s'alignent d'un mois à
   l'autre malgré « 500 € » et « 1 100 € ». */
.frise__prix {
  font-family: var(--font-text);
  font-size: var(--fs-fine);
  font-weight: var(--fw-regular);
  line-height: var(--lh-ui);
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  margin-top: calc(var(--sp-3) * -0.35);
}
.frise__item--haute .frise__prix { color: var(--text-strong); font-weight: var(--fw-medium); }
/* `--text-muted` seul, sans opacité supplémentaire : l'abréviation d'un mois
   fermé tenait à 2,64:1 sur l'ivoire, ce qui n'est pas lisible. Le jeton du
   site donne 5,17:1, et l'écart avec un mois ouvert (13,97:1) reste très
   net — c'est l'opposition texte fort / texte atténué employée partout. */
.frise__item--ferme  .frise__nom { color: var(--text-muted); }

/* --- Aujourd'hui ---------------------------------------------------------- */

/* La position n'est pas une approximation en pourcentage : la gouttière
   fausserait le calcul d'un ou deux pixels à chaque mois. Le script pose le
   rang du mois (`--i`) et la fraction parcourue dans ce mois (`--f`) ; la
   feuille refait l'addition exacte, et elle reste juste à toutes les
   largeurs, sans une seule mesure en JavaScript. */
.frise__jour {
  position: absolute;
  top: 1px;
  left: calc(var(--i, 0) * (var(--frise-pas) + var(--frise-jeu)) + var(--f, 0) * var(--frise-pas));
  width: 9px;
  height: 9px;
  margin: -4.5px 0 0 -4.5px;
  border-radius: 999px;
  background-color: var(--c-gold);
  /* Un anneau de fond de page : sans lui, le point d'or se perdrait dans le
     trait de charbon sur lequel il est posé. */
  box-shadow: 0 0 0 2px var(--bg-page);
  opacity: 0;
  transition: opacity var(--dur-long) var(--ease-doux) 520ms;
}
.frise.is-in .frise__jour { opacity: 1; }

/* Le halo. Il ne part qu'une fois la frise atteinte — `is-in` sert de
   détente — et il ne coûte qu'une transformation et une opacité sur un
   élément de neuf pixels. */
.frise__jour::after {
  content: "";
  position: absolute;
  inset: -3px;
  border-radius: 999px;
  border: 1px solid var(--c-gold);
}
.frise.is-in .frise__jour::after { animation: frise-halo 3200ms ease-out infinite; }
.frise.is-in .frise__jour { animation: frise-pouls 3200ms ease-in-out infinite; }

@keyframes frise-halo {
  0%   { transform: scale(0.6); opacity: 0; }
  18%  { opacity: 0.5; }
  100% { transform: scale(2.6); opacity: 0; }
}
@keyframes frise-pouls {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.14); }
}

/* --- Écrans étroits ------------------------------------------------------- */

/* Treize colonnes ne tiennent pas dans 335 px. Plutôt que de rogner les
   libellés ou d'en supprimer, la frise défile — et elle seule, la page ne
   déborde pas.

   La largeur minimale se déduit du contenu le plus large, mesuré, et non
   d'une estimation : les 620 px d'origine avaient été calés sur les seules
   abréviations de mois (38 px pour « JANV »). Depuis que la frise porte aussi
   les MONTANTS, c'est « 1 000 € » qui commande — 52 px — et les prix se
   trouvaient coupés dans une colonne de 45.

       13 colonnes × 52 px  +  12 gouttières × 3 px  =  712 px

   Un minimum trop juste ne se voit pas au premier regard : les chiffres sont
   simplement tronqués, sans débordement ni erreur. */
@media (max-width: 719.98px) {
  .frise {
    overflow-x: auto;
    overscroll-behavior-x: contain;
    padding-bottom: var(--sp-2);
  }
  .frise__in { min-width: 712px; }
  .frise__nom { letter-spacing: 0.06em; }
}

/* --- Les trois notes ------------------------------------------------------ */

body[data-page="tarifs"] .season { margin-top: var(--sp-7); }
body[data-page="tarifs"] .season__item {
  border-top: 0;
  padding-top: var(--sp-4);
  position: relative;
}
/* Le filet doré des trois notes se dessine lui aussi, dans le même sens. */
body[data-page="tarifs"] .season__item::before {
  content: "";
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 1px;
  background-color: var(--rule-gold);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform var(--dur-long) var(--ease-lux);
}
body[data-page="tarifs"] .season.is-in .season__item::before { transform: scaleX(1); }
body[data-page="tarifs"] .season.is-in .season__item:nth-child(2)::before { transition-delay: 90ms; }
body[data-page="tarifs"] .season.is-in .season__item:nth-child(3)::before { transition-delay: 180ms; }

body[data-page="tarifs"] .saison-bloc { margin-top: clamp(28px, 3vw, 48px); }
body[data-page="tarifs"] .booking__renvoi { margin-top: var(--gap-action); }


/* ==========================================================================
   8. La demi-maison
   Elle n'était qu'un encadré de texte, huit cents pixels sous le calendrier,
   qui demandait au visiteur de recopier lui-même sa demande dans le message.
   Elle devient une offre : posée là où la décision se prend, juste sous le
   calendrier, avec les dates déjà choisies.

   Le dessin cite la carte d'estimation — même filet, même rehaut de deux
   pixels sur le bord haut — mais en doré : c'est l'AUTRE formule, pas une
   seconde estimation.
   ========================================================================== */

.demi {
  margin-top: clamp(28px, 3vw, 44px);
  padding: clamp(22px, 2.2vw, 32px);
  background-color: var(--bg-raised);
  border: 1px solid var(--rule);
  border-top: 2px solid var(--c-gold);
  border-radius: var(--radius);
}
.demi__sur {
  font-family: var(--font-text);
  font-size: var(--fs-eyebrow);
  font-weight: var(--fw-medium);
  line-height: var(--lh-ui);
  letter-spacing: var(--ls-eyebrow);
  text-transform: uppercase;
  color: var(--text-muted);
}
.demi__titre {
  margin-top: var(--sp-3);
  font-family: var(--font-display);
  font-weight: var(--fw-regular);
  font-size: clamp(1.1875rem, 1.1rem + 0.35vw, 1.4375rem);
  line-height: var(--lh-title);
  color: var(--text-strong);
}
.demi__texte {
  margin-top: var(--sp-4);
  font-size: var(--fs-small);
  line-height: var(--lh-secondary);
  color: var(--text-body);
  max-width: 46ch;
}

/* La ligne d'état. Tant qu'aucune plage n'est choisie, elle dit quoi faire ;
   dès qu'une plage existe, elle la relit — c'est le même contrat que le
   message sous le calendrier, et c'est ce qui fait qu'on clique en sachant
   ce qu'on envoie. */
.demi__dates {
  margin-top: var(--sp-4);
  padding-left: var(--sp-4);
  border-left: 1px solid var(--rule-gold);
  font-size: var(--fs-fine);
  line-height: var(--lh-secondary);
  color: var(--text-muted);
  transition: color var(--dur-moyen) var(--ease-doux),
              border-color var(--dur-moyen) var(--ease-doux);
}
.demi__dates.is-set {
  color: var(--text-strong);
  border-left-color: var(--c-gold);
}
.demi .actions { margin-top: var(--sp-5); }


/* --- La respiration vers la galerie --------------------------------------- */

/* Elle n'existe QUE sur deux colonnes, et c'est délibéré : elle comble un vide
   de mise en page — deux cent soixante-dix pixels sous la demi-maison, face au
   tableau des montants — et ce vide n'existe pas en une seule colonne. L'y
   laisser aurait glissé une photographie entre la demi-maison et l'estimation,
   au beau milieu du parcours de réservation. La galerie reste à un clic dans
   la navigation et dans le pied de page.

   Effet de bord voulu : sous 1 025 px, l'image n'est même pas demandée. */
.vue { display: none; }

@media (min-width: 1025px) {
  .vue {
    display: grid;
    /* Le cadre prend les trois cinquièmes : c'est la photographie qui parle,
       le texte se réduit à un titre et à un renvoi. */
    grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr);
    gap: clamp(20px, 2vw, 32px);
    align-items: center;
    margin-top: clamp(28px, 3vw, 44px);
    color: inherit;
    text-decoration: none;
  }
}

.vue__mot {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--sp-3);
}
.vue__ligne {
  font-family: var(--font-display);
  font-weight: var(--fw-regular);
  font-size: clamp(1.25rem, 1.05rem + 0.7vw, 1.75rem);
  line-height: var(--lh-title);
  color: var(--text-strong);
  max-width: 10ch;
}

/* Le même filet que `.link-line`, mais porté par un `span` : le bloc entier
   est déjà un lien, on n'en imbrique pas un second. */
.vue__cta {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  margin-top: var(--sp-2);
  padding-bottom: var(--sp-2);
  border-bottom: 1px solid var(--rule);
  font-size: var(--fs-small);
  font-weight: var(--fw-regular);
  letter-spacing: var(--ls-button);
  line-height: var(--lh-ui);
  color: var(--text-strong);
  transition: border-color var(--dur-moyen) var(--ease-doux);
}
.vue__cta .arrow { transition: transform var(--dur-moyen) var(--ease-lux); }
.vue:hover .vue__cta,
.vue:focus-visible .vue__cta { border-bottom-color: var(--c-charcoal); }
.vue:hover .vue__cta .arrow,
.vue:focus-visible .vue__cta .arrow { transform: translateX(4px); }


/* ==========================================================================
   8 bis. Sept nuits au minimum
   `calendar.js` traitait ce cas en silence : choisir un départ trop proche
   redémarrait la sélection sur la nouvelle date, et le visiteur voyait son
   arrivée bouger sans comprendre pourquoi.

   `<dialog>` natif, ouvert par `showModal()` : piège de focus, touche Échap,
   arrière-plan inerte et couche supérieure viennent du navigateur. Aucune de
   ces quatre mécaniques n'est réécrite ici — et la dernière compte, puisque
   l'en-tête du site est `position: fixed` avec un `z-index` de 100.
   ========================================================================== */

.modale {
  width: min(30rem, calc(100vw - 2 * var(--gutter)));
  max-height: calc(100svh - 2 * var(--sp-7));
  padding: 0;
  border: 0;
  border-top: 2px solid var(--c-charcoal);
  border-radius: var(--radius);
  background-color: var(--bg-page);
  color: var(--text-body);
  box-shadow: var(--shadow-lg);
}
.modale::backdrop { background-color: rgba(32, 35, 31, 0.42); }
.modale__in { padding: clamp(26px, 3.4vw, 40px); }

.modale__sur {
  font-family: var(--font-text);
  font-size: var(--fs-eyebrow);
  font-weight: var(--fw-medium);
  line-height: var(--lh-ui);
  letter-spacing: var(--ls-eyebrow);
  text-transform: uppercase;
  color: var(--text-muted);
}
.modale__titre {
  margin-top: var(--sp-3);
  font-family: var(--font-display);
  font-weight: var(--fw-regular);
  font-size: clamp(1.375rem, 1.2rem + 0.7vw, 1.75rem);
  line-height: var(--lh-title);
  color: var(--text-strong);
}
.modale__texte {
  margin-top: var(--sp-4);
  font-size: var(--fs-small);
  line-height: var(--lh-secondary);
  color: var(--text-body);
}
.modale__fine {
  margin-top: var(--sp-4);
  font-size: var(--fs-fine);
  line-height: var(--lh-secondary);
  color: var(--text-muted);
}
.modale__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3);
  margin-top: var(--sp-6);
}
.modale__actions .btn { flex: 1 1 12rem; }

/* L'ouverture. La fermeture, elle, reste instantanée : l'animer demande
   `transition-behavior: allow-discrete`, encore inégalement suivi, et une
   modale qui se ferme net n'a jamais gêné personne. */
.modale[open] { animation: modale-in var(--dur-long) var(--ease-lux); }
.modale[open]::backdrop { animation: modale-fond var(--dur-moyen) var(--ease-doux); }
@keyframes modale-in {
  from { opacity: 0; transform: translateY(12px); }
}
@keyframes modale-fond {
  from { opacity: 0; }
}


/* ==========================================================================
   9. La respiration
   Une seule image sur toute la page, et elle est placée APRÈS le détail des
   montants : entre le calendrier et le tableau, elle aurait coupé net la
   continuité que l'on cherche justement à créer entre une estimation et son
   explication. Ici, elle fait le pivot — on a compris le prix, on regarde à
   nouveau la maison, on écrit.
   ========================================================================== */

.respire { margin: 0; }
.respire__cadre {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius);
  background-color: var(--bg-sunken);
  aspect-ratio: 4 / 3;
  clip-path: inset(0 0 100% 0);
  transition: clip-path 1400ms var(--ease-lux);
}
.respire.is-in .respire__cadre { clip-path: inset(0 0 0 0); }

/* Deux éléments, deux mouvements, et c'est volontaire : `--shift` change à
   chaque image et ne doit surtout pas être amorti, tandis que la mise à
   l'échelle d'entrée dure une seconde et demie. Les mettre sur la même
   transformation aurait fait traîner la parallaxe derrière le défilement. */
.respire__mouvant {
  position: absolute;
  inset: -5% 0;
  transform: translate(0, var(--shift, 0px));
}
.respire__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 55%;
  transform: scale(1.06);
  transition: transform 1800ms var(--ease-lux);
}
.respire.is-in .respire__img { transform: scale(1); }

.respire__mot {
  margin-top: var(--sp-5);
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--sp-2) var(--sp-5);
}
.respire__sur {
  font-family: var(--font-text);
  font-size: var(--fs-eyebrow);
  font-weight: var(--fw-medium);
  line-height: var(--lh-ui);
  letter-spacing: var(--ls-eyebrow);
  text-transform: uppercase;
  color: var(--text-muted);
}
.respire__ligne {
  font-family: var(--font-display);
  font-size: var(--fs-heading-card);
  font-weight: var(--fw-regular);
  line-height: var(--lh-title);
  color: var(--text-strong);
}

/* 768 px, la borne « tablette » du projet : au-dela, la bande est assez large
   pour tenir un cadrage panoramique sans devenir une meurtriere. */
@media (min-width: 768px) {
  .respire__cadre { aspect-ratio: 21 / 9; }
  .respire__mot { justify-content: space-between; }
}


/* ==========================================================================
   10. La clôture
   ========================================================================== */

/* La bande sombre ouvre par le haut, d'un centimètre à peine. Le fond de page
   apparaît une demi-seconde au-dessus d'elle pendant qu'elle se déplie : ce
   n'est pas un rideau, c'est une épaisseur. */
/* `.reveal` n'est la que pour recuperer `is-in` de l'observateur de
   `main.js` — on reutilise son mecanisme plutot que d'en ouvrir un second.
   L'opacite et le deplacement du socle sont donc neutralises : une section
   sombre de pleine largeur qui s'allume ferait un clignotement, la ou un
   depliage de dix-huit pixels ne se remarque pas.

   Le `clip-path` peut ici etre porte par l'element OBSERVE lui-meme, ce qui
   serait une faute avec `inset(0 0 100%)` : celui-la reduit a zero l'aire
   visible, et l'IntersectionObserver la mesure. Dix-huit pixels retires en
   haut d'une bande de plusieurs centaines laissent la boite intacte. */
body[data-page="tarifs"] .request-band.reveal { opacity: 1; transform: none; }
body[data-page="tarifs"] .request-band {
  clip-path: inset(18px 0 0 0);
  transition: clip-path 900ms var(--ease-lux);
}
body[data-page="tarifs"] .request-band.is-in { clip-path: inset(0 0 0 0); }

body[data-page="tarifs"] .request-band {
  padding-block: clamp(2.75rem, 1.6rem + 4.6vw, 5.5rem);
}
body[data-page="tarifs"] .request-band .duo { align-items: center; }
body[data-page="tarifs"] .request-band h2 { max-width: 16ch; }
/* `:not(.actions)` n'est pas une precaution : le bloc d'action est LUI AUSSI
   un `p`. Sans l'exclusion, la mesure de 42 signes s'appliquait au bouton et
   le cadrait dans 415 px au lieu de la colonne entiere.
   Et la borne est 1025, pas 900 : `.duo` ne passe a deux colonnes qu'au-dessus
   de 1024 px (responsive.css). En dessous, aligner a droite aurait pousse le
   bouton contre le bord oppose au texte, sur toute la largeur. */
body[data-page="tarifs"] .request-band p:not(.actions) { margin-top: var(--sp-4); max-width: 42ch; }
@media (min-width: 1025px) {
  /* `base.css` pose `main p { max-width: var(--measure) }` — une mesure de
     lecture de 544 px, juste pour de la prose, hors sujet pour une rangee de
     boutons. Sans cette levee, « flex-end » alignait le bouton sur le bord
     droit d'une boite de 544 px et il s'arretait a 160 px de la marge. La
     question a gauche, la reponse a droite : les deux touchent enfin les deux
     bords de la page. */
  body[data-page="tarifs"] .request-band .actions { max-width: none; justify-content: flex-end; }
}


/* ==========================================================================
   11. Écrans étroits
   Rien n'est coupé : on retire ce qui suppose une souris, on raccourcit le
   reste. Le calendrier et le tableau restent entièrement lisibles.
   ========================================================================== */

@media (max-width: 1024px) {
  /* C'est a ce palier que `responsive.css` remet la carte dans le flux et
     replie la grille en une colonne. On s'assure qu'aucune transformation
     residuelle de l'inclinaison ne subsiste. */
  body[data-page="tarifs"] .estimate { transform: none; }
}

@media (max-width: 620px) {
  .respire__mot { gap: var(--sp-1) var(--sp-4); }
}

@media (max-width: 560px) {
  /* Les cibles descendent a 44 px — le plancher tactile — mais le champ
     garde sa largeur : la commande ne doit pas se remettre a s'etaler. */
  .pas { grid-template-columns: 44px 5rem 44px; }
}

/* Sans survol, un appui doit répondre — et surtout, rien ne doit disparaître.
   La précision des remises ne peut plus se découvrir au survol : elle passe
   donc en seconde ligne, visible en permanence, sur les deux seules rangées
   concernées. Premier réglage : `display: none`, qui la retirait AUSSI de
   l'arbre d'accessibilité — sur téléphone, l'information n'existait plus du
   tout, pour personne. */
@media (hover: none) {
  body[data-page="tarifs"] .day--request:hover { transform: none; box-shadow: none; }
  /* On défait l'empilement : les deux textes redeviennent deux lignes, l'une
     sous l'autre. */
  body[data-page="tarifs"] .rate-desc { display: block; }
  body[data-page="tarifs"] .rate-why {
    opacity: 1;
    display: block;
    margin-top: var(--sp-1);
    color: var(--text-muted);
  }
}


/* ==========================================================================
   12. Mouvement réduit
   Tout ce qui précède est décoratif : rien ne disparaît, tout devient
   instantané. Les états — plage retenue, mois affiché, total, saison — sont
   posés directement dans leur forme finale.
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  body[data-page="tarifs"] .page-head h1 > span { transform: none; transition: none; }
  body[data-page="tarifs"] .page-head .shell[data-parallax] { transform: none; }

  body[data-page="tarifs"] .day,
  body[data-page="tarifs"] .day--request:hover,
  body[data-page="tarifs"] .day--request:active,
  body[data-page="tarifs"] .calendar__navbtn,
  body[data-page="tarifs"] .calendar__navbtn svg,
  body[data-page="tarifs"] .calendar__months,
  .cal-ghost { transition: none; transform: none; }
  body[data-page="tarifs"] .day--start::after,
  body[data-page="tarifs"] .day--end::after { animation: none; }
  body[data-page="tarifs"] .estimate__row { animation: none; }
  .pas__num.is-bump { animation: none; }

  body[data-page="tarifs"] .estimate { transform: none; transition: none; }
  body[data-page="tarifs"] .field-row--dates::before { transform: scaleX(1); transition: none; }
  body[data-page="tarifs"] .field-row--dates::after  { opacity: 0; transition: none; }
  body[data-page="tarifs"] .field-row--dates:not(.is-liee)::before { transform: scaleX(0); }

  .btn--fleche .btn__mot,
  .btn__fleche,
  [data-magnetic] { transition: none; transform: none; }

  body[data-page="tarifs"] .rate-lib,
  body[data-page="tarifs"] .rate-mt { transition: none; transform: none; }

  .frise__trait { transform: scaleX(1) scaleY(var(--epaisseur, 0.28)); transition: none; }
  body[data-page="tarifs"] .season__item::before { transform: scaleX(1); transition: none; }

  .respire__cadre { clip-path: none; transition: none; }
  .respire__mouvant { transform: none; }
  .respire__img { transform: none; transition: none; }

  body[data-page="tarifs"] .request-band { clip-path: none; transition: none; }

  /* Le point d'aujourd'hui reste — c'est un repère, pas une décoration.
     Seuls le halo et la respiration s'arrêtent. */
  .frise.is-in .frise__jour,
  .frise.is-in .frise__jour::after { animation: none; }
  .frise__jour { opacity: 1; transition: none; }

  .demi__dates { transition: none; }
  .vue__cta,
  .vue__cta .arrow { transition: none; transform: none; }
  .modale[open],
  .modale[open]::backdrop { animation: none; }
}
