/* ═══════════════════════════════════════════════════════════════════════
   DYSON ART WORK — feuille de style
   La charte est celle du site que Dyson a validé : fond noir, texte
   couleur os (#FFF0D4), et un seul accent, le rouge (#D6001C) — celui
   de l'œil du cyborg et des mots qu'il souligne.

   LES POLICES DISENT LE SUJET — révision du 9 septembre 2026. Le logo
   est un visage coupé en deux : une moitié humaine, une moitié
   machine. La typographie reprend cette coupure au lieu de l'ignorer.
     · Bodoni Moda pour les titres — un didone, très fort contraste
       entre pleins et déliés. C'est la typographie des catalogues
       d'exposition ; elle porte la moitié humaine, et elle place
       d'emblée le site du côté de l'art, pas du portfolio.
     · Archivo pour le texte courant — une grotesque robuste, lisible
       à 15 pixels comme en gras.
     · Space Mono pour les étiquettes, compteurs et surtitres — la
       moitié machine. Chaque chiffre a la même largeur.
   Jost et Work Sans les précédaient : corrects, mais neutres au point
   de ne rien dire, et reconnaissables entre mille comme le réglage par
   défaut de qui n'a pas choisi.
   Tout le mouvement respecte prefers-reduced-motion, tout en bas.
   ═══════════════════════════════════════════════════════════════════════ */

/* ═══════════════════════════════════════════════════════════════════════
   VTKS ANIMAL 2 — la police du logo, et d'elle seule
   ───────────────────────────────────────────────────────────────────────
   C'est la typographie du logo de Dyson : lettres griffées, irrégulières,
   dessinées à la main. Elle n'est employée QUE pour « Dyson Art Work »
   en tête de page et au pied — deux mots. Une police de titre décorative
   perd tout son effet dès qu'on lui donne un paragraphe, et devient
   illisible en dessous d'un certain corps.

   `font-display: swap` : le nom s'affiche tout de suite dans la police
   de secours et bascule quand le fichier arrive. Un logo invisible
   pendant deux secondes est pire qu'un logo mal dessiné.

   LICENCE — À VÉRIFIER AVANT LA MISE EN LIGNE. Les polices VTKS sont
   distribuées libres pour un usage personnel ; l'usage commercial
   demande l'accord de l'auteur (Douglas Vitkauskas). Le site d'un
   artiste qui vend ses pièces est un usage commercial. Le fichier pèse
   508 Ko en TTF — il gagnerait à être converti en WOFF2 (environ 5 fois
   plus léger) le jour où l'accord est obtenu.
   ═══════════════════════════════════════════════════════════════════════ */
@font-face {
  font-family: "VTKS Animal 2";
  src: url("vtks-animal-2.ttf") format("truetype");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

:root {
  --noir: #000000;
  --suie: #070707;
  --suie-2: #0f0e0d;
  --suie-3: #171614;
  /* L'os reste la couleur de la charte, mais il ne sert plus PARTOUT :
     du crème pleine intensité sur chaque paragraphe donne un rendu
     jauni et fatigant. Le texte courant descend d'un cran, les titres
     seuls gardent l'os pur. C'est ce qui fait la hiérarchie. */
  --os: #fff0d4;
  --os-2: #cfc2a9;
  --os-3: #9d9384;
  --cendre: #6b6459;
  --cendre-2: #34302b;
  --rouge: #d6001c;
  --rouge-clair: #ff3a53;
  --rouge-sombre: #8f0013;
  --rouge-ombre: rgba(214, 0, 28, .4);
  --trait: rgba(255, 240, 212, .09);
  --trait-fort: rgba(255, 240, 212, .18);
  --trait-rouge: rgba(214, 0, 28, .55);

  --display: "Bodoni Moda", "Didot", "Bodoni 72", "Times New Roman", serif;
  --texte: "Archivo", "Helvetica Neue", Helvetica, Arial, sans-serif;
  --mono: "Space Mono", "SFMono-Regular", ui-monospace, Menlo, Consolas, monospace;
  --marque-police: "VTKS Animal 2", var(--display);

/* LA VOIX DURE EST DÉJÀ DANS LA PAGE.
     Ilyes, le 11 septembre 2026 : « les caractères Créer, Détruire et
     Reconstruire sont trop arrondis, mets une typo plus droite plus
     dure », puis — en regardant le site — « la typo du menu en haut
     sur ordi est bien, inspire-toi de ça ».
     Ce menu est en Space Mono, capitales, interlettré. C'est-à-dire
     exactement ce qu'il demandait : une chasse fixe, des fûts droits,
     aucune rondeur de didone. Elle est DÉJÀ chargée par la page — la
     bonne réponse ne coûte donc pas un téléchargement de plus.
     Pas de variable nouvelle : l'accroche prend --mono, et le jour où
     la police du site change, l'accroche suit sans qu'on y pense. */

  --marge: clamp(1rem, 4vw, 3.5rem);
  /* L'ESPACE ENTRE DEUX GRANDS BLOCS. Il montait à 8 rem — 128 pixels
     de noir — et sur un écran large, un visiteur pouvait croire la page
     finie avant la section suivante. 6 rem au plus garde la respiration ;
     sur téléphone rien ne change, le minimum reste 4 rem. */
  --respiration: clamp(4rem, 6.5vw, 6rem);
  --largeur: 1360px;
  --etroit: 46rem;
  --entete: 4.5rem;
  --rayon: 2px;

  --t-court: .32s;
  --t-moyen: .7s;
  --t-long: 1.1s;
  --courbe: cubic-bezier(.2, .7, .2, 1);

  color-scheme: dark;
}

/* ── ESSAI TYPOGRAPHIQUE (démonstration, voir gabarits/base.html) ─────
   Dyson trouve le caractère des titres trop arrondi : le Bodoni est un
   didone, ses terminaisons finissent en gouttes. Deux propositions à
   arêtes nettes, qui ne s'activent QUE par « ?typo=… » dans l'adresse.
   - machine : Chakra Petch, des courbes coupées en facettes — la moitié
     mécanique des bêtes de L'Arche, sans tomber dans l'écran de jeu.
   - acier : Big Shoulders Display, une linéale étroite et droite, de
     fonderie ; elle tient les titres longs sur une ligne.
   Le jour où l'une est retenue, elle remplace --display dans :root et
   ce bloc disparaît. */
html[data-typo="machine"] { --display: "Chakra Petch", "Archivo", "Helvetica Neue", Arial, sans-serif; }
html[data-typo="acier"] { --display: "Big Shoulders Display", "Archivo Narrow", "Arial Narrow", sans-serif; }
html[data-typo="machine"] h1, html[data-typo="machine"] h2, html[data-typo="machine"] h3, html[data-typo="machine"] h4 { letter-spacing: -.01em; font-weight: 600; }
html[data-typo="acier"] h1, html[data-typo="acier"] h2, html[data-typo="acier"] h3, html[data-typo="acier"] h4 { letter-spacing: .005em; font-weight: 700; }
html[data-typo]::after {
  content: "Essai typographique · " attr(data-typo);
  position: fixed; left: .6rem; bottom: .6rem; z-index: 10000; pointer-events: none;
  padding: .3rem .6rem; background: var(--rouge); color: var(--os);
  font: 400 .65rem/1.2 "Space Mono", monospace; letter-spacing: .12em; text-transform: uppercase;
}

/* ── Socle ─────────────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }
html {
  background: var(--noir);
  color: var(--os);
  font-family: var(--texte);
  font-weight: 400;
  font-size: 100%;
  line-height: 1.62;
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--entete) + 1rem);
}
body {
  margin: 0;
  min-height: 100vh;
  background: var(--noir);
  color: var(--os);
  overflow-x: hidden;
}
/* Le menu mobile glisse hors de l'écran par la droite : sans ceci, sa
   largeur compte dans le défilement horizontal du document. */
html { overflow-x: clip; }
/* MENU OUVERT : la page dessous ne glisse plus.
   `overflow: hidden` seul ne retient pas le geste sur un téléphone —
   le doigt continue de faire défiler ce qu'il y a derrière, et l'on
   ressort du menu ailleurs qu'où l'on était entré.
   `overscroll-behavior: contain` arrête le geste à la limite du menu ;
   `touch-action` sur le voile empêche le glissement de traverser. */
body.menu-ouvert { overflow: hidden; overscroll-behavior: none; }
body.menu-ouvert .nav { overscroll-behavior: contain; touch-action: pan-y; }
img, picture, video, svg { display: block; max-width: 100%; height: auto; }

/* ═══════════════════════════════════════════════════════════════════════
   TOUTE ENVELOPPE <picture> REMPLIT SON CADRE
   ───────────────────────────────────────────────────────────────────────
   C'EST LA CAUSE DES ZONES NOIRES SOUS LES PHOTOS, et elle est invisible
   à la lecture du HTML.

   La ligne juste au-dessus pose `height: auto` sur toute image — et le
   <picture> qui enveloppe le <img> la reçoit aussi, puisqu'il est visé
   par le même sélecteur. Le <img>, à qui l'on demande `height: 100 %`,
   calcule alors sa hauteur sur un parent en hauteur AUTOMATIQUE :
   c'est-à-dire sur lui-même. Il garde donc sa taille naturelle et se
   colle en haut du cadre, laissant tout le noir en bas.

   Le défaut ne se voit que sur les cadres à proportions fixes, et il
   fallait le corriger UNE FOIS POUR TOUTES : il est revenu trois fois —
   sur les vignettes, sur les cartes de jeu, puis sur le portrait de
   l'accueil, celui qu'Ilyes a fini par entourer en rouge. Chaque cadre
   à `aspect-ratio` de la feuille est listé ici ; un cadre nouveau s'y
   ajoute, et `tests/epreuve-pages.mjs` vérifie qu'aucun ne l'oublie.
   ═══════════════════════════════════════════════════════════════════════ */
.carte-visuel picture,
.oeuvre-principale picture,
.vignette picture,
.artiste-portrait picture,
.artiste-visuel picture,
.arche-visuel picture,
.jeu-appel-visuel picture,
.actu-visuel picture,
.periode-visuel picture,
.carte-jeu-visuel picture,
.atelier picture,
.agrandir picture {
  display: block; width: 100%; height: 100%;
}
a { color: inherit; text-decoration: none; }
a:hover { color: var(--rouge-clair); }
:focus-visible { outline: 2px solid var(--rouge-clair); outline-offset: 3px; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }
ul, ol { margin: 0; padding: 0; list-style: none; }
figure { margin: 0; }
/* Un didone n'a de force qu'en grand et serré : à interlettrage nul et
   en 400, Bodoni ressemble à du Times. On serre, on monte en graisse,
   et on ne l'emploie jamais sous 1,4 rem. */
h1, h2, h3, h4 {
  margin: 0; font-family: var(--display); font-weight: 500;
  line-height: 1.02; letter-spacing: -.015em; text-wrap: balance;
  font-optical-sizing: auto;
  /* Un mot plus large que l'écran — un titre anglais, un nom composé
     sur 320 px — passe à la ligne au lieu de sortir du cadre, où
     `overflow-x: clip` sur <html> le coupait sans rien dire. */
  overflow-wrap: break-word;
}
p { margin: 0; }
::selection { background: var(--rouge); color: var(--os); }
[hidden] { display: none !important; }

.saut {
  position: absolute; left: 1rem; top: -4rem; z-index: 100;
  padding: .6rem 1rem; background: var(--rouge); color: var(--os);
  font-family: var(--mono); font-size: .85rem;
}
.saut:focus { top: 1rem; }

/* Le grain : un voile très léger qui donne au noir une matière d'argile
   plutôt qu'un aplat d'écran. Une texture SVG minuscule, en boucle. */
.grain {
  position: fixed; inset: 0; z-index: 90; pointer-events: none;
  opacity: .045; mix-blend-mode: screen;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='180' height='180'><filter id='g'><feTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 .6 0'/></filter><rect width='100%' height='100%' filter='url(%23g)'/></svg>");
}

main { display: block; }
main:focus { outline: none; }

/* ── Typographie utilitaire ────────────────────────────────────────── */
.mono {
  font-family: var(--mono); font-size: .68rem; font-weight: 400;
  letter-spacing: .22em; text-transform: uppercase;
  font-variant-numeric: tabular-nums;
}
.surtitre {
  display: flex; align-items: center; gap: .9rem;
  margin: 0 0 1.1rem; color: var(--os-3);
}
/* Le trait rouge suffit à marquer la section : le texte de l'étiquette
   n'a pas besoin d'être rouge lui aussi. Deux accents côte à côte
   s'annulent. */
.surtitre::before { content: ""; width: 2.4rem; height: 1px; background: var(--rouge); flex: none; }
.surtitre a { color: inherit; }
.titre-section {
  font-size: clamp(1.8rem, 1.1rem + 2vw, 3rem);
  font-weight: 500; letter-spacing: -.022em;
}
.titre-page {
  font-size: clamp(2.2rem, 1.3rem + 3.2vw, 4.2rem);
  font-weight: 500; letter-spacing: -.025em;
}
.titre-page.grand { font-size: clamp(2.8rem, 1.2rem + 5vw, 5.8rem); }
/* Le chapeau descend aussi : il suivait les titres et finissait par
   concurrencer le texte courant. */
.chapeau {
  max-width: var(--etroit);
  /* Le chapeau passe en Archivo : en didone, il concurrençait le titre
     juste au-dessus — deux serifs à fort contraste l'un sur l'autre, et
     l'œil ne sait plus lequel lire en premier. */
  font-family: var(--texte); font-weight: 300;
  font-size: clamp(1.02rem, .97rem + .35vw, 1.2rem); line-height: 1.62;
  color: var(--os-2);
}
.chapeau p + p { margin-top: .6em; }
.lien-fleche {
  display: inline-flex; align-items: center; gap: .5rem;
  font-family: var(--mono); font-size: .8rem; letter-spacing: .1em; text-transform: uppercase;
  color: var(--rouge-clair);
}
.lien-fleche span { transition: transform var(--t-court) var(--courbe); }
.lien-fleche:hover span { transform: translateX(.35rem); }

/* Boutons : le rouge plein pour l'action, le trait pour la seconde voie. */
.bt {
  display: inline-flex; align-items: center; justify-content: center; gap: .6rem;
  min-height: 3rem; padding: .7rem 1.6rem;
  font-family: var(--mono); font-size: .8rem; letter-spacing: .12em; text-transform: uppercase;
  border: 1px solid var(--rouge); border-radius: var(--rayon);
  color: var(--os); position: relative; overflow: hidden; isolation: isolate;
  transition: color var(--t-court), border-color var(--t-court), transform var(--t-court) var(--courbe);
}
.bt::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background: var(--rouge);
  transform: translateY(101%);
  transition: transform var(--t-moyen) var(--courbe);
}
.bt.plein { color: var(--os); }
.bt.plein::before { transform: none; }
.bt.plein:hover { color: var(--noir); border-color: var(--os); }
.bt.plein:hover::before { background: var(--os); }
.bt.ligne:hover { color: var(--os); }
.bt.ligne:hover::before { transform: none; }
.bt:active { transform: translateY(1px); }
.bt.petit { min-height: 2.4rem; padding: .4rem 1.1rem; font-size: .72rem; }
.bt[disabled] { opacity: .55; cursor: wait; }
.actions { display: flex; flex-wrap: wrap; gap: .8rem 1rem; align-items: center; }

