/* ============================================================
   lesgriotsxstudio — minimal cabinet/viewer
   ============================================================ */

/* Local Geist + Geist Mono webfonts (woff2) */
@font-face {
  font-family: "Geist";
  src: url("fonts/Geist-Regular.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Geist";
  src: url("fonts/Geist-Medium.woff2") format("woff2");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Geist";
  src: url("fonts/Geist-Bold.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Geist Mono";
  src: url("fonts/GeistMono-Regular.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Geist Mono";
  src: url("fonts/GeistMono-Medium.woff2") format("woff2");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Geist Mono";
  src: url("fonts/GeistMono-Bold.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

:root {
  --bg: #050505;
  --ink: #8a7a20;
  --ink-dim: #5a5018;
  --rule: #1a1814;
  --yellow: #f6e21c;
  --yellow-deep: #d9c510;
  --black: #0d0c0a;

  --font-display: "Instrument Serif", "Times New Roman", serif;
  --font-sans:   "Geist", "Inter", system-ui, -apple-system, sans-serif;
  --font-mono:   "Geist Mono", "JetBrains Mono", "IBM Plex Mono", "Space Mono", "Courier New", monospace;
  --font-mono-fine: "Geist Mono", "JetBrains Mono", "IBM Plex Mono", "Space Mono", "Courier New", monospace;
}

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

html, body {
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-mono);
  font-size: 14px;
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
  font-feature-settings: "ss01";
}

/* Terminal caret — same look & rhythm as the cursor in the INDEX (I)
   view of the Work page: full block character, 1s blink, hard steps. */
.caret {
  display: inline-block;
  width: 1ch;
  color: var(--ink);
  margin-left: 1px;
  animation: caret-blink 1s steps(1) infinite;
  font-family: var(--font-mono);
}
@keyframes caret-blink {
  0%, 49%   { opacity: 1; }
  50%, 100% { opacity: 0; }
}

body {
  overflow-x: hidden;
  min-height: 100vh;
  cursor: default;
}

/* ---------- Curseur pixel jaune custom ------------------------------ */
/* Flèche pixel art jaune sur fond transparent + contour noir 1px (pour
   rester visible sur fond clair OU foncé). 24x24 = bonne taille Retina.
   Le hotspot est à 1,1 (la pointe en haut à gauche).                   */
html, body, html * {
  cursor: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' shape-rendering='crispEdges'><polygon points='1,1 1,17 5,13 8,20 11,19 8,12 14,12' fill='black' stroke='%238a7a20' stroke-width='1' stroke-linejoin='miter'/></svg>") 1 1, default;
}
/* Main pointée pixel art noire contour jaune pour tout ce qui est cliquable.
   Hotspot à 8,1 (la pointe du doigt en haut).
   /!\ React lie les handlers via addEventListener — donc le sélecteur
   `[onclick]` ne capture PAS les onClick JSX. Pour les <video> et <img>
   du viewer (cliquables pour play/pause), on les target explicitement
   ci-dessous. */
a, button, [role="button"], label, summary, select,
.is-clickable, [onclick],
.viewer__media-wrap video,
.viewer__media-wrap img,
.ahome__cell img,
.ahome__cell video,
.viewer__thumb img,
.viewer__thumb video,
.idcard,
.idcard__img,
.menubar__items button,
.menubar__items button *,
.menubar__items a,
.menubar__items a *,
.menubar__toggle,
.menubar__toggle *,
.menubar__lang button,
.menubar__lang button * {
  cursor: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' shape-rendering='crispEdges'><path d='M8 1 L11 1 L11 9 L13 9 L13 7 L15 7 L15 9 L17 9 L17 11 L19 11 L19 19 L7 19 L7 14 L5 14 L5 11 L8 11 Z' fill='black' stroke='%238a7a20' stroke-width='1' stroke-linejoin='miter'/></svg>") 8 1, pointer;
}
/* Les inputs gardent un curseur texte natif — plus pratique pour saisir */
input[type="text"], input[type="email"], input[type="search"],
input[type="url"], input[type="number"], input[type="password"],
input:not([type]), textarea, [contenteditable="true"] {
  cursor: text;
}

/* Film grain — site-wide, always visible */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 200;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 1  0 0 0 0 1  0 0 0 0 1  0 0 0 0.55 0'/></filter><rect width='100%25' height='100%25' filter='url(%23n)' opacity='0.7'/></svg>");
  background-size: var(--grain-size, 180px) var(--grain-size, 180px);
  opacity: var(--grain-opacity, 0.14);
  mix-blend-mode: screen;
}

a { color: inherit; text-decoration: none; }
/* /!\ cursor: pointer retiré ici — la règle plus haut (a, button, ...)
   applique le SVG inline custom mustard. Ce reset générique du button
   venait écraser le cursor mustard par spécificité identique + ordre
   source plus bas. */
button { font: inherit; color: inherit; background: none; border: none; padding: 0; }

.app { position: relative; min-height: 100vh; width: 100%; }

/* ============================================================
   ID card (top-left, always visible) — sticker.png
   ============================================================ */

.idcard {
  position: fixed;
  top: 14px;
  left: 14px;
  width: 280px;
  z-index: 90;
  /* Curseur custom mustard pointing-finger : géré par la règle globale
     en haut du fichier (qui couvre .idcard ET .idcard__img). */
  background: transparent;
  border: 0;
  /* Transition douce pour l'effet bookmark au hover (slide vers la gauche) */
  transition: transform 0.35s cubic-bezier(0.2, 0.7, 0.2, 1);
  padding: 0;
  outline: none; /* kill le focus ring par défaut du button */
  -webkit-tap-highlight-color: transparent; /* kill le flash bleu mobile */
  transition: transform 0.35s cubic-bezier(0.2, 0.7, 0.2, 1);
  filter: drop-shadow(2px 4px 0 rgba(0, 0, 0, 0.35));
  /* PAR DÉFAUT le sticker est masqué — pas de trace à son emplacement
     pendant le boot, pas de flash au premier render. Il devient
     display:block UNIQUEMENT quand body.booted est posée (cf. App.jsx
     useLayoutEffect → appliqué avant le premier paint).
     Animation slide-in déclenchée à l'apparition (animation: both
     garde l'état final si on quitte la page). */
  display: none;
}
body.booted .idcard {
  display: block;
  /* fill-mode "backwards" : applique la from-keyframe AVANT le start
     (évite le flash à t=0 où le sticker apparaitrait à sa position
     finale puis sauterait à -100%), MAIS relâche le transform à la
     fin de l'animation pour que .idcard:hover puisse le surcharger.
     Avec "both" ou "forwards" l'animation aurait gardé son to-keyframe
     en mémoire et bloqué tout autre transform. */
  animation: idcard-slide-in 700ms cubic-bezier(0.2, 0.7, 0.2, 1) backwards;
}
@keyframes idcard-slide-in {
  from {
    transform: translateX(calc(-100% - 14px));
    opacity: 0;
  }
  to {
    transform: translateX(0);
    opacity: 1;
  }
}
/* Effet bookmark : au hover le sticker glisse vers la gauche comme un
   marque-page qu'on tire pour le retirer du livre. Le slide-in initial
   (idcard-slide-in) doit être fini sinon il y a conflit avec le hover —
   pas de souci, l'animation a both/forwards et finit en transform: none.
   /!\ Réservé aux DEVICES AVEC POINTEUR (souris) — sur tactile ce serait
   un trap "tap then tap" : 1er tap = hover, 2e tap = clic. */
@media (hover: hover) and (pointer: fine) {
  .idcard:hover { transform: translateX(-10px); }
}
.idcard__img { display: block; width: 100%; height: auto; }

/* Label "HOME" / "ACCUEIL" flottant sous le sticker — même esprit que
   le .ahome__cell__label des miniatures. Caché par défaut, révélé au
   hover du sticker. Style mono, mustard, uppercase. */
.idcard__label {
  position: absolute;
  left: 14px;            /* aligné sur le bord gauche du sticker */
  top: calc(100% + 8px); /* juste en dessous du sticker, 8px d'air */
  pointer-events: none;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink);
  white-space: nowrap;
  opacity: 0;
  transition: opacity 0.18s ease;
}
/* Pas de caret > devant — le label affiche juste "HOME" / "ACCUEIL" */
/* Le label n'apparait qu'aux devices avec souris (hover natif). Sur
   tactile, un seul tap doit suffire à naviguer — pas d'étape "hover"
   intermédiaire qui demande un 2e tap. */
@media (hover: hover) and (pointer: fine) {
  .idcard:hover .idcard__label { opacity: 1; }
}
/* Hide the sticker on the ecosystem page */
body:has(.eco3) .idcard { display: none !important; }

/* Sticker du boot loader — même look que .idcard (sticker mustard fixé
   en haut à gauche). On déclare la base ICI plutôt qu'à côté du reste
   du CSS boot-matrix (plus bas dans le fichier) parce que les @media
   queries qui le repassent à 180px arrivent juste après, et qu'on a
   besoin de cette base AVANT pour que la cascade fonctionne (même
   spécificité = la dernière déclaration gagne). */
.boot-matrix__sticker {
  position: fixed;
  left: 14px;
  top: 14px;
  width: 280px;
  height: auto;
  pointer-events: none;
  filter: drop-shadow(2px 4px 0 rgba(0,0,0,0.5));
  z-index: 1;
}

/* Sticker mobile : on bascule à 180px dès que le viewer passe en mobile
   (≤1200px), pour que le boot ne paraisse pas démesuré sur tablette
   paysage / petit laptop par rapport au sticker de la page work une
   fois entré dans le site. Le coin reste à 14px ici, on resserre à
   10px seulement sur les vrais petits écrans (≤760px, plus bas). */
@media (max-width: 1200px) {
  .idcard,
  .boot-matrix__sticker { width: 180px; }
}

/* ============================================================
   Boot loader — CRT terminal screen
   ============================================================ */

.boot {
  position: fixed;
  inset: 0;
  z-index: 9999;
  background: #000;
  color: #eafff5;
  font-family: var(--font-mono);
  display: grid;
  place-items: center;
  pointer-events: none;
  transition: opacity 0.55s ease;
}
.boot.is-leaving { opacity: 0; }

/* The actual CRT "tube" — full viewport, amber phosphor cast, heavy grain. */
.boot__screen {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border-radius: 0;
  overflow: hidden;
  padding: clamp(40px, 6vw, 90px) clamp(36px, 6vw, 80px);
  background:
    radial-gradient(ellipse at 50% 45%,
      #2a2410 0%,
      #1c1808 45%,
      #0e0c04 75%,
      #050402 100%);
  filter: saturate(0.95) contrast(1.1);
  animation: crt-on 0.7s ease-out;
}
@keyframes crt-on {
  0%   { transform: scaleY(0.005); opacity: 0; filter: brightness(2); }
  40%  { transform: scaleY(1.02); opacity: 1; filter: brightness(1.6); }
  100% { transform: scaleY(1); opacity: 1; filter: brightness(1) saturate(0.95) contrast(1.1); }
}

/* Heavy noise on top of the screen — amber tint, animated */
.boot__screen::before {
  content: "";
  position: absolute;
  inset: -20%;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='320' height='320'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='1.2' numOctaves='3' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 1  0 0 0 0 0.9  0 0 0 0 0.4  0 0 0 1 0'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>");
  background-size: 260px 260px;
  mix-blend-mode: screen;
  opacity: 0.95;
  pointer-events: none;
  animation: noise-shift 0.09s steps(1) infinite;
  will-change: transform, background-position;
}

/* Second noise layer — slower, larger, screen-blended for added grit */
.boot__screen .noise2 {
  position: absolute;
  inset: -20%;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='480' height='480'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.55' numOctaves='2' stitchTiles='stitch' seed='4'/><feColorMatrix values='0 0 0 0 1  0 0 0 0 0.85  0 0 0 0 0.35  0 0 0 0.9 0'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>");
  background-size: 520px 520px;
  mix-blend-mode: overlay;
  opacity: 0.75;
  pointer-events: none;
  animation: noise-drift 0.32s steps(1) infinite;
  z-index: 1;
}

@keyframes noise-shift {
  0%   { transform: translate(0,0); }
  10%  { transform: translate(-18px,4px); }
  20%  { transform: translate(12px,-9px); }
  30%  { transform: translate(-6px,-14px); }
  40%  { transform: translate(16px,12px); }
  50%  { transform: translate(-22px,3px); }
  60%  { transform: translate(8px,-18px); }
  70%  { transform: translate(-14px,16px); }
  80%  { transform: translate(20px,-4px); }
  90%  { transform: translate(-10px,-12px); }
  100% { transform: translate(4px,8px); }
}
@keyframes noise-drift {
  0%   { transform: translate(0,0) scale(1); }
  25%  { transform: translate(-30px,20px) scale(1.02); }
  50%  { transform: translate(40px,-30px) scale(0.98); }
  75%  { transform: translate(-20px,-40px) scale(1.03); }
  100% { transform: translate(25px,30px) scale(1); }
}

/* Scanlines + vignette + curved-edge dark falloff */
.boot__screen::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    radial-gradient(ellipse at center,
      transparent 55%,
      rgba(0, 0, 0, 0.55) 92%,
      rgba(0, 0, 0, 0.9) 100%),
    repeating-linear-gradient(
      0deg,
      rgba(0, 0, 0, 0) 0px,
      rgba(0, 0, 0, 0) 2px,
      rgba(0, 0, 0, 0.18) 3px,
      rgba(0, 0, 0, 0.18) 3px
    );
  mix-blend-mode: multiply;
}

/* Reflection sweep along the top of the tube */
.boot__screen .glare {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 28%;
  background: linear-gradient(180deg, rgba(255,255,255,0.07), transparent);
  pointer-events: none;
  z-index: 2;
}

/* All text content sits above the noise — amber phosphor */
.boot__screen > *:not(.glare) {
  position: relative;
  z-index: 3;
  color: #f0d860;
  text-shadow: 0 0 6px rgba(246, 226, 28, 0.35), 0 0 14px rgba(246, 226, 28, 0.18);
}

.boot__header {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: #c9a832;
  opacity: 0.9;
}
.boot__header .dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  background: #f6e21c;
  border-radius: 50%;
  margin-right: 8px;
  vertical-align: 1px;
  box-shadow: 0 0 6px #f6e21c;
  animation: blink 1.1s steps(2) infinite;
}

.boot__lines {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  font-size: clamp(13px, 1.25vw, 16px);
  letter-spacing: 0.06em;
  line-height: 1.85;
  text-transform: uppercase;
  width: min(82%, 720px);
  text-align: left;
}
.boot__lines .ok  { color: #f6e21c; }
.boot__lines .dim { color: #b08a1f; opacity: 0.85; }

.boot__bar-wrap {
  margin-top: 26px;
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: #c9a832;
}
.boot__bar {
  margin-top: 10px;
  height: 4px;
  background: rgba(246, 226, 28, 0.12);
  position: relative;
  overflow: hidden;
}
.boot__bar__fill {
  position: absolute;
  inset: 0 100% 0 0;
  background: #f6e21c;
  box-shadow: 0 0 10px rgba(246, 226, 28, 0.6);
  transition: right 0.18s linear;
}
.boot__bar__pct {
  margin-top: 8px;
  font-variant-numeric: tabular-nums;
  color: #f6e21c;
}

.boot__footer {
  position: absolute;
  left: clamp(36px, 6vw, 80px);
  right: clamp(36px, 6vw, 80px);
  bottom: clamp(28px, 4vw, 56px);
  display: flex;
  justify-content: space-between;
  font-size: 10px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: #c9a832;
  opacity: 0.85;
}

/* App fades in once boot finishes */
.app--booting { opacity: 0; }
.app--booted { animation: appIn 0.7s ease both; }
@keyframes appIn {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* ============================================================
   Top-right menu (inline dropdown)
   ============================================================ */

.menubar {
  position: fixed;
  top: 22px;
  right: 28px;
  z-index: 95;
  /* /!\ Le menubar enfant .menubar__term a min-width: 280px et reste dans
     le flow (juste opacity: 0 / translateY) quand fermé → la div .menubar
     est étirée à 280px de large et bouffe les clics dans cette zone
     fantôme (notamment les tap zones edge-next à droite sur mobile).
     Solution : pointer-events: none sur le conteneur, on ré-active
     localement sur les enfants vraiment interactifs (toggle, term ouvert,
     lang switch, items du menu). */
  pointer-events: none;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0;
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--yellow);
  /* Slight glow + dark shadow keep the text readable when scrolled
     content (images) ends up behind the bar. */
  text-shadow:
    0 0 1px rgba(0, 0, 0, 0.9),
    0 1px 4px rgba(0, 0, 0, 0.75);
}
.menubar__topline {
  display: flex;
  align-items: center;
  gap: 60px;
}
.menubar__lang {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.12em;
}
.menubar__lang button {
  /* Mustard tres sombre par défaut (ink-dim) ; active/hover passe en
     mustard sombre (ink). Plus de jaune vif. */
  color: var(--ink-dim);
  opacity: 1;
  padding: 4px 2px;
  transition: color 0.18s ease, opacity 0.18s ease;
}
.menubar__lang button.is-active { color: var(--ink); opacity: 1; }
.menubar__lang button:hover { color: var(--ink); opacity: 1; }
.menubar__lang .sep { color: var(--ink-dim); opacity: 1; }

.menubar__mode {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.12em;
}
.menubar__mode button {
  color: var(--ink-dim);
  padding: 4px 2px;
  transition: color 0.18s ease;
}
.menubar__mode button.is-active { color: var(--ink); }
.menubar__mode button:hover { color: var(--ink); }
.menubar__mode .sep { color: var(--ink-dim); opacity: 0.7; }

.menubar__inquiry {
  color: var(--ink);
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.16em;
  padding: 4px 8px;
  border: 1px solid var(--ink-dim);
  transition: background 0.18s ease, color 0.18s ease, border-color 0.18s ease;
}
.menubar__inquiry:hover {
  background: var(--ink);
  color: var(--bg);
  border-color: var(--ink);
}

/* ============================================================
   INDEX view — Chems-style vertical project list
   ============================================================ */

/* ============================================================
   INDEX (I) view — terminal-style inline listing of projects.
   Constrained to viewport — no scroll. Use the size toggle to navigate.
   ============================================================ */
.ahome--index {
  height: 100vh;
  min-height: 100vh;
  max-height: 100vh;
  overflow: hidden;
  padding: 0;
}
.idx-term {
  position: relative;
  z-index: 20;
  height: 100vh;
  max-height: 100vh;
  padding: 92px 40px 80px;
  display: flex;
  /* Centre : la liste tenait en haut avec la moitie basse de l'ecran vide. */
  align-items: center;
  justify-content: center;
  pointer-events: none;
  overflow: hidden;
}
.idx-term > * { pointer-events: auto; }

.idx-term__inner {
  width: 100%;
  max-width: 1200px;
  max-height: 100%;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  font-family: var(--font-mono);
  font-size: 13px;
  line-height: 1.45;
  letter-spacing: 0.04em;
  color: var(--ink);
}

.idx-term__line {
  white-space: pre;
  color: var(--ink-dim);
}
.idx-term__cmd { color: var(--ink); }
.idx-term__cmd .prompt { color: var(--yellow); margin-right: 8px; }
.idx-term__meta { color: var(--ink-dim); }
.idx-term__spacer { height: 0.6em; }

.idx-term__rows { list-style: none; padding: 0; margin: 0; flex: 1 1 auto; min-height: 0; overflow: hidden; }

/* Each row = one terminal-style line. Use display:grid so columns
   stay visually aligned regardless of content length. */
.idx-term__row {
  display: grid;
  /* Piste C : le nom porte la ligne, la nature du travail suit, le client et
     la date se retirent a droite. L'image du projet passe en fond. */
  /* Largeurs FIXES (pas de max-content) : chaque ligne est sa propre grille,
     donc des colonnes dimensionnees par leur contenu ne s'alignent pas d'une
     ligne a l'autre. Un index doit se lire en colonne. */
  grid-template-columns:
    34px                          /* 01 */
    minmax(200px, 1fr)            /* NOM DU PROJET */
    200px                         /* nature */
    270px;                        /* client · date */
  gap: 24px;
  align-items: baseline;
  padding: 6px 0 6px 10px;
  color: var(--ink);
  cursor: pointer;
  border-left: 2px solid transparent;
  transition: color 0.18s ease, border-color 0.18s ease, background 0.18s ease;
}
.idx-term__row .c-num,
.idx-term__row .c-date,
.idx-term__row .c-client,
.idx-term__row .c-svc {
  color: var(--ink-dim);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.idx-term__row .c-name {
  color: var(--ink);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  font-weight: 500;
}

/* When a row is hovered: highlight it, dim the others. */
.idx-term__rows:hover .idx-term__row:not(.is-active),
.idx-term__rows:hover .idx-term__row:not(.is-active) .c-name {
  color: rgba(138, 122, 32, 0.3);
}
.idx-term__rows:hover .idx-term__row:not(.is-active) .c-num,
.idx-term__rows:hover .idx-term__row:not(.is-active) .c-date,
.idx-term__rows:hover .idx-term__row:not(.is-active) .c-client,
.idx-term__rows:hover .idx-term__row:not(.is-active) .c-svc {
  color: rgba(138, 122, 32, 0.2);
}
.idx-term__row.is-active {
  border-left-color: var(--ink);
  background: rgba(138, 122, 32, 0.06);
}
.idx-term__row.is-active,
.idx-term__row.is-active .c-name { color: var(--ink); }
.idx-term__row.is-active .c-num,
.idx-term__row.is-active .c-date,
.idx-term__row.is-active .c-client,
.idx-term__row.is-active .c-svc { color: var(--ink); }

/* Variante INFO — pour l'overlay détails d'un projet (viewer InfoOverlay).
   Bloc centré horizontalement ET verticalement. On override le height: 100vh
   et l'align-items: flex-start de la version Work pour que le bloc se
   compacte autour de son contenu et se laisse centrer par le parent
   .infos__inner (qui a justify-content: center). */
.idx-term--info {
  max-width: 800px;
  width: 100%;
  height: auto;
  max-height: none;
  padding: 0;
  align-items: center;
  text-align: center;
}
.idx-term--info .idx-term__inner {
  align-items: center;
  max-height: none;
  overflow: visible;
}
.idx-term--info .idx-term__cmd,
.idx-term--info .idx-term__rows { text-align: center; }
.idx-term--info .idx-term__rows {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
}
.idx-term__row--info {
  grid-template-columns:
    34px                          /* 01 */
    minmax(180px, max-content)    /* LABEL (CLIENT, YEAR, ...) */
    minmax(220px, 1fr);           /* VALEUR */
  cursor: default;
  /* Justifier le contenu de chaque cellule à gauche pour garder la grille
     lisible, mais le bloc entier reste centré dans la page. */
  text-align: left;
}
.idx-term__row--info .c-label {
  color: var(--ink-dim);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  font-weight: 500;
  white-space: nowrap;
}
.idx-term__row--info .c-value {
  color: var(--ink);
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
/* En mode info, on désactive le highlight au hover (rows pas cliquables). */
.idx-term--info .idx-term__rows:hover .idx-term__row--info,
.idx-term--info .idx-term__rows:hover .idx-term__row--info .c-label,
.idx-term--info .idx-term__rows:hover .idx-term__row--info .c-value,
.idx-term--info .idx-term__rows:hover .idx-term__row--info .c-num {
  color: inherit;
}

/* Blinking caret at the bottom prompt */
.idx-term .blink {
  animation: idx-blink 1s steps(1) infinite;
}
@keyframes idx-blink {
  0%, 49%   { opacity: 1; }
  50%, 100% { opacity: 0; }
}

/* ============================================================
   INQUIRY modal — terminal contact form
   ============================================================ */

.inq {
  position: fixed;
  inset: 0;
  z-index: 220;
  background: rgba(2, 2, 2, 0.78);
  backdrop-filter: blur(6px);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 40px;
  animation: inq-fade 0.25s ease;
}
@keyframes inq-fade { from { opacity: 0; } to { opacity: 1; } }

.inq__panel {
  width: min(760px, 96vw);
  max-height: 92vh;
  overflow-y: auto;
  background: #0a0807;
  border: 1px solid var(--ink-dim);
  padding: 32px 38px 28px;
  font-family: var(--font-mono);
  color: var(--ink);
  position: relative;
  animation: inq-pop 0.32s cubic-bezier(0.2, 0.7, 0.2, 1);
  scrollbar-width: none;
}
.inq__panel::-webkit-scrollbar { display: none; }
@keyframes inq-pop {
  from { transform: scale(0.96) translateY(8px); opacity: 0; }
  to   { transform: none; opacity: 1; }
}

.inq__head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding-bottom: 14px;
  border-bottom: 1px solid rgba(138,122,32,0.22);
  margin-bottom: 18px;
  font-size: 12px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
}
.inq__head .dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  background: var(--ink);
  border-radius: 50%;
  margin-right: 10px;
  vertical-align: 1px;
  animation: blink 1.1s steps(2) infinite;
}
.inq__close { color: var(--ink-dim); font-size: 11px; letter-spacing: 0.18em; }
.inq__close:hover { color: var(--ink); }
.inq__close .key { color: var(--ink); margin-right: 4px; }

.inq__lead {
  font-size: 12px;
  line-height: 1.5;
  color: var(--ink-dim);
  margin-bottom: 22px;
  letter-spacing: 0.04em;
}

.inq__grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px 22px;
  margin-bottom: 22px;
}
.inq__field { display: flex; flex-direction: column; gap: 6px; }
.inq__field--full { grid-column: 1 / -1; }
.inq__field > span {
  font-size: 10px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-dim);
}
.inq__field > span em { color: var(--ink); font-style: normal; }
.inq__field input,
.inq__field textarea {
  background: transparent;
  border: 0;
  border-bottom: 1px solid rgba(138,122,32,0.3);
  padding: 8px 0;
  color: var(--ink);
  font-family: var(--font-mono);
  font-size: 13px;
  letter-spacing: 0.04em;
  outline: none;
  resize: vertical;
  transition: border-color 0.18s ease;
}
.inq__field input:focus,
.inq__field textarea:focus { border-color: var(--ink); }

.inq__group {
  border: 0;
  padding: 0;
  margin: 0 0 22px;
}
.inq__group legend {
  font-size: 10px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-dim);
  margin-bottom: 10px;
}
.inq__group legend em { color: var(--ink); font-style: normal; }

.inq__chips {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 14px;
}
.inq__chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 10px;
  color: var(--ink-dim);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  border: 1px solid transparent;
  transition: color 0.18s ease, border-color 0.18s ease;
}
.inq__chip .box { color: var(--ink); min-width: 1.4em; }
.inq__chip:hover { color: var(--ink); }
.inq__chip.is-on { color: var(--ink); border-color: var(--ink-dim); }

