/* ============================================================
   UMEA LAB — expérience scroll type "Watches & Wonders"
   Vert velours, blanc, noir ; casquettes en couleur.
   ============================================================ */

@font-face {
  font-family: "Dahlia";
  src: url("../fonts/Dahlia-BoldCondensed.otf") format("opentype");
  font-weight: 700;
  font-display: swap;
}
@font-face {
  font-family: "Naskh";
  src: url("../fonts/Naskh.ttf") format("truetype");
  font-display: swap;
}

:root {
  /* Trois couleurs : vert velours foncé, blanc, noir.
     (échantillonnés dans la photo d'accueil) */
  --velours:      #0b2320;  /* vert velours foncé */
  --velours-deep: #051413;  /* le creux des plis */
  --velours-lit:  #0e4a3c;  /* le velours éclairé : sert d'accent */

  /* du blanc (fonds) au noir (encre) */
  --g-950: #ffffff;
  --g-900: #fafafa;
  --g-850: #f2f2f3;
  --g-800: #e4e4e6;  /* bordures */
  --g-700: #d3d3d6;
  --g-600: #adadb2;
  --g-500: #86868c;
  --g-400: #56565c;  /* texte secondaire */
  --g-300: #2e2e33;
  --g-200: #19191c;
  --g-100: #111114;  /* texte courant */
  --g-50:  #0a0a0c;  /* noir : titres */

  /* le pied de page : marron cuir, dans le ton des cadres */
  --brun:      #2c1a13;
  --brun-deep: #1d100b;
  --brun-lit:  #5c3d2a;

  /* accent de la marque */
  --red: var(--velours-lit);
  --red-deep: var(--velours);

  /* Mouvement : deux courbes pour tout le site, et rien d'autre. La
     première pour ce qui entre en scène (départ franc, longue traîne),
     la seconde pour les objets d'interface. */
  --ease-scene: cubic-bezier(0.22, 0.61, 0.24, 1);
  --ease-doux:  cubic-bezier(0.4, 0, 0.2, 1);

  --display: "Dahlia", "Arial Narrow", sans-serif;
  --body: ui-sans-serif, system-ui, -apple-system, "Helvetica Neue", sans-serif;
}

* { margin: 0; padding: 0; box-sizing: border-box; }

html {
  /* Aucun calage : le scroll est entièrement libre, la page ne glisse
     jamais toute seule vers l'écran suivant. C'est la barre de
     progression, à droite, qui dit où l'on en est. */
  scroll-snap-type: none;
  /* Sans ça, un débordement de quelques pixels (arrondi de police, cadre
     collé à un bord) élargit tout le viewport horizontal sur mobile :
     bande blanche visible en haut et sur tout le flanc droit.
     « clip » et non « hidden » : hidden (sans overflow-y déclaré à côté)
     promeut silencieusement l'axe vertical en scroll auto, ce qui casse
     position:sticky pour tout ce qui suit dans la page (la galerie
     mobile, notamment) — clip ne crée pas de conteneur de défilement. */
  overflow-x: clip;
}

body {
  /* Le fond du <body> n'est jamais visible en usage normal (chaque
     écran pose sa propre couleur), sauf à deux endroits sur iOS : le
     rebond élastique en haut/bas de page, et la zone sous l'encoche.
     Safari peint ces deux zones avec CETTE couleur, pas avec celle de
     l'écran affiché — un fond blanc y créait donc des bandes blanches
     alors que le site est entièrement sombre.
     --velours-deep plutôt que --velours : la photo d'accueil est plus
     sombre sur ses bords (vignette) que le vert plat de la charte,
     échantillonné à l'écran c'est --velours-deep qui s'en rapproche. */
  background: var(--velours-deep);
  color: var(--g-100);
  font-family: var(--body);
  transition: background-color 0.4s ease;
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  /* voir le commentaire sur html : clip, pas hidden, pour ne pas casser
     position:sticky plus bas dans la page */
  overflow-x: clip;
}

::selection { background: var(--red); color: #fff; }

img, svg { max-width: 100%; display: block; }

/* ---------- Freins à la récupération des visuels ----------
   À dire clairement : on NE PEUT PAS empêcher la copie d'une image
   affichée dans un navigateur. Une capture d'écran suffit toujours, et
   les fichiers restent lisibles dans l'onglet réseau. Ce qui suit ne
   sert qu'à écarter les gestes faciles — glisser-déposer, clic droit
   « enregistrer l'image », appui long sur mobile. La vraie protection
   est ailleurs : en-têtes anti-hotlinking (.htaccess) et définition
   servie juste suffisante pour l'écran, jamais les fichiers d'atelier. */
img {
  -webkit-user-drag: none;
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
}
a { color: inherit; text-decoration: none; }
.red { color: var(--red); }

.cap-svg {
  width: 100%;
  height: auto;
  aspect-ratio: 800 / 620;
}

/* ---------- Chrome fixe (nav, compteur, points) ---------- */
.nav {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 100;
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  /* + l'encoche : en haut en portrait, sur les côtés en paysage. Sans
     ça, la marque déborde sous la découpe et le fond du <body> (blanc)
     se voit dans la zone que le safe-area réserve. */
  padding-top: calc(22px + env(safe-area-inset-top, 0px));
  padding-right: calc(clamp(20px, 4vw, 48px) + env(safe-area-inset-right, 0px));
  padding-bottom: 22px;
  padding-left: calc(clamp(20px, 4vw, 48px) + env(safe-area-inset-left, 0px));
  pointer-events: none;
}
.nav > * { pointer-events: auto; }

/* Le monogramme « ul » dans l'angle haut droit — raccourci vers le
   contact. Même dessin que l'icône d'onglet. */
.mark {
  grid-column: 3;
  justify-self: end;
  font-family: var(--display);
  font-size: 24px;
  letter-spacing: 0.08em;
  color: var(--red);
  transition: transform 0.3s ease, opacity 0.3s ease;
}
.mark:hover { transform: translateY(-2px); opacity: 0.75; }

/* nom de la marque, centré en haut */
.logo {
  grid-column: 2;
  justify-self: center;
  font-family: var(--display);
  font-size: 37px;
  letter-spacing: 0.045em;
  color: var(--red);
  /* même traitement que sur l'écran de chargement : le tracé est
     épaissi, la seule graisse de Dahlia ne suffisant pas */
  -webkit-text-stroke: 0.9px currentColor;
  paint-order: stroke fill;
  transition: transform 0.3s ease;
}
.logo:hover { transform: scale(1.06); }

/* ---------- Invitation au survol ----------
   Elle ne paraît que sur la galerie et disparaît pour de bon au premier
   effleurement : une invitation qui insiste serait une gêne. */
.invite {
  position: fixed;
  left: 50%;
  bottom: clamp(26px, 4.6vh, 46px);
  transform: translateX(-50%);
  z-index: 100;
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 9px 17px 9px 12px;
  border-radius: 999px;
  background: rgba(5, 20, 19, 0.42);
  border: 1px solid rgba(255, 255, 255, 0.18);
  backdrop-filter: blur(9px);
  color: rgba(255, 255, 255, 0.9);
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.6s var(--ease-doux), visibility 0.6s,
              transform 0.6s var(--ease-scene);
}
/* visible seulement dans la galerie, et seulement tant qu'on n'a pas
   compris — la classe « comprise » est posée en JS au premier survol */
body.sur-galerie:not(.survol-compris) .invite {
  opacity: 1;
  visibility: visible;
}
/* au doigt, l'invitation reste : c'est la même interaction, on la
   nomme seulement autrement (le texte est posé en JS) */

/* le petit curseur qui tourne : il montre le geste au lieu de le dire */
.invite-rond {
  position: relative;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.35);
  flex: 0 0 auto;
}
.invite-point {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 5px;
  height: 5px;
  margin: -2.5px;
  border-radius: 50%;
  background: #ffffff;
  box-shadow: 0 0 8px rgba(255, 255, 255, 0.8);
  animation: rondeSurvol 3.4s var(--ease-doux) infinite;
}
/* le point décrit un petit tour, comme une main qui balaie le tirage */
@keyframes rondeSurvol {
  0%, 100% { transform: translate(-4px, 3px); }
  25%      { transform: translate(4px, 3px); }
  50%      { transform: translate(4px, -3px); }
  75%      { transform: translate(-4px, -3px); }
}

