(function (){
const cyan=(a)=> `rgba(52,213,255,${a})`;
const green=(a)=> `rgba(61,255,181,${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 scale=()=> (window.innerWidth > 3000 ? 0.5:window.innerWidth > 2200 ? 0.66:Math.min(window.devicePixelRatio||1, 2));
const bg=document.querySelector('.cloud-flow-bg');
if(bg){
const bx=bg.getContext('2d');
let bw=0, bh=0, last=0, ups=[], downs=[];
const fit=()=> {
bw=window.innerWidth; bh=window.innerHeight;
const dpr=scale();
bg.width=bw * dpr; bg.height=bh * dpr;
bx.setTransform(dpr, 0, 0, dpr, 0, 0);
const N=Math.min(26, Math.max(10, Math.round(bw / 90)));
ups=[]; downs=[];
for (let i=0; i < N; i++) ups.push({ x: Math.random() * bw, y: Math.random() * bh, len: 40 + Math.random() * 40, sp: 60 + Math.random() * 90, a: 0.16 + Math.random() * 0.24 });
for (let i=0; i < Math.round(N * 0.7); i++) downs.push({ x: Math.random() * bw, y: Math.random() * bh, len: 46 + Math.random() * 44, sp: 90 + Math.random() * 130, a: 0.14 + Math.random() * 0.22 });
};
const loop=(ts)=> {
requestAnimationFrame(loop);
if(document.hidden) return;
if(ts - last < 33) return;
const dt=Math.min(0.05, (ts - last) / 1000);
last=ts;
bx.clearRect(0, 0, bw, bh);
bx.lineCap='round';
for (const p of ups){
p.y -=p.sp * dt;
if(p.y + p.len < 0){ p.y=bh + p.len; p.x=Math.random() * bw; }
const g=bx.createLinearGradient(p.x, p.y, p.x, p.y + p.len);
g.addColorStop(0, cyan(p.a)); g.addColorStop(1, cyan(0));
bx.strokeStyle=g; bx.lineWidth=1.6;
bx.beginPath(); bx.moveTo(p.x, p.y); bx.lineTo(p.x, p.y + p.len); bx.stroke();
}
for (const p of downs){
p.y +=p.sp * dt;
if(p.y - p.len > bh){ p.y=-p.len; p.x=Math.random() * bw; }
const g=bx.createLinearGradient(p.x, p.y, p.x, p.y - p.len);
g.addColorStop(0, green(p.a)); g.addColorStop(1, green(0));
bx.strokeStyle=g; bx.lineWidth=2.1;
bx.beginPath(); bx.moveTo(p.x, p.y); bx.lineTo(p.x, p.y - p.len); bx.stroke();
}};
fit(); loop(0);
window.addEventListener('resize', fit, { passive: true });
}
const gbg=document.querySelector('.cloud-glyph-bg');
if(gbg){
const gx=gbg.getContext('2d');
let gw=0, gh=0, glast=0, gt=0, items=[];
const kinds=['folder', 'photo', 'doc', 'video', 'nas', 'share'];
const codes=['SSD', 'RAID', 'SYNC', 'HTTPS', 'DSM', 'SHARE', 'BACKUP', 'TW'];
const rr=(x, y, w, h, r)=> { gx.beginPath(); gx.moveTo(x + r, y); gx.arcTo(x + w, y, x + w, y + h, r); gx.arcTo(x + w, y + h, x, y + h, r); gx.arcTo(x, y + h, x, y, r); gx.arcTo(x, y, x + w, y, r); gx.closePath(); };
const gfit=()=> {
gw=window.innerWidth; gh=window.innerHeight;
const dpr=scale();
gbg.width=gw * dpr; gbg.height=gh * dpr;
gx.setTransform(dpr, 0, 0, dpr, 0, 0);
items=[];
const I=Math.min(11, Math.max(5, Math.round(gw / 240)));
for (let i=0; i < I; i++) items.push({ type: 'icon', kind: kinds[i % kinds.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.13 + Math.random() * 0.14 });
const G=Math.min(9, Math.max(4, Math.round(gw / 280)));
for (let i=0; i < G; i++) items.push({ type: 'code', text: codes[(Math.random() * codes.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.11 + Math.random() * 0.15 });
};
const draw=(it)=> {
gx.save(); gx.translate(it.x, it.y); gx.scale(it.sc, it.sc);
gx.strokeStyle=cyan(it.a); gx.fillStyle=cyan(it.a * 0.6); gx.lineWidth=1.4;
if(it.kind==='folder'){
gx.beginPath(); gx.moveTo(-14, -8); gx.lineTo(-4, -8); gx.lineTo(-1, -11); gx.lineTo(14, -11); gx.lineTo(14, 10); gx.lineTo(-14, 10); gx.closePath(); gx.stroke();
}else if(it.kind==='photo'){
rr(-13, -10, 26, 20, 3); gx.stroke();
gx.beginPath(); gx.arc(-5, -3, 2.4, 0, Math.PI * 2); gx.fill();
gx.beginPath(); gx.moveTo(-11, 7); gx.lineTo(-2, -1); gx.lineTo(4, 4); gx.lineTo(8, 0); gx.lineTo(13, 7); gx.stroke();
}else if(it.kind==='doc'){
gx.beginPath(); gx.moveTo(-9, -13); gx.lineTo(5, -13); gx.lineTo(10, -8); gx.lineTo(10, 13); gx.lineTo(-9, 13); gx.closePath(); gx.stroke();
for (let i=0; i < 3; i++){ gx.beginPath(); gx.moveTo(-5, -3 + i * 5); gx.lineTo(6, -3 + i * 5); gx.stroke(); }}else if(it.kind==='video'){
rr(-14, -8, 20, 16, 2.5); gx.stroke();
gx.beginPath(); gx.moveTo(8, -4); gx.lineTo(14, -8); gx.lineTo(14, 8); gx.lineTo(8, 4); gx.closePath(); gx.stroke();
}else if(it.kind==='nas'){
rr(-10, -13, 20, 26, 2); gx.stroke();
for (let i=0; i < 3; i++){ gx.beginPath(); gx.moveTo(-6, -7 + i * 7); gx.lineTo(6, -7 + i * 7); gx.stroke(); gx.beginPath(); gx.arc(4.5, -7 + i * 7, 1, 0, Math.PI * 2); gx.fill(); }}else{
gx.beginPath(); gx.arc(-8, 6, 3, 0, Math.PI * 2); gx.stroke();
gx.beginPath(); gx.arc(8, -8, 3, 0, Math.PI * 2); gx.stroke();
gx.beginPath(); gx.arc(8, 8, 3, 0, Math.PI * 2); gx.stroke();
gx.beginPath(); gx.moveTo(-6, 4.5); gx.lineTo(6, -6.5); gx.moveTo(-6, 7); gx.lineTo(6, 7.5); 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 items){
it.y -=it.sp * dt;
if(it.y < -26){ it.y=gh + 26; it.x=50 + Math.random() * (gw - 100); }
if(it.type==='icon') draw(it);
else { gx.font=`700 ${it.size}px "JetBrains Mono", monospace`; gx.fillStyle=cyan(it.a + 0.05 * 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=['#34D5FF', '#3DFFB5', '#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, nodes=[], pulses=[], cemit=0, cvis=true;
const cfit=()=> {
cw=cta.clientWidth; ch=cta.clientHeight;
const dpr=scale();
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)));
nodes=[];
for (let i=0; i < N; i++) nodes.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] });
pulses=[];
};
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 nodes){ 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 < 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 < 160){ cx.strokeStyle=crgba(a.c, (1 - d / 160) * 0.22); 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&&nodes.length > 1){ cemit=0.3 + Math.random() * 0.4; const a=nodes[(Math.random() * nodes.length) | 0]; let best=null, bd=170; 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(ct * 1.6 + n.ph); cx.fillStyle=crgba(n.c, 0.5 + pu * 0.4); cx.beginPath(); cx.arc(n.x, n.y, 2 + pu * 0.8, 0, Math.PI * 2); cx.fill(); }
pulses=pulses.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 });
}})();
(function (){
const els=document.querySelectorAll('.vps-reveal');
if(!els.length) return;
if(!('IntersectionObserver' in window)){
els.forEach(el=> el.classList.add('is-in'));
return;
}
const io=new IntersectionObserver((entries)=> {
entries.forEach((e)=> {
if(e.isIntersecting){ e.target.classList.add('is-in'); io.unobserve(e.target); }});
}, { threshold: 0.12, rootMargin: '0px 0px -8% 0px' });
els.forEach((el, i)=> {
if(!el.style.transitionDelay) el.style.transitionDelay=((i % 4) * 0.06) + 's';
io.observe(el);
});
})();