.inq__actions {
  display: flex;
  justify-content: space-between;
  gap: 16px;
  padding-top: 18px;
  border-top: 1px solid rgba(138,122,32,0.18);
  margin-top: 10px;
}
.inq__cancel,
.inq__send {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  padding: 10px 18px;
  transition: background 0.18s ease, color 0.18s ease;
}
.inq__cancel { color: var(--ink-dim); }
.inq__cancel:hover { color: var(--ink); }
.inq__send {
  color: var(--ink);
  border: 1px solid var(--ink);
}
.inq__send:hover { background: var(--ink); color: var(--bg); }

.inq__thanks {
  padding: 40px 0 20px;
  text-align: center;
}
.inq__thanks .inq__ascii {
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--ink);
  white-space: pre;
  margin-bottom: 22px;
}
.inq__thanks p {
  color: var(--ink-dim);
  font-size: 12px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  margin-bottom: 26px;
}
.inq__thanks .inq__send {
  border: 1px solid var(--ink);
}

@media (max-width: 700px) {
  .inq__panel { padding: 20px 22px; }
  .inq__grid { grid-template-columns: 1fr; }
  .idx__head, .idx__row { grid-template-columns: 1.5fr 1fr 0.8fr; gap: 12px; }
  .idx__col--tc, .idx__col--svc, .idx__col--year { display: none; }
}

.menubar__toggle {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 4px 0;
  color: var(--ink);
  /* Ré-activation des clics sur le toggle (le parent .menubar a
     pointer-events: none pour ne pas bouffer les clics dans sa zone
     fantôme à droite). */
  pointer-events: auto;
}
/* Ré-activation des clics sur le switch de langue + le terminal du menu
   (quand ouvert). Le terminal fermé reste pointer-events: none via la
   règle plus bas, donc pas de double-capture en zone fantôme. */
.menubar__lang,
.menubar.open .menubar__term {
  pointer-events: auto;
}
.menubar__icon {
  display: inline-flex;
  flex-direction: column;
  gap: 3px;
}
.menubar__icon span {
  display: block;
  width: 14px;
  height: 1.4px;
  background: var(--ink);
  transition: transform 0.2s ease;
}
.menubar.open .menubar__icon span:nth-child(1) { transform: translate(0, 4.4px) rotate(45deg); }
.menubar.open .menubar__icon span:nth-child(2) { opacity: 0; }
.menubar.open .menubar__icon span:nth-child(3) { transform: translate(0, -4.4px) rotate(-45deg); }

/* SIDEBAR latérale du menu (desktop). Position fixed à droite, full
   height, slide-in depuis la droite. Garde l'esthétique terminal
   (dots + title bar) en haut.
   /!\ Sur mobile (≤1200px), un @media plus bas override complètement
   ces règles pour passer en fullscreen split avec backdrop-blur. */
.menubar__term {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  width: 340px;
  background: var(--bg);
  /* Plus de border-left jaune ni d'inset jaune — sidebar clean,
     délimitée juste par le bg solide vs le reste de la page. */
  border-left: 0;
  box-shadow: -16px 0 40px rgba(0, 0, 0, 0.55);
  opacity: 1;
  transform: translateX(100%);
  pointer-events: none;
  /* visibility: hidden en état fermé (appliquée APRÈS la transition de
     sortie) = garantie que le panneau ne peut jamais "dépasser" sur le
     bord droit, même si le navigateur rate le recalcul du translateX
     pendant un resize (bug constaté le 14/07/2026 : bordure + items
     visibles sur le côté après fermeture + réduction de fenêtre). */
  visibility: hidden;
  transition: transform 0.35s cubic-bezier(0.2, 0.7, 0.2, 1),
              visibility 0s 0.35s;
  display: flex;
  flex-direction: column;
  z-index: 95;
}
.menubar.open .menubar__term {
  transform: translateX(0);
  pointer-events: auto;
  visibility: visible;
  transition: transform 0.35s cubic-bezier(0.2, 0.7, 0.2, 1);
}
/* Pas de blur du contenu derrière sur desktop — la page reste nette,
   seule la sidebar slide par-dessus. Le blur reste en mobile (override
   plus bas dans @media (max-width: 1200px)). */
.menubar__term__bar {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 7px 10px;
  background: var(--bg);
  border-bottom: 1px solid var(--rule);
  color: var(--ink-dim);
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: lowercase;
}
.menubar__term__dots {
  display: inline-flex;
  align-items: center;
  gap: 5px;
}
.menubar__term__dots .d {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  display: inline-block;
}
.menubar__term__dots .d--r { background: #ff5f56; }
.menubar__term__dots .d--y { background: #ffbd2e; }
.menubar__term__dots .d--g { background: #27c93f; }
.menubar__term__title {
  flex: 1;
  text-align: center;
  font-family: var(--font-mono);
}
.menubar__term__x {
  color: var(--ink-dim);
  font-size: 14px;
  line-height: 1;
  padding: 0 4px;
  transition: color 0.15s ease;
}
.menubar__term__x:hover { color: var(--yellow); }

.menubar__items {
  display: flex;
  flex-direction: column;
  gap: 0;
  padding: 18px 28px 28px;
  flex: 1; /* prend toute la hauteur dispo sous la term bar */
}
.menubar__items button,
.menubar__items a {
  display: flex;
  text-decoration: none;
  justify-content: flex-start;
  align-items: baseline;
  gap: 10px;
  /* Padding compact pour resserer les items. Padding-left 14px réserve
     la place pour le border-left de 2px qui apparaît au hover. */
  padding: 6px 12px 6px 14px;
  /* Même couleur et même taille que le bouton MENU dans le topline
     (mustard var(--ink), 12px mono, letter-spacing 0.12em). */
  color: var(--ink);
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  border-left: 2px solid transparent;
  background: transparent;
  transition: color 0.18s ease,
              border-color 0.18s ease,
              background 0.18s ease,
              opacity 0.18s ease;
  text-align: left;
  opacity: 0.85;
}
/* Hover UNIQUEMENT : effet identique à .idx-term__row.is-active de
   la vue INDEX — mustard sombre partout (plus de jaune vif). */
.menubar__items button:hover,
.menubar__items a:hover {
  opacity: 1;
  color: var(--ink);
  border-left-color: var(--ink);
  background: rgba(138, 122, 32, 0.06);
}
.menubar__items button:hover .prompt,
.menubar__items a:hover .prompt {
  color: var(--ink);
  opacity: 1;
}
/* État ACTIVE (page courante) : juste mustard à pleine opacité, pas
   de jaune, pas de border, pas de background. C'est subtil mais
   suffisant pour différencier la page courante des autres. */
.menubar__items button.active {
  opacity: 1;
  color: var(--ink);
}
.menubar__items button.active .prompt {
  color: var(--ink);
  opacity: 1;
}
/* Effet INDEX view : quand on hover la liste, les items non-hover
   se dim — SAUF l'active qui reste en mustard pleine opacité. */
.menubar__items:hover button:not(:hover):not(.active) {
  opacity: 0.3;
}
.menubar__items:hover button:not(:hover):not(.active) .prompt {
  opacity: 0.2;
}
.menubar__items button .prompt {
  /* Prompt > en ink aussi (plus en yellow) pour rester cohérent avec
     la couleur du bouton MENU. */
  color: var(--ink);
  font-weight: 600;
  opacity: 0.6;
}
.menubar__items .voir {
  color: var(--ink);
  opacity: 0.45;
  margin-left: auto;
  font-size: 10px;
  letter-spacing: 0.18em;
}

/* Switch de langue dupliqué pour le menu mobile : masqué par défaut
   (desktop). Le @media plus bas l'affiche en mobile et masque celui
   du topline. */
.menubar__lang--in-term { display: none; }

/* ============================================================
   Menu mobile — fullscreen overlay style ACRE Creative
   Nav items alignés à gauche en colonne, [X] close en haut à droite,
   on garde tout le délire terminal (mono, brackets, prompt >, cascade
   typewriter) mais sans la term bar dots + ~ menu.sh qui ne servent
   que sur le panel desktop compact.
   ============================================================ */
@media (max-width: 1200px) {
  /* Menubar passe en fullscreen au lieu d'être un panel compact en
     haut à droite. Le topline (lang switch + bouton MENU) reste en
     haut à droite quand le menu est fermé. */
  .menubar {
    top: 14px;
    right: 16px;
  }
  /* Quand le menu est ouvert, le term devient un overlay plein viewport
     MAIS visuellement scindé en deux : moitié haute = bg solide
     (les items + le close [X]), moitié basse = transparente avec
     backdrop-filter blur → la page derrière (work grid, viewer, etc.)
     transparaît floutée façon glassmorphism. */
  .menubar.open .menubar__term {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    width: 100vw;
    height: 100vh;
    height: 100dvh;
    margin: 0;
    min-width: 0;
    border: none;
    box-shadow: none;
    /* /!\ transform: none !important — même un transform identité
       (translateY(0) scale(1)) crée un stacking context qui peut
       casser backdrop-filter sur certaines pages (viewer, eco avec
       leurs propres transforms). On annule pour que le blur capte
       toujours le contenu derrière. */
    transform: none !important;
    /* will-change pour pousser Safari iOS à composer le backdrop-filter
       sur le GPU et le rendre correctement sur toutes les pages. */
    will-change: backdrop-filter;
    isolation: auto;
    /* Gradient noir solide sur la moitié haute → transparent sur la
       moitié basse. La moitié basse laisse voir le contenu de la page
       derrière (qui est lui-même blurré via filter:blur sur
       body.menu-open). On utilise #000 et non var(--bg) (mustard
       sombre) pour avoir un fond vraiment NOIR, pas grisé. */
    background: linear-gradient(
      to bottom,
      #000 0%,
      #000 50%,
      transparent 50%,
      transparent 100%
    );
    /* Padding global supprimé : la term bar et la liste d'items ont
       maintenant leur propre padding pour que la term bar (header)
       colle bien au bord haut comme sur une vraie fenêtre de terminal. */
    padding: 0;
    box-sizing: border-box;
    display: flex;
    flex-direction: column;
  }
  /* Term bar full-width en haut : on garde EXACTEMENT le chrome
     desktop (3 dots de couleur trafficlight + "~ menu.sh" centré +
     close × à droite) pour que ça ressemble pour de vrai à une
     fenêtre de terminal. */
  .menubar.open .menubar__term__bar {
    width: 100%;
    padding: 9px 14px;
    background: var(--bg);
    border-bottom: 1px solid var(--rule);
  }
  /* Dots traffic-light et title visibles (comme desktop) */
  .menubar.open .menubar__term__dots,
  .menubar.open .menubar__term__title {
    display: inline-flex;
  }
  /* Close × : style desktop, taille un peu plus présente pour le tap */
  .menubar.open .menubar__term__x {
    font-family: var(--font-mono);
    font-size: 18px;
    line-height: 1;
    padding: 0 8px;
    color: var(--ink-dim);
  }
  /* Items sous la term bar — colonne avec le prompt > jaune et le
     "VOIR" à droite. Exactement le même listing que desktop, juste
     en pleine largeur. */
  .menubar.open .menubar__items {
    display: flex;
    flex-direction: column;
    gap: 0;
    padding: 14px 18px;
    margin-top: 8px;
    align-items: flex-start;
  }
  .menubar.open .menubar__items button {
    width: 100%;
    justify-content: flex-start;
    padding: 6px 0;
    font-size: 11px;
    letter-spacing: 0.14em;
    gap: 10px;
  }
  /* "VOIR" masqué en mobile : moins chargé visuellement, le prompt >
     et le label de l'item suffisent pour le délire terminal. */
  .menubar.open .menubar__items .voir {
    display: none;
  }

  /* Blur direct du contenu de la page derrière le menu (méthode robuste
     cross-browser, indépendante des stacking contexts qui peuvent casser
     backdrop-filter sur iOS Safari / certaines pages comme le viewer
     qui a des transforms). On cible les VRAIS conteneurs racine de
     chaque page (ahome, idx-term, about-min, viewer, eco-solar, hm)
     pour que le blur soit effectif depuis n'importe quelle page. Le
     menubar et l'idcard restent nets car ils sont au niveau body, hors
     de ces conteneurs. */
  body.menu-open .ahome,
  body.menu-open .hm,
  body.menu-open .idx-term,
  body.menu-open .viewer,
  body.menu-open .about-min,
  body.menu-open .eco-solar {
    filter: blur(10px);
    transition: filter 0.3s ease;
  }
  /* .infos est un enfant de .viewer (InfoOverlay), donc déjà couvert
     par le filter sur .viewer. Pas besoin de cibler séparément. */

  /* Griot menu mobile retiré : le menu reste épuré, juste term bar + items. */

  /* Switch de langue : par défaut le topline EN/FR + MENU s'affiche en
     haut-droit même sur mobile. On masque celui du topline, on affiche
     celui qui est dans le term (au-dessus des items, ou en bas si besoin). */
  .menubar > .menubar__topline > .menubar__lang {
    display: none;
  }
  /* Switch lang dans le term : visible uniquement en mobile, posé en
     haut de la liste d'items (juste sous la term bar). */
  .menubar__lang--in-term {
    display: inline-flex !important;
    padding: 12px 18px 6px;
    align-items: center;
    gap: 6px;
    font-family: var(--font-mono);
    font-size: 11px;
    letter-spacing: 0.14em;
    color: var(--yellow);
  }
  .menubar__lang--in-term button {
    background: transparent;
    border: 0;
    color: var(--yellow);
    opacity: 0.6;
    padding: 2px 4px;
    font-family: inherit;
    font-size: inherit;
    letter-spacing: inherit;
    cursor: pointer;
    transition: opacity 0.18s ease;
  }
  .menubar__lang--in-term button.is-active {
    opacity: 1;
  }
  .menubar__lang--in-term .sep {
    opacity: 0.55;
  }
}

/* ============================================================
   HOME — bottom strip of projects
   ============================================================ */

.home {
  position: relative;
  min-height: 100vh;
  padding-bottom: 0;
}

/* Griot pixel-art figure — smaller, centered, composes/decomposes
   from scattered pixels. Visible only when no project is hovered. */
.home::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background-image: url("img/griot-bg.png");
  background-repeat: no-repeat;
  background-position: center 52%;
  background-size: auto 38vh;
  image-rendering: pixelated;
  filter: invert(1) sepia(0.5) hue-rotate(15deg) saturate(0.8) brightness(0.85);
  animation: griot-pixelate 7s ease-in-out infinite;
  will-change: filter, opacity, transform;
}
.home:has(.home__bg.is-active)::after { opacity: 0.05; animation-play-state: paused; }

@keyframes griot-pixelate {
  /* 0% — fully scattered/decomposed (large pixels, very faded) */
  0% {
    opacity: 0;
    filter: invert(1) sepia(0.5) hue-rotate(15deg) saturate(0.8) brightness(0.85) blur(14px) contrast(8);
    transform: scale(1.06);
  }
  /* 20% — pixels start landing */
  20% {
    opacity: 0.32;
    filter: invert(1) sepia(0.5) hue-rotate(15deg) saturate(0.8) brightness(0.85) blur(4px) contrast(5);
    transform: scale(1.02);
  }
  /* 40–60% — fully composed */
  40%, 60% {
    opacity: 0.28;
    filter: invert(1) sepia(0.5) hue-rotate(15deg) saturate(0.8) brightness(0.85) blur(0) contrast(1.05);
    transform: scale(1);
  }
  /* 80% — pixels start dispersing */
  80% {
    opacity: 0.32;
    filter: invert(1) sepia(0.5) hue-rotate(15deg) saturate(0.8) brightness(0.85) blur(4px) contrast(5);
    transform: scale(1.02);
  }
  /* 100% — fully scattered again */
  100% {
    opacity: 0;
    filter: invert(1) sepia(0.5) hue-rotate(15deg) saturate(0.8) brightness(0.85) blur(14px) contrast(8);
    transform: scale(1.06);
  }
}

/* 35mm-style film grain on the work page — fine, organic, animated.
   Uses screen blend so the grain is visible on the bare black background
   AND on the project covers when hovered. */
.home::before {
  content: "";
  position: fixed;
  inset: -40%;
  z-index: 1;
  pointer-events: none;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='420' height='420'><filter id='g'><feTurbulence type='fractalNoise' baseFrequency='1.1' numOctaves='2' stitchTiles='stitch' seed='7'/><feColorMatrix values='0 0 0 0 1  0 0 0 0 1  0 0 0 0 1  0 0 0 0.95 0'/><feComponentTransfer><feFuncA type='linear' slope='1.5' intercept='-0.12'/></feComponentTransfer></filter><rect width='100%25' height='100%25' filter='url(%23g)'/></svg>");
  background-size: 520px 520px;
  mix-blend-mode: screen;
  opacity: 0.22;
  animation: film-grain 0.55s steps(1) infinite;
  will-change: transform;
}
.home__bg.is-active ~ * .home::before,
.home:has(.home__bg.is-active)::before {
  /* When a project is being hovered, soften the grain so the cover reads */
  mix-blend-mode: soft-light;
  opacity: 0.55;
}
@keyframes film-grain {
  0%   { transform: translate(0, 0); }
  10%  { transform: translate(-3%, 2%); }
  20%  { transform: translate(2%, -3%); }
  30%  { transform: translate(-1%, 4%); }
  40%  { transform: translate(3%, 1%); }
  50%  { transform: translate(-4%, -2%); }
  60%  { transform: translate(2%, 3%); }
  70%  { transform: translate(-2%, -3%); }
  80%  { transform: translate(4%, 2%); }
  90%  { transform: translate(-3%, -1%); }
  100% { transform: translate(0, 0); }
}

/* Full-bleed hover background — shows the active project's cover */
.home__bg {
  position: fixed;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  overflow: hidden;
}
.home__bg__layer {
  position: absolute;
  inset: 0;
  background-size: cover;
  background-position: center;
  opacity: 0;
  transition: opacity 0.55s cubic-bezier(0.2, 0.7, 0.2, 1);
  filter:
    sepia(var(--bg-sepia, 0.55))
    saturate(var(--bg-saturate, 0.85))
    contrast(var(--bg-contrast, 1.08))
    brightness(var(--bg-brightness, 0.92))
    var(--bg-distort-url, url(#crt-distort));
  transform: scale(1.04);
}
.home__bg__layer.is-active { opacity: 1; }

/* Noise overlay on hover background — replaces the scanlines */
.home__bg__scanlines {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='220' height='220'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.95' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 1  0 0 0 0 1  0 0 0 0 1  0 0 0 0.85 0'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>");
  background-size: 220px 220px;
  mix-blend-mode: overlay;
  opacity: 0;
  transition: opacity 0.4s ease;
  z-index: 3;
}
.home__bg.is-active .home__bg__scanlines {
  opacity: var(--scan-opacity, 0.55);
}

/* Faint horizontal sweep line, slowly travelling — CRT refresh */
.home__bg__sweep {
  position: absolute;
  left: 0;
  right: 0;
  height: 220px;
  background: linear-gradient(180deg, transparent, rgba(255, 230, 100, 0.05) 50%, transparent);
  pointer-events: none;
  z-index: 4;
  opacity: 0;
  transition: opacity 0.4s ease;
  animation: crt-sweep 7s linear infinite;
}
.home__bg.is-active .home__bg__sweep { opacity: 1; }
@keyframes crt-sweep {
  from { top: -240px; }
  to { top: 100vh; }
}

.home__bg__vignette {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(ellipse at center, transparent 0%, rgba(0,0,0,0.35) 55%, rgba(0,0,0,0.92) 100%),
    linear-gradient(180deg, rgba(5,5,5,0.45) 0%, transparent 25%, transparent 55%, rgba(5,5,5,0.9) 100%);
  opacity: 0;
  transition: opacity 0.55s ease;
  pointer-events: none;
  z-index: 2;
  /* Curvature corners — soften the rectangle into a tube */
  box-shadow:
    inset 0 0 80px rgba(0,0,0,0.65),
    inset 0 0 220px rgba(0,0,0,0.5);
}
.home__bg.is-active .home__bg__vignette {
  opacity: var(--vignette-opacity, 1);
}

.home__strip-wrap {
  position: fixed;
  left: 0;
  right: 0;
  bottom: -180px; /* peek effect — extends past viewport */
  height: 540px;
  z-index: 10;
  transition: filter 0.3s ease;
}
.home__strip-wrap.is-peeking::before {
  /* extra darken under the strip for legibility */
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 360px;
  background: linear-gradient(180deg, transparent 0%, rgba(5,5,5,0.6) 60%, rgba(5,5,5,0.9) 100%);
  pointer-events: none;
  z-index: -1;
}

.home__strip {
  display: flex;
  align-items: flex-start;
  height: 100%;
  padding-left: 14px;
  padding-right: 14px;
  gap: 26px;
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: none;
  scroll-behavior: smooth;
}
.home__strip::-webkit-scrollbar { display: none; }

.home__group {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  position: relative;
  padding-top: 24px;
}
.home__group__label {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink);
  margin-bottom: 12px;
  min-height: 1.5em;
  transition: color 0.25s ease, opacity 0.25s ease;
}
.home__group__row {
  display: flex;
  gap: 12px;
}
.home__shot {
  position: relative;
  flex: 0 0 auto;
  overflow: hidden;
  cursor: pointer;
  background: #111;
  transition: filter 0.3s ease, transform 0.4s cubic-bezier(0.2, 0.7, 0.2, 1);
}
.home__shot img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: contrast(1.04);
  transition: transform 0.6s ease;
}

/* Dim others when one project is hovered — same sepia treatment as the bg */
.home__strip-wrap.is-peeking .home__group:not(.is-active) .home__shot img {
  filter: sepia(0.7) saturate(0.85) contrast(1.05) brightness(0.45);
  transition: filter 0.4s ease;
}
.home__strip-wrap.is-peeking .home__group:not(.is-active) .home__shot {
  position: relative;
}
.home__strip-wrap.is-peeking .home__group:not(.is-active) .home__shot::after {
  /* matching scanlines on dimmed thumbs */
  content: "";
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(
    0deg,
    rgba(0,0,0,0) 0px,
    rgba(0,0,0,0) 2px,
    rgba(0,0,0,0.28) 3px,
    rgba(0,0,0,0.28) 4px
  );
  mix-blend-mode: multiply;
  pointer-events: none;
}
.home__strip-wrap.is-peeking .home__group:not(.is-active) .home__group__label {
  opacity: 0.25;
}
.home__strip-wrap.is-peeking .home__group.is-active .home__shot img {
  filter: contrast(1.06) brightness(1.05);
}

.home__group.is-active .home__shot {
  transform: translateY(-6px);
}
.home__group.is-active .home__group__label {
  color: var(--ink);
}

/* The peek overlay: VOIR / name / role / client / date appears above hovered project */
.home__peek {
  position: absolute;
  left: 0;
  right: 0;
  top: -450px;
  display: flex;
  flex-direction: column;
  gap: 50px;
  padding: 0 4px;
  font-family: var(--font-mono);
  font-size: 13px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink);
  pointer-events: none;
}
.home__peek > * { min-height: 1.5em; }
.home__peek .voir {
  color: var(--ink);
  pointer-events: auto;
  cursor: pointer;
  font-size: 14px;
}
.home__peek .role,
.home__peek .name,
.home__peek .date {
  color: var(--ink);
}

/* Hint strip at top of home for status */
.home__status {
  position: fixed;
  top: 24px;
  left: 320px;
  right: 280px;
  display: flex;
  justify-content: center;
  gap: 32px;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-dim);
  z-index: 80;
  pointer-events: none;
}
.home__status .dot {
  display: inline-block;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--yellow);
  margin-right: 8px;
  vertical-align: middle;
  animation: blink 1.5s ease-in-out infinite;
}
@keyframes blink { 0%, 100% { opacity: 1; } 50% { opacity: 0.3; } }

/* Slug shown in status bar on hover */
.home__status__slug {
  color: var(--ink);
  text-transform: none;
  letter-spacing: 0.06em;
  min-width: 200px;
  text-align: right;
}

/* Filter chips above the strip — ASCII checkbox style */
.home__filters {
  position: fixed;
  top: 56px;
  left: 320px;
  right: 280px;
  z-index: 80;
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: 22px;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-dim);
  pointer-events: none;
}
.home__filter {
  pointer-events: auto;
  color: var(--ink-dim);
  transition: color 0.18s ease;
  padding: 4px 0;
}
.home__filter .box {
  display: inline-block;
  min-width: 1.4em;
  color: var(--ink);
}
.home__filter:hover { color: var(--ink); }
.home__filter.is-active { color: var(--ink); }
.home__filter.is-active .box { color: var(--ink); }

/* Timecode + category badges on each project shot */
.home__shot__tc,
.home__shot__cat {
  position: absolute;
  z-index: 3;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink);
  padding: 4px 6px;
  background: rgba(5, 5, 5, 0.65);
  backdrop-filter: blur(4px);
  pointer-events: none;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.7);
  font-variant-numeric: tabular-nums;
  opacity: 0.85;
  transition: opacity 0.2s ease;
}
.home__shot__tc  { bottom: 8px; right: 8px; }
.home__shot__cat { top: 8px; left: 8px; }
.home__strip-wrap.is-peeking .home__group:not(.is-active) .home__shot__tc,
.home__strip-wrap.is-peeking .home__group:not(.is-active) .home__shot__cat {
  opacity: 0.35;
}
.home__group.is-active .home__shot__tc,
.home__group.is-active .home__shot__cat { opacity: 1; }


/* ============================================================
   Sticker easter egg — click-to-slap
   ============================================================ */

.sticker-hotspot {
  position: fixed;
  right: 18px;
  bottom: 18px;
  width: 28px;
  height: 28px;
  z-index: 130;
  background: transparent;
  border: 0;
  padding: 0;
  cursor: pointer;
  opacity: 0.35;
  transition: opacity 0.2s ease, transform 0.2s ease;
}
.sticker-hotspot:hover { opacity: 1; transform: scale(1.18) rotate(-6deg); }
.sticker-hotspot img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  pointer-events: none;
  filter: drop-shadow(1px 2px 0 rgba(0,0,0,0.6));
}

.sticker-clear {
  position: fixed;
  right: 56px;
  bottom: 22px;
  z-index: 130;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.18em;
  color: var(--ink-dim);
  background: transparent;
  border: 0;
  padding: 4px 6px;
  cursor: pointer;
  transition: color 0.18s ease;
}
.sticker-clear:hover { color: var(--ink); }

.sticker-layer {
  position: fixed;
  inset: 0;
  z-index: 125;
  pointer-events: none;
}

