/* ==========================================================================
   Le tapis roulant de l'opportunité (section 05).
   17 septembre 2026, sur demande : deux bandes en perspective, les moteurs où
   la demande se tape, puis les outils qu'on branche.

   ⚠️ **C'est un mouvement permanent de plus**, quand la section 2 de
   CLAUDE.md en autorise trois. Il est borné : il s'arrête hors de l'écran
   (tapis.js pose data-visible), et sous prefers-reduced-motion il ne bouge
   pas du tout, il ne se raccourcit pas.

   La vitesse est une seule valeur, en pixels par seconde, et les deux bandes
   la partagent : la durée d'un tour est la longueur d'un lot divisée par elle,
   mesurée par le script. Les stries de la bande lisent la même vitesse, pour
   que les pièces ne glissent pas sur leur tapis.
   ========================================================================== */

/* Le contour, à double coque comme le bouton de la barre : une enveloppe de
   verre, une gouttière de 4 px, et le cœur posé dedans. Rayons concentriques,
   32 dehors et 28 dedans, multiples de quatre. */
.tapis {
  position: relative;
  --vitesse: 38;
  --pas-strie: 24;
  margin: 0;
  align-self: stretch;
  /* La largeur est celle de la colonne : sans elle, une rangée plus haute que
     le 4/3 (quatre atouts à gauche) élargit la carte hors de la page. */
  inline-size: 100%;
  aspect-ratio: 4 / 3;
  padding: calc(var(--spacing) * 4);
  background-color: rgb(28 28 28 / .04);
  border: 1px solid rgb(28 28 28 / .08);
  border-radius: var(--r-2xl);
  box-shadow:
    inset 0 1px 0 rgb(255 255 255 / .9),
    0 24px 48px -32px rgb(28 28 28 / .28);
}

.tapis-coeur {
  position: relative;
  height: 100%;
  overflow: hidden;
  isolation: isolate;
  border: 1px solid var(--gris-grille);
  border-radius: calc(var(--r-2xl) - var(--spacing) * 4);
  /* Une trame de points au pas de 16, la gouttière du système, sous la
     matière des cartes. */
  background:
    radial-gradient(circle, rgb(28 28 28 / .07) 1px, transparent 1.4px) 0 0 / 16px 16px,
    var(--fond-carte);
  box-shadow: var(--lueur-carte);
}

/* Le fil du gradient de site sur l'arête haute, et sa lueur au pied. Il
   s'éteint aux deux bouts : un filet plein se lirait comme une bordure
   colorée. La lueur éclaircit plus qu'elle ne teinte (8 %), aucun texte
   n'en dépend. */
.tapis-coeur::before,
.tapis-coeur::after {
  content: "";
  position: absolute;
  pointer-events: none;
  z-index: 2;
}

.tapis-coeur::before {
  top: 0;
  left: 10%;
  right: 10%;
  height: 1px;
  background: var(--gradient-site);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 30%, #000 70%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 30%, #000 70%, transparent);
}

