/* Page d'accueil — Résidence Majorelle
   Sections propres à index.html : hero, avancée du chantier, services,
   galerie, localisation, partenaires, typologies et formulaire de contact. */

/* ── Rideau d'ouverture ─────────────────────────────────── */

/* Sans l'attribut posé par assets/js/intro.js, le rideau n'existe pas : la
   page est directement là. C'est le sens de la règle — un voile qui
   s'afficherait par défaut dépendrait d'un script pour partir, et bloquerait
   le site le jour où ce script manque. */
.intro { display: none; }

/* Le rideau n'anime qu'opacity et transform : le navigateur joue ces
   animations hors du fil principal, qu'un script occupé ne fige pas. Or
   le runtime tient justement le navigateur au chargement, une demi-seconde
   et plus — menée par des minuteurs JS, la frappe sautait des lettres et
   la levée restait à mi-hauteur. */
[data-intro] .intro {
  --intro-pas: 25ms;     /* entre deux lettres : le nom s'écrit en 0,5 s */
  position: fixed;
  inset: 0;
  z-index: 200;
  display: grid;
  place-items: center;
  background: var(--surface-inverse);
}
/* Le temps du rideau, la page dessous ne défile pas. */
[data-intro="play"] body { overflow: hidden; }

/* La levée part quand intro.js passe data-intro à « done » : le nom écrit
   et la page rendue dessous — sans quoi le rideau s'ouvrirait sur un écran
   vide. Le mouvement de rideau de la charte (--ease-curtain), pour une
   fois employé au pied de la lettre, mais en 400 ms plutôt que les 600 de
   --duration-curtain. Les clics rejoignent déjà la page ; une fois en
   haut, intro.js retire le bloc. */
[data-intro="done"] .intro {
  pointer-events: none;
  animation: intro-lever 400ms var(--ease-curtain) forwards;
}
@keyframes intro-lever {
  to { transform: translateY(-100%); }
}

.intro__name {
  margin: 0;
  padding-inline: var(--gutter-page);
  white-space: nowrap;
  font-family: var(--font-display);
  font-weight: var(--weight-display);
  font-size: clamp(1.5rem, 6.5vw, 4.5rem);
  letter-spacing: -0.01em;
  color: var(--text-on-dark);
}

/* Chaque lettre paraît à son rang. Toutes sont posées dès le départ : le
   nom ne glisse pas vers la gauche à mesure qu'il s'écrit.
   Les animations du rideau remplissent avant et après (both) : leur état
   de repos est écrit dans l'animation elle-même, que le navigateur tient
   seul. Laissé au style ordinaire, il attendrait que le fil principal se
   libère — un curseur restait ainsi allumé derrière la frappe. */
.intro__name > span {
  position: relative;
  animation: intro-lettre 1ms calc(var(--i) * var(--intro-pas)) both;
}
@keyframes intro-lettre {
  from { opacity: 0; }
  to { opacity: 1; }
}

/* Le curseur de frappe, un trait d'or : chaque lettre porte le sien, visible
   le seul temps où elle est la dernière écrite — il semble ainsi avancer
   avec la frappe. Avant son tour, il est allumé mais sa lettre, encore
   transparente, le cache. Celui de la dernière lettre bat tant que le
   rideau attend la page, et tous s'effacent quand il se lève. */
.intro__name i {
  position: absolute;
  left: calc(100% + 0.08em);
  bottom: 0.2em;          /* sur la ligne de base */
  width: 2px;
  height: 0.88em;
  background: var(--gold-400);
  animation: intro-curseur var(--intro-pas) steps(1, end) calc(var(--i) * var(--intro-pas)) both;
}
.intro__name > span:last-child i {
  animation: intro-battement 0.9s linear calc(var(--i) * var(--intro-pas)) infinite both;
}
[data-intro="done"] .intro__name i { animation: none; opacity: 0; }
@keyframes intro-curseur {
  from { opacity: 1; }
  to { opacity: 0; }
}
@keyframes intro-battement {
  0%, 49.9% { opacity: 1; }
  50%, 100% { opacity: 0; }
}

/* ── Hero ───────────────────────────────────────────────── */

.hero {
  position: relative;
  isolation: isolate;   /* le fond reste sous le texte, jamais sous la page */
  background: var(--surface-inverse);
  color: var(--text-on-dark);
  --heading-color: currentColor;
  display: flex;
  flex-direction: column;
  padding-inline: var(--gutter-page);
  /* La vidéo tenait la hauteur du hero ; le fond photo n'en impose aucune,
     c'est donc ici qu'elle se règle. `svh` vise la fenêtre barre d'adresse
     dépliée : sur mobile, le bandeau de chiffres reste atteignable d'un
     geste. La ligne `vh` au-dessus sert de repli. */
  min-height: clamp(560px, 86vh, 880px);
  min-height: clamp(560px, 86svh, 880px);
}

/* ── Fond photo du hero ─────────────────────────────────── */

/* Les vues sont empilées au même endroit ; une seule est opaque à la fois. */
.hero__backdrop {
  position: absolute;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  /* Noir neutre, pas le brun de la charte : c'est le fond que l'on aperçoit
     le temps du chargement et pendant le fondu d'une vue à l'autre, et il ne
     doit pas teinter la photo. */
  background: #000;
}
.hero__shot {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  /* La photo est assombrie à la source plutôt que recouverte d'un voile
     teinté : les couleurs restent celles du cliché, seule la luminosité
     baisse. C'est le réglage à toucher si le hero est trop sombre ou trop
     clair — rien d'autre n'en dépend. */
  filter: brightness(var(--hero-brightness, 0.42));
  /* Deux mouvements de même durée et de même retard, séparés parce qu'ils
     n'ont pas la même allure : le fondu s'adoucit à ses deux bouts, le
     dézoom avance d'un pas régulier. Mélangés dans une seule animation, le
     dézoom hériterait de l'adoucissement et semblerait s'arrêter en route. */
  animation:
    hero-fade var(--hero-cycle) var(--hero-ease) infinite,
    hero-zoom var(--hero-cycle) var(--hero-zoom-ease) infinite;
}
/* Trois vues, trois crans d'un tiers de tour, tous décalés d'un fondu en
   arrière : la première vue est ainsi déjà posée au premier rendu, et elle
   entame son dézoom sans faire passer un noir. Ajouter une vue = un cran de
   plus ici, et des paliers recalculés dans les deux @keyframes. */
.hero__backdrop {
  --hero-cycle: 21s;
  /* Un tiers du tour par vue, soit sept secondes chacune. */
  --hero-step: calc(var(--hero-cycle) / 3);
  --hero-lead: calc(var(--hero-cycle) * 0.04);   /* la durée d'un fondu */
  --hero-ease: ease-in-out;
  /* Échelle d'arrivée de la photo : elle redescend à 1 sur la première moitié
     de son passage. C'est l'ampleur du dézoom. */
  --hero-zoom: 1.16;
  /* Décélération douce : la photo part vite et se pose sans à-coup, la courbe
     accostant à vitesse nulle. Volontairement plus plate que le jeton
     --ease-decelerate, taillé pour des transitions de deux dixièmes : sur
     quatre secondes, il dépenserait presque tout le mouvement pendant le
     fondu d'entrée, et la vue paraîtrait déjà posée en devenant lisible. */
  --hero-zoom-ease: ease-out;
}
.hero__shot:nth-child(1) { animation-delay: calc(0s                   - var(--hero-lead)); }
.hero__shot:nth-child(2) { animation-delay: calc(var(--hero-step)     - var(--hero-lead)); }
.hero__shot:nth-child(3) { animation-delay: calc(var(--hero-step) * 2 - var(--hero-lead)); }

/* Une vue paraît en quatre pour cent — un peu plus de huit dixièmes sur les
   vingt-et-une secondes du tour —, tient son tiers, puis cède la place dans
   le même temps. Le tiers suivant démarre pile sur ce fondu : les deux vues
   se croisent. */
@keyframes hero-fade {
  0%      { opacity: 0; }
  4%      { opacity: 1; }
  33.333% { opacity: 1; }
  37.333% { opacity: 0; }
  100%    { opacity: 0; }
}

/* Le dézoom : la photo arrive élargie et se pose sur sa taille normale à
   20 % du tour, soit un peu plus de quatre secondes — le mouvement se joue
   donc à l'arrivée, pas sur tout le passage, et la vue reste ensuite immobile
   jusqu'à son effacement. Elle reprend sa valeur d'entrée juste après, hors
   d'écran : d'où le palier collé à 37,333 %, qui empêche de voir le retour.
   Le débord est rogné par .hero__backdrop (overflow: hidden).
   Les paliers ne peuvent pas être calculés — une variable n'a pas cours dans
   un sélecteur de @keyframes —, ils suivent donc --hero-cycle à la main. */
@keyframes hero-zoom {
  0%       { transform: scale(var(--hero-zoom)); }
  20%      { transform: scale(1); }
  37.333%  { transform: scale(1); }
  37.334%  { transform: scale(var(--hero-zoom)); }
  100%     { transform: scale(var(--hero-zoom)); }
}

/* Mouvement réduit : ni fondu ni dézoom, on garde la première vue, fixe et à
   l'échelle 1. */
@media (prefers-reduced-motion: reduce) {
  .hero__shot { animation: none; transform: none; }
  .hero__shot:nth-child(1) { opacity: 1; }
}

/* Suscription, titre et chapô forment un bloc centré, posé sur le fond. */
.hero__content {
  max-width: 900px;
  margin-inline: auto;
  padding: clamp(3.5rem, 13vw, 6.5rem) 0 clamp(2rem, 6vw, 3rem);
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  text-align: center;
  flex: 1;   /* occupe la hauteur libre : le bandeau de chiffres reste en bas */
}
.hero__content .eyebrow { margin-bottom: var(--space-6); }
.hero__title {
  color: var(--text-on-dark);
  font-family: var(--font-display);
  font-weight: var(--weight-display);
  font-size: clamp(3rem, 7vw, 6.5rem);
  line-height: 1;
  letter-spacing: -0.01em;
  margin: 0 0 var(--space-6);
}
.hero__lede {
  font-size: 1.25rem;
  line-height: 1.6;
  max-width: 560px;
  margin: 0;   /* dernier élément du bloc : c'est son padding qui écarte */
  color: var(--ivory-200);
}

.hero__cta {
  display: flex;
  justify-content: center;
  padding: var(--space-10) 0 clamp(2.5rem, 9vw, 4rem);
}
/* Le libellé est long et capitalisé : sur un téléphone il doit pouvoir passer
   à la ligne plutôt que pousser la page au défilement latéral. */
