/* Composants du prototype MSG.

   Dépend de tokens.css, qui est la seule source de vérité : aucune couleur,
   aucun rayon et aucune valeur d'échelle ne sont écrits en dur ici. Les seules
   valeurs littérales sont des filets (2 px, 3 px), des cibles tactiles (44 px)
   et les seuils de media queries — que les custom properties ne savent pas
   porter. Chacune est commentée à l'endroit où elle apparaît.

   Écriture mobile-first : le socle décrit le cas mobile, les media queries
   `min-width` ajoutent le cas desktop. La maquette 1920 est la cible haute.

   Aucune remise à zéro : la page hôte fournit le reset.

   Règle de contraste à ne jamais casser : --msg-sage ne sert de couleur de
   TEXTE que sur fond vert profond (4.70). Sur fond clair il tombe à 1.94.
   Les titres display sur fond clair prennent --msg-sage-dark. */

/* --- Focus visible, commun à tous les composants ---------------------------

   Deux anneaux concentriques, blanc à l'intérieur, vert à l'extérieur.
   Aucune couleur unique de la charte ne contraste avec tous les fonds :
   corail tombe à 1.98 sur le vert profond et à 2.38 sur le sauge. Avec la
   paire blanc/vert, au moins un des deux anneaux dépasse toujours le seuil
   de 3.0 de la règle 1.4.11 :

     fond blanc  → anneau vert  9.15     fond sauge  → anneau vert  4.70
     fond gris   → anneau vert  7.71     fond corail → anneau blanc 4.62
     fond vert   → anneau blanc 9.15

   Les deux anneaux contrastent entre eux à 9.15, la forme reste lisible.
   L'ombre portée comble exactement l'`outline-offset` : pas de recouvrement. */
.pill:focus-visible,
.nav__link:focus-visible,
.nav__burger:focus-visible,
.faq__question:focus-visible {
  outline: 3px solid var(--msg-green);
  outline-offset: 2px;
  box-shadow: 0 0 0 2px var(--msg-white);
}

/* --- 1. Vague (séparateur de sections) -------------------------------------

   Deux tracés dans le HTML, un seul visible à la fois. La zone pleine du
   chemin occupe le HAUT du viewBox : le remplissage reprend donc la couleur
   de la section PRÉCÉDENTE, et le fond de la section suivante apparaît sous
   la ligne festonnée.

   6 crêtes au-dessus de 768 px, 2 en dessous : conserver la fréquence
   d'origine sur un écran étroit transforme le feston en grésillement.

   `display` n'est déclaré que sur les modificateurs, jamais sur `.wave` :
   aucune règle n'en annule une autre. */
.wave {
  width: 100%;
  height: var(--wave-height);
  fill: var(--msg-white);
}

.wave--mobile { display: block; }
.wave--desktop { display: none; }

.wave--from-green { fill: var(--msg-green); }
.wave--from-grey { fill: var(--msg-grey); }

@media (min-width: 768px) {
  .wave--mobile { display: none; }
  .wave--desktop { display: block; }
}

/* --- 2. Carte projet -------------------------------------------------------

   Structure attendue :
     <article class="card">
       <div class="card__frame">
         <img class="card__image" src="…" alt="…">
         <span class="card__tag">Illustration</span>
       </div>
       <p class="card__caption">…</p>
     </article>

   Le cadre ne masque pas ses débordements : c'est l'image qui porte le rayon,
   ce qui laisse le tag corail déborder sous le bord bas.

   Le décalage de la légende vient d'une seule déclaration, calculée sur
   --card-tag-height. Pas de `gap` sur .card : deux sources d'espacement
   finiraient par s'additionner sans qu'on s'en aperçoive. */
.card {
  /* Hauteur du tag, déclarée sur la carte parce que la légende doit dégager
     la moitié qui déborde. Les libellés sont des termes de taxonomie courts. */
  --card-tag-height: calc(var(--fs-lead) * 2.2);
  display: flex;
  flex-direction: column;
}

.card__frame {
  position: relative;
  aspect-ratio: var(--card-ratio);
  border-radius: var(--card-radius);
  background: var(--msg-white);
}

.card__image {
  display: block;
  width: 100%;
  height: 100%;
  border-radius: inherit;
  object-fit: cover;
}

/* L'image est servie dans un <picture>, qui s'intercale entre le cadre et
   l'image : sans cette règle, « border-radius: inherit » hérite du picture,
   qui n'a pas de rayon, et les angles redeviennent droits. */
.card__frame > picture {
  display: block;
  height: 100%;
  border-radius: inherit;
}

