/* ==========================================================================
   components.css — boutons, messages, formulaires, cartes, récapitulatif.
   Tout ce qui apparaît sur plus d'une page.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Boutons
   -------------------------------------------------------------------------- */
.bouton {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.375rem;
  min-height: 3.125rem;
  padding: 0 1.5625rem;
  font-size: 1rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  text-align: center;
  color: var(--blanc);
  background-color: var(--accent);
  border-radius: var(--r-pill);
  cursor: pointer;
  transition: background-color 0.2s ease-out, transform 0.1s ease-out;
}
.bouton:hover { background-color: var(--accent-fonce); }
.bouton:active { transform: translateY(1px); }

/* Bouton centré sous un formulaire (Rechercher). En inline-flex, la marge
   automatique ne centrait rien : le bouton restait collé à gauche alors que
   la maquette le centrait (display:block; margin: 25px auto 0). */
.bouton--principal {
  display: flex;
  width: 100%;
  max-width: 12.5rem;
  margin: 1.5625rem auto 0;
}

.bouton:disabled,
.bouton[aria-disabled="true"] {
  background-color: var(--line);
  color: var(--blanc);
  cursor: not-allowed;
}
.bouton:disabled:hover { background-color: var(--line); }

/* Lien souligné épais, motif récurrent de la maquette. */
.lien-souligne {
  display: inline-block;
  padding-bottom: 0.75rem;
  font-size: 0.875rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--ink-4);
  border-bottom: 3px solid var(--ink-4);
  transition: color 0.2s ease-out, border-color 0.2s ease-out;
}
.lien-souligne:hover { color: var(--accent); border-color: var(--accent); }

/* Variante « action » : le « Voir plus » des avis était dessiné dans la
   couleur d'action de la maquette ($g), avec un filet de 2 px. */
.lien-souligne--accent {
  color: var(--accent);
  border-bottom: 2px solid var(--accent);
}
.lien-souligne--accent:hover { color: var(--accent-fonce); border-color: var(--accent-fonce); }

/* --------------------------------------------------------------------------
   Messages
   -------------------------------------------------------------------------- */
.message {
  display: flex;
  align-items: flex-start;
  gap: 0.625rem;
  padding: 0.875rem 1.125rem;
  margin-bottom: 1.25rem;
  font-size: 0.9375rem;
  border-radius: var(--r-sm);
  border: 1px solid;
}
.message .ico { margin-top: 0.2em; font-size: 1.125rem; }
/* Le message reçoit le focus après une redirection (js/main.js) : pas
   d'anneau, c'est la lecture du message qui compte. */
.message:focus { outline: none; }

.message--ok {
  color: var(--succes-texte);
  background-color: var(--succes-bg);
  border-color: var(--succes);
}
.message--erreur {
  color: var(--erreur);
  background-color: var(--erreur-bg);
  border-color: var(--erreur);
}
.message--info {
  color: var(--ink-1);
  background-color: var(--alerte-bg);
  border-color: var(--or);
}

.liste-erreurs { margin: 0.375rem 0 0 1.125rem; }
.liste-erreurs li { list-style: disc; }

/* --------------------------------------------------------------------------
   Champs de formulaire
   -------------------------------------------------------------------------- */
.champ { margin-bottom: 1rem; }

.champ__label {
  display: block;
  margin-bottom: 0.375rem;
  font-size: 0.8125rem;
  font-weight: 700;
}

.champ__aide {
  display: block;
  margin-top: 0.25rem;
  font-size: 0.8125rem;
  color: var(--ink-3);
}

.champ__erreur {
  display: block;
  margin-top: 0.25rem;
  font-size: 0.8125rem;
  font-weight: 700;
  color: var(--erreur);
}

input[type="text"],
input[type="password"],
input[type="tel"],
input[type="email"],
input[type="number"],
input[type="search"],
input[type="date"],
textarea,
select {
  display: block;
  width: 100%;
  min-height: 2.5rem;
  padding: 0.5rem 0.625rem;
  color: var(--ink-1);
  background-color: transparent;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  appearance: none;
  transition: border-color 0.2s ease-out, background-color 0.2s ease-out;
}
/* Date : l'icône de calendrier native reste (pas d'appearance: none). Hauteur
   imposée, égale à celle des autres champs (une ligne + marges + bordures) :
   le champ date natif de Chrome ajoute 2 px, et dans un même formulaire il
   dépassait ses voisins (44 px contre 42). Le min-height des écrans tactiles
   (plus bas) l'emporte toujours. */
input[type="date"] { appearance: auto; height: calc(1lh + 1rem + 2px); }

input:hover,
textarea:hover,
select:hover { border-color: var(--accent); }

input:focus,
textarea:focus,
select:focus { background-color: var(--bg-carte); border-color: var(--accent); }

input:disabled,
select:disabled,
textarea:disabled {
  color: var(--ink-3);
  background-color: var(--bg-desactive);
  border-color: var(--line);
  cursor: not-allowed;
}

textarea { min-height: 6.25rem; resize: vertical; }

/* Champ refusé par le serveur : bordure rouge, posée par aria-invalid. */
input[aria-invalid="true"],
select[aria-invalid="true"],
textarea[aria-invalid="true"] { border-color: var(--erreur); }

/* Chevron du select : l'élément natif ne se met pas en forme, on superpose
   une icône et on la laisse transparente aux clics. */
.select-wrapper { position: relative; }
.select-wrapper .ico {
  position: absolute;
  top: 50%;
  right: 0.5625rem;
  transform: translateY(-50%);
  pointer-events: none;
  color: var(--accent);
}
.select-wrapper select { padding-right: 1.75rem; }

/* Date éclatée en jour / mois / année : c'est la présentation validée de la
   maquette. Seuls les name= ont changé — ils étaient tous identiques et les
   valeurs s'écrasaient.
   En flex plutôt qu'en grille à colonnes fixes (4rem 1fr 1fr) : à PC 175 %,
   le moteur ne mesure qu'un tiers de la page et « Septembre » s'affichait
   « Septem ». Le mois garde assez de place pour son nom le plus long, et
   l'année passe à la ligne plutôt que d'être tronquée quand la colonne est
   vraiment étroite. min-width: 0 est indispensable : sans lui, un input
   garde sa largeur minimale native d'environ 195 px. */
.champ--date { display: flex; flex-wrap: wrap; gap: 0.5rem 0.375rem; }
.champ--date .champ__label,
.champ--date .champ__erreur { flex: 0 0 100%; }
.champ--date > input { flex: 0 0 3rem; min-width: 0; }
.champ--date > .select-wrapper { flex: 1 1 7rem; min-width: 0; }
.champ--date > .select-wrapper:last-of-type { flex: 1 1 4.5rem; }
.champ--date select { padding-left: 0.5rem; }

.champ--duo { display: grid; gap: 0.75rem 4%; }

