/* Signentis — feuille de style unique
   Thème sombre par défaut, clair au choix. Aucune dépendance hors polices. */

/* ---------- Jetons ---------- */
:root {
  color-scheme: dark;

  --bg:        oklch(0.14 0.028 245);
  --bg-deep:   oklch(0.09 0.024 245);
  --surface:   oklch(0.185 0.030 245);
  --surface-2: oklch(0.235 0.032 245);
  --line:      oklch(0.34 0.040 230);
  --line-soft: oklch(0.26 0.032 240);

  --text:      oklch(0.96 0.006 265);
  --text-mid:  oklch(0.78 0.012 265);
  --text-dim:  oklch(0.62 0.014 265);

  --violet:    oklch(0.66 0.26 292);
  --magenta:   oklch(0.68 0.27 340);
  --cyan:      oklch(0.84 0.15 205);
  --azur:      oklch(0.68 0.21 252);
  --lime:      oklch(0.86 0.19 138);
  --amber:     oklch(0.81 0.16 72);
  --rose:      oklch(0.68 0.22 18);
  /* Identite Signentis : lecture holographique. Le cyan porte la marque,
     l'azur porte la profondeur, l'arc apporte la seule note chaude — sans
     elle l'ensemble vire au bleu d'hopital. */
  --hologramme: oklch(0.86 0.14 202);
  --arc:        oklch(0.84 0.14 88);

  --accent:      var(--hologramme);
  --accent-soft: color-mix(in oklch, var(--hologramme) 18%, transparent);
  --on-accent:   oklch(0.13 0.03 230);

  --grad:      linear-gradient(110deg, var(--hologramme), var(--azur));
  --grad-rich: linear-gradient(115deg, var(--hologramme) 0%, var(--cyan) 26%, var(--azur) 56%, var(--violet) 80%, var(--magenta) 100%);
  --grad-ligne: linear-gradient(90deg, transparent, var(--hologramme), var(--azur), transparent);
  /* Ruban de marque : il glisse en boucle, donc il reboucle sur le cyan.
     L'arc n'y passe qu'une fois, en pointe chaude. */
  --grad-marque: linear-gradient(115deg, var(--hologramme) 0%, var(--azur) 34%,
                                 var(--arc) 66%, var(--hologramme) 100%);

  --r-sm: 8px;
  --r:    14px;
  --r-lg: 22px;
  --r-xl: 30px;

  --shadow:    0 1px 2px oklch(0 0 0 / .4), 0 8px 28px oklch(0 0 0 / .32);
  --shadow-lg: 0 2px 6px oklch(0 0 0 / .45), 0 28px 70px oklch(0 0 0 / .45);

  --sans: "Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --display: "Space Grotesk", var(--sans);
  --mono: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, monospace;

  --gutter: clamp(1rem, 4vw, 2.5rem);
  --max: 1200px;
  --nav-h: 68px;
}

:root[data-theme="light"] {
  color-scheme: light;
  --bg:        oklch(0.985 0.004 265);
  --bg-deep:   oklch(0.95 0.008 265);
  --surface:   oklch(1 0 0);
  --surface-2: oklch(0.97 0.006 265);
  --line:      oklch(0.87 0.010 265);
  --line-soft: oklch(0.92 0.008 265);
  --text:      oklch(0.20 0.020 265);
  --text-mid:  oklch(0.32 0.020 265);
  --text-dim:  oklch(0.46 0.018 265);
  --violet:    oklch(0.52 0.25 292);
  --magenta:   oklch(0.55 0.25 340);
  --cyan:      oklch(0.58 0.14 205);
  --azur:      oklch(0.53 0.20 252);
  --lime:      oklch(0.58 0.17 140);
  --amber:     oklch(0.62 0.16 65);
  --rose:      oklch(0.55 0.21 20);
  --hologramme: oklch(0.55 0.15 210);
  --arc:        oklch(0.60 0.14 80);
  --on-accent: oklch(0.99 0 0);
  --shadow:    0 1px 2px oklch(0.2 0.02 265 / .06), 0 8px 24px oklch(0.2 0.02 265 / .08);
  --shadow-lg: 0 2px 6px oklch(0.2 0.02 265 / .08), 0 26px 60px oklch(0.2 0.02 265 / .14);
}

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

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

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.65;
  font-feature-settings: "cv05" 1, "ss03" 1;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

/* ---------- Fond vivant ----------
   Trois couches superposées : une aurore qui dérive, un maillage en
   perspective, et un grain. Tout est décoratif et tout s'arrête si le
   système demande moins d'animation. */

/* 1. Aurore — quatre masses colorées qui se déplacent lentement */
body::before {
  content: "";
  position: fixed;
  inset: -30vh -20vw;
  z-index: -4;                      /* au-dessus du mur et de son voile */
  pointer-events: none;
  /* Réparties sur toute la hauteur : la couche est fixe, donc ce qu'on voit
     ici est ce qu'on voit à n'importe quel endroit de la page. Des masses
     concentrées en haut laisseraient le bas de l'écran gris en permanence. */
  background:
    radial-gradient(36% 30% at 16% 10%, color-mix(in oklch, var(--violet) 48%, transparent), transparent 70%),
    radial-gradient(32% 28% at 84% 18%, color-mix(in oklch, var(--cyan) 36%, transparent), transparent 70%),
    radial-gradient(38% 30% at 58% 44%, color-mix(in oklch, var(--magenta) 34%, transparent), transparent 72%),
    radial-gradient(34% 30% at 10% 62%, color-mix(in oklch, var(--azur) 34%, transparent), transparent 72%),
    radial-gradient(34% 28% at 88% 76%, color-mix(in oklch, var(--violet) 32%, transparent), transparent 72%),
    radial-gradient(30% 26% at 42% 94%, color-mix(in oklch, var(--cyan) 26%, transparent), transparent 72%);
  filter: blur(40px) saturate(1.25);
  /* Discrete : c'est la lumiere des tubes qui se pose sur la pierre. Au-dela,
     elle repeint le mur et on ne voit plus la brique. */
  opacity: 0;              /* aucune couche coloree sur la photographie */
  animation: derive 34s ease-in-out infinite alternate;
  will-change: transform;
}
@keyframes derive {
  0%   { transform: translate3d(0,0,0) scale(1); }
  33%  { transform: translate3d(-3%, 2%, 0) scale(1.08); }
  66%  { transform: translate3d(2.5%, -1.5%, 0) scale(1.03); }
  100% { transform: translate3d(-1.5%, 3%, 0) scale(1.1); }
}

/* 2. Grain — casse le lissage des dégradés, qui sinon font « plastique » */
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  opacity: .055;
  mix-blend-mode: overlay;
  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='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");
}

:root[data-theme="light"] body::before { opacity: 0; filter: blur(48px) saturate(1.1); }
:root[data-theme="light"] body::after  { opacity: .03; mix-blend-mode: multiply; }

@media (prefers-reduced-motion: reduce) {
  body::before { animation: none; }
}

h1, h2, h3, h4 {
  font-family: var(--display);
  font-weight: 600;
  line-height: 1.12;
  letter-spacing: -0.025em;
  margin: 0 0 .5em;
  text-wrap: balance;
}
h1 { font-size: clamp(2.4rem, 6.2vw, 4.4rem); }
h2 { font-size: clamp(1.75rem, 3.6vw, 2.7rem); }
h3 { font-size: clamp(1.15rem, 1.9vw, 1.4rem); }

p { margin: 0 0 1.1em; text-wrap: pretty; }
p:last-child { margin-bottom: 0; }

a { color: inherit; text-decoration-color: color-mix(in oklch, currentColor 35%, transparent); text-underline-offset: 3px; }
a:hover { text-decoration-color: currentColor; }

img, svg { max-width: 100%; display: block; }

:focus-visible { outline: 2px solid var(--cyan); outline-offset: 3px; border-radius: 4px; }

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

.wrap { width: 100%; max-width: var(--max); margin-inline: auto; padding-inline: var(--gutter); }
.narrow { max-width: 760px; }

section { padding-block: clamp(4rem, 9vw, 8rem); }

.lead { font-size: clamp(1.05rem, 1.7vw, 1.3rem); color: var(--text-mid); max-width: 62ch; }
.dim { color: var(--text-dim); }
.mono { font-family: var(--mono); font-size: .86em; }

/* ---------- Étiquette de section ---------- */
.eyebrow {
  display: inline-flex; align-items: center; gap: .55rem;
  font-family: var(--mono); font-size: .74rem; font-weight: 500;
  letter-spacing: .14em; text-transform: uppercase;
  color: var(--text-dim); margin-bottom: 1.1rem;
}
.eyebrow::before {
  content: ""; width: 26px; height: 1px;
  background: var(--grad);
}

.gradient-text {
  background: var(--grad);
  -webkit-background-clip: text; background-clip: text;
  color: transparent;
}

