/* ==========================================================================
   Escales du Monde — services
   Styles des pages de section :
     · bandeau de section
     · barre de recherche + onglets de filtre
     · liste de cartes (catalogue)
     · fiche détail en surcouche (overlay)
     · configurateur de demande (vols, ferry, hôtels)
   ========================================================================== */

/* ------------------------------------------------------ Bandeau section -- */

/* Dégradé bleu seul en fallback (pages sans photo, s'il y en a un jour) ;
   avec une photo (.sec-hero-photo), il ne sert plus qu'à combler les bords
   le temps que l'image charge. Le voile bleu ici (::before) n'est PAS le
   même arbitrage que sur les cartes de services : là-bas il ternissait des
   photos déjà cohérentes entre elles, retiré pour ça. Ici, chaque hero n'a
   QU'UNE photo, isolée, sur laquelle un h1/p blanc doit rester lisible —
   le voile a donc un vrai rôle de contraste, pas seulement décoratif.      */
.sec-hero {
  background: linear-gradient(160deg, var(--blue) 0%, var(--blue-deep) 100%);
  color: #fff;
  padding: 34px 0 38px;
  position: relative;
  overflow: hidden;
}
/* Les photos de hero (…-hero.jpg) sont déjà recadrées à la source, sujet
   à droite : le réglage par défaut ne fait donc plus rien (zoom 1, origine
   centrée). Pour une photo qui ne l'est pas (ex. l'ancienne e-SIM, pas
   encore refaite dans ce format), une page peut encore forcer un zoom et
   un point d'ancrage via un style en ligne — voir esim/index.html — sur
   le même principe qu'avant : le bandeau est bien plus large que haut,
   « cover » seul montre déjà toute la largeur de la photo, il faut zoomer
   pour créer une marge de recadrage à décaler vers la droite.             */
.sec-hero-photo {
  position: absolute; inset: 0; z-index: 0;
  width: 100%; height: 100%; object-fit: cover;
  transform: scale(var(--hero-zoom, 1));
  transform-origin: var(--hero-focus, 50%) 50%;
}
/* Voile de marque allégé à la demande du client (.56/.64 auparavant) : il
   masquait trop la photo.

   Mais l'alléger seul faisait tomber le paragraphe à 2,6:1 sur les photos
   claires (mesuré sur le ciel de /vols/) — il était déjà à 3,9:1 avant,
   donc sous les 4,5:1 exigés. D'où le ::after ci-dessous : plutôt que de
   rassombrir TOUTE l'image, on n'assombrit que le côté où se trouve le
   texte. Les photos de hero sont recadrées avec leur sujet à l'opposé du
   texte (voir la note sur --hero-focus juste au-dessus), l'avion et les
   dunes restent donc parfaitement visibles.                              */
.sec-hero::before {
  content: ''; position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(160deg, rgba(0, 85, 221, .22) 0%, rgba(0, 38, 160, .28) 100%);
  mix-blend-mode: multiply;
}
/* Le dégradé part du côté du texte et s'efface avant le sujet de la photo.
   « scaleX(-1) » en RTL : en arabe le texte passe à droite, et la photo
   est déjà mise en miroir pour la même raison (voir styles.css).          */
.sec-hero::after {
  content: ''; position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(to right,
    rgba(9, 30, 49, .76) 0%,
    rgba(9, 30, 49, .52) 40%,
    rgba(9, 30, 49, 0) 72%);
}
[dir="rtl"] .sec-hero::after { transform: scaleX(-1); }
.sec-hero > .wrap { position: relative; z-index: 2; }
/* Ombre portée : le client a demandé deux fois d'alléger le voile bleu, et
   à ce niveau le paragraphe tombe à 3,6:1 sur les photos claires (mesuré
   sur le ciel de /vols/). Rassombrir irait contre la demande ; l'ombre,
   elle, ne noircit pas un pixel de la photo et rend le texte lisible sur
   n'importe quel fond. À garder si on rallège encore le voile.

   Blanc PUR pour le paragraphe, et non 88 % comme ailleurs : sur une
   photo, ces 12 % de transparence coûtaient 0,7 point de contraste. La
   hiérarchie avec le h1 est déjà portée par la taille et la graisse.      */
.sec-hero h1 {
  font-size: clamp(26px, 6.2vw, 38px); margin-bottom: 8px;
  text-shadow: 0 1px 12px rgba(9, 30, 49, .5), 0 1px 2px rgba(9, 30, 49, .35);
}
.sec-hero p  {
  margin: 0; color: #fff; max-width: 58ch; font-size: 15.5px;
  text-shadow: 0 1px 10px rgba(9, 30, 49, .6), 0 1px 2px rgba(9, 30, 49, .45);
}

/* ------------------------------------------------- Frise de transporteurs */
/* Sur vols/, charter/ et ferry/ : une capacité générale (« nous pouvons
   émettre avec ces compagnies »), pas une liste de partenariats officiels
   ni une compagnie assignée à une route/traversée précise — ni l'un ni
   l'autre n'est vérifiable. Logos réels, sourcés et crédités un par un
   dans assets/IMAGE-CREDITS.md (marques déposées : identification
   uniquement). Nom générique (« brand », pas « airlines ») : les mêmes
   règles servent aussi bien pour les compagnies aériennes que maritimes.  */
.brand-strip { padding: 26px 0; border-bottom: 1px solid var(--border-soft); }
.brand-label {
  font-size: 12px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  color: var(--text-muted); margin: 0 0 16px; text-align: center;
}
.brand-list {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: 24px 36px;
}
.brand-list img { display: block; height: 24px; width: auto; max-width: 110px; object-fit: contain; }

/* --------------------------------------- Repères courts sous le hero ----- */
/* Quelques faits en checklist, avant le configurateur (transferts/ pour
   l'instant) — réutilise .detail-list.is-check plutôt qu'un nouveau
   composant : mêmes puces à coche que dans les fiches catalogue.           */
.sec-features { padding: 28px 0 4px; }

/* --------------------------------------------- Barre de recherche/filtre -- */

.sec-tools { padding: 22px 0 4px; }

.search-box { position: relative; margin-bottom: 14px; }
.search-box svg {
  position: absolute; inset-inline-start: 15px; top: 50%; transform: translateY(-50%);
  width: 19px; height: 19px; stroke: var(--text-muted); fill: none; stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round; pointer-events: none;
}
.search-box input {
  width: 100%; font: inherit; font-size: 16px;   /* 16px : évite le zoom iOS */
  padding-block: 13px; padding-inline: 44px 16px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--surface); color: var(--text);
  box-shadow: var(--shadow-sm);
  transition: border-color .16s ease, box-shadow .16s ease;
}
.search-box input::placeholder { color: #93a0b2; }
.search-box input:focus {
  outline: none;
  border-color: var(--blue);
  box-shadow: 0 0 0 4px rgba(0, 85, 221, .14);
}

.filter-tabs {
  display: flex; flex-wrap: wrap; gap: 8px;
  margin: 0 0 6px; padding: 0; list-style: none;
}
/* Les mêmes pastilles servent à filtrer (/visa/) et à trier (/voyages/) —
   voir « LA BARRE DE PASTILLES » en tête de js/catalog.js.                */
.filter-tabs button {
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text-muted);
  font-size: 14px; font-weight: 500;
  padding: 8px 16px; border-radius: 999px;
  cursor: pointer;
  transition: background .16s ease, color .16s ease, border-color .16s ease;
}
.filter-tabs button:hover { border-color: var(--blue); color: var(--blue); }
.filter-tabs button[aria-pressed='true'] {
  background: var(--blue); border-color: var(--blue); color: #fff;
}

.result-count {
  font-size: 13.5px; color: var(--text-muted);
  margin: 10px 0 0;
}

/* ----------------------------------------------------- Liste catalogue --- */

.catalog {
  display: grid; gap: 12px;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  list-style: none; margin: 18px 0 0; padding: 0;
}

