(function (){
const green=(a)=> `rgba(61,255,181,${a})`;
const cyan=(a)=> `rgba(52,213,255,${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 bg=null;
if(false){
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 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;
blast=ts; bt +=0.02;
bctx.clearRect(0, 0, bw, bh);
const dt=0.04;
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=green(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, green(0.5)); g.addColorStop(1, green(0));
bctx.fillStyle=g;
bctx.beginPath(); bctx.arc(p.x, p.y, 26, 0, Math.PI * 2); bctx.fill();
bctx.fillStyle=green(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');
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=['router', 'switch', 'nas', 'firewall', 'wifi', 'printer'];
const gcode=['LAN', 'VLAN', 'PoE', 'AP', 'DHCP', 'RAID', 'NAS', 'FW', 'UPS', 'Wi-Fi7'];
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=green(it.a); gx.fillStyle=green(it.a * 0.7); gx.lineWidth=1.4;
if(it.kind==='router'){
grr(-16, -4, 32, 12, 2); gx.stroke();
gx.beginPath(); gx.arc(-9, 2, 1.5, 0, Math.PI * 2); gx.arc(-2, 2, 1.5, 0, Math.PI * 2); gx.fill();
gx.beginPath(); gx.moveTo(6, -4); gx.lineTo(10, -12); gx.moveTo(11, -4); gx.lineTo(15, -13); gx.stroke();
}else if(it.kind==='switch'){
grr(-16, -6, 32, 12, 2); gx.stroke();
for (let i=0; i < 6; i++){ grr(-13 + i * 4.4, 0, 3, 3, 0.5); gx.stroke(); }}else if(it.kind==='nas'){
grr(-11, -13, 22, 26, 2); gx.stroke();
for (let i=0; i < 3; i++){ gx.beginPath(); gx.moveTo(-7, -8 + i * 7); gx.lineTo(7, -8 + i * 7); gx.stroke(); gx.beginPath(); gx.arc(5, -8 + i * 7, 1, 0, Math.PI * 2); gx.fill(); }}else if(it.kind==='firewall'){
grr(-13, -13, 26, 26, 3); gx.stroke();
for (let r=0; r < 3; r++){ gx.beginPath(); gx.moveTo(-13, -5 + r * 6); gx.lineTo(13, -5 + r * 6); gx.stroke(); }
gx.beginPath(); gx.moveTo(0, -13); gx.lineTo(0, 13); gx.stroke();
}else if(it.kind==='wifi'){
for (let r=1; r <=3; r++){ gx.beginPath(); gx.arc(0, 8, r * 6, Math.PI * 1.15, Math.PI * 1.85); gx.stroke(); }
gx.beginPath(); gx.arc(0, 8, 1.6, 0, Math.PI * 2); gx.fill();
}else{
grr(-13, -6, 26, 14, 2); gx.stroke();
grr(-8, -13, 16, 8, 1); gx.stroke();
grr(-8, 4, 16, 8, 1); 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=green(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('.onsite-repair');
if(rbg){
const rx=rbg.getContext('2d');
const color='#3DFFB5', fault='#FF7A6B';
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=[], repairs=[];
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(30, Math.max(12, Math.round((rw * rh) / 70000)));
nodes=[];
for (let i=0; i < N; i++) nodes.push({ x: 40 + Math.random() * (rw - 80), y: 40 + Math.random() * (rh - 80), ph: Math.random() * 6.28, state: 'ok', timer: Math.random() * 6 });
repairs=[];
};
const drawTool=(x, y, r, a, rot)=> { rx.save(); rx.translate(x, y); rx.rotate(rot); rx.strokeStyle=rgba(color, a); rx.lineWidth=1.4; rx.beginPath(); rx.moveTo(-r, 0); rx.lineTo(r, 0); rx.moveTo(0, -r); rx.lineTo(0, r); rx.stroke(); rx.restore(); };
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 (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 < 150){ rx.strokeStyle=rgba(color, (1 - d / 150) * 0.24); rx.lineWidth=0.9; rx.beginPath(); rx.moveTo(a.x, a.y); rx.lineTo(b.x, b.y); rx.stroke(); }}
for (const nd of nodes){
nd.timer -=dt;
if(nd.state==='ok'&&nd.timer <=0){ nd.state='fault'; nd.timer=1.4 + Math.random() * 2; repairs.push({ x: Math.random() < 0.5 ? 0:rw, y: Math.random() * rh, tx: nd.x, ty: nd.y, node: nd, p: 0 });}
else if(nd.state==='fault'&&nd.timer <=0){ nd.state='ok'; nd.timer=5 + Math.random() * 6; }
else if(nd.state==='fixing'){ nd.fixT -=dt; if(nd.fixT <=0){ nd.state='ok'; nd.timer=6 + Math.random() * 7; }}
const pulse=0.5 + 0.5 * Math.sin(rt * 2 + nd.ph);
let col, rad, a;
if(nd.state==='fault'){ col=fault; rad=3; a=0.5 + 0.4 * Math.sin(rt * 12); }
else if(nd.state==='fixing'){ col=color; rad=3.4; a=0.95; drawTool(nd.x, nd.y, 8, 0.7 * nd.fixT / 0.6, rt * 4); }else{ col=color; rad=2.6; a=0.55 + pulse * 0.4; }
rx.fillStyle=rgba(col, a); rx.beginPath(); rx.arc(nd.x, nd.y, rad, 0, Math.PI * 2); rx.fill();
}
repairs=repairs.filter(rp=> {
rp.p +=dt * 0.9;
const x=rp.x + (rp.tx - rp.x) * rp.p, y=rp.y + (rp.ty - rp.y) * rp.p;
if(rp.p < 1){ const g=rx.createRadialGradient(x, y, 0, x, y, 7); g.addColorStop(0, rgba(color, 0.9)); g.addColorStop(1, rgba(color, 0)); rx.fillStyle=g; rx.beginPath(); rx.arc(x, y, 7, 0, Math.PI * 2); rx.fill(); return true; }
if(rp.node.state==='fault'){ rp.node.state='fixing'; rp.node.fixT=0.6; }
rp.ring=(rp.ring||0) + dt * 90;
const ring=rp.ring;
rx.strokeStyle=rgba(color, Math.max(0, 0.7 - ring / 60)); rx.lineWidth=1.6; rx.beginPath(); rx.arc(rp.tx, rp.ty, ring, 0, Math.PI * 2); rx.stroke();
return ring < 44;
});
};
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=['#3DFFB5', '#34D5FF', '#7AA2FF'];
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 });
}})();