/* ==========================================================================
   VOX AGENCY
   Direction : Tactical Telemetry (industrial-brutalist-ui)
   Accent    : #1AFF7D, teinte du logo (#7AF0A7) poussee a saturation max
   Contrainte: HTML/CSS/JS natifs, aucun build
   ========================================================================== */

/* --------------------------------------------------------------- 1. FONTS */

@font-face {
  font-family: 'Archivo Black';
  src: url('../assets/fonts/archivo-black-latin.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                 U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
                 U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Archivo Black';
  src: url('../assets/fonts/archivo-black-latin-ext.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF,
                 U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'JetBrains Mono';
  src: url('../assets/fonts/jetbrains-mono-latin.woff2') format('woff2');
  font-weight: 100 800;
  font-style: normal;
  font-display: swap;
}

/* --------------------------------------------------------------- 2. TOKENS */

:root {
  /* Substrat CRT desactive. Jamais de noir pur. */
  --bg:          #0A0A0A;
  --bg-raise:    #121212;
  --bg-sink:     #060606;
  --line:        #242424;
  --line-strong: #3A3A3A;

  /* Phosphore blanc */
  --fg:          #EAEAEA;
  --fg-mid:      #9A9A9A;
  --fg-dim:      #6B6B6B;

  /* Accent unique, verrouille sur toute la page */
  --accent:      #1AFF7D;
  --accent-sink: #0FD866;
  --on-accent:   #050505;

  --font-display: 'Archivo Black', 'Arial Black', system-ui, sans-serif;
  --font-mono:    'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, monospace;

  --gut:      clamp(1.25rem, 4vw, 3.5rem);
  --max:      1440px;
  --nav-h:    64px;
  --sec-pad:  clamp(5rem, 12vw, var(--t-sec));

  --ease: cubic-bezier(.2, .7, .2, 1);

  /* Rend --vhero lisible hors de .vhero : le bouton permanent est pose
     en fin de body, il n est donc pas descendant de la section. */
  timeline-scope: --vhero;

  /* ---- Plafonds reglables -------------------------------------------------
     Ces huit valeurs commandent la taille des grands blocs. Elles sont isolees
     ici pour se regler d un seul endroit, et un outil d atelier les reecrit
     automatiquement dans ce bloc. D ou deux regles : ne pas les deplacer
     ailleurs, et ne pas en faire des calc(). Une valeur simple, une seule
     occurrence.
     Chaque plafond est le terme HAUT d un clamp ; le plancher et la pente en
     vw restent dans la regle concernee, parce qu eux tiennent la tenue sur
     petit ecran et ne se reglent pas a l oeil. */
  --t-titre:    6.5rem;   /* plafond du titre du hero */
  --t-lede:     .97rem;   /* plafond de la phrase du hero */
  --t-lede-gap: 1.8rem;   /* ecart entre la phrase et les boutons */
  --t-metiers:  25rem;    /* largeur de la liste des metiers du hero */
  --t-carte:    360px;    /* largeur de la carte video du carrousel */
  --t-sec:      10rem;    /* plafond de la marge verticale des sections */
  --t-reals:    6rem;     /* idem, pour la seule section Realisations */
  --t-cellule:  400px;    /* hauteur mini de la cellule Realisation */
}

/* --------------------------------------------------------------- 3. RESET */

*, *::before, *::after { box-sizing: border-box; }

* {
  margin: 0;
  padding: 0;
  /* Rigidite mecanique : aucun angle arrondi sur tout le document. */
  border-radius: 0;
}

html {
  scroll-behavior: smooth;
  -webkit-text-size-adjust: 100%;
}
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

body {
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font-mono);
  font-size: 15px;
  line-height: 1.55;
  font-weight: 400;
  /* "hidden" sur un axe force l autre en "auto" : body devient alors un
     conteneur de defilement sans defilement, et les timelines de vue du
     hero deviennent inactives. "clip" coupe sans creer de conteneur. */
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
@supports (overflow-x: clip) { body { overflow-x: clip; } }

img, video, svg { display: block; max-width: 100%; height: auto; }
a { color: inherit; text-decoration: none; }
button, input, textarea, select { font: inherit; color: inherit; }
ul, ol { list-style: none; }

::selection { background: var(--accent); color: var(--on-accent); }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

/* ------------------------------------------------- 4. DEGRADATION ANALOGIQUE */

/* Balayage du faisceau CRT. Sous le contenu, non interactif. */
.crt-scan {
  position: fixed;
  inset: 0;
  z-index: 900;
  pointer-events: none;
  background: repeating-linear-gradient(
    0deg,
    transparent 0px,
    transparent 2px,
    rgba(0, 0, 0, .22) 2px,
    rgba(0, 0, 0, .22) 4px
  );
  mix-blend-mode: multiply;
  opacity: .55;
}

/* Grain physique unifie sur toute la racine du DOM. */
.crt-grain {
  position: fixed;
  inset: 0;
  z-index: 901;
  pointer-events: none;
  opacity: .05;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.82' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}

@media (prefers-reduced-motion: reduce) {
  .crt-scan { opacity: .3; }
}

/* --------------------------------------------------------- 5. TYPOGRAPHIE */

.macro {
  font-family: var(--font-display);
  text-transform: uppercase;
  letter-spacing: -.045em;
  line-height: .88;
  font-weight: 400;
}

/* Echelle fluide, clamp exclusivement sur la macro-typographie. */
/* Cale sur mesure navigateur : en Archivo Black capitales avec -.045em de
   tracking, la ligne la plus longue du hero occupe 10.11em. Les valeurs ci
   dessous gardent 5 pour cent de marge et garantissent 2 lignes maximum,
   de 320px a 1920px. Une seule echelle : le hero n a plus de video a cote
   du titre, il dispose de toute la largeur a toutes les tailles.
   Mesure du 02/11/2026, token video compris : ratio 10.880 (largeur de la
   ligne la plus longue / taille de police). Deux bornes en decoulent :
     320px        -> 280 / 10.880 = 25.7px, soit 8.04vw au maximum
     .wrap au max -> 1328 / 10.880 = 122.1px, soit 7.63rem au maximum
   On garde 4 pour cent de marge sur chacune : 7.7vw et 7.3rem.
   Le plancher est a 1.5rem et non 1.6 : a 1.6rem il valait 25.6px pour
   25.7px tenables a 320px, soit un dixieme de pixel de marge. */
/* Ratio 12.486 depuis que la ligne 1 est "Vos reseaux sociaux" (02/11/2026,
   mesure au Range, pas au scrollWidth). L ancien reglage 8.4vw / 7.6rem etait
   cale sur 10.11 et faisait deborder la ligne de 182px a 1440. */
.macro--xl { font-size: clamp(1.35rem, 6.8vw, var(--t-titre)); }
.macro--l  { font-size: clamp(2.2rem, 5.6vw, 5rem); }
.macro--m  { font-size: clamp(1.6rem, 3.2vw, 2.9rem); }
.macro--s  { font-size: clamp(1.15rem, 1.9vw, 1.6rem); letter-spacing: -.03em; }

/* Micro-typographie : metadonnees, navigation, unites. */
.mono {
  font-family: var(--font-mono);
  text-transform: uppercase;
  letter-spacing: .085em;
  font-size: .72rem;
  line-height: 1.35;
  color: var(--fg-mid);
}
.mono--accent { color: var(--accent); }
.mono--fg     { color: var(--fg); }

.eyebrow {
  font-family: var(--font-mono);
  text-transform: uppercase;
  letter-spacing: .2em;
  font-size: .69rem;
  font-weight: 700;
  color: var(--accent);
}

.lede {
  font-size: clamp(.95rem, 1.15vw, 1.075rem);
  line-height: 1.62;
  color: var(--fg-mid);
  max-width: 58ch;
}

.txt-accent { color: var(--accent); }

/* -------------------------------------------------------------- 6. LAYOUT */

.wrap {
  width: 100%;
  max-width: var(--max);
  margin-inline: auto;
  padding-inline: var(--gut);
}

.section { padding-block: var(--sec-pad); border-top: 1px solid var(--line); }

.section__head { margin-bottom: clamp(2.5rem, 5vw, 4.5rem); }
.section__head .lede { margin-top: 1.25rem; }
.section__head .eyebrow + .macro { margin-top: .9rem; }

/* Trait de grille mathematique : gap 1px sur fond contrastant. */
.hairgrid {
  display: grid;
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
}
.hairgrid > * { background: var(--bg); }

/* ----------------------------------------------------------- 7. NAVIGATION */

.nav {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 800;
  height: var(--nav-h);
  /* Barre opaque, pas de flou d arriere-plan : le glassmorphism est hors
     direction, et un backdrop-filter plein ecran coute cher au defilement. */
  background: var(--bg);
  border-bottom: 1px solid var(--line);
}
.nav__inner {
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 2rem;
}
.nav__brand { display: flex; align-items: center; gap: .7rem; flex-shrink: 0; }
.nav__mark  { width: 34px; height: 34px; }
.nav__name  {
  font-family: var(--font-display);
  text-transform: uppercase;
  font-size: .95rem;
  letter-spacing: -.02em;
}
.nav__links { display: flex; align-items: center; gap: clamp(1rem, 2.2vw, 2.1rem); }
.nav__links a {
  font-family: var(--font-mono);
  text-transform: uppercase;
  letter-spacing: .1em;
  font-size: .71rem;
  color: var(--fg-mid);
  white-space: nowrap;
  transition: color .18s var(--ease);
}
.nav__links a:hover { color: var(--accent); }

/* Filet de separation : a gauche les ancres de la page courante, a droite les
   deux pages qui ont leur vie propre. Un trait et non un espace plus large :
   sur une barre serree un espace ne se lit pas comme une separation. */
.nav__sep {
  width: 1px;
  align-self: stretch;
  margin-block: .35rem;
  background: var(--line-strong);
}

.nav__toggle { display: none; }

@media (max-width: 900px) {
  .nav__links { display: none; }
  .nav__links.is-open {
    display: flex;
    position: fixed;
    inset: var(--nav-h) 0 auto 0;
    flex-direction: column;
    align-items: flex-start;
    gap: 0;
    background: var(--bg);
    border-bottom: 1px solid var(--line);
    padding: 0;
  }
  .nav__links.is-open a {
    width: 100%;
    padding: 1.15rem var(--gut);
    border-bottom: 1px solid var(--line);
    font-size: .8rem;
    color: var(--fg);
  }
  /* En colonne, le filet vertical n aurait aucun sens : la separation se fait
     par un retrait, et le groupe de droite passe en vert pour se distinguer
     des ancres de la page. */
  .nav__links.is-open .nav__sep {
    width: 100%;
    height: 0;
    margin: 0;
    background: none;
    border-bottom: 1px solid var(--line-strong);
  }
  .nav__links.is-open .nav__sep ~ a { color: var(--accent); }
  .nav__toggle {
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 5px;
    width: 42px;
    height: 42px;
    background: none;
    border: 1px solid var(--line-strong);
    cursor: pointer;
    padding: 0 10px;
  }
  .nav__toggle span {
    display: block;
    height: 2px;
    width: 100%;
    background: var(--fg);
    transition: transform .22s var(--ease), opacity .22s var(--ease);
  }
  .nav__toggle[aria-expanded="true"] span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
  .nav__toggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
  .nav__toggle[aria-expanded="true"] span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }
  /* Specificite renforcee : .btn est declare plus bas et gagnerait
     autrement sur une simple classe a egalite de poids. */
  .nav__inner .nav__cta { display: none; }
}

/* -------------------------------------------------------------- 8. BOUTONS */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .6rem;
  padding: .95rem 1.5rem;
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: .74rem;
  text-transform: uppercase;
  letter-spacing: .11em;
  white-space: nowrap;
  border: 1px solid transparent;
  cursor: pointer;
  transition: background .18s var(--ease), color .18s var(--ease),
              border-color .18s var(--ease), transform .1s var(--ease);
}
.btn:active { transform: translateY(1px); }

/* Contraste verifie : #1AFF7D sur #050505 = 15.6:1 */
.btn--primary { background: var(--accent); color: var(--on-accent); }
.btn--primary:hover { background: var(--accent-sink); }

/* Contraste verifie : #EAEAEA sur #0A0A0A = 15.9:1 */
.btn--ghost { background: transparent; color: var(--fg); border-color: var(--line-strong); }
.btn--ghost:hover { border-color: var(--accent); color: var(--accent); }

.btn--nav { padding: .65rem 1.05rem; font-size: .69rem; white-space: nowrap; }
/* Sur les pages interieures la barre n a pas de menu burger : le bouton de
   contact est seul face au logo. En dessous de 380px il depassait du cadre,
   on reduit le bouton puis le nom de la marque plutot que de le laisser
   sortir de l ecran. */
@media (max-width: 400px) {
  .nav__inner { gap: .8rem; }
  .btn--nav { padding: .6rem .75rem; font-size: .62rem; letter-spacing: .08em; }
}
@media (max-width: 340px) {
  .nav__brand .nav__name { display: none; }
}
.btn--block { width: 100%; }

.btn__arrow { font-family: var(--font-mono); font-weight: 400; }

/* ---------------------------------------------------------------- 9. HERO */

/* Plus de video ici : le titre recupere toute la largeur, ce que la direction
   brutaliste reclame depuis le debut (macro-typographie debordante).

   Deux mouvements, tous deux en CSS pur :
   1. A l arrivee, chaque ligne se devoile par un balayage mecanique, decale.
   2. Au defilement, les deux lignes S ECARTENT horizontalement, l une vers la
      gauche, l autre vers la droite, comme deux portes qui s ouvrent. Le
      debordement est coupe par la scene collante.

   Aucun script. L etat par defaut est l etat VISIBLE et final : si les
   animations ou les timelines manquent, la page reste lisible et correcte. */

.vhero {
  position: relative;
  height: 185svh;
  view-timeline-name: --vhero;
  view-timeline-axis: block;
}
@supports not (height: 100svh) { .vhero { height: 185vh; } }

.vhero__sticky {
  position: sticky;
  top: 0;
  height: 100svh;
  display: flex;
  align-items: center;
  overflow: hidden;
}
@supports not (height: 100svh) { .vhero__sticky { height: 100vh; } }

.vhero__inner { width: 100%; padding-top: var(--nav-h); }

.vhero__title { margin-bottom: 2.1rem; }
.vhero__title .line { display: block; white-space: nowrap; }

/* Le bloc texte + boutons pesait 173px sous un titre de 213px : trois lignes
   de JetBrains Mono a 16.5px, puis 41.6px de vide, puis les boutons. Le mono
   est une fonte de donnees, il s epaissit vite des qu on lui confie un
   paragraphe. La phrase descend donc d un cran et sa laisse s elargit, pour
   tomber sur DEUX lignes et non trois, et l ecart avant les boutons se
   resserre. Mesure le 02/11/2026. */
.vhero__lede {
  /* Terme en vw volontairement doux : avec 1.05vw la valeur restait collee au
     plancher de 320 a 1280px, donc aucun ecran de portable ne beneficiait du
     clamp. .75rem + .24vw donne 14.5px a 1024, 15.1px a 1280, 15.5px a 1440. */
  font-size: clamp(.88rem, .75rem + .24vw, var(--t-lede));
  max-width: 66ch;
  margin-bottom: var(--t-lede-gap);
}
.vhero__actions { display: flex; flex-wrap: wrap; gap: .8rem; }

/* La rangee sous le titre tenait sur sa moitie gauche et laissait 808px de
   vide a droite. Elle se lit maintenant en deux blocs : ce qu on dit, et ce
   qu on fait. Le second n est pas du remplissage, ce sont les quatre
   metiers, la meme liste que la section Services. */
.vhero__under {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: clamp(1.8rem, 5vw, 5rem);
  align-items: end;
}

.vhero__sheet {
  margin: 0;
  min-width: clamp(17rem, 30vw, var(--t-metiers));
  border-top: 1px solid var(--line);
}
.vhero__sheet > div {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1.4rem;
  padding: .62rem 0;
  border-bottom: 1px solid var(--line);
}
.vhero__sheet dt,
.vhero__sheet dd {
  font-family: var(--font-mono);
  font-size: .76rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  margin: 0;
}
.vhero__sheet dt { color: var(--accent); }
.vhero__sheet dd { color: var(--fg-dim); text-align: right; }

/* La rangee et son relais occupent la MEME case : l un s efface, l autre
   arrive au meme endroit. Sans grille superposee il faudrait du positionnement
   absolu, qui sortirait le relais du flux et ferait perdre sa hauteur a la
   rangee. */
.vhero__stack { display: grid; }
.vhero__stack > * { grid-area: 1 / 1; }

/* Le relais annonce la section suivante quand la rangee a disparu. Il est
   masque par defaut et n apparait que la ou les timelines de defilement
   existent : la ou elles manquent, il ne se passe rien et personne ne voit
   un bloc vide. Regle de robustesse, aucun contenu reel n est en jeu ici. */
.vhero__relais { display: none; }
.vhero__relais-titre {
  font-family: var(--font-display);
  text-transform: uppercase;
  letter-spacing: -.035em;
  line-height: .92;
  font-size: clamp(1.4rem, 3.1vw, 2.7rem);
  color: var(--fg);
  margin-bottom: 1.1rem;
}
.vhero__relais-titre span { display: block; }
.vhero__relais-titre span:last-child { color: var(--accent); }
.vhero__relais-texte {
  color: var(--fg-mid);
  font-size: clamp(.84rem, .72rem + .22vw, .94rem);
  line-height: 1.66;
  max-width: 58ch;
}



/* Sous 900px la rangee redevient une colonne et la liste passe dessous. */
@media (max-width: 900px) {
  .vhero__under { grid-template-columns: minmax(0, 1fr); gap: 2.2rem; }
  .vhero__sheet { min-width: 0; }
}

/* --- 1. Arrivee : balayage mecanique, sans ressort ---------------------- */

@keyframes hero-wipe {
  from { clip-path: inset(0 100% 0 0); transform: translateY(.14em); }
  to   { clip-path: inset(0 0 0 0);    transform: translateY(0); }
}
@keyframes hero-rise {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: no-preference) {
  .vhero__title .line {
    animation: hero-wipe .62s cubic-bezier(.16, .84, .3, 1) both;
  }
  .vhero__title .line:nth-child(2) { animation-delay: .13s; }
  .vhero__lede    { animation: hero-rise .5s cubic-bezier(.2, .7, .2, 1) .34s both; }
  .vhero__actions { animation: hero-rise .5s cubic-bezier(.2, .7, .2, 1) .44s both; }
  /* La liste des metiers arrive apres les boutons : elle se lit en second. */
  .vhero__sheet > div { animation: hero-rise .42s cubic-bezier(.2, .7, .2, 1) both; }
  .vhero__sheet > div:nth-child(1) { animation-delay: .50s; }
  .vhero__sheet > div:nth-child(2) { animation-delay: .57s; }
  .vhero__sheet > div:nth-child(3) { animation-delay: .64s; }
  .vhero__sheet > div:nth-child(4) { animation-delay: .71s; }
}

/* --- 2. Defilement : les deux lignes s ecartent ------------------------- */

@keyframes hero-part-left  { to { transform: translateX(-16%); } }
@keyframes hero-part-right { to { transform: translateX(16%); } }
@keyframes hero-recede     { to { opacity: 0; transform: translateY(-26px); } }
@keyframes relais-in  { from { opacity: 0; transform: translateY(18px); } to { opacity: 1; transform: none; } }
@keyframes relais-out { to   { opacity: 0; transform: translateY(-18px); } }

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    /* Mesure navigateur : au sommet de page la progression "cover" vaut
       1/(1.85+1) = 35 pour cent, la fin du collage 65 pour cent.
       L ecartement tient dans cette fenetre. */
    .vhero__title .line:nth-child(1) {
      animation: hero-wipe .62s cubic-bezier(.16,.84,.3,1) both,
                 hero-part-left linear both;
      animation-timeline: auto, --vhero;
      animation-range: normal, cover 38% cover 62%;
    }
    .vhero__title .line:nth-child(2) {
      animation: hero-wipe .62s cubic-bezier(.16,.84,.3,1) .13s both,
                 hero-part-right linear both;
      animation-timeline: auto, --vhero;
      animation-range: normal, cover 38% cover 62%;
    }
    .vhero__under {
      animation: hero-recede linear both;
      animation-timeline: --vhero;
      animation-range: cover 38% cover 54%;
    }
    /* Le relais prend la suite : il entre quand la rangee a fini de partir,
       ses deux lignes decalees de 4% l une de l autre, puis il s efface
       avant que la section suivante n arrive, pour que son titre ne se lise
       pas deux fois a l ecran. Le decalage se fait sur la FENETRE et non en
       animation-delay : sur une timeline de defilement un delai ne veut rien
       dire. */
    /* Le parent ne fait que reapparaitre : s il animait lui aussi, sa propre
       opacite se multiplierait a celle de ses etages et l entree serait sale. */
    .vhero__relais { display: block; }
    .vhero__relais-titre span,
    .vhero__relais-texte {
      animation: relais-in linear both, relais-out linear both;
      animation-timeline: --vhero, --vhero;
    }
    .vhero__relais-titre span:nth-child(1)  { animation-range: cover 50% cover 60%, cover 80% cover 90%; }
    .vhero__relais-titre span:nth-child(2)  { animation-range: cover 53% cover 63%, cover 80% cover 90%; }
    .vhero__relais-texte                    { animation-range: cover 57% cover 67%, cover 80% cover 90%; }
  }
}

