/* ═══════════════════════════════════════════════════════════
   TEMPLATE « SITE IMMERSIF » — feuille de style canonique
   NE PAS MODIFIER en dehors des tokens :root (thème + accent).
   Le thème se choisit par <html data-theme="dark"> (light par
   défaut) ; l'accent par --lime / --lime-ink.
   ═══════════════════════════════════════════════════════════ */

:root {
  /* ═══ THÈME LIGHT (défaut) — DA Kaalyo « indigo-ink ledger » ═══ */
  --ink-rgb: 6, 27, 49;           /* Midnight Ink #061b31 en composantes RGB */
  --bone: #ffffff;                /* Pure White — toile de fond */
  --ink: #061b31;                 /* Midnight Ink — texte principal */
  --ash: #5e6e86;                 /* Slate, un cran plus sombre que #64748d : ≥ 4,9:1 sur blanc comme sur Mist */
  --white: #f8fafd;               /* Mist — surfaces élevées (header, panneau, tuiles) */

  /* ═══ ACCENT — Indigo Ink, texte blanc posé dessus ═══ */
  --lime: #533afd;                /* accent (bouton CTA, bande devise, éléments accent) */
  --lime-ink: #ffffff;            /* texte posé SUR l'accent */

  --ink-faint: rgba(var(--ink-rgb), 0.16);

  /* Famille unique (substitut de sohne-var), labels compris */
  --sans: 'Inter Tight', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  --geo: var(--sans);
  --mono: var(--sans);

  --t-heading: clamp(34px, 4.6vw, 57px);
  --radius: 4px;
  --gutter: clamp(20px, 4vw, 64px);

  --ease-out: cubic-bezier(0.19, 1, 0.22, 1);
  --ease-inout: cubic-bezier(0.77, 0, 0.175, 1);
}

/* ═══ THÈME DARK — activé par <html data-theme="dark"> ═══ */
html[data-theme="dark"] {
  --ink-rgb: 242, 241, 236;
  --bone: #141311;
  --ink: #f2f1ec;
  --ash: #8f938f;
  --white: #201f1c;
  --ink-faint: rgba(var(--ink-rgb), 0.16);
}

*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
html { scroll-behavior: auto; }
body {
  background: var(--bone);
  color: var(--ink);
  font-family: var(--sans);
  font-weight: 400;
  font-size: 19px;
  letter-spacing: -0.007em;
  line-height: 1.3;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
}
/* un mot trop long pour sa colonne (nom d'outil, e-mail) passe à la
   ligne au lieu de déborder — sans effet sur les lignes insécables */
body { overflow-wrap: break-word; }
img { display: block; max-width: 100%; border-radius: var(--radius); }

/* focus clavier : partout le même anneau indigo, jamais retiré */
:focus-visible { outline: 2px solid var(--lime); outline-offset: 3px; }
/* cible d'une ancre (section focalisée par app.js) : pas d'anneau géant */
[tabindex="-1"]:focus { outline: none; }

/* lien d'évitement : invisible jusqu'au premier Tab */
.skip-link {
  position: fixed; top: 8px; left: 8px; z-index: 300;
  padding: 10px 16px; border-radius: 999px;
  background: var(--lime); color: var(--lime-ink);
  font-size: 15px; font-weight: 500;
  transform: translateY(-160%);
  transition: transform .2s var(--ease-out);
}
.skip-link:focus-visible { transform: none; outline-offset: 2px; }

/* image introuvable (marquée par le script de tête d'index.html) : pas
   d'icône cassée ; une tuile d'outil affiche l'initiale de l'outil */
img.is-broken { visibility: hidden; }
:where(.has-broken[data-initial]) { position: relative; }   /* sans écraser un absolute */
.has-broken[data-initial]::before {
  content: attr(data-initial);
  position: absolute; inset: 0;
  display: grid; place-items: center;
  border-radius: inherit;
  background: #ffffff;
  box-shadow: 0 0 0 1px rgba(var(--ink-rgb), 0.08);
  font-size: 17px; font-weight: 500; color: var(--ink);
}
.sys-tool.has-broken::before { font-size: calc(var(--node) * 0.42); }
.net-tool.has-broken::before { font-size: calc(var(--tile, 56px) * 0.4); }

/* sans JavaScript : pas de loader, pas de scènes vides — le titre, le
   bouton, puis le pied de page avec le lien cal.com et l'e-mail */
html:not(.js) .loader,
html:not(.js) .hero-visual, html:not(.js) .hero-flow, html:not(.js) .spot,
html:not(.js) #systemes, html:not(.js) #explorer,
html:not(.js) #temoignage, html:not(.js) #apropos,
html:not(.js) .dock-nav, html:not(.js) .dock-burger, html:not(.js) .footer-cal { display: none; }
html:not(.js) .dock { transform: none; }
html:not(.js) .hero-line2 { display: block; }   /* comme en mode statique */
html:not(.js) .s-hero .grow { display: none; }
html:not(.js) .cal-fallback { display: block; }

/* filet de sécurité (scripts en échec) : mode statique, calendrier remplacé
   par son secours, tout ce qui dépendait d'un script est retiré */
body.is-rescue .footer-cal, body.is-rescue .hero-visual, body.is-rescue .hero-flow,
body.is-rescue .ribbon-layer, body.is-rescue .net-stage { display: none; }
body.is-rescue .cal-fallback { display: block; }
ul { list-style: none; }
a { color: inherit; text-decoration: none; }
h1, h2, h3 { font-weight: 400; }

.mono {
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.03em;
  line-height: 1.4;
  text-transform: uppercase;
  font-weight: 400;
}
.ash { color: var(--ash); }

/* ═══════════ SMOOTH WRAPPER ═══════════ */
body.is-smooth .smooth {
  position: fixed;
  top: 0; left: 0;
  width: 100%;
  z-index: 1;              /* devant les écharpes (.ribbon-layer) */
  will-change: transform;
}

/* ═══════════ PRELOADER ═══════════ */
/* preloader : le nom + un trait qui se remplit. À la fin le voile
   s'efface et le nom REJOINT sa place dans le dock — la page ne se
   substitue pas au loader, elle en sort. */
.loader {
  position: fixed; inset: 0;
  z-index: 200;
  display: flex; align-items: center; justify-content: center;
}
.loader::before {
  content: ''; position: absolute; inset: 0;
  background: var(--bone);
  transition: opacity .75s var(--ease-inout);
}
.loader.done { pointer-events: none; }
.loader.done::before { opacity: 0; }
.loader-inner {
  position: relative;
  display: flex; flex-direction: column; align-items: center; gap: 20px;
}
.loader-wordmark {
  font-size: 23px; letter-spacing: -0.007em; font-weight: 500;
  transform-origin: center center;
  transition: transform .9s var(--ease-inout);
  will-change: transform;
}
/* posé : le nom du loader s'efface pendant que le dock se révèle, sur la
   MÊME durée et au même endroit — un fondu croisé, jamais une bascule
   sèche ni un instant sans nom à l'écran */
.loader-wordmark.landed { opacity: 0; transition: opacity .32s var(--ease-out); }
.loader-rule {
  display: block; width: 128px; height: 1px;
  background: var(--ink-faint); overflow: hidden;
  transition: opacity .3s var(--ease-out), transform .5s var(--ease-inout);
}
.loader.done .loader-rule { opacity: 0; transform: scaleX(0.4); }
.loader-rule i {
  display: block; width: 100%; height: 100%;
  background: var(--ink);
  transform: scaleX(0); transform-origin: left;
}
/* pendant le relais le dock est déjà à sa place, simplement invisible :
   il ne descend pas en même temps que le wordmark vole vers lui. */
body.handoff .dock {
  transform: translateY(0);
  opacity: 0;
  transition: none;
}

/* ═══════════ SCÈNES (pin émulé) ═══════════ */
.scene { position: relative; }
.scene .pin {
  height: 100vh;
  position: relative;
  overflow: hidden;
  will-change: transform;
}

/* ═══════════ RÉVÉLATIONS GÉNÉRIQUES ═══════════ */
.reveal {
  opacity: 0; transform: translateY(24px);
  transition: opacity 1s var(--ease-out), transform 1s var(--ease-out);
  transition-delay: calc(var(--i, 0) * 90ms);
}
.reveal.in-view { opacity: 1; transform: translateY(0); }

/* ═══════════ S1 · HÉROS — texte | carte to-do ═══════════ */
/* conteneur de requête : 100cqw = largeur utile, barre de défilement
   exclue (100vw la compte) — pour mesurer la marge gauche réelle du titre */
/* transparent : la bande des systèmes, qui monte dessus, laisse voir son
   écharpe (app.js coupe ce bloc au bord de la bande) — le blanc vient du body */
.s-hero .pin { background: transparent; container-type: inline-size; }

