/* =========================================================
   LE MUR CHAT-MAN — feuille de style
   Univers Chat-Man (coloré), distinct du noir & blanc Epoque.
   La couleur vient des post-it (photos) ; l'interface reste sobre.
   ========================================================= */

:root {
  /* Fond : ta tile monogramme marron. Remplace le fichier, garde le nom.
     La couleur reprend le fond du tile lui-meme (62 % de ses pixels) et non
     le brun sombre du monogramme : elle sert de repli partout ou le motif ne
     peint pas — au-dela du document en fin de defilement, par exemple — et
     une teinte plus sombre s'y verrait comme une bande. */
  --wall-brown:      #ac8a67;
  --tile:            url("../img/monogramChM1-200.jpg");

  --ink:             #1a1410;   /* texte sombre */
  --paper:           #f4efe3;   /* papier crème (fond de la modale) */
  --wall-text:       #ffffff;   /* texte posé sur le monogramme : blanc pur */
  --paper-edge:      #e2d9c6;
  --sticky:          #ffe14d;   /* jaune post-it — seul accent, lié au sujet */
  --sticky-edge:     #eacf2e;

  /* Direction de l'ombre a l'ecran, en vecteur unitaire (y vers le bas).
     Source lumineuse fixee a 2h sur un cadran : l'ombre tombe a l'oppose,
     vers 8h, soit en bas a gauche. C'est la reference du mur entier, elle
     ne bouge jamais. */
  --light-x: -0.866;
  --light-y:  0.5;

  /* Rayon de flou admissible, en fraction de la distance de l'ombre. Un flou
     de rayon r deborde d'environ 0.75 r autour du bord decale : passe
     (--light-y / 0.75) x distance, l'ombre remonte au-dessus du bord haut de
     l'objet, la ou il touche son support — une ombre ne peut pas s'y trouver.
     Le rayon suit donc la distance au lieu d'etre fixe. Les valeurs en px
     ci-dessous restent la distance voulue, jamais le flou.
     La limite stricte serait --light-y / 0.75, soit x1.33 ; on garde 1.15 pour
     que le 0.75 — une approximation du flou gaussien, pas une garantie — ne
     laisse pas passer un liseré. */
  --sh-blur: calc(var(--light-y) * 1.15);

  /* Vecteur reellement utilise pour dessiner, exprime dans le repere de
     l'element. Pour tout ce qui n'est pas incline, les deux coincident. */
  --lx: var(--light-x);
  --ly: var(--light-y);

  /* Ligne de colle : hauteur de la bande plaquee au mur, en % de la note.
     C'est l'origine de tout ce qui bascule ou projette. Repli de la constante
     GLUE_BAND de postit.js, qui la repose ici au demarrage. */
  --glue: 15%;

  /* Ombres du bouton d'ajout, qui n'est pas incline aleatoirement et peut
     donc garder un vecteur fige. Les post-it, eux, composent la leur en
     calc() a partir de --lx / --ly : une custom property declaree ici
     resoudrait var(--lx) sur :root et perdrait la valeur par note. */
  --shadow-note:
      calc(var(--lx) *  2px) calc(var(--ly) *  2px) calc(var(--sh-blur) *  2px) -1px rgba(0,0,0,.45),
      calc(var(--lx) *  7px) calc(var(--ly) *  7px) calc(var(--sh-blur) *  7px) -4px rgba(0,0,0,.65);
  --shadow-note-hover:
      calc(var(--lx) *  5px) calc(var(--ly) *  5px) calc(var(--sh-blur) *  5px) -1px rgba(0,0,0,.45),
      calc(var(--lx) * 20px) calc(var(--ly) * 20px) calc(var(--sh-blur) * 20px) -6px rgba(0,0,0,.7);

  /* Contour noir net (~2px) simulé par une couronne de text-shadow :
     détache les lettres du motif monogramme, qui sinon les mange. */
  --text-outline:
      -1px -1px 0 #000,  0   -2px 0 #000,  1px -1px 0 #000,
       2px  0   0 #000,  1px  1px 0 #000,  0    2px 0 #000,
      -1px  1px 0 #000, -2px  0   0 #000,
      -2px -1px 0 #000, -1px -2px 0 #000,  1px -2px 0 #000,
       2px -1px 0 #000,  2px  1px 0 #000,  1px  2px 0 #000,
      -1px  2px 0 #000, -2px  1px 0 #000;

  /* Texte clair posé sur le fond marron : le contour pour la lisibilité,
     puis un flou plus large pour le décollement. */
  --shadow-text:
      var(--text-outline),
      0 2px 5px rgba(0,0,0,.9);

  --font-mono:  "Space Mono", ui-monospace, monospace;
  --font-body:  "Space Grotesk", system-ui, sans-serif;

  --max: 1180px;
}