@media (max-width: 900px) {
  .vhero { height: 150svh; }
  .vhero__inner { padding-top: calc(var(--nav-h) + 1rem); }
}

/* -------------------------------------------------------- 10. TRAJECTOIRES */

/* Deux phrases de transformation posees juste sous le hero. C est la
   premiere preuve que rencontre le visiteur : elle remplace un mur de logos,
   qui n aurait dit que "ils existent" la ou ceci dit "il s est passe quelque
   chose". */
/* Les collaborations ont quitte l accueil le 02/11/2026 pour leur propre
   page, collaborations.html : une collaboration se lit quand on la cherche,
   pas en travers du hero. La regle .turn qui habillait le bloc sur l accueil
   a disparu avec lui ; il ne reste que la cellule, qui sert sur la page. */
.turn__grid { grid-template-columns: minmax(0, 1fr); }
.turn__grid--page { margin-top: clamp(2.5rem, 5vw, 4rem); border-top: 1px solid var(--line); }

.turn__cell {
  padding: clamp(1.2rem, 2vw, 1.8rem);
  display: flex;
  flex-direction: column;
  gap: .9rem;
}
.turn__cell > * { display: block; }
.turn__client { color: var(--accent); }
.turn__line { color: var(--fg); }
.turn__note {
  font-family: var(--font-mono);
  font-size: .82rem;
  letter-spacing: .04em;
  color: var(--fg-mid);
  margin-top: auto;
}
.turn__go { margin-top: .3rem; }