.placed-sticker {
  position: absolute;
  transform-origin: 50% 50%;
  pointer-events: auto;
  filter: drop-shadow(3px 5px 0 rgba(0, 0, 0, 0.55));
  animation: sticker-slap 0.4s cubic-bezier(0.2, 1.4, 0.4, 1) both;
}
.placed-sticker img {
  display: block;
  width: 100%;
  height: auto;
  user-select: none;
  -webkit-user-drag: none;
}
.placed-sticker__close {
  position: absolute;
  top: 4px;
  right: 4px;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.04em;
  color: #0d0c0a;
  background: transparent;
  border: 0;
  padding: 2px 4px;
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.2s ease;
}
.placed-sticker:hover .placed-sticker__close { opacity: 1; }

@keyframes sticker-slap {
  0%   { opacity: 0; transform: translate(-50%, -50%) rotate(var(--rot, 0deg)) scale(2.2); }
  60%  { opacity: 1; transform: translate(-50%, -50%) rotate(var(--rot, 0deg)) scale(0.94); }
  100% { opacity: 1; }
}

.ahome {
  position: relative;
  min-height: 100vh;
  padding: 80px 56px 90px;
  background: #050505;
  color: #eee;
}

.ahome__top {
  position: fixed;
  top: 22px;
  left: 28px;
  right: 28px;
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  z-index: 60;
  pointer-events: none;
  font-family: var(--font-mono);
}
.ahome__top > * { pointer-events: auto; }
.ahome__brand {
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: 28px;
  letter-spacing: -0.02em;
  color: #fff;
  line-height: 1;
}
.ahome__brand sup {
  font-size: 13px;
  font-weight: 400;
  vertical-align: top;
  margin-left: 1px;
  opacity: 0.85;
}
.ahome__view {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: #fff;
  opacity: 0.9;
  padding: 4px 0;
}
.ahome__view:hover { opacity: 1; text-decoration: underline; text-underline-offset: 4px; }

/* Scattered grid — 8 cols × 4 rows with random sizing and translation */
.ahome__grid {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  grid-auto-rows: 1fr;
  gap: 90px 70px;
  align-items: center;
  justify-items: center;
  /* Un peu plus bas : le bloc etait legerement trop haut, et la barre
     SMALL / LARGE / INDEX en bas d'ecran tire l'oeil vers le bas. On
     redistribue la respiration, sans changer la hauteur totale. */
  padding: 132px 0 68px;
  max-width: 1700px;
  margin: 0 auto;
  position: relative;
  z-index: 5;
  min-height: calc(100vh - 180px);
}
/* Grille fluide : les cells ont des largeurs fixes (130-190px) + gaps
   généreux → en dessous de ~1280px, 5 colonnes débordent du viewport
   (cellules coupées au bord droit). On réduit le nombre de colonnes
   progressivement — le placement des cells est en auto-flow, donc la
   grille se réorganise toute seule. */
@media (max-width: 1300px) {
  .ahome { padding-left: 36px; padding-right: 36px; }
  .ahome__grid { grid-template-columns: repeat(4, 1fr); gap: 70px 50px; }
}
@media (max-width: 1020px) {
  .ahome__grid { grid-template-columns: repeat(3, 1fr); gap: 60px 40px; }
}

.ahome__cell {
  position: relative;
  background: #0a0a0a;
  border: 0;
  padding: 0;
  /* /!\ pas de cursor: pointer ici — la cell est un <button> donc le
     curseur custom mustard (.button matché par la règle globale en
     haut du fichier) s'applique. cursor: pointer aurait une spécificité
     supérieure et écraserait le SVG inline custom. */
  /* overflow: visible so the hover label (positioned above the cell)
     can render outside the cell's bounds */
  overflow: visible;
  transition: transform 0.4s cubic-bezier(0.2,0.7,0.2,1), opacity 0.3s ease, filter 0.3s ease;
  /* Animation d'apparition staggered. On anime UNIQUEMENT opacity et
     filter (pas transform) → ça ne rentre pas en conflit avec le
     transform inline qui porte le jitter de scatter. Le delay est posé
     en var --enter-delay depuis home.jsx (35ms × index de la cell). */
  animation: ahome-cell-in 600ms cubic-bezier(0.2, 0.7, 0.2, 1) both;
  animation-delay: var(--enter-delay, 0ms);
}
@keyframes ahome-cell-in {
  from {
    opacity: 0;
    filter: blur(6px);
  }
  to {
    opacity: 1;
    filter: blur(0);
  }
}
.ahome__cell img,
.ahome__cell video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: grayscale(1) contrast(1.05) brightness(0.95);
  transition: filter 0.3s ease, transform 0.5s ease;
}
.ahome__cell:hover img,
.ahome__cell:hover video {
  filter: grayscale(0.4) contrast(1.1);
  transform: scale(1.04);
}

/* Spotlight — when a cell is hovered (via JS state, more reliable than
   :hover with overlapping layers), the other cells fade out so the
   fullscreen preview backdrop can shine through. */
.ahome__grid.has-hover .ahome__cell:not(.is-hovered) {
  opacity: 0 !important;
  pointer-events: none;
  transition: opacity 0.25s ease;
}
.ahome__grid.has-hover .ahome__cell.is-hovered {
  opacity: 1 !important;
  z-index: 6;
}

/* Size buckets — narrow range for an evenly-paced grid */
.ahome__cell--sm { width: 130px; }
.ahome__cell--md { width: 170px; }
.ahome__cell--lg { width: 190px; }

/* Cells portrait : INVERSION width/height (portrait_height = landscape_width
   pour même sizeClass) — équilibrage visuel.
   !important pour battre tout override de spécificité. */
.ahome__cell--portrait.ahome__cell--sm { width: auto !important; height: 130px !important; }
.ahome__cell--portrait.ahome__cell--md { width: auto !important; height: 170px !important; }
.ahome__cell--portrait.ahome__cell--lg { width: auto !important; height: 190px !important; }

/* Large mode — bigger grid, fewer columns, fewer items */
.ahome--large .ahome__grid {
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: minmax(320px, auto);
  gap: 120px 80px;
  max-width: 1400px;
}
.ahome--large .ahome__cell--sm { width: 220px; }
.ahome--large .ahome__cell--md { width: 320px; }
.ahome--large .ahome__cell--lg { width: 420px; }
/* Inversion idem en mode LARGE */
.ahome--large .ahome__cell--portrait.ahome__cell--sm { width: auto !important; height: 220px !important; }
.ahome--large .ahome__cell--portrait.ahome__cell--md { width: auto !important; height: 320px !important; }
.ahome--large .ahome__cell--portrait.ahome__cell--lg { width: auto !important; height: 420px !important; }

/* THUMB / FRAME / DATA toggle, bottom-left */
.ahome__size {
  position: fixed;
  left: 28px;
  bottom: 22px;
  z-index: 60;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.2em;
  color: var(--ink);
  pointer-events: auto;
}
.ahome__size button {
  color: var(--ink);
  opacity: 0.45;
  padding: 4px 4px;
  background: transparent;
  border: 0;
  cursor: pointer;
  transition: color 0.18s ease, opacity 0.18s ease;
}
.ahome__size button:hover { color: var(--ink); opacity: 1; }
.ahome__size button.is-active {
  /* Vue active = mustard sombre + underline (plus de jaune vif). */
  color: var(--ink);
  opacity: 1;
  text-decoration: underline;
  text-underline-offset: 4px;
}
.ahome__size .sep { color: var(--ink); opacity: 0.4; }

/* Aspect ratios */
.ahome__cell--portrait  { aspect-ratio: 3 / 4.2; }
.ahome__cell--landscape { aspect-ratio: 4 / 3; }

/* Camera icon overlay on some cells */
.ahome__cam {
  position: absolute;
  top: 6px;
  right: 6px;
  width: 20px;
  height: 20px;
  display: grid;
  place-items: center;
  color: #fff;
  pointer-events: none;
  opacity: 0.9;
  text-shadow: 0 1px 2px rgba(0,0,0,0.6);
}

/* Fullscreen video-preview backdrop */
.ahome__bg {
  position: fixed;
  inset: 0;
  z-index: 0;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  opacity: 0;
  filter: grayscale(1) contrast(1.1) brightness(0.6);
  transform: scale(1.05);
  transition: opacity 0.45s ease, transform 1.2s ease;
  pointer-events: none;
}
.ahome__bg.is-active { opacity: 1; transform: scale(1); }
.ahome__bg__video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: 0;
}
.ahome__bg__grain {
  position: absolute;
  inset: -10%;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='420' height='420'><filter id='g'><feTurbulence type='fractalNoise' baseFrequency='1.4' numOctaves='2' stitchTiles='stitch' seed='9'/><feColorMatrix values='0 0 0 0 1  0 0 0 0 1  0 0 0 0 1  0 0 0 1.1 0'/><feComponentTransfer><feFuncA type='linear' slope='1.6' intercept='-0.15'/></feComponentTransfer></filter><rect width='100%25' height='100%25' filter='url(%23g)'/></svg>");
  background-size: 520px 520px;
  mix-blend-mode: screen;
  opacity: 0.45;
  animation: film-grain 0.55s steps(1) infinite;
}
.ahome__bg__veil {
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse at center, rgba(0,0,0,0.35) 0%, rgba(0,0,0,0.7) 100%);
}

/* ASCII griot fade — bottom-right corner on S/L modes */
.ahome__griot {
  position: fixed;
  right: 24px;
  bottom: 70px;
  z-index: 1;
  pointer-events: none;
  opacity: 0.55;
}
.ahome__griot .matrix-griot {
  /* Override the default .matrix-griot fixed-centered positioning so it
     stays inside its bottom-right wrapper. */
  position: static;
  top: auto;
  left: auto;
  transform: none;
  font-size: 5px;
  line-height: 1;
  color: var(--ink);
  white-space: pre;
  text-shadow: 0 0 6px rgba(138, 122, 32, 0.2);
}
@media (max-width: 760px) {
  .ahome__griot { right: 12px; bottom: 60px; }
  .ahome__griot .matrix-griot { font-size: 3px; }
}

/* Mini terminal label that floats just above each cell when hovered */
.ahome__cell { position: relative; }
/* Plain-text label below the cell on hover — aligned on the left edge,
   types out like the About page. */
.ahome__cell__label {
  position: absolute;
  left: 0;
  top: calc(100% + 10px);
  z-index: 10;
  pointer-events: none;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink);
  white-space: nowrap;
  text-align: left;
  line-height: 1.45;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
}
.ahome__cell__label__client {
  display: block;
  font-size: 10px;
  opacity: 0.65;
}
.ahome__cell__label__role {
  display: block;
  font-size: 10px;
  opacity: 0.45;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

/* Bottom-left archive lockup */
.ahome__archive {
  display: none;
}

/* Grain is only shown on the hover backdrop (.ahome__bg__grain) — no
   persistent grain on the bare page. */
.ahome::before { display: none; }

/* Hide the legacy strip-style home + mobile rules already handle small screens */
.home, .home__strip-wrap, .home__status { display: none !important; }
.ahome__grid > * { pointer-events: auto; }

@media (max-width: 1100px) {
  .ahome__grid {
    grid-template-columns: repeat(2, 1fr);
    gap: 70px 40px;
    padding: 60px 0;
  }
  .ahome__cell--sm { width: 160px; }
  .ahome__cell--md { width: 220px; }
  .ahome__cell--lg { width: 280px; }
  /* Inversion portrait idem au breakpoint tablette */
  .ahome__cell--portrait.ahome__cell--sm { width: auto; height: 160px; }
  .ahome__cell--portrait.ahome__cell--md { width: auto; height: 220px; }
  .ahome__cell--portrait.ahome__cell--lg { width: auto; height: 280px; }
}
@media (max-width: 760px) {
  .ahome { display: none; }
}

.chome {
  position: fixed;
  inset: 0;
  z-index: 10;
  cursor: crosshair;
  overflow: hidden;
}

.chome__bg {
  position: absolute;
  inset: 0;
  background: #050505;
}
.chome__bg__layer {
  position: absolute;
  inset: 0;
  background-size: cover;
  background-position: center;
  opacity: 0;
  transition: opacity 0.7s ease;
  filter:
    sepia(var(--bg-sepia, 0.45))
    saturate(var(--bg-saturate, 0.85))
    contrast(var(--bg-contrast, 1.08))
    brightness(var(--bg-brightness, 0.78));
  transform: scale(1.04);
}
.chome__bg__layer.is-active { opacity: 1; }

/* Long vertical streaks à la Chems — drawn with stacked thin gradients */
.chome__bg__streaks {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    repeating-linear-gradient(
      90deg,
      transparent 0,
      transparent 6px,
      rgba(0, 0, 0, 0.22) 6px,
      rgba(0, 0, 0, 0.22) 7px,
      transparent 7px,
      transparent 22px,
      rgba(255, 255, 255, 0.05) 22px,
      rgba(255, 255, 255, 0.05) 23px
    ),
    repeating-linear-gradient(
      90deg,
      transparent 0,
      transparent 60px,
      rgba(0, 0, 0, 0.18) 60px,
      rgba(0, 0, 0, 0.18) 61px
    );
  mix-blend-mode: multiply;
  opacity: 0.65;
}
.chome__bg__vignette {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    radial-gradient(ellipse at center, transparent 35%, rgba(0, 0, 0, 0.55) 100%),
    linear-gradient(180deg, rgba(0,0,0,0.55) 0%, rgba(0,0,0,0) 18%, rgba(0,0,0,0) 70%, rgba(0,0,0,0.78) 100%);
}

/* ── Top inline nav ────────────────────────────────── */
.chome__top {
  position: absolute;
  top: 22px;
  left: 0;
  right: 0;
  padding: 0 28px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  z-index: 40;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink);
  cursor: default;
}
.chome__top-left {
  display: inline-flex;
  align-items: center;
  gap: 28px;
}
.chome__brand { color: var(--ink); }
.chome__navlink {
  color: var(--ink);
  padding: 4px 0;
  transition: opacity 0.18s ease;
}
.chome__navlink:hover { opacity: 0.65; }
.chome__top-right {
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}
.chome__tz { color: var(--ink-dim); }

/* ── Centered crosshair ────────────────────────────── */
.chome__cross {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  z-index: 30;
  width: 22px;
  height: 22px;
  display: grid;
  place-items: center;
  background: rgba(0, 0, 0, 0.4);
  color: var(--ink);
  font-family: var(--font-mono);
  font-size: 18px;
  line-height: 1;
  pointer-events: none;
  user-select: none;
}

/* ── Bottom rows ───────────────────────────────────── */
.chome__rows {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 35;
  cursor: pointer;
}
.chome__rows__inner,
.chome__rows__peek {
  width: 100%;
  position: relative;
}
.chome__rows__peek {
  opacity: 0.55;
  height: 32px;
  overflow: hidden;
  transition: opacity 0.25s ease, height 0.25s ease;
}
.chome__rows__peek:hover { opacity: 1; height: auto; }
.chome__rows__peek::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, rgba(217, 70, 90, 0.35), rgba(217, 70, 90, 0.18) 60%, transparent);
  pointer-events: none;
  z-index: -1;
}

/* one metadata row */
.chome__row {
  display: grid;
  grid-template-columns:
    auto 2.2fr
    auto 1.5fr
    auto auto
    auto 1.7fr;
  gap: 14px 18px;
  align-items: baseline;
  padding: 18px 28px;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink);
  background: rgba(5, 5, 5, 0.55);
  backdrop-filter: blur(4px);
  border-top: 1px solid rgba(138, 122, 32, 0.25);
}
.chome__row.is-active {
  border-top-color: var(--ink);
  background: rgba(5, 5, 5, 0.4);
}
.chome__col--label  { color: var(--ink-dim); font-size: 10px; letter-spacing: 0.22em; }
.chome__col--value  { color: var(--ink); }
.chome__col--name {
  font-family: var(--font-display);
  font-style: italic;
  font-size: clamp(22px, 2.4vw, 34px);
  letter-spacing: -0.01em;
  text-transform: none;
  line-height: 1;
  color: var(--ink);
}
.chome__col--tc {
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  font-size: 11px;
}
.chome__col--svc { letter-spacing: 0.18em; }

/* ── Pager on right edge ───────────────────────────── */
.chome__pager {
  position: absolute;
  right: 18px;
  top: 50%;
  transform: translateY(-50%);
  z-index: 40;
  display: flex;
  flex-direction: column;
  gap: 14px;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.18em;
  color: var(--ink-dim);
}
.chome__pager__dot {
  color: var(--ink-dim);
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 4px 0;
  font-variant-numeric: tabular-nums;
  transition: color 0.18s ease;
}
.chome__pager__dot .dash { opacity: 0.4; }
.chome__pager__dot:hover { color: var(--ink); }
.chome__pager__dot.is-active { color: var(--ink); }
.chome__pager__dot.is-active .dash { opacity: 1; }

/* Hide the chems-style home — restored to strip version */
.chome { display: none !important; }

/* Mobile keeps the staggered grid (HomeMobile) */
@media (max-width: 700px) {
  .chome__row {
    grid-template-columns: auto 1fr;
    grid-auto-flow: row dense;
    gap: 6px 12px;
    padding: 14px 18px;
    font-size: 10px;
  }
}

/* ============================================================
   VIEWER — single project, focused image + right rail
   ============================================================ */

.viewer {
  position: relative;
  min-height: 100vh;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}

.viewer__main {
  position: relative;
  /* Plein viewport : stage en absolu prend toute la place et le média
     est centré dedans. Les contrôles + caption sont positionnés en bas. */
  width: 100vw;
  height: 100vh;
  margin: 0;
}

/* Video resources, fullscreen mode: video sits in a contained frame with
   controls + label flowing directly below it (so the playback bar is
   always glued to the bottom of the video, not to the bottom of the screen). */
.viewer--video {
  align-items: center;
  justify-content: center;
}
.viewer--video .viewer__main {
  position: relative;
  inset: auto;
  width: 100vw;
  max-width: none;
  padding: 0 32px;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  justify-content: center;
  gap: 0;
}
.viewer--video .viewer__stage {
  position: relative;
  inset: auto;
  width: 100%;
  height: auto;
  max-height: calc(100vh - 220px);
  aspect-ratio: 16 / 9;
  background: #000;
}
.viewer--video .viewer__stage img,
.viewer--video .viewer__stage video {
  width: 100%;
  height: 100%;
  max-height: calc(100vh - 220px);
  object-fit: contain;
}

/* Fullscreen video : barre de lecture avec fond sombre pour rester lisible
   sur n'importe quel contenu */
.viewer--video .viewer__controls {
  background: rgba(5,5,5,0.55);
  backdrop-filter: blur(6px);
  padding: 12px 14px;
}
/* Fullscreen toggle button — sits in the bottom-right of the stage */
.viewer__stage { position: relative; }
.viewer__fs {
  position: absolute;
  right: 10px;
  bottom: 10px;
  z-index: 6;
  width: 34px;
  height: 34px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: rgba(5, 5, 5, 0.55);
  color: var(--ink);
  backdrop-filter: blur(6px);
  border-radius: 0;
  transition: background 0.2s ease, transform 0.2s ease;
}
.viewer__fs:hover { background: rgba(5, 5, 5, 0.85); transform: scale(1.06); }
.viewer--video .viewer__fs {
  right: 28px;
  bottom: 28px;
  width: 40px;
  height: 40px;
  z-index: 10;
}

/* Keep the rail and utilbar above the fullscreen video */
.viewer--video .viewer__rail { z-index: 8; }
.viewer--video .utilbar { z-index: 8; }

/* Soft top + bottom vignette so chrome stays legible on any image */
.viewer--video::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 5;
  pointer-events: none;
  background:
    linear-gradient(180deg,
      rgba(0,0,0,0.55) 0%,
      rgba(0,0,0,0)   18%,
      rgba(0,0,0,0)   62%,
      rgba(0,0,0,0.7) 100%);
}

/* Stage is locked to a constant 16:9 reference frame so the surrounding
   chrome (title above, player below) doesn't jump when switching between
   resources of different aspect ratios. The actual media inside fits
   within that frame at its own ratio (letterbox / pillarbox as needed). */
/* Stage : hauteur FIXE, peu importe le ratio du média. C'est le contenu
   qui s'adapte à la hauteur (width: auto), pas l'inverse. Comme Renell
   Medrano : portrait, landscape, carré → tous occupent la même hauteur. */
.viewer__stage {
  /* Absolu plein viewport — média + contrôles centrés ensemble.
     Les contrôles vidéo sont directement sous le média (pas en bas
     de l'écran), comme demandé. */
  position: absolute;
  inset: 0;
  background: transparent;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  /* Gap réduit (4px au lieu de 14px) : la barre colle quasi au bord
     bas de la vidéo. Le margin-top sur .viewer__controls--fixed est
     également mis à 0 pour cumuler l'effet. */
  gap: 4px;
}
/* Wrapper média — englobe le média (vidéo/image) ET la barre de lecture
   pour qu'elles soient toujours soudées visuellement, à la MÊME LARGEUR,
   sans aucun gap parasite.
   Pattern : display: flex column + width: fit-content. Le wrap s'ajuste
   au pixel près à la largeur du média (la barre, plus étroite, ne contraint
   pas le wrap car elle est en align-self:stretch et suit la largeur
   décidée par le média). Du coup la barre fait toujours pile la largeur
   de la vidéo, sans dépendre du JS mediaWidth (qui reste comme renfort).
   En mobile, on positionne le wrap en absolu pile au centre du viewport
   (cf. règle dans @media (max-width: 1200px) plus bas). */
.viewer__media-wrap {
  display: flex;
  flex-direction: column;
  width: fit-content;
  max-width: 100%;
  /* z-index > .viewer__edge (4) → la barre de lecture (qui vit dans
     media-wrap) reçoit bien ses clics même sur ses bords gauche/droite
     qui chevauchent les tap zones prev/next du stage. Sinon les clics
     près des extrémités de la barre déclenchaient goPrev/goNext au
     lieu de seek. position: relative pour que z-index s'applique. */
  position: relative;
  z-index: 6;
}
/* La barre s'étire à la largeur du wrap = largeur du média */
.viewer__media-wrap > .viewer__controls--fixed {
  align-self: stretch;
  width: auto;
}

/* Message d'échec de chargement du média principal du viewer */
.viewer__media-fail {
  padding: 18px 22px;
  border: 1px dashed var(--rule);
  color: var(--ink);
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  text-align: center;
  background: rgba(15, 14, 10, 0.8);
}

/* Label optionnel au-dessus du média — mêmes dimensions que la projmeta
   (.viewer__projmeta) en dessous du média : font-size 11px,
   letter-spacing 0.14em, uppercase, opacity 0.55 (= projmeta__client).
   Centré horizontalement, animation typewriter via le composant Type. */
.viewer__media-label {
  align-self: stretch;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink);
  opacity: 0.55;
  padding: 0 0 18px 0;
  line-height: 1;
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
@media (max-width: 1200px) {
  .viewer__media-label {
    font-size: 10px;
    letter-spacing: 0.1em;
    padding: 0 0 14px 0;
  }
}
.viewer__media-wrap > img,
.viewer__media-wrap > video {
  display: block;
  /* /!\ max-height (pas height fixe) pour que le box du <video> shrinke
     à la taille intrinsèque du contenu visible. Sinon, pour une vidéo
     paysage capée à max-width 80vw, le box garde la hauteur calc(100vh
     - 340px) tandis que le contenu visible est plus petit (letterboxing
     dans le box) → vide entre la fin visible de la vidéo et la barre
     de lecture qui suit dans media-wrap. Là, le box colle au contenu
     = bar littéralement collée sous la vidéo, peu importe l'aspect
     ratio. Même fix qu'en mobile. */
  max-height: calc(100vh - 340px);
  max-width: 80vw;
  width: auto;
  height: auto;
  object-fit: contain;
  background: transparent;
}
.viewer__stage.fade-in {
  animation: fadeIn 0.35s ease;
}
@keyframes fadeIn {
  from { opacity: 0; transform: scale(0.99); }
  to { opacity: 1; transform: scale(1); }
}

/* Caption "PROJET" + "CLIENT" — stack vertical (client en dessous du nom),
   fixée verticalement entre le player bar et l'utilbar du bas. */
.viewer__projmeta {
  position: fixed;
  left: 0;
  right: 0;
  transform: translateY(-50%);
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  gap: 4px;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink);
  text-align: center;
  pointer-events: none;
  z-index: 85;
}
.viewer__projmeta__name { display: block; }
.viewer__projmeta__sep { display: none; } /* plus de séparateur en stack vertical */
.viewer__projmeta__client { display: block; opacity: 0.55; }
.viewer__projmeta__role { display: block; opacity: 0.4; }
.viewer__projmeta__counter {
  display: block;
  margin-top: 8px;
  opacity: 0.4;
  font-variant-numeric: tabular-nums;
}

/* En mobile, la caption se place juste SOUS la vidéo / le player (pas de
   centrage entre 2 ancres puisque l'utilbar desktop est masquée). On retire
   le translateY(-50%) pour que le `top` injecté en JS corresponde bien au
   bord supérieur de la caption.
   Breakpoint 1200px (au lieu de 1000) : sur petits laptops/iPad paysage,
   le rail droit (right:0, ~150px de footprint) chevauchait le média
   (max-width:90vw centré). À 1200px et en dessous, on bascule en layout
   mobile où le rail est pinné en bas. */
@media (max-width: 1200px) {
  /* En mobile, la caption projet passe en bas du viewport, centrée
     horizontalement et verticalement entre le bas du média et le bord
     bas du viewport. Le `top` est posé en inline-style par le JS
     (captionY = midpoint(media bottom, viewport bottom)).
     flex-direction: column → ordre NOM → CLIENT → ROLE → counter
     (counter remonté en order:-1 au-dessus). */
  .viewer__projmeta {
    /* On NE met PAS top:auto ici — on laisse le JS poser top:captionY
       en inline-style. Idem pas de bottom fixe. translate(-50%) sur Y
       pour caler le bloc visuellement au centre vertical de l'espace. */
    bottom: auto !important;
    left: 0;
    right: 0;
    transform: translateY(-50%) !important;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: 2px;
    font-size: 9px;
    letter-spacing: 0.08em;
    line-height: 1.15;
    max-width: 90vw;
    margin: 0 auto;
    text-shadow: 0 1px 4px rgba(0, 0, 0, 0.7);
  }
  /* Toutes les lignes de la projmeta (counter, name, client, role) sont
     à la même taille typographique — on garde la hiérarchie par opacité
     et casse (pas par font-size). Le nom reste pleine opacité ; client
     et role sont déjà à 0.55 / 0.4 dans la base. */
  .viewer__projmeta__name {
    font-size: 9px;
    letter-spacing: 0.08em;
    opacity: 1;
    text-transform: uppercase;
  }
  /* Counter au-dessus du nom (style Renell) : order: -1 le remonte en
     haut de la pile flex sans toucher au DOM. L'ordre visuel devient
     donc COUNTER (top) → NAME → CLIENT → ROLE. Margin-bottom plus
     généreux (16px) pour DÉTACHER le counter du bloc nom/client/role :
     visuellement, le counter "01/10" devient un meta-marker discret
     posé au-dessus, séparé du titre. */
  .viewer__projmeta__counter {
    order: -1;
    margin-top: 0;
    margin-bottom: 16px;
  }
  /* Pas de rail en mobile (style Renell) — la navigation se fait via
     les edge tap zones (prev/next) et les contrôles HUD. Le rail
     entier est masqué, y compris son counter. */
  .viewer__rail,
  .viewer__rail__counter {
    display: none !important;
  }
}

