/*
 * MADS — socle : remise à zéro, typographie, primitives de mise en page,
 * en-tête, pied de page et éléments partagés.
 *
 * Les composants dont le contenu doit rester indexable (projets, colonnes,
 * pied de page) sont stylés ici, en light DOM. Seuls les widgets autonomes
 * (nuée, menu, carrousel, étiquette) encapsulent leur style en shadow DOM.
 */

/* ------------------------------------------------------------------ reset */

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* Le rognage horizontal est porté par <html>, pas par <body>. Posé sur le
     body, il se propage au viewport par la règle d'héritage de l'overflow, et
     le conteneur d'accrochage cesse d'être celui qui défile réellement : le
     scroll-snap devient alors totalement inopérant. */
  overflow-x: clip;
  /* La gouttière de barre de défilement est réservée en permanence. Sans
     cela, le verrouillage du défilement à l'ouverture du menu fait
     disparaître la barre, le viewport s'élargit d'une quinzaine de pixels et
     tout ce qui est calé à droite — l'étoile du menu, la croix de fermeture —
     saute latéralement au moment précis où l'animation démarre. */
  scrollbar-gutter: stable;
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
}

body {
  margin: 0;
  background: var(--c-bg);
  color: var(--c-fg);
  font-family: var(--f-body);
  font-size: var(--t-body);
  font-weight: 400;
  line-height: var(--lh-body);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  /* Le pied de page ne remonte jamais au-dessus du bas de l'écran : la page
     occupe au minimum une hauteur de fenêtre et <main> absorbe l'espace
     restant. Sans cela, une page courte — mentions légales, plan du site,
     résultat de filtre vide — laissait le pied de page flotter au milieu.
     L'en-tête et le lien d'évitement étant en position fixe, ils ne comptent
     pas parmi les enfants de la colonne. */
  min-height: var(--vh);
  display: flex;
  flex-direction: column;
}

main {
  /* `1 0 auto` et non `1` : le contenu garde sa hauteur naturelle quand elle
     dépasse la fenêtre, au lieu d'être comprimé par la base 0 % de `flex: 1`. */
  flex: 1 0 auto;
}

.site-footer {
  flex: none;
}

body.is-menu-open {
  overflow: hidden;
}

h1,
h2,
h3,
h4,
p,
figure,
blockquote,
ul,
ol {
  margin: 0;
}

ul,
ol {
  padding: 0;
  list-style: none;
}

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

/* [hidden] doit l'emporter sur les display: flex/grid déclarés plus bas —
   plusieurs composants masquent des éléments par cet attribut. */
[hidden] {
  display: none !important;
}

img {
  height: auto;
}

a {
  color: inherit;
  text-decoration: none;
}

button {
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
}

input,
textarea {
  font: inherit;
  color: inherit;
}

/* Focus visible pour la navigation clavier, invisible à la souris */
:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 3px;
  border-radius: 2px;
}

::selection {
  background: var(--c-ink);
  color: var(--c-cream);
}

/* Réservé aux lecteurs d'écran */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Lien d'évitement : premier élément focusable du document.

   Le masquage ne peut pas reposer sur le seul décalage hors écran. Pendant un
   défilement animé vers le haut, le navigateur compose la zone au-dessus du
   viewport et le bouton apparaissait fugitivement. On ajoute donc opacity: 0,
   qui le rend réellement invisible sans le retirer de l'ordre de tabulation —
   contrairement à visibility ou display, qui le rendraient infocusable et
   priveraient les utilisateurs au clavier de leur raccourci. */
.skip-link {
  position: fixed;
  top: 0;
  left: 50%;
  transform: translate(-50%, -110%);
  z-index: 200;
  padding: 0.75rem 1.25rem;
  background: var(--c-ink);
  color: var(--c-cream);
  font-size: var(--t-small);
  opacity: 0;
  pointer-events: none;
  transition:
    transform var(--d-fast) var(--e-out),
    opacity var(--d-fast) var(--e-out);
}

.skip-link:focus {
  transform: translate(-50%, 0);
  opacity: 1;
  pointer-events: auto;
}

/* Exposants et indices : par défaut, un <sup> agrandit la boîte de sa ligne,
   ce qui décale verticalement tout ce qui suit. C'est ce qui désalignait
   « Nous contacter » du logo Instagram — la ligne « Paris 6ᵉ » était plus
   haute que son homologue de la colonne voisine. `line-height: 0` neutralise
   cet effet, le décalage visuel étant obtenu par `top`. */
