/* ===================================================================
   Prazile — Feuille de style
   Direction : « tech chaleureux » façon Oblivion
   Blanc lumineux & chaud · verre dépoli · turquoise holographique
   Typo géométrique fine (Outfit) · détails HUD · monospace
   =================================================================== */

/* Un seul thème : sombre. La DA Prazile EST sombre — noir chaud, crème, rouge
   signature. Proposer un thème clair, c'était proposer une version du site où la
   charte ne s'applique plus : le hero y perdait son contraste, le rouge y virait,
   et chaque nouveau composant devait être pensé deux fois. Une identité forte n'a
   pas de variante. */
:root {
  --bg:            #100d0a;   /* noir chaud */
  --bg-alt:        #15110c;
  --surface:       #16120d;
  --glass:         rgba(255, 255, 255, .045);
  --glass-border:  rgba(255, 255, 255, .12);
  --text:          #f3ede2;
  --text-soft:     #a79c8d;
  --border:        rgba(255, 255, 255, .09);
  --border-strong: rgba(255, 255, 255, .16);
  --accent:        #2dd4bf;   /* turquoise — ACTION uniquement, jamais décor.
                                 Valeur unique : les pages légales, .tenvoi et le
                                 reste du site divergeaient (#2fd4c6 vs #2dd4bf). */
  --accent-ink:    #07201e;
  --warm:          #d98a4a;
  --shadow:        0 1px 2px rgba(0, 0, 0, .4), 0 14px 40px rgba(0, 0, 0, .4);
  --shadow-lg:     0 1px 2px rgba(0, 0, 0, .5), 0 26px 70px rgba(0, 0, 0, .55);
  --glow:          0 8px 34px rgba(47, 212, 198, .26);
  --radius:        16px;
  --radius-sm:     11px;
  --max:           1120px;
  --font:          'Inter', system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
  --font-head:     'Outfit', var(--font);
  --font-mono:     'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
  /* UN rouge, et c'est celui-là. Il valait #e23b30 ici et #f0554a en sombre,
     pendant que le moteur du hero, le générateur, le terminal, l'arcade et la
     carte de visite utilisaient tous #e2231a. La signature était le seul endroit
     à ne pas employer le rouge de la marque. */
  --pz-red:        #e2231a;
}

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

html { scroll-behavior: smooth; }

body {
  position: relative;
  font-family: var(--font);
  color: var(--text);
  background: var(--bg);
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  transition: background .3s ease, color .3s ease;
}

/* Atmosphère : lumière chaude + lueur turquoise, fixe, très douce */
body::before {
  content: ""; position: fixed; inset: 0; z-index: -1; pointer-events: none;
  background:
    radial-gradient(900px 520px at 82% -8%, color-mix(in srgb, var(--warm) 32%, transparent), transparent 60%),
    radial-gradient(760px 520px at 8% 4%, color-mix(in srgb, var(--accent) 16%, transparent), transparent 58%);
}
/* (Le grain global existe déjà via le div .grain — voir plus bas. Un body::after
   ajouté ici en doublon a été retiré : deux pellicules superposées, inutile.
   Pour plus de matière, on renforce le .grain existant, pas un second calque.) */

.container { width: 100%; max-width: var(--max); margin: 0 auto; padding: 0 28px; }

h1, h2, h3 { font-family: var(--font-head); line-height: 1.12; letter-spacing: -.01em; font-weight: 500; }

a { color: inherit; text-decoration: none; }

/* Lien d'évitement — hors écran jusqu'au focus, puis visible en haut à gauche. */
.skip-link { position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--accent); color: #08110f; padding: 10px 16px; border-radius: 0 0 8px 0;
  font-weight: 600; font-size: 14px; }
.skip-link:focus { left: 0; }

/* ===== BOUTONS ===== */
.btn {
  display: inline-flex; align-items: center; justify-content: center;
  gap: 8px; padding: 13px 26px; border-radius: 999px;
  font-weight: 500; font-size: 15px; cursor: pointer; border: 1px solid transparent;
  transition: transform .15s ease, box-shadow .25s ease, background .2s ease, border-color .2s ease, opacity .2s ease;
  white-space: nowrap;
}
.btn:active { transform: translateY(1px); }
.btn-primary { background: var(--accent); color: var(--accent-ink); box-shadow: var(--glow); }
.btn-primary:hover { transform: translateY(-2px); box-shadow: 0 14px 40px rgba(15, 169, 160, .42); }
.btn-ghost {
  background: var(--glass); color: var(--text);
  border-color: var(--border-strong); backdrop-filter: blur(8px);
}
.btn-ghost:hover { border-color: var(--accent); color: var(--accent); }
.btn-block { width: 100%; }

/* ===== HEADER ===== */
.site-header {
  position: sticky; top: 0; z-index: 50;
  background: color-mix(in srgb, var(--bg) 70%, transparent);
  backdrop-filter: blur(16px) saturate(120%);
  border-bottom: 1px solid transparent;
  transition: border-color .3s ease, background .3s ease;
}
.site-header.scrolled { border-bottom-color: var(--border); }
.nav { display: flex; align-items: center; justify-content: space-between; height: 72px; }
.logo { display: inline-flex; align-items: center; gap: 9px; font-family: var(--font-head); font-weight: 600; font-size: 22px; letter-spacing: -.01em; }
.logo-mark { width: 26px; height: 26px; flex: none; }
.logo-dot { color: var(--accent); }
/* wordmark de l'en-tête : PRAZILE, Z rouge (raccord avec le PRAZILE du splash) */
.logo-word { font-family: var(--font-head); font-weight: 700; font-size: 22px; letter-spacing: .01em; color: var(--text); }
.logo-word b { font-weight: 700; color: var(--pz-red); }
.pz-dot { color: var(--pz-red); }
.emb-word .pz-dot { color: var(--pz-red);
  text-shadow: 0 1px 0 rgba(255,180,170,.28), 0 -1px 2px rgba(0,0,0,.55), 0 0 12px rgba(226,59,48,.5); }

