/* Le hub /portfolio/ — « Couverture ». Bucket ③, identité d'île.
 *
 * Vit à part de css/style.css pour deux raisons, et pas par goût du découpage :
 *  1. la page ne charge que ce qu'elle utilise — la feuille, son crossfade et sa peinture
 *     n'existent nulle part ailleurs ;
 *  2. home-css.test.cjs lit style.css comme du TEXTE et interdit toute règle qui masque du
 *     contenu publié. L'état initial `opacity: 0` de la couche « b » y serait un faux rouge
 *     permanent. Sa garde équivalente vit dans portfolio-hub.test.cjs, sur CE fichier.
 *
 * ⚠️ Pas de `position: sticky` sur la feuille. Ce n'est pas un oubli : sur cette île, le sticky
 *    peint par-dessus pendant le scroll sous WebKit (chantier hero Safari, 2026-08-29).
 */

.pf-hub {
  /* Le rouge de la peinture, prélevé au cœur de la touche. Déclaré sur la PAGE et jamais
     dans :root — c'est sa rareté qui le rend premium, et une couleur d'accent générale le
     banaliserait. Il ne colore plus un aplat mais du TEXTE (le chiffre au survol) : son
     contraste sur le fond crème est donc mesuré, pas supposé — voir portfolio-hub.test.cjs. */
  --pf-encre: #9E4B38;

  /* ── Rythme des gestes : UN bloc (ordre-de-conception.md, workflow ⑥). Un réglage demandé à l'essai ne touche
     qu'ici. Fondu au survol (#177, RETOUR 1 : « Net ») : l'entrante monte en `--pf-fondu-entree`, sur la courbe
     `--pf-fondu-courbe`, par-dessus la sortante qui reste opaque — ni zoom, ni effacement préalable. */
  --pf-fondu-entree: 240ms;
  --pf-fondu-courbe: cubic-bezier(0.2, 0, 0, 1);
}

/* L'ombre de la feuille déborde à droite de la fenêtre entre 900 et 1290 px (+51 px, défilement horizontal). On
   rogne au bord de la FENÊTRE, jamais l'ombre : `main` est le seul ancêtre de la planche dont la boîte vaut la largeur
   de la fenêtre sur toute la plage (relevé #177 : `.pf-section` plafonne à 1200 px dès 1280). `clip` et non
   `hidden` : `hidden` en ferait un conteneur de défilement, et la touche serait rognée en hauteur. */
.pf-hub main { overflow-x: clip; }

/* ── En-tête : le titre seul ───────────────────────────────────────────────────
   Pas de sur-titre au-dessus du H1. Il y en avait un ; posé sous un mot en très grand
   corps, il passait derrière les jambages du « g » et du « p ». La page ouvre donc sur
   le titre et son intro, rien d'autre.

   La réserve au-dessus du titre est RÉDUITE par rapport aux autres sections (`.section`
   monte à 7rem) : ici le H1 fait à lui seul jusqu'à 10rem de corps, et une réserve
   calibrée pour un titre de section ordinaire creuse un vide au-dessus de celui-ci. La
   page doit ouvrir sur le mot, pas sur du blanc. */
.pf-section { padding-top: clamp(1.75rem, 4vw, 3.25rem); }

.pf-head {
  position: relative;
  padding-bottom: clamp(1.2rem, 2.5vw, 1.8rem);
  margin-bottom: clamp(2rem, 5vw, 3.25rem);
}
/* Le filet sous le titre était une bordure. Il devient un pseudo-élément pour pouvoir se TRACER
   à l'arrivée (voir « L'entrée du hub ») : une bordure ne s'anime qu'en largeur, donc en reflow. */
.pf-head::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 1px;
  background: rgba(28, 25, 21, 0.13);
  transform-origin: left;
}

/* Le H1 réutilise le markup « word-mask » du hero (renderH1 est la source unique du rendu de
   H1), mais PAS son animation : celle-ci est portée par `.hero-title .word-inner`, sélecteur
   que cette page n'active pas. Le mot est donc posé, jamais joué — et le contract interdit
   d'animer l'élément LCP au chargement. L'overflow du masque est rendu visible : sans lui,
   il rognerait les jambages du mot en très grand corps. */
