/*
 * Gabarit d'aperçu — maquette HTML/CSS fidèle à la structure du moteur :
 * en-tête (dédicace, titre, métadonnées en pictos), deux colonnes 40/60
 * (ingrédients illustrés à gauche, étapes numérotées à droite), pied
 * (secret, extrait manuscrit, dressage). Tout est dimensionné en cqw :
 * l'affiche reste juste à n'importe quelle largeur d'écran.
 */

.poster {
  container-type: inline-size;
  position: relative;
  width: 100%;
  overflow: hidden;
  background: var(--p-bg);
  color: var(--p-ink);
  font-family: var(--p-corps);
  line-height: 1.5;
  box-shadow: 0 2px 6px rgba(43, 33, 25, 0.12), 0 16px 40px rgba(43, 33, 25, 0.18);
}

.poster-inner {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  padding: 5.5cqw;
  gap: 3cqw;
}

/* ── En-tête ─────────────────────────────────────────────── */
.p-entete {
  text-align: center;
  flex-shrink: 0;
}
.p-dedicace {
  font-size: 2.7cqw;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  opacity: 0.85;
  margin-bottom: 1.6cqw;
}
.p-titre {
  font-family: var(--p-titre);
  font-size: 8cqw;
  font-weight: 700;
  line-height: 1.08;
  margin: 0 auto;
  max-width: 92%;
  overflow-wrap: break-word;
}
.p-meta {
  margin-top: 2.6cqw;
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: 1.2cqw 4cqw;
  font-size: 2.5cqw;
  font-weight: 600;
}
.p-meta-item {
  display: inline-flex;
  align-items: center;
  gap: 1.1cqw;
}
.p-meta-item svg {
  width: 2.8cqw;
  height: 2.8cqw;
  flex-shrink: 0;
}

/* ── Colonnes 40 / 60 ────────────────────────────────────── */
.p-colonnes {
  display: grid;
  grid-template-columns: 40fr 60fr;
  gap: 4.5cqw;
  flex: 1;
  min-height: 0;
  align-items: start;
  text-align: left;
}
/* ── L'AQUARELLE EN FRISE INVERSE SES COLONNES ─────────────────────────
 *
 * 18/08/2026, « en frise latérale tout est décalé ». Ce n'était pas un décalage
 * dans le fichier livré, c'était un RETOURNEMENT entre les deux compositions
 * que le client voit coup sur coup. Sur /creer/, la boîte montre d'abord le
 * brouillon composé par le navigateur, puis l'image du moteur le remplace deux
 * secondes plus tard. Or la variante A15 du moteur, celle de l'aquarelle en
 * frise, porte `.page.c-frise .body { flex-direction: row-reverse; }` : c'est
 * un parti pris de la planche peinte, la marche à suivre à gauche et
 * l'inventaire contre le bord droit. Le brouillon, lui, posait les ingrédients
 * à gauche dans les quatre univers. L'aquarelle étant l'univers PAR DÉFAUT,
 * c'était la toute première chose que voyait un visiteur : sa page se
 * retournait sous ses yeux.
 *
 * Mesuré par `3-MOTEUR/tools/sonde-frise.mjs` : aquarelle pose sa colonne
 * d'ingrédients à 81 % de la cage, cahier, kawaii et chic à 19 %. Seule
 * l'aquarelle est concernée, et seulement en frise. Le brouillon suit donc le
 * moteur pour cette cellule, et pour elle seule. */
.poster[data-style="aquarelle"][data-agencement="frise"] .p-colonnes {
  grid-template-columns: 60fr 40fr;
}
.poster[data-style="aquarelle"][data-agencement="frise"] .p-ingredients { order: 2; }
.poster[data-style="aquarelle"][data-agencement="frise"] .p-etapes { order: 1; }

.p-rubrique {
  font-family: var(--p-titre);
  font-size: 3.3cqw;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  margin-bottom: 2.2cqw;
  padding-bottom: 1.1cqw;
  border-bottom: 0.3cqw solid var(--p-accent);
}
.p-ingredients ul {
  display: flex;
  flex-direction: column;
  gap: 1.7cqw;
  font-size: 2.6cqw;
}
.p-ingredients li {
  display: flex;
  align-items: baseline;
  gap: 1.5cqw;
}
.p-marqueur {
  flex-shrink: 0;
  width: 2.4cqw;
  height: 2.4cqw;
  border-radius: 50%;
  background: var(--p-accent);
  align-self: center;
}
.p-ingredients li:nth-child(3n + 2) .p-marqueur {
  background: var(--p-accent2);
}
.p-ingredients li:nth-child(3n) .p-marqueur {
  background: var(--p-accent3);
}
.p-qte {
  font-weight: 700;
  white-space: nowrap;
}
.p-etapes ol {
  display: flex;
  flex-direction: column;
  gap: 2.1cqw;
  font-size: 2.6cqw;
}
.p-etapes li {
  display: flex;
  gap: 1.8cqw;
  align-items: flex-start;
}
.p-num {
  flex-shrink: 0;
  width: 3.8cqw;
  height: 3.8cqw;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--p-titre);
  font-weight: 700;
  font-size: 2.2cqw;
  color: var(--p-bg);
  background: var(--p-ink);
  margin-top: 0.2cqw;
}

/* ── Pied ────────────────────────────────────────────────── */
.p-pied {
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  gap: 1.6cqw;
  text-align: center;
}
.p-secret {
  font-size: 2.7cqw;
  font-style: italic;
}
.p-secret strong {
  font-style: normal;
}
.p-manuscrit {
  align-self: center;
  max-width: 46%;
  background: rgba(255, 253, 246, 0.92);
  padding: 1.4cqw 1.8cqw 2cqw;
  box-shadow: 0 0.5cqw 1.8cqw rgba(43, 33, 25, 0.25);
  transform: rotate(-1.6deg);
}
.p-manuscrit img {
  display: block;
  width: 100%;
  height: auto;
}
.p-manuscrit-legende {
  font-size: 1.8cqw;
  opacity: 0.7;
  margin-top: 0.9cqw;
}
.p-dressage {
  font-size: 2.4cqw;
  opacity: 0.85;
}

/* ── Filigrane APERÇU ────────────────────────────────────── */
/* ── Protection de l'aperçu ─────────────────────────────────────────────
 *
 * 12/08/2026. L'aperçu montre l'affiche entière avant paiement : c'est la
 * mécanique de vente, et c'est aussi le fichier lui-même s'il n'est pas
 * protégé. Trois couches de filigrane aux inclinaisons opposées, denses,
 * en fusion « multiply » pour qu'elles mordent sur les aplats clairs comme
 * sur les fonds peints.
 *
 * Honnêteté technique : aucune de ces protections n'est infranchissable.
 * Le navigateur exécute le DOM chez le client, donc qui ouvre l'inspecteur
 * peut retirer les couches. Et AUCUNE page web ne peut empêcher une capture
 * d'écran : ni Windows, ni macOS, ni iOS, ni Android n'exposent ce contrôle
 * au web. La seule protection réelle est la définition : ce qui est à
 * l'écran fait autour de 600 px de large, un tirage 30 x 40 en demande
 * 3500. Une capture donne une image inutilisable en impression.
 */
.p-filigrane {
  position: absolute;
  inset: -20%;
  pointer-events: none;
  z-index: 40;
  mix-blend-mode: multiply;
  background-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='260' height='150'%3E%3Ctext x='130' y='84' font-family='Arial, sans-serif' font-size='27' font-weight='700' letter-spacing='7' fill='rgba(43,33,25,0.30)' text-anchor='middle' transform='rotate(-24 130 80)'%3EAPER%C3%87U%3C/text%3E%3C/svg%3E");
  background-size: 30cqw auto;
}

.p-filigrane.f2 {
  background-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='300' height='170'%3E%3Ctext x='150' y='92' font-family='Arial, sans-serif' font-size='19' font-weight='700' letter-spacing='5' fill='rgba(43,33,25,0.22)' text-anchor='middle' transform='rotate(26 150 88)'%3Emarecetteillustree.fr%3C/text%3E%3C/svg%3E");
  background-size: 34cqw auto;
  background-position: 12% 40%;
}

.p-filigrane.f3 {
  background-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Ccircle cx='90' cy='90' r='2.4' fill='rgba(43,33,25,0.20)'/%3E%3C/svg%3E");
  background-size: 7cqw auto;
  mix-blend-mode: multiply;
}

