/* ─────────────────────────────────────────
   MLinguistique : fiche Ville (template-ville.php)
   Chargée seulement sur les pages singulières du
   type de contenu ml_ville (voir functions.php,
   ml_fiches_destinations_enqueue()). Dépend de
   assets/css/main.css : tous les tokens --ml-*
   viennent de son :root, aucun n'est redéclaré ici.
   Mobile first, repli 900px comme le reste du thème.
───────────────────────────────────────── */

/* ── Fil d'Ariane : dans le flux, en pleine largeur de la grille ──────────
   Rendu par CE gabarit (template-ville.php), à l'intérieur de
   .ml-ville-hero-inner, juste avant la carte de verre d'intro : il suit donc
   l'ordre de lecture naturel du DOM, comme le reste du contenu du hero. Plus
   de superposition en absolu par dessus la photo, donc plus rien à compenser
   pour la barre de menu flottante ou la barre d'administration : c'était le
   point qui coûtait le plus cher à maintenir dans l'ancienne architecture.

   .ml-ville-hero-inner passe en grille à deux colonnes à partir de 900px
   (voir plus bas) : le fil d'Ariane y occupe les deux, en tête, avant que les
   deux cartes ne se répartissent sur la ligne suivante (placement automatique
   de la grille, aucun besoin de préciser une ligne). L'espacement avec les
   cartes vient du « gap » déjà posé sur .ml-ville-hero-inner, pas d'une marge
   ajoutée ici.

   Il reste blanc et ombré, comme le reste du texte du hero, pour se lire sur
   la photo. Les propriétés de mise en page du fil d'Ariane « générique »
   (assets/css/main.css : largeur maximale centrée, padding) ne conviennent
   plus ici, elles sont réinitialisées en conséquence. */
.ml-ville-hero-inner .ml-fil-ariane {
  max-inline-size: none;
  margin: 0;
  padding: 0;
  color: rgba(255, 255, 255, 0.85);
  text-shadow: 0 1px 6px rgba(6, 14, 26, 0.55);
}
@media (min-width: 900px) {
  .ml-ville-hero-inner .ml-fil-ariane {
    grid-column: 1 / -1;
  }
}
.ml-ville-hero-inner .ml-fil-ariane a {
  color: rgba(255, 255, 255, 0.85);
}
.ml-ville-hero-inner .ml-fil-ariane a:hover,
.ml-ville-hero-inner .ml-fil-ariane a:focus-visible {
  color: #fff;
}
.ml-ville-hero-inner .ml-fil-ariane a:focus-visible {
  outline-color: #fff;
}
.ml-ville-hero-inner .ml-fil-ariane li[aria-current="page"] {
  color: #fff;
}

/* ── Verre dépoli partagé : cartes du haut de page et carte de permanence ──
   Même recette, déjà validée ailleurs sur ce thème (voir .ml-hero-badge et
   la barre de menu transparente dans main.css) : une teinte sombre neutre
   posée sur la photo, puis un voile blanc translucide, avec flou
   d'arrière-plan. C'est elle, et elle seule, qui donne au texte blanc ses
   4,5:1, quelle que soit la photo dessous. Le repli sans flou (Firefox
   ancien, vieux téléphones) retombe sur un fond opaque franc : la matière
   du verre disparaît, jamais la lisibilité. */
.ml-ville-glass {
  background:
    linear-gradient(rgba(255, 255, 255, 0.12), rgba(255, 255, 255, 0.12)),
    linear-gradient(rgba(30, 10, 42, 0.5), rgba(30, 10, 42, 0.5));
  -webkit-backdrop-filter: blur(18px) saturate(140%);
          backdrop-filter: blur(18px) saturate(140%);
  border: 1px solid rgba(255, 255, 255, 0.22);
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .ml-ville-glass {
    background: rgba(30, 10, 42, 0.72);
  }
}

/* ─────────────────────────────────────────
   HAUT DE PAGE
───────────────────────────────────────── */
.ml-ville-hero {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  min-block-size: 100svh;
  /* La barre de menu flotte par dessus, réserve faite pour elle en haut. Le
     fil d'Ariane vit dans le flux normal du contenu, plus bas dans le hero
     (voir plus haut) : il n'a pas besoin de sa propre réserve. */
  padding-block: calc(var(--ml-header-h) + var(--ml-space-lg)) var(--ml-space-lg);
  background-color: var(--ml-accent-nuit);
}
body.admin-bar .ml-ville-hero {
  min-block-size: calc(100svh - 32px);
}
@media (max-width: 782px) {
  body.admin-bar .ml-ville-hero {
    min-block-size: calc(100svh - 46px);
  }
}
@media (max-width: 600px) {
  body.admin-bar .ml-ville-hero {
    min-block-size: 100svh;
  }
}
@media (max-height: 760px) {
  .ml-ville-hero {
    padding-block: calc(var(--ml-header-h) + var(--ml-space-sm)) var(--ml-space-md);
  }
}

.ml-ville-hero-media,
.ml-ville-hero-media img {
  position: absolute;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  z-index: -2;
}

/* Zoom avant discret à l'arrivée sur la page (effet Ken Burns), une seule
   fois : l'overflow: hidden de .ml-ville-hero (ci dessus) recadre l'image
   grandissante, sans jamais faire bouger le dégradé de lisibilité posé à
   part sur .ml-ville-hero-media::after, juste en dessous. */
@keyframes ml-ville-hero-zoom {
  from { transform: scale(1); }
  to   { transform: scale(1.06); }
}
.ml-ville-hero-img {
  animation: ml-ville-hero-zoom 10s var(--ml-ease) forwards;
}
@media (prefers-reduced-motion: reduce) {
  .ml-ville-hero-img {
    animation: none;
  }
}

/* Le dégradé de lisibilité : sombre en haut (la barre de menu et le fil
   d'Ariane), sombre en bas (les cartes de verre), presque nu au milieu.
   Les deux cartes portent déjà leur propre lisibilité (.ml-ville-glass) :
   ce dégradé ne fait qu'aider la photo à se fondre, il ne porte pas seul
   le contraste du texte. Couleur dérivée de --ml-accent-nuit (46, 18, 63),
   jamais une teinte inventée à côté des tokens. */
.ml-ville-hero-media::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(
    to bottom,
    rgba(46, 18, 63, 0.55) 0%,
    rgba(46, 18, 63, 0.14) 28%,
    rgba(46, 18, 63, 0.32) 60%,
    rgba(46, 18, 63, 0.78) 100%
  );
}

.ml-ville-hero-inner {
  position: relative;
  z-index: 1;
  display: grid;
  gap: var(--ml-space-md);
  margin-block-start: auto;
}
@media (min-width: 900px) {
  .ml-ville-hero-inner {
    grid-template-columns: 1.4fr 1fr;
    align-items: end;
  }
}

.ml-ville-hero-carte {
  border-radius: var(--ml-radius-lg);
  padding: var(--ml-space-md) var(--ml-space-lg);
}

/* Le haut de page ne porte qu'UN SEUL <h1>, empilé sur deux lignes : le
   surtitre « Séjour linguistique » et le nom de la fiche. Le titre lu par les
   moteurs devient « Séjour linguistique Birmingham », le visiteur voit exactement
   le même rendu qu'avant. L'ancien badge de verre est devenu le span de surtitre,
   aux mêmes réglages ; le nom hérite tel quel de la grande ligne du titre. */
.ml-ville-hero-titre {
  color: #fff;
  text-shadow: 0 2px 20px rgba(6, 14, 26, 0.5);
  margin-block-end: var(--ml-space-sm);
}
/* Le surtitre : l'ancien badge de verre, à l'identique. Bloc à largeur de son
   contenu (la pastille épouse son texte tout en poussant le nom à la ligne
   suivante). line-height de base, pas celle serrée des titres, et ombre coupée :
   sinon le <h1> parent les transmettrait à la pastille, qui n'en a jamais porté. */
.ml-ville-h1-surtitre {
  display: block;
  inline-size: fit-content;
  margin-block-end: var(--ml-space-sm);
  padding: 0.45em 1.1em;
  border-radius: var(--ml-radius-pill);
  color: #fff;
  font-family: var(--ml-font-heading);
  font-size: 0.8125rem;
  font-weight: 700;
  line-height: var(--ml-lh-base);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  text-shadow: none;
}
/* Le nom de la fiche : la grande ligne blanche du titre. Bloc, il tombe sous la
   pastille et hérite de la taille, de la graisse et de l'ombre du <h1>. */
.ml-ville-h1-nom {
  display: block;
}

.ml-ville-hero-texte {
  color: rgba(255, 255, 255, 0.92);
  max-width: 46ch;
  margin: 0;
}

.ml-ville-faits-liste {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--ml-space-sm) var(--ml-space-md);
  margin: 0 0 var(--ml-space-md);
  /* Deux colonnes même sur 320px : une valeur d'un seul long mot (nom
     d'aéroport) se coupe plutôt que de déborder sa colonne (audit mobile). */
  overflow-wrap: anywhere;
}
.ml-ville-fait dt {
  margin: 0 0 0.2em;
  color: rgba(255, 255, 255, 0.8);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
}
.ml-ville-fait dd {
  margin: 0;
  color: #fff;
  font-family: var(--ml-font-heading);
  font-weight: 800;
  font-size: 1rem;
}