/* ---------- Navigation ---------- */
.nav {
  position: sticky; top: 0; z-index: 60;
  height: var(--nav-h);
  display: flex; align-items: center;
  background: color-mix(in oklch, var(--bg) 72%, transparent);
  backdrop-filter: blur(16px) saturate(1.5);
  -webkit-backdrop-filter: blur(16px) saturate(1.5);
  border-bottom: 1px solid transparent;
  transition: border-color .3s, background .3s;
}
.nav[data-scrolled] { border-bottom-color: var(--line-soft); }
.nav .wrap { display: flex; align-items: center; gap: 1.5rem; }

.brand {
  display: flex; align-items: center; gap: .6rem;
  font-family: var(--display); font-weight: 600; font-size: 1.12rem;
  letter-spacing: -.02em; text-decoration: none; margin-right: auto;
}
.brand .mark {
  width: 26px; height: 26px; border-radius: 8px;
  background: var(--grad);
  display: grid; place-items: center;
  font-size: .8rem; color: var(--on-accent); font-weight: 700;
  box-shadow: 0 0 0 1px color-mix(in oklch, var(--accent) 40%, transparent),
              0 4px 14px color-mix(in oklch, var(--accent) 35%, transparent);
}

.nav-links { display: flex; gap: .25rem; align-items: center; }
.nav-links a {
  padding: .45rem .8rem; border-radius: var(--r-sm);
  font-size: .92rem; color: var(--text-mid); text-decoration: none;
  transition: color .18s, background .18s;
}
.nav-links a:hover, .nav-links a[aria-current="page"] { color: var(--text); background: var(--surface-2); }

.nav-tools { display: flex; gap: .5rem; align-items: center; }

.icon-btn {
  width: 38px; height: 38px; display: grid; place-items: center;
  border: 1px solid var(--line-soft); border-radius: var(--r-sm);
  background: var(--surface); color: var(--text-mid);
  cursor: pointer; transition: .18s;
}
.icon-btn:hover { color: var(--text); border-color: var(--line); }

.nav-toggle { display: none; }

@media (max-width: 820px) {
  .nav-toggle { display: grid; }
  .nav-links {
    position: fixed; inset: var(--nav-h) 0 auto;
    flex-direction: column; align-items: stretch; gap: 0;
    padding: .5rem var(--gutter) 1.2rem;
    background: var(--bg);
    border-bottom: 1px solid var(--line-soft);
    box-shadow: var(--shadow-lg);
    transform: translateY(-120%); transition: transform .3s cubic-bezier(.4,0,.2,1);
  }
  .nav-links[data-open] { transform: none; }
  .nav-links a { padding: .8rem .5rem; font-size: 1rem; }
}

/* ---------- Boutons ---------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .5rem;
  padding: .72rem 1.35rem;
  font: inherit; font-size: .95rem; font-weight: 500;
  border-radius: 999px; border: 1px solid transparent;
  cursor: pointer; text-decoration: none; white-space: nowrap;
  transition: transform .16s cubic-bezier(.34,1.3,.64,1), box-shadow .2s, background .2s, border-color .2s, color .2s;
}
.btn:active { transform: scale(.975); }

.btn-primary {
  background: var(--grad); color: var(--on-accent); font-weight: 600;
  box-shadow: 0 1px 0 color-mix(in oklch, white 25%, transparent) inset,
              0 6px 22px color-mix(in oklch, var(--accent) 40%, transparent);
}
.btn-primary:hover { box-shadow: 0 1px 0 color-mix(in oklch, white 30%, transparent) inset,
                                 0 10px 32px color-mix(in oklch, var(--accent) 55%, transparent); }

.btn-ghost { background: var(--surface); color: var(--text); border-color: var(--line); }
.btn-ghost:hover { border-color: color-mix(in oklch, var(--accent) 55%, var(--line)); background: var(--surface-2); }

.btn-quiet { background: transparent; color: var(--text-mid); padding-inline: .75rem; }
.btn-quiet:hover { color: var(--text); }

.btn-sm { padding: .42rem .85rem; font-size: .85rem; }
.btn-danger { background: color-mix(in oklch, var(--rose) 16%, var(--surface)); color: var(--rose); border-color: color-mix(in oklch, var(--rose) 35%, transparent); }

/* ---------- Cartes ---------- */
.card {
  background: color-mix(in oklch, var(--surface) 94%, transparent);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-lg);
  padding: clamp(1.4rem, 2.4vw, 2rem);
  transition: border-color .25s, transform .25s, box-shadow .25s;
}
.card-link { text-decoration: none; display: block; }
.card-link:hover { border-color: color-mix(in oklch, var(--accent) 45%, var(--line)); transform: translateY(-3px); box-shadow: var(--shadow); }

.grid { display: grid; gap: 1.1rem; }
.grid-2 { grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); }
.grid-3 { grid-template-columns: repeat(auto-fit, minmax(265px, 1fr)); }
.grid-4 { grid-template-columns: repeat(auto-fit, minmax(205px, 1fr)); }

/* ---------- Puces ---------- */
.chip {
  display: inline-flex; align-items: center; gap: .4rem;
  padding: .24rem .7rem; border-radius: 999px;
  font-size: .76rem; font-weight: 500; letter-spacing: .01em;
  border: 1px solid var(--line-soft); background: var(--surface-2); color: var(--text-mid);
  white-space: nowrap;
}
.chip-dot::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.chip-ok    { color: var(--lime);  border-color: color-mix(in oklch, var(--lime) 32%, transparent);  background: color-mix(in oklch, var(--lime) 12%, transparent); }
.chip-warn  { color: var(--amber); border-color: color-mix(in oklch, var(--amber) 32%, transparent); background: color-mix(in oklch, var(--amber) 12%, transparent); }
.chip-bad   { color: var(--rose);  border-color: color-mix(in oklch, var(--rose) 32%, transparent);  background: color-mix(in oklch, var(--rose) 12%, transparent); }
.chip-info  { color: var(--cyan);  border-color: color-mix(in oklch, var(--cyan) 32%, transparent);  background: color-mix(in oklch, var(--cyan) 12%, transparent); }
.chip-marque{ color: var(--accent);border-color: color-mix(in oklch, var(--accent) 38%, transparent);background: color-mix(in oklch, var(--accent) 14%, transparent); }

/* ---------- Apparition au défilement ---------- */
/* Le contenu est visible par défaut. L'animation n'existe que si le JavaScript
   a pu s'exécuter — sans quoi un visiteur, ou un robot d'indexation, verrait
   une page à moitié vide. */
.js .reveal { opacity: 0; transform: translateY(18px); transition: opacity .6s ease, transform .6s cubic-bezier(.2,.7,.3,1); }
.js .reveal[data-seen] { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) { .js .reveal { opacity: 1; transform: none; transition: none; } }

/* ---------- Pied de page ---------- */
.footer { border-top: 1px solid var(--line-soft); padding-block: 3rem 2.5rem; background: var(--bg-deep); }
.footer .wrap { display: grid; gap: 2rem; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); }
.footer h4 { font-family: var(--sans); font-size: .78rem; letter-spacing: .12em; text-transform: uppercase; color: var(--text-dim); margin-bottom: .9rem; }
.footer ul { list-style: none; margin: 0; padding: 0; display: grid; gap: .5rem; }
.footer a { font-size: .92rem; color: var(--text-mid); text-decoration: none; }
.footer a:hover { color: var(--text); }
.footer-bottom { border-top: 1px solid var(--line-soft); margin-top: 2.5rem; padding-top: 1.5rem; font-size: .84rem; color: var(--text-dim); display: flex; flex-wrap: wrap; gap: 1rem; justify-content: space-between; }

/* ---------- Formulaires ---------- */
label { display: block; font-size: .82rem; font-weight: 500; color: var(--text-mid); margin-bottom: .35rem; }
input, select, textarea {
  width: 100%; font: inherit; font-size: .95rem;
  padding: .6rem .8rem;
  color: var(--text); background: var(--bg-deep);
  border: 1px solid var(--line-soft); border-radius: var(--r-sm);
  transition: border-color .18s, box-shadow .18s;
}
input:focus, select:focus, textarea:focus {
  outline: none; border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
input::placeholder, textarea::placeholder { color: var(--text-dim); }
.field { margin-bottom: 1rem; }
.field-row { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); }
.hint { font-size: .78rem; color: var(--text-dim); margin-top: .3rem; }

/* ---------- Tableaux ---------- */
.table-scroll { overflow-x: auto; border: 1px solid var(--line-soft); border-radius: var(--r); background: var(--surface); }
table { width: 100%; border-collapse: collapse; font-size: .9rem; }
thead th {
  text-align: left; font-size: .74rem; letter-spacing: .08em; text-transform: uppercase;
  color: var(--text-dim); font-weight: 600;
  padding: .85rem 1rem; border-bottom: 1px solid var(--line-soft);
  white-space: nowrap; background: var(--surface-2);
}
tbody td { padding: .85rem 1rem; border-bottom: 1px solid var(--line-soft); vertical-align: middle; }
tbody tr:last-child td { border-bottom: 0; }
tbody tr { transition: background .15s; }
tbody tr:hover { background: var(--surface-2); }
th.sortable { cursor: pointer; user-select: none; }
th.sortable:hover { color: var(--text); }