/* Colonne, comme `.pu-h1` sur les univers : chaque segment garde sa ligne — la coupe du titre est
   décidée par `seo.pages.portfolio.h1.words`, jamais par la largeur disponible. */
.pf-title {
  margin: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}
.pf-title .word-mask { overflow: visible; }
/* ── Sous-titre de zone : DERNIER segment du <h1>, donc sous le titre sans rien réordonner ──────
   Même règle que `.pu-h1 .h1-location` (css/portfolio-univers.css) : ordre du DOM = ordre de
   lecture (contrat SEO v1.7), aucun `order`. Le reste est un MIROIR des univers, vérifié
   déclaration par déclaration par portfolio-hub.test — ne pas le faire diverger à la main.

   Seule la RÉSERVE au-dessus diffère, et elle n'est pas reprise telle quelle des univers. Avec
   `line-height: 0.86`, la descendante du « g » de « Photographies » SORT de sa boîte de ligne :
   ~0,17 em (mesuré sur la page servie, TextMetrics). Ce qu'il faut aligner, c'est l'AIR SOUS
   L'ENCRE (11-12 px sur les univers, constant) : d'où `descendante + constante` — une pente en vw
   qui reprend 0,17 × la pente du titre, plus un terme fixe (0,7rem) qui est l'air lui-même.
   La pente suit le titre quand il change de corps (ramenée de 2,3vw à 1,8vw avec la réduction).
   Filet + gouttière = 2,5rem = retrait de la ligne en italique : voir portfolio-univers.css. */
.pf-title .h1-location {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  color: var(--color-muted-text);
  margin: clamp(1.15rem, calc(1.8vw + 0.7rem), 2.05rem) 0 0;
  font-family: var(--font-body);
  font-size: 0.72rem;
  font-weight: 500;
  font-variant: small-caps;
  text-transform: uppercase;
  letter-spacing: 0.26em;
  line-height: 1.4;
}
.pf-title .h1-location::before {
  content: '';
  flex: 0 0 1.6rem;
  height: 1px;
  background: currentColor;
  opacity: 0.45;
}
/* La 2e ligne en accent, comme « grossesse » sur les univers : une seule signature pour les six
   pages du portfolio. --color-accent tient AA sur --color-bg (5,43:1, calculé). */
.pf-title .italic-mask .word-inner { font-style: italic; color: var(--color-accent); }
/* Corps réduit d'environ 18 % le 2026-09-17 (10rem → 8rem au plafond) : à 10rem, les deux lignes
   du titre repoussaient la photo d'ouverture et le coup de pinceau sous la ligne de flottaison —
   il fallait défiler pour voir leur animation, donc on la manquait. Le titre reste l'élément LCP,
   simplement moins encombrant. La réserve du sous-titre suit (voir plus haut) : elle est calée sur
   la descendante, qui rétrécit avec le corps. */
/* Plancher PROPORTIONNEL sous 375 px (#95, C123) : 11.52vw = 2.7rem × 16 / 375 × 100. À 2.7rem,
   « Photographies » (4,69 em, italique, mesuré Chromium) = 203 px + 40 de retrait : la page
   débordait à 260 px (267). Même taille à 375 px et au-dessus. Commentaire HORS de la règle :
   portfolio-hub.test.cjs lit ses déclarations une à une. */
.pf-title .word-inner {
  font-family: var(--font-title);
  font-size: clamp(min(2.7rem, 11.52vw), 10.5vw, 8rem);
  font-weight: 300;
  line-height: 0.86;
  letter-spacing: -0.015em;
}

.pf-intro {
  /* 2,6rem et non 1,6 : sous un mot de 10rem de corps, une ligne de texte posée trop près
     se lit comme sa légende. L'écart doit dire « deux choses », pas « une et sa suite ». */
  margin: 2.6rem 0 0;
  max-width: 44ch;
  color: var(--color-muted-text);
  line-height: 1.75;
}

.pf-body { display: grid; gap: clamp(2.5rem, 6vw, 5.5rem); align-items: start; }

