/* ==========================================================================
   Système visuel — relevé sur pear.no le 3 septembre 2026, rôles inversés.
   Voir docs/superpowers/specs/2026-09-03-portfolio-systeme-pear-design.md

   Cette feuille n'est PAS dans un @layer : elle doit battre le @layer sw
   injecté par scrub-engine.js, quel que soit l'ordre de chargement.
   ========================================================================== */

@import url("https://fonts.googleapis.com/css2?family=Source+Serif+4:opsz,wght@8..60,300;8..60,400&family=Geist:wght@400;500&family=Geist+Mono:wght@400;500&display=swap");

:root {
  /* --- Couleurs. Aucun gris neutre : tout tire vers le brun. --- */
  --paper:    #f2f1ed;
  --ink:      #1d1c19;
  --ink-soft: #33322d;
  --press:    #0b0a09;
  --cream:    #fffaea;
  --rule:     #1d1c1924;   /* ink à 14 % */
  --sky:      #0a4c9b;     /* accent UNIQUE — le bleu du film */

  /* --- Familles. Flecha et GT Standard prennent la main dès que les
         fichiers existent dans assets/fonts/. Rien d'autre à changer. --- */
  --title: "Flecha M", "Flecha S", "Source Serif 4", Georgia, serif;
  --text:  "GT Standard L", "Geist", "Helvetica Neue", Arial, sans-serif;
  --code:  "GT Standard Mono", "Geist Mono", ui-monospace, Menlo, monospace;

  /* --- Échelle. Le rapport de 1 à 6 entre statement et corps est
         la caractéristique centrale du système. Ne pas la resserrer. --- */
  --fs-statement: 5.3vw;                         /* 78px @1470 */
  --fs-h1:        clamp(2.6rem, 3.6vw, 3.35rem); /* ~53px */
  --fs-title:     1.64rem;                       /* 26.2px */
  --fs-title-s:   1.45rem;                       /* 23.2px */
  --fs-lede:      1.25rem;                       /* 20px, en --title */
  --fs-body-lg:   0.99rem;                       /* 15.8px */
  --fs-body:      0.8125rem;                     /* 13px */
  --fs-label:     0.6875rem;                     /* 11px */
  --fs-micro:     0.5175rem;                     /* 8.28px */

  /* Les titres sont serrés : pear.no met 24,45 de hauteur sur 26,22 de corps. */
  --lh-statement: 1.04;
  --lh-title:     0.94;
  --lh-body:      1.3;
  --lh-label:     1.5;

  --tr-statement: -0.024em;
  --tr-title:     -0.02em;
  --tr-body:      -0.005em;
  --tr-label:      0.18em;
  --tr-micro:      0.22em;

  --ease: cubic-bezier(.22, 1, .36, 1);

  --gutter: clamp(24px, 5vw, 64px);
}

/* --- Registres typographiques -------------------------------------------
   Trois registres, et rien entre les deux. C'est l'écart qui fait le style,
   pas le dessin des lettres.
   ------------------------------------------------------------------------ */

.statement {
  font-family: var(--title);
  font-weight: 300;
  font-size: var(--fs-statement);
  line-height: var(--lh-statement);
  letter-spacing: var(--tr-statement);
  margin: 0;
}

.title {
  font-family: var(--title);
  font-weight: 400;
  font-size: var(--fs-title);
  line-height: var(--lh-title);
  letter-spacing: var(--tr-title);
  margin: 0;
}
.title--s { font-size: var(--fs-title-s); }

.lede {
  font-family: var(--title);
  font-weight: 400;
  font-size: var(--fs-lede);
  line-height: 1.22;
  letter-spacing: var(--tr-title);
  margin: 0;
}

.body {
  font-family: var(--text);
  font-weight: 400;
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  letter-spacing: var(--tr-body);
  margin: 0;
  max-width: 46ch;
}
.body--lg { font-size: var(--fs-body-lg); line-height: 1.26; }

.label {
  font-family: var(--code);
  font-weight: 500;
  font-size: var(--fs-label);
  line-height: var(--lh-label);
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
  margin: 0;
}
.label--micro {
  font-weight: 400;
  font-size: var(--fs-micro);
  letter-spacing: var(--tr-micro);
}