.ml-ville-hero-cta.ml-btn {
  inline-size: 100%;
  justify-content: center;
}
.ml-ville-hero-carte a:focus-visible,
.ml-ville-hero-carte button:focus-visible {
  outline-color: #fff;
}

/* ─────────────────────────────────────────
   À PROPOS DE LA VILLE
───────────────────────────────────────── */
.ml-ville-apropos-grid {
  display: grid;
  gap: var(--ml-space-lg);
  align-items: center;
}
@media (min-width: 900px) {
  .ml-ville-apropos-grid {
    grid-template-columns: 1fr 1fr;
  }
}

.ml-ville-apropos-collage {
  position: relative;
  min-block-size: 280px;
}
@media (min-width: 900px) {
  .ml-ville-apropos-collage {
    min-block-size: 400px;
  }
}
.ml-ville-apropos-collage-grande,
.ml-ville-apropos-collage-petite {
  position: absolute;
  border-radius: var(--ml-radius-lg);
  overflow: hidden;
  box-shadow: var(--ml-shadow-lg);
}
.ml-ville-apropos-collage-grande {
  inset-inline-end: 0;
  inset-block-start: 0;
  inline-size: 78%;
  block-size: 70%;
}
.ml-ville-apropos-collage-petite {
  inset-inline-start: 0;
  inset-block-end: 0;
  inline-size: 54%;
  block-size: 54%;
  border: 6px solid var(--ml-bg);
}
.ml-ville-apropos-collage img {
  display: block;
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
}

/* ─────────────────────────────────────────
   VOTRE ÉCOLE SUR PLACE (fond lilas doux)
───────────────────────────────────────── */
.ml-ville-ecole {
  background: var(--ml-accent-soft);
}
.ml-ville-ecole-entete {
  max-width: 68ch;
  margin-block-end: var(--ml-space-lg);
}

/* LE NOM RÉEL DE L'ÉCOLE ET SON ACCROCHE, sous le titre de la section.
   Ils étaient posés par le modèle depuis le relevé de l'ancien site, et le
   gabarit ne les rendait pas : la section montrait trois lignes qui se
   répétaient et rien de ce qui distingue vraiment un établissement d'un autre
   (constat de Marino sur Barcelone).

   Le nom est un titre de niveau 3, plus discret que celui de la section : c'est
   un sous-titre, pas un second départ. L'accroche est la phrase de l'école
   elle même, en italique pour qu'on l'entende comme une citation et non comme
   la description du thème. */
.ml-ville-ecole-nom {
  margin-block: var(--ml-space-sm) 0;
  font-size: var(--ml-fs-lg);
  color: var(--ml-accent-dark);
}

.ml-ville-ecole-accroche {
  margin-block: 0.2em var(--ml-space-sm);
  font-style: italic;
  color: var(--ml-text-mid);
}

.ml-ville-ecole-entete p + p {
  margin-block-start: var(--ml-space-sm);
}

/* La première rangée de la section : l'entête (titre, texte) à gauche, la
   photo de l'école en colonne de DROITE dès 900px (demande de Marino, juillet
   2026 : plus jamais un bandeau pleine largeur). Sur mobile, la photo passe
   sous le texte. Sans photo, la classe modificatrice n'est pas posée et
   l'entête garde toute la largeur. Les cartes d'équipements suivent, en
   pleine largeur sous cette rangée.

   Hauteur de rangée dictée PAR LE TEXTE (demande de Marino) : la photo est
   posée en absolu dans sa colonne, hauteur 100 pour cent de la rangée et
   LARGEUR AUTOMATIQUE (ratio d'origine conservé, image entière, jamais
   recadrée), calée à droite : elle se réduit d'elle-même quand le texte est
   court, sans jamais influencer la hauteur de la rangée. Le space-between
   vient de la grille (texte borné à gauche, photo au bord droit). */
.ml-ville-ecole-haut {
  margin-block-end: var(--ml-space-md);
}
/* ── La photo de l'école, à GAUCHE, sur trente pour cent de la largeur ──
   Demande de Marino, juillet 2026, après avoir vu les photos réelles arriver :
   elles s'affichaient en miniature.

   La cause n'était pas leur taille de fichier mais la mise en page. La photo
   était calée en hauteur sur la rangée, donc sur la colonne de texte d'à côté,
   avec object-fit: contain : dès que la description de l'école était courte,
   la rangée était basse et la photo rétrécissait avec elle. Une photo dont la
   taille dépend de la longueur d'un texte n'a aucune raison d'être bien
   dimensionnée un jour.

   Elle a maintenant sa propre proportion, indépendante du texte : trois
   quarts, portrait, ce qui lui donne de la présence sur une colonne étroite.
   Elle passe à gauche par « order », sans toucher au gabarit : dans le HTML,
   le texte reste avant la photo, ce qui est le bon ordre de lecture pour une
   synthèse vocale. */
@media (min-width: 900px) {
  .ml-ville-ecole-haut--photo {
    display: grid;
    grid-template-columns: minmax(0, 30%) minmax(0, 1fr);
    gap: var(--ml-space-lg);
    align-items: start;
  }
  .ml-ville-ecole-haut--photo .ml-ville-ecole-entete {
    margin-block-end: 0;
  }
  .ml-ville-ecole-haut--photo .ml-ville-ecole-photo {
    order: -1;
  }

  /* Tout le reste de la section, cases à cocher et services étudiants
     compris, tient dans la colonne de droite (demande de Marino) : la photo
     court alors sur toute la hauteur au lieu de laisser un vide sous elle.
     Les équipements passent de cinq colonnes à trois : cinq ne tiendraient pas
     dans soixante-dix pour cent de la largeur, mais deux allongeaient la
     colonne pour rien et repoussaient les services étudiants sous la photo
     (constat de Marino). Trois rangent six équipements en deux rangées au lieu
     de trois, et tout remonte. */
  .ml-ville-ecole-haut--photo .ml-ville-ecole-equipements {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
  .ml-ville-ecole-haut--photo .ml-ville-ecole-photo img {
    inline-size: 100%;
    block-size: auto;
    aspect-ratio: 3 / 4;
    max-block-size: none;
    object-fit: cover;
    border-radius: var(--ml-radius-lg);
    box-shadow: var(--ml-shadow-md);
  }
}
/* ── Mobile : LA PHOTO PASSE EN TETE DE SECTION, ET PLUS HAUTE ──────────────
   Demande de Marino. Elle arrivait sous le texte, en bas d'un long bloc de
   paragraphes : on lisait la description d'une école avant d'en avoir vu la
   moindre image, et la photo, écrasée à deux cents pixels, ressemblait plus à
   une vignette qu'à une présentation.

   Elle remonte par « order », sans toucher au gabarit : dans le HTML, le texte
   reste avant la photo, ce qui est le bon ordre de lecture pour une synthèse
   vocale. C'est exactement le même procédé qu'au dessus de 900px, où elle passe
   à gauche.

   La proportion est posée, quatre tiers, plutôt qu'un simple plafond de
   hauteur : une hauteur qui dépend de la proportion d'origine du fichier donne
   une section haute pour une photo, basse pour la suivante. */
@media (max-width: 899.98px) {
  .ml-ville-ecole-haut--photo {
    display: flex;
    flex-direction: column;
    gap: var(--ml-space-md);
  }
  .ml-ville-ecole-haut--photo .ml-ville-ecole-photo {
    order: -1;
  }
}

.ml-ville-ecole-photo img {
  display: block;
  inline-size: 100%;
  block-size: auto;
  object-fit: cover;
  border-radius: var(--ml-radius-lg);
  box-shadow: var(--ml-shadow-md);
}

@media (max-width: 899.98px) {
  .ml-ville-ecole-photo img {
    aspect-ratio: 4 / 3;
    max-block-size: clamp(260px, 70vw, 380px);
  }
}

/* Les équipements : cartes compactes, hauteur au contenu (fini la rangée fixe
   qui les creusait). Une colonne sur mobile, deux en tablette, cinq au bureau
   (demande de Marino : les cinq équipements types tiennent alors sur une seule
   rangée, plus de carte orpheline). */
.ml-ville-ecole-equipements {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--ml-space-sm);
}
@media (min-width: 600px) {
  .ml-ville-ecole-equipements {
    grid-template-columns: repeat(2, 1fr);
  }
}
@media (min-width: 900px) {
  .ml-ville-ecole-equipements {
    grid-template-columns: repeat(5, 1fr);
  }
}
.ml-ville-ecole-carte {
  display: flex;
  align-items: center;
  gap: var(--ml-space-xs);
  background: var(--ml-bg);
  border: 1px solid var(--ml-accent-bordure);
  border-radius: var(--ml-radius-lg);
  padding: var(--ml-space-sm);
}
.ml-ville-ecole-carte-icone {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: 2.1rem;
  block-size: 2.1rem;
  border-radius: var(--ml-radius-sm);
  background: var(--ml-accent-soft);
  color: var(--ml-accent);
}
.ml-ville-ecole-carte-texte {
  font-family: var(--ml-font-heading);
  font-weight: 700;
  color: var(--ml-primary);
  font-size: var(--ml-fs-sm);
}