.hero-grid {
  position: absolute; inset: 0;
  z-index: 2;
  display: grid;
  /* minmax(0, …) : la colonne ne s'élargit pas quand l'image grandit dans
     la ligne insécable du titre — sinon la ligne 1 se redéroule et le
     zoom se décale */
  grid-template-columns: minmax(0, 45fr) minmax(0, 55fr);
  grid-template-rows: minmax(0, 1fr);
  align-items: center;
  --hero-top: 64px;                /* place de la barre flottante */
  /* au-delà de 1240 px, le texte recule dans la marge de la page */
  --hero-shift: clamp(0px, (100vw - 1240px) / 2, 64px);
  /* tout le bloc texte + carte recule encore vers la gauche (écart entre
     eux inchangé) pour laisser plus de place au ruban à droite de la carte ;
     le titre garde au moins max(32px, 3vw) du bord de l'écran */
  --hero-edge: calc(max(0px, (100cqw - 1240px) / 2) + var(--gutter) - var(--hero-shift));   /* marge gauche du titre sans recul */
  --hero-pull0: clamp(0px, var(--hero-edge) - max(32px, 3vw), 48px);
  /* puis la marge restante est réduite d'un tiers (demande de Jules) */
  --hero-pull: calc(var(--hero-pull0) + (var(--hero-edge) - var(--hero-pull0)) / 3);
  column-gap: clamp(40px, 6vw, 96px);
  width: 100%; max-width: 1240px;
  margin: 0 auto;
  padding: var(--hero-top) var(--gutter) 0;
}
/* colonne sur toute la hauteur de l'écran (elle remonte sous la barre),
   contenu centré : quand l'image grandit dans le titre, la colonne déborde
   autant en haut qu'en bas — le zoom reste calé au milieu de l'écran.
   « gardez ce qui compte. » est posée SUR la ligne médiane : le titre
   remonte d'une ligne (marge -1lh) et la signature sous le titre rend
   sa hauteur par sa marge basse, si bien que seule la ligne 2 compte
   pour le centrage. Au scroll, elle glisse donc vers le centre à
   l'horizontale, sans remonter.
   La colonne recule à gauche sans s'élargir à droite (--hero-shift) : de
   l'air entre le titre et la carte. Puis texte et carte reculent ensemble
   (--hero-pull, décalage relatif : la grille ne bouge pas, le ruban se
   recale sur la carte mesurée).
   Conteneur de requête : le titre se dimensionne sur sa largeur (cqi). */
.hero-copy {
  position: relative; left: calc(var(--hero-pull) * -1);
  align-self: stretch;
  min-height: 0;
  margin-top: calc(var(--hero-top) * -1);
  margin-left: calc(var(--hero-shift) * -1);
  container-type: inline-size;
  /* taille du titre (cqi = cette colonne) */
  --title-fs: min(var(--t-heading), 10.2cqi);
  --title-lh: 1.12;
  display: flex; flex-direction: column;
  justify-content: center; align-items: flex-start;
  text-align: left;
}
/* sous le titre : pour qui, puis l'action. Hors du flux, juste sous la
   ligne 2 (posée sur la ligne médiane, cf. ci-dessus) : le centrage du
   titre n'en dépend pas, et le zoom reste calé au milieu de l'écran. */
.hero-foot {
  position: absolute; left: 0; right: 0;
  top: calc(50% + var(--title-fs) * var(--title-lh) / 2 + clamp(20px, 3vh, 28px));
  will-change: opacity;
}
.hero-for {
  max-width: 30em;
  font-size: 18px; font-weight: 400; line-height: 1.45; letter-spacing: -0.005em;
  color: var(--ash);
  text-wrap: pretty;
}
.hero-act {
  display: flex; flex-wrap: wrap; align-items: center; gap: 12px 20px;
  margin-top: 28px;
}

/* le bouton : une seule action pleine sur la page */
.btn {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 46px; padding: 0 24px;
  border-radius: 999px;
  background: var(--lime); color: var(--lime-ink);
  font-size: 16px; font-weight: 500; letter-spacing: -0.005em; line-height: 1;
  transition: background-color .25s var(--ease-out);
}
.btn:hover { background: #4329e0; }
.btn:active { background: #3a22c9; }
.btn:focus-visible { outline: 2px solid var(--lime); outline-offset: 3px; }
/* largeur figée sur la colonne : sans ça le titre s'élargit avec l'image
   qui grandit dans sa ligne insécable, et la ligne 1 se redéroule.
   Chaque ligne tient d'un seul tenant : « Automatisez le répétitif, »
   fait ~9,7 em, d'où le plafond à 10,2cqi (--title-fs, sur .hero-copy). */
.hero-title { width: 100%; margin-top: -1lh; font-size: var(--title-fs); letter-spacing: -0.02em; line-height: var(--title-lh); font-weight: 400; }
.hero-line1 { display: block; white-space: nowrap; will-change: opacity; }
.hero-line2 {
  display: flex; align-items: center; justify-content: flex-start;
  white-space: nowrap;
  will-change: transform;
}
.hero-line2 .hl { flex: 0 0 auto; }

/* la carte : c'est cette enveloppe que la scène agrandit et envoie à droite */
.hero-visual {
  position: relative; left: calc(var(--hero-pull) * -1);   /* suit le texte */
  /* centre de la carte sur la ligne médiane, comme « gardez ce qui compte. » :
     la grille est décalée vers le bas par la place de la barre (--hero-top) */
  top: calc(var(--hero-top) / -2);
  justify-self: center;
  width: 100%; max-width: 480px;
  will-change: transform;
}

/* image qui naît puis grandit dans le texte */
.grow {
  display: block; flex: 0 0 auto;
  overflow: hidden;
  border-radius: var(--radius);
  width: 0; height: 0;
  will-change: width, height;
  position: relative;
}
.grow img, .grow > .net-stage {
  position: absolute; top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  width: 100vw; height: 100vh;
  object-fit: cover;
  border-radius: 0;
  max-width: none;
}

/* calque série en lumière */
.spot { position: absolute; inset: 0; z-index: 4; pointer-events: none; }
/* la phrase : le vrai titre de la scène. Taille de titre, posée juste
   sous la ceinture du réseau (--net-bottom, mesuré par app.js) */
.spot-intro {
  position: absolute; left: 50%;
  top: calc(var(--net-bottom, 52vh) + clamp(28px, 6vh, 72px));
  width: min(1180px, calc(100% - 2 * var(--gutter)));
  transform: translateX(-50%);
  text-align: center;
  color: #ffffff;
  font-size: clamp(28px, 3.3vw, 50px); letter-spacing: -0.02em; line-height: 1.12;
  text-wrap: balance;
}
.spot-intro span { display: inline-block; will-change: opacity, transform; opacity: 0; }
/* ═══════════ CARTE TO-DO (todo-card.js) ═══════════
   Lignes de hauteur fixe et compteur à largeur fixe : rien ne bouge
   autour pendant que les tâches se cochent.
   Seule surface du site qui flotte : fond Mist (celui de la barre du
   haut) et ombre longue et diffuse, à peine perceptible — la carte se
   détache du blanc sans cadre appuyé. Pas de bordure : un filet de 1 px
   dans l'ombre dessine le bord. */
.todo-card {
  background: var(--white);
  color: var(--ink);
  border-radius: 14px;
  padding: 16px 18px 4px;
  box-shadow: var(--card-shadow);   /* recette partagée avec les fiches (surcouche DA) */
}
.todo-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 12px;
  padding-bottom: 12px;
}
.todo-title { font-size: 16px; font-weight: 600; letter-spacing: -0.01em; }
.todo-gain { font-size: 14px; font-weight: 600; color: var(--ash); white-space: nowrap; }
.todo-count {
  display: inline-block;
  min-width: 7ch;
  font-size: 28px;
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1;
  text-align: right;
  color: var(--todo-green);
  font-variant-numeric: tabular-nums;
}
.todo-count-val { display: inline-block; transform-origin: center; }
@media (max-width: 420px) {
  .todo-head { gap: 8px; }
  .todo-gain { font-size: 13px; }
  .todo-count { min-width: 6.6ch; }
}
.todo-row {
  display: flex; align-items: center; gap: 12px;
  height: 44px;
  border-top: 1px solid var(--frost);
}
.todo-box {
  flex: 0 0 auto;
  display: grid; place-items: center;
  width: 18px; height: 18px;
  border: 1.5px solid #b4bfcc;
  border-radius: 50%;
  transition: background-color .12s ease-out, border-color .12s ease-out;
}
.todo-check {
  width: 12px; height: 12px;
  opacity: 0; transform: scale(.6);
  transition: opacity .12s ease-out, transform .25s cubic-bezier(.34, 1.56, .64, 1);
}
.todo-check path { fill: none; stroke: #ffffff; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.todo-label {
  min-width: 0;
  font-size: 14px; font-weight: 400; line-height: 1.3;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  text-decoration: line-through 1px transparent;
  transition: color .2s ease-out, text-decoration-color .2s ease-out;
}
.todo-row.is-pop .todo-box { animation: todo-pop 420ms cubic-bezier(.34, 1.56, .64, 1); }
.todo-row.is-done .todo-box { background: var(--todo-green); border-color: var(--todo-green); }
.todo-row.is-done .todo-check { opacity: 1; transform: none; }
.todo-row.is-done .todo-label { color: var(--ash); text-decoration-color: currentColor; }
@keyframes todo-pop {
  0%   { transform: scale(1); }
  28%  { transform: scale(.8); }
  68%  { transform: scale(1.12); }
  100% { transform: scale(1); }
}

/* texte réservé aux lecteurs d'écran */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden; clip: rect(0 0 0 0);
  white-space: nowrap; border: 0;
}

/* ═══════════ S2 · SYSTÈMES — exemples sur mesure, en train horizontal ═══════════ */
/* La bande du ruban monte par-dessus le réseau pendant son dernier écran
   (marge -100vh), puis s'épingle : en-tête en haut, fiches au milieu,
   compteur en bas. Les fiches glissent à l'horizontale (app.js), chacune
   marque un palier au centre de l'écran. */
.band { position: relative; z-index: 3; margin-top: -100vh; }
.diag { height: 12vw; position: relative; margin: -1px 0; }
.to-lime   { background: linear-gradient(to bottom right, transparent 49.7%, var(--lime) 50.3%); }
.from-lime { background: linear-gradient(to bottom right, var(--lime) 49.7%, transparent 50.3%); }

.s-systems .pin {
  background: var(--lime);
  color: var(--lime-ink);
  display: flex; flex-direction: column;
  /* sous la barre (48 px), puis un peu d'air */
  padding: calc(48px + clamp(20px, 5.5vh, 60px)) 0 clamp(20px, 4.5vh, 44px);
}
.sys-head { flex: none; text-align: center; padding: 0 var(--gutter); }
.sys-kicker { opacity: 0.85; }
.sys-title { font-size: var(--t-heading); letter-spacing: -0.02em; line-height: 1.08; margin: 14px 0 12px; }
.sys-sub { max-width: 52ch; margin: 0 auto; opacity: 0.88; text-wrap: balance; }