/* Video controls — Frame 110 style.
   Barre de lecture plein largeur en bas du viewport (style cinéma). */
.viewer__controls {
  display: flex;
  align-items: center;
  gap: 18px;
  padding: 14px 4px 0;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.1em;
  color: var(--ink);
  width: 100%;
  box-sizing: border-box;
}
/* Barre de lecture juste sous la vidéo — en flow normal du stage flex
   column. Sa largeur est verrouillée sur la largeur réelle du média
   (inline-style depuis le JS) pour s'aligner sur ses bords. */
.viewer__controls--fixed {
  position: relative;
  /* margin-top retiré : la barre colle direct sous la vidéo, séparée
     uniquement par le gap de 4px du stage flex. */
  margin-top: 0;
  padding: 0;
  z-index: 5;
}
.viewer__controls .play-btn,
.viewer__controls .mute-btn,
.viewer__controls .fs-btn {
  color: var(--ink);
  padding: 4px 0;
  background: transparent;
  border: 0;
  font-family: var(--font-mono);
  /* Taille fluide qui rétrécit avec la largeur de la vidéo : on autorise
     une plage de 9px (vidéos étroites) à 11px (vidéos larges). */
  font-size: clamp(9px, 1.1cqi, 11px);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  cursor: pointer;
  white-space: nowrap;
  flex-shrink: 0;
  transition: color 0.18s ease;
}
.viewer__controls .play-btn { text-align: left; }
.viewer__controls .fs-btn {
  /* SVG icon-only, centré vertical, taille pile alignée sur les textes */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 4px 2px;
}
.viewer__controls .fs-btn svg { display: block; }
.viewer__controls .play-btn:hover,
.viewer__controls .mute-btn:hover,
.viewer__controls .fs-btn:hover { color: var(--yellow); }
/* Strike-through line on the SOUND label when muted */
.viewer__controls .mute-btn.is-muted {
  text-decoration: line-through;
  text-decoration-thickness: 1px;
  text-decoration-skip-ink: none;
}
.viewer__controls .bar {
  flex: 1 1 0;
  min-width: 0;
  height: 18px;
  background: transparent;
  position: relative;
  cursor: pointer;
  display: flex;
  align-items: center;
}
.viewer__controls .bar::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  height: 1px;
  background: var(--rule);
  transform: translateY(-50%);
}
.viewer__controls .bar__fill {
  position: absolute;
  left: 0;
  top: 50%;
  height: 3px;
  /* Même mustard que le texte au lieu du jaune accent bright */
  background: var(--ink);
  transform: translateY(-50%);
  /* Le navigateur n'envoie un timeupdate que ~4-5 fois/seconde — on
     interpole en CSS pour que la barre avance en continu pendant la
     lecture, au lieu de sauter d'un cran toutes les 250ms.            */
  transition: width 0.25s linear;
}
.viewer__controls .time {
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  flex-shrink: 0;
  font-size: clamp(9px, 1.1cqi, 11px);
}
.viewer__controls .time .total { color: var(--ink); }

/* Right rail of resources — pinned flush to the right edge, vertically centred,
   capped so it never spans the full viewport height. */
.viewer__rail {
  position: absolute;
  right: 0;
  top: 50%;
  transform: translateY(-50%);
  /* Flex column : thumbs stack naturellement, centrés verticalement.
     Pas de scroll. Si peu de thumbs → rail compact. Si beaucoup → le
     calcul JS dans viewer.jsx définit --rail-thumb-w (largeur réduite)
     pour que tout rentre dans 70vh sans dépasser. */
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  justify-content: center;
  gap: 10px;
  max-height: 70vh;
  overflow: hidden;
  /* z-index > viewer__edge (4) pour que les mousemove du dock effect
     atteignent bien les miniatures. */
  z-index: 6;
  /* Padding gauche pour que la miniature qui se décale à gauche ait la
     place visuelle nécessaire sans déborder du parent. */
  padding-left: 50px;
}
.viewer__rail::-webkit-scrollbar { display: none; }

/* Compteur de position dans la galerie (01/07) — affiché en haut du rail. */
.viewer__rail__counter {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink);
  opacity: 0.55;
  padding: 0 4px 8px;
  font-variant-numeric: tabular-nums;
  text-align: right;
  white-space: nowrap;
  align-self: flex-end;
}

.viewer__thumb {
  position: relative;
  /* Largeur = var --rail-thumb-w injectée depuis viewer.jsx (calculée
     pour que tous les thumbs rentrent dans 70vh sans dépasser).
     Hauteur auto, dictée par l'aspect natif du media. */
  display: block !important;
  width: var(--rail-thumb-w, 100px) !important;
  height: auto !important;
  flex-shrink: 0 !important;
  box-sizing: border-box;
  overflow: hidden;
  cursor: pointer;
  background: #0a0908;
  border: 0;
  padding: 0;
  /* Seule la miniature sous le curseur reçoit un translateX (--mag-x).
     Les autres restent statiques. Pas d'agrandissement, pas de push. */
  transform: translateX(var(--mag-x, 0));
  transition: opacity 0.2s ease,
              transform 0.18s ease-out;
}
/* /!\ Les variations forcées par nth-child (height: 110px portrait, 50px
   pano, etc.) sont SUPPRIMÉES : on respecte maintenant l'aspect réel
   de chaque resource via aspect-ratio inline-style. */
/* Hover : juste l'opacité — le scale est géré par le dock effect (--mag) */
.viewer__thumb:hover { opacity: 1; }
/* Miniature active : juste full opacity + version couleur (passe-passe via
   le sélecteur .active img/video plus bas). Plus de contour jaune. */
.viewer__thumb.active { opacity: 1; }
/* Same B&W treatment as the home (work) grid — only the currently
   selected resource is shown in colour. Hover restores some colour
   and scales slightly. */
.viewer__thumb img,
.viewer__thumb video {
  display: block;
  /* Largeur 100% du parent (= --rail-thumb-w), hauteur auto → l'image
     garde son ratio natif et dicte sa hauteur. */
  width: 100% !important;
  height: auto !important;
  object-fit: initial !important;
  filter: grayscale(1) contrast(1.05) brightness(0.95);
  transition: filter 0.3s ease, transform 0.5s ease;
}
.viewer__thumb:hover img,
.viewer__thumb:hover video {
  /* Plus de scale interne sur le hover : c'est le parent .viewer__thumb
     qui scale via --mag (dock effect). Sinon ça compose deux transforms
     et l'image semble "shifter" / cropper différemment au survol. */
  filter: grayscale(0.4) contrast(1.1);
}
.viewer__thumb.active img,
.viewer__thumb.active video,
.viewer__thumb.active:hover img,
.viewer__thumb.active:hover video {
  filter: none;
  transform: none;
}
/* Fallback affiché si une resource a ni poster ni src valide */
.viewer__thumb__missing {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  color: var(--dim);
  font-family: var(--font-mono);
  font-size: 14px;
  background: #141210;
}

/* When the rail is hovered, the non-hovered thumbs fade out — same
   "spotlight" behaviour as the home grid. */
.viewer__rail:hover .viewer__thumb:not(:hover) {
  opacity: 0.55;
}
.viewer__thumb__play {
  /* Logo ▶ masqué sur les miniatures du rail — choix éditorial. La
     reconnaissance "vidéo vs image" se fait visuellement (preview qui
     bouge au hover si vidéo réelle) plutôt que via un pictogramme. */
  display: none;
}

/* Bottom utility bar — shared across viewer + info overlay */
.utilbar {
  position: fixed;
  bottom: 14px;
  left: 18px;
  right: 18px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  z-index: 90;
}
.utilbar > button,
.utilbar > span {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: var(--ink);
}
.utilbar > *:first-child { justify-self: start; }
.utilbar > *:nth-child(2) { justify-self: center; }
.utilbar > *:last-child { justify-self: end; }
.utilbar .key {
  color: var(--ink);
  font-family: var(--font-mono);
  letter-spacing: 0;
}
.utilbar button:hover { color: var(--ink); }

/* OVERVIEW trigger — bouton standalone, MOBILE-ONLY. Positionné en
   bas-gauche, même taille typographique que le bouton MENU (top-right).
   Plain text, pas de box. */
.viewer__overview-trigger {
  display: none;
}
@media (max-width: 1200px) {
  .viewer__overview-trigger {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    position: fixed;
    bottom: 14px;
    left: 14px;
    z-index: 100; /* > utilbar (90) pour rester cliquable au-dessus du fond */
    font-family: var(--font-mono);
    font-size: 10px;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--ink);
    background: transparent;
    border: 0;
    padding: 4px 0;
    cursor: pointer;
  }
  .viewer__overview-trigger:active { opacity: 0.6; }
  /* La utilbar entière est cachée sur mobile — ses deux boutons (CLOSE,
     INFO) sont remplacés par les triggers standalone OVERVIEW (bas-gauche)
     et INFO (bas-droit). Pour quitter le viewer : passer par le menu. */
  .utilbar { display: none !important; }
}

/* INFO trigger — bouton standalone, MOBILE-ONLY, miroir de OVERVIEW
   mais en bas-droit. Même taille typographique que MENU (top-right). */
.viewer__info-trigger {
  display: none;
}
@media (max-width: 1200px) {
  .viewer__info-trigger {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    position: fixed;
    bottom: 14px;
    right: 14px;
    z-index: 100;
    font-family: var(--font-mono);
    font-size: 10px;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--ink);
    background: transparent;
    border: 0;
    padding: 4px 0;
    cursor: pointer;
  }
  .viewer__info-trigger:active { opacity: 0.6; }
}

/* ============================================================
   OVERVIEW panel — mobile gallery grid (style Renell Medrano)
   - Panel qui slide-up depuis le bas → couvre la moitié inférieure
   - Le viewer derrière est blurré (body.overview-open .viewer)
   - Backdrop sur la moitié haute : tap → close
   - Visible mobile only
   ============================================================ */
.viewer__overview-backdrop {
  position: fixed;
  left: 0;
  right: 0;
  top: 0;
  bottom: 50dvh; /* couvre exactement la moitié supérieure */
  z-index: 69; /* sous le panel (70), au-dessus du viewer */
  background: transparent;
  display: none;
  /* pointer-events off quand fermé → le viewer reste interactif */
  pointer-events: none;
}
@media (max-width: 1200px) {
  .viewer__overview-backdrop { display: block; }
}
.viewer__overview-backdrop.is-open {
  pointer-events: auto;
}
.viewer__overview {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  top: 50dvh; /* moitié inférieure du viewport visible */
  /* Noir solide (pas le mustard semi-transparent qui paraissait gris). */
  background: #000;
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  z-index: 70;
  /* Scroll vertical réactivé : si y a beaucoup de resources, on scrolle
     dans le panel pour voir le reste de la grille. */
  overflow-y: auto;
  overflow-x: hidden;
  padding: 18px 14px 70px; /* bottom-padding pour pas chevaucher OVERVIEW button */
  display: none;
  /* Slide-up animation depuis le bas */
  transform: translateY(100%);
  transition: transform 280ms cubic-bezier(0.22, 0.78, 0.32, 1);
}
@media (max-width: 1200px) {
  .viewer__overview { display: block; }
}
.viewer__overview.is-open {
  transform: translateY(0);
}
/* Blur du viewer quand l'overview est ouvert (même mécanique que .menu-open) */
body.overview-open .viewer__main,
body.overview-open .viewer__projmeta,
body.overview-open .viewer__sticker,
body.overview-open .utilbar {
  filter: blur(10px);
  transition: filter 0.3s ease;
}
.viewer__overview__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  padding: 0 4px 12px;
  margin-bottom: 12px;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink);
}
.viewer__overview__title-client { opacity: 0.55; }
.viewer__overview__count { opacity: 0.55; font-size: 9px; white-space: nowrap; }
.viewer__overview__grid {
  display: grid;
  /* Nombre de colonnes calculé dynamiquement depuis JS (CSS var --grid-cols).
     Toutes les colonnes 1fr → cells de MÊME LARGEUR. */
  grid-template-columns: repeat(var(--grid-cols, 3), 1fr);
  /* Rangées auto : la hauteur d'une rangée = celle de la cell la plus
     haute (image avec la plus grande hauteur naturelle). align-items:
     start pour que les cells plus petites collent au haut au lieu
     d'être centrées. */
  grid-auto-rows: auto;
  align-items: start;
  gap: 6px;
}
.viewer__overview__cell {
  position: relative;
  background: transparent;
  border: 0;
  padding: 0;
  overflow: hidden;
  cursor: pointer;
  border-radius: 2px;
  transition: transform 120ms ease, opacity 120ms ease;
  width: 100%; /* remplit la colonne */
  /* aspect-ratio inline depuis JSX (style={{ aspectRatio: r.aspect }}) */
}
.viewer__overview__cell:active { transform: scale(0.96); }
/* Plus d'outline mustard sur la cell active — pas de marqueur visuel
   de la resource actuellement consultée. */
.viewer__overview__cell.is-active { outline: none; }
/* (Plus de feature 2×2 sur la 1ère cell — toutes les cells sont
   égales dans la grille. */
.viewer__overview__cell > img,
.viewer__overview__cell > video {
  /* L'image remplit 100% de la largeur de sa cell et sa hauteur est
     dictée par son aspect natif. Pas de crop, pas de position absolute. */
  position: static;
  display: block;
  width: 100%;
  height: auto;
  object-fit: initial;
  display: block;
}
.viewer__overview__cell__missing {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  color: var(--ink);
  opacity: 0.4;
  font-family: var(--font-mono);
  font-size: 24px;
}
.viewer__overview__cell__num {
  position: absolute;
  top: 8px;
  left: 8px;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.14em;
  color: #fff;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
}
/* Icône play ▶ retirée des thumbs vidéo de l'overview mobile. */
.viewer__overview__cell__play { display: none; }

/* ============================================================
   INFORMATIONS overlay
   ============================================================ */

/* Information overlay — slides up from the bottom; background reuses the
   same CRT-noise treatment used on the home hover. */
.infos {
  position: fixed;
  inset: 0;
  z-index: 70;
  pointer-events: none;
  visibility: hidden;
}
.infos.open { pointer-events: auto; visibility: visible; }

.infos__bg {
  position: absolute;
  inset: 0;
  overflow: hidden;
  /* Mêmes transitions que .ahome__bg : fade-in + léger zoom vers 1 */
  transform: scale(1.05);
  transition: opacity 0.5s ease, transform 1.2s ease;
  background: #050505;
  opacity: 0;
  transition: opacity 0.5s ease;
}
.infos.open .infos__bg { opacity: 1; transform: scale(1); }

/* Vidéo / image preview en fond, mêmes filtres que .ahome__bg
   mais brightness réduite (0.6 → 0.35) pour assombrir et améliorer
   la lisibilité du texte par-dessus. */
.infos__bg video,
.infos__bg__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  filter: grayscale(1) contrast(1.1) brightness(0.35);
}
/* Veil radial plus opaque pour assombrir davantage le fond — surtout
   sur les bords pour préserver la lisibilité du texte sur toutes
   les tailles de viewport. */
.infos__bg__veil {
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse at center, rgba(0,0,0,0.6) 0%, rgba(0,0,0,0.92) 100%);
}

.infos__inner {
  position: absolute;
  inset: 0;
  padding: 80px 60px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  /* Centrage horizontal du bloc terminal (idx-term--info) à l'intérieur. */
  align-items: center;
  gap: 38px;
  color: var(--ink);
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  transform: translateY(40px);
  opacity: 0;
  transition: transform 0.55s cubic-bezier(0.2, 0.7, 0.2, 1), opacity 0.45s ease;
}
.infos.open .infos__inner { transform: translateY(0); opacity: 1; }

.infos__cols {
  display: grid;
  grid-template-columns: 1fr 1fr 1.2fr;
  gap: 60px;
  align-content: start;
}
.infos__col {
  display: flex;
  flex-direction: column;
  gap: 18px;
}
.infos__col .row {
  display: flex;
  gap: 18px;
  flex-wrap: wrap;
}
.infos__col a, .infos__col span {
  color: var(--ink);
  cursor: pointer;
}
.infos__col a:hover { color: var(--ink); }

.infos__footer {
  display: flex;
  gap: 22px;
  flex-wrap: wrap;
  color: var(--ink);
  border-top: 1px solid rgba(246, 226, 28, 0.2);
  padding-top: 28px;
}
.infos__footer a:hover { color: var(--ink); }

/* ============================================================
   Idle screensaver — dithered noise + bouncing pixel griot
   ============================================================ */

.screensaver {
  position: fixed;
  inset: 0;
  z-index: 250;
  background: #050505;
  overflow: hidden;
  animation: ss-fade-in 0.6s ease forwards;
  cursor: none;
}
@keyframes ss-fade-in {
  from { opacity: 0; }
  to { opacity: 1; }
}
.screensaver__bg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  filter: contrast(1.4);
  z-index: 0;
}
.screensaver__scene {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  width: 100%;
  height: 100%;
  z-index: 1;
  image-rendering: pixelated;
}
.screensaver__hero {
  position: absolute;
  left: 50%;
  bottom: 18%;
  transform: translateX(-50%);
  z-index: 4;
  filter: drop-shadow(4px 6px 0 rgba(0, 0, 0, 0.85));
  image-rendering: pixelated;
  animation: hero-bob 2.4s ease-in-out infinite;
}
@keyframes hero-bob {
  0%, 100% { transform: translateX(-50%) translateY(0); }
  50%      { transform: translateX(-50%) translateY(-4px); }
}

.screensaver::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: repeating-linear-gradient(
    0deg,
    rgba(0,0,0,0) 0px,
    rgba(0,0,0,0) 2px,
    rgba(0,0,0,0.4) 3px,
    rgba(0,0,0,0.4) 4px
  );
  mix-blend-mode: multiply;
  z-index: 5;
}
.screensaver::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  height: 220px;
  background: linear-gradient(180deg, transparent, rgba(255,230,80,0.06) 50%, transparent);
  pointer-events: none;
  z-index: 6;
  animation: ss-sweep 7s linear infinite;
}
@keyframes ss-sweep {
  from { top: -240px; }
  to { top: 100vh; }
}

.screensaver__title {
  position: absolute;
  top: 7%;
  left: 50%;
  transform: translateX(-50%);
  text-align: center;
  z-index: 7;
}
.screensaver__title .t-line {
  font-family: var(--font-mono);
  font-size: clamp(28px, 5.5vw, 64px);
  letter-spacing: 0.18em;
  color: var(--ink);
  text-shadow: 4px 4px 0 #000;
}
.screensaver__title .t-sub {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.24em;
  color: var(--ink);
  margin-top: 10px;
  opacity: 0.8;
}

.screensaver__hint {
  position: absolute;
  left: 50%;
  bottom: 5%;
  transform: translateX(-50%);
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.22em;
  color: var(--ink);
  background: rgba(0, 0, 0, 0.85);
  padding: 10px 18px;
  border: 1px solid var(--yellow);
  z-index: 7;
  animation: ss-blink 1.4s steps(2) infinite;
}
@keyframes ss-blink {
  0%, 49% { opacity: 1; }
  50%, 100% { opacity: 0.25; }
}

.griot-sprite {
  position: fixed;
  pointer-events: none;
  z-index: 60;
  transform: translate(-50%, -50%);
  animation: griot-life 3.4s cubic-bezier(0.2, 0.7, 0.2, 1) forwards;
  filter: drop-shadow(2px 3px 0 rgba(0, 0, 0, 0.6));
}
@keyframes griot-life {
  0%   { opacity: 0; transform: translate(-50%, -50%) scale(0.4); }
  12%  { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  85%  { opacity: 1; transform: translate(-50%, -50%) scale(1) translateY(-6px); }
  100% { opacity: 0; transform: translate(-50%, -50%) scale(1) translateY(-14px); }
}

/* Music note that floats up next to the griot */
.griot-note {
  position: fixed;
  pointer-events: none;
  z-index: 61;
  font-family: var(--font-mono);
  font-size: 18px;
  color: var(--ink);
  transform: translate(-50%, -50%);
  animation: note-float 2.6s ease-out forwards;
}
@keyframes note-float {
  0%   { opacity: 0; transform: translate(-50%, -50%) scale(0.6); }
  20%  { opacity: 1; }
  100% { opacity: 0; transform: translate(-50%, -120%) scale(1.1) rotate(8deg); }
}

.about-min {
  min-height: 100vh;
  padding: 110px 40px 110px;
  display: grid;
  grid-template-columns: minmax(420px, 560px) minmax(240px, 320px) minmax(220px, 280px);
  gap: 80px;
  /* The row of blocks is centred vertically as a group, but each block
     aligns by its top edge — shorter blocks don't rise to the centre. */
  align-items: start;
  align-content: center;
  justify-content: center;
  position: relative;
}
/* Keep the services list on a single line each — even the long
   "STAGE DIRECTION & MOVEMENT" / "DIRECTION SCÉNIQUE & MOUVEMENT". */
.about-min__col ul li { white-space: nowrap; min-height: 1.65em; }
/* Reserve the height of every typing line so the parent grid centering
   doesn't shift the whole block upward as text fills in. */
.about-min__col p { min-height: 1.65em; }

.about-min__intro {
  display: flex;
  flex-direction: column;
  gap: 22px;
  padding-left: 0;
  /* Réserve la hauteur finale dès le départ — sinon le centrage vertical
     du parent (align-content: center) recalcule à chaque caractère tapé
     et le bloc "remonte" pendant le typing. */
  min-height: 360px;
  justify-content: flex-start;
}
.about-min__intro p {
  font-family: var(--font-mono);
  font-size: 13px;
  letter-spacing: 0.08em;
  line-height: 1.55;
  color: var(--ink);
  text-transform: uppercase;
  text-wrap: pretty;
  /* Chaque paragraphe garde sa hauteur même quand il est encore vide
     pendant la cascade de typing. */
  min-height: 1.55em;
}

.about-min__col h6 {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink);
  font-weight: 400;
  margin-bottom: 16px;
}
.about-min__col address,
.about-min__col ul,
.about-min__col p {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.08em;
  line-height: 1.65;
  color: var(--ink);
  text-transform: uppercase;
  font-style: normal;
}
.about-min__col ul { list-style: none; padding: 0; }
.about-min__col a { color: var(--ink); }
.about-min__col a:hover { color: var(--ink); text-decoration: underline; text-underline-offset: 3px; }

/* Bottom-right seal logo */
.about-min__seal {
  position: fixed;
  right: 32px;
  bottom: 30px;
  width: 220px;
  height: auto;
  pointer-events: none;
}
.about-min__seal svg { display: block; width: 100%; height: auto; }
.about-min__seal .r {
  position: absolute;
  top: 12px;
  right: -10px;
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--ink);
}

.about-min__archive {
  position: fixed;
  left: 40px;
  bottom: 30px;
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink);
}
.about-min__archive a { cursor: pointer; }
.about-min__archive a.u { text-decoration: underline; text-underline-offset: 4px; }
.about-min__archive a:hover { color: var(--ink); }

@media (max-width: 1100px) {
  .about-min { grid-template-columns: 1fr 1fr; gap: 50px; padding: 110px 28px 110px; }
  .about-min__intro { padding-left: 0; grid-column: 1 / -1; }
  .about-min__seal { width: 160px; }
}
@media (max-width: 600px) {
  /* Remonte tout : padding-top réduit pour que le bloc commence juste sous
     le sticker, gap serré entre les blocs. */
  .about-min { grid-template-columns: 1fr; gap: 22px; padding: 130px 20px 110px; align-content: start; }

  /* L'intro garde sa hauteur finale réservée — comme ça quand le texte se
     tape, il REMPLIT des boîtes existantes au lieu de pousser Services et
     Contacts vers le bas. Le bloc ne monte/descend jamais. */
  .about-min__intro {
    min-height: 290px;       /* hauteur approximative des 4 paragraphes finals */
    gap: 14px;
    justify-content: flex-start;
  }
  .about-min__intro p {
    font-size: 11px;
    letter-spacing: 0.07em;
    line-height: 1.5;
    /* Chaque paragraphe a sa hauteur finale réservée (3 lignes max
       de 16.5px). Le texte se tape DANS la boîte, vers le bas. */
    min-height: calc(3 * 1.5em);
  }
  /* Premier paragraphe (1 ligne) — on ne réserve qu'1 ligne pour pas
     créer d'espace mort */
  .about-min__intro p:first-child { min-height: 1.5em; }

  .about-min__col h6 { font-size: 10px; letter-spacing: 0.14em; margin-bottom: 12px; min-height: 1.4em; }
  .about-min__col address,
  .about-min__col ul,
  .about-min__col p { font-size: 10px; letter-spacing: 0.07em; line-height: 1.55; }
  /* Reserve aussi la hauteur de chaque ligne typée dans Services / Contact */
  .about-min__col ul li, .about-min__col p { min-height: 1.55em; }
  .about-min__seal { width: 130px; right: 16px; bottom: 18px; }
  .about-min__archive { left: 16px; bottom: 18px; font-size: 10px; }
}