/* --- Révélation ---------------------------------------------------------
   Signature d'animation de pear.no : sa page d'accueil découpe ses textes
   en 348 .ch et 85 .wd, révélés au scroll.
   ------------------------------------------------------------------------ */

[data-reveal] .ch,
[data-reveal] .wd,
[data-reveal] .ln {
  display: inline-block;
  opacity: 0;
  transform: translateY(0.42em);
  transition: opacity .62s var(--ease), transform .62s var(--ease);
}
/* En mode lettre, le mot n'est qu'un conteneur de non-césure : il ne s'anime
   pas lui-même, sinon le mouvement se joue deux fois et double. */
[data-reveal="ch"] .wd { opacity: 1; transform: none; transition: none; }

[data-reveal].is-in .ch,
[data-reveal].is-in .wd,
[data-reveal].is-in .ln {
  opacity: 1;
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  [data-reveal] .ch, [data-reveal] .wd, [data-reveal] .ln {
    opacity: 1; transform: none; transition: none;
  }
}

/* --- Pont vers le moteur ------------------------------------------------
   Le moteur suppose « page claire à encre sombre ». En passant paper en base,
   cette supposition redevient vraie : aucune rustine n'est nécessaire, à la
   différence d'index-test.html qui devait repeindre trois éléments à la main.
   ------------------------------------------------------------------------ */

:root, .sw-root {
  --sw-bg:           var(--paper);
  --sw-ink:          var(--ink);
  --sw-ink-soft:     var(--ink-soft);
  --sw-accent:       var(--sky);
  --sw-font-display: var(--title);
  --sw-font-body:    var(--text);
}

/* --- Chrome du moteur ---------------------------------------------------
   Le moteur dessine des pastilles arrondies, des ombres et du gras 700.
   Le système n'a que du texte et des filets à 14 %. On défait, on ne
   complète pas. Références de ligne : scrub-engine.js:361-442.
   ------------------------------------------------------------------------ */

/* La marque porte le logo — voir la section « La marque » en fin de fichier. */
.sw-root .sw-brand__name {
  font-family: var(--code);
  font-weight: 500;
  font-size: var(--fs-label);
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
}

/* Le CTA : pastille pleine → étiquette mono, comme le « Apply → » de pear.no */
.sw-root .sw-topcta {
  background: none;
  color: var(--ink);
  padding: 0 0 3px;
  border-radius: 0;
  font-family: var(--code);
  font-weight: 400;
  font-size: var(--fs-micro);
  letter-spacing: var(--tr-micro);
  text-transform: uppercase;
  border-bottom: 1px solid var(--rule);
}

/* La couche de texte : c'est ici que se joue l'écart de 1 à 6. */
.sw-root .sw-copy__eyebrow {
  font-family: var(--code);
  font-weight: 500;
  font-size: var(--fs-label);
  line-height: var(--lh-label);
  letter-spacing: var(--tr-label);
  color: var(--ink-soft);          /* pas l'accent : pear.no reste sobre */
  margin-top: 0;
}
.sw-root .sw-copy__num {
  font-family: var(--code);
  font-size: var(--fs-micro);
  letter-spacing: var(--tr-micro);
  color: var(--ink-soft);
}
.sw-root .sw-copy__title {
  font-family: var(--title);
  font-weight: 300;                /* le moteur met 700 */
  font-size: var(--fs-h1);
  line-height: 1.0;
  letter-spacing: var(--tr-statement);
  text-shadow: none;               /* le système n'a aucune ombre */
  margin: 14px 0 0;
}
.sw-root .sw-copy__body {
  font-family: var(--text);
  font-size: var(--fs-body);       /* le moteur monte à 16–18px */
  line-height: var(--lh-body);
  letter-spacing: var(--tr-body);
  color: var(--ink);          /* posé sur le film : l'encre pleine, pas la douce */
  text-shadow: none;
  max-width: 42ch;
  margin-top: 20px;
}