/* ---------- Boîte de dialogue ---------- */
dialog {
  border: 1px solid var(--line); border-radius: var(--r-lg);
  background: var(--surface); color: var(--text);
  padding: 0; width: min(640px, calc(100vw - 2rem));
  box-shadow: var(--shadow-lg);
}
dialog::backdrop { background: oklch(0 0 0 / .62); backdrop-filter: blur(4px); }
.dialog-head { display: flex; align-items: center; gap: 1rem; padding: 1.2rem 1.5rem; border-bottom: 1px solid var(--line-soft); }
.dialog-head h3 { margin: 0; flex: 1; }
.dialog-body { padding: 1.5rem; max-height: min(65vh, 560px); overflow-y: auto; }
.dialog-foot { display: flex; gap: .7rem; justify-content: flex-end; padding: 1.1rem 1.5rem; border-top: 1px solid var(--line-soft); background: var(--surface-2); border-radius: 0 0 var(--r-lg) var(--r-lg); }

/* ---------- Divers ---------- */
.stack { display: grid; gap: 1.1rem; }
.row { display: flex; gap: .7rem; align-items: center; flex-wrap: wrap; }
.row-between { display: flex; gap: 1rem; align-items: center; justify-content: space-between; flex-wrap: wrap; }
.empty { text-align: center; padding: 3.5rem 1.5rem; color: var(--text-dim); }
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.skip { position: absolute; left: -999px; }
.skip:focus { left: var(--gutter); top: .5rem; z-index: 99; background: var(--surface); padding: .6rem 1rem; border-radius: var(--r-sm); border: 1px solid var(--line); }

/* ====================================================================
   Couche « futuriste »
   Effets lumineux, bordures en dégradé, héros animé.
   Tout ce qui bouge ici s'arrête sous prefers-reduced-motion.
   ==================================================================== */

/* ---------- Héros ---------- */
.hero { position: relative; overflow: clip; }

/* Conteneur du média de fond. Aujourd'hui il porte une animation générée.
   Pour y mettre une vraie vidéo : déposer le fichier et décommenter la
   balise <video> dans index.html — le canvas s'efface tout seul. */
.hero-media {
  position: absolute;
  inset: 0;
  z-index: -2;
  pointer-events: none;
  -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 58%, transparent 96%);
  mask-image: linear-gradient(to bottom, #000 0%, #000 58%, transparent 96%);
}
.hero-media canvas,
.hero-media video {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  display: block;
  object-fit: cover;
}
/* La vidéo ne s'affiche qu'une fois prête : sinon on voit un rectangle noir
   par-dessus le mur pendant le chargement. */
.hero-media video { opacity: 0; transition: opacity .8s ease; }
.hero-media video[data-prete] { opacity: 1; }
/* Les particules s'effacent quand la vidéo tourne — deux animations
   superposées se concurrencent. */
.hero-video .hero-media video[data-prete] ~ canvas { opacity: 0; }

/* La vidéo porte déjà l'enseigne : celle en CSS ferait doublon. Mais elle
   ne s'efface que si la vidéo tourne VRAIMENT — sinon un visiteur dont la
   lecture échoue se retrouverait sans enseigne du tout. */
.hero-video:has(video[data-prete]) .enseigne,
.hero-video:has(video[data-prete]) .hero-grille { display: none; }
:root[data-theme="light"] .hero-media canvas { opacity: .5; }