.about {
  padding: 110px 60px 90px 360px;
  max-width: 1200px;
}
.about h1 {
  font-family: var(--font-mono);
  font-size: 13px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink);
  margin-bottom: 50px;
  font-weight: 400;
}
.about__lead {
  font-family: var(--font-display);
  font-style: italic;
  font-size: clamp(36px, 4.6vw, 68px);
  line-height: 1.06;
  letter-spacing: -0.01em;
  color: var(--ink);
  margin-bottom: 60px;
  max-width: 900px;
}
.about__lead em {
  color: var(--ink);
  font-style: italic;
}
.about__grid {
  display: grid;
  grid-template-columns: 220px 1fr 1fr;
  gap: 40px;
  border-top: 1px solid var(--rule);
  padding-top: 30px;
  margin-bottom: 60px;
}
.about__grid h3 {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink);
  font-weight: 400;
  margin-bottom: 16px;
}
.about__grid p {
  font-family: var(--font-mono);
  font-size: 12px;
  line-height: 1.6;
  color: var(--ink);
}
.about__credo {
  background: var(--yellow);
  color: var(--black);
  padding: 50px 40px;
  margin-bottom: 60px;
  background-image:
    radial-gradient(circle at 1px 1px, rgba(0,0,0,0.07) 0.6px, transparent 1px),
    linear-gradient(180deg, var(--yellow) 0%, var(--yellow-deep) 100%);
  background-size: 5px 5px, 100% 100%;
}
.about__credo p {
  font-family: var(--font-display);
  font-style: italic;
  font-size: clamp(28px, 3.5vw, 48px);
  line-height: 1.08;
  color: var(--black);
}
.about__credo h3 {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--black);
  margin-bottom: 20px;
  font-weight: 700;
}

/* ============================================================
   ECOSYSTEME page — partners + collaborators
   ============================================================ */

.eco {
  padding: 110px 60px 90px 360px;
}
.eco h1 {
  font-family: var(--font-mono);
  font-size: 13px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink);
  font-weight: 400;
  margin-bottom: 24px;
}
.eco__lead {
  font-family: var(--font-display);
  font-style: italic;
  font-size: clamp(34px, 4.2vw, 64px);
  line-height: 1.04;
  letter-spacing: -0.01em;
  margin-bottom: 70px;
  max-width: 1100px;
}
.eco__lead em { color: var(--ink); }

.eco__section {
  border-top: 1px solid var(--rule);
  padding: 30px 0 50px;
  display: grid;
  grid-template-columns: 200px 1fr;
  gap: 40px;
}
.eco__section h3 {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-dim);
  font-weight: 400;
}
.eco__list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: 22px;
}
.eco__item {
  border-top: 1px solid var(--rule);
  padding-top: 12px;
}
.eco__item .name {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 22px;
  color: var(--ink);
  display: block;
  margin-bottom: 4px;
}
.eco__item:hover .name { color: var(--ink); }
.eco__item .meta {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-dim);
}

/* ============================================================
   Footer utility bar for non-viewer pages
   ============================================================ */

.page-foot {
  position: fixed;
  bottom: 14px;
  left: 18px;
  right: 18px;
  display: flex;
  justify-content: space-between;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-dim);
  z-index: 40;
  pointer-events: none;
}
.page-foot .key { color: var(--ink); }
.page-foot button { pointer-events: auto; }
.page-foot button:hover { color: var(--ink); }
/* Prompt terminal façon menu.sh : caret jaune brand juste avant le label
   "back". Le curseur clignotant (cursor="always" sur Type) suit le label. */
.page-foot button .prompt {
  color: var(--yellow);
  margin-right: 8px;
}

/* Mobile : on retire le bouton [X] BACK en bas à gauche de la page-foot
   (utilisable sur About / Eco). Sur mobile le sticker top-left assume
   déjà le rôle "retour accueil", et les visiteurs peuvent aussi swiper.
   On garde par contre copy + locs (les 2 spans à droite). */
@media (max-width: 760px) {
  .page-foot button { display: none; }
}

/* ============================================================
   Responsive
   ============================================================ */

@media (max-width: 1100px) {
  .viewer { grid-template-columns: 1fr 100px; }
  .viewer__main { padding-left: 0; padding-top: 200px; }
  /* Width un peu plus serré sur tablette (92px vs 100px desktop).
     La hauteur reste dérivée de l'aspect natif du media. */
  .viewer__thumb { width: 92px; height: auto; }
  .infos__inner, .about, .eco { padding-left: 32px; padding-right: 32px; }
  .infos__cols { grid-template-columns: 1fr 1fr; }
  .home__status { left: 320px; right: 220px; gap: 16px; }
}
/* ============================================================
   MOBILE — 3-col staggered grid (Acre-style)
   ============================================================ */

.home-mobile {
  display: none;
}

@media (max-width: 760px) {
  .home-mobile {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 14px 10px;
    padding: 110px 10px 110px;
    align-items: start;
  }
  .home-mobile__col {
    display: flex;
    flex-direction: column;
    gap: 80px;
  }
  .home-mobile__col--offset-1 { padding-top: 90px; }
  .home-mobile__col--offset-2 { padding-top: 200px; }

  .home-mobile__cell {
    position: relative;
    width: 100%;
    overflow: hidden;
    background: #0d0c0a;
    cursor: pointer;
  }
  .home-mobile__cell img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    filter: contrast(1.04) grayscale(0.05);
    transition: transform 0.5s ease, filter 0.4s ease;
  }
  .home-mobile__cell:active img,
  .home-mobile__cell:hover img {
    transform: scale(1.03);
    filter: contrast(1.08) grayscale(0);
  }
  .home-mobile__cell .cam {
    position: absolute;
    top: 6px;
    right: 6px;
    width: 16px;
    height: 13px;
    border: 1px solid rgba(255,255,255,0.85);
    border-radius: 1px;
  }
  .home-mobile__cell .cam::before {
    content: "";
    position: absolute;
    top: -3px;
    left: 3px;
    width: 6px;
    height: 3px;
    border: 1px solid rgba(255,255,255,0.85);
    border-bottom: 0;
    border-radius: 1px 1px 0 0;
  }
  .home-mobile__cell .cam::after {
    content: "";
    position: absolute;
    inset: 2px;
    border: 0.5px solid rgba(255,255,255,0.85);
    border-radius: 50%;
  }
  .home-mobile__cell--ratio-portrait { aspect-ratio: 3 / 4.4; }
  .home-mobile__cell--ratio-tall { aspect-ratio: 3 / 5; }
  .home-mobile__cell--ratio-mid { aspect-ratio: 3 / 3.6; }
  .home-mobile__cell--ratio-landscape { aspect-ratio: 4 / 3; }

  .home-mobile__label {
    grid-column: 1 / -1;
    font-family: var(--font-mono);
    font-size: 10px;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--ink-dim);
    padding: 10px 4px 0;
  }

  /* Mobile shell: hide the desktop strip & status, simplify chrome */
  .home__strip-wrap,
  .home__bg,
  .home__status { display: none !important; }

  /* idcard et boot-matrix sticker : taille mobile à 180px (cf. block
     dédié plus bas qui déclenche dès 1200px pour rester cohérent avec
     le breakpoint mobile du viewer). On garde aussi la déclaration ici
     pour les très petits écrans (≤760px) où le top/left est plus serré. */
  .idcard { width: 180px; top: 10px; left: 10px; }
  .boot-matrix__sticker { width: 180px; top: 10px; left: 10px; }
  .menubar { top: 14px; right: 14px; }
  .menubar__toggle { font-size: 10px; gap: 6px; }

  .viewer { grid-template-columns: 1fr; padding: 80px 14px 80px; }
  .viewer__main { padding: 0; padding-top: 30px; }
  .viewer__rail {
    flex-direction: row;
    height: auto;
    width: 100%;
    overflow-x: auto;
    overflow-y: hidden;
    padding: 16px 0 0;
  }
  .viewer__thumb { flex: 0 0 auto; width: 96px; height: 64px; }
  .about, .eco { padding: 110px 18px 90px; }
  .about__grid { grid-template-columns: 1fr; gap: 16px; }
  .eco__section { grid-template-columns: 1fr; gap: 16px; }
  .infos__cols { grid-template-columns: 1fr; gap: 30px; }
  .infos__inner { padding: 110px 18px 80px; }
  .utilbar, .page-foot { left: 12px; right: 12px; font-size: 10px; }
}


/* ============================================================
   ECOSYSTEM — 3-panel cinema hub
   ============================================================ */

/* ============================================================
   ECOSYSTEM — minimalist solar system with orbiting planets
   ============================================================ */
.eco-solar {
  position: relative;
  min-height: 100vh;
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  font-family: var(--font-mono);
  color: var(--ink);
  overscroll-behavior: contain;
  perspective: 1200px;
  perspective-origin: 50% 50%;
  cursor: grab;
  user-select: none;
}
.eco-solar.is-dragging { cursor: grabbing; }

/* The 3D plane that holds the whole system — its transform is driven
   by the JS rAF loop (translate3d + rotateX/Y). */
.eco-solar__plane {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 0;
  height: 0;
  transform-style: preserve-3d;
  will-change: transform;
  z-index: 2;
}

/* Hover backdrop — preview video or poster of the hovered planet */
/* Aligné sur .ahome__bg (page Work) : même fade, même B&W, même zoom-in
   subtil quand actif, même veil radial pour faire ressortir la planète. */
.eco-solar__bg {
  position: fixed;
  inset: 0;
  z-index: 0;
  opacity: 0;
  transform: scale(1.05);
  transition: opacity 0.45s ease, transform 1.2s ease;
  pointer-events: none;
}
.eco-solar__bg.is-active { opacity: 1; transform: scale(1); }
.eco-solar__bg video,
.eco-solar__bg__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  filter: grayscale(1) contrast(1.1) brightness(0.6);
}
.eco-solar__bg__veil {
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse at center, rgba(0,0,0,0.35) 0%, rgba(0,0,0,0.7) 100%);
}

/* ============================================================
   ECO MOBILE — 3 sections empilées, une par univers.
   Cachée en desktop. Sur mobile, masque la vue solaire (qui n'est
   de toute façon pas interactive en touch).
   ============================================================ */
.eco-mobile { display: none; }
@media (max-width: 900px) {
  /* ============================================================
     MOBILE — redesign 14/07/2026 : le texte et les identités des
     3 univers sont VISIBLES (demande Moos). Liste de cards
     scrollable : vignette pleine largeur + nom façon wordmark +
     kicker + description + CTA. (L'ancienne version "purement
     visuelle" cachait tout le texte.)
     ============================================================ */
  .eco-mobile {
    display: flex;
    flex-direction: column;
    position: relative;
    z-index: 5;
    min-height: 100dvh;
    padding-bottom: 48px;
  }
  .eco-solar > .eco-solar__bg,
  .eco-solar > .eco-solar__plane { display: none !important; }
  /* Le conteneur solaire desktop est overflow:hidden + centré flex —
     en mobile on le neutralise pour laisser la liste scroller. */
  .eco-solar {
    overflow: visible;
    display: block;
    min-height: 0;
    cursor: default;
    perspective: none;
  }
  /* La page scrolle normalement en mobile (cards + textes). */
  body:has(.eco-mobile) { overflow-y: auto; }
  /* Le footer fixe (copyright) chevauchait les cards au scroll → on le
     replace en flux, après la liste. */
  body:has(.eco-mobile) .page-foot {
    position: static;
    margin: 28px 18px 0;
    flex-direction: column;
    gap: 6px;
    align-items: flex-start;
  }

  /* Hero : l'accroche seule, alignee a gauche et calee SOUS le sticker
     jaune (fixe en haut a gauche, ~140px de haut avec sa marge) pour ne
     plus jamais passer derriere. */
  .eco-mobile__hero {
    flex: 0 0 auto;
    padding: 150px 24px 10px;
    text-align: left;
  }
  .eco-mobile__hero__lead {
    font-family: var(--font-mono);
    font-size: 11px;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--ink-dim);
    max-width: 400px;
    line-height: 1.55;
    margin: 0;
  }
  /* Griot ASCII en bas a droite : margin-top:auto le pousse au fond du
     conteneur flex 100dvh, align-self a droite. Decoratif, il ne bloque
     aucun tap. */
  .eco-mobile__griot {
    margin-top: auto;
    align-self: flex-end;
    padding: 28px 18px 0;
    pointer-events: none;
    line-height: 0;
  }
  /* Le pre .matrix-griot est fixe/centre plein ecran par defaut : on le
     remet dans le flux de son wrapper, exactement comme .ahome__griot
     (le griot bas-droite de la home). */
  .eco-mobile__griot .matrix-griot {
    position: static;
    top: auto;
    left: auto;
    transform: none;
    font-size: 3px;
    line-height: 1;
    color: var(--ink);
    white-space: pre;
    text-shadow: 0 0 6px rgba(138, 122, 32, 0.2);
    pointer-events: none;
  }

  /* ---- Cards empilées : vignette pleine largeur + bloc texte ------- */
  .eco-mobile__list {
    list-style: none;
    padding: 18px 18px 0;
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: 30px;
  }
  /* Ligne façon YARD "Initiatives" : vignette carrée à gauche,
     titre + sous-titre à droite, beaucoup d'air entre les lignes. */
  .eco-mobile__row {
    display: grid;
    grid-template-columns: minmax(96px, 34vw) 1fr;
    gap: 20px;
    align-items: start;
    cursor: pointer;
  }
  .eco-mobile__row__thumb {
    width: 100%;
    max-width: 170px;
    aspect-ratio: 16 / 9;
    overflow: hidden;
    background: #0d0c0a;
  }
  .eco-mobile__row__thumb video,
  .eco-mobile__row__thumb img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
  }
  .eco-mobile__row__text {
    display: flex;
    flex-direction: column;
    gap: 5px;
    min-width: 0;
    padding-top: 4px;
  }
  /* Nom = wordmark typographique de l'entité */
  .eco-mobile__row__name {
    font-family: var(--font-mono);
    font-size: 17px;
    font-weight: 500;
    color: var(--ink);
    letter-spacing: 0.1em;
    line-height: 1.3;
    margin: 0;
    text-transform: uppercase;
    word-break: break-word;
  }
  .eco-mobile__row.is-current .eco-mobile__row__name { color: var(--yellow); }
  .eco-mobile__row__kicker {
    font-family: var(--font-mono);
    font-size: 10px;
    color: var(--ink-dim);
    letter-spacing: 0.16em;
    text-transform: uppercase;
    margin: 0;
    line-height: 1.5;
  }
  /* Façon YARD : titre + sous-titre suffisent, la ligne est tappable.
     La description reste dispo en desktop (vue solaire) et dans le BO. */
  .eco-mobile__row__desc { display: none; }
  .eco-mobile__row__cta {
    font-family: var(--font-mono);
    font-size: 10px;
    color: var(--ink-dim);
    letter-spacing: 0.16em;
    margin: 10px 0 0;
    text-transform: uppercase;
    line-height: 1.4;
  }
  .eco-mobile__row:active .eco-mobile__row__cta { color: var(--yellow); }
  .eco-mobile__row__here {
    font-family: var(--font-mono);
    font-size: 10px;
    color: var(--yellow);
    letter-spacing: 0.16em;
    margin: 8px 0 0;
    text-transform: uppercase;
    line-height: 1.4;
  }
}

/* Sun = the central griot */
.eco-solar__sun {
  position: absolute;
  top: 0;
  left: 0;
  transform: translate(-50%, -50%);
  z-index: 3;
  /* Wrapper transparent aux events pour ne pas bloquer le pan dans le vide,
     mais le griot interne (matrix-griot) accepte les clicks via override. */
  pointer-events: none;
  transition: transform 0.6s cubic-bezier(0.2, 0.7, 0.2, 1);
}
.eco-solar__sun.is-boosted {
  /* Pulse léger pendant le boost pour signaler l'effet déclenché */
  transform: translate(-50%, -50%) scale(1.12);
}
.eco-solar__sun .matrix-griot {
  font-size: 4px;
  line-height: 1;
  color: var(--ink);
  text-shadow: 0 0 8px rgba(138, 122, 32, 0.35);
  white-space: pre;
  /* Réactive les clicks UNIQUEMENT sur le griot ASCII */
  pointer-events: auto;
  cursor: pointer;
}

/* Orbits = thin concentric circles */
.eco-solar__orbit {
  position: absolute;
  top: 0;
  left: 0;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  border: 1px solid rgba(138, 122, 32, 0.25);
  pointer-events: none;
}

/* Planet anchor: a 0×0 point that orbits around the centre along an
   ellipse defined by --rx / --ry. We use a translate-only animation
   (24 keyframes ≈ 15° apart) so the planet itself never inherits
   rotation or scale — it stays upright and round. */
/* Wrapper qui tilte chaque orbite individuellement → chaque planète
   gravite sur son propre plan 3D, plus de coplanarité. Le orbit ring
   et le anchor (planète + animation) partagent ce plan incliné. */
.eco-solar__orbit-tilt {
  position: absolute;
  top: 0;
  left: 0;
  width: 0;
  height: 0;
  transform-style: preserve-3d;
}
.eco-solar__anchor {
  position: absolute;
  top: 0;
  left: 0;
  width: 0;
  height: 0;
  animation: eco-orbit-ellipse linear infinite;
  transform-style: preserve-3d;
}

/* ===================================================================
   Trackball / globe interactif — bottom-center
   =================================================================== */
.eco-solar__trackball {
  position: absolute;
  left: 50%;
  bottom: 32px;
  transform: translateX(-50%);
  z-index: 5;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 14px;
  background: rgba(0, 0, 0, 0.5);
  border: 1px solid var(--ink);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  pointer-events: auto;
  user-select: none;
}
.eco-solar__trackball__ball {
  position: relative;
  width: 72px;
  height: 72px;
  border-radius: 50%;
  background:
    radial-gradient(circle at 30% 30%, rgba(255, 202, 0, 0.25), transparent 60%),
    radial-gradient(circle at 50% 50%, #1a1a1a, #000 70%);
  border: 1px solid var(--ink);
  cursor: grab;
  touch-action: none;
  overflow: hidden;
}
.eco-solar__trackball__ball:active { cursor: grabbing; }
.eco-solar__trackball__ring {
  position: absolute;
  inset: 14%;
  border: 1px dashed rgba(138, 122, 32, 0.35);
  border-radius: 50%;
}
.eco-solar__trackball__pin {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 8px;
  height: 8px;
  background: var(--ink);
  border-radius: 50%;
  box-shadow: 0 0 8px rgba(255, 202, 0, 0.5);
}
.eco-solar__trackball__reset {
  background: none;
  border: 1px solid var(--ink);
  color: var(--ink);
  font-family: var(--font-mono);
  font-size: 14px;
  cursor: pointer;
  width: 28px;
  height: 28px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  transition: background 0.15s ease, color 0.15s ease;
}
.eco-solar__trackball__reset:hover {
  background: var(--ink);
  color: var(--paper);
}
/* Trackball masqué sur mobile — la vue solaire (.eco-solar__plane) est déjà
   désactivée au profit du layout .eco-mobile. */
@media (max-width: 760px) {
  .eco-solar__trackball { display: none !important; }
}
.eco-solar__tilt__row {
  display: flex;
  align-items: center;
  gap: 8px;
}
.eco-solar__tilt__reset {
  background: none;
  border: 1px solid var(--ink);
  color: var(--ink);
  font-family: var(--font-mono);
  font-size: 13px;
  cursor: pointer;
  width: 24px;
  height: 24px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  transition: background 0.15s ease, color 0.15s ease;
}
.eco-solar__tilt__reset:hover {
  background: var(--ink);
  color: var(--paper);
}
.eco-solar__tilt__label {
  font-weight: 500;
  min-width: 10px;
  text-align: center;
}
.eco-solar__tilt__value {
  min-width: 38px;
  text-align: right;
  font-variant-numeric: tabular-nums;
  opacity: 0.65;
}
.eco-solar__tilt__slider {
  -webkit-appearance: none;
  appearance: none;
  width: 120px;
  height: 2px;
  background: var(--ink);
  border-radius: 0;
  outline: none;
  cursor: pointer;
}
.eco-solar__tilt__slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 12px;
  height: 12px;
  background: var(--ink);
  border: 1px solid var(--ink);
  border-radius: 50%;
  cursor: pointer;
  transition: transform 0.15s ease;
}
.eco-solar__tilt__slider::-webkit-slider-thumb:hover {
  transform: scale(1.25);
}
.eco-solar__tilt__slider::-moz-range-thumb {
  width: 12px;
  height: 12px;
  background: var(--ink);
  border: 1px solid var(--ink);
  border-radius: 50%;
  cursor: pointer;
}
@media (max-width: 760px) {
  .eco-solar__tilt {
    bottom: 80px;
    padding: 10px 12px;
    gap: 8px;
    flex-direction: column;
    align-items: stretch;
  }
  .eco-solar__tilt__row { gap: 6px; }
  .eco-solar__tilt__slider { width: 140px; }
}
@keyframes eco-orbit-ellipse {
  0%        { transform: translate(calc(var(--rx) *  1.0000), calc(var(--ry) *  0.0000)); }
  4.1667%   { transform: translate(calc(var(--rx) *  0.9659), calc(var(--ry) *  0.2588)); }
  8.3333%   { transform: translate(calc(var(--rx) *  0.8660), calc(var(--ry) *  0.5000)); }
  12.5%     { transform: translate(calc(var(--rx) *  0.7071), calc(var(--ry) *  0.7071)); }
  16.6667%  { transform: translate(calc(var(--rx) *  0.5000), calc(var(--ry) *  0.8660)); }
  20.8333%  { transform: translate(calc(var(--rx) *  0.2588), calc(var(--ry) *  0.9659)); }
  25%       { transform: translate(calc(var(--rx) *  0.0000), calc(var(--ry) *  1.0000)); }
  29.1667%  { transform: translate(calc(var(--rx) * -0.2588), calc(var(--ry) *  0.9659)); }
  33.3333%  { transform: translate(calc(var(--rx) * -0.5000), calc(var(--ry) *  0.8660)); }
  37.5%     { transform: translate(calc(var(--rx) * -0.7071), calc(var(--ry) *  0.7071)); }
  41.6667%  { transform: translate(calc(var(--rx) * -0.8660), calc(var(--ry) *  0.5000)); }
  45.8333%  { transform: translate(calc(var(--rx) * -0.9659), calc(var(--ry) *  0.2588)); }
  50%       { transform: translate(calc(var(--rx) * -1.0000), calc(var(--ry) *  0.0000)); }
  54.1667%  { transform: translate(calc(var(--rx) * -0.9659), calc(var(--ry) * -0.2588)); }
  58.3333%  { transform: translate(calc(var(--rx) * -0.8660), calc(var(--ry) * -0.5000)); }
  62.5%     { transform: translate(calc(var(--rx) * -0.7071), calc(var(--ry) * -0.7071)); }
  66.6667%  { transform: translate(calc(var(--rx) * -0.5000), calc(var(--ry) * -0.8660)); }
  70.8333%  { transform: translate(calc(var(--rx) * -0.2588), calc(var(--ry) * -0.9659)); }
  75%       { transform: translate(calc(var(--rx) *  0.0000), calc(var(--ry) * -1.0000)); }
  79.1667%  { transform: translate(calc(var(--rx) *  0.2588), calc(var(--ry) * -0.9659)); }
  83.3333%  { transform: translate(calc(var(--rx) *  0.5000), calc(var(--ry) * -0.8660)); }
  87.5%     { transform: translate(calc(var(--rx) *  0.7071), calc(var(--ry) * -0.7071)); }
  91.6667%  { transform: translate(calc(var(--rx) *  0.8660), calc(var(--ry) * -0.5000)); }
  95.8333%  { transform: translate(calc(var(--rx) *  0.9659), calc(var(--ry) * -0.2588)); }
  100%      { transform: translate(calc(var(--rx) *  1.0000), calc(var(--ry) *  0.0000)); }
}

/* The planet button sits at the anchor's (0,0). It's translated up by
   half the dot's height (inline-style) so the dot's centre lies exactly
   on the orbit ellipse path. */
.eco-solar__planet {
  position: absolute;
  top: 0;
  left: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 4px 8px;
  background: transparent;
  border: 0;
  color: var(--ink);
  cursor: pointer;
  transform-style: preserve-3d;
  z-index: 4;
  transition: opacity 0.25s ease;
}
.eco-solar__planet.is-current { cursor: default; }
/* Billboard : wrapper qui contre-rotate la rotation du plane (trackball)
   pour que la sphère interne reste toujours orientée vers la caméra. Sans
   ça, le radial-gradient (qui est 2D) se vrille et la "sphère" redevient
   visiblement plate quand on tilte la galaxie. */
.eco-solar__planet__billboard {
  display: inline-block;
  transform-style: preserve-3d;
  line-height: 0;
}

/* Planètes en vraies sphères CSS : superposition de plusieurs radial-gradients
   pour le volume (lumière diffuse, terminator, ombre propre, spéculaire) +
   inset shadows pour l'ambient occlusion + glow extérieur. */
