/* ═══════════════════════════════════════════════════════════════════════════
   NUIT — système de design du site public Messana.      (2ᵉ refonte, 31/07)

   L'IDÉE, en une phrase : on ENTRE AU JOUR et on descend dans la nuit. Le
   héros est un papier blanc, presque vide ; une bande de photo fait passer le
   jour à la nuit ; le Mystère et l'Aventure se jouent dans l'encre la plus
   profonde ; puis la page remonte vers le papier et finit à l'aube. La
   luminosité dessine un V — c'est ce V qui tient la page, pas une couleur.

   CE QUE CETTE RÉFONTE CORRIGE. La version précédente empilait huit fonds
   teintés (bleu nuit, ambre brûlé, vert profond, bordeaux, trois crèmes) et
   un seul geste d'animation répété partout. Comparée à tinder.com, l'écart
   n'était pas la palette :
     1. le titre de héros occupait 45 % de la largeur d'écran contre 78 %
        chez eux, bridé par `max-width:15ch` et un plafond de 6,6 rem ;
     2. ce héros portait six éléments là où une affiche en porte trois ;
     3. les chapitres étaient des grilles deux colonnes — une page SaaS, pas
        une suite d'affiches ;
     4. `.monte` (translateY 26 px + fondu) animait TOUT, à l'identique.
   Les quatre sont traités ici. Le cinquième — la direction artistique des
   images — ne se règle pas en CSS et reste ouvert.

   D'où vient la couleur : de `frontend/src/proTheme.ts`, verrouillé le 11/07.
   Le terracotta « j'aime » (#C25B54), le champagne (#E7D3AC) et la nuit
   (#16131A) sont ceux de l'app. Le vert (#3A6B63) aussi, mais il a changé de
   RÔLE : il n'est plus l'accent, il est la couleur d'état (focus, sélection,
   survol). L'accent expressif est désormais la lune — bronze sur papier,
   crème sur encre.

   CONTRAINTE DE PLATEFORME : la CSP du site est `style-src 'self'`
   (backend/src/app.js). AUCUN style inline n'est possible — ni attribut
   `style=`, ni bloc <style>. Tout vit dans ce fichier et ses deux compagnons
   (accueil.css, abonnement.css). Aucune variable ne peut être posée depuis le
   HTML : les variations passent par des classes et des sélecteurs de position.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── Polices auto-hébergées ───────────────────────────────────────────────────
   Fraunces (display) et Inter (texte), variables, sous-ensembles latin +
   latin-ext, licence SIL OFL. Auto-hébergées pour trois raisons : `font-src
   'self'` dans la CSP, aucune IP de visiteur envoyée à un tiers (RGPD), et
   surtout un rendu IDENTIQUE partout. L'ancien site déclarait 'Iowan Old
   Style' — une police qui n'existe que chez Apple : sur Windows et Android il
   retombait sur Georgia, et le site n'avait la typographie de personne.

   `font-display: swap` : le texte s'affiche immédiatement dans la police de
   repli, puis bascule. Jamais de titre invisible pendant le chargement.
   ────────────────────────────────────────────────────────────────────────── */