/* La mention d'index dit quelque chose de VRAI : le nombre d'entrées et leur borne haute,
   tous deux calculés au build depuis categories[]. Une sixième catégorie et elle se met à
   jour seule. Un ornement qui ment au premier changement de contenu n'est pas un ornement,
   c'est une dette. */
.pf-index {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  font-size: 0.62rem;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--color-muted-text);
  padding-bottom: 0.7rem;
}

/* ── Le sommaire : les chiffres portent la page ────────────────────────────── */
.pf-list { list-style: none; margin: 0; padding: 0; }

.pf-item { border-top: 1px solid rgba(28, 25, 21, 0.13); }
.pf-item:last-child { border-bottom: 1px solid rgba(28, 25, 21, 0.13); }

.pf-link {
  display: flex;
  align-items: baseline;
  gap: clamp(0.8rem, 2vw, 1.7rem);
  padding: clamp(0.5rem, 1.4vw, 0.95rem) 0;
  color: inherit;
  text-decoration: none;
}

/* Le chiffre est l'élément graphique de la page, pas une étiquette d'ordre. D'où le corps,
   et d'où `line-height: 0.74` : les nombres se serrent jusqu'à se frôler d'une ligne à
   l'autre, ce qui est la tension d'un sommaire de magazine. */
.pf-num {
  flex: none;
  font-family: var(--font-title);
  font-weight: 600;
  font-size: clamp(4rem, 11vw, 8.4rem);
  line-height: 0.74;
  letter-spacing: -0.05em;
  transition: color 300ms ease;
}

.pf-name {
  font-family: var(--font-title);
  font-weight: 300;
  /* Plancher PROPORTIONNEL sous 375 px (#95, C123) : 6.827vw = 1.6rem × 16 / 375 × 100. Au plancher
     fixe, la ligne « 02 Naissance + vignette » mesurait 237,5 px incompressibles (Chromium) : elle
     mangeait la gouttière droite dès 280 px et élargissait la page à 260. Même taille dès 375 px. */
  font-size: clamp(min(1.6rem, 6.827vw), 5vw, 3.4rem);
  line-height: 1;
  transition: color 300ms ease, transform 400ms cubic-bezier(0.2, 0.7, 0.3, 1);
}

/* La vignette est la ligne MOBILE, et la source src/alt que lit js/portfolio.js.
 *
 * Sa taille était FIXE (3,4rem), donc juste sur un téléphone et minuscule partout ailleurs :
 * entre 600 et 900 px — une tablette en portrait, une fenêtre de bureau réduite — elle
 * tenait 54 px à droite d'une ligne dont le chiffre en faisait 90. La photographie y avait
 * l'air d'une miniature oubliée, alors que c'est le seul aperçu que cette forme propose.
 *
 * Elle suit donc la largeur, comme le chiffre qui lui fait face (clamp(4rem, 11vw, 8,4rem)),
 * et reste calée sous lui : à 800 px le chiffre fait 88 px de haut, la vignette 80 de large.
 * Le ratio est porté par `aspect-ratio` plutôt que par une hauteur en dur — une hauteur fixe
 * avec une largeur fluide déforme le cadrage dès que les deux cessent d'être accordées. */
.pf-thumb {
  margin-left: auto;
  flex: none;
  width: clamp(4rem, 10vw, 6.5rem);
  height: auto;
  aspect-ratio: 3 / 4;
  object-fit: cover;
  align-self: center;
}

/* La feuille naît absente : le mobile n'a pas de survol, et on ne transpose pas le desktop
   au chausse-pied. Liste + vignettes portent seules le contenu, tap → subpage. */
.pf-plate { display: none; }

/* L'opacité des couches vit ICI, hors du point de rupture, et c'est délibéré : elle appartient
   à la COUCHE, pas au seuil. Enfermée dans le @media desktop, elle laissait les deux couches à
   `opacity: 1` sous 900 px — inoffensif tant que la feuille est masquée, mais c'est un piège
   armé pour qui l'afficherait un jour. Mesuré à 400 px sur la page servie le 2026-09-10. */
