const c = document.createElement('canvas');
document.body.appendChild(c);
const ctx = c.getContext('2d');
const cs = getComputedStyle(document.documentElement);
const BG = cs.getPropertyValue('--bg').trim() || '#0d0d12';
const ACCENT = cs.getPropertyValue('--accent').trim() || '#5b5bf7';
let w, h;
function resize() {
const dpr = Math.min(devicePixelRatio || 1, 2);
w = innerWidth; h = innerHeight;
c.width = w * dpr; c.height = h * dpr;
c.style.width = '100%'; c.style.height = '100%';
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
ctx.fillStyle = BG; ctx.fillRect(0, 0, w, h);
}
addEventListener('resize', resize);
resize();
// 부드럽게 이어지는 값 노이즈 (해시 기반)
function hash(i, j) { const s = Math.sin(i * 127.1 + j * 311.7) * 43758.5453; return s - Math.floor(s); }
function noise2(x, y) {
const xi = Math.floor(x), yi = Math.floor(y), xf = x - xi, yf = y - yi;
const u = xf * xf * (3 - 2 * xf), v = yf * yf * (3 - 2 * yf);
const a = hash(xi, yi), b = hash(xi + 1, yi), cc = hash(xi, yi + 1), d = hash(xi + 1, yi + 1);
return a + (b - a) * u + (cc - a) * v + (a - b - cc + d) * u * v;
}
const N = 320, SCALE = 0.0045;
const pts = Array.from({ length: N }, () => ({ x: Math.random() * w, y: Math.random() * h }));
function tick() {
ctx.fillStyle = 'rgba(13,13,18,0.045)';
ctx.fillRect(0, 0, w, h);
ctx.strokeStyle = ACCENT;
ctx.globalAlpha = 0.55;
ctx.lineWidth = 1.1;
for (const p of pts) {
const angle = noise2(p.x * SCALE, p.y * SCALE) * Math.PI * 4;
const nx = p.x + Math.cos(angle) * 2, ny = p.y + Math.sin(angle) * 2;
ctx.beginPath(); ctx.moveTo(p.x, p.y); ctx.lineTo(nx, ny); ctx.stroke();
p.x = nx; p.y = ny;
if (p.x < 0 || p.x > w || p.y < 0 || p.y > h) { p.x = Math.random() * w; p.y = Math.random() * h; }
}
ctx.globalAlpha = 1;
}
for (let i = 0; i < 80; i++) tick(); // 스크린샷 시점에도 궤적이 보이도록 미리 진행
(function loop() { tick(); requestAnimationFrame(loop); })();