/* =========================================================================
   GUÉNO — sections
   ========================================================================= */

.section{padding-block:var(--block)}
.section__head{
  display:flex;align-items:baseline;justify-content:space-between;
  gap:var(--gap) clamp(20px,3vw,60px);flex-wrap:wrap;
  padding-bottom:clamp(18px,2vw,28px);
  border-bottom:1px solid var(--line);
  margin-bottom:clamp(24px,3vw,44px);
}
.section__head .label{padding-bottom:.6em}

/* ---------- Le hero -----------------------------------------------------
   Le sujet occupe le tiers droit du cadre et regarde vers la gauche : la
   moitié gauche accueille le contenu, et il le regarde. Aucun miroir.

   La vie ne dépend pas du défilement : l'image se pose à l'arrivée, le texte
   monte en cascade, et le cadre dérive en continu — très lentement — tout en
   suivant le pointeur. Un seul transform, écrit par une seule boucle.
   ------------------------------------------------------------------------- */

.hero{position:relative;height:calc(100svh + 55vh)}
.hero__stick{position:sticky;top:0;height:100svh;overflow:hidden}

.hero__frame{
  position:absolute;inset:0;overflow:hidden;
  transform:scale(calc(1 - var(--p,0) * .065));
  border-radius:calc(var(--p,0) * var(--r-lg));
  will-change:transform;
}

/* Le mouvement est autonome : une dérive lente en CSS, sans rien à faire et
   sans suivre le pointeur. L'image respire, elle ne réagit pas. */
.hero__pan{
  position:absolute;inset:-1.5%;
  will-change:transform;
  animation:drift 34s ease-in-out infinite;
}
/* Plus de mise à l'échelle : elle grossissait l'image jusqu'à 106 % en plus
   du débord du cadre, et la photographie paraissait déjà trop rapprochée. Il
   ne reste qu'un très lent glissement, que la marge de 1,5 % suffit à couvrir. */
@keyframes drift{
  0%   {transform:translate3d(-1.1%, .5%, 0)}
  50%  {transform:translate3d(1.1%, -.6%, 0)}
  100% {transform:translate3d(-1.1%, .5%, 0)}
}
.hero__img{
  position:absolute;inset:0;width:100%;height:100%;
  object-fit:cover;object-position:50% 50%;
}
/* la pose à l'arrivée, séparée du reste pour ne pas se disputer le transform */
.js .hero__img{transform:scale(1.07);transition:transform 2.2s var(--ease-out)}
.js .hero.is-ready .hero__img{transform:none}

.hero__frame::after{
  content:"";position:absolute;inset:0;pointer-events:none;
  /* Le voile est CONCENTRÉ sur le bloc de texte, en bas à gauche, au lieu
     d'être nappé sur toute la photographie. Un noir translucide multiplie
     l'image : il garde les rapports de couleur mais écrase la dynamique, et
     une nappe large délavait la scène entière. Ici les deux tiers droits, où
     vivent le vélo, les circuits et l'écran, ne sont pas touchés du tout. */
  background:
    radial-gradient(44% 60% at 7% 56%,
      rgba(8,7,6,.76) 0%, rgba(8,7,6,.46) 46%, transparent 78%),
    linear-gradient(to right,
      rgba(8,7,6,.44) 0%, rgba(8,7,6,.16) 22%, transparent 44%);
}

/* ---------- Le repérage ---------------------------------------------------
   Le viseur du curseur n'est pas une décoration : c'est un système. À
   l'arrivée, un second viseur se pilote tout seul, balaie la photographie,
   s'arrête sur trois éléments et annonce ce qu'il a reconnu — une machine qui
   lit l'image, sur le site de quelqu'un qui travaille avec une machine.

   Il ne joue qu'une fois. Au premier geste — souris, molette, défilement,
   touche — il s'efface et rend la main. Le reste du site est calme.
   ------------------------------------------------------------------------- */

.scan{
  position:absolute;inset:0;z-index:4;pointer-events:none;
  opacity:0;transition:opacity .5s var(--ease);
}
.scan.is-on{opacity:1}

.scan__box{
  position:absolute;left:0;top:0;
  width:var(--sw,120px);height:var(--sh,120px);
  transform:translate3d(var(--sx,0px),var(--sy,0px),0);
  transition:transform .62s var(--ease-out),
             width .62s var(--ease-out), height .62s var(--ease-out);
}
.scan__box > i{
  position:absolute;width:15px;height:15px;
  border:1.5px solid rgba(247,244,238,.92);
}
.scan__box > i:nth-child(1){top:0;left:0;border-right:0;border-bottom:0}
.scan__box > i:nth-child(2){top:0;right:0;border-left:0;border-bottom:0}
.scan__box > i:nth-child(3){bottom:0;right:0;border-left:0;border-top:0}
.scan__box > i:nth-child(4){bottom:0;left:0;border-right:0;border-top:0}

/* La fiche : même matière que les pilules de la barre haute — voile sombre,
   flou d'arrière-plan, filet clair. Elle ne se montre qu'une fois le cadre
   arrivé sur sa cible. */
/* Largeur et hauteur minimales fixes : la fiche est posée par le script,
   qui a besoin d'une taille stable avant de savoir où la mettre. */
.scan__card{
  position:absolute;left:var(--kx,0px);top:var(--ky,0px);
  width:268px;min-height:118px;
  padding:12px 15px 14px;
  background:rgba(12,11,10,.92);
  -webkit-backdrop-filter:blur(14px);backdrop-filter:blur(14px);
  border:1px solid rgba(247,244,238,.18);border-radius:14px;
  opacity:0;transform:translateY(7px);
  transition:opacity .28s var(--ease),transform .28s var(--ease-out),
             left .52s var(--ease-out),top .52s var(--ease-out);
}
.scan.is-read .scan__card{opacity:1;transform:none}

