/* Marseille Unlocked — feuille de style
   Design system : Meridian (voir DESIGN.md fourni par le client) — neutres clairs,
   accent bleu unique, DM Sans / DM Mono, cartes structurées, ombres discrètes.
   Composants de type "dashboard" (nav latérale, recherche) non repris : cette page
   est une vitrine publique, pas un back-office — seuls les tokens (couleurs,
   typo, rayons, ombres, espacements) et les patterns de carte/bouton sont réutilisés. */

/* Polices hébergées en local (voir /fonts) plutôt que chargées depuis Google
   Fonts : évite un domaine tiers bloquant sur chaque page, et surtout une
   dépendance à un CDN régulièrement ralenti ou inaccessible en Chine
   continentale — un vrai problème vu que le chinois fait partie des 5 langues
   du site. Mêmes fichiers, même rendu, juste servis depuis notre propre
   domaine. Sous-ensemble "latin" uniquement (glyphes fr/en/es/ar) : le
   "latin-ext" de Google (diacritiques d'Europe centrale) ne sert à aucune des
   5 langues du site. */
@font-face {
  font-family: 'DM Sans';
  font-style: normal;
  font-weight: 400 700; /* fichier variable : une seule source pour tout l'axe de graisse */
  font-display: swap;
  src: url('/fonts/dm-sans-variable.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'DM Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/fonts/dm-mono-400.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'DM Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/fonts/dm-mono-500.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
  /* Couleurs — Meridian */
  --bg: #f4f4f2;
  --surface: #ffffff;
  --surface-subtle: #f9f9f7;
  --border: #e5e5e1;
  --border-soft: #ededea;
  --primary: #2460e0;
  --primary-hover: #1e52c2;
  --primary-light: #edf1fe;
  --primary-dim: #c7d6fc;
  --text-primary: #181816;
  --text-secondary: #64645e;
  --text-tertiary: #a0a09a;
  --text-disabled: #c5c5bf;
  --green: #166534;
  --green-bg: #f0fdf4;
  --green-border: #bbf7d0;
  --red: #b91c1c;
  --red-bg: #fef2f2;
  --amber: #9a3412;
  --amber-bg: #fffbeb;
  --amber-border: #fde68a;
  --pink: #9d174d;
  --pink-bg: #fdf2f8;
  --pink-border: #fbcfe8;
  --coffee: #6f4518;
  --coffee-bg: #fbf0e4;
  --coffee-border: #e3c39c;

  /* Rayons — 3 paliers stricts, jamais mélangés sur un même type de conteneur */
  --radius-xs: 4px;   /* micro-badges (tags, statuts) */
  --radius-sm: 8px;   /* boutons, chips de filtre, inputs */
  --radius-md: 12px;  /* cartes et conteneurs */

  --shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
  --shadow-hover: 0 4px 14px rgba(0, 0, 0, 0.08);

  --font-sans: 'DM Sans', system-ui, sans-serif;
  --font-mono: 'DM Mono', 'SFMono-Regular', ui-monospace, monospace;

  --maxw: 1120px;
}

* { box-sizing: border-box; }

/* Empêche le rebond élastique (rubber-band) en haut/bas de page sur mobile
   (iOS Safari surtout) : sans ça, tirer la page au-delà du footer révèle un
   fond vide sous la barre CTA flottante fixe.
   ATTENTION — NE PAS remplacer par un modèle "body devient le conteneur de
   scroll" (overflow:hidden sur <html>, overflow-y:auto sur <body>) : testé
   et confirmé cassant le clic/tap sur les pins de la carte Leaflet
   (/spots.html) — le mousedown touche bien le pin mais le "click" final
   atterrit sur <html> au lieu du marqueur, tiroir de fiche jamais ouvert.
   Rester sur ce fix plus simple, appliqué à la racine du document normale. */
html, body {
  overscroll-behavior: none;
}

html {
  scroll-behavior: smooth;
  background: var(--bg);
}

body {
  margin: 0;
  font-family: var(--font-sans);
  font-size: 16px;
  color: var(--text-primary);
  background: var(--bg);
  line-height: 1.55;
  -webkit-text-size-adjust: 100%;
}

h1, h2, h3, h4 {
  font-family: var(--font-sans);
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.2;
  margin: 0 0 0.5em;
  color: var(--text-primary);
}

p { margin: 0 0 1em; color: var(--text-secondary); }

a { color: inherit; }

/* Anneau de focus visible et cohérent sur tous les éléments interactifs (clavier) */
a:focus-visible,
button:focus-visible,
.filter-chip:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
  border-radius: var(--radius-xs);
}

.wrap {
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 0 24px;
}

@media (max-width: 480px) {
  .wrap { padding: 0 16px; }
}

img { max-width: 100%; display: block; }

/* Toute table/carte large scrolle dans son propre conteneur plutôt que la page */
.hscroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }

/* ---------- Nav ---------- */

.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: rgba(255, 255, 255, 0.92);
  backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--border);
}

.nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 24px;
  max-width: var(--maxw);
  margin: 0 auto;
  min-height: 60px;
}

.logo {
  font-family: var(--font-sans);
  font-weight: 700;
  font-size: 1.15rem;
  letter-spacing: -0.02em;
  text-decoration: none;
  color: var(--text-primary);
}

.logo span { color: var(--primary); }

.nav-links {
  display: flex;
  align-items: center;
  gap: 4px;
  list-style: none;
  margin: 0;
  padding: 0;
}

.nav-links a:not(.btn) {
  display: inline-block;
  text-decoration: none;
  font-weight: 500;
  font-size: 0.92rem;
  color: var(--text-secondary);
  padding: 8px 12px;
  border-radius: var(--radius-sm);
  transition: background 0.15s ease, color 0.15s ease;
}

.nav-links a:not(.btn):hover { background: var(--surface-subtle); color: var(--text-primary); }
.nav-links a.active:not(.btn) { background: var(--primary-light); color: var(--primary); }

.nav-links li:last-child { margin-left: 8px; }

.nav-toggle {
  display: none;
  background: none;
  border: none;
  font-size: 1.5rem;
  line-height: 1;
  cursor: pointer;
  color: var(--text-primary);
  padding: 8px;
}

/* Le menu passe en hamburger dès 1024px : avec 5 liens + le CTA, la barre
   horizontale se met à retourner à la ligne dans chaque lien en dessous de
   ~980px (tablette, petit laptop) — mieux vaut basculer tôt que d'avoir une
   nav cassée sur cette zone intermédiaire. */
@media (max-width: 1024px) {
  .nav-links {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    background: var(--surface);
    flex-direction: column;
    align-items: stretch;
    padding: 12px 16px 20px;
    gap: 4px;
    border-bottom: 1px solid var(--border);
    box-shadow: var(--shadow-hover);
    display: none;
  }
  .nav-links.open { display: flex; }
  .nav-links li:last-child { margin-left: 0; margin-top: 8px; }
  .nav-links a:not(.btn) { padding: 12px; }
  /* Cible tactile confortable (≥44px) pour le CTA principal du menu mobile */
  .nav-links .btn { width: 100%; min-height: 44px; font-size: 0.92rem; padding: 12px 20px; }
  .nav-toggle { display: block; }
}

/* Filet de sécurité au-dessus de 1024px : le texte des liens ne doit jamais
   se remettre à la ligne dans la barre horizontale. */
.nav-links a:not(.btn) { white-space: nowrap; }

/* ---------- Sélecteur de langue ---------- */

.lang-switch { position: relative; margin-left: 4px; }

.lang-switch-btn {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  background: none;
  border: 1px solid var(--border);
  color: var(--text-secondary);
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: 0.82rem;
  padding: 7px 10px;
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease;
}
.lang-switch-btn:hover { background: var(--surface-subtle); color: var(--text-primary); }
.lang-switch-caret { font-size: 0.7em; opacity: 0.7; }

.lang-switch-menu {
  display: none;
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  min-width: 160px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-hover);
  padding: 6px;
  list-style: none;
  margin: 0;
  z-index: 60;
}
.lang-switch.open .lang-switch-menu { display: block; }

.lang-switch-option {
  display: block;
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  font-family: var(--font-sans);
  font-size: 0.88rem;
  color: var(--text-secondary);
  padding: 9px 10px;
  border-radius: var(--radius-xs);
  cursor: pointer;
}
.lang-switch-option:hover { background: var(--surface-subtle); color: var(--text-primary); }
.lang-switch-option.active { color: var(--primary); background: var(--primary-light); font-weight: 600; }

@media (max-width: 1024px) {
  .lang-switch { margin: 4px 0 0; }
  .lang-switch-btn { width: 100%; justify-content: space-between; padding: 12px; }
  .lang-switch-menu { position: static; box-shadow: none; border: none; margin-top: 4px; width: 100%; }
}

/* ---------- Buttons ---------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 11px 20px;
  min-height: 42px;
  border-radius: var(--radius-sm);
  font-weight: 600;
  font-size: 0.92rem;
  text-decoration: none;
  border: 1px solid transparent;
  cursor: pointer;
  transition: transform 0.1s ease, box-shadow 0.15s ease, background 0.15s ease, border-color 0.15s ease;
  font-family: var(--font-sans);
}

.btn:active { transform: scale(0.98); }
/* Sans ça, un .btn caché via l'attribut [hidden] (ex: #nearme-reset,
   #admin-spot-delete) resterait visible : display:inline-flex ci-dessus a la
   même spécificité que le display:none par défaut du navigateur pour
   [hidden], et notre feuille de style passe après — donc le nôtre l'emporte.
   Corrigé ici une bonne fois pour toutes pour n'importe quel .btn, pas
   juste ce cas précis. */
.btn[hidden] { display: none; }

.btn-primary {
  background: var(--primary);
  color: #ffffff;
}
.btn-primary:hover { background: var(--primary-hover); }

/* Visiteur déjà débloqué : le bouton nav passe au vert "acquis" plutôt que
   de continuer à ressembler à un appel à payer. Deux classes (spécificité
   supérieure à .btn-primary seul) pour ne pas dépendre de l'ordre dans la
   feuille de style. */
.nav-cta-unlocked.btn-primary { background: var(--green); }
.nav-cta-unlocked.btn-primary:hover { background: #14532d; }

.btn-ghost,
.btn-light {
  background: var(--surface);
  color: var(--text-secondary);
  border-color: var(--border);
}
.btn-ghost:hover,
.btn-light:hover { background: var(--surface-subtle); color: var(--text-primary); }

.btn[disabled] { opacity: 0.6; cursor: not-allowed; transform: none; }
.btn-sm { padding: 8px 14px; min-height: 34px; font-size: 0.82rem; }

/* ---------- Hero ---------- */

.hero {
  padding: 64px 0 48px;
  text-align: center;
}

@media (max-width: 620px) { .hero { padding: 40px 0 32px; } }

.eyebrow {
  display: inline-block;
  font-size: 0.76rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--primary);
  background: var(--primary-light);
  padding: 6px 12px;
  border-radius: var(--radius-xs);
  margin-bottom: 20px;
}

.hero h1 {
  font-size: clamp(1.9rem, 4.8vw, 3.1rem);
  letter-spacing: -0.03em;
  max-width: 780px;
  margin-left: auto;
  margin-right: auto;
}

.hero .lede {
  max-width: 560px;
  margin: 0 auto 32px;
  font-size: 1.05rem;
}

.hero-cta {
  display: flex;
  gap: 12px;
  justify-content: center;
  flex-wrap: wrap;
}

/* Boutons d'appel à l'action pleine largeur et empilés sur mobile, partout
   sur le site — cohérence demandée entre toutes les pages. */
.cta-center { text-align: center; }
@media (max-width: 560px) {
  .hero-cta { flex-direction: column; align-items: stretch; }
  .hero-cta .btn,
  .cta-center .btn,
  #cta-final .btn { width: 100%; }
}

/* ---------- Hero (page d'accueil) ---------- */

.hero-main { padding: 48px 0 60px; }
@media (max-width: 620px) { .hero-main { padding: 32px 0 40px; } }

.hero-main-grid {
  display: grid;
  grid-template-columns: 1.05fr 0.95fr;
  gap: 48px;
  align-items: center;
}
@media (max-width: 900px) {
  .hero-main-grid { grid-template-columns: 1fr; gap: 32px; }
}

.hero-copy h1 {
  text-align: left;
  margin-left: 0;
  margin-right: 0;
  max-width: none;
  font-size: clamp(2rem, 4.2vw, 2.9rem);
}
.hero-copy .lede { text-align: left; margin-left: 0; max-width: 480px; }
.hero-copy .hero-cta { justify-content: flex-start; }

@media (max-width: 900px) {
  .hero-copy h1, .hero-copy .lede { text-align: center; margin-left: auto; margin-right: auto; }
  .hero-copy .hero-cta { justify-content: center; }
}

/* Ce que contient le guide, listé juste avant son prix. Le prix, lui, reste
   dans le premier écran (voir .hero-price-row plus bas) : ce n'est pas de
   l'afficher tôt qui coûtait des ventes, c'est de l'afficher NU — il était
   posé directement sous l'accroche, premier chiffre de la page, sans rien à
   quoi se rattacher. Ces trois lignes donnent le contenu d'abord. */
.hero-value-list {
  list-style: none;
  padding: 0;
  margin: 18px 0 20px;
  display: flex;
  flex-direction: column;
  gap: 9px;
  max-width: 480px;
}
.hero-value-list li {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  font-size: 0.95rem;
  line-height: 1.45;
  color: var(--text-secondary);
}
.hero-value-list .icon { font-size: 1.05rem; line-height: 1.4; }
/* Sous 900px le reste du hero est centré, mais une liste centrée se lit mal
   (les débuts de ligne ne s'alignent plus) : on centre le bloc, pas le texte. */
@media (max-width: 900px) {
  .hero-value-list { margin-left: auto; margin-right: auto; }
}
html[dir="rtl"] .hero-value-list li { direction: rtl; }

/* Prix + garanties directement dans le hero : sur la version précédente, le
   prix n'apparaissait qu'après une dizaine de sections, largement après que
   la plupart des visiteurs aient quitté la page (21s de temps moyen mesuré
   en prod). Ici, il est visible dès le premier écran, juste au-dessus du
   bouton d'action — la boîte de prix détaillée plus bas (#guide) reste la
   version complète pour qui veut tous les arguments avant de se décider. */
.hero-price-row {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 6px 8px;
  margin-bottom: 14px;
}
.hero-price-row .price {
  font-family: var(--font-mono);
  font-size: 1.6rem;
  font-weight: 500;
  color: var(--primary);
  letter-spacing: -0.02em;
}
.hero-price-row .price-note {
  font-size: 0.86rem;
  color: var(--text-tertiary);
}
@media (max-width: 900px) { .hero-price-row { justify-content: center; } }

/* Garantie, seule ligne sous les boutons : c'est l'argument qui annule le
   risque à l'instant précis où l'on hésite à cliquer. Volontairement en
   texte simple et non en pastille colorée — la pastille juste en dessous
   (.hero-social-proof) en est une, et deux pastilles collées se neutralisent
   l'une l'autre au lieu de se renforcer. */
.hero-guarantee {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  margin: 14px 0 0;
  font-size: 0.86rem;
  font-weight: 500;
  color: var(--text-secondary);
}
.hero-guarantee .icon { font-size: 1rem; }
@media (max-width: 900px) { .hero-guarantee { margin-left: auto; margin-right: auto; } }
html[dir="rtl"] .hero-guarantee { direction: rtl; }

.hero-social-proof {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  margin: 14px 0 0;
  padding: 6px 12px;
  border-radius: 999px;
  background: var(--green-bg);
  border: 1px solid var(--green-border);
  color: var(--green);
  font-size: 0.85rem;
  font-weight: 500;
}
.hero-social-proof .icon { font-size: 0.95rem; }
.hero-social-proof strong { font-weight: 700; }
@media (max-width: 900px) { .hero-social-proof { margin-left: auto; margin-right: auto; } }
html[dir="rtl"] .hero-social-proof { direction: rtl; }

html[dir="rtl"] .hero-price-row { direction: rtl; }

.trust-row {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin-top: 32px;
}
@media (max-width: 900px) { .trust-row { align-items: center; } }

.trust-item {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 0.86rem;
  color: var(--text-secondary);
  font-weight: 500;
}
.trust-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  flex-shrink: 0;
  border-radius: 50%;
  background: var(--primary-light);
  font-size: 0.85rem;
}

.hero-photo { position: relative; max-width: 420px; margin: 0 auto; }
.hero-photo picture,
.hero-photo img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--radius-md);
  border: 1px solid var(--border);
  box-shadow: var(--shadow-hover);
}
.hero-photo-badge {
  position: absolute;
  left: 14px;
  bottom: 14px;
  right: 14px;
  background: rgba(255, 255, 255, 0.94);
  backdrop-filter: blur(6px);
  padding: 9px 14px;
  border-radius: var(--radius-sm);
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--text-primary);
  box-shadow: var(--shadow);
}

/* Le macaron "tampon" en haut à droite de la photo : dit d'emblée, avant
   même le H1, que c'est LE guide et combien d'adresses il contient — un
   nombre concret vend mieux qu'une formule vague type "100% authentique".
   Légèrement pivoté pour l'effet autocollant plutôt qu'étiquette plate. */
.hero-photo-stamp {
  position: absolute;
  top: -16px;
  right: -16px;
  width: 108px;
  height: 108px;
  border-radius: 50%;
  background: var(--primary);
  color: #fff;
  border: 3px solid var(--surface);
  box-shadow: var(--shadow-hover);
  transform: rotate(-9deg);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  line-height: 1.15;
  z-index: 1;
}
.hero-photo-stamp-label {
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  text-transform: uppercase;
}
.hero-photo-stamp-number {
  font-family: var(--font-mono);
  font-size: 2.1rem;
  font-weight: 700;
  margin: 1px 0;
}
.hero-photo-stamp-sub {
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  text-transform: uppercase;
}

@media (max-width: 480px) {
  .hero-photo-stamp { width: 88px; height: 88px; top: -10px; right: -10px; }
  .hero-photo-stamp-number { font-size: 1.7rem; }
  .hero-photo-stamp-label, .hero-photo-stamp-sub { font-size: 0.54rem; }
}

html[dir="rtl"] .hero-photo-stamp { right: auto; left: -16px; }
@media (max-width: 480px) { html[dir="rtl"] .hero-photo-stamp { left: -10px; } }

/* ---------- Témoignage vidéo (hero, les 5 langues) ----------
   Remplace l'ancien .hero-photo sur la home, dans toutes les langues : un
   vrai maquette de téléphone (bezel + encoche) plutôt qu'une simple vidéo en
   rectangle, pour qu'elle se lise immédiatement comme "quelqu'un a filmé un
   avis avec son téléphone" plutôt que comme une pub tournée en studio. Le
   macaron "tampon" (.hero-photo-stamp, mêmes classes que l'ancien hero photo)
   est repositionné ici sur le coin du téléphone plutôt que de la photo. */
.hero-video { max-width: 300px; margin: 0 auto; display: flex; flex-direction: column; align-items: center; gap: 12px; }

.hero-video-phone {
  position: relative;
  width: 100%;
  aspect-ratio: 768 / 1364;
  background: linear-gradient(160deg, #2c2c30, #09090b);
  border-radius: 38px;
  padding: 12px;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.1),
    0 24px 48px -16px rgba(0, 0, 0, 0.45),
    var(--shadow-hover);
}
.hero-video-screen {
  position: relative;
  width: 100%;
  height: 100%;
  border-radius: 26px;
  overflow: hidden;
  background: #000;
}
.hero-video-el { display: block; width: 100%; height: 100%; object-fit: cover; }

/* Encoche posée sur l'écran (pas dans la marge du bezel), comme sur un vrai
   téléphone : c'est ce détail qui vend l'illusion, pas juste des coins
   arrondis. */
.hero-video-notch {
  position: absolute;
  top: 12px;
  left: 50%;
  transform: translateX(-50%);
  width: 30%;
  height: 20px;
  background: #0a0a0b;
  border-radius: 0 0 14px 14px;
  z-index: 2;
}

/* Bouton son : même traitement "scrim sombre + icône blanche" que
   .spot-sheet-close (posé sur un contenu qui varie, jamais un fond uni). */
.hero-video-mute-btn {
  position: absolute;
  right: 22px;
  bottom: 24px;
  z-index: 3;
  width: 42px;
  height: 42px;
  border-radius: 50%;
  border: none;
  background: rgba(20, 20, 18, 0.55);
  backdrop-filter: blur(6px);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
  transition: background 0.15s ease, transform 0.15s ease;
  animation: hero-video-mute-pulse 2.2s ease-in-out 1s 3;
}
.hero-video-mute-btn:hover { background: rgba(20, 20, 18, 0.75); transform: scale(1.06); }
.hero-video-mute-btn svg { width: 19px; height: 19px; }
.hero-video-mute-btn .icon-unmuted { display: none; }
.hero-video-mute-btn[aria-pressed="true"] .icon-muted { display: none; }
.hero-video-mute-btn[aria-pressed="true"] .icon-unmuted { display: block; }
/* Pulse discret trois fois au chargement, pour signaler que le son est
   activable sans avoir à afficher un texte "cliquez ici" en permanence. */
