home.js
// Scroll Reveal — IntersectionObserver adds .visible to .reveal elements
(function () {
var reveals = document.querySelectorAll('.reveal');
if (!reveals.length) return;
var observer = new IntersectionObserver(function (entries) {
entries.forEach(function (entry) {
if (!entry.isIntersecting) return;
var el = entry.target;
var delay = parseInt(el.getAttribute('data-delay') || '0', 10);
setTimeout(function () {
el.classList.add('visible');
}, delay);
observer.unobserve(el);
});
}, { threshold: 0.15 });
reveals.forEach(function (el) {
observer.observe(el);
});
})();
// Terminal Typing — sequentially reveal lines when terminal enters viewport
(function () {
var terminal = document.querySelector('.terminal-body');
if (!terminal) return;
var lines = terminal.querySelectorAll('.terminal-line');
var cursor = terminal.querySelector('.terminal-cursor');
var triggered = false;
var observer = new IntersectionObserver(function (entries) {
entries.forEach(function (entry) {
if (!entry.isIntersecting || triggered) return;
triggered = true;
observer.unobserve(entry.target);
lines.forEach(function (line, i) {
setTimeout(function () {
line.classList.add('visible');
// Move cursor after last visible line
if (cursor && i === lines.length - 1) {
setTimeout(function () {
cursor.style.display = 'none';
}, 1500);
}
}, i * 600);
});
});
}, { threshold: 0.3 });
observer.observe(terminal);
})();
// Particle Emitter — ember-like pixels off accent borders
(function () {
var websiteNode = document.getElementById('website-node');
var salesforceNode = document.getElementById('salesforce-node');
var violetColors = ['#8b5cf6', '#a78bfa', '#c4b5fd', '#22d3ee', '#7c3aed', '#818cf8'];
var emeraldColors = ['#10b981', '#34d399', '#6ee7b7', '#22d3ee', '#059669', '#a7f3d0'];
function spawnParticle(emitter, colors, side) {
var isMobile = window.innerWidth < 768;
var el = document.createElement('div');
el.className = 'accent-particle accent-particle-' + side;
if (isMobile) el.classList.add('accent-particle-v');
// Pick random color
var color = colors[Math.floor(Math.random() * colors.length)];
el.style.background = color;
var glowSize = side === 'left' ? '8px' : '6px';
el.style.boxShadow = '0 0 ' + glowSize + ' ' + color;
var dist, px, py;
if (isMobile) {
// Mobile: position along horizontal edge, vertical drift
el.style.left = (5 + Math.random() * 90) + '%';
dist = 14 + Math.random() * 34;
// Salesforce (left): converge from above into top border
// Website (right): drift downward from bottom border
py = (side === 'left' ? -dist : dist) + 'px';
px = (Math.random() - 0.5) * 40 + 'px';
} else {
// Desktop: position along vertical edge, horizontal drift
el.style.top = (5 + Math.random() * 90) + '%';
dist = side === 'left' ? (14 + Math.random() * 34) : (14 + Math.random() * 32);
px = (side === 'left' ? -dist : dist) + 'px';
py = (Math.random() - 0.5) * 40 + 'px';
}
el.style.setProperty('--px', px);
el.style.setProperty('--py', py);
el.style.setProperty('--duration', (1.2 + Math.random() * 1.3) + 's');
emitter.appendChild(el);
// Clean up after animation
setTimeout(function () {
if (el.parentNode) el.parentNode.removeChild(el);
}, 2600);
}
if (websiteNode) {
setInterval(function () { spawnParticle(websiteNode, violetColors, 'right'); }, 70);
}
if (salesforceNode) {
setInterval(function () { spawnParticle(salesforceNode, emeraldColors, 'left'); }, 150);
}
})();