/* le point rouge du logo glitche brièvement toutes les ~3,5 s */
.logo-word .pz-dot { display: inline-block; will-change: transform; animation: pzDotGlitch 3.6s infinite; }
@keyframes pzDotGlitch {
  0%, 85%, 100% { transform: translate(0, 0); text-shadow: none; }
  86% { transform: translate(-2px, -1px) scale(1.12); text-shadow: 2px 0 rgba(255,70,120,.9); }
  88% { transform: translate(3px, 1px); text-shadow: -3px 0 rgba(150,0,30,.95); opacity: .55; }
  90% { transform: translate(-2px, 1px) scale(.9); opacity: 1; }
  92% { transform: translate(2px, -1px); text-shadow: 2px 0 rgba(255,70,120,.9); }
  93% { transform: translate(0, 0); text-shadow: none; }
  95% { opacity: .3; }
  96% { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) { .logo-word .pz-dot { animation: none; } }

/* Le point débarque comme un signal corrompu — dans le vocabulaire EXACT du
   point du logo (pzDotGlitch, juste au-dessus), qui fait référence : mêmes
   fantômes rose/rouge sombre, même amplitude (±3 px), même échelle discrète
   (1,12 / 0,9), mêmes creux d'opacité (.55 / .3).

   Il était auparavant étiré en barres (scale 2.4 / .22) et projeté à ±21 px :
   le point du splash ne se lisait pas comme LE point de la marque, mais comme
   un autre effet, sur un autre site. C'est le premier écran — c'est là que la
   signature doit être la plus juste.

   Ce qui reste propre à l'ARRIVÉE, et seulement ça : les coupures franches à 0
   (il se matérialise, il ne fait pas que trembler) et la durée — 1100 ms contre
   ~360. La violence vient donc de la DENSITÉ, pas de l'amplitude, et elle
   décroît : le signal s'accroche, lutte, se verrouille. Ce qui prépare la
   charge, puis la détonation. */
@keyframes pzDotArrive {
  0%   { opacity: 0; transform: translate(0,0); text-shadow: none; }
  /* il s'accroche : coupures franches, sauts serrés */
  3%   { opacity: 1; transform: translate(-2px, -1px) scale(1.12); text-shadow: 2px 0 rgba(255,70,120,.9); }
  6%   { opacity: 0; transform: translate(-2px, -1px) scale(1.12); }
  9%   { opacity: 1; transform: translate(3px, 1px); text-shadow: -3px 0 rgba(150,0,30,.95); }
  13%  { opacity: .3; transform: translate(-2px, 1px) scale(.9); }
  17%  { opacity: 1; transform: translate(2px, -1px) scale(1.12); text-shadow: 2px 0 rgba(255,70,120,.9); }
  20%  { opacity: 0; }
  24%  { opacity: 1; transform: translate(-3px, -1px) scale(1.1); text-shadow: -3px 0 rgba(150,0,30,.95); }
  29%  { opacity: .55; transform: translate(2px, 2px) scale(.9); text-shadow: 2px 0 rgba(255,70,120,.9); }
  33%  { opacity: .3; transform: translate(2px, 2px) scale(.9); }
  38%  { opacity: 1; transform: translate(-2px, -1px) scale(1.12); text-shadow: -3px 0 rgba(150,0,30,.95); }
  43%  { opacity: 1; transform: translate(3px, 1px); text-shadow: 2px 0 rgba(255,70,120,.9); }
  48%  { opacity: 0; }
  /* il se verrouille : plus de coupures, l'amplitude tombe */
  52%  { opacity: 1; transform: translate(-2px, 1px) scale(1.08); text-shadow: 2px 0 rgba(255,70,120,.9); }
  58%  { opacity: 1; transform: translate(2px, -1px) scale(.94); text-shadow: -2px 0 rgba(150,0,30,.95); }
  63%  { opacity: .3; transform: translate(-1px, 1px); }
  69%  { opacity: 1; transform: translate(2px, 1px) scale(1.05); text-shadow: 2px 0 rgba(255,70,120,.9); }
  74%  { opacity: .55; }
  79%  { opacity: 1; transform: translate(-1px, -1px) scale(.97); text-shadow: -2px 0 rgba(150,0,30,.95); }
  85%  { opacity: 1; transform: translate(1px, 1px); text-shadow: 2px 0 rgba(255,70,120,.9); }
  91%  { opacity: 1; transform: translate(-1px, 0); text-shadow: none; }
  100% { opacity: 1; transform: none; text-shadow: none; }
}

/* ===== LOGO GLITCH "PRAZILE" (en-tête) ===== */
.pz-glitch { position: relative; display: inline-block; line-height: 1;
  font-family: var(--font-head), "Segoe UI", Roboto, monospace;
  font-size: 21px; font-weight: 700; letter-spacing: 3px;
  color: #c8f7ff; text-shadow: 0 0 8px rgba(0, 240, 255, .5); }
.pz-glitch b { font-weight: 700; color: #ff003c; text-shadow: 0 0 8px #ff003c; }
.pz-glitch::before, .pz-glitch::after { content: attr(data-text); position: absolute; left: 0; top: 0;
  width: 100%; overflow: hidden; pointer-events: none; }
.pz-glitch::before { color: #ff003c; text-shadow: 0 0 6px #ff003c; transform: translate(-1px, 0);
  animation: pzGlitchA 2.6s infinite linear alternate-reverse; }
.pz-glitch::after { color: #00f0ff; text-shadow: 0 0 6px #00f0ff; transform: translate(1px, 0);
  animation: pzGlitchB 2s infinite linear alternate-reverse; }
@keyframes pzGlitchA {
  0%, 92%, 100% { clip-path: inset(0 0 100% 0); transform: translate(0, 0); }
  93% { clip-path: inset(20% 0 40% 0); transform: translate(-2px, -1px); }
  95% { clip-path: inset(60% 0 10% 0); transform: translate(2px, 1px); }
  97% { clip-path: inset(10% 0 70% 0); transform: translate(-1px, 1px); }
}
@keyframes pzGlitchB {
  0%, 90%, 100% { clip-path: inset(100% 0 0 0); transform: translate(0, 0); }
  91% { clip-path: inset(40% 0 30% 0); transform: translate(2px, 1px); }
  94% { clip-path: inset(10% 0 60% 0); transform: translate(-2px, -1px); }
  96% { clip-path: inset(70% 0 5% 0); transform: translate(1px, -1px); }
}
/* thème clair (papier) : base lisible en encre, halo teal discret — les calques rouge/cyan claquent toujours */
@media (prefers-reduced-motion: reduce) { .pz-glitch::before, .pz-glitch::after { animation: none; } }
.nav-links { display: flex; align-items: center; gap: 30px; list-style: none; }
.nav-links a { color: var(--text-soft); font-weight: 450; font-size: 15px; transition: color .2s ease; }
.nav-links a:hover { color: var(--text); }
.nav-cta {
  padding: 8px 18px; border-radius: 999px;
  background: var(--accent); color: var(--accent-ink) !important; font-weight: 500;
  box-shadow: var(--glow);
}
.nav-cta:hover { opacity: .92; }

.nav-toggle {
  background: var(--glass); border: 1px solid var(--border-strong);
  color: var(--text); cursor: pointer; border-radius: 10px;
  width: 40px; height: 40px; display: inline-flex; align-items: center; justify-content: center;
  transition: border-color .2s ease; backdrop-filter: blur(8px);
}
.nav-toggle:hover { border-color: var(--accent); }

.nav-toggle { display: none; flex-direction: column; gap: 5px; }
.nav-toggle span { width: 18px; height: 1.5px; background: var(--text); border-radius: 2px; transition: .25s; }

/* ===== HERO ===== */
/* .hero porte l'habillage commun ; le hero actuel ajoute .hero-soft (voir plus
   bas). Tout l'ancien hero « panneau HUD » (grille, panneau de verre, stats,
   coins turquoise) a été retiré le 2026-07-16 : son markup avait disparu
   d'index.html lors de la refonte Canvas, seul le CSS orphelin restait. */
.hero { position: relative; padding: 96px 0 88px; overflow: hidden; }

/* ===== SECTIONS ===== */
.section { padding: 84px 0; position: relative; }
/* Pas de border-block ici : le filet blanc haut/bas se posait pile sous
   l'hélice du séparateur (et sous la section suivante) — le « filet blanc »
   signalé. Les hélices assurent déjà la transition ; la teinte de fond suffit
   à distinguer la bande. */
.section-alt { background: color-mix(in srgb, var(--bg-alt) 60%, transparent); }

/* Séparateur entre sections : une DOUBLE HÉLICE d'ADN en poussière de points
   rouges, très fine, qui tourne en continu. Écho du nuage du hero (des points, du
   Canvas), et « pulsation = vie » sans jamais se figer. Les points passent de
   l'avant (nets) à l'arrière (fondus) au fil de la rotation → relief 3D + mouvement
   perpétuel. Rendu en Canvas (voir script.js) ; il ne tourne que quand le
   séparateur est à l'écran, et s'arrête en reduced-motion (image fixe). */
/* 58 px et non 46 : il faut de la marge au-dessus et en dessous pour que
   l'hélice puisse se gonfler sous la musique sans se faire couper par son
   propre cadre (voir render(), le facteur 0,238 garde son rayon AU REPOS
   rigoureusement identique — le bandeau grandit, pas l'hélice). */
.dna-sep { height: 58px; margin: -6px 0; pointer-events: none; }
.dna-sep canvas { display: block; width: 100%; height: 100%; }
.section-head { max-width: 560px; margin: 0 auto 46px; text-align: center; }
.section-tag {
  display: inline-block; font-family: var(--font-mono);
  font-size: 13px; font-weight: 500; letter-spacing: .02em;
  color: var(--accent); margin-bottom: 16px;
}
.section-tag::before { content: "// "; opacity: .55; }
.section-head h2 { font-size: clamp(1.9rem, 4vw, 2.8rem); font-weight: 400; margin-bottom: 14px; }
.section-head p { color: var(--text-soft); font-size: 1.05rem; }

/* ===== CARTES SERVICES ===== */
.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(232px, 1fr)); gap: 18px; }
.card {
  position: relative; background: var(--glass); border: 1px solid var(--glass-border);
  border-radius: var(--radius); padding: 30px 28px;
  backdrop-filter: blur(12px); box-shadow: var(--shadow);
  transition: transform .2s ease, box-shadow .2s ease, border-color .2s ease;
}
.card:hover { transform: translateY(-5px); box-shadow: var(--shadow-lg); border-color: color-mix(in srgb, var(--accent) 40%, var(--glass-border)); }
.card-num {
  font-family: var(--font-mono); font-weight: 500; font-size: 13px;
  letter-spacing: .04em; color: var(--accent); margin-bottom: 20px;
  padding-bottom: 16px; border-bottom: 1px solid var(--border); display: block;
}
.card h3 { font-size: 1.18rem; font-weight: 500; margin-bottom: 10px; }
.card p { color: var(--text-soft); font-size: .95rem; }

/* La corde en plus : la musique. Une bande, pas une carte — un « aussi » qui
   surprend. Filet rouge à gauche (signature) + un petit égaliseur qui bouge :
   l'audio qui vit, comme le reste du site. */
.also { display: flex; align-items: center; gap: 24px; margin-top: 20px;
  background: var(--glass); border: 1px solid var(--glass-border); border-left: 2px solid var(--pz-red);
  border-radius: var(--radius); padding: 24px 30px; backdrop-filter: blur(12px); }
.also-txt h3 { font-size: 1.15rem; font-weight: 500; margin-bottom: 7px; }
.also-txt p { color: var(--text-soft); font-size: .96rem; max-width: 64ch; }
.also-eq { display: flex; align-items: flex-end; gap: 3px; height: 32px; flex: 0 0 auto; }
/* AU REPOS par défaut. Il gigotait en permanence — il mimait un son qui
   n'existait pas, sur un site muet. Il ne bouge donc plus que quand il y a
   vraiment quelque chose à montrer (html.amb-on, posé par ambience.js) : la
   bande prend vie à l'instant exact où on l'allume, ce qui est tout l'intérêt.
   La transition adoucit le retour au repos quand on coupe. */
.also-eq i { width: 3px; border-radius: 2px; background: var(--pz-red);
  height: 60%; transition: height .5s ease; }
.also-eq i:nth-child(2), .also-eq i:nth-child(5) { height: 100%; }
.also-eq i:nth-child(3), .also-eq i:nth-child(6) { height: 40%; }
.also-eq i:nth-child(4) { height: 26%; }
.also-eq i:nth-child(7) { height: 74%; }
/* Pendant la lecture, ambience.js écrit la hauteur À CHAQUE IMAGE : la
   transition de .5s ci-dessus transformerait le spectre en bouillie (chaque
   valeur mettrait une demi-seconde à arriver, la suivante l'écraserait avant).
   On la coupe donc tant que ça joue ; elle ne sert qu'au retour au repos. */
.amb-on .also-eq i { transition: none; }

/* Plus d'animation CSS quand ça joue : ambience.js pilote les hauteurs en
   direct, lues dans le spectre du morceau. Une @keyframes ici GAGNERAIT contre
   le style inline (les animations passent devant, même inline) et le vrai
   signal serait écrasé par une fausse danse. Cet égaliseur montre enfin ce
   qu'il prétendait montrer ; la transition ci-dessus adoucit son retour au
   repos quand on coupe. */
@media (max-width: 640px) {
  .also { flex-direction: column; align-items: flex-start; gap: 16px; padding: 22px 22px; }
}
@media (prefers-reduced-motion: reduce) {
  .amb-on .also-eq i { animation: none; }
}

/* --- L'ambiance sonore -----------------------------------------------
   Allumer le son est une ACTION → turquoise. Le rouge reste la signature, il
   n'a rien à faire ici. Registre « fantôme » comme .btn-ghost : c'est une
   invitation discrète, pas l'appel à l'action de la page — le vrai CTA du site
   reste le devis, et deux boutons qui crient aussi fort ne valent qu'un.
   [hidden] doit gagner contre le display du bouton : sans cette règle, un
   navigateur sans Web Audio afficherait quand même l'invitation. */
/* Les deux commandes voyagent ensemble, poussées à droite du bandeau. */
.also-cmd { margin-left: auto; flex: 0 0 auto; display: flex; align-items: center; gap: 10px; }
.also-play[hidden], .also-zen[hidden] { display: none; }
.also-play {
  flex: 0 0 auto; white-space: nowrap;
  display: inline-flex; align-items: center; gap: 9px;
  padding: 11px 18px; border-radius: 999px; cursor: pointer;
  background: var(--glass); color: var(--text-soft);
  border: 1px solid var(--border-strong);
  font: inherit; font-size: .88rem; font-weight: 500;
  transition: border-color .2s, color .2s;
}
.also-play:hover { border-color: var(--accent); color: var(--accent); }
.also-play.on { border-color: var(--accent); color: var(--accent); }
.also-play:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

/* Le mode zen : registre SECONDAIRE, volontairement. C'est un repli, pas une
   invitation — il ne doit pas concurrencer « Écouter l'atelier », qui est le
   geste que le bandeau vient de proposer. D'où pas de bordure au repos, une
   taille moindre, et le turquoise seulement une fois enclenché (c'est une
   action, donc jamais le rouge signature). */
.also-zen {
  flex: 0 0 auto; white-space: nowrap; cursor: pointer;
  padding: 9px 13px; border-radius: 999px;
  background: none; color: var(--faible, #8f8469);
  border: 1px solid transparent;
  font: inherit; font-size: .82rem; font-weight: 500;
  transition: color .2s, border-color .2s, background-color .2s;
}
.also-zen:hover { color: var(--text-soft); border-color: var(--border); }
.also-zen.on {
  color: var(--accent); border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 10%, transparent);
}
.also-zen:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

/* =====================================================================
   MODE ZEN « FONDU » — on clique, le contenu s'efface (fondu + léger flou),
   il ne reste que la sphère centrée qui respire avec la musique. On revient
   par le fondu inverse. Pas de particules : les rendre nettes sur toute la
   page en Canvas2D est infaisable (il faudrait du WebGL). Orchestré dans
   ambience.js. Sobre, sûr, calme.
   ===================================================================== */

/* La transition est posée par zen-fade-ready (présente pendant tout le zen) :
   elle vaut donc à l'aller ET au retour. zen-fade porte l'état effacé. */
.zen-fade-ready .site-header,
.zen-fade-ready main > section:not(#accueil),
.zen-fade-ready main > .dna-sep,
.zen-fade-ready .site-footer,
.zen-fade-ready #accueil .hero-soft-inner {
  transition: opacity .6s ease, filter .6s ease;
}
.zen-fade .site-header,
.zen-fade main > section:not(#accueil),
.zen-fade main > .dna-sep,
.zen-fade .site-footer,
.zen-fade #accueil .hero-soft-inner {
  opacity: 0; filter: blur(8px); pointer-events: none;
}

/* La sphère prend tout l'écran, fixe et centrée (le moteur centre le nuage),
   sur le fond sombre. Elle éclôt une fois le contenu effacé (décalage dans le
   JS). Rien d'autre : ni texte, ni masque latéral, ni habillage. */
.zen-immersed #accueil {
  position: fixed; inset: 0; height: 100vh; min-height: 0;
  margin: 0; padding: 0; z-index: 9000;
  background: var(--bg);
  animation: zenBloom .7s ease both;
}
@keyframes zenBloom { from { opacity: 0; } to { opacity: 1; } }
.zen-immersed #accueil .hero-halftone { -webkit-mask-image: none; mask-image: none; }
/* ATTENTION : #accueil EST .hero-soft — la même section porte l'identifiant et
   les deux classes. Les règles écrites « #accueil .hero-soft::after », avec une
   espace, cherchaient un descendant qui n'existe pas : elles n'ont jamais rien
   retiré. Il faut donc viser #accueil directement. */