.pastille {
  display: inline-block; padding: .18em .6em; border-radius: 999px;
  font-family: var(--mono); font-size: .68rem; letter-spacing: .1em; text-transform: uppercase;
  border: 1px solid var(--rouge); color: var(--os);
}
.pastille-vendue, .pastille-collection { border-color: var(--cendre-2); color: var(--cendre); }
.pastille-archive { border-color: var(--os-2); color: var(--os-2); letter-spacing: .1em; }
.pastille-reserve, .pastille-reservee { border-color: var(--os-2); color: var(--os-2); }

/* ── Prose ─────────────────────────────────────────────────────────── */
.prose { max-width: 62ch; font-size: 1rem; font-weight: 300; line-height: 1.75; color: var(--os-2); }
.prose p { margin: 0 0 1.1em; }
.prose p:last-child { margin-bottom: 0; }
.prose strong { color: var(--os); font-weight: 600; }
.prose a { color: var(--rouge-clair); border-bottom: 1px solid var(--trait-rouge); }
.prose.grande {
  font-family: var(--display); font-weight: 400;
  font-size: clamp(1.25rem, 1.05rem + 1vw, 1.75rem); line-height: 1.4; color: var(--os);
}
.prose.petite { font-size: .95rem; }
.prose.extrait { display: -webkit-box; -webkit-line-clamp: 6; -webkit-box-orient: vertical; overflow: hidden; }
.prose.colonnes { max-width: none; columns: 2 22rem; column-gap: 3.5rem; }
.prose.colonnes p { break-inside: avoid; }
.prose.grande p:first-child::first-letter,
.arche-manifeste .prose p:first-child::first-letter {
  float: left; margin: .1em .15em 0 0;
  font-size: 3.2em; line-height: .8; color: var(--rouge);
}

/* ── En-tête ───────────────────────────────────────────────────────── */
.entete {
  position: sticky; top: 0; z-index: 80;
  display: flex; align-items: center; justify-content: space-between; gap: 1.5rem;
  min-height: var(--entete); padding: 0 var(--marge);
  /* AUCUN FOND. Même un fondu très sombre restait un aplat de quatre
     centimètres posé sur un fond qui bouge : la poussière disparaissait
     dessous et le bord se voyait au défilement.
     Le menu flotte donc dans le vide. Ce qui le rend lisible n'est plus
     une plaque mais une ombre portée sur le TEXTE : elle épouse chaque
     lettre au lieu de couvrir la bande entière. */
  background: none;
  border-bottom: 0;
  text-shadow: 0 1px 12px rgba(0, 0, 0, .95), 0 0 4px rgba(0, 0, 0, .9);
}
.entete .marque img { filter: drop-shadow(0 2px 10px rgba(0, 0, 0, .9)); }
/* Au défilement, un voile TRÈS court sous le menu : assez pour décoller
   le texte de ce qui passe dessous, trop court pour faire une bande. */
body.est-defile .entete {
  background: linear-gradient(to bottom, rgba(0, 0, 0, .62), rgba(0, 0, 0, 0));
}
.marque { display: inline-flex; align-items: center; gap: .8rem; }
/* Le halo doré autour de la marque a sauté : une lueur posée derrière un
   logo est le raccourci le plus courant pour faire « premium », et le
   plus reconnaissable. Le logo est assez fort sans projecteur. */
.marque img { width: 32px; height: 45px; transition: opacity var(--t-court) ease; }
.marque:hover img { opacity: .82; }
.marque-nom {
  font-family: var(--marque-police); font-weight: 400;
  /* Une police griffée demande plus de corps et d'air qu'une grotesque
     pour rester lisible : elle n'a ni pleins réguliers ni approches. */
  font-size: 1.2rem; line-height: 1; letter-spacing: .05em;
  color: var(--os);
}
.marque-nom em { font-style: normal; font-weight: 400; color: var(--rouge); }

.nav ul { display: flex; align-items: center; gap: clamp(.6rem, 1.6vw, 1.8rem); }
.nav a {
  display: inline-block; padding: .4rem 0; position: relative;
  font-family: var(--mono); font-size: .74rem; letter-spacing: .12em; text-transform: uppercase;
  color: var(--os-2);
}
.nav a::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 1px;
  background: var(--rouge); transform: scaleX(0); transform-origin: left;
  transition: transform var(--t-moyen) var(--courbe);
}
.nav a:hover, .nav a[aria-current] { color: var(--os); }
.nav a:hover::after, .nav a[aria-current]::after { transform: scaleX(1); }
.nav-langue a { color: var(--rouge); border: 1px solid var(--trait-rouge); padding: .3rem .55rem; border-radius: var(--rayon); }
.nav-langue a::after { display: none; }
.nav-langue a:hover { background: var(--rouge); color: var(--os); }

.burger {
  display: none; width: 2.6rem; height: 2.6rem; padding: .6rem;
  flex-direction: column; justify-content: space-between;
}
.burger span { display: block; height: 1px; background: var(--os); transition: transform var(--t-moyen) var(--courbe), opacity var(--t-court); }
.burger[aria-expanded="true"] span:nth-child(1) { transform: translateY(9px) rotate(45deg); }
.burger[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.burger[aria-expanded="true"] span:nth-child(3) { transform: translateY(-9px) rotate(-45deg); }

@media (max-width: 860px) {
  .burger { display: flex; }
  /* La marque aussi rétrécit : VTKS est large, et elle poussait le
     burger contre le bord. */
  .marque-nom { font-size: 1rem; letter-spacing: .04em; }
  .nav {
    position: fixed; inset: 0 0 0 0; z-index: 79;
    /* OPAQUE, ET DEPUIS LE HAUT DE L'ÉCRAN. À 96 % de noir on voyait
       encore le logo au travers — et un menu à travers lequel on lit la
       page est illisible. Il part aussi de zéro et non de sous l'entête :
       l'entête n'a plus de fond depuis qu'elle ne pose plus de bande, le
       menu doit donc couvrir jusqu'en haut. */
    background: #000; padding: calc(var(--entete) + 1rem) var(--marge) 2rem;
    transform: translateX(100%); opacity: 0;
    /* Fermé, le menu est aussi invisible pour le clavier et les lecteurs
       d'écran : un lien glissé hors de l'écran reste sinon tabulable. */
    visibility: hidden;
    transition: transform var(--t-moyen) var(--courbe), opacity var(--t-moyen), visibility 0s var(--t-moyen);
  }
  body.menu-ouvert .nav { transform: none; opacity: 1; visibility: visible; transition-delay: 0s; }
  .nav ul { flex-direction: column; align-items: flex-start; gap: .4rem; }
  /* LE MENU TIENT EN UN ÉCRAN, SOUS-MENUS COMPRIS. Sept entrées plus
     quatre périodes plus deux jeux : à 1,45 rem, il fallait faire défiler
     pour atteindre « Contact ». À 1,15 rem tout tient, et le padding
     garde la cible tactile au-dessus des 44 pixels réglementaires. */
  .nav a { font-family: var(--display); font-size: 1.15rem; letter-spacing: -.01em; text-transform: none; padding: .42rem 0; }
  .nav { overflow-y: auto; -webkit-overflow-scrolling: touch; }
  .nav ul { gap: .1rem; padding-bottom: 3rem; }
  /* Les sous-menus sont plus discrets que les entrées principales :
     sans cette hiérarchie, treize lignes de même taille ne se lisent
     plus comme un menu mais comme une liste. */
  .nav-sous a { padding: .3rem .6rem; }
  .nav-sous .nav-sous-nom { font-size: .92rem; }
  .nav-sous .nav-sous-dates { font-size: .55rem; }
  .nav-langue { margin-top: 1.5rem; }
  .nav-langue a { font-family: var(--mono); font-size: .8rem; letter-spacing: .12em; text-transform: uppercase; }
}

/* ── Bandes et grilles ─────────────────────────────────────────────── */
.bande {
  width: min(100% - 2 * var(--marge), var(--largeur));
  margin: var(--respiration) auto 0;
}
.bande.etroite { width: min(100% - 2 * var(--marge), var(--etroit)); }
.bande-tete { margin-bottom: clamp(1.5rem, 3vw, 2.5rem); }
.bande-tete /* Le chapeau descend aussi : il suivait les titres et finissait par
   concurrencer le texte courant. */
.chapeau { margin-top: .8rem; }
.suite { margin-top: 2rem; }

.grille { display: grid; gap: clamp(1rem, 2.4vw, 2rem); }
.grille-3 { grid-template-columns: repeat(auto-fill, minmax(min(100%, 18rem), 1fr)); }
.grille-4 { grid-template-columns: repeat(auto-fill, minmax(min(100%, 14rem), 1fr)); }
.grille-oeuvres { grid-template-columns: repeat(auto-fill, minmax(min(100%, 17rem), 1fr)); }
/* `minmax(0, 1fr)` et non `1fr` : une colonne `1fr` ne descend pas sous
   la largeur de son plus long mot, et un titre de carte trop long
   élargissait sa colonne jusqu'à pousser la seconde hors de l'écran. */
@media (max-width: 600px) { .grille-oeuvres, .grille-3, .grille-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .8rem; } }
.carte-titre { overflow-wrap: anywhere; }
.grille-vide { margin: 2rem 0; }

/* La carte d'œuvre : la photo, une fissure d'or qui s'ouvre au survol,
   un titre en capitale de garamond. Rien de plus. */
.carte { position: relative; }
.carte-lien { display: block; color: inherit; }
/* LA VIGNETTE EST CARRÉE, ET NE ROGNE RIEN.
   Les photos de L'Arche sont maintenant carrées, sujet centré sur du
   noir pur (voir outils/cadrer-arche.php). Un cadre en 4/5 avec
   `cover` reprenait donc les côtés de chaque tête — les oreilles, les
   cornes, le bec — c'est-à-dire exactement ce qu'il y a à voir.
   Un carré avec `contain` montre la pièce entière ; et comme son fond
   est le noir de la page, le raccord ne se voit pas. */
.carte-visuel {
  position: relative; overflow: hidden; aspect-ratio: 1;
  background: var(--noir); border-radius: var(--rayon);
}
/* Le cadrage revient au centre : les planches ont été recadrées sous
   leur texte À LA SOURCE (voir le commit « les planches du livret sont
   recadrées »), il n'y a donc plus rien à fuir en haut de l'image. */
.carte-visuel img {
  width: 100%; height: 100%; object-fit: cover; object-position: 50% 50%;
  transition: transform 1.4s var(--courbe), filter var(--t-long);
}
.carte-lien:hover .carte-visuel img { transform: scale(1.06); filter: contrast(1.04) saturate(1.05); }
.carte-visuel::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(180deg, transparent 55%, rgba(0, 4, 8, .55));
  opacity: 0; transition: opacity var(--t-moyen);
}
.carte-lien:hover .carte-visuel::after { opacity: 1; }
.carte-vide {
  display: block; width: 100%; height: 100%;
  background:
    linear-gradient(135deg, transparent 48%, var(--trait) 49%, var(--trait) 51%, transparent 52%),
    linear-gradient(45deg, transparent 48%, var(--trait) 49%, var(--trait) 51%, transparent 52%),
    var(--suie-2);
}
.carte-vide.grande { aspect-ratio: 4 / 5; }

.fissure { position: absolute; inset: 0; pointer-events: none; overflow: hidden; }
.fissure::before, .fissure::after {
  content: ""; position: absolute; height: 1px; background: var(--os);
  box-shadow: 0 0 10px var(--rouge), 0 0 2px var(--os);
  transform: scaleX(0); transform-origin: left;
  transition: transform var(--t-long) var(--courbe);
}
.fissure::before { left: -4%; top: 34%; width: 70%; transform: scaleX(0) rotate(22deg); }
.fissure::after { left: 38%; top: 58%; width: 62%; transform: scaleX(0) rotate(-31deg); transition-delay: .18s; }
.carte-lien:hover .fissure::before, .agrandir:hover .fissure::before, .artiste-portrait:hover .fissure::before, .periode-visuel:hover .fissure::before { transform: scaleX(1) rotate(22deg); }
.carte-lien:hover .fissure::after, .agrandir:hover .fissure::after, .artiste-portrait:hover .fissure::after, .periode-visuel:hover .fissure::after { transform: scaleX(1) rotate(-31deg); }

.carte-texte { padding: .9rem .1rem 0; }
.carte-titre { font-size: 1.35rem; font-weight: 500; letter-spacing: .005em; transition: color var(--t-court); }
.carte-lien:hover .carte-titre { color: var(--rouge-clair); }
.carte-meta {
  display: flex; flex-wrap: wrap; gap: .3rem .9rem; align-items: center; margin-top: .3rem;
  font-family: var(--mono); font-size: .72rem; letter-spacing: .08em; text-transform: uppercase; color: var(--cendre);
}
.carte-meta span:empty { display: none; }
.carte-prix { margin-top: .5rem; font-family: var(--display); font-size: 1.25rem; color: var(--os); }
.carte-action { margin-top: .8rem; }

/* ── Accueil : le héros ────────────────────────────────────────────── */
/* TROIS BLOCS DE TEXTE À DROITE DU VISAGE : l'accroche, l'aperçu de
   L'Arche, puis la présentation et les boutons. L'aperçu est DANS le
   premier écran, sur ordinateur comme sur téléphone — c'était la
   demande : qu'on arrive sur ce que Dyson fait maintenant, sans devoir
   descendre pour le découvrir. */
.heros {
  position: relative; display: grid; grid-template-columns: 5fr 6fr; align-items: center;
  grid-template-areas: "visuel haut" "visuel apercu" "visuel bas";
  align-content: center;
  column-gap: clamp(1.5rem, 4vw, 5rem); row-gap: clamp(1.4rem, 2.4vw, 2.2rem);
  width: min(100% - 2 * var(--marge), var(--largeur)); margin: 0 auto;
  min-height: calc(100vh - var(--entete)); min-height: calc(100svh - var(--entete));
  padding: clamp(1.25rem, 3vw, 3rem) 0;
}
.heros-visuel { grid-area: visuel; }
.heros-haut { grid-area: haut; }
.heros-bas { grid-area: bas; }
.heros-bas .sous-accroche { margin-top: 0; }
.heros-apercu { grid-area: apercu; min-width: 0; }
.heros-apercu-tete {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: .4rem 1.2rem; margin-bottom: .9rem;
}
.heros-apercu-tete .surtitre { margin: 0; line-height: 1.4; }
.grille-5 { grid-template-columns: repeat(5, minmax(0, 1fr)); gap: clamp(.5rem, 1vw, .9rem); }
/* Des vignettes, pas des cartes : la période est la même pour les cinq,
   la répéter sous chacune ne dirait rien. */