/* Hauteur de carte uniforme
   ------------------------
   Le nombre d'étiquettes varie d'une fiche à l'autre — pour les visas, de 2
   à 4 — avec des libellés de longueur très inégale (« Tourisme » contre
   « Délai indicatif : qq. jours à 2 sem. »). La rangée d'étiquettes occupait
   donc une à trois lignes selon le pays ; or une rangée de grille se règle
   sur son élément le plus haut, si bien que deux rangées n'avaient jamais la
   même hauteur. D'où le rendu décousu, constaté aussi bien en français qu'en
   arabe.

   « grid-auto-rows: 1fr » aligne TOUTES les rangées sur la plus haute : les
   cartes deviennent identiques, quelle que soit la langue et quel que soit
   le nombre de pays ajoutés par la suite. Rien n'est masqué ni tronqué — le
   contenu, centré verticalement, respire seulement un peu plus sur les
   fiches les plus courtes.

   La présentation « vol » est exclue : elle n'a qu'une seule colonne, où
   égaliser n'apporterait rien et creuserait de grands vides.               */
.catalog:not(.is-flight) { grid-auto-rows: 1fr; }
.catalog:not(.is-flight) > li { display: flex; }
.item-card, .poster-card { height: 100%; }

.item-card {
  width: 100%; text-align: start;
  display: flex; align-items: center; gap: 14px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 14px 16px;
  cursor: pointer;
  box-shadow: var(--shadow-sm);
  transition: transform .16s ease, box-shadow .16s ease, border-color .16s ease;
}
.item-card:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-md);
  border-color: var(--blue);
}

/* ------------------------------------------------ Vignette d'un élément --
   UNE SEULE BOÎTE, au format 4:3, pour les trois types de vignette :
   drapeau SVG, photo et glyphe. C'est ce qui garantit que la colonne de
   gauche reste parfaitement alignée d'une ligne à l'autre, et qu'un pays
   ajouté demain aura exactement la même apparence que les autres.

   Les drapeaux viennent de flag-icons (MIT) et sont TOUS dessinés dans un
   viewBox 640×480 — aucun n'a besoin d'être recadré ou retouché.          */

.item-flag-img,
.item-thumb,
.item-flag {
  flex: none;
  width: 60px; height: 45px;      /* 4:3 exact */
  border-radius: 7px;
}

.item-flag-img {
  object-fit: cover;
  background: var(--blue-tint);
  /* Bordure fine : sans elle, les drapeaux à fond blanc (Japon, Pologne…)
     n'auraient aucun contour sur la carte blanche.                        */
  border: 1px solid rgba(9, 30, 49, .14);
}

.item-thumb {
  object-fit: cover;
  background: var(--blue-tint);
}

/* Glyphe (Omra, Monde, assurance) : ce ne sont pas des pays, il n'existe
   pas de drapeau — la boîte reste néanmoins identique.                    */
.item-flag {
  display: grid; place-items: center;
  background: var(--blue-tint);
  font-size: 24px; line-height: 1;
  font-family: 'Apple Color Emoji', 'Segoe UI Emoji', 'Noto Color Emoji', var(--font-body);
  font-weight: 700; color: var(--blue);
}

/* Version agrandie, dans l'en-tête de la fiche détail. */
.item-flag-img.is-lg,
.item-thumb.is-lg,
.item-flag.is-lg {
  width: 76px; height: 57px; border-radius: 9px;
}
.item-flag.is-lg { font-size: 30px; }