.hero__cta button {
  max-width: 100%;
  white-space: normal;
  text-align: center;
}

.hero__facts {
  display: grid;
  /* Toujours sur une rangée, téléphone compris : empilés, les trois chiffres
     allongeaient le hero d'un demi-écran. */
  grid-template-columns: repeat(3, 1fr);
  column-gap: var(--space-2);
  border-top: 1px solid color-mix(in srgb, var(--gold-400) 40%, transparent);
}
.fact {
  padding: clamp(1.125rem, 3.5vw, 1.75rem) 0;
  display: flex;
  flex-direction: column;
  align-items: center;   /* le hero est centré, le bandeau de chiffres suit */
  gap: var(--space-1);
}
.fact__value {
  font-family: var(--font-display);
  font-size: 2.25rem;
  color: var(--gold-400);
}
.fact__label {
  font-size: var(--fs-eyebrow);
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--ivory-200);
}
/* À l'étroit, un cran plus petit et une approche resserrée pour que
   « Appartements » tienne dans son tiers. */
@media (max-width: 560px) {
  .fact__value { font-size: 1.625rem; }
  .fact__label { letter-spacing: 0.1em; text-align: center; }
}

/* ── Film de présentation ───────────────────────────────── */

/* Le cadre 16/9 est posé en CSS, pas par le lecteur : la page garde la même
   hauteur pendant que Vimeo se charge, et le fond sombre tient la place en
   attendant la première image. */
.film-section {
  /* Le cheveu doré qui borde le film. Le socle le pose sur le film ; la
     scène au défilement le redessine par le cadre (voir plus bas). */
  --film-filet: color-mix(in srgb, var(--gold-400) 35%, transparent);
}
.film {
  width: min(100%, 980px);
  margin-inline: auto;
  aspect-ratio: 16 / 9;
  background: var(--cocoa-900);
  border: 1px solid var(--film-filet);
}
/* Le lecteur remplit le cadre, sans rien autour. Le ratio porte sur la boîte
   de bordure : les 100 % s'arrêtent donc au bord intérieur, et le cheveu doré
   reste visible tout autour. */
.film iframe,
.film video {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
}

/* ── Le film au défilement ──────────────────────────────── */

/* Le film part en vignette sous le titre, grandit jusqu'à couvrir l'écran,
   s'y tient un instant, puis se rétrécit jusqu'à son cadre, et la page
   reprend sa course. C'est le défilement qui mène, pas une horloge : on
   remonte, la scène repart en arrière.

   Même principe que la localisation : le socle est le film ci-dessus, immobile
   dans son cadre, et la scène ne vient que par-dessus — là où le navigateur
   sait lier une animation au défilement, et pour qui n'a pas demandé moins
   de mouvement. Ailleurs, la piste et le cadre sont deux boîtes neutres.

   Deux boîtes, donc. La piste donne la longueur du défilement ; le cadre, de
   la taille de la vignette, reste collé au milieu de l'écran pendant qu'elle
   passe derrière lui. Le film y est centré, et seules varient son échelle et
   des opacités : ce sont les mouvements que la carte graphique calcule
   seule, au rythme exact du défilement, là où changer la taille du lecteur
   ferait recalculer la page et la fenêtre de Vimeo à chaque image. */

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {

    /* La page porte ce que la scène partage avec l'en-tête, qui s'efface
       pendant qu'elle joue : la frise du film, publiée hors de la section, et
       les mesures dont on tire ses deux instants. */
    .page {
      timeline-scope: --film;

      /* Au repos, la largeur du socle. Dans la section, qui sert de
         conteneur, 100cqw vaut la largeur de son contenu : la page moins ses
         deux gouttières, barre de défilement déduite — ce que 100vw ne sait
         pas faire. Hors d'elle, dans l'en-tête, c'est la fenêtre entière :
         sa scène y finit un peu plus tôt, d'une trentaine de pixels de
         défilement au plus, ce qui ne se voit pas dans un fondu. */
      --film-l-repos: min(100cqw, 980px);
      /* Hauteurs de la vignette (un quart du repos) et du film au repos. */
      --film-h-petit: calc(var(--film-l-repos) * 9 / 64);
      --film-h-repos: calc(var(--film-l-repos) * 9 / 16);

      /* Le défilement que dure la scène, cadre collé. */
      --film-course: 140svh;

      /* La scène commence quand la vignette atteint le milieu de l'écran et
         finit quand le film reposé y est centré : les instants où le cadre se
         colle et se décroche. Aucune borne nommée de la frise n'y tombe, on
         les place donc à la main — le pourcentage y vaut la hauteur de la
         fenêtre. */
      --film-scene:
        entry calc(50% + var(--film-h-petit) / 2)
        exit  calc(50% - var(--film-h-repos) / 2);
    }
    /* Sur un téléphone, la scène est plus courte. */
    @media (max-width: 760px) {
      .page { --film-course: 90svh; }
    }

    .film-section {
      container-type: inline-size;
      /* Au plus fort, le film déborde de l'écran sur les côtés. Il est rogné
         ici plutôt que d'ouvrir un défilement horizontal ; `clip` et non
         `hidden`, qui ferait de la section une zone de défilement et
         décrocherait le cadre collant. */
      overflow-x: clip;

      /* En plein écran, la plus petite largeur qui couvre toute la fenêtre :
         la sienne, ou sa hauteur ramenée au 16/9 quand l'écran est moins
         allongé que le film. Le surplus est rogné. */
      --film-l-plein: max(100cqw + 2 * var(--gutter-page), 100svh * 16 / 9);

      /* Le film est dessiné à sa taille plein écran, puis réduit. Le lecteur
         choisit sa définition d'après la taille de sa fenêtre, et c'est en
         plein écran qu'elle se voit : dessiné petit puis agrandi, le film
         serait flou précisément là.
         Les échelles sont donc des rapports de longueurs, ce que calc() ne
         sait pas faire — tan(atan2(a, b)) rend a / b. */
      --film-repos: tan(atan2(var(--film-l-repos), var(--film-l-plein)));
      --film-petit: calc(var(--film-repos) / 4);   /* la vignette : un quart du repos */
    }
    /* En portrait, couvrir l'écran rognerait les trois quarts de l'image : le
       film s'arrête à la largeur de l'écran, et c'est le noir monté autour de
       lui qui fait la salle (le voile, plus bas). */
    @media (orientation: portrait) {
      .film-section { --film-l-plein: calc(100cqw + 2 * var(--gutter-page)); }
    }

    /* La piste : la vignette, la course, puis de quoi loger le bas du film
       revenu à sa taille. Un élément collé ne descend jamais dans le
       remplissage de son parent : le cadre se décroche au bord de celui-ci,
       et le film au repos, centré sur le cadre, finit pile au bas de la
       piste — là où le socle l'aurait posé. */
    .film__piste {
      --film-marge: calc((var(--film-h-repos) - var(--film-h-petit)) / 2);
      height: calc(var(--film-h-petit) + var(--film-course) + var(--film-marge));
      padding-bottom: var(--film-marge);
      view-timeline-name: --film;
    }

    /* Le cadre se colle au milieu de l'écran. Sur un élément collé, le
       pourcentage de `top` se rapporte à la hauteur de la fenêtre. */
    .film__cadre {
      position: sticky;
      top: calc(50% - var(--film-h-petit) / 2);
      width: var(--film-l-repos);
      height: var(--film-h-petit);
      margin-inline: auto;
      /* Sous l'en-tête (z-index 10), mais devant la section suivante, isolée
         pour sa bande crème : sans cela, en portrait, elle échapperait au
         voile en entrant à l'écran à la fin de la scène. */
      z-index: 1;
    }

    .film {
      position: absolute;
      top: 50%;
      left: 50%;
      width: var(--film-l-plein);
      translate: -50% -50%;
      border: 0;   /* le cheveu doré passe au cadre, ci-dessous */
      /* Chaque étape s'adoucit à ses deux bouts : le film ne part ni ne
         s'arrête d'un coup, qu'il quitte la vignette, touche le plein écran
         ou se pose. */
      animation: film-zoom ease-in-out both;
      animation-timeline: --film;
      animation-range: var(--film-scene);
    }
    /* Sans commandes à offrir, le lecteur n'a rien à recevoir de la souris
       ni du doigt : ils vont droit à la page, sans transiter par la fenêtre
       de Vimeo — qui couvre tout l'écran au plus fort de la scène. */
    .film iframe { pointer-events: none; }

    /* Le cheveu doré, redessiné par le cadre aux dimensions du repos. Porté
       par le film, il serait réduit avec lui ; et l'écran arrondissant
       l'épaisseur d'une bordure à son pixel, aucune épaisseur posée sur le
       film ne retombe à coup sûr sur un trait net au repos. Ici, il garde son
       pixel exact. Il paraît sur le dernier centième de la course — le film
       est alors à un demi-pixel de son bord — et s'efface dès qu'il en
       repart. */
    .film__cadre::after {
      content: "";
      position: absolute;
      top: 50%;
      left: 0;
      width: 100%;
      height: var(--film-h-repos);
      translate: 0 -50%;
      border: 1px solid var(--film-filet);
      pointer-events: none;
      animation: film-filet linear both;
      animation-timeline: --film;
      animation-range: var(--film-scene);
    }

    /* Le voile du portrait : le noir monte autour du film à mesure qu'il
       grandit et se retire quand il revient. Il déborde largement du cadre
       pour tenir tout l'écran, barre d'adresse repliée comprise. */
    @media (orientation: portrait) {
      .film__cadre::before {
        content: "";
        position: absolute;
        top: -100lvh;
        bottom: -100lvh;
        left: 50%;
        width: 100vw;
        translate: -50% 0;
        background: var(--cocoa-900);
        pointer-events: none;
        animation: film-voile ease-in-out both;
        animation-timeline: --film;
        animation-range: var(--film-scene);
      }
    }

    /* L'en-tête collant s'efface pendant que le film prend l'écran, et revient
       quand il se pose. Le film reste dessous, comme le reste de la page : sur
       un écran de portable, le film au repos mord encore sur l'en-tête, qui
       doit alors revenir par-dessus lui en fondu — passé devant, le film
       l'aurait laissé reparaître d'un coup.
       Transparent, il est aussi masqué, pour qu'aucun lien invisible ne
       reçoive un clic ou le clavier. C'est une seconde animation : la carte
       graphique ne calcule pas `visibility`, et logée dans la même, elle y
       aurait retenu le fondu. */
    .site-header {
      animation: film-entete ease-in-out both, film-entete-masque linear both;
      animation-timeline: --film, --film;
      animation-range: var(--film-scene), var(--film-scene);
    }
  }
}