/* LA MENTION DU SITE, DANS LE FLUX ET LISIBLE (23/08/2026, Sebastian).
 *
 * Elle etait collee en bas a droite, hors du flux, en 2,1 cqw a 46 %
 * d'opacite. A cette taille et a cet endroit, personne ne la lit : l'affiche
 * de quelqu'un qui n'a PAS paye l'euro ne disait donc pas d'ou elle venait, et
 * c'est exactement la seule qui devrait le dire. Elle passe dans le flux, sous
 * le dernier bloc, centree comme lui, a la taille du dressage : dans la
 * continuite, et visible.
 *
 * L'apercu doit montrer ce que le client emportera, mention comprise, AVANT
 * qu'il paie : une mention decouverte sur le fichier livre est une
 * reclamation. Ce bloc suit donc celui du moteur, regle `.mention` de
 * 3-MOTEUR/lib/templates.js. Les deux se modifient ensemble. */
.p-mention {
  margin-top: 1.6cqw;
  text-align: center;
  font-family: inherit;
  font-size: 2.4cqw;
  letter-spacing: 0.08em;
  text-transform: lowercase;
  color: var(--p-ink, #33221d);
  opacity: 0.78;
}

/* Ni glisser-déposer, ni sélection, ni menu contextuel sur l'aperçu : ça
   n'arrête personne de déterminé, ça arrête le geste réflexe. */
.poster, .poster * {
  -webkit-user-select: none;
  user-select: none;
  -webkit-user-drag: none;
}
.poster img { pointer-events: none; }

/* L'impression de la page est un contournement gratuit du filigrane à
   l'écran : on la ferme. */
@media print {
  .poster, .apercu-toile, .apercu-serveur { display: none !important; }
  body::after {
    content: "The preview does not print. The high-resolution files are delivered after ordering.";
    display: block; padding: 40px; font: 16px/1.5 Arial, sans-serif;
  }
}

/* ════ Univers 3 — Rétro ══════════════════════════════════ */
.poster[data-style="retro"] {
  --p-bg: #f4e7c8;
  --p-ink: #3b2a1c;
  --p-accent: #b5502a;
  --p-accent2: #c98a2c;
  --p-accent3: #7fa3a8;
  --p-titre: "Abril Fatface", Georgia, serif;
  --p-corps: "Archivo", system-ui, sans-serif;
  background-image: radial-gradient(
    ellipse 140% 55% at 50% -12%,
    rgba(233, 180, 76, 0.5),
    transparent 62%
  );
}
.poster[data-style="retro"] .poster-inner {
  border: 0.7cqw solid var(--p-ink);
  outline: 0.25cqw solid var(--p-ink);
  outline-offset: -2.2cqw;
  margin: 2.2cqw;
}
.poster[data-style="retro"] .p-titre {
  background: var(--p-accent);
  color: #f4e7c8;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  font-size: 6.6cqw;
  padding: 1.4cqw 3.4cqw 1.8cqw;
  display: inline-block;
  box-decoration-break: clone;
}
.poster[data-style="retro"] .p-dedicace {
  color: var(--p-accent);
  font-weight: 700;
}
.poster[data-style="retro"] .p-marqueur {
  border-radius: 0.5cqw;
  transform: rotate(45deg) scale(0.9);
}
.poster[data-style="retro"] .p-secret {
  background: rgba(255, 251, 238, 0.75);
  border: 0.2cqw solid rgba(59, 42, 28, 0.35);
  padding: 1.3cqw 2.4cqw;
  align-self: center;
  max-width: 88%;
  margin: 0 auto;
}

/* Les univers « Poétique japonais » et « Éditorial illustré » ont été retirés
   du catalogue le 11/08/2026 : leurs habillages sont supprimés d'ici, plus
   aucun StyleId ne peut les viser. */

/* ════ Univers 2 — Cahier de famille ══════════════════════ */
.poster[data-style="cahier"] {
  --p-bg: #f7f1e1;
  --p-ink: #2f4a6e;
  --p-accent: #b4453a;
  --p-accent2: #8a6a4f;
  --p-accent3: #2f4a6e;
  --p-titre: "Lora", Georgia, serif;
  --p-corps: "Lora", Georgia, serif;
  background-image:
    linear-gradient(to right, transparent 11.4cqw, rgba(180, 69, 58, 0.55) 11.4cqw, rgba(180, 69, 58, 0.55) 11.7cqw, transparent 11.7cqw),
    repeating-linear-gradient(
      to bottom,
      transparent 0,
      transparent 5.2cqw,
      rgba(122, 148, 182, 0.28) 5.2cqw,
      rgba(122, 148, 182, 0.28) 5.35cqw
    );
}
.poster[data-style="cahier"]::before,
.poster[data-style="cahier"]::after {
  content: "";
  position: absolute;
  top: -1.4cqw;
  width: 12cqw;
  height: 4.6cqw;
  background: rgba(228, 214, 180, 0.75);
  box-shadow: 0 0.3cqw 0.8cqw rgba(43, 33, 25, 0.12);
  z-index: 2;
}
.poster[data-style="cahier"]::before {
  left: 7cqw;
  transform: rotate(-5deg);
}
.poster[data-style="cahier"]::after {
  right: 7cqw;
  transform: rotate(4deg);
}
.poster[data-style="cahier"] .poster-inner {
  padding-left: 14cqw;
}
.poster[data-style="cahier"] .p-titre {
  font-style: italic;
  font-weight: 600;
}
.poster[data-style="cahier"] .p-dedicace {
  color: var(--p-accent);
}
.poster[data-style="cahier"] .p-rubrique {
  border-bottom-style: double;
  border-bottom-width: 0.7cqw;
  border-bottom-color: var(--p-accent);
  letter-spacing: 0.22em;
}
.poster[data-style="cahier"] .p-ingredients li .p-marqueur {
  width: 2cqw;
  height: 2cqw;
  border: 0.35cqw solid var(--p-accent2);
  background: transparent;
}
.poster[data-style="cahier"] .p-num {
  background: transparent;
  color: var(--p-accent);
  border: 0.3cqw solid var(--p-accent);
}
.poster[data-style="cahier"] .p-secret {
  font-family: "Caveat", cursive;
  font-size: 3.6cqw;
  font-style: normal;
}
.poster[data-style="cahier"] .p-manuscrit {
  transform: rotate(-2.2deg);
  outline: 0.25cqw dashed rgba(138, 106, 79, 0.5);
  outline-offset: 0.6cqw;
}
/* Tache de café discrète */
.poster[data-style="cahier"] .poster-inner::after {
  content: "";
  position: absolute;
  right: 6cqw;
  bottom: 7cqw;
  width: 9cqw;
  height: 9cqw;
  border-radius: 50%;
  border: 1.1cqw solid rgba(138, 106, 79, 0.16);
  filter: blur(0.35cqw);
  pointer-events: none;
}

/* ════ Univers 1 — Aquarelle ══════════════════════════════ */
.poster[data-style="aquarelle"] {
  --p-bg: #fbf4e6;
  --p-ink: #3e2c2a;
  --p-accent: #c86b4a;
  --p-accent2: #7c8b4e;
  --p-accent3: #6e8ca0;
  --p-olive-fonce: #5e6b39;
  --p-terracotta-texte: #a9502f;
  --p-abricot: #e4a94f;
  --p-creme: #fdf9f0;
  --p-titre: "Petrona", Georgia, serif;
  --p-corps: "Karla", system-ui, sans-serif;
  /* Deux lavis diffus : pigment déposé en haut à gauche, en bas à droite */
  background-image:
    radial-gradient(ellipse 46% 20% at 12% 4%, rgba(124, 139, 78, 0.34), transparent 70%),
    radial-gradient(ellipse 30% 14% at 40% 1%, rgba(228, 169, 79, 0.36), transparent 72%),
    radial-gradient(ellipse 44% 18% at 88% 97%, rgba(200, 107, 74, 0.28), transparent 70%),
    radial-gradient(ellipse 30% 13% at 58% 99%, rgba(110, 140, 160, 0.24), transparent 72%);
}
.poster[data-style="aquarelle"] .poster-inner {
  padding: 5.2cqw 5cqw 4.6cqw;
  gap: 2.2cqw;
}
.poster[data-style="aquarelle"] .p-dedicace {
  color: var(--p-olive-fonce);
  font-weight: 700;
  letter-spacing: 0.22em;
  opacity: 1;
}
.poster[data-style="aquarelle"] .p-titre {
  font-weight: 800;
  font-size: 8cqw;
  line-height: 1.05;
  letter-spacing: -0.012em;
}
/* Filet peint sous le titre : deux traits terre cuite, un point abricot */
.poster[data-style="aquarelle"] .p-titre::after {
  content: "";
  display: block;
  width: 30cqw;
  height: 2cqw;
  margin: 1.8cqw auto 0;
  background-image:
    radial-gradient(circle at 50% 50%, var(--p-abricot) 0 0.85cqw, transparent 0.9cqw),
    linear-gradient(to right, rgba(200, 107, 74, 0.55), rgba(200, 107, 74, 0.55)),
    linear-gradient(to right, rgba(200, 107, 74, 0.55), rgba(200, 107, 74, 0.55));
  background-repeat: no-repeat;
  background-size: 100% 100%, 10cqw 0.42cqw, 10cqw 0.42cqw;
  background-position: center, left center, right center;
}
.poster[data-style="aquarelle"] .p-meta {
  margin-top: 1.6cqw;
  color: #61483f;
}
/* Colonne d'ingrédients : lavis olive très pâle, coins irréguliers */
.poster[data-style="aquarelle"] .p-ingredients {
  position: relative;
  padding: 3.4cqw 3.2cqw;
}
.poster[data-style="aquarelle"] .p-ingredients::before {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(124, 139, 78, 0.14);
  border-radius: 4cqw 7cqw 3cqw 6cqw;
}
.poster[data-style="aquarelle"] .p-ingredients > * {
  position: relative;
}
.poster[data-style="aquarelle"] .p-rubrique {
  font-weight: 700;
  letter-spacing: 0.14em;
  color: var(--p-olive-fonce);
  border-bottom: none;
  padding-bottom: 0;
  margin-bottom: 0;
  text-align: center;
}
.poster[data-style="aquarelle"] .p-etapes .p-rubrique {
  color: var(--p-terracotta-texte);
  text-align: left;
}
.poster[data-style="aquarelle"] .p-rubrique::after {
  content: "";
  display: block;
  width: 11cqw;
  height: 0.55cqw;
  border-radius: 1cqw;
  background: var(--p-abricot);
  margin: 1.4cqw auto 1.7cqw;
  opacity: 0.85;
}
.poster[data-style="aquarelle"] .p-etapes .p-rubrique::after {
  margin-left: 0;
}
/* Ingrédients : filets olive entre les lignes, pas de puces */
.poster[data-style="aquarelle"] .p-marqueur {
  display: none;
}
.poster[data-style="aquarelle"] .p-ingredients ul {
  gap: 0;
}
.poster[data-style="aquarelle"] .p-ingredients li {
  padding: 1.1cqw 0;
}
.poster[data-style="aquarelle"] .p-ingredients li + li {
  border-top: 0.28cqw solid rgba(94, 107, 57, 0.2);
}
.poster[data-style="aquarelle"] .p-qte {
  font-family: var(--p-titre);
  font-style: italic;
  color: var(--p-olive-fonce);
}
/* Pastilles d'étape : contours organiques, trois pigments en alternance */
.poster[data-style="aquarelle"] .p-num {
  background: var(--p-accent);
  color: var(--p-creme);
  border-radius: 52% 48% 46% 54% / 48% 52% 48% 52%;
  width: 4.2cqw;
  height: 4.2cqw;
}
.poster[data-style="aquarelle"] .p-etapes li:nth-child(3n + 2) .p-num {
  background: var(--p-accent2);
  border-radius: 48% 52% 54% 46% / 52% 46% 54% 48%;
}
.poster[data-style="aquarelle"] .p-etapes li:nth-child(3n) .p-num {
  background: var(--p-accent3);
  border-radius: 54% 46% 50% 50% / 46% 54% 46% 54%;
}
/* Pied : le secret dans un lavis abricot aux coins irréguliers */
.poster[data-style="aquarelle"] .p-secret {
  position: relative;
  align-self: center;
  max-width: 94%;
  padding: 1.9cqw 3cqw;
  font-style: normal;
  text-align: left;
}
.poster[data-style="aquarelle"] .p-secret::before {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(228, 169, 79, 0.2);
  border-radius: 5cqw 2.6cqw 6cqw 3.4cqw;
}
.poster[data-style="aquarelle"] .p-secret > * {
  position: relative;
}
.poster[data-style="aquarelle"] .p-secret strong {
  font-family: var(--p-titre);
  font-weight: 700;
  color: var(--p-terracotta-texte);
}
.poster[data-style="aquarelle"] .p-dressage {
  font-style: italic;
  color: #61483f;
}
.poster[data-style="aquarelle"] .p-manuscrit {
  transform: rotate(-1.1deg);
  border-radius: 1.4cqw;
  box-shadow: 0 0.5cqw 1.8cqw rgba(62, 44, 42, 0.18);
}

/* ════ Univers 4 — Chic parisien ══════════════════════════ */
/* Gravure au trait : noir K100 seul, aucun gris d'aplat, cadre à filet double,
   symétrie axiale, capitales espacées, chiffres romains d'étape. */
.poster[data-style="chic"] {
  --p-bg: #f6f6f4;
  --p-ink: #111111;
  --p-accent: #111111;
  --p-accent2: #767b7e;
  --p-accent3: #c9cbca;
  --p-gris: #767b7e;
  --p-titre: "Bodoni Moda", Georgia, serif;
  --p-corps: "Archivo", system-ui, sans-serif;
}
/* le filet double du cadre de page : un fin dehors, un gras dedans */
.poster[data-style="chic"]::before {
  content: "";
  position: absolute;
  inset: 3.2cqw;
  border: 0.18cqw solid var(--p-ink);
  pointer-events: none;
}
.poster[data-style="chic"]::after {
  content: "";
  position: absolute;
  inset: 3.9cqw;
  border: 0.45cqw solid var(--p-ink);
  pointer-events: none;
}
.poster[data-style="chic"] .poster-inner {
  padding: 7.4cqw 7.6cqw 6.4cqw;
  gap: 2.6cqw;
}
.poster[data-style="chic"] .p-dedicace {
  font-family: var(--p-titre);
  font-style: italic;
  text-transform: none;
  letter-spacing: 0.02em;
  font-size: 3.2cqw;
  opacity: 1;
  order: 2;
  margin: 1.4cqw 0 0;
}
.poster[data-style="chic"] .p-entete {
  display: flex;
  flex-direction: column;
}
.poster[data-style="chic"] .p-titre {
  order: 1;
  font-weight: 500;
  font-size: 7.6cqw;
  line-height: 1.12;
  letter-spacing: 0.045em;
  text-transform: uppercase;
}
.poster[data-style="chic"] .p-meta {
  order: 3;
  margin-top: 2.6cqw;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  font-size: 2.2cqw;
  font-weight: 500;
  gap: 1cqw 3cqw;
}
.poster[data-style="chic"] .p-meta-item svg {
  display: none;
}
.poster[data-style="chic"] .p-meta::after {
  content: "";
  display: block;
  width: 18cqw;
  height: 0.14cqw;
  background: var(--p-accent3);
  margin: 2.4cqw auto 0;
}
.poster[data-style="chic"] .p-rubrique {
  text-align: center;
  letter-spacing: 0.3em;
  font-family: var(--p-corps);
  font-weight: 500;
  font-size: 2.5cqw;
  border-bottom: none;
  padding-bottom: 0;
}
.poster[data-style="chic"] .p-ingredients li .p-marqueur,
.poster[data-style="chic"] .p-ingredients li:nth-child(3n) .p-marqueur,
.poster[data-style="chic"] .p-ingredients li:nth-child(3n + 2) .p-marqueur {
  width: 2.6cqw;
  height: 3.25cqw; /* l'ovale du cartouche : rapport 1,25 */
  border-radius: 50%;
  background: transparent;
  border: 0.16cqw solid var(--p-ink);
  box-shadow: inset 0 0 0 0.28cqw var(--p-bg), inset 0 0 0 0.45cqw var(--p-ink);
}
.poster[data-style="chic"] .p-qte {
  font-weight: 400;
  color: var(--p-gris);
}
.poster[data-style="chic"] .p-num {
  width: auto;
  height: auto;
  min-width: 3.4cqw;
  border-radius: 0;
  background: transparent;
  color: var(--p-ink);
  font-family: var(--p-titre);
  font-weight: 500;
  letter-spacing: 0.08em;
  justify-content: flex-start;
}
.poster[data-style="chic"] .p-secret {
  font-style: normal;
  text-align: center;
}
.poster[data-style="chic"] .p-secret strong {
  font-family: var(--p-titre);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  display: block;
  font-size: 2.02cqw;
  margin-bottom: 0.9cqw;
}
.poster[data-style="chic"] .p-dressage {
  color: var(--p-gris);
}
/* ── Le bas de la fiche prend la fonte du prénom ──────────────────────────
 * 21/08/2026, demande de Sebastian : la police où il y a écrit MAMAN, reprise
 * pour la marche à suivre et pour tout ce qui la suit. Le haut de l'affiche
 * était déjà en bodoni (titre, prénom en italique, chiffres romains) et le
 * corps en Archivo, une grotesque : la page changeait de famille en son
 * milieu, et sa moitié basse ne ressemblait plus à ce qui la vend. Passe en
 * bodoni tout ce qui est sous les ingrédients, les deux intertitres du bas
 * compris. Reste en Archivo la colonne des ingrédients, son intertitre
 * compris : c'est la seule frontière, et elle tombe là où la page se coupe
 * déjà en deux.
 *
 * Les corps montent de 8,7 %, et ce n'est pas un réglage à l'oeil : la bodoni
 * a une hauteur d'x de 0,46 là où Archivo est à 0,53, elle se lit donc 13 %
 * plus petite à corps égal. C'est exactement le facteur que le moteur applique
 * de son côté (kDe, 3-MOTEUR/lib/fontes.js) : sans lui, l'aperçu mentirait sur
 * le PDF acheté. Les intertitres, eux, sont composés en CAPITALES, donc leur
 * référence n'est pas la hauteur d'x mais la hauteur de capitale, 0,75 em
 * contre 0,69 : ils DESCENDENT de 8 %, sinon ils ne répondraient plus à
 * INGRÉDIENTS resté en face.
 *
 * Cette règle vaut pour les trois agencements : aucun d'eux ne redéfinit le
 * corps des étapes ni celui du pied, ils ne touchent qu'aux colonnes.
 */
.poster[data-style="chic"] .p-etapes,
.poster[data-style="chic"] .p-pied {
  font-family: var(--p-titre);
}
.poster[data-style="chic"] .p-etapes .p-rubrique {
  font-family: var(--p-titre);
  font-size: 2.3cqw;
}
.poster[data-style="chic"] .p-etapes ol {
  font-size: 2.83cqw;
}
.poster[data-style="chic"] .p-secret {
  font-size: 2.93cqw;
}
.poster[data-style="chic"] .p-dressage {
  font-size: 2.61cqw;
}

/* ════ Agencements ════════════════════════════════════════
 * Un agencement, c'est le rôle donné aux illustrations. La frise latérale est
 * l'état par défaut du gabarit, elle n'a rien à redéfinir. Les deux autres
 * réorganisent la page sans toucher au DOM : c'est ce qui garantit que les
 * trois agencements valent pour les quatre univers.
 */

/* ── Bandeau haut : la frise en tête, les étapes en deux colonnes ── */
.poster[data-agencement="bandeau"] .poster-inner {
  gap: 2.4cqw;
}
.poster[data-agencement="bandeau"] .p-colonnes {
  display: flex;
  flex-direction: column;
  gap: 3cqw;
}
.poster[data-agencement="bandeau"] .p-entete {
  order: 2;
}
.poster[data-agencement="bandeau"] .p-colonnes {
  order: 3;
}
.poster[data-agencement="bandeau"] .p-pied {
  order: 4;
}
.poster[data-agencement="bandeau"] .p-ingredients {
  order: -1;
  margin-top: -1cqw;
}
.poster[data-agencement="bandeau"] .p-ingredients ul {
  flex-direction: row;
  flex-wrap: wrap;
  justify-content: center;
  gap: 1.4cqw 4cqw;
}
.poster[data-agencement="bandeau"] .p-ingredients li {
  flex: 0 1 auto;
}
.poster[data-agencement="bandeau"] .p-ingredients .p-rubrique,
.poster[data-agencement="bandeau"] .p-etapes .p-rubrique {
  text-align: center;
}
.poster[data-agencement="bandeau"] .p-etapes ol {
  columns: 2;
  column-gap: 4.5cqw;
  display: block;
}
.poster[data-agencement="bandeau"] .p-etapes li {
  break-inside: avoid;
  margin-bottom: 2.1cqw;
}

/* ── Couronne : les ingrédients encerclent le bloc de titre ──
 *
 * 18/08/2026. Un client a reçu cette composition avec son TITRE EN BAS de la
 * page, sous les étapes et sous le mot de dressage. Ce n'était pas un caprice
 * du navigateur, c'était écrit là : `.p-entete` portait `order: 2` pour venir
 * se poser entre les ingrédients et les étapes, mais `order` ne classe qu'entre
 * FRÈRES, et `.p-entete` n'est pas le frère des ingrédients. Il est le frère de
 * `.p-colonnes`, qui les contient. Dans `.poster-inner` il se retrouvait donc
 * seul avec un ordre 2 face à des frères restés à 0, c'est-à-dire dernier.
 * L'intention (ingrédients en haut, titre au milieu, étapes en dessous) était
 * juste, la hiérarchie l'empêchait.
 *
 * `display: contents` efface la boîte de `.p-colonnes` sans toucher au HTML :
 * ses deux enfants deviennent frères de `.p-entete` et les trois ordres jouent
 * enfin les uns contre les autres. Le pied passe à 4, sinon son ordre 0 le
 * ferait remonter en tête. Le `gap` de `.p-colonnes` disparaît avec sa boîte,
 * celui de `.poster-inner` le remplace.
 *
 * Ce brouillon n'est servi que lorsque le moteur ne répond pas, mais c'est
 * précisément le moment où il compte : c'est la seule image que le client voit
 * alors, et elle doit ressembler à ce qu'il achète. */
.poster[data-agencement="couronne"] .poster-inner {
  gap: 2.6cqw;
  justify-content: center;
}
.poster[data-agencement="couronne"] .p-colonnes {
  display: contents;
}
.poster[data-agencement="couronne"] .p-entete {
  order: 2;
  padding: 0 12cqw;
}
.poster[data-agencement="couronne"] .p-pied {
  order: 4;
}
.poster[data-agencement="couronne"] .p-ingredients {
  order: 1;
}
.poster[data-agencement="couronne"] .p-etapes {
  order: 3;
}
.poster[data-agencement="couronne"] .p-ingredients ul {
  flex-direction: row;
  flex-wrap: wrap;
  justify-content: center;
  gap: 1.5cqw 4.4cqw;
}
.poster[data-agencement="couronne"] .p-ingredients li {
  flex: 0 1 auto;
}
.poster[data-agencement="couronne"] .p-ingredients .p-rubrique,
.poster[data-agencement="couronne"] .p-etapes .p-rubrique {
  text-align: center;
}
.poster[data-agencement="couronne"] .p-etapes ol {
  columns: 2;
  column-gap: 4.5cqw;
  display: block;
}
.poster[data-agencement="couronne"] .p-etapes li {
  break-inside: avoid;
  margin-bottom: 2cqw;
}
.poster[data-agencement="couronne"] .p-titre {
  font-size: 7cqw;
}

/* ── Slot d'illustration d'ingrédient ─────────────────────
 * La bibliothèque détourée du moteur, réduite pour le navigateur. Quand
 * l'ingrédient n'a pas d'illustration, le marqueur historique reprend sa place.
 */
.p-ill { flex-shrink: 0; width: 7.4cqw; height: 7.4cqw; object-fit: contain;
  display: block; align-self: center; mix-blend-mode: multiply; }
.poster[data-agencement="bandeau"] .p-ill,
.poster[data-agencement="couronne"] .p-ill { width: 9cqw; height: 9cqw; }
.poster[data-agencement="bandeau"] .p-ingredients li,
.poster[data-agencement="couronne"] .p-ingredients li {
  flex-direction: column; align-items: center; text-align: center; gap: 0.6cqw;
  width: 17%; }
.poster[data-agencement="bandeau"] .p-ingredients li .p-qte,
.poster[data-agencement="couronne"] .p-ingredients li .p-qte {
  display: block; }
.poster[data-style="chic"] .p-ill { mix-blend-mode: multiply; }

/* ── Ajustement automatique ───────────────────────────────
 * Le gabarit est dessiné pour un 30 x 40 lu de près. Dans une vignette, ou
 * quand la recette est longue, le contenu déborde de la cage. Le composeur
 * mesure et pose --k : la page est composée plus grande puis ramenée à
 * l'échelle, ce qui fait entrer le texte sans le tronquer.
 */
.poster-inner {
  inset: auto; top: 0; left: 0;
  width: calc(100% / var(--k, 1));
  height: calc(100% / var(--k, 1));
  transform: scale(var(--k, 1));
  transform-origin: top left;
}
/* la colonne des étapes doit pouvoir se mesurer : sans min-height auto, son
   débordement reste invisible au composeur. */
.p-colonnes, .p-etapes, .p-ingredients { overflow: hidden; }

/* Le texte de l'ingrédient doit tenir sa place à côté de l'illustration, et le
   fond des gravures chic, qui sont sur blanc, doit se fondre dans le papier. */
.poster-inner { background: var(--p-bg); }
.p-ingredients li { align-items: center; }
.p-ingredients li > span:last-child { flex: 1 1 auto; min-width: 0; }
.p-ill { flex: 0 0 auto; }

/* L'illustration se dimensionne sur le corps du texte, pas en unités de
   conteneur : c'est ce qui garantit qu'elle reste proportionnée à la ligne
   quelle que soit la taille de l'aperçu. */
.p-ill { width: 2.8em !important; height: 2.8em !important; }
.poster[data-agencement="bandeau"] .p-ill,
.poster[data-agencement="couronne"] .p-ill { width: 3.4em !important; height: 3.4em !important; }

/* Les intertitres sont composés en capitales espacées : dans une colonne
   étroite, ils dépassent. On leur rend l'air nécessaire plutôt que de les
   couper. */
.p-ingredients, .p-etapes { overflow: visible; }
.p-rubrique { letter-spacing: 0.1em; font-size: 3cqw; }
.p-colonnes { overflow: hidden; }

/* Le rappel de l'affiche sur la page de paiement : on ne paie pas ce qu'on ne
   voit pas. Classe nouvelle, elle ne redéfinit rien du site. */
.rappel { display: grid; grid-template-columns: 190px 1fr; gap: 26px;
  align-items: center; margin: 0 0 30px; }
@media (max-width: 620px) { .rappel { grid-template-columns: 1fr; gap: 18px; } }
.rappel .cadre { padding: 8px; }

/* ── Aperçu rendu par le serveur ────────────────────────────────────────── */
.apercu-serveur { position: relative; }
.apercu-serveur .apercu-img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 2px;
  box-shadow: 0 18px 48px rgba(29, 47, 79, .16);
  /* Ni glisser, ni « enregistrer l'image sous » au premier réflexe. */
  -webkit-user-drag: none;
  user-select: none;
  pointer-events: none;
}
.apercu-attente { opacity: .72; transition: opacity .2s; }