/* Une seule collaboration mise en avant : la cellule prend toute la largeur.
   Place APRES la regle de base, qui pose display:flex et l emporterait
   sinon sur le display:grid de cette requete. */
@media (min-width: 821px) {
  .turn__cell {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    column-gap: clamp(1.5rem, 4vw, 4rem);
    row-gap: .7rem;
  }
  /* Le titre garde toute la largeur : le couper en trois lignes pour loger
     la note a cote laissait un "CALAIS" orphelin. La note descend donc sur
     la ligne de pied, face au lien. */
  .turn__client { grid-column: 1 / -1; grid-row: 1; }
  .turn__line   { grid-column: 1 / -1; grid-row: 2; }
  .turn__go     { grid-column: 1; grid-row: 3; justify-self: start; align-self: center; }
  .turn__note   { grid-column: 2; grid-row: 3; text-align: right; margin-top: 0; align-self: center; }
}

/* ------------------------------------------------------------ 11. SERVICES */

.svc { grid-template-columns: repeat(12, minmax(0, 1fr)); }
.svc__cell { padding: clamp(1.6rem, 2.8vw, 2.6rem); display: flex; flex-direction: column; }

/* Deux rangees en 7+5 puis 5+7 : l alternance evite la grille symetrique
   et la rangee de cartes identiques que le socle interdit. */