@keyframes hero-video-mute-pulse {
  0%, 100% { box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3), 0 0 0 0 rgba(255, 255, 255, 0); }
  50% { box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3), 0 0 0 8px rgba(255, 255, 255, 0.18); }
}
@media (prefers-reduced-motion: reduce) { .hero-video-mute-btn { animation: none; } }

.hero-video-caption {
  display: inline-block;
  background: var(--surface);
  border: 1px solid var(--border);
  padding: 8px 16px;
  border-radius: 999px;
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--text-secondary);
  box-shadow: var(--shadow);
}

@media (max-width: 480px) {
  .hero-video { max-width: 240px; }
  .hero-video-mute-btn { width: 38px; height: 38px; right: 16px; bottom: 18px; }
}

/* ---------- Stat band (accueil) ---------- */

.stat-band {
  padding: 30px 0;
  background: var(--primary-light);
  border-top: 1px solid var(--primary-dim);
  border-bottom: 1px solid var(--primary-dim);
}

.stat-strip {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 56px;
  flex-wrap: wrap;
  text-align: center;
}

.stat-item strong {
  display: block;
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 2.1rem;
  letter-spacing: -0.02em;
  color: var(--primary);
  line-height: 1.1;
}
.stat-item span {
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--text-secondary);
}

@media (max-width: 480px) {
  .stat-strip { gap: 28px; }
  .stat-item strong { font-size: 1.6rem; }
  .stat-item span { font-size: 0.76rem; }
}

/* ---------- Value banner (accroche punchy avant "avant-goût") ---------- */

.value-banner {
  padding: 22px 0;
  background: var(--primary);
}

.value-banner p {
  margin: 0;
  text-align: center;
  color: #fff;
  font-weight: 700;
  font-size: clamp(1.05rem, 2.4vw, 1.35rem);
  line-height: 1.35;
  letter-spacing: -0.01em;
}

@media (max-width: 480px) {
  .value-banner { padding: 18px 0; }
}

/* ---------- Avis (témoignages de vrais utilisateurs) ---------- */

.review-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 16px;
}
@media (max-width: 760px) { .review-grid { grid-template-columns: 1fr; } }

.review-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 24px 22px;
  box-shadow: var(--shadow);
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.review-head {
  display: flex;
  align-items: center;
  gap: 12px;
}
.review-avatar {
  width: 56px;
  height: 56px;
  border-radius: 50%;
  object-fit: cover;
  flex-shrink: 0;
  border: 1px solid var(--border);
}
.review-stars {
  color: var(--amber);
  font-size: 0.88rem;
  letter-spacing: 2px;
  margin-top: 2px;
}
.review-quote {
  margin: 0;
  color: var(--text-primary);
  font-size: 0.95rem;
  line-height: 1.55;
  flex-grow: 1;
}
.review-author {
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--text-primary);
}
html[dir="rtl"] .review-stars { direction: ltr; text-align: right; }

/* ---------- Persona grid ("Pour qui ?") ---------- */

.persona-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 16px;
}
@media (max-width: 900px) { .persona-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 480px) { .persona-grid { grid-template-columns: 1fr; } }

.persona-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 22px 20px;
  box-shadow: var(--shadow);
}
.persona-card .icon {
  width: 42px;
  height: 42px;
  border-radius: 50%;
  background: var(--primary-light);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.25rem;
  margin-bottom: 14px;
}
.persona-card h3 { font-size: 0.98rem; margin-bottom: 6px; letter-spacing: -0.01em; }
.persona-card p { font-size: 0.86rem; margin: 0; }

/* ---------- Tips panel ("Avant d'y aller") ---------- */

.tips-panel {
  background: var(--amber-bg);
  border: 1px solid var(--amber-border);
  border-radius: var(--radius-md);
  padding: 32px;
}
@media (max-width: 480px) { .tips-panel { padding: 22px 20px; } }

.tips-panel .section-head h2 { color: var(--text-primary); }

/* ---------- Formulaire de contact ---------- */

.contact-form { max-width: 520px; margin: 0 auto; display: flex; flex-direction: column; gap: 16px; }
.form-row { display: flex; flex-direction: column; gap: 6px; }
.form-row label { font-size: 0.86rem; font-weight: 600; color: var(--text-primary); }
.form-row input,
.form-row textarea {
  font-family: inherit;
  font-size: 0.95rem;
  padding: 11px 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text-primary);
  resize: vertical;
}
.form-row input:focus,
.form-row textarea:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 3px var(--primary-light);
}
.contact-form .btn { align-self: flex-start; }
@media (max-width: 480px) { .contact-form .btn { width: 100%; } }

/* Honeypot anti-bot : invisible pour un humain, mais présent dans le DOM
   (contrairement à display:none, ignoré par certains bots peu sophistiqués).
   Technique "clip" plutôt que left:-9999px : cette dernière déborde de la
   page en RTL (left reste une propriété physique, elle ne s'inverse pas
   avec direction:rtl) et gonflait la largeur de scroll sur les pages arabes. */
.form-honeypot {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.form-status { font-size: 0.9rem; margin: 0; min-height: 1.2em; }
.form-status--success { color: var(--green); }
.form-status--error { color: var(--red); }

/* Bloc "nous contacter" intégré en bas de /spots.html et /planning.html */
.contact-block {
  background: var(--surface-subtle);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 36px 32px;
}
.contact-block .section-head { margin-bottom: 24px; }
@media (max-width: 480px) { .contact-block { padding: 26px 20px; } }

.tips-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 24px;
  margin-bottom: 26px;
}
@media (max-width: 760px) { .tips-grid { grid-template-columns: 1fr; gap: 20px; } }

.tip-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 42px;
  height: 42px;
  border-radius: 50%;
  background: var(--surface);
  border: 1px solid var(--amber-border);
  font-size: 1.2rem;
  margin-bottom: 12px;
}
.tip-item h3 { font-size: 0.96rem; margin-bottom: 6px; letter-spacing: -0.01em; }
.tip-item p { font-size: 0.86rem; color: var(--text-secondary); margin: 0; }

.tips-disclaimer {
  font-size: 0.82rem;
  color: var(--text-secondary);
  border-top: 1px solid var(--amber-border);
  padding-top: 18px;
  margin: 0;
  line-height: 1.6;
}

/* ---------- Astuces pratiques (/astuces.html) ---------- */

.tips-cat-heading {
  font-size: 1.3rem;
  margin: 44px 0 18px;
  letter-spacing: -0.01em;
}
.tips-cat-heading:first-child { margin-top: 0; }

.tip-card-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 16px;
}
@media (max-width: 760px) { .tip-card-grid { grid-template-columns: 1fr; } }

.tip-card {
  position: relative; /* ancre .tip-status-badge et .spot-lock-panel */
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow);
  padding: 22px 24px;
}
.tip-card-icon { display: block; font-size: 1.7rem; margin-bottom: 12px; }
.tip-card h3 { font-size: 1rem; margin-bottom: 8px; letter-spacing: -0.01em; }
.tip-card p { font-size: 0.88rem; color: var(--text-secondary); margin: 0 0 12px; }
.tip-card p:last-child { margin-bottom: 0; }

/* Détail chiffré (dates, quotas, horaires) : mis à part visuellement du
   reste du texte, pour qu'on le retrouve d'un coup d'œil sans tout relire. */
.tip-card-detail {
  background: var(--surface-subtle);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  font-size: 0.82rem;
  color: var(--text-primary);
  margin: 0 0 12px;
}

.tip-card-link {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 0.82rem;
  font-weight: 700;
  color: var(--primary);
  text-decoration: none;
}
.tip-card-link:hover { text-decoration: underline; }
/* La flèche reste le même glyphe "→", juste retournée visuellement en RTL —
   plus simple et plus sûr qu'un caractère "←" différent à traduire à part. */
html[dir="rtl"] .tip-card-link-arrow { display: inline-block; transform: scaleX(-1); }

/* ---------- Astuces verrouillées (aperçu gratuit de 2 astuces par catégorie)

   Même langage visuel que les adresses verrouillées de /spots.html : le corps
   de la carte passe derrière une vitre dépolie et un panneau CTA en verre
   dépoli vient se poser dessus. Les classes .spot-card-blur-body et
   .spot-lock-panel sont d'ailleurs réutilisées telles quelles (définies plus
   bas, section "Carte verrouillée façon vitre dépolie") — un seul design de
   cadenas sur tout le site, pas deux à garder en synchro.

   Différence avec les adresses : le texte de l'astuce n'est PAS envoyé au
   navigateur d'un visiteur non débloqué (voir functions/astuces.html.js), il
   n'y a donc rien de réel à flouter. Les barres .tip-lock-skeleton tiennent
   la place du texte absent, et le flou leur donne l'aspect d'un paragraphe
   qu'on devine sans pouvoir le lire. ---------- */

.tip-status-badge {
  position: absolute;
  top: 16px;
  inset-inline-end: 16px;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 0.68rem;
  font-weight: 700;
  padding: 3px 9px;
  border-radius: 999px;
  white-space: nowrap;
}
.tip-status-badge--free { background: var(--green-bg); color: var(--green); border: 1px solid var(--green-border); }
.tip-status-badge--locked { background: var(--amber-bg); color: var(--amber); border: 1px solid var(--amber-border); }

/* Réserve la place du panneau CTA posé en bas de carte (position:absolute,
   donc hors flux) : sans ce padding, il recouvrirait le bas du texte flouté
   au lieu de se poser en dessous. */
.tip-card.locked {
  overflow: hidden;
  min-height: 250px;
  padding-bottom: 150px;
}

.tip-lock-skeleton {
  display: block;
  height: 10px;
  border-radius: 999px;
  background: var(--border);
  margin: 0 0 10px;
}
.tip-lock-skeleton:nth-of-type(1) { width: 96%; }
.tip-lock-skeleton:nth-of-type(2) { width: 82%; }
.tip-lock-skeleton:nth-of-type(3) { width: 58%; }

/* Le bandeau de déblocage n'a plus lieu d'être une fois le guide payé :
   functions/astuces.html.js le masque déjà côté serveur, cette règle couvre
   le cas où la page est servie en statique (js/app.js pose .is-unlocked sur
   <body> après /api/unlock-status). */
body.is-unlocked #astuces-unlock-banner { display: none; }


/* ---------- FAQ ("Pourquoi payer ?") ---------- */

.faq-list {
  max-width: 680px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.faq-item {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 16px 20px;
  box-shadow: var(--shadow);
}

.faq-item summary {
  cursor: pointer;
  font-weight: 600;
  font-size: 0.96rem;
  color: var(--text-primary);
  letter-spacing: -0.01em;
  list-style: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary::after {
  content: '+';
  flex-shrink: 0;
  font-size: 1.2rem;
  color: var(--primary);
  font-weight: 400;
  transition: transform 0.15s ease;
}
.faq-item[open] summary::after { transform: rotate(45deg); }

.faq-item p { margin: 12px 0 0; font-size: 0.9rem; }

/* Variante compacte : mini-FAQ objections sur /debloquer.html, dans la
   colonne étroite (480px) de la boîte de prix plutôt qu'une section pleine
   largeur — mêmes .faq-item, juste un conteneur plus resserré. */
.faq-list--compact { max-width: none; margin: 24px 0 0; gap: 8px; }
.faq-list-heading { font-size: 0.82rem; font-weight: 600; color: var(--text-tertiary); text-align: center; margin: 0 0 4px; text-transform: uppercase; letter-spacing: 0.03em; }
.faq-list--compact .faq-item { padding: 12px 16px; }
.faq-list--compact .faq-item summary { font-size: 0.88rem; }
.faq-list--compact .faq-item p { font-size: 0.84rem; }
.faq-item a { color: var(--primary); text-decoration: underline; }

/* ---------- Story teaser (home) ---------- */

.story-teaser {
  display: grid;
  grid-template-columns: 0.85fr 1.15fr;
  gap: 40px;
  align-items: center;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow);
  padding: 20px;
}
@media (max-width: 760px) {
  .story-teaser { grid-template-columns: 1fr; padding: 20px; gap: 20px; }
}

.story-teaser-photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: var(--radius-sm);
  display: block;
  aspect-ratio: 4 / 3;
}

.story-teaser-copy .eyebrow { margin-bottom: 14px; }
.story-teaser-copy h2 { font-size: clamp(1.3rem, 2.4vw, 1.7rem); margin-bottom: 10px; }
.story-teaser-copy p { margin-bottom: 14px; }

.story-link {
  display: inline-block;
  font-weight: 600;
  font-size: 0.92rem;
  color: var(--primary);
  text-decoration: none;
}
.story-link:hover { color: var(--primary-hover); }

/* ---------- Story page ---------- */

.story-wrap { max-width: 720px; }

.story-hero-photo { position: relative; margin-bottom: 40px; }
.story-hero-photo img {
  width: 100%;
  height: auto;
  border-radius: var(--radius-md);
  border: 1px solid var(--border);
  box-shadow: var(--shadow-hover);
  display: block;
}

.story-body p { font-size: 1rem; line-height: 1.7; color: var(--text-secondary); }

.pull-quote {
  margin: 32px 0;
  padding: 4px 0 4px 20px;
  border-left: 3px solid var(--primary);
  font-family: var(--font-sans);
  font-size: 1.2rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--text-primary);
  font-style: normal;
}

.story-authors { margin: 40px 0 32px; }

/* ---------- Sections generic ---------- */

section { padding: 56px 0; }
@media (max-width: 620px) { section { padding: 40px 0; } }

.section-head { text-align: center; max-width: 620px; margin: 0 auto 40px; }
.section-head h2 { font-size: clamp(1.5rem, 3vw, 2rem); letter-spacing: -0.03em; }

.alt-bg { background: var(--surface); }

/* ---------- Authors ---------- */

.authors {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 20px;
  max-width: 760px;
  margin: 0 auto;
}

.author-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 24px;
  box-shadow: var(--shadow);
  text-align: center;
}

.avatar {
  width: 68px;
  height: 68px;
  border-radius: 50%;
  margin: 0 auto 14px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-sans);
  font-weight: 700;
  font-size: 1.3rem;
  color: #ffffff;
}

.avatar.cannelle { background: var(--primary); }
.avatar.josh { background: var(--text-primary); }

.author-card h3 { margin-bottom: 4px; }
.author-role { color: var(--primary); font-size: 0.82rem; font-weight: 600; margin-bottom: 10px; }

@media (max-width: 560px) { .authors { grid-template-columns: 1fr; } }

/* ---------- Feature grid ---------- */

.feature-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 16px;
}

.feature {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 20px;
  box-shadow: var(--shadow);
}

.feature .icon { font-size: 1.4rem; margin-bottom: 10px; }
.feature h3 { font-size: 0.98rem; margin-bottom: 6px; letter-spacing: -0.01em; }
.feature p { font-size: 0.88rem; margin: 0; }

@media (max-width: 760px) { .feature-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 480px) { .feature-grid { grid-template-columns: 1fr; } }

/* ---------- Barre d'outils (recherche + localisation + filtres) ---------- */
/* Trois rangées avant le contenu — recherche+localisation, puces "type de
   lieu" (l'essentiel : "je veux un bar", "je veux manger"), puis filtres
   secondaires+compteur+vue — au lieu de sept blocs empilés. L'ambiance et
   l'arrondissement, moins fondamentaux pour trouver où aller, vivent dans
   le panneau #filter-sheet plutôt que sur la ligne principale. */

.spots-toolbar { margin-bottom: 16px; }

/* Réutilisé par planning.html (filtres "genre de journée" + "durée") : ce
   n'est qu'un conteneur de marge, tout le reste (.filter-envie-label,
   .theme-grid) est partagé et défini ci-dessous. */
.filters-block { margin-bottom: 20px; }

.spots-search-row { display: flex; gap: 10px; margin-bottom: 10px; }

.spots-search-wrap { position: relative; flex: 1; min-width: 0; }
.spots-search-icon {
  position: absolute;
  left: 18px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--text-tertiary);
  font-size: 0.95rem;
  pointer-events: none;
}
.spots-search {
  width: 100%;
  padding: 13px 18px 13px 44px;
  border-radius: 999px;
  border: 1.5px solid var(--border);
  background: var(--surface);
  font-family: var(--font-sans);
  font-size: 0.92rem;
  color: var(--text-primary);
}
.spots-search:focus { outline: none; border-color: var(--primary-dim); box-shadow: 0 0 0 3px var(--primary-light); }
.spots-search::placeholder { color: var(--text-tertiary); }

/* Localisation intégrée directement à la barre de recherche plutôt que
   posée à part dans son propre bloc : un simple bouton rond dont l'état
   (idle / en cours / actif / erreur) se lit sur sa couleur. */
.nearme-icon-btn {
  flex-shrink: 0;
  width: 46px;
  height: 46px;
  border-radius: 50%;
  border: 1.5px solid var(--border);
  background: var(--surface);
  font-size: 1.15rem;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: border-color 0.15s ease, background 0.15s ease;
}
.nearme-icon-btn:hover { border-color: var(--primary-dim); background: var(--surface-subtle); }
.nearme-icon-btn[aria-pressed="true"] { background: var(--primary); border-color: var(--primary); }
.nearme-icon-btn[aria-pressed="true"] .nearme-icon-glyph { filter: brightness(0) invert(1); }
.nearme-icon-btn--error { border-color: var(--red); }
.nearme-icon-btn[disabled] { opacity: 0.65; cursor: not-allowed; }

/* Pendant la localisation, l'icône tourne doucement — plus lisible qu'un
   texte "Localisation…" écrasé dans un petit bouton rond ; le tiroir de
   statut juste en dessous porte ce texte à la place. */
@keyframes nearme-spin { to { transform: rotate(360deg); } }
.nearme-icon-btn--locating .nearme-icon-glyph { display: inline-block; animation: nearme-spin 1s linear infinite; }

/* Tiroir de statut : n'apparaît que quand il y a vraiment quelque chose à
   dire (triées par distance, ou une erreur) — pas un bandeau vide affiché
   en permanence comme l'ancien bloc "Près de moi". */
.nearme-status {
  display: flex;
  align-items: center;
  gap: 10px;
  background: var(--primary-light);
  border-radius: var(--radius-sm);
  padding: 9px 14px;
  margin-bottom: 10px;
}
/* [hidden] a la même spécificité que ".nearme-status { display: flex }"
   ci-dessus et notre feuille passe après celle du navigateur : sans cet
   override, le tiroir resterait visible (vide) en permanence dès que JS lui
   retire son texte — même bug que .btn[hidden], déjà rencontré et corrigé
   là aussi, corrigé ici une bonne fois pour toutes pour ce composant aussi. */
.nearme-status[hidden] { display: none; }
.nearme-status--error { background: var(--red-bg); }
.nearme-status-text { flex: 1; min-width: 0; font-size: 0.85rem; font-weight: 600; color: var(--primary); }
.nearme-status--error .nearme-status-text { color: var(--red); }
.nearme-status-reset {
  flex-shrink: 0;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  border: none;
  background: rgba(255, 255, 255, 0.7);
  color: inherit;
  font-size: 0.8rem;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}
.nearme-status-reset:hover { background: rgba(255, 255, 255, 0.95); }
.nearme-status-reset[hidden] { display: none; } /* voir la note sur .nearme-status[hidden] plus haut */

/* Puces "type de lieu" : le filtre essentiel, toujours visible, en rangée
   qui défile horizontalement sur mobile (comme les filtres de catégorie de
   Google Maps ou Yelp) et s'enveloppe naturellement dès qu'il y a la place. */
.type-chip-row {
  display: flex;
  gap: 8px;
  overflow-x: auto;
  padding-bottom: 6px;
  margin-bottom: 12px;
  scrollbar-width: thin;
  -webkit-overflow-scrolling: touch;
}
@media (min-width: 700px) {
  .type-chip-row { flex-wrap: wrap; overflow-x: visible; }
}

.filter-chip {
  border: 1.5px solid var(--border);
  background: var(--surface);
  padding: 12px 18px;
  border-radius: var(--radius-md);
  font-size: 0.92rem;
  font-weight: 700;
  cursor: pointer;
  color: var(--text-primary);
  transition: all 0.15s ease;
  font-family: var(--font-sans);
  white-space: nowrap;
  flex-shrink: 0;
}
.filter-chip:hover { border-color: var(--primary-dim); background: var(--surface-subtle); }
.filter-chip:active { transform: scale(0.96); }
.filter-chip.active { background: var(--primary); border-color: var(--primary); color: #fff; }

/* Les puces "type de lieu" empruntent la couleur de leur famille (même
   palette que les cartes adresse et la carte du PDF, voir FAMILIES.color)
   une fois actives, pour rester cohérentes et un peu plus ludiques qu'un
   simple bleu uniforme partout. */
.type-chip.active { background: var(--fam-color, var(--primary)); border-color: var(--fam-color, var(--primary)); }

.filter-envie-label {
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--primary);
  margin: 0 0 12px;
}