/* La toile qui remplace l'image de l'aperçu depuis le 14/08/2026. Une <canvas>
   n'offre ni « enregistrer l'image sous », ni glisser-déposer, ni appui long
   qui propose la sauvegarde sur mobile. Voir assets/apercu-garde.js. */
.apercu-serveur .apercu-toile {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 2px;
  box-shadow: 0 18px 48px rgba(29, 47, 79, .16);
  -webkit-user-drag: none;
  -webkit-touch-callout: none;
  user-select: none;
  pointer-events: none;
}

/* Le masquage de garde, sur les gestes qui précèdent une capture. Il est
   nommé et réversible d'un clic : une page qui blanchit sans explication passe
   pour une panne, et une panne coûte la vente. `visibility` plutôt que
   `display` pour que le cadre garde sa taille et que rien ne saute. */
.apercu-masque { position: relative; }
.apercu-masque > * { visibility: hidden !important; }
.apercu-masque::after {
  content: attr(data-masque);
  position: absolute;
  inset: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 18px;
  background: #F3F0E7;
  border: 1px dashed #B9AE93;
  border-radius: 2px;
  font: 600 13px/1.5 Archivo, Arial, Helvetica, sans-serif;
  letter-spacing: .04em;
  color: #6A5B3F;
}

/* `hidden` est un attribut, pas une règle : `img { display: block }` posé plus
   haut dans la feuille du site le neutralise. La bascule démonstration vers
   recette du visiteur qui l'exigeait sur l'accueil a été retirée le 14/08/2026
   (la vitrine ne montre plus jamais l'affiche du visiteur), mais la règle sert
   partout ailleurs et reste. */