/* Trois metiers depuis le 02/11/2026, plus quatre : deux cellules en haut,
   une bande pleine largeur en bas. La bande recupere la photo qui habillait
   l ancienne quatrieme cellule, sinon elle quittait la page. */
.svc__cell--lead   { grid-column: span 7; min-height: var(--t-cellule); justify-content: flex-end; position: relative; overflow: hidden; }
.svc__cell--b      { grid-column: span 5; }
.svc__cell--c      { grid-column: span 12; min-height: 340px; justify-content: flex-end; position: relative; overflow: hidden; }

/* Variation visuelle reelle dans la grille : une photo, une trame. */
.svc__bg { position: absolute; inset: 0; z-index: 0; }
.svc__bg img { width: 100%; height: 100%; object-fit: cover; filter: grayscale(1) brightness(.5) contrast(1.2); }
/* Les images de cette grille sont des plans tires de videos sous-titrees : le
   sous-titre incruste tombait en plein milieu de la cellule et passait pour
   un defaut d impression. Le cadrage remonte dans le tiers haut du plan, ou
   il n y a jamais de texte. Et a .36 l image n etait plus qu une tache : .5
   rend une salle lisible sans concurrencer le titre. Verifie le 02/11/2026
   sur les deux cellules. */
.svc__cell--lead .svc__bg img { object-position: 50% 20%; }
.svc__bg::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(to top, var(--bg) 6%, rgba(10,10,10,.55) 55%, rgba(10,10,10,.2) 100%);
}
.svc__cell--lead > *:not(.svc__bg),
.svc__cell--c > *:not(.svc__bg) { position: relative; z-index: 1; }

/* La trame habille la seule cellule sans photo, pour qu aucune des trois ne
   soit nue a cote des deux autres. */
.svc__cell--b {
  background:
    repeating-linear-gradient(90deg, transparent 0 7px, rgba(26,255,125,.05) 7px 8px),
    var(--bg);
}

.svc__name { margin-bottom: .9rem; }
.svc__body { color: var(--fg-mid); font-size: .9rem; line-height: 1.6; max-width: 44ch; }
.svc__meta { margin-top: auto; padding-top: 1.9rem; }

@media (max-width: 900px) {
  .svc__cell--lead, .svc__cell--b, .svc__cell--c { grid-column: span 12; }
  .svc__cell--lead, .svc__cell--c { min-height: 320px; }
}

/* ------------------------------------------------------------- 12. METHODE */

.method__grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
@media (max-width: 900px) { .method__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 520px) { .method__grid { grid-template-columns: 1fr; } }

.method__cell { padding: clamp(1.5rem, 2.4vw, 2.2rem); min-height: 260px; display: flex; flex-direction: column; }
.method__verb { color: var(--accent); margin-bottom: 1rem; }
.method__cell p { color: var(--fg-mid); font-size: .87rem; line-height: 1.6; }

/* ------------------------------------------------------------- 13. PREUVE */

/* -------------------------------------------------------- 14. REALISATIONS */

.work__grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
}
@media (max-width: 900px) {
  .work__grid {
    display: flex;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    border: none;
    background: none;
    gap: 1px;
    padding-bottom: .5rem;
    -webkit-overflow-scrolling: touch;
  }
  .work__item { flex: 0 0 68%; scroll-snap-align: start; border: 1px solid var(--line); }
}

.work__item { position: relative; background: var(--bg-sink); aspect-ratio: 9 / 16; overflow: hidden; }
.work__video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  background: var(--bg-sink);
}

/* La carte plafonnait a 230px : sur un ecran de 1920 le clip vertical, qui est
   le produit meme de VOX, tenait dans une vignette posee au milieu du vide.
   Le plafond passe a 360px et la section rend sa marge a l image : meme
   encombrement vertical qu avant, media moitie plus grand. Mesure le
   02/11/2026 a 1024, 1440 et 1920. */
.cf { --cf-card: clamp(150px, 24vw, var(--t-carte)); width: 100%; }
#realisations { padding-block: clamp(3.5rem, 7vw, var(--t-reals)); }

/* Sans JS, .cf--on est absent : le repli est la grille de la section 14. */
.cf__frame { position: relative; }

.cf--on .cf__frame {
  overflow: hidden;
  /* Marge verticale pour que les cartes inclinees ne soient pas rognees. */
  padding-block: clamp(1.5rem, 3vw, 2.5rem);
  cursor: grab;
  outline: none;
  perspective: calc(var(--cf-card) * 3);
  /* Le glissement horizontal nous appartient, la page garde le vertical. */
  touch-action: pan-y;
}
.cf--on .cf__frame:active { cursor: grabbing; }
.cf--on .cf__frame:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

.cf--on .cf__stage {
  position: relative;
  display: block;
  /* Le repli mobile fait de .work__grid un conteneur a defilement horizontal.
     En coverflow il faut le neutraliser : le navigateur y posait un
     scrollLeft en amenant une carte au point, ce qui decalait tout le rail
     en dessous de 901px. Le rognage est le travail de .cf__frame. */
  overflow: visible;
  -webkit-overflow-scrolling: auto;
  scroll-snap-type: none;
  padding-bottom: 0;
  gap: 0;
  border: 0;
  background: none;
  user-select: none;
  transform-style: preserve-3d;
  /* Cartes 9:16, le format que VOX produit. */
  height: calc(var(--cf-card) * 16 / 9);
}

.cf--on .work__item {
  position: absolute;
  left: 50%;
  top: 0;
  flex: none;
  scroll-snap-align: none;
  width: var(--cf-card);
  aspect-ratio: 9 / 16;
  border: 1px solid var(--line-strong);
  will-change: transform, opacity;
  /* Angles droits : le skill brutaliste interdit tout arrondi. */
  border-radius: 0;
}

/* Hors du centre, les cartes sont grises et sombres : elles font decor.
   La carte active retrouve couleur et luminosite, c est le seul repere
   visuel qui designe la diapositive en cours. */
.cf--on .work__video {
  filter: grayscale(1) contrast(1.15) brightness(.5);
  transition: filter .3s var(--ease);
  transform: none;
}
.cf--on .work__item.is-centre .work__video {
  filter: grayscale(0) contrast(1.02) brightness(1);
}
.cf--on .work__item.is-centre { border-color: var(--accent); }

@media (prefers-reduced-motion: reduce) {
  .cf--on .work__video { transition: none; }
}

.cf__nav { position: absolute; inset: 0; pointer-events: none; z-index: 200; }
.cf__arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  pointer-events: auto;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  padding: 0;
  background: rgba(6, 6, 6, .82);
  border: 1px solid var(--line-strong);
  color: var(--fg);
  cursor: pointer;
  transition: border-color .18s var(--ease), color .18s var(--ease);
}
.cf__arrow:hover { border-color: var(--accent); color: var(--accent); }
.cf__arrow:active { transform: translateY(-50%) translateY(1px); }
.cf__arrow svg { width: 20px; height: 20px; }
.cf__arrow--prev { left: 0; }
.cf__arrow--next { right: 0; }