@media (min-width: 30rem) {
  .champ--duo { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* --------------------------------------------------------------------------
   Formulaire de recherche
   -------------------------------------------------------------------------- */
.recherche { position: relative; }

.recherche__mention {
  margin-top: 0.625rem;
  font-size: 0.8125rem;
  text-align: center;
  color: var(--ink-2);
}

/* Variante « carte » : posée sur fond blanc, au cœur de l'accueil. */
.recherche--carte { color: var(--ink-1); }

/* Variante « sombre » : colonne latérale des pages intérieures. */
.recherche--sombre { color: var(--blanc); }
.recherche--sombre .champ__label { color: var(--blanc-90); }
.recherche--sombre .recherche__mention { color: var(--ink-inverse); }
.recherche--sombre .requis { color: var(--erreur-clair); }
.recherche--sombre input,
.recherche--sombre select {
  color: var(--blanc);
  border-color: var(--line-claire);
  border-radius: 0;
}
.recherche--sombre input::placeholder { color: var(--blanc-50); }
.recherche--sombre input:hover,
.recherche--sombre select:hover { border-color: var(--blanc); }
.recherche--sombre input:focus,
.recherche--sombre select:focus { color: var(--ink-1); background-color: var(--blanc); }
.recherche--sombre option { color: var(--ink-1); }
.recherche--sombre .message--erreur { color: var(--erreur); }
/* Sur la colonne sombre, le rouge du thème tombait à 2,8:1. */
.recherche--sombre .champ__erreur { color: var(--erreur-clair); }
.recherche--sombre input[aria-invalid="true"],
.recherche--sombre select[aria-invalid="true"] { border-color: var(--erreur-clair); }

/* Colonne de recherche sombre des pages intérieures (résultats et fiche).
   Vivait dans resultat.css : la fiche établissement l'appelait sans l'avoir,
   et affichait donc des champs blancs sur fond crème. */
.moteur-aside {
  padding: 1.5625rem;
  margin-bottom: 1.25rem;
  background-color: var(--bg-nuit);
  border-radius: var(--r-sm);
}
.moteur-aside__titre { margin-bottom: 1rem; font-size: 1.25rem; color: var(--blanc); }

/* --------------------------------------------------------------------------
   Carte établissement
   -------------------------------------------------------------------------- */
.carte {
  display: block;
  padding: 1.25rem;
  margin-bottom: 1.25rem;
  background-color: var(--bg-carte);
  border-radius: var(--r-sm);
  box-shadow: var(--ombre-1);
}

/* Les atouts ont leur propre zone, qui s'étend sous le prix : dans la
   maquette, seule la ligne du nom réservait de la place au prix, et les
   atouts tenaient sur deux lignes au lieu de trois à PC 175 %. */
.etab {
  display: grid;
  gap: 0.75rem;
  grid-template-areas:
    "visuel"
    "corps"
    "atouts"
    "prix"
    "pied";
}

.etab__visuel {
  grid-area: visuel;
  width: 100%;          /* réserve la place avant chargement (pas de saut) */
  aspect-ratio: 16 / 9;
  object-fit: cover;
  background-color: var(--bg-gris);
  border-radius: var(--r-sm);
}

.etab__corps  { grid-area: corps; }
.etab__atouts { grid-area: atouts; }
.etab__prix   { grid-area: prix; }
.etab__pied   { grid-area: pied; }

/* Nom et étoiles sur la même ligne, comme dans la maquette (inline-block). */
.etab__nom {
  display: inline;
  margin-right: 0.5rem;
  font-size: 1.125rem;
  font-weight: 700;
  color: var(--accent);
}
.etab__corps .stars { vertical-align: -0.05em; }

.etab__adresse { margin-top: 0.25rem; }

.stars { display: inline-flex; gap: 1px; color: var(--or); }

.etab__atouts {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem 0.9375rem;
  font-size: 0.9375rem;
  font-weight: 400;
}
.etab__atouts li { display: flex; align-items: center; gap: 0.375rem; }

.prix-actuel {
  display: block;
  font-size: 1.3125rem;
  font-weight: 700;
  line-height: 1;
  color: var(--accent);
}
.prix-barre {
  display: block;
  font-size: 0.9375rem;
  text-decoration: line-through;
  color: var(--ink-3);
}
.etab__unite { display: block; font-size: 0.8125rem; color: var(--ink-3); }

.etab__pied {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  align-items: center;
  justify-content: flex-end;
}

.etab__lien {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-weight: 700;
  color: var(--ink-4);
  transition: color 0.2s ease-out;
}
.etab__lien .ico { color: var(--accent); }
.etab__lien:hover { color: var(--accent); }

.etab__indispo {
  display: flex;
  align-items: flex-start;
  gap: 0.625rem;
  width: 100%;
  padding: 0.625rem;
  font-size: 0.875rem;
  line-height: 1.35;
  text-align: left;
  background-color: var(--alerte-bg);
  border: 1px solid var(--or);
  border-radius: 3px;
}
.etab__indispo .ico { flex: none; margin-top: 0.15em; font-size: 1.25rem; color: var(--or); }

@media (min-width: 40rem) {
  .carte { padding: 1.875rem; }
  .etab {
    grid-template-columns: 9.375rem minmax(0, 1fr) auto;
    grid-template-areas:
      "visuel corps  prix"
      "visuel atouts atouts"
      "visuel pied   pied";
    grid-template-rows: auto auto 1fr;
    gap: 0.5rem 1.5625rem;
  }
  .etab__visuel { aspect-ratio: 10 / 9; align-self: start; }
  .etab__prix { text-align: right; }
}

/* --------------------------------------------------------------------------
   Étapes du tunnel de réservation
   -------------------------------------------------------------------------- */
.etapes {
  width: 100%;
  max-width: var(--largeur-cadre);
  margin: 1.25rem auto;
  padding-inline: var(--gouttiere);
}
.etapes ol {
  display: grid;
  gap: 2px;
  overflow: hidden;
  border-radius: var(--r-sm);
}
.etapes li {
  padding: 0.875rem 1rem;
  font-size: 0.9375rem;
  font-weight: 700;
  text-align: center;
  color: var(--ink-3);
  background-color: var(--bg-carte);
}
/* Étape courante en couleur d'action et soulignée, comme dans la maquette. */
.etapes li[aria-current="step"] {
  color: var(--accent);
  text-decoration: underline;
  text-underline-offset: 0.2em;
}
.etapes li[data-etat="fait"] { color: var(--ink-2); }

@media (min-width: 40rem) {
  /* Chevrons entre les étapes. La maquette les fabriquait avec un carré
     pivoté à 45° et une ombre ; un clip-path dessine la même flèche. Le
     petit écart entre deux flèches laisse voir le fond crème : c'est lui
     qui trace le filet en pointe, sans pseudo-élément. */
  .etapes ol { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0.25rem; overflow: visible; border-radius: 0; }
  .etapes li {
    padding-inline: 2.25rem 1.75rem;
    font-size: 1.0625rem;
    letter-spacing: -0.015em;
    clip-path: polygon(0 0, calc(100% - 1.25rem) 0, 100% 50%, calc(100% - 1.25rem) 100%, 0 100%, 1.25rem 50%);
  }
  .etapes li:first-child {
    padding-left: 1.75rem;
    clip-path: polygon(0 0, calc(100% - 1.25rem) 0, 100% 50%, calc(100% - 1.25rem) 100%, 0 100%);
    border-radius: var(--r-sm) 0 0 var(--r-sm);
  }
  .etapes li:last-child {
    clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%, 1.25rem 50%);
    border-radius: 0 var(--r-sm) var(--r-sm) 0;
  }
}

/* --------------------------------------------------------------------------
   Récapitulatif de facturation
   -------------------------------------------------------------------------- */
.recap__titre {
  margin-bottom: 0.3125rem;
  font-size: 1.125rem;
  color: var(--accent);
}

.recap__inclus { margin-bottom: 0.625rem; color: var(--accent); }
.recap__inclus li { margin-left: 1rem; list-style: circle; }

.facture {
  padding-block: 0.625rem;
  border-top: 1px dotted var(--line);
}

.facture__ligne {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  line-height: 1.5;
}
.facture__ligne dt { font-weight: 700; }

/* Le total de la maquette recopiait la ligne TVA (17,18 € au lieu de 189 €).
   Il est désormais calculé par lv_devis(). */
.facture__ligne--total {
  padding-top: 0.5rem;
  margin-top: 0.5rem;
  font-size: 1.125rem;
  border-top: 1px solid var(--line);
}
.facture__ligne--total dt,
.facture__ligne--total dd { font-weight: 700; color: var(--ink-1); }

.facture__action { padding-block: 0.3125rem; text-align: right; }
.facture__action a { color: var(--accent); text-decoration: underline; }

.facture__encart {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
  padding: 1.25rem;
  margin-block: 0.625rem;
  background-color: var(--bg-gris);
  border-radius: var(--r-sm);
}
.facture__encart-libelle { font-size: 1.5rem; font-weight: 700; line-height: 1.2; color: var(--ink-1); }
.facture__encart-hint { display: block; font-size: 0.875rem; font-weight: 400; color: var(--accent); }
.facture__encart-prix { text-align: right; }
/* Sur le gris de l'encart, --ink-3 tombait sous 4,5:1. */
.facture__encart .prix-barre { color: var(--ink-2); }

.facture__mention { font-size: 0.875rem; color: var(--ink-3); }

/* Paiement réglé, sous le total du récapitulatif figé. */
.facture__paiement {
  display: flex;
  align-items: flex-start;
  gap: 0.5rem;
  margin-top: 0.75rem;
  font-size: 0.875rem;
  color: var(--succes-texte);
}
.facture__paiement .ico { flex: none; margin-top: 0.2em; color: var(--succes); }

/* Récapitulatif : détail du calcul sous chaque libellé, remise en vert,
   taxe de séjour (réglée sur place, hors total) en gris. Déplacé de
   tunnel.css : le compte affiche le même récapitulatif figé. */
.facture__detail { display: block; font-size: 0.8125rem; font-weight: 400; color: var(--ink-3); }
.facture__ligne + .facture__ligne { margin-top: 0.375rem; }
.facture__ligne dd { white-space: nowrap; }
.facture__ligne--remise dt,
.facture__ligne--remise dd { color: var(--succes-texte); }
.facture__ligne--taxe dt,
.facture__ligne--taxe dd { font-weight: 400; color: var(--ink-3); }
.facture__ligne--taxe dt { font-size: 0.9375rem; }

/* --------------------------------------------------------------------------
   Encarts de la colonne latérale
   -------------------------------------------------------------------------- */
.encart-aside {
  padding: 1.25rem;
  margin-bottom: 1.25rem;
  background-color: var(--bg-carte);
  border-radius: var(--r-sm);
}

.encart-connexion { text-align: center; }
.encart-connexion > .ico { display: block; margin: 0 auto 0.625rem; font-size: 3rem; color: var(--accent); }
.encart-connexion .bouton { margin-top: 1.25rem; }

/* « À savoir » était posé directement sur le fond crème dans la maquette. */
.encart-savoir { padding-inline: 0; background-color: transparent; }
.encart-savoir h2 {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  margin-bottom: 0.5rem;
  font-size: 1.3125rem;
}
.encart-savoir h2 .ico { color: var(--accent); }

@media (min-width: 40rem) {
  .encart-aside { padding: 1.875rem; }
  .encart-savoir { padding-inline: 0; }
  /* Marges de l'encart Montant reprises de la maquette (30/30/40). */
  .facture__encart { padding: 1.875rem 1.875rem 2.5rem; }
}

/* --------------------------------------------------------------------------
   Avis voyageurs
   -------------------------------------------------------------------------- */
.avis__titre { margin-bottom: 0.625rem; font-size: 1.3125rem; }

.avis__item {
  display: grid;
  grid-template-columns: 2.5rem minmax(0, 1fr);
  gap: 1.25rem;
  align-items: start;
  padding: 0.9375rem;
  margin-bottom: 1.25rem;
  background-color: var(--bg-carte);
  border-radius: var(--r-sm);
}

.avis__initiale {
  display: grid;
  place-items: center;
  width: 2.5rem;
  height: 2.5rem;
  font-weight: 700;
  background-color: var(--bg-gris);
  border-radius: 50%;
}

.avis__plus { margin-bottom: 1.25rem; text-align: center; }

/* --------------------------------------------------------------------------
   Carte de localisation
   -------------------------------------------------------------------------- */
.carte-lieu {
  position: relative;
  margin-bottom: 1.25rem;
  overflow: hidden;
  border-radius: var(--r-sm);
}
/* Enfants directs seulement : la mini-carte .carte-osm qui remplace l'image
   contient ses propres liens (liste des établissements, bulles), qui ne
   doivent pas prendre l'aspect du bouton posé sur la carte. Le bouton
   « Afficher la carte » de la fiche (<button>, chargement à la demande)
   reprend le dessin du lien. */
.carte-lieu > img { width: 100%; height: 18.75rem; object-fit: cover; }
.carte-lieu > a,
.carte-lieu > button {
  position: absolute;
  bottom: 1.875rem;
  left: 50%;
  transform: translateX(-50%);
  padding: 0.75rem 0.9375rem;
  font-weight: 700;
  white-space: nowrap;
  color: var(--blanc);
  background-color: var(--accent);
  border-radius: var(--r-sm);
  cursor: pointer;
  transition: background-color 0.2s ease-out;
}
.carte-lieu > a:hover,
.carte-lieu > button:hover { background-color: var(--accent-fonce); }

/* ==========================================================================
   Formulaires en blocs et dates du séjour
   --------------------------------------------------------------------------
   Déplacés à l'identique depuis tunnel.css (01/10/2026) : le compte, le
   contact et le voyage sur mesure reprennent les mêmes blocs que le tunnel.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Dates du séjour
   -------------------------------------------------------------------------- */
.sejour {
  position: relative;
  padding: 1.25rem;
  margin-bottom: 1.25rem;
  background-color: var(--bg-carte);
  border-radius: var(--r-sm);
}

/* La maquette affichait trois entrées dont deux « Départ » identiques : la
   troisième était un copier-coller. Il n'en reste que deux, plus la durée. */
.sejour__dates {
  display: grid;
  gap: 1rem;
  margin-bottom: 1rem;
}
/* Un nom de famille long ne doit pas faire déborder la carte. */
.sejour__dates li { min-width: 0; overflow-wrap: anywhere; }
.sejour__libelle { display: block; font-weight: 700; color: var(--ink-1); }

.sejour__modifier {
  display: inline-block;
  padding: 0.5rem 0.75rem;
  font-size: 0.9375rem;
  font-weight: 700;
  color: var(--blanc);
  background-color: var(--accent);
  border-radius: var(--r-sm);
  transition: background-color 0.2s ease-out;
}
.sejour__modifier:hover { background-color: var(--accent-fonce); }

@media (min-width: 40rem) {
  .sejour { padding: 1.875rem; }
  .sejour__dates { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  /* Le lien « Changez les dates » était placé dans le <ul> hors de tout <li> :
     HTML invalide. Il vit maintenant à côté de la liste. */
}

/* Bouton à droite des dates, comme dans la maquette, seulement quand la
   place le permet : à PC 175 %, il réduisait chaque date à 116 px et les
   dates en toutes lettres tenaient sur quatre lignes. En dessous, il passe
   sous les dates. */
@media (min-width: 80rem) {
  .sejour__modifier { position: absolute; top: 50%; right: 1.875rem; transform: translateY(-50%); }
  .sejour--avec-action .sejour__dates { padding-right: 10rem; margin-bottom: 0; }
}

/* --------------------------------------------------------------------------
   Blocs de formulaire
   -------------------------------------------------------------------------- */
.bloc-form {
  padding: 1.25rem;
  margin-bottom: 1.25rem;
  background-color: var(--bg-carte);
  border-radius: var(--r-sm);
}
.bloc-form__titre { margin-bottom: 0.625rem; font-size: 1.5rem; }
.bloc-form p { font-size: 0.875rem; }
/* Libellés en 16 px, comme dans la maquette : en 13 px, ils paraissaient
   grêles à côté des champs. */
.bloc-form .champ__label,
.bloc-form legend.champ__label { font-size: 1rem; }

/* Grille de champs : la maquette posait des largeurs en pourcentage sur des
   fieldsets inline-block (15 %, 40 %, 48,75 %…). Une grille nommée fait la
   même chose et s'empile toute seule sur petit écran. */
.grille-champs { display: grid; gap: 0 1rem; }

/* Lignes à champ court + champs longs (Civilité / Prénom / Nom, Personnes) :
   la maquette donnait 15 % à la civilité. Sur 2 colonnes de 12, le select
   ne laissait que 32 px au texte à PC 175 % et « Mme » s'affichait « Mm ».
   La colonne courte ne descend jamais sous 6rem. */
.ligne-champs { display: grid; gap: 0 1rem; }

@media (min-width: 40rem) {
  .bloc-form { padding: 1.875rem; }
  .grille-champs { grid-template-columns: repeat(12, minmax(0, 1fr)); }
  .col-6  { grid-column: span 6; }
  .col-12 { grid-column: 1 / -1; }
  .ligne-champs { grid-column: 1 / -1; grid-template-columns: minmax(6rem, 15%) repeat(2, minmax(0, 1fr)); }
  .ligne-champs--seul { grid-template-columns: minmax(6rem, 15%); }
}

.actions-form { margin-top: 1.25rem; text-align: center; }

@media (pointer: coarse) {
  .sejour__modifier { padding-block: 0.75rem; }
}

/* « Dates à préciser » occupe deux colonnes au lieu d'être comprimé dans un
   tiers de la largeur. */
@media (min-width: 40rem) {
  .sejour__large { grid-column: span 2; }
}

/* ==========================================================================
   Composants partagés des nouvelles pages
   --------------------------------------------------------------------------
   Compte, contenus, recherche, fiche et tunnel s'appuient sur ces blocs au
   lieu d'écrire chacun le leur. Ils reprennent le vocabulaire de la
   maquette : cartes blanches à coins de 5 px sur le fond crème, bleu
   d'accent, libellés en capitales espacées, filets gris clair, ombre
   légère. Exemples de balisage : voir le commentaire de chaque bloc.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Bandeau de démonstration
   --------------------------------------------------------------------------
   <p class="bandeau-demo" role="note"> posé par header.php quand
   LV_MODE_DEMO vaut true, avec la classe avec-bandeau-demo sur <html>.
   Fixé au-dessus de la barre de navigation, il a une hauteur connue (deux
   lignes sur téléphone, une au-delà) : la barre, le décalage des pages
   intérieures et les ancres s'en déduisent par --bandeau-demo-h. */
html.avec-bandeau-demo { --bandeau-demo-h: 2.5rem; }

.bandeau-demo {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 51;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  height: var(--bandeau-demo-h);
  padding-inline: var(--gouttiere);
  overflow: hidden;
  font-size: 0.75rem;
  line-height: 1.25;
  text-align: center;
  color: var(--ink-inverse);
  background-color: var(--bg-nuit);
}
.bandeau-demo b { color: var(--jaune); }
.bandeau-demo .ico { font-size: 1rem; color: var(--jaune); }

@media (min-width: 40rem) {
  html.avec-bandeau-demo { --bandeau-demo-h: 1.75rem; }
}

/* Sur papier, le bandeau reste (un bon d'échange imprimé doit dire qu'il
   vient d'une démonstration), mais dans le flux, en encre noire. */
@media print {
  .bandeau-demo {
    position: static;
    height: auto;
    padding-block: 0.25rem;
    color: var(--ink-1);
    background-color: transparent;
    border-bottom: 1px solid var(--line);
  }
  .bandeau-demo b,
  .bandeau-demo .ico { color: inherit; }
}

/* --------------------------------------------------------------------------
   Variantes de boutons
   -------------------------------------------------------------------------- */
/* Action secondaire à côté d'un bouton plein (« Imprimer », « Annuler la
   réservation »). Le contour est une ombre intérieure : même hauteur et
   même largeur que le bouton plein. */
.bouton--secondaire {
  color: var(--accent);
  background-color: transparent;
  box-shadow: inset 0 0 0 2px var(--accent);
}
.bouton--secondaire:hover { color: var(--accent-fonce); background-color: var(--accent-clair); }
.bouton--secondaire:disabled,
.bouton--secondaire[aria-disabled="true"] { box-shadow: none; }

/* Action irréversible (suppression du compte). */
.bouton--danger { background-color: var(--erreur); }
.bouton--danger:hover { background-color: var(--erreur-fonce); }

/* Bouton compact : tableaux, cartes, barres d'actions. */
.bouton--petit {
  min-height: 2.5rem;
  padding-inline: 1.125rem;
  font-size: 0.8125rem;
}

/* Pleine largeur : colonne latérale, téléphone. */
.bouton--bloc { display: flex; width: 100%; }

/* Bouton d'aspect lien. Une commande qui modifie quelque chose (retirer un
   favori, se déconnecter) est un formulaire POST avec jeton CSRF, jamais un
   lien GET, mais elle peut se lire comme un lien dans un texte. */
.bouton-lien {
  display: inline;
  padding: 0;
  font-weight: 700;
  color: var(--accent);
  text-decoration: underline;
  text-underline-offset: 0.15em;
  cursor: pointer;
  transition: color 0.2s ease-out;
}
.bouton-lien:hover { color: var(--accent-fonce); }

/* --------------------------------------------------------------------------
   Badges de statut
   --------------------------------------------------------------------------
   <span class="badge badge--ok">Confirmée</span> ; la variante vient de
   lv_statut_badge() : ok (confirmée), attente (en attente de paiement,
   demande), erreur (paiement refusé), neutre (annulée, expirée). Mêmes
   couleurs que les messages, pour qu'un statut se lise d'un coup d'œil. */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  padding: 0.125rem 0.625rem;
  font-size: 0.75rem;
  font-weight: 700;
  line-height: 1.5;
  letter-spacing: 0.02em;
  white-space: nowrap;
  vertical-align: middle;
  border: 1px solid;
  border-radius: var(--r-pill);
}
.badge--ok {
  color: var(--succes-texte);
  background-color: var(--succes-bg);
  border-color: var(--succes);
}
.badge--attente {
  color: var(--ink-1);
  background-color: var(--alerte-bg);
  border-color: var(--or);
}
.badge--erreur {
  color: var(--erreur);
  background-color: var(--erreur-bg);
  border-color: var(--erreur);
}
.badge--neutre {
  color: var(--ink-2);
  background-color: var(--bg-gris);
  border-color: var(--line);
}

/* --------------------------------------------------------------------------
   Lien de retour
   --------------------------------------------------------------------------
   <a class="lien-retour" href="compte.php"><?= icone('angle-left') ?>Mes
   réservations</a>, au-dessus du titre d'une page de détail. */
.lien-retour {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  margin-bottom: 1rem;
  font-size: 0.875rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--ink-3);
  transition: color 0.2s ease-out;
}
.lien-retour .ico { font-size: 1.125rem; color: var(--accent); }
.lien-retour:hover { color: var(--accent); }