sup,
sub {
  font-size: 0.68em;
  line-height: 0;
  position: relative;
  vertical-align: baseline;
}

sup { top: -0.45em; }
sub { bottom: -0.25em; }

/* Logotype substitué au mot « MADS » par lib/brand.js. Il se cale sur la
   hauteur des capitales de la ligne où il s'insère, et non sur le corps de
   police : sans cela, il paraîtrait systématiquement trop grand. */
.brand-mark {
  display: inline-block;
  width: auto;
  height: 0.72em;
  vertical-align: -0.045em;
  /* La marge compense l'air blanc contenu dans le tracé du logo, sans quoi le
     mot semblerait collé à ce qui le suit. */
  margin-inline: 0.06em 0.1em;
}

.brand-mark svg {
  display: block;
  width: auto;
  height: 100%;
  fill: currentColor;
}

/* ------------------------------------------------------------ typographie */

.t-hero {
  font-family: var(--f-title);
  font-weight: 300;
  font-size: var(--t-hero);
  line-height: var(--lh-tight);
  letter-spacing: -0.01em;
}

/* Équilibrage des retours à la ligne.

   `balance` répartit les mots également entre les lignes — c'est ce qu'on veut
   pour un titre ou un sous-titre, qui tiennent en deux ou trois lignes. Les
   navigateurs cessent de l'appliquer au-delà de quatre à six lignes, pour des
   raisons de coût de calcul : sur un paragraphe, il ne ferait donc rien.
   D'où `pretty` sur le corps de texte, qui lui n'égalise pas les lignes mais
   empêche les lignes veuves d'un seul mot. */
h1, h2, h3, h4, h5, h6,
.t-h1, .t-h2, .t-h3, .t-lead, .t-subtitle, .t-eyebrow {
  text-wrap: balance;
}

p,
li,
figcaption,
address,
.t-body,
.t-prose {
  text-wrap: pretty;
}

h1,
.t-h1 {
  font-family: var(--f-title);
  font-weight: 300;
  font-size: var(--t-h1);
  line-height: var(--lh-title);
  letter-spacing: -0.005em;
}

h2,
.t-h2 {
  font-family: var(--f-title);
  font-weight: 300;
  font-size: var(--t-h2);
  line-height: var(--lh-title);
}

/* Surtitre court en Poppins demi-gras. Les titres de page l'utilisaient ;
   ils sont passés à `.page-title` (pages.css), qui unifie leur corps. La
   classe reste disponible pour les intertitres secondaires. */
.t-eyebrow {
  font-family: var(--f-body);
  font-size: 1.125rem;
  font-weight: 600;
  line-height: 1.3;
  letter-spacing: -0.01em;
}

.t-lead {
  font-family: var(--f-title);
  font-size: var(--t-lead);
  font-weight: 300;
  line-height: 1.35;
}

.t-subtitle {
  font-family: var(--f-body);
  font-size: var(--t-h3);
  font-weight: 600;
  line-height: 1.34;
}

.t-body {
  font-size: var(--t-body);
  line-height: var(--lh-body);
}

.t-body p + p,
.t-prose p + p {
  margin-top: 1.15em;
}

.t-prose {
  max-width: 62ch;
}

.t-prose a {
  text-decoration: underline;
  text-underline-offset: 0.18em;
  text-decoration-thickness: 1px;
}

.t-small {
  font-size: var(--t-small);
}

/* ------------------------------------------------------- mise en page */