.counter {
  position: fixed;
  left: calc(clamp(20px, 4vw, 48px) + env(safe-area-inset-left, 0px));
  bottom: calc(30px + env(safe-area-inset-bottom, 0px));
  z-index: 100;
  font-family: var(--display);
  font-size: 15px;
  letter-spacing: 0.14em;
  color: var(--g-400);
}
.counter b { color: var(--g-50); font-weight: 700; font-size: 22px; }
.counter .sep { color: var(--red); margin: 0 6px; }

/* ---------- Barre de progression (flanc droit) ----------
   Un filet blanc qui se remplit au fil du scroll ; un éclat le
   parcourt en boucle pour qu'il scintille même à l'arrêt. */
.progres {
  position: fixed;
  right: calc(clamp(16px, 2.4vw, 34px) + env(safe-area-inset-right, 0px));
  top: 50%;
  transform: translateY(-50%);
  z-index: 100;
  height: min(46vh, 380px);
  padding: 10px 8px;          /* la zone cliquable déborde du filet */
  cursor: pointer;
}
.progres-piste {
  position: relative;
  width: 2px;
  height: 100%;
  border-radius: 2px;
  background: rgba(255, 255, 255, 0.22);
  overflow: visible;
}
.progres-jauge {
  position: absolute;
  inset: 0 0 auto 0;
  height: 0%;                  /* piloté en JS */
  border-radius: 2px;
  background: #ffffff;
  box-shadow: 0 0 10px rgba(255, 255, 255, 0.55);
  transition: height 0.18s linear;
}
/* la tête de la jauge : un point qui palpite */
.progres-jauge::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: 0;
  width: 7px; height: 7px;
  border-radius: 50%;
  background: #ffffff;
  transform: translate(-50%, 50%);
  box-shadow: 0 0 12px rgba(255, 255, 255, 0.9);
  animation: pouls 3.6s ease-in-out infinite;
}
/* l'éclat qui descend le long du filet, sur toute sa hauteur : c'est
   lui qui fait scintiller la barre même à l'arrêt */
.progres-eclat {
  position: absolute;
  left: -1px;
  top: -34%;
  pointer-events: none;
  width: 4px;
  height: 34%;
  border-radius: 4px;
  background: linear-gradient(rgba(255, 255, 255, 0), #ffffff, rgba(255, 255, 255, 0));
  filter: blur(1.5px);
  animation: scintille 5.4s ease-in-out infinite;
}
@keyframes scintille {
  0%   { top: -34%; opacity: 0; }
  22%  { opacity: 0.95; }
  78%  { opacity: 0.95; }
  100% { top: 100%; opacity: 0; }
}
@keyframes pouls {
  0%, 100% { box-shadow: 0 0 8px rgba(255, 255, 255, 0.55); opacity: 0.85; }
  50%      { box-shadow: 0 0 16px rgba(255, 255, 255, 1); opacity: 1; }
}
/* sur les écrans clairs, le blanc ne se verrait plus : on lui donne
   une ombre portée et une piste sombre */
body:not(.on-dark) .progres-piste { background: rgba(0, 0, 0, 0.14); }
body:not(.on-dark) .progres-jauge {
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.22), 0 0 10px rgba(0, 0, 0, 0.28);
}
body:not(.on-dark) .progres-jauge::after {
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.25), 0 0 12px rgba(0, 0, 0, 0.3);
  animation: none;
}
/* sur fond clair, l'éclat blanc ne se verrait pas : il passe en sombre */
body:not(.on-dark) .progres-eclat {
  background: linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.55), rgba(0, 0, 0, 0));
}
body.at-end .progres { opacity: 0; visibility: hidden; }
.progres { transition: opacity 0.5s ease, visibility 0.5s; }
/* sur l'écran de contact, le rebond élastique / la zone sous l'encoche
   doivent montrer le marron du pied de page, pas le vert du reste.
   --brun-deep plutôt que --brun : le pied de page n'est pas un aplat,
   son dégradé assombrit précisément les bords — --brun-deep est le
   ton qu'on y trouve réellement, --brun tout court y paraît trop clair. */
body.at-end { background: var(--brun-deep); }

/* Une fois le chargement fini, le chrome ne surgit pas d'un bloc :
   il se pose dans l'ordre où l'œil le lit. */
.nav, .counter, .progres, .scroll-cue {
  opacity: 0;
  transition: opacity 0.9s var(--ease-doux);
}
body.loaded .nav        { opacity: 1; transition-delay: 0.25s; }
body.loaded .counter    { opacity: 1; transition-delay: 0.42s; }

/* L'ACCUEIL NE PORTE AUCUN TEXTE. Seuls « us. » et le nom restent —
   le bouton de commande et le compteur attendent l'écran suivant, et
   le repère de défilement n'est plus qu'un trait. */
body.a-l-accueil .counter { opacity: 0; visibility: hidden; }

body.loaded .progres    { opacity: 1; transition-delay: 0.58s; }
body.loaded .scroll-cue { opacity: 1; transition-delay: 0.74s; }

.scroll-cue {
  position: fixed;
  bottom: calc(30px + env(safe-area-inset-bottom, 0px));
  right: calc(clamp(20px, 4vw, 48px) + env(safe-area-inset-right, 0px));
  z-index: 100;
  display: flex;
  align-items: center;
  transition: opacity 0.5s;
}
.scroll-cue::after {
  content: "";
  width: 1px; height: 34px;
  background: linear-gradient(var(--red), transparent);
  animation: drip 1.8s ease-in-out infinite;
}
@keyframes drip {
  0% { transform: scaleY(0); transform-origin: top; }
  50% { transform: scaleY(1); transform-origin: top; }
  51% { transform-origin: bottom; }
  100% { transform: scaleY(0); transform-origin: bottom; }
}
body.at-end .scroll-cue,
body:not(.a-l-accueil) .scroll-cue { opacity: 0; visibility: hidden; }

/* ---------- Slides ---------- */
.slide {
  position: relative;
  min-height: 100svh;
  overflow: hidden;
  display: grid;
  place-items: center;
  isolation: isolate;
  background: var(--slide-bg, var(--g-950));
}

/* décor : halos flous colorés */
.blob {
  position: absolute;
  border-radius: 50%;
  filter: blur(90px);
  opacity: 0.5;
  z-index: -2;
  transform: translateZ(0);
}
.grain-line {
  position: absolute;
  inset: auto 0 0 0;
  height: 1px;
  background: var(--g-800);
}

/* motifs flottants du décor */
.floaters { position: absolute; inset: 0; z-index: -1; pointer-events: none; }
.floaters svg {
  position: absolute;
  opacity: 0;
  transition: opacity 1.2s ease 0.5s;
  animation: floatDrift 9s ease-in-out infinite;
}
.slide.active .floaters svg { opacity: 0.85; }
.floaters svg:nth-child(2n) { animation-duration: 12s; animation-delay: -3s; }
.floaters svg:nth-child(3n) { animation-duration: 15s; animation-delay: -6s; }
@keyframes floatDrift {
  0%, 100% { transform: translate(0, 0) rotate(-6deg); }
  50% { transform: translate(14px, -26px) rotate(8deg); }
}

/* contenu du slide */
.slide-inner {
  position: relative;
  width: min(860px, 92vw);
  text-align: center;
  padding: 90px 0 70px;
}
.slide .kicker {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.34em;
  text-transform: uppercase;
  color: var(--g-400);
  opacity: 0;
  transform: translateY(20px);
  transition: all 0.7s ease 0.1s;
}
.slide .kicker .n { color: var(--red); }
.slide.active .kicker { opacity: 1; transform: none; }

.slide .desc {
  max-width: 460px;
  margin: 10px auto 0;
  color: var(--g-300);
  font-size: 15.5px;
  opacity: 0;
  transform: translateY(24px);
  transition: all 0.8s ease 0.45s;
}
.slide.active .desc { opacity: 1; transform: none; }
.slide .desc strong { color: var(--g-50); }

.slide .meta {
  margin-top: 18px;
  display: inline-flex;
  align-items: center;
  gap: 14px;
  opacity: 0;
  transform: translateY(20px);
  transition: all 0.8s ease 0.6s;
}
.slide.active .meta { opacity: 1; transform: none; }
.pill {
  font-size: 10.5px;
  font-weight: 800;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  padding: 8px 16px;
  border-radius: 999px;
  border: 1px solid var(--g-600);
  color: var(--g-300);
}
.pill.sold { background: var(--red); border-color: var(--red); color: #fff; }
.swatches { display: flex; gap: 6px; }
.swatches i {
  width: 13px; height: 13px;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.25);
}