/* --------------------------------------------------------------------------
   Barre d'actions
   --------------------------------------------------------------------------
   Rangée de boutons ou de liens (Payer, Annuler, Ajouter au calendrier,
   Imprimer) qui passe à la ligne au besoin. Un <form> enfant (action en
   POST) s'efface : son bouton se range avec les autres. Absente du papier. */
.barre-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem 1rem;
  margin-block: 1.25rem;
}
.barre-actions > form { display: contents; }
.barre-actions--centre { justify-content: center; }
.barre-actions--fin { justify-content: flex-end; }

@media print {
  .barre-actions { display: none; }
}

/* --------------------------------------------------------------------------
   Onglets
   --------------------------------------------------------------------------
   <nav class="onglets" aria-label="…"> avec des liens .onglets__onglet
   (aria-current="page" sur l'onglet affiché), ou un role="tablist" avec des
   <button role="tab" aria-selected>. Même trait épais que .lien-souligne.
   Sur téléphone, la rangée défile au lieu de passer à la ligne. */
.onglets {
  display: flex;
  gap: 0 1.5rem;
  margin-bottom: 1.25rem;
  overflow-x: auto;
  scrollbar-width: thin;
  border-bottom: 1px solid var(--line);
}
.onglets__onglet {
  display: inline-flex;
  flex: none;
  align-items: center;
  gap: 0.5rem;
  padding: 0.75rem 0 0.5rem;
  font-size: 0.875rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  white-space: nowrap;
  color: var(--ink-3);
  border-bottom: 3px solid transparent;
  cursor: pointer;
  transition: color 0.2s ease-out, border-color 0.2s ease-out;
}
.onglets__onglet:hover { color: var(--accent); }
.onglets__onglet[aria-current="page"],
.onglets__onglet[aria-selected="true"] { color: var(--accent); border-bottom-color: var(--accent); }
/* Dans un conteneur qui défile, un anneau extérieur serait rogné. */
.onglets__onglet:focus-visible { outline-offset: -2px; }