.card__tag {
  position: absolute;
  bottom: 0;
  left: 50%;
  display: inline-flex;
  align-items: center;
  /* `min-height` et non `height` : un libellé long passe sur deux lignes au
     lieu de déborder. `max-width` le contient dans le cadre, ce qui évite
     tout défilement horizontal et tout chevauchement avec la carte voisine. */
  min-height: var(--card-tag-height);
  /* Largeur au contenu, bornée par la carte. Sans « width: max-content », un
     élément positionné en absolu avec « left: 50% » ne dispose que de la
     moitié de la largeur de la carte pour calculer la sienne : les libellés
     passaient sur deux lignes bien avant d'atteindre le bord. */
  width: max-content;
  max-width: 100%;
  padding: 0 calc(var(--fs-lead) * 1.1);
  /* Filet blanc : le tag chevauche une photo dont on ne maîtrise pas les
     couleurs. Il garantit la limite exigée par la règle 1.4.11, et c'est
     déjà ce que font les maquettes. */
  border: 2px solid var(--msg-white);
  border-radius: var(--pill-radius);
  background: var(--msg-coral);
  color: var(--msg-white);
  font-family: var(--ff-heading);
  font-size: var(--fs-lead);
  font-weight: 700;
  line-height: 1.2;
  text-align: center;
  transform: translate(-50%, 50%);
}

.card__caption {
  margin: 0;
  padding-top: calc(var(--card-tag-height) / 2 + var(--gutter) * 0.5);
  color: var(--msg-green);
  font-family: var(--ff-body);
  font-size: var(--fs-body);
  line-height: 1.5;
  text-align: center;
}

/* --- 3. Grille de projets --------------------------------------------------
   1 colonne, puis 2 à partir de 560 px, puis 3 à partir de 900 px. */
.card-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--gutter);
  margin: 0;
  padding: 0;
  list-style: none;
}

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

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

/* --- 4. Bouton pill --------------------------------------------------------

   Deux variantes. Le corail porte toujours son filet blanc : invisible sur
   fond blanc ou gris, il devient la limite du bouton sur fond vert profond,
   où le corail ne contraste plus qu'à 1.98. C'est le traitement des maquettes.
   La variante verte est destinée aux fonds clairs (9.15 sur blanc).

   Seuil verrouillé à 4.5 dans contrast.py : --fs-lead plafonne à 18 px sous
   1125 px de large, où le libellé n'est plus du gros texte. Historique : le
   corail des maquettes (#E34659) plaçait le blanc à 3.97 et faisait échouer
   le CTA principal sur mobile ; assombri en #DF2B41 le 7 août 2026, puis
   remplacé le 14 septembre par le #D43A58 de la charte, conforme tel quel
   à 4.62. */
.pill {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* 44 px : cible tactile, pas une valeur de charte. */
  min-height: 44px;
  padding: calc(var(--fs-lead) * 0.5) calc(var(--fs-lead) * 1.4);
  border: 2px solid transparent;
  border-radius: var(--pill-radius);
  font-family: var(--ff-heading);
  font-size: var(--fs-lead);
  font-weight: 700;
  line-height: 1.2;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
}

.pill--coral {
  border-color: var(--msg-white);
  background: var(--msg-coral);
  color: var(--msg-white);
}

.pill--green {
  background: var(--msg-green);
  color: var(--msg-white);
}

/* Aucun état de survol n'est défini par les maquettes. Un assombrissement
   neutre évite d'introduire une couleur hors charte. */
.pill:hover { filter: brightness(0.93); }

/* --- 5. Accordéon FAQ ------------------------------------------------------

   Structure attendue :
     <div class="faq">
       <details class="faq__item">
         <summary class="faq__question">
           Question<span class="faq__marker" aria-hidden="true"></span>
         </summary>
         <div class="faq__answer"><p>…</p></div>
       </details>
     </div>

   `<details>` natif : focusable, actionnable à Entrée comme à Espace, état
   déplié exposé par le navigateur. Zéro JavaScript.

   Barre sauge et texte vert profond : ici le sauge est un FOND, pas une
   couleur de texte. Le vert sur sauge vaut 4.70, conforme au seuil de 4.5
   du texte courant. La règle interdite reste le sauge en TEXTE sur clair. */
.faq {
  display: grid;
  gap: calc(var(--gutter) * 0.25);
}

.faq__question {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: calc(var(--gutter) * 0.5);
  min-height: 44px;
  padding: calc(var(--fs-lead) * 0.7) calc(var(--fs-lead) * 0.9);
  background: var(--msg-sage);
  color: var(--msg-green);
  font-family: var(--ff-body);
  font-size: var(--fs-lead);
  font-weight: 500;
  line-height: 1.3;
  cursor: pointer;
  /* Retire le triangle natif : `list-style` pour Firefox et Chrome, */
  list-style: none;
}

/* … le pseudo-élément pour les WebKit plus anciens. */
.faq__question::-webkit-details-marker { display: none; }