.eco-solar__planet__dot {
  display: block;
  border-radius: 50%;
  background:
    /* Highlight spéculaire (petit point très clair en haut-gauche) */
    radial-gradient(circle at 28% 22%, rgba(255, 255, 255, 0.85) 0%, rgba(255, 255, 255, 0) 9%),
    /* Lumière diffuse principale (terminator marqué) */
    radial-gradient(circle at 32% 28%, #d8c878 0%, #8a7a20 38%, #2a2410 76%, #0a0805 100%);
  box-shadow:
    /* Ombre propre : un croissant sombre en bas-droite (face non éclairée) */
    inset -6px -8px 12px -4px rgba(0, 0, 0, 0.85),
    /* Léger rim light sur le bord opposé (rebond ambiant) */
    inset 4px 4px 6px -2px rgba(255, 220, 120, 0.18),
    /* Halo de la planète + ombre portée subtile */
    0 0 16px rgba(246, 226, 28, 0.28),
    0 4px 10px rgba(0, 0, 0, 0.4);
  transition: transform 0.3s cubic-bezier(0.2, 1.4, 0.4, 1), box-shadow 0.3s ease;
}
.eco-solar__planet.is-current .eco-solar__planet__dot {
  background:
    radial-gradient(circle at 28% 22%, rgba(255, 255, 255, 0.95) 0%, rgba(255, 255, 255, 0) 10%),
    radial-gradient(circle at 32% 28%, #fff4b8 0%, var(--yellow) 36%, #8a6d00 78%, #2a2200 100%);
  box-shadow:
    inset -6px -8px 14px -4px rgba(40, 30, 0, 0.85),
    inset 4px 4px 7px -2px rgba(255, 240, 200, 0.45),
    0 0 26px rgba(246, 226, 28, 0.7),
    0 4px 12px rgba(0, 0, 0, 0.4);
}
.eco-solar__planet:hover .eco-solar__planet__dot,
.eco-solar__planet.is-hover .eco-solar__planet__dot {
  transform: scale(1.4);
  box-shadow:
    inset -6px -8px 14px -4px rgba(0, 0, 0, 0.85),
    inset 4px 4px 7px -2px rgba(255, 230, 140, 0.3),
    0 0 32px rgba(246, 226, 28, 0.7),
    0 6px 14px rgba(0, 0, 0, 0.45);
}
.eco-solar__planet__label {
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink);
  white-space: nowrap;
  text-shadow: 0 0 8px rgba(0, 0, 0, 0.6);
}

/* Only pause the anchor (= orbit) of the planet currently hovered —
   the other planets keep orbiting. */
.eco-solar__anchor:has(.eco-solar__planet:hover) {
  animation-play-state: paused;
}

/* Dim non-hovered planets when one is hovered (spotlight) */
.eco-solar:has(.eco-solar__planet.is-hover) .eco-solar__planet:not(.is-hover) {
  opacity: 0.4;
}

/* Bottom readout — legacy (kept for safety, hidden when card is used) */
.eco-solar__readout { display: none; }

/* Overlay au hover d'une planète — wrap qui suit le curseur (left/top via JS).
   Contient : la fenêtre terminal + le bloc texte EN DEHORS du terminal. */
.eco-solar__overlay {
  position: fixed;
  z-index: 6;
  width: 360px;
  pointer-events: none;
  display: flex;
  flex-direction: column;
  gap: 10px;
  animation: eco-term-in 0.18s cubic-bezier(0.2, 0.7, 0.2, 1) both;
}
@keyframes eco-term-in {
  from { opacity: 0; transform: translateY(-6px) scale(0.98); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}

/* Fenêtre terminal — exactement comme .menubar__term (bar + body) */
.eco-solar__term {
  background: var(--bg);
  border: 1px solid var(--rule);
  box-shadow: 0 12px 30px rgba(0, 0, 0, 0.6),
              0 0 0 1px rgba(246, 226, 28, 0.04) inset;
}
.eco-solar__term__bar {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 7px 10px;
  background: var(--bg);
  border-bottom: 1px solid var(--rule);
  color: var(--ink-dim);
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: lowercase;
}
.eco-solar__term__dots {
  display: inline-flex;
  align-items: center;
  gap: 5px;
}
.eco-solar__term__dots .d {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  display: inline-block;
}
.eco-solar__term__dots .d--y { background: #ffbd2e; }
.eco-solar__term__title {
  flex: 1;
  text-align: center;
  font-family: var(--font-mono);
}
.eco-solar__term__x {
  color: var(--ink-dim);
  font-size: 14px;
  line-height: 1;
  padding: 0 4px;
}
.eco-solar__term__shot {
  width: 100%;
  aspect-ratio: 16 / 10;
  background: #000;
  overflow: hidden;
}
.eco-solar__term__shot img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Texte EN DEHORS du terminal — sous la fenêtre */
.eco-solar__info {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 0 2px;
}
.eco-solar__info__kicker {
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--yellow, #f6e21c);
  margin: 0;
}
.eco-solar__info__desc {
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 400;
  line-height: 1.55;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ink);
  margin: 0;
  text-shadow: 0 1px 6px rgba(0, 0, 0, 0.85);
}
/* Caret = exactement le même que sur la page About (terminal block) */
.eco-solar__info__desc .caret {
  display: inline-block;
  width: 1ch;
  margin-left: 1px;
  color: var(--ink);
  font-family: var(--font-mono);
  animation: caret-blink 1s steps(1) infinite;
}
.eco-solar__info__cta {
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-dim);
  margin: 4px 0 0;
}

/* On smaller screens, tighten the solar layout */
@media (max-width: 760px) {
  .eco-solar__sun .matrix-griot { font-size: 3px; }
  .eco-solar__planet__label { font-size: 9px; letter-spacing: 0.14em; }
  .eco-solar__overlay {
    /* Mobile : ignore le tracking curseur, repositionne en bas plein largeur */
    top: auto !important;
    right: 16px !important;
    left: 16px !important;
    bottom: 32px !important;
    width: auto;
  }
  .eco-solar__info__desc { font-size: 11px; }
}

.eco3 {
  position: fixed;
  inset: 0;
  z-index: 5;
  background: #050505;
  display: block;
  color: var(--ink);
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
}

.eco3__split {
  position: absolute;
  inset: 0;
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  overflow: hidden;
}

.eco3__pane {
  position: relative;
  overflow: hidden;
  background: #050505;
  border: 0;
  padding: 0;
  cursor: pointer;
}
.eco3__pane > img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.eco3__pane--side > img {
  filter: grayscale(1) contrast(1.05) brightness(0.45);
  transition: filter 0.3s ease, transform 0.5s ease;
}
.eco3__pane--side:hover > img {
  filter: grayscale(0.4) contrast(1.1) brightness(0.7);
  transform: scale(1.03);
}
.eco3__pane--right > img { filter: grayscale(1) contrast(1.1) brightness(0.35); }
.eco3__pane--center { cursor: default; }
.eco3__pane--center > img {
  filter: contrast(1.04) brightness(0.6);
  animation: eco3-in 0.55s ease;
}
@keyframes eco3-in {
  from { opacity: 0.4; filter: blur(6px) brightness(0.3); }
  to   { opacity: 1; filter: contrast(1.04) brightness(0.6); }
}

.eco3__veil {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(ellipse at center, rgba(0,0,0,0) 30%, rgba(0,0,0,0.7) 100%),
    linear-gradient(180deg, rgba(0,0,0,0.35) 0%, rgba(0,0,0,0) 30%, rgba(0,0,0,0) 60%, rgba(0,0,0,0.75) 100%);
  pointer-events: none;
}

.eco3__side-label {
  position: absolute;
  left: 18px;
  bottom: 16px;
  color: var(--ink);
  font-size: 10px;
  letter-spacing: 0.22em;
  text-shadow: 0 1px 4px rgba(0,0,0,0.8);
}

/* Center card overlay */
.eco3__card {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: min(560px, 84%);
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  color: var(--ink);
  text-shadow: 0 1px 6px rgba(0,0,0,0.7);
}
.eco3__idx {
  font-size: 11px;
  letter-spacing: 0.22em;
  color: var(--ink-dim);
}
.eco3__kicker {
  font-size: 10px;
  letter-spacing: 0.28em;
  color: var(--ink-dim);
}
.eco3__name {
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: clamp(24px, 3.4vw, 46px);
  letter-spacing: 0.04em;
  margin: 4px 0 8px;
  color: var(--ink);
  line-height: 1;
}
.eco3__tagline {
  font-family: var(--font-mono);
  font-size: 12px;
  line-height: 1.55;
  letter-spacing: 0.06em;
  color: var(--ink);
  max-width: 460px;
}
.eco3__urlrow {
  margin-top: 18px;
  display: inline-flex;
  align-items: center;
  gap: 22px;
  font-size: 11px;
  letter-spacing: 0.2em;
}
.eco3__url { color: var(--ink-dim); }
.eco3__cta {
  color: var(--ink);
  background: none;
  border: 0;
  padding: 6px 14px;
  cursor: pointer;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  border: 1px solid var(--ink);
  transition: background 0.2s ease, color 0.2s ease, letter-spacing 0.25s ease;
}
.eco3__cta:hover:not(.is-disabled) {
  background: var(--ink);
  color: var(--bg);
  letter-spacing: 0.32em;
}
.eco3__cta.is-disabled {
  border-color: var(--ink-dim);
  color: var(--ink-dim);
  cursor: default;
}

/* Horizontal line through the middle */
.eco3__line {
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  height: 1px;
  background: rgba(138,122,32,0.32);
  z-index: 1;
}
.eco3__line__fill {
  position: absolute;
  left: 0;
  top: -1px;
  height: 3px;
  background: var(--ink);
  transition: width 0.35s ease;
}

/* Bottom meta strip */
.eco3__bottom {
  display: grid;
  grid-template-columns: 1.4fr 1fr auto 1.4fr;
  align-items: center;
  gap: 28px;
  padding: 0 28px;
  border-top: 1px solid rgba(138,122,32,0.18);
}
.eco3__meta {
  display: inline-flex;
  gap: 14px;
  align-items: baseline;
}
.eco3__meta--center { justify-self: start; }
.eco3__meta--right { justify-self: end; }
.eco3__meta__label { color: var(--ink-dim); }
.eco3__meta__value { color: var(--ink); }
.eco3__tcs {
  display: inline-flex;
  gap: 14px;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  justify-self: end;
}

@media (max-width: 900px) {
  .eco3__split { grid-template-columns: 1fr; }
  .eco3__pane--side { display: none; }
  .eco3__bottom { grid-template-columns: 1fr 1fr; gap: 8px; padding: 8px 14px; font-size: 9px; }
  .eco3__tcs, .eco3__meta--right { display: none; }
}


/* ============================================================
   Wordmark marquee — HXOUSE-style horizontal scroll
   ============================================================ */

.ahome__marquee {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 40;
  overflow: hidden;
  pointer-events: none;
  background: linear-gradient(180deg, rgba(5,5,5,0) 0%, rgba(5,5,5,0.8) 60%, rgba(5,5,5,1) 100%);
  padding: 18px 0 14px;
}
.ahome__marquee__track {
  display: inline-flex;
  gap: 60px;
  white-space: nowrap;
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: clamp(40px, 8vw, 110px);
  letter-spacing: -0.02em;
  color: var(--ink);
  text-transform: uppercase;
  line-height: 1;
  animation: ahome-marquee 28s linear infinite;
  will-change: transform;
}
.ahome__marquee__item {
  display: inline-flex;
  align-items: baseline;
}
.ahome__marquee__item .x {
  font-style: italic;
  font-family: var(--font-display);
  font-weight: 400;
  margin: 0 0.04em;
  color: var(--ink-dim);
}
.ahome__marquee__item sup {
  font-size: 0.28em;
  font-weight: 400;
  margin-left: 0.12em;
  vertical-align: top;
  color: var(--ink-dim);
}

/* Reverse + faster on hover to feel responsive */
.ahome:hover .ahome__marquee__track {
  animation-duration: 18s;
}

@keyframes ahome-marquee {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}

@media (max-width: 760px) {
  .ahome__marquee { display: none; }
}


/* ============================================================
   Matrix-style griot — background on the work page
   ============================================================ */

.matrix-griot {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  font-family: "JetBrains Mono", "Courier New", monospace;
  font-size: clamp(8px, 0.9vw, 14px);
  line-height: 1;
  letter-spacing: 0.02em;
  /* color initial fixé sur ink-dim (mustard très sombre) — l'animation
     matrix-pulse plus bas va respirer entre ink-dim et ink. */
  color: var(--ink-dim);
  white-space: pre;
  z-index: 1;
  pointer-events: none;
  user-select: none;
  text-shadow: 0 0 6px rgba(138, 122, 32, 0.15);
  animation: matrix-pulse 4.2s ease-in-out infinite;
}
/* Le griot respire entre mustard sombre (--ink #8a7a20) au pic et
   un mustard ULTRA sombre (#332c0d, plus sombre que --ink-dim) au creux.
   Range élargie pour un effet de respiration plus prononcé. */
@keyframes matrix-pulse {
  0%, 100% { color: #332c0d; }
  50%      { color: var(--ink); }
}


/* ============================================================
   Boot loader — Matrix griot reveal
   ============================================================ */

.boot-matrix {
  position: fixed;
  inset: 0;
  z-index: 9999;
  background: #050505;
  display: grid;
  place-items: center;
  transition: opacity 0.6s ease;
  /* Curseur main pointée pixel art mustard custom — même SVG que les
     éléments cliquables du reste du site (cf. règle a, button, etc.
     plus haut). Évite le pointer OS par défaut qui briserait la
     cohérence visuelle pendant le boot. */
  cursor: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' shape-rendering='crispEdges'><path d='M8 1 L11 1 L11 9 L13 9 L13 7 L15 7 L15 9 L17 9 L17 11 L19 11 L19 19 L7 19 L7 14 L5 14 L5 11 L8 11 Z' fill='black' stroke='%238a7a20' stroke-width='1' stroke-linejoin='miter'/></svg>") 8 1, pointer;
  overflow: hidden;
}
.boot-matrix.is-leaving { opacity: 0; }

/* Backdrop vidéo du boot — réplique EXACTE du hover de la page Work
   (.ahome__bg / .ahome__bg__video / .ahome__bg__grain / .ahome__bg__veil).
   Même filtre B&W + brightness 0.6, même grain animé, même veil radial. */
.boot-matrix__bg {
  position: absolute;
  inset: 0;
  z-index: 0;
  filter: grayscale(1) contrast(1.1) brightness(0.6);
  /* Zoom-in subtil 1.05 → 1 sur 4s pour mimer la transition hover Work
     (qui passe scale(1.05) → scale(1) en 1.2s). Sur la durée totale du
     boot (3s) on a un slow-mo cinéma léger. */
  transform: scale(1.05);
  animation: boot-zoom-in 4s ease-out forwards;
  pointer-events: none;
  overflow: hidden;
}
@keyframes boot-zoom-in {
  to { transform: scale(1); }
}
.boot-matrix__bg__video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: 0;
}
/* Film grain animé — SVG turbulence en screen blend (copié 1:1 du
   .ahome__bg__grain pour matcher exactement le look hover Work). */
.boot-matrix__bg__grain {
  position: absolute;
  inset: -10%;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='420' height='420'><filter id='g'><feTurbulence type='fractalNoise' baseFrequency='1.4' numOctaves='2' stitchTiles='stitch' seed='9'/><feColorMatrix values='0 0 0 0 1  0 0 0 0 1  0 0 0 0 1  0 0 0 1.1 0'/><feComponentTransfer><feFuncA type='linear' slope='1.6' intercept='-0.15'/></feComponentTransfer></filter><rect width='100%25' height='100%25' filter='url(%23g)'/></svg>");
  background-size: 520px 520px;
  mix-blend-mode: screen;
  opacity: 0.45;
  animation: film-grain 0.55s steps(1) infinite;
  z-index: 1;
}
.boot-matrix__bg__veil {
  position: absolute;
  inset: 0;
  z-index: 2;
  background: radial-gradient(
    ellipse at center,
    rgba(0, 0, 0, 0.35) 0%,
    rgba(0, 0, 0, 0.7) 100%
  );
}
/* Le matrix griot et le LOADING doivent rester au-dessus de la vidéo
   + veil. Le sticker est en position:fixed (déjà au-dessus du fond)
   mais son z-index doit aussi passer au-dessus du veil. */
.boot-matrix .matrix-griot,
.boot-matrix__loading {
  position: relative;
  z-index: 2;
}
.boot-matrix__sticker { z-index: 2; }

/* Override the matrix griot styling inside the boot — brighter & larger */
.boot-matrix .matrix-griot {
  position: static;
  transform: none;
  color: rgba(138, 122, 32, 0.85);
  text-shadow: 0 0 8px rgba(138, 122, 32, 0.35);
  font-size: clamp(6px, 0.55vw, 9px);
  line-height: 1;
  animation: matrix-pulse 1.6s ease-in-out infinite;
}

/* Mobile : le griot fait ~110 chars de large, donc en monospace à 6px ça
   déborde de l'écran. Padding latéral + font-size fluide pour qu'il tienne
   avec marge et reste lisible. */
@media (max-width: 760px) {
  .boot-matrix {
    padding: 0 16px;
  }
  .boot-matrix .matrix-griot {
    font-size: clamp(3.4px, 1.05vw, 6px);
  }
  /* LOADING sous le griot avec un peu d'air, sans le toucher */
  .boot-matrix__loading {
    bottom: auto;
    top: 72vh;
  }
}

.boot-matrix__caption {
  position: fixed;
  bottom: 30px;
  left: 0;
  right: 0;
  text-align: center;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-dim);
}
.boot-matrix__caption .dot {
  display: inline-block;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--ink);
  margin-right: 10px;
  vertical-align: 1px;
  animation: blink 1.1s steps(2) infinite;
}


/* Sticker mark inside the boot loader — bottom-left */
/* "LOADING…" caption under the griot on the boot screen */
.boot-matrix__loading {
  position: fixed;
  left: 50%;
  bottom: 14vh;
  transform: translateX(-50%);
  font-family: var(--font-mono);
  font-size: 13px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--ink);
  z-index: 2;
  pointer-events: none;
}
.boot-matrix__loading .dots,
.boot-matrix__loading__dots {
  display: inline-block;
  margin-left: 4px;
  letter-spacing: 0.12em;
}
.boot-matrix__loading .dots span,
.boot-matrix__loading__dots span {
  opacity: 0;
  animation: boot-dot 1.4s steps(1) infinite;
}
.boot-matrix__loading .dots span:nth-child(1),
.boot-matrix__loading__dots span:nth-child(1) { animation-delay: 0s; }
.boot-matrix__loading .dots span:nth-child(2),
.boot-matrix__loading__dots span:nth-child(2) { animation-delay: 0.35s; }
.boot-matrix__loading .dots span:nth-child(3),
.boot-matrix__loading__dots span:nth-child(3) { animation-delay: 0.7s; }
/* Prompt > devant LOADING, ton dim pour pas voler la vedette au mot */
.boot-matrix__loading__prompt {
  color: var(--ink-dim);
  opacity: 0.7;
}
/* Le placeholder = reste de "LOADING" pas encore tapé, invisible mais
   présent dans le layout → préserve la largeur totale du bloc pour
   éviter le shift horizontal pendant que LOADING s'écrit. */
.boot-matrix__loading__placeholder {
  visibility: hidden;
}
/* Caret block qui clignote derrière le texte typé — style terminal */
.boot-matrix__loading__caret {
  display: inline-block;
  width: 0.55em;
  color: var(--yellow);
  margin-left: 1px;
  vertical-align: baseline;
  animation: boot-caret-blink 0.9s steps(1) infinite;
}
@keyframes boot-caret-blink {
  0%, 49%   { opacity: 1; }
  50%, 100% { opacity: 0; }
}
@keyframes boot-dot {
  0%, 24%   { opacity: 0; }
  25%, 100% { opacity: 1; }
}

/* La déclaration de base de .boot-matrix__sticker est remontée tout en haut
   du fichier (juste sous .idcard), pour qu'elle soit déclarée AVANT les
   @media (max-width: 1200px) et @media (max-width: 760px) qui la passent
   à 180px. Sinon, à spécificité égale, la déclaration tardive l'emporte
   et le sticker reste à 280px même sur mobile.
   Voir bloc autour de la ligne 167. */


.boot-matrix__tagline {
  position: fixed;
  left: 0; right: 0;
  bottom: 64px;
  text-align: center;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink);
  opacity: 0.78;
}


/* Matrix griot on the about page — pinned bottom-right */
.about-min__griot {
  position: fixed;
  right: 24px;
  bottom: 70px;
  z-index: 30;
  pointer-events: none;
}
.about-min__griot .matrix-griot {
  position: static;
  transform: none;
  font-size: 5px;
  line-height: 1;
  color: rgba(138, 122, 32, 0.55);
  text-shadow: 0 0 5px rgba(138, 122, 32, 0.2);
  white-space: pre;
}
@media (max-width: 760px) {
  .about-min__griot {
    right: 12px;
    bottom: 60px;
    max-width: 60vw;
    overflow: hidden;
  }
  .about-min__griot .matrix-griot {
    font-size: 3px;
    line-height: 1;
  }
}


/* Hide the sticker easter egg on mobile */
@media (max-width: 760px) {
  .sticker-hotspot, .sticker-clear, .sticker-layer { display: none !important; }
}


/* ============================================================
   MOBILE HOME — vertical stack with overlay meta
   ============================================================ */

.hm { display: none; }

@media (max-width: 760px) {
  /* Mobile : layout style a.cre — 3 colonnes, chaque cell remplit
     100% de sa colonne, l'image dicte sa hauteur naturelle.
     PAS DE CROP : l'image est affichée en entier, son aspect natif
     détermine la hauteur de la cell. Plein color (pas de B&W). */
  .ahome__cell img,
  .ahome__cell video {
    filter: none !important;
    width: 100% !important;
    height: auto !important;
    object-fit: initial !important;
  }

  /* On mobile we reuse the desktop .ahome scatter grid, locked to a
     dense 3-column contact-sheet (style a.cre). FRAME and DATA modes
     are desktop-only — JS forces the state to "compact" on mobile.
     Same idea as the desktop S view: big vertical gap between rows,
     tight horizontal gap between columns. */
  .hm { display: none !important; }
  /* Padding-top plus généreux pour que les projets commencent sous le
     sticker + le menu, sans superposition au scroll initial. */
  .ahome { display: block !important; min-height: 100vh; padding: 160px 0 90px; }
  .ahome__grid,
  .ahome--large .ahome__grid {
    grid-template-columns: repeat(3, 1fr);
    /* Generous breathing room in both directions — cells stay small
       but the layout feels airy like the a.cre reference. */
    gap: 100px 30px;
    padding: 30px 16px 80px;
    max-width: none;
  }
  /* RÈGLE DURE : portrait_height ≤ landscape_width.
     /!\ NE PAS override aspect-ratio : on a besoin de l'aspect-ratio
     inline pour que le browser calcule la dimension manquante
     (ex : height fixée + aspect 2/3 → width = height × 2/3).
     Spécificité bumpée à (0,3,0) pour battre les desktop (0,2,0)+!important. */
  .ahome .ahome__cell.ahome__cell--landscape,
  .ahome--large .ahome__cell.ahome__cell--landscape {
    width: 100% !important;
    height: auto !important;
    justify-self: center;
  }
  .ahome .ahome__cell.ahome__cell--portrait,
  .ahome--large .ahome__cell.ahome__cell--portrait {
    width: auto !important;
    height: calc((100vw - 92px) / 3 * 1.0) !important;
    justify-self: center;
  }
  /* Reset sizeClass widths : neutralisé via unset. */
  .ahome .ahome__cell.ahome__cell--sm,
  .ahome .ahome__cell.ahome__cell--md,
  .ahome .ahome__cell.ahome__cell--lg,
  .ahome--large .ahome__cell.ahome__cell--sm,
  .ahome--large .ahome__cell.ahome__cell--md,
  .ahome--large .ahome__cell.ahome__cell--lg {
    width: unset;
    height: unset;
  }
  /* Image à l'intérieur des cells : remplit le cell (qui a maintenant
     ses dimensions définies par CSS + aspect-ratio inline). PAS height:
     auto qui causait un calcul circulaire avec parent width: auto. */
  .ahome .ahome__cell img,
  .ahome .ahome__cell video {
    width: 100% !important;
    height: 100% !important;
    object-fit: cover !important;
  }
  /* Ignore les positions grid 5-cols du JS et laisse la grille couler en
     3 cols. On GARDE le jitter pour un décalage volontairement irrégulier
     (style contact-sheet bricolé, pas grille rigide). */
  .ahome__cell {
    grid-column: auto !important;
    grid-row: auto !important;
  }
  /* Pas de rotation — juste le jitter translate(--jx, --jy) du JS qui
     décale chaque cellule de quelques pixels en X/Y. Les cellules restent
     droites mais ne sont jamais parfaitement alignées sur une grille rigide.
     Le flow de gauche à droite est assuré par la grille 3-cols ci-dessus. */
  /* Auto-rows sized by content. align-items: center centre chaque cell
     verticalement dans sa rangée — les projets ne sont JAMAIS alignés
     par le haut ni par le bas. Combiné avec les translateY irréguliers
     par nth-child plus bas pour varier davantage. */
  .ahome__grid,
  .ahome--large .ahome__grid {
    grid-auto-rows: auto;
    align-items: center;
    justify-items: stretch;
  }
  /* Effet "drift" — décalages verticaux SUBTILS par nth-child (cycle de
     5 pour éviter un pattern régulier sur 3-col grid). Les cells ne
     sont jamais alignées strictement au top/bottom mais l'œil perçoit
     toujours la grille en colonnes/rangées. Style contact-sheet
     bricolé. !important pour battre le transform inline React. */
  .ahome__cell:nth-child(5n+1) {
    transform: translate(var(--jx, 0), calc(var(--jy, 0px) - 8px)) !important;
  }
  .ahome__cell:nth-child(5n+2) {
    transform: translate(var(--jx, 0), calc(var(--jy, 0px) + 12px)) !important;
  }
  .ahome__cell:nth-child(5n+3) {
    transform: translate(var(--jx, 0), calc(var(--jy, 0px) - 5px)) !important;
  }
  .ahome__cell:nth-child(5n+4) {
    transform: translate(var(--jx, 0), calc(var(--jy, 0px) + 9px)) !important;
  }
  .ahome__cell:nth-child(5n+5) {
    transform: translate(var(--jx, 0), calc(var(--jy, 0px) - 14px)) !important;
  }
  /* The label sits below the cell — wraps if too long on narrow screens */
  .ahome__cell__label { white-space: normal; max-width: 100%; font-size: 9px; letter-spacing: 0.1em; }
  .ahome__cell__label__client { font-size: 8px; }
  /* Hide the desktop brand/size toggle visuals that don't fit phone width */
  .ahome__top { display: none; }
  /* On mobile there is only one view, so hide the whole toggle. */
  .ahome__size { display: none !important; }
  /* On mobile we keep [MENU] visible everywhere */
}

/* The old .hm stack styles below are kept for reference but inert
   since .hm display:none !important above hides everything. */

@media (max-width: 0px) {
  .hm {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    padding: 0;
    background: var(--bg);
    min-height: 100vh;
  }
  .hm__card {
    position: relative;
    width: 100%;
    margin: 0;
    padding: 0;
    border: 0;
    background: transparent;
    cursor: pointer;
    overflow: hidden;
    display: block;
  }
  .hm__card img,
  .hm__card video {
    display: block;
    width: 100%;
    height: auto;
    object-fit: cover;
    filter: grayscale(1) contrast(1.05) brightness(0.95);
    transition: filter 0.3s ease;
  }
  .hm__card:active img,
  .hm__card:active video {
    filter: grayscale(0.3) contrast(1.1);
  }
  /* Small camera icon top-left of cards that have video resources */
  .hm__cam {
    position: absolute;
    top: 8px;
    left: 8px;
    width: 22px;
    height: 22px;
    display: grid;
    place-items: center;
    background: rgba(0, 0, 0, 0.55);
    color: var(--ink);
    border-radius: 2px;
    pointer-events: none;
  }
  /* Brand mark sprinkled between project cards */
  .hm__brand {
    align-self: flex-start;
    padding: 20px 14px;
    font-family: var(--font-mono);
    font-size: 18px;
    letter-spacing: 0.02em;
    color: var(--ink);
    text-transform: lowercase;
  }
  .hm__brand sup {
    font-size: 8px;
    vertical-align: super;
    margin-left: 1px;
  }

  /* Top / bottom black "carton" bars — full-width strips around the image */
  .hm__bar {
    flex: 0 0 auto;
    background: #000;
    padding: 12px 14px;
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: 14px;
    color: var(--ink);
    font-family: var(--font-mono);
    font-size: 11px;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    text-align: left;
  }
  .hm__bar__l { display: flex; flex-direction: column; gap: 2px; }
  .hm__bar__r { display: flex; flex-direction: column; align-items: flex-end; gap: 2px; }
  .hm__brand { color: var(--ink); letter-spacing: 0.14em; font-size: 11px; }
  .hm__clk { color: var(--ink); font-size: 10px; letter-spacing: 0.14em; white-space: nowrap; }
  /* Meta table inside the bottom carton bar */
  .hm__meta {
    display: grid;
    grid-template-columns: auto auto;
    column-gap: 22px;
    row-gap: 3px;
    color: var(--ink);
    font-family: var(--font-mono);
    font-size: 11px;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    max-width: calc(100vw - 32px);
  }
  .hm__meta__lbl {
    color: var(--ink);
    opacity: 0.55;
    font-size: 10px;
    letter-spacing: 0.18em;
  }
  .hm__meta__val {
    color: var(--ink);
    font-size: 11px;
    letter-spacing: 0.1em;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 60vw;
  }
}