* { box-sizing: border-box; }
[hidden] { display: none !important; }

html {
  -webkit-text-size-adjust: 100%;

  /* Pas de scrollbar-gutter: stable ici. La gouttiere reservee reste vide sur
     les navigateurs a barres en surimpression : la boite de <html> perd une
     quinzaine de pixels, le motif s'y arrete net et il ne reste que la couleur
     de fond — une bande marron permanente le long du bord droit. Sans
     gouttiere, la boite occupe toute la largeur et le monogramme va jusqu'au
     bord ; quand la barre prend vraiment de la place, c'est elle qui peint la
     zone, pas un vide.
     Le risque que la gouttiere evitait (une page qui tient tout juste dans la
     fenetre se recompose quand la barre apparait) ne se pose pas avec des
     barres en surimpression, et le mur de post-it deborde de toute facon. */

  /* L'elasticite de fin de defilement decouvre le canevas au-dela du document,
     ou le motif ne s'etend pas : meme bande marron, en bas cette fois. */
  overscroll-behavior-y: none;
}

body {
  margin: 0;
  min-height: 100vh;
  color: var(--wall-text);
  font-family: var(--font-body);

  /* Le monogramme fait partie du flux : il défile AVEC les post-it.
     (pas de background-attachment: fixed) */
  background-color: var(--wall-brown);
  background-image: var(--tile);
  background-repeat: repeat;
  background-size: 50px 50px;

  padding-bottom: 120px; /* espace pour ne pas cacher les notes sous le CTA */
}

/* ---------- Bandeau démo ---------- */
.demo-banner {
  font-family: var(--font-mono);
  font-size: 12px;
  text-align: center;
  padding: 8px 16px;
  background: var(--sticky);
  color: var(--ink);
  border-bottom: 1px solid var(--sticky-edge);
}

/* ---------- En-tête ---------- */
.wall-head {
  max-width: var(--max);
  margin: 0 auto;
  padding: 64px 24px 28px;
  text-align: center;
}
/* Titre composé : "epoque"  POST-IT !  "chat-man™".
   Le flex en baseline pose les deux mentions latérales sur la ligne de
   base du grand titre plutôt que centrées sur sa hauteur. */
.wall-title {
  display: flex;
  align-items: baseline;
  justify-content: center;
  flex-wrap: wrap;
  gap: clamp(8px, 2vw, 20px);

  font-family: var(--font-mono);
  margin: 0 0 14px;
  color: var(--wall-text);
  text-shadow: var(--shadow-text);
}
.wall-title-main {
  font-weight: 700;
  letter-spacing: .14em;
  font-size: clamp(28px, 5vw, 52px);
  line-height: 1.05;
}
.wall-title-side {
  font-weight: 400;
  font-size: clamp(10px, 1.5vw, 14px);
  letter-spacing: .22em;
  text-transform: lowercase;
  white-space: nowrap;
}
.wall-title-tm {
  font-size: .6em;
  letter-spacing: .06em;
  line-height: 0;   /* évite que le sup ne creuse l'interligne */
}
.wall-intro {
  max-width: 44ch;
  margin: 0 auto;
  font-size: clamp(15px, 2vw, 18px);
  line-height: 1.5;
  color: var(--wall-text);
  text-shadow: var(--shadow-text);
}