.theme-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-bottom: 22px;
}

/* Rangée 3 : bouton "Filtres" (ambiance + arrondissement), compteur,
   bascule liste/carte — un seul niveau de contrôles secondaires. */
.spots-toolbar-row3 {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 10px;
}

.filters-more-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border: 1.5px solid var(--border);
  background: var(--surface);
  padding: 10px 16px;
  border-radius: 999px;
  font-family: var(--font-sans);
  font-size: 0.88rem;
  font-weight: 700;
  color: var(--text-primary);
  cursor: pointer;
}
.filters-more-btn:hover { border-color: var(--primary-dim); }
.filters-more-btn.active { border-color: var(--primary); color: var(--primary); background: var(--primary-light); }
.filters-more-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  border-radius: 999px;
  background: var(--primary);
  color: #fff;
  font-size: 0.7rem;
  font-weight: 700;
}
.filters-more-badge[hidden] { display: none; } /* voir la note sur .nearme-status[hidden] plus haut */

.result-count-inline {
  flex: 1;
  min-width: 90px;
  text-align: center;
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--text-secondary);
  margin: 0;
}

.view-toggle { display: flex; gap: 6px; flex-shrink: 0; }
.view-toggle-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 42px;
  padding: 0 16px;
  border: 1.5px solid var(--border);
  background: var(--surface);
  border-radius: 999px;
  font-family: var(--font-sans);
  font-size: 0.86rem;
  font-weight: 700;
  color: var(--text-secondary);
  cursor: pointer;
  transition: all 0.15s ease;
  white-space: nowrap;
}
.view-toggle-btn:hover { border-color: var(--primary-dim); }
.view-toggle-btn.active { background: var(--text-primary); border-color: var(--text-primary); color: #fff; }
@media (max-width: 400px) { .view-toggle-btn { padding: 0 12px; font-size: 0.8rem; } }

/* Bascule Liste/Carte : un seul des deux conteneurs est affiché à la fois,
   plutôt que d'empiler la carte puis la grille en permanence. Uniquement
   sous 1025px (mobile/tablette) — au-delà, la vue "les deux à la fois" prend
   le relais ci-dessous et cette bascule ne s'applique plus (voir la
   surcharge dans le bloc @media (min-width: 1025px)). */
@media (max-width: 1024px) {
  #spots-content[data-view="list"] #map { display: none; }
  #spots-content[data-view="map"] #spot-grid { display: none; }
}

@media (max-width: 480px) {
  .result-count-inline { order: 3; width: 100%; }
}

/* ---------- Vue scindée desktop : liste à gauche, carte à droite ----------
   Au-delà de 1024px, il y a la place d'afficher les deux à la fois plutôt
   que de forcer un choix — plus besoin de la bascule Liste/Carte, cachée ici
   (elle reste utile en dessous, seule solution avec la largeur qu'on a).
   Cliquer un pin fait défiler jusqu'à sa fiche dans la liste au lieu
   d'ouvrir une popup (voir scrollToCard() dans js/spots.js) : la vraie fiche
   complète est déjà visible en permanence à gauche, doublonner son contenu
   dans une popup n'apporterait rien. La carte est en position sticky pour
   rester visible pendant qu'on parcourt la liste. */
@media (min-width: 1025px) {
  .view-toggle { display: none; }
  #spots-content {
    display: grid;
    grid-template-columns: minmax(360px, 460px) 1fr;
    gap: 24px;
    align-items: start;
  }
  /* #map précède #spot-grid dans le HTML (ordre historique), mais on veut
     visuellement la liste à gauche et la carte à droite : order plutôt que
     de réordonner le markup, pour ne rien changer aux références JS. */
  /* La liste défile dans son propre cadre (même hauteur que la carte) plutôt
     que la page entière : sticky + hauteur fixe + overflow-y, comme la
     carte juste en dessous — cliquer un pin fait défiler ce cadre-là (voir
     scrollToCard() dans js/spots.js, .scrollIntoView cible le bon ancêtre
     scrollable automatiquement), jamais la page. */
  /* Flex (colonne) plutôt que Grid pour empiler les fiches : avec Grid, une
     rangée peut se figer sur min-height sans jamais grandir pour son vrai
     contenu (rencontré une 1ère fois avec la vignette à cheval sur
     plusieurs rangées, voir plus bas — même famille de piège avec des
     rangées implicites "auto"). En Flex, chaque fiche garde tout simplement
     la hauteur de son propre contenu : plus fiable ici. */
  #spot-grid {
    order: 1;
    display: flex;
    flex-direction: column;
    gap: 12px;
    position: sticky;
    top: 90px;
    height: calc(100vh - 120px);
    min-height: 460px;
    overflow-y: auto;
    overscroll-behavior: contain;
    padding-right: 6px;
    padding-bottom: 4px;
  }
  #spot-grid .spot-card { flex-shrink: 0; }
  /* #spots-content #map (au lieu de #map seul) : la règle de base #map plus
     bas dans ce fichier (height: 460px, hors media query) a la même
     spécificité qu'un simple "#map" ici — à spécificité égale, c'est l'ordre
     dans le fichier qui tranche, et cette règle-ci vient AVANT dans la
     feuille de style, donc elle perdrait sans le sélecteur composé. Repéré
     en testant : la carte restait bloquée à 460px au lieu de suivre
     calc(100vh - 120px) comme la liste juste à côté. */
  #spots-content #map {
    order: 2;
    display: block;
    position: sticky;
    top: 90px;
    height: calc(100vh - 120px);
    min-height: 460px;
    margin-bottom: 0;
  }

  /* Cards compactes en liste : la .spot-card complète (image plein cadre,
     description entière, hashtags, encart "pourquoi y aller"…) est pensée
     pour une grille vitrine — bien trop haute une fois collée à gauche
     d'une carte, sur une liste qui peut compter des dizaines d'adresses.
     La vignette est posée en position absolue à gauche (plutôt qu'en CSS
     Grid à cheval sur toutes les lignes : essayé d'abord, mais un item qui
     chevauche plusieurs lignes "auto" force la 1ère ligne à sa propre
     hauteur au lieu de laisser le texte dicter la sienne — comportement du
     moteur de mise en page, pas un bug de ce fichier) et le reste du
     contenu garde son flux normal, juste poussé par un padding-left — même
     composant partout (HTML/JS inchangés), juste une mise en page
     différente selon le contexte. Taille de vignette "liste web classique"
     (façon Doctolib/Airbnb) : ni minuscule ni pleine largeur. */
  #spot-grid .spot-card {
    position: relative;
    padding: 12px 14px 12px 124px;
    min-height: 120px;
    /* Cliquer la fiche met en avant son pin (voir renderGrid() dans
       js/spots.js) : curseur en pointeur pour que ce soit évident, même
       en dehors des liens/boutons de la carte. */
    cursor: pointer;
  }
  #spot-grid .spot-card > .spot-media:first-child {
    position: absolute;
    top: 12px;
    left: 14px;
    margin: 0;
    width: 96px;
    height: 96px;
    aspect-ratio: 1;
    border-radius: var(--radius-sm);
  }
  #spot-grid .spot-media .spot-status-badge { display: none; }
  #spot-grid .spot-card-top { margin-bottom: 4px; }
  #spot-grid .spot-name { font-size: 0.94rem; margin-bottom: 3px; }
  #spot-grid .spot-desc {
    margin-bottom: 6px;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }
  #spot-grid .spot-whygo,
  #spot-grid .spot-hashtags { display: none; }
  #spot-grid .spot-footer { padding-top: 6px; margin-top: 4px; }
  #spot-grid .spot-address {
    margin-bottom: 6px;
    font-size: 0.74rem;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  #spot-grid .spot-actions { gap: 6px; }
  #spot-grid .spot-action-btn { padding: 4px 10px; font-size: 0.76rem; }

  /* Fiche verrouillée : le panneau de déblocage flottant (voir
     .spot-lock-panel plus haut, pensé pour une grande carte de vitrine)
     devient une ligne compacte en flux normal plutôt qu'un encart absolu
     centré en bas — sans quoi il détonnerait sur une carte de ~110px. */
  #spot-grid .spot-lock-panel {
    position: static;
    transform: none;
    width: auto;
    max-width: none;
    flex-direction: row;
    align-items: center;
    text-align: left;
    gap: 8px;
    padding: 6px 0 0;
    background: none;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    border: none;
    box-shadow: none;
  }
  #spot-grid .spot-lock-panel-icon { width: 22px; height: 22px; font-size: 0.75rem; margin-bottom: 0; flex-shrink: 0; }
  #spot-grid .spot-lock-panel-hint { display: none; }
  #spot-grid .spot-lock-panel strong { font-size: 0.8rem; }
  #spot-grid .spot-lock-panel .btn { margin-top: 0; padding: 4px 10px; font-size: 0.74rem; }
}

/* Fiche brièvement mise en avant après un clic sur son pin (voir
   scrollToCard() dans js/spots.js) — même logique que .view-toggle-btn.active
   ou le focus des filtres : un contour de la couleur primaire, le temps de
   repérer la bonne carte dans une liste qui peut être longue.
   Sélecteur composé (.spot-card.spot-card--pin-highlight) plutôt que la
   seule classe modificatrice : la règle .spot-card de base, plus bas dans
   la feuille de style, fixe border/box-shadow en shorthand avec la même
   spécificité — à spécificité égale, sans ce doublement, c'est elle qui
   l'emportait à la cascade et le liseret ne s'affichait jamais. */
.spot-card.spot-card--pin-highlight {
  border-color: var(--primary);
  box-shadow: 0 0 0 3px var(--primary), 0 0 0 7px var(--primary-dim), var(--shadow-hover);
}

html[dir="rtl"] .spots-search-icon { left: auto; right: 18px; }
html[dir="rtl"] .spots-search { padding: 13px 44px 13px 18px; }
html[dir="rtl"] .filter-envie-label { text-align: right; }

/* ---------- Panneau "Filtres" (ambiance + arrondissement) ---------- */
/* Même mécanique que la fiche adresse (#spot-sheet, voir plus bas) : tiroir
   qui remonte du bas sur mobile, popover centré sur desktop — un seul
   système de superposition dans toute la page, jamais un menu qui recouvre
   par accident le contenu en dessous (le bug de l'ancien
   .type-dropdown-panel, positionné en absolute au milieu des autres
   contrôles au lieu d'un vrai calque dédié). */

.filter-sheet-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(24, 24, 22, 0.45);
  z-index: 200;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s ease;
}
.filter-sheet-backdrop.open { opacity: 1; pointer-events: auto; }

.filter-sheet {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 201;
  background: var(--surface);
  border-radius: 20px 20px 0 0;
  box-shadow: 0 -10px 30px rgba(0, 0, 0, 0.18);
  max-height: 85vh;
  overflow-y: auto;
  padding: 10px 20px 24px;
  transform: translateY(100%);
  transition: transform 0.28s cubic-bezier(0.22, 0.9, 0.3, 1);
}
.filter-sheet.open { transform: translateY(0); }

.filter-sheet-handle {
  width: 40px;
  height: 4px;
  border-radius: 2px;
  background: var(--border);
  margin: 6px auto 16px;
}
.filter-sheet-close {
  position: absolute;
  top: 14px;
  right: 16px;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  border: 1px solid var(--border);
  background: var(--surface-subtle);
  color: var(--text-secondary);
  font-size: 0.95rem;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}
.filter-sheet-close:hover { background: var(--border-soft); color: var(--text-primary); }
.filter-sheet-title { font-size: 1.1rem; margin: 0 0 18px; padding-right: 40px; }

.arr-select {
  width: 100%;
  padding: 13px 16px;
  border-radius: var(--radius-sm);
  border: 1.5px solid var(--border);
  background: var(--surface);
  font-family: var(--font-sans);
  font-size: 0.92rem;
  font-weight: 600;
  color: var(--text-primary);
  margin-bottom: 22px;
  cursor: pointer;
}
.arr-select:focus { outline: none; border-color: var(--primary-dim); box-shadow: 0 0 0 3px var(--primary-light); }

.filter-sheet-actions { display: flex; gap: 10px; }
.filter-sheet-actions .btn { flex: 1; }

/* Au-delà de 640px (même seuil que #spot-sheet), le panneau devient un
   popover centré plutôt qu'un tiroir plein écran — plus adapté à une
   souris, et cohérent avec le comportement desktop de la fiche adresse. */
@media (min-width: 640px) {
  .filter-sheet-backdrop.open { opacity: 1; pointer-events: auto; }
  .filter-sheet {
    left: 50%;
    right: auto;
    bottom: auto;
    top: 50%;
    transform: translate(-50%, -50%) scale(0.96);
    opacity: 0;
    pointer-events: none;
    width: 420px;
    max-width: calc(100vw - 40px);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-hover);
    transition: transform 0.2s ease, opacity 0.2s ease;
  }
  .filter-sheet.open {
    transform: translate(-50%, -50%) scale(1);
    opacity: 1;
    pointer-events: auto;
  }
  .filter-sheet-handle { display: none; }
}

html[dir="rtl"] .filter-sheet-close { right: auto; left: 16px; }
html[dir="rtl"] .filter-sheet-title { padding-right: 0; padding-left: 40px; }

.spot-distance-badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 0.78rem;
  font-weight: 700;
  color: var(--primary);
  background: var(--primary-light);
  border-radius: 999px;
  padding: 4px 10px;
  margin: 8px 0 0;
  width: fit-content;
}

/* Marqueur "vous êtes ici" : un point plein qui pulse doucement, bien
   distinct des badges-emoji des adresses pour ne jamais le confondre avec
   un pin cliquable. */
.user-location-marker { background: none; border: none; }
.user-location-dot {
  display: block;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--primary);
  border: 3px solid #fff;
  box-shadow: 0 0 0 4px rgba(36, 96, 224, 0.28), var(--shadow-hover);
  animation: user-location-pulse 2.2s ease-in-out infinite;
}
@keyframes user-location-pulse {
  0%, 100% { box-shadow: 0 0 0 4px rgba(36, 96, 224, 0.28), var(--shadow-hover); }
  50% { box-shadow: 0 0 0 8px rgba(36, 96, 224, 0.12), var(--shadow-hover); }
}

/* ---------- Cards (spots) ---------- */

.card-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 18px;
}

@media (max-width: 900px) { .card-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 620px) { .card-grid { grid-template-columns: 1fr; } }

/* ---------- Grille de catégories (page d'accueil, remplace les 3
   pillar-card) ----------
   Tuiles photo plutôt qu'icône-sur-fond-blanc : on veut que ça donne
   immédiatement envie de cliquer, pas juste informer qu'une catégorie
   existe. Chaque tuile est un lien entier vers /spots.html?family=xxx (voir
   js/spots.js, lit ce paramètre au chargement pour pré-filtrer). Photo
   réelle d'une adresse de la famille si une a déjà été postée depuis
   /admin (voir js/home-preview.js), sinon dégradé + icône — même principe
   de secours que .spot-media--fallback, jamais de tuile vide. */