/* Les tags : pastilles bordées → étiquettes mono séparées par des filets. */
.sw-root .sw-copy__tags { gap: 0; margin-top: 26px; }
.sw-root .sw-copy__tags li {
  background: none;
  border: 0;
  border-left: 1px solid var(--rule);
  border-radius: 0;
  padding: 2px 12px;
  font-family: var(--code);
  font-weight: 400;
  font-size: var(--fs-micro);
  letter-spacing: var(--tr-micro);
  text-transform: uppercase;
  color: var(--ink);          /* idem : sur le film, l'encre pleine */
}
.sw-root .sw-copy__tags li:first-child { border-left: 0; padding-left: 0; }

/* Les boutons : pastilles → étiquettes soulignées. */
.sw-root .sw-btn {
  border-radius: 0;
  padding: 0 0 4px;
  font-family: var(--code);
  font-weight: 500;
  font-size: var(--fs-label);
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
  transition: opacity .25s var(--ease);
}
.sw-root .sw-btn:hover { transform: none; opacity: .6; }
.sw-root .sw-btn--primary { background: none; color: var(--ink); border-bottom: 1px solid var(--ink); }
.sw-root .sw-btn--ghost   { border: 0; color: var(--ink-soft); border-bottom: 1px solid var(--rule); }

/* L'indice de scroll : la molette dessinée → une étiquette mono. */
.sw-root .sw-hint {
  font-family: var(--code);
  font-size: var(--fs-micro);
  letter-spacing: var(--tr-micro);
  color: var(--ink-soft);
}
.sw-root .sw-hint i { display: none; }

/* La barre de progression : gardée, mais réduite à un filet. */
.sw-root .sw-scrollbar { height: 1px; background: var(--rule); }
.sw-root .sw-scrollbar span { background: var(--ink); }

/* Le rail latéral : `nav:false` ne masque que la barre du haut, le rail est
   un élément distinct. Il est reporté — il servira à pointer vers les autres
   pages du portfolio, pas à naviguer dans le scroll. */
.sw-root .sw-route { display: none; }

/* --- Lisibilité du chrome par-dessus le film ----------------------------
   Le chrome est fixe, le film change sous lui : rien ne garantit le
   contraste. Le système interdit les ombres portées, on passe donc par des
   voiles de paper — la même matière que le fond de page.
   ------------------------------------------------------------------------ */

.sw-root .sw-topbar::before {
  content: "";
  position: absolute;
  inset: -10px 0 auto 0;
  height: 140px;
  z-index: -1;
  pointer-events: none;
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--paper) 82%, transparent) 0%,
    color-mix(in srgb, var(--paper) 46%, transparent) 46%,
    transparent 100%);
}

.sw-root .sw-hint::before {
  content: "";
  position: absolute;
  left: 50%;
  bottom: -30px;
  transform: translateX(-50%);
  width: min(46vw, 520px);
  height: 120px;
  z-index: -1;
  pointer-events: none;
  background: radial-gradient(58% 100% at 50% 100%,
    color-mix(in srgb, var(--paper) 76%, transparent), transparent 72%);
}

/* Le voile du texte : le moteur part d'un blanc plein sur 58vw, ce qui efface
   le tiers gauche de l'image. On garde de quoi lire un corps de 13px, pas plus. */
.sw-root .sw-copylayer::before {
  width: min(52vw, 700px);
  background: linear-gradient(90deg,
    color-mix(in srgb, var(--paper) 94%, transparent) 0%,
    color-mix(in srgb, var(--paper) 84%, transparent) 34%,
    color-mix(in srgb, var(--paper) 50%, transparent) 62%,
    transparent 100%);
}

/* --- Statements ---------------------------------------------------------
   Posés au-dessus du film (z 25), sous le chrome (z 50). Le film fournit
   lui-même l'aplat : on n'ajoute aucun fond.
   ------------------------------------------------------------------------ */

.fin {
  position: fixed;
  inset: 0;
  z-index: 25;
  display: grid;
  place-items: center;
  padding: 0 var(--gutter);
  text-align: center;
  pointer-events: none;
  opacity: 0;
  transition: opacity .4s var(--ease);
}
.fin.is-on { opacity: var(--fin-p, 0); }
.fin[data-tone="ink"]   { color: var(--ink); }
.fin[data-tone="cream"] { color: var(--cream); }
.fin .statement { max-width: 18ch; }

