chris 066364d2b7 Fix easter egg trigger: 7 quick taps on non-interactive area
Long press on the logo link was unreliable. Switch to detecting 7 rapid
taps on any non-link/button area within 2 seconds — works on mobile and
desktop without conflicting with navigation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 21:15:26 -04:00

122 lines
4.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

(function () {
var COLORS = ['#11b3be','#ff6b9d','#ffd93d','#6bcb77','#4d96ff','#ff6b35','#c77dff','#ff9f43','#54a0ff','#ff6348'];
var SVG_NS = 'http://www.w3.org/2000/svg';
function makeBalloon(color, size) {
var svg = document.createElementNS(SVG_NS, 'svg');
svg.setAttribute('viewBox', '0 0 50 82');
svg.setAttribute('width', size);
svg.setAttribute('height', Math.round(size * 1.64));
var body = document.createElementNS(SVG_NS, 'ellipse');
body.setAttribute('cx', '25'); body.setAttribute('cy', '26');
body.setAttribute('rx', '21'); body.setAttribute('ry', '25');
body.setAttribute('fill', color); body.setAttribute('opacity', '0.88');
// shine
var shine = document.createElementNS(SVG_NS, 'ellipse');
shine.setAttribute('cx', '18'); shine.setAttribute('cy', '16');
shine.setAttribute('rx', '7'); shine.setAttribute('ry', '5');
shine.setAttribute('fill', 'white'); shine.setAttribute('opacity', '0.25');
shine.setAttribute('transform', 'rotate(-20 18 16)');
var knot = document.createElementNS(SVG_NS, 'path');
knot.setAttribute('d', 'M25 51 Q27.5 55.5 25 58.5 Q22.5 55.5 25 51Z');
knot.setAttribute('fill', color); knot.setAttribute('opacity', '0.88');
var str = document.createElementNS(SVG_NS, 'path');
str.setAttribute('d', 'M25 58.5 Q20 68 25 80');
str.setAttribute('stroke', '#999'); str.setAttribute('stroke-width', '1.2');
str.setAttribute('fill', 'none'); str.setAttribute('opacity', '0.7');
svg.appendChild(body);
svg.appendChild(shine);
svg.appendChild(knot);
svg.appendChild(str);
return svg;
}
var active = false;
var tapCount = 0;
var tapTimer = null;
// 7 quick taps on any non-interactive part of the page triggers it
document.addEventListener('click', function (e) {
if (active) return;
if (e.target.closest('a, button, input, select, textarea, label')) return;
tapCount++;
clearTimeout(tapTimer);
tapTimer = setTimeout(function () { tapCount = 0; }, 2000);
if (tapCount >= 7) {
tapCount = 0;
launch();
}
});
function launch() {
active = true;
// Inject keyframes once
if (!document.getElementById('bpb-balloon-style')) {
var style = document.createElement('style');
style.id = 'bpb-balloon-style';
style.textContent = [
'@keyframes bpb-float {',
' 0% { transform: translateX(var(--bx)) translateY(0) rotate(var(--br)); opacity: 0; }',
' 8% { opacity: 1; }',
' 85% { opacity: 1; }',
' 100% { transform: translateX(calc(var(--bx) * -1)) translateY(var(--by)) rotate(calc(var(--br) * -0.5)); opacity: 0; }',
'}',
].join('\n');
document.head.appendChild(style);
}
var container = document.createElement('div');
Object.assign(container.style, {
position: 'fixed',
inset: '0',
pointerEvents: 'none',
zIndex: '9', // below Bulma navbar (z-index 30)
overflow: 'hidden',
});
document.body.insertBefore(container, document.body.firstChild);
var count = 22;
for (var i = 0; i < count; i++) {
(function (idx) {
setTimeout(function () {
var color = COLORS[Math.floor(Math.random() * COLORS.length)];
var size = 38 + Math.random() * 32; // 3870 px
var leftPct = 3 + Math.random() * 94; // 397 %
var sway = (Math.random() * 40 - 20) + 'px'; // ±20 px horizontal drift
var tilt = (Math.random() * 24 - 12) + 'deg';
var dur = 4500 + Math.random() * 3500; // 4.58 s
var riseAmt = -(window.innerHeight + size * 2); // rise full viewport height
var wrap = document.createElement('div');
Object.assign(wrap.style, {
position: 'absolute',
bottom: (-size * 1.64 - 10) + 'px',
left: leftPct + '%',
'--bx': sway,
'--by': riseAmt + 'px',
'--br': tilt,
animation: 'bpb-float ' + dur + 'ms cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards',
animationDelay: (idx * 120 + Math.random() * 200) + 'ms',
});
wrap.appendChild(makeBalloon(color, size));
container.appendChild(wrap);
}, 0);
})(i);
}
setTimeout(function () {
container.remove();
active = false;
}, 12000);
}
})();