.category-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 14px;
}
@media (max-width: 900px) { .category-grid { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 620px) { .category-grid { grid-template-columns: repeat(2, 1fr); gap: 10px; } }

.category-tile {
  position: relative;
  display: block;
  aspect-ratio: 4 / 5;
  border-radius: var(--radius-md);
  overflow: hidden;
  background: linear-gradient(160deg, color-mix(in srgb, var(--fam-color, #6b7280) 30%, white) 0%, color-mix(in srgb, var(--fam-color, #6b7280) 10%, white) 100%);
  box-shadow: var(--shadow);
  transition: box-shadow 0.2s ease, transform 0.2s ease;
}
.category-tile:hover { box-shadow: var(--shadow-hover); transform: translateY(-2px); }
.category-tile img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* Le nom de la catégorie vit dans un badge à fond plein, jamais posé
   directement sur la photo — un simple voile dégradé restait illisible
   selon la photo (ciel clair, sable, plat lumineux...), un badge opaque
   garantit la lisibilité quelle que soit l'image en dessous. */
.category-tile-body {
  position: absolute;
  left: 8px;
  right: 8px;
  bottom: 8px;
  z-index: 1;
  display: flex;
  align-items: center;
  gap: 8px;
  background: var(--surface);
  border-radius: var(--radius-sm);
  padding: 7px 10px;
  box-shadow: 0 2px 8px rgba(24, 24, 22, 0.18);
}
.category-tile-icon { font-size: 1.05rem; flex-shrink: 0; }
.category-tile-text { min-width: 0; }
.category-tile-body h3 {
  font-size: 0.8rem;
  margin: 0;
  line-height: 1.2;
  letter-spacing: -0.01em;
  color: var(--text-primary);
}
.category-tile-count { display: block; font-size: 0.68rem; color: var(--text-tertiary); font-family: var(--font-mono); }
@media (max-width: 480px) { .category-tile-body h3 { font-size: 0.76rem; } }

/* ---------- Liste "adresses préférées" (page d'accueil) ----------
   Format liste compacte plutôt que grille de grandes cartes : on veut en
   montrer davantage (6 plutôt que 3) sans allonger démesurément la page.
   Même logique de verrouillage que /spots.html (voir js/home-preview.js) :
   une adresse verrouillée garde sa photo et sa catégorie visibles, seul le
   nom est masqué — pour donner envie de débloquer, pas juste l'annoncer. */
.spot-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
  max-width: 640px;
  margin: 0 auto;
}
.spot-list-item {
  display: flex;
  align-items: center;
  gap: 14px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 10px 16px 10px 10px;
  text-decoration: none;
  transition: box-shadow 0.15s ease, border-color 0.15s ease;
}
.spot-list-item:hover { box-shadow: var(--shadow-hover); border-color: var(--text-disabled); }
.spot-list-thumb {
  position: relative;
  flex-shrink: 0;
  width: 56px;
  height: 56px;
  border-radius: var(--radius-sm);
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  background: linear-gradient(160deg, color-mix(in srgb, var(--fam-color, #6b7280) 26%, white) 0%, color-mix(in srgb, var(--fam-color, #6b7280) 8%, white) 100%);
}
.spot-list-thumb img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.spot-list-thumb .icon { font-size: 1.3rem; }
.spot-list-body { flex: 1; min-width: 0; }
.spot-list-name { font-size: 0.95rem; font-weight: 600; color: var(--text-primary); margin: 0 0 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.spot-list-meta { font-size: 0.8rem; color: var(--text-tertiary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.spot-list-item.locked .spot-list-name { color: var(--amber); }
.spot-list-chevron { flex-shrink: 0; color: var(--text-disabled); font-size: 1.1rem; }
html[dir="rtl"] .spot-list-item { padding: 10px 10px 10px 16px; }
html[dir="rtl"] .spot-list-chevron { transform: scaleX(-1); }

.spot-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  overflow: hidden;
  box-shadow: var(--shadow);
  position: relative;
  display: flex;
  flex-direction: column;
  transition: box-shadow 0.15s ease, border-color 0.15s ease;
}
.spot-card:hover { box-shadow: var(--shadow-hover); border-color: var(--text-disabled); }

.spot-card { padding: 18px 20px 20px; }

/* Badge de statut ("Offert" / "À débloquer") : posé en fin de colonne flex
   (align-self:flex-end), pas en position absolute — s'aligne à droite en
   LTR et à gauche en RTL tout seul, sans média-query ni override manuel. */
.spot-status-badge {
  align-self: flex-end;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 0.7rem;
  font-weight: 700;
  padding: 4px 10px;
  border-radius: 999px;
  white-space: nowrap;
  margin-bottom: 8px;
}
.spot-status-badge--free { background: var(--green-bg); color: var(--green); border: 1px solid var(--green-border); }
.spot-status-badge--locked { background: var(--amber-bg); color: var(--amber); border: 1px solid var(--amber-border); }

/* ---- Visuel en tête de fiche (photo, ou dégradé de secours par famille) ----
   Un seul bloc réutilisé partout où spotInnerHTML() est injecté (grille,
   tiroir mobile, popup carte) : le rendu "plein cadre vs encart" ne dépend
   que du contexte CSS environnant (voir plus bas), jamais du markup. */
.spot-media {
  position: relative;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  background: var(--surface-subtle);
  display: block;
}
.spot-media img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.spot-media .spot-media-icon { display: none; }

/* Pas de photo (ou lien cassé, voir onerror dans js/spots.js) : un dégradé
   teinté par la couleur de la famille + son icône, plutôt qu'un bloc vide —
   --fam-color est posé en style inline par spotMediaHTML() (js/spots.js) et
   updateImagePreview() (js/admin-spots.js), mêmes couleurs que les points de
   la carte du PDF (voir FAMILIES dans functions/lib/content.js). */
.spot-media--fallback {
  display: flex;
  align-items: center;
  justify-content: center;
  background: linear-gradient(135deg, color-mix(in srgb, var(--fam-color, #6b7280) 22%, white) 0%, color-mix(in srgb, var(--fam-color, #6b7280) 6%, white) 100%);
}
.spot-media--fallback .spot-media-icon {
  display: block;
  font-size: 2.6rem;
  filter: drop-shadow(0 3px 8px rgba(24, 24, 22, 0.12));
}

/* Le badge de statut flotte sur l'image plutôt que de pousser le contenu en
   dessous — même spécificité qui l'emporte naturellement sur les couleurs
   pastel de .spot-status-badge--free/--locked (voir plus haut) : un badge
   translucide se lit mieux sur une photo quelconque qu'un aplat de couleur. */
.spot-media .spot-status-badge {
  position: absolute;
  top: 10px;
  inset-inline-end: 10px;
  align-self: unset;
  margin-bottom: 0;
  background: rgba(255, 255, 255, 0.92);
  backdrop-filter: blur(6px) saturate(160%);
  -webkit-backdrop-filter: blur(6px) saturate(160%);
  box-shadow: 0 2px 8px rgba(24, 24, 22, 0.14);
}

/* Carte de grille (/spots.html) : l'image mord sur le padding de la carte
   pour aller jusqu'aux bords (look "plein cadre"), grâce à .spot-card qui a
   déjà overflow:hidden — ses coins arrondis suffisent à recadrer l'image
   sans qu'elle ait besoin de son propre border-radius. Les cartes qui n'ont
   pas de .spot-media (aperçu figé de la home, /index.html) ne sont pas
   concernées : cette règle ne s'active que si le bloc est présent. */
.spot-card > .spot-media:first-child { margin: -18px -20px 14px; }

.spot-card-top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 10px;
}

.spot-category {
  font-size: 0.78rem;
  font-weight: 700;
  color: var(--text-primary);
  white-space: nowrap;
}

.spot-zone-line {
  font-size: 0.78rem;
  color: var(--text-tertiary);
  font-weight: 500;
  text-align: right;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.spot-name {
  font-size: 1.05rem;
  font-weight: 700;
  letter-spacing: -0.01em;
  margin-bottom: 8px;
}

.spot-desc { font-size: 0.89rem; color: var(--text-secondary); margin-bottom: 14px; }

.spot-whygo {
  background: var(--surface-subtle);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  margin-bottom: 14px;
}
.spot-whygo-label {
  display: block;
  font-size: 0.66rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--primary);
  margin-bottom: 3px;
}
.spot-whygo p { margin: 0; font-size: 0.85rem; color: var(--text-primary); }

.spot-hashtags {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 10px;
  margin-bottom: 14px;
  font-size: 0.78rem;
}
.spot-hashtags span { color: var(--primary); font-weight: 600; }

.spot-footer {
  margin-top: auto;
  padding-top: 12px;
  border-top: 1px solid var(--border-soft);
}
.spot-address { font-size: 0.78rem; color: var(--text-tertiary); margin-bottom: 10px; }

.spot-actions { display: flex; flex-wrap: wrap; gap: 8px; }

/* Pastilles bien visibles et colorées par type d'action — plus faciles à
   repérer d'un coup d'œil qu'une seule couleur uniforme, et plus "vivantes"
   qu'un simple lien texte. */
.spot-action-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 9px 14px;
  min-height: 38px;
  border-radius: 999px;
  border: 1.5px solid transparent;
  font-size: 0.82rem;
  font-weight: 700;
  text-decoration: none;
  white-space: nowrap;
  transition: transform 0.12s ease, box-shadow 0.15s ease, background 0.15s ease;
}
.spot-action-btn:hover { transform: translateY(-1px); box-shadow: var(--shadow-hover); }
.spot-action-btn:active { transform: translateY(0); box-shadow: none; }

.spot-action-btn--directions { background: var(--primary-light); border-color: var(--primary-dim); color: var(--primary); }
.spot-action-btn--directions:hover { background: var(--primary-dim); color: var(--primary-hover); }

.spot-action-btn--call { background: var(--green-bg); border-color: var(--green-border); color: var(--green); }
.spot-action-btn--call:hover { background: var(--green-border); }

.spot-action-btn--instagram { background: var(--pink-bg); border-color: var(--pink-border); color: var(--pink); }
.spot-action-btn--instagram:hover { background: var(--pink-border); }

.spot-action-btn--website { background: var(--amber-bg); border-color: var(--amber-border); color: var(--amber); }
.spot-action-btn--website:hover { background: var(--amber-border); }

/* Carte verrouillée façon "vitre dépolie" : plutôt que de ne flouter que le
   nom (l'ancien design), tout le corps de la fiche — nom, description,
   pourquoi y aller, tags — est flouté d'un coup derrière un panneau CTA en
   verre dépoli ancré en bas de carte. Le badge de statut et la catégorie,
   eux, restent en dehors du flou : on garde de quoi donner envie (le type
   de lieu, le quartier) sans jamais révéler l'adresse elle-même. Mêmes
   classes réutilisées telles quelles entre /spots.html (js/spots.js) et
   l'aperçu de la home (markup statique) — un seul design à maintenir. */
.spot-card.locked { position: relative; overflow: hidden; min-height: 300px; }

.spot-card-blur-body {
  filter: blur(6px);
  user-select: none;
  pointer-events: none;
}

.spot-lock-panel {
  position: absolute;
  left: 50%;
  bottom: 16px;
  transform: translateX(-50%);
  width: calc(100% - 28px);
  max-width: 240px;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 5px;
  padding: 16px 16px 14px;
  border-radius: var(--radius-md);
  background: rgba(255, 255, 255, 0.86);
  backdrop-filter: blur(14px) saturate(160%);
  -webkit-backdrop-filter: blur(14px) saturate(160%);
  border: 1px solid rgba(255, 255, 255, 0.7);
  box-shadow: 0 14px 34px rgba(24, 24, 22, 0.16);
}
.spot-lock-panel-icon {
  width: 40px;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 999px;
  background: var(--amber-bg);
  border: 1px solid var(--amber-border);
  font-size: 1.05rem;
  margin-bottom: 2px;
}
.spot-lock-panel strong { font-size: 0.92rem; color: var(--text-primary); line-height: 1.25; }
.spot-lock-panel-hint { font-size: 0.76rem; color: var(--text-secondary); line-height: 1.3; }
.spot-lock-panel .btn { margin-top: 6px; }

/* Support navigateurs sans backdrop-filter (rare) : le panneau reste
   parfaitement lisible, juste sans le "verre dépoli". */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .spot-lock-panel { background: rgba(255, 255, 255, 0.97); }
}

/* ---------- Map ---------- */

#map {
  width: 100%;
  height: 460px;
  border-radius: var(--radius-md);
  border: 1px solid var(--border);
  box-shadow: var(--shadow);
  margin-bottom: 24px;
  /* Sans ça, les calques internes de Leaflet (contrôles de zoom à z-index
     1000, popups à 700...) n'ont pas de contexte d'empilement qui les
     contient : ils passent alors AU-DESSUS de la fiche qui remonte du bas
     sur mobile (z-index 201), qui semble "cassée" et coincée sous la carte.
     position+z-index ici crée ce contexte et borne tout Leaflet en dessous. */
  position: relative;
  z-index: 0;
}

@media (max-width: 620px) { #map { height: 340px; } }

/* Pins = badges ronds avec l'emoji de la catégorie, assez grands pour le
   doigt. Bleu = adresse débloquée, ambre = encore verrouillée. */
.spot-marker { background: none; border: none; }
.spot-marker-badge {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: var(--surface);
  border: 2px solid var(--primary);
  box-shadow: var(--shadow-hover);
  font-size: 1.15rem;
  cursor: pointer;
  transition: transform 0.12s ease;
}
.spot-marker-badge:hover,
.spot-marker-badge:active { transform: scale(1.12); }
.spot-marker-locked .spot-marker-badge { border-color: var(--amber); background: var(--amber-bg); }

/* Pin mis en avant (clic sur sa fiche, voir highlightPin() dans js/spots.js)
   — même logique que .spot-card--pin-highlight côté fiche : un liseret bleu
   le temps de le repérer, passé devant les autres pins qui peuvent se
   chevaucher à cet endroit (z-index). */
.spot-marker--pin-highlight { z-index: 1000 !important; }
.spot-marker--pin-highlight .spot-marker-badge {
  border-color: var(--primary);
  box-shadow: 0 0 0 4px var(--primary-light), var(--shadow-hover);
  transform: scale(1.2);
}

/* ---------- Fiche adresse au clic sur un pin ----------
   Mobile (<640px) : tiroir plein écran qui remonte du bas, avec fond assombri.
   Desktop (≥640px) : popup Leaflet ancrée au pin (voir plus bas), pas de
   tiroir ni de fond assombri — trop intrusif, on ne bloque pas la page. */

.spot-sheet-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(24, 24, 22, 0.45);
  z-index: 200;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s ease;
}
.spot-sheet-backdrop.open { opacity: 1; pointer-events: auto; }

.spot-sheet {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 201;
  background: var(--surface);
  border-radius: 20px 20px 0 0;
  box-shadow: 0 -10px 30px rgba(0, 0, 0, 0.18);
  max-height: 85vh;
  overflow-y: auto;
  padding: 10px 20px 28px;
  transform: translateY(100%);
  transition: transform 0.28s cubic-bezier(0.22, 0.9, 0.3, 1);
}
.spot-sheet.open { transform: translateY(0); }

.spot-sheet-handle {
  width: 40px;
  height: 4px;
  border-radius: 2px;
  background: var(--border);
  margin: 6px auto 16px;
}

/* Toujours posé sur la photo de la fiche (ou sa tuile de secours), jamais
   sur un fond de surface uni : un cercle clair sur bordure claire s'y
   fondait littéralement (ciel, lumières...). Traitement "bouton fermer sur
   photo" classique — fond sombre semi-transparent + icône blanche — lisible
   quelle que soit la photo, contrairement à une couleur calée sur le thème. */
.spot-sheet-close {
  position: absolute;
  top: 14px;
  right: 16px;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  border: none;
  background: rgba(20, 20, 18, 0.55);
  color: #ffffff;
  font-size: 1rem;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);
  z-index: 1;
}
.spot-sheet-close:hover { background: rgba(20, 20, 18, 0.72); }
html[dir="rtl"] .spot-sheet-close { right: auto; left: 16px; }

.spot-sheet-content .spot-card-top { margin-top: 4px; }
/* Même traitement "plein cadre" que la grille : le tiroir mobile a son
   propre padding horizontal (voir .spot-sheet, 20px de chaque côté), l'image
   mord dessus pour aller jusqu'aux bords du tiroir. Poignée et bouton fermer
   restent en position:absolute par rapport à .spot-sheet, donc inchangés —
   ils flottent simplement au-dessus de l'image plutôt qu'au-dessus d'un
   espace vide, ce qui reste lisible grâce à leur propre fond. */
.spot-sheet-content > .spot-media:first-child { margin: 0 -20px 14px; }
/* La fiche pin (tiroir mobile / popup desktop) ne réutilise pas <article
   class="spot-card"> autour du contenu — sans ce position:relative, un
   .spot-lock-panel absolu se positionnerait par rapport au sheet/popup
   entier plutôt que par rapport au bloc flouté juste au-dessus de lui.
   Le .locked réserve, lui, la hauteur du panneau flottant : comme il est en
   position:absolute (hors flux), le tiroir/la popup se dimensionneraient
   sinon sur la seule hauteur du corps flouté et couperaient le bas du
   panneau (bouton "Voir l'adresse" tronqué). */
.spot-sheet-content { position: relative; }
.spot-sheet-content.locked { min-height: 300px; }

/* Au-delà de 640px, le tiroir/fond assombri ne sert plus (voir spots.js :
   les clics sur les pins ouvrent une popup Leaflet à la place) — on
   s'assure juste qu'ils restent hors écran si jamais ils sont déclenchés
   pendant un redimensionnement de fenêtre. */
@media (min-width: 640px) {
  .spot-sheet-backdrop.open { opacity: 0; pointer-events: none; }
  .spot-sheet.open { transform: translateY(100%); }
}

/* ---------- Popup carte (desktop) : ancrée au pin, style Leaflet réhabillé
   pour ressembler à nos cartes plutôt qu'à une bulle par défaut. ---------- */

.leaflet-popup.spot-popup {
  margin-bottom: 12px;
}
.spot-popup .leaflet-popup-content-wrapper {
  border-radius: var(--radius-md);
  border: 1px solid var(--border);
  box-shadow: var(--shadow-hover);
  padding: 0;
}
.spot-popup .leaflet-popup-content {
  margin: 18px 20px;
  font-family: var(--font-sans);
  line-height: 1.5;
}
.spot-popup .leaflet-popup-tip {
  border: 1px solid var(--border);
  box-shadow: none;
}
.spot-popup .leaflet-popup-close-button {
  top: 10px;
  right: 10px;
  width: 26px;
  height: 26px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--surface-subtle);
  color: var(--text-secondary) !important;
  font-size: 1rem;
}
.spot-popup .leaflet-popup-close-button:hover { background: var(--border-soft); }
.spot-popup-inner { width: 260px; max-width: 100%; position: relative; }
.spot-popup-inner.locked { min-height: 300px; }
/* Leaflet impose `.leaflet-popup-content a { color:#0078A8 }` par défaut
   (plus spécifique qu'un simple .btn-primary) — sans cette règle, le texte
   du bouton "Voir l'adresse" du panneau de déblocage devient bleu-gris
   quasi illisible sur son propre fond bleu, à l'intérieur d'une popup. */
.spot-popup-inner .btn-primary { color: #ffffff; }
.spot-popup-inner .spot-card-top { margin-top: 2px; }
.spot-popup-inner .spot-footer { border-top: 1px solid var(--border-soft); }
/* Contrairement à la grille et au tiroir mobile, la popup Leaflet vit dans
   .leaflet-popup-content (marge fixe imposée par Leaflet) : plutôt que de
   lutter contre ce conteneur pour un plein cadre, l'image reste en encart —
   toujours en avant, juste avec ses propres coins arrondis et un peu d'air
   en dessous plutôt qu'un débordement. */
.spot-popup-inner .spot-media {
  border-radius: var(--radius-sm);
  aspect-ratio: 16 / 10;
  margin-bottom: 10px;
}

/* ---------- Unlock banner ---------- */

.unlock-banner {
  background: var(--primary-light);
  border: 1px solid var(--primary-dim);
  color: var(--text-primary);
  border-radius: var(--radius-md);
  padding: 24px 28px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  flex-wrap: wrap;
  margin: 32px 0;
}

.unlock-banner h3 { color: var(--text-primary); margin-bottom: 4px; font-size: 1.05rem; }
.unlock-banner h3 span { font-family: var(--font-mono); color: var(--primary); }
.unlock-banner p { color: var(--text-secondary); margin: 0; font-size: 0.9rem; }
.unlock-banner-action { display: flex; flex-direction: column; align-items: center; gap: 6px; }
.unlock-banner-price { font-size: 0.74rem; color: var(--text-tertiary); }

@media (max-width: 560px) {
  .unlock-banner { padding: 20px; }
  .unlock-banner-action { width: 100%; }
  .unlock-banner .btn { width: 100%; }
}

/* ---------- Itineraries ---------- */

.itinerary-list { display: flex; flex-direction: column; gap: 16px; }

.itinerary-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  overflow: hidden;
  box-shadow: var(--shadow);
  padding: 24px 26px;
  position: relative;
}

@media (max-width: 480px) { .itinerary-card { padding: 20px 18px; } }

.itinerary-card h3 { font-size: 1.15rem; letter-spacing: -0.02em; margin-bottom: 4px; }
.itinerary-zone { color: var(--primary); font-weight: 600; font-size: 0.82rem; margin-bottom: 6px; }
.itinerary-card > p { margin-bottom: 20px; }

/* Bandeau en tête de card : vraie photo si le parcours en a une (posée
   depuis /admin, onglet "Journées"), sinon le même traitement "dégradé
   pastel + icône surdimensionnée" que .spot-media--fallback (fiches
   adresses) — une carte ne doit jamais paraître vide, avec ou sans photo.
   --itin-color est posé en style inline (js/planning.js), une couleur par
   parcours (voir ITINERARIES dans functions/lib/content.js). Même structure
   que .spot-media/.spot-media--fallback (js/spots.js) : icône cachée dès
   qu'une image est présente, visible seulement en secours. */
.itinerary-media {
  position: relative;
  overflow: hidden;
  aspect-ratio: 3 / 1;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--surface-subtle);
}
.itinerary-media img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.itinerary-media .itinerary-media-icon { display: none; }
.itinerary-media--fallback {
  background: linear-gradient(135deg, color-mix(in srgb, var(--itin-color, #6b7280) 22%, white) 0%, color-mix(in srgb, var(--itin-color, #6b7280) 6%, white) 100%);
}
.itinerary-media--fallback .itinerary-media-icon {
  display: block;
  font-size: 2.8rem;
  filter: drop-shadow(0 3px 8px rgba(24, 24, 22, 0.12));
}
.itinerary-card > .itinerary-media:first-child { margin: -24px -26px 20px; }
@media (max-width: 480px) { .itinerary-card > .itinerary-media:first-child { margin: -20px -18px 18px; } }

.itinerary-card-head { display: flex; align-items: flex-start; gap: 14px; margin-bottom: 4px; }
.itinerary-icon { font-size: 1.9rem; line-height: 1.15; flex-shrink: 0; }
.itinerary-head-text { min-width: 0; }
.itinerary-head-text h3 { margin-bottom: 2px; }

.itinerary-badges { display: flex; flex-wrap: wrap; gap: 8px; margin: 14px 0 18px; }
.itinerary-badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 0.76rem;
  font-weight: 700;
  padding: 5px 12px;
  border-radius: 999px;
  background: var(--surface-subtle);
  color: var(--text-secondary);
  border: 1px solid var(--border);
  white-space: nowrap;
}
.itinerary-badge--duration { background: var(--primary-light); color: var(--primary); border-color: var(--primary-dim); }
.itinerary-badge--free { background: var(--green-bg); color: var(--green); border-color: var(--green-border); }
.itinerary-badge--premium { color: var(--text-secondary); }

.itinerary-teaser { color: var(--text-secondary); }

/* Encart "Bon à savoir" : un vrai conseil pratique par parcours (vérifié,
   pas une phrase de remplissage), distinct visuellement du reste de la
   carte pour qu'on le repère d'un coup d'œil avant de partir. */
.itinerary-tip {
  background: var(--amber-bg);
  border: 1px solid var(--amber-border);
  border-radius: var(--radius-sm);
  padding: 12px 16px;
  margin: 0 0 22px;
}
.itinerary-tip-label {
  display: block;
  font-size: 0.74rem;
  font-weight: 700;
  color: var(--amber);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  margin-bottom: 4px;
}
.itinerary-tip p { margin: 0; font-size: 0.88rem; color: var(--text-primary); line-height: 1.5; }

/* Deuxième rangée de filtres ("Durée"), plus discrète que "Envie de…" */
.filter-envie-label--sub { font-size: 0.92rem; margin: 18px 0 10px; }
.theme-grid--duration .filter-chip { padding: 9px 16px; font-size: 0.85rem; }

/* Timeline verticale : ligne de connexion + pastilles, horaire et icône
   toujours visibles (même verrouillé) pour montrer le déroulé complet de
   la journée sans révéler les adresses. */
.timeline-v {
  list-style: none;
  margin: 0;
  padding: 0;
  position: relative;
}
.timeline-v::before {
  content: '';
  position: absolute;
  left: 15px;
  top: 8px;
  bottom: 8px;
  width: 2px;
  background: var(--border-soft);
}

.timeline-step {
  position: relative;
  display: flex;
  gap: 16px;
  padding-bottom: 22px;
}
.timeline-step:last-child { padding-bottom: 0; }

.timeline-dot {
  position: relative;
  z-index: 1;
  flex-shrink: 0;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: var(--primary-light);
  border: 1.5px solid var(--primary-dim);
  color: var(--primary);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1rem;
}

.timeline-step-body { flex: 1; padding-top: 4px; min-width: 0; }

.timeline-time {
  font-family: var(--font-mono);
  font-weight: 500;
  color: var(--text-primary);
  font-size: 0.85rem;
  margin-bottom: 4px;
}

.timeline-activity { margin: 0 0 8px; font-size: 0.92rem; color: var(--text-secondary); }

/* Étape avec une adresse précise : la même fiche que /spots.html (voir
   venueCardHTML dans js/planning.js), réutilisée telle quelle — mêmes
   classes .spot-card/.spot-media/.spot-card-top/.spot-card-blur-body/
   .spot-lock-panel, donc même style de base déjà en place plus haut. Ici,
   toujours compacte (miniature + texte empilé), quelle que soit la largeur
   d'écran — elle vit dans le flux d'une étape, pas dans une grille vitrine
   (même technique que la vue scindée de /spots.html : vignette en position
   absolute + padding-left, voir plus haut pourquoi pas CSS Grid ici). */
.timeline-venue-card {
  margin-top: 8px;
  padding: 10px 12px 10px 96px;
  min-height: 92px;
}
/* .spot-card.locked (plus haut, pensé pour une grande carte de vitrine)
   impose min-height: 300px avec la même spécificité (deux classes) — sans
   ce ré-équilibrage, la version verrouillée resterait forcée à 300px alors
   que son contenu compact n'en a besoin que d'environ 92px. */
.timeline-venue-card.locked { min-height: 92px; }
.timeline-venue-card > .spot-media:first-child {
  position: absolute;
  top: 10px;
  left: 10px;
  margin: 0;
  width: 72px;
  height: 72px;
  aspect-ratio: 1;
  border-radius: var(--radius-sm);
}
.timeline-venue-card .spot-media .spot-status-badge { display: none; }
.timeline-venue-card .spot-card-top { margin-bottom: 3px; }
.timeline-venue-card .spot-name { font-size: 0.88rem; margin-bottom: 2px; }
.timeline-venue-card .spot-desc {
  margin-bottom: 4px;
  font-size: 0.78rem;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.timeline-venue-card .spot-footer { padding-top: 4px; margin-top: 2px; }
.timeline-venue-card .spot-address {
  margin-bottom: 4px;
  font-size: 0.72rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.timeline-venue-card .spot-actions { gap: 4px; }
.timeline-venue-card .spot-action-btn { padding: 3px 8px; font-size: 0.72rem; }

.timeline-venue-card .spot-lock-panel {
  position: static;
  transform: none;
  width: auto;
  max-width: none;
  flex-direction: row;
  align-items: center;
  text-align: left;
  gap: 6px;
  padding: 4px 0 0;
  background: none;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  border: none;
  box-shadow: none;
}
.timeline-venue-card .spot-lock-panel-icon { width: 18px; height: 18px; font-size: 0.68rem; margin-bottom: 0; flex-shrink: 0; }
.timeline-venue-card .spot-lock-panel-hint { display: none; }
.timeline-venue-card .spot-lock-panel strong { font-size: 0.74rem; }
.timeline-venue-card .spot-lock-panel .btn { margin-top: 0; padding: 3px 8px; font-size: 0.7rem; }

/* Pied de carte, en flux normal (pas de calque flottant ni de fondu qui
   cache le déroulé) : juste un rappel du nombre d'adresses encore
   verrouillées dans ce parcours-là, et un accès direct au déblocage. */
.itinerary-unlock-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  margin-top: 20px;
  padding-top: 16px;
  border-top: 1px solid var(--border-soft);
  font-size: 0.85rem;
  color: var(--text-secondary);
}

@media (max-width: 480px) {
  .itinerary-unlock-footer { flex-direction: column; align-items: stretch; text-align: center; }
  .itinerary-unlock-footer .btn { width: 100%; }
}

/* ---------- Aperçu verrouillé / débloqué (juste avant le prix) ----------
   Deux vraies cartes adresse (mêmes classes .spot-card que sur /spots.html,
   mêmes données réelles), côte à côte : ce qu'on voit avant et après avoir
   débloqué. Montrer plutôt que décrire — la liste à puces au-dessus reste,
   mais ça donne quelque chose de concret à regarder juste avant de payer. */

/* ---------- Comment ça marche (3 étapes, section "Le guide complet") ---------- */

.how-it-works {
  max-width: 780px;
  margin: 0 auto 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
}
.how-step {
  flex: 1;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 22px 18px;
  text-align: center;
  box-shadow: var(--shadow);
}
.how-step-icon { font-size: 1.6rem; margin-bottom: 8px; }
.how-step h3 { font-size: 0.98rem; margin: 0 0 6px; }
.how-step p { font-size: 0.85rem; color: var(--text-secondary); margin: 0; }
.how-step-arrow {
  flex-shrink: 0;
  color: var(--text-tertiary);
  font-size: 1.3rem;
}
@media (max-width: 760px) {
  .how-it-works { flex-direction: column; }
  .how-step-arrow { transform: rotate(90deg); }
}
html[dir="rtl"] .how-step-arrow:not(.rotated) { transform: scaleX(-1); }
@media (max-width: 760px) {
  html[dir="rtl"] .how-step-arrow { transform: rotate(90deg); }
}

.preview-compare { max-width: 720px; margin: 0 auto 40px; }
.preview-compare > .eyebrow { display: block; text-align: center; margin-bottom: 16px; }
.preview-compare-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
  align-items: start;
}
.preview-col { display: flex; flex-direction: column; gap: 10px; }
.preview-tag {
  align-self: center;
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  padding: 3px 10px;
  border-radius: 999px;
}
.preview-tag--locked { color: var(--amber); background: var(--amber-bg); border: 1px solid var(--amber-border); }
.preview-tag--unlocked { color: var(--green); background: var(--green-bg); border: 1px solid var(--green-border); }
.preview-compare .spot-card { text-align: left; height: 100%; }
@media (max-width: 620px) {
  .preview-compare-grid { grid-template-columns: 1fr; }
}
html[dir="rtl"] .preview-compare .spot-card { text-align: right; }

/* ---------- Pricing ---------- */

.pricing-box {
  max-width: 460px;
  margin: 0 auto;
  background: var(--surface);
  border: 1px solid var(--border);
  color: var(--text-primary);
  border-radius: var(--radius-md);
  padding: 32px;
  text-align: center;
  box-shadow: var(--shadow-hover);
}

@media (max-width: 480px) { .pricing-box { padding: 24px 20px; } }

/* État "déjà débloqué" sur /debloquer.html : mêmes proportions que la boîte
   de prix qu'elle remplace, accent vert plutôt que la couleur d'achat. */
.unlocked-box { border-color: var(--green-border); }
.unlocked-check { font-size: 2.4rem; margin-bottom: 6px; }
.unlocked-box p { color: var(--text-secondary); margin-bottom: 22px; }

/* ---------- Page /restaurer-acces.html : email -> code -> accès ---------- */

.restore-box .form-row { text-align: left; margin-bottom: 18px; }
.restore-box .form-row label {
  display: block;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--text-secondary);
  margin-bottom: 6px;
}
.restore-box input[type="email"] {
  width: 100%;
  padding: 12px 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-family: var(--font-sans);
  font-size: 1rem;
  color: var(--text-primary);
}
.restore-box input[type="email"]:focus { outline: none; border-color: var(--primary-dim); box-shadow: 0 0 0 3px var(--primary-light); }
.restore-box .btn { width: 100%; }
.restore-hint { font-size: 0.78rem; color: var(--text-tertiary); margin: 14px 0 0; }
.restore-icon { font-size: 2.2rem; margin-bottom: 6px; }
.restore-box > p { color: var(--text-secondary); }
.restore-email-shown { font-size: 0.95rem; margin-bottom: 18px !important; }

/* Cases de code façon carte bancaire : toujours de gauche à droite, même en
   arabe (RTL) — un code numérique ne s'inverse pas, comme un numéro de tél. */
.otp-row {
  display: flex;
  gap: 8px;
  justify-content: center;
  direction: ltr;
  margin: 18px 0;
}
.otp-digit {
  width: 44px;
  height: 54px;
  text-align: center;
  font-family: var(--font-mono);
  font-size: 1.4rem;
  font-weight: 600;
  color: var(--text-primary);
  border: 1.5px solid var(--border);
  border-radius: var(--radius-sm);
}
.otp-digit:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-light); }
@media (max-width: 360px) { .otp-digit { width: 38px; height: 48px; font-size: 1.2rem; } }

.restore-error { color: var(--red); font-size: 0.85rem; margin: 0 0 14px; }

.link-btn {
  display: block;
  width: 100%;
  background: none;
  border: none;
  color: var(--primary);
  font-weight: 600;
  font-size: 0.85rem;
  text-decoration: underline;
  cursor: pointer;
  margin-top: 16px;
  font-family: var(--font-sans);
}

.restore-back-link { text-align: center; font-size: 0.85rem; margin-top: 22px; color: var(--text-secondary); }

@keyframes restore-shake {
  10%, 90% { transform: translateX(-2px); }
  20%, 80% { transform: translateX(4px); }
  30%, 50%, 70% { transform: translateX(-6px); }
  40%, 60% { transform: translateX(6px); }
}
.shake { animation: restore-shake 0.4s ease; }
@media (prefers-reduced-motion: reduce) { .shake { animation: none; } }

.pricing-box .price {
  font-family: var(--font-mono);
  font-size: 2.6rem;
  font-weight: 500;
  letter-spacing: -0.02em;
  margin: 10px 0;
  color: var(--primary);
}
.pricing-box .price .price-suffix { font-size: 1rem; color: var(--text-tertiary); font-family: var(--font-sans); font-weight: 400; }

.price-anchor {
  font-size: 0.84rem;
  color: var(--text-tertiary);
  font-style: italic;
  margin: 0 0 22px;
  line-height: 1.5;
}

.pricing-list { list-style: none; margin: 20px 0 26px; padding: 0; text-align: left; }
.pricing-list li { padding: 7px 0; font-size: 0.92rem; color: var(--text-secondary); }
.pricing-list li::before { content: "✓ "; color: var(--green); font-weight: 700; }

.pricing-box .btn-primary { width: 100%; }

/* Preuve compacte juste sous le bouton d'achat : même avis que dans la
   section témoignages plus bas, mais repris ici pile au moment où on
   hésite à cliquer plutôt que seulement en bas de page. */
.mini-proof {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 14px;
  padding: 10px 12px;
  background: var(--green-bg);
  border: 1px solid var(--green-border);
  border-radius: var(--radius-md);
  text-align: left;
}
.mini-proof picture { flex-shrink: 0; line-height: 0; }
.mini-proof img { width: 32px; height: 32px; border-radius: 50%; object-fit: cover; display: block; }
.mini-proof-stars { display: block; font-size: 0.7rem; color: var(--green); letter-spacing: 1px; margin-bottom: 1px; }
.mini-proof-quote {
  flex: 1;
  min-width: 0;
  font-size: 0.8rem;
  color: var(--text-secondary);
  line-height: 1.35;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.mini-proof-quote strong { color: var(--text-primary); font-weight: 600; }
html[dir="rtl"] .mini-proof { text-align: right; }

.reassure-line { font-size: 0.82rem; color: var(--green); font-weight: 500; margin-top: 12px; }
.checkout-note { font-size: 0.78rem; color: var(--text-tertiary); margin-top: 6px; }

/* Explique, avant même d'arriver sur la page Stripe, que l'email demandé
   là-bas (pour le reçu) est ce qui permettra ensuite "Restaurer mon accès" —
   pour que le lien entre les deux ne soit jamais une surprise. */
.email-reassure-line {
  max-width: 420px;
  margin: 14px auto 0;
  text-align: center;
  font-size: 0.82rem;
  color: var(--text-secondary);
  line-height: 1.5;
}

/* Lien "déjà payé, restaurer mon accès" : doit être facile à repérer pour un
   client qui revient sur un nouvel appareil, pas noyé en petit texte gris. */
.restore-access-cta { text-align: center; margin: 18px 0; }
.restore-access-cta a {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 10px 18px;
  border: 1px solid var(--primary-dim);
  border-radius: 100px;
  background: var(--primary-light);
  color: var(--primary);
  font-weight: 600;
  font-size: 0.88rem;
  text-decoration: none;
  transition: background 0.15s ease, border-color 0.15s ease;
}
.restore-access-cta a:hover { background: var(--primary-dim); border-color: var(--primary); }

/* ---------- Page /debloquer.html : bandeau de réassurance paiement ----------
   Une colonne unique plutôt qu'une grille 2 colonnes : avec 3 badges (dont la
   garantie satisfait-remboursé, plus longue à lire), une seule ligne par
   badge reste plus lisible qu'une grille resserrée sur ce conteneur étroit. */
.trust-badges {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin: 20px 0 16px;
}
.trust-badge {
  display: flex;
  align-items: center;
  gap: 8px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 12px 14px;
  font-size: 0.82rem;
  color: var(--text-secondary);
  font-weight: 500;
}
.trust-badge-icon { font-size: 1.15rem; flex-shrink: 0; }
.trust-badge--guarantee { background: var(--green-bg); border-color: var(--green-border); color: var(--green); font-weight: 600; }
.trust-badge--social { background: var(--primary-light); border-color: var(--primary-dim); color: var(--primary); font-weight: 600; }

.payment-methods-label {
  text-align: center;
  font-size: 0.76rem;
  color: var(--text-tertiary);
  margin: 0 0 10px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.payment-logos {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0 0 20px;
}
.payment-logo {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 52px;
  height: 34px;
  background: #fff;
  border: 1px solid var(--border);
  border-radius: 6px;
  box-shadow: var(--shadow);
}
.payment-logo svg { display: block; }

.stripe-note { font-size: 0.76rem; color: var(--text-tertiary); text-align: center; margin: 0 0 8px; line-height: 1.5; }

/* ---------- Footer ---------- */

footer {
  border-top: 1px solid var(--border);
  padding: 44px 0 24px;
  margin-top: 32px;
}
/* Sous 1024px, la barre .sticky-cta est fixe en bas d'écran en permanence
   (voir plus bas) : sans cette réserve, son dernier état mesuré (posé par
   js/app.js dans --sticky-cta-h) recouvre la ligne de copyright et le lien
   admin une fois qu'on scrolle tout en bas. --sticky-cta-h vaut 0px tant que
   la barre n'existe pas (desktop, page déjà débloquée...), donc cette règle
   n'ajoute rien là où il n'y a rien à réserver. */
body.has-sticky-cta footer { padding-bottom: calc(24px + var(--sticky-cta-h, 0px)); }

.footer-grid {
  display: grid;
  grid-template-columns: 1.3fr repeat(3, 1fr);
  gap: 32px;
  padding-bottom: 30px;
}

.footer-brand .logo { font-size: 1.02rem; display: inline-block; }
.footer-brand p {
  margin: 12px 0 0;
  font-size: 0.82rem;
  color: var(--text-tertiary);
  max-width: 260px;
  line-height: 1.55;
}

.footer-col h3 {
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--text-tertiary);
  margin: 0 0 14px;
}

.footer-col a {
  display: block;
  font-size: 0.87rem;
  color: var(--text-secondary);
  text-decoration: none;
  padding: 6px 0;
}
.footer-col a:hover { color: var(--primary); }

.footer-bottom {
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
  padding-top: 20px;
  border-top: 1px solid var(--border);
  font-size: 0.78rem;
  color: var(--text-tertiary);
}

.footer-admin-link { opacity: 0.5; font-size: 0.78rem; text-decoration: none; color: inherit; }
.footer-admin-link:hover { opacity: 1; }

@media (max-width: 760px) {
  .footer-grid { grid-template-columns: repeat(2, 1fr); gap: 30px 20px; }
  .footer-brand { grid-column: 1 / -1; }
}

@media (max-width: 480px) {
  .footer-grid { grid-template-columns: 1fr; gap: 26px; text-align: center; }
  .footer-brand p { max-width: none; margin-left: auto; margin-right: auto; }
  .footer-bottom { flex-direction: column; text-align: center; }
}

/* ---------- Blog ---------- */

.blog-hero-lede { max-width: 620px; margin: 0 auto; }

/* Barre d'outils : recherche + catégories, au-dessus de la grille. Réutilise
   volontairement les classes de /spots.html (.spots-search, .filter-chip)
   plutôt que d'en recréer des nouvelles — même vocabulaire visuel partout
   sur le site, cohérence "pro" plutôt que composants réinventés à chaque
   page. */
.blog-toolbar { margin-bottom: 18px; }

.blog-category-row {
  display: flex;
  gap: 8px;
  overflow-x: auto;
  padding-bottom: 6px;
  margin-bottom: 4px;
  scrollbar-width: thin;
  -webkit-overflow-scrolling: touch;
}
@media (min-width: 700px) {
  .blog-category-row { flex-wrap: wrap; overflow-x: visible; }
}

/* Nombre de résultats + bouton reset, affiché seulement quand une recherche
   ou un filtre autre que "Tout" est actif (voir .blog-results-bar[hidden]
   plus bas) — pas de texte permanent qui n'apporte rien tant qu'on regarde
   juste la liste complète. */
.blog-results-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin: 4px 2px 18px;
  font-size: 0.85rem;
  color: var(--text-tertiary);
}
.blog-results-bar[hidden] { display: none; }
.blog-results-reset {
  border: none;
  background: none;
  font-family: var(--font-sans);
  font-size: 0.85rem;
  font-weight: 700;
  color: var(--primary);
  cursor: pointer;
  padding: 0;
}
.blog-results-reset:hover { text-decoration: underline; }

.blog-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 24px;
}
@media (max-width: 900px) { .blog-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 620px) { .blog-grid { grid-template-columns: 1fr; } }

.blog-card {
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow);
  overflow: hidden; /* pour que la miniature épouse les coins arrondis de la card */
  text-decoration: none;
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.blog-card:hover { transform: translateY(-2px); box-shadow: var(--shadow-hover); }
/* Masqué par défaut via JS uniquement (voir js/blog.js) : sans JS, ou pour
   tout robot qui lit le HTML brut, chaque card reste visible et son lien
   pleinement présent — le filtre/la recherche/la pagination sont une
   amélioration progressive, jamais une condition d'accès au contenu. */
.blog-card[hidden] { display: none; }

.blog-card-thumb-wrap {
  aspect-ratio: 1200 / 420;
  background: var(--surface-subtle);
  overflow: hidden;
}
.blog-card-thumb {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: transform 0.2s ease;
}
.blog-card:hover .blog-card-thumb { transform: scale(1.04); }

.blog-card-body { display: flex; flex-direction: column; flex: 1; padding: 20px 24px 24px; }
.blog-card-eyebrow { font-size: 0.76rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; color: var(--primary); margin-bottom: 10px; }
.blog-card h2 { font-size: 1.15rem; letter-spacing: -0.01em; color: var(--text-primary); margin-bottom: 10px; }
.blog-card p { font-size: 0.9rem; color: var(--text-secondary); line-height: 1.55; margin-bottom: 14px; }
.blog-card-meta { margin-top: auto; font-size: 0.78rem; color: var(--text-tertiary); }

/* État vide (recherche/filtre sans résultat) : même ton chaleureux que le
   reste du site plutôt qu'un message sec type "0 résultat". */
.blog-empty {
  text-align: center;
  padding: 56px 24px;
  background: var(--surface);
  border: 1px dashed var(--border);
  border-radius: var(--radius-md);
}
.blog-empty[hidden] { display: none; }
.blog-empty-emoji { font-size: 2rem; margin-bottom: 12px; }
.blog-empty h3 { font-size: 1.1rem; margin-bottom: 8px; color: var(--text-primary); }
.blog-empty p { color: var(--text-secondary); font-size: 0.92rem; margin-bottom: 18px; }

.blog-load-more-wrap { display: flex; justify-content: center; margin-top: 32px; }
.blog-load-more-wrap[hidden] { display: none; }
.blog-load-more {
  border: 1.5px solid var(--border);
  background: var(--surface);
  padding: 13px 28px;
  border-radius: 999px;
  font-family: var(--font-sans);
  font-size: 0.92rem;
  font-weight: 700;
  color: var(--text-primary);
  cursor: pointer;
  transition: border-color 0.15s ease, background 0.15s ease;
}
.blog-load-more:hover { border-color: var(--primary-dim); background: var(--surface-subtle); }

/* ---------- Article (page blog individuelle) ---------- */

.article-wrap { max-width: 720px; }

.breadcrumb { font-size: 0.82rem; color: var(--text-tertiary); margin-bottom: 18px; }
.breadcrumb a { color: var(--text-tertiary); text-decoration: none; }
.breadcrumb a:hover { color: var(--primary); }

.article-meta {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 0.84rem;
  color: var(--text-tertiary);
  margin-top: 16px;
}
.article-meta .avatar { width: 32px; height: 32px; font-size: 0.82rem; margin: 0; flex-shrink: 0; }
.article-meta strong { color: var(--text-secondary); font-weight: 600; }

/* Bandeau d'illustration en tête d'article — un dessin plutôt qu'une photo
   de stock : cet environnement n'a pas d'accès réseau vers une banque
   d'images (testé), et un dessin maison évite au passage tout souci de
   licence. Une dizaine de scènes réutilisées selon le thème de l'article
   (voir img/blog/scene-*.svg), pas une par article — cohérence visuelle
   d'ensemble plutôt que 24 styles différents. */
.article-hero-illus {
  display: block;
  width: 100%;
  aspect-ratio: 3 / 1.15;
  object-fit: cover;
  border-radius: var(--radius-md);
  margin-top: 24px;
  box-shadow: var(--shadow);
}
@media (max-width: 640px) { .article-hero-illus { aspect-ratio: 16 / 11; } }

.article-toc {
  background: var(--surface-subtle);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-md);
  padding: 20px 24px;
  margin: 32px 0;
}
.article-toc-label { font-size: 0.76rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-tertiary); margin-bottom: 10px; }
.article-toc ol { margin: 0; padding-left: 20px; }
.article-toc li { margin-bottom: 6px; font-size: 0.92rem; }
.article-toc a { color: var(--primary); text-decoration: none; }
.article-toc a:hover { text-decoration: underline; }