/* Deux cinquièmes de la course pour grandir, un pour tenir le plein écran,
   deux pour revenir au repos. */
@keyframes film-zoom {
  0%       { scale: var(--film-petit); }
  40%, 60% { scale: 1; }
  100%     { scale: var(--film-repos); }
}
@keyframes film-voile {
  0%, 100% { opacity: 0; }
  40%, 60% { opacity: 1; }
}
@keyframes film-filet {
  0%, 99% { opacity: 0; }
  100%    { opacity: 1; }
}
/* L'en-tête est parti avant que le film ne l'atteigne, même sur un écran
   bas, et ne revient qu'une fois le film presque posé. */
@keyframes film-entete {
  0%, 100% { opacity: 1; }
  15%, 85% { opacity: 0; }
}
@keyframes film-entete-masque {
  15%, 85% { visibility: hidden; }
}

/* ── Avancée du chantier : frise chronologique ──────────── */

.timeline {
  --node-size: 88px;
  --ring-thickness: 13px;
  --line-thickness: 16px;
  --timeline-ink: var(--cocoa-700);
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  list-style: none;
  margin: 0;
  padding: 0;
}

.milestone {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}

/* Le trait court d'un centre de pastille à l'autre, et passe derrière elles :
   on le tronque sur le premier et le dernier jalon. */
.milestone::before {
  content: "";
  position: absolute;
  top: calc((var(--node-size) - var(--line-thickness)) / 2);
  /* 1 px de débord de chaque côté : les colonnes de la grille tombent sur des
     fractions de pixel, et les barres bout à bout laisseraient une couture. */
  left: -1px;
  right: -1px;
  height: var(--line-thickness);
  background: var(--timeline-ink);
}
.milestone:first-child::before { left: 50%; }
.milestone:last-child::before { right: 50%; }

.milestone__node {
  position: relative;        /* passe au-dessus du trait */
  width: var(--node-size);
  height: var(--node-size);
  border: var(--ring-thickness) solid var(--timeline-ink);
  border-radius: var(--radius-full);
  /* Ivoire et non blanc : la pastille doit se détacher à peine de la bande. */
  background: var(--surface-page);
  display: grid;
  place-items: center;
}

/* Fichiers d'assets/img/jalons/ : trait fin déjà à l'encre de la frise
   (#4B3727), fond transparent. Rien à teinter ici.

   En revanche, les quatre dessins n'occupent pas la même part de leur carré
   source : à 44 px uniformes, « démarrage des travaux » (dessin large, 90 %
   du cadre) pèse une fois et demie « permis de construire » et vient frôler
   l'anneau, alors que « commercialisation » flotte au milieu. On règle donc
   la taille jalon par jalon pour égaliser la surface d'encre, pas le cadre. */
.milestone__icon {
  display: block;
  width: var(--icon-size, 44px);
  height: var(--icon-size, 44px);
}
.milestone--permis    { --icon-size: 45px; }
.milestone--commerce  { --icon-size: 43px; }
.milestone--travaux   { --icon-size: 38px; }
.milestone--livraison { --icon-size: 43px; }

.milestone__body { margin-top: var(--space-6); }

.milestone__date {
  margin: 0;
  font-size: var(--fs-caption);
  font-weight: var(--weight-semibold);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--text-primary);
}
.milestone__date::after {
  content: "";
  display: block;
  width: 28px;
  height: 1px;
  margin: var(--space-3) auto 0;
  background: var(--beige-300);
}

.milestone__label {
  margin: var(--space-3) 0 0;
  font-size: 1rem;
  color: var(--text-primary);
}

/* En colonne étroite, la frise bascule à la verticale. */
@media (max-width: 760px) {
  .timeline {
    grid-template-columns: 1fr;
    row-gap: var(--space-10);
  }
  .milestone {
    flex-direction: row;
    align-items: center;
    gap: var(--space-6);
    text-align: left;
  }
  /* Les deux sélecteurs d'extrémité sont repris ici : ils tronquent le trait
     horizontal, mais ils sont plus spécifiques que .milestone::before — sans
     cette reprise, le « left: 50% » du premier jalon survivait au basculement
     et plantait la barre au milieu du libellé. */
  .milestone::before,
  .milestone:first-child::before,
  .milestone:last-child::before {
    top: calc(var(--node-size) / 2);
    bottom: calc(-1 * var(--space-10));
    left: calc((var(--node-size) - var(--line-thickness)) / 2);
    right: auto;
    width: var(--line-thickness);
    height: auto;
  }
  .milestone:last-child::before { display: none; }
  .milestone__node { flex: none; }
  .milestone__body { margin-top: 0; }
  .milestone__date::after { margin-inline: 0; }
}

/* ── L'art de vivre ─────────────────────────────────────── */

/* La composition de la maquette : le texte à gauche, la photo en plein cintre
   à droite, à cheval sur une bande crème qui file jusqu'au bord de la page.
   Un filet doré descend du haut à gauche et la marque se tient debout contre
   le bord droit. Tout ce décor tient à la mise en deux colonnes : il s'efface
   entièrement quand les blocs s'empilent (voir la requête média plus bas). */
.living-section {
  position: relative;
  /* La bande est posée derrière le contenu (z-index négatif) ; isoler la
     section lui donne un plancher, sinon elle passerait aussi derrière le
     fond de la page. */
  isolation: isolate;
}

/* La bande crème. Son bord tombe à 63 % de la page, comme sur la maquette :
   la photo le chevauche, un tiers sur le fond clair, le reste sur la bande. */
.living-section::before {
  content: "";
  position: absolute;
  inset: 0 0 0 63%;
  z-index: -1;
  background: var(--surface-sunken);
}

/* Le filet : un cheveu doré qui part du haut de la section et longe la colonne
   de texte, posé dans la marge de page. max() refait le calcul de
   .container--wide, qui colle aux marges tant qu'il y tient puis se centre
   au-delà de sa largeur maximale (la seconde valeur est négative dans le
   premier cas) ; le retrait l'écarte ensuite du texte, sans quoi il viendrait
   se coller à la première lettre de chaque ligne. */
.living-section::after {
  content: "";
  position: absolute;
  top: 0;
  left: calc(
    max(var(--gutter-page), (100% - var(--width-wide)) / 2)
    - clamp(1.25rem, 2.5vw, 2.5rem)
  );
  width: 1px;
  height: 38%;
  background: var(--gold-700);
}

/* La marque debout. writing-mode fait descendre la ligne au lieu de la
   coucher : les lettres pivotent avec elle, comme sur la maquette. */
.living__marque {
  position: absolute;
  top: clamp(1.5rem, 4vw, 3rem);
  right: clamp(0.75rem, 2vw, 1.75rem);
  margin: 0;
  writing-mode: vertical-rl;
  font-size: var(--fs-eyebrow);
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
  color: var(--gold-700);
}

/* Deux colonnes égales, comme .split, mais posées en toutes lettres : .split
   empile quand ses pistes de 320 px ne tiennent plus, soit ici à 770 px, et
   ce seuil-là bouge avec les marges de page. Il faut qu'il coïncide avec
   celui de la requête média ci-dessous, sinon il reste une bande où la photo
   passe pleine largeur en portrait. Autant le fixer une fois. */
.living {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(2rem, 5vw, 6rem);
  align-items: center;
}
/* Le second bloc retrouve l'écart qu'il avait quand les trois paragraphes
   ne faisaient qu'un seul .prose. */
.living__p2 { margin-top: var(--space-5); }

/* Le plein cintre. Les deux rayons du haut sont donnés très grands à dessein :
   quand la somme des rayons d'un côté dépasse sa longueur, le navigateur les
   réduit tous d'un même facteur jusqu'à ce qu'ils tiennent. Le côté le plus
   contraignant est ici le haut, et chaque rayon retombe exactement à la
   moitié de la largeur — horizontalement comme verticalement, donc un
   demi-cercle juste, quelle que soit la taille rendue de la photo.
   La photo est calée au début de sa colonne pour venir chevaucher la bande
   plutôt que de se poser dessus. */
.living__photo {
  aspect-ratio: 3 / 4;
  object-fit: cover;
  max-width: 30rem;
  justify-self: start;
  border-radius: 9999px 9999px 0 0;
  /* Cheveu doré posé à distance, comme les piliers de À propos : l'outline
     épouse le plein cintre sans toucher à son rayon. */
  outline: 1px solid var(--gold-700);
  outline-offset: 10px;
}

/* Une fois les colonnes empilées, le conteneur du texte s'efface : ses
   enfants deviennent les éléments de .living, et la photo peut alors se
   glisser entre le premier et le deuxième paragraphe. C'est le seul moyen
   de l'intercaler, order ne classant que des frères. */
@media (max-width: 770px) {
  /* Le décor suppose deux colonnes : la bande n'a plus de côté où se tenir,
     le filet et la marque croiseraient le texte. On les retire tous les
     trois plutôt que de les rejouer à plat. */
  .living-section::before,
  .living-section::after,
  .living__marque { display: none; }

  .living {
    display: flex;          /* au-dessus, c'est la grille à deux colonnes */
    flex-direction: column;
    /* La grille centre les deux colonnes l'une sur l'autre ; empilés, les
       blocs doivent au contraire prendre toute la largeur, sinon chacun se
       rétrécit sur son contenu — la suscription y passait sur trois lignes. */
    align-items: stretch;
    /* Et l'écart des colonnes ne doit pas se glisser entre les blocs
       empilés : ici le rythme vient des marges, celle de la photo comprise. */
    gap: 0;
  }
  .living__text { display: contents; }

  .living > .eyebrow       { order: 1; }
  .living > .section-title { order: 2; }
  .living__p1              { order: 3; }
  .living__photo           { order: 4; }
  .living__p2              { order: 5; margin-top: 0; }

  /* Seule au-dessus du premier paragraphe, la tête prend l'écart d'une tête
     de section (.section-head) et non celui d'un titre suivi de son texte. */
  .living .living__p1 { margin-top: var(--space-12); }

  /* Le cintre garde le cadrage portrait — c'est lui qui fait l'arche. Pour
     qu'il ne mange pas l'écran, la photo est bornée et centrée au lieu de
     s'étirer d'un bord à l'autre comme les blocs de texte. */
  .living__photo {
    aspect-ratio: 4 / 5;
    max-width: 24rem;
    align-self: center;     /* la pile est en flex : plus de justify-self */
    margin-block: clamp(2rem, 4vw, 3.5rem);
  }
}

/* ── Services ───────────────────────────────────────────── */