/* ---------- Le mur (masonry en colonnes) ---------- */
.wall {
  max-width: var(--max);
  margin: 0 auto;
  padding: 24px 20px 40px;
  column-width: 230px;
  column-gap: 22px;
}

.note {
  --rot: 0deg;
  /* --fit est pose par le JS : une note tournee est plus large que sa boite
     droite, et deborderait sur ses voisines. Repli a 1 — sans JS, la note
     n'est de toute facon pas tournee. */
  --fit: 1;
  /* --lx / --ly sont poses par le JS, contre-tournes de --rot : l'ombre est
     peinte dans le repere tourne de la note, donc un vecteur brut tournerait
     avec le papier et la lumiere changerait de direction d'une note a
     l'autre. En contre-tournant, elle retombe toujours a 2h a l'ecran. */
  break-inside: avoid;
  width: 100%;
  margin: 0 0 22px;
  /* z-index au survol n'a d'effet que sur un element positionne : sans ca la
     note qui se souleve passerait sous ses voisines. */
  position: relative;
  /* Pivot sur la ligne de colle, en haut : c'est ce point qui est fixe au
     mur. Le redressement au survol fait bouger le bas de la feuille, pas
     le haut. Sur un post-it detoure, ce haut est le vrai bord colle : la
     console publie le PNG d'aplomb et range son inclinaison en base, que le
     JS rend a la note via --rot. Avant ca, le pivot tombait sur le haut de
     la boite englobante, donc a cote du papier des que la photo penchait. */
  transform-origin: 50% 0;
  transform: rotate(var(--rot)) scale(var(--fit));
  transition: transform .25s ease;
}
.note-inner {
  position: relative;
  /* Au-dessus du calque d'ombre, qui le precede dans le DOM. */
  z-index: 1;
  border-radius: 3px;
  overflow: hidden;
  background: #fff;
  line-height: 0;
}

/* ---------- Ombre portee du bord libre ----------
   Un post-it colle en haut ne projette pas une ombre uniforme tout autour :
   sa bande de colle touche le mur — la, aucune ombre n'est possible — et le
   reste s'en ecarte de plus en plus jusqu'au bord libre. L'ombre naît donc de
   rien sur la ligne de colle et s'elargit vers le bas, meme au repos.

   Geometrie : pour une source lointaine, la silhouette est deplacee de
   (--lx, --ly) x d, ou d est la distance au mur. d valant zero au depart et
   croissant lineairement jusqu'au bord libre, ce deplacement est une
   application affine — donc exprimable en une seule transformation, un
   cisaillement horizontal plus un etirement vertical, tous deux pris depuis un
   meme point. Aucune approximation : l'ombre y tombe exactement sous le papier
   et reste invisible, par construction.

   Ce point de depart n'est pas la ligne de colle mais --sh-root, plus bas. Une
   feuille qui s'enroule ne quitte pas le mur des la colle : elle reste plaquee
   un moment puis s'ecarte, et cette sortie est quadratique, pas lineaire — sur
   le premier tiers l'ecart est negligeable. Le cisaillement, lui, ne sait etre
   que lineaire : le faire partir de la colle epaississait l'ombre bien trop
   haut. La faire naitre plus bas rend le haut du biseau aussi fin qu'il doit
   l'etre, sans avoir a trancher la silhouette en segments comme .note-curl.

   --f est le decollement du bord libre, en fraction de la hauteur *sous
   --sh-root* : un nombre pur, donc l'ombre suit la taille de la note. Il est la
   seule chose que le survol change ; direction, flou et etalement en
   decoulent. */