.heros-apercu .carte-texte { padding-top: .45rem; }
.heros-apercu .carte-titre { font-size: clamp(.9rem, .7rem + .45vw, 1.1rem); line-height: 1.15; }
.heros-apercu .carte-meta { display: none; }
.heros::before {
  content: ""; position: absolute; z-index: -1; left: -20%; top: -10%;
  width: 70%; aspect-ratio: 1; border-radius: 50%; pointer-events: none;
  background: radial-gradient(circle, rgba(214, 0, 28, .10), transparent 62%);
  filter: blur(70px);
}
/* L'aplat noir posé derrière le logo a été retiré avec le reste : le
   PNG détouré ne pose plus de rectangle, il n'y a donc plus rien à
   cacher — et un halo noir sur un fond qui bouge était lui-même une
   tache. */
.heros-visuel { perspective: 1200px; position: relative; }
.heros-cadre {
  /* Le rapport est celui du logo APRÈS recadrage (811 × 1145) : gardé à
     870 × 1200, le cadre était plus haut que l'image et rajoutait lui-
     même une bande noire en bas. */
  /* PLUS DE FONDU, PLUS DE MÉLANGE, PLUS DE RUSE : Dyson a livré le
     logo DÉTOURÉ, en PNG à fond transparent, le 10 septembre 2026. Le
     crâne est entier, la bande « DYSON » est gardée, et il n'y a plus
     rien autour à cacher. Tout ce qui était là pour masquer un
     rectangle — le fondu radial, `mix-blend-mode: lighten`, l'aplat
     noir derrière — a été retiré : ces rustines coûtaient de la netteté
     sur les bords du visage, et elles n'ont plus d'objet. */
  position: relative; aspect-ratio: 792 / 1132; max-height: min(84vh, 800px); margin: 0 auto;
  overflow: visible;
  /* PAS DE CADRE, PAS DE COIN ARRONDI, PAS D'OMBRE PORTÉE. Le fond de
     l'illustration est du noir pur, celui de la page aussi : dès qu'on
     pose un filet ou une ombre, le rectangle réapparaît et l'image se
     lit comme une vignette collée sur la page. Sans eux, le visage
     flotte dans le noir — c'est exactement ce qu'on veut. */
  transform: rotateY(calc(var(--px, 0) * -6deg)) rotateX(calc(var(--py, 0) * 5deg)) translateZ(0);
  transition: transform .9s var(--courbe);
  will-change: transform;
}
.heros-visage, .heros-visage img { width: 100%; height: 100%; }
/* ── LE FOND NOIR DISPARAÎT SANS DÉTOURAGE ──────────────────────────
   Le problème est apparu le jour où le fond du site a cessé d'être
   noir : la braise et la poussière l'éclaircissent légèrement, et
   l'illustration — dont le fond est du noir PUR — s'est mise à dessiner
   un rectangle plus sombre que la page.

   `mix-blend-mode: lighten` répond exactement à ça : chaque pixel garde
   la valeur la PLUS CLAIRE entre l'image et ce qu'il y a derrière. Le
   noir pur de l'illustration laisse donc passer le fond, pendant que le
   visage, lui, reste intact. Aucun détourage, aucun PNG à découper : le
   crâne flotte pour de bon, et il continuera de flotter si le fond
   change encore.

   Le voile de fondu qui masquait les bords devient inutile : il servait
   à cacher un raccord qui n'existe plus. */
.heros-visage img { object-fit: contain; }

/* L'œil rouge : mesuré sur l'image (68,59 % × 43,57 %). Il ne bouge
   pas, il respire. Le mélange « screen » le fait brûler sur le noir. */
.heros-oeil {
  position: absolute; left: 68.59%; top: 43.57%; width: 9%; aspect-ratio: 1;
  transform: translate(-50%, -50%); border-radius: 50%; pointer-events: none;
  background: radial-gradient(circle, rgba(255, 90, 70, .95) 0%, rgba(224, 38, 28, .7) 28%, rgba(224, 38, 28, 0) 70%);
  mix-blend-mode: screen; opacity: .6;
  /* Il respire, il ne clignote pas : neuf secondes de cycle et une
     amplitude faible. Une pulsation rapide attire l'œil en permanence
     et empêche de lire le texte à côté. */
  animation: oeil 9s ease-in-out infinite;
}
@keyframes oeil {
  0%, 100% { opacity: .42; transform: translate(-50%, -50%) scale(1); }
  50% { opacity: .85; transform: translate(-50%, -50%) scale(1.12); }
}
/* LE REFLET QUI BALAIE L'IMAGE A ÉTÉ RETIRÉ. Une brillance qui traverse
   une photo toutes les neuf secondes est le vocabulaire des bannières
   publicitaires : elle dit « regarde-moi » à une image qui se suffisait.
   L'élément reste dans le gabarit mais ne montre plus rien — le retirer
   du HTML obligerait à republier toutes les pages pour un span vide. */
.heros-balayage { display: none; }

.heros-texte { position: relative; z-index: 1; }
.heros-texte .surtitre { margin-bottom: 1.6rem; }
/* UN CRAN EN DESSOUS SUR GRAND ÉCRAN. À 6,6 rem — plus de cent pixels —
   « Créer, Détruire, Reconstruire » prenait la moitié de la page et le
   didone perdait son élégance : passé une certaine taille, un caractère
   à fort contraste devient une affiche, pas une typographie.
   5,4 rem garde la force et rend la ligne lisible d'un seul regard. */
.accroche {
  /* Le menu porte .12em à 0,74 rem. Recopier cette valeur ici serait
     une erreur de lecture : un interlettrage se calcule pour un corps.
     Ce qui vaut à douze pixels disloque un titre à cinquante. On garde
     donc le CARACTÈRE — chasse fixe, capitales — et on rend l'espace
     que le corps a déjà donné.
     La chasse fixe rend `ch` exact : à 18ch, « CRÉER, DÉTRUIRE, » tient
     sur sa ligne, et la phrase tombe en trois blocs égaux. */
  font-family: var(--mono);
  font-size: clamp(1.45rem, .5rem + 3.1vw, 3.35rem);
  font-weight: 700; line-height: 1.14; letter-spacing: .012em;
  text-transform: uppercase;
  max-width: 18ch;
}
/* Une chasse fixe a besoin de sa pleine espace : .26em, réglé pour une
   proportionnelle, colle les mots d'un monospace. */
.accroche .mot-espace { width: .62em; }
/* Le mot « détruire » porte l'accent — un seul mot en rouge dans toute
   la page. Un accent qui sert partout ne souligne plus rien. */
.accroche .mot-bloc:nth-child(3) { color: var(--rouge); }
.sous-accroche {
  margin-top: 1.6rem; max-width: 36rem;
  font-family: var(--texte); font-weight: 300;
  font-size: clamp(1rem, .95rem + .35vw, 1.15rem); line-height: 1.66; color: var(--os-2);
}
.heros-texte .actions { margin-top: 2.2rem; }
.heros-compte { margin-top: 2.6rem; }
@media (max-width: 900px) {
  .heros { grid-template-columns: minmax(0, 1fr); grid-template-areas: "visuel" "haut" "apercu" "bas"; min-height: 0; padding-top: 1rem; }
  .heros-cadre { max-height: 68vh; }
  .heros-texte { text-align: left; }
  /* L'aperçu devient un bandeau qu'on fait glisser du pouce : deux
     pièces et demie à l'écran, la troisième coupée dit qu'il y en a
     d'autres. Cinq vignettes empilées en grille prenaient un écran. */
  .grille-5 {
    grid-template-columns: none; grid-auto-flow: column; grid-auto-columns: minmax(7.5rem, 40%);
    overflow-x: auto; overscroll-behavior-x: contain; scroll-snap-type: x mandatory;
    padding-bottom: .5rem; scrollbar-width: thin; scrollbar-color: var(--cendre-2) transparent;
  }
  .grille-5 > li { scroll-snap-align: start; }
}

/* ── La frise chronologique ────────────────────────────────────────── */
.frise {
  position: relative; display: grid; grid-auto-flow: column; grid-auto-columns: minmax(11rem, 1fr);
  gap: 1rem; padding-top: 1.6rem; overflow-x: auto; scroll-snap-type: x proximity;
  scrollbar-width: thin; scrollbar-color: var(--cendre-2) transparent;
}
.frise::before {
  content: ""; position: absolute; left: 0; right: 0; top: .5rem; height: 1px;
  background: linear-gradient(90deg, var(--rouge), var(--trait-rouge) 60%, transparent);
  transform: scaleX(0); transform-origin: left; transition: transform 1.6s var(--courbe);
}
.frise.est-visible::before { transform: scaleX(1); }
.frise-etape { scroll-snap-align: start; }
.frise-etape a { display: grid; gap: .35rem; position: relative; padding-top: .8rem; }
.frise-etape a::before {
  content: ""; position: absolute; left: 0; top: -1.35rem; width: 7px; height: 7px;
  border-radius: 50%; background: var(--noir); border: 1px solid var(--rouge);
  transition: background var(--t-court), box-shadow var(--t-court);
}
.frise-etape a:hover::before { background: var(--rouge); box-shadow: 0 0 12px var(--rouge); }
.frise-dates { color: var(--rouge); }
.frise-nom { font-family: var(--display); font-size: 1.5rem; font-weight: 500; line-height: 1.1; color: var(--os); }
.frise-compte { font-size: .7rem; }
.frise-compte::after { content: " œuvres"; text-transform: none; letter-spacing: 0; }
html[lang="en"] .frise-compte::after { content: " works"; }
.chrono.compacte .frise-nom { font-size: 1.25rem; }

/* ── Appels : l'Arche, l'artiste, le jeu ───────────────────────────── */
.arche-appel, .artiste-appel, .jeu-appel {
  display: grid; grid-template-columns: 1fr 1fr; gap: clamp(2rem, 5vw, 6rem); align-items: center;
}
.arche-visuel, .artiste-visuel { position: relative; overflow: hidden; border-radius: var(--rayon); background: var(--suie); }
.arche-visuel { aspect-ratio: 4 / 3; }
.arche-visuel img, .artiste-visuel img { width: 100%; height: 100%; object-fit: cover; }
.artiste-visuel { aspect-ratio: 4 / 5; max-width: 30rem; justify-self: end; }
.arche-appel .titre-section, .artiste-appel .titre-section { margin-bottom: 1.2rem; }
.arche-appel .bt, .artiste-appel .lien-fleche { margin-top: 1.6rem; }
.jeu-appel {
  padding: clamp(1.5rem, 4vw, 3.5rem); background: var(--suie); border: 1px solid var(--trait); border-radius: var(--rayon);
  grid-template-columns: minmax(10rem, 18rem) 1fr;
}
.jeu-appel-visuel { position: relative; aspect-ratio: 1; overflow: hidden; border-radius: var(--rayon); }
.jeu-appel-visuel img { width: 100%; height: 100%; object-fit: cover; }
.jeu-appel-grille {
  position: absolute; inset: 0; pointer-events: none;
  background:
    linear-gradient(90deg, var(--noir) 1px, transparent 1px) 0 0 / 33.34% 100%,
    linear-gradient(0deg, var(--noir) 1px, transparent 1px) 0 0 / 100% 33.34%;
  animation: grille-glisse 6s linear infinite;
}
@keyframes grille-glisse { to { background-position: 33.34% 0, 0 33.34%; } }
.jeu-appel-texte /* Le chapeau descend aussi : il suivait les titres et finissait par
   concurrencer le texte courant. */
.chapeau { margin: .8rem 0 1.4rem; }
@media (max-width: 800px) {
  .arche-appel, .artiste-appel, .jeu-appel { grid-template-columns: 1fr; }
  .artiste-appel .artiste-texte { order: 2; }
  .artiste-visuel { justify-self: start; }
}

/* ── Actualités ────────────────────────────────────────────────────── */
.actus-liste { display: grid; gap: 0; border-top: 1px solid var(--trait); }
.actu { padding: 1.4rem 0; border-bottom: 1px solid var(--trait); }
.actu h3, .actu-titre { font-size: 1.5rem; margin-top: .4rem; }
.actu h3 a:hover { color: var(--rouge-clair); }
.actu-lieu { margin-top: .3rem; color: var(--cendre); }
.actu-lieu .mono { margin-right: .4rem; }
.actus-liste.detaillee .actu { display: grid; grid-template-columns: minmax(0, 20rem) 1fr; gap: 1.5rem 2.5rem; align-items: start; }
.actus-liste.detaillee .actu:not(:has(.actu-visuel)) { grid-template-columns: 1fr; }
.actu-visuel { overflow: hidden; border-radius: var(--rayon); aspect-ratio: 4 / 3; background: var(--suie); }
.actu-visuel img { width: 100%; height: 100%; object-fit: cover; }
.actu-corps .prose { margin-top: .8rem; }
.actu-corps .lien-fleche { margin-top: .8rem; }
.actu-type time { margin-left: .6rem; }
.pastille-exposition { border-color: var(--rouge); }
@media (max-width: 700px) { .actus-liste.detaillee .actu { grid-template-columns: 1fr; } }

/* ── Têtes de page, fil d'Ariane ───────────────────────────────────── */
.tete-page {
  width: min(100% - 2 * var(--marge), var(--largeur)); margin: clamp(3rem, 7vw, 6rem) auto 0;
}
.tete-page /* Le chapeau descend aussi : il suivait les titres et finissait par
   concurrencer le texte courant. */
.chapeau { margin-top: 1.2rem; }
.tete-page.etroite { width: min(100% - 2 * var(--marge), var(--etroit)); }
.tete-page.message { min-height: 50vh; display: grid; align-content: center; text-align: center; justify-items: center; }
.tete-page.message .surtitre::before { display: none; }
.tete-page.message .actions { margin-top: 2rem; justify-content: center; }
.fil {
  display: flex; flex-wrap: wrap; gap: .5rem; align-items: center;
  width: min(100% - 2 * var(--marge), var(--largeur)); margin: 1.4rem auto 0;
}
.fil a:hover { color: var(--rouge-clair); }
.fil [aria-current] { color: var(--os-2); }

/* ── Œuvres : filtres ──────────────────────────────────────────────── */
.filtres {
  display: flex; flex-wrap: wrap; gap: .5rem;
  width: min(100% - 2 * var(--marge), var(--largeur)); margin: 2.2rem auto 0;
}
.filtre {
  padding: .5rem 1rem; border: 1px solid var(--trait); border-radius: 999px;
  font-family: var(--display); font-size: 1.05rem; color: var(--os-2);
  transition: border-color var(--t-court), color var(--t-court), background var(--t-court);
}
.filtre .mono { margin-left: .4rem; font-size: .68rem; color: var(--cendre); }
.filtre:hover { border-color: var(--rouge); color: var(--os); }
.filtre.est-actif { background: var(--rouge); border-color: var(--rouge); color: var(--os); }
.filtre.est-actif .mono { color: rgba(0, 4, 8, .7); }
.page-oeuvres .grille-oeuvres { width: min(100% - 2 * var(--marge), var(--largeur)); margin: 2.5rem auto 0; }
.page-oeuvres .grille-vide, .page-boutique .grille-vide, .page-actualites .grille-vide { width: min(100% - 2 * var(--marge), var(--largeur)); margin: 2.5rem auto; }
.carte.est-filtree { display: none; }