.ml-ville-ecole-services {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--ml-space-xs) var(--ml-space-sm);
  /* De l'air avec les cartes d'équipements du dessus (demande de Marino). */
  margin-block-start: var(--ml-space-lg);
}
.ml-ville-ecole-services-label {
  font-family: var(--ml-font-heading);
  font-weight: 700;
  color: var(--ml-primary);
  font-size: var(--ml-fs-sm);
}
.ml-ville-ecole-service {
  background: var(--ml-bg);
  border: 1px solid var(--ml-accent-bordure);
  color: var(--ml-text-mid);
  font-weight: 600;
  font-size: var(--ml-fs-sm);
  padding: 0.5em 0.9em;
  border-radius: var(--ml-radius-pill);
}

/* UNE PUCE QUI PORTE UN LIEN EST CLIQUABLE EN ENTIER (demande de Marino). La
   cliente pose le lien sur le texte dans l'éditeur ; le gabarit en tire
   l'adresse et fait de la pastille elle même le lien, plutôt que de laisser un
   mot cliquable au milieu d'une pastille qui, elle, ne l'est pas.

   Elle doit se voir : la couleur de marque, et le geste au survol comme au
   focus. Pas de soulignement, la forme de pastille dit déjà que c'est un
   élément à part. */
.ml-ville-ecole-service--lien {
  color: var(--ml-accent-dark);
  border-color: var(--ml-accent);
  text-decoration: none;
  transition: background-color 160ms var(--ml-ease), color 160ms var(--ml-ease);
}
.ml-ville-ecole-service--lien:hover {
  background: var(--ml-accent);
  color: #fff;
}
.ml-ville-ecole-service--lien:focus-visible {
  outline: 3px solid var(--ml-accent);
  outline-offset: 2px;
}

/* Même règle pour une carte d'équipement qui porte un lien. */
a.ml-ville-ecole-carte {
  text-decoration: none;
  color: inherit;
  transition: border-color 160ms var(--ml-ease), box-shadow 160ms var(--ml-ease);
}
a.ml-ville-ecole-carte:hover {
  border-color: var(--ml-accent);
  box-shadow: var(--ml-shadow-sm);
}
a.ml-ville-ecole-carte:focus-visible {
  outline: 3px solid var(--ml-accent);
  outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  .ml-ville-ecole-service--lien,
  a.ml-ville-ecole-carte {
    transition: none;
  }
}
.ml-ville-ecole-lien {
  display: inline-flex;
  align-items: center;
  gap: 0.4em;
  margin-inline-start: auto;
  color: var(--ml-accent);
  font-weight: 700;
  font-size: var(--ml-fs-sm);
  transition: color 160ms var(--ml-ease);
}
.ml-ville-ecole-lien:hover,
.ml-ville-ecole-lien:focus-visible {
  color: var(--ml-accent-dark);
}
@media (prefers-reduced-motion: reduce) {
  .ml-ville-ecole-lien {
    transition: none;
  }
}

/* ─────────────────────────────────────────
   COURS PROPOSÉS (fond nuit)
───────────────────────────────────────── */
/* Le violet de marque, celui des autres fonds de section, et non l'aubergine
   nuit : trop foncee ici, elle isolait la section du reste de la page
   (demande de Marino). */
.ml-ville-cours {
  background: var(--ml-accent);
}
.ml-ville-cours-entete {
  max-width: 66ch;
}
.ml-ville-cours-entete h2 {
  color: #fff;
}
.ml-ville-cours-entete p {
  color: rgba(255, 255, 255, 0.82);
}
.ml-ville-cours .ml-eyebrow {
  color: #fff;
}
.ml-ville-cours .ml-eyebrow::before {
  background: var(--ml-accent-soft);
}

.ml-ville-cours-grille {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--ml-space-md);
  margin: var(--ml-space-lg) 0 0;
  padding: 0;
  list-style: none;
}
@media (min-width: 900px) {
  .ml-ville-cours-grille {
    grid-template-columns: repeat(3, 1fr);
  }
}
.ml-ville-cours-carte {
  background: var(--ml-bg);
  border-radius: var(--ml-radius-lg);
  padding: var(--ml-space-md);
}
.ml-ville-cours-badge {
  display: inline-block;
  background: var(--ml-accent-soft);
  color: var(--ml-accent);
  font-weight: 700;
  font-size: 0.75rem;
  padding: 0.35em 0.8em;
  border-radius: var(--ml-radius-pill);
  margin-block-end: var(--ml-space-xs);
}
.ml-ville-cours-carte h3 {
  margin-block-end: var(--ml-space-xs);
}
/* DES CHEVRONS, PAS DES PUCES. Le marqueur natif dependait d une reinitialisation
   de liste posee ailleurs dans le theme : selon la cascade, il disparaissait et
   il ne restait qu un retrait sans repere (constat de Marino). Un chevron pose
   en ::before ne depend de personne, et il dit « voici un detail de plus »
   mieux qu un point.

   Meme technique que la coche de la page Formule : un masque CSS sur un petit
   SVG integre a l URL, teinte par background-color. Aucun fichier de plus, et la
   couleur suit la marque. */
.ml-ville-cours-liste {
  margin: 0;
  padding: 0;
  list-style: none;
  color: var(--ml-text-mid);
  font-size: var(--ml-fs-sm);
}
.ml-ville-cours-liste li {
  position: relative;
  margin-block-end: 0.4em;
  padding-inline-start: 1.35em;
}
.ml-ville-cours-liste li::before {
  content: '';
  position: absolute;
  inset-inline-start: 0;
  /* Cale sur la premiere ligne du texte, pas sur la hauteur du <li> : un detail
     qui passe sur deux lignes garde son chevron en haut, en face du debut. */
  inset-block-start: 0.42em;
  inline-size: 0.62em;
  block-size: 0.62em;
  background-color: var(--ml-accent);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 3l5 5-5 5'/%3E%3C/svg%3E") no-repeat center / contain;
          mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 3l5 5-5 5'/%3E%3C/svg%3E") no-repeat center / contain;
}

/* ─────────────────────────────────────────
   LES FORMULES SPÉCIALES (fond lilas doux)
   Section chrome (aucun groupe éditable), entre les cours (fond nuit) et le
   logement (fond clair) : le lilas doux la sépare des deux. Les cartes sont le
   vocabulaire partagé du listing (.ml-grille-formules, .ml-carte-formule,
   posés dans main.css) : rien à redéfinir ici, seul l'entête et le fond de la
   section, avec la pastille d'âge stylée dans popup-formules.css.
───────────────────────────────────────── */
.ml-ville-formules-section {
  background: var(--ml-accent-soft);
}
/* Sur la fiche ville, une formule pour mineurs ne porte QUE sa puce d'âge à
   gauche (posée par le gabarit, voir template-ville.php). L'ancien badge du
   coin droit a depuis été retiré de la carte partagée elle-même (demande de
   Marino : un seul marquage, en haut à gauche, sur tous les listings). */
.ml-ville-formules-titre {
  max-width: 66ch;
  margin: 0;
}
/* La petite phrase entre le titre et le listing (demande de Marino). */
.ml-ville-formules-intro {
  max-width: 66ch;
  margin: var(--ml-space-xs) 0 0;
  color: var(--ml-text-mid);
}

/* ─────────────────────────────────────────
   OÙ LOGER
───────────────────────────────────────── */
.ml-ville-logements-entete {
  max-width: 66ch;
}

/* TROIS OPTIONS DE LOGEMENT TIENNENT SUR UNE LIGNE (demande de Marino). La
   grille était figée à deux colonnes : trois cartes s'y rangeaient en deux plus
   une, la dernière seule sur sa ligne et deux fois plus large que ses voisines.

   auto-fit plutôt qu'un nombre de colonnes écrit en dur : les pistes vides sont
   supprimées, donc deux cartes donnent deux colonnes pleine largeur, et trois en
   donnent trois. Le minimum de 300px borne l'emballement au delà : quatre cartes
   repassent à trois plus une plutôt que de se serrer à 280px chacune. */
.ml-ville-logements-grille {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--ml-space-md);
  margin-block-start: var(--ml-space-lg);
}
@media (min-width: 900px) {
  .ml-ville-logements-grille {
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  }

  /* UNE SEULE OPTION NE PREND PAS TOUTE LA LARGEUR. Stuttgart et Southport n en
     proposent qu une, la famille d accueil, et auto-fit etirait sa carte d un
     bord a l autre du conteneur : une photo de mille deux cents pixels de large
     pour trois lignes de texte, ca ne ressemble plus a une carte (constat de
     Marino).

     Elle garde donc la largeur qu elle aurait si elle avait une voisine, et
     reste calee a gauche. Le calcul reprend la gouttiere de la grille : deux
     colonnes egales separees d un gap valent chacune la moitie de ce qui reste.

     :only-child plutot qu une requete de quantite en :has() : la regle ne
     depend d aucun support recent, et sans elle la carte reprendrait simplement
     toute la largeur, comme aujourd hui. */
  .ml-ville-logements-carte:only-child {
    max-inline-size: calc((100% - var(--ml-space-md)) / 2);
  }
}
.ml-ville-logements-carte {
  border: 1px solid var(--ml-accent-bordure);
  border-radius: var(--ml-radius-lg);
  overflow: hidden;
  background: var(--ml-bg);
}
/* TOUTES LES CARTES D'UNE LIGNE ONT LA MÊME HAUTEUR, quelle que soit la photo
   et quelle que soit la longueur du texte (demande de Marino).

   Trois pièces, et il faut les trois. La carte devient une colonne flex, sinon
   son contenu ne remplit pas la hauteur que la grille lui donne déjà. La zone
   photo prend un format fixe (4/3) au lieu d'une hauteur minimale : c'était la
   photo qui faisait varier la hauteur d'une carte à l'autre. Et le corps prend
   la place restante, pour que le fond descende jusqu'en bas. */