.item-body { min-width: 0; flex: 1; }
.item-body h3 {
  font-family: var(--font-body);
  font-size: 16px; font-weight: 600;
  margin-bottom: 2px;
}
.item-body p {
  margin: 0; font-size: 13.5px; color: var(--text-muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.item-tags { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 6px; }
.tag {
  font-size: 11.5px; font-weight: 600; letter-spacing: .02em;
  padding: 3px 8px; border-radius: 999px;
  background: var(--blue-soft); color: var(--blue-deep);
}
.tag.is-muted { background: var(--border-soft); color: var(--text-muted); }
.tag.is-todo  { background: var(--warn-bg); color: var(--warn-text); }
/* Même teinte que le badge « Bientôt » des cartes affiche (.poster-badge.is-soon
   dans ce même fichier) : un seul vocabulaire de couleur pour « pas encore prêt »,
   qu'il s'agisse d'une étiquette ou d'un badge.                                  */
.tag.is-soon  { background: var(--navy); color: #fff; }

.item-chevron {
  flex: none; width: 18px; height: 18px;
  stroke: var(--text-muted); fill: none; stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round;
}

.catalog-empty {
  text-align: center; color: var(--text-muted);
  padding: 44px 20px;
  background: var(--surface);
  border: 1px dashed var(--border);
  border-radius: var(--radius);
  margin-top: 18px;
}

/* ------------------------------------------------------------- Overlay --- */

.overlay {
  position: fixed; inset: 0; z-index: 120;
  display: none;
}
.overlay.is-open { display: block; }

.overlay-backdrop {
  position: absolute; inset: 0;
  background: rgba(9, 30, 49, .55);
  animation: fade-in .2s ease;
}
@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }

.overlay-panel {
  position: absolute; inset: auto 0 0 0;
  max-height: 92vh;
  background: var(--surface);
  border-radius: 22px 22px 0 0;
  overflow: hidden; /* Sans ça, la scrollbar native (droite) de .overlay-body
                        dessine un angle carré qui déborde de l'arrondi ici. */
  display: flex; flex-direction: column;
  animation: slide-up .28s cubic-bezier(.22, .61, .36, 1);
  box-shadow: var(--shadow-lg);
}
@keyframes slide-up { from { transform: translateY(28px); opacity: 0; } to { transform: translateY(0); opacity: 1; } }

@media (min-width: 760px) {
  .overlay-panel {
    inset: 50% auto auto 50%;
    transform: translate(-50%, -50%);
    width: min(640px, calc(100vw - 48px));
    max-height: 86vh;
    border-radius: var(--radius-lg);
    animation: pop-in .24s cubic-bezier(.22, .61, .36, 1);
  }
  @keyframes pop-in {
    from { transform: translate(-50%, -46%); opacity: 0; }
    to   { transform: translate(-50%, -50%); opacity: 1; }
  }
}

.overlay-head {
  display: flex; align-items: flex-start; gap: 14px;
  padding: 20px 20px 16px;
  border-bottom: 1px solid var(--border-soft);
  flex: none;
}
/* La taille de la vignette est portée par « .is-lg » (voir plus haut) :
   pas de surcharge ici, sinon les trois types de vignette se remettraient
   à diverger dans l'en-tête de la fiche.                                  */
.overlay-head h2 { font-size: 22px; margin-bottom: 3px; }
.overlay-head .sub { margin: 0; font-size: 14px; color: var(--text-muted); }

/* Groupe « Partager / Fermer » : un seul wrapper flex, positionné une
   fois par mise en page (voir .overlay-head, .is-poster, .is-flight plus
   bas), plutôt que de repositionner chaque bouton séparément.            */
.overlay-controls {
  display: flex; gap: 8px; flex: none;
  margin-inline-start: auto;
}

.overlay-close, .overlay-share {
  flex: none;
  width: 36px; height: 36px; border-radius: 50%;
  border: 1px solid var(--border); background: var(--surface);
  display: grid; place-items: center; cursor: pointer;
  transition: background .16s ease;
}
.overlay-close:hover, .overlay-share:hover { background: var(--bg); }
.overlay-close svg, .overlay-share svg {
  width: 16px; height: 16px;
  stroke: var(--text); fill: none; stroke-width: 2.2; stroke-linecap: round;
}
.overlay-share.is-copied { border-color: #1a9e5c; }
.overlay-share.is-copied svg { stroke: #1a9e5c; }

.overlay-body {
  padding: 18px 20px 20px;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  flex: 1 1 auto;
}

.detail-block { margin-bottom: 22px; }
.detail-block:last-child { margin-bottom: 0; }
.detail-block h3 {
  font-family: var(--font-body);
  font-size: 12.5px; font-weight: 700;
  letter-spacing: .07em; text-transform: uppercase;
  color: var(--blue);
  margin-bottom: 10px;
}
.detail-block p { margin: 0; color: var(--text); }

.detail-list { list-style: none; margin: 0; padding: 0; }
.detail-list li {
  position: relative;
  padding-block: 6px; padding-inline: 26px 0;
  border-bottom: 1px solid var(--border-soft);
  font-size: 15px;
}
.detail-list li:last-child { border-bottom: 0; }
.detail-list li::before {
  content: ''; position: absolute; inset-inline-start: 6px; top: 14px;
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--blue);
}
.detail-list.is-check li::before {
  content: '\2713';
  inset-inline-start: 2px; top: 5px; width: auto; height: auto;
  background: none; color: var(--whatsapp-dk); font-weight: 700; font-size: 14px;
}
.detail-list.is-cross li::before {
  content: '\00d7';
  inset-inline-start: 4px; top: 3px; width: auto; height: auto;
  background: none; color: #c05050; font-weight: 700; font-size: 17px;
}

.detail-pairs { display: grid; gap: 1px; background: var(--border-soft); border-radius: 10px; overflow: hidden; }
.detail-pairs > div {
  display: flex; justify-content: space-between; gap: 16px;
  background: var(--surface); padding: 10px 13px; font-size: 14.5px;
}
.detail-pairs dt { color: var(--text-muted); }
.detail-pairs dd { margin: 0; font-weight: 600; text-align: end; }

.price-line {
  display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
  margin-bottom: 4px;
}
.price-line .from { font-size: 13.5px; color: var(--text-muted); }
.price-line .amount { font-size: 27px; font-weight: 700; color: var(--blue); font-family: var(--font-title); }
/* Prix promo : ancien prix barré et grisé, juste avant le nouveau (voir
   js/catalog.js → renderBlock, type 'price', champ « old »).              */
.price-line .price-old {
  font-size: 16px; font-family: var(--font-title); color: var(--text-muted);
  text-decoration: line-through; text-decoration-thickness: 1px;
}

.price-meta { font-size: 12.5px; color: var(--text-muted); margin: 0; }

.notice {
  background: var(--warn-bg);
  border: 1px solid var(--warn-border);
  color: var(--warn-text);
  border-radius: 10px;
  padding: 11px 14px;
  font-size: 13.5px;
  /* Rendu hors de « .detail-block » (voir js/catalog.js) : il n'hérite pas
     de son « margin-bottom: 22px », d'où le même écart en haut ET en bas
     pour ne pas rester collé au bloc suivant (bug visuel repéré en usage
     réel — le bloc « note » est souvent suivi d'un autre bloc).           */
  margin-block: 22px;
}
.notice:first-child { margin-top: 0; }
.notice strong { font-weight: 700; }

.overlay-foot {
  flex: none;
  padding: 14px 20px calc(14px + env(safe-area-inset-bottom, 0px));
  border-top: 1px solid var(--border-soft);
  background: var(--surface);
  border-radius: 0 0 var(--radius-lg) var(--radius-lg);
}
.overlay-foot .btn-wa { width: 100%; }
.overlay-foot .foot-hint {
  margin: 9px 0 0; text-align: center;
  font-size: 12.5px; color: var(--text-muted);
}

body.is-locked { overflow: hidden; }

/* -------------------------------------------------------- Configurateur -- */

.configurator {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  padding: 22px 20px;
  margin-top: 22px;
}
.configurator h2 { font-size: 21px; margin-bottom: 4px; }
.configurator > .lede { margin: 0 0 20px; color: var(--text-muted); font-size: 14.5px; }

.field-grid { display: grid; gap: 14px; grid-template-columns: 1fr; }
@media (min-width: 620px) { .field-grid { grid-template-columns: 1fr 1fr; } }
.field.is-full { grid-column: 1 / -1; }

/* Indispensable : par défaut un élément de grille a « min-width: auto », si
   bien qu'un contenu large — deux champs date côte à côte — élargit la
   colonne et fait déborder toute la carte. Le min-width: 0 laisse les
   champs se comprimer à la largeur disponible.                            */
.field-grid > .field { min-width: 0; }
.field .segmented,
.field .dr-wrap,
.field .segmented button,
.field .daterange,
.field .daterange input,
.field .picker { min-width: 0; }

.field label {
  display: block;
  font-size: 13px; font-weight: 600;
  color: var(--text-muted);
  margin-bottom: 5px;
}
.field .req { color: var(--blue); }

.field input[type='text'],
.field input[type='date'],
.field input[type='time'],
.field input[type='number'],
.field select,
.field textarea {
  width: 100%; font: inherit; font-size: 16px;
  padding: 11px 13px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--surface); color: var(--text);
  transition: border-color .16s ease, box-shadow .16s ease;
}
.field textarea { resize: vertical; min-height: 76px; }
.field input:focus, .field select:focus, .field textarea:focus {
  outline: none;
  border-color: var(--blue);
  box-shadow: 0 0 0 4px rgba(0, 85, 221, .14);
}
.field input:disabled, .field select:disabled {
  background: var(--border-soft); color: #9aa5b4; cursor: not-allowed;
}
.field .hint { font-size: 12.5px; color: var(--text-muted); margin: 5px 0 0; }

/* Segments (aller simple / aller-retour, classe, etc.) */
.segmented {
  display: flex; gap: 0;
  border: 1px solid var(--border);
  border-radius: 10px;
  overflow: hidden;
  background: var(--surface);
}
.segmented button {
  flex: 1; border: 0; background: transparent;
  padding: 11px 8px; font-size: 14.5px; font-weight: 500;
  color: var(--text-muted); cursor: pointer;
  transition: background .16s ease, color .16s ease;
}
.segmented button + button { border-inline-start: 1px solid var(--border); }
.segmented button[aria-pressed='true'] { background: var(--blue); color: #fff; font-weight: 600; }

/* ------------------------------------------------- Bloc de dates unique -- */
/* Deux moitiés qui affichent la sélection, et UN SEUL calendrier commun qui
   s'ouvre dessous : premier clic la date de départ, second clic la date de
   retour. Pas de sélecteur natif : il ne sait pas afficher une plage.      */

.dr-wrap { position: relative; }

.daterange {
  display: flex;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--surface);
  overflow: hidden;
}

.field .daterange .dr-half {
  flex: 1 1 0; min-width: 0;
  padding: 8px 13px 9px;
  display: block; text-align: start; cursor: pointer;
  background: transparent; border: 0;
  transition: background .16s ease;
}
.field .daterange .dr-half + .dr-half { border-inline-start: 1px solid var(--border); }
.field .daterange .dr-half:hover { background: var(--blue-tint); }
/* La moitié en cours de saisie est mise en avant : on voit d'un coup d'œil
   si le calendrier attend le départ ou le retour.                          */
.field .daterange .dr-half.is-active { background: var(--blue-soft); }

.daterange .dr-half > span {
  display: block;
  font-size: 11px; font-weight: 700;
  letter-spacing: .07em; text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: 1px;
}
.daterange .dr-half > em {
  display: block; font-style: normal;
  font-size: 16px; color: var(--text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.daterange .dr-half > em.is-empty { color: #9aa5b4; }

/* Aller simple : la moitié « retour » disparaît, le cadre reste entier. */
.daterange.is-single .dr-half + .dr-half { display: none; }

/* ------------------------------------------------------- Le calendrier -- */

.cal-pop {
  position: absolute; z-index: 30;
  top: calc(100% + 8px); inset-inline-start: 0;
  width: min(320px, 100%);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 14px;
  box-shadow: var(--shadow-lg);
  padding: 12px 12px 8px;
  animation: picker-in .16s ease;
}

.cal-top {
  display: flex; align-items: center; justify-content: space-between;
  gap: 8px; margin-bottom: 8px;
}
.cal-title {
  font-weight: 600; font-size: 15.5px;
  text-transform: capitalize;
}
.cal-nav {
  width: 32px; height: 32px; flex: none;
  border: 1px solid var(--border); border-radius: 50%;
  background: var(--surface); cursor: pointer;
  display: grid; place-items: center;
  transition: background .16s ease, border-color .16s ease;
}
.cal-nav:hover:not(:disabled) { background: var(--blue-soft); border-color: var(--blue); }
.cal-nav:disabled { opacity: .35; cursor: not-allowed; }
.cal-nav svg {
  width: 15px; height: 15px;
  stroke: var(--blue); fill: none; stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round;
}

.cal-dow {
  display: grid; grid-template-columns: repeat(7, 1fr);
  margin-bottom: 2px;
}
.cal-dow span {
  text-align: center;
  font-size: 11px; font-weight: 600;
  color: var(--text-muted); text-transform: uppercase;
  padding: 4px 0;
}

.cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); }

/* La cellule porte la bande de la plage, le bouton porte la pastille : sans
   cette séparation, impossible d'avoir une bande continue entre deux dates
   tout en gardant les extrémités rondes.                                   */
.cal-cell {
  position: relative;
  display: grid; place-items: center;
  height: 38px;
}
.cal-cell.is-in,
.cal-cell.is-edge-l,
.cal-cell.is-edge-r { background: var(--blue-soft); }
.cal-cell.is-edge-l { border-radius: 999px 0 0 999px; }
.cal-cell.is-edge-r { border-radius: 0 999px 999px 0; }

.cal-day {
  width: 34px; height: 34px;
  border: 0; border-radius: 50%;
  background: transparent;
  font-size: 14.5px; color: var(--text);
  cursor: pointer;
  display: grid; place-items: center;
  transition: background .14s ease, color .14s ease;
}
.cal-day:hover:not(:disabled) { background: var(--blue-tint); }
.cal-day:disabled { color: #c7cedb; cursor: not-allowed; }
.cal-day.is-today { box-shadow: inset 0 0 0 1px var(--border); }
.cal-cell.is-sel .cal-day {
  background: var(--blue); color: #fff; font-weight: 600;
  box-shadow: none;
}

.cal-foot {
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px;
  margin-top: 8px; padding-top: 9px;
  border-top: 1px solid var(--border-soft);
}
.cal-hint { font-size: 13px; color: var(--text-muted); }
.cal-clear {
  border: 0; background: transparent;
  color: var(--blue); font-size: 13px; font-weight: 600;
  cursor: pointer; padding: 2px 0;
}
.cal-clear:hover { text-decoration: underline; }

/* ------------------------------------------------ Sélecteur d'aéroport -- */
/* Champ texte enrichi d'une liste déroulante (js/airport-picker.js). Le
   client garde la main : il peut valider une suggestion ou continuer à
   taper librement, rien n'est bloqué sur la liste.                       */

.airport-field { position: relative; }

.airport-field input {
  width: 100%; font: inherit; font-size: 16px;   /* 16px : évite le zoom iOS */
  padding: 11px 13px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--surface); color: var(--text);
  transition: border-color .16s ease, box-shadow .16s ease;
}
.airport-field input:focus {
  outline: none;
  border-color: var(--blue);
  box-shadow: 0 0 0 4px rgba(0, 85, 221, .14);
}

.airport-pop {
  position: absolute; z-index: 30;
  top: calc(100% + 6px); inset-inline: 0;
  max-height: 320px; overflow-y: auto;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  box-shadow: var(--shadow-lg);
  padding: 6px;
  animation: picker-in .16s ease;
}

.airport-item {
  width: 100%;
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 9px 10px;
  border: 0; border-radius: 8px;
  background: transparent;
  text-align: start; cursor: pointer;
  transition: background .12s ease;
}
.airport-item:hover,
.airport-item.is-active { background: var(--blue-tint); }

.ai-main { min-width: 0; }
.ai-city {
  display: block; font-size: 14.5px; font-weight: 600; color: var(--text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ai-name {
  display: block; font-size: 12.5px; color: var(--text-muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ai-code {
  flex: none;
  font-family: var(--font-body); font-size: 12px; font-weight: 700;
  letter-spacing: .03em;
  padding: 3px 8px; border-radius: 999px;
  background: var(--blue-soft); color: var(--blue-deep);
}

.airport-empty {
  margin: 0; padding: 12px 10px;
  font-size: 13.5px; color: var(--text-muted);
}

/* ------------------------------------------- Sélecteur replié (passagers) */
/* Replié, il occupe la hauteur d'un champ ordinaire et résume la sélection
   (« 2 adultes, 1 enfant »). Le détail s'ouvre en surcouche, donc rien ne
   se décale sur la page.                                                    */

.picker { position: relative; }

.picker-toggle {
  width: 100%;
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 11px 13px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--surface);
  font-size: 16px; color: var(--text);
  text-align: start; cursor: pointer;
  transition: border-color .16s ease, box-shadow .16s ease;
}
.picker-toggle:hover { border-color: var(--blue); }
.picker-toggle[aria-expanded='true'] {
  border-color: var(--blue);
  box-shadow: 0 0 0 4px rgba(0, 85, 221, .14);
}
.picker-toggle .picker-value {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.picker-toggle > svg {
  width: 17px; height: 17px; flex: none;
  stroke: var(--text-muted); fill: none; stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round;
  transition: transform .18s ease;
}
.picker-toggle[aria-expanded='true'] > svg { transform: rotate(180deg); }

.picker-panel {
  position: absolute; z-index: 20;
  top: calc(100% + 6px); inset-inline: 0;
  min-width: 260px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  box-shadow: var(--shadow-lg);
  padding: 4px 14px;
  animation: picker-in .16s ease;
}
@keyframes picker-in {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* Compteurs de passagers */
.counter-row {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 11px 0;
  border-bottom: 1px solid var(--border-soft);
}
.counter-row:last-child { border-bottom: 0; }
.counter-row .who { font-size: 15px; font-weight: 500; }
.counter-row .who small { display: block; font-size: 12.5px; color: var(--text-muted); font-weight: 400; }

.counter { display: flex; align-items: center; gap: 4px; flex: none; }
.counter button {
  width: 34px; height: 34px; border-radius: 50%;
  border: 1px solid var(--border); background: var(--surface);
  font-size: 19px; line-height: 1; color: var(--blue);
  cursor: pointer; display: grid; place-items: center;
  transition: background .16s ease, border-color .16s ease;
}
.counter button:hover:not(:disabled) { background: var(--blue-soft); border-color: var(--blue); }
.counter button:disabled { color: #c2cbd8; cursor: not-allowed; }
.counter output { min-width: 26px; text-align: center; font-weight: 600; font-size: 16px; }

/* Cases à cocher */
.check {
  display: flex; align-items: flex-start; gap: 10px;
  cursor: pointer; font-size: 15px;
  padding: 4px 0;
}
.check input { width: 19px; height: 19px; margin: 2px 0 0; accent-color: var(--blue); flex: none; cursor: pointer; }

.config-foot {
  margin-top: 22px; padding-top: 18px;
  border-top: 1px solid var(--border-soft);
}
.config-foot .btn-wa { width: 100%; }
.config-foot .foot-hint { margin: 10px 0 0; text-align: center; font-size: 12.5px; color: var(--text-muted); }

/* Aperçu du message qui partira sur WhatsApp */
.msg-preview {
  margin-top: 18px;
  background: #e7f7ec;
  border: 1px solid #bde5c8;
  border-radius: 12px;
  padding: 13px 15px;
}
.msg-preview h3 {
  font-family: var(--font-body); font-size: 12px; font-weight: 700;
  letter-spacing: .07em; text-transform: uppercase;
  color: var(--whatsapp-dk); margin-bottom: 8px;
}
.msg-preview pre {
  margin: 0; font-family: var(--font-body); font-size: 14px; line-height: 1.55;
  color: #143424;
  /* Un <pre> ne retourne jamais seul à la ligne par défaut (white-space:
     pre) : une ligne composée un peu longue (route, dates) débordait donc
     du cadre sur mobile, cassant la mise en page. « pre-wrap » garde les
     retours à la ligne voulus par le gabarit tout en repliant les lignes
     trop longues ; « break-word » est un filet en plus si un seul mot
     (aucun espace où couper) est encore trop large pour la largeur.       */
  white-space: pre-wrap;
  overflow-wrap: break-word;
  word-break: break-word;
}

/* ------------------------------------------------- Promos (page Vols) ---- */

.promo-strip { margin-top: 26px; }
.promo-strip > h2 { font-size: 21px; margin-bottom: 4px; }
.promo-strip > p.lede { margin: 0 0 16px; color: var(--text-muted); font-size: 14.5px; }

.promo-grid {
  display: grid; gap: 12px;
  grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  list-style: none; margin: 0; padding: 0;
}
.promo-card {
  width: 100%; text-align: start;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 16px;
  cursor: pointer;
  box-shadow: var(--shadow-sm);
  transition: transform .16s ease, box-shadow .16s ease, border-color .16s ease;
}
.promo-card:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-md);
  border-color: var(--blue);
}
.promo-card .route {
  font-size: 16px; font-weight: 600; margin-bottom: 2px;
  display: flex; align-items: center; gap: 7px; flex-wrap: wrap;
}
.promo-card .route .arrow { color: var(--blue); }
.promo-card .when { font-size: 13px; color: var(--text-muted); margin: 0 0 10px; }
.promo-card .price { font-family: var(--font-title); font-size: 22px; font-weight: 700; color: var(--blue); }
.promo-card .price small { font-family: var(--font-body); font-size: 12.5px; font-weight: 500; color: var(--text-muted); }
.promo-card .updated { font-size: 11.5px; color: var(--text-muted); margin: 6px 0 0; }

/* -------------------------------------------------------- Page simple --- */
/* Utilisée par « Permis international » : une seule offre, pas de liste.   */

.simple-page { padding: 26px 0 0; }
.simple-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  padding: 24px 20px;
  max-width: 680px;
}
.simple-card .detail-block:first-child { margin-top: 0; }
.simple-card .btn-wa { width: 100%; margin-top: 20px; }

/* ==========================================================================
   VARIANTE « AFFICHE » — voyages organisés
   ==========================================================================
   Une présentation à part, activée par « layout: 'poster' » dans le fichier
   de données (voir js/catalog.js). Les autres sections (visa, omra, e-SIM,
   assurance) gardent la liste compacte : là, c'est le TEXTE qui compte
   (pièces à fournir, délais). Ici, c'est la DESTINATION qui donne envie —
   d'où la carte verticale à photo, et la fiche à carrousel.

   Le drapeau n'est plus une vignette : il redevient ce qu'il doit être ici,
   une petite signature dans un coin de la photo. Sur une page de voyages,
   la photo du lieu porte l'envie, pas le drapeau du pays.
   ========================================================================== */

.catalog.is-poster {
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 20px;
  margin-top: 22px;
}

.poster-card {
  width: 100%; text-align: start; padding: 0;
  display: flex; flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  overflow: hidden;
  cursor: pointer;
  box-shadow: var(--shadow-sm);
  transition: transform .18s ease, box-shadow .18s ease, border-color .18s ease;
}
.poster-card:hover {
  transform: translateY(-3px);
  box-shadow: var(--shadow-md);
  border-color: var(--blue);
}
.poster-card:focus-visible { outline: 3px solid var(--blue); outline-offset: 2px; }

/* Format portrait : c'est ce qui distingue ces cartes des listes des
   autres sections, et ce qui laisse la photo respirer.                    */
.poster-media {
  position: relative;
  aspect-ratio: 4 / 5;
  background: linear-gradient(160deg, var(--blue-soft) 0%, var(--blue-tint) 100%);
  overflow: hidden;
}
/* Cibler « .poster-photo », pas « .poster-media img » : la signature de
   drapeau est elle aussi une <img> dans cette boîte, et un sélecteur trop
   large l'étirait sur toute la carte — exactement ce qu'on veut éviter.  */
.poster-photo {
  width: 100%; height: 100%; object-fit: cover; display: block;
  transition: transform .4s ease;
}
.poster-card:hover .poster-photo { transform: scale(1.04); }

/* Sur un écran étroit la grille tombe à une colonne : une photo 4:5 y
   ferait un pavé presque plein écran par voyage, et six voyages
   deviendraient interminables à faire défiler. On aplatit donc la photo
   plutôt que de rétrécir les cartes jusqu'à les rendre illisibles.       */
@media (max-width: 560px) {
  .catalog.is-poster .poster-media { aspect-ratio: 4 / 3; }
}

/* Sans photo, surtout pas d'image cassée ni de faux visuel : un aplat de
   marque qui annonce honnêtement ce qui manque.                          */
.poster-empty {
  position: absolute; inset: 0;
  display: grid; place-content: center; gap: 8px; justify-items: center;
  color: var(--blue-deep); text-align: center; padding: 16px;
}
.poster-empty svg { width: 30px; height: 30px; stroke: currentColor; fill: none; stroke-width: 1.4; opacity: .5; }
.poster-empty span { font-size: 12.5px; font-weight: 600; opacity: .75; }

/* La signature : petit drapeau posé dans un coin, jamais au centre. */
.flag-sign {
  position: absolute; inset-inline-start: 12px; bottom: 12px;
  width: 26px; height: 19.5px;               /* 4:3, comme les fichiers source */
  border-radius: 4px; object-fit: cover;
  box-shadow: 0 0 0 1.5px rgba(255, 255, 255, .9), 0 2px 6px rgba(9, 30, 49, .3);
}

.poster-body { padding: 14px 16px 16px; display: flex; flex-direction: column; gap: 6px; flex: 1; }
.poster-body h3 {
  font-family: var(--font-title);
  font-size: 18px; font-weight: 700; margin: 0;
}
.poster-body p {
  margin: 0; font-size: 13.5px; color: var(--text-muted); line-height: 1.45;
}
/* Le prix, quand il existe, est toujours le dernier élément du corps — on
   l'ancre donc en bas et les tags juste au-dessus. S'il n'y a pas de prix,
   ce sont les tags (ou rien) qui héritent de cet ancrage.                 */
.poster-body .item-tags { padding-top: 8px; }
.poster-body .item-tags:last-child { margin-top: auto; }

.poster-badges { position: absolute; top: 12px; inset-inline-start: 12px; z-index: 1; display: flex; flex-direction: column; gap: 6px; }
.poster-badge {
  display: inline-block; padding: 4px 10px; border-radius: 999px;
  font-size: 11px; font-weight: 700; letter-spacing: .02em;
  background: rgba(255, 255, 255, .92); color: var(--text);
  box-shadow: 0 1px 4px rgba(9, 30, 49, .18);
}
/* Texte blanc choisi par le client malgré un contraste mesuré (≈ 2,8:1,
   sous les 4,5:1 recommandés) : accepté sciemment pour ce badge, court et
   décoratif — voir la note sur le token --salmon dans styles.css.          */
.poster-badge.is-promo { background: var(--salmon); color: #fff; }
.poster-badge.is-soon  { background: var(--navy); color: #fff; }
/* « Combiné » : information de nature, pas argument commercial — il prend
   donc le bleu de marque, et laisse le saumon aux signaux commerciaux.
   Contraste vérifié : blanc sur --blue ≈ 6,9:1, au-dessus des 4,5:1.      */
.poster-badge.is-combo { background: var(--blue); color: #fff; }
/* « Événement » : violet dédié, hors charte — voir le token --event dans
   styles.css pour le pourquoi. Blanc dessus, 7,1:1 : le badge coloré le
   plus lisible du lot.                                                   */
.poster-badge.is-event { background: var(--event); color: #fff; }
/* « Places limitées » : même saumon que « Meilleur prix ». Les deux disent
   la même chose au visiteur — « ne passe pas à côté » — et aucune fiche ne
   porte les deux à la fois, donc pas d'ambiguïté sur une même carte.      */
.poster-badge.is-limited { background: var(--salmon); color: #fff; }

/* Durée et point de départ : les deux repères qu'on cherche juste après
   la photo et le prix. Facultatifs — voir « RÈGLE COMMUNE » dans
   js/catalog.js : absents des données, ils ne s'affichent simplement pas. */
.poster-meta, .voy-meta { display: flex; flex-wrap: wrap; gap: 5px 14px; }
.voy-meta { margin-top: 6px; }
.poster-meta .pm, .voy-meta .pm { display: inline-flex; align-items: center; gap: 5px; font-size: 12.5px; color: var(--text-muted); }
.poster-meta .pm svg, .voy-meta .pm svg { width: 14px; height: 14px; stroke: var(--blue); fill: none; stroke-width: 2; flex: none; }

/* Compagnie aérienne (fiche détaillée uniquement, voir js/catalog.js
   airlineHtml) — même registre visuel que « .voy-meta » juste au-dessus
   (petit texte gris) plutôt qu'un traitement à part, pour que ça reste un
   repère secondaire et non un élément qui rivalise avec le prix.          */
.voy-airline {
  display: flex; align-items: center; gap: 7px; margin: 8px 0 0;
  font-size: 12.5px; color: var(--text-muted);
}
.voy-airline img { height: 15px; width: auto; flex: none; }

.poster-price, .voy-price {
  display: flex; align-items: baseline; flex-wrap: wrap; gap: 2px 6px;
}
.poster-price { margin-top: auto; padding-top: 8px; }
.voy-price { margin: 2px 0 0; }
/* « À partir de » et le montant ne doivent jamais se couper au milieu —
   un prix éclaté sur plusieurs lignes (« 269 / 000 / DZD ») est illisible.
   Seul « .pp-per », une phrase plus longue, peut passer à la ligne
   suivante ENTIÈREMENT (flex-wrap ci-dessus), pas mot par mot.            */
.poster-price .pp-from, .voy-price .pp-from { font-size: 12px; color: var(--text-muted); white-space: nowrap; }
.poster-price .pp-amount, .voy-price .pp-amount {
  font-family: var(--font-title); font-weight: 700; color: var(--blue);
  white-space: nowrap;
}
.poster-price .pp-amount { font-size: 17px; }
.voy-price .pp-amount { font-size: 22px; }
.poster-price .pp-per, .voy-price .pp-per { font-size: 12px; color: var(--text-muted); }
/* Prix promo : ancien prix barré et grisé, juste avant le nouveau (voir
   js/catalog.js → priceHtml(), champ « price.old »).                     */
.poster-price .pp-old, .voy-price .pp-old {
  font-family: var(--font-title); color: var(--text-muted);
  text-decoration: line-through; text-decoration-thickness: 1px;
  white-space: nowrap;
}
.poster-price .pp-old { font-size: 13px; }
.voy-price .pp-old { font-size: 16px; }

/* ------------------------------------------- Fiche : fond flouté -------- */
/* Demande explicite du client : flouter la page derrière plutôt que la
   teinter en bleu. Le voile reste très léger — c'est le flou qui isole la
   fiche, pas la couleur. backdrop-filter n'est pas universel : sans lui,
   le voile seul suffit à faire reculer l'arrière-plan.                    */

.overlay.is-poster .overlay-backdrop {
  background: rgba(9, 30, 49, .22);
  -webkit-backdrop-filter: blur(14px) saturate(115%);
  backdrop-filter: blur(14px) saturate(115%);
}

.overlay-panel.is-poster { max-height: 94vh; }
@media (min-width: 760px) {
  .overlay-panel.is-poster { width: min(760px, calc(100vw - 48px)); max-height: 88vh; }
}

/* La croix flotte au-dessus du contenu : la fiche n'a plus d'en-tête à
   part, le titre fait partie du corps qui défile (comme la référence).
   « right » dégage volontairement la largeur d'une scrollbar native
   (~15-17px selon le navigateur) : à 14px, la croix chevauchait la barre
   de défilement de .overlay-body dès que la fiche dépassait la hauteur
   visible — un bug visuel repéré en usage réel, pas en théorie.           */
.overlay-panel.is-poster .overlay-controls {
  position: absolute; top: 14px; inset-inline-end: 28px; z-index: 3;
  margin-inline-start: 0;
}
.overlay-panel.is-poster .overlay-close,
.overlay-panel.is-poster .overlay-share {
  width: 40px; height: 40px;
  background: var(--surface);
  box-shadow: var(--shadow-sm);
}

.voy-head { margin-bottom: 18px; }
/* Seul le titre laisse la place aux 3 boutons (brochure, partager, fermer) : le reste
   du texte prend toute la largeur, sinon il est comprimé sur téléphone. */
.voy-head h2 { padding-inline-end: 152px; }
.voy-head h2 { font-size: clamp(24px, 5vw, 32px); margin: 0 0 10px; }
.voy-head .voy-rule {
  display: block; width: 46px; height: 3px; border-radius: 2px;
  background: var(--blue); margin-bottom: 14px;
}
.voy-head .item-tags { margin: 0 0 14px; }
.voy-head .voy-intro { margin: 0 0 6px; font-size: 15.5px; line-height: 1.6; color: var(--text); }
.voy-head .voy-intro:last-child { margin-bottom: 0; }

/* Sur fond blanc, la pastille translucide des cartes (faite pour une
   photo) devient illisible : un fond plein, dans les mêmes teintes que
   les tags déjà utilisés dans la fiche.                                  */
.voy-badges { display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 12px; }
.voy-badges .poster-badge { background: var(--blue-soft); color: var(--blue-deep); box-shadow: none; }
.voy-badges .poster-badge.is-promo   { background: var(--salmon); color: #fff; }
.voy-badges .poster-badge.is-soon    { background: var(--navy); color: #fff; }
.voy-badges .poster-badge.is-combo   { background: var(--blue); color: #fff; }
.voy-badges .poster-badge.is-event   { background: var(--event); color: #fff; }
.voy-badges .poster-badge.is-limited { background: var(--salmon); color: #fff; }

/* ------------------------------------------------ Fiche : carrousel ----- */
/* Navigation MANUELLE uniquement — glissement du doigt, flèches, points,
   clavier. Aucun défilement automatique : règle du projet, le client l'a
   refusé.

   Défilement NATIF (.voy-frame en overflow-x + scroll-snap), pas un
   glissement piloté en JS : un geste du doigt fonctionne alors tout seul,
   sans code à écrire pour ça. Avant, seules les flèches faisaient avancer
   la piste (transform: translateX en JS) — contre-intuitif au doigt sur
   téléphone, où glisser est le geste attendu. Voir moveGallery() dans
   js/catalog.js pour la partie JS (flèches/points restent, en confort
   souris/clavier, mais ne sont plus la seule façon d'avancer).            */

.voy-gallery { position: relative; margin: 0 0 22px; }
/* Ancre de positionnement des flèches et du drapeau (voir js/catalog.js,
   galleryHtml) : leur conteneur DOIT être ce wrapper, pas « .voy-frame »
   lui-même — ce dernier défile horizontalement, et un descendant absolu
   D'UN conteneur qui défile défile avec lui plutôt que de rester fixe à
   l'écran. « .voy-viewport » n'entoure QUE la photo (jamais les points en
   dessous), pour que le centrage vertical « top: 50% » des flèches reste
   calé sur la photo.                                                     */
.voy-viewport { position: relative; }
.voy-frame {
  position: relative;
  aspect-ratio: 3 / 2;
  border-radius: var(--radius);
  overflow-x: auto; overflow-y: hidden;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;                 /* Firefox */
  background: linear-gradient(160deg, var(--blue-soft) 0%, var(--blue-tint) 100%);
}
.voy-frame::-webkit-scrollbar { display: none; }   /* Chrome, Safari, Edge */
.voy-track { display: flex; height: 100%; }
.voy-track img { width: 100%; height: 100%; flex: none; object-fit: cover; display: block; scroll-snap-align: start; }

.voy-nav {
  position: absolute; top: 50%; transform: translateY(-50%);
  width: 40px; height: 40px; border-radius: 50%;
  border: 0; background: rgba(255, 255, 255, .94);
  box-shadow: 0 2px 10px rgba(9, 30, 49, .22);
  display: grid; place-items: center; cursor: pointer;
  transition: background .16s ease;
}
.voy-nav:hover { background: #fff; }
.voy-nav[hidden] { display: none; }
.voy-nav.prev { inset-inline-start: 12px; }
.voy-nav.next { inset-inline-end: 12px; }
.voy-nav svg { width: 18px; height: 18px; stroke: var(--text); fill: none; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; }

.voy-dots { display: flex; justify-content: center; gap: 6px; margin-top: 12px; }
.voy-dots button {
  width: 8px; height: 8px; padding: 0; border: 0; border-radius: 999px;
  background: var(--border); cursor: pointer;
  transition: width .2s ease, background .2s ease;
}
.voy-dots button[aria-current='true'] { width: 22px; background: var(--blue); }

/* ------------------------------------- Fiche : informations pratiques --- */
/* Même donnée que « detail-pairs » ailleurs, présentée en grille plutôt
   qu'en lignes étiquette/valeur : plus lisible quand il y a cinq ou six
   informations courtes (durée, départ, taille du groupe…).                */

.detail-pairs.is-grid {
  display: grid; gap: 18px 24px;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  background: var(--bg);
  border-radius: var(--radius);
  padding: 18px 20px;
}
.detail-pairs.is-grid > div { display: block; background: none; padding: 0; }
.detail-pairs.is-grid dt {
  font-size: 11.5px; font-weight: 700; letter-spacing: .07em;
  text-transform: uppercase; color: var(--text-muted); margin-bottom: 5px;
}
.detail-pairs.is-grid dd {
  margin: 0; text-align: start; font-weight: 500; font-size: 14.5px; line-height: 1.5;
}

/* ------------------------------------------------- Fiche : programme --- */
/* Frise verticale, un jour par ligne — le contenu qu'on attend le plus
   d'un voyage organisé et qui manquait jusqu'ici : QUE fait-on chaque
   jour, pas seulement « ce qui est compris ».                             */

.itinerary { list-style: none; margin: 0; padding: 0; }
.itinerary li {
  position: relative;
  padding-block: 0 20px; padding-inline: 26px 0;
  border-inline-start: 2px solid var(--border);
}
.itinerary li:last-child { padding-bottom: 0; border-left-color: transparent; }
.itinerary li::before {
  content: ''; position: absolute; inset-inline-start: -5px; top: 2px;
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--blue); box-shadow: 0 0 0 3px var(--surface);
}
.itin-day {
  display: block; font-size: 11.5px; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--blue); margin-bottom: 3px;
}
.itinerary h4 { margin: 0 0 3px; font-size: 15px; font-weight: 600; }
.itinerary p { margin: 0; font-size: 14px; color: var(--text-muted); line-height: 1.55; }

/* --------------------------------------------- Fiche : dates de départ - */
/* Une ligne cliquable par départ ouvert : le clic n'envoie rien, il
   précise seulement le message du bouton WhatsApp du bas — voir
   pickDeparture() dans js/catalog.js. Un départ complet n'est pas un
   bouton, juste une ligne d'information.                                  */

.departures { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.dep-row {
  width: 100%; display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 10px 14px; border-radius: 10px;
  border: 1px solid var(--border); background: var(--surface);
  font: inherit; text-align: start; cursor: pointer;
  transition: border-color .16s ease, background .16s ease;
}
.dep-row.is-off { cursor: default; opacity: .6; }
button.dep-row:hover { border-color: var(--blue); }
button.dep-row[aria-pressed='true'] {
  border-color: var(--blue); background: var(--blue-tint);
}
.dep-date { font-weight: 600; font-size: 14.5px; }
.dep-price { font-size: 13.5px; color: var(--text-muted); }
.dep-status {
  margin-inline-start: auto; font-size: 11.5px; font-weight: 700;
  padding: 3px 9px; border-radius: 999px; white-space: nowrap;
}
.dep-status.is-open    { background: #e7f7ef; color: #0d7a45; }
.dep-status.is-few     { background: var(--warn-bg); color: var(--warn-text); }
.dep-status.is-full    { background: var(--border-soft); color: var(--text-muted); }
.dep-status.is-request { background: var(--blue-soft); color: var(--blue-deep); }

/* ------------------------------- Fiche : catégories d'hôtel (accordéon) - */
/* Nom, étoiles et tarif d'appel toujours visibles ; le détail (bébé,
   enfant, supplément single) replié dessous. Cliquer une ligne la déplie
   ET la sélectionne pour le message WhatsApp — voir pickHotel() dans
   js/catalog.js. Mêmes tokens visuels que .dep-row, pour que les deux
   accordéons de la fiche (dates, hôtels) se lisent comme un seul système. */

.hotel-tiers { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.hotel-row {
  width: 100%; display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 12px 14px; border-radius: 10px;
  border: 1px solid var(--border); background: var(--surface);
  font: inherit; text-align: start; cursor: pointer;
  transition: border-color .16s ease, background .16s ease;
}
.hotel-row:hover { border-color: var(--blue); }
.hotel-row[aria-pressed='true'] { border-color: var(--blue); background: var(--blue-tint); }
.hotel-name { font-weight: 600; font-size: 14.5px; }
.hotel-stars { margin-inline-start: 6px; color: var(--blue); letter-spacing: 1px; }
.hotel-price {
  margin-inline-start: auto; text-align: end; font-weight: 700; font-size: 14.5px; color: var(--blue);
}
.hotel-price-per { display: block; font-weight: 500; font-size: 11.5px; color: var(--text-muted); }
.hotel-chevron {
  flex: none; width: 16px; height: 16px; margin-inline-start: 2px;
  stroke: var(--text-muted); fill: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round;
  transition: transform .16s ease;
}
.hotel-row[aria-expanded='true'] .hotel-chevron { transform: rotate(90deg); }
.hotel-panel {
  margin: 6px 2px 0; padding: 12px 14px;
  border: 1px solid var(--border-soft); border-radius: 10px; background: var(--bg);
}

/* Vignettes de l'hôtel — voir « photos » dans le bloc 'hotels' en tête de
   js/catalog.js.

   DEUX COMPORTEMENTS, et c'est délibéré :

   · Sur MOBILE, bande qui défile à l'horizontale. Défilement natif +
     scroll-snap, comme le carrousel des fiches : le glissement du doigt
     fonctionne sans une ligne de JS. « proximity » plutôt que
     « mandatory » — les vignettes sont petites et plusieurs tiennent à
     l'écran, forcer l'alignement sur chacune rendrait le geste collant.
     On garde une seule rangée : la règle du projet est que la fiche reste
     courte sur téléphone (voir la sélection exclusive des hôtels).

   · Sur PC, GRILLE. Une bande qui défile y était un piège : la barre est
     masquée, la molette verticale ne la déplace pas et rien n'est
     focusable au clavier — la souris seule ne pouvait pas atteindre les
     dernières photos (41 px hors champ, mesurés avec 4 vignettes). La
     grille supprime le problème au lieu de lui ajouter des flèches :
     tout est visible d'un coup, et ça tient avec 4 comme avec 10 photos.
     « auto-fill » et non « auto-fit » : la dernière rangée garde la même
     largeur de colonne que les autres au lieu de s'étirer.               */
.hotel-photos {
  display: flex; gap: 8px;
  list-style: none; margin: 12px 0 0; padding: 0;
  overflow-x: auto;
  scroll-snap-type: x proximity;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;                 /* Firefox */
}
.hotel-photos::-webkit-scrollbar { display: none; }   /* Chrome, Safari, Edge */
.hotel-photos li { flex: none; scroll-snap-align: start; }
/* La vignette est un bouton (agrandir est une action) : on lui retire son
   apparence de bouton, on garde son focus visible au clavier.            */
.hotel-photo {
  display: block; padding: 0; border: 0; background: none;
  cursor: zoom-in; border-radius: 10px; line-height: 0;
}
.hotel-photo:focus-visible {
  outline: 2px solid var(--blue); outline-offset: 2px;
}
.hotel-photos img {
  display: block; width: 172px; aspect-ratio: 3 / 2;
  object-fit: cover; border-radius: 10px;
  background: var(--border-soft);        /* aplat le temps du chargement */
  transition: transform .16s ease;
}
.hotel-photo:hover img { transform: scale(1.03); }

@media (min-width: 620px) {
  .hotel-photos {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    overflow-x: visible;
  }
  .hotel-photos img { width: 100%; }
}

/* ------------------------------------------- Photo agrandie (zoom) ------ */
/* Posée sur <body>, au-dessus de la fiche (z-index 120) — voir openZoom()
   dans js/catalog.js pour la raison : « .overlay-panel » est en
   overflow:hidden, une visionneuse à l'intérieur y serait rognée.        */
.photo-zoom {
  position: fixed; inset: 0; z-index: 140;
  display: none; place-items: center;
  padding: 24px;
  background: rgba(9, 30, 49, .88);
  cursor: zoom-out;
}
.photo-zoom.is-open { display: grid; animation: fade-in .18s ease; }
.photo-zoom img {
  max-width: 100%; max-height: 100%;
  border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
}
.photo-zoom-close {
  position: absolute; top: 16px; inset-inline-end: 16px;
  width: 42px; height: 42px; border-radius: 50%;
  border: 0; background: rgba(255, 255, 255, .92);
  display: grid; place-items: center; cursor: pointer;
}
.photo-zoom-close svg {
  width: 18px; height: 18px;
  stroke: var(--text); fill: none; stroke-width: 2.2; stroke-linecap: round;
}
/* Flèches précédent / suivant de la photo agrandie. Sur les côtés, centrées
   verticalement ; propriétés logiques : « précédent » est à droite en arabe.
   Les photos qui restent au-dessus de 640 px de large laissent la place aux
   flèches (padding latéral), sur téléphone elles se posent sur l'image.   */
.photo-zoom-nav {
  position: absolute; top: 50%; transform: translateY(-50%);
  width: 46px; height: 46px; border-radius: 50%;
  border: 0; background: rgba(255, 255, 255, .92);
  box-shadow: 0 2px 12px rgba(9, 30, 49, .35);
  display: grid; place-items: center; cursor: pointer; z-index: 2;
  transition: background .16s ease;
}
.photo-zoom-nav:hover { background: #fff; }
.photo-zoom-nav[hidden] { display: none; }
.photo-zoom-nav.prev { inset-inline-start: 16px; }
.photo-zoom-nav.next { inset-inline-end: 16px; }
.photo-zoom-nav svg {
  width: 20px; height: 20px;
  stroke: var(--text); fill: none; stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round;
}
.photo-zoom-nav:focus-visible, .photo-zoom-close:focus-visible { outline: 2px solid #fff; outline-offset: 3px; }
/* « 2 / 4 » en bas, centré. */
.photo-zoom-count {
  position: absolute; bottom: 18px; inset-inline: 0; text-align: center;
  color: #fff; font-size: 13px; font-weight: 600; letter-spacing: .06em;
  text-shadow: 0 1px 6px rgba(9, 30, 49, .8);
  pointer-events: none;
}
.photo-zoom-count:empty { display: none; }
@media (min-width: 720px) {
  .photo-zoom { padding-inline: 84px; }
}

/* --------------------------------------------- Présentation « vol » ----- */
/* charter/ uniquement (layout: 'flight' dans data/charter.js) — trajet en
   ligne pointillée avec avion au centre, inspiré d'une maquette fournie
   par le client. Une seule colonne, plus large que la grille par défaut :
   le trajet a besoin de place pour respirer des deux côtés.              */
.catalog.is-flight {
  grid-template-columns: 1fr;
  max-width: 640px; margin: 22px auto 0;
  gap: 16px;
}

.flight-card {
  position: relative;
  width: 100%; text-align: start; padding: 20px 20px 16px;
  display: flex; flex-direction: column; gap: 4px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  cursor: pointer;
  box-shadow: var(--shadow-sm);
  transition: transform .16s ease, box-shadow .16s ease, border-color .16s ease;
}
.flight-card:hover { transform: translateY(-2px); box-shadow: var(--shadow-md); border-color: var(--blue); }
.flight-card:focus-visible { outline: 3px solid var(--blue); outline-offset: 2px; }

.flight-airline { position: absolute; top: 18px; inset-inline-end: 20px; height: 22px; width: auto; max-width: 96px; object-fit: contain; }
/* Pastille (ex. « Promo ») : coin opposé au logo de la compagnie, même
   logique que .poster-badges sur les cartes voyages — voir js/catalog.js
   → flightCardHtml(). Fond plein (pas de photo à assombrir en dessous),
   même traitement que .voy-badges.                                       */
.flight-badges { position: absolute; top: 18px; inset-inline-start: 20px; z-index: 1; display: flex; flex-direction: column; gap: 6px; }
.flight-badges .poster-badge { background: var(--blue-soft); color: var(--blue-deep); box-shadow: none; }
.flight-badges .poster-badge.is-promo   { background: var(--salmon); color: #fff; }
.flight-badges .poster-badge.is-soon    { background: var(--navy); color: #fff; }
.flight-badges .poster-badge.is-combo   { background: var(--blue); color: #fff; }
.flight-badges .poster-badge.is-event   { background: var(--event); color: #fff; }
.flight-badges .poster-badge.is-limited { background: var(--salmon); color: #fff; }
.flight-airline.is-lg { position: static; height: 28px; max-width: 130px; margin-bottom: 10px; }

.flight-seg-label {
  display: inline-block; align-self: flex-start;
  font-size: 11px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  color: var(--blue-deep); background: var(--blue-soft);
  padding: 3px 10px; border-radius: 999px; margin-bottom: 12px;
}
.flight-seg { margin-bottom: 20px; }
.flight-seg + .flight-seg { padding-top: 18px; border-top: 1px solid var(--border-soft); }

.flight-route { display: flex; align-items: center; gap: 10px; }
.flight-point { display: flex; flex-direction: column; min-width: 0; }
.flight-point.is-end { align-items: flex-end; text-align: end; }
.flight-code {
  font-family: var(--font-body); font-size: 22px; font-weight: 800;
  color: var(--navy); letter-spacing: .01em; line-height: 1.1;
}
.flight-city { font-size: 12.5px; color: var(--text-muted); margin-top: 1px; }
.flight-time { font-size: 14px; font-weight: 700; color: var(--text); margin-top: 4px; }

.flight-path { flex: 1 1 auto; min-width: 60px; display: flex; flex-direction: column; align-items: center; gap: 4px; }
.flight-duration { font-size: 11.5px; color: var(--text-muted); }
.flight-line {
  position: relative; width: 100%; height: 22px;
  display: flex; align-items: center;
}
.flight-line::before {
  content: ''; position: absolute; inset-inline: 0; top: 50%; height: 1px;
  background-image: linear-gradient(to right, var(--border) 55%, transparent 0);
  background-size: 7px 1px; background-repeat: repeat-x;
}
.flight-line svg {
  position: relative; z-index: 1; margin: 0 auto;
  width: 20px; height: 20px; padding: 3px; border-radius: 50%;
  background: var(--surface); fill: var(--blue);
}
.flight-direct { font-size: 11px; font-weight: 700; color: #0d7a45; }

.flight-foot {
  display: flex; align-items: flex-end; justify-content: space-between; gap: 12px;
  margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--border-soft);
}
.flight-foot .item-tags { margin-top: 0; }
.flight-price { display: flex; align-items: baseline; gap: 6px; margin-inline-start: auto; flex: none; white-space: nowrap; }
.flight-price .pp-from { font-size: 12px; color: var(--text-muted); }
/* Sur mobile, tags + prix n'ont plus la place cote a cote sans que
   « À partir de » ne se coupe mot par mot — on les empile plutôt.        */
@media (max-width: 420px) {
  .flight-foot { flex-direction: column; align-items: flex-start; }
  .flight-price { margin-inline-start: 0; }
}
.flight-price .pp-amount { font-family: var(--font-title); font-weight: 700; color: var(--blue); font-size: 18px; }
.flight-price .pp-per { font-size: 12px; color: var(--text-muted); }
/* Prix promo : ancien prix barré et grisé, juste avant le nouveau (voir
   js/catalog.js → priceHtml(), champ « price.old »).                     */
.flight-price .pp-old {
  font-family: var(--font-title); color: var(--text-muted); font-size: 13px;
  text-decoration: line-through; text-decoration-thickness: 1px;
}

/* Fiche : même trajet, juste replacé sous le titre plutôt que dans la
   carte — pas de deuxième composant à maintenir.                        */
.flight-head h2 { font-size: 22px; margin: 0 0 4px; }
.flight-head .sub { margin: 0 0 10px; font-size: 14px; color: var(--text-muted); }
.flight-head .item-tags { margin-top: 8px; }
/* Pastille dans l'en-tête de la fiche vol : même traitement que .voy-badges
   (ligne en flux normal, pas de photo à assombrir en dessous).            */
.flight-head-badges { display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 8px; }
.flight-head-badges .poster-badge { background: var(--blue-soft); color: var(--blue-deep); box-shadow: none; }
.flight-head-badges .poster-badge.is-promo   { background: var(--salmon); color: #fff; }
.flight-head-badges .poster-badge.is-soon    { background: var(--navy); color: #fff; }
.flight-head-badges .poster-badge.is-combo   { background: var(--blue); color: #fff; }
.flight-head-badges .poster-badge.is-event   { background: var(--event); color: #fff; }
.flight-head-badges .poster-badge.is-limited { background: var(--salmon); color: #fff; }

/* ------------------------------------------------------- Lien de bloc --- */

.block-link {
  display: inline-flex; align-items: center; gap: 5px;
  margin-top: 10px; font-size: 13.5px; font-weight: 600; color: var(--blue);
  text-decoration: none;
}
.block-link:hover { text-decoration: underline; }
.block-link svg { width: 15px; height: 15px; stroke: currentColor; fill: none; stroke-width: 2.2; }

@media (prefers-reduced-motion: reduce) {
  .poster-card, .poster-photo, .flight-card { transition: none; }
}

.overlay-print { text-decoration: none; }