/* ── Une œuvre ─────────────────────────────────────────────────────── */
.oeuvre {
  display: grid; grid-template-columns: 3fr 2fr; gap: clamp(2rem, 5vw, 5rem); align-items: start;
  width: min(100% - 2 * var(--marge), var(--largeur)); margin: 1.5rem auto 0;
}
/* LE CADRE A UNE HAUTEUR FIXE, ET C'EST LA CORRECTION D'UN VRAI DÉFAUT.
   Sans elle, échanger une photo en largeur contre une photo en hauteur
   changeait la hauteur de la page de plusieurs centaines de pixels : le
   navigateur gardait la position de défilement, et le visiteur se
   retrouvait en bas de page après avoir cliqué une vignette. Rien ne le
   signalait — il croyait avoir cliqué sur autre chose.
   La photo se pose maintenant DANS le cadre (`contain`), quelle que
   soit son orientation : rien ne bouge, rien n'est rogné. */
.oeuvre-principale {
  position: relative; border-radius: var(--rayon); background: var(--noir);
  aspect-ratio: 4 / 3;
  /* PAS D'OVERFLOW CACHÉ PENDANT LA CASSE : les morceaux sortent du
     cadre et y reviennent. Découpés au bord, ils disparaissaient — on
     voyait une image qui se vide au lieu d'une pièce qui se brise.
     Le débordement est rétabli une fois la casse finie, pour que le
     zoom au survol reste contenu. */
  overflow: hidden;
}
.oeuvre-principale.est-brise { overflow: visible; z-index: 5; }
.oeuvre-principale img, .oeuvre-principale picture {
  width: 100%; height: 100%; object-fit: contain;
}
@media (max-width: 700px) { .oeuvre-principale { aspect-ratio: 1; } }
.agrandir { display: block; position: relative; cursor: zoom-in; }
.agrandir img { width: 100%; height: auto; transition: transform 1.6s var(--courbe); }
.agrandir:hover img { transform: scale(1.02); }
.vignettes { display: flex; flex-wrap: wrap; gap: .6rem; margin-top: .8rem; }
.vignette {
  position: relative;   /* les fissures et le voile s'y accrochent */
  display: block; width: 5.6rem; aspect-ratio: 1; overflow: hidden; border-radius: var(--rayon);
  border: 0; opacity: .72; transition: opacity var(--t-court);
}
.vignette:hover, .vignette.est-active { opacity: 1; }
.vignette img { width: 100%; height: 100%; object-fit: cover; }

.oeuvre-fiche { position: sticky; top: calc(var(--entete) + 1.5rem); }
.oeuvre-tete .titre-page { font-size: clamp(2.2rem, 1.2rem + 3vw, 3.8rem); }
.fiche { display: grid; gap: .55rem; margin: 1.8rem 0; padding: 1.2rem 0; border-top: 1px solid var(--trait); border-bottom: 1px solid var(--trait); }
.fiche div { display: grid; grid-template-columns: 8rem 1fr; gap: 1rem; align-items: baseline; }
.fiche dt { font-family: var(--mono); font-size: .72rem; letter-spacing: .1em; text-transform: uppercase; color: var(--cendre); }
.fiche dd { margin: 0; color: var(--os); }
.prix { font-family: var(--display); font-size: 1.5rem; color: var(--os); }
.demande { margin-top: 1.8rem; }
/* La porte des archives : présente, mais plus basse que celle d'une
   pièce en vente — on propose, on ne réclame pas. */
.demande-archive {
  border-top: 1px solid var(--trait);
  padding-top: 1.4rem;
  display: grid;
  gap: .6rem;
  justify-items: start;
}
.demande-archive .indice { max-width: 46ch; }

/* Le repli du formulaire. Il s'ouvre : sans transition, un bloc de six
   champs qui apparaît d'un coup se lit comme une erreur de la page. */
.champs-repli {
  border: 1px solid var(--trait);
  border-radius: 2px;
  padding: 1.2rem 1.2rem 1.4rem;
  margin: 0 0 1.2rem;
  display: grid;
  gap: 1rem;
  animation: repli-ouvre .32s ease both;
}
.champs-repli > legend { padding: 0 .5rem; }
.champs-repli .indice { margin: 0; }
@keyframes repli-ouvre { from { opacity: 0; transform: translateY(-.4rem); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .champs-repli { animation: none; } }
.indice { margin-top: .7rem; font-size: .85rem; color: var(--cendre); max-width: 30rem; }
.voisines {
  display: grid; grid-template-columns: 1fr auto 1fr; gap: 1rem; align-items: stretch;
  width: min(100% - 2 * var(--marge), var(--largeur)); margin: clamp(3rem, 6vw, 5rem) auto 0;
  border-top: 1px solid var(--trait); border-bottom: 1px solid var(--trait);
}
.voisine { display: grid; gap: .3rem; align-content: center; padding: 1.2rem 0; }
.voisine.suiv { text-align: right; }
.voisine.tout { align-self: center; padding: 1.2rem 1.5rem; border-left: 1px solid var(--trait); border-right: 1px solid var(--trait); }
.voisine-titre { font-family: var(--display); font-size: 1.4rem; font-weight: 500; color: var(--os); transition: color var(--t-court); }
.voisine:hover .voisine-titre { color: var(--rouge-clair); }
@media (max-width: 900px) {
  .oeuvre { grid-template-columns: 1fr; }
  .oeuvre-fiche { position: static; }
  .voisines { grid-template-columns: 1fr 1fr; }
  .voisine.tout { grid-column: 1 / -1; order: 3; border: 0; border-top: 1px solid var(--trait); text-align: center; }
}

/* ── Une période ───────────────────────────────────────────────────── */
.periode-tete {
  display: grid; grid-template-columns: 3fr 2fr; gap: clamp(2rem, 5vw, 5rem); align-items: center;
  width: min(100% - 2 * var(--marge), var(--largeur)); margin: 2rem auto 0;
}
.periode-texte .prose { margin-top: 1.5rem; }
.periode-visuel { position: relative; overflow: hidden; border-radius: var(--rayon); aspect-ratio: 4 / 5; background: var(--suie); max-width: 28rem; justify-self: end; }
.periode-visuel img { width: 100%; height: 100%; object-fit: cover; }
@media (max-width: 800px) { .periode-tete { grid-template-columns: 1fr; } .periode-visuel { justify-self: start; } }

/* ── L'Arche ───────────────────────────────────────────────────────── */
.arche-heros {
  position: relative; min-height: 60vh; min-height: 60svh; display: grid; align-items: end; overflow: hidden;
  margin-top: -1px;
}
.arche-fond { position: absolute; inset: 0; z-index: 0; }
.arche-fond img { width: 100%; height: 100%; object-fit: cover; opacity: .55; filter: saturate(.8) contrast(1.1); }
.arche-fond::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(0, 4, 8, .2), rgba(0, 4, 8, .55) 55%, var(--noir));
}
.arche-heros-texte {
  position: relative; z-index: 1; width: min(100% - 2 * var(--marge), var(--largeur)); margin: 0 auto; padding: 4rem 0 3rem;
}
.arche-manifeste { width: min(100% - 2 * var(--marge), 56rem); margin: clamp(2rem, 5vw, 4rem) auto 0; }
.arche-manifeste .prose { max-width: none; }
.arche-cycle { width: min(100% - 2 * var(--marge), var(--largeur)); margin: var(--respiration) auto 0; }
.cycle { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; border-top: 1px solid var(--trait); border-bottom: 1px solid var(--trait); }
.cycle-etape {
  display: grid; gap: .6rem; padding: 2rem 1rem; text-align: center; position: relative;
  transition: background var(--t-long);
}
.cycle-etape + .cycle-etape { border-left: 1px solid var(--trait); }
.cycle-num { color: var(--cendre); transition: color var(--t-moyen); }
.cycle-mot {
  font-family: var(--display); font-weight: 500; font-size: clamp(1.8rem, 1rem + 3.4vw, 4rem); line-height: 1;
  color: var(--cendre-2); transition: color var(--t-long), transform var(--t-long) var(--courbe), text-shadow var(--t-long);
}
.cycle-etape.est-actif .cycle-num { color: var(--rouge); }
.cycle-etape.est-actif .cycle-mot { color: var(--os); transform: scale(1.06); text-shadow: 0 0 30px var(--rouge-ombre); }
.cycle-etape:nth-child(2).est-actif .cycle-mot { color: var(--rouge); text-shadow: 0 0 30px var(--rouge-ombre); animation: secousse .5s var(--courbe); }
.cycle-etape:nth-child(3).est-actif .cycle-mot { color: var(--os); text-shadow: 0 0 30px rgba(255, 240, 212, .35); }
@keyframes secousse { 0%, 100% { transform: scale(1.06); } 25% { transform: scale(1.06) translateX(-3px) rotate(-.6deg); } 75% { transform: scale(1.06) translateX(3px) rotate(.6deg); } }
.arche-chapitres {
  display: grid; grid-template-columns: 1fr 1fr; gap: clamp(2rem, 5vw, 5rem);
  width: min(100% - 2 * var(--marge), var(--largeur)); margin: var(--respiration) auto 0;
}
.chapitre .titre-section { margin-bottom: 1.2rem; }
.chapitre.destruction .surtitre { color: var(--rouge); }
.chapitre.destruction .surtitre::before { background: var(--rouge); }
.a-venir { padding: 1.2rem; border: 1px dashed var(--trait-rouge); color: var(--os-2); }
@media (max-width: 800px) { .arche-chapitres { grid-template-columns: 1fr; } .cycle { grid-template-columns: 1fr; } .cycle-etape + .cycle-etape { border-left: 0; border-top: 1px solid var(--trait); } }

/* ── L'artiste ─────────────────────────────────────────────────────── */
.artiste-tete {
  display: grid; grid-template-columns: 2fr 3fr; gap: clamp(2rem, 5vw, 5rem); align-items: center;
  width: min(100% - 2 * var(--marge), var(--largeur)); margin: clamp(2rem, 5vw, 4rem) auto 0;
}
.artiste-portrait { position: relative; overflow: hidden; border-radius: var(--rayon); aspect-ratio: 4 / 5; background: var(--suie); }
.artiste-portrait img { width: 100%; height: 100%; object-fit: cover; filter: grayscale(.25) contrast(1.05); transition: filter var(--t-long); }
.artiste-portrait:hover img { filter: none; }
.artiste-presentation .prose { margin-top: 1.5rem; }
.artiste-bio { width: min(100% - 2 * var(--marge), var(--largeur)); margin: var(--respiration) auto 0; }
.artiste-bio .titre-section { margin-bottom: 1.5rem; }
.pourquoi {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(1.5rem, 3vw, 3rem);
  width: min(100% - 2 * var(--marge), var(--largeur)); margin: var(--respiration) auto 0;
}
.pourquoi-bloc { padding: 1.8rem; background: var(--suie); border: 1px solid var(--trait); border-radius: var(--rayon); }
.pourquoi-bloc .titre-section { font-size: clamp(1.5rem, 1.1rem + 1.2vw, 2rem); margin-bottom: 1rem; }
.pourquoi-bloc .prose { font-size: .96rem; }
@media (max-width: 900px) { .artiste-tete { grid-template-columns: 1fr; } .artiste-portrait { max-width: 26rem; } .pourquoi { grid-template-columns: 1fr; } }

/* ── Le jeu ────────────────────────────────────────────────────────── */
.jeu { width: min(100% - 2 * var(--marge), var(--largeur)); margin: 2.5rem auto 0; }
.jeu-barre { display: flex; justify-content: space-between; align-items: center; gap: 1rem; flex-wrap: wrap; margin-bottom: 1rem; }
.jeu-compte { color: var(--os-2); }
.jeu-compte [data-compte] { color: var(--rouge-clair); font-size: 1.1rem; }
.jeu-scene { display: grid; grid-template-columns: minmax(0, 1fr) minmax(10rem, 15rem); gap: 2.5rem; align-items: start; }
/* Les éclats sortent du plateau : l'aire leur laisse la marge, et elle
   seule découpe — sinon un morceau lancé loin déborde de la page. */
.jeu-aire {
  position: relative;
  /* La marge est là pour que les éclats aient où tomber. `clip` plutôt
     que `hidden` : il coupe sans créer de conteneur de défilement, donc
     sans barre qui apparaît quand un morceau dépasse. */
  padding: clamp(2rem, 6vw, 4.5rem) clamp(1rem, 4vw, 3rem);
  overflow: clip;
}

/* ── LE PLATEAU N'EST PLUS UNE GRILLE ────────────────────────────────
   Les éclats sont éparpillés AUTOUR du creux, pas rangés dedans. Le
   plateau doit donc être plus grand que le visage : on lui donne une
   marge par le padding, et le creux occupe le carré central. */
.jeu-plateau {
  position: relative; aspect-ratio: 1; margin: 0 auto;
  /* Le plateau doit TENIR DANS L'ÉCRAN, hauteur comprise : les éclats
     sortent de son cadre, et un plateau plus haut que la fenêtre envoie
     la moitié des morceaux hors de vue. */
  width: 100%; max-width: min(560px, 58vh);
  touch-action: none; user-select: none; -webkit-user-select: none;
  overflow: visible;
  isolation: isolate;
}

/* Le creux : le visage éteint, là où les morceaux doivent revenir.
   Presque noir, juste assez visible pour qu'on sache viser. */
/* LE CREUX NE MONTRE PLUS LE VISAGE — il ne montre que sa PLACE.
   Avec le visage en filigrane, on posait chaque éclat par superposition
   sans jamais regarder ce qu'on tenait : le jeu se résolvait à l'œil,
   et il n'y avait rien à reconstruire.
   Il reste un cadre en pointillés et le halo rouge : on sait toujours
   OÙ viser — sans cette limite le jeu deviendrait injuste — mais plus
   ce qui va où. C'est la mémoire du joueur qui travaille, comme celle
   de quelqu'un qui recolle une pièce cassée. */
.jeu-creux {
  position: absolute; inset: 0; pointer-events: none;
  outline: 1px dashed rgba(255, 240, 212, .2);
  outline-offset: 6px;
  /* Un fond à peine plus clair que la page : la zone se devine, elle ne
     se lit pas. */
  background: radial-gradient(closest-side, rgba(255, 240, 212, .035), transparent 78%);
  opacity: calc(1 - var(--vu, 0) * .8);
  transition: opacity var(--t-moyen) ease;
}
.jeu-plateau::after {
  /* Un halo rouge sous le creux : la place vide appelle. */
  content: ""; position: absolute; inset: -14%; z-index: -1; pointer-events: none;
  background: radial-gradient(circle, rgba(214, 0, 28, .16), transparent 62%);
  filter: blur(48px);
  opacity: calc(1 - var(--vu, 0) * .5);
}

/* Les fissures : elles se révèlent au fil des éclats reposés. À la fin,
   elles restent — on ne recolle pas à neuf. */
.jeu-fissures {
  position: absolute; inset: 0; width: 100%; height: 100%;
  pointer-events: none; z-index: 3;
  color: rgba(255, 240, 212, .34);
  opacity: var(--vu, 0);
  transition: opacity var(--t-moyen) ease;
}

/* UN ÉCLAT couvre tout le plateau et n'en montre qu'un fragment, taillé
   au clip-path. C'est ce qui permet à l'image de rester continue d'un
   morceau à l'autre : chacun porte l'image entière, découpée. */