/* ============================================================
   MOBILE PROJECT VIEWER — stacked layout
   Breakpoint à 1200px (cf. note sur .viewer__projmeta plus haut) pour
   éviter que le rail droit chevauche le média sur petits écrans paysage.
   ============================================================ */

@media (max-width: 1200px) {
  /* 3-row grid:
       row 1 = empty top spacer (1fr)
       row 2 = main block (auto, contains image + controls) → centred in viewport
       row 3 = bottom area (1fr) containing the thumbnails rail,
               vertically centred between image bottom and page bottom. */
  .viewer {
    display: grid !important;
    grid-template-columns: 1fr !important;
    /* Pas de rangée HUD réservée : le HUD est en position:fixed, il
       overlay le viewport sans prendre de place dans le grid. Un seul
       1fr pour le main → centrage parfait du média dans tout le
       viewport. */
    grid-template-rows: 1fr;
    align-items: stretch;
    justify-items: stretch;
    height: 100vh;
    height: 100dvh;
    max-height: 100vh;
    max-height: 100dvh;
    overflow: hidden;
    /* Paddings haut/bas SYMÉTRIQUES (60px chacun) : le HUD (top) et la
       projmeta (bottom-right) sont fixed et flottent au-dessus, ils
       ne mangent pas le centrage. */
    padding: 60px 0 60px;
  }
  .viewer__main {
    /* grid-row: 1 : le grid n'a plus qu'une seule rangée (1fr) puisque
       le HUD est en position:fixed et n'a pas besoin de réservation.
       Sans ça, main allait dans une rangée implicite 2 (= auto height)
       et le stage perdait tout son centrage vertical. */
    grid-row: 1;
    grid-column: 1;
    width: 100% !important;
    max-width: 100% !important;
    margin: 0 !important;
    padding-top: 0 !important;
    padding-left: 0 !important;
    display: flex !important;
    flex-direction: column;
    min-height: 0;
  }
  .viewer__main {
    width: 100%;
    max-width: none;
    padding: 0;
  }
  .viewer__stage {
    width: 100vw;
    max-width: 100vw;
    aspect-ratio: auto;
    /* Stage = remplit la rangée 1fr du grid, mais ne dépasse pas. */
    height: 100%;
    max-height: 100%;
    min-height: 0;
    overflow: hidden;
    padding-top: 0;
    padding-bottom: 0;
    /* Stage en position:relative (déjà via base) pour servir d'ancre
       au media-wrap positionné en absolu (centrage par translate, voir
       plus bas). */
    position: relative !important;
  }
  /* Centrage GÉOMÉTRIQUE pile au milieu du viewport (top: 50dvh +
     translateY(-50%)). Pour que le média ne déborde JAMAIS sur le
     sticker (top-left 180px de haut) ni sur la projmeta (bottom ~60px),
     on cap son max-height à 2 × (50% - sticker_bottom) ≈ calc(100vh -
     380px). À cette taille, l'image centrée a son bord supérieur pile
     à la bordure inférieure du sticker, et son bord inférieur bien
     au-dessus de la projmeta — donc centrage visuel garanti sans
     overlap. */
  .viewer__media-wrap {
    position: fixed !important;
    top: 50dvh !important;
    bottom: auto !important;
    left: 50% !important;
    right: auto !important;
    transform: translate(-50%, -50%) !important;
    /* z-index 6 > .viewer__edge (4) → la barre de lecture en mobile
       reçoit bien ses clics même sur ses bords gauche/droite (qui
       chevauchent les tap zones prev/next). Bumped de 3 à 6. */
    z-index: 6;
    align-items: center;
    justify-content: center;
  }
  .viewer__stage img,
  .viewer__stage video {
    /* /!\ IMPORTANT : on utilise max-height (pas height fixe) pour que
       le box du <video> shrinke à la taille intrinsèque du contenu
       visible. Sinon, pour une vidéo paysage capée à max-width 92vw,
       le box garde 644px de haut tandis que la vidéo visible ne fait
       que ~194px → énorme vide noir AVANT que le bar (qui suit dans
       media-wrap) ne soit rendu. Là, le box colle au contenu = bar
       littéralement collé sous la vidéo, peu importe l'aspect ratio. */
    max-height: calc(100vh - 380px);
    max-width: 92vw;
    width: auto;
    height: auto;
    object-fit: contain;
    display: block;
    margin: 0 auto;
  }
  /* Rail thumbs PIN VERS LE BAS DU VIEWPORT (mais pas tout au bord).
     bottom: 40px → la rail flotte au-dessus du bord pour respirer.
     justify-content: center → quand le contenu tient dans la largeur
     du viewport, les thumbs sont centrées. Si ça déborde, l'overflow-x
     auto prend le relais et on peut scroller. */
  .viewer__rail {
    position: fixed !important;
    bottom: 40px !important;
    left: 0 !important;
    right: 0 !important;
    top: auto !important;
    transform: none !important;
    width: 100% !important;
    height: 80px !important;
    max-height: 80px !important;
    padding: 8px 12px !important;
    background: transparent;
    z-index: 70;
    overflow-x: auto;
    overflow-y: hidden;
    scroll-snap-type: x mandatory;
    justify-content: center !important;
  }
  /* Compteur déplacé au-dessus du rail (centré) au lieu d'être en colonne. */
  .viewer__rail__counter {
    position: absolute;
    bottom: 100%;
    left: 0;
    right: 0;
    text-align: center;
    align-self: auto;
    padding: 0 0 6px;
  }
  /* Rail = thumbnails row, lives in grid row 3 (bottom area) and is
     vertically centred between the image bottom and the page bottom.
     justify-content: center → thumbs centrées horizontalement quand le
     contenu tient ; scroll horizontal en fallback si débordement. */
  .viewer__rail {
    position: static;
    transform: none;
    grid-row: 3;
    align-self: center;
    display: flex;
    flex-direction: row;
    flex-wrap: nowrap;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 0 14px;
    margin-top: 0;
    overflow-x: auto;
    overflow-y: hidden;
    max-height: none;
    width: 100%;
    min-height: 0;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
  }
  /* On mobile every thumb is the same size — overrides desktop nth-child variations */
  .viewer__rail > .viewer__thumb,
  .viewer__rail > .viewer__thumb:nth-child(3n+1),
  .viewer__rail > .viewer__thumb:nth-child(3n+2),
  .viewer__rail > .viewer__thumb:nth-child(3n+3),
  .viewer__rail > .viewer__thumb:nth-child(5n),
  .viewer__rail > .viewer__thumb:nth-child(7n) {
    width: 84px;
    height: 56px;
    flex: 0 0 auto;
    scroll-snap-align: center;
  }

  /* Floating utility bar — fixed at bottom (CLOSE à gauche, INFO à droite,
     space-between du flex les répartit aux extrémités). Le bouton OVERVIEW
     est en dehors de la utilbar, positionné indépendamment au-dessus. */
  .utilbar {
    position: fixed;
    left: 0; right: 0; bottom: 0;
    gap: 10px;
    padding: 12px 14px;
    background: rgba(5, 5, 5, 0.85);
    backdrop-filter: blur(6px);
    border-top: 1px solid rgba(138,122,32,0.18);
    font-size: 10px;
  }

  /* Fullscreen toggle out of place on small screens — hide */
  .viewer__fs { right: 8px; bottom: 8px; width: 30px; height: 30px; }

  /* Infos overlay — full screen + 1 column on mobile */
  .infos__inner {
    padding: 80px 22px;
  }
  .infos__cols { grid-template-columns: 1fr; gap: 28px; }
  .infos__footer { padding-top: 18px; font-size: 10px; }
  /* Texte des credits sur mobile : plus petit que desktop mais quand
     même lisible (12px au lieu de 13px desktop). */
  .idx-term--info .idx-term__inner { font-size: 12px; line-height: 1.5; }
  .idx-term--info .idx-term__row--info {
    grid-template-columns: 28px minmax(80px, max-content) minmax(120px, 1fr);
    gap: 12px;
  }
}


/* ============================================================
   Viewer edge tap zones — left = previous, right = next resource
   ============================================================ */

.viewer__edge {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 18%;
  min-width: 60px;
  border: 0;
  background: transparent;
  cursor: pointer;
  z-index: 4;
  display: grid;
  place-items: center;
  color: var(--ink);
  opacity: 0;
  transition: opacity 0.25s ease, background 0.25s ease;
  pointer-events: auto;
}
/* Cursor SVG custom — fichiers SVG dédiés dans img/, mis en cache par le
   navigateur dès le 1er chargement (au lieu d'un data URI inline qui est
   re-parsé à chaque hover, ce qui créait des délais d'affichage).
   Hotspot au bout de la pointe. */
.viewer__edge--prev {
  left: 0;
  cursor: url("img/cursor-prev.svg") 4 14, w-resize;
}
.viewer__edge--next {
  right: 0;
  cursor: url("img/cursor-next.svg") 24 14, e-resize;
}
.viewer__stage:hover .viewer__edge { opacity: 0.75; }
.viewer__edge:hover { opacity: 1 !important; background: transparent; }
.viewer__edge--next:hover { background: transparent; }
/* .viewer__edge__arrow retiré — les flèches ‹ › ne sont plus dans le DOM,
   les zones de tap restent fonctionnelles mais invisibles. */

@media (max-width: 1200px) {
  /* Tap zones beaucoup plus larges sur mobile, en position:fixed full
     height pour étendre la surface tappable au-dessus et en dessous
     du média (où elles ne se font pas voler les clics par
     .viewer__media-wrap z-index 6).
     - left/right: 0
     - top: 60px (sous le sticker pour ne pas le bloquer)
     - bottom: 50px (au-dessus des triggers OVERVIEW / INFO)
     - width: 38% de chaque côté → 76% total en sides, 24% au centre
       pour le média (mais le média conserve play/pause via son z-index
       supérieur dans les overlaps).
     - z-index: 4 (en dessous du média 6, au-dessus du fond 0). */
  .viewer__edge {
    position: fixed;
    top: 0;
    bottom: 50px;
    width: 38%;
    min-width: 120px;
    opacity: 0.55;
    z-index: 4;
  }
  .viewer__edge--prev { left: 0; right: auto; }
  .viewer__edge--next { right: 0; left: auto; }
  /* La menubar (z-index 95) et le sticker (z-index 90) sont au-dessus,
     donc leurs clics restent intacts même si les edge zones s'étendent
     jusqu'au top: 0. */
  /* Override de .viewer--video .viewer__edge (ligne ~5021, hors @media)
     qui forçait width: 22% pour les projets vidéo. Sans ça, dès qu'un
     projet contient une vidéo (.viewer--video), nos 38% en fixed sont
     écrasés et les edge zones reviennent à 22% en absolute. */
  .viewer--video .viewer__edge {
    position: fixed;
    top: 0;
    bottom: 50px;
    width: 38%;
    min-width: 120px;
  }
}

/* Video project mode — zones de tap un peu plus larges sur desktop.
   On exclut explicitement mobile (≤1200px) parce que sur mobile on a
   un override @media plus haut qui pousse à 38% en position fixed
   pleine hauteur — sans ce gating, cette règle ré-écraserait celle
   du mobile par source order (même spécificité, dernier rule wins). */
@media (min-width: 1201px) {
  .viewer--video .viewer__edge { width: 22%; }
}


/* Real browser-fullscreen mode — the viewer element fills the screen.
   Hide the body chrome (yellow card, menubar, etc.) since the browser
   already takes us out of the page; black background; full-bleed image. */
.viewer--fs {
  background: #000;
  /* position fixed + 100dvh : indispensable pour le mode plein écran CSS
     sur iPhone (pas de Fullscreen API) — couvre tout le viewport, barres
     Safari comprises, et suit la rotation du téléphone. */
  position: fixed;
  inset: 0;
  z-index: 200;
  width: 100vw;
  height: 100vh;
  height: 100dvh;
  padding: 0 !important;
  display: flex !important;
  flex-direction: column;
  align-items: stretch;
  justify-content: center;
}
/* En plein écran mobile, on cache le HUD 4-coins et les triggers
   GALERIE / INFORMATION — seul le média + notre barre restent. */
.viewer--fs .viewer__hud,
.viewer--fs .viewer__overview-trigger,
.viewer--fs .viewer__info-trigger,
.viewer--fs .viewer__overview,
.viewer--fs .viewer__overview-backdrop {
  display: none !important;
}
.viewer--fs .viewer__main {
  width: 100vw !important;
  max-width: 100vw !important;
  margin: 0 !important;
  padding: 0 !important;
}
.viewer--fs .viewer__stage {
  width: 100vw;
  height: 100vh;
  max-height: 100vh;
  background: #000;
}
.viewer--fs .viewer__stage img,
.viewer--fs .viewer__stage video {
  width: 100%;
  height: 100%;
  max-height: 100vh;
  object-fit: contain;
  background: #000;
}
.viewer--fs .viewer__rail,
.viewer--fs .utilbar,
.viewer--fs .viewer__edge,
.viewer--fs .viewer__projmeta {
  /* En fullscreen, on cache le rail, l'utilbar, les edge tap zones
     (prev/next) et la projmeta. Le fullscreen est dédié à UNE vidéo
     unique sans gallery navigation — pour revenir au projet, sortir
     du fullscreen (Esc ou bouton FS dans le player bar). */
  display: none !important;
}
/* Le média vit dans .viewer__media-wrap (fit-content, max-width 80vw en
   mode normal pour coller la barre à la vidéo). En fullscreen ces
   contraintes doivent SAUTER, sinon la vidéo plafonne à 80vw / 100vh-340px
   et ne remplit pas l'écran (régression détectée le 13/07/2026). */
.viewer--fs .viewer__media-wrap {
  width: 100vw !important;
  height: 100vh !important;
  height: 100dvh !important;
  /* neutralise le cap mobile (max-height calc(100vh - 380px)) et le
     positionnement centre du mode normal */
  max-height: none !important;
  /* PAS de « top » ici. En dessous de 1200 px, le wrap est en
     position:fixed avec translate(-50%,-50%) : « top: 50dvh » l'y centre.
     Au-dessus, il reste en position:relative, et le meme « top » le
     decalait alors d'un demi-ecran vers le bas — la moitie basse de la
     video sortait de l'ecran. Le decalage est donc redonne plus bas,
     uniquement la ou le positionnement fixe existe. */
  top: auto !important;
  max-width: none !important;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}
.viewer--fs .viewer__media-wrap > img,
.viewer--fs .viewer__media-wrap > video {
  width: 100vw !important;
  height: 100vh !important;
  height: 100dvh !important;
  max-width: none !important;
  max-height: 100vh !important;
  max-height: 100dvh !important;
  object-fit: contain;
  background: #000;
}
/* Le centrage geometrique du wrap n'a de sens que la ou il est en
   position:fixed, c'est-a-dire sous 1200 px. */
@media (max-width: 1200px) {
  .viewer--fs .viewer__media-wrap { top: 50dvh !important; }
}
/* Embeds YouTube/Vimeo : le conteneur a des dimensions inline (80vw…) —
   on les écrase pour occuper tout l'écran, l'iframe suit en 100 %.
   :not() pour ne PAS toucher la barre de lecture ni le label, qui sont
   aussi des div enfants directs du media-wrap. */
.viewer--fs .viewer__media-wrap > div:not(.viewer__controls):not(.viewer__media-label) {
  width: 100vw !important;
  height: 100vh !important;
  max-height: 100vh !important;
  aspect-ratio: auto !important;
}
/* Label de resource au-dessus du média : hors sujet en plein écran. */
.viewer--fs .viewer__media-label { display: none !important; }

/* En fullscreen, la barre de lecture passe en position fixed bas écran
   pour rester accessible quel que soit le ratio du média plein écran */
.viewer--fs .viewer__controls--fixed {
  position: fixed !important;
  left: 0;
  right: 0;
  bottom: calc(18px + env(safe-area-inset-bottom, 0px));
  width: min(900px, calc(100vw - 48px)) !important;
  margin: 0 auto;
  z-index: 100;
}

/* ============================================================
   Viewer HUD — mobile-only 4-corner overlay (CHEMS-style layout)
   ============================================================ */
.viewer__hud { display: none; }

@media (max-width: 1200px) {
  .viewer__hud {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 50;
    pointer-events: none;
    font-family: var(--font-mono);
    color: var(--ink);
    text-transform: uppercase;
    letter-spacing: 0.1em;
  }
  .viewer__hud > * { pointer-events: auto; }

  .viewer__hud__tl,
  .viewer__hud__tr {
    position: absolute;
    padding: 14px 16px;
    display: flex;
    flex-direction: column;
    gap: 2px;
    font-size: 10px;
    line-height: 1.4;
    pointer-events: auto;
  }
  /* HUD top-left masqué : ses 3 entrées ([ LESGRIOTSXSTUDIO ] brand,
     [ INFORMATION ] et [ BACK ]) sont déjà toutes assumées ailleurs
     dans l'UI mobile :
     - sticker idcard top-left = brand + retour home (click)
     - viewer__info-trigger bottom-right = bouton INFORMATION
     Le HUD __tl apparaissait en transparence avant que le sticker ne
     se reveal (body.booted), ce qui créait un flash de chrome dupliqué
     sous le sticker pendant le chargement. */
  .viewer__hud__tl { display: none; }
  .viewer__hud__tr { top: 0; right: 0; align-items: flex-end; flex-direction: row; gap: 10px; }

  .viewer__hud__brand {
    font-size: 10px;
    color: var(--ink);
    letter-spacing: 0.12em;
    margin-bottom: 4px;
  }
  .viewer__hud__link {
    color: var(--ink);
    padding: 0;
    background: transparent;
    border: 0;
    font: inherit;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    cursor: pointer;
    text-align: left;
  }
  .viewer__hud__code {
    color: var(--ink);
    font-size: 10px;
    letter-spacing: 0.1em;
  }
  .viewer__hud__fs {
    color: var(--ink);
    background: transparent;
    border: 0;
    font-family: var(--font-mono);
    font-size: 11px;
    letter-spacing: 0.08em;
    cursor: pointer;
    padding: 0;
  }

  /* Sur mobile, l'utilbar desktop est cachée — remplacée par le HUD 4-coins. */
  .utilbar { display: none !important; }
}

/* Mobile : la barre est désormais directement enfant du media-wrap
   (cf. règle .viewer__media-wrap plus haut), donc elle vit ANATOMIQUEMENT
   sous la vidéo, à la largeur exacte du média. On désactive les styles
   "footer flottant" (background sombre, blur, padding) hérités de
   .viewer--video .viewer__controls qui sont prévus pour le mode
   fullscreen desktop et n'ont rien à faire ici.

   /!\ CRITIQUE : sur iOS Safari (et autres browsers sans Fullscreen API
   sur les <div>), un clic sur le bouton FULL tombe sur un fallback
   CSS-only qui ajoute quand même la classe .viewer--fs sur le viewer.
   Sans neutralisation explicite, les règles .viewer--fs et .viewer--video
   (positionnement bar en position:fixed bottom:24px, aspect-ratio 16/9
   sur le stage, etc.) s'activent et BRISENT le layout mobile. On les
   override toutes ici pour rester sur le layout mobile natif quelle que
   soit la valeur du state fullscreen. */
@media (max-width: 1200px) {
  .viewer__controls--fixed,
  .viewer--video .viewer__controls,
  .viewer--video .viewer__controls--fixed,
  .viewer--fs .viewer__controls,
  .viewer--fs .viewer__controls--fixed {
    background: transparent !important;
    backdrop-filter: none !important;
    padding: 0 !important;
    margin: 0 !important;
    position: relative !important;
    bottom: auto !important;
    left: auto !important;
    right: auto !important;
    width: auto !important;
    z-index: auto !important;
  }
  /* Stage : on annule le aspect-ratio et le fond noir de .viewer--video
     pour qu'il reste fluide comme en mode non-fullscreen. */
  .viewer--video .viewer__stage,
  .viewer--fs .viewer__stage {
    aspect-ratio: auto !important;
    background: transparent !important;
    height: 100% !important;
    max-height: 100% !important;
  }
  /* Médias : max-height + max-width (PAS height fixe), pour que le box
     du média shrinke à la taille intrinsèque du contenu. Le bar (dans
     media-wrap) suit immédiatement, sans vide entre eux. Cf. note sur
     .viewer__stage img/video plus haut pour le détail. */
  .viewer--video .viewer__stage img,
  .viewer--video .viewer__stage video,
  .viewer--fs .viewer__stage img,
  .viewer--fs .viewer__stage video {
    width: auto !important;
    height: auto !important;
    max-width: 92vw !important;
    max-height: calc(100vh - 380px) !important;
    object-fit: contain !important;
  }
  .viewer--video .viewer__main { padding: 0; }
  .viewer__main { gap: 0 !important; }
  .viewer__stage { margin-bottom: 0; }
}


/* Griot cliquable : déclenche le son de kora + binarize les textes
   de la page pendant 1.6s. Curseur custom mustard pointing-finger
   (qui hérite de la règle globale grâce au sélecteur .is-clickable). */
.matrix-griot {
  pointer-events: auto;
  user-select: none;
  cursor: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' shape-rendering='crispEdges'><path d='M8 1 L11 1 L11 9 L13 9 L13 7 L15 7 L15 9 L17 9 L17 11 L19 11 L19 19 L7 19 L7 14 L5 14 L5 11 L8 11 Z' fill='black' stroke='%238a7a20' stroke-width='1' stroke-linejoin='miter'/></svg>") 8 1, pointer;
}
.matrix-griot:hover { filter: brightness(1.3); }


/* ============================================================
   MOBILE — defilement doux & satisfaisant
   ============================================================
   Plusieurs techniques combinées pour un scroll qui se sent
   premium sur iPhone/Android :
   - momentum iOS (continue à glisser après le release du doigt)
   - overscroll-behavior contain (pas de bounce qui remonte le body
     quand on scrolle dans un panel interne)
   - overscroll-behavior-y none sur le body (pas de pull-to-refresh
     intempestif qui fait clignoter)
   - scroll-behavior: smooth pour les anchors/programmatic scrolls
   - hide scrollbar (look plus propre, pas de bande qui apparait)
   - GPU compositing sur les zones qui scrollent (will-change: transform)
   - Optimisation des transitions filter/backdrop qui peuvent jank
   ============================================================ */
@media (max-width: 760px), (orientation: landscape) and (max-height: 500px) {
  html {
    scroll-behavior: smooth;
  }
  html, body {
    -webkit-overflow-scrolling: touch;  /* momentum iOS legacy */
    overscroll-behavior-y: none;        /* pas de pull-to-refresh */
  }
  /* Containers qui scrollent en interne (galerie overview du viewer,
     info credits, etc.) : momentum + contain pour ne pas faire scroller
     le body quand on arrive en bout de container. */
  .viewer__overview,
  .viewer__info,
  .ahome {
    -webkit-overflow-scrolling: touch;
    overscroll-behavior: contain;
    scroll-behavior: smooth;
  }
  /* Hide scrollbar sur les containers (visuel plus propre, le scroll
     reste fonctionnel). */
  body::-webkit-scrollbar,
  .ahome::-webkit-scrollbar,
  .viewer__overview::-webkit-scrollbar,
  .viewer__info::-webkit-scrollbar { display: none; }
  body, .ahome, .viewer__overview, .viewer__info {
    scrollbar-width: none;             /* Firefox mobile */
    -ms-overflow-style: none;          /* IE/Edge legacy */
  }
  /* GPU hint : les cellules de la grille bénéficient de la compositing
     accélérée pendant le scroll, ce qui élimine les micro-jank. */
  .ahome__cell {
    -webkit-transform: translateZ(0);
    transform: translateZ(0);
  }
}

/* ============================================================
   LANDSCAPE MOBILE — téléphone tenu en mode paysage
   ============================================================
   Cible : téléphones en paysage (max-height: 500px évite de
   matcher iPad mini ~768px de haut).

   Décision UX : on garde le MÊME layout qu'en mobile portrait
   (sticker, menu, grille, chrome — tout identique) pour la
   cohérence visuelle. La SEULE chose qui change : le viewer
   exploite la hauteur disponible pour rendre les médias plus
   visibles (vidéos et photos plus grandes quand l'utilisateur
   tourne son téléphone — c'est précisément pour ça qu'il tourne). */
@media (orientation: landscape) and (max-height: 500px) {

  /* En landscape mobile :
     - le compteur "01 / 10" est posé EN DESSOUS DU PLAYER (juste sous le
       bas du média) via une position absolute calée sur la hauteur média.
     - le reste de la caption (nom + client + rôle) reste ancré en bas du
       viewport. L'espace flotte entre les deux. */
  .viewer__projmeta {
    top: auto !important;
    bottom: 6px !important;
    transform: none !important;
  }
  .viewer__projmeta__counter {
    position: absolute !important;
    top: calc(100dvh - 84px) !important;
    left: 0 !important;
    right: 0 !important;
    bottom: auto !important;
    margin: 0 !important;
    text-align: center;
    z-index: 10;
    pointer-events: none;
  }
  /* La galerie (rail) garde le même comportement qu'en mobile portrait
     (masquée). Pas d'override spécifique au landscape. */

  /* Viewer paysage : on override TOUTES les contraintes max-height
     qui s'empilaient en mobile portrait :
     - .viewer--video .viewer__stage : calc(100vh - 220px) → 180px en paysage
     - .viewer--video .viewer__stage img/video : idem
     - .viewer__stage img/video (mobile @760) : calc(100vh - 380px) → 20px (!)
     Tous remplacés par calc(100dvh - 90px) pour laisser pile la place
     en bas à projmeta (50px) + bouttons (10px) + un peu d'air. */
  .viewer__media-wrap,
  .viewer__stage,
  .viewer__stage img,
  .viewer__stage video,
  .viewer--video .viewer__stage,
  .viewer--video .viewer__stage img,
  .viewer--video .viewer__stage video {
    max-height: calc(100dvh - 140px) !important;
    max-width: 100vw !important;
  }
  /* Sticker un poil plus discret. 130px au lieu de 180px portrait. */
  .idcard { width: 130px; }

  /* Projmeta : on laisse le JS poser le top inline (captionY = juste
     sous le média avec +8px d'air en paysage, cf. viewer.jsx). Le bloc
     suit le bas de la vidéo, peu importe sa taille. */
  .viewer__projmeta {
    line-height: 1.35;
  }
  /* Boutons GALLERY (gauche) + INFORMATION (droite) collés tout en bas
     du viewport, niveau dédié séparé du projmeta. */
  .viewer__overview-trigger,
  .viewer__info-trigger {
    bottom: 10px !important;
  }
}