.scan__kind{
  display:flex;align-items:baseline;justify-content:space-between;gap:1.4em;
  font-size:var(--t-label);letter-spacing:.14em;text-transform:uppercase;
  color:var(--text-dim);
}
.scan__kind b{font-weight:500;color:var(--accent)}
.scan__name{
  font-family:var(--font-fat);font-weight:800;
  font-size:1.4rem;line-height:.94;letter-spacing:.005em;text-transform:uppercase;
  color:#F7F4EE;min-height:1.32em;
}
.scan__note{font-size:var(--t-small);line-height:1.45;color:var(--text-dim);padding-top:5px;min-height:1.45em}

/* La vignette, découpée dans la zone que le viseur vient de cadrer. */
.scan__row{display:flex;gap:12px;align-items:flex-start;padding-top:10px}
.scan__vue{
  width:66px;height:66px;flex:none;border-radius:9px;object-fit:cover;
  border:1px solid rgba(247,244,238,.16);
  transition:filter .5s var(--ease),opacity .5s var(--ease);
}
.scan__txt{min-width:0;flex:1}

/* L'état « analyse » : la machine cherche avant de répondre. Deux barres
   balayées, la vignette encore floue, le taux pas encore tombé. */
.scan__card.is-think .scan__vue{filter:blur(7px);opacity:.55}
.scan__bars{display:none;flex-direction:column;gap:8px;padding-top:3px}
.scan__card.is-think .scan__bars{display:flex}
.scan__card.is-think .scan__txt{display:none}
.scan__bars i{
  display:block;height:11px;border-radius:4px;
  background:linear-gradient(90deg,
    rgba(247,244,238,.09) 0%, rgba(247,244,238,.28) 42%, rgba(247,244,238,.09) 82%);
  background-size:280% 100%;
  animation:scanwipe 1.15s linear infinite;
}
.scan__bars i:nth-child(1){width:86%}
.scan__bars i:nth-child(2){width:56%;height:9px}
@keyframes scanwipe{0%{background-position:130% 0}100%{background-position:-70% 0}}

/* Le curseur d'écriture, tant que la ligne se compose. */
.scan__name.is-typing::after,
.scan__note.is-typing::after{
  content:"";display:inline-block;width:.46em;height:.86em;
  margin-left:.1em;vertical-align:-.09em;
  background:currentColor;animation:scancaret .9s steps(1) infinite;
}
@keyframes scancaret{0%,49%{opacity:1}50%,100%{opacity:0}}

/* Sur petit écran, le cadre est trop étroit pour qu'une fiche flotte à côté
   du viseur sans le masquer. Elle devient un bandeau au bas de la photo : le
   viseur balaie au-dessus, la fiche reste toujours lisible et toujours en
   place. C'est la même idée, dans le format du téléphone. */