/* "L'essentiel en bref" : résumé scannable juste après l'intro, avant le
   premier H2 — pour deux lecteurs à la fois. Quelqu'un qui scanne l'article
   sur mobile sans tout lire, et les outils qui extraient un résumé (aperçus
   IA, featured snippets Google) : une liste de faits courts se cite et se
   reformule bien plus facilement qu'un paragraphe de prose noyé dans le
   texte. Couleur primaire plutôt que le gris neutre de .article-toc :
   visuellement "la réponse", pas juste un sommaire de navigation. */
.article-tldr {
  background: var(--primary-light);
  border: 1px solid var(--primary-dim);
  border-radius: var(--radius-md);
  padding: 20px 24px;
  margin: 28px 0;
}
.article-tldr-label { font-size: 0.76rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; color: var(--primary); margin-bottom: 10px; }
.article-tldr ul { margin: 0; padding-left: 20px; color: var(--text-primary); }
.article-tldr li { margin-bottom: 6px; font-size: 0.94rem; line-height: 1.5; }
.article-tldr li:last-child { margin-bottom: 0; }

.article-body h2 { font-size: 1.35rem; margin-top: 2em; letter-spacing: -0.01em; }
.article-body h3 { font-size: 1.1rem; margin-top: 1.6em; }
.article-body p { font-size: 1rem; line-height: 1.75; color: var(--text-secondary); margin-bottom: 1.2em; }
.article-body ul, .article-body ol { margin: 0 0 1.2em; padding-left: 22px; color: var(--text-secondary); line-height: 1.7; }
.article-body li { margin-bottom: 6px; }
.article-body strong { color: var(--text-primary); }