[hidden] { display: none !important; }

/* L'aperçu vivant dans le cadre de la scène 2 : il occupe le cadre entier,
   ratio compris, comme l'image qu'il remplace. */
.demo .cadre > [data-affiche] { width: 100%; }
.demo .cadre > [data-affiche] .poster { width: 100%; }

/* Le contrôle de longueur : ce que le client doit voir pendant qu'il saisit,
   et non après avoir payé. Le ton reste celui d'un conseil, pas d'une erreur :
   la recette n'est pas fausse, c'est la page qui a une taille. */
.alerte-longueur {
  margin: 14px 0 0; padding: 11px 14px;
  background: #FDF3E3; border-left: 3px solid #C8802E;
  font-size: 13.5px; line-height: 1.5; color: #6A4A18;
}
.alerte-longueur[data-bloquant="1"] { background: #FBEDE9; border-left-color: #C8402E; color: #7A2A1C; }
[data-set-a].indisponible { opacity: .42; }
[data-set-a].indisponible b::after { content: " · too long"; font-weight: 400; opacity: .8; }
.inerte { opacity: .45; pointer-events: none; }


/* ═══════════════════════════════════════════════════════════════════════
   14/08/2026 — L'ATTENTE, LES CASES, ET LA CAISSE QUI ENCAISSE
   Trois défauts vus par le client sur /creer/, corrigés ensemble parce
   qu'ils se tenaient : l'aperçu se corrigeait tout seul sous ses yeux, les
   cases légales n'étaient pas cochables, et le bouton de paiement menait à
   une deuxième caisse au lieu de Stripe.
   ═══════════════════════════════════════════════════════════════════════ */

/* L'annonce d'attente. Elle occupe EXACTEMENT la place de l'affiche à venir
   (ratio 3/4, celui du 30 x 40) : sans ça, la page saute au moment où l'image
   arrive, et un saut de mise en page au moment où le client regarde est pire
   que l'attente elle-même. */
.apercu-encours {
  position: relative;
  aspect-ratio: 3 / 4;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  padding: 20px;
  text-align: center;
  background: #F5F2EA;
  border: 1px solid rgba(29, 47, 79, .14);
  overflow: hidden;
}
/* Le lent balayage clair : il dit que quelque chose travaille, sans tourner
   comme un sablier, qui suggère l'attente longue. */
.apercu-encours::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(105deg,
    rgba(255, 255, 255, 0) 38%,
    rgba(255, 255, 255, .72) 50%,
    rgba(255, 255, 255, 0) 62%);
  transform: translateX(-100%);
  animation: apercu-balayage 1.9s ease-in-out infinite;
}
@keyframes apercu-balayage { to { transform: translateX(100%); } }
.apercu-encours-trait {
  position: relative;
  display: block;
  width: 34px;
  height: 2px;
  background: #5C87A6;
}
.apercu-encours-mot {
  position: relative;
  font-family: "Oswald", sans-serif;
  font-weight: 600;
  font-size: 11px;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: #6B7A8C;
  line-height: 1.6;
}
/* LE REFUS N'EST PAS UNE ATTENTE (23/08/2026).
   Même boîte, mais le balayage s'arrête : quelque chose qui balaye promet que
   ça va arriver, et ici rien n'arrivera tant que la recette n'a pas raccourci.
   Le trait passe au rouge de la vitrine, c'est le seul signal de refus du
   site. */