/* Un statement est seul à l'écran. Sans ceci, le texte de la section reste
   affiché à gauche et se dispute la place avec la phrase — en encre sombre
   sur le bleu nuit de l'insula, donc illisible par-dessus le marché.
   On fade la couche entière, pas .sw-copy : le moteur pilote l'opacité de
   .sw-copy en style inline, qui battrait une règle de feuille. */
.sw-root .sw-copylayer { transition: opacity .4s var(--ease); }
body:has(.fin.is-on) .sw-copylayer { opacity: 0; }

/* Sur les deux statements crème, le film passe au bleu plein : le voile de
   paper du haut devient une bande pâle bien visible, et l'encre sombre du
   chrome n'a plus lieu d'être. Le chrome bascule avec la phrase. */
.sw-root .sw-topbar::before,
.sw-root .sw-hint::before { transition: opacity .4s var(--ease); }

body:has(.fin[data-tone="cream"].is-on) .sw-topbar::before,
body:has(.fin[data-tone="cream"].is-on) .sw-hint::before { opacity: 0; }

body:has(.fin[data-tone="cream"].is-on) .sw-brand,
body:has(.fin[data-tone="cream"].is-on) .sw-brand__name,
body:has(.fin[data-tone="cream"].is-on) .sw-topcta,
body:has(.fin[data-tone="cream"].is-on) .sw-hint {
  color: var(--cream);
  transition: color .4s var(--ease);
}
body:has(.fin[data-tone="cream"].is-on) .sw-topcta {
  border-bottom-color: color-mix(in srgb, var(--cream) 34%, transparent);
}

/* --- Ce qui suit le film ------------------------------------------------
   Piège structurel : .sw-stage est en position:fixed. Sans ce conteneur
   opaque en z-index supérieur, le film resterait visible derrière la FAQ
   jusqu'en bas de page.
   ------------------------------------------------------------------------ */

.after {
  position: relative;
  z-index: 30;
  background: var(--paper);
  color: var(--ink);
  padding: clamp(90px, 12vh, 160px) var(--gutter);
}
.after__inner { max-width: 1180px; margin: 0 auto; }

/* Un intertitre de section : étiquette mono à gauche, filet plein. */
.sec-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 24px;
  padding-bottom: 18px;
  border-bottom: 1px solid var(--ink);
  margin-bottom: 8px;
}

/* --- pf : les blocs preuve --------------------------------------------- */
.pf {
  display: grid;
  grid-template-columns: 1fr 2fr;
  gap: var(--gutter);
  padding: 56px 0;
  border-top: 1px solid var(--rule);
}
.pf:first-of-type { border-top: 0; }
.pf__chips { display: flex; gap: 0; margin-top: 22px; flex-wrap: wrap; }
.pf__chips span {
  font-family: var(--code);
  font-size: var(--fs-micro);
  letter-spacing: var(--tr-micro);
  text-transform: uppercase;
  color: var(--ink-soft);
  border-left: 1px solid var(--rule);
  padding: 2px 12px;
}
.pf__chips span:first-child { border-left: 0; padding-left: 0; }
.pf__body { margin-top: 20px; }
/* Sous le nom du produit : pour qui, et quel rôle. C'est la ligne qu'un
   prospect lit en premier après le nom — elle dit si c'est un client, un
   associé ou un projet seul. */
.pf__meta { margin-top: 14px; color: var(--ink-soft); }

/* --- fq : la FAQ -------------------------------------------------------- */
.fq { border-top: 1px solid var(--rule); padding: 26px 0; }
.fq summary {
  cursor: pointer;
  list-style: none;
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 24px;
}
.fq summary::-webkit-details-marker { display: none; }
.fq summary::after {
  content: "+";
  font-family: var(--code);
  font-size: var(--fs-label);
  color: var(--ink-soft);
  transition: transform .3s var(--ease);
}
.fq[open] summary::after { content: "–"; }
.fq[open] .body { margin-top: 16px; }