/* ---------- ACCUEIL ----------
   Un écran comme les autres. Pas de scène collée, pas de course de
   plusieurs écrans, aucune transition propre : la galerie arrive en
   défilant par-dessous, exactement comme la commande arrive après la
   galerie et le contact après la commande. Un seul geste pour tout le
   site. Et aucun texte : le nom et « us. » sont dans l'en-tête. */
.slide.hero {
  display: block;
  padding: 0;
  overflow: hidden;
  /* La toute première section : on l'étire sous l'encoche avec sa
     propre photo plutôt que de laisser Safari combler cette bande
     avec la couleur plate du <body>. Il faut agrandir min-height, pas
     ajouter du padding : .hero-bg est en inset:0 (calé sur la boîte de
     padding quel que soit le padding, donc un padding seul ne le
     bougerait pas), et 100svh dominait de toute façon un padding aussi
     petit qu'un safe-area-inset. La marge négative annule le surplus
     de hauteur pour que le bas de la section ne bouge pas — seul le
     haut remonte sous l'encoche. */
  min-height: calc(100svh + env(safe-area-inset-top, 0px));
  margin-top: calc(-1 * env(safe-area-inset-top, 0px));
}
.slide.hero .hero-bg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  opacity: 0;
  transform: scale(1.06);
  filter: blur(20px);
  transition:
    opacity 1.15s var(--ease-scene) 0.05s,
    transform 2.1s var(--ease-scene) 0.05s,
    filter 1.5s var(--ease-scene) 0.05s;
}
/* Attention à l'ordre : l'écran de chargement couvre la page pendant
   plus de deux secondes. Accrochée à .active seule, la mise au point se
   jouerait dessous et personne ne la verrait. */
body.loaded .slide.hero.active .hero-bg {
  opacity: 1;
  transform: none;
  filter: blur(0);
  transition-delay: 0.15s;
}