.wrap {
  width: 100%;
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* Grille de référence : 12 colonnes au-delà de 900 px, 1 en dessous */
.grid {
  display: grid;
  gap: var(--gap);
  grid-template-columns: repeat(var(--cols, 1), minmax(0, 1fr));
}

.section {
  padding-block: var(--section-y);
}

/* Un écran plein, utilisé par le hero et les blocs de la page d'accueil */
.screen {
  min-height: var(--vh);
  display: flex;
  flex-direction: column;
}

/* ---------------------------------------------------- lien fléché « → » */

/* « Lire la suite », « Nous contacter », « Nous découvrir » : une flèche qui
   avance légèrement au survol. */
.arrow-link {
  display: inline-flex;
  align-items: center;
  gap: 0.6em;
  font-size: var(--t-body);
  font-weight: 500;
  text-decoration: underline;
  text-underline-offset: 0.25em;
  text-decoration-thickness: 1px;
  text-decoration-color: currentColor;
}

/* Le saut en fin de transition — le mouvement se déroule normalement, puis
   l'icône ressaute d'un poil — vient de la rastérisation. Pendant l'animation
   la flèche vit sur sa propre couche, positionnée au sous-pixel ; à la fin, le
   navigateur détruit la couche et la recale sur la grille de pixels, d'où le
   décalage visible. Déclarer une transformation 3D dès l'état de repos garde
   l'élément composité en permanence : il n'y a plus de bascule, donc plus de
   saut. `backface-visibility` verrouille le même choix sur les WebKit anciens. */
.arrow-link svg {
  flex: none;
  width: 0.78em;
  height: auto;
  fill: currentColor;
  transform: translate3d(0, 0, 0);
  backface-visibility: hidden;
  transition: transform var(--d-fast) var(--e-out);
}

.arrow-link:hover svg,
.arrow-link:focus-visible svg {
  transform: translate3d(0.28em, 0, 0);
}

.arrow-link--lead {
  font-size: var(--t-lead);
}

/* --------------------------------------------------------------- images */

mads-picture {
  position: relative;
  display: block;
  overflow: hidden;
  /* Miniature floutée en fond : elle occupe le cadre pendant le
     téléchargement, ce qui évite le carré vide au défilement. */
  background-image: var(--placeholder);
  background-size: cover;
  background-position: var(--focus, center);
  background-color: var(--c-rule);
}

/* Le recadrage se cale sur le point d'intérêt de la photographie quand elle en
   a un — `--focus` est posée par le serveur, et vaut pour les cinq rapports du
   site. Sans elle, le centre : le comportement par défaut du navigateur. Le
   fond flouté suit le même point, sans quoi l'image sauterait au moment du
   fondu. */
mads-picture img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: var(--focus, center);
}

/* Le fondu n'est armé qu'une fois le composant enregistré : lui seul posera
   `is-loaded`. Sans JavaScript — ou si le module échoue — `:defined` reste
   faux et l'image s'affiche normalement, sans jamais rester à opacité nulle. */
mads-picture:defined img {
  opacity: 0;
  transition: opacity var(--d-base) ease;
}

mads-picture:defined.is-loaded img {
  opacity: 1;
}

/* Cadre à ratio fixe : évite tout décalage de mise en page */
mads-picture.ratio {
  aspect-ratio: var(--ratio, 3 / 2);
}

mads-picture.fill {
  height: 100%;
}

/* --------------------------------------------------------------- header */

.site-header {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: var(--z-header);
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  padding: clamp(1.25rem, 2.6vw, 2.25rem) var(--gutter);
  pointer-events: none;
}

.site-header > * {
  pointer-events: auto;
}

/* --logo-color est écrit par app.js d'après le fond réellement mesuré derrière
   le logo, exactement comme --menu-color pour l'étoile du menu. Le repli sur
   l'encre vaut tant que le script n'a pas mesuré, et sans JavaScript. */
.site-header__logo {
  display: block;
  width: clamp(104px, 10vw, 142px);
  color: var(--logo-color, var(--c-ink));
  transition:
    opacity var(--d-base) var(--e-out),
    color 260ms var(--e-out);
}

.site-header__logo svg {
  width: 100%;
  height: auto;
  fill: currentColor;
}

/* Sur la page d'accueil, le logo du hero fait déjà office de titre : celui de
   l'en-tête n'apparaît qu'une fois le hero sorti du champ. */
html[data-hero-visible='1'] .site-header__logo,
/* Posée par app.js quand un texte passe sous le logo : aucune couleur ne rend
   deux tracés superposés lisibles, mieux vaut céder la place au texte. */
.site-header__logo.is-over-text {
  opacity: 0;
  pointer-events: none;
}

/* --------------------------------------------------------------- footer */

/* Ombre portée vers le HAUT : elle démarque le pied de page de la section qui
   le précède, dont il partage souvent la couleur de fond. Le décalage vertical
   négatif envoie l'ombre au-dessus de l'élément ; `position: relative` la place
   au-dessus du contenu précédent plutôt que dessous. */
.site-footer {
  position: relative;
  z-index: 1;
  padding: clamp(2.5rem, 5vw, 4.5rem) var(--gutter) clamp(2rem, 3vw, 3rem);
  background: var(--c-cream);
  box-shadow: 0 -14px 34px -18px rgba(27, 28, 27, 0.35);
}

/* Deux rangées explicites : « bloc de texte » puis « action ». Chaque colonne
   reprend ces rangées en subgrid, si bien que « → Nous contacter » et l'icône
   Instagram tombent dans la MÊME rangée — leur alignement ne dépend plus de
   ce que leurs textes respectifs occupent une hauteur identique. */