/* --- cf : le contact ---------------------------------------------------- */
.cf { border-top: 1px solid var(--ink); padding-top: 46px; margin-top: 26px; }
.cf__link {
  display: inline-block;
  margin-top: 24px;
  font-family: var(--code);
  font-weight: 500;
  font-size: var(--fs-label);
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
  color: var(--ink);
  text-decoration: none;
  border-bottom: 1px solid var(--ink);
  padding-bottom: 4px;
  transition: opacity .25s var(--ease);
}
.cf__link:hover { opacity: .6; }

/* --- ft : le pied ------------------------------------------------------- */
.ft {
  border-top: 1px solid var(--rule);
  margin-top: 90px;
  padding-top: 26px;
  display: flex;
  justify-content: space-between;
  gap: 24px;
  color: var(--ink-soft);
}
/* La passerelle vers la version détaillée : discrète, mais c'est un lien. */
.ft a {
  color: inherit;
  text-decoration: none;
  border-bottom: 1px solid var(--rule);
  padding-bottom: 2px;
  transition: opacity .25s var(--ease);
}
.ft a:hover { opacity: .6; }

/* Espacements verticaux entre les grands ensembles. */
.after__group + .after__group { margin-top: clamp(80px, 10vh, 140px); }
/* La barre du haut est fixe : sans cette marge, arriver par une ancre
   (« Voir les projets », en fin de film) glisse l'intertitre dessous. */
.after__group { scroll-margin-top: 110px; }

/* Passé la fin du film, le voile du chrome n'a plus rien à couvrir : il
   laverait le contenu des blocs qui défilent sous la barre. */
body.is-past-world .sw-topbar::before,
body.is-past-world .sw-hint::before { opacity: 0; }

/* Le décalage par fragment : c'est ce qui fait une vague plutôt qu'un bloc.
   L'index court sur toute la phrase, il ne repart pas à zéro à chaque mot. */
[data-reveal] .ch,
[data-reveal] .wd,
[data-reveal] .ln { transition-delay: calc(var(--i, 0) * 26ms); }

/* En sortie, tout se replie d'un coup : une vague inversée serait bavarde. */
[data-reveal]:not(.is-in) .ch,
[data-reveal]:not(.is-in) .wd,
[data-reveal]:not(.is-in) .ln { transition-delay: 0ms; }

@media (prefers-reduced-motion: reduce) {
  [data-reveal] .ch, [data-reveal] .wd, [data-reveal] .ln { transition-delay: 0ms; }
}

/* Lisible par les lecteurs d'écran, absent de l'écran. Pas display:none,
   qui le retirerait aussi de l'arbre d'accessibilité. */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* `atmosphere: false` ne désactive que le dégradé et le halo du moteur
   (scrub-engine.js:111-114) : le conteneur de particules est créé sans
   condition ligne 115, et seedParticles appelé ligne 315. Vingt éléments
   animés en permanence derrière le film, pour rien. */
.sw-root .sw-particles { display: none; }

/* --- La marque -----------------------------------------------------------
   Le moteur dessine un losange en dégradé avec une ombre portée. On le
   remplace par le logo, en masque plutôt qu'en <img> : le fond du masque est
   `currentColor`, donc le logo hérite de la couleur du texte autour de lui et
   bascule tout seul avec le chrome, sans dupliquer de fichier.

   Le tracé fait 1,79 de rapport — c'est une marque large, pas un carré.
   ------------------------------------------------------------------------ */
.sw-root .sw-brand__mark {
  display: block;
  width: 39px;
  height: 22px;
  flex: none;
  border-radius: 0;
  box-shadow: none;
  background: currentColor;
  -webkit-mask: url("logo.svg") left center / contain no-repeat;
          mask: url("logo.svg") left center / contain no-repeat;
}
.sw-root .sw-brand { gap: 13px; }

/* --- Placement du texte, monde par monde --------------------------------
   Le moteur plaque tout à gauche. Chaque image ayant sa zone vide à un
   endroit différent, on repositionne bloc par bloc. Les valeurs viennent
   des posters livrés, relevées à l'œil — pas de la bible, dont l'exèdre
   ne correspond plus à l'image finale.
   ------------------------------------------------------------------------ */

