// ============================================================================ // zs-experience-n1-v1-20260902.js · L'EXPÉRIENCE N° 1 · la chorégraphie // ---------------------------------------------------------------------------- // ▸ CE QU'IL FAIT · il pilote la scène de /einstein/ au scroll, en quatre // temps · la date entre lettre à lettre · les axes s'allument famille par // famille (l'or, le rose, le cyan) et les arcs de résonance se tracent à // leur longueur réelle · l'indice de convergence monte à 71 · le verdict // tombe. Tout est réversible · remonter la page rembobine la démonstration. // ▸ NOT FICTION · il n'apporte AUCUN chiffre · la carte est celle du livre // servi, les 71, 3 752 et 185 sont déjà dans le HTML de la page. La // chorégraphie rejoint les chiffres, elle n'en fabrique pas. // ⛔ LES GARDES · prefers-reduced-motion coupe tout (la scène devient une // planche immobile, la feuille s'en charge) · le HTML servi ne cache rien // (c'est ce script qui arme, avant la première peinture) · un onglet en // arrière plan ne consomme rien (scroll + requestAnimationFrame seuls). // ✦ L'ÉGG DES 147 ANS · l'orbe centrale de la carte, survolée ou touchée, // dit la seule chose que la page ne dit nulle part · 14/03/1879 → 12/05/2026, // cent quarante sept ans séparent la naissance d'Einstein de celle de // l'Oracle. Le chiffre est déjà un nœud de la carte. // ============================================================================ (function () { 'use strict'; var doc = document, racine = doc.documentElement; var exp = doc.getElementById('zsExp'); if (!exp || exp.__zsx) return; exp.__zsx = 1; // ── replacer la scène juste sous l'en tête de la page ───────────────────── var tete = doc.querySelector('#zsEin .ein-tete'); if (tete && tete.parentNode) tete.parentNode.insertBefore(exp, tete.nextSibling); var CALME = false; try { CALME = matchMedia('(prefers-reduced-motion: reduce)').matches; } catch (e) {} // ── l'égg des 147 ans · il vit aussi sur la planche immobile ────────────── var orbe = exp.querySelector('svg circle[fill="url(#zsx-orb)"]') || exp.querySelector('svg circle[r="34"]'); var egg = exp.querySelector('.zsx-egg'); if (orbe && egg) { orbe.classList.add('zsx-orbe-centre'); var dire = function () { egg.classList.add('zsx-dit'); }; orbe.addEventListener('mouseenter', dire); orbe.addEventListener('click', dire); } if (CALME) return; // la feuille montre tout, posé · rien d'autre à faire // ── armer la chorégraphie · avant la première peinture (script defer) ───── // ⛔ sticky exige un overflow-x en clip (le noyau global pose hidden) · un // navigateur sans clip garde la planche immobile, et elle se suffit. var porteClip = false; try { porteClip = CSS.supports('overflow-x', 'clip'); } catch (e) {} if (!porteClip) return; racine.classList.add('zsexp-vole'); // la date, lettre à lettre var date = exp.querySelector('.exp-date'); var lettres = []; if (date && !date.querySelector('.exp-l')) { var morceaux = []; (function decoupe(n) { for (var i = 0; i < n.childNodes.length; i++) { var c = n.childNodes[i]; if (c.nodeType === 3) morceaux.push(c); else if (c.nodeType === 1) decoupe(c); } })(date); for (var m = 0; m < morceaux.length; m++) { var tx = morceaux[m].nodeValue, frag = doc.createDocumentFragment(); for (var k = 0; k < tx.length; k++) { var s = doc.createElement('span'); s.className = 'exp-l'; s.textContent = tx.charAt(k); frag.appendChild(s); } morceaux[m].parentNode.replaceChild(frag, morceaux[m]); } var cur = doc.createElement('span'); cur.className = 'exp-cur'; date.appendChild(cur); lettres = [].slice.call(date.querySelectorAll('.exp-l')); } // les nœuds, ordonnés par famille · l'or compte, le rose lit, le cyan tire var ordreFam = ['or', 'rose', 'cyan', 'autre']; var noeuds = []; for (var f = 0; f < ordreFam.length; f++) noeuds = noeuds.concat([].slice.call(exp.querySelectorAll('.cm-noeud[data-fam="' + ordreFam[f] + '"]'))); var fams = { or: exp.querySelector('.exp-fam .f-or'), rose: exp.querySelector('.exp-fam .f-rose'), cyan: exp.querySelector('.exp-fam .f-cyan') }; var bornesFam = { or: 0, rose: 0, cyan: 0 }; (function () { var nOr = exp.querySelectorAll('.cm-noeud[data-fam="or"]').length; var nRose = exp.querySelectorAll('.cm-noeud[data-fam="rose"]').length; bornesFam.or = 0; bornesFam.rose = nOr; bornesFam.cyan = nOr + nRose; })(); // les arcs · leur longueur réelle, mesurée var arcs = [].slice.call(exp.querySelectorAll('svg > path')); for (var a = 0; a < arcs.length; a++) { var L = 900; try { L = Math.ceil(arcs[a].getTotalLength()); } catch (e) {} arcs[a].style.setProperty('--L', L); } // l'indice · son vrai chiffre est déjà écrit dans le HTML servi var chiffre = exp.querySelector('.exp-chiffre'); var vrai = chiffre ? parseInt(chiffre.textContent, 10) || 71 : 71; var jauge = exp.querySelector('.exp-jauge > b'); var etapes = { e1: exp.querySelector('.exp-e1'), e2: exp.querySelector('.exp-e2'), e3: exp.querySelector('.exp-e3'), e4: exp.querySelector('.exp-e4') }; var piste = exp.querySelector('.exp-piste'); function borne(v, a, b) { return Math.max(0, Math.min(1, (v - a) / (b - a))); } var derniereScene = ''; function scene(p) { // temps 1 · la date entre · 0 → 0,20 var t1 = borne(p, 0.02, 0.17); var n = Math.round(lettres.length * t1); for (var i = 0; i < lettres.length; i++) lettres[i].classList.toggle('exp-tape', i < n); // les étapes en scène var s = p < 0.22 ? 'e1' : (p < 0.62 ? 'e2' : (p < 0.86 ? 'e3' : 'e4')); if (s !== derniereScene) { derniereScene = s; for (var e in etapes) if (etapes[e]) { etapes[e].classList.toggle('exp-on', e === s); etapes[e].classList.remove('exp-fond'); } // la carte reste en fond derrière l'indice et le verdict if ((s === 'e3' || s === 'e4') && etapes.e2) etapes.e2.classList.add('exp-fond'); } // temps 2 · les axes s'allument famille par famille · 0,24 → 0,58 var t2 = borne(p, 0.24, 0.58); var vus = Math.round(noeuds.length * t2); for (var j = 0; j < noeuds.length; j++) noeuds[j].classList.toggle('zsx-on', j < vus); if (fams.or) fams.or.classList.toggle('exp-vive', vus > bornesFam.or); if (fams.rose) fams.rose.classList.toggle('exp-vive', vus > bornesFam.rose); if (fams.cyan) fams.cyan.classList.toggle('exp-vive', vus > bornesFam.cyan); // les arcs se tracent · 0,40 → 0,62 var t2b = borne(p, 0.40, 0.62); var traces = Math.round(arcs.length * t2b); for (var q = 0; q < arcs.length; q++) arcs[q].classList.toggle('zsx-trace', q < traces); // temps 3 · l'indice monte à son vrai chiffre · 0,64 → 0,84 var t3 = borne(p, 0.64, 0.84); if (chiffre) chiffre.textContent = String(Math.round(vrai * t3)); if (jauge) jauge.style.setProperty('--j', (t3 * vrai / 100).toFixed(3)); if (p >= 0.84 && chiffre) chiffre.textContent = String(vrai); } var vole = false; function cadre() { vole = false; var r = piste.getBoundingClientRect(); var total = r.height - innerHeight; if (total <= 0) return; scene(Math.max(0, Math.min(1, -r.top / total))); } addEventListener('scroll', function () { if (!vole) { vole = true; requestAnimationFrame(cadre); } }, { passive: true }); addEventListener('resize', function () { if (!vole) { vole = true; requestAnimationFrame(cadre); } }, { passive: true }); cadre(); })();