/* Maillage en perspective, sous le texte */
.hero-grille {
  position: absolute;
  inset: auto 0 -10% 0;
  height: 52%;
  z-index: -2;
  pointer-events: none;
  background-image:
    linear-gradient(to right, color-mix(in oklch, var(--cyan) 22%, transparent) 1px, transparent 1px),
    linear-gradient(to bottom, color-mix(in oklch, var(--violet) 20%, transparent) 1px, transparent 1px);
  background-size: 64px 64px;
  transform: perspective(520px) rotateX(64deg);
  transform-origin: bottom center;
  -webkit-mask-image: radial-gradient(70% 90% at 50% 100%, #000, transparent 72%);
  mask-image: radial-gradient(70% 90% at 50% 100%, #000, transparent 72%);
  opacity: .55;
  animation: defile 9s linear infinite;
}
@keyframes defile { to { background-position: 0 64px, 0 64px; } }
:root[data-theme="light"] .hero-grille { opacity: .3; }

/* Balayage lumineux qui traverse le titre */
.balayage { position: relative; display: inline-block; }
.balayage::after {
  content: "";
  position: absolute; inset: 0;
  background: linear-gradient(105deg, transparent 38%, color-mix(in oklch, white 70%, transparent) 50%, transparent 62%);
  background-size: 260% 100%;
  -webkit-mask-image: linear-gradient(#000, #000);
  mix-blend-mode: overlay;
  animation: balaye 6.5s ease-in-out 1.6s infinite;
  pointer-events: none;
}
@keyframes balaye {
  0%, 72%  { background-position: 190% 0; }
  100%     { background-position: -60% 0; }
}

/* Sur la maçonnerie nue, le gris ne tient pas : le texte du héros passe en
   blanc franc. Ailleurs sur la page, le gris reste, les cartes lui donnant
   un fond uni. */
:root:not([data-theme="light"]) .hero .lead { color: oklch(0.99 0.004 265); }
:root:not([data-theme="light"]) .hero .eyebrow { color: oklch(0.94 0.006 265); }
:root[data-theme="light"] .hero .lead { color: oklch(0.22 0.02 265); }
:root[data-theme="light"] .hero .eyebrow { color: oklch(0.30 0.02 265); }

/* Les pages intérieures posent aussi du texte directement sur la
   maçonnerie : sans le même traitement, il y devient illisible. Les
   contenus de cartes en sont exclus, ils ont déjà un fond opaque. */
.sur-mur :is(h1, h2, h3, p, li, .eyebrow):not(.card *):not(.table-scroll *) {
  text-shadow:
    0 0 3px oklch(0.05 0.01 40 / .98),
    0 1px 3px oklch(0.05 0.01 40 / .95),
    0 2px 9px oklch(0.05 0.01 40 / .9),
    0 0 22px oklch(0.05 0.01 40 / .8);
}
:root[data-theme="light"] .sur-mur :is(h1, h2, h3, p, li, .eyebrow):not(.card *):not(.table-scroll *) {
  text-shadow:
    0 0 3px oklch(1 0 0 / .98),
    0 1px 3px oklch(1 0 0 / .95),
    0 2px 9px oklch(1 0 0 / .92),
    0 0 22px oklch(1 0 0 / .85);
}
:root:not([data-theme="light"]) .sur-mur :is(h1, .lead):not(.card *) { color: oklch(0.99 0.004 265); }
:root[data-theme="light"] .sur-mur :is(h1, .lead):not(.card *) { color: oklch(0.20 0.02 265); }
.sur-mur .btn, .sur-mur .chip { text-shadow: none; }

/* ---------- Surface de lecture ----------
   Les pages légales se lisent ligne à ligne, parfois trois écrans durant.
   L'ombre portée de .sur-mur détache un titre de la maçonnerie ; elle ne
   suffit pas à un texte long, qui finit par fatiguer l'œil sur un fond
   chargé. Ces pages posent donc une surface opaque — la seule du site à le
   faire, et c'est assumé : on ne demande pas à un visiteur d'admirer un mur
   pendant qu'il lit des conditions de vente.

   La classe .card est conservée : c'est elle qui, via le :not(.card *) de
   .sur-mur, annule les ombres portées devenues inutiles sur fond plein. */
.lecture {
  padding: clamp(1.3rem, 4vw, 2.6rem);
  margin-top: .5rem;
  background: color-mix(in oklch, var(--surface) 96%, transparent);
}
.lecture > :first-child { margin-top: 0; }
.lecture > :last-child  { margin-bottom: 0; }
.lecture h2 { margin-top: 2.2rem; }
.lecture h3 { margin-top: 1.6rem; }
.lecture code {
  font-family: var(--mono); font-size: .88em;
  background: var(--surface-2); border: 1px solid var(--line-soft);
  border-radius: var(--r-sm); padding: .1em .4em;
}

/* ---------- Lisibilité sans voile ----------
   Le mur ne doit rien recevoir par-dessus. Le contraste est donc porté par
   le texte lui-même : une ombre sombre serrée autour des lettres, qui les
   détache de la maçonnerie sans en masquer un seul pixel. */
.hero h1, .hero .lead, .hero .eyebrow {
  text-shadow:
    0 0 3px oklch(0.05 0.01 40 / .98),
    0 1px 3px oklch(0.05 0.01 40 / .95),
    0 2px 9px oklch(0.05 0.01 40 / .9),
    0 0 22px oklch(0.05 0.01 40 / .8),
    0 0 44px oklch(0.05 0.01 40 / .6);
}
/* Le titre garde en plus sa lueur colorée, posée par-dessus l'ombre. */
:root:not([data-theme="light"]) .hero h1 {
  text-shadow:
    0 1px 2px oklch(0.06 0.01 40 / .95),
    0 2px 12px oklch(0.06 0.01 40 / .85),
    0 0 30px oklch(0.06 0.01 40 / .7),
    0 0 46px color-mix(in oklch, var(--accent) 32%, transparent);
  animation: none;
}
/* En plein jour, l'ombre s'inverse : un halo clair derrière un texte sombre. */
:root[data-theme="light"] .hero h1,
:root[data-theme="light"] .hero .lead,
:root[data-theme="light"] .hero .eyebrow {
  text-shadow:
    0 0 3px oklch(1 0 0 / .98),
    0 1px 3px oklch(1 0 0 / .95),
    0 2px 9px oklch(1 0 0 / .92),
    0 0 22px oklch(1 0 0 / .85),
    0 0 44px oklch(1 0 0 / .65);
}
/* Les boutons posent leur propre fond, ils n'en ont pas besoin. */
.hero .btn { text-shadow: none; }

/* Le texte en dégradé est découpé dans son fond : une ombre portée passe
   au travers et le délave. Il lui faut une ombre sur la forme des lettres,
   pas derrière elles — et un dégradé assez sombre pour tenir en plein jour.
   Le traitement vaut partout où la signature est posée sur le mur, pas
   seulement dans le héros de l'accueil : sans lui, les quatre ombres
   blanches de .sur-mur traversent les lettres et les délavent. */
:root[data-theme="light"] .hero .gradient-text,
:root[data-theme="light"] .sur-mur .gradient-text {
  text-shadow: none;
  /* Valeurs assombries a L=0.48, chroma maximale tenant dans le gamut sRGB :
     7.1:1 / 6.7:1 / 7.3:1 sur blanc. Memes teintes que le ruban de marque. */
  background: linear-gradient(115deg,
    oklch(0.48 0.125 215) 0%, oklch(0.48 0.165 252) 34%,
    oklch(0.48 0.175 292) 68%, oklch(0.48 0.125 215) 100%);
  -webkit-background-clip: text; background-clip: text;
  filter:
    drop-shadow(0 1px 2px oklch(1 0 0 / .95))
    drop-shadow(0 0 9px oklch(1 0 0 / .85))
    drop-shadow(0 0 20px oklch(1 0 0 / .6));
}
:root:not([data-theme="light"]) .hero .gradient-text,
:root:not([data-theme="light"]) .sur-mur .gradient-text {
  text-shadow: none;
  filter:
    drop-shadow(0 1px 2px oklch(0.05 0.01 40 / .9))
    drop-shadow(0 0 18px color-mix(in oklch, var(--amber) 55%, transparent));
}

/* ---------- Texte en dégradé animé ---------- */
.gradient-text {
  background: var(--grad-marque);
  background-size: 220% 100%;
  -webkit-background-clip: text; background-clip: text;
  color: transparent;
  animation: glisse 11s ease-in-out infinite alternate;
}
@keyframes glisse { to { background-position: 100% 0; } }

/* ---------- Bordures en dégradé ---------- */
/* Le dégradé est peint dans la bordure elle-même : un fond en deux couches,
   l'une limitée au contenu, l'autre à la boîte entière. */
.card-neon {
  position: relative;
  background:
    linear-gradient(var(--surface), var(--surface)) padding-box,
    var(--grad-rich) border-box;
  border: 1px solid transparent;
}

.card-link:hover {
  border-color: transparent;
  background:
    linear-gradient(var(--surface-2), var(--surface-2)) padding-box,
    var(--grad-rich) border-box;
  box-shadow: 0 0 0 1px color-mix(in oklch, var(--accent) 22%, transparent),
              0 14px 46px color-mix(in oklch, var(--accent) 26%, transparent);
}

/* Lueur d'ambiance derrière une carte mise en avant */
.halo { position: relative; }
.halo::before {
  content: "";
  position: absolute; inset: -1px;
  border-radius: inherit;
  background: var(--grad-rich);
  filter: blur(22px);
  opacity: .32;
  z-index: -1;
  transition: opacity .35s;
}
.halo:hover::before { opacity: .5; }

/* ---------- Filets lumineux ---------- */
.filet {
  height: 1px; border: 0; margin: 0;
  background: var(--grad-ligne);
  opacity: .5;
}

section + section { border-top: 1px solid transparent; }

/* ---------- Bandes de section ----------
   Translucides : l'aurore du fond, qui est fixe, doit rester visible d'un
   bout à l'autre de la page. Un fond opaque la couperait net sous le héros. */
.bande {
  position: relative;
  background: color-mix(in oklch, var(--bg-deep) 84%, transparent);
  border-block: 1px solid var(--line-soft);
  /* Pas de flou ici : il brouillait le mur sur toute la hauteur des bandes. */
  backdrop-filter: saturate(1.05);
  -webkit-backdrop-filter: saturate(1.05);
}
:root[data-theme="light"] .bande { background: color-mix(in oklch, var(--bg-deep) 76%, transparent); }

/* Filet lumineux en haut de bande */
.bande::before {
  content: "";
  position: absolute; inset: 0 0 auto 0; height: 1px;
  background: var(--grad-ligne);
  opacity: .55;
}

/* Les cartes sont assez opaques pour se passer de flou — celui-ci rendait
   la maçonnerie trouble tout autour d'elles. */

.footer { background: color-mix(in oklch, var(--bg-deep) 82%, transparent); }

/* ---------- Bandeau défilant ---------- */
.ruban {
  overflow: hidden;
  border-block: 1px solid var(--line-soft);
  background: color-mix(in oklch, var(--bg-deep) 70%, transparent);
  padding-block: .9rem;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
}
.ruban-piste {
  display: flex; gap: 3rem; width: max-content;
  animation: ruban 38s linear infinite;
}
.ruban span {
  font-family: var(--display); font-size: .95rem; font-weight: 500;
  color: var(--text-dim); white-space: nowrap;
  display: flex; align-items: center; gap: 3rem;
}
.ruban span::after {
  content: ""; width: 5px; height: 5px; border-radius: 50%;
  background: var(--grad-rich);
}
@keyframes ruban { to { transform: translateX(-50%); } }
.ruban-piste:hover { animation-play-state: paused; }

/* ---------- Boutons ---------- */
.btn-primary {
  background: var(--grad-marque);
  background-size: 200% 100%;
  animation: glisse 8s ease-in-out infinite alternate;
}
.btn-primary:hover { animation-play-state: paused; }

/* ---------- Pastilles d'indicateur ---------- */
.kpi-carte {
  position: relative;
  overflow: hidden;
}
.kpi-carte::after {
  content: "";
  position: absolute; inset: auto 0 0 0; height: 2px;
  background: var(--grad-rich);
  transform: scaleX(0); transform-origin: left;
  transition: transform .5s cubic-bezier(.2,.7,.3,1);
}
.kpi-carte[data-seen]::after { transform: scaleX(1); }

/* ---------- Respect des préférences système ---------- */
@media (prefers-reduced-motion: reduce) {
  .hero-grille, .balayage::after, .gradient-text,
  .ruban-piste, .btn-primary { animation: none !important; }
  .ruban-piste { width: max-content; }
  .kpi-carte::after { transition: none; transform: scaleX(1); }
}

/* ====================================================================
   Néon
   Les lueurs sont réservées au thème sombre : en clair elles salissent
   le texte au lieu de l'éclairer. Le corps de texte n'est jamais touché,
   seuls les titres, bordures et accents le sont — une lueur sur du texte
   de lecture le rend flou.
   ==================================================================== */

:root {
  /* Jeu de lueurs reutilisables, nommees d'apres la couleur qu'elles portent. */
  --neon-marque: 0 0 12px color-mix(in oklch, var(--accent) 70%, transparent);
  --neon-cyan:   0 0 12px color-mix(in oklch, var(--cyan) 70%, transparent);
  --neon-ambre:  0 0 12px color-mix(in oklch, var(--amber) 70%, transparent);
}

/* ---------- Le néon ne s'applique qu'en sombre ---------- */
:root:not([data-theme="light"]) {

  /* Titres : halo coloré, discret sur les gros corps */
  h1, h2 { text-shadow: 0 0 28px color-mix(in oklch, var(--accent) 34%, transparent); }
  .gradient-text { filter: drop-shadow(0 0 22px color-mix(in oklch, var(--amber) 45%, transparent)); }

  /* Toutes les cartes prennent la bordure en dégradé */
  .card {
    background:
      linear-gradient(color-mix(in oklch, var(--surface) 95%, transparent),
                      color-mix(in oklch, var(--surface) 95%, transparent)) padding-box,
      linear-gradient(150deg,
        color-mix(in oklch, var(--accent) 55%, transparent),
        color-mix(in oklch, var(--line-soft) 90%, transparent) 42%,
        color-mix(in oklch, var(--cyan) 45%, transparent)) border-box;
    border: 1px solid transparent;
  }
  .card:hover {
    box-shadow: 0 0 0 1px color-mix(in oklch, var(--accent) 26%, transparent),
                0 0 34px -8px color-mix(in oklch, var(--accent) 50%, transparent),
                0 18px 46px -14px color-mix(in oklch, var(--amber) 40%, transparent);
  }

  /* Marque : pastille qui respire */
  .brand .mark { animation: respire 3.4s ease-in-out infinite; }

  /* Boutons */
  .btn-primary {
    box-shadow: 0 1px 0 color-mix(in oklch, white 28%, transparent) inset,
                0 0 22px color-mix(in oklch, var(--accent) 55%, transparent),
                0 8px 34px -6px color-mix(in oklch, var(--amber) 55%, transparent);
  }
  .btn-primary:hover {
    box-shadow: 0 1px 0 color-mix(in oklch, white 36%, transparent) inset,
                0 0 34px color-mix(in oklch, var(--accent) 75%, transparent),
                0 12px 46px -6px color-mix(in oklch, var(--amber) 70%, transparent);
  }
  .btn-ghost:hover {
    border-color: color-mix(in oklch, var(--cyan) 65%, transparent);
    box-shadow: 0 0 20px -4px color-mix(in oklch, var(--cyan) 55%, transparent);
  }

  /* Pastilles colorées */
  .chip-ok     { box-shadow: 0 0 16px -4px color-mix(in oklch, var(--lime) 70%, transparent); }
  .chip-info   { box-shadow: 0 0 16px -4px color-mix(in oklch, var(--cyan) 70%, transparent); }
  .chip-marque { box-shadow: 0 0 16px -4px color-mix(in oklch, var(--accent) 75%, transparent); }
  .chip-warn   { box-shadow: 0 0 16px -4px color-mix(in oklch, var(--amber) 65%, transparent); }
  .chip-bad    { box-shadow: 0 0 16px -4px color-mix(in oklch, var(--rose) 65%, transparent); }
  .chip-dot::before { box-shadow: 0 0 9px 1px currentColor; }

  /* Filets et séparateurs */
  .bande::before { opacity: .9; box-shadow: 0 0 14px color-mix(in oklch, var(--cyan) 55%, transparent); }
  .footer { border-top-color: transparent; box-shadow: inset 0 1px 0 color-mix(in oklch, var(--accent) 40%, transparent); }

  /* Étiquette de section : le trait devient une barre lumineuse */
  .eyebrow::before {
    height: 2px; width: 30px; border-radius: 2px;
    box-shadow: 0 0 12px color-mix(in oklch, var(--cyan) 85%, transparent);
  }

  /* Soulignement des indicateurs */
  .kpi-carte::after { box-shadow: 0 0 16px color-mix(in oklch, var(--accent) 80%, transparent); }

  /* Champs de saisie */
  input:focus, select:focus, textarea:focus {
    box-shadow: 0 0 0 3px var(--accent-soft),
                0 0 22px -6px color-mix(in oklch, var(--accent) 70%, transparent);
  }
}

@keyframes respire {
  0%, 100% { box-shadow: 0 0 0 1px color-mix(in oklch, var(--accent) 45%, transparent),
                         0 0 14px color-mix(in oklch, var(--accent) 55%, transparent); }
  50%      { box-shadow: 0 0 0 1px color-mix(in oklch, var(--cyan) 55%, transparent),
                         0 0 26px color-mix(in oklch, var(--amber) 75%, transparent); }
}

/* ---------- Liens de navigation : soulignement néon ---------- */
.nav-links a { position: relative; }
.nav-links a::after {
  content: "";
  position: absolute; left: .8rem; right: .8rem; bottom: .2rem; height: 1px;
  background: var(--grad-rich);
  transform: scaleX(0); transform-origin: center;
  transition: transform .28s cubic-bezier(.2,.7,.3,1);
}
.nav-links a:hover::after,
.nav-links a[aria-current="page"]::after { transform: scaleX(1); }
:root:not([data-theme="light"]) .nav-links a::after {
  box-shadow: 0 0 10px color-mix(in oklch, var(--cyan) 90%, transparent);
}

/* ---------- Angles façon interface technique ---------- */
.hud { position: relative; }
.hud::before, .hud::after {
  content: "";
  position: absolute; width: 20px; height: 20px;
  border-style: solid;
  border-color: color-mix(in oklch, var(--cyan) 80%, transparent);
  border-width: 0;
  opacity: .75;
  pointer-events: none;
}
/* Deux bords seulement : l'angle doit se lire comme une équerre, pas comme
   un carré — à 13 px la nuance disparaissait. */
.hud::before { top: 8px; left: 8px;
  border-top-width: 1.5px; border-left-width: 1.5px; border-radius: 6px 0 0 0; }
.hud::after  { bottom: 8px; right: 8px;
  border-bottom-width: 1.5px; border-right-width: 1.5px; border-radius: 0 0 6px 0; }
:root:not([data-theme="light"]) .hud::before,
:root:not([data-theme="light"]) .hud::after {
  filter: drop-shadow(0 0 5px color-mix(in oklch, var(--cyan) 80%, transparent));
}

/* ---------- Lignes de balayage ----------
   Très discrètes : au-delà, elles fatiguent l'œil sur une page qu'on lit. */
:root:not([data-theme="light"]) body::after {
  /* Plus de lignes de balayage colorees : elles voilaient la photographie
     d'un gris bleute sur toute la page. */
  opacity: 0;
}

/* ---------- Titre du héros : lueur pulsée ---------- */
:root:not([data-theme="light"]) .hero h1 {
  animation: pulse-titre 7s ease-in-out infinite;
}
@keyframes pulse-titre {
  0%, 100% { text-shadow: 0 0 28px color-mix(in oklch, var(--accent) 30%, transparent); }
  50%      { text-shadow: 0 0 46px color-mix(in oklch, var(--amber) 46%, transparent); }
}

@media (prefers-reduced-motion: reduce) {
  .brand .mark, .hero h1 { animation: none !important; }
  .nav-links a::after { transition: none; }
}


/* ====================================================================
   Mur et enseigne
   Le mur est dessiné en SVG — aucune photo, quelques centaines d'octets,
   net à toutes les résolutions. L'aurore colorée passe par-dessus : elle
   se lit alors comme la lumière des néons projetée sur la pierre.
   ==================================================================== */

:root {
  /* Le fond de page n'est plus une photographie. La maçonnerie se répétait
     proprement parce qu'une brique n'a pas de sens de lecture ; un flux de
     données en a un, et sa mosaïque laissait des coutures visibles tous les
     2106 px. La grille est donc dessinée en dégradés : elle se répète sans
     raccord, pèse zéro octet et reste nette à toute résolution.
     Les photographies sous licence servent là où elles ont une place
     tenue : le prisme sur le héros, le flux sur les bandes. */
  --grille-pas: 46px;
  --grille-trait: color-mix(in oklch, var(--hologramme) 7%, transparent);
  --grille-fort:  color-mix(in oklch, var(--hologramme) 13%, transparent);
}

body {
  background-color: var(--bg);
  background-image:
    /* croix aux intersections, tous les 5 pas */
    linear-gradient(var(--grille-fort) 1px, transparent 1px),
    linear-gradient(90deg, var(--grille-fort) 1px, transparent 1px),
    /* trame fine */
    linear-gradient(var(--grille-trait) 1px, transparent 1px),
    linear-gradient(90deg, var(--grille-trait) 1px, transparent 1px),
    /* halo froid en haut de page, qui donne la profondeur */
    radial-gradient(120% 70% at 50% -10%,
      color-mix(in oklch, var(--azur) 16%, transparent) 0%, transparent 62%);
  background-size:
    calc(var(--grille-pas) * 5) calc(var(--grille-pas) * 5),
    calc(var(--grille-pas) * 5) calc(var(--grille-pas) * 5),
    var(--grille-pas) var(--grille-pas),
    var(--grille-pas) var(--grille-pas),
    100% 100%;
  background-attachment: fixed;
}
:root[data-theme="light"] body {
  background-color: var(--bg);
  --grille-trait: color-mix(in oklch, var(--hologramme) 11%, transparent);
  --grille-fort:  color-mix(in oklch, var(--hologramme) 18%, transparent);
}

/* Voile neutralise : la photographie est demandée sans aucun filtre.
   Remettre un de ces blocs suffit à récupérer du contraste — voir la note
   dans docs/site-vitrine.md. */
.voile-mur { display: none; }
.voile-mur--actif {
  position: fixed; inset: 0; z-index: -5; pointer-events: none;
  background:
    radial-gradient(130% 85% at 50% 0%, transparent 42%, oklch(0.12 0.02 45 / .52) 100%),
    linear-gradient(oklch(0.13 0.02 45 / .24), oklch(0.12 0.02 45 / .46));
}
:root[data-theme="light"] .voile-mur--actif {
  background:
    radial-gradient(130% 85% at 50% 0%, transparent 40%, oklch(0.99 0.005 70 / .38) 100%),
    linear-gradient(oklch(0.99 0.005 70 / .30), oklch(0.99 0.005 70 / .48));
}

/* Le héros laisse voir le mur davantage : c'est là qu'est l'enseigne. */
.fond-sobre .hero::before {
  content: "";
  position: absolute; inset: 0; z-index: -3; pointer-events: none;
  background: radial-gradient(75% 75% at 50% 40%, oklch(0.10 0.02 45 / 0) 0%, oklch(0.10 0.02 45 / .30) 100%);
}
/* L'ancienne regle posait ici un voile blanc avec la propriete raccourcie
   « background », ce qui effacait l'image du heros : plus specifique que la
   regle qui la pose, elle gagnait, et le visuel n'existait pas en plein
   jour. Le voile est desormais empile AU-DESSUS de l'image, plus bas. */

/* Les particules deviennent de la poussière lumineuse devant le mur. */
.hero-media canvas { opacity: .38; }
:root[data-theme="light"] .hero-media canvas { opacity: .3; }

/* Voile local derrière le seul texte du héros. */
.hero .wrap { position: relative; }
.fond-lisible .hero .wrap::before,
.fond-sobre .hero .wrap::before {
  content: "";
  position: absolute; inset: -6% -10% -12% -10%;
  z-index: -1; pointer-events: none;
  background: radial-gradient(72% 92% at 26% 52%,
    oklch(0.10 0.02 45 / .90) 0%, oklch(0.10 0.02 45 / .70) 48%, transparent 80%);
}
:root[data-theme="light"] .hero .wrap::before {
  background: radial-gradient(64% 86% at 26% 52%,
    oklch(1 0 0 / .86) 0%, oklch(1 0 0 / .62) 46%, transparent 74%);
}

/* ---------- Enseigne néon ----------
   C'est la photographie fournie, détourée. Le seuil de détourage a coupé
   le halo diffus : il est rendu ici en ombres portées, qui suivent la
   forme du tube et non son rectangle. Le mélange « screen » fait le reste
   — c'est ainsi que la lumière se compose sur une surface. */

:root { --neon: oklch(0.86 0.15 202); }

/* ---------- Enseigne Signentis ----------
   L'enseigne etait une photographie de tube au neon : le nom y etait grave
   dans les pixels, et aucune substitution de texte ne pouvait le changer.
   Elle est remplacee par du texte reel, mis en lumiere en CSS. On y gagne
   choses : le nom se change en une ligne, le texte est selectionnable et lu
   par les lecteurs d'ecran, et le poids tombe de 437 Ko a zero. */
.enseigne {
  position: relative;
  display: block;
  width: max-content;
  max-width: 100%;
  margin: 0 0 clamp(1.6rem, 4.5vw, 3.4rem);
  padding: clamp(.9rem, 2.4vw, 1.6rem) clamp(1.4rem, 4vw, 2.8rem);
  isolation: isolate;
}

.enseigne-nom {
  display: block;
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(2.6rem, 10.5vw, 7rem);
  line-height: .96;
  letter-spacing: clamp(.01em, .6vw, .05em);
  text-transform: uppercase;
  color: oklch(0.99 0.01 200);
  /* Un tube au neon ne rayonne pas en une passe : le halo se construit par
     couches, de la plus serree a la plus large. */
  text-shadow:
    0 0 2px  color-mix(in oklch, var(--neon) 95%, transparent),
    0 0 10px color-mix(in oklch, var(--neon) 85%, transparent),
    0 0 26px color-mix(in oklch, var(--neon) 62%, transparent),
    0 0 56px color-mix(in oklch, var(--neon) 42%, transparent),
    0 0 96px color-mix(in oklch, var(--azur)  34%, transparent);
  animation: neon-scintille 9.2s steps(1, end) infinite;
}

/* Mention de service sous le nom, en telemetrie. */
.enseigne-sous {
  display: flex;
  align-items: center;
  gap: .7rem;
  /* Le halo du nom descend bas : sans cette marge, la mention passe
     dessous et la premiere lettre devient illisible. */
  margin-top: clamp(1.1rem, 2.6vw, 1.8rem);
  font-family: var(--mono);
  font-size: clamp(.6rem, 1.5vw, .74rem);
  letter-spacing: .34em;
  text-transform: uppercase;
  color: color-mix(in oklch, var(--hologramme) 82%, var(--text));
}
.enseigne-sous::before {
  content: "";
  width: clamp(1.4rem, 5vw, 3.2rem); height: 1px;
  background: linear-gradient(90deg, transparent, var(--hologramme));
}
/* Temoin d'activite : il respire, il ne clignote pas. */
.enseigne-sous::after {
  content: "";
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--hologramme);
  box-shadow: 0 0 10px var(--hologramme);
  animation: pouls 2.6s ease-in-out infinite;
}
@keyframes pouls {
  0%, 100% { opacity: 1; transform: scale(1); }
  50%      { opacity: .35; transform: scale(.8); }
}

/* Equerres de visee facon viseur. Les quatre coins sont dessines en une
   seule passe avec deux pseudo-elements et des degrades lineaires. */
.enseigne::before {
  content: "";
  position: absolute; inset: 0;
  z-index: -1; pointer-events: none;
  background:
    linear-gradient(var(--hologramme), var(--hologramme)) 0    0    / 1.4rem 1px no-repeat,
    linear-gradient(var(--hologramme), var(--hologramme)) 0    0    / 1px 1.4rem no-repeat,
    linear-gradient(var(--hologramme), var(--hologramme)) 100% 0    / 1.4rem 1px no-repeat,
    linear-gradient(var(--hologramme), var(--hologramme)) 100% 0    / 1px 1.4rem no-repeat,
    linear-gradient(var(--hologramme), var(--hologramme)) 0    100% / 1.4rem 1px no-repeat,
    linear-gradient(var(--hologramme), var(--hologramme)) 0    100% / 1px 1.4rem no-repeat,
    linear-gradient(var(--hologramme), var(--hologramme)) 100% 100% / 1.4rem 1px no-repeat,
    linear-gradient(var(--hologramme), var(--hologramme)) 100% 100% / 1px 1.4rem no-repeat;
  opacity: .55;
}

/* Halo projete derriere le nom, qui bat lentement. */
.enseigne::after {
  content: "";
  position: absolute; inset: -40% -18%;
  z-index: -2; pointer-events: none;
  background: radial-gradient(52% 50% at 50% 50%,
    color-mix(in oklch, var(--neon) 26%, transparent) 0%,
    color-mix(in oklch, var(--azur) 13%, transparent) 46%,
    transparent 74%);
  animation: lueur-mur 6s ease-in-out infinite;
}

/* Un neon qui peine a s'allumer : saccades breves et irregulieres, jamais un
   clignotement regulier — le regulier se lit comme une panne d'ecran. */
@keyframes neon-scintille {
  0%, 7.2%   { opacity: 1; }
  7.5%, 8.3% { opacity: .3; }
  7.9%       { opacity: .88; }
  8.6%, 61%  { opacity: 1; }
  61.4%      { opacity: .42; }
  61.8%, 100%{ opacity: 1; }
}
@keyframes lueur-mur {
  0%, 100% { opacity: .8; }
  50%      { opacity: 1; }
}
/* En plein jour, le halo d'un neon ne porte plus : le nom passe en encre
   sombre et ne garde qu'un fin liseré lumineux, sinon il disparait. */
:root[data-theme="light"] .enseigne-nom {
  color: oklch(0.30 0.06 230);
  text-shadow:
    0 0 1px  color-mix(in oklch, var(--hologramme) 55%, transparent),
    0 0 14px color-mix(in oklch, var(--hologramme) 30%, transparent);
  animation: none;
}
:root[data-theme="light"] .enseigne::before { opacity: .3; }
:root[data-theme="light"] .enseigne::after  { opacity: .35; }

@media (prefers-reduced-motion: reduce) {
  .enseigne-nom, .enseigne::after, .enseigne-sous::after { animation: none !important; }
}

/* ======================================================================
   Couche HUD — identite Signentis
   ----------------------------------------------------------------------
   Quatre elements, tous decoratifs et tous en aria-hidden : le prisme,
   un balayage, un reticule et une bande de telemetrie. Ils ne portent
   aucune information : si l'un d'eux ne s'affiche pas, rien n'est perdu.
   Tous s'arretent sous prefers-reduced-motion.
   ====================================================================== */

/* ---------- Fond de prisme ----------
   Photographie sous licence Adobe Stock (ref. 1816701387), dont les teintes
   ont ete remappees sur la palette : l'image d'origine va du bleu au rouge
   en passant par le magenta, cet arc de 190 degres a ete replie sur
   185-300, soit cyan -> azur -> violet. Les pixels a faible saturation ou
   tres sombres sont laisses intacts, sinon le fond noir se teinte et la
   zone de texte est perdue.
   L'anneau neon (ref. 621805531) reste dans img/anneau.webp : il suffit de
   changer les deux urls ci-dessous pour y revenir. */
.hero::before {
  content: "";
  position: absolute; inset: 0;
  z-index: -3; pointer-events: none;
  /* Voile en biais sur la colonne de texte. L'anneau est magnifique mais sa
     fumee est claire, et le paragraphe tombait dessus a 3:1 — sous le seuil
     AA de 4,5:1 pour du texte courant. Le voile assombrit le tiers gauche et
     laisse l'anneau intact a droite. */
  background:
    linear-gradient(100deg,
      oklch(0.07 0.02 245 / .62) 0%,
      oklch(0.07 0.02 245 / .50) 32%,
      oklch(0.07 0.02 245 / .28) 50%,
      oklch(0.07 0.02 245 / .08) 66%,
      transparent 80%) 0 0 / 100% 100% no-repeat,
    url("img/prisme.webp") center / cover no-repeat,
    var(--bg-deep);
  /* L'image s'eteint vers le bas pour rejoindre le fond de page sans
     couture visible. */
  -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 54%, transparent 94%);
  mask-image: linear-gradient(to bottom, #000 0%, #000 58%, transparent 96%);
  opacity: 1;
}
@media (max-width: 760px) {
  .hero::before { background-image: url("img/prisme-petit.webp"); }
}

:root[data-theme="light"] .hero::before {
  /* En plein jour l'image est conservee telle quelle — l'inverser la
     reduisait a un lavis invisible. Deux voiles blancs sont empiles
     par-dessus : un en biais sur la colonne de texte, un radial qui
     eclaircit l'ensemble pour rejoindre le fond de page. */
  background:
    linear-gradient(100deg,
      oklch(1 0 0 / .90) 0%,
      oklch(1 0 0 / .82) 30%,
      oklch(1 0 0 / .52) 50%,
      oklch(1 0 0 / .18) 68%,
      oklch(1 0 0 / 0) 82%) 0 0 / 100% 100% no-repeat,
    radial-gradient(78% 78% at 50% 38%,
      oklch(1 0 0 / .10) 0%, oklch(1 0 0 / .46) 100%) 0 0 / 100% 100% no-repeat,
    url("img/prisme.webp") center / cover no-repeat,
    var(--bg);
  opacity: .62;
  filter: none;
}
@media (max-width: 760px) {
  :root[data-theme="light"] .hero::before { background-image:
    linear-gradient(100deg, oklch(1 0 0 / .90) 0%, oklch(1 0 0 / .82) 30%,
      oklch(1 0 0 / .52) 50%, oklch(1 0 0 / .18) 68%, oklch(1 0 0 / 0) 82%),
    radial-gradient(78% 78% at 50% 38%, oklch(1 0 0 / .10) 0%, oklch(1 0 0 / .46) 100%),
    url("img/prisme-petit.webp"); }
}

/* ---------- Balayage ----------
   Une barre de lumiere qui descend lentement, comme un scanner. Elle passe
   toutes les 7 s et reste discrete : au-dela, elle devient un gyrophare. */
.hero-scan {
  position: absolute; inset: 0;
  z-index: -1; pointer-events: none;
  overflow: clip;
}
.hero-scan::before {
  content: "";
  position: absolute; left: 0; right: 0; height: 28vh;
  background: linear-gradient(to bottom,
    transparent,
    color-mix(in oklch, var(--hologramme) 7%, transparent) 46%,
    color-mix(in oklch, var(--hologramme) 13%, transparent) 50%,
    transparent);
  animation: balayage-vertical 7s cubic-bezier(.4, 0, .6, 1) infinite;
}
@keyframes balayage-vertical {
  0%        { transform: translateY(-30vh); opacity: 0; }
  12%, 72%  { opacity: 1; }
  100%      { transform: translateY(110vh); opacity: 0; }
}

/* ---------- Reticule ----------
   Deux anneaux concentriques qui tournent en sens inverse. Purement
   decoratif, place dans l'angle et masque sous 900 px : sur un telephone
   il n'y a pas la place, et il mangerait le texte. */
.visee {
  position: absolute;
  top: clamp(4rem, 9vw, 7rem); right: clamp(1.5rem, 5vw, 5rem);
  width: clamp(120px, 15vw, 210px); aspect-ratio: 1;
  z-index: -1; pointer-events: none;
  opacity: .5;
}
.visee::before, .visee::after {
  content: "";
  position: absolute; inset: 0;
  border-radius: 50%;
  /* Un anneau pointille : les tirets sont dessines par le degrade conique,
     ce qui evite un SVG et reste net a toute taille. */
  border: 1px solid transparent;
  background:
    conic-gradient(from 0deg,
      var(--hologramme) 0 3deg, transparent 3deg 18deg) border-box;
  -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 2px), #000 calc(100% - 1px));
  mask: radial-gradient(farthest-side, transparent calc(100% - 2px), #000 calc(100% - 1px));
  animation: tourne 28s linear infinite;
}
.visee::after {
  inset: 22%;
  animation-duration: 17s;
  animation-direction: reverse;
  opacity: .75;
}
@keyframes tourne { to { transform: rotate(1turn); } }
@media (max-width: 900px) { .visee { display: none; } }

/* ---------- Telemetrie ----------
   Bande de valeurs en chasse fixe sous le hero. C'est du decor assume :
   les chiffres ne sont relies a rien, et le texte le dit. */
.telemetrie {
  display: flex;
  flex-wrap: wrap;
  gap: clamp(.8rem, 3vw, 2.2rem);
  margin-top: clamp(1.6rem, 4vw, 2.6rem);
  padding-top: clamp(.9rem, 2.4vw, 1.4rem);
  border-top: 1px solid color-mix(in oklch, var(--hologramme) 24%, transparent);
  font-family: var(--mono);
  font-size: clamp(.58rem, 1.5vw, .7rem);
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--text-dim);
}
.telemetrie b {
  color: color-mix(in oklch, var(--hologramme) 86%, var(--text));
  font-weight: 500;
}
/* Le curseur de fin de ligne, qui bat comme un terminal. */
.telemetrie span:last-child::after {
  content: "_";
  color: var(--hologramme);
  animation: curseur 1.1s steps(2, end) infinite;
}
@keyframes curseur { 50% { opacity: 0; } }

@media (prefers-reduced-motion: reduce) {
  .hero-scan::before, .visee::before, .visee::after,
  .telemetrie span:last-child::after { animation: none !important; }
  .hero-scan::before { opacity: 0; }
}

/* ---------- Flux sur les bandes ----------
   La seconde photographie sous licence (Adobe Stock, réf. 1687968410) ne
   tapisse rien : elle habille les sections en bande, une fois, en « cover ».
   Elle reste sous un voile dense — c'est un fond, pas un sujet. */
.bande { position: relative; isolation: isolate; }
.bande::before {
  content: "";
  position: absolute; inset: 0;
  z-index: -1; pointer-events: none;
  background: url("img/flux.webp") center / cover no-repeat;
  opacity: .38;
  -webkit-mask-image: linear-gradient(to bottom, transparent, #000 22%, #000 78%, transparent);
  mask-image: linear-gradient(to bottom, transparent, #000 22%, #000 78%, transparent);
}
:root[data-theme="light"] .bande::before { opacity: .10; }

/* ---------- Choix de rubriques (page contact) ----------
   Des cases a cocher presentees en pastilles. La premiere version empilait
   huit cartes avec leur description : 881 px de haut sur un telephone, soit
   trois ecrans avant d'atteindre le formulaire. Les descriptions faisaient
   doublon avec la page d'accueil, qui les porte deja. La case native reste
   dans le DOM, invisible mais focalisable : clavier et lecteurs d'ecran
   fonctionnent sans code supplementaire. */
.choix-grille {
  display: flex;
  flex-wrap: wrap;
  gap: .55rem;
  margin-bottom: 1.8rem;
}
.choix {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  padding: .5rem .9rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--bg-deep);
  font-size: .9rem;
  line-height: 1.2;
  cursor: pointer;
  user-select: none;
  transition: border-color .16s, background .16s, color .16s;
}
.choix:hover { border-color: color-mix(in oklch, var(--accent) 50%, var(--line)); }
.choix input {
  width: .95rem; height: .95rem;
  margin: 0;
  flex: 0 0 auto;
  accent-color: var(--accent);
  cursor: pointer;
}
.choix:has(input:checked) {
  border-color: color-mix(in oklch, var(--accent) 65%, transparent);
  background: color-mix(in oklch, var(--accent) 12%, var(--bg-deep));
  color: color-mix(in oklch, var(--accent) 30%, var(--text));
}
.choix:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }

.erreur {
  margin: 1rem 0 0;
  padding: .7rem .9rem;
  border-radius: var(--r-sm);
  font-size: .9rem;
  color: var(--rose);
  border: 1px solid color-mix(in oklch, var(--rose) 35%, transparent);
  background: color-mix(in oklch, var(--rose) 12%, transparent);
}

/* ---------- Halos animés du hero ----------
   Deux nappes de couleur posées sur le prisme, plus un anneau en orbite.

   Les deux nappes sont en mix-blend-mode: color. Ce mode ne retient que la
   teinte et la saturation du calque et conserve la luminosité de l'image
   dessous : la couleur peut dériver autant qu'on veut sans déplacer d'un
   iota le contraste du texte. Un voile opaque, lui, aurait fait retomber le
   paragraphe sous le seuil AA — c'est exactement ce qui était arrivé avec
   l'anneau néon.

   À gauche la teinte de l'arc tourne ; à droite la bulle dérive, respire et
   change de couleur. Les deux cycles sont de durées premières entre elles
   (23 s et 19 s) pour que la boucle ne se referme jamais au même endroit. */
.halos {
  position: absolute;
  inset: 0;
  z-index: -2;
  pointer-events: none;
  overflow: clip;
}

/* L'arc de gauche : balayage de teinte en rotation */
.halos::before {
  content: "";
  position: absolute;
  left: -24%; top: -8%;
  width: 78%; aspect-ratio: 1;
  border-radius: 50%;
  background: conic-gradient(from 0deg,
    var(--hologramme) 0deg,
    var(--azur)      95deg,
    var(--violet)   185deg,
    var(--magenta)  255deg,
    var(--hologramme) 360deg);
  mix-blend-mode: color;
  filter: blur(30px);
  opacity: .55;
  animation: halo-tourne 23s linear infinite;
}

/* La bulle de droite : dérive, respiration et dérive de teinte */
.halos::after {
  content: "";
  position: absolute;
  right: -16%; top: -12%;
  width: 66%; aspect-ratio: 1;
  border-radius: 50%;
  background: radial-gradient(circle at 42% 38%,
    var(--violet) 0%,
    color-mix(in oklch, var(--magenta) 72%, transparent) 40%,
    transparent 72%);
  mix-blend-mode: color;
  opacity: .62;
  animation: bulle-derive 19s ease-in-out infinite;
}

@keyframes halo-tourne { to { transform: rotate(1turn); } }

@keyframes bulle-derive {
  0%, 100% { transform: translate3d(0, 0, 0)     scale(1);    filter: blur(44px) hue-rotate(0deg); }
  33%      { transform: translate3d(-5%, 6%, 0)  scale(1.10); filter: blur(54px) hue-rotate(30deg); }
  66%      { transform: translate3d(4%, -5%, 0)  scale(.94);  filter: blur(38px) hue-rotate(-22deg); }
}

/* En plein jour les nappes saturent vite : on les retient. */
:root[data-theme="light"] .halos::before { opacity: .34; }
:root[data-theme="light"] .halos::after  { opacity: .38; }

/* ---------- Orbite ----------
   L'accent futuriste : un anneau fin qui tourne, posé en bas à gauche, loin
   de la colonne de texte. Deux arcs à vitesses différentes, le second en
   sens inverse. Masqué sous 1100 px — sur un écran étroit il passerait sous
   le paragraphe. */
.orbite {
  position: absolute;
  left: clamp(-6rem, -4vw, -2rem); bottom: clamp(-8rem, -6vw, -3rem);
  width: clamp(240px, 30vw, 440px); aspect-ratio: 1;
  z-index: -1; pointer-events: none;
  opacity: .45;
}
.orbite::before, .orbite::after {
  content: "";
  position: absolute; inset: 0;
  border-radius: 50%;
  background: conic-gradient(from 0deg,
    transparent 0 14deg,
    var(--hologramme) 30deg,
    transparent 58deg 190deg,
    var(--violet) 214deg,
    transparent 244deg) border-box;
  -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 2px), #000 calc(100% - 1px));
  mask: radial-gradient(farthest-side, transparent calc(100% - 2px), #000 calc(100% - 1px));
  animation: halo-tourne 41s linear infinite;
}
.orbite::after {
  inset: 17%;
  animation-duration: 26s;
  animation-direction: reverse;
  opacity: .8;
}
:root:not([data-theme="light"]) .orbite {
  filter: drop-shadow(0 0 7px color-mix(in oklch, var(--hologramme) 55%, transparent));
}
@media (max-width: 1100px) { .orbite { display: none; } }

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

/* ---------- En-tête des pages intérieures ----------
   Même vocabulaire que le hero d'accueil — grille en perspective, balayage,
   halos — mais en bande courte : sur une page de texte, un hero pleine
   hauteur repousse tout le contenu sous la ligne de flottaison. Pas de
   prisme ici, un dégradé suffit et ne coûte aucune image. */
.hero-page {
  position: relative;
  overflow: clip;
  padding-block: clamp(2.6rem, 6vw, 4.5rem) clamp(1.8rem, 4vw, 3rem);
  border-bottom: 1px solid var(--line-soft);
}
.hero-page::before {
  content: "";
  position: absolute; inset: 0;
  z-index: -3; pointer-events: none;
  background:
    radial-gradient(118% 140% at 10% 0%,
      color-mix(in oklch, var(--violet) 20%, transparent), transparent 62%),
    radial-gradient(92% 120% at 96% 8%,
      color-mix(in oklch, var(--hologramme) 16%, transparent), transparent 58%),
    var(--bg-deep);
  -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 64%, transparent 100%);
  mask-image: linear-gradient(to bottom, #000 0%, #000 64%, transparent 100%);
}
.hero-page .wrap { position: relative; }

/* La grille d'accueil est calibrée pour un hero haut : dans une bande courte
   elle doit être plus basse et moins marquée, sinon elle monte dans le titre. */
.hero-page .hero-grille { height: 72%; opacity: .4; }
.hero-page .visee { top: clamp(1.5rem, 4vw, 3rem); }

/* ---------- Voile de lisibilité ----------
   Le voile de .hero::before est un dégradé à 100° : il assombrit le tiers
   gauche, ce qui suffit sur un écran large où le texte tient dans une
   colonne. Sur un téléphone le texte prend toute la largeur et retombait
   sur les rubans clairs du prisme — mesuré à 1,6:1, très en dessous du
   seuil AA de 4,5:1. Ce calque-ci suit la colonne de texte quelle que soit
   la largeur, et se renforce quand elle s'élargit. */
.hero .wrap::before,
.hero-page .wrap::before {
  content: "";
  position: absolute;
  /* Deux reglages separes, et c'est volontaire : le fond donne la force du
     voile (donc le contraste du texte), le masque donne la douceur du bord.
     Les melanger — un degrade qui s'eteint tout seul — oblige a choisir
     entre un bord visible et un texte illisible. */
  inset: -7rem -11rem;
  z-index: -1;
  pointer-events: none;
  background: oklch(0.07 0.02 245 / .87);
  -webkit-mask: radial-gradient(58% 62% at 30% 50%, #000 0%, #000 46%, transparent 80%);
  mask: radial-gradient(58% 62% at 30% 50%, #000 0%, #000 46%, transparent 80%);
}
.hero-page .wrap::before {
  inset: -5rem -9rem;
  /* Plus genereux que sur l'accueil : les pages legales utilisent .narrow,
     une colonne etroite, et le surtitre tombait dans la zone estompee. */
  -webkit-mask: radial-gradient(78% 88% at 35% 50%, #000 0%, #000 58%, transparent 92%);
  mask: radial-gradient(78% 88% at 35% 50%, #000 0%, #000 58%, transparent 92%);
}

/* Sous 860 px la colonne occupe toute la largeur : le dégradé radial laisse
   filer les bords, il faut un voile franc de haut en bas. */
@media (max-width: 860px) {
  .hero .wrap::before,
  .hero-page .wrap::before {
    /* Pleine largeur : un voile vertical laisserait deux aretes nettes sur
       les cotes, la colonne occupant deja tout l'ecran. */
    inset: -2rem -50vw;
    background: oklch(0.07 0.02 245 / .90);
    -webkit-mask: linear-gradient(to bottom, transparent, #000 7%, #000 93%, transparent);
    mask: linear-gradient(to bottom, transparent, #000 7%, #000 93%, transparent);
  }
}

:root[data-theme="light"] .hero .wrap::before,
:root[data-theme="light"] .hero-page .wrap::before {
  background: oklch(1 0 0 / .88);
}

/* Les halos sont du décor : sur un téléphone ils passent derrière le texte
   et n'ont plus la place de respirer. On les retient nettement. */
@media (max-width: 860px) {
  .halos::before { opacity: .38; }
  .halos::after  { opacity: .42; }
}

/* Le surtitre hérite de --text-dim, un gris qui tombe à 3,3:1 sur le fond
   des bandes d'en-tête — sous le seuil AA de 4,5:1 pour du petit texte en
   capitales. Le hero d'accueil avait déjà sa propre règle ; les pages
   intérieures ne l'avaient pas. */
.hero-page .eyebrow { color: var(--text-mid); }

/* ---------- Carte de formation ----------
   La colonne du tarif etait posee en style en ligne avec un minmax(240px) :
   a 320 px, 240 px de colonne plus les marges depassaient de 10 px et la
   page defilait lateralement. En CSS, elle peut passer sous le programme
   quand il n'y a plus la place. */
.formation-corps {
  grid-template-columns: minmax(0, 1.6fr) minmax(240px, .9fr);
  gap: clamp(1.5rem, 3vw, 2.5rem);
  align-items: start;
}
@media (max-width: 900px) {
  .formation-corps { grid-template-columns: minmax(0, 1fr); }
  /* Empilee, la colonne du tarif n'a plus de raison de coller au menu. */
  .formation-corps > aside { position: static; }
}