/* Encart qui vend le guide, glissé dans le corps de l'article — même esprit
   visuel que le reste du site (pas un bandeau publicitaire criard), mais
   assez distinct du texte autour pour donner envie de cliquer : liseré en
   dégradé, pastille de stat façon vitrine, et — quand des photos d'adresses
   sont disponibles — un petit collage façon Polaroid en tête de carte (voir
   .guide-promo-photos ci-dessous, injecté par initGuidePromoPolaroids() dans
   js/app.js : de vraies photos posées depuis /admin, pas des images en dur,
   donc l'encart s'anime tout seul au fur et à mesure que le guide se garnit
   de photos, sans jamais rien à retoucher ici). Deux occurrences par article
   (voir insertion dans le HTML) : une au fil du texte, une juste avant la
   FAQ/conclusion — pas la même carte copiée-collée deux fois, la seconde
   (.guide-promo--final) referme l'article sur un ton plus direct. */
.guide-promo {
  position: relative;
  background: linear-gradient(155deg, var(--primary-light) 0%, var(--surface) 65%);
  border: 1px solid var(--primary-dim);
  border-radius: 18px;
  padding: 30px 28px 28px;
  margin: 2.4em 0;
  box-shadow: var(--shadow);
  overflow: hidden;
  transition: box-shadow 0.2s ease, transform 0.2s ease;
}
.guide-promo:hover { box-shadow: var(--shadow-hover); transform: translateY(-2px); }
.guide-promo::before {
  content: '';
  position: absolute;
  inset: 0 0 auto 0;
  height: 4px;
  background: linear-gradient(90deg, var(--primary) 0%, #3fc2c9 100%);
}
.guide-promo-eyebrow {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  font-size: 0.76rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--primary);
  margin-bottom: 10px;
}
.guide-promo-stat {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 700;
  color: var(--primary);
  background: var(--surface);
  border: 1px solid var(--primary-dim);
  border-radius: 999px;
  padding: 3px 10px;
  white-space: nowrap;
}
.guide-promo h3 { font-size: 1.2rem; margin-bottom: 8px; letter-spacing: -0.01em; }
.guide-promo p { font-size: 0.93rem; color: var(--text-secondary); margin-bottom: 20px; line-height: 1.6; }
.guide-promo .btn {
  width: auto;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  box-shadow: 0 2px 10px rgba(36, 96, 224, 0.25);
}
.guide-promo .btn::after { content: '→'; transition: transform 0.15s ease; }
.guide-promo .btn:hover::after { transform: translateX(3px); }
/* La flèche pointe dans le sens de lecture : vers la gauche en arabe (RTL),
   pas vers la droite comme pour les autres langues (LTR). Repéré lors d'un
   audit RTL : la flèche restait "→" sur les pages arabes. */
html[dir="rtl"] .guide-promo .btn::after { content: '←'; }
html[dir="rtl"] .guide-promo .btn:hover::after { transform: translateX(-3px); }
@media (max-width: 480px) { .guide-promo .btn { width: 100%; justify-content: center; } }

/* Collage "Polaroid" : 2 ou 3 photos réelles d'adresses du guide, légèrement
   pivotées et superposées comme posées à la main sur la carte, cadre blanc
   épais en bas façon tirage instantané. N'existe que si le JS en a trouvé
   assez (voir initGuidePromoPolaroids()) — sans ce bloc, la carte retombe
   proprement sur son habillage texte seul, pas de trou ni de placeholder vide. */
.guide-promo-photos {
  display: flex;
  justify-content: center;
  margin: -4px 0 22px;
}
.guide-promo-photo {
  display: block;
  flex-shrink: 0;
  width: 92px;
  background: #fff;
  padding: 6px 6px 16px;
  border-radius: 3px;
  box-shadow: 0 10px 20px rgba(24, 24, 22, 0.18), 0 2px 4px rgba(24, 24, 22, 0.1);
  transition: transform 0.25s ease;
}
.guide-promo-photo:nth-child(1) { transform: rotate(-7deg) translate(6px, 3px); z-index: 1; }
.guide-promo-photo:nth-child(2) { transform: rotate(4deg); z-index: 3; margin: 0 -14px; }
.guide-promo-photo:nth-child(3) { transform: rotate(-3deg) translate(-6px, 4px); z-index: 2; }
.guide-promo:hover .guide-promo-photo:nth-child(1) { transform: rotate(-11deg) translate(2px, -2px); }
.guide-promo:hover .guide-promo-photo:nth-child(2) { transform: rotate(4deg) translateY(-4px); }
.guide-promo:hover .guide-promo-photo:nth-child(3) { transform: rotate(1deg) translate(-2px, -1px); }
.guide-promo-photo-img {
  display: block;
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  border-radius: 1px;
  background: var(--surface-subtle);
}
@media (max-width: 480px) {
  .guide-promo-photo { width: 78px; padding: 5px 5px 13px; }
  /* La pastille de stat (ex: "63 adresses · 6 journées") passe à la ligne
     plutôt que de se faire couper : white-space:nowrap dessus + une rangée
     trop étroite en portrait mobile la tronquaient sinon. */
  .guide-promo-eyebrow { flex-wrap: wrap; row-gap: 6px; }
}

/* Variante de clôture : fond plein plutôt que dégradé pastel, pour un
   dernier appel plus affirmé juste avant que le lecteur ne quitte l'article. */