.faq__marker {
  display: grid;
  flex: none;
  place-items: center;
  width: calc(var(--fs-lead) * 1.5);
  height: calc(var(--fs-lead) * 1.5);
  border-radius: var(--pill-radius);
  background: var(--msg-coral);
}

/* Chevron tracé au filet, sans image ni couleur littérale.
   Blanc sur corail : 4.62, au-dessus du seuil de 3.0 des éléments non textuels. */
.faq__marker::before {
  width: 30%;
  height: 30%;
  border-right: 2px solid var(--msg-white);
  border-bottom: 2px solid var(--msg-white);
  content: "";
  transform: translateY(-20%) rotate(45deg);
}

.faq__item[open] .faq__marker { transform: rotate(180deg); }

.faq__answer {
  padding: calc(var(--fs-body) * 1.2) calc(var(--fs-lead) * 0.9);
  color: var(--msg-green);
  font-family: var(--ff-body);
  font-size: var(--fs-body);
  line-height: 1.6;
}

/* --- 6. Navigation ---------------------------------------------------------

   Structure attendue :
     <nav class="nav" aria-label="Navigation principale">
       <details class="nav__disclosure">
         <summary class="nav__burger">
           <span class="nav__burger-icon" aria-hidden="true"></span>Menu
         </summary>
       </details>
       <ul class="nav__list nav__list--start"> 3 × .nav__item > .nav__link </ul>
       <a class="nav__logo" href="./"><img …></a>
       <ul class="nav__list nav__list--end">   3 × .nav__item > .nav__link </ul>
     </nav>

   Le `<details>` ne contient que son `<summary>`. Les deux listes sont ses
   FRÈRES et leur affichage est piloté par `[open] ~`. Conséquence : la
   visibilité du panneau ne dépend jamais de la façon dont le navigateur
   masque le contenu d'un `<details>` replié — mécanisme qui a changé deux
   fois depuis 2024 avec l'arrivée de `::details-content`. En desktop il
   suffit d'afficher les listes, sans rien avoir à forcer.

   Contrepartie assumée : le contenu révélé n'est pas un descendant du
   `<summary>`. Il le suit immédiatement dans l'ordre du document, ce qui est
   l'ordre de lecture d'un lecteur d'écran. Voir le rapport pour les limites
   qui subsistent (pas de fermeture à Échap sans JavaScript). */
.nav {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  column-gap: var(--gutter);
  row-gap: calc(var(--gutter) * 0.5);
  padding: calc(var(--gutter) * 0.5) var(--gutter);
}

.nav__disclosure {
  grid-column: 1;
  grid-row: 1;
  justify-self: start;
}

.nav__burger {
  display: flex;
  align-items: center;
  gap: calc(var(--fs-body) * 0.6);
  min-height: 44px;
  color: var(--msg-green);
  font-family: var(--ff-body);
  font-size: var(--fs-caption);
  font-weight: 500;
  cursor: pointer;
  list-style: none;
}

.nav__burger::-webkit-details-marker { display: none; }

/* Trois traits : l'élément lui-même et ses deux pseudo-éléments. */
.nav__burger-icon,
.nav__burger-icon::before,
.nav__burger-icon::after {
  display: block;
  width: calc(var(--fs-body) * 1.4);
  /* 2 px : filet. */
  height: 2px;
  border-radius: var(--pill-radius);
  background: var(--msg-green);
}

.nav__burger-icon { position: relative; }

.nav__burger-icon::before,
.nav__burger-icon::after {
  position: absolute;
  left: 0;
  content: "";
}

.nav__burger-icon::before { top: calc(var(--fs-body) * -0.45); }
.nav__burger-icon::after { top: calc(var(--fs-body) * 0.45); }

.nav__logo {
  grid-column: 2;
  grid-row: 1;
  display: block;
}

.nav__list {
  grid-column: 1 / -1;
  display: none;
  margin: 0;
  padding: 0;
  /* Décision de composant, pas un reset : une navigation ne porte pas de puces. */
  list-style: none;
}

.nav__disclosure[open] ~ .nav__list { display: grid; }

.nav__link {
  display: flex;
  align-items: center;
  min-height: 44px;
  color: var(--msg-green);
  font-family: var(--ff-body);
  font-size: var(--fs-body);
  font-weight: 500;
  text-decoration: none;
}

.nav__link:hover { text-decoration: underline; }

/* L'entrée courante est en gras dans les maquettes ; `aria-current` porte
   l'information pour les lecteurs d'écran, la graisse pour les autres. */
.nav__link[aria-current="page"] { font-weight: 700; }