/* L'ÉCLAT EST FAIT DE DEUX ÉLÉMENTS, et ce n'est pas une coquetterie :
   un filtre CSS s'applique AVANT le découpage. Le drop-shadow posé sur
   l'élément découpé était donc calculé sur son rectangle entier — grand
   comme le plateau — et chaque morceau traînait derrière lui une plaque
   noire. Le bouton porte le filtre, l'enfant porte la forme.

   Le bouton ne reçoit pas la souris (`pointer-events: none`) : c'est la
   FORME qui la reçoit, sinon un rectangle invisible de la taille du
   plateau intercepterait tous les clics de la page. */
.jeu-eclat {
  position: absolute; inset: 0; padding: 0; border: 0; border-radius: 0; background: none;
  transform: translate(var(--dx, 0), var(--dy, 0)) rotate(var(--rot, 0deg));
  transform-origin: 50% 50%;
  cursor: grab; touch-action: none;
  pointer-events: none;
  transition: transform .42s var(--courbe), filter var(--t-court);
}
.jeu-eclat-forme {
  position: absolute; inset: 0; display: block;
  background-image: var(--image); background-size: 100% 100%; background-repeat: no-repeat;
  pointer-events: auto;
  /* Même raison que le héros : le noir de l'illustration laisse passer
     le fond au lieu de poser une plaque. Les éclats sont donc de vrais
     morceaux de visage, sans le rectangle qui les portait. */
  mix-blend-mode: lighten;
}
.jeu-eclat.est-pose .jeu-eclat-forme { pointer-events: none; }
.jeu-eclat:not(.est-pose) {
  /* Le liseré clair rend visible un morceau sombre sur une page noire.
     Deux ombres empilées : un contour net, puis une ombre douce. Un
     `border` ne suivrait pas le découpage — l'ombre portée, si, parce
     qu'elle est calculée sur l'enfant déjà découpé. */
  filter: drop-shadow(0 0 1px rgba(255, 240, 212, .55)) drop-shadow(0 16px 26px rgba(0, 0, 0, .85));
}
.jeu-eclat:hover:not(.est-pose) { filter: drop-shadow(0 0 2px var(--os)) drop-shadow(0 16px 26px rgba(0, 0, 0, .85)) brightness(1.14); }
.jeu-eclat:focus-visible { outline: none; filter: drop-shadow(0 0 0 2px var(--rouge-clair)) drop-shadow(0 14px 22px rgba(0, 0, 0, .75)); }
/* Pendant qu'on le tient : aucune transition, sinon le morceau traîne
   derrière le doigt. */
.jeu-eclat.est-tenu { cursor: grabbing; transition: none; z-index: 6; filter: drop-shadow(0 26px 34px rgba(0, 0, 0, .85)) brightness(1.18); }
/* Assez près pour être aspiré : le morceau le dit avant qu'on lâche. */
.jeu-eclat.est-proche { filter: drop-shadow(0 0 14px var(--rouge)) brightness(1.25); }
.jeu-eclat.est-pose {
  cursor: default; z-index: 1;
  filter: none;
  animation: eclat-choc .5s var(--courbe);
}
@keyframes eclat-choc {
  0% { filter: brightness(2.1); }
  40% { filter: brightness(1.3); }
  100% { filter: none; }
}
.jeu-plateau.est-fini .jeu-eclat { pointer-events: none; }
.jeu-plateau.est-fini { animation: jeu-entier 1.2s var(--courbe); }
@keyframes jeu-entier {
  0% { transform: scale(1); }
  30% { transform: scale(1.035); }
  100% { transform: scale(1); }
}

@media (max-width: 900px) {
  .jeu-scene { grid-template-columns: 1fr; }
  .jeu-modele { display: grid; grid-template-columns: 6rem 1fr; gap: 1rem; align-items: center; }
  /* Sur téléphone, les éclats sortent moins loin : ils doivent rester
     dans l'écran, sinon on attrape un morceau qu'on ne voit pas. */
  .jeu-plateau { max-width: min(100%, 30rem); }
}
@media (prefers-reduced-motion: reduce) {
  .jeu-eclat { transition: none; animation: none; }
  .jeu-plateau.est-fini { animation: none; }
}
.jeu-modele { display: grid; gap: .8rem; align-content: start; justify-items: start; }
.jeu-modele-boite { display: grid; gap: .6rem; animation: surgir var(--t-moyen) var(--courbe) both; }
.jeu-modele img { width: 100%; opacity: .75; border-radius: var(--rayon); border: 1px solid var(--trait); }
.jeu-aide { margin-top: 1rem; }
.surprise { margin-top: 2.5rem; padding: clamp(1.5rem, 4vw, 3rem); border: 1px solid var(--rouge); border-radius: var(--rayon); background: var(--suie); animation: surgir var(--t-long) var(--courbe) both; }
@keyframes surgir { from { opacity: 0; transform: translateY(24px); } to { opacity: 1; transform: none; } }
.surprise .titre-section { margin-bottom: 1rem; }
.surprise-visuel { margin-top: 1.5rem; max-width: 40rem; }
.surprise-visuel img { border-radius: var(--rayon); }
.surprise-video { margin-top: 1.5rem; max-width: 50rem; aspect-ratio: 16 / 9; }
.surprise-video iframe, .surprise-video video { width: 100%; height: 100%; border: 0; border-radius: var(--rayon); }
.surprise-code { margin-top: 1.5rem; padding: 1rem 1.2rem; border: 1px dashed var(--rouge); color: var(--rouge-clair); font-size: 1.1rem; letter-spacing: .2em; display: inline-block; }
.surprise .actions { margin-top: 1.8rem; }
.jeu-plateau.est-fini::after {
  content: ""; position: absolute; left: 68.59%; top: 43.57%; width: 9%; aspect-ratio: 1; transform: translate(-50%, -50%);
  border-radius: 50%; mix-blend-mode: screen; pointer-events: none;
  background: radial-gradient(circle, rgba(255, 90, 70, .95) 0%, rgba(224, 38, 28, .7) 28%, rgba(224, 38, 28, 0) 70%);
  animation: oeil 3.2s ease-in-out infinite;
}
@media (max-width: 800px) { .jeu-scene { grid-template-columns: 1fr; } .jeu-modele { display: flex; gap: 1rem; align-items: center; } .jeu-modele img { width: 6rem; } }

/* ── La boutique ───────────────────────────────────────────────────── */
.page-boutique .boutique-grille { width: min(100% - 2 * var(--marge), var(--largeur)); margin: 2.5rem auto 0; }