@media (max-width:860px){
  /* Le cadre suit maintenant le ratio de l'image, donc il ne fait plus que
     219 px de haut sur un téléphone : la fiche doit rester basse pour ne pas
     recouvrir la scène qu'elle commente. Vignette réduite, note sur une seule
     ligne, tout tient sous un tiers du cadre. */
  .scan__card{
    left:8px;right:8px;top:auto;bottom:8px;
    width:auto;min-height:0;padding:7px 10px 8px;
    transition:opacity .28s var(--ease),transform .28s var(--ease-out);
  }
  .scan__vue{width:38px;height:38px;border-radius:6px}
  .scan__row{gap:8px;padding-top:5px}
  .scan__kind{font-size:.58rem;letter-spacing:.14em}
  .scan__name{font-size:.94rem;line-height:1.05}
  .scan__note{font-size:.72rem;line-height:1.32;
    display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
  .scan__bars i{height:8px}
  .scan__bars i:nth-child(2){height:6px}
}
@media (prefers-reduced-motion:reduce){ .scan{display:none} }

.hero__over{
  position:absolute;inset:0;z-index:2;pointer-events:none;
  display:grid;align-content:center;
  padding:clamp(76px,10vh,120px) var(--pad) clamp(24px,4vh,56px);
  max-width:var(--maxw);margin-inline:auto;
}
.hero__col{
  pointer-events:auto;
  display:flex;flex-direction:column;gap:clamp(14px,1.8vh,26px);
  max-width:min(46ch, 52%);
}
/* L'interlignage serré de la condensée (.84) collait la ligne de rôle au
   nom : il lui faut une respiration que la graisse précédente n'exigeait pas. */
.hero__over .name{color:#F7F4EE;margin-bottom:.16em}
.hero__over .small{color:rgba(247,244,238,.94)}
.hero__over .lead{color:#F7F4EE;max-width:20ch}
.hero__acts{display:flex;flex-wrap:wrap;gap:8px;padding-top:clamp(4px,1vh,12px)}

/* la signature, posée en bas du cadre */
/* Le coin droit est de l'herbe claire, hors du voile : le tampon se pose à
   gauche, dans la zone que le voile protège déjà. */
.hero__stamp{
  position:absolute;left:var(--pad);bottom:clamp(24px,4vh,52px);z-index:2;
  --stamp:rgba(247,244,238,.66);--tilt:-3.5deg;
}
@media (max-width:860px){ .hero__stamp{display:none} }
.hero__acts .pill{border-color:rgba(247,244,238,.34);color:#F7F4EE}
.hero__acts .pill:hover{background:var(--cream);color:var(--on-cream);border-color:var(--cream)}
.hero__acts .pill--solid{background:var(--cream);color:var(--on-cream);border-color:var(--cream)}

/* le texte monte en cascade dès l'arrivée — rien à faire pour le déclencher */
.js .hero__col > *{
  opacity:0;transform:translate3d(0,16px,0);
  transition:opacity .9s var(--ease-out), transform .9s var(--ease-out);
}
.js .hero.is-ready .hero__col > *{opacity:1;transform:none}
.js .hero.is-ready .hero__col > *:nth-child(1){transition-delay:.20s}
.js .hero.is-ready .hero__col > *:nth-child(2){transition-delay:.34s}
.js .hero.is-ready .hero__col > *:nth-child(3){transition-delay:.48s}

@media (prefers-reduced-motion:reduce){
  .hero__pan{animation:none;transform:none}
  .js .hero__img{transform:none}
  .js .hero__col > *{opacity:1;transform:none}
}

@media (max-width:860px){
  .hero{height:auto}
  .hero__stick{position:static;height:auto;overflow:visible}
  /* Le cadre prend le RATIO DE L'IMAGE au lieu d'une hauteur imposée : à
     390 px de large, un cadre de 520 px de haut rognait 58 % de la largeur et
     il ne restait que le sujet. Ici la scène entière est visible, et les zones
     du viseur redeviennent atteignables sur téléphone. */
  .hero__frame{
    position:relative;height:auto;aspect-ratio:16/9;
    transform:none;border-radius:var(--r-lg);
  }
  .hero__img{object-position:50% 50%}
  .hero__frame::after{display:none}
  /* Le hero et la section suivante partagent le même fond dès que la
     photographie ne remplit plus l'écran : rien ne disait où l'un finissait.
     Un filet le referme, comme sous les titres de section et au-dessus du
     défilé des outils. */
  .hero__over{
    position:static;inset:auto;
    padding:clamp(20px,5vw,32px) var(--pad) clamp(30px,7vw,46px);
    border-bottom:1px solid var(--line);
    margin-inline:var(--pad);
    padding-inline:0;
  }
  .hero__col{max-width:none}
  .hero__over .name,.hero__over .lead{color:var(--text)}
  .hero__over .small{color:var(--text-dim)}
  .hero__acts .pill:not(.pill--solid){border-color:var(--line);color:var(--text)}
}

/* ---------- Ce que je fais ----------------------------------------------
   Structure reprise d'Ilja : le titre occupe la colonne de gauche (il n'est
   pas collant chez lui non plus), les quatre tuiles la colonne de droite, le
   bouton dessous. La ligne concevoir → livrer descend ici : elle n'a plus à
   charger la section À propos.
   ------------------------------------------------------------------------- */

.skills{
  display:grid;
  grid-template-columns:minmax(0,1fr) minmax(0,2fr);
  gap:clamp(28px,4vw,90px);
  align-items:start;
}
.skills__head{display:flex;flex-direction:column;gap:clamp(16px,1.8vw,26px)}
.skills__head .body{max-width:34ch}

.flow{
  display:flex;align-items:center;flex-wrap:wrap;gap:clamp(8px,1vw,14px);
  padding-block:clamp(14px,1.6vw,20px);
  border-top:1px solid var(--line);border-bottom:1px solid var(--line);
}
.flow span{font-size:var(--t-small);font-weight:500;letter-spacing:.02em}
.flow i{display:block;width:clamp(14px,1.8vw,26px);height:1px;background:var(--line);flex:none}

.skills__grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--gap)}

.skill{
  display:flex;flex-direction:column;gap:clamp(14px,1.6vw,20px);
  min-height:clamp(230px,24vw,300px);
}
.skill__ic{
  width:50px;height:50px;border-radius:14px;flex:none;
  background:var(--cream);color:var(--accent);
  display:grid;place-items:center;
}
.skill__ic svg{width:24px;height:24px}
.skill h3{font-size:var(--t-body);font-weight:500;letter-spacing:-.01em}
.skill__b{margin-top:auto;display:flex;flex-direction:column;gap:12px}
.skill__b p{font-size:var(--t-small);color:var(--text-dim);line-height:1.55}
.skill__tags{display:flex;flex-wrap:wrap;gap:6px}


/* ---- Le défilé des outils ------------------------------------------------
   Deux groupes identiques dans la piste : la translation de -50 % ramène le
   second exactement là où le premier commençait, la boucle est invisible.
   Les bords s'estompent par un masque plutôt que par un dégradé posé dessus,
   pour que le fond de section reste libre. ------------------------------- */
/* La section borne le débordement : 100vw dépasse la zone visible de la largeur
   de la barre de défilement sur les postes à barres classiques. `clip` coupe sans
   créer de conteneur de défilement — il ne casserait pas un position:sticky, et
   #faire n'en contient de toute façon aucun. */
#faire{overflow-x:clip}
.tools{
  /* pleine largeur : on sort de la gouttière et du max-width du .wrap */
  width:100vw;margin-inline:calc(50% - 50vw);
  margin-top:clamp(30px,4vw,56px);
  border-top:1px solid var(--line);border-bottom:1px solid var(--line);
  padding:clamp(15px,1.7vw,23px) 0;
  overflow:hidden;
  -webkit-mask-image:linear-gradient(to right,transparent 0,#000 7%,#000 93%,transparent 100%);
          mask-image:linear-gradient(to right,transparent 0,#000 7%,#000 93%,transparent 100%);
}
.tools__track{display:flex;width:max-content;animation:defile 42s linear infinite}
.tools:hover .tools__track,.tools:focus-within .tools__track{animation-play-state:paused}
/* Un groupe DOIT être au moins aussi large que l'écran : la piste avance de la
   largeur d'un groupe, et s'il est plus court, le vide apparaît derrière lui
   avant que le suivant n'arrive. C'est ce qui creusait un trou au-delà de
   1024 px, où 1150 px de logos devaient couvrir jusqu'à 2560 px.
   min-width:100vw le garantit à toute largeur, et space-around répartit le
   surplus : ses demi-espaces aux extrémités se recollent en un espace entier
   à la jointure, donc le rythme ne se rompt pas. */
.tools__grp{
  display:flex;align-items:center;justify-content:space-around;
  min-width:100vw;
  gap:clamp(30px,3.6vw,60px);
  margin:0;padding:0;list-style:none;
}
.tools__i{display:inline-flex;align-items:center;gap:10px;color:var(--text-dim);white-space:nowrap}
.tools__i svg{width:19px;height:19px;flex:none;color:var(--text-dim)}
.tools__i span{font-size:var(--t-small);font-weight:500;letter-spacing:.015em}
@keyframes defile{from{transform:translate3d(0,0,0)}to{transform:translate3d(-50%,0,0)}}

@media (prefers-reduced-motion:reduce){
  .tools__track{animation:none}
  .tools{overflow-x:auto;-webkit-mask-image:none;mask-image:none}
}

@media (max-width:1080px){ .skills{grid-template-columns:1fr;gap:clamp(24px,4vw,40px)} }
@media (max-width:620px){ .skills__grid{grid-template-columns:1fr} }

/* Les lignes servent encore à la timeline d'À propos */
.rows{border-top:1px solid var(--line)}
.rows__r{
  display:grid;
  grid-template-columns:3.5rem minmax(0,10rem) minmax(0,1fr);
  gap:var(--gap);
  padding-block:clamp(16px,1.8vw,26px);
  border-bottom:1px solid var(--line);
  align-items:baseline;
}
.rows__r > .label:first-child{color:var(--text-dim)}
.rows__r h3{font-size:var(--t-body);font-weight:500;letter-spacing:-.01em}
.rows__r p{color:var(--text-dim);max-width:46ch}
@media (max-width:760px){
  .rows__r{grid-template-columns:2.5rem minmax(0,1fr);row-gap:6px}
  .rows__r p{grid-column:2}
}

/* ---------- Le travail : image à gauche, projet à droite -----------------
   Les tuiles s'empilent : `position: sticky; top: 0` sur des frères de même
   hauteur — aucun JavaScript derrière.
   ------------------------------------------------------------------------- */

/* Un léger retrait en tête de pile : sans lui, l'écart entre le titre de
   section et la première carte tombait à 250 px quand les autres respirations
   entre sections en font 276. */
.stack{position:relative;padding-top:clamp(14px,1.8vw,26px);padding-bottom:var(--block)}

.stack__card{
  position:sticky;top:0;height:100svh;
  display:grid;
  padding:clamp(10px,1.4vw,22px);
  padding-top:clamp(58px,7vh,86px);
}
.stack__inner{
  position:relative;
  min-height:0;
  background:var(--proj);color:var(--proj-fg);
  border-radius:var(--r-lg);
  overflow:hidden;
  display:grid;
  grid-template-columns:minmax(0,1.55fr) minmax(0,1fr);
  box-shadow:0 -1px 0 0 rgba(255,255,255,.09), 0 -30px 60px -34px rgba(0,0,0,.7);
}

/* En grille, `max-height:100%` sur l'image ne résout pas contre la piste et
   l'artwork déborde. En flex, il résout. */
.stack__stage{
  min-width:0;min-height:0;
  display:flex;align-items:center;justify-content:center;
  padding:clamp(18px,2.6vw,44px);
}
.stack__stage img,
.stack__stage video{
  max-width:100%;max-height:100%;width:auto;height:auto;min-height:0;
  border-radius:8px;
  box-shadow:0 24px 60px -32px rgba(0,0,0,.7);
}
/* Un projet web se montre en mouvement : la tuile accepte une vidéo à la
   place de l'image, avec le même traitement que les bobines —
   rien avant l'approche, pause hors du champ, contrôles rendus sans script. */
.stack__v{display:block;background-size:cover;background-position:center}

.stack__side{
  min-width:0;
  display:flex;flex-direction:column;justify-content:center;
  gap:clamp(12px,1.6vw,20px);
  padding:clamp(22px,3vw,52px) clamp(22px,3vw,52px) clamp(22px,3vw,52px) 0;
  border-left:1px solid var(--proj-line);
  margin-left:clamp(4px,1vw,16px);
  padding-left:clamp(22px,3vw,52px);
}
.stack__no{font-size:var(--t-label);letter-spacing:.14em;opacity:.6}
.stack__name{font-family:var(--font-fat);font-weight:800;
             font-size:clamp(2rem,3.6vw,3.5rem);line-height:.86;
             letter-spacing:.005em;text-transform:uppercase}
.stack__said{opacity:.76;max-width:34ch;font-size:var(--t-small);line-height:1.55}
.stack__tags{display:flex;flex-wrap:wrap;gap:6px;padding-top:clamp(2px,.6vw,8px)}
/* Les outils, que les pastilles ne disaient pas : avec quoi le projet a été
   réellement fabriqué. */
.stack__tools{display:flex;gap:.8em;align-items:baseline;font-size:var(--t-small)}
.stack__tools b{
  font-size:var(--t-label);letter-spacing:.14em;text-transform:uppercase;
  font-weight:500;opacity:.72;flex:none;
}
.stack__stamp{
  position:absolute;top:clamp(18px,2.6vw,34px);right:clamp(18px,2.6vw,34px);z-index:2;
  --stamp:color-mix(in srgb, var(--proj-fg) 62%, transparent);--tilt:3deg;
}
.stack__tags .tag{border-color:var(--proj-line);color:var(--proj-fg);opacity:.9}
/* Le bouton du projet emprunte les couleurs de sa tuile : il ne peut pas
   garder les jetons du site, qui sont ceux du fond sombre. */
.stack__go{
  margin-top:clamp(6px,1vw,12px);
  border-color:var(--proj-line);color:var(--proj-fg);
}
.stack__go:hover{
  background:var(--proj-fg);color:var(--proj);border-color:var(--proj-fg);
}

@media (max-width:900px), (max-height:600px){
  .stack__card{position:static;height:auto;min-height:0;padding-top:clamp(10px,1.4vw,22px)}
  .stack__inner{grid-template-columns:1fr;box-shadow:0 18px 40px -26px rgba(0,0,0,.6)}
  .stack__stage{padding-bottom:0}
  /* Empilées, les tuiles n'ont plus de hauteur imposée : la vidéo peut prendre
     toute la largeur et réserver sa place. Sans ça elle mesurait 0 × 0 jusqu'à
     l'arrivée de son affiche, et la page sautait pendant le défilement. */
  .stack__stage video{width:100%;aspect-ratio:auto 1440/900}
  .stack__side{
    border-left:0;border-top:1px solid var(--proj-line);
    margin-left:0;padding:clamp(20px,4vw,30px);
  }
  .stack__card + .stack__card{margin-top:var(--gap)}
}

/* Une couleur par projet : c'est elle qui peint la tuile. Celles des quatre
   derniers sont de moi — les vraies dominantes restent à fournir. */
/* Taupe de la charte du domaine, assombri de 28 %. À la valeur fournie
   (#6B5644) le texte de corps, posé à 76 % d'opacité, tombait à 3,68:1,
   sous le seuil de 4,5:1. La teinte est conservée, seule la clarté baisse. */
#clos     {--proj:#4D3E31;--proj-fg:#F5EDE4;--proj-line:rgba(245,237,228,.28)}
#banquiz  {--proj:#2C2C7F;--proj-fg:#EFEFFA;--proj-line:rgba(239,239,250,.28)}
#mas      {--proj:#390504;--proj-fg:#F8E9D8;--proj-line:rgba(248,233,216,.28)}
#kairos   {--proj:#232120;--proj-fg:#EFEBE6;--proj-line:rgba(239,235,230,.28)}
#flower   {--proj:#3B1526;--proj-fg:#FBEAF1;--proj-line:rgba(251,234,241,.28)}
#sol      {--proj:#043333;--proj-fg:#E7F4EE;--proj-line:rgba(231,244,238,.28)}
#hws      {--proj:#0E1A3A;--proj-fg:#E9EFFD;--proj-line:rgba(233,239,253,.28)}
/* Le bordeaux de ses boutons, assombri pour tenir dans la pile, sa crème
   pâle en texte : 10,75:1. */
#clara    {--proj:#7A0827;--proj-fg:#FDFFCB;--proj-line:rgba(253,255,203,.28)}
/* ---------- À propos ----------------------------------------------------
   Pas de dégradé : un dégradé sombre → clair ne produit qu'une bande grise.
   La section est une FEUILLE claire qui glisse par-dessus le fond, et la
   section suivante glisse par-dessus elle. Des arêtes nettes, des angles
   généreux, aucune bouillie.
   ------------------------------------------------------------------------- */

/* --coin porte le rayon, --sheet garde le chevauchement : sans cette
   séparation, animer le rayon déplacerait la marge négative, et toute la
   page bougerait au défilement. Il faut @property pour que le navigateur
   sache interpoler une variable : sans déclaration, elle est traitée comme
   du texte et saute d'une valeur à l'autre. */
@property --coin{syntax:"<length>";inherits:false;initial-value:0px}

.sheet{
  /* --coin est POSÉ ici, pas laissé à sa valeur initiale : une propriété
     déclarée par @property vaut toujours quelque chose, donc un var(--coin,
     secours) n'atteindrait jamais son secours et les angles seraient carrés
     partout où l'animation ne tourne pas. */
  --coin:var(--sheet,clamp(26px,3.4vw,54px));
  position:relative;z-index:1;
  border-radius:var(--coin) var(--coin) 0 0;
  margin-top:calc(-1 * var(--sheet,clamp(26px,3.4vw,54px)));
}

/* Les angles s'ouvrent pendant que la feuille remonte : nets quand elle
   apparaît en bas de l'écran, pleinement arrondis quand elle a fini son
   entrée. Hors des navigateurs qui savent lier une animation au défilement,
   --coin n'est jamais posé et le rayon fixe s'applique. */
@supports (animation-timeline: view()){
  @media (prefers-reduced-motion: no-preference){
    .sheet{
      animation:coins linear both;
      animation-timeline:view();
      animation-range:entry 0% entry 100%;
    }
    /* L'écart doit être large pour se voir : d'une arête franche à un angle
       très ouvert. La marge de chevauchement, elle, ne bouge pas — le rayon
       ne fait que dégager davantage le fond sombre derrière le coin. */
    @keyframes coins{
      from{--coin:0px}
      to{--coin:clamp(52px,7.4vw,116px)}
    }
  }
}

.about{
  --sheet:clamp(26px,3.4vw,54px);
  --text:var(--on-cream);
  --text-dim:var(--on-cream-dim);
  --line:rgba(18,17,16,.16);
  color:var(--on-cream);
  background:var(--cream);
  padding-block:var(--block);
  box-shadow:0 -24px 60px -40px rgba(0,0,0,.8);
}

/* la section sombre qui suit remonte par-dessus la feuille claire */
.after-sheet{
  --sheet:clamp(26px,3.4vw,54px);
  background:var(--ground);
}

.about__grid{
  display:grid;grid-template-columns:repeat(12,minmax(0,1fr));
  gap:var(--gap);align-items:start;
}
.about__story{grid-column:1/7;display:flex;flex-direction:column;gap:clamp(16px,1.8vw,26px)}
.about__story .lead span{display:block}
.about__me{
  grid-column:8/-1;
  border-radius:var(--r-lg);overflow:hidden;
  aspect-ratio:4/5;position:relative;
}
.about__me img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;object-position:52% 30%}

.about__time{margin-top:clamp(40px,5vw,80px)}
.about__cv{margin-top:clamp(26px,3.2vw,44px)}
/* .about redéfinit --text et --line pour le fond crème : le survol par défaut
   poserait du crème sur du crème. On inverse, comme dans le pied de page. */
.about .pill:hover{background:var(--on-cream);color:var(--cream);border-color:var(--on-cream)}
.about .pill--solid{background:var(--on-cream);color:var(--cream);border-color:var(--on-cream)}
.about .pill--solid:hover{background:var(--accent);border-color:var(--accent);color:#140D08}
.about__stamp{
  margin-top:clamp(28px,3.4vw,44px);
  --stamp:rgba(18,17,16,.62);--tilt:-2.5deg;
}

@media (max-width:900px){
  .about__story{grid-column:1/-1}
  .about__me{grid-column:1/-1;aspect-ratio:5/4;margin-top:clamp(24px,4vw,36px)}
}

/* ---------- Les bobines --------------------------------------------------
   Calme au repos, comme demandé : trois tuiles, un titre chacune. Le
   désordre n'apparaît qu'à l'interaction — la tuile se soulève et pivote
   d'un cheveu, et la note se découvre.

   Les trois portent une bobine. Les fichiers sont recadrés en 4/5 à
   l'encodage — la fente est en portrait, autant ne pas transporter ce qui
   serait rogné. La bobine web fait exception : ses maquettes sont en pleine
   largeur et un recadrage centré n'en gardait qu'une tranche illisible, elle
   est donc recomposée entière sur un agrandissement flou d'elle-même.
   ------------------------------------------------------------------------- */

.quests{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--gap)}
.quest{
  display:flex;flex-direction:column;gap:var(--gap);
  min-height:250px;
  transition:transform .45s var(--ease-out), border-color .45s var(--ease);
}
/* `.js [data-in].is-in{transform:none}` est plus spécifique que `.quest:hover`
   et écrasait le mouvement : la règle de survol doit reprendre la main. */
.quest:hover,.quest:focus-within,
.js .quest[data-in].is-in:hover,
.js .quest[data-in].is-in:focus-within{
  transform:translateY(-6px) rotate(var(--tilt,-.7deg));
  border-color:color-mix(in srgb, var(--text) 26%, transparent);
}
.quest:nth-child(2n){--tilt:.8deg}
.quest:nth-child(3n){--tilt:-1.1deg}
.quest__img{border-radius:var(--r);overflow:hidden;aspect-ratio:4/5;background:var(--ground)}
.quest__img img,
.quest__img video{width:100%;height:100%;object-fit:cover;object-position:var(--focus,50% 40%)}
/* Deux tuiles portent une bobine plutôt qu'une image. Les fichiers sont
   recadrés en 4/5 à l'encodage — la fente est en portrait, autant ne pas
   transporter les 55 % de largeur qui seraient rognés à l'affichage. Sans
   son, en boucle, et elles ne tournent que dans le champ de vision. */
.quest__v{display:block;background-size:cover;background-position:center}
.quest__b{margin-top:auto;display:flex;flex-direction:column;gap:6px}
.quest h3{font-size:var(--t-body);font-weight:500;letter-spacing:-.01em}
.quest p{font-size:var(--t-small);color:var(--text-dim);max-width:30ch}
/* la note se découvre au survol — au toucher elle reste lisible */
.quest__note{
  opacity:0;transform:translateY(4px);
  transition:opacity .4s var(--ease),transform .4s var(--ease-out);
}
.quest:hover .quest__note,.quest:focus-within .quest__note{opacity:1;transform:none}
@media (hover:none){ .quest__note{opacity:1;transform:none} }
@media (prefers-reduced-motion:reduce){
  .quest:hover,.quest:focus-within{transform:none}
  .quest__note{opacity:1;transform:none}
}
/* Trois tuiles : une grille à deux colonnes en laisserait une orpheline.
   On tient donc les trois colonnes jusqu'aux tablettes — 237 px par tuile à
   768 px, ça tient — puis on passe d'un coup à une seule colonne. */
@media (max-width:620px){ .quests{grid-template-columns:1fr} }

/* ---------- En ce moment ------------------------------------------------- */

.now{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:0 clamp(24px,5vw,96px)}
.now dl{border-top:1px solid var(--line)}
.now div{display:flex;align-items:baseline;justify-content:space-between;gap:var(--gap);
         padding-block:12px;border-bottom:1px solid var(--line)}
.now dt{font-size:var(--t-small);color:var(--text-dim)}
.now dd{margin:0;font-size:var(--t-small);font-weight:500;text-align:right}
.now dd .live{color:var(--accent);font-variant-numeric:tabular-nums}
@media (max-width:760px){ .now{grid-template-columns:1fr} }

/* ---------- Le chargeur -------------------------------------------------
   Il n'existe que si le JavaScript tourne (`display:none` par défaut), et il
   se retire tout seul. Sous `prefers-reduced-motion` il ne s'affiche pas.
   ------------------------------------------------------------------------- */

.loader{display:none}
.js .loader{
  display:grid;grid-template-rows:auto 1fr auto;
  position:fixed;inset:0;z-index:400;
  background:var(--ground);color:var(--text);
  padding:var(--pad);
  transition:transform .85s var(--ease-out);
  will-change:transform;
}
.loader.is-done{transform:translate3d(0,-101%,0)}
.loader__top{display:flex;justify-content:space-between;gap:var(--gap);align-items:flex-start}
.loader__mid{display:grid;align-content:center;justify-items:end}
/* Largeur réservée : sans elle, chaque chiffre ajouté (0, 10, 100) déplaçait
   le compteur, et Google comptait ces sauts comme un décalage de mise en page
   (CLS 0,12 sur ordinateur, au-dessus du seuil de 0,1). */
.loader__count{
  font-family:var(--font-fat);font-weight:800;
  font-size:clamp(5rem,26vw,26rem);line-height:.8;
  letter-spacing:.005em;font-variant-numeric:tabular-nums;
  min-width:2.3em;text-align:right;
}
.loader__count sup{font-size:.28em;vertical-align:super;margin-left:.06em;opacity:.5}
.loader__foot{display:flex;flex-direction:column;gap:var(--gap)}
.loader__bar{position:relative;height:1px;background:var(--line);overflow:hidden}
.loader__bar i{
  position:absolute;inset:0 auto 0 0;display:block;
  width:calc(var(--pct,0) * 1%);background:var(--accent);
  transition:width .25s linear;
}
.loader__row{display:flex;justify-content:space-between;gap:var(--gap)}
@media (prefers-reduced-motion:reduce){ .js .loader{display:none} }

/* ---------- Pied --------------------------------------------------------
   Panneau clair inversé, presque plein écran : trois zones — informations en
   haut, l'invitation en grand au centre, liens et mentions en bas.
   ------------------------------------------------------------------------- */

.foot{
  --text:var(--on-cream);
  --text-dim:var(--on-cream-dim);
  --line:rgba(18,17,16,.16);
  background:var(--cream);color:var(--on-cream);
  border-radius:var(--r-lg);
  margin:var(--block) clamp(10px,1.4vw,22px) clamp(10px,1.4vw,22px);
  min-height:min(94svh,940px);
  /* la barre est fixe : la rangée du haut doit passer dessous */
  padding:clamp(72px,7vw,110px) clamp(22px,2.6vw,42px) clamp(22px,2.6vw,42px);
  display:grid;grid-template-rows:auto 1fr auto;
  gap:clamp(28px,4vw,60px);
}

.foot__top{display:flex;justify-content:space-between;align-items:flex-start;gap:var(--gap)}
.foot__mark{
  font-family:var(--font-fat);font-weight:800;
  font-size:clamp(1rem,1.5vw,1.375rem);line-height:1;
  letter-spacing:.02em;text-transform:uppercase;
  display:inline-flex;align-items:flex-start;gap:.2em;
}
.foot__mark sup{font-size:.5em;line-height:1.4}
.foot__top div:last-child{text-align:right}
@media (max-width:760px){ .foot__top .foot__mark{display:none} }

.foot__mid{display:grid;align-content:center;justify-items:center;gap:clamp(20px,3vw,42px);text-align:center}
.foot__mid .blast{max-width:14ch}
.foot__mail{display:flex;flex-direction:column;align-items:center;gap:6px}
.foot__mail a{
  font-size:clamp(1.25rem,2.6vw,2.25rem);font-weight:500;letter-spacing:-.025em;
  display:inline-flex;align-items:center;gap:.4em;
  border-bottom:1px solid transparent;
  transition:border-color .3s var(--ease),color .3s var(--ease);
}
.foot__mail a:hover{border-color:currentColor;color:var(--accent-ink,#B33F0C)}
.foot__mail svg{width:.5em;height:.5em}

.foot__bottom{
  display:flex;justify-content:space-between;align-items:flex-end;
  gap:var(--gap) clamp(20px,3vw,48px);flex-wrap:wrap;
  border-top:1px solid var(--line);padding-top:clamp(18px,2vw,26px);
}
.foot__links{display:flex;flex-wrap:wrap;gap:8px}
.foot__bottom > div:last-child{text-align:right;margin-left:auto}
.foot__up{display:inline-flex;align-items:center;gap:.4em}
.foot__up svg{width:10px;height:10px;transform:rotate(-45deg)}
/* Le lien des mentions légales : même soulignement au survol que l'adresse
   e-mail, pour qu'il appartienne visiblement au même pied de page. */
.foot__lgl{border-bottom:1px solid transparent;transition:border-color .3s var(--ease),color .3s var(--ease)}
.foot__lgl:hover{border-color:currentColor;color:var(--accent-ink,#B33F0C)}
@media (max-width:760px){
  .foot__bottom > div:last-child{text-align:left;margin-left:0}
}

/* ---------- Le panneau CV -------------------------------------------------
   Ouvert par :target, fermé par un lien vers #top. Pas de JavaScript, donc il
   tient aussi dans le lien partagé et sans script. `visibility` plutôt que
   `display` pour que la transition existe, et pour que le contenu reste hors
   de l'ordre de tabulation tant qu'il est fermé.
   ------------------------------------------------------------------------- */
.cv{
  /* au-dessus de la barre (120) et du menu (130), sous le curseur (350)
     et le chargeur (400).
     `display:none` et non `visibility:hidden` : une boîte masquée est quand
     même générée, et le navigateur télécharge alors l'image du CV sur toutes
     les visites — 69 ko pour un panneau que personne n'a ouvert. */
  display:none;
  position:fixed;inset:0;z-index:200;
  place-items:center;padding:clamp(14px,3vw,40px);
}
.cv:target{display:grid}
.cv__fond{position:absolute;inset:0;background:rgba(8,7,6,.82);cursor:zoom-out}

.cv__boite{
  position:relative;
  width:min(760px,100%);height:100%;min-height:0;
  display:flex;flex-direction:column;gap:clamp(10px,1.4vw,16px);
  animation:cv-entre .34s var(--ease-out) both;
}
@keyframes cv-entre{from{opacity:0;transform:translateY(12px) scale(.987)}to{opacity:1;transform:none}}

.cv__barre{
  display:flex;align-items:center;justify-content:space-between;
  gap:clamp(10px,2vw,24px);flex-wrap:wrap;color:var(--text);
}
.cv__acts{display:flex;gap:8px;flex-wrap:wrap}
.cv .pill{border-color:rgba(247,244,238,.34);color:#F7F4EE}
.cv .pill:hover{background:var(--cream);color:var(--on-cream);border-color:var(--cream)}
.cv .pill--solid{background:var(--cream);color:var(--on-cream);border-color:var(--cream)}
.cv .pill--solid:hover{background:var(--accent);border-color:var(--accent);color:#140D08}

/* La barre garde sa hauteur, la vue prend le reste : l'image se contraint
   alors par la HAUTEUR disponible et non par la largeur, sinon une page A4
   déborde de l'écran dès que la fenêtre est plus large que haute. */
.cv__barre{flex:0 0 auto}
.cv__vue{flex:1 1 auto;min-height:0;display:flex;justify-content:center;align-items:flex-start}
.cv__page{
  display:block;width:auto;height:auto;
  max-width:100%;max-height:100%;
  border-radius:clamp(6px,.8vw,12px);
  box-shadow:0 40px 90px -40px rgba(0,0,0,.9);
}
@media (prefers-reduced-motion:reduce){
  .cv__boite{animation:none}
}


/* =========================================================================
   SHOWREEL
   Trois temps, qui ne se chevauchent pas : la vidéo grandit d'abord jusqu'au
   bord à bord, puis la phrase monte depuis le bas, puis tout se retire. Un
   premier réglage lançait les deux ensemble et laissait ensuite un long
   moment où plus rien ne bougeait : le défilement paraissait mort.

   Les mesures animées passent par @property : sans déclaration typée, le
   navigateur ne sait pas interpoler une longueur portée par une variable.
   Corollaire appris à nos dépens : une variable déclarée par @property vaut
   toujours quelque chose, donc les valeurs de repli sont posées en clair sur
   .reel__frame et jamais laissées à un var(--x, repli) inatteignable.
   ========================================================================= */
@property --reel-l{syntax:"<percentage>";inherits:false;initial-value:100%}
@property --reel-h{syntax:"<length>";inherits:false;initial-value:100svh}
@property --reel-o{syntax:"<number>";inherits:false;initial-value:1}
@property --reel-r{syntax:"<length>";inherits:false;initial-value:0px}

.reel{background:var(--ground);position:relative}
.reel__rail{height:200svh}
.reel__stick{position:sticky;top:0;height:100svh;display:grid;place-items:center;
  overflow:clip}

/* Repli, et état de départ quand le défilement ne pilote rien. */
.reel__frame{--reel-l:92%;--reel-h:min(78svh,51.75vw);--reel-r:clamp(14px,1.6vw,28px);
  position:relative;width:var(--reel-l);height:var(--reel-h);
  border-radius:var(--reel-r);overflow:hidden;background:#000;
  box-shadow:0 30px 90px rgba(0,0,0,calc(.42 * var(--reel-r) / 26px))}
.reel__v{width:100%;height:100%;object-fit:cover;display:block}

/* Le voile suit la phrase, pas la vidéo : il n'apparaît qu'avec elle et s'en
   va avec elle. Sans lui, un texte clair posé sur un showreel qui contient
   lui-même du texte clair devient illisible. */
.reel__voile{--reel-o:0;position:absolute;inset:0;pointer-events:none;
  opacity:var(--reel-o);
  background:linear-gradient(to bottom, rgba(8,7,6,.30) 0%, rgba(8,7,6,.62) 30%,
    rgba(8,7,6,.70) 54%, rgba(8,7,6,.60) 80%, rgba(8,7,6,.40) 100%)}

/* Ce n'est pas une phrase mais le tampon du site, composé exactement comme
   celui du pied de page : voix grasse, capitales, le petit TM à un demi-
   cadratin. Le bloc extérieur masque, l'intérieur monte depuis le bas. */
/* La largeur est donnée en pixels, pas en ch : l'unité ch se résout sur la
   taille de police de l'élément qui la porte, ici le paragraphe à 16 px, et
   non sur celle du texte à l'intérieur. Le premier essai donnait une colonne
   de 168 px et onze lignes. */
.reel__mot{position:absolute;left:50%;top:54%;translate:-50% -50%;
  width:min(1560px,92vw);margin:0;overflow:hidden;pointer-events:none;
  padding:.1em 0}
.reel__mot-i{display:block;font-family:var(--font-fat);font-weight:800;
  font-size:clamp(2.6rem,9.4vw,9rem);line-height:1;letter-spacing:.02em;
  text-transform:uppercase;text-align:center;white-space:nowrap;
  color:var(--cream);translate:0 112%;opacity:0}
.reel__mot-i sup{font-size:.5em;line-height:1.4;vertical-align:super;
  text-decoration:none;letter-spacing:0}

/* Le viseur du site se cale sur le centre de sa cible. Sur une vidéo qui
   occupe tout l'écran, il se fige donc au milieu et l'on ne sait plus où l'on
   pointe : impossible de viser l'interrupteur. Sur cette section, le curseur
   natif reprend la main. */
.js.fine .reel,
.js.fine .reel *{cursor:auto}
.js.fine .reel__son{cursor:pointer}

/* L'interrupteur est centré en bas, comme sur la référence. */
.reel__son{--reel-o:1;opacity:var(--reel-o);
  position:absolute;left:50%;translate:-50% 0;bottom:clamp(18px,3.4vh,38px);
  display:inline-flex;align-items:center;gap:12px;
  padding:9px 15px;border:1px solid var(--line);border-radius:999px;
  background:rgba(18,17,16,.62);backdrop-filter:blur(8px);
  color:var(--text);font:inherit;font-size:.8125rem;letter-spacing:.02em;
  cursor:pointer;transition:border-color .2s,background .2s}
.reel__son:hover{border-color:var(--accent)}
.reel__son-mot{text-transform:uppercase;letter-spacing:.14em;font-weight:700;font-size:.6875rem}
.reel__son-rail{width:34px;height:19px;border-radius:999px;background:var(--line);
  position:relative;transition:background .22s}
.reel__son-pastille{position:absolute;top:2px;left:2px;width:15px;height:15px;
  border-radius:50%;background:var(--text);transition:translate .22s,background .22s}
.reel__son[aria-pressed="true"] .reel__son-rail{background:var(--accent)}
.reel__son[aria-pressed="true"] .reel__son-pastille{translate:15px 0;background:#121110}

@supports (animation-timeline: view()){
  @media (prefers-reduced-motion: no-preference){
    .reel{timeline-scope:--reel}
    .reel__rail{view-timeline-name:--reel}

    /* Le bloc collant tient de 25 % à 75 % du parcours. Tout se joue dedans :
       la croissance sur la première moitié, la phrase sur la seconde. */
    .reel__frame{animation:reel-grandit linear both;animation-timeline:--reel;
      animation-range:cover 12% cover 40%}
    @keyframes reel-grandit{
      from{--reel-l:54%;--reel-h:48svh;--reel-r:24px}
      84% {--reel-r:9px}
      to  {--reel-l:100%;--reel-h:100svh;--reel-r:0px}
    }

    .reel__mot-i{animation:reel-monte linear both;animation-timeline:--reel;
      animation-range:cover 44% cover 66%}
    @keyframes reel-monte{
      0%  {translate:0 112%;opacity:0}
      34% {opacity:1}
      70% {translate:0 0;opacity:1}
      100%{translate:0 0;opacity:1}
    }

    .reel__voile{animation:reel-voile linear both;animation-timeline:--reel;
      animation-range:cover 44% cover 66%}
    @keyframes reel-voile{0%{--reel-o:0} 40%{--reel-o:1} 100%{--reel-o:1}}

    .reel__son{animation:reel-bouton linear both;animation-timeline:--reel;
      animation-range:cover 16% cover 74%}
    @keyframes reel-bouton{0%{--reel-o:0} 12%{--reel-o:1} 84%{--reel-o:1} 100%{--reel-o:0}}
  }
}

/* Sur téléphone, la vidéo reste en 16:9 du début à la fin : un cadre 16:9 porté
   au plein écran d'un appareil très allongé obligerait à rogner les deux tiers
   de l'image. */
@media (max-width:860px){
  .reel__rail{height:190svh}
  .reel__frame{--reel-l:94%;--reel-h:52.9vw}
  .reel__mot{width:min(560px,92vw);top:50%}
  .reel__mot-i{font-size:clamp(1.6rem,10.4vw,3rem)}
  @supports (animation-timeline: view()){
    @media (prefers-reduced-motion: no-preference){
      @keyframes reel-grandit{
        from{--reel-l:74%;--reel-h:41.6vw;--reel-r:14px}
        84% {--reel-r:6px}
        to  {--reel-l:100%;--reel-h:56.3vw;--reel-r:0px}
      }
    }
  }
}