.services__caption { margin-bottom: var(--space-4); }
.services {
  --service-filet: color-mix(in srgb, var(--gold-400) 35%, transparent);
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr));
  column-gap: var(--space-12);
}
.service {
  display: flex;
  align-items: center;
  gap: var(--space-5);
  padding: 1rem 0;
  border-bottom: 1px solid var(--service-filet);
}

/* L'icône, dans un anneau d'or fin : le langage des pastilles de la frise du
   chantier, ramené à l'échelle d'une liste et passé à l'or du fond sombre.
   L'anneau reprend le filet qui sépare les services.

   Les dessins (assets/img/services/) sont au trait, 1,5 sur une grille de
   32 : à 30 px, le trait tombe à 1,4 px, la finesse des jalons. Ils servent
   de masque — le fichier ne donne que la forme, c'est le fond posé ici qui
   la peint. L'or reste donc celui de la charte, et suivra le jeton s'il
   change. Le préfixe -webkit- couvre les navigateurs d'avant `mask`, qui
   peindraient sinon un carré plein. */
.service__icon {
  flex: none;
  display: grid;
  place-items: center;
  width: 3.5rem;
  height: 3.5rem;
  border: 1px solid var(--service-filet);
  border-radius: var(--radius-full);
}
.service__icon::before {
  content: "";
  width: 1.875rem;
  height: 1.875rem;
  background: var(--gold-400);
  -webkit-mask: center / contain no-repeat;
  mask: center / contain no-repeat;
}
.service__icon--salle-de-sport::before {
  -webkit-mask-image: url("../../img/services/salle-de-sport.svg");
  mask-image: url("../../img/services/salle-de-sport.svg");
}
.service__icon--piscine::before {
  -webkit-mask-image: url("../../img/services/piscine.svg");
  mask-image: url("../../img/services/piscine.svg");
}
.service__icon--conciergerie::before {
  -webkit-mask-image: url("../../img/services/conciergerie.svg");
  mask-image: url("../../img/services/conciergerie.svg");
}
.service__icon--gardien::before {
  -webkit-mask-image: url("../../img/services/gardien.svg");
  mask-image: url("../../img/services/gardien.svg");
}
.service__icon--espaces-communs::before {
  -webkit-mask-image: url("../../img/services/espaces-communs.svg");
  mask-image: url("../../img/services/espaces-communs.svg");
}
.service__icon--meubles::before {
  -webkit-mask-image: url("../../img/services/meubles.svg");
  mask-image: url("../../img/services/meubles.svg");
}

/* L'anneau prend sa place sur la ligne : en colonne étroite, un nom passe
   plus souvent sur deux lignes. Équilibrées, elles se partagent le nom
   plutôt que de le couper au premier trait d'union venu (« bien- / être »). */
.service__name {
  font-family: var(--font-display);
  font-size: 1.375rem;
  text-wrap: balance;
}

/* ── Espaces professionnels ─────────────────────────────── */

.quote {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 1.375rem;
  line-height: 1.5;
  margin: 0 0 var(--space-6);
  color: var(--text-primary);
}

/* La composition de la maquette : la photo tient le bord gauche de la page,
   son bord intérieur arrondi en demi-ellipse, et la rosace du logo monte en
   arabesque sur cinq colonnes au ras du même bord — au-dessus et au-dessous
   de la photo, jamais derrière elle.

   La grille est posée sur la section elle-même, sans .container : une photo
   qui va jusqu'au bord de la page ne peut pas vivre dans un conteneur centré.
   La marge de page est donc rendue à la grille, en quatrième piste, du côté
   droit seulement — c'est elle qui aligne le bord droit du texte sur celui
   des autres sections. L'air de section devient de la même façon une piste
   haute et une piste basse, où les arabesques se logent. */
.pro {
  display: grid;
  grid-template-columns:
    minmax(0, 1fr)                                            /* la photo */
    clamp(2rem, 5vw, 6rem)                                    /* l'écart */
    minmax(0, 1fr)                                            /* le texte */
    max(var(--gutter-page), (100% - var(--width-wide)) / 2);  /* la marge, à droite */
  /* L'air que .section pose en marge intérieure devient ici deux pistes,
     où l'arabesque se loge ; le padding est mis à zéro juste en dessous. */
  grid-template-rows: var(--section-air) auto var(--section-air);
  /* Les pistes s'étirent plutôt que de se centrer : c'est le texte qui donne
     la mesure — de la suscription au bas du dernier paragraphe — et la photo
     s'y règle. */
  align-items: stretch;
  padding: 0;   /* .section pose de l'air tout autour ; ici c'est la grille */
}

/* Les arabesques. Le fichier ne porte que le motif, découpé du logo lui-même :
   même dessin, mêmes ors, aucune interprétation. Une tuile au cinquième de la
   largeur en range cinq de front, et le fond la répète vers le bas.

   Deux blocs, jamais derrière la photo : ce qu'on ne verrait pas n'a pas à
   être peint. Celui du haut tient dans la piste haute de la grille. Celui du
   bas ne s'arrête plus au pied de la section : il descend jusqu'aux photos du
   showcase, et c'est donc la tête du showcase qui le porte. */
.pro::before,
.showcase__tete::before {
  content: "";
  /* Cinq rosaces de front — c'est le 20 % de la taille de fond qui les
     compte, la largeur ne fait que leur donner leur échelle. */
  width: clamp(5.5rem, 9.5vw, 10rem);
  background-image: url("../../img/photos/rosace-majorelle.webp");
  background-repeat: repeat;
  background-size: 20% auto;
}

/* Au-dessus de la photo : calé par le bas, pour que le motif rencontre la
   photo net. En haut, il s'arrête où s'arrête la section — franchement, sans
   fondu : l'or est à pleine force d'un bout à l'autre de la colonne. */
.pro::before {
  grid-column: 1;
  grid-row: 1;
  justify-self: start;
  background-position: left bottom;
}

/* Au-dessous : la colonne part du bas de la photo et descend d'un seul tenant
   jusqu'au carrousel. Elle est portée par la tête du showcase et remonte de deux
   airs de section — celui qui reste sous la photo, puis celui qui ouvre la
   section suivante. D'un seul tenant, et non deux blocs bout à bout : la
   hauteur parcourue ne tombe pas sur un nombre rond de motifs, et un raccord
   se verrait. */
.showcase__tete {
  position: relative;
  /* La marge basse de .section-head doit rester dedans : sinon elle s'échappe
     par le bas, le bloc s'arrête au ras du titre, et il manque cet écart-là
     pour atteindre les photos. */
  display: flow-root;
}
.showcase__tete::before {
  position: absolute;
  left: 0;
  top: calc(-2 * var(--section-air));
  bottom: 0;
  background-position: left top;
}

/* La photo remplit la hauteur de la rangée, donc celle du texte, et recadre au
   centre ce qui dépasse. Son bord intérieur s'arrondit en demi-ellipse : large
   du quart de la photo, haute de toute sa hauteur — les deux rayons verticaux
   se rejoignent au milieu du bord, la courbe est donc continue d'un coin à
   l'autre et ne laisse pas de segment droit. */
.pro__photo {
  grid-column: 1;
  grid-row: 2;
  height: 100%;
  object-fit: cover;
  border-radius: 0 28% 28% 0 / 0 50% 50% 0;
  /* Même cheveu doré que le séjour, mais rentré dans la photo : elle va
     jusqu'au bord de la page, un contour posé dehors y serait rogné. */
  outline: 1px solid var(--gold-700);
  outline-offset: -12px;
}

/* La piste de texte s'élargit avec la page ; le texte, lui, s'arrête à la
   demi-largeur de contenu et se range à droite de sa piste. Sans ce plafond,
   le titre s'étalerait sur toute la largeur d'un très grand écran. */
.pro__text {
  grid-column: 3;
  grid-row: 2;
  justify-self: end;
  max-width: calc(var(--width-wide) / 2);
}

@media (max-width: 760px) {
  /* Empilés, les blocs reprennent l'ordre où ils sont écrits : le texte, puis
     la photo. Les arabesques se retirent — la photo passe sous le texte, il
     n'y a plus de haut ni de bas de photo où les loger — et l'air redevient
     une simple marge intérieure. */
  .pro {
    grid-template-columns: 1fr;
    grid-template-rows: auto;
    padding-block: var(--section-air);
  }
  .pro::before,
  .showcase__tete::before { display: none; }

  /* Le bloc de texte s'efface pour que la photo vienne se glisser entre la
     citation et les paragraphes : ses enfants deviennent les rangées de la
     grille, et les paragraphes passent après la photo. */
  .pro__text { display: contents; }
  .pro__text > * {
    /* La section n'a pas de marge latérale : le texte reprend la sienne,
       quand la photo continue d'aller d'un bord à l'autre. */
    padding-inline: var(--gutter-page);
  }
  .pro__text > .prose { order: 2; }

  /* La photo n'a plus de texte à côté d'elle dont copier la hauteur : elle
     reprend son cadrage d'origine. L'arrondi reste, c'est lui qui fait le
     style. La citation pose déjà son écart au-dessus. */
  .pro__photo {
    order: 1;
    grid-column: 1;
    grid-row: auto;
    height: auto;
    margin: var(--space-2) 0 var(--space-8);
  }
}

/* ── Showcase : le carrousel de photos ──────────────────── */

/* Sous la tête claire, une scène sombre d'un bord à l'autre : une bande de
   photos qui fait la mise au point (assets/js/vitrine.js), puis le nom de la
   vue, le compteur et les flèches, et la ligne qui suit la position dans la
   série. Le pied et la ligne s'alignent sur le conteneur large.

   Sans script, la bande reste un défilement aimanté qui se manie au doigt
   et au pavé tactile ; seuls la mise au point et le texte manquent. */

.showcase {
  padding-inline: 0;      /* la scène va d'un bord à l'autre */
  padding-bottom: 0;      /* et descend jusqu'au pied de la section */
}
.showcase .container { padding-inline: var(--gutter-page); }

.vitrine {
  /* Largeur d'une vue. */
  --vitrine-l: min(max(64vw, 290px), 960px);
  /* Marge du pied et de la ligne : celle du conteneur large. */
  --vitrine-marge: max(var(--gutter-page), (100% - var(--width-wide)) / 2);
  --vitrine-encre: var(--text-on-dark);
  --vitrine-pale: color-mix(in srgb, var(--vitrine-encre) 60%, transparent);
  --vitrine-trait: color-mix(in srgb, var(--vitrine-encre) 24%, transparent);
  position: relative;
  display: flex;
  flex-direction: column;
  gap: clamp(1.25rem, 3.5vh, 2.25rem);
  padding-block: clamp(2.5rem, 7vh, 4.5rem) clamp(1.5rem, 4vh, 2.5rem);
  background: var(--surface-inverse);
  color: var(--vitrine-encre);
  outline: none;
}
.vitrine:focus-visible { box-shadow: inset 0 0 0 2px var(--focus-ring); }