.cf__caption {
  display: none;
  margin-top: 1.6rem;
  align-items: baseline;
  justify-content: center;
  gap: 1rem;
  text-align: center;
  flex-wrap: wrap;
}
.cf--on .cf__caption { display: flex; }
.cf__client {
  font-family: var(--font-display);
  text-transform: uppercase;
  font-size: clamp(1.05rem, 1.8vw, 1.5rem);
  letter-spacing: -.03em;
  color: var(--fg);
}
.cf__kind  { font-family: var(--font-mono); text-transform: uppercase; letter-spacing: .1em; font-size: .72rem; color: var(--accent); }
.cf__count { font-family: var(--font-mono); letter-spacing: .1em; font-size: .72rem; color: var(--fg-dim); }

/* Bouton son : masque sur les clips sans piste audio, pour qu un volume
   inerte ne passe jamais pour une panne. */
.cf__sound {
  font-family: var(--font-mono);
  font-size: .68rem;
  text-transform: uppercase;
  letter-spacing: .12em;
  color: var(--fg-mid);
  background: transparent;
  border: 1px solid var(--line-strong);
  padding: .4rem .7rem;
  cursor: pointer;
  transition: color .18s var(--ease), border-color .18s var(--ease);
}
.cf__sound:hover { color: var(--accent); border-color: var(--accent); }
.cf__sound[aria-pressed="true"] { color: var(--accent); border-color: var(--accent); }
.cf__sound:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.cf__dots { display: flex; justify-content: center; gap: .45rem; margin-top: 1.5rem; }
.cf__dots:empty { display: none; }
.cf__dot {
  width: 22px;
  height: 3px;
  padding: 0;
  border: 0;
  background: var(--line-strong);
  cursor: pointer;
  transition: background .18s var(--ease);
}
.cf__dot[aria-current="true"] { background: var(--accent); }
.cf__dot:hover { background: var(--fg-mid); }

@media (max-width: 700px) {
  .cf__arrow { display: none; }
}

/* ------------------------------------------------- 14ter. GALERIE PHOTO */

/* Mosaique en colonnes CSS : les hauteurs restent naturelles, donc les
   formats portrait et paysage cohabitent sans recadrage. */
/* Pas de cadre autour de la mosaique : l equilibrage des colonnes CSS laisse
   forcement une colonne plus courte, et dans une boite bordee ce vide se lit
   comme un trou. Sans cadre, il se lit comme la fin naturelle de la grille.
   Chaque image porte donc son propre filet. */
.gal {
  column-count: 1;
  column-gap: 2px;
}
@media (min-width: 560px)  { .gal { column-count: 2; } }
@media (min-width: 900px)  { .gal { column-count: 3; } }
@media (min-width: 1280px) { .gal { column-count: 4; } }

.gal__item {
  position: relative;
  display: block;
  break-inside: avoid;
  margin-bottom: 2px;
  background: var(--bg-sink);
  border: 1px solid var(--line);
  overflow: hidden;
}
.gal--on .gal__item { cursor: zoom-in; }
.gal__item:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

.gal__item img {
  width: 100%;
  height: auto;
  display: block;
  /* Ici la photo EST le contenu : elle reste lisible au repos. Le
     monochrome suffit a tenir la palette, l assombrissement la tuait
     (les cliches de nuit devenaient des rectangles noirs). */
  filter: grayscale(1) contrast(1.04) brightness(.96);
  transition: filter .35s var(--ease), transform .45s var(--ease);
}
.gal--on .gal__item:hover img,
.gal--on .gal__item:focus-visible img {
  filter: grayscale(0) contrast(1.02) brightness(1);
  transform: scale(1.02);
}

/* Au survol, la photo passe AU-DESSUS des calques CRT (scanlines z-900,
   grain z-901). Les traits se retirent donc en meme temps que la couleur
   revient : on voit la photographie telle qu elle est, sans filtre.
   Au repos elle repasse dessous et reprend sa place dans l ambiance. */
.gal--on .gal__item:hover,
.gal--on .gal__item:focus-visible {
  z-index: 910;
}

/* Reperage au survol, sans pastille ni etiquette posee sur la photo. */
.gal__item::after {
  content: '';
  position: absolute;
  inset: 0;
  border: 1px solid transparent;
  pointer-events: none;
  transition: border-color .25s var(--ease);
}
.gal--on .gal__item:hover::after,
.gal--on .gal__item:focus-visible::after { border-color: var(--accent); }

/* ------------------------------------------------ VISIONNEUSE (LIGHTBOX) */

.lb {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
}
.lb[hidden] { display: none; }

.lb__backdrop {
  position: absolute;
  inset: 0;
  background: #060606;
  opacity: 0;
  transition: opacity .3s var(--ease);
}
.lb.is-open .lb__backdrop { opacity: var(--lb-fade, 1); }

.lb__stage {
  position: relative;
  z-index: 1;
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(3rem, 8vw, 5rem) clamp(1rem, 6vw, 5rem);
  cursor: zoom-out;
}

.lb__figure {
  max-width: min(92vw, 1400px);
  max-height: 82vh;
  display: flex;
  will-change: transform;
}
.lb__img {
  max-width: 100%;
  max-height: 82vh;
  width: auto;
  height: auto;
  object-fit: contain;
  border: 1px solid var(--line-strong);
  user-select: none;
}

.lb__btn {
  position: absolute;
  z-index: 3;
  display: grid;
  place-items: center;
  width: 46px;
  height: 46px;
  padding: 0;
  background: rgba(10, 10, 10, .8);
  border: 1px solid var(--line-strong);
  color: var(--fg);
  cursor: pointer;
  transition: border-color .18s var(--ease), color .18s var(--ease);
}
.lb__btn:hover { border-color: var(--accent); color: var(--accent); }
.lb__btn:active { transform: translateY(1px); }
.lb__btn svg { width: 20px; height: 20px; }
.lb__btn--close { top: clamp(.75rem, 2vw, 1.5rem); right: clamp(.75rem, 2vw, 1.5rem); }
.lb__btn--prev  { left: clamp(.75rem, 2vw, 1.5rem); top: 50%; transform: translateY(-50%); }
.lb__btn--next  { right: clamp(.75rem, 2vw, 1.5rem); top: 50%; transform: translateY(-50%); }
.lb__btn--prev:active { transform: translateY(-50%) translateY(1px); }
.lb__btn--next:active { transform: translateY(-50%) translateY(1px); }

.lb__meta {
  position: absolute;
  z-index: 3;
  left: 0;
  right: 0;
  bottom: clamp(.9rem, 2.5vw, 1.6rem);
  display: flex;
  justify-content: center;
  align-items: baseline;
  gap: 1.1rem;
  flex-wrap: wrap;
  padding-inline: 4.5rem;
  text-align: center;
}
.lb__count {
  font-family: var(--font-mono);
  font-size: .72rem;
  letter-spacing: .1em;
  color: var(--accent);
}
.lb__legend {
  font-family: var(--font-mono);
  font-size: .76rem;
  color: var(--fg-mid);
}

@media (max-width: 640px) {
  .lb__btn--prev { left: .5rem; }
  .lb__btn--next { right: .5rem; }
  .lb__figure, .lb__img { max-height: 70vh; }
}

@media (prefers-reduced-motion: reduce) {
  .gal__item img { transition: none; }
  .gal--on .gal__item:hover img { transform: none; }
  .lb__backdrop { transition: none; }
}

/* ------------------------------------------------- 14ter. PAGE EN ATTENTE */

/* Etat d une rubrique annoncee mais pas encore remplie. Le filet du haut et
   la mesure courte reprennent la grammaire du reste du site : c est une page
   qui attend, pas une page cassee. */
.attente {
  border-top: 1px solid var(--line);
  padding-top: clamp(2rem, 4vw, 3rem);
  margin-top: clamp(2.5rem, 5vw, 4rem);
  max-width: 54ch;
}
.attente__etat {
  display: inline-block;
  padding: .35rem .6rem;
  border: 1px solid var(--accent);
  color: var(--accent);
  font-size: .68rem;
  letter-spacing: .16em;
  text-transform: uppercase;
  margin-bottom: 1.6rem;
}
.attente__dire {
  color: var(--fg-mid);
  font-size: clamp(.9rem, .78rem + .22vw, 1rem);
  line-height: 1.68;
}
.attente__dire + .attente__dire { margin-top: 1.1rem; }
.attente__action { margin-top: clamp(1.8rem, 3.5vw, 2.6rem); }