.tapis-coeur::after {
  inset: 0;
  z-index: -1;
  background: var(--gradient-site);
  opacity: .08;
  -webkit-mask-image: radial-gradient(70% 55% at 50% 100%, #000, transparent);
  mask-image: radial-gradient(70% 55% at 50% 100%, #000, transparent);
}

/* Quatre repères de coin, comme sur une planche. */
.tapis-coin {
  position: absolute;
  z-index: 2;
  width: calc(var(--spacing) * 12);
  height: calc(var(--spacing) * 12);
  border: 0 solid var(--ink-3);
  pointer-events: none;
}
.tapis-coin-hg { top: 16px; left: 16px; border-top-width: 1.5px; border-left-width: 1.5px; border-top-left-radius: 4px; }
.tapis-coin-hd { top: 16px; right: 16px; border-top-width: 1.5px; border-right-width: 1.5px; border-top-right-radius: 4px; }
.tapis-coin-bg { bottom: 16px; left: 16px; border-bottom-width: 1.5px; border-left-width: 1.5px; border-bottom-left-radius: 4px; }
.tapis-coin-bd { bottom: 16px; right: 16px; border-bottom-width: 1.5px; border-right-width: 1.5px; border-bottom-right-radius: 4px; }

/* La scène. Le fondu latéral fait entrer et sortir les pièces au lieu de
   les trancher sur l'arête du cadre. */
.tapis-scene {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  perspective: 900px;
  perspective-origin: 50% 20%;
  -webkit-mask-image: linear-gradient(90deg, transparent 2%, #000 18%, #000 82%, transparent 98%);
  mask-image: linear-gradient(90deg, transparent 2%, #000 18%, #000 82%, transparent 98%);
}

.tapis-plan {
  display: grid;
  gap: calc(var(--spacing) * 28);
  width: 160%;
  transform: translateY(-8%) rotateX(46deg) rotateZ(-9deg);
  transform-origin: 50% 50%;
}

.tapis-rang {
  display: grid;
  gap: calc(var(--spacing) * 10);
}

.tapis-nom {
  max-width: none;
  margin: 0;
  padding-inline-start: 38%;
  font-family: var(--font-mono);
  font-size: var(--text-s);
  font-weight: 500;
  line-height: 1.2;
  letter-spacing: .02em;
  text-transform: uppercase;
  color: var(--ink-2);
}

/* La bande : une gomme grise, ses stries, et l'ombre qu'elle pose sur la
   planche. */
.tapis-bande {
  position: relative;
  overflow: hidden;
  padding-block: calc(var(--spacing) * 14);
  background: linear-gradient(180deg, #EFEFEF, #E3E3E3);
  border: 1px solid rgb(28 28 28 / .07);
  border-radius: var(--r-xl);
  box-shadow:
    inset 0 1px 0 rgb(255 255 255 / .9),
    inset 0 -3px 0 rgb(28 28 28 / .06),
    0 22px 30px -20px rgb(28 28 28 / .4);
}

.tapis-bande::before {
  content: "";
  position: absolute;
  inset: 0 auto 0 0;
  width: calc(100% + var(--pas-strie) * 1px);
  background: repeating-linear-gradient(90deg, rgb(28 28 28 / .06) 0 1px, transparent 1px calc(var(--pas-strie) * 1px));
  animation: tapis-strie calc(var(--pas-strie) / var(--vitesse) * 1s) linear infinite;
}

.tapis-piste {
  position: relative;
  display: flex;
  width: max-content;
  animation: tapis-defile var(--duree, 40s) linear infinite;
  will-change: transform;
}

.tapis-droite .tapis-piste,
.tapis-droite .tapis-bande::before { animation-direction: reverse; }

.tapis-lot {
  display: flex;
  gap: calc(var(--spacing) * 16);
  margin: 0;
  padding: 0 calc(var(--spacing) * 16) 0 0;
  list-style: none;
}

.tapis-piece {
  flex: none;
  display: grid;
  justify-items: center;
  align-content: center;
  gap: calc(var(--spacing) * 8);
  width: calc(var(--spacing) * 100);
  padding: calc(var(--spacing) * 14) calc(var(--spacing) * 6) calc(var(--spacing) * 10);
  background: var(--blanc);
  border: 1px solid rgb(28 28 28 / .06);
  border-radius: var(--r-lg);
  box-shadow:
    inset 0 -2px 0 rgb(28 28 28 / .04),
    0 1px 1px rgb(28 28 28 / .06),
    0 8px 14px -8px rgb(28 28 28 / .28);
}

.tapis-piece img,
.tapis-piece-mot b {
  display: block;
  width: calc(var(--spacing) * 36);
  height: calc(var(--spacing) * 36);
}

.tapis-piece span {
  font-size: var(--text-xs);
  font-weight: 500;
  line-height: 1.2;
  white-space: nowrap;
  color: var(--noir);
}

/* DataForSEO en toutes lettres, faute du fichier officiel. */
.tapis-piece-mot b {
  display: flex;
  align-items: center;
  justify-content: center;
  white-space: nowrap;
  width: auto;
  font-family: var(--font-titre);
  font-weight: var(--graisse-titre);
  font-size: calc(var(--spacing) * 15);
  letter-spacing: -.03em;
  color: var(--noir);
}
.tapis-piece-mot i { font-style: normal; color: var(--gris-lisible); }

@keyframes tapis-defile { to { transform: translateX(-50%); } }
@keyframes tapis-strie  { to { transform: translateX(calc(var(--pas-strie) * -1px)); } }

/* Hors de l'écran, rien ne tourne. Seulement quand le script est là pour
   dire ce qui est à l'écran : sans lui, le tapis tourne, rien n'est caché. */
.tapis[data-arme]:not([data-visible]) .tapis-piste,
.tapis[data-arme]:not([data-visible]) .tapis-bande::before {
  animation-play-state: paused;
}

/* --------------------------------------------------------------------------
   Charly, au premier plan. 17 septembre 2026, sur demande : la section du
   chatbot est supprimée, et sa scène tient ici. Le visiteur demande, Charly
   répond, la discussion est relue dans Signal, puis devient une réponse de
   FAQ. Une seule durée, 12 s, et toutes les étapes sont des pourcentages de
   ce même tour : elles ne peuvent pas se décaler l'une de l'autre.
   L'interface est celle du dock (chatbot.css), mêmes classes.
   -------------------------------------------------------------------------- */
/* Depuis le même soir, sur demande, la fenêtre est posée dans le plan du
   tapis, sous les deux bandes : elle prend la même perspective qu'elles, et
   le plan remonte pour lui faire la place. Elle est centrée sur le plan, qui
   est centré sur la carte, donc elle reste hors du fondu latéral. */
.tapis-chat {
  --tour-chat: 12s;
  justify-self: center;
  inline-size: calc(var(--spacing) * 360);
  margin-block-start: calc(var(--spacing) * -4);
  pointer-events: none;
}

.tapis-chat .chat-coque {
  border-radius: var(--r-xl);
  box-shadow:
    var(--lueur-carte),
    inset 0 -3px 0 rgb(28 28 28 / .06),
    0 30px 40px -24px rgb(28 28 28 / .45);
}
.tapis-chat .chat-entete { padding: calc(var(--spacing) * 10) calc(var(--spacing) * 14) calc(var(--spacing) * 8); }
.tapis-chat .chat-entete .chat-avatar { inline-size: calc(var(--spacing) * 24); block-size: calc(var(--spacing) * 24); }
.tapis-chat .chat-nom { font-size: var(--text-s); }
.tapis-chat .chat-statut { font-size: var(--text-xs); margin: 0; }

/* L'attente et la réponse partagent une case : l'une remplace l'autre sans
   que le fil saute. */
.tapis-chat-fil {
  display: grid;
  gap: calc(var(--spacing) * 8);
  max-block-size: none;
  overflow: visible;
  padding: calc(var(--spacing) * 12) calc(var(--spacing) * 14) calc(var(--spacing) * 10);
}
.tc-attente,
.tc-reponse { grid-area: 2 / 1; }
.tc-attente { opacity: 0; }

.tapis-chat .chat-bulle {
  font-size: var(--text-s);
  line-height: 1.4;
  padding: calc(var(--spacing) * 6) calc(var(--spacing) * 12);
}
.tapis-chat .chat-points { padding-block: calc(var(--spacing) * 5); }

.tapis-chat-suite {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: calc(var(--spacing) * 6);
  padding: calc(var(--spacing) * 10) calc(var(--spacing) * 14) calc(var(--spacing) * 12);
  border-block-start: 1px solid var(--gris-grille);
}

.tc-etape {
  display: inline-flex;
  align-items: center;
  gap: calc(var(--spacing) * 6);
  padding: calc(var(--spacing) * 4) calc(var(--spacing) * 10);
  border: 1px solid var(--gris-grille);
  border-radius: var(--r-full);
  background: var(--blanc);
  font-size: var(--text-xs);
  font-weight: 500;
  line-height: 1.3;
  white-space: nowrap;
  color: var(--noir);
}
.tc-etape img { inline-size: calc(var(--spacing) * 14); block-size: calc(var(--spacing) * 14); }

/* La réponse de FAQ est ce que la scène fabrique : le seul endroit qui prend
   le gradient de site, en liseré. Le texte reste noir. */
.tc-faq {
  border-color: transparent;
  background:
    linear-gradient(var(--blanc), var(--blanc)) padding-box,
    var(--gradient-site) border-box;
}

.tc-fleche {
  inline-size: calc(var(--spacing) * 14);
  block-size: calc(var(--spacing) * 14);
  color: var(--gris-lisible);
}

.tc-question  { animation: tc-question  var(--tour-chat) var(--ease-out) infinite; }
.tc-attente   { animation: tc-attente   var(--tour-chat) linear infinite; }
.tc-reponse   { animation: tc-reponse   var(--tour-chat) var(--ease-out) infinite; }
.tc-signal    { animation: tc-signal    var(--tour-chat) var(--ease-out) infinite; }
.tc-fleche    { animation: tc-fleche    var(--tour-chat) var(--ease-out) infinite; }
.tc-faq       { animation: tc-faq       var(--tour-chat) var(--ease-out) infinite; }
/* La question s'entoure quand Signal la relit. */
.tc-question .chat-bulle { animation: tc-relue var(--tour-chat) var(--ease-out) infinite; }

@keyframes tc-question {
  0%, 5%    { opacity: 0; translate: 0 calc(var(--spacing) * 8); }
  9%, 90%   { opacity: 1; translate: 0 0; }
  96%, 100% { opacity: 0; translate: 0 0; }
}
@keyframes tc-attente {
  0%, 14%   { opacity: 0; }
  17%, 28%  { opacity: 1; }
  31%, 100% { opacity: 0; }
}
@keyframes tc-reponse {
  0%, 30%   { opacity: 0; translate: 0 calc(var(--spacing) * 8); }
  34%, 90%  { opacity: 1; translate: 0 0; }
  96%, 100% { opacity: 0; translate: 0 0; }
}
@keyframes tc-signal {
  0%, 46%   { opacity: 0; scale: .9; }
  50%, 90%  { opacity: 1; scale: 1; }
  96%, 100% { opacity: 0; scale: 1; }
}
@keyframes tc-fleche {
  0%, 56%   { opacity: 0; translate: calc(var(--spacing) * -4) 0; }
  60%, 90%  { opacity: 1; translate: 0 0; }
  96%, 100% { opacity: 0; translate: 0 0; }
}
@keyframes tc-faq {
  0%, 62%   { opacity: 0; scale: .9; }
  66%, 90%  { opacity: 1; scale: 1; }
  96%, 100% { opacity: 0; scale: 1; }
}
@keyframes tc-relue {
  0%, 46%   { box-shadow: 0 0 0 0 rgb(102 71 240 / 0); }
  50%, 90%  { box-shadow: 0 0 0 2px var(--violet); }
  96%, 100% { box-shadow: 0 0 0 0 rgb(102 71 240 / 0); }
}

.tapis[data-arme]:not([data-visible]) .tapis-chat,
.tapis[data-arme]:not([data-visible]) .tapis-chat * {
  animation-play-state: paused;
}

@media (max-width: 767px) {
  .tapis-chat { inline-size: calc(var(--spacing) * 290); translate: calc(var(--spacing) * -40) 0; }
}

@media (max-width: 767px) {
  /* Plus haute que carrée depuis que Charly est au premier plan : le tapis
     garde le haut, la fenêtre le bas. */
  .tapis { --vitesse: 30; aspect-ratio: 3 / 4; }
  .tapis-plan { gap: calc(var(--spacing) * 18); width: 190%; transform: translateY(-14%) rotateX(46deg) rotateZ(-9deg) scale(.74); }
  .tapis-piece { width: calc(var(--spacing) * 76); padding-block: calc(var(--spacing) * 10) calc(var(--spacing) * 8); }
  .tapis-piece img, .tapis-piece-mot b { width: calc(var(--spacing) * 28); height: calc(var(--spacing) * 28); }
  .tapis-piece-mot b { width: auto; font-size: calc(var(--spacing) * 12); }
}

/* On retire le mouvement, on ne le raccourcit pas : le tapis est posé, à
   l'arrêt, avec ses pièces en place. */
@media (prefers-reduced-motion: reduce) {
  .tapis-piste,
  .tapis-bande::before { animation: none !important; }
  /* La scène de Charly est posée entière, la réponse à la place de l'attente. */
  .tapis-chat,
  .tapis-chat * { animation: none !important; }
  .tc-attente { display: none; }
  .tc-question .chat-bulle { box-shadow: 0 0 0 2px var(--violet); }
}


/* --------------------------------------------------------------------------
   Charly, caché derrière la carte. 18 septembre 2026, sur demande.

   Il se tient à l'arête haute de ses deux mains ; au repos on ne voit que
   son antenne, ses oreilles et le haut de sa tête, et au survol de la carte
   il monte assez pour montrer ses yeux.

   **Deux copies du même dessin, figé** (chatbot.js) : le corps, découpé à
   l'arête par une fenêtre, donc derrière la carte ; et les mains seules,
   posées devant, à cheval sur l'arête. Charly n'a pas de bras, ses mains
   flottent déjà à côté de lui : elles peuvent rester sur l'arête pendant
   que la tête monte sans que rien ne se voie de travers.

   ⚠️ **Il est figé, et c'est voulu.** Le moteur fait respirer Charly en
   permanence, un mouvement de plus que les trois de la section 2. Ici il ne
   bouge qu'au survol, et s'arrête avec lui : ce n'est pas un mouvement
   permanent. Sur un écran tactile, sans survol, il se découvre une fois
   quand la carte arrive à l'écran. Sous prefers-reduced-motion, il saute à
   sa pose au lieu de glisser.

   Les positions sont en unités du dessin (300 de côté, centre à 0) :
   l'antenne culmine à -144, les yeux vont de -14 à +26, les mains de +12 à
   +56. « --coupe » est la hauteur du dessin qui tombe sur l'arête.
   -------------------------------------------------------------------------- */
.tapis-cache {
  --charly: calc(var(--spacing) * 120);
  --unite-charly: calc(var(--charly) / 300);
  --coupe: -38;          /* au repos : le haut de la tête, pas les yeux */
  --coupe-mains: 34;     /* les mains : les doigts passent l'arête */
  position: absolute;
  inset-block-end: calc(100% + 1px);
  inset-inline-end: 12%;
  inline-size: var(--charly);
  block-size: var(--charly);
  pointer-events: none;
}

.tapis-cache-corps {
  position: absolute;
  inset: 0;
  overflow: hidden;
}

.tapis-cache svg {
  position: absolute;
  inset-inline-start: 0;
  inset-block-start: 0;
  inline-size: var(--charly);
  block-size: var(--charly);
  overflow: visible;
}

.tapis-cache-corps svg {
  translate: 0 calc(var(--unite-charly) * (150 - var(--coupe)));
  transform-origin: 50% 100%;
  filter: drop-shadow(0 calc(var(--spacing) * 2) calc(var(--spacing) * 4) rgb(102 71 240 / .35));
  transition:
    translate var(--motion-slow) var(--ease-out),
    rotate var(--motion-slow) var(--ease-out);
}

.tapis-cache-mains svg {
  translate: 0 calc(var(--unite-charly) * (150 - var(--coupe-mains)));
  filter: drop-shadow(0 calc(var(--spacing) * 2) calc(var(--spacing) * 2) rgb(28 28 28 / .22));
  transition: translate var(--motion-slow) var(--ease-out);
}

/* Le corps est blanc, éclairé du violet vers le bleu, comme dans la pilule. */
.tapis-cache svg > g > path,
.tapis-cache svg > g > g:not([clip-path]) > path { fill: url(#charly-lumiere); }
.tapis-cache [data-main-cachee] { display: none; }

/* Il se découvre : les yeux passent l'arête, la tête penche un peu vers la
   carte, et les mains serrent d'un pixel. */
.tapis:hover .tapis-cache,
.tapis[data-coucou] .tapis-cache { --coupe: 30; }
.tapis:hover .tapis-cache-corps svg,
.tapis[data-coucou] .tapis-cache-corps svg { rotate: -6deg; }
.tapis:hover .tapis-cache-mains svg,
.tapis[data-coucou] .tapis-cache-mains svg { translate: 0 calc(var(--unite-charly) * (150 - var(--coupe-mains)) + 1px); }

@media (prefers-reduced-motion: reduce) {
  .tapis-cache svg { transition: none; }
}

/* Empilé sous les atouts (sous 1 024 px), la carte n'a que 40 px d'air
   au-dessus d'elle. Charly y est plus petit, il se découvre moins haut (les
   yeux à moitié), et la carte prend 12 px de plus : sans ça, en se
   découvrant, il montait sur la carte d'au-dessus. */
@media (max-width: 1023px) {
  .tapis { margin-block-start: calc(var(--spacing) * 12); }
  .tapis-cache {
    --charly: calc(var(--spacing) * 88);
    inset-inline-end: 14%;
  }
  .tapis:hover .tapis-cache,
  .tapis[data-coucou] .tapis-cache { --coupe: 12; }
}