/* La bande : la première et la dernière vue doivent pouvoir venir au centre,
   d'où une marge latérale d'une demi-page moins une demi-vue. */
.vitrine__piste {
  position: relative;     /* les offsetLeft des vues se comptent d'ici */
  display: flex;
  align-items: center;
  gap: clamp(0.75rem, 2vw, 1.75rem);
  overflow-x: auto;
  overflow-y: hidden;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  padding-inline: calc(50% - var(--vitrine-l) / 2);
  scrollbar-width: none;
  cursor: grab;
}
.vitrine__piste::-webkit-scrollbar { display: none; }
.vitrine__piste[data-glisse] {
  scroll-snap-type: none;
  cursor: grabbing;
}
/* Pendant un passage animé (vitrine.js), l'aimantation ramènerait la bande
   à chaque image. */
.vitrine__piste[data-anime] { scroll-snap-type: none; }
.vitrine__vue {
  position: relative;
  flex: 0 0 var(--vitrine-l);
  aspect-ratio: 16 / 9;   /* le format des photos : rien n'est rogné */
  margin: 0;
  scroll-snap-align: center;
  transform-origin: center;
  will-change: transform, filter;
}
.vitrine__vue img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: var(--radius-image);
  background: color-mix(in srgb, var(--vitrine-encre) 8%, transparent);
  pointer-events: none;
  user-select: none;
}

/* Le pied : le nom de la vue à gauche, compteur et flèches à droite. */
.vitrine__pied {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: end;
  gap: var(--space-4);
  padding-inline: var(--vitrine-marge);
}
/* Le nom remonte de sous sa ligne à chaque vue : la ligne le rogne. */
.vitrine__titre {
  margin: 0;
  min-height: 1.15em;
  overflow: hidden;
  padding-bottom: 0.08em;
  font-family: var(--font-display);
  font-size: clamp(1.75rem, 3.8vw, 3.25rem);
  font-weight: 400;
  line-height: 1.05;
  letter-spacing: -0.01em;
}
.vitrine__titre > span {
  display: block;
  animation: vitrine-monte 0.9s cubic-bezier(0.2, 0.8, 0.2, 1) both;
}
.vitrine__nav {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}
.vitrine__compte {
  margin-right: var(--space-2);
  font-size: var(--fs-meta);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.14em;
  color: var(--vitrine-pale);
}
.vitrine__compte b {
  color: var(--vitrine-encre);
  font-weight: 500;
}
.vitrine__fleche {
  appearance: none;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 1px solid color-mix(in srgb, var(--vitrine-encre) 45%, transparent);
  border-radius: 50%;
  background: transparent;
  color: inherit;
  cursor: pointer;
  transition: background 0.2s ease, color 0.2s ease;
}
/* Le chevron : deux côtés d'un carré, tournés vers la gauche ou la droite. */
.vitrine__fleche::before {
  content: "";
  width: 9px;
  height: 9px;
  border-left: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
}
.vitrine__fleche--prec::before { rotate: 45deg; translate: 2px 0; }
.vitrine__fleche--suiv::before { rotate: -135deg; translate: -2px 0; }
.vitrine__fleche:hover {
  background: var(--vitrine-encre);
  color: var(--surface-inverse);
}
.vitrine__fleche:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}

/* La ligne : la part de la série déjà parcourue (--vitrine-p, de 0 à 1). */
.vitrine__ligne {
  position: relative;
  height: 1px;
  margin-inline: var(--vitrine-marge);
  background: var(--vitrine-trait);
}
.vitrine__ligne > i {
  position: absolute;
  inset: 0;
  background: var(--vitrine-encre);
  transform-origin: left;
  transform: scaleX(var(--vitrine-p, 0));
}

/* Lue par les lecteurs d'écran à chaque vue, invisible à l'œil. */
.vitrine__annonce {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

@media (max-width: 640px) {
  .vitrine { --vitrine-l: 84vw; }
}

@media (prefers-reduced-motion: reduce) {
  .vitrine__titre > span { animation: none; }
}

@keyframes vitrine-monte {
  from { transform: translateY(105%); }
  to   { transform: none; }
}

/* ── Partenaires ────────────────────────────────────────── */

/* La carte porte la couleur ; la vidéo vient à ras de son bord haut. */
.partner {
  background: var(--surface-sunken);
}

/* Le cadre 16/9 est posé en CSS pour que la grille ne saute pas pendant le
   chargement du lecteur. Il porte d'abord l'affiche ; au clic, le lecteur
   Vimeo prend sa place (assets/js/partenaires.js). */
.partner__video {
  position: relative;
  aspect-ratio: 16 / 9;
  background: var(--surface-inverse);
  overflow: hidden;
}
.partner__affiche,
.partner__lecteur,
.partner__lecteur iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}
.partner__affiche {
  z-index: 1;   /* devant .partner__lecteur, vide mais posé sur tout le cadre */
  padding: 0;
  background: var(--surface-inverse);
  cursor: pointer;
}
.partner__affiche img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Assombrie pour que le bouton et la durée se lisent sur n'importe quelle vue. */
  filter: brightness(0.62);
  transition: transform 0.6s var(--ease-standard), filter 0.3s var(--ease-standard);
}
.partner__affiche:hover img { transform: scale(1.03); filter: brightness(0.7); }
.partner__affiche:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: -4px;
}
/* Le bouton de lecture : un disque d'or, le triangle découpé dedans. */
.partner__lire {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 4rem;
  height: 4rem;
  translate: -50% -50%;
  border-radius: 50%;
  background: var(--gold-400);
  transition: scale 0.3s var(--ease-standard);
}
.partner__lire::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 54%;
  width: 1.1rem;
  height: 1.3rem;
  translate: -50% -50%;
  background: var(--cocoa-900);
  clip-path: polygon(0 0, 100% 50%, 0 100%);
}
.partner__affiche:hover .partner__lire { scale: 1.08; }
.partner__duree {
  position: absolute;
  left: var(--space-5);
  bottom: var(--space-4);
  font-size: var(--fs-eyebrow);
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--ivory-50);
}
@media (prefers-reduced-motion: reduce) {
  .partner__affiche img,
  .partner__lire { transition: none; }
}
/* Le lecteur créé, l'affiche s'efface. */
.partner__video[data-etat="lecture"] .partner__affiche { display: none; }

.partner__body {
  padding: var(--gutter-card-tight) var(--gutter-card-tight) var(--gutter-card);
}
.partner__role {
  margin: 0;
  font-size: var(--fs-eyebrow);
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--text-accent);
}
.partner__name {
  margin: var(--space-2) 0 0;
  font-family: var(--font-display);
  font-weight: var(--weight-display);
  font-size: var(--fs-h4);
  line-height: var(--leading-tight);
}
.partner__desc {
  margin: var(--space-3) 0 0;
  color: var(--text-secondary);
}

/* ── Localisation ───────────────────────────────────────── */

/* Deux colonnes posées par zones : le plan occupe toute la hauteur à droite,
   le texte s'empile à gauche. L'écart entre rangées reprend celui que les
   marges d'enchaînement donnaient dans l'ancienne colonne (--space-8). */
.locality {
  /* Nommée : la scène au défilement en tire la place du plan (voir .lieu). */
  --locality-gap: clamp(2rem, 5vw, 6rem);
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-areas:
    "head map"
    "text map"
    "near map";
  column-gap: var(--locality-gap);
  row-gap: var(--space-8);
}
.locality__head { grid-area: head; }
.locality__text { grid-area: text; }
.locality__near { grid-area: near; }
.locality__plan {
  grid-area: map;
  align-self: center;   /* le plan est plus court que la colonne de texte */
}
.locality__map {
  /* Arrondi au repos ; la scène ci-dessous le retire tant que le plan couvre
     l'écran (voir lieu-carte-rayon). */
  border-radius: var(--radius-lg);
  display: block;
}
/* Le plan est un lien vers Google Maps : l'image suit l'arrondi du lien,
   que la scène au défilement anime. */
.locality__map img {
  border-radius: inherit;
  transition: filter 0.3s ease;
}
.locality__map:hover img { filter: brightness(1.05) saturate(1.05); }
.locality__map:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 4px;
}
/* Sous le plan : une image cliquable ne se devine pas, surtout au doigt. */
.locality__legende {
  display: block;
  margin-top: var(--space-3);
  text-align: center;
  color: var(--text-secondary);
  font-size: var(--fs-meta);
  text-decoration: underline;
  text-decoration-color: var(--border-subtle);
  text-underline-offset: 3px;
  transition: var(--transition-color);
}
.locality__legende:hover { color: var(--text-accent); text-decoration-color: currentColor; }

/* En une colonne, le plan vient s'intercaler entre les paragraphes et les
   temps de trajet, au lieu de fermer la section. Le seuil est celui des
   autres sections à deux colonnes, pour que les têtes se centrent toutes au
   même moment (voir .head-center-mobile). */
@media (max-width: 770px) {
  .locality {
    grid-template-columns: 1fr;
    grid-template-areas:
      "head"
      "text"
      "map"
      "near";
  }
}

.proximity {
  display: flex;
  flex-direction: column;
}
.proximity__row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--space-4);
  flex-wrap: wrap;   /* le nom du lieu et son temps de trajet, à l'étroit */
  padding: 0.875rem 0;
  border-top: 1px solid var(--beige-200);
  font-size: 0.875rem;
}
/* Temps de trajet : c'est le chiffre qu'on vient lire, il porte donc un cran
   de plus que le nom du lieu. L'approche reste courte pour que « 4 min » se
   lise d'un bloc. */
.proximity__tag {
  color: var(--text-accent);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  font-size: var(--fs-meta);
  font-weight: var(--weight-semibold);
  white-space: nowrap;
}

/* ── La carte au défilement ─────────────────────────────── */

