(function (){
const orange=(a)=> `rgba(255,179,92,${a})`;
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 gbg=document.querySelector('.ai-glyph');
if(gbg){
const gx=gbg.getContext('2d');
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=['brain', 'chip', 'db', 'graph', 'bot', 'vector'];
const gcode=['LLM', 'RAG', 'API', 'JSON', 'Agent', 'Embed', 'Token', 'Prompt', 'Vector', 'Webhook'];
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=orange(it.a); gx.fillStyle=orange(it.a * 0.7); gx.lineWidth=1.4;
if(it.kind==='brain'){
gx.beginPath(); gx.arc(-5, 0, 8, Math.PI * 0.5, Math.PI * 1.5); gx.stroke();
gx.beginPath(); gx.arc(5, 0, 8, Math.PI * 1.5, Math.PI * 0.5); gx.stroke();
gx.beginPath(); gx.moveTo(0, -8); gx.lineTo(0, 8); gx.stroke();
gx.beginPath(); gx.moveTo(-5, -3); gx.lineTo(-9, -6); gx.moveTo(5, 3); gx.lineTo(9, 6); gx.stroke();
}else if(it.kind==='chip'){
grr(-9, -9, 18, 18, 3); gx.stroke();
grr(-4, -4, 8, 8, 1); gx.stroke();
for (let i=-1; i <=1; i++){ gx.beginPath(); gx.moveTo(i * 5, -9); gx.lineTo(i * 5, -13); gx.moveTo(i * 5, 9); gx.lineTo(i * 5, 13); gx.moveTo(-9, i * 5); gx.lineTo(-13, i * 5); gx.moveTo(9, i * 5); gx.lineTo(13, i * 5); gx.stroke(); }}else if(it.kind==='db'){
gx.beginPath(); gx.ellipse(0, -9, 10, 3.6, 0, 0, Math.PI * 2); gx.stroke();
gx.beginPath(); gx.moveTo(-10, -9); gx.lineTo(-10, 9); gx.moveTo(10, -9); gx.lineTo(10, 9); gx.stroke();
gx.beginPath(); gx.ellipse(0, 0, 10, 3.6, 0, 0, Math.PI); gx.stroke();
gx.beginPath(); gx.ellipse(0, 9, 10, 3.6, 0, 0, Math.PI); gx.stroke();
}else if(it.kind==='graph'){
const pts=[[-11, 6], [-4, -4], [3, 2], [11, -8]];
gx.beginPath(); pts.forEach((p, i)=> i ? gx.lineTo(p[0], p[1]):gx.moveTo(p[0], p[1])); gx.stroke();
pts.forEach(p=> { gx.beginPath(); gx.arc(p[0], p[1], 1.8, 0, Math.PI * 2); gx.fill(); });
}else if(it.kind==='bot'){
grr(-10, -6, 20, 15, 4); gx.stroke();
gx.beginPath(); gx.arc(-4, 1, 1.6, 0, Math.PI * 2); gx.arc(4, 1, 1.6, 0, Math.PI * 2); gx.fill();
gx.beginPath(); gx.moveTo(0, -6); gx.lineTo(0, -11); gx.stroke();
gx.beginPath(); gx.arc(0, -12.5, 1.6, 0, Math.PI * 2); gx.fill();
}else{
for (let i=0; i < 5; i++){ const a=(i / 5) * Math.PI * 2; gx.beginPath(); gx.moveTo(0, 0); gx.lineTo(Math.cos(a) * 11, Math.sin(a) * 11); gx.stroke(); gx.beginPath(); gx.arc(Math.cos(a) * 11, Math.sin(a) * 11, 1.7, 0, Math.PI * 2); gx.fill(); }
gx.beginPath(); gx.arc(0, 0, 2.4, 0, Math.PI * 2); gx.fill();
}
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=orange(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 rbg=document.querySelector('.ai-flow');
if(rbg){
const rx=rbg.getContext('2d');
const pal=['#FFB35C', '#FF8F6B', '#FFD98A'];
const rgba=(hex, a)=> { const n=parseInt(hex.slice(1), 16); return `rgba(${(n >> 16) & 255},${(n >> 8) & 255},${n & 255},${a})`; };
const rscale=()=> (window.innerWidth > 3000 ? 0.5:window.innerWidth > 2200 ? 0.66:Math.min(window.devicePixelRatio||1, 2));
let rw=0, rh=0, rlast=0, rt=0, nodes=[], pulses=[], emit=0;
const rfit=()=> {
rw=window.innerWidth; rh=window.innerHeight;
const dpr=rscale();
rbg.width=rw * dpr; rbg.height=rh * dpr;
rx.setTransform(dpr, 0, 0, dpr, 0, 0);
const N=Math.min(28, Math.max(12, Math.round((rw * rh) / 72000)));
nodes=[];
for (let i=0; i < N; i++) nodes.push({ x: 40 + Math.random() * (rw - 80), y: 40 + Math.random() * (rh - 80), vx: (Math.random() - 0.5) * 0.18, vy: (Math.random() - 0.5) * 0.18, ph: Math.random() * 6.28, c: pal[i % pal.length] });
pulses=[];
};
const rloop=(ts)=> {
requestAnimationFrame(rloop);
if(document.hidden) return;
if(ts - rlast < 33) return;
const dt=Math.min(0.05, (ts - rlast) / 1000);
rlast=ts; rt +=dt;
rx.clearRect(0, 0, rw, rh);
for (const n of nodes){ n.x +=n.vx; n.y +=n.vy; if(n.x < 20||n.x > rw - 20) n.vx *=-1; if(n.y < 20||n.y > rh - 20) n.vy *=-1; }
for (let i=0; i < nodes.length; i++) for (let j=i + 1; j < nodes.length; j++){
const a=nodes[i], b=nodes[j], d=Math.hypot(a.x - b.x, a.y - b.y);
if(d < 165){ rx.strokeStyle=rgba(a.c, (1 - d / 165) * 0.22); rx.lineWidth=0.9; rx.beginPath(); rx.moveTo(a.x, a.y); rx.lineTo(b.x, b.y); rx.stroke(); }}
emit -=dt;
if(emit <=0&&nodes.length > 1){
emit=0.28 + Math.random() * 0.4;
const a=nodes[(Math.random() * nodes.length) | 0];
let best=null, bd=175;
for (const b of nodes){ 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) pulses.push({ a, b: best, p: 0, c: a.c });
}
for (const n of nodes){ const pu=0.5 + 0.5 * Math.sin(rt * 1.7 + n.ph); rx.fillStyle=rgba(n.c, 0.5 + pu * 0.42); rx.beginPath(); rx.arc(n.x, n.y, 2.2 + pu * 0.9, 0, Math.PI * 2); rx.fill(); }
pulses=pulses.filter(p=> {
p.p +=dt * 1.05;
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=rx.createRadialGradient(x, y, 0, x, y, 8);
g.addColorStop(0, rgba(p.c, 0.92)); g.addColorStop(1, rgba(p.c, 0));
rx.fillStyle=g; rx.beginPath(); rx.arc(x, y, 8, 0, Math.PI * 2); rx.fill();
return p.p < 1;
});
};
rfit(); rloop(0);
window.addEventListener('resize', rfit, { 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=['#FFB35C', '#FF8F6B', '#FFD98A'];
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 });
}})();