/* ==========================================================================
   GINKO DISPLAYS. Design System
   Noir & blanc. HW Cigars (serif) + Area Inktrap (sans).
   Aucune dépendance externe. Tout est local.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. FONTS
   -------------------------------------------------------------------------- */
@font-face {
  font-family: "HW Cigars";
  src: url("../fonts/HWCigars-Light.woff2") format("woff2");
  font-weight: 300 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Area Inktrap";
  src: url("../fonts/Area-Inktrap-Trial-Regular.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
/* La version Trial embarque une ligature "fi" defectueuse : elle avale le point
   du i et affiche "confrm" au lieu de "confirm". On desactive les ligatures
   standard sur cette famille. A revoir si la version sous licence est correcte. */
body, input, button, textarea, select {
  font-variant-ligatures: no-common-ligatures;
  font-feature-settings: "liga" 0, "clig" 0;
}
@font-face {
  font-family: "Area Inktrap Extended";
  src: url("../fonts/Area-Inktrap-Extended-Medium.woff2") format("woff2");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Degular";
  src: url("../fonts/DegularVariable.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

/* Poppins : remplace les deux Area Inktrap sur les pages francaises,
   dont la licence ne couvre que la version anglaise. */
@font-face {
  font-family: "Poppins";
  src: url("../fonts/Poppins-Regular.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Poppins";
  src: url("../fonts/Poppins-Medium.woff2") format("woff2");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}

/* Polices de scene : elles n'habillent jamais la voix de Ginko, seulement
   les gabarits "clients" figures dans l'editeur de la page Studio. */
@font-face {
  font-family: "EB Garamond";
  src: url("../fonts/EBGaramond-400.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "EB Garamond";
  src: url("../fonts/EBGaramond-500.woff2") format("woff2");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Archivo";
  src: url("../fonts/Archivo-500.woff2") format("woff2");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Archivo";
  src: url("../fonts/Archivo-600.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}

/* --------------------------------------------------------------------------
   2. TOKENS
   -------------------------------------------------------------------------- */
:root {
  /* Palette : noir et blanc, aucun accent coloré */
  --gk-black: #000000;
  --gk-near-black: #0a0a0a;
  --gk-charcoal: #1a1a18;
  --gk-white: #ffffff;
  --gk-off-white: #f5f5f4;
  --gk-paper: #eeeeec;
  --gk-mid-gray: #a8a8a6;
  --gk-warm-gray: #737370;

  /* Gris sourd sémantique : bascule selon le fond, pour rester lisible.
     Sur noir 6.54:1, sur blanc 5.62:1, sur paper 4.84:1. Tous au-dessus de AA. */
  --muted: #909089;
  --body-ink: var(--gk-mid-gray);

  /* La seule couleur du site, et elle ne sert qu'a dire "ce champ ne va
     pas". Un rouge brique, pas une alarme. */
  --gk-erreur: #E0685C;

  /* Typographie */
  --font-serif: "HW Cigars", "PP Editorial New", "Cormorant Garamond", Georgia, serif;
  --font-sans: "Area Inktrap", "Degular", "Söhne", system-ui, sans-serif;
  --font-sans-ext: "Area Inktrap Extended", var(--font-sans);
}

/* Pages francaises : la licence Area Inktrap ne couvre que l'anglais,
   Poppins prend sa place (corps comme eyebrows). Le serif ne change pas. */
html[lang="fr"] {
  --font-sans: "Poppins", "Degular", system-ui, sans-serif;
  --font-sans-ext: "Poppins", "Degular", system-ui, sans-serif;
}

:root {

  /* Échelle typographique : reprise du site d'origine.
     Plate et resserrée : le titre ne hurle pas, il pose.
     Titres : line-height 1, letter-spacing -0.6px. */
  --fs-eyebrow: 0.625rem;   /* 10px */
  --fs-body: 0.875rem;      /* 14px */
  --fs-lead: 0.875rem;      /* 14px : aucun paragraphe au-dela du corps */
  --fs-h3: 1.625rem;        /* 26px */
  --fs-h2: 2rem;            /* 32px */
  --fs-h1: 2.5rem;          /* 40px */
  --fs-hero: 3rem;          /* 48px, plafond, jamais au-delà */

  /* Rythme vertical : plus d'air qu'à l'origine, c'est le luxe */
  --space-2xs: 0.5rem;
  --space-xs: 1rem;
  --space-sm: 1.75rem;
  --space-md: 2.75rem;
  --space-lg: 4.5rem;
  --space-xl: 6.5rem;
  --space-2xl: 8.5rem;
  --space-3xl: 10.5rem;

  /* Layout */
  --wrap: 80rem;
  --wrap-narrow: 48.75rem;
  --gutter: 2.5rem;

  /* Aucun arrondi nulle part : bords droits, c'est la direction artistique. */
  --radius: 0;

  /* Motion : Ginko ne bondit pas, Ginko se révèle */
  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
  /* La courbe des revelations : un depart plus franc, une pose tres longue.
     La meme duree parait plus vive au debut et plus douce a la fin. */
  --ease-reveal: cubic-bezier(0.22, 1, 0.36, 1);
  --dur: 900ms;
  --dur-fast: 600ms;
}

/* Sur un tres grand ecran, un gabarit fige a 1280 px flotte dans le vide et
   la typographie parait timide. Tout monte d'un cran, dans les memes
   proportions : le dessin ne change pas, il respire plus grand. */
@media (min-width: 1680px) {
  :root {
    --wrap: 90rem;
    --fs-hero: 3.5rem;
    --fs-h1: 2.875rem;
    --fs-h2: 2.25rem;
    --fs-lead: 1.25rem;
    --space-lg: 5.25rem;
    --space-xl: 7.5rem;
  }
}

/* Au-dela d'un ecran de bureau classique, ce n'est plus le gabarit qui
   s'elargit : c'est la racine qui grandit, en continu. Tout le systeme est
   en rem, le dessin entier monte donc d'echelle dans ses proportions
   exactes -- typographie, espaces, gabarit, cartes -- au lieu de flotter
   en petit au milieu du vide. Le calc travaille en pixels : la racine part
   de 16 px a 1920 et gagne 0.55 px tous les 100 px d'ecran, soit ~122 % sur
   un 2560 et le plafond de 140 % vers 3100. */
@media (min-width: 1920px) {
  html {
    font-size: clamp(16px, calc(16px + (100vw - 1920px) * 0.0055), 22.4px);
  }
}

/* Écrans intermédiaires : on resserre l'échelle d'un cran */
@media (max-width: 1100px) {
  :root {
    --fs-hero: 2.5rem;   /* 40px */
    --fs-h1: 2.25rem;    /* 36px */
    --fs-h2: 1.75rem;    /* 28px */
    --fs-h3: 1.5rem;     /* 24px */
    --fs-lead: 0.875rem;
    --gutter: 2rem;
  }
}

@media (max-width: 700px) {
  :root {
    --fs-hero: 2rem;     /* 32px */
    --fs-h1: 1.875rem;   /* 30px */
    --fs-h2: 1.5rem;     /* 24px */
    --fs-h3: 1.3125rem;  /* 21px */
    --fs-lead: 0.875rem;
    --gutter: 1.5rem;
    --space-sm: 1.5rem;
    --space-md: 2.25rem;
    --space-lg: 3.5rem;
    --space-xl: 5rem;
    --space-2xl: 6.5rem;
    --space-3xl: 8rem;
  }
}

/* --------------------------------------------------------------------------
   3. RESET
   -------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }

html {
  -webkit-text-size-adjust: 100%;
}
/* scroll-behavior: smooth ne vit plus ici.

   Pose sur <html>, il s'appliquait aussi a la restauration de position au
   rechargement : le navigateur replacait la page en l'animant, et cela se
   voyait comme un depart du bas suivi d'une remontee jusqu'a l'endroit ou
   l'on etait. Le glissement ne concerne plus que les liens d'ancre, et il
   est demande explicitement par le script, qui respecte la preference de
   mouvement reduit. */

body {
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  line-height: 1.78;
  letter-spacing: 0;
  color: var(--gk-white);
  background: var(--gk-black);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}

img, picture, video, svg { display: block; max-width: 100%; }
img, video { height: auto; }

/* On retire les prises les plus evidentes sur les images : le glisser vers
   le bureau, la selection, et l'appui long qui ouvre le menu de sauvegarde
   sur iPhone. Ce n'est pas une protection, c'est un ralentisseur : le
   navigateur doit telecharger l'image pour l'afficher, elle est donc deja
   sur la machine du visiteur. Voir le commentaire du script. */
img {
  -webkit-user-drag: none;
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
}
input, button, textarea, select { font: inherit; color: inherit; }
button { background: none; border: 0; cursor: pointer; }
a { color: inherit; text-decoration: none; }

/* Accessibilité : lien d'évitement */
.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 200;
  padding: 0.75rem 1.25rem;
  background: var(--gk-white);
  color: var(--gk-black);
}
.skip-link:focus { left: 1rem; top: 1rem; }

:focus-visible {
  outline: 1px solid currentColor;
  outline-offset: 3px;
}

/* --------------------------------------------------------------------------
   4. TYPOGRAPHIE
   -------------------------------------------------------------------------- */
.eyebrow {
  font-family: var(--font-sans-ext);
  font-size: var(--fs-eyebrow);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;   /* = 2px à 10px, comme l'origine */
  line-height: 1;
  color: var(--muted);
  margin-bottom: var(--space-md);
}
/* Un intertitre colle a son titre n'ouvre pas une section, il l'annonce :
   l'ecart general le detachait trop, et les reprises en ligne le collaient
   trop. Une seule valeur, partout ou les deux se suivent. Un intertitre qui
   coiffe une grille, lui, garde l'ecart plein : il n'a pas de titre pour
   porter le regard jusqu'au contenu. */
.section .eyebrow:has(+ h2),
.section .eyebrow:has(+ h3) { margin-bottom: var(--space-sm); }
.section--light .eyebrow,
.section--paper .eyebrow { color: var(--muted); }

/* Titres : serif, très peu de graisse, interligne serré.
   C'est l'interligne à 1 qui donne le ton éditorial. */
h1, h2, h3, .h3 {
  font-family: var(--font-serif);
  font-weight: 300;
  line-height: 1.04;
  letter-spacing: -0.015em;
  text-wrap: balance;
}

h1 { font-size: var(--fs-h1); }
h2 { font-size: var(--fs-h2); }
h3, .h3 { font-size: var(--fs-h3); line-height: 1.12; }

/* Le hero est le seul à monter à 48px */
.hero__title, .page-hero h1 { font-size: var(--fs-hero); }

h4, .h4 {
  font-family: var(--font-sans-ext);
  font-size: var(--fs-body);
  font-weight: 500;
  line-height: 1.45;
  letter-spacing: 0;
}

/* Chapô : plus grand que le corps, en sans, légèrement resserré */
.lead {
  font-size: var(--fs-lead);
  line-height: 1.7;
  letter-spacing: 0;
  color: var(--gk-mid-gray);
  max-width: 52ch;
}
.section--light .lead, .section--paper .lead { color: var(--muted); }

/* La largeur de lecture ne concerne que le texte courant.
   L'appliquer à tous les p décalait eyebrows et boutons dans les sections centrées. */
.prose p, .legal p { max-width: 60ch; }
.prose p + p { margin-top: 1.6em; }
.prose { color: var(--gk-mid-gray); }
.section--light .prose, .section--paper .prose { color: var(--gk-charcoal); }

.caption {
  font-size: 0.75rem;
  line-height: 1.7;
  color: var(--muted);
}

em { font-style: italic; }

/* --------------------------------------------------------------------------
   5. LAYOUT
   -------------------------------------------------------------------------- */
.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.wrap--narrow { max-width: var(--wrap-narrow); }

.section {
  padding-block: var(--space-2xl);
  position: relative;
}
.section--tight { padding-block: var(--space-xl); }
/* Deux sections de meme fond qui se suivent additionnent leurs marges : le
   bas de l'une plus le haut de l'autre, soit dix-sept rem de vide au milieu
   d'un aplat, sans rien pour dire qu'on a change de chapitre. Cette classe
   supprime la marge haute de la seconde, et un filet articule le passage.
   Elle se pose a la main, la ou l'enchainement existe : soixante-quatre
   frontieres de ce genre vivent sur le site, toutes n'ont pas le meme
   besoin. */
/* Une section qui enchaine sur le chapeau de page sans titre a elle : le
   texte est la suite du h1, pas un nouveau chapitre. Le chapeau a deja sa
   marge basse, celle-ci n'a donc pas besoin d'une marge haute pleine. */
.section--ouverture { padding-top: var(--space-xl); }

.section--suite { padding-top: 0; }
.section--suite > .wrap { border-top: 1px solid rgba(0, 0, 0, 0.1); padding-top: var(--space-xl); }
.section--dark.section--suite > .wrap { border-top-color: rgba(255, 255, 255, 0.12); }

.section--light {
  background: var(--gk-white);
  color: var(--gk-charcoal);
  --muted: #68685f;
  --body-ink: var(--gk-charcoal);
}
.section--paper {
  background: var(--gk-paper);
  color: var(--gk-charcoal);
  --muted: #68685f;
  --body-ink: var(--gk-charcoal);
}
.section--dark {
  background: var(--gk-black);
  color: var(--gk-white);
}

.grid { display: grid; gap: var(--space-md); }
.grid--2 { grid-template-columns: repeat(2, 1fr); }
.grid--3 { grid-template-columns: repeat(3, 1fr); }

@media (max-width: 1000px) {
  .grid--3, .grid--4 { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 640px) {
  .grid--2, .grid--3, .grid--4 { grid-template-columns: 1fr; }
}

/* Bloc éditorial asymétrique : titre à gauche, texte à droite */
.split {
  display: grid;
  grid-template-columns: minmax(0, 5fr) minmax(0, 6fr);
  gap: var(--space-md) var(--space-xl);
  align-items: start;
}
@media (max-width: 900px) {
  .split { grid-template-columns: 1fr; gap: var(--space-md); }
}

/* --------------------------------------------------------------------------
   6. BOUTONS
   -------------------------------------------------------------------------- */
/* Au doigt, un bouton sans reponse a l'air mort : un tassement d'un pour
   cent le temps de l'appui suffit. */
.btn:active { transform: scale(0.99); }
.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.8em;
  font-family: var(--font-sans-ext);
  font-size: 0.625rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  padding: 1.15em 2.4em;
  border-radius: 0;
  border: 1px solid transparent;
  transition: background var(--dur-fast) var(--ease),
              color var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease);
}

/* --- la regle des deux CTA ------------------------------------------------
   Un bouton ne porte pas de fleche : son cadre dit deja qu'on clique. Un lien
   n'a pas de cadre, c'est sa fleche qui le dit, et rien d'autre : un filet
   sous le mot faisait une deuxieme ligne pleine a cote du bouton. Les deux
   sont en Area Inktrap Extended, meme corps et meme chasse : cote a cote, ils
   se lisent comme une action principale et son alternative.
   -------------------------------------------------------------------------- */
.link-underline {
  display: inline-flex;
  align-items: baseline;
  gap: 0.7em;
  font-size: 0.625rem;
  text-transform: uppercase;
  letter-spacing: 0.2em;
}
.link-underline::after {
  content: "\2192";
  letter-spacing: 0;
  transition: transform var(--dur-fast) var(--ease);
}
.link-underline:hover::after { transform: translateX(4px); }

/* Un bouton et son alternative discrete, sur une meme ligne. En flex : sur un
   ecran etroit ils passent l'un sous l'autre en gardant le meme bord gauche,
   la ou une marge fixe les decalait en escalier. */
.actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1.15rem 2rem;
}

/* ---- le bouton se remplit de gauche a droite -----------------------------
   Un voile balaie le bouton et le retourne en negatif.

   Le voile est pose AU-DESSUS du texte, en mix-blend-mode: difference : il
   inverse pixel par pixel ce qu'il recouvre, fond et lettres comprises. On
   a essaye plus simple, un voile derriere le texte et la couleur du mot qui
   bascule : impossible a regler. Le voile est noir et le texte aussi, donc
   les lettres deja recouvertes disparaissaient ; en faisant basculer le mot
   plus tot, c'etait la moitie pas encore recouverte qui s'effacait, blanche
   sur blanc. Aucun retard ne rattrape cela, puisqu'il faudrait que chaque
   lettre change au moment precis ou le voile la passe. La difference le
   fait d'elle-meme, et aucune transition de couleur n'est necessaire.

   isolation: isolate enferme le melange dans le bouton : sans cela, le
   voile inverserait aussi la section derriere lui. C'est aussi pour cela
   que chaque bouton porte desormais un fond opaque, meme quand il parait
   transparent : la difference a besoin de quelque chose a inverser. Un
   fond noir sur une section sombre ne se voit pas, mais il donne au voile
   sa matiere. */
.btn {
  position: relative;
  isolation: isolate;
  overflow: hidden;
}
.btn::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  background: var(--gk-white);
  mix-blend-mode: difference;
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 520ms var(--ease-reveal);
  pointer-events: none;
}
.btn:hover::before { transform: scaleX(1); }

/* Le filet du bouton plein n'est pas recouvert par le voile, qui s'arrete
   a la boite de padding : pose sur le fond du bouton il est invisible au
   repos, et il dessine le contour une fois le bouton retourne. Sans lui, un
   bouton blanc inverse en noir disparaitrait dans une section sombre. */
.btn--primary {
  background: var(--gk-white);
  color: var(--gk-black);
  border-color: rgba(255, 255, 255, 0.35);
}
/* Le fantome reste transparent sur fond sombre : la difference appliquee a
   un fond transparent donne du blanc, et le mot blanc devient noir. Le
   retournement se fait donc sans fond a peindre, et le bouton ne pose pas
   de rectangle opaque sur la video des capsules d'encre. */
.btn--ghost {
  border-color: rgba(255, 255, 255, 0.35);
  color: var(--gk-white);
}

.section--light .btn--primary,
.section--paper .btn--primary {
  background: var(--gk-black);
  color: var(--gk-white);
  border-color: rgba(0, 0, 0, 0.25);
}
.section--light .btn--ghost,
.section--paper .btn--ghost {
  background: var(--gk-white);
  border-color: rgba(0, 0, 0, 0.25);
  color: var(--gk-charcoal);
}
.section--paper .btn--ghost { background: var(--gk-paper); }

/* --------------------------------------------------------------------------
   LA BIBLIOTHEQUE : UNE CONSTELLATION
   Les visuels flottent autour du texte au lieu de defiler en bandeau. Chacun
   derive a son propre pas quand la page bouge, et change d'image de temps en
   temps, au hasard : on ne regarde pas une selection arretee, on regarde une
   bibliotheque qui respire. Le texte garde le centre, rien ne passe dessus.
   -------------------------------------------------------------------------- */
.bibliotheque {
  position: relative;
  display: grid;
  place-items: center;
  /* Assez haute pour la derive, assez courte pour qu'une bonne dizaine de
     cadres entourent le texte au meme moment. */
  min-height: 132svh;
  /* Les cadres debordent des deux bords : on les coupe sans faire du bloc un
     conteneur de defilement. */
  overflow-x: clip;
}

.galaxie {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.galaxie__vue {
  position: absolute;
  left: var(--x);
  top: var(--y);
  width: var(--w);
  display: block;
  /* La derive suit le defilement : --defile va de -1 a 1, --amp donne a
     chaque cadre son amplitude, donc son eloignement apparent. Amplitude et
     largeur se comptent en svh, comme la hauteur de la section : la geometrie
     verticale ne depend alors plus du format de l'ecran, et deux cadres qui
     ne se touchent pas ici ne se toucheront nulle part. */
  transform: translate3d(0, calc(var(--defile, 0) * var(--amp, 5) * -1svh), 0);
}
.galaxie__vue--portrait { aspect-ratio: 3 / 4; }
.galaxie__vue--paysage  { aspect-ratio: 3 / 2; }

.galaxie__couche {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  /* Un fondu long et symetrique : l'image sortante s'efface aussi
     lentement que l'entrante se revele, rien ne "coupe". Chaque cadre
     change a son propre rythme (voir le moteur), donc l'enchainement ne
     boucle jamais malgre la lenteur du fondu lui-meme. */
  transition: opacity 2600ms cubic-bezier(0.45, 0, 0.2, 1);
}
.galaxie__couche.est-vue { opacity: 1; }

.bibliotheque__mot {
  position: relative;
  z-index: 2;
  max-width: 48ch;
  text-align: center;
  padding-block: var(--space-2xl);
}
.bibliotheque__mot .lead,
.bibliotheque__mot .caption { margin-inline: auto; }
.bibliotheque__mot .lead { max-width: 46ch; }

@media (max-width: 900px) {
  /* Dix cadres autour d'une colonne etroite, c'est un fouillis : on n'en
     garde que quatre, aux quatre coins, et plus grands. */
  .bibliotheque { min-height: 0; }
  .galaxie__vue { display: none; }
  .galaxie__vue:nth-child(2),
  .galaxie__vue:nth-child(5),
  .galaxie__vue:nth-child(10),
  .galaxie__vue:nth-child(12) { display: block; width: 32vw; }
  .bibliotheque__mot { padding-block: calc(var(--space-2xl) * 1.6); }
}

@media (prefers-reduced-motion: reduce) {
  .galaxie__vue { transform: none; }
  .galaxie__couche { transition: none; }
}

/* --------------------------------------------------------------------------
   LES SERVICES, EN CARTES
   Quatre prestations que rien n'illustrait : quatre titres et quatre
   paragraphes dans le noir. Une image dit en un regard ce qu'une prestation
   recouvre, la ou trois lignes de texte demandent qu'on les lise.
   -------------------------------------------------------------------------- */
.cartes-services {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--space-sm);
}

.cartes-services--duo { grid-template-columns: repeat(2, 1fr); }
@media (max-width: 760px) { .cartes-services--duo { grid-template-columns: 1fr; } }

.carte-service {
  display: flex;
  flex-direction: column;
  border: 1px solid rgba(255, 255, 255, 0.14);
  background: var(--gk-black);
}
/* Quand la carte entiere est cliquable, elle ne doit rien prendre du style
   des liens : c'est un bloc, pas un mot souligne. */
a.carte-service {
  color: inherit;
  text-decoration: none;
  transition: border-color var(--dur-fast) var(--ease);
}
.carte-service__label {
  font-family: var(--font-sans-ext);
  font-size: var(--fs-eyebrow);
  text-transform: uppercase;
  letter-spacing: 0.18em;
  color: var(--muted);
}
.carte-service__txt {
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  line-height: 1.6;
  color: var(--gk-mid-gray);
}
.carte-service__lien {
  display: inline-flex;
  align-items: baseline;
  gap: 0.45rem;
  margin-top: 0.4rem;
  font-family: var(--font-sans-ext);
  font-size: var(--fs-eyebrow);
  text-transform: uppercase;
  letter-spacing: 0.18em;
  color: var(--gk-black);
}
.carte-service__lien i {
  font-style: normal;
  transition: transform var(--dur-fast) var(--ease);
}
a.carte-service:hover .carte-service__lien i { transform: translateX(3px); }
.section--light .carte-service,
.section--paper .carte-service {
  border-color: rgba(0, 0, 0, 0.14);
  background: transparent;
}

.carte-service__vue {
  display: block;
  aspect-ratio: 16 / 10;
  overflow: hidden;
  background: var(--gk-near-black);
}
.carte-service__vue img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* Rendu produit detoure sur blanc : contenu, jamais rogne, sinon la tablette
   sort du cadrage. */
.carte-service__vue--clair { background: var(--gk-white); }
.carte-service__vue--clair img { object-fit: contain; }

.carte-service__corps {
  padding: var(--space-sm);
  display: grid;
  gap: 0.6rem;
}
.carte-service__nom {
  font-family: var(--font-serif);
  font-size: 1.25rem;
  line-height: 1.15;
  letter-spacing: -0.015em;
  color: var(--gk-white);
}
.section--light .carte-service__nom,
.section--paper .carte-service__nom { color: var(--gk-black); }
.carte-service__note {
  font-size: 0.8125rem;
  line-height: 1.6;
  color: var(--gk-mid-gray);
}
.section--light .carte-service__note,
.section--paper .carte-service__note { color: var(--muted); }

@media (max-width: 1000px) { .cartes-services { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 600px) {
  .cartes-services { grid-template-columns: 1fr; gap: var(--space-xs); }
}

/* L'application sous le chapeau : large, posee, elle ouvre la page sur
   l'objet dont il va etre question. */
/* La porte vers les tarifs : centree et ample. Elle ne montre rien, elle
   annonce ; il lui faut donc de la place pour peser, sans quoi elle se lit
   comme une note de bas de page. */
.porte-tarifs {
  display: grid;
  place-items: center;
  min-height: 82svh;
}
.porte-tarifs .wrap { max-width: 54ch; }
.porte-tarifs .lead { max-width: 48ch; }

/* --------------------------------------------------------------------------
   LE SIMULATEUR DE SCENARIO
   Trois axes, quatre choix chacun. Le propos a gauche, le cadre au centre,
   les commandes a droite, la regle sous les trois. Tout dans un ecran.

   Le visuel est pose dans un vrai cadre Ginko, a l'emplacement exact de la
   toile mesure sur l'image : 6,124 % depuis la gauche, 4,922 % depuis le
   haut, 87,66 % de large, 90,262 % de haut.
   -------------------------------------------------------------------------- */
/* ---- le rafraichissement de dalle ---------------------------------------
   Le refresh d'une dalle electrophoretique monochrome, 1750 ms.

   Rien ici n'est un fondu croise, et rien n'est un aplat : les deux images
   gardent leur opacite du debut a la fin, et la nouvelle est composee sur
   l'ancienne en mode difference. C'est le mecanisme physique lui-meme :
   |A - B| vaut zero la ou les deux images s'accordent et un la ou le pigment
   doit se retourner. La couche du dessus dessine donc exactement la carte des
   pixels qui changent d'etat, avec des bords durs et aucune transparence.

   Trois impulsions optiques, comme sur la dalle :

     A stable  ->  SOMBRE : A ecrasee, B affleure a 10 %, solarisation locale
     ->  CLAIR : polarite inversee, |A - B| en negatif, les deux images
         coexistent dans le meme espace, contours nets
     ->  SOMBRE : la polarite revient, la geometrie bascule sur B, fantome de A
     ->  release : B remonte, le fantome s'eteint, contraste stabilise

   Deux proprietes portent tout : la luminosite de chaque couche, et la
   polarite de la toile. La couche A finit a brightness(0) -- une difference
   sur du noir rend la source intacte, la sortie d'animation est donc B, sans
   raccord. Aucun flou, aucun grain, aucun deplacement, jamais de noir ni de
   blanc uniforme : une information demeure a chaque milliseconde.
   -------------------------------------------------------------------------- */
/* --- la polarite de la dalle : elle bascule net, jamais en fondu ---------
   Un invert progressif passerait par un gris uniforme a mi-course, c'est-a-dire
   par la perte totale de l'information. Il bascule donc d'un pas. */
@keyframes gk-eink-pol {
  0%              { filter: none; }
  24.571%         { filter: invert(1); }   /* 430 ms : impulsion claire */
  43.429%         { filter: none; }        /* 760 ms : retour de polarite */
  100%            { filter: none; }
}

/* --- A : ecrasee, relevee, puis reduite a un fantome qui s'eteint -------- */
@keyframes gk-eink-a {
  /* 0 -> 180 ms : stable, aucun mouvement */
  0%, 10.286%     { filter: grayscale(1) brightness(1) contrast(1);
                    animation-timing-function: cubic-bezier(0.4, 0, 0.8, 1); }
  /* 300 ms : compensation sombre. Les blancs de A tombent au gris fonce,
     ses noirs restent noirs : la geometrie de A est intacte. */
  17.143%         { filter: grayscale(1) brightness(0.28) contrast(1.1); }
  /* maintien 130 ms */
  24.571%         { filter: grayscale(1) brightness(0.28) contrast(1.1);
                    animation-timing-function: cubic-bezier(0.4, 0, 0.8, 1); }
  /* 570 ms : A revient a pleine valeur sous polarite inversee */
  32.571%         { filter: grayscale(1) brightness(1) contrast(1); }
  /* maintien 190 ms : le composite clair */
  43.429%         { filter: grayscale(1) brightness(1) contrast(1);
                    animation-timing-function: cubic-bezier(0.4, 0, 0.8, 1); }
  /* 900 ms : A n'est plus qu'un fantome de 12 % sous B */
  51.429%         { filter: grayscale(1) brightness(0.12) contrast(1); }
  62.857%         { filter: grayscale(1) brightness(0.12) contrast(1);
                    animation-timing-function: cubic-bezier(0.2, 0.8, 0.2, 1); }
  /* 1170 / 1250 / 1320 ms : 6 %, 2 %, puis plus rien */
  66.857%         { filter: grayscale(1) brightness(0.06) contrast(1); }
  71.429%         { filter: grayscale(1) brightness(0.02) contrast(1); }
  75.429%, 100%   { filter: grayscale(1) brightness(0) contrast(1); }
}

/* --- B : absente, affleurante, pleine, sombre, puis stabilisee ----------
   A brightness(0) elle ne modifie rien : une difference avec du noir rend
   le fond tel quel. C'est ainsi qu'elle n'existe pas encore, sans recourir
   a l'opacite. */
@keyframes gk-eink-b {
  0%, 10.286%     { filter: grayscale(1) brightness(0) contrast(1);
                    animation-timing-function: cubic-bezier(0.4, 0, 0.8, 1); }
  /* 300 ms : 10 % de B suffisent a retourner localement les pixels de A */
  17.143%         { filter: grayscale(1) brightness(0.1) contrast(1); }
  24.571%         { filter: grayscale(1) brightness(0.1) contrast(1);
                    animation-timing-function: cubic-bezier(0.4, 0, 0.8, 1); }
  /* 570 ms : carte complete des pixels qui changent d'etat */
  32.571%         { filter: grayscale(1) brightness(1) contrast(1); }
  43.429%         { filter: grayscale(1) brightness(1) contrast(1);
                    animation-timing-function: cubic-bezier(0.4, 0, 0.8, 1); }
  /* 900 ms : B domine la geometrie, mais sous-exposee */
  51.429%         { filter: grayscale(1) brightness(0.38) contrast(1.05); }
  62.857%         { filter: grayscale(1) brightness(0.38) contrast(1.05);
                    animation-timing-function: cubic-bezier(0.2, 0.8, 0.2, 1); }
  /* 1320 ms : B propre, encore un point de contraste sous la normale */
  75.429%         { filter: grayscale(0) brightness(1) contrast(0.94); }
  /* 1500 -> 1750 ms : stabilisee, puis plus aucun mouvement */
  85.714%, 100%   { filter: grayscale(0) brightness(1) contrast(1); }
}

/* --------------------------------------------------------------------------
   LE CONFIGURATEUR STUDIO

   Un seul choix, l'espace ; puis la journee de ce mur, quatre moments poses
   sur une frise. On ne compose plus une image, on regarde un lieu vivre : le
   contenu change, il change a des heures ecrites d'avance, et sa nature varie
   selon l'heure. Trois choses comprises en une interaction.

   Deux colonnes, 55 / 45. A gauche le cadre et rien d'autre que ce qui le
   commente : la frise, puis la legende. A droite le propos et les commandes,
   fer a gauche, cales en haut du cadre -- l'asymetrie verticale est voulue.
   Aucune masse noire : la section ne doit peser que par son cadre.
   -------------------------------------------------------------------------- */
.configurateur {
  display: grid;
  place-items: center;
  min-height: 100svh;
  padding-block: var(--space-lg);
}
.confi {
  display: grid;
  /* Deux colonnes cadrees, centrees ensemble plutot qu'etalees sur toute la
     largeur : en 55/45 le cadre flottait au milieu d'un vide a gauche et le
     texte allait mourir contre le bord droit de la fenetre. */
  grid-template-columns: minmax(0, 26rem) minmax(0, 28rem);
  grid-template-areas: "centre cote";
  justify-content: center;
  align-items: center;
  gap: var(--space-xl);
}
/* Le configurateur tient en un seul bloc : ainsi il se centre sur le cadre
   au lieu de se coller en haut, rangee par rangee. */
.confi__cote {
  grid-area: cote;
  display: grid;
  align-content: center;
  justify-items: start;
  gap: var(--space-lg);
}
.confi__propos {
  display: grid;
  justify-items: start;
  gap: var(--space-sm);
  text-align: left;
}
.confi__propos .eyebrow,
.confi__propos h2,
.confi__propos .lead { margin: 0; }

.confi__centre { grid-area: centre; display: grid; justify-items: center; }
.confi__commandes {
  display: grid;
  justify-items: stretch;
  gap: var(--space-md);
  width: 100%;
}

/* ---- le cadre : la meme baguette que partout ailleurs --------------------
   La largeur est aussi bornee par la hauteur d'ecran : le cadre est haut,
   sans quoi la section deborderait sur les portables a ecran bas. */
.confi__cadre {
  margin: 0;
  width: min(100%, 46svh);
  display: grid;
}
/* Le passe-partout est un carton, pas une image : il ne se rafraichit pas.
   Il vit donc HORS de la toile, dont la polarite s'inverse pendant le
   rafraichissement d'encre -- s'il etait dedans, ou incruste dans les
   visuels comme au depart, il passerait en negatif avec elles. Pose ici, il
   recouvre le bord des images et ne bouge jamais.
   Sa largeur se mesure sur l'objet : 3 % de la toile, soit 2,79 % du cadre,
   d'ou l'unite de conteneur plutot qu'une valeur figee. */
.confi__objet { position: relative; display: block; container-type: inline-size; }
.confi__ml {
  position: absolute;
  left: 3.5%;
  top: 2.9%;
  width: 93%;
  height: 94.58%;
  border: 2.79cqw solid #f2f1ec;
  z-index: 3;
  pointer-events: none;
}
.confi__baguette {
  display: block;
  width: 100%;
  height: auto;
  position: relative;
  z-index: 1;
  pointer-events: none;
}
/* L'ouverture couvre tout l'interieur du bois, passe-partout compris :
   valeurs mesurees au pixel dans l'asset de la baguette (800 x 1033). */
.confi__toile {
  position: absolute;
  left: 3.5%;
  top: 2.9%;
  width: 93%;
  height: 94.58%;
  overflow: hidden;
  background: #f2f1ec;
  z-index: 2;
  /* Le mode difference ne doit composer qu'avec l'autre couche. */
  isolation: isolate;
}
/* L'image se pose DANS le passe-partout, pas dessous. Le carton etant
   opaque et pose par-dessus, une image qui remplissait toute l'ouverture
   voyait sa bordure recouverte : les visuels qui portent du texte au ras du
   bord s'en trouvaient ampute. L'ouverture moins le carton fait exactement
   3:4, soit le format des visuels : rien n'est rogne.
   La marge est exprimee dans la meme unite que le carton (2,79 % du cadre),
   donc les deux ne peuvent pas se desaccorder. Largeur et hauteur sont
   donnees explicitement : une image, element remplace, ne s'etire pas entre
   un left et un right. */
.confi__vue {
  position: absolute;
  left: 2.79cqw;
  top: 2.79cqw;
  width: calc(100% - 5.58cqw);
  height: calc(100% - 5.58cqw);
  object-fit: cover;
  opacity: 0;
}
.confi__vue.est-vue { opacity: 1; }

/* ---- le rafraichissement de dalle ---------------------------------------
   La waveform decrite plus haut, appliquee aux couches du configurateur.
   -------------------------------------------------------------------------- */
.confi__toile.rafraichit .confi__vue--sort,
.confi__toile.rafraichit .confi__vue--entre { opacity: 1; }
.confi__toile.rafraichit .confi__vue--sort  { z-index: 1; }
.confi__toile.rafraichit .confi__vue--entre { z-index: 2; mix-blend-mode: difference; }

.confi__toile.rafraichit                     { animation: gk-eink-pol 1750ms step-end forwards; }
.confi__toile.rafraichit .confi__vue--sort   { animation: gk-eink-a 1750ms linear forwards; }
.confi__toile.rafraichit .confi__vue--entre  { animation: gk-eink-b 1750ms linear forwards; }

/* L'heure en incrustation : elle ne sert qu'en mobile, ou la frise disparait. */
.confi__horaire {
  display: none;
  position: absolute;
  left: 10%;
  bottom: 9%;
  z-index: 3;
  padding: 0.4em 0.75em;
  font-family: var(--font-sans-ext);
  font-size: 0.625rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--gk-white);
  background: rgba(23, 24, 27, 0.72);
}

/* ---- la frise horaire, sous le cadre et a sa largeur ---------------------
   Pas de pastilles vides : une ligne, et le seul palier atteint porte un
   point plein. Le reste se lit dans les heures.
   -------------------------------------------------------------------------- */
.confi__temps { width: 100%; }
/* Le rang atteint sur la frise, de 0 a 1. Declare : sans cela il change d'un
   coup, et il fallait animer separement la largeur du trait et la position du
   curseur -- deux transitions jumelles qui se desynchronisaient, le trait
   trainant derriere la bille. Une seule valeur interpolee, et les deux se
   recalculent ensemble a chaque image. */
@property --pos {
  syntax: "<number>";
  inherits: true;
  initial-value: 0;
}

.confi__frise {
  position: relative;
  min-height: 2.8rem;
}
.confi__frise[hidden] { display: none; }

/* La ligne est pointillee tant que l'heure n'est pas passee, pleine derriere
   le curseur : la journee se remplit a mesure qu'elle se consomme. */
.confi__frise::before,
.confi__frise::after {
  content: "";
  position: absolute;
  left: 4.5px;
  top: calc(0.35rem + 4px);
  height: 1px;
}
.confi__frise::before {
  right: 4.5px;
  background-image: repeating-linear-gradient(to right,
    var(--gk-mid-gray) 0 3px, transparent 3px 8px);
}
.confi__frise::after {
  width: calc(var(--pos, 0) * (100% - 9px));
  background: var(--gk-charcoal);
}
.confi__frise.glisse { transition: --pos var(--glisse, 4000ms) linear; }

/* Les paliers sont poses a la main sur la ligne : en colonnes egales, le
   premier point tombait a 12,5 % et la frise ne s'alignait pas sur le texte
   au-dessus. Le premier est desormais a fleur de bord, le dernier a l'autre. */
.confi__palier {
  position: absolute;
  top: 0;
  /* Meme calcul que le curseur, au pixel pres : reparti en pourcentages purs,
     le point du milieu tombait a 1,5 px du passage de la bille et laissait un
     croissant. */
  left: calc(4.5px + var(--i, 0) * (100% - 9px) / 4);
  transform: translateX(-50%);
  display: grid;
  justify-items: center;
  gap: 0.7rem;
  padding: 0.35rem 0 0;
  border: 0;
  background: none;
  cursor: pointer;
  font-family: var(--font-sans);
  font-size: 0.6875rem;
  white-space: nowrap;
  color: var(--gk-warm-gray);
  transition: color 300ms var(--ease);
}
.confi__palier:first-of-type { left: 0; transform: none; justify-items: start; }

/* Le point de fin : la gare ou la journee s'acheve, sans heure a lire. Il
   ferme la ligne et donne au dernier moment le parcours qu'il n'avait pas. */
.confi__terminus {
  position: absolute;
  top: calc(0.35rem);
  left: 100%;
  transform: translateX(-100%);
  width: 9px;
  height: 9px;
  border-radius: 50%;
  border: 1px solid var(--gk-mid-gray);
  background: var(--gk-off-white);
  z-index: 1;
}
.confi__palier:hover { color: var(--gk-charcoal); }
.confi__point {
  position: relative;
  z-index: 1;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  border: 1px solid var(--gk-mid-gray);
  background: var(--gk-off-white);
  transition: border-color 300ms var(--ease);
}
.confi__palier[aria-selected="true"] { color: var(--gk-charcoal); }
.confi__palier.est-passe .confi__point {
  background: var(--gk-charcoal);
  border-color: var(--gk-charcoal);
}

/* Le curseur : la seule marque pleine de la frise. Il glisse d'un palier au
   suivant pendant les quatre secondes du moment, et le cadre change quand il
   arrive. On voit donc le temps passer, au lieu de le subir. */
.confi__curseur {
  position: absolute;
  z-index: 2;
  top: calc(0.35rem + 4px);
  left: calc(4.5px + var(--pos, 0) * (100% - 9px));
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--gk-charcoal);
  transform: translate(-50%, -50%);
  pointer-events: none;
}


/* ---- la legende : le tag en prefixe, sur la meme ligne -------------------
   Un bloc de tag autonome faisait une troisieme masse sous le cadre pour
   deux mots. Il est desormais l'amorce de la phrase.
   -------------------------------------------------------------------------- */
.confi__dits {
  display: grid;
  width: 100%;
  margin-top: 1.5rem;
  min-height: 4.6rem;
}
.confi__dit {
  grid-area: 1 / 1;
  margin: 0;
  text-align: center;
  opacity: 0;
  transition: opacity 220ms var(--ease);
}
.confi__dit.est-vue { opacity: 1; }
/* L'usage se lit d'abord, la scene ensuite : colles sur une meme ligne, les
   deux se mangeaient. */
.confi__tag {
  display: block;
  font-family: var(--font-sans-ext);
  font-size: 0.625rem;
  text-transform: uppercase;
  letter-spacing: 0.18em;
  color: var(--gk-warm-gray);
}
.confi__legende {
  display: block;
  margin: 0.65rem auto 0;
  max-width: 40ch;
  font-family: var(--font-serif);
  font-size: 1.0625rem;
  line-height: 1.55;
  color: var(--gk-charcoal);
}

/* ---- le selecteur d'espace et le deroule ---------------------------------
   Une liste verticale : cinq lieux se lisent, ils ne se parcourent pas. */
.confi__bloc { display: grid; gap: 0.85rem; justify-items: start; }
/* L'intitule et la commande de pause partagent la largeur de la frise :
   le bouton se pose au bout de la ligne, pas au-dessus d'elle. */
.confi__tete {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  width: 100%;
}
.confi__pause {
  flex-shrink: 0;
  display: grid;
  place-items: center;
  width: 1.65rem; height: 1.65rem;
  padding: 0;
  border: 1px solid var(--gk-mid-gray);
  border-radius: 0;
  background: none;
  color: var(--muted);
  cursor: pointer;
  transition: color var(--dur-fast) var(--ease), background-color var(--dur-fast) var(--ease);
}
.confi__pause svg { width: 0.62rem; height: 0.62rem; fill: currentColor; }
.confi__pause:hover { color: var(--gk-black); background: rgba(0, 0, 0, 0.04); }
.confi__pause:focus-visible { outline: 1px solid currentColor; outline-offset: 3px; }
/* Au repos il propose la pause (deux barres) ; une fois en pause, il
   propose la reprise (un triangle). Jamais les deux a la fois. */
.confi__pause-fleche { display: none; }
.confi__pause[aria-pressed="true"] .confi__pause-barres { display: none; }
.confi__pause[aria-pressed="true"] .confi__pause-fleche { display: block; }

/* Le cadre dit qu'il repond au survol. */
.confi__cadre { cursor: default; }
.confi__label {
  font-family: var(--font-sans-ext);
  font-size: var(--fs-eyebrow);
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--muted);
}
/* Les six lieux tiennent sur une ligne : au-dela, la colonne devient un
   menu et le sixieme se retrouve seul en dessous. */
.confi__espaces { display: flex; flex-wrap: wrap; gap: 0.6rem 1.15rem; }
.confi__espace {
  position: relative;
  padding: 0;
  border: 0;
  background: none;
  /* Meme fonte et meme corps que les heures de la frise : ce sont deux
     rangees de la meme nature, elles doivent se lire du meme oeil. */
  font-family: var(--font-sans);
  font-size: 0.6875rem;
  white-space: nowrap;
  color: var(--gk-mid-gray);
  cursor: pointer;
  transition: color 300ms var(--ease);
}
.confi__espace:hover { color: var(--gk-warm-gray); }
.confi__espace[aria-selected="true"] { color: var(--gk-charcoal); }
/* Un trait de la largeur du mot, six pixels dessous. Pas d'aplat : le noir
   plein d'une pastille serait la masse la plus lourde de la page. */
.confi__espace[aria-selected="true"]::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -6px;
  height: 1px;
  background: currentColor;
}

.confi__espace:focus-visible,
.confi__palier:focus-visible {
  outline: 1px solid var(--gk-charcoal);
  outline-offset: 3px;
}

/* ---- tablette et mobile : une colonne, le cadre en tete de lecture ------- */
@media (max-width: 1023px) {
  .confi {
    grid-template-columns: 1fr;
    grid-template-areas: "cote" "centre";
    gap: var(--space-lg);
    justify-items: center;
  }
  .configurateur { min-height: 0; }
  .confi__propos { max-width: 40rem; width: 100%; }
  .confi__cote { align-content: start; }
  .confi__cadre { width: min(82vw, 24rem); }
  .confi__cote { width: min(82vw, 24rem); justify-items: start; }
}

@media (max-width: 767px) {
  .confi__horaire { display: block; }
  .confi__heure { font-size: 0.625rem; }
}

@media (prefers-reduced-motion: reduce) {
  .confi__toile.rafraichit,
  .confi__toile.rafraichit .confi__vue--sort,
  .confi__toile.rafraichit .confi__vue--entre { animation: none; }
  .confi__dit,
  .confi__frise.glisse { transition: none; }
}

/* --------------------------------------------------------------------------
   LA JOURNEE EPINGLEE (pages univers)

   Le cadre tient le centre de l'ecran pendant que la page defile : le
   defilement vertical devient le temps de la journee. Les moments glissent
   horizontalement sous le cadre, la dalle se rafraichit avec la waveform
   e-ink, un fil se remplit. Sans script, rien n'est epingle : la section
   retombe en bloc statique d'un ecran, premier moment pose.
   -------------------------------------------------------------------------- */
/* Le trait de la journee s'encre a mesure qu'elle avance : --jour va de 0
   a 1, pilote par le moteur, et l'encre du trait suit. */
.journee {
  padding: 0;
  /* Le trait a venir s'assombrit un peu au fil du jour, sans jamais
     rivaliser avec l'encre pleine du trace deja parcouru. */
  --trait: rgba(0, 0, 0, calc(0.1 + 0.2 * var(--jour, 0)));
}
.journee__piste { position: relative; }
/* La hauteur de la piste fixe la vitesse de la journee : ~85 svh de
   defilement par moment, plus l'ecran epingle lui-meme. */
.journee--anime .journee__piste { height: calc(var(--n, 4) * 85svh + 100svh); }

/* La scene : le cadre fixe a gauche, et toute la droite est un paysage qui
   defile. Pas de rembourrage a droite : le train saigne jusqu'au bord de
   l'ecran, on sent qu'il en arrive d'autres. */
.journee__scene {
  display: grid;
  grid-template-columns: minmax(0, 26rem) minmax(0, 1fr);
  grid-template-rows: minmax(0, 1fr);
  align-content: center;
  align-items: center;
  column-gap: clamp(2.5rem, 6vw, 6rem);
  min-height: 100svh;
  /* La marge gauche est celle du .wrap : le cadre s'aligne sur les textes
     de la page, quel que soit l'ecran. A droite, rien : le train saigne. */
  padding: calc(4.5rem + 2svh) 0 4svh
           max(var(--gutter), calc((100vw - var(--wrap)) / 2 + var(--gutter)));
  box-sizing: border-box;
}
.journee--anime .journee__scene {
  position: sticky;
  top: 0;
  min-height: 0;
  height: 100svh;
  overflow: hidden;
}

/* La naissance de la scene : on traverse un ecran vide, la scene se cale,
   puis le cadre parait, puis le propos, chacun deja a sa place. Un pur
   fondu, aucun mouvement : rien ne monte du bas. */
.journee--anime .journee__cadre {
  opacity: 0;
  transition: opacity 1200ms var(--ease-reveal);
}
.journee--anime .journee__defile {
  opacity: 0;
  transition: opacity 1200ms var(--ease-reveal) 350ms;
}
.journee.est-nee .journee__cadre,
.journee.est-nee .journee__defile { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  .journee--anime .journee__cadre,
  .journee--anime .journee__defile {
    opacity: 1;
    transition: none;
  }
}

/* Le cadre remplit la hauteur qui reste entre le propos et le train : c'est
   la rangee qui le dimensionne, jamais lui qui la deborde. Le ratio est
   celui de la baguette (2188 x 2824), la largeur en decoule. */
.journee__cadre {
  margin: 0;
  align-self: stretch;
  justify-self: stretch;
  min-height: 0;
  display: grid;
  /* Cale a gauche, sur la gouttiere de la page ; centre en hauteur. */
  place-items: center start;
  /* La rangee se mesure elle-meme : le cadre se dimensionne en cqh, la
     seule unite qui lit de facon fiable la hauteur restante de la scene. */
  container-type: size;
}
.journee .confi__objet {
  height: 48svh; /* repli si les unites de conteneur manquent */
  /* Trois bornes : la hauteur restante de la scene, la largeur reelle de
     la colonne (le cadre ne peut plus en deborder), et un plafond. */
  height: min(100cqh, calc(100cqw * 1.2907), calc(min(38vw, 28rem) * 1.2907));
  aspect-ratio: 2188 / 2824;
}
.journee .confi__baguette { height: 100%; width: 100%; }

/* Le rafraichissement se joue plus court qu'en autoplay : il repond au
   geste du visiteur, il ne doit pas trainer derriere lui. */
.journee .confi__toile.rafraichit,
.journee .confi__toile.rafraichit .confi__vue--sort,
.journee .confi__toile.rafraichit .confi__vue--entre { animation-duration: 1100ms; }

/* ---- le train des moments : une fenetre, quatre voitures -----------------
   Chaque moment occupe la largeur de la fenetre ; le train se translate en
   pourcentages de cette largeur, au rythme exact du defilement. */
/* ---- le train : un paysage horizontal, une gare par moment ---------------
   Chaque panneau occupe la fenetre entiere du defile ; le train se
   translate en continu, au pas exact du geste. Le premier panneau porte le
   propos, les suivants une heure en tres grand, la ligne qui traverse tout
   l'ecran, et la legende. */
/* Les bords de la fenetre sont des fondus, pas des lames : ce qui entre ou
   sort se dissout au lieu de se faire trancher. Le fondu gauche est colle
   au bord du cadre (--bord-cadre, mesure par le moteur) : le texte semble
   passer derriere le bois. */
.journee__defile {
  overflow: hidden;
  align-self: center;
  min-width: 0;
  -webkit-mask-image: linear-gradient(to right,
    transparent 0, #000 4.5rem, #000 92%, transparent 100%);
  mask-image: linear-gradient(to right,
    transparent 0, #000 4.5rem, #000 92%, transparent 100%);
}
/* La scene epinglee (sticky) est deja le referent des enfants absolus :
   surtout ne pas la repasser en relative, cela deferait l'epingle. */
@media (min-width: 1024px) {
  .journee--anime .journee__defile {
    position: absolute;
    left: var(--bord-cadre, 32rem);
    right: 0;
    top: 50%;
    translate: 0 -50%;
  }
}
.journee__train { display: flex; will-change: transform; }
.journee__panneau {
  flex: 0 0 100%;
  min-width: 0;
  /* Le retrait gauche ecarte franchement le texte du cadre : la gare se
     lit dans son propre espace, loin du bord et de la zone de fondu. */
  --retrait: clamp(4rem, 16vw, 15rem);
  --issue: clamp(2rem, 8vw, 7rem);
  padding-left: var(--retrait);
  padding-right: var(--issue);
  text-align: left;
}
/* Le fondu est pilote image par image depuis le moteur (--fondu) : le
   texte s'eteint avant que le bord de la fenetre puisse le trancher. La
   ligne, elle, reste pleine et traverse. */
.journee__heure,
.journee__dit,
.journee__panneau--intro > * { opacity: var(--fondu, 1); }
.journee__heure { transform: translateX(calc(var(--gliss, 0) * 1rem)); }

.journee__panneau--intro {
  display: grid;
  justify-items: start;
  gap: 0.9rem;
}
.journee__panneau--intro .eyebrow,
.journee__panneau--intro h2 { margin: 0; }
.journee__lead {
  margin: 0;
  max-width: 42ch;
  font-size: 0.9375rem;
  line-height: 1.6;
  color: var(--muted);
}

.journee__heure {
  margin: 0;
  font-family: var(--font-serif);
  font-size: clamp(3rem, 6.5vw, 5.5rem);
  line-height: 1;
  letter-spacing: -0.02em;
  color: var(--gk-charcoal);
}

/* La ligne de la journee : elle court d'une gare a l'autre sans se rompre,
   par-dessus les rembourrages du panneau, et son point s'encre au passage.
   Le point, lui, reste sur l'axe du texte. */
.journee__gare {
  position: relative;
  height: 9px;
  margin: 1.6rem 0 1.4rem;
}
/* Chaque gare ne dessine que son propre troncon : de son point jusqu'a
   celui de la gare suivante, exactement. Les troncons se posent bout a
   bout et forment une seule ligne continue, au lieu de se chevaucher et
   de defiler les uns sur les autres. */
.journee__gare::before,
.journee__gare::after {
  content: "";
  position: absolute;
  left: 0;
  right: calc(-1 * (var(--retrait, 0rem) + var(--issue, 0rem)));
  top: 4px;
  height: 1px;
}
.journee__gare::before { background: var(--trait); }
/* Le trace : le troncon s'encre pendant le voyage vers la gare suivante
   (--rempli, pilote du moteur). Les gares passees sont donc pleines, celle
   qu'on quitte se remplit, celles a venir restent pales : a l'ecran, une
   seule ligne encree jusqu'au point, pale au-dela. */
.journee__gare::after {
  background: var(--gk-charcoal);
  transform: scaleX(var(--rempli, 0));
  transform-origin: left center;
}
.journee__point {
  position: absolute;
  left: 0;
  top: 0;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  border: 1px solid var(--gk-mid-gray);
  background: transparent;
  transition: background-color 400ms var(--ease), border-color 400ms var(--ease);
}
.journee__panneau.est-passe .journee__point {
  background: var(--gk-charcoal);
  border-color: var(--gk-charcoal);
}

.journee__dit { margin: 0; max-width: 44ch; }
.journee__panneau .confi__legende {
  margin-inline: 0;
  max-width: none;
}
.journee__dit .confi__legende { margin-top: 0.45rem; }

/* ---- tablette et mobile : le cadre au-dessus, le voyage en dessous ------- */
@media (max-width: 1023px) {
  .journee__scene {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: minmax(0, 1fr) auto;
    row-gap: clamp(1.4rem, 3.5svh, 2.4rem);
    justify-items: center;
    padding-inline: clamp(1.25rem, 5vw, 3rem) 0;
  }
  .journee__cadre { justify-self: center; place-items: center; width: 100%; }
  .journee__defile { width: 100%; }
  .journee__panneau {
    --retrait: clamp(1.5rem, 6vw, 3rem);
    --issue: clamp(1.5rem, 10vw, 4rem);
  }
  .journee .confi__objet {
    height: 34svh;
    height: min(100cqh, calc(100cqw * 1.2907), calc(min(70vw, 20rem) * 1.2907));
  }
}
@media (max-height: 620px) {
  .journee__lead { display: none; }
}
@media (max-width: 767px) {
  .journee__heure { font-size: 2.5rem; }
  .journee__panneau .confi__legende { font-size: 0.9375rem; }
}

/* --------------------------------------------------------------------------
   LES LICENCES STUDIO
   Trois colonnes, pas trois cartes. Chacune dit d'abord par quelle porte on y
   entre, puis son nom, son montant, et ce qu'elle ajoute. Le detail vit sur la
   page tarifs : ici on montre qu'une licence accompagne deja chaque display.
   Meme hierarchie que la page tarifs : mention en capitales, nom en fonte de
   texte, montant en serif. Les deux pages parlent la meme langue.
   -------------------------------------------------------------------------- */
.licences {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-md);
}
.licences > li {
  display: grid;
  align-content: start;
  gap: 0.55rem;
  border-top: 1px solid rgba(0, 0, 0, 0.16);
  padding-top: 1.3rem;
}
.section--dark .licences > li { border-top-color: rgba(255, 255, 255, 0.18); }

.licence__badge {
  font-family: var(--font-sans-ext);
  font-weight: 400;
  font-size: var(--fs-eyebrow);
  text-transform: uppercase;
  letter-spacing: 0.2em;
  line-height: 1.5;
  color: var(--muted);
}
.licence__nom {
  font-family: var(--font-sans);
  font-size: 1.0625rem;
  font-weight: 500;
  line-height: 1.2;
  letter-spacing: 0.02em;
  margin-top: 0.35rem;
}
.licence__prix {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0 0.5rem;
  margin-top: 0.1rem;
}
.licence__montant {
  font-family: var(--font-serif);
  font-size: clamp(2rem, 2.8vw, 2.6rem);
  line-height: 1;
  letter-spacing: -0.02em;
}
.licence__unite {
  font-family: var(--font-sans);
  font-size: 0.8125rem;
  line-height: 1.4;
  color: var(--muted);
}
.licence__note {
  font-size: 0.8125rem;
  line-height: 1.65;
  color: var(--muted);
  margin-top: 0.6rem;
  max-width: 26ch;
}

@media (max-width: 900px) {
  .licences { grid-template-columns: 1fr; gap: var(--space-sm); }
  .licence__note { max-width: none; }
}

/* --------------------------------------------------------------------------
   LA SCENE STUDIO
   L'application entre de face, en grand, puis recule sur la droite en trois
   quarts pour devenir la demonstration qu'on regarde en lisant le parcours.
   Un seul element du debut a la fin : deux images, meme identiques, se
   liraient comme une coupure.
   -------------------------------------------------------------------------- */
.studio-scene {
  position: relative;
  background: var(--gk-black);
  /* L'application deborde a droite en fin de course : on la coupe sans faire
     de la scene un conteneur de defilement, ce qui decrocherait les sticky.
     Sur les deux axes : au rechargement en cours de page, elle debordait par
     le bas sur la section suivante. */
  overflow: clip;
}

.app-vol {
  position: sticky;
  top: 0;
  height: 100svh;
  display: grid;
  place-items: center;
  z-index: 5;
  pointer-events: none;
}

.app-vol__scene {
  /* Les cinq ecrans se rangent dans la meme case de grille : ils se
     superposent sans sortir du flux. En positionnement absolu a l'interieur
     de la transformation en perspective, ils ne s'affichaient plus du tout. */
  display: grid;
  width: min(67.5rem, 88vw);
  /* Le voyage : translation, echelle, puis bascule en perspective. L'ordre
     compte, perspective() ne s'applique qu'a ce qui la suit. */
  transform:
    translate3d(var(--vx, 0px), var(--vy, 0px), 0)
    scale(var(--vs, 1))
    perspective(100rem)
    rotateY(var(--vry, 0deg))
    rotateX(var(--vrx, 0deg));
  transform-origin: center;
  will-change: transform;
}

/* Les cinq ecrans se superposent, un seul se voit. Le fondu est lent : c'est
   l'application qui change d'etat, pas une diapositive qui claque. */
.app-vol__ecran {
  grid-area: 1 / 1;
  display: block;
  width: 100%;
  height: auto;
  opacity: 0;
  transition: opacity 700ms var(--ease);
}
.app-vol__ecran.est-vu { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .app-vol__ecran { transition: none; }
}

@media (max-width: 900px) {
  /* Pas de voyage sur telephone : l'application se pose une fois, de face,
     sous le chapeau, et le parcours se lit ensuite. */
  .app-vol {
    position: static;
    height: auto;
    padding-block: var(--space-xl);
  }
  .app-vol__scene { width: 92vw; transform: none; }
  /* L'image ne suit plus les chapitres : seul l'ecran par defaut reste. */
  .app-vol__ecran:not(.est-vu) { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .app-vol__scene { transform: none; }
}

/* --------------------------------------------------------------------------
   LE PARCOURS STUDIO
   Quatre chapitres qui se suivent, simplement. Celui qu'on traverse s'allume,
   les autres reculent d'un cran : c'est la lecture qui avance, pas un
   dispositif. L'application reste epinglee a cote, du debut a la fin.
   -------------------------------------------------------------------------- */
.parcours {
  --parcours-retrait: 4.5rem;
}

.parcours__grille {
  display: grid;
  grid-template-columns: minmax(0, 6fr) minmax(0, 5fr);
  gap: var(--space-xl);
  align-items: start;
}

/* La case ou l'application se tient : vide, elle ne fait que reserver la
   colonne de droite. */
.parcours__media {
  position: sticky;
  top: calc(var(--space-lg) + 3rem);
  aspect-ratio: 2061 / 1487;
}

.parcours__etapes {
  list-style: none;
  margin: 0;
  padding: 0;
}

/* Chaque chapitre se donne les trois quarts d'un ecran. Le texte reste en
   haut, sous son filet ; l'air qui suit est du temps de defilement, celui
   qu'il faut pour que l'image d'a cote change avant le chapitre suivant. */
.parcours__etape {
  border-top: 1px solid rgba(255, 255, 255, 0.14);
  padding: var(--space-lg) 0;
  min-height: 78svh;
}
/* Le retrait n'apparait que si le script a pris la main : sans lui, les
   quatre chapitres se lisent tous, pleinement. */
.parcours__etapes[data-anime] .parcours__etape {
  opacity: 0.34;
  transition: opacity 760ms var(--ease);
}
.parcours__etapes[data-anime] .parcours__etape.est-lue { opacity: 1; }

.parcours__tete {
  display: grid;
  grid-template-columns: var(--parcours-retrait) minmax(0, 1fr);
  align-items: baseline;
}

/* Numerotation entre crochets, comme partout ailleurs sur le site. */
.parcours__num {
  font-family: var(--font-sans-ext);
  font-size: 0.5625rem;
  font-weight: 500;
  letter-spacing: 0;
  line-height: 1;
  color: rgba(255, 255, 255, 0.35);
}
/* Le titre est un reperage, pas une phrase : capitales et interlettrage, le
   registre des intertitres. C'est le texte en dessous qui parle. */
.parcours__tete h3 {
  font-family: var(--font-sans-ext);
  font-size: var(--fs-eyebrow);
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--gk-white);
  margin: 0;
}

.parcours__corps {
  padding: var(--space-md) 0 0 var(--parcours-retrait);
  max-width: 48ch;
}
.parcours__corps p {
  color: var(--gk-mid-gray);
  font-size: var(--fs-body);
  line-height: 1.8;
}

@media (max-width: 900px) {
  /* Plus de place pour epingler quoi que ce soit : la surface passe devant,
     les quatre chapitres se lisent ensuite, d'un trait. */
  .parcours__grille { grid-template-columns: 1fr; gap: var(--space-lg); }
  .parcours__media { display: none; }
  /* Sur telephone l'image ne suit pas : l'air entre les chapitres n'aurait
     plus d'objet, ils se lisent d'un trait. */
  .parcours__etape { padding: var(--space-md) 0; min-height: 0; }
  .parcours__etapes[data-anime] .parcours__etape { opacity: 1; }
  .parcours__corps { padding: var(--space-sm) 0 0 var(--parcours-retrait); }
}

@media (prefers-reduced-motion: reduce) {
  .parcours__etapes[data-anime] .parcours__etape { opacity: 1; transition: none; }
}

/* --------------------------------------------------------------------------
   7. HEADER
   -------------------------------------------------------------------------- */
.site-header {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 100;
  isolation: isolate;
  padding-block: 1.5rem;
  transition: background var(--dur-fast) var(--ease),
              padding var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease),
              opacity 900ms var(--ease);
  border-bottom: 1px solid transparent;
}
.site-header.is-scrolled {
  background: rgba(0, 0, 0, 0.82);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  padding-block: 1rem;
  border-bottom-color: rgba(255, 255, 255, 0.08);
}

.site-header__inner {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: var(--space-sm);
}

.nav {
  display: flex;
  align-items: center;
  gap: var(--space-md);
}

/* Restreint a .nav__link : appliquee a tous les liens du menu, cette regle
   imposait capitales et interlettrage aux descriptions du panneau. */
.nav > .nav__item > .nav__link {
  color: var(--gk-white);
  opacity: 0.7;
  transition: opacity var(--dur-fast) var(--ease);
}
/* Le survol doit se dire ici aussi : plus specifique que .nav__link:hover,
   cette regle l'emportait et gardait a 0.7 les entrees sans panneau. Les
   autres s'allumaient par .is-open, que le script ne leur donne jamais :
   Tarifs restait donc grise sous le curseur. */
/* Le survol seulement : la page courante ne reste pas allumee, une entree
   grise sous le curseur et blanche au repos se lirait comme un etat fige. */
.nav > .nav__item > .nav__link:hover,
.nav > .nav__item > .nav__link:focus-visible { opacity: 1; }

.brand {
  display: flex;
  align-items: center;
  justify-self: center;
}
.brand img { height: 1.1875rem; width: auto; }

/* En-tete posee sur une page qui s'ouvre en clair : ecrite en blanc elle
   serait invisible. Tout passe au noir tant que la barre reste transparente,
   c'est a dire tant qu'on n'a pas defile et qu'aucun panneau n'est ouvert :
   dans ces deux cas la barre redevient noire et reprend son texte blanc. */
.site-header--sur-clair:not(.is-scrolled):not(:has(.nav__item--has-menu.is-open)) {
  color: var(--gk-black);
}
.site-header--sur-clair:not(.is-scrolled):not(:has(.nav__item--has-menu.is-open)) .brand img {
  filter: invert(1);
}
.site-header--sur-clair:not(.is-scrolled):not(:has(.nav__item--has-menu.is-open)) .nav__link,
.site-header--sur-clair:not(.is-scrolled):not(:has(.nav__item--has-menu.is-open)) .nav__social,
.site-header--sur-clair:not(.is-scrolled):not(:has(.nav__item--has-menu.is-open)) .prefs__label,
.site-header--sur-clair:not(.is-scrolled):not(:has(.nav__item--has-menu.is-open)) .lang-switch {
  color: var(--gk-black);
}
.site-header--sur-clair:not(.is-scrolled):not(:has(.nav__item--has-menu.is-open)) .nav__cta {
  background: var(--gk-black);
  color: var(--gk-white);
}
.site-header--sur-clair:not(.is-scrolled):not(:has(.nav__item--has-menu.is-open)) .nav__cta { border-color: rgba(0, 0, 0, 0.25); }
.site-header--sur-clair:not(.is-scrolled):not(:has(.nav__item--has-menu.is-open)) .burger span {
  background: var(--gk-black);
}

/* Le CTA vit hors du bloc .nav : il porte donc sa propre typographie. */
.nav__cta {
  display: inline-flex;
  align-items: center;
  font-family: var(--font-sans-ext);
  font-size: var(--fs-eyebrow);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  /* Le filet compense son epaisseur sur le padding : le CTA garde la
     hauteur qu'il avait, la barre ne bouge pas. Invisible au repos, pose
     sur le fond du bouton, il dessine le contour une fois retourne. */
  padding: calc(1em - 1px) calc(1.9em - 1px);
  border: 1px solid rgba(255, 255, 255, 0.35);
  background: var(--gk-white);
  color: var(--gk-black);
  position: relative;
  isolation: isolate;
  overflow: hidden;
  transition: background var(--dur-fast) var(--ease),
              color var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease);
}
/* Le meme voile que les .btn, en difference : quel que soit le fond du
   moment (barre sombre, barre sur section claire, page passee au clair),
   il retourne le bouton sans qu'on ait a lui decrire chaque cas. */
.nav__cta::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  background: var(--gk-white);
  mix-blend-mode: difference;
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 520ms var(--ease-reveal);
  pointer-events: none;
}
.nav__cta:hover::before { transform: scaleX(1); }

.nav__social svg { width: 1rem; height: 1rem; fill: currentColor; }

/* Burger */
.burger {
  display: none;
  width: 40px;
  height: 40px;
  position: relative;
  justify-self: end;
}
.burger span {
  position: absolute;
  left: 9px;
  width: 22px;
  height: 1px;
  background: var(--gk-white);
  transition: transform var(--dur-fast) var(--ease), opacity 200ms;
}
.burger span:nth-child(1) { top: 17px; }
.burger span:nth-child(2) { top: 23px; }
.burger.is-open span:nth-child(1) { transform: translateY(3px) rotate(45deg); }
.burger.is-open span:nth-child(2) { transform: translateY(-3px) rotate(-45deg); }

@media (max-width: 1000px) {
  .site-header__inner { grid-template-columns: auto 1fr; }
  .brand { justify-self: start; order: -1; }
  .burger { display: block; }
}

/* Menu mobile
   Il s'ouvre sous la barre, qui reste visible avec son logo et sa croix.
   Les rubriques sont repliees : la liste tient dans l'ecran sans defiler,
   et l'on distingue enfin une rubrique de ses pages. */
.mobile-nav {
  position: fixed;
  inset: 0;
  z-index: 99;
  background: var(--gk-black);
  display: flex;
  flex-direction: column;
  padding: calc(var(--space-xl) + 3rem) var(--gutter) var(--space-md);
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--dur-fast) var(--ease), visibility var(--dur-fast);
}
.mobile-nav.is-open { opacity: 1; visibility: visible; }

/* --------------------------------------------------------------------------
   8. HERO
   -------------------------------------------------------------------------- */
.hero {
  position: relative;
  min-height: 100svh;
  display: flex;
  align-items: flex-end;
  overflow: hidden;
  background: var(--gk-black);
}

.hero__media {
  position: absolute;
  inset: 0;
  z-index: 0;
  background: var(--gk-black);
}
.hero__media img,
.hero__media video {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* La vidéo alterne des plans très clairs et très sombres.
   Deux couches : un voile uniforme discret pour tenir les plans blancs,
   puis un dégradé bas appuyé sous lequel le texte reste toujours lisible. */
.hero__media::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  background: rgba(0, 0, 0, 0.16);
}
/* Le texte occupe les 40 % inférieurs : le dégradé s'assombrit à partir de 28 %
   pour que le titre repose toujours sur un fond sombre, quel que soit le plan.
   Et il ne s'arrête pas à mi-teinte : il atteint le noir plein au ras du bord
   bas. La vidéo se dissout alors dans le noir de la section suivante au lieu
   de s'arrêter net sur une ligne. Le durcissement se fait sous le texte, dans
   les derniers pourcents, pour ne pas assombrir les deux CTA. */
.hero__media::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  background:
    linear-gradient(to bottom,
      rgba(0,0,0,0.42) 0%,
      rgba(0,0,0,0.06) 16%,
      rgba(0,0,0,0.08) 28%,
      rgba(0,0,0,0.34) 48%,
      rgba(0,0,0,0.56) 72%,
      rgba(0,0,0,0.68) 82%,
      rgba(0,0,0,0.84) 91%,
      rgba(0,0,0,0.95) 96%,
      rgba(0,0,0,1) 100%);
}

/* Texte ancré en bas : la vidéo respire au-dessus,
   le texte repose sur la zone la plus sombre du dégradé. */
.hero__inner {
  position: relative;
  z-index: 2;
  width: 100%;
  padding-bottom: var(--space-xl);
  padding-top: var(--space-2xl);
}

/* ---- le hero des fiches d'espace ----------------------------------------
   Meme ouverture que l'accueil, mais le texte occupe les deux bouts : le
   titre en haut, le constat en bas. Le degrade doit donc appuyer aux deux
   extremites et laisser respirer le milieu de l'image.
   -------------------------------------------------------------------------- */
.hero--fiche { align-items: stretch; }

/* Le constat : le titre a gauche sur l'image, le paragraphe a droite sur
   un panneau noir. La colonne de droite partage son axe avec le panneau
   de la section objet, juste en dessous. */
.probleme {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  align-items: center;
}
/* L'image du hero derive plus lentement que la page (data-parallax) : la
   sur-echelle couvre le debattement, aucun bord ne se decouvre. */
.hero--fiche .hero__media img { scale: 1.08; }

.probleme__titre { padding-right: var(--space-lg); }
/* Pas de fond : seul le retrait tient l'axe, partage avec la carte objet. */
.probleme__panneau { padding-inline: var(--gouttiere-carte); }
@media (max-width: 860px) {
  /* En une colonne, le retrait qui tenait l'axe commun n'a plus d'objet :
     le constat s'aligne sur le titre, comme le reste de la page. */
  .probleme { grid-template-columns: 1fr; gap: var(--space-sm); }
  .probleme__titre { padding-right: 0; }
  .probleme__panneau { padding-inline: 0; }
}
.hero--fiche .hero__media::after {
  background:
    linear-gradient(to bottom,
      rgba(0, 0, 0, 0.86) 0%,
      rgba(0, 0, 0, 0.48) 24%,
      rgba(0, 0, 0, 0.28) 44%,
      rgba(0, 0, 0, 0.58) 62%,
      rgba(0, 0, 0, 0.84) 78%,
      rgba(0, 0, 0, 0.94) 100%);
}
.hero--fiche .hero__inner {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: var(--space-xl);
  padding-top: var(--space-2xl);
  padding-bottom: var(--space-xl);
}
.hero--fiche .hero__title { max-width: 20ch; }
.hero--fiche .hero__bas h2 {
  font-size: var(--fs-h3, 1.6rem);
  margin-top: 0.6rem;
}
.hero--fiche .hero__bas .prose { color: rgba(255, 255, 255, 0.82); }
.hero--fiche .hero__bas .prose p { margin: 0; }

@media (max-width: 860px) {
  /* Sur un ecran etroit, le paragraphe complet ne tient plus sous le titre :
     le hero s'allonge plutot que de rogner le texte. */
  .hero--fiche { min-height: 0; }
  .hero--fiche .hero__inner {
    gap: var(--space-lg);
    padding-top: calc(var(--space-2xl) * 0.7);
  }
}

.hero__title { max-width: 17ch; }
/* Le chapo du hero descend au corps de texte : le titre porte seul. */
.hero__sub {
  margin-top: var(--space-sm);
  max-width: 54ch;
  font-size: var(--fs-body);
  line-height: 1.75;
  letter-spacing: 0;
}

/* Indicateur de scroll */
/* Décalé à droite : il n'entre plus en conflit avec le texte à gauche */

/* Hero secondaire (pages internes) */
.page-hero {
  padding-top: calc(var(--space-xl) + 5rem);
  padding-bottom: var(--space-xl);
  background: var(--gk-black);
  /* Tous les chapeaux de page se lisent sur un axe central : le titre, son
     intertitre et son chapo descendent ensemble. */
  text-align: center;
}
.page-hero .wrap > * { margin-inline: auto; }
/* Le chapô du hero peut respirer plus large que dans le corps de page :
   à 38ch il cassait sur un mot orphelin. */
.page-hero .lead { max-width: 52ch; }
.hero__sub { max-width: 46ch; }

/* Chapeau de page centre : quand l'introduction porte a elle seule tout ce
   qu'il y a a dire avant la comparaison. */
.page-hero--centre .wrap { max-width: 62ch; text-align: center; }
.page-hero--centre .lead { max-width: 52ch; margin: var(--space-md) auto 0; }

/* Chapeau aligne a gauche : quand ce qui suit n'est pas un discours mais une
   grille, le titre prend le meme bord que le tableau qu'il annonce. Centre,
   il flottait au-dessus d'une colonne alignee a gauche. */
.page-hero--gauche { text-align: left; }
.page-hero--gauche .wrap > * { margin-inline: 0; }
.page-hero--gauche .lead { margin-left: 0; margin-right: 0; }

/* Chapeau clair : l'introduction et ce qu'elle annonce partagent alors le
   meme fond, et la page s'ouvre d'un seul tenant au lieu de se couper en
   deux. Le bas n'a plus de marge : la suite enchaine. */
.page-hero--clair {
  background: var(--gk-white);
  color: var(--gk-charcoal);
  --muted: #68685f;
  --body-ink: var(--gk-charcoal);
  padding-bottom: var(--space-lg);
}
.page-hero--clair .eyebrow { color: var(--muted); }
.page-hero--clair h1 { color: var(--gk-black); }
.page-hero--clair .lead { color: var(--muted); }

/* La suite immediate : pas de padding haut, le bloc de texte fait office
   d'en-tete de section. */
.section--formats { padding-top: 0; padding-bottom: var(--space-2xl); }

/* Chapeau noir suivi d'une section noire : rien ne separe les deux fonds, et
   leurs deux marges s'additionnaient en un long vide. Le chapeau et ce qu'il
   annonce se lisent d'un trait : une seule respiration entre les deux. */
.page-hero:has(+ .section--dark) { padding-bottom: var(--space-lg); }
.page-hero + .section--dark { padding-top: var(--space-md); }

/* --------------------------------------------------------------------------
   9. STATS
   -------------------------------------------------------------------------- */
.stats {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  border-top: 1px solid rgba(255, 255, 255, 0.14);
}
.stats > div {
  padding: var(--space-md) var(--space-sm) var(--space-md) 0;
  border-right: 1px solid rgba(255, 255, 255, 0.14);
  /* La legende est poussee en bas de sa cellule : sans cela, un chiffre sur
     deux lignes ("Remote control") decalait sa legende vers le bas et les
     quatre legendes ne formaient plus une ligne. */
  display: flex;
  flex-direction: column;
}
.stats .stat__label { margin-top: auto; padding-top: 1rem; }
.stats > div:last-child { border-right: 0; }
.stats > div:not(:first-child) { padding-left: var(--space-sm); }

.stat__value {
  font-family: var(--font-serif);
  font-size: var(--fs-h3);
  line-height: 1.04;
  letter-spacing: -0.015em;
}
.stat__label {
  font-family: var(--font-sans-ext);
  font-size: var(--fs-eyebrow);
  text-transform: uppercase;
  letter-spacing: 0.2em;
  line-height: 1.35;   /* la legende tient parfois sur deux lignes */
  color: var(--muted);
  margin-top: 1rem;
  /* "Wi-Fi" se coupait apres le trait d'union, en plein milieu du mot. Un
     trait d'union est un point de coupure legitime pour le navigateur ;
     il faut le lui interdire ici, pas ailleurs. */
  hyphens: none;
}
.stat__label .nb { white-space: nowrap; }

/* Au survol d'une colonne : le chiffre se souleve d'un cheveu, et sa legende
   le suit juste apres en sortant de sa penombre. Pas de filet sous le mot :
   souligne, il se lisait comme un lien alors que rien ne se clique ici. Les
   filets de la grille ne changent jamais de couleur, c'est une regle de
   marque. Rien de tout cela sur un ecran tactile, ou le survol reste colle
   une fois qu'on a touche. */
@media (hover: hover) {
  .stat__value,
  .stat__label { transition: transform 700ms var(--ease), color 700ms var(--ease); }
  .stat__label { transition-delay: 90ms; }

  .stats > div:hover .stat__value { transform: translateY(-3px); }
  .stats > div:hover .stat__label {
    transform: translateY(-2px);
    color: var(--gk-white);
  }

  /* Sur la phase claire du cameleon, la legende va au noir, pas au blanc. */
  body.gk-clair .stats > div:hover .stat__label { color: var(--gk-black); }
}

@media (prefers-reduced-motion: reduce) {
  .stat__value,
  .stat__label { transition: none; }
  .stats > div:hover .stat__value,
  .stats > div:hover .stat__label { transform: none; }
}

@media (max-width: 780px) {
  .stats { grid-template-columns: repeat(2, 1fr); }
  .stats > div:nth-child(2n) { border-right: 0; }
  .stats > div:nth-child(n+3) { border-top: 1px solid rgba(255,255,255,0.14); }
  /* Seule la cellule qui ouvre une rangee colle au bord : la troisieme
     ouvre la rangee du bas et perd donc sa marge gauche, que la regle
     generale lui donnait parce qu'elle n'est pas la premiere de la grille. */
  .stats > div:nth-child(2n+1) { padding-left: 0; }
  .stats > div { padding-block: var(--space-lg); }
  /* Un interlettrage de 0.2em dans une colonne de 160 px faisait casser
     chaque legende. On le resserre, et la legende tient sur sa ligne. */
  .stat__label { letter-spacing: 0.12em; }
}

/* --------------------------------------------------------------------------
   10. CAS D'USAGE
   -------------------------------------------------------------------------- */

.usecase {
  position: relative;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  background: var(--gk-near-black);
}
.usecase img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0.85;
  /* Une photographie de 1000 px qu'on met a l'echelle sans la promouvoir sur
     sa propre couche est re-rasterisee a chaque image : le zoom avance alors
     par paliers, et se lit comme une coupure plutot que comme un mouvement.
     translateZ(0) la promeut une fois pour toutes. L'echelle et la lumiere
     partagent desormais la meme duree et la meme courbe : elles montent
     ensemble, au lieu que la luminosite arrive la premiere. */
  transform: translateZ(0);
  transition: transform 1200ms var(--ease-reveal), opacity 1200ms var(--ease-reveal);
}
@media (hover: hover) {
  .usecase:hover img {
    transform: translateZ(0) scale(1.035);
    opacity: 1;
  }
}
.usecase figcaption {
  position: absolute;
  inset: auto 0 0 0;
  padding: var(--space-sm);
  background: linear-gradient(to top, rgba(0,0,0,0.8), transparent);
  font-family: var(--font-serif);
  font-size: 1.125rem;
  line-height: 1.15;
  letter-spacing: -0.015em;
  color: var(--gk-white);
}


/* --------------------------------------------------------------------------
   11. SPECS
   -------------------------------------------------------------------------- */
/* La grille est solidaire du theme de sa section.
   Avec un fond noir fige, le texte d'une section claire devenait illisible. */
.specs {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-lg) var(--space-md);
}
@media (max-width: 860px) { .specs { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 520px) { .specs { grid-template-columns: 1fr; } }

.spec { padding: 0; }
.spec dt {
  font-family: var(--font-sans-ext);
  font-size: var(--fs-eyebrow);
  text-transform: uppercase;
  letter-spacing: 0.2em;
  line-height: 1;
  color: var(--muted);
}
.spec dd {
  font-family: var(--font-serif);
  font-size: var(--fs-h3);
  line-height: 1.08;
  letter-spacing: -0.015em;
  margin-top: 1rem;
}

/* --------------------------------------------------------------------------
   12. CARTES NUMÉROTÉES (Frame / Studio)
   -------------------------------------------------------------------------- */
/* Les separateurs sont de vrais filets, pas un fond gris qu'on recouvre de
   cases opaques : ce montage laissait voir la trame grise le temps que la
   page peigne, et le fond de section suffit en dessous. */
.steps { display: grid; gap: 0; grid-template-columns: repeat(4, 1fr); }
@media (max-width: 900px) { .steps { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px) { .steps { grid-template-columns: 1fr; } }

.step { padding: var(--space-md) var(--space-sm); }
/* Les cellules de bord ne portent pas de retrait exterieur : la premiere et
   la derniere s'alignent alors sur le titre de section, pas sur une gouttiere
   interne. */
.steps > .step:first-child { padding-left: 0; }
.steps > .step:last-child { padding-right: 0; }
@media (max-width: 900px) {
  .steps > .step:nth-child(2n+1) { padding-left: 0; }
  .steps > .step:nth-child(2n) { padding-right: 0; }
}
@media (max-width: 560px) {
  .steps > .step { padding-left: 0; padding-right: 0; }
}
.step + .step { border-left: 1px solid rgba(255, 255, 255, 0.14); }
.section--light .step + .step,
.section--paper .step + .step { border-left-color: rgba(0, 0, 0, 0.12); }

/* En deux colonnes, la premiere de chaque ligne ne porte pas de filet a
   gauche, mais les lignes suivantes en portent un au-dessus. */
@media (max-width: 900px) {
  .steps > .step:nth-child(2n+1) { border-left: 0; }
  .steps > .step:nth-child(n+3) { border-top: 1px solid rgba(255, 255, 255, 0.14); }
  .section--light .steps > .step:nth-child(n+3),
  .section--paper .steps > .step:nth-child(n+3) { border-top-color: rgba(0, 0, 0, 0.12); }
}
@media (max-width: 560px) {
  .steps > .step + .step { border-left: 0; border-top: 1px solid rgba(255, 255, 255, 0.14); }
  .section--light .steps > .step + .step,
  .section--paper .steps > .step + .step { border-top-color: rgba(0, 0, 0, 0.12); }
}

.step__num {
  font-family: var(--font-sans-ext);
  font-size: 0.5625rem;
  letter-spacing: 0;
  color: var(--muted);
  display: block;
  margin-bottom: var(--space-sm);
}
.step__title { margin-bottom: 0.6rem; }
.step__text { font-size: var(--fs-body); color: var(--gk-mid-gray); }
.section--light .step__text, .section--paper .step__text { color: var(--muted); }

/* Bloc détaillé alterné */
.section--dark .feature { border-top-color: rgba(255, 255, 255, 0.12); }
@media (max-width: 900px) {
  .feature { grid-template-columns: 1fr; padding-block: var(--space-lg); }
  .feature--reverse > .feature__media { order: 0; }
}

/* --------------------------------------------------------------------------
   13. SEGMENTATION (Monetize)
   -------------------------------------------------------------------------- */
.segments { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--space-md); }
@media (max-width: 1000px) { .segments { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px) { .segments { grid-template-columns: 1fr; } }

/* Quatre entrees, quatre filets. Sans trait au-dessus, elles flottaient dans
   le noir et l'on ne voyait plus ou commencait chacune : la couleur du filet
   etait declaree, sa largeur avait disparu. */
.segment {
  border-top: 1px solid rgba(0, 0, 0, 0.16);
  padding-top: 1.3rem;
}
.section--dark .segment { border-top-color: rgba(255, 255, 255, 0.2); }
.segment h3 { font-size: var(--fs-h3); margin-bottom: 1rem; }

.segment p { font-size: var(--fs-body); color: var(--muted); }
.section--dark .segment p { color: var(--gk-mid-gray); }

/* --------------------------------------------------------------------------
   14. PRICING
   -------------------------------------------------------------------------- */
.price-card {
  border: 1px solid rgba(255, 255, 255, 0.16);
  /* Marge egale sur les quatre cotes : le haut et le bas respiraient deux
     fois plus que les flancs, la carte paraissait creuse. */
  padding: var(--space-md);
  border-radius: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}
.section--light .price-card, .section--paper .price-card { border-color: rgba(0,0,0,0.15); }

.price {
  font-family: var(--font-serif);
  font-size: var(--fs-hero);
  line-height: 1;
  letter-spacing: -0.015em;
}
.price small {
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  letter-spacing: 0;
  color: var(--muted);
  margin-left: 0.35em;
}
.section--light .price small { color: var(--muted); }

.check-list { list-style: none; padding: 0; display: grid; gap: 0.9rem; }
.check-list li {
  display: grid;
  grid-template-columns: 1.4em 1fr;
  gap: 0.7em;
  align-items: start;
  font-size: var(--fs-body);
  color: var(--gk-mid-gray);
}
.section--light .check-list li, .section--paper .check-list li { color: var(--gk-charcoal); }
.check-list svg { width: 14px; height: 14px; margin-top: 0.3em; stroke: currentColor; fill: none; stroke-width: 1.5; }

/* Liste de lieux, séparateur médian */
.venues {
  list-style: none;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem 0;
  font-family: var(--font-sans-ext);
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--gk-mid-gray);
}
.section--light .venues { color: var(--muted); }
.venues li::after { content: " · "; margin: 0 0.7em; opacity: 0.5; }
.venues li:last-child::after { content: ""; }

/* --------------------------------------------------------------------------
   15. GALERIE / BIBLIOTHÈQUE (Studio)
   -------------------------------------------------------------------------- */
.marquee__track {
  display: flex;
  gap: var(--space-xs);
  width: max-content;
  animation: marquee var(--marquee-dur) linear infinite;
}
.marquee:hover .marquee__track { animation-play-state: paused; }
.marquee__track img {
  height: clamp(11.25rem, 24vw, 20rem);
  width: auto;
  object-fit: cover;
  filter: grayscale(1);
  opacity: 0.9;
}
@keyframes marquee {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}
.marquee--reverse .marquee__track { animation-direction: reverse; }

@media (prefers-reduced-motion: reduce) {
  .marquee__track { animation: none; }
  .marquee { overflow-x: auto; }
}

/* --------------------------------------------------------------------------
   16. VIDÉO
   -------------------------------------------------------------------------- */

/* Le paragraphe qui porte le bouton doit se recentrer comme le reste. */

.cta__meta {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-sm) var(--space-lg);
  margin-top: var(--space-lg);
}
.cta__meta a, .cta__meta span {
  display: inline-flex;
  align-items: center;
  gap: 0.6em;
  font-size: var(--fs-body);
  color: var(--gk-mid-gray);
  transition: color var(--dur-fast) var(--ease);
}
.cta__meta a:hover { color: var(--gk-white); }
.cta__meta img { width: 0.9375rem; height: 0.9375rem; opacity: 0.7; }

/* --------------------------------------------------------------------------
   18. FORMULAIRES
   -------------------------------------------------------------------------- */
.form { display: grid; gap: var(--space-sm); }
.form__row { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-sm); }
@media (max-width: 640px) { .form__row { grid-template-columns: 1fr; } }

.field { display: grid; gap: 0.5rem; }
.field label {
  font-family: var(--font-sans-ext);
  font-size: 0.625rem;
  text-transform: uppercase;
  letter-spacing: 0.16em;
  color: var(--muted);
}
.field input,
.field select,
.field textarea {
  width: 100%;
  background: transparent;
  border: 0;
  border-bottom: 1px solid rgba(255, 255, 255, 0.25);
  padding: 0.7rem 0;
  font-size: var(--fs-body);
  border-radius: 0;
  transition: border-color var(--dur-fast) var(--ease);
}
.field input:focus,
.field select:focus,
.field textarea:focus {
  outline: none;
  border-bottom-color: var(--gk-white);
}
.field textarea { resize: vertical; min-height: 7rem; }

/* ---- les domaines proposes apres l'arobase -------------------------------
   Meme fond, meme trait, meme survol que la liste des pays du telephone :
   le formulaire n'a qu'une facon de proposer quelque chose. La liste se
   pose sous le champ sans jamais pousser la page. */
.suggest {
  position: absolute;
  top: calc(100% + 1px);
  left: 0;
  right: 0;
  z-index: 20;
  margin: 0;
  padding: 0.35rem 0;
  list-style: none;
  background: var(--gk-near-black);
  max-height: 13rem;
  overflow-y: auto;
}
.suggest[hidden] { display: none; }
.section--light .suggest,
.section--paper .suggest,
body.gk-clair .suggest { background: var(--gk-white); }
.suggest__item {
  padding: 0.5rem 1rem;
  font-size: var(--fs-body);
  color: var(--gk-white);
  cursor: pointer;
}
.section--light .suggest__item,
.section--paper .suggest__item,
body.gk-clair .suggest__item { color: var(--gk-charcoal); }
.suggest__item:hover,
.suggest__item.est-actif { background: rgba(255, 255, 255, 0.07); }
.section--light .suggest__item:hover,
.section--light .suggest__item.est-actif,
body.gk-clair .suggest__item:hover,
body.gk-clair .suggest__item.est-actif { background: rgba(0, 0, 0, 0.05); }

/* ---- le jaune du remplissage automatique --------------------------------
   Safari et Chrome peignent un fond jaune paille sur les champs qu'ils
   remplissent. Ce fond est pose par la feuille du navigateur avec une
   priorite qu'aucune declaration ne reprend : background-color ne sert a
   rien ici. La parade connue est une ombre interieure assez large pour
   couvrir le champ entier, qui elle se laisse ecrire, plus
   -webkit-text-fill-color pour la couleur du texte, que color ne pilote
   plus dans cet etat.

   Une transition de plusieurs heures sur le fond fait le reste chez
   Chrome : la couleur part vers le jaune et n'y arrive jamais. */
.field input:-webkit-autofill,
.field input:-webkit-autofill:hover,
.field input:-webkit-autofill:focus,
.field input:-webkit-autofill:active,
.field textarea:-webkit-autofill,
.field select:-webkit-autofill {
  -webkit-box-shadow: 0 0 0 40rem var(--gk-black) inset;
  box-shadow: 0 0 0 40rem var(--gk-black) inset;
  -webkit-text-fill-color: var(--gk-white);
  caret-color: var(--gk-white);
  /* Safari sur iPhone ignore parfois l'ombre : on limite alors la peinture
     du fond aux seules lettres, que -webkit-text-fill-color recouvre
     aussitot. Le jaune n'a plus de surface ou s'etaler. Sans effet chez
     nous : le fond du champ est transparent, il n'y a rien a rogner. */
  -webkit-background-clip: text;
  background-clip: text;
  transition: background-color 100000s ease-in-out 0s,
              border-color var(--dur-fast) var(--ease);
}
.section--light .field input:-webkit-autofill,
.section--light .field input:-webkit-autofill:hover,
.section--light .field input:-webkit-autofill:focus,
.section--light .field textarea:-webkit-autofill,
.section--paper .field input:-webkit-autofill,
.section--paper .field textarea:-webkit-autofill,
body.gk-clair .field input:-webkit-autofill,
body.gk-clair .field textarea:-webkit-autofill {
  -webkit-box-shadow: 0 0 0 40rem var(--gk-white) inset;
  box-shadow: 0 0 0 40rem var(--gk-white) inset;
  -webkit-text-fill-color: var(--gk-charcoal);
  caret-color: var(--gk-charcoal);
  -webkit-background-clip: text;
  background-clip: text;
}

/* L'etoile des champs obligatoires. Discrete : elle marque, elle n'accuse
   pas. Elle est masquee aux lecteurs d'ecran, qui ont deja l'attribut
   required : l'entendre deux fois par champ n'aide personne. */
.oblig { color: var(--gk-erreur); opacity: 0.8; }

/* ---- le champ telephone : intl-tel-input, en noir et blanc --------------
   La bibliotheque se pilote par variables : c'est la voie qu'elle prevoit,
   et la seule qui tienne. Ses regles internes sont imbriquees, donc plus
   specifiques que les notres ; les surcharger a la main echouait sans rien
   dire. On repose donc ses propres variables, et on ne surcharge que ce
   qu'elles ne couvrent pas. Elles sont posees sur .iti et non sur :root :
   rien ne deborde sur le reste de la page. */
.iti {
  width: 100%;
  --iti-border-color: transparent;
  --iti-country-selector-bg: var(--gk-near-black);
  --iti-hover-color: rgba(255, 255, 255, 0.07);
  --iti-icon-color: var(--muted);
  --iti-spacer-horizontal: 1rem;
}
.section--light .iti,
body.gk-clair .iti {
  --iti-country-selector-bg: var(--gk-white);
  --iti-hover-color: rgba(0, 0, 0, 0.05);
}

/* Le bouton de pays se fond dans le champ : ni fond, ni cadre. Le filet du
   champ court dessous, comme pour tous les autres. */
.iti__selected-country,
.iti__selected-country-primary {
  background: none;
  border: 0;
  padding-inline: 0 0.5rem;
}

.iti--allow-dropdown input[type="tel"] { padding-left: 3.1rem; }

/* Le panneau. La bibliotheque le borde et lui met une ombre portee ; les
   deux jurent sur fond noir. La regle vient d'un selecteur imbrique, donc
   il faut au moins autant de specificite pour la reprendre. */
.iti--inline-country-selector .iti__country-selector {
  border: 0;
  border-radius: 0;
  box-shadow: none;
  color: var(--gk-white);
}
.section--light .iti--inline-country-selector .iti__country-selector,
body.gk-clair .iti--inline-country-selector .iti__country-selector {
  color: var(--gk-charcoal);
}

/* Un seul trait dans tout le panneau : celui qui separe la recherche de la
   liste. Il est discret, et c'est le seul. */
.iti .iti__search-input-wrapper { border-bottom: 1px solid rgba(255, 255, 255, 0.09); }
.section--light .iti .iti__search-input-wrapper { border-bottom-color: rgba(0, 0, 0, 0.09); }

/* La loupe est posee en absolu a gauche : le champ lui laisse la place.
   La regle de la bibliotheque etant imbriquee, on remonte la specificite
   plutot que d'esperer gagner a egalite. */
.iti .iti__search-input {
  background: none;
  color: inherit;
  border: 0;
  font-family: inherit;
  font-size: var(--fs-body);
  padding: 0.85rem 1rem 0.85rem 2.9rem;
}
.iti .iti__search-input::placeholder { color: var(--muted); }
.iti .iti__search-input:focus { outline: none; }

.iti__country-list { max-height: 16.5rem; }
.iti__country { padding: 0.5rem 1rem; font-size: var(--fs-body); }
.iti__country-name { color: inherit; }
.iti__dial-code { color: var(--muted); }

/* Le libelle flottant ne doit pas se poser sur le drapeau : le champ porte
   toujours un pays, il n'est jamais visuellement vide. */
.field:has(.iti) > label { top: 0 !important; font-size: 0.625rem !important; }

/* Le champ mal rempli se signale, mais seulement une fois qu'on y a touche :
   :user-invalid attend l'interaction, la ou :invalid aurait peint en rouge
   tous les champs obligatoires des l'ouverture de la page. Le trait et
   l'intitule passent ensemble : la couleur n'est jamais le seul indice, le
   message du navigateur dit ce qui manque. */
.field input:user-invalid,
.field select:user-invalid,
.field textarea:user-invalid,
.field input:user-invalid:focus,
.field textarea:user-invalid:focus { border-bottom-color: var(--gk-erreur); }
.field:has(:user-invalid) label { color: var(--gk-erreur); }
.field select option { background: var(--gk-black); }

/* Sur fond clair, le meme rouge serait trop pale. */
.section--light .field:has(:user-invalid) label,
.section--paper .field:has(:user-invalid) label { color: #B23B2E; }
.section--light .field input:user-invalid,
.section--light .field textarea:user-invalid,
.section--paper .field input:user-invalid,
.section--paper .field textarea:user-invalid { border-bottom-color: #B23B2E; }

.section--light .field input,
.section--light .field select,
.section--light .field textarea { border-bottom-color: rgba(0,0,0,0.25); }
.section--light .field input:focus,
.section--light .field select:focus,
.section--light .field textarea:focus { border-bottom-color: var(--gk-black); }

.checkbox {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.8rem;
  align-items: start;
  font-size: var(--fs-body);
  color: var(--muted);
  cursor: pointer;
}
/* La case a cocher, dessinee plutot que subie. La case native arrive
   arrondie, bleue au survol et grise une fois cochee : trois choses que la
   marque ne fait pas. On repart donc de zero avec appearance: none, et on
   redessine un carre a angles vifs, un filet d'un pixel, et une coche qui
   se trace au lieu d'apparaitre.

   Le carre garde la taille d'origine : c'est une cible que le doigt doit
   trouver, et la reduire pour faire elegant se paie a l'usage. */
.checkbox input[type="checkbox"] {
  appearance: none;
  -webkit-appearance: none;
  flex: none;
  width: 0.9375rem;
  height: 0.9375rem;
  margin: 0.3em 0 0;
  border: 1px solid rgba(255, 255, 255, 0.35);
  border-radius: 0;
  background: none;
  display: grid;
  place-content: center;
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease);
}
.checkbox input[type="checkbox"]:hover { border-color: var(--gk-white); }
.checkbox input[type="checkbox"]:checked {
  background: var(--gk-white);
  border-color: var(--gk-white);
}
/* La coche : deux traits, pas une police ni une image. Elle se revele en
   s'etirant, de la meme facon que les filets du site. */
.checkbox input[type="checkbox"]::before {
  content: "";
  width: 0.5rem;
  height: 0.28rem;
  border-left: 1.5px solid var(--gk-black);
  border-bottom: 1.5px solid var(--gk-black);
  transform: rotate(-45deg) translate(0.03rem, -0.04rem) scale(0);
  transform-origin: center;
  transition: transform 220ms var(--ease-reveal);
}
.checkbox input[type="checkbox"]:checked::before {
  transform: rotate(-45deg) translate(0.03rem, -0.04rem) scale(1);
}
.checkbox input[type="checkbox"]:focus-visible {
  outline: 1px solid var(--gk-white);
  outline-offset: 3px;
}

/* Sur fond clair, l'encre remplace le blanc. */
.section--light .checkbox input[type="checkbox"],
.section--paper .checkbox input[type="checkbox"],
body.gk-clair .checkbox input[type="checkbox"] { border-color: rgba(0, 0, 0, 0.35); }
.section--light .checkbox input[type="checkbox"]:hover,
.section--paper .checkbox input[type="checkbox"]:hover,
body.gk-clair .checkbox input[type="checkbox"]:hover { border-color: var(--gk-black); }
.section--light .checkbox input[type="checkbox"]:checked,
.section--paper .checkbox input[type="checkbox"]:checked,
body.gk-clair .checkbox input[type="checkbox"]:checked {
  background: var(--gk-black);
  border-color: var(--gk-black);
}
.section--light .checkbox input[type="checkbox"]::before,
.section--paper .checkbox input[type="checkbox"]::before,
body.gk-clair .checkbox input[type="checkbox"]::before {
  border-left-color: var(--gk-white);
  border-bottom-color: var(--gk-white);
}

/* Le bouton de pays se coloriait d'un carre gris au survol et a l'ouverture :
   un pave de fond au milieu d'un champ souligne d'un trait. La bibliotheque
   le peint avec var(--iti-hover-color) ; plutot que de lutter de specificite
   contre ses selecteurs imbriques, on rend la variable transparente sur ce
   seul bouton. La liste, elle, garde la sienne. Le survol se dit alors par
   le drapeau et le chevron qui s'eclaircissent. */
.iti .iti__selected-country { --iti-hover-color: transparent; }
.iti .iti__selected-country:hover,
.iti .iti__selected-country[aria-expanded="true"] { --iti-icon-color: var(--gk-white); }
.section--light .iti .iti__selected-country:hover,
.section--light .iti .iti__selected-country[aria-expanded="true"] {
  --iti-icon-color: var(--gk-black);
}

/* Newsletter en ligne */
.newsletter { display: flex; gap: var(--space-xs); align-items: flex-end; flex-wrap: wrap; }
.newsletter .field { flex: 1 1 15rem; }

/* --------------------------------------------------------------------------
   19. FOOTER
   -------------------------------------------------------------------------- */
.site-footer {
  background: var(--gk-black);
  border-top: 1px solid rgba(255, 255, 255, 0.12);
  padding-block: var(--space-xl) var(--space-md);
}


/* --------------------------------------------------------------------------
   20. ANIMATIONS AU SCROLL
   Ginko ne bondit pas. Ginko se révèle.
   -------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------
   L'ARRIVEE SUR LA PAGE

   Une navigation interne se faisait d'un coup sec : tout etait deja en cache,
   la page se peignait d'un seul bloc et aucune revelation n'avait le temps de
   se voir. Chaque page s'ouvre donc sur un fondu court, et le pied suit d'un
   cran pour que le regard parte du haut.

   L'opacite revient exactement a 1 : aucun contexte d'empilement ne subsiste
   ensuite, l'en-tete fixe et les menus deroulants gardent leur comportement.
   -------------------------------------------------------------------------- */
@keyframes gk-entree {
  from { opacity: 0; }
  to   { opacity: 1; }
}
/* Ni delai ni mode de remplissage : un delai impose de tenir l'element cache
   avant le depart, et un remplissage laisse la page blanche si l'animation ne
   demarre jamais. Les trois durees suffisent a decaler les arrivees. */
.site-header { animation: gk-entree 480ms var(--ease); }
main          { animation: gk-entree 640ms var(--ease); }
.site-footer  { animation: gk-entree 780ms var(--ease); }

@media (prefers-reduced-motion: reduce) {
  .site-header,
  main,
  .site-footer { animation: none; }
}

[data-reveal] {
  opacity: 0;
  transform: translateY(34px);
  transition: opacity var(--dur) var(--ease-reveal), transform var(--dur) var(--ease-reveal);
  transition-delay: var(--reveal-delay, 0ms);
  will-change: opacity, transform;
}
[data-reveal].is-visible {
  opacity: 1;
  transform: none;
}

/* Variante : révélation sans déplacement (grandes images) */
[data-reveal="fade"] { transform: none; }

/* Une image differee qui arrive apres son cadre claquait d'un coup dans la
   page. Elle nait en fondu. La classe est posee par le script uniquement sur
   les images encore en chargement : celles deja en cache ne clignotent pas,
   et les couches qui pilotent leur propre opacite ne sont jamais touchees. */
img.gk-nait { opacity: 0; }
/* La naissance passe par une animation, pas par une transition.
   Une transition ici posait "transition: opacity 700ms" sur l'image, ce qui
   remplacait la transition de son composant : les images de cas d'usage
   perdaient celle de leur transform, et leur zoom au survol claquait d'un
   coup au lieu de monter. Une animation ne se melange a rien.
   Le keyframe n'a pas de "to" : l'image rejoint sa propre opacite, 1 en
   general, 0.85 pour un .usecase, sans que la regle ait a la connaitre. */
img.gk-nee { animation: gk-naissance 700ms var(--ease-reveal); }
@keyframes gk-naissance { from { opacity: 0; } }

/* ---- le depart vers une autre page --------------------------------------
   L'arrivee se fait en fondu depuis un moment ; sans depart assorti, on
   passait d'une page pleine a un blanc sec. Le voile est pose sur le body :
   l'opacite du body lui-meme casserait les contextes d'empilement. */
html.gk-sort body { opacity: 0; transition: opacity 240ms ease; }

/* Parallaxe douce */
[data-parallax] { will-change: transform; }

@media (prefers-reduced-motion: reduce) {
  [data-reveal] { opacity: 1 !important; transform: none !important; transition: none; }
  [data-parallax] { transform: none !important; }
}

/* --------------------------------------------------------------------------
   21. UTILITAIRES
   -------------------------------------------------------------------------- */
.mt-xs { margin-top: var(--space-xs); }
.mt-sm { margin-top: var(--space-sm); }
.mt-md { margin-top: var(--space-md); }
.mt-lg { margin-top: var(--space-lg); }
.mt-xl { margin-top: var(--space-xl); }
.mt-2xl { margin-top: var(--space-2xl); }
.text-center { text-align: center; }
/* Dans une section centrée, toute boîte contrainte se recentre aussi. */
.text-center .lead,
.text-center .prose p,
.text-center p { margin-inline: auto; }
.text-center .check-list { justify-items: center; }
.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}


/* En-tête de section avec le sélecteur aligné à droite du titre */
/* Chapeau centre : l'oeil descend sur un axe unique. L'intertitre s'ecarte
   du titre, le chapo s'en rapproche : c'est le titre qui commande le bloc,
   pas l'espace regulier entre trois lignes. */
.section-head--centre {
  max-width: 46ch;
  margin-inline: auto;
  text-align: center;
}
.section-head--centre .eyebrow { margin-bottom: var(--space-sm); }
.section-head--centre h2 { margin: 0; }
.section-head--centre .lead { margin: 0.9rem auto 0; }

.section-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--space-sm) var(--space-md);
  flex-wrap: wrap;
  padding-bottom: var(--space-sm);
}


/* --------------------------------------------------------------------------
   LES FINITIONS, EN REGARD DU TEXTE
   Une photo d'ambiance occupait la moitie du chapitre alors que le sujet,
   ce sont les essences. Elles se lisent maintenant comme la planche d'un
   encadreur : un coin de baguette, un nom, une mention, un filet.
   -------------------------------------------------------------------------- */
.section--dark .finition { border-top-color: rgba(255, 255, 255, 0.14); }
.section--dark .finition:last-child { border-bottom-color: rgba(255, 255, 255, 0.14); }

.section--dark .finition__coin { mix-blend-mode: normal; }


@media (max-width: 560px) {
  .finition { grid-template-columns: 64px minmax(0, 1fr) auto; gap: var(--space-xs); }
  .finition__coin { width: 64px; height: 64px; }
}


/* --------------------------------------------------------------------------
   L'ATELIER
   Un plein ecran, tenu comme une petite application. Un seul cadre, dessine
   une fois : les chapitres n'echangent pas des images, ils allument une piece
   de ce meme cadre. Le survol choisit, le defilement avance tout seul. Tout
   passe par une cle posee sur le panneau, le reste est de la cascade : rien
   n'est reconstruit, l'objet ne cligne jamais.
   -------------------------------------------------------------------------- */
@media (max-width: 900px) {
  .atelier { height: auto; }
  .atelier__pane {
    position: static;
    height: auto;
    grid-template-columns: 1fr;
    gap: var(--space-xl);
    padding-block: var(--space-2xl);
  }
}

.atelier__tete { margin-bottom: clamp(2rem, 6vh, 4.5rem); }
.atelier__tete .eyebrow {
  color: rgba(255, 255, 255, 0.55);
  margin-bottom: clamp(1.4rem, 3vh, 2.2rem);
}
.atelier__tete h2 { margin: 0; max-width: 18ch; }

/* ---------- Le sommaire ---------- */
.atelier__menu { display: grid; }
.atelier__onglet {
  display: grid;
  align-items: center;
  width: 100%;
  /* Hauteur posee une fois pour toutes : le sommaire ne bouge jamais. Le
     contenu, lui, est centre, donc un titre sans sous-titre se recentre de
     lui-meme au lieu de rester colle en haut. */
  min-height: clamp(4.5rem, 11vh, 6.5rem);
  padding: 0;
  text-align: left;
  background: none;
  border: 0;
  color: inherit;
  cursor: pointer;
}
/* Les filets separent, ils n'encadrent pas : rien au-dessus du premier
   chapitre, rien sous le dernier. */
.atelier__onglet + .atelier__onglet { border-top: 1px solid rgba(255, 255, 255, 0.12); }

/* Numerotation entre crochets, comme le sommaire du pied de page. */
.atelier__num {
  grid-row: 1;
  align-self: center;
  font-family: var(--font-sans-ext);
  font-size: 0.5625rem;
  font-weight: 500;
  letter-spacing: 0;
  color: rgba(255, 255, 255, 0.35);
  transition: color var(--dur-fast) var(--ease);
}
.atelier__onglet.is-on .atelier__num { color: var(--gk-white); }

.atelier__titre {
  /* Place explicitement : sans cela il tombait en deuxieme rangee et le
     numero se retrouvait quarante pixels au-dessus de lui. */
  grid-column: 2;
  grid-row: 1;
  font-family: var(--font-serif);
  font-weight: 300;
  font-size: clamp(1rem, 1.4vw, 1.25rem);
  line-height: 1.25;
  letter-spacing: -0.005em;
  color: rgba(255, 255, 255, 0.55);
  transition: color var(--dur-fast) var(--ease);
}
.atelier__onglet.is-on .atelier__titre { color: var(--gk-white); }

/* Le sous-titre se replie a hauteur nulle sans disparaitre du flux : la
   rangee passe de 0fr a 1fr, ce qui s'anime, contrairement a une hauteur
   automatique. */
.atelier__ligne {
  display: grid;
  grid-template-columns: 2.6rem minmax(0, 1fr);
  column-gap: var(--space-xs);
  /* minmax(0, ...) et non 0fr seul : une rangee a 0fr garde quand meme la
     taille minimale de son contenu, ici le retrait du sous-titre. Elle
     mesurait donc 8,8 px et decalait le titre vers le haut. */
  grid-template-rows: auto minmax(0, 0fr);
  transition: grid-template-rows 420ms var(--ease);
}
.atelier__onglet.is-on .atelier__ligne { grid-template-rows: auto minmax(0, 1fr); }

.atelier__sous {
  grid-column: 2;
  min-height: 0;
  overflow: hidden;
  padding-top: 0.55rem;
  opacity: 0;
  font-family: var(--font-sans);
  font-size: 0.75rem;
  font-weight: 400;
  text-transform: none;
  letter-spacing: 0;
  line-height: 1.4;
  color: rgba(255, 255, 255, 0.45);
  transition: opacity 420ms var(--ease);
}
.atelier__onglet.is-on .atelier__sous { opacity: 1; }
.atelier__sous > span, .atelier__sous { display: block; }

/* ---------- Le cadre ---------- */
/* Le dessin se cale a gauche, sur le meme bord que le texte : deux axes dans
   la meme colonne se voyaient tout de suite. */
.croquis {
  /* Hauteur posee, jamais deduite : elle dependait de la longueur du texte
     du chapitre, et le cadre changeait donc de taille et de position d'un
     volet a l'autre. C'est exactement ce que l'animation doit eviter. */
  width: auto;
  height: clamp(16.25rem, 42vh, 28.75rem);
  max-width: 100%;
  color: var(--gk-white);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}
/* Un seul blanc, une seule epaisseur, et les traits toujours pleinement
   visibles. Ce n'est pas l'opacite qui designe le chapitre actif, c'est le
   remplissage : le trait dit la forme, la matiere dit le choix. */
.croquis .piece {
  opacity: 1;
  transition: opacity 620ms var(--ease);
}
.croquis .piece--acc,
.croquis .piece--fix,
.croquis .piece--verre { opacity: 0; }

/* La baguette et la marie-louise se remplissent au lieu de changer de trait :
   c'est une matiere qu'on designe, pas un contour. */
.croquis .plein {
  fill: currentColor;
  opacity: 0;
  transition: opacity 620ms var(--ease);
}
[data-atelier-corps][data-piece="passe"] .croquis .piece--passe .plein { opacity: 1; }

/* Quatre profils de baguette, pas quatre teintes : une fine, une large, une
   a gradins, une texturee. Ils ne se substituent pas, ils se deforment. Les
   proprietes de geometrie d'un rectangle SVG etant animables en CSS, il
   suffit de faire varier une epaisseur pour que le cadre se transforme. */
.croquis {
  overflow: visible;
  --ep: 19px;                  /* epaisseur relevee sur la photographie */
  --ext-x: 0px;   --ext-y: 110px;
  --ext-l: 1000px; --ext-h: 800px;
}
.croquis .f-plein, .croquis .f-hachure, .croquis .f-creux,
.croquis .f-trait, .croquis .f-gradin, .croquis .m-plein {
  transition: x 620ms var(--ease), y 620ms var(--ease),
              width 620ms var(--ease), height 620ms var(--ease),
              opacity 620ms var(--ease);
}

/* La matiere de la baguette, et l'evidement qui la creuse. */
.croquis .f-plein { fill: currentColor; opacity: 0; }
.croquis .f-hachure { fill: url(#gk-hachure); opacity: 0; }
.croquis .f-creux { fill: var(--gk-black); }
.croquis .f-creux,
.croquis .f-int,
.croquis .m-plein {
  x: calc(var(--ext-x) + var(--ep));
  y: calc(var(--ext-y) + var(--ep));
  width: calc(var(--ext-l) - 2 * var(--ep));
  height: calc(var(--ext-h) - 2 * var(--ep));
}
.croquis .f-gradin { opacity: 0; }

/* La marie-louise : sa matiere s'arrete au bord de l'image. */
.croquis .m-plein { fill: currentColor; opacity: 0; }
.croquis .m-creux { fill: var(--gk-black); }

/* Les quatre profils. */
[data-atelier-corps][data-bois="0"] .croquis { --ep: 8px; }
[data-atelier-corps][data-bois="1"] .croquis { --ep: 42px; }
[data-atelier-corps][data-bois="2"] .croquis { --ep: 34px; }
[data-atelier-corps][data-bois="3"] .croquis { --ep: 26px; }

/* Le gradin n'existe que sur le troisieme profil. */
[data-atelier-corps][data-bois="2"] .croquis .f-gradin {
  opacity: 1;
  x: calc(var(--ext-x) + var(--ep) / 2);
  y: calc(var(--ext-y) + var(--ep) / 2);
  width: calc(var(--ext-l) - var(--ep));
  height: calc(var(--ext-h) - var(--ep));
}

/* La matiere n'apparait que sur le chapitre de la baguette, et la texture
   seulement sur le quatrieme profil. */
[data-atelier-corps][data-piece="cadre"] .croquis .f-plein { opacity: 1; }
[data-atelier-corps][data-piece="cadre"][data-bois="3"] .croquis .f-hachure { opacity: 1; }
[data-atelier-corps][data-piece="passe"] .croquis .m-plein { opacity: 1; }

/* La baguette ne se remplit pas de blanc : elle prend ses essences, l'une
   apres l'autre, une seconde chacune. C'est la seule couleur du site, et
   elle appartient au produit. */
.croquis .essence { opacity: 0; transition: opacity 500ms var(--ease); }
[data-atelier-corps][data-bois="0"] .croquis .essence[data-bois="0"],
[data-atelier-corps][data-bois="1"] .croquis .essence[data-bois="1"],
[data-atelier-corps][data-bois="2"] .croquis .essence[data-bois="2"],
[data-atelier-corps][data-bois="3"] .croquis .essence[data-bois="3"] { opacity: 1; }

[data-atelier-corps][data-piece="cadre"] .croquis .piece--cadre,
[data-atelier-corps][data-piece="passe"] .croquis .piece--passe,
[data-atelier-corps][data-piece="verre"] .croquis .piece--verre,
[data-atelier-corps][data-piece="acc"] .croquis .piece--acc,
[data-atelier-corps][data-piece="fix"] .croquis .piece--fix { opacity: 1; }

.croquis [data-fix],
.croquis [data-acc] { opacity: 0; transition: opacity 460ms var(--ease); }
[data-atelier-corps][data-fix="0"] .croquis [data-fix="0"],
[data-atelier-corps][data-fix="1"] .croquis [data-fix="1"],
[data-atelier-corps][data-fix="2"] .croquis [data-fix="2"],
[data-atelier-corps][data-fix="3"] .croquis [data-fix="3"],
[data-atelier-corps][data-acc="0"] .croquis [data-acc="0"],
[data-atelier-corps][data-acc="1"] .croquis [data-acc="1"] { opacity: 1; }

/* ---------- Le texte ---------- */
/* La place du texte est reservee sur le volet le plus long : sans cela le
   cadre remontait ou descendait selon le chapitre. */
.atelier__texte[hidden] { display: none; }
.atelier__texte {
  max-width: 54ch;
  animation: atelierEntree 460ms var(--ease) both;
}
@keyframes atelierEntree {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: none; }
}

.atelier__texte p {
  font-size: var(--fs-body);
  line-height: 1.8;
  color: rgba(255, 255, 255, 0.7);
}
.atelier__texte p + p { margin-top: 0.9rem; }

/* La planche des essences ne concerne que la baguette. */

@media (prefers-reduced-motion: reduce) {
  .atelier { height: auto; }
  .atelier__pane { position: static; height: auto; }
  .croquis .piece, .croquis [data-fix], .croquis [data-acc] { transition: none; }
  .atelier__texte { animation: none; }
}


/* --------------------------------------------------------------------------
   L'ATELIER, L'OUVERTURE
   Le cadre entre seul, tient le centre de l'ecran, puis rejoint sa place a
   droite en retrecissant, et la section se decouvre autour de lui. Le point
   d'arrivee n'est pas une valeur en dur : c'est une case reservee dans la
   mise en page, que le script mesure. La choregraphie reste donc juste quelle
   que soit la taille de l'ecran.
   -------------------------------------------------------------------------- */
.ouverture {
  position: relative;
  height: 420vh;
  /* Le chapeau pose deja son retrait bas : on remonte la piste d'autant,
     sinon le cadre demarre trop loin du titre. */
  margin-top: calc(var(--space-xl) * -1);
  background: var(--gk-black);
  color: var(--gk-white);
}
.ouverture__pane {
  position: sticky;
  --entete: 5.5rem;
  top: var(--entete);
  height: calc(100vh - var(--entete));
  width: 100%;
  max-width: calc(var(--wrap) + 2 * var(--gutter));
  margin-inline: auto;
  padding: clamp(2rem, 5vh, 4rem) var(--gutter);
  display: grid;
  grid-template-columns: minmax(0, 5fr) minmax(0, 6fr);
  gap: clamp(3rem, 9vw, 9rem);
  align-items: center;
  overflow: hidden;
}

/* Sommaire et texte ne se decouvrent qu'une fois le cadre range. */
.ouverture__gauche, .ouverture__droite {
  display: grid;
  align-content: center;
  min-height: 0;
  opacity: var(--ouv-texte, 0);
  transform: translateY(calc((1 - var(--ouv-texte, 0)) * 16px));
}
.ouverture__droite { gap: clamp(1.5rem, 4vh, 3rem); }

/* La case reservee : elle ne se voit pas, elle tient la place du cadre. */
.ouverture__cible { height: clamp(12.5rem, 34vh, 23.75rem); }
.ouverture__dits { min-height: clamp(9rem, 22vh, 12rem); }

/* Le cadre est pose sur le panneau, pas dans une colonne : il part donc du
   centre de l'ecran avant de rejoindre sa case. */
.ouverture__cadre {
  position: absolute;
  top: 50%;
  left: 50%;
  transform:
    translate(-50%, -50%)
    translate(var(--ouv-x, 0px), var(--ouv-y, 0px))
    scale(var(--ouv-e, 1));
  transform-origin: center;
  will-change: transform;
  pointer-events: none;
}
.ouverture__cadre {
  position: absolute;
  /* La hauteur du dessin, nommee ici : la photographie se cale dessus. */
  --croquis-h: clamp(22.5rem, 74vh, 51.25rem);
}
/* Les deux cotes sont ecrits, aucun n'est laisse a "auto". Avec une largeur
   automatique, une boite trop etroite rabotait le SVG : lui gardait son
   rapport en se recentrant dans la boite reduite, la photographie non, et
   elle s'ecrasait d'autant. Au zoom arriere l'ecart devenait franc. */
.ouverture__cadre .croquis {
  display: block;
  height: var(--croquis-h);
  width: calc(var(--croquis-h) * 1000 / 1040);
  /* Le max-width general rabotait justement cette largeur : ici la boite
     doit tenir son rapport, quitte a deborder. Elle plafonne a 820px de
     haut, soit 788 de large : elle tient dans n'importe quel bureau. */
  max-width: none;
  color: var(--gk-white);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}

/* La photographie du vrai cadre occupe exactement la boite du dessin : le
   trait se trace donc par-dessus elle, au meme endroit, pendant qu'elle
   s'efface. C'est ce qui donne le passage de l'objet a son epure. */
.ouverture__photo {
  /* La photographie se cale sur la baguette du dessin, pas sur la boite du
     SVG : celle-ci reserve de la place au-dessus et au-dessous pour le
     crochet et le chevalet, et un simple "contain" decalait donc l'image
     d'une poignee de pixels. Les valeurs sont les proportions du viewBox :
     la baguette occupe 110 a 910 sur 1040 de haut, toute la largeur. */
  position: absolute;
  left: 0;
  top: 10.5769%;
  width: 100%;
  height: 76.9231%;
  object-fit: fill;
  opacity: var(--ouv-photo, 1);
  pointer-events: none;
}

/* Les traits se dessinent : pathLength normalise chaque trace, tous
   avancent donc du meme pas quelle que soit leur longueur reelle. */
.ouverture__cadre [pathLength] {
  stroke-dasharray: 1;
  /* Rien de trace tant que le script n'a pas parle. */
  stroke-dashoffset: calc(1 - var(--ouv-trace, 0));
}
.ouverture__cadre .piece--cadre .plein,
.ouverture__cadre .essence,
.ouverture__cadre .f-plein,
.ouverture__cadre .f-hachure,
.ouverture__cadre .m-plein { opacity: 0; }

@media (max-width: 900px) {
  .ouverture { height: auto; }
  .ouverture__pane {
    position: static;
    height: auto;
    grid-template-columns: 1fr;
    gap: var(--space-xl);
    padding-block: var(--space-2xl);
    overflow: visible;
  }
  .ouverture__gauche, .ouverture__droite { opacity: 1; transform: none; }
  .ouverture__cible { display: none; }
  /* Sur telephone, c'est la photographie qu'on montre : le dessin ne vaut
     que par ses etats, et il n'y a plus d'interaction pour les appeler. */
  .ouverture__cadre { position: static; transform: none; order: -1; }
  .ouverture__cadre .croquis { display: none; }
  .ouverture__photo {
    position: static;
    display: block;
    width: min(100%, 32.5rem);
    height: auto;
    margin-inline: auto;
    opacity: 1;
  }

  /* Chaque chapitre porte son texte : on lit une liste, plus un dispositif.
     Une seule colonne, un seul bord gauche : le numero, le titre, son
     intertitre et le texte s'alignent tous, au lieu de se decaler en marches
     d'escalier. */
  .ouverture__dits { min-height: 0; }
  .atelier__menu { gap: 0; }

  .atelier__onglet {
    min-height: 0;
    padding: var(--space-md) 0 0;
    cursor: default;
  }
  .atelier__onglet + .atelier__onglet { border-top: 0; }

  .atelier__ligne {
    grid-template-columns: 1fr;
    grid-template-rows: auto auto auto;
    column-gap: 0;
  }
  .atelier__num {
    grid-row: 1;
    grid-column: 1;
    margin-bottom: 0.9rem;
  }
  /* Le titre reprend le dessus : il etait plus petit et plus gris que le
     texte qu'il annonce. */
  .atelier__titre {
    grid-row: 2;
    grid-column: 1;
    font-size: 1.5rem;
    color: var(--gk-white);
  }
  .atelier__sous {
    grid-row: 3;
    grid-column: 1;
    opacity: 1;
    padding-top: 0.4rem;
  }

  .atelier__menu [data-texte] {
    padding: var(--space-sm) 0 var(--space-lg);
    max-width: none;
    animation: none;
    border-bottom: 1px solid rgba(255, 255, 255, 0.12);
  }
  .atelier__menu [data-texte]:last-child { border-bottom: 0; }
  .atelier__menu [data-texte] p { font-size: var(--fs-body); line-height: 1.8; }
}

@media (prefers-reduced-motion: reduce) {
  .ouverture { height: auto; }
  .ouverture__pane { position: static; height: auto; overflow: visible; }
  .ouverture__gauche, .ouverture__droite { opacity: 1; transform: none; }
  .ouverture__cible { display: none; }
  .ouverture__cadre { position: static; transform: none; }
  .ouverture__photo { display: none; }
  .ouverture__cadre [pathLength] { stroke-dasharray: none; }
}


/* La note commerciale ferme le passage a l'acte : le prix et le delai se
   disent la, pas au detour d'une etape. */
.steps__note {
  max-width: 62ch;
  margin: var(--space-lg) auto 0;
  text-align: center;
}

/* --------------------------------------------------------------------------
   LE DERNIER MOT
   L'invitation, le bouton, puis la phrase de cloture sous un filet. Elle est
   la derniere chose qu'on lit : elle n'a pas a se battre avec le bouton.
   -------------------------------------------------------------------------- */
.devis {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  padding-block: clamp(6rem, 18vh, 11rem);
  color: var(--gk-white);
  background: var(--gk-black);
}
.devis__fond { position: absolute; inset: 0; z-index: -2; }
.devis__fond img { width: 100%; height: 100%; object-fit: cover; object-position: center 45%; }
.devis::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(180deg,
    rgba(0,0,0,0.9) 0%, rgba(0,0,0,0.62) 40%,
    rgba(0,0,0,0.62) 60%, rgba(0,0,0,0.92) 100%);
}
.devis__inner {
  position: relative;
  max-width: 46ch;
  text-align: center;
  /* Trois elements, deux ecarts identiques : l'intertitre, le titre et le
     bouton se suivent au meme pas, et le bloc se centre d'un seul tenant. */
  display: grid;
  justify-items: center;
  gap: clamp(1.6rem, 4vh, 2.6rem);
}
.devis .eyebrow { color: rgba(255, 255, 255, 0.6); }
.devis__inner .eyebrow { margin: 0; }
.devis__inner h2 { margin: 0 auto; }
.devis__cta { margin: 0; }


/* --------------------------------------------------------------------------
   23b. CHOISIR SON FORMAT
   Les deux formats sont montres cote a cote, a la meme echelle : c'est la
   seule facon de voir que le Monumental est plus grand. Deux pages separees
   demandaient de s'en souvenir d'un onglet a l'autre.
   -------------------------------------------------------------------------- */


/* La livraison : moitie image, moitie texte. L'image tient toute la hauteur
   de la section, elle n'est pas une illustration posee au-dessus d'un
   paragraphe mais l'autre moitie du propos. */
.livraison {
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: stretch;
  min-height: min(84vh, 48.75rem);
  background: var(--gk-paper);
  /* La section ne porte pas de classe --paper : sans ces jetons elle heritait
     du texte blanc et du gris clair du theme sombre, illisibles ici. */
  color: var(--gk-charcoal);
  --muted: #68685f;
  --body-ink: var(--gk-charcoal);
}
.livraison h2 { color: var(--gk-black); }
.livraison .eyebrow, .livraison .lead { color: var(--muted); }
.livraison__media { margin: 0; overflow: hidden; }
.livraison__media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.livraison__dit {
  display: flex;
  align-items: center;
  padding: var(--space-xl) var(--gutter);
}
.livraison__dit > div { max-width: 40ch; }
.livraison__dit .eyebrow { margin-bottom: var(--space-sm); }
.livraison__dit .lead { margin-top: var(--space-sm); }

@media (max-width: 860px) {
  .livraison { grid-template-columns: 1fr; min-height: 0; }
  .livraison__media img { height: auto; aspect-ratio: 4 / 3; }
  .livraison__dit { padding-block: var(--space-lg); }
}

.formats {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-lg) var(--space-xl);
  align-items: start;
}
@media (max-width: 760px) { .formats { grid-template-columns: 1fr; } }

.format { margin: 0; scroll-margin-top: 6rem; }
.format__media { margin: 0 0 var(--space-md); }
.format__media img {
  display: block;
  width: 100%;
  height: auto;
  /* Les rendus sont detoures sur blanc : la section l'est aussi, le fond de
     l'image se confond donc avec celui de la page. Un mode de fusion aurait
     suffi, mais le contexte d'empilement cree par les animations de
     revelation l'annule. */
}
.format__eye { margin-bottom: 0.5rem; }
.format__name {
  font-family: var(--font-serif);
  font-size: var(--fs-h3);
  line-height: 1.15;
  letter-spacing: -0.015em;
  margin: 0;
}

.format__specs {
  display: grid;
  gap: 0;
  margin: var(--space-sm) 0 0;
}
/* Seuls les filets interieurs separent : la table s'ouvre et se ferme
   sur le fond, comme partout ailleurs. */
.format__specs > div:last-child { border-bottom: 0; }
.format__specs > div {
  display: flex;
  justify-content: space-between;
  /* Un libelle en capitales n'a pas de jambages : centre sur sa boite,
     il parait toujours trop haut. Les deux textes partagent donc leur
     ligne de base, le seul alignement qui se lit comme aligne. */
  align-items: baseline;
  gap: var(--space-xs);
  padding: 0.65rem 0;
  border-bottom: 1px solid rgba(0, 0, 0, 0.12);
}
.format__specs dt {
  font-family: var(--font-sans-ext);
  font-size: var(--fs-eyebrow);
  text-transform: uppercase;
  letter-spacing: 0.18em;
  color: var(--muted);
}
.format__specs dd { margin: 0; font-size: var(--fs-body); }


.format__for {
  margin-top: var(--space-sm);
  font-size: var(--fs-body);
  line-height: 1.7;
  color: var(--muted);
}

/* La phrase qui aide a trancher, quand la comparaison n'a pas suffi */
.formats__aide {
  max-width: 46ch;
  margin: var(--space-xl) auto 0;
  text-align: center;
}
.formats__aide p { font-size: var(--fs-lead); color: var(--body-ink); }

/* --------------------------------------------------------------------------
   23. FICHES PRODUIT
   -------------------------------------------------------------------------- */
.model-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  border: 1px solid rgba(255, 255, 255, 0.16);
  border-radius: 0;
  overflow: hidden;
  background: var(--gk-near-black);
}
.section--light .model-card, .section--paper .model-card {
  border-color: rgba(0, 0, 0, 0.14);
  background: var(--gk-off-white);
}
.model-card__media { aspect-ratio: 4 / 3; overflow: hidden; }
.model-card__media img { width: 100%; height: 100%; object-fit: cover; }
.model-card__body { padding: 0 var(--space-md) var(--space-md); display: grid; gap: var(--space-sm); }

/* Séparateur "ou" entre achat et leasing */
.or-rule {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  color: var(--muted);
  font-family: var(--font-sans-ext);
  font-size: var(--fs-eyebrow);
  text-transform: uppercase;
  letter-spacing: 0.2em;
}
.or-rule::before, .or-rule::after {
  content: "";
  flex: 1;
  height: 1px;
  background: rgba(255, 255, 255, 0.16);
}
.section--light .or-rule::before, .section--light .or-rule::after,
.section--paper .or-rule::before, .section--paper .or-rule::after {
  background: rgba(0, 0, 0, 0.14);
}

/* Plans Studio */
.plans { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-sm); }
/* Trois licences : jamais 2 + 1, l'orpheline sous les deux autres a l'air
   d'une erreur de grille. On passe directement en pile. */
@media (max-width: 1000px) { .plans { grid-template-columns: 1fr; max-width: 34rem; margin-inline: auto; } }

.plan {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  padding: var(--space-md) var(--space-sm);
  border: 1px solid rgba(255, 255, 255, 0.16);
  border-radius: 0;
}
.section--light .plan, .section--paper .plan { border-color: rgba(0,0,0,0.14); }
/* Une mention en capitales : c'est le registre des intertitres, donc
   l'extended. Une regle groupee plus bas la ramenait a la fonte de texte. */
.plan__badge {
  font-family: var(--font-sans-ext);
  font-weight: 400;
  font-size: var(--fs-eyebrow);
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--muted);
  line-height: 1.5;
}
/* Trois registres, trois roles. La mention en capitales dit a qui la formule
   s'adresse, le nom l'identifie, le montant porte le regard. Nom et montant
   partageaient la meme serif au meme corps : on lisait deux titres au lieu
   d'un nom et d'un prix. */
/* Le nom n'est pas un label : fonte de texte, bas de casse. L'extended en
   capitales est deja pris par la mention juste au-dessus. */
.plan__nom {
  font-family: var(--font-sans);
  font-size: 1.0625rem;
  font-weight: 500;
  line-height: 1.2;
  letter-spacing: 0.02em;
  margin-top: 0.5rem;
}
.plan__price {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0 0.5rem;
  margin-top: 0.2rem;
}
.plan__montant {
  font-family: var(--font-serif);
  font-size: clamp(2.25rem, 3.4vw, 3rem);
  line-height: 1;
  letter-spacing: -0.02em;
}
.plan__unite {
  font-family: var(--font-sans);
  font-size: 0.8125rem;
  line-height: 1.4;
  color: var(--muted);
}
.plan--featured .plan__unite { color: inherit; opacity: 0.6; }
.plan .check-list li { font-size: var(--fs-body); }

/* Étapes numérotées horizontales (Player) */
.flow { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-md); }
@media (max-width: 780px) { .flow { grid-template-columns: 1fr; } }

/* Bandeau de statut sobre */
.notice {
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 0;
  padding: var(--space-md);
}
.section--light .notice, .section--paper .notice { border-color: rgba(0,0,0,0.14); }

/* Fil d'Ariane */
/* --------------------------------------------------------------------------
   FIL D'ARIANE
   Place en fin de page, juste avant le pied : il sert au retour en arriere,
   pas a l'entree. En haut du hero il coupait la lecture du titre.
   -------------------------------------------------------------------------- */
.crumbbar {
  background: var(--gk-black);
  border-top: 1px solid rgba(255, 255, 255, 0.1);
  padding-block: var(--space-md);
}
.crumbbar ol {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.55em;
  font-family: var(--font-sans-ext);
  font-size: var(--fs-eyebrow);
  text-transform: uppercase;
  letter-spacing: 0.2em;
  line-height: 1;
  color: var(--muted);
}
.crumbbar a { color: var(--muted); transition: color var(--dur-fast) var(--ease); }
.crumbbar a:hover { color: var(--gk-white); }
.crumbbar [aria-current="page"] { color: var(--gk-mid-gray); }
.crumbbar li::after { content: "/"; margin-left: 0.55em; opacity: 0.4; }
.crumbbar li:last-child::after { content: ""; margin: 0; }

/* --------------------------------------------------------------------------
   24. PIED DE PAGE, 4 COLONNES
   Remplace la grille en 3 parties de la première version.
   -------------------------------------------------------------------------- */


/* --------------------------------------------------------------------------
   25. MENU PRINCIPAL ET PANNEAU DEROULANT
   Le lien de tete reste un vrai lien : les moteurs le suivent, le clavier
   l'atteint. Le panneau n'est qu'un confort, jamais un passage oblige.
   -------------------------------------------------------------------------- */
.site-header__inner {
  grid-template-columns: auto 1fr auto;
  gap: var(--space-md);
}

.nav {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  justify-self: center;
}

/* Volontairement pas de position ici : le panneau doit se caler sur toute
   la largeur du header, pas sur l'entree de menu. */
.nav__item { position: static; }
.nav__item--has-menu > .nav__link::before {
  content: "";
  position: absolute;
  left: -0.9rem;
  right: -0.9rem;
  top: 100%;
  /* Doit couvrir le centrage vertical de la barre plus son padding bas :
     c'est exactement la bande que la souris traverse pour atteindre le
     panneau. Le panneau, absolu et plus loin dans le DOM, passe au-dessus,
     ce pont ne bloque donc aucun clic. */
  height: 3rem;
}

.nav__link {
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  font-family: var(--font-sans-ext);
  font-size: var(--fs-eyebrow);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--gk-white);
  opacity: 0.72;
  padding-block: 0.6rem;
  transition: opacity var(--dur-fast) var(--ease);
}
.nav__link:hover,
.nav__link[aria-current="page"],
.nav__item--has-menu.is-open .nav__link,
.nav__item--has-menu:focus-within .nav__link { opacity: 1; }

/* Pas de trait sous les entrees de navigation : l'opacite pleine suffit a
   marquer le survol, et le chevron dit le reste. */
.nav__link { position: relative; }

.nav__chev {
  width: 9px;
  height: 6px;
  transition: transform var(--dur-fast) var(--ease);
}
.nav__item--has-menu.is-open .nav__chev,
.nav__item--has-menu:focus-within .nav__chev { transform: rotate(180deg); }

.nav__end {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  justify-self: end;
}

.nav__social {
  display: inline-flex;
  opacity: 0.6;
  transition: opacity var(--dur-fast) var(--ease);
}
.nav__social:hover { opacity: 1; }
.nav__social svg { width: 0.9375rem; height: 0.9375rem; fill: currentColor; }

/* ---------- Le panneau ---------- */
.mega {
  position: absolute;
  left: 0;
  right: 0;
  top: 100%;
  /* Le panneau doit se detacher du fond noir de la page : fond legerement
     releve, filet net en haut et en bas, et ombre portee. */
  background: var(--gk-black);
  border-top: 1px solid rgba(255, 255, 255, 0.14);
  border-bottom: 1px solid rgba(255, 255, 255, 0.14);
  /* Le panneau se deroule du haut vers le bas, il n'apparait pas en fondu :
     c'est une hauteur qui s'ouvre, et le contenu se decouvre au fur et a
     mesure. Aucune transformation ici, sinon le voile en position fixe
     serait rogne par l'overflow. */
  max-height: 0;
  overflow: hidden;
  visibility: hidden;
  transition: max-height 720ms cubic-bezier(0.16, 0.84, 0.24, 1), visibility 720ms;
}
/* Bascule d'une rubrique a l'autre : le cadre ne bouge pas, il n'a pas a
   coulisser une seconde fois. Le sortant s'efface vite, l'entrant apparait
   sans glissement : c'est le contenu qui change, pas le panneau. */
.site-header.nav-swap .mega { transition: none; }
.site-header.nav-swap .mega::after { transition: none; }

/* Le contenu se renouvelle en cascade, jamais d'un bloc. */
@keyframes megaEntree {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: none; }
}
.nav__item--has-menu.is-open > .mega .mega__lead {
  animation: megaEntree 620ms var(--ease) 120ms both;
}
/* Les cartes n'arrivent plus en glissant : elles se posent. Un fondu seul,
   et l'image qui se dezoome pendant ce fondu, un peu plus lentement pour
   qu'elle finisse de s'installer apres que le texte est lisible. */
.nav__item--has-menu.is-open > .mega .mega__card {
  animation: megaFondu 700ms var(--ease) calc(150ms + var(--i, 0) * 90ms) backwards;
}
.nav__item--has-menu.is-open > .mega .mega__zoom {
  animation: megaDezoom 1100ms var(--ease-reveal) calc(150ms + var(--i, 0) * 90ms) backwards;
}
@keyframes megaFondu {
  from { opacity: 0; }
}
@keyframes megaDezoom {
  from { transform: scale(1.07); }
}
/* La vignette n'a plus d'animation d'arrivee a elle.

   Elle en avait une, qui tenait tour a tour l'echelle puis la lumiere. Or ce
   sont exactement les deux proprietes que le survol doit changer, et une
   animation l'emporte toujours sur une regle ordinaire : survoler une carte
   pendant son arrivee ne faisait rien, puis l'animation rendait la main d'un
   coup a la fin, sans transition. D'ou un premier survol qui sautait apres
   chaque ouverture du menu. L'image arrive maintenant portee par le fondu de
   sa carte, qui l'englobe : rien ne lui dispute plus ni l'echelle ni la
   lumiere, et le survol repond du premier coup. */
.nav__item--has-menu.is-open > .mega .mega__links li {
  animation: megaEntree 580ms var(--ease) calc(280ms + var(--i, 0) * 55ms) both;
}
.nav__item--has-menu.is-open > .mega .mega__outro {
  animation: megaEntree 580ms var(--ease) 380ms both;
}

/* L'arrivee des cartes est la meme a chaque fois : ouverture depuis zero
   comme passage d'une rubrique a l'autre. Le fondu et le dezoom sont assez
   doux pour se rejouer sans lasser, la ou la cascade glissee, elle, donnait
   l'impression d'un contenu qui se remonte sans arret. */

@media (prefers-reduced-motion: reduce) {
  .nav__item--has-menu.is-open > .mega * { animation: none !important; }
}

/* La hauteur d'ouverture est posee par le script, a la hauteur reelle du
   contenu : le plafond ci-dessous ne sert qu'au clavier, quand le script
   n'intervient pas. */
.nav__item--has-menu:focus-within > .mega,
.nav__item--has-menu.is-open > .mega {
  max-height: 78vh;
  visibility: visible;
}

/* Le panneau reste noir. C'est la page derriere qui recule : elle est
   floutee et assombrie, ce qui detache le panneau sans l'eclaircir. */
.mega::after {
  content: "";
  position: fixed;
  inset: 0;
  top: 100%;
  z-index: -1;
  background: rgba(0, 0, 0, 0.72);
  backdrop-filter: blur(6px) saturate(0.8);
  -webkit-backdrop-filter: blur(6px) saturate(0.8);
  pointer-events: none;
  opacity: 0;
  transition: opacity 620ms var(--ease);
}
.nav__item--has-menu.is-open > .mega::after,
.nav__item--has-menu:focus-within > .mega::after { opacity: 1; }

/* En haut de page, l'en-tete doit devenir opaque quand un panneau s'ouvre,
   sinon le contenu de la page transparait derriere la barre. */
.site-header:has(.nav__item--has-menu:focus-within),
.site-header:has(.nav__item--has-menu.is-open) {
  background: var(--gk-black);
  border-bottom-color: rgba(255, 255, 255, 0.14);
}

.mega__inner {
  display: grid;
  grid-template-columns: minmax(0, 4fr) minmax(0, 8fr);
  gap: var(--space-xl);
  padding-block: var(--space-lg);
}
.mega__lead { max-width: 30ch; }
.mega__intro {
  font-size: var(--fs-body);
  line-height: 1.7;
  color: var(--gk-mid-gray);
  margin-block: 0 var(--space-sm);
}

/* Grille de 6 colonnes : la repartition suit le nombre d'entrees, pour ne
   jamais laisser une case vide en bout de ligne. */
.mega__grid {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 1px;
  background: rgba(255, 255, 255, 0.1);
  border: 1px solid rgba(255, 255, 255, 0.1);
}

/* 2 entrees : moitie et moitie */
.mega__grid[data-count="1"] > .mega__card { grid-column: span 6; }
.mega__grid[data-count="2"] > .mega__card { grid-column: span 3; }

/* 3 entrees : trois tiers */
.mega__grid[data-count="3"] > .mega__card { grid-column: span 2; }

/* 4 entrees : deux et deux */
.mega__grid[data-count="4"] > .mega__card { grid-column: span 3; }

/* 5 entrees : les deux modeles en avant sur la premiere ligne,
   les trois pages de reference en tiers sur la seconde. */
.mega__grid[data-count="5"] > .mega__card { grid-column: span 2; }
.mega__grid[data-count="5"] > .mega__card:nth-child(1),
.mega__grid[data-count="5"] > .mega__card:nth-child(2) { grid-column: span 3; }

@media (max-width: 1100px) {
  .mega__grid > .mega__card,
  .mega__grid[data-count="2"] > .mega__card,
  .mega__grid[data-count="3"] > .mega__card,
  .mega__grid[data-count="4"] > .mega__card,
  .mega__grid[data-count="5"] > .mega__card,
  .mega__grid[data-count="5"] > .mega__card:nth-child(1),
  .mega__grid[data-count="5"] > .mega__card:nth-child(2) { grid-column: span 3; }
}

.mega__card {
  position: relative;
  display: block;
  background: var(--gk-black);
  transition: background var(--dur-fast) var(--ease);
}
.mega__card:hover { background: var(--gk-near-black); }

/* La vignette : le produit est visuel, le menu doit l'etre aussi. */
.mega__thumb {
  display: block;
  aspect-ratio: 16 / 10;
  overflow: hidden;
  background: var(--gk-near-black);
}
/* L'enveloppe du zoom d'arrivee. Elle existe pour porter une transformation
   qui n'appartienne qu'a l'ouverture : sur l'image elle-meme, elle se
   disputerait la transformation avec le zoom du survol, et le premier survol
   apres chaque ouverture sauterait. Deux elements, deux transformations,
   aucun arbitrage a rendre. */
.mega__zoom { display: block; width: 100%; height: 100%; }

.mega__thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0.62;
  transform: scale(1.02);
  /* Sept cents millisecondes, pas mille deux cents : on survole une carte de
     menu une demi-seconde, et un mouvement plus long ne se montrait jamais
     qu'a moitie avant qu'on soit passe ailleurs. La lumiere monte au meme
     rythme que l'echelle, pour que ce soit un seul geste. */
  transition: opacity 700ms var(--ease), transform 700ms var(--ease);
}
.mega__card:hover .mega__thumb img { opacity: 1; transform: scale(1.065); }

/* Vignette claire : pour les rendus produit detoures sur blanc. L'image est
   contenue, jamais rognee, sinon la baguette sortirait du cadrage 16/10. */
.mega__thumb--light { background: var(--gk-white); }
/* Pleine opacite au repos : sur fond blanc, un voile ne fait pas reculer
   l'image, il la delave et donne l'air d'un chargement inacheve. */
.mega__thumb--light img { object-fit: contain; opacity: 1; transform: scale(1); }
.mega__card:hover .mega__thumb--light img { opacity: 1; transform: scale(1.045); }

.mega__body { padding: var(--space-sm); display: grid; gap: 0.55rem; }

.mega__name {
  font-family: var(--font-serif);
  font-size: 1.25rem;
  line-height: 1.12;
  letter-spacing: -0.015em;
  text-transform: none;
  color: var(--gk-white);
}
.mega__desc {
  font-family: var(--font-sans);
  font-size: 0.8125rem;
  line-height: 1.55;
  letter-spacing: 0;
  text-transform: none;
  color: var(--gk-mid-gray);
  max-width: 30ch;
}
.mega__arrow {
  position: absolute;
  top: 0.9rem;
  right: 1rem;
  color: var(--gk-white);
  opacity: 0;
  transform: translateX(-4px);
  transition: opacity var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
.mega__card:hover .mega__arrow { opacity: 1; transform: translateX(0); }

@media (max-width: 1000px) {
  .nav, .nav__end { display: none; }
  .site-header__inner { grid-template-columns: auto 1fr; }
}

/* ---------- Menu mobile ---------- */
.mobile-nav__inner {
  display: flex;
  flex-direction: column;
  width: 100%;
  height: 100%;
  min-height: 0;
}

.mnav {
  list-style: none;
  margin: 0;
  padding: 0;
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}
.mnav__item + .mnav__item { border-top: 1px solid rgba(255, 255, 255, 0.12); }

.mnav__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-xs);
}
.mnav__link {
  display: block;
  flex: 1 1 auto;
  padding: 1.45rem 0;
  font-family: var(--font-serif);
  font-size: 1.5rem;
  line-height: 1.15;
  letter-spacing: -0.015em;
  color: var(--gk-white);
}

/* Le chevron a sa propre cible tactile : ouvrir la rubrique et aller sur sa
   page sont deux intentions differentes, elles meritent deux cibles. */
.mnav__toggle {
  flex: none;
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  color: rgba(255, 255, 255, 0.6);
  background: none;
  border: 0;
}
.mnav__toggle svg {
  width: 12px;
  height: 8px;
  transition: transform var(--dur-fast) var(--ease);
}
.mnav__toggle[aria-expanded="true"] { color: var(--gk-white); }
.mnav__toggle[aria-expanded="true"] svg { transform: rotate(180deg); }

/* Le depliage se joue, il ne saute pas. La hauteur est posee en pixels par
   le script, la transition la conduit ; les liens suivent en decale, du haut
   vers le bas, comme une liste qui se decouvre. Sans JS, l'attribut hidden
   garde le panneau replie. */
.mnav__panel {
  display: grid;
  gap: 0.85rem;
  padding: 0 0 0;
  height: 0;
  overflow: hidden;
  visibility: hidden;
  /* La visibilite bascule aux deux bouts, pas au milieu : visible des le
     premier pixel a l'ouverture, masquee seulement une fois repliee. */
  transition: height 560ms var(--ease),
              padding 560ms var(--ease),
              visibility 0s linear 560ms;
}
.mnav__panel[hidden] { display: none; }
.mnav__panel.is-open {
  visibility: visible;
  padding: 0.2rem 0 1.35rem;
  transition: height 560ms var(--ease),
              padding 560ms var(--ease),
              visibility 0s linear 0s;
}
.mnav__panel a {
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  line-height: 1.3;
  color: rgba(255, 255, 255, 0.62);
  opacity: 0;
  transform: translateY(7px);
  transition: opacity 420ms var(--ease), transform 420ms var(--ease);
}
.mnav__panel.is-open a { opacity: 1; transform: none; }
.mnav__panel.is-open a:nth-child(1) { transition-delay: 110ms; }
.mnav__panel.is-open a:nth-child(2) { transition-delay: 165ms; }
.mnav__panel.is-open a:nth-child(3) { transition-delay: 220ms; }
.mnav__panel.is-open a:nth-child(4) { transition-delay: 275ms; }
.mnav__panel.is-open a:nth-child(5) { transition-delay: 330ms; }
.mnav__panel.is-open a:nth-child(n+6) { transition-delay: 385ms; }

@media (prefers-reduced-motion: reduce) {
  .mnav__panel,
  .mnav__panel a { transition: none; }
  .mnav__panel a { transition-delay: 0s; }
}

/* Le pied reste en bas, hors du defilement de la liste */
.mnav__foot {
  flex: none;
  padding-top: var(--space-sm);
  border-top: 1px solid rgba(255, 255, 255, 0.12);
}
.mnav__cta { display: block; width: 100%; text-align: center; }
.mnav__prefs {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-xs);
  margin-top: var(--space-sm);
}
.mnav__lang {
  font-family: var(--font-sans-ext);
  font-size: var(--fs-eyebrow);
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: rgba(255, 255, 255, 0.62);
}
/* Dans le pied du menu, la devise est une preference discrete, pas un choix
   a faire : deux petits libelles, l'actif souligne. Les encadres blancs du
   reste du site y prenaient toute la place. */
.mnav__currency { display: flex; gap: 1rem; }
.mnav__currency .prefs__opt {
  padding: 0;
  border: 0;
  background: none;
  font-family: var(--font-sans-ext);
  font-size: var(--fs-eyebrow);
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: rgba(255, 255, 255, 0.45);
}
.mnav__currency .prefs__opt:hover { background: none; color: var(--gk-white); }
.mnav__currency .prefs__opt.is-on,
.mnav__currency .prefs__opt[aria-pressed="true"] {
  color: var(--gk-white);
  background: none;
  border: 0;
  text-decoration: underline;
  text-underline-offset: 0.35em;
  text-decoration-thickness: 1px;
}

/* --------------------------------------------------------------------------
   26. TABLEAU COMPARATIF
   Il doit rester lisible sur mobile : il defile dans son propre conteneur,
   la page ne part jamais en scroll horizontal.
   -------------------------------------------------------------------------- */
.table-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}
/* Dans un conteneur a defilement, une ligne qui nait en remontant deborde
   et fait apparaitre un ascenseur le temps de l'animation : ici, les
   revelations se font en fondu pur, comme les grilles de specs. */
.table-scroll [data-reveal] {
  transform: none !important;
  transition-duration: 0.9s;
}

.cmp {
  width: 100%;
  border-collapse: collapse;
  text-align: left;
}
/* Le tableau tient toujours dans la page : les cellules se plient plutot
   que de pousser un ascenseur. La reserve de largeur ne vaut que sur les
   petits ecrans, ou cinq colonnes ne peuvent pas tenir lisiblement. */
@media (max-width: 860px) {
  .cmp { min-width: 640px; }
}
.cmp th, .cmp td {
  padding: 1.1rem 1.2rem;
  border-bottom: 1px solid rgba(0, 0, 0, 0.12);
  vertical-align: top;
  font-size: var(--fs-body);
  line-height: 1.5;
}
/* La colonne des criteres s'aligne sur le fer de la page, sans retrait ;
   et le tableau se ferme sur le fond, sans filet sous la derniere ligne. */
.cmp th:first-child,
.cmp td:first-child { padding-left: 0; }
.cmp tbody tr:last-child th,
.cmp tbody tr:last-child td { border-bottom: 0; }
.section--dark .cmp th,
.section--dark .cmp td { border-bottom-color: rgba(255, 255, 255, 0.14); }

.cmp thead th {
  font-family: var(--font-sans-ext);
  font-size: var(--fs-eyebrow);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--muted);
  padding-bottom: 1.3rem;
  transition: opacity 450ms var(--ease), color 450ms var(--ease);
}
/* Seuls les en-tetes de colonnes portent l'extended : sur une ligne de
   tableau, elle etire la lecture au lieu de la guider. Les criteres gardent
   la graisse pour se distinguer des valeurs, pas la chasse. */
.cmp tbody th {
  font-family: var(--font-sans);
  font-weight: 500;
  color: var(--body-ink);
}
.cmp td { color: var(--muted); }

/* La colonne Ginko porte la valeur qui compte : elle est en pleine encre. */
.cmp td.cmp--yes { color: var(--body-ink); }
/* Le survol isole la ligne lue : elle s'eclaire d'un souffle et s'encre
   d'un cran, les autres s'effacent sans disparaitre. Toute la hierarchie
   monte ensemble : les reponses Ginko restent les plus noires. */
.cmp tbody tr { transition: opacity 450ms var(--ease); }
.cmp tbody th,
.cmp tbody td {
  transition: color 450ms var(--ease), background-color 450ms var(--ease),
              padding-left 450ms var(--ease-reveal),
              padding-right 450ms var(--ease-reveal);
}
@media (hover: hover) {
  .cmp tbody:hover tr:not(:hover) { opacity: 0.4; }
  .cmp tbody tr:hover th,
  .cmp tbody tr:hover td { background: rgba(0, 0, 0, 0.03); }
  /* La ligne lue avance d'un cran : le souffle du fond reste en place et
     le texte s'en decolle, ce qui ouvre une marge a gauche. C'est un
     text-indent et non un transform : le transform aurait emporte le fond
     de la cellule avec lui, et la bande grise se serait desalignee des
     colonnes. L'indentation ne deplace que la ligne de texte. */
  /* Le decalage se fait sur la marge de gauche, pas sur un text-indent :
     l'indentation ne pousse que la premiere ligne, et une cellule qui se
     replie laissait la seconde en arriere. Ce que la gauche prend, la
     droite le rend : la cellule garde sa largeur totale et les colonnes ne
     bougent pas sous le curseur. Deux pixels de battement pour qu'aucun
     arrondi ne fasse replier une ligne de plus. */
  .cmp tbody tr:hover th,
  .cmp tbody tr:hover td {
    padding-left: calc(1.2rem + 10px);
    padding-right: calc(1.2rem - 12px);
  }
  .cmp tbody tr:hover th:first-child,
  .cmp tbody tr:hover td:first-child { padding-left: 10px; }
  /* Les deux cellules de bout rendent leur fond plein au voile ci-dessous. */
  .cmp tbody tr:hover th:first-child,
  .cmp tbody tr:hover td:last-child { background: transparent; }
  .cmp tbody tr:hover th:first-child::before,
  .cmp tbody tr:hover td:last-child::before { opacity: 1; }

  /* Les deux coordonnees de la case lue. La ligne s'encre deja, son critere
     avec elle ; il manquait l'autre axe. L'en-tete s'efface comme les lignes
     voisines, sauf celui de la colonne survolee : la case se retrouve au
     croisement de deux reperes, et on sait toujours ce qu'on compare a quoi.
     Le :has() lit la colonne par son rang, la premiere etant un th. */
  .cmp:has(tbody:hover) thead th { opacity: 0.4; }
  .cmp:has(tbody th:hover) thead th:nth-child(1),
  .cmp:has(tbody td:nth-child(2):hover) thead th:nth-child(2),
  .cmp:has(tbody td:nth-child(3):hover) thead th:nth-child(3),
  .cmp:has(tbody td:nth-child(4):hover) thead th:nth-child(4),
  .cmp:has(tbody td:nth-child(5):hover) thead th:nth-child(5),
  .cmp:has(tbody td:nth-child(6):hover) thead th:nth-child(6) {
    opacity: 1;
    color: var(--body-ink);
  }
  .cmp tbody tr:hover td { color: var(--body-ink); }
  .cmp tbody tr:hover td.cmp--yes { color: var(--gk-charcoal); }
  .section--dark .cmp tbody tr:hover th,
  .section--dark .cmp tbody tr:hover td { background: rgba(255, 255, 255, 0.03); }
  .section--dark .cmp tbody tr:hover td.cmp--yes { color: var(--gk-white); }
}

/* La bande du survol s'eteint vers les bords au lieu de s'y couper net.
   Un degrade ne sait pas se transitionner : on fait donc varier l'opacite
   d'un calque qui, lui, porte le degrade. Il vit sous le texte grace au
   z-index negatif, dans le contexte d'empilement ouvert par la cellule. */
.cmp tbody th:first-child,
.cmp tbody td:last-child { position: relative; z-index: 0; }
.cmp tbody th:first-child::before,
.cmp tbody td:last-child::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  opacity: 0;
  transition: opacity 450ms var(--ease);
  pointer-events: none;
}
.cmp tbody th:first-child::before {
  background: linear-gradient(to right, rgba(0, 0, 0, 0) 0, rgba(0, 0, 0, 0.03) 64px);
}
.cmp tbody td:last-child::before {
  background: linear-gradient(to left, rgba(0, 0, 0, 0) 0, rgba(0, 0, 0, 0.03) 64px);
}
.section--dark .cmp tbody th:first-child::before {
  background: linear-gradient(to right, rgba(255, 255, 255, 0) 0, rgba(255, 255, 255, 0.03) 64px);
}
.section--dark .cmp tbody td:last-child::before {
  background: linear-gradient(to left, rgba(255, 255, 255, 0) 0, rgba(255, 255, 255, 0.03) 64px);
}

/* La colonne Ginko ferme le tableau : on lit le familier, puis l'ecran,
   puis elle. Le fond la designe sans la commenter. */
.cmp thead th:nth-child(4),
.cmp tbody td:nth-child(4) { background: rgba(0, 0, 0, 0.04); }
.section--dark .cmp thead th:nth-child(4),
.section--dark .cmp tbody td:nth-child(4) { background: rgba(255, 255, 255, 0.04); }

/* --------------------------------------------------------------------------
   27. HUB DES UNIVERS
   -------------------------------------------------------------------------- */
.universes {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 1px;
  background: rgba(255, 255, 255, 0.14);
  border: 1px solid rgba(255, 255, 255, 0.14);
}
@media (max-width: 1100px) { .universes { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 620px)  { .universes { grid-template-columns: 1fr; } }

.universe {
  background: var(--gk-black);
  padding: var(--space-md) var(--space-sm);
  transition: background var(--dur-fast) var(--ease);
  /* Le contenu se cale au milieu de la tuile : les huit textes n'ont pas la
     meme longueur, alignes en haut ils flottaient chacun a sa hauteur. */
  display: flex;
  flex-direction: column;
  justify-content: center;
}
.universe:hover { background: var(--gk-near-black); }
.universe { text-align: center; }
.universe__texte { display: block; }
.universe__texte .eyebrow { display: block; margin-bottom: 1.1rem; }
.universe__titre {
  display: block;
  font-family: var(--font-serif);
  font-size: 1.25rem;
  line-height: 1.25;
  letter-spacing: -0.01em;
  color: var(--gk-white);
  margin-bottom: 0.8rem;
}
.universe__line {
  font-size: var(--fs-body);
  color: var(--muted);
  max-width: 28ch;
  margin-inline: auto;
}

/* ---- le carrousel des univers -------------------------------------------
   Trois cartes de front, les cinq autres a portee de pouce. La piste est un
   defilement natif avec accrochage : au doigt on la pousse, a la souris on
   se sert des deux fleches, et le clavier la traverse comme une zone de
   defilement ordinaire. Aucune position n'est calculee en JavaScript, le
   script ne fait qu'appuyer sur le meme mecanisme que le doigt. */
.carrousel {
  position: relative;
  /* L'axe de la colonne de texte, mesure depuis la pleine largeur : la
     piste et les fleches s'y alignent toutes les deux. */
  --axe: max(var(--gutter), calc((100% - var(--wrap)) / 2 + var(--gutter)));
}
.carrousel__piste {
  display: flex;
  gap: var(--space-md);
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  /* La piste traverse l'ecran, mais son retrait ramene la premiere carte sur
     l'axe de la colonne de texte : au-dela de la largeur de page, le retrait
     vaut la moitie du debordement plus la gouttiere ; en dessous, la
     gouttiere seule. Les cartes voisines vivent donc dans les marges, et on
     les apercoit de part et d'autre des trois qui se lisent. */
  padding-inline: var(--axe);
  scroll-padding-inline-start: var(--axe);
  /* Deux pixels en haut et en bas pour que le cadre de focus d'une carte ne
     soit pas rogne par le debordement horizontal. */
  padding-block: 2px;
  margin-block: -2px;
  scrollbar-width: none;
  /* Les bords s'eteignent au lieu de se couper : un masque, et non un voile
     pose par-dessus, pour que le fondu suive la couleur de la page quand le
     cameleon la fait changer. */
  -webkit-mask-image: linear-gradient(to right, transparent 0,
                      #000 var(--axe), #000 calc(100% - var(--axe)), transparent 100%);
  mask-image: linear-gradient(to right, transparent 0,
              #000 var(--axe), #000 calc(100% - var(--axe)), transparent 100%);
}
.carrousel__piste::-webkit-scrollbar { display: none; }
.carrousel__piste:focus-visible { outline: 1px solid currentColor; outline-offset: 4px; }

.carte-univers {
  /* Trois cartes dans la largeur de la colonne de texte, gouttieres
     comprises : la piste est plus large qu'elle, un simple tiers de la
     piste aurait donne des cartes demesurees sur grand ecran. */
  flex: 0 0 calc((min(100vw, var(--wrap)) - 2 * var(--gutter) - 2 * var(--space-md)) / 3);
  scroll-snap-align: start;
  display: flex;
  flex-direction: column;
  color: inherit;
  text-decoration: none;
  /* Pas de fond propre : la carte laisse passer celui de la page, qui fond
     d'une section a l'autre. Un noir en dur ici restait noir quand la page
     passait au clair, et le titre s'y perdait. */
  border: 1px solid rgba(255, 255, 255, 0.14);
  transition: border-color var(--dur-fast) var(--ease);
}
.carte-univers:hover { border-color: rgba(255, 255, 255, 0.3); }
.carte-univers:focus-visible { outline: 1px solid currentColor; outline-offset: 3px; }

/* Le cadre de l'image tient le rapport, quelle que soit la photographie :
   les huit sources ne partagent pas le meme, et une rangee de cartes de
   hauteurs differentes ne se lit pas. */
.carte-univers__vue {
  display: block;
  aspect-ratio: 4 / 3;
  overflow: hidden;
}
.carte-univers__vue img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 700ms var(--ease-reveal);
}
.carte-univers:hover .carte-univers__vue img { transform: scale(1.04); }

.carte-univers__corps {
  display: flex;
  flex-direction: column;
  flex: 1;
  padding: var(--space-md) var(--space-sm) var(--space-sm);
}
.carte-univers__corps .eyebrow { display: block; margin-bottom: 0.9rem; }
.carte-univers__titre {
  display: block;
  font-family: var(--font-serif);
  font-size: 1.375rem;
  line-height: 1.2;
  letter-spacing: -0.01em;
  color: inherit;
  margin-bottom: 0.7rem;
}
.carte-univers__ligne {
  display: block;
  font-size: var(--fs-body);
  color: var(--muted);
}
/* Le lien se pose en bas de carte : les huit textes n'ont pas la meme
   longueur, il flottait a huit hauteurs differentes. */
.carte-univers__voir {
  display: inline-flex;
  align-items: baseline;
  gap: 0.6em;
  margin-top: auto;
  padding-top: var(--space-md);
  font-family: var(--font-sans-ext);
  font-size: var(--fs-eyebrow);
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: inherit;
  transition: opacity var(--dur-fast) var(--ease);
}
.carte-univers__voir i { transition: transform var(--dur-fast) var(--ease); }
/* Huit fois la meme phrase en bas de huit cartes ne dit plus rien : elle
   n'apparait qu'a la carte qu'on regarde. Elle garde sa place dans la mise
   en page, sinon la carte se reorganiserait sous le curseur. La ou il n'y a
   pas de survol, au doigt, elle reste lisible en permanence. */
@media (hover: hover) {
  .carte-univers__voir { opacity: 0; }
}
.carte-univers:hover .carte-univers__voir,
.carte-univers:focus-visible .carte-univers__voir,
.carte-univers:focus-within .carte-univers__voir { opacity: 1; }
.carte-univers:hover .carte-univers__voir i { transform: translateX(3px); }

.carrousel__barre {
  display: flex;
  align-items: center;
  /* Le rang a gauche, les fleches a droite, tous deux sur l'axe du texte :
     la ligne tient la largeur de la piste au lieu de se tasser au milieu. */
  justify-content: space-between;
  gap: var(--space-md);
  margin-top: var(--space-lg);
  padding-inline: var(--axe);
}
.carrousel__fleches { display: flex; gap: 0.6rem; }
/* Le rang, entre les deux fleches. Le chiffre qui arrive vient du bas quand
   on avance et du haut quand on recule : le geste de la piste se retrouve
   dans le compteur. Seule l'entree est animee, la sortie serait un second
   chiffre a tenir en place pour un dixieme de seconde. Le total ne bouge
   jamais, il ne fait pas partie du mouvement. */
.carrousel__compte {
  margin: 0;
  display: flex;
  align-items: baseline;
  min-width: 4.5rem;
  font-family: var(--font-sans-ext);
  font-size: var(--fs-eyebrow);
  letter-spacing: 0.16em;
  /* Chiffres de meme chasse : sans cela, le compteur sautillait a chaque
     passage d'un 1 a un 8. */
  font-variant-numeric: tabular-nums;
}
.carrousel__rang {
  display: inline-block;
  color: inherit;
}
.carrousel__total { opacity: 0.45; }
.carrousel__rang.gk-monte { animation: gk-rang-monte 420ms var(--ease-reveal); }
.carrousel__rang.gk-descend { animation: gk-rang-descend 420ms var(--ease-reveal); }
@keyframes gk-rang-monte {
  from { opacity: 0; transform: translateY(0.55em); }
}
@keyframes gk-rang-descend {
  from { opacity: 0; transform: translateY(-0.55em); }
}

.carrousel__fleche {
  width: 2.75rem;
  height: 2.75rem;
  display: grid;
  place-items: center;
  border: 1px solid rgba(255, 255, 255, 0.24);
  background: none;
  color: inherit;
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease),
              opacity var(--dur-fast) var(--ease);
}
.carrousel__fleche svg { width: 1.1rem; height: 1.1rem; }
.carrousel__fleche:hover { background: rgba(255, 255, 255, 0.08); border-color: rgba(255, 255, 255, 0.4); }
/* En bout de piste la fleche s'efface sans disparaitre : on voit qu'elle
   existe, on voit qu'elle ne sert plus. */
.carrousel__fleche[disabled] { opacity: 0.25; cursor: default; }
.carrousel__fleche[disabled]:hover { background: none; border-color: rgba(255, 255, 255, 0.24); }

/* Sur fond clair, les memes traits en encre. Le carrousel vit dans une
   section sombre, mais le cameleon peut la faire lire sur du blanc le temps
   d'un fondu : sans cela les cartes et les fleches disparaissaient. */
body.gk-clair .carte-univers,
.section--light .carte-univers,
.section--paper .carte-univers { border-color: rgba(0, 0, 0, 0.14); }
body.gk-clair .carte-univers:hover,
.section--light .carte-univers:hover,
.section--paper .carte-univers:hover { border-color: rgba(0, 0, 0, 0.3); }
body.gk-clair .carrousel__fleche,
.section--light .carrousel__fleche,
.section--paper .carrousel__fleche { border-color: rgba(0, 0, 0, 0.24); }
body.gk-clair .carrousel__fleche:hover,
.section--light .carrousel__fleche:hover,
.section--paper .carrousel__fleche:hover {
  background: rgba(0, 0, 0, 0.05);
  border-color: rgba(0, 0, 0, 0.4);
}
body.gk-clair .carrousel__fleche[disabled]:hover,
.section--light .carrousel__fleche[disabled]:hover {
  background: none;
  border-color: rgba(0, 0, 0, 0.24);
}

@media (max-width: 1100px) {
  .carte-univers {
    flex-basis: calc((min(100vw, var(--wrap)) - 2 * var(--gutter) - var(--space-md)) / 2);
  }
}
@media (max-width: 700px) {
  .carte-univers { flex-basis: calc(100vw - 2 * var(--gutter) - var(--space-md)); }
}

/* ---- la page 404 : le mot au milieu de la trame --------------------------
   La matrice occupe toute la section, le texte se pose dessus. On garde la
   section hors cameleon : elle est noire de bout en bout, c'est une page
   qui n'existe pas, elle n'a personne a qui passer la main. */
.p404 {
  position: relative;
  overflow: hidden;
  min-height: 100svh;
  display: flex;
  align-items: center;
  text-align: center;
}
.p404__matrice {
  position: absolute;
  inset: 0;
  pointer-events: none;
}
.p404__matrice canvas { display: block; width: 100%; height: 100%; }
/* Un halo tres doux au centre : la trame s'efface derriere le texte au
   lieu de le grignoter. */
.p404__matrice::after {
  content: "";
  position: absolute;
  inset: 0;
  /* Le halo ne sert qu'a une chose : que le texte se lise. Assez large
     pour couvrir le titre et sa phrase, assez court pour laisser la trame
     vivre partout ailleurs. */
  background: radial-gradient(46% 30% at 50% 42%,
              rgba(0, 0, 0, 0.88) 20%, rgba(0, 0, 0, 0) 100%);
}
.p404__mot {
  position: relative;
  z-index: 1;
  max-width: 46rem;
}
.p404__mot h1 { text-wrap: balance; }
.p404__mot .lead { margin-inline: auto; max-width: 34rem; }

/* --------------------------------------------------------------------------
   28. FRISE
   -------------------------------------------------------------------------- */


/* --------------------------------------------------------------------------
   29. FONDATEURS
   -------------------------------------------------------------------------- */
/* ---- les trois fondateurs -----------------------------------------------
   Une fiche est une colonne : le portrait en haut, le texte dessous, et le
   lien tout en bas. C'est ce qui fait que les trois liens tombent a la
   meme hauteur alors que les biographies n'ont pas la meme longueur. */
.person {
  padding-top: 0;
  display: flex;
  flex-direction: column;
  /* Tout est centre : le nom, le role, la biographie et le lien. Aligne a
     gauche, chaque fiche tirait vers son propre bord et les trois colonnes
     ne formaient plus un ensemble. */
  text-align: center;
  align-items: center;
}
.person__corps {
  display: flex;
  flex-direction: column;
  align-items: center;
  flex: 1;
  width: 100%;
}
.section--light .person, .section--paper .person { border-top-color: rgba(0, 0, 0, 0.16); }

.person__photo {
  width: 100%;
  margin: 0 0 var(--space-md);
  aspect-ratio: 4 / 5;
  overflow: hidden;
  background: rgba(0, 0, 0, 0.05);
}
.person__photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 900ms var(--ease-reveal);
}
.person:hover .person__photo img { transform: scale(1.03); }

.person h3 { font-size: var(--fs-h3); margin-bottom: 0.4rem; }
.person__role {
  font-family: var(--font-sans-ext);
  font-size: var(--fs-eyebrow);
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--muted);
  margin-bottom: 1.1rem;
}
/* Un texte de fond se lit dans une colonne mesuree et centree. Il vivait
   auparavant dans un .split, pousse contre un titre d'une seule ligne : la
   moitie gauche de la page restait vide sur toute la hauteur du propos. */
.prose--centre {
  max-width: 62ch;
  margin-inline: auto;
}
.prose--centre p + p { margin-top: 1.4em; }

.person__texte {
  font-size: var(--fs-body);
  line-height: 1.8;
  color: var(--muted);
  margin: 0;
  max-width: 34ch;
  margin-inline: auto;
  text-wrap: pretty;
}
/* Le lien se pose en bas de fiche : marge automatique au-dessus, filet au
   ras du texte pour le detacher sans l'encadrer. */
.person__lien {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: auto;
  padding-top: var(--space-md);
  font-family: var(--font-sans-ext);
  font-size: var(--fs-eyebrow);
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: inherit;
  text-decoration: none;
  opacity: 0.55;
  transition: opacity var(--dur-fast) var(--ease);
}
.person__lien svg { width: 0.9rem; height: 0.9rem; fill: currentColor; }
.person:hover .person__lien,
.person__lien:focus-visible { opacity: 1; }

/* --------------------------------------------------------------------------
   30. FAQ
   Un accordeon natif : il fonctionne sans JavaScript et reste accessible.
   -------------------------------------------------------------------------- */
/* La FAQ en panneau : un seul sommaire colle a gauche, tous les chapitres
   en colonne a droite. Le chapitre sous les yeux s'allume dans le
   sommaire, et un clic y defile en douceur. */
.faq-panneau {
  display: grid;
  grid-template-columns: minmax(0, 3fr) minmax(0, 8fr);
  gap: var(--space-lg);
  align-items: start;
}
.faq-rail {
  position: sticky;
  top: 7rem;
  display: grid;
  gap: var(--space-md);
  align-content: start;
}
.faq-sommaire {
  display: grid;
  justify-items: start;
  gap: 0.85rem;
}
.faq-sommaire__lien {
  font-family: var(--font-sans-ext);
  font-size: var(--fs-eyebrow);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.18em;
  line-height: 1.3;
  color: var(--muted);
  transition: color var(--dur-fast) var(--ease), padding-left var(--dur-fast) var(--ease);
}
.faq-sommaire__lien:hover { padding-left: 0.5rem; }
.faq-sommaire__lien.est-actif { color: currentColor; }
.section--dark .faq-sommaire__lien.est-actif { color: var(--gk-white); }

.faq-groupe { scroll-margin-top: 7rem; }
.faq-groupe + .faq-groupe { margin-top: var(--space-xl); }
.faq-groupe__titre { margin-bottom: var(--space-sm); }

@media (max-width: 800px) {
  .faq-panneau { grid-template-columns: 1fr; gap: var(--space-md); }
  /* En mobile le rail se pose en haut : champ pleine largeur, sommaire
     en rangee, toujours utile pour sauter. */
  .faq-rail { position: static; gap: var(--space-sm); }
  .faq-sommaire {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem 1.4rem;
  }
  .faq-sommaire__lien { font-size: var(--fs-eyebrow); }
  .faq-sommaire__lien:hover { padding-left: 0; }
  .faq-groupe + .faq-groupe { margin-top: var(--space-lg); }
}

/* Les filets ne separent que les questions entre elles : ni trait au-dessus
   de la premiere, ni trait sous la derniere -- le chapitre respire. */
.faq__item { border-bottom: 1px solid rgba(255, 255, 255, 0.14); }
.section--light .faq__item { border-bottom-color: rgba(0, 0, 0, 0.12); }
.faq__item:last-child { border-bottom: 0; }

.faq__item summary {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-sm);
  padding-block: var(--space-sm);
  cursor: pointer;
  list-style: none;
  font-family: var(--font-serif);
  font-size: 1.25rem;
  line-height: 1.25;
  letter-spacing: -0.015em;
  /* Le survol s'indente, comme les lignes d'univers : la question s'avance
     vers la main au lieu de s'eteindre. */
  transition: padding-left var(--dur-fast) var(--ease);
}
.faq__item summary::-webkit-details-marker { display: none; }
.faq__item summary:hover { padding-left: 0.6rem; }

/* Le signe est un chevron : pointe en bas ferme, en haut ouvert. */
.faq__sign {
  flex: none;
  width: 9px;
  height: 9px;
  margin-top: 0.45em;
  border-right: 1.2px solid currentColor;
  border-bottom: 1.2px solid currentColor;
  transform: rotate(45deg);
  transition: transform 420ms var(--ease-reveal);
}
.faq__item[open] .faq__sign { transform: translateY(4px) rotate(225deg); }
.faq__item.se-ferme .faq__sign { transform: rotate(45deg); }

.faq__answer { max-width: 62ch; }
/* Le rembourrage vit dans le paragraphe : la hauteur animee du conteneur
   couvre ainsi tout ce qui se voit, et rien ne saute aux bornes. */
.faq__answer p { padding-bottom: var(--space-sm); }
details.faq__item[open]:not(.se-ferme) .faq__answer p {
  animation: gk-fondu-texte 560ms var(--ease-reveal);
}

.faq__answer p { color: var(--gk-mid-gray); }
.section--light .faq__answer p { color: var(--gk-charcoal); }

/* --------------------------------------------------------------------------
   31. LES TROIS MOMENTS (pages univers)
   -------------------------------------------------------------------------- */
.moments { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-lg); }
@media (max-width: 900px) { .moments { grid-template-columns: 1fr; gap: var(--space-md); } }

.moment { padding-top: 0; }
.section--dark .moment { border-top-color: rgba(255, 255, 255, 0.2); }
.moment__num {
  font-family: var(--font-serif);
  font-size: 1.125rem;
  color: var(--muted);
  margin-bottom: 1.1rem;
}
.moment h3 { font-size: var(--fs-h3); margin-bottom: 0.9rem; }
.moment__text { font-size: var(--fs-body); color: var(--body-ink); max-width: 34ch; }
/* Le mur qu'on survole s'avance d'un souffle, son numero s'encre. */
.moment h3 { transition: transform 600ms var(--ease-reveal); }
.moment__num { transition: color 450ms var(--ease); }
@media (hover: hover) {
  .moment:hover h3 { transform: translateX(0.4rem); }
  .moment:hover .moment__num { color: inherit; }
}

/* La gouttiere interieure des panneaux de texte : le paragraphe du constat
   (hero) et le propos de la carte objet partagent le meme retrait, donc le
   meme axe de lecture. */
main { --gouttiere-carte: clamp(1.5rem, 3.5vw, 4rem); }

/* Le hero et la carte objet se suivent de pres : le constat vit deja en
   bas de l'image, la carte n'a pas besoin d'un second grand silence. */
.hero--fiche + .section { padding-top: var(--space-lg); }

/* ---- l'objet : la reponse au probleme, en carte encadree -----------------
   Le visiteur vient de lire le probleme ; cette carte repond avant de
   demontrer. Une moitie pour la photo du cadre en situation, une moitie
   pour le propos et trois faits en situation, le tout tenu par un filet.
   La hauteur vient du texte, la photo s'y cadre (cover) et s'encre a
   l'arrivee (data-eink). */
.objet {
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: stretch;
  /* Meme surface que les blocs de la page tarifs : un fond legerement
     detache du noir de la page, tenu par un filet. */
  border: 1px solid rgba(255, 255, 255, 0.16);
  background: var(--gk-near-black);
}
.section--light .objet,
.section--paper .objet {
  border-color: rgba(0, 0, 0, 0.14);
  background: var(--gk-off-white);
}
.objet__photo { position: relative; margin: 0; overflow: hidden; }
.objet__photo img {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: scale 1600ms var(--ease);
}
.objet__propos {
  align-self: center;
  padding: clamp(2.5rem, 6svh, 4rem) var(--gouttiere-carte);
}

/* Le voile du bloc texte, ancre sur l'arete de l'image. Deux valeurs
   declarees, donc interpolables : ou commence le fondu (--bord, l'arete
   de l'image lue dans le repere du bloc) et sur quelle largeur il se fait
   (--voile). Sans declaration, elles sauteraient d'un etat a l'autre. */
@property --bord {
  syntax: "<percentage>";
  inherits: true;
  initial-value: 0%;
}
@property --voile {
  syntax: "<length>";
  inherits: true;
  initial-value: 0px;
}

/* Le volet : ferme, la carte entiere est retractee autour de l'image, qui
   se tient seule au centre ; le filet l'epouse, et le propos est cache
   derriere elle, pas seulement transparent. En s'ouvrant, la carte s'ecarte
   des deux cotes, l'image glisse a gauche et le texte se decouvre a droite.
   Trois choses bougent ensemble : la fenetre de la carte (clip-path), le
   filet (ses bords), et les deux volets (transform). */
/* Le filet ne sert que pendant le volet : ailleurs, la carte porte le sien. */
.objet__filet { display: none; }

@media (min-width: 1024px) {
  /* La liste de transitions est complete et le selecteur porte assez de
     poids pour passer devant la regle cameleon : sans cela le volet
     perdrait son clip-path anime. */
  .objet.va-se-deployer,
  body.gk-cameleon main .objet.va-se-deployer {
    position: relative;
    /* Le filet passe a l'element dedie, qui peut se retracter. La bordure
       reste declaree : elle tient les mesures de la carte. */
    border-color: transparent;
    clip-path: inset(0 25% 0 25%);
    transition: clip-path 1400ms var(--ease-reveal),
                background-color 900ms ease,
                color 900ms ease;
  }
  /* La couleur du filet reste ici, a poids leger : le cameleon doit
     pouvoir l'encrer quand la page passe au clair. */
  .objet.va-se-deployer .objet__filet {
    display: block;
    position: absolute;
    top: 0;
    bottom: 0;
    left: 25%;
    right: 25%;
    z-index: 3;
    pointer-events: none;
    border: 1px solid rgba(255, 255, 255, 0.16);
  }
  .objet.va-se-deployer .objet__filet,
  body.gk-cameleon main .objet.va-se-deployer .objet__filet {
    transition: left 1400ms var(--ease-reveal),
                right 1400ms var(--ease-reveal),
                border-color 900ms ease;
  }
  /* L'image est au-dessus du propos : le texte est reellement dessous. */
  .objet.va-se-deployer .objet__photo {
    z-index: 2;
    transform: translateX(50%);
  }
  /* Le bloc de droite est range tout entier sous l'image : il parcourt la
     meme distance qu'elle, en sens inverse. Son bord droit reste colle a
     celui de la carte pendant tout le trajet, si bien qu'il coulisse
     vraiment au lieu d'etre devoile par un masque. */
  /* Le fondu se tient sur l'arete de l'image, jamais sur le bord du bloc :
     ce bord-la reste cache dessous pendant tout le trajet, un voile pose
     dessus ne se verrait pas. L'arete, elle, se lit dans le repere du bloc
     a 100 % ferme et 0 % ouvert -- les deux avancent du meme pas, le fondu
     reste donc colle a l'image, et le texte s'y dissout en sortant. */
  .objet.va-se-deployer .objet__propos {
    transform: translateX(-50%);
    --bord: 100%;
    --voile: 7.5rem;
    -webkit-mask-image: linear-gradient(to right,
      transparent 0, transparent var(--bord), #000 calc(var(--bord) + var(--voile)));
    mask-image: linear-gradient(to right,
      transparent 0, transparent var(--bord), #000 calc(var(--bord) + var(--voile)));
  }
  .objet.va-se-deployer .objet__photo,
  .objet.va-se-deployer .objet__propos {
    transition: transform 1400ms var(--ease-reveal),
                --bord 1400ms var(--ease-reveal),
                --voile 1400ms var(--ease-reveal);
  }

  /* Meme poids que la regle du volet ferme, et posee apres : sans cela le
     clip reste retracte alors que les volets, eux, se sont ouverts. */
  .objet.est-deploye,
  body.gk-cameleon main .objet.est-deploye { clip-path: inset(0 0 0 0); }
  .objet.est-deploye .objet__filet { left: 0; right: 0; }
  .objet.est-deploye .objet__photo,
  .objet.est-deploye .objet__propos { transform: none; }
  /* Ouvert, le fondu se resorbe entierement : sans cela il mangerait en
     permanence le premier mot de chaque ligne. */
  .objet.est-deploye .objet__propos { --bord: 0%; --voile: 0px; }
}
@media (hover: hover) {
  .objet:hover .objet__photo img { scale: 1.04; }
  .objet__faits .spec:hover { transform: translateX(0.5rem); }
  .objet__faits .spec:hover dt { color: var(--gk-white); }
}
.objet__faits .spec { transition: transform 600ms var(--ease-reveal); }
.objet__faits .spec dt { transition: color 450ms var(--ease); }
@media (max-width: 1023px) {
  .objet__photo { position: static; }
  .objet__photo img {
    position: static;
    height: auto;
    aspect-ratio: 4 / 3;
  }
}
/* Trois faits en pile, chacun mis en situation dans l'espace du lecteur :
   le detail est une phrase, pas une valeur affichee en grand. */
.objet__faits {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-md);
  margin: var(--space-lg) 0 0;
}
.objet__faits .spec dd {
  font-size: 1.0625rem;
  line-height: 1.55;
  letter-spacing: 0;
  margin: 0.55rem 0 0;
  max-width: 52ch;
}
@media (max-width: 1023px) {
  .objet { grid-template-columns: 1fr; }
}

/* ---- ce que Studio orchestre ici : la partition --------------------------
   Le propos tient la colonne de gauche et l'accompagne en epingle ; chaque
   capacite est une ligne numerotee a droite, en serif, separee d'un filet.
   Pas de coche : quatre lignes se lisent comme une partition, pas comme un
   argumentaire. */
.orchestre {
  display: grid;
  grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
  gap: var(--space-lg) var(--space-xl);
  align-items: start;
}
.orchestre__tete { position: sticky; top: 7rem; }
.orchestre__lignes { list-style: none; margin: 0; padding: 0; }
.orchestre__ligne {
  display: grid;
  grid-template-columns: 3rem minmax(0, 1fr);
  align-items: baseline;
  gap: 1.1rem;
  padding-block: 1.8rem;
  transition: transform 600ms var(--ease-reveal);
}
.orchestre__ligne:first-child { padding-top: 0.4rem; }
.orchestre__ligne + .orchestre__ligne { border-top: 1px solid rgba(255, 255, 255, 0.14); }
.orchestre__num {
  font-family: var(--font-sans-ext);
  font-size: var(--fs-eyebrow);
  letter-spacing: 0.18em;
  color: var(--gk-warm-gray);
  transition: color 450ms var(--ease);
}
.orchestre__texte {
  margin: 0;
  font-family: var(--font-serif);
  font-size: clamp(1.15rem, 1.8vw, 1.4rem);
  line-height: 1.5;
}
@media (hover: hover) {
  .orchestre__ligne:hover { transform: translateX(0.5rem); }
  .orchestre__ligne:hover .orchestre__num { color: currentColor; }
}
@media (max-width: 1023px) {
  .orchestre { grid-template-columns: 1fr; }
  .orchestre__tete { position: static; }
}

/* ---- ce que ca change : le texte s'encre a la lecture --------------------
   Un seul grand texte, centre, sans titre : il s'encre mot a mot au rythme
   du defilement, et les deux gestes se posent dessous, centres eux aussi.
   Sans script ni avec le mouvement reduit, le texte est plein. */
.lecture {
  display: grid;
  justify-items: center;
  text-align: center;
}
.lecture__texte {
  margin: var(--space-md) 0 0;
  max-width: 32em;
  font-family: var(--font-serif);
  font-size: clamp(1.5rem, 2.4vw, 2rem);
  line-height: 1.5;
  letter-spacing: -0.01em;
}
.lecture__actions { justify-content: center; }
.lecture__texte.est-scrute .lecture__mot {
  opacity: 0.16;
  transition: opacity 600ms var(--ease);
}
.lecture__texte.est-scrute .lecture__mot.est-lu { opacity: 1; }

/* Les tuiles du hub sont des liens : elles doivent le montrer. */
.universe {
  position: relative;
  display: block;
  color: inherit;
}
/* Une fleche posee dans un coin ne dit pas ou elle mene : le lien se nomme.
   La place qu'il occupe est reservee en permanence, donc la tuile ne change
   jamais de hauteur et la grille ne bouge pas quand on survole. Au repos le
   texte se centre dans la tuile entiere ; a l'arrivee du lien il remonte de
   la moitie de la reserve, et les deux se centrent ensemble. */
.universe { --reserve: calc(1.15rem + var(--space-sm)); }
.universe__texte {
  transform: translateY(calc(var(--reserve) / 2));
  transition: transform 340ms var(--ease);
}
.universe:hover .universe__texte,
.universe:focus-visible .universe__texte { transform: none; }

.universe__voir {
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 0.45rem;
  height: 1.15rem;
  margin-top: var(--space-sm);
  font-family: var(--font-sans-ext);
  font-size: var(--fs-eyebrow);
  text-transform: uppercase;
  letter-spacing: 0.18em;
  color: var(--gk-white);
  opacity: 0;
  transition: opacity 260ms var(--ease);
}
.universe__voir i {
  font-style: normal;
  transition: transform var(--dur-fast) var(--ease);
}
.universe:hover .universe__voir,
.universe:focus-visible .universe__voir { opacity: 1; }
.universe:hover .universe__voir i { transform: translateX(3px); }

/* Sans survol, le lien reste ouvert : au doigt, la tuile serait restee muette. */
@media (hover: none) {
  .universe__texte { transform: none; }
  .universe__voir { opacity: 1; }
}

/* ==========================================================================
   32. PAGE D'ACCUEIL
   ========================================================================== */

.hero__actions {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  flex-wrap: wrap;
  margin-top: var(--space-md);
}

/* --------------------------------------------------------------------------
   L'ANCIEN RAFRAICHISSEMENT ENCRE
   La trame de pixels et l'eclair qui accompagnaient l'apparition des images
   ont ete retires : les images naissent desormais dans le meme fondu que le
   reste (data-reveal). Les attributs data-eink et les classes posees par le
   script restent inertes.
   -------------------------------------------------------------------------- */
.eink { position: relative; overflow: hidden; }
/* Rendu produit : centre et contenu, jamais pleine largeur */
.eink > img { display: block; width: 100%; }

/* --------------------------------------------------------------------------
   CARTES MODELE CLIQUABLES
   -------------------------------------------------------------------------- */
.model-card--link { color: inherit; display: block; }
.model-card--link .model-card__media { display: block; aspect-ratio: 4 / 3; }
.model-card--link .model-card__media img { width: 100%; height: 100%; object-fit: cover; }

.model-card--link .model-card__body {
  display: grid;
  gap: 0.75rem;
  padding: var(--space-md) var(--space-sm) var(--space-md);
  justify-items: center;
  text-align: center;
}
.model-card--link .eyebrow { margin-bottom: 0; }
.model-card--link .h3 { display: block; }

.model-card__meta {
  font-size: var(--fs-body);
  color: var(--muted);
}

.model-card--link .link-underline { justify-self: center; margin-top: 0.4rem; }

/* Toute la carte repond au survol : l'image avance d'un cran dans son cadre,
   le lien s'allume et sa fleche part. Le pointeur peut etre n'importe ou
   sur la carte, c'est elle qui est cliquable, pas seulement le lien. */
.model-card__media img,
.model-card--link .model-card__media img { transition: transform 1.1s var(--ease-reveal); }
.model-card--link:hover .model-card__media img { transform: scale(1.045); }
.model-card--link .link-underline {
  color: var(--muted, var(--gk-mid-gray));
  transition: color var(--dur-fast) var(--ease);
}
.section--dark .model-card--link:hover .link-underline { color: var(--gk-white); }
.section--light .model-card--link:hover .link-underline,
.section--paper .model-card--link:hover .link-underline { color: var(--gk-black); }
.model-card--link:hover .link-underline::after { transform: translateX(4px); }

/* --------------------------------------------------------------------------
   LES HUIT UNIVERS, EN LISTE
   Une ligne par univers : le nom bascule en encre pleine au survol.
   -------------------------------------------------------------------------- */
.worlds {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 0 var(--space-xl);
}
/* Ni filet au-dessus de la premiere rangee, ni sous la derniere : la
   liste s'ouvre et se ferme sur le blanc, comme la FAQ. */
.worlds .world:nth-last-child(-n+2) { border-bottom: 0; }
@media (max-width: 800px) {
  .worlds { grid-template-columns: 1fr; gap: 0; }
  .worlds .world:nth-last-child(2) { border-bottom: 1px solid rgba(0, 0, 0, 0.12); }
  .section--dark .worlds .world:nth-last-child(2) { border-bottom-color: rgba(255, 255, 255, 0.14); }
}

.world {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-sm);
  padding-block: var(--space-sm);
  border-bottom: 1px solid rgba(0, 0, 0, 0.12);
  font-family: var(--font-serif);
  font-size: var(--fs-h3);
  line-height: 1.1;
  letter-spacing: -0.015em;
  color: var(--muted);
  transition: color var(--dur-fast) var(--ease), padding-left var(--dur-fast) var(--ease);
}
.section--dark .world { border-bottom-color: rgba(255, 255, 255, 0.14); }

.world:hover {
  color: var(--gk-charcoal);
  padding-left: 0.6rem;
}
.section--dark .world:hover { color: var(--gk-white); }

.world i {
  font-style: normal;
  opacity: 0;
  transform: translateX(-6px);
  transition: opacity var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
.world:hover i { opacity: 1; transform: translateX(0); }

/* --------------------------------------------------------------------------
   33. PARTENAIRES
   En texte, pas en logos : aucun logo tiers ne peut etre publie sans accord
   ecrit, et les formulations retenues sont celles validees commercialement.
   -------------------------------------------------------------------------- */


/* --------------------------------------------------------------------------
   34. LIENS TEXTE DU PANNEAU
   Toutes les entrees ne meritent pas une carte. Les pages de reference
   vivent en liste, a gauche, sous l'intro.
   -------------------------------------------------------------------------- */
.mega__links {
  list-style: none;
  padding: 0;
  margin: var(--space-sm) 0 0;
  display: grid;
  gap: 0.15rem;
}
/* Les filets ne servent qu'a separer : un trait au-dessus du premier et un
   autre sous le dernier fermaient la liste comme un tableau. */
.mega__links li + li { border-top: 1px solid rgba(255, 255, 255, 0.12); }
.mega__links a {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-xs);
  padding-block: 0.85rem;
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  letter-spacing: 0;
  text-transform: none;
  color: rgba(255, 255, 255, 0.78);
  transition: color var(--dur-fast) var(--ease), padding-left var(--dur-fast) var(--ease);
}
.mega__links a:hover { color: var(--gk-white); padding-left: 0.45rem; }
.mega__links i {
  font-style: normal;
  opacity: 0.45;
  transition: opacity var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
.mega__links a:hover i { opacity: 1; transform: translateX(3px); }

/* Le "Tout voir" respire sous la liste et se lit comme une action a part */
.mega__lead .link-underline { display: inline-block; margin-top: 1.4rem; }

/* Le lien vers la rubrique est la premiere entree de la liste : meme filet,
   meme rythme. Seule sa couleur pleine dit que c'est la destination
   principale ; une typographie a part l'aurait sorti de l'alignement. */
.mega__links--tout a { color: var(--gk-white); }
.mega__links--tout i { opacity: 0.8; }

/* Avec deux cartes seulement, la colonne de gauche peut respirer davantage. */
.mega__inner { grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); }

/* --------------------------------------------------------------------------
   35. DECLARATION ET ARGUMENTS
   Une affirmation qui porte, puis les faits qui la soutiennent.
   Le recit a la troisieme personne sonnait anecdotique : ici la phrase
   fait le travail, et les colonnes donnent la matiere commerciale.
   -------------------------------------------------------------------------- */
.statement {
  font-family: var(--font-serif);
  font-size: var(--fs-hero);
  font-weight: 300;
  line-height: 1.08;
  letter-spacing: -0.02em;
  max-width: 22ch;
  text-wrap: balance;
}
.statement em {
  font-style: italic;
  color: var(--muted);
}

/* --------------------------------------------------------------------------
   LA SCENE
   Trois colonnes de gris se lisaient en diagonale, donc pas du tout. Ici la
   phrase que les gens prononcent vraiment devant le cadre passe devant, en
   serif et en grand ; l'explication la suit, discrete. On s'arrete sur une
   phrase entendue, pas sur un intertitre qui la resume.
   -------------------------------------------------------------------------- */
/* La phrase d'ouverture et son bouton se partagent la ligne : le bouton
   n'attend plus la fin de la scene pour se montrer, et la phrase dispose de
   la largeur qu'il lui faut pour tenir en deux lignes. */
.reaction__tete {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--space-lg) var(--space-xl);
  flex-wrap: wrap;
}
.reaction__tete .statement { max-width: 30ch; }
/* Deux actions cote a cote : la demonstration, et la FAQ pour qui veut
   lire avant de parler. Le meme ecart que dans le hero. */
.reaction__cta {
  /* flex: none (0 0 auto) empechait ce bloc de retrecir : les deux actions
     restaient cote a cote quoi qu'il arrive, 411 px dans une fenetre de
     375, et toute la page partait en defilement horizontal. Il peut
     desormais retrecir, et son propre flex-wrap fait passer la seconde
     action sous la premiere. */
  flex: 0 1 auto;
  margin: 0;
  display: flex;
  align-items: center;
  gap: var(--space-md);
  flex-wrap: wrap;
}
@media (max-width: 860px) {
  .reaction__tete { align-items: flex-start; }
  .reaction__tete .statement { max-width: 22ch; }
  /* Sur un ecran etroit les deux actions prennent toute la largeur et se
     rangent l'une sous l'autre, plutot que de se serrer sur une ligne. */
  .reaction__cta { width: 100%; }
}

.scene {
  list-style: none;
  /* La phrase d'ouverture a besoin de respirer avant que la scene commence :
     le premier filet ne doit pas venir la souligner. */
  margin: var(--space-2xl) 0 0;
  padding: 0;
}
.scene__temps {
  display: grid;
  grid-template-columns: 4rem minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--space-md) var(--space-lg);
  align-items: baseline;
  padding-block: var(--space-lg);
  border-top: 1px solid rgba(0, 0, 0, 0.14);
}
/* Aucun filet sous le dernier temps : la scene se termine, elle ne referme
   pas un tableau. */
.section--dark .scene__temps { border-top-color: rgba(255, 255, 255, 0.16); }

.scene__num {
  font-family: var(--font-sans-ext);
  font-size: var(--fs-eyebrow);
  font-weight: 500;
  letter-spacing: 0.2em;
  color: var(--muted);
}
.scene__dit {
  font-family: var(--font-serif);
  font-size: clamp(1.375rem, 2.4vw, 1.875rem);
  line-height: 1.2;
  letter-spacing: -0.015em;
  text-wrap: balance;
}
.scene__note {
  font-size: var(--fs-body);
  line-height: 1.8;
  color: var(--muted);
  max-width: 38ch;
}
/* La reponse s'ouvre sur un mot net, en pleine encre : c'est lui qui dit,
   meme lu en diagonale, que cette colonne repond a la question d'en face. */
.scene__note strong {
  color: var(--gk-white);
  font-weight: 500;
}
body.gk-clair .scene__note strong { color: var(--gk-black); }

@media (max-width: 900px) {
  .scene__temps {
    grid-template-columns: 1fr;
    gap: 0.9rem;
    padding-block: var(--space-md);
  }
  .scene__note { max-width: none; }
}

/* --------------------------------------------------------------------------
   36. CARTES PARTENAIRES A TRAME DE POINTS
   Reprise fidele de la page de tests d'origine. Le logo est un SVG injecte
   inline par hovercards.js (data-svg-path) et trace en contour blanc 0.5px.
   Au survol : la trame WebGL s'allume dans la couleur de la carte, le trait
   du logo prend cette couleur et se remplit, le logo zoome, le nom apparait.
   -------------------------------------------------------------------------- */
.ginko-hovercards {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-sm);
}
@media (max-width: 900px) { .ginko-hovercards { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px) { .ginko-hovercards { grid-template-columns: 1fr; } }

.hover-card {
  position: relative;
  aspect-ratio: 16 / 10;
  overflow: hidden;
  background: #09090b;          /* le JS pose les memes valeurs, on evite le flash */
  border: 1px solid #111;
  transition: border-color 300ms ease;
}

.hover-card .depth-background,
.hover-card .canvas-container,
.hover-card .gradient-mask-center,
.hover-card .gradient-mask-bottom {
  position: absolute;
  inset: 0;
  display: block;
  pointer-events: none;
}
.hover-card .canvas-container { z-index: 1; }
.hover-card .canvas-container canvas {
  width: 100% !important;
  height: 100% !important;
  display: block;
}
.hover-card .depth-background {
  background: radial-gradient(60% 60% at 50% 42%, rgba(255, 255, 255, 0.05), transparent 72%);
}
.hover-card .gradient-mask-center {
  z-index: 2;
  background: radial-gradient(circle at 50% 50%, transparent 0%, rgba(9, 9, 11, 0.85) 78%);
}
.hover-card .gradient-mask-bottom {
  z-index: 2;
  background: linear-gradient(to top, #09090b 0%, transparent 42%);
}

.hover-card .card-content {
  position: absolute;
  inset: 0;
  z-index: 10;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  /* Padding symetrique : le nom ne s'affiche plus sous le logo, rien ne
     doit plus decaler le centrage. */
  padding: var(--space-md);
  transition: transform 300ms ease;
}

/* Le logo, injecte inline : contour blanc fin, rempli en couleur au survol */
.hover-card .icon-container {
  width: 58%;
  max-width: 15rem;
  height: 3.75rem;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: visible;
  transform: scale(1);
  transition: transform 300ms ease;
}
.hover-card:hover .icon-container { transform: scale(1.15); }

.hover-card .icon-container svg {
  width: 100%;
  height: 100%;
  overflow: visible;
}
.hover-card .icon-container path,
.hover-card .icon-container circle,
.hover-card .icon-container rect,
.hover-card .icon-container polygon,
.hover-card .icon-container polyline,
.hover-card .icon-container ellipse,
.hover-card .icon-container line {
  fill: var(--svg-fill-color, transparent) !important;
  stroke: var(--svg-stroke-color, #ffffff) !important;
  stroke-width: 1.1px !important;
  vector-effect: non-scaling-stroke !important;
  transition: fill 300ms ease, stroke 300ms ease !important;
}
/* Au repos, le contour se lit sans effort ; au survol il s'affirme. */
.hover-card .icon-container {
  opacity: 0.72;
  transition: opacity 300ms ease, transform 300ms ease;
}
.hover-card:hover .icon-container { opacity: 1; }
.hover-card:hover .icon-container path,
.hover-card:hover .icon-container circle,
.hover-card:hover .icon-container rect,
.hover-card:hover .icon-container polygon,
.hover-card:hover .icon-container polyline,
.hover-card:hover .icon-container ellipse,
.hover-card:hover .icon-container line { stroke-width: 1.4px !important; }

/* Le nom, sous le logo, seulement au survol */
.hover-card .card-title {
  position: relative;
  margin-top: 0.9rem;
  font-family: var(--font-sans-ext);
  font-size: var(--fs-eyebrow);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--gk-white);
  opacity: 0;
  transition: opacity 300ms ease;
  white-space: nowrap;
}
.hover-card:hover .card-title { opacity: 1; }

/* La quatrieme case reste muette : elle annonce sans rien promettre. */

/* --------------------------------------------------------------------------
   37. VUE ECLATEE, SEQUENCE AU SCROLL
   L'objet se demonte a mesure qu'on descend. Le visuel est colle pendant
   que le texte defile a cote : on comprend l'assemblage sans quitter la page.
   -------------------------------------------------------------------------- */
/* La section doit etre nettement plus haute que l'ecran, sinon la sequence
   n'a pas de course : le visuel colle change de frame pendant que le texte
   defile a cote. */
/* Vue eclatee.
   La section est haute par necessite : sa hauteur est la duree de la
   sequence. Mais seule l'image tenait au scroll, le texte s'arretait au bout
   d'un ecran et les deux suivants restaient vides. C'est desormais le bloc
   entier qui reste colle, image et texte ensemble : on ne descend plus dans
   du vide, on regarde l'objet se defaire. */
.exploded {
  min-height: 240vh;
  position: relative;
  padding-block: 0;
}
@media (max-width: 1000px) { .exploded { min-height: 0; padding-block: var(--space-2xl); } }

.exploded__inner {
  position: sticky;
  top: 0;
  /* Hauteur exacte de la fenetre : le bloc colle doit tenir en entier, sinon
     son bas, bouton compris, reste hors d'atteinte pendant toute la
     sequence. */
  height: 100vh;
  display: grid;
  grid-template-columns: minmax(0, 5fr) minmax(0, 6fr);
  gap: var(--space-lg) var(--space-xl);
  align-items: center;
  padding-block: var(--space-md);
}
@media (max-width: 1000px) {
  .exploded__inner {
    position: static;
    min-height: 0;
    grid-template-columns: 1fr;
    gap: var(--space-lg);
    padding-block: 0;
  }
}

.exploded__stage { display: flex; justify-content: center; align-items: center; }

.exploded__canvas {
  width: 100%;
  max-width: 480px;
  height: auto;
  display: block;
}

.exploded__text { padding-block: 0; }

/* Ecran court : le bloc ne tiendrait pas dans la fenetre, on renonce au
   collage plutot que de rogner le texte. */
@media (max-height: 780px) {
  .exploded { min-height: 0; padding-block: var(--space-2xl); }
  .exploded__inner { position: static; height: auto; padding-block: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .exploded { min-height: 0; padding-block: var(--space-2xl); }
  .exploded__inner { position: static; height: auto; padding-block: 0; }
}


/* Le selecteur de devise en pied de section garde son ancienne allure */


/* --------------------------------------------------------------------------
   39. PANNEAU DE PREFERENCES : REGION ET DEVISE
   Un seul bouton dans le header, un petit panneau dessous. La region est un
   vrai lien (suivi par les moteurs), la devise un bouton memorise.
   -------------------------------------------------------------------------- */
.prefs { position: relative; }

.prefs__btn {
  display: inline-flex;
  align-items: center;
  gap: 0.55em;
  font-family: var(--font-sans-ext);
  font-size: var(--fs-eyebrow);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.16em;
  line-height: 1;
  padding: 0.72em 0;
  border: 0;
  background: transparent;
  color: var(--gk-mid-gray);
  transition: color var(--dur-fast) var(--ease);
}
.prefs__btn:hover,
.prefs__btn[aria-expanded="true"] { color: var(--gk-white); }
.prefs__btn[aria-expanded="true"] .nav__chev { transform: rotate(180deg); }

/* La modale : le site reste visible derriere, flou, sous un voile noir 60 % */
.prefs-modal {
  position: fixed;
  inset: 0;
  z-index: 300;
  display: grid;
  place-items: center;
  padding: 1.5rem;
  background: rgba(0, 0, 0, 0.6);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--dur-fast) var(--ease), visibility var(--dur-fast);
}
.prefs-modal.is-open { opacity: 1; visibility: visible; }

.prefs-modal__dialog {
  position: relative;
  width: min(25rem, 100%);
  background: var(--gk-near-black, #0a0a0a);
  border: 1px solid rgba(255, 255, 255, 0.14);
  padding: 2.2rem 2.2rem 2.4rem;
  transform: translateY(10px);
  transition: transform var(--dur-med, 0.4s) var(--ease);
}
.prefs-modal.is-open .prefs-modal__dialog { transform: none; }

.prefs-modal__close {
  position: absolute;
  top: 1.05rem;
  right: 1.05rem;
  display: grid;
  place-items: center;
  width: 2rem;
  height: 2rem;
  color: var(--gk-mid-gray);
  background: transparent;
  border: 0;
  transition: color var(--dur-fast) var(--ease);
}
.prefs-modal__close:hover { color: var(--gk-white); }
.prefs-modal__close svg { width: 12px; height: 12px; }

.prefs__label {
  font-family: var(--font-sans-ext);
  font-size: 0.5625rem;
  text-transform: uppercase;
  letter-spacing: 0.22em;
  color: var(--muted);
  margin: 0 0 0.55rem;
}
.prefs__group { display: grid; gap: 1px; margin-bottom: 1.1rem; }
.prefs__group:last-child { margin-bottom: 0; }
.prefs__group[role="group"] { grid-template-columns: 1fr 1fr; }

.prefs__opt {
  display: block;
  text-align: left;
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  letter-spacing: 0;
  text-transform: none;
  padding: 0.6rem 0.7rem;
  color: var(--gk-mid-gray);
  border: 1px solid rgba(255, 255, 255, 0.1);
  background: transparent;
  transition: color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
}
.prefs__opt:hover { color: var(--gk-white); background: rgba(255, 255, 255, 0.05); }
.prefs__opt.is-on,
.prefs__opt[aria-pressed="true"] {
  color: var(--gk-black);
  background: var(--gk-white);
  border-color: var(--gk-white);
}


/* --------------------------------------------------------------------------
   40. DISCIPLINE TYPOGRAPHIQUE
   Area Inktrap Extended est reservee aux eyebrows : tres courts, uppercase,
   traques. Partout ou le texte peut s'allonger (boutons, titres de cartes,
   badges), on redescend sur Area Inktrap regular.
   -------------------------------------------------------------------------- */
.btn,
.link-underline,
.nav__cta,
.plan__cta { font-family: var(--font-sans-ext); font-weight: 500; }

/* Exception : un lien pose dans une phrase garde la police de la phrase.
   L'Extended est reservee aux libelles courts, pas au fil du texte. */
.link-inline { font-family: inherit; font-weight: inherit; letter-spacing: inherit; }

h4, .h4,
.step__title { font-family: var(--font-sans); font-weight: 400; }

/* Le titre de la newsletter est une phrase, pas un label */


/* --------------------------------------------------------------------------
   41. LE CHAMP OUVERT DES ESPACES
   La cible ne se compte pas : les pages detaillees sont des exemples, la
   liste ouverte et la signature disent que le champ continue.
   -------------------------------------------------------------------------- */
/* ---- le mur des lieux ----------------------------------------------------
   Six lignes de noms qui traversent l'ecran, une sur deux a contresens. Le
   principe tient en une ligne : chaque piste porte deux fois la meme serie
   et se deplace d'exactement la moitie de sa largeur, donc d'une serie
   entiere. A la fin de l'animation, l'image est au pixel pres celle du
   depart, et la reprise ne se voit pas. Aucune mesure, aucun script.

   Les mots ne sont pas des liens et le bloc est cache aux lecteurs d'ecran :
   c'est une image faite de mots, pas une liste a parcourir. La phrase qui la
   precede dit deja ce qu'elle raconte. */
.mur {
  overflow: hidden;
  padding-block: var(--space-lg);
  -webkit-mask-image: linear-gradient(to right, transparent 0, #000 12%, #000 88%, transparent 100%);
  mask-image: linear-gradient(to right, transparent 0, #000 12%, #000 88%, transparent 100%);
}
.mur__ligne {
  display: flex;
  width: max-content;
  animation: gk-mur var(--duree, 70s) linear infinite;
}
.mur__ligne--inverse { animation-direction: reverse; }
.mur__serie { display: flex; flex: none; }
.mur__mot {
  display: block;
  padding-inline: 1.1ch;
  font-family: var(--font-serif);
  /* Le plancher vaut pour le telephone, ou 3.2vw ne fait que douze pixels :
     c'est lui qui donne sa taille au mur sur petit ecran, et il etait trop
     bas pour que les noms portent. */
  font-size: clamp(1.9rem, 3.2vw, 2.75rem);
  line-height: 1.55;
  letter-spacing: -0.015em;
  color: inherit;
  white-space: nowrap;
  transition: opacity 420ms var(--ease);
}
/* Le mot lu retient la lumiere, les autres s'effacent sans disparaitre.
   La condition porte sur un mot reellement survole, pas sur le bloc : entre
   deux lignes et dans les marges laterales, le curseur est dans le mur sans
   etre sur aucun nom, et tout s'eteignait d'un coup sans que rien ne
   s'allume.

   Les deux regles repartent du meme :has(), et la seconde ajoute :hover :
   c'est necessaire. Un :has() pese la specificite de son argument, donc
   .mur:has(.mur__mot:hover) .mur__mot vaut quatre classes, et le simple
   .mur .mur__mot:hover n'en valait que trois : le mot survole s'eteignait
   avec les autres. */
.mur:has(.mur__mot:hover) .mur__mot { opacity: 0.22; }
.mur:has(.mur__mot:hover) .mur__mot:hover { opacity: 1; }
@keyframes gk-mur {
  to { transform: translateX(-50%); }
}

.spaces-open { max-width: 52ch; margin-inline: auto; text-align: center; }
.spaces-open__words {
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  line-height: 2;
  color: var(--muted);
}
.spaces-open__line {
  font-family: var(--font-serif);
  font-size: var(--fs-h3);
  line-height: 1.2;
  letter-spacing: -0.015em;
  color: var(--gk-white);
  margin-top: var(--space-sm);
}

/* La fin de la liste : la phrase qui ferme le propos passe devant, la liste
   des autres lieux la suit en petit, et un lien mene a l'ensemble. Les deux
   lignes se disputaient l'attention en bas a gauche, dans deux typographies
   differentes et sans hierarchie. */
.worlds__fin {
  margin-top: var(--space-lg);
  display: grid;
  justify-items: end;
  text-align: right;
  gap: var(--space-sm);
}
/* La coda referme la liste en bas a droite, la ou la lecture s'acheve :
   le titre ouvre l'enumeration en haut a gauche, elle la conclut a
   l'oppose, et le geste la suit. Une seule et meme section. */
.worlds__silence {
  font-family: var(--font-serif);
  font-size: clamp(1.5rem, 2.4vw, 2rem);
  line-height: 1.2;
  letter-spacing: -0.015em;
  max-width: 30ch;
  color: var(--gk-charcoal);
}
.worlds__cta { margin-top: var(--space-2xs); }

.mega__outro {
  margin-top: 1.1rem;
  font-size: var(--fs-caption);
  font-family: var(--font-serif);
  font-style: normal;
  color: rgba(255, 255, 255, 0.55);
}

/* --------------------------------------------------------------------------
   42. FORMULES STUDIO : LISIBLES ET VIVANTES
   Un filtre eclaire la formule qui correspond au mode d'acquisition. La
   carte Pro (voie la plus courante) s'inverse en blanc. L'heritage
   "Tout X, et en plus" devient une chip, chaque carte a son action.
   -------------------------------------------------------------------------- */
.plan {
  position: relative;
  transition: transform var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease),
              opacity var(--dur-fast) var(--ease),
              box-shadow var(--dur-fast) var(--ease);
}
.plan:hover { transform: translateY(-3px); }

/* La formule des flottes multi-sites : inversee, c'est elle qu'on montre */
.section--light .plan--featured, .section--paper .plan--featured {
  background: var(--gk-black);
  border-color: var(--gk-black);
  color: var(--gk-off-white, #f5f5f4);
}
.section--light .plan--featured .plan__badge,
.section--paper .plan--featured .plan__badge { color: rgba(255, 255, 255, 0.6); }
.section--light .plan--featured .check-list li,
.section--paper .plan--featured .check-list li { color: rgba(255, 255, 255, 0.82); }
.section--dark .plan--featured {
  background: var(--gk-white);
  border-color: var(--gk-white);
  color: var(--gk-black);
}
.section--dark .plan--featured .plan__badge { color: rgba(0, 0, 0, 0.55); }
.section--dark .plan--featured .check-list li { color: rgba(0, 0, 0, 0.8); }

/* Une ligne, pas une pastille encadree : le filet lui donnait l'air d'un
   champ de saisie vide au milieu de la carte. */
.plan__inherit {
  font-family: var(--font-sans);
  font-size: var(--fs-caption);
  color: var(--muted);
  border-top: 1px solid rgba(128, 128, 128, 0.22);
  padding-top: var(--space-xs);
}
.plan--featured .plan__inherit { color: inherit; opacity: 0.7; }

.plan__cta {
  margin-top: auto;
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  color: inherit;
  border-top: 1px solid rgba(128, 128, 128, 0.25);
  padding-top: var(--space-xs);
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  transition: letter-spacing var(--dur-fast) var(--ease);
}
.plan__cta span { transition: transform var(--dur-fast) var(--ease); display: inline-block; }
.plan__cta:hover span { transform: translateX(4px); }

/* Panneau Spaces : la page hub en grand, les sous-pages en liens egaux */
.mega__inner--feature {
  display: grid;
  grid-template-columns: minmax(0, 3fr) minmax(0, 5fr) minmax(0, 4fr);
  gap: var(--space-xl);
  align-items: start;
}
.mega__card--feature { display: flex; flex-direction: column; }
.mega__card--feature .mega__thumb { aspect-ratio: 16 / 10; }
.mega__card--feature .mega__name {
  font-size: var(--fs-h3);
}
.mega__inner--feature .mega__links { margin-top: 0; }
.mega__inner--feature .mega__outro { margin-top: 1.4rem; }


/* Les ajouts de chaque formule : marques d'un +, pas d'une coche */
.check-list--plus li.plus { display: flex; gap: 0.6rem; align-items: baseline; }
.plus__mark {
  font-family: var(--font-serif);
  font-size: 1.1em;
  line-height: 1;
  flex: none;
}


/* --------------------------------------------------------------------------
   43. LABELS FLOTTANTS
   Le libelle attend a la place du texte ; au focus ou une fois rempli,
   il monte et devient le titre du champ. Les selects gardent leur titre
   fixe : ils ont toujours une valeur affichee.
   -------------------------------------------------------------------------- */
.field:has(:where(input[placeholder], textarea[placeholder])) {
  position: relative;
  padding-top: 1.3rem;
  gap: 0;
}
/* Un libelle reste un libelle, en bas comme en haut : meme fonte, meme
   capitale, meme interlettrage. Il changeait de famille et de casse en
   montant, et l'on croyait lire deux mots differents. Seuls la taille, la
   place et la couleur bougent. */
.field:has(:where(input[placeholder], textarea[placeholder])) > label {
  position: absolute;
  left: 0;
  top: 2rem;
  font-family: var(--font-sans-ext);
  /* En capitales interlettrees, un libelle prend beaucoup plus de place qu'en
     bas de casse : au corps du texte courant il ecrasait le champ. */
  font-size: 0.6875rem;
  text-transform: uppercase;
  letter-spacing: 0.16em;
  color: var(--muted);
  pointer-events: none;
  transition: top var(--dur-fast) var(--ease), font-size var(--dur-fast) var(--ease),
              color var(--dur-fast) var(--ease);
}
.field:has(input[placeholder]):focus-within > label,
.field:has(textarea[placeholder]):focus-within > label,
.field:has(input[placeholder]:not(:placeholder-shown)) > label,
.field:has(textarea[placeholder]:not(:placeholder-shown)) > label {
  top: 0 !important;
  font-size: 0.625rem !important;
}
/* Le telephone porte un selecteur d'indicatif, qui affiche toujours quelque
   chose : son libelle n'a aucune raison d'attendre a la place du texte, il
   s'y poserait par-dessus. Il prend donc sa place haute tout de suite. */
.field:has(.tel) > label { top: 0 !important; font-size: 0.625rem !important; }
.field:has([placeholder]):focus-within > label { color: var(--gk-white); }
.section--light .field:has([placeholder]):focus-within > label { color: var(--gk-black); }

/* --------------------------------------------------------------------------
   44. LE FOOTER
   La newsletter vit dans un cadre Ginko : une baguette fine, un
   passe-partout, le contenu comme un tirage. En pied de page, le wordmark
   geant tronque se remplit d'encre a l'apparition, lettre apres lettre.
   -------------------------------------------------------------------------- */
.site-footer { overflow: hidden; padding-bottom: 0; }


/* --------------------------------------------------------------------------
   45. L'INTRO
   La video hero en plein ecran a l'arrivee sur le site, une fois par
   session. Un pourcentage de chargement, le son au choix, un passage
   possible. Le site apparait quand la video se termine.
   -------------------------------------------------------------------------- */
.intro {
  position: fixed;
  inset: 0;
  z-index: 500;
  /* Un rideau, pas une scene : noir tant que la video ne joue pas, puis
     transparent au-dessus d'elle. La seule video de la page est celle du
     hero, qui vit dessous, plein ecran tant que la page est verrouillee. */
  background: var(--gk-black);
  transition: opacity 0.9s var(--ease), visibility 0.9s,
              background-color 0.9s var(--ease);
  /* Toute la surface du film active le son : le curseur doit le dire, sans
     quoi l'invitation propose un geste que rien n'annonce. Des que le son
     est choisi, plus rien ne se clique ici et la main se retire. */
  cursor: pointer;
}
.intro.son-donne { cursor: default; }
.intro__ctl { cursor: pointer; }
.intro.is-playing { background: transparent; }
.intro.is-done { opacity: 0; visibility: hidden; pointer-events: none; }
.intro__video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 0.8s var(--ease);
}
.intro.is-playing .intro__video { opacity: 1; }
html.gk-rideau .site-header,
html.gk-rideau .hero__inner { opacity: 0; pointer-events: none; }

/* Le rideau ci-dessus est pose par le script, qui ne tourne qu'une fois le
   document analyse : entre le premier rendu et lui, l'en-tete paraissait,
   puis s'effacait. Un battement, mais bien visible a l'arrivee.
   L'intro etant ecrite avant ses voisins dans le corps, on peut les masquer
   des le premier rendu, en CSS seul, sans attendre quoi que ce soit. La
   regle se libere d'elle-meme quand l'intro se termine (is-done), puis
   l'element disparait. Sans JavaScript, le <noscript> de la page rend la
   main : sinon l'en-tete resterait masque pour toujours. */
.intro:not(.is-done) ~ .site-header,
.intro:not(.is-done) ~ main .hero__inner {
  /* animation: none est ce qui compte ici. L'en-tete porte une animation
     d'arrivee (gk-entree), et une animation l'emporte sur une regle
     ordinaire : pendant ses 480 ms elle ramenait l'opacite a 1, donc
     l'en-tete paraissait, puis disparaissait d'un coup en rendant la main.
     C'etait ca, le clignotement. Il n'a rien a annoncer tant que le rideau
     est baisse : on coupe l'animation, et l'opacite tient. */
  animation: none;
  opacity: 0;
  pointer-events: none;
}

.hero__inner { transition: opacity 900ms var(--ease); }
.intro.is-ready .intro__pct { opacity: 0; }

/* La marque, posee avant tout reseau : le premier ecran n'est jamais un
   noir vide. Elle respire doucement le temps du chargement, puis s'efface
   des que la video se montre. */
/* La marque entre en fondu, respire par son enfant, et sort en fondu :
   deux animations qui ne se disputent jamais la meme opacite, donc aucun
   saut ni a l'arrivee ni quand la video prend le relais. */
.intro__marque {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  z-index: 1;
  margin: 0;
  font-family: var(--font-sans-ext);
  font-size: 0.9375rem;
  font-weight: 500;
  letter-spacing: 0.5em;
  text-indent: 0.5em;
  text-transform: uppercase;
  color: var(--gk-white);
  text-shadow: 0 1px 14px rgba(0, 0, 0, 0.9);
  animation: gk-marque-entre 1100ms var(--ease) 150ms both;
  pointer-events: none;
}
.intro__marque span {
  display: block;
  animation: gk-respire 2.8s ease-in-out 1250ms infinite;
}
.intro.is-playing .intro__marque { animation: gk-marque-sort 800ms var(--ease) both; }
@keyframes gk-marque-entre { from { opacity: 0; } to { opacity: 1; } }
@keyframes gk-marque-sort { from { opacity: 1; } to { opacity: 0; } }
@keyframes gk-respire {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.55; }
}
@media (prefers-reduced-motion: reduce) {
  .intro__marque, .intro__marque span { animation: none; }
  .intro.is-playing .intro__marque { opacity: 0; }
}

/* L'invitation au son, posee a meme le film.

   Pas une fenetre, pas de panneau : un haut-parleur au trait et un mot, qui
   laissent tout voir derriere. Elle suit le curseur avec un temps de retard,
   comme une etiquette qu'on promene, et se tient legerement sous lui pour ne
   jamais passer dessous. Sa position vient du script, en transform : c'est la
   seule chose qui bouge, donc rien ne declenche de recalcul de mise en page.
   Le clic se prend sur toute la surface du film, pas sur ces quelques mots :
   une cible qui fuit le curseur serait injouable. */
.intro__invite {
  position: absolute;
  /* Au repos, c'est le CSS qui centre : aucune mesure a prendre, donc rien
     qui puisse se tromper si la fenetre n'a pas encore sa taille. Le script
     ne reprend la main qu'au premier vrai mouvement de souris, avec des
     coordonnees qui viennent de l'evenement lui-meme. */
  left: 50%;
  top: calc(50% + 4.25rem);
  transform: translate(-50%, -50%);
  z-index: 3;
  display: inline-flex;
  align-items: center;
  gap: 0.7rem;
  padding: 0.6rem 0.2rem;
  border: 0;
  background: none;
  cursor: pointer;
  color: var(--gk-white);
  font-family: var(--font-sans-ext);
  font-size: 0.625rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.22em;
  text-indent: 0.22em;
  white-space: nowrap;
  text-shadow: 0 1px 14px rgba(0, 0, 0, 0.9), 0 0 34px rgba(0, 0, 0, 0.7);
  opacity: 0;
  transition: opacity 900ms var(--ease);
  will-change: transform;
  pointer-events: none; /* le film entier prend le clic */
}
.intro__invite.suit-la-souris { left: 0; top: 0; }
.intro__invite.est-la { opacity: 1; }
/* Au clic elle s'en va sans rien dire, et plus vite qu'elle n'est venue :
   la reponse, c'est le son qui arrive. Elle continue de suivre la souris
   pendant qu'elle s'efface, sinon elle se figerait en plein vol. */
.intro__invite.est-parti { opacity: 0; transition-duration: 420ms; }

.intro__invite-icone { display: block; width: 1.05rem; height: 1.05rem; }
.intro__invite-icone svg {
  width: 100%; height: 100%;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
  filter: drop-shadow(0 1px 10px rgba(0, 0, 0, 0.85));
}
/* Les ondes battent l'une apres l'autre : c'est ce battement qui dit qu'il y
   a quelque chose a entendre, sans rien recouvrir. */
.intro__onde {
  opacity: 0.25;
  animation: gk-onde 2.6s ease-in-out infinite;
  animation-delay: calc(var(--i) * 420ms);
}
@keyframes gk-onde {
  0%, 62%, 100% { opacity: 0.2; }
  28% { opacity: 1; }
}


@media (prefers-reduced-motion: reduce) {
  .intro__onde { animation: none; opacity: 0.85; }
  .intro__invite { transition-duration: 1ms; }
}

.intro__bar {
  position: absolute;
  left: clamp(1.5rem, 5vw, 4rem);
  right: clamp(1.5rem, 5vw, 4rem);
  bottom: clamp(1.3rem, 4vh, 2.6rem);
  display: flex;
  justify-content: space-between;
  align-items: baseline;
}
.intro__ctl {
  font-family: var(--font-sans-ext);
  font-size: 0.6875rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--gk-white);
  background: transparent;
  border: 0;
  padding: 0.55rem 0;
  /* La video peut etre claire sous les commandes : une ombre portee les
     detache sans ajouter de cadre. */
  text-shadow: 0 1px 14px rgba(0, 0, 0, 0.9), 0 0 34px rgba(0, 0, 0, 0.7);
  transition: opacity var(--dur-fast) var(--ease);
}
.intro__ctl:hover { opacity: 0.72; }

/* Deux voiles d'encre en pied d'ecran, sous les commandes */
.intro::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 34vh;
  pointer-events: none;
  background: linear-gradient(to top,
    rgba(0, 0, 0, 0.92) 0%,
    rgba(0, 0, 0, 0.55) 42%,
    rgba(0, 0, 0, 0) 100%);
}
.intro__bar { z-index: 2; }
.intro__pct {
  z-index: 2;
  text-shadow: 0 1px 14px rgba(0, 0, 0, 0.9);
}

/* --------------------------------------------------------------------------
   46. INK, NOT LIGHT : DANS L'ENCRE
   Les capsules en fond de section, pleine largeur, voilees pour la
   lisibilite. Le texte est pose dessus, l'image respire en parallaxe.
   -------------------------------------------------------------------------- */
.ink-hero {
  position: relative;
  overflow: hidden;
  min-height: 88vh;
  display: flex;
  align-items: center;
  padding-block: var(--space-xxl, 8rem);
  background: var(--gk-black);
  color: var(--gk-white);
}
.ink-hero__bg { position: absolute; inset: -10% 0; z-index: 0; }
.ink-hero__bg img,
.ink-hero__video {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.ink-hero__veil {
  position: absolute;
  inset: 0;
  background:
    linear-gradient(90deg, rgba(0, 0, 0, 0.86) 0%, rgba(0, 0, 0, 0.55) 48%, rgba(0, 0, 0, 0.2) 100%),
    linear-gradient(0deg, rgba(0, 0, 0, 0.55) 0%, rgba(0, 0, 0, 0) 34%);
}
.ink-hero__content {
  position: relative;
  z-index: 1;
  width: 100%;
  display: grid;
  grid-template-columns: minmax(0, 5fr) minmax(0, 6fr);
  gap: var(--space-xl);
  align-items: start;
}
@media (max-width: 900px) {
  .ink-hero__content { grid-template-columns: 1fr; gap: var(--space-lg); }
}

/* Version chapeau de page : elle passe sous la barre fixe, et son intertitre
   accompagne le titre dans la meme colonne. */
.ink-hero--page { padding-top: calc(var(--space-2xl) + 5rem); }
.ink-hero__tete .eyebrow { color: rgba(255, 255, 255, 0.6); margin-bottom: var(--space-sm); }
.ink-hero__tete .ink-hero__title { margin: 0; }

.ink-hero__prose {
  max-width: 56ch;
  align-self: start;
  margin-top: clamp(3.5rem, 10vh, 8rem);
  font-size: 0.8125rem;
}
.ink-hero__prose p + p { margin-top: 1.4em; }
.ink-hero__prose p { color: rgba(255, 255, 255, 0.82); font-size: 0.8125rem; line-height: 1.75; }
/* Le lien de fin se detache de la prose : c'est une action, pas une phrase */
.ink-hero__prose p:has(.link-underline) { margin-top: var(--space-md); }
/* Le bouton n'appartient pas au paragraphe : il lui faut son propre recul.
   Le selecteur reprend le conteneur, sinon ".ink-hero__prose p + p" le
   remporte -- deux elements pesent plus qu'un -- et le bouton se retrouve a
   19,5 px, colle au texte comme une phrase de plus. */
.ink-hero__prose p.ink-hero__cta { margin-top: 2.5rem; }


/* --------------------------------------------------------------------------
   47. ACCESSIBILITE AA : FOCUS VISIBLE ET CONTRASTES
   Ajouts seulement, aucune regle precedente n'est reecrite : ce qui suit
   gagne par la cascade ou par la specificite. Un seul anneau de focus pour
   tout le site : 2px, decale de 3px, bords droits comme le reste. L'anneau
   se pose autour de l'element, donc sur le fond de la section : sa couleur
   suit la section (blanc sur sombre, noir sur clair), pas l'element.
   -------------------------------------------------------------------------- */

/* Renforce l'anneau global de la section 3 : 1px currentColor etait trop
   fin, et gris sur les liens attenues (.crumbbar, .footer-nav, --muted). */
:focus-visible {
  outline: 2px solid var(--gk-white);
  outline-offset: 3px;
}

/* Sections claires : l'anneau passe a l'encre noire. */
.section--light :focus-visible,
.section--paper :focus-visible {
  outline-color: var(--gk-black);
}

/* Les champs n'ont pas d'anneau : l'encadre casse la finesse du trait.
   Le focus se signale par le soulignement qui passe au blanc et par le
   libelle qui monte. Le trait garde son epaisseur : il l'a longtemps
   doublee, et la ligne se mettait a peser au lieu de s'eclairer. */
.field input:focus-visible,
.field select:focus-visible,
.field textarea:focus-visible {
  outline: none;
  border-bottom-color: var(--gk-white);
}
.section--light .field input:focus-visible,
.section--light .field select:focus-visible,
.section--light .field textarea:focus-visible,
.section--paper .field input:focus-visible,
.section--paper .field select:focus-visible,
.section--paper .field textarea:focus-visible {
  border-bottom-color: var(--gk-black);
}

/* Le lien d'evitement et les elements sur fond blanc dans une page noire
   (CTA du header, option active de la modale) : l'anneau decale tombe sur
   le fond sombre autour, le blanc global convient. Rien a surcharger. */

/* Contraste : les commandes de l'intro flottent sur la video, dont certains
   plans sont tres clairs. On monte l'encre (0.72 vers 0.85) et on pose une
   ombre discrete qui garantit la lecture quel que soit le plan. */
.intro__ctl {
  color: rgba(255, 255, 255, 0.85);
  text-shadow: 0 1px 10px rgba(0, 0, 0, 0.55);
}

/* Contraste : la legende du bandeau encre etait a 0.5 d'opacite sur une
   image voilee, environ 5.3:1 sur noir pur mais sans garantie sur les zones
   les moins voilees. 0.72 tient AA avec de la marge sans changer le ton. */


/* --------------------------------------------------------------------------
   48. SPECS SANS CADRES
   Les grilles de caracteristiques respirent : pas de cellules, pas de
   filets, la valeur en serif un peu plus grande. Elles apparaissent en un
   seul fondu, sans cascade ni glissement.
   -------------------------------------------------------------------------- */
.specs [data-reveal],
.specs[data-reveal] {
  transform: none !important;
  transition-delay: 0ms !important;
  transition-duration: 0.9s;
}


/* --------------------------------------------------------------------------
   50. LA FICHE TECHNIQUE
   /specifications/ se lit comme une notice : groupes en deux colonnes,
   rangees libelle / valeur compactes, filets a peine poses. Chaque groupe
   apparait en un seul fondu.
   -------------------------------------------------------------------------- */
.sheet { border-top: 1px solid rgba(255, 255, 255, 0.16); }
.sheet__group {
  display: grid;
  grid-template-columns: minmax(0, 4fr) minmax(0, 8fr);
  gap: var(--space-sm) var(--space-xl);
  padding-block: var(--space-md);
  border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
@media (max-width: 780px) { .sheet__group { grid-template-columns: 1fr; } }
.sheet__title {
  font-family: var(--font-sans-ext);
  font-size: var(--fs-eyebrow);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--muted);
  padding-top: 0.6rem;
}
.sheet__rows { margin: 0; }
.sheet__row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--space-sm);
  padding-block: 0.6rem;
}
.sheet__row + .sheet__row { border-top: 1px solid rgba(255, 255, 255, 0.07); }
.sheet__row dt { color: var(--muted); font-size: var(--fs-body); }
.sheet__row dd { font-size: var(--fs-body); color: rgba(255, 255, 255, 0.92); }
.sheet__group[data-reveal] { transform: none; transition-duration: 0.9s; }
.sheet__group[data-reveal].is-visible { transform: none; }

/* Cartes partenaires : le logo suffit, pas de nom au survol */
.ginko-hovercards .card-title { display: none; }


/* --------------------------------------------------------------------------
   52. FOOTER, QUATRIEME LECTURE : LA COMPOSITION DE STUDIO
   Trois zones sur une ligne : newsletter typographique, navigation
   serree, cartouche atelier avec l'heure de Paris. En cloture, une fine
   trame d'encre qui s'imprime. Rien ne crie.
   -------------------------------------------------------------------------- */


.footer-legal a { color: inherit; transition: color var(--dur-fast) var(--ease); }
.footer-legal a:hover { color: var(--gk-white); }


/* --------------------------------------------------------------------------
   53. FOOTER, REPRISE DE L'ORIGINAL : DEUX MATIERES
   L'encre a gauche, le papier a droite. Le panneau clair de la newsletter
   posé sur le noir : c'est la signature du footer d'origine. Il deborde
   legerement vers le haut, comme une feuille glissee sur la page.
   -------------------------------------------------------------------------- */

.site-footer { padding: 0; overflow: visible; }


.footer-legal a { color: inherit; transition: color var(--dur-fast) var(--ease); }
.footer-legal a:hover { color: var(--gk-white); }

/* Le papier : le panneau clair, seule zone lumineuse du pied de page */


/* --------------------------------------------------------------------------
   54. FOOTER PLEIN ECRAN
   Le footer occupe un ecran entier et absorbe la derniere invitation. Un
   cadre Ginko en fond sous un voile d'encre, la phrase du mur au centre,
   et quatre zones indexees en pied, comme le sommaire d'un dossier.
   -------------------------------------------------------------------------- */

.footer-full {
  position: relative;
  min-height: 100svh;
  display: flex;
  overflow: hidden;
  padding: 0;
  background: var(--gk-black);
}
.footer-full__bg { position: absolute; inset: 0; z-index: 0; }
.footer-full__bg img { width: 100%; height: 100%; object-fit: cover; opacity: 0.5; }
.footer-full__bg::after {
  content: "";
  position: absolute;
  inset: 0;
  /* L'image se devoile derriere l'invitation, puis s'efface sous le plan
     du site, qui doit rester parfaitement lisible. */
  background:
    linear-gradient(180deg,
      rgba(0, 0, 0, 0.55) 0%,
      rgba(0, 0, 0, 0.3) 24%,
      rgba(0, 0, 0, 0.48) 46%,
      rgba(0, 0, 0, 0.9) 62%,
      rgba(0, 0, 0, 0.97) 78%),
    linear-gradient(90deg, rgba(0, 0, 0, 0.42) 0%, rgba(0, 0, 0, 0.08) 50%, rgba(0, 0, 0, 0.42) 100%);
}
.footer-full__inner {
  position: relative;
  z-index: 1;
  width: min(100% - 3rem, var(--wrap));
  margin-inline: auto;
  padding-block: clamp(3rem, 7vh, 5rem) clamp(1.2rem, 3vh, 2rem);
  display: grid;
  grid-template-rows: 1fr auto auto;
  gap: clamp(1.6rem, 4vh, 3rem);
  align-items: center;
}

/* La phrase du mur, au centre de l'ecran */
.footer-say {
  text-align: center;
  align-self: center;
  padding-block: clamp(3rem, 8vh, 6rem);
}
.footer-say__title {
  font-family: var(--font-serif);
  font-size: clamp(1.9rem, 4.2vw, 3.4rem);
  line-height: 1.08;
  letter-spacing: -0.02em;
  max-width: 20ch;
  margin-inline: auto;
}

/* Les zones indexees, comme un sommaire */
.footer-index {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: var(--space-sm);
  align-items: start;
  /* Le sommaire respire autant au-dessus qu'en dessous, sous un filet
     qui separe l'invitation du plan du site. */
  margin-block: clamp(3rem, 8vh, 6rem);
  padding-top: clamp(3rem, 8vh, 6rem);
  border-top: 1px solid rgba(255, 255, 255, 0.16);
}
@media (max-width: 1100px) { .footer-index { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 700px)  { .footer-index { grid-template-columns: 1fr 1fr; } }
@media (max-width: 440px)  { .footer-index { grid-template-columns: 1fr; } }

.fz__label {
  font-family: var(--font-sans-ext);
  font-size: 0.5625rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--gk-white);
  margin-bottom: 0.9rem;
}
.fz__list { list-style: none; padding: 0; margin: 0; display: grid; gap: 0.18rem; }
.fz__list a, .fz__list li {
  font-size: 0.75rem;
  line-height: 1.75;
  color: rgba(255, 255, 255, 0.74);
  transition: color var(--dur-fast) var(--ease);
}
.fz__list a:hover { color: var(--gk-white); }

.fz--news { grid-column: span 2; }
@media (max-width: 1100px) { .fz--news { grid-column: span 3; } }
@media (max-width: 700px)  { .fz--news { grid-column: span 2; } }
@media (max-width: 440px)  { .fz--news { grid-column: span 1; } }
.fz__intro { font-size: 0.8125rem; color: var(--gk-mid-gray); max-width: 34ch; }
.fz__form {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: var(--space-sm);
  align-items: end;
  margin-top: var(--space-sm);
}
.fz__form [data-form-status] { grid-column: 1 / -1; margin: 0; }
.fz__consent { margin-top: 0.7rem; max-width: 42ch; font-size: 0.6875rem; }

.footer-close {
  display: flex;
  justify-content: space-between;
  gap: var(--space-sm);
  flex-wrap: wrap;
  padding-top: var(--space-sm);
  border-top: 1px solid rgba(255, 255, 255, 0.1);
}
.footer-legal a { color: inherit; transition: color var(--dur-fast) var(--ease); }
.footer-legal a:hover { color: var(--gk-white); }

/* Le footer plein ecran doit tenir dans l'ecran, y compris sur les
   hauteurs courtes : l'echelle suit la hauteur de la fenetre. */
.footer-say__title { font-size: clamp(1.75rem, 3.4vw, 2.5rem); }  /* 40 px au maximum */

@media (max-height: 860px) {
  .footer-say__title { max-width: 24ch; }
  .fz__list a, .fz__list li { line-height: 1.6; }
  .fz__intro { display: none; }
  .fz__consent { font-size: 0.625rem; max-width: 46ch; }
}

/* Le bloc central du footer : la section Let's begin au complet */
.footer-say__eyebrow { margin-bottom: clamp(2rem, 4.5vh, 3.2rem); }
.footer-say__meta {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: 0.8rem clamp(1.5rem, 4vw, 3.5rem);
  margin-top: clamp(2.4rem, 5.5vh, 3.8rem);
  font-size: var(--fs-body);
  color: var(--gk-mid-gray);
}
.footer-say__meta a, .footer-say__meta span {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  transition: color var(--dur-fast) var(--ease);
}
.footer-say__meta img { opacity: 0.6; }
.footer-say__meta a:hover { color: var(--gk-white); }
.footer-say__actions {
  display: flex;
  justify-content: center;
  align-items: center;
  flex-wrap: wrap;
  gap: clamp(1rem, 3vw, 2.5rem);
  margin-top: clamp(2.4rem, 5.5vh, 3.8rem);
}


/* Le footer porte de nouveau le bloc Let's begin : un seul element,
   donc l'image de fond court sous l'ensemble, sans rupture. */
.footer-full { min-height: 100svh; }
.footer-full__inner { grid-template-rows: 1fr auto auto; }
/* L'index reste compact : l'interlettrage du libelle ecarterait les
   crochets de la lettre. */
.fz__label span {
  color: var(--muted);
  margin-right: 0.7em;
  letter-spacing: 0;
}


/* --------------------------------------------------------------------------
   55. LES FINITIONS
   Un seul recit, centre : quatre bois compris dans le prix, puis toute autre
   matiere sur demande. Les coins de cadre sont photographies sur fond blanc,
   la section est donc blanche elle aussi : seule la baguette flotte, sans
   vignette apparente. Bords droits, conformement a la direction artistique.
   -------------------------------------------------------------------------- */
.finishes__head {
  max-width: 46ch;
  margin-inline: auto;
  text-align: center;
}
.finishes__head .eyebrow { margin-bottom: var(--space-xs); }
.finishes__head .lead { margin-top: var(--space-xs); margin-inline: auto; }

/* Les quatre essences */
.swatches {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--space-sm);
  max-width: 880px;
  margin: var(--space-lg) auto 0;
  padding: 0;
  list-style: none;
}
@media (max-width: 900px) { .swatches--five { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 720px) { .swatches { grid-template-columns: repeat(2, 1fr); gap: var(--space-xs); } }

.swatch {
  display: grid;
  justify-items: center;
  text-align: center;
  margin: 0;
}
.swatch img {
  width: 100%;
  height: auto;
  aspect-ratio: 386 / 380;
  object-fit: cover;
  /* Les coins sont photographies sur fond blanc. Le produit fait disparaitre
     ce blanc quel que soit le fond de section : seule la baguette flotte,
     sans vignette rapportee. Les teintes sont toutes plus sombres que le
     papier, elles traversent le melange sans se deformer. */
  mix-blend-mode: multiply;
  transition: transform var(--dur-fast) var(--ease);
}
.swatch:hover img { transform: translateY(-4px); }
.swatch__name {
  font-family: var(--font-sans-ext);
  font-size: var(--fs-eyebrow);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.18em;
  line-height: 1.5;
  margin-top: 0.9rem;
}
.swatch__note {
  font-size: 0.6875rem;
  color: var(--muted);
  margin-top: 0.3rem;
}

/* Aller plus loin : plein cadre sur l'atelier, texte par-dessus.
   L'image est un <img> et non un background : elle profite ainsi du srcset
   et du chargement differe, ce qu'un background-image ne sait pas faire. */
.beyond {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  color: var(--gk-white);
  background: var(--gk-black);
  padding-block: var(--space-2xl);
}
.beyond__media { position: absolute; inset: 0; z-index: -2; }
.beyond__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 42%;
}
/* Deux voiles : un aplat qui garantit le contraste du texte, un degrade qui
   raccroche la section aux sections noires voisines par ses bords. */
.beyond::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(180deg,
    rgba(0,0,0,0.88) 0%, rgba(0,0,0,0.52) 34%,
    rgba(0,0,0,0.52) 66%, rgba(0,0,0,0.9) 100%);
}

.beyond__inner {
  max-width: 66ch;
  margin-inline: auto;
  text-align: center;
}
.beyond__inner .eyebrow { color: rgba(255, 255, 255, 0.62); }
.beyond__inner h2 { margin-top: var(--space-xs); }
.beyond__inner .lead {
  max-width: 44ch;
  margin: var(--space-sm) auto 0;
  color: rgba(255, 255, 255, 0.86);
}

/* Les matieres possibles, en petites capitales separees par un filet */
.beyond__mats {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.55rem 1.1rem;
  margin: var(--space-md) 0 0;
  padding: 0;
  list-style: none;
}
.beyond__mats li {
  position: relative;
  font-family: var(--font-sans-ext);
  font-size: var(--fs-eyebrow);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.18em;
  color: rgba(255, 255, 255, 0.72);
}
.beyond__mats li + li::before {
  content: "";
  position: absolute;
  left: -0.58rem;
  top: 0.25em;
  bottom: 0.15em;
  width: 1px;
  background: rgba(255, 255, 255, 0.28);
}
.beyond__cta { margin-top: var(--space-md); }


/* Le choix du son : deux etats cote a cote, l'actif souligne */
.intro__sound { display: inline-flex; align-items: baseline; gap: 0.5rem; }
/* L'icone de son du hero : posee sur la ligne du CTA, poussee a droite.
   Elle reste apres l'intro : le son se rallume quand on veut. */
.hero__son {
  margin-left: auto;
  padding: 0.55rem;
  border: 0;
  background: transparent;
  color: var(--gk-white);
  cursor: pointer;
  opacity: 0.75;
  transition: opacity var(--dur-fast) var(--ease);
  filter: drop-shadow(0 1px 10px rgba(0, 0, 0, 0.8));
}
.hero__son:hover { opacity: 1; }
.hero__son svg { display: block; width: 1.5rem; height: 1.5rem; }
.hero__son-ondes path { opacity: 0; transition: opacity 400ms var(--ease); }
.hero__son-barre { opacity: 1; transition: opacity 400ms var(--ease); }
.hero__son.est-actif .hero__son-ondes path { opacity: 1; }
.hero__son.est-actif .hero__son-barre { opacity: 0; }
.intro__sep {
  font-family: var(--font-sans-ext);
  font-size: 0.8125rem;
  color: rgba(255, 255, 255, 0.5);
  text-shadow: 0 1px 14px rgba(0, 0, 0, 0.9);
}
.intro__sound .intro__ctl { opacity: 0.6; }
.intro__sound .intro__ctl:hover { opacity: 0.85; }
.intro__sound .intro__ctl.is-on {
  opacity: 1;
  text-decoration: underline;
  text-underline-offset: 5px;
  text-decoration-thickness: 1px;
}


/* --------------------------------------------------------------------------
   56. L'INTRO : COMMANDES ET APPARITIONS
   Le libelle "son" reste neutre, seuls les deux etats se soulignent. Le
   compteur occupe le coin bas droit, et le bouton "passer" vient prendre
   sa place une fois le chargement acheve. Tout entre en douceur.
   -------------------------------------------------------------------------- */
.intro__sound { display: inline-flex; align-items: baseline; gap: 0.55rem; }
.intro__sound-label,
.intro__sep {
  font-family: var(--font-sans-ext);
  font-size: 0.6875rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--gk-white);
  text-shadow: 0 1px 14px rgba(0, 0, 0, 0.9);
}
.intro__sep { opacity: 0.45; font-weight: 400; }
.intro__sound .intro__ctl { opacity: 0.55; }
.intro__sound .intro__ctl:hover { opacity: 0.85; }
.intro__sound .intro__ctl.is-on {
  opacity: 1;
  text-decoration: underline;
  text-underline-offset: 5px;
  text-decoration-thickness: 1.5px;
}

/* Compteur et bouton "passer" partagent le meme coin */
.intro__end {
  position: relative;
  display: grid;
  justify-items: end;
  align-items: center;
  min-width: 6rem;
}
.intro__end > * { grid-area: 1 / 1; justify-self: end; margin: 0; }
.intro__pct {
  /* Le compteur et le bouton "passer" occupent la meme case de grille. Une
     fois efface le compteur restait cliquable et, avec son z-index, couvrait
     les trois quarts du bouton : le clic tombait dans le vide. Il n'a jamais
     rien a intercepter. */
  pointer-events: none;
  font-family: var(--font-sans-ext);
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  color: var(--gk-white);
  text-shadow: 0 1px 14px rgba(0, 0, 0, 0.9);
  font-variant-numeric: tabular-nums;
  transition: opacity 0.5s var(--ease), transform 0.5s var(--ease);
}
.intro.is-ready .intro__pct { opacity: 0; transform: translateY(-6px); }
.intro__skip {
  opacity: 0;
  transform: translateY(6px);
  pointer-events: none;
  transition: opacity 0.6s var(--ease) 0.15s, transform 0.6s var(--ease) 0.15s;
}
.intro.is-ready .intro__skip { opacity: 1; transform: none; pointer-events: auto; }
.intro__skip span { display: inline-block; transition: transform var(--dur-fast) var(--ease); }
.intro__skip:hover span { transform: translateX(4px); }

/* Entree en scene : les commandes montent doucement au premier affichage */
@keyframes introEntree {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: none; }
}
.intro__sound { animation: introEntree 1.1s var(--ease) 0.5s both; }
.intro__end   { animation: introEntree 1.1s var(--ease) 0.75s both; }
@media (prefers-reduced-motion: reduce) {
  .intro__sound, .intro__end { animation: none; }
}

/* --------------------------------------------------------------------------
   57. L'APERCU DES UNIVERS
   Au survol d'un univers, une vignette du lieu suit le curseur avec un
   leger retard, et se remplace quand on passe au suivant. Purement
   decoratif : la liste reste utilisable sans lui, et il ne s'affiche pas
   sur les appareils tactiles ni si les animations sont reduites.
   -------------------------------------------------------------------------- */
.worlds { position: relative; }
.worlds .world { position: relative; z-index: 1; }
.worlds__peek {
  transform-origin: 0 100%;
  position: fixed;
  top: 0;
  left: 0;
  z-index: 0;
  width: clamp(11.875rem, 17vw, 18.75rem);
  aspect-ratio: 3 / 2;
  overflow: hidden;
  pointer-events: none;
  opacity: 0;
  /* Le coin bas gauche de la vignette repose sur le curseur */
  transform: translate3d(0, -100%, 0) scale(0.94);
  transition: opacity 0.45s var(--ease), transform 0.45s var(--ease);
  will-change: transform;
}
.worlds__peek img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 0.35s var(--ease);
}
.worlds__peek.is-on { opacity: 1; transform: translate3d(0, -100%, 0) scale(1); }
.worlds__peek.is-on img { opacity: 1; }

@media (hover: none), (prefers-reduced-motion: reduce) {
  .worlds__peek { display: none; }
}


/* La composition de la section encre : le titre tient le haut a gauche,
   la prose descend a droite. La video occupe tout le fond. */
.ink-hero { align-items: stretch; }
.ink-hero__title {
  font-size: clamp(1.5rem, 2.4vw, 2.1rem);
  line-height: 1.16;
  letter-spacing: -0.02em;
  max-width: 14ch;
  align-self: start;
}
.ink-hero__prose p + p { margin-top: 1.5em; }

@media (max-width: 900px) {
  .ink-hero__title { max-width: 18ch; }
  .ink-hero__prose { align-self: start; margin-top: var(--space-md); }
}


/* La legende des capsules ferme la section, en bas a gauche, en retrait */
.ink-hero__content { position: relative; min-height: 62vh; }
.ink-hero__caption {
  grid-column: 1 / -1;
  justify-self: start;
  align-self: end;
  margin: 0;
  /* Une seule ligne : la legende ne doit pas se replier */
  max-width: none;
  white-space: nowrap;
  color: rgba(255, 255, 255, 0.42);
}
@media (max-width: 640px) {
  .ink-hero__caption { white-space: normal; max-width: 40ch; }
}
@media (max-width: 900px) { .ink-hero__caption { margin-top: var(--space-md); } }

/* --------------------------------------------------------------------------
   58. GINKO STUDIO : LE TEXTE AU CENTRE, LES CARTES AUTOUR
   Le discours tient le milieu, les cartes exportees de l'application
   flottent de part et d'autre. La typographie reste celle de Ginko
   Displays : seules les cartes viennent du logiciel.
   -------------------------------------------------------------------------- */
.studio {
  /* Un gris a peine plus soutenu que le blanc, et hors cameleon
     (data-cameleon="non") : la section garde son propre fond et se coupe
     net en haut comme en bas. C'est l'interlude Studio, pas la page. */
  background: var(--gk-off-white);
  overflow: hidden;
  /* La section occupe au moins un ecran : les cartes ont besoin de place
     pour deriver au defilement sans se marcher dessus. */
  min-height: 100svh;
  display: flex;
  align-items: center;
}
.studio__inner { position: relative; width: 100%; }

.studio__center {
  position: relative;
  z-index: 2;
  max-width: 44rem;
  margin-inline: auto;
  text-align: center;
  padding-block: clamp(2rem, 6vh, 5rem);
}
.studio__center .lead { margin-inline: auto; }
.studio__actions {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: var(--space-sm);
  margin-top: var(--space-lg);
}
.studio__note { margin-top: var(--space-md); color: var(--muted); }

/* Les cartes exportees de l'application, posees autour du texte */
.studio__card {
  position: absolute;
  z-index: 1;
  height: auto;
  filter: drop-shadow(0 24px 48px rgba(0, 0, 0, 0.55));
}
/* Sur fond clair, l'ombre s'allege : une carte posee, pas une carte qui pese. */
.section--light .studio__card { filter: drop-shadow(0 10px 22px rgba(0, 0, 0, 0.10)); }
.studio__card--floor   { width: 262px; top: 4%;  left: -5%; }
.studio__card--gallery { width: 15.375rem; top: 46%; left: -2%; }
.studio__card--lounge  { width: 15.375rem; top: 14%; right: -3%; }

@media (max-width: 1240px) {
  .studio__card--floor   { width: 216px; left: -8%; }
  .studio__card--gallery { width: 204px; left: -7%; }
  .studio__card--lounge  { width: 204px; right: -7%; }
}
/* Sous cette largeur les cartes chevaucheraient le texte : elles passent
   dessous plutot que de disparaitre. */
@media (max-width: 1000px) {
  .studio__inner { display: grid; gap: var(--space-lg); }
  .studio__center { order: -1; padding-block: 0; }
  .studio__card {
    position: static;
    width: 100%;
    max-width: 236px;
    justify-self: center;
  }
  .studio__card--floor { display: none; }
}



/* --------------------------------------------------------------------------
   59. LES CARTES VIVANTES
   Les cartes de Studio suivent le curseur en profondeur, et un trait
   pointille anime relie l'etage a la galerie : la donnee descend.
   -------------------------------------------------------------------------- */
/* L'inclinaison est portee par le transform du script, qui compose la
   translation du defilement et la rotation propre a chaque carte.
   L'image, elle, ne porte que le zoom au survol. */
.studio__card {
  display: block;
  will-change: transform;
  pointer-events: auto;
}
.studio__card img {
  display: block;
  width: 100%;
  height: auto;
  transition: transform 0.55s cubic-bezier(0.22, 0.61, 0.36, 1);
}
.studio__card:hover img { transform: scale(1.045); }
/* Etat de repli si le script ne tourne pas : les cartes restent penchees. */
.studio__card--floor   { transform: rotate(-5.5deg); }
.studio__card--gallery { transform: rotate(4.2deg); }
.studio__card--lounge  { transform: rotate(-3.8deg); }

/* Le fil de transmission entre les deux cartes de gauche */
.studio__wire {
  /* Le fil couvre toute la zone : son trace est recalcule au defilement,
     puisque les deux cartes se deplacent a des vitesses differentes. */
  position: absolute;
  inset: 0;
  z-index: 1;
  width: 100%;
  height: 100%;
  pointer-events: none;
  overflow: visible;
}
.studio__wire-path {
  stroke: rgba(255, 255, 255, 0.32);
}
/* Sur fond clair, le fil s'ecrit a l'encre. */
.section--light .studio__wire-path {
  stroke: rgba(0, 0, 0, 0.35);
  stroke-width: 1.2;
  stroke-dasharray: 5 7;
  animation: wireFlow 1.6s linear infinite;
}
@keyframes wireFlow { to { stroke-dashoffset: -24; } }

@media (max-width: 1000px) { .studio__wire { display: none; } }
@media (prefers-reduced-motion: reduce) {
  .studio__wire-path { animation: none; }
  .studio__card { transition: none; }
}

/* ==========================================================================
   Le site vivant : masques, aimants, cameleon
   Chaque geste repond a un geste du visiteur. Amplitudes courtes, courbes
   longues : Ginko ne bondit pas, Ginko se revele.
   ========================================================================== */

/* ---- les grands titres se tirent comme une epreuve -----------------------
   La ligne monte dans son propre cadre : le masque s'ouvre pendant que le
   texte se pose. La classe est donnee par le script aux h1/h2 reveles. */
/* Le cadre du masque : il clippe la ligne pendant sa montee, avec une
   marge interieure compensee pour ne pas rogner les deliés du serif.
   Transform pur, aucun clip-path : le geste passe partout. */
[data-reveal].gk-masque {
  transform: none;
  overflow: hidden;
  padding: 0.18em 0.12em;
  margin: -0.18em -0.12em;
}
.gk-masque .gk-ligne {
  display: block;
  transform: translateY(118%);
  transition: transform 0.9s var(--ease-reveal);
  transition-delay: var(--reveal-delay, 0ms);
}
.gk-masque.is-visible .gk-ligne { transform: none; }

/* ---- les images se decouvrent en reculant d'un cran ----------------------
   Un leger exces d'echelle qui se resorbe : le cadre recadre l'image.
   :where() garde la priorite basse : une figure qui a deja son propre
   mouvement (zoom au survol des usecases) n'est pas contrariee. */
:where(figure[data-reveal]) { overflow: hidden; }
:where(figure[data-reveal]) img {
  transform: scale(1.06);
  transition: transform 1.3s var(--ease-reveal);
  transition-delay: var(--reveal-delay, 0ms);
}
:where(figure[data-reveal].is-visible) img { transform: none; }

/* ---- l'en-tete s'efface a la descente, revient a la remontee ------------ */
.site-header {
  transition: background var(--dur-fast) var(--ease),
              padding var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease),
              transform 560ms var(--ease-reveal);
}
.site-header.est-cache { transform: translateY(-102%); }

/* ---- le cameleon : le fond de page fond d'une section a l'autre ----------
   Les sections a fond plein pretent leur couleur au body, qui fond de l'une
   a l'autre : plus de coupure nette entre le noir et le blanc. Le texte des
   sections visibles suit le mouvement, pour rester du bon cote du contraste. */
body.gk-cameleon { transition: background-color 900ms ease; }
body.gk-cameleon main section { transition: color 900ms ease; }
/* Une section qui a prete son fond au body se retrouve lue sur la couleur
   de la section voisine. Son texte suivait deja ; il manquait le gris des
   textes secondaires et le bouton plein, restes habilles pour leur fond
   d'origine. Un bouton blanc a texte noir devenait invisible sur une page
   passee au blanc, et l'intertitre gris avec lui. */
body.gk-cameleon.gk-clair main section.section--dark {
  color: var(--gk-charcoal);
  --muted: #68685f;
}
body.gk-cameleon:not(.gk-clair) main section.section--light,
body.gk-cameleon:not(.gk-clair) main section.section--paper {
  color: var(--gk-white);
  --muted: #909089;
}
body.gk-cameleon.gk-clair main section.section--dark .btn--primary {
  background: var(--gk-black);
  color: var(--gk-white);
  border-color: rgba(0, 0, 0, 0.25);
}
body.gk-cameleon.gk-clair main section.section--dark .btn--primary::before {
  background: var(--gk-white);
}
body.gk-cameleon.gk-clair main section.section--dark .btn--ghost {
  background: var(--gk-white);
  border-color: rgba(0, 0, 0, 0.25);
  color: var(--gk-charcoal);
}
body.gk-cameleon:not(.gk-clair) main section.section--light .btn--primary,
body.gk-cameleon:not(.gk-clair) main section.section--paper .btn--primary {
  background: var(--gk-white);
  color: var(--gk-black);
  border-color: rgba(255, 255, 255, 0.35);
}
body.gk-cameleon:not(.gk-clair) main section.section--light .btn--primary::before,
body.gk-cameleon:not(.gk-clair) main section.section--paper .btn--primary::before {
  background: var(--gk-black);
}

/* ---- les formulaires repondent au doigt ---------------------------------
   Un trait plein balaie le champ qui prend le focus ; le message d'envoi
   nait en fondu plutot que d'apparaitre sec. */
form .field { position: relative; }
form .field::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 520ms var(--ease-reveal);
  pointer-events: none;
}
form .field:focus-within::after { transform: scaleX(1); }

[data-form-status]:not(:empty) { animation: gk-fondu-texte 520ms var(--ease-reveal); }
@keyframes gk-fondu-texte {
  from { opacity: 0; transform: translateY(6px); }
}

/* ---- le pied de page se pose --------------------------------------------
   L'image du fond arrive un cran trop grande et se resorbe tres lentement
   quand l'invitation se revele : la page se termine sur un mouvement. */
.footer-full__bg { overflow: hidden; }
.footer-full__bg img {
  transform: scale(1.07);
  transition: transform 2.4s var(--ease-reveal);
}
.footer-full:has([data-reveal].is-visible) .footer-full__bg img { transform: none; }

@media (prefers-reduced-motion: reduce) {
  .gk-masque .gk-ligne { transform: none !important; transition: none; }
  :where(figure[data-reveal]) img { transform: none !important; transition: none; }
  .site-header.est-cache { transform: none; }
  .btn::before { transition: none; }
  /* Le mur ne defile plus : il se replie en un pave centre, et le
     second exemplaire de chaque serie disparait pour ne pas lire
     deux fois les memes lieux. */
  .mur { -webkit-mask-image: none; mask-image: none; }
  .mur__ligne { animation: none; width: auto; flex-wrap: wrap;
                justify-content: center; }
  .mur__serie:nth-child(2) { display: none; }
  .mur__serie { flex-wrap: wrap; justify-content: center; }
  .mur__mot { transition: none; }
  .carrousel__rang.gk-monte,
  .carrousel__rang.gk-descend { animation: none; }
  body.gk-cameleon,
  body.gk-cameleon main section { transition: none; }
  .footer-full__bg img { transform: none; transition: none; }
}

/* ---- l'en-tete suit le cameleon -----------------------------------------
   Quand le fond de page est au blanc, la barre passe au blanc elle aussi
   et son texte a l'encre, dans le meme fondu que la page. Le panneau
   deroulant et le menu mobile gardent leur propre traitement. */
body.gk-cameleon .site-header {
  transition: background 900ms ease,
              padding var(--dur-fast) var(--ease),
              border-color 900ms ease,
              transform 560ms var(--ease-reveal);
}
body.gk-clair .site-header { color: var(--gk-black); }
body.gk-clair .site-header.is-scrolled {
  background: rgba(255, 255, 255, 0.86);
  border-bottom-color: rgba(0, 0, 0, 0.08);
}
body.gk-cameleon .site-header .brand img { transition: filter 500ms ease; }
body.gk-clair .site-header .brand img { filter: invert(1); }
body.gk-cameleon .site-header .nav__link,
body.gk-cameleon .site-header .nav__social,
body.gk-cameleon .site-header .prefs__label,
body.gk-cameleon .site-header .lang-switch { transition: color 500ms ease; }
body.gk-clair .site-header .nav__link,
body.gk-clair .site-header .nav__social,
body.gk-clair .site-header .prefs__label,
body.gk-clair .site-header .lang-switch { color: var(--gk-black); }
body.gk-clair .site-header .nav__cta {
  background: var(--gk-black);
  color: var(--gk-white);
}
body.gk-clair .site-header .nav__cta { border-color: rgba(0, 0, 0, 0.25); }
body.gk-clair .site-header .burger span { background: var(--gk-black); }

/* ---- les cartes de prix se remplissent d'encre au survol -----------------
   Un voile noir balaie la carte de gauche a droite ; les textes passent en
   blanc et glissent d'un cran, en cascade, le prix prend un souffle.
   Sur les sections sombres, c'est le blanc qui remplit et l'encre qui ecrit. */
.price-card {
  position: relative;
  overflow: hidden;
  transition: border-color 640ms var(--ease-reveal);
}
/* Les cartes sont des liens vers la page tarifs : elles en gardent la
   parure du texte, et le balayage de survol dit deja le clic.
   Le display reste en colonne : passe en block, le lien perdait le gap de
   la carte et l'intertitre, le prix et la liste se touchaient. */
a.price-card {
  display: flex;
  flex-direction: column;
  color: inherit;
  text-decoration: none;
}
a.price-card:focus-visible {
  outline: 1px solid currentColor;
  outline-offset: 4px;
}
.price-card::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--gk-black);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 640ms var(--ease-reveal);
  pointer-events: none;
}
.price-card > * { position: relative; z-index: 1; }
.price-card:hover::before { transform: scaleX(1); }

.price-card .eyebrow,
.price-card .price,
.price-card .check-list li {
  transition: color 480ms ease, transform 640ms var(--ease-reveal);
}
.price-card .price { transform-origin: left center; }
.price-card:hover .eyebrow { color: var(--gk-mid-gray); transform: translateX(8px); }
.price-card:hover .price {
  color: var(--gk-white);
  transform: translateX(8px) scale(1.02);
  transition-delay: 40ms;
}
.price-card:hover .check-list li { color: var(--gk-white); transform: translateX(8px); }
.price-card:hover .check-list li:nth-child(1) { transition-delay: 90ms; }
.price-card:hover .check-list li:nth-child(2) { transition-delay: 140ms; }
.price-card:hover .check-list li:nth-child(3) { transition-delay: 190ms; }

.section--dark .price-card::before { background: var(--gk-white); }
.section--dark .price-card:hover .eyebrow { color: var(--gk-warm-gray); }
.section--dark .price-card:hover .price,
.section--dark .price-card:hover .check-list li { color: var(--gk-black); }

@media (prefers-reduced-motion: reduce) {
  .price-card::before { transition: none; }
  .price-card .eyebrow,
  .price-card .price,
  .price-card .check-list li { transition: color 480ms ease; transform: none !important; }
}

/* ---- les univers au doigt ------------------------------------------------
   Sur ecran tactile le survol n'existe pas : c'est la lecture qui choisit.
   La ligne qui traverse la zone de lecture se decale et s'encre, comme
   sous le pointeur, et la vignette du lieu se pose en bas a droite. Elle
   change de ligne en ligne et se retire avec la section. */
.world.est-lu { color: var(--gk-charcoal); padding-left: 0.6rem; }
.section--dark .world.est-lu { color: var(--gk-white); }
.world.est-lu i { opacity: 1; transform: translateX(0); }

/* Au doigt, l'apercu est tenu par l'ecran, pas par la liste.

   Il ne bouge pas quand l'entree active change : seule l'image qu'il
   montre change. Il se tient au centre et assez bas pour laisser voir
   l'entree active, qui se lit a 45 % de la hauteur de l'ecran -- d'ou le
   depart a 52 %. Il passe derriere les lignes (z-index 0 contre 1),
   comme au survol sur grand ecran. */
.worlds__peek--pose {
  display: block;
  position: fixed;
  top: 52vh;
  left: 50%;
  right: auto;
  bottom: auto;
  z-index: 0;
  width: min(58vw, 16rem);
  transform: none;
  translate: -50% 14px;
  opacity: 0;
  transition: opacity 480ms var(--ease-reveal),
              translate 480ms var(--ease-reveal);
}
.worlds__peek--pose.est-la { opacity: 1; translate: -50% 0; }
.worlds__peek--pose img { opacity: 1; }

/* ---- les logos au doigt --------------------------------------------------
   La carte lue rejoue son survol : memes etats que :hover, la scene
   canvas est relancee par le script via les evenements de souris. */
.hover-card.est-lu .icon-container { transform: scale(1.15); opacity: 1; }
.hover-card.est-lu .icon-container path,
.hover-card.est-lu .icon-container circle,
.hover-card.est-lu .icon-container rect,
.hover-card.est-lu .icon-container polygon,
.hover-card.est-lu .icon-container polyline,
.hover-card.est-lu .icon-container ellipse,
.hover-card.est-lu .icon-container line { stroke-width: 1.4px !important; }
.hover-card.est-lu .card-title { opacity: 1; }

/* ---- les menus suivent le cameleon --------------------------------------
   Panneau ouvert en phase claire : l'interieur passe au blanc, texte a
   l'encre. En phase sombre, rien ne change : le noir reste le noir. */
body.gk-clair .site-header:has(.nav__item--has-menu:focus-within),
body.gk-clair .site-header:has(.nav__item--has-menu.is-open) {
  background: var(--gk-white);
  border-bottom-color: rgba(0, 0, 0, 0.12);
}

/* le grand panneau */
body.gk-clair .mega {
  background: var(--gk-white);
  border-top-color: rgba(0, 0, 0, 0.12);
  border-bottom-color: rgba(0, 0, 0, 0.12);
}
body.gk-clair .mega__intro { color: var(--gk-warm-gray); }
body.gk-clair .mega__grid {
  background: rgba(0, 0, 0, 0.1);
  border-color: rgba(0, 0, 0, 0.1);
}
body.gk-clair .mega__card { background: var(--gk-white); }
body.gk-clair .mega__card:hover { background: var(--gk-off-white); }
body.gk-clair .mega__name { color: var(--gk-black); }
body.gk-clair .mega__desc { color: var(--gk-warm-gray); }
body.gk-clair .mega__arrow { color: var(--gk-black); }
body.gk-clair .mega__links a { color: rgba(0, 0, 0, 0.62); }
body.gk-clair .mega__links a:hover { color: var(--gk-black); }
body.gk-clair .mega__lead .link-underline { color: var(--gk-black); }

/* le menu mobile */
body.gk-clair .mobile-nav { background: var(--gk-white); }
body.gk-clair .mnav__item + .mnav__item { border-top-color: rgba(0, 0, 0, 0.12); }
body.gk-clair .mnav__link { color: var(--gk-black); }
body.gk-clair .mnav__toggle { color: rgba(0, 0, 0, 0.55); }
body.gk-clair .mnav__toggle[aria-expanded="true"] { color: var(--gk-black); }
body.gk-clair .mnav__panel a { color: rgba(0, 0, 0, 0.6); }
body.gk-clair .mnav__foot { border-top-color: rgba(0, 0, 0, 0.12); }
body.gk-clair .mnav__cta { background: var(--gk-black); color: var(--gk-white); }
body.gk-clair .mnav__cta:hover { background: var(--gk-charcoal); }
body.gk-clair .mnav__lang { color: rgba(0, 0, 0, 0.6); }
body.gk-clair .mnav__currency .prefs__opt { color: rgba(0, 0, 0, 0.45); }
body.gk-clair .mnav__currency .prefs__opt:hover,
body.gk-clair .mnav__currency .prefs__opt[aria-pressed="true"] { color: var(--gk-black); }

/* ---- le chemin du signal (page Player) ----------------------------------
   Trois relais au trait, relies par un fil pointille qui court : d'ou
   vient l'image, par ou elle passe, ou elle s'affiche. Le boitier n'a pas
   de photographie ; le trace en dit plus qu'un rendu. */
.relais {
  display: grid;
  grid-template-columns: 1fr auto 1fr auto 1fr;
  align-items: start;
  gap: var(--space-sm);
  max-width: 54rem;
  margin-inline: auto;
  text-align: center;
}
.relais__noeud { display: grid; gap: 0.5rem; justify-items: center; }
.relais__nom {
  font-family: var(--font-sans-ext);
  font-size: var(--fs-eyebrow);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.18em;
  margin-top: 0.6rem;
}
.relais__dit { font-size: 0.8125rem; color: var(--muted); max-width: 16ch; }

/* Les formes, au trait de 1px, dans l'esprit des croquis de l'atelier. */
.relais__forme {
  position: relative;
  display: grid;
  border: 1px solid currentColor;
  opacity: 0.85;
}
/* Studio : l'application, trois lignes posees comme un ordonnancement. */
.relais__forme--studio {
  width: 4.75rem;
  height: 3.5rem;
  padding: 0.625rem 0.75rem;
  gap: 0.375rem;
  align-content: center;
}
.relais__forme--studio span { height: 1px; background: currentColor; }
.relais__forme--studio span:nth-child(2) { width: 70%; }
.relais__forme--studio span:nth-child(3) { width: 45%; }
/* Le boitier : petit, epais, une diode discrete. */
.relais__forme--boitier {
  width: 2.875rem;
  height: 1.875rem;
  margin-top: 13px;
  border-width: 1.5px;
}
.relais__forme--boitier::after {
  content: "";
  position: absolute;
  right: 7px;
  top: 7px;
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: currentColor;
  animation: relais-diode 2.4s ease-in-out infinite;
}
@keyframes relais-diode { 0%, 100% { opacity: 1; } 50% { opacity: 0.2; } }
/* L'ecran du client : un cadre 16:9 sur son pied. */
.relais__forme--ecran {
  width: 84px;
  height: 3.125rem;
  padding: 0.5rem 0.625rem;
  gap: 5px;
  align-content: center;
}
.relais__forme--ecran span { height: 1px; background: currentColor; width: 60%; }
.relais__forme--ecran span:nth-child(2) { width: 80%; }
.relais__forme--ecran::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: -7px;
  width: 22px;
  height: 1px;
  background: currentColor;
  transform: translateX(-50%);
}

/* Le fil : un pointille qui court d'un relais au suivant. */
.relais__fil {
  align-self: start;
  margin-top: 1.75rem;
  min-width: 3rem;
  height: 1px;
  background: repeating-linear-gradient(90deg,
    currentColor 0 5px, transparent 5px 12px);
  opacity: 0.5;
  animation: relais-court 1.4s linear infinite;
}
@keyframes relais-court { to { background-position: -24px 0; } }

@media (max-width: 640px) {
  .relais { grid-template-columns: 1fr; justify-items: center; gap: var(--space-xs); }
  .relais__fil {
    min-width: 0;
    width: 1px;
    height: 2.6rem;
    margin-top: 0;
    background: repeating-linear-gradient(180deg,
      currentColor 0 5px, transparent 5px 12px);
    animation-name: relais-descend;
  }
  @keyframes relais-descend { to { background-position: 0 24px; } }
}
@media (prefers-reduced-motion: reduce) {
  .relais__fil, .relais__forme--boitier::after { animation: none; }
}

/* ---- la page societe : bande d'image -------------------------------------
   Le portrait avait ici un second jeu de regles, ecrit plus tard et donc
   gagnant : les deux se contredisaient sur la marge et le fond. Un seul
   bloc desormais, plus haut, avec le reste des fiches. */
/* Les marges laterales et basses d'une figure sont remises a zero, mais
   pas la marge haute : elle vient de l'utilitaire mt-* pose dans le
   gabarit. Un margin: 0 ici l'annulait, parce que ce bloc est ecrit plus
   bas dans la feuille que les utilitaires et gagne a specificite egale.
   L'image venait alors se coller au dernier paragraphe. */
/* Aucune declaration de margin-top ici : elle vient de l'utilitaire mt-*
   pose dans le gabarit. Un raccourci margin l'ecrasait, meme ecrit a trois
   valeurs, et l'image se collait au dernier paragraphe. */
.bande { margin-inline: 0; margin-bottom: 0; overflow: hidden; }
.bande img { width: 100%; height: auto; }

/* ---- l'editeur de gabarits (page Studio) ---------------------------------
   Une seule affiche, celle du soir, et un vrai petit editeur par-dessus :
   une souris de demonstration qui saisit, deplace, reecrit ; puis la main
   du visiteur, qui peut tout faire elle-meme. Le bleu et le trait rouge
   des guides appartiennent a l'outil qu'on figure, comme le contenu d'un
   client ; la voix de Ginko reste noir et blanc. */
.gabarits {
  min-height: 100svh;
  display: flex;
  align-items: center;
}
.gabarits__inner {
  display: grid;
  grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
  gap: var(--space-xl);
  align-items: center;
  width: 100%;
}
@media (max-width: 900px) {
  .gabarits { min-height: 0; }
  .gabarits__inner { grid-template-columns: 1fr; gap: var(--space-lg); }
}
.gabarits__essai {
  margin-top: var(--space-md);
  font-size: var(--fs-eyebrow);
  color: var(--muted);
}

.gabarits__scene { display: grid; justify-items: center; }
.gabarit-pose {
  position: relative;
  height: min(72svh, 40rem);
  aspect-ratio: 3 / 4;
  max-width: 100%;
  container-type: size;
}
@media (max-width: 900px) { .gabarit-pose { height: auto; width: min(72vw, 380px); } }
.gabarit {
  position: relative;
  display: block;
  width: 100%;
  height: 100%;
  border: 1px solid rgba(0, 0, 0, 0.35);
  border-radius: 1.4rem; /* l'arrondi doux de l'outil figure, pas celui de Ginko */
  overflow: hidden;
  user-select: none;
  -webkit-user-select: none;
}
.gabarit--nuit { background: #050505; }
/* L'ombre portee de la photo sur ses propres noirs : l'affiche garde ses
   blancs, le texte flotte au-dessus d'un fond apaise. */
.gabarit--nuit::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(180deg,
    rgba(0, 0, 0, 0.30) 0%,
    rgba(0, 0, 0, 0.10) 30%,
    rgba(0, 0, 0, 0.22) 62%,
    rgba(0, 0, 0, 0.52) 100%);
  pointer-events: none;
}

/* L'affiche elle-meme. Les blocs naissent centres par le flux ; le moteur
   les libere ensuite en absolu, a la meme place, pour qu'ils se saisissent. */
.gw-fond {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  opacity: 0.74;
}
.gw-corps {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  text-align: center;
  color: #fff;
}
.gw-corps p { margin: 0; }
.gw-avant {
  font-family: "Archivo", "Helvetica Neue", sans-serif;
  font-size: 2.8cqw;
  color: rgba(255, 255, 255, 0.55);
}
.gw-titre {
  margin-top: 2.2cqw !important;
  font-family: "EB Garamond", "Georgia", serif;
  font-size: 11.5cqw;
  line-height: 1.02;
  letter-spacing: 0;
  text-align: center;
}
.gw-scan {
  margin-top: 7cqw !important;
  font-family: "Archivo", "Helvetica Neue", sans-serif;
  font-size: 3.1cqw;
}
.gw-qr {
  position: absolute;
  z-index: 2;
  left: 50%; bottom: 15%;
  transform: translateX(-50%);
  width: 12cqw; height: auto;
  filter: invert(1);
  opacity: 0.95;
}

/* Une fois liberes : chaque bloc est absolu, pose par le moteur en
   pourcentages de la toile, et se laisse prendre. */
.est-libre .gw-avant,
.est-libre .gw-titre,
.est-libre .gw-scan {
  position: absolute;
  margin: 0 !important;
}
.est-libre .gw-qr { bottom: auto; transform: none; }
.est-libre [data-bloc] { cursor: move; touch-action: none; }
.est-libre [data-bloc]:hover { outline: 1px solid rgba(91, 141, 239, 0.65); outline-offset: 2px; }

/* Une ligne en cours d'ecriture : la demo qui tape, ou le visiteur. */
[data-bloc].est-ecrit {
  outline: 1.5px solid #5b8def;
  outline-offset: 2px;
  cursor: text;
  user-select: text;
  -webkit-user-select: text;
}
[data-bloc].est-ecrit:hover { outline-color: #5b8def; }
[data-bloc]:focus { outline: 1.5px solid #5b8def; outline-offset: 2px; }
.gcaret {
  display: inline-block;
  width: 2px;
  height: 0.95em;
  margin-left: 0.06em;
  vertical-align: -0.1em;
  background: #5b8def;
  animation: gk-caret 1s steps(1) infinite;
}
@keyframes gk-caret { 50% { opacity: 0; } }

/* Les guides d'alignement, le temps d'un accrochage. */
.gguide {
  position: absolute;
  background: #f24822;
  opacity: 0;
  z-index: 4;
  transition: opacity 0.15s var(--ease);
  pointer-events: none;
}
.gguide--v { left: 50%; top: 0; bottom: 0; width: 1px; margin-left: -0.5px; }
.gguide--h { top: 50%; left: 0; right: 0; height: 1px; margin-top: -0.5px; }
.gguide.est-visible { opacity: 1; }

/* Le cadre de selection et sa cote, puis la souris de demonstration. */
.gsel {
  position: absolute;
  border: 1.5px solid #5b8def;
  opacity: 0;
  z-index: 5;
  pointer-events: none;
  transition: opacity 0.22s var(--ease);
}
.gsel.est-visible { opacity: 1; }
.gsel__p {
  position: absolute;
  width: 0.5rem; height: 0.5rem;
  background: #fff;
  border: 1.5px solid #5b8def;
}
.gsel__p--no { left: -0.3rem; top: -0.3rem; }
.gsel__p--ne { right: -0.3rem; top: -0.3rem; }
.gsel__p--so { left: -0.3rem; bottom: -0.3rem; }
.gsel__p--se { right: -0.3rem; bottom: -0.3rem; }
.gsel__rond {
  position: absolute;
  left: 50%; top: -1.6rem;
  width: 0.65rem; height: 0.65rem;
  margin-left: -0.325rem;
  border: 1.5px solid #5b8def;
  border-radius: 50%;
}
.gsel__cote {
  position: absolute;
  top: calc(100% + 0.4rem);
  left: 50%;
  transform: translateX(-50%);
  padding: 0.12rem 0.35rem;
  background: #5b8def;
  color: #fff;
  font-family: var(--font-sans-ext);
  font-style: normal;
  font-size: 0.62rem;
  letter-spacing: 0.03em;
  white-space: nowrap;
}
/* La barre d'outils de l'editeur figure : une pastille blanche posee au
   bas de la toile, l'arrondi doux et le bleu appartiennent a l'outil. */
.goutils {
  position: absolute;
  left: 50%; bottom: 0;
  transform: translate(-50%, 50%);
  display: flex;
  gap: 1.7cqw;
  padding: 1.6cqw;
  background: #fff;
  border-radius: 2.6cqw;
  box-shadow: 0 1.2cqw 4cqw rgba(0, 0, 0, 0.14);
  z-index: 7;
}
.goutil {
  display: grid;
  place-items: center;
  width: 8cqw; height: 8cqw;
  border: 1px solid #ebebe9;
  border-radius: 1.9cqw;
  background: #fff;
  color: #71717a;
  transition: background-color var(--dur-fast) var(--ease);
}
.goutil svg {
  width: 58%; height: 58%;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.goutil:hover { background: #f4f4f3; }
.goutil.est-actif,
.goutil.est-actif:hover { background: #7b8cf0; border-color: #7b8cf0; color: #fff; }

.gcurseur {
  position: absolute;
  left: 0; top: 0;
  width: 1.4rem; height: 1.4rem;
  z-index: 8;
  opacity: 0;
  transform: translate(50%, 90%);
  transition-property: transform, opacity;
  transition-timing-function: cubic-bezier(0.3, 0, 0.25, 1);
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.35));
  pointer-events: none;
}
.gcurseur.est-la { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .gcurseur { display: none; }
  .gsel, .gguide { transition: none; }
}

/* ---- l'app partout (page Studio) -----------------------------------------
   Les deux fenetres reelles de l'application, ombres comprises dans les
   images : la grande posee au centre, celle de la poche a cheval sur son
   flanc droit, un peu plus basse. Elles derivent a des pas differents au
   defilement. Le fond gris est peint ici mais cede au cameleon, qui le
   fond au fil du defilement comme les autres. */
.partout {
  overflow: hidden;
  background: #f8f8f8;
}
/* Le titre ouvre la section, centre sur le meme axe que la scene. */
.partout__titre {
  max-width: 46rem;
  margin: 0 auto;
  text-align: center;
}

/* La scene a exactement la largeur de la grande fenetre : elle se centre
   donc sur l'axe du texte, et la poche deborde sur son flanc droit. Les
   ombres sont posees ici, en drop-shadow sur la silhouette des fenetres :
   celles des fichiers d'origine ne survivaient pas au recadrage. */
.partout__scene {
  position: relative;
  width: min(46rem, 82%);
  margin: var(--space-lg) auto 0;
}
.partout__fenetre {
  display: block;
  width: 100%;
  height: auto;
  filter: drop-shadow(0 1.8rem 3.2rem rgba(0, 0, 0, 0.10))
          drop-shadow(0 0.4rem 1rem rgba(0, 0, 0, 0.06));
}
.partout__poche {
  position: absolute;
  right: -7%;
  top: 31%;
  width: 26.5%;
  height: auto;
  filter: drop-shadow(0 1.2rem 2.4rem rgba(0, 0, 0, 0.13))
          drop-shadow(0 0.3rem 0.8rem rgba(0, 0, 0, 0.07));
}
@media (max-width: 700px) {
  .partout__scene { width: 92%; }
  .partout__poche { right: -2%; top: 30%; width: 30%; }
}

.partout__texte {
  /* la poche deborde sous la fenetre : le texte prend ses distances.
     .lead porte une largeur max sans marges auto : sur un tres grand
     ecran il resterait ancre a gauche du bloc centre. */
  max-width: 46rem;
  margin: var(--space-xl) auto 0;
  text-align: center;
}

/* ---- le devis en une ligne (page Pricing) --------------------------------
   Un mode, une duree, deux formats. Un seul montant par format, en grand
   serif ; il fond d'une valeur a l'autre a chaque reglage. Les onglets
   reprennent le dessin de ceux de l'editeur de gabarits. */
.simu__reglages {
  display: grid;
  justify-items: center;
}

/* L'interrupteur : deux segments dans un cadre de 1px, et un curseur
   plein qui glisse de l'un a l'autre. Le texte du segment couvert passe
   a l'encre : c'est le curseur qui dit l'etat, pas un soulignement. */
.simu__bascule {
  position: relative;
  display: inline-grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  border: 1px solid rgba(255, 255, 255, 0.4);
  isolation: isolate;
}
.section--light .simu__bascule, .section--paper .simu__bascule { border-color: rgba(0, 0, 0, 0.4); }
.simu__curseur {
  position: absolute;
  z-index: 0;
  top: 0;
  left: 0;
  width: 50%;
  height: 100%;
  background: var(--gk-white);
  transition: transform 460ms var(--ease-reveal);
}
.section--light .simu__curseur, .section--paper .simu__curseur { background: var(--gk-black); }
.simu[data-simu-mode="leasing"] .simu__curseur { transform: translateX(100%); }
.simu__seg {
  position: relative;
  z-index: 1;
  border: 0;
  background: none;
  cursor: pointer;
  padding: 0.9em 2.2em;
  font-family: var(--font-sans-ext);
  font-size: 0.625rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--muted);
  transition: color 460ms var(--ease-reveal);
}
.simu__seg:hover { color: var(--gk-white); }
.simu__seg[aria-selected="true"] { color: var(--gk-black); }
.section--light .simu__seg:hover, .section--paper .simu__seg:hover { color: var(--gk-charcoal); }
.section--light .simu__seg[aria-selected="true"],
.section--paper .simu__seg[aria-selected="true"] { color: var(--gk-white); }

/* La note sous l'interrupteur : une seule phrase a la fois, qui porte le
   Studio inclus et explique la duree. Empilees au meme endroit, elles
   fondent de l'une a l'autre sans bouger la page. */

/* Les deux cadres cote a cote, puis le bloc des conditions dessous. */
.simu__ensemble {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-md);
  align-items: stretch;
}
@media (max-width: 700px) { .simu__ensemble { grid-template-columns: 1fr; } }

.simu__conditions-inner {
  display: grid;
  grid-template-columns: minmax(0, 4fr) minmax(0, 8fr);
  gap: var(--space-lg);
  padding: 3rem var(--space-lg);
  align-items: start;
}
.simu__conditions-tete {
  align-self: stretch;
  display: grid;
  align-content: start;
  justify-items: start;
  gap: 0.7rem;
  border-right: 1px solid rgba(255, 255, 255, 0.12);
  padding-right: var(--space-lg);
}
.section--light .simu__conditions-tete,
.section--paper .simu__conditions-tete { border-right-color: rgba(0, 0, 0, 0.12); }
.simu__conditions .eyebrow { margin-bottom: 0; }
/* Les coches en deux colonnes : verticales a lire, courtes a parcourir. */
.simu__liste {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: var(--space-lg);
  align-content: center;
}
@media (max-width: 800px) {
  .simu__conditions-inner { grid-template-columns: 1fr; gap: var(--space-sm); }
  .simu__conditions-tete { border-right: 0; padding-right: 0; }
  .simu__liste { grid-template-columns: 1fr; }
}
.simu__offre-mode { display: grid; margin-bottom: 0; }
.simu__mode-mot {
  grid-area: 1 / 1;
  opacity: 0;
  transition: opacity 420ms var(--ease-reveal);
}
.simu__mode-mot.est-vue { opacity: 1; }

/* La liste visible est seule dans le bloc : la hauteur epouse le contenu
   du mode choisi, et la liste entrante nait en fondu. */
.simu__liste:not(.est-vue) { display: none; }
/* Les coches entrent une par une : la liste ne s'affiche pas, elle se
   deroule. Le retard croit d'un demi-temps par ligne. */
.simu__liste.est-vue li { animation: gk-item-entre 460ms var(--ease-reveal) backwards; }
.simu__liste.est-vue li:nth-child(1) { animation-delay: 40ms; }
.simu__liste.est-vue li:nth-child(2) { animation-delay: 110ms; }
.simu__liste.est-vue li:nth-child(3) { animation-delay: 180ms; }
.simu__liste.est-vue li:nth-child(4) { animation-delay: 250ms; }
.simu__liste.est-vue li:nth-child(5) { animation-delay: 320ms; }

/* Le retrait : ce qui part monte d'un souffle et s'efface, puis laisse
   la place. Le forwards tient l'etat efface jusqu'a l'echange. */
.simu__valeur.se-retire,
.simu__liste.se-retire,
.simu__mode-mot.se-retire {
  animation: gk-texte-sort 180ms var(--ease) forwards;
}

@keyframes gk-prix-entre {
  from { opacity: 0; transform: translateY(12px) scale(0.985); }
}
@keyframes gk-item-entre {
  from { opacity: 0; transform: translateY(10px); }
}
@keyframes gk-texte-sort {
  to { opacity: 0; transform: translateY(-8px); }
}

/* Les durees, sous l'interrupteur. La ligne garde sa place : elle se
   revele, elle ne pousse rien. */

/* Les deux cartes : la photo du cadre en tete, recadree basse pour que la
   section reste courte, puis le corps du devis. */
.simu__media { aspect-ratio: 16 / 7; }
.simu__media img { transition: transform 1.1s var(--ease-reveal); }
.simu__format:hover .simu__media img { transform: scale(1.035); }

/* Toute la carte est un lien vers les displays : au survol elle se
   souleve d'un souffle, son cadre s'allume, l'image avance. Le transform
   du survol partage la propriete avec la revelation : on redonne donc la
   liste complete, en ne laissant le retard de cascade qu'a l'opacite. */
/* La liste de transitions est complete et posee avec une specificite
   superieure a celle des surfaces cameleon : sans cela, cette derniere
   ecrasait la liste et le souleve du survol claquait sec. */
a.simu__format,
body.gk-cameleon main a.simu__format {
  display: block;
  color: inherit;
  transition: opacity var(--dur) var(--ease-reveal),
              transform 560ms var(--ease-reveal),
              background-color 900ms ease,
              border-color 900ms ease;
  transition-delay: var(--reveal-delay, 0ms), 0ms, 0ms, 0ms;
}
a.simu__format:hover {
  transform: translateY(-5px);
}
.simu__corps .eyebrow { transition: color 420ms var(--ease); }
a.simu__format:hover .simu__corps .eyebrow { color: var(--gk-white); }
.simu__corps {
  padding: 3rem var(--space-lg);
  display: grid;
  justify-items: center;
  align-content: start;
  gap: 0.3rem;
  text-align: center;
}
/* L'eyebrow porte une grande marge basse pour les tetes de section ; dans
   la carte c'est la grille qui rythme, la marge fausserait l'equilibre
   haut/bas du bloc. */
.simu__corps .eyebrow { margin-bottom: 0.3rem; }
.simu__meta {
  font-family: var(--font-sans-ext);
  font-size: var(--fs-eyebrow);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
}

/* Les montants, empiles au meme endroit : celui du reglage se montre,
   les autres attendent dessous. La hauteur ne bouge jamais. */
.simu__prix {
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 0.4em;
  margin-top: 0.7rem;
}
.simu__pile { display: grid; }
.simu__des {
  font-size: var(--fs-body);
  color: var(--muted);
}
/* Seul le montant du mode existe : la boite epouse sa largeur, "from" et
   "/ month" restent colles a lui. L'entrant nait en fondu. */
.simu__valeur {
  font-family: var(--font-serif);
  font-size: clamp(2.4rem, 3.6vw, 3.25rem);
  line-height: 1;
  letter-spacing: -0.02em;
}
.simu__valeur:not(.est-vue) { display: none; }
.simu__valeur.est-vue { animation: gk-prix-entre 480ms var(--ease-reveal) backwards; }

.simu__mois {
  font-size: var(--fs-body);
  color: var(--muted);
  opacity: 0;
  transition: opacity 260ms var(--ease);
}
.simu[data-simu-mode="leasing"] .simu__mois {
  opacity: 1;
  transition: opacity 420ms var(--ease-reveal) 240ms;
}
.simu__voir { margin-top: var(--space-sm); }
.simu__note { text-align: center; }

@media (prefers-reduced-motion: reduce) {
  .simu__valeur, .simu__mois, .simu__des, .simu__liste, .simu__mode-mot, .simu__curseur, .simu__seg { transition: none; }
  .simu__valeur.est-vue, .simu__liste.est-vue li, .simu__valeur.se-retire,
  .simu__liste.se-retire, .simu__mode-mot.se-retire { animation: none; }
}

/* ---- le cameleon emporte aussi les surfaces ------------------------------
   Quand le fond de page fond vers le blanc, une carte posee sur une
   section sombre s'eclaircit avec lui, au meme rythme -- et inversement
   quand la page repasse au noir. Sans cela, des rectangles noirs
   flottaient sur le blanc le temps de la traversee. */
body.gk-cameleon main .model-card {
  transition: background-color 900ms ease, border-color 900ms ease;
}
body.gk-clair main section.section--dark .model-card,
body.gk-clair main section.section--dark .carte-service {
  background: var(--gk-off-white);
  border-color: rgba(0, 0, 0, 0.14);
}
body.gk-cameleon:not(.gk-clair) main section.section--light .model-card,
body.gk-cameleon:not(.gk-clair) main section.section--paper .model-card {
  background: var(--gk-near-black);
  border-color: rgba(255, 255, 255, 0.16);
}

/* La carte objet des pages univers suit le meme mouvement, filet compris.
   Elle emporte sa palette entiere : sur fond papier, son texte s'encre. */
.objet,
body.gk-cameleon main .objet {
  transition: background-color 900ms ease, border-color 900ms ease,
              color 900ms ease;
}
body.gk-clair main section.section--dark .objet {
  background: var(--gk-off-white);
  color: var(--gk-charcoal);
  --muted: #68685f;
  --body-ink: var(--gk-charcoal);
}
/* En volet, c'est le filet qui porte la bordure : la carte n'y touche pas. */
body.gk-clair main section.section--dark .objet:not(.va-se-deployer),
body.gk-clair main section.section--dark .objet__filet {
  border-color: rgba(0, 0, 0, 0.14);
}
body.gk-clair main section.section--dark .objet__faits .spec:hover dt {
  color: var(--gk-black);
}

/* Les commandes du simulateur suivent le meme mouvement. */
body.gk-clair main section.section--dark .simu__bascule { border-color: rgba(0, 0, 0, 0.4); }
body.gk-clair main section.section--dark .simu__curseur { background: var(--gk-black); }
body.gk-clair main section.section--dark .simu__seg:hover { color: var(--gk-charcoal); }
body.gk-clair main section.section--dark .simu__seg[aria-selected="true"] { color: var(--gk-white); }
body.gk-clair main section.section--dark .simu__conditions-tete { border-right-color: rgba(0, 0, 0, 0.12); }

/* ---- le bloc des conditions repond au survol -----------------------------
   Meme famille que les cartes : il se souleve d'un souffle, et les coches
   glissent d'un cran vers la droite, une par une, en s'allumant. La liste
   de transitions est complete, au-dessus de la regle cameleon. */
.simu__conditions,
body.gk-cameleon main .simu__conditions {
  transition: opacity var(--dur) var(--ease-reveal),
              transform 560ms var(--ease-reveal),
              background-color 900ms ease,
              border-color 900ms ease;
  transition-delay: var(--reveal-delay, 0ms), 0ms, 0ms, 0ms;
}
.simu__conditions:hover { transform: translateY(-5px); }

.simu__conditions .check-list li {
  transition: color 420ms var(--ease), transform 520ms var(--ease-reveal);
}
.simu__conditions:hover .check-list li {
  color: var(--gk-white);
  transform: translateX(6px);
}
.simu__conditions:hover .check-list li:nth-child(1) { transition-delay: 0ms; }
.simu__conditions:hover .check-list li:nth-child(2) { transition-delay: 50ms; }
.simu__conditions:hover .check-list li:nth-child(3) { transition-delay: 100ms; }
.simu__conditions:hover .check-list li:nth-child(4) { transition-delay: 150ms; }
.simu__conditions:hover .check-list li:nth-child(5) { transition-delay: 200ms; }
/* En phase claire, les coches s'encrent au lieu de blanchir. */
body.gk-clair main section.section--dark .simu__conditions:hover .check-list li { color: var(--gk-black); }

.simu__offre-mode .simu__mode-mot { transition: color 420ms var(--ease), opacity 420ms var(--ease-reveal); }
.simu__conditions:hover .simu__mode-mot.est-vue { color: var(--gk-white); }
body.gk-clair main section.section--dark .simu__conditions:hover .simu__mode-mot.est-vue { color: var(--gk-black); }

@media (prefers-reduced-motion: reduce) {
  .simu__conditions, .simu__conditions .check-list li { transition: none; }
  .simu__conditions:hover { transform: none; }
  .simu__conditions:hover .check-list li { transform: none; }
}

/* ---- l'heritage des licences (cartes Studio) -----------------------------
   Survoler une licence eteint les deux autres, piece par piece -- sauf ce
   qui lui appartient deja : Pro contient Essential, Scale contient les
   deux. Les features comprises restent allumees dans les cartes eteintes
   et glissent d'un cran : on voit ce qu'on emporte.  */
.plan,
body.gk-cameleon main .plan {
  transition: opacity var(--dur) var(--ease-reveal),
              transform 560ms var(--ease-reveal);
  transition-delay: var(--reveal-delay, 0ms), 0ms;
}
.plan > * { transition: opacity 480ms var(--ease-reveal); }
.plan .check-list li {
  transition: opacity 480ms var(--ease-reveal), transform 520ms var(--ease-reveal);
}

.plan:hover { transform: translateY(-6px); }

/* les autres s'eteignent : l'opacite se pose sur leurs pieces, pour
   pouvoir rallumer une liste sans rallumer la carte */
.plans:has(.plan:hover) .plan:not(:hover) > * { opacity: 0.25; }
.plans:has(.plan:hover) .plan:not(:hover) { transform: scale(0.988); }

/* ce qui est deja compris se rallume... */
.plans:has(.plan[data-plan="pro"]:hover) .plan[data-plan="essential"] .check-list,
.plans:has(.plan[data-plan="scale"]:hover) .plan[data-plan="essential"] .check-list,
.plans:has(.plan[data-plan="scale"]:hover) .plan[data-plan="pro"] .check-list {
  opacity: 1;
}
/* ...et glisse d'un cran, en cascade */
.plans:has(.plan[data-plan="pro"]:hover) .plan[data-plan="essential"] .check-list li,
.plans:has(.plan[data-plan="scale"]:hover) .plan[data-plan="essential"] .check-list li,
.plans:has(.plan[data-plan="scale"]:hover) .plan[data-plan="pro"] .check-list li {
  transform: translateX(6px);
}
/* "1 user" et "1 group" ne sont pas herites : Pro et Scale les remplacent
   par de l'illimite. Ils restent eteints avec le reste de la carte. */
.plans:has(.plan[data-plan="pro"]:hover) .plan[data-plan="essential"] .check-list li:nth-child(-n+2),
.plans:has(.plan[data-plan="scale"]:hover) .plan[data-plan="essential"] .check-list li:nth-child(-n+2) {
  opacity: 0.25;
  transform: none;
}
.plans:has(.plan:hover) .plan:not(:hover) .check-list li:nth-child(1) { transition-delay: 0ms; }
.plans:has(.plan:hover) .plan:not(:hover) .check-list li:nth-child(2) { transition-delay: 45ms; }
.plans:has(.plan:hover) .plan:not(:hover) .check-list li:nth-child(3) { transition-delay: 90ms; }
.plans:has(.plan:hover) .plan:not(:hover) .check-list li:nth-child(4) { transition-delay: 135ms; }
.plans:has(.plan:hover) .plan:not(:hover) .check-list li:nth-child(5) { transition-delay: 180ms; }
.plans:has(.plan:hover) .plan:not(:hover) .check-list li:nth-child(6) { transition-delay: 225ms; }

@media (prefers-reduced-motion: reduce) {
  .plan, .plan > *, .plan .check-list li { transition: none; }
  .plan:hover { transform: none; }
  .plans:has(.plan:hover) .plan:not(:hover) { transform: none; }
  .plans:has(.plan:hover) .plan:not(:hover) .check-list li { transform: none; }
}

/* ---- les puces + pivotent au survol de leur carte ------------------------
   Un quart de tour chacune, l'une apres l'autre : le signe revient sur
   lui-meme, seul le mouvement se voit -- un frisson qui traverse la
   liste de haut en bas. */
.plan .plus__mark {
  display: inline-block;
  transition: transform 560ms var(--ease-reveal);
}
.plan:hover li:nth-child(1) .plus__mark { transition-delay: 0ms; }
.plan:hover li:nth-child(2) .plus__mark { transition-delay: 60ms; }
.plan:hover li:nth-child(3) .plus__mark { transition-delay: 120ms; }
.plan:hover li:nth-child(4) .plus__mark { transition-delay: 180ms; }
.plan:hover li:nth-child(5) .plus__mark { transition-delay: 240ms; }
.plan:hover li:nth-child(6) .plus__mark { transition-delay: 300ms; }

@media (prefers-reduced-motion: reduce) {
  .plan .plus__mark { transition: none; }
  .plan:hover .plus__mark { transform: none; }
}

/* ---- les cartes services respirent au survol -----------------------------
   Meme famille que les cartes du haut de page : la carte se souleve d'un
   souffle, l'image avance d'un cran dans son cadre, le titre s'allume.
   La liste de transitions est complete, au-dessus de la regle cameleon. */
.carte-service,
body.gk-cameleon main .carte-service {
  transition: opacity var(--dur) var(--ease-reveal),
              transform 560ms var(--ease-reveal),
              background-color 900ms ease,
              border-color 900ms ease;
  transition-delay: var(--reveal-delay, 0ms), 0ms, 0ms, 0ms;
}
.carte-service:hover { transform: translateY(-5px); }

.carte-service__vue { overflow: hidden; }
.carte-service__vue img { transition: transform 1.1s var(--ease-reveal); }
.carte-service:hover .carte-service__vue img { transform: scale(1.045); }

.carte-service h3,
.carte-service .carte-service__label { transition: color 420ms var(--ease); }
.carte-service:hover h3 { color: var(--gk-white); }
.section--light .carte-service:hover h3,
.section--paper .carte-service:hover h3,
body.gk-clair main section.section--dark .carte-service:hover h3 { color: var(--gk-black); }

@media (prefers-reduced-motion: reduce) {
  .carte-service, .carte-service__vue img { transition: none; }
  .carte-service:hover { transform: none; }
  .carte-service:hover .carte-service__vue img { transform: none; }
}

/* ---- la recherche de la FAQ ----------------------------------------------
   Un champ seul sous le chapeau. Le trait plein balaie le champ au focus,
   comme sur les formulaires. Ce qui ne correspond pas se retire en
   fondu ; ce qui revient renait de meme, et le sommaire s'eteint en face
   des chapitres vides. */
/* Le champ est celui des formulaires de la maison : pas de cadre, un
   filet sous le texte, l'icone posee au bout de la ligne. */
.faq-quete {
  position: relative;
  display: flex;
  align-items: center;
  gap: 0.8rem;
  border-bottom: 1px solid rgba(255, 255, 255, 0.25);
  padding: 0.55rem 0.1rem 0.7rem;
  text-align: left;
}
.faq-quete__champ {
  flex: 1 1 auto;
  min-width: 0;
  background: none;
  border: 0;
  padding: 0;
  color: var(--gk-white);
}
.faq-quete__champ:focus { outline: none; }
.faq-quete__champ::placeholder { color: var(--muted); }
.faq-quete__champ::-webkit-search-cancel-button { -webkit-appearance: none; }
/* Une seule case d'icone, a droite : la loupe au repos, la croix des
   qu'un mot est pose. Elles fondent de l'une a l'autre. */
.faq-quete__icone {
  flex: none;
  width: 16px;
  height: 16px;
  color: var(--muted);
  border: 0;
  background: none;
  padding: 0;
  animation: gk-fondu-texte 320ms var(--ease-reveal);
}
.faq-quete__icone svg { width: 100%; height: 100%; display: block; }
.faq-quete__icone--effacer { cursor: pointer; transition: color var(--dur-fast) var(--ease); }
.faq-quete__icone--effacer:hover { color: var(--gk-white); }
.faq-quete__icone[hidden] { display: none; }
/* Au focus, le trait plein balaie le filet, de gauche a droite, comme
   sur les champs du formulaire de contact. */
.faq-quete::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: -1px;
  height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 520ms var(--ease-reveal);
  pointer-events: none;
}
.faq-quete:focus-within::after { transform: scaleX(1); }

/* le retrait et la renaissance des questions et chapitres */
.faq__item.se-filtre,
.faq-groupe.se-filtre { animation: gk-texte-sort 180ms var(--ease) forwards; }
.faq__item.renait { animation: gk-item-entre 460ms var(--ease-reveal); }
.faq-groupe.renait { animation: gk-item-entre 460ms var(--ease-reveal); }

.faq-sommaire__lien { opacity: 1; transition: color var(--dur-fast) var(--ease), padding-left var(--dur-fast) var(--ease), opacity 420ms var(--ease-reveal); }
.faq-sommaire__lien.est-eteint { opacity: 0.25; pointer-events: none; }

.faq-vide {
  color: var(--muted);
  max-width: 52ch;
  animation: gk-fondu-texte 460ms var(--ease-reveal);
}

@media (prefers-reduced-motion: reduce) {
  .faq__item.se-filtre, .faq-groupe.se-filtre,
  .faq__item.renait, .faq-groupe.renait, .faq-vide { animation: none; }
}

/* ---- le push de fin de FAQ ----------------------------------------------- */
.faq-push {
  text-align: center;
  max-width: 56ch;
  margin-inline: auto;
}
.faq-push .lead { margin-inline: auto; }
.faq-push__actions {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: var(--space-sm) var(--space-md);
}

/* ---- le consentement cookies ---------------------------------------------
   Un panneau noir pose en bas de page, jamais un mur : le site reste
   lisible pendant qu'on choisit, puisque rien n'est charge tant que le
   choix n'est pas fait. Refuser tient au meme niveau qu'accepter, dans la
   meme rangee, meme corps, meme chasse : la loi l'exige et le dessin ne
   cherche pas a la contourner. Le volet de reglages se deplie dans le
   panneau plutot que de s'ouvrir ailleurs, pour que le refus ne disparaisse
   jamais derriere un deuxieme ecran. Il se revele en fondu, sans bondir. */
.cookies {
  position: fixed;
  z-index: 80;
  left: 0; right: 0; bottom: 0;
  display: flex;
  justify-content: center;
  padding: var(--space-sm);
  pointer-events: none;
}
.cookies[hidden] { display: none; }

.cookies__panneau {
  width: min(56rem, 100%);
  max-height: calc(100svh - 2 * var(--space-sm));
  overflow-y: auto;
  padding: 2rem 2.2rem;
  background: var(--gk-black);
  color: var(--gk-white);
  border: 1px solid rgba(255, 255, 255, 0.16);
  pointer-events: auto;
  opacity: 0;
  transform: translateY(0.75rem);
  transition: opacity 900ms var(--ease), transform 900ms var(--ease);
}
.cookies.est-la .cookies__panneau { opacity: 1; transform: none; }
.cookies__panneau:focus { outline: none; }

.cookies__titre {
  margin: 0;
  font-family: var(--font-serif);
  font-size: 1.5rem;
  line-height: 1.15;
  letter-spacing: -0.01em;
}
.cookies__texte {
  margin: 0.7rem 0 0;
  max-width: 52ch;
  font-size: 0.875rem;
  line-height: 1.65;
  color: rgba(255, 255, 255, 0.66);
}
.cookies__texte a {
  color: var(--gk-white);
  text-decoration: underline;
  text-underline-offset: 0.25em;
  text-decoration-thickness: 1px;
}

/* Le volet de reglages : il pousse le panneau vers le haut en se depliant. */
.cookies__reglages {
  margin-top: 1.8rem;
  padding-top: 1.6rem;
  border-top: 1px solid rgba(255, 255, 255, 0.16);
}
.cookies__reglages[hidden] { display: none; }
.cookies__soustitre {
  margin: 0 0 1.2rem;
  font-family: var(--font-sans-ext);
  font-size: 0.625rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: rgba(255, 255, 255, 0.5);
}
.cookies__poste {
  display: flex;
  align-items: flex-start;
  gap: 1.6rem;
  padding: 1rem 0;
}
.cookies__poste + .cookies__poste { border-top: 1px solid rgba(255, 255, 255, 0.1); }
.cookies__dit { flex: 1; min-width: 0; }
.cookies__nom {
  margin: 0;
  font-size: 0.9375rem;
}
.cookies__detail {
  margin: 0.35rem 0 0;
  font-size: 0.8125rem;
  line-height: 1.6;
  color: rgba(255, 255, 255, 0.52);
}
.cookies__fige {
  flex-shrink: 0;
  padding-top: 0.15rem;
  font-family: var(--font-sans-ext);
  font-size: 0.5625rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.18em;
  color: rgba(255, 255, 255, 0.42);
}

/* L'interrupteur : un rectangle, comme tout le reste ici. Jamais coche
   d'avance, c'est la regle. */
.bascule {
  flex-shrink: 0;
  margin-top: 0.1rem;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
}
.bascule__piste {
  display: block;
  width: 2.75rem;
  height: 1.375rem;
  padding: 2px;
  border: 1px solid rgba(255, 255, 255, 0.32);
  transition: border-color var(--dur-fast) var(--ease),
              background-color var(--dur-fast) var(--ease);
}
.bascule__pion {
  display: block;
  width: calc(50% - 1px);
  height: 100%;
  background: rgba(255, 255, 255, 0.42);
  transition: transform 320ms var(--ease), background-color 320ms var(--ease);
}
.bascule:hover .bascule__piste { border-color: rgba(255, 255, 255, 0.6); }
.bascule[aria-checked="true"] .bascule__piste { border-color: var(--gk-white); }
.bascule[aria-checked="true"] .bascule__pion {
  background: var(--gk-white);
  transform: translateX(calc(100% + 2px));
}
.bascule:focus-visible .bascule__piste { outline: 1px solid var(--gk-white); outline-offset: 2px; }

/* La rangee d'actions. Accepter, refuser : meme rangee, meme corps. */
.cookies__actes {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem;
  margin-top: 1.8rem;
}
.cookies__acte {
  font-family: var(--font-sans-ext);
  font-size: 0.625rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  padding: 1.15em 2.2em;
  border-radius: 0;
  border: 1px solid rgba(255, 255, 255, 0.32);
  background: none;
  color: var(--gk-white);
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease),
              color var(--dur-fast) var(--ease);
}
.cookies__acte[hidden] { display: none; }
.cookies__acte:hover { background: rgba(255, 255, 255, 0.1); }
.cookies__acte--plein {
  margin-left: auto;
  background: var(--gk-white);
  color: var(--gk-black);
  border-color: var(--gk-white);
}
.cookies__acte--plein:hover { background: var(--gk-paper); border-color: var(--gk-paper); }
.cookies__acte:focus-visible { outline: 1px solid var(--gk-white); outline-offset: 2px; }

@media (max-width: 620px) {
  .cookies { padding: 0; }
  .cookies__panneau {
    width: 100%;
    padding: 1.6rem 1.4rem;
    border-left: 0; border-right: 0; border-bottom: 0;
  }
  .cookies__actes { gap: 0.5rem; }
  .cookies__acte { flex: 1 1 auto; text-align: center; padding: 1.15em 1rem; }
  .cookies__acte--plein { margin-left: 0; flex-basis: 100%; order: -1; }
}

/* Rouvrir son choix depuis le pied de page. */
.footer-legal__rouvrir {
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: inherit;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 0.25em;
  text-decoration-thickness: 1px;
  text-decoration-color: rgba(255, 255, 255, 0.3);
  transition: text-decoration-color var(--dur-fast) var(--ease);
}
.footer-legal__rouvrir:hover { text-decoration-color: currentColor; }

@media (prefers-reduced-motion: reduce) {
  .cookies__panneau { transition: none; transform: none; }
  .bascule__pion { transition: none; }
}

/* ---- l'invitation vers les cadres, en fin de fiche d'espace --------------
   Une ligne et un bouton, centres, avec de l'air autour : la fiche a dit ce
   qu'elle avait a dire, elle passe la main sans hausser la voix. */
.pousse {
  max-width: 44rem;
  margin-inline: auto;
  text-align: center;
}
.pousse__cta { margin-top: var(--space-md); }

/* Une variante, et une seule : le push des pages univers, coince entre une
   section sombre et le configurateur clair. Il garde son noir pendant que la
   page bascule sous lui, et son contenu s'efface plutot que de rester en
   contraste mort sur un bloc qui s'en va.

   Ce comportement demande son propre modificateur. Il a d'abord vecu sur
   .pousse tout court, et le jour ou la page societe a repris cette classe
   pour son invitation finale, le texte a disparu : la page est claire a cet
   endroit, la regle s'appliquait, et rien n'expliquait pourquoi. Un
   comportement qui depend du voisinage ne doit pas voyager avec le nom
   d'un composant. */
body.gk-cameleon.gk-clair .pousse--eteint > [data-reveal] {
  opacity: 0;
  pointer-events: none;
}

/* --------------------------------------------------------------------------
   LE TELEPHONE : L'ARRIVEE, LE HERO, ET LE FILM

   Trois decisions tiennent ensemble ici, et se comprennent mal separement.

   Le film d'ouverture ne joue plus a l'arrivee sur un ecran vertical : il
   pese dix-neuf megaoctets, il est cadre pour le paysage, et il retenait
   quelqu'un qui voulait entrer. A la place, une intro sobre -- la marque,
   un compteur -- qui se leve d'elle-meme.

   Le hero, lui, garde la video en fond, mais son texte passe au centre :
   sans image large a sa droite pour l'equilibrer, un bloc pose en bas a
   gauche d'un ecran vertical parait tombe. Le voile s'appuie d'autant.

   Et le film se retrouve plus bas, dans sa vraie proportion et avec le
   son, pour qui veut le voir.
   -------------------------------------------------------------------------- */

/* ---- l'intro sobre -------------------------------------------------------
   Rien a cliquer : le compteur monte, le rideau se leve. Le pourcentage a
   rejoint la marque au centre (c'est le script qui l'y deplace), il doit
   donc defaire l'interlettrage genereux dont il herite. */
.intro--sobre { cursor: default; }
.intro--sobre .intro__marque {
  align-content: center;   /* sinon les deux lignes se partagent l'ecran */
  gap: 1.15rem;
}
.intro--sobre .intro__pct {
  position: static;
  margin: 0;
  font-family: var(--font-sans-ext);
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-indent: 0.2em;
  color: rgba(255, 255, 255, 0.5);
  opacity: 1;
  font-variant-numeric: tabular-nums;  /* le chiffre ne danse pas en montant */
}

@media (max-width: 860px) {
  /* ---- le hero de l'accueil ---------------------------------------------
     :not(.hero--fiche) est indispensable : les seize pages d'espace
     partagent la classe .hero et posent align-items: stretch avec la meme
     specificite. Sans cette exclusion, la regle d'ici, ecrite plus loin
     dans le fichier, l'emporterait sur elles. */
  .hero:not(.hero--fiche) { align-items: center; }
  .hero:not(.hero--fiche) .hero__inner {
    text-align: center;
    padding-block: calc(var(--space-2xl) + 3rem) var(--space-2xl);
  }
  .hero:not(.hero--fiche) .hero__title,
  .hero:not(.hero--fiche) .hero__sub {
    margin-inline: auto;
  }
  .hero:not(.hero--fiche) .hero__title { max-width: 15ch; }
  .hero:not(.hero--fiche) .hero__sub { max-width: 38ch; }
  .hero:not(.hero--fiche) .hero__actions {
    justify-content: center;
    margin-top: var(--space-lg);
  }
  /* Le son se commande dans la section du film, plus bas, ou il a un sens.
     Ici il agissait sur un fond muet que personne ne regarde. */
  .hero:not(.hero--fiche) .hero__son { display: none; }

  /* Le texte n'est plus dans le bas du cadre, la ou le degrade etait le
     plus sombre : il est au milieu, sur le plan lui-meme. Le voile monte
     donc, et le degrade appuie au centre autant qu'aux bords. */
  .hero:not(.hero--fiche) .hero__media::before {
    background: rgba(0, 0, 0, 0.44);
  }
  .hero:not(.hero--fiche) .hero__media::after {
    background:
      linear-gradient(to bottom,
        rgba(0,0,0,0.62) 0%,
        rgba(0,0,0,0.38) 20%,
        rgba(0,0,0,0.34) 46%,
        rgba(0,0,0,0.44) 68%,
        rgba(0,0,0,0.70) 86%,
        rgba(0,0,0,0.92) 96%,
        rgba(0,0,0,1) 100%);
  }
}

/* ---- le film, sur telephone ----------------------------------------------
   Pleine largeur, dans sa proportion reelle (16/9), avec ses commandes :
   le son se donne ici, volontairement, au lieu d'etre impose a l'arrivee.
   La section n'existe nulle part ailleurs. */
.film-mobile { display: none; }
@media (max-width: 860px) {
  .film-mobile { display: block; }
}
.film-mobile__cadre {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  background: #000;
  overflow: hidden;
}
/* Ce qui s'affiche d'abord est une image fixe, tiree du film lui-meme.
   Rien de YouTube n'est charge tant que personne n'a clique. */
.film-mobile__couverture {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: opacity 600ms var(--ease);
}

/* L'invitation couvre toute l'image : la cible est le film entier, pas
   deux mots. Le mot est en capitales largement espacees, comme les
   eyebrows du site : c'est la meme voix. */
.film-mobile__jouer {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.75rem;
  padding: 0;
  border: 0;
  background: rgba(0, 0, 0, 0.26);
  color: var(--gk-white);
  cursor: pointer;
  transition: background-color 500ms var(--ease), opacity 600ms var(--ease);
}
.film-mobile__jouer:hover { background: rgba(0, 0, 0, 0.14); }
.film-mobile__icone { display: block; width: 0.7rem; height: 0.7rem; }
.film-mobile__icone svg {
  display: block;
  width: 100%;
  height: 100%;
  fill: currentColor;
  filter: drop-shadow(0 1px 10px rgba(0, 0, 0, 0.8));
}
.film-mobile__mot {
  font-family: var(--font-sans-ext);
  font-size: 0.6875rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.24em;
  text-indent: 0.24em;   /* rattrape l'espace que le suivi ajoute a droite */
  text-shadow: 0 1px 12px rgba(0, 0, 0, 0.85);
}

/* Le lecteur ne vient qu'au clic, et recouvre la couverture.

   Il deborde d'un pixel de chaque cote, et le cadre le rogne. La raison :
   le cadre fait 16/9 d'une largeur d'ecran, donc une hauteur fractionnaire
   (375 / 16 * 9 = 210,9375 px). YouTube cale sa video dans cette hauteur
   avec ses propres arrondis, et laisse parfois une rangee d'un pixel plus
   claire ou plus sombre sur la tranche. Notre couverture, elle, est nette
   au pixel pres -- mesure a l'echelle 1 comme a l'echelle 3. Faire deborder
   le lecteur met cette rangee hors du cadre, ou elle est coupee. */
.film-mobile__lecteur {
  position: absolute;
  inset: -1px;
  display: block;
  width: calc(100% + 2px);
  height: calc(100% + 2px);
  border: 0;
}
.film-mobile__cadre.est-lance .film-mobile__couverture,
.film-mobile__cadre.est-lance .film-mobile__jouer {
  opacity: 0;
  pointer-events: none;
}

/* Les commandes sont celles de YouTube : lecture, pause, plein ecran,
   volume. Le lecteur recoit donc les gestes normalement. Seule la
   couverture, avant le clic, tient le dessus. */
.film-mobile__lecteur { z-index: 1; }
.film-mobile__couverture { z-index: 2; }
.film-mobile__jouer { z-index: 3; }

/* ---- la section encre, sur telephone --------------------------------------
   Elle etait composee pour le paysage : 88vh de haut, huit rem de marge
   interne, et une grille a min-height: 62vh. Les trois rangees -- titre,
   prose, legende -- sont en hauteur automatique, donc elles s'etiraient
   pour remplir cette hauteur imposee : c'est de la que venaient les grands
   vides entre les blocs, et non d'une marge qu'on aurait pu reduire.

   Sur un ecran vertical, la hauteur suit le contenu, et rien ne s'etire. */
@media (max-width: 860px) {
  .ink-hero {
    min-height: 0;
    padding-block: var(--space-2xl);
  }
  .ink-hero__content {
    min-height: 0;
    align-content: start;   /* les rangees ne remplissent plus l'espace */
    gap: var(--space-md);
  }
  /* Le titre etait tenu a 18ch pendant que la prose courait sur toute la
     largeur : il cassait a mi-ecran et paraissait comprime a cote d'elle.
     Il prend la largeur du texte qu'il annonce.

     Ses coupures de ligne sont dessinees pour le paysage : sur un ecran
     etroit elles tombent n'importe ou. On les retire, et le titre se
     casse tout seul. Le texte porte une espace avant chaque <br> : sans
     elle, masquer la balise collerait les deux mots qu'elle separait
     ("inventa new"). Sur grand ecran cette espace ne se voit pas, la
     ligne casse a cet endroit precis. */
  .ink-hero__title {
    max-width: none;
    line-height: 1.2;
  }
  .ink-hero__title br { display: none; }
  .ink-hero__prose { margin-top: 0; }
  .ink-hero__caption { margin-top: var(--space-sm); }
}

/* ---- les quatre faits, sur telephone --------------------------------------
   Quatre lignes plutot qu'une grille deux par deux : la valeur a gauche, sa
   legende a droite, posees sur la meme ligne de base. La grille laissait des
   cellules a moitie vides -- une valeur courte a cote d'une valeur sur deux
   lignes -- et etirait la section sur presque deux ecrans.

   Les selecteurs :not(:first-child) et :nth-child() des regles precedentes
   pesent plus qu'un simple ".stats > div" : il faut les reprendre tels quels
   pour les defaire, sinon la marge a gauche revient sur trois lignes sur
   quatre. */
@media (max-width: 860px) {
  .stats {
    grid-template-columns: 1fr;
    border-top: 0;
  }
  .stats > div,
  .stats > div:not(:first-child),
  .stats > div:nth-child(2n+1) {
    display: flex;
    flex-direction: row;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-sm);
    padding: var(--space-md) 0;
    border-right: 0;
    border-top: 1px solid rgba(255, 255, 255, 0.14);
  }
  /* Seuls les filets qui separent deux faits restent : celui du haut et
     celui du bas encadraient la section comme un tableau. */
  .stats > div:first-child { border-top: 0; }
  .stats .stat__value { font-size: 1.15rem; }
  .stats .stat__label {
    margin-top: 0;       /* defait le margin-top: auto de la version colonne */
    padding-top: 0;
    text-align: right;
    flex: none;
  }

  /* Le film et les quatre faits sont deux sections voisines : leurs marges
     s'additionnaient, cent cinquante pixels de vide entre l'image et le
     premier filet. */
  .film-mobile { padding-bottom: 0; }
  .film-mobile + .section { padding-top: var(--space-lg); }
}



/* ---- les cartes modele : l'image nait en s'ouvrant ------------------------
   L'image arrivait telle quelle, a sa taille finale, pendant que la carte
   se revelait autour d'elle. Elle part maintenant legerement agrandie et
   rejoint sa taille en meme temps que la carte parait : un zoom arriere,
   double du fondu que toute image differee a deja (gk-nee) une fois
   reellement chargee.

   Le zoom passe par une variable, et non par une regle sur l'image. La
   raison : la carte a deja un zoom au survol, sur la meme propriete. Deux
   regles concurrentes sur transform, l'une l'emporte et l'autre disparait
   -- le survol serait mort. Avec une variable, chaque etat ecrit sa valeur
   et la transition de l'image, elle, ne bouge pas. */
.model-card__media img,
.model-card--link .model-card__media img { transform: scale(var(--gk-zoom, 1)); }

.model-card[data-reveal] { --gk-zoom: 1.06; }
.model-card[data-reveal].is-visible { --gk-zoom: 1; }
/* Le survol doit peser plus que l'etat revele, sinon il ne s'applique
   jamais une fois la carte apparue. */
.model-card--link[data-reveal].is-visible:hover { --gk-zoom: 1.045; }

@media (prefers-reduced-motion: reduce) {
  .model-card[data-reveal],
  .model-card[data-reveal].is-visible { --gk-zoom: 1; }
}


/* ---- la section studio, sur telephone -------------------------------------
   Une seule carte, et posee avant le chapeau. Les trois empilees sous le
   texte rallongeaient la section d'un ecran entier : on faisait defiler
   trois vignettes avant d'arriver a la suite.

   Cette regle vit en fin de fichier, et pas a cote des autres regles
   studio, pour une raison precise : ".studio__card { display: block }" est
   declare plus bas dans la feuille. A specificite egale, c'est la derniere
   regle lue qui l'emporte, et une requete media n'ajoute aucun poids. Ecrite
   plus haut, "display: none" etait donc ignoree, sans que rien ne le
   signale. */
@media (max-width: 700px) {
  .studio__card--floor,
  .studio__card--lounge { display: none; }
  .studio__card--gallery {
    display: block;
    order: -2;              /* le chapeau porte -1 : la carte passe devant */
    margin-bottom: var(--space-sm);
  }
}