.ml-ville-logements-carte {
  display: flex;
  flex-direction: column;
}
.ml-ville-logements-carte-photo {
  flex: none;
  aspect-ratio: 4 / 3;
  overflow: hidden;
}
.ml-ville-logements-carte-photo img {
  display: block;
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
}
/* La piste du défilé et ses vues suivent la hauteur de la zone photo : sans
   cela, elles se dimensionneraient sur la photo elle même et le format fixe ci
   dessus ne servirait à rien. */
.ml-ville-logements-carte-photo .ml-defile-piste,
.ml-ville-logements-carte-photo .ml-defile-vue {
  block-size: 100%;
}
.ml-ville-logements-carte-corps {
  flex: 1 1 auto;
  padding: var(--ml-space-md);
}
/* LA CASE D'INFORMATIONS D'UN LOGEMENT : pension, type de chambre, salle de
   bain, distance, age minimum, accessibilite.

   Une icone d'INFORMATION, plus une epingle de carte : l'epingle annoncait une
   distance alors que la case porte de tout, et deux logements n'ont meme aucune
   distance (demande de Marino).

   Les elements restent EN LIGNE, separes par un point median : ils se lisaient a
   la suite, majuscule contre minuscule, sans rien pour dire ou l'un finissait.
   Une liste verticale prendrait trois fois la hauteur dans une carte deja
   dense. */
.ml-ville-logements-infos {
  display: flex;
  align-items: flex-start;
  gap: 0.5em;
  margin: var(--ml-space-sm) 0 0;
  padding: 0.5em 0.85em;
  border-radius: var(--ml-radius-sm);
  background: var(--ml-accent-soft);
  color: var(--ml-accent-dark);
  font-size: var(--ml-fs-sm);
  line-height: 1.5;
}

/* L'icone ne retrecit jamais, et se cale sur la premiere ligne du texte. */
.ml-ville-info-icone {
  flex: 0 0 auto;
  margin-block-start: 0.15em;
  color: var(--ml-accent);
}

.ml-ville-logements-infos-texte {
  min-inline-size: 0;
}

/* Le point median : un peu d'air de chaque cote, et une teinte plus douce que
   le texte pour qu'il separe sans se lire comme un mot. */
.ml-ville-logements-sep {
  margin-inline: 0.45em 0.3em;
  opacity: 0.55;
}

/* Un lien pose dans une info reste lisible sur ce fond lilas. */
.ml-ville-logements-infos a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 0.15em;
}

.ml-ville-logements-note {
  margin-block-start: var(--ml-space-md);
  color: var(--ml-text-dim);
  font-size: var(--ml-fs-sm);
}

/* ─────────────────────────────────────────
   EN IMAGES (fond lilas doux)
───────────────────────────────────────── */
.ml-ville-galerie {
  background: var(--ml-accent-soft);
}
.ml-ville-galerie-entete {
  max-width: 66ch;
  margin-block-end: var(--ml-space-lg);
}

/* Le bloc galerie natif (core/gallery v2, « has-nested-images ») impose sa
   PROPRE mise en page : le figure.wp-block-gallery est un conteneur flex, et
   chaque figure.wp-block-image enfant reçoit une largeur calculée
   (width: calc(...)) qui donnait, dans le conteneur en grille du thème, des
   colonnes étroites en portrait (capture de Marino).

   La difficulté est la spécificité : WordPress pose ces largeurs avec des
   sélecteurs qui embarquent un ou deux « id-hacks » (:not(#individual-image)),
   jusqu'à une spécificité (2,7,2) pour la variante columns-default à plusieurs
   photos (wp-includes/blocks/gallery/style.css). Aucun sélecteur de thème
   raisonnable ne passe devant : la seule voie sûre, quel que soit l'ordre de
   chargement des feuilles, est un !important ciblé sur la largeur des enfants.
   Le reste (grille, aspect régulier, cover, bande de crédit) se pose à
   spécificité normale, relevée juste ce qu'il faut avec .has-nested-images
   pour dépasser les règles core sans id.

   Résultat visé : une colonne en mobile, deux en tablette, trois au bureau, des
   cases régulières en 4:3 (aspect-ratio, donc vraiment proportionnelles à toute
   largeur, là où une hauteur de rangée fixe ne l'était pas), images en cover,
   crédits sur leur bande fine. */
.ml-ville-galerie-grille .wp-block-gallery.has-nested-images {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--ml-space-sm);
  margin: 0;
}

/* AU DELÀ DE QUATRE PHOTOS, DEUX COLONNES DÈS LE TÉLÉPHONE (demande de Marino).
   Une galerie de douze photos sur une seule colonne fait douze écrans à faire
   défiler avant d'atteindre la suite de la page : la galerie n'est pas le sujet
   de la fiche, elle l'illustre. Deux colonnes la ramènent à six rangées.

   Jusqu'à quatre photos, la colonne unique reste : chaque photo y est grande, et
   quatre grandes rangées ne fatiguent personne.

   « :has » compte les enfants RÉELS, sans une ligne de script. Un navigateur qui
   ne le connaît pas garde la colonne unique : le repli est l'ancien
   comportement, jamais un écran cassé. */
@media (max-width: 599.98px) {
  .ml-ville-galerie-grille .wp-block-gallery.has-nested-images:has(> :nth-child(5)) {
    grid-template-columns: repeat(2, 1fr);
    gap: var(--ml-space-xs);
  }
}

@media (min-width: 600px) {
  .ml-ville-galerie-grille .wp-block-gallery.has-nested-images {
    grid-template-columns: repeat(2, 1fr);
  }
}
@media (min-width: 900px) {
  .ml-ville-galerie-grille .wp-block-gallery.has-nested-images {
    grid-template-columns: repeat(3, 1fr);
  }
}
/* La case : chaque figure enfant devient une cellule de grille régulière. Le
   width: auto !important déloge la largeur calculée du bloc (voir plus haut) ;
   display: block neutralise le flex interne, aspect-ratio donne le 4:3. */
.ml-ville-galerie-grille .wp-block-gallery.has-nested-images figure.wp-block-image {
  width: auto !important;
  max-width: none !important;
  display: block;
  margin: 0;
  position: relative;
  aspect-ratio: 4 / 3;
  border-radius: var(--ml-radius-lg);
  overflow: hidden;
  box-shadow: var(--ml-shadow-sm);
}
/* Le bloc pose un voile flou (pseudo :before) sur le bas de chaque image, sous
   la légende : il ferait double emploi avec notre bande de crédit, on le retire
   comme le bloc lui-même le fait pour ses variantes arrondies. */
.ml-ville-galerie-grille .wp-block-gallery.has-nested-images figure.wp-block-image::before {
  content: none;
}
.ml-ville-galerie-grille .wp-block-gallery.has-nested-images figure.wp-block-image img {
  position: absolute;
  inset: 0;
  display: block;
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
}

/* Légende de crédit (auteur, licence, parfois un lien vers le texte de la
   licence, voir ml_galeries_credit_html() dans inc/semis-galeries.php).
   WordPress la rend par défaut en bandeau flou centré qui monte haut sur la
   photo : sur nos petites cases, il recouvrait presque toute l'image. Ses
   règles de légende ont une spécificité (0,3,2) ; on la dépasse ici en
   ajoutant .has-nested-images et figure.wp-block-image au sélecteur, pour que
   notre bande fine gagne à coup sûr. Une seule ligne discrète en bas de la
   case, TOUJOURS visible (donc atteignable sans interaction, au clavier comme
   à la lecture d'écran), tronquée par un point de suspension si elle déborde,
   le lien de licence restant un vrai lien cliquable et focalisable. Fond uni
   (pas de dégradé) : à 92% d'opacité sur --ml-accent-nuit, le contraste tient
   plus de 13:1 même sur une photo blanche, très au dessus du 4,5:1 requis. */