/* ------------------------------------------------------------ 14quater. JOURNAL */

/* Liste et non cartes : un blog grandit, et une liste absorbe dix entrees
   aussi bien que trois. Un seul filet sous chaque ligne, jamais au-dessus
   ET en dessous. */
.journal__list { border-top: 1px solid var(--line); }

.journal__item {
  display: grid;
  grid-template-columns: 8.5rem 10rem minmax(0, 1fr) 2rem;
  gap: 1.5rem;
  align-items: baseline;
  padding-block: clamp(1.1rem, 2vw, 1.6rem);
  border-bottom: 1px solid var(--line);
  transition: background .2s var(--ease);
}
.journal__item:hover { background: var(--bg-raise); }
.journal__item:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

.journal__date { color: var(--fg-dim); font-variant-numeric: tabular-nums; }
.journal__cat  { color: var(--accent); }
.journal__title {
  font-family: var(--font-display);
  text-transform: uppercase;
  font-size: clamp(1rem, 1.6vw, 1.35rem);
  letter-spacing: -.025em;
  line-height: 1.1;
  color: var(--fg);
}
.journal__arrow {
  justify-self: end;
  color: var(--fg-dim);
  transition: color .2s var(--ease), transform .2s var(--ease);
}
.journal__item:hover .journal__arrow { color: var(--accent); transform: translateX(4px); }

/* Etat brouillon : visible, pour qu un exemple non remplace ne passe jamais
   pour un article publie. */
.journal__draft {
  justify-self: start;
  color: var(--fg-dim);
  border: 1px solid var(--line-strong);
  padding: .1rem .4rem;
  font-size: .6rem;
  letter-spacing: .14em;
}

.journal__more { margin-top: 2.2rem; }

@media (max-width: 820px) {
  /* Deux lignes : metadonnees en haut, titre en dessous. Placement explicite
     plutot que "order", qui laissait la fleche seule sur une ligne. */
  .journal__item {
    grid-template-columns: auto 1fr;
    gap: .45rem .9rem;
    align-items: center;
  }
  .journal__date,
  .journal__draft { grid-column: 1; grid-row: 1; }
  .journal__cat   { grid-column: 2; grid-row: 1; justify-self: start; }
  .journal__title { grid-column: 1 / -1; grid-row: 2; }
  .journal__arrow { display: none; }
}

/* ------------------------------------------------------ 14quinquies. ARTICLE */

/* La barre de navigation est fixe : sans ce decalage, le haut de l article
   se glissait DESSOUS sur mobile, ou 4rem de padding valent exactement la
   hauteur de la barre. */
.article {
  padding-top: calc(var(--nav-h) + clamp(2rem, 5vw, 4rem));
  padding-bottom: clamp(4rem, 9vw, 7rem);
  border-top: 1px solid var(--line);
}
.article__head { margin-bottom: clamp(2.5rem, 5vw, 4rem); max-width: 26ch; }
/* Un titre d article long sort de la laisse de 26ch, qui vaut 234px et le
   cassait en douze lignes.

   Mesure du 02/11/2026 : la ligne la plus longue occupe 18.982 fois la
   taille de police. A 320px cela imposerait du 14.8px pour tenir sur une
   ligne, donc l insecable est reserve aux ecrans larges :
     a partir de 900px, deux lignes nettes, police plafonnee a 4.6vw et
     4.1rem pour 4.85vw et 4.37rem tenables ;
     en dessous, le titre retombe a la ligne normalement et garde une
     taille lisible. */
.article__head:has(.article__title) { max-width: none; }
.article__title { font-size: clamp(1.5rem, 4.4vw, 2.3rem); }
@media (min-width: 900px) {
  .article__title { font-size: clamp(2.6rem, 4.6vw, 4.1rem); }
  .article__title .ln { white-space: nowrap; }
}
.article__meta { display: flex; flex-wrap: wrap; gap: 1.2rem; margin-bottom: 1.2rem; }
/* Le nom du client quitte le titre pour la ligne de metadonnees, a cote de
   la rubrique : le titre porte la transformation, la ligne porte le qui. */
.article__client { color: var(--fg-mid); }
.article__body { max-width: 68ch; }
.article__body h2 {
  font-family: var(--font-display);
  text-transform: uppercase;
  font-size: clamp(1.2rem, 2.2vw, 1.7rem);
  letter-spacing: -.03em;
  color: var(--fg);
  margin: 2.8rem 0 1rem;
}
.article__body p { color: var(--fg-mid); line-height: 1.75; margin-bottom: 1.1rem; }
.article__body a { color: var(--accent); }
.article__body a:hover { text-decoration: underline; text-underline-offset: 3px; }
.article__body strong { color: var(--fg); font-weight: 400; }
.article__body ul { margin: 0 0 1.1rem 0; }
.article__body li {
  color: var(--fg-mid);
  line-height: 1.75;
  padding-left: 1.2rem;
  position: relative;
  margin-bottom: .4rem;
}
.article__body li::before {
  content: '';
  position: absolute;
  left: 0; top: .7em;
  width: 7px; height: 1px;
  background: var(--accent);
}
.article__body figure { margin: 2rem 0; }
.article__body figure img { width: 100%; border: 1px solid var(--line-strong); }
.article__body figcaption { margin-top: .6rem; font-size: .78rem; color: var(--fg-dim); }
.article__back { display: inline-flex; gap: .5rem; margin-top: 3rem; }

/* ------------------------------------------------------ 14sexies. PAGE CLIENT */

/* Les deux cellules de trajectoire deviennent cliquables. */
.turn__cell {
  text-decoration: none;
  transition: background .2s var(--ease);
}
a.turn__cell:hover { background: var(--bg-raise); }
a.turn__cell:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.turn__go {
  display: inline-flex;
  align-items: center;
  gap: .45rem;
  color: var(--accent);
  font-family: var(--font-mono);
  font-size: .7rem;
  text-transform: uppercase;
  letter-spacing: .12em;
  transition: transform .2s var(--ease);
}
a.turn__cell:hover .turn__go { transform: translateX(4px); }