.onglets__nombre {
  display: inline-grid;
  place-items: center;
  min-width: 1.5rem;
  height: 1.5rem;
  padding-inline: 0.375rem;
  font-size: 0.75rem;
  letter-spacing: 0;
  color: var(--ink-2);
  background-color: var(--bg-gris);
  border-radius: var(--r-pill);
}
.onglets__onglet[aria-current="page"] .onglets__nombre,
.onglets__onglet[aria-selected="true"] .onglets__nombre { color: var(--blanc); background-color: var(--accent); }

/* --------------------------------------------------------------------------
   Tableau
   --------------------------------------------------------------------------
   <div class="tableau-cadre" role="region" aria-label="…" tabindex="0">
     <table class="tableau">…</table>
   </div>
   Le cadre défile horizontalement sur téléphone au lieu d'élargir la page ;
   tabindex et aria-label le rendent défilable au clavier. Les montants
   portent .tableau__nombre (alignés à droite, chiffres de même largeur).
   Variante .tableau--empile : sous 40rem, chaque ligne devient une petite
   fiche dont chaque cellule rappelle son intitulé (attribut data-label). */
.tableau-cadre {
  margin-bottom: 1.25rem;
  overflow-x: auto;
  background-color: var(--bg-carte);
  border-radius: var(--r-sm);
  box-shadow: var(--ombre-1);
}