/* Le plan couvre d'abord toute la section, puis se pose à sa place pendant
   que le texte paraît. Comme pour le film, c'est le défilement qui mène : la
   section se colle sous l'en-tête le temps que le plan se pose, puis reprend
   sa course. On remonte, la scène repart en arrière.

   Même socle que les autres scènes : la section telle qu'au-dessus,
   immobile, et la scène par-dessus seulement là où le navigateur lie une
   animation au défilement, pour qui n'a pas demandé moins de mouvement.

   Elle ne joue qu'en deux colonnes, et sur un écran assez haut pour loger la
   section sous l'en-tête. En une colonne, la section dépasse l'écran et le
   plan tombe entre les paragraphes : le couvrir d'abord cacherait le texte
   qu'on est en train de lire. Les seuils de hauteur viennent du texte
   actuel — la grille fait 557 px au plus étroit des deux colonnes (771 px),
   521 px au plus à partir de 1000 px ; un texte plus long les ferait
   relever. */

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) and (min-width: 771px) and (min-height: 640px),
         (prefers-reduced-motion: no-preference) and (min-width: 1000px) and (min-height: 560px) {

    .lieu {
      /* La section sert de conteneur : 100cqw y vaut la largeur de la page,
         barre de défilement déduite. */
      container-type: inline-size;
      /* La scène va d'un bord à l'autre de la section : le cadre reprend
         l'air, la grille reprend les marges de page. */
      padding: 0;

      /* L'en-tête collant, sous lequel le cadre se colle : sa marge haute et
         basse, le logo de 48 px et son filet (.site-header et .site-logo,
         dans components.css). */
      --lieu-entete: calc(2 * var(--space-5) + 48px + 1px);
      /* Ce que l'écran montre de la section, sous l'en-tête. */
      --lieu-vue: calc(100svh - var(--lieu-entete));
      /* Le défilement que dure la scène. */
      --lieu-course: 70svh;
      /* Le cadre se colle quand le haut de la section touche l'en-tête, et se
         décroche une course plus loin. Ces deux instants sont comptés en
         longueurs, depuis celui où la section paraît au bas de l'écran. */
      --lieu-scene:
        cover calc(100svh - var(--lieu-entete))
        cover calc(100svh - var(--lieu-entete) + var(--lieu-course));
    }

    /* La piste : le cadre, puis la course qu'il parcourt collé. Un élément
       collé ne descend pas dans le remplissage de son parent, ni sous sa
       propre marge : la course est donc une boîte, dessinée par ::after. */
    .lieu__piste { view-timeline-name: --lieu; }
    .lieu__piste::after {
      content: "";
      display: block;
      height: var(--lieu-course);
    }

    /* Le cadre tient au moins l'écran sous l'en-tête, la grille centrée
       dedans. Plus haute, elle le fait grandir au lieu de déborder : collée,
       elle montre alors son haut, titre compris, et le bas des temps de
       trajet paraît quand la page repart. */
    .lieu__cadre {
      position: sticky;
      top: var(--lieu-entete);
      min-height: var(--lieu-vue);
      padding-block: var(--space-8);
      display: grid;
      align-content: center;
      /* Au départ, le plan déborde de toute part : il est rogné au bord du
         cadre, qui est le bord de la section. */
      overflow: clip;
    }

    .lieu .locality {
      /* La largeur du conteneur, que .section ne borde plus. */
      --lieu-c: min(100cqw - 2 * var(--gutter-page), var(--width-wide));
      width: var(--lieu-c);
      /* La colonne de droite, où le plan se pose. */
      --lieu-col: calc((var(--lieu-c) - var(--locality-gap)) / 2);

      /* Au départ, le plan est centré sur l'écran et grandi jusqu'à couvrir
         ce qu'il montre de la section. L'écart horizontal se calcule : le
         centre de la colonne de droite est à un quart de (conteneur +
         gouttière) du centre de la page. Le vertical est nul — la grille est
         centrée dans le cadre, le plan dans la grille —, sauf quand la
         grille, plus haute que l'écran, se cale en haut : le plan descend
         alors de 4rem au plus, que l'échelle couvre d'avance.
         tan(atan2(a, b)) rend a / b, que calc() ne sait pas faire ; 1492 / 1054
         est le format du plan. */
      --lieu-dx: calc((var(--lieu-c) + var(--locality-gap)) / -4);
      --lieu-s: tan(atan2(max(100cqw, (var(--lieu-vue) + 8rem) * 1492 / 1054), var(--lieu-col)));
    }

    /* Le plan passe devant le texte, qu'il couvre au départ. Il s'adoucit à
       ses deux bouts : il quitte l'écran plein sans à-coup et se pose de
       même. */
    .lieu .locality__map {
      position: relative;
      z-index: 1;
      animation: lieu-carte ease-in-out both, lieu-carte-rayon linear both;
      animation-timeline: --lieu, --lieu;
      animation-range: var(--lieu-scene), var(--lieu-scene);
    }

    .lieu .locality__head,
    .lieu .locality__text,
    .lieu .locality__near,
    .lieu .locality__legende {
      animation: lieu-texte ease-out both;
      animation-timeline: --lieu;
      animation-range: var(--lieu-scene);
    }
  }
}

@keyframes lieu-carte {
  from { translate: var(--lieu-dx) 0; scale: var(--lieu-s); }
}
/* L'arrondi ne paraît qu'une fois le plan posé : tant qu'il couvre l'écran,
   des coins ronds y découperaient la page. Le basculement tient sur le
   dernier centième de la course, pour ne pas animer un rayon — que la carte
   graphique ne calcule pas seule — pendant tout le défilement. */
@keyframes lieu-carte-rayon {
  0%, 99% { border-radius: 0; }
  100%    { border-radius: var(--radius-lg); }
}
/* Le texte paraît une fois que le plan l'a découvert : le bord gauche du
   plan ne quitte la colonne de texte qu'aux quatre cinquièmes de la course
   (mesuré de 800 à 1440 px de large). Plus tôt, le texte se lisait à
   moitié sous le plan. */
@keyframes lieu-texte {
  0%, 80% { opacity: 0; translate: 0 1rem; }
  100%    { opacity: 1; translate: 0 0; }
}

/* ── Typologies ─────────────────────────────────────────── */

/* Carte : la photo à fond perdu en tête, posée par typology__photo--<id>
   (assets/img/typologies/<id>/carte.webp), puis le résumé tiré des lots
   disponibles (assets/js/disponibilites.js). Le bouton descend au pied de la
   carte, pour que les quatre s'alignent quelle que soit la longueur du texte. */
.typology {
  background: var(--surface-card);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  transition: var(--transition-elev);
}
.typology:hover { box-shadow: var(--shadow-card-hover); }
.typology__photo {
  position: relative;
  aspect-ratio: 4 / 3;
  background-color: var(--surface-sunken);
  background-size: cover;
  background-position: center;
}
.typology__photo--studio { background-image: url("../../img/typologies/studio/carte.webp"); }
.typology__photo--f2a,
.typology__photo--f2b    { background-image: url("../../img/typologies/f2/carte.webp"); }
.typology__photo--f3     { background-image: url("../../img/typologies/f3/carte.webp"); }
.typology__photo--f4     { background-image: url("../../img/typologies/f4/carte.webp"); }
.typology__photo--bureaux { background-image: url("../../img/typologies/bureaux/carte.webp"); }
.typology__badge {
  position: absolute;
  top: var(--space-3);
  left: var(--space-3);
  padding: 0.375rem 0.75rem;
  border-radius: var(--radius-tag);
  background: var(--surface-card);
  color: var(--text-accent);
  font-size: var(--fs-eyebrow);
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  box-shadow: var(--shadow-xs);
}
.typology__body {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-6);
}
.typology__name {
  font-family: var(--font-display);
  font-size: 2rem;
  font-weight: 400;
  line-height: 1.1;
  margin: 0;
}
.typology__surface,
.typology__prix { margin: 0; }
.typology__surface {
  font-size: var(--fs-eyebrow);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-secondary);
}
.typology__prix {
  color: var(--text-accent);
  font-size: var(--fs-meta);
}
.typology__action { margin-top: auto; padding-top: var(--space-4); }
/* Photo et nom ouvrent aussi la modale (disponibilites.js) ; le clavier passe
   par le bouton, d'où l'absence de tabindex. */
.typology--dispo .typology__photo,
.typology--dispo .typology__name { cursor: pointer; }
.typology--dispo .typology__name { transition: var(--transition-color); }
.typology--dispo .typology__name:hover { color: var(--text-accent); }
/* Typologie épuisée : la photo passe en sépia doux, le badge en encre. */
.typology--vendu .typology__photo { filter: grayscale(0.55) sepia(0.15); }
.typology--vendu .typology__badge {
  background: var(--surface-inverse);
  color: var(--text-on-dark);
}
.typology--vendu .typology__prix { color: var(--text-muted); }
.card-grid--typologies {
  --card-min: 230px;
  --card-gap: var(--space-6);
}
/* Six cartes (studio et bureaux, puis F2 A, F2 B, F3, F4) : sur une ligne,
   les boutons passeraient sur deux lignes. En grand écran, deux rangées de
   trois. */
@media (min-width: 1024px) {
  .card-grid--typologies { grid-template-columns: repeat(3, 1fr); }
  .card-grid--typologies .typology__photo { aspect-ratio: 3 / 2; }
}

/* ── FAQ ──────────────────────────────────────────────── */
/* Deux piles indépendantes côte à côte, une seule sous 880 px. Chaque
   question est un <details> : la croix (deux traits) tourne en « + » fermé,
   en « × » ouvert. Là où le navigateur sait animer ::details-content, la
   réponse se déplie en douceur ; ailleurs elle paraît d'un coup. */
.faq {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0 var(--space-12);
  align-items: start;
}
@media (min-width: 881px) {
  .faq { grid-template-columns: 1fr 1fr; }
}
.faq__item {
  border-bottom: 1px solid var(--border-subtle);
  interpolate-size: allow-keywords;
}
.faq__col > .faq__item:first-child { border-top: 1px solid var(--border-subtle); }
.faq__question {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-5) 0;
  font-family: var(--font-display);
  font-size: 1.125rem;
  line-height: 1.35;
  color: var(--text-heading);
  cursor: pointer;
  list-style: none;
  transition: var(--transition-color);
}
.faq__question::-webkit-details-marker { display: none; }
.faq__question:hover { color: var(--text-accent); }
.faq__question:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}
.faq__icone {
  position: relative;
  flex: 0 0 auto;
  width: 0.875rem;
  height: 0.875rem;
  color: var(--text-accent);
  transition: rotate 0.3s ease;
}
.faq__icone::before,
.faq__icone::after {
  content: "";
  position: absolute;
  inset: 50% 0 auto;
  height: 1.5px;
  margin-top: -0.75px;
  background: currentColor;
}
.faq__icone::after { rotate: 90deg; }
.faq__item[open] .faq__icone { rotate: 45deg; }
.faq__reponse {
  margin: 0;
  padding: 0 var(--space-8) var(--space-5) 0;
  font-size: var(--fs-body);
  line-height: 1.7;
  color: var(--text-secondary);
}
.faq__reponse > p { margin: 0; }
.faq__reponse > p + p { margin-top: var(--space-3); }
.faq__lien {
  display: inline-block;
  margin-top: var(--space-3);
  color: var(--text-accent);
  font-weight: 500;
  text-decoration: underline;
  text-underline-offset: 3px;
}
.faq__lien:hover { color: var(--text-heading); }
.faq__item::details-content {
  block-size: 0;
  overflow: clip;
  transition: block-size 0.35s ease, content-visibility 0.35s allow-discrete;
}
.faq__item[open]::details-content { block-size: auto; }
@media (prefers-reduced-motion: reduce) {
  .faq__icone, .faq__item::details-content { transition: none; }
}
/* Une seule colonne : la seconde pile prolonge la première, sans double
   filet à la jointure. */