@media (min-width: 1024px) {
  .nav__disclosure { display: none; }

  /* Le second sélecteur neutralise explicitement la règle `[open] ~` du socle.
     Sa spécificité (0,2,0) l'emporterait sinon dès qu'un menu ouvert en
     mobile est élargi au-delà de 1024 px, et les listes resteraient empilées. */
  .nav__list,
  .nav__disclosure[open] ~ .nav__list {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--gutter);
  }

  .nav__list--start {
    grid-column: 1;
    grid-row: 1;
    justify-content: flex-start;
  }

  .nav__list--end {
    grid-column: 3;
    grid-row: 1;
    justify-content: flex-end;
  }
}

/* --- 7. Bloc photo détourée ------------------------------------------------

   Structure attendue :
     <div class="cutout">
       <div class="cutout__body">… titre, texte, bouton …</div>
       <figure class="cutout__figure"><img class="cutout__image" …></figure>
     </div>

   Socle mobile : une colonne, photo en pleine largeur sous le texte. Les
   débordements en diagonale des maquettes sont ingérables en colonne unique.
   À partir de 768 px la superposition reprend : la photo déborde au-dessus
   et au-dessous du bloc, y compris par-dessus la vague suivante. */
.cutout {
  display: grid;
  row-gap: var(--gutter);
}

.cutout__figure {
  margin: 0;
}

.cutout__image {
  display: block;
  width: 100%;
  max-width: 100%;
  height: auto;
  /* Une photo rectangulaire prend le rayon des cartes, comme dans la maquette ;
     sur une photo détourée, le rayon ne se voit pas. */
  border-radius: var(--card-radius);
}

@media (min-width: 768px) {
  .cutout {
    grid-template-columns: 1fr 1fr;
    align-items: center;
    column-gap: var(--gutter);
  }

  .cutout__figure {
    position: relative;
    z-index: 1;
    margin-top: calc(var(--space-section) * -0.5);
    margin-bottom: calc(var(--space-section) * -0.5);
  }

  /* Quand la photo passe derrière la vague, elle est haute et calée en bas :
     le texte se cale en haut, à hauteur de la tête, comme dans la maquette —
     centré, le titre descendait de 160 px sous le haut de la photo. */
  .cutout--derriere {
    align-items: start;
  }
}

/* Variante « derrière la vague » : une photo détourée coupée net en bas, comme
   le portrait bras croisés, ne peut pas déborder par-dessus la section
   suivante — son bord se verrait. Elle est alors calée en bas du bloc et
   descend sous la section suivante, qui la recouvre (`.section--devant`) :
   transparente sur la hauteur de sa vague, pleine ensuite. La photo reste
   visible au-dessus de la courbe, sa coupe disparaît sous la matière. */
.cutout__figure--derriere {
  --enfouissement: 0px;
  position: relative;
  z-index: 0;
  align-self: end;
  margin-bottom: calc(var(--space-section) * -1 - var(--wave-height) - 2rem - var(--enfouissement));
}

/* Variante enfouie : une photo très haute (la silhouette assise) s'enfonce plus
   loin sous la section suivante, qui en cache davantage — la vague la coupe aux
   genoux plutôt qu'aux chevilles — et le bloc raccourcit d'autant. Elle remonte
   aussi un peu au-dessus du titre, comme la maquette. */
.cutout__figure--enfouie {
  --enfouissement: clamp(6rem, 15vw, 18rem);
}

@media (min-width: 768px) {
  .cutout__figure--derriere {
    z-index: 0;
    margin-top: 0;
    margin-bottom: calc(var(--space-section) * -1 - var(--wave-height) - 2rem - var(--enfouissement));
  }

  .cutout__figure--enfouie {
    margin-top: calc(var(--space-section) * -0.5);
  }
}

.section--devant {
  z-index: 1;
  margin-top: calc(var(--wave-height) * -1);
}

/* Deux classes : ce fond doit l'emporter sur celui de `.section--white` ou
   `.section--green`, déclaré après dans la page. */
.section.section--devant {
  background: linear-gradient(to bottom, transparent var(--wave-height), var(--devant-fond) var(--wave-height));
}

.section--devant.section--white { --devant-fond: var(--msg-white); }
.section--devant.section--green { --devant-fond: var(--msg-green); }

/* Sa vague est « en creux » (#vague-6-creux) : la matière est sous la courbe,
   de la couleur de la section ; au-dessus, on voit la section précédente. */
.section--devant .wave { fill: var(--devant-fond); }

/* Illustrations au trait des sections argumentaires, servies en image. */
.illus {
  display: block;
  width: 100%;
  height: auto;
}

/* --- Mouvement -------------------------------------------------------------
   Les transitions ne sont ajoutées que si l'utilisateur ne demande pas de
   réduction. La rotation du chevron, elle, s'applique dans tous les cas :
   c'est une information d'état, seule son animation est conditionnelle. */
@media (prefers-reduced-motion: no-preference) {
  .pill { transition: filter 150ms ease-out; }
  .faq__marker { transition: transform 200ms ease-out; }
}
