(function (){
const spark=document.querySelector('[data-spark]');
if(spark){
for (let i=0; i < 26; i++){
const b=document.createElement('i');
b.style.height=(24 + Math.random() * 74) + '%';
b.style.animationDelay=(-Math.random() * 5) + 's';
b.style.animationDuration=(4.5 + Math.random() * 3.5) + 's';
spark.appendChild(b);
}}
const io=new IntersectionObserver((entries)=> {
entries.forEach((e)=> { if(e.isIntersecting){ e.target.classList.add('is-in'); io.unobserve(e.target); }});
}, { threshold: 0.14 });
document.querySelectorAll('.vps-reveal').forEach((el, i)=> {
el.style.transitionDelay=((i % 4) * 0.06) + 's';
io.observe(el);
});
const bg=document.querySelector('.vps-telemetry-bg');
if(bg){
const bctx=bg.getContext('2d');
let bw=0, bh=0, bt=0, blast=0, bcells=[], bpts=[];
const bscale=()=> (window.innerWidth > 3000 ? 0.5:window.innerWidth > 2200 ? 0.66:Math.min(window.devicePixelRatio||1, 2));
const brgba=(a)=> `rgba(184,156,255,${a})`;
const brr=(x, y, cw, ch, r)=> { bctx.beginPath(); bctx.moveTo(x + r, y); bctx.arcTo(x + cw, y, x + cw, y + ch, r); bctx.arcTo(x + cw, y + ch, x, y + ch, r); bctx.arcTo(x, y + ch, x, y, r); bctx.arcTo(x, y, x + cw, y, r); bctx.closePath(); };
const fitBg=()=> {
bw=window.innerWidth; bh=window.innerHeight;
const dpr=bscale();
bg.width=bw * dpr; bg.height=bh * dpr;
bctx.setTransform(dpr, 0, 0, dpr, 0, 0);
const cw=30, ch=15, gap=14;
const cols=Math.max(1, Math.floor(bw / (cw + gap)));
const rows=Math.max(1, Math.floor(bh / (ch + gap)));
bcells=[];
for (let r=0; r < rows; r++) for (let c=0; c < cols; c++)
bcells.push({ x: c * (cw + gap) + gap, y: r * (ch + gap) + gap, w: cw, h: ch, ph: Math.random() * 6.28, on: Math.random() < 0.2 });
const P=Math.max(5, Math.round(bw / 320));
bpts=[];
for (let i=0; i < P; i++) bpts.push({ x: Math.random() * bw, y: Math.random() * bh, sp: 40 + Math.random() * 90, drift: (Math.random() - 0.5) * 14 });
};
const bloop=(ts)=> {
requestAnimationFrame(bloop);
if(document.hidden) return;
if(ts - blast < 40) return;
const dt=Math.min(0.05, (ts - blast) / 1000);
blast=ts; bt +=0.02;
bctx.clearRect(0, 0, bw, bh);
for (const p of bpts){
p.x +=p.sp * dt; p.y +=p.drift * dt;
if(p.x > bw + 40){ p.x=-40; p.y=Math.random() * bh; }
if(p.y < 0||p.y > bh) p.drift *=-1;
}
for (const cell of bcells){
let near=0;
const cx=cell.x + cell.w / 2, cy=cell.y + cell.h / 2;
for (const p of bpts){ const d=Math.hypot(cx - p.x, cy - p.y); if(d < 110) near=Math.max(near, 1 - d / 110); }
const bs=cell.on ? 0.24 + 0.18 * Math.sin(bt * 2 + cell.ph):0.06;
bctx.fillStyle=brgba(Math.min(0.92, bs + near * 0.6));
brr(cell.x, cell.y, cell.w, cell.h, 3); bctx.fill();
}
for (const p of bpts){
const g=bctx.createRadialGradient(p.x, p.y, 0, p.x, p.y, 26);
g.addColorStop(0, brgba(0.5)); g.addColorStop(1, brgba(0));
bctx.fillStyle=g;
bctx.beginPath(); bctx.arc(p.x, p.y, 26, 0, Math.PI * 2); bctx.fill();
bctx.fillStyle=brgba(0.9);
bctx.beginPath(); bctx.arc(p.x, p.y, 2.2, 0, Math.PI * 2); bctx.fill();
}};
fitBg(); bloop(0);
window.addEventListener('resize', fitBg, { passive: true });
}
const gbg=document.querySelector('.vps-glyph-bg');
if(gbg){
const gx=gbg.getContext('2d');
const grgba=(a)=> `rgba(184,156,255,${a})`;
let gw=0, gh=0, glast=0, gt=0, gitems=[];
const gscale=()=> (window.innerWidth > 3000 ? 0.5:window.innerWidth > 2200 ? 0.66:Math.min(window.devicePixelRatio||1, 2));
const gkinds=['db', 'server', 'docker', 'ssl', 'vm', 'terminal'];
const gcode=['sudo', 'ssh', ':~$', 'root@', 'docker', '443', 'nginx', 'systemctl', 'backup', '.sh'];
const grr=(x, y, bw, bh, r)=> { gx.beginPath(); gx.moveTo(x + r, y); gx.arcTo(x + bw, y, x + bw, y + bh, r); gx.arcTo(x + bw, y + bh, x, y + bh, r); gx.arcTo(x, y + bh, x, y, r); gx.arcTo(x, y, x + bw, y, r); gx.closePath(); };
const gfit=()=> {
gw=window.innerWidth; gh=window.innerHeight;
const dpr=gscale();
gbg.width=gw * dpr; gbg.height=gh * dpr;
gx.setTransform(dpr, 0, 0, dpr, 0, 0);
gitems=[];
const I=Math.min(11, Math.max(5, Math.round(gw / 240)));
for (let i=0; i < I; i++) gitems.push({ type: 'icon', kind: gkinds[i % gkinds.length], x: 50 + Math.random() * (gw - 100), y: Math.random() * gh, sp: 6 + Math.random() * 10, ph: Math.random() * 6.28, sc: 0.85 + Math.random() * 0.6, a: 0.14 + Math.random() * 0.14 });
const G=Math.min(11, Math.max(5, Math.round(gw / 220)));
for (let i=0; i < G; i++) gitems.push({ type: 'code', text: gcode[(Math.random() * gcode.length) | 0], x: Math.random() * gw, y: Math.random() * gh, sp: 8 + Math.random() * 12, ph: Math.random() * 6.28, size: 12 + Math.random() * 7, a: 0.12 + Math.random() * 0.16 });
};
const gdraw=(it)=> {
gx.save(); gx.translate(it.x, it.y); gx.scale(it.sc, it.sc);
gx.strokeStyle=grgba(it.a); gx.fillStyle=grgba(it.a * 0.7); gx.lineWidth=1.4;
if(it.kind==='db'){
gx.beginPath(); gx.ellipse(0, -12, 13, 4.5, 0, 0, Math.PI * 2); gx.stroke();
gx.beginPath(); gx.moveTo(-13, -12); gx.lineTo(-13, 12); gx.moveTo(13, -12); gx.lineTo(13, 12); gx.stroke();
gx.beginPath(); gx.ellipse(0, 0, 13, 4.5, 0, 0, Math.PI); gx.stroke();
gx.beginPath(); gx.ellipse(0, 12, 13, 4.5, 0, 0, Math.PI); gx.stroke();
}else if(it.kind==='server'){
grr(-15, -12, 30, 10, 2); gx.stroke(); grr(-15, 2, 30, 10, 2); gx.stroke();
gx.beginPath(); gx.arc(-10, -7, 1.5, 0, Math.PI * 2); gx.arc(-10, 7, 1.5, 0, Math.PI * 2); gx.fill();
gx.beginPath(); gx.moveTo(5, -7); gx.lineTo(11, -7); gx.moveTo(5, 7); gx.lineTo(11, 7); gx.stroke();
}else if(it.kind==='docker'){
for (let r=0; r < 2; r++) for (let c=0; c < 3; c++){ grr(-14 + c * 10, -8 + r * 10, 8, 8, 1.5); gx.stroke(); }
grr(-4, -18, 8, 8, 1.5); gx.stroke();
}else if(it.kind==='ssl'){
grr(-9, -2, 18, 15, 3); gx.stroke();
gx.beginPath(); gx.arc(0, -2, 6, Math.PI, 0); gx.stroke();
gx.beginPath(); gx.arc(0, 5, 2, 0, Math.PI * 2); gx.fill();
}else if(it.kind==='vm'){
grr(-15, -12, 30, 24, 3); gx.stroke(); grr(-9, -6, 18, 14, 2); gx.stroke();
}else{
grr(-15, -11, 30, 22, 3); gx.stroke();
gx.beginPath(); gx.moveTo(-9, -3); gx.lineTo(-4, 0); gx.lineTo(-9, 3); gx.moveTo(-2, 4); gx.lineTo(6, 4); gx.stroke();
}
gx.restore();
};
const gloop=(ts)=> {
requestAnimationFrame(gloop);
if(document.hidden) return;
if(ts - glast < 40) return;
const dt=Math.min(0.05, (ts - glast) / 1000);
glast=ts; gt +=dt;
gx.clearRect(0, 0, gw, gh);
gx.textBaseline='middle'; gx.textAlign='center';
for (const it of gitems){
it.y -=it.sp * dt;
if(it.y < -24){ it.y=gh + 24; it.x=50 + Math.random() * (gw - 100); }
if(it.type==='icon') gdraw(it);
else { gx.font=`700 ${it.size}px "JetBrains Mono", monospace`; gx.fillStyle=grgba(it.a + 0.06 * Math.sin(gt * 1.5 + it.ph)); gx.fillText(it.text, it.x, it.y); }}
gx.textAlign='left';
};
gfit(); gloop(0);
window.addEventListener('resize', gfit, { passive: true });
}
const cta=document.querySelector('.vps-cta');
const ctaCanvas=document.querySelector('.vps-cta-canvas');
if(cta&&ctaCanvas){
const cx=ctaCanvas.getContext('2d');
const pal=['#B89CFF', '#7C3CFF', '#FF2F92'];
const crgba=(hex, a)=> { const n=parseInt(hex.slice(1), 16); return `rgba(${(n>>16)&255},${(n>>8)&255},${n&255},${a})`; };
let cw=0, ch=0, clast=0, ct=0, cnodes=[], cpulses=[], cemit=0, cvis=true;
const cscale=()=> (window.innerWidth > 3000 ? 0.5:window.innerWidth > 2200 ? 0.66:Math.min(window.devicePixelRatio||1, 2));
const cfit=()=> {
cw=cta.clientWidth; ch=cta.clientHeight;
const dpr=cscale();
ctaCanvas.width=cw * dpr; ctaCanvas.height=ch * dpr;
cx.setTransform(dpr, 0, 0, dpr, 0, 0);
const N=Math.min(70, Math.max(28, Math.round((cw * ch) / 4800)));
cnodes=[];
for (let i=0; i < N; i++) cnodes.push({ x: Math.random()*cw, y: Math.random()*ch, vx:(Math.random()-0.5)*0.22, vy:(Math.random()-0.5)*0.22, ph:Math.random()*6.28, c: pal[i%pal.length] });
cpulses=[];
};
const cloop=(ts)=> {
requestAnimationFrame(cloop);
if(!cvis||document.hidden) return;
if(ts - clast < 33) return;
const dt=Math.min(0.05, (ts - clast)/1000);
clast=ts; ct +=dt;
cx.clearRect(0, 0, cw, ch);
for (const n of cnodes){ n.x +=n.vx; n.y +=n.vy; if(n.x<0||n.x>cw) n.vx*=-1; if(n.y<0||n.y>ch) n.vy*=-1; }
for (let i=0;i<cnodes.length;i++) for (let j=i+1;j<cnodes.length;j++){
const a=cnodes[i],b=cnodes[j],d=Math.hypot(a.x-b.x,a.y-b.y);
if(d<160){ cx.strokeStyle=crgba(a.c,(1-d/160)*0.24); cx.lineWidth=0.8; cx.beginPath(); cx.moveTo(a.x,a.y); cx.lineTo(b.x,b.y); cx.stroke(); }}
cemit -=dt;
if(cemit<=0&&cnodes.length>1){ cemit=0.3+Math.random()*0.4; const a=cnodes[(Math.random()*cnodes.length)|0]; let best=null,bd=170; for(const b of cnodes){ if(b===a)continue; const d=Math.hypot(a.x-b.x,a.y-b.y); if(d<bd){bd=d;best=b;}} if(best) cpulses.push({a,b:best,p:0,c:a.c});}
for (const n of cnodes){ const pu=0.5+0.5*Math.sin(ct*1.6+n.ph); cx.fillStyle=crgba(n.c,0.55+pu*0.4); cx.beginPath(); cx.arc(n.x,n.y,2+pu*0.8,0,Math.PI*2); cx.fill(); }
cpulses=cpulses.filter(p=>{ p.p+=dt*1.1; const x=p.a.x+(p.b.x-p.a.x)*p.p, y=p.a.y+(p.b.y-p.a.y)*p.p; const g=cx.createRadialGradient(x,y,0,x,y,7); g.addColorStop(0,crgba(p.c,0.95)); g.addColorStop(1,crgba(p.c,0)); cx.fillStyle=g; cx.beginPath(); cx.arc(x,y,7,0,Math.PI*2); cx.fill(); return p.p<1; });
};
new IntersectionObserver((es)=>{ cvis=es[0].isIntersecting; }, { threshold: 0.01 }).observe(cta);
cfit(); cloop(0);
window.addEventListener('resize', cfit, { passive: true });
}})();