.sw-root .sw-copy {
  left: var(--copy-x, var(--gutter));
  right: auto;
  top: var(--copy-y, 50%);
  transform: translateY(-50%);
  width: var(--copy-w, min(34vw, 420px));
  text-align: var(--copy-align, left);
}
/* `--copy-y` est toujours le CENTRE du bloc : c'est ce que renvoie la mesure. */
.sw-root .sw-copy[data-centre="1"] { transform: translate(-50%, -50%); }

/* Le voile suit le texte au lieu d'être un dégradé fixe à gauche. Il doit
   s'éteindre bien avant le bord de sa boîte, sinon il se lit comme un panneau
   posé sur l'image — ce que le premier essai faisait. D'où l'ellipse large et
   la coupure à 62 %. */
.sw-root .sw-copy::before {
  content: "";
  position: absolute;
  inset: -90px -120px;
  z-index: -1;
  pointer-events: none;
  background: radial-gradient(closest-side ellipse at var(--voile-x, 34%) 50%,
    color-mix(in srgb, var(--voile) var(--voile-f, 62%), transparent) 0%,
    color-mix(in srgb, var(--voile) calc(var(--voile-f, 62%) * 0.42), transparent) 38%,
    transparent 62%);
}
/* Sur le marbre, très clair et très chargé, il faut de la matière.
   Sur le ciel, uniforme et saturé, la crème se lit seule : presque rien. */
.sw-root .sw-copy[data-tone="ink"]   { --voile: var(--paper); }
.sw-root .sw-copy[data-tone="cream"] { --voile: var(--press); }
/* Le foyer du dégradé suit l'alignement du texte. */
.sw-root .sw-copy[data-centre="1"] { --voile-x: 50%; }

/* Encre claire sur les scènes où le texte tombe dans le ciel. */
.sw-root .sw-copy[data-tone="cream"] .sw-copy__title { color: var(--cream); }
.sw-root .sw-copy[data-tone="cream"] .sw-copy__body,
.sw-root .sw-copy[data-tone="cream"] .sw-copy__eyebrow,
.sw-root .sw-copy[data-tone="cream"] .sw-copy__num,
.sw-root .sw-copy[data-tone="cream"] .sw-copy__tags li {
  color: color-mix(in srgb, var(--cream) 82%, transparent);
}
.sw-root .sw-copy[data-tone="cream"] .sw-copy__tags li {
  border-left-color: color-mix(in srgb, var(--cream) 26%, transparent);
}
/* Le CTA final tombe dans le ciel de nuit : sans ceci il restait à l'encre. */
.sw-root .sw-copy[data-tone="cream"] .sw-btn--primary {
  color: var(--cream);
  border-bottom-color: var(--cream);
}
.sw-root .sw-copy[data-tone="cream"] .sw-btn--ghost {
  color: color-mix(in srgb, var(--cream) 82%, transparent);
  border-bottom-color: color-mix(in srgb, var(--cream) 26%, transparent);
}

/* Le voile global du moteur n'a plus lieu d'être : chaque bloc porte le sien. */
.sw-root .sw-copylayer::before { display: none; }

/* --- Chorégraphie : le texte se construit, il n'apparaît pas d'un bloc ---
   Bible §5.8. Le moteur pilote l'opacité de .sw-copy en style inline ; on ne
   la touche pas. On anime les enfants, qui sont à lui seuls.
   ------------------------------------------------------------------------ */

.sw-root .sw-copy__eyebrow,
.sw-root .sw-copy__num,
.sw-root .sw-copy__body,
.sw-root .sw-copy__tags,
.sw-root .sw-copy__cta {
  opacity: 0;
  transform: translateY(0.7em);
  transition: opacity .7s var(--ease), transform .7s var(--ease);
}
.sw-root .sw-copy.is-on .sw-copy__num     { opacity: 1; transform: none; transition-delay: 0ms; }
.sw-root .sw-copy.is-on .sw-copy__eyebrow { opacity: 1; transform: none; transition-delay: 90ms; }
.sw-root .sw-copy.is-on .sw-copy__body    { opacity: 1; transform: none; transition-delay: 340ms; }
.sw-root .sw-copy.is-on .sw-copy__tags    { opacity: 1; transform: none; transition-delay: 440ms; }
.sw-root .sw-copy.is-on .sw-copy__cta     { opacity: 1; transform: none; transition-delay: 520ms; }