.ml-ville-galerie-grille .wp-block-gallery.has-nested-images figure.wp-block-image figcaption.wp-element-caption {
  position: absolute;
  inset-inline: 0;
  inset-block-end: 0;
  margin: 0;
  padding: 0.4em 0.75em;
  background: rgba(46, 18, 63, 0.92);
  color: #fff;
  font-size: 11px;
  line-height: 1.4;
  text-align: start;
  text-shadow: none;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ml-ville-galerie-grille .wp-block-gallery.has-nested-images figure.wp-block-image figcaption.wp-element-caption a {
  color: #fff;
  text-decoration: underline;
  text-underline-offset: 2px;
}
.ml-ville-galerie-grille .wp-block-gallery.has-nested-images figure.wp-block-image figcaption.wp-element-caption a:focus-visible {
  outline-color: #fff;
}

/* ─────────────────────────────────────────
   LES LIENS DANS LE TEXTE EDITABLE
   Le theme pose « a { color: inherit; text-decoration: none } » sur tout le
   site, et c'est volontaire : la barre de menu, les cartes, les boutons et les
   pastilles sont des liens qui ne doivent surtout pas se souligner. Mais dans
   une PHRASE, la consequence est qu'un lien devient invisible : meme couleur
   que le texte, aucun soulignement, rien qui dise qu'on peut cliquer (constat
   de Marino, qui venait d'en poser un dans le texte d'une ecole).

   La regle ci dessous ne vaut donc QUE dans les zones de prose editable de la
   fiche ville, jamais sur les liens de chrome. C'est ici qu'on ajoute un
   conteneur le jour ou une nouvelle section porte du texte libre.

   :where() garde une specificite nulle : une regle de section (le lien du site
   de l'ecole, une puce de service) l'emporte sans avoir a surencherir.

   Le soulignement est la des le depart, il ne se decouvre pas au survol : un
   lien qui ne se signale qu'une fois qu'on est dessus ne sert a rien pour qui
   ne le survole jamais. Au survol le trait s'epaissit et un voile lilas se
   pose derriere : le mot reste a sa place, aucune ligne ne se decale.
───────────────────────────────────────── */
:where(
  .ml-ville-apropos-texte,
  .ml-ville-ecole-entete,
  .ml-ville-logements-carte-corps,
  .ml-ville-cours-entete
) p a {
  color: var(--ml-accent-dark);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.18em;
  border-radius: 3px;
  transition: background-color 160ms var(--ml-ease), text-decoration-thickness 160ms var(--ml-ease);
}

:where(
  .ml-ville-apropos-texte,
  .ml-ville-ecole-entete,
  .ml-ville-logements-carte-corps
) p a:hover {
  background-color: var(--ml-accent-soft);
  text-decoration-thickness: 2px;
}

:where(
  .ml-ville-apropos-texte,
  .ml-ville-ecole-entete,
  .ml-ville-logements-carte-corps,
  .ml-ville-cours-entete
) p a:focus-visible {
  outline: 3px solid var(--ml-accent);
  outline-offset: 2px;
  background-color: var(--ml-accent-soft);
}

/* La section des cours est sur fond violet : le violet du texte ne s'y lit pas.
   Le lien y reste blanc, souligne, et son voile est un blanc translucide. */
:where( .ml-ville-cours-entete ) p a {
  color: #fff;
  text-decoration-color: rgba(255, 255, 255, 0.6);
}
:where( .ml-ville-cours-entete ) p a:hover {
  background-color: rgba(255, 255, 255, 0.16);
  text-decoration-thickness: 2px;
  text-decoration-color: #fff;
}

@media (prefers-reduced-motion: reduce) {
  :where(
    .ml-ville-apropos-texte,
    .ml-ville-ecole-entete,
    .ml-ville-logements-carte-corps,
    .ml-ville-cours-entete
  ) p a {
    transition: none;
  }
}

/* ─────────────────────────────────────────
   VISIONNEUSE DE LA GALERIE
   Construite entièrement par assets/js/galerie-ville.js, posée à la racine de
   <body>. Rien de tout ceci n'existe sans JavaScript : le script pose la classe
   .ml-gv-actif sur la galerie et la classe .ml-gv-vignette sur chaque photo, et
   c'est à ces classes que les affordances ci-dessous sont accrochées. La grille
   sans script reste une simple grille de photos.
───────────────────────────────────────── */

/* ── Les vignettes, promues en boutons par le script ─────────────────────────
   Curseur d'agrandissement, léger rapprochement de l'image au survol et au
   focus (recadré par la case, qui porte overflow: hidden), contour de focus
   posé À L'INTÉRIEUR de la case (outline-offset négatif) : un contour débordant
   serait rogné par ce même overflow.

   Accroché à .ml-gv-vignette, la classe que le script pose sur chaque photo
   promue, et non plus au balisage de la galerie : les photos des cartes de
   logement s'ouvrent elles aussi en visionneuse depuis août 2026, et elles ne
   sont pas dans une <figure>. Une seule règle sert les deux. */
.ml-gv-actif .ml-gv-vignette {
  cursor: zoom-in;
  transition: transform 420ms var(--ml-ease);
}
/* LE RAPPROCHEMENT AU SURVOL NE VAUT QUE POUR LA GALERIE. Sur une carte de
   logement, la photo vit dans une bande à défilement : l'agrandir la faisait
   déborder sur sa voisine, et un bout de la photo suivante apparaissait à
   droite (constat de Marino). Une carte de logement se parcourt aux flèches,
   pas au survol. */
.ml-gv-actif figure.wp-block-image:hover .ml-gv-vignette {
  transform: scale(1.05);
}
.ml-gv-actif .ml-gv-vignette:focus-visible {
  outline: 3px solid var(--ml-accent);
  outline-offset: -3px;
}
@media (prefers-reduced-motion: reduce) {
  .ml-gv-actif .ml-gv-vignette {
    transition: none;
  }
  .ml-gv-actif figure.wp-block-image:hover .ml-gv-vignette {
    transform: none;
  }
}

/* ── La fenêtre plein écran ───────────────────────────────────────────────────
   Fond nuit profond de la marque (rgb de --ml-primary-dark, à peine translucide
   avec un léger flou), image centrée en « contain » (jamais rognée, on la
   regarde en grand), flèches sur les bords, crédit et compteur juste sous
   l'image. z-index au dessus de la fenêtre des formules (1000) et de la
   mascotte fixe. */
.ml-gv {
  position: fixed;
  inset: 0;
  z-index: 1100;
  opacity: 0;
  transition: opacity 220ms var(--ml-ease);
}
.ml-gv[hidden] {
  display: none;
}
.ml-gv.is-open {
  opacity: 1;
}
@media (prefers-reduced-motion: reduce) {
  .ml-gv {
    transition: none;
  }
}

.ml-gv-boite {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(var(--ml-space-sm), 4vw, var(--ml-space-lg));
}
.ml-gv-boite:focus {
  outline: none;
}

/* Le fond : c'est lui qui reçoit le clic de fermeture dans le vide autour de
   l'image (curseur de réduction), pas l'image elle-même. */
.ml-gv-fond {
  position: absolute;
  inset: 0;
  z-index: 0;
  background: rgba(30, 10, 42, 0.95); /* --ml-primary-dark, à peine translucide */
  -webkit-backdrop-filter: blur(6px);
          backdrop-filter: blur(6px);
  cursor: zoom-out;
}

.ml-gv-scene {
  position: relative;
  z-index: 1;
  margin: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--ml-space-sm);
  max-inline-size: min(100%, 1400px);
  max-block-size: 100%;
  /* Le navigateur gère le défilement vertical (rien à faire, le fond est fixe),
     le script gère le glissement horizontal : touch-action: pan-y évite que le
     navigateur intercepte le geste latéral. */
  touch-action: pan-y;
}

.ml-gv-img {
  display: block;
  max-inline-size: 100%;
  /* Réserve la place du crédit et du compteur sous l'image, sous la barre de
     menu au dessus : l'image ne colle jamais aux bords hauts et bas. */
  max-block-size: calc(100svh - 8rem);
  inline-size: auto;
  block-size: auto;
  object-fit: contain;
  border-radius: var(--ml-radius);
  box-shadow: var(--ml-shadow-lg);
  -webkit-user-select: none;
          user-select: none;
  -webkit-user-drag: none;
}

.ml-gv-bas {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.35rem;
  max-inline-size: 100%;
}
.ml-gv-legende {
  margin: 0;
  max-inline-size: 62ch;
  color: rgba(255, 255, 255, 0.9);
  font-size: var(--ml-fs-sm);
  line-height: 1.4;
  text-align: center;
}
.ml-gv-legende[hidden] {
  display: none;
}
.ml-gv-legende a {
  color: #fff;
  text-decoration: underline;
  text-underline-offset: 2px;
}
.ml-gv-legende a:focus-visible {
  outline-color: #fff;
}

.ml-gv-compteur {
  margin: 0;
  color: rgba(255, 255, 255, 0.82);
  font-family: var(--ml-font-heading);
  font-weight: 700;
  font-size: var(--ml-fs-sm);
  letter-spacing: 0.05em;
}
.ml-gv-compteur[hidden] {
  display: none;
}

/* ── Les boutons (fermer, précédent, suivant) ────────────────────────────────
   Ronds, verre nuit translucide sur bordure blanche discrète, blanc franc au
   dessus : le contraste tient sur n'importe quelle photo derrière eux. */
.ml-gv-bouton {
  position: absolute;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  color: #fff;
  background: rgba(30, 10, 42, 0.55);
  border: 1px solid rgba(255, 255, 255, 0.3);
  border-radius: var(--ml-radius-pill);
  cursor: pointer;
  -webkit-backdrop-filter: blur(4px);
          backdrop-filter: blur(4px);
  transition: background-color 200ms var(--ml-ease), transform 200ms var(--ml-ease);
}
.ml-gv-bouton:hover {
  background: var(--ml-accent);
}
.ml-gv-bouton:focus-visible {
  outline: 2px solid #fff;
  outline-offset: 3px;
}
.ml-gv-bouton[hidden] {
  display: none;
}
@media (prefers-reduced-motion: reduce) {
  .ml-gv-bouton {
    transition: none;
  }
}