/* ─── Page TALENT ─────────────────────────────────────────────────────
   Réutilise la grammaire visuelle de la page Work :
   - Wrapper .ahome (fond noir + ASCII griot bottom-right via .ahome__griot)
   - Cellule unique stylée comme .ahome__cell (border jaune au hover,
     vidéo qui joue au survol, label typewriter en bas)
   La variant .ahome--talent désactive le scatter grid au profit d'un
   simple flex 2 colonnes : cellule grand format + bio terminal à côté.
   ────────────────────────────────────────────────────────────────── */
.ahome--talent {
  /* Pas de grid scattered, juste un layout deux colonnes propre */
  position: relative;
}
.ahome__grid--talent {
  /* Override le grid auto du scatter : photo (auto) à gauche, bio (1fr)
     à droite. La photo est COLLÉE au bord gauche du viewport — on aligne
     juste sur le padding 7vw standard, pas de max-width ni de centering
     pour qu'elle reste à gauche sur tous les écrans, y compris >1600px.
     Padding-top élevé (180px) → la photo passe verticalement en dessous
     du sticker .idcard (qui descend à ~140-150px), donc PAS BESOIN de
     padding-left géant : sticker et photo se croisent sans se chevaucher,
     l'un en haut, l'autre en bas. */
  display: grid !important;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 5vw;
  padding: 180px 7vw 70px;
  max-width: none;
  margin: 0;
  align-items: start;  /* la bio commence en haut, alignée sur la cellule */
}

/* Cellule single — REPRODUCTION EXACTE d'une miniature portrait en
   mode LARGE desktop (cf .ahome--large .ahome__cell--portrait.ahome__cell--lg).
   On force ici les dimensions car la cellule n'est plus dans le scatter
   grid qui calcule normalement size/position via useScatter. */
.ahome__cell--single {
  /* Annule les transforms scatter */
  transform: none !important;
  grid-column: auto !important;
  grid-row: auto !important;
  /* Format portrait — même proportions que les vignettes Work portrait */
  aspect-ratio: 3 / 4.2;
  /* Même taille qu'une cellule .ahome--large .ahome__cell--portrait.ahome__cell--lg :
     420px de haut → ~300px de large. Taille LARGE pour donner de la présence
     au portrait du talent unique. */
  height: 420px;
  width: auto;
  flex-shrink: 0;
  cursor: pointer;
  position: relative;
  overflow: hidden;
  background: #000;
}
/* Le rest (MatrixGriot / image) prend toute la cellule.
   Au repos : la photo est en N&B (filter grayscale 1) — cohérent avec
   la grille Work où le backdrop est aussi N&B.
   Au hover : filter retiré → la photo passe en couleur, le sujet
   (Moos) reste accroché au regard pendant que la vidéo indigo-cristal
   joue en grand format derrière toute la page. */
.ahome__cell--single .ahome__cell__rest {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  filter: grayscale(1) contrast(1.05);
  transition: filter 0.5s ease;
  z-index: 1;
}
.ahome__cell--single .ahome__cell__rest > * {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.ahome__cell--single.is-hovered .ahome__cell__rest {
  filter: none;
}

/* ─── Bio (col droite) ────────────────────────────────────────────────
   Typo strictement identique à la page About : .about-min__intro p
   (var(--font-mono) · 13px · letter-spacing 0.08em · line-height 1.55 ·
   uppercase · color var(--ink)). On garde les prompts > en jaune brand
   pour le signal "terminal" mais le corps du texte est calé sur About.
   ───────────────────────────────────────────────────────────────── */
.talent-bio {
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  align-self: stretch;
  padding: 0;
  color: var(--ink);
}
.talent-bio__head {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink);
  font-weight: 400;
  margin-bottom: 22px;
}
.talent-bio__body {
  display: flex;
  flex-direction: column;
  gap: 22px;
}
.talent-bio__body p,
.talent-bio__body .talent-bio__p {
  /* Bio prose 3e personne — typo strictement identique à .about-min__intro p
     (var(--font-mono) 13px 0.08em line-height 1.55 uppercase). On garde le
     max-width 62ch pour conserver une mesure de lecture confortable
     puisque le texte est de la prose longue.
     IMPORTANT : pas de coupure de mots automatique en fin de ligne
     (hyphens: manual + word-break: normal). On ne veut JAMAIS voir un
     mot scindé par un trait d'union — chaque mot reste entier, et si
     besoin il passe à la ligne suivante. */
  font-family: var(--font-mono);
  font-size: 13px;
  letter-spacing: 0.08em;
  line-height: 1.6;
  color: var(--ink);
  text-transform: uppercase;
  text-wrap: pretty;
  hyphens: manual;
  -webkit-hyphens: manual;
  word-break: normal;
  overflow-wrap: normal;
  margin: 0;
  max-width: 62ch;
}

/* Reserve-space typewriter : .ghost contient le texte complet en
   visibility: hidden pour réserver la place (le paragraphe a sa hauteur
   et son layout final dès le mount). .typed contient le <Type> en absolute
   par-dessus, qui s'écrit caractère par caractère. Le bloc ne bouge plus,
   seul le curseur clignotant se déplace dans cet espace réservé. */
.talent-bio__p {
  position: relative;
}
.talent-bio__ghost {
  visibility: hidden;
  display: block;
  /* Mêmes attributs typo que le <p> parent — hérités automatiquement,
     donc rien à override ici. */
}
.talent-bio__typed {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

/* Lien handle (@mooscoulibaly) — hérite de la typo du paragraphe,
   ajoute juste un soulignement subtil et un effet de couleur au hover
   (jaune brand). Cohérent avec le pattern .about-min__col a. */
.talent-bio__link {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
  transition: color 0.2s ease;
}
.talent-bio__link:hover {
  color: var(--yellow);
}

/* ─── Savoir-faire (compétences du talent) ─────────────────────────────
   Tags sous la bio. Même grammaire terminale : mono, uppercase, moutarde,
   filet 1px. Hover jaune comme les liens/handle. */
.talent-skills {
  margin-top: 34px;
  padding-top: 22px;
  border-top: 1px solid var(--rule);
}
.talent-skills__label {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink);
  opacity: 0.7;
  margin: 0 0 14px;
}
/* Ligne de compétences — rendu terminal (typewriter) identique à la bio :
   texte simple sans rectangle, séparé par des points médians, avec la
   technique ghost/typed pour réserver la hauteur et éviter tout saut. */
.talent-skills__line {
  position: relative;
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.08em;
  line-height: 1.6;
  text-transform: uppercase;
  color: var(--ink);
  max-width: 62ch;
  margin: 0;
}
.talent-skills__ghost {
  visibility: hidden;
  display: block;
}
.talent-skills__typed {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

/* ─── Mobile ───────────────────────────────────────────────────────────
   On force le wrapper à tenir dans 100vh (height + overflow hidden) pour
   qu'il n'y ait PAS DE SCROLL sur la page Talent en mobile, comme la page
   About. Conséquence : on dimensionne la photo en vh pour qu'elle s'adapte
   à la hauteur disponible et la bio prend le reste.
   ──────────────────────────────────────────────────────────────────── */
@media (max-width: 820px) {
  .ahome--talent {
    height: 100vh;
    min-height: 0;        /* override le min-height: 100vh de .ahome */
    overflow: hidden;     /* zéro scroll */
    padding: 0;           /* le padding est porté par .ahome__grid--talent */
  }
  .ahome__grid--talent {
    grid-template-columns: 1fr !important;
    /* Layout vertical : photo en haut (taille fixe), bio en bas (auto) */
    grid-template-rows: auto 1fr;
    gap: 22px;
    /* Padding top 120px → garantit que la photo passe en dessous du
       sticker mobile (.idcard ~95-100 px de haut + 10 px top = ~110 px).
       Sur les petits viewports (sticker 130 px de large = ~65 px de haut),
       120px reste safe. */
    padding: 120px 5vw 70px;
    height: 100vh;
    justify-items: start;   /* photo alignée à gauche en mobile */
    align-content: start;
  }
  .ahome__cell--single {
    /* Photo PETITE en mobile par défaut : ~32 % de la hauteur viewport
       → ~270 px sur iPhone 14 (852 px). Garde la place pour que la bio
       s'affiche entièrement sans scroll. Alignée à GAUCHE (margin 0).
       Transition height pour un toggle fluide vers le grand format. */
    height: 32vh;
    aspect-ratio: 3 / 4.2;
    margin: 0;
    transition: height 0.45s cubic-bezier(0.2, 0.7, 0.2, 1),
                width 0.45s cubic-bezier(0.2, 0.7, 0.2, 1),
                filter 0.5s ease;
  }
  /* TOGGLE GRAND FORMAT — au tap sur la photo en mobile :
     - La cellule prend toute la hauteur dispo (~75vh).
     - La bio sœur (aside.talent-bio) se masque (display: none).
     Re-tap → retour à l'état par défaut. */
  .ahome__cell--single.is-hovered {
    height: 75vh;
  }
  .ahome__cell--single.is-hovered ~ .talent-bio {
    display: none;
  }
  /* Cellule au repos : la photo doit bien remplir la cell — l'image
     est en object-fit cover, on ne change rien à ce niveau. */
  .talent-bio {
    padding: 0;
    width: 100%;
    /* La bio occupe ce qu'il reste — overflow hidden pour qu'aucun
       paragraphe ne fasse déborder le viewport. */
    overflow: hidden;
  }
  .talent-bio__body {
    gap: 14px;
  }
  .talent-bio__body p,
  .talent-bio__body .talent-bio__p {
    /* Typo un peu plus compacte sur mobile pour gagner de la place et
       que les 2 paragraphes tiennent sans déborder. */
    font-size: 11.5px;
    letter-spacing: 0.06em;
    line-height: 1.5;
    max-width: none;
  }
}


/* ============================================================
   PAGE COMPTE À REBOURS (pré-lancement) — même moule que About
   Le conteneur porte .about-min (layout/typo/griot hérités) + .lx-cd.
   Ici on ne style QUE le timer ; le reste vient de .about-min.
   ============================================================ */
.lx-cd__timer {
  display: flex;
  align-items: flex-start;
  gap: clamp(14px, 2.2vw, 30px);
  margin-top: 2px;
}
.lx-cd__unit {
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-width: 2.3em;
}
.lx-cd__num {
  font-family: var(--font-mono);
  font-weight: 400;
  font-size: clamp(30px, 4.4vw, 52px);
  line-height: 1;
  letter-spacing: -0.01em;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}
.lx-cd__lab {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-dim, #5a5018);
}
@media (max-width: 640px) {
  .lx-cd__timer { gap: 18px; }
  .lx-cd__unit { min-width: 2.6em; }
  .lx-cd__num { font-size: 34px; }
}

/* Switch FR/EN sur la page compte à rebours (menu du site absent ici). */
.lx-cd__lang {
  position: fixed;
  top: 24px;
  right: 28px;
  z-index: 40;
  display: flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.lx-cd__lang button {
  font: inherit;
  letter-spacing: inherit;
  color: var(--ink-dim, #5a5018);
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  transition: color 0.15s ease;
}
.lx-cd__lang button:hover { color: var(--ink); }
.lx-cd__lang button.is-active {
  color: var(--ink);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.lx-cd__lang__sep { color: var(--ink-dim, #5a5018); opacity: 0.6; }
@media (max-width: 640px) {
  .lx-cd__lang { top: 16px; right: 16px; }
}

/* Sticker (artwork jaune) sur la page compte à rebours — même look que le boot. */
.lx-cd__sticker {
  position: fixed;
  top: 14px;
  left: 14px;
  width: 280px;
  height: auto;
  z-index: 35;
  pointer-events: none;
  filter: drop-shadow(2px 4px 0 rgba(0, 0, 0, 0.5));
}
@media (max-width: 1200px) {
  .lx-cd__sticker { width: 180px; }
}
@media (max-width: 640px) {
  .lx-cd__sticker { width: 150px; top: 12px; left: 12px; }
}


/* ==========================================================================
   FILIGRANE — le mot-marque LES GRIOTS pleine forme, au fond du site.
   Le PNG sert de POCHOIR : la couleur vient de la CSS, pas de l'image, donc
   la forme est peinte en --ink, exactement le mustard du griot ASCII.
   Colle en haut, fixe, derriere absolument tout.
   ========================================================================== */
html { background-color: var(--bg); }
body { background-color: transparent; }
/* Les conteneurs de page peignent leur propre fond opaque, de la meme
   couleur : sans ca ils recouvrent le filigrane. Transparents, l'aspect ne
   change pas et le mot redevient visible. */
.ahome,
.about-min,
.talent-page,
.home,
.viewer { background-color: transparent; }

/* ::after et PAS ::before : ::before porte deja le grain de film. */
body::after {
  content: "";
  position: fixed;
  left: 0;
  right: 0;
  top: 0;
  bottom: auto;
  /* Le logo fait 2587 x 665 : a pleine largeur, sa hauteur vaut 25,7 % de
     la largeur de l'ecran. */
  height: 25.71vw;
  background-color: var(--ink);
  -webkit-mask-image: url("img/les-griots-logo.png");
  mask-image: url("img/les-griots-logo.png");
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: top center;
  mask-position: top center;
  -webkit-mask-size: 100% auto;
  mask-size: 100% auto;
  opacity: 0.13;
  pointer-events: none;
  z-index: -1;
}
/* Ecran de demarrage : pas de filigrane. (Page Ecosysteme : le filigrane revient, comme partout.) */
body.is-booting::after { display: none; }


/* ---- Mode INDEX : ligne d'en-tete des colonnes -----------------------
   Meme grille que les lignes de projet, donc les colonnes s'alignent. Elle
   ne reagit ni au survol ni au clic : c'est une legende, pas une entree. */
.idx-term__row--head {
  cursor: default;
  pointer-events: none;
  padding-bottom: 10px;
  margin-bottom: 6px;
  border-bottom: 1px solid rgba(138, 122, 32, 0.22);
}
.idx-term__row--head span,
.idx-term__row--head .c-name {
  color: rgba(138, 122, 32, 0.5);
  text-transform: uppercase;
  letter-spacing: 0.16em;
  font-size: 10px;
  font-weight: 500;
}
/* L'en-tete ne s'eteint pas quand on survole la liste. */
.idx-term__rows:hover .idx-term__row--head span,
.idx-term__rows:hover .idx-term__row--head .c-name {
  color: rgba(138, 122, 32, 0.5);
}


/* ---- Mode INDEX : liste compacte ---------------------------------------- */
.idx-term__row .c-meta {
  color: var(--ink-dim);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  text-align: right;
  white-space: nowrap;
}
.idx-term__rows:hover .idx-term__row:not(.is-active) .c-meta { color: rgba(138, 122, 32, 0.2); }
.idx-term__row.is-active .c-meta { color: var(--ink); }
/* Un filet fin separe les lignes : la liste se lit comme un depouillement. */
.idx-term__row:not(.idx-term__row--head) { border-bottom: 1px solid rgba(138, 122, 32, 0.12); }

/* En-tete : le libelle « Client · Date » se cale a droite comme sa colonne. */
.idx-term__row--head .c-meta { text-align: right; }

/* La nature du travail, dans la ligne. Meme graisse que le client et la date,
   pour que le nom du projet reste seul a porter la ligne. */
.idx-term__row .c-svc {
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

/* Planetes de l'ecosysteme : vraies spheres 3D dessinees en canvas (ecosysteme.jsx) —
   binaire, geodesique, fil de fer : pas de fond, pas de halo, juste le dessin. */
.eco-solar__planet__dot.eco-sphere,
.eco-solar__planet.is-current .eco-solar__planet__dot.eco-sphere,
.eco-solar__planet:hover .eco-solar__planet__dot.eco-sphere,
.eco-solar__planet.is-hover .eco-solar__planet__dot.eco-sphere {
  background: none !important;
  box-shadow: none !important;
  border-radius: 0 !important;
}
.eco-solar__planet:hover .eco-solar__planet__dot.eco-sphere,
.eco-solar__planet.is-hover .eco-solar__planet__dot.eco-sphere { transform: scale(1.18); }


/* Ecosystem : les curseurs du site (pixel noir, contour moutarde) partout —
   main ouverte pour faire tourner/deplacer, poing ferme pendant le geste,
   main pointee sur les planetes et les boutons. */
.eco-solar, .eco-solar__trackball__ball { cursor: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' shape-rendering='crispEdges'><path d='M5 6 L7 6 L7 10 L8 10 L8 3 L10 3 L10 10 L11 10 L11 2 L13 2 L13 10 L14 10 L14 3 L16 3 L16 11 L17 11 L17 8 L19 8 L19 17 L17 17 L17 20 L8 20 L8 18 L6 18 L6 15 L5 15 Z' fill='black' stroke='%238a7a20' stroke-width='1' stroke-linejoin='miter'/></svg>") 12 10, grab !important; }
.eco-solar.is-dragging, .eco-solar.is-dragging *, .eco-solar__trackball__ball:active { cursor: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' shape-rendering='crispEdges'><path d='M7 8 L9 8 L9 7 L11 7 L11 8 L12 8 L12 7 L14 7 L14 8 L15 8 L15 7 L17 7 L17 9 L19 9 L19 17 L17 17 L17 20 L8 20 L8 18 L6 18 L6 11 L7 11 Z' fill='black' stroke='%238a7a20' stroke-width='1' stroke-linejoin='miter'/></svg>") 12 12, grabbing !important; }
.eco-solar__planet, .eco-solar__planet *, .eco-solar__trackball__reset, .eco-solar__tilt__reset, .eco-solar__sun, .eco-solar__sun * { cursor: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' shape-rendering='crispEdges'><path d='M8 1 L11 1 L11 9 L13 9 L13 7 L15 7 L15 9 L17 9 L17 11 L19 11 L19 19 L7 19 L7 14 L5 14 L5 11 L8 11 Z' fill='black' stroke='%238a7a20' stroke-width='1' stroke-linejoin='miter'/></svg>") 8 1, pointer !important; }
/* (la planete du studio est cliquable elle aussi : retour a l'accueil du studio) */

/* Ecosystem : on fait tourner la galaxie en la glissant — la petite boule n'est plus utile */
.eco-solar__trackball { display: none !important; }
.eco-solar__planet__dot.eco-sphere { transition: none !important; will-change: transform; }

/* Voyage dans une planete : une fenetre ronde sur le site de destination, qui s'ouvre jusqu'a l'ecran */
.eco-voyage { position: fixed; inset: 0; z-index: 10000; pointer-events: all; overflow: hidden; background: #050505; }
.eco-voyage__img { position: absolute; inset: 0; background-size: cover; background-position: center top; filter: grayscale(.15) contrast(1.05); }
.eco-voyage__voile { position: absolute; inset: 0; background: radial-gradient(ellipse at center, rgba(0,0,0,.15) 0%, rgba(0,0,0,.6) 100%); }
.eco-voyage__dest { position: absolute; left: 50%; bottom: 9vh; transform: translateX(-50%); display: flex; flex-direction: column; align-items: center; gap: 10px; font-family: var(--font-mono); text-transform: uppercase; white-space: nowrap; }
.eco-voyage__nom { font-size: 13px; letter-spacing: .18em; color: var(--yellow, #f6e21c); text-shadow: 0 0 12px rgba(0,0,0,.8); min-height: 1.2em; }
.eco-voyage__url { font-size: 11px; letter-spacing: .16em; color: var(--ink, #8a7a20); text-shadow: 0 0 12px rgba(0,0,0,.8); min-height: 1.2em; }

/* Le ciel de l'ecosysteme : etoiles en spirale + astre errant (dessines en canvas) */
.eco-solar__ciel { position: absolute; inset: 0; width: 100%; height: 100%; z-index: 1; pointer-events: none; }

/* Les noms des planetes n'apparaissent qu'a l'approche (survol) */
.eco-solar__planet .eco-solar__planet__label { opacity: 0; transition: opacity .3s ease; }
.eco-solar__planet.is-near .eco-solar__planet__label,
.eco-solar__planet.is-hover .eco-solar__planet__label,
.eco-solar__planet:focus-visible .eco-solar__planet__label { opacity: 1; }

/* Clic sur le griot : le compteur des annees qui passent */
.eco-temps { position: absolute; left: 50%; bottom: 13vh; transform: translateX(-50%); z-index: 5; pointer-events: none; opacity: 0; transition: opacity .3s ease;
  font-family: var(--font-mono); font-size: 13px; letter-spacing: .22em; color: var(--yellow, #f6e21c); text-transform: uppercase; white-space: nowrap; }
.eco-solar__lune { position: absolute; inset: 0; width: 100%; height: 100%; z-index: 3; pointer-events: none; }

/* Ecosystem — carte au survol, minimaliste (barre d'adresse + film + nom) */
.eco-solar__planet .eco-solar__planet__label,
.eco-solar__planet.is-near .eco-solar__planet__label,
.eco-solar__planet.is-hover .eco-solar__planet__label { display: none; }
.eco-cartes { position: absolute; inset: 0; z-index: 6; pointer-events: none; }
.eco-carte { position: absolute; left: 0; top: 0; width: 220px; opacity: 0; transition: opacity .3s ease; will-change: transform; }
.eco-carte.on { opacity: 1; }
.eco-carte__fil { position: absolute; left: 0; top: 0; height: 1px; background: rgba(138,122,32,.55); transform-origin: 0 0; }
.eco-carte__fen { border: 1px solid rgba(138,122,32,.55); background: #050505; }
.eco-carte__bar { height: 22px; display: flex; align-items: center; padding: 0 9px; border-bottom: 1px solid rgba(138,122,32,.3);
  font-family: var(--font-mono); font-size: 10px; letter-spacing: .1em; color: var(--ink); white-space: nowrap; overflow: hidden; }
.eco-carte__film { aspect-ratio: 16 / 9; overflow: hidden; background: #0a0a0a; }
.eco-carte__film video, .eco-carte__film img { width: 100%; height: 100%; object-fit: cover; display: block; filter: grayscale(1) contrast(1.1) brightness(.85); }
.eco-carte__nom { padding-top: 9px; min-height: 1.3em; font-family: var(--font-mono); font-size: 10px; letter-spacing: .18em; text-transform: uppercase; color: var(--yellow, #f6e21c); white-space: nowrap; }
/* l'ancienne fiche qui suivait la souris n'existe plus */
.eco-solar__overlay { display: none !important; }

/* Menu : la ligne « BACK TO LES GRIOTS », detachee des autres, meme jaune que TRAINING */
.menubar__items .menubar__retour { margin-top: 2.2em; opacity: .85; }
.menubar__items .menubar__retour:hover { opacity: 1; }
.menubar__items .menubar__retour .prompt { opacity: 1 !important; }

/* Ecosystem sur telephone : neutre — pas de galaxie, seulement le griot ASCII qui scintille */
@media (max-width: 900px) {
  .eco-solar__ciel, .eco-solar__lune, .eco-cartes, .eco-temps { display: none !important; }
}

/* Ecosystem telephone — contenus reequilibres : intro plus pres du sticker, lignes separees
   par un filet et centrees, vignettes un peu plus grandes, le griot au centre pour finir */
@media (max-width: 900px) {
  .eco-mobile__hero { padding: 132px 22px 0; }
  .eco-mobile__hero__lead { font-size: 11px; line-height: 1.7; max-width: none; color: var(--ink); opacity: .8; }
  .eco-mobile__list { padding: 34px 22px 0; gap: 0; }
  .eco-mobile__row { grid-template-columns: 44vw 1fr; gap: 16px; align-items: center; padding: 20px 0; border-top: 1px solid rgba(138, 122, 32, .22); }
  .eco-mobile__row:last-child { border-bottom: 1px solid rgba(138, 122, 32, .22); }
  .eco-mobile__row__thumb { max-width: none; }
  .eco-mobile__row__name { font-size: 14px; letter-spacing: .12em; }
  .eco-mobile__row__text { padding-top: 0; gap: 4px; }
  .eco-mobile__row__cta, .eco-mobile__row__here { margin-top: 8px; }
  .eco-mobile__griot { align-self: center; margin-top: 44px; padding: 0 0 8px; }
  .eco-mobile__griot .matrix-griot { font-size: 3.6px; }
  body:has(.eco-mobile) .page-foot { margin: 34px 22px 0; }
}
@media (max-width: 900px) {
  /* le nom tient sur une ligne */
  .eco-mobile__row { grid-template-columns: 40vw 1fr; }
  .eco-mobile__row__name { font-size: 13px; letter-spacing: .08em; white-space: nowrap; word-break: normal; }
  /* le griot passe en fond : grand, centre, tres discret, il ne gene ni la lecture ni le doigt */
  .eco-mobile__griot { position: fixed; left: 50%; top: 56%; transform: translate(-50%, -50%); margin: 0; padding: 0; z-index: 0; opacity: .22; pointer-events: none; }
  .eco-mobile__griot .matrix-griot { font-size: 5.2px; text-shadow: none; }
  .eco-mobile__hero, .eco-mobile__list { position: relative; z-index: 1; }
  .eco-mobile__row__thumb { position: relative; z-index: 1; }
}
@media (max-width: 900px) {
  /* tout tient dans l'ecran : pas de scroll */
  body:has(.eco-mobile) { overflow: hidden !important; height: 100dvh; }
  .eco-mobile { height: 100dvh; min-height: 0; overflow: hidden; padding-bottom: 0; }
  .eco-mobile__hero { padding: 118px 22px 0; }
  .eco-mobile__hero__lead { font-size: 10.5px; line-height: 1.6; }
  .eco-mobile__list { padding-top: 20px; }
  .eco-mobile__row { grid-template-columns: 36vw 1fr; padding: 12px 0; }
  .eco-mobile__row__cta, .eco-mobile__row__here { margin-top: 5px; }
  body:has(.eco-mobile) .page-foot { position: fixed; left: 22px; right: 22px; bottom: 14px; margin: 0; z-index: 2; }
}
/* Ecosystem : le filigrane seulement sur telephone ; sur ordinateur, la galaxie occupe le fond */
@media (min-width: 901px) { body:has(.eco-solar)::after { display: none; } }