.note-shadow {
  --sh-root: calc(var(--glue) + 20%);
  --f: .078;
  position: absolute;
  top: 0; left: 0;
  width: 100%;
  z-index: 0;
  pointer-events: none;
  opacity: .52;
  /* Le flou suit le decollement : plus la feuille s'ecarte du mur, plus son
     ombre est molle.
     Il est volontairement pris sous la penombre du bord libre — a peu pres sa
     moitie. Une penombre croit avec la distance au mur et vaut donc zero a la
     racine, ou le papier touche ; un flou unique, lui, s'applique partout
     pareil. Regle sur le bord libre, il debordait la-haut de son rayon entier
     tout autour de la note, du cote eclaire compris : une bave a droite, ou
     aucune ombre ne peut se trouver. En le reglant sur le milieu de l'ombre, ce
     qui s'echappe a la racine reste sous le seuil de perception, et le bord
     libre n'est qu'a peine trop net.
     Il vit ici et non sur la silhouette, pour passer *apres* la coupe — floute
     d'abord, eteint ensuite, et le flou ramenerait de l'ombre au-dessus de la
     racine. */
  filter: blur(calc(var(--f) * 34px));
  /* Le cisaillement sort de la boite, et un element en absolu fait remonter ce
     debordement jusqu'au viewport : la page grandirait, une barre de scroll
     apparaitrait (meme raison que .note-curl). Une marge de clip laisse peindre
     sans rendre scrollable ; le flou, lui, est un debordement d'encre, qui n'a
     jamais fait defiler quoi que ce soit — il passe outre le clip sans risque. */
  overflow: clip;
  overflow-clip-margin: 64px;
  transition: filter .25s ease, opacity .25s ease;
}
/* La silhouette : l'image elle-meme passee au noir. Sur une photo pleine c'est
   un rectangle, sur un PNG detoure c'est le contour decoupe — un seul chemin de
   code pour les deux, et la hauteur du calque vient de l'image sans mesure. */