/* thème clair (hero) : le chrome fixe s'adapte */
/* sur une slide sombre (le hero), le chrome fixe passe en blanc */
body.on-dark .logo,
body.on-dark .mark { color: #ffffff; }
body.on-dark .counter { color: rgba(255,255,255,0.7); }
body.on-dark .counter b { color: #ffffff; }
body.on-dark .counter .sep { color: rgba(255,255,255,0.6); }
body.on-dark .scroll-cue { color: rgba(255,255,255,0.75); }
body.on-dark .scroll-cue::after { background: linear-gradient(#ffffff, transparent); }

/* ============================================================
   GALERIE HORIZONTALE — le scroll vertical fait glisser les
   panneaux latéralement (le conteneur reste collé à l'écran).
   ============================================================ */
.rail {
  position: relative;
  background: var(--velours-deep);
  height: calc(var(--panneaux, 5) * 100svh);
}
.rail-sticky { position: sticky; top: 0; height: 100svh; overflow: hidden; }
/* les panneaux sont empilés au même endroit : aucun n'en pousse un autre */
.rail-track { position: absolute; inset: 0; }
.rail-snaps { position: absolute; inset: 0; pointer-events: none; }
/* un cran par tirage : chaque bloc couvre sa part de la course */
.rail-snaps > div { height: calc(var(--panneaux, 3) * 100svh / 3); }

/* La scène est la boîte de référence du panneau : le décor la remplit,
   le tirage s'y place en fractions (--fx/--fy/--fw/--fh). Sur grand
   écran elle occupe tout l'écran ; en portrait elle devient une bande
   à la proportion de l'image (voir la passe mobile), ce qui permet de
   TOUT montrer sans rien rogner. */
.scene {
  position: absolute;
  inset: 0;
}

/* ---------- panneaux d'ambiance ----------
   --d  = distance signée au centre, en écrans (posée en JS)
   --ad = sa valeur absolue
   --entrant / --sortant = les deux moitiés de --d, déjà adoucies
   --fx/--fy/--fw/--fh = la place du tirage dans la scène (posée dans le
       HTML sur le panneau, pas sur la scène : le balayage a besoin de
       savoir où est le cadre pour changer de couleur en le traversant)
   --trainee = la couleur que prend la traînée sur le cadre

   LA TRANSITION EST DANS LE FOND. Les trois décors sont le même plan :
   même cadrage, lettrage UMEA au même endroit au pixel près — seule la
   couleur change. La scène suivante se découvre donc par un balayage
   franc, TOUJOURS de la gauche vers la droite, et comme le lettrage ne
   bouge pas, l'œil ne voit pas deux images se remplacer mais une seule
   qui change de couleur, lettre après lettre.

   Le balayage prend tout le panneau, tirage compris : le cadre est
   repeint dans le même geste que la salle, pas après. Ce qui change,
   c'est la couleur de la traînée : blanche sur la salle, et le temps
   qu'elle traverse le cadre, elle prend --trainee, la couleur de
   l'écran qu'on quitte. Le passage se voit, la coupe ne se voit pas.

   Le décor ne bouge pas d'un pixel : ni fondu, ni zoom, ni glissement.
   Toute translation décalerait le lettrage, c'est-à-dire tuerait l'effet. */
.panneau-image {
  /* 1 = scène encore cachée, 0 = salle entièrement repeinte */
  --balayage: clamp(0, (var(--entrant, 0) - 0.32) / 0.54, 1);
  /* la position du front, en fraction de l'écran */
  --front: calc(1 - var(--balayage));
  /* 1 quand le front est en train de traverser le cadre */
  --sur-cadre: clamp(0,
    min(var(--front) - var(--fx, 0), var(--fx, 0) + var(--fw, 1) - var(--front)) / 0.035, 1);
  padding: 0;
  background: transparent;
  clip-path: inset(0 calc(var(--balayage) * 100%) 0 0);
  will-change: clip-path;
}
/* le premier panneau ne se découvre pas — mais il lui faut quand même
   son propre plan, sinon son tirage passerait au-dessus du panneau
   suivant au lieu d'être recouvert par lui */
.premier { clip-path: inset(0); }

/* la traînée sur la salle : une lueur blanche posée sur le front. Le
   clip lui coupe la moitié avant, il ne reste que ce qui traîne
   derrière — d'où l'impression de matière. */
.panneau-image::before,
.panneau-image::after {
  content: "";
  position: absolute;
  left: calc(var(--front) * 100%);
  width: 13vw;
  margin-left: -6.5vw;
  pointer-events: none;
  /* elle s'allume et s'éteint aux deux bouts du passage */
  opacity: calc(min(1, var(--front) * 6) * min(1, var(--balayage) * 6));
}
.panneau-image::before {
  top: 0;
  bottom: 0;
  z-index: 3;
  mix-blend-mode: screen;
  background: linear-gradient(90deg,
    transparent,
    rgba(255, 255, 255, 0.16) 34%,
    rgba(255, 255, 255, 0.5) 50%,
    rgba(255, 255, 255, 0.16) 66%,
    transparent);
}
/* la traînée sur le cadre : même front, autre couleur. Elle passe
   au-dessus du tirage (z 6) et n'existe que le temps de la traversée. */
.panneau-image::after {
  top: calc(var(--fy, 0.25) * 100%);
  height: calc(var(--fh, 0.5) * 100%);
  z-index: 6;
  background: linear-gradient(90deg,
    transparent,
    color-mix(in srgb, var(--trainee, #0e4a3c) 62%, transparent) 34%,
    var(--trainee, #0e4a3c) 50%,
    color-mix(in srgb, var(--trainee, #0e4a3c) 62%, transparent) 66%,
    transparent);
  opacity: calc(min(1, var(--front) * 6) * min(1, var(--balayage) * 6)
                * var(--sur-cadre));
}
.premier::before, .premier::after { display: none; }

/* le décor : franc, immobile. */
.decor {
  position: absolute;
  inset: 0;
  z-index: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* ---- LE TIRAGE COMME UNE CARTE ----
   Il ne se contente plus d'être posé : il s'incline vers le curseur.
   --rx/--ry sont donnés en JS d'après la position de la souris dans le
   cadre. La perspective est portée par le panneau, sinon chaque cadre
   aurait son propre point de fuite et l'inclinaison serait fausse. */
.panneau-image { perspective: 1500px; }

.cadre {
  /* ---- L'EFFACEMENT DU TIRAGE SORTANT ----
     Les trois tirages n'occupent pas la même place : celui de l'écran 2
     est calé à droite, celui du 3 au centre, celui du 4 à gauche. Quand
     le balayage a fini de découvrir le tirage qui arrive, celui qu'on
     quitte dépasse donc encore d'un côté, et il traîne à l'écran alors
     qu'il n'a plus rien à y faire.
     On le retire à cet instant précis : dès que le front du balayage
     suivant a passé le bord droit du tirage suivant (--fin-suivant,
     posé sur le panneau dans le HTML), le nôtre s'efface en un souffle.
     Le balayage, lui, continue tranquillement de découvrir la salle.
     --fin-suivant vaut 2 par défaut : le dernier panneau n'a pas de
     suivant, donc son tirage ne s'efface jamais. */
  --front-suivant: calc(1 - clamp(0, (0.68 - var(--sortant, 0)) / 0.54, 1));
  --efface: clamp(0, (var(--front-suivant) - var(--fin-suivant, 2)) / 0.08, 1);
  opacity: calc(1 - var(--efface));
  transform: rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg))
             translateZ(var(--rz, 0px));
  transition: transform 320ms ease-out, box-shadow 320ms ease-out;
  position: absolute;
  z-index: 5;
  left: calc(var(--fx, 0.25) * 100%);
  top: calc(var(--fy, 0.25) * 100%);
  width: calc(var(--fw, 0.5) * 100%);
  height: calc(var(--fh, 0.5) * 100%);
  margin: 0;
  border-radius: 3px;
  overflow: hidden;
  background: var(--velours-deep);
  box-shadow:
    0 34px 78px rgba(0, 0, 0, 0.5),
    0 10px 26px rgba(0, 0, 0, 0.34);
  will-change: clip-path;
}
/* <picture> est un élément en ligne : sans cette règle, l'image à
   l'intérieur calculerait ses 100 % sur une boîte rétrécie au contenu */
.cadre picture {
  display: block;
  width: 100%;
  height: 100%;
}
.cadre img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* le lustre : le reflet qui court sur le tirage. Son foyer suit lui
   aussi le curseur (--lx/--ly), ce qui donne l'illusion d'une surface
   vernie sous une source de lumière. */
.lustre {
  position: absolute;
  inset: 0;
  pointer-events: none;
  /* deux foyers : un éclat serré qui suit le curseur de près, et un
     halo large qui donne le galbe. Le fond sombre creuse les bords, ce
     qui fait ressortir le relief quand la carte s'incline. */
  background-image:
    radial-gradient(circle at var(--lx, 50%) var(--ly, -20%),
      rgba(255, 255, 255, 0.42), rgba(255, 255, 255, 0) 34%),
    radial-gradient(circle at var(--lx, 50%) var(--ly, -20%),
      rgba(255, 255, 255, 0.16), rgba(0, 0, 0, 0.16) 72%);
  transition: background-image 320ms ease-out, opacity 320ms ease-out;
}

/* la carte réagit : elle se rapproche et son ombre s'ouvre */
@media (hover: hover) {
  .cadre.survole {
    transition-duration: 150ms;
    box-shadow:
      0 64px 130px rgba(0, 0, 0, 0.62),
      0 20px 46px rgba(0, 0, 0, 0.46),
      /* le liseré qui accroche la lumière sur l'arête haute */
      inset 0 1px 0 rgba(255, 255, 255, 0.34);
  }
  .cadre.survole .lustre { transition-duration: 150ms; }
}

/* chaque panneau occupe tout l'écran, au même endroit que ses voisins ;
   c'est leur contenu qui se déplace, jamais le panneau lui-même */
.panneau {
  position: absolute;
  inset: 0;
  display: block;
}

/* ============================================================
   DERNIER PANNEAU — L'OFFRE
   Un seul objectif : envoyer le visiteur en DM. Titre, prix,
   bouton ; tout le reste est du soutien à la décision.
   ============================================================ */
.slide.offre {
  /* Le contenu commande la hauteur : au moins un écran, davantage si le
     texte en a besoin. Le brider à un écran pile obligeait à tout
     resserrer, et l'écran de commande finissait tassé. */
  min-height: 100svh;
  /* la grille rend la course : le contenu occupe toute la hauteur de
     l'écran de commande au lieu de se tasser en haut */
  display: grid;
  place-items: stretch;
  align-content: stretch;
  padding: 0;
  background: var(--velours);
  color: #ffffff;
  overflow-x: hidden;
  overflow-y: auto;   /* filet : sur une fenêtre étroite et haute, rien n'est coupé */
}
.slide.offre::before {            /* matière velours, comme le pied de page */
  content: "";
  position: absolute;
  inset: 0;
  background:
    radial-gradient(120% 90% at 82% 22%, rgba(255, 255, 255, 0.09), transparent 62%),
    radial-gradient(90% 80% at 8% 96%, rgba(0, 0, 0, 0.42), transparent 68%);
  pointer-events: none;
}

.offre-inner {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: minmax(0, 1.08fr) minmax(340px, 0.92fr);
  align-items: stretch;
  gap: clamp(32px, 5.5vw, 96px);
  padding: clamp(84px, 12vh, 148px) clamp(24px, 5.5vw, 90px) clamp(64px, 9vh, 112px);
  max-width: 1560px;
  margin: 0 auto;
}
/* Les deux colonnes occupent toute la hauteur. À gauche, la promesse
   reste groupée en haut et les étapes descendent en pied de colonne :
   l'écran est rempli sans que le texte se disloque. */
.offre-gauche,
.offre-droite {
  display: flex;
  flex-direction: column;
  min-height: 0;
}
/* la colonne se lit d'un bloc, sans écart artificiel entre le haut et
   le bas : c'est ce qui creusait le vide. Les blocs ne se compriment
   pas — sinon la liste des étapes déborde de sa boîte et vient
   chevaucher les preuves. */
.offre-gauche { justify-content: center; }
.offre-gauche > * { flex-shrink: 0; }
.offre-droite { justify-content: center; }

/* --- colonne gauche : la promesse --- */
.offre-kicker {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.62);
}
.offre-titre {
  font-family: var(--display);
  font-size: clamp(42px, 6.1vw, 104px);
  line-height: 0.9;
  text-shadow: 0 18px 46px rgba(0, 0, 0, 0.42);
  margin: clamp(14px, 2.2vh, 26px) 0 clamp(20px, 3vh, 34px);
  color: #ffffff;
}
/* Les deux paragraphes sont de même rang : même corps, même
   interlignage. Le second était plus petit et plus pâle, ce qui se
   lisait comme une note de bas de page alors qu'il dit l'essentiel —
   comment la commande se passe. */
.offre-sous {
  max-width: 46ch;
  font-size: clamp(15.5px, 1.4vw, 20px);
  line-height: 1.75;
  color: rgba(255, 255, 255, 0.78);
}
.offre-sous strong { color: #ffffff; }
.offre-direct {
  margin-top: clamp(16px, 2.6vh, 30px);
  padding-top: clamp(16px, 2.6vh, 30px);
  border-top: 1px solid rgba(255, 255, 255, 0.14);
}

.offre-etapes {
  list-style: none;
  margin: clamp(28px, 4.4vh, 52px) 0 0;
  display: grid;
  gap: clamp(14px, 2.6vh, 28px);
  max-width: 520px;
}
.offre-etapes li {
  display: flex;
  align-items: center;
  gap: 16px;
  font-size: clamp(14px, 1.2vw, 18px);
  color: rgba(255, 255, 255, 0.88);
}
/* Le chiffre était en Dahlia : à cette taille la police perd ses
   contreformes et le « 1 » se lit mal. On repasse à la police de
   texte, un cran plus grande, et le rond gagne en présence. */
.offre-etapes span {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: clamp(30px, 2.6vw, 38px);
  height: clamp(30px, 2.6vw, 38px);
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.3);
  background: rgba(255, 255, 255, 0.07);
  font-family: var(--body);
  font-size: clamp(13px, 1.1vw, 15px);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0;
  color: #ffffff;
}

/* --- colonne droite : le prix et le bouton --- */
.offre-droite { gap: clamp(12px, 2vh, 20px); justify-content: center; }
.offre-carte {
  background: rgba(255, 255, 255, 0.055);
  border: 1px solid rgba(255, 255, 255, 0.2);
  border-radius: 4px;
  /* la carte est posée sur le velours, pas incrustée dedans */
  box-shadow:
    0 42px 90px rgba(0, 0, 0, 0.46),
    0 10px 26px rgba(0, 0, 0, 0.3),
    inset 0 1px 0 rgba(255, 255, 255, 0.12);
  padding: clamp(24px, 3.4vh, 46px) clamp(24px, 2.6vw, 44px);
  backdrop-filter: blur(2px);
  margin-bottom: clamp(10px, 1.6vh, 18px);
}
/* la tête de fiche : intitulé à gauche, mention de série à droite */
.carte-tete {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  padding-bottom: clamp(10px, 1.4vh, 16px);
  border-bottom: 1px solid rgba(255, 255, 255, 0.16);
}
.carte-serie {
  font-size: 10.5px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--velours-lit);
  background: rgba(255, 255, 255, 0.9);
  border-radius: 2px;
  padding: 4px 9px 3px;
  white-space: nowrap;
}
/* le prix et sa mention sur la même ligne de base */
.carte-prix {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 14px;
}
.carte-mention {
  font-size: 12px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.58);
  padding-bottom: 6px;
}
/* le cartel : quatre repères en deux colonnes, comme une étiquette
   d'atelier — c'est ce qui remplit la carte sans la bavarder */
.carte-fiche {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(14px, 2.2vh, 24px) clamp(16px, 2vw, 28px);
  margin: clamp(16px, 2.6vh, 28px) 0 clamp(14px, 2.2vh, 24px);
  padding: clamp(16px, 2.6vh, 26px) 0;
  border-top: 1px solid rgba(255, 255, 255, 0.14);
  border-bottom: 1px solid rgba(255, 255, 255, 0.14);
}
.carte-fiche dt {
  font-size: 9.5px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.5);
  margin-bottom: 3px;
}
/* Même raison : en Dahlia et en petit, « 59FIFTY » ou « 1 exemplaire »
   devenaient illisibles. La valeur passe en police de texte, la
   hiérarchie tient par la graisse et non par le dessin. */
.carte-fiche dd {
  font-family: var(--body);
  font-size: clamp(13.5px, 1.05vw, 15.5px);
  font-weight: 600;
  letter-spacing: 0.005em;
  color: #ffffff;
}

.offre-label {
  font-size: 10.5px;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.6);
}
.offre-prix {
  display: flex;
  align-items: baseline;
  gap: 6px;
  font-family: var(--display);
  color: #ffffff;
  line-height: 0.9;
  margin: clamp(6px, 1vh, 12px) 0 8px;
}
.prix-chiffre { font-size: clamp(52px, 5.6vw, 92px); letter-spacing: -0.015em; }
.prix-devise { font-size: clamp(28px, 3vw, 46px); }
.offre-prix-detail {
  font-size: 13.5px;
  line-height: 1.5;
  color: rgba(255, 255, 255, 0.66);
  max-width: 34ch;
}

.offre-cta {
  display: block;
  margin-top: clamp(18px, 2.8vh, 30px);
  text-align: center;
  background: #ffffff;
  color: var(--velours);
  font-family: var(--display);
  font-size: clamp(21px, 2.1vw, 31px);
  letter-spacing: 0.02em;
  padding: clamp(16px, 2.4vh, 26px) 22px;
  border-radius: 3px;
  position: relative;
  overflow: hidden;
  transition: transform 0.25s ease, box-shadow 0.3s ease;
  /* le bouton flotte au-dessus de la carte : c'est le seul objet du
     site à porter une ombre aussi franche */
  box-shadow: 0 18px 44px rgba(0, 0, 0, 0.42), 0 4px 10px rgba(0, 0, 0, 0.24);
}
.offre-cta::after {               /* reflet qui balaie le bouton */
  content: "";
  position: absolute;
  top: 0; bottom: 0;
  left: -60%;
  width: 45%;
  background: linear-gradient(100deg, transparent, rgba(11, 35, 32, 0.14), transparent);
  transform: skewX(-18deg);
  animation: refletCta 3.4s ease-in-out 1.2s infinite;
}
.offre-cta:hover { transform: translateY(-3px); box-shadow: 0 26px 54px rgba(0, 0, 0, 0.46); }
.offre-cta:active { transform: translateY(0); }
@keyframes refletCta {
  0%   { left: -60%; }
  55%  { left: 125%; }
  100% { left: 125%; }
}

.offre-rassure {
  margin-top: 12px;
  font-size: 13px;
  line-height: 1.5;
  color: rgba(255, 255, 255, 0.7);
  text-align: center;
}

.offre-gauche .offre-preuves {
  margin-top: clamp(26px, 4vh, 46px);
  columns: 2;
  column-gap: clamp(20px, 2.4vw, 40px);
}
.offre-gauche .offre-preuves li { break-inside: avoid; }
.offre-gauche .offre-preuves {
  margin-top: clamp(26px, 4vh, 46px);
  columns: 2;
  column-gap: clamp(20px, 2.4vw, 40px);
}
.offre-gauche .offre-preuves li { break-inside: avoid; }
.offre-preuves {
  list-style: none;
  margin: clamp(16px, 2.4vh, 24px) 0 0;
  padding-top: clamp(14px, 2vh, 20px);
  border-top: 1px solid rgba(255, 255, 255, 0.16);
  display: grid;
  gap: 8px;
}
.offre-preuves li {
  position: relative;
  padding-left: 22px;
  font-size: 13.5px;
  color: rgba(255, 255, 255, 0.78);
}
.offre-preuves li::before {
  content: "";
  position: absolute;
  left: 2px; top: 0.52em;
  width: 9px; height: 5px;
  border-left: 1.5px solid #ffffff;
  border-bottom: 1.5px solid #ffffff;
  transform: rotate(-45deg);
}

.offre-second {
  font-size: 13px;
  color: rgba(255, 255, 255, 0.6);
  text-align: center;
}
.offre-second a { color: #ffffff; text-decoration: underline; text-underline-offset: 3px; }

/* Le contenu de l'offre est visible d'entrée : l'animation en escalier
   (kicker, puis titre, puis texte...) faisait apparaître le prix et le
   bouton après le reste, dans un ordre qui semblait décousu au premier
   scroll. Tout arrive maintenant déjà en place, sans transition. */
.offre-kicker,
.offre-titre,
.offre-sous,
.offre-etapes,
.offre-preuves,
.offre-carte,
.offre-second {
  opacity: 1;
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  .offre-kicker, .offre-titre, .offre-sous, .offre-etapes,
  .offre-preuves, .offre-carte, .offre-second { opacity: 1; transform: none; }
  .offre-cta::after { animation: none; }
  .cadre { transform: none; }
  .decor { transform: none; }
  .panneau-image { clip-path: none; }
  .panneau-image::before,
  .panneau-image::after { opacity: 0; }
  .progres-eclat, .progres-jauge::after { animation: none; }
  /* sans mouvement, on saute directement à la scène de la galerie */
}

/* ============================================================
   ÉCRAN CONTACT — deux bandes qui tiennent sur un seul écran
   ============================================================ */
.slide.outro {
  min-height: 50svh;
  background: var(--brun);
  display: grid;
  grid-template-rows: 1fr;
  /* .slide centre ses enfants : ici les bandes doivent occuper toute la largeur */
  place-items: stretch;
  align-content: stretch;
  padding: 0;
}

/* --- bande haute : inscription --- */
.outro-top {
  position: relative;
  display: grid;
  align-items: center;
  padding: clamp(28px, 5vh, 64px) clamp(22px, 6vw, 90px);
  background: var(--brun);
  overflow: hidden;
}
.outro-top::after {           /* rappel de la matière */
  content: "";
  position: absolute;
  inset: 0;
  background:
    radial-gradient(120% 90% at 78% 30%, rgba(255,255,255,0.07), transparent 60%),
    radial-gradient(80% 70% at 10% 90%, rgba(0,0,0,0.35), transparent 65%);
  pointer-events: none;
}

/* le marron n'est pas un aplat : un peu de relief, comme le cuir */
.outro-bottom::before {
  content: "";
  position: absolute;
  inset: 0;
  background:
    radial-gradient(110% 80% at 82% 12%, rgba(255, 226, 198, 0.09), transparent 62%),
    radial-gradient(90% 80% at 6% 96%, var(--brun-deep), transparent 70%);
  pointer-events: none;
}
.outro-bottom > * { position: relative; z-index: 1; }
.outro-top-inner {
  position: relative;
  z-index: 1;
  max-width: 620px;
  opacity: 0;
  transform: translateY(22px);
  transition: opacity 0.9s ease 0.15s, transform 0.9s var(--ease-scene) 0.15s;
}
.slide.outro.active .outro-top-inner { opacity: 1; transform: none; }

.outro-title {
  font-family: var(--display);
  font-size: clamp(30px, 4vw, 52px);
  color: #ffffff;
  line-height: 1;
}
.outro-tag {
  margin-top: 10px;
  color: rgba(255, 255, 255, 0.66);
  font-size: 14px;
}

.contact-form {
  margin-top: clamp(16px, 2.6vh, 30px);
  display: grid;
  gap: clamp(12px, 2vh, 20px);
  max-width: 760px;
}
.champs-duo { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(14px, 3vw, 38px); }
.contact-form input,
.contact-form textarea {
  background: transparent;
  border: 0;
  border-bottom: 1px solid rgba(255, 255, 255, 0.28);
  color: #ffffff;
  font-family: var(--display);
  font-size: clamp(15px, 1.5vw, 19px);
  letter-spacing: 0.01em;
  padding: 6px 2px 9px;
  resize: none;
  transition: border-color 0.3s ease;
}
.contact-form input::placeholder,
.contact-form textarea::placeholder { color: rgba(255, 255, 255, 0.42); }
.contact-form input:focus,
.contact-form textarea:focus { outline: none; border-bottom-color: #ffffff; }

.form-bas { display: flex; align-items: center; gap: 18px; flex-wrap: wrap; }
.contact-form button {
  background: transparent;
  border: 1px solid rgba(255, 255, 255, 0.6);
  color: #ffffff;
  font-family: var(--body);
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  padding: 15px 40px;
  transition: all 0.3s ease;
}
.contact-form button:hover { background: #ffffff; color: var(--brun); }
.form-note { font-size: 12px; color: rgba(255, 255, 255, 0.65); min-height: 1em; }

/* --- bande basse : logo, liens, nom --- */
.outro-bottom {
  position: relative;
  background: var(--brun);
  color: #ffffff;
  display: block;
  /* les coordonnées viennent se loger juste sous le bord haut : le nom
     géant occupe le bas, rien ne flotte au milieu */
  padding: clamp(18px, 2.6vh, 30px) clamp(22px, 4vw, 54px) 0;
  overflow: hidden;
}
.outro-logo {
  display: block;
  width: clamp(78px, 11vw, 132px);
  aspect-ratio: 1;
  overflow: hidden;
  align-self: end;
  transition: transform 0.35s ease;
}
.outro-logo img { width: 100%; height: 100%; object-fit: cover; }
.outro-logo:hover { transform: translateY(-4px); }

.outro-liens {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 26px;
  flex-wrap: wrap;
}
.liens { display: flex; gap: clamp(22px, 4vw, 64px); color: #ffffff; flex-wrap: wrap; }
/* les trois contacts empilés */
.liens.colonne { flex-direction: column; gap: clamp(10px, 1.6vh, 18px); }
.liens a {
  display: grid;
  gap: 3px;
  transition: opacity 0.25s ease, transform 0.25s ease;
}
.liens a:hover { opacity: 0.65; transform: translateY(-2px); }

/* appel depuis le bouton « Nous contacter » */
/* appel depuis « Nous contacter » : le texte s'illumine, sans cadre */
.liens a.surligne .pseudo { animation: brilleTexte 2.6s ease; }
.liens a.surligne .reseau { animation: brilleLabel 2.6s ease; }
@keyframes brilleTexte {
  0%, 100% { text-shadow: none; }
  8%, 76% {
    text-shadow: 0 0 12px rgba(255, 255, 255, 0.95),
                 0 0 34px rgba(255, 255, 255, 0.6),
                 0 0 60px rgba(255, 255, 255, 0.35);
  }
}
@keyframes brilleLabel {
  0%, 100% { color: rgba(255, 255, 255, 0.55); }
  8%, 76% { color: #ffffff; }
}
.reseau {
  font-size: 10.5px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.55);
}
.pseudo {
  font-family: var(--display);
  font-size: clamp(16px, 1.8vw, 23px);
  letter-spacing: 0.01em;
}
.copyright { font-size: 13px; color: rgba(255, 255, 255, 0.6); }

/* le nom, aligné en bas comme une signature */
.outro-nom {
  text-shadow: 0 22px 60px rgba(0, 0, 0, 0.5);
  position: absolute;
  right: clamp(18px, 3vw, 46px);
  bottom: 4px;
  margin: 0;
  font-family: var(--display);
  font-size: clamp(56px, 14vw, 210px);
  line-height: 0.84;
  color: #ffffff;
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  transform: translateY(24px);
  transition: opacity 1s ease 0.35s, transform 1s var(--ease-scene) 0.35s;
}
.slide.outro.active .outro-nom {
  text-shadow: 0 22px 60px rgba(0, 0, 0, 0.5); opacity: 1; transform: none; }

/* ---------- Curseur étoile ----------
   blanche par défaut, cœur vert velours sur ce qui est cliquable */
html, body { cursor: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='28' height='28' viewBox='0 0 28 28'%3E%3Cpath d='M14 1 C15.1 8.4 19.6 12.9 27 14 C19.6 15.1 15.1 19.6 14 27 C12.9 19.6 8.4 15.1 1 14 C8.4 12.9 12.9 8.4 14 1 Z' fill='%23ffffff' stroke='%230b2320' stroke-width='1.1'/%3E%3C/svg%3E") 14 14, auto; }
a, button, input, textarea, label, .progres, .logo, .mark { cursor: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='36' height='36' viewBox='0 0 36 36'%3E%3Cpath d='M18 2 C19.4 9.9 26.1 16.6 34 18 C26.1 19.4 19.4 26.1 18 34 C16.6 26.1 9.9 19.4 2 18 C9.9 16.6 16.6 9.9 18 2 Z' fill='%230b2320' stroke='%23ffffff' stroke-width='2'/%3E%3C/svg%3E") 18 18, pointer; }

/* Sur l'écran contact, l'en-tête n'a plus lieu d'être :
   le nom est déjà en grand et les liens sont sous les yeux. */
.nav { transition: opacity 0.5s ease, visibility 0.5s; }
body.at-end .nav,
body.sur-commande .nav { opacity: 0; visibility: hidden; }
/* écran d'offre : plus rien ne doit passer devant le prix et le bouton */
.counter, .scroll-cue { transition: opacity 0.5s ease, visibility 0.5s; }
body.sur-commande .counter,
body.sur-commande .scroll-cue,
body.sur-commande .progres { opacity: 0; visibility: hidden; }

/* ============================================================
   ÉCRAN DE CHARGEMENT
   ============================================================ */
.loader {
  position: fixed;
  inset: 0;
  z-index: 1000;
  background: #f2f2f5;
  display: grid;
  place-items: center;
  align-content: center;
  gap: 10px;                    /* le pourcentage vient se loger sous le mot */
  transition: opacity 0.7s ease, visibility 0.7s;
}
body.loaded .loader { opacity: 0; visibility: hidden; }
body.loading { overflow: hidden; }

/* « Umea Lab » se remplit comme un verre : une vague monte
   derrière les lettres (path SVG recalculé à chaque image). */
.loader-mark {
  width: min(62vw, 620px);
  height: auto;
  overflow: visible;
}
.loader-mark text {
  font-family: var(--display);
  font-size: 150px;
  letter-spacing: 0.005em;
  /* Dahlia n'a qu'une graisse : on épaissit le tracé nous-mêmes pour
     que « Umea Lab » sonne franchement gras */
  paint-order: stroke fill;
  stroke-width: 5px;
  stroke-linejoin: round;
}
.mot-vide { stroke: #d9d9de; }
.mot-plein { stroke: var(--velours); }
.mot-vide { fill: #d9d9de; }
.mot-plein { fill: var(--velours); }

.loader-pct {
  /* Naskh dessine des chiffres calligraphiques illisibles à cette taille :
     on prend une police arabe aux proportions normales. */
  font-family: "Geeza Pro", "Damascus", "Noto Naskh Arabic", serif;
  font-size: 34px;
  line-height: 1.2;
  direction: ltr;
  color: var(--g-50);
  letter-spacing: 0.04em;
}

/* ============================================================
   MOBILE / TABLETTE
   ============================================================ */
@media (max-width: 900px) {
  /* Écran étroit : la scène est agrandie et décalée pour amener le
     cadre au centre — sinon le tirage serait minuscule et pourrait
     même sortir de l'écran. */
  .cadre { border-radius: 2px; }
  /* L'offre passe en une colonne. Contrainte forte : le prix et le bouton
     doivent tenir dans l'écran — c'est tout l'objet du panneau. Les
     espacements sont donc calés sur la hauteur disponible (vh), pas sur
     des valeurs fixes ; le défilement interne ne sert que de filet. */
  .slide.offre { overflow-y: auto; -webkit-overflow-scrolling: touch; }
  .offre-inner {
    grid-template-columns: 1fr;
    align-content: start;
    /* l'air va ENTRE les blocs, pas au-dessus : il y avait cent pixels
       de velours vide avant le premier mot */
    gap: clamp(26px, 4vh, 44px);
    padding: clamp(28px, 4vh, 44px) 20px clamp(22px, 4vh, 40px);
    height: auto;
    min-height: 100%;
  }
  /* le chapeau tient sur une ligne : à 10 px avec un fort interlettrage
     il passait à la ligne sur « tout compris » */
  .offre-kicker {
    font-size: clamp(8.5px, 2.5vw, 10px);
    letter-spacing: 0.12em;
    white-space: nowrap;
  }
  /* le titre aussi tient sur une ligne : la police est très étroite,
     elle le permet sans devenir minuscule */
  .offre-titre {
    font-size: min(7.4vw, 34px);
    white-space: nowrap;
    margin: 14px 0 28px;
  }
  .offre-titre br { display: none; }
  /* de l'air entre les paragraphes et entre les sections — resserré
     par une règle .offre-etapes en double plus bas, fusionnée ici */
  .offre-direct { margin-top: clamp(18px, 2.8vh, 26px); padding-top: clamp(18px, 2.8vh, 26px); }
  .offre-sous { font-size: 14.5px; line-height: 1.5; max-width: none; }
  .offre-etapes {
    margin-top: clamp(28px, 4.2vh, 42px);
    max-width: none;
    gap: clamp(16px, 2.4vh, 22px);
  }
  .offre-etapes li { font-size: 13.5px; gap: 11px; }
  .offre-etapes span { width: 24px; height: 24px; font-size: 12.5px; }
  .offre-droite { gap: 10px; }
  .offre-carte { padding: clamp(16px, 2.4vh, 24px) 18px; }
  .prix-chiffre { font-size: clamp(44px, 13vw, 64px); }
  .prix-devise { font-size: 24px; }
  .offre-prix { margin: 4px 0 6px; }
  .offre-prix-detail { font-size: 12.5px; max-width: none; }
  .offre-cta {
    margin-top: clamp(12px, 2vh, 20px);
    font-size: 20px;
    padding: clamp(12px, 1.7vh, 16px) 18px;
  }
  .offre-rassure { font-size: 12px; margin-top: 9px; }
  .offre-gauche .offre-preuves {
  margin-top: clamp(36px, 5.4vh, 56px);
  columns: 2;
  column-gap: clamp(20px, 2.4vw, 40px);
}
.offre-gauche .offre-preuves li { break-inside: avoid; }
.offre-preuves {
    padding-top: clamp(16px, 2.4vh, 24px);
    gap: 12px;
  }
  .offre-preuves li { font-size: 12.5px; }
  .offre-second { font-size: 10.5px; }

  .slide.hero .hero-bg {
    height: 100%;
    top: 0;
    object-position: 56% 42%;
  }

  .nav {
    padding-top: calc(16px + env(safe-area-inset-top, 0px));
    padding-right: calc(18px + env(safe-area-inset-right, 0px));
    padding-bottom: 16px;
    padding-left: calc(18px + env(safe-area-inset-left, 0px));
  }
  .logo { font-size: 27px; }
  .mark { font-size: 20px; }

  .progres { right: calc(8px + env(safe-area-inset-right, 0px)); height: min(38vh, 260px); }
  .scroll-cue { font-size: 9px; letter-spacing: 0.24em; }

  .outro-title { font-size: clamp(26px, 6vw, 40px); }
  .outro-nom {
  text-shadow: 0 22px 60px rgba(0, 0, 0, 0.5); font-size: clamp(42px, 15vw, 110px); }
  .liens { gap: 18px; }
  .loader-mark { width: 84vw; }
  .loader-pct { font-size: 27px; }
}

/* Fenêtre de bureau étroite mais haute : le titre passerait sur trois
   lignes et ferait déborder la colonne. On le resserre. */
@media (min-width: 901px) and (max-width: 1360px) {
  .offre-titre { font-size: clamp(36px, 4.6vw, 62px); }
  .offre-sous { font-size: 16px; }
  .offre-etapes li { font-size: 15px; }
  .offre-etapes { gap: clamp(8px, 1.4vh, 14px); margin-top: clamp(16px, 2.4vh, 28px); }
  .offre-carte { padding: clamp(20px, 2.6vh, 34px) clamp(20px, 2.2vw, 32px); }
  .prix-chiffre { font-size: clamp(52px, 6vw, 84px); }
  .offre-cta { font-size: 22px; padding: clamp(13px, 1.8vh, 18px) 20px; }
}

/* Écrans courts : on resserre, on ne retire rien. Tout ce qui est sur
   grand écran doit rester atteignable sur téléphone — l'écran défile,
   c'est son métier. */
@media (max-width: 900px) and (max-height: 780px) {
  .offre-titre { font-size: clamp(28px, 7.6vw, 40px); }
  .offre-inner { padding-top: clamp(52px, 8vh, 72px); }
}

@media (max-width: 700px) {
  /* PORTRAIT ÉTROIT : la photo verticale dédiée remplit l'écran.
     Elle est assez haute pour qu'un cadrage plein écran ne rogne que du
     velours sur les côtés (environ 15 % de chaque bord sur un téléphone
     courant) : la casquette, qui n'occupe que le tiers central, reste
     entière. C'est pour ça qu'on peut revenir à un plein cadre ici
     alors que l'image 16:9 imposait un letterbox. */
  .slide.hero .hero-bg {
    object-fit: cover;
    object-position: center;
  }
  .scroll-cue { right: auto; left: 50%; transform: translateX(-50%); }
}

/* écrans tactiles : le curseur dessiné n'a pas de sens */
@media (hover: none) {
  html, body, a, button, input, textarea, label, .progres, .logo, .mark { cursor: auto; }
}

/* ============================================================
   TABLETTE ET MOBILE — passe dédiée
   Trois choses commandent tout le reste : le bouton de commande doit
   rester atteignable au pouce, le texte doit rester lisible sans
   pincer, et rien ne doit passer sous la barre d'action.
   ============================================================ */

/* --- tablette : l'accroche et l'en-tête se resserrent --- */
@media (min-width: 901px) and (max-width: 1180px) {
  
  
  .logo { font-size: 31px; }
}

/* --- tablette en portrait : la galerie a besoin d'air --- */
@media (min-width: 701px) and (max-width: 900px) and (orientation: portrait) {
}

/* --- mobile et petite tablette --- */
@media (max-width: 900px) {
  /* ============================================================
     LA GALERIE EN PILE
     Sur grand écran les trois scènes sont superposées au même endroit
     et se relaient par balayage. Sur un téléphone ce dispositif ne
     tient pas : le décor est en 16:9, l'écran deux fois plus haut, et
     l'écran restait aux trois quarts vide.
     On les met donc BOUT À BOUT, dans le fil du défilement. Celle qu'on
     regarde est en couleur, les autres en noir et blanc : on voit d'un
     coup d'œil où l'on en est, et il n'y a plus d'espace mort.

     RALENTIR SANS ROGNER NI LAISSER DE VIDE : chaque scène garde sa
     taille et son collage naturels (aucun agrandissement, aucune marge
     visible) — c'est le budget de défilement AUTOUR d'elle qui
     s'allonge, dans sa propre enveloppe .panneau-pause. Le panneau se
     fige au centre de l'écran (position:sticky) le temps que son
     enveloppe, plus haute que lui, défile derrière : la photo reste
     affichée plus longtemps avant que la suivante prenne le relais.
     Une pause PAR tirage, pas une seule pour les trois : sinon un seul
     figeait pour le groupe entier, et seul celui qui se trouvait par
     hasard au centre de l'écran captait la ligne d'observation du
     milieu (voir plus bas, .en-vue) — les deux autres ne l'auraient
     jamais. Aucun JS : juste du sticky. */
  .rail { height: auto; }
  .rail-sticky { position: static; height: auto; overflow: visible; }
  .rail-track { position: static; }
  .rail-snaps { height: 100%; }
  .rail-snaps > div { height: 33.3333%; }

  .panneau-pause {
    height: calc((100vw / 1.7917) + 28vh);
  }

  .panneau-image {
    position: sticky;
    inset: auto;
    /* cale le CENTRE du panneau (pas son bord haut) sur le centre de
       l'écran : .en-vue observe une ligne au milieu de l'écran, un
       panneau figé en haut ne la toucherait jamais. Doit être déclaré
       APRÈS inset:auto (qui remettrait top à auto sinon). */
    top: max(0px, calc(50vh - (100vw / 1.7917) / 2));
    height: auto;
    padding: 0;
    /* plus de balayage ici : c'est le défilement qui fait la transition */
    clip-path: none;
    /* la scène qu'on ne regarde pas passe en noir et blanc et recule */
    filter: grayscale(1) brightness(0.72);
    opacity: 0.72;
    transition: filter 0.55s var(--ease-doux), opacity 0.55s var(--ease-doux);
  }
  .panneau-image.en-vue { filter: none; opacity: 1; }
  .panneau-image::before { display: none; }

  /* la scène : une bande à la proportion exacte de l'image, pleine
     largeur. Rien n'est rogné, le lettrage UMEA reste entier. */
  .scene {
    position: relative;
    inset: auto;
    width: 100%;
    height: calc(100vw / var(--sar, 1.7917));
  }

  /* Le compteur (01/06, 02/06…) ne sert à rien sur mobile : on le
     retire complètement plutôt que de le recaler. */
  .counter { display: none; }
  /* l'en-tête cède son bouton à la barre du bas : deux appels à
     l'action à l'écran, c'est un de trop */
  
  .nav {
    padding-top: calc(14px + env(safe-area-inset-top, 0px));
    padding-right: calc(16px + env(safe-area-inset-right, 0px));
    padding-bottom: 14px;
    padding-left: calc(16px + env(safe-area-inset-left, 0px));
  }
  .scroll-cue { bottom: calc(26px + env(safe-area-inset-bottom, 0px)); }

  /* l'accroche : elle reste au-dessus de la barre d'action */
  

  /* rien ne doit finir sous la barre d'action */
  .offre-inner { padding-bottom: calc(24px + env(safe-area-inset-bottom, 0px)); }

  /* LE PIED DE PAGE SE RESSERRE.
     Il réservait la moitié d'un écran alors que son contenu en occupe
     le tiers, et le nom géant était posé en absolu tout en bas : entre
     les deux, un grand vide. La hauteur suit le contenu, et le nom
     revient dans le flux, juste sous les coordonnées. */
  .slide.outro { min-height: 0; }
  .outro-bottom {
    position: relative;
    /* Instagram doit toucher le bord haut du pied de page, pas flotter
       dans un bloc marron vide au-dessus */
    padding-top: clamp(16px, 2.2vh, 22px);
    /* les 26px restent un vrai espace de lecture (le nom ne doit pas
       coller à l'indicateur d'accueil) ; seule la part safe-area est
       annulée par la marge négative, pour que SEUL ce morceau-là
       étire le dégradé de .outro-bottom::before sous la barre du bas
       sans ajouter de vide supplémentaire au défilement normal */
    padding-bottom: calc(26px + env(safe-area-inset-bottom, 0px));
    margin-bottom: calc(-1 * env(safe-area-inset-bottom, 0px));
  }
  /* Les coordonnées : intitulé au-dessus, adresse en dessous, les trois
     liens resserrés (Instagram/TikTok/Email) pour une colonne nette. */
  .liens.colonne { gap: clamp(10px, 1.5vh, 14px); }
  /* Le copyright quitte la ligne des liens (elle n'a pas la place pour
     les deux côte à côte sur un écran étroit, et il finissait renvoyé
     à la ligne, collé à gauche) : il se pose dans l'angle haut droit,
     sur une largeur assez étroite pour ne jamais chevaucher la colonne
     Instagram/TikTok/Email juste en dessous. */
  .copyright {
    position: absolute;
    top: clamp(6px, 1vh, 10px);
    right: clamp(12px, 3vw, 24px);
    max-width: calc(100vw - 205px);
    text-align: right;
    font-size: 10.5px;
    line-height: 1.45;
  }
  .liens a {
    display: grid;
    min-height: 0;
    gap: 2px;
  }
  .reseau { font-size: 9.5px; letter-spacing: 0.2em; }
  /* l'adresse passe en police de texte : en police d'affichage, un long
     courriel devient filiforme et paraît gris alors qu'il est blanc */
  .pseudo {
    font-family: var(--body);
    font-size: 15px;
    font-weight: 600;
    letter-spacing: 0.005em;
    color: #ffffff;
  }
  /* le pied de page s'allonge : Instagram/TikTok/Email restent en
     haut, mais Studio redescend nettement plus bas qu'avant */
  .outro-liens { margin-bottom: clamp(70px, 11vh, 120px); }
  .outro-nom {
    position: relative;
    right: auto;
    bottom: auto;
    margin-top: clamp(14px, 2.2vh, 24px);
    text-align: right;
    line-height: 0.8;
  }

  /* L'écran de commande garde TOUT : cartel, preuves, lien de repli.
     Il fait plus d'une hauteur d'écran et défile, ce qui est normal sur
     un téléphone. */
  .offre-carte { padding: 20px 18px 22px; }
  .offre-sous { max-width: 42ch; }
  .carte-fiche { gap: 14px 18px; }

  /* la relance passe en pile, le bouton prend toute la largeur */

  /* la barre de progression se fait discrète : l'écran est étroit */
  .progres { height: min(30vh, 200px); right: calc(6px + env(safe-area-inset-right, 0px)); }

  /* Les scènes défilent maintenant les unes sous les autres : plus de
     bande centrée à laquelle s'accrocher. L'invitation reprend sa place
     en bas de l'écran, que la barre d'action n'occupe plus. */
  .invite {
    bottom: calc(26px + env(safe-area-inset-bottom, 0px));
    font-size: 11px;
    padding: 8px 14px 8px 10px;
  }

  /* l'en-tête sur une seule ligne, sans que le nom touche « us. ».
     Les deux liens reçoivent de quoi atteindre 44 px de haut : ce sont
     des cibles tactiles, pas seulement du texte. */
  .nav {
    padding-top: calc(4px + env(safe-area-inset-top, 0px));
    padding-right: calc(12px + env(safe-area-inset-right, 0px));
    padding-bottom: 4px;
    padding-left: calc(12px + env(safe-area-inset-left, 0px));
    gap: 12px;
  }
  .logo, .mark {
    display: flex;
    align-items: center;
    min-height: 44px;
    padding: 0 6px;
  }
  .logo { font-size: clamp(20px, 6.2vw, 26px); }
  .mark { font-size: clamp(15px, 4.6vw, 19px); }

  /* cibles tactiles : rien en dessous de 44 px de haut */
  /* (les coordonnées ont leur propre règle plus bas : intitulé au-dessus,
     adresse en dessous — voir « pied de page ») */
}

/* --- téléphone --- */
@media (max-width: 700px) {
  /* le tirage occupe presque toute la largeur : sur un écran étroit,
     le laisser à sa géométrie de bureau le réduisait à une vignette */
  .cadre { border-radius: 2px; }
  /* l'inclinaison marche aussi au doigt : la perspective reste, un peu
     resserrée pour que quelques degrés se voient sur un petit cadre */
  .panneau-image { perspective: 900px; }
  .logo { font-size: 24px; }
  .mark { font-size: 18px; }
  .carte-fiche { grid-template-columns: 1fr 1fr; gap: 12px 16px; }
  .outro-nom { font-size: clamp(38px, 14vw, 84px); }
}

/* --- téléphone couché : très peu de hauteur --- */
@media (max-width: 900px) and (orientation: landscape) and (max-height: 500px) {
  
}

