0xc485e340…8dfd·#25,809,447·view on Etherscan
const IP_STATE = window.INFLECTION_POINTS || {};
const SEED_SAFE = Number.isFinite(Number(IP_STATE.seed))
? Number(IP_STATE.seed)
: Number.isFinite(Number(IP_STATE.tokenId))
? Number(IP_STATE.tokenId)
: 1;
function makeRng(seed) {
// deterministic PRNG (mulberry32)
let t = seed >>> 0;
return function () {
t += 0x6D2B79F5;
let x = t;
x = Math.imul(x ^ (x >>> 15), x | 1);
x ^= x + Math.imul(x ^ (x >>> 7), x | 61);
return ((x ^ (x >>> 14)) >>> 0) / 4294967296;
};
}
function mix32(x) {
x >>>= 0;
x ^= x >>> 16;
x = Math.imul(x, 0x7feb352d);
x ^= x >>> 15;
x = Math.imul(x, 0x846ca68b);
x ^= x >>> 16;
return x >>> 0;
}
function randRange(rng, a, b) {
return a + (b - a) * rng();
}
(() => {
const svgArt = document.querySelector("#art");
if (!svgArt) return;
// Keep blink-slow safe without relying on contract-injected CSS variables.
document.documentElement.style.setProperty("--ric-blink-slow", "0.8s");
const rng = makeRng(SEED_SAFE);
const toMaskedGroup = (el) => el?.closest?.('g[data-ric-layer]');
const toAnimGroup = (el) => el?.closest?.('g.g-to');
const normalizeBlink = (value) => String(value || "").trim().toLowerCase();
// foreignObjects live under: outer g[data-ric-layer] > inner g.g-to > foreignObject
const allFOs = Array.from(svgArt.querySelectorAll(":scope > g[data-ric-layer] > g.g-to > foreignObject"));
const allAnimGroups = Array.from(new Set(allFOs.map(toAnimGroup).filter(Boolean)));
const allOuterGroups = Array.from(
document.querySelectorAll("#art > g[data-ric-layer]")
);
const fastTargets = allOuterGroups.filter((g) =>
normalizeBlink(g.getAttribute("data-blink")) === "fast"
);
const slowTargets = allOuterGroups.filter((g) =>
normalizeBlink(g.getAttribute("data-blink")) === "slow"
);
// Optional future role; old v3 kinds are only a fallback and do not affect current Inflection layers.
const outlineGroups = Array.from(
new Set(
allFOs
.filter((fo) => {
const outer = toMaskedGroup(fo);
const role = normalizeBlink(outer?.getAttribute("data-phase-role"));
if (role === "outline") return true;
return !!fo.querySelector(
'[data-kind="outline"], [data-kind="outline2"], [data-kind="mouth"], [data-kind="eyes"]'
);
})
.map(toAnimGroup)
.filter(Boolean)
)
);
const phase3Targets = allAnimGroups.filter((g) => !outlineGroups.includes(g));
const originalOrder = allOuterGroups.slice();
const originalIndex = new Map(originalOrder.map((g, i) => [g, i]));
let phase = 3;
let transitioning = false;
function clearBlinks() {
fastTargets.forEach((el) => {
el.classList.remove("blink-fast");
el.style.animationDelay = "";
});
slowTargets.forEach((el) => {
el.classList.remove("blink-slow");
el.style.animationDelay = "";
});
}
function forceRestartAnimation(el) {
el.classList.remove("phase3-unfold", "phase3-return", "phase3-outline", "phase3-outline-return");
el.style.animation = "none";
void el.offsetWidth; // force reflow
el.style.animation = "";
}
function clearPhase3Vars(g) {
g.style.transform = "";
g.style.animationDelay = "";
g.style.removeProperty("--toCx");
g.style.removeProperty("--toCy");
g.style.removeProperty("--outDx");
g.style.removeProperty("--outDy");
g.style.removeProperty("--rot");
g.style.removeProperty("--dur");
g.style.removeProperty("--delay");
g.style.removeProperty("--s");
g.style.removeProperty("--retDur");
}
function clearOutlineVars(g) {
g.style.transform = "";
g.style.animationDelay = "";
g.style.removeProperty("--outlineDur");
g.style.removeProperty("--outlineDelay");
g.style.removeProperty("--outlineRetDur");
g.style.removeProperty("--rotSign");
}
function clearPhase3All() {
phase3Targets.forEach((g) => {
g.classList.remove("phase3-unfold", "phase3-return");
clearPhase3Vars(g);
});
outlineGroups.forEach((g) => {
g.classList.remove("phase3-outline", "phase3-outline-return");
clearOutlineVars(g);
});
}
function bringOutlinesToFront() {
const outlineOuterGroups = Array.from(
new Set(outlineGroups.map(toMaskedGroup).filter(Boolean))
);
outlineOuterGroups.forEach((g) => svgArt.appendChild(g));
}
async function restoreOriginalOrder(stepMs = 18) {
const current = Array.from(svgArt.querySelectorAll(':scope > g[data-ric-layer]'))
.sort((a, b) => (originalIndex.get(a) ?? 1e9) - (originalIndex.get(b) ?? 1e9));
for (const g of current) {
svgArt.appendChild(g);
if (stepMs > 0) await new Promise((r) => setTimeout(r, stepMs));
}
}
function setupPhase3ForFO(group, baseSeed, index, centerX = 400, centerY = 400) {
const childFO = group?.querySelector?.("foreignObject") || group;
const localSeed = mix32((baseSeed >>> 0) ^ mix32(index + 1));
const r = makeRng(localSeed);
let bx, by, bw, bh;
try {
const bb = childFO.getBBox();
bx = bb.x;
by = bb.y;
bw = bb.width;
bh = bb.height;
} catch {
bx = parseFloat(childFO.getAttribute("x")) || 0;
by = parseFloat(childFO.getAttribute("y")) || 0;
bw = parseFloat(childFO.getAttribute("width")) || 0;
bh = parseFloat(childFO.getAttribute("height")) || 0;
}
const cx = bx + bw / 2;
const cy = by + bh / 2;
const toCx = centerX - cx;
const toCy = centerY - cy;
const ang = randRange(r, 0, Math.PI * 2);
const dist = randRange(r, 120, 340);
const outDx = Math.cos(ang) * dist;
const outDy = Math.sin(ang) * dist;
const dir = r() < 0.5 ? -1 : 1;
const rotDeg = dir * randRange(r, 120, 520);
const dur = randRange(r, 1.2, 2.4);
const delay = randRange(r, 0.0, 0.45);
group.style.setProperty("--toCx", `${toCx.toFixed(2)}px`);
group.style.setProperty("--toCy", `${toCy.toFixed(2)}px`);
group.style.setProperty("--outDx", `${outDx.toFixed(2)}px`);
group.style.setProperty("--outDy", `${outDy.toFixed(2)}px`);
group.style.setProperty("--rot", `${rotDeg.toFixed(1)}deg`);
group.style.setProperty("--dur", `${dur.toFixed(2)}s`);
group.style.setProperty("--delay", `${delay.toFixed(2)}s`);
group.classList.add("phase3-unfold");
}
function startPhase3() {
clearBlinks();
svgArt && svgArt.classList.add("phase3-active");
phase3Targets.forEach(forceRestartAnimation);
outlineGroups.forEach(forceRestartAnimation);
bringOutlinesToFront();
outlineGroups.forEach((g) => {
const outer = toMaskedGroup(g);
const role = normalizeBlink(outer?.getAttribute("data-phase-role"));
const isReverse = role === "outline-reverse" || !!g.querySelector('[data-kind="outline2"]');
g.style.setProperty("--rotSign", isReverse ? "-1" : "6");
g.style.setProperty("--outlineDur", `${randRange(rng, 0.7, 1.05).toFixed(2)}s`);
g.style.setProperty("--outlineDelay", `${randRange(rng, 0.0, 0.15).toFixed(2)}s`);
g.classList.add("phase3-outline");
});
phase3Targets.forEach((g, i) => setupPhase3ForFO(g, SEED_SAFE, i, 400, 400));
}
function startReturnToBase(thenPhase1 = true, after = null) {
clearBlinks();
svgArt && svgArt.classList.remove("phase3-active");
outlineGroups.forEach((g) => {
forceRestartAnimation(g);
g.style.setProperty("--outlineRetDur", `${randRange(rng, 0.65, 0.9).toFixed(2)}s`);
g.classList.add("phase3-outline-return");
});
phase3Targets.forEach((g) => {
forceRestartAnimation(g);
g.style.setProperty("--retDur", `${randRange(rng, 0.85, 1.15).toFixed(2)}s`);
g.classList.add("phase3-return");
});
const RETURN_MS = 1250;
restoreOriginalOrder(20);
window.setTimeout(() => {
clearPhase3All();
if (typeof after === "function") {
after();
return;
}
if (thenPhase1) {
fastTargets.forEach((el, i) => {
el.style.animationDelay = `${i * 0.1}s`;
el.classList.add("blink-fast");
});
}
}, RETURN_MS);
}
function applyPhase(p) {
clearBlinks();
clearPhase3All();
if (p === 0) return;
requestAnimationFrame(() => {
if (p === 1) {
fastTargets.forEach((el, i) => {
el.style.animationDelay = `${i * 0.1}s`;
el.classList.add("blink-fast");
});
return;
}
if (p === 2) {
fastTargets.forEach((el, i) => {
el.style.animationDelay = `${i * 0.1}s`;
el.classList.add("blink-fast");
});
slowTargets.forEach((el, i) => {
el.style.animationDelay = `${i * 0.05}s`;
el.classList.add("blink-slow");
});
return;
}
if (p === 3) {
fastTargets.forEach((el, i) => {
el.style.animationDelay = `${i * 0.1}s`;
el.classList.add("blink-fast");
});
startPhase3();
}
});
}
function gotoPhase(next) {
next = Math.max(0, Math.min(3, Number(next) || 0));
if (transitioning) return;
const prev = phase;
if (next === prev) return;
if (prev === 3 && next !== 3) {
phase = next;
setActiveButton();
transitioning = true;
startReturnToBase(false, () => {
transitioning = false;
applyPhase(next);
setActiveButton();
});
return;
}
phase = next;
applyPhase(next);
setActiveButton();
}
function setActiveButton() {
const panel = document.getElementById("phasePanel");
if (!panel) return;
panel.querySelectorAll("button[data-phase]").forEach((btn) => {
const p = parseInt(btn.getAttribute("data-phase"), 10);
btn.classList.toggle("active", p === phase);
});
}
applyPhase(phase);
setActiveButton();
window.gotoPhase = gotoPhase;
function handleArtworkTap(ev) {
if (ev.target && ev.target.closest && ev.target.closest("#phasePanel")) return;
ev.preventDefault?.();
gotoPhase((phase + 1) % 4);
}
if (window.PointerEvent) {
document.addEventListener("pointerup", handleArtworkTap, { passive: false });
} else {
document.addEventListener("touchend", handleArtworkTap, { passive: false });
}
(function initPhasePanel() {
const panel = document.getElementById("phasePanel");
if (!panel) return;
const toggle = document.getElementById("phasePanelToggle");
const collapse = (collapsed) => {
panel.classList.toggle("collapsed", collapsed);
if (toggle) toggle.textContent = collapsed ? "▶" : "◀";
};
collapse(false);
let hideTimer = window.setTimeout(() => collapse(true), 10000);
const resetAutoHide = () => {
window.clearTimeout(hideTimer);
hideTimer = window.setTimeout(() => collapse(true), 10000);
};
toggle && toggle.addEventListener("click", (e) => {
e.stopPropagation();
const isCollapsed = panel.classList.contains("collapsed");
collapse(!isCollapsed);
resetAutoHide();
});
panel.querySelectorAll("button[data-phase]").forEach((btn) => {
btn.addEventListener("click", (e) => {
e.stopPropagation();
const p = parseInt(btn.getAttribute("data-phase"), 10);
gotoPhase(p);
resetAutoHide();
});
});
setActiveButton();
})();
window.addEventListener("DOMContentLoaded", () => {
document.documentElement.classList.remove("ready");
const revealTargets = Array.from(
document.querySelectorAll("#art foreignObject")
);
document.documentElement.classList.add("ready");
revealTargets.forEach((fo, i) => {
fo.classList.add("reveal");
fo.style.animationDelay = `${i * 0.1}s`;
fo.addEventListener(
"animationend",
() => {
fo.classList.remove("reveal");
fo.style.animation = "";
fo.style.animationDelay = "";
},
{ once: true }
);
});
});
})();
</script>
</body>
</html>