/* Extraits en bas de page client : trois verticales, comme le produit. */
.case__shots {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
  margin: 2rem 0;
}
@media (max-width: 700px) { .case__shots { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.case__shots img {
  width: 100%;
  aspect-ratio: 9 / 16;
  object-fit: cover;
  display: block;
  filter: grayscale(1) contrast(1.05) brightness(.9);
  transition: filter .3s var(--ease);
}
.case__shots img:hover { filter: grayscale(0) contrast(1.02) brightness(1); }

.case__hero {
  max-width: 300px;
  aspect-ratio: 9 / 16;
  border: 1px solid var(--line-strong);
  overflow: hidden;
  margin: 2rem 0;
  position: relative;
}
.case__hero video { width: 100%; height: 100%; object-fit: cover; filter: grayscale(1) contrast(1.1) brightness(.85); }
.case__hero::after {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--accent);
  mix-blend-mode: color;
  opacity: .15;
  pointer-events: none;
}

.case__result {
  border: 1px solid var(--accent);
  padding: clamp(1.4rem, 3vw, 2.2rem);
  margin: 2.5rem 0;
}
.case__result p:last-child { margin-bottom: 0; }
/* Specificite renforcee : .article__body p (deux classes) l emportait sur
   .case__figure (une seule) et repeignait le chiffre en gris. */
.article__body .case__figure,
.case__figure {
  font-family: var(--font-display);
  text-transform: uppercase;
  font-size: clamp(2rem, 5vw, 3.4rem);
  line-height: .95;
  letter-spacing: -.04em;
  color: var(--accent);
  margin-bottom: .8rem;
}

/* ------------------------------------------------------------ 15. SECTEURS */

.marq {
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  overflow: hidden;
  padding-block: clamp(1.6rem, 3vw, 2.4rem);
  background: var(--bg-raise);
}
.marq__track {
  display: flex;
  width: max-content;
  gap: 0;
  animation: marq 42s linear infinite;
}
/* Deuxieme rangee a contresens : deux vitesses opposees donnent au bandeau
   une profondeur que la rangee unique n avait pas. Elle est purement
   decorative, donc aria-hidden dans le HTML. */
.marq__track--rev {
  animation-name: marq-rev;
  animation-duration: 54s;
  margin-top: .35rem;
  opacity: .42;
}
.marq__track--rev .marq__item { -webkit-text-stroke: 1px var(--line-strong); color: transparent; }
.marq__group { display: flex; gap: 0; }
.marq__item {
  font-family: var(--font-display);
  text-transform: uppercase;
  font-size: clamp(1.5rem, 3.4vw, 3rem);
  letter-spacing: -.035em;
  line-height: 1;
  padding-inline: clamp(1rem, 2.2vw, 2rem);
  color: var(--fg);
  white-space: nowrap;
  display: flex;
  align-items: center;
  gap: clamp(1rem, 2.2vw, 2rem);
}
.marq__item::after { content: '/'; color: var(--accent); font-size: .7em; }

@keyframes marq     { from { transform: translate3d(0,0,0); }    to { transform: translate3d(-50%,0,0); } }
@keyframes marq-rev { from { transform: translate3d(-50%,0,0); } to { transform: translate3d(0,0,0); } }
@media (prefers-reduced-motion: reduce) {
  .marq__track { animation: none; }
  .marq { overflow-x: auto; }
}

/* ------------------------------------------------------------- 16. CONTACT */

.contact__grid {
  display: grid;
  grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
  gap: clamp(2.5rem, 5vw, 5rem);
  align-items: start;
}
@media (max-width: 900px) { .contact__grid { grid-template-columns: 1fr; } }

/* Meme taille que les libelles du formulaire : la section passe ainsi de
   sept tailles de texte a quatre, ce qui est le vrai moteur de la lecture
   facile chez le modele. */
.contact__aside .lede { margin-top: 1.3rem; font-size: clamp(.95rem, .85rem + .2vw, 1.12rem); }
.contact__direct { margin-top: 2.4rem; display: flex; flex-direction: column; gap: .55rem; }
.contact__direct a { color: var(--accent); font-size: .8rem; letter-spacing: .04em; }
.contact__direct a:hover { text-decoration: underline; text-underline-offset: 4px; }
.contact__zone {
  /* Quatre lignes de capitales se lisent mal : on garde la mono, pas la casse. */
  text-transform: none;
  margin-top: 2.2rem;
  padding-top: 1.4rem;
  border-top: 1px solid var(--line);
  font-size: .8rem;
  line-height: 1.7;
  color: var(--fg-dim);
  max-width: 40ch;
}

/* Formulaire refait le 02/11/2026 sur le modele de la page de contact de
   Deuce Studio, releve au navigateur. Quatre mesures expliquent leur lecture
   facile, et aucune ne tient a la police :
     1. UN champ par ligne, jamais deux cote a cote ;
     2. le champ n est pas une boite mais un simple filet sous la ligne de
        saisie, donc rien a lire avant d arriver au texte ;
     3. le libelle fait 25.7px chez eux contre 10.7 ici, en bas de casse et
        sans interlettrage force ;
     4. six tailles de texte sur toute la page. Ce formulaire en avait
        quatre a lui seul, il en a deux.
   La police reste JetBrains Mono, verrouillee par ce projet. */
.form { display: grid; gap: clamp(1.7rem, 3vw, 2.4rem); }
/* .form__row ne met plus deux champs cote a cote : la classe reste pour ne
   pas toucher au HTML, mais elle empile. */
.form__row { display: grid; grid-template-columns: minmax(0, 1fr); gap: clamp(1.7rem, 3vw, 2.4rem); }

.field { display: grid; gap: .3rem; }
/* Label toujours au-dessus du champ. Jamais de placeholder en guise de label. */
.field label {
  font-family: var(--font-mono);
  font-size: clamp(.95rem, .85rem + .2vw, 1.12rem);
  line-height: 1.3;
  color: var(--fg);
}
.field input, .field textarea, .field select {
  width: 100%;
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--line-strong);
  padding: .4rem 0 .55rem;
  font-family: var(--font-mono);
  font-size: clamp(.95rem, .85rem + .2vw, 1.12rem);
  color: var(--fg);
  transition: border-color .18s var(--ease);
  appearance: none;
}
.field select { cursor: pointer; }
/* Tant que rien n est choisi, "Selectionner" s affichait aussi noir que le
   libelle juste au-dessus et se lisait comme un second libelle. */
