(function (){
if(window.__ktRevealSafety) return;
window.__ktRevealSafety=true;
var run=function (){
var els=document.querySelectorAll('.vps-reveal');
if(!els.length) return;
if(!('IntersectionObserver' in window)){
els.forEach(function (el){ el.classList.add('is-in'); });
return;
}
var io=new IntersectionObserver(function (entries){
entries.forEach(function (e){ if(e.isIntersecting){ e.target.classList.add('is-in'); io.unobserve(e.target); }});
}, { threshold: 0.08, rootMargin: '0px 0px -5% 0px' });
els.forEach(function (el){ io.observe(el); });
setTimeout(function (){
document.querySelectorAll('.vps-reveal:not(.is-in)').forEach(function (el){
var r=el.getBoundingClientRect();
if(r.top < window.innerHeight * 1.3){ el.classList.add('is-in'); }});
}, 1200);
};
if(document.readyState==='loading'){
document.addEventListener('DOMContentLoaded', run);
}else{ run(); }
window.addEventListener('load', run);
})();
window.__kurtechVisibleScenes=window.__kurtechVisibleScenes||new WeakSet();
window.__kurtechDisableParticles = !!(window.matchMedia&&window.matchMedia('(prefers-reduced-motion: reduce)').matches);
window.__kurtechResumeCallbacks=window.__kurtechResumeCallbacks||new Map();
function kurtechObserveVisible(el){
const io=new IntersectionObserver((entries)=> {
entries.forEach((entry)=> {
if(entry.isIntersecting){
window.__kurtechVisibleScenes.add(el);
const resumes=window.__kurtechResumeCallbacks.get(el);
if(resumes) resumes.forEach((fn)=> fn());
}else{
window.__kurtechVisibleScenes.delete(el);
}});
}, { threshold: 0.01 });
io.observe(el);
}
document.addEventListener('visibilitychange', ()=> {
if(document.hidden) return;
window.__kurtechResumeCallbacks.forEach((resumes, el)=> {
if(window.__kurtechVisibleScenes.has(el)) resumes.forEach((fn)=> fn());
});
});
function kurtechSceneStep(el){
var c=el.classList;
if(c.contains('service-scene-web')) return 1;
if(c.contains('service-scene-cloud')) return 2;
if(c.contains('service-scene-vps')) return 3;
if(c.contains('service-scene-data')) return 4;
if(c.contains('service-scene-onsite')) return 5;
if(c.contains('service-scene-ads')) return 6;
if(c.contains('launch-landing')) return 0;
return null;
}
function kurtechShouldRender(el){
if(document.hidden) return false;
if(!window.__kurtechVisibleScenes.has(el)) return false;
var step=kurtechSceneStep(el);
if(step!==null&&window.__kurtechActiveStep!=null&&step!==window.__kurtechActiveStep) return false;
return true;
}
window.__kurtechTouch = !!(window.matchMedia&&window.matchMedia('(pointer: coarse)').matches);
if(window.__kurtechTouch) window.__kurtechDisableParticles=true;
if(window.__kurtechDisableParticles){
document.addEventListener('DOMContentLoaded', kurtechDropDecorCanvas);
kurtechDropDecorCanvas();
}
function kurtechDropDecorCanvas(){
document.querySelectorAll([
'.hero-code-canvas', '.hero-network-canvas', '.vps-cta-canvas',
'.cloud-flow-bg', '.cloud-glyph-bg',
'.vps-telemetry-bg', '.vps-glyph-bg',
'.onsite-repair', '.onsite-glyph', '.ads-glyph'
].join(',')).forEach((c)=> c.remove());
}
function kurtechParticleScale(){
const w=window.innerWidth;
if(window.__kurtechTouch) return w <=768 ? 0.22:0.3;
if(w <=480) return 0.35;
if(w <=768) return 0.45;
if(w <=1024) return 0.65;
return 1;
}
function kurtechRenderScale(w){
if(window.__kurtechTouch) return w <=768 ? 0.45:0.55;
if(w <=480) return 0.5;
if(w <=768) return 0.6;
if(w <=1024) return 0.75;
if(w > 3000) return 0.5;
if(w > 2200) return 0.66;
if(w > 1700) return 0.85;
return Math.min(window.devicePixelRatio||1, 2);
}
function kurtechRegisterLoop(el, tick){
if(window.__kurtechDisableParticles) return;
let raf=0;
let lastT=0;
const minDelta=1000 / (window.__kurtechTouch ? 20:30);
const loop=(ts)=> {
raf=0;
if(!kurtechShouldRender(el)) return;
raf=requestAnimationFrame(loop);
if(ts - lastT < minDelta) return;
lastT=ts;
tick(ts);
};
const resumeFn=()=> {
if(!raf) raf=requestAnimationFrame(loop);
};
const existing=window.__kurtechResumeCallbacks.get(el);
if(existing) existing.push(resumeFn);
else window.__kurtechResumeCallbacks.set(el, [resumeFn]);
raf=requestAnimationFrame(loop);
}
setTimeout(()=> {
const heroScene=document.querySelector('.launch-landing');
const canvas=document.querySelector('.hero-code-canvas');
if(!heroScene||!canvas||window.__kurtechDisableParticles) return;
const ctx=canvas.getContext('2d');
const palette=['#7C3CFF', '#FF2F92', '#FF4D6D', '#FF7A2F', '#34D5FF', '#B89CFF'];
const rgba=(hex, a)=> {
const n=parseInt(hex.slice(1), 16);
return `rgba(${(n >> 16) & 255}, ${(n >> 8) & 255}, ${n & 255}, ${a})`;
};
let lastT=0, t=0, visible=true;
let w=0, h=0, nodes=[], pulses=[], emitTimer=0;
const fit=()=> {
w=window.innerWidth;
h=window.innerHeight;
const dpr=kurtechRenderScale(w);
canvas.width=w * dpr;
canvas.height=h * dpr;
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
const N=Math.max(3, Math.round(Math.min(66, Math.max(28, Math.round((w * h) / 28000))) * kurtechParticleScale()));
nodes=[];
for (let i=0; i < N; i++){
nodes.push({
x: Math.random() * w,
y: Math.random() * h,
vx: (Math.random() - 0.5) * 0.16,
vy: (Math.random() - 0.5) * 0.16,
ph: Math.random() * 6.28,
c: palette[i % palette.length]
});
}
pulses=[];
};
const loop=(ts)=> {
requestAnimationFrame(loop);
if(!visible||document.hidden) return;
if(ts - lastT < 33) return;
const dt=Math.min(0.05, (ts - lastT) / 1000);
lastT=ts;
t +=dt;
ctx.clearRect(0, 0, w, h);
for (const nd of nodes){
nd.x +=nd.vx; nd.y +=nd.vy;
if(nd.x < 0||nd.x > w) nd.vx *=-1;
if(nd.y < 0||nd.y > h) nd.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];
const d=Math.hypot(a.x - b.x, a.y - b.y);
if(d < 190){
ctx.strokeStyle=rgba(a.c, (1 - d / 190) * 0.28);
ctx.lineWidth=0.9;
ctx.beginPath();
ctx.moveTo(a.x, a.y);
ctx.lineTo(b.x, b.y);
ctx.stroke();
}}
}
emitTimer -=dt;
if(emitTimer <=0&&nodes.length > 1){
emitTimer=0.28 + Math.random() * 0.4;
const a=nodes[(Math.random() * nodes.length) | 0];
let best=null, bestD=200;
for (const b of nodes){
if(b===a) continue;
const d=Math.hypot(a.x - b.x, a.y - b.y);
if(d < bestD){ bestD=d; best=b; }}
if(best) pulses.push({ a, b: best, p: 0, c: a.c });
}
for (const nd of nodes){
const pulse=0.5 + 0.5 * Math.sin(t * 1.6 + nd.ph);
ctx.fillStyle=rgba(nd.c, 0.6 + pulse * 0.4);
ctx.beginPath();
ctx.arc(nd.x, nd.y, 2.4 + pulse * 0.9, 0, Math.PI * 2);
ctx.fill();
}
pulses=pulses.filter(p=> {
p.p +=dt * 1.1;
const x=p.a.x + (p.b.x - p.a.x) * p.p;
const y=p.a.y + (p.b.y - p.a.y) * p.p;
const g=ctx.createRadialGradient(x, y, 0, x, y, 7);
g.addColorStop(0, rgba(p.c, 0.95));
g.addColorStop(1, rgba(p.c, 0));
ctx.fillStyle=g;
ctx.beginPath();
ctx.arc(x, y, 7, 0, Math.PI * 2);
ctx.fill();
return p.p < 1;
});
};
new IntersectionObserver((es)=> { visible=es[0].isIntersecting; }, { threshold: 0.01 }).observe(heroScene);
fit();
requestAnimationFrame(()=> { fit(); });
window.addEventListener('load', ()=> fit());
setTimeout(fit, 600);
requestAnimationFrame(loop);
window.addEventListener('resize', ()=> fit(), { passive: true });
}, 0);
setTimeout(()=> {
const heroScene=document.querySelector('.launch-landing');
const canvas=document.querySelector('.hero-network-canvas');
if(!heroScene||!canvas||window.__kurtechDisableParticles) return;
const ctx=canvas.getContext('2d');
const palette=['#7C3CFF', '#FF2F92', '#FF7A2F', '#34D5FF', '#B89CFF'];
const rgba=(hex, a)=> {
const n=parseInt(hex.slice(1), 16);
return `rgba(${(n >> 16) & 255}, ${(n >> 8) & 255}, ${n & 255}, ${a})`;
};
let lastT=0;
let w=0, h=0, ns=[], pulses=[];
const fit=()=> {
w=window.innerWidth;
h=window.innerHeight;
const dpr=kurtechRenderScale(w);
canvas.width=w * dpr;
canvas.height=h * dpr;
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
const N=Math.max(3, Math.round(Math.min(110, Math.max(40, Math.round((w * h) / 13000))) * kurtechParticleScale()));
ns=[];
for (let i=0; i < N; i++){
ns.push({
x: Math.random() * w,
y: Math.random() * h,
vx: (Math.random() - 0.5) * 0.25,
vy: (Math.random() - 0.5) * 0.25,
c: palette[i % palette.length]
});
}
pulses=[];
};
const animate=(ts)=> {
if(ts - lastT < 33) return;
lastT=ts;
ctx.clearRect(0, 0, w, h);
const N=ns.length;
for (const n of ns){
n.x +=n.vx;
n.y +=n.vy;
if(n.x < 0||n.x > w) n.vx *=-1;
if(n.y < 0||n.y > h) n.vy *=-1;
}
for (let i=0; i < N; i++){
for (let j=i + 1; j < N; j++){
const a=ns[i], b=ns[j];
const d=Math.hypot(a.x - b.x, a.y - b.y);
if(d < 160){
ctx.strokeStyle=rgba(a.c, (1 - d / 160) * 0.28);
ctx.lineWidth=0.9;
ctx.beginPath();
ctx.moveTo(a.x, a.y);
ctx.lineTo(b.x, b.y);
ctx.stroke();
}}
}
for (const n of ns){
ctx.fillStyle=rgba(n.c, 0.95);
ctx.beginPath();
ctx.arc(n.x, n.y, 2, 0, Math.PI * 2);
ctx.fill();
}
if(Math.random() < 0.06&&N > 1){
const a=ns[(Math.random() * N) | 0], b=ns[(Math.random() * N) | 0];
if(a!==b) pulses.push({ a, b, t: 0, c: a.c });
}
pulses=pulses.filter(p=> {
p.t +=0.02;
const x=p.a.x + (p.b.x - p.a.x) * p.t;
const y=p.a.y + (p.b.y - p.a.y) * p.t;
const glow=ctx.createRadialGradient(x, y, 0, x, y, 6);
glow.addColorStop(0, p.c);
glow.addColorStop(1, rgba(p.c, 0));
ctx.fillStyle=glow;
ctx.beginPath();
ctx.arc(x, y, 6, 0, Math.PI * 2);
ctx.fill();
return p.t < 1;
});
};
kurtechObserveVisible(heroScene);
fit();
kurtechRegisterLoop(heroScene, animate);
window.addEventListener('resize', ()=> fit(), { passive: true });
}, 0);
setTimeout(()=> {
const webScene=document.querySelector('.service-scene-web');
if(!webScene||window.__kurtechDisableParticles) return;
const canvas=document.createElement('canvas');
canvas.style.cssText='position:absolute;inset:0;width:100%;height:100%;pointer-events:none;z-index:0;';
webScene.insertBefore(canvas, webScene.firstChild);
const ctx=canvas.getContext('2d');
const color='#FF2F92';
const rgba=(hex, a)=> {
const n=parseInt(hex.slice(1), 16);
return `rgba(${(n >> 16) & 255}, ${(n >> 8) & 255}, ${n & 255}, ${a})`;
};
const rr=(x, y, bw, bh, r)=> {
ctx.beginPath();
ctx.moveTo(x + r, y);
ctx.arcTo(x + bw, y, x + bw, y + bh, r);
ctx.arcTo(x + bw, y + bh, x, y + bh, r);
ctx.arcTo(x, y + bh, x, y, r);
ctx.arcTo(x, y, x + bw, y, r);
ctx.closePath();
};
let lastT=0, t=0;
let w=0, h=0, ns=[], pulses=[], devices=[], glyphs=[];
const glyphChars=['</>', '{ }', '()', '[ ]', '&&', '||', '=>', ';'];
const fit=()=> {
w=webScene.clientWidth;
h=webScene.clientHeight;
const dpr=kurtechRenderScale(window.innerWidth);
canvas.width=w * dpr;
canvas.height=h * dpr;
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
const N=Math.max(3, Math.round(Math.min(50, Math.max(14, Math.round((w * h) / 26000))) * kurtechParticleScale()));
ns=[];
for (let i=0; i < N; i++){
ns.push({
x: Math.random() * w,
y: Math.random() * h,
vx: (Math.random() - 0.5) * 0.25,
vy: (Math.random() - 0.5) * 0.25
});
}
pulses=[];
const D=Math.max(3, Math.round(Math.min(7, Math.max(3, Math.round(w / 320))) * kurtechParticleScale()));
devices=[];
for (let i=0; i < D; i++){
const phone=Math.random() < 0.5;
devices.push({
x: 60 + Math.random() * (w - 160),
y: Math.random() * h,
w: phone ? 46:96,
h: phone ? 92:66,
phone,
sp: 6 + Math.random() * 10,
ph: Math.random() * 6.28,
rot: (Math.random() - 0.5) * 0.24,
a: 0.16 + Math.random() * 0.16
});
}
const G=Math.max(3, Math.round(Math.min(9, Math.max(4, Math.round(w / 260))) * kurtechParticleScale()));
glyphs=[];
for (let i=0; i < G; i++){
glyphs.push({
x: Math.random() * w,
y: Math.random() * h,
sp: 8 + Math.random() * 14,
ph: Math.random() * 6.28,
size: 13 + Math.random() * 9,
a: 0.14 + Math.random() * 0.2,
text: glyphChars[(Math.random() * glyphChars.length) | 0]
});
}};
const drawDevice=(d)=> {
ctx.save();
ctx.translate(d.x + d.w / 2, d.y + d.h / 2);
ctx.rotate(d.rot + Math.sin(t * 0.6 + d.ph) * 0.04);
ctx.translate(-d.w / 2, -d.h / 2);
ctx.strokeStyle=rgba(color, d.a);
ctx.lineWidth=1.2;
rr(0, 0, d.w, d.h, d.phone ? 8:6);
ctx.stroke();
if(d.phone){
ctx.fillStyle=rgba(color, d.a * 0.8);
rr(d.w / 2 - 7, 5, 14, 3, 1.5); ctx.fill();
ctx.strokeStyle=rgba(color, d.a * 0.7);
ctx.lineWidth=1;
for (let k=0; k < 4; k++){
const yy=16 + k * 15;
ctx.beginPath(); ctx.moveTo(8, yy); ctx.lineTo(d.w - 8, yy - (k===0 ? 0:0)); ctx.stroke();
}}else{
ctx.beginPath(); ctx.moveTo(0, 14); ctx.lineTo(d.w, 14); ctx.stroke();
ctx.fillStyle=rgba(color, d.a * 0.9);
for (let k=0; k < 3; k++){ ctx.beginPath(); ctx.arc(9 + k * 8, 7, 1.6, 0, Math.PI * 2); ctx.fill(); }
ctx.strokeStyle=rgba(color, d.a * 0.6);
ctx.lineWidth=1;
for (let k=0; k < 3; k++){
const yy=26 + k * 12;
ctx.beginPath(); ctx.moveTo(10, yy); ctx.lineTo(d.w - 10 - (k * 12), yy); ctx.stroke();
}}
ctx.restore();
};
const animate=(ts)=> {
if(ts - lastT < 33) return;
const dt=Math.min(0.05, (ts - lastT) / 1000);
lastT=ts;
t +=dt;
ctx.clearRect(0, 0, w, h);
const N=ns.length;
for (const n of ns){
n.x +=n.vx;
n.y +=n.vy;
if(n.x < 0||n.x > w) n.vx *=-1;
if(n.y < 0||n.y > h) n.vy *=-1;
}
for (let i=0; i < N; i++){
for (let j=i + 1; j < N; j++){
const a=ns[i], b=ns[j];
const d=Math.hypot(a.x - b.x, a.y - b.y);
if(d < 150){
ctx.strokeStyle=rgba(color, (1 - d / 150) * 0.32);
ctx.lineWidth=0.9;
ctx.beginPath();
ctx.moveTo(a.x, a.y);
ctx.lineTo(b.x, b.y);
ctx.stroke();
}}
}
ctx.fillStyle=rgba(color, 0.95);
for (const n of ns){
ctx.beginPath();
ctx.arc(n.x, n.y, 2, 0, Math.PI * 2);
ctx.fill();
}
for (const d of devices){
d.y -=d.sp * dt;
if(d.y + d.h < -10){ d.y=h + 10; d.x=60 + Math.random() * (w - 160); }
drawDevice(d);
}
ctx.textBaseline='middle';
ctx.textAlign='center';
for (const g of glyphs){
g.y -=g.sp * dt;
if(g.y < -14){ g.y=h + 14; g.x=Math.random() * w; }
ctx.font=`700 ${g.size}px "JetBrains Mono", monospace`;
ctx.fillStyle=rgba(color, g.a + 0.06 * Math.sin(t * 1.5 + g.ph));
ctx.fillText(g.text, g.x, g.y);
}
ctx.textAlign='left';
if(Math.random() < 0.04&&N > 1){
const a=ns[(Math.random() * N) | 0], b=ns[(Math.random() * N) | 0];
if(a!==b) pulses.push({ a, b, t: 0 });
}
pulses=pulses.filter(p=> {
p.t +=0.02;
const x=p.a.x + (p.b.x - p.a.x) * p.t;
const y=p.a.y + (p.b.y - p.a.y) * p.t;
const glow=ctx.createRadialGradient(x, y, 0, x, y, 5);
glow.addColorStop(0, color);
glow.addColorStop(1, rgba(color, 0));
ctx.fillStyle=glow;
ctx.beginPath();
ctx.arc(x, y, 5, 0, Math.PI * 2);
ctx.fill();
return p.t < 1;
});
};
kurtechObserveVisible(webScene);
fit();
kurtechRegisterLoop(webScene, animate);
window.addEventListener('resize', ()=> fit(), { passive: true });
}, 0);
setTimeout(()=> {
const vpsScene=document.querySelector('.service-scene-vps');
if(!vpsScene||window.__kurtechDisableParticles) return;
const canvas=document.createElement('canvas');
canvas.style.cssText='position:absolute;inset:0;width:100%;height:100%;pointer-events:none;z-index:0;opacity:0.1;';
vpsScene.insertBefore(canvas, vpsScene.firstChild);
const ctx=canvas.getContext('2d');
const color='#B89CFF';
const rgba=(hex, a)=> {
const n=parseInt(hex.slice(1), 16);
return `rgba(${(n >> 16) & 255}, ${(n >> 8) & 255}, ${n & 255}, ${a})`;
};
const rr=(x, y, w, h, r)=> {
ctx.beginPath();
ctx.moveTo(x + r, y);
ctx.arcTo(x + w, y, x + w, y + h, r);
ctx.arcTo(x + w, y + h, x, y + h, r);
ctx.arcTo(x, y + h, x, y, r);
ctx.arcTo(x, y, x + w, y, r);
ctx.closePath();
};
let lastT=0;
let w=0, h=0, cells=[], t=0;
const fit=()=> {
w=vpsScene.clientWidth;
h=vpsScene.clientHeight;
const dpr=kurtechRenderScale(window.innerWidth);
canvas.width=w * dpr;
canvas.height=h * dpr;
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
const cw=30, ch=15, gap=14;
const cols=Math.max(1, Math.floor(w / (cw + gap)));
const rows=Math.max(1, Math.floor(h / (ch + gap)));
cells=[];
for (let r=0; r < rows; r++){
for (let c=0; c < cols; c++){
cells.push({ x: c * (cw + gap) + gap, y: r * (ch + gap) + gap, w: cw, h: ch, ph: Math.random() * 6.28, on: Math.random() < 0.22 });
}}
};
const animate=(ts)=> {
if(ts - lastT < 33) return;
lastT=ts;
ctx.clearRect(0, 0, w, h);
t +=0.02;
const scan=(t * 160) % (h + 80) - 40;
for (const cell of cells){
const near=1 - Math.min(1, Math.abs(cell.y - scan) / 60);
const bs=cell.on ? 0.28 + 0.22 * Math.sin(t * 2 + cell.ph):0.07;
const a=Math.min(0.95, bs + near * 0.5);
ctx.fillStyle=rgba(color, a);
rr(cell.x, cell.y, cell.w, cell.h, 3);
ctx.fill();
}
const g=ctx.createLinearGradient(0, scan - 30, 0, scan + 30);
g.addColorStop(0, rgba(color, 0));
g.addColorStop(0.5, rgba(color, 0.16));
g.addColorStop(1, rgba(color, 0));
ctx.fillStyle=g;
ctx.fillRect(0, scan - 30, w, 60);
};
kurtechObserveVisible(vpsScene);
fit();
kurtechRegisterLoop(vpsScene, animate);
window.addEventListener('resize', ()=> fit(), { passive: true });
}, 0);
setTimeout(()=> {
const vpsScene=document.querySelector('.service-scene-vps');
if(!vpsScene||window.__kurtechDisableParticles) return;
const canvas=document.createElement('canvas');
canvas.style.cssText='position:absolute;inset:0;width:100%;height:100%;pointer-events:none;z-index:0;';
vpsScene.insertBefore(canvas, vpsScene.firstChild);
const ctx=canvas.getContext('2d');
const color='#B89CFF';
const rgba=(a)=> `rgba(184,156,255,${a})`;
let lastT=0, t=0;
let w=0, h=0, items=[];
const iconKinds=['db', 'server', 'docker', 'ssl', 'vm', 'terminal'];
const codeGlyphs=['sudo', 'ssh', ':~$', 'root@', 'docker', '443', 'nginx', 'systemctl', 'backup', '.sh'];
const fit=()=> {
w=vpsScene.clientWidth;
h=vpsScene.clientHeight;
const dpr=kurtechRenderScale(window.innerWidth);
canvas.width=w * dpr;
canvas.height=h * dpr;
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
items=[];
const I=Math.max(3, Math.round(Math.min(9, Math.max(4, Math.round(w / 300))) * kurtechParticleScale()));
for (let i=0; i < I; i++){
items.push({
type: 'icon',
kind: iconKinds[i % iconKinds.length],
x: 60 + Math.random() * (w - 120),
y: Math.random() * h,
sp: 6 + Math.random() * 10,
ph: Math.random() * 6.28,
sc: 0.85 + Math.random() * 0.6,
a: 0.16 + Math.random() * 0.16
});
}
const G=Math.max(3, Math.round(Math.min(9, Math.max(4, Math.round(w / 280))) * kurtechParticleScale()));
for (let i=0; i < G; i++){
items.push({
type: 'code',
text: codeGlyphs[(Math.random() * codeGlyphs.length) | 0],
x: Math.random() * w,
y: Math.random() * h,
sp: 8 + Math.random() * 12,
ph: Math.random() * 6.28,
size: 12 + Math.random() * 7,
a: 0.14 + Math.random() * 0.18
});
}};
const rr=(x, y, bw, bh, r)=> { ctx.beginPath(); ctx.moveTo(x + r, y); ctx.arcTo(x + bw, y, x + bw, y + bh, r); ctx.arcTo(x + bw, y + bh, x, y + bh, r); ctx.arcTo(x, y + bh, x, y, r); ctx.arcTo(x, y, x + bw, y, r); ctx.closePath(); };
const drawIcon=(it)=> {
ctx.save();
ctx.translate(it.x, it.y);
ctx.scale(it.sc, it.sc);
ctx.strokeStyle=rgba(it.a);
ctx.fillStyle=rgba(it.a * 0.7);
ctx.lineWidth=1.4;
if(it.kind==='db'){
ctx.beginPath(); ctx.ellipse(0, -12, 13, 4.5, 0, 0, Math.PI * 2); ctx.stroke();
ctx.beginPath(); ctx.moveTo(-13, -12); ctx.lineTo(-13, 12); ctx.moveTo(13, -12); ctx.lineTo(13, 12); ctx.stroke();
ctx.beginPath(); ctx.ellipse(0, 0, 13, 4.5, 0, 0, Math.PI); ctx.stroke();
ctx.beginPath(); ctx.ellipse(0, 12, 13, 4.5, 0, 0, Math.PI); ctx.stroke();
}else if(it.kind==='server'){
rr(-15, -12, 30, 10, 2); ctx.stroke();
rr(-15, 2, 30, 10, 2); ctx.stroke();
ctx.beginPath(); ctx.arc(-10, -7, 1.5, 0, Math.PI * 2); ctx.arc(-10, 7, 1.5, 0, Math.PI * 2); ctx.fill();
ctx.beginPath(); ctx.moveTo(5, -7); ctx.lineTo(11, -7); ctx.moveTo(5, 7); ctx.lineTo(11, 7); ctx.stroke();
}else if(it.kind==='docker'){
for (let r=0; r < 2; r++) for (let c=0; c < 3; c++){ rr(-14 + c * 10, -8 + r * 10, 8, 8, 1.5); ctx.stroke(); }
rr(-4, -18, 8, 8, 1.5); ctx.stroke();
}else if(it.kind==='ssl'){
rr(-9, -2, 18, 15, 3); ctx.stroke();
ctx.beginPath(); ctx.arc(0, -2, 6, Math.PI, 0); ctx.stroke();
ctx.beginPath(); ctx.arc(0, 5, 2, 0, Math.PI * 2); ctx.fill();
}else if(it.kind==='vm'){
rr(-15, -12, 30, 24, 3); ctx.stroke();
rr(-9, -6, 18, 14, 2); ctx.stroke();
}else{
rr(-15, -11, 30, 22, 3); ctx.stroke();
ctx.beginPath(); ctx.moveTo(-9, -3); ctx.lineTo(-4, 0); ctx.lineTo(-9, 3); ctx.moveTo(-2, 4); ctx.lineTo(6, 4); ctx.stroke();
}
ctx.restore();
};
const animate=(ts)=> {
if(ts - lastT < 33) return;
const dt=Math.min(0.05, (ts - lastT) / 1000);
lastT=ts;
t +=dt;
ctx.clearRect(0, 0, w, h);
ctx.textBaseline='middle';
ctx.textAlign='center';
for (const it of items){
it.y -=it.sp * dt;
if(it.y < -24){ it.y=h + 24; it.x=60 + Math.random() * (w - 120); }
if(it.type==='icon'){
drawIcon(it);
}else{
ctx.font=`700 ${it.size}px "JetBrains Mono", monospace`;
ctx.fillStyle=rgba(it.a + 0.06 * Math.sin(t * 1.5 + it.ph));
ctx.fillText(it.text, it.x, it.y);
}}
ctx.textAlign='left';
};
kurtechObserveVisible(vpsScene);
fit();
kurtechRegisterLoop(vpsScene, animate);
window.addEventListener('resize', ()=> fit(), { passive: true });
}, 0);
setTimeout(()=> {
const dataScene=document.querySelector('.service-scene-data');
if(!dataScene||window.__kurtechDisableParticles) return;
const canvas=document.createElement('canvas');
canvas.style.cssText='position:absolute;inset:0;width:100%;height:100%;pointer-events:none;z-index:0;';
dataScene.insertBefore(canvas, dataScene.firstChild);
const ctx=canvas.getContext('2d');
const color='#FF7A2F';
const alt='#7C3CFF';
const rgba=(hex, a)=> {
const n=parseInt(hex.slice(1), 16);
return `rgba(${(n >> 16) & 255}, ${(n >> 8) & 255}, ${n & 255}, ${a})`;
};
let lastT=0, t=0, fireTimer=0;
let w=0, h=0, layers=[], edges=[], signals=[];
const fit=()=> {
w=dataScene.clientWidth;
h=dataScene.clientHeight;
const dpr=kurtechRenderScale(window.innerWidth);
canvas.width=w * dpr;
canvas.height=h * dpr;
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
const counts=[4, 6, 6, 5, 3];
const marginX=w * 0.16, marginY=h * 0.24;
const usableW=w - marginX * 2;
layers=counts.map((c, li)=> {
const x=marginX + (usableW * li) / (counts.length - 1);
const arr=[];
for (let i=0; i < c; i++){
const y=marginY + ((h - marginY * 2) * (i + 0.5)) / c;
arr.push({ x, y, ph: Math.random() * 6.28 });
}
return arr;
});
edges=[];
for (let li=0; li < layers.length - 1; li++){
for (const a of layers[li]){
for (const b of layers[li + 1]){
edges.push({ a, b });
}}
}
signals=[];
};
const animate=(ts)=> {
if(ts - lastT < 33) return;
const dt=Math.min(0.05, (ts - lastT) / 1000);
lastT=ts;
t +=dt;
ctx.clearRect(0, 0, w, h);
ctx.lineWidth=0.6;
for (const e of edges){
ctx.strokeStyle=rgba(color, 0.05);
ctx.beginPath();
ctx.moveTo(e.a.x, e.a.y);
ctx.lineTo(e.b.x, e.b.y);
ctx.stroke();
}
fireTimer -=dt;
if(fireTimer <=0){
fireTimer=0.5 + Math.random() * 0.5;
const src=layers[0][(Math.random() * layers[0].length) | 0];
const dstLayer=layers[1];
const fanout=2 + ((Math.random() * 2) | 0);
for (let k=0; k < fanout; k++){
const dst=dstLayer[(Math.random() * dstLayer.length) | 0];
signals.push({ a: src, b: dst, p: 0, layer: 0, c: Math.random() < 0.3 ? alt:color });
}}
const next=[];
for (const s of signals){
s.p +=dt * 1.6;
const x=s.a.x + (s.b.x - s.a.x) * s.p;
const y=s.a.y + (s.b.y - s.a.y) * s.p;
if(s.p < 1){
const g=ctx.createRadialGradient(x, y, 0, x, y, 6);
g.addColorStop(0, rgba(s.c, 0.95));
g.addColorStop(1, rgba(s.c, 0));
ctx.fillStyle=g;
ctx.beginPath();
ctx.arc(x, y, 6, 0, Math.PI * 2);
ctx.fill();
next.push(s);
}else{
s.b.fire=0.6;
const nl=s.layer + 2;
if(nl < layers.length){
const dst=layers[nl][(Math.random() * layers[nl].length) | 0];
if(Math.random() < 0.7) next.push({ a: s.b, b: dst, p: 0, layer: s.layer + 1, c: s.c });
}}
}
signals=next.length > 260 ? next.slice(-260):next;
for (const layer of layers){
for (const nd of layer){
const pulse=0.4 + 0.3 * Math.sin(t * 2 + nd.ph);
let a=0.3 + pulse * 0.3, rad=2.4;
if(nd.fire > 0){ nd.fire -=dt; a=0.95; rad=3.6; }
ctx.fillStyle=rgba(color, a);
ctx.beginPath();
ctx.arc(nd.x, nd.y, rad, 0, Math.PI * 2);
ctx.fill();
}}
};
kurtechObserveVisible(dataScene);
fit();
kurtechRegisterLoop(dataScene, animate);
window.addEventListener('resize', ()=> fit(), { passive: true });
}, 100);
setTimeout(()=> {
const cloudScene=document.querySelector('.service-scene-cloud');
if(!cloudScene||window.__kurtechDisableParticles) return;
const canvas=document.createElement('canvas');
canvas.style.cssText='position:absolute;inset:0;width:100%;height:100%;pointer-events:none;z-index:0;';
cloudScene.insertBefore(canvas, cloudScene.firstChild);
const ctx=canvas.getContext('2d');
const colorUp='#34D5FF';
const colorDown='#3DFFB5';
const rgba=(hex, a)=> {
const n=parseInt(hex.slice(1), 16);
return `rgba(${(n >> 16) & 255}, ${(n >> 8) & 255}, ${n & 255}, ${a})`;
};
let lastT=0;
let w=0, h=0, cols=0, psUp=[], psDown=[];
const fit=()=> {
w=cloudScene.clientWidth;
h=cloudScene.clientHeight;
const dpr=kurtechRenderScale(window.innerWidth);
canvas.width=w * dpr;
canvas.height=h * dpr;
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
cols=Math.min(50, Math.max(6, Math.round(w / 74)));
const P=cols * 6;
psUp=[];
psDown=[];
for (let i=0; i < P; i++){
psUp.push({
x: ((Math.random() * cols) | 0) * (w / cols) + w / cols / 2,
y: Math.random() * h,
sp: 0.22 + Math.random() * 0.6,
s: 1 + Math.random() * 1.7
});
}
const PD=Math.round(P * 0.7);
for (let i=0; i < PD; i++){
psDown.push({
x: ((Math.random() * cols) | 0) * (w / cols) + w / cols / 2,
y: Math.random() * h,
sp: 2.4 + Math.random() * 4.0,
s: 2 + Math.random() * 2.2
});
}};
const animate=(ts)=> {
if(ts - lastT < 33) return;
lastT=ts;
ctx.clearRect(0, 0, w, h);
ctx.strokeStyle=rgba(colorUp, 0.05);
ctx.lineWidth=1;
for (let c=0; c < cols; c++){
const x=c * (w / cols) + w / cols / 2;
ctx.beginPath();
ctx.moveTo(x, 0);
ctx.lineTo(x, h);
ctx.stroke();
}
for (const p of psUp){
p.y -=p.sp;
if(p.y < -4) p.y=h + 4;
const g=ctx.createLinearGradient(0, p.y, 0, p.y + 15);
g.addColorStop(0, rgba(colorUp, 0.85));
g.addColorStop(1, rgba(colorUp, 0));
ctx.fillStyle=g;
ctx.fillRect(p.x - p.s / 2, p.y, p.s, 15);
}
for (const p of psDown){
p.y +=p.sp;
if(p.y > h + 4) p.y=-4;
const g=ctx.createLinearGradient(0, p.y - 15, 0, p.y);
g.addColorStop(0, rgba(colorDown, 0));
g.addColorStop(1, rgba(colorDown, 0.85));
ctx.fillStyle=g;
ctx.fillRect(p.x - p.s / 2, p.y - 15, p.s, 15);
}};
kurtechObserveVisible(cloudScene);
fit();
kurtechRegisterLoop(cloudScene, animate);
window.addEventListener('resize', ()=> fit(), { passive: true });
}, 500);
setTimeout(()=> {
const onsiteScene=document.querySelector('.service-scene-onsite');
if(!onsiteScene||window.__kurtechDisableParticles) return;
const canvas=document.createElement('canvas');
canvas.style.cssText='position:absolute;inset:0;width:100%;height:100%;pointer-events:none;z-index:0;';
onsiteScene.insertBefore(canvas, onsiteScene.firstChild);
const ctx=canvas.getContext('2d');
const rgba=(a)=> `rgba(61,255,181,${a})`;
let lastT=0, t=0;
let w=0, h=0, items=[];
const kinds=['router', 'switch', 'nas', 'firewall', 'wifi', 'ap'];
const codes=['LAN', 'PoE', 'VLAN', 'Wi-Fi 7', '2.5G', 'NAS', 'AP', 'DHCP'];
const rr=(x, y, bw, bh, r)=> { ctx.beginPath(); ctx.moveTo(x + r, y); ctx.arcTo(x + bw, y, x + bw, y + bh, r); ctx.arcTo(x + bw, y + bh, x, y + bh, r); ctx.arcTo(x, y + bh, x, y, r); ctx.arcTo(x, y, x + bw, y, r); ctx.closePath(); };
const fit=()=> {
w=onsiteScene.clientWidth;
h=onsiteScene.clientHeight;
const dpr=kurtechRenderScale(window.innerWidth);
canvas.width=w * dpr;
canvas.height=h * dpr;
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
items=[];
const I=Math.max(3, Math.round(Math.min(9, Math.max(4, Math.round(w / 300))) * kurtechParticleScale()));
for (let i=0; i < I; i++) items.push({ type: 'icon', kind: kinds[i % kinds.length], x: 60 + Math.random() * (w - 120), y: Math.random() * h, sp: 6 + Math.random() * 10, ph: Math.random() * 6.28, sc: 0.85 + Math.random() * 0.6, a: 0.16 + Math.random() * 0.16 });
const G=Math.max(3, Math.round(Math.min(9, Math.max(4, Math.round(w / 280))) * kurtechParticleScale()));
for (let i=0; i < G; i++) items.push({ type: 'code', text: codes[(Math.random() * codes.length) | 0], x: Math.random() * w, y: Math.random() * h, sp: 8 + Math.random() * 12, ph: Math.random() * 6.28, size: 12 + Math.random() * 7, a: 0.14 + Math.random() * 0.18 });
};
const drawIcon=(it)=> {
ctx.save(); ctx.translate(it.x, it.y); ctx.scale(it.sc, it.sc);
ctx.strokeStyle=rgba(it.a); ctx.fillStyle=rgba(it.a * 0.7); ctx.lineWidth=1.4;
if(it.kind==='router'){
rr(-16, 0, 32, 11, 2.5); ctx.stroke();
ctx.beginPath(); ctx.arc(-9, 5.5, 1.4, 0, Math.PI * 2); ctx.arc(-3, 5.5, 1.4, 0, Math.PI * 2); ctx.fill();
ctx.beginPath(); ctx.moveTo(6, 0); ctx.lineTo(3, -12); ctx.moveTo(11, 0); ctx.lineTo(14, -12); ctx.stroke();
}else if(it.kind==='switch'){
rr(-17, -6, 34, 12, 2.5); ctx.stroke();
for (let k=0; k < 6; k++){ rr(-14 + k * 5, 0, 3.4, 3.4, 1); ctx.stroke(); }}else if(it.kind==='nas'){
rr(-11, -13, 22, 26, 3); ctx.stroke();
for (let k=0; k < 3; k++){ ctx.beginPath(); ctx.moveTo(-7, -8 + k * 7); ctx.lineTo(7, -8 + k * 7); ctx.stroke(); ctx.beginPath(); ctx.arc(5, -8 + k * 7, 1, 0, Math.PI * 2); ctx.fill(); }}else if(it.kind==='firewall'){
for (let r=0; r < 3; r++) for (let c=0; c < 3; c++){ rr(-15 + c * 10 + (r % 2 ? 5:0), -9 + r * 6, 9, 5, 1); ctx.stroke(); }}else if(it.kind==='wifi'){
for (let k=1; k <=3; k++){ ctx.beginPath(); ctx.arc(0, 8, k * 6, Math.PI * 1.25, Math.PI * 1.75); ctx.stroke(); }
ctx.beginPath(); ctx.arc(0, 8, 1.6, 0, Math.PI * 2); ctx.fill();
}else{
ctx.beginPath(); ctx.ellipse(0, 6, 14, 5, 0, 0, Math.PI * 2); ctx.stroke();
for (let k=1; k <=2; k++){ ctx.beginPath(); ctx.arc(0, 2, k * 6, Math.PI * 1.2, Math.PI * 1.8); ctx.stroke(); }}
ctx.restore();
};
const animate=(ts)=> {
if(ts - lastT < 33) return;
const dt=Math.min(0.05, (ts - lastT) / 1000);
lastT=ts; t +=dt;
ctx.clearRect(0, 0, w, h);
ctx.textBaseline='middle'; ctx.textAlign='center';
for (const it of items){
it.y -=it.sp * dt;
if(it.y < -24){ it.y=h + 24; it.x=60 + Math.random() * (w - 120); }
if(it.type==='icon') drawIcon(it);
else { ctx.font=`700 ${it.size}px "JetBrains Mono", monospace`; ctx.fillStyle=rgba(it.a + 0.06 * Math.sin(t * 1.5 + it.ph)); ctx.fillText(it.text, it.x, it.y); }}
ctx.textAlign='left';
};
kurtechObserveVisible(onsiteScene);
fit();
kurtechRegisterLoop(onsiteScene, animate);
window.addEventListener('resize', ()=> fit(), { passive: true });
}, 0);
setTimeout(()=> {
const onsiteScene=document.querySelector('.service-scene-onsite');
if(!onsiteScene||window.__kurtechDisableParticles) return;
const canvas=document.createElement('canvas');
canvas.style.cssText='position:absolute;inset:0;width:100%;height:100%;pointer-events:none;z-index:0;';
onsiteScene.insertBefore(canvas, onsiteScene.firstChild);
const ctx=canvas.getContext('2d');
const color='#3DFFB5';
const fault='#FF7A6B';
const rgba=(hex, a)=> {
const n=parseInt(hex.slice(1), 16);
return `rgba(${(n >> 16) & 255}, ${(n >> 8) & 255}, ${n & 255}, ${a})`;
};
let lastT=0, t=0;
let w=0, h=0, nodes=[], repairs=[];
const fit=()=> {
w=onsiteScene.clientWidth;
h=onsiteScene.clientHeight;
const dpr=kurtechRenderScale(window.innerWidth);
canvas.width=w * dpr;
canvas.height=h * dpr;
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
const N=Math.max(3, Math.round(Math.min(26, Math.max(10, Math.round((w * h) / 60000))) * kurtechParticleScale()));
nodes=[];
for (let i=0; i < N; i++){
nodes.push({
x: 40 + Math.random() * (w - 80),
y: 40 + Math.random() * (h - 80),
ph: Math.random() * 6.28,
state: 'ok',
timer: Math.random() * 6
});
}
repairs=[];
};
const drawTool=(x, y, r, a, rot)=> {
ctx.save();
ctx.translate(x, y);
ctx.rotate(rot);
ctx.strokeStyle=rgba(color, a);
ctx.lineWidth=1.4;
ctx.beginPath();
ctx.moveTo(-r, 0); ctx.lineTo(r, 0);
ctx.moveTo(0, -r); ctx.lineTo(0, r);
ctx.stroke();
ctx.restore();
};
const animate=(ts)=> {
if(ts - lastT < 33) return;
const dt=Math.min(0.05, (ts - lastT) / 1000);
lastT=ts;
t +=dt;
ctx.clearRect(0, 0, w, h);
for (let i=0; i < nodes.length; i++){
for (let j=i + 1; j < nodes.length; j++){
const a=nodes[i], b=nodes[j];
const d=Math.hypot(a.x - b.x, a.y - b.y);
if(d < 150){
ctx.strokeStyle=rgba(color, (1 - d / 150) * 0.24);
ctx.lineWidth=0.9;
ctx.beginPath();
ctx.moveTo(a.x, a.y);
ctx.lineTo(b.x, b.y);
ctx.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:w, y: Math.random() * h, 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(t * 2 + nd.ph);
let col, rad, a;
if(nd.state==='fault'){ col=fault; rad=3; a=0.5 + 0.4 * Math.sin(t * 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, t * 4); }else{ col=color; rad=2.6; a=0.55 + pulse * 0.4; }
ctx.fillStyle=rgba(col, a);
ctx.beginPath();
ctx.arc(nd.x, nd.y, rad, 0, Math.PI * 2);
ctx.fill();
}
repairs=repairs.filter(rp=> {
rp.p +=dt * 0.9;
const x=rp.x + (rp.tx - rp.x) * rp.p;
const y=rp.y + (rp.ty - rp.y) * rp.p;
if(rp.p < 1){
const g=ctx.createRadialGradient(x, y, 0, x, y, 7);
g.addColorStop(0, rgba(color, 0.9));
g.addColorStop(1, rgba(color, 0));
ctx.fillStyle=g;
ctx.beginPath();
ctx.arc(x, y, 7, 0, Math.PI * 2);
ctx.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 rr=rp.ring;
ctx.strokeStyle=rgba(color, Math.max(0, 0.7 - rr / 60));
ctx.lineWidth=1.6;
ctx.beginPath();
ctx.arc(rp.tx, rp.ty, rr, 0, Math.PI * 2);
ctx.stroke();
return rr < 44;
});
};
kurtechObserveVisible(onsiteScene);
fit();
kurtechRegisterLoop(onsiteScene, animate);
window.addEventListener('resize', ()=> fit(), { passive: true });
}, 200);
setTimeout(()=> {
const adsScene=document.querySelector('.service-scene-ads');
if(!adsScene||window.__kurtechDisableParticles) return;
const canvas=document.createElement('canvas');
canvas.style.cssText='position:absolute;inset:0;width:100%;height:100%;pointer-events:none;z-index:0;';
adsScene.insertBefore(canvas, adsScene.firstChild);
const ctx=canvas.getContext('2d');
const color='#7AA2FF';
const hit='#B7C9FF';
const rgba=(hex, a)=> {
const n=parseInt(hex.slice(1), 16);
return `rgba(${(n >> 16) & 255}, ${(n >> 8) & 255}, ${n & 255}, ${a})`;
};
let lastT=0, t=0, emitTimer=0;
let w=0, h=0, audience=[], waves=[], emitters=[];
const fit=()=> {
w=adsScene.clientWidth;
h=adsScene.clientHeight;
const dpr=kurtechRenderScale(window.innerWidth);
canvas.width=w * dpr;
canvas.height=h * dpr;
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
const N=Math.max(3, Math.round(Math.min(120, Math.max(40, Math.round((w * h) / 14000))) * kurtechParticleScale()));
audience=[];
for (let i=0; i < N; i++){
audience.push({ x: Math.random() * w, y: Math.random() * h, lit: 0 });
}
emitters=[
{ x: w * 0.24, y: h * 0.36 },
{ x: w * 0.68, y: h * 0.30 },
{ x: w * 0.5,  y: h * 0.66 }
];
waves=[];
};
const emit=(ex, ey)=> { waves.push({ x: ex, y: ey, r: 0, max: Math.min(w, h) * 0.62 });};
const animate=(ts)=> {
if(ts - lastT < 33) return;
const dt=Math.min(0.05, (ts - lastT) / 1000);
lastT=ts;
t +=dt;
ctx.clearRect(0, 0, w, h);
emitTimer -=dt;
if(emitTimer <=0){
const e=emitters[(Math.random() * emitters.length) | 0];
emit(e.x, e.y);
emitTimer=0.9 + Math.random() * 0.8;
}
for (const e of emitters){
const pulse=0.5 + 0.5 * Math.sin(t * 3 + e.x);
const g=ctx.createRadialGradient(e.x, e.y, 0, e.x, e.y, 10);
g.addColorStop(0, rgba(color, 0.55 + pulse * 0.3));
g.addColorStop(1, rgba(color, 0));
ctx.fillStyle=g;
ctx.beginPath();
ctx.arc(e.x, e.y, 10, 0, Math.PI * 2);
ctx.fill();
}
waves=waves.filter(wv=> {
wv.r +=dt * 150;
const a=Math.max(0, 0.72 * (1 - wv.r / wv.max));
ctx.strokeStyle=rgba(color, a);
ctx.lineWidth=1.8;
ctx.beginPath();
ctx.arc(wv.x, wv.y, wv.r, 0, Math.PI * 2);
ctx.stroke();
for (const p of audience){
if(p.lit <=0){
const d=Math.hypot(p.x - wv.x, p.y - wv.y);
if(Math.abs(d - wv.r) < 6) p.lit=1;
}}
return wv.r < wv.max;
});
for (const p of audience){
let a, rad, col;
if(p.lit > 0){
p.lit -=dt * 0.9;
a=0.25 + p.lit * 0.7;
rad=1.3 + p.lit * 2.2;
col=hit;
}else{
a=0.26;
rad=1.4;
col=color;
}
ctx.fillStyle=rgba(col, a);
ctx.beginPath();
ctx.arc(p.x, p.y, rad, 0, Math.PI * 2);
ctx.fill();
}};
kurtechObserveVisible(adsScene);
fit();
kurtechRegisterLoop(adsScene, animate);
window.addEventListener('resize', ()=> fit(), { passive: true });
}, 250);
function kurtechDrawerNav(){
const t=document.querySelector('[data-mobile-toggle]');
return !!t&&getComputedStyle(t).display!=='none';
}
if(!window.__ktNavInit){
window.__ktNavInit=true;
const navToggle=document.querySelector('[data-mobile-toggle]');
const navLinks=document.querySelector('[data-nav-links]');
const navDropdowns=[...document.querySelectorAll('[data-nav-group]')];
if(navToggle&&navLinks){
navToggle.addEventListener('click', ()=> {
const open=navLinks.classList.toggle('is-open');
navToggle.setAttribute('aria-expanded', String(open));
if(!open) navDropdowns.forEach(group=> group.classList.remove('is-open'));
});
}
const page=document.body.dataset.page;
const pageGroup=document.body.dataset.pageGroup;
document.querySelectorAll('[data-nav]').forEach(link=> {
if(link.dataset.nav===page) link.classList.add('active');
});
navDropdowns.forEach(group=> {
if(group.dataset.navGroup===pageGroup) group.classList.add('active');
const toggle=group.querySelector('[data-nav-dropdown-toggle]');
if(!toggle) return;
toggle.addEventListener('click', event=> {
if(!kurtechDrawerNav()) return;
event.preventDefault();
navDropdowns.forEach(item=> {
if(item!==group) item.classList.remove('is-open');
});
group.classList.toggle('is-open');
});
});
document.addEventListener('click', event=> {
if(!navLinks||!navLinks.contains(event.target)){
navDropdowns.forEach(group=> group.classList.remove('is-open'));
}});
const toast=document.querySelector('[data-kt-toast]');
if(toast){
let dismissed=false;
try { dismissed=sessionStorage.getItem('ktToastOff')==='1'; } catch (e){}
if(!dismissed){
setTimeout(()=> {
toast.hidden=false;
requestAnimationFrame(()=> toast.classList.add('is-in'));
}, 900);
const hide=()=> {
toast.classList.remove('is-in');
setTimeout(()=> { toast.hidden=true; }, 320);
try { sessionStorage.setItem('ktToastOff', '1'); } catch (e){}};
toast.querySelector('[data-kt-toast-close]').addEventListener('click', hide);
toast.querySelector('a').addEventListener('click', ()=> {
try { sessionStorage.setItem('ktToastOff', '1'); } catch (e){}});
setTimeout(()=> { if(!toast.hidden) hide(); }, 12000);
}}
const acc=document.querySelector('[data-nav-account]');
if(acc){
const accBtn=acc.querySelector('[data-account-toggle]');
const accMenu=acc.querySelector('[data-account-menu]');
const closeAcc=()=> {
acc.classList.remove('is-open');
accMenu.hidden=true;
accBtn.setAttribute('aria-expanded', 'false');
};
accBtn.addEventListener('click', event=> {
event.stopPropagation();
const open=accMenu.hidden;
acc.classList.toggle('is-open', open);
accMenu.hidden = !open;
accBtn.setAttribute('aria-expanded', String(open));
});
document.addEventListener('click', event=> {
if(!acc.contains(event.target)) closeAcc();
});
document.addEventListener('keydown', event=> {
if(event.key==='Escape') closeAcc();
});
}}
const launchWall=document.querySelector('.official-site .launch-landing');
const focusCards=[...document.querySelectorAll('[data-focus-service]')];
const dockCards=[...document.querySelectorAll('[data-dock-service]')];
if(launchWall){
let wallRaf=0;
let repelRaf=0;
let previousWallActive=-1;
let wallShuffleTimer=0;
const wallSteps=6;
const keywordTags=[...document.querySelectorAll('.official-site .perimeter-tag')];
const keywordSlots=[
[4, 10], [20, 13], [38, 9], [62, 13], [82, 10],
[8, 28], [28, 32], [48, 25], [70, 31], [88, 38],
[4, 55], [22, 62], [42, 55], [62, 64], [84, 58],
[10, 78], [28, 84], [48, 78], [68, 84], [88, 78],
[14, 43], [76, 46], [16, 20], [80, 24]
];
const shuffleArray=array=> {
const copy=[...array];
for (let index=copy.length - 1; index > 0; index -=1){
const swapIndex=Math.floor(Math.random() * (index + 1));
[copy[index], copy[swapIndex]]=[copy[swapIndex], copy[index]];
}
return copy;
};
const scatterKeywords=()=> {
const slots=shuffleArray(keywordSlots);
keywordTags.forEach((tag, index)=> {
const slot=slots[index % slots.length];
const x=slot[0] + (Math.random() * 2.8 - 1.4);
const y=slot[1] + (Math.random() * 2.8 - 1.4);
const orbit=34 + Math.random() * 68;
const spin=Math.random() > .5 ? 1:-1;
tag.style.setProperty('--kw-x', `${x.toFixed(2)}vw`);
tag.style.setProperty('--kw-y', `${y.toFixed(2)}vh`);
tag.style.setProperty('--repel-x', '0px');
tag.style.setProperty('--repel-y', '0px');
tag.style.setProperty('--kw-gather-x', `${(50 - x).toFixed(2)}vw`);
tag.style.setProperty('--kw-gather-y', `${(48 - y).toFixed(2)}vh`);
tag.style.setProperty('--kw-orbit-x', `${(Math.cos(index * 1.7) * orbit).toFixed(1)}px`);
tag.style.setProperty('--kw-orbit-y', `${(Math.sin(index * 1.7) * orbit).toFixed(1)}px`);
tag.style.setProperty('--kw-spin', `${spin * (120 + Math.random() * 160)}deg`);
});
};
const repelKeywords=event=> {
if(repelRaf) return;
repelRaf=requestAnimationFrame(()=> {
repelRaf=0;
keywordTags.forEach(tag=> {
const rect=tag.getBoundingClientRect();
const centerX=rect.left + rect.width / 2;
const centerY=rect.top + rect.height / 2;
const deltaX=centerX - event.clientX;
const deltaY=centerY - event.clientY;
const distance=Math.hypot(deltaX, deltaY);
const radius=170;
if(distance > radius||distance===0){
tag.style.setProperty('--repel-x', '0px');
tag.style.setProperty('--repel-y', '0px');
return;
}
const force=(1 - distance / radius) * 44;
tag.style.setProperty('--repel-x', `${(deltaX / distance * force).toFixed(1)}px`);
tag.style.setProperty('--repel-y', `${(deltaY / distance * force).toFixed(1)}px`);
});
});
};
const updateWallCinema=()=> {
wallRaf=0;
const rect=launchWall.getBoundingClientRect();
const total=Math.max(1, launchWall.offsetHeight - window.innerHeight);
const progress=Math.min(1, Math.max(0, -rect.top / total));
const exited=false;
const overScroll=Math.max(0, (-rect.top) - total);
const exitFade=Math.min(1, overScroll / (window.innerHeight * 0.62));
document.body.classList.toggle('wall-exited', exited);
document.body.style.setProperty('--exit-fade', exitFade.toFixed(4));
const isFlowLayout=window.matchMedia('(max-width: 1024px), (orientation: landscape) and (pointer: coarse) and (max-width: 1400px) and (max-height: 1040px)'
).matches;
let scaled;
if(isFlowLayout){
const line=window.innerHeight * 0.42;
const segs=[];
focusCards.forEach((c)=> segs.push({ el: c, step: Number(c.dataset.focusService) }));
const works=document.querySelector('.works-showcase');
if(works) segs.push({ el: works, step: 1 });
const vpsSc=document.querySelector('.vps-showcase');
if(vpsSc) segs.push({ el: vpsSc, step: 3 });
const osSc=document.querySelector('.onsite-showcase');
if(osSc) segs.push({ el: osSc, step: 5 });
const adsSc=document.querySelector('.ads-showcase');
if(adsSc) segs.push({ el: adsSc, step: 6 });
const clSc=document.querySelector('.cloud-showcase');
if(clSc) segs.push({ el: clSc, step: 2 });
const aiSc=document.querySelector('.ai-showcase');
if(aiSc) segs.push({ el: aiSc, step: 4 });
let hit=null, bestD=Infinity;
segs.forEach((s)=> {
const r=s.el.getBoundingClientRect();
if(!r.height) return;
if(r.top <=line&&r.bottom >=line){ hit=s.step; bestD=-1; return; }
if(bestD===-1) return;
const d=r.top > line ? r.top - line:line - r.bottom;
if(d < bestD){ bestD=d; hit=s.step; }});
const firstCard=focusCards[0];
const firstRect=firstCard ? firstCard.getBoundingClientRect():null;
scaled=(firstRect&&firstRect.top > window.innerHeight * 0.6) ? 0:(hit==null ? 0:hit);
}else{
scaled=progress * wallSteps;
}
const active=Math.min(wallSteps, Math.max(0, Math.round(scaled)));
const local=progress >=1 ? 1:Math.min(1, Math.max(0, scaled - Math.floor(scaled)));
const smoothLocal=local * local * (3 - 2 * local);
launchWall.style.setProperty('--wall-progress', progress.toFixed(4));
launchWall.style.setProperty('--wall-local', local.toFixed(4));
launchWall.style.setProperty('--wall-local-smooth', smoothLocal.toFixed(4));
launchWall.style.setProperty('--wall-phase', scaled.toFixed(4));
launchWall.style.setProperty('--wall-active', active);
document.documentElement.style.setProperty('--wall-progress', progress.toFixed(4));
document.documentElement.style.setProperty('--wall-local-smooth', smoothLocal.toFixed(4));
document.documentElement.style.setProperty('--wall-phase', scaled.toFixed(4));
document.documentElement.style.setProperty('--wall-active', active);
for (let index=0; index <=wallSteps; index +=1){
const rawWeight=Math.max(0, 1 - Math.abs(scaled - index));
const smoothWeight=rawWeight * rawWeight * (3 - 2 * rawWeight);
launchWall.style.setProperty(`--scene-${index}-weight`, smoothWeight.toFixed(4));
document.documentElement.style.setProperty(`--scene-${index}-weight`, smoothWeight.toFixed(4));
}
if(previousWallActive!==active){
launchWall.classList.forEach(className=> {
if(className.startsWith('wall-step-')) launchWall.classList.remove(className);
});
launchWall.classList.add(`wall-step-${active}`);
previousWallActive=active;
window.__kurtechActiveStep=active;
window.__kurtechResumeCallbacks.forEach((resumes, el)=> {
if(kurtechShouldRender(el)) resumes.forEach((fn)=> fn());
});
scatterKeywords();
launchWall.classList.remove('is-shuffling');
void launchWall.offsetWidth;
launchWall.classList.add('is-shuffling');
window.clearTimeout(wallShuffleTimer);
wallShuffleTimer=window.setTimeout(()=> launchWall.classList.remove('is-shuffling'), 920);
}
focusCards.forEach(card=> {
card.classList.toggle('is-active', Number(card.dataset.focusService)===active);
card.classList.toggle('is-before', Number(card.dataset.focusService) < active);
card.classList.toggle('is-after', Number(card.dataset.focusService) > active);
});
dockCards.forEach(card=> {
card.classList.toggle('is-active', Number(card.dataset.dockService)===active);
});
};
const requestWallCinema=()=> {
if(wallRaf) return;
wallRaf=requestAnimationFrame(updateWallCinema);
};
scatterKeywords();
updateWallCinema();
window.addEventListener('scroll', requestWallCinema, { passive: true });
window.addEventListener('resize', requestWallCinema, { passive: true });
window.addEventListener('pointermove', repelKeywords, { passive: true });
window.addEventListener('pointerleave', ()=> {
keywordTags.forEach(tag=> {
tag.style.setProperty('--repel-x', '0px');
tag.style.setProperty('--repel-y', '0px');
});
}, { passive: true });
dockCards.forEach(card=> {
card.addEventListener('click', ()=> {
const service=Number(card.dataset.dockService);
const total=Math.max(1, launchWall.offsetHeight - window.innerHeight);
const targetProgress=Math.min(1, Math.max(0, service / wallSteps));
window.scrollTo({
top: launchWall.offsetTop + total * targetProgress,
behavior: 'smooth'
});
});
});
}
(function (){
const isTouch=window.matchMedia('(hover: none)').matches||'ontouchstart' in window;
if(!isTouch) return;
const SEL='.vps-card, .vps-step, .op-flow-step, .vps-host-chip, .os-note, .calc-field';
const hasTip=(el)=> !!el.querySelector(':scope > .vps-detail, :scope > .chip-tip, :scope > .os-tip, :scope > .calc-tip');
document.addEventListener('click', (e)=> {
const host=e.target.closest(SEL);
if(e.target.closest('.vps-detail, .chip-tip, .os-tip, .calc-tip')) return;
if(e.target.closest('a, button, input, select, textarea, label')) return;
document.querySelectorAll('.tip-open').forEach((el)=> { if(el!==host) el.classList.remove('tip-open'); });
if(host&&hasTip(host)){
e.preventDefault();
host.classList.toggle('tip-open');
}}, { passive: false });
})();
(function (){
const hd=document.querySelector('[data-site-header]');
if(!hd) return;
let ticking=false;
let lastY=window.scrollY;
const update=()=> {
ticking=false;
const y=window.scrollY;
hd.classList.toggle('is-scrolled', y > 24);
const menuOpen=document.body.classList.contains('nav-open')||hd.querySelector('.nav-item.is-open');
if(!menuOpen){
if(y > 140&&y > lastY + 6) hd.classList.add('is-hidden');
else if(y < lastY - 6||y <=140) hd.classList.remove('is-hidden');
}
lastY=y;
};
update();
window.addEventListener('scroll', ()=> {
if(ticking) return;
ticking=true;
requestAnimationFrame(update);
}, { passive: true });
window.addEventListener('mousemove', (e)=> {
if(e.clientY < 90) hd.classList.remove('is-hidden');
}, { passive: true });
})();
(function (){
if(window.__ktDockMini) return;
const dock=document.querySelector('.service-dock');
if(!dock||!window.matchMedia('(pointer: coarse)').matches) return;
window.__ktDockMini=true;
const cards=[...dock.querySelectorAll('.dock-card')];
if(!cards.length) return;
const label=(c)=> ({
idx: (c.querySelector('b')||{}).textContent||'',
en: (c.querySelector('strong')||{}).textContent||''
});
const bar=document.createElement('nav');
bar.className='dock-mini';
bar.setAttribute('aria-label', '服務段落導覽');
bar.innerHTML =
'<span class="dm-now"><b data-dm-idx></b><strong data-dm-now></strong></span>' +
'<span class="dm-dots">' + cards.map((c, i)=>
'<button class="dm-dot" type="button" data-dm-i="' + i + '" aria-label="' + label(c).en + '"></button>'
).join('') + '</span>' +
'<button class="dm-all" type="button" data-dm-all>ALL</button>';
document.body.appendChild(bar);
const idxEl=bar.querySelector('[data-dm-idx]');
const nowEl=bar.querySelector('[data-dm-now]');
const dots=[...bar.querySelectorAll('.dm-dot')];
dots.forEach((d, i)=> {
d.style.setProperty('--dm-c', getComputedStyle(cards[i]).color);
});
const sync=()=> {
let n=cards.findIndex((c)=> c.classList.contains('is-active'));
if(n < 0) n=0;
const { idx, en }=label(cards[n]);
idxEl.textContent='SERVICE ' + idx;
nowEl.textContent=en;
dots.forEach((d, i)=> d.classList.toggle('is-on', i===n));
};
new MutationObserver(sync).observe(dock, { subtree: true, attributes: true, attributeFilter: ['class'] });
sync();
const close=()=> document.body.classList.remove('dock-open');
bar.querySelector('[data-dm-all]').addEventListener('click', (e)=> {
e.stopPropagation();
document.body.classList.toggle('dock-open');
});
dots.forEach((d, i)=> d.addEventListener('click', ()=> { cards[i].click(); close(); }));
cards.forEach((c)=> c.addEventListener('click', close));
document.addEventListener('click', (e)=> {
if(!e.target.closest('.dock-mini')&&!e.target.closest('.service-dock')) close();
});
})();
(function (){
if(!window.matchMedia('(pointer: coarse)').matches) return;
let armed=null, timer=0;
const disarm=()=> {
if(!armed) return;
armed.classList.remove('is-armed');
const hint=armed.querySelector('.work-armhint');
if(hint) hint.remove();
armed=null;
window.clearTimeout(timer);
};
const arm=(tile)=> {
disarm();
armed=tile;
tile.classList.add('is-armed');
const hint=document.createElement('span');
hint.className='work-armhint';
hint.textContent='再按一次前往';
tile.appendChild(hint);
timer=window.setTimeout(disarm, 2600);
};
const defuse=()=> {
document.querySelectorAll('.works-showcase a.work-tile[href]').forEach((a)=> {
a.dataset.href=a.getAttribute('href');
a.removeAttribute('href');
a.removeAttribute('target');
a.style.cursor='pointer';
});
};
defuse();
const wall=document.querySelector('.works-showcase');
if(wall) new MutationObserver(defuse).observe(wall, { childList: true, subtree: true });
document.addEventListener('click', (e)=> {
const tile=e.target.closest ? e.target.closest('.works-showcase a.work-tile'):null;
if(!tile){ disarm(); return; }
if(tile!==armed){ arm(tile); return; }
const url=tile.dataset.href;
disarm();
if(url) window.open(url, '_blank', 'noopener');
});
window.addEventListener('scroll', disarm, { passive: true });
})();