.site-footer__inner {
  display: grid;
  grid-template-columns: minmax(0, 1fr) repeat(3, minmax(0, 1fr));
  grid-template-rows: auto auto;
  column-gap: var(--col-gap);
  row-gap: clamp(1rem, 2vw, 1.6rem);
  /* Pas d'`align-items: start` ici : les colonnes doivent s'étirer sur les
     deux rangées pour que leur subgrid s'y superpose. L'alignement se règle
     à l'intérieur, élément par élément. */
}

/* Le « A » traverse les DEUX rangées. Laissé en placement automatique, il
   tombait dans la première et sa hauteur — jusqu'à 124 px — la dilatait
   d'autant, repoussant la rangée des actions très loin sous les textes.
   C'est ce qui creusait le grand vide vertical du pied de page. */
.site-footer__mark {
  grid-row: 1 / -1;
  align-self: start;
  display: block;
  width: clamp(72px, 8vw, 124px);
  color: var(--c-ink);
}

.site-footer__mark svg {
  width: 100%;
  height: auto;
  fill: currentColor;
}

.site-footer__col {
  --footer-action-h: 27px;
  display: grid;
  grid-row: 1 / -1;
  grid-template-rows: subgrid;
  font-size: var(--t-body);
  line-height: 1.55;
}

/* Placement EXPLICITE dans les deux rangées, et non déduit de l'ordre des
   enfants. La colonne des liens légaux n'a qu'un seul enfant : il était donc
   à la fois premier et dernier, et la règle « le dernier va dans la rangée
   des actions » le centrait dans la rangée du texte — d'où « Mentions
   légales » plus bas que l'adresse voisine. */
.site-footer__stack,
.site-footer__address,
.site-footer__social-text {
  grid-row: 1;
  align-self: start;
  margin: 0;
}

/* Rangée des actions : chacun centré sur la hauteur commune, pour que les
   centres coïncident même si la flèche et l'icône n'ont pas la même hauteur. */
.site-footer__contact,
.site-footer__social {
  grid-row: 2;
  align-self: center;
}

.site-footer__stack {
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
}

.site-footer__col a:not(.arrow-link):hover {
  text-decoration: underline;
  text-underline-offset: 0.22em;
}

.site-footer__address {
  font-style: normal;
}

/* L'icône Instagram doit s'aligner sur le centre optique de la ligne
   « → Nous contacter » de la colonne voisine. Cette ligne est un arrow-link de
   --t-small ; on reproduit sa hauteur de ligne et on centre l'icône dedans,
   plutôt que de la caler par une marge arbitraire qui se décalerait au
   moindre changement de corps. */
.site-footer__social {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  /* L'espacement vertical vient du row-gap de la grille, plus d'une marge :
     deux sources concurrentes finiraient par diverger. */
  min-height: var(--footer-action-h);
}

.site-footer__social a {
  display: grid;
  place-items: center;
  width: 27px;
  height: 27px;
  color: var(--c-ink);
  /* Même remède que pour les flèches : couche maintenue, pas de re-rendu en
     fin de transition, donc pas de saut d'échelle. */
  transform: scale3d(1, 1, 1);
  backface-visibility: hidden;
  transition: transform var(--d-fast) var(--e-out);
}

.site-footer__social a:hover {
  transform: scale3d(1.1, 1.1, 1);
}

.site-footer__social svg {
  width: 100%;
  height: auto;
  fill: currentColor;
}

.site-footer__contact {
  min-height: var(--footer-action-h);
}

/* Mobile : version simplifiée — le « A », « Nous contacter », « Instagram ».
   Les liens légaux et l'adresse sont masqués, conformément aux maquettes
   mobiles, mais le lien de contact reste unique dans le document : il n'est
   pas dupliqué pour les deux mises en page. */