.ml-gv-fermer {
  inset-block-start: clamp(var(--ml-space-sm), 3vw, var(--ml-space-md));
  inset-inline-end: clamp(var(--ml-space-sm), 3vw, var(--ml-space-md));
  inline-size: 2.75rem;
  block-size: 2.75rem;
}

.ml-gv-nav {
  inset-block-start: 50%;
  transform: translateY(-50%);
  inline-size: 3rem;
  block-size: 3rem;
}
.ml-gv-nav:hover {
  transform: translateY(-50%) scale(1.06);
}
.ml-gv-prec {
  inset-inline-start: clamp(var(--ml-space-xs), 2vw, var(--ml-space-md));
}
.ml-gv-suiv {
  inset-inline-end: clamp(var(--ml-space-xs), 2vw, var(--ml-space-md));
}
@media (prefers-reduced-motion: reduce) {
  .ml-gv-nav:hover {
    transform: translateY(-50%);
  }
}

/* ─────────────────────────────────────────
   PERMANENCE ET APPEL FINAL (chrome, dégradé de marque)
───────────────────────────────────────── */
.ml-ville-cta {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  background: linear-gradient(120deg, var(--ml-accent) 0%, var(--ml-accent-dark) 100%);
  color: #fff;
}

/* Filigrane : le même champ de bulles de la marque que l'accueil et la fiche
   Pays (assets/img/bulles-fond.png, identique bit à bit à AAA.png). Image de
   fond CSS, aucune balise, donc rien à décrire pour une synthèse vocale.
   Bulles violettes éclaircies en fusion « screen » à faible opacité : elles se
   devinent sur le dégradé violet sans jamais crier, ton sur ton. Le texte et
   la carte de permanence sont peints au dessus (dans .ml-container) : leur
   contraste n'est pas touché, le blanc garde plus de 5:1 même au coeur d'une
   bulle. Le repli sans mix-blend-mode ne fait que les rendre plus discrètes. */
.ml-ville-cta::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  background-image: url('../img/bulles-fond.png');
  background-repeat: no-repeat;
  background-position: 58% 0;
  background-size: 190% auto;
  mix-blend-mode: screen;
  opacity: 0.4;
  pointer-events: none;
}
@media (min-width: 900px) {
  .ml-ville-cta::before {
    background-position: center 0;
    background-size: 100% auto;
  }
}
.ml-ville-cta-grid {
  display: grid;
  gap: var(--ml-space-lg);
  align-items: center;
}
@media (min-width: 900px) {
  .ml-ville-cta-grid {
    grid-template-columns: 1.3fr 0.9fr;
  }
}
.ml-ville-cta-texte h2 {
  color: #fff;
}
.ml-ville-cta-texte p {
  color: rgba(255, 255, 255, 0.9);
  max-width: 52ch;
  margin-block-end: var(--ml-space-md);
}
.ml-ville-cta a:focus-visible,
.ml-ville-cta button:focus-visible {
  outline-color: #fff;
}

/* Le même bouton que sur la page du pays (maquette validée) : blanc, libellé
   violet, gros et gras (10:1 sur blanc ; la consigne « jamais de violet en
   texte sur fond clair » vise les petits corps, pas un libellé de bouton).
   Sans cet habillage, .ml-btn-secondary garderait son aubergine (--ml-primary) : lisible
   aussi, mais les deux pages du même parcours porteraient deux boutons
   différents au même endroit. */
.ml-ville-cta .ml-btn-secondary {
  background: #fff;
  border-color: transparent;
  color: var(--ml-accent);
}
.ml-ville-cta .ml-btn-secondary:hover {
  background: var(--ml-accent-soft);
  border-color: transparent;
  color: var(--ml-accent);
}

/* Le pas de côté vers la page du pays : un lien souligné, blanc, sous le
   bouton. Il complète le fil d'Ariane, trop haut pour qui est arrivé en bas
   de page. */
.ml-ville-cta-retour {
  display: block;
  inline-size: fit-content;
  margin-block-start: var(--ml-space-sm);
  color: #fff;
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 0.25em;
}
.ml-ville-cta-retour:hover {
  text-decoration-thickness: 2px;
}

.ml-ville-permanence {
  border-radius: var(--ml-radius-lg);
  padding: var(--ml-space-md) var(--ml-space-lg);
}
.ml-ville-permanence-titre {
  font-family: var(--ml-font-heading);
  font-weight: 800;
  color: #fff;
  font-size: 1.15rem;
  margin: 0 0 0.2em;
}
.ml-ville-permanence-souligne {
  color: rgba(255, 255, 255, 0.75);
  font-size: var(--ml-fs-sm);
  margin: 0 0 var(--ml-space-sm);
}
/* Permanence détaillée, jour par jour (réglage « Permanence téléphonique »
   du Customizer, une ligne par jour). Quand elle est remplie, elle remplace
   la ligne principale et la note : un horaire lisible d'un coup d'oeil, chaque
   jour séparé du suivant par un filet discret. Blanc sur la carte de verre
   (fond sombre translucide), même contraste que .ml-ville-permanence-ligne,
   bien au dessus de 4,5:1. */
.ml-ville-permanence-jours {
  margin: 0 0 var(--ml-space-sm);
  padding: 0;
  list-style: none;
}
.ml-ville-permanence-jours li {
  color: #fff;
  font-weight: 600;
  font-size: var(--ml-fs-sm);
  padding-block: 0.35em;
}
.ml-ville-permanence-jours li + li {
  border-block-start: 1px solid rgba(255, 255, 255, 0.16);
}

.ml-ville-permanence-ligne {
  color: #fff;
  font-weight: 700;
  margin: 0 0 0.2em;
}
.ml-ville-permanence-note {
  color: rgba(255, 255, 255, 0.75);
  font-size: var(--ml-fs-sm);
  margin: 0 0 var(--ml-space-sm);
}
.ml-ville-permanence-tel {
  display: inline-block;
  color: #fff;
  font-weight: 700;
  text-decoration: underline;
  text-underline-offset: 0.25em;
}

/* La colonne de droite de la section école : l'entête, les cases à cocher et
   les services étudiants, empilés. Elle existe dans tous les cas, avec ou sans
   photo, pour que le gabarit n'ait pas deux structures à maintenir. */
.ml-ville-ecole-colonne {
  display: flex;
  flex-direction: column;
  gap: var(--ml-space-md);
  min-inline-size: 0;
}

/* « Modifier la galerie » : raccourci réservé à l'équipe, jamais rendu pour un
   visiteur (le gabarit ne le pose qu'aux comptes autorisés). Discret, aligné
   avec l'entête de la section : c'est un outil, pas un appel à l'action. */
.ml-ville-galerie-editer {
  margin-block: var(--ml-space-sm) 0;
}

.ml-ville-galerie-editer .ml-btn {
  font-size: var(--ml-fs-sm);
  padding-block: 0.5em;
  padding-inline: 1em;
}

/* ─────────────────────────────────────────
   LE DÉFILÉ DES PHOTOS D'UN LOGEMENT
   Une option d'hébergement peut porter plusieurs photos (l'ancien site les
   montrait déjà en carrousel). UNE SEULE EST VISIBLE À LA FOIS, on passe de
   l'une à l'autre par les flèches.

   LE DÉFILEMENT EST EN CSS, PAS EN JAVASCRIPT, et c'est la décision qui tient
   tout le reste : la piste est une bande à défilement horizontal avec accroche
   (scroll-snap). Sans script, les photos restent donc toutes atteignables, au
   doigt, à la molette et au clavier. Le script n'ajoute que les deux flèches,
   un confort par dessus, jamais la condition d'accès au contenu. Rien n'est
   masqué en attendant un script, comme partout ailleurs dans ce thème.
───────────────────────────────────────── */

.ml-defile {
  position: relative;
}

.ml-defile-piste {
  display: flex;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  /* La barre de défilement native est masquée : les flèches et le glissement
     au doigt suffisent, et une barre sous chaque photo alourdirait la carte.
     Le défilement lui-même n'est jamais empêché. */
  scrollbar-width: none;
}

.ml-defile-piste::-webkit-scrollbar {
  display: none;
}

.ml-defile-vue {
  flex: 0 0 100%;
  scroll-snap-align: center;
}

.ml-defile-vue img {
  display: block;
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
}

@media (prefers-reduced-motion: reduce) {
  .ml-defile-piste {
    scroll-behavior: auto;
  }
}

/* Les flèches, posées par le script seulement. Contraste tenu sur n'importe
   quelle photo : fond sombre à 88 pour cent, glyphe blanc. */
.ml-defile-fleche {
  position: absolute;
  inset-block-start: 50%;
  transform: translateY(-50%);
  display: flex;
  align-items: center;
  justify-content: center;
  inline-size: 36px;
  block-size: 36px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: rgba(46, 18, 63, 0.88);
  color: #fff;
  cursor: pointer;
  transition: background 180ms var(--ml-ease), opacity 180ms var(--ml-ease);
}

.ml-defile-fleche:hover {
  background: var(--ml-accent);
}

.ml-defile-fleche:focus-visible {
  outline: 3px solid #fff;
  outline-offset: -3px;
}