@media (max-width: 880px) {
  .faq__col + .faq__col > .faq__item:first-child { border-top: 0; }
}

/* ── À propos (aperçu) ─────────────────────────────────── */
/* Vitrine de a-propos.html : portrait encadré à gauche, le constat du
   fondateur et les trois piliers à droite, puis le lien vers la page. */
.apropos__portrait {
  margin: 0;
  /* Place pour le filet décalé de .photo--frame (outline-offset: 10px). */
  padding: 11px;
}
.apropos__role {
  margin-top: var(--space-6);
  text-align: center;
}
.apropos__piliers {
  list-style: none;
  margin: var(--space-8) 0;
  padding: 0;
  border-top: 1px solid var(--border-default);
}
.apropos__piliers li {
  display: flex;
  align-items: baseline;
  gap: var(--space-4);
  padding: var(--space-4) 0;
  border-bottom: 1px solid var(--border-default);
  font-family: var(--font-display);
  font-size: 1.125rem;
  line-height: 1.35;
  color: var(--text-heading);
}
.apropos__numero {
  flex: 0 0 auto;
  font-family: var(--font-text);
  font-size: var(--fs-eyebrow);
  font-weight: 600;
  letter-spacing: 0.18em;
  color: var(--text-accent);
}
/* Colonnes empilées : le lien se centre avec la tête (.head-center-mobile). */
@media (max-width: 770px) {
  .apropos .bouton-lien {
    display: flex;
    width: fit-content;
    margin-inline: auto;
  }
}

/* ── Blog (aperçu) ──────────────────────────────────────── */
/* Les trois derniers articles, en cartes partagées avec le blog
   (.article, voir components.css), puis le lien vers la page. */
.card-grid--articles {
  --card-min: 280px;
  --card-gap: var(--space-6);
}
/* La section repose sur --surface-raised, la teinte des cartes au blog :
   elles passent en blanc pour s'en détacher, comme les cartes d'appartement. */
.card-grid--articles .article { background: var(--surface-card); }
.blog-apercu__suite {
  margin-top: var(--space-10);
  text-align: center;
}

/* ── Modale disponibilités ────────────────────────────── */
/* Même mécanique que la brochure ci-dessous (fondu, rien ne bouge), mais
   sur fond clair : on y lit un tableau de prix. Sous la brochure dans
   l'empilement, pour que le relais « Recevoir la brochure » passe devant. */
.dispos {
  position: fixed;
  inset: 0;
  z-index: 290;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--gutter-page);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 0.35s ease, visibility 0s linear 0.35s;
}
.dispos.is-open {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transition: opacity 0.35s ease;
}
.dispos__voile {
  position: absolute;
  inset: 0;
  background: rgba(46, 35, 23, 0.72);
}
.dispos__carte {
  position: relative;
  width: min(880px, 100%);
  max-height: calc(100vh - 3rem);
  max-height: calc(100dvh - 3rem);
  overflow: auto;
  overscroll-behavior: contain;
  background: var(--surface-page);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-modal);
  padding: clamp(1.25rem, 4vw, 2.5rem);
}
.dispos__carte .eyebrow { margin-bottom: var(--space-3); }
.dispos__compte {
  margin: var(--space-2) 0 var(--space-6);
  color: var(--text-secondary);
}
.dispos__croix {
  position: absolute;
  top: 0.5rem;
  right: 0.75rem;
  background: transparent;
  border: 0;
  padding: 0.5rem;
  font-size: 1.75rem;
  line-height: 1;
  color: var(--text-accent);
  cursor: pointer;
}
.dispos__croix:hover { color: var(--text-primary); }

.dispos__galerie { margin: 0 0 var(--space-8); }
.dispos__vue {
  position: relative;
  aspect-ratio: 16 / 9;
  border-radius: var(--radius-image);
  overflow: hidden;
  background: var(--surface-sunken);
}
.dispos__vue img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.dispos__pas {
  position: absolute;
  top: 50%;
  translate: 0 -50%;
  width: 2.75rem;
  height: 2.75rem;
  border: 0;
  border-radius: var(--radius-full);
  background: rgba(250, 246, 240, 0.85);
  color: var(--text-primary);
  font-size: 1.75rem;
  line-height: 1;
  cursor: pointer;
  box-shadow: var(--shadow-sm);
  transition: var(--transition-color);
}
.dispos__pas:hover { background: var(--surface-card); }
.dispos__pas--prec { left: var(--space-3); }
.dispos__pas--suiv { right: var(--space-3); }
.dispos__legende {
  margin-top: var(--space-2);
  font-size: var(--fs-caption);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.dispos__vignettes {
  display: flex;
  gap: var(--space-2);
  margin-top: var(--space-3);
  overflow-x: auto;
  scrollbar-width: thin;
  padding-bottom: 0.25rem;
}
.dispos__vignette {
  flex: 0 0 auto;
  width: 5.5rem;
  aspect-ratio: 16 / 9;
  padding: 0;
  border: 2px solid transparent;
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--surface-sunken);
  cursor: pointer;
  opacity: 0.65;
  transition: var(--transition-color), opacity 0.2s ease;
}
.dispos__vignette:hover { opacity: 1; }
.dispos__vignette[aria-current="true"] {
  border-color: var(--gold-400);
  opacity: 1;
}
.dispos__vignette img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.dispos__table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--fs-body);
  font-variant-numeric: tabular-nums;
}
.dispos__table th {
  text-align: left;
  font-size: var(--fs-eyebrow);
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-accent);
  padding: 0 var(--space-3) var(--space-3) 0;
  border-bottom: 1px solid var(--gold-400);
}
.dispos__table td {
  padding: var(--space-3) var(--space-3) var(--space-3) 0;
  border-bottom: 1px solid var(--border-subtle);
}
.dispos__table th:nth-child(n+3),
.dispos__table td:nth-child(n+3) { text-align: right; }
.dispos__table th:last-child,
.dispos__table td:last-child { padding-right: 0; }
/* Statut du lot : tous vendus aujourd’hui, la colonne reste prête pour
   un « Réservé » si la grille en fait figurer. */
.dispos__statut {
  font-weight: 600;
  white-space: nowrap;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  font-size: var(--fs-eyebrow);
  color: var(--text-accent);
}
/* Petit écran : chaque lot devient une fiche, libellés repris de data-label. */
@media (max-width: 640px) {
  .dispos__table thead { display: none; }
  .dispos__table tr {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--space-2) var(--space-4);
    padding: var(--space-4) 0;
    border-bottom: 1px solid var(--border-subtle);
  }
  .dispos__table td,
  .dispos__table td:nth-child(n+3) {
    padding: 0;
    border: 0;
    text-align: left;
  }
  .dispos__table td::before {
    content: attr(data-label);
    display: block;
    font-size: var(--fs-eyebrow);
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--text-muted);
  }
}
/* Pied de modale : « Voir les rendements » (effacé une fois déplié) et
   la brochure. Pas de display sur les enveloppes : leur [hidden] joue. */
.dispos__actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-3);
  margin-top: var(--space-8);
}

/* Rendements : en-tête à l'encre comme sur la grille commerciale, scénario
   central teinté, rendement visé mis en avant au pied du tableau. */
.dispos__rendements { margin-top: var(--space-10); }
.dispos__rendements-titre {
  font-family: var(--font-display);
  font-size: var(--fs-h4);
  font-weight: 400;
  line-height: 1.2;
  color: var(--text-heading);
  margin: 0 0 var(--space-5);
}
.rendements {
  width: 100%;
  border-collapse: collapse;
  font-variant-numeric: tabular-nums;
  background: var(--surface-card);
  border: 1px solid var(--border-subtle);
}
.rendements th,
.rendements td {
  padding: var(--space-3) var(--space-4);
  text-align: left;
}
.rendements thead th {
  background: var(--surface-inverse);
  color: var(--text-on-dark);
  font-size: var(--fs-eyebrow);
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
}
.rendements thead th:first-child { color: var(--gold-400); }
.rendements tbody tr + tr { border-top: 1px solid var(--border-subtle); }
.rendements tbody th {
  font-size: var(--fs-meta);
  font-weight: 400;
  color: var(--text-secondary);
}
.rendements tbody td { font-weight: 600; }
.rendements td:nth-child(3) { background: var(--surface-raised); }
.rendements tfoot { border-top: 1px solid var(--gold-400); }
/* Encre sur ivoire : l'or sur beige n'atteint pas le contraste voulu. */
.rendements tfoot th,
.rendements tfoot td { background: var(--surface-raised); }
.rendements tfoot td:nth-child(3) { background: var(--surface-sunken); }
.rendements tfoot th {
  font-size: var(--fs-meta);
  font-weight: 600;
  color: var(--text-primary);
}
.rendements tfoot td {
  font-family: var(--font-display);
  font-size: var(--fs-h5);
  color: var(--text-primary);
  white-space: nowrap;
}
.dispos__rendements-note {
  margin: var(--space-3) 0 0;
  font-size: var(--fs-caption);
  color: var(--text-muted);
}
/* Petit écran : les quatre colonnes tiennent en resserrant les marges. */
@media (max-width: 640px) {
  .rendements th,
  .rendements td { padding: var(--space-2); }
  .rendements thead th { font-size: 0.625rem; letter-spacing: 0.08em; }
  .rendements tfoot td { font-size: var(--fs-body); }
}
@media (prefers-reduced-motion: reduce) {
  .dispos, .dispos.is-open, .typology { transition: none; }
}

/* ── Modale brochure ──────────────────────────────────── */
/* Les boutons du menu et du hero n'emmènent pas au formulaire du bas de
   page : la modale couvre l'écran et n'apparaît qu'en fondu (opacity et
   visibility, aucun déplacement). Sous prefers-reduced-motion, le fondu
   tombe et l'apparition est immédiate. */