.apercu-refus::before { animation: none; background: none; }
.apercu-refus .apercu-encours-trait { background: #C8402E; }
.apercu-refus .apercu-encours-mot { color: #C8402E; }
.apercu-refus-quoi {
  position: relative;
  max-width: 26em;
  /* Aucune famille declaree : cette phrase est du texte courant, elle herite
     du Source Sans 3 de la page comme tout le reste. Le mot au-dessus garde
     l'Oswald capitale des etiquettes. */
  font-size: 13px;
  line-height: 1.55;
  letter-spacing: 0;
  text-transform: none;
  color: #6B7A8C;
}
.apercu-refus.mini .apercu-refus-quoi { display: none; }

/* Dans une vignette de 165 px, la phrase entière ne rentre pas et le trait
   mangerait la moitié de la hauteur. */
.apercu-encours.mini { gap: 8px; padding: 8px; }
.apercu-encours.mini .apercu-encours-trait { width: 18px; height: 1.5px; }
.apercu-encours.mini .apercu-encours-mot { font-size: 8px; letter-spacing: .12em; }

@media (prefers-reduced-motion: reduce) {
  .apercu-encours::before { animation: none; }
}

/* Les cases légales. C'étaient des carrés dessinés (`.legal i`, un bord et
   rien dedans) : on ne pouvait pas les cocher, donc on ne pouvait pas payer,
   et la page n'en disait rien. Ce sont maintenant de vraies cases, dans un
   `<label>`, donc cochables aussi en cliquant le texte. */
.legal { cursor: pointer; }
.legal input[type="checkbox"] {
  width: 15px; height: 15px;
  flex: none;
  margin: 2px 0 0;
  accent-color: #E8683C;
  cursor: pointer;
}
.legal:hover span { opacity: 1; }
.legal a { color: inherit; }

/* Où l'affiche est livrée. Le bloc est neuf : la vitrine ne demandait pas
   l'email, elle laissait le tunnel le demander. Elle encaisse maintenant
   elle-même, donc c'est ici que ça se passe. */
.livraison {
  margin-top: 24px;
  padding: 18px 20px;
  background: #FBF8F1;
  border: 1px solid rgba(29, 47, 79, .16);
}
.livraison .intertitre { margin-bottom: 12px; }
.livraison-duo { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
@media (max-width: 620px) { .livraison-duo { grid-template-columns: 1fr; } }
.livraison-duo label { display: block; }
.livraison-duo label > span {
  display: block;
  font-family: "Oswald", sans-serif;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: .15em;
  text-transform: uppercase;
  color: #1D2F4F;
  opacity: .72;
  margin-bottom: 6px;
}
.livraison-duo label > span em { font-style: normal; font-weight: 400; opacity: .75; }
.livraison-duo input {
  width: 100%;
  font: inherit;
  font-size: 16px;      /* sous 16 px, iOS zoome au focus et casse la page */
  padding: 11px 12px;
  color: #1D2F4F;
  background: #fff;
  border: 1.5px solid rgba(29, 47, 79, .3);
  border-radius: 0;
}
.livraison-duo input:focus {
  outline: none;
  border-color: #E8683C;
  box-shadow: 0 0 0 3px rgba(232, 104, 60, .16);
}

/* « Vouliez-vous dire… ». Un doute, pas un refus : `client@gmial.com` est une
   adresse valide qui n'appartient à personne, et le client ne le découvrirait
   qu'en ne recevant rien. On le lui montre pendant qu'il est encore là, dans
   le ton de l'avertissement et pas de l'erreur, et la correction reste un
   geste qu'il fait lui-même. */
.doute-email {
  margin: 10px 0 0;
  font-size: 13.5px;
  line-height: 1.5;
  color: #7A5210;
}
.doute-email-oui {
  font: inherit;
  font-weight: 700;
  color: #7A5210;
  background: #FCF3DF;
  border: 1px solid rgba(122, 82, 16, .35);
  border-radius: 0;
  padding: 2px 7px;
  cursor: pointer;
}
.doute-email-oui:hover { background: #F7E7C0; }
.doute-email-oui:focus-visible { outline: 2px solid #E8683C; outline-offset: 2px; }

/* Le refus, nommé. Un bouton qui ne réagit pas passe pour une panne : quand
   la caisse refuse, elle dit laquelle des conditions manque. */
.refus-caisse {
  margin: 16px 0 0;
  padding: 12px 14px;
  background: #FBEDE9;
  border-left: 3px solid #C8402E;
  font-size: 13.5px;
  line-height: 1.5;
  color: #7A2A1C;
}
.refus-caisse b {
  display: block;
  font-family: "Oswald", sans-serif;
  font-size: 11px;
  letter-spacing: .16em;
  text-transform: uppercase;
  margin-bottom: 6px;
}
.refus-caisse ul { margin: 0; padding-left: 18px; }
.refus-caisse li + li { margin-top: 4px; }

/* Le bouton pendant l'ouverture de la session Stripe. Le libellé d'attente est
   posé PAR-DESSUS, en CSS, et non écrit dans le bouton : le total y vit dans
   un `<span data-total>` que la peinture réécrit en continu, et le remplacer
   le supprimerait pour de bon. */
.bouton.occupe { position: relative; color: transparent; cursor: progress; }
.bouton.occupe > * { visibility: hidden; }
.bouton.occupe::after {
  content: attr(data-occupe);
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
}
.bouton:disabled { cursor: default; transform: none; }

/* Le filigrane CSS de la boîte s'efface devant l'image du moteur.
   Il servait quand l'aperçu venait du navigateur. Sur une image dont le
   filigrane est cuit dans les pixels — non périodique, à deux polarités, plus
   une gondole — il ne protège plus rien : il ne fait que doubler la marque et
   rendre l'affiche illisible, donc invendable. C'est le « c'est moche » du
   14/08. Il reste actif sur le repli, qui est bien une composition du DOM. */
.porte:has(> .apercu-serveur) > .filigrane,
.porte-serveur > .filigrane { display: none; }

/* Une vignette de 165 px ne porte pas l'ombre portée du grand aperçu : le
   cadre blanc l'a déjà, et la doubler empâte les quatre choix. */
.bandeau .apercu-serveur .apercu-toile,
.rappel .apercu-serveur .apercu-toile { box-shadow: none; }

/* ── L'APERÇU TIENT DANS L'ÉCRAN, MÊME À 100 % DE ZOOM ──────────────────
 *
 * 18/08/2026, constaté par Sebastian. La colonne d'aperçu de /creer/ ne
 * connaissait que sa LARGEUR : `.colle` était collante, plafonnée à 500 px,
 * et l'affiche prenait la hauteur que son format 3/4 impose, soit 667 px,
 * plus les quatre vignettes d'univers et les trois boutons de mise en page.
 * Environ 850 px de colonne, sous un décalage collant de 86 px. Sur un écran
 * de portable à 100 % de zoom il reste moins de 800 px : le bas de l'affiche
 * passait sous le pli, et c'est précisément le bas qui porte les derniers
 * ingrédients et le mot de dressage. Le client dézoomait pour voir ce qu'il
 * achète, ou n'en voyait qu'une partie.
 *
 * PREMIÈRE TENTATIVE, RETIRÉE LE JOUR MÊME. Elle passait `.colle` en colonne
 * flex et donnait `flex: 1 1 auto` à l'affiche pour qu'elle prenne la place
 * restante. Elle a fait exactement l'inverse : `flex-grow` ne distribue que
 * l'espace LIBRE d'un conteneur à hauteur définie, or `.colle` n'avait qu'un
 * `max-height`, donc sa hauteur restait dictée par son contenu et il n'y avait
 * aucun espace à distribuer. L'affiche est tombée de 500 x 667 à 265 x 353 sur
 * un écran de 1920. Mesuré par `build/banc-creer.mjs`, dans un vrai navigateur,
 * ce qu'aucune relecture de CSS n'aurait dit.
 *
 * CE QUI EST FAIT À LA PLACE, et qui ne touche à aucune structure : l'affiche
 * est en 3/4, donc sa largeur vaut 0,75 fois sa hauteur. Il suffit de borner la
 * LARGEUR de la colonne par la place verticale disponible. Rien ne devient
 * flex, rien ne peut se replier à zéro, et le pire cas est une colonne un peu
 * étroite, jamais une colonne absente.
 *
 * Les 440 px retirés sont mesurés, pas estimés : 86 px de décalage collant, et
 * 345 px que prennent les quatre vignettes, leurs libellés, les trois boutons
 * de mise en page et la légende sous l'aperçu. Le plancher de 360 px existe
 * parce que sous cette largeur l'affiche n'est plus lisible : sur un portable
 * court, mieux vaut faire défiler de cent pixels que montrer une vignette.
 * `svh` plutôt que `vh` : sur mobile, `vh` compte la barre d'adresse rétractée
 * et déborde tant qu'elle est là. Sous 1000 px la mise en page passe sur une
 * seule colonne et la question ne se pose plus : la règle s'arrête là. */
@media (min-width: 1001px) {
  .ecran .droite .colle {
    width: min(500px, max(360px, calc((100svh - 440px) * 0.75)));
  }
}

/* ═══════════════════════════════════════════════════════════════════════
   15/08/2026 — LE BANDEAU DE PARRAINAGE

   Il dit au filleul, AVANT qu'il saisisse quoi que ce soit, que sa
   première affiche est à 2,95 €. C'est tout l'enjeu : une remise que le
   client découvre à l'écran de paiement n'a persuadé personne, elle n'a
   fait que réduire une facture qu'il avait déjà acceptée. Posé par
   `assets/parrainage.js`, absent de la page quand il n'y a pas de code.
   ═══════════════════════════════════════════════════════════════════════ */
.parrain {
  margin: 0 0 18px;
  padding: 12px 16px;
  background: #F2F6F0;
  border-left: 3px solid #7C8B4E;
  border-radius: 2px;
  font-size: 14px;
  line-height: 1.5;
  color: #3F4A32;
}
.parrain b { font-weight: 700; }
.parrain strong { font-weight: 700; color: #33221D; }


/* ═══════════════════════════════════════════════════════════════════════
   18/08/2026 — LA CASE QUI MANQUE, DITE LA OU ELLE SE REMPLIT
   « Voir ma recette illustrée » partait sans rien vérifier. Un temps de
   préparation vide donne une affiche bancale une page plus loin, parce
   que le moteur refuse la recette et que la page retombe sur sa
   composition de secours. Le refus se voit maintenant ici : le champ
   rougit, le bouton ne part pas, et la page remonte sur la case.
   ═══════════════════════════════════════════════════════════════════ */
.refus-atelier {
  margin: 0 0 16px;
  padding: 12px 14px;
  background: #FBEDE9;
  border-left: 3px solid #C8402E;
  font-size: 13.5px;
  line-height: 1.5;
  color: #7A2A1C;
  text-align: left;
}
.refus-atelier b {
  display: block;
  font-family: "Oswald", sans-serif;
  font-size: 11px;
  letter-spacing: .16em;
  text-transform: uppercase;
  margin-bottom: 6px;
}
.refus-atelier[hidden] { display: none; }

/* Le champ en faute. Le rouge tient tant que la case est vide, et le
   halo ne bat qu'une fois : il attire l'oeil a l'arrivee, il ne
   clignote pas pendant qu'on tape dedans. */
.champ2 input.en-faute,
.ligne input.en-faute,
.pas textarea.en-faute {
  border-bottom: 1.5px solid #C8402E;
  background: #FBEDE9;
  animation: faute-halo .9s ease-out 1;
}
.champ2 input.en-faute::placeholder,
.ligne input.en-faute::placeholder,
.pas textarea.en-faute::placeholder { color: #C8402E; opacity: .55; }
.champ2:has(input.en-faute) label { color: #C8402E; opacity: 1; }
@keyframes faute-halo {
  from { box-shadow: 0 0 0 6px rgba(200, 64, 46, .22); }
  to   { box-shadow: 0 0 0 6px rgba(200, 64, 46, 0); }
}
@media (prefers-reduced-motion: reduce) {
  .champ2 input.en-faute,
  .ligne input.en-faute,
  .pas textarea.en-faute { animation: none; }
}

/* Le champ vise est ramene sous l'en-tete collant, jamais dessous. */
.champ2, .ligne, .pas { scroll-margin-top: 96px; }


/* ═══════════════════════════════════════════════════════════════════════
   18/08/2026 — LA SCENE DE /creer/ : L'AFFICHE AU CENTRE, EN UN ECRAN
   Retour Sebastian : « la première chose qu'on doit voir c'est l'affiche
   qu'on a générée, et la possibilité de changer ». L'ancien écran mettait
   le texte à gauche et l'image à droite, à demi sortie du cadre : on
   arrivait sur un titre. Le présentoir tient maintenant dans une hauteur
   d'écran, l'affiche est au milieu, les douze combinaisons juste dessous.
   La taille de l'affiche n'est plus commandée par sa largeur mais par la
   hauteur qui reste : c'est la seule façon qu'elle tienne toujours.
   ═══════════════════════════════════════════════════════════════════ */
/* Portee LIMITEE a la section de /creer/ (20/08/2026) : `.scene` nu attrapait
   aussi les trois scenes de la demonstration de l'accueil (`.demo .scene`), qui
   heritaient alors d'une hauteur d'ecran ; leur `aspect-ratio:3/4` la
   retransformait en largeur, et le bloc Bim-Bam-Boum sortait de la page.
   Le selecteur porte donc l'element : sur /creer/ la scene est une <section>,
   sur l'accueil c'est un <div>. */
section.scene {
  margin-top: 0;
  border-top: none;
  padding: 0;
  background: var(--c);
  color: #FCFBF7;
  /* La hauteur d'ecran se pose ICI, sur la section entiere, et non sur son
     contenu : l'onglet « 1 · Votre image » est un enfant a part, et le
     compter en dur dans un calc() faisait deborder le bas de 32 px. La
     section se partage en colonne, l'onglet prend ce qu'il faut, le reste
     va au contenu. */
  height: calc(100svh - 63px);
  min-height: 620px;
  display: flex;
  flex-direction: column;
}
/* `display: flex` d'auteur l'emporte sur le `display: none` que le navigateur
   attache a [hidden] : sans cette ligne, l'ecran 1 resterait affiche sous la
   caisse au passage a l'etape 2. */
section.scene[hidden] { display: none; }
section.scene .onglet {
  position: static;
  transform: none;
  display: block;
  width: max-content;
  margin: 0 auto;
  background: #FCFBF7;
  color: #1D2F4F;
  padding: 7px 24px 6px;
}
.scene-in {
  /* Le contenu prend tout ce que l'onglet n'a pas pris. `min-height: 0` est
     ce qui l'autorise a etre plus court que son contenu, donc a distribuer
     une hauteur exacte au plateau. */
  flex: 1 1 auto;
  min-height: 0;
  /* `width: 100%` n'est pas decoratif : dans une colonne flexible, une marge
     laterale `auto` ANNULE l'etirement, et la boite se reduisait alors a la
     largeur de son contenu (mesure : 620 px au lieu de 1128, affiche a 183 px
     de large). La largeur explicite la remet a plat. */
  width: 100%;
  max-width: 1180px;
  margin: 0 auto;
  padding: clamp(14px, 2.2vh, 26px) 26px clamp(16px, 2.4vh, 30px);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(8px, 1.4vh, 18px);
}
.scene-tete { text-align: center; flex: none; }
.scene-titre {
  font-family: "Anton", "Oswald", sans-serif;
  font-weight: 400;
  font-size: clamp(24px, 3.4vh, 40px);
  line-height: 1;
  letter-spacing: .005em;
  text-transform: uppercase;
  margin: 0;
  color: #FCFBF7;
}
.scene-recette {
  font-family: "Oswald", sans-serif;
  font-weight: 500;
  font-size: clamp(12px, 1.6vh, 15px);
  letter-spacing: .14em;
  text-transform: uppercase;
  margin: 6px 0 0;
  opacity: .82;
}
section.scene .parrain { margin: 0 0 8px; }

/* Le plateau : les univers a gauche, l'affiche au centre, les mises en page
   a droite. Trois colonnes plutot qu'une pile, pour une raison mesuree : en
   pile, les quatre vignettes mangeaient 270 px de hauteur et il ne restait que
   337 px a l'affiche sur un ecran de 900. Posees sur les cotes, elles ne
   coutent plus rien a la hauteur, l'affiche double, et la largeur qui etait
   vide sert enfin a quelque chose. */
.scene-plateau {
  flex: 1 1 auto;
  min-height: 240px;
  width: 100%;
  display: grid;
  grid-template-columns: minmax(132px, 196px) minmax(0, 1fr) minmax(118px, 168px);
  /* La rangee prend TOUTE la hauteur du plateau : c'est elle qui donne sa
     hauteur definie a la colonne du milieu, donc sa taille a l'affiche. */
  grid-template-rows: minmax(0, 1fr);
  gap: clamp(16px, 2.4vw, 40px);
}
/* Les deux colonnes de reglages se centrent sur la hauteur de l'affiche,
   l'affiche, elle, s'etire. */
.scene-colonne { align-self: center; min-width: 0; }
.scene-etiquette {
  font-family: "Oswald", sans-serif;
  font-weight: 600;
  font-size: 9.5px;
  letter-spacing: .17em;
  text-transform: uppercase;
  margin: 0 0 10px;
  opacity: .7;
  text-align: center;
}
/* Les quatre univers en carre plutot qu'en colonne : quatre vignettes
   empilees demandent 700 px de haut, le carre en demande 300, et le plateau
   n'en a que 580 sur un ecran de 900. */
.scene-univers .bandeau { grid-template-columns: repeat(2, 1fr); gap: 10px; margin-top: 0; }
.scene-univers .bandeau p { font-size: 9px; margin-top: 5px; }
.scene-pages .rangagencement { grid-template-columns: 1fr; gap: 8px; margin-top: 0; }
.scene-pages .rangagencement label { padding: 11px 6px; }
/* La jauge du mur d'apercus, posee par mur.js juste apres les mises en page.
   Sa mise en ligne (une puce, puis le texte) ne tient pas dans une colonne de
   160 px : elle passe en bloc, et la puce disparait. */
.scene-pages .mur-jauge {
  display: block;
  color: #FCFBF7;
  opacity: .72;
  margin-top: 14px;
  font-size: 9px;
  line-height: 1.55;
  text-align: center;
}
.scene-pages .mur-jauge i { display: none; }

/* L'affiche. Sa taille n'est plus commandee par sa largeur mais par la hauteur
   qui reste, sans quoi elle deborde de l'ecran des que la fenetre est basse.
   Le positionnement absolu est ce qui rend cette hauteur DEFINIE : dans une
   simple boite flexible, le navigateur calcule d'abord la largeur d'apres le
   contenu et `aspect-ratio` arrive trop tard. Mesure du 18/08 avant la
   correction, contenu vide : 0 px de large sur 0 de haut. */
.scene-image { position: relative; height: 100%; min-height: 0; }
.scene-image .porte {
  position: absolute;
  inset: 0;
  margin: auto;
  height: 100%;
  width: auto;
  aspect-ratio: 3 / 4;
  max-width: 100%;
  background: transparent;
  cursor: zoom-in;
  box-shadow: 0 30px 60px -28px rgba(11, 24, 44, .55);
}
.scene-image .porte > [data-affiche] {
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}
.scene-image .apercu-toile,
.scene-image .apercu-img {
  height: 100%;
  width: 100%;
  object-fit: contain;
  box-shadow: none;
  border-radius: 0;
}
.scene-image .apercu-encours { height: 100%; width: 100%; aspect-ratio: auto; }
.scene-image .poster { height: 100%; }

/* Le bouton. Sur le bleu du présentoir, `var(--c)` serait bleu sur bleu :
   il passe en crème, et c'est la seule tache claire pleine de l'écran. */
.scene-pied { flex: none; width: 100%; max-width: 420px; text-align: center; }
.scene-veux {
  background: #FCFBF7;
  color: #1D2F4F;
  padding: 15px 30px;
  font-size: 15px;
  transition: transform .13s, box-shadow .13s;
  box-shadow: 0 12px 26px -14px rgba(11, 24, 44, .7);
}
.scene-veux:hover { transform: translateY(-2px); box-shadow: 0 18px 32px -14px rgba(11, 24, 44, .75); }
.scene-veux.inerte { background: rgba(252, 251, 247, .45); }
.scene-mention {
  font-family: "Oswald", sans-serif;
  font-size: 10.5px;
  letter-spacing: .14em;
  text-transform: uppercase;
  margin: 10px 0 0;
  opacity: .78;
}
.scene-mention a { text-decoration: underline; }
section.scene .alerte-longueur { flex: none; width: 100%; max-width: 620px; margin: 0; }
.scene-desamorce {
  flex: none;
  color: #FCFBF7;
  opacity: .62;
  max-width: 620px;
  margin: 0;
  text-align: center;
  font-size: 11.5px;
  line-height: 1.4;
}

/* Sous 900 px de haut, le texte de désamorce et le sous-titre mangent la
   place de l'affiche : ils partent, l'affiche reste. */
@media (max-height: 780px) {
  .scene-desamorce { display: none; }
  .scene-image .jauge { font-size: 8.5px; letter-spacing: .1em; padding: 3px 7px; }
  .scene-univers .bandeau p { font-size: 8.5px; }
}
@media (max-height: 640px) {
  .scene-recette { display: none; }
}

/* Téléphone : on ne force plus l'écran unique, une affiche de 3/4 dans une
   hauteur de 600 px donnerait un timbre-poste. Elle reprend sa largeur, et
   la page redevient une page qui défile. */
@media (max-width: 900px) {
  .scene { height: auto; min-height: 0; }
  .scene-in { padding: 18px 20px 30px; gap: 16px; }
  .scene-plateau {
    grid-template-columns: 1fr;
    min-height: 0;
    gap: 16px;
    max-width: 460px;
    margin: 0 auto;
  }
  /* L'affiche repasse devant les reglages et reprend sa largeur : un 3/4
     contraint par la hauteur d'un telephone donnerait un timbre-poste. */
  .scene-image { order: -1; position: static; height: auto; }
  /* `relative` et non `static` : la mention « apercu basse definition » est
     posee en absolu DANS la porte, et une porte non positionnee la renvoyait
     en bas de page, sous le pied. */
  .scene-image .porte {
    position: relative;
    inset: auto;
    height: auto;
    width: 100%;
    aspect-ratio: 3 / 4;
    margin: 0;
  }
  .scene-univers .bandeau { grid-template-columns: repeat(4, 1fr); }
  .scene-pages .rangagencement { grid-template-columns: repeat(3, 1fr); }
  .scene-etiquette { text-align: left; }
  .scene-desamorce { display: block; }
  .scene-recette { display: block; }
}

/* ═══════════════════════════════════════════════════════════════════════
   20/08/2026 — TELEPHONE : L'AFFICHE RECOUVRAIT LE STYLE ET LA MISE EN PAGE
   Retour Sebastian, capture a l'appui : « dans l'image avec l'apercu de la
   recette on ne peut pas changer de style ni de configuration ».
   Les reglages etaient bien la, sous l'affiche, mais dessous au sens propre :
   l'affiche leur passait par-dessus. Deux causes, toutes deux dans le bloc
   telephone du 18/08, qui croyait avoir range la scene en pile.
   1. `.scene { height: auto }` ne pouvait pas gagner contre le
      `section.scene { height: calc(100svh - 63px) }` pose plus haut : une
      classe seule ne bat pas un element plus une classe. La section gardait
      donc une hauteur d'ecran, et le plateau la hauteur qui restait.
   2. Le plateau gardait `grid-template-rows: minmax(0, 1fr)`, qui n'a de sens
      qu'en trois colonnes. En pile, la premiere rangee - l'affiche - recevait
      ce qui restait apres les reglages, soit 158 px mesures a 390 px de large,
      pendant que la porte, elle, tenait son 3/4 sur 350 px de large, donc 466
      px de haut. L'affiche debordait de 308 px, exactement de quoi couvrir
      « Le style » et « La mise en page ».
   Mesure avant : plateau 463 px, affiche 158, bandeau des styles a y=425 sous
   une affiche qui descend a y=692. Les deux lignes ci-dessous suffisent.
   ═══════════════════════════════════════════════════════════════════ */
@media (max-width: 900px) {
  section.scene { height: auto; min-height: 0; }
  .scene-plateau { grid-template-rows: auto; }
  /* `1fr` vaut `minmax(auto, 1fr)` : une colonne ne descend pas sous la
     largeur minimale de son contenu. Tant que l'apercu n'est pas revenu du
     moteur, la vignette porte le mot « COMPOSITION », large de 85 px, et les
     quatre colonnes reclamaient 373 px pour 350 disponibles : le quatrieme
     style sortait de l'ecran, donc redevenait inatteignable, le defaut meme
     qu'on repare ici. `minmax(0, 1fr)` rend aux colonnes le droit de retrecir. */
  .scene-univers .bandeau { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .scene-pages .rangagencement { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