@font-face {
  font-family: 'Fraunces'; font-style: normal; font-weight: 300 900;
  font-display: swap; src: url('/assets/fonts/fraunces-normal-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
    U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Fraunces'; font-style: normal; font-weight: 300 900;
  font-display: swap; src: url('/assets/fonts/fraunces-normal-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304,
    U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0,
    U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Fraunces'; font-style: italic; font-weight: 300 900;
  font-display: swap; src: url('/assets/fonts/fraunces-italic-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
    U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Fraunces'; font-style: italic; font-weight: 300 900;
  font-display: swap; src: url('/assets/fonts/fraunces-italic-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304,
    U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0,
    U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Inter'; font-style: normal; font-weight: 300 800;
  font-display: swap; src: url('/assets/fonts/inter-normal-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
    U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Inter'; font-style: normal; font-weight: 300 800;
  font-display: swap; src: url('/assets/fonts/inter-normal-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304,
    U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0,
    U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ── Jetons ────────────────────────────────────────────────────────────────── */
:root {
  color-scheme: light;

  /* ── LES DEUX FAMILLES ───────────────────────────────────────────────────
     Version précédente : huit fonds — bleu nuit, ambre brûlé, vert profond,
     bordeaux, trois crèmes. Huit teintes, donc aucune identité. Un site
     premium ne se reconnaît pas à la richesse de sa palette mais à sa
     DISCIPLINE : Hinge publie la sienne — 80 % de la surface en blanc + noir,
     20 % en UNE couleur expressive.

     Il ne reste donc que deux familles : trois papiers chauds et quatre encres
     de la même teinte (le violet-nuit #16131A de proTheme.ts, désaturé). Ce
     qui porte le rythme n'est plus la teinte mais la VALEUR — la page descend
     du papier le plus clair jusqu'au cœur de la nuit, puis remonte vers
     l'aube. Un écart de valeur se voit de loin ; six noirs à peine teintés,
     non. */

  --p-blanc:#FDFCFA;       /* le héros — blanc chaud, jamais #FFF */
  --p-ivoire:#F2EDE5;
  --p-sable:#E9E2D6;

  --e-haute:#191521;
  --e-1:#15121D;
  --e-fond:#0B0910;        /* le cœur de la nuit — l'Aventure */
  --e-aube:#241722;        /* l'encre réchauffée de la fin */

  --fond:var(--p-blanc);

  /* La couleur du TEXTE et ses retraits — valeurs de jour par défaut, `.nuit`
     les retourne d'un bloc. `--texte-4` reste sous le seuil AA : il n'habille
     QUE du décor (filets, tirets d'absence), jamais une phrase.

     `--texte-3` est à .62 et non .58. Le .58 venait du système SOMBRE, où il
     donnait 6,1 de contraste ; en basculant sur papier il tombait à 4,12 sur
     ivoire et 4,00 sur sable — sous les 4,5 exigés par WCAG AA, et sur du
     12 px (surtitres, mentions, prix). Une valeur d'opacité ne se transporte
     PAS d'un fond à l'autre : elle se recalcule.
     Recalculé contre le pire des trois papiers (--p-sable, le plus foncé) :
     .58→4,00 · .61→4,52 · .62→4,66 · .63→4,81. Retenu .62 pour la marge. */
  --texte:#171320;
  --texte-2:rgba(23,19,32,.74);
  --texte-3:rgba(23,19,32,.62);
  --texte-4:rgba(23,19,32,.30);
  --hair:rgba(23,19,32,.12);
  --hair-2:rgba(23,19,32,.22);

  /* ── LE 20 % EXPRESSIF ───────────────────────────────────────────────────
     UN seul accent traverse toute la page, à deux valeurs : bronze sur papier,
     crème lunaire sur encre. C'est la LUNE — le cœur de l'identité Messana.

     Le vert n'est pas supprimé, il est DÉMIS : il devient une couleur d'ÉTAT
     (focus clavier, sélection, survol, le « oui » des tableaux). Il vient de
     proTheme.ts et garde la continuité avec l'app. Mesuré avant de décider :
     au repos il n'occupait déjà que deux endroits de la page d'accueil — un
     surtitre et trois filets de 26 px. Ce n'était pas un accent, c'était un
     résidu. */
  --lune:#7A5A22;          /* bronze — l'accent sur papier */
  --accent:#2C5C52;        /* état : focus, sélection, survol */
  --love:#A93F37;          /* strictement le « j'aime » */
  --or:#8A6420;
  --champagne:#7A5A22;

  /* Élévation. Sur papier l'ombre existe vraiment — mais teintée d'encre, pas
     du gris neutre qui fait « composant Material ». */
  --ombre-1:0 1px 2px rgba(23,19,32,.07);
  --ombre-2:0 4px 12px rgba(23,19,32,.09), 0 1px 2px rgba(23,19,32,.06);
  --ombre-3:0 14px 34px -18px rgba(23,19,32,.42), 0 1px 3px rgba(23,19,32,.10);
  --ombre-4:0 30px 70px -34px rgba(23,19,32,.50), 0 2px 8px rgba(23,19,32,.12);

  --display:'Fraunces','Iowan Old Style','Palatino Linotype',Palatino,Georgia,serif;
  --police:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,'Helvetica Neue',sans-serif;
  --mono:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;

  /* ── L'ÉCHELLE ───────────────────────────────────────────────────────────
     Mesuré sur tinder.com : leur titre de héros occupe ~78 % de la largeur de
     l'écran. Ici il en occupait ~45 % — `--t-hero` plafonnait à 6.6rem ET le
     titre était bridé à `max-width:15ch` centré. Un titre qui n'ose pas
     remplir l'écran ne sera jamais premium ; c'est le premier écart, avant
     toute question de couleur.

     `--t-affiche` est une taille NOUVELLE, réservée au héros et au manifeste :
     elle se pilote au vw pur (pas de `rem + vw`) pour que le titre garde
     exactement la même proportion d'écran à toutes les largeurs.

     Le 8,45vw n'est pas choisi au jugé. Chasse de Fraunces MESURÉE sur la page
     rendue, à ce poids et avec ce crénage : 0,335 em par caractère — nettement
     plus étroite que l'estimation à vue (0,47), d'où un premier réglage qui
     laissait le titre à 58 %. La ligne la plus longue du héros fait 27,5
     caractères ; il faut donc 8,45vw pour qu'elle occupe 78 % de la largeur
     d'écran. Le plafond de 10rem est celui qui tient encore dans la coque
     large une fois ses gouttières déduites (1 536 px utiles) : au delà, le
     titre déborderait au lieu de grandir. */
  --t-affiche:clamp(2.4rem, 8.45vw, 10rem);
  --t-geant:clamp(2.7rem, 6.2vw, 7.4rem);
  --t-hero:clamp(2.5rem, 5.4vw, 6.2rem);
  --t-h1:clamp(2.3rem, 1rem + 5vw, 4.9rem);
  --t-h2:clamp(2rem, 1.1rem + 3.4vw, 3.9rem);
  --t-h3:clamp(1.22rem, 1.02rem + .82vw, 1.72rem);
  --t-lede:clamp(1.1rem, 1rem + .72vw, 1.44rem);
  --t-corps:clamp(1rem, .968rem + .16vw, 1.09rem);
  --t-petit:.875rem;
  --t-micro:.78rem;

  /* Le héros et les plaques pleine page ÉCHAPPENT à `--coque` : un titre
     d'affiche enfermé dans 1240 px se lit comme un article, pas comme une
     affiche. */
  --coque:1240px;
  --coque-large:1680px;
  --coque-etroite:820px;
  --gout:clamp(20px, 4.4vw, 44px);
  --air:clamp(72px, 11vh, 148px);
  /* Hauteur réelle de la barre, mesurée sur la page rendue. Elle est `sticky`,
     donc elle occupe cette hauteur DANS LE FLUX : tout ce qui veut faire un
     plein écran doit la déduire. Elle rétrécit sous 860 px — cf. plus bas. */
  --nav-h:82px;
  --r-s:10px; --r-m:16px; --r-l:24px; --r-xl:34px;

  --sortie:cubic-bezier(.16,1,.3,1);
  --entree-sortie:cubic-bezier(.65,0,.35,1);
  --doux:cubic-bezier(.22,.61,.36,1);
}

/* ── Base ──────────────────────────────────────────────────────────────────── */
*,*::before,*::after { box-sizing:border-box }

html {
  -webkit-text-size-adjust:100%;
  scroll-behavior:smooth;
  /* La nav collante mesure ~70px : sans ce décalage, une ancre pose le titre
     visé DERRIÈRE la barre. */
  scroll-padding-top:88px;
}
@media (prefers-reduced-motion: reduce) { html { scroll-behavior:auto } }

body {
  margin:0;
  background:var(--p-blanc);
  color:var(--texte);
  font-family:var(--police);
  font-size:var(--t-corps);
  line-height:1.62;
  font-weight:350;
  font-optical-sizing:auto;
  -webkit-font-smoothing:antialiased;
  -moz-osx-font-smoothing:grayscale;
  text-rendering:optimizeLegibility;
  overflow-x:hidden;
}

img,svg,video { display:block; max-width:100% }
a { color:inherit }
h1,h2,h3,h4 { margin:0; font-weight:inherit; text-wrap:balance }
p { margin:0; text-wrap:pretty }
ul,ol { margin:0; padding:0; list-style:none }
button { font:inherit; color:inherit }

::selection { background:var(--accent); color:var(--fond) }

:focus-visible {
  outline:2px solid var(--accent);
  outline-offset:3px;
  border-radius:6px;
}

.sr {
  position:absolute; width:1px; height:1px;
  padding:0; margin:-1px; overflow:hidden;
  clip-path:inset(50%); white-space:nowrap; border:0;
}

.saut {
  position:fixed; left:16px; top:-80px; z-index:120;
  background:var(--texte); color:var(--fond);
  padding:12px 20px; border-radius:var(--r-s);
  font-weight:600; font-size:var(--t-petit); text-decoration:none;
  transition:top .2s var(--sortie);
}
.saut:focus { top:16px }

/* ── Grilles ───────────────────────────────────────────────────────────────── */
.coque { width:100%; max-width:var(--coque); margin-inline:auto; padding-inline:var(--gout) }
.coque-large { width:100%; max-width:var(--coque-large); margin-inline:auto; padding-inline:clamp(20px,5vw,72px) }
.coque-etroite { width:100%; max-width:var(--coque-etroite); margin-inline:auto; padding-inline:var(--gout) }

/* Un chapitre = une VALEUR, plus une teinte. `chapitre` donne le rythme
   vertical, `p-*` / `e-*` posent le papier ou l'encre. Chacun pose `--fond`
   PUIS s'en sert : la couleur n'est écrite qu'une fois, et tout le reste du
   système peut la relire. */
.chapitre { position:relative; padding-block:var(--air); overflow:clip; background:var(--fond); color:var(--texte) }

.p-blanc  { --fond:var(--p-blanc) }
.p-ivoire { --fond:var(--p-ivoire) }
.p-sable  { --fond:var(--p-sable) }

/* Les encres portent TOUTES `.nuit` en plus — cf. l'inversion, plus bas. */
.e-haute { --fond:var(--e-haute) }
.e-1     { --fond:var(--e-1) }
.e-fond  { --fond:var(--e-fond) }
.e-aube  { --fond:var(--e-aube) }

/* ── LA PLAQUE ────────────────────────────────────────────────────────────
   Une idée, un écran. C'est le deuxième écart mesuré face à tinder.com : là
   où ils donnent à chaque idée un écran entier pleine largeur, le site
   empilait des grilles deux colonnes texte-gauche / matière-droite. C'est une
   composition de page SaaS — efficace, et sans aucune tenue.

   `100svh` et non `100vh` : sur mobile `vh` compte la barre d'URL rétractée,
   donc la plaque dépasse de l'écran au chargement. */
.plaque {
  min-height:100svh;
  display:grid; align-content:center;
  padding-block:clamp(88px,13vh,150px);
}
/* Une plaque qui n'a PAS besoin de l'écran entier (sécurité, tarifs, FAQ :
   elles portent une liste, pas une affiche) garde le rythme sans la hauteur. */
.plaque-basse { min-height:auto; padding-block:clamp(96px,15vh,190px) }

/* ── L'inversion ──────────────────────────────────────────────────────────
   Le système est écrit PAPIER-D'ABORD : les jetons par défaut sont ceux du
   jour. Un seul bloc bascule un conteneur — et tout ce qu'il contient — dans
   la nuit, parce qu'aucun composant ne code de couleur en dur : ils ne
   connaissent que `--texte`, `--hair`, `--lune`, `--fond`.

   Les accents montent aussi : le bronze #7A5A22 est fait pour mordre sur du
   crème, il tombe à 1,9 de contraste sur de l'encre. Chaque accent a donc sa
   valeur de nuit, vérifiée au même audit que le reste.

   `.nuit-locale` est le même bloc avec son propre fond : il sert à ce qui
   représente un ÉCRAN posé sur du papier — la maquette de téléphone reste
   sombre, sinon ses textes héritent de l'encre et deviennent noirs sur une
   photo noire.
   ────────────────────────────────────────────────────────────────────────── */
.nuit, .nuit-locale {
  --texte:#F6F3EC;
  --texte-2:rgba(246,243,236,.72);
  --texte-3:rgba(246,243,236,.58);
  --texte-4:rgba(246,243,236,.32);
  --hair:rgba(246,243,236,.13);
  --hair-2:rgba(246,243,236,.22);
  --lune:#E8DFC9;
  --accent:#7FB6A8;
  --love:#D9736A;
  --or:#C9A15C;
  --champagne:#E7D3AC;
  --ombre-1:0 1px 2px rgba(0,0,0,.5);
  --ombre-2:0 4px 12px rgba(0,0,0,.45), 0 1px 2px rgba(0,0,0,.4);
  --ombre-3:0 18px 48px -20px rgba(0,0,0,.9), 0 2px 6px rgba(0,0,0,.5);
  --ombre-4:0 40px 90px -40px rgba(0,0,0,1), 0 4px 12px rgba(0,0,0,.6);
  color-scheme:dark;
  color:var(--texte);
}
.nuit-locale { --fond:var(--e-haute) }

/* Frontières FRANCHES entre plaques. La version précédente fondait chaque
   chapitre dans du noir sur 140 px — un raccord utile quand les couleurs se
   ressemblaient, et exactement ce qui les empêchait de trancher une fois
   qu'elles ne se ressemblaient plus. Une arête nette se lit comme un choix.
   La SEULE transition fondue du site est la bande crépuscule (accueil.css),
   parce qu'elle raconte quelque chose : le passage du jour à la nuit. */

/* ── Typographie ───────────────────────────────────────────────────────────── */

/* Fraunces a une très large plage optique (opsz 9→144). L'utiliser, c'est la
   moitié du « premium » : à 9 les empattements sont épais et lisibles, à 144
   ils s'affinent et le contraste monte, comme un vrai caractère de titrage
   gravé pour la grande taille. Une police statique ne peut pas faire ça. */
.affiche, .geant, .hero-titre, h1, h2, .titre {
  font-family:var(--display);
  font-weight:600;
  letter-spacing:-.028em;
  line-height:.98;
  font-variation-settings:'opsz' 144;
}
/* `.affiche` est la taille du héros et du manifeste. À cette échelle les
   coupes de ligne ne se devinent pas, elles se DÉCIDENT : chaque ligne est
   écrite à la main dans le HTML (`.l`), ce qui rend au passage le dévoilement
   ligne à ligne possible. `balance` est donc désarmé — il n'a rien à
   rééquilibrer et ferait bouger des coupes déjà choisies. Le repli reste
   possible : sur un téléphone une `.l` trop longue passe à la ligne, et le
   bloc se dévoile alors d'un seul tenant. */
.affiche {
  font-size:var(--t-affiche); line-height:.88; letter-spacing:-.042em;
  text-wrap:initial;
}
.geant { font-size:var(--t-geant); line-height:.9; letter-spacing:-.038em }
.hero-titre { font-size:var(--t-hero); line-height:.94; letter-spacing:-.034em }
h1 { font-size:var(--t-h1) }
h2 { font-size:var(--t-h2) }
h3 {
  font-family:var(--display); font-size:var(--t-h3); font-weight:600;
  letter-spacing:-.015em; line-height:1.18; font-variation-settings:'opsz' 40;
}

/* L'italique de Fraunces est le seul « effet » typographique du site. Il porte
   l'accent d'une phrase — jamais une phrase entière, jamais un paragraphe. */
em, .it { font-style:italic; font-variation-settings:'opsz' 144 }

.sourdine { color:var(--texte-3) }
.lueur { color:var(--lune) }

.surtitre {
  font-family:var(--police);
  font-size:var(--t-micro); font-weight:600;
  letter-spacing:.19em; text-transform:uppercase;
  color:var(--texte-3);
  margin-bottom:1.1rem;
}
.surtitre.acc { color:var(--accent) }

.lede {
  font-size:var(--t-lede); line-height:1.5; font-weight:300;
  color:var(--texte-2); max-width:44ch;
}
.corps { color:var(--texte-2); max-width:62ch }
.corps + .corps { margin-top:1.1em }
.corps a, .fin a { color:var(--texte); text-decoration:underline; text-underline-offset:3px;
  text-decoration-color:var(--texte-4); transition:text-decoration-color .2s }
.corps a:hover, .fin a:hover { text-decoration-color:var(--accent) }

/* Une citation qui coupe le flux. Pas de guillemets décoratifs géants : le
   changement de casse et l'italique suffisent, et ils ne datent pas. */
.detache {
  font-family:var(--display); font-style:italic; font-weight:400;
  font-size:clamp(1.35rem, 1rem + 1.6vw, 2.1rem);
  line-height:1.28; letter-spacing:-.02em;
  color:var(--lune);
  margin-top:2rem; padding-left:1.4rem;
  border-left:1px solid var(--hair-2);
  max-width:34ch;
}

.fin {
  font-size:var(--t-micro); line-height:1.6; color:var(--texte-3);
  max-width:78ch;
}

/* ── Boutons ───────────────────────────────────────────────────────────────── */
.btn {
  display:inline-flex; align-items:center; justify-content:center; gap:.6em;
  padding:.92em 1.7em; border-radius:999px; border:1px solid transparent;
  font-family:var(--police); font-size:var(--t-petit); font-weight:600;
  letter-spacing:.005em; text-decoration:none; cursor:pointer;
  transition:transform .22s var(--sortie), background-color .22s, border-color .22s, color .22s;
}
.btn:active { transform:scale(.975) }

/* `color:var(--fond)` et non une encre en dur : sur une plaque claire, le bouton
   plein devient encre sur crème — écrire le noir en dur donnait du noir sur
   noir. Le bouton lit la couleur du chapitre où il se trouve. */
.btn-clair { background:var(--texte); color:var(--fond) }
.btn-clair:hover { background:color-mix(in srgb, var(--texte) 88%, var(--fond)) }

.btn-fantome { border-color:var(--hair-2); color:var(--texte); background:transparent }
.btn-fantome:hover { border-color:var(--texte-4); background:color-mix(in srgb, var(--texte) 7%, transparent) }

.btn-accent { background:var(--accent); color:var(--fond) }
.btn-accent:hover { background:color-mix(in srgb, var(--accent) 84%, var(--texte)) }

.btn-lg { padding:1.05em 2.1em; font-size:1rem }

/* Lien fléché : la flèche avance au survol. Le seul micro-mouvement des liens. */
.lien-fleche {
  display:inline-flex; align-items:center; gap:.5em;
  font-weight:600; font-size:var(--t-petit); text-decoration:none;
  color:var(--texte); border-bottom:1px solid var(--hair-2);
  padding-bottom:.25em; transition:border-color .2s, color .2s;
}
.lien-fleche svg { transition:transform .28s var(--sortie) }
.lien-fleche:hover { border-color:var(--accent); color:var(--accent) }
.lien-fleche:hover svg { transform:translateX(4px) }

/* ── Boutons de boutique ───────────────────────────────────────────────────── */
.boutiques { display:flex; flex-wrap:wrap; gap:12px }
.boutique {
  display:inline-flex; align-items:center; gap:.75rem;
  padding:.72rem 1.25rem .78rem; border-radius:14px;
  background:var(--texte); color:var(--fond);
  text-decoration:none;
  transition:transform .22s var(--sortie), background-color .22s;
}
.boutique:hover { background:color-mix(in srgb, var(--texte) 88%, var(--fond)); transform:translateY(-2px) }
.boutique:active { transform:translateY(0) scale(.98) }
.boutique span { display:flex; flex-direction:column; line-height:1.12; text-align:left }
.boutique .k { font-size:.63rem; font-weight:500; letter-spacing:.055em; text-transform:uppercase; opacity:.62 }
.boutique .n { font-size:1rem; font-weight:700; letter-spacing:-.012em }

/* Boutique pas encore ouverte (iOS). C'est un ÉTAT, pas un bouton : ni lien, ni
   curseur main, ni élan au survol — trois signaux qui, réunis, disent « rien ne
   se passera si tu cliques » avant même de lire le libellé. Le contraste baisse
   sans descendre sous le seuil de lisibilité : l'information reste utile, elle
   cesse simplement d'appeler le geste. */
.boutique-attente {
  background:color-mix(in srgb, var(--texte) 26%, transparent);
  color:var(--texte);
  border:1px solid color-mix(in srgb, var(--texte) 34%, transparent);
  cursor:default;
}
.boutique-attente:hover { background:color-mix(in srgb, var(--texte) 26%, transparent); transform:none }
.boutique-attente .k { opacity:.72 }

/* ── La lune ───────────────────────────────────────────────────────────────────
   Le sceau de la marque est une VRAIE lune, et sa phase suit la lecture : à
   l'entrée du site c'est un croissant, en bas de page elle est pleine. Le
   Mystère s'ouvre « à l'arrivée de la lune » — le site le raconte sans un mot.

   Comment : un masque SVG dont le disque occultant glisse en X. La translation
   tourne sur le compositeur (transform), et `animation-timeline: scroll(root)`
   la pilote au scroll SANS un octet de JavaScript ni écouteur d'événement.

   ⚠ LE DÉCALAGE DE REPOS EST LOAD-BEARING. L'occulteur du sceau de nav est
   écrit `cx=50 cy=50 r=42` dans le SVG — c'est-à-dire EXACTEMENT le disque.
   Sans transformation il le recouvre donc intégralement : masque tout noir,
   lune invisible. La lune n'existait que par l'animation, et disparaissait
   partout où l'animation ne s'applique pas — Firefox (pas de `scroll()` par
   défaut) et toute machine réglée sur « réduire les animations ». Ce commentaire
   affirmait le contraire (« on reste sur un croissant fixe ») ; c'était faux.

   D'où le `translateX(14px)` posé ICI, en dur, dans l'état de repos : il vaut
   le `from` de l'animation, donc le croissant du haut de page. L'animation ne
   fait plus que le remplacer quand elle est disponible. Le sceau du PIED n'a
   jamais eu ce défaut : son occulteur est décalé dans le SVG (`cx=58 cy=44`).
   ────────────────────────────────────────────────────────────────────────── */
.lune-sceau { width:1.65em; height:1.65em; flex:none; color:var(--lune) }
.lune-sceau .disque { fill:currentColor }
.lune-sceau .occulteur { fill:#000; transform:translateX(14px) }

@supports (animation-timeline: scroll(root)) {
  @media (prefers-reduced-motion: no-preference) {
    .lune-sceau .occulteur {
      animation:phase-lunaire linear both;
      animation-timeline:scroll(root);
      animation-range:0% 92%;
    }
  }
}
@keyframes phase-lunaire {
  from { transform:translateX(14px) }
  to   { transform:translateX(58px) }
}

/* ── Navigation ────────────────────────────────────────────────────────────── */
.nav {
  position:sticky; top:0; z-index:60;
  border-bottom:1px solid transparent;
  transition:background-color .35s var(--sortie), border-color .35s var(--sortie),
             backdrop-filter .35s var(--sortie);
}
/* La barre reste CLAIRE sur toute la page, y compris au-dessus des plaques
   d'encre. Une barre qui change de camp à chaque plaque oblige à connaître le
   fond qui passe dessous — soit un observateur JS par section, soit une teinte
   qui se trompe une fois sur deux. Une barre constante est aussi ce que font
   Tinder et Hinge : le repère de navigation ne participe pas à la mise en
   scène, il la surplombe. */
.nav.pose {
  background:color-mix(in srgb, var(--p-blanc) 84%, transparent);
  border-bottom-color:var(--hair);
  backdrop-filter:saturate(150%) blur(18px);
  -webkit-backdrop-filter:saturate(150%) blur(18px);
}
.nav-in {
  max-width:var(--coque); margin-inline:auto;
  padding:16px var(--gout);
  display:flex; align-items:center; justify-content:space-between; gap:20px;
}
.marque {
  display:inline-flex; align-items:center; gap:.55em;
  font-family:var(--display); font-size:1.22rem; font-weight:600;
  letter-spacing:-.012em; text-decoration:none; color:var(--texte);
  font-variation-settings:'opsz' 40;
}
.nav-liens { display:flex; align-items:center; gap:clamp(14px, 2vw, 30px) }
/* `:not(.btn)` n'est PAS cosmétique. `.nav-liens a` pèse 0-1-1, `.btn-clair`
   pèse 0-1-0 : sans l'exclusion, la barre repeignait le texte du bouton en
   crème SUR un fond crème, et « Obtenir l'app » devenait illisible. Trouvé par
   l'audit de contraste automatique, pas à l'œil — le rapport était de 1,00. */
/* `padding-block` + `inline-block` pour la CIBLE, pas pour le rythme : mesurés
   sur la page rendue, ces liens faisaient 23 px de haut, sous le minimum de
   24 px de WCAG 2.5.8. L'exception « lien dans une phrase » ne s'applique pas
   ici — ce sont des entrées de navigation isolées. Le `margin-block` négatif
   rend les 4 px repris, pour que la barre garde exactement sa hauteur. */
.nav-liens a:not(.btn) {
  display:inline-block; padding-block:5px; margin-block:-5px;
  font-size:var(--t-petit); font-weight:450; color:var(--texte-2);
  text-decoration:none; transition:color .2s;
}
.nav-liens a:not(.btn):hover,
.nav-liens a:not(.btn)[aria-current="page"] { color:var(--texte) }
.nav-cta { margin-left:8px }

/* `flex-direction:column` est LOAD-BEARING. Sans lui le conteneur reste en
   `row` : les trois barres se posaient CÔTE À CÔTE au lieu de s'empiler, et
   comme 3 × 20 px ne tiennent pas dans 44, elles étaient écrasées à 11 px
   chacune. Le `margin-top` de `span + span` décalait alors la 2ᵉ et la 3ᵉ
   verticalement au lieu de les espacer — au lieu d'un hamburger, deux tirets
   bancals. Mesuré sur la page rendue : 11 × 1,5 px aux ordonnées 37, 40, 40. */
.nav-bascule {
  display:none; width:44px; height:44px; margin-right:-8px;
  flex-direction:column; align-items:center; justify-content:center;
  background:none; border:0; cursor:pointer; color:var(--texte);
}
.nav-bascule span {
  display:block; width:20px; height:1.5px; background:currentColor;
  transition:transform .3s var(--sortie), opacity .2s;
}
.nav-bascule span + span { margin-top:5px }
.nav[data-menu="ouvert"] .nav-bascule span:first-child { transform:translateY(6.5px) rotate(45deg) }
.nav[data-menu="ouvert"] .nav-bascule span:nth-child(2) { opacity:0 }
.nav[data-menu="ouvert"] .nav-bascule span:last-child { transform:translateY(-6.5px) rotate(-45deg) }

@media (max-width: 860px) {
  /* Mesurée à 77 px sur téléphone contre 82 en bureau : le bouton hamburger
     est moins haut que la rangée de liens. Sans cette correction, tout ce qui
     calcule `100svh - var(--nav-h)` retire 5 px de trop. */
  :root { --nav-h:77px }
  .nav-bascule { display:flex }
  /* `absolute` sous la barre, et non `fixed` à 69 px du haut. Ce 69 était une
     hauteur de nav constatée un jour puis figée : la barre en fait 77 sur un
     téléphone, donc le panneau démarrait 8 px TROP HAUT et passait derrière
     elle. `top:100%` sur un parent `sticky` — qui fait bloc conteneur — colle
     le panneau au bas de la barre quelle que soit sa hauteur, et supprime le
     nombre magique. */
  .nav-liens {
    position:absolute; top:100%; left:0; right:0; z-index:59;
    flex-direction:column; align-items:stretch; gap:0;
    padding:12px var(--gout) 28px;
    background:color-mix(in srgb, var(--p-blanc) 97%, transparent);
    backdrop-filter:blur(22px); -webkit-backdrop-filter:blur(22px);
    border-bottom:1px solid var(--hair);
    transform:translateY(-8px); opacity:0; visibility:hidden;
    transition:opacity .24s var(--sortie), transform .24s var(--sortie), visibility .24s;
  }
  .nav[data-menu="ouvert"] .nav-liens { transform:none; opacity:1; visibility:visible }
  .nav-liens a:not(.btn) { padding:15px 2px; font-size:1.05rem; border-bottom:1px solid var(--hair) }
  .nav-cta { margin:18px 0 0; align-self:flex-start }
}

/* ── Photographies ─────────────────────────────────────────────────────────────
   Toutes les photos du site sont NOCTURNES et sans visage reconnaissable de
   face : silhouettes, profils à contre-jour, mains. Ce n'est pas un tic de
   style, c'est la doctrine produit — on ne vend pas des visages, et le Mystère
   cache le sien. Une banque d'images de couples souriants dirait le contraire
   de ce que fait l'application.

   Chaque photo porte un voile : sans lui, le texte posé dessus n'a aucun
   contraste garanti (une photo change de luminosité selon la zone).
   ────────────────────────────────────────────────────────────────────────── */
.photo { position:relative; overflow:hidden; background:var(--e-haute) }
.photo img { width:100%; height:100%; object-fit:cover }

.voile::after {
  content:''; position:absolute; inset:0; pointer-events:none;
}
.voile-bas::after   { background:linear-gradient(to top, var(--fond) 2%, color-mix(in srgb, var(--fond) 62%, transparent) 34%, transparent 68%) }
.voile-plein::after { background:radial-gradient(120% 90% at 50% 40%, transparent 0%, color-mix(in srgb, var(--fond) 62%, transparent) 62%, var(--fond) 100%) }
.voile-cote::after  { background:linear-gradient(to right, var(--fond) 0%, color-mix(in srgb, var(--fond) 55%, transparent) 42%, transparent 82%) }

/* ── Cartes ────────────────────────────────────────────────────────────────── */
/* Le fond de carte est un VOILE D'ENCRE, pas une couleur : `color-mix` sur
   `--texte` donne un blanc à 4,5 % sur fond sombre et un noir à 4,5 % sur fond
   clair, sans écrire deux règles. C'est ce qui permet à `.nuit` de retourner
   toutes les cartes du site d'un seul bloc. */
.carte {
  position:relative;
  background:linear-gradient(to bottom,
    color-mix(in srgb, var(--texte) 5%, transparent),
    color-mix(in srgb, var(--texte) 1.6%, transparent));
  border:1px solid var(--hair);
  border-radius:var(--r-l);
  padding:clamp(22px, 3vw, 32px);
}
.carte h3 { margin-bottom:.6rem }
.carte p { color:var(--texte-2); font-size:var(--t-petit); line-height:1.62 }

/* ── LE MOUVEMENT ─────────────────────────────────────────────────────────────
   Quatrième écart mesuré face à tinder.com, et le plus coûteux à ignorer. Le
   site n'avait qu'UN geste : `.monte`, un translateY de 26 px avec fondu,
   appliqué à l'identique à tout ce qui bouge. Ce n'est pas une animation,
   c'est un réglage par défaut — quand tout entre de la même façon, plus rien
   n'entre.

   Quatre gestes ici, un par rôle, et le rôle décide :
     · `.lignes`  un titre d'affiche se dévoile LIGNE PAR LIGNE, décalées
     · `.monte`   un bloc de texte courant arrive, discrètement
     · `.cascade` une série (cartes, listes) arrive en éventail
     · `.poser`   une matière (photo, écran) se pose en s'ouvrant

   PRINCIPE DE SÛRETÉ, conservé : par défaut tout est VISIBLE. L'état caché
   n'existe qu'à l'intérieur de `@supports (animation-timeline: view())`. Un
   navigateur sans la fonctionnalité — ou quelqu'un qui a demandé moins
   d'animations — voit la page complète, jamais une page blanche. C'est
   l'inverse du réflexe habituel (masquer en CSS, révéler en JS), qui vide la
   page dès que le script ne part pas.

   Tout tourne sur `transform`/`opacity` — donc sur le compositeur — et tout
   est piloté par `animation-timeline`. Aucun écouteur de scroll, aucun
   recalcul de mise en page, rien à saccader sur un téléphone d'entrée de
   gamme. Le site est plus animé qu'avant et ne coûte pas un octet de JS.
   ────────────────────────────────────────────────────────────────────────── */

/* Le dévoilement ligne à ligne a besoin d'une fenêtre par ligne. `overflow`
   coupe la ligne qui monte — mais il couperait aussi les jambages du `p` et
   du `g` une fois en place : d'où le rembourrage bas, repris en marge
   négative pour ne rien changer au rythme vertical. */
.lignes .l { display:block; overflow:hidden; padding-bottom:.15em; margin-bottom:-.15em }
.lignes .l > span { display:block; transform-origin:bottom }

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .lignes .l > span {
      animation:devoiler linear both;
      animation-timeline:view();
    }
    .lignes .l:nth-child(1) > span { animation-range:entry 6%  cover 21% }
    .lignes .l:nth-child(2) > span { animation-range:entry 12% cover 28% }
    .lignes .l:nth-child(3) > span { animation-range:entry 18% cover 35% }

    .monte {
      animation:monter linear both;
      animation-timeline:view();
      animation-range:entry 5% cover 27%;
    }

    .poser {
      animation:se-poser linear both;
      animation-timeline:view();
      animation-range:entry 2% cover 34%;
    }

    /* Cascade : chaque enfant direct part un cran plus tard que le précédent.
       Impossible de poser un `--i` en HTML (la CSP interdit `style=`), donc
       c'est la position qui décide — et le résultat est identique. */
    .cascade > * {
      animation:monter linear both;
      animation-timeline:view();
    }
    .cascade > :nth-child(1) { animation-range:entry 2%  cover 20% }
    .cascade > :nth-child(2) { animation-range:entry 6%  cover 24% }
    .cascade > :nth-child(3) { animation-range:entry 10% cover 28% }
    .cascade > :nth-child(4) { animation-range:entry 14% cover 32% }
    .cascade > :nth-child(5) { animation-range:entry 18% cover 36% }
    .cascade > :nth-child(6) { animation-range:entry 22% cover 40% }
  }
}

/* La ligne monte de sa propre hauteur ET pivote très légèrement — 1,2° depuis
   le coin bas gauche. C'est ce demi-degré qui sépare un fondu de logiciel
   d'une lettre posée à la main ; au-delà de 2° ça devient un effet. */
@keyframes devoiler {
  from { transform:translateY(104%) rotate(1.2deg) }
  to   { transform:none }
}
@keyframes monter {
  from { opacity:0; transform:translateY(22px) }
  to   { opacity:1; transform:none }
}
@keyframes se-poser {
  from { opacity:0; transform:translateY(46px) scale(.945) }
  to   { opacity:1; transform:none }
}

/* ── Pied de page ──────────────────────────────────────────────────────────── */
.pied { background:var(--e-fond); border-top:1px solid var(--hair); padding-block:clamp(56px,8vh,88px) 34px }
.pied-grille {
  display:grid; gap:clamp(30px,5vw,52px);
  grid-template-columns:1.5fr repeat(3, 1fr);
}
.pied-dit { margin-top:1rem; color:var(--texte-3); font-size:var(--t-petit); max-width:30ch; font-style:italic;
  font-family:var(--display) }
.pied h3 {
  font-family:var(--police); font-size:var(--t-micro); font-weight:600;
  letter-spacing:.16em; text-transform:uppercase; color:var(--texte-3);
  margin-bottom:1.05rem;
}
.pied li + li { margin-top:.3rem }
/* `inline-block` + rembourrage vertical : sans ça la zone cliquable ne fait que
   15 px de haut. WCAG 2.5.8 demande 24 px minimum, et l'exception « texte dans
   une phrase » ne couvre pas une liste de liens. Le rythme visuel est conservé
   en retirant du `margin-top` ce qu'on ajoute en `padding`. */
.pied li a {
  display:inline-block; padding-block:6px;
  font-size:var(--t-petit); color:var(--texte-2); text-decoration:none; transition:color .2s;
}
.pied li a:hover { color:var(--texte) }
.pied-bas {
  margin-top:clamp(42px,6vh,64px); padding-top:24px; border-top:1px solid var(--hair);
  display:flex; flex-wrap:wrap; align-items:center; gap:18px;
  font-size:var(--t-micro); color:var(--texte-3);
}
.age {
  display:inline-flex; align-items:center; justify-content:center;
  min-width:34px; padding:3px 9px; border:1px solid var(--hair-2); border-radius:999px;
  font-weight:700; font-size:.72rem; letter-spacing:.03em; color:var(--texte-2);
}
@media (max-width: 820px) { .pied-grille { grid-template-columns:1fr 1fr } }
@media (max-width: 520px) { .pied-grille { grid-template-columns:1fr } }

/* ── FAQ ───────────────────────────────────────────────────────────────────────
   <details>/<summary> natifs : aucun JavaScript, ouverture au clavier acquise,
   et la recherche du navigateur (Ctrl+F) trouve le contenu replié sur les
   moteurs modernes. C'est déjà ce que faisaient les pages légales du site.
   ────────────────────────────────────────────────────────────────────────── */
.faq { border-top:1px solid var(--hair); margin-top:2.6rem }
.faq details { border-bottom:1px solid var(--hair) }
.faq summary {
  display:flex; align-items:flex-start; justify-content:space-between; gap:24px;
  padding:1.35rem 0; cursor:pointer; list-style:none;
  font-family:var(--display); font-size:clamp(1.05rem,1rem + .5vw,1.32rem);
  font-weight:600; letter-spacing:-.014em; line-height:1.32;
  font-variation-settings:'opsz' 40;
}
.faq summary::-webkit-details-marker { display:none }
.faq summary::after {
  content:''; flex:none; width:13px; height:13px; margin-top:.42em;
  border-right:1.5px solid var(--texte-3); border-bottom:1.5px solid var(--texte-3);
  transform:rotate(45deg); transition:transform .28s var(--sortie), border-color .2s;
}
.faq summary:hover::after { border-color:var(--accent) }
.faq details[open] summary::after { transform:rotate(-135deg) }
.faq .reponse { padding:0 0 1.6rem; color:var(--texte-2); max-width:66ch; font-size:var(--t-petit) }
.faq .reponse p + p { margin-top:.9em }

/* ── Bande d'appel final ───────────────────────────────────────────────────── */
.finale {
  --fond:var(--e-aube);
  position:relative; text-align:center; background:var(--fond);
  padding-block:clamp(96px,15vh,180px); overflow:clip;
}
.finale .fond { position:absolute; inset:0; z-index:0 }
.finale .fond img { width:100%; height:100%; object-fit:cover; opacity:.55 }
.finale .fond::after {
  content:''; position:absolute; inset:0;
  background:radial-gradient(100% 78% at 50% 52%,
    color-mix(in srgb, var(--fond) 30%, transparent) 0%,
    color-mix(in srgb, var(--fond) 86%, transparent) 60%,
    var(--fond) 100%);
}
.finale .dedans { position:relative; z-index:1 }
.finale h2 { margin-bottom:1.3rem }
.finale .boutiques { justify-content:center; margin-top:2.4rem }