@media (max-width: 900px) {
  .site-footer__inner {
    grid-template-columns: auto minmax(0, 1fr);
    grid-template-rows: auto auto;
    gap: 0.9rem 1.5rem;
    align-items: center;
  }

  /* La mise en page mobile est une pile, pas trois colonnes alignées : le
     subgrid n'a plus d'objet et imposerait ses deux rangées à des colonnes
     qui n'en ont plus besoin. Le placement explicite en rangées doit être
     annulé avec lui, sinon les enfants restent assignés à des rangées qui
     n'existent plus dans ce contexte. */
  .site-footer__col {
    display: flex;
    flex-direction: column;
    grid-row: auto;
    gap: 0.55rem;
  }

  .site-footer__mark {
    grid-row: auto;
  }

  .site-footer__stack,
  .site-footer__address,
  .site-footer__social-text,
  .site-footer__contact,
  .site-footer__social {
    grid-row: auto;
    align-self: flex-start;
  }

  .site-footer__mark {
    /* Le « A » occupe la colonne de gauche sur les deux lignes */
    grid-row: 1 / span 2;
    align-self: center;
  }

  .site-footer__col--links {
    display: none;
  }

  .site-footer__address {
    display: none;
  }

  .site-footer__col--address {
    grid-column: 2;
    grid-row: 1;
  }

  .site-footer__col--social {
    grid-column: 2;
    grid-row: 2;
    flex-direction: row;
    align-items: center;
    gap: 0.75rem;
  }

  /* Sur mobile, l'icône précède le texte sur une seule ligne */
  .site-footer__social {
    order: -1;
    margin-top: 0;
  }

  .site-footer__social a {
    width: 22px;
  }

  .site-footer__social-text {
    font-size: var(--t-tiny);
    line-height: 1.3;
  }

  .site-footer__contact {
    margin-top: 0;
  }
}

/* ------------------------------------------------------------ étiquettes */

/* mads-label encapsule son rendu ; ces variables pilotent ses variantes
   depuis l'extérieur du shadow DOM. */
mads-label {
  --label-color: var(--c-ink);
  --label-border: var(--c-ink);
}

mads-label[data-tone='muted'] {
  --label-color: var(--c-muted);
  --label-border: var(--c-muted);
}

/* Composant non enregistré (JavaScript absent ou en échec) : la pilule est
   reconstituée en CSS pur, sans l'étoile. Mieux vaut un libellé encadré
   qu'un fragment de texte nu au milieu d'une page. */
mads-label:not(:defined) {
  display: inline-block;
  padding: 4px 12px;
  border: 1px solid var(--label-border);
  border-radius: var(--radius-label);
  color: var(--label-color);
  font-size: var(--t-small);
  line-height: 1.25;
}

/* Idem pour le menu : il se replie en barre de liens horizontale dans
   l'en-tête, lisible sur fond clair. */
mads-menu:not(:defined) {
  display: block;
  padding-right: 2.5rem;
}

mads-menu:not(:defined) .menu-nav {
  flex-direction: row;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 0.35rem 1.15rem;
}

mads-menu:not(:defined) .menu-nav a {
  font-family: var(--f-body);
  font-size: var(--t-small);
  color: var(--c-ink);
}

mads-menu:not(:defined) .menu-foot {
  display: none;
}

/* --------------------------------------------- transitions entre pages */

/* Nommer main permet à l'API View Transitions de faire correspondre l'ancien
   et le nouveau contenu, et donc de les animer l'un vers l'autre plutôt que
   de recolorer tout l'écran. */
main {
  view-transition-name: mads-main;
}

@media (prefers-reduced-motion: no-preference) {
  ::view-transition-old(mads-main) {
    animation: mads-fade-out var(--d-fast) var(--e-inout) both;
  }

  ::view-transition-old(root) {
    animation-duration: var(--d-fast);
  }

  ::view-transition-new(mads-main) {
    animation: mads-rise var(--d-base) var(--e-out) both;
  }
}

@keyframes mads-fade-out {
  to {
    opacity: 0;
    transform: translateY(-1.25rem);
  }
}

@keyframes mads-rise {
  from {
    opacity: 0;
    transform: translateY(1.75rem);
  }
}

/* Repli pour les navigateurs sans View Transitions : le routeur pose ces
   classes sur <html> à la place. */
html.is-leaving main {
  opacity: 0;
  transform: translateY(-1.25rem);
  transition: opacity var(--d-fast) var(--e-inout), transform var(--d-fast) var(--e-inout);
}

html.is-entering main {
  animation: mads-rise var(--d-base) var(--e-out) both;
}

/* ------------------------------------------- révélation au défilement */

/* Progressive enhancement : sans JS, aucun élément n'est masqué. C'est
   mads-reveal qui pose data-reveal="pending", donc rien ne peut rester
   invisible si le script échoue à se charger. */
[data-reveal='pending'] {
  opacity: 0;
  transform: translateY(1.5rem);
}

[data-reveal='in'] {
  opacity: 1;
  transform: none;
  transition:
    opacity var(--d-slow) var(--e-out),
    transform var(--d-slow) var(--e-out);
  transition-delay: var(--reveal-delay, 0ms);
}

@media (prefers-reduced-motion: reduce) {
  [data-reveal='pending'] {
    opacity: 1;
    transform: none;
  }
}