.pf-layer { opacity: 0; z-index: 1; transition: opacity var(--pf-fondu-entree) var(--pf-fondu-courbe); }
.pf-layer.is-on { opacity: 1; }
/* La couche ENTRANTE passe au-dessus et monte seule ; la sortante reste opaque dessous.
   Sans cet empilement, le fondu serait croisé et sa couverture tomberait à 75 % en son
   milieu — le fond de la boîte transparaîtrait à chaque survol. Voir js/portfolio.js. */
.pf-layer.est-dessus { z-index: 2; }

/* ── L'entrée du hub ──────────────────────────────────────────────────────────
   Le fondu entre pages a été retiré (13/09/26) : le hub arrivait figé à côté d'univers qui
   s'animent. Il entre donc en cascade, dans le vocabulaire des univers — montée courte, courbe
   du tronc — pour que passer de l'un à l'autre se lise comme un même site.

   Le TITRE N'ENTRE PAS : c'est la candidate LCP, et le contrat SEO interdit de l'animer. Il est
   là dès la première image, et c'est tout ce qui l'entoure qui vient s'y poser — le filet se
   trace sous lui, l'intro monte, puis le sommaire ligne à ligne.

   Le sommaire anime le LIEN, jamais la ligne : les filets de `.pf-item` sont là d'emblée, et les
   chiffres montent dans une grille déjà tracée — celle d'un sommaire imprimé.

   Tout est en `backwards`, jamais `both` : l'animation terminée, il ne reste AUCUN style animé
   actif, et l'état de repos est l'état naturel. Si elle ne joue pas — réglage d'OS, éditeur,
   moteur qui l'ignore — rien ne reste caché. Et aucune classe à poser depuis un script : une
   animation déclarée dans une feuille bloquante s'applique dès le premier style, donc sans le
   clignotement payé sur les univers (voir portfolio-univers.css). */
@keyframes pf-trait    { from { transform: scaleX(0); } to { transform: scaleX(1); } }
@keyframes pf-monte    { from { opacity: 0; transform: translateY(1.2rem); } to { opacity: 1; transform: none; } }
@keyframes pf-apparait { from { opacity: 0; } to { opacity: 1; } }

.pf-head::after { animation: pf-trait 1.1s cubic-bezier(0.22, 1, 0.36, 1) 0.1s backwards; }
.pf-intro       { animation: pf-monte 0.7s cubic-bezier(0.16, 1, 0.3, 1) 0.2s backwards; }
.pf-index       { animation: pf-apparait 0.7s ease 0.3s backwards; }
/* Au-delà de cinq lignes, les suivantes partent avec la dernière : une sixième catégorie ne doit
   pas allonger l'attente de la page. */
.pf-link        { animation: pf-monte 0.7s cubic-bezier(0.16, 1, 0.3, 1) 0.64s backwards; }
.pf-item:nth-child(1) .pf-link { animation-delay: 0.36s; }
.pf-item:nth-child(2) .pf-link { animation-delay: 0.43s; }
.pf-item:nth-child(3) .pf-link { animation-delay: 0.5s; }
.pf-item:nth-child(4) .pf-link { animation-delay: 0.57s; }
.pf-item:nth-child(5) .pf-link { animation-delay: 0.64s; }

/* Le CTA est centré : il ne conclut pas la colonne de gauche, il conclut la PAGE. */
.pf-cta { text-align: center; margin-top: clamp(3rem, 8vw, 5.5rem); }