.brochure {
  position: fixed;
  inset: 0;
  z-index: 300;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--gutter-page);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 0.35s ease, visibility 0s linear 0.35s;
}
.brochure.is-open {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transition: opacity 0.35s ease;
}
.brochure__voile {
  position: absolute;
  inset: 0;
  background: rgba(46, 35, 23, 0.72);
}
.brochure__carte {
  position: relative;
  width: min(560px, 100%);
  max-height: calc(100vh - 3rem);
  max-height: calc(100dvh - 3rem);
  overflow: auto;
  background: var(--surface-inverse);
  color: var(--text-on-dark);
  --heading-color: currentColor;
  border: 1px solid var(--gold-700);
  border-radius: var(--radius-lg);
  padding: clamp(1.5rem, 4vw, 2.5rem);
}
.brochure__carte .eyebrow { margin-bottom: var(--space-4); }
.brochure__texte {
  color: var(--beige-100);
  margin-bottom: var(--space-6);
}
/* Une fois la demande envoyée, l'invitation à la remplir n'a plus lieu d'être. */
.brochure__carte:has(.demande[data-etat="envoye"]) .brochure__texte { display: none; }
.brochure__croix {
  position: absolute;
  top: 0.5rem;
  right: 0.75rem;
  background: transparent;
  border: 0;
  padding: 0.5rem;
  font-size: 1.75rem;
  line-height: 1;
  color: var(--gold-400);
  cursor: pointer;
}
.brochure__croix:hover { color: var(--ivory-50); }
@media (prefers-reduced-motion: reduce) {
  .brochure, .brochure.is-open { transition: none; }
}

/* ── Appel à l'action final ─────────────────────────────── */

.cta__title { margin-bottom: var(--space-8); }
.cta__points {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  font-size: var(--fs-eyebrow);
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--beige-100);
}
/* Les trois mentions suivent le titre : centrées une fois la colonne seule,
   au fer à gauche dès que le formulaire revient à côté. */
@media (max-width: 770px) {
  .cta__points { align-items: center; }
}
.cta__form {
  display: flex;
  flex-direction: column;
  gap: 1.75rem;
}
.field {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.field__label {
  font-size: var(--fs-eyebrow);
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--gold-400);
}
.field__input {
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--gold-700);
  padding: 0.625rem 0;
  color: var(--text-on-dark);
  font-family: var(--font-text);
  font-size: 1rem;
  transition: var(--transition-color);
}
.field__input:focus-visible {
  outline: none;
  border-bottom-color: var(--gold-400);
  border-bottom-width: 2px;
}

/* ── Demande de brochure ────────────────────────────────── */
/* Les deux formulaires .demande (bas de page et fenêtre) sur fond sombre.
   L'état vit dans data-etat, posé par assets/js/demande.js : « envoi »
   pendant l'envoi, « envoye » une fois la demande enregistrée — le
   formulaire laisse alors la place au lien de téléchargement. */
.demande {
  /* Terracotta éclairci : lisible sur le brun, sans crier. */
  --demande-erreur: #F2A98F;
}

/* Téléphone : l'indicatif à gauche, le numéro à droite, même filet doré. */
.field__tel {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: var(--space-4);
}

/* Indicatif : drapeau et indicatif sur le filet du champ ; le panneau de
   recherche s'ouvre dans la couche supérieure (assets/js/demande.js le
   place sous le champ téléphone). */
.indicatif { min-width: 0; }
.indicatif__bouton {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-width: 6.25rem;
  padding-right: 1.25rem;
  text-align: left;
  cursor: pointer;
  /* Chevron en deux triangles, à la couleur des libellés. */
  background-image:
    linear-gradient(45deg, transparent 50%, var(--gold-400) 50%),
    linear-gradient(135deg, var(--gold-400) 50%, transparent 50%);
  background-position: calc(100% - 5px) 55%, 100% 55%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}
.indicatif__bouton:focus-visible {
  outline: none;
  border-bottom-color: var(--gold-400);
  border-bottom-width: 2px;
}
.indicatif__drapeau {
  flex: 0 0 auto;
  width: 1.25rem;
  height: 0.9375rem;
  border-radius: 2px;
  object-fit: cover;
  /* Filet discret : les drapeaux blancs (Japon, Chypre) ne se perdent pas. */
  box-shadow: 0 0 0 1px rgba(46, 35, 23, 0.14);
}
.indicatif__code { font-variant-numeric: tabular-nums; }

.indicatif__panneau {
  position: fixed;
  inset: auto;
  margin: 0;
  z-index: 400;
  display: flex;
  flex-direction: column;
  padding: 0.5rem;
  border: 1px solid var(--ivory-200);
  border-radius: var(--radius-md);
  background: var(--ivory-50);
  color: var(--text-primary);
  box-shadow: var(--shadow-lg);
  font-family: var(--font-text);
}
.indicatif__panneau[hidden],
.indicatif__panneau[popover]:not(:popover-open) { display: none; }

.indicatif__recherche {
  flex: 0 0 auto;
  width: 100%;
  padding: 0.625rem 0.75rem 0.625rem 2.25rem;
  border: 1px solid var(--ivory-200);
  border-radius: var(--radius-sm);
  background-color: #fff;
  /* Loupe en SVG, à la couleur des textes secondaires. */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='none' stroke='%23857360' stroke-width='1.6' stroke-linecap='round'%3E%3Ccircle cx='7' cy='7' r='5'/%3E%3Cpath d='m11 11 3.5 3.5'/%3E%3C/svg%3E");
  background-position: 0.75rem 50%;
  background-repeat: no-repeat;
  color: var(--text-primary);
  font: inherit;
  font-size: 0.9375rem;
}
.indicatif__recherche::placeholder { color: var(--text-muted); }
.indicatif__recherche::-webkit-search-cancel-button { appearance: none; }
.indicatif__recherche:focus-visible {
  outline: 2px solid var(--gold-400);
  outline-offset: -1px;
}

.indicatif__liste {
  flex: 1 1 auto;
  min-height: 0;
  margin-top: 0.375rem;
  overflow-y: auto;
  overscroll-behavior: contain;
}
.indicatif__groupe {
  margin: 0;
  padding: 0;
  list-style: none;
}
.indicatif__groupe::before {
  content: attr(data-titre);
  display: block;
  padding: 0.625rem 0.625rem 0.25rem;
  font-size: var(--fs-eyebrow);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.indicatif__groupe[hidden] { display: none; }
/* Pendant une recherche, un seul groupe : son titre n'apprend rien. */
.indicatif__groupe[hidden] + .indicatif__groupe::before { display: none; }
.indicatif__groupe + .indicatif__groupe:not(:first-child) { border-top: 1px solid var(--ivory-200); margin-top: 0.25rem; }
.indicatif__groupe[hidden] + .indicatif__groupe { border-top: 0; margin-top: 0; }

.indicatif__option {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  padding: 0.5rem 0.625rem;
  border-radius: var(--radius-sm);
  font-size: 0.9375rem;
  line-height: 1.3;
  cursor: pointer;
}
.indicatif__option[hidden] { display: none; }
.indicatif__option:hover,
.indicatif__option.is-actif { background: var(--ivory-200); }
.indicatif__option[aria-selected="true"] { font-weight: 600; }
.indicatif__nom {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.indicatif__dial {
  flex: 0 0 auto;
  color: var(--text-secondary);
  font-variant-numeric: tabular-nums;
}
.indicatif__vide {
  margin: 0;
  padding: 1rem 0.625rem;
  font-size: 0.9375rem;
  color: var(--text-secondary);
}
.demande .field__input[aria-invalid="true"] { border-bottom-color: var(--demande-erreur); }

/* Consentement : une case native aux couleurs de la charte. */
.consentement {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  font-size: var(--fs-meta);
  line-height: 1.55;
  color: var(--beige-100);
  cursor: pointer;
}
.consentement__case {
  flex: 0 0 auto;
  width: 1.125rem;
  height: 1.125rem;
  margin: 0.15rem 0 0;
  accent-color: var(--gold-400);
  cursor: pointer;
}
.consentement__case[aria-invalid="true"] {
  outline: 2px solid var(--demande-erreur);
  outline-offset: 2px;
}
.consentement a {
  color: var(--gold-400);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.consentement a:hover { color: var(--ivory-50); }

/* Champ piège pour les robots : hors de l'écran, hors du parcours clavier. */
.demande__piege {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  opacity: 0;
}

.demande__erreur {
  margin: 0;
  color: var(--demande-erreur);
  font-size: var(--fs-meta);
}
.demande__erreur:empty { display: none; }

.demande[data-etat="envoi"] .demande__form {
  opacity: 0.7;
  pointer-events: none;
  cursor: progress;
}

/* Après l'envoi : remerciement et téléchargement. */
.demande__merci { display: none; }
.demande[data-etat="envoye"] .demande__form { display: none; }
.demande[data-etat="envoye"] .demande__merci {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  outline: none;
}
.demande__merci .eyebrow { margin: 0; }
.demande__merci-titre {
  margin: 0;
  font-family: var(--font-display);
  font-size: clamp(1.5rem, 3vw, 2rem);
  line-height: 1.15;
  color: var(--text-on-dark);
}
.demande__merci-texte {
  margin: 0;
  color: var(--beige-100);
}
/* Même allure que le bouton doré du design system (variante gold, lg). */
.demande__telecharger {
  align-self: flex-start;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  margin-top: var(--space-2);
  padding: 16px 32px;
  border: 1px solid var(--gold-400);
  border-radius: var(--radius-button);
  background: var(--gold-400);
  color: var(--cocoa-900);
  font-family: var(--font-text);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  line-height: 1;
  text-decoration: none;
  transition: var(--transition-color);
}
.demande__telecharger span {
  font-weight: 400;
  letter-spacing: 0.08em;
  text-transform: none;
  opacity: 0.75;
}
.demande__telecharger:hover {
  background: var(--ivory-50);
  border-color: var(--ivory-50);
  text-decoration: none;
}
.demande__telecharger:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}
@media (max-width: 480px) {
  .demande__telecharger { align-self: stretch; justify-content: center; padding-inline: 20px; }
}

/* ── Zones tactiles (voir components.css) ───────────────── */
.locality__legende,
.faq__lien {
  padding-block: 12px;
  margin-block: -12px;
}
.locality__legende,
.faq__lien { margin-top: calc(var(--space-3) - 12px); }
.dispos__croix,
.brochure__croix {
  min-width: 44px;
  min-height: 44px;
}
.field__input,
.indicatif__bouton {
  min-height: 44px;
}
