git / moonbite-network / templates/logo_sting.html · raw
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>MoonBite โ Logo Sting</title><meta name="description" content="The MoonBite logo animation: a bite carves the full moon into a crescent, then the MOONBITE wordmark locks up."><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@500;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet"><style> :root { --accent: #5FE9D0; --ice: #F2F6FF; --void: #04050a; } * { box-sizing: border-box; } html, body { margin: 0; height: 100%; background: var(--void); } body { display: grid; place-items: center; min-height: 100vh; min-height: 100svh; overflow: hidden; font-family: "Space Grotesk", system-ui, sans-serif; } .sting-stage { position: relative; width: 100%; height: 100vh; height: 100svh; background: var(--void); overflow: hidden; } .sting-stage svg { display: block; width: 100%; height: 100%; } /* Overlay controls */ .sting-skip { position: absolute; top: clamp(1rem, 3vw, 2rem); right: clamp(1rem, 3vw, 2rem); z-index: 4; font-family: "JetBrains Mono", monospace; font-size: 0.72rem; letter-spacing: 0.18em; text-transform: uppercase; color: rgba(242,246,255,0.5); text-decoration: none; padding: 0.5rem 0.85rem; border: 1px solid rgba(242,246,255,0.16); border-radius: 999px; transition: color .25s ease, border-color .25s ease; } .sting-skip:hover { color: var(--ice); border-color: rgba(95,233,208,0.6); } .sting-outro { position: absolute; left: 50%; bottom: clamp(2.2rem, 8vh, 6rem); transform: translateX(-50%) translateY(12px); z-index: 4; display: flex; gap: 0.85rem; align-items: center; opacity: 0; pointer-events: none; transition: opacity .7s ease, transform .7s cubic-bezier(.2,.8,.2,1); } .sting-outro.is-in { opacity: 1; transform: translateX(-50%) translateY(0); pointer-events: auto; } .sting-btn { font-family: "Space Grotesk", sans-serif; font-weight: 700; font-size: 1rem; text-decoration: none; padding: 0.85rem 1.6rem; border-radius: 999px; border: 1px solid transparent; transition: transform .25s ease, box-shadow .25s ease, border-color .25s ease; } .sting-btn.primary { color: #04121; color: #041210; background: var(--accent); box-shadow: 0 12px 40px rgba(95,233,208,0.35); } .sting-btn.primary:hover { transform: translateY(-2px); box-shadow: 0 16px 52px rgba(95,233,208,0.5); } .sting-btn.ghost { color: var(--ice); background: rgba(255,255,255,0.04); border-color: rgba(242,246,255,0.18); } .sting-btn.ghost:hover { transform: translateY(-2px); border-color: rgba(95,233,208,0.6); } @media (prefers-reduced-motion: reduce) { .sting-outro { transition: none; } }</style></head><body><div class="sting-stage" id="stage"> <a class="sting-skip" href="/" id="skip">Skip intro โ</a> <svg id="sting" viewBox="0 0 1920 1080" preserveAspectRatio="xMidYMid meet" aria-label="MoonBite logo animation"> <defs> <radialGradient id="bloom"> <stop offset="0" stop-color="#F2F6FF" stop-opacity="0.10"/> <stop offset="0.45" stop-color="#5FE9D0" stop-opacity="0.07"/> <stop offset="1" stop-color="#5FE9D0" stop-opacity="0"/> </radialGradient> <radialGradient id="haze" cx="0.5" cy="0.5"> <stop offset="0" stop-color="#101a3a" stop-opacity="0.65"/> <stop offset="1" stop-color="#04050a" stop-opacity="0"/> </radialGradient> <pattern id="scan" width="6" height="6" patternUnits="userSpaceOnUse"> <rect x="0" y="0" width="6" height="1" fill="#F2F6FF" opacity="0.05"/> </pattern> <mask id="markMask" maskUnits="userSpaceOnUse" x="-266.5" y="-266.5" width="533" height="533"> <rect id="sweepRect" x="-266.5" y="-209" width="533" height="418" fill="#fff"/> <circle id="biteCircle" cx="102.5" cy="-26.65" r="172.2" fill="#000"/> </mask> </defs> <!-- VOID --> <g id="void"> <rect x="0" y="0" width="1920" height="1080" fill="#04050a"/> <rect x="0" y="0" width="1920" height="1080" fill="url(#haze)"/> <rect x="0" y="0" width="1920" height="1080" fill="url(#scan)" opacity="0.5"/> <g id="motes"></g> <g id="baseline"></g> </g> <!-- HUD --> <g id="hud" font-family="JetBrains Mono, monospace" font-size="24" letter-spacing="4"> <text x="72" y="92" fill="#F2F6FF" opacity="0.5">MB//ORBIT-01</text> <text id="hudSync" x="72" y="128" fill="#5FE9D0" opacity="0.75">SYNC 0.500</text> <text x="1848" y="92" text-anchor="end" fill="#F2F6FF" opacity="0.5">PRE-MAINNET</text> <text x="1848" y="128" text-anchor="end" fill="#F2F6FF" opacity="0.28">TESTNET LIVE</text> <g stroke="#F2F6FF" stroke-width="2" opacity="0.22" fill="none"> <path d="M40 40 h44 M40 40 v44"/><path d="M1880 40 h-44 M1880 40 v44"/> <path d="M40 1040 h44 M40 1040 v-44"/><path d="M1880 1040 h-44 M1880 1040 v-44"/> </g> </g> <!-- ZOOM --> <g id="zoomG"> <g id="groupG"> <g id="moonG"> <!-- MARK --> <g id="markG"> <circle id="bloom" cx="0" cy="0" r="492" fill="url(#bloom)" opacity="1"/> <!-- RING --> <g id="ringG" opacity="0.95"> <circle cx="0" cy="0" r="307.5" fill="none" stroke="#F2F6FF" stroke-width="1.5" opacity="0.2"/> <circle cx="0" cy="0" r="352.6" fill="none" stroke="#5FE9D0" stroke-width="1.5" opacity="0.3" stroke-dasharray="2 26"/> <g id="ticks"></g> </g> <!-- DISCS (chroma split) --> <g id="discA" mask="url(#markMask)" opacity="0.55" transform="translate(-12.6 0)"><circle cx="0" cy="0" r="205" fill="#5FE9D0"/></g> <g id="discC" mask="url(#markMask)" opacity="0.55" transform="translate(12.6 0)"><circle cx="0" cy="0" r="205" fill="#5B7BFF"/></g> <g id="discI" mask="url(#markMask)" opacity="1"><circle cx="0" cy="0" r="205" fill="#F2F6FF"/></g> <!-- CRATERS --> <g mask="url(#markMask)" opacity="0.5"> <circle cx="0" cy="0" r="139.4" fill="none" stroke="#04050a" stroke-width="1.5" opacity="0.35"/> <circle cx="-77.9" cy="-61.5" r="11.3" fill="#04050a" opacity="0.3"/> <circle cx="-32.8" cy="82" r="7.2" fill="#04050a" opacity="0.25"/> </g> <!-- SWEEP EDGE --> <g id="sweepEdge" style="display:none"> <line id="sweepEdgeA" x1="-256.25" y1="0" x2="256.25" y2="0" stroke="#5FE9D0" stroke-width="4"/> <line id="sweepEdgeB" x1="-256.25" y1="0" x2="256.25" y2="0" stroke="#F2F6FF" stroke-width="1.5" opacity="0.9"/> </g> <!-- FLASH --> <circle id="flashRing" cx="0" cy="0" r="205" fill="none" stroke="#5FE9D0" stroke-width="1" opacity="0" style="display:none"/> </g> <!-- TARGET + SHARDS (scaled by moonScale) --> <g id="moonScaleG"> <g id="target" style="display:none" stroke="#5FE9D0" stroke-width="3" fill="none"></g> <g id="shards" style="display:none"></g> </g> </g> <!-- WORDMARK --> <clipPath id="wordClip"><rect id="wordClipRect" x="-262" y="-150" width="0" height="300"/></clipPath> <g id="wordG" clip-path="url(#wordClip)" opacity="0"> <text x="-250" y="4" fill="#F2F6FF" font-family="Space Grotesk, sans-serif" font-weight="700" font-size="132" letter-spacing="2" dominant-baseline="middle">MOON<tspan fill="#5FE9D0">BITE</tspan></text> </g> <line id="wordWipe" x1="0" y1="-150" x2="0" y2="150" stroke="#5FE9D0" stroke-width="3" opacity="0.9" style="display:none"/> <!-- TAGLINE --> <g id="tagG" opacity="0"> <rect id="tagRect" x="-248" y="86" width="0" height="1.5" fill="#F2F6FF" opacity="0.28"/> <text x="-246" y="146" fill="#F2F6FF" opacity="0.6" font-family="JetBrains Mono, monospace" font-weight="400" font-size="30" letter-spacing="8">PROOF-OF-WORK ยท NO PREMINE</text> </g> </g> </g> </svg> <div class="sting-outro" id="outro"> <a class="sting-btn primary" href="/">Enter MoonBite โ</a> <button class="sting-btn ghost" id="replay" type="button">Replay</button> </div></div><script nonce="{{ csp_nonce }}">(function () { "use strict"; var W = 1920, H = 1080, R = 205; var ICE = "#F2F6FF", COOL = "#5B7BFF", ACCENT = "#5FE9D0"; var svgNS = "http://www.w3.org/2000/svg"; var $ = function (id) { return document.getElementById(id); }; /* ---- easing + tweening (ports of Easing / animate / interpolate) ---- */ function clamp(v, a, b) { a = a == null ? 0 : a; b = b == null ? 1 : b; return v < a ? a : v > b ? b : v; } function easeOutCubic(x) { return 1 - Math.pow(1 - x, 3); } function easeInOutCubic(x) { return x < 0.5 ? 4 * x * x * x : 1 - Math.pow(-2 * x + 2, 3) / 2; } function linear(x) { return x; } function animate(o) { var from = o.from, to = o.to, start = o.start == null ? 0 : o.start, end = o.end == null ? 1 : o.end, ease = o.ease || linear; return function (t) { var u = clamp((t - start) / (end - start), 0, 1); return from + (to - from) * ease(u); }; } function interpolate(stops, values, ease) { ease = ease || linear; return function (t) { if (t <= stops[0]) return values[0]; if (t >= stops[stops.length - 1]) return values[values.length - 1]; for (var i = 0; i < stops.length - 1; i++) { if (t >= stops[i] && t < stops[i + 1]) { var u = (t - stops[i]) / (stops[i + 1] - stops[i]); return values[i] + (values[i + 1] - values[i]) * ease(u); } } return values[values.length - 1]; }; } /* ---- deterministic randoms (same seed formula as the design) ---- */ function rnd(s) { var x = Math.sin(s * 127.1 + 311.7) * 43758.5453; return x - Math.floor(x); } var MOTES = []; for (var i = 0; i < 46; i++) MOTES.push({ x: rnd(i + 1) * W, y: rnd(i + 21) * H, r: 0.8 + rnd(i + 41) * 1.6, ph: rnd(i + 61) * 6.283, sp: 0.7 + rnd(i + 81) * 1.6 }); var SHARDS = []; for (var j = 0; j < 8; j++) SHARDS.push({ a: -1.0 + rnd(j + 5) * 1.9, d: 250 + rnd(j + 15) * 330, r: 8 + rnd(j + 25) * 16, spin: (rnd(j + 35) - 0.5) * 760 }); /* ---- build repeated SVG bits once ---- */ function el(tag, attrs) { var e = document.createElementNS(svgNS, tag); for (var k in attrs) e.setAttribute(k, attrs[k]); return e; } (function buildMotes() { var g = $("motes"); MOTES.forEach(function (m, k) { g.appendChild(el("rect", { x: m.x, y: m.y, width: m.r * 2, height: m.r * 2, fill: k % 7 === 0 ? ACCENT : ICE, opacity: 0.2 })); }); })(); (function buildBaseline() { var g = $("baseline"); g.appendChild(el("line", { x1: 0, y1: H - 118, x2: W, y2: H - 118, stroke: ICE, "stroke-width": 1, opacity: 0.1 })); for (var k = 0; k < 33; k++) { g.appendChild(el("line", { x1: 60 + k * 56, y1: H - 118, x2: 60 + k * 56, y2: H - (k % 4 === 0 ? 100 : 108), stroke: ICE, "stroke-width": 1, opacity: 0.16 })); } })(); (function buildTicks() { var g = $("ticks"); for (var k = 0; k < 36; k++) { var a = k * 10, rad = a * Math.PI / 180, long = k % 9 === 0; g.appendChild(el("line", { x1: Math.cos(rad) * R * 1.5, y1: Math.sin(rad) * R * 1.5, x2: Math.cos(rad) * (R * 1.5 + (long ? 26 : 12)), y2: Math.sin(rad) * (R * 1.5 + (long ? 26 : 12)), stroke: long ? ACCENT : ICE, "stroke-width": long ? 3 : 1.5, opacity: long ? 0.7 : 0.28 })); } })(); var shardEls = []; (function buildShards() { var g = $("shards"); SHARDS.forEach(function (c) { var r = el("rect", { x: -c.r / 2, y: -c.r / 2, width: c.r, height: c.r, fill: "none", stroke: ICE, "stroke-width": 2, opacity: 0.8 }); shardEls.push(r); g.appendChild(r); }); // colour every 3rd shard with accent for (var s = 0; s < shardEls.length; s++) if (s % 3 === 0) shardEls[s].setAttribute("stroke", ACCENT); })(); /* ---- param presets ---- */ var BASE = { moonX: 0, moonY: 0, moonScale: 1, squash: 0, groupX: 0, zoom: 1, sweep: 1, bite: 0, chroma: 6, flash: 0, targetP: 0, crumbP: 0, ringOpacity: 0.95, ringScale: 1, hudP: 1, wordP: 0, tagP: 0, wordX: -250 }; function merge() { var o = {}, a; for (var n = 0; n < arguments.length; n++) { a = arguments[n]; for (var k in a) o[k] = a[k]; } return o; } var LOCKUP = merge(BASE, { bite: 1, moonX: -470, moonScale: 0.52, groupX: 50, wordP: 1, tagP: 1, ringScale: 1.05 }); /* ---- scenes ---- */ var SCENES = [ { name: "Boot", dur: 2.2, fn: function (t) { return merge(BASE, { sweep: animate({ from: 0, to: 1, start: 0.12, end: 0.72, ease: easeInOutCubic })(t), chroma: interpolate([0.12, 0.72, 0.92], [34, 12, 6], easeOutCubic)(t), hudP: animate({ from: 0, to: 1, start: 0.34, end: 0.86 })(t), ringOpacity: animate({ from: 0, to: 0.95, start: 0.04, end: 0.5 })(t), ringScale: animate({ from: 1.45, to: 1, start: 0.04, end: 0.62, ease: easeOutCubic })(t), moonScale: interpolate([0.12, 0.72, 1], [0.96, 1.01, 1], easeOutCubic)(t), zoom: animate({ from: 1.14, to: 1, start: 0, end: 1, ease: easeOutCubic })(t) }); } }, { name: "Bite", dur: 1.7, fn: function (t) { return merge(BASE, { targetP: t < 0.04 ? 0 : clamp((t - 0.04) / 0.42, 0, 1), bite: animate({ from: 0, to: 1, start: 0.32, end: 0.43, ease: easeOutCubic })(t), flash: (t > 0.32 && t < 0.68) ? clamp((t - 0.32) / 0.36, 0, 1) : 0, crumbP: t < 0.32 ? 0 : clamp((t - 0.32) / 0.6, 0, 1), chroma: interpolate([0.24, 0.32, 0.43, 0.6, 0.86], [6, 14, 46, 16, 6], easeOutCubic)(t), squash: interpolate([0.1, 0.26, 0.32, 0.43, 0.62, 0.8, 1], [0, 0.05, -0.02, 0.08, -0.035, 0.012, 0], easeInOutCubic)(t), moonY: interpolate([0.22, 0.32, 0.46, 0.68, 1], [0, -20, 12, -3, 0], easeInOutCubic)(t), ringScale: interpolate([0.3, 0.43, 0.75, 1], [1, 1.06, 0.99, 1], easeOutCubic)(t), zoom: interpolate([0, 0.32, 0.43, 1], [1, 1.06, 1.11, 1], easeInOutCubic)(t) }); } }, { name: "Lockup", dur: 2.3, fn: function (t) { var move = { start: 0.05, end: 0.5, ease: easeInOutCubic }; return merge(LOCKUP, { moonX: animate(merge({ from: 0, to: -470 }, move))(t), moonScale: animate(merge({ from: 1, to: 0.52 }, move))(t), groupX: animate(merge({ from: 0, to: 50 }, move))(t), ringScale: animate(merge({ from: 1, to: 1.05 }, move))(t), squash: interpolate([0.42, 0.54, 0.68, 1], [0, 0.03, -0.01, 0], easeInOutCubic)(t), wordP: animate({ from: 0, to: 1, start: 0.32, end: 0.7 })(t), tagP: animate({ from: 0, to: 1, start: 0.62, end: 0.86 })(t), zoom: animate({ from: 1, to: 1.02, start: 0, end: 1, ease: easeInOutCubic })(t) }); } } ]; var TOTAL = SCENES.reduce(function (a, s) { return a + s.dur; }, 0); /* ---- refs ---- */ var refs = { hud: $("hud"), hudSync: $("hudSync"), motes: $("motes").children, zoomG: $("zoomG"), groupG: $("groupG"), moonG: $("moonG"), markG: $("markG"), ringG: $("ringG"), bloom: $("bloom"), discA: $("discA"), discC: $("discC"), discI: $("discI"), sweepRect: $("sweepRect"), biteCircle: $("biteCircle"), sweepEdge: $("sweepEdge"), sweepEdgeA: $("sweepEdgeA"), sweepEdgeB: $("sweepEdgeB"), flashRing: $("flashRing"), moonScaleG: $("moonScaleG"), target: $("target"), shards: $("shards"), wordG: $("wordG"), wordClipRect: $("wordClipRect"), wordWipe: $("wordWipe"), tagG: $("tagG"), tagRect: $("tagRect"), outro: $("outro") }; /* ---- render one composed frame ---- */ function render(p, tSec) { var sx = p.moonScale * (1 + p.squash), sy = p.moonScale * (1 - p.squash); var top = -R - 4, span = (2 * R + 8) * easeInOutCubic(clamp(p.sweep, 0, 1)); var biteCx = R * 0.5 + (1 - p.bite) * R * 3.1; // stage transforms refs.zoomG.setAttribute("transform", "translate(" + (W / 2) + " " + (H / 2) + ") scale(" + p.zoom + ") translate(" + (-W / 2) + " " + (-H / 2) + ")"); refs.groupG.setAttribute("transform", "translate(" + (W / 2 + p.groupX) + " " + (H / 2) + ")"); refs.moonG.setAttribute("transform", "translate(" + p.moonX + " " + p.moonY + ")"); refs.markG.setAttribute("transform", "scale(" + sx + " " + sy + ")"); refs.moonScaleG.setAttribute("transform", "scale(" + p.moonScale + ")"); // hud refs.hud.setAttribute("opacity", p.hudP); refs.hudSync.textContent = "SYNC " + (0.5 + 0.5 * Math.sin(tSec * 2.1)).toFixed(3); for (var m = 0; m < refs.motes.length; m++) { refs.motes[m].setAttribute("opacity", (0.1 + 0.3 * (0.5 + 0.5 * Math.sin(tSec * MOTES[m].sp + MOTES[m].ph))) * p.hudP); } // bloom + ring refs.bloom.setAttribute("opacity", 0.4 + 0.6 * clamp(p.sweep, 0, 1)); refs.ringG.setAttribute("opacity", p.ringOpacity); refs.ringG.setAttribute("transform", "scale(" + p.ringScale + ") rotate(" + (tSec * 7) + ")"); // mask (sweep + bite) refs.sweepRect.setAttribute("y", top); refs.sweepRect.setAttribute("height", Math.max(0, span)); refs.biteCircle.setAttribute("cx", biteCx); // chroma discs refs.discA.setAttribute("transform", "translate(" + (-9 - p.chroma * 0.6) + " 0)"); refs.discC.setAttribute("transform", "translate(" + (9 + p.chroma * 0.6) + " 0)"); // sweep edge line if (p.sweep > 0.02 && p.sweep < 0.99) { refs.sweepEdge.style.display = ""; var ey = top + span; refs.sweepEdgeA.setAttribute("y1", ey); refs.sweepEdgeA.setAttribute("y2", ey); refs.sweepEdgeB.setAttribute("y1", ey); refs.sweepEdgeB.setAttribute("y2", ey); } else { refs.sweepEdge.style.display = "none"; } // flash ring if (p.flash > 0) { refs.flashRing.style.display = ""; refs.flashRing.setAttribute("r", R * (1 + 0.34 * p.flash)); refs.flashRing.setAttribute("stroke-width", 4 * (1 - p.flash) + 1); refs.flashRing.setAttribute("opacity", 0.85 * (1 - p.flash)); } else { refs.flashRing.style.display = "none"; } // target reticle if (p.targetP > 0) { var pp = p.targetP; var s = 1 + 0.5 * (1 - easeOutCubic(clamp(pp * 1.6, 0, 1))); var b = R * 0.66 * s; var op = clamp(pp * 2, 0, 1) * (pp > 0.8 ? (1 - pp) / 0.2 : 1); refs.target.style.display = ""; refs.target.setAttribute("opacity", op); refs.target.setAttribute("transform", "translate(" + (R * 0.5) + " " + (-R * 0.13) + ")"); refs.target.innerHTML = '<path d="M' + (-b) + ' ' + (-b + 34) + ' v-34 h34 M' + (b - 34) + ' ' + (-b) + ' h34 v34 M' + b + ' ' + (b - 34) + ' v34 h-34 M' + (-b + 34) + ' ' + b + ' h-34 v-34"/>' + '<line x1="' + (-b - 40) + '" y1="0" x2="' + (-b - 8) + '" y2="0" stroke-width="2" opacity="0.6"/>' + '<line x1="' + (b + 8) + '" y1="0" x2="' + (b + 40) + '" y2="0" stroke-width="2" opacity="0.6"/>'; } else { refs.target.style.display = "none"; } // shards if (p.crumbP > 0 && p.crumbP < 1) { var out = easeOutCubic(p.crumbP); var fade = p.crumbP < 0.62 ? 1 : 1 - (p.crumbP - 0.62) / 0.38; refs.shards.style.display = ""; refs.shards.setAttribute("opacity", fade); refs.shards.setAttribute("transform", "translate(" + (R * 0.55) + " " + (-R * 0.2) + ")"); for (var sh = 0; sh < SHARDS.length; sh++) { var c = SHARDS[sh], d = c.d * out; var x = Math.cos(c.a) * d * 1.2, y = Math.sin(c.a) * d - 120 * out + 240 * out * out; shardEls[sh].setAttribute("transform", "translate(" + x + " " + y + ") rotate(" + (c.spin * out) + ")"); } } else { refs.shards.style.display = "none"; } // wordmark refs.wordG.setAttribute("opacity", p.wordP > 0 ? 1 : 0); var ww = (760 + 12) * easeInOutCubic(clamp(p.wordP, 0, 1)); refs.wordClipRect.setAttribute("width", ww); if (p.wordP > 0 && p.wordP < 0.99) { refs.wordWipe.style.display = ""; var wx = p.wordX - 12 + ww; refs.wordWipe.setAttribute("x1", wx); refs.wordWipe.setAttribute("x2", wx); } else { refs.wordWipe.style.display = "none"; } // tagline refs.tagG.setAttribute("opacity", p.tagP); refs.tagRect.setAttribute("width", 392 * p.tagP); } /* ---- playback ---- */ var startTime = null, done = false; var reduce = window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches; function frameAt(elapsed) { var e = clamp(elapsed, 0, TOTAL), acc = 0, p; for (var s = 0; s < SCENES.length; s++) { if (e < acc + SCENES[s].dur || s === SCENES.length - 1) { p = SCENES[s].fn(clamp((e - acc) / SCENES[s].dur, 0, 1)); break; } acc += SCENES[s].dur; } return p; } function loop(now) { if (startTime === null) startTime = now; var elapsed = (now - startTime) / 1000; render(frameAt(elapsed), elapsed); if (elapsed >= TOTAL && !done) { done = true; refs.outro.classList.add("is-in"); } // keep the ring + hud alive after lockup; motes/sync use tSec requestAnimationFrame(loop); } function play() { startTime = null; done = false; refs.outro.classList.remove("is-in"); if (reduce) { render(frameAt(TOTAL), TOTAL); refs.outro.classList.add("is-in"); return; } requestAnimationFrame(loop); } $("replay").addEventListener("click", play); play();})();</script></body></html>