.field select:has(option[value=""]:checked) { color: #7E7E7E; }
.field textarea { resize: vertical; min-height: 150px; }
/* Contraste placeholder : #7E7E7E sur #0A0A0A = 4.8:1, au-dessus du seuil AA. */
.field input::placeholder, .field textarea::placeholder { color: #7E7E7E; }
.field input:focus, .field textarea:focus, .field select:focus {
  outline: none;
  border-bottom-color: var(--accent);
}
.field__help { font-size: .8rem; color: var(--fg-mid); }
.field__err {
  font-size: .8rem;
  color: var(--accent);
  font-weight: 700;
  letter-spacing: .04em;
  display: none;
}
.field.is-invalid input, .field.is-invalid textarea { border-bottom-color: var(--accent); }
.field.is-invalid .field__err { display: block; }

.form__status {
  display: none;
  padding: .95rem 1.1rem;
  border: 1px solid var(--accent);
  background: rgba(26, 255, 125, .07);
  font-size: .82rem;
  color: var(--fg);
}
.form__status.is-shown { display: block; }

.btn[aria-busy="true"] { opacity: .7; pointer-events: none; }

/* --------------------------------------------------- 16bis. PAGE LEGALE */

/* Page de texte long : la lisibilite prime sur l effet. Mesure courte,
   interlignage genereux, hierarchie portee par le poids et non la taille. */
/* Meme decalage que .article : la barre fixe mangeait le haut de page. */
.legal {
  padding-top: calc(var(--nav-h) + clamp(2rem, 5vw, 4rem));
  padding-bottom: clamp(4rem, 9vw, 7rem);
  border-top: 1px solid var(--line);
}
.legal__head { margin-bottom: clamp(2.5rem, 5vw, 4rem); }
.legal__body { max-width: 68ch; }
.legal__body h2 {
  font-family: var(--font-mono);
  font-size: .78rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .17em;
  color: var(--accent);
  margin: 3rem 0 .9rem;
  padding-bottom: .6rem;
  border-bottom: 1px solid var(--line);
}
.legal__body h2:first-child { margin-top: 0; }
.legal__body p { color: var(--fg-mid); line-height: 1.72; margin-bottom: 1rem; }
.legal__body a { color: var(--accent); }
.legal__body a:hover { text-decoration: underline; text-underline-offset: 3px; }
.legal__body strong { color: var(--fg); font-weight: 400; }

/* Fiche d identite : libelle a gauche, valeur a droite, filets sobres. */
.legal__dl,
.case__facts { display: grid; grid-template-columns: minmax(140px, 1fr) 2fr; gap: 1px; background: var(--line); border: 1px solid var(--line); margin-bottom: 1.4rem; }
.legal__dl > *,
.case__facts > * { background: var(--bg); padding: .7rem .9rem; margin: 0; }
.legal__dl dt,
.case__facts dt { font-family: var(--font-mono); font-size: .7rem; text-transform: uppercase; letter-spacing: .1em; color: var(--fg-mid); }
.legal__dl dd,
.case__facts dd { color: var(--fg); font-size: .92rem; }
@media (max-width: 620px) {
  .legal__dl,
.case__facts { grid-template-columns: 1fr; }
  .legal__dl dt,
.case__facts dt { padding-bottom: 0; }
}

.legal__back { display: inline-flex; align-items: center; gap: .5rem; margin-top: 3rem; }

/* -------------------------------------------------------------- 17. FOOTER */

.footer { border-top: 1px solid var(--line); padding-block: clamp(3rem, 6vw, 4.5rem); background: var(--bg-sink); }
.footer__top {
  display: grid;
  grid-template-columns: minmax(0, 2fr) repeat(3, minmax(0, 1fr));
  gap: 2.5rem;
  padding-bottom: 2.5rem;
  border-bottom: 1px solid var(--line);
}
@media (max-width: 1000px) {
  .footer__top { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1fr); }
  .footer__col:first-child { grid-column: 1 / -1; }
}
@media (max-width: 680px) {
  .footer__top { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .footer__col:first-child { grid-column: 1 / -1; }
}
@media (max-width: 420px) {
  .footer__top { grid-template-columns: minmax(0, 1fr); }
  .footer__col:first-child { grid-column: auto; }
}
/* La colonne de zone n est pas une liste de liens : meme graisse que les
   liens, mais sans couleur de survol qui promettrait un clic. */
.footer__col li { font-size: .82rem; color: var(--fg-mid); }

.footer__brand { display: flex; align-items: center; gap: .8rem; margin-bottom: 1.1rem; }
.footer__brand img { width: 44px; height: 44px; }
.footer__col h3 { margin-bottom: 1rem; }
.footer__col ul { display: grid; gap: .6rem; }
.footer__col a { font-size: .82rem; color: var(--fg-mid); transition: color .18s var(--ease); }
.footer__col a:hover { color: var(--accent); }

.footer__bottom {
  padding-top: 1.8rem;
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  justify-content: space-between;
}

/* ------------------------------------------------------- 18. REVELATION */

/* PRINCIPE : le contenu est VISIBLE PAR DEFAUT. Aucune regle ne le cache.
   L animation d apparition est un supplement, pilote par les timelines de
   defilement CSS, sans une ligne de JavaScript.

   La version precedente faisait l inverse : .reveal partait a opacity 0 et
   un script devait ajouter une classe pour la remonter a 1. Il a suffi que
   la transition ne s achève pas pour que TOUT le site s affiche vide, alors
   meme que le script avait correctement fait son travail. Un contenu ne
   doit jamais dependre d un mecanisme d animation pour etre lu.

   La ou les timelines ne sont pas supportees (Firefox a ce jour), il ne se
   passe simplement rien : la page s affiche, sans apparition progressive. */

@keyframes reveal-in {
  from { opacity: 0; clip-path: inset(0 0 100% 0); }
  to   { opacity: 1; clip-path: inset(0 0 0 0); }
}

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .reveal {
      animation: reveal-in linear both;
      /* Timeline anonyme : chaque element suit sa propre entree a l ecran. */
      animation-timeline: view();
      animation-range: entry 4% entry 42%;
    }

    /* Les titres de section se decouvrent ligne par ligne. Le decalage ne
       passe pas par animation-delay, qui ne veut rien dire sur une timeline
       de defilement : c est la FENETRE de chaque ligne qui est decalee.
       Resultat identique a un stagger, mais pilote par la molette. */
    .ln {
      display: block;
      animation: ln-up linear both;
      animation-timeline: view();
      animation-range: entry calc(6% + var(--i, 0) * 7%)
                       entry calc(44% + var(--i, 0) * 7%);
    }
    /* Le titre porte deja la cascade : il ne doit pas la jouer deux fois. */
    .reveal:has(.ln) { animation: none; }
  }
}

/* Etat par defaut d une ligne de titre : VISIBLE. L animation ci-dessus est
   un supplement, jamais la condition de lisibilite. */
.ln { display: block; }

@keyframes ln-up {
  from { opacity: 0; transform: translateY(.22em); }
  to   { opacity: 1; transform: none; }
}

/* ------------------------------------------- 18bis. PASSAGE D UNE PAGE A L AUTRE */

/* Deuce Studio enchaine ses pages avec Swup, une bibliotheque qui intercepte
   les liens et remplace le contenu en JavaScript. Meme resultat ici en deux
   regles de CSS : le navigateur fond la page sortante dans l entrante.
   La ou l API manque, la navigation reste une navigation ordinaire. */

@view-transition { navigation: auto; }

@media (prefers-reduced-motion: no-preference) {
  ::view-transition-old(root) {
    animation: vt-out .22s cubic-bezier(.4, 0, 1, 1) both;
  }
  ::view-transition-new(root) {
    animation: vt-in .3s cubic-bezier(0, 0, .2, 1) both;
  }
}
@media (prefers-reduced-motion: reduce) {
  ::view-transition-old(root),
  ::view-transition-new(root) { animation: none; }
}

@keyframes vt-out { to   { opacity: 0; transform: translateY(-10px); } }
@keyframes vt-in  { from { opacity: 0; transform: translateY(10px);  } }

/* La barre de navigation ne doit pas participer au fondu : elle est commune
   aux deux pages, elle reste en place pendant que le contenu change. */
.nav { view-transition-name: nav-bar; }

/* --------------------------------------------------------------- 19. A11Y */

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* ------------------------------------------------- 18ter. BOUTON PERMANENT */

/* Visible par defaut, regle de robustesse : si les timelines manquent il
   reste simplement la en permanence, ce qui est le pire acceptable. */
.parlons {
  position: fixed;
  right: 0;
  bottom: 0;
  z-index: 800;
  display: inline-flex;
  align-items: center;
  gap: .6rem;
  padding: 1.05rem 1.3rem;
  background: var(--accent);
  color: var(--on-accent);
  font-family: var(--font-mono);
  font-size: .72rem;
  font-weight: 700;
  letter-spacing: .14em;
  text-transform: uppercase;
  border-top: 1px solid var(--bg);
  border-left: 1px solid var(--bg);
  transition: background .18s var(--ease);
}
.parlons:hover { background: var(--accent-sink); }
.parlons:focus-visible { outline: 2px solid var(--fg); outline-offset: -5px; }
.parlons__fleche { transition: transform .18s var(--ease); }
.parlons:hover .parlons__fleche { transform: translateX(3px); }

@media (max-width: 520px) {
  .parlons { left: 0; right: 0; justify-content: center; border-left: 0; }
}

/* Pendant le hero il ferait doublon avec le bouton de la rangee : il se
   retire par une coupe nette, pas par un fondu. */
@keyframes parlons-monte {
  from { clip-path: inset(100% 0 0 0); }
  to   { clip-path: inset(0 0 0 0); }
}
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .parlons {
      animation: parlons-monte linear both;
      animation-timeline: --vhero;
      /* 54-62 et non 34-44 : sur la PREMIERE section de la page, la fenetre
         cover est deja avancee de 35% a scroll zero, puisque la section
         occupe deja l ecran. A 34% le bouton depassait donc de 11% des
         l arrivee. 54% tombe juste apres le retrait de la rangee du hero. */
      animation-range: cover 54% cover 62%;
    }
  }
}

.skip {
  position: absolute;
  top: calc(var(--nav-h) + 8px);
  transform: translateY(calc(-100% - var(--nav-h) - 16px));
  left: var(--gut);
  z-index: 1000;
  background: var(--accent);
  color: var(--on-accent);
  padding: .8rem 1.2rem;
  font-weight: 700;
  font-size: .75rem;
  text-transform: uppercase;
  letter-spacing: .1em;
  /* Deplacement en transform et non en top : la propriete reste sur le
     compositeur, le lien d evitement ne declenche pas de recalcul de mise
     en page au moment ou le clavier arrive sur la page. */
  transition: transform .2s var(--ease);
}
.skip:focus { transform: none; }

@media print {
  .crt-scan, .crt-grain, .nav, .marq { display: none; }
  body { background: #fff; color: #000; }
}