/* scène des fiches : le train est centré verticalement dans la place
   laissée entre l'en-tête et le compteur */
.sys-stage { position: relative; flex: 1; min-height: 0; }
.sys-track {
  position: absolute; top: 50%; left: 0;
  /* stretch : toutes les fiches prennent la hauteur de la plus haute */
  display: flex; align-items: stretch;
  gap: clamp(20px, 3.5vw, 56px);
  will-change: transform;
}

/* une fiche : blanche, posée sur le ruban. Minimaliste, centrée, lue de
   haut en bas : illustration, « secteur · rythme », titre, puis le
   workflow sur une ligne (outils → agent IA → outil). Aucun cadre interne. */
.sys-card {
  flex: 0 0 auto;
  width: min(720px, 64vw);
  min-height: clamp(330px, 48vh, 450px);
  display: flex;
  background: #ffffff;
  color: var(--ink);
  border-radius: 14px;
  will-change: transform;
  --art-h: clamp(76px, 13vh, 120px);
}
.sys-inner {
  flex: 1;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  text-align: center;
  padding: clamp(24px, 4.4vh, 48px) clamp(22px, 3vw, 40px);
  will-change: opacity;
}
.sys-art {
  display: block;
  width: auto; max-width: 100%;
  height: var(--art-h);
  object-fit: contain;
  margin-bottom: clamp(14px, 2.6vh, 26px);
}
/* fiche finale : illustration sur une bande moins haute (84 au lieu de 140,
   même échelle de trait), pour que la 2e ligne du titre ne grandisse pas
   toutes les fiches (elles prennent la hauteur de la plus haute) */
.is-outro .sys-art { height: calc(var(--art-h) * 0.6); }
.sys-sector { color: var(--ash); }
.sys-name { margin-top: 10px; font-size: clamp(28px, 3vw, 44px); letter-spacing: -0.02em; line-height: 1.08; text-wrap: balance; }
/* seconde ligne du titre (fiche finale), en gris */
.sys-name-2 { display: block; color: var(--ash); }

/* le workflow : trois points sur une ligne, reliés par un trait. Un point
   s'allume (l'agent y travaille), puis se coche en vert et le trait vers le
   suivant se remplit, comme la carte to-do du héros. */