.ml-defile-fleche--avant {
  inset-inline-start: 10px;
}

.ml-defile-fleche--apres {
  inset-inline-end: 10px;
}

/* PLUS AUCUNE FLÈCHE N'EST DÉSACTIVÉE : le défilé boucle (voir
   assets/js/defile-photos.js). La règle qui les effaçait en bout de course a
   donc été retirée, elle ne décrivait plus rien d'atteignable. */

/* Le compteur « 2 / 5 », discret, en bas de la photo. */
.ml-defile-compteur {
  position: absolute;
  inset-block-end: 10px;
  inset-inline-end: 10px;
  padding: 2px 8px;
  border-radius: 999px;
  background: rgba(46, 18, 63, 0.88);
  color: #fff;
  font-size: 11px;
  font-variant-numeric: tabular-nums;
}

/* ─────────────────────────────────────────
   PLUSIEURS ÉCOLES DANS UNE MÊME VILLE
   Berlin (GLS et BWS Germanlingua) et Vancouver (LSI et VGC). Une seule page
   par ville, mais chaque école porte ses propres cours et ses propres formules
   de logement : elles diffèrent réellement.

   RIEN DE TOUT CECI NE S'AFFICHE SUR LES TRENTE ET UNE AUTRES VILLES.

   ATTENTION AU NOM DES CLASSES : les cartes de ce carrefour s'appellent
   ml-ville-choix-carte, et surtout PAS ml-ville-ecole-carte, qui désigne déjà
   les puces d'équipement de la section école. La collision a existé : une
   règle flex-direction: column posée pour ce carrefour est allée renverser les
   puces d'équipement, dont le pictogramme s'est retrouvé au dessus du texte au
   lieu d'être à sa gauche.
───────────────────────────────────────── */

/* ── Le carrefour : deux écoles, deux cartes, deux boutons ─────────────────
   Le moment du choix est le moment le plus important d'une page qui présente
   deux établissements : il a le traitement d'un appel à l'action, fond violet
   plein compris. Tout l'en-tête est centré et blanc. */
.ml-ville-ecoles-choix {
  background: var(--ml-accent);
  color: #fff;
  padding-block: var(--ml-space-xl);
  text-align: center;
}

/* LES TROIS LIGNES SONT BLANCHES ET CENTREES, sans exception (demande de
   Marino). Deux details les en empechaient, et aucun des deux ne se voit en
   lisant la regle de la section :

   1. le surtitre est un conteneur FLEX (voir .ml-eyebrow dans main.css, qui
      pose son petit trait decoratif en ::before). text-align: center ne
      deplace pas les elements d'un flex : il restait cale a gauche pendant que
      le reste de la section etait centre. C'est justify-content qui le centre ;

   2. l'intro etait a quatre-vingt-dix pour cent de blanc, pour ne pas
      concurrencer le titre. A cote de deux lignes franchement blanches, ca ne
      se lisait pas comme une nuance mais comme un defaut. */
/* LE CENTRAGE NE PASSE PLUS PAR text-align, ET C'EST DELIBERE.

   text-align centre le TEXTE a l'interieur de la boite, et il ne suffisait pas
   ici pour deux raisons differentes : le surtitre est un conteneur flex (voir
   .ml-eyebrow dans main.css, qui pose son trait decoratif en ::before), et
   text-align ne deplace pas les elements d'un flex ; et le titre restait cale a
   gauche malgre la regle de section, ce qui veut dire qu'une declaration la
   surcharge quelque part dans la cascade.

   On centre donc les BOITES, pas leur contenu : le conteneur devient une
   colonne flex centree. Aucune declaration text-align ne peut plus rien y
   changer, d'ou qu'elle vienne. La grille des cartes, elle, doit garder toute
   la largeur : elle s'etire explicitement. */
.ml-ville-ecoles-choix > .ml-container {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.ml-ville-ecoles-choix > .ml-container > * {
  max-inline-size: 100%;
}

.ml-ville-ecoles-choix .ml-eyebrow,
.ml-ville-ecoles-choix h2,
.ml-ville-ecoles-choix-intro {
  color: #fff;
  text-align: center;
}

.ml-ville-ecoles-choix .ml-eyebrow {
  justify-content: center;
}

.ml-ville-ecoles-choix h2 {
  margin-block-end: var(--ml-space-xs);
}

/* L'INTRO RESTAIT GRISE, ET LA COUPABLE EST UNE REGLE GENERIQUE DE main.css :

     .ml-section > .ml-container > h2 + p { color: var(--ml-text-mid); }

   Elle habille le paragraphe d'introduction de n'importe quelle section, et
   notre intro EST le paragraphe qui suit le titre. Sa specificite est 0,2,2 :
   .ml-ville-ecoles-choix-intro, avec sa seule classe, ne pesait pas assez, et
   le blanc qu'on y ecrivait ne s'appliquait jamais.

   LA MEME REGLE EXPLIQUAIT LE TITRE, deux lignes plus haut dans main.css :
   .ml-section > .ml-container > h2 { max-inline-size: 22ch }. La BOITE du titre
   etait donc bornee a vingt-deux caracteres et calee a gauche, et text-align ne
   centrait le texte qu'a l'interieur de cette boite etroite. D'ou un titre qui
   paraissait a gauche alors qu'il etait bien centre, dans une boite qui, elle,
   ne l'etait pas. La colonne flex de la section l'a regle en centrant les
   boites.

   On repasse donc par le conteneur pour peser 0,3,0, ce qui l'emporte sur les
   deux classes de la regle generique. Un simple surcroit de specificite, jamais
   un !important : le jour ou la regle generique change, celle-ci reste lisible
   et localisable. */
.ml-ville-ecoles-choix > .ml-container > .ml-ville-ecoles-choix-intro {
  max-inline-size: 56ch;
  margin-inline: auto;
  margin-block-end: var(--ml-space-lg);
  color: #fff;
}

.ml-ville-ecoles-grille {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--ml-space-md);
  text-align: start;
  /* Seule enfant a ne pas se centrer : elle prend toute la largeur du
     conteneur devenu colonne flex. */
  align-self: stretch;
  inline-size: 100%;
}

@media (min-width: 760px) {
  .ml-ville-ecoles-grille {
    grid-template-columns: repeat(2, 1fr);
  }
}

/* La carte est une VIGNETTE : la photo de l'école occupe toute la surface, le
   texte se pose dessus dans un dégradé sombre. Une carte blanche avec une
   photo en bandeau faisait un objet d'administration ; celle-ci donne envie
   d'entrer dans l'école, ce qui est exactement ce qu'on lui demande. */
.ml-ville-choix-carte {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  min-block-size: 340px;
  padding: var(--ml-space-md);
  border-radius: var(--ml-radius-lg);
  overflow: hidden;
  background: var(--ml-accent-dark);
  box-shadow: var(--ml-shadow-md);
  isolation: isolate;
}

.ml-ville-choix-carte-media {
  position: absolute;
  inset: 0;
  z-index: -2;
}

.ml-ville-choix-carte-media img {
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  display: block;
  transition: transform 520ms var(--ml-ease);
}

.ml-ville-choix-carte:hover .ml-ville-choix-carte-media img,
.ml-ville-choix-carte:focus-within .ml-ville-choix-carte-media img {
  transform: scale(1.06);
}

/* Le voile porte la lisibilité du texte, et lui seul : il monte haut et reste
   dense en bas, là où le nom et la phrase se posent. Sans lui, une photo
   claire rendrait le titre blanc illisible. */
.ml-ville-choix-carte::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(
    to top,
    rgba(46, 18, 63, 0.94) 0%,
    rgba(46, 18, 63, 0.78) 38%,
    rgba(46, 18, 63, 0.28) 70%,
    rgba(46, 18, 63, 0.12) 100%
  );
}

.ml-ville-choix-carte-corps {
  display: flex;
  flex-direction: column;
  gap: var(--ml-space-xs);
  color: #fff;
}

.ml-ville-choix-carte-nom {
  margin: 0;
  color: #fff;
  font-size: var(--ml-fs-lg);
  line-height: 1.2;
}

.ml-ville-choix-carte-accroche {
  margin: 0;
  color: rgba(255, 255, 255, 0.88);
  font-size: var(--ml-fs-sm);
}

.ml-ville-choix-carte-bouton {
  align-self: flex-start;
  margin-block-start: var(--ml-space-xs);
}

/* Toute la carte est cliquable, sans pour autant imbriquer deux liens : le
   bouton reste le seul lien du document, et son ombre portée couvre la carte
   entière. Un clic n'importe où sur la vignette suit donc le bouton, et la
   navigation au clavier ne rencontre qu'une seule cible. */
.ml-ville-choix-carte-bouton::before {
  content: "";
  position: absolute;
  inset: 0;
}

@media (prefers-reduced-motion: reduce) {
  .ml-ville-choix-carte-media img {
    transition: none;
  }
  .ml-ville-choix-carte:hover .ml-ville-choix-carte-media img,
  .ml-ville-choix-carte:focus-within .ml-ville-choix-carte-media img {
    transform: none;
  }
}