.guide-promo--final {
  background: linear-gradient(155deg, var(--primary) 0%, #173e94 100%);
  border-color: transparent;
}
.guide-promo--final::before { display: none; }
.guide-promo--final .guide-promo-eyebrow { color: #cfe0fb; }
.guide-promo--final .guide-promo-stat { background: rgba(255, 255, 255, 0.14); border-color: rgba(255, 255, 255, 0.3); color: #fff; }
.guide-promo--final h3 { color: #fff; }
.guide-promo--final p { color: #dbe6fb; }
.guide-promo--final .btn-primary { background: #fff; color: var(--primary); border-color: #fff; }
.guide-promo--final .btn-primary:hover { background: #eef1ff; }

.article-faq { margin-top: 2.5em; }
.article-faq h2 { font-size: 1.35rem; margin-bottom: 1em; }

.article-footer-nav {
  display: flex;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  margin-top: 3em;
  padding-top: 24px;
  border-top: 1px solid var(--border-soft);
  font-size: 0.9rem;
}
.article-footer-nav a { color: var(--primary); text-decoration: none; font-weight: 600; }
.article-footer-nav a:hover { text-decoration: underline; }

/* ---------- Simple pages (legal) ---------- */

.legal { max-width: 720px; margin: 0 auto; padding: 48px 24px; }
.legal h2 { margin-top: 1.4em; }
.legal p a { color: var(--primary); text-decoration: underline; }
.placeholder-note {
  background: var(--amber-bg);
  border: 1px solid var(--amber-border);
  color: var(--amber);
  border-radius: var(--radius-sm);
  padding: 14px 18px;
  font-size: 0.86rem;
  margin-bottom: 30px;
}

/* ---------- Barre CTA flottante (mobile/tablette) ----------
   En dessous de 1024px, le CTA de la nav passe dans le menu hamburger fermé
   par défaut (voir .nav-links) : sans ça, l'action principale du site devient
   invisible pour la majorité des visiteurs. Cette barre, injectée par app.js
   sur toutes les pages sauf merci.html, reste visible en permanence.
   z-index 90 : sous le toast (100) et sous le tiroir de fiche adresse (200/201)
   pour que ceux-ci restent lisibles par-dessus sans réglage supplémentaire. */

.sticky-cta {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 90;
  display: none;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  background: rgba(255, 255, 255, 0.96);
  backdrop-filter: blur(8px);
  border-top: 1px solid var(--border);
  box-shadow: 0 -4px 16px rgba(0, 0, 0, 0.06);
  padding: 10px 16px calc(10px + env(safe-area-inset-bottom, 0px));
  animation: sticky-cta-in 0.25s ease;
}
.sticky-cta-price {
  font-size: 0.82rem;
  font-weight: 500;
  color: var(--text-secondary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Action secondaire de la barre (toutes les pages sauf /spots.html) : occupe
   la même place que .sticky-cta-price, mais c'est un vrai lien cliquable
   plutôt qu'un texte inerte — d'où la couleur d'action et le soulignement,
   seule façon de le distinguer visuellement du bouton primaire à côté sans
   lui donner le même poids. */
.sticky-cta-secondary {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--primary);
  text-decoration: underline;
  text-decoration-color: var(--primary-dim);
  text-underline-offset: 2px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.sticky-cta-secondary:hover { color: var(--primary-hover); }
.sticky-cta .btn {
  flex-shrink: 0;
  min-height: 44px;
  padding: 10px 20px;
  font-size: 0.92rem;
  white-space: nowrap;
}
@keyframes sticky-cta-in {
  from { transform: translateY(100%); }
  to { transform: translateY(0); }
}
@media (max-width: 1024px) {
  .sticky-cta { display: flex; }
  /* La place qu'elle prend en bas d'écran est réservée sur le footer
     (voir body.has-sticky-cta footer, hauteur mesurée en JS dans
     js/app.js) pour qu'elle ne recouvre jamais la ligne de copyright ou
     le lien admin une fois qu'on scrolle tout en bas. */
}
/* Page d'accueil : masquée quand la section prix (#guide) est déjà à l'écran,
   pour ne pas empiler deux CTA identiques l'un sur l'autre. */
.sticky-cta.sticky-cta-hidden { display: none; }
@media (max-width: 340px) {
  /* Écrans très étroits : l'action secondaire (prix ou lien) cède la place,
     le bouton primaire seul reste lisible. */
  .sticky-cta-price, .sticky-cta-secondary { display: none; }
}
html[dir="rtl"] .sticky-cta-price,
html[dir="rtl"] .sticky-cta-secondary { text-align: right; }

/* ---------- Toast ---------- */

.toast {
  position: fixed;
  bottom: 24px;
  left: 50%;
  transform: translateX(-50%) translateY(20px);
  background: var(--text-primary);
  color: #ffffff;
  padding: 12px 20px;
  border-radius: var(--radius-sm);
  font-size: 0.88rem;
  box-shadow: var(--shadow-hover);
  opacity: 0;
  pointer-events: none;
  transition: all 0.25s ease;
  z-index: 100; /* au-dessus de .sticky-cta (90) pour ne jamais se faire recouvrir */
  max-width: calc(100vw - 32px);
  text-align: center;
}
.toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }
/* La barre CTA flottante occupe le bas de l'écran sous 1024px : le toast doit
   remonter au-dessus d'elle plutôt que de se retrouver caché derrière. */
@media (max-width: 1024px) {
  body.has-sticky-cta .toast { bottom: 78px; }
}

/* ---------- Merci page ---------- */

.merci-box {
  max-width: 560px;
  margin: 64px auto;
  text-align: center;
  padding: 0 24px;
}
.merci-icon { font-size: 2.6rem; margin-bottom: 16px; }
/* Un seul <h1> dans le DOM à la fois n'est pas possible ici (3 états
   exclusifs togglés en JS) : succès/erreur utilisent un <h2> pour la
   hiérarchie de titres, mais gardent la même taille visuelle que le <h1>
   de l'état "chargement". */
.merci-box h1, .merci-box h2 { font-size: clamp(1.6rem, 4vw, 2rem); }

.merci-email-note {
  background: var(--primary-light);
  border-radius: var(--radius-sm);
  padding: 12px 16px;
  margin: 4px 0 20px;
  font-size: 0.86rem;
  color: var(--text-secondary);
  line-height: 1.5;
}
.merci-email-note strong { color: var(--primary); }
.merci-email-note[hidden] { display: none; } /* [hidden] vs display, voir la note sur .nearme-status[hidden] dans css/style.css */

@media (max-width: 480px) { .merci-box { margin: 40px auto; } }

/* ---------- RTL (arabe) ---------- */
/* Le flexbox et le grid se réorientent tout seuls avec dir="rtl" ; ce qui
   reste à corriger à la main, ce sont les propriétés "physiques" (left/right,
   text-align, margin-left) qui ne suivent pas le sens de lecture automatiquement. */

html[dir="rtl"] .lang-switch-menu { right: auto; left: 0; }
html[dir="rtl"] .lang-switch-option { text-align: right; }
html[dir="rtl"] .nav-links li:last-child { margin-left: 0; margin-right: 8px; }
html[dir="rtl"] .lang-switch { margin-left: 0; margin-right: 4px; }

html[dir="rtl"] .hero-copy h1,
html[dir="rtl"] .hero-copy .lede { text-align: right; }
@media (max-width: 900px) {
  html[dir="rtl"] .hero-copy h1,
  html[dir="rtl"] .hero-copy .lede { text-align: center; }
}

html[dir="rtl"] .pull-quote { border-left: none; border-right: 3px solid var(--primary); padding-left: 0; padding-right: 20px; }

html[dir="rtl"] .pricing-list { text-align: right; }
html[dir="rtl"] .pricing-list li::before { content: none; }
html[dir="rtl"] .pricing-list li::after { content: " ✓"; color: var(--green); font-weight: 700; }

html[dir="rtl"] .spot-address,
html[dir="rtl"] .spot-desc,
html[dir="rtl"] .spot-whygo,
html[dir="rtl"] .story-body,
html[dir="rtl"] .faq-item { text-align: right; }

/* ---------- /mon-espace.html : verrouillé / débloqué + PDF ---------- */

.account-state { margin-bottom: 8px; }
.account-loading-text { text-align: center; color: var(--text-secondary); padding: 40px 0; }
.account-error { text-align: center; color: var(--red); font-size: 0.9rem; }

.account-box { margin-bottom: 18px; }
.account-box-icon { font-size: 2.2rem; margin-bottom: 8px; }
.account-box--unlocked { border-color: var(--green-border); }

/* Aperçu flouté sur l'état verrouillé : montre la forme du contenu (email,
   montant, date) sans jamais afficher de fausses données lisibles. */
.account-preview {
  border: 1.5px dashed var(--border);
  border-radius: var(--radius-sm);
  padding: 14px 16px;
  margin: 20px 0 10px;
  text-align: left;
}
.account-preview-row {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  padding: 6px 0;
  font-size: 0.85rem;
  color: var(--text-secondary);
}
.account-preview-blur {
  filter: blur(4px);
  user-select: none;
  font-family: var(--font-mono);
  color: var(--text-tertiary);
}
.account-preview-caption {
  font-size: 0.78rem;
  color: var(--text-tertiary);
  font-style: italic;
  margin: 0 0 22px;
}

.account-receipt { text-align: left; margin-top: 4px; }
.account-receipt h3 { font-size: 1rem; margin: 0 0 10px; text-align: center; }
/* [hidden] doit rester prioritaire sur le display:flex ci-dessous — même
   spécificité (une classe, un attribut), donc sans cette règle explicite
   l'ordre d'apparition dans la feuille l'emporterait et une ligne "cachée"
   par JS resterait visible (voir .admin-login-screen[hidden] plus haut pour
   le même correctif). */
.account-receipt-row[hidden] { display: none; }
.account-receipt-row {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 0;
  border-bottom: 1px solid var(--border-soft);
  font-size: 0.92rem;
}
.account-receipt-row:last-of-type { border-bottom: none; }
.account-receipt-row span { color: var(--text-secondary); }
.account-receipt-row strong { color: var(--text-primary); font-weight: 600; }
#account-receipt-details p[data-receipt-fallback] { color: var(--text-secondary); font-size: 0.88rem; text-align: center; margin: 6px 0 0; }

.account-pdf-box { text-align: center; }
.account-pdf-box .btn { width: 100%; }
.account-pdf-hint { font-size: 0.78rem; color: var(--text-tertiary); margin: 12px 0 0; }
.account-pdf-error { color: var(--red); font-size: 0.85rem; margin: 10px 0 0; }
.account-links { margin-top: 6px; }

html[dir="rtl"] .account-preview,
html[dir="rtl"] .account-receipt { text-align: right; }

/* ============================================================
   Admin (/admin.html) — tableau de bord interne, pas de i18n,
   densité d'information plus élevée que le reste du site public.
   Réutilise les tokens Meridian (couleurs, rayons, ombres) pour rester
   cohérent visuellement, mais avec sa propre mise en page.
   ============================================================ */

.admin-body { background: var(--bg); }
.admin-wrap { min-height: 100vh; }

/* ---- Écran de connexion ----
   [hidden] doit rester prioritaire sur le display:flex ci-dessous — sans cette
   règle explicite, la règle "display:flex" de la classe (origine "auteur")
   l'emporterait sur le display:none de [hidden] (origine "user agent"), et le
   formulaire de connexion resterait visible même une fois .hidden posé en JS. */
.admin-login-screen[hidden] { display: none; }
.admin-login-screen {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
}
.admin-login-box {
  width: 100%;
  max-width: 360px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-hover);
  padding: 36px 32px;
  text-align: center;
}
.admin-login-box h1 { font-size: 1.3rem; margin: 0 0 22px; }
.admin-login-box form { display: flex; flex-direction: column; gap: 14px; text-align: left; }
.admin-login-box .btn { margin-top: 4px; }
/* Un champ sous 16px déclenche un zoom automatique au focus sur iOS Safari —
   gênant pour se connecter depuis un téléphone. */
.admin-login-box .form-row input { font-size: 16px; }
@media (max-width: 400px) { .admin-login-box { padding: 28px 20px; } }

/* ---- Header du dashboard ---- */
.admin-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 24px;
  border-bottom: 1px solid var(--border);
  background: var(--surface);
  flex-wrap: wrap;
  gap: 12px;
}
.admin-header-right { display: flex; align-items: center; gap: 8px; }
.admin-tabs { display: flex; align-items: center; gap: 8px; }
.admin-tab {
  font-size: 0.88rem;
  font-weight: 500;
  color: var(--text-secondary);
  padding: 8px 14px;
  border-radius: var(--radius-sm);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.admin-tab:hover { background: var(--surface-subtle); }
.admin-tab.active { background: var(--primary-light); color: var(--primary); }
.admin-badge {
  background: var(--red);
  color: #fff;
  font-size: 0.7rem;
  font-weight: 700;
  padding: 1px 7px;
  border-radius: 999px;
}
.admin-logout-btn {
  font-size: 0.85rem;
  color: var(--text-secondary);
  background: none;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 8px 14px;
  cursor: pointer;
}
.admin-logout-btn:hover { background: var(--surface-subtle); color: var(--text-primary); }

/* ---- Mobile : bandeau d'onglets en segmented control pleine largeur,
   plutôt que 3 petits onglets + déconnexion tassés sur une seule ligne
   qui finissait par passer à la ligne n'importe où.
   Grille 2×2 plutôt que les 4 onglets sur une seule ligne (flex: 1 1 0) :
   avec "Statistiques" (mot le plus long, aucun point de coupure) partagé
   sur 4 colonnes à 375px, le contenu ne tenait pas dans sa colonne — un
   flex item garde par défaut min-width:auto (= la largeur de son mot le
   plus long) et ignore tout rétrécissement, ce qui poussait .admin-tabs
   au-delà du viewport et faisait défiler toute la page horizontalement.
   Deux onglets par ligne donne à chaque libellé largement la place qu'il
   lui faut, sans troncature ni astuce de taille de police. ---- */
@media (max-width: 640px) {
  .admin-header { padding: 14px 16px; row-gap: 12px; }
  .admin-header-right { flex: 1 1 100%; flex-direction: column; align-items: stretch; gap: 10px; }
  .admin-tabs { width: 100%; display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; }
  .admin-tab {
    justify-content: center;
    min-height: 44px;
    padding: 10px 8px;
    text-align: center;
  }
  .admin-logout-btn { align-self: flex-end; min-height: 40px; }
}

.admin-panel { padding: 28px 24px 60px; max-width: 1080px; margin: 0 auto; }
@media (max-width: 640px) {
  .admin-panel { padding: 18px 14px 48px; }
  .admin-stat-card { padding: 16px 16px; }
  .admin-card { padding: 16px; }
}

/* Action destructive (vide la table `events`, voir /api/admin/stats-reset) —
   discrète et à l'écart des cartes, comme .admin-spot-delete-btn : jamais un
   gros bouton rouge accrocheur pour une action qu'on ne veut pas déclencher
   par mégarde. */
.admin-stats-toolbar { display: flex; justify-content: space-between; align-items: center; gap: 12px; margin-bottom: 12px; }

/* ---- Brief du jour : carte d'accueil ludique en tête du tableau de bord ---- */
.admin-brief {
  background: linear-gradient(160deg, var(--primary-light) 0%, var(--surface) 55%);
  border: 1px solid var(--primary-dim);
  border-radius: var(--radius-md);
  padding: 26px 26px 22px;
  margin-bottom: 20px;
  box-shadow: var(--shadow);
}
.admin-brief-eyebrow {
  font-size: 0.76rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--primary);
  margin-bottom: 8px;
}
.admin-brief-greeting {
  font-size: clamp(1.4rem, 3vw, 1.8rem);
  font-weight: 700;
  letter-spacing: -0.02em;
  margin: 0 0 4px;
  color: var(--text-primary);
}
.admin-brief-date { font-size: 0.9rem; color: var(--text-secondary); margin-bottom: 14px; text-transform: capitalize; }
.admin-brief-live {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--green);
  background: var(--green-bg);
  border: 1px solid var(--green-border);
  border-radius: 999px;
  padding: 5px 12px 5px 10px;
}
.admin-brief-live-dot {
  width: 7px;
  height: 7px;
  border-radius: 999px;
  background: var(--green);
  animation: admin-brief-pulse 1.8s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) { .admin-brief-live-dot { animation: none; } }
@keyframes admin-brief-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: 0.4; transform: scale(0.75); }
}
.admin-brief-divider { height: 1px; background: var(--primary-dim); margin: 20px 0 16px; }
.admin-brief-section-label {
  font-size: 0.78rem;
  font-weight: 700;
  color: var(--text-secondary);
  margin-bottom: 12px;
}
.admin-brief-section-label--inline { margin-bottom: 0; }

.admin-brief-row-empty { font-size: 0.9rem; color: var(--text-tertiary); text-align: center; }
.admin-brief-pill {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  font-family: var(--font-mono);
  font-size: 0.76rem;
  font-weight: 600;
  border-radius: 999px;
  padding: 3px 10px;
  white-space: nowrap;
  flex-shrink: 0;
}
.admin-brief-pill--good { color: var(--green); background: var(--green-bg); border: 1px solid var(--green-border); }
.admin-brief-pill--bad { color: var(--red); background: var(--red-bg); border: 1px solid var(--red); }
.admin-brief-pill--flat { color: var(--text-secondary); background: var(--surface-subtle); border: 1px solid var(--border); }
@media (max-width: 480px) {
  .admin-brief { padding: 20px 18px 18px; }
}
.admin-stats-reset-btn {
  background: none;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 6px 12px;
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--red, #b3261e);
  cursor: pointer;
}
.admin-stats-reset-btn:hover { background: var(--surface-subtle); border-color: var(--red, #b3261e); }

/* ---- Cartes de stats ---- */
.admin-stat-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 14px;
  margin-bottom: 20px;
}
@media (max-width: 760px) { .admin-stat-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 480px) { .admin-stat-grid { grid-template-columns: 1fr; } }

.admin-stat-card {
  /* Sans ce min-width:0, une valeur ou un libellé trop long empêcherait la
     grille (.admin-stat-grid) de rétrécir sous 375px — comportement par
     défaut d'une grille CSS : la piste s'agrandit pour loger le contenu
     "incompressible" au lieu de laisser le texte passer à la ligne, ce qui
     fait déborder toute la page horizontalement sur mobile. */
  min-width: 0;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 20px 22px;
  box-shadow: var(--shadow);
}
.admin-stat-card--accent { border-color: var(--primary-dim); background: var(--primary-light); }
.admin-stat-card--good { border-color: var(--green-border); background: var(--green-bg); }
.admin-stat-card--bad { border-color: var(--red); background: var(--red-bg); }
.admin-stat-card--bad .admin-stat-value { color: var(--red); }
.admin-stat-label { font-size: 0.8rem; color: var(--text-secondary); margin-bottom: 6px; }
.admin-stat-value {
  font-family: var(--font-mono);
  font-size: 1.7rem;
  font-weight: 500;
  color: var(--text-primary);
  font-variant-numeric: tabular-nums;
}

/* ---- Tuiles "Aujourd'hui", dans le bandeau .admin-brief ----
   Même grammaire visuelle que .admin-stat-card juste en dessous (icône +
   gros chiffre + libellé) mais sur un fond clair semi-transparent pour
   ressortir sur le dégradé du bandeau — un seul langage de carte pour tout
   le haut du tableau de bord, plutôt que deux styles différents. */
.admin-today-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 10px;
}
@media (max-width: 760px) { .admin-today-grid { grid-template-columns: repeat(2, 1fr); } }
.admin-stat-tile {
  min-width: 0; /* même raison que .admin-stat-card ci-dessus */
  background: color-mix(in srgb, var(--surface) 78%, transparent);
  border: 1px solid var(--primary-dim);
  border-radius: var(--radius-sm);
  padding: 14px 12px;
  text-align: center;
}
.admin-stat-tile--good { border-color: var(--green-border); background: color-mix(in srgb, var(--green-bg) 85%, transparent); }
.admin-stat-tile-icon { font-size: 1.4rem; line-height: 1; margin-bottom: 6px; }
.admin-stat-tile-value {
  font-family: var(--font-mono);
  font-size: 1.5rem;
  font-weight: 600;
  color: var(--text-primary);
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
}
.admin-stat-tile-label { font-size: 0.74rem; color: var(--text-secondary); margin-top: 4px; }
.admin-today-trend { display: flex; justify-content: center; margin-top: 14px; }

/* ---- Rangée de cartes secondaires ---- */
.admin-row { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
@media (max-width: 760px) { .admin-row { grid-template-columns: 1fr; } }
.admin-card {
  min-width: 0; /* même raison que .admin-stat-card plus haut — cible réelle du débordement mobile trouvé sur "Qui clique sur « Je veux le guide » ?" */
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 22px;
  box-shadow: var(--shadow);
}
.admin-card h2 { font-size: 1rem; margin: 0 0 16px; }
.admin-card-sub { margin: -10px 0 16px; font-size: 0.82rem; color: var(--text-tertiary); }
.admin-empty { color: var(--text-tertiary); font-size: 0.88rem; }

/* Chapeau d'une nouvelle zone du tableau de bord (ex. "Comprendre les
   parcours"), sous le libellé de section — même rôle que .admin-card-sub
   mais hors carte, donc sans la marge négative pensée pour suivre un <h2>. */
.admin-section-sub { margin: -6px 0 16px; font-size: 0.84rem; color: var(--text-tertiary); max-width: 640px; }

/* Carte "taux de rebond" : un seul gros chiffre centré, même famille que
   .admin-stat-card mais sans dépendre de la grille à 3 colonnes. */
.admin-bounce-card { text-align: center; padding: 24px 22px; }
.admin-bounce-card .admin-stat-value { font-size: 2.2rem; margin-bottom: 4px; }
.admin-bounce-card .admin-stat-label { margin-bottom: 0; }

/* ---- Répartition par langue ---- */
.admin-lang-breakdown { display: flex; flex-direction: column; gap: 10px; }
.admin-lang-row { display: grid; grid-template-columns: minmax(110px, max-content) 1fr 48px; align-items: center; gap: 10px; }
.admin-lang-name { font-size: 0.85rem; color: var(--text-primary); white-space: nowrap; }
.admin-lang-bar-track { height: 8px; background: var(--surface-subtle); border-radius: 999px; overflow: hidden; }
.admin-lang-bar-fill { height: 100%; background: var(--primary); border-radius: 999px; }
.admin-lang-pct { font-family: var(--font-mono); font-size: 0.8rem; color: var(--text-secondary); text-align: right; }

/* ---- Tendance 14 jours ---- */
.admin-trend-bars { display: flex; align-items: flex-end; gap: 6px; height: 140px; }
.admin-trend-bar { flex: 1; display: flex; flex-direction: column; justify-content: flex-end; align-items: center; height: 100%; }
.admin-trend-bar-fill { width: 100%; background: var(--primary); border-radius: 3px 3px 0 0; min-height: 4px; }
.admin-trend-bar-label { font-size: 0.62rem; color: var(--text-tertiary); margin-top: 6px; writing-mode: vertical-rl; transform: rotate(180deg); }

/* ---- Bandeau "record" ludique, au-dessus des cartes de stats ---- */
.admin-milestone {
  background: linear-gradient(90deg, var(--primary-light), var(--surface));
  border: 1px solid var(--primary-dim);
  border-radius: var(--radius-md);
  padding: 12px 18px;
  margin-bottom: 16px;
  font-size: 0.9rem;
  color: var(--text-primary);
}
.admin-milestone strong { color: var(--primary-hover); }

/* ---- Pays des visiteurs ---- */
.admin-country-list { display: flex; flex-direction: column; gap: 10px; }
.admin-country-row { display: grid; grid-template-columns: 26px 1fr 1fr 40px; align-items: center; gap: 10px; }
.admin-country-flag { font-size: 1.2rem; line-height: 1; }
.admin-country-name { font-size: 0.85rem; color: var(--text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.admin-country-count { font-family: var(--font-mono); font-size: 0.8rem; color: var(--text-secondary); text-align: right; }

/* ---- Pages les plus vues ---- */
.admin-top-pages-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; counter-reset: admin-page; }
.admin-top-pages-list li {
  counter-increment: admin-page;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  font-size: 0.85rem;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--border-soft);
}
.admin-top-pages-list li:last-child { border-bottom: none; padding-bottom: 0; }
.admin-top-pages-list li::before {
  content: counter(admin-page);
  flex-shrink: 0;
  width: 20px; height: 20px;
  border-radius: 50%;
  background: var(--primary-light);
  color: var(--primary);
  font-size: 0.7rem;
  font-weight: 700;
  display: flex; align-items: center; justify-content: center;
}
.admin-page-path { flex: 1; color: var(--text-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; direction: ltr; text-align: left; }
.admin-page-views { font-family: var(--font-mono); font-size: 0.78rem; color: var(--text-secondary); white-space: nowrap; }

/* ---- Tunnel de conversion ---- */
.admin-funnel { display: flex; flex-direction: column; gap: 14px; }
.admin-funnel-row { display: grid; grid-template-columns: 180px 1fr 56px; align-items: center; gap: 12px; }
@media (max-width: 620px) { .admin-funnel-row { grid-template-columns: 1fr; gap: 4px; } }
.admin-funnel-label { font-size: 0.85rem; color: var(--text-primary); }
.admin-funnel-track { height: 26px; background: var(--surface-subtle); border-radius: var(--radius-sm); overflow: hidden; }
.admin-funnel-fill {
  height: 100%;
  min-width: fit-content;
  background: linear-gradient(90deg, var(--primary), var(--primary-hover));
  color: #fff;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  font-weight: 700;
  display: flex; align-items: center;
  padding: 0 10px;
  border-radius: var(--radius-sm);
  white-space: nowrap;
}
.admin-funnel-pct { font-family: var(--font-mono); font-size: 0.8rem; color: var(--text-secondary); text-align: right; }
.admin-funnel-drop {
  display: inline-block;
  margin-left: 8px;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 700;
  color: var(--red);
  background: var(--red-bg);
  border: 1px solid var(--red);
  border-radius: 999px;
  padding: 1px 8px;
  vertical-align: middle;
}

/* ---- Messages ---- */
/* overflow-y seul suffit en théorie, mais la spec CSS force le calcul de
   overflow-x à "auto" dès que overflow-y n'est pas "visible" (voir CSS2.1
   §11.1.1) : sans quoi un message contenant une chaîne sans espace (URL
   collée, texte copié-collé sans retour à la ligne) déborde silencieusement
   de sa carte et fait apparaître un scroll horizontal sur tout l'onglet.
   overflow-wrap: anywhere sur .admin-message-body règle la cause (le texte
   casse proprement dans la carte) plutôt que le symptôme. */
.admin-messages-list { display: flex; flex-direction: column; gap: 14px; max-height: 70vh; overflow-y: auto; }
.admin-message { border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 14px 16px; min-width: 0; }
.admin-message-top { display: flex; flex-wrap: wrap; gap: 8px 14px; align-items: baseline; margin-bottom: 8px; font-size: 0.85rem; }
.admin-message-top strong { color: var(--text-primary); }
.admin-message-top a { color: var(--primary); overflow-wrap: anywhere; }
.admin-message-lang, .admin-message-date { color: var(--text-tertiary); font-size: 0.78rem; }
.admin-message-body { margin: 0; color: var(--text-primary); font-size: 0.92rem; line-height: 1.5; white-space: pre-wrap; overflow-wrap: anywhere; }
.admin-message-page { margin-top: 8px; font-size: 0.76rem; color: var(--text-tertiary); overflow-wrap: anywhere; }
.admin-message--unread { border-left: 3px solid var(--primary); background: var(--primary-light); }
.admin-message-mark-read { margin-left: auto; }
.admin-message-read-tag { margin-left: auto; color: var(--text-tertiary); font-size: 0.78rem; }

/* ---- Derniers achats confirmés (table `purchases`, voir functions/lib/purchases.js) ---- */
.admin-purchases-list { display: flex; flex-direction: column; gap: 2px; max-height: 50vh; overflow-y: auto; }
.admin-purchase-row {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 6px 14px;
  padding: 10px 4px;
  border-bottom: 1px solid var(--border-soft);
  font-size: 0.88rem;
}
.admin-purchase-row:last-child { border-bottom: none; }
.admin-purchase-email { color: var(--primary); font-weight: 600; word-break: break-all; }
.admin-purchase-amount { font-family: var(--font-mono); color: var(--text-primary); font-weight: 600; }
.admin-purchase-date { margin-left: auto; color: var(--text-tertiary); font-size: 0.78rem; }
@media (max-width: 480px) { .admin-purchase-date { margin-left: 0; } }

/* ---- Onglet "Adresses" : liste + formulaire création/édition ---- */

.admin-spots-toolbar { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 10px; }
.admin-spots-search {
  flex: 1 1 220px;
  font-family: inherit;
  font-size: 16px; /* évite le zoom auto au focus sur iOS Safari */
  padding: 9px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text-primary);
}
.admin-spots-filter {
  font-family: inherit;
  font-size: 16px;
  padding: 9px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text-primary);
}
.admin-spots-toolbar .btn { flex-shrink: 0; }
@media (max-width: 640px) {
  .admin-spots-toolbar { flex-direction: column; }
  /* .admin-spots-search garde flex:1 1 220px (basis) pour le layout en ligne
     du desktop : une fois le conteneur passé en colonne ci-dessus, ce basis
     s'applique à la hauteur au lieu de la largeur et gonfle le champ à
     220px de haut. flex:0 0 auto annule ça, width:100% prend le relais. */
  .admin-spots-search,
  .admin-spots-filter,
  .admin-spots-toolbar .btn { flex: 0 0 auto; width: 100%; min-height: 44px; }
}

/* ---------- Onglet admin "Journées" : photo par parcours ---------- */
.admin-itineraries-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 18px;
}
.admin-itin-card {
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--surface);
}
.admin-itin-card .itinerary-media { aspect-ratio: 16 / 9; }
.admin-itin-body { padding: 14px 16px 16px; }
.admin-itin-body h3 { font-size: 0.98rem; margin-bottom: 2px; }
.admin-itin-zone { color: var(--text-tertiary); font-size: 0.8rem; margin-bottom: 12px; }
.admin-itin-actions { display: flex; flex-wrap: wrap; gap: 8px; }
.admin-itin-upload-btn { cursor: pointer; white-space: nowrap; }

.admin-spots-list { display: flex; flex-direction: column; gap: 8px; max-height: 65vh; overflow-y: auto; }
/* Toute la ligne est un <button> (pas juste un petit lien "Modifier") : cible
   tactile confortable sur mobile, un tap n'importe où sur l'adresse ouvre
   l'édition — pattern standard de liste mobile. */
.admin-spot-row {
  display: flex;
  align-items: center;
  width: 100%;
  gap: 12px;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  font-family: inherit;
  text-align: left;
  cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease;
}
.admin-spot-row:hover { background: var(--surface-subtle); }
.admin-spot-row:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
.admin-spot-row-chevron { flex-shrink: 0; color: var(--text-tertiary); font-size: 1.1rem; line-height: 1; }
.admin-spot-row-icon {
  width: 36px;
  height: 36px;
  flex-shrink: 0;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1rem;
  background: color-mix(in srgb, var(--fam-color, #6b7280) 16%, white);
}
.admin-spot-row-thumb {
  width: 36px;
  height: 36px;
  flex-shrink: 0;
  border-radius: 50%;
  object-fit: cover;
}
.admin-spot-row-main { display: flex; flex-direction: column; gap: 2px; flex: 1; min-width: 0; }
.admin-spot-row-main strong { font-size: 0.9rem; color: var(--text-primary); }
.admin-spot-row-sub {
  font-size: 0.78rem;
  color: var(--text-tertiary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.admin-spot-row-badge {
  font-size: 0.72rem;
  color: var(--text-secondary);
  background: var(--surface-subtle);
  padding: 3px 9px;
  border-radius: 999px;
  white-space: nowrap;
  flex-shrink: 0;
}
.admin-spot-row-badge--free { color: var(--green, #1a7f4b); background: var(--green-bg, #eafaf0); }

/* Le <dialog> natif gère déjà la boîte + le fond assombri (::backdrop) :
   pas besoin de recoder l'overlay/le focus trap à la main. */
.admin-spot-dialog {
  border: none;
  border-radius: var(--radius-md);
  padding: 0;
  width: min(760px, calc(100vw - 32px));
  max-height: min(88vh, 820px);
  box-shadow: var(--shadow-hover);
}
.admin-spot-dialog::backdrop { background: rgba(24, 24, 22, 0.5); }
.admin-spot-dialog[open] { display: flex; flex-direction: column; }
.admin-spot-dialog form { display: flex; flex-direction: column; min-height: 0; }

.admin-spot-dialog-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 18px 22px;
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}
.admin-spot-dialog-head h2 { margin: 0; font-size: 1.1rem; }
.admin-spot-close {
  background: none;
  border: none;
  font-size: 1.1rem;
  color: var(--text-secondary);
  cursor: pointer;
  min-width: 40px;
  min-height: 40px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-sm);
}
.admin-spot-close:hover { background: var(--surface-subtle); color: var(--text-primary); }

.admin-spot-dialog-body {
  display: grid;
  grid-template-columns: 1.2fr 1fr;
  gap: 20px;
  padding: 20px 22px;
  overflow-y: auto;
}
@media (max-width: 640px) { .admin-spot-dialog-body { grid-template-columns: 1fr; } }
/* Une colonne de grille a par défaut min-width:auto, pas 0 : elle refuse de
   rétrécir sous la largeur intrinsèque de son contenu (ici, le champ
   Longitude), qui déborde alors silencieusement du dialogue au lieu de se
   redimensionner. min-width:0 laisse la grille faire son travail normalement. */
.admin-spot-dialog-body > * { min-width: 0; }

.admin-spot-fields { display: flex; flex-direction: column; gap: 14px; }
.admin-spot-fields select {
  font-family: inherit;
  font-size: 16px;
  padding: 11px 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text-primary);
}
/* .form-row input/textarea (partagé avec contact.html, restaurer-acces.html…)
   est à 0.95rem : trop peu pour éviter le zoom auto au focus sur iOS Safari.
   On ne touche pas la règle globale (utilisée ailleurs sur le site public),
   seulement ces champs-ci, dans le formulaire admin. */
.admin-spot-dialog .form-row input,
.admin-spot-dialog .form-row textarea { font-size: 16px; }
.form-row-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.form-row-pair > * { min-width: 0; }
@media (max-width: 480px) { .form-row-pair { grid-template-columns: 1fr; } }
.admin-field-hint { margin: 2px 0 0; font-size: 0.76rem; color: var(--text-tertiary); }

/* Champ photo : URL à coller, ou upload direct (voir handleImageFileSelect()
   dans js/admin-spots.js — stocké sur R2, servi via /api/images/:key). Le
   rendu réel (vraie image ou dégradé de secours) se voit dans l'aperçu de
   fiche complet ci-dessous, pas ici : plus la peine d'une vignette séparée. */
.admin-spot-image-controls { display: flex; gap: 8px; align-items: center; }
.admin-spot-image-controls input { flex: 1; min-width: 0; }
.admin-spot-upload-btn { flex-shrink: 0; white-space: nowrap; cursor: pointer; }
@media (max-width: 480px) { .admin-spot-image-controls { flex-direction: column; align-items: stretch; } }

.admin-checkbox-row {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 0.88rem;
  color: var(--text-primary);
  cursor: pointer;
}

/* Aperçu complet de la fiche (voir renderCardPreview() dans
   js/admin-spots.js) : reconstitue le vrai rendu de /spots.html avec les
   mêmes classes .spot-card/.spot-media/… — jamais une maquette approchée,
   toujours le composant réel, pour que l'aperçu ne mente jamais. */
.admin-spot-preview-block { margin-bottom: 18px; }
.admin-spot-preview-block > label { font-size: 0.86rem; font-weight: 600; color: var(--text-primary); }
.admin-spot-preview-wrap {
  margin-top: 8px;
  padding: 16px;
  background: var(--surface-subtle);
  border-radius: var(--radius-md);
  display: flex;
  justify-content: center;
}
.admin-spot-preview-card { width: 100%; max-width: 300px; }

.admin-spot-map-col { display: flex; flex-direction: column; gap: 6px; }
.admin-spot-map-col > label { font-size: 0.86rem; font-weight: 600; color: var(--text-primary); }
.admin-spot-map {
  width: 100%;
  height: 220px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  margin-bottom: 8px;
}

.admin-spot-dialog-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 16px 22px;
  border-top: 1px solid var(--border);
  flex-shrink: 0;
}
.admin-spot-dialog-actions-right { display: flex; gap: 10px; }
.admin-spot-delete-btn { color: var(--red, #b3261e); }
#admin-spot-status { margin: 0 22px; }
#admin-spot-status:not(:empty) { padding: 0 0 4px; }

/* Mobile : "Supprimer cette adresse" + "Annuler" + "Enregistrer" sur une
   seule ligne ne tenait pas (débordement/retour à la ligne moche). On empile
   en pleine largeur, avec l'action principale (Enregistrer) tout en haut —
   la plus accessible au pouce — et la suppression, destructive, tout en bas
   et discrète pour limiter les taps accidentels. */
@media (max-width: 640px) {
  .admin-spot-dialog-actions { flex-direction: column-reverse; align-items: stretch; gap: 8px; }
  .admin-spot-dialog-actions-right { flex-direction: column-reverse; width: 100%; gap: 8px; }
  .admin-spot-dialog-actions-right .btn { width: 100%; }
  .admin-spot-delete-btn { width: 100%; text-align: center; border-color: transparent; background: none; }
}

/* ============================================================
   Impression du guide complet (/mon-espace.html → "Générer mon PDF")
   Aucune librairie : #print-root est rempli en JS (voir js/mon-espace.js)
   avec tout le contenu débloqué, cache à l'écran, révélé uniquement ici via
   @media print, pendant que le reste de la page (nav, footer, boutons)
   disparaît. window.print() ouvre ensuite la boîte de dialogue du
   navigateur — "Enregistrer en PDF" comme destination fait le téléchargement.
   ============================================================ */

.print-only { display: none; }

@media print {
  .site-header, main, footer, .sticky-cta, .toast { display: none !important; }
  #print-root.print-only { display: block !important; }

  @page { margin: 16mm 14mm; }

  html, body { background: #fff; }

  .print-page { font-family: Georgia, 'Times New Roman', serif; color: #16160f; font-size: 12px; }

  .print-header { text-align: center; margin-bottom: 28px; padding-bottom: 18px; border-bottom: 2px solid #16160f; }
  .print-logo { font-family: var(--font-sans); font-weight: 700; font-size: 1rem; letter-spacing: -0.01em; margin-bottom: 12px; }
  .print-logo span { color: var(--primary); }
  .print-header h1 { font-family: var(--font-sans); font-size: 1.6rem; margin: 0 0 6px; }
  .print-subtitle { margin: 0 0 16px; font-size: 0.95rem; color: #444; }
  .print-meta { margin: 12px 0 0; font-size: 0.75rem; color: #777; font-family: var(--font-sans); }

  /* Petite ligne de stats façon "carte de visite" : 66 adresses, 6 journées —
     donne tout de suite une idée du volume du guide avant de le feuilleter. */
  .print-stats { display: flex; justify-content: center; gap: 40px; }
  .print-stat { display: flex; flex-direction: column; align-items: center; font-family: var(--font-sans); }
  .print-stat strong { font-size: 1.6rem; font-weight: 700; color: var(--primary); line-height: 1.1; }
  .print-stat span { font-size: 0.72rem; color: #777; text-transform: uppercase; letter-spacing: 0.03em; margin-top: 2px; }

  .print-page h2 {
    font-family: var(--font-sans);
    font-size: 1.2rem;
    border-bottom: 2px solid #16160f;
    padding-bottom: 6px;
    margin: 30px 0 16px;
    break-after: avoid;
  }

  /* Sommaire par famille en tête de la section adresses : nombre d'adresses
     par catégorie, avec la même couleur d'accent que les fiches ci-dessous. */
  .print-toc {
    list-style: none;
    margin: 0 0 24px;
    padding: 0;
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 6px 24px;
    break-inside: avoid;
  }
  .print-toc li {
    display: flex;
    justify-content: space-between;
    align-items: center;
    font-family: var(--font-sans);
    font-size: 0.82rem;
    font-weight: 600;
    padding: 4px 0 4px 10px;
    border-inline-start: 3px solid;
  }
  .print-toc-count { font-family: var(--font-mono); color: #777; font-weight: 400; }

  .print-group { break-inside: avoid; margin-bottom: 18px; }
  .print-group h3 { font-family: var(--font-sans); font-size: 1rem; margin: 0 0 10px; }

  .print-spot-list { display: grid; grid-template-columns: repeat(2, 1fr); gap: 3px 24px; }
  .print-spot {
    break-inside: avoid;
    padding: 8px 0 8px 10px;
    margin-bottom: 2px;
    border-bottom: 1px dotted #ccc;
    border-inline-start: 3px solid;
  }
  .print-spot-name { font-weight: 700; font-size: 0.92rem; }
  .print-spot-detail { font-size: 0.78rem; color: #555; font-family: var(--font-sans); }
  .print-spot-note { font-size: 0.78rem; color: #555; font-style: italic; margin-top: 3px; font-family: var(--font-sans); }

  .print-itinerary { position: relative; }
  .print-duration-badge {
    font-family: var(--font-sans);
    font-size: 0.68rem;
    font-weight: 700;
    color: var(--primary);
    background: var(--primary-light);
    border-radius: 999px;
    padding: 2px 9px;
    white-space: nowrap;
  }
  .print-itinerary-teaser { font-style: italic; color: #555; font-size: 0.85rem; margin: 0 0 8px; }
  .print-tip {
    background: var(--amber-bg);
    border-inline-start: 3px solid var(--amber);
    padding: 6px 10px;
    margin: 0 0 10px;
    font-size: 0.82rem;
    font-family: var(--font-sans);
    color: #4a3006;
  }
  .print-itinerary-list { list-style: none; margin: 0; padding: 0; font-size: 0.86rem; }
  .print-itinerary-list li { padding: 4px 0; }
  .print-time { font-family: var(--font-mono); font-weight: 700; color: var(--primary); margin-inline-end: 6px; }

  .print-footer { margin-top: 30px; padding-top: 12px; border-top: 1px solid #ccc; text-align: center; font-size: 0.7rem; color: #888; font-family: var(--font-sans); }
}

/* ---------- Home : "3 volets du guide" (adresses / journées / astuces) ----------
   Bloc juste après le hero pour expliquer d'un coup d'œil ce qu'est le site :
   un seul guide, trois façons de s'en servir — dont Astuces, gratuit, qui en
   fait partie au même titre que les adresses et les journées payantes. */

.pillar-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 18px;
}
@media (max-width: 760px) { .pillar-grid { grid-template-columns: 1fr; } }

.pillar-card {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 24px 22px;
  box-shadow: var(--shadow);
  text-decoration: none;
  color: inherit;
  transition: transform 0.15s ease, box-shadow 0.15s ease, border-color 0.15s ease;
}
.pillar-card:hover { transform: translateY(-3px); box-shadow: var(--shadow-hover); border-color: var(--text-disabled); }

.pillar-icon {
  width: 46px;
  height: 46px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.35rem;
  margin-bottom: 16px;
}
.pillar-card--spots .pillar-icon { background: var(--primary-light); }
.pillar-card--planning .pillar-icon { background: var(--amber-bg); }
.pillar-card--tips .pillar-icon { background: var(--green-bg); }

.pillar-card h3 { font-size: 1.05rem; margin-bottom: 6px; letter-spacing: -0.01em; }
.pillar-card p { font-size: 0.87rem; color: var(--text-secondary); margin: 0 0 14px; }
.pillar-link { margin-top: auto; font-size: 0.85rem; font-weight: 700; color: var(--primary); }
.pillar-card:hover .pillar-link { text-decoration: underline; }
.pillar-card--tips .pillar-link { color: var(--green); }
.pillar-card--planning .pillar-link { color: var(--amber); }

/* ---------- Home : bandeau de mots-clés cible, section "Pour qui" ---------- */

.audience-tags {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px;
  margin: 18px 0 30px;
}
.audience-tag {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 7px 14px;
  border-radius: 999px;
  background: var(--primary-light);
  border: 1px solid var(--primary-dim);
  color: var(--primary-hover);
  font-size: 0.82rem;
  font-weight: 700;
  white-space: nowrap;
}

/* ---------- Home : aperçu freemium (2 adresses en clair + 1 floutée) ----------
   Réutilise telles quelles les classes .spot-card / .spot-media /
   .spot-card.locked / .spot-card-blur-body / .spot-lock-panel de
   /spots.html (voir plus haut dans ce fichier) — un seul design de carte
   dans tout le site, image/dégradé compris. 3 cartes seulement (pas 5) :
   la grille hérite de .card-grid (3 colonnes de base, mêmes seuils de
   bascule 2/1 colonne que /spots.html) plutôt que de forcer 5 colonnes
   dans la même largeur — chaque carte respire au lieu de se tasser. Un
   écart un peu plus généreux qu'ailleurs, pour une section vitrine plutôt
   qu'une liste dense. */

.card-grid--teaser { gap: 24px; }

/* ---------- Home : aperçu des journées (mêmes principes que le teaser
   adresses juste au-dessus — 3 cartes qui donnent envie, pas la liste
   complète). Réutilise .itinerary-media telle quelle (voir plus haut, même
   dégradé pastel + icône que sur /planning.html et que .spot-media--fallback
   sur les fiches adresses — une seule identité visuelle "pas de vraie photo"
   dans tout le site) et .itinerary-badge* pour la pastille durée. Le corps
   est volontairement allégé par rapport à la vraie carte de /planning.html :
   pas le programme minute par minute ici, juste de quoi donner envie de
   cliquer — le détail complet reste la raison d'aller sur /planning.html. */
.card-grid--itin-preview { gap: 24px; }
.itin-preview-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  overflow: hidden;
  box-shadow: var(--shadow);
  display: flex;
  flex-direction: column;
  transition: box-shadow 0.15s ease, border-color 0.15s ease;
}
.itin-preview-card:hover { box-shadow: var(--shadow-hover); border-color: var(--text-disabled); }
.itin-preview-body { padding: 18px 20px 20px; display: flex; flex-direction: column; flex: 1; }
.itin-preview-body h3 { font-size: 1.05rem; letter-spacing: -0.01em; margin: 10px 0 4px; }
.itin-preview-zone { color: var(--primary); font-weight: 600; font-size: 0.8rem; margin-bottom: 8px; }
.itin-preview-desc { color: var(--text-secondary); font-size: 0.92rem; margin-bottom: 16px; flex: 1; }
.itin-preview-link { font-weight: 700; font-size: 0.9rem; color: var(--primary); align-self: flex-start; }
.itin-preview-link:hover { text-decoration: underline; }

/* ---------- Simulateur de budget (/budget-marseille.html) ----------
   Carte unique qui regroupe les entrées (budget/jours/voyageurs) et le
   résultat live, dans le même conteneur, plutôt qu'un formulaire séparé du
   résultat : sur un outil "ludique" comme demandé, l'utilisateur doit sentir
   que ça répond tout de suite, pas remonter les yeux vers une autre carte
   pour voir l'effet de son réglage. Réutilise .filter-chip (déjà stylé sur
   /spots.html) pour les boutons jours/voyageurs : un seul système de puce
   dans tout le site plutôt qu'un composant redondant. */
.budget-tool {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow);
  padding: 28px 26px;
}
.budget-input-block { margin-bottom: 26px; }
.budget-input-block:last-of-type { margin-bottom: 0; }
.budget-input-label {
  display: block;
  font-size: 0.86rem;
  font-weight: 700;
  color: var(--text-secondary);
  margin-bottom: 10px;
}
.budget-amount-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: 10px;
}
.budget-amount-label { font-size: 0.86rem; font-weight: 700; color: var(--text-secondary); }
.budget-amount-value {
  font-family: var(--font-mono);
  font-size: 2rem;
  font-weight: 500;
  color: var(--primary);
  font-variant-numeric: tabular-nums;
}

/* Curseur natif restylé plutôt qu'un composant JS : moins de poids, and un
   comportement clavier/tactile gratuit. Les pastilles ::-webkit/::-moz sont
   dupliquées par nécessité (pas de syntaxe commune), mêmes valeurs des deux
   côtés pour un rendu identique Chrome/Firefox/Safari. */
.budget-slider {
  width: 100%;
  height: 8px;
  border-radius: 999px;
  background: linear-gradient(90deg, var(--primary) 0%, var(--primary) var(--fill, 15%), var(--surface-subtle) var(--fill, 15%), var(--surface-subtle) 100%);
  appearance: none;
  -webkit-appearance: none;
  outline: none;
  cursor: pointer;
}
.budget-slider::-webkit-slider-thumb {
  appearance: none;
  -webkit-appearance: none;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--primary);
  border: 3px solid var(--surface);
  box-shadow: var(--shadow-hover);
  cursor: pointer;
}
.budget-slider::-moz-range-thumb {
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--primary);
  border: 3px solid var(--surface);
  box-shadow: var(--shadow-hover);
  cursor: pointer;
}
.budget-slider::-moz-range-track { height: 8px; border-radius: 999px; background: var(--surface-subtle); }
.budget-slider::-moz-range-progress { height: 8px; border-radius: 999px; background: var(--primary); }

.budget-chip-row { display: flex; flex-wrap: wrap; gap: 8px; }
.budget-chip-row .filter-chip { padding: 9px 16px; font-size: 0.86rem; }

.budget-result {
  margin-top: 28px;
  padding-top: 24px;
  border-top: 1px solid var(--border-soft);
}
.budget-verdict {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  margin-bottom: 18px;
}
.budget-verdict-emoji {
  font-size: 2.1rem;
  line-height: 1;
  flex-shrink: 0;
}
.budget-verdict-title { font-size: 1.15rem; font-weight: 800; letter-spacing: -0.01em; margin-bottom: 4px; }
.budget-verdict-text { font-size: 0.9rem; color: var(--text-secondary); line-height: 1.55; margin: 0; }

/* Barre unique segmentée : vue d'ensemble en un coup d'œil, avant le détail
   poste par poste juste en dessous (voir dataviz : macro puis détail). */
.budget-stacked-bar {
  display: flex;
  height: 12px;
  border-radius: 999px;
  overflow: hidden;
  background: var(--surface-subtle);
  margin-bottom: 20px;
}
.budget-stacked-seg { height: 100%; transition: width 0.2s ease; }

.budget-breakdown { display: flex; flex-direction: column; gap: 16px; }
.budget-breakdown-head { display: flex; align-items: baseline; justify-content: space-between; margin-bottom: 6px; }
.budget-breakdown-label { font-size: 0.92rem; font-weight: 700; }
.budget-breakdown-amount { font-family: var(--font-mono); font-size: 0.95rem; font-weight: 500; }
.budget-breakdown-track { height: 6px; border-radius: 999px; background: var(--surface-subtle); overflow: hidden; }
.budget-breakdown-fill { height: 100%; border-radius: 999px; transition: width 0.2s ease; }
.budget-breakdown-pct { font-size: 0.78rem; color: var(--text-tertiary); margin-top: 5px; }

/* CTA de conversion : variante pleine couleur du même encart que
   .guide-promo--final (voir plus haut, bas d'article de blog) — cohérence
   visuelle avec le reste du site plutôt qu'un nouveau style de carte, mais
   sa propre classe pour un suivi analytics distinct (voir CTA_SELECTORS
   dans js/analytics.js, label "budget_tool"). */
.budget-cta {
  position: relative;
  background: linear-gradient(155deg, var(--primary) 0%, #173e94 100%);
  border-radius: var(--radius-md);
  padding: 30px 28px;
  margin-top: 24px;
  box-shadow: var(--shadow);
  text-align: center;
}
.budget-cta-eyebrow {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  font-size: 0.76rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: #cfe0fb;
  margin-bottom: 12px;
}
.budget-cta-stat {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 700;
  color: #fff;
  background: rgba(255, 255, 255, 0.14);
  border: 1px solid rgba(255, 255, 255, 0.3);
  border-radius: 999px;
  padding: 3px 10px;
  white-space: nowrap;
}
.budget-cta h3 { color: #fff; font-size: 1.25rem; margin: 0 0 10px; letter-spacing: -0.01em; }
.budget-cta p { color: #dbe6fb; font-size: 0.92rem; line-height: 1.6; max-width: 46ch; margin: 0 auto 20px; }
/* Aperçu de 2 vraies adresses (voir js/budget-tool.js, #budget-cta-spots) :
   rend le CTA concret plutôt qu'un simple texte de promesse — vide et sans
   marge tant que /api/spots n'a pas répondu ou n'a rien à proposer. */
.budget-cta .spot-list { margin: 0 auto 22px; gap: 8px; }
.budget-cta .btn-primary { background: #fff; color: var(--primary); border-color: #fff; }
.budget-cta .btn-primary:hover { background: #eef1ff; }

/* Table de référence (3 profils × 4 postes), sous l'outil : contenu texte
   pour le SEO et pour qui préfère lire un tableau plutôt que bouger un
   curseur. overflow-x: auto sur son propre conteneur seulement — jamais la
   page entière qui doit défiler horizontalement (voir le bug corrigé sur
   .admin-messages-list plus haut, même principe appliqué ici dès le départ). */
.budget-table-wrap { overflow-x: auto; margin: 24px 0; border: 1px solid var(--border); border-radius: var(--radius-md); }
.budget-table { width: 100%; border-collapse: collapse; font-size: 0.88rem; min-width: 480px; }
.budget-table th, .budget-table td { padding: 12px 16px; text-align: left; border-bottom: 1px solid var(--border-soft); white-space: nowrap; }
.budget-table th { background: var(--surface-subtle); font-weight: 700; color: var(--text-secondary); font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.03em; }
.budget-table td:first-child { font-weight: 600; }
.budget-table tr:last-child td { border-bottom: none; font-weight: 700; }
html[dir="rtl"] .budget-table th, html[dir="rtl"] .budget-table td { text-align: right; }

/* "L'essentiel en un coup d'œil" : les 4 faits clés du calculateur, en tête
   de page, avant même le curseur. Objectif double : un lecteur pressé (ou
   un extrait enrichi / une réponse d'IA) repart avec l'essentiel sans
   toucher à l'outil, et le sélecteur ciblé par le schema WebPage/speakable
   (voir #essentiel dans le <head>) a un contenu concis et factuel à citer. */
.budget-tldr {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 10px;
  margin: 22px 0;
}
.budget-tldr-item {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 14px 16px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.budget-tldr-item strong { font-size: 0.86rem; }
.budget-tldr-item span { font-size: 0.82rem; color: var(--text-secondary); line-height: 1.4; }

@media (max-width: 600px) {
  .budget-tool { padding: 22px 18px; }
  .budget-amount-value { font-size: 1.6rem; }
  .budget-verdict { flex-direction: column; gap: 8px; }
}