.zen-immersed #accueil::after,
.zen-immersed #accueil::before { display: none; }
/* La vignette et le grain sont des ÉLÉMENTS, pas des pseudo-éléments : ils
   échappaient à la règle ci-dessus. La vignette est le vrai coupable du
   « masque » sur mobile — son ouverture y est calée à 36 % de hauteur, là où le
   nuage se tient en mise en page normale, alors qu'en zen la sphère est
   recentrée. Elle lui mangeait donc le bas. Le zen ne garde que la sphère. */
.zen-immersed #accueil .hero-vig,
.zen-immersed #accueil .hero-noise { display: none; }
.zen-immersed #accueil .hero-soft-inner { display: none; }   /* pas de texte, juste la sphère */
.zen-immersed .amb-float { display: none; }                  /* la sortie du zen, c'est le ✕ */

/* La sortie : discrète mais toujours là (avec Échap et le clic sur le fond). */
.zen-quit { position: fixed; z-index: 9600; right: 22px; top: 20px;
  display: inline-flex; align-items: center; gap: 8px; cursor: pointer;
  padding: 9px 15px; border-radius: 999px; font: inherit; font-size: .82rem;
  background: rgba(16,13,10,.5); backdrop-filter: blur(8px);
  border: 1px solid var(--border); color: var(--text-soft);
  opacity: 0; transform: translateY(-6px);
  transition: opacity .5s ease .35s, transform .5s ease .35s, color .2s, border-color .2s; }
.zen-quit.vu { opacity: 1; transform: none; }
.zen-quit:hover { color: var(--accent); border-color: var(--accent); }
.zen-quit:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

@media (prefers-reduced-motion: reduce) {
  /* On garde le mode (la sphère + la musique), on retire le mouvement : le
     contenu disparaît net (pas de fondu ni de flou), la sphère n'éclôt pas. */
  .zen-fade-ready .site-header, .zen-fade-ready main > section:not(#accueil),
  .zen-fade-ready main > .dna-sep, .zen-fade-ready .site-footer,
  .zen-fade-ready #accueil .hero-soft-inner { transition: none; }
  .zen-fade #accueil .hero-soft-inner, .zen-fade .site-header,
  .zen-fade main > section:not(#accueil), .zen-fade .site-footer { filter: none; }
  .zen-immersed #accueil { animation: none; }
  .zen-quit { transition: color .2s, border-color .2s; }
}

/* Trois barres : au repos quand c'est muet, elles bougent quand ça joue. Le
   bouton dit donc son état sans qu'on ait à lire son libellé. */
.also-play-ico { display: flex; align-items: center; gap: 2px; height: 13px; flex: 0 0 auto; }
.also-play-ico i { width: 2px; height: 4px; border-radius: 1px; background: currentColor; }
.also-play-ico i:nth-child(2) { height: 9px; }
.also-play.on .also-play-ico i { animation: ambBar .9s ease-in-out infinite; }
.also-play.on .also-play-ico i:nth-child(2) { animation-delay: -.45s; }
.also-play.on .also-play-ico i:nth-child(3) { animation-delay: -.7s; }
@keyframes ambBar { 0%, 100% { height: 3px; } 50% { height: 13px; } }

/* La sortie. Un son qu'on ne sait plus couper une fois la page défilée est un
   piège ; mais un bouton « son » permanent sur un site muet est du bruit
   visuel. Il n'existe donc que le temps où il sert — créé par ambience.js au
   démarrage, retiré à l'arrêt. */
.amb-float {
  position: fixed; right: 18px; bottom: 18px; z-index: 60;
  width: 42px; height: 42px; border-radius: 50%; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  background: rgba(16, 13, 10, .82); backdrop-filter: blur(10px);
  border: 1px solid var(--accent); color: var(--accent);
  opacity: 0; transform: translateY(8px) scale(.9);
  transition: opacity .24s ease, transform .24s ease, background-color .2s;
}
.amb-float.vu { opacity: 1; transform: none; }
.amb-float:hover { background: rgba(45, 212, 191, .14); }
.amb-float:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.amb-float-eq { display: flex; align-items: center; gap: 2px; height: 14px; }
.amb-float-eq i { width: 2px; height: 4px; border-radius: 1px; background: currentColor;
  animation: ambBar .9s ease-in-out infinite; }
.amb-float-eq i:nth-child(2) { animation-delay: -.45s; }
.amb-float-eq i:nth-child(3) { animation-delay: -.7s; }

@media (max-width: 640px) {
  /* En colonne, le groupe de commandes se range simplement sous le texte, à
     gauche (le margin-left:auto n'a plus rien à pousser). */
  .also-cmd { margin-left: 0; }
  .amb-float { right: 14px; bottom: 14px; }
}
/* Le son reste possible en reduced-motion — c'est du mouvement qu'on ne veut
   pas, pas de l'audio. Seules les barres cessent de gigoter. */
@media (prefers-reduced-motion: reduce) {
  .also-play-ico i, .amb-float-eq i { animation: none; }
  .amb-float-eq i:nth-child(2) { height: 11px; }
  .amb-float { transition: opacity .01s; }
}

/* ===== PROJETS ===== */
.projects { display: flex; flex-wrap: wrap; gap: 20px; justify-content: center; }
.project {
  flex: 1 1 320px; max-width: 352px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); overflow: hidden; box-shadow: var(--shadow);
  transition: transform .2s ease, box-shadow .2s ease, border-color .2s ease;
}
@media (max-width: 680px) { .project { flex-basis: 100%; max-width: none; } }
.project { background: var(--glass); backdrop-filter: blur(12px); }   /* le sombre est devenu la seule règle */
.project:hover { transform: translateY(-5px); box-shadow: var(--shadow-lg); border-color: var(--border-strong); }
.project-thumb {
  position: relative; height: 220px; overflow: hidden;
  border-bottom: 1px solid var(--border); background: var(--bg-alt);
}
/* Fond flouté tiré du poster : remplit la vignette pour les médias portrait */
.project-thumb::before {
  content: ""; position: absolute; inset: 0; z-index: 0;
  background-image: var(--poster); background-size: cover; background-position: center;
  filter: blur(24px) saturate(1.2) brightness(.5); transform: scale(1.25);
}
.project-thumb img, .project-thumb video {
  position: relative; z-index: 1;
  width: 100%; height: 100%; object-fit: contain; object-position: center;
  display: block;
}
/* Filet supérieur des vignettes projet : décoratif, donc PAS turquoise (la charte
   réserve le turquoise à l'action). Repassé en filet neutre discret. */
.project-thumb::after { content: ""; position: absolute; top: 0; left: 0; right: 0; height: 2px; background: var(--border-strong); opacity: .85; z-index: 2; }
.play-hint {
  position: absolute; bottom: 12px; right: 12px; z-index: 3;
  width: 34px; height: 34px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-size: 11px; padding-left: 2px;
  color: var(--accent-ink); background: var(--accent); box-shadow: var(--glow);
  opacity: .92; transition: opacity .25s ease, transform .25s ease;
}
.project:hover .play-hint { opacity: 0; transform: scale(.8); }

/* Carte cliquable + badge "Essayer" / "Voir le site" */
.has-demo, .has-link { cursor: pointer; }
.has-demo:focus-visible, .has-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.demo-badge {
  position: absolute; top: 12px; left: 12px; z-index: 3;
  font-family: var(--font-mono); font-size: 11px; font-weight: 500; letter-spacing: .03em;
  padding: 6px 11px; border-radius: 999px;
  color: var(--accent-ink); background: var(--accent); box-shadow: var(--glow);
  transition: transform .2s ease;
}
.has-demo:hover .demo-badge { transform: translateY(-2px); }

/* ===== Bonus « cadeau » : la carte Ballast + sa transition ===== */
.has-gift { cursor: pointer; }
.has-gift:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.ballast-thumb {
  display: flex; align-items: center; justify-content: center;
  background: radial-gradient(120% 120% at 50% 40%, #17120d 0%, #100d0a 72%);
}
.ballast-mark {
  display: flex; align-items: baseline; gap: .04em;
  font-family: 'Outfit', var(--font-head), sans-serif; font-weight: 800;
  font-size: 116px; line-height: 1; letter-spacing: -.02em; color: #f3ede2;
  transition: transform .3s ease;
}
.ballast-mark .d {
  width: .17em; height: .17em; border-radius: 50%; background: #e2231a;
  align-self: flex-end; margin-bottom: .09em; box-shadow: 0 0 18px rgba(226,35,26,.55);
}
.has-gift:hover .ballast-mark { transform: translateY(-2px) scale(1.03); }
.ballast-ribbon {
  position: absolute; top: 12px; right: 12px; z-index: 3;
  font-family: var(--font-mono); font-size: 11px; font-weight: 500;
  letter-spacing: .1em; text-transform: uppercase; padding: 5px 10px; border-radius: 999px;
  color: #f3ede2; background: rgba(226,35,26,.16); border: 1px solid rgba(226,35,26,.5);
}

/* Transition plein écran : révélation circulaire depuis le clic vers un noir chaud */
#gift-transition {
  position: fixed; inset: 0; z-index: 3000; pointer-events: none;
  display: flex; align-items: center; justify-content: center;
  background: radial-gradient(120% 120% at 50% 42%, #17120d 0%, #0c0a07 72%);
  clip-path: circle(0 at var(--gx, 50%) var(--gy, 50%));
  transition: clip-path .62s cubic-bezier(.7, 0, .3, 1);
}
#gift-transition.go { clip-path: circle(150% at var(--gx, 50%) var(--gy, 50%)); pointer-events: all; }
#gift-transition .gt-mark {
  display: flex; align-items: baseline; gap: .04em;
  font-family: 'Outfit', var(--font-head), sans-serif; font-weight: 800;
  font-size: clamp(64px, 16vw, 140px); line-height: 1; letter-spacing: -.02em; color: #f3ede2;
  opacity: 0; transform: scale(.9);
  transition: opacity .4s ease .18s, transform .55s cubic-bezier(.2, .9, .3, 1.25) .18s;
}
#gift-transition .gt-mark .d {
  width: .17em; height: .17em; border-radius: 50%; background: #e2231a;
  align-self: flex-end; margin-bottom: .09em; box-shadow: 0 0 22px rgba(226,35,26,.6);
}
#gift-transition.go .gt-mark { opacity: 1; transform: none; }
#gift-transition .gt-cap {
  position: absolute; bottom: 15%; font-family: var(--font-mono);
  font-size: 12px; letter-spacing: .2em; text-transform: uppercase; color: #8f8469;
  opacity: 0; transition: opacity .4s ease .38s;
}
#gift-transition.go .gt-cap { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  #gift-transition, #gift-transition .gt-mark, #gift-transition .gt-cap { transition: none; }
}