/* Le titre part après l'eyebrow, mot à mot (voir copy.js). */
.sw-root .sw-copy__title [data-reveal] { display: inline; }
.sw-root .sw-copy__title .wd { transition-delay: calc(180ms + var(--i, 0) * 34ms); }
.sw-root .sw-copy__title:not(.is-in) .wd { transition-delay: 0ms; }

@media (prefers-reduced-motion: reduce) {
  .sw-root .sw-copy__eyebrow, .sw-root .sw-copy__num, .sw-root .sw-copy__body,
  .sw-root .sw-copy__tags, .sw-root .sw-copy__cta {
    opacity: 1; transform: none; transition: none;
  }
}

/* --- L'écran d'ouverture ------------------------------------------------
   Le premier écran affichait « Tout ce qui se répète » : personne ne pouvait
   deviner chez qui il était arrivé. Le nom passe devant, et s'efface dès
   qu'on entre dans la première plongée.
   ------------------------------------------------------------------------ */

.intro {
  position: fixed;
  inset: 0;
  z-index: 24;
  display: grid;
  align-content: center;
  justify-items: start;
  padding: 0 var(--gutter);
  pointer-events: none;
  opacity: var(--intro-p, 1);
  color: var(--ink);
}
.intro.is-out { visibility: hidden; }

.intro__eyebrow { color: var(--ink-soft); margin: 0 0 22px; }

.intro__nom {
  font-family: var(--title);
  font-weight: 300;
  font-size: var(--fs-statement);
  line-height: var(--lh-statement);
  letter-spacing: var(--tr-statement);
  margin: 0;
}

.intro__ligne {
  font-family: var(--text);
  font-size: var(--fs-body-lg);
  line-height: 1.35;
  letter-spacing: var(--tr-body);
  color: var(--ink-soft);
  margin: 26px 0 0;
  max-width: 38ch;
}

/* Le voile qui garantit la lecture par-dessus la première image. */
.intro::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background: linear-gradient(100deg,
    color-mix(in srgb, var(--paper) 92%, transparent) 0%,
    color-mix(in srgb, var(--paper) 72%, transparent) 38%,
    color-mix(in srgb, var(--paper) 22%, transparent) 66%,
    transparent 88%);
}

/* Tant que l'ouverture est là, le texte du premier monde n'a rien à faire
   à l'écran : deux blocs se disputeraient la place. */
body:has(.intro:not(.is-out)) .sw-copy:first-child { opacity: 0 !important; }

/* --- Qui pilote l'opacité du texte --------------------------------------
   Le moteur écrit `opacity` en style inline sur .sw-copy, à chaque frame,
   suivant sa propre courbe. Elle ne coïncide pas avec la chorégraphie de la
   bible que copy.js rejoue : au coeur de la scène 1, le bloc était dessiné à
   18 % — le texte semblait mal placé alors qu'il était simplement effacé.

   On reprend donc la main. `!important` est ici le seul moyen de battre un
   style inline réécrit en continu ; c'est délibéré, pas une rustine.
   ------------------------------------------------------------------------ */
.sw-root .sw-copy {
  opacity: 0 !important;
  transition: opacity .55s var(--ease);
}
.sw-root .sw-copy.is-on { opacity: 1 !important; }
/* Tant que l'ouverture couvre l'écran, le premier bloc n'a rien à y faire. */
body:has(.intro:not(.is-out)) .sw-copy:first-child { opacity: 0 !important; }

/* Garde-fou : aucun bloc ne doit sortir du cadre, quelle que soit la zone
   mesurée ou la hauteur du texte une fois traduit. */
.sw-root .sw-copy { max-height: 78vh; }