/* ── Contact ───────────────────────────────────────────────────────── */
.contact {
  display: grid; grid-template-columns: 3fr 2fr; gap: clamp(2rem, 6vw, 6rem); align-items: start;
  width: min(100% - 2 * var(--marge), var(--largeur)); margin: 2.5rem auto 0;
}
.formulaire { display: grid; gap: 1.2rem; }
.champs.deux { display: grid; grid-template-columns: 1fr 1fr; gap: 1.2rem; }
.champ { display: grid; gap: .4rem; }
.champ label { font-family: var(--mono); font-size: .72rem; letter-spacing: .1em; text-transform: uppercase; color: var(--cendre); }
.champ input, .champ select, .champ textarea {
  width: 100%; padding: .8rem .9rem; font: inherit; font-weight: 300; color: var(--os);
  background: var(--suie); border: 1px solid var(--trait); border-radius: var(--rayon);
  transition: border-color var(--t-court), box-shadow var(--t-court);
}
.champ select { appearance: none; -webkit-appearance: none; background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='8'><path d='M1 1l5 5 5-5' fill='none' stroke='%23d6001c' stroke-width='1.5'/></svg>"); background-repeat: no-repeat; background-position: right .9rem center; padding-right: 2.4rem; }
.champ select option { background: var(--suie-2); }
.champ input:focus, .champ select:focus, .champ textarea:focus { outline: none; border-color: var(--rouge); box-shadow: 0 0 0 3px var(--rouge-ombre); }
.champ textarea { resize: vertical; min-height: 9rem; }
.champ.case label { display: flex; gap: .7rem; align-items: flex-start; font-family: var(--texte); font-size: .9rem; letter-spacing: 0; text-transform: none; color: var(--os-2); }
.champ.case input { width: 1.1rem; height: 1.1rem; margin-top: .2rem; accent-color: var(--rouge); }
.piege { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
.formulaire-erreur { padding: 1rem 1.2rem; border: 1px solid var(--rouge); color: var(--os); background: rgba(214, 0, 28, .1); }
.contact-direct { display: grid; gap: .6rem; padding: 1.8rem; background: var(--suie); border: 1px solid var(--trait); border-radius: var(--rayon); }
.contact-direct .surtitre { margin: 0 0 .4rem; }
.contact-direct .surtitre + .surtitre, .contact-direct .contact-ligne + .surtitre { margin-top: 1.2rem; }
.contact-ligne { font-family: var(--display); font-size: 1.35rem; }
.contact-ligne a:hover { color: var(--rouge-clair); }
.contact-reseaux { display: flex; flex-wrap: wrap; gap: 1rem; }
.contact-reseaux a { font-family: var(--mono); font-size: .78rem; letter-spacing: .1em; text-transform: uppercase; border-bottom: 1px solid var(--trait-rouge); }
@media (max-width: 900px) { .contact { grid-template-columns: 1fr; } }
@media (max-width: 560px) { .champs.deux { grid-template-columns: 1fr; } }

/* ── Mentions ──────────────────────────────────────────────────────── */
.mentions { width: min(100% - 2 * var(--marge), var(--etroit)); margin: 2.5rem auto 0; }
.mentions .titre-section { font-size: 1.6rem; margin: 2.2rem 0 .8rem; }

/* ── Pied ──────────────────────────────────────────────────────────── */
.pied { margin-top: clamp(4.5rem, 8vw, 7rem); border-top: 1px solid var(--trait); background: var(--suie); }
.pied-haut {
  display: grid; grid-template-columns: 1.4fr 1fr 1fr; gap: 2rem;
  width: min(100% - 2 * var(--marge), var(--largeur)); margin: 0 auto; padding: 3rem 0;
}
.pied-marque { display: flex; gap: 1rem; align-items: center; }
.pied-marque p { color: var(--os-2); }
.pied-marque strong { font-family: var(--marque-police); font-size: 1.45rem; font-weight: 400; letter-spacing: .03em; line-height: 1; color: var(--os); }
.pied-marque p { font-size: .9rem; font-weight: 300; }
.pied-nav, .pied-reseaux { display: grid; gap: .62rem; align-content: start; }
.pied-nav a, .pied-reseaux a {
  font-family: var(--mono); font-size: .7rem; letter-spacing: .2em; text-transform: uppercase;
  color: var(--os-3); transition: color var(--t-court) ease;
}
.pied-nav a:hover, .pied-reseaux a:hover { color: var(--os); }
.pied-bas {
  width: min(100% - 2 * var(--marge), var(--largeur)); margin: 0 auto; padding: 1.2rem 0 2rem;
  display: flex; flex-wrap: wrap; justify-content: space-between; gap: .5rem 2rem;
  border-top: 1px solid var(--trait); font-size: .82rem; color: var(--cendre);
}
.pied-bas { align-items: baseline; }
.pied-droits { font-size: .82rem; font-weight: 300; color: var(--cendre); max-width: 52ch; }
.pied-liens { display: flex; flex-wrap: wrap; gap: .7rem; color: var(--cendre); }
.pied-bas a { color: var(--os-3); transition: color var(--t-court) ease; }
.pied-bas a:hover { color: var(--os); }
/* La signature de l'agence : discrète, mais jamais illisible. Un crédit
   qu'on ne peut pas lire ne sert personne — ni le client, qui a l'air
   d'avoir quelque chose à cacher, ni l'agence, qui a payé la ligne. */
.pied-ekho { color: var(--cendre); letter-spacing: .16em; }
.pied-ekho a {
  color: var(--os-2); font-weight: 700; letter-spacing: .2em;
  border-bottom: 1px solid var(--trait-rouge); padding-bottom: 1px;
}
.pied-ekho a:hover { color: var(--rouge-clair); border-color: var(--rouge); }
@media (max-width: 760px) { .pied-haut { grid-template-columns: 1fr; } }

/* ── La lumière (visionneuse) ──────────────────────────────────────── */
/* LA VISIONNEUSE — le cadre ne bouge plus d'une photo à l'autre.
   Il était en `align-items: center` sur une grille dont la hauteur
   suivait celle de l'image : passer d'une photo en largeur à une photo
   en hauteur déplaçait tout, et le compteur comme les flèches sautaient
   d'un bord à l'autre. Le cadre occupe désormais TOUTE la fenêtre, et
   c'est la photo qui se centre dedans. */
.lumiere {
  position: fixed; inset: 0; z-index: 100;
  display: grid; grid-template-columns: 4.5rem 1fr 4.5rem; grid-template-rows: 1fr;
  align-items: stretch;
  background: rgba(0, 0, 0, .97); animation: fondu var(--t-court) both;
}
@keyframes fondu { from { opacity: 0; } to { opacity: 1; } }
.lumiere.se-ferme { opacity: 0; transition: opacity var(--t-court); animation: none; }
/* Le corps seul est bloqué — pas <html>. Bloquer les deux fait perdre
   la position de défilement au navigateur, qui la ramène à zéro. */
body.lumiere-ouverte { overflow: hidden; overscroll-behavior: contain; }
.lumiere-cadre {
  grid-column: 2; grid-row: 1;
  display: grid; grid-template-rows: 1fr auto; justify-items: center; align-content: center;
  gap: 1.1rem; padding: 3.5rem 0 2rem; min-height: 0;
}
.lumiere-cadre img {
  max-width: 100%; max-height: 100%; width: auto; height: auto;
  object-fit: contain; align-self: center;
  box-shadow: 0 40px 80px rgba(0, 0, 0, .8);
  animation: surgir var(--t-moyen) var(--courbe) both;
}
/* Le compteur : savoir où l'on en est dans une série de neuf photos. */
.lumiere-compte { font-family: var(--mono); font-size: .7rem; letter-spacing: .2em; color: var(--cendre); }
.lumiere-cadre figcaption { display: grid; gap: .4rem; justify-items: center; font-family: var(--mono); font-size: .74rem; letter-spacing: .1em; text-transform: uppercase; color: var(--cendre); text-align: center; max-width: 60ch; }
.lumiere-fermer { position: absolute; top: 1rem; right: 1.2rem; font-size: 2.4rem; line-height: 1; color: var(--os-2); width: 3rem; height: 3rem; }
.lumiere-fermer:hover, .lumiere-nav:hover { color: var(--rouge-clair); }
.lumiere-nav { font-size: 2rem; height: 100%; color: var(--os-2); grid-row: 1; }
.lumiere-aide { position: absolute; left: 0; right: 0; bottom: .8rem; text-align: center; color: var(--cendre-2); }
.lumiere-prec { grid-column: 1; } .lumiere-suiv { grid-column: 3; }
.lumiere-nav[disabled] { opacity: .2; cursor: default; }
@media (max-width: 600px) { .lumiere { grid-template-columns: 2.6rem 1fr 2.6rem; } .lumiere-nav { font-size: 1.5rem; } }

/* ── Mouvement : révélations ───────────────────────────────────────── */
/* Tout ce qui bouge n'est caché que si le script tourne (html.js) :
   sans lui, la page est simplement là, entière. */
html.js .apparait:not(.grille):not(.frise) {
  opacity: 0; transform: translateY(26px);
  transition: opacity var(--t-moyen) ease, transform var(--t-long) var(--courbe);
  transition-delay: calc(var(--i, 0) * 90ms);
}
html.js .apparait.est-visible:not(.grille):not(.frise) { opacity: 1; transform: none; }
html.js .grille.apparait > li {
  opacity: 0; transform: translateY(30px);
  transition: opacity var(--t-moyen) ease, transform var(--t-long) var(--courbe);
  transition-delay: calc(var(--i, 0) * 70ms);
}
html.js .grille.apparait.est-visible > li { opacity: 1; transform: none; }

/* AVANT : chaque lettre tombait du ciel en tournant de deux degrés.
   L'effet est reconnaissable au premier coup d'œil comme celui d'un
   gabarit — et il fait sautiller un titre qu'on essaie de lire.

   MAINTENANT : le mot monte derrière une fenêtre qui le découpe, comme
   un générique de film. Le décalage entre mots est court (40 ms) et
   s'arrête au sixième : au-delà, on n'attend plus, on patiente. */
/* LES TITRES COUPÉS SUR TÉLÉPHONE — la cause était ici.
   La fenêtre qui découpe le mot (`overflow: hidden`) ne débordait que de
   0,06 em sous la ligne, et pas du tout au-dessus. Or ces titres ont un
   interligne serré — 1,02, et 1 pour l'accroche sur téléphone — plus
   petit que la hauteur réelle des lettres : l'accent de « É » dépasse
   en haut, les jambages de « p », « g », « j » dépassent en bas. La
   fenêtre les rognait, sur chaque titre animé, dans les deux langues ;
   plus l'interligne est serré, plus ça se voit.
   La fenêtre s'agrandit maintenant des deux côtés, et une marge
   négative égale rend la place : la ligne garde exactement la même
   hauteur, seule la zone visible grandit. Le mot part donc de plus bas
   (140 %) pour rester caché au départ de l'animation. */
html.js [data-mot] .mot-bloc {
  display: inline-block; overflow: hidden; vertical-align: bottom;
  max-width: 100%;
  padding: .26em 0 .24em; margin: -.26em 0 -.24em;
}
html.js [data-mot] .mot-bloc > .mot-l {
  display: inline-block; transform: translateY(140%);
  animation: mot-monte .85s var(--courbe) forwards;
  animation-delay: calc(120ms + min(var(--i, 0), 6) * 40ms);
}
html.js [data-mot] .mot-espace { display: inline-block; width: .26em; }
@keyframes mot-monte { to { transform: none; } }

/* Les éclats : le script découpe l'image en morceaux et les laisse
   revenir à leur place. Créer, détruire, reconstruire — au chargement. */
/* LA SCÈNE A UNE PROFONDEUR. Sans `perspective`, une bascule sur X ou Y
   se projette à plat et ne produit qu'un écrasement — c'est ce qui
   trahit une fausse casse. 1300 px est la distance d'un œil qui regarde
   l'image : assez pour que le relief se voie, assez peu pour que les
   morceaux ne partent pas en fuite. */
.eclats { position: relative; perspective: 1300px; perspective-origin: 50% 45%; }
.eclats.est-brise img { opacity: 0; }
.eclats.est-fini img { opacity: 1; transition: opacity .3s; }
.eclat-piece {
  position: absolute; inset: 0; pointer-events: none;
  background-image: var(--image); background-size: 100% 100%; background-repeat: no-repeat;
  clip-path: var(--forme);
  /* L'ÉTAT BRISÉ : le morceau est en avant, basculé sur deux axes, et
     plus clair — il présente une autre face à la lumière. */
  transform:
    translate3d(var(--dx, 0), var(--dy, 0), var(--dz, 0))
    rotateX(var(--rx, 0)) rotateY(var(--ry, 0)) rotateZ(var(--r, 0));
  opacity: 0;
  filter: brightness(var(--clair, 1.3));
  /* 2,2 s, avec une courbe qui dépasse à peine sa cible avant d'y
     revenir : le morceau SE POSE au lieu de glisser. C'est le poids de
     la terre cuite, rendu par la seule courbe d'accélération.
     La lumière met plus longtemps que la position à revenir au repos :
     le morceau est déjà en place qu'il finit de s'éteindre — c'est ce
     décalage qui donne l'impression d'une matière, et pas d'un calque. */
  transition:
    transform 2.2s cubic-bezier(.17, .89, .3, 1.04),
    opacity .75s ease,
    filter 2.9s ease;
  transition-delay: var(--delai, 0ms);
  will-change: transform, filter;
  backface-visibility: hidden;
}
.eclats.est-assemble .eclat-piece {
  transform: translate3d(0, 0, 0) rotateX(0) rotateY(0) rotateZ(0);
  opacity: 1;
  filter: brightness(1);
}
/* LA CICATRICE — remise, mais SOMBRE. Elle avait été coupée parce
   qu'elle était rouge et vive : des rayons laser posés sur l'œuvre.
   Mais sans elle, une image qui se recompose n'est qu'un fondu — rien
   ne dit qu'elle a été brisée. Un trait NOIR, fin, qui s'efface en trois
   secondes, ne se voit pas ; il se ressent. C'est la différence entre
   une animation et le souvenir d'un choc. */
.eclat-trait {
  position: absolute; inset: 0; width: 100%; height: 100%;
  pointer-events: none; z-index: 3;
  color: rgba(0, 0, 0, .75);
  opacity: 0;
  transition: opacity 2.6s ease;
  transition-delay: var(--delai, 0ms);
}
.eclats.est-assemble .eclat-trait { opacity: 1; transition-duration: .5s; }
.eclats.est-fini .eclat-trait { opacity: 0; transition-duration: 3.2s; }
.eclat-trait-hors-service {
  position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; z-index: 2;
  color: var(--os); filter: drop-shadow(0 0 5px var(--rouge)) drop-shadow(0 0 1px var(--rouge));
  opacity: 0; transition: opacity 1.2s ease; transition-delay: var(--delai, 0ms);
}
.eclats.est-assemble /* LA CICATRICE — remise, mais SOMBRE. Elle avait été coupée parce
   qu'elle était rouge et vive : des rayons laser posés sur l'œuvre.
   Mais sans elle, une image qui se recompose n'est qu'un fondu — rien
   ne dit qu'elle a été brisée. Un trait NOIR, fin, qui s'efface en trois
   secondes, ne se voit pas ; il se ressent. C'est la différence entre
   une animation et le souvenir d'un choc. */
.eclat-trait {
  position: absolute; inset: 0; width: 100%; height: 100%;
  pointer-events: none; z-index: 3;
  color: rgba(0, 0, 0, .75);
  opacity: 0;
  transition: opacity 2.6s ease;
  transition-delay: var(--delai, 0ms);
}
.eclats.est-assemble .eclat-trait { opacity: 1; transition-duration: .5s; }
.eclats.est-fini .eclat-trait { opacity: 0; transition-duration: 3.2s; }
.eclat-trait-hors-service { opacity: 1; }
.eclats.est-fini /* LA CICATRICE — remise, mais SOMBRE. Elle avait été coupée parce
   qu'elle était rouge et vive : des rayons laser posés sur l'œuvre.
   Mais sans elle, une image qui se recompose n'est qu'un fondu — rien
   ne dit qu'elle a été brisée. Un trait NOIR, fin, qui s'efface en trois
   secondes, ne se voit pas ; il se ressent. C'est la différence entre
   une animation et le souvenir d'un choc. */
.eclat-trait {
  position: absolute; inset: 0; width: 100%; height: 100%;
  pointer-events: none; z-index: 3;
  color: rgba(0, 0, 0, .75);
  opacity: 0;
  transition: opacity 2.6s ease;
  transition-delay: var(--delai, 0ms);
}
.eclats.est-assemble .eclat-trait { opacity: 1; transition-duration: .5s; }
.eclats.est-fini .eclat-trait { opacity: 0; transition-duration: 3.2s; }
.eclat-trait-hors-service { opacity: 0; transition: opacity 1.8s ease 1.2s; }

/* ── Mouvement réduit ──────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: .001ms !important; animation-iteration-count: 1 !important; transition-duration: .001ms !important; }
  .heros-oeil { animation: none; opacity: .8; }
  .heros-balayage, .jeu-appel-grille { animation: none; display: none; }
  .heros-cadre { transform: none; }
  html.js .apparait, html.js .apparait:not(.grille):not(.frise), html.js .grille.apparait > li { opacity: 1; transform: none; }
  html.js [data-mot] .mot-l { opacity: 1; transform: none; animation: none; }
  .eclats.est-brise img { opacity: 1; }
  .eclat-piece, /* LA CICATRICE — remise, mais SOMBRE. Elle avait été coupée parce
   qu'elle était rouge et vive : des rayons laser posés sur l'œuvre.
   Mais sans elle, une image qui se recompose n'est qu'un fondu — rien
   ne dit qu'elle a été brisée. Un trait NOIR, fin, qui s'efface en trois
   secondes, ne se voit pas ; il se ressent. C'est la différence entre
   une animation et le souvenir d'un choc. */
.eclat-trait {
  position: absolute; inset: 0; width: 100%; height: 100%;
  pointer-events: none; z-index: 3;
  color: rgba(0, 0, 0, .75);
  opacity: 0;
  transition: opacity 2.6s ease;
  transition-delay: var(--delai, 0ms);
}
.eclats.est-assemble .eclat-trait { opacity: 1; transition-duration: .5s; }
.eclats.est-fini .eclat-trait { opacity: 0; transition-duration: 3.2s; }
.eclat-trait-hors-service { display: none; }
  .frise::before { transform: scaleX(1); }
}

/* ── Impression ────────────────────────────────────────────────────── */
@media print {
  html, body { background: #fff; color: #000; }
  .grain, .entete, .pied, .lumiere, .heros-oeil, .heros-balayage, .actions, .voisines, .filtres { display: none !important; }
  .prose, /* Le chapeau descend aussi : il suivait les titres et finissait par
   concurrencer le texte courant. */
.chapeau { color: #000; }
  a { color: #000; }
}


/* ═══════════════════════════════════════════════════════════════════════
   LE GRAIN — ajouté le 9 septembre 2026
   Un aplat de noir pur sur un écran moderne est parfaitement lisse, et
   c'est ce qui fait « page web » plutôt que « tirage ». Le grain donne
   une matière au fond, et il en donne une à l'argile photographiée
   par-dessus : les deux se répondent au lieu de se contredire.

   Il est dessiné en SVG dans la feuille — aucun fichier à charger, rien
   à téléverser, et il reste net à toutes les densités d'écran.
   ═══════════════════════════════════════════════════════════════════════ */
body::after {
  content: ""; position: fixed; inset: 0; z-index: 9999; pointer-events: none;
  opacity: .07; mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.82' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23g)'/%3E%3C/svg%3E");
}
@media (prefers-reduced-motion: reduce) { body::after { opacity: .035; } }

/* ── LE TÉLÉPHONE ET LA TABLETTE ────────────────────────────────────────
   Le site se lit d'abord sur un téléphone : un artiste se partage par
   message, pas par courriel. Ce qui suit corrige ce que les grandes
   valeurs de `clamp` laissent passer sur les petites largeurs.
   ═══════════════════════════════════════════════════════════════════════ */
@media (max-width: 900px) {
  /* Un didone perd sa lisibilité en dessous d'un certain corps : sur
     tablette, on resserre moins et on remonte la graisse. */
  h1, h2, h3, h4 { letter-spacing: -.012em; }
  .prose.colonnes { columns: 1; }
  /* 62 % sur tablette repoussait l'aperçu de L'Arche sous la ligne de
     flottaison, même en portrait sur 768 × 1024. */
  .heros-cadre { max-height: 46vh; max-height: 46svh; }
}
@media (max-width: 600px) {
  .accroche { max-width: none; letter-spacing: -.022em; line-height: 1; }
  .sous-accroche { font-size: 1rem; }
  .prose { font-size: 1.02rem; line-height: 1.7; }   /* plus grand sur petit écran, pas plus petit */
  .mono { font-size: .64rem; letter-spacing: .18em; }
  .titre-section { letter-spacing: -.015em; }
  /* Les deux boutons du héros passent l'un sous l'autre et prennent
     toute la largeur : une cible tactile se vise avec le pouce. */
  .heros-texte .actions { display: grid; gap: .7rem; }
  .heros-texte .actions .bt { justify-content: center; width: 100%; }
  /* LE VISAGE CÈDE DE LA HAUTEUR À L'APERÇU. À 56 % de l'écran, lui et
     l'accroche remplissaient déjà la première vue : les pièces de
     L'Arche arrivaient loin dessous. À 36 %, sur 360 × 640 comme sur
     414 × 896, le haut des vignettes se voit en arrivant. */
  .heros-cadre { max-height: 36vh; max-height: 36svh; }
  .heros { row-gap: 1.2rem; }
  .heros-haut .surtitre { margin-bottom: 1rem; }
}
/* Une cible tactile fait au moins 44 pixels de côté — recommandation
   d'Apple comme de la WCAG. En dessous, on rate et on s'énerve. */
@media (pointer: coarse) {
  .bt, .nav a, .pied-nav a, .lumiere-fermer, .lumiere-nav { min-height: 44px; }
  .nav a { display: flex; align-items: center; }
}

/* ── L'appel à la commande, sur la boutique ─────────────────────────── */
.appel-demande {
  display: grid; grid-template-columns: 1fr auto; align-items: end; gap: 2rem;
  border-top: 1px solid var(--trait); padding-top: clamp(2rem, 4vw, 3.5rem);
}
.appel-demande-actions { display: flex; flex-wrap: wrap; gap: .8rem; }
@media (max-width: 800px) {
  .appel-demande { grid-template-columns: 1fr; align-items: start; }
  .appel-demande-actions { display: grid; }
  .appel-demande-actions .bt { justify-content: center; }
}

/* ── « Casse-la », et la cicatrice qui reste ────────────────────────── */
.demande-boutons { display: flex; flex-wrap: wrap; gap: .8rem; margin-bottom: .9rem; }
@media (max-width: 560px) { .demande-boutons { display: grid; } .demande-boutons .bt { justify-content: center; } }
.casser { margin-top: 1.8rem; display: grid; gap: .7rem; justify-items: start; }
.bt.casse {
  border: 1px solid var(--trait-rouge); color: var(--rouge-clair);
  background: none; letter-spacing: .18em;
}
.bt.casse:hover:not([disabled]) { background: var(--rouge); color: var(--noir); border-color: var(--rouge); }
.bt.casse[disabled] { opacity: .4; cursor: wait; }
.casser-compte { max-width: 40ch; }

/* La cicatrice ne s'efface pas : même tracé que la fissure, mais il
   reste. C'est le geste de Dyson — on répare en montrant la réparation. */
.eclat-cicatrice {
  position: absolute; inset: 0; width: 100%; height: 100%;
  color: rgba(255, 240, 212, .5);
  opacity: 1; pointer-events: none;
  transition: opacity 1.2s ease;
}

/* Le curseur n'écrit plus sur la page : il éclaire la poussière du
   fond, derrière le contenu (voir assets/effets.js). Il ne reste donc
   aucune nappe par-dessus le texte — c'est tout l'objet du changement.
   `[data-fissure]` reste posé sur les grandes photos : il ne coûte rien
   et sert de repère si l'on veut y revenir. */
@media (prefers-reduced-motion: reduce) { .casser { display: none; } }

/* ── LA PHOTO D'ATELIER, PLEINE LARGEUR ─────────────────────────────── */
.atelier {
  position: relative;
  margin: clamp(3rem, 8vw, 6rem) calc(50% - 50vw);
  width: 100vw; max-width: 100vw;
  overflow: hidden;
}
.atelier img {
  width: 100%; height: clamp(22rem, 62vh, 44rem); object-fit: cover; object-position: 50% 42%;
  /* Un fondu par le bas : la photo n'a pas de bord franc, elle s'éteint
     dans la page. Sans lui, une bande grise coupe le noir en deux. */
  -webkit-mask-image: linear-gradient(to bottom, transparent, #000 12%, #000 82%, transparent);
          mask-image: linear-gradient(to bottom, transparent, #000 12%, #000 82%, transparent);
}
.atelier figcaption {
  position: absolute; left: 0; right: 0; bottom: clamp(1rem, 3vw, 2.4rem);
  width: min(100% - 2 * var(--marge), var(--largeur)); margin: 0 auto;
  color: var(--os-2); text-shadow: 0 2px 18px rgba(0, 0, 0, .9);
}
@media (max-width: 700px) { .atelier img { height: 60vh; } }

/* ── Le fond qui vit ────────────────────────────────────────────────
   Un seul canvas, fixe, DERRIÈRE tout le contenu. `pointer-events:none`
   n'est pas une précaution : sans lui, il attraperait chaque clic de la
   page. */
.fond-vivant {
  position: fixed; inset: 0; z-index: 0;
  width: 100%; height: 100%;
  pointer-events: none;
}
/* Le contenu doit passer devant : sans contexte d'empilement explicite,
   un enfant du body sans z-index se retrouverait derrière le canvas. */
/* LA VISIONNEUSE N'EST PAS DANS CETTE LISTE, ET C'EST IMPORTANT.
   Cette règle sert à faire passer le contenu devant le canvas du fond.
   Elle y avait été mise par réflexe — et `position: relative` écrasait
   son `position: fixed` : la visionneuse s'affichait alors EN BAS de la
   page, sous le pied, bouton de fermeture compris. Elle est déjà fixe
   et déjà au-dessus de tout : elle n'a besoin de rien. */
body > .entete, body > main, body > .pied, body > .grain, body > .saut { position: relative; z-index: 1; }
body > .entete { z-index: 40; }
@media (prefers-reduced-motion: reduce) { .fond-vivant { display: none; } }

/* ═══════════════════════════════════════════════════════════════════════
   LES PHOTOS D'UNE ŒUVRE — que la série de vignettes cesse d'être fade
   ───────────────────────────────────────────────────────────────────────
   Une rangée de carrés identiques présente les photos sans jamais les
   regarder. Trois gestes, tirés du même sujet — la terre cuite qui
   casse, et qu'on recolle.
   ═══════════════════════════════════════════════════════════════════════ */

/* 1 · LES VIGNETTES EN QUINCONCE, et plus grandes. Une sur deux descend
   d'un cran : la rangée cesse d'être une règle, l'œil circule. */
/* LE QUINCONCE EST RETIRÉ. Il marchait avec trois vignettes ; avec
   neuf, qui passent à la ligne, une sur deux descendait au hasard et la
   série devenait un tas. Une planche de photos se lit en rangs — c'est
   la photo qui doit être belle, pas sa grille. */
.vignettes { display: grid; grid-template-columns: repeat(auto-fill, minmax(4.8rem, 1fr)); gap: .6rem; margin-top: 1.4rem; }
.vignette { width: 100%; height: auto; aspect-ratio: 1; }

/* 2 · LA FISSURE QUI S'OUVRE AU SURVOL. Un trait clair court sur deux
   bords, comme une craquelure — puis fait le tour. */
.vignette::before {
  content: ""; position: absolute; inset: 0; z-index: 2; pointer-events: none;
  border: 1px solid transparent;
  clip-path: polygon(0 0, 100% 0, 100% 10%, 14% 10%, 14% 100%, 0 100%);
  transition: border-color var(--t-moyen) ease, clip-path .9s var(--courbe);
}
.vignette:hover::before, .vignette.est-active::before {
  border-color: rgba(255, 240, 212, .5);
  clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%);
}

/* 3 · LA PHOTO RESPIRE, et un voile se lève. */
.vignette img { transition: transform 1.6s var(--courbe), filter var(--t-long) ease; }
.vignette::after {
  content: ""; position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: linear-gradient(to top, rgba(0, 0, 0, .5), transparent 55%);
  transition: opacity var(--t-long) ease;
}
.vignette:hover img { transform: scale(1.08); filter: contrast(1.06) saturate(1.06); }
.vignette:hover::after, .vignette.est-active::after { opacity: .25; }

/* LA PHOTO PRINCIPALE : un cadre qui ne se referme jamais tout à fait,
   et un grossissement très lent au survol. Elle porte déjà la fissure
   au curseur — les deux racontent la même chose. */
.oeuvre-principale { position: relative; overflow: hidden; }
.oeuvre-principale img { transition: transform 2.4s var(--courbe); }
.oeuvre-principale:hover img { transform: scale(1.025); }
.oeuvre-principale::after {
  content: ""; position: absolute; inset: 0; pointer-events: none; z-index: 2;
  border: 1px solid rgba(255, 240, 212, .1);
  clip-path: polygon(0 0, 62% 0, 62% 2px, 100% 2px, 100% 100%, 38% 100%, 38% calc(100% - 2px), 0 calc(100% - 2px));
}

@media (pointer: coarse) { .vignette::after { opacity: .45; } }
@media (max-width: 600px) { .vignettes > li:nth-child(even) { transform: none; } .vignette { width: 4.4rem; height: 4.4rem; } }
@media (prefers-reduced-motion: reduce) {
  .vignettes > li:nth-child(even) { transform: none; }
  .vignette img, .vignette::before, .vignette::after, .oeuvre-principale img { transition: none; }
}

/* ── LA VISIONNEUSE SUR TÉLÉPHONE ───────────────────────────────────
   Trois défauts, trois causes :
   · les flèches prenaient 4,5 rem de chaque côté — sur 390 pixels, il
     ne restait presque rien pour la photo, d'où l'impression qu'elle
     était tronquée. Elles passent SOUS l'image, en barre ;
   · une image en `max-height: 100 %` dans une rangée de grille ne se
     limite à rien tant que la rangée n'a pas `min-height: 0` : la photo
     débordait par le bas, et la page derrière se mettait à défiler ;
   · le corps continuait de défiler sous la visionneuse ouverte, ce qui
     donnait le « la souris descend » — on rouvrait la page ailleurs
     qu'où on l'avait quittée. `position: fixed` sur le corps le fige
     pour de bon, là où `overflow: hidden` ne suffit pas sur iOS. */
@media (max-width: 720px) {
  .lumiere {
    grid-template-columns: 1fr;
    grid-template-rows: minmax(0, 1fr) auto;
    padding: 3.4rem .8rem 1rem;
  }
  .lumiere-cadre { grid-column: 1; grid-row: 1; padding: 0; min-height: 0; }
  .lumiere-cadre img { max-height: 100%; }
  .lumiere-nav {
    grid-row: 2; height: auto; padding: .9rem 2.2rem; font-size: 1.6rem;
    border: 1px solid var(--trait); border-radius: var(--rayon);
  }
  .lumiere-prec { grid-column: 1; justify-self: start; }
  .lumiere-suiv { grid-column: 1; justify-self: end; }
  .lumiere-fermer { top: .6rem; right: .6rem; }
  .lumiere-aide { display: none; }   /* pas de clavier sur un téléphone */
}

/* ═══════════════════════════════════════════════════════════════════════
   LE CADRE QUI SE DESSINE — habillage des vignettes du catalogue
   ───────────────────────────────────────────────────────────────────────
   Une grille de photos posées sur du noir, sans rien autour, se lit
   comme un dossier de fichiers. Il fallait un cadre — mais un filet
   permanent alourdit soixante vignettes d'un coup.

   Celui-ci se DESSINE : quatre équerres aux coins, tracées en deux
   temps quand la carte entre dans le champ, puis un filet qui les
   relie au survol. C'est le geste d'un cadre qu'on pose, pas une
   bordure qu'on allume. Et comme il naît des coins, il évoque une mise
   au point d'appareil photo — ce qui n'est pas un hasard sur des
   photographies de sculptures.
   ═══════════════════════════════════════════════════════════════════════ */
.carte { position: relative; }
.carte-visuel::before, .carte-visuel::after {
  content: ""; position: absolute; z-index: 2; pointer-events: none;
  width: 1.6rem; height: 1.6rem;
  border: 1px solid var(--os-3);
  opacity: 0;
  transition: opacity .5s ease, width .7s var(--courbe), height .7s var(--courbe), border-color var(--t-moyen);
}
/* Coin haut-gauche et coin bas-droit : deux équerres suffisent à dire
   le cadre. Quatre feraient une boîte. */
.carte-visuel::before { top: .55rem; left: .55rem; border-right: 0; border-bottom: 0; }
.carte-visuel::after  { bottom: .55rem; right: .55rem; border-left: 0; border-top: 0; }
.grille.est-visible .carte-visuel::before,
.grille.est-visible .carte-visuel::after { opacity: .55; transition-delay: calc(var(--i, 0) * 70ms + 240ms); }
/* Au survol, les équerres s'étirent et rougissent : le cadre se ferme. */
.carte-lien:hover ~ * .carte-visuel::before,
.carte:hover .carte-visuel::before,
.carte:hover .carte-visuel::after {
  opacity: 1; width: 2.6rem; height: 2.6rem; border-color: var(--rouge);
}
/* Et une ligne fine court sous le titre, du bord vers l'intérieur. */
.carte-texte { position: relative; padding-top: .9rem; }
.carte-texte::before {
  content: ""; position: absolute; top: 0; left: 0; height: 1px; width: 100%;
  background: var(--trait); transform: scaleX(0); transform-origin: left;
  transition: transform .8s var(--courbe), background var(--t-moyen);
}
.grille.est-visible .carte-texte::before { transform: scaleX(1); transition-delay: calc(var(--i, 0) * 70ms + 180ms); }
.carte:hover .carte-texte::before { background: var(--rouge); }
@media (prefers-reduced-motion: reduce) {
  .carte-visuel::before, .carte-visuel::after { opacity: .4; transition: none; }
  .carte-texte::before { transform: scaleX(1); transition: none; }
}

/* ═══════════════════════════════════════════════════════════════════════
   L'ARÈNE — vue de dessus, à l'ancienne
   ───────────────────────────────────────────────────────────────────────
   L'écran est carré, cerné d'un filet, avec les lignes d'un tube
   cathodique par-dessus. Ces lignes sont posées EN CSS et jamais
   dessinées dans le canvas : dessinées dedans, elles suivraient
   l'échelle de l'écran et doubleraient d'épaisseur sur un téléphone.
   ═══════════════════════════════════════════════════════════════════════ */
.arene { display: grid; gap: 1.4rem; justify-items: center; margin-top: clamp(1.5rem, 4vw, 3rem); }
.arene-barre {
  display: flex; flex-wrap: wrap; gap: 1.6rem; justify-content: center;
  width: 100%; color: var(--os-3);
}
.arene-nombre { color: var(--os); font-weight: 700; font-variant-numeric: tabular-nums; }
.arene-vies .arene-nombre { color: var(--rouge-clair); }

.arene-ecran {
  /* PLUS PETITE. À 52 rem l'arène remplissait l'écran d'un ordinateur et
     l'on perdait les bêtes du coin de l'œil ; le regard doit tenir le
     plateau entier sans bouger la tête. */
  position: relative; width: 100%; max-width: min(40rem, 62vh);
  aspect-ratio: 1; border: 1px solid var(--trait-fort); border-radius: var(--rayon);
  background: #030303; overflow: hidden;
  box-shadow: inset 0 0 90px rgba(0, 0, 0, .9), 0 0 0 1px rgba(0,0,0,.6);
}
.arene-ecran canvas { display: block; margin: 0 auto; touch-action: none; cursor: crosshair; }
.arene-lignes {
  position: absolute; inset: 0; pointer-events: none; z-index: 2;
  background: repeating-linear-gradient(to bottom, rgba(0,0,0,.28) 0 1px, transparent 1px 3px);
  mix-blend-mode: multiply;
}
/* Le coin sombre du tube : il ramène l'œil au centre, là où est Dyson. */
.arene-ecran::after {
  content: ""; position: absolute; inset: 0; pointer-events: none; z-index: 1;
  background: radial-gradient(closest-side, transparent 58%, rgba(0,0,0,.75) 100%);
}
.arene-voile {
  position: absolute; inset: 0; z-index: 3;
  display: grid; align-content: center; justify-items: center; gap: 1rem;
  padding: clamp(1.2rem, 5vw, 3rem); text-align: center;
  /* Plus transparent : on doit deviner le visage derrière avant de
     lancer. Un voile opaque cache ce qu'on vient chercher. */
  background: rgba(0, 0, 0, .55);
  backdrop-filter: blur(2px);
}
.arene-message { max-width: 40ch; color: var(--os-2); font-weight: 300; }
.arene-voile .indice { max-width: 42ch; }
@media (max-width: 600px) { .arene-barre { gap: 1rem; font-size: .62rem; } }
@media (prefers-reduced-motion: reduce) { .arene-lignes { display: none; } }

/* ═══════════════════════════════════════════════════════════════════════
   LE SOUS-MENU DES PÉRIODES
   ───────────────────────────────────────────────────────────────────────
   « Œuvres » reste un lien vers la liste complète ; la flèche à côté
   ouvre le tiroir. Séparer les deux n'est pas une coquetterie : un menu
   déroulant dont l'entête n'est pas cliquable oblige à choisir une
   sous-rubrique pour voir l'ensemble, et au doigt il devient un piège —
   on ouvre le tiroir en croyant suivre le lien.

   Sur ORDINATEUR le tiroir s'ouvre aussi au survol, parce que c'est ce
   qu'on attend ; au TOUCHER il n'y a pas de survol, et seule la flèche
   l'ouvre. Les deux mènent au même état, tenu par `aria-expanded`.
   ═══════════════════════════════════════════════════════════════════════ */
.nav-groupe { position: relative; display: flex; align-items: center; gap: .25rem; }
.nav-fleche {
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.6rem; height: 1.6rem; padding: 0; font-size: .7rem;
  color: var(--os-3); transition: color var(--t-court), transform var(--t-court);
}
.nav-fleche:hover, .nav-fleche[aria-expanded="true"] { color: var(--rouge); }
.nav-fleche[aria-expanded="true"] { transform: rotate(180deg); }

.nav-sous {
  position: absolute; top: 100%; left: -1rem; z-index: 90;
  min-width: 16rem; padding: .6rem;
  display: grid; gap: .1rem;
  background: rgba(6, 5, 5, .97);
  border: 1px solid var(--trait-fort); border-radius: var(--rayon);
  box-shadow: 0 26px 50px -18px rgba(0, 0, 0, .95);
  /* Fermé, il est aussi hors du parcours clavier : un lien invisible
     mais tabulable envoie le curseur nulle part. */
  opacity: 0; visibility: hidden; transform: translateY(-.5rem);
  transition: opacity var(--t-court) ease, transform var(--t-court) var(--courbe), visibility 0s var(--t-court);
}
.nav-groupe:hover .nav-sous,
.nav-groupe:focus-within .nav-sous,
.nav-groupe.est-ouvert .nav-sous {
  opacity: 1; visibility: visible; transform: none; transition-delay: 0s;
}
.nav-sous a {
  display: grid; gap: .1rem; padding: .55rem .7rem; border-radius: 1px;
  font-family: var(--texte); font-size: .95rem; letter-spacing: 0; text-transform: none;
  color: var(--os-2); transition: background var(--t-court), color var(--t-court);
}
.nav-sous a::after { display: none; }
.nav-sous a:hover { background: rgba(214, 0, 28, .14); color: var(--os); }
.nav-sous-nom { font-family: var(--display); font-size: 1.1rem; font-weight: 500; letter-spacing: -.01em; }
.nav-sous-dates { font-size: .6rem; color: var(--cendre); letter-spacing: .18em; }
.nav-sous-tout { border-top: 1px solid var(--trait); margin-top: .3rem; padding-top: .3rem; }
.nav-sous-tout .nav-sous-nom { font-family: var(--mono); font-size: .68rem; letter-spacing: .2em; text-transform: uppercase; color: var(--os-3); }

/* Sur téléphone, le tiroir n'est plus un tiroir : les périodes se
   posent sous « Œuvres », en retrait. Un menu déroulant dans un menu
   déroulant, sur un écran de six centimètres, ne s'ouvre jamais. */
@media (max-width: 860px) {
  .nav-groupe { display: block; }
  .nav-fleche { display: none; }
  .nav-sous {
    position: static; opacity: 1; visibility: visible; transform: none;
    background: none; border: 0; box-shadow: none;
    min-width: 0; padding: .2rem 0 .6rem 1rem; gap: 0;
    border-left: 1px solid var(--trait-rouge);
    margin-left: .2rem;
  }
  .nav-sous a { padding: .45rem .6rem; }
  .nav-sous-nom { font-size: 1rem; }
}

/* ── LES CARTES DE LA SIMULATION ────────────────────────────────────
   Grandes, une par ligne sur téléphone, deux sur écran. Le visuel est
   posé sur une grille lumineuse — celle du sol de l'Arène — pour que la
   page annonce le jeu avant même qu'on l'ouvre. */
.grille-jeux { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 22rem), 1fr)); gap: clamp(1.5rem, 3vw, 2.5rem); }
.carte-jeu .carte-lien { display: grid; gap: 1.2rem; }
.carte-jeu-visuel {
  /* LES DEUX CARTES SONT CARRÉES. L'une en 4/3 et l'autre carrée,
     leurs titres ne tombaient plus à la même hauteur — deux colonnes
     décalées de cent soixante pixels se lisent comme un défaut. */
  position: relative; aspect-ratio: 1; overflow: hidden;
  border: 1px solid var(--trait-fort); border-radius: var(--rayon);
  /* NOIR PUR, pas « presque noir » : les photos de L'Arche sont posées
     sur du noir pur, et un fond à #050505 dessinait leur carré. Cinq
     valeurs d'écart suffisent à faire apparaître un rectangle. */
  background: #000;
  display: grid; place-items: center;
}
.carte-jeu-visuel img {
  width: 62%; height: auto; object-fit: contain; z-index: 1;
  transition: transform 1.2s var(--courbe), filter var(--t-long);
}
/* LA CAPTURE DU JEU REMPLIT SA CARTE. Le logo, lui, reste contenu :
   c'est un objet posé sur un fond, et le serrer le priverait de l'air
   qui le met en valeur. Une capture d'écran, au contraire, n'a pas de
   bord — la rogner ne lui retire rien, et lui laisser des marges noires
   la fait passer pour une vignette oubliée au milieu de la carte. */
/* LA CAPTURE EST CARRÉE, LA CARTE EST EN 4/3 : en `cover`, l'image est
   mise à la largeur et déborde en hauteur — le haut et le bas de
   l'arène disparaissent, et le cercle paraît décentré alors qu'il ne
   l'est pas. En `contain`, l'arène tient entière : c'est une capture,
   il n'y a rien à gagner à la rogner. */
/* LA CARTE DE L'ARÈNE EST CARRÉE, comme la capture qu'elle porte.
   En 4/3, l'image débordait en hauteur et le cercle paraissait décentré
   alors qu'il ne l'était pas — le cadre lui demandait de remplir une
   forme qui n'était pas la sienne. Une carte au format de son image n'a
   ni bande noire ni recadrage à subir.
   `place-self: stretch` est nécessaire : la figure centre ses enfants,
   et un enfant centré garde sa taille naturelle — « height: 100 % » ne
   trouvait alors aucune hauteur à laquelle se rapporter. */
.visuel-arene img { width: 100%; height: 100%; object-fit: contain; place-self: stretch; min-width: 0; min-height: 0; }
.carte-jeu-grille {
  position: absolute; inset: 0; pointer-events: none;
  background:
    repeating-linear-gradient(to right, rgba(255,240,212,.05) 0 1px, transparent 1px 44px),
    repeating-linear-gradient(to bottom, rgba(255,240,212,.05) 0 1px, transparent 1px 44px),
    radial-gradient(closest-side, rgba(214,0,28,.16), transparent 70%);
  transition: opacity var(--t-long);
}
.carte-jeu:hover .carte-jeu-visuel img { transform: scale(1.05); filter: brightness(1.1); }
.carte-jeu:hover .carte-jeu-grille { opacity: .55; }
.carte-jeu-texte { display: grid; gap: .6rem; }
.carte-jeu-texte .prose { max-width: 46ch; }
.carte-jeu .lien-fleche { color: var(--rouge-clair); }

/* ═══════════════════════════════════════════════════════════════════════
   « CASSE-LA » — la casse jouée image par image
   ───────────────────────────────────────────────────────────────────────
   Aucune transition CSS ici : la position de chaque morceau est écrite
   à chaque image par le script (voir assets/effets.js). Une transition
   entrerait en conflit avec lui — elle amortirait ce qu'il calcule, et
   la chute paraîtrait molle.
   ═══════════════════════════════════════════════════════════════════════ */
/* LE MORCEAU DOIT MONTRER EXACTEMENT CE QUE MONTRAIT LA PHOTO.
   `background-size: 100% 100%` étirait l'image sur tout le cadre,
   pendant que le <img>, lui, est en `object-fit: contain` — donc
   centré avec des bandes noires. Les morceaux dessinaient une AUTRE
   image, plus grande, et la casse ne se lisait pas : on voyait un lion
   entier et zoomé au lieu d'un lion en morceaux.
   `contain` et `center` reproduisent le cadrage du <img> au pixel près. */
.casse-eclat {
  position: absolute; inset: 0; pointer-events: none; z-index: 2;
  background-image: var(--image); background-size: contain;
  background-position: center; background-repeat: no-repeat;
  will-change: transform;
  filter: drop-shadow(0 6px 14px rgba(0, 0, 0, .8));
}
/* L'éclair au rescellement : le morceau s'allume une demi-seconde en
   se reposant. C'est ce qui fait qu'on VOIT chaque pièce revenir,
   plutôt qu'une image qui se recompose d'un bloc. */
.casse-eclat.est-scelle { animation: casse-sceau .5s ease-out; }
@keyframes casse-sceau {
  0%   { filter: brightness(2.4) drop-shadow(0 0 12px rgba(255, 240, 212, .9)); }
  100% { filter: brightness(1) drop-shadow(0 6px 14px rgba(0, 0, 0, .8)); }
}
/* L'image d'origine s'efface pendant la casse : sans ça, les morceaux
   volent AU-DESSUS d'une image encore entière. */
.eclats.est-brise img { opacity: 0; }
.eclats.est-fini img { opacity: 1; transition: opacity .35s ease .1s; }

.casse-trait {
  position: absolute; inset: 0; width: 100%; height: 100%;
  pointer-events: none; z-index: 3;
  color: rgba(0, 0, 0, .8);
  opacity: 0; transition: opacity .6s ease;
}
.casse-trait.est-cicatrice { opacity: 1; }
/* La cicatrice pâlit sans jamais disparaître : la pièce garde la trace
   de ce qu'on lui a fait. C'est le geste de Dyson, pas une animation
   qui se remet à zéro. */
.eclats.est-fini .casse-trait.est-cicatrice { opacity: .45; transition: opacity 2.4s ease 1s; }

/* Le point d'impact : un éclair blanc, très bref. */
.casse-flash {
  position: absolute; width: 1px; height: 1px; z-index: 4; pointer-events: none;
  border-radius: 50%;
  box-shadow: 0 0 0 0 rgba(255, 240, 212, .95);
  animation: casse-choc .7s cubic-bezier(.1, .7, .3, 1) forwards;
}
@keyframes casse-choc {
  0%   { box-shadow: 0 0 0 0 rgba(255, 240, 212, .95), 0 0 30px 10px rgba(214, 0, 28, .55); opacity: 1; }
  100% { box-shadow: 0 0 0 140px rgba(255, 240, 212, 0), 0 0 60px 30px rgba(214, 0, 28, 0); opacity: 0; }
}

/* La secousse : un dixième de seconde, une seule fois. Un écran qui
   tremble longtemps donne mal au cœur ; un écran qui tremble à peine
   donne un coup. */
.eclats.a-encaisse { animation: casse-secousse .42s cubic-bezier(.36, .07, .19, .97); }
@keyframes casse-secousse {
  10%, 90% { transform: translate3d(-2px, 1px, 0); }
  20%, 80% { transform: translate3d(4px, -2px, 0); }
  30%, 50%, 70% { transform: translate3d(-6px, 2px, 0); }
  40%, 60% { transform: translate3d(6px, -1px, 0); }
}
@media (prefers-reduced-motion: reduce) {
  .casse-eclat, .casse-flash, .eclats.a-encaisse { animation: none !important; }
}

/* ── LE VISEUR ──────────────────────────────────────────────────────
   Quatre équerres et un point, en retard sur la souris. Discret par
   construction : rien au milieu, un contour d'un pixel, et une opacité
   qui laisse lire le texte au travers. */
.viseur {
  position: fixed; top: 0; left: 0; z-index: 95;
  width: 0; height: 0; pointer-events: none;
  opacity: 0; transition: opacity .35s ease;
  will-change: transform;
}
.viseur.est-vu { opacity: .75; }
.viseur-coin {
  position: absolute; width: 9px; height: 9px;
  border: 1px solid var(--os-3);
  transition: transform .28s var(--courbe), border-color .28s ease, width .28s var(--courbe), height .28s var(--courbe);
}
.viseur-coin.hg { top: -17px; left: -17px; border-right: 0; border-bottom: 0; }
.viseur-coin.hd { top: -17px; left: 8px;  border-left: 0;  border-bottom: 0; }
.viseur-coin.bg { top: 8px;   left: -17px; border-right: 0; border-top: 0; }
.viseur-coin.bd { top: 8px;   left: 8px;   border-left: 0;  border-top: 0; }
/* Le point central : l'œil de la machine. Il respire très lentement —
   deux secondes — pour qu'on le sente sans le regarder. */
.viseur-point {
  position: absolute; top: -1.5px; left: -1.5px;
  width: 3px; height: 3px; border-radius: 50%;
  background: var(--rouge);
  box-shadow: 0 0 6px rgba(214, 0, 28, .8);
  animation: viseur-oeil 2.4s ease-in-out infinite;
}
@keyframes viseur-oeil { 0%, 100% { opacity: .55; } 50% { opacity: 1; } }

/* VERROUILLÉ : sur un lien ou un bouton, les équerres se resserrent et
   rougissent. C'est le seul moment où le viseur sert à quelque chose —
   il dit où l'on peut agir, ce qu'une flèche fait mal sur du noir. */
.viseur.est-verrouille { opacity: 1; }
.viseur.est-verrouille .viseur-coin { border-color: var(--rouge); width: 7px; height: 7px; }
.viseur.est-verrouille .hg { top: -11px; left: -11px; }
.viseur.est-verrouille .hd { top: -11px; left: 4px; }
.viseur.est-verrouille .bg { top: 4px;   left: -11px; }
.viseur.est-verrouille .bd { top: 4px;   left: 4px; }

/* Pas de viseur au doigt, ni si l'on a demandé moins de mouvement. */
@media (pointer: coarse) { .viseur { display: none; } }
@media (prefers-reduced-motion: reduce) { .viseur { display: none; } }

/* ── LES CARTES DE LA SIMULATION, VIVANTES ─────────────────────────── */
.carte-jeu {
  transform: perspective(900px) rotateX(var(--incline-x, 0deg)) rotateY(var(--incline-y, 0deg));
  transition: transform .5s var(--courbe);
  transform-style: preserve-3d;
}
.carte-jeu-visuel { position: relative; }
/* Le balayage du scanner : une ligne claire qui descend, lentement, en
   boucle. C'est le seul mouvement permanent de la page — un deuxième
   ferait clignoter la grille. */
.carte-jeu-scan {
  position: absolute; left: 0; right: 0; height: 2px; z-index: 2; pointer-events: none;
  background: linear-gradient(to right, transparent, rgba(255, 240, 212, .5), transparent);
  box-shadow: 0 0 14px rgba(214, 0, 28, .5);
  animation: carte-scan 5.5s linear infinite;
}
@keyframes carte-scan { 0% { top: -2%; opacity: 0; } 8% { opacity: 1; } 92% { opacity: 1; } 100% { top: 102%; opacity: 0; } }
/* La fêlure au survol. */
.carte-jeu-felure {
  position: absolute; inset: 0; width: 100%; height: 100%;
  z-index: 3; pointer-events: none;
  color: rgba(255, 240, 212, .8);
  animation: carte-felure 1.6s ease forwards;
}
@keyframes carte-felure {
  0% { opacity: 0; }
  10% { opacity: 1; }
  55% { opacity: 1; }
  100% { opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .carte-jeu { transform: none; }
  .carte-jeu-scan { display: none; }
}

/* LE PALIER DANS LA BARRE — un nom, pas un nombre.
   Il passe à la ligne sur les écrans étroits plutôt que de comprimer le
   score : sur un téléphone, la barre tient quatre repères, pas cinq. */
.arene-palier .arene-nom {
  font-weight: 700;
  color: var(--rouge, #d6001c);
  letter-spacing: .02em;
}
@media (max-width: 30rem) {
  .arene-palier { flex-basis: 100%; text-align: center; order: 9; }
}