/* ===== FENÊTRE DE DÉMO ===== */
.demo-modal { position: fixed; inset: 0; z-index: 200; display: none; }
.demo-modal.open { display: block; }
.demo-backdrop { position: absolute; inset: 0; background: rgba(8, 8, 10, .72); backdrop-filter: blur(6px); }
.demo-window {
  position: relative; z-index: 1; display: flex; flex-direction: column;
  width: min(1100px, 94vw); height: min(86vh, 900px); margin: 5vh auto 0;
  background: var(--surface); border: 1px solid var(--glass-border);
  border-radius: 16px; overflow: hidden; box-shadow: var(--shadow-lg);
}
.demo-bar {
  display: flex; align-items: center; gap: 14px; padding: 12px 16px;
  border-bottom: 1px solid var(--border); background: var(--bg-alt); font-family: var(--font-mono);
}
.demo-name { font-weight: 500; font-size: 13px; }
.demo-timer { margin-left: auto; font-size: 12px; color: var(--accent); letter-spacing: .06em; }
.demo-timer.low { color: #e5484d; }
.demo-close {
  background: transparent; border: 1px solid var(--border-strong); color: var(--text);
  width: 32px; height: 32px; border-radius: 8px; cursor: pointer; transition: border-color .2s ease, color .2s ease;
}
.demo-close:hover { border-color: var(--accent); color: var(--accent); }
.demo-stage { position: relative; flex: 1; background: var(--bg); }
.demo-frame { width: 100%; height: 100%; border: 0; display: block; background: #fff; }
.demo-lock {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  text-align: center; padding: 24px;
  background: color-mix(in srgb, var(--bg) 82%, transparent); backdrop-filter: blur(16px);
}
.demo-lock[hidden] { display: none; }
.demo-lock-inner { max-width: 380px; }
.demo-lock-tag { font-family: var(--font-mono); font-size: 12px; letter-spacing: .14em; color: var(--accent); }
.demo-lock-title { font-family: var(--font-head); font-size: 1.6rem; font-weight: 500; margin: 10px 0 8px; }
.demo-lock-sub { color: var(--text-soft); margin-bottom: 22px; }
.demo-lock-actions { display: flex; gap: 12px; justify-content: center; flex-wrap: wrap; }
body.demo-open { overflow: hidden; }
@media (max-width: 600px) {
  .demo-window { width: 100vw; height: 100dvh; margin: 0; border-radius: 0; border: 0; }
}
.project-body { padding: 24px; }
.project-body h3 { font-size: 1.08rem; font-weight: 500; margin-bottom: 8px; }
.project-body p { color: var(--text-soft); font-size: .94rem; margin-bottom: 18px; }
.tags { display: flex; flex-wrap: wrap; gap: 8px; }
.tags span {
  font-family: var(--font-mono); font-size: 11px; font-weight: 500;
  padding: 5px 10px; border-radius: 7px;
  background: var(--bg-alt); color: var(--text-soft); border: 1px solid var(--border);
}

/* ===== À PROPOS ===== */
.about { display: grid; grid-template-columns: 1.5fr 1fr; gap: 56px; align-items: start; }
.about-text h2 { margin: 14px 0 20px; font-size: clamp(1.8rem, 3.5vw, 2.5rem); font-weight: 400; }
.about-text p { color: var(--text-soft); margin-bottom: 16px; }
.skills { list-style: none; display: flex; flex-wrap: wrap; gap: 9px; margin-top: 26px; }
.skills li {
  font-family: var(--font-mono); font-size: 13px; font-weight: 500;
  padding: 7px 13px; border-radius: 8px;
  background: var(--glass); border: 1px solid var(--border); color: var(--text-soft);
}
.about-card {
  position: relative; background: var(--glass); border: 1px solid var(--glass-border);
  border-radius: var(--radius); padding: 34px; box-shadow: var(--shadow);
  backdrop-filter: blur(14px);
}
/* Le wordmark remplace le carré turquoise : c'est le logo du site, et il porte
   la signature — le Z et le point en rouge. Le carré était l'ancien logo, et il
   employait le turquoise en décor alors que la charte le réserve à l'action.
   Plus de lueur non plus (--glow) : l'affinage du 2026-07-09 la bannit. */
.about-mark {
  font-family: var(--font-head); font-weight: 800; font-size: 1.6rem;
  letter-spacing: -.02em; color: var(--text); margin-bottom: 4px;
}
.about-mark b { color: var(--pz-red); font-weight: 800; }
.about-role { font-family: var(--font-mono); font-size: 13px; color: var(--text-soft); margin-bottom: 24px; }
.about-list { list-style: none; display: grid; gap: 12px; }
.about-list li { color: var(--text-soft); font-size: .95rem; padding-left: 24px; position: relative; }
/* Le tiret passe au rouge : c'est un ornement, pas une action. Il devient la
   signature au lieu d'usurper la couleur des boutons. */
.about-list li::before {
  content: ""; position: absolute; left: 0; top: .55em;
  width: 12px; height: 1.5px; background: var(--pz-red);
}

/* ===== CONTACT ===== */
.contact { max-width: 680px; }
.contact-form { display: grid; gap: 18px; }
.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
.field { display: flex; flex-direction: column; gap: 8px; }
.field label { font-family: var(--font-mono); font-size: 12px; font-weight: 500; color: var(--text-soft); }
.field input, .field textarea {
  font-family: inherit; font-size: 15px; color: var(--text);
  background: var(--glass); border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm); padding: 13px 15px; backdrop-filter: blur(8px);
  transition: border-color .2s ease, box-shadow .2s ease; resize: vertical;
}
.field input::placeholder, .field textarea::placeholder { color: color-mix(in srgb, var(--text-soft) 70%, transparent); }
.field input:focus, .field textarea:focus {
  outline: none; border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 18%, transparent);
}
.field input.invalid, .field textarea.invalid { border-color: #e5484d; }
.form-note { text-align: center; font-size: 14px; min-height: 20px; }
.form-note.ok { color: var(--accent); }
.form-note.err { color: #e5484d; }
.contact-direct { text-align: center; margin-top: 28px; color: var(--text-soft); font-size: .95rem; }
.contact-direct a { color: var(--accent); font-weight: 500; border-bottom: 1px solid color-mix(in srgb, var(--accent) 35%, transparent); }
.contact-direct a:hover { border-bottom-color: var(--accent); }

/* ===== FOOTER ===== */
.site-footer { border-top: 1px solid var(--border); padding: 40px 0; }
.footer-inner { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 16px; }
.footer-inner p { color: var(--text-soft); font-size: 13.5px; }
/* flex-wrap : cinq liens en ligne font ~441px et débordaient l'écran mobile
   (375px), forçant une barre de défilement horizontale. Ils reviennent à la
   ligne, centrés, quand la place manque. */
.footer-links { display: flex; flex-wrap: wrap; justify-content: center; gap: 16px 24px; }
.footer-links a { color: var(--text-soft); font-size: 14px; transition: color .2s ease; }
.footer-links a:hover { color: var(--accent); }

/* Le CSS du blog (.articles-grid, .article-*, .prose) a été retiré le 2026-07-16
   avec les pages articles : il ne servait plus rien et portait plusieurs
   turquoises décoratifs contraires à la charte. */

/* ===== DEVIS =====
   Le CSS de l ancien estimateur (.est-*) et de sa configuration automatique
   par IA (.est-auto-*) vivait ici : 53 lignes devenues orphelines quand les
   deux sections ont fusionné en une. Le devis se style plus bas, sous
   « DEVIS EN TERMINAL ». */

/* ===== ÉCRAN DE DÉMARRAGE (time-lapse typographique : "PRAZILE" 1965 → 2026) ===== */
.boot {
  position: fixed; inset: 0; z-index: 9999;
  background: #000;              /* fond noir du splash */
  transition: opacity .7s ease;
}
.era-stage { position: absolute; inset: 0; overflow: hidden; background-color: #000;
  transition: background-color 380ms ease; }

/* les deux calques du mot — morphing : smear horizontal + gros flou entre époques */
.era-word { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  font-size: clamp(40px, 12vw, 122px); font-weight: 700; white-space: nowrap; opacity: 0; z-index: 2;
  transition: opacity 460ms ease, filter 460ms ease, transform 460ms cubic-bezier(.5,0,.3,1);
  will-change: opacity, filter, transform; }
/* décalage RGB (aberration chromatique) appliqué au mot pendant un à-coup de glitch */
.era-word.glitching { text-shadow: -4px 0 #ff0040, 4px 0 #00e5ff, 0 0 2px #fff !important; }

/* calque de glitch : barres/tranches colorées qui claquent sur tout l'écran */
.era-glitch { position: absolute; inset: 0; z-index: 3; pointer-events: none; opacity: 0;
  mix-blend-mode: screen; will-change: opacity, clip-path; }

/* texture d'époque (bandes de listing, scanlines CRT…) */
.era-fx { position: absolute; inset: 0; z-index: 1; pointer-events: none; opacity: 0; transition: opacity 380ms ease; }
.era-fx.fx-bars { opacity: 1; background: repeating-linear-gradient(0deg, rgba(90,120,60,.16) 0 15px, transparent 15px 30px); }
.era-fx.fx-scan { opacity: .55; background: repeating-linear-gradient(0deg, transparent 0 2px, rgba(0,0,0,.4) 2px 3px); }
.era-fx.fx-twill { opacity: 1; background:
    repeating-linear-gradient(63deg, rgba(255,255,255,.04) 0 1px, transparent 1px 3px),
    repeating-linear-gradient(63deg, rgba(0,0,0,.16) 0 2px, transparent 2px 4px); }

/* état final : la reproduction native du PRAZILE (marine, Z rouge en relief) */
.emb-word { position: absolute; inset: 0; z-index: 3; display: flex; align-items: center; justify-content: center;
  font-family: 'Outfit', sans-serif; font-weight: 800; letter-spacing: .06em;
  opacity: 0; transform: scale(1.05); transition: opacity 560ms ease, transform 900ms cubic-bezier(.2,.8,.2,1);
  will-change: opacity, transform; }
.emb-word span { display: inline-block; color: #cfe7f6; font-size: clamp(46px, 13vw, 132px);
  text-shadow: 0 1px 0 rgba(255,255,255,.16), 0 -1px 2px rgba(0,0,0,.5), 0 0 1px rgba(0,0,0,.45);
  transition: color .8s ease, text-shadow .8s ease; }
.emb-word span.z { color: var(--pz-red);
  text-shadow: 0 1px 0 rgba(255,180,170,.28), 0 -1px 2px rgba(0,0,0,.55), 0 0 12px rgba(226,59,48,.5); }
/* pendant le vol vers l'en-tête : les lettres se recolorent vers la teinte du header (le Z reste rouge) */
.emb-word.flying span:not(.z):not(.pz-dot) { color: var(--text); text-shadow: none; }

/* déploiement « cascade » : la page monte en douceur depuis le haut */
body.pz-deploy main section { animation: pzRise .6s ease both; }
body.pz-deploy main section:nth-of-type(2) { animation-delay: .08s; }
body.pz-deploy main section:nth-of-type(3) { animation-delay: .16s; }
body.pz-deploy main section:nth-of-type(4) { animation-delay: .24s; }
body.pz-deploy main section:nth-of-type(n+5) { animation-delay: .3s; }
@keyframes pzRise { from { opacity: 0; transform: translateY(26px); } to { opacity: 1; transform: none; } }

/* déploiement iris « qui claque » : la page zoome depuis le point + secousse d'impact */
@keyframes pzSlam {
  0%   { transform: scale(1.06); opacity: .5; }
  16%  { transform: scale(1.008) translate(4px, -3px); opacity: 1; }
  30%  { transform: scale(1) translate(-4px, 2px); }
  44%  { transform: translate(3px, 2px); }
  58%  { transform: translate(-2px, -1px); }
  72%  { transform: translate(1px, 1px); }
  100% { transform: scale(1) translate(0, 0); opacity: 1; }
}
/* aberration chromatique (split RGB) sur toute la page à l'impact */
@keyframes pzChroma {
  0%   { filter: none; }
  8%   { filter: drop-shadow(5px 0 rgba(255,0,60,.9)) drop-shadow(-5px 0 rgba(0,229,255,.9)) contrast(1.18); }
  22%  { filter: drop-shadow(-4px 0 rgba(255,0,60,.8)) drop-shadow(4px 0 rgba(0,229,255,.8)); }
  40%  { filter: drop-shadow(3px 0 rgba(255,0,60,.55)) drop-shadow(-3px 0 rgba(0,229,255,.55)); }
  65%  { filter: drop-shadow(1px 0 rgba(255,0,60,.28)) drop-shadow(-1px 0 rgba(0,229,255,.28)); }
  100% { filter: none; }
}

/* --- styles typographiques par époque --- */
.e-1965 { font-family: 'JetBrains Mono', monospace; font-weight: 500; color: #2f3d21;
  letter-spacing: .22em; text-transform: uppercase; text-shadow: 0 0 1px rgba(0,0,0,.5); }
.e-1977 { font-family: 'JetBrains Mono', monospace; font-weight: 800; color: #ffd23f;
  letter-spacing: .18em; text-transform: uppercase;
  text-shadow: 3px 0 #ff2f7a, -3px 0 #2ff0ff, 4px 4px 0 rgba(0,0,0,.55); }
.e-1984 { font-family: 'JetBrains Mono', monospace; font-weight: 500; color: #33ff66;
  letter-spacing: .16em; text-shadow: 0 0 8px rgba(51,255,102,.85), 0 0 22px rgba(51,255,102,.45); }
.e-1994 { font-family: 'Times New Roman', Times, serif; font-weight: 700; color: #0000ee;
  letter-spacing: .01em; text-decoration: underline; }
.e-2001 { font-family: Arial, Helvetica, sans-serif; font-weight: 800; letter-spacing: .02em;
  background: linear-gradient(#ffffff 0%, #cdd8ee 30%, #6b7f9f 49%, #2b3a5c 51%, #8fa0c4 70%, #eef4ff 100%);
  -webkit-background-clip: text; background-clip: text; color: transparent;
  filter: drop-shadow(0 2px 2px rgba(0,0,0,.55)) drop-shadow(0 0 1px rgba(255,255,255,.6)); }
.e-2008 { font-family: Arial, Helvetica, sans-serif; font-weight: 800; letter-spacing: 0;
  background: linear-gradient(#8fd6ff 0%, #269ee6 47%, #0a63b0 53%, #43a9ee 100%);
  -webkit-background-clip: text; background-clip: text; color: transparent;
  filter: drop-shadow(0 1px 0 rgba(255,255,255,.6)); }
.e-2015 { font-family: Arial, Helvetica, sans-serif; font-weight: 300; color: #2c3e50;
  letter-spacing: .16em; text-transform: uppercase; }
.e-2019 { font-family: 'Outfit', sans-serif; font-weight: 600; color: #eafff8; letter-spacing: .08em;
  text-shadow: 0 0 6px #2fd4c6, 0 0 18px #0fa9a0, 0 0 40px rgba(15,169,160,.6); }
.e-2026 { font-family: 'Outfit', sans-serif; font-weight: 700; color: #12100e; letter-spacing: .02em; }

.era-vignette { position: absolute; inset: 0; z-index: 4; pointer-events: none;
  background: radial-gradient(125% 120% at 50% 50%, transparent 62%, rgba(0,0,0,.18) 100%); }

html.booted .boot { display: none; } /* déjà vu pendant la visite : on ne le rejoue pas */
.boot.out { opacity: 0; pointer-events: none; }

/* ===== TRANSITION CRT (révélation du site) ===== */
.crt-fx {
  position: fixed; inset: 0; z-index: 10000; pointer-events: none; opacity: 0;
  background:
    radial-gradient(132% 132% at 50% 50%, transparent 56%, rgba(0, 0, 0, .30)),
    repeating-linear-gradient(0deg, rgba(0, 0, 0, 0) 0 2px, rgba(0, 0, 0, .10) 2px 3px);
}
.crt-fx::before {
  content: ""; position: absolute; left: 0; right: 0; top: 50%; height: 100%;
  transform: translateY(-50%) scaleY(.004); background: rgba(255, 255, 255, .92); opacity: 0;
}
.crt-fx.run { animation: crtScan 1s ease-out forwards; }
.crt-fx.run::before { animation: crtOn .42s ease-out forwards; }
@keyframes crtOn {
  0%   { opacity: .5;  transform: translateY(-50%) scaleY(.004); }
  55%  { opacity: .28; transform: translateY(-50%) scaleY(1); }
  100% { opacity: 0;   transform: translateY(-50%) scaleY(1); }
}
@keyframes crtScan {
  0%   { opacity: 0; }
  12%  { opacity: .5; }
  22%  { opacity: .28; }  /* scintillement, doux */
  34%  { opacity: .42; }
  70%  { opacity: .26; }
  100% { opacity: 0; }
}
@keyframes pzChromaSoft {
  0%   { filter: none; }
  12%  { filter: drop-shadow(2px 0 rgba(255,0,60,.5)) drop-shadow(-2px 0 rgba(0,229,255,.5)); }
  48%  { filter: drop-shadow(1px 0 rgba(255,0,60,.26)) drop-shadow(-1px 0 rgba(0,229,255,.26)); }
  100% { filter: none; }
}

/* ===== ANIMATION D'APPARITION ===== */
.reveal { opacity: 0; transform: translateY(20px); filter: blur(6px); transition: opacity .7s ease, transform .7s ease, filter .7s ease; }
.reveal.visible { opacity: 1; transform: none; filter: none; }
/* Cascade : les cartes apparaissent l'une après l'autre */
.cards .card:nth-child(2), .projects .project:nth-child(2) { transition-delay: .07s; }
.cards .card:nth-child(3), .projects .project:nth-child(3) { transition-delay: .14s; }
.cards .card:nth-child(4), .projects .project:nth-child(4) { transition-delay: .21s; }

/* ===== UX : profondeur & ambiance ===== */
/* Relief 3D des cartes projet (piloté par JS) + reflet lumineux */
.projects { perspective: 1100px; }
.project { transform-style: preserve-3d; }
.project::after {
  content: ""; position: absolute; inset: 0; z-index: 4; pointer-events: none; border-radius: inherit;
  opacity: 0; transition: opacity .3s ease;
  background: radial-gradient(240px circle at var(--gx, 50%) var(--gy, 0%), rgba(255, 255, 255, .16), transparent 60%);
}
.project.tilting::after { opacity: 1; }

/* Barre de progression de scroll (HUD) */
.scroll-progress {
  position: fixed; top: 0; left: 0; height: 2px; width: 0; z-index: 60;
  background: linear-gradient(90deg, var(--accent), color-mix(in srgb, var(--accent) 45%, var(--warm)));
  box-shadow: 0 0 10px color-mix(in srgb, var(--accent) 60%, transparent);
}

/* Lien de navigation actif */
.nav-links a.active { color: var(--accent); }

/* Grain de film cinématographique (très subtil) */
.grain {
  position: fixed; inset: 0; z-index: 90; pointer-events: none; opacity: .04;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 160px;
}

/* Boutons : reflet qui balaie au survol */
.btn-primary { position: relative; overflow: hidden; }
.btn-primary::after {
  content: ""; position: absolute; top: 0; left: -130%; width: 55%; height: 100%;
  background: linear-gradient(100deg, transparent, rgba(255, 255, 255, .35), transparent);
  transform: skewX(-16deg); transition: left .6s ease;
}
.btn-primary:hover::after { left: 150%; }

/* ===== RESPONSIVE ===== */
@media (max-width: 820px) {
  .about { grid-template-columns: 1fr; }
  .nav-links {
    position: fixed; inset: 72px 0 auto 0;
    flex-direction: column; align-items: flex-start; gap: 0;
    background: var(--bg); border-bottom: 1px solid var(--border);
    padding: 10px 28px 24px; transform: translateY(-150%);
    transition: transform .3s ease; box-shadow: var(--shadow);
  }
  .nav-links.open { transform: translateY(0); }
  .nav-links li { width: 100%; }
  .nav-links a { display: block; padding: 14px 0; width: 100%; }
  .nav-cta { display: inline-block; margin-top: 8px; }
  .nav-toggle { display: flex; }
}
@media (max-width: 540px) {
  .form-row { grid-template-columns: 1fr; }
  .hero { padding: 70px 0 64px; }
  .footer-inner { flex-direction: column; text-align: center; }
}
/* Sur mobile, le padding des sections faisait des trous entre les blocs sur un
   écran de 844px. Resserré plus fort que le desktop, titre de section compris. */
@media (max-width: 700px) {
  .section { padding: 50px 0; }
  .section-head { margin-bottom: 30px; }
}

/* ===================================================================
   HERO — animation légère abstraite (dégradé mouvant + grain)
   =================================================================== */
.hero-soft { position: relative; overflow: hidden; min-height: 80vh; display: flex; align-items: center;
  background: var(--bg); isolation: isolate; }

/* aura : deux nappes de dégradé floutées qui dérivent lentement */
.hero-aura { position: absolute; inset: 0; z-index: 0; pointer-events: none; }
.hero-aura::before, .hero-aura::after { content: ''; position: absolute; inset: -30%;
  background-repeat: no-repeat; filter: blur(74px); will-change: transform; }
.hero-aura::before {
  background:
    radial-gradient(38% 44% at 24% 32%, #2b5bd7 0%, transparent 60%),
    radial-gradient(34% 40% at 78% 68%, #e2231a 0%, transparent 58%);
  opacity: .42; animation: heroDriftA 26s ease-in-out infinite alternate; }
.hero-aura::after {
  background:
    radial-gradient(40% 46% at 72% 24%, #7fa2e6 0%, transparent 60%),
    radial-gradient(44% 50% at 30% 82%, #a9c0ee 0%, transparent 62%);
  opacity: .5; animation: heroDriftB 34s ease-in-out infinite alternate; }
@keyframes heroDriftA {
  0%   { transform: translate3d(-3%, 2%, 0) scale(1.05) rotate(-3deg); }
  100% { transform: translate3d(4%, -3%, 0) scale(1.14) rotate(4deg); } }
@keyframes heroDriftB {
  0%   { transform: translate3d(3%, -2%, 0) scale(1.1) rotate(2deg); }
  100% { transform: translate3d(-4%, 3%, 0) scale(1.04) rotate(-3deg); } }

/* voile latéral : assoit le texte sans masquer l'aura */
.hero-soft::after { content: ''; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background: linear-gradient(95deg, var(--bg) 0%, color-mix(in srgb, var(--bg) 55%, transparent) 46%, transparent 78%); }

/* grain analogique très discret */
.hero-grain { position: absolute; inset: 0; z-index: 1; pointer-events: none; opacity: .32; mix-blend-mode: soft-light;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E"); }

/* contenu : message d'identité */
.hero-soft-inner { position: relative; z-index: 2; padding-top: clamp(56px, 8vh, 96px); padding-bottom: 4vh; }
.hero-kicker { font-family: var(--font-mono); font-size: 12px; letter-spacing: .22em; text-transform: uppercase;
  color: var(--text-soft); margin: 0 0 22px; }
.hero-h1 { font-family: var(--font-head); font-weight: 300; color: var(--text);
  font-size: clamp(2.4rem, 6.4vw, 4.7rem); line-height: 1.04; letter-spacing: -.022em; margin: 0; text-wrap: balance; }
.hero-dot { color: #e2231a; }
.hero-lead { font-family: 'Inter', sans-serif; font-size: clamp(15px, 1.35vw, 17px); line-height: 1.7;
  color: var(--text-soft); max-width: 46ch; margin: 26px 0 0; }
.hero-actions { display: flex; gap: 14px; margin-top: 34px; flex-wrap: wrap; }
.hero-btn { font-family: 'Inter', sans-serif; font-size: 15px; font-weight: 600; text-decoration: none;
  padding: 13px 24px; border-radius: 11px; transition: transform .2s ease, background .2s ease, border-color .2s ease, color .2s ease; }
.hero-btn.primary { background: #e2231a; color: #fff; }
.hero-btn.primary:hover { background: #c71d14; transform: translateY(-1px); }
.hero-btn.ghost { color: var(--text); border: 1px solid color-mix(in srgb, var(--text) 24%, transparent); }
.hero-btn.ghost:hover { border-color: #e2231a; color: #e2231a; }

/* en-tête transparente au-dessus du hero clair (texte du thème, normal) */
.site-header:not(.scrolled) { background: transparent; border-bottom-color: transparent; box-shadow: none; }

@media (prefers-reduced-motion: reduce) {
  .hero-aura::before, .hero-aura::after { animation: none; }
}

/* ===================================================================
   DIRECTION ARTISTIQUE — « Atelier glitché » (validée 2026-07-09)
   Règle : le ROUGE (du Z et du point du wordmark) = SIGNATURE — il
   ponctue, numérote, souligne ; il ne se clique JAMAIS. Le TURQUOISE
   (--accent) = ACTION — liens, boutons, focus, rien d'autre.
   Le wordmark et le splash restent intouchés.
   =================================================================== */
/* --pz-red est défini une seule fois, en tête de fichier. Il valait #e23b30 ici
   et #f0554a en thème sombre — deux rouges pour une seule signature, alors que
   tout le reste du projet utilise #e2231a. Le thème clair ayant disparu, la
   variante « poussée pour rester lisible sur noir » n'a plus d'objet. */

/* ===================================================================
   LE POINT QUI GLITCHE

   Le point rouge est la signature ; le glitch est son comportement. Le splash
   le fait déjà arriver « dans un glitch » — ceci n'est que la même idée,
   prolongée sur tout le site.

   Trois règles, et elles font tout :
   · RARE — une fois toutes les 20 à 45 s. Un point qui glitche en continu,
     c'est l'effet qui crie, banni par l'affinage du 2026-07-09.
   · DÉSYNCHRONISÉ — chaque point tire son propre délai. Deux points qui
     glitchent ensemble ne se lisent pas comme une signature mais comme une page
     qui bugue.
   · BREF — 200 ms. On l'attrape du coin de l'œil sans être sûr de l'avoir vu.

   Animé en position:relative et NON en transform : ces points sont des <span>
   et <b> en flux inline, où transform est ignoré. Les passer en inline-block
   les ferait fonctionner, mais créerait un point de césure — le point pourrait
   tomber seul sur la ligne suivante à certaines largeurs. */
@keyframes pzGlitch {
  0%   { left: 0; top: 0; opacity: 1; text-shadow: none; }
  15%  { left: -3px; top: 1px; opacity: 1; text-shadow: 4px 0 rgba(122,0,22,.85), -5px 0 rgba(26,0,6,.7); }
  30%  { left: 4px; top: -1px; opacity: .35; text-shadow: -4px 0 rgba(163,0,30,.9); }
  45%  { left: -2px; top: 2px; opacity: 1; text-shadow: 3px 1px rgba(122,0,22,.7), -3px 0 rgba(26,0,6,.6); }
  60%  { left: 2px; top: 0; opacity: .6; text-shadow: none; }
  75%  { left: -1px; top: -1px; opacity: 1; text-shadow: 2px 0 rgba(163,0,30,.5); }
  100% { left: 0; top: 0; opacity: 1; text-shadow: none; }
}
.pz-dot, .pz-punct, .hero-dot { position: relative; }
.pz-dot.is-glitch, .pz-punct.is-glitch, .hero-dot.is-glitch {
  animation: pzGlitch 200ms steps(2, end) 1;
}

/* --- LE POINT SAUTE, sur la frappe du morceau ------------------------
   Le glitch d'ambiance (pzGlitch, ci-dessus) se déplace de ±4 px : c'est sa
   nature, il doit s'attraper du coin de l'œil sans qu'on en soit sûr. Ici c'est
   l'inverse — il faut le VOIR. ±26 px, soit près de trois fois sa propre
   hauteur.

   Ces ±4 px étaient un CHOIX, jamais une limite : rien n'interdit à un petit
   objet un grand geste. J'avais confondu la taille de l'objet avec l'amplitude
   du mouvement, et calibré en pourcentage de lui-même — d'où 0,8 px, invisible.

   top/left et non transform : ces points sont des <span> et <b> en flux inline,
   où transform est ignoré (voir pzGlitch). Mais un décalage en
   position:relative ne reflowe rien — le point peut voyager sans déranger sa
   ligne d'un pixel. */
.pz-dot.is-bat, .pz-punct.is-bat, .hero-dot.is-bat {
  animation: pzBat 280ms cubic-bezier(.15,.9,.3,1) 1;
}
@keyframes pzBat {
  0%   { top: 0; left: 0; color: #fff;
         text-shadow: 0 0 12px #ff2d1a, 0 0 28px rgba(226,35,26,.9); }
  22%  { top: -26px; left: 2px; color: #fff;
         text-shadow: 0 0 12px #ff2d1a, 0 0 28px rgba(226,35,26,.9); }
  48%  { top: 6px; left: -2px; text-shadow: 3px 0 rgba(150,0,30,.95); }
  68%  { top: -9px; left: 1px; }
  85%  { top: 2px; left: 0; }
  100% { top: 0; left: 0; color: var(--pz-red); text-shadow: none; }
}
@media (prefers-reduced-motion: reduce) {
  .pz-dot.is-bat, .pz-punct.is-bat, .hero-dot.is-bat { animation: none; }
}
/* Le mouvement est le sujet, mais il reste facultatif : qui demande moins
   d'animation garde le point, immobile. */
@media (prefers-reduced-motion: reduce) {
  .pz-dot.is-glitch, .pz-punct.is-glitch, .hero-dot.is-glitch { animation: none; }
}

/* 1 · Ponctuation finale des titres = marque (raccord « PRAZILE. » / « Le web, fait main. ») */
.pz-punct { color: var(--pz-red); font-weight: 700; }
.hero-dot { color: var(--pz-red); }

/* étiquettes de section en rouge signature */
.section-tag { color: var(--pz-red); }

/* numérotation des cartes services : rouge + point maison */
.card-num { color: var(--pz-red); font-weight: 500; }
.card-num::after { content: "."; }

/* 2 · Turquoise = action : le CTA du hero repasse à l'accent (seule entorse corrigée) */
.hero-btn.primary { background: var(--accent); color: var(--accent-ink); box-shadow: var(--glow); }
.hero-btn.primary:hover { background: color-mix(in srgb, var(--accent) 86%, black); transform: translateY(-1px); }
.hero-btn.ghost:hover { border-color: var(--accent); color: var(--accent); }

/* 3 · Échelle typographique alignée sur l'aplomb du wordmark */
.section-head h2, .about-text h2 { font-weight: 600; letter-spacing: -.022em; }
.section-head h2 { font-size: clamp(2.1rem, 4.6vw, 3.2rem); }
.hero-h1 { font-weight: 500; }
.card h3, .project-body h3, .about-card h3 { font-weight: 600; }

/* 4 · Réalisations = pièces d'atelier (cartel mono rouge) */
.projects { counter-reset: piece; }
.project { counter-increment: piece; }
.project-body::before {
  content: "pièce n°0" counter(piece);
  display: block;
  font-family: var(--font-mono);
  font-size: 11px; font-weight: 500;
  letter-spacing: .18em; text-transform: uppercase;
  color: var(--pz-red);
  margin-bottom: 8px;
}

/* 5 · Emphase au crayon rouge (surlignage bas, jamais cliquable) */
.about-text strong {
  font-weight: 600;
  box-shadow: inset 0 -0.34em 0 color-mix(in srgb, var(--pz-red) 20%, transparent);
}

/* 6 · Glitch dosé : il ne s'excite qu'au survol du logo */
.logo:hover .pz-dot { animation-duration: .9s; }

/* 7 · Matière papier : grain global +½ cran */
.grain { opacity: .09; }   /* renforcé : plus de matière sur le noir (remplace le doublon body::after retiré) */

/* ===================================================================
   AFFINAGE — « moins d'effets, plus de matière » (2026-07-09)
   On retire les effets qui « crient » : aura pastel, lueur globale,
   ombres lourdes, relief 3D. Le papier + le rouge + la typo suffisent.
   =================================================================== */
/* fond : plus de lueur turquoise ni d'aura pastel — papier quasi plat */
body::before { background: radial-gradient(1200px 700px at 90% -15%, color-mix(in srgb, var(--warm) 8%, transparent), transparent 60%); }
.hero-aura { display: none; }
.hero-soft::after { display: none; }
.hero-grain { opacity: .13; }

/* hero : typo plus grande, plus serrée, plus d'aplomb ; filet rouge sur le kicker */
.hero-h1 { font-weight: 500; letter-spacing: -.034em; line-height: .98; font-size: clamp(2.8rem, 7.2vw, 5.2rem); }
.hero-kicker { display: inline-flex; align-items: center; gap: 11px; margin-bottom: 26px; }
.hero-kicker::before { content: ''; width: 24px; height: 1px; background: var(--pz-red); }
.hero-lead { max-width: 42ch; }

/* boutons : à plat, angles nets, aucune lueur */
.hero-btn { border-radius: 6px; }
.hero-btn.primary { box-shadow: none; }
.hero-btn.primary:hover { box-shadow: none; transform: none; }
.hero-btn.ghost { border-color: color-mix(in srgb, var(--text) 18%, transparent); }

/* cartes & projets : à plat, filet fin, pas de survol qui saute */
.card { box-shadow: none; }
.card:hover { transform: none; box-shadow: none; border-color: color-mix(in srgb, var(--pz-red) 28%, var(--border)); }
.project:hover { box-shadow: none; }

/* ===================================================================
   HERO SOMBRE + NUAGE DE POINTS (génératif, Canvas 2D)
   Fond sombre chaud, points crème avec accent rouge, copie en clair.
   =================================================================== */
/* --hero-bg est posé par le moteur d'après la config publiée : un préset qui
   change la couleur de fond doit l'emporter sur ce défaut. */
.hero-soft { background: var(--hero-bg, #0e0b08); }
.hero-halftone { position: absolute; inset: 0; width: 100%; height: 100%; z-index: 0; display: block; }
.hero.no-webgl .hero-halftone { display: none; }
.hero.no-webgl.hero-soft { background: radial-gradient(120% 120% at 30% 20%, #171009, #0b0906 70%); }
/* prefers-reduced-motion ne masque plus le visuel : le script se contente de ne
   dessiner qu'une seule image. On respecte la demande (pas de mouvement) sans
   priver ces visiteurs du hero. */
.hero-soft-inner { position: relative; z-index: 2; }

/* copie en clair sur le hero sombre */
.hero-soft .hero-h1 { color: #f3ede2; }
.hero-soft .hero-lead { color: #b9ad9c; }
.hero-soft .hero-dot { color: var(--pz-red); }
.hero-soft .hero-btn.ghost { color: #f3ede2; border-color: rgba(243,237,226,.24); }
.hero-soft .hero-btn.ghost:hover { border-color: var(--accent); color: var(--accent); }

/* en-tête au-dessus du hero sombre : texte clair tant qu'on n'a pas défilé */
.site-header:not(.scrolled) .logo-word { color: #f3ede2; }
.site-header:not(.scrolled) .nav-links a { color: rgba(243,237,226,.82); }
.site-header:not(.scrolled) .nav-links a:hover { color: #fff; }
.site-header:not(.scrolled) .nav-toggle { color: #f3ede2; border-color: rgba(243,237,226,.22); }
.site-header:not(.scrolled) .nav-toggle span { background: #f3ede2; }
/* Voile côté texte : garantit la lisibilité de la copie sur la trame.
   display:block est indispensable — l'affinage du 2026-07-09 (plus haut) le passe
   à « none », et sans ce rétablissement tout ce bloc est du code mort. */
.hero-soft::after { content: ''; display: block; position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: linear-gradient(94deg, rgba(14,11,8,.86) 0%, rgba(14,11,8,.52) 34%, rgba(14,11,8,.05) 66%, rgba(14,11,8,0) 100%); }
.hero.no-webgl .hero-soft::after { display: none; }

/* Hero au-dessus de la ligne de flottaison : toujours visible (pas de dépendance au fade) */
.hero-soft .hero-soft-inner.reveal { opacity: 1 !important; transform: none !important; filter: none !important; }

/* ===================================================================
   RÉALISATIONS — index typographique
   Le texte d'abord : une liste de pièces, lisible, sélectionnable et
   référençable. Au survol (desktop) un grand aperçu suit le curseur ;
   au doigt (mobile) une vignette s'affiche directement dans la ligne,
   puisqu'il n'y a pas de survol.
   =================================================================== */
.works-index { border-top: 1px solid var(--border); }
.work-row {
  position: relative; display: grid; align-items: center; gap: 14px;
  grid-template-columns: 30px 64px 1fr;                 /* mobile par défaut */
  padding: 16px 4px; border-bottom: 1px solid var(--border); cursor: pointer;
  transition: opacity .3s ease, padding-left .35s cubic-bezier(.2, .9, .3, 1);
}
.work-row:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }
.w-num { font-family: var(--font-mono); font-size: 12px; letter-spacing: .1em; color: var(--text-soft); }
.w-name { font-size: 18px; font-weight: 500; letter-spacing: -.02em; line-height: 1.1; transition: color .25s ease; }
.w-kind, .w-year { display: none; }
.w-thumb { width: 64px; height: 44px; object-fit: cover; border-radius: 6px; border: 1px solid var(--border); }
.w-thumb-gift { display: flex; align-items: center; justify-content: center; background: #100d0a;
  color: var(--text); font-weight: 700; font-size: 22px; }
.w-thumb-gift i { width: 5px; height: 5px; border-radius: 50%; background: #e2231a;
  margin-left: 3px; align-self: flex-end; margin-bottom: 11px; }
.w-dot { position: absolute; left: -14px; top: 50%; width: 7px; height: 7px; border-radius: 50%;
  background: #e2231a; transform: translateY(-50%) scale(0); box-shadow: 0 0 12px rgba(226, 35, 26, .6);
  transition: transform .3s cubic-bezier(.2, .9, .3, 1.4); }
/* « à partager. » — écrit en bas de casse, comme une note manuscrite en marge,
   avec le point final en rouge : la ponctuation-signature de la marque. */
.w-gift { font-family: var(--font-mono); font-size: 10px; letter-spacing: .06em;
  color: var(--text-soft); background: rgba(226, 35, 26, .12); border: 1px solid rgba(226, 35, 26, .42);
  border-radius: 999px; padding: 3px 9px; margin-left: 12px; vertical-align: middle; white-space: nowrap; }
.w-gift b { color: #e2231a; font-weight: 700; }

/* Desktop : la vignette disparaît (l'aperçu flottant la remplace), le type respire */
@media (min-width: 700px) {
  .work-row { grid-template-columns: 44px 1fr auto auto; gap: 22px; align-items: baseline; padding: 24px 6px; }
  .w-thumb { display: none; }
  .w-kind, .w-year { display: block; font-family: var(--font-mono); font-size: 11.5px;
    letter-spacing: .05em; color: var(--text-soft); }
  .w-name { font-size: clamp(22px, 3vw, 32px); }
}

/* Le survol : la ligne visée s'allume, les autres s'effacent */
@media (hover: hover) and (min-width: 700px) {
  .works-index:hover .work-row { opacity: .34; }
  .works-index .work-row:hover { opacity: 1; padding-left: 22px; }
  .work-row:hover .w-name { color: #fff; }
  .work-row:hover .w-dot { transform: translateY(-50%) scale(1); }
}

/* Sans souris, c'est le DÉFILEMENT qui désigne la ligne : celle qui traverse le
   milieu de l'écran s'allume, les autres s'atténuent. Le doigt fait le travail
   du curseur, sans rien de plus à toucher. La classe « scan » n'est posée par
   script.js que si le pilotage fonctionne — sans elle, rien n'est atténué, donc
   pas de liste à moitié éteinte si le script ne tourne pas. */
@media (hover: none), (max-width: 699px) {
  .works-index.scan .work-row { opacity: .62; }
  .works-index.scan .work-row.vue { opacity: 1; padding-left: 14px; }
  .work-row.vue .w-name { color: #fff; }
  .work-row.vue .w-dot { transform: translateY(-50%) scale(1); }
  .work-row.vue .w-thumb { border-color: var(--border-strong); }
}
/* Mouvement réduit : on garde la désignation, on retire l'atténuation. */
@media (prefers-reduced-motion: reduce) {
  .works-index.scan .work-row { opacity: 1; }
}

/* L'aperçu flottant, piloté par script.js */
.work-peek {
  position: fixed; top: 0; left: 0; width: 340px; height: 222px; z-index: 40;
  border-radius: 12px; overflow: hidden; pointer-events: none; opacity: 0;
  transform: translate(-50%, -50%) scale(.94);
  border: 1px solid var(--border); box-shadow: 0 30px 70px rgba(0, 0, 0, .7);
  transition: opacity .28s ease, transform .28s cubic-bezier(.2, .9, .3, 1.2);
}
.work-peek.on { opacity: 1; transform: translate(-50%, -50%) scale(1); }
.work-peek img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* Aperçu de Ballast : son afficheur de départs plutôt qu'un simple logo —
   on montre ce que fait l'appli, comme les captures des autres lignes. */
.work-peek .peek-board { width: 100%; height: 100%; display: flex; flex-direction: column;
  background: linear-gradient(180deg, #17120d 0%, #120f0a 100%); }
.peek-board .pb-top { display: flex; align-items: center; gap: 7px; padding: 9px 12px;
  border-bottom: 1px solid #2a2018; font-size: 12px; font-weight: 600; color: #f3ede2; }
.peek-board .pb-live { width: 6px; height: 6px; border-radius: 50%; background: #2dd4bf;
  box-shadow: 0 0 7px #2dd4bf; flex: none; }
.peek-board .pb-lab { margin-left: auto; font-family: var(--font-mono); font-size: 8.5px;
  letter-spacing: .14em; text-transform: uppercase; color: #8f8469; }
.peek-board .pb-row { display: flex; align-items: center; gap: 10px; padding: 11px 12px;
  border-bottom: 1px solid #221a13; }
.peek-board .pb-row:last-child { border-bottom: none; }
.peek-board .pb-t { font-family: var(--font-mono); font-weight: 700; font-size: 15px;
  color: #f3ede2; min-width: 46px; }
.peek-board .pb-d { flex: 1; min-width: 0; font-size: 12.5px; color: #e8e0d2;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.peek-board .pb-s { font-size: 10px; font-weight: 600; }
.peek-board .pb-s.ok { color: #4fd08a; } .peek-board .pb-s.late { color: #f5a623; }
.peek-board .pb-v { font-family: var(--font-mono); font-weight: 700; font-size: 14px; color: #2dd4bf;
  background: #0b0906; border: 1px solid #2a2018; border-radius: 3px; padding: 1px 6px;
  min-width: 26px; text-align: center; flex: none; }
@media (max-width: 699px), (hover: none) { .work-peek { display: none; } }
/* Voile renforcé côté texte → lisibilité garantie sur la trame. Sa teinte suit le
   fond : sinon un préset bleu nuit garderait un dégradé brun par-dessus. */
.hero-soft::after { background: linear-gradient(94deg,
  color-mix(in srgb, var(--hero-bg, #0e0b08) 93%, transparent) 0%,
  color-mix(in srgb, var(--hero-bg, #0e0b08) 74%, transparent) 40%,
  color-mix(in srgb, var(--hero-bg, #0e0b08) 18%, transparent) 70%,
  transparent 100%); }
.hero-soft .hero-lead { color: #d0c5b3; }

/* la trame ne s'affiche QUE sur la droite — la zone de texte reste un fond sombre net (aucun point) */
.hero-halftone {
  -webkit-mask-image: linear-gradient(95deg, transparent 0, transparent 45%, #000 76%, #000 100%);
          mask-image: linear-gradient(95deg, transparent 0, transparent 45%, #000 76%, #000 100%);
}

/* Filtres du générateur : scanlines + vignette. Intensités pilotées par le script
   (--hero-scan / --hero-vig) pour suivre la config envoyée au hero.
   Le grain n'est pas repris : à l'intensité retenue il passait sous le seuil du
   visible, et il aurait coûté un canvas plein écran plus un mode de fusion. */
.hero-soft::before {
  content: ''; position: absolute; inset: 0; z-index: 1; pointer-events: none;
  opacity: var(--hero-scan, 0);
  background: repeating-linear-gradient(180deg, rgba(0,0,0,.5) 0 1px, transparent 1px 3px);
}
.hero-soft .hero-vig {
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  opacity: var(--hero-vig, 0);
  background: radial-gradient(115% 85% at 68% 50%, transparent 36%, rgba(0,0,0,.85) 100%);
}
/* Grain : feTurbulence en data-URI, donc zéro requête et zéro canvas. En mode
   screen — le seul qui produise quelque chose sur un fond noir ; overlay et
   soft-light préservent les noirs par construction et ne feraient rien ici.
   Contrepartie assumée : screen relève les noirs, comme un grain argentique. */
.hero-soft .hero-noise {
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  opacity: var(--hero-grain, 0); mix-blend-mode: screen;
  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='.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}
.hero.no-webgl .hero-soft::before,
.hero.no-webgl .hero-vig,
.hero.no-webgl .hero-noise { display: none; }

/* Mobile : le masque ne laisserait qu'un bandeau de points sur 390 px de large.
   On le retire, la forme se recentre (côté script) et le voile passe en vertical. */
@media (max-width: 819px) {
  .hero-halftone { -webkit-mask-image: none; mask-image: none; }
  /* Voile calibré pour un nuage DENSE, pas pour une poussière : le texte occupe
     le bas de l'écran en portrait, il doit rester net quelle que soit la densité.
     Le nuage garde le tiers supérieur, qui est sa vraie place ici. */
  .hero-soft::after {
    background: linear-gradient(180deg,
      transparent 12%,
      color-mix(in srgb, var(--hero-bg, #0e0b08) 58%, transparent) 30%,
      color-mix(in srgb, var(--hero-bg, #0e0b08) 90%, transparent) 46%,
      color-mix(in srgb, var(--hero-bg, #0e0b08) 97%, transparent) 100%);
  }
  .hero-soft .hero-vig { background: radial-gradient(115% 70% at 50% 36%, transparent 38%, rgba(0,0,0,.85) 100%); }
}

/* ===================================================================
   DEVIS EN TERMINAL (#devis)
   Pas un tube cathodique de hacker : crème sur noir chaud, un télex.
   Pas de phosphore vert, pas de lueur, pas de courbure — l'affinage du
   2026-07-09 retire « les effets qui crient ». Le rétro vient de la
   matière (scanlines, grain), jamais du bloom.
   Le curseur EST le point rouge de PRAZILE : le curseur est l'élément le
   plus iconique d'un terminal, le point rouge est la signature. Même objet.
   =================================================================== */
.term { position: relative; background: #0e0b08; border: 1px solid #241d16; overflow: hidden;
  max-width: 860px; margin: 0 auto; }
.term-bar { display: flex; align-items: center; gap: 14px; padding: 11px 16px;
  border-bottom: 1px solid #241d16; color: #8f8469; }
.term-mark { font-weight: 800; font-size: 13px; letter-spacing: -.02em; color: #ece7db; }
.term-mark b { color: #e2231a; }
.term-cap { font-family: 'JetBrains Mono', ui-monospace, monospace; font-size: 10px;
  letter-spacing: .14em; text-transform: uppercase; }
.term-etat { margin-left: auto; display: flex; align-items: center; gap: 7px;
  font-family: 'JetBrains Mono', monospace; font-size: 10px; letter-spacing: .14em;
  text-transform: uppercase; }
.term-etat i { width: 6px; height: 6px; background: #e2231a; border-radius: 50%; }
.term-ecran { position: relative; padding: 22px 20px 18px; min-height: 360px; max-height: 58vh;
  overflow-y: auto; font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 13px; line-height: 1.75; color: #ece7db; }
/* La zone de conversation est focusable (tabindex=0) : un focus invisible laisse
   l'utilisateur clavier sans repère. Filet turquoise discret en inset. */
.term-ecran:focus-visible { outline: 2px solid color-mix(in srgb, var(--accent) 55%, transparent); outline-offset: -2px; }
.tl { white-space: pre-wrap; word-break: break-word; }
.tl.sys { color: #8f8469; } .tl.dit { color: #ece7db; }
.tl.moi { color: #b8ac97; } .tl.acc { color: #e2231a; }
.tv { height: 12px; }
.tch { margin: 4px 0 2px; }
.topt { position: relative; display: block; width: 100%; text-align: left; background: none; border: 0;
  color: #b8ac97; font-family: inherit; font-size: 13px; line-height: 1.9; cursor: pointer;
  padding: 0 18px 0 0; transition: color .12s; }
/* État coché (étape multi-choix) : signalé par un ✓, pas seulement par la couleur
   (WCAG 1.4.1 — ne pas véhiculer l'information par la couleur seule). */
.topt.on::after { content: "✓"; position: absolute; right: 2px; top: 0; color: #e2231a; }
.topt:hover, .topt:focus-visible { color: #ece7db; }
/* Focus clavier visible : la seule bascule de couleur du texte ne suffit pas
   (indicateur « couleur seule »). Filet turquoise net autour de l'option. */
.topt:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 2px; }
.topt:hover .n, .topt:focus-visible .n, .topt.on .n { color: #e2231a; }
.topt .n { color: #8f8469; transition: color .12s; }
.topt.on { color: #ece7db; }
/* Champ « Autre besoin — préciser » : même trait discret que les champs du formulaire. */
.tprec { margin: 1px 0 6px 18px; }
.tprec-inp { width: calc(100% - 18px); background: none; border: 0; border-bottom: 1px solid #241d16;
  color: #ece7db; font-family: inherit; font-size: 13px; padding: 4px 0; }
.tprec-inp:focus { outline: none; border-bottom-color: var(--accent); }
.tprec-inp::placeholder { color: #7c7060; }
.tc { display: inline-block; width: 8px; height: 8px; border-radius: 50%; background: #e2231a;
  vertical-align: baseline; margin-left: 3px; animation: tbat 1.1s steps(1, end) infinite; }
@keyframes tbat { 0%,55% { opacity: 1 } 56%,100% { opacity: 0 } }
.tc.carre { border-radius: 0; width: 7px; height: 13px; transform: translateY(2px); }
.tc.tiret { border-radius: 0; width: 9px; height: 2px; transform: translateY(-1px); }
.tform { margin-top: 2px; }
.tch2 { display: flex; align-items: baseline; gap: 8px; margin: 2px 0; }
.tch2.haut { align-items: flex-start; }
.tch2 label { color: #8f8469; white-space: nowrap; min-width: 66px; }
.tch2 input, .tch2 textarea { flex: 1; background: none; border: 0; border-bottom: 1px solid #241d16;
  color: #ece7db; font-family: inherit; font-size: 13px; padding: 3px 0; resize: none; }
.tch2 input:focus, .tch2 textarea:focus { outline: none; border-bottom-color: #e2231a; }
.tch2 input::placeholder, .tch2 textarea::placeholder { color: #7c7060; }
.tch2 .fac { color: #7c7060; font-size: 11px; }
/* Sur mobile, le libellé collé à gauche étranglait le champ — le placeholder du
   téléphone se retrouvait tronqué. Le libellé (et « facultatif ») passent sur une
   première ligne, le champ prend toute la largeur en dessous. */
@media (max-width: 540px) {
  .tch2 { flex-wrap: wrap; align-items: baseline; row-gap: 2px; }
  .tch2 label { min-width: 0; }
  .tch2 .fac { order: 2; margin-left: auto; }
  .tch2 input, .tch2 textarea { order: 3; flex: 1 1 100%; }
}
/* Pot de miel : hors écran, jamais focusable. Un humain ne le voit pas. */
.tmiel { position: absolute; left: -9999px; width: 1px; height: 1px; opacity: 0; }
.tenvoi { background: var(--accent); color: #08110f; border: 0; font-family: 'Outfit', sans-serif;
  font-size: 12.5px; font-weight: 600; padding: 9px 15px; cursor: pointer; margin-top: 12px; }
.tenvoi[disabled] { opacity: .5; cursor: default; }
/* Le message d'erreur portait du rouge #e2231a à 12px : ~4,2:1, sous le seuil AA
   (4,5:1) — or c'est le SEUL canal pour « envoi impossible ». Texte en crème
   lisible, le rouge signature déplacé sur un filet qui signale l'alerte. */
.terr { color: #ece7db; font-size: 12.5px; margin-top: 8px; border-left: 2px solid #e2231a;
  padding-left: 10px; }
.tconf { color: #8f8469; font-size: 11px; margin-top: 10px; line-height: 1.5; }
.tconf a { color: #b8ac97; }
.term-pied { display: flex; align-items: center; gap: 12px; padding: 11px 16px;
  border-top: 1px solid #241d16; color: #8f8469; }
.term-rej { margin-left: auto; background: none; border: 1px solid #241d16; color: #b8ac97;
  font-family: 'JetBrains Mono', monospace; font-size: 10px; letter-spacing: .1em;
  text-transform: uppercase; padding: 6px 10px; cursor: pointer; }
.term-rej:hover { border-color: #3a2f24; color: #ece7db; }
/* La matière : c'est ELLE qui fait le rétro. Jamais la souris. */
.term-matiere { position: absolute; inset: 0; pointer-events: none; z-index: 5; }
.term-matiere::before { content: ''; position: absolute; inset: 0; opacity: .5;
  background: repeating-linear-gradient(180deg, rgba(0,0,0,.5) 0 1px, transparent 1px 3px); }
.term-matiere::after { content: ''; position: absolute; inset: 0; opacity: .6;
  background: radial-gradient(115% 90% at 50% 45%, transparent 42%, rgba(0,0,0,.8) 100%); }
.term-bruit { position: absolute; inset: 0; pointer-events: none; z-index: 6;
  opacity: .045; mix-blend-mode: screen;
  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='.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E"); }
/* .contact-direct est défini plus haut (bloc complet avec le style du lien). Un
   second bloc vivait ici avec « color: var(--muted) » — une variable jamais
   déclarée, donc une couleur ignorée. Retiré : la définition du haut suffit. */
@media (max-width: 640px) { .term-ecran { font-size: 12px; padding: 18px 15px; min-height: 300px; } }

/* Sous 380 px (iPhone SE et compagnie), la barre du terminal n'a plus la place
   pour ses trois éléments : « Sous 24 h » repassait sur deux lignes et la barre
   grossissait de 44 à 56 px. C'est « Devis express » qui cède, parce qu'il est
   déjà écrit en toutes lettres dans le titre de la section, quelques pixels plus
   haut — le supprimer ici ne fait perdre aucune information, il en retire une
   qu'on affichait deux fois. Le wordmark et le délai, eux, ne se répètent pas. */
@media (max-width: 380px) { .term-cap { display: none; } }

/* ===================================================================
   SERVICES — FICHES D'ATELIER SOUS LA LAMPE (2026-07-21)
   La carte de verre qui lévite (flou + ombre portée) était le seul geste
   générique du site : la fiche se POSE désormais sur l'établi — fond mat
   chaud, filet fin, grain, assise fixe. Le numéro devient un POINÇON
   frappé dans la matière, le titre porte le point rouge de signature.
   Et les quatre fiches SE BALANCENT, chacune sur son propre axe (01
   pique du nez, 02 se redresse, 03 roule à droite, 04 à gauche), sous
   une même horloge --lampx posée par script.js. Peint en LUMIÈRE
   (reflet + liseré), jamais en ombre : sur fond sombre, une ombre qui
   bouge est invisible — vérifié à la dure.
   La MATIÈRE (fond mat, grain, assise, point de signature) vaut pour
   Services ET Méthode — deux grilles voisines, une seule matière. La
   LAMPE, le poinçon et le liseré restent l'apanage de Services : Méthode
   est la section des promesses, son calme est un argument.
   =================================================================== */
#services .cards { --lampx: 0; }
#services .card, #methode .card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  backdrop-filter: none;
  box-shadow: 0 2px 5px rgba(0, 0, 0, .35);      /* assise fixe : posé, pas flottant */
  overflow: hidden; isolation: isolate;
}
#services .card:hover, #methode .card:hover { box-shadow: 0 2px 5px rgba(0, 0, 0, .35); }
#services .card::before, #methode .card::before {                        /* le grain de l'établi */
  content: ''; position: absolute; inset: 0; z-index: -1; opacity: .05;
  mix-blend-mode: screen; pointer-events: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}
/* Le poinçon : grand chiffre fantôme gravé derrière le texte. Remplace le
   numéro-cartel (et son point ajouté en ::after, qu'on éteint). */
#services .card-num {
  position: absolute; top: -8px; right: 6px; z-index: -1;
  font-family: var(--font-head); font-weight: 800; font-size: 118px;
  line-height: 1; color: #f3ede2; opacity: .05; letter-spacing: -.04em;
  user-select: none; margin: 0; padding: 0; border: 0;
}
#services .card-num::after { content: none; }
/* Le point rouge de signature — un cercle CSS, pas le glyphe (carré en Outfit). */
#services .card h3 .card-dot, #methode .card h3 .card-dot {
  display: inline-block; width: .26em; height: .26em; border-radius: 50%;
  background: var(--pz-red); margin-left: .12em; vertical-align: baseline;
  box-shadow: 0 0 10px rgba(226, 35, 26, .45);
}

/* Les quatre axes de balancement — reflet chaud sur la face… */
#services .card:nth-child(1) { background:
  radial-gradient(130% 95% at 50% calc(50% + var(--lampx)*85%),
    rgba(217, 138, 74, .12), rgba(217, 138, 74, 0) 55%), var(--surface); }
#services .card:nth-child(2) { background:
  radial-gradient(130% 95% at 50% calc(50% - var(--lampx)*85%),
    rgba(217, 138, 74, .12), rgba(217, 138, 74, 0) 55%), var(--surface); }
#services .card:nth-child(3) { background:
  radial-gradient(120% 105% at calc(50% + var(--lampx)*46%) -18%,
    rgba(217, 138, 74, .13), rgba(217, 138, 74, 0) 58%), var(--surface); }
#services .card:nth-child(4) { background:
  radial-gradient(120% 105% at calc(50% - var(--lampx)*46%) -18%,
    rgba(217, 138, 74, .13), rgba(217, 138, 74, 0) 58%), var(--surface); }

/* …et le liseré, sur le bord propre à l'axe. */
#services .card::after { content: ''; position: absolute; pointer-events: none; }
#services .card:nth-child(1)::after { top: 0; bottom: 0; left: 0; width: 1px;
  background: linear-gradient(180deg, transparent 12%, rgba(255, 205, 150, .5) 50%, transparent 88%);
  transform: translateY(calc(var(--lampx)*46%)); }
#services .card:nth-child(2)::after { top: 0; bottom: 0; right: 0; width: 1px;
  background: linear-gradient(180deg, transparent 12%, rgba(255, 205, 150, .5) 50%, transparent 88%);
  transform: translateY(calc(var(--lampx)*-46%)); }
#services .card:nth-child(3)::after { left: 0; right: 0; top: 0; height: 1px;
  background: linear-gradient(90deg, transparent 12%, rgba(255, 205, 150, .55) 50%, transparent 88%);
  transform: translateX(calc(var(--lampx)*46%)); }
#services .card:nth-child(4)::after { left: 0; right: 0; top: 0; height: 1px;
  background: linear-gradient(90deg, transparent 12%, rgba(255, 205, 150, .55) 50%, transparent 88%);
  transform: translateX(calc(var(--lampx)*-46%)); }