.sys-steps {
  --node: 38px;
  list-style: none;
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  width: min(580px, 100%);
  margin-top: clamp(24px, 4.6vh, 46px);
}
.sys-step { position: relative; display: flex; flex-direction: column; align-items: center; gap: 12px; }
/* trait d'un centre au suivant, masqué sous les points (fond blanc) */
.sys-step:not(:last-child)::before,
.sys-step:not(:last-child)::after {
  content: ''; position: absolute;
  top: calc(var(--node) / 2 - 0.75px); left: 50%; right: -50%;
  height: 1.5px;
  background: rgba(var(--ink-rgb), 0.12);
}
.sys-step:not(:last-child)::after {
  background: var(--todo-green);
  transform: scaleX(0); transform-origin: left;
  transition: transform .45s var(--ease-out);
}
.sys-step.is-done::after { transform: scaleX(1); }
.sys-node {
  position: relative; z-index: 1;
  height: var(--node);
  display: flex; align-items: center; gap: 5px;
  padding: 0 10px;
  background: #ffffff;
}
.sys-node > :not(.sys-dot) { opacity: 0.4; transition: opacity .3s var(--ease-out), border-color .2s ease-out; }
.sys-step.is-on .sys-node > :not(.sys-dot),
.sys-step.is-done .sys-node > :not(.sys-dot) { opacity: 1; }
/* les outils : les vrais logos, en icônes d'app comme dans le réseau */
.sys-tool {
  flex: none;
  width: var(--node); height: var(--node);
  display: grid; place-items: center;
  border-radius: 24%;
  background: #ffffff;
  box-shadow: 0 0 0 1px rgba(var(--ink-rgb), 0.08);
}
.sys-tool img { width: 64%; height: 64%; object-fit: contain; border-radius: 0; }
.sys-tool.is-fill { background: transparent; }
.sys-tool.is-fill img { width: 100%; height: 100%; object-fit: cover; border-radius: inherit; }
/* l'agent IA : un rond, cerclé d'indigo quand il travaille */
.sys-agent {
  width: var(--node); height: var(--node);
  display: grid; place-items: center;
  border-radius: 50%;
  border: 1.5px solid rgba(var(--ink-rgb), 0.16);
  font-size: 13px; letter-spacing: 0;
}
.sys-step.is-on .sys-agent, .sys-step.is-done .sys-agent { border-color: var(--lime); }
/* pictogramme neutre, quand aucun outil ne parle */
.sys-pict {
  width: var(--node); height: var(--node);
  display: grid; place-items: center;
  border-radius: 24%;
  box-shadow: 0 0 0 1px rgba(var(--ink-rgb), 0.08);
}
.sys-pict svg { width: 55%; height: 55%; fill: none; stroke: var(--ink); stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
/* la coche : petite pastille verte au coin du point */
.sys-dot {
  position: absolute; top: -6px; right: 2px;
  width: 17px; height: 17px;
  display: grid; place-items: center;
  border-radius: 50%;
  background: var(--todo-green);
  box-shadow: 0 0 0 2px #ffffff;
  opacity: 0; transform: scale(.5);
  transition: opacity .12s ease-out, transform .25s cubic-bezier(.34, 1.56, .64, 1);
}
.sys-dot svg { width: 11px; height: 11px; }
.sys-dot path { fill: none; stroke: #ffffff; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.sys-step.is-done .sys-dot { opacity: 1; transform: none; }
/* étape pas encore jouée : en gris plein (lisible, ≥ 4,5:1), l'encre marque
   l'étape en cours — la différence passe par la teinte, pas l'opacité */
.sys-step-txt { font-size: 15px; line-height: 1.3; color: var(--ash); transition: color .3s var(--ease-out); }
.sys-step.is-on .sys-step-txt, .sys-step.is-done .sys-step-txt { color: var(--ink); }

/* compteur : « EXEMPLE 02 / 06 » et un trait par fiche (fiche finale comprise) */
.sys-rail { flex: none; display: flex; flex-direction: column; align-items: center; gap: 12px; }
.sys-count { min-height: 1.4em; opacity: 0.9; transition: opacity .25s; }
.sys-count.swap { opacity: 0; }
.sys-ticks { display: flex; align-items: center; gap: 6px; }
.sys-ticks i { width: 22px; height: 2px; border-radius: 1px; background: rgba(255, 255, 255, 0.35); transition: background-color .3s; }
.sys-ticks i.on { background: #ffffff; }

/* tablette et mobile : la fiche prend presque toute la largeur ; la voisine dépasse à
   peine, assez pour dire que ça défile */
@media (max-width: 900px) {
  .sys-track { gap: 14px; }
  .sys-card { width: calc(100vw - 2 * var(--gutter) - 12px); max-width: 640px; min-height: 0; border-radius: 12px; }
  .sys-inner { padding: 28px 20px; }
  .sys-name { font-size: clamp(24px, 6.6vw, 34px); }
}
@media (max-width: 767px) {
  .s-systems .pin { padding: calc(52px + 3vh) 0 16px; }
  .sys-title { margin: 10px 0 8px; }
  .sys-sub { font-size: 15px; }
  .sys-inner { padding: 24px 14px; }
  .sys-steps { --node: 30px; margin-top: 24px; }
  .sys-node { gap: 3px; padding: 0 6px; }
  .sys-step { gap: 8px; }
  .sys-step-txt { font-size: 13px; }
  .sys-rail { gap: 8px; }
}
/* petit téléphone : l'illustration rapetisse */
@media (max-width: 767px) and (max-height: 720px) {
  .sys-card { --art-h: 60px; }
  .sys-art { margin-bottom: 12px; }
}

/* écrans bas (portable 13", fenêtre réduite) : l'en-tête rend de la
   hauteur aux fiches */
@media (max-height: 760px) and (min-width: 768px) {
  .sys-title { font-size: min(var(--t-heading), 6.6vh); margin: 10px 0 8px; }
  .sys-sub { font-size: 17px; }
  .sys-card { --art-h: 64px; }
  /* processus : la carte (52vh) tient entre le sur-titre et le CTA ; le
     titre suit la hauteur pour laisser la place à la description */
  .pcard-name { font-size: clamp(34px, 6.4vh, 52px); }
}

/* ═══════════ S5 · MÉTHODE ═══════════ */
.s-night .pin { background: transparent; }   /* son écharpe passe derrière */
/* titre fixe au-dessus de la pile, présent dès l'arrivée sur la section */
.pcards-title {
  position: absolute; left: var(--gutter); right: var(--gutter); top: 15vh;
  text-align: center;
  font-size: var(--t-heading); letter-spacing: -0.02em; line-height: 1.1;
  z-index: 8;
}
/* les étapes en cartes : toutes au même endroit, chacune monte et recouvre
   la précédente (app.js) */
.pcards { position: absolute; inset: 0; z-index: 8; pointer-events: none; }
.pcard {
  position: absolute; left: 50%; top: 58%;
  width: min(1040px, calc(100% - 2 * var(--gutter)));
  height: min(50vh, 480px);
  padding: clamp(28px, 4vw, 56px);
  border-radius: 28px;
  overflow: hidden;
  color: var(--ink);
  transform: translate3d(-50%, calc(-50% + 100vh), 0);
  transform-origin: top center;
  will-change: transform;
}
.pcard-pill {
  position: absolute; top: clamp(20px, 2.6vw, 32px); right: clamp(20px, 2.6vw, 32px);
  padding: 6px 14px;
  border-radius: 99px;
  /* voile blanc sur le pastel : la pastille se lit comme un petit papier
     posé sur le tissu, texte en encre */
  background: rgba(255, 255, 255, 0.62);
  box-shadow: 0 0 0 1px rgba(var(--ink-rgb), 0.06);
  font-size: 13px; line-height: 1.3;
  white-space: nowrap;
}
/* texte en encre : la hiérarchie se fait par la taille ; le sur-titre en
   encre adoucie (au-dessus de 5,5:1 sur les trois pastels) */
.pcard-meta { margin-bottom: 14px; color: rgba(var(--ink-rgb), 0.72); }
.pcard-name {
  font-size: clamp(38px, 4.6vw, 64px);
  letter-spacing: -0.018em;
  line-height: 1.05;
  margin-bottom: 16px;
}
.pcard-desc { position: relative; z-index: 1; font-size: 19px; line-height: 1.45; max-width: 26em; }
/* numéro en filigrane, épais, posé en retrait des bords */
.pcard-num {
  position: absolute; right: clamp(28px, 4vw, 56px); bottom: clamp(24px, 3.4vw, 48px);
  font-size: min(22vh, 13vw);
  font-weight: 600; line-height: 0.8;
  letter-spacing: -0.04em;
  font-variant-numeric: tabular-nums;
  /* filigrane en lumière : un blanc voilé sur le pastel, comme un reflet
     sur le tissu du ruban — jamais un gris posé dessus */
  color: rgba(255, 255, 255, 0.72);
  pointer-events: none;
}

/* CTA de conversion en fin de processus */
.steps-cta {
  position: absolute; left: 50%; bottom: 5vh;
  transform: translateX(-50%);
  z-index: 12;   /* au-dessus des cartes qui montent du bas */
}

/* ═══════════ 9 · À PROPOS (scène épinglée) ═══════════ */
.s-about .pin {
  background: transparent;
  display: flex; align-items: center; justify-content: center;
}
/* photo à gauche, texte à droite : le miroir du héros */
.about-grid {
  position: relative; z-index: 2;
  display: grid; grid-template-columns: auto minmax(0, 1fr);
  align-items: end;
  gap: clamp(40px, 6vw, 96px);
  width: min(1120px, 88vw);
}
/* carte pastel (dégradé des étapes, teinte --about-* : surcouche DA) ;
   --about-pop = part de la photo qui dépasse au-dessus de la carte.
   La marge haute réserve cette place dans la grille. */
.about-card {
  --card-w: min(400px, 32vw, 58vh);
  --about-pop: 0.4;
  --card-r: 28px;
  position: relative;
  width: var(--card-w);
  height: calc(var(--card-w) * (1 - var(--about-pop)));
  margin: calc(var(--card-w) * var(--about-pop)) 0 0;
  border-radius: var(--card-r);
}
/* le haut reste ouvert (la tête sort), le bas et les côtés suivent les
   coins de la carte : la photo semble monter de l'intérieur */
.about-clip {
  position: absolute; left: 0; right: 0; bottom: 0;
  top: calc(var(--card-w) * var(--about-pop) * -1);
  clip-path: inset(0 round 0 0 var(--card-r) var(--card-r));
}
.about-photo {
  position: absolute; left: 0; bottom: 0;
  display: block; width: 100%; height: auto;
  will-change: transform;
}
.about-copy { min-width: 0; max-width: 620px; }
.about-kicker { margin-bottom: 16px; }
.about-title { font-size: var(--t-heading); letter-spacing: -0.02em; line-height: 1.1; }
/* l'ovale déborde de 0,3em : un peu d'air pour ne pas mordre « appelle » */
.about-title .pill { margin-left: 0.14em; }
.about-text {
  margin-top: clamp(20px, 2.4vw, 32px);
  font-size: clamp(19px, 1.55vw, 23px); line-height: 1.45;
  max-width: 30em;
}
.about-sign {
  margin-top: clamp(22px, 2.6vw, 36px);
  font-size: 17px; color: var(--ash);
  opacity: 0; will-change: opacity, transform;
}
.fs { color: var(--ink-faint); transition: color .5s; }
.fs.on { color: var(--ink); }
.pill { position: relative; white-space: nowrap; }
.pill-svg {
  position: absolute; inset: -0.2em -0.3em;
  width: calc(100% + 0.6em); height: calc(100% + 0.4em);
  overflow: visible;
}
.pill-svg path {
  fill: none; stroke: var(--ink); stroke-width: 1.3;
  vector-effect: non-scaling-stroke;
}
/* ═══════════ 8 · PREUVE SOCIALE (scène épinglée) ═══════════ */
.s-quote .pin {
  background: transparent;
  display: flex; align-items: center; justify-content: center;
}
/* témoignage : vidéo à gauche (facultative), parole à droite */
.q-layout {
  position: relative; z-index: 2;
  display: flex; align-items: center;
  gap: clamp(32px, 5vw, 80px);
  width: min(1180px, 88vw);
}
.q-media {
  position: relative; flex: none; margin: 0;
  width: min(clamp(220px, 24vw, 330px), calc(72vh * 9 / 16));
  aspect-ratio: 9 / 16;
  border-radius: 22px; overflow: hidden;
  background: var(--white);
  border: 1px solid var(--frost);
  opacity: 0; will-change: opacity, transform;
}
.q-video { display: block; width: 100%; height: 100%; object-fit: cover; }
.q-play {
  position: absolute; left: 50%; top: 50%;
  width: 68px; height: 68px; margin: -34px 0 0 -34px;
  border: 0; border-radius: 50%; cursor: pointer;
  background: var(--lime); color: var(--lime-ink);
  display: grid; place-items: center;
  transition: transform .4s var(--ease-out);
}
.q-play:hover { transform: scale(1.06); }
.q-play:focus-visible { outline: 2px solid var(--lime); outline-offset: 4px; }
.q-play svg { width: 26px; height: 26px; margin-left: 3px; fill: currentColor; }
.q-media.is-playing .q-play { opacity: 0; pointer-events: none; }
/* le résultat, la parole, le filet, le système — on voit le chiffre avant
   de lire qui le dit */
.q-stack { position: relative; z-index: 2; flex: 1; min-width: 0; width: min(980px, 84vw); }
.q-media + .q-stack .q-text { font-size: clamp(24px, 2.5vw, 40px); }
/* le chiffre à gauche, son sens à côté : lu d'une seule fixation. Seule
   touche de couleur du bloc, en violet accent — un aplat, pas un effet */
.q-result {
  display: flex; align-items: center;
  gap: clamp(16px, 1.8vw, 26px);
  margin-bottom: clamp(16px, 1.8vw, 24px);
  opacity: 0; will-change: opacity, transform;
}
/* filet sous le chiffre : il le sépare de la parole, comme celui qui
   sépare la parole du système */
.q-rule-top { margin-bottom: clamp(18px, 2.2vw, 28px); }
.q-fig {
  flex: none;
  font-size: clamp(44px, 5vw, 70px); line-height: 0.9;
  font-weight: 600; letter-spacing: -0.03em;
  color: var(--lime);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
/* le « × » en retrait : c'est le nombre qu'on lit */
.fig-sign { font-size: 0.62em; margin-right: 0.04em; vertical-align: 0.14em; }
.q-result-txt { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.q-result-label {
  font-size: clamp(19px, 1.85vw, 27px); line-height: 1.2;
  letter-spacing: -0.015em; color: var(--ink);
  text-wrap: balance;
}
.q-result-detail { font-size: 15px; line-height: 1.3; color: var(--ash); }
.q-block { position: relative; }
.q-text {
  font-size: clamp(25px, 3vw, 46px);
  line-height: 1.28; letter-spacing: -0.018em;
  color: var(--ink);
  opacity: 0; will-change: opacity, transform;
}
.q-name { margin-top: 16px; font-size: 18px; color: var(--ash); opacity: 0; will-change: opacity, transform; }
.q-sign { margin-top: clamp(26px, 3.4vw, 44px); }
.q-rule {
  display: block; height: 1px; background: var(--ink-faint);
  transform: scaleX(0); transform-origin: left;
}
/* le système qui a produit le chiffre, sur une rangée */
.q-steps { --node: 36px; margin-top: 22px; width: min(440px, 100%); opacity: 0; }
.q-steps .sys-step { gap: 0; }
.q-steps .sys-step-txt { margin-top: 12px; font-size: 14px; text-align: center; max-width: 100%; padding: 0 4px; text-wrap: balance; }
/* le trait se remplit en gris, pas en vert : rien ne rivalise avec le chiffre */
.q-steps .sys-step:not(:last-child)::after { background: var(--ash); }

/* ═══════════ FOOTER ═══════════ */
.footer {
  position: relative; z-index: 2;
  background: transparent;
  padding: 16vh var(--gutter) 28px;   /* bas resserré : la barre finit la page, sans vide dessous */
  text-align: center;
  overflow: hidden;
}
/* titre et marge suivent aussi la HAUTEUR d'écran : sur un portable
   (650-760 px utiles), titre + calendrier (570 px) tiennent dans l'écran.
   Inchangés dès 900 px de haut (marge : 45 px à 900, 18 px à 675) */
.footer-title {
  font-size: min(var(--t-heading), 6.4vh); letter-spacing: -0.02em; line-height: 1.1;
  margin: 0 0 clamp(18px, 12vh - 63px, 48px);
}
/* ce que coûte l'appel, avec qui : sous le calendrier */
.host-facts { display: flex; flex-wrap: wrap; justify-content: center; gap: 6px 22px; margin-top: 20px; font-size: 16px; font-weight: 400; letter-spacing: 0; }
.host-facts li { display: inline-flex; align-items: center; gap: 7px; }
.host-facts svg { width: 14px; height: 14px; flex: none; fill: none; stroke: var(--todo-green); stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
@media (max-width: 600px) { .host-facts { font-size: 15px; gap: 6px 18px; } }
/* calendrier cal.com (app.js) : place réservée jusqu'à `linkReady`, puis
   l'iframe prend sa propre hauteur — jamais de défilement imbriqué.
   Largeur : dès 1024 px d'iframe, le booker de cal.com passe en grande
   grille (280 + 479 + 280 = 1039 px) ; en dessous, en grille compacte
   (~762 px). Entre 1024 et ~1045 px il déborderait : on l'évite (cf. 1150px) */
.footer-cal {
  position: relative;
  width: 100%; max-width: 1080px;
  margin: 0 auto;
  min-height: 560px;
  text-align: left;
}
.footer-cal::before {
  content: attr(data-loading);
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  font-size: 12px; font-weight: 400; letter-spacing: 0.05em;
  color: var(--ash);
  transition: opacity .4s var(--ease-out);
  pointer-events: none;
}
.footer-cal iframe { opacity: 0; transition: opacity .6s var(--ease-out); }
.footer-cal.is-ready { min-height: 0; }
.footer-cal.is-ready::before { opacity: 0; }
.footer-cal.is-ready iframe { opacity: 1; }
/* secours (#calFallback) : caché tant que le calendrier tient ; montré
   s'il tarde (sous la place réservée), s'il échoue (seul), sans JS */
.footer-cal.is-failed { display: none; }
.cal-fallback {
  display: none;
  max-width: 34em; margin: 0 auto; padding: 28px 0 4px;
  font-size: 18px; font-weight: 400; line-height: 1.45; color: var(--ink);
  text-align: center; text-wrap: balance;
}
.cal-fallback.is-shown { display: block; }
.footer-cal.is-failed + .cal-fallback { padding-top: 8px; }
.cal-fallback a { white-space: nowrap; color: var(--lime); text-decoration: underline; text-underline-offset: 3px; text-decoration-thickness: 1px; }
@media (max-width: 1150px) { .footer-cal { max-width: 1016px; } }
/* sous 768 px d'iframe (fenêtre < ~823 px), cal.com empile infos, mois et
   créneaux, et ses jours, carrés, grandissent avec la largeur : 1 438 px
   de haut à 745 px de large. On le garde à la largeur d'un téléphone. */
@media (max-width: 822px) { .footer-cal { max-width: 420px; } }
.footer-bottom {
  display: flex; justify-content: space-between; gap: 24px;
  margin-top: 32px; padding-top: 19px;   /* le logo Cal.com sous le calendrier fait déjà la respiration */
  border-top: 1px solid var(--ink);
  text-align: left;
}

/* ═══════════ CHROME FIXE — barre pleine largeur, façon apple.com ═══════════
   Transparente en haut de page : juste le texte. Dès qu'on scrolle, un voile
   dépoli et un filet très fin la détachent du contenu qui passe dessous. */
.dock {
  position: fixed; top: 0; left: 0; right: 0;
  height: 48px;
  transform: translateY(-100%);
  display: flex; align-items: center; justify-content: center;
  gap: clamp(28px, 4vw, 48px);
  padding: 0 var(--gutter);
  background: transparent;
  border-bottom: 1px solid transparent;
  z-index: 100;
  transition: transform .9s var(--ease-out) .15s, opacity .32s var(--ease-out),
              background-color .4s var(--ease-out), border-color .4s var(--ease-out),
              backdrop-filter .4s var(--ease-out), -webkit-backdrop-filter .4s var(--ease-out);
}
body.is-ready .dock { transform: translateY(0); }
body.is-scrolled .dock {
  background: color-mix(in srgb, var(--bone) 72%, transparent);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
  backdrop-filter: saturate(180%) blur(20px);
  border-bottom-color: rgba(var(--ink-rgb), 0.08);
}
.dock-wordmark { font-size: 19px; letter-spacing: -0.02em; font-weight: 500; transition: color .4s var(--ease-out); }
.dock-nav { display: flex; gap: clamp(28px, 4vw, 48px); }
.dock-link, .dock-cta {
  font-size: 14px; font-weight: 500; letter-spacing: 0; line-height: 1;
  padding: 6px 0;
  transition: color .4s var(--ease-out);
}
.dock-link { color: rgba(var(--ink-rgb), 0.72); }
.dock-link:hover { color: var(--ink); }
/* le bouton du héros, à l'échelle de la barre */
.dock-cta.btn { min-height: 36px; padding: 0 16px; font-size: 14px; transition: background-color .25s var(--ease-out); }

/* au-dessus d'une photo plein cadre ou de la bande indigo, la barre passe
   en sombre : voile noir dépoli, texte clair — elle se fond dans l'image
   au lieu de poser une bande blanche dessus (app.js pose .dock-dark). */
body.dock-dark .dock { border-bottom-color: rgba(255, 255, 255, 0.10); }
body.dock-dark.is-scrolled .dock { background: rgba(10, 12, 18, 0.38); }
body.dock-dark .dock-wordmark { color: #fff; }
body.dock-dark .dock-link { color: rgba(255, 255, 255, 0.78); }
body.dock-dark .dock-link:hover { color: #fff; }

/* ═══════════ MODE STATIQUE (tactile / réduit / étroit) ═══════════ */
body.static .scene { height: auto !important; }
body.static .scene .pin { height: auto; overflow: visible; }

/* héros : flux normal, carte déjà cochée, titre sans vignette */
body.static .hero-grid { position: relative; inset: auto; min-height: 100vh; padding: 96px var(--gutter) 64px; }
body.static .hero-line1, body.static .hero-line2 { display: block; }
body.static .s-hero .grow { display: none; }
body.static .grow { width: 1.6em; height: 1em; margin: 0 0.18em; }
body.static .grow img { width: 100%; height: 100%; position: static; transform: none; object-fit: cover; }
body.static .spot { position: relative; inset: auto; height: 100vh; pointer-events: auto; overflow: hidden; }
body.static .spot .net-stage { position: absolute; inset: 0; width: 100%; height: 100%; transform: none; }
body.static .spot-intro { z-index: 80; }
body.static .spot-intro span { opacity: 1; transform: none; }

body.static .band { margin-top: 0; }
/* systèmes : les fiches s'empilent, workflows déjà cochés (app.js) */
body.static .s-systems .pin { display: block; padding: 10vh var(--gutter) 12vh; }
body.static .sys-stage { margin-top: 6vh; }
body.static .sys-track { position: static; transform: none !important; flex-direction: column; align-items: center; gap: 20px; }
body.static .sys-card { width: min(1000px, 100%); height: auto; transform: none !important; }
body.static .sys-inner { opacity: 1 !important; }
body.static .sys-rail { display: none; }

body.static .s-night .pin { padding: 10vh 0 0; }
/* les cartes passent dans le flux, l'une sous l'autre : sans ça elles
   restent superposées (absolues, hors champ pour la scène scrubée) */
body.static .pcards-title { position: static; padding: 0 var(--gutter) 5vh; }
body.static .pcards {
  position: static; pointer-events: auto;
  display: flex; flex-direction: column; align-items: center; gap: 16px;
  padding: 0 var(--gutter) 6vh;
}
body.static .pcard {
  position: relative; left: auto; top: auto;
  width: min(1040px, 100%); height: auto; min-height: 300px;
  opacity: 1 !important; transform: none !important;
}
body.static .steps-cta {
  position: static; opacity: 1 !important; transform: none !important;
  text-align: center; padding-bottom: 8vh;
}
body.static .s-quote .pin { padding: 12vh var(--gutter); }
body.static .q-result, body.static .q-text, body.static .q-name,
body.static .q-steps { opacity: 1; }
body.static .q-rule { transform: scaleX(1); }

body.static .s-about .pin { min-height: 60vh; padding: 14vh 0; }
body.static .fs { color: var(--ink); }
body.static .about-sign { opacity: 1; }

/* ═══════════ RESPONSIVE ÉTROIT ═══════════ */
@media (max-width: 900px) {
  :root {
    --gutter: 20px;
    --t-heading: clamp(27px, 6.6vw, 40px);
  }
  body { font-size: 17px; }

  /* ── barre : sur écran étroit, nom à gauche, liens à droite */
  .dock { justify-content: space-between; gap: 20px; height: 52px; white-space: nowrap; }
  .dock-nav { margin-left: auto; gap: 20px; }
  .dock-nav .dock-link:first-child { display: none; }
  .dock-link, .dock-cta { font-size: 15px; }
  .loader-rule { width: 96px; }

  /* ── héros : titre et surtitre resserrés (la pile texte/carte : < 768 px) */
  .hero-copy { --title-fs: min(clamp(26px, 7vw, 40px), 10.2cqi); }
  .hero-for { font-size: 16px; }
  /* la phrase sous le réseau : plus de retour forcé, elle s'équilibre */
  .spot-intro { font-size: clamp(26px, 7.4vw, 36px); line-height: 1.14; }
  .spot-br { display: none; }

  /* systèmes */
  .sys-title { font-size: clamp(27px, 7vw, 38px); }
  .sys-sub { font-size: 16px; }

  /* processus */

  /* témoignage */
  .q-text { font-size: clamp(20px, 5.6vw, 30px); }
  .q-name { font-size: 15px; margin-top: 10px; }

  /* footer */
  .footer-cal { min-height: 640px; }   /* le calendrier s'empile : plus haut */
  .footer-title { margin-bottom: 24px; }   /* le fil de l'écharpe passe au-dessus */
  .footer-bottom { flex-direction: column; gap: 10px; }

  /* ── RYTHME VERTICAL : les paddings du mode statique sont en vh et
     mangent 70 % d'un écran mobile. On les resserre section par section. */
  /* la copie du hero tient une hauteur d'écran : c'est elle qui donne au
     nuage la place de s'éparpiller AUTOUR du titre. À 44vh il ne restait
     que ~8vh sous le titre, donc les visuels du bas s'alignaient en une
     rangée collée au texte au lieu d'entourer la zone. */
  body.static .hero-grid { min-height: 0; padding: 88px var(--gutter) 56px; }
  body.static .s-systems .pin { padding: 8vh var(--gutter); }
  body.static .s-quote .pin { padding: 9vh var(--gutter); }
  body.static .s-about .pin { min-height: 0; padding: 9vh 0; }
  body.static .pcards { padding: 0 var(--gutter) 4vh; }
  body.static .steps-cta { padding-bottom: 5vh; }

  /* ══ SCÈNES ANIMÉES SUR MOBILE ══
     Le mode statique ne se déclenche plus que sur « prefers-reduced-motion » :
     les scènes scrubées tournent donc aussi sur téléphone, et trois mises en
     page pensées pour un grand écran doivent y être reprises. Les règles
     `body.static` plus haut ne suffisent pas — elles ne s'appliquent plus. */

  /* Méthode : la carte prend toute la largeur utile ; le titre
     descend sous la barre. */
  .pcards-title { top: 14vh; }
  /* hauteur au contenu (sur-titre, titre sur 2 lignes, 4 lignes de texte) +
     la place du numéro : à 52vh la moitié basse restait vide */
  .pcard { height: clamp(320px, 42vh, 360px); padding: 24px; border-radius: 22px; }
  .pcard-pill { top: 20px; right: 20px; padding: 5px 12px; font-size: 12px; }
  .pcard-meta { margin-bottom: 18px; }
  .pcard-name { font-size: clamp(30px, 8.4vw, 44px); margin-bottom: 12px; }
  .pcard-desc { font-size: 16px; max-width: none; }
  .pcard-num { right: 24px; bottom: 20px; font-size: min(12vh, 24vw); }

  /* CTA du processus : il est posé en `left: 50%`, donc sa largeur utile
     n'est que la moitié droite de l'écran et « Réserver une séance → » passe
     à la ligne. On lui rend toute la largeur et on interdit la coupure. */
  .steps-cta {
    /* NE PAS toucher à `left` ni à `transform` : le centrage repose sur
       `left: 50%` + `translateX(-50%)` posés plus haut. Le vrai
       problème est ailleurs : sans largeur, un absolu posé à 50 % ne
       dispose que de la moitié droite de l'écran, et le libellé passe à la
       ligne. On lui donne sa largeur naturelle, bornée à l'écran. */
    width: max-content;
    max-width: calc(100% - 2 * var(--gutter));
  }
  .steps-cta a { white-space: nowrap; }

  /* Témoignage : le chiffre au-dessus de son sens */
  .q-stack { width: 100%; }
  .q-result { flex-direction: column; align-items: flex-start; gap: 8px; margin-bottom: 14px; }
  .q-rule-top { margin-bottom: 16px; }
  .q-fig { font-size: clamp(34px, 10vw, 42px); }
  .q-result-txt { gap: 3px; }
  .q-result-label { font-size: 18px; }
  .q-result-detail { font-size: 14px; }
  .q-steps { --node: 30px; margin-top: 16px; width: 100%; }
  .q-steps .sys-step-txt { margin-top: 8px; font-size: 12.5px; }

  /* Témoignage vidéo : la carte passe au-dessus, en vignette, pour que
     la scène épinglée tienne dans un écran de téléphone. */
  .q-layout { flex-direction: column; align-items: flex-start; gap: 22px; width: 100%; padding: 0 var(--gutter); }
  /* …et le chiffre se pose à sa droite, dans la place qu'elle laissait
     vide : la pile s'efface (display: contents) pour que le résultat
     rejoigne la rangée de la vidéo, le reste passe dessous, pleine largeur */
  .q-layout:has(> .q-media) {
    display: grid; grid-template-columns: auto minmax(0, 1fr);
    align-items: center; column-gap: 18px; row-gap: 0;
  }
  .q-layout:has(> .q-media) > .q-media { grid-row: 1; grid-column: 1; margin-bottom: 22px; }
  .q-media + .q-stack { display: contents; }
  .q-media + .q-stack > * { grid-column: 1 / -1; }
  .q-media + .q-stack > .q-result { grid-row: 1; grid-column: 2; margin: 0 0 22px; align-self: center; }
  .q-media + .q-stack .q-fig { font-size: clamp(44px, 13vw, 60px); }
  .q-media { width: min(30vw, 120px); border-radius: 14px; }
  .q-play { width: 46px; height: 46px; margin: -23px 0 0 -23px; }
  .q-play svg { width: 18px; height: 18px; }
  .q-media + .q-stack .q-text { font-size: clamp(19px, 5.2vw, 28px); }
}

/* À propos < 768 px : la carte photo au-dessus, le texte dessous */
@media (max-width: 767px) {
  .about-grid {
    grid-template-columns: minmax(0, 1fr);
    gap: 26px;
    width: 100%; padding: 0 var(--gutter);
  }
  .about-card { --card-w: min(62vw, 250px, 34vh); --card-r: 22px; }
  .about-kicker { margin-bottom: 10px; }
  .about-text { margin-top: 14px; font-size: 17px; }
  .about-sign { margin-top: 16px; font-size: 15px; }
}

/* héros < 768 px : texte puis carte, pleine largeur. Pile centrée : quand
   l'image grandit dans le titre, elle déborde à parts égales en haut et en
   bas (rien ne rétrécit), et le zoom reste calé au milieu de l'écran. */
@media (max-width: 767px) {
  .hero-grid {
    display: flex; flex-direction: column;
    justify-content: center; align-items: stretch;
    row-gap: 32px;
    padding: 64px var(--gutter) 0;
    --hero-pull: 0px;
  }
  .hero-copy, .hero-visual { flex-shrink: 0; }
  /* pile : le texte au-dessus de la carte, en flux normal */
  .hero-copy, .hero-title { margin-top: 0; }
  /* titre au plus grand : « Automatisez le répétitif, » (~9,65 em, la plus
     longue des deux lignes) remplit toute la colonne, sur une seule ligne */
  .hero-copy { --title-fs: min(48px, 10.3cqi); }
  .hero-foot { position: static; margin-top: 20px; }
  .hero-act { margin-top: 20px; }
  .hero-copy { margin-left: 0; }
  .hero-visual { max-width: 440px; top: 0; }
  body.static .hero-grid { display: flex; }
}

@media (max-width: 480px) {
  :root { --gutter: 18px; }
}

/* ═══════════ MENU MOBILE — sous 600 px, les liens passent dans un
   panneau qui se déroule sous la barre, ouvert par l'icône 3 barres ═══════════ */
.dock-burger { display: none; }
@media (max-width: 600px) {
  .dock { gap: 12px; }
  .dock-cta { margin-left: auto; }
  .dock-burger {
    display: inline-flex; flex-direction: column; gap: 5.5px;
    width: 40px; height: 40px; margin: -1px -9px 0 0; padding: 12px 9px 0;
    /* traits fins (1,5 px) mais tous calés au même départ de pixel (17, 24, 31 px) :
       -1px compense le filet du bas de la barre, le pas de 7 px reste entier —
       sinon chaque trait s'arrondit différemment et les épaisseurs varient */
    background: none; border: 0; color: var(--ink); cursor: pointer;
    -webkit-tap-highlight-color: transparent;
  }
  .dock-burger span {
    display: block; height: 1.5px; border-radius: 1px; background: currentColor;
    transition: transform .35s var(--ease-out), opacity .2s var(--ease-out);
  }
  .dock-burger:focus-visible { outline: 2px solid var(--lime); outline-offset: -2px; border-radius: 6px; }
  body.nav-open .dock-burger span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
  body.nav-open .dock-burger span:nth-child(2) { opacity: 0; }
  body.nav-open .dock-burger span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

  .dock-nav {
    position: absolute; top: 100%; left: 0; right: 0;
    flex-direction: column; gap: 0; margin: 0;
    padding: 6px var(--gutter) 14px;
    background: var(--bone);
    border-bottom: 1px solid rgba(var(--ink-rgb), 0.08);
    box-shadow: 0 12px 24px -16px rgba(var(--ink-rgb), 0.22);
    visibility: hidden; opacity: 0; transform: translateY(-8px);
    transition: opacity .25s var(--ease-out), transform .35s var(--ease-out), visibility 0s linear .35s;
  }
  body.nav-open .dock-nav {
    visibility: visible; opacity: 1; transform: none;
    transition: opacity .25s var(--ease-out), transform .35s var(--ease-out), visibility 0s;
  }
  body .dock-nav .dock-link,
  body .dock-nav .dock-link:first-child {
    display: block; padding: 15px 0; font-size: 17px; color: var(--ink);
    border-bottom: 1px solid rgba(var(--ink-rgb), 0.08);
  }
  .dock-nav .dock-link:last-child { border-bottom: 0; }
  .dock-nav .dock-link::after { height: 100%; }
  /* barre ouverte : toujours en clair, même au-dessus d'une photo */
  body.nav-open .dock { background: var(--bone); border-bottom-color: rgba(var(--ink-rgb), 0.08); }
  body.nav-open .dock-wordmark { color: var(--ink); }
  body.dock-dark:not(.nav-open) .dock-burger { color: #fff; }
}

/* ═══════════ REDUCED MOTION ═══════════ */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
  }
}

/* ═══════════════════════════════════════════════════════════
   DA KAALYO — surcouche du système de design « indigo-ink ledger »
   Uniquement graisses, approches, OpenType, couleurs de liens et
   filets : aucune dimension, position ni animation n'est touchée.
   ═══════════════════════════════════════════════════════════ */
:root {
  --frost: #e5edf5;               /* filets et séparateurs */
  --lilac: #d6d9fc;               /* filigranes teintés violet */
  --indigo-hover: #7389ff;
  --todo-green: #15803d;          /* « fait » de la carte to-do — 5:1 sur blanc */

  /* le fluide : couleur secondaire, teintes inspirées du ruban de stripe.com
     (bleu pervenche → violet → rose orchidée → orange → pêche dorée) */
  --flow-0: #bccbff;              /* bleu pervenche — ruban du héros seulement */
  --flow-1: #533afd;              /* indigo — le même que les CTA */
  --flow-2: #7b61ff;              /* violet bleuté — blanc dessus ≥ 4:1 */
  --flow-3: #f47ce2;              /* rose orchidée */
  --flow-4: #ff9418;              /* orange vif */
  --flow-5: #ffb866;              /* pêche dorée */
  --flow-deep-3: #d63fc4;         /* orchidée approfondie : texte blanc lisible (≥ 3:1) */

  /* cartes d'étapes : le ruban ÉTALÉ À PLAT. Mêmes teintes que le tissu du
     héros, dans sa lumière claire (là où il s'étale, il pâlit), et dans
     son ordre : 01 pervenche → violet, 02 violet → orchidée, 03 orchidée →
     pêche. Texte en encre, comme partout (cohérence du 2026-10-13 : plus
     de violet-prune ni de texte blanc). 4 points chacune :
     a = coin haut-gauche (le plus clair, sous le texte), b = teinte
     principale, c = éclat haut-droite, d = coin bas-droite (sous le numéro),
     qui annonce déjà la carte suivante. */
  --step-1a: #ecebff;  --step-1b: #dfdbff;  --step-1c: #d2caff;  --step-1d: #ead4f8;
  --step-2a: #f3e6fb;  --step-2b: #f2d9f4;  --step-2c: #f6cdee;  --step-2d: #ffd8cc;
  --step-3a: #fdeae9;  --step-3b: #ffe1d2;  --step-3c: #ffd5b5;  --step-3d: #ffc797;
  /* carte « À propos » : le ruban ENTIER sur une carte (lavande → orchidée,
     éclat violet, coin pêche), plus vif que les étapes — choix de Jules du
     2026-10-14 (« F5 » : pastels poussés ×1,8 loin du blanc, en Oklab) */
  --about-a: #dddbfe;  --about-b: #e7bbeb;  --about-c: #b19ffc;  --about-d: #fcb277;

  /* la carte : une seule recette pour tout ce qui flotte sur le ruban
     (carte to-do, fiches des systèmes, cartes des étapes, vidéo du
     témoignage, carte « À propos ») — un filet de 1 px dans l'ombre, puis
     une ombre longue et diffuse, à peine perceptible */
  --card-shadow:
    0 0 0 1px rgba(var(--ink-rgb), 0.05),
    0 30px 60px -28px rgba(50, 50, 93, 0.16),
    0 14px 28px -16px rgba(var(--ink-rgb), 0.08);
}

/* Inter Tight 300 partout : les titres chuchotent, ils ne crient pas */
body {
  font-weight: 300;
  letter-spacing: -0.01em;
  font-feature-settings: "ss01" on, "tnum" on;
}
h1, h2, h3, .hero-title, .q-name { font-weight: 300; }

/* l'approche se resserre à mesure que le corps grandit */
.hero-title, .spot-intro, .footer-title { letter-spacing: -0.025em; }

/* labels : petites capitales espacées, graisse 400 pour la lisibilité */
.mono { font-weight: 400; letter-spacing: 0.05em; }

/* chrome : wordmark du dock et du loader au même dessin */
.dock-wordmark, .loader-wordmark { font-weight: 500; letter-spacing: -0.02em; }

/* actions en indigo : e-mail, liens sociaux */
.footer-bottom a { color: var(--lime); }

/* filets #e5edf5, jamais d'ombre */
.footer-bottom { border-top-color: var(--frost); }

/* footer sur la même toile blanche que tout le site (transparent : son
   écharpe passe derrière) : c'est le calendrier qui porte la teinte
   (fond Mist via CAL_VARS dans app.js), comme la carte to-do du héros —
   un objet clair posé sur le blanc */
.footer { background: transparent; }

/* barre du bas : © | e-mail + LinkedIn (pictos) | liens légaux */
.footer-bottom { flex-wrap: wrap; align-items: center; row-gap: 12px; }
.footer-bottom > * { white-space: nowrap; }

/* contact : en casse normale (une adresse e-mail ne se lit pas en
   capitales), picto en currentColor devant chaque lien */
.footer-contact { display: flex; gap: 24px; font-size: 14px; letter-spacing: 0; }
.footer-contact a { display: inline-flex; align-items: center; gap: 8px; transition: color .2s var(--ease-out); }
.footer-contact a:hover { color: var(--indigo-hover); }
.footer-ico { width: 16px; height: 16px; flex: none; }

/* liens légaux : en encre discrète, l'indigo reste aux actions */
.footer-legal { display: flex; gap: 24px; }
.footer-bottom .footer-legal a { color: var(--ash); transition: color .2s var(--ease-out); }
.footer-bottom .footer-legal a:hover { color: var(--ink); }
@media (max-width: 900px) {
  .footer-bottom { align-items: flex-start; }
  .footer-contact, .footer-legal { gap: 20px; }
}

/* étapes : le ruban étalé, de la pervenche (01) à la pêche (03), dégradé
   à plusieurs points — le plus clair sous le texte, un éclat vers la
   droite, la teinte de la carte suivante dans le coin du numéro. La
   carte « À propos » a sa propre teinte (--about-*) : tout le ruban,
   derrière la photo. Pas de halo ni d'ombre. */
.pcard:nth-child(3n+1) { --a: var(--step-1a); --b: var(--step-1b); --c: var(--step-1c); --d: var(--step-1d); }
.pcard:nth-child(3n+2) { --a: var(--step-2a); --b: var(--step-2b); --c: var(--step-2c); --d: var(--step-2d); }
.pcard:nth-child(3n+3) { --a: var(--step-3a); --b: var(--step-3b); --c: var(--step-3c); --d: var(--step-3d); }
.about-card { --a: var(--about-a); --b: var(--about-b); --c: var(--about-c); --d: var(--about-d); }
/* posées sur le ruban : la recette de la carte flottante (le filet est
   dans l'ombre, la vidéo perd donc sa bordure) */
.pcard, .about-card, .q-media { box-shadow: var(--card-shadow); }
.q-media { border: 0; }
.pcard, .about-card {
  background:
    radial-gradient(70% 90% at 100% 100%, var(--d) 0%, transparent 70%),
    radial-gradient(55% 75% at 88% 0%, var(--c) 0%, transparent 66%),
    radial-gradient(70% 100% at 0% 100%, var(--b) 0%, transparent 75%),
    linear-gradient(135deg, var(--a) 0%, var(--b) 60%, var(--c) 100%);
}

/* ═══════════════════════════════════════════════════════════
   LE FLUIDE — couleur secondaire, façon Stripe
   Contrat de couleur (cohérence du 2026-10-13) :
   · une seule toile, blanche, du héros au footer — aucune section teintée ;
   · une seule recette de carte flottante (--card-shadow) ;
   · une seule source de couleur, le ruban, en volume : celui du héros,
     derrière la carte to-do, qui part avec elle ; puis, dans chaque scène
     après le réseau, une écharpe de la même étoffe, de bord à bord derrière
     l'objet de la scène — elle défile avec sa scène, jamais sous le texte
     (app.js → SCARVES). Pas de fil continu : c'est la matière qui revient.
     Toile fixe à l'écran (.ribbon-layer), derrière des scènes transparentes ;
     en fil (liserés de la bande, ovales, souligné, anneau de l'agent),
     étalé à plat en pastel (cartes des étapes, carte « À propos ») ;
   · le texte toujours en encre sur clair ; l'indigo aux actions, le vert
     à ce qui est fait.
   ═══════════════════════════════════════════════════════════ */

/* ── héros : calque du ruban, dans le contexte d'empilement de la grille.
   z-index -1 : derrière le texte et la carte, devant le blanc du pin.
   Déborde jusqu'aux bords de l'écran malgré le max-width de la grille. */
.hero-flow {
  position: absolute; top: 0; bottom: 0;
  left: calc(50% - 50vw); width: 100vw;
  z-index: -1;
  pointer-events: none;
  will-change: transform, opacity;
}
.hero-flow canvas { display: block; width: 100%; height: 100%; }
/* pas de WebGL : le même ruban en dégradé CSS, figé */
.hero-flow.is-fallback {
  background: linear-gradient(120deg, var(--flow-1), var(--flow-2) 30%, var(--flow-3) 55%, var(--flow-4) 78%, var(--flow-5));
  clip-path: polygon(60% 0, 100% 0, 100% 74%, 44% 100%);
}
.hero-flow.is-fallback canvas { display: none; }

/* ── les écharpes (flow-gradient.js, mode écharpes) : une seule toile,
   fixe à l'écran, sous le contenu qui défile (.smooth, z 1). Le body
   reste blanc derrière elle ; les scènes sont transparentes. */
.ribbon-layer { position: fixed; inset: 0; z-index: 0; pointer-events: none; }
.ribbon-layer canvas { display: block; width: 100%; height: 100%; }

/* ── bande des systèmes : le ruban n'est plus un fond mais un liseré.
   La section est sur la toile blanche du site (plus de brume : une seule
   toile, du héros au footer) et la couleur ne survit que sur les deux
   biais : un fil de 0,9vw aux couleurs du ruban (pêche → orange →
   orchidée → violet), à l'entrée et à la sortie. Il sert aussi de bord
   visible quand la bande monte sur le réseau, blanc sur blanc. Texte en
   encre, fiches à la recette de la carte to-do (Mist + ombre douce).
   Transparente : son écharpe passe derrière ; le réseau, lui, est
   coupé au bord de la bande qui monte (app.js → clipUnderBand). */
.band {
  background: transparent;
  clip-path: polygon(0 12vw, 100% 0, 100% calc(100% - 12vw), 0 100%);
}
.band::before, .band::after {
  content: ''; position: absolute; inset: 0; z-index: 5; pointer-events: none;
  background: linear-gradient(100deg, var(--flow-5), var(--flow-4) 14%, var(--flow-3) 36%, var(--flow-deep-3) 55%, var(--flow-2));
}
.band::before { clip-path: polygon(0 12vw, 100% 0, 100% 0.9vw, 0 12.9vw); }
.band::after  { clip-path: polygon(0 calc(100% - 0.9vw), 100% calc(100% - 12.9vw), 100% calc(100% - 12vw), 0 100%); }
.s-systems .pin { color: var(--ink); }
.sys-kicker, .sys-sub, .sys-count { color: var(--ash); opacity: 1; }
/* fiches : même recette que la carte to-do du héros (fond Mist, filet
   dans l'ombre, ombre longue et diffuse) — sur le blanc, un simple filet
   ne suffisait plus à les détacher. Le fond des points du workflow et
   l'anneau des coches suivent la fiche (sinon un rectangle blanc). */
.sys-card { background: var(--white); box-shadow: var(--card-shadow); }
.sys-card .sys-node { background: var(--white); }
.sys-card .sys-dot { box-shadow: 0 0 0 2px var(--white); }
.sys-ticks i { background: rgba(var(--ink-rgb), 0.14); }
.sys-ticks i.on { background: var(--ink); }
.band .to-lime, .band .from-lime, .s-systems .pin { background: transparent; }

/* ── ovales dessinés à la main : un trait en dégradé (#flowStroke dans
   index.html), un peu plus épais pour que la couleur se lise */
.pill-svg path { stroke: url(#flowStroke); stroke-width: 1.8; }
/* même trait, souligné à la main sous les verbes de la phrase du réseau */
.spot-intro .spot-mark { position: relative; display: inline; opacity: 1; white-space: nowrap; }
.spot-mark-svg {
  position: absolute; left: -2%; bottom: -0.16em;
  width: 104%; height: 0.32em;
  overflow: visible; pointer-events: none;
}
.spot-mark-svg path { fill: none; stroke: url(#flowMark); stroke-width: 4; stroke-linecap: round; vector-effect: non-scaling-stroke; }
@media (max-width: 767px) {
  .spot-intro .spot-mark { white-space: normal; }
  .spot-mark-svg { display: none; }
}

.flow-defs { position: absolute; width: 0; height: 0; overflow: hidden; }

/* ═══════════════════════════════════════════════════════════
   RÉSEAU D'OUTILS (tool-network.js) — l'agent IA au centre,
   les outils des clients en ceinture de météorites autour de lui.
   Fond blanc, la toile du site (la brume gris-bleu #f6f9fc cassait la
   continuité avec le héros : retirée le 2026-10-13). La fenêtre qui
   s'ouvre dans le titre est dessinée par un filet de 1 px. Scène
   monochrome : ni halo, ni flou, ni dégradé — les
   couleurs du ruban restent au ruban. Un seul accent, l'indigo de
   l'agent (la couleur d'action), plus le vert de la coche. Les tuiles
   sont des icônes d'app, logos dans leurs couleurs d'origine.
   tool-network.js pose --tile, --core, --cx, --cy.
   ═══════════════════════════════════════════════════════════ */
/* la fenêtre entre « gardez ce qui » et « compte. » : blanc sur blanc,
   c'est le filet qui la fait exister pendant qu'elle grandit. Au-dessus
   de la scène (qui forme son propre empilement), sous rien d'autre. */
.s-hero .grow::after {
  content: ''; position: absolute; inset: 0; z-index: 2;
  border-radius: inherit;
  box-shadow: inset 0 0 0 1px rgba(var(--ink-rgb), 0.1);
  pointer-events: none;
}
.net-stage {
  /* la toile du site (la coche des outils s'en sert pour son anneau) ;
     transparente, comme le bloc du héros qui la porte */
  --net-bg: var(--bone);
  display: block;
  overflow: hidden;
  background: transparent;
  user-select: none;
}
.net-canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; pointer-events: none; }
.net-back { z-index: 1; }
.net-front { z-index: 41; }

/* l'agent : disque blanc cerclé d'un contour net, sans lueur */
.net-core {
  position: absolute; z-index: 40;
  left: var(--cx, 50%); top: var(--cy, 34%);
  width: var(--core, 104px); height: var(--core, 104px);
  transform: translate(-50%, -50%);
}
.net-core-disc {
  position: absolute; inset: 0; border-radius: 50%;
  background: #ffffff;
  display: grid; place-items: center;
}
/* contour : dégradé du ruban (indigo → violet → orchidée → orange → pêche)
   qui tourne très lentement. JAMAIS de halo flou autour (rendu « IA »).
   Un anneau net de 1,5 px (masque), sans flou ni lueur. */
.net-core-disc::before {
  content: ''; position: absolute; inset: 0; border-radius: 50%;
  /* 2 px : à 1,5 px le trait tombe entre deux pixels et s'amincit par
     endroits. Sur fond clair, les couleurs du ruban telles quelles. */
  padding: 2px;
  background: conic-gradient(
    var(--flow-1), var(--flow-2), var(--flow-3),
    var(--flow-4), var(--flow-3), var(--flow-2), var(--flow-1));
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box exclude, linear-gradient(#000 0 0);
  animation: net-ring 24s linear infinite;
  pointer-events: none;
}
.net-stage:not(.is-live) .net-core-disc::before { animation-play-state: paused; }
@keyframes net-ring { to { transform: rotate(1turn); } }
@media (prefers-reduced-motion: reduce) { .net-core-disc::before { animation: none; } }
.net-core-label {
  color: var(--ink); font-weight: 400;
  font-size: clamp(12px, calc(var(--core, 104px) * 0.15), 17px);
  letter-spacing: 0;
  white-space: nowrap;
}

/* les outils : icônes d'app. Logo posé sur une tuile blanche, ou icône
   carrée qui remplit la tuile (.is-fill). Un filet encre très léger
   détache les tuiles blanches du fond brume. */
.net-tool {
  position: absolute; left: 0; top: 0;
  width: var(--tile, 56px); height: var(--tile, 56px);
  border-radius: 24%;
  background: #fff;
  box-shadow: 0 0 0 1px rgba(var(--ink-rgb), 0.08), 0 2px 6px rgba(var(--ink-rgb), 0.06);
  display: grid; place-items: center;
  will-change: transform, opacity;
  opacity: 0;
}
.net-tool img { width: 64%; height: 64%; object-fit: contain; border-radius: 0; pointer-events: none; }
.net-tool.is-fill { background: transparent; }
.net-tool.is-fill img { width: 100%; height: 100%; object-fit: cover; border-radius: inherit; }

/* la coche de la carte to-do, en écho : l'agent vient d'y faire une tâche */
.net-tick {
  position: absolute; top: -5px; right: -5px;
  width: 16px; height: 16px; border-radius: 50%;
  background: var(--todo-green);
  box-shadow: 0 0 0 2px var(--net-bg);
  display: grid; place-items: center;
  opacity: 0; transform: scale(0.4);
  transition: opacity .25s, transform .4s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.net-tick svg { width: 10px; height: 10px; fill: none; stroke: #fff; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.net-tool.is-done .net-tick { opacity: 1; transform: scale(1); }

@media (max-width: 767px) {
  .net-tick { width: 14px; height: 14px; top: -4px; right: -4px; }
  .net-tick svg { width: 9px; height: 9px; }
}

/* phrase en encre, comme le reste du site ; un peu plus d'air sous la ceinture */
.spot-intro { color: var(--ink); top: calc(var(--net-bottom, 52vh) + clamp(40px, 8.5vh, 96px)); }

/* LinkedIn de Jules, au bout de la signature « À propos » : action → indigo */
.about-link { margin-left: 0.6em; color: var(--lime); white-space: nowrap; transition: color .2s var(--ease-out); }
.about-link:hover { color: var(--indigo-hover); }

/* ═══════════════════════════════════════════════════════════
   FINITIONS (passe polish du 2026-10-13)
   ═══════════════════════════════════════════════════════════ */

/* sélection de texte à la couleur de la marque, pas le bleu du système */
::selection { background: #dcd7ff; color: var(--ink); }

/* titres de section : pas de mot seul en fin de titre (« … pas des / gadgets ») ;
   titres des étapes : la coupure tombe sur la virgule (« On construit, / vous validez. ») */
.sys-title, .pcards-title, .pcard-name, .footer-title, .about-title { text-wrap: balance; }

/* témoignage : le bouton lecture sous le visage de Rayan, pas dessus */
.q-play { top: 78%; }

/* écran tactile : les petits liens gardent leur dessin, mais leur zone de
   toucher s'agrandit (44 px dans la barre, 32 px dans le pied de page,
   où les lignes sont serrées) */
@media (max-width: 900px) {
  .dock-wordmark, .dock-link, .dock-cta,
  .footer-contact a, .footer-legal a { position: relative; }
  .dock-wordmark::after, .dock-link::after, .dock-cta::after,
  .footer-contact a::after, .footer-legal a::after {
    content: ''; position: absolute; left: -6px; right: -6px; top: 50%;
    height: 44px; transform: translateY(-50%);
  }
  .footer-contact a::after, .footer-legal a::after { height: 32px; }
  .footer-bottom { gap: 14px; }
}