.note-shadow img {
  display: block;
  width: 100%;
  height: auto;
  filter: brightness(0);
  /* Coupe franche, pas un degrade : l'ombre a la meme densite partout, c'est sa
     *forme* qui s'affine vers le haut — le papier est davantage decolle en bas,
     donc il masque davantage de mur en bas. Un degrade d'opacite par-dessus
     ferait le travail une seconde fois et delaverait le bas.
     La coupe tombe sur --sh-root et nulle part ailleurs : c'est la que le
     cisaillement vaut zero, donc que l'ombre passe exactement sous le papier et
     s'y trouve deja cachee. Couper plus haut laisserait sortir la moitie du
     cisaillement qui repart de l'autre cote au-dessus du pivot ; couper plus bas
     trancherait de l'ombre visible. Le masque est applique avant la
     transformation, ses pourcentages sont donc ceux de la note. */
  -webkit-mask-image: linear-gradient(to bottom, transparent var(--sh-root), #000 var(--sh-root));
          mask-image: linear-gradient(to bottom, transparent var(--sh-root), #000 var(--sh-root));
  transform-origin: 50% var(--sh-root);
  /* skewX veut un angle : tan(a) = lx x f, et a ces valeurs (moins d'un demi
     degre) l'angle vaut sa tangente. 57.2958 convertit radians en degres. */
  transform:
      skewX(calc(var(--lx) * var(--f) * 57.2958deg))
      scaleY(calc(1 + var(--ly) * var(--f)));
  transition: transform .25s ease;
}
.note img {
  width: 100%;
  height: auto;
  display: block;
}
/* petite épingle discrète pour vendre le mur */
.note-inner::before {
  content: "";
  position: absolute;
  top: 7px; left: 50%;
  width: 9px; height: 9px;
  transform: translateX(-50%);
  border-radius: 50%;
  background: rgba(0,0,0,.35);
  box-shadow: 0 1px 2px rgba(0,0,0,.4);
  z-index: 2;
}
/* La note ne se redresse pas : elle est collee, son inclinaison lui
   appartient. Le survol la fait basculer en 3D autour de la ligne de colle
   (transform-origin en haut), donc le bas de la feuille vient vers l'oeil et
   grandit legerement, comme un coin qui se decolle. L'axe de rotateX est
   l'horizontale de la note apres rotate() : sur un post-it redresse, c'est
   le bord colle lui-meme, et la feuille bascule autour de sa colle plutot
   que de travers.
   Garder rotate(var(--rot)) est aussi ce qui rend --lx / --ly valables
   pendant tout le survol : l'inclinaison ne change pas, la contre-rotation
   reste juste. L'ordre compte, perspective() doit venir en premier. */
.note:hover, .note:focus-within {
  transform: perspective(700px) rotate(var(--rot)) scale(var(--fit)) rotateX(9deg);
  z-index: 3;
}
/* Le bord libre s'ecarte du mur : l'ombre s'etale et mollit d'autant. Une
   seule valeur a changer, tout le reste en decoule. */
.note:hover .note-shadow, .note:focus-within .note-shadow {
  /* La valeur physique serait .22 : pour un arc de 20 degres (CURL_ANGLE) sur
     toute la hauteur libre, le bord libre finit a L/theta x (1 - cos theta) du
     mur, soit 0.17 x cette hauteur libre, ou 0.22 ramene a la hauteur sous
     --sh-root. On reste volontairement en dessous. A .22 l'ombre bondit d'un
     facteur trois au survol et attire l'oeil plus que le papier qu'elle sert ;
     et comme le flou suit --f, elle deborde alors aussi du cote eclaire. Le
     mouvement du calque de courbure porte deja le decollement — l'ombre n'a
     qu'a le confirmer. */
  --f: .15;
  opacity: .46;
}

/* Post-it detoure (PNG transparent publie depuis la console de moderation).
   Le cadre blanc et le rognage sont faits pour une photo pleine : ici ils
   reboucheraient la transparence. L'ombre, elle, n'a rien de special a faire —
   le calque .note-shadow porte deja le contour decoupe, puisqu'il est fait de
   l'image elle-meme. */
.note--cut .note-inner {
  background: transparent;
  border-radius: 0;
  overflow: visible;
}
/* L'epingle flotterait dans le vide au-dessus d'un contour irregulier. */
.note--cut .note-inner::before { display: none; }

/* ---------- Decollement en courbure ----------
   Un post-it ne se souleve pas d'un bloc : sa bande de colle reste plaquee au
   mur et le reste s'enroule. Un seul element ne peut pas plier — il n'a qu'un
   plan. On superpose donc a la note un calque de tranches horizontales, chacune
   un peu plus inclinee que la precedente et posee au bout d'elle : la suite des
   plis fait la courbure. Geometrie et nombre de tranches : postit.js,
   constantes CURL_*.

   Le calque n'existe que le temps du survol. Au repos il redessinerait la note
   exactement par-dessus elle-meme, et sur un PNG detoure les bords
   antialiases, composites deux fois, durciraient.

   Toutes les hauteurs sont en % : chaque tranche fait height:100% du plan, donc
   la hauteur de la note, et un pourcentage signifie la meme chose pour toutes.
   C'est ce qui permet de placer l'image entiere dans chaque tranche et de la
   decouper en coordonnees d'image — et c'est aussi ce qui rend les
   pourcentages de translate3d lisibles comme des positions sur la note, leur
   reference etant la boite de l'element. L'aspect-ratio pose par le JS donne
   au plan cette hauteur.

   Le prix de cette mise a plat : chaque tranche fait la hauteur entiere de la
   note alors qu'elle n'en montre qu'une bande, et celle du bas est descendue
   de ~91%, donc son bord bas est a ~1.91 fois la hauteur de la note. Ce vide
   ne peint rien — les images sont decoupees au clip-path — mais il compte
   comme debordement, et un calque en absolu le fait remonter jusqu'au
   viewport : la page grandissait au survol, une barre de defilement
   apparaissait, la largeur perdue recomposait les colonnes. D'ou la boite de
   coupe ci-dessous.

   `overflow: hidden` et non `clip` : `clip` a l'avantage d'accepter une marge,
   mais mesure sur cette page, il ne retient pas le debordement de tranches
   transformees en 3D — Chromium continue d'en tenir compte dans la hauteur du
   document tant qu'un recalcul force ne le detrompe pas. `hidden`, lui, fait
   du calque un conteneur de defilement : le debordement lui appartient, par
   definition il ne remonte plus.

   Reste que `hidden` coupe pile sur sa boite, alors que le bord libre vient
   vers l'oeil et en sort (~13px sur les cotes, ~23px dessous pour une note de
   342px). La boite de coupe est donc elargie — mais lateralement seulement.
   Vers le bas elle s'arrete a la note : une boite de coupe plus haute que la
   note depasse sous elle et fait grandir la page a son tour, ce qui reviendrait
   a deplacer le probleme au lieu de le resoudre. La place pour le bord libre
   vient de la legende, qui appartient a la note et vit sous l'image.
   Lateralement, elle sort dans la gouttiere entre colonnes ou dans les 20px
   de marge du mur, jamais au-dela de la page. */
.note-curl {
  --bleed: 20px;
  position: absolute;
  top: 0;
  left: calc(-1 * var(--bleed));
  width: calc(100% + 2 * var(--bleed));
  height: 100%;
  padding: 0 var(--bleed);
  overflow: hidden;
  pointer-events: none;
  z-index: 1;
}
/* Le plan de reference : la boite exacte de la note, dont tout le reste tire
   ses pourcentages. Separe de la boite de coupe, qui elle est plus grande. */
.curl-plane {
  position: relative;
  width: 100%;
  /* Ni perspective ni preserve-3d : la projection est calculee en JS et posee
     en 2D (postit.js, buildCurl). Toute transformation 3D ferait promouvoir les
     tranches en couches composees, rendues dans une texture dont la resolution,
     sous un ancetre tourne, n'est pas celle de l'ecran — net a 100%, flou des
     que l'affichage est mis a 125% ou 150%. Le point de fuite d'alors est
     devenu CURL_OEIL, et il est toujours sur la ligne de colle : la feuille
     s'ouvre vers le bas et vers l'oeil, elle ne derive pas lateralement. */
}
/* Les tranches sont soeurs, chacune posee a sa bande par `top` et portant son
   inclinaison absolue — rien ne s'additionne par la hierarchie, donc aucune
   imprecision de rendu ne se propage du haut de la feuille vers le bas.
   Position et angle sont calcules en JS (postit.js, buildCurl) ; ne restent
   ici que les deux etats.

   L'origine sur le bord haut de la tranche : c'est sa charniere. Et les deux
   transformations s'ecrivent avec les memes fonctions dans le meme ordre —
   sans ca le navigateur interpolerait des matrices au lieu des composantes, et
   la feuille se deplierait de travers.

   Au repos la transformation est l'identite, et c'est voulu : une couche posee
   entre deux pixels est reechantillonnee, donc molle, meme immobile. */
.curl-seg {
  position: absolute;
  left: 0; right: 0;
  height: 100%;
  transform-origin: 50% 0;
  transform: translateY(0) scale(1, 1);
  transition: transform .25s ease;
}
.note.is-lifted .curl-seg {
  transform: translateY(var(--ty)) scale(var(--sx), var(--sy));
}
/* > img : ne pas atteindre les images des tranches imbriquees, qui portent
   chacune leur propre decoupe. top et clip-path sont poses par le JS. */
.curl-seg > img {
  position: absolute;
  left: 0;
  width: 100%;
  height: auto;
}
/* Le jour sur la feuille qui s'enroule. Un seul degrade, pose sur la note
   entiere : il part de rien sur la ligne de colle — la, le papier touche encore
   le mur et garde son eclairage — et monte jusqu'au bord libre, le plus proche
   de la source. Chaque tranche en montre sa portion, decoupee au meme
   clip-path que son image ; la rampe, elle, ne connait pas le decoupage, donc
   les raccords sont invisibles et le nombre de tranches ne se lit plus dans la
   lumiere. Un eclairci par tranche donnait autant d'aplats que de plis.
   --glow-rgb et --glow-a viennent du JS : blanc quand la source est en haut,
   noir quand elle vient d'en bas, amplitude tiree de --ly. */
.curl-light {
  position: absolute;
  left: 0;
  width: 100%;
  pointer-events: none;
  background: linear-gradient(to bottom,
      rgb(var(--glow-rgb) / 0) var(--glue),
      rgb(var(--glow-rgb) / var(--glow-a)) 100%);
  /* Masque par l'image : sur un PNG detoure, le jour s'arrete au bord du
     papier au lieu de peindre le rectangle entier. */
  -webkit-mask-size: 100% 100%;
          mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
  /* Le voile monte avec la bascule. Applique d'emblee, il ferait clignoter la
     note au montage du calque, alors qu'elle est encore a plat. */
  opacity: 0;
  transition: opacity .25s ease;
}
.note.is-lifted .curl-light { opacity: 1; }
/* Le calque bascule a la place de la note : sans ca les deux rotations
   s'additionneraient. Plus specifique que .note:hover, et place apres. */
.note--curled:hover, .note--curled:focus-within {
  transform: rotate(var(--rot)) scale(var(--fit));
}
.note-sign {
  font-family: var(--font-mono);
  font-size: 12px;
  line-height: 1.3;
  color: var(--wall-text);
  padding: 8px 4px 0;
  text-align: center;
  word-break: break-word;
  text-shadow: var(--shadow-text);
}
.note-sign a { color: var(--sticky); text-decoration: none; }
.note-sign a:hover { text-decoration: underline; }

/* apparition */
.note { opacity: 0; }
.note.in { opacity: 1; transition: opacity .5s ease, transform .25s ease; }

/* ---------- Etats vide / chargement ---------- */
.wall-empty, .wall-loading {
  text-align: center;
  font-family: var(--font-mono);
  font-size: 14px;
  color: var(--wall-text);
  padding: 20px 24px 60px;
  text-shadow: var(--shadow-text);
}

/* ---------- CTA fixe (post-it vierge) ---------- */
.cta-pin {
  position: fixed;
  left: 50%;
  bottom: 22px;
  transform: translateX(-50%) rotate(-2deg);
  z-index: 40;

  font-family: var(--font-mono);
  font-weight: 700;
  font-size: 15px;
  color: var(--ink);
  background: var(--sticky);
  border: none;
  border-radius: 4px;
  padding: 14px 22px;
  cursor: pointer;
  box-shadow: var(--shadow-note);
  transition: transform .18s ease, box-shadow .18s ease;
}
.cta-pin:hover {
  transform: translateX(-50%) rotate(0deg) translateY(-2px);
  box-shadow: var(--shadow-note-hover);
}
.cta-pin:active { transform: translateX(-50%) rotate(0deg) translateY(0); }

/* ---------- Modale ---------- */
.modal { position: fixed; inset: 0; z-index: 100; display: flex; }
.modal-backdrop {
  position: absolute; inset: 0;
  background: rgba(15,10,6,.66);
  backdrop-filter: blur(2px);
}
.modal-card {
  position: relative;
  margin: auto;
  width: min(480px, 92vw);
  max-height: 92vh;
  overflow-y: auto;
  background: var(--paper);
  color: var(--ink);
  border-radius: 6px;
  padding: 28px 26px 26px;
  box-shadow: 0 30px 80px rgba(0,0,0,.5);
  -webkit-overflow-scrolling: touch;
}
.modal-x {
  position: absolute; top: 12px; right: 12px;
  width: 34px; height: 34px;
  border: none; background: transparent;
  font-size: 18px; color: var(--ink);
  cursor: pointer; border-radius: 50%;
}
.modal-x:hover { background: rgba(0,0,0,.06); }

.form-title {
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: 22px;
  margin: 0 0 6px;
}
.form-subtitle {
  margin: 0 0 20px;
  font-size: 14px;
  color: color-mix(in srgb, var(--ink) 72%, transparent);
}

/* zone photo */
.photo-row {
  margin-bottom: 20px;
}
.photo-row .photo-drop { margin-bottom: 0; }

.photo-drop {
  display: block;
  border: 1.5px dashed var(--paper-edge);
  border-radius: 6px;
  background: #fbf8f0;
  padding: 0;
  margin-bottom: 20px;
  cursor: pointer;
  overflow: hidden;
}
.photo-drop:hover { border-color: var(--sticky-edge); }
.photo-empty {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 6px; padding: 34px 16px; text-align: center;
}
.photo-plus { font-size: 30px; line-height: 1; color: var(--sticky-edge); }
.photo-label { font-family: var(--font-mono); font-size: 14px; }
.photo-hint  { font-size: 12px; color: color-mix(in srgb, var(--ink) 55%, transparent); }
.photo-preview { width: 100%; display: block; }

/* champs */
.field { margin-bottom: 16px; }
.field-label {
  display: block;
  font-family: var(--font-mono);
  font-size: 12.5px;
  margin-bottom: 6px;
}
.field-label .req { color: #b23b2e; font-style: normal; font-size: 11px; }
.field-label .opt { color: color-mix(in srgb, var(--ink) 50%, transparent); font-style: normal; font-size: 11px; }
.field input[type="text"],
.field input[type="email"] {
  width: 100%;
  font-family: var(--font-body);
  font-size: 15px;
  color: var(--ink);
  background: #fff;
  border: 1px solid var(--paper-edge);
  border-radius: 5px;
  padding: 11px 12px;
}
.field input:focus { outline: 2px solid var(--sticky-edge); outline-offset: 1px; border-color: var(--sticky-edge); }
.field-note {
  font-size: 12px;
  line-height: 1.4;
  color: color-mix(in srgb, var(--ink) 62%, transparent);
  margin: 8px 0 0;
}

/* honeypot */
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; opacity: 0; }

/* cases à cocher */
.check {
  display: flex; gap: 10px; align-items: flex-start;
  font-size: 12.5px; line-height: 1.45;
  margin-bottom: 12px;
  color: color-mix(in srgb, var(--ink) 82%, transparent);
}
.check input { margin-top: 2px; flex: 0 0 auto; width: 16px; height: 16px; accent-color: var(--ink); }
.check-optin { font-family: var(--font-mono); font-size: 13px; color: var(--ink); }

/* bloc concours révélé sous l'opt-in */
.concours-block {
  margin: -2px 0 14px 26px;
  padding-left: 12px;
  border-left: 2px solid var(--paper-edge);
}
.concours-block .field { margin-bottom: 8px; }

.form-error {
  color: #b23b2e;
  font-size: 13px;
  font-family: var(--font-mono);
  margin: 4px 0 12px;
}

.form-actions { display: flex; gap: 10px; margin-top: 14px; }
.btn-solid {
  flex: 1;
  font-family: var(--font-mono); font-weight: 700; font-size: 15px;
  color: var(--paper); background: var(--ink);
  border: none; border-radius: 5px; padding: 13px 18px; cursor: pointer;
}
.btn-solid:hover { background: #000; }
.btn-solid:disabled { opacity: .55; cursor: default; }
.btn-ghost {
  font-family: var(--font-mono); font-size: 14px;
  color: var(--ink); background: transparent;
  border: 1px solid var(--paper-edge); border-radius: 5px;
  padding: 13px 18px; cursor: pointer;
}
.btn-ghost:hover { background: rgba(0,0,0,.04); }

/* succès */
.success-view { text-align: center; padding: 12px 0 6px; }
.success-mark {
  width: 54px; height: 54px; margin: 0 auto 14px;
  border-radius: 50%; background: var(--sticky); color: var(--ink);
  font-size: 26px; display: flex; align-items: center; justify-content: center;
}

/* focus visible clavier */
:focus-visible { outline: 2px solid var(--sticky-edge); outline-offset: 2px; }

/* mobile */
@media (max-width: 560px) {
  .wall { column-width: 150px; column-gap: 14px; padding: 16px 14px 30px; }
  .note { margin-bottom: 14px; }
  .wall-head { padding: 44px 20px 20px; }
  .cta-pin { font-size: 14px; padding: 13px 18px; bottom: 16px; }
  .modal-card { padding: 24px 18px 20px; }
}

/* mouvement réduit */
@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; }
  .note { opacity: 1; transform: none; }
}