/* ── Desktop : la feuille se pose ──────────────────────────────────────────── */
@media (min-width: 900px) {
  .pf-body { grid-template-columns: minmax(0, 1.06fr) minmax(0, 0.94fr); }

  /* Le desktop n'affiche pas les vignettes : la feuille les remplace. Combiné à
     loading="lazy", rien n'entre jamais dans le viewport ⇒ aucune des cinq n'est
     téléchargée ici. L'ATTRIBUT src, lui, reste lisible — c'est tout ce dont le JS a
     besoin, et c'est ce qui évite une table JS parallèle qui divergerait. */
  .pf-thumb { display: none; }

  /* La planche descend. Calée en haut, elle démarrait au niveau de la ligne d'index et
     laissait le bas de la colonne des chiffres sans vis-à-vis. Elle se pose maintenant à
     peu près en regard du 01 et du 05 — un peu plus bas que le centre exact, parce qu'un
     centrage strict ferait remonter la peinture jusqu'à frôler l'index. La réserve suit la
     largeur : sous 1100 px la colonne des chiffres raccourcit, et un décalage figé y
     pousserait la feuille sous le pli. */
  /* Le fondu d'entrée couvre la première visite : le papier (non préchargé — écran gris Firefox)
     arrive ~50–80 ms après la première image ; posée d'emblée, la planche montrait le rideau crème
     seul, un rectangle blanc, avant la feuille déchirée. Voir portfolio-hub.test.cjs. */
  .pf-plate {
    display: block;
    position: relative;
    perspective: 1200px;
    margin-top: clamp(2rem, 4vw, 3.5rem);
    animation: pf-apparait 0.7s ease 0.15s backwards;
  }

  /* La bascule 3D vit sur un élément SÉPARÉ : un `filter` posé sur l'élément transformé
     aplatit la scène 3D.
     Pas de `transition` ici. Elle y était, à 500 ms, pendant que le JS réécrit `transform`
     à chaque image : on demandait donc au moteur d'aller quelque part en 500 ms, puis, 16 ms
     plus tard, ailleurs, et ainsi de suite. Une transition dont la cible est remplacée à
     chaque image n'adoucit rien — elle fait traîner la feuille derrière le curseur et
     interpole pour rien. Le fondu ne sert qu'au RETOUR, et c'est `.revient` qui le porte,
     posé au départ du pointeur puis retiré. Voir js/portfolio.js. */
  .pf-tilt { transform-style: preserve-3d; }
  .pf-tilt.revient { transition: transform 500ms cubic-bezier(0.2, 0.7, 0.3, 1); }

  .pf-feuille { position: relative; }

  /* ── L'ombre du papier ───────────────────────────────────────────────────────
     Elle était un `filter: drop-shadow` posé sur le PARENT de la feuille masquée. Le rendu
     était juste — mesuré le 11/09/2026, les coins de l'ombre suivaient la déchirure au point
     près (31/25/13/26 contre 31/24/12/26 pour le papier). Ce n'est pas sa FORME qui était
     fautive, c'est sa REPEINTURE.

     Pour dessiner une ombre à bord déchiré, le moteur doit composer tout le sous-arbre hors
     écran, en extraire l'alpha APRÈS masquage, le décaler et le flouter. Ce sous-arbre porte
     les deux images du fondu : chaque survol invalidait donc le calque filtré entier. Sous
     WebKit il n'était pas toujours refait en entier — mesuré sur la page servie, l'ombre
     n'arrivait qu'à 26,9 d'assombrissement là où une repeinture forcée en donne 50,8. La
     moitié de l'encre, et une coupe nette au milieu : le « rectangle en deux blocs ».

     Elle est donc DÉCOUPLÉE du contenu. Deux couches, chacune découpée par la MÊME
     déchirure que le papier — même turbulence, même graine, même amplitude — dont le flou
     est cuit plutôt que calculé par un filtre. Rien à composer hors écran, rien à
     réinvalider quand la photographie change, et le bord reste déchiré.

     Papier et ombres sont des IMAGES DÉJÀ DÉCOUPÉES, posées en simples fonds : même rendu
     que les anciens `mask-image`, sans masquage ni SVG calculé à chaque document. Sources :
     scripts/masques-feuille.cjs. ⚠️ JAMAIS de <link rel="preload"> sur ces images : sous Firefox,
     il faisait passer toute la page GRISE une fraction de seconde à chaque arrivée sur le hub par
     un lien (tranché le 13/09/26 sur le poste qui reproduit : ~80 % → 0/40 en le retirant seul).

     La boîte déborde de 60 unités SVG sur les quatre côtés (15 % en largeur, 11,54 % en
     hauteur) : c'est la marge qui laisse le flou s'étaler sans être rogné par la viewBox. Le
     rect y garde ses coordonnées d'origine, donc sa silhouette coïncide avec celle du papier. */
  /* ⟦feuille cuite — empreinte 817ab4cfb2ef51f1 (node scripts/cuire-masques.mjs) ⟧ */

  .pf-ombre {
    position: absolute;
    inset: -17.31% -22.5%;
    z-index: 0;
    pointer-events: none;
    transform: rotate(-0.55deg);         /* la feuille est posée de biais, son ombre aussi */
  }
  .pf-ombre::before,
  .pf-ombre::after {
    content: "";
    position: absolute;
    inset: 0;
    background-size: 100% 100%;
    background-repeat: no-repeat;
  }
  /* La portée : large et douce, c'est elle qui décolle la feuille de la page. */
  .pf-ombre::before {
    background-image: url("/images/ui/pf-feuille-portee.webp");   /* encre rgba(28, 25, 21, 0.36) cuite */
    transform: translateY(26px);
  }
  /* Le contact : serré et court, c'est lui qui la POSE. Sans cette seconde couche la feuille
     flotte au-dessus de la page au lieu d'y être déposée. */
  .pf-ombre::after {
    background-image: url("/images/ui/pf-feuille-contact.webp");   /* encre rgba(28, 25, 21, 0.13) cuite */
    transform: translateY(4px);
  }

  /* La feuille : marges latérales franches, marge basse plus haute que la marge haute —
     la proportion d'un tirage monté sur carton, pas celle d'un cadre. Le bord est déchiré
     par un SVG : un rectangle plein déformé par du bruit fractal, basse fréquence
     en X (longues ondulations) et plus haute en Y (fibres). Amplitude ~3,7 % : une
     déchirure légère, pas un confetti.
     Cette forme, sa couleur et le grain du papier sont CUITS dans une seule image (voir
     « L'ombre du papier » pour le pourquoi). Le grain ne valait que ±0,7 niveau sur 255 :
     l'image garde sa teinte moyenne, pas son bruit — invisible, et 13 Ko au lieu de 182. */
  .pf-sheet {
    position: relative;
    z-index: 1;                          /* au-dessus de .pf-ombre, qui la précède */
    margin: 0;
    /* Le papier cuit : forme déchirée, couleur #FCF9F3 et grain, dans une seule image. */
    background: url("/images/ui/pf-feuille-papier.webp") 0 0 / 100% 100% no-repeat;
    padding: 2.5rem 2.5rem 0;
    transform: rotate(-0.55deg);
  }

  /* Boîte à ratio FIXE : la couche « b » change d'image sans jamais déplacer quoi que ce
     soit autour d'elle. C'est ce qui rend le crossfade sans reflow. */
  .pf-shot {
    position: relative;
    aspect-ratio: 3 / 4;
    max-width: 100%;
    overflow: hidden;
    /* La couleur du PAPIER, pas un beige d'attente. Ce fond ne se voit qu'avant l'arrivée
       de la première image : autant qu'il ne se remarque pas quand il se voit. */
    background: #FCF9F3;
  }

  /* À l'arrivée, la photographie se découvre de bas en haut SUR le papier, sous un aplat de sa
     couleur — le geste de `jmpRevelePhoto` à l'accueil. La feuille, son ombre et sa déchirure
     sont là d'emblée : on pose un tirage sur un papier déjà sur la table.
     Un pseudo-élément par-dessus les couches, jamais les couches elles-mêmes : elles portent le
     crossfade du survol (js/portfolio.js) et la photo peut être la candidate LCP. Repos replié,
     `backwards`, `transform` seul — voir « L'entrée du hub ». */
  .pf-shot::after {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 3;
    background: #FCF9F3;
    transform: scaleY(0);
    transform-origin: top;
    pointer-events: none;
    animation: pf-rideau 1.15s cubic-bezier(0.22, 1, 0.36, 1) 0.25s backwards;
  }

  .pf-layer {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
  }

  /* Le cartouche : le nom de la catégorie au centre du papier, le folio à droite. C'est là
     que le nom se lit, et c'est pourquoi la peinture n'en porte pas — deux libellés pour
     une même chose, c'est du bruit. */
  .pf-cartouche {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: baseline;
    padding: 1.7rem 0.3rem 2.3rem;
  }
  .pf-cat {
    grid-column: 2;
    font-family: var(--font-body);
    font-size: 0.76rem;
    letter-spacing: 0.36em;
    text-transform: uppercase;
  }
  .pf-folio {
    grid-column: 3;
    justify-self: end;
    font-family: var(--font-title);
    font-weight: 300;
    font-style: italic;
    font-size: 1rem;
    color: var(--color-muted-text);
  }

  /* ── Le coup de pinceau ──────────────────────────────────────────────────────
     Une vraie touche peinte, détourée. Hors de .pf-sheet, donc jamais rognée par le masque
     du papier : la peinture arrive APRÈS le tirage, elle ne s'aligne sur rien.

     DEUX éléments, et c'est nécessaire : en CSS le `filter` s'applique AVANT le `mask`, et
     avant tout ce qui découpe la boîte. Une ombre portée posée sur la touche elle-même
     serait donc l'ombre de son RECTANGLE. Elle vit sur le parent, qui n'est pas découpé, et
     épouse alors la forme réellement peinte. C'est elle qui pose la peinture SUR le papier
     plutôt que dedans. */
  .pf-brosse {
    position: absolute;
    z-index: 3;
    pointer-events: none;
    top: -2.4rem;
    /* Centrée sur le papier. Elle était à 46 %, décalée d'un demi-pourcent de largeur vers
       la gauche — assez pour se voir quand on cherche le centre, pas assez pour passer pour
       une intention. La touche est peinte : son irrégularité suffit à la rendre vivante. */
    left: 50%;
    width: 78%;
    aspect-ratio: 820 / 222;
    /* Le `translateZ` n'est pas un ornement, c'est ce qui FIXE l'ordre de peinture.
       `.pf-tilt` porte `transform-style: preserve-3d` : ses enfants ne sont plus empilés par
       `z-index` mais TRIÉS EN PROFONDEUR. Sans lui, la peinture et la feuille sont toutes
       deux à z = 0 — coplanaires — et le moteur doit départager une égalité. WebKit la
       départageait dans un sens ou dans l'autre selon la re-rastérisation du calque : la
       peinture passait derrière le papier le temps d'une image, après un changement de
       catégorie suivi d'un survol. Deux pixels suffisent à lever l'égalité ; sous une
       perspective de 1200 px ils agrandissent la touche de 0,17 %, soit rien de visible. */
    transform: translateX(-50%) rotate(-1.1deg) translateZ(2px);
    filter: drop-shadow(0 2px 3px rgba(46, 10, 3, 0.32)) drop-shadow(0 1px 1px rgba(46, 10, 3, 0.22));
    /* La touche est PEINTE une fois le tirage découvert : un balayage de gauche à droite, à la
       vitesse d'un pinceau — lent au départ, lent à la fin. Le découpage déborde de la boîte
       (valeurs négatives) pour ne jamais rogner l'ombre portée ; il n'existe plus une fois
       l'animation finie (`backwards`), donc aucun découpage ne reste actif sur ce calque 3D. */
    animation: pf-pinceau 0.8s cubic-bezier(0.65, 0, 0.35, 1) 1s backwards;
  }
  .pf-brosse img { display: block; width: 100%; height: 100%; }

  .pf-link:hover .pf-num,
  .pf-link:focus-visible .pf-num { color: var(--pf-encre); }
  .pf-link:hover .pf-name,
  .pf-link:focus-visible .pf-name { color: var(--color-accent); transform: translateX(0.45rem); }
}

@keyframes pf-rideau  { from { transform: scaleY(1); } to { transform: scaleY(0); } }
@keyframes pf-pinceau { from { clip-path: inset(-30% 100% -30% -5%); } to { clip-path: inset(-30% -5% -30% -5%); } }

/* ── Réglage OS : les mouvements se coupent, la navigation reste entière ────── */
@media (prefers-reduced-motion: reduce) {
  /* Nommés un à un : le réglage global de style.css passe par `*`, qui ne vise pas les
     pseudo-éléments — le filet et le rideau joueraient encore. */
  .pf-head::after,
  .pf-intro,
  .pf-index,
  .pf-link,
  .pf-shot::after,
  .pf-plate,
  .pf-brosse { animation: none; }
  .pf-layer,
  .pf-num,
  .pf-name,
  .pf-tilt,
  .pf-tilt.revient { transition: none; }
  .pf-layer { transform: none; }
  .pf-layer.is-on { transform: none; }
}