.tableau { width: 100%; font-size: 0.9375rem; }

.tableau caption {
  padding: 1rem 1.25rem 0.5rem;
  font-weight: 700;
  text-align: left;
  color: var(--ink-1);
}

.tableau th,
.tableau td {
  padding: 0.75rem 1.25rem;
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid var(--line-douce);
}
.tableau thead th {
  padding-block: 0.625rem;
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  white-space: nowrap;
  color: var(--ink-3);
  border-bottom-color: var(--line);
}
.tableau tbody th { font-weight: 700; color: var(--ink-1); }
.tableau tbody tr:last-child > * { border-bottom: 0; }
.tableau .tableau__nombre {
  text-align: right;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

.tableau--empile thead {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
.tableau--empile tr {
  display: block;
  padding-block: 0.5rem;
  border-bottom: 1px solid var(--line-douce);
}
.tableau--empile tbody tr:last-child { border-bottom: 0; }
.tableau--empile td,
.tableau--empile tbody th {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.25rem 1.25rem;
  text-align: right;
  border-bottom: 0;
}
.tableau--empile td::before,
.tableau--empile tbody th::before {
  content: attr(data-label);
  flex: none;
  font-weight: 700;
  text-align: left;
  color: var(--ink-1);
}

@media (min-width: 40rem) {
  .tableau--empile thead {
    position: static;
    width: auto;
    height: auto;
    overflow: visible;
    clip-path: none;
  }
  .tableau--empile tr { display: table-row; padding-block: 0; border-bottom: 0; }
  .tableau--empile td,
  .tableau--empile tbody th {
    display: table-cell;
    padding: 0.75rem 1.25rem;
    text-align: left;
    border-bottom: 1px solid var(--line-douce);
  }
  .tableau--empile td::before,
  .tableau--empile tbody th::before { content: none; }
  .tableau--empile .tableau__nombre { text-align: right; }
}

/* --------------------------------------------------------------------------
   Pagination
   --------------------------------------------------------------------------
   <nav class="pagination" aria-label="Pages de résultats"><ul>
     <li><a class="pagination__precedent" href="…" rel="prev">angle-left
         <span class="pagination__texte">Précédent</span></a></li>
     <li><a href="…">1</a></li>
     <li><span aria-current="page">2</span></li>
     <li><span class="pagination__ellipse" aria-hidden="true">…</span></li>
     <li><a class="pagination__suivant" href="…" rel="next">…</a></li>
   </ul></nav>
   Première ou dernière page : la flèche devient un <span aria-disabled>. */
.pagination { margin-block: 1.5rem 2rem; }

.pagination ul {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 0.375rem;
}

.pagination li > * {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.375rem;
  min-width: 2.5rem;
  height: 2.5rem;
  padding-inline: 0.75rem;
  font-weight: 700;
  color: var(--ink-2);
  background-color: var(--bg-carte);
  border-radius: var(--r-sm);
  transition: color 0.2s ease-out, background-color 0.2s ease-out;
}
.pagination a:hover { color: var(--accent); background-color: var(--accent-clair); }
.pagination [aria-current="page"] { color: var(--blanc); background-color: var(--accent); }
.pagination a[aria-current="page"]:hover { color: var(--blanc); background-color: var(--accent-fonce); }
.pagination [aria-disabled="true"] { color: var(--ink-3); background-color: transparent; }
.pagination .pagination__ellipse { min-width: 1.5rem; padding-inline: 0; background-color: transparent; }

/* Sur téléphone, « Précédent » et « Suivant » se réduisent à leur flèche,
   le libellé restant annoncé aux lecteurs d'écran. */
.pagination__texte {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
@media (min-width: 40rem) {
  .pagination__texte {
    position: static;
    width: auto;
    height: auto;
    overflow: visible;
    clip-path: none;
  }
}

/* --------------------------------------------------------------------------
   État vide
   --------------------------------------------------------------------------
   <div class="vide"><?= icone('suitcase', '', 'vide__icone') ?>
     <h2 class="vide__titre">Aucun séjour à venir</h2>
     <p class="vide__texte">…</p>
     <div class="barre-actions">…</div></div>
   Une liste vide dit pourquoi et propose la suite, au lieu d'un blanc. */
.vide {
  display: grid;
  justify-items: center;
  gap: 0.75rem;
  padding: 2.5rem 1.25rem;
  margin-bottom: 1.25rem;
  text-align: center;
  background-color: var(--bg-carte);
  border-radius: var(--r-sm);
}
.vide__icone { font-size: 3rem; color: var(--accent); }
.vide__titre { font-size: 1.3125rem; }
.vide__texte { max-width: 32rem; }
.vide .barre-actions { justify-content: center; margin-block: 0.5rem 0; }

/* --------------------------------------------------------------------------
   Favori (cœur)
   --------------------------------------------------------------------------
   <form action="traitements/favori.php" method="post" data-envoi-js>
     csrf + etablissement + depuis
     <button type="submit" class="favori" aria-pressed="false">
       <?= icone('heart-o', '', 'favori__vide') ?><?= icone('heart', '', 'favori__plein') ?>
       <span class="favori__texte">Favori&nbsp;: Nom de l'établissement</span>
     </button></form>
   aria-pressed porte l'état : le cœur plein n'apparaît que s'il vaut
   "true". Libellé fixe, état porté par aria-pressed seul (un libellé
   « Retirer… » doublé de « enfoncé » se contredisait).
   aria-busy="true" pendant l'envoi par js/favoris.js. */
.favori {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  min-height: 2.5rem;
  padding: 0.375rem 1rem 0.375rem 0.75rem;
  font-size: 0.875rem;
  font-weight: 700;
  color: var(--ink-1);
  background-color: var(--bg-carte);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  cursor: pointer;
  transition: border-color 0.2s ease-out, box-shadow 0.2s ease-out;
}
.favori:hover { border-color: var(--favori); }
.favori .ico { font-size: 1.25rem; color: var(--favori); }
.favori .favori__plein { display: none; }
.favori[aria-pressed="true"] .favori__plein { display: inline-block; }
.favori[aria-pressed="true"] .favori__vide { display: none; }
.favori[aria-busy="true"] { cursor: progress; opacity: 0.7; }

/* Variante ronde, posée sur une photo (galerie de la fiche, cartes) : le
   texte n'est plus lu que par les lecteurs d'écran. */
.favori--rond {
  justify-content: center;
  width: 2.75rem;
  height: 2.75rem;
  min-height: 0;
  padding: 0;
  border: 0;
  border-radius: 50%;
  box-shadow: var(--ombre-2);
}
.favori--rond:hover { box-shadow: var(--ombre-3); }
.favori--rond .favori__texte {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* --------------------------------------------------------------------------
   Grille de cartes
   --------------------------------------------------------------------------
   Favoris, actualités, suggestions : des cartes verticales qui se rangent
   seules en 1 à 4 colonnes.
   <ul class="grille-cartes"><li class="grille-cartes__carte">
     <img class="grille-cartes__visuel" …>
     <div class="grille-cartes__corps">
       <h3 class="grille-cartes__titre"><a href="…">Nom</a></h3>
       <p class="grille-cartes__meta">Paris 8e · ★★★★</p></div>
     <div class="grille-cartes__pied">prix, bouton…</div>
     <form class="grille-cartes__favori">…favori--rond…</form></li></ul>
   Le lien du titre s'étend sur toute la carte ; le pied et le favori
   restent cliquables par-dessus. */
.grille-cartes {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 15rem), 1fr));
  gap: 1.25rem;
  margin-bottom: 1.25rem;
}

.grille-cartes__carte {
  position: relative;
  display: flex;
  flex-direction: column;
  min-width: 0;
  overflow: hidden;
  background-color: var(--bg-carte);
  border-radius: var(--r-sm);
  box-shadow: var(--ombre-1);
  transition: box-shadow 0.2s ease-out;
}
.grille-cartes__carte:hover { box-shadow: var(--ombre-3); }

.grille-cartes__visuel {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  background-color: var(--bg-gris);
}

.grille-cartes__corps {
  display: grid;
  align-content: start;
  gap: 0.375rem;
  padding: 1rem 1.25rem;
}
.grille-cartes__titre { font-size: 1.125rem; color: var(--accent); }
.grille-cartes__titre a::after { content: ""; position: absolute; inset: 0; }
.grille-cartes__meta { font-size: 0.875rem; color: var(--ink-3); }

.grille-cartes__pied {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem 1rem;
  padding: 0 1.25rem 1.25rem;
  margin-top: auto;
}
.grille-cartes__pied > * { position: relative; z-index: 1; }

.grille-cartes__favori {
  position: absolute;
  top: 0.75rem;
  right: 0.75rem;
  z-index: 1;
}

/* --------------------------------------------------------------------------
   Liste de définitions
   --------------------------------------------------------------------------
   <dl class="liste-definitions"><div><dt>Référence</dt><dd>LV-…</dd></div>…</dl>
   (les <div> sont facultatifs ; un seul <dd> par <dt>). Libellé et valeur
   côte à côte dès 30rem, séparés par le pointillé du récapitulatif. */
.liste-definitions {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  margin-bottom: 1.25rem;
  border-top: 1px dotted var(--line);
}
.liste-definitions > div { display: contents; }
.liste-definitions dt { padding-top: 0.625rem; font-weight: 700; color: var(--ink-1); }
.liste-definitions dd {
  min-width: 0;
  padding-bottom: 0.625rem;
  overflow-wrap: anywhere;
  border-bottom: 1px dotted var(--line);
}

/* Pas d'écart de colonne : le pointillé court sans coupure sous le libellé
   et la valeur ; c'est la marge de la valeur qui les sépare. */
@media (min-width: 30rem) {
  .liste-definitions { grid-template-columns: minmax(7rem, 2fr) minmax(0, 3fr); }
  .liste-definitions dt,
  .liste-definitions dd { padding-block: 0.625rem; border-bottom: 1px dotted var(--line); }
  .liste-definitions dd { padding-left: 1rem; }
}

/* --------------------------------------------------------------------------
   Page de texte (mentions légales, CGV, FAQ, article)
   --------------------------------------------------------------------------
   <article class="page-texte"> sur le fond crème : une carte blanche à la
   largeur de lecture (environ 75 caractères par ligne). Les éléments sans
   classe reçoivent la typographie de lecture ; :where() garde une
   spécificité minimale, pour que les composants posés dedans (tableau,
   liste de définitions, boutons) gardent leur propre dessin. */
.page-texte {
  width: calc(100% - 2 * var(--gouttiere));
  max-width: 48rem;
  margin: 1.25rem auto 3rem;
  padding: 1.5rem 1.25rem 2rem;
  background-color: var(--bg-carte);
  border-radius: var(--r-sm);
}
.page-texte :where(h1) { margin-bottom: 1rem; font-size: var(--titre-page); }
.page-texte :where(h2) { margin: 2rem 0 0.75rem; font-size: 1.3125rem; }
.page-texte :where(h3) { margin: 1.5rem 0 0.5rem; font-size: 1.0625rem; }
.page-texte :where(p, ul, ol, dl, table, blockquote) { margin-bottom: 1rem; }
.page-texte :where(ul:not([class]), ol:not([class])) { padding-left: 1.25rem; }
.page-texte :where(ul:not([class])) > li { list-style: disc; }
.page-texte :where(ol:not([class])) > li { list-style: decimal; }
.page-texte :where(ul:not([class]), ol:not([class])) > li + li { margin-top: 0.375rem; }
.page-texte :where(a:not([class])) {
  color: var(--accent);
  text-decoration: underline;
  text-underline-offset: 0.15em;
}
.page-texte :where(a:not([class])):hover { color: var(--accent-fonce); }
.page-texte > :last-child { margin-bottom: 0; }

.page-texte__chapo { font-size: 1.125rem; color: var(--ink-1); }
.page-texte__maj { font-size: 0.875rem; color: var(--ink-3); }

@media (min-width: 40rem) {
  .page-texte { padding: 2.5rem 3rem 3rem; margin-top: 2rem; }
}

/* Information légale ou d'entreprise pas encore fournie : un marqueur
   visible « [À compléter] », jamais une valeur inventée. */
.a-completer {
  padding: 0 0.25rem;
  font-weight: 700;
  color: var(--ink-1);
  background-color: var(--jaune);
  border-radius: 2px;
}

/* --------------------------------------------------------------------------
   Case à cocher avec son texte
   --------------------------------------------------------------------------
   <label class="case"><input type="checkbox" name="…" value="1">
   <span>Texte, éventuellement avec un lien</span></label>
   Consentements (newsletter, politique de confidentialité), même dessin
   que la case des conditions du tunnel. */
.case {
  display: flex;
  align-items: flex-start;
  gap: 0.625rem;
  margin-block: 1rem;
  font-size: 0.875rem;
  cursor: pointer;
}
.case input {
  flex: none;
  width: 1.125rem;
  height: 1.125rem;
  min-height: 0;
  margin-top: 0.15em;
  accent-color: var(--accent);
}
.case a { color: var(--accent); text-decoration: underline; }

/* --------------------------------------------------------------------------
   Liens utiles (page 404, pages d'erreur)
   -------------------------------------------------------------------------- */
.liens-utiles {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1rem;
  margin-block: 1.5rem 0.5rem;
}
@media (min-width: 40rem) {
  .liens-utiles { flex-direction: row; flex-wrap: wrap; justify-content: center; gap: 1rem 2rem; }
}

/* --------------------------------------------------------------------------
   Carte OpenStreetMap (.carte-osm)
   --------------------------------------------------------------------------
   <div class="carte-osm" data-lat data-lng data-zoom data-tuiles
        data-points='[{"id","nom","lat","lng","prix","url"}]'>
     <ul class="carte-osm__liste">
       <li><a href="destination.php?…"><?= icone('map-marker') ?>Nom
           <span class="carte-osm__prix">189 €</span></a></li>
     </ul>
   </div>
   Sans JS, sans tuiles (LV_CARTE_TUILES vide) ou en cas d'erreur : la
   liste seule, défilante dans le cadre. js/carte.js (RECHERCHE) pose
   .carte-osm--active et ajoute .carte-osm__fond (tuiles .carte-osm__tuile),
   les repères .carte-osm__repere (bouton ou lien ; --point pour un seul
   établissement, --actif ou aria-expanded="true" pour le repère ouvert),
   la bulle .carte-osm__bulle, les commandes .carte-osm__commandes (+ / −)
   et l'attribution .carte-osm__attribution. isolation : les z-index de la
   carte restent dans son cadre, sous le bouton « Voir sur la carte ». */
.carte-osm {
  position: relative;
  isolation: isolate;
  height: 18.75rem;
  overflow: hidden;
  background-color: var(--bg-gris);
  border-radius: var(--r-sm);
}
/* Grande carte (page carte.php, carte dépliée de la fiche). */
.carte-osm--grande { height: 28rem; }

.carte-osm__liste {
  height: 100%;
  padding: 0.5rem 1.25rem;
  overflow-y: auto;
  background-color: var(--bg-carte);
}
.carte-osm__liste li + li { border-top: 1px solid var(--line-douce); }
.carte-osm__liste a {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  padding-block: 0.5rem;
  font-weight: 700;
  color: var(--ink-1);
  transition: color 0.2s ease-out;
}
.carte-osm__liste a:hover { color: var(--accent); }
.carte-osm__liste .ico { flex: none; align-self: center; color: var(--accent); }
.carte-osm__prix { margin-left: auto; white-space: nowrap; color: var(--accent); }

.carte-osm--active .carte-osm__liste { display: none; }

.carte-osm__fond {
  position: absolute;
  inset: 0;
  overflow: hidden;
  cursor: grab;
  touch-action: none;
}
.carte-osm__fond:active { cursor: grabbing; }
/* Le fond reçoit le focus (flèches du clavier) : anneau dessiné à
   l'intérieur, sinon l'overflow: hidden du cadre arrondi le rognait. */
.carte-osm__fond:focus-visible { outline-offset: -3px; }
.carte-osm__tuile { position: absolute; max-width: none; user-select: none; }

/* Repère : étiquette de prix blanche à pointe, comme les cartes de
   réservation ; le repère survolé ou ouvert passe en bleu, au premier plan. */
.carte-osm__repere {
  position: absolute;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  padding: 0.25rem 0.5rem;
  font-size: 0.8125rem;
  font-weight: 700;
  line-height: 1.2;
  white-space: nowrap;
  color: var(--accent);
  background-color: var(--bg-carte);
  border-radius: var(--r-pill);
  box-shadow: var(--ombre-2);
  transform: translate(-50%, calc(-100% - 0.375rem));
  cursor: pointer;
  transition: color 0.15s ease-out, background-color 0.15s ease-out;
}
.carte-osm__repere::after {
  content: "";
  position: absolute;
  top: 100%;
  left: 50%;
  width: 0.625rem;
  height: 0.375rem;
  background-color: inherit;
  clip-path: polygon(0 0, 100% 0, 50% 100%);
  transform: translateX(-50%);
}
.carte-osm__repere:hover,
.carte-osm__repere[aria-expanded="true"],
.carte-osm__repere--actif { z-index: 2; color: var(--blanc); background-color: var(--accent); }

/* Établissement de la fiche : un point, sans prix. */
.carte-osm__repere--point {
  width: 1.25rem;
  height: 1.25rem;
  padding: 0;
  background-color: var(--accent);
  border: 3px solid var(--blanc);
  border-radius: 50%;
  transform: translate(-50%, -50%);
}
.carte-osm__repere--point::after { content: none; }

/* Au-dessus des commandes de zoom : une bulle ouverte près du bord droit
   restait sinon en partie cachée sous le bouton « − ». */
.carte-osm__bulle {
  position: absolute;
  z-index: 5;
  width: max-content;
  max-width: min(16rem, 80%);
  padding: 0.75rem 1rem;
  font-size: 0.875rem;
  background-color: var(--bg-carte);
  border-radius: var(--r-sm);
  box-shadow: var(--ombre-3);
  transform: translate(-50%, calc(-100% - 2.5rem));
}
.carte-osm__bulle-nom { display: block; font-weight: 700; color: var(--ink-1); }
.carte-osm__bulle a { font-weight: 700; color: var(--accent); text-decoration: underline; }

.carte-osm__commandes {
  position: absolute;
  top: 0.75rem;
  right: 0.75rem;
  z-index: 4;
  display: grid;
  gap: 1px;
  overflow: hidden;
  background-color: var(--line);
  border-radius: var(--r-sm);
  box-shadow: var(--ombre-2);
}
.carte-osm__commandes button {
  display: grid;
  place-items: center;
  width: 2.25rem;
  height: 2.25rem;
  font-size: 1.25rem;
  font-weight: 700;
  line-height: 1;
  color: var(--ink-1);
  background-color: var(--bg-carte);
  cursor: pointer;
  transition: color 0.2s ease-out, background-color 0.2s ease-out;
}
.carte-osm__commandes button:hover { color: var(--accent); background-color: var(--accent-clair); }
.carte-osm__commandes button:disabled { color: var(--ink-3); cursor: default; }

.carte-osm__attribution {
  position: absolute;
  right: 0;
  bottom: 0;
  z-index: 4;
  padding: 0.125rem 0.375rem;
  font-size: 0.6875rem;
  color: var(--ink-2);
  background-color: var(--voile-clair);
  border-top-left-radius: var(--r-sm);
}
.carte-osm__attribution a { text-decoration: underline; }

@media (min-width: 60rem) {
  .carte-osm--grande { height: 37.5rem; }
}

/* --------------------------------------------------------------------------
   Cibles tactiles
   --------------------------------------------------------------------------
   Au doigt, les commandes principales atteignent 44 × 44 px sans que le
   dessin validé du bureau change : la règle ne vise que les pointeurs
   grossiers. */
@media (pointer: coarse) {
  input[type="text"],
  input[type="password"],
  input[type="tel"],
  input[type="email"],
  input[type="number"],
  input[type="search"],
  input[type="date"],
  select { min-height: 2.75rem; }
  .facture__action a,
  .etab__lien { display: inline-flex; min-height: 2.75rem; align-items: center; }
  .bouton--petit,
  .favori { min-height: 2.75rem; }
  .pagination li > * { min-width: 2.75rem; height: 2.75rem; }
  .onglets__onglet,
  .lien-retour { min-height: 2.75rem; }
  .carte-osm__commandes button { width: 2.75rem; height: 2.75rem; }
}