/* ── La pastille du nom de l'école, collée sous la barre de navigation ─────
   ELLE NE PREND AUCUNE HAUTEUR DANS LE FLUX (block-size: 0). Sans cela, sa
   ligne posait une bande de fond de page entre la section violet foncé et la
   suivante : un liseré blanc au milieu du dégradé, que rien ne justifiait.
   Elle passe donc PAR DESSUS la section qui suit.

   L'écart du haut est celui de la barre de navigation PLUS une marge : la
   barre du site flotte, son bord bas descend plus bas que sa hauteur nominale,
   et sans cette marge le haut de la pastille passait dessous et son texte s'y
   coupait. */
.ml-ville-ecole-bandeau {
  position: sticky;
  inset-block-start: calc(var(--ml-header-h, 72px) + var(--ml-space-sm));
  z-index: 5;
  block-size: 0;
  margin: 0;
  text-align: center;
  pointer-events: none;
  overflow: visible;
}

.ml-ville-ecole-bandeau-pastille {
  display: inline-block;
  max-inline-size: min(92vw, 22rem);
  padding: 0.5em 1.5em 0.6em;
  background: var(--ml-accent);
  color: #fff;
  font-family: var(--ml-font-heading);
  font-size: var(--ml-fs-sm);
  line-height: 1.3;
  border-end-start-radius: var(--ml-radius-lg);
  border-end-end-radius: var(--ml-radius-lg);
  box-shadow: var(--ml-shadow-sm);
  /* Un nom d'école tient sur une ligne : au delà il est coupé, la pastille
     doit rester un repère et non devenir un titre. */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* La barre d'administration de WordPress occupe le haut de l'écran : la
   pastille se pose dessous, jamais derrière. */
.admin-bar .ml-ville-ecole-bandeau {
  inset-block-start: calc(var(--ml-header-h, 72px) + var(--ml-space-sm) + 32px);
}

@media (max-width: 782px) {
  .admin-bar .ml-ville-ecole-bandeau {
    inset-block-start: calc(var(--ml-header-h, 72px) + var(--ml-space-sm) + 46px);
  }
}

/* La cible d'une ancre ne doit pas se retrouver sous la barre de navigation. */
.ml-ville-ecole-groupe {
  scroll-margin-block-start: 104px;
}

/* LA COUPURE ENTRE DEUX ÉCOLES (demande de Marino, août 2026).

   Les deux écoles d'une ville s'enchaînaient sans rien entre elles : les
   logements de la première touchaient l'entête de la seconde, et rien ne disait
   qu'on venait de changer d'établissement.

   PREMIÈRE VERSION : un trait et un losange violet. Marino ne l'a pas gardée,
   ça se lisait comme un séparateur de documentation. Il a demandé, et c'est
   plus juste, que la seconde école arrive PAR DESSUS la première : une ombre
   portée vers le haut, comme une feuille posée sur une autre, et la fin de
   l'école précédente qui s'efface doucement. La coupure se sent au lieu de se
   voir, et rien ne ressemble à une bordure oubliée.

   Sur les trente et une villes à une seule école, ces sélecteurs ne
   s'appliquent jamais : il leur faut un groupe voisin. */
.ml-ville-ecole-groupe + .ml-ville-ecole-groupe {
  position: relative;
  z-index: 1;
  margin-block-start: var(--ml-space-lg);
  border-start-start-radius: var(--ml-radius-lg);
  border-start-end-radius: var(--ml-radius-lg);
  background: #fff;
  box-shadow: 0 -22px 44px -26px rgba(46, 18, 63, 0.45);
}

/* La dernière section de l'école précédente s'efface sur sa fin. Le fondu porte
   sur le contenu ET sur son fond : c'est ce qui fait qu'on ne voit pas où la
   première s'arrête, seulement que la seconde commence.

   Sous @supports parce qu'un navigateur sans masque doit afficher la section
   entière plutôt qu'une section coupée : le repli, ici, c'est l'absence
   d'effet, jamais un contenu perdu. */
@supports (mask-image: linear-gradient(#000, transparent)) {
  .ml-ville-ecole-groupe:has(+ .ml-ville-ecole-groupe) > :last-child {
    -webkit-mask-image: linear-gradient(to bottom, #000 74%, rgba(0, 0, 0, 0.12) 100%);
    mask-image: linear-gradient(to bottom, #000 74%, rgba(0, 0, 0, 0.12) 100%);
  }
}

/* ══ INFOS PRATIQUES : UN LIEN SUR TÉLÉPHONE, UNE FENÊTRE AU CLIC ═══════════
   Demande de Marino. Le haut de page portait deux surfaces de verre dépoli, la
   pastille du titre et le carré des informations. Deux blocs translucides l'un
   sous l'autre sur un petit écran, c'est un de trop : le titre y perdait sa
   place et le haut de page devenait un empilement.

   Le carré perd donc son verre sur téléphone et se réduit à ce qu'il porte
   d'utile là : le bouton principal, plus un lien qui ouvre les informations.
   La liste, elle, est DÉPLACÉE dans la fenêtre par le script, jamais recopiée
   (voir assets/js/fiche-ville-infos.js). */
@media (max-width: 899.98px) {
  .ml-ville-hero-faits {
    padding: 0;
    background: none;
    border: 0;
    box-shadow: none;
    -webkit-backdrop-filter: none;
            backdrop-filter: none;
  }
}

/* Le lien qui ouvre la fenêtre. Souligné, pas encadré : un second bouton juste
   sous le bouton principal se disputerait l'attention avec lui, alors qu'il ne
   fait qu'ouvrir un complément. La cible tactile reste à 44px de haut. */
.ml-ville-infos-lien {
  display: inline-flex;
  align-items: center;
  min-block-size: 44px;
  margin-block-start: var(--ml-space-xs);
  padding: 0;
  border: 0;
  background: none;
  color: #fff;
  font: inherit;
  font-size: var(--ml-fs-sm);
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 0.25em;
  cursor: pointer;
}

.ml-ville-infos-lien:hover,
.ml-ville-infos-lien:focus-visible {
  text-decoration-thickness: 2px;
}

/* La fenêtre. Elle vient du bas, comme les feuilles que l'on tire sur un
   téléphone : c'est le geste attendu pour un complément d'information, et ça
   la garde à portée du pouce. */
.ml-ville-infos-dialogue {
  inline-size: 100%;
  max-inline-size: 100%;
  margin: 0;
  margin-block-start: auto;
  padding: 0;
  border: 0;
  background: none;
}

.ml-ville-infos-dialogue::backdrop {
  background: rgba(34, 29, 41, 0.55);
}

.ml-ville-infos-panneau {
  padding: var(--ml-space-md);
  padding-block-end: calc(var(--ml-space-md) + env(safe-area-inset-bottom, 0px));
  background: var(--ml-bg);
  border-start-start-radius: var(--ml-radius-lg);
  border-start-end-radius: var(--ml-radius-lg);
}

.ml-ville-infos-entete {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--ml-space-sm);
  margin-block-end: var(--ml-space-sm);
}

.ml-ville-infos-titre {
  margin: 0;
  font-family: var(--ml-font-heading);
  font-size: var(--ml-fs-lg);
  color: var(--ml-primary);
}

.ml-ville-infos-fermer {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  inline-size: 44px;
  block-size: 44px;
  border: 0;
  border-radius: 50%;
  background: var(--ml-bg-alt);
  color: var(--ml-primary);
  cursor: pointer;
}

/* Dans la fenêtre, la liste est sur fond clair : elle était écrite pour du
   verre posé sur une photo, donc en blanc. */
.ml-ville-infos-panneau .ml-ville-faits-liste {
  margin: 0;
  color: var(--ml-text);
}

.ml-ville-infos-panneau .ml-ville-fait dt {
  color: var(--ml-text-mid);
}

.ml-ville-infos-panneau .ml-ville-fait dd {
  color: var(--ml-primary);
}

@media (prefers-reduced-motion: no-preference) {
  .ml-ville-infos-dialogue[open] {
    animation: ml-ville-infos-monte 220ms var(--ml-ease);
  }
}

@keyframes ml-ville-infos-monte {
  from { transform: translateY(12px); opacity: 0; }
  to   { transform: translateY(0); opacity: 1; }
}

/* ══ HAUT DE PAGE SUR TÉLÉPHONE : ENCORE DEUX CRANS D'ALLÉGÉ ════════════════
   Demande de Marino, après avoir vu le résultat du passage des informations en
   fenêtre.

   1. LE BOUTON PRINCIPAL DISPARAIT DU HAUT DE PAGE. Il n'est pas perdu : la
      page se termine par une section d'appel à l'action qui porte le même
      bouton, et sur un téléphone on y arrive en faisant défiler, ce qui est le
      geste naturel. Le garder ici, c'est demander la décision avant d'avoir
      montré l'école, les cours et les logements.

      Il reste dans le document, seul son affichage est masqué : sur un écran
      large il garde tout son sens, et le lien de prise de rendez-vous continue
      d'exister pour les moteurs.

   2. LA PASTILLE DU TITRE SE RESSERRE. Ses marges intérieures étaient calées
      pour un écran large ; sur un petit, elles poussaient « Séjour
      linguistique » vers une largeur qui mangeait la ligne du titre juste
      dessous. */
@media (max-width: 899.98px) {
  .ml-ville-hero-cta {
    display: none;
  }

  .ml-ville-h1-surtitre {
    padding-inline: 0.7